From a9f6b2f52ec77663199b202ab18f135e42ae8c15 Mon Sep 17 00:00:00 2001 From: Pushkin Sergey Date: Mon, 1 Apr 2019 18:48:22 +0300 Subject: [PATCH 001/717] setting direction in PlantUMLWriter #117 --- .../src/com/structurizr/example/PlantUML.java | 3 +++ .../io/plantuml/PlantUMLWriter.java | 17 +++++++++++--- .../io/plantuml/PlantUMLWriterTests.java | 23 +++++++++++++++++++ 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/structurizr-examples/src/com/structurizr/example/PlantUML.java b/structurizr-examples/src/com/structurizr/example/PlantUML.java index 520e4fd7a..21b4fde25 100644 --- a/structurizr-examples/src/com/structurizr/example/PlantUML.java +++ b/structurizr-examples/src/com/structurizr/example/PlantUML.java @@ -44,6 +44,9 @@ public static void main(String[] args) throws Exception { plantUMLWriter.addSkinParam("rectangleFontColor", "#ffffff"); plantUMLWriter.addSkinParam("rectangleStereotypeFontColor", "#ffffff"); + // can set direction, default is "top to bottom direction" + // plantUMLWriter.setDirection("left to right direction"); + // write to a specific writer StringWriter stringWriter = new StringWriter(); plantUMLWriter.write(workspace, stringWriter); diff --git a/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLWriter.java b/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLWriter.java index 3bb85fc3c..a939bf06c 100644 --- a/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLWriter.java +++ b/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLWriter.java @@ -27,12 +27,14 @@ */ public class PlantUMLWriter { + private final Map skinParams = new LinkedHashMap<>(); + private final List includes = new ArrayList<>(); + /** maximum diagram width or height; defaults to 2000px to match public plantuml.com installation */ private int sizeLimit = 2000; private boolean includeNotesForActors = true; private boolean useSequenceDiagrams = false; - private final Map skinParams = new LinkedHashMap<>(); - private final List includes = new ArrayList<>(); + private String direction; /** * Creates a new PlantUMLWriter, with some default skin params. @@ -124,6 +126,10 @@ public void setSizeLimit(int sizeLimit) { this.sizeLimit = sizeLimit; } + public void setDirection(String direction) { + this.direction = direction; + } + /** * Writes the views in the given workspace as PlantUML definitions, to the specified writer. * @@ -676,6 +682,11 @@ protected void writeHeader(View view, Writer writer) throws IOException { writer.write(Integer.toString(height)); writer.write(System.lineSeparator()); + if (direction != null && direction.trim().length() > 0) { + writer.write(direction); + writer.write(System.lineSeparator()); + } + String viewTitle = view.getTitle(); if (StringUtils.isNullOrEmpty(viewTitle)) { viewTitle = view.getName(); @@ -704,4 +715,4 @@ protected void writeFooter(Writer writer) throws IOException { writer.write(System.lineSeparator()); } -} \ No newline at end of file +} diff --git a/structurizr-plantuml/test/unit/com/structurizr/io/plantuml/PlantUMLWriterTests.java b/structurizr-plantuml/test/unit/com/structurizr/io/plantuml/PlantUMLWriterTests.java index e7de5dcc9..14af8227d 100644 --- a/structurizr-plantuml/test/unit/com/structurizr/io/plantuml/PlantUMLWriterTests.java +++ b/structurizr-plantuml/test/unit/com/structurizr/io/plantuml/PlantUMLWriterTests.java @@ -423,6 +423,29 @@ public void test_writeView_SpaceInKey() throws Exception { "@enduml" + System.lineSeparator(), stringWriter.toString()); } + @Test + public void test_writeView_changeDirection_whenSetDirectionAreChanged() throws Exception { + workspace = new Workspace("", ""); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("My software system", ""); + Person user = workspace.getModel().addPerson("A user", ""); + user.uses(softwareSystem, "Uses"); + workspace.getViews().createSystemContextView(softwareSystem, "the key", "").addAllElements(); // Space in key + plantUMLWriter.clearSkinParams(); + plantUMLWriter.setDirection("left to right direction"); + + plantUMLWriter.write(workspace, stringWriter); + + assertEquals("@startuml(id=the_key)" + System.lineSeparator() + // Quotes added + "scale max 1999x1999" + System.lineSeparator() + + "left to right direction" + System.lineSeparator() + + "title My software system - System Context" + System.lineSeparator() + + "" + System.lineSeparator() + + "rectangle \"A user\" <> as 2 #dddddd" + System.lineSeparator() + + "rectangle \"My software system\" <> as 1 #dddddd" + System.lineSeparator() + + "2 .[#707070].> 1 : Uses" + System.lineSeparator() + + "@enduml" + System.lineSeparator(), stringWriter.toString()); + } + private static final String SYSTEM_LANDSCAPE_VIEW = "@startuml(id=enterpriseContext)" + System.lineSeparator() + "scale max 1999x1999" + System.lineSeparator() + "title System Landscape for Some Enterprise" + System.lineSeparator() + From f371f962030dc6bc15c4e7c721a0f2b56635b36c Mon Sep 17 00:00:00 2001 From: Matthias Fritschi Date: Mon, 8 Apr 2019 11:03:46 +0200 Subject: [PATCH 002/717] Upgrade httpclient5 to 5.0-beta4 which includes a fix for proxy connections Fixes #115 See also https://issues.apache.org/jira/browse/HTTPCLIENT-1959 --- structurizr-client/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/structurizr-client/build.gradle b/structurizr-client/build.gradle index df1d06662..3724b09a7 100644 --- a/structurizr-client/build.gradle +++ b/structurizr-client/build.gradle @@ -4,7 +4,7 @@ dependencies { compile 'com.fasterxml.jackson.core:jackson-databind:2.4.0' - compile 'org.apache.httpcomponents.client5:httpclient5:5.0-beta2' + compile 'org.apache.httpcomponents.client5:httpclient5:5.0-beta4' compile 'javax.xml.bind:jaxb-api:2.3.0' @@ -23,4 +23,4 @@ sourceSets { srcDir 'test/integration' } } -} \ No newline at end of file +} From 733cf6bd6219b0eef05468fe70f9e7998855977b Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 24 Apr 2019 17:38:26 +0100 Subject: [PATCH 003/717] The auto-layout algorithm can now be configured on individual views. --- build.gradle | 2 +- docs/changelog.md | 4 + .../com/structurizr/view/AutomaticLayout.java | 113 ++++++++++++++++++ .../src/com/structurizr/view/View.java | 42 +++++++ .../view/AutomaticLayoutTests.java | 64 ++++++++++ .../unit/com/structurizr/view/ViewTests.java | 37 ++++++ 6 files changed, 261 insertions(+), 1 deletion(-) create mode 100644 structurizr-core/src/com/structurizr/view/AutomaticLayout.java create mode 100644 structurizr-core/test/unit/com/structurizr/view/AutomaticLayoutTests.java diff --git a/build.gradle b/build.gradle index 7dad70c9d..e3032958f 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.3.0' + version = '1.3.1' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 82f065939..ea67fb6c3 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.3.1 (unreleased) + +- The automatic layout algorithm can now be configured on individual views. + ## 1.3.0 (3rd March 2019) - Added the ability to lock and unlock workspaces, to prevent concurrent updates. diff --git a/structurizr-core/src/com/structurizr/view/AutomaticLayout.java b/structurizr-core/src/com/structurizr/view/AutomaticLayout.java new file mode 100644 index 000000000..d907c9b36 --- /dev/null +++ b/structurizr-core/src/com/structurizr/view/AutomaticLayout.java @@ -0,0 +1,113 @@ +package com.structurizr.view; + +/** + * A wrapper for automatic layout configuration. + */ +public final class AutomaticLayout { + + private RankDirection rankDirection; + private int rankSeparation; + private int nodeSeparation; + private int edgeSeparation; + private boolean vertices; + + AutomaticLayout() { + } + + AutomaticLayout(RankDirection rankDirection, int rankSeparation, int nodeSeparation, int edgeSeparation, boolean vertices) { + setRankDirection(rankDirection); + setRankSeparation(rankSeparation); + setNodeSeparation(nodeSeparation); + setEdgeSeparation(edgeSeparation); + setVertices(vertices); + } + + /** + * Gets the rank direction. + * + * @return a RankDirection enum + */ + public RankDirection getRankDirection() { + return rankDirection; + } + + void setRankDirection(RankDirection rankDirection) { + if (rankDirection == null) { + throw new IllegalArgumentException("A rank direction must be specified."); + } + + this.rankDirection = rankDirection; + } + + /** + * Gets the rank separation (in pixels). + * + * @return a positive integer + */ + public int getRankSeparation() { + return rankSeparation; + } + + void setRankSeparation(int rankSeparation) { + if (rankSeparation < 0) { + throw new IllegalArgumentException("The rank separation must be a positive integer."); + } + + this.rankSeparation = rankSeparation; + } + + /** + * Gets the node separation (in pixels). + * + * @return a positive integer + */ + public int getNodeSeparation() { + return nodeSeparation; + } + + void setNodeSeparation(int nodeSeparation) { + if (nodeSeparation < 0) { + throw new IllegalArgumentException("The node separation must be a positive integer."); + } + + this.nodeSeparation = nodeSeparation; + } + + /** + * Gets the edge separation (in pixels). + * + * @return a positive integer + */ + public int getEdgeSeparation() { + return edgeSeparation; + } + + void setEdgeSeparation(int edgeSeparation) { + if (edgeSeparation < 0) { + throw new IllegalArgumentException("The edge separation must be a positive integer."); + } + + this.edgeSeparation = edgeSeparation; + } + + /** + * Gets whether the automatic layout algorithm should create vertices. + * + * @return a boolean + */ + public boolean isVertices() { + return vertices; + } + + void setVertices(boolean vertices) { + this.vertices = vertices; + } + + public enum RankDirection { + TopBottom, + BottomTop, + LeftRight, + RightLeft + } + +} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/view/View.java b/structurizr-core/src/com/structurizr/view/View.java index e48196f32..039530669 100644 --- a/structurizr-core/src/com/structurizr/view/View.java +++ b/structurizr-core/src/com/structurizr/view/View.java @@ -1,6 +1,7 @@ package com.structurizr.view; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonSetter; import com.structurizr.model.Element; import com.structurizr.model.Model; import com.structurizr.model.Relationship; @@ -24,6 +25,7 @@ public abstract class View { private String description = ""; private String key; private PaperSize paperSize = null; + private AutomaticLayout automaticLayout = null; private String title; private Set elementViews = new LinkedHashSet<>(); @@ -128,6 +130,46 @@ public void setPaperSize(PaperSize paperSize) { this.paperSize = paperSize; } + /** + * Gets the automatic layout settings for this view. + * + * @return an AutomaticLayout object, or null if not enabled + */ + public AutomaticLayout getAutomaticLayout() { + return automaticLayout; + } + + @JsonSetter + void setAutomaticLayout(AutomaticLayout automaticLayout) { + this.automaticLayout = automaticLayout; + } + + /** + * Enables the automatic layout for this view, with some default settings. + * + * @param enable a boolean + */ + public void setAutomaticLayout(boolean enable) { + if (enable) { + this.setAutomaticLayout(AutomaticLayout.RankDirection.TopBottom, 300, 600, 200, false); + } else { + this.automaticLayout = null; + } + } + + /** + * Enables the automatic layout for this view, with the specified settings. + * + * @param rankDirection the rank direction + * @param rankSeparation the separation between ranks (in pixels, a positive integer) + * @param nodeSeparation the separation between nodes within the same rank (in pixels, a positive integer) + * @param edgeSeparation the separation between edges (in pixels, a positive integer) + * @param vertices whether vertices should be created during automatic layout + */ + public void setAutomaticLayout(AutomaticLayout.RankDirection rankDirection, int rankSeparation, int nodeSeparation, int edgeSeparation, boolean vertices) { + this.automaticLayout = new AutomaticLayout(rankDirection, rankSeparation, nodeSeparation, edgeSeparation, vertices); + } + /** * Gets the title of this view, if one has been set. * diff --git a/structurizr-core/test/unit/com/structurizr/view/AutomaticLayoutTests.java b/structurizr-core/test/unit/com/structurizr/view/AutomaticLayoutTests.java new file mode 100644 index 000000000..128328e41 --- /dev/null +++ b/structurizr-core/test/unit/com/structurizr/view/AutomaticLayoutTests.java @@ -0,0 +1,64 @@ +package com.structurizr.view; + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class AutomaticLayoutTests { + + @Test + public void test_setAutomaticLayout() { + AutomaticLayout automaticLayout = new AutomaticLayout(AutomaticLayout.RankDirection.LeftRight, 100, 200, 300, true); + + assertEquals(AutomaticLayout.RankDirection.LeftRight, automaticLayout.getRankDirection()); + assertEquals(100, automaticLayout.getRankSeparation()); + assertEquals(200, automaticLayout.getNodeSeparation()); + assertEquals(300, automaticLayout.getEdgeSeparation()); + assertTrue(automaticLayout.isVertices()); + } + + @Test + public void test_setRankDirection_ThrowsAnException_WhenNullIsSpecified() { + try { + AutomaticLayout automaticLayout = new AutomaticLayout(); + automaticLayout.setRankDirection(null); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("A rank direction must be specified.", iae.getMessage()); + } + } + + @Test + public void test_setRankSeparation_ThrowsAnException_WhenANegativeIntegerIsSpecified() { + try { + AutomaticLayout automaticLayout = new AutomaticLayout(); + automaticLayout.setRankSeparation(-100); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("The rank separation must be a positive integer.", iae.getMessage()); + } + } + + @Test + public void test_setNodeSeparation_ThrowsAnException_WhenANegativeIntegerIsSpecified() { + try { + AutomaticLayout automaticLayout = new AutomaticLayout(); + automaticLayout.setNodeSeparation(-100); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("The node separation must be a positive integer.", iae.getMessage()); + } + } + + @Test + public void test_setEdgeSeparation_ThrowsAnException_WhenANegativeIntegerIsSpecified() { + try { + AutomaticLayout automaticLayout = new AutomaticLayout(); + automaticLayout.setEdgeSeparation(-100); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("The edge separation must be a positive integer.", iae.getMessage()); + } + } + +} \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/view/ViewTests.java b/structurizr-core/test/unit/com/structurizr/view/ViewTests.java index 6bbc4fa00..c4eb18059 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ViewTests.java @@ -18,6 +18,7 @@ public void test_construction() { StaticView view = new SystemContextView(softwareSystem, "key", "Description"); assertEquals("key", view.getKey()); assertEquals("Description", view.getDescription()); + assertNull(view.getAutomaticLayout()); } @Test @@ -369,4 +370,40 @@ public void test_addElement_ThrowsAnException_WhenTheSpecifiedElementDoesNotExis } } + @Test + public void test_setAutomaticLayout_EnablesAutoLayoutWithSomeDefaultValues_WhenTrueIsSpecified() { + SystemLandscapeView view = new Workspace("", "").getViews().createSystemLandscapeView("key", "Description"); + view.setAutomaticLayout(true); + + assertNotNull(view.getAutomaticLayout()); + assertEquals(AutomaticLayout.RankDirection.TopBottom, view.getAutomaticLayout().getRankDirection()); + assertEquals(300, view.getAutomaticLayout().getRankSeparation()); + assertEquals(600, view.getAutomaticLayout().getNodeSeparation()); + assertEquals(200, view.getAutomaticLayout().getEdgeSeparation()); + assertFalse(view.getAutomaticLayout().isVertices()); + } + + @Test + public void test_setAutomaticLayout_DisablesAutoLayout_WhenFalseIsSpecified() { + SystemLandscapeView view = new Workspace("", "").getViews().createSystemLandscapeView("key", "Description"); + view.setAutomaticLayout(true); + assertNotNull(view.getAutomaticLayout()); + + view.setAutomaticLayout(false); + assertNull(view.getAutomaticLayout()); + } + + @Test + public void test_setAutomaticLayout() { + SystemLandscapeView view = new Workspace("", "").getViews().createSystemLandscapeView("key", "Description"); + view.setAutomaticLayout(AutomaticLayout.RankDirection.LeftRight, 100, 200, 300, true); + + assertNotNull(view.getAutomaticLayout()); + assertEquals(AutomaticLayout.RankDirection.LeftRight, view.getAutomaticLayout().getRankDirection()); + assertEquals(100, view.getAutomaticLayout().getRankSeparation()); + assertEquals(200, view.getAutomaticLayout().getNodeSeparation()); + assertEquals(300, view.getAutomaticLayout().getEdgeSeparation()); + assertTrue(view.getAutomaticLayout().isVertices()); + } + } From 4b204f077877a24bcac363f5ecf0e129a0f9f4c5 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 24 Apr 2019 17:40:10 +0100 Subject: [PATCH 004/717] Added a helper method to get all views (except filtered views). --- .../src/com/structurizr/view/ViewSet.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/structurizr-core/src/com/structurizr/view/ViewSet.java b/structurizr-core/src/com/structurizr/view/ViewSet.java index e9c7420ee..df75e6aae 100644 --- a/structurizr-core/src/com/structurizr/view/ViewSet.java +++ b/structurizr-core/src/com/structurizr/view/ViewSet.java @@ -415,6 +415,25 @@ void setDeploymentViews(Set deploymentViews) { } } + /** + * Gets the set of all views (except filtered views). + * + * @return a Collection of View objects + */ + @JsonIgnore + public Collection getViews() { + HashSet views = new HashSet<>(); + + views.addAll(getSystemLandscapeViews()); + views.addAll(getSystemContextViews()); + views.addAll(getContainerViews()); + views.addAll(getComponentViews()); + views.addAll(getDynamicViews()); + views.addAll(getDeploymentViews()); + + return views; + } + void hydrate(Model model) { this.model = model; From 387e4f8f8b6d50e8343b6467e57bbe2841ec98c8 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 9 May 2019 10:53:02 +0100 Subject: [PATCH 005/717] Additional manifest headers for OSGi compatibility. --- structurizr-annotations/build.gradle | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 structurizr-annotations/build.gradle diff --git a/structurizr-annotations/build.gradle b/structurizr-annotations/build.gradle new file mode 100644 index 000000000..0e91be7ea --- /dev/null +++ b/structurizr-annotations/build.gradle @@ -0,0 +1,10 @@ +jar { + manifest { + attributes( + 'Bundle-ManifestVersion': "2", + 'Bundle-Name': "Structurizr for Java", + 'Bundle-SymbolicName': "com.structurizr.annotations", + 'Bundle-Version': version + ) + } +} \ No newline at end of file From af96cc411a2af626763c0898040d407e10bffbcd Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 21 May 2019 07:43:28 +0100 Subject: [PATCH 006/717] Added the Export-Package header to the manifest. --- structurizr-annotations/build.gradle | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/structurizr-annotations/build.gradle b/structurizr-annotations/build.gradle index 0e91be7ea..cede5329a 100644 --- a/structurizr-annotations/build.gradle +++ b/structurizr-annotations/build.gradle @@ -4,7 +4,8 @@ jar { 'Bundle-ManifestVersion': "2", 'Bundle-Name': "Structurizr for Java", 'Bundle-SymbolicName': "com.structurizr.annotations", - 'Bundle-Version': version + 'Bundle-Version': version, + 'Export-Package': "com.structurizr.annotation;version=\"$version\"" ) } } \ No newline at end of file From 50ad7a9db68c7bf31441448c3bddc5fcbe55d609 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 21 May 2019 07:49:43 +0100 Subject: [PATCH 007/717] Added a note about OSGi. --- docs/changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.md b/docs/changelog.md index ea67fb6c3..82f3f46ca 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -3,6 +3,7 @@ ## 1.3.1 (unreleased) - The automatic layout algorithm can now be configured on individual views. +- The structurizr-annotations library can now be more easily used with OSGi applications. ## 1.3.0 (3rd March 2019) From 310b778d346c85c147ac205e7a09dbf48e7a2adf Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 21 Jun 2019 14:37:58 +0100 Subject: [PATCH 008/717] Added an implementation of the PlantUML encoder, which is used in PlantUML image URLs. --- .../io/plantuml/PlantUMLEncoder.java | 73 +++++++++++++++++++ .../io/plantuml/PlantUMLEncoderTests.java | 14 ++++ 2 files changed, 87 insertions(+) create mode 100644 structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLEncoder.java create mode 100644 structurizr-plantuml/test/unit/com/structurizr/io/plantuml/PlantUMLEncoderTests.java diff --git a/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLEncoder.java b/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLEncoder.java new file mode 100644 index 000000000..91f95f80c --- /dev/null +++ b/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLEncoder.java @@ -0,0 +1,73 @@ +package com.structurizr.io.plantuml; + +import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; +import java.util.zip.Deflater; +import java.util.zip.DeflaterOutputStream; + +/** + * A Java implementation of http://plantuml.com/code-javascript-synchronous + * that uses Java's built-in Deflate algorithm. + */ +public class PlantUMLEncoder { + + public String encode(String plantUMLDefinition) throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION, true); + + DeflaterOutputStream dos = new DeflaterOutputStream(baos, deflater, true); + dos.write(plantUMLDefinition.getBytes(StandardCharsets.UTF_8)); + dos.finish(); + + return encode(baos.toByteArray()); + } + + private String encode(byte[] bytes) { + StringBuilder buf = new StringBuilder(); + for (int i = 0; i < bytes.length; i += 3) { + int b1 = (bytes[i]) & 0xFF; + int b2 = (i + 1 < bytes.length ? bytes[i + 1] : (byte)0) & 0xFF; + int b3 = (i + 2 < bytes.length ? bytes[i + 2] : (byte)0) & 0xFF; + + append3bytes(buf, b1, b2, b3); + } + + return buf.toString(); + } + + private char encode6bit(byte b) { + if (b < 10) { + return (char) ('0' + b); + } + b -= 10; + if (b < 26) { + return (char) ('A' + b); + } + b -= 26; + if (b < 26) { + return (char) ('a' + b); + } + b -= 26; + if (b == 0) { + return '-'; + } + if (b == 1) { + return '_'; + } + + return '?'; + } + + private void append3bytes(StringBuilder buf, int b1, int b2, int b3) { + int c1 = b1 >> 2; + int c2 = (b1 & 0x3) << 4 | b2 >> 4; + int c3 = (b2 & 0xF) << 2 | b3 >> 6; + int c4 = b3 & 0x3F; + + buf.append(encode6bit((byte)(c1 & 0x3F))); + buf.append(encode6bit((byte)(c2 & 0x3F))); + buf.append(encode6bit((byte)(c3 & 0x3F))); + buf.append(encode6bit((byte)(c4 & 0x3F))); + } + +} \ No newline at end of file diff --git a/structurizr-plantuml/test/unit/com/structurizr/io/plantuml/PlantUMLEncoderTests.java b/structurizr-plantuml/test/unit/com/structurizr/io/plantuml/PlantUMLEncoderTests.java new file mode 100644 index 000000000..557e36389 --- /dev/null +++ b/structurizr-plantuml/test/unit/com/structurizr/io/plantuml/PlantUMLEncoderTests.java @@ -0,0 +1,14 @@ +package com.structurizr.io.plantuml; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class PlantUMLEncoderTests { + + @Test + public void test() throws Exception { + assertEquals("SyfFqhLppCbCJbMmKiX8pSd91m00", new PlantUMLEncoder().encode("Bob->Alice : hello")); + } + +} \ No newline at end of file From 0129b4183bff84b8950089849becaf2b24220e27 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 28 Jun 2019 13:52:10 +0100 Subject: [PATCH 009/717] System landscape and system context diagrams now work in the same way re: the "enterprise boundary". --- .../io/plantuml/PlantUMLWriter.java | 48 ++++++++++--------- .../io/plantuml/PlantUMLWriterTests.java | 22 ++++----- 2 files changed, 36 insertions(+), 34 deletions(-) diff --git a/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLWriter.java b/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLWriter.java index a939bf06c..3e19db22b 100644 --- a/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLWriter.java +++ b/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLWriter.java @@ -221,24 +221,40 @@ public void write(View view, Writer writer) { } protected void write(SystemLandscapeView view, Writer writer) { + writeSystemLandscapeOrContextView(view, writer, view.isEnterpriseBoundaryVisible()); + } + + protected void write(SystemContextView view, Writer writer) { + writeSystemLandscapeOrContextView(view, writer, view.isEnterpriseBoundaryVisible()); + } + + private void writeSystemLandscapeOrContextView(View view, Writer writer, boolean showEnterpriseBoundary) { try { writeHeader(view, writer); + boolean enterpriseBoundaryVisible; + enterpriseBoundaryVisible = + showEnterpriseBoundary && + (view.getElements().stream().map(ElementView::getElement).anyMatch(e -> e instanceof Person && ((Person)e).getLocation() == Location.Internal) || + view.getElements().stream().map(ElementView::getElement).anyMatch(e -> e instanceof SoftwareSystem && ((SoftwareSystem)e).getLocation() == Location.Internal)); + view.getElements().stream() .map(ElementView::getElement) - .filter(e -> e instanceof Person && ((Person)e).getLocation() == Location.External) + .filter(e -> e instanceof Person && ((Person)e).getLocation() != Location.Internal) .sorted((e1, e2) -> e1.getName().compareTo(e2.getName())) .forEach(e -> write(view, e, writer, false)); view.getElements().stream() .map(ElementView::getElement) - .filter(e -> e instanceof SoftwareSystem && ((SoftwareSystem)e).getLocation() == Location.External) + .filter(e -> e instanceof SoftwareSystem && ((SoftwareSystem)e).getLocation() != Location.Internal) .sorted((e1, e2) -> e1.getName().compareTo(e2.getName())) .forEach(e -> write(view, e, writer, false)); - String name = view.getModel().getEnterprise() != null ? view.getModel().getEnterprise().getName() : "Enterprise"; - writer.write("package \"" + name + "\" {"); - writer.write(System.lineSeparator()); + if (enterpriseBoundaryVisible) { + String name = view.getModel().getEnterprise() != null ? view.getModel().getEnterprise().getName() : "Enterprise"; + writer.write("package \"" + name + "\" {"); + writer.write(System.lineSeparator()); + } view.getElements().stream() .map(ElementView::getElement) @@ -252,25 +268,11 @@ protected void write(SystemLandscapeView view, Writer writer) { .sorted((e1, e2) -> e1.getName().compareTo(e2.getName())) .forEach(e -> write(view, e, writer, true)); - writer.write("}"); - writer.write(System.lineSeparator()); - - writeRelationships(view, writer); - - writeFooter(writer); - } catch (IOException e) { - e.printStackTrace(); - } - } - - protected void write(SystemContextView view, Writer writer) { - try { - writeHeader(view, writer); + if (enterpriseBoundaryVisible) { + writer.write("}"); + writer.write(System.lineSeparator()); + } - view.getElements().stream() - .map(ElementView::getElement) - .sorted((e1, e2) -> e1.getName().compareTo(e2.getName())) - .forEach(e -> write(view, e, writer, false)); writeRelationships(view, writer); writeFooter(writer); diff --git a/structurizr-plantuml/test/unit/com/structurizr/io/plantuml/PlantUMLWriterTests.java b/structurizr-plantuml/test/unit/com/structurizr/io/plantuml/PlantUMLWriterTests.java index 14af8227d..968436668 100644 --- a/structurizr-plantuml/test/unit/com/structurizr/io/plantuml/PlantUMLWriterTests.java +++ b/structurizr-plantuml/test/unit/com/structurizr/io/plantuml/PlantUMLWriterTests.java @@ -239,7 +239,7 @@ private void populateWorkspace() { @Test public void test_toString_ThrowsAnException_WhenPassedANullWorkspace() throws Exception { try { - assertEquals(0, plantUMLWriter.toString(null).length); + assertEquals(0, plantUMLWriter.toString((Workspace)null).length); fail(); } catch (Exception e) { assertEquals("A workspace must be provided.", e.getMessage()); @@ -295,8 +295,6 @@ public void test_writeView_UsesTheOverridableViewTitle_WhenTheViewTitleIsSet() t " noteBackgroundColor #ffffff" + System.lineSeparator() + " noteBorderColor #707070" + System.lineSeparator() + "}" + System.lineSeparator() + - "package \"Enterprise\" {" + System.lineSeparator() + - "}" + System.lineSeparator() + "@enduml" + System.lineSeparator(), stringWriter.toString()); } @@ -499,14 +497,16 @@ public void test_writeView_changeDirection_whenSetDirectionAreChanged() throws E " An SMTP relay configured to" + System.lineSeparator() + " send emails to the users." + System.lineSeparator() + "]" + System.lineSeparator() + - "rectangle \"Software System\" <> as 2 #dddddd" + System.lineSeparator() + - "rectangle 1 <> #dddddd [" + System.lineSeparator() + - " User" + System.lineSeparator() + - " --" + System.lineSeparator() + - " A detailed description of the" + System.lineSeparator() + - " user to be displayed on the" + System.lineSeparator() + - " diagrams" + System.lineSeparator() + - "]" + System.lineSeparator() + + "package \"Some Enterprise\" {" + System.lineSeparator() + + " rectangle 1 <> #dddddd [" + System.lineSeparator() + + " User" + System.lineSeparator() + + " --" + System.lineSeparator() + + " A detailed description of the" + System.lineSeparator() + + " user to be displayed on the" + System.lineSeparator() + + " diagrams" + System.lineSeparator() + + " ]" + System.lineSeparator() + + " rectangle \"Software System\" <> as 2 #dddddd" + System.lineSeparator() + + "}" + System.lineSeparator() + "4 .[#707070].> 1 : Delivers e-mails to" + System.lineSeparator() + "2 .[#707070].> 4 : Sends e-mail using" + System.lineSeparator() + "1 .[#707070].> 2 : Uses" + System.lineSeparator() + From 9a55a71a21a1a8ce68e6f5854f2b3adc0dac437e Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 28 Jun 2019 13:52:44 +0100 Subject: [PATCH 010/717] Added a method to export a single view as a string. --- .../com/structurizr/io/plantuml/PlantUMLWriter.java | 11 +++++++++++ .../structurizr/io/plantuml/PlantUMLWriterTests.java | 10 ++++++++++ 2 files changed, 21 insertions(+) diff --git a/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLWriter.java b/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLWriter.java index 3e19db22b..6bec091ea 100644 --- a/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLWriter.java +++ b/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLWriter.java @@ -190,6 +190,17 @@ public String[] toString(Workspace workspace) { } } + /** + * Gets a single view as a PlantUML diagram definition. + * + * @param view the view to write + */ + public String toString(View view) { + StringWriter stringWriter = new StringWriter(); + write(view, stringWriter); + return stringWriter.toString(); + } + /** * Writes a single view as a PlantUML diagram definition, to the specified writer. * diff --git a/structurizr-plantuml/test/unit/com/structurizr/io/plantuml/PlantUMLWriterTests.java b/structurizr-plantuml/test/unit/com/structurizr/io/plantuml/PlantUMLWriterTests.java index 968436668..d29c8ae3e 100644 --- a/structurizr-plantuml/test/unit/com/structurizr/io/plantuml/PlantUMLWriterTests.java +++ b/structurizr-plantuml/test/unit/com/structurizr/io/plantuml/PlantUMLWriterTests.java @@ -236,6 +236,16 @@ private void populateWorkspace() { styles.addElementStyle(DATA_STORE_TAG).shape(Shape.Cylinder); } + @Test + public void test_toString_ThrowsAnException_WhenPassedANullView() throws Exception { + try { + plantUMLWriter.toString((View)null); + fail(); + } catch (Exception e) { + assertEquals("A view must be provided.", e.getMessage()); + } + } + @Test public void test_toString_ThrowsAnException_WhenPassedANullWorkspace() throws Exception { try { From 4dab93924f0100a9277d10643658f22cb55541e7 Mon Sep 17 00:00:00 2001 From: Tyler Van Gorder Date: Tue, 20 Aug 2019 14:07:55 -0700 Subject: [PATCH 011/717] Fixing version in example to use latest. --- docs/spring-petclinic.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/spring-petclinic.md b/docs/spring-petclinic.md index ff4bab38d..495cd06b9 100644 --- a/docs/spring-petclinic.md +++ b/docs/spring-petclinic.md @@ -31,8 +31,8 @@ With the Spring PetClinic application built, we now need to create a software ar Name | Description ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-client:1.0.0 | The Structurizr API client library. -com.structurizr:structurizr-spring:1.0.0 | The Spring component finder. +com.structurizr:structurizr-client:1.3.0 | The Structurizr API client library. +com.structurizr:structurizr-spring:1.3.0 | The Spring component finder. First we need to create a little boilerplate code to create a workspace and a model. From ea76b800bc9f2dabd1084fbc1ccfe3537b49a907 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 28 Aug 2019 14:05:02 +0100 Subject: [PATCH 012/717] Fixes a bug with the PlantUML and WebSequenceDiagram writers, where relationships were sorted incorrectly (alphabetically, rather than numerically). --- .../src/com/structurizr/view/DynamicView.java | 35 +++++++++- .../structurizr/view/DynamicViewTests.java | 68 +++++++++++++++++++ .../io/plantuml/PlantUMLWriter.java | 3 +- .../WebSequenceDiagramsWriter.java | 6 +- 4 files changed, 103 insertions(+), 9 deletions(-) diff --git a/structurizr-core/src/com/structurizr/view/DynamicView.java b/structurizr-core/src/com/structurizr/view/DynamicView.java index ab25318d9..b15d2fbcf 100644 --- a/structurizr-core/src/com/structurizr/view/DynamicView.java +++ b/structurizr-core/src/com/structurizr/view/DynamicView.java @@ -4,9 +4,7 @@ import com.structurizr.model.*; import javax.annotation.Nonnull; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import java.util.*; /** * A dynamic view, used to describe behaviour between static elements at runtime. @@ -222,4 +220,35 @@ protected boolean canBeRemoved(Element element) { return true; } + /** + * Gets the list of RelationshipView objects for this view, ordered by the order property. + * + * @return a List of RelationshipView objects + */ + public List getOrderedRelationships() { + List list = new LinkedList<>(getRelationships()); + boolean ordersAreNumeric = true; + + for (RelationshipView relationshipView : list) { + ordersAreNumeric = ordersAreNumeric && isNumeric(relationshipView.getOrder()); + } + + if (ordersAreNumeric) { + list.sort(Comparator.comparingDouble(o -> Double.parseDouble(o.getOrder()))); + } else { + list.sort(Comparator.comparing(RelationshipView::getOrder)); + } + + return list; + } + + private boolean isNumeric(String str) { + try { + Double.parseDouble(str); + return true; + } catch(NumberFormatException e){ + return false; + } + } + } \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java b/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java index 6c375dcc7..63cf24b54 100644 --- a/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java @@ -6,6 +6,8 @@ import org.junit.Before; import org.junit.Test; +import java.util.List; + import static org.junit.Assert.*; public class DynamicViewTests extends AbstractWorkspaceTestBase { @@ -276,4 +278,70 @@ public void test_parallelSequence() { assertEquals(1, view.getRelationships().stream().filter(r -> r.getOrder().equals("4")).count()); } + @Test + public void test_getOrderedRelationships_WhenTheOrderPropertyIsAnInteger() { + containerA1.uses(containerA2, "uses"); + DynamicView view = workspace.getViews().createDynamicView(softwareSystemA, "key", "Description"); + for (int i = 0; i < 10; i++) { + view.add(containerA1, containerA2); + } + + List relationships = view.getOrderedRelationships(); + assertEquals("1", relationships.get(0).getOrder()); + assertEquals("2", relationships.get(1).getOrder()); + assertEquals("3", relationships.get(2).getOrder()); + assertEquals("4", relationships.get(3).getOrder()); + assertEquals("5", relationships.get(4).getOrder()); + assertEquals("6", relationships.get(5).getOrder()); + assertEquals("7", relationships.get(6).getOrder()); + assertEquals("8", relationships.get(7).getOrder()); + assertEquals("9", relationships.get(8).getOrder()); + assertEquals("10", relationships.get(9).getOrder()); + } + + @Test + public void test_getOrderedRelationships_WhenTheOrderPropertyIsADecimal() { + containerA1.uses(containerA2, "uses"); + DynamicView view = workspace.getViews().createDynamicView(softwareSystemA, "key", "Description"); + for (int i = 0; i < 10; i++) { + RelationshipView relationshipView = view.add(containerA1, containerA2); + relationshipView.setOrder("1." + i); + } + + List relationships = view.getOrderedRelationships(); + assertEquals("1.0", relationships.get(0).getOrder()); + assertEquals("1.1", relationships.get(1).getOrder()); + assertEquals("1.2", relationships.get(2).getOrder()); + assertEquals("1.3", relationships.get(3).getOrder()); + assertEquals("1.4", relationships.get(4).getOrder()); + assertEquals("1.5", relationships.get(5).getOrder()); + assertEquals("1.6", relationships.get(6).getOrder()); + assertEquals("1.7", relationships.get(7).getOrder()); + assertEquals("1.8", relationships.get(8).getOrder()); + assertEquals("1.9", relationships.get(9).getOrder()); + } + + @Test + public void test_getOrderedRelationships_WhenTheOrderPropertyIsAString() { + String characters = "abcdefghij"; + containerA1.uses(containerA2, "uses"); + DynamicView view = workspace.getViews().createDynamicView(softwareSystemA, "key", "Description"); + for (int i = 0; i < 10; i++) { + RelationshipView relationshipView = view.add(containerA1, containerA2); + relationshipView.setOrder("1" + characters.charAt(i)); + } + + List relationships = view.getOrderedRelationships(); + assertEquals("1a", relationships.get(0).getOrder()); + assertEquals("1b", relationships.get(1).getOrder()); + assertEquals("1c", relationships.get(2).getOrder()); + assertEquals("1d", relationships.get(3).getOrder()); + assertEquals("1e", relationships.get(4).getOrder()); + assertEquals("1f", relationships.get(5).getOrder()); + assertEquals("1g", relationships.get(6).getOrder()); + assertEquals("1h", relationships.get(7).getOrder()); + assertEquals("1i", relationships.get(8).getOrder()); + assertEquals("1j", relationships.get(9).getOrder()); + } + } \ No newline at end of file diff --git a/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLWriter.java b/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLWriter.java index 6bec091ea..0b4426f87 100644 --- a/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLWriter.java +++ b/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLWriter.java @@ -373,8 +373,7 @@ protected void write(DynamicView view, Writer writer) { .sorted((e1, e2) -> e1.getName().compareTo(e2.getName())). forEach(e -> write(view, e, writer, false)); } - view.getRelationships().stream() - .sorted((rv1, rv2) -> (rv1.getOrder().compareTo(rv2.getOrder()))) + view.getOrderedRelationships() .forEach(relationship -> { try { writer.write( diff --git a/structurizr-websequencediagrams/src/com/structurizr/io/websequencediagrams/WebSequenceDiagramsWriter.java b/structurizr-websequencediagrams/src/com/structurizr/io/websequencediagrams/WebSequenceDiagramsWriter.java index fa85c8536..26f812960 100644 --- a/structurizr-websequencediagrams/src/com/structurizr/io/websequencediagrams/WebSequenceDiagramsWriter.java +++ b/structurizr-websequencediagrams/src/com/structurizr/io/websequencediagrams/WebSequenceDiagramsWriter.java @@ -9,6 +9,7 @@ import java.io.IOException; import java.io.StringWriter; import java.io.Writer; +import java.util.List; import java.util.Set; import java.util.TreeSet; @@ -56,10 +57,7 @@ private void write(DynamicView view, Writer writer) { writer.write(System.lineSeparator()); writer.write(System.lineSeparator()); - Set relationships = new TreeSet<>((rv1, rv2) -> rv1.getOrder().compareTo(rv2.getOrder())); - relationships.addAll(view.getRelationships()); - - for (RelationshipView relationshipView : relationships) { + for (RelationshipView relationshipView : view.getOrderedRelationships()) { Relationship r = relationshipView.getRelationship(); // Thing A->Thing B: Description writer.write(String.format("%s%s%s: %s", From b4d5584c92dfdd33df00bfa8f7c0591b7d08a179 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 28 Aug 2019 14:12:25 +0100 Subject: [PATCH 013/717] Updated the changelog to reflect the previous commit. --- docs/changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.md b/docs/changelog.md index 82f3f46ca..cc27f38cc 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -4,6 +4,7 @@ - The automatic layout algorithm can now be configured on individual views. - The structurizr-annotations library can now be more easily used with OSGi applications. +- Fixes a bug with the PlantUML and WebSequenceDiagram writers, where relationships were sorted incorrectly (alphabetically, rather than numerically). ## 1.3.0 (3rd March 2019) From cf1c2adb14d0c9a737211bf4767ab753e6f935af Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 31 Aug 2019 16:35:00 +0100 Subject: [PATCH 014/717] Removed additional method, and added an override instead. --- .../src/com/structurizr/view/DynamicView.java | 13 +++++++------ .../unit/com/structurizr/view/DynamicViewTests.java | 7 ++++--- .../com/structurizr/io/plantuml/PlantUMLWriter.java | 2 +- .../WebSequenceDiagramsWriter.java | 2 +- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/structurizr-core/src/com/structurizr/view/DynamicView.java b/structurizr-core/src/com/structurizr/view/DynamicView.java index b15d2fbcf..d55c0c858 100644 --- a/structurizr-core/src/com/structurizr/view/DynamicView.java +++ b/structurizr-core/src/com/structurizr/view/DynamicView.java @@ -221,12 +221,13 @@ protected boolean canBeRemoved(Element element) { } /** - * Gets the list of RelationshipView objects for this view, ordered by the order property. + * Gets the set of RelationshipView objects for this view, ordered by the order property. * - * @return a List of RelationshipView objects + * @return an ordered set of RelationshipView objects */ - public List getOrderedRelationships() { - List list = new LinkedList<>(getRelationships()); + @Override + public Set getRelationships() { + List list = new LinkedList<>(super.getRelationships()); boolean ordersAreNumeric = true; for (RelationshipView relationshipView : list) { @@ -234,12 +235,12 @@ public List getOrderedRelationships() { } if (ordersAreNumeric) { - list.sort(Comparator.comparingDouble(o -> Double.parseDouble(o.getOrder()))); + list.sort(Comparator.comparingDouble(rv -> Double.parseDouble(rv.getOrder()))); } else { list.sort(Comparator.comparing(RelationshipView::getOrder)); } - return list; + return new LinkedHashSet<>(list); } private boolean isNumeric(String str) { diff --git a/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java b/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java index 63cf24b54..e0d6d3ac6 100644 --- a/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java @@ -6,6 +6,7 @@ import org.junit.Before; import org.junit.Test; +import java.util.LinkedList; import java.util.List; import static org.junit.Assert.*; @@ -286,7 +287,7 @@ public void test_getOrderedRelationships_WhenTheOrderPropertyIsAnInteger() { view.add(containerA1, containerA2); } - List relationships = view.getOrderedRelationships(); + List relationships = new LinkedList<>(view.getRelationships()); assertEquals("1", relationships.get(0).getOrder()); assertEquals("2", relationships.get(1).getOrder()); assertEquals("3", relationships.get(2).getOrder()); @@ -308,7 +309,7 @@ public void test_getOrderedRelationships_WhenTheOrderPropertyIsADecimal() { relationshipView.setOrder("1." + i); } - List relationships = view.getOrderedRelationships(); + List relationships = new LinkedList<>(view.getRelationships()); assertEquals("1.0", relationships.get(0).getOrder()); assertEquals("1.1", relationships.get(1).getOrder()); assertEquals("1.2", relationships.get(2).getOrder()); @@ -331,7 +332,7 @@ public void test_getOrderedRelationships_WhenTheOrderPropertyIsAString() { relationshipView.setOrder("1" + characters.charAt(i)); } - List relationships = view.getOrderedRelationships(); + List relationships = new LinkedList<>(view.getRelationships()); assertEquals("1a", relationships.get(0).getOrder()); assertEquals("1b", relationships.get(1).getOrder()); assertEquals("1c", relationships.get(2).getOrder()); diff --git a/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLWriter.java b/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLWriter.java index 0b4426f87..79ce8c2d4 100644 --- a/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLWriter.java +++ b/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLWriter.java @@ -373,7 +373,7 @@ protected void write(DynamicView view, Writer writer) { .sorted((e1, e2) -> e1.getName().compareTo(e2.getName())). forEach(e -> write(view, e, writer, false)); } - view.getOrderedRelationships() + view.getRelationships() .forEach(relationship -> { try { writer.write( diff --git a/structurizr-websequencediagrams/src/com/structurizr/io/websequencediagrams/WebSequenceDiagramsWriter.java b/structurizr-websequencediagrams/src/com/structurizr/io/websequencediagrams/WebSequenceDiagramsWriter.java index 26f812960..e4d0900a1 100644 --- a/structurizr-websequencediagrams/src/com/structurizr/io/websequencediagrams/WebSequenceDiagramsWriter.java +++ b/structurizr-websequencediagrams/src/com/structurizr/io/websequencediagrams/WebSequenceDiagramsWriter.java @@ -57,7 +57,7 @@ private void write(DynamicView view, Writer writer) { writer.write(System.lineSeparator()); writer.write(System.lineSeparator()); - for (RelationshipView relationshipView : view.getOrderedRelationships()) { + for (RelationshipView relationshipView : view.getRelationships()) { Relationship r = relationshipView.getRelationship(); // Thing A->Thing B: Description writer.write(String.format("%s%s%s: %s", From b3d01d54041ab82ad2bd1a47a9c814c1984641ee Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 10 Sep 2019 14:46:22 +0100 Subject: [PATCH 015/717] Renamed some tests. --- .../test/unit/com/structurizr/view/DynamicViewTests.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java b/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java index e0d6d3ac6..c14c15088 100644 --- a/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java @@ -280,7 +280,7 @@ public void test_parallelSequence() { } @Test - public void test_getOrderedRelationships_WhenTheOrderPropertyIsAnInteger() { + public void test_getRelationships_WhenTheOrderPropertyIsAnInteger() { containerA1.uses(containerA2, "uses"); DynamicView view = workspace.getViews().createDynamicView(softwareSystemA, "key", "Description"); for (int i = 0; i < 10; i++) { @@ -301,7 +301,7 @@ public void test_getOrderedRelationships_WhenTheOrderPropertyIsAnInteger() { } @Test - public void test_getOrderedRelationships_WhenTheOrderPropertyIsADecimal() { + public void test_getRelationships_WhenTheOrderPropertyIsADecimal() { containerA1.uses(containerA2, "uses"); DynamicView view = workspace.getViews().createDynamicView(softwareSystemA, "key", "Description"); for (int i = 0; i < 10; i++) { @@ -323,7 +323,7 @@ public void test_getOrderedRelationships_WhenTheOrderPropertyIsADecimal() { } @Test - public void test_getOrderedRelationships_WhenTheOrderPropertyIsAString() { + public void test_getRelationships_WhenTheOrderPropertyIsAString() { String characters = "abcdefghij"; containerA1.uses(containerA2, "uses"); DynamicView view = workspace.getViews().createDynamicView(softwareSystemA, "key", "Description"); From 3b969c6758e18cc472a8a34f3fc0f2841e565eda Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 8 Oct 2019 11:41:21 +0100 Subject: [PATCH 016/717] Upgrade Jackson. --- structurizr-client/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structurizr-client/build.gradle b/structurizr-client/build.gradle index 3724b09a7..082dd536d 100644 --- a/structurizr-client/build.gradle +++ b/structurizr-client/build.gradle @@ -2,7 +2,7 @@ dependencies { compile project(':structurizr-core') - compile 'com.fasterxml.jackson.core:jackson-databind:2.4.0' + compile 'com.fasterxml.jackson.core:jackson-databind:2.9.5' compile 'org.apache.httpcomponents.client5:httpclient5:5.0-beta4' From 8afa1c3bd472035454d4399951cac999d5b29967 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 8 Oct 2019 12:10:10 +0100 Subject: [PATCH 017/717] Fixes a bug that allows relationships to be created between parents and children. --- docs/changelog.md | 1 + .../src/com/structurizr/model/Model.java | 47 +++++++------- .../com/structurizr/model/ModelTests.java | 62 +++++++++++++++++++ 3 files changed, 85 insertions(+), 25 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index cc27f38cc..a51ce3e6a 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -5,6 +5,7 @@ - The automatic layout algorithm can now be configured on individual views. - The structurizr-annotations library can now be more easily used with OSGi applications. - Fixes a bug with the PlantUML and WebSequenceDiagram writers, where relationships were sorted incorrectly (alphabetically, rather than numerically). +- Fixes a bug that allows relationships to be created between parents and children. ## 1.3.0 (3rd March 2019) diff --git a/structurizr-core/src/com/structurizr/model/Model.java b/structurizr-core/src/com/structurizr/model/Model.java index ee5150f2d..b988f611a 100644 --- a/structurizr-core/src/com/structurizr/model/Model.java +++ b/structurizr-core/src/com/structurizr/model/Model.java @@ -175,6 +175,10 @@ Relationship addRelationship(Element source, @Nonnull Element destination, Strin } + if (isChildOf(source, destination) || isChildOf(destination, source)) { + throw new IllegalArgumentException("Relationships cannot be added between parents and children."); + } + Relationship relationship = new Relationship(source, destination, description, technology, interactionStyle); if (addRelationship(relationship)) { return relationship; @@ -183,6 +187,23 @@ Relationship addRelationship(Element source, @Nonnull Element destination, Strin } } + private boolean isChildOf(Element e1, Element e2) { + if (e1 instanceof Person || e2 instanceof Person) { + return false; + } + + Element parent = e2.getParent(); + while (parent != null) { + if (parent.getId().equals(e1.getId())) { + return true; + } + + parent = parent.getParent(); + } + + return false; + } + private boolean addRelationship(Relationship relationship) { if (!relationship.getSource().has(relationship)) { relationship.setId(idGenerator.generateId(relationship)); @@ -509,31 +530,7 @@ private boolean propagatedRelationshipIsAllowed(Element source, Element destinat return false; } - if (source.getParent() != null) { - if (destination.equals(source.getParent())) { - return false; - } - - if (source.getParent().getParent() != null) { - if (destination.equals(source.getParent().getParent())) { - return false; - } - } - } - - if (destination.getParent() != null) { - if (source.equals(destination.getParent())) { - return false; - } - - if (destination.getParent().getParent() != null) { - if (source.equals(destination.getParent().getParent())) { - return false; - } - } - } - - return true; + return !(isChildOf(source, destination) || isChildOf(destination, source)); } /** diff --git a/structurizr-core/test/unit/com/structurizr/model/ModelTests.java b/structurizr-core/test/unit/com/structurizr/model/ModelTests.java index 38297209f..454df337b 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ModelTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ModelTests.java @@ -242,6 +242,68 @@ public void test_addRelationship_AllowsMultipleRelationshipsBetweenElements() { assertEquals(2, element1.getRelationships().size()); } + @Test + public void test_addRelationship_ThrowsAnException_WhenTheDestinationIsAChildOfTheSource() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); + Container container = softwareSystem.addContainer("Container", "", ""); + Component component = container.addComponent("Component", "", ""); + + try { + softwareSystem.uses(container, "Uses"); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("Relationships cannot be added between parents and children.", iae.getMessage()); + assertEquals(0, softwareSystem.getRelationships().size()); + } + + try { + container.uses(component, "Uses"); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("Relationships cannot be added between parents and children.", iae.getMessage()); + assertEquals(0, softwareSystem.getRelationships().size()); + } + + try { + softwareSystem.uses(component, "Uses"); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("Relationships cannot be added between parents and children.", iae.getMessage()); + assertEquals(0, softwareSystem.getRelationships().size()); + } + } + + @Test + public void test_addRelationship_ThrowsAnException_WhenTheSourceIsAChildOfTheDestination() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); + Container container = softwareSystem.addContainer("Container", "", ""); + Component component = container.addComponent("Component", "", ""); + + try { + container.uses(softwareSystem, "Uses"); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("Relationships cannot be added between parents and children.", iae.getMessage()); + assertEquals(0, softwareSystem.getRelationships().size()); + } + + try { + component.uses(container, "Uses"); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("Relationships cannot be added between parents and children.", iae.getMessage()); + assertEquals(0, softwareSystem.getRelationships().size()); + } + + try { + component.uses(softwareSystem, "Uses"); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("Relationships cannot be added between parents and children.", iae.getMessage()); + assertEquals(0, softwareSystem.getRelationships().size()); + } + } + @Test public void test_modifyRelationship_ThrowsAnException_WhenARelationshipIsNotSpecified() { try { From b91b364a07f76d04ca52d837c6e681d9859a10bb Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 23 Oct 2019 13:58:33 +0100 Subject: [PATCH 018/717] Added a way to create a collection of PlantUML diagrams, that include some metadata (e.g. view key). --- .../io/plantuml/PlantUMLDiagram.java | 27 ++++++++++++++++++ .../io/plantuml/PlantUMLWriter.java | 28 ++++++++++++++++--- .../io/plantuml/PlantUMLWriterTests.java | 15 ++++++++++ 3 files changed, 66 insertions(+), 4 deletions(-) create mode 100644 structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLDiagram.java diff --git a/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLDiagram.java b/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLDiagram.java new file mode 100644 index 000000000..4f7ee5cf1 --- /dev/null +++ b/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLDiagram.java @@ -0,0 +1,27 @@ +package com.structurizr.io.plantuml; + +public class PlantUMLDiagram { + + private String key; + private String name; + private String definition; + + PlantUMLDiagram(String key, String name, String definition) { + this.key = key; + this.name = name; + this.definition = definition; + } + + public String getKey() { + return key; + } + + public String getName() { + return name; + } + + public String getDefinition() { + return definition; + } + +} \ No newline at end of file diff --git a/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLWriter.java b/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLWriter.java index 79ce8c2d4..2f90a76e8 100644 --- a/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLWriter.java +++ b/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLWriter.java @@ -9,10 +9,7 @@ import java.io.StringWriter; import java.io.Writer; import java.net.URI; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import static java.lang.String.format; import static java.util.Collections.emptyList; @@ -201,6 +198,29 @@ public String toString(View view) { return stringWriter.toString(); } + /** + * Creates PlantUML diagram definitions based upon the specified workspace. + * + * @param workspace the workspace containing the views to be written + * @return a collection of PlantUML diagram definitions, one per view + */ + public Collection toPlantUMLDiagrams(Workspace workspace) { + if (workspace == null) { + throw new IllegalArgumentException("A workspace must be provided."); + } + + Collection diagrams = new ArrayList<>(); + + for (View view : workspace.getViews().getViews()) { + StringWriter stringWriter = new StringWriter(); + write(view, stringWriter); + + diagrams.add(new PlantUMLDiagram(view.getKey(), view.getName(), stringWriter.toString())); + } + + return diagrams; + } + /** * Writes a single view as a PlantUML diagram definition, to the specified writer. * diff --git a/structurizr-plantuml/test/unit/com/structurizr/io/plantuml/PlantUMLWriterTests.java b/structurizr-plantuml/test/unit/com/structurizr/io/plantuml/PlantUMLWriterTests.java index d29c8ae3e..a7692c98c 100644 --- a/structurizr-plantuml/test/unit/com/structurizr/io/plantuml/PlantUMLWriterTests.java +++ b/structurizr-plantuml/test/unit/com/structurizr/io/plantuml/PlantUMLWriterTests.java @@ -8,6 +8,7 @@ import java.io.StringWriter; import java.net.URI; +import java.util.Collection; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; @@ -284,6 +285,20 @@ public void test_toString_ReturnsAnArrayOfDiagramsWhenThereAreDiagrams() throws assertEquals(DEPLOYMENT_VIEW, diagrams[5]); } + @Test + public void test_toPlantUMLDiagrams_ReturnsAnCollectionOfDiagramsWhenThereAreDiagrams() throws Exception { + populateWorkspace(); + Collection diagrams = plantUMLWriter.toPlantUMLDiagrams(workspace); + assertEquals(6, diagrams.size()); + + assertEquals(SYSTEM_LANDSCAPE_VIEW, diagrams.stream().filter(d -> d.getKey().equals("enterpriseContext")).findFirst().get().getDefinition()); + assertEquals(SYSTEM_CONTEXT_VIEW, diagrams.stream().filter(d -> d.getKey().equals("systemContext")).findFirst().get().getDefinition()); + assertEquals(CONTAINER_VIEW, diagrams.stream().filter(d -> d.getKey().equals("containers")).findFirst().get().getDefinition()); + assertEquals(COMPONENT_VIEW, diagrams.stream().filter(d -> d.getKey().equals("components")).findFirst().get().getDefinition()); + assertEquals(DYNAMIC_VIEW, diagrams.stream().filter(d -> d.getKey().equals("dynamic")).findFirst().get().getDefinition()); + assertEquals(DEPLOYMENT_VIEW, diagrams.stream().filter(d -> d.getKey().equals("deployment")).findFirst().get().getDefinition()); + } + @Test public void test_writeView_UsesTheOverridableViewTitle_WhenTheViewTitleIsSet() throws Exception { workspace = new Workspace("", ""); From 2b80037e345d5c74a081a2060c2d7f08410acbab Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 24 Oct 2019 08:58:41 +0100 Subject: [PATCH 019/717] Added some workspace validation checks (initial commit, more checks will be added). --- .../com/structurizr/io/json/JsonReader.java | 2 + .../api/WorkspaceRulesValidationTests.java | 84 +++++++++++++++++ .../src/com/structurizr/Workspace.java | 9 ++ .../WorkspaceValidationException.java | 9 ++ .../src/com/structurizr/model/Element.java | 6 +- .../src/com/structurizr/model/Model.java | 36 +++++++ .../com/structurizr/model/Relationship.java | 4 +- .../src/com/structurizr/view/ViewSet.java | 93 +++++++++++++++++-- .../com/structurizr/view/ViewSetTests.java | 6 ++ 9 files changed, 238 insertions(+), 11 deletions(-) create mode 100644 structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java create mode 100644 structurizr-core/src/com/structurizr/WorkspaceValidationException.java diff --git a/structurizr-client/src/com/structurizr/io/json/JsonReader.java b/structurizr-client/src/com/structurizr/io/json/JsonReader.java index c59b23282..0dd5cf6c3 100644 --- a/structurizr-client/src/com/structurizr/io/json/JsonReader.java +++ b/structurizr-client/src/com/structurizr/io/json/JsonReader.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.structurizr.Workspace; +import com.structurizr.WorkspaceValidationException; import com.structurizr.io.WorkspaceReader; import com.structurizr.io.WorkspaceReaderException; @@ -32,6 +33,7 @@ public Workspace read(Reader reader) throws WorkspaceReaderException { return workspace; } catch (IOException ioe) { + ioe.printStackTrace(); throw new WorkspaceReaderException("Could not read JSON", ioe); } } diff --git a/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java b/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java new file mode 100644 index 000000000..2cb07d594 --- /dev/null +++ b/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java @@ -0,0 +1,84 @@ +package com.structurizr.api; + +import com.structurizr.WorkspaceValidationException; +import com.structurizr.util.WorkspaceUtils; +import org.junit.Test; + +import java.io.File; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +public class WorkspaceRulesValidationTests { + + private static final File PATH_TO_WORKSPACE_FILES = new File("test/integration/workspaceValidation"); + + @Test + public void test_exceptionThrown_WhenElementIdsAreNotUnique() throws Exception { + try { + WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "ElementIdsAreNotUnique.json")); + fail(); + } catch (WorkspaceValidationException we) { + assertEquals("The element /Software System 1 has a non-unique ID of 1.", we.getMessage()); + } + } + + @Test + public void test_exceptionThrown_WhenRelationshipIdsAreNotUnique() throws Exception { + try { + WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "RelationshipIdsAreNotUnique.json")); + fail(); + } catch (WorkspaceValidationException we) { + assertEquals("The relationship {1 | User | null} ---[Uses 2]---> {2 | Software System | null} has a non-unique ID of 3.", we.getMessage()); + } + } + + @Test + public void test_exceptionThrown_WhenViewKeysAreNotUnique() throws Exception { + try { + WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "ViewKeysAreNotUnique.json")); + fail(); + } catch (WorkspaceValidationException we) { + assertEquals("A view with the key key already exists.", we.getMessage()); + } + } + + @Test + public void test_exceptionThrown_WhenPeopleAndSoftwareSystemNamesAreNotUnique() throws Exception { + try { + WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "PeopleAndSoftwareSystemNamesAreNotUnique.json")); + fail(); + } catch (WorkspaceValidationException we) { + assertEquals("An element named Name already exists in this context.", we.getMessage()); + } + } + + @Test + public void test_exceptionThrown_WhenContainerNamesAreNotUnique() throws Exception { + try { + WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "ContainerNamesAreNotUnique.json")); + fail(); + } catch (WorkspaceValidationException we) { + assertEquals("An element named Container already exists in this context.", we.getMessage()); + } + } + + @Test + public void test_exceptionThrown_WhenComponentNamesAreNotUnique() throws Exception { + try { + WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "ComponentNamesAreNotUnique.json")); + fail(); + } catch (WorkspaceValidationException we) { + assertEquals("An element named Component already exists in this context.", we.getMessage()); + } + } + +// public static void main(String[] args) throws Exception { +// Workspace workspace = new Workspace("Name", "Description"); +// Model model = workspace.getModel(); +// ViewSet views = workspace.getViews(); +// +// WorkspaceUtils.saveWorkspaceToJson(workspace, new File (new File("structurizr-client/test/integration/workspaceValidation"), "x.json")); +// } + +} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/Workspace.java b/structurizr-core/src/com/structurizr/Workspace.java index be9613f20..87ac4adc7 100644 --- a/structurizr-core/src/com/structurizr/Workspace.java +++ b/structurizr-core/src/com/structurizr/Workspace.java @@ -9,6 +9,7 @@ import javax.annotation.Nonnull; import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.LinkedList; import java.util.List; @@ -110,6 +111,10 @@ private void hydrateModel() { Method hydrateMethod = Model.class.getDeclaredMethod("hydrate"); hydrateMethod.setAccessible(true); hydrateMethod.invoke(model); + } catch (InvocationTargetException ite) { + if (ite.getCause() != null && ite.getCause() instanceof WorkspaceValidationException) { + throw (WorkspaceValidationException)ite.getCause(); + } } catch (Exception e) { throw new RuntimeException(e); } @@ -120,6 +125,10 @@ private void hydrateViewSet() { Method hydrateMethod = ViewSet.class.getDeclaredMethod("hydrate", Model.class); hydrateMethod.setAccessible(true); hydrateMethod.invoke(viewSet, model); + } catch (InvocationTargetException ite) { + if (ite.getCause() != null && ite.getCause() instanceof WorkspaceValidationException) { + throw (WorkspaceValidationException)ite.getCause(); + } } catch (Exception e) { throw new RuntimeException(e); } diff --git a/structurizr-core/src/com/structurizr/WorkspaceValidationException.java b/structurizr-core/src/com/structurizr/WorkspaceValidationException.java new file mode 100644 index 000000000..7608dbb84 --- /dev/null +++ b/structurizr-core/src/com/structurizr/WorkspaceValidationException.java @@ -0,0 +1,9 @@ +package com.structurizr; + +public class WorkspaceValidationException extends RuntimeException { + + public WorkspaceValidationException(String message) { + super(message); + } + +} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/model/Element.java b/structurizr-core/src/com/structurizr/model/Element.java index ec4df843b..e5d708c35 100644 --- a/structurizr-core/src/com/structurizr/model/Element.java +++ b/structurizr-core/src/com/structurizr/model/Element.java @@ -182,8 +182,8 @@ public String toString() { @Override public int hashCode() { - if (name != null) { - return name.hashCode(); + if (getId() != null) { + return getId().hashCode(); } else { return super.hashCode(); } @@ -204,7 +204,7 @@ public boolean equals(Object o) { } Element element = (Element)o; - return getCanonicalName().equals(element.getCanonicalName()); + return getId().equals(element.getId()); } } \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/model/Model.java b/structurizr-core/src/com/structurizr/model/Model.java index b988f611a..f48a10023 100644 --- a/structurizr-core/src/com/structurizr/model/Model.java +++ b/structurizr-core/src/com/structurizr/model/Model.java @@ -1,6 +1,7 @@ package com.structurizr.model; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.structurizr.WorkspaceValidationException; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -217,12 +218,22 @@ private boolean addRelationship(Relationship relationship) { } private void addElementToInternalStructures(Element element) { + // check that the ID is unique + if (getElement(element.getId()) != null || getRelationship(element.getId()) != null) { + throw new WorkspaceValidationException("The element " + element.getCanonicalName() + " has a non-unique ID of " + element.getId() + "."); + } + elementsById.put(element.getId(), element); element.setModel(this); idGenerator.found(element.getId()); } private void addRelationshipToInternalStructures(Relationship relationship) { + // check that the ID is unique + if (getElement(relationship.getId()) != null || getRelationship(relationship.getId()) != null) { + throw new WorkspaceValidationException("The relationship " + relationship.toString() + " has a non-unique ID of " + relationship.getId() + "."); + } + relationshipsById.put(relationship.getId(), relationship); idGenerator.found(relationship.getId()); } @@ -332,6 +343,7 @@ void setDeploymentNodes(Set deploymentNodes) { void hydrate() { // add all of the elements to the model people.forEach(this::addElementToInternalStructures); + for (SoftwareSystem softwareSystem : softwareSystems) { addElementToInternalStructures(softwareSystem); for (Container container : softwareSystem.getContainers()) { @@ -348,6 +360,24 @@ void hydrate() { // now hydrate the relationships getElements().forEach(this::hydrateRelationships); + + // now check all of the element names are unique + Collection peopleAndSoftwareSystems = new ArrayList<>(); + peopleAndSoftwareSystems.addAll(people); + peopleAndSoftwareSystems.addAll(softwareSystems); + for (Element element : peopleAndSoftwareSystems) { + checkNameIsUnique(peopleAndSoftwareSystems, element.getName()); + } + + for (SoftwareSystem softwareSystem : softwareSystems) { + for (Container container : softwareSystem.getContainers()) { + checkNameIsUnique(softwareSystem.getContainers(), container.getName()); + + for (Component component : container.getComponents()) { + checkNameIsUnique(container.getComponents(), component.getName()); + } + } + } } private void hydrateDeploymentNode(DeploymentNode deploymentNode, DeploymentNode parent) { @@ -362,6 +392,12 @@ private void hydrateDeploymentNode(DeploymentNode deploymentNode, DeploymentNode } } + private void checkNameIsUnique(Collection elements, String name) { + if (elements.stream().filter(e -> e.getName().equalsIgnoreCase(name)).count() != 1) { + throw new WorkspaceValidationException("An element named " + name + " already exists in this context."); + } + } + private void hydrateRelationships(Element element) { for (Relationship relationship : element.getRelationships()) { relationship.setSource(getElement(relationship.getSourceId())); diff --git a/structurizr-core/src/com/structurizr/model/Relationship.java b/structurizr-core/src/com/structurizr/model/Relationship.java index c5881072e..cfe045323 100644 --- a/structurizr-core/src/com/structurizr/model/Relationship.java +++ b/structurizr-core/src/com/structurizr/model/Relationship.java @@ -153,8 +153,8 @@ public boolean equals(Object o) { Relationship that = (Relationship) o; if (!getDescription().equals(that.getDescription())) return false; - if (!getDestination().equals(that.getDestination())) return false; - if (!getSource().equals(that.getSource())) return false; + if (!getDestination().getCanonicalName().equals(that.getDestination().getCanonicalName())) return false; + if (!getSource().getCanonicalName().equals(that.getSource().getCanonicalName())) return false; return true; } diff --git a/structurizr-core/src/com/structurizr/view/ViewSet.java b/structurizr-core/src/com/structurizr/view/ViewSet.java index df75e6aae..f4bd9f86b 100644 --- a/structurizr-core/src/com/structurizr/view/ViewSet.java +++ b/structurizr-core/src/com/structurizr/view/ViewSet.java @@ -2,13 +2,16 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonSetter; +import com.structurizr.WorkspaceValidationException; import com.structurizr.model.Container; +import com.structurizr.model.Element; import com.structurizr.model.Model; import com.structurizr.model.SoftwareSystem; import com.structurizr.util.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Set; @@ -437,30 +440,73 @@ public Collection getViews() { void hydrate(Model model) { this.model = model; + checkViewKeysAreUnique(); + for (SystemLandscapeView view : systemLandscapeViews) { view.setModel(model); hydrateView(view); } for (SystemContextView view : systemContextViews) { - view.setSoftwareSystem(model.getSoftwareSystemWithId(view.getSoftwareSystemId())); + SoftwareSystem softwareSystem = model.getSoftwareSystemWithId(view.getSoftwareSystemId()); + if (softwareSystem == null) { + throw new WorkspaceValidationException( + String.format("The system context view with key %s is associated with a software system (id=%s), but that element does not exist in the model.", + view.getKey(), view.getSoftwareSystemId()) + ); + } + + view.setSoftwareSystem(softwareSystem); hydrateView(view); } for (ContainerView view : containerViews) { - view.setSoftwareSystem(model.getSoftwareSystemWithId(view.getSoftwareSystemId())); + SoftwareSystem softwareSystem = model.getSoftwareSystemWithId(view.getSoftwareSystemId()); + if (softwareSystem == null) { + throw new WorkspaceValidationException( + String.format("The container view with key %s is associated with a software system (id=%s), but that element does not exist in the model.", + view.getKey(), view.getSoftwareSystemId()) + ); + } + + view.setSoftwareSystem(softwareSystem); hydrateView(view); } for (ComponentView view : componentViews) { - view.setSoftwareSystem(model.getSoftwareSystemWithId(view.getSoftwareSystemId())); - view.setContainer(view.getSoftwareSystem().getContainerWithId(view.getContainerId())); + SoftwareSystem softwareSystem = model.getSoftwareSystemWithId(view.getSoftwareSystemId()); + if (softwareSystem == null) { + throw new WorkspaceValidationException( + String.format("The component view with key %s is associated with a software system (id=%s), but that element does not exist in the model.", + view.getKey(), view.getSoftwareSystemId()) + ); + } + + view.setSoftwareSystem(softwareSystem); + + Container container = softwareSystem.getContainerWithId(view.getContainerId()); + if (container == null) { + throw new WorkspaceValidationException( + String.format("The component view with key %s is associated with a container (id=%s), but that element does not exist in the model.", + view.getKey(), view.getContainerId()) + ); + } + + view.setContainer(container); hydrateView(view); } for (DynamicView view : dynamicViews) { if (!isNullOrEmpty(view.getElementId())) { - view.setElement(model.getElement(view.getElementId())); + Element element = model.getElement(view.getElementId()); + if (element == null) { + throw new WorkspaceValidationException( + String.format("The dynamic view with key %s is associated with an element (id=%s), but that element does not exist in the model.", + view.getKey(), view.getElementId()) + ); + } + + view.setElement(element); } view.setModel(model); @@ -469,7 +515,15 @@ void hydrate(Model model) { for (DeploymentView view : deploymentViews) { if (!isNullOrEmpty(view.getSoftwareSystemId())) { - view.setSoftwareSystem(model.getSoftwareSystemWithId(view.getSoftwareSystemId())); + SoftwareSystem softwareSystem = model.getSoftwareSystemWithId(view.getSoftwareSystemId()); + if (softwareSystem == null) { + throw new WorkspaceValidationException( + String.format("The deployment view with key %s is associated with a software system (id=%s), but that element does not exist in the model.", + view.getKey(), view.getSoftwareSystemId()) + ); + } + + view.setSoftwareSystem(softwareSystem); } view.setModel(model); @@ -493,6 +547,33 @@ private void hydrateView(View view) { } } + private void checkViewKeysAreUnique() { + Set keys = new HashSet<>(); + Collection views = new ArrayList<>(); + views.addAll(systemLandscapeViews); + views.addAll(systemContextViews); + views.addAll(containerViews); + views.addAll(componentViews); + views.addAll(dynamicViews); + views.addAll(deploymentViews); + + for (View view : views) { + if (keys.contains(view.getKey())) { + throw new WorkspaceValidationException("A view with the key " + view.getKey() + " already exists."); + } else { + keys.add(view.getKey()); + } + } + + for (FilteredView filteredView : filteredViews) { + if (keys.contains(filteredView.getKey())) { + throw new WorkspaceValidationException("A view with the key " + filteredView.getKey() + " already exists."); + } else { + keys.add(filteredView.getKey()); + } + } + } + /** * Gets the configuration object associated with this set of views. * diff --git a/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java b/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java index 651fa699f..28f5af9b1 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java @@ -879,32 +879,38 @@ public void test_hydrate() { views.setSystemLandscapeViews(Collections.singleton(systemLandscapeView)); SystemContextView systemContextView = new SystemContextView(); + systemContextView.setKey("systemContext"); systemContextView.setSoftwareSystemId(softwareSystem.getId()); systemContextView.setElements(elementViewsFor(softwareSystem)); views.setSystemContextViews(Collections.singleton(systemContextView)); ContainerView containerView = new ContainerView(); + containerView.setKey("containers"); containerView.setSoftwareSystemId(softwareSystem.getId()); containerView.setElements(elementViewsFor(container)); views.setContainerViews(Collections.singleton(containerView)); ComponentView componentView = new ComponentView(); + componentView.setKey("components"); componentView.setSoftwareSystemId(softwareSystem.getId()); componentView.setContainerId(container.getId()); componentView.setElements(elementViewsFor(component)); views.setComponentViews(Collections.singleton(componentView)); DynamicView dynamicView = new DynamicView(); + dynamicView.setKey("dynamic"); dynamicView.setElementId(softwareSystem.getId()); dynamicView.setElements(elementViewsFor(component)); views.setDynamicViews(Collections.singleton(dynamicView)); DeploymentView deploymentView = new DeploymentView(); + deploymentView.setKey("deployment"); deploymentView.setSoftwareSystemId(softwareSystem.getId()); deploymentView.setElements(elementViewsFor(deploymentNode, containerInstance)); views.setDeploymentViews(Collections.singleton(deploymentView)); FilteredView filteredView = new FilteredView(); + filteredView.setKey("filtered"); filteredView.setBaseViewKey(systemLandscapeView.getKey()); views.setFilteredViews(Collections.singleton(filteredView)); From 5375ed245d137946c2677495ef4dc7baebb3c04d Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 24 Oct 2019 09:05:38 +0100 Subject: [PATCH 020/717] And the test JSON files. --- .gitignore | 2 - .../ComponentNamesAreNotUnique.json | 38 ++++++++++++++++ .../ContainerNamesAreNotUnique.json | 31 +++++++++++++ .../ElementIdsAreNotUnique.json | 27 ++++++++++++ ...pleAndSoftwareSystemNamesAreNotUnique.json | 28 ++++++++++++ .../RelationshipIdsAreNotUnique.json | 43 +++++++++++++++++++ .../ViewKeysAreNotUnique.json | 22 ++++++++++ 7 files changed, 189 insertions(+), 2 deletions(-) create mode 100644 structurizr-client/test/integration/workspaceValidation/ComponentNamesAreNotUnique.json create mode 100644 structurizr-client/test/integration/workspaceValidation/ContainerNamesAreNotUnique.json create mode 100644 structurizr-client/test/integration/workspaceValidation/ElementIdsAreNotUnique.json create mode 100644 structurizr-client/test/integration/workspaceValidation/PeopleAndSoftwareSystemNamesAreNotUnique.json create mode 100644 structurizr-client/test/integration/workspaceValidation/RelationshipIdsAreNotUnique.json create mode 100644 structurizr-client/test/integration/workspaceValidation/ViewKeysAreNotUnique.json diff --git a/.gitignore b/.gitignore index 5547fea44..7068eac3c 100644 --- a/.gitignore +++ b/.gitignore @@ -27,5 +27,3 @@ bin # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* -# Structurizr workspace archives -*.json diff --git a/structurizr-client/test/integration/workspaceValidation/ComponentNamesAreNotUnique.json b/structurizr-client/test/integration/workspaceValidation/ComponentNamesAreNotUnique.json new file mode 100644 index 000000000..4ff7867c0 --- /dev/null +++ b/structurizr-client/test/integration/workspaceValidation/ComponentNamesAreNotUnique.json @@ -0,0 +1,38 @@ +{ + "id" : 0, + "name" : "Name", + "description" : "Description", + "configuration" : { }, + "model" : { + "softwareSystems" : [ { + "id" : "1", + "tags" : "Element,Software System", + "name" : "Software System", + "location" : "Unspecified", + "containers" : [ { + "id" : "2", + "tags" : "Element,Container", + "name" : "Container", + "components" : [ { + "id" : "3", + "tags" : "Element,Component", + "name" : "Component", + "size" : 0 + }, { + "id" : "4", + "tags" : "Element,Component", + "name" : "Component", + "size" : 0 + } ] + } ] + } ] + }, + "documentation" : { }, + "views" : { + "configuration" : { + "branding" : { }, + "styles" : { }, + "terminology" : { } + } + } +} \ No newline at end of file diff --git a/structurizr-client/test/integration/workspaceValidation/ContainerNamesAreNotUnique.json b/structurizr-client/test/integration/workspaceValidation/ContainerNamesAreNotUnique.json new file mode 100644 index 000000000..ac7bb09a2 --- /dev/null +++ b/structurizr-client/test/integration/workspaceValidation/ContainerNamesAreNotUnique.json @@ -0,0 +1,31 @@ +{ + "id" : 0, + "name" : "Name", + "description" : "Description", + "configuration" : { }, + "model" : { + "softwareSystems" : [ { + "id" : "1", + "tags" : "Element,Software System", + "name" : "Software System", + "location" : "Unspecified", + "containers" : [ { + "id" : "2", + "tags" : "Element,Container", + "name" : "Container" + }, { + "id" : "3", + "tags" : "Element,Container", + "name" : "Container" + } ] + } ] + }, + "documentation" : { }, + "views" : { + "configuration" : { + "branding" : { }, + "styles" : { }, + "terminology" : { } + } + } +} \ No newline at end of file diff --git a/structurizr-client/test/integration/workspaceValidation/ElementIdsAreNotUnique.json b/structurizr-client/test/integration/workspaceValidation/ElementIdsAreNotUnique.json new file mode 100644 index 000000000..671db2c38 --- /dev/null +++ b/structurizr-client/test/integration/workspaceValidation/ElementIdsAreNotUnique.json @@ -0,0 +1,27 @@ +{ + "id" : 0, + "name" : "Name", + "description" : "Description", + "configuration" : { }, + "model" : { + "softwareSystems" : [ { + "id" : "1", + "tags" : "Element,Software System", + "name" : "Software System 1", + "location" : "Unspecified" + }, { + "id" : "1", + "tags" : "Element,Software System", + "name" : "Software System 2", + "location" : "Unspecified" + } ] + }, + "documentation" : { }, + "views" : { + "configuration" : { + "branding" : { }, + "styles" : { }, + "terminology" : { } + } + } +} \ No newline at end of file diff --git a/structurizr-client/test/integration/workspaceValidation/PeopleAndSoftwareSystemNamesAreNotUnique.json b/structurizr-client/test/integration/workspaceValidation/PeopleAndSoftwareSystemNamesAreNotUnique.json new file mode 100644 index 000000000..345b1812b --- /dev/null +++ b/structurizr-client/test/integration/workspaceValidation/PeopleAndSoftwareSystemNamesAreNotUnique.json @@ -0,0 +1,28 @@ +{ + "id" : 0, + "name" : "Name", + "description" : "Description", + "configuration" : { }, + "model" : { + "people" : [ { + "id" : "1", + "tags" : "Element,Person", + "name" : "Name", + "location" : "Unspecified" + } ], + "softwareSystems" : [ { + "id" : "2", + "tags" : "Element,Software System", + "name" : "Name", + "location" : "Unspecified" + } ] + }, + "documentation" : { }, + "views" : { + "configuration" : { + "branding" : { }, + "styles" : { }, + "terminology" : { } + } + } +} \ No newline at end of file diff --git a/structurizr-client/test/integration/workspaceValidation/RelationshipIdsAreNotUnique.json b/structurizr-client/test/integration/workspaceValidation/RelationshipIdsAreNotUnique.json new file mode 100644 index 000000000..a756781c1 --- /dev/null +++ b/structurizr-client/test/integration/workspaceValidation/RelationshipIdsAreNotUnique.json @@ -0,0 +1,43 @@ +{ + "id" : 0, + "name" : "Name", + "description" : "Description", + "configuration" : { }, + "model" : { + "people" : [ { + "id" : "1", + "tags" : "Element,Person", + "name" : "User", + "relationships" : [ { + "id" : "3", + "tags" : "Relationship,Synchronous", + "sourceId" : "1", + "destinationId" : "2", + "description" : "Uses 1", + "interactionStyle" : "Synchronous" + }, { + "id" : "3", + "tags" : "Relationship,Synchronous", + "sourceId" : "1", + "destinationId" : "2", + "description" : "Uses 2", + "interactionStyle" : "Synchronous" + } ], + "location" : "Unspecified" + } ], + "softwareSystems" : [ { + "id" : "2", + "tags" : "Element,Software System", + "name" : "Software System", + "location" : "Unspecified" + } ] + }, + "documentation" : { }, + "views" : { + "configuration" : { + "branding" : { }, + "styles" : { }, + "terminology" : { } + } + } +} \ No newline at end of file diff --git a/structurizr-client/test/integration/workspaceValidation/ViewKeysAreNotUnique.json b/structurizr-client/test/integration/workspaceValidation/ViewKeysAreNotUnique.json new file mode 100644 index 000000000..d3732b727 --- /dev/null +++ b/structurizr-client/test/integration/workspaceValidation/ViewKeysAreNotUnique.json @@ -0,0 +1,22 @@ +{ + "id" : 0, + "name" : "Name", + "description" : "Description", + "configuration" : { }, + "model" : { }, + "documentation" : { }, + "views" : { + "systemLandscapeViews" : [ { + "key" : "key", + "enterpriseBoundaryVisible" : true + }, { + "key" : "key", + "enterpriseBoundaryVisible" : true + } ], + "configuration" : { + "branding" : { }, + "styles" : { }, + "terminology" : { } + } + } +} \ No newline at end of file From 6be569ca6f643a4a5bb86c8a29995b5c064bcae5 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 24 Oct 2019 09:09:00 +0100 Subject: [PATCH 021/717] A little tidy up. --- .../src/com/structurizr/api/StructurizrClient.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/structurizr-client/src/com/structurizr/api/StructurizrClient.java b/structurizr-client/src/com/structurizr/api/StructurizrClient.java index 3fa5ff32c..b40e33039 100644 --- a/structurizr-client/src/com/structurizr/api/StructurizrClient.java +++ b/structurizr-client/src/com/structurizr/api/StructurizrClient.java @@ -107,7 +107,7 @@ public StructurizrClient(String apiKey, String apiSecret) { /** * Creates a new Structurizr client with the specified API URL, key and secret. * - * @param url the URL of your structurizr web instance + * @param url the URL of your Structurizr instance * @param apiKey the API key of your workspace * @param apiSecret the API secret of your workspace */ @@ -246,13 +246,12 @@ private boolean manageLockForWorkspace(long workspaceId, boolean lock) throws St debugResponse(response); String responseText = EntityUtils.toString(response.getEntity()); + ApiResponse apiResponse = ApiResponse.parse(responseText); + log.info(responseText); + if (response.getCode() == HttpStatus.SC_OK) { - ApiResponse apiResponse = ApiResponse.parse(responseText); - log.info(responseText); return apiResponse.isSuccess(); } else { - ApiResponse apiResponse = ApiResponse.parse(responseText); - log.info(responseText); throw new StructurizrClientException(apiResponse.getMessage()); } } @@ -265,7 +264,7 @@ private boolean manageLockForWorkspace(long workspaceId, boolean lock) throws St /** * Gets the workspace with the given ID. * - * @param workspaceId the ID of your workspace + * @param workspaceId the workspace ID * @return a Workspace instance * @throws StructurizrClientException if there are problems related to the network, authorization, JSON deserialization, etc */ @@ -314,7 +313,7 @@ public Workspace getWorkspace(long workspaceId) throws StructurizrClientExceptio /** * Updates the given workspace. * - * @param workspaceId the ID of your workspace + * @param workspaceId the workspace ID * @param workspace the workspace instance to update * @throws StructurizrClientException if there are problems related to the network, authorization, JSON serialization, etc */ From 7555f860b866ca95bb840effeac0dfedd0a72862 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 24 Oct 2019 09:40:51 +0100 Subject: [PATCH 022/717] Changed how element equality is calculated ... since the model only has a single instance of any element, there's no need to override hashcode/equals. --- .../StructurizrClientIntegrationTests.java | 11 ++++---- .../api/WorkspaceRulesValidationTests.java | 6 ++--- .../src/com/structurizr/model/Element.java | 27 ------------------- .../src/com/structurizr/view/View.java | 2 +- .../com/structurizr/model/ElementTests.java | 8 ------ 5 files changed, 9 insertions(+), 45 deletions(-) diff --git a/structurizr-client/test/integration/com/structurizr/api/StructurizrClientIntegrationTests.java b/structurizr-client/test/integration/com/structurizr/api/StructurizrClientIntegrationTests.java index f72349861..d0c16ef78 100644 --- a/structurizr-client/test/integration/com/structurizr/api/StructurizrClientIntegrationTests.java +++ b/structurizr-client/test/integration/com/structurizr/api/StructurizrClientIntegrationTests.java @@ -15,8 +15,7 @@ import java.io.File; import java.io.FileReader; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; public class StructurizrClientIntegrationTests { @@ -63,8 +62,8 @@ public void test_putAndGetWorkspace_WithoutEncryption() throws Exception { structurizrClient.putWorkspace(20081, workspace); workspace = structurizrClient.getWorkspace(20081); - assertTrue(workspace.getModel().contains(softwareSystem)); - assertTrue(workspace.getModel().contains(person)); + assertNotNull(workspace.getModel().getSoftwareSystemWithName("Software System")); + assertNotNull(workspace.getModel().getPersonWithName("Person")); assertEquals(1, workspace.getModel().getRelationships().size()); assertEquals(1, workspace.getViews().getSystemContextViews().size()); @@ -90,8 +89,8 @@ public void test_putAndGetWorkspace_WithEncryption() throws Exception { structurizrClient.putWorkspace(20081, workspace); workspace = structurizrClient.getWorkspace(20081); - assertTrue(workspace.getModel().contains(softwareSystem)); - assertTrue(workspace.getModel().contains(person)); + assertNotNull(workspace.getModel().getSoftwareSystemWithName("Software System")); + assertNotNull(workspace.getModel().getPersonWithName("Person")); assertEquals(1, workspace.getModel().getRelationships().size()); assertEquals(1, workspace.getViews().getSystemContextViews().size()); diff --git a/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java b/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java index 2cb07d594..1d8face7a 100644 --- a/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java +++ b/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java @@ -6,8 +6,7 @@ import java.io.File; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; public class WorkspaceRulesValidationTests { @@ -19,7 +18,8 @@ public void test_exceptionThrown_WhenElementIdsAreNotUnique() throws Exception { WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "ElementIdsAreNotUnique.json")); fail(); } catch (WorkspaceValidationException we) { - assertEquals("The element /Software System 1 has a non-unique ID of 1.", we.getMessage()); + assertTrue(we.getMessage().startsWith("The element /Software System ")); + assertTrue(we.getMessage().endsWith(" has a non-unique ID of 1.")); } } diff --git a/structurizr-core/src/com/structurizr/model/Element.java b/structurizr-core/src/com/structurizr/model/Element.java index e5d708c35..384d1379d 100644 --- a/structurizr-core/src/com/structurizr/model/Element.java +++ b/structurizr-core/src/com/structurizr/model/Element.java @@ -180,31 +180,4 @@ public String toString() { return "{" + getId() + " | " + getName() + " | " + getDescription() + "}"; } - @Override - public int hashCode() { - if (getId() != null) { - return getId().hashCode(); - } else { - return super.hashCode(); - } - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (o == null || !(o instanceof Element)) { - return false; - } - - if (!this.getClass().equals(o.getClass())) { - return false; - } - - Element element = (Element)o; - return getId().equals(element.getId()); - } - } \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/view/View.java b/structurizr-core/src/com/structurizr/view/View.java index 039530669..0ecf3db0f 100644 --- a/structurizr-core/src/com/structurizr/view/View.java +++ b/structurizr-core/src/com/structurizr/view/View.java @@ -384,7 +384,7 @@ void copyLayoutInformationFrom(@Nonnull View source) { private ElementView findElementView(ElementView sourceElementView) { for (ElementView elementView : getElements()) { - if (elementView.getElement().equals(sourceElementView.getElement())) { + if (elementView.getElement().getCanonicalName().equals(sourceElementView.getElement().getCanonicalName())) { return elementView; } } diff --git a/structurizr-core/test/unit/com/structurizr/model/ElementTests.java b/structurizr-core/test/unit/com/structurizr/model/ElementTests.java index 7fb03df2c..1d55cb1a6 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ElementTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ElementTests.java @@ -231,14 +231,6 @@ public void test_equals_ReturnsFalse_WhenTheTwoElementsHaveTheSameCanonicalNameB assertFalse(softwareSystem.equals(person)); } - @Test - public void test_equals_ReturnsTrue_WhenTheAnObjectWithTheSameCanonicalNameIsPassed() { - SoftwareSystem softwareSystem1 = new Workspace("", "").getModel().addSoftwareSystem("System 1", ""); - SoftwareSystem softwareSystem2 = new Workspace("", "").getModel().addSoftwareSystem("System 1", ""); - assertTrue(softwareSystem1.equals(softwareSystem2)); - assertTrue(softwareSystem2.equals(softwareSystem1)); - } - @Test public void test_setUrl() { Element element = model.addSoftwareSystem("Name", "Description"); From 786f7dbc584220ab89ac8a11bbf1a0c0fdc66157 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 24 Oct 2019 11:14:55 +0100 Subject: [PATCH 023/717] Typo. --- structurizr-core/src/com/structurizr/model/SoftwareSystem.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structurizr-core/src/com/structurizr/model/SoftwareSystem.java b/structurizr-core/src/com/structurizr/model/SoftwareSystem.java index 60911aceb..6ef721569 100644 --- a/structurizr-core/src/com/structurizr/model/SoftwareSystem.java +++ b/structurizr-core/src/com/structurizr/model/SoftwareSystem.java @@ -79,7 +79,7 @@ void setContainers(Set containers) { * * @param name the name of the container (e.g. "Web Application") * @param description a short description/list of responsibilities - * @param technology the technoogy choice (e.g. "Spring MVC", "Java EE", etc) + * @param technology the technology choice (e.g. "Spring MVC", "Java EE", etc) * @return the newly created Container instance added to the model (or null) * @throws IllegalArgumentException if a container with the same name exists already */ From a91c12215f9d066622c4851a72000456152bb4f0 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 24 Oct 2019 11:18:12 +0100 Subject: [PATCH 024/717] Added an initial version of a Mermaid writer. --- settings.gradle | 1 + structurizr-examples/build.gradle | 1 + structurizr-mermaid/build.gradle | 22 + .../io/mermaid/MermaidEncoder.java | 54 ++ .../structurizr/io/mermaid/MermaidWriter.java | 519 ++++++++++++++++++ .../io/mermaid/MermaidEncoderTests.java | 21 + .../io/mermaid/MermaidWriterTests.java | 12 + 7 files changed, 630 insertions(+) create mode 100644 structurizr-mermaid/build.gradle create mode 100644 structurizr-mermaid/src/com/structurizr/io/mermaid/MermaidEncoder.java create mode 100644 structurizr-mermaid/src/com/structurizr/io/mermaid/MermaidWriter.java create mode 100644 structurizr-mermaid/test/unit/com/structurizr/io/mermaid/MermaidEncoderTests.java create mode 100644 structurizr-mermaid/test/unit/com/structurizr/io/mermaid/MermaidWriterTests.java diff --git a/settings.gradle b/settings.gradle index 03240d858..268d5b6fd 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,6 +5,7 @@ include 'structurizr-analysis' include 'structurizr-annotations' include 'structurizr-client' include 'structurizr-core' +include 'structurizr-mermaid' include 'structurizr-plantuml' include 'structurizr-dot' include 'structurizr-examples' diff --git a/structurizr-examples/build.gradle b/structurizr-examples/build.gradle index 9b5390f95..85f5ecaac 100644 --- a/structurizr-examples/build.gradle +++ b/structurizr-examples/build.gradle @@ -3,6 +3,7 @@ dependencies { compile project(':structurizr-annotations') compile project(':structurizr-client') compile project(':structurizr-dot') + compile project(':structurizr-mermaid') compile project(':structurizr-plantuml') compile project(':structurizr-spring') diff --git a/structurizr-mermaid/build.gradle b/structurizr-mermaid/build.gradle new file mode 100644 index 000000000..f6bb34b2d --- /dev/null +++ b/structurizr-mermaid/build.gradle @@ -0,0 +1,22 @@ +dependencies { + + compile project(':structurizr-core') + compile 'com.fasterxml.jackson.core:jackson-databind:2.4.0' + + testCompile 'junit:junit:4.12' + testCompile 'org.assertj:assertj-core:3.4.0' + +} + +sourceSets { + main { + java { + srcDir 'src' + } + } + test { + java { + srcDir 'test/unit' + } + } +} \ No newline at end of file diff --git a/structurizr-mermaid/src/com/structurizr/io/mermaid/MermaidEncoder.java b/structurizr-mermaid/src/com/structurizr/io/mermaid/MermaidEncoder.java new file mode 100644 index 000000000..7672bda62 --- /dev/null +++ b/structurizr-mermaid/src/com/structurizr/io/mermaid/MermaidEncoder.java @@ -0,0 +1,54 @@ +package com.structurizr.io.mermaid; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +public class MermaidEncoder { + + public String encode(String mermaidDefinition) throws Exception { + ObjectMapper objectMapper = new ObjectMapper(); + String json = objectMapper.writeValueAsString(new MermaidCode(mermaidDefinition)); + String base64 = Base64.getEncoder().encodeToString(json.getBytes(StandardCharsets.UTF_8)); + + // this encoded version is used in URLs so: + // - strip trailing = characters + base64 = base64.replaceAll("=", ""); + + // and switch + characters + base64 = base64.replaceAll("\\+", "-"); + + return base64; + } + +} + +class MermaidCode { + + private String code; + private Mermaid mermaid = new Mermaid(); + + public MermaidCode(String code) { + this.code = code; + } + + public String getCode() { + return code; + } + + public Mermaid getMermaid() { + return mermaid; + } + +} + +class Mermaid { + + private String theme = "default"; + + public String getTheme() { + return theme; + } + +} \ No newline at end of file diff --git a/structurizr-mermaid/src/com/structurizr/io/mermaid/MermaidWriter.java b/structurizr-mermaid/src/com/structurizr/io/mermaid/MermaidWriter.java new file mode 100644 index 000000000..1dbc27728 --- /dev/null +++ b/structurizr-mermaid/src/com/structurizr/io/mermaid/MermaidWriter.java @@ -0,0 +1,519 @@ +package com.structurizr.io.mermaid; + +import com.structurizr.Workspace; +import com.structurizr.model.*; +import com.structurizr.util.StringUtils; +import com.structurizr.view.*; + +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import java.net.URI; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import static java.lang.String.format; +import static java.util.Collections.emptyList; + +/** + * A writer that outputs diagram definitions that can be used to create diagrams + * using mermaid (https://mermaidjs.github.io). + * + * System landscape, system context, container, component, dynamic and deployment diagrams are supported. + * Deployment node -> deployment node relationships are not rendered. + */ +public class MermaidWriter { + + private boolean useSequenceDiagrams = false; + + /** + * Creates a new PlantUMLWriter, with some default skin params. + */ + public MermaidWriter() { + } + + public boolean isUseSequenceDiagrams() { + return useSequenceDiagrams; + } + + public void setUseSequenceDiagrams(boolean useSequenceDiagrams) { + this.useSequenceDiagrams = useSequenceDiagrams; + } + + /** + * Writes the views in the given workspace as PlantUML definitions, to the specified writer. + * + * @param workspace the workspace containing the views to be written + * @param writer the Writer to write to + */ + public void write(Workspace workspace, Writer writer) { + if (workspace == null) { + throw new IllegalArgumentException("A workspace must be provided."); + } + + if (writer == null) { + throw new IllegalArgumentException("A writer must be provided."); + } + + workspace.getViews().getSystemLandscapeViews().forEach(v -> write(v, writer)); + workspace.getViews().getSystemContextViews().forEach(v -> write(v, writer)); + workspace.getViews().getContainerViews().forEach(v -> write(v, writer)); + workspace.getViews().getComponentViews().forEach(v -> write(v, writer)); + workspace.getViews().getDynamicViews().forEach(v -> write(v, writer)); + workspace.getViews().getDeploymentViews().forEach(v -> write(v, writer)); + } + + /** + * Write the views in the given workspace as PlantUML definitions, to stdout. + * + * @param workspace the workspace containing the views to be written + */ + public void toStdOut(Workspace workspace) { + if (workspace == null) { + throw new IllegalArgumentException("A workspace must be provided."); + } + + StringWriter stringWriter = new StringWriter(); + write(workspace, stringWriter); + System.out.println(stringWriter.toString()); + } + + /** + * Creates PlantUML diagram definitions based upon the specified workspace, returning them as strings. + * + * @param workspace the workspace containing the views to be written + * @return an array of PlantUML diagram definitions, one per view + */ + public String[] toString(Workspace workspace) { + if (workspace == null) { + throw new IllegalArgumentException("A workspace must be provided."); + } + + StringWriter stringWriter = new StringWriter(); + write(workspace, stringWriter); + + String diagrams = stringWriter.toString(); + if (diagrams != null && diagrams.contains("graph TB")) { + return stringWriter.toString().split("(?=graph TB)"); + } else { + return new String[0]; + } + } + + /** + * Gets a single view as a mermaid diagram definition. + * + * @param view the view to write + */ + public String toString(View view) { + StringWriter stringWriter = new StringWriter(); + write(view, stringWriter); + return stringWriter.toString(); + } + + /** + * Writes a single view as a PlantUML diagram definition, to the specified writer. + * + * @param view the view to write + * @param writer the Writer to write the PlantUML definition to + */ + public void write(View view, Writer writer) { + if (view == null) { + throw new IllegalArgumentException("A view must be provided."); + } + + if (writer == null) { + throw new IllegalArgumentException("A writer must be provided."); + } + + if (SystemLandscapeView.class.isAssignableFrom(view.getClass())) { + write((SystemLandscapeView) view, writer); + } else if (SystemContextView.class.isAssignableFrom(view.getClass())) { + write((SystemContextView) view, writer); + } else if (ContainerView.class.isAssignableFrom(view.getClass())) { + write((ContainerView) view, writer); + } else if (ComponentView.class.isAssignableFrom(view.getClass())) { + write((ComponentView) view, writer); + } else if (DynamicView.class.isAssignableFrom(view.getClass())) { + write((DynamicView) view, writer); + } else if (DeploymentView.class.isAssignableFrom(view.getClass())) { + write((DeploymentView) view, writer); + } + } + + protected void write(SystemLandscapeView view, Writer writer) { + writeSystemLandscapeOrContextView(view, writer, view.isEnterpriseBoundaryVisible()); + } + + protected void write(SystemContextView view, Writer writer) { + writeSystemLandscapeOrContextView(view, writer, view.isEnterpriseBoundaryVisible()); + } + + private void writeSystemLandscapeOrContextView(View view, Writer writer, boolean showEnterpriseBoundary) { + try { + writeHeader(view, writer); + + boolean enterpriseBoundaryVisible; + enterpriseBoundaryVisible = + showEnterpriseBoundary && + (view.getElements().stream().map(ElementView::getElement).anyMatch(e -> e instanceof Person && ((Person)e).getLocation() == Location.Internal) || + view.getElements().stream().map(ElementView::getElement).anyMatch(e -> e instanceof SoftwareSystem && ((SoftwareSystem)e).getLocation() == Location.Internal)); + + view.getElements().stream() + .map(ElementView::getElement) + .filter(e -> e instanceof Person && ((Person)e).getLocation() != Location.Internal) + .sorted((e1, e2) -> e1.getName().compareTo(e2.getName())) + .forEach(e -> write(view, e, writer, 0)); + + view.getElements().stream() + .map(ElementView::getElement) + .filter(e -> e instanceof SoftwareSystem && ((SoftwareSystem)e).getLocation() != Location.Internal) + .sorted((e1, e2) -> e1.getName().compareTo(e2.getName())) + .forEach(e -> write(view, e, writer, 0)); + + if (enterpriseBoundaryVisible) { + String name = view.getModel().getEnterprise() != null ? view.getModel().getEnterprise().getName() : "Enterprise"; + writer.write(" subgraph \"" + name + "\""); + writer.write(System.lineSeparator()); + } + + view.getElements().stream() + .map(ElementView::getElement) + .filter(e -> e instanceof Person && ((Person)e).getLocation() == Location.Internal) + .sorted((e1, e2) -> e1.getName().compareTo(e2.getName())) + .forEach(e -> write(view, e, writer, enterpriseBoundaryVisible ? 1 : 0)); + + view.getElements().stream() + .map(ElementView::getElement) + .filter(e -> e instanceof SoftwareSystem && ((SoftwareSystem)e).getLocation() == Location.Internal) + .sorted((e1, e2) -> e1.getName().compareTo(e2.getName())) + .forEach(e -> write(view, e, writer, enterpriseBoundaryVisible ? 1 : 0)); + + if (enterpriseBoundaryVisible) { + writer.write(" end"); + writer.write(System.lineSeparator()); + } + + writeRelationships(view, writer); + + writeFooter(writer); + } catch (IOException e) { + e.printStackTrace(); + } + } + + protected void write(ContainerView view, Writer writer) { + try { + writeHeader(view, writer); + + view.getElements().stream() + .filter(ev -> !(ev.getElement() instanceof Container)) + .map(ElementView::getElement) + .sorted((e1, e2) -> e1.getName().compareTo(e2.getName())) + .forEach(e -> write(view, e, writer, 0)); + + writer.write(" subgraph \"" + view.getSoftwareSystem().getName() + "\""); + writer.write(System.lineSeparator()); + + view.getElements().stream() + .filter(ev -> ev.getElement() instanceof Container) + .map(ElementView::getElement) + .sorted((e1, e2) -> e1.getName().compareTo(e2.getName())) + .forEach(e -> write(view, e, writer, 1)); + + writer.write(" end"); + writer.write(System.lineSeparator()); + + writeRelationships(view, writer); + + writeFooter(writer); + } catch (IOException e) { + e.printStackTrace(); + } + } + + protected void write(ComponentView view, Writer writer) { + try { + writeHeader(view, writer); + + view.getElements().stream() + .filter(ev -> !(ev.getElement() instanceof Component)) + .map(ElementView::getElement) + .sorted((e1, e2) -> e1.getName().compareTo(e2.getName())) + .forEach(e -> write(view, e, writer, 0)); + + writer.write(" subgraph \"" + view.getContainer().getName() + "\""); + writer.write(System.lineSeparator()); + + view.getElements().stream() + .filter(ev -> ev.getElement() instanceof Component) + .map(ElementView::getElement) + .sorted((e1, e2) -> e1.getName().compareTo(e2.getName())) + .forEach(e -> write(view, e, writer, 1)); + + writer.write(" end"); + writer.write(System.lineSeparator()); + + writeRelationships(view, writer); + + writeFooter(writer); + } catch (IOException e) { + e.printStackTrace(); + } + } + + protected void write(DynamicView view, Writer writer) { + try { + writeHeader(view, writer); + + if (useSequenceDiagrams) { + throw new UnsupportedOperationException("Not implemented yet."); + } else { + view.getElements().stream() + .map(ElementView::getElement) + .sorted((e1, e2) -> e1.getName().compareTo(e2.getName())). + forEach(e -> write(view, e, writer, 0)); + } + view.getRelationships().stream() + .sorted((rv1, rv2) -> (rv1.getOrder().compareTo(rv2.getOrder()))) + .forEach(relationship -> { + try { + writer.write( + format(" %s-->|\"%s. %s
%s\"|%s", + idOf(relationship.getRelationship().getSource()), + relationship.getOrder(), + lines(hasValue(relationship.getDescription()) ? relationship.getDescription() : hasValue(relationship.getRelationship().getDescription()) ? relationship.getRelationship().getDescription() : ""), + !StringUtils.isNullOrEmpty(relationship.getRelationship().getTechnology()) ? "[" + relationship.getRelationship().getTechnology() + "]" : "", + idOf(relationship.getRelationship().getDestination()) + ) + ); + + writer.write(System.lineSeparator()); + } catch (IOException e) { + e.printStackTrace(); + } + }); + + writeFooter(writer); + } catch (IOException e) { + e.printStackTrace(); + } + } + + protected void write(DeploymentView view, Writer writer) { + try { + writeHeader(view, writer); + + view.getElements().stream() + .filter(ev -> ev.getElement() instanceof DeploymentNode && ev.getElement().getParent() == null) + .map(ev -> (DeploymentNode)ev.getElement()) + .sorted((e1, e2) -> e1.getName().compareTo(e2.getName())) + .forEach(e -> write(view, e, writer, 1)); + + writeRelationships(view, writer); + + writeFooter(writer); + } catch (IOException e) { + e.printStackTrace(); + } + } + + protected void write(View view, DeploymentNode deploymentNode, Writer writer, int indent) { + try { + writer.write( + format("%ssubgraph \"%s\"", + calculateIndent(indent), + deploymentNode.getName() + ) + ); + + writer.write(System.lineSeparator()); + + for (DeploymentNode child : deploymentNode.getChildren()) { + write(view, child, writer, indent+1); + } + + for (ContainerInstance containerInstance : deploymentNode.getContainerInstances()) { + write(view, containerInstance, writer, indent+1); + } + + writer.write( + format("%send", calculateIndent(indent)) + ); + writer.write(System.lineSeparator()); + } catch (IOException e) { + e.printStackTrace(); + } + } + + protected String calculateIndent(int indent) { + StringBuilder buf = new StringBuilder(); + + for (int i = 0; i < indent; i++) { + buf.append(" "); + } + + return buf.toString(); + } + + protected void write(View view, Element element, Writer writer, int indent) { + try { + final String separator = System.lineSeparator(); + + if (element instanceof ContainerInstance) { + Container container = ((ContainerInstance) element).getContainer(); + writer.write(format(" %s%s(\"
%s
[%s]
%s
\")", + calculateIndent(indent), + idOf(element), + backgroundOf(view, container), + colorOf(view, container), + container.getName(), typeOf(container), lines(container.getDescription()) + )); + } else { + writer.write(format(" %s%s(\"
%s
[%s]
%s
\")", + calculateIndent(indent), + idOf(element), + backgroundOf(view, element), + colorOf(view, element), + element.getName(), typeOf(element), lines(element.getDescription()) + )); + } + writer.write(format("%s", separator)); + + if (!StringUtils.isNullOrEmpty(element.getUrl())) { + writer.write(format("click %s %s \"%s\"", idOf(element), element.getUrl(), element.getUrl())); + writer.write(format("%s", separator)); + } + + if (element instanceof ContainerInstance) { + Container container = ((ContainerInstance) element).getContainer(); + writer.write(format(" %sstyle %s fill:%s", calculateIndent(indent),idOf(element), backgroundOf(view, container))); + } else { + writer.write(format(" %sstyle %s fill:%s", calculateIndent(indent),idOf(element), backgroundOf(view, element))); + } + + writer.write(format("%s", separator)); + } catch (IOException e) { + e.printStackTrace(); + } + } + + protected void writeSequenceElement(View view, Element element, Writer writer, boolean indent, String type) throws IOException { + writer.write(format("%s%s \"%s\" as %s <<%s>> %s%s", + indent ? " " : "", + type, + element.getName(), + idOf(element), + typeOf(element), + backgroundOf(view, element), + System.lineSeparator())); + } + + protected String lines(final String text) { + StringBuilder buf = new StringBuilder(); + if (text != null) { + final String[] words = text.trim().split("\\s+"); + + final StringBuilder line = new StringBuilder(); + for (final String word : words) { + if (line.length() == 0) { + line.append(word); + } else if (line.length() + word.length() + 1 < 30) { + line.append(' ').append(word); + } else { + buf.append(line.toString()); + buf.append("
"); + line.setLength(0); + line.append(word); + } + } + if (line.length() > 0) { + buf.append(line.toString()); + } + } + + return buf.toString(); + } + + protected String backgroundOf(View view, Element element) { + return view.getViewSet().getConfiguration().getStyles().findElementStyle(element).getBackground(); + } + + protected String colorOf(View view, Element element) { + return view.getViewSet().getConfiguration().getStyles().findElementStyle(element).getColor(); + } + + protected RelationshipStyle relationshipStyleOf(View view, Relationship relationship) { + return view.getViewSet().getConfiguration().getStyles().findRelationshipStyle(relationship); + } + + protected void writeRelationships(View view, Writer writer) { + view.getRelationships().stream() + .map(RelationshipView::getRelationship) + .filter(r -> !(r.getSource() instanceof DeploymentNode || r.getDestination() instanceof DeploymentNode)) + .sorted((r1, r2) -> (r1.getSource().getName() + r1.getDestination().getName()).compareTo(r2.getSource().getName() + r2.getDestination().getName())) + .forEach(r -> writeRelationship(view, r, writer)); + } + + protected void writeRelationship(View view, Relationship relationship, Writer writer) { + try { + RelationshipStyle style = relationshipStyleOf(view, relationship); + // solid: A-- text -->B + // dotted: A-. text .->B + + if (style.getDashed() == null) { + style.setDashed(true); + } + + writer.write( + format(" %s-%s \"%s
%s\" %s->%s", + idOf(relationship.getSource()), + style.getDashed() ? "." : "-", + lines(relationship.getDescription()), + !StringUtils.isNullOrEmpty(relationship.getTechnology()) ? "[" + relationship.getTechnology() + "]" : "", + style.getDashed() ? "." : "-", + idOf(relationship.getDestination()) + ) + ); + writer.write(System.lineSeparator()); + } catch (IOException e) { + e.printStackTrace(); + } + } + + protected String idOf(Element e) { + return e.getId(); + } + + protected String typeOf(Element e) { + if (e instanceof SoftwareSystem) { + return "Software System"; + } else if (e instanceof Component) { + Component component = (Component)e; + return hasValue(component.getTechnology()) ? component.getTechnology() : "Component"; + } else if (e instanceof DeploymentNode) { + DeploymentNode deploymentNode = (DeploymentNode)e; + return hasValue(deploymentNode.getTechnology()) ? deploymentNode.getTechnology() : "Deployment Node"; + } else if (e instanceof ContainerInstance) { + return "Container"; + } else { + return e.getClass().getSimpleName(); + } + } + + protected boolean hasValue(String s) { + return s != null && s.trim().length() > 0; + } + + protected void writeHeader(View view, Writer writer) throws IOException { + writer.write("graph TB"); + writer.write(System.lineSeparator()); + } + + protected void writeFooter(Writer writer) throws IOException { + writer.write(System.lineSeparator()); + } + +} diff --git a/structurizr-mermaid/test/unit/com/structurizr/io/mermaid/MermaidEncoderTests.java b/structurizr-mermaid/test/unit/com/structurizr/io/mermaid/MermaidEncoderTests.java new file mode 100644 index 000000000..434b7621f --- /dev/null +++ b/structurizr-mermaid/test/unit/com/structurizr/io/mermaid/MermaidEncoderTests.java @@ -0,0 +1,21 @@ +package com.structurizr.io.mermaid; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class MermaidEncoderTests { + + @Test + public void test_encode() throws Exception { + assertEquals( + "eyJjb2RlIjoiZ3JhcGggVERcbkFbQ2hyaXN0bWFzXSAtLT58R2V0IG1vbmV5fCBCKEdvIHNob3BwaW5nKVxuQiAtLT4gQ3tMZXQgbWUgdGhpbmt9XG5DIC0tPnxPbmV8IERbTGFwdG9wXVxuQyAtLT58VHdvfCBFW2lQaG9uZV1cbkMgLS0-fFRocmVlfCBGW2ZhOmZhLWNhciBDYXJdXG4iLCJtZXJtYWlkIjp7InRoZW1lIjoiZGVmYXVsdCJ9fQ", + new MermaidEncoder().encode("graph TD\n" + + "A[Christmas] -->|Get money| B(Go shopping)\n" + + "B --> C{Let me think}\n" + + "C -->|One| D[Laptop]\n" + + "C -->|Two| E[iPhone]\n" + + "C -->|Three| F[fa:fa-car Car]\n")); + } + +} \ No newline at end of file diff --git a/structurizr-mermaid/test/unit/com/structurizr/io/mermaid/MermaidWriterTests.java b/structurizr-mermaid/test/unit/com/structurizr/io/mermaid/MermaidWriterTests.java new file mode 100644 index 000000000..8a043888f --- /dev/null +++ b/structurizr-mermaid/test/unit/com/structurizr/io/mermaid/MermaidWriterTests.java @@ -0,0 +1,12 @@ +package com.structurizr.io.mermaid; + +import org.junit.Test; + +public class MermaidWriterTests { + + @Test + public void test() { + + } + +} From 0ea3288a662d9a9c2c8b368360f1e011a8366fc0 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 24 Oct 2019 11:19:07 +0100 Subject: [PATCH 025/717] Added an encoder for WebSequenceDiagrams definitions. --- .../WebSequenceDiagramsEncoder.java | 21 ++++++++++++ .../WebSequenceDiagramsWriter.java | 11 +++++++ .../WebSequenceDiagramsEncoderTests.java | 32 +++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 structurizr-websequencediagrams/src/com/structurizr/io/websequencediagrams/WebSequenceDiagramsEncoder.java create mode 100644 structurizr-websequencediagrams/test/unit/com/structurizr/io/websequencediagrams/WebSequenceDiagramsEncoderTests.java diff --git a/structurizr-websequencediagrams/src/com/structurizr/io/websequencediagrams/WebSequenceDiagramsEncoder.java b/structurizr-websequencediagrams/src/com/structurizr/io/websequencediagrams/WebSequenceDiagramsEncoder.java new file mode 100644 index 000000000..82ec34253 --- /dev/null +++ b/structurizr-websequencediagrams/src/com/structurizr/io/websequencediagrams/WebSequenceDiagramsEncoder.java @@ -0,0 +1,21 @@ +package com.structurizr.io.websequencediagrams; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +public class WebSequenceDiagramsEncoder { + + public String encode(String webSequenceDiagramsDefinition) throws Exception { + String base64 = Base64.getEncoder().encodeToString(webSequenceDiagramsDefinition.getBytes(StandardCharsets.UTF_8)); + + // this encoded version is used in URLs so: + // - strip trailing = characters + base64 = base64.replaceAll("=", ""); + + // and switch + characters + base64 = base64.replaceAll("\\+", "-"); + + return base64; + } + +} \ No newline at end of file diff --git a/structurizr-websequencediagrams/src/com/structurizr/io/websequencediagrams/WebSequenceDiagramsWriter.java b/structurizr-websequencediagrams/src/com/structurizr/io/websequencediagrams/WebSequenceDiagramsWriter.java index e4d0900a1..6ddce22ee 100644 --- a/structurizr-websequencediagrams/src/com/structurizr/io/websequencediagrams/WebSequenceDiagramsWriter.java +++ b/structurizr-websequencediagrams/src/com/structurizr/io/websequencediagrams/WebSequenceDiagramsWriter.java @@ -26,6 +26,17 @@ public final class WebSequenceDiagramsWriter { private static final String SYNCHRONOUS_INTERACTION = "->"; private static final String ASYNCHRONOUS_INTERACTION = "->>"; + /** + * Gets a single view as a WebSequenceDiagrams diagram definition. + * + * @param view the view to write + */ + public String toString(DynamicView view) { + StringWriter stringWriter = new StringWriter(); + write(view, stringWriter); + return stringWriter.toString(); + } + /** * Writes the dynamic views in the given workspace as WebSequenceDiagrams definitions, to the specified writer. * diff --git a/structurizr-websequencediagrams/test/unit/com/structurizr/io/websequencediagrams/WebSequenceDiagramsEncoderTests.java b/structurizr-websequencediagrams/test/unit/com/structurizr/io/websequencediagrams/WebSequenceDiagramsEncoderTests.java new file mode 100644 index 000000000..4f1ff4af1 --- /dev/null +++ b/structurizr-websequencediagrams/test/unit/com/structurizr/io/websequencediagrams/WebSequenceDiagramsEncoderTests.java @@ -0,0 +1,32 @@ +package com.structurizr.io.websequencediagrams; + +import org.junit.Test; + +import java.util.Base64; + +import static org.junit.Assert.assertEquals; + +public class WebSequenceDiagramsEncoderTests { + + @Test + public void test_encode() throws Exception { + String decoded = new String(Base64.getDecoder().decode("dGl0bGUgQXV0aGVudGljYXRpb24gU2VxdWVuY2UKCkFsaWNlLT5Cb2I6ABUQUmVxdWVzdApub3RlIHJpZ2h0IG9mIAAlBUJvYiB0aGlua3MgYWJvdXQgaXQKQm9iLT4ASgUANxNzcG9uc2UK")); + System.out.println(decoded); + + for (char c : decoded.toCharArray()) { + System.out.println(c + " -> " + (int)c); + } + + String encoded = new WebSequenceDiagramsEncoder().encode( + "title Authentication Sequence\n" + + "\n" + + "Alice->Bob: Authentication Request\n" + + "note right of Bob: Bob thinks about it\n" + + "Bob->Alice: Authentication Response\n"); + + System.out.println(encoded); + + assertEquals("dGl0bGUgQXV0aGVudGljYXRpb24gU2VxdWVuY2UKCkFsaWNlLT5Cb2I6ABUQUmVxdWVzdApub3RlIHJpZ2h0IG9mIAAlBUJvYiB0aGlua3MgYWJvdXQgaXQKQm9iLT4ASgUANxNzcG9uc2UK", encoded); + } + +} \ No newline at end of file From c47416866e9a3929bf5ba92de38f847f03696afc Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 24 Oct 2019 11:20:44 +0100 Subject: [PATCH 026/717] Updated the example diagrams, to have better relationship descriptions. --- .../com/structurizr/example/BigBankPlc.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/structurizr-examples/src/com/structurizr/example/BigBankPlc.java b/structurizr-examples/src/com/structurizr/example/BigBankPlc.java index 5914e9062..6b5874249 100644 --- a/structurizr-examples/src/com/structurizr/example/BigBankPlc.java +++ b/structurizr-examples/src/com/structurizr/example/BigBankPlc.java @@ -38,11 +38,11 @@ private static Workspace create() { Person customer = model.addPerson(Location.External, "Personal Banking Customer", "A customer of the bank, with personal bank accounts."); SoftwareSystem internetBankingSystem = model.addSoftwareSystem(Location.Internal, "Internet Banking System", "Allows customers to view information about their bank accounts, and make payments."); - customer.uses(internetBankingSystem, "Uses"); + customer.uses(internetBankingSystem, "Views account balances, and makes payments using"); SoftwareSystem mainframeBankingSystem = model.addSoftwareSystem(Location.Internal, "Mainframe Banking System", "Stores all of the core banking information about customers, accounts, transactions, etc."); mainframeBankingSystem.addTags(EXISTING_SYSTEM_TAG); - internetBankingSystem.uses(mainframeBankingSystem, "Uses"); + internetBankingSystem.uses(mainframeBankingSystem, "Gets account information from, and makes payments using"); SoftwareSystem emailSystem = model.addSoftwareSystem(Location.Internal, "E-mail System", "The internal Microsoft Exchange e-mail system."); internetBankingSystem.uses(emailSystem, "Sends e-mail using"); @@ -73,12 +73,12 @@ private static Workspace create() { Container database = internetBankingSystem.addContainer("Database", "Stores user registration information, hashed authentication credentials, access logs, etc.", "Relational Database Schema"); database.addTags(DATABASE_TAG); - customer.uses(webApplication, "Uses", "HTTPS"); - customer.uses(singlePageApplication, "Uses", ""); - customer.uses(mobileApp, "Uses", ""); - webApplication.uses(singlePageApplication, "Delivers", ""); + customer.uses(webApplication, "Visits bigbank.com/ib using", "HTTPS"); + customer.uses(singlePageApplication, "Views account balances, and makes payments using", ""); + customer.uses(mobileApp, "Views account balances, and makes payments using", ""); + webApplication.uses(singlePageApplication, "Delivers to the customer's web browser", ""); apiApplication.uses(database, "Reads from and writes to", "JDBC"); - apiApplication.uses(mainframeBankingSystem, "Uses", "XML/HTTPS"); + apiApplication.uses(mainframeBankingSystem, "Makes API calls to", "XML/HTTPS"); apiApplication.uses(emailSystem, "Sends e-mail using", "SMTP"); // components @@ -91,8 +91,8 @@ private static Workspace create() { Component mainframeBankingSystemFacade = apiApplication.addComponent("Mainframe Banking System Facade", "A facade onto the mainframe banking system.", "Spring Bean"); Component emailComponent = apiApplication.addComponent("E-mail Component", "Sends e-mails to users.", "Spring Bean"); - apiApplication.getComponents().stream().filter(c -> "Spring MVC Rest Controller".equals(c.getTechnology())).forEach(c -> singlePageApplication.uses(c, "Uses", "JSON/HTTPS")); - apiApplication.getComponents().stream().filter(c -> "Spring MVC Rest Controller".equals(c.getTechnology())).forEach(c -> mobileApp.uses(c, "Uses", "JSON/HTTPS")); + apiApplication.getComponents().stream().filter(c -> "Spring MVC Rest Controller".equals(c.getTechnology())).forEach(c -> singlePageApplication.uses(c, "Makes API calls to", "JSON/HTTPS")); + apiApplication.getComponents().stream().filter(c -> "Spring MVC Rest Controller".equals(c.getTechnology())).forEach(c -> mobileApp.uses(c, "Makes API calls to", "JSON/HTTPS")); signinController.uses(securityComponent, "Uses"); accountsSummaryController.uses(mainframeBankingSystemFacade, "Uses"); resetPasswordController.uses(securityComponent, "Uses"); From 9d9e464737c6b780e116cafb719590bcc22578bb Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 24 Oct 2019 11:24:17 +0100 Subject: [PATCH 027/717] Ignore the test ... the implementation needs rewriting. --- .../websequencediagrams/WebSequenceDiagramsEncoderTests.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/structurizr-websequencediagrams/test/unit/com/structurizr/io/websequencediagrams/WebSequenceDiagramsEncoderTests.java b/structurizr-websequencediagrams/test/unit/com/structurizr/io/websequencediagrams/WebSequenceDiagramsEncoderTests.java index 4f1ff4af1..5c48389d1 100644 --- a/structurizr-websequencediagrams/test/unit/com/structurizr/io/websequencediagrams/WebSequenceDiagramsEncoderTests.java +++ b/structurizr-websequencediagrams/test/unit/com/structurizr/io/websequencediagrams/WebSequenceDiagramsEncoderTests.java @@ -1,5 +1,6 @@ package com.structurizr.io.websequencediagrams; +import org.junit.Ignore; import org.junit.Test; import java.util.Base64; @@ -9,7 +10,11 @@ public class WebSequenceDiagramsEncoderTests { @Test + @Ignore public void test_encode() throws Exception { + // this test does not work, and requires a proper implementation of the algorithm used to encode diagram definitions + // TODO + // String decoded = new String(Base64.getDecoder().decode("dGl0bGUgQXV0aGVudGljYXRpb24gU2VxdWVuY2UKCkFsaWNlLT5Cb2I6ABUQUmVxdWVzdApub3RlIHJpZ2h0IG9mIAAlBUJvYiB0aGlua3MgYWJvdXQgaXQKQm9iLT4ASgUANxNzcG9uc2UK")); System.out.println(decoded); From bfe5aeb4d8e14164357491bc3c5d6c3c3bf0d5b3 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 24 Oct 2019 11:42:52 +0100 Subject: [PATCH 028/717] Tweaked the animation sequence for the component diagram example. --- .../src/com/structurizr/example/BigBankPlc.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/structurizr-examples/src/com/structurizr/example/BigBankPlc.java b/structurizr-examples/src/com/structurizr/example/BigBankPlc.java index 6b5874249..628d0216c 100644 --- a/structurizr-examples/src/com/structurizr/example/BigBankPlc.java +++ b/structurizr-examples/src/com/structurizr/example/BigBankPlc.java @@ -186,10 +186,10 @@ private static Workspace create() { containerView.addAnimation(apiApplication); containerView.addAnimation(database); - componentView.addAnimation(singlePageApplication, mobileApp); - componentView.addAnimation(signinController, securityComponent, database); - componentView.addAnimation(accountsSummaryController, mainframeBankingSystemFacade, mainframeBankingSystem); - componentView.addAnimation(resetPasswordController, emailComponent, database); + componentView.addAnimation(singlePageApplication, mobileApp, database, emailSystem, mainframeBankingSystem); + componentView.addAnimation(signinController, securityComponent); + componentView.addAnimation(accountsSummaryController, mainframeBankingSystemFacade); + componentView.addAnimation(resetPasswordController, emailComponent); // dynamic diagrams and deployment diagrams are not available with the Free Plan DynamicView dynamicView = views.createDynamicView(apiApplication, "SignIn", "Summarises how the sign in feature works in the single-page application."); From 0f463f22e4ce5466542351f8b0c8ec4ccbd36c28 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 24 Oct 2019 11:53:40 +0100 Subject: [PATCH 029/717] The way layout information is copied between different versions of a view is now configurable by setting a custom LayoutMergeStrategy on a per view basis. --- docs/changelog.md | 1 + .../view/DefaultLayoutMergeStrategy.java | 97 +++++++++++++++++++ .../src/com/structurizr/view/DynamicView.java | 14 --- .../structurizr/view/LayoutMergeStrategy.java | 19 ++++ .../src/com/structurizr/view/View.java | 54 ++++------- 5 files changed, 134 insertions(+), 51 deletions(-) create mode 100644 structurizr-core/src/com/structurizr/view/DefaultLayoutMergeStrategy.java create mode 100644 structurizr-core/src/com/structurizr/view/LayoutMergeStrategy.java diff --git a/docs/changelog.md b/docs/changelog.md index a51ce3e6a..3cd1a7d19 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -6,6 +6,7 @@ - The structurizr-annotations library can now be more easily used with OSGi applications. - Fixes a bug with the PlantUML and WebSequenceDiagram writers, where relationships were sorted incorrectly (alphabetically, rather than numerically). - Fixes a bug that allows relationships to be created between parents and children. +- The way layout information is copied between different versions of a view is now configurable by setting a custom LayoutMergeStrategy on a per view basis. ## 1.3.0 (3rd March 2019) diff --git a/structurizr-core/src/com/structurizr/view/DefaultLayoutMergeStrategy.java b/structurizr-core/src/com/structurizr/view/DefaultLayoutMergeStrategy.java new file mode 100644 index 000000000..e840a148e --- /dev/null +++ b/structurizr-core/src/com/structurizr/view/DefaultLayoutMergeStrategy.java @@ -0,0 +1,97 @@ +package com.structurizr.view; + +import com.structurizr.model.Element; +import com.structurizr.model.Relationship; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.annotation.Nonnull; + +/** + * A default implementation of a LayoutMergeStrategy that: + * + * - Sets the paper size (if not set). + * - Copies element x,y positions. + * - Copies relationship vertices. + * + * Elements are matched by the full canonical name. The downside of this approach is that if an element is renamed + * between versions of a workspace, it won't be possible to find/copy the layout information associated with an element. + */ +public class DefaultLayoutMergeStrategy implements LayoutMergeStrategy { + + private static final Log log = LogFactory.getLog(View.class); + + /** + * Attempts to copy the visual layout information (e.g. x,y coordinates) of elements and relationships + * from the specified source view into the specified destination view. + * + * @param sourceView the source view (e.g. the version stored by the Structurizr service) + * @param destinationView the destination View (e.g. the new version, created locally with code) + */ + public void copyLayoutInformation(@Nonnull View sourceView, @Nonnull View destinationView) { + setPaperSizeIfNotSpecified(sourceView, destinationView); + + for (ElementView destinationElementView : destinationView.getElements()) { + ElementView sourceElementView = findElementView(sourceView, destinationElementView.getElement()); + if (sourceElementView != null) { + destinationElementView.copyLayoutInformationFrom(sourceElementView); + } else { + log.warn("There is no layout information for the element named " + destinationElementView.getElement().getName() + " on view " + sourceView.getKey()); + } + } + + for (RelationshipView sourceRelationshipView : sourceView.getRelationships()) { + RelationshipView destinationRelationshipView; + if (destinationView instanceof DynamicView) { + destinationRelationshipView = findRelationshipView((DynamicView)destinationView, sourceRelationshipView); + } else { + destinationRelationshipView = findRelationshipView(destinationView, sourceRelationshipView.getRelationship()); + } + + if (destinationRelationshipView != null) { + destinationRelationshipView.copyLayoutInformationFrom(sourceRelationshipView); + } + } + } + + private void setPaperSizeIfNotSpecified(@Nonnull View remoteView, @Nonnull View localView) { + if (localView.getPaperSize() == null) { + localView.setPaperSize(remoteView.getPaperSize()); + } + } + + /** + * Finds an element by canonical name. Override this to change the behaviour. + + * @param view the view to search + * @param element the Element to find + * @return an ElementView + */ + protected ElementView findElementView(View view, Element element) { + return view.getElements().stream().filter(ev -> ev.getElement().getCanonicalName().equals(element.getCanonicalName())).findFirst().orElse(null); + } + + protected RelationshipView findRelationshipView(View view, Relationship relationship) { + for (RelationshipView rv : view.getRelationships()) { + if (rv.getRelationship().equals(relationship)) { + return rv; + } + } + + return null; + } + + protected RelationshipView findRelationshipView(DynamicView view, RelationshipView sourceRelationshipView) { + for (RelationshipView relationshipView : view.getRelationships()) { + if (relationshipView.getRelationship().equals(sourceRelationshipView.getRelationship())) { + if ((relationshipView.getDescription() != null && relationshipView.getDescription().equals(sourceRelationshipView.getDescription())) && + relationshipView.getOrder().equals(sourceRelationshipView.getOrder())) { + return relationshipView; + } + } + } + + return null; + } + +} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/view/DynamicView.java b/structurizr-core/src/com/structurizr/view/DynamicView.java index d55c0c858..e151abc52 100644 --- a/structurizr-core/src/com/structurizr/view/DynamicView.java +++ b/structurizr-core/src/com/structurizr/view/DynamicView.java @@ -165,20 +165,6 @@ private void checkElement(Element elementToBeAdded) { } } - @Override - protected RelationshipView findRelationshipView(@Nonnull RelationshipView sourceRelationshipView) { - for (RelationshipView relationshipView : getRelationships()) { - if (relationshipView.getRelationship().equals(sourceRelationshipView.getRelationship())) { - if ((relationshipView.getDescription() != null && relationshipView.getDescription().equals(sourceRelationshipView.getDescription())) && - relationshipView.getOrder().equals(sourceRelationshipView.getOrder())) { - return relationshipView; - } - } - } - - return null; - } - /** * Gets the (computed) name of this view. * diff --git a/structurizr-core/src/com/structurizr/view/LayoutMergeStrategy.java b/structurizr-core/src/com/structurizr/view/LayoutMergeStrategy.java new file mode 100644 index 000000000..063f7bf84 --- /dev/null +++ b/structurizr-core/src/com/structurizr/view/LayoutMergeStrategy.java @@ -0,0 +1,19 @@ +package com.structurizr.view; + +import javax.annotation.Nonnull; + +/** + * A pluggable strategy that can be used to copy layout information from one version of a view to another. + */ +public interface LayoutMergeStrategy { + + /** + * Attempts to copy the visual layout information (e.g. x,y coordinates) of elements and relationships + * from the specified source view into the specified destination view. + * + * @param sourceView the source view (e.g. the version stored by the Structurizr service) + * @param destinationView the destination View (e.g. the new version, created locally with code) + */ + void copyLayoutInformation(@Nonnull View sourceView, @Nonnull View destinationView); + +} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/view/View.java b/structurizr-core/src/com/structurizr/view/View.java index 0ecf3db0f..baadff42f 100644 --- a/structurizr-core/src/com/structurizr/view/View.java +++ b/structurizr-core/src/com/structurizr/view/View.java @@ -31,6 +31,8 @@ public abstract class View { private Set elementViews = new LinkedHashSet<>(); private Set relationshipViews = new LinkedHashSet<>(); + private LayoutMergeStrategy layoutMergeStrategy = new DefaultLayoutMergeStrategy(); + private ViewSet viewSet; View() { @@ -357,39 +359,27 @@ public void removeElementsWithNoRelationships() { } /** - * Attempts to copy the visual layout information (e.g. x,y coordinates) of elements and relationships - * from the specified source view into this view. + * Sets the strategy used for merging layout information (paper size, x/y positioning, etc) + * from one version of this view to another. * - * @param source the source View + * @param layoutMergeStrategy an instance of LayoutMergeStrategy */ - void copyLayoutInformationFrom(@Nonnull View source) { - if (this.getPaperSize() == null) { - this.setPaperSize(source.getPaperSize()); - } - - for (ElementView sourceElementView : source.getElements()) { - ElementView destinationElementView = findElementView(sourceElementView); - if (destinationElementView != null) { - destinationElementView.copyLayoutInformationFrom(sourceElementView); - } + public void setLayoutMergeStrategy(LayoutMergeStrategy layoutMergeStrategy) { + if (layoutMergeStrategy == null) { + throw new IllegalArgumentException("A LayoutMergeStrategy object must be provided."); } - for (RelationshipView sourceRelationshipView : source.getRelationships()) { - RelationshipView destinationRelationshipView = findRelationshipView(sourceRelationshipView); - if (destinationRelationshipView != null) { - destinationRelationshipView.copyLayoutInformationFrom(sourceRelationshipView); - } - } + this.layoutMergeStrategy = layoutMergeStrategy; } - private ElementView findElementView(ElementView sourceElementView) { - for (ElementView elementView : getElements()) { - if (elementView.getElement().getCanonicalName().equals(sourceElementView.getElement().getCanonicalName())) { - return elementView; - } - } - - return null; + /** + * Attempts to copy the visual layout information (e.g. x,y coordinates) of elements and relationships + * from the specified source view into this view. + * + * @param source the source View + */ + void copyLayoutInformationFrom(@Nonnull View source) { + layoutMergeStrategy.copyLayoutInformation(source, this); } /** @@ -403,16 +393,6 @@ public ElementView getElementView(@Nonnull Element element) { return elementView.orElse(null); } - protected RelationshipView findRelationshipView(@Nonnull RelationshipView sourceRelationshipView) { - for (RelationshipView relationshipView : getRelationships()) { - if (relationshipView.getRelationship().equals(sourceRelationshipView.getRelationship())) { - return relationshipView; - } - } - - return null; - } - /** * Gets the relationship view for the given relationship. * From a6836b0ac84466f099139d7fd21436fa39c70667 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 24 Oct 2019 13:56:52 +0100 Subject: [PATCH 030/717] Fixed javadoc errors. --- .../src/com/structurizr/io/mermaid/MermaidWriter.java | 3 ++- .../src/com/structurizr/io/plantuml/PlantUMLWriter.java | 1 + .../io/websequencediagrams/WebSequenceDiagramsWriter.java | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/structurizr-mermaid/src/com/structurizr/io/mermaid/MermaidWriter.java b/structurizr-mermaid/src/com/structurizr/io/mermaid/MermaidWriter.java index 1dbc27728..76d94e0bd 100644 --- a/structurizr-mermaid/src/com/structurizr/io/mermaid/MermaidWriter.java +++ b/structurizr-mermaid/src/com/structurizr/io/mermaid/MermaidWriter.java @@ -22,7 +22,7 @@ * using mermaid (https://mermaidjs.github.io). * * System landscape, system context, container, component, dynamic and deployment diagrams are supported. - * Deployment node -> deployment node relationships are not rendered. + * Deployment node -> deployment node relationships are not rendered. */ public class MermaidWriter { @@ -106,6 +106,7 @@ public String[] toString(Workspace workspace) { * Gets a single view as a mermaid diagram definition. * * @param view the view to write + * @return the Mermaid definition as a String */ public String toString(View view) { StringWriter stringWriter = new StringWriter(); diff --git a/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLWriter.java b/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLWriter.java index 2f90a76e8..a1e34c9a2 100644 --- a/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLWriter.java +++ b/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLWriter.java @@ -191,6 +191,7 @@ public String[] toString(Workspace workspace) { * Gets a single view as a PlantUML diagram definition. * * @param view the view to write + * @return the PlantUML definition as a String */ public String toString(View view) { StringWriter stringWriter = new StringWriter(); diff --git a/structurizr-websequencediagrams/src/com/structurizr/io/websequencediagrams/WebSequenceDiagramsWriter.java b/structurizr-websequencediagrams/src/com/structurizr/io/websequencediagrams/WebSequenceDiagramsWriter.java index 6ddce22ee..316c25d59 100644 --- a/structurizr-websequencediagrams/src/com/structurizr/io/websequencediagrams/WebSequenceDiagramsWriter.java +++ b/structurizr-websequencediagrams/src/com/structurizr/io/websequencediagrams/WebSequenceDiagramsWriter.java @@ -30,6 +30,7 @@ public final class WebSequenceDiagramsWriter { * Gets a single view as a WebSequenceDiagrams diagram definition. * * @param view the view to write + * @return the WebSequenceDiagrams definition as a String */ public String toString(DynamicView view) { StringWriter stringWriter = new StringWriter(); From c1c342e6f8565b806bca432ee655f62ac37090bb Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 24 Oct 2019 17:07:11 +0100 Subject: [PATCH 031/717] Added more validation checks (which required some changes to how relationships are handled). --- .../api/WorkspaceRulesValidationTests.java | 44 ++++++++- .../ChildDeploymentNodeNamesAreNotUnique.json | 39 ++++++++ .../RelationshipDescriptionsAreNotUnique.json | 43 +++++++++ ...pLevelDeploymentNodeNamesAreNotUnique.json | 27 ++++++ .../com/structurizr/model/DeploymentNode.java | 5 + .../src/com/structurizr/model/Element.java | 2 +- .../src/com/structurizr/model/Model.java | 50 ++++++++-- .../com/structurizr/model/Relationship.java | 22 ----- .../view/DefaultLayoutMergeStrategy.java | 31 ++++--- .../unit/com/structurizr/view/ViewTests.java | 92 ++++++++++++------- 10 files changed, 274 insertions(+), 81 deletions(-) create mode 100644 structurizr-client/test/integration/workspaceValidation/ChildDeploymentNodeNamesAreNotUnique.json create mode 100644 structurizr-client/test/integration/workspaceValidation/RelationshipDescriptionsAreNotUnique.json create mode 100644 structurizr-client/test/integration/workspaceValidation/TopLevelDeploymentNodeNamesAreNotUnique.json diff --git a/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java b/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java index 1d8face7a..bcb6fa6a4 100644 --- a/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java +++ b/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java @@ -49,7 +49,7 @@ public void test_exceptionThrown_WhenPeopleAndSoftwareSystemNamesAreNotUnique() WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "PeopleAndSoftwareSystemNamesAreNotUnique.json")); fail(); } catch (WorkspaceValidationException we) { - assertEquals("An element named Name already exists in this context.", we.getMessage()); + assertEquals("A person or software system named \"Name\" already exists.", we.getMessage()); } } @@ -59,7 +59,7 @@ public void test_exceptionThrown_WhenContainerNamesAreNotUnique() throws Excepti WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "ContainerNamesAreNotUnique.json")); fail(); } catch (WorkspaceValidationException we) { - assertEquals("An element named Container already exists in this context.", we.getMessage()); + assertEquals("A container named \"Container\" already exists within \"Software System\".", we.getMessage()); } } @@ -69,7 +69,37 @@ public void test_exceptionThrown_WhenComponentNamesAreNotUnique() throws Excepti WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "ComponentNamesAreNotUnique.json")); fail(); } catch (WorkspaceValidationException we) { - assertEquals("An element named Component already exists in this context.", we.getMessage()); + assertEquals("A component named \"Component\" already exists within \"Container\".", we.getMessage()); + } + } + + @Test + public void test_exceptionThrown_WhenTopLevelDeploymentNodeNamesAreNotUnique() throws Exception { + try { + WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "TopLevelDeploymentNodeNamesAreNotUnique.json")); + fail(); + } catch (WorkspaceValidationException we) { + assertEquals("A top-level deployment node named \"Deployment Node\" already exists.", we.getMessage()); + } + } + + @Test + public void test_exceptionThrown_WhenChildDeploymentNodeNamesAreNotUnique() throws Exception { + try { + WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "ChildDeploymentNodeNamesAreNotUnique.json")); + fail(); + } catch (WorkspaceValidationException we) { + assertEquals("A deployment node named \"Child\" already exists within \"Deployment Node\".", we.getMessage()); + } + } + + @Test + public void test_exceptionThrown_WhenRelationshipDescriptionsAreNotUnique() throws Exception { + try { + WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "RelationshipDescriptionsAreNotUnique.json")); + fail(); + } catch (WorkspaceValidationException we) { + assertEquals("A relationship with the description \"Uses\" already exists between \"User\" and \"Software System\".", we.getMessage()); } } @@ -78,7 +108,13 @@ public void test_exceptionThrown_WhenComponentNamesAreNotUnique() throws Excepti // Model model = workspace.getModel(); // ViewSet views = workspace.getViews(); // -// WorkspaceUtils.saveWorkspaceToJson(workspace, new File (new File("structurizr-client/test/integration/workspaceValidation"), "x.json")); +// Person person = model.addPerson("User", ""); +// SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); +// +// person.uses(softwareSystem, "Uses"); +// person.uses(softwareSystem, "Uses2"); +// +// WorkspaceUtils.saveWorkspaceToJson(workspace, new File(new File("./structurizr-client/" + PATH_TO_WORKSPACE_FILES), "RelationshipDescriptionsAreNotUnique.json")); // } } \ No newline at end of file diff --git a/structurizr-client/test/integration/workspaceValidation/ChildDeploymentNodeNamesAreNotUnique.json b/structurizr-client/test/integration/workspaceValidation/ChildDeploymentNodeNamesAreNotUnique.json new file mode 100644 index 000000000..ca78d57b5 --- /dev/null +++ b/structurizr-client/test/integration/workspaceValidation/ChildDeploymentNodeNamesAreNotUnique.json @@ -0,0 +1,39 @@ +{ + "id" : 0, + "name" : "Name", + "description" : "Description", + "configuration" : { }, + "model" : { + "deploymentNodes" : [ { + "id" : "1", + "name" : "Deployment Node", + "environment" : "Default", + "instances" : 1, + "children" : [ { + "id" : "2", + "name" : "Deployment Node", + "environment" : "Default", + "instances" : 1, + "children" : [ { + "id" : "4", + "name" : "Child", + "environment" : "Default", + "instances" : 1 + }, { + "id" : "3", + "name" : "Child", + "environment" : "Default", + "instances" : 1 + } ] + } ] + } ] + }, + "documentation" : { }, + "views" : { + "configuration" : { + "branding" : { }, + "styles" : { }, + "terminology" : { } + } + } +} \ No newline at end of file diff --git a/structurizr-client/test/integration/workspaceValidation/RelationshipDescriptionsAreNotUnique.json b/structurizr-client/test/integration/workspaceValidation/RelationshipDescriptionsAreNotUnique.json new file mode 100644 index 000000000..bc2e72d25 --- /dev/null +++ b/structurizr-client/test/integration/workspaceValidation/RelationshipDescriptionsAreNotUnique.json @@ -0,0 +1,43 @@ +{ + "id" : 0, + "name" : "Name", + "description" : "Description", + "configuration" : { }, + "model" : { + "people" : [ { + "id" : "1", + "tags" : "Element,Person", + "name" : "User", + "relationships" : [ { + "id" : "3", + "tags" : "Relationship,Synchronous", + "sourceId" : "1", + "destinationId" : "2", + "description" : "Uses", + "interactionStyle" : "Synchronous" + }, { + "id" : "4", + "tags" : "Relationship,Synchronous", + "sourceId" : "1", + "destinationId" : "2", + "description" : "Uses", + "interactionStyle" : "Synchronous" + } ], + "location" : "Unspecified" + } ], + "softwareSystems" : [ { + "id" : "2", + "tags" : "Element,Software System", + "name" : "Software System", + "location" : "Unspecified" + } ] + }, + "documentation" : { }, + "views" : { + "configuration" : { + "branding" : { }, + "styles" : { }, + "terminology" : { } + } + } +} \ No newline at end of file diff --git a/structurizr-client/test/integration/workspaceValidation/TopLevelDeploymentNodeNamesAreNotUnique.json b/structurizr-client/test/integration/workspaceValidation/TopLevelDeploymentNodeNamesAreNotUnique.json new file mode 100644 index 000000000..1ed26eacd --- /dev/null +++ b/structurizr-client/test/integration/workspaceValidation/TopLevelDeploymentNodeNamesAreNotUnique.json @@ -0,0 +1,27 @@ +{ + "id" : 0, + "name" : "Name", + "description" : "Description", + "configuration" : { }, + "model" : { + "deploymentNodes" : [ { + "id" : "1", + "name" : "Deployment Node", + "environment" : "Default", + "instances" : 1 + }, { + "id" : "2", + "name" : "Deployment Node", + "environment" : "Default", + "instances" : 1 + } ] + }, + "documentation" : { }, + "views" : { + "configuration" : { + "branding" : { }, + "styles" : { }, + "terminology" : { } + } + } +} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/model/DeploymentNode.java b/structurizr-core/src/com/structurizr/model/DeploymentNode.java index d53968da2..aadbb9f2d 100644 --- a/structurizr-core/src/com/structurizr/model/DeploymentNode.java +++ b/structurizr-core/src/com/structurizr/model/DeploymentNode.java @@ -157,6 +157,11 @@ void setChildren(Set children) { } } + @JsonIgnore + public boolean hasChildren() { + return !children.isEmpty(); + } + /** * Gets the set of container instances associated with this deployment node. * diff --git a/structurizr-core/src/com/structurizr/model/Element.java b/structurizr-core/src/com/structurizr/model/Element.java index 384d1379d..02df5657f 100644 --- a/structurizr-core/src/com/structurizr/model/Element.java +++ b/structurizr-core/src/com/structurizr/model/Element.java @@ -168,7 +168,7 @@ public Relationship getEfferentRelationshipWith(Element element) { } boolean has(Relationship relationship) { - return relationships.contains(relationship); + return relationships.stream().anyMatch(r -> r.getDestination().equals(relationship.getDestination()) && r.getDescription().equalsIgnoreCase(relationship.getDescription())); } void addRelationship(Relationship relationship) { diff --git a/structurizr-core/src/com/structurizr/model/Model.java b/structurizr-core/src/com/structurizr/model/Model.java index f48a10023..0d0f4a8e7 100644 --- a/structurizr-core/src/com/structurizr/model/Model.java +++ b/structurizr-core/src/com/structurizr/model/Model.java @@ -173,7 +173,6 @@ Component addComponentOfType(Container parent, String name, String type, String Relationship addRelationship(Element source, @Nonnull Element destination, String description, String technology, InteractionStyle interactionStyle) { if (destination == null) { throw new IllegalArgumentException("The destination must be specified."); - } if (isChildOf(source, destination) || isChildOf(destination, source)) { @@ -183,9 +182,9 @@ Relationship addRelationship(Element source, @Nonnull Element destination, Strin Relationship relationship = new Relationship(source, destination, description, technology, interactionStyle); if (addRelationship(relationship)) { return relationship; - } else { - return null; } + + return null; } private boolean isChildOf(Element e1, Element e2) { @@ -366,18 +365,33 @@ void hydrate() { peopleAndSoftwareSystems.addAll(people); peopleAndSoftwareSystems.addAll(softwareSystems); for (Element element : peopleAndSoftwareSystems) { - checkNameIsUnique(peopleAndSoftwareSystems, element.getName()); + checkNameIsUnique(peopleAndSoftwareSystems, element.getName(), "A person or software system named \"%s\" already exists."); } for (SoftwareSystem softwareSystem : softwareSystems) { for (Container container : softwareSystem.getContainers()) { - checkNameIsUnique(softwareSystem.getContainers(), container.getName()); + checkNameIsUnique(softwareSystem.getContainers(), container.getName(), "A container named \"%s\" already exists within \"" + softwareSystem.getName() + "\"."); for (Component component : container.getComponents()) { - checkNameIsUnique(container.getComponents(), component.getName()); + checkNameIsUnique(container.getComponents(), component.getName(), "A component named \"%s\" already exists within \"" + container.getName() + "\"."); } } } + + for (DeploymentNode deploymentNode : deploymentNodes) { + checkNameIsUnique(deploymentNodes, deploymentNode.getName(), "A top-level deployment node named \"%s\" already exists."); + + if (deploymentNode.hasChildren()) { + checkChildNamesAreUnique(deploymentNode); + } + } + + // and check that all relationships are unique + for (Element element : getElements()) { + for (Relationship relationship : element.getRelationships()) { + checkDescriptionIsUnique(element.getRelationships(), relationship); + } + } } private void hydrateDeploymentNode(DeploymentNode deploymentNode, DeploymentNode parent) { @@ -392,9 +406,29 @@ private void hydrateDeploymentNode(DeploymentNode deploymentNode, DeploymentNode } } - private void checkNameIsUnique(Collection elements, String name) { + private void checkNameIsUnique(Collection elements, String name, String errorMessage) { if (elements.stream().filter(e -> e.getName().equalsIgnoreCase(name)).count() != 1) { - throw new WorkspaceValidationException("An element named " + name + " already exists in this context."); + throw new WorkspaceValidationException( + String.format(errorMessage, name)); + } + } + + private void checkChildNamesAreUnique(DeploymentNode deploymentNode) { + for (DeploymentNode child : deploymentNode.getChildren()) { + checkNameIsUnique(deploymentNode.getChildren(), child.getName(), "A deployment node named \"%s\" already exists within \"" + deploymentNode.getName() + "\"."); + + if (child.hasChildren()) { + checkChildNamesAreUnique(child); + } + } + } + + private void checkDescriptionIsUnique(Collection relationships, Relationship relationship) { + if (relationships.stream().filter(r -> r.getDestination().equals(relationship.getDestination()) && r.getDescription().equalsIgnoreCase(relationship.getDescription())).count() != 1) { + throw new WorkspaceValidationException( + String.format( + "A relationship with the description \"%s\" already exists between \"%s\" and \"%s\".", + relationship.getDescription(), relationship.getSource().getName(), relationship.getDestination().getName())); } } diff --git a/structurizr-core/src/com/structurizr/model/Relationship.java b/structurizr-core/src/com/structurizr/model/Relationship.java index cfe045323..4517bcdc5 100644 --- a/structurizr-core/src/com/structurizr/model/Relationship.java +++ b/structurizr-core/src/com/structurizr/model/Relationship.java @@ -145,28 +145,6 @@ protected Set getRequiredTags() { } } - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - Relationship that = (Relationship) o; - - if (!getDescription().equals(that.getDescription())) return false; - if (!getDestination().getCanonicalName().equals(that.getDestination().getCanonicalName())) return false; - if (!getSource().getCanonicalName().equals(that.getSource().getCanonicalName())) return false; - - return true; - } - - @Override - public int hashCode() { - int result = getSourceId().hashCode(); - result = 31 * result + getDestinationId().hashCode(); - result = 31 * result + getDescription().hashCode(); - return result; - } - @Override public String toString() { return source.toString() + " ---[" + description + "]---> " + destination.toString(); diff --git a/structurizr-core/src/com/structurizr/view/DefaultLayoutMergeStrategy.java b/structurizr-core/src/com/structurizr/view/DefaultLayoutMergeStrategy.java index e840a148e..e18c258de 100644 --- a/structurizr-core/src/com/structurizr/view/DefaultLayoutMergeStrategy.java +++ b/structurizr-core/src/com/structurizr/view/DefaultLayoutMergeStrategy.java @@ -40,15 +40,15 @@ public void copyLayoutInformation(@Nonnull View sourceView, @Nonnull View destin } } - for (RelationshipView sourceRelationshipView : sourceView.getRelationships()) { - RelationshipView destinationRelationshipView; + for (RelationshipView destinationRelationshipView : destinationView.getRelationships()) { + RelationshipView sourceRelationshipView; if (destinationView instanceof DynamicView) { - destinationRelationshipView = findRelationshipView((DynamicView)destinationView, sourceRelationshipView); + sourceRelationshipView = findRelationshipView(sourceView, destinationRelationshipView); } else { - destinationRelationshipView = findRelationshipView(destinationView, sourceRelationshipView.getRelationship()); + sourceRelationshipView = findRelationshipView(sourceView, destinationRelationshipView.getRelationship()); } - if (destinationRelationshipView != null) { + if (sourceRelationshipView != null) { destinationRelationshipView.copyLayoutInformationFrom(sourceRelationshipView); } } @@ -73,7 +73,11 @@ protected ElementView findElementView(View view, Element element) { protected RelationshipView findRelationshipView(View view, Relationship relationship) { for (RelationshipView rv : view.getRelationships()) { - if (rv.getRelationship().equals(relationship)) { + if ( + rv.getRelationship().getSource().getCanonicalName().equals(relationship.getSource().getCanonicalName()) && + rv.getRelationship().getDestination().getCanonicalName().equals(relationship.getDestination().getCanonicalName()) && + rv.getRelationship().getDescription().equals(relationship.getDescription()) + ) { return rv; } } @@ -81,13 +85,14 @@ protected RelationshipView findRelationshipView(View view, Relationship relation return null; } - protected RelationshipView findRelationshipView(DynamicView view, RelationshipView sourceRelationshipView) { - for (RelationshipView relationshipView : view.getRelationships()) { - if (relationshipView.getRelationship().equals(sourceRelationshipView.getRelationship())) { - if ((relationshipView.getDescription() != null && relationshipView.getDescription().equals(sourceRelationshipView.getDescription())) && - relationshipView.getOrder().equals(sourceRelationshipView.getOrder())) { - return relationshipView; - } + protected RelationshipView findRelationshipView(View view, RelationshipView relationshipView) { + for (RelationshipView rv : view.getRelationships()) { + if ( + rv.getRelationship().getSource().getCanonicalName().equals(relationshipView.getRelationship().getSource().getCanonicalName()) && + rv.getRelationship().getDestination().getCanonicalName().equals(relationshipView.getRelationship().getDestination().getCanonicalName()) && + rv.getDescription().equals(relationshipView.getDescription()) && + rv.getOrder().equals(relationshipView.getOrder())) { + return rv; } } diff --git a/structurizr-core/test/unit/com/structurizr/view/ViewTests.java b/structurizr-core/test/unit/com/structurizr/view/ViewTests.java index c4eb18059..c38d29b46 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ViewTests.java @@ -172,16 +172,27 @@ public void test_copyLayoutInformationFrom() { Relationship personUsesSoftwareSystem1 = person1.uses(softwareSystem1A, "Uses"); // create a view with SystemA and Person (locations are set for both, relationship has vertices) - StaticView view1 = new SystemContextView(softwareSystem1A, "context", "Description"); - view1.add(softwareSystem1B); - view1.getElementView(softwareSystem1B).setX(123); - view1.getElementView(softwareSystem1B).setY(321); - view1.add(person1); - view1.getElementView(person1).setX(456); - view1.getElementView(person1).setY(654); - view1.getRelationshipView(personUsesSoftwareSystem1).setVertices(Arrays.asList(new Vertex(123, 456))); - view1.getRelationshipView(personUsesSoftwareSystem1).setPosition(70); - view1.getRelationshipView(personUsesSoftwareSystem1).setRouting(Routing.Orthogonal); + StaticView staticView1 = new SystemContextView(softwareSystem1A, "context", "Description"); + staticView1.add(softwareSystem1B); + staticView1.getElementView(softwareSystem1B).setX(123); + staticView1.getElementView(softwareSystem1B).setY(321); + staticView1.add(person1); + staticView1.getElementView(person1).setX(456); + staticView1.getElementView(person1).setY(654); + staticView1.getRelationshipView(personUsesSoftwareSystem1).setVertices(Arrays.asList(new Vertex(123, 456))); + staticView1.getRelationshipView(personUsesSoftwareSystem1).setPosition(70); + staticView1.getRelationshipView(personUsesSoftwareSystem1).setRouting(Routing.Orthogonal); + + // and create a dynamic view, as they are treated slightly differently + DynamicView dynamicView1 = new DynamicView(model1, "dynamic", "Description"); + dynamicView1.add(person1, "Overridden description", softwareSystem1A); + dynamicView1.getElementView(person1).setX(111); + dynamicView1.getElementView(person1).setY(222); + dynamicView1.getElementView(softwareSystem1A).setX(333); + dynamicView1.getElementView(softwareSystem1A).setY(444); + dynamicView1.getRelationshipView(personUsesSoftwareSystem1).setVertices(Arrays.asList(new Vertex(555, 666))); + dynamicView1.getRelationshipView(personUsesSoftwareSystem1).setPosition(30); + dynamicView1.getRelationshipView(personUsesSoftwareSystem1).setRouting(Routing.Direct); Workspace workspace2 = new Workspace("", ""); Model model2 = workspace2.getModel(); @@ -192,29 +203,44 @@ public void test_copyLayoutInformationFrom() { Relationship personUsesSoftwareSystem2 = person2.uses(softwareSystem2A, "Uses"); // create a view with SystemB and Person (locations are 0,0 for both) - StaticView view2 = new SystemContextView(softwareSystem2A, "context", "Description"); - view2.add(softwareSystem2B); - view2.add(person2); - assertEquals(0, view2.getElementView(softwareSystem2B).getX()); - assertEquals(0, view2.getElementView(softwareSystem2B).getY()); - assertEquals(0, view2.getElementView(softwareSystem2B).getX()); - assertEquals(0, view2.getElementView(softwareSystem2B).getY()); - assertEquals(0, view2.getElementView(person2).getX()); - assertEquals(0, view2.getElementView(person2).getY()); - assertTrue(view2.getRelationshipView(personUsesSoftwareSystem2).getVertices().isEmpty()); - - view2.copyLayoutInformationFrom(view1); - assertEquals(0, view2.getElementView(softwareSystem2A).getX()); - assertEquals(0, view2.getElementView(softwareSystem2A).getY()); - assertEquals(123, view2.getElementView(softwareSystem2B).getX()); - assertEquals(321, view2.getElementView(softwareSystem2B).getY()); - assertEquals(456, view2.getElementView(person2).getX()); - assertEquals(654, view2.getElementView(person2).getY()); - Vertex vertex = view2.getRelationshipView(personUsesSoftwareSystem2).getVertices().iterator().next(); - assertEquals(123, vertex.getX()); - assertEquals(456, vertex.getY()); - assertEquals(70, view2.getRelationshipView(personUsesSoftwareSystem2).getPosition().intValue()); - assertEquals(Routing.Orthogonal, view2.getRelationshipView(personUsesSoftwareSystem2).getRouting()); + StaticView staticView2 = new SystemContextView(softwareSystem2A, "context", "Description"); + staticView2.add(softwareSystem2B); + staticView2.add(person2); + assertEquals(0, staticView2.getElementView(softwareSystem2B).getX()); + assertEquals(0, staticView2.getElementView(softwareSystem2B).getY()); + assertEquals(0, staticView2.getElementView(softwareSystem2B).getX()); + assertEquals(0, staticView2.getElementView(softwareSystem2B).getY()); + assertEquals(0, staticView2.getElementView(person2).getX()); + assertEquals(0, staticView2.getElementView(person2).getY()); + assertTrue(staticView2.getRelationshipView(personUsesSoftwareSystem2).getVertices().isEmpty()); + + // and create a dynamic view (locations are 0,0) + DynamicView dynamicView2 = new DynamicView(model2, "dynamic", "Description"); + dynamicView2.add(person2, "Overridden description", softwareSystem2A); + + staticView2.copyLayoutInformationFrom(staticView1); + assertEquals(0, staticView2.getElementView(softwareSystem2A).getX()); + assertEquals(0, staticView2.getElementView(softwareSystem2A).getY()); + assertEquals(123, staticView2.getElementView(softwareSystem2B).getX()); + assertEquals(321, staticView2.getElementView(softwareSystem2B).getY()); + assertEquals(456, staticView2.getElementView(person2).getX()); + assertEquals(654, staticView2.getElementView(person2).getY()); + Vertex vertex1 = staticView2.getRelationshipView(personUsesSoftwareSystem2).getVertices().iterator().next(); + assertEquals(123, vertex1.getX()); + assertEquals(456, vertex1.getY()); + assertEquals(70, staticView2.getRelationshipView(personUsesSoftwareSystem2).getPosition().intValue()); + assertEquals(Routing.Orthogonal, staticView2.getRelationshipView(personUsesSoftwareSystem2).getRouting()); + + dynamicView2.copyLayoutInformationFrom(dynamicView1); + assertEquals(111, dynamicView2.getElementView(person2).getX()); + assertEquals(222, dynamicView2.getElementView(person2).getY()); + assertEquals(333, dynamicView2.getElementView(softwareSystem2A).getX()); + assertEquals(444, dynamicView2.getElementView(softwareSystem2A).getY()); + Vertex vertex2 = dynamicView2.getRelationshipView(personUsesSoftwareSystem2).getVertices().iterator().next(); + assertEquals(555, vertex2.getX()); + assertEquals(666, vertex2.getY()); + assertEquals(30, dynamicView2.getRelationshipView(personUsesSoftwareSystem2).getPosition().intValue()); + assertEquals(Routing.Direct, dynamicView2.getRelationshipView(personUsesSoftwareSystem2).getRouting()); } @Test From c7ff97df46835fd35589634bc0e59e4815f2cfb3 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 24 Oct 2019 20:41:08 +0100 Subject: [PATCH 032/717] Added more validation tests. --- .../api/WorkspaceRulesValidationTests.java | 112 +++++++++++++++--- ...ithComponentViewIsMissingFromTheModel.json | 33 ++++++ ...atedWithDecisionIsMissingFromTheModel.json | 32 +++++ ...mentationSectionIsMissingFromTheModel.json | 35 ++++++ ...dWithDynamicViewIsMissingFromTheModel.json | 27 +++++ ...ithComponentViewIsMissingFromTheModel.json | 33 ++++++ ...ithContainerViewIsMissingFromTheModel.json | 27 +++++ ...thDeploymentViewIsMissingFromTheModel.json | 27 +++++ ...ystemContextViewIsMissingFromTheModel.json | 33 ++++++ ...FilteredViewIsMissingFromTheWorkspace.json | 24 ++++ .../src/com/structurizr/Workspace.java | 4 + .../documentation/Documentation.java | 23 +++- .../src/com/structurizr/view/ViewSet.java | 22 ++-- 13 files changed, 409 insertions(+), 23 deletions(-) create mode 100644 structurizr-client/test/integration/workspaceValidation/ContainerAssociatedWithComponentViewIsMissingFromTheModel.json create mode 100644 structurizr-client/test/integration/workspaceValidation/ElementAssociatedWithDecisionIsMissingFromTheModel.json create mode 100644 structurizr-client/test/integration/workspaceValidation/ElementAssociatedWithDocumentationSectionIsMissingFromTheModel.json create mode 100644 structurizr-client/test/integration/workspaceValidation/ElementAssociatedWithDynamicViewIsMissingFromTheModel.json create mode 100644 structurizr-client/test/integration/workspaceValidation/SoftwareSystemAssociatedWithComponentViewIsMissingFromTheModel.json create mode 100644 structurizr-client/test/integration/workspaceValidation/SoftwareSystemAssociatedWithContainerViewIsMissingFromTheModel.json create mode 100644 structurizr-client/test/integration/workspaceValidation/SoftwareSystemAssociatedWithDeploymentViewIsMissingFromTheModel.json create mode 100644 structurizr-client/test/integration/workspaceValidation/SoftwareSystemAssociatedWithSystemContextViewIsMissingFromTheModel.json create mode 100644 structurizr-client/test/integration/workspaceValidation/ViewAssociatedWithFilteredViewIsMissingFromTheWorkspace.json diff --git a/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java b/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java index bcb6fa6a4..bbe8b7bed 100644 --- a/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java +++ b/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java @@ -1,7 +1,14 @@ package com.structurizr.api; +import com.structurizr.Workspace; import com.structurizr.WorkspaceValidationException; +import com.structurizr.documentation.Format; +import com.structurizr.documentation.StructurizrDocumentationTemplate; +import com.structurizr.model.Model; +import com.structurizr.model.Person; +import com.structurizr.model.SoftwareSystem; import com.structurizr.util.WorkspaceUtils; +import com.structurizr.view.ViewSet; import org.junit.Test; import java.io.File; @@ -29,7 +36,8 @@ public void test_exceptionThrown_WhenRelationshipIdsAreNotUnique() throws Except WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "RelationshipIdsAreNotUnique.json")); fail(); } catch (WorkspaceValidationException we) { - assertEquals("The relationship {1 | User | null} ---[Uses 2]---> {2 | Software System | null} has a non-unique ID of 3.", we.getMessage()); + assertTrue(we.getMessage().startsWith("The relationship {1 | User | null} ---[Uses ")); + assertTrue(we.getMessage().endsWith("]---> {2 | Software System | null} has a non-unique ID of 3.")); } } @@ -103,18 +111,94 @@ public void test_exceptionThrown_WhenRelationshipDescriptionsAreNotUnique() thro } } -// public static void main(String[] args) throws Exception { -// Workspace workspace = new Workspace("Name", "Description"); -// Model model = workspace.getModel(); -// ViewSet views = workspace.getViews(); -// -// Person person = model.addPerson("User", ""); -// SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); -// -// person.uses(softwareSystem, "Uses"); -// person.uses(softwareSystem, "Uses2"); -// -// WorkspaceUtils.saveWorkspaceToJson(workspace, new File(new File("./structurizr-client/" + PATH_TO_WORKSPACE_FILES), "RelationshipDescriptionsAreNotUnique.json")); -// } + @Test + public void test_exceptionThrown_WhenSoftwareSystemAssociatedWithSystemContextViewIsMissingFromTheModel() throws Exception { + try { + WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "SoftwareSystemAssociatedWithSystemContextViewIsMissingFromTheModel.json")); + fail(); + } catch (WorkspaceValidationException we) { + assertEquals("The system context view with key \"SystemContext\" is associated with a software system (id=2), but that element does not exist in the model.", we.getMessage()); + } + } + + @Test + public void test_exceptionThrown_WhenSoftwareSystemAssociatedWithContainerViewIsMissingFromTheModel() throws Exception { + try { + WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "SoftwareSystemAssociatedWithContainerViewIsMissingFromTheModel.json")); + fail(); + } catch (WorkspaceValidationException we) { + assertEquals("The container view with key \"Containers\" is associated with a software system (id=2), but that element does not exist in the model.", we.getMessage()); + } + } + + @Test + public void test_exceptionThrown_WhenSoftwareSystemAssociatedWithComponentViewIsMissingFromTheModel() throws Exception { + try { + WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "SoftwareSystemAssociatedWithComponentViewIsMissingFromTheModel.json")); + fail(); + } catch (WorkspaceValidationException we) { + assertEquals("The component view with key \"Components\" is associated with a software system (id=3), but that element does not exist in the model.", we.getMessage()); + } + } + + @Test + public void test_exceptionThrown_WhenContainerAssociatedWithComponentViewIsMissingFromTheModel() throws Exception { + try { + WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "ContainerAssociatedWithComponentViewIsMissingFromTheModel.json")); + fail(); + } catch (WorkspaceValidationException we) { + assertEquals("The component view with key \"Components\" is associated with a container (id=3), but that element does not exist in the model.", we.getMessage()); + } + } + + @Test + public void test_exceptionThrown_WhenElementAssociatedWithDynamicViewIsMissingFromTheModel() throws Exception { + try { + WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "ElementAssociatedWithDynamicViewIsMissingFromTheModel.json")); + fail(); + } catch (WorkspaceValidationException we) { + assertEquals("The dynamic view with key \"Dynamic\" is associated with an element (id=2), but that element does not exist in the model.", we.getMessage()); + } + } + + @Test + public void test_exceptionThrown_WhenSoftwareSystemAssociatedWithDeploymentViewIsMissingFromTheModel() throws Exception { + try { + WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "SoftwareSystemAssociatedWithDeploymentViewIsMissingFromTheModel.json")); + fail(); + } catch (WorkspaceValidationException we) { + assertEquals("The deployment view with key \"Deployment\" is associated with a software system (id=2), but that element does not exist in the model.", we.getMessage()); + } + } + + @Test + public void test_exceptionThrown_WhenViewAssociatedWithFilteredViewIsMissingFromTheWorkspace() throws Exception { + try { + WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "ViewAssociatedWithFilteredViewIsMissingFromTheWorkspace.json")); + fail(); + } catch (WorkspaceValidationException we) { + assertEquals("The filtered view with key \"Filtered\" is based upon a view (key=SystemContext), but that view does not exist in the workspace.", we.getMessage()); + } + } + + @Test + public void test_exceptionThrown_WhenElementAssociatedWithDocumentationSectionIsMissingFromTheModel() throws Exception { + try { + WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "ElementAssociatedWithDocumentationSectionIsMissingFromTheModel.json")); + fail(); + } catch (WorkspaceValidationException we) { + assertEquals("The documentation section with title \"Context\" is associated with an element (id=2), but that element does not exist in the model.", we.getMessage()); + } + } + + @Test + public void test_exceptionThrown_WhenElementAssociatedWithDecisionIsMissingFromTheModel() throws Exception { + try { + WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "ElementAssociatedWithDecisionIsMissingFromTheModel.json")); + fail(); + } catch (WorkspaceValidationException we) { + assertEquals("The decision record with title \"Use Java\" is associated with an element (id=2), but that element does not exist in the model.", we.getMessage()); + } + } } \ No newline at end of file diff --git a/structurizr-client/test/integration/workspaceValidation/ContainerAssociatedWithComponentViewIsMissingFromTheModel.json b/structurizr-client/test/integration/workspaceValidation/ContainerAssociatedWithComponentViewIsMissingFromTheModel.json new file mode 100644 index 000000000..f7d2ebc91 --- /dev/null +++ b/structurizr-client/test/integration/workspaceValidation/ContainerAssociatedWithComponentViewIsMissingFromTheModel.json @@ -0,0 +1,33 @@ +{ + "id" : 0, + "name" : "Name", + "description" : "Description", + "configuration" : { }, + "model" : { + "softwareSystems" : [ { + "id" : "1", + "tags" : "Element,Software System", + "name" : "Software System", + "location" : "Unspecified", + "containers" : [ { + "id" : "2", + "tags" : "Element,Container", + "name" : "Container" + } ] + } ] + }, + "documentation" : { }, + "views" : { + "componentViews" : [ { + "softwareSystemId" : "1", + "description" : "Description", + "key" : "Components", + "containerId" : "3" + } ], + "configuration" : { + "branding" : { }, + "styles" : { }, + "terminology" : { } + } + } +} \ No newline at end of file diff --git a/structurizr-client/test/integration/workspaceValidation/ElementAssociatedWithDecisionIsMissingFromTheModel.json b/structurizr-client/test/integration/workspaceValidation/ElementAssociatedWithDecisionIsMissingFromTheModel.json new file mode 100644 index 000000000..ce89370a5 --- /dev/null +++ b/structurizr-client/test/integration/workspaceValidation/ElementAssociatedWithDecisionIsMissingFromTheModel.json @@ -0,0 +1,32 @@ +{ + "id" : 0, + "name" : "Name", + "description" : "Description", + "configuration" : { }, + "model" : { + "softwareSystems" : [ { + "id" : "1", + "tags" : "Element,Software System", + "name" : "Software System", + "location" : "Unspecified" + } ] + }, + "documentation" : { + "decisions" : [ { + "elementId" : "2", + "id" : "1", + "date" : "2019-10-24T19:17:46Z", + "title" : "Use Java", + "status" : "Proposed", + "content" : "Content", + "format" : "Markdown" + } ] + }, + "views" : { + "configuration" : { + "branding" : { }, + "styles" : { }, + "terminology" : { } + } + } +} \ No newline at end of file diff --git a/structurizr-client/test/integration/workspaceValidation/ElementAssociatedWithDocumentationSectionIsMissingFromTheModel.json b/structurizr-client/test/integration/workspaceValidation/ElementAssociatedWithDocumentationSectionIsMissingFromTheModel.json new file mode 100644 index 000000000..036e40fad --- /dev/null +++ b/structurizr-client/test/integration/workspaceValidation/ElementAssociatedWithDocumentationSectionIsMissingFromTheModel.json @@ -0,0 +1,35 @@ +{ + "id" : 0, + "name" : "Name", + "description" : "Description", + "configuration" : { }, + "model" : { + "softwareSystems" : [ { + "id" : "1", + "tags" : "Element,Software System", + "name" : "Software System", + "location" : "Unspecified" + } ] + }, + "documentation" : { + "sections" : [ { + "elementId" : "2", + "title" : "Context", + "order" : 1, + "format" : "Markdown", + "content" : "Content" + } ], + "template" : { + "name" : "Software Guidebook", + "author" : "Simon Brown", + "url" : "https://leanpub.com/visualising-software-architecture" + } + }, + "views" : { + "configuration" : { + "branding" : { }, + "styles" : { }, + "terminology" : { } + } + } +} \ No newline at end of file diff --git a/structurizr-client/test/integration/workspaceValidation/ElementAssociatedWithDynamicViewIsMissingFromTheModel.json b/structurizr-client/test/integration/workspaceValidation/ElementAssociatedWithDynamicViewIsMissingFromTheModel.json new file mode 100644 index 000000000..788c4acc0 --- /dev/null +++ b/structurizr-client/test/integration/workspaceValidation/ElementAssociatedWithDynamicViewIsMissingFromTheModel.json @@ -0,0 +1,27 @@ +{ + "id" : 0, + "name" : "Name", + "description" : "Description", + "configuration" : { }, + "model" : { + "softwareSystems" : [ { + "id" : "1", + "tags" : "Element,Software System", + "name" : "Software System", + "location" : "Unspecified" + } ] + }, + "documentation" : { }, + "views" : { + "dynamicViews" : [ { + "description" : "Description", + "key" : "Dynamic", + "elementId" : "2" + } ], + "configuration" : { + "branding" : { }, + "styles" : { }, + "terminology" : { } + } + } +} \ No newline at end of file diff --git a/structurizr-client/test/integration/workspaceValidation/SoftwareSystemAssociatedWithComponentViewIsMissingFromTheModel.json b/structurizr-client/test/integration/workspaceValidation/SoftwareSystemAssociatedWithComponentViewIsMissingFromTheModel.json new file mode 100644 index 000000000..30d75416f --- /dev/null +++ b/structurizr-client/test/integration/workspaceValidation/SoftwareSystemAssociatedWithComponentViewIsMissingFromTheModel.json @@ -0,0 +1,33 @@ +{ + "id" : 0, + "name" : "Name", + "description" : "Description", + "configuration" : { }, + "model" : { + "softwareSystems" : [ { + "id" : "1", + "tags" : "Element,Software System", + "name" : "Software System", + "location" : "Unspecified", + "containers" : [ { + "id" : "2", + "tags" : "Element,Container", + "name" : "Container" + } ] + } ] + }, + "documentation" : { }, + "views" : { + "componentViews" : [ { + "softwareSystemId" : "3", + "description" : "Description", + "key" : "Components", + "containerId" : "2" + } ], + "configuration" : { + "branding" : { }, + "styles" : { }, + "terminology" : { } + } + } +} \ No newline at end of file diff --git a/structurizr-client/test/integration/workspaceValidation/SoftwareSystemAssociatedWithContainerViewIsMissingFromTheModel.json b/structurizr-client/test/integration/workspaceValidation/SoftwareSystemAssociatedWithContainerViewIsMissingFromTheModel.json new file mode 100644 index 000000000..fe0fafb75 --- /dev/null +++ b/structurizr-client/test/integration/workspaceValidation/SoftwareSystemAssociatedWithContainerViewIsMissingFromTheModel.json @@ -0,0 +1,27 @@ +{ + "id" : 0, + "name" : "Name", + "description" : "Description", + "configuration" : { }, + "model" : { + "softwareSystems" : [ { + "id" : "1", + "tags" : "Element,Software System", + "name" : "Software System", + "location" : "Unspecified" + } ] + }, + "documentation" : { }, + "views" : { + "containerViews" : [ { + "softwareSystemId" : "2", + "description" : "Description", + "key" : "Containers" + } ], + "configuration" : { + "branding" : { }, + "styles" : { }, + "terminology" : { } + } + } +} \ No newline at end of file diff --git a/structurizr-client/test/integration/workspaceValidation/SoftwareSystemAssociatedWithDeploymentViewIsMissingFromTheModel.json b/structurizr-client/test/integration/workspaceValidation/SoftwareSystemAssociatedWithDeploymentViewIsMissingFromTheModel.json new file mode 100644 index 000000000..2e0ae1beb --- /dev/null +++ b/structurizr-client/test/integration/workspaceValidation/SoftwareSystemAssociatedWithDeploymentViewIsMissingFromTheModel.json @@ -0,0 +1,27 @@ +{ + "id" : 0, + "name" : "Name", + "description" : "Description", + "configuration" : { }, + "model" : { + "softwareSystems" : [ { + "id" : "1", + "tags" : "Element,Software System", + "name" : "Software System", + "location" : "Unspecified" + } ] + }, + "documentation" : { }, + "views" : { + "deploymentViews" : [ { + "softwareSystemId" : "2", + "description" : "Description", + "key" : "Deployment" + } ], + "configuration" : { + "branding" : { }, + "styles" : { }, + "terminology" : { } + } + } +} \ No newline at end of file diff --git a/structurizr-client/test/integration/workspaceValidation/SoftwareSystemAssociatedWithSystemContextViewIsMissingFromTheModel.json b/structurizr-client/test/integration/workspaceValidation/SoftwareSystemAssociatedWithSystemContextViewIsMissingFromTheModel.json new file mode 100644 index 000000000..3d5f52048 --- /dev/null +++ b/structurizr-client/test/integration/workspaceValidation/SoftwareSystemAssociatedWithSystemContextViewIsMissingFromTheModel.json @@ -0,0 +1,33 @@ +{ + "id" : 0, + "name" : "Name", + "description" : "Description", + "configuration" : { }, + "model" : { + "softwareSystems" : [ { + "id" : "1", + "tags" : "Element,Software System", + "name" : "Software System", + "location" : "Unspecified" + } ] + }, + "documentation" : { }, + "views" : { + "systemContextViews" : [ { + "softwareSystemId" : "2", + "description" : "Description", + "key" : "SystemContext", + "enterpriseBoundaryVisible" : true, + "elements" : [ { + "id" : "1", + "x" : 0, + "y" : 0 + } ] + } ], + "configuration" : { + "branding" : { }, + "styles" : { }, + "terminology" : { } + } + } +} \ No newline at end of file diff --git a/structurizr-client/test/integration/workspaceValidation/ViewAssociatedWithFilteredViewIsMissingFromTheWorkspace.json b/structurizr-client/test/integration/workspaceValidation/ViewAssociatedWithFilteredViewIsMissingFromTheWorkspace.json new file mode 100644 index 000000000..ad814cd11 --- /dev/null +++ b/structurizr-client/test/integration/workspaceValidation/ViewAssociatedWithFilteredViewIsMissingFromTheWorkspace.json @@ -0,0 +1,24 @@ +{ + "id" : 0, + "name" : "Name", + "description" : "Description", + "configuration" : { }, + "model" : { }, + "documentation" : { }, + "views" : { + "systemLandscapeViews" : [ { + "key" : "SystemLandscape", + "enterpriseBoundaryVisible" : true + } ], + "filteredViews" : [ { + "baseViewKey" : "SystemContext", + "key" : "Filtered", + "mode" : "Include" + } ], + "configuration" : { + "branding" : { }, + "styles" : { }, + "terminology" : { } + } + } +} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/Workspace.java b/structurizr-core/src/com/structurizr/Workspace.java index 87ac4adc7..2eb458636 100644 --- a/structurizr-core/src/com/structurizr/Workspace.java +++ b/structurizr-core/src/com/structurizr/Workspace.java @@ -139,6 +139,10 @@ private void hydrateDocumentation() { Method hydrateMethod = Documentation.class.getDeclaredMethod("hydrate", Model.class); hydrateMethod.setAccessible(true); hydrateMethod.invoke(documentation, model); + } catch (InvocationTargetException ite) { + if (ite.getCause() != null && ite.getCause() instanceof WorkspaceValidationException) { + throw (WorkspaceValidationException)ite.getCause(); + } } catch (Exception e) { throw new RuntimeException(e); } diff --git a/structurizr-core/src/com/structurizr/documentation/Documentation.java b/structurizr-core/src/com/structurizr/documentation/Documentation.java index 1664a01c0..553e19ad3 100644 --- a/structurizr-core/src/com/structurizr/documentation/Documentation.java +++ b/structurizr-core/src/com/structurizr/documentation/Documentation.java @@ -1,6 +1,7 @@ package com.structurizr.documentation; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.structurizr.WorkspaceValidationException; import com.structurizr.model.Element; import com.structurizr.model.Model; import com.structurizr.model.SoftwareSystem; @@ -210,13 +211,31 @@ void hydrate(Model model) { for (Section section : sections) { if (!StringUtils.isNullOrEmpty(section.getElementId())) { - section.setElement(model.getElement(section.getElementId())); + Element element = model.getElement(section.getElementId()); + + if (element == null) { + throw new WorkspaceValidationException( + String.format("The documentation section with title \"%s\" is associated with an element (id=%s), but that element does not exist in the model.", + section.getTitle(), section.getElementId()) + ); + } + + section.setElement(element); } } for (Decision decision : decisions) { if (!StringUtils.isNullOrEmpty(decision.getElementId())) { - decision.setElement(model.getElement(decision.getElementId())); + Element element = model.getElement(decision.getElementId()); + + if (element == null) { + throw new WorkspaceValidationException( + String.format("The decision record with title \"%s\" is associated with an element (id=%s), but that element does not exist in the model.", + decision.getTitle(), decision.getElementId()) + ); + } + + decision.setElement(element); } } } diff --git a/structurizr-core/src/com/structurizr/view/ViewSet.java b/structurizr-core/src/com/structurizr/view/ViewSet.java index f4bd9f86b..cec9cd145 100644 --- a/structurizr-core/src/com/structurizr/view/ViewSet.java +++ b/structurizr-core/src/com/structurizr/view/ViewSet.java @@ -451,7 +451,7 @@ void hydrate(Model model) { SoftwareSystem softwareSystem = model.getSoftwareSystemWithId(view.getSoftwareSystemId()); if (softwareSystem == null) { throw new WorkspaceValidationException( - String.format("The system context view with key %s is associated with a software system (id=%s), but that element does not exist in the model.", + String.format("The system context view with key \"%s\" is associated with a software system (id=%s), but that element does not exist in the model.", view.getKey(), view.getSoftwareSystemId()) ); } @@ -464,7 +464,7 @@ void hydrate(Model model) { SoftwareSystem softwareSystem = model.getSoftwareSystemWithId(view.getSoftwareSystemId()); if (softwareSystem == null) { throw new WorkspaceValidationException( - String.format("The container view with key %s is associated with a software system (id=%s), but that element does not exist in the model.", + String.format("The container view with key \"%s\" is associated with a software system (id=%s), but that element does not exist in the model.", view.getKey(), view.getSoftwareSystemId()) ); } @@ -477,7 +477,7 @@ void hydrate(Model model) { SoftwareSystem softwareSystem = model.getSoftwareSystemWithId(view.getSoftwareSystemId()); if (softwareSystem == null) { throw new WorkspaceValidationException( - String.format("The component view with key %s is associated with a software system (id=%s), but that element does not exist in the model.", + String.format("The component view with key \"%s\" is associated with a software system (id=%s), but that element does not exist in the model.", view.getKey(), view.getSoftwareSystemId()) ); } @@ -487,7 +487,7 @@ void hydrate(Model model) { Container container = softwareSystem.getContainerWithId(view.getContainerId()); if (container == null) { throw new WorkspaceValidationException( - String.format("The component view with key %s is associated with a container (id=%s), but that element does not exist in the model.", + String.format("The component view with key \"%s\" is associated with a container (id=%s), but that element does not exist in the model.", view.getKey(), view.getContainerId()) ); } @@ -501,7 +501,7 @@ void hydrate(Model model) { Element element = model.getElement(view.getElementId()); if (element == null) { throw new WorkspaceValidationException( - String.format("The dynamic view with key %s is associated with an element (id=%s), but that element does not exist in the model.", + String.format("The dynamic view with key \"%s\" is associated with an element (id=%s), but that element does not exist in the model.", view.getKey(), view.getElementId()) ); } @@ -518,7 +518,7 @@ void hydrate(Model model) { SoftwareSystem softwareSystem = model.getSoftwareSystemWithId(view.getSoftwareSystemId()); if (softwareSystem == null) { throw new WorkspaceValidationException( - String.format("The deployment view with key %s is associated with a software system (id=%s), but that element does not exist in the model.", + String.format("The deployment view with key \"%s\" is associated with a software system (id=%s), but that element does not exist in the model.", view.getKey(), view.getSoftwareSystemId()) ); } @@ -531,7 +531,15 @@ void hydrate(Model model) { } for (FilteredView filteredView : filteredViews) { - filteredView.setView(getViewWithKey(filteredView.getBaseViewKey())); + View view = getViewWithKey(filteredView.getBaseViewKey()); + if (view == null) { + throw new WorkspaceValidationException( + String.format("The filtered view with key \"%s\" is based upon a view (key=%s), but that view does not exist in the workspace.", + filteredView.getKey(), filteredView.getBaseViewKey()) + ); + } + + filteredView.setView(view); } } From 3dfbea539e8a11079f65d989985594211d7093ef Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 24 Oct 2019 20:50:10 +0100 Subject: [PATCH 033/717] And more validation tests. --- .../api/WorkspaceRulesValidationTests.java | 20 +++++++ ...ReferencedByViewIsMissingFromTheModel.json | 31 +++++++++++ ...ReferencedByViewIsMissingFromTheModel.json | 52 +++++++++++++++++++ .../src/com/structurizr/view/ViewSet.java | 25 ++++++--- 4 files changed, 122 insertions(+), 6 deletions(-) create mode 100644 structurizr-client/test/integration/workspaceValidation/ElementReferencedByViewIsMissingFromTheModel.json create mode 100644 structurizr-client/test/integration/workspaceValidation/RelationshipReferencedByViewIsMissingFromTheModel.json diff --git a/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java b/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java index bbe8b7bed..35dfd1acc 100644 --- a/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java +++ b/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java @@ -181,6 +181,26 @@ public void test_exceptionThrown_WhenViewAssociatedWithFilteredViewIsMissingFrom } } + @Test + public void test_exceptionThrown_WhenElementReferencedByViewIsMissingFromTheModel() throws Exception { + try { + WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "ElementReferencedByViewIsMissingFromTheModel.json")); + fail(); + } catch (WorkspaceValidationException we) { + assertEquals("The view with key \"SystemLandscape\" references an element (id=2), but that element does not exist in the model.", we.getMessage()); + } + } + + @Test + public void test_exceptionThrown_WhenRelationshipReferencedByViewIsMissingFromTheModel() throws Exception { + try { + WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "RelationshipReferencedByViewIsMissingFromTheModel.json")); + fail(); + } catch (WorkspaceValidationException we) { + assertEquals("The view with key \"SystemLandscape\" references a relationship (id=4), but that relationship does not exist in the model.", we.getMessage()); + } + } + @Test public void test_exceptionThrown_WhenElementAssociatedWithDocumentationSectionIsMissingFromTheModel() throws Exception { try { diff --git a/structurizr-client/test/integration/workspaceValidation/ElementReferencedByViewIsMissingFromTheModel.json b/structurizr-client/test/integration/workspaceValidation/ElementReferencedByViewIsMissingFromTheModel.json new file mode 100644 index 000000000..573257f6d --- /dev/null +++ b/structurizr-client/test/integration/workspaceValidation/ElementReferencedByViewIsMissingFromTheModel.json @@ -0,0 +1,31 @@ +{ + "id" : 0, + "name" : "Name", + "description" : "Description", + "configuration" : { }, + "model" : { + "people" : [ { + "id" : "1", + "tags" : "Element,Person", + "name" : "Person", + "location" : "Unspecified" + } ] + }, + "documentation" : { }, + "views" : { + "systemLandscapeViews" : [ { + "key" : "SystemLandscape", + "enterpriseBoundaryVisible" : true, + "elements" : [ { + "id" : "2", + "x" : 0, + "y" : 0 + } ] + } ], + "configuration" : { + "branding" : { }, + "styles" : { }, + "terminology" : { } + } + } +} \ No newline at end of file diff --git a/structurizr-client/test/integration/workspaceValidation/RelationshipReferencedByViewIsMissingFromTheModel.json b/structurizr-client/test/integration/workspaceValidation/RelationshipReferencedByViewIsMissingFromTheModel.json new file mode 100644 index 000000000..65cc277bf --- /dev/null +++ b/structurizr-client/test/integration/workspaceValidation/RelationshipReferencedByViewIsMissingFromTheModel.json @@ -0,0 +1,52 @@ +{ + "id" : 0, + "name" : "Name", + "description" : "Description", + "configuration" : { }, + "model" : { + "people" : [ { + "id" : "1", + "tags" : "Element,Person", + "name" : "Person", + "relationships" : [ { + "id" : "3", + "tags" : "Relationship,Synchronous", + "sourceId" : "1", + "destinationId" : "2", + "description" : "Uses", + "interactionStyle" : "Synchronous" + } ], + "location" : "Unspecified" + } ], + "softwareSystems" : [ { + "id" : "2", + "tags" : "Element,Software System", + "name" : "Software System", + "location" : "Unspecified" + } ] + }, + "documentation" : { }, + "views" : { + "systemLandscapeViews" : [ { + "key" : "SystemLandscape", + "enterpriseBoundaryVisible" : true, + "elements" : [ { + "id" : "1", + "x" : 0, + "y" : 0 + }, { + "id" : "2", + "x" : 0, + "y" : 0 + } ], + "relationships" : [ { + "id" : "4" + } ] + } ], + "configuration" : { + "branding" : { }, + "styles" : { }, + "terminology" : { } + } + } +} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/view/ViewSet.java b/structurizr-core/src/com/structurizr/view/ViewSet.java index cec9cd145..2ce0314e7 100644 --- a/structurizr-core/src/com/structurizr/view/ViewSet.java +++ b/structurizr-core/src/com/structurizr/view/ViewSet.java @@ -3,10 +3,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonSetter; import com.structurizr.WorkspaceValidationException; -import com.structurizr.model.Container; -import com.structurizr.model.Element; -import com.structurizr.model.Model; -import com.structurizr.model.SoftwareSystem; +import com.structurizr.model.*; import com.structurizr.util.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -547,11 +544,27 @@ private void hydrateView(View view) { view.setViewSet(this); for (ElementView elementView : view.getElements()) { - elementView.setElement(model.getElement(elementView.getId())); + Element element = model.getElement(elementView.getId()); + if (element == null) { + throw new WorkspaceValidationException( + String.format("The view with key \"%s\" references an element (id=%s), but that element does not exist in the model.", + view.getKey(), elementView.getId()) + ); + } + + elementView.setElement(element); } for (RelationshipView relationshipView : view.getRelationships()) { - relationshipView.setRelationship(model.getRelationship(relationshipView.getId())); + Relationship relationship = model.getRelationship(relationshipView.getId()); + if (relationship == null) { + throw new WorkspaceValidationException( + String.format("The view with key \"%s\" references a relationship (id=%s), but that relationship does not exist in the model.", + view.getKey(), relationshipView.getId()) + ); + } + + relationshipView.setRelationship(relationship); } } From c9d3aa5e7fe9b71bf26848f9c05cdf8e29650d1e Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 29 Oct 2019 08:42:58 +0000 Subject: [PATCH 034/717] Added a way to clear styles, and some checks to ensure that multiple styles for the same tag cannot be added. --- .../src/com/structurizr/view/Styles.java | 22 ++++++++++ .../unit/com/structurizr/view/StyleTests.java | 43 +++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/structurizr-core/src/com/structurizr/view/Styles.java b/structurizr-core/src/com/structurizr/view/Styles.java index a0ebf51d1..3111838d4 100644 --- a/structurizr-core/src/com/structurizr/view/Styles.java +++ b/structurizr-core/src/com/structurizr/view/Styles.java @@ -25,6 +25,10 @@ public ElementStyle addElementStyle(String tag) { ElementStyle elementStyle = null; if (tag != null) { + if (elements.stream().anyMatch(es -> es.getTag().equals(tag))) { + throw new IllegalArgumentException("An element style for the tag \"" + tag + "\" already exists."); + } + elementStyle = new ElementStyle(); elementStyle.setTag(tag); add(elementStyle); @@ -33,6 +37,20 @@ public ElementStyle addElementStyle(String tag) { return elementStyle; } + /** + * Removes all element styles. + */ + public void clearElementStyles() { + this.elements = new LinkedList<>(); + } + + /** + * Removes all relationship styles. + */ + public void clearRelationshipStyles() { + this.relationships = new LinkedList<>(); + } + public Collection getRelationships() { return relationships; } @@ -47,6 +65,10 @@ public RelationshipStyle addRelationshipStyle(String tag) { RelationshipStyle relationshipStyle = null; if (tag != null) { + if (relationships.stream().anyMatch(rs -> rs.getTag().equals(tag))) { + throw new IllegalArgumentException("A relationship style for the tag \"" + tag + "\" already exists."); + } + relationshipStyle = new RelationshipStyle(); relationshipStyle.setTag(tag); add(relationshipStyle); diff --git a/structurizr-core/test/unit/com/structurizr/view/StyleTests.java b/structurizr-core/test/unit/com/structurizr/view/StyleTests.java index 6bfc22314..53bfbccdb 100644 --- a/structurizr-core/test/unit/com/structurizr/view/StyleTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/StyleTests.java @@ -7,6 +7,7 @@ import org.junit.Test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; public class StyleTests extends AbstractWorkspaceTestBase { @@ -70,4 +71,46 @@ public void test_findRelationshipStyle_ReturnsTheCorrectStyle_WhenStylesAreDefin assertEquals("#0000ff", style.getColor()); } + @Test + public void test_addElementStyle_ThrowsAnException_WhenAStyleWithTheSameTagExistsAlready() { + try { + styles.addElementStyle(Tags.SOFTWARE_SYSTEM).color("#ff0000"); + styles.addElementStyle(Tags.SOFTWARE_SYSTEM).color("#ff0000"); + + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("An element style for the tag \"Software System\" already exists.", iae.getMessage()); + } + } + + @Test + public void test_addRelationshipStyle_ThrowsAnException_WhenAStyleWithTheSameTagExistsAlready() { + try { + styles.addRelationshipStyle(Tags.RELATIONSHIP).color("#ff0000"); + styles.addRelationshipStyle(Tags.RELATIONSHIP).color("#ff0000"); + + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("A relationship style for the tag \"Relationship\" already exists.", iae.getMessage()); + } + } + + @Test + public void test_clearElementStyles_RemovesAllElementStyles() { + styles.addElementStyle(Tags.SOFTWARE_SYSTEM).color("#ff0000"); + assertEquals(1, styles.getElements().size()); + + styles.clearElementStyles(); + assertEquals(0, styles.getElements().size()); + } + + @Test + public void test_clearRelationshipStyles_RemovesAllRelationshipStyles() { + styles.addRelationshipStyle(Tags.RELATIONSHIP).color("#ff0000"); + assertEquals(1, styles.getRelationships().size()); + + styles.clearRelationshipStyles(); + assertEquals(0, styles.getRelationships().size()); + } + } \ No newline at end of file From 93d89493d7cd7ab6791028ec7c4820af569161b1 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 29 Oct 2019 13:15:09 +0000 Subject: [PATCH 035/717] Updated version numbers and release date. --- docs/binaries.md | 18 +++++++++--------- docs/changelog.md | 2 +- docs/getting-started.md | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/binaries.md b/docs/binaries.md index 1a42bb04f..3b323c437 100644 --- a/docs/binaries.md +++ b/docs/binaries.md @@ -3,12 +3,12 @@ The "Structurizr for Java" binaries are hosted on [Maven Central](https://repo1. Name | Description ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-core:1.1.0 | The core library that can used to create software architecture models. -com.structurizr:structurizr-client:1.1.0 | The API client for publishing models on the Structurizr cloud service and on-premises installation. -com.structurizr:structurizr-analysis:1.1.0 | Provides analysis capabilities, using reflection on compiled bytecode to find components. -com.structurizr:structurizr-spring:1.1.0 | Extends structurizr-analysis to help find Spring components that correspond to Java types annotated ```@Controller```, ```@RestController```, ```@Component```, ```@Service``` and ```@Repository```, plus those that extend ```JpaRepository```. -com.structurizr:structurizr-annotations:1.1.0 | A very small, standalone, library that allows you to add software architecture hints into your own code. -com.structurizr:structurizr-plantuml:1.1.0 | Provides the ability to export view definitions to PlantUML diagram definitions. -com.structurizr:structurizr-dot:1.1.0 | Provides the ability to export view definitions to a DOT file, so they can be rendered with graphviz. -com.structurizr:structurizr-websequencediagrams:1.1.0 | Provides the ability to export dynamic view definitions to WebSequenceDiagrams diagram definitions. -com.structurizr:structurizr-adr-tools:1.1.0 | Imports architecture decision records (ADRs) from the adr-tools tooling. \ No newline at end of file +com.structurizr:structurizr-core:1.3.1 | The core library that can used to create software architecture models. +com.structurizr:structurizr-client:1.3.1 | The API client for publishing models on the Structurizr cloud service and on-premises installation. +com.structurizr:structurizr-analysis:1.3.1 | Provides analysis capabilities, using reflection on compiled bytecode to find components. +com.structurizr:structurizr-spring:1.3.1 | Extends structurizr-analysis to help find Spring components that correspond to Java types annotated ```@Controller```, ```@RestController```, ```@Component```, ```@Service``` and ```@Repository```, plus those that extend ```JpaRepository```. +com.structurizr:structurizr-annotations:1.3.1 | A very small, standalone, library that allows you to add software architecture hints into your own code. +com.structurizr:structurizr-plantuml:1.3.1 | Provides the ability to export view definitions to PlantUML diagram definitions. +com.structurizr:structurizr-dot:1.3.1 | Provides the ability to export view definitions to a DOT file, so they can be rendered with graphviz. +com.structurizr:structurizr-websequencediagrams:1.3.1 | Provides the ability to export dynamic view definitions to WebSequenceDiagrams diagram definitions. +com.structurizr:structurizr-adr-tools:1.3.1 | Imports architecture decision records (ADRs) from the adr-tools tooling. \ No newline at end of file diff --git a/docs/changelog.md b/docs/changelog.md index 3cd1a7d19..cecbe42a4 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 1.3.1 (unreleased) +## 1.3.1 (29th October 2019) - The automatic layout algorithm can now be configured on individual views. - The structurizr-annotations library can now be more easily used with OSGi applications. diff --git a/docs/getting-started.md b/docs/getting-started.md index e90d131c2..d6491d3bd 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -12,7 +12,7 @@ The Structurizr for Java binaries are hosted on [Maven Central](https://repo1.ma Name | Description ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-client:1.1.0 | The Structurizr API client library. +com.structurizr:structurizr-client:1.3.1 | The Structurizr API client library. ## 2. Create a Java program From 9cb64da9d32d2a6441d34b53ba55944d100f651c Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 8 Nov 2019 15:23:22 +0400 Subject: [PATCH 036/717] Fixes a bug where top-level deployment nodes couldn't be named the same, despite residing in different environments. --- .../api/WorkspaceRulesValidationTests.java | 14 +++++----- ...ueButTheyExistInDifferentEnvironments.json | 27 +++++++++++++++++++ .../src/com/structurizr/model/Model.java | 11 ++++++-- 3 files changed, 42 insertions(+), 10 deletions(-) create mode 100644 structurizr-client/test/integration/workspaceValidation/TopLevelDeploymentNodeNamesAreNotUniqueButTheyExistInDifferentEnvironments.json diff --git a/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java b/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java index 35dfd1acc..7773f698f 100644 --- a/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java +++ b/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java @@ -1,14 +1,7 @@ package com.structurizr.api; -import com.structurizr.Workspace; import com.structurizr.WorkspaceValidationException; -import com.structurizr.documentation.Format; -import com.structurizr.documentation.StructurizrDocumentationTemplate; -import com.structurizr.model.Model; -import com.structurizr.model.Person; -import com.structurizr.model.SoftwareSystem; import com.structurizr.util.WorkspaceUtils; -import com.structurizr.view.ViewSet; import org.junit.Test; import java.io.File; @@ -87,10 +80,15 @@ public void test_exceptionThrown_WhenTopLevelDeploymentNodeNamesAreNotUnique() t WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "TopLevelDeploymentNodeNamesAreNotUnique.json")); fail(); } catch (WorkspaceValidationException we) { - assertEquals("A top-level deployment node named \"Deployment Node\" already exists.", we.getMessage()); + assertEquals("A top-level deployment node named \"Deployment Node\" already exists for the environment named \"Default\".", we.getMessage()); } } + @Test + public void test_exceptionNotThrown_WhenTopLevelDeploymentNodeNamesAreNotUniqueButTheyExistInDifferentEnvironments() throws Exception { + WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "TopLevelDeploymentNodeNamesAreNotUniqueButTheyExistInDifferentEnvironments.json")); + } + @Test public void test_exceptionThrown_WhenChildDeploymentNodeNamesAreNotUnique() throws Exception { try { diff --git a/structurizr-client/test/integration/workspaceValidation/TopLevelDeploymentNodeNamesAreNotUniqueButTheyExistInDifferentEnvironments.json b/structurizr-client/test/integration/workspaceValidation/TopLevelDeploymentNodeNamesAreNotUniqueButTheyExistInDifferentEnvironments.json new file mode 100644 index 000000000..14b569cc3 --- /dev/null +++ b/structurizr-client/test/integration/workspaceValidation/TopLevelDeploymentNodeNamesAreNotUniqueButTheyExistInDifferentEnvironments.json @@ -0,0 +1,27 @@ +{ + "id" : 0, + "name" : "Name", + "description" : "Description", + "configuration" : { }, + "model" : { + "deploymentNodes" : [ { + "id" : "1", + "name" : "Deployment Node", + "environment" : "Development", + "instances" : 1 + }, { + "id" : "2", + "name" : "Deployment Node", + "environment" : "Production", + "instances" : 1 + } ] + }, + "documentation" : { }, + "views" : { + "configuration" : { + "branding" : { }, + "styles" : { }, + "terminology" : { } + } + } +} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/model/Model.java b/structurizr-core/src/com/structurizr/model/Model.java index 0d0f4a8e7..1e304c56f 100644 --- a/structurizr-core/src/com/structurizr/model/Model.java +++ b/structurizr-core/src/com/structurizr/model/Model.java @@ -379,7 +379,7 @@ void hydrate() { } for (DeploymentNode deploymentNode : deploymentNodes) { - checkNameIsUnique(deploymentNodes, deploymentNode.getName(), "A top-level deployment node named \"%s\" already exists."); + checkNameIsUnique(deploymentNodes, deploymentNode.getName(), deploymentNode.getEnvironment(), "A top-level deployment node named \"%s\" already exists for the environment named \"" + deploymentNode.getEnvironment() + "\"."); if (deploymentNode.hasChildren()) { checkChildNamesAreUnique(deploymentNode); @@ -413,9 +413,16 @@ private void checkNameIsUnique(Collection elements, String na } } + private void checkNameIsUnique(Collection deploymentNodes, String name, String environment, String errorMessage) { + if (deploymentNodes.stream().filter(dn -> dn.getName().equalsIgnoreCase(name) && dn.getEnvironment().equals(environment)).count() != 1) { + throw new WorkspaceValidationException( + String.format(errorMessage, name)); + } + } + private void checkChildNamesAreUnique(DeploymentNode deploymentNode) { for (DeploymentNode child : deploymentNode.getChildren()) { - checkNameIsUnique(deploymentNode.getChildren(), child.getName(), "A deployment node named \"%s\" already exists within \"" + deploymentNode.getName() + "\"."); + checkNameIsUnique(deploymentNode.getChildren(), child.getName(), deploymentNode.getEnvironment(), "A deployment node named \"%s\" already exists within \"" + deploymentNode.getName() + "\"."); if (child.hasChildren()) { checkChildNamesAreUnique(child); From 344ca4c02d80f338dd05db3686cadb3e2aa422f4 Mon Sep 17 00:00:00 2001 From: Nicolas Delsaux Date: Sat, 16 Nov 2019 21:02:54 +0100 Subject: [PATCH 037/717] Initial commit of an implementation for #132 --- .../io/plantuml/C4PlantUMLWriter.java | 248 ++++++++++++++++++ 1 file changed, 248 insertions(+) create mode 100644 structurizr-plantuml/src/com/structurizr/io/plantuml/C4PlantUMLWriter.java diff --git a/structurizr-plantuml/src/com/structurizr/io/plantuml/C4PlantUMLWriter.java b/structurizr-plantuml/src/com/structurizr/io/plantuml/C4PlantUMLWriter.java new file mode 100644 index 000000000..adbbc3d98 --- /dev/null +++ b/structurizr-plantuml/src/com/structurizr/io/plantuml/C4PlantUMLWriter.java @@ -0,0 +1,248 @@ +package com.structurizr.io.plantuml; + +import static java.lang.String.format; + +import java.awt.Window.Type; +import java.io.IOException; +import java.io.Writer; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.logging.Level; + +import com.structurizr.io.plantuml.PlantUMLWriter; +import com.structurizr.model.Component; +import com.structurizr.model.Container; +import com.structurizr.model.ContainerInstance; +import com.structurizr.model.Element; +import com.structurizr.model.Person; +import com.structurizr.model.Relationship; +import com.structurizr.model.SoftwareSystem; +import com.structurizr.view.View; + +/** + * This writer extends the classical one to use the C4-PlantUML sprite library + * avaiable on GitHub. + * + * To make full use of that sprite library, we use Structurizr properties to + * tweak rendering. + * + * It is possible to change relationships directions and rendering using the + * {@link #C4_LAYOUT_DIRECTION} and {@link #C4_LAYOUT_MODE} mode properties. + * + * @see https://github.com/RicardoNiepel/C4-PlantUML + * @author nicolas-delsaux + * + */ +public class C4PlantUMLWriter extends PlantUMLWriter { + /** + * When the value associated with this name resolves to the boolean value "true" (the default) + * the associated element is concerned internal (if possible). + * Currently, this applies to Person, SoftwareSystem, + */ + public static final String C4_INTERNAL = "c4:state:internal"; + /** + * This property indicates to C4-PlantUML library which relationship type to + * use. Possible values are given in the {@link Directions} enum + */ + public static final String C4_LAYOUT_DIRECTION = "c4:layout:direction"; + /** + * This property indicates to C4-PlantUML library which relationship mode to + * use. Possible values are given in the {@link RelationshipModes} enum + */ + public static final String C4_LAYOUT_MODE = "c4:layout:mode"; + /** + * Defines the {@link Type} of component. + */ + public static final String C4_ELEMENT_TYPE = "c4:element:type"; + + public static enum Type { + Default, Db + } + + public static enum Directions { + Up, Down, Right, Left + } + + public static enum RelationshipModes { + Rel, Neighbor, Back, Back_Neighbor, Lay + } + + public static class NoMacroFound extends RuntimeException { + + } + + private abstract class C4ElementWriter { + + public void write(View view, WrittenElement element, Writer writer, String prefix) throws IOException { + final String id = idOf(element); + final String separator = System.lineSeparator(); + doWrite(view, element, writer, prefix, id, separator); + } + + abstract void doWrite(View view, WrittenElement element, Writer writer, String prefix, String id, + String separator) throws IOException; + + } + + private class PersonWriter extends C4ElementWriter { + + @Override + void doWrite(View view, Person element, Writer writer, String prefix, String id, String separator) + throws IOException { + String macro = "Person_Ext"; + if(Boolean.parseBoolean(element.getProperties().getOrDefault(C4_INTERNAL, "true"))) { + macro = "Person"; + } + writer.write(format("%s%s(%s, \"%s\", \"%s\")%s", prefix, macro, id, element.getName(), + element.getDescription(), separator)); + } + } + + private class SoftwareSystemWriter extends C4ElementWriter { + @Override + void doWrite(View view, SoftwareSystem element, Writer writer, String prefix, String id, String separator) + throws IOException { + boolean internal = Boolean.parseBoolean(element.getProperties().getOrDefault(C4_INTERNAL, "true")); + Type type = Type.valueOf(element.getProperties().getOrDefault(C4_ELEMENT_TYPE, Type.Default.name())); + String macro = String.format("System%s%s", + type==Type.Default ? "" : type.name(), + internal ? "" : "_Ext"); + writer.write(format("%s%s(%s, \"%s\", \"%s\")%s", prefix, macro, id, element.getName(), + element.getDescription(), separator)); + } + } + + private class ContainerWriter extends C4ElementWriter { + @Override + void doWrite(View view, Container element, Writer writer, String prefix, String id, String separator) + throws IOException { + Type type = Type.valueOf(element.getProperties().getOrDefault(C4_ELEMENT_TYPE, Type.Default.name())); + String macro = String.format("Container%s", + type==Type.Default ? "" : type.name()); + writer.write(format("%s%s(%s, \"%s\", \"%s\", \"%s\")%s", prefix, macro, id, element.getName(), + element.getTechnology(), element.getDescription(), separator)); + } + } + + private class ComponentWriter extends C4ElementWriter { + @Override + void doWrite(View view, Component element, Writer writer, String prefix, String id, String separator) + throws IOException { + Type type = Type.valueOf(element.getProperties().getOrDefault(C4_ELEMENT_TYPE, Type.Default.name())); + String macro = String.format("Component%s", + type==Type.Default ? "" : type.name()); + writer.write(format("%s%s(%s, \"%s\", \"%s\", \"%s\")%s", prefix, macro, id, element.getName(), + element.getTechnology(), element.getDescription(), separator)); + } + } + + private class ContainerInstanceWriter extends C4ElementWriter { + @Override + void doWrite(View view, ContainerInstance element, Writer writer, String prefix, String id, String separator) + throws IOException { + Type type = Type.valueOf(element.getProperties().getOrDefault(C4_ELEMENT_TYPE, + element.getContainer().getProperties().getOrDefault(C4_ELEMENT_TYPE,Type.Default.name()) + )); + String macro = String.format("Container%s", + type==Type.Default ? "" : type.name()); + writer.write(format("%s%s(%s, \"%s\", \"%s\", \"%s\")%s", prefix, macro, id, + element.getContainer().getName(), element.getContainer().getTechnology(), + element.getContainer().getDescription(), separator)); + } + } + + private static final java.util.logging.Logger logger = java.util.logging.Logger + .getLogger(C4PlantUMLWriter.class.getName()); + + public C4PlantUMLWriter() { + super(); + try { + addIncludeURL(new URI("https://raw.githubusercontent.com/RicardoNiepel/C4-PlantUML/master/C4.puml")); + addIncludeURL( + new URI("https://raw.githubusercontent.com/RicardoNiepel/C4-PlantUML/master/C4_Context.puml")); + addIncludeURL( + new URI("https://raw.githubusercontent.com/RicardoNiepel/C4-PlantUML/master/C4_Container.puml")); + addIncludeURL( + new URI("https://raw.githubusercontent.com/RicardoNiepel/C4-PlantUML/master/C4_Component.puml")); + } catch (URISyntaxException e) { + logger.log(Level.SEVERE, "Using C4-PlantUML shoulld not trigger URI error", e); + } + } + + @Override + protected void write(View view, ContainerInstance container, Writer writer, int indent) { + try { + new ContainerInstanceWriter().write(view, container, writer, calculateIndent(indent)); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * We replace the write element method to use macros provided by C4-PlantUML + */ + protected void write(View view, Element element, Writer writer, boolean indent) { + try { + final String prefix = indent ? " " : ""; + final String id = idOf(element); + final String separator = System.lineSeparator(); + getWriterFor(element).write(view, element, writer, prefix); + } catch (NoMacroFound noMacro) { + super.write(view, element, writer, indent); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private C4ElementWriter getWriterFor(Element element) { + if (element instanceof Person) { + return new PersonWriter(); + } else if (element instanceof SoftwareSystem) { + return new SoftwareSystemWriter(); + } else if (element instanceof Container) { + return new ContainerWriter(); + } else if (element instanceof Component) { + return new ComponentWriter(); + } + throw new NoMacroFound(); + } + + @Override + protected void writeRelationship(View view, Relationship relationship, Writer writer) { + try { + final String separator = System.lineSeparator(); + String relationshipMacro = null; + RelationshipModes mode = RelationshipModes.Rel; + if (relationship.getProperties().containsKey(C4_LAYOUT_MODE)) { + mode = RelationshipModes.valueOf(relationship.getProperties().get(C4_LAYOUT_MODE)); + } + switch (mode) { + case Lay: + case Rel: + relationshipMacro = mode.name(); + if (relationship.getProperties().containsKey(C4_LAYOUT_DIRECTION)) { + Directions direction = Directions.valueOf(relationship.getProperties().get(C4_LAYOUT_DIRECTION)); + relationshipMacro = String.format("%s_%s", relationshipMacro, direction); + } + break; + default: + relationshipMacro = String.format("%s_%s", relationshipMacro, mode); + } + if (relationship.getDescription() == null) { + writer.write(format("%s(%s, %s)%s", relationshipMacro, idOf(relationship.getSource()), + idOf(relationship.getDestination()), separator)); + } else { + if (relationship.getTechnology() == null) { + writer.write(format("%s(%s, %s, '%s')%s", relationshipMacro, idOf(relationship.getSource()), + idOf(relationship.getDestination()), relationship.getDescription(), separator)); + } else { + writer.write(format("%s(%s, %s, '%s', %s)%s", relationshipMacro, idOf(relationship.getSource()), + idOf(relationship.getDestination()), relationship.getDescription(), + relationship.getTechnology(), separator)); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + } +} \ No newline at end of file From 6460f29503205ac962ea22b6b5f9b03e3ef21c9d Mon Sep 17 00:00:00 2001 From: Nicolas Delsaux Date: Sun, 17 Nov 2019 15:37:30 +0100 Subject: [PATCH 038/717] Using structurizr location instead of properties is undoubtly better! --- .../io/plantuml/C4PlantUMLWriter.java | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/structurizr-plantuml/src/com/structurizr/io/plantuml/C4PlantUMLWriter.java b/structurizr-plantuml/src/com/structurizr/io/plantuml/C4PlantUMLWriter.java index adbbc3d98..86f8baca0 100644 --- a/structurizr-plantuml/src/com/structurizr/io/plantuml/C4PlantUMLWriter.java +++ b/structurizr-plantuml/src/com/structurizr/io/plantuml/C4PlantUMLWriter.java @@ -34,12 +34,6 @@ * */ public class C4PlantUMLWriter extends PlantUMLWriter { - /** - * When the value associated with this name resolves to the boolean value "true" (the default) - * the associated element is concerned internal (if possible). - * Currently, this applies to Person, SoftwareSystem, - */ - public static final String C4_INTERNAL = "c4:state:internal"; /** * This property indicates to C4-PlantUML library which relationship type to * use. Possible values are given in the {@link Directions} enum @@ -89,11 +83,16 @@ private class PersonWriter extends C4ElementWriter { @Override void doWrite(View view, Person element, Writer writer, String prefix, String id, String separator) throws IOException { - String macro = "Person_Ext"; - if(Boolean.parseBoolean(element.getProperties().getOrDefault(C4_INTERNAL, "true"))) { + String macro = null; + switch(element.getLocation()) { + case External: + macro = "Person_Ext"; + break; + default: macro = "Person"; } - writer.write(format("%s%s(%s, \"%s\", \"%s\")%s", prefix, macro, id, element.getName(), + writer.write(format("%s%s(%s, \"%s\", \"%s\")%s", prefix, + macro, id, element.getName(), element.getDescription(), separator)); } } @@ -102,7 +101,7 @@ private class SoftwareSystemWriter extends C4ElementWriter { @Override void doWrite(View view, SoftwareSystem element, Writer writer, String prefix, String id, String separator) throws IOException { - boolean internal = Boolean.parseBoolean(element.getProperties().getOrDefault(C4_INTERNAL, "true")); + boolean internal = !element.getLocation().equals(Location.External); Type type = Type.valueOf(element.getProperties().getOrDefault(C4_ELEMENT_TYPE, Type.Default.name())); String macro = String.format("System%s%s", type==Type.Default ? "" : type.name(), From 25e81a912879d3e1066dca9daef4b58ff04caf51 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 19 Nov 2019 15:39:03 +0000 Subject: [PATCH 039/717] Fixes a bug during JSON deserialization. --- structurizr-core/src/com/structurizr/view/RelationshipView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structurizr-core/src/com/structurizr/view/RelationshipView.java b/structurizr-core/src/com/structurizr/view/RelationshipView.java index 16f9fc5a1..0c03e72cd 100644 --- a/structurizr-core/src/com/structurizr/view/RelationshipView.java +++ b/structurizr-core/src/com/structurizr/view/RelationshipView.java @@ -90,7 +90,7 @@ public void setDescription(String description) { * @return the order, as a String */ public String getOrder() { - return order; + return order != null ? order : ""; } /** From 9e0ab156ebedf88be776ec1d5e355942a60bb5b0 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 19 Nov 2019 18:30:48 +0000 Subject: [PATCH 040/717] Added support for element border colours. --- build.gradle | 2 +- docs/changelog.md | 4 +++ .../com/structurizr/view/ElementStyle.java | 25 +++++++++++++ .../src/com/structurizr/view/Styles.java | 9 +++-- .../structurizr/view/ElementStyleTests.java | 36 +++++++++++++++++++ .../unit/com/structurizr/view/StyleTests.java | 3 +- 6 files changed, 75 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index e3032958f..f150a8369 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.3.1' + version = '1.3.2' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index cecbe42a4..359736e50 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.3.2 (unreleased) + +- Added support for element border colours. + ## 1.3.1 (29th October 2019) - The automatic layout algorithm can now be configured on individual views. diff --git a/structurizr-core/src/com/structurizr/view/ElementStyle.java b/structurizr-core/src/com/structurizr/view/ElementStyle.java index 1bf04fbc0..7ae14e58b 100644 --- a/structurizr-core/src/com/structurizr/view/ElementStyle.java +++ b/structurizr-core/src/com/structurizr/view/ElementStyle.java @@ -37,6 +37,9 @@ public final class ElementStyle { @JsonInclude(value = JsonInclude.Include.NON_NULL) private Border border; + @JsonInclude(value = JsonInclude.Include.NON_NULL) + private String borderColor; + @JsonInclude(value = JsonInclude.Include.NON_NULL) private Integer opacity; @@ -238,6 +241,28 @@ public ElementStyle border(Border border) { return this; } + /** + * Gets the border colour of the element, as a HTML RGB hex string (e.g. #123456). + * + * @return the border colour as a String, or null if not specified + */ + public String getBorderColor() { + return borderColor; + } + + public void setBorderColor(String color) { + if (Color.isHexColorCode(color)) { + this.borderColor = color.toLowerCase(); + } else { + throw new IllegalArgumentException(color + " is not a valid hex colour code."); + } + } + + public ElementStyle borderColor(String color) { + setBorderColor(color); + return this; + } + /** * Gets the opacity used when rendering the element. * diff --git a/structurizr-core/src/com/structurizr/view/Styles.java b/structurizr-core/src/com/structurizr/view/Styles.java index 3111838d4..e3e5a2aff 100644 --- a/structurizr-core/src/com/structurizr/view/Styles.java +++ b/structurizr-core/src/com/structurizr/view/Styles.java @@ -2,6 +2,7 @@ import com.structurizr.model.Element; import com.structurizr.model.Relationship; +import com.structurizr.util.StringUtils; import java.util.Collection; import java.util.LinkedList; @@ -108,14 +109,18 @@ public ElementStyle findElementStyle(Element element) { for (String tag : element.getTagsAsSet()) { ElementStyle elementStyle = findElementStyle(tag); if (elementStyle != null) { - if (elementStyle.getBackground() != null && elementStyle.getBackground().trim().length() > 0) { + if (!StringUtils.isNullOrEmpty(elementStyle.getBackground())) { style.setBackground(elementStyle.getBackground()); } - if (elementStyle.getColor() != null && elementStyle.getColor().trim().length() > 0) { + if (!StringUtils.isNullOrEmpty(elementStyle.getColor())) { style.setColor(elementStyle.getColor()); } + if (!StringUtils.isNullOrEmpty(elementStyle.getBorderColor())) { + style.setBorderColor(elementStyle.getBorderColor()); + } + if (elementStyle.getShape() != null) { style.setShape(elementStyle.getShape()); } diff --git a/structurizr-core/test/unit/com/structurizr/view/ElementStyleTests.java b/structurizr-core/test/unit/com/structurizr/view/ElementStyleTests.java index f7d5de954..790bc9f38 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ElementStyleTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ElementStyleTests.java @@ -166,6 +166,42 @@ public void test_setIcon_DoesNothing_WhenAnEmptyUrlIsSpecified() { assertNull(style.getIcon()); } + @Test + public void test_setBorderColor_SetsTheBorderColorProperty_WhenAValidHexColorCodeIsSpecified() { + ElementStyle style = new ElementStyle(); + style.setBorderColor("#ffffff"); + assertEquals("#ffffff", style.getBorderColor()); + style.setBorderColor("#FFFFFF"); + assertEquals("#ffffff", style.getBorderColor()); + style.setBorderColor("#123456"); + assertEquals("#123456", style.getBorderColor()); + } + + @Test + public void test_borderColor_SetsTheBorderColorProperty_WhenAValidHexColorCodeIsSpecified() { + ElementStyle style = new ElementStyle(); + style.borderColor("#ffffff"); + assertEquals("#ffffff", style.getBorderColor()); + + style.borderColor("#FFFFFF"); + assertEquals("#ffffff", style.getBorderColor()); + + style.borderColor("#123456"); + assertEquals("#123456", style.getBorderColor()); + } + + @Test(expected = IllegalArgumentException.class) + public void test_setBorderColor_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { + ElementStyle style = new ElementStyle(); + style.setBorderColor("white"); + } + + @Test(expected = IllegalArgumentException.class) + public void test_borderColor_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { + ElementStyle style = new ElementStyle(); + style.borderColor("white"); + } + } \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/view/StyleTests.java b/structurizr-core/test/unit/com/structurizr/view/StyleTests.java index 53bfbccdb..736b6dac2 100644 --- a/structurizr-core/test/unit/com/structurizr/view/StyleTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/StyleTests.java @@ -36,11 +36,12 @@ public void test_findElementStyle_ReturnsTheCorrectStyle_WhenStylesAreDefined() element.addTags("Some Tag"); styles.addElementStyle(Tags.SOFTWARE_SYSTEM).background("#ff0000").color("#ffffff"); - styles.addElementStyle("Some Tag").color("#0000ff").shape(Shape.RoundedBox); + styles.addElementStyle("Some Tag").color("#0000ff").borderColor("#00ff00").shape(Shape.RoundedBox); ElementStyle style = styles.findElementStyle(element); assertEquals("#ff0000", style.getBackground()); assertEquals("#0000ff", style.getColor()); + assertEquals("#00ff00", style.getBorderColor()); assertEquals(Shape.RoundedBox, style.getShape()); } From 62ad16bee218154015ae65bcaea9787ad00989ef Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 19 Nov 2019 19:23:58 +0000 Subject: [PATCH 041/717] Updated version numbers again. --- docs/spring-petclinic.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/spring-petclinic.md b/docs/spring-petclinic.md index 495cd06b9..c3b56740f 100644 --- a/docs/spring-petclinic.md +++ b/docs/spring-petclinic.md @@ -31,8 +31,8 @@ With the Spring PetClinic application built, we now need to create a software ar Name | Description ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-client:1.3.0 | The Structurizr API client library. -com.structurizr:structurizr-spring:1.3.0 | The Spring component finder. +com.structurizr:structurizr-client:1.3.1 | The Structurizr API client library. +com.structurizr:structurizr-spring:1.3.1 | The Spring component finder. First we need to create a little boilerplate code to create a workspace and a model. From b9de61066e759b97056028a5eb466db21c266a3b Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 19 Nov 2019 19:26:49 +0000 Subject: [PATCH 042/717] Optimized imports, and fixed a typo. --- .../io/plantuml/C4PlantUMLWriter.java | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/structurizr-plantuml/src/com/structurizr/io/plantuml/C4PlantUMLWriter.java b/structurizr-plantuml/src/com/structurizr/io/plantuml/C4PlantUMLWriter.java index 86f8baca0..b88ab4f51 100644 --- a/structurizr-plantuml/src/com/structurizr/io/plantuml/C4PlantUMLWriter.java +++ b/structurizr-plantuml/src/com/structurizr/io/plantuml/C4PlantUMLWriter.java @@ -1,27 +1,19 @@ package com.structurizr.io.plantuml; -import static java.lang.String.format; +import com.structurizr.model.*; +import com.structurizr.view.View; -import java.awt.Window.Type; import java.io.IOException; import java.io.Writer; import java.net.URI; import java.net.URISyntaxException; import java.util.logging.Level; -import com.structurizr.io.plantuml.PlantUMLWriter; -import com.structurizr.model.Component; -import com.structurizr.model.Container; -import com.structurizr.model.ContainerInstance; -import com.structurizr.model.Element; -import com.structurizr.model.Person; -import com.structurizr.model.Relationship; -import com.structurizr.model.SoftwareSystem; -import com.structurizr.view.View; +import static java.lang.String.format; /** * This writer extends the classical one to use the C4-PlantUML sprite library - * avaiable on GitHub. + * available on GitHub. * * To make full use of that sprite library, we use Structurizr properties to * tweak rendering. From f5a0c3543bc57d1c46a0165320e7cb2a8afac3bd Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 20 Nov 2019 07:32:10 +0000 Subject: [PATCH 043/717] Fixed javadoc error. --- .../src/com/structurizr/io/plantuml/C4PlantUMLWriter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structurizr-plantuml/src/com/structurizr/io/plantuml/C4PlantUMLWriter.java b/structurizr-plantuml/src/com/structurizr/io/plantuml/C4PlantUMLWriter.java index b88ab4f51..0d5ff0984 100644 --- a/structurizr-plantuml/src/com/structurizr/io/plantuml/C4PlantUMLWriter.java +++ b/structurizr-plantuml/src/com/structurizr/io/plantuml/C4PlantUMLWriter.java @@ -21,7 +21,7 @@ * It is possible to change relationships directions and rendering using the * {@link #C4_LAYOUT_DIRECTION} and {@link #C4_LAYOUT_MODE} mode properties. * - * @see https://github.com/RicardoNiepel/C4-PlantUML + * @see https://github.com/RicardoNiepel/C4-PlantUML * @author nicolas-delsaux * */ From 25eba12c7861fb5c3e7734026030f750f85ff7a1 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 20 Nov 2019 07:33:29 +0000 Subject: [PATCH 044/717] Renamed borderColor to stroke. --- .../com/structurizr/view/ElementStyle.java | 50 +++++++++---------- .../src/com/structurizr/view/Styles.java | 4 +- .../structurizr/view/ElementStyleTests.java | 36 ++++++------- .../unit/com/structurizr/view/StyleTests.java | 4 +- 4 files changed, 47 insertions(+), 47 deletions(-) diff --git a/structurizr-core/src/com/structurizr/view/ElementStyle.java b/structurizr-core/src/com/structurizr/view/ElementStyle.java index 7ae14e58b..071a130b0 100644 --- a/structurizr-core/src/com/structurizr/view/ElementStyle.java +++ b/structurizr-core/src/com/structurizr/view/ElementStyle.java @@ -22,6 +22,9 @@ public final class ElementStyle { @JsonInclude(value = JsonInclude.Include.NON_NULL) private String background; + @JsonInclude(value = JsonInclude.Include.NON_NULL) + private String stroke; + @JsonInclude(value = JsonInclude.Include.NON_NULL) private String color; @@ -37,9 +40,6 @@ public final class ElementStyle { @JsonInclude(value = JsonInclude.Include.NON_NULL) private Border border; - @JsonInclude(value = JsonInclude.Include.NON_NULL) - private String borderColor; - @JsonInclude(value = JsonInclude.Include.NON_NULL) private Integer opacity; @@ -141,6 +141,28 @@ public ElementStyle background(String background) { return this; } + /** + * Gets the stroke colour of the element, as a HTML RGB hex string (e.g. #123456). + * + * @return the stroke colour as a String, or null if not specified + */ + public String getStroke() { + return stroke; + } + + public void setStroke(String color) { + if (Color.isHexColorCode(color)) { + this.stroke = color.toLowerCase(); + } else { + throw new IllegalArgumentException(color + " is not a valid hex colour code."); + } + } + + public ElementStyle stroke(String color) { + setStroke(color); + return this; + } + /** * Gets the foreground (text) colour of the element, as a HTML RGB hex string (e.g. #123456). * @@ -241,28 +263,6 @@ public ElementStyle border(Border border) { return this; } - /** - * Gets the border colour of the element, as a HTML RGB hex string (e.g. #123456). - * - * @return the border colour as a String, or null if not specified - */ - public String getBorderColor() { - return borderColor; - } - - public void setBorderColor(String color) { - if (Color.isHexColorCode(color)) { - this.borderColor = color.toLowerCase(); - } else { - throw new IllegalArgumentException(color + " is not a valid hex colour code."); - } - } - - public ElementStyle borderColor(String color) { - setBorderColor(color); - return this; - } - /** * Gets the opacity used when rendering the element. * diff --git a/structurizr-core/src/com/structurizr/view/Styles.java b/structurizr-core/src/com/structurizr/view/Styles.java index e3e5a2aff..62f731b2b 100644 --- a/structurizr-core/src/com/structurizr/view/Styles.java +++ b/structurizr-core/src/com/structurizr/view/Styles.java @@ -117,8 +117,8 @@ public ElementStyle findElementStyle(Element element) { style.setColor(elementStyle.getColor()); } - if (!StringUtils.isNullOrEmpty(elementStyle.getBorderColor())) { - style.setBorderColor(elementStyle.getBorderColor()); + if (!StringUtils.isNullOrEmpty(elementStyle.getStroke())) { + style.setStroke(elementStyle.getStroke()); } if (elementStyle.getShape() != null) { diff --git a/structurizr-core/test/unit/com/structurizr/view/ElementStyleTests.java b/structurizr-core/test/unit/com/structurizr/view/ElementStyleTests.java index 790bc9f38..2f85d4d6a 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ElementStyleTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ElementStyleTests.java @@ -167,41 +167,41 @@ public void test_setIcon_DoesNothing_WhenAnEmptyUrlIsSpecified() { } @Test - public void test_setBorderColor_SetsTheBorderColorProperty_WhenAValidHexColorCodeIsSpecified() { + public void test_setStroke_SetsTheStrokeProperty_WhenAValidHexColorCodeIsSpecified() { ElementStyle style = new ElementStyle(); - style.setBorderColor("#ffffff"); - assertEquals("#ffffff", style.getBorderColor()); + style.setStroke("#ffffff"); + assertEquals("#ffffff", style.getStroke()); - style.setBorderColor("#FFFFFF"); - assertEquals("#ffffff", style.getBorderColor()); + style.setStroke("#FFFFFF"); + assertEquals("#ffffff", style.getStroke()); - style.setBorderColor("#123456"); - assertEquals("#123456", style.getBorderColor()); + style.setStroke("#123456"); + assertEquals("#123456", style.getStroke()); } @Test - public void test_borderColor_SetsTheBorderColorProperty_WhenAValidHexColorCodeIsSpecified() { + public void test_Stroke_SetsTheStrokeProperty_WhenAValidHexColorCodeIsSpecified() { ElementStyle style = new ElementStyle(); - style.borderColor("#ffffff"); - assertEquals("#ffffff", style.getBorderColor()); + style.stroke("#ffffff"); + assertEquals("#ffffff", style.getStroke()); - style.borderColor("#FFFFFF"); - assertEquals("#ffffff", style.getBorderColor()); + style.stroke("#FFFFFF"); + assertEquals("#ffffff", style.getStroke()); - style.borderColor("#123456"); - assertEquals("#123456", style.getBorderColor()); + style.stroke("#123456"); + assertEquals("#123456", style.getStroke()); } @Test(expected = IllegalArgumentException.class) - public void test_setBorderColor_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { + public void test_setStroke_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { ElementStyle style = new ElementStyle(); - style.setBorderColor("white"); + style.setStroke("white"); } @Test(expected = IllegalArgumentException.class) - public void test_borderColor_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { + public void test_Stroke_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { ElementStyle style = new ElementStyle(); - style.borderColor("white"); + style.stroke("white"); } } \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/view/StyleTests.java b/structurizr-core/test/unit/com/structurizr/view/StyleTests.java index 736b6dac2..165ea6c09 100644 --- a/structurizr-core/test/unit/com/structurizr/view/StyleTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/StyleTests.java @@ -36,12 +36,12 @@ public void test_findElementStyle_ReturnsTheCorrectStyle_WhenStylesAreDefined() element.addTags("Some Tag"); styles.addElementStyle(Tags.SOFTWARE_SYSTEM).background("#ff0000").color("#ffffff"); - styles.addElementStyle("Some Tag").color("#0000ff").borderColor("#00ff00").shape(Shape.RoundedBox); + styles.addElementStyle("Some Tag").color("#0000ff").stroke("#00ff00").shape(Shape.RoundedBox); ElementStyle style = styles.findElementStyle(element); assertEquals("#ff0000", style.getBackground()); assertEquals("#0000ff", style.getColor()); - assertEquals("#00ff00", style.getBorderColor()); + assertEquals("#00ff00", style.getStroke()); assertEquals(Shape.RoundedBox, style.getShape()); } From 45ef886cbaf4a0d73dd9ea9301e73ba735a8760c Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 22 Nov 2019 13:15:54 +0000 Subject: [PATCH 045/717] Updated changelog to reflect release. --- docs/changelog.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 359736e50..89ddabd62 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,8 +1,8 @@ # Changelog -## 1.3.2 (unreleased) +## 1.3.2 (22nd November 2019) -- Added support for element border colours. +- Added support for element stroke colours. ## 1.3.1 (29th October 2019) From 5408ba353f3cec2017bf901d33c1b63dfbb442e1 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 22 Nov 2019 13:30:18 +0000 Subject: [PATCH 046/717] Repository split into "java" and "java-extensions". --- README.md | 19 +- docs/binaries.md | 11 +- docs/component-diagram.md | 2 +- docs/component-finder.md | 48 -- docs/decisions.md | 2 +- docs/deployment-diagram.md | 2 - docs/dynamic-diagram.md | 2 - docs/graphviz-and-dot.md | 58 -- docs/model.md | 2 +- docs/plantuml.md | 98 --- docs/spring-component-finder-strategies.md | 31 - docs/spring-petclinic.md | 221 ------ docs/structurizr-annotations.md | 106 --- docs/supplementing-from-source-code.md | 32 - docs/supporting-types.md | 104 --- docs/type-matchers.md | 65 -- docs/websequencediagrams.md | 18 - settings.gradle | 10 +- structurizr-adr-tools/build.gradle | 20 - .../documentation/AdrToolsImporter.java | 158 ---- .../0001-record-architecture-decisions.md | 19 - .../adrs/0002-implement-as-shell-scripts.md | 28 - .../0003-single-command-with-subcommands.md | 45 -- .../test/unit/adrs/0004-markdown-format.md | 40 - .../test/unit/adrs/0005-help-comments.md | 42 - ...n-in-other-version-control-repositories.md | 41 - ...-config-executable-to-get-configuration.md | 31 - .../0008-use-iso-8601-format-for-dates.md | 43 - .../test/unit/adrs/0009-help-scripts.md | 28 - .../test/unit/adrs/0010-asciidoc-format.md | 21 - .../documentation/AdrToolsImporterTests.java | 133 ---- structurizr-analysis/build.gradle | 26 - .../AbstractComponentFinderStrategy.java | 208 ----- .../analysis/AbstractTypeMatcher.java | 26 - .../analysis/AnnotationTypeMatcher.java | 27 - .../structurizr/analysis/ComponentFinder.java | 186 ----- .../analysis/ComponentFinderStrategy.java | 45 -- .../analysis/DefaultTypeRepository.java | 187 ----- .../analysis/DuplicateComponentException.java | 9 - .../analysis/DuplicateComponentStrategy.java | 23 - .../analysis/ExtendsClassTypeMatcher.java | 25 - ...ionOfInterfaceSupportingTypesStrategy.java | 36 - .../ImplementsInterfaceTypeMatcher.java | 25 - .../analysis/JavadocCommentFilter.java | 35 - .../analysis/NameSuffixTypeMatcher.java | 25 - ...sInSamePackageSupportingTypesStrategy.java | 42 - ...eferencedTypesSupportingTypesStrategy.java | 80 -- .../analysis/RegexTypeMatcher.java | 41 - .../SourceCodeComponentFinderStrategy.java | 148 ---- ...izrAnnotationsComponentFinderStrategy.java | 240 ------ .../analysis/SupportingTypesStrategy.java | 24 - ...owExceptionDuplicateComponentStrategy.java | 18 - .../structurizr/analysis/TypeCategory.java | 25 - .../com/structurizr/analysis/TypeMatcher.java | 14 - .../TypeMatcherComponentFinderStrategy.java | 46 -- .../structurizr/analysis/TypeRepository.java | 41 - .../com/structurizr/analysis/TypeUtils.java | 117 --- .../structurizr/analysis/TypeVisibility.java | 25 - .../AbstractWorkspaceTestBase.java | 12 - .../AbstractComponentFinderStrategyTests.java | 22 - .../analysis/AnnotationTypeMatcherTests.java | 38 - .../analysis/ComponentFinderTests.java | 68 -- .../analysis/DefaultTypeRepositoryTests.java | 85 -- .../ExtendsClassTypeMatcherTests.java | 55 -- .../ImplementsInterfaceTypeMatcherTests.java | 39 - .../analysis/JavadocCommentFilterTests.java | 61 -- .../analysis/NameSuffixTypeMatcherTests.java | 39 - .../analysis/RegexTypeMatcherTests.java | 57 -- ...ourceCodeComponentFinderStrategyTests.java | 74 -- ...notationsComponentFinderStrategyTests.java | 166 ---- ...peMatcherComponentFinderStrategyTests.java | 72 -- .../structurizr/analysis/TypeUtilsTests.java | 162 ---- .../AbstractComponentFinderStrategyTests.java | 285 ------- .../cyclicDependency/AComponent.java | 7 - .../cyclicDependency/BComponent.java | 7 - .../ComponentBase.java | 7 - .../LoggingComponent.java | 4 - .../SomeComponent.java | 4 - .../featureinterface/FeatureInterface.java | 4 - .../featureinterface/OtherComponent.java | 4 - .../featureinterface/SomeComponent.java | 4 - .../package1/MyController.java | 9 - .../package2/MyRepository.java | 4 - .../myapp/AbstractComponent.java | 4 - .../myapp/data/MyRepository.java | 7 - .../myapp/data/MyRepositoryImpl.java | 9 - .../myapp/data/MyRepositoryRowMapper.java | 9 - .../myapp/util/RowMapperHelper.java | 4 - .../myapp/web/MyController.java | 12 - .../SomeAbstractClass.java | 4 - .../test/DefaultTypeRepository/SomeClass.java | 10 - .../test/DefaultTypeRepository/SomeEnum.java | 4 - .../DefaultTypeRepository/SomeInterface.java | 4 - .../AnotherClass.java | 4 - .../SomeComponent.java | 10 - .../SomeComponentImpl.java | 10 - .../Controller.java | 18 - .../Repository.java | 10 - .../RepositoryImpl.java | 12 - .../MyController.java | 7 - .../MyRepository.java | 4 - .../MyRepositoryImpl.java | 15 - .../unit/test/TypeUtils/AnotherClass.java | 4 - .../test/TypeUtils/SomeAbstractClass.java | 4 - .../test/unit/test/TypeUtils/SomeClass.java | 10 - .../test/unit/test/TypeUtils/SomeEnum.java | 4 - .../unit/test/TypeUtils/SomeInterface.java | 4 - structurizr-annotations/build.gradle | 11 - .../com/structurizr/annotation/Component.java | 17 - .../annotation/UsedByContainer.java | 20 - .../annotation/UsedByContainers.java | 15 - .../structurizr/annotation/UsedByPeople.java | 15 - .../structurizr/annotation/UsedByPerson.java | 20 - .../annotation/UsedBySoftwareSystem.java | 20 - .../annotation/UsedBySoftwareSystems.java | 15 - .../structurizr/annotation/UsesComponent.java | 17 - .../structurizr/annotation/UsesContainer.java | 20 - .../annotation/UsesContainers.java | 15 - .../annotation/UsesSoftwareSystem.java | 20 - .../annotation/UsesSoftwareSystems.java | 15 - structurizr-dot/build.gradle | 6 - structurizr-dot/etc/graphviz-dot.properties | 13 - .../src/com/structurizr/io/dot/DotWriter.java | 84 -- structurizr-examples/build.gradle | 16 - .../src/com/structurizr/example/AdrTools.java | 64 -- .../src/com/structurizr/example/PlantUML.java | 59 -- .../example/SpringBootPetClinic.java | 143 ---- .../structurizr/example/SpringPetClinic.java | 201 ----- .../example/StructurizrAnnotations.java | 65 -- .../example/annotations/HtmlController.java | 14 - .../example/annotations/JdbcRepository.java | 12 - .../example/annotations/Repository.java | 10 - structurizr-javaee/build.gradle | 5 - .../JavaEEComponentFinderStrategy.java | 38 - structurizr-mermaid/build.gradle | 22 - .../io/mermaid/MermaidEncoder.java | 54 -- .../structurizr/io/mermaid/MermaidWriter.java | 520 ------------ .../io/mermaid/MermaidEncoderTests.java | 21 - .../io/mermaid/MermaidWriterTests.java | 12 - structurizr-plantuml/build.gradle | 21 - .../io/plantuml/C4PlantUMLWriter.java | 239 ------ .../io/plantuml/PlantUMLDiagram.java | 27 - .../io/plantuml/PlantUMLEncoder.java | 73 -- .../io/plantuml/PlantUMLWriter.java | 751 ------------------ .../io/plantuml/PlantUMLEncoderTests.java | 14 - .../io/plantuml/PlantUMLWriterTests.java | 717 ----------------- structurizr-spring/build.gradle | 11 - ...AbstractSpringComponentFinderStrategy.java | 92 --- ...pringComponentComponentFinderStrategy.java | 24 - .../SpringComponentFinderStrategy.java | 68 -- ...gMvcControllerComponentFinderStrategy.java | 25 - ...ringRepositoryComponentFinderStrategy.java | 75 -- ...RestControllerComponentFinderStrategy.java | 25 - .../SpringServiceComponentFinderStrategy.java | 24 - ...erviceEndpointComponentFinderStrategy.java | 25 - ...actSpringComponentFinderStrategyTests.java | 65 -- ...ComponentComponentFinderStrategyTests.java | 35 - .../SpringComponentFinderStrategyTests.java | 123 --- ...ontrollerComponentFinderStrategyTests.java | 35 - ...epositoryComponentFinderStrategyTests.java | 80 -- ...ontrollerComponentFinderStrategyTests.java | 35 - ...ngServiceComponentFinderStrategyTests.java | 35 - ...eEndpointComponentFinderStrategyTests.java | 35 - .../analysis/myapp/api/SomeApiController.java | 20 - .../myapp/data/JdbcSomeRepository.java | 14 - .../myapp/data/SomeNonPublicRepository.java | 6 - .../myapp/data/SomeOtherRepository.java | 6 - .../analysis/myapp/data/SomeRepository.java | 9 - .../analysis/myapp/domain/Something.java | 7 - .../analysis/myapp/service/SomeService.java | 7 - .../myapp/service/SomeServiceImpl.java | 21 - .../analysis/myapp/web/SomeController.java | 20 - .../SomeController.java | 7 - .../SomeNonPublicRepository.java | 6 - .../SomePublicRepository.java | 6 - .../SomeComponent.java | 4 - .../TheSomeComponentImpl.java | 7 - .../SomeController.java | 7 - .../annotation/JdbcSomeRepository.java | 4 - .../annotation/SomeRepository.java | 7 - .../crudRepository/SomeCrudRepository.java | 6 - .../jpaRepository/SomeJpaRepository.java | 6 - .../SomeController.java | 7 - .../SomeService.java | 4 - .../SomeServiceImpl.java | 7 - .../SomeWebService.java | 7 - structurizr-websequencediagrams/build.gradle | 21 - .../WebSequenceDiagramsEncoder.java | 21 - .../WebSequenceDiagramsWriter.java | 90 --- .../WebSequenceDiagramsEncoderTests.java | 37 - .../WebSequenceDiagramsWriterTests.java | 57 -- 191 files changed, 9 insertions(+), 9341 deletions(-) delete mode 100644 docs/component-finder.md delete mode 100644 docs/graphviz-and-dot.md delete mode 100644 docs/plantuml.md delete mode 100644 docs/spring-component-finder-strategies.md delete mode 100644 docs/spring-petclinic.md delete mode 100644 docs/structurizr-annotations.md delete mode 100644 docs/supplementing-from-source-code.md delete mode 100644 docs/supporting-types.md delete mode 100644 docs/type-matchers.md delete mode 100644 docs/websequencediagrams.md delete mode 100644 structurizr-adr-tools/build.gradle delete mode 100644 structurizr-adr-tools/src/com/structurizr/documentation/AdrToolsImporter.java delete mode 100644 structurizr-adr-tools/test/unit/adrs/0001-record-architecture-decisions.md delete mode 100644 structurizr-adr-tools/test/unit/adrs/0002-implement-as-shell-scripts.md delete mode 100644 structurizr-adr-tools/test/unit/adrs/0003-single-command-with-subcommands.md delete mode 100644 structurizr-adr-tools/test/unit/adrs/0004-markdown-format.md delete mode 100644 structurizr-adr-tools/test/unit/adrs/0005-help-comments.md delete mode 100644 structurizr-adr-tools/test/unit/adrs/0006-packaging-and-distribution-in-other-version-control-repositories.md delete mode 100644 structurizr-adr-tools/test/unit/adrs/0007-invoke-adr-config-executable-to-get-configuration.md delete mode 100644 structurizr-adr-tools/test/unit/adrs/0008-use-iso-8601-format-for-dates.md delete mode 100644 structurizr-adr-tools/test/unit/adrs/0009-help-scripts.md delete mode 100644 structurizr-adr-tools/test/unit/adrs/0010-asciidoc-format.md delete mode 100644 structurizr-adr-tools/test/unit/com/structurizr/documentation/AdrToolsImporterTests.java delete mode 100644 structurizr-analysis/build.gradle delete mode 100644 structurizr-analysis/src/com/structurizr/analysis/AbstractComponentFinderStrategy.java delete mode 100644 structurizr-analysis/src/com/structurizr/analysis/AbstractTypeMatcher.java delete mode 100644 structurizr-analysis/src/com/structurizr/analysis/AnnotationTypeMatcher.java delete mode 100644 structurizr-analysis/src/com/structurizr/analysis/ComponentFinder.java delete mode 100644 structurizr-analysis/src/com/structurizr/analysis/ComponentFinderStrategy.java delete mode 100644 structurizr-analysis/src/com/structurizr/analysis/DefaultTypeRepository.java delete mode 100644 structurizr-analysis/src/com/structurizr/analysis/DuplicateComponentException.java delete mode 100644 structurizr-analysis/src/com/structurizr/analysis/DuplicateComponentStrategy.java delete mode 100644 structurizr-analysis/src/com/structurizr/analysis/ExtendsClassTypeMatcher.java delete mode 100644 structurizr-analysis/src/com/structurizr/analysis/FirstImplementationOfInterfaceSupportingTypesStrategy.java delete mode 100644 structurizr-analysis/src/com/structurizr/analysis/ImplementsInterfaceTypeMatcher.java delete mode 100644 structurizr-analysis/src/com/structurizr/analysis/JavadocCommentFilter.java delete mode 100644 structurizr-analysis/src/com/structurizr/analysis/NameSuffixTypeMatcher.java delete mode 100644 structurizr-analysis/src/com/structurizr/analysis/ReferencedTypesInSamePackageSupportingTypesStrategy.java delete mode 100644 structurizr-analysis/src/com/structurizr/analysis/ReferencedTypesSupportingTypesStrategy.java delete mode 100644 structurizr-analysis/src/com/structurizr/analysis/RegexTypeMatcher.java delete mode 100644 structurizr-analysis/src/com/structurizr/analysis/SourceCodeComponentFinderStrategy.java delete mode 100644 structurizr-analysis/src/com/structurizr/analysis/StructurizrAnnotationsComponentFinderStrategy.java delete mode 100644 structurizr-analysis/src/com/structurizr/analysis/SupportingTypesStrategy.java delete mode 100644 structurizr-analysis/src/com/structurizr/analysis/ThrowExceptionDuplicateComponentStrategy.java delete mode 100644 structurizr-analysis/src/com/structurizr/analysis/TypeCategory.java delete mode 100644 structurizr-analysis/src/com/structurizr/analysis/TypeMatcher.java delete mode 100644 structurizr-analysis/src/com/structurizr/analysis/TypeMatcherComponentFinderStrategy.java delete mode 100644 structurizr-analysis/src/com/structurizr/analysis/TypeRepository.java delete mode 100644 structurizr-analysis/src/com/structurizr/analysis/TypeUtils.java delete mode 100644 structurizr-analysis/src/com/structurizr/analysis/TypeVisibility.java delete mode 100644 structurizr-analysis/test/unit/com/structurizr/AbstractWorkspaceTestBase.java delete mode 100644 structurizr-analysis/test/unit/com/structurizr/analysis/AbstractComponentFinderStrategyTests.java delete mode 100644 structurizr-analysis/test/unit/com/structurizr/analysis/AnnotationTypeMatcherTests.java delete mode 100644 structurizr-analysis/test/unit/com/structurizr/analysis/ComponentFinderTests.java delete mode 100644 structurizr-analysis/test/unit/com/structurizr/analysis/DefaultTypeRepositoryTests.java delete mode 100644 structurizr-analysis/test/unit/com/structurizr/analysis/ExtendsClassTypeMatcherTests.java delete mode 100644 structurizr-analysis/test/unit/com/structurizr/analysis/ImplementsInterfaceTypeMatcherTests.java delete mode 100644 structurizr-analysis/test/unit/com/structurizr/analysis/JavadocCommentFilterTests.java delete mode 100644 structurizr-analysis/test/unit/com/structurizr/analysis/NameSuffixTypeMatcherTests.java delete mode 100644 structurizr-analysis/test/unit/com/structurizr/analysis/RegexTypeMatcherTests.java delete mode 100644 structurizr-analysis/test/unit/com/structurizr/analysis/SourceCodeComponentFinderStrategyTests.java delete mode 100644 structurizr-analysis/test/unit/com/structurizr/analysis/StructurizrAnnotationsComponentFinderStrategyTests.java delete mode 100644 structurizr-analysis/test/unit/com/structurizr/analysis/TypeMatcherComponentFinderStrategyTests.java delete mode 100644 structurizr-analysis/test/unit/com/structurizr/analysis/TypeUtilsTests.java delete mode 100644 structurizr-analysis/test/unit/com/structurizr/analysis/reflections/AbstractComponentFinderStrategyTests.java delete mode 100644 structurizr-analysis/test/unit/com/structurizr/analysis/reflections/cyclicDependency/AComponent.java delete mode 100644 structurizr-analysis/test/unit/com/structurizr/analysis/reflections/cyclicDependency/BComponent.java delete mode 100644 structurizr-analysis/test/unit/com/structurizr/analysis/reflections/dependenciesFromSuperClass/ComponentBase.java delete mode 100644 structurizr-analysis/test/unit/com/structurizr/analysis/reflections/dependenciesFromSuperClass/LoggingComponent.java delete mode 100644 structurizr-analysis/test/unit/com/structurizr/analysis/reflections/dependenciesFromSuperClass/SomeComponent.java delete mode 100644 structurizr-analysis/test/unit/com/structurizr/analysis/reflections/featureinterface/FeatureInterface.java delete mode 100644 structurizr-analysis/test/unit/com/structurizr/analysis/reflections/featureinterface/OtherComponent.java delete mode 100644 structurizr-analysis/test/unit/com/structurizr/analysis/reflections/featureinterface/SomeComponent.java delete mode 100644 structurizr-analysis/test/unit/com/structurizr/analysis/reflections/multipleComponentFinders/package1/MyController.java delete mode 100644 structurizr-analysis/test/unit/com/structurizr/analysis/reflections/multipleComponentFinders/package2/MyRepository.java delete mode 100644 structurizr-analysis/test/unit/com/structurizr/analysis/reflections/supportingTypes/myapp/AbstractComponent.java delete mode 100644 structurizr-analysis/test/unit/com/structurizr/analysis/reflections/supportingTypes/myapp/data/MyRepository.java delete mode 100644 structurizr-analysis/test/unit/com/structurizr/analysis/reflections/supportingTypes/myapp/data/MyRepositoryImpl.java delete mode 100644 structurizr-analysis/test/unit/com/structurizr/analysis/reflections/supportingTypes/myapp/data/MyRepositoryRowMapper.java delete mode 100644 structurizr-analysis/test/unit/com/structurizr/analysis/reflections/supportingTypes/myapp/util/RowMapperHelper.java delete mode 100644 structurizr-analysis/test/unit/com/structurizr/analysis/reflections/supportingTypes/myapp/web/MyController.java delete mode 100644 structurizr-analysis/test/unit/test/DefaultTypeRepository/SomeAbstractClass.java delete mode 100644 structurizr-analysis/test/unit/test/DefaultTypeRepository/SomeClass.java delete mode 100644 structurizr-analysis/test/unit/test/DefaultTypeRepository/SomeEnum.java delete mode 100644 structurizr-analysis/test/unit/test/DefaultTypeRepository/SomeInterface.java delete mode 100644 structurizr-analysis/test/unit/test/MoreDefaultTypeRepository/AnotherClass.java delete mode 100644 structurizr-analysis/test/unit/test/SourceCodeComponentFinderStrategy/SomeComponent.java delete mode 100644 structurizr-analysis/test/unit/test/SourceCodeComponentFinderStrategy/SomeComponentImpl.java delete mode 100644 structurizr-analysis/test/unit/test/StructurizrAnnotationsComponentFinderStrategy/Controller.java delete mode 100644 structurizr-analysis/test/unit/test/StructurizrAnnotationsComponentFinderStrategy/Repository.java delete mode 100644 structurizr-analysis/test/unit/test/StructurizrAnnotationsComponentFinderStrategy/RepositoryImpl.java delete mode 100644 structurizr-analysis/test/unit/test/TypeMatcherComponentFinderStrategy/MyController.java delete mode 100644 structurizr-analysis/test/unit/test/TypeMatcherComponentFinderStrategy/MyRepository.java delete mode 100644 structurizr-analysis/test/unit/test/TypeMatcherComponentFinderStrategy/MyRepositoryImpl.java delete mode 100644 structurizr-analysis/test/unit/test/TypeUtils/AnotherClass.java delete mode 100644 structurizr-analysis/test/unit/test/TypeUtils/SomeAbstractClass.java delete mode 100644 structurizr-analysis/test/unit/test/TypeUtils/SomeClass.java delete mode 100644 structurizr-analysis/test/unit/test/TypeUtils/SomeEnum.java delete mode 100644 structurizr-analysis/test/unit/test/TypeUtils/SomeInterface.java delete mode 100644 structurizr-annotations/build.gradle delete mode 100644 structurizr-annotations/src/com/structurizr/annotation/Component.java delete mode 100644 structurizr-annotations/src/com/structurizr/annotation/UsedByContainer.java delete mode 100644 structurizr-annotations/src/com/structurizr/annotation/UsedByContainers.java delete mode 100644 structurizr-annotations/src/com/structurizr/annotation/UsedByPeople.java delete mode 100644 structurizr-annotations/src/com/structurizr/annotation/UsedByPerson.java delete mode 100644 structurizr-annotations/src/com/structurizr/annotation/UsedBySoftwareSystem.java delete mode 100644 structurizr-annotations/src/com/structurizr/annotation/UsedBySoftwareSystems.java delete mode 100644 structurizr-annotations/src/com/structurizr/annotation/UsesComponent.java delete mode 100644 structurizr-annotations/src/com/structurizr/annotation/UsesContainer.java delete mode 100644 structurizr-annotations/src/com/structurizr/annotation/UsesContainers.java delete mode 100644 structurizr-annotations/src/com/structurizr/annotation/UsesSoftwareSystem.java delete mode 100644 structurizr-annotations/src/com/structurizr/annotation/UsesSoftwareSystems.java delete mode 100644 structurizr-dot/build.gradle delete mode 100644 structurizr-dot/etc/graphviz-dot.properties delete mode 100644 structurizr-dot/src/com/structurizr/io/dot/DotWriter.java delete mode 100644 structurizr-examples/src/com/structurizr/example/AdrTools.java delete mode 100644 structurizr-examples/src/com/structurizr/example/PlantUML.java delete mode 100644 structurizr-examples/src/com/structurizr/example/SpringBootPetClinic.java delete mode 100644 structurizr-examples/src/com/structurizr/example/SpringPetClinic.java delete mode 100644 structurizr-examples/src/com/structurizr/example/StructurizrAnnotations.java delete mode 100644 structurizr-examples/src/com/structurizr/example/annotations/HtmlController.java delete mode 100644 structurizr-examples/src/com/structurizr/example/annotations/JdbcRepository.java delete mode 100644 structurizr-examples/src/com/structurizr/example/annotations/Repository.java delete mode 100644 structurizr-javaee/build.gradle delete mode 100644 structurizr-javaee/src/com/structurizr/analysis/JavaEEComponentFinderStrategy.java delete mode 100644 structurizr-mermaid/build.gradle delete mode 100644 structurizr-mermaid/src/com/structurizr/io/mermaid/MermaidEncoder.java delete mode 100644 structurizr-mermaid/src/com/structurizr/io/mermaid/MermaidWriter.java delete mode 100644 structurizr-mermaid/test/unit/com/structurizr/io/mermaid/MermaidEncoderTests.java delete mode 100644 structurizr-mermaid/test/unit/com/structurizr/io/mermaid/MermaidWriterTests.java delete mode 100644 structurizr-plantuml/build.gradle delete mode 100644 structurizr-plantuml/src/com/structurizr/io/plantuml/C4PlantUMLWriter.java delete mode 100644 structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLDiagram.java delete mode 100644 structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLEncoder.java delete mode 100644 structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLWriter.java delete mode 100644 structurizr-plantuml/test/unit/com/structurizr/io/plantuml/PlantUMLEncoderTests.java delete mode 100644 structurizr-plantuml/test/unit/com/structurizr/io/plantuml/PlantUMLWriterTests.java delete mode 100644 structurizr-spring/build.gradle delete mode 100644 structurizr-spring/src/com/structurizr/analysis/AbstractSpringComponentFinderStrategy.java delete mode 100644 structurizr-spring/src/com/structurizr/analysis/SpringComponentComponentFinderStrategy.java delete mode 100644 structurizr-spring/src/com/structurizr/analysis/SpringComponentFinderStrategy.java delete mode 100644 structurizr-spring/src/com/structurizr/analysis/SpringMvcControllerComponentFinderStrategy.java delete mode 100644 structurizr-spring/src/com/structurizr/analysis/SpringRepositoryComponentFinderStrategy.java delete mode 100644 structurizr-spring/src/com/structurizr/analysis/SpringRestControllerComponentFinderStrategy.java delete mode 100644 structurizr-spring/src/com/structurizr/analysis/SpringServiceComponentFinderStrategy.java delete mode 100644 structurizr-spring/src/com/structurizr/analysis/SpringWebServiceEndpointComponentFinderStrategy.java delete mode 100644 structurizr-spring/test/unit/com/structurizr/analysis/AbstractSpringComponentFinderStrategyTests.java delete mode 100644 structurizr-spring/test/unit/com/structurizr/analysis/SpringComponentComponentFinderStrategyTests.java delete mode 100644 structurizr-spring/test/unit/com/structurizr/analysis/SpringComponentFinderStrategyTests.java delete mode 100644 structurizr-spring/test/unit/com/structurizr/analysis/SpringMvcControllerComponentFinderStrategyTests.java delete mode 100644 structurizr-spring/test/unit/com/structurizr/analysis/SpringRepositoryComponentFinderStrategyTests.java delete mode 100644 structurizr-spring/test/unit/com/structurizr/analysis/SpringRestControllerComponentFinderStrategyTests.java delete mode 100644 structurizr-spring/test/unit/com/structurizr/analysis/SpringServiceComponentFinderStrategyTests.java delete mode 100644 structurizr-spring/test/unit/com/structurizr/analysis/SpringWebServiceEndpointComponentFinderStrategyTests.java delete mode 100644 structurizr-spring/test/unit/com/structurizr/analysis/myapp/api/SomeApiController.java delete mode 100644 structurizr-spring/test/unit/com/structurizr/analysis/myapp/data/JdbcSomeRepository.java delete mode 100644 structurizr-spring/test/unit/com/structurizr/analysis/myapp/data/SomeNonPublicRepository.java delete mode 100644 structurizr-spring/test/unit/com/structurizr/analysis/myapp/data/SomeOtherRepository.java delete mode 100644 structurizr-spring/test/unit/com/structurizr/analysis/myapp/data/SomeRepository.java delete mode 100644 structurizr-spring/test/unit/com/structurizr/analysis/myapp/domain/Something.java delete mode 100644 structurizr-spring/test/unit/com/structurizr/analysis/myapp/service/SomeService.java delete mode 100644 structurizr-spring/test/unit/com/structurizr/analysis/myapp/service/SomeServiceImpl.java delete mode 100644 structurizr-spring/test/unit/com/structurizr/analysis/myapp/web/SomeController.java delete mode 100644 structurizr-spring/test/unit/test/AbstractSpringComponentFinderStrategy/SomeController.java delete mode 100644 structurizr-spring/test/unit/test/AbstractSpringComponentFinderStrategy/SomeNonPublicRepository.java delete mode 100644 structurizr-spring/test/unit/test/AbstractSpringComponentFinderStrategy/SomePublicRepository.java delete mode 100644 structurizr-spring/test/unit/test/SpringComponentComponentFinderStrategy/SomeComponent.java delete mode 100644 structurizr-spring/test/unit/test/SpringComponentComponentFinderStrategy/TheSomeComponentImpl.java delete mode 100644 structurizr-spring/test/unit/test/SpringMvcControllerComponentFinderStrategy/SomeController.java delete mode 100644 structurizr-spring/test/unit/test/SpringRepositoryComponentFinderStrategy/annotation/JdbcSomeRepository.java delete mode 100644 structurizr-spring/test/unit/test/SpringRepositoryComponentFinderStrategy/annotation/SomeRepository.java delete mode 100644 structurizr-spring/test/unit/test/SpringRepositoryComponentFinderStrategy/crudRepository/SomeCrudRepository.java delete mode 100644 structurizr-spring/test/unit/test/SpringRepositoryComponentFinderStrategy/jpaRepository/SomeJpaRepository.java delete mode 100644 structurizr-spring/test/unit/test/SpringRestControllerComponentFinderStrategy/SomeController.java delete mode 100644 structurizr-spring/test/unit/test/SpringServiceComponentFinderStrategy/SomeService.java delete mode 100644 structurizr-spring/test/unit/test/SpringServiceComponentFinderStrategy/SomeServiceImpl.java delete mode 100644 structurizr-spring/test/unit/test/SpringWebServiceEndpointComponentFinderStrategy/SomeWebService.java delete mode 100644 structurizr-websequencediagrams/build.gradle delete mode 100644 structurizr-websequencediagrams/src/com/structurizr/io/websequencediagrams/WebSequenceDiagramsEncoder.java delete mode 100644 structurizr-websequencediagrams/src/com/structurizr/io/websequencediagrams/WebSequenceDiagramsWriter.java delete mode 100644 structurizr-websequencediagrams/test/unit/com/structurizr/io/websequencediagrams/WebSequenceDiagramsEncoderTests.java delete mode 100644 structurizr-websequencediagrams/test/unit/com/structurizr/io/websequencediagrams/WebSequenceDiagramsWriterTests.java diff --git a/README.md b/README.md index 7db324642..12fe995a7 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,7 @@ # Structurizr for Java -This GitHub repository is primarily a client library for [Structurizr](https://structurizr.com), a web-based publishing platform for software architecture models based upon the [C4 model](https://c4model.com). It can also be used to create software architecture diagrams that can be rendered with tools such as [PlantUML](docs/plantuml.md), [Graphviz](docs/graphviz-and-dot.md) and [WebSequenceDiagrams](docs/websequencediagrams.md). - -In essence, Structurizr for Java is an implementation of an __executable architecture description language__; a domain-specific language to describe software architecture, using code. The key benefit of using code to create a software architecture model is that you can use the static analysis and reflection features of Java to help you extract components from the codebase you are modelling. Integration of this tooling with your continuous integration/build process helps your software architecture diagrams stay up to date. +This GitHub repository is the official client library for the [Structurizr](https://structurizr.com) cloud service and on-premises installation, both of which are web-based publishing platforms for software architecture models based upon the [C4 model](https://c4model.com). __This repository is supported by Structurizr Limited__, as a part of the Structurizr service. ## A quick example @@ -26,7 +24,7 @@ public static void main(String[] args) throws Exception { } ``` -The view can then be exported to be visualised in a number of different ways; e.g. PlantUML, Structurizr and Graphviz: +The view can then be exported to be visualised using the [Structurizr service](https://structurizr.com), or other formats including PlantUML, WebSequenceDiagrams and Graphviz via the [Structurizr for Java extensions](https://github.com/structurizr/java-extensions). ![Views can be exported and visualised in many ways; e.g. PlantUML, Structurizr and Graphviz](docs/images/readme-1.png) @@ -62,18 +60,6 @@ The view can then be exported to be visualised in a number of different ways; e. * [Viewpoints and Perspectives](docs/documentation-viewpoints-and-perspectives.md) * [Automatic template](docs/documentation-automatic.md) * [Architecture decision records](docs/decisions.md) -* Extracting software architecture information from code - * [Component finder](docs/component-finder.md) - * [Structurizr annotations](docs/structurizr-annotations.md) - * [Type matchers](docs/type-matchers.md) - * [Spring component finder strategies](docs/spring-component-finder-strategies.md) - * [Supplementing the model from source code](docs/supplementing-from-source-code.md) - * [Components and supporting types](docs/supporting-types.md) - * [The Spring PetClinic example](docs/spring-petclinic.md) -* Exporting and visualising with other tools - * [PlantUML](docs/plantuml.md) - * [Graphviz and DOT](docs/graphviz-and-dot.md) - * [WebSequenceDiagrams](docs/websequencediagrams.md) * Other * [HTTP-based health checks](docs/health-checks.md) * [Client-side encryption](docs/client-side-encryption.md) @@ -81,6 +67,7 @@ The view can then be exported to be visualised in a number of different ways; e. * [Building from source](docs/building.md) * Related projects * [java-quickstart](https://github.com/structurizr/java-quickstart): A simple starting point for using Structurizr for Java + * [java-extensions](https://github.com/structurizr/java-extensions): A collection of Structurizr for Java extensions; including the ability to extract software architecture information from code, export views to PlantUML, etc. * [structurizr-kotlin](https://github.com/Catalysts/structurizr-extensions/tree/master/structurizr-kotlin): An extension for Structurizr that lets you create your models in a fluent way. * [structurizr-spring-boot](https://github.com/Catalysts/structurizr-extensions/tree/master/structurizr-spring-boot): A way to apply dependency management to help modularise Structurizr code. * [structurizr-groovy](https://github.com/tidyjava/structurizr-groovy): An initial version of a Groovy wrapper around Structurizr for Java. diff --git a/docs/binaries.md b/docs/binaries.md index 3b323c437..d22d25ebb 100644 --- a/docs/binaries.md +++ b/docs/binaries.md @@ -3,12 +3,5 @@ The "Structurizr for Java" binaries are hosted on [Maven Central](https://repo1. Name | Description ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-core:1.3.1 | The core library that can used to create software architecture models. -com.structurizr:structurizr-client:1.3.1 | The API client for publishing models on the Structurizr cloud service and on-premises installation. -com.structurizr:structurizr-analysis:1.3.1 | Provides analysis capabilities, using reflection on compiled bytecode to find components. -com.structurizr:structurizr-spring:1.3.1 | Extends structurizr-analysis to help find Spring components that correspond to Java types annotated ```@Controller```, ```@RestController```, ```@Component```, ```@Service``` and ```@Repository```, plus those that extend ```JpaRepository```. -com.structurizr:structurizr-annotations:1.3.1 | A very small, standalone, library that allows you to add software architecture hints into your own code. -com.structurizr:structurizr-plantuml:1.3.1 | Provides the ability to export view definitions to PlantUML diagram definitions. -com.structurizr:structurizr-dot:1.3.1 | Provides the ability to export view definitions to a DOT file, so they can be rendered with graphviz. -com.structurizr:structurizr-websequencediagrams:1.3.1 | Provides the ability to export dynamic view definitions to WebSequenceDiagrams diagram definitions. -com.structurizr:structurizr-adr-tools:1.3.1 | Imports architecture decision records (ADRs) from the adr-tools tooling. \ No newline at end of file +com.structurizr:structurizr-core:1.3.2 | The core library that can used to create software architecture models. +com.structurizr:structurizr-client:1.3.2 | The API client for publishing models on the Structurizr cloud service and on-premises installation. \ No newline at end of file diff --git a/docs/component-diagram.md b/docs/component-diagram.md index 0e4cdca7e..61d2b99c6 100644 --- a/docs/component-diagram.md +++ b/docs/component-diagram.md @@ -14,4 +14,4 @@ See [BigBankPlc.java](https://github.com/structurizr/java/blob/master/structuriz ### Extracting components automatically -Please note that, in a real-world scenario, you would probably want to extract components automatically from a codebase with the [component finder](component-finder.md), using static analysis and reflection techniques. \ No newline at end of file +Please note that, in a real-world scenario, you would probably want to extract components automatically from a codebase with the [component finder](https://github.com/structurizr/java-extensions/blob/master/docs/component-finder.md), using static analysis and reflection techniques. \ No newline at end of file diff --git a/docs/component-finder.md b/docs/component-finder.md deleted file mode 100644 index f98743a50..000000000 --- a/docs/component-finder.md +++ /dev/null @@ -1,48 +0,0 @@ -# Component finder - -The Structurizr for Java library includes a component finder and a number of prebuilt pluggable strategies that allow you to extract components from a codebase. - -## Background - -The idea behind the [C4 model](https://c4model.com) and Structurizr is that there are a number of levels of abstraction sitting above the code. Although we write Java code using interfaces and classes in packages, it's often useful to think about how that code is organised into "components". In its simplest form, a "component" is just a grouping of classes and interfaces. - -If you reverse-engineer some Java code using a UML tool, you'll typically get a UML class diagram showing all of the classes and interfaces. The component diagram in the C4 model is about hiding some of this complexity and implementation detail. You can read more about this at [Components vs classes](https://structurizr.com/help/components-vs-classes). - -## Purpose - -The purpose of the [component finder](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/analysis/ComponentFinder.java) is to find components in your codebase. Since every codebase is different (i.e. code structure, naming conventions, frameworks used, etc), different pluggable component finder strategies allow you to customize the rules that you use to find components. -Some example rules that you might use to find components include: - -- All classes where the name ends with ```Component```. -- All classes where the name ends with ```Controller```. -- All classes that are annotated with the Spring ```@Repository``` annotation. -- All classes that inherit from ```AbstractComponent```. -- etc - -## Basic usage - -To use a component finder, simply create an instance of the ```ComponentFinder``` class and configure it as needed. - -```java -Container webApplication = mySoftwareSystem.addContainer("Web Application", "Description", "Apache Tomcat 7.x"); - -ComponentFinder componentFinder = new ComponentFinder( - webApplication, "com.mycompany.mysoftwaresystem", - ... a number of component finder strategies ...); -componentFinder.findComponents(); -``` - -In this case, we're going to find components and associate them with the ```webApplication``` container, and we're only going to find components that reside somewhere underneath the ```com.mycompany.mysoftwaresystem``` package to avoid accidentally finding components residing in frameworks that we might be using. - -We also need to plug in one or more component finder strategies, which actually implement the logic to find and extract components from a codebase. - -## Component finder strategies - -The are a number of component finder strategies already implemented in this GitHub repository and, since the code is open source, you can build your own too. Some of the component finder strategies work using static analysis and reflection techniques against the compiled version of the code (you will need this on your classpath), others by parsing the source code. - -Name | Dependency | Description | Extracted from ----- | ---------- | ----------- | -------------- -[TypeMatcherComponentFinderStrategy](type-matchers.md) | structurizr-core | A component finder strategy that uses type information to find components, based upon a number of pluggable TypeMatcher implementations (e.g. [NameSuffixTypeMatcher](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/analysis/NameSuffixTypeMatcher.java), [ImplementsInterfaceTypeMatcher](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/analysis/ImplementsInterfaceTypeMatcher.java), [RegexTypeMatcher](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/analysis/RegexTypeMatcher.java) and [AnnotationTypeMatcher](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/analysis/AnnotationTypeMatcher.java)). | Compiled bytecode -[SpringComponentFinderStrategy](spring-component-finder-strategies.md) | structurizr-spring | Finds types annotated ```@Controller```, ```@RestController```, ```@Component```, ```@Service``` and ```@Repository```, plus classes that extend ```JpaRepository```. | Compiled bytecode -[StructurizrAnnotationsComponentFinderStrategy](structurizr-annotations.md) | structurizr-core | Finds the Structurizr annotations ```@Component```, ```@UsedByPerson```, ```@UsedBySoftwareSystem```, ```@UsedByContainer```, ```@UsesSoftwareSystem```, ```@UsesContainer``` and ```@UsesComponent```. | Compiled bytecode -[SourceCodeComponentFinderStrategy](supplementing-from-source-code.md) | structurizr-core | This component finder strategy doesn't really find components, it instead extracts the top-level Javadoc comment from the code so that this can be added to existing component definitions. It also calculates the size of components, based upon the number of lines of source code. | Source code diff --git a/docs/decisions.md b/docs/decisions.md index 0d6543e34..7b71f7f0c 100644 --- a/docs/decisions.md +++ b/docs/decisions.md @@ -2,6 +2,6 @@ Although architecture decisions can be included in supplementary documentation, Structurizr also provides support for publishing architecture decision records, as described by [as described by Michael Nygard](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions). -Decision records can either created manually using the API on the [Documentation class](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/documentation/Documentation.java), or using the [AdrToolsImporter](https://github.com/structurizr/java/blob/master/structurizr-adr-tools/src/com/structurizr/documentation/AdrToolsImporter.java) to import ADRs from Nat Pryce's popular [adr-tools](https://github.com/npryce/adr-tools) tooling. Here is [an example](https://github.com/structurizr/java/blob/master/structurizr-examples/src/com/structurizr/example/AdrTools.java). +Decision records can either created manually using the API on the [Documentation class](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/documentation/Documentation.java), or using the [AdrToolsImporter](https://github.com/structurizr/java-extensions/blob/master/structurizr-adr-tools/src/com/structurizr/documentation/AdrToolsImporter.java) to import ADRs from Nat Pryce's popular [adr-tools](https://github.com/npryce/adr-tools) tooling. Here is [an example](https://github.com/structurizr/java/blob/master/structurizr-examples/src/com/structurizr/example/AdrTools.java). See [Structurizr - Decision Log](https://structurizr.com/help/decision-log) for more details. \ No newline at end of file diff --git a/docs/deployment-diagram.md b/docs/deployment-diagram.md index 03b5b521a..5abda112f 100644 --- a/docs/deployment-diagram.md +++ b/docs/deployment-diagram.md @@ -2,8 +2,6 @@ A deployment diagram allows you to illustrate how containers in the static model are mapped to infrastructure. This deployment diagram is based upon a [UML deployment diagram](https://en.wikipedia.org/wiki/Deployment_diagram), although simplified slightly to show the mapping between containers and deployment nodes. A deployment node is something like physical infrastructure (e.g. a physical server or device), virtualised infrastructure (e.g. IaaS, PaaS, a virtual machine), containerised infrastructure (e.g. a Docker container), an execution environment (e.g. a database server, Java EE web/application server, Microsoft IIS), etc. Deployment nodes can be nested. -> Note: this page describes a feature that is not available to use with Structurizr's Free Plan. You can, however, render Deployment diagrams using the [PlantUMLWriter](plantuml.md). - ## Example As an example, a Deployment diagram for the live environment of a simplified, fictional Internet Banking System might look something like this. In summary, it shows the deployment of the Web Application and the Database, with a secondary Database being used for failover purposes. diff --git a/docs/dynamic-diagram.md b/docs/dynamic-diagram.md index 7056460c4..88374e8ea 100644 --- a/docs/dynamic-diagram.md +++ b/docs/dynamic-diagram.md @@ -2,8 +2,6 @@ A simple dynamic diagram can be useful when you want to show how elements in a static model collaborate at runtime to implement a user story, use case, feature, etc. This dynamic diagram is based upon a [UML communication diagram](https://en.wikipedia.org/wiki/Communication_diagram) (previously known as a "UML collaboration diagram"). It is similar to a [UML sequence diagram](https://en.wikipedia.org/wiki/Sequence_diagram) although it allows a free-form arrangement of diagram elements with numbered interactions to indicate ordering. -> Note: this page describes a feature that is not available to use with Structurizr's Free Plan. You can, however, render Dynamic diagrams using the [PlantUMLWriter](plantuml.md). - ## Example As an example, a Dynamic diagram describing the customer sign in process for a simplified, fictional Internet Banking System might look something like this. In summary, it shows the components involved in the sign in process, and the interactions between them. diff --git a/docs/graphviz-and-dot.md b/docs/graphviz-and-dot.md deleted file mode 100644 index ab28ed867..000000000 --- a/docs/graphviz-and-dot.md +++ /dev/null @@ -1,58 +0,0 @@ -# Graphviz and DOT - -Structurizr for Java also includes the ```structurizr-dot``` library, which in turn uses Cyrille Martraire's [dot-diagram library](https://github.com/LivingDocumentation/dot-diagram) to create DOT (graph description language) files that can be imported into the [Graphviz tool](http://www.graphviz.org). - -Simply create your software architecture model and views as usual, and use the [DotWriter](https://github.com/structurizr/java/blob/master/structurizr-dot/src/com/structurizr/io/dot/DotWriter.java) class to export the views. [For example](https://github.com/structurizr/java/blob/master/structurizr-dot/src/com/structurizr/io/dot/DotWriterExample.java): - -```java -Workspace workspace = new Workspace("Getting Started", "This is a model of my software system."); -Model model = workspace.getModel(); - -Person user = model.addPerson("User", "A user of my software system."); -SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "My software system."); -user.uses(softwareSystem, "Uses"); - -ViewSet views = workspace.getViews(); -SystemContextView contextView = views.createSystemContextView(softwareSystem, "SystemContext", "An example of a System Context diagram."); -contextView.addAllSoftwareSystems(); -contextView.addAllPeople(); - -StringWriter stringWriter = new StringWriter(); -DotWriter dotWriter = new DotWriter(); -dotWriter.write(workspace, stringWriter); -System.out.println(stringWriter); -``` - -> You will need Graphviz installed. - -This code will generate and output a DOT diagram definition that looks like this: - -``` -digraph G { - graph [labelloc=top,label="Software System - System Context",fontname="Verdana",fontsize=12]; - edge [fontname="Verdana",fontsize=9,labelfontname="Verdana",labelfontsize=9]; - node [fontname="Verdana",fontsize=9,shape=record]; - c0 [label="User"] - c1 [label="Software System"] - // null - c0 -> c1 [label="Uses" , ]; -} -``` - -Importing this graph definition into Graphviz (or [GraphvizFiddle](https://stamm-wilbrandt.de/GraphvizFiddle/)) gives you the following image: - -![A simple Graphviz diagram](images/graphviz-getting-started.png) - -## Benefits of using Graphviz with Structurizr - -The key benefit of using Graphviz in conjunction with the Structurizr client library is that you can create diagrams from a __model__ of your software system. The model provides a set of rules that must be followed; related to elements, relationships, and how they are exposed using diagrams. This means: - -1. Rather than looking after a collection of disjointed Graphviz diagram definitions, you can create many Graphviz diagrams from a single model and keep them all up to date easily, especially if integrated with your continuous build server and build pipeline. -1. The naming of elements and the definition of relationships between elements _remains consistent across diagrams_. -1. The software architecture model at the component level can be created by extracting components from a codebase, using _static analysis and reflection techniques_. - -### Example - -Here is a Graphviz version of the Component diagram from the [Spring PetClinic example](https://structurizr.com/share/1#components). - -![](images/graphviz-spring-petclinic-components.png) \ No newline at end of file diff --git a/docs/model.md b/docs/model.md index 49be33daf..7c5999e19 100644 --- a/docs/model.md +++ b/docs/model.md @@ -19,6 +19,6 @@ Manually adding elements to the model is the simplest way to use the Structurizr ## 2. Automatic extraction -You can also extract components (and add them to a ```Container``` instance) automatically from a given codebase, using a number of different component finder strategies. See [Component finder](component-finder.md) for more details. +You can also extract components (and add them to a ```Container``` instance) automatically from a given codebase, using a number of different component finder strategies. See [Component finder](https://github.com/structurizr/java-extensions/blob/master/docs/component-finder.md) for more details. Although there is nothing included in the Structurizr for Java library to support this, you could also choose to parse an external definition of your software architecture (e.g. an AWS infrastructure topology, another Architecture Description Language, etc) and create model elements accordingly. \ No newline at end of file diff --git a/docs/plantuml.md b/docs/plantuml.md deleted file mode 100644 index 9804d10b0..000000000 --- a/docs/plantuml.md +++ /dev/null @@ -1,98 +0,0 @@ -# PlantUML - -Structurizr for Java also includes a simple exporter that can create diagram definitions compatible with [PlantUML](http://www.plantuml.com). The following diagram types are supported: - -- System Landscape -- System Context -- Container -- Component -- Dynamic -- Deployment - -Simply create your software architecture model and views as usual, and use the [PlantUMLWriter](https://github.com/structurizr/java/blob/master/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLWriter.java) class to export the views. [For example](https://github.com/structurizr/java/blob/master/structurizr-examples/src/com/structurizr/example/PlantUML.java): - -```java -Workspace workspace = new Workspace("Getting Started", "This is a model of my software system."); -Model model = workspace.getModel(); - -Person user = model.addPerson("User", "A user of my software system."); -SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "My software system."); -user.uses(softwareSystem, "Uses"); - -ViewSet views = workspace.getViews(); -SystemContextView contextView = views.createSystemContextView(softwareSystem, "SystemContext", "An example of a System Context diagram."); -contextView.addAllSoftwareSystems(); -contextView.addAllPeople(); - -Styles styles = views.getConfiguration().getStyles(); -styles.addElementStyle(Tags.SOFTWARE_SYSTEM).background("#1168bd").color("#ffffff"); -styles.addElementStyle(Tags.PERSON).background("#08427b").color("#ffffff").shape(Shape.Person); - -StringWriter stringWriter = new StringWriter(); -PlantUMLWriter plantUMLWriter = new PlantUMLWriter(); -plantUMLWriter.addSkinParam("rectangleFontColor", "#ffffff"); -plantUMLWriter.addSkinParam("rectangleStereotypeFontColor", "#ffffff"); -plantUMLWriter.write(workspace, stringWriter); -System.out.println(stringWriter.toString()); -``` - -This code will generate and output a PlantUML diagram definition that looks like this: - -``` -@startuml -title Software System - System Context -caption An example of a System Context diagram. - -skinparam { - shadowing false - arrowColor #707070 - actorBorderColor #707070 - componentBorderColor #707070 - rectangleBorderColor #707070 - noteBackgroundColor #ffffff - noteBorderColor #707070 - rectangleFontColor #ffffff - rectangleStereotypeFontColor #ffffff -} -rectangle 2 <> #1168bd [ - Software System - -- - My software system. -] -actor "User" <> as 1 #08427b -note right of 1 - A user of my software system. -end note -1 .[#707070].> 2 : Uses -@enduml -``` - -If you copy/paste this into [PlantUML online](http://www.plantuml.com/plantuml/), you will get something like this: - -![A simple PlantUML diagram](images/plantuml-getting-started.png) - -## Benefits of using PlantUML with Structurizr - -The key benefit of using PlantUML in conjunction with the Structurizr client library is that you can create diagrams from a __model__ of your software system. The model provides a set of rules that must be followed; related to elements, relationships, and how they are exposed using diagrams. This means: - -1. Rather than looking after a collection of disjointed PlantUML diagram definitions, you can create many PlantUML diagrams from a single model and keep them all up to date easily, especially if integrated with your continuous build server and build pipeline. -1. The naming of elements and the definition of relationships between elements _remains consistent across diagrams_. -1. The software architecture model at the component level can be created by extracting components from a codebase, using _static analysis and reflection techniques_. - -### Example - -Here are the PlantUML versions of the diagrams from the [Spring PetClinic example](https://structurizr.com/share/1). - -![](images/plantuml-spring-petclinic-system-context.png) - -![](images/plantuml-spring-petclinic-containers.png) - -![](images/plantuml-spring-petclinic-components.png) - -![](images/plantuml-spring-petclinic-dynamic.png) - -![](images/plantuml-spring-petclinic-deployment-development.png) - -![](images/plantuml-spring-petclinic-deployment-staging.png) - -![](images/plantuml-spring-petclinic-deployment-live.png) diff --git a/docs/spring-component-finder-strategies.md b/docs/spring-component-finder-strategies.md deleted file mode 100644 index 14fe2a2f3..000000000 --- a/docs/spring-component-finder-strategies.md +++ /dev/null @@ -1,31 +0,0 @@ -# Spring component finder strategies - -Included in the ```structurizr-spring``` library are a number of component finder strategies that help you identify components in Spring applications, including those built using Spring Boot. - -* __SpringMvcControllerComponentFinderStrategy__: A component finder strategy that finds Spring MVC controllers (classes annotated ```@Controller```). -* __SpringRestControllerComponentFinderStrategy__: A component finder strategy that finds Spring REST controllers (classes annotated ```@RestController```). -* __SpringServiceComponentFinderStrategy__: A component finder strategy that finds Spring services (classes annotated ```@Service```). -* __SpringComponentComponentFinderStrategy__: A component finder strategy that finds Spring components (classes annotated ```@Component```). -* __SpringRepositoryComponentFinderStrategy__: A component finder strategy for Spring repositories (classes annotated ```@Repository```, plus those that extend ```JpaRepository``` or ```CrudRepository```). -* __SpringComponentFinderStrategy__: A combined component finder strategy that uses all of the individual strategies listed above. - -## Spring naming conventions and interfaces vs implementation classes - -Some of the Spring annotations (e.g. ```@Component```, ```@Service``` and ```@Repository```) are typically used to annotate _implementation classes_. For example, a ```JdbcCustomerRepository``` class might be annotated ```@Repository```, rather than the ```CustomerRepository``` interface. This component finder strategy tries to refer to interface types rather than implementation classes, based upon the following assumptions: - -1. Having a component named ```CustomerRepository``` is preferable to ```JdbcCustomerRespository```. -2. Other types in the codebase will likely have a dependency on the interface type (e.g. via dependency injection) rather than being coupled to the implementation class. - -Given that a class can implement any number of interfaces, for a given implementation class, the component finder strategies will try to find the interface where the name of that interface is included in the name of the implementation class (i.e. ```*Interface```, ```Interface*``` and ```*Interface*```). For example, the following implementation classes will match an interface named ```CustomerRepository```: - -* ```JdbcCustomerRepository``` -* ```CustomerRepositoryImpl``` -* ```JdbcCustomerRepositoryImpl``` - -## Public vs non-public types - -By default, non-public types will be ignored so that, for example, you can hide repository implementations behind services, as described at [Whoops! Where did my architecture go](http://olivergierke.de/2013/01/whoops-where-did-my-architecture-go/). Use the ```setIncludePublicTypesOnly``` method to change this behaviour. - -## Example - -You can see an example of how to use the Spring component finder strategies in the [Spring PetClinic example](spring-petclinic.md). \ No newline at end of file diff --git a/docs/spring-petclinic.md b/docs/spring-petclinic.md deleted file mode 100644 index c3b56740f..000000000 --- a/docs/spring-petclinic.md +++ /dev/null @@ -1,221 +0,0 @@ -# The Spring PetClinic example - -This is a step-by-step guide to recreating the System Context, Container and Component diagrams from the [Spring PetClinic example](https://structurizr.com/share/1), which are based upon the original version of the application. It assumes that you have working Java, Maven and git installations plus a development environment to write code. The full source code for this example can be found in [SpringPetClinic.java](https://github.com/structurizr/java/blob/master/structurizr-examples/src/com/structurizr/example/SpringPetClinic.java). - -## 1. Clone and build the Spring PetClinic code - -First of all, we need to download a copy of the [Spring PetClinic source code](https://github.com/spring-projects/spring-petclinic/). Please note that this example was created with a specific version of the Spring PetClinic codebase, so please be sure to perform the ```git checkout``` step too. - -``` -git clone https://github.com/spring-projects/spring-petclinic.git -cd spring-petclinic -git checkout 95de1d9f8bf63560915331664b27a4a75ce1f1f6 -``` - -Next we need to run the build. - -``` -mvn -``` - -The Spring PetClinic is a sample application and includes three persistence implementations (JDBC, JPA and Spring Data) that all do the same thing. As this is unrealistic for most applications, let's make things easier by removing the JPA and Spring Data implementations. - -``` -rm -r target/spring-petclinic-1.0.0-SNAPSHOT/WEB-INF/classes/org/springframework/samples/petclinic/repository/jpa/ -rm -r target/spring-petclinic-1.0.0-SNAPSHOT/WEB-INF/classes/org/springframework/samples/petclinic/repository/springdatajpa/ -``` - -## 2. Create a model - -With the Spring PetClinic application built, we now need to create a software architecture model using the [extract and supplement](https://structurizr.com/help/extract-and-supplement) approach. We will do this by creating a simple Java program to create the model. The Maven, Gradle, etc dependencies you will need are as follows: - -Name | Description ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-client:1.3.1 | The Structurizr API client library. -com.structurizr:structurizr-spring:1.3.1 | The Spring component finder. - -First we need to create a little boilerplate code to create a workspace and a model. - -```java -public static void main(String[] args) throws Exception { - Workspace workspace = new Workspace("Spring PetClinic", - "This is a C4 representation of the Spring PetClinic sample app (https://github.com/spring-projects/spring-petclinic/)"); - Model model = workspace.getModel(); -``` - -## 3. People and software systems - -A system context diagram for the Spring PetClinic system would simply consist of a single type of user (a clinic employee) using the Spring PetClinic system. With Structurizr for Java, we can represent this in code as follows. - -```java -SoftwareSystem springPetClinic = model.addSoftwareSystem("Spring PetClinic", - "Allows employees to view and manage information regarding the veterinarians, the clients, and their pets."); -Person clinicEmployee = model.addPerson("Clinic Employee", "An employee of the clinic"); - -clinicEmployee.uses(springPetClinic, "Uses"); -``` - -### 4. Containers - -Stepping down to containers, the Spring PetClinic system is made up of a Java web application that uses a database to store data. If we make some assumptions about the deployment technology stack, we can represent this in code as follows. - -```java -Container webApplication = springPetClinic.addContainer("Web Application", - "Allows employees to view and manage information regarding the veterinarians, the clients, and their pets.", - "Java and Spring"); -Container relationalDatabase = springPetClinic.addContainer("Relational Database", - "Stores information regarding the veterinarians, the clients, and their pets.", - "Relational Database Schema"); - -clinicEmployee.uses(webApplication, "Uses", "HTTPS"); -webApplication.uses(relationalDatabase, "Reads from and writes to", "JDBC"); -``` - -## 5. Components - -At the next level of abstraction, we need to open up the web application to see the components inside it. Although we couldn't really get the two previous levels of abstraction from the codebase easily, we *can* get the components. All we need to do is [understand what a "component" means in the context of this codebase](https://structurizr.com/help/components-vs-classes). We can then use this information to help us find and extract them in order to populate the software architecture model. - -Spring MVC uses Java annotations (```@Controller```, ```@Service``` and ```@Repository```) to signify classes as being web controllers, services and repositories respectively. Assuming that we consider these to be our architecturally significant code elements, it's then a simple job of extracting these annotated classes (Spring Beans) from the codebase. - -```java -ComponentFinder componentFinder = new ComponentFinder( - webApplication, "org.springframework.samples.petclinic", - new SpringComponentFinderStrategy( - new ReferencedTypesSupportingTypesStrategy(false) - ), - new SourceCodeComponentFinderStrategy(new File(sourceRoot, "/src/main/java/"), 150)); - -componentFinder.findComponents(); -``` - -The ```SpringComponentFinderStrategy``` is a pre-built component finder strategy that understands how applications are built with Spring and how to identify Spring components, such as MVC controllers, REST controllers, services, repositories, JPA repositories, etc. The way that you identify supporting types (i.e. the Java classes and interfaces) that implement a component is also pluggable. Here, with the ```ReferencedTypesSupportingTypesStrategy```, we're looking for all types directly referenced by the component type(s). See [Components and supporting types](supporting-types.md) for more details about this. - -Once the components and their supporting types have been identified, the dependencies between components are also identified and extracted. - -In addition, the ```SourceCodeComponentFinderStrategy``` will parse the top-level Javadoc comment from the source file for each component type for inclusion in the model. It will also calculate the size of each component based upon the number of lines of source code across all supporting types. - -The final thing we need to do is connect the user to the web controllers, and the repositories to the database. This is easy to do since the software architecture model is represented in code. - -```java -webApplication.getComponents().stream() - .filter(c -> c.getTechnology().equals(SpringComponentFinderStrategy.SPRING_MVC_CONTROLLER)) - .forEach(c -> clinicEmployee.uses(c, "Uses", "HTTP")); - -webApplication.getComponents().stream() - .filter(c -> c.getTechnology().equals(SpringComponentFinderStrategy.SPRING_REPOSITORY)) - .forEach(c -> c.uses(relationalDatabase, "Reads from and writes to", "JDBC")); -``` - -## 6. Create some views - -With the software architecture model in place, we now need to create some views with which to visualise the model. Again, we can do this using code. First the context diagram, which includes all people and all software systems. - -```java -ViewSet viewSet = workspace.getViews(); -SystemContextView contextView = viewSet.createSystemContextView(springPetClinic, "context", "Context view for Spring PetClinic"); -contextView.addAllSoftwareSystems(); -contextView.addAllPeople(); -``` - -Next is the container diagram. - -```java -ContainerView containerView = viewSet.createContainerView(springPetClinic, "containers", "Container view for Spring PetClinic"); -containerView.addAllPeople(); -containerView.addAllSoftwareSystems(); -containerView.addAllContainers(); -``` - -And finally is the component diagram. - -```java -ComponentView componentView = viewSet.createComponentView(webApplication, "components", "The Components diagram for the Spring PetClinic web application."); -componentView.addAllComponents(); -componentView.addAllPeople(); -componentView.add(relationalDatabase); -``` - -## 7. Linking elements to external resources - -In order to create a set of maps for the Spring PetClinic system that reflect reality, we can link the components on the component diagram to the source code. This isn't necessary, but doing so means that we can [navigate from the diagrams to the code](https://structurizr.com/help/diagram-navigation). - -```java -for (Component component : webApplication.getComponents()) { - for (CodeElement codeElement : component.getCode()) { - String sourcePath = codeElement.getUrl(); - if (sourcePath != null) { - codeElement.setUrl(sourcePath.replace( - sourceRoot.toURI().toString(), - "https://github.com/spring-projects/spring-petclinic/tree/864580702f8ef4d2cdfd7fe4497fb8c9e86018d2/")); - } - } -} -``` - -Since we don't have a component model for the database, let's instead simply link the database element to the data definition language in GitHub. - -```java -relationalDatabase.setUrl("https://github.com/spring-projects/spring-petclinic/tree/master/src/main/resources/db/hsqldb"); -``` - -## 8. Styling the diagrams - -By default, Structurizr will render all of the elements as grey boxes. However, the elements and relationships can be styled. - -```java -springPetClinic.addTags("Spring PetClinic"); -webApplication.getComponents().stream().filter(c -> c.getTechnology().equals(SpringComponentFinderStrategy.SPRING_MVC_CONTROLLER)) - .forEach(c -> c.addTags("Spring MVC Controller")); -webApplication.getComponents().stream().filter(c -> c.getTechnology().equals(SpringComponentFinderStrategy.SPRING_SERVICE)) - .forEach(c -> c.addTags("Spring Service")); -webApplication.getComponents().stream().filter(c -> c.getTechnology().equals(SpringComponentFinderStrategy.SPRING_REPOSITORY)) - .forEach(c -> c.addTags("Spring Repository")); -relationalDatabase.addTags("Database"); - -Styles styles = viewSet.getConfiguration().getStyles(); -styles.addElementStyle("Spring PetClinic").background("#6CB33E").color("#ffffff"); -styles.addElementStyle(Tags.PERSON).background("#519823").color("#ffffff").shape(Shape.Person); -styles.addElementStyle(Tags.CONTAINER).background("#91D366").color("#ffffff"); -styles.addElementStyle("Database").shape(Shape.Cylinder); -styles.addElementStyle("Spring MVC Controller").background("#D4F3C0").color("#000000"); -styles.addElementStyle("Spring Service").background("#6CB33E").color("#000000"); -styles.addElementStyle("Spring Repository").background("#95D46C").color("#000000"); -``` - -## 9. Upload the model and views to Structurizr - -The code we've just seen simply creates an in-memory representation of the software architecture model, in this case as a collection of Java objects. The open source Structurizr for Java library also includes a way to export this model to an intermediate JSON representation, which can then be imported into some tooling that is able to visualise it. This is what Structurizr does. - -```java -StructurizrClient structurizrClient = new StructurizrClient("key", "secret"); -structurizrClient.putWorkspace(1234, workspace); -``` - -In order to upload your model to Structurizr using the web API, you'll need to [sign up](https://structurizr.com/signup) to get your own API key and secret. -Also, when you run the Structurizr program you just created, you'll need to ensure that the compiled version of the Spring PetClinic application is on your classpath; specifically these directories: - -``` -target/spring-petclinic-1.0.0-SNAPSHOT/WEB-INF/classes -target/spring-petclinic-1.0.0-SNAPSHOT/WEB-INF/lib -``` - -## 10. View the diagrams and layout the elements - -If you sign in to Structurizr and open the workspace you just uploaded, you'll see something like this. - -![The Spring PetClinic workspace](images/spring-petclinic-1.png) - -Structurizr doesn't do any automatic layout of the elements on your diagrams, so you will need to drag the boxes around to create a layout that you like. Here are the links to the live example diagrams: - -- [System Context diagram](https://structurizr.com/share/1#context) -- [Container diagram](https://structurizr.com/share/1#containers) -- [Component diagram](https://structurizr.com/share/1#components) - -## 11. Explore the model -Once you have a model of your software system, you can explore it using a number of different visualisations. For example: - -- [Static structure, rendered as a tree](https://structurizr.com/share/1/explore/tree) -- [Static structure, rendered as circles based upon size](https://structurizr.com/share/1/explore/size-circles) -- [Component dependencies](https://structurizr.com/share/1/explore/component-dependencies) -- [Component and code dependencies](https://structurizr.com/share/1/explore/component-and-code-dependencies) - diff --git a/docs/structurizr-annotations.md b/docs/structurizr-annotations.md deleted file mode 100644 index d5d74d31d..000000000 --- a/docs/structurizr-annotations.md +++ /dev/null @@ -1,106 +0,0 @@ -# Structurizr annotations - -Structurizr for Java includes some custom annotations that you can add to your code. These serve to either make it explicit how components should be extracted from your codebase, or they help supplement the software architecture model. - -The annotations can be found in the [structurizr-annotations](https://bintray.com/structurizr/maven/structurizr-java) artifact, which is a very small standalone JAR file containing only the Structurizr annotations. All annotations have a runtime retention policy, so they will be present in the compiled bytecode. - -## @Component - -A type-level annotation that can be used to signify that the annotated type (an interface or class) can be considered to be a "component". The properties are as follows: - -- description: The description of the component (optional). -- technology: The technology of component (optional). - -## @UsedByPerson - -A type-level annotation that can be used to signify that the named person uses the component on which this annotation is placed, creating a relationship form the person to the component. The properties are as follows: - -- name: The name of the person (required). -- description: The description of the relationship (optional). -- technology: The technology of relationship (optional). - -## @UsedBySoftwareSystem - -A type-level annotation that can be used to signify that the named software system uses the component on which this annotation is placed, creating a relationship from the software system to the component. The properties are as follows: - -- name: The name of the software system (required). -- description: The description of the relationship (optional). -- technology: The technology of relationship (optional). - -## @UsedByContainer - -A type-level annotation that can be used to signify that the named container uses the component on which this annotation is placed, creating a relationship from the container to the component. The properties are as follows: - -- name: The name of the container (required). -- description: The description of the relationship (optional). -- technology: The technology of relationship (optional). - -If the container resides in the same software system as the component, the simple name can be used to identify the container (e.g. "Database"). Otherwise, the full canonical name of the form "Software System/Container" must be used (e.g. "Some Other Software System/Database"). - -## @UsesSoftwareSystem - -A type-level annotation that can be used to signify that the component on which this annotation is placed has a relationship to the named software system, creating a relationship from the component to the software system. The properties are as follows: - -- name: The name of the software system (required). -- description: The description of the relationship (optional). -- technology: The technology of relationship (optional). - -## @UsesContainer - -A type-level annotation that can be used to signify that the component on which this annotation is placed has a relationship to the named container, creating a relationship from the component to the container. The properties are as follows: - -- name: The name of the container (required). -- description: The description of the relationship (optional). -- technology: The technology of relationship (optional). - -If the container resides in the same software system as the component, the simple name can be used to identify the container (e.g. "Database"). Otherwise, the full canonical name of the form "Software System/Container" must be used (e.g. "Some Other Software System/Database"). - -## @UsesComponent - -A field-level annotation that can be used to supplement the existing relationship (i.e. add a description and/or technology) between two components. - -When using the various component finder strategies, Structurizr for Java will identify components along with the relationships between those components. Since this is typically done using reflection against the compiled bytecode, you'll notice that the description and technology properties of the resulting relationships is always empty. The ```@UsesComponent``` annotation provides a simple way to ensure that such information is added into the model. - -The properties are as follows: - -- description: The description of the relationship (required). -- technology: The technology of relationship (optional). - -## Example - -Here are some examples of the annotations, which have been used to create the following diagram. - -![](images/structurizr-annotations-1.png) - -```java -@Component(description = "Serves HTML pages to users.", technology = "Java") -@UsedByPerson(name = "User", description = "Uses", technology = "HTTPS") -class HtmlController { - - @UsesComponent(description = "Gets data using") - private Repository repository = new JdbcRepository(); - -} -``` - -```java -@Component(description = "Provides access to data stored in the database.", technology = "Java and JPA") -public interface Repository { - - String getData(long id); - -} -``` - -```java -@UsesContainer(name = "Database", description = "Reads from", technology = "JDBC") -class JdbcRepository implements Repository { - - public String getData(long id) { - return "..."; - } - -} -``` - -See [StructurizrAnnotations.java](https://github.com/structurizr/java/blob/master/structurizr-examples/src/com/structurizr/example/StructurizrAnnotations.java) for the full source code illustrating how to use the various annotations in conjunction with the component finder. The resulting diagrams can be found at [https://structurizr.com/share/36571](https://structurizr.com/share/36571). \ No newline at end of file diff --git a/docs/supplementing-from-source-code.md b/docs/supplementing-from-source-code.md deleted file mode 100644 index eb27fd451..000000000 --- a/docs/supplementing-from-source-code.md +++ /dev/null @@ -1,32 +0,0 @@ -# Supplementing the model from source code - -Most of the component finder strategies included in Structurizr for Java find components using reflection against the compiled bytecode. Some useful information exists in the source code though; including: - -* The type-level doc comment (```/** ... */```, typically extracted using the javadoc tool). This can be used to populate the description property of components. -* The number of lines of source code. This can be a used to calculate the "size" of a component. - -A pre-built [SourceCodeComponentFinderStrategy](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/analysis/SourceCodeComponentFinderStrategy.java) is provided to do this, which uses the standard ```javadoc``` tool. You will need to include ```JAVA_HOME/lib/tools.jar``` on your classpath. - -## Example - -Here's an example of how to use the ```SourceCodeComponentFinderStrategy```, taken from the [Spring PetClinic example](spring-petclinic.md). - -```java -File sourceRoot = new File("/some/path"); - -ComponentFinder componentFinder = new ComponentFinder( - webApplication, "org.springframework.samples.petclinic", - new SpringComponentFinderStrategy( - new ReferencedTypesSupportingTypesStrategy(false) - ), - new SourceCodeComponentFinderStrategy(new File(sourceRoot, "/src/main/java/"), 150)); - -componentFinder.findComponents(); -``` - -For every ```CodeElement``` that belongs to every ```Component``` in the ```Container``` passed to the ```ComponentFinder```, the ```SourceCodeComponentFinderStrategy``` will: - -* Set the description property, based on the type-level doc comment. The doc comment will be truncated if necessary (e.g. to 150 characters in the example above). -* Set the size property to be the number of lines of the file that the type was found in. - -Additionally, the description property of the ```Component``` will be set to be that of the primary ```CodeElement```, if a description has not been set on the ```Component``` already. \ No newline at end of file diff --git a/docs/supporting-types.md b/docs/supporting-types.md deleted file mode 100644 index ad1f1dd44..000000000 --- a/docs/supporting-types.md +++ /dev/null @@ -1,104 +0,0 @@ -# Components and supporting types - -In Structurizr, a [Component](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/model/Component.java) is described by a number of properties and can have a number of [CodeElement](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/model/CodeElement.java)s associated with it that represent the Java classes and interfaces that implement and/or support that component. - -Because each codebase is different, the mechanism to find a component's supporting types is pluggable via a number of strategies ([SupportingTypesStrategy](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/analysis/SupportingTypesStrategy.java)), which can be used in combination. Let's look at the [Spring PetClinic example](spring-petclinic.md) to see how the various supporting types strategies affect the software architecture model. - -## 1. No supporting types strategy - -This simple example uses the SpringComponentFinderStrategy with no supporting types strategy. - -```java -ComponentFinder componentFinder = new ComponentFinder( - webApplication, "org.springframework.samples.petclinic", - new SpringComponentFinderStrategy(), - new SourceCodeComponentFinderStrategy(new File(sourceRoot, "/src/main/java/"), 150)); - -componentFinder.findComponents(); -``` - -When executed against the compiled version of the Spring PetClinic codebase, each component identified will be made up of only the types found by the component finder strategy. For example, the ```VisitRepository``` component will be made up of an interface (```VisitRepository```) and the implementation class (```JdbcVisitRepositoryImpl```). You can visualise this using [Structurizr's tree exploration](https://structurizr.com/help/explorations). - -![](images/supporting-types-1.png) - -## 2. Referenced types in the same package - -Next, let's use the [ReferencedTypesInSamePackageSupportingTypesStrategy](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/analysis/ReferencedTypesInSamePackageSupportingTypesStrategy.java) to find all supporting types in the same package as the component type. This is particularly useful when each component neatly resides in its own Java package, and you aren't interested in any other code outside of this package. - -```java -ComponentFinder componentFinder = new ComponentFinder( - webApplication, "org.springframework.samples.petclinic", - new SpringComponentFinderStrategy( - new ReferencedTypesInSamePackageSupportingTypesStrategy() - ), - new SourceCodeComponentFinderStrategy(new File(sourceRoot, "/src/main/java/"), 150)); - -componentFinder.findComponents(); -``` - -This strategy finds all of the supporting types that are referenced by the types found by the component finder strategy. In real terms, we've now additionally picked up the ```JdbcVisitRowMapper``` class, since this is used by the ```JdbcVisitRepositoryImpl``` class. - -![](images/supporting-types-2.png) - -## 3. All referenced types - -Next, let's use the [ReferencedTypesSupportingTypesStrategy](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/analysis/ReferencedTypesSupportingTypesStrategy.java) to find all of the referenced types, irrespective of which package they reside in, provided that package sits somewhere underneath ```org.springframework.samples.petclinic```. - -```java -ComponentFinder componentFinder = new ComponentFinder( - webApplication, "org.springframework.samples.petclinic", - new SpringComponentFinderStrategy( - new ReferencedTypesSupportingTypesStrategy() - ), - new SourceCodeComponentFinderStrategy(new File(sourceRoot, "/src/main/java/"), 150)); - -componentFinder.findComponents(); -``` - -As the following image illustrates, we now have many more classes that are supporting the implementation of the ```VisitRepository``` component. - -![](images/supporting-types-3.png) - -This collection of classes may look confusing at first, but the ```JdbcVisitRepositoryImpl``` class references the ```Visit``` class, which in turn references the ```Pet``` class, which in turn references the ```Owner``` class, etc. The Structurizr tree exploration shows that these classes are shared between the ```VisitRepository``` and other components by rendering their names in grey. - -## 4. Directly referenced types only - -Of course, the ```JdbcVisitRepositoryImpl``` class may not actually use all of these classes, but they are certainly available to it. This is one of the drawbacks of using static analysis based upon compiled bytecode. You can modify this behaviour and find only those types directly referenced by the component by passing ```false``` to the constructor of the [ReferencedTypesSupportingTypesStrategy](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/analysis/ReferencedTypesSupportingTypesStrategy.java). - -![](images/supporting-types-4.png) - -## Excluding types - -The Structurizr ```ComponentFinder``` will also allow you to exclude types from the component scanning process using one or more regular expressions. If we wanted to exclude the ```BaseEntity``` and ```NamedEntity``` classes in one of the previous examples, we could add the following line of code before calling ```componentFinder.findComponents()```. - -``` -componentFinder.exclude("org\\.springframework\\.samples\\.petclinic\\.model\\..*Entity"); -``` - -The result is as follows. - -![](images/supporting-types-5.png) - -## Type erasure - -The implementation of the various supporting types strategies above use reflection based upon Java bytecode. For this reason, the results are subject to the rules around Java's [type erasure](https://docs.oracle.com/javase/tutorial/java/generics/erasure.html). For example, if you have a component that references ```Collection``` in method signatures, you may find ```Customer``` missing from the list of supporting types. - -## Visualising shared code - -The easiest way to analyse the component-code relationships is to visualise them. Structurizr has a number of built-in explorations that can help with this. - -### Component size - -[Structurizr's component size exploration](https://structurizr.com/share/1/explore/size-circles) renders components and code elements as a collection of nested circles, where the size of each circle is based upon the number of lines of code. Shared code elements are rendered using a different style, and hovering the mouse over a shared code element will highlight all other occurrences. This allows you to easily see where code elements (interfaces and classes) are shared between components. - -![](images/supporting-types-6.gif) - -### Component and code dependencies - -The [component and code dependency exploration](https://structurizr.com/share/1/explore/component-and-code-dependencies) renders a force-directed graph of the components and code elements, along with all of the relationships between them. Shared code elements are rendered in grey. - -![](images/supporting-types-7.png) - -Clicking any node will allow you to easily see which other nodes are directly connected to it. - -![](images/supporting-types-8.png) diff --git a/docs/type-matchers.md b/docs/type-matchers.md deleted file mode 100644 index 5082cc3d1..000000000 --- a/docs/type-matchers.md +++ /dev/null @@ -1,65 +0,0 @@ -# Type matchers - -Structurizr for Java includes a number "type matchers", which when used in conjunction with the [TypeMatcherComponentFinderStrategy](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/analysis/TypeMatcherComponentFinderStrategy.java), provides a simple way to find components in your codebase based upon specific types. The following pre-built type matchers are included. - -## NameSuffixTypeMatcher - -Matches types where the (simple) name of the type ends with the specified suffix. For example, to find all types named ```*Controller```: - -```java -new NameSuffixTypeMatcher("Controller", "", ""); -``` - -## RegexTypeMatcher - -Matches types using a regex against the fully qualified type name. For example, to find all types with a fully qualified name of ```*.web.*Controller```: - -```java -new RegexTypeMatcher(".*\.web\..*Controller", "", ""); -``` - -## AnnotationTypeMatcher - -Matches types based upon the presence of a type-level annotation. For example, to find all types that are annotated with the Java EE ```@Stateless``` annotation: - -```java -new AnnotationTypeMatcher(javax.ejb.Stateless.class, "", ""); -``` - -## ImplementsInterfaceTypeMatcher - -Matches types where the type implements the specified interface. For example, to find all types that implement the ```Repository``` interface: - -```java -new ImplementsInterfaceTypeMatcher(Repository.class, "", ""); -``` - -## ExtendsClassTypeMatcher - -Matches types where the type extends the specified class. For example, to find all types that extend the ```AbstractComponent``` class: - -```java -new ExtendsClassTypeMatcher(AbstractComponent.class, "", ""); -``` - -## Example - -Here is an example of how you might use the type matchers in conjunction with the component finder: - -```java -ComponentFinder componentFinder = new ComponentFinder( - someContainer, - "com.mycompany.myapp", - new TypeMatcherComponentFinderStrategy( - new NameSuffixTypeMatcher("Controller", "Controller description", "Controller technology"), - new NameSuffixTypeMatcher("Repository", "Repository description", "Repository technology") - ) -); -componentFinder.findComponents(); -``` - -The description and technology properties specified on the type matchers will be used to set the corresponding properties on the ```Component``` instances that are created when matching types are found. - -## Writing your own type matchers - -You can write your own type matchers by implementing the [TypeMatcher](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/analysis/TypeMatcher.java) interface, or by extending the [AbstractTypeMatcher](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/analysis/AbstractTypeMatcher.java) class. \ No newline at end of file diff --git a/docs/websequencediagrams.md b/docs/websequencediagrams.md deleted file mode 100644 index 96468c5b4..000000000 --- a/docs/websequencediagrams.md +++ /dev/null @@ -1,18 +0,0 @@ -# WebSequenceDiagrams - -Structurizr for Java also includes a simple exporter that can create [dynamic diagram](dynamic-diagram.md) definitions compatible with [WebSequenceDiagrams](https://www.websequencediagrams.com). - -Simply create your software architecture model and views as usual, and use the [WebSequenceDiagramsWriter](https://github.com/structurizr/java/blob/master/structurizr-websequencediagrams/src/com/structurizr/io/websequencediagrams/WebSequenceDiagramsWriter.java) class to export the dynamic views and generate WebSequenceDiagrams diagram definitions. A WebSequenceDiagram version of the [Big Bank plc dynamic diagram](dynamic-diagram.md) looks something like this: - -``` -title Web Application - Dynamic - SignIn - -Customer->Sign In Controller: Requests /signin from -Customer->Sign In Controller: Submits credentials to -Sign In Controller->Security Component: Calls isAuthenticated() on -Security Component->Database: select * from users where username = ? -``` - -If you copy/paste this into [WebSequenceDiagrams](https://www.websequencediagrams.com), you will get something like this: - -![A simple WebSequenceDiagrams diagram](images/websequencediagrams-1.png) \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 268d5b6fd..b719b46af 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,13 +1,5 @@ rootProject.name = 'structurizr' -include 'structurizr-adr-tools' -include 'structurizr-analysis' -include 'structurizr-annotations' include 'structurizr-client' include 'structurizr-core' -include 'structurizr-mermaid' -include 'structurizr-plantuml' -include 'structurizr-dot' -include 'structurizr-examples' -include 'structurizr-spring' -include 'structurizr-websequencediagrams' +include 'structurizr-examples' \ No newline at end of file diff --git a/structurizr-adr-tools/build.gradle b/structurizr-adr-tools/build.gradle deleted file mode 100644 index 542a4b1ef..000000000 --- a/structurizr-adr-tools/build.gradle +++ /dev/null @@ -1,20 +0,0 @@ -dependencies { - - compile project(':structurizr-core') - - testCompile 'junit:junit:4.12' - -} - -sourceSets { - main { - java { - srcDir 'src' - } - } - test { - java { - srcDir 'test/unit' - } - } -} \ No newline at end of file diff --git a/structurizr-adr-tools/src/com/structurizr/documentation/AdrToolsImporter.java b/structurizr-adr-tools/src/com/structurizr/documentation/AdrToolsImporter.java deleted file mode 100644 index af681576e..000000000 --- a/structurizr-adr-tools/src/com/structurizr/documentation/AdrToolsImporter.java +++ /dev/null @@ -1,158 +0,0 @@ -package com.structurizr.documentation; - -import com.structurizr.Workspace; -import com.structurizr.model.SoftwareSystem; - -import java.io.File; -import java.io.FilenameFilter; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.text.SimpleDateFormat; -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Imports architecture decision records created/managed by adr-tools (https://github.com/npryce/adr-tools). - */ -public class AdrToolsImporter { - - private static final Pattern titleRegex = Pattern.compile("^# \\d*\\. (.*)$", Pattern.MULTILINE); - private static final Pattern dateRegex = Pattern.compile("^Date: (\\d\\d\\d\\d-\\d\\d-\\d\\d)$",Pattern.MULTILINE); - private static final Pattern statusRegex = Pattern.compile("## Status\\n\\n(\\w*)"); - - private static final String SUPERCEDED_ALTERNATIVE_SPELLING = "Superceded"; - - private Workspace workspace; - private File path; - private TimeZone timeZone = TimeZone.getTimeZone("UTC"); - - public AdrToolsImporter(Workspace workspace, File path) { - if (workspace == null) { - throw new IllegalArgumentException("A workspace must be specified."); - } - - if (path == null) { - throw new IllegalArgumentException("The path to the architecture decision records must be specified."); - } - - if (!path.exists()) { - throw new IllegalArgumentException(path.getAbsolutePath() + " does not exist."); - } - - if (!path.isDirectory()) { - throw new IllegalArgumentException(path.getAbsolutePath() + " is not a directory."); - } - - this.workspace = workspace; - this.path = path; - } - - public void setTimeZone(String timeZone) { - this.timeZone = TimeZone.getTimeZone(timeZone); - } - - public void setTimeZone(TimeZone timeZone) { - this.timeZone = timeZone; - } - - public Set importArchitectureDecisionRecords() throws Exception { - return importArchitectureDecisionRecords(null); - } - - public Set importArchitectureDecisionRecords(SoftwareSystem softwareSystem) throws Exception { - Set decisions = new HashSet<>(); - - File[] markdownFiles = path.listFiles(new FilenameFilter() { - @Override - public boolean accept(File dir, String name) { - return name.endsWith(".md"); - } - }); - - if (markdownFiles != null) { - // first create an index of filename -> ID - Map index = new HashMap<>(); - for (File file : markdownFiles) { - index.put(file.getName(), extractIntegerIdFromFileName(file)); - } - - for (File file : markdownFiles) { - String id = extractIntegerIdFromFileName(file); - Date date = new Date(); - String title = ""; - DecisionStatus status = DecisionStatus.Proposed; - String content = new String(Files.readAllBytes(file.toPath()), "UTF-8"); - content = content.replace("\r", ""); - Format format = Format.Markdown; - - title = extractTitle(content); - date = extractDate(content); - status = extractStatus(content); - - for (String filename : index.keySet()) { - content = content.replace(filename, calculateUrl(softwareSystem, index.get(filename))); - } - - Decision decision = workspace.getDocumentation().addDecision(softwareSystem, id, date, title, status, format, content); - decisions.add(decision); - } - } - - return decisions; - } - - private String calculateUrl(SoftwareSystem softwareSystem, String id) throws Exception { - if (softwareSystem == null) { - return "#/:" + urlEncode(id); - } else { - return "#" + urlEncode(softwareSystem.getCanonicalName()) + ":" + urlEncode(id); - } - } - - private String urlEncode(String value) throws Exception { - return URLEncoder.encode(value, StandardCharsets.UTF_8.toString()).replaceAll("\\+", "%20"); - } - - private String extractIntegerIdFromFileName(File file) { - return "" + Integer.parseInt(file.getName().substring(0, 4)); - } - - private String extractTitle(String content) { - Matcher matcher = titleRegex.matcher(content); - if (matcher.find()) { - return matcher.group(1); - } else { - return "Untitled"; - } - } - - private Date extractDate(String content) throws Exception { - Matcher matcher = dateRegex.matcher(content); - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); - sdf.setTimeZone(timeZone); - - if (matcher.find()) { - return sdf.parse(matcher.group(1)); - } else { - return new Date(); - } - } - - private DecisionStatus extractStatus(String content) { - Matcher matcher = statusRegex.matcher(content); - if (matcher.find()) { - String status = matcher.group(1); - - if (status.equals(SUPERCEDED_ALTERNATIVE_SPELLING)) { - return DecisionStatus.Superseded; - } else { - return DecisionStatus.valueOf(status); - } - } else { - return DecisionStatus.Proposed; - } - } - -} \ No newline at end of file diff --git a/structurizr-adr-tools/test/unit/adrs/0001-record-architecture-decisions.md b/structurizr-adr-tools/test/unit/adrs/0001-record-architecture-decisions.md deleted file mode 100644 index f30860000..000000000 --- a/structurizr-adr-tools/test/unit/adrs/0001-record-architecture-decisions.md +++ /dev/null @@ -1,19 +0,0 @@ -# 1. Record architecture decisions - -Date: 2016-02-12 - -## Status - -Accepted - -## Context - -We need to record the architectural decisions made on this project. - -## Decision - -We will use Architecture Decision Records, as described by Michael Nygard in this article: http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions - -## Consequences - -See Michael Nygard's article, linked above. diff --git a/structurizr-adr-tools/test/unit/adrs/0002-implement-as-shell-scripts.md b/structurizr-adr-tools/test/unit/adrs/0002-implement-as-shell-scripts.md deleted file mode 100644 index 8e6ea15e6..000000000 --- a/structurizr-adr-tools/test/unit/adrs/0002-implement-as-shell-scripts.md +++ /dev/null @@ -1,28 +0,0 @@ -# 2. Implement as shell scripts - -Date: 2016-02-12 - -## Status - -Accepted - -## Context - -ADRs are plain text files stored in a subdirectory of the project. - -The tool needs to create new files and apply small edits to -the Status section of existing files. - -## Decision - -The tool is implemented as shell scripts that use standard Unix -tools -- grep, sed, awk, etc. - -## Consequences - -The tool won't support Windows. Being plain text files, ADRs can -be created by hand and edited in any text editor. This tool just -makes the process more convenient. - -Development will have to cope with differences between Unix -variants, particularly Linux and MacOS X. diff --git a/structurizr-adr-tools/test/unit/adrs/0003-single-command-with-subcommands.md b/structurizr-adr-tools/test/unit/adrs/0003-single-command-with-subcommands.md deleted file mode 100644 index f64db8da1..000000000 --- a/structurizr-adr-tools/test/unit/adrs/0003-single-command-with-subcommands.md +++ /dev/null @@ -1,45 +0,0 @@ -# 3. Single command with subcommands - -Date: 2016-02-12 - -## Status - -Accepted - -## Context - -The tool provides a number of related commands to create -and manipulate architecture decision records. - -How can the user find out about the commands that are available? - -## Decision - -The tool defines a single command, called `adr`. - -The first argument to `adr` (the subcommand) specifies the -action to perform. Further arguments are interpreted by the -subcommand. - -Running `adr` without any arguments lists the available -subcommands. - -Subcommands are implemented as scripts in the same -directory as the `adr` script. E.g. the subcommand `new` is -implemented as the script `adr-new`, the subcommand `help` -as the script `adr-help` and so on. - -Helper scripts that are part of the implementation but not -subcommands follow a different naming convention, so that -subcommands can be listed by filtering and transforming script -file names. - -## Consequences - -Users can more easily explore the capabilities of the tool. - -Users are already used to this style of command-line tool. For -example, Git works this way. - -Each subcommand can be implemented in the most appropriate -language. diff --git a/structurizr-adr-tools/test/unit/adrs/0004-markdown-format.md b/structurizr-adr-tools/test/unit/adrs/0004-markdown-format.md deleted file mode 100644 index 86a21bf7e..000000000 --- a/structurizr-adr-tools/test/unit/adrs/0004-markdown-format.md +++ /dev/null @@ -1,40 +0,0 @@ -# 4. Markdown format - -Date: 2016-02-12 - -## Status - -Superceded by [10. AsciiDoc format](0010-asciidoc-format.md) - -## Context - -The decision records must be stored in a plain text format: - -* This works well with version control systems. - -* It allows the tool to modify the status of records and insert - hyperlinks when one decision supercedes another. - -* Decisions can be read in the terminal, IDE, version control - browser, etc. - -People will want to use some formatting: lists, code examples, -and so on. - -People will want to view the decision records in a more readable -format than plain text, and maybe print them out. - - -## Decision - -Record architecture decisions in [Markdown format](https://daringfireball.net/projects/markdown/). - -## Consequences - -Decisions can be read in the terminal. - -Decisions will be formatted nicely and hyperlinked by the -browsers of project hosting sites like GitHub and Bitbucket. - -Tools like [Pandoc](http://pandoc.org/) can be used to convert -the decision records into HTML or PDF. diff --git a/structurizr-adr-tools/test/unit/adrs/0005-help-comments.md b/structurizr-adr-tools/test/unit/adrs/0005-help-comments.md deleted file mode 100644 index b19bf0fb1..000000000 --- a/structurizr-adr-tools/test/unit/adrs/0005-help-comments.md +++ /dev/null @@ -1,42 +0,0 @@ -# 5. Help comments - -Date: 2016-02-13 - -## Status - -Accepted - -Amended by [9. Help scripts](0009-help-scripts.md) - -## Context - -The tool will have a `help` subcommand to provide documentation -for users. - -It's nice to have usage documentation in the script files -themselves, in comments. When reading the code, that's the first -place to look for information about how to run a script. - -## Decision - -Write usage documentation in comments in the source file. - -Distinguish between documentation comments and normal comments. -Documentation comments have two hash characters at the start of -the line. - -The `adr help` command can parse comments out from the script -using the standard Unix tools `grep` and `cut`. - -## Consequences - -No need to maintain help text in a separate file. - -Help text can easily be kept up to date as the script is edited. - -There's no automated check that the help text is up to date. The -tests do not work well as documentation for users, and the help -text is not easily cross-checked against the code. - -This won't work if any subcommands are not implemented as scripts -that use '#' as a comment character. diff --git a/structurizr-adr-tools/test/unit/adrs/0006-packaging-and-distribution-in-other-version-control-repositories.md b/structurizr-adr-tools/test/unit/adrs/0006-packaging-and-distribution-in-other-version-control-repositories.md deleted file mode 100644 index 4a3485f79..000000000 --- a/structurizr-adr-tools/test/unit/adrs/0006-packaging-and-distribution-in-other-version-control-repositories.md +++ /dev/null @@ -1,41 +0,0 @@ -# 6. Packaging and distribution in other version control repositories - -Date: 2016-02-16 - -## Status - -Accepted - -## Context - -Users want to install adr-tools with their preferred package -manager. For example, Ubuntu users use `apt`, RedHat users use -`yum` and Mac OS X users use [Homebrew](http://brew.sh). - -The developers of `adr-tools` don't know how, nor have permissions, -to use all these packaging and distribution systems. Therefore packaging -and distribution must be done by "downstream" parties. - -The developers of the tool should not favour any one particular -packaging and distribution solution. - -## Decision - -The `adr-tools` project will not contain any packaging or -distribution scripts and config. - -Packaging and distribution will be managed by other projects in -separate version control repositories. - -## Consequences - -The git repo of this project will be simpler. - -Eventually, users will not have to use Git to get the software. - -We will have to tag releases in the `adr-tools` repository so that -packaging projects know what can be published and how it should be -identified. - -We will document how users can install the software in this -project's README file. diff --git a/structurizr-adr-tools/test/unit/adrs/0007-invoke-adr-config-executable-to-get-configuration.md b/structurizr-adr-tools/test/unit/adrs/0007-invoke-adr-config-executable-to-get-configuration.md deleted file mode 100644 index a649b2356..000000000 --- a/structurizr-adr-tools/test/unit/adrs/0007-invoke-adr-config-executable-to-get-configuration.md +++ /dev/null @@ -1,31 +0,0 @@ -# 7. Invoke adr-config executable to get configuration - -Date: 2016-12-17 - -## Status - -Accepted - -## Context - -Packagers (e.g. Homebrew developers) want to configure adr-tools to match the conventions of their installation. - -Currently, this is done by sourcing a file `config.sh`, which should sit beside the `adr` executable. - -This name is too common. - -The `config.sh` file is not executable, and so doesn't belong in a bin directory. - -## Decision - -Replace `config.sh` with an executable, named `adr-config` that outputs configuration. - -Each script in ADR Tools will eval the output of `adr-config` to configure itself. - -## Consequences - -Configuration within ADR Tools is a little more complicated. - -Packagers can write their own implementation of `adr-config` that outputs configuration that matches the platform's installation conventions, and deploy it next to the `adr` script. - -To make development easier, the implementation of `adr-config` in the project's src/ directory will output configuration that lets the tool to run from the src/ directory without any installation step. (Packagers should not include this script in a deployable package.) diff --git a/structurizr-adr-tools/test/unit/adrs/0008-use-iso-8601-format-for-dates.md b/structurizr-adr-tools/test/unit/adrs/0008-use-iso-8601-format-for-dates.md deleted file mode 100644 index 4146f11df..000000000 --- a/structurizr-adr-tools/test/unit/adrs/0008-use-iso-8601-format-for-dates.md +++ /dev/null @@ -1,43 +0,0 @@ -# 8. Use ISO 8601 Format for Dates - -Date: 2017-02-21 - -## Status - -Accepted - -## Context - -`adr-tools` seeks to communicate the history of architectural decisions of a -project. An important component of the history is the time at which a decision -was made. - -To communicate effectively, `adr-tools` should present information as -unambiguously as possible. That means that culture-neutral data formats should -be preferred over culture-specific formats. - -Existing `adr-tools` deployments format dates as `dd/mm/yyyy` by default. That -formatting is common formatting in the United Kingdom (where the `adr-tools` -project was originally written), but is easily confused with the `mm/dd/yyyy` -format preferred in the United States. - -The default date format may be overridden by setting `ADR_DATE` in `config.sh`. - -## Decision - -`adr-tools` will use the ISO 8601 format for dates: `yyyy-mm-dd` - -## Consequences - -Dates are displayed in a standard, culture-neutral format. - -The UK-style and ISO 8601 formats can be distinguished by their separator -character. The UK-style dates used a slash (`/`), while the ISO dates use a -hyphen (`-`). - -Prior to this decision, `adr-tools` was deployed using the UK format for dates. -After adopting the ISO 8601 format, existing deployments of `adr-tools` must do -one of the following: - - * Accept mixed formatting of dates within their documentation library. - * Update existing documents to use ISO 8601 dates by running `adr upgrade-repository` diff --git a/structurizr-adr-tools/test/unit/adrs/0009-help-scripts.md b/structurizr-adr-tools/test/unit/adrs/0009-help-scripts.md deleted file mode 100644 index 0146d127c..000000000 --- a/structurizr-adr-tools/test/unit/adrs/0009-help-scripts.md +++ /dev/null @@ -1,28 +0,0 @@ -# 9. Help scripts - -Date: 2018-06-26 - -## Status - -Accepted - -Amends [5. Help comments](0005-help-comments.md) - -## Context - -Currently help text is generated by extracting specially formatted comments from the top of the command script. - -This makes it easy for developers of the tool: documentation and code is all in one place. - -But, it means that help text cannot include calculated values, such as the location of files. - -## Decision - -Where necessary, help text can be generated by a script. - -The script will be called _adr_help__ - -## Consequences - -Help scripts can include helper scripts to locate files, giving more accurate instructions to the user that reflect how the tool is deployed in their environment. - diff --git a/structurizr-adr-tools/test/unit/adrs/0010-asciidoc-format.md b/structurizr-adr-tools/test/unit/adrs/0010-asciidoc-format.md deleted file mode 100644 index edb613d1a..000000000 --- a/structurizr-adr-tools/test/unit/adrs/0010-asciidoc-format.md +++ /dev/null @@ -1,21 +0,0 @@ -# 10. AsciiDoc format - -Date: 2018-08-11 - -## Status - -Accepted - -Supercedes [4. Markdown format](0004-markdown-format.md) - -## Context - -The issue motivating this decision, and any context that influences or constrains the decision. - -## Decision - -The change that we're proposing or have agreed to implement. - -## Consequences - -What becomes easier or more difficult to do and any risks introduced by the change that will need to be mitigated. diff --git a/structurizr-adr-tools/test/unit/com/structurizr/documentation/AdrToolsImporterTests.java b/structurizr-adr-tools/test/unit/com/structurizr/documentation/AdrToolsImporterTests.java deleted file mode 100644 index 6d03fcb17..000000000 --- a/structurizr-adr-tools/test/unit/com/structurizr/documentation/AdrToolsImporterTests.java +++ /dev/null @@ -1,133 +0,0 @@ -package com.structurizr.documentation; - -import com.structurizr.Workspace; -import com.structurizr.model.SoftwareSystem; -import org.junit.Before; -import org.junit.Test; - -import java.io.File; -import java.text.SimpleDateFormat; -import java.util.TimeZone; - -import static org.junit.Assert.*; - -public class AdrToolsImporterTests { - - private Workspace workspace; - private SoftwareSystem softwareSystem; - private Documentation documentation; - - @Before - public void setUp() { - workspace = new Workspace("Name", "Description"); - softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); - documentation = workspace.getDocumentation(); - } - - @Test - public void test_construction_ThrowsAnException_WhenAWorkspaceIsNotSpecified() { - try { - new AdrToolsImporter(null, null); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("A workspace must be specified.", iae.getMessage()); - } - } - - @Test - public void test_construction_ThrowsAnException_WhenADirectoryIsNotSpecified() { - try { - new AdrToolsImporter(workspace, null); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("The path to the architecture decision records must be specified.", iae.getMessage()); - } - } - - @Test - public void test_construction_ThrowsAnException_WhenADirectoryIsSpecifiedButItDoesNotExist() { - try { - new AdrToolsImporter(workspace, new File("some-random-path")); - fail(); - } catch (IllegalArgumentException iae) { - assertTrue(iae.getMessage().endsWith("structurizr-adr-tools/some-random-path does not exist.")); - } - } - - @Test - public void test_construction_ThrowsAnException_WhenAPathIsSpecifiedButItIsNotADirectory() { - try { - new AdrToolsImporter(workspace, new File("build.gradle")); - fail(); - } catch (IllegalArgumentException iae) { - assertTrue(iae.getMessage().endsWith("structurizr-adr-tools/build.gradle is not a directory.")); - } - } - - @Test - public void test_importArchitectureDecisionRecords() throws Exception { - AdrToolsImporter importer = new AdrToolsImporter(workspace, new File("test/unit/adrs")); - importer.importArchitectureDecisionRecords(); - - assertEquals(10, documentation.getDecisions().size()); - - Decision decision1 = documentation.getDecisions().stream().filter(d -> d.getId().equals("1")).findFirst().get(); - assertEquals("1", decision1.getId()); - assertEquals("Record architecture decisions", decision1.getTitle()); - SimpleDateFormat sdf = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss ZZZ"); - sdf.setTimeZone(TimeZone.getTimeZone("UTC")); - assertEquals("12-Feb-2016 00:00:00 +0000", sdf.format(decision1.getDate())); - assertEquals(DecisionStatus.Accepted, decision1.getStatus()); - assertEquals(Format.Markdown, decision1.getFormat()); - assertEquals("# 1. Record architecture decisions\n" + - "\n" + - "Date: 2016-02-12\n" + - "\n" + - "## Status\n" + - "\n" + - "Accepted\n" + - "\n" + - "## Context\n" + - "\n" + - "We need to record the architectural decisions made on this project.\n" + - "\n" + - "## Decision\n" + - "\n" + - "We will use Architecture Decision Records, as described by Michael Nygard in this article: http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions\n" + - "\n" + - "## Consequences\n" + - "\n" + - "See Michael Nygard's article, linked above.\n", - decision1.getContent()); - } - - @Test - public void test_importArchitectureDecisionRecords_RewritesLinksBetweenADRsWhenTheyAreNotAssociatedWithAnElement() throws Exception { - AdrToolsImporter importer = new AdrToolsImporter(workspace, new File("test/unit/adrs")); - importer.importArchitectureDecisionRecords(); - - Decision decision5 = documentation.getDecisions().stream().filter(d -> d.getId().equals("5")).findFirst().get(); - assertTrue(decision5.getContent().contains("Amended by [9. Help scripts](#/:9)")); - } - - @Test - public void test_importArchitectureDecisionRecords_RewritesLinksBetweenADRsWhenTheyAreAssociatedWithAnElement() throws Exception { - AdrToolsImporter importer = new AdrToolsImporter(workspace, new File("test/unit/adrs")); - importer.importArchitectureDecisionRecords(softwareSystem); - - Decision decision5 = documentation.getDecisions().stream().filter(d -> d.getId().equals("5")).findFirst().get(); - assertTrue(decision5.getContent().contains("Amended by [9. Help scripts](#%2FSoftware%20System:9)")); - } - - @Test - public void test_importArchitectureDecisionRecords_SupportsTheIncorrectSpellingOfSuperseded() throws Exception { - AdrToolsImporter importer = new AdrToolsImporter(workspace, new File("test/unit/adrs")); - importer.importArchitectureDecisionRecords(); - - Decision decision4 = documentation.getDecisions().stream().filter(d -> d.getId().equals("4")).findFirst().get(); - assertEquals(DecisionStatus.Superseded, decision4.getStatus()); - System.out.println(decision4.getContent()); - assertTrue(decision4.getContent().contains("Superceded by [10. AsciiDoc format](#/:10)")); - } - -} \ No newline at end of file diff --git a/structurizr-analysis/build.gradle b/structurizr-analysis/build.gradle deleted file mode 100644 index 37e82fbcd..000000000 --- a/structurizr-analysis/build.gradle +++ /dev/null @@ -1,26 +0,0 @@ -dependencies { - - compile project(':structurizr-annotations') - compile project(':structurizr-core') - - compile 'org.reflections:reflections:0.9.10' - compile 'org.javassist:javassist:3.22.0-CR2' - - compile files("${System.getProperty('java.home')}/../lib/tools.jar") - - testCompile 'junit:junit:4.12' - -} - -sourceSets { - main { - java { - srcDir 'src' - } - } - test { - java { - srcDir 'test/unit' - } - } -} \ No newline at end of file diff --git a/structurizr-analysis/src/com/structurizr/analysis/AbstractComponentFinderStrategy.java b/structurizr-analysis/src/com/structurizr/analysis/AbstractComponentFinderStrategy.java deleted file mode 100644 index 1cbd100ba..000000000 --- a/structurizr-analysis/src/com/structurizr/analysis/AbstractComponentFinderStrategy.java +++ /dev/null @@ -1,208 +0,0 @@ -package com.structurizr.analysis; - -import com.structurizr.model.CodeElement; -import com.structurizr.model.Component; -import com.structurizr.model.Container; -import com.structurizr.util.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Modifier; -import java.util.*; - -/** - * This is the superclass for a number of component finder strategies. - */ -public abstract class AbstractComponentFinderStrategy implements ComponentFinderStrategy { - - private static final Log log = LogFactory.getLog(AbstractComponentFinderStrategy.class); - - private Set componentsFound = new HashSet<>(); - - protected ComponentFinder componentFinder; - - protected List supportingTypesStrategies = new ArrayList<>(); - - private DuplicateComponentStrategy duplicateComponentStrategy = new ThrowExceptionDuplicateComponentStrategy(); - - protected AbstractComponentFinderStrategy(SupportingTypesStrategy... strategies) { - Arrays.stream(strategies).forEach(this::addSupportingTypesStrategy); - } - - protected ComponentFinder getComponentFinder() { - return componentFinder; - } - - /** - * Sets a reference to the parent component finder. - * - * @param componentFinder a ComponentFinder instance - */ - public void setComponentFinder(ComponentFinder componentFinder) { - this.componentFinder = componentFinder; - } - - protected TypeRepository getTypeRepository() { - return componentFinder.getTypeRepository(); - } - - @Override - public void beforeFindComponents() { - supportingTypesStrategies.forEach(sts -> sts.setTypeRepository(getTypeRepository())); - } - - @Override - public Set findComponents() { - componentsFound.addAll(doFindComponents()); - - return componentsFound; - } - - /** - * A template method into which subclasses can put their component finding code. - * - * @return the Set of Components found, or an empty set if no components were found - */ - protected abstract Set doFindComponents(); - - @Override - public void afterFindComponents() { - findSupportingTypes(componentsFound); - findDependencies(); - } - - private void findSupportingTypes(Set components) { - for (Component component : components) { - for (CodeElement codeElement : component.getCode()) { - TypeVisibility visibility = TypeUtils.getVisibility(getTypeRepository(), codeElement.getType()); - if (visibility != null) { - codeElement.setVisibility(visibility.getName()); - } - - TypeCategory category = TypeUtils.getCategory(getTypeRepository(), codeElement.getType()); - if (category != null) { - codeElement.setCategory(category.getName()); - } - } - - for (SupportingTypesStrategy strategy : supportingTypesStrategies) { - for (Class type : strategy.findSupportingTypes(component)) { - if (!isNestedClass(type) && componentFinder.getContainer().getComponentOfType(type.getCanonicalName()) == null) { - CodeElement codeElement = component.addSupportingType(type.getCanonicalName()); - - TypeVisibility visibility = TypeUtils.getVisibility(getTypeRepository(), codeElement.getType()); - if (visibility != null) { - codeElement.setVisibility(visibility.getName()); - } - - TypeCategory category = TypeUtils.getCategory(getTypeRepository(), codeElement.getType()); - if (category != null) { - codeElement.setCategory(category.getName()); - } - } - } - } - } - } - - private boolean isNestedClass(Class type) { - return type != null && type.getName().indexOf('$') > -1; - } - - private void findDependencies() { - for (Component component : componentFinder.getContainer().getComponents()) { - for (CodeElement codeElement : component.getCode()) { - addEfferentDependencies(component, codeElement.getType(), new HashSet<>()); - } - } - } - - private void addEfferentDependencies(Component component, String type, Set typesVisited) { - typesVisited.add(type); - - for (Class referencedType : getTypeRepository().findReferencedTypes(type)) { - try { - if (!isNestedClass(referencedType)) { - String referencedTypeName = referencedType.getCanonicalName(); - if (!StringUtils.isNullOrEmpty(referencedTypeName)) { - Component destinationComponent = componentFinder.getContainer().getComponentOfType(referencedTypeName); - if (destinationComponent != null) { - if (component != destinationComponent) { - component.uses(destinationComponent, ""); - } - } else if (!typesVisited.contains(referencedTypeName)) { - addEfferentDependencies(component, referencedTypeName, typesVisited); - } - } - } - } catch (Throwable t) { - log.warn(t); - } - } - } - - /** - * Adds a supporting type strategy to this component finder strategy. - * - * @param supportingTypesStrategy a SupportingTypesStrategy instance - */ - public void addSupportingTypesStrategy(SupportingTypesStrategy supportingTypesStrategy) { - if (supportingTypesStrategy == null) { - throw new IllegalArgumentException("A supporting types strategy must be provided."); - } - - supportingTypesStrategies.add(supportingTypesStrategy); - } - - protected Set> findTypesAnnotatedWith(Class annotation) { - return TypeUtils.findTypesAnnotatedWith(annotation, getTypeRepository().getAllTypes()); - } - - protected Set findClassesWithAnnotation(Class type, String technology) { - return findClassesWithAnnotation(type, technology, false); - } - - protected Set findClassesWithAnnotation(Class type, String technology, boolean includePublicTypesOnly) { - Set components = new HashSet<>(); - Set> componentTypes = findTypesAnnotatedWith(type); - for (Class componentType : componentTypes) { - if (!includePublicTypesOnly || Modifier.isPublic(componentType.getModifiers())) { - final Container container = getComponentFinder().getContainer(); - Component newComponent = addComponent( - container, - componentType.getSimpleName(), - componentType.getCanonicalName(), - "", - technology); - - if (newComponent != null) { - components.add(newComponent); - } - } - } - - return components; - } - - public DuplicateComponentStrategy getDuplicateComponentStrategy() { - return duplicateComponentStrategy; - } - - public void setDuplicateComponentStrategy(DuplicateComponentStrategy duplicateComponentStrategy) { - if (duplicateComponentStrategy != null) { - this.duplicateComponentStrategy = duplicateComponentStrategy; - } else { - this.duplicateComponentStrategy = new ThrowExceptionDuplicateComponentStrategy(); - } - } - - protected Component addComponent(Container container, String name, String type, String description, String technology) { - if (container.getComponentWithName(name) == null) { - return container.addComponent(name, type, description, technology); - } else { - return duplicateComponentStrategy.duplicateComponentFound(container.getComponentWithName(name), name, type, description, technology); - } - } - -} \ No newline at end of file diff --git a/structurizr-analysis/src/com/structurizr/analysis/AbstractTypeMatcher.java b/structurizr-analysis/src/com/structurizr/analysis/AbstractTypeMatcher.java deleted file mode 100644 index f2f800980..000000000 --- a/structurizr-analysis/src/com/structurizr/analysis/AbstractTypeMatcher.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.structurizr.analysis; - -/** - * A superclass used for TypeMatcher implementations. - */ -public abstract class AbstractTypeMatcher implements TypeMatcher { - - private String description; - private String technology; - - public AbstractTypeMatcher(String description, String technology) { - this.description = description; - this.technology = technology; - } - - @Override - public String getDescription() { - return description; - } - - @Override - public String getTechnology() { - return technology; - } - -} \ No newline at end of file diff --git a/structurizr-analysis/src/com/structurizr/analysis/AnnotationTypeMatcher.java b/structurizr-analysis/src/com/structurizr/analysis/AnnotationTypeMatcher.java deleted file mode 100644 index 059b5c13d..000000000 --- a/structurizr-analysis/src/com/structurizr/analysis/AnnotationTypeMatcher.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.structurizr.analysis; - -import java.lang.annotation.Annotation; - -/** - * Matches types based upon the presence of a type-level annotation. - */ -public class AnnotationTypeMatcher extends AbstractTypeMatcher { - - private Class annotation; - - public AnnotationTypeMatcher(Class annotation, String description, String technology) { - super(description, technology); - - if (annotation == null) { - throw new IllegalArgumentException("An annotation must be supplied"); - } - - this.annotation = annotation; - } - - @Override - public boolean matches(Class type) { - return type.getAnnotation(annotation) != null; - } - -} \ No newline at end of file diff --git a/structurizr-analysis/src/com/structurizr/analysis/ComponentFinder.java b/structurizr-analysis/src/com/structurizr/analysis/ComponentFinder.java deleted file mode 100644 index ce5c9cd3c..000000000 --- a/structurizr-analysis/src/com/structurizr/analysis/ComponentFinder.java +++ /dev/null @@ -1,186 +0,0 @@ -package com.structurizr.analysis; - -import com.structurizr.model.Component; -import com.structurizr.model.Container; - -import java.net.URLClassLoader; -import java.util.*; -import java.util.regex.Pattern; - -import static com.structurizr.util.StringUtils.isNullOrEmpty; - -/** - * This class allows you to find components in a Java codebase, when used in conjunction - * with a number of pluggable component finder strategies. - */ -public class ComponentFinder { - - private URLClassLoader urlClassLoader; - private TypeRepository typeRepository; - private Container container; - private List packageNames = new ArrayList<>(); - - // this is a default of regexes representing types we're probably not interested in */ - private Set exclusions = new HashSet<>(Arrays.asList( - Pattern.compile("java\\..*"), - Pattern.compile("javax\\..*"), - Pattern.compile("sun\\..*") - )); - - // the list of strategies, which will be executed in the order they are added - private List componentFinderStrategies = new ArrayList<>(); - - /** - * Create a new component finder. - * - * @param container the Container that components will be added to - * @param packageName the Java package name to be scanned (e.g. "com.mycompany.myapp") - * @param componentFinderStrategies one or more ComponentFinderStrategy objects, describing how to find components - */ - public ComponentFinder(Container container, String packageName, ComponentFinderStrategy... componentFinderStrategies) { - if (container == null) { - throw new IllegalArgumentException("A container must be specified."); - } - - if (isNullOrEmpty(packageName)) { - throw new IllegalArgumentException("A package name must be specified."); - } - - if (componentFinderStrategies.length == 0) { - throw new IllegalArgumentException("One or more ComponentFinderStrategy objects must be specified."); - } - - this.container = container; - this.packageNames.add(packageName); - - for (ComponentFinderStrategy componentFinderStrategy : componentFinderStrategies) { - this.componentFinderStrategies.add(componentFinderStrategy); - componentFinderStrategy.setComponentFinder(this); - } - } - - /** - * Find components, using all of the configured component finder strategies - * in the order they were added. - * - * @return the set of Components that were found - * @throws Exception if something goes wrong - */ - public Set findComponents() throws Exception { - Set componentsFound = new HashSet<>(); - - for (ComponentFinderStrategy componentFinderStrategy : componentFinderStrategies) { - componentFinderStrategy.beforeFindComponents(); - } - - for (ComponentFinderStrategy componentFinderStrategy : componentFinderStrategies) { - componentsFound.addAll(componentFinderStrategy.findComponents()); - } - - for (ComponentFinderStrategy componentFinderStrategy : componentFinderStrategies) { - componentFinderStrategy.afterFindComponents(); - } - - return componentsFound; - } - - /** - * Gets the Container that components will be added to. - * - * @return a Container instance - */ - public Container getContainer() { - return this.container; - } - - /** - * Adds a package name to be scanned. - * - * @param packageName the package name as a String - */ - public void addPackageName(String packageName) { - if (isNullOrEmpty(packageName)) { - throw new IllegalArgumentException("A package name must be specified."); - } - - packageNames.add(packageName); - } - - /** - * Gets the names of the packages to be scanned. - * - * @return the package names, as a List of String - */ - public List getPackageNames() { - return new ArrayList<>(packageNames); - } - - /** - * Gets the set of regexes that define which types should be excluded during the component finding process. - * - * @return a set of Pattern (regex) instances - */ - public Set getExclusions() { - return new HashSet<>(exclusions); - } - - /** - * Adds one or more regexes to the set of regexes that define which types should be excluded during the component finding process. - * - * @param regexes one or more regular expressions, as Strings - */ - public void exclude(String... regexes) { - if (regexes != null) { - for (String regex : regexes) { - this.exclusions.add(Pattern.compile(regex)); - } - } - } - - /** - * Clears the set of exclusions. - */ - public void clearExclusions() { - this.exclusions.clear(); - } - - /** - * Sets a classloader to load classes from instead of the system classloader. - * - * @param urlClassLoader the classloader to use - */ - public void setUrlClassLoader(URLClassLoader urlClassLoader) { - this.urlClassLoader = urlClassLoader; - } - - /** - * Gets the classloader used to load classes. - * - * @return the classloader to use, or null if system classloader - */ - public URLClassLoader getUrlClassLoader() { - return urlClassLoader; - } - - /** - * Sets the type repository used to analyse java classes. - * - * @param typeRepository the type repository to use when analysing - */ - public void setTypeRepository(TypeRepository typeRepository) { - this.typeRepository = typeRepository; - } - - /** - * Gets the type repository used to analyse java classes. - * - * @return the type supplied type repository, or a default implementation - */ - public TypeRepository getTypeRepository() { - if (typeRepository == null) { - typeRepository = new DefaultTypeRepository(getPackageNames(), getExclusions(), getUrlClassLoader()); - } - return typeRepository; - } - -} \ No newline at end of file diff --git a/structurizr-analysis/src/com/structurizr/analysis/ComponentFinderStrategy.java b/structurizr-analysis/src/com/structurizr/analysis/ComponentFinderStrategy.java deleted file mode 100644 index f0c5e25b5..000000000 --- a/structurizr-analysis/src/com/structurizr/analysis/ComponentFinderStrategy.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.structurizr.analysis; - -import com.structurizr.model.Component; - -import java.util.Set; - -/** - * The interface that all component finder strategies must implement. - */ -public interface ComponentFinderStrategy { - - /** - * Sets a reference to the parent component finder. - * - * @param componentFinder a ComponentFinder instance - */ - void setComponentFinder(ComponentFinder componentFinder); - - /** - * Called before all component finder strategies belonging to the - * same component finder are asked to find components. - * - * @throws Exception if something goes wrong - */ - void beforeFindComponents() throws Exception; - - /** - * Finds components. - * - * @return the set of components found - * @throws Exception if something goes wrong - */ - Set findComponents() throws Exception; - - /** - * Called after all component finder strategies belonging to the - * same component finder have found components. This can be used - * to supplement the component with more information, such as - * dependencies. - * - * @throws Exception if something goes wrong - */ - void afterFindComponents() throws Exception; - -} \ No newline at end of file diff --git a/structurizr-analysis/src/com/structurizr/analysis/DefaultTypeRepository.java b/structurizr-analysis/src/com/structurizr/analysis/DefaultTypeRepository.java deleted file mode 100644 index 9a618280e..000000000 --- a/structurizr-analysis/src/com/structurizr/analysis/DefaultTypeRepository.java +++ /dev/null @@ -1,187 +0,0 @@ -package com.structurizr.analysis; - -import javassist.ClassPool; -import javassist.CtClass; -import javassist.LoaderClassPath; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.reflections.ReflectionUtils; -import org.reflections.Reflections; -import org.reflections.scanners.AbstractScanner; -import org.reflections.scanners.SubTypesScanner; -import org.reflections.util.ClasspathHelper; -import org.reflections.util.ConfigurationBuilder; -import org.reflections.util.FilterBuilder; - -import java.net.URL; -import java.net.URLClassLoader; -import java.util.*; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -import static java.util.Arrays.asList; - -/** - * This is an implementation of a TypeRepository that uses a combination of: - * - The Reflections library (https://github.com/ronmamo/reflections). - * - Javassist (http://jboss-javassist.github.io/javassist/) - * - Java Reflection - */ -public class DefaultTypeRepository implements TypeRepository { - - private static final Log log = LogFactory.getLog(DefaultTypeRepository.class); - - private final Set> types; - private final ClassLoader classLoader; - - private List packagesToScan; - private Set exclusions = new HashSet<>(); - - private ClassPool classPool; - private Map>> referencedTypesCache = new HashMap<>(); - - /** - * Creates a new instance based upon a package to scan, and a set of exclusions. - * - * @param packageToScan a fully qualified package name - * @param exclusions a Set of Pattern objects - */ - DefaultTypeRepository(String packageToScan, Set exclusions, URLClassLoader urlClassLoader) { - this(asList(packageToScan), exclusions, urlClassLoader); - } - - /** - * Creates a new instance based upon a list of packages to scan, and a set of exclusions. - * - * @param packagesToScan the fully qualified package names - * @param exclusions a Set of Pattern objects - */ - DefaultTypeRepository(List packagesToScan, Set exclusions, URLClassLoader urlClassLoader) { - final Collection urls; - - if (urlClassLoader == null) { - classLoader = ClassLoader.getSystemClassLoader(); - urls = ClasspathHelper.forJavaClassPath(); - classPool = ClassPool.getDefault(); - } else { - classLoader = urlClassLoader; - urls = asList(urlClassLoader.getURLs()); - classPool = new ClassPool(); - classPool.insertClassPath(new LoaderClassPath(urlClassLoader)); - } - - this.packagesToScan = packagesToScan; - if (exclusions != null) { - this.exclusions.addAll(exclusions); - } - - AllTypesScanner allTypesScanner = new AllTypesScanner(); - Reflections reflections = new Reflections(new ConfigurationBuilder() - .setUrls(urls) - .filterInputsBy(new FilterBuilder().includePackage(packagesToScan.toArray(new String[packagesToScan.size()]))) - .setScanners(new SubTypesScanner(false), allTypesScanner) - ); - - types = new HashSet<>(); - types.addAll(ReflectionUtils.forNames(allTypesScanner.types, classLoader)); - } - - @Override - public Class loadClass(String typeName) throws ClassNotFoundException { - return classLoader.loadClass(typeName); - } - - /** - * Gets the packages that this type repository is associated with scanning. - * - * @return the fully qualified package names - */ - public List getPackages() { - return packagesToScan; - } - - /** - * Gets all of the types found by this type repository. - * - * @return a Set of Class objects, or an empty set of no classes were found - */ - public Set> getAllTypes() { - return new HashSet<>(types); - } - - /** - * Finds the set of types referenced by the specified type. - * - * @param typeName the starting type - * @return a Set of Class objects, or an empty set if none were found - */ - public Set> findReferencedTypes(String typeName) { - Set> referencedTypes = new HashSet<>(); - - // use the cached version if possible - if (referencedTypesCache.containsKey(typeName)) { - return referencedTypesCache.get(typeName); - } - - try { - CtClass cc = classPool.get(typeName); - for (Object referencedType : cc.getRefClasses()) { - String referencedTypeName = (String)referencedType; - - if (!isExcluded(referencedTypeName)) { - try { - referencedTypes.add(loadClass(referencedTypeName)); - } catch (Throwable t) { - log.debug("Could not find " + referencedTypeName + " ... ignoring."); - } - } - } - - // remove the type itself - referencedTypes.remove(loadClass(typeName)); - } catch (Exception e) { - log.debug("Error finding referenced types for " + typeName + " ... ignoring."); - - // since there was an error, we can't find the set of referenced types from it, so... - referencedTypesCache.put(typeName, new HashSet<>()); - } - - // cache for the next time - referencedTypesCache.put(typeName, referencedTypes); - - return referencedTypes; - } - - private Set> filter(Set> types) { - return types.stream().filter(c -> !isExcluded(c.getCanonicalName())).collect(Collectors.toSet()); - } - - private boolean isExcluded(String typeName) { - if (typeName == null) { - return true; - } - - for (Pattern exclude : exclusions) { - if (exclude.matcher(typeName).matches()) { - return true; - } - } - - return false; - } - - class AllTypesScanner extends AbstractScanner { - - Set types = new HashSet<>(); - - @Override - public void scan(Object cls) { - String typeName = getMetadataAdapter().getClassName(cls); - - if (!isExcluded(typeName)) { - types.add(typeName); - } - } - } - -} \ No newline at end of file diff --git a/structurizr-analysis/src/com/structurizr/analysis/DuplicateComponentException.java b/structurizr-analysis/src/com/structurizr/analysis/DuplicateComponentException.java deleted file mode 100644 index dbb591502..000000000 --- a/structurizr-analysis/src/com/structurizr/analysis/DuplicateComponentException.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.structurizr.analysis; - -public class DuplicateComponentException extends RuntimeException { - - DuplicateComponentException(String message) { - super(message); - } - -} \ No newline at end of file diff --git a/structurizr-analysis/src/com/structurizr/analysis/DuplicateComponentStrategy.java b/structurizr-analysis/src/com/structurizr/analysis/DuplicateComponentStrategy.java deleted file mode 100644 index fa32efaff..000000000 --- a/structurizr-analysis/src/com/structurizr/analysis/DuplicateComponentStrategy.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.structurizr.analysis; - -import com.structurizr.model.Component; - -/** - * Defines a strategy that should be called when a duplicate component is found. - */ -public interface DuplicateComponentStrategy { - - /** - * Called when a duplicate component is found. - * - * @param component the existing Component object - * @param name the new component name - * @param type the new component type - * @param description the new description - * @param technology the new technology - * - * @return a Component instance, or null if a new component should not be created - */ - Component duplicateComponentFound(Component component, String name, String type, String description, String technology); - -} \ No newline at end of file diff --git a/structurizr-analysis/src/com/structurizr/analysis/ExtendsClassTypeMatcher.java b/structurizr-analysis/src/com/structurizr/analysis/ExtendsClassTypeMatcher.java deleted file mode 100644 index 129fccce2..000000000 --- a/structurizr-analysis/src/com/structurizr/analysis/ExtendsClassTypeMatcher.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.structurizr.analysis; - -/** - * Matches types where the type extends the specified class. - */ -public class ExtendsClassTypeMatcher extends AbstractTypeMatcher { - - private Class classType; - - public ExtendsClassTypeMatcher(Class classType, String description, String technology) { - super(description, technology); - - if (classType.isInterface() || classType.isEnum()) { - throw new IllegalArgumentException(classType + " is not a class type"); - } - - this.classType = classType; - } - - @Override - public boolean matches(Class type) { - return classType.isAssignableFrom(type); - } - -} \ No newline at end of file diff --git a/structurizr-analysis/src/com/structurizr/analysis/FirstImplementationOfInterfaceSupportingTypesStrategy.java b/structurizr-analysis/src/com/structurizr/analysis/FirstImplementationOfInterfaceSupportingTypesStrategy.java deleted file mode 100644 index 6c2ffe34a..000000000 --- a/structurizr-analysis/src/com/structurizr/analysis/FirstImplementationOfInterfaceSupportingTypesStrategy.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.structurizr.analysis; - -import com.structurizr.model.Component; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import java.util.HashSet; -import java.util.Set; - -/** - * If the component type is an interface, this strategy finds the first implementation of that interface. - */ -public class FirstImplementationOfInterfaceSupportingTypesStrategy extends SupportingTypesStrategy { - - private static final Log log = LogFactory.getLog(FirstImplementationOfInterfaceSupportingTypesStrategy.class); - - @Override - public Set> findSupportingTypes(Component component) { - Set> set = new HashSet<>(); - - try { - Class componentType = getTypeRepository().loadClass(component.getType().getType()); - if (componentType.isInterface()) { - Class type = TypeUtils.findFirstImplementationOfInterface(componentType, getTypeRepository().getAllTypes()); - if (type != null) { - set.add(type); - } - } - } catch (ClassNotFoundException e) { - log.warn("Could not load type " + component.getType().getType()); - } - - return set; - } - -} diff --git a/structurizr-analysis/src/com/structurizr/analysis/ImplementsInterfaceTypeMatcher.java b/structurizr-analysis/src/com/structurizr/analysis/ImplementsInterfaceTypeMatcher.java deleted file mode 100644 index a95766f42..000000000 --- a/structurizr-analysis/src/com/structurizr/analysis/ImplementsInterfaceTypeMatcher.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.structurizr.analysis; - -/** - * Matches types where the type implements the specified interface. - */ -public class ImplementsInterfaceTypeMatcher extends AbstractTypeMatcher { - - private Class interfaceType; - - public ImplementsInterfaceTypeMatcher(Class interfaceType, String description, String technology) { - super(description, technology); - - if (!interfaceType.isInterface()) { - throw new IllegalArgumentException(interfaceType + " is not an interface type"); - } - - this.interfaceType = interfaceType; - } - - @Override - public boolean matches(Class type) { - return interfaceType.isAssignableFrom(type); - } - -} \ No newline at end of file diff --git a/structurizr-analysis/src/com/structurizr/analysis/JavadocCommentFilter.java b/structurizr-analysis/src/com/structurizr/analysis/JavadocCommentFilter.java deleted file mode 100644 index 4d1db64f7..000000000 --- a/structurizr-analysis/src/com/structurizr/analysis/JavadocCommentFilter.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.structurizr.analysis; - -/** - * Cleans up Javadoc comments for inclusion in the software architecture model. - */ -class JavadocCommentFilter { - - private Integer maxCommentLength; - - JavadocCommentFilter(Integer maxCommentLength) { - if (maxCommentLength != null && maxCommentLength < 1) { - throw new IllegalArgumentException("Maximum comment length must be greater than 0."); - } - - this.maxCommentLength = maxCommentLength; - } - - String filterAndTruncate(String s) { - if (s == null) { - return null; - } - - s = s.replaceAll("\\n", " "); - s = s.replaceAll("(?s)<.*?>", ""); - s = s.replaceAll("\\{@link (\\S*)\\}", "$1"); - s = s.replaceAll("\\{@link (\\S*) (.*?)\\}", "$2"); - - if (maxCommentLength != null && s.length() > maxCommentLength) { - return s.substring(0, maxCommentLength-3) + "..."; - } else { - return s; - } - } - -} \ No newline at end of file diff --git a/structurizr-analysis/src/com/structurizr/analysis/NameSuffixTypeMatcher.java b/structurizr-analysis/src/com/structurizr/analysis/NameSuffixTypeMatcher.java deleted file mode 100644 index 6a139ed22..000000000 --- a/structurizr-analysis/src/com/structurizr/analysis/NameSuffixTypeMatcher.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.structurizr.analysis; - -/** - * Matches types where the name of the type ends with the specified suffix. - */ -public class NameSuffixTypeMatcher extends AbstractTypeMatcher { - - private String suffix; - - public NameSuffixTypeMatcher(String suffix, String description, String technology) { - super(description, technology); - - if (suffix == null || suffix.trim().length() == 0) { - throw new IllegalArgumentException("A suffix must be supplied"); - } - - this.suffix = suffix; - } - - @Override - public boolean matches(Class type) { - return type.getSimpleName().endsWith(suffix); - } - -} diff --git a/structurizr-analysis/src/com/structurizr/analysis/ReferencedTypesInSamePackageSupportingTypesStrategy.java b/structurizr-analysis/src/com/structurizr/analysis/ReferencedTypesInSamePackageSupportingTypesStrategy.java deleted file mode 100644 index 8dc62ff0c..000000000 --- a/structurizr-analysis/src/com/structurizr/analysis/ReferencedTypesInSamePackageSupportingTypesStrategy.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.structurizr.analysis; - -import com.structurizr.model.CodeElement; -import com.structurizr.model.Component; - -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; - -/** - * This strategy finds all referenced types in the same package as the component type, - * and is useful if each component resides in its own Java package. - */ -public class ReferencedTypesInSamePackageSupportingTypesStrategy extends SupportingTypesStrategy { - - private boolean includeIndirectlyReferencedTypes; - - public ReferencedTypesInSamePackageSupportingTypesStrategy() { - this(true); - } - - public ReferencedTypesInSamePackageSupportingTypesStrategy(boolean includeIndirectlyReferencedTypes) { - this.includeIndirectlyReferencedTypes = includeIndirectlyReferencedTypes; - } - - @Override - public Set> findSupportingTypes(Component component) { - CodeElement codeElement = component.getType(); - if (codeElement != null) { - ReferencedTypesSupportingTypesStrategy referencedTypesSupportingTypesStrategy = new ReferencedTypesSupportingTypesStrategy(includeIndirectlyReferencedTypes); - referencedTypesSupportingTypesStrategy.setTypeRepository(getTypeRepository()); - Set> supportingTypes = referencedTypesSupportingTypesStrategy.findSupportingTypes(component); - - return supportingTypes.stream() - .filter(type -> type.getPackage().getName().startsWith(codeElement.getPackage())) - .collect(Collectors.toSet()); - } else { - return new HashSet<>(); - } - } - -} diff --git a/structurizr-analysis/src/com/structurizr/analysis/ReferencedTypesSupportingTypesStrategy.java b/structurizr-analysis/src/com/structurizr/analysis/ReferencedTypesSupportingTypesStrategy.java deleted file mode 100644 index e27bb7363..000000000 --- a/structurizr-analysis/src/com/structurizr/analysis/ReferencedTypesSupportingTypesStrategy.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.structurizr.analysis; - -import com.structurizr.model.CodeElement; -import com.structurizr.model.Component; - -import java.util.HashSet; -import java.util.Set; - -import static java.util.stream.Collectors.toSet; - -/** - * This strategy finds all types that are referenced by the component type - * and supporting types. - */ -public class ReferencedTypesSupportingTypesStrategy extends SupportingTypesStrategy { - - private boolean includeIndirectlyReferencedTypes; - - public ReferencedTypesSupportingTypesStrategy() { - this(true); - } - - public ReferencedTypesSupportingTypesStrategy(boolean includeIndirectlyReferencedTypes) { - this.includeIndirectlyReferencedTypes = includeIndirectlyReferencedTypes; - } - - private Set> getReferencedTypesInPackage(String type) { - return getTypeRepository() - .findReferencedTypes(type) - .stream() - .filter(this::accepts) - .collect(toSet()); - } - - private boolean accepts(final Class clazz) { - final String type = clazz.getCanonicalName(); - - if (type != null) { - for (final String packageName : getTypeRepository().getPackages()) { - if (type.startsWith(packageName)) { - return true; - } - } - } - - return false; - } - - @Override - public Set> findSupportingTypes(Component component) { - Set> referencedTypes = new HashSet<>(); - referencedTypes.addAll(getReferencedTypesInPackage(component.getType().getType())); - - for (CodeElement codeElement : component.getCode()) { - referencedTypes.addAll(getReferencedTypesInPackage(codeElement.getType())); - } - - if (includeIndirectlyReferencedTypes) { - int numberOfTypes = referencedTypes.size(); - boolean foundMore = true; - while (foundMore) { - Set> indirectlyReferencedTypes = new HashSet<>(); - for (Class type : referencedTypes) { - indirectlyReferencedTypes.addAll(getReferencedTypesInPackage(type.getCanonicalName())); - } - referencedTypes.addAll(indirectlyReferencedTypes); - - if (referencedTypes.size() > numberOfTypes) { - foundMore = true; - numberOfTypes = referencedTypes.size(); - } else { - foundMore = false; - } - } - } - - return referencedTypes; - } - -} diff --git a/structurizr-analysis/src/com/structurizr/analysis/RegexTypeMatcher.java b/structurizr-analysis/src/com/structurizr/analysis/RegexTypeMatcher.java deleted file mode 100644 index 7e1d5bd09..000000000 --- a/structurizr-analysis/src/com/structurizr/analysis/RegexTypeMatcher.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.structurizr.analysis; - -import java.util.regex.Pattern; - -/** - * Matches types using a regex against the fully qualified type name. - */ -public class RegexTypeMatcher extends AbstractTypeMatcher { - - private Pattern regex; - - public RegexTypeMatcher(String regex, String description, String technology) { - super(description, technology); - - if (regex == null) { - throw new IllegalArgumentException("A regex must be supplied"); - } - - this.regex = Pattern.compile(regex); - } - - public RegexTypeMatcher(Pattern regex, String description, String technology) { - super(description, technology); - - if (regex == null) { - throw new IllegalArgumentException("A regex must be supplied"); - } - - this.regex = regex; - } - - @Override - public boolean matches(Class type) { - if (type != null && type.getCanonicalName() != null) { - return Pattern.matches(regex.pattern(), type.getCanonicalName()); - } else { - return false; - } - } - -} diff --git a/structurizr-analysis/src/com/structurizr/analysis/SourceCodeComponentFinderStrategy.java b/structurizr-analysis/src/com/structurizr/analysis/SourceCodeComponentFinderStrategy.java deleted file mode 100644 index c83c27fb3..000000000 --- a/structurizr-analysis/src/com/structurizr/analysis/SourceCodeComponentFinderStrategy.java +++ /dev/null @@ -1,148 +0,0 @@ -package com.structurizr.analysis; - -import com.structurizr.model.CodeElement; -import com.structurizr.model.CodeElementRole; -import com.structurizr.model.Component; -import com.sun.javadoc.ClassDoc; -import com.sun.javadoc.RootDoc; - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.PrintStream; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.*; - -/** - * This component finder strategy doesn't really find components, it instead: - *
    - *
  • Extracts the top-level Javadoc comment from the code so that this can be added to existing component definitions.
  • - *
  • Calculates the size of components based upon the number of lines of source code.
  • - *
- */ -public class SourceCodeComponentFinderStrategy implements ComponentFinderStrategy { - - private ComponentFinder componentFinder; - private static RootDoc ROOTDOC; - - private File sourcePath; - private Integer maxDescriptionLength = null; - private String encoding = null; - - private Map typeToSourceFile = new HashMap<>(); - private Map typeToDescription = new HashMap<>(); - - public SourceCodeComponentFinderStrategy(File sourcePath) { - this.sourcePath = sourcePath; - } - - public SourceCodeComponentFinderStrategy(File sourcePath, int maxDescriptionLength) { - this.sourcePath = sourcePath; - this.maxDescriptionLength = maxDescriptionLength; - } - - @Override - public void setComponentFinder(ComponentFinder componentFinder) { - this.componentFinder = componentFinder; - } - - public void setEncoding(String encoding) { - this.encoding = encoding; - } - - @Override - public void beforeFindComponents() throws Exception { - } - - @Override - public Set findComponents() throws Exception { - return new HashSet<>(); // this component finder doesn't find components - } - - @Override - public void afterFindComponents() throws Exception { - runJavaDoc(); - - JavadocCommentFilter filter = new JavadocCommentFilter(maxDescriptionLength); - for (ClassDoc classDoc : ROOTDOC.classes()) { - String type = classDoc.qualifiedTypeName(); - String comment = filter.filterAndTruncate(classDoc.commentText()); - String pathToSourceFile = classDoc.position().file().getCanonicalPath(); - - typeToSourceFile.put(type, new File(pathToSourceFile)); - typeToDescription.put(type, comment); - } - - for (Component component : componentFinder.getContainer().getComponents()) { - long count = 0; - - for (CodeElement codeElement : component.getCode()) { - if (typeToDescription.containsKey(codeElement.getType())) { - codeElement.setDescription(typeToDescription.get(codeElement.getType())); - - // additionally set the description on the component, if it's not already been set - if (codeElement.getRole() == CodeElementRole.Primary) { - if (component.getDescription() == null || component.getDescription().trim().length() == 0) { - component.setDescription(typeToDescription.get(component.getType().getType())); - } - } - } - - File sourceFile = typeToSourceFile.get(codeElement.getType()); - if (sourceFile != null) { - long numberOfLinesInFile = Files.lines(Paths.get(sourceFile.toURI())).count(); - codeElement.setUrl(sourceFile.toURI().toString()); - codeElement.setSize(numberOfLinesInFile); - count += numberOfLinesInFile; - } - } - - if (count > 0) { - component.setSize(count); - } - } - } - - private void runJavaDoc() throws Exception { - List parameters = new LinkedList<>(); - parameters.add("-sourcepath"); - parameters.add(sourcePath.getCanonicalPath()); - parameters.add("-subpackages"); - parameters.add(String.join(":", componentFinder.getPackageNames())); - - if (encoding != null) { - parameters.add("-encoding"); - parameters.add(encoding); - } - - parameters.add("-private"); - - final PrintStream outOriginal = System.out; - final PrintStream errOriginal = System.err; - final ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - System.setOut(new PrintStream(bytes)); - System.setErr(System.out); - try { - com.sun.tools.javadoc.Main.execute( - "StructurizrDoclet", - this.getClass().getName(), - parameters.toArray(new String[parameters.size()]) - ); - } - catch (Throwable t) { - outOriginal.write(bytes.toByteArray()); - outOriginal.flush(); - throw t; - } - finally { - System.setOut(outOriginal); - System.setOut(errOriginal); - } - } - - public static boolean start(RootDoc rootDoc) { - ROOTDOC = rootDoc; - return true; - } - -} \ No newline at end of file diff --git a/structurizr-analysis/src/com/structurizr/analysis/StructurizrAnnotationsComponentFinderStrategy.java b/structurizr-analysis/src/com/structurizr/analysis/StructurizrAnnotationsComponentFinderStrategy.java deleted file mode 100644 index b5aff3305..000000000 --- a/structurizr-analysis/src/com/structurizr/analysis/StructurizrAnnotationsComponentFinderStrategy.java +++ /dev/null @@ -1,240 +0,0 @@ -package com.structurizr.analysis; - -import com.structurizr.annotation.*; -import com.structurizr.model.*; -import com.structurizr.model.Component; -import com.structurizr.util.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import java.lang.reflect.Field; -import java.util.HashSet; -import java.util.Set; - -/** - * This component finder strategy looks for the following Structurizr annotations. - * - * - Definitions: @Component - * - Efferent dependencies: @UsesSoftwareSystem, @UsesContainer, @UsesComponent - * - Afferent dependencies: @UsedByPerson, @UsedBySoftwareSystem, @UsedByContainer - */ -public class StructurizrAnnotationsComponentFinderStrategy extends AbstractComponentFinderStrategy { - - private static final Log log = LogFactory.getLog(StructurizrAnnotationsComponentFinderStrategy.class); - - public StructurizrAnnotationsComponentFinderStrategy() { - super(new FirstImplementationOfInterfaceSupportingTypesStrategy()); - } - - public StructurizrAnnotationsComponentFinderStrategy(SupportingTypesStrategy... strategies) { - super(strategies); - } - - @Override - protected Set doFindComponents() { - Set components = new HashSet<>(); - Container container = getComponentFinder().getContainer(); - - // find all types that have been annotated @Component - Set> componentTypes = findTypesAnnotatedWith(com.structurizr.annotation.Component.class); - for (Class componentType : componentTypes) { - Component component = container.addComponent( - componentType.getSimpleName(), - componentType, - componentType.getAnnotation(com.structurizr.annotation.Component.class).description(), - componentType.getAnnotation(com.structurizr.annotation.Component.class).technology() - ); - components.add(component); - } - - return components; - } - - @Override - public void afterFindComponents() { - // this will find component dependencies, but the relationship descriptions - // will be empty because we can't get that from the code - super.afterFindComponents(); - - for (Component component : getComponentFinder().getContainer().getComponents()) { - for (CodeElement codeElement : component.getCode()) { - // find the efferent dependencies - findUsesComponentAnnotations(component, codeElement.getType()); - findUsesSoftwareSystemsAnnotations(component, codeElement.getType()); - findUsesContainerAnnotations(component, codeElement.getType()); - - // and also the afferent dependencies - findUsedByPersonAnnotations(component, codeElement.getType()); - findUsedBySoftwareSystemAnnotations(component, codeElement.getType()); - findUsedByContainerAnnotations(component, codeElement.getType()); - } - } - } - - /** - * This will add a description to existing component dependencies, where they have - * been annotated @UsesComponent. - */ - private void findUsesComponentAnnotations(Component component, String typeName) { - try { - Class type = getTypeRepository().loadClass(typeName); - for (Field field : type.getDeclaredFields()) { - UsesComponent annotation = field.getAnnotation(UsesComponent.class); - if (annotation != null) { - String name = field.getType().getCanonicalName(); - String description = field.getAnnotation(UsesComponent.class).description(); - String technology = annotation.technology(); - - Component destination = componentFinder.getContainer().getComponentOfType(name); - if (destination != null) { - for (Relationship relationship : component.getRelationships()) { - if (relationship.getDestination() == destination && StringUtils.isNullOrEmpty(relationship.getDescription())) { - // only change the details of relationships that have no description - component.getModel().modifyRelationship(relationship, description, technology); - } - } - } else { - log.warn("A component of type \"" + name + "\" could not be found."); - } - } - } - } catch (ClassNotFoundException e) { - log.warn("Could not load type " + typeName); - } - } - - /** - * Find the @UsesSoftwareSystem annotations. - */ - private void findUsesSoftwareSystemsAnnotations(Component component, String typeName) { - try { - Class type = getTypeRepository().loadClass(typeName); - UsesSoftwareSystem[] annotations = type.getAnnotationsByType(UsesSoftwareSystem.class); - for (UsesSoftwareSystem annotation : annotations) { - String name = annotation.name(); - String description = annotation.description(); - String technology = annotation.technology(); - - SoftwareSystem softwareSystem = component.getModel().getSoftwareSystemWithName(name); - if (softwareSystem != null) { - component.uses(softwareSystem, description, technology); - } else { - log.warn("A software system named \"" + name + "\" could not be found."); - } - } - } catch (ClassNotFoundException e) { - log.warn("Could not load type " + typeName); - } - } - - /** - * Find the @UsesContainer annotations. - */ - private void findUsesContainerAnnotations(Component component, String typeName) { - try { - Class type = getTypeRepository().loadClass(typeName); - UsesContainer[] annotations = type.getAnnotationsByType(UsesContainer.class); - for (UsesContainer annotation : annotations) { - String name = annotation.name(); - String description = annotation.description(); - String technology = annotation.technology(); - - Container container = findContainerByNameOrCanonicalName(component, name); - if (container != null) { - component.uses(container, description, technology); - } else { - log.warn("A container named \"" + name + "\" could not be found."); - } - } - } catch (ClassNotFoundException e) { - log.warn("Could not load type " + typeName); - } - } - - /** - * Finds @UsedByPerson annotations. - */ - private void findUsedByPersonAnnotations(Component component, String typeName) { - try { - Class type = getTypeRepository().loadClass(typeName); - UsedByPerson[] annotations = type.getAnnotationsByType(UsedByPerson.class); - for (UsedByPerson annotation : annotations) { - String name = annotation.name(); - String description = annotation.description(); - String technology = annotation.technology(); - - Person person = component.getModel().getPersonWithName(name); - if (person != null) { - person.uses(component, description, technology); - } else { - log.warn("A person named \"" + name + "\" could not be found."); - } - } - } catch (ClassNotFoundException e) { - log.warn("Could not load type " + typeName); - } - } - - /** - * Finds @UsedBySoftwareSystem annotations. - */ - private void findUsedBySoftwareSystemAnnotations(Component component, String typeName) { - try { - Class type = getTypeRepository().loadClass(typeName); - UsedBySoftwareSystem[] annotations = type.getAnnotationsByType(UsedBySoftwareSystem.class); - for (UsedBySoftwareSystem annotation : annotations) { - String name = annotation.name(); - String description = annotation.description(); - String technology = annotation.technology(); - - SoftwareSystem softwareSystem = component.getModel().getSoftwareSystemWithName(name); - if (softwareSystem != null) { - softwareSystem.uses(component, description, technology); - } else { - log.warn("A software system named \"" + name + "\" could not be found."); - } - } - } catch (ClassNotFoundException e) { - log.warn("Could not load type " + typeName); - } - } - - /** - * Finds @UsedByContainer annotations. - */ - private void findUsedByContainerAnnotations(Component component, String typeName) { - try { - Class type = getTypeRepository().loadClass(typeName); - UsedByContainer[] annotations = type.getAnnotationsByType(UsedByContainer.class); - for (UsedByContainer annotation : annotations) { - String name = annotation.name(); - String description = annotation.description(); - String technology = annotation.technology(); - - Container container = findContainerByNameOrCanonicalName(component, name); - if (container != null) { - container.uses(component, description, technology); - } else { - log.warn("A container named \"" + name + "\" could not be found."); - } - } - } catch (ClassNotFoundException e) { - log.warn("Could not load type " + typeName); - } - } - - private Container findContainerByNameOrCanonicalName(Component component, String name) { - // assume that the container resides in the same software system - Container container = component.getContainer().getSoftwareSystem().getContainerWithName(name); - if (container == null) { - // perhaps a canonical name has been specified - Element element = component.getModel().getElementWithCanonicalName(name); - if (element != null && element instanceof Container) { - container = (Container)element; - } - } - - return container; - } - -} \ No newline at end of file diff --git a/structurizr-analysis/src/com/structurizr/analysis/SupportingTypesStrategy.java b/structurizr-analysis/src/com/structurizr/analysis/SupportingTypesStrategy.java deleted file mode 100644 index af83634f0..000000000 --- a/structurizr-analysis/src/com/structurizr/analysis/SupportingTypesStrategy.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.structurizr.analysis; - -import com.structurizr.model.Component; - -import java.util.Set; - -/** - * Superclass for strategies used to find the types that support a component. - */ -public abstract class SupportingTypesStrategy { - - private TypeRepository typeRepository; - - protected TypeRepository getTypeRepository() { - return typeRepository; - } - - void setTypeRepository(TypeRepository typeRepository) { - this.typeRepository = typeRepository; - } - - public abstract Set> findSupportingTypes(Component component); - -} diff --git a/structurizr-analysis/src/com/structurizr/analysis/ThrowExceptionDuplicateComponentStrategy.java b/structurizr-analysis/src/com/structurizr/analysis/ThrowExceptionDuplicateComponentStrategy.java deleted file mode 100644 index e39bebfb7..000000000 --- a/structurizr-analysis/src/com/structurizr/analysis/ThrowExceptionDuplicateComponentStrategy.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.structurizr.analysis; - -import com.structurizr.model.Component; - -/** - * Throws an exception when a duplicate component is found. - */ -public class ThrowExceptionDuplicateComponentStrategy implements DuplicateComponentStrategy { - - @Override - public Component duplicateComponentFound(Component component, String name, String type, String description, String technology) { - throw new DuplicateComponentException(String.format( - "A component named \"%s\" already exists in the container named \"%s\".", - component.getName(), - component.getContainer().getName())); - } - -} \ No newline at end of file diff --git a/structurizr-analysis/src/com/structurizr/analysis/TypeCategory.java b/structurizr-analysis/src/com/structurizr/analysis/TypeCategory.java deleted file mode 100644 index e579c4435..000000000 --- a/structurizr-analysis/src/com/structurizr/analysis/TypeCategory.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.structurizr.analysis; - -public final class TypeCategory { - - public static final TypeCategory CLASS = new TypeCategory("class"); - public static final TypeCategory INTERFACE = new TypeCategory("interface"); - public static final TypeCategory ABSTRACT_CLASS = new TypeCategory("abstract"); - public static final TypeCategory ENUM = new TypeCategory("enum"); - - private String name; - - private TypeCategory(String name) { - this.name = name; - } - - public String getName() { - return name; - } - - @Override - public String toString() { - return name; - } - -} diff --git a/structurizr-analysis/src/com/structurizr/analysis/TypeMatcher.java b/structurizr-analysis/src/com/structurizr/analysis/TypeMatcher.java deleted file mode 100644 index ae012fd99..000000000 --- a/structurizr-analysis/src/com/structurizr/analysis/TypeMatcher.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.structurizr.analysis; - -/** - * Determines whether a given type implements the rules for being identified as a component. - */ -public interface TypeMatcher { - - boolean matches(Class type); - - String getDescription(); - - String getTechnology(); - -} diff --git a/structurizr-analysis/src/com/structurizr/analysis/TypeMatcherComponentFinderStrategy.java b/structurizr-analysis/src/com/structurizr/analysis/TypeMatcherComponentFinderStrategy.java deleted file mode 100644 index 591476253..000000000 --- a/structurizr-analysis/src/com/structurizr/analysis/TypeMatcherComponentFinderStrategy.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.structurizr.analysis; - -import com.structurizr.model.Component; -import com.structurizr.model.Container; - -import java.util.*; - -/** - * A component finder strategy that uses type information to find components, based upon a number - * of pluggable {@link TypeMatcher} implementations. - */ -public class TypeMatcherComponentFinderStrategy extends AbstractComponentFinderStrategy { - - private List typeMatchers = new LinkedList<>(); - - public TypeMatcherComponentFinderStrategy(TypeMatcher... typeMatchers) { - this.typeMatchers.addAll(Arrays.asList(typeMatchers)); - } - - @Override - protected Set doFindComponents() { - Set components = new HashSet<>(); - - Set> types = getTypeRepository().getAllTypes(); - for (Class type : types) { - for (TypeMatcher typeMatcher : typeMatchers) { - if (typeMatcher.matches(type)) { - final Container container = getComponentFinder().getContainer(); - Component newComponent = addComponent( - container, - type.getSimpleName(), - type.getCanonicalName(), - typeMatcher.getDescription(), - typeMatcher.getTechnology()); - - if (newComponent != null) { - components.add(newComponent); - } - } - } - } - - return components; - } - -} \ No newline at end of file diff --git a/structurizr-analysis/src/com/structurizr/analysis/TypeRepository.java b/structurizr-analysis/src/com/structurizr/analysis/TypeRepository.java deleted file mode 100644 index 963e1fc3c..000000000 --- a/structurizr-analysis/src/com/structurizr/analysis/TypeRepository.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.structurizr.analysis; - -import java.util.List; -import java.util.Set; - -/** - * This represents an abstraction for a repository of type information. - */ -public interface TypeRepository { - - /** - * Gets the packages that this type repository is associated with scanning. - * - * @return the fully qualified package names - */ - List getPackages(); - - /** - * Gets all of the types found by this type repository. - * - * @return a Set of Class objects, or an empty set of no classes were found - */ - Set> getAllTypes(); - - /** - * Finds the set of types referenced by the specified type. - * - * @param typeName the starting type - * @return a Set of Class objects, or an empty set if none were found - */ - Set> findReferencedTypes(String typeName); - - /** - * Loads the specified type. - * @param typeName the type to load - * @return a Class object - * @throws ClassNotFoundException if the class cannot be found and created - */ - Class loadClass(String typeName) throws ClassNotFoundException; - -} \ No newline at end of file diff --git a/structurizr-analysis/src/com/structurizr/analysis/TypeUtils.java b/structurizr-analysis/src/com/structurizr/analysis/TypeUtils.java deleted file mode 100644 index 13ff98619..000000000 --- a/structurizr-analysis/src/com/structurizr/analysis/TypeUtils.java +++ /dev/null @@ -1,117 +0,0 @@ -package com.structurizr.analysis; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Modifier; -import java.util.Set; -import java.util.stream.Collectors; - -/** - * Some utility methods for working with types. - */ -public class TypeUtils { - - private static final Log log = LogFactory.getLog(TypeUtils.class); - - /** - * Finds the visibility of a given type. - * - * @param typeRepository the repository where types should be loaded from - * @param typeName the fully qualified type name - * @return a TypeVisibility object representing the visibility (e.g. public, package, etc) - */ - public static TypeVisibility getVisibility(TypeRepository typeRepository, String typeName) { - try { - Class type = typeRepository.loadClass(typeName); - int modifiers = type.getModifiers(); - if (Modifier.isPrivate(modifiers)) { - return TypeVisibility.PRIVATE; - } else if (Modifier.isPublic(modifiers)) { - return TypeVisibility.PUBLIC; - } else if (Modifier.isProtected(modifiers)) { - return TypeVisibility.PROTECTED; - } else { - return TypeVisibility.PACKAGE; - } - } catch (ClassNotFoundException e) { - log.warn("Visibility for type " + typeName + " could not be found."); - return null; - } - } - - /** - * Finds the category of a given type. - * - * @param typeRepository the repository where types should be loaded from - * @param typeName the fully qualified type name - * @return a TypeCategory object representing the category (e.g. class, interface, enum, etc) - */ - public static TypeCategory getCategory(TypeRepository typeRepository, String typeName) { - try { - Class type = typeRepository.loadClass(typeName); - if (type.isInterface()) { - return TypeCategory.INTERFACE; - } else if (type.isEnum()) { - return TypeCategory.ENUM; - } else { - if (Modifier.isAbstract(type.getModifiers())) { - return TypeCategory.ABSTRACT_CLASS; - } else{ - return TypeCategory.CLASS; - } - } - } catch (ClassNotFoundException e) { - log.warn("Category for type " + typeName + " could not be found."); - return null; - } - } - - /** - * Finds the set of types that are annotated with the specified annotation. - * - * @param annotation the Annotation to find - * @param types the set of Class objects to search through - * @return a Set of Class objects, or an empty set of none could be found - */ - public static Set> findTypesAnnotatedWith(Class annotation, Set> types) { - if (annotation == null) { - throw new IllegalArgumentException("An annotation type must be specified."); - } - - return types.stream().filter(c -> c.isAnnotationPresent(annotation)).collect(Collectors.toSet()); - } - - /** - * Finds the first implementation of the given interface. - * - * @param interfaceType a Class object representing the interface type - * @param types the set of Class objects to search through - * @return the first concrete implementation class of the given interface, - * or null if one can't be found - */ - public static Class findFirstImplementationOfInterface(Class interfaceType, Set> types) { - if (interfaceType == null) { - throw new IllegalArgumentException("An interface type must be provided."); - } else if (!interfaceType.isInterface()) { - throw new IllegalArgumentException("The interface type must represent an interface."); - } - - if (types == null) { - throw new IllegalArgumentException("The set of types to search through must be provided."); - } - - for (Class type : types) { - boolean isInterface = type.isInterface(); - boolean isAbstract = Modifier.isAbstract(type.getModifiers()); - boolean isAssignable = interfaceType.isAssignableFrom(type); - if (!isInterface && !isAbstract && isAssignable) { - return type; - } - } - - return null; - } - -} \ No newline at end of file diff --git a/structurizr-analysis/src/com/structurizr/analysis/TypeVisibility.java b/structurizr-analysis/src/com/structurizr/analysis/TypeVisibility.java deleted file mode 100644 index c79a18a7c..000000000 --- a/structurizr-analysis/src/com/structurizr/analysis/TypeVisibility.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.structurizr.analysis; - -public final class TypeVisibility { - - public static final TypeVisibility PUBLIC = new TypeVisibility("public"); - public static final TypeVisibility PACKAGE = new TypeVisibility("package"); - public static final TypeVisibility PROTECTED = new TypeVisibility("protected"); - public static final TypeVisibility PRIVATE = new TypeVisibility("private"); - - private String name; - - private TypeVisibility(String name) { - this.name = name; - } - - public String getName() { - return name; - } - - @Override - public String toString() { - return name; - } - -} diff --git a/structurizr-analysis/test/unit/com/structurizr/AbstractWorkspaceTestBase.java b/structurizr-analysis/test/unit/com/structurizr/AbstractWorkspaceTestBase.java deleted file mode 100644 index 91be93b65..000000000 --- a/structurizr-analysis/test/unit/com/structurizr/AbstractWorkspaceTestBase.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.structurizr; - -import com.structurizr.model.Model; -import com.structurizr.view.ViewSet; - -public class AbstractWorkspaceTestBase { - - protected Workspace workspace = new Workspace("Name", "Description"); - protected Model model = workspace.getModel(); - protected ViewSet views = workspace.getViews(); - -} diff --git a/structurizr-analysis/test/unit/com/structurizr/analysis/AbstractComponentFinderStrategyTests.java b/structurizr-analysis/test/unit/com/structurizr/analysis/AbstractComponentFinderStrategyTests.java deleted file mode 100644 index 3c466760e..000000000 --- a/structurizr-analysis/test/unit/com/structurizr/analysis/AbstractComponentFinderStrategyTests.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.structurizr.analysis; - -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -public class AbstractComponentFinderStrategyTests { - - private AbstractComponentFinderStrategy strategy = new TypeMatcherComponentFinderStrategy(); - - @Test - public void test_addSupportingTypesStrategy_ThrowsAnException_WhenANullSupportingTypesStrategyIsSpecified() { - try { - strategy.addSupportingTypesStrategy(null); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("A supporting types strategy must be provided.", iae.getMessage()); - } - } - -} diff --git a/structurizr-analysis/test/unit/com/structurizr/analysis/AnnotationTypeMatcherTests.java b/structurizr-analysis/test/unit/com/structurizr/analysis/AnnotationTypeMatcherTests.java deleted file mode 100644 index 6e2ed87bc..000000000 --- a/structurizr-analysis/test/unit/com/structurizr/analysis/AnnotationTypeMatcherTests.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.structurizr.analysis; - -import com.structurizr.annotation.Component; -import org.junit.Test; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -public class AnnotationTypeMatcherTests { - - @Test(expected = IllegalArgumentException.class) - public void test_construction_ThrowsAnExceptionWhenANullAnnotationClassIsSupplied() - { - new AnnotationTypeMatcher(null, "", ""); - } - - @Test - public void test_matches_ReturnsFalse_WhenTheGivenTypeDoesNotHaveTheAnnotation() - { - AnnotationTypeMatcher matcher = new AnnotationTypeMatcher(Component.class, "", ""); - assertFalse(matcher.matches(MyService.class)); - } - - @Test - public void test_matches_ReturnsTrue_WhenTheGivenTypeDoesHaveTheAnnotation() - { - AnnotationTypeMatcher matcher = new AnnotationTypeMatcher(Component.class, "", ""); - assertTrue(matcher.matches(MyController.class)); - } - - @Component - private class MyController { - } - - private class MyService { - } - -} diff --git a/structurizr-analysis/test/unit/com/structurizr/analysis/ComponentFinderTests.java b/structurizr-analysis/test/unit/com/structurizr/analysis/ComponentFinderTests.java deleted file mode 100644 index 528a98f92..000000000 --- a/structurizr-analysis/test/unit/com/structurizr/analysis/ComponentFinderTests.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.structurizr.analysis; - -import com.structurizr.AbstractWorkspaceTestBase; -import com.structurizr.model.Container; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -public class ComponentFinderTests extends AbstractWorkspaceTestBase { - - @Test - public void test_construction_ThrowsAnException_WhenANullContainerIsSpecified() { - try { - new ComponentFinder(null, null); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("A container must be specified.", iae.getMessage()); - } - } - - @Test - public void test_construction_ThrowsAnException_WhenANullPackageNameIsSpecified() { - try { - Container container = model.addSoftwareSystem("Software System", "").addContainer("Container", "", ""); - new ComponentFinder(container, null); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("A package name must be specified.", iae.getMessage()); - } - } - - @Test - public void test_construction_ThrowsAnException_WhenNoComponentFinderStrategiesAreSpecified() { - try { - Container container = model.addSoftwareSystem("Software System", "").addContainer("Container", "", ""); - new ComponentFinder(container, "com.mycompany.myapp"); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("One or more ComponentFinderStrategy objects must be specified.", iae.getMessage()); - } - } - - @Test - public void test_addPackageName_ThrowsAnException_WhenANullPackageNameIsSpecified() { - try { - Container container = model.addSoftwareSystem("Software System", "").addContainer("Container", "", ""); - ComponentFinder componentFinder = new ComponentFinder(container, "com.mycompany.myapp", new TypeMatcherComponentFinderStrategy(new NameSuffixTypeMatcher("Component", "", ""))); - componentFinder.addPackageName(null); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("A package name must be specified.", iae.getMessage()); - } - } - - @Test - public void test_addPackageName_ThrowsAnException_WhenAnEmptyPackageNameIsSpecified() { - try { - Container container = model.addSoftwareSystem("Software System", "").addContainer("Container", "", ""); - ComponentFinder componentFinder = new ComponentFinder(container, "com.mycompany.myapp", new TypeMatcherComponentFinderStrategy(new NameSuffixTypeMatcher("Component", "", ""))); - componentFinder.addPackageName(" "); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("A package name must be specified.", iae.getMessage()); - } - } - -} \ No newline at end of file diff --git a/structurizr-analysis/test/unit/com/structurizr/analysis/DefaultTypeRepositoryTests.java b/structurizr-analysis/test/unit/com/structurizr/analysis/DefaultTypeRepositoryTests.java deleted file mode 100644 index c99fd7c8d..000000000 --- a/structurizr-analysis/test/unit/com/structurizr/analysis/DefaultTypeRepositoryTests.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.structurizr.analysis; - -import org.junit.Test; - -import java.util.HashSet; -import java.util.Set; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -import static java.util.Arrays.asList; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -public class DefaultTypeRepositoryTests { - - private DefaultTypeRepository typeRepository; - - @Test - public void test_getAllTypes_ReturnsAnEmptySet_WhenNoTypesWereFound() { - typeRepository = new DefaultTypeRepository("com.structurizr.analysis.foo", new HashSet<>(), null); - Set> types = typeRepository.getAllTypes(); - assertTrue(types.isEmpty()); - } - - @Test - public void test_getAllTypes_ReturnsANonEmptySet_WhenTypesWereFound() { - typeRepository = new DefaultTypeRepository("test.DefaultTypeRepository", new HashSet<>(), null); - Set types = typeRepository.getAllTypes().stream().map(Class::getCanonicalName).collect(Collectors.toSet()); - assertEquals(4, types.size()); - - assertTrue(types.contains("test.DefaultTypeRepository.SomeAbstractClass")); - assertTrue(types.contains("test.DefaultTypeRepository.SomeClass")); - assertTrue(types.contains("test.DefaultTypeRepository.SomeEnum")); - assertTrue(types.contains("test.DefaultTypeRepository.SomeInterface")); - } - - @Test - public void test_getAllTypes_ReturnsANonEmptySet_WhenTypesAreFoundAndExclusionsHaveBeenSpecified() { - Set exclusions = new HashSet<>(); - exclusions.add(Pattern.compile(".*Abstract.*")); - typeRepository = new DefaultTypeRepository("test.DefaultTypeRepository", exclusions, null); - Set types = typeRepository.getAllTypes().stream().map(Class::getCanonicalName).collect(Collectors.toSet()); - assertEquals(3, types.size()); - - assertTrue(types.contains("test.DefaultTypeRepository.SomeEnum")); - assertTrue(types.contains("test.DefaultTypeRepository.SomeClass")); - assertTrue(types.contains("test.DefaultTypeRepository.SomeInterface")); - } - - @Test - public void test_getAllTypes_ReturnsCompleteSet_WhenThereAreMultiplePackages() throws Exception { - typeRepository = new DefaultTypeRepository(asList("test.DefaultTypeRepository","test.MoreDefaultTypeRepository"), new HashSet<>(), null); - - Set types = typeRepository.getAllTypes().stream().map(Class::getCanonicalName).collect(Collectors.toSet()); - assertEquals(4+1, types.size()); - - assertTrue(types.contains("test.DefaultTypeRepository.SomeAbstractClass")); - assertTrue(types.contains("test.DefaultTypeRepository.SomeClass")); - assertTrue(types.contains("test.DefaultTypeRepository.SomeEnum")); - assertTrue(types.contains("test.DefaultTypeRepository.SomeInterface")); - - assertTrue(types.contains("test.MoreDefaultTypeRepository.AnotherClass")); - } - - @Test - public void test_findReferencedTypes_ReturnsASetOnlyContainingJavaLangObject_WhenThereAreNoTypesReferenced() throws Exception { - typeRepository = new DefaultTypeRepository("test.DefaultTypeRepository", new HashSet<>(), null); - Set types = typeRepository.findReferencedTypes("test.DefaultTypeRepository.SomeInterface").stream().map(Class::getCanonicalName).collect(Collectors.toSet()); - assertEquals(1, types.size()); - - assertTrue(types.contains("java.lang.Object")); - } - - @Test - public void test_findReferencedTypes_ReturnsANonEmptySet_WhenThereAreTypesReferenced() throws Exception { - typeRepository = new DefaultTypeRepository("test.DefaultTypeRepository", new HashSet<>(), null); - Set types = typeRepository.findReferencedTypes("test.DefaultTypeRepository.SomeClass").stream().map(Class::getCanonicalName).collect(Collectors.toSet()); - assertEquals(3, types.size()); - - assertTrue(types.contains("test.DefaultTypeRepository.SomeEnum")); - assertTrue(types.contains("test.DefaultTypeRepository.SomeAbstractClass")); - assertTrue(types.contains("com.structurizr.annotation.Component")); - } - -} \ No newline at end of file diff --git a/structurizr-analysis/test/unit/com/structurizr/analysis/ExtendsClassTypeMatcherTests.java b/structurizr-analysis/test/unit/com/structurizr/analysis/ExtendsClassTypeMatcherTests.java deleted file mode 100644 index 49f81ff65..000000000 --- a/structurizr-analysis/test/unit/com/structurizr/analysis/ExtendsClassTypeMatcherTests.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.structurizr.analysis; - -import org.junit.Test; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -public class ExtendsClassTypeMatcherTests { - - @Test(expected = IllegalArgumentException.class) - public void test_construction_ThrowsAnExceptionWhenAnInterfaceTypeIsSupplied() - { - new ExtendsClassTypeMatcher(MyRepository.class, "", ""); - } - - @Test(expected = IllegalArgumentException.class) - public void test_construction_ThrowsAnExceptionWhenAnEnumTypeIsSupplied() - { - new ExtendsClassTypeMatcher(MyEnum.class, "", ""); - } - - @Test - public void test_matches_ReturnsFalse_WhenTheGivenTypeDoesNotExtendTheClass() - { - ExtendsClassTypeMatcher matcher = new ExtendsClassTypeMatcher(AbstractComponent.class, "", ""); - assertFalse(matcher.matches(MyOtherClass.class)); - } - - @Test - public void test_matches_ReturnsTrue_WhenTheGivenTypeDoesExtendTheClass() - { - ExtendsClassTypeMatcher matcher = new ExtendsClassTypeMatcher(AbstractComponent.class, "", ""); - assertTrue(matcher.matches(MyController.class)); - assertTrue(matcher.matches(MyRepositoryImpl.class)); - } - - private abstract class AbstractComponent { - } - - private class MyController extends AbstractComponent { - } - - private interface MyRepository { - } - - private class MyRepositoryImpl extends AbstractComponent implements MyRepository { - } - - private class MyOtherClass { - } - - private enum MyEnum { - } - -} diff --git a/structurizr-analysis/test/unit/com/structurizr/analysis/ImplementsInterfaceTypeMatcherTests.java b/structurizr-analysis/test/unit/com/structurizr/analysis/ImplementsInterfaceTypeMatcherTests.java deleted file mode 100644 index 517fcb248..000000000 --- a/structurizr-analysis/test/unit/com/structurizr/analysis/ImplementsInterfaceTypeMatcherTests.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.structurizr.analysis; - -import org.junit.Test; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -public class ImplementsInterfaceTypeMatcherTests { - - @Test(expected = IllegalArgumentException.class) - public void test_construction_ThrowsAnExceptionWhenANonInterfaceTypeIsSupplied() - { - new ImplementsInterfaceTypeMatcher(MyRepositoryImpl.class, "", ""); - } - - @Test - public void test_matches_ReturnsFalse_WhenTheGivenTypeDoesNotImplementTheInterface() - { - ImplementsInterfaceTypeMatcher matcher = new ImplementsInterfaceTypeMatcher(MyRepository.class, "", ""); - assertFalse(matcher.matches(MyController.class)); - } - - @Test - public void test_matches_ReturnsTrue_WhenTheGivenTypeDoesImplementTheInterface() - { - ImplementsInterfaceTypeMatcher matcher = new ImplementsInterfaceTypeMatcher(MyRepository.class, "", ""); - assertTrue(matcher.matches(MyRepositoryImpl.class)); - } - - private class MyController { - } - - private interface MyRepository { - } - - private class MyRepositoryImpl implements MyRepository { - } - -} diff --git a/structurizr-analysis/test/unit/com/structurizr/analysis/JavadocCommentFilterTests.java b/structurizr-analysis/test/unit/com/structurizr/analysis/JavadocCommentFilterTests.java deleted file mode 100644 index 01e774e1f..000000000 --- a/structurizr-analysis/test/unit/com/structurizr/analysis/JavadocCommentFilterTests.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.structurizr.analysis; - -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; - -public class JavadocCommentFilterTests { - - @Test(expected = IllegalArgumentException.class) - public void test_construction_ThrowsAnIllegalArgumentException_WhenZeroIsSpecified() { - new JavadocCommentFilter(0); - } - - @Test(expected = IllegalArgumentException.class) - public void test_construction_ThrowsAnIllegalArgumentException_WhenANegativeNumberIsSpecified() { - new JavadocCommentFilter(-1); - } - - @Test - public void test_filterAndTruncate_ReturnsNull_WhenGivenNull() { - assertNull(new JavadocCommentFilter(null).filterAndTruncate(null)); - } - - @Test - public void test_filterAndTruncate_ReturnsTheOriginalText_WhenNoMaxLengthHasBeenSpecified() - { - assertEquals("Here is some text.", new JavadocCommentFilter(null).filterAndTruncate("Here is some text.")); - } - - @Test - public void test_filterAndTruncate_TruncatesTheTextWhenAMaxLengthHasBeenSpecified() - { - assertEquals("Here...", new JavadocCommentFilter(7).filterAndTruncate("Here is some text.")); - } - - @Test - public void test_filterAndTruncate_FiltersJavadocLinkTags() - { - assertEquals("Uses SomeClass and AnotherClass to do some work.", new JavadocCommentFilter(null).filterAndTruncate("Uses {@link SomeClass} and {@link AnotherClass} to do some work.")); - } - - @Test - public void test_filterAndTruncate_FiltersJavadocLinkTagsWithLabels() - { - assertEquals("Uses some class and another class to do some work.", new JavadocCommentFilter(null).filterAndTruncate("Uses {@link SomeClass some class} and {@link AnotherClass another class} to do some work.")); - } - - @Test - public void test_filterAndTruncate_FiltersHtml() - { - assertEquals("Uses SomeClass and AnotherClass to do some work.", new JavadocCommentFilter(null).filterAndTruncate("Uses SomeClass and AnotherClass to do some work.")); - } - - @Test - public void test_filterAndTruncate_FiltersLineBreaks() - { - assertEquals("Uses SomeClass and AnotherClass to do some work.", new JavadocCommentFilter(null).filterAndTruncate("Uses SomeClass and AnotherClass\nto do some work.")); - } - -} diff --git a/structurizr-analysis/test/unit/com/structurizr/analysis/NameSuffixTypeMatcherTests.java b/structurizr-analysis/test/unit/com/structurizr/analysis/NameSuffixTypeMatcherTests.java deleted file mode 100644 index 8942ea3f3..000000000 --- a/structurizr-analysis/test/unit/com/structurizr/analysis/NameSuffixTypeMatcherTests.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.structurizr.analysis; - -import org.junit.Test; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -public class NameSuffixTypeMatcherTests { - - @Test(expected = IllegalArgumentException.class) - public void test_construction_ThrowsAnExceptionWhenANullSuffixIsSupplied() - { - new NameSuffixTypeMatcher(null, "", ""); - } - - @Test(expected = IllegalArgumentException.class) - public void test_construction_ThrowsAnExceptionWhenAnEmptyStringSuffixIsSupplied() - { - new NameSuffixTypeMatcher(" ", "", ""); - } - - @Test - public void test_matches_ReturnsFalse_WhenTheNameOfTheGivenTypeDoesNotHaveTheSuffix() - { - NameSuffixTypeMatcher matcher = new NameSuffixTypeMatcher("Component", "", ""); - assertFalse(matcher.matches(MyController.class)); - } - - @Test - public void Ttest_matches_ReturnsTrue_WhenTheNameOfTheGivenTypeDoesHaveTheSuffix() - { - NameSuffixTypeMatcher matcher = new NameSuffixTypeMatcher("Controller", "", ""); - assertTrue(matcher.matches(MyController.class)); - } - - private class MyController { - } - -} \ No newline at end of file diff --git a/structurizr-analysis/test/unit/com/structurizr/analysis/RegexTypeMatcherTests.java b/structurizr-analysis/test/unit/com/structurizr/analysis/RegexTypeMatcherTests.java deleted file mode 100644 index 25cca6946..000000000 --- a/structurizr-analysis/test/unit/com/structurizr/analysis/RegexTypeMatcherTests.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.structurizr.analysis; - -import org.junit.Test; - -import java.util.regex.Pattern; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -public class RegexTypeMatcherTests { - - @Test(expected = IllegalArgumentException.class) - public void test_construction_ThrowsAnExceptionWhenANullRegexAsAStringIsSupplied() - { - new RegexTypeMatcher((String)null, "", ""); - } - - @Test(expected = IllegalArgumentException.class) - public void test_construction_ThrowsAnExceptionWhenANullRegexAsAPatternIsSupplied() - { - new RegexTypeMatcher((Pattern)null, "", ""); - } - - @Test - public void test_matches_ReturnsFalse_WhenTheNameOfTheGivenTypeDoesNotMatchTheRegex() - { - RegexTypeMatcher matcher = new RegexTypeMatcher(Pattern.compile("MyController"), "", ""); - assertFalse(matcher.matches(MyController.class)); - - matcher = new RegexTypeMatcher("MyController", "", ""); - assertFalse(matcher.matches(MyController.class)); - } - - @Test - public void test_matches_ReturnsTrue_WhenTheNameOfTheGivenTypeDoesMatchTheRegex() - { - String regex = ".*\\.analysis\\..*Controller"; - - RegexTypeMatcher matcher = new RegexTypeMatcher(Pattern.compile(regex), "", ""); - assertTrue(matcher.matches(MyController.class)); - - matcher = new RegexTypeMatcher(regex, "", ""); - assertTrue(matcher.matches(MyController.class)); - } - - @Test - public void test_matches_ReturnsFalse_WhenPassedANullType() { - String regex = ".*\\.analysis\\..*Controller"; - - RegexTypeMatcher matcher = new RegexTypeMatcher(Pattern.compile(regex), "", ""); - assertFalse(matcher.matches(null)); - } - - private class MyController { - } - -} \ No newline at end of file diff --git a/structurizr-analysis/test/unit/com/structurizr/analysis/SourceCodeComponentFinderStrategyTests.java b/structurizr-analysis/test/unit/com/structurizr/analysis/SourceCodeComponentFinderStrategyTests.java deleted file mode 100644 index b69f2ebe8..000000000 --- a/structurizr-analysis/test/unit/com/structurizr/analysis/SourceCodeComponentFinderStrategyTests.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.structurizr.analysis; - -import com.structurizr.Workspace; -import com.structurizr.model.Component; -import com.structurizr.model.Container; -import com.structurizr.model.Model; -import com.structurizr.model.SoftwareSystem; -import org.junit.Before; -import org.junit.Test; - -import java.io.File; - -import static org.junit.Assert.assertEquals; - -public class SourceCodeComponentFinderStrategyTests { - - private Container webApplication; - private Component someComponent; - private File sourcePath = new File("test/unit"); - - @Before - public void setUp() { - Workspace workspace = new Workspace("Name", "Description"); - Model model = workspace.getModel(); - - SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); - webApplication = softwareSystem.addContainer("Name", "Description", "Technology"); - - someComponent = webApplication.addComponent( - "SomeComponent", - "test.SourceCodeComponentFinderStrategy.SomeComponent", - "", ""); - someComponent.addSupportingType("test.SourceCodeComponentFinderStrategy.SomeComponentImpl"); - } - - @Test - public void test_findComponents() throws Exception { - ComponentFinder componentFinder = new ComponentFinder( - webApplication, - "test.SourceCodeComponentFinderStrategy", - new SourceCodeComponentFinderStrategy(sourcePath) - ); - componentFinder.findComponents(); - - assertEquals("A component that does something.", someComponent.getDescription()); - assertEquals(20, someComponent.getSize()); - } - - @Test - public void test_findComponents_TruncatesComponentDescriptions_WhenComponentDescriptionsAreTooLong() throws Exception { - ComponentFinder componentFinder = new ComponentFinder( - webApplication, - "test.SourceCodeComponentFinderStrategy", - new SourceCodeComponentFinderStrategy(sourcePath, 10) - ); - componentFinder.findComponents(); - - assertEquals("A compo...", someComponent.getDescription()); - } - - @Test - public void test_findComponents_DoesNotSetTheComponentDescription_WhenTheComponentAlreadyHasADescription() throws Exception { - someComponent.setDescription("An existing description."); - ComponentFinder componentFinder = new ComponentFinder( - webApplication, - "test.SourceCodeComponentFinderStrategy", - new SourceCodeComponentFinderStrategy(sourcePath, 10) - ); - componentFinder.findComponents(); - - assertEquals("An existing description.", someComponent.getDescription()); - } - -} \ No newline at end of file diff --git a/structurizr-analysis/test/unit/com/structurizr/analysis/StructurizrAnnotationsComponentFinderStrategyTests.java b/structurizr-analysis/test/unit/com/structurizr/analysis/StructurizrAnnotationsComponentFinderStrategyTests.java deleted file mode 100644 index 14d61c266..000000000 --- a/structurizr-analysis/test/unit/com/structurizr/analysis/StructurizrAnnotationsComponentFinderStrategyTests.java +++ /dev/null @@ -1,166 +0,0 @@ -package com.structurizr.analysis; - -import com.structurizr.Workspace; -import com.structurizr.model.*; -import org.junit.Before; -import org.junit.Test; - -import static org.junit.Assert.*; - -public class StructurizrAnnotationsComponentFinderStrategyTests { - - private SoftwareSystem external1, external2, softwareSystem; - private Person anonymousUser, authenticatedUser; - private Container webBrowser, apiClient; - private Container webApplication; - private Container database; - private ComponentFinder componentFinder; - - @Before - public void setUp() throws Exception { - Workspace workspace = new Workspace("Name", ""); - Model model = workspace.getModel(); - - external1 = model.addSoftwareSystem("External 1", ""); - external2 = model.addSoftwareSystem("External 2", ""); - anonymousUser = model.addPerson("Anonymous User", ""); - authenticatedUser = model.addPerson("Authenticated User", ""); - softwareSystem = model.addSoftwareSystem("Software System", ""); - webBrowser = softwareSystem.addContainer("Web Browser", "", ""); - apiClient = softwareSystem.addContainer("API Client", "", ""); - webApplication = softwareSystem.addContainer("Name", "", ""); - database = softwareSystem.addContainer("Database", "", ""); - - // the default usage of the StructurizrAnnotationsComponentFinderStrategy - // just has the FirstImplementationOfInterfaceSupportingTypesStrategy included - componentFinder = new ComponentFinder( - webApplication, - "test.StructurizrAnnotationsComponentFinderStrategy", - new StructurizrAnnotationsComponentFinderStrategy() - ); - componentFinder.findComponents(); - } - - @Test - public void test_Component() { - assertEquals(2, webApplication.getComponents().size()); - - Component controller = webApplication.getComponentWithName("Controller"); - assertNotNull(controller); - assertEquals("Controller", controller.getName()); - assertEquals("test.StructurizrAnnotationsComponentFinderStrategy.Controller", controller.getType().getType()); - assertEquals("Does something.", controller.getDescription()); - assertEquals(1, controller.getCode().size()); - assertCodeElementInComponent(controller, "test.StructurizrAnnotationsComponentFinderStrategy.Controller", CodeElementRole.Primary); - - Component repository = webApplication.getComponentWithName("Repository"); - assertNotNull(repository); - assertEquals("Repository", repository.getName()); - assertEquals("test.StructurizrAnnotationsComponentFinderStrategy.Repository", repository.getType().getType()); - assertEquals("Manages some data.", repository.getDescription()); - assertEquals(2, repository.getCode().size()); - assertCodeElementInComponent(repository, "test.StructurizrAnnotationsComponentFinderStrategy.Repository", CodeElementRole.Primary); - assertCodeElementInComponent(repository, "test.StructurizrAnnotationsComponentFinderStrategy.RepositoryImpl", CodeElementRole.Supporting); - } - - private void assertCodeElementInComponent(Component component, String type, CodeElementRole role) { - for (CodeElement codeElement : component.getCode()) { - if (codeElement.getType().equals(type)) { - if (codeElement.getRole() == role) { - return; - } - } - } - - fail(); - } - - @Test - public void test_UsedByPerson() { - Component controller = webApplication.getComponentWithName("Controller"); - - assertEquals(1, anonymousUser.getRelationships().size()); - Relationship relationship = anonymousUser.getRelationships().stream().filter(r -> r.getDestination() == controller).findFirst().get(); - assertEquals("Uses to do something", relationship.getDescription()); - assertEquals("HTTPS", relationship.getTechnology()); - - assertEquals(1, authenticatedUser.getRelationships().size()); - relationship = authenticatedUser.getRelationships().stream().filter(r -> r.getDestination() == controller).findFirst().get(); - assertEquals("Uses to do something too", relationship.getDescription()); - assertEquals("", relationship.getTechnology()); - } - - @Test - public void test_UsedBySoftwareSystem() { - Component controller = webApplication.getComponentWithName("Controller"); - - assertEquals(1, external1.getRelationships().size()); - Relationship relationship = external1.getRelationships().stream().filter(r -> r.getDestination() == controller).findFirst().get(); - assertEquals("Uses to do something", relationship.getDescription()); - assertEquals("HTTPS", relationship.getTechnology()); - - assertEquals(1, external2.getRelationships().size()); - relationship = external2.getRelationships().stream().filter(r -> r.getDestination() == controller).findFirst().get(); - assertEquals("Uses to do something too", relationship.getDescription()); - assertEquals("", relationship.getTechnology()); - } - - @Test - public void test_UsedByContainer() { - Component controller = webApplication.getComponentWithName("Controller"); - - assertEquals(1, webBrowser.getRelationships().size()); - Relationship relationship = webBrowser.getRelationships().stream().filter(r -> r.getDestination() == controller).findFirst().get(); - assertEquals("Makes calls to", relationship.getDescription()); - assertEquals("HTTPS", relationship.getTechnology()); - - assertEquals(1, apiClient.getRelationships().size()); - relationship = apiClient.getRelationships().stream().filter(r -> r.getDestination() == controller).findFirst().get(); - assertEquals("Makes API calls to", relationship.getDescription()); - assertEquals("HTTPS", relationship.getTechnology()); - } - - @Test - public void test_UsesComponent() - { - Component controller = webApplication.getComponentWithName("Controller"); - Component repository = webApplication.getComponentWithName("Repository"); - - Relationship relationship = controller.getRelationships().stream().filter(r -> r.getDestination() == repository).findFirst().get(); - assertEquals("Reads from and writes to", relationship.getDescription()); - assertEquals("Just a method call", relationship.getTechnology()); - } - - @Test - public void test_UsesContainer() - { - Component repository = webApplication.getComponentWithName("Repository"); - - assertEquals(1, repository.getRelationships().size()); - Relationship relationship = repository.getRelationships().stream().filter(r -> r.getDestination() == database).findFirst().get(); - assertEquals("Reads from and writes to", relationship.getDescription()); - assertEquals("JDBC", relationship.getTechnology()); - } - - @Test - public void test_UsesSoftwareSystem() - { - Component controller = webApplication.getComponentWithName("Controller"); - - Relationship relationship = controller.getRelationships().stream().filter(r -> r.getDestination() == external1).findFirst().get(); - assertEquals("Sends information to", relationship.getDescription()); - assertEquals("HTTPS", relationship.getTechnology()); - } - - @Test - public void test_findingDuplicateComponentsThrowsAnException() throws Exception { - try { - componentFinder.findComponents(); - fail(); - } catch (IllegalArgumentException iae) { - assertTrue(iae.getMessage().startsWith("A component named '")); - assertTrue(iae.getMessage().endsWith("' already exists for this container.")); - } - } - -} \ No newline at end of file diff --git a/structurizr-analysis/test/unit/com/structurizr/analysis/TypeMatcherComponentFinderStrategyTests.java b/structurizr-analysis/test/unit/com/structurizr/analysis/TypeMatcherComponentFinderStrategyTests.java deleted file mode 100644 index f939f243b..000000000 --- a/structurizr-analysis/test/unit/com/structurizr/analysis/TypeMatcherComponentFinderStrategyTests.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.structurizr.analysis; - -import com.structurizr.Workspace; -import com.structurizr.model.Component; -import com.structurizr.model.Container; -import com.structurizr.model.Model; -import com.structurizr.model.SoftwareSystem; -import org.junit.Before; -import org.junit.Test; - -import static org.junit.Assert.*; -import static org.junit.Assert.assertTrue; - -public class TypeMatcherComponentFinderStrategyTests { - - private Container container; - private ComponentFinder componentFinder; - - @Before - public void setUp() throws Exception { - Workspace workspace = new Workspace("Name", "Description"); - Model model = workspace.getModel(); - - SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); - container = softwareSystem.addContainer("Name", "Description", "Technology"); - - componentFinder = new ComponentFinder( - container, - "test.TypeMatcherComponentFinderStrategy", - new TypeMatcherComponentFinderStrategy( - new NameSuffixTypeMatcher("Controller", "Controller description", "Controller technology"), - new NameSuffixTypeMatcher("Repository", "Repository description", "Repository technology") - ) - ); - componentFinder.findComponents(); - } - - @Test - public void test_basicUsage() throws Exception { - assertEquals(2, container.getComponents().size()); - - Component myController = container.getComponentWithName("MyController"); - assertNotNull(myController); - assertEquals("MyController", myController.getName()); - assertEquals("test.TypeMatcherComponentFinderStrategy.MyController", myController.getType().getType()); - assertEquals("Controller description", myController.getDescription()); - assertEquals("Controller technology", myController.getTechnology()); - - Component myRepository = container.getComponentWithName("MyRepository"); - assertNotNull(myRepository); - assertEquals("MyRepository", myRepository.getName()); - assertEquals("test.TypeMatcherComponentFinderStrategy.MyRepository", myRepository.getType().getType()); - assertEquals("Repository description", myRepository.getDescription()); - assertEquals("Repository technology", myRepository.getTechnology()); - - assertEquals(1, myController.getRelationships().size()); - assertNotNull(myController.getRelationships().stream().filter(r -> r.getDestination() == myRepository).findFirst().get()); - } - - @Test - public void test_findingDuplicateComponentsThrowsAnException() throws Exception { - try { - componentFinder.findComponents(); - fail(); - } catch (DuplicateComponentException dce) { - assertTrue(dce.getMessage().startsWith("A component named \"")); - assertTrue(dce.getMessage().endsWith("\" already exists in the container named \"Name\".")); - } - } - - -} \ No newline at end of file diff --git a/structurizr-analysis/test/unit/com/structurizr/analysis/TypeUtilsTests.java b/structurizr-analysis/test/unit/com/structurizr/analysis/TypeUtilsTests.java deleted file mode 100644 index 5e892805c..000000000 --- a/structurizr-analysis/test/unit/com/structurizr/analysis/TypeUtilsTests.java +++ /dev/null @@ -1,162 +0,0 @@ -package com.structurizr.analysis; - -import com.structurizr.annotation.Component; -import com.structurizr.annotation.UsedByPerson; -import org.junit.Test; -import test.TypeUtils.AnotherClass; -import test.TypeUtils.SomeInterface; - -import java.util.HashSet; -import java.util.Set; - -import static java.util.Collections.emptyList; -import static java.util.Collections.emptySet; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -public class TypeUtilsTests { - - private static final TypeRepository types = new DefaultTypeRepository(emptyList(), emptySet(), null); - - @Test - public void test_getCategory_ReturnsNull_WhenTheSpecifiedTypeCouldNotBeFound() throws Exception { - assertNull(TypeUtils.getCategory(types, "com.company.app.Class")); - } - - @Test - public void test_getCategory_ReturnsInterface_WhenTheSpecifiedTypeIsAnInterface() throws Exception { - TypeCategory typeCategory = TypeUtils.getCategory(types, "test.TypeUtils.SomeInterface"); - assertSame(TypeCategory.INTERFACE, typeCategory); - } - - @Test - public void test_getCategory_ReturnsAbstractClass_WhenTheSpecifiedTypeIsAnAbstractClass() throws Exception { - TypeCategory typeCategory = TypeUtils.getCategory(types, "test.TypeUtils.SomeAbstractClass"); - assertSame(TypeCategory.ABSTRACT_CLASS, typeCategory); - } - - @Test - public void test_getCategory_ReturnsAbstractClass_WhenTheSpecifiedTypeIsAClass() throws Exception { - TypeCategory typeCategory = TypeUtils.getCategory(types, "test.TypeUtils.SomeClass"); - assertSame(TypeCategory.CLASS, typeCategory); - } - - @Test - public void test_getCategory_ReturnsEnum_WhenTheSpecifiedTypeIsAnEnum() throws Exception { - TypeCategory typeCategory = TypeUtils.getCategory(types, "test.TypeUtils.SomeEnum"); - assertSame(TypeCategory.ENUM, typeCategory); - } - - @Test - public void test_getVisibility_ReturnsNull_WhenTheSpecifiedTypeCouldNotBeFound() throws Exception { - assertNull(TypeUtils.getVisibility(types, "com.company.app.Class")); - } - - @Test - public void test_getVisibility_ReturnsPublic_WhenTheSpecifiedTypeIsPublic() throws Exception { - TypeVisibility typeCategory= TypeUtils.getVisibility(types, "test.TypeUtils.SomeInterface"); - assertSame(TypeVisibility.PUBLIC, typeCategory); - } - - @Test - public void test_getVisibility_ReturnsPackage_WhenTheSpecifiedTypeIsPackageScoped() throws Exception { - TypeVisibility typeCategory= TypeUtils.getVisibility(types, "test.TypeUtils.SomeClass"); - assertSame(TypeVisibility.PACKAGE, typeCategory); - } - - @Test - public void test_findTypesAnnotatedWith_ThrowsAnException_WhenANullAnnotationTypeIsSpecified() { - try { - TypeUtils.findTypesAnnotatedWith(null, new HashSet<>()); - fail(); - } catch (IllegalArgumentException iae) { - iae.printStackTrace(); - assertEquals("An annotation type must be specified.", iae.getMessage()); - } - } - - @Test - public void test_findTypesAnnotatedWith_ReturnsAnEmptySet_WhenNoTypesWithTheSpecifiedAnnotationAreFound() throws Exception { - Set> typesToSearch = new HashSet<>(); - typesToSearch.add(ClassLoader.getSystemClassLoader().loadClass("test.TypeUtils.SomeClass")); - Set> types = TypeUtils.findTypesAnnotatedWith(UsedByPerson.class, typesToSearch); - assertTrue(types.isEmpty()); - } - - @Test - public void test_findTypesAnnotatedWith_ReturnsANonEmptySet_WhenTypesWithTheSpecifiedAnnotationAreFound() throws Exception { - Set> typesToSearch = new HashSet<>(); - typesToSearch.add(ClassLoader.getSystemClassLoader().loadClass("test.TypeUtils.SomeClass")); - Set> types = TypeUtils.findTypesAnnotatedWith(Component.class, typesToSearch); - assertEquals(1, types.size()); - assertEquals("test.TypeUtils.SomeClass", types.iterator().next().getCanonicalName()); - } - - @Test - public void test_getFirstImplementationOfInterface_ThrowsAnException_WhenANullInterfaceIsSpecified() { - try { - TypeUtils.findFirstImplementationOfInterface(null, new HashSet<>()); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("An interface type must be provided.", iae.getMessage()); - } - } - - @Test - public void test_getFirstImplementationOfInterface_ThrowsAnException_WhenANonInterfaceIsSpecified() { - try { - TypeUtils.findFirstImplementationOfInterface(this.getClass(), new HashSet<>()); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("The interface type must represent an interface.", iae.getMessage()); - } - } - - @Test - public void test_getFirstImplementationOfInterface_ThrowsAnException_WhenANullSetOfTypesIsSpecified() { - try { - TypeUtils.findFirstImplementationOfInterface(SomeInterface.class, null); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("The set of types to search through must be provided.", iae.getMessage()); - } - } - - @Test - public void test_getFirstImplementationOfInterface_ReturnsNull_WhenAnImplementationCannotBeFound() { - Set> classes = new HashSet<>(); - classes.add(AnotherClass.class); - Class implementationClass = TypeUtils.findFirstImplementationOfInterface(SomeInterface.class, classes); - assertNull(implementationClass); - } - - @Test - public void test_getFirstImplementationOfInterface_ReturnsNull_WhenOnlyTheInterfaceIsFound() { - Set> classes = new HashSet<>(); - classes.add(SomeInterface.class); - Class implementationClass = TypeUtils.findFirstImplementationOfInterface(SomeInterface.class, classes); - assertNull(implementationClass); - } - - @Test - public void test_getFirstImplementationOfInterface_ReturnsNull_WhenOnlyAnAbstractImplementationIsFound() throws Exception { - Set> classes = new HashSet<>(); - classes.add(Class.forName("test.TypeUtils.SomeAbstractClass")); - Class implementationClass = TypeUtils.findFirstImplementationOfInterface(SomeInterface.class, classes); - assertNull(implementationClass); - } - - @Test - public void test_getFirstImplementationOfInterface_ReturnsAnImplementation_WhenAnConcreteImplementationIsFound() throws Exception { - Set> classes = new HashSet<>(); - classes.add(SomeInterface.class); - classes.add(Class.forName("test.TypeUtils.SomeAbstractClass")); - classes.add(Class.forName("test.TypeUtils.SomeClass")); - Class implementationClass = TypeUtils.findFirstImplementationOfInterface(SomeInterface.class, classes); - assertSame("test.TypeUtils.SomeClass", implementationClass.getCanonicalName()); - } - -} \ No newline at end of file diff --git a/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/AbstractComponentFinderStrategyTests.java b/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/AbstractComponentFinderStrategyTests.java deleted file mode 100644 index 0f9a52106..000000000 --- a/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/AbstractComponentFinderStrategyTests.java +++ /dev/null @@ -1,285 +0,0 @@ -package com.structurizr.analysis.reflections; - -import com.structurizr.Workspace; -import com.structurizr.analysis.*; -import com.structurizr.model.*; -import org.junit.Before; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; - -public class AbstractComponentFinderStrategyTests { - - private Container webApplication; - - @Before - public void setUp() { - Workspace workspace = new Workspace("Name", "Description"); - Model model = workspace.getModel(); - - SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); - webApplication = softwareSystem.addContainer("Name", "Description", "Technology"); - } - - @Test - public void test_findComponents_DoesNotBreak_WhenThereIsACyclicDependency() throws Exception { - ComponentFinder componentFinder = new ComponentFinder( - webApplication, - "com.structurizr.analysis.reflections.cyclicDependency", - new TypeMatcherComponentFinderStrategy( - new NameSuffixTypeMatcher("Component", "", "") - ) - ); - componentFinder.findComponents(); - - assertEquals(2, webApplication.getComponents().size()); - - Component aComponent = webApplication.getComponentWithName("AComponent"); - assertNotNull(aComponent); - assertEquals("AComponent", aComponent.getName()); - assertEquals("com.structurizr.analysis.reflections.cyclicDependency.AComponent", aComponent.getType().getType()); - - Component bComponent = webApplication.getComponentWithName("BComponent"); - assertNotNull(bComponent); - assertEquals("BComponent", bComponent.getName()); - assertEquals("com.structurizr.analysis.reflections.cyclicDependency.BComponent", bComponent.getType().getType()); - - assertEquals(1, aComponent.getRelationships().size()); - assertNotNull(aComponent.getRelationships().stream().filter(r -> r.getDestination() == bComponent).findFirst().get()); - - assertEquals(1, bComponent.getRelationships().size()); - assertNotNull(bComponent.getRelationships().stream().filter(r -> r.getDestination() == aComponent).findFirst().get()); - } - - @Test - public void test_findComponents_CorrectlyFindsDependenciesFromSuperclass() throws Exception { - ComponentFinder componentFinder = new ComponentFinder( - webApplication, - "com.structurizr.analysis.reflections.dependenciesFromSuperClass", - new TypeMatcherComponentFinderStrategy( - new NameSuffixTypeMatcher("Component", "", "") - ) - ); - componentFinder.findComponents(); - - assertEquals(2, webApplication.getComponents().size()); - - Component someComponent = webApplication.getComponentWithName("SomeComponent"); - assertNotNull(someComponent); - assertEquals("SomeComponent", someComponent.getName()); - assertEquals("com.structurizr.analysis.reflections.dependenciesFromSuperClass.SomeComponent", someComponent.getType().getType()); - - Component loggingComponent = webApplication.getComponentWithName("LoggingComponent"); - assertNotNull(loggingComponent); - assertEquals("LoggingComponent", loggingComponent.getName()); - assertEquals("com.structurizr.analysis.reflections.dependenciesFromSuperClass.LoggingComponent", loggingComponent.getType().getType()); - - assertEquals(1, someComponent.getRelationships().size()); - assertNotNull(someComponent.getRelationships().stream().filter(r -> r.getDestination() == loggingComponent).findFirst().get()); - } - - @Test - public void test_findComponents_CorrectlyFindsNoDependenciesWhenTwoComponentsImplementTheSameInterface() throws Exception { - ComponentFinder componentFinder = new ComponentFinder( - webApplication, - "com.structurizr.analysis.reflections.featureinterface", - new TypeMatcherComponentFinderStrategy( - new NameSuffixTypeMatcher("Component", "", "") - ) - ); - componentFinder.findComponents(); - - assertEquals(2, webApplication.getComponents().size()); - - Component someComponent = webApplication.getComponentWithName("SomeComponent"); - assertNotNull(someComponent); - assertEquals("SomeComponent", someComponent.getName()); - assertEquals("com.structurizr.analysis.reflections.featureinterface.SomeComponent", someComponent.getType().getType()); - - Component otherComponent = webApplication.getComponentWithName("OtherComponent"); - assertNotNull(otherComponent); - assertEquals("OtherComponent", otherComponent.getName()); - assertEquals("com.structurizr.analysis.reflections.featureinterface.OtherComponent", otherComponent.getType().getType()); - - assertEquals(0, someComponent.getRelationships().size()); - assertEquals(0, otherComponent.getRelationships().size()); - } - - @Test - public void test_findComponents_CorrectlyFindsDependenciesBetweenComponentsFoundByDifferentComponentFinders_WhenPackage1IsScannedFirst() throws Exception { - ComponentFinder componentFinder1 = new ComponentFinder( - webApplication, - "com.structurizr.analysis.reflections.multipleComponentFinders.package1", - new TypeMatcherComponentFinderStrategy( - new NameSuffixTypeMatcher("Controller", "", "") - ) - ); - - ComponentFinder componentFinder2 = new ComponentFinder( - webApplication, - "com.structurizr.analysis.reflections.multipleComponentFinders.package2", - new TypeMatcherComponentFinderStrategy( - new NameSuffixTypeMatcher("Repository", "", "") - ) - ); - - componentFinder1.findComponents(); - componentFinder2.findComponents(); - - assertEquals(2, webApplication.getComponents().size()); - Component myController = webApplication.getComponentWithName("MyController"); - Component myRepository = webApplication.getComponentWithName("MyRepository"); - assertEquals(1, myController.getRelationships().size()); - assertNotNull(myController.getRelationships().stream().filter(r -> r.getDestination() == myRepository).findFirst().get()); - } - - @Test - public void test_findComponents_CorrectlyFindsDependenciesBetweenComponentsFoundByDifferentComponentFinders_WhenPackage2IsScannedFirst() throws Exception { - ComponentFinder componentFinder1 = new ComponentFinder( - webApplication, - "com.structurizr.analysis.reflections.multipleComponentFinders.package1", - new TypeMatcherComponentFinderStrategy( - new NameSuffixTypeMatcher("Controller", "", "") - ) - ); - - ComponentFinder componentFinder2 = new ComponentFinder( - webApplication, - "com.structurizr.analysis.reflections.multipleComponentFinders.package2", - new TypeMatcherComponentFinderStrategy( - new NameSuffixTypeMatcher("Repository", "", "") - ) - ); - - componentFinder2.findComponents(); - componentFinder1.findComponents(); - - assertEquals(2, webApplication.getComponents().size()); - Component myController = webApplication.getComponentWithName("MyController"); - Component myRepository = webApplication.getComponentWithName("MyRepository"); - assertEquals(1, myController.getRelationships().size()); - assertNotNull(myController.getRelationships().stream().filter(r -> r.getDestination() == myRepository).findFirst().get()); - } - - @Test - public void test_findComponents_CorrectlyFindsSupportingTypes_WhenTheDefaultStrategyIsUsed() throws Exception { - ComponentFinder componentFinder = new ComponentFinder( - webApplication, - "com.structurizr.analysis.reflections.supportingTypes.myapp", - new StructurizrAnnotationsComponentFinderStrategy() - ); - componentFinder.findComponents(); - - assertEquals(2, webApplication.getComponents().size()); - Component myController = webApplication.getComponentWithName("MyController"); - Component myRepository = webApplication.getComponentWithName("MyRepository"); - assertEquals(1, myController.getRelationships().size()); - assertNotNull(myController.getRelationships().stream().filter(r -> r.getDestination() == myRepository).findFirst().get()); - - // the default strategy for supporting types is to find the first implementation - // class if the component type is an interface - assertEquals(1, myController.getCode().size()); - assertCodeElementInComponent(myController, "com.structurizr.analysis.reflections.supportingTypes.myapp.web.MyController", CodeElementRole.Primary); - - assertEquals(2, myRepository.getCode().size()); - assertCodeElementInComponent(myRepository, "com.structurizr.analysis.reflections.supportingTypes.myapp.data.MyRepository", CodeElementRole.Primary); - assertCodeElementInComponent(myRepository, "com.structurizr.analysis.reflections.supportingTypes.myapp.data.MyRepositoryImpl", CodeElementRole.Supporting); - } - - @Test - public void test_findComponents_CorrectlyFindsSupportingTypes_WhenTheReferencedTypesInSamePackageSupportingTypesStrategyIsUsed() throws Exception { - ComponentFinder componentFinder = new ComponentFinder( - webApplication, - "com.structurizr.analysis.reflections.supportingTypes.myapp", - new StructurizrAnnotationsComponentFinderStrategy( - new FirstImplementationOfInterfaceSupportingTypesStrategy(), - new ReferencedTypesInSamePackageSupportingTypesStrategy() - ) - ); - componentFinder.findComponents(); - - assertEquals(2, webApplication.getComponents().size()); - Component myController = webApplication.getComponentWithName("MyController"); - Component myRepository = webApplication.getComponentWithName("MyRepository"); - assertEquals(1, myController.getRelationships().size()); - assertNotNull(myController.getRelationships().stream().filter(r -> r.getDestination() == myRepository).findFirst().get()); - - assertEquals(1, myController.getCode().size()); - assertEquals(3, myRepository.getCode().size()); - assertCodeElementInComponent(myRepository, "com.structurizr.analysis.reflections.supportingTypes.myapp.data.MyRepository", CodeElementRole.Primary); - assertCodeElementInComponent(myRepository, "com.structurizr.analysis.reflections.supportingTypes.myapp.data.MyRepositoryImpl", CodeElementRole.Supporting); - assertCodeElementInComponent(myRepository, "com.structurizr.analysis.reflections.supportingTypes.myapp.data.MyRepositoryRowMapper", CodeElementRole.Supporting); - } - - @Test - public void test_findComponents_CorrectlyFindsSupportingTypes_WhenTheReferencedTypesStrategyIsUsedAndIndirectlyReferencedTypesShouldBeIncluded() throws Exception { - ComponentFinder componentFinder = new ComponentFinder( - webApplication, - "com.structurizr.analysis.reflections.supportingTypes.myapp", - new StructurizrAnnotationsComponentFinderStrategy( - new FirstImplementationOfInterfaceSupportingTypesStrategy(), - new ReferencedTypesSupportingTypesStrategy() - ) - ); - componentFinder.findComponents(); - - assertEquals(2, webApplication.getComponents().size()); - Component myController = webApplication.getComponentWithName("MyController"); - Component myRepository = webApplication.getComponentWithName("MyRepository"); - assertEquals(1, myController.getRelationships().size()); - assertNotNull(myController.getRelationships().stream().filter(r -> r.getDestination() == myRepository).findFirst().get()); - - assertEquals(2, myController.getCode().size()); - assertCodeElementInComponent(myController, "com.structurizr.analysis.reflections.supportingTypes.myapp.web.MyController", CodeElementRole.Primary); - assertCodeElementInComponent(myController, "com.structurizr.analysis.reflections.supportingTypes.myapp.AbstractComponent", CodeElementRole.Supporting); - - assertEquals(5, myRepository.getCode().size()); - assertCodeElementInComponent(myRepository, "com.structurizr.analysis.reflections.supportingTypes.myapp.data.MyRepository", CodeElementRole.Primary); - assertCodeElementInComponent(myRepository, "com.structurizr.analysis.reflections.supportingTypes.myapp.AbstractComponent", CodeElementRole.Supporting); - assertCodeElementInComponent(myRepository, "com.structurizr.analysis.reflections.supportingTypes.myapp.data.MyRepositoryImpl", CodeElementRole.Supporting); - assertCodeElementInComponent(myRepository, "com.structurizr.analysis.reflections.supportingTypes.myapp.data.MyRepositoryRowMapper", CodeElementRole.Supporting); - assertCodeElementInComponent(myRepository, "com.structurizr.analysis.reflections.supportingTypes.myapp.util.RowMapperHelper", CodeElementRole.Supporting); - } - - @Test - public void test_findComponents_CorrectlyFindsSupportingTypes_WhenTheReferencedTypesStrategyIsUsedAndIndirectlyReferencedTypesShouldBeExcluded() throws Exception { - ComponentFinder componentFinder = new ComponentFinder( - webApplication, - "com.structurizr.analysis.reflections.supportingTypes.myapp", - new StructurizrAnnotationsComponentFinderStrategy( - new FirstImplementationOfInterfaceSupportingTypesStrategy(), - new ReferencedTypesSupportingTypesStrategy(false) - ) - ); - componentFinder.findComponents(); - - assertEquals(2, webApplication.getComponents().size()); - Component myController = webApplication.getComponentWithName("MyController"); - Component myRepository = webApplication.getComponentWithName("MyRepository"); - assertEquals(1, myController.getRelationships().size()); - assertNotNull(myController.getRelationships().stream().filter(r -> r.getDestination() == myRepository).findFirst().get()); - - assertEquals(2, myController.getCode().size()); - assertCodeElementInComponent(myController, "com.structurizr.analysis.reflections.supportingTypes.myapp.web.MyController", CodeElementRole.Primary); - assertCodeElementInComponent(myController, "com.structurizr.analysis.reflections.supportingTypes.myapp.AbstractComponent", CodeElementRole.Supporting); - - assertEquals(4, myRepository.getCode().size()); - assertCodeElementInComponent(myRepository, "com.structurizr.analysis.reflections.supportingTypes.myapp.data.MyRepository", CodeElementRole.Primary); - assertCodeElementInComponent(myRepository, "com.structurizr.analysis.reflections.supportingTypes.myapp.AbstractComponent", CodeElementRole.Supporting); - assertCodeElementInComponent(myRepository, "com.structurizr.analysis.reflections.supportingTypes.myapp.data.MyRepositoryImpl", CodeElementRole.Supporting); - assertCodeElementInComponent(myRepository, "com.structurizr.analysis.reflections.supportingTypes.myapp.data.MyRepositoryRowMapper", CodeElementRole.Supporting); - } - - private void assertCodeElementInComponent(Component component, String type, CodeElementRole role) { - for (CodeElement codeElement : component.getCode()) { - if (codeElement.getType().equals(type) && codeElement.getRole() == role) { - return; - } - } - - fail("Component " + component.getName() + " does not have a " + role + " code element of type " + type); - } - -} diff --git a/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/cyclicDependency/AComponent.java b/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/cyclicDependency/AComponent.java deleted file mode 100644 index 2ffb6ddfc..000000000 --- a/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/cyclicDependency/AComponent.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.structurizr.analysis.reflections.cyclicDependency; - -public class AComponent { - - private BComponent bComponent = new BComponent(); - -} diff --git a/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/cyclicDependency/BComponent.java b/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/cyclicDependency/BComponent.java deleted file mode 100644 index e5f2e4203..000000000 --- a/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/cyclicDependency/BComponent.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.structurizr.analysis.reflections.cyclicDependency; - -public class BComponent { - - private AComponent aComponent = new AComponent(); - -} diff --git a/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/dependenciesFromSuperClass/ComponentBase.java b/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/dependenciesFromSuperClass/ComponentBase.java deleted file mode 100644 index 6ea109ea1..000000000 --- a/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/dependenciesFromSuperClass/ComponentBase.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.structurizr.analysis.reflections.dependenciesFromSuperClass; - -public abstract class ComponentBase { - - private LoggingComponent loggingComponent = new LoggingComponent(); - -} diff --git a/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/dependenciesFromSuperClass/LoggingComponent.java b/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/dependenciesFromSuperClass/LoggingComponent.java deleted file mode 100644 index 73b0fe07f..000000000 --- a/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/dependenciesFromSuperClass/LoggingComponent.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.structurizr.analysis.reflections.dependenciesFromSuperClass; - -public class LoggingComponent { -} diff --git a/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/dependenciesFromSuperClass/SomeComponent.java b/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/dependenciesFromSuperClass/SomeComponent.java deleted file mode 100644 index 215a7507b..000000000 --- a/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/dependenciesFromSuperClass/SomeComponent.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.structurizr.analysis.reflections.dependenciesFromSuperClass; - -public class SomeComponent extends ComponentBase { -} diff --git a/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/featureinterface/FeatureInterface.java b/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/featureinterface/FeatureInterface.java deleted file mode 100644 index 7268297d7..000000000 --- a/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/featureinterface/FeatureInterface.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.structurizr.analysis.reflections.featureinterface; - -public interface FeatureInterface { -} diff --git a/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/featureinterface/OtherComponent.java b/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/featureinterface/OtherComponent.java deleted file mode 100644 index 74d366918..000000000 --- a/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/featureinterface/OtherComponent.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.structurizr.analysis.reflections.featureinterface; - -public class OtherComponent implements FeatureInterface { -} diff --git a/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/featureinterface/SomeComponent.java b/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/featureinterface/SomeComponent.java deleted file mode 100644 index 06b9a6ebf..000000000 --- a/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/featureinterface/SomeComponent.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.structurizr.analysis.reflections.featureinterface; - -public class SomeComponent implements FeatureInterface { -} diff --git a/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/multipleComponentFinders/package1/MyController.java b/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/multipleComponentFinders/package1/MyController.java deleted file mode 100644 index c5bf1591f..000000000 --- a/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/multipleComponentFinders/package1/MyController.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.structurizr.analysis.reflections.multipleComponentFinders.package1; - -import com.structurizr.analysis.reflections.multipleComponentFinders.package2.MyRepository; - -public class MyController { - - private MyRepository myRepository; - -} diff --git a/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/multipleComponentFinders/package2/MyRepository.java b/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/multipleComponentFinders/package2/MyRepository.java deleted file mode 100644 index 0bd641e00..000000000 --- a/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/multipleComponentFinders/package2/MyRepository.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.structurizr.analysis.reflections.multipleComponentFinders.package2; - -public interface MyRepository { -} diff --git a/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/supportingTypes/myapp/AbstractComponent.java b/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/supportingTypes/myapp/AbstractComponent.java deleted file mode 100644 index fd4633e7e..000000000 --- a/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/supportingTypes/myapp/AbstractComponent.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.structurizr.analysis.reflections.supportingTypes.myapp; - -public abstract class AbstractComponent { -} diff --git a/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/supportingTypes/myapp/data/MyRepository.java b/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/supportingTypes/myapp/data/MyRepository.java deleted file mode 100644 index 5f0b0cf90..000000000 --- a/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/supportingTypes/myapp/data/MyRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.structurizr.analysis.reflections.supportingTypes.myapp.data; - -import com.structurizr.annotation.Component; - -@Component -public interface MyRepository { -} diff --git a/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/supportingTypes/myapp/data/MyRepositoryImpl.java b/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/supportingTypes/myapp/data/MyRepositoryImpl.java deleted file mode 100644 index f37daa89d..000000000 --- a/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/supportingTypes/myapp/data/MyRepositoryImpl.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.structurizr.analysis.reflections.supportingTypes.myapp.data; - -import com.structurizr.analysis.reflections.supportingTypes.myapp.AbstractComponent; - -class MyRepositoryImpl extends AbstractComponent implements MyRepository { - - private MyRepositoryRowMapper rowMapper; - -} diff --git a/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/supportingTypes/myapp/data/MyRepositoryRowMapper.java b/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/supportingTypes/myapp/data/MyRepositoryRowMapper.java deleted file mode 100644 index 235e2e5a2..000000000 --- a/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/supportingTypes/myapp/data/MyRepositoryRowMapper.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.structurizr.analysis.reflections.supportingTypes.myapp.data; - -import com.structurizr.analysis.reflections.supportingTypes.myapp.util.RowMapperHelper; - -class MyRepositoryRowMapper { - - private RowMapperHelper helper; - -} diff --git a/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/supportingTypes/myapp/util/RowMapperHelper.java b/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/supportingTypes/myapp/util/RowMapperHelper.java deleted file mode 100644 index 23ffbbc60..000000000 --- a/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/supportingTypes/myapp/util/RowMapperHelper.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.structurizr.analysis.reflections.supportingTypes.myapp.util; - -public class RowMapperHelper { -} diff --git a/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/supportingTypes/myapp/web/MyController.java b/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/supportingTypes/myapp/web/MyController.java deleted file mode 100644 index bfa706a20..000000000 --- a/structurizr-analysis/test/unit/com/structurizr/analysis/reflections/supportingTypes/myapp/web/MyController.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.structurizr.analysis.reflections.supportingTypes.myapp.web; - -import com.structurizr.analysis.reflections.supportingTypes.myapp.AbstractComponent; -import com.structurizr.analysis.reflections.supportingTypes.myapp.data.MyRepository; -import com.structurizr.annotation.Component; - -@Component -class MyController extends AbstractComponent { - - private MyRepository myRepository; - -} diff --git a/structurizr-analysis/test/unit/test/DefaultTypeRepository/SomeAbstractClass.java b/structurizr-analysis/test/unit/test/DefaultTypeRepository/SomeAbstractClass.java deleted file mode 100644 index 91f01fc4f..000000000 --- a/structurizr-analysis/test/unit/test/DefaultTypeRepository/SomeAbstractClass.java +++ /dev/null @@ -1,4 +0,0 @@ -package test.DefaultTypeRepository; - -abstract class SomeAbstractClass implements SomeInterface { -} diff --git a/structurizr-analysis/test/unit/test/DefaultTypeRepository/SomeClass.java b/structurizr-analysis/test/unit/test/DefaultTypeRepository/SomeClass.java deleted file mode 100644 index c0e3fbe05..000000000 --- a/structurizr-analysis/test/unit/test/DefaultTypeRepository/SomeClass.java +++ /dev/null @@ -1,10 +0,0 @@ -package test.DefaultTypeRepository; - -import com.structurizr.annotation.Component; - -@Component -class SomeClass extends SomeAbstractClass { - - private SomeEnum someEnum; - -} diff --git a/structurizr-analysis/test/unit/test/DefaultTypeRepository/SomeEnum.java b/structurizr-analysis/test/unit/test/DefaultTypeRepository/SomeEnum.java deleted file mode 100644 index ddb081dd1..000000000 --- a/structurizr-analysis/test/unit/test/DefaultTypeRepository/SomeEnum.java +++ /dev/null @@ -1,4 +0,0 @@ -package test.DefaultTypeRepository; - -public enum SomeEnum { -} diff --git a/structurizr-analysis/test/unit/test/DefaultTypeRepository/SomeInterface.java b/structurizr-analysis/test/unit/test/DefaultTypeRepository/SomeInterface.java deleted file mode 100644 index 46651dfe3..000000000 --- a/structurizr-analysis/test/unit/test/DefaultTypeRepository/SomeInterface.java +++ /dev/null @@ -1,4 +0,0 @@ -package test.DefaultTypeRepository; - -public interface SomeInterface { -} diff --git a/structurizr-analysis/test/unit/test/MoreDefaultTypeRepository/AnotherClass.java b/structurizr-analysis/test/unit/test/MoreDefaultTypeRepository/AnotherClass.java deleted file mode 100644 index 05bb1c66a..000000000 --- a/structurizr-analysis/test/unit/test/MoreDefaultTypeRepository/AnotherClass.java +++ /dev/null @@ -1,4 +0,0 @@ -package test.MoreDefaultTypeRepository; - -public class AnotherClass { -} diff --git a/structurizr-analysis/test/unit/test/SourceCodeComponentFinderStrategy/SomeComponent.java b/structurizr-analysis/test/unit/test/SourceCodeComponentFinderStrategy/SomeComponent.java deleted file mode 100644 index 72d727f81..000000000 --- a/structurizr-analysis/test/unit/test/SourceCodeComponentFinderStrategy/SomeComponent.java +++ /dev/null @@ -1,10 +0,0 @@ -package test.SourceCodeComponentFinderStrategy; - -/** - * A component that does something. - */ -public interface SomeComponent { - - void doSomething(); - -} \ No newline at end of file diff --git a/structurizr-analysis/test/unit/test/SourceCodeComponentFinderStrategy/SomeComponentImpl.java b/structurizr-analysis/test/unit/test/SourceCodeComponentFinderStrategy/SomeComponentImpl.java deleted file mode 100644 index 764a78133..000000000 --- a/structurizr-analysis/test/unit/test/SourceCodeComponentFinderStrategy/SomeComponentImpl.java +++ /dev/null @@ -1,10 +0,0 @@ -package test.SourceCodeComponentFinderStrategy; - -class SomeComponentImpl implements SomeComponent { - - @Override - public void doSomething() { - System.out.println("Doing something"); - } - -} \ No newline at end of file diff --git a/structurizr-analysis/test/unit/test/StructurizrAnnotationsComponentFinderStrategy/Controller.java b/structurizr-analysis/test/unit/test/StructurizrAnnotationsComponentFinderStrategy/Controller.java deleted file mode 100644 index ae153d3f5..000000000 --- a/structurizr-analysis/test/unit/test/StructurizrAnnotationsComponentFinderStrategy/Controller.java +++ /dev/null @@ -1,18 +0,0 @@ -package test.StructurizrAnnotationsComponentFinderStrategy; - -import com.structurizr.annotation.*; - -@Component(description = "Does something.") -@UsedByPerson(name = "Anonymous User", description = "Uses to do something", technology = "HTTPS") -@UsedByPerson(name = "Authenticated User", description = "Uses to do something too") -@UsedBySoftwareSystem(name = "External 1", description = "Uses to do something", technology = "HTTPS") -@UsedBySoftwareSystem(name = "External 2", description = "Uses to do something too") -@UsedByContainer(name = "Software System/Web Browser", description = "Makes calls to", technology = "HTTPS") // an example of using a canonical name -@UsedByContainer(name = "API Client", description = "Makes API calls to", technology = "HTTPS") // an example of not using a canonical name -@UsesSoftwareSystem(name = "External 1", description = "Sends information to", technology = "HTTPS") -public class Controller { - - @UsesComponent(description = "Reads from and writes to", technology = "Just a method call") - protected Repository repository; - -} diff --git a/structurizr-analysis/test/unit/test/StructurizrAnnotationsComponentFinderStrategy/Repository.java b/structurizr-analysis/test/unit/test/StructurizrAnnotationsComponentFinderStrategy/Repository.java deleted file mode 100644 index ac90151a5..000000000 --- a/structurizr-analysis/test/unit/test/StructurizrAnnotationsComponentFinderStrategy/Repository.java +++ /dev/null @@ -1,10 +0,0 @@ -package test.StructurizrAnnotationsComponentFinderStrategy; - -import com.structurizr.annotation.Component; - -@Component(description = "Manages some data.") -public interface Repository { - - void getData(); - -} \ No newline at end of file diff --git a/structurizr-analysis/test/unit/test/StructurizrAnnotationsComponentFinderStrategy/RepositoryImpl.java b/structurizr-analysis/test/unit/test/StructurizrAnnotationsComponentFinderStrategy/RepositoryImpl.java deleted file mode 100644 index 92bfce223..000000000 --- a/structurizr-analysis/test/unit/test/StructurizrAnnotationsComponentFinderStrategy/RepositoryImpl.java +++ /dev/null @@ -1,12 +0,0 @@ -package test.StructurizrAnnotationsComponentFinderStrategy; - -import com.structurizr.annotation.UsesContainer; - -@UsesContainer(name = "Database", description = "Reads from and writes to", technology = "JDBC") -class RepositoryImpl implements Repository { - - @Override - public void getData() { - } - -} \ No newline at end of file diff --git a/structurizr-analysis/test/unit/test/TypeMatcherComponentFinderStrategy/MyController.java b/structurizr-analysis/test/unit/test/TypeMatcherComponentFinderStrategy/MyController.java deleted file mode 100644 index 8a9ce1ae6..000000000 --- a/structurizr-analysis/test/unit/test/TypeMatcherComponentFinderStrategy/MyController.java +++ /dev/null @@ -1,7 +0,0 @@ -package test.TypeMatcherComponentFinderStrategy; - -public class MyController { - - private MyRepository myRepository = new MyRepositoryImpl(); - -} diff --git a/structurizr-analysis/test/unit/test/TypeMatcherComponentFinderStrategy/MyRepository.java b/structurizr-analysis/test/unit/test/TypeMatcherComponentFinderStrategy/MyRepository.java deleted file mode 100644 index f3a29c5b1..000000000 --- a/structurizr-analysis/test/unit/test/TypeMatcherComponentFinderStrategy/MyRepository.java +++ /dev/null @@ -1,4 +0,0 @@ -package test.TypeMatcherComponentFinderStrategy; - -public interface MyRepository { -} diff --git a/structurizr-analysis/test/unit/test/TypeMatcherComponentFinderStrategy/MyRepositoryImpl.java b/structurizr-analysis/test/unit/test/TypeMatcherComponentFinderStrategy/MyRepositoryImpl.java deleted file mode 100644 index 05530156a..000000000 --- a/structurizr-analysis/test/unit/test/TypeMatcherComponentFinderStrategy/MyRepositoryImpl.java +++ /dev/null @@ -1,15 +0,0 @@ -package test.TypeMatcherComponentFinderStrategy; - -public class MyRepositoryImpl implements MyRepository { - - public void doSomething() { - // make a reference to an inner class - throw new RuntimeException() { - @Override - public String getMessage() { - return "I'm an inner class."; - } - }; - } - -} \ No newline at end of file diff --git a/structurizr-analysis/test/unit/test/TypeUtils/AnotherClass.java b/structurizr-analysis/test/unit/test/TypeUtils/AnotherClass.java deleted file mode 100644 index 5df64fe1a..000000000 --- a/structurizr-analysis/test/unit/test/TypeUtils/AnotherClass.java +++ /dev/null @@ -1,4 +0,0 @@ -package test.TypeUtils; - -public class AnotherClass { -} diff --git a/structurizr-analysis/test/unit/test/TypeUtils/SomeAbstractClass.java b/structurizr-analysis/test/unit/test/TypeUtils/SomeAbstractClass.java deleted file mode 100644 index 83ae95523..000000000 --- a/structurizr-analysis/test/unit/test/TypeUtils/SomeAbstractClass.java +++ /dev/null @@ -1,4 +0,0 @@ -package test.TypeUtils; - -abstract class SomeAbstractClass implements SomeInterface { -} diff --git a/structurizr-analysis/test/unit/test/TypeUtils/SomeClass.java b/structurizr-analysis/test/unit/test/TypeUtils/SomeClass.java deleted file mode 100644 index 4344ba4fd..000000000 --- a/structurizr-analysis/test/unit/test/TypeUtils/SomeClass.java +++ /dev/null @@ -1,10 +0,0 @@ -package test.TypeUtils; - -import com.structurizr.annotation.Component; - -@Component -class SomeClass extends SomeAbstractClass { - - private SomeEnum someEnum; - -} diff --git a/structurizr-analysis/test/unit/test/TypeUtils/SomeEnum.java b/structurizr-analysis/test/unit/test/TypeUtils/SomeEnum.java deleted file mode 100644 index 00b590946..000000000 --- a/structurizr-analysis/test/unit/test/TypeUtils/SomeEnum.java +++ /dev/null @@ -1,4 +0,0 @@ -package test.TypeUtils; - -public enum SomeEnum { -} diff --git a/structurizr-analysis/test/unit/test/TypeUtils/SomeInterface.java b/structurizr-analysis/test/unit/test/TypeUtils/SomeInterface.java deleted file mode 100644 index 834aaea50..000000000 --- a/structurizr-analysis/test/unit/test/TypeUtils/SomeInterface.java +++ /dev/null @@ -1,4 +0,0 @@ -package test.TypeUtils; - -public interface SomeInterface { -} diff --git a/structurizr-annotations/build.gradle b/structurizr-annotations/build.gradle deleted file mode 100644 index cede5329a..000000000 --- a/structurizr-annotations/build.gradle +++ /dev/null @@ -1,11 +0,0 @@ -jar { - manifest { - attributes( - 'Bundle-ManifestVersion': "2", - 'Bundle-Name': "Structurizr for Java", - 'Bundle-SymbolicName': "com.structurizr.annotations", - 'Bundle-Version': version, - 'Export-Package': "com.structurizr.annotation;version=\"$version\"" - ) - } -} \ No newline at end of file diff --git a/structurizr-annotations/src/com/structurizr/annotation/Component.java b/structurizr-annotations/src/com/structurizr/annotation/Component.java deleted file mode 100644 index c53c86ff7..000000000 --- a/structurizr-annotations/src/com/structurizr/annotation/Component.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.structurizr.annotation; - -import java.lang.annotation.*; - -/** - * A type-level annotation that can be used to signify that the annotated type - * (an interface or class) can be considered to be a "component". - */ -@Documented -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -public @interface Component { - - String description() default ""; - String technology() default ""; - -} diff --git a/structurizr-annotations/src/com/structurizr/annotation/UsedByContainer.java b/structurizr-annotations/src/com/structurizr/annotation/UsedByContainer.java deleted file mode 100644 index 2af4686bb..000000000 --- a/structurizr-annotations/src/com/structurizr/annotation/UsedByContainer.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.structurizr.annotation; - -import java.lang.annotation.*; - -/** - * A type-level annotation that can be used to signify that the named - * container uses the component on which this annotation is placed, - * creating a relationship from the container to the component. - */ -@Documented -@Target({ElementType.TYPE}) -@Retention(RetentionPolicy.RUNTIME) -@Repeatable(UsedByContainers.class) -public @interface UsedByContainer { - - String name(); - String description() default ""; - String technology() default ""; - -} diff --git a/structurizr-annotations/src/com/structurizr/annotation/UsedByContainers.java b/structurizr-annotations/src/com/structurizr/annotation/UsedByContainers.java deleted file mode 100644 index 3d580b27d..000000000 --- a/structurizr-annotations/src/com/structurizr/annotation/UsedByContainers.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.structurizr.annotation; - -import java.lang.annotation.*; - -/** - * A wrapper for multiple @UsedByContainer annotations. - */ -@Documented -@Target({ElementType.TYPE}) -@Retention(RetentionPolicy.RUNTIME) -public @interface UsedByContainers { - - UsedByContainer[] value(); - -} diff --git a/structurizr-annotations/src/com/structurizr/annotation/UsedByPeople.java b/structurizr-annotations/src/com/structurizr/annotation/UsedByPeople.java deleted file mode 100644 index 87138f3ac..000000000 --- a/structurizr-annotations/src/com/structurizr/annotation/UsedByPeople.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.structurizr.annotation; - -import java.lang.annotation.*; - -/** - * A wrapper for multiple @UsedByPerson annotations. - */ -@Documented -@Target({ElementType.TYPE}) -@Retention(RetentionPolicy.RUNTIME) -public @interface UsedByPeople { - - UsedByPerson[] value(); - -} diff --git a/structurizr-annotations/src/com/structurizr/annotation/UsedByPerson.java b/structurizr-annotations/src/com/structurizr/annotation/UsedByPerson.java deleted file mode 100644 index 551625a34..000000000 --- a/structurizr-annotations/src/com/structurizr/annotation/UsedByPerson.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.structurizr.annotation; - -import java.lang.annotation.*; - -/** - * A type-level annotation that can be used to signify that the named - * person uses the component on which this annotation is placed, - * creating a relationship from the person to the component. - */ -@Documented -@Target({ElementType.TYPE}) -@Retention(RetentionPolicy.RUNTIME) -@Repeatable(UsedByPeople.class) -public @interface UsedByPerson { - - String name(); - String description() default ""; - String technology() default ""; - -} diff --git a/structurizr-annotations/src/com/structurizr/annotation/UsedBySoftwareSystem.java b/structurizr-annotations/src/com/structurizr/annotation/UsedBySoftwareSystem.java deleted file mode 100644 index 464e7b811..000000000 --- a/structurizr-annotations/src/com/structurizr/annotation/UsedBySoftwareSystem.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.structurizr.annotation; - -import java.lang.annotation.*; - -/** - * A type-level annotation that can be used to signify that the named - * software system uses the component on which this annotation is placed, - * creating a relationship from the software system to the component. - */ -@Documented -@Target({ElementType.TYPE}) -@Retention(RetentionPolicy.RUNTIME) -@Repeatable(UsedBySoftwareSystems.class) -public @interface UsedBySoftwareSystem { - - String name(); - String description() default ""; - String technology() default ""; - -} diff --git a/structurizr-annotations/src/com/structurizr/annotation/UsedBySoftwareSystems.java b/structurizr-annotations/src/com/structurizr/annotation/UsedBySoftwareSystems.java deleted file mode 100644 index 137f31b41..000000000 --- a/structurizr-annotations/src/com/structurizr/annotation/UsedBySoftwareSystems.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.structurizr.annotation; - -import java.lang.annotation.*; - -/** - * A wrapper for multiple @UsedBySoftwareSystem annotations. - */ -@Documented -@Target({ElementType.TYPE}) -@Retention(RetentionPolicy.RUNTIME) -public @interface UsedBySoftwareSystems { - - UsedBySoftwareSystem[] value(); - -} diff --git a/structurizr-annotations/src/com/structurizr/annotation/UsesComponent.java b/structurizr-annotations/src/com/structurizr/annotation/UsesComponent.java deleted file mode 100644 index 4d55dbb96..000000000 --- a/structurizr-annotations/src/com/structurizr/annotation/UsesComponent.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.structurizr.annotation; - -import java.lang.annotation.*; - -/** - * A field-level annotation that can be used to supplement the existing relationship - * (i.e. add a description and/or technology) between two components. - */ -@Documented -@Target({ElementType.FIELD}) -@Retention(RetentionPolicy.RUNTIME) -public @interface UsesComponent { - - String description(); - String technology() default ""; - -} diff --git a/structurizr-annotations/src/com/structurizr/annotation/UsesContainer.java b/structurizr-annotations/src/com/structurizr/annotation/UsesContainer.java deleted file mode 100644 index 53df48342..000000000 --- a/structurizr-annotations/src/com/structurizr/annotation/UsesContainer.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.structurizr.annotation; - -import java.lang.annotation.*; - -/** - * A type-level annotation that can be used to signify that the component - * on which this annotation is placed has a relationship to the named container, - * creating a relationship from the component to the container. - */ -@Documented -@Target({ElementType.TYPE}) -@Retention(RetentionPolicy.RUNTIME) -@Repeatable(UsesContainers.class) -public @interface UsesContainer { - - String name(); - String description() default ""; - String technology() default ""; - -} diff --git a/structurizr-annotations/src/com/structurizr/annotation/UsesContainers.java b/structurizr-annotations/src/com/structurizr/annotation/UsesContainers.java deleted file mode 100644 index 646c22c87..000000000 --- a/structurizr-annotations/src/com/structurizr/annotation/UsesContainers.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.structurizr.annotation; - -import java.lang.annotation.*; - -/** - * A wrapper for multiple @UsesContainer annotations. - */ -@Documented -@Target({ElementType.TYPE}) -@Retention(RetentionPolicy.RUNTIME) -public @interface UsesContainers { - - UsesContainer[] value(); - -} diff --git a/structurizr-annotations/src/com/structurizr/annotation/UsesSoftwareSystem.java b/structurizr-annotations/src/com/structurizr/annotation/UsesSoftwareSystem.java deleted file mode 100644 index a2a00065d..000000000 --- a/structurizr-annotations/src/com/structurizr/annotation/UsesSoftwareSystem.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.structurizr.annotation; - -import java.lang.annotation.*; - -/** - * A type-level annotation that can be used to signify that the component - * on which this annotation is placed has a relationship to the named software system, - * creating a relationship from the component to the software system. - */ -@Documented -@Target({ElementType.TYPE}) -@Retention(RetentionPolicy.RUNTIME) -@Repeatable(UsesSoftwareSystems.class) -public @interface UsesSoftwareSystem { - - String name(); - String description() default ""; - String technology() default ""; - -} diff --git a/structurizr-annotations/src/com/structurizr/annotation/UsesSoftwareSystems.java b/structurizr-annotations/src/com/structurizr/annotation/UsesSoftwareSystems.java deleted file mode 100644 index b64124624..000000000 --- a/structurizr-annotations/src/com/structurizr/annotation/UsesSoftwareSystems.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.structurizr.annotation; - -import java.lang.annotation.*; - -/** - * A wrapper for multiple @UsesSoftwareSystem annotations. - */ -@Documented -@Target({ElementType.TYPE}) -@Retention(RetentionPolicy.RUNTIME) -public @interface UsesSoftwareSystems { - - UsesSoftwareSystem[] value(); - -} diff --git a/structurizr-dot/build.gradle b/structurizr-dot/build.gradle deleted file mode 100644 index 1d828ea6d..000000000 --- a/structurizr-dot/build.gradle +++ /dev/null @@ -1,6 +0,0 @@ -dependencies { - - compile project(':structurizr-core') - compile 'io.github.livingdocumentation:dot-diagram:1.1' - -} \ No newline at end of file diff --git a/structurizr-dot/etc/graphviz-dot.properties b/structurizr-dot/etc/graphviz-dot.properties deleted file mode 100644 index cb9d6f6d5..000000000 --- a/structurizr-dot/etc/graphviz-dot.properties +++ /dev/null @@ -1,13 +0,0 @@ -# The path where to output generated documentation, with ending slash -outpath= - -# The path to the dot tool (Graphviz), with ending slash -dotpath=/usr/local/bin/ - -# The image extension, with the leading dot -imageextension=.png - - -# $0: outpath -# $1: outpath+filename -commandline={0}dot -Tpng {1}.dot -o {1}.png -Gdpi=72 -Gsize="6,8.5" \ No newline at end of file diff --git a/structurizr-dot/src/com/structurizr/io/dot/DotWriter.java b/structurizr-dot/src/com/structurizr/io/dot/DotWriter.java deleted file mode 100644 index ed15bce0a..000000000 --- a/structurizr-dot/src/com/structurizr/io/dot/DotWriter.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.structurizr.io.dot; - -import com.structurizr.Workspace; -import com.structurizr.model.Element; -import com.structurizr.model.Relationship; -import com.structurizr.view.ElementView; -import com.structurizr.view.RelationshipView; -import com.structurizr.view.View; -import io.github.livingdocumentation.dotdiagram.DotGraph; - -import java.io.IOException; -import java.io.StringWriter; -import java.io.Writer; - -/** - * This is a simple implementation of a workspace writer that outputs - * to the Graphviz DOT format. You will need graphviz installed and - * correctly configured. - * - * See https://github.com/LivingDocumentation/dot-diagram for more information. - */ -public class DotWriter { - - /** - * Writes the views in the given workspace as DOT notation, to the specified Writer. - * - * @param workspace the workspace containing the views to be written - * @param writer the Writer to write to - */ - public void write(Workspace workspace, Writer writer) { - workspace.getViews().getSystemContextViews().forEach(v -> write(v, null, writer)); - workspace.getViews().getContainerViews().forEach(v -> write(v, v.getSoftwareSystem(), writer)); - workspace.getViews().getComponentViews().forEach(v -> write(v, v.getContainer(), writer)); - } - - /** - * Write the views in the given workspace as DOT notation, to stdout. - * - * @param workspace the workspace containing the views to be written - */ - public void write(Workspace workspace) { - StringWriter stringWriter = new StringWriter(); - write(workspace, stringWriter); - System.out.println(stringWriter.toString()); - } - - private void write(View view, Element clusterElement, Writer writer) { - try { - DotGraph graph = new DotGraph(view.getName()); - DotGraph.Digraph digraph = graph.getDigraph(); - DotGraph.Cluster cluster = null; - - if (clusterElement != null) { - cluster = digraph.addCluster(clusterElement.getId()); - cluster.setLabel(clusterElement.getName()); - } - - for (ElementView elementView : view.getElements()) { - Element element = elementView.getElement(); - - if (clusterElement != null && element.getParent() == clusterElement) { - cluster.addNode(element.getId()).setLabel(element.getName()); - } else { - digraph.addNode(element.getId()).setLabel(element.getName()); - } - } - - for (RelationshipView relationshipView : view.getRelationships()) { - Relationship relationship = relationshipView.getRelationship(); - digraph.addAssociation( - relationship.getSourceId(), - relationship.getDestinationId()).setLabel(relationship.getDescription()); - } - - String output = graph.render().trim(); - writer.write(output); - writer.write(System.lineSeparator()); - writer.write(System.lineSeparator()); - } catch (IOException ioe) { - ioe.printStackTrace(); - } - } - -} \ No newline at end of file diff --git a/structurizr-examples/build.gradle b/structurizr-examples/build.gradle index 85f5ecaac..ee4f739d1 100644 --- a/structurizr-examples/build.gradle +++ b/structurizr-examples/build.gradle @@ -1,22 +1,6 @@ dependencies { - compile project(':structurizr-adr-tools') - compile project(':structurizr-annotations') compile project(':structurizr-client') - compile project(':structurizr-dot') - compile project(':structurizr-mermaid') - compile project(':structurizr-plantuml') - compile project(':structurizr-spring') compile 'org.slf4j:slf4j-api:1.7.21' compile 'org.slf4j:slf4j-simple:1.7.21' -} - -task springPetClinic(type:JavaExec) { - main = "com.structurizr.example.spring.petclinic.SpringPetClinic" - classpath( - sourceSets.main.runtimeClasspath, - '/Users/structurizr/Documents/spring-petclinic/target/spring-petclinic-1.0.0-SNAPSHOT/WEB-INF/classes', - fileTree(dir: '/Users/structurizr/Documents/spring-petclinic/target/spring-petclinic-1.0.0-SNAPSHOT/WEB-INF/lib', include: '*.jar') - ) - args '/Users/structurizr/Documents/spring-petclinic' } \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/AdrTools.java b/structurizr-examples/src/com/structurizr/example/AdrTools.java deleted file mode 100644 index 35c1cbed9..000000000 --- a/structurizr-examples/src/com/structurizr/example/AdrTools.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.structurizr.example; - -import com.structurizr.Workspace; -import com.structurizr.api.StructurizrClient; -import com.structurizr.documentation.AdrToolsImporter; -import com.structurizr.model.*; -import com.structurizr.view.*; - -import java.io.File; - -/** - * An example of how to import architecture decision records from Nat Pryce's adr-tools tool. - * - * The live workspace is available to view at https://structurizr.com/share/39459 - */ -public class AdrTools { - - private static final long WORKSPACE_ID = 39459; - private static final String API_KEY = "key"; - private static final String API_SECRET = "secret"; - - private static final String FILE_SYSTEM_TAG = "File System"; - - public static void main(String[] args) throws Exception { - Workspace workspace = new Workspace("adr-tools", "A description of the adr-tools command line utility."); - Model model = workspace.getModel(); - - Person user = model.addPerson("User", "Somebody on a software development team."); - SoftwareSystem adrTools = model.addSoftwareSystem("adr-tools", "A command-line tool for working with Architecture Decision Records (ADRs)."); - adrTools.setUrl("https://github.com/npryce/adr-tools"); - - Container adrShellScripts = adrTools.addContainer("adr", "A command-line tool for working with Architecture Decision Records (ADRs).", "Shell Scripts"); - adrShellScripts.setUrl("https://github.com/npryce/adr-tools/tree/master/src"); - Container fileSystem = adrTools.addContainer("File System", "Stores ADRs, templates, etc.", "File System"); - fileSystem.addTags(FILE_SYSTEM_TAG); - user.uses(adrShellScripts, "Manages ADRs using"); - adrShellScripts.uses(fileSystem, "Reads from and writes to"); - model.addImplicitRelationships(); - - ViewSet views = workspace.getViews(); - SystemContextView contextView = views.createSystemContextView(adrTools, "SystemContext", "The system context diagram for adr-tools."); - contextView.addAllElements(); - - ContainerView containerView = views.createContainerView(adrTools, "Containers", "The container diagram for adr-tools."); - containerView.addAllElements(); - - File adrDirectory = new File("./structurizr-examples/src/com/structurizr/example/documentation/adr"); - - AdrToolsImporter adrToolsImporter = new AdrToolsImporter(workspace, adrDirectory); - adrToolsImporter.importArchitectureDecisionRecords(adrTools); - - Styles styles = views.getConfiguration().getStyles(); - styles.addElementStyle(Tags.ELEMENT).shape(Shape.RoundedBox).color("#ffffff"); - styles.addElementStyle(Tags.SOFTWARE_SYSTEM).background("#18ADAD").color("#ffffff"); - styles.addElementStyle(Tags.PERSON).shape(Shape.Person).background("#008282").color("#ffffff"); - styles.addElementStyle(Tags.CONTAINER).background("#6DBFBF"); - styles.addElementStyle(FILE_SYSTEM_TAG).shape(Shape.Folder); - - StructurizrClient structurizrClient = new StructurizrClient(API_KEY, API_SECRET); - structurizrClient.setMergeFromRemote(false); - structurizrClient.putWorkspace(WORKSPACE_ID, workspace); - } - -} \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/PlantUML.java b/structurizr-examples/src/com/structurizr/example/PlantUML.java deleted file mode 100644 index 21b4fde25..000000000 --- a/structurizr-examples/src/com/structurizr/example/PlantUML.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.structurizr.example; - -import com.structurizr.Workspace; -import com.structurizr.io.plantuml.PlantUMLWriter; -import com.structurizr.model.Model; -import com.structurizr.model.Person; -import com.structurizr.model.SoftwareSystem; -import com.structurizr.model.Tags; -import com.structurizr.view.PaperSize; -import com.structurizr.view.Shape; -import com.structurizr.view.Styles; -import com.structurizr.view.SystemContextView; -import com.structurizr.view.ViewSet; - -import java.io.StringWriter; - -/** - * An example of how to use the PlantUML writer. Run this program and copy/paste - * the output into http://www.plantuml.com/plantuml/ - */ -public class PlantUML { - - public static void main(String[] args) throws Exception { - Workspace workspace = new Workspace("Getting Started", "This is a model of my software system."); - Model model = workspace.getModel(); - - Person user = model.addPerson("User", "A user of my software system."); - SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "My software system."); - user.uses(softwareSystem, "Uses"); - - ViewSet views = workspace.getViews(); - SystemContextView contextView = views.createSystemContextView(softwareSystem, "SystemContext", "An example of a System Context diagram."); - contextView.addAllSoftwareSystems(); - contextView.addAllPeople(); - - Styles styles = views.getConfiguration().getStyles(); - styles.addElementStyle(Tags.SOFTWARE_SYSTEM).background("#1168bd").color("#ffffff"); - styles.addElementStyle(Tags.PERSON).background("#08427b").color("#ffffff").shape(Shape.Person); - - PlantUMLWriter plantUMLWriter = new PlantUMLWriter(); - - // if you're using dark background colours, you might need to explicitly set the foreground colour using skin params - // e.g. rectangleFontColor, rectangleFontColor<>, etc - plantUMLWriter.addSkinParam("rectangleFontColor", "#ffffff"); - plantUMLWriter.addSkinParam("rectangleStereotypeFontColor", "#ffffff"); - - // can set direction, default is "top to bottom direction" - // plantUMLWriter.setDirection("left to right direction"); - - // write to a specific writer - StringWriter stringWriter = new StringWriter(); - plantUMLWriter.write(workspace, stringWriter); - System.out.println(stringWriter.toString()); - - // or just output directly to stdout - // plantUMLWriter.toStdOut(workspace); - } - -} diff --git a/structurizr-examples/src/com/structurizr/example/SpringBootPetClinic.java b/structurizr-examples/src/com/structurizr/example/SpringBootPetClinic.java deleted file mode 100644 index b7764b479..000000000 --- a/structurizr-examples/src/com/structurizr/example/SpringBootPetClinic.java +++ /dev/null @@ -1,143 +0,0 @@ -package com.structurizr.example; - -import com.structurizr.Workspace; -import com.structurizr.analysis.ComponentFinder; -import com.structurizr.analysis.ReferencedTypesSupportingTypesStrategy; -import com.structurizr.analysis.SourceCodeComponentFinderStrategy; -import com.structurizr.analysis.SpringComponentFinderStrategy; -import com.structurizr.api.StructurizrClient; -import com.structurizr.model.*; -import com.structurizr.view.*; - -import java.io.File; - -/** - * This is a C4 representation of the Spring Boot version of the PetClinic sample app - * (https://github.com/spring-projects/spring-petclinic/tree/ffa967c94b65a70ea6d3b44275632821838d9fd3) and you can see - * the resulting diagrams at https://www.structurizr.com/public/6031 - * - * Please note, you will need to modify the paths specified in the structurizr-examples/build.gradle file. - */ -public class SpringBootPetClinic { - - private static final String VERSION = "ffa967c94b65a70ea6d3b44275632821838d9fd3"; - - private static final long WORKSPACE_ID = 6031; - private static final String API_KEY = ""; - private static final String API_SECRET = ""; - - public static void main(String[] args) throws Exception { - if (args.length == 0) { - System.out.println("The path to the Spring Boot PetClinic source code must be provided."); - System.exit(-1); - } - File sourceRoot = new File(args[0]); - - try { - ClassLoader.getSystemClassLoader().loadClass("org.springframework.samples.petclinic.vet.Vet"); - } catch (ClassNotFoundException e) { - System.err.println("Please check that the compiled version of the Spring Boot PetClinic application is on the classpath"); - System.exit(-1); - } - - Workspace workspace = new Workspace("Spring Boot PetClinic", - "This is a C4 representation of the Spring Boot PetClinic sample app (https://github.com/spring-projects/spring-petclinic/)"); - workspace.setVersion(VERSION); - Model model = workspace.getModel(); - - // create the basic model (the stuff we can't get from the code) - SoftwareSystem springPetClinic = model.addSoftwareSystem("Spring PetClinic", "Allows employees to view and manage information regarding the veterinarians, the clients, and their pets."); - Person clinicEmployee = model.addPerson("Clinic Employee", "An employee of the clinic"); - clinicEmployee.uses(springPetClinic, "Uses"); - - Container webApplication = springPetClinic.addContainer( - "Web Application", "Allows employees to view and manage information regarding the veterinarians, the clients, and their pets.", "Apache Tomcat 7.x"); - Container relationalDatabase = springPetClinic.addContainer( - "Relational Database", "Stores information regarding the veterinarians, the clients, and their pets.", "HSQLDB"); - clinicEmployee.uses(webApplication, "Uses", "HTTP"); - webApplication.uses(relationalDatabase, "Reads from and writes to", "JDBC, port 9001"); - - SpringComponentFinderStrategy springComponentFinderStrategy = new SpringComponentFinderStrategy(new ReferencedTypesSupportingTypesStrategy(false)); - springComponentFinderStrategy.setIncludePublicTypesOnly(false); - - // and now automatically find all Spring @Controller, @Component, @Service and @Repository components - ComponentFinder componentFinder = new ComponentFinder( - webApplication, - "org.springframework.samples.petclinic", - springComponentFinderStrategy, - new SourceCodeComponentFinderStrategy(new File(sourceRoot, "/src/main/java/"), 150)); - - componentFinder.exclude(".*Formatter.*"); - componentFinder.findComponents(); - - // connect the user to all of the Spring MVC controllers - webApplication.getComponents().stream() - .filter(c -> c.getTechnology().equals(SpringComponentFinderStrategy.SPRING_MVC_CONTROLLER)) - .forEach(c -> clinicEmployee.uses(c, "Uses", "HTTP")); - - // connect all of the repository components to the relational database - webApplication.getComponents().stream() - .filter(c -> c.getTechnology().equals(SpringComponentFinderStrategy.SPRING_REPOSITORY)) - .forEach(c -> c.uses(relationalDatabase, "Reads from and writes to", "JDBC")); - - // finally create some views - ViewSet views = workspace.getViews(); - SystemContextView contextView = views.createSystemContextView(springPetClinic, "context", "The System Context diagram for the Spring PetClinic system."); - contextView.addAllSoftwareSystems(); - contextView.addAllPeople(); - - ContainerView containerView = views.createContainerView(springPetClinic, "containers", "The Containers diagram for the Spring PetClinic system."); - containerView.addAllPeople(); - containerView.addAllSoftwareSystems(); - containerView.addAllContainers(); - - ComponentView componentView = views.createComponentView(webApplication, "components", "The Components diagram for the Spring PetClinic web application."); - componentView.addAllComponents(); - componentView.addAllPeople(); - componentView.add(relationalDatabase); - - // link the architecture model with the code - for (Component component : webApplication.getComponents()) { - for (CodeElement codeElement : component.getCode()) { - String sourcePath = codeElement.getUrl(); - if (sourcePath != null) { - codeElement.setUrl(sourcePath.replace( - sourceRoot.toURI().toString(), - "https://github.com/spring-projects/spring-petclinic/tree/" + VERSION + "/")); - } - } - } - - // rather than creating a component model for the database, let's simply link to the DDL - // (this is really just an example of linking an arbitrary element in the model to an external resource) - relationalDatabase.setUrl("https://github.com/spring-projects/spring-petclinic/tree/" + VERSION + "/src/main/resources/db/hsqldb"); - - // tag and style some elements - springPetClinic.addTags("Spring PetClinic"); - webApplication.getComponents().stream().filter(c -> c.getTechnology().equals(SpringComponentFinderStrategy.SPRING_MVC_CONTROLLER)).forEach(c -> c.addTags("Spring MVC Controller")); - webApplication.getComponents().stream().filter(c -> c.getTechnology().equals(SpringComponentFinderStrategy.SPRING_SERVICE)).forEach(c -> c.addTags("Spring Service")); - webApplication.getComponents().stream().filter(c -> c.getTechnology().equals(SpringComponentFinderStrategy.SPRING_REPOSITORY)).forEach(c -> c.addTags("Spring Repository")); - relationalDatabase.addTags("Database"); - - Component vetController = webApplication.getComponentWithName("VetController"); - Component vetRepository = webApplication.getComponentWithName("VetRepository"); - - DynamicView dynamicView = views.createDynamicView(webApplication, "viewListOfVets", "Shows how the \"view list of vets\" feature works."); - dynamicView.add(clinicEmployee, "Requests list of vets from /vets", vetController); - dynamicView.add(vetController, "Calls findAll", vetRepository); - dynamicView.add(vetRepository, "select * from vets", relationalDatabase); - - Styles styles = views.getConfiguration().getStyles(); - styles.addElementStyle("Spring PetClinic").background("#6CB33E").color("#ffffff"); - styles.addElementStyle(Tags.PERSON).background("#519823").color("#ffffff").shape(Shape.Person); - styles.addElementStyle(Tags.CONTAINER).background("#91D366").color("#ffffff"); - styles.addElementStyle("Database").shape(Shape.Cylinder); - styles.addElementStyle("Spring MVC Controller").background("#D4F3C0").color("#000000"); - styles.addElementStyle("Spring Service").background("#6CB33E").color("#000000"); - styles.addElementStyle("Spring Repository").background("#95D46C").color("#000000"); - - StructurizrClient structurizrClient = new StructurizrClient(API_KEY, API_SECRET); - structurizrClient.putWorkspace(WORKSPACE_ID, workspace); - } - -} \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/SpringPetClinic.java b/structurizr-examples/src/com/structurizr/example/SpringPetClinic.java deleted file mode 100644 index 3804f7d3f..000000000 --- a/structurizr-examples/src/com/structurizr/example/SpringPetClinic.java +++ /dev/null @@ -1,201 +0,0 @@ -package com.structurizr.example; - -import com.structurizr.Workspace; -import com.structurizr.api.StructurizrClient; -import com.structurizr.analysis.ComponentFinder; -import com.structurizr.analysis.ReferencedTypesSupportingTypesStrategy; -import com.structurizr.analysis.SourceCodeComponentFinderStrategy; -import com.structurizr.analysis.SpringComponentFinderStrategy; -import com.structurizr.documentation.Format; -import com.structurizr.documentation.StructurizrDocumentationTemplate; -import com.structurizr.model.*; -import com.structurizr.util.MapUtils; -import com.structurizr.view.*; - -import java.io.File; - -/** - * This is a C4 representation of the original Spring PetClinic sample app - * (https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6) and you can see - * the resulting diagrams at https://www.structurizr.com/public/1 - * - * Use the following command to run this example: ./gradlew :structurizr-example:springPetClinic - * - * Please note, you will need to modify the paths specified in the structurizr-examples/build.gradle file. - */ -public class SpringPetClinic { - - private static final String VERSION = "95de1d9f8bf63560915331664b27a4a75ce1f1f6"; - - private static final long WORKSPACE_ID = 1; - private static final String API_KEY = ""; - private static final String API_SECRET = ""; - - public static void main(String[] args) throws Exception { - if (args.length == 0) { - System.out.println("The path to the Spring PetClinic source code must be provided."); - System.exit(-1); - } - File sourceRoot = new File(args[0]); - - try { - ClassLoader.getSystemClassLoader().loadClass("org.springframework.samples.petclinic.model.Vet"); - } catch (ClassNotFoundException e) { - System.err.println("Please check that the compiled version of the Spring PetClinic application is on the classpath"); - System.exit(-1); - } - - Workspace workspace = new Workspace("Spring PetClinic", - "This is a C4 representation of the Spring PetClinic sample app (https://github.com/spring-projects/spring-petclinic/)"); - workspace.setVersion(VERSION); - Model model = workspace.getModel(); - - // create the basic model (the stuff we can't get from the code) - SoftwareSystem springPetClinic = model.addSoftwareSystem("Spring PetClinic", "Allows employees to view and manage information regarding the veterinarians, the clients, and their pets."); - Person clinicEmployee = model.addPerson("Clinic Employee", "An employee of the clinic"); - clinicEmployee.uses(springPetClinic, "Uses"); - - Container webApplication = springPetClinic.addContainer( - "Web Application", "Allows employees to view and manage information regarding the veterinarians, the clients, and their pets.", "Java and Spring"); - webApplication.addProperty("Deployable artifact name", "petclinic.war"); - Container relationalDatabase = springPetClinic.addContainer( - "Database", "Stores information regarding the veterinarians, the clients, and their pets.", "Relational Database Schema"); - clinicEmployee.uses(webApplication, "Uses", "HTTPS"); - webApplication.uses(relationalDatabase, "Reads from and writes to", "JDBC"); - - // and now automatically find all Spring @Controller, @Component, @Service and @Repository components - ComponentFinder componentFinder = new ComponentFinder( - webApplication, - "org.springframework.samples.petclinic", - new SpringComponentFinderStrategy( - new ReferencedTypesSupportingTypesStrategy(false) - ), - new SourceCodeComponentFinderStrategy(new File(sourceRoot, "/src/main/java/"), 150)); - componentFinder.findComponents(); - - // connect the user to all of the Spring MVC controllers - webApplication.getComponents().stream() - .filter(c -> c.getTechnology().equals(SpringComponentFinderStrategy.SPRING_MVC_CONTROLLER)) - .forEach(c -> clinicEmployee.uses(c, "Uses", "HTTP")); - - // connect all of the repository components to the relational database - webApplication.getComponents().stream() - .filter(c -> c.getTechnology().equals(SpringComponentFinderStrategy.SPRING_REPOSITORY)) - .forEach(c -> c.uses(relationalDatabase, "Reads from and writes to", "JDBC")); - - // finally create some views - ViewSet views = workspace.getViews(); - SystemContextView contextView = views.createSystemContextView(springPetClinic, "context", "The System Context diagram for the Spring PetClinic system."); - contextView.addAllSoftwareSystems(); - contextView.addAllPeople(); - - ContainerView containerView = views.createContainerView(springPetClinic, "containers", "The Container diagram for the Spring PetClinic system."); - containerView.addAllPeople(); - containerView.addAllSoftwareSystems(); - containerView.addAllContainers(); - - ComponentView componentView = views.createComponentView(webApplication, "components", "The Component diagram for the Spring PetClinic web application."); - componentView.addAllComponents(); - componentView.addAllPeople(); - componentView.add(relationalDatabase); - - // link the architecture model with the code - for (Component component : webApplication.getComponents()) { - for (CodeElement codeElement : component.getCode()) { - String sourcePath = codeElement.getUrl(); - if (sourcePath != null) { - codeElement.setUrl(sourcePath.replace( - sourceRoot.toURI().toString(), - "https://github.com/spring-projects/spring-petclinic/tree/" + VERSION + "/")); - } - } - } - - // rather than creating a component model for the database, let's simply link to the DDL - // (this is really just an example of linking an arbitrary element in the model to an external resource) - relationalDatabase.setUrl("https://github.com/spring-projects/spring-petclinic/tree/" + VERSION + "/src/main/resources/db/hsqldb"); - relationalDatabase.addProperty("Schema name", "petclinic"); - - // tag and style some elements - springPetClinic.addTags("Spring PetClinic"); - webApplication.getComponents().stream().filter(c -> c.getTechnology().equals(SpringComponentFinderStrategy.SPRING_MVC_CONTROLLER)).forEach(c -> c.addTags("Spring MVC Controller")); - webApplication.getComponents().stream().filter(c -> c.getTechnology().equals(SpringComponentFinderStrategy.SPRING_SERVICE)).forEach(c -> c.addTags("Spring Service")); - webApplication.getComponents().stream().filter(c -> c.getTechnology().equals(SpringComponentFinderStrategy.SPRING_REPOSITORY)).forEach(c -> c.addTags("Spring Repository")); - relationalDatabase.addTags("Database"); - - Component vetController = webApplication.getComponentWithName("VetController"); - Component clinicService = webApplication.getComponentWithName("ClinicService"); - Component vetRepository = webApplication.getComponentWithName("VetRepository"); - - DynamicView dynamicView = views.createDynamicView(webApplication, "viewListOfVets", "Shows how the \"view list of vets\" feature works."); - dynamicView.add(clinicEmployee, "Requests list of vets from /vets", vetController); - dynamicView.add(vetController, "Calls findVets", clinicService); - dynamicView.add(clinicService, "Calls findAll", vetRepository); - dynamicView.add(vetRepository, "select * from vets", relationalDatabase); - - DeploymentNode developerLaptop = model.addDeploymentNode("Developer Laptop", "A developer laptop.", "Windows 7+ or macOS"); - developerLaptop.addDeploymentNode("Docker Container - Web Server", "A Docker container.", "Docker") - .addDeploymentNode("Apache Tomcat", "An open source Java EE web server.", "Apache Tomcat 7.x", 1, MapUtils.create("Xmx=256M", "Xms=512M", "Java Version=8")) - .add(webApplication); - - developerLaptop.addDeploymentNode("Docker Container - Database Server", "A Docker container.", "Docker") - .addDeploymentNode("Database Server", "A development database.", "HSQLDB") - .add(relationalDatabase); - - DeploymentNode stagingServer = model.addDeploymentNode("Staging Server", "A server hosted at Amazon AWS EC2", "Ubuntu 12.04 LTS", 1, MapUtils.create("AWS instance type=t2.medium", "AWS region=us-west-1")); - stagingServer.addDeploymentNode("Apache Tomcat", "An open source Java EE web server.", "Apache Tomcat 7.x", 1, MapUtils.create("Xmx=512M", "Xms=1024M", "Java Version=8")) - .add(webApplication); - stagingServer.addDeploymentNode("MySQL", "The staging database server.", "MySQL 5.5.x", 1) - .add(relationalDatabase);; - - DeploymentNode liveWebServer = model.addDeploymentNode("Web Server", "A server hosted at Amazon AWS EC2, accessed via Elastic Load Balancing.", "Ubuntu 12.04 LTS", 2, MapUtils.create("AWS instance type=t2.small", "AWS region=us-west-1")); - liveWebServer.addDeploymentNode("Apache Tomcat", "An open source Java EE web server.", "Apache Tomcat 7.x", 1, MapUtils.create("Xmx=512M", "Xms=1024M", "Java Version=8")) - .add(webApplication); - - DeploymentNode primaryDatabaseServer = model.addDeploymentNode("Database Server - Primary", "A server hosted at Amazon AWS EC2.", "Ubuntu 12.04 LTS", 1, MapUtils.create("AWS instance type=t2.medium", "AWS region=us-west-1")) - .addDeploymentNode("MySQL - Primary", "The primary, live database server.", "MySQL 5.5.x"); - primaryDatabaseServer.add(relationalDatabase); - - DeploymentNode secondaryDatabaseServer = model.addDeploymentNode("Database Server - Secondary", "A server hosted at Amazon AWS EC2.", "Ubuntu 12.04 LTS", 1, MapUtils.create("AWS instance type=t2.small", "AWS region=us-east-1")) - .addDeploymentNode("MySQL - Secondary", "A secondary database server, used for failover purposes.", "MySQL 5.5.x"); - ContainerInstance secondaryDatabase = secondaryDatabaseServer.add(relationalDatabase); - - model.getRelationships().stream().filter(r -> r.getDestination().equals(secondaryDatabase)).forEach(r -> r.addTags("Failover")); - Relationship dataReplicationRelationship = primaryDatabaseServer.uses(secondaryDatabaseServer, "Replicates data to", ""); - secondaryDatabase.addTags("Failover"); - - DeploymentView developmentDeploymentView = views.createDeploymentView(springPetClinic, "developmentDeployment", "An example development deployment scenario for the Spring PetClinic software system."); - developmentDeploymentView.add(developerLaptop); - - DeploymentView stagingDeploymentView = views.createDeploymentView(springPetClinic, "stagingDeployment", "An example staging deployment scenario for the Spring PetClinic software system."); - stagingDeploymentView.add(stagingServer); - - DeploymentView liveDeploymentView = views.createDeploymentView(springPetClinic, "liveDeployment", "An example live deployment scenario for the Spring PetClinic software system."); - liveDeploymentView.add(liveWebServer); - liveDeploymentView.add(primaryDatabaseServer); - liveDeploymentView.add(secondaryDatabaseServer); - liveDeploymentView.add(dataReplicationRelationship); - - StructurizrDocumentationTemplate template = new StructurizrDocumentationTemplate(workspace); - template.addContextSection(springPetClinic, Format.Markdown, "This is the context section for the Spring PetClinic System...\n![](embed:context)"); - template.addContainersSection(springPetClinic, Format.Markdown, "This is the containers section for the Spring PetClinic System...\n![](embed:containers)"); - template.addComponentsSection(webApplication, Format.Markdown, "This is the components section for the Spring PetClinic web application...\n![](embed:components)"); - template.addDeploymentSection(springPetClinic, Format.Markdown, "This is the deployment section for the Spring PetClinic web application...\n### Staging environment\n![](embed:stagingDeployment)\n### Live environment\n![](embed:liveDeployment)"); - template.addDevelopmentEnvironmentSection(springPetClinic, Format.Markdown, "This is the development environment section for the Spring PetClinic web application...\n![](embed:developmentDeployment)"); - - Styles styles = views.getConfiguration().getStyles(); - styles.addElementStyle("Spring PetClinic").background("#6CB33E").color("#ffffff"); - styles.addElementStyle(Tags.PERSON).background("#519823").color("#ffffff").shape(Shape.Person); - styles.addElementStyle(Tags.CONTAINER).background("#91D366").color("#ffffff"); - styles.addElementStyle("Database").shape(Shape.Cylinder); - styles.addElementStyle("Spring MVC Controller").background("#D4F3C0").color("#000000"); - styles.addElementStyle("Spring Service").background("#6CB33E").color("#000000"); - styles.addElementStyle("Spring Repository").background("#95D46C").color("#000000"); - styles.addElementStyle("Failover").opacity(25); - styles.addRelationshipStyle("Failover").opacity(25).position(70); - - StructurizrClient structurizrClient = new StructurizrClient(API_KEY, API_SECRET); - structurizrClient.putWorkspace(WORKSPACE_ID, workspace); - } - -} \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/StructurizrAnnotations.java b/structurizr-examples/src/com/structurizr/example/StructurizrAnnotations.java deleted file mode 100644 index 91be1b4a3..000000000 --- a/structurizr-examples/src/com/structurizr/example/StructurizrAnnotations.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.structurizr.example; - -import com.structurizr.Workspace; -import com.structurizr.analysis.ComponentFinder; -import com.structurizr.analysis.StructurizrAnnotationsComponentFinderStrategy; -import com.structurizr.api.StructurizrClient; -import com.structurizr.model.*; -import com.structurizr.view.*; - -/** - * An small example that illustrates how to use the Structurizr annotations - * in conjunction with the StructurizrAnnotationsComponentFinderStrategy. - * - * The live workspace is available to view at https://structurizr.com/share/36571 - */ -public class StructurizrAnnotations { - - private static final String DATABASE_TAG = "Database"; - - private static final long WORKSPACE_ID = 36571; - private static final String API_KEY = ""; - private static final String API_SECRET = ""; - - public static void main(String[] args) throws Exception { - Workspace workspace = new Workspace("Structurizr for Java Annotations", "This is a model of my software system."); - Model model = workspace.getModel(); - - Person user = model.addPerson("User", "A user of my software system."); - SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "My software system."); - - Container webApplication = softwareSystem.addContainer("Web Application", "Provides users with information.", "Java"); - Container database = softwareSystem.addContainer("Database", "Stores information.", "Relational database schema"); - database.addTags(DATABASE_TAG); - - ComponentFinder componentFinder = new ComponentFinder( - webApplication, - "com.structurizr.example.annotations", - new StructurizrAnnotationsComponentFinderStrategy() - ); - componentFinder.findComponents(); - model.addImplicitRelationships(); - - ViewSet views = workspace.getViews(); - SystemContextView contextView = views.createSystemContextView(softwareSystem, "SystemContext", "An example of a System Context diagram."); - contextView.addAllElements(); - - ContainerView containerView = views.createContainerView(softwareSystem, "Containers", "The container diagram from my software system."); - containerView.addAllElements(); - - ComponentView componentView = views.createComponentView(webApplication, "Components", "The component diagram for the web application."); - componentView.addAllElements(); - - Styles styles = views.getConfiguration().getStyles(); - styles.addElementStyle(Tags.ELEMENT).color("#ffffff"); - styles.addElementStyle(Tags.SOFTWARE_SYSTEM).background("#1168bd"); - styles.addElementStyle(Tags.CONTAINER).background("#438dd5"); - styles.addElementStyle(Tags.COMPONENT).background("#85bbf0").color("#000000"); - styles.addElementStyle(Tags.PERSON).background("#08427b").shape(Shape.Person); - styles.addElementStyle(DATABASE_TAG).shape(Shape.Cylinder); - - StructurizrClient structurizrClient = new StructurizrClient(API_KEY, API_SECRET); - structurizrClient.putWorkspace(WORKSPACE_ID, workspace); - } - -} \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/annotations/HtmlController.java b/structurizr-examples/src/com/structurizr/example/annotations/HtmlController.java deleted file mode 100644 index 9a4a1e2f4..000000000 --- a/structurizr-examples/src/com/structurizr/example/annotations/HtmlController.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.structurizr.example.annotations; - -import com.structurizr.annotation.Component; -import com.structurizr.annotation.UsedByPerson; -import com.structurizr.annotation.UsesComponent; - -@Component(description = "Serves HTML pages to users.", technology = "Java") -@UsedByPerson(name = "User", description = "Uses", technology = "HTTPS") -class HtmlController { - - @UsesComponent(description = "Gets data using") - private Repository repository = new JdbcRepository(); - -} \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/annotations/JdbcRepository.java b/structurizr-examples/src/com/structurizr/example/annotations/JdbcRepository.java deleted file mode 100644 index fa21c78f9..000000000 --- a/structurizr-examples/src/com/structurizr/example/annotations/JdbcRepository.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.structurizr.example.annotations; - -import com.structurizr.annotation.UsesContainer; - -@UsesContainer(name = "Database", description = "Reads from", technology = "JDBC") -class JdbcRepository implements Repository { - - public String getData(long id) { - return "..."; - } - -} \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/annotations/Repository.java b/structurizr-examples/src/com/structurizr/example/annotations/Repository.java deleted file mode 100644 index 57d458441..000000000 --- a/structurizr-examples/src/com/structurizr/example/annotations/Repository.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.structurizr.example.annotations; - -import com.structurizr.annotation.Component; - -@Component(description = "Provides access to data stored in the database.", technology = "Java and JPA") -public interface Repository { - - String getData(long id); - -} \ No newline at end of file diff --git a/structurizr-javaee/build.gradle b/structurizr-javaee/build.gradle deleted file mode 100644 index 6cf72e7e5..000000000 --- a/structurizr-javaee/build.gradle +++ /dev/null @@ -1,5 +0,0 @@ -dependencies { - compile project(':structurizr-core') - - compile 'javax:javaee-api:7.0' -} diff --git a/structurizr-javaee/src/com/structurizr/analysis/JavaEEComponentFinderStrategy.java b/structurizr-javaee/src/com/structurizr/analysis/JavaEEComponentFinderStrategy.java deleted file mode 100644 index a78673e49..000000000 --- a/structurizr-javaee/src/com/structurizr/analysis/JavaEEComponentFinderStrategy.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.structurizr.analysis; - -import com.structurizr.model.Component; - -import javax.ejb.Singleton; -import javax.ejb.Stateful; -import javax.ejb.Stateless; -import javax.inject.Named; -import javax.websocket.server.ServerEndpoint; -import javax.ws.rs.Path; -import java.util.HashSet; -import java.util.Set; - -public class JavaEEComponentFinderStrategy extends AbstractComponentFinderStrategy { - - public JavaEEComponentFinderStrategy() { - super(new FirstImplementationOfInterfaceSupportingTypesStrategy()); - } - - public JavaEEComponentFinderStrategy(SupportingTypesStrategy... strategies) { - super(strategies); - } - - @Override - protected Set doFindComponents() { - Set components = new HashSet<>(); - - components.addAll(findClassesWithAnnotation(Path.class, "JAX-RS web service")); - components.addAll(findClassesWithAnnotation(ServerEndpoint.class, "Websocket endpoint")); - components.addAll(findClassesWithAnnotation(Stateless.class, "Stateless session bean")); - components.addAll(findClassesWithAnnotation(Stateful.class, "Stateful session bean")); - components.addAll(findClassesWithAnnotation(Singleton.class, "Singleton session bean")); - components.addAll(findClassesWithAnnotation(Named.class, "Named bean")); - - return components; - } - -} diff --git a/structurizr-mermaid/build.gradle b/structurizr-mermaid/build.gradle deleted file mode 100644 index f6bb34b2d..000000000 --- a/structurizr-mermaid/build.gradle +++ /dev/null @@ -1,22 +0,0 @@ -dependencies { - - compile project(':structurizr-core') - compile 'com.fasterxml.jackson.core:jackson-databind:2.4.0' - - testCompile 'junit:junit:4.12' - testCompile 'org.assertj:assertj-core:3.4.0' - -} - -sourceSets { - main { - java { - srcDir 'src' - } - } - test { - java { - srcDir 'test/unit' - } - } -} \ No newline at end of file diff --git a/structurizr-mermaid/src/com/structurizr/io/mermaid/MermaidEncoder.java b/structurizr-mermaid/src/com/structurizr/io/mermaid/MermaidEncoder.java deleted file mode 100644 index 7672bda62..000000000 --- a/structurizr-mermaid/src/com/structurizr/io/mermaid/MermaidEncoder.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.structurizr.io.mermaid; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import java.nio.charset.StandardCharsets; -import java.util.Base64; - -public class MermaidEncoder { - - public String encode(String mermaidDefinition) throws Exception { - ObjectMapper objectMapper = new ObjectMapper(); - String json = objectMapper.writeValueAsString(new MermaidCode(mermaidDefinition)); - String base64 = Base64.getEncoder().encodeToString(json.getBytes(StandardCharsets.UTF_8)); - - // this encoded version is used in URLs so: - // - strip trailing = characters - base64 = base64.replaceAll("=", ""); - - // and switch + characters - base64 = base64.replaceAll("\\+", "-"); - - return base64; - } - -} - -class MermaidCode { - - private String code; - private Mermaid mermaid = new Mermaid(); - - public MermaidCode(String code) { - this.code = code; - } - - public String getCode() { - return code; - } - - public Mermaid getMermaid() { - return mermaid; - } - -} - -class Mermaid { - - private String theme = "default"; - - public String getTheme() { - return theme; - } - -} \ No newline at end of file diff --git a/structurizr-mermaid/src/com/structurizr/io/mermaid/MermaidWriter.java b/structurizr-mermaid/src/com/structurizr/io/mermaid/MermaidWriter.java deleted file mode 100644 index 76d94e0bd..000000000 --- a/structurizr-mermaid/src/com/structurizr/io/mermaid/MermaidWriter.java +++ /dev/null @@ -1,520 +0,0 @@ -package com.structurizr.io.mermaid; - -import com.structurizr.Workspace; -import com.structurizr.model.*; -import com.structurizr.util.StringUtils; -import com.structurizr.view.*; - -import java.io.IOException; -import java.io.StringWriter; -import java.io.Writer; -import java.net.URI; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import static java.lang.String.format; -import static java.util.Collections.emptyList; - -/** - * A writer that outputs diagram definitions that can be used to create diagrams - * using mermaid (https://mermaidjs.github.io). - * - * System landscape, system context, container, component, dynamic and deployment diagrams are supported. - * Deployment node -> deployment node relationships are not rendered. - */ -public class MermaidWriter { - - private boolean useSequenceDiagrams = false; - - /** - * Creates a new PlantUMLWriter, with some default skin params. - */ - public MermaidWriter() { - } - - public boolean isUseSequenceDiagrams() { - return useSequenceDiagrams; - } - - public void setUseSequenceDiagrams(boolean useSequenceDiagrams) { - this.useSequenceDiagrams = useSequenceDiagrams; - } - - /** - * Writes the views in the given workspace as PlantUML definitions, to the specified writer. - * - * @param workspace the workspace containing the views to be written - * @param writer the Writer to write to - */ - public void write(Workspace workspace, Writer writer) { - if (workspace == null) { - throw new IllegalArgumentException("A workspace must be provided."); - } - - if (writer == null) { - throw new IllegalArgumentException("A writer must be provided."); - } - - workspace.getViews().getSystemLandscapeViews().forEach(v -> write(v, writer)); - workspace.getViews().getSystemContextViews().forEach(v -> write(v, writer)); - workspace.getViews().getContainerViews().forEach(v -> write(v, writer)); - workspace.getViews().getComponentViews().forEach(v -> write(v, writer)); - workspace.getViews().getDynamicViews().forEach(v -> write(v, writer)); - workspace.getViews().getDeploymentViews().forEach(v -> write(v, writer)); - } - - /** - * Write the views in the given workspace as PlantUML definitions, to stdout. - * - * @param workspace the workspace containing the views to be written - */ - public void toStdOut(Workspace workspace) { - if (workspace == null) { - throw new IllegalArgumentException("A workspace must be provided."); - } - - StringWriter stringWriter = new StringWriter(); - write(workspace, stringWriter); - System.out.println(stringWriter.toString()); - } - - /** - * Creates PlantUML diagram definitions based upon the specified workspace, returning them as strings. - * - * @param workspace the workspace containing the views to be written - * @return an array of PlantUML diagram definitions, one per view - */ - public String[] toString(Workspace workspace) { - if (workspace == null) { - throw new IllegalArgumentException("A workspace must be provided."); - } - - StringWriter stringWriter = new StringWriter(); - write(workspace, stringWriter); - - String diagrams = stringWriter.toString(); - if (diagrams != null && diagrams.contains("graph TB")) { - return stringWriter.toString().split("(?=graph TB)"); - } else { - return new String[0]; - } - } - - /** - * Gets a single view as a mermaid diagram definition. - * - * @param view the view to write - * @return the Mermaid definition as a String - */ - public String toString(View view) { - StringWriter stringWriter = new StringWriter(); - write(view, stringWriter); - return stringWriter.toString(); - } - - /** - * Writes a single view as a PlantUML diagram definition, to the specified writer. - * - * @param view the view to write - * @param writer the Writer to write the PlantUML definition to - */ - public void write(View view, Writer writer) { - if (view == null) { - throw new IllegalArgumentException("A view must be provided."); - } - - if (writer == null) { - throw new IllegalArgumentException("A writer must be provided."); - } - - if (SystemLandscapeView.class.isAssignableFrom(view.getClass())) { - write((SystemLandscapeView) view, writer); - } else if (SystemContextView.class.isAssignableFrom(view.getClass())) { - write((SystemContextView) view, writer); - } else if (ContainerView.class.isAssignableFrom(view.getClass())) { - write((ContainerView) view, writer); - } else if (ComponentView.class.isAssignableFrom(view.getClass())) { - write((ComponentView) view, writer); - } else if (DynamicView.class.isAssignableFrom(view.getClass())) { - write((DynamicView) view, writer); - } else if (DeploymentView.class.isAssignableFrom(view.getClass())) { - write((DeploymentView) view, writer); - } - } - - protected void write(SystemLandscapeView view, Writer writer) { - writeSystemLandscapeOrContextView(view, writer, view.isEnterpriseBoundaryVisible()); - } - - protected void write(SystemContextView view, Writer writer) { - writeSystemLandscapeOrContextView(view, writer, view.isEnterpriseBoundaryVisible()); - } - - private void writeSystemLandscapeOrContextView(View view, Writer writer, boolean showEnterpriseBoundary) { - try { - writeHeader(view, writer); - - boolean enterpriseBoundaryVisible; - enterpriseBoundaryVisible = - showEnterpriseBoundary && - (view.getElements().stream().map(ElementView::getElement).anyMatch(e -> e instanceof Person && ((Person)e).getLocation() == Location.Internal) || - view.getElements().stream().map(ElementView::getElement).anyMatch(e -> e instanceof SoftwareSystem && ((SoftwareSystem)e).getLocation() == Location.Internal)); - - view.getElements().stream() - .map(ElementView::getElement) - .filter(e -> e instanceof Person && ((Person)e).getLocation() != Location.Internal) - .sorted((e1, e2) -> e1.getName().compareTo(e2.getName())) - .forEach(e -> write(view, e, writer, 0)); - - view.getElements().stream() - .map(ElementView::getElement) - .filter(e -> e instanceof SoftwareSystem && ((SoftwareSystem)e).getLocation() != Location.Internal) - .sorted((e1, e2) -> e1.getName().compareTo(e2.getName())) - .forEach(e -> write(view, e, writer, 0)); - - if (enterpriseBoundaryVisible) { - String name = view.getModel().getEnterprise() != null ? view.getModel().getEnterprise().getName() : "Enterprise"; - writer.write(" subgraph \"" + name + "\""); - writer.write(System.lineSeparator()); - } - - view.getElements().stream() - .map(ElementView::getElement) - .filter(e -> e instanceof Person && ((Person)e).getLocation() == Location.Internal) - .sorted((e1, e2) -> e1.getName().compareTo(e2.getName())) - .forEach(e -> write(view, e, writer, enterpriseBoundaryVisible ? 1 : 0)); - - view.getElements().stream() - .map(ElementView::getElement) - .filter(e -> e instanceof SoftwareSystem && ((SoftwareSystem)e).getLocation() == Location.Internal) - .sorted((e1, e2) -> e1.getName().compareTo(e2.getName())) - .forEach(e -> write(view, e, writer, enterpriseBoundaryVisible ? 1 : 0)); - - if (enterpriseBoundaryVisible) { - writer.write(" end"); - writer.write(System.lineSeparator()); - } - - writeRelationships(view, writer); - - writeFooter(writer); - } catch (IOException e) { - e.printStackTrace(); - } - } - - protected void write(ContainerView view, Writer writer) { - try { - writeHeader(view, writer); - - view.getElements().stream() - .filter(ev -> !(ev.getElement() instanceof Container)) - .map(ElementView::getElement) - .sorted((e1, e2) -> e1.getName().compareTo(e2.getName())) - .forEach(e -> write(view, e, writer, 0)); - - writer.write(" subgraph \"" + view.getSoftwareSystem().getName() + "\""); - writer.write(System.lineSeparator()); - - view.getElements().stream() - .filter(ev -> ev.getElement() instanceof Container) - .map(ElementView::getElement) - .sorted((e1, e2) -> e1.getName().compareTo(e2.getName())) - .forEach(e -> write(view, e, writer, 1)); - - writer.write(" end"); - writer.write(System.lineSeparator()); - - writeRelationships(view, writer); - - writeFooter(writer); - } catch (IOException e) { - e.printStackTrace(); - } - } - - protected void write(ComponentView view, Writer writer) { - try { - writeHeader(view, writer); - - view.getElements().stream() - .filter(ev -> !(ev.getElement() instanceof Component)) - .map(ElementView::getElement) - .sorted((e1, e2) -> e1.getName().compareTo(e2.getName())) - .forEach(e -> write(view, e, writer, 0)); - - writer.write(" subgraph \"" + view.getContainer().getName() + "\""); - writer.write(System.lineSeparator()); - - view.getElements().stream() - .filter(ev -> ev.getElement() instanceof Component) - .map(ElementView::getElement) - .sorted((e1, e2) -> e1.getName().compareTo(e2.getName())) - .forEach(e -> write(view, e, writer, 1)); - - writer.write(" end"); - writer.write(System.lineSeparator()); - - writeRelationships(view, writer); - - writeFooter(writer); - } catch (IOException e) { - e.printStackTrace(); - } - } - - protected void write(DynamicView view, Writer writer) { - try { - writeHeader(view, writer); - - if (useSequenceDiagrams) { - throw new UnsupportedOperationException("Not implemented yet."); - } else { - view.getElements().stream() - .map(ElementView::getElement) - .sorted((e1, e2) -> e1.getName().compareTo(e2.getName())). - forEach(e -> write(view, e, writer, 0)); - } - view.getRelationships().stream() - .sorted((rv1, rv2) -> (rv1.getOrder().compareTo(rv2.getOrder()))) - .forEach(relationship -> { - try { - writer.write( - format(" %s-->|\"%s. %s
%s\"|%s", - idOf(relationship.getRelationship().getSource()), - relationship.getOrder(), - lines(hasValue(relationship.getDescription()) ? relationship.getDescription() : hasValue(relationship.getRelationship().getDescription()) ? relationship.getRelationship().getDescription() : ""), - !StringUtils.isNullOrEmpty(relationship.getRelationship().getTechnology()) ? "[" + relationship.getRelationship().getTechnology() + "]" : "", - idOf(relationship.getRelationship().getDestination()) - ) - ); - - writer.write(System.lineSeparator()); - } catch (IOException e) { - e.printStackTrace(); - } - }); - - writeFooter(writer); - } catch (IOException e) { - e.printStackTrace(); - } - } - - protected void write(DeploymentView view, Writer writer) { - try { - writeHeader(view, writer); - - view.getElements().stream() - .filter(ev -> ev.getElement() instanceof DeploymentNode && ev.getElement().getParent() == null) - .map(ev -> (DeploymentNode)ev.getElement()) - .sorted((e1, e2) -> e1.getName().compareTo(e2.getName())) - .forEach(e -> write(view, e, writer, 1)); - - writeRelationships(view, writer); - - writeFooter(writer); - } catch (IOException e) { - e.printStackTrace(); - } - } - - protected void write(View view, DeploymentNode deploymentNode, Writer writer, int indent) { - try { - writer.write( - format("%ssubgraph \"%s\"", - calculateIndent(indent), - deploymentNode.getName() - ) - ); - - writer.write(System.lineSeparator()); - - for (DeploymentNode child : deploymentNode.getChildren()) { - write(view, child, writer, indent+1); - } - - for (ContainerInstance containerInstance : deploymentNode.getContainerInstances()) { - write(view, containerInstance, writer, indent+1); - } - - writer.write( - format("%send", calculateIndent(indent)) - ); - writer.write(System.lineSeparator()); - } catch (IOException e) { - e.printStackTrace(); - } - } - - protected String calculateIndent(int indent) { - StringBuilder buf = new StringBuilder(); - - for (int i = 0; i < indent; i++) { - buf.append(" "); - } - - return buf.toString(); - } - - protected void write(View view, Element element, Writer writer, int indent) { - try { - final String separator = System.lineSeparator(); - - if (element instanceof ContainerInstance) { - Container container = ((ContainerInstance) element).getContainer(); - writer.write(format(" %s%s(\"
%s
[%s]
%s
\")", - calculateIndent(indent), - idOf(element), - backgroundOf(view, container), - colorOf(view, container), - container.getName(), typeOf(container), lines(container.getDescription()) - )); - } else { - writer.write(format(" %s%s(\"
%s
[%s]
%s
\")", - calculateIndent(indent), - idOf(element), - backgroundOf(view, element), - colorOf(view, element), - element.getName(), typeOf(element), lines(element.getDescription()) - )); - } - writer.write(format("%s", separator)); - - if (!StringUtils.isNullOrEmpty(element.getUrl())) { - writer.write(format("click %s %s \"%s\"", idOf(element), element.getUrl(), element.getUrl())); - writer.write(format("%s", separator)); - } - - if (element instanceof ContainerInstance) { - Container container = ((ContainerInstance) element).getContainer(); - writer.write(format(" %sstyle %s fill:%s", calculateIndent(indent),idOf(element), backgroundOf(view, container))); - } else { - writer.write(format(" %sstyle %s fill:%s", calculateIndent(indent),idOf(element), backgroundOf(view, element))); - } - - writer.write(format("%s", separator)); - } catch (IOException e) { - e.printStackTrace(); - } - } - - protected void writeSequenceElement(View view, Element element, Writer writer, boolean indent, String type) throws IOException { - writer.write(format("%s%s \"%s\" as %s <<%s>> %s%s", - indent ? " " : "", - type, - element.getName(), - idOf(element), - typeOf(element), - backgroundOf(view, element), - System.lineSeparator())); - } - - protected String lines(final String text) { - StringBuilder buf = new StringBuilder(); - if (text != null) { - final String[] words = text.trim().split("\\s+"); - - final StringBuilder line = new StringBuilder(); - for (final String word : words) { - if (line.length() == 0) { - line.append(word); - } else if (line.length() + word.length() + 1 < 30) { - line.append(' ').append(word); - } else { - buf.append(line.toString()); - buf.append("
"); - line.setLength(0); - line.append(word); - } - } - if (line.length() > 0) { - buf.append(line.toString()); - } - } - - return buf.toString(); - } - - protected String backgroundOf(View view, Element element) { - return view.getViewSet().getConfiguration().getStyles().findElementStyle(element).getBackground(); - } - - protected String colorOf(View view, Element element) { - return view.getViewSet().getConfiguration().getStyles().findElementStyle(element).getColor(); - } - - protected RelationshipStyle relationshipStyleOf(View view, Relationship relationship) { - return view.getViewSet().getConfiguration().getStyles().findRelationshipStyle(relationship); - } - - protected void writeRelationships(View view, Writer writer) { - view.getRelationships().stream() - .map(RelationshipView::getRelationship) - .filter(r -> !(r.getSource() instanceof DeploymentNode || r.getDestination() instanceof DeploymentNode)) - .sorted((r1, r2) -> (r1.getSource().getName() + r1.getDestination().getName()).compareTo(r2.getSource().getName() + r2.getDestination().getName())) - .forEach(r -> writeRelationship(view, r, writer)); - } - - protected void writeRelationship(View view, Relationship relationship, Writer writer) { - try { - RelationshipStyle style = relationshipStyleOf(view, relationship); - // solid: A-- text -->B - // dotted: A-. text .->B - - if (style.getDashed() == null) { - style.setDashed(true); - } - - writer.write( - format(" %s-%s \"%s
%s\" %s->%s", - idOf(relationship.getSource()), - style.getDashed() ? "." : "-", - lines(relationship.getDescription()), - !StringUtils.isNullOrEmpty(relationship.getTechnology()) ? "[" + relationship.getTechnology() + "]" : "", - style.getDashed() ? "." : "-", - idOf(relationship.getDestination()) - ) - ); - writer.write(System.lineSeparator()); - } catch (IOException e) { - e.printStackTrace(); - } - } - - protected String idOf(Element e) { - return e.getId(); - } - - protected String typeOf(Element e) { - if (e instanceof SoftwareSystem) { - return "Software System"; - } else if (e instanceof Component) { - Component component = (Component)e; - return hasValue(component.getTechnology()) ? component.getTechnology() : "Component"; - } else if (e instanceof DeploymentNode) { - DeploymentNode deploymentNode = (DeploymentNode)e; - return hasValue(deploymentNode.getTechnology()) ? deploymentNode.getTechnology() : "Deployment Node"; - } else if (e instanceof ContainerInstance) { - return "Container"; - } else { - return e.getClass().getSimpleName(); - } - } - - protected boolean hasValue(String s) { - return s != null && s.trim().length() > 0; - } - - protected void writeHeader(View view, Writer writer) throws IOException { - writer.write("graph TB"); - writer.write(System.lineSeparator()); - } - - protected void writeFooter(Writer writer) throws IOException { - writer.write(System.lineSeparator()); - } - -} diff --git a/structurizr-mermaid/test/unit/com/structurizr/io/mermaid/MermaidEncoderTests.java b/structurizr-mermaid/test/unit/com/structurizr/io/mermaid/MermaidEncoderTests.java deleted file mode 100644 index 434b7621f..000000000 --- a/structurizr-mermaid/test/unit/com/structurizr/io/mermaid/MermaidEncoderTests.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.structurizr.io.mermaid; - -import org.junit.Test; - -import static org.junit.Assert.assertEquals; - -public class MermaidEncoderTests { - - @Test - public void test_encode() throws Exception { - assertEquals( - "eyJjb2RlIjoiZ3JhcGggVERcbkFbQ2hyaXN0bWFzXSAtLT58R2V0IG1vbmV5fCBCKEdvIHNob3BwaW5nKVxuQiAtLT4gQ3tMZXQgbWUgdGhpbmt9XG5DIC0tPnxPbmV8IERbTGFwdG9wXVxuQyAtLT58VHdvfCBFW2lQaG9uZV1cbkMgLS0-fFRocmVlfCBGW2ZhOmZhLWNhciBDYXJdXG4iLCJtZXJtYWlkIjp7InRoZW1lIjoiZGVmYXVsdCJ9fQ", - new MermaidEncoder().encode("graph TD\n" + - "A[Christmas] -->|Get money| B(Go shopping)\n" + - "B --> C{Let me think}\n" + - "C -->|One| D[Laptop]\n" + - "C -->|Two| E[iPhone]\n" + - "C -->|Three| F[fa:fa-car Car]\n")); - } - -} \ No newline at end of file diff --git a/structurizr-mermaid/test/unit/com/structurizr/io/mermaid/MermaidWriterTests.java b/structurizr-mermaid/test/unit/com/structurizr/io/mermaid/MermaidWriterTests.java deleted file mode 100644 index 8a043888f..000000000 --- a/structurizr-mermaid/test/unit/com/structurizr/io/mermaid/MermaidWriterTests.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.structurizr.io.mermaid; - -import org.junit.Test; - -public class MermaidWriterTests { - - @Test - public void test() { - - } - -} diff --git a/structurizr-plantuml/build.gradle b/structurizr-plantuml/build.gradle deleted file mode 100644 index 9a20eb465..000000000 --- a/structurizr-plantuml/build.gradle +++ /dev/null @@ -1,21 +0,0 @@ -dependencies { - - compile project(':structurizr-core') - - testCompile 'junit:junit:4.12' - testCompile 'org.assertj:assertj-core:3.4.0' - -} - -sourceSets { - main { - java { - srcDir 'src' - } - } - test { - java { - srcDir 'test/unit' - } - } -} \ No newline at end of file diff --git a/structurizr-plantuml/src/com/structurizr/io/plantuml/C4PlantUMLWriter.java b/structurizr-plantuml/src/com/structurizr/io/plantuml/C4PlantUMLWriter.java deleted file mode 100644 index 0d5ff0984..000000000 --- a/structurizr-plantuml/src/com/structurizr/io/plantuml/C4PlantUMLWriter.java +++ /dev/null @@ -1,239 +0,0 @@ -package com.structurizr.io.plantuml; - -import com.structurizr.model.*; -import com.structurizr.view.View; - -import java.io.IOException; -import java.io.Writer; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.logging.Level; - -import static java.lang.String.format; - -/** - * This writer extends the classical one to use the C4-PlantUML sprite library - * available on GitHub. - * - * To make full use of that sprite library, we use Structurizr properties to - * tweak rendering. - * - * It is possible to change relationships directions and rendering using the - * {@link #C4_LAYOUT_DIRECTION} and {@link #C4_LAYOUT_MODE} mode properties. - * - * @see https://github.com/RicardoNiepel/C4-PlantUML - * @author nicolas-delsaux - * - */ -public class C4PlantUMLWriter extends PlantUMLWriter { - /** - * This property indicates to C4-PlantUML library which relationship type to - * use. Possible values are given in the {@link Directions} enum - */ - public static final String C4_LAYOUT_DIRECTION = "c4:layout:direction"; - /** - * This property indicates to C4-PlantUML library which relationship mode to - * use. Possible values are given in the {@link RelationshipModes} enum - */ - public static final String C4_LAYOUT_MODE = "c4:layout:mode"; - /** - * Defines the {@link Type} of component. - */ - public static final String C4_ELEMENT_TYPE = "c4:element:type"; - - public static enum Type { - Default, Db - } - - public static enum Directions { - Up, Down, Right, Left - } - - public static enum RelationshipModes { - Rel, Neighbor, Back, Back_Neighbor, Lay - } - - public static class NoMacroFound extends RuntimeException { - - } - - private abstract class C4ElementWriter { - - public void write(View view, WrittenElement element, Writer writer, String prefix) throws IOException { - final String id = idOf(element); - final String separator = System.lineSeparator(); - doWrite(view, element, writer, prefix, id, separator); - } - - abstract void doWrite(View view, WrittenElement element, Writer writer, String prefix, String id, - String separator) throws IOException; - - } - - private class PersonWriter extends C4ElementWriter { - - @Override - void doWrite(View view, Person element, Writer writer, String prefix, String id, String separator) - throws IOException { - String macro = null; - switch(element.getLocation()) { - case External: - macro = "Person_Ext"; - break; - default: - macro = "Person"; - } - writer.write(format("%s%s(%s, \"%s\", \"%s\")%s", prefix, - macro, id, element.getName(), - element.getDescription(), separator)); - } - } - - private class SoftwareSystemWriter extends C4ElementWriter { - @Override - void doWrite(View view, SoftwareSystem element, Writer writer, String prefix, String id, String separator) - throws IOException { - boolean internal = !element.getLocation().equals(Location.External); - Type type = Type.valueOf(element.getProperties().getOrDefault(C4_ELEMENT_TYPE, Type.Default.name())); - String macro = String.format("System%s%s", - type==Type.Default ? "" : type.name(), - internal ? "" : "_Ext"); - writer.write(format("%s%s(%s, \"%s\", \"%s\")%s", prefix, macro, id, element.getName(), - element.getDescription(), separator)); - } - } - - private class ContainerWriter extends C4ElementWriter { - @Override - void doWrite(View view, Container element, Writer writer, String prefix, String id, String separator) - throws IOException { - Type type = Type.valueOf(element.getProperties().getOrDefault(C4_ELEMENT_TYPE, Type.Default.name())); - String macro = String.format("Container%s", - type==Type.Default ? "" : type.name()); - writer.write(format("%s%s(%s, \"%s\", \"%s\", \"%s\")%s", prefix, macro, id, element.getName(), - element.getTechnology(), element.getDescription(), separator)); - } - } - - private class ComponentWriter extends C4ElementWriter { - @Override - void doWrite(View view, Component element, Writer writer, String prefix, String id, String separator) - throws IOException { - Type type = Type.valueOf(element.getProperties().getOrDefault(C4_ELEMENT_TYPE, Type.Default.name())); - String macro = String.format("Component%s", - type==Type.Default ? "" : type.name()); - writer.write(format("%s%s(%s, \"%s\", \"%s\", \"%s\")%s", prefix, macro, id, element.getName(), - element.getTechnology(), element.getDescription(), separator)); - } - } - - private class ContainerInstanceWriter extends C4ElementWriter { - @Override - void doWrite(View view, ContainerInstance element, Writer writer, String prefix, String id, String separator) - throws IOException { - Type type = Type.valueOf(element.getProperties().getOrDefault(C4_ELEMENT_TYPE, - element.getContainer().getProperties().getOrDefault(C4_ELEMENT_TYPE,Type.Default.name()) - )); - String macro = String.format("Container%s", - type==Type.Default ? "" : type.name()); - writer.write(format("%s%s(%s, \"%s\", \"%s\", \"%s\")%s", prefix, macro, id, - element.getContainer().getName(), element.getContainer().getTechnology(), - element.getContainer().getDescription(), separator)); - } - } - - private static final java.util.logging.Logger logger = java.util.logging.Logger - .getLogger(C4PlantUMLWriter.class.getName()); - - public C4PlantUMLWriter() { - super(); - try { - addIncludeURL(new URI("https://raw.githubusercontent.com/RicardoNiepel/C4-PlantUML/master/C4.puml")); - addIncludeURL( - new URI("https://raw.githubusercontent.com/RicardoNiepel/C4-PlantUML/master/C4_Context.puml")); - addIncludeURL( - new URI("https://raw.githubusercontent.com/RicardoNiepel/C4-PlantUML/master/C4_Container.puml")); - addIncludeURL( - new URI("https://raw.githubusercontent.com/RicardoNiepel/C4-PlantUML/master/C4_Component.puml")); - } catch (URISyntaxException e) { - logger.log(Level.SEVERE, "Using C4-PlantUML shoulld not trigger URI error", e); - } - } - - @Override - protected void write(View view, ContainerInstance container, Writer writer, int indent) { - try { - new ContainerInstanceWriter().write(view, container, writer, calculateIndent(indent)); - } catch (IOException e) { - e.printStackTrace(); - } - } - - /** - * We replace the write element method to use macros provided by C4-PlantUML - */ - protected void write(View view, Element element, Writer writer, boolean indent) { - try { - final String prefix = indent ? " " : ""; - final String id = idOf(element); - final String separator = System.lineSeparator(); - getWriterFor(element).write(view, element, writer, prefix); - } catch (NoMacroFound noMacro) { - super.write(view, element, writer, indent); - } catch (IOException e) { - e.printStackTrace(); - } - } - - private C4ElementWriter getWriterFor(Element element) { - if (element instanceof Person) { - return new PersonWriter(); - } else if (element instanceof SoftwareSystem) { - return new SoftwareSystemWriter(); - } else if (element instanceof Container) { - return new ContainerWriter(); - } else if (element instanceof Component) { - return new ComponentWriter(); - } - throw new NoMacroFound(); - } - - @Override - protected void writeRelationship(View view, Relationship relationship, Writer writer) { - try { - final String separator = System.lineSeparator(); - String relationshipMacro = null; - RelationshipModes mode = RelationshipModes.Rel; - if (relationship.getProperties().containsKey(C4_LAYOUT_MODE)) { - mode = RelationshipModes.valueOf(relationship.getProperties().get(C4_LAYOUT_MODE)); - } - switch (mode) { - case Lay: - case Rel: - relationshipMacro = mode.name(); - if (relationship.getProperties().containsKey(C4_LAYOUT_DIRECTION)) { - Directions direction = Directions.valueOf(relationship.getProperties().get(C4_LAYOUT_DIRECTION)); - relationshipMacro = String.format("%s_%s", relationshipMacro, direction); - } - break; - default: - relationshipMacro = String.format("%s_%s", relationshipMacro, mode); - } - if (relationship.getDescription() == null) { - writer.write(format("%s(%s, %s)%s", relationshipMacro, idOf(relationship.getSource()), - idOf(relationship.getDestination()), separator)); - } else { - if (relationship.getTechnology() == null) { - writer.write(format("%s(%s, %s, '%s')%s", relationshipMacro, idOf(relationship.getSource()), - idOf(relationship.getDestination()), relationship.getDescription(), separator)); - } else { - writer.write(format("%s(%s, %s, '%s', %s)%s", relationshipMacro, idOf(relationship.getSource()), - idOf(relationship.getDestination()), relationship.getDescription(), - relationship.getTechnology(), separator)); - } - } - } catch (IOException e) { - e.printStackTrace(); - } - } -} \ No newline at end of file diff --git a/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLDiagram.java b/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLDiagram.java deleted file mode 100644 index 4f7ee5cf1..000000000 --- a/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLDiagram.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.structurizr.io.plantuml; - -public class PlantUMLDiagram { - - private String key; - private String name; - private String definition; - - PlantUMLDiagram(String key, String name, String definition) { - this.key = key; - this.name = name; - this.definition = definition; - } - - public String getKey() { - return key; - } - - public String getName() { - return name; - } - - public String getDefinition() { - return definition; - } - -} \ No newline at end of file diff --git a/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLEncoder.java b/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLEncoder.java deleted file mode 100644 index 91f95f80c..000000000 --- a/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLEncoder.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.structurizr.io.plantuml; - -import java.io.ByteArrayOutputStream; -import java.nio.charset.StandardCharsets; -import java.util.zip.Deflater; -import java.util.zip.DeflaterOutputStream; - -/** - * A Java implementation of http://plantuml.com/code-javascript-synchronous - * that uses Java's built-in Deflate algorithm. - */ -public class PlantUMLEncoder { - - public String encode(String plantUMLDefinition) throws Exception { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION, true); - - DeflaterOutputStream dos = new DeflaterOutputStream(baos, deflater, true); - dos.write(plantUMLDefinition.getBytes(StandardCharsets.UTF_8)); - dos.finish(); - - return encode(baos.toByteArray()); - } - - private String encode(byte[] bytes) { - StringBuilder buf = new StringBuilder(); - for (int i = 0; i < bytes.length; i += 3) { - int b1 = (bytes[i]) & 0xFF; - int b2 = (i + 1 < bytes.length ? bytes[i + 1] : (byte)0) & 0xFF; - int b3 = (i + 2 < bytes.length ? bytes[i + 2] : (byte)0) & 0xFF; - - append3bytes(buf, b1, b2, b3); - } - - return buf.toString(); - } - - private char encode6bit(byte b) { - if (b < 10) { - return (char) ('0' + b); - } - b -= 10; - if (b < 26) { - return (char) ('A' + b); - } - b -= 26; - if (b < 26) { - return (char) ('a' + b); - } - b -= 26; - if (b == 0) { - return '-'; - } - if (b == 1) { - return '_'; - } - - return '?'; - } - - private void append3bytes(StringBuilder buf, int b1, int b2, int b3) { - int c1 = b1 >> 2; - int c2 = (b1 & 0x3) << 4 | b2 >> 4; - int c3 = (b2 & 0xF) << 2 | b3 >> 6; - int c4 = b3 & 0x3F; - - buf.append(encode6bit((byte)(c1 & 0x3F))); - buf.append(encode6bit((byte)(c2 & 0x3F))); - buf.append(encode6bit((byte)(c3 & 0x3F))); - buf.append(encode6bit((byte)(c4 & 0x3F))); - } - -} \ No newline at end of file diff --git a/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLWriter.java b/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLWriter.java deleted file mode 100644 index a1e34c9a2..000000000 --- a/structurizr-plantuml/src/com/structurizr/io/plantuml/PlantUMLWriter.java +++ /dev/null @@ -1,751 +0,0 @@ -package com.structurizr.io.plantuml; - -import com.structurizr.Workspace; -import com.structurizr.model.*; -import com.structurizr.util.StringUtils; -import com.structurizr.view.*; - -import java.io.IOException; -import java.io.StringWriter; -import java.io.Writer; -import java.net.URI; -import java.util.*; - -import static java.lang.String.format; -import static java.util.Collections.emptyList; - -/** - * A writer that outputs diagram definitions that can be used to create diagrams - * using PlantUML (http://plantuml.com/plantuml/). - * - * System landscape, system context, container, component, dynamic and deployment diagrams are supported. - * - * Note: This won't work if you have two elements named the same on a diagram. - */ -public class PlantUMLWriter { - - private final Map skinParams = new LinkedHashMap<>(); - private final List includes = new ArrayList<>(); - - /** maximum diagram width or height; defaults to 2000px to match public plantuml.com installation */ - private int sizeLimit = 2000; - private boolean includeNotesForActors = true; - private boolean useSequenceDiagrams = false; - private String direction; - - /** - * Creates a new PlantUMLWriter, with some default skin params. - */ - public PlantUMLWriter() { - // add some default skin params - addSkinParam("shadowing", "false"); - addSkinParam("arrowColor", "#707070"); - addSkinParam("actorBorderColor", "#707070"); - addSkinParam("componentBorderColor", "#707070"); - addSkinParam("rectangleBorderColor", "#707070"); - addSkinParam("noteBackgroundColor", "#ffffff"); - addSkinParam("noteBorderColor", "#707070"); - } - - protected List getIncludes() { - return includes; - } - - public void addIncludeFile(String file) { - addIncludeFile(file, null); - } - - public void addIncludeFile(String file, int id) { - addIncludeFile(file, String.valueOf(id)); - } - - public void addIncludeFile(String file, String id) { - if (id==null) { - includes.add(format("!include %s", file)); - } else { - includes.add(format("!include %s!%s", file, id)); - } - } - - public void addIncludeURL(URI file) { - addIncludeURL(file, null); - } - - public void addIncludeURL(URI file, int id) { - addIncludeURL(file, String.valueOf(id)); - } - - public void addIncludeURL(URI file, String id) { - if (id==null) { - includes.add(format("!includeurl %s", file)); - } else { - includes.add(format("!includeurl %s!%s", file, id)); - } - } - - public void clearIncludes() { - includes.clear(); - } - - protected Map getSkinParams() { - return skinParams; - } - - public void addSkinParam(String name, String value) { - skinParams.put(name, value); - } - - public void clearSkinParams() { - skinParams.clear(); - } - - protected boolean isIncludeNotesForActors() { - return includeNotesForActors; - } - - public void setIncludeNotesForActors(boolean includeNotesForActors) { - this.includeNotesForActors = includeNotesForActors; - } - - public boolean isUseSequenceDiagrams() { - return useSequenceDiagrams; - } - - public void setUseSequenceDiagrams(boolean useSequenceDiagrams) { - this.useSequenceDiagrams = useSequenceDiagrams; - } - - protected int getSizeLimit() { - return sizeLimit; - } - - public void setSizeLimit(int sizeLimit) { - this.sizeLimit = sizeLimit; - } - - public void setDirection(String direction) { - this.direction = direction; - } - - /** - * Writes the views in the given workspace as PlantUML definitions, to the specified writer. - * - * @param workspace the workspace containing the views to be written - * @param writer the Writer to write to - */ - public void write(Workspace workspace, Writer writer) { - if (workspace == null) { - throw new IllegalArgumentException("A workspace must be provided."); - } - - if (writer == null) { - throw new IllegalArgumentException("A writer must be provided."); - } - - workspace.getViews().getSystemLandscapeViews().forEach(v -> write(v, writer)); - workspace.getViews().getSystemContextViews().forEach(v -> write(v, writer)); - workspace.getViews().getContainerViews().forEach(v -> write(v, writer)); - workspace.getViews().getComponentViews().forEach(v -> write(v, writer)); - workspace.getViews().getDynamicViews().forEach(v -> write(v, writer)); - workspace.getViews().getDeploymentViews().forEach(v -> write(v, writer)); - } - - /** - * Write the views in the given workspace as PlantUML definitions, to stdout. - * - * @param workspace the workspace containing the views to be written - */ - public void toStdOut(Workspace workspace) { - if (workspace == null) { - throw new IllegalArgumentException("A workspace must be provided."); - } - - StringWriter stringWriter = new StringWriter(); - write(workspace, stringWriter); - System.out.println(stringWriter.toString()); - } - - /** - * Creates PlantUML diagram definitions based upon the specified workspace, returning them as strings. - * - * @param workspace the workspace containing the views to be written - * @return an array of PlantUML diagram definitions, one per view - */ - public String[] toString(Workspace workspace) { - if (workspace == null) { - throw new IllegalArgumentException("A workspace must be provided."); - } - - StringWriter stringWriter = new StringWriter(); - write(workspace, stringWriter); - - String diagrams = stringWriter.toString(); - if (diagrams != null && diagrams.contains("@startuml")) { - return stringWriter.toString().split("(?=@startuml)"); - } else { - return new String[0]; - } - } - - /** - * Gets a single view as a PlantUML diagram definition. - * - * @param view the view to write - * @return the PlantUML definition as a String - */ - public String toString(View view) { - StringWriter stringWriter = new StringWriter(); - write(view, stringWriter); - return stringWriter.toString(); - } - - /** - * Creates PlantUML diagram definitions based upon the specified workspace. - * - * @param workspace the workspace containing the views to be written - * @return a collection of PlantUML diagram definitions, one per view - */ - public Collection toPlantUMLDiagrams(Workspace workspace) { - if (workspace == null) { - throw new IllegalArgumentException("A workspace must be provided."); - } - - Collection diagrams = new ArrayList<>(); - - for (View view : workspace.getViews().getViews()) { - StringWriter stringWriter = new StringWriter(); - write(view, stringWriter); - - diagrams.add(new PlantUMLDiagram(view.getKey(), view.getName(), stringWriter.toString())); - } - - return diagrams; - } - - /** - * Writes a single view as a PlantUML diagram definition, to the specified writer. - * - * @param view the view to write - * @param writer the Writer to write the PlantUML definition to - */ - public void write(View view, Writer writer) { - if (view == null) { - throw new IllegalArgumentException("A view must be provided."); - } - - if (writer == null) { - throw new IllegalArgumentException("A writer must be provided."); - } - - if (SystemLandscapeView.class.isAssignableFrom(view.getClass())) { - write((SystemLandscapeView) view, writer); - } else if (SystemContextView.class.isAssignableFrom(view.getClass())) { - write((SystemContextView) view, writer); - } else if (ContainerView.class.isAssignableFrom(view.getClass())) { - write((ContainerView) view, writer); - } else if (ComponentView.class.isAssignableFrom(view.getClass())) { - write((ComponentView) view, writer); - } else if (DynamicView.class.isAssignableFrom(view.getClass())) { - write((DynamicView) view, writer); - } else if (DeploymentView.class.isAssignableFrom(view.getClass())) { - write((DeploymentView) view, writer); - } - } - - protected void write(SystemLandscapeView view, Writer writer) { - writeSystemLandscapeOrContextView(view, writer, view.isEnterpriseBoundaryVisible()); - } - - protected void write(SystemContextView view, Writer writer) { - writeSystemLandscapeOrContextView(view, writer, view.isEnterpriseBoundaryVisible()); - } - - private void writeSystemLandscapeOrContextView(View view, Writer writer, boolean showEnterpriseBoundary) { - try { - writeHeader(view, writer); - - boolean enterpriseBoundaryVisible; - enterpriseBoundaryVisible = - showEnterpriseBoundary && - (view.getElements().stream().map(ElementView::getElement).anyMatch(e -> e instanceof Person && ((Person)e).getLocation() == Location.Internal) || - view.getElements().stream().map(ElementView::getElement).anyMatch(e -> e instanceof SoftwareSystem && ((SoftwareSystem)e).getLocation() == Location.Internal)); - - view.getElements().stream() - .map(ElementView::getElement) - .filter(e -> e instanceof Person && ((Person)e).getLocation() != Location.Internal) - .sorted((e1, e2) -> e1.getName().compareTo(e2.getName())) - .forEach(e -> write(view, e, writer, false)); - - view.getElements().stream() - .map(ElementView::getElement) - .filter(e -> e instanceof SoftwareSystem && ((SoftwareSystem)e).getLocation() != Location.Internal) - .sorted((e1, e2) -> e1.getName().compareTo(e2.getName())) - .forEach(e -> write(view, e, writer, false)); - - if (enterpriseBoundaryVisible) { - String name = view.getModel().getEnterprise() != null ? view.getModel().getEnterprise().getName() : "Enterprise"; - writer.write("package \"" + name + "\" {"); - writer.write(System.lineSeparator()); - } - - view.getElements().stream() - .map(ElementView::getElement) - .filter(e -> e instanceof Person && ((Person)e).getLocation() == Location.Internal) - .sorted((e1, e2) -> e1.getName().compareTo(e2.getName())) - .forEach(e -> write(view, e, writer, true)); - - view.getElements().stream() - .map(ElementView::getElement) - .filter(e -> e instanceof SoftwareSystem && ((SoftwareSystem)e).getLocation() == Location.Internal) - .sorted((e1, e2) -> e1.getName().compareTo(e2.getName())) - .forEach(e -> write(view, e, writer, true)); - - if (enterpriseBoundaryVisible) { - writer.write("}"); - writer.write(System.lineSeparator()); - } - - writeRelationships(view, writer); - - writeFooter(writer); - } catch (IOException e) { - e.printStackTrace(); - } - } - - protected void write(ContainerView view, Writer writer) { - try { - writeHeader(view, writer); - - view.getElements().stream() - .filter(ev -> !(ev.getElement() instanceof Container)) - .map(ElementView::getElement) - .sorted((e1, e2) -> e1.getName().compareTo(e2.getName())) - .forEach(e -> write(view, e, writer, false)); - - writer.write("package \"" + view.getSoftwareSystem().getName() + "\" <<" + typeOf(view.getSoftwareSystem()) + ">> {"); - writer.write(System.lineSeparator()); - - view.getElements().stream() - .filter(ev -> ev.getElement() instanceof Container) - .map(ElementView::getElement) - .sorted((e1, e2) -> e1.getName().compareTo(e2.getName())) - .forEach(e -> write(view, e, writer, true)); - - writer.write("}"); - writer.write(System.lineSeparator()); - - writeRelationships(view, writer); - - writeFooter(writer); - } catch (IOException e) { - e.printStackTrace(); - } - } - - protected void write(ComponentView view, Writer writer) { - try { - writeHeader(view, writer); - - view.getElements().stream() - .filter(ev -> !(ev.getElement() instanceof Component)) - .map(ElementView::getElement) - .sorted((e1, e2) -> e1.getName().compareTo(e2.getName())) - .forEach(e -> write(view, e, writer, false)); - - writer.write("package \"" + view.getContainer().getName() + "\" <<" + typeOf(view.getContainer()) + ">> {"); - writer.write(System.lineSeparator()); - - view.getElements().stream() - .filter(ev -> ev.getElement() instanceof Component) - .map(ElementView::getElement) - .sorted((e1, e2) -> e1.getName().compareTo(e2.getName())) - .forEach(e -> write(view, e, writer, true)); - - writer.write("}"); - writer.write(System.lineSeparator()); - - writeRelationships(view, writer); - - writeFooter(writer); - } catch (IOException e) { - e.printStackTrace(); - } - } - - protected void write(DynamicView view, Writer writer) { - try { - writeHeader(view, writer); - - if (useSequenceDiagrams) { - view.getElements().stream() - .map(ElementView::getElement) - .sorted((e1, e2) -> e1.getId().compareTo(e2.getId())) - .forEach(e -> { - try { - writeSequenceElement(view, e, writer, false, plantumlSequenceType(view, e)); - } catch (IOException e3) { - e3.printStackTrace(); - } - }); - } else { - view.getElements().stream() - .map(ElementView::getElement) - .sorted((e1, e2) -> e1.getName().compareTo(e2.getName())). - forEach(e -> write(view, e, writer, false)); - } - view.getRelationships() - .forEach(relationship -> { - try { - writer.write( - format("%s -[%s]> %s : %s. %s", - idOf(relationship.getRelationship().getSource()), - view.getViewSet().getConfiguration().getStyles().findRelationshipStyle(relationship.getRelationship()).getColor(), - idOf(relationship.getRelationship().getDestination()), - relationship.getOrder(), - hasValue(relationship.getDescription()) ? relationship.getDescription() : hasValue(relationship.getRelationship().getDescription()) ? relationship.getRelationship().getDescription() : "" - ) - ); - writer.write(System.lineSeparator()); - } catch (IOException e) { - e.printStackTrace(); - } - }); - - writeFooter(writer); - } catch (IOException e) { - e.printStackTrace(); - } - } - - protected void write(DeploymentView view, Writer writer) { - try { - writeHeader(view, writer); - - view.getElements().stream() - .filter(ev -> ev.getElement() instanceof DeploymentNode && ev.getElement().getParent() == null) - .map(ev -> (DeploymentNode)ev.getElement()) - .sorted((e1, e2) -> e1.getName().compareTo(e2.getName())) - .forEach(e -> write(view, e, writer, 0)); - - writeRelationships(view, writer); - - writeFooter(writer); - } catch (IOException e) { - e.printStackTrace(); - } - } - - protected void write(View view, DeploymentNode deploymentNode, Writer writer, int indent) { - try { - writer.write( - format("%snode \"%s\" <<%s>> as %s {", - calculateIndent(indent), - deploymentNode.getName() + (deploymentNode.getInstances() > 1 ? " (x" + deploymentNode.getInstances() + ")" : ""), - typeOf(deploymentNode), - idOf(deploymentNode) - ) - ); - - writer.write(System.lineSeparator()); - - for (DeploymentNode child : deploymentNode.getChildren()) { - write(view, child, writer, indent+1); - } - - for (ContainerInstance containerInstance : deploymentNode.getContainerInstances()) { - write(view, containerInstance, writer, indent+1); - } - - writer.write( - format("%s}", calculateIndent(indent)) - ); - writer.write(System.lineSeparator()); - } catch (IOException e) { - e.printStackTrace(); - } - } - - protected void write(View view, ContainerInstance containerInstance, Writer writer, int indent) { - try { - writer.write( - format("%s%s \"%s\" <<%s>> as %s %s", - calculateIndent(indent), - plantumlType(view, containerInstance.getContainer()), - containerInstance.getContainer().getName(), - typeOf(containerInstance), - idOf(containerInstance), - backgroundOf(view, containerInstance.getContainer()) - ) - ); - - - writer.write(System.lineSeparator()); - } catch (IOException e) { - e.printStackTrace(); - } - } - - protected String calculateIndent(int indent) { - StringBuilder buf = new StringBuilder(); - - for (int i = 0; i < indent; i++) { - buf.append(" "); - } - - return buf.toString(); - } - - protected void write(View view, Element element, Writer writer, boolean indent) { - try { - final String type = plantumlType(view, element); - final List description = lines(element.getDescription()); - - if(description.isEmpty() || "actor".equals(type)) { - writeSimpleElement(view, element, writer, indent, type); - - if (includeNotesForActors) { - writeDescriptionAsNote(element, writer, indent, description); - } - } - else { - final String prefix = indent ? " " : ""; - final String separator = System.lineSeparator(); - final String id = idOf(element); - - writer.write(format("%s%s %s <<%s>> %s [%s", - prefix, type, id, typeOf(element), backgroundOf(view, element), separator)); - writer.write(format("%s %s%s", prefix, element.getName(), separator)); - writer.write(format("%s --%s", prefix, separator)); - for (final String line : description) { - writer.write(format("%s %s%s", prefix, line, separator)); - } - writer.write(format("%s]%s", prefix, separator)); - } - - } catch (IOException e) { - e.printStackTrace(); - } - } - - protected void writeSimpleElement(View view, Element element, Writer writer, boolean indent, String type) throws IOException { - writer.write(format("%s%s \"%s\" <<%s>> as %s %s%s", - indent ? " " : "", - type, - element.getName(), - typeOf(element), - idOf(element), - backgroundOf(view, element), - System.lineSeparator())); - } - - protected void writeSequenceElement(View view, Element element, Writer writer, boolean indent, String type) throws IOException { - writer.write(format("%s%s \"%s\" as %s <<%s>> %s%s", - indent ? " " : "", - type, - element.getName(), - idOf(element), - typeOf(element), - backgroundOf(view, element), - System.lineSeparator())); - } - - protected void writeDescriptionAsNote(Element element, Writer writer, boolean indent, List description) throws IOException { - if (!description.isEmpty()) { - final String prefix = indent ? " " : ""; - final String separator = System.lineSeparator(); - final String id = idOf(element); - writer.write(format("%snote right of %s%s", prefix, id, separator)); - for (final String line : description) { - writer.write(format("%s %s%s", prefix, line, separator)); - } - writer.write(format("%send note%s", prefix, separator)); - } - } - - protected List lines(final String text) { - if(text==null) { - return emptyList(); - } - final String[] words = text.trim().split("\\s+"); - final List lines = new ArrayList<>(); - final StringBuilder line = new StringBuilder(); - for (final String word : words) { - if(line.length()==0) { - line.append(word); - } - else if(line.length() + word.length() + 1 < 30) { - line.append(' ').append(word); - } - else { - lines.add(line.toString()); - line.setLength(0); - line.append(word); - } - } - if(line.length()>0) { - lines.add(line.toString()); - } - return lines; - } - - protected String backgroundOf(View view, Element element) { - return view.getViewSet().getConfiguration().getStyles().findElementStyle(element).getBackground(); - } - - protected String plantumlType(View view, Element element) { - Shape shape = view.getViewSet().getConfiguration().getStyles().findElementStyle(element).getShape(); - - switch(shape) { - case Box: - return element instanceof Component ? "component" : "rectangle"; - case Person: - return "actor"; - case Cylinder: - return "database"; - case Folder: - return "folder"; - case Ellipse: - case Circle: - return "storage"; - default: - return "rectangle"; - } - } - - protected String plantumlSequenceType(View view, Element element) { - Shape shape = view.getViewSet().getConfiguration().getStyles().findElementStyle(element).getShape(); - - switch(shape) { - case Box: - return "participant"; - case Person: - return "actor"; - case Cylinder: - return "database"; - case Folder: - return "collections"; - case Ellipse: - case Circle: - return "entity"; - default: - return "participant"; - } - } - - protected void writeRelationships(View view, Writer writer) { - view.getRelationships().stream() - .map(RelationshipView::getRelationship) - .sorted((r1, r2) -> (r1.getSource().getName() + r1.getDestination().getName()).compareTo(r2.getSource().getName() + r2.getDestination().getName())) - .forEach(r -> writeRelationship(view, r, writer)); - } - - protected void writeRelationship(View view, Relationship relationship, Writer writer) { - try { - String stereotypeAndDescription = - (hasValue(relationship.getTechnology()) ? "<<" + relationship.getTechnology() + ">>\\n" : "") + - (hasValue(relationship.getDescription()) ? relationship.getDescription() : ""); - - writer.write( - format("%s .[%s].> %s %s", - idOf(relationship.getSource()), - view.getViewSet().getConfiguration().getStyles().findRelationshipStyle(relationship).getColor(), - idOf(relationship.getDestination()), - hasValue(stereotypeAndDescription) ? ": " + stereotypeAndDescription : "" - ) - ); - writer.write(System.lineSeparator()); - } catch (IOException e) { - e.printStackTrace(); - } - } - - protected String idOf(Element e) { - return e.getId(); - } - - protected String typeOf(Element e) { - if (e instanceof SoftwareSystem) { - return "Software System"; - } else if (e instanceof Component) { - Component component = (Component)e; - return hasValue(component.getTechnology()) ? component.getTechnology() : "Component"; - } else if (e instanceof DeploymentNode) { - DeploymentNode deploymentNode = (DeploymentNode)e; - return hasValue(deploymentNode.getTechnology()) ? deploymentNode.getTechnology() : "Deployment Node"; - } else if (e instanceof ContainerInstance) { - return "Container"; - } else { - return e.getClass().getSimpleName(); - } - } - - protected boolean hasValue(String s) { - return s != null && s.trim().length() > 0; - } - - protected void writeHeader(View view, Writer writer) throws IOException { - // Spaces in PlantUML ids can cause issues. Alternatively, id can be surrounded with double quotes - writer.write(format("@startuml(id=%s)", view.getKey().replace(' ', '_'))); - writer.write(System.lineSeparator()); - - for (String include : includes) { - writer.write(include); - writer.write(System.lineSeparator()); - } - - PaperSize size = view.getPaperSize(); - int width; - int height; - if(size==null) { - width = height = sizeLimit; - } - else { - width = size.getWidth(); - height = size.getHeight(); - if(width>sizeLimit || height>sizeLimit) { - int max = Math.max(width, height); - width = (width * sizeLimit) / max; - height = (height * sizeLimit) / max; - } - } - writer.write("scale max "); - writer.write(Integer.toString(width)); - writer.write("x"); - writer.write(Integer.toString(height)); - writer.write(System.lineSeparator()); - - if (direction != null && direction.trim().length() > 0) { - writer.write(direction); - writer.write(System.lineSeparator()); - } - - String viewTitle = view.getTitle(); - if (StringUtils.isNullOrEmpty(viewTitle)) { - viewTitle = view.getName(); - } - writer.write("title " + viewTitle); - writer.write(System.lineSeparator()); - - if (view.getDescription() != null && view.getDescription().trim().length() > 0) { - writer.write("caption " + view.getDescription()); - writer.write(System.lineSeparator()); - } - - writer.write(System.lineSeparator()); - - if (!skinParams.isEmpty()) { - writer.write(format("skinparam {%s", System.lineSeparator())); - for (final String name : skinParams.keySet()) { - writer.write(format(" %s %s%s", name, skinParams.get(name), System.lineSeparator())); - } - writer.write(format("}%s", System.lineSeparator())); - } - } - - protected void writeFooter(Writer writer) throws IOException { - writer.write("@enduml"); - writer.write(System.lineSeparator()); - } - -} diff --git a/structurizr-plantuml/test/unit/com/structurizr/io/plantuml/PlantUMLEncoderTests.java b/structurizr-plantuml/test/unit/com/structurizr/io/plantuml/PlantUMLEncoderTests.java deleted file mode 100644 index 557e36389..000000000 --- a/structurizr-plantuml/test/unit/com/structurizr/io/plantuml/PlantUMLEncoderTests.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.structurizr.io.plantuml; - -import org.junit.Test; - -import static org.junit.Assert.assertEquals; - -public class PlantUMLEncoderTests { - - @Test - public void test() throws Exception { - assertEquals("SyfFqhLppCbCJbMmKiX8pSd91m00", new PlantUMLEncoder().encode("Bob->Alice : hello")); - } - -} \ No newline at end of file diff --git a/structurizr-plantuml/test/unit/com/structurizr/io/plantuml/PlantUMLWriterTests.java b/structurizr-plantuml/test/unit/com/structurizr/io/plantuml/PlantUMLWriterTests.java deleted file mode 100644 index a7692c98c..000000000 --- a/structurizr-plantuml/test/unit/com/structurizr/io/plantuml/PlantUMLWriterTests.java +++ /dev/null @@ -1,717 +0,0 @@ -package com.structurizr.io.plantuml; - -import com.structurizr.Workspace; -import com.structurizr.model.*; -import com.structurizr.view.*; -import org.junit.Before; -import org.junit.Test; - -import java.io.StringWriter; -import java.net.URI; -import java.util.Collection; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -public class PlantUMLWriterTests { - private static final String DATA_STORE_TAG = "DataStore"; - private static final String SOME_TAG = "Some"; - - private PlantUMLWriter plantUMLWriter; - private Workspace workspace; - private StringWriter stringWriter; - - @Before - public void setUp() { - plantUMLWriter = new PlantUMLWriter(); - - // Public plantuml.com/plantuml server limits dimensions to 2000, but local servers can be configured - // differently. Setting limit here to 1999 to be provably different to plantuml.com AND still usable at the - // public server AND bigger than A6 so that we can ensure smaller paper sizes are respected correctly. - plantUMLWriter.setSizeLimit(1999); - workspace = new Workspace("Name", "Description"); - stringWriter = new StringWriter(); - } - - @Test - public void test_writeWorkspace_ThrowsAnExceptionWhenPassedANullWorkspace() throws Exception { - try { - plantUMLWriter.write((Workspace)null, null); - fail(); - } catch (Exception e) { - assertEquals("A workspace must be provided.", e.getMessage()); - } - } - - @Test - public void test_writeWorkspace_ThrowsAnExceptionWhenPassedANullWriter() throws Exception { - try { - plantUMLWriter.write(workspace, null); - fail(); - } catch (Exception e) { - assertEquals("A writer must be provided.", e.getMessage()); - } - } - - @Test - public void test_writeView_ThrowsAnExceptionWhenPassedANullView() throws Exception { - try { - plantUMLWriter.write((View)null, null); - fail(); - } catch (Exception e) { - assertEquals("A view must be provided.", e.getMessage()); - } - } - - @Test - public void test_writeView_ThrowsAnExceptionWhenPassedANullWriter() throws Exception { - populateWorkspace(); - - try { - plantUMLWriter.write((View) workspace.getViews().getSystemLandscapeViews().stream().findFirst().get(), null); - fail(); - } catch (Exception e) { - assertEquals("A writer must be provided.", e.getMessage()); - } - } - - @Test - public void test_writeWorkspace() throws Exception { - populateWorkspace(); - - plantUMLWriter.write(workspace, stringWriter); - assertEquals( - SYSTEM_LANDSCAPE_VIEW + - SYSTEM_CONTEXT_VIEW + - CONTAINER_VIEW + - COMPONENT_VIEW + - DYNAMIC_VIEW + - DEPLOYMENT_VIEW, stringWriter.toString()); - } - - @Test - public void test_writeEnterpriseContextView() throws Exception { - populateWorkspace(); - - SystemLandscapeView systemLandscapeView = workspace.getViews().getSystemLandscapeViews() - .stream().findFirst().get(); - plantUMLWriter.write(systemLandscapeView, stringWriter); - - assertEquals(SYSTEM_LANDSCAPE_VIEW, stringWriter.toString()); - - } - - @Test - public void test_writeSystemContextView() throws Exception { - populateWorkspace(); - - SystemContextView systemContextView = workspace.getViews().getSystemContextViews() - .stream().findFirst().get(); - plantUMLWriter.write(systemContextView, stringWriter); - - assertEquals(SYSTEM_CONTEXT_VIEW, stringWriter.toString()); - } - - @Test - public void test_writeContainerView() throws Exception { - populateWorkspace(); - - ContainerView containerView = workspace.getViews().getContainerViews() - .stream().findFirst().get(); - plantUMLWriter.write(containerView, stringWriter); - - assertEquals(CONTAINER_VIEW, stringWriter.toString()); - } - - @Test - public void test_writeComponentsView() throws Exception { - populateWorkspace(); - - ComponentView componentView = workspace.getViews().getComponentViews() - .stream().findFirst().get(); - plantUMLWriter.write(componentView, stringWriter); - - assertEquals(COMPONENT_VIEW, stringWriter.toString()); - } - - @Test - public void test_writeDynamicView() throws Exception { - populateWorkspace(); - - DynamicView dynamicView = workspace.getViews().getDynamicViews() - .stream().findFirst().get(); - plantUMLWriter.write(dynamicView, stringWriter); - - assertEquals(DYNAMIC_VIEW, stringWriter.toString()); - } - - @Test - public void test_writeDynamicViewSequence() throws Exception { - populateWorkspace(); - - DynamicView dynamicView = workspace.getViews().getDynamicViews() - .stream().findFirst().get(); - plantUMLWriter.setUseSequenceDiagrams(true); - plantUMLWriter.write(dynamicView, stringWriter); - plantUMLWriter.setUseSequenceDiagrams(false); - - System.out.print(stringWriter.toString()); - - assertEquals(DYNAMIC_VIEW_SEQUENCE, stringWriter.toString()); - } - - @Test - public void test_writeDeploymentView() throws Exception { - populateWorkspace(); - - DeploymentView deploymentView = workspace.getViews().getDeploymentViews() - .stream().findFirst().get(); - plantUMLWriter.write(deploymentView, stringWriter); - - assertEquals(DEPLOYMENT_VIEW, stringWriter.toString()); - } - - private void populateWorkspace() { - Model model = workspace.getModel(); - model.setEnterprise(new Enterprise("Some Enterprise")); - - Person user = model.addPerson(Location.Internal, "User", - "A detailed description of the user to be displayed on the diagrams"); - SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "Software System", ""); - user.uses(softwareSystem, "Uses"); - - SoftwareSystem emailSystem = model.addSoftwareSystem(Location.External, "E-mail System", "An SMTP relay configured to send emails to the users."); - softwareSystem.uses(emailSystem, "Sends e-mail using"); - emailSystem.delivers(user, "Delivers e-mails to"); - - Container webApplication = softwareSystem.addContainer("Web Application", "", ""); - Container database = softwareSystem.addContainer("Database", "A relational database management system, likely PostgreSQL or MySQL but anything with JDBC drivers would be suitable.", ""); - database.addTags(DATA_STORE_TAG); - user.uses(webApplication, "Uses", "HTTP"); - webApplication.uses(database, "Reads from and writes to", "JDBC"); - webApplication.uses(emailSystem, "Sends e-mail using"); - - Component controller = webApplication.addComponent("SomeController", "", "Spring MVC Controller"); - controller.addTags(SOME_TAG); - Component emailComponent = webApplication.addComponent("EmailComponent", ""); - Component repository = webApplication.addComponent("SomeRepository", "", "Spring Data"); - repository.addTags(SOME_TAG); - user.uses(controller, "Uses", "HTTP"); - controller.uses(repository, "Uses"); - controller.uses(emailComponent, "Sends e-mail using"); - repository.uses(database, "Reads from and writes to", "JDBC"); - emailComponent.uses(emailSystem, "Sends e-mails using", "SMTP"); - - DeploymentNode webServer = model.addDeploymentNode("Web Server", "A server hosted at AWS EC2.", "Ubuntu 12.04 LTS"); - webServer.addDeploymentNode("Apache Tomcat", "The live web server", "Apache Tomcat 8.x") - .add(webApplication); - DeploymentNode databaseServer = model.addDeploymentNode("Database Server", "A server hosted at AWS EC2.", "Ubuntu 12.04 LTS"); - databaseServer.addDeploymentNode("MySQL", "The live database server", "MySQL 5.5.x") - .add(database); - - SystemLandscapeView - systemLandscapeView = workspace.getViews().createSystemLandscapeView("enterpriseContext", ""); - systemLandscapeView.addAllElements(); - - SystemContextView - systemContextView = workspace.getViews().createSystemContextView(softwareSystem, "systemContext", ""); - systemContextView.addAllElements(); - - ContainerView containerView = workspace.getViews().createContainerView(softwareSystem, "containers", ""); - containerView.setPaperSize(PaperSize.A2_Landscape); - containerView.addAllElements(); - - ComponentView componentView = workspace.getViews().createComponentView(webApplication, "components", ""); - componentView.setPaperSize(PaperSize.A6_Portrait); - componentView.addAllElements(); - - DynamicView dynamicView = workspace.getViews().createDynamicView(webApplication, "dynamic", ""); - dynamicView.add(user, "Requests /something", controller); - dynamicView.add(controller, repository); - dynamicView.add(repository, "select * from something", database); - - DeploymentView deploymentView = workspace.getViews().createDeploymentView(softwareSystem, "deployment", ""); - deploymentView.addAllDeploymentNodes(); - - Styles styles = workspace.getViews().getConfiguration().getStyles(); - styles.addElementStyle(DATA_STORE_TAG).shape(Shape.Cylinder); - } - - @Test - public void test_toString_ThrowsAnException_WhenPassedANullView() throws Exception { - try { - plantUMLWriter.toString((View)null); - fail(); - } catch (Exception e) { - assertEquals("A view must be provided.", e.getMessage()); - } - } - - @Test - public void test_toString_ThrowsAnException_WhenPassedANullWorkspace() throws Exception { - try { - assertEquals(0, plantUMLWriter.toString((Workspace)null).length); - fail(); - } catch (Exception e) { - assertEquals("A workspace must be provided.", e.getMessage()); - } - } - - @Test - public void test_toStdOut_ThrowsAnException_WhenPassedANullWorkspace() throws Exception { - try { - plantUMLWriter.toStdOut(null); - fail(); - } catch (Exception e) { - assertEquals("A workspace must be provided.", e.getMessage()); - } - } - - @Test - public void test_toString_ReturnsAnEmptyArray_WhenTheWorkspaceContainsNoDiagrams() throws Exception { - assertEquals(0, plantUMLWriter.toString(new Workspace("", "")).length); - } - - @Test - public void test_toString_ReturnsAnArrayOfDiagramsWhenThereAreDiagrams() throws Exception { - populateWorkspace(); - String diagrams[] = plantUMLWriter.toString(workspace); - assertEquals(6, diagrams.length); - assertEquals(SYSTEM_LANDSCAPE_VIEW, diagrams[0]); - assertEquals(SYSTEM_CONTEXT_VIEW, diagrams[1]); - assertEquals(CONTAINER_VIEW, diagrams[2]); - assertEquals(COMPONENT_VIEW, diagrams[3]); - assertEquals(DYNAMIC_VIEW, diagrams[4]); - assertEquals(DEPLOYMENT_VIEW, diagrams[5]); - } - - @Test - public void test_toPlantUMLDiagrams_ReturnsAnCollectionOfDiagramsWhenThereAreDiagrams() throws Exception { - populateWorkspace(); - Collection diagrams = plantUMLWriter.toPlantUMLDiagrams(workspace); - assertEquals(6, diagrams.size()); - - assertEquals(SYSTEM_LANDSCAPE_VIEW, diagrams.stream().filter(d -> d.getKey().equals("enterpriseContext")).findFirst().get().getDefinition()); - assertEquals(SYSTEM_CONTEXT_VIEW, diagrams.stream().filter(d -> d.getKey().equals("systemContext")).findFirst().get().getDefinition()); - assertEquals(CONTAINER_VIEW, diagrams.stream().filter(d -> d.getKey().equals("containers")).findFirst().get().getDefinition()); - assertEquals(COMPONENT_VIEW, diagrams.stream().filter(d -> d.getKey().equals("components")).findFirst().get().getDefinition()); - assertEquals(DYNAMIC_VIEW, diagrams.stream().filter(d -> d.getKey().equals("dynamic")).findFirst().get().getDefinition()); - assertEquals(DEPLOYMENT_VIEW, diagrams.stream().filter(d -> d.getKey().equals("deployment")).findFirst().get().getDefinition()); - } - - @Test - public void test_writeView_UsesTheOverridableViewTitle_WhenTheViewTitleIsSet() throws Exception { - workspace = new Workspace("", ""); - workspace.getModel().addSoftwareSystem("My software system", "").setLocation(Location.Internal); - workspace.getViews().createSystemLandscapeView("thekey", "").setTitle("A view title"); - - plantUMLWriter.write(workspace, stringWriter); - - assertEquals("@startuml(id=thekey)" + System.lineSeparator() + - "scale max 1999x1999" + System.lineSeparator() + - "title A view title" + System.lineSeparator() + - "" + System.lineSeparator() + - "skinparam {" + System.lineSeparator() + - " shadowing false" + System.lineSeparator() + - " arrowColor #707070" + System.lineSeparator() + - " actorBorderColor #707070" + System.lineSeparator() + - " componentBorderColor #707070" + System.lineSeparator() + - " rectangleBorderColor #707070" + System.lineSeparator() + - " noteBackgroundColor #ffffff" + System.lineSeparator() + - " noteBorderColor #707070" + System.lineSeparator() + - "}" + System.lineSeparator() + - "@enduml" + System.lineSeparator(), stringWriter.toString()); - } - - @Test - public void test_writeView_IncludesSkinParams_WhenSkinParamsAreAdded() throws Exception { - workspace = new Workspace("", ""); - workspace.getModel().addSoftwareSystem("My software system", "").setLocation(Location.Internal); - workspace.getViews().createSystemLandscapeView("thekey", "").addAllElements(); - plantUMLWriter.addSkinParam("handwritten", "true"); - - plantUMLWriter.write(workspace, stringWriter); - - assertEquals("@startuml(id=thekey)" + System.lineSeparator() + - "scale max 1999x1999" + System.lineSeparator() + - "title System Landscape" + System.lineSeparator() + - "" + System.lineSeparator() + - "skinparam {" + System.lineSeparator() + - " shadowing false" + System.lineSeparator() + - " arrowColor #707070" + System.lineSeparator() + - " actorBorderColor #707070" + System.lineSeparator() + - " componentBorderColor #707070" + System.lineSeparator() + - " rectangleBorderColor #707070" + System.lineSeparator() + - " noteBackgroundColor #ffffff" + System.lineSeparator() + - " noteBorderColor #707070" + System.lineSeparator() + - " handwritten true" + System.lineSeparator() + - "}" + System.lineSeparator() + - "package \"Enterprise\" {" + System.lineSeparator() + - " rectangle \"My software system\" <> as 1 #dddddd" + System.lineSeparator() + - "}" + System.lineSeparator() + - "@enduml" + System.lineSeparator(), stringWriter.toString()); - } - - @Test - public void test_writeView_IncludesStyling_WhenStylesAreAdded() throws Exception { - workspace = new Workspace("", ""); - SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("My software system", ""); - Person user = workspace.getModel().addPerson("A user", ""); - user.uses(softwareSystem, "Uses"); - workspace.getViews().createSystemContextView(softwareSystem, "key", "").addAllElements(); - workspace.getViews().getConfiguration().getStyles().addElementStyle(Tags.SOFTWARE_SYSTEM).background("#ff0000"); - workspace.getViews().getConfiguration().getStyles().addElementStyle(Tags.PERSON).background("#00ff00"); - workspace.getViews().getConfiguration().getStyles().addRelationshipStyle(Tags.RELATIONSHIP).color("#0000ff"); - - plantUMLWriter.write(workspace, stringWriter); - - assertEquals("@startuml(id=key)" + System.lineSeparator() + - "scale max 1999x1999" + System.lineSeparator() + - "title My software system - System Context" + System.lineSeparator() + - "" + System.lineSeparator() + - "skinparam {" + System.lineSeparator() + - " shadowing false" + System.lineSeparator() + - " arrowColor #707070" + System.lineSeparator() + - " actorBorderColor #707070" + System.lineSeparator() + - " componentBorderColor #707070" + System.lineSeparator() + - " rectangleBorderColor #707070" + System.lineSeparator() + - " noteBackgroundColor #ffffff" + System.lineSeparator() + - " noteBorderColor #707070" + System.lineSeparator() + - "}" + System.lineSeparator() + - "rectangle \"A user\" <> as 2 #00ff00" + System.lineSeparator() + - "rectangle \"My software system\" <> as 1 #ff0000" + System.lineSeparator() + - "2 .[#0000ff].> 1 : Uses" + System.lineSeparator() + - "@enduml" + System.lineSeparator(), stringWriter.toString()); - } - - @Test - public void test_writeView_IncludesIncludes_WhenIncludesAreAdded() throws Exception { - workspace = new Workspace("", ""); - SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("My software system", ""); - Person user = workspace.getModel().addPerson("A user", ""); - user.uses(softwareSystem, "Uses"); - workspace.getViews().createSystemContextView(softwareSystem, "thekey", "").addAllElements(); - workspace.getViews().getConfiguration().getStyles().addElementStyle(Tags.SOFTWARE_SYSTEM).background("#ff0000"); - workspace.getViews().getConfiguration().getStyles().addElementStyle(Tags.PERSON).background("#00ff00"); - workspace.getViews().getConfiguration().getStyles().addRelationshipStyle(Tags.RELATIONSHIP).color("#0000ff"); - - plantUMLWriter.addIncludeFile("do-not-include-me.puml"); - plantUMLWriter.clearIncludes(); - plantUMLWriter.addIncludeFile("whole-file.puml"); - plantUMLWriter.addIncludeFile("unnamed-diagrams.puml", 0); - plantUMLWriter.addIncludeFile("named-diagrams.puml", "diagram-name"); - plantUMLWriter.addIncludeURL(URI.create("http://example.com/whole-file.puml")); - plantUMLWriter.addIncludeURL(URI.create("http://example.com/unnamed-diagrams.puml"), 0); - plantUMLWriter.addIncludeURL(URI.create("http://example.com/named-diagrams.puml"), "diagram-name"); - - plantUMLWriter.clearSkinParams(); - - plantUMLWriter.write(workspace, stringWriter); - - assertEquals("@startuml(id=thekey)" + System.lineSeparator() + - "!include whole-file.puml" + System.lineSeparator() + - "!include unnamed-diagrams.puml!0" + System.lineSeparator() + - "!include named-diagrams.puml!diagram-name" + System.lineSeparator() + - "!includeurl http://example.com/whole-file.puml" + System.lineSeparator() + - "!includeurl http://example.com/unnamed-diagrams.puml!0" + System.lineSeparator() + - "!includeurl http://example.com/named-diagrams.puml!diagram-name" + System.lineSeparator() + - "scale max 1999x1999" + System.lineSeparator() + - "title My software system - System Context" + System.lineSeparator() + - "" + System.lineSeparator() + - "rectangle \"A user\" <> as 2 #00ff00" + System.lineSeparator() + - "rectangle \"My software system\" <> as 1 #ff0000" + System.lineSeparator() + - "2 .[#0000ff].> 1 : Uses" + System.lineSeparator() + - "@enduml" + System.lineSeparator(), stringWriter.toString()); - } - - @Test - public void test_writeView_SpaceInKey() throws Exception { - workspace = new Workspace("", ""); - SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("My software system", ""); - Person user = workspace.getModel().addPerson("A user", ""); - user.uses(softwareSystem, "Uses"); - workspace.getViews().createSystemContextView(softwareSystem, "the key", "").addAllElements(); // Space in key - - plantUMLWriter.clearSkinParams(); - - plantUMLWriter.write(workspace, stringWriter); - - assertEquals("@startuml(id=the_key)" + System.lineSeparator() + // Quotes added - "scale max 1999x1999" + System.lineSeparator() + - "title My software system - System Context" + System.lineSeparator() + - "" + System.lineSeparator() + - "rectangle \"A user\" <> as 2 #dddddd" + System.lineSeparator() + - "rectangle \"My software system\" <> as 1 #dddddd" + System.lineSeparator() + - "2 .[#707070].> 1 : Uses" + System.lineSeparator() + - "@enduml" + System.lineSeparator(), stringWriter.toString()); - } - - @Test - public void test_writeView_changeDirection_whenSetDirectionAreChanged() throws Exception { - workspace = new Workspace("", ""); - SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("My software system", ""); - Person user = workspace.getModel().addPerson("A user", ""); - user.uses(softwareSystem, "Uses"); - workspace.getViews().createSystemContextView(softwareSystem, "the key", "").addAllElements(); // Space in key - plantUMLWriter.clearSkinParams(); - plantUMLWriter.setDirection("left to right direction"); - - plantUMLWriter.write(workspace, stringWriter); - - assertEquals("@startuml(id=the_key)" + System.lineSeparator() + // Quotes added - "scale max 1999x1999" + System.lineSeparator() + - "left to right direction" + System.lineSeparator() + - "title My software system - System Context" + System.lineSeparator() + - "" + System.lineSeparator() + - "rectangle \"A user\" <> as 2 #dddddd" + System.lineSeparator() + - "rectangle \"My software system\" <> as 1 #dddddd" + System.lineSeparator() + - "2 .[#707070].> 1 : Uses" + System.lineSeparator() + - "@enduml" + System.lineSeparator(), stringWriter.toString()); - } - - private static final String SYSTEM_LANDSCAPE_VIEW = "@startuml(id=enterpriseContext)" + System.lineSeparator() + - "scale max 1999x1999" + System.lineSeparator() + - "title System Landscape for Some Enterprise" + System.lineSeparator() + - "" + System.lineSeparator() + - "skinparam {" + System.lineSeparator() + - " shadowing false" + System.lineSeparator() + - " arrowColor #707070" + System.lineSeparator() + - " actorBorderColor #707070" + System.lineSeparator() + - " componentBorderColor #707070" + System.lineSeparator() + - " rectangleBorderColor #707070" + System.lineSeparator() + - " noteBackgroundColor #ffffff" + System.lineSeparator() + - " noteBorderColor #707070" + System.lineSeparator() + - "}" + System.lineSeparator() + - "rectangle 4 <> #dddddd [" + System.lineSeparator() + - " E-mail System" + System.lineSeparator() + - " --" + System.lineSeparator() + - " An SMTP relay configured to" + System.lineSeparator() + - " send emails to the users." + System.lineSeparator() + - "]" + System.lineSeparator() + - "package \"Some Enterprise\" {" + System.lineSeparator() + - " rectangle 1 <> #dddddd [" + System.lineSeparator() + - " User" + System.lineSeparator() + - " --" + System.lineSeparator() + - " A detailed description of the" + System.lineSeparator() + - " user to be displayed on the" + System.lineSeparator() + - " diagrams" + System.lineSeparator() + - " ]" + System.lineSeparator() + - " rectangle \"Software System\" <> as 2 #dddddd" + System.lineSeparator() + - "}" + System.lineSeparator() + - "4 .[#707070].> 1 : Delivers e-mails to" + System.lineSeparator() + - "2 .[#707070].> 4 : Sends e-mail using" + System.lineSeparator() + - "1 .[#707070].> 2 : Uses" + System.lineSeparator() + - "@enduml" + System.lineSeparator(); - - private static final String SYSTEM_CONTEXT_VIEW = "@startuml(id=systemContext)" + System.lineSeparator() + - "scale max 1999x1999" + System.lineSeparator() + - "title Software System - System Context" + System.lineSeparator() + - "" + System.lineSeparator() + - "skinparam {" + System.lineSeparator() + - " shadowing false" + System.lineSeparator() + - " arrowColor #707070" + System.lineSeparator() + - " actorBorderColor #707070" + System.lineSeparator() + - " componentBorderColor #707070" + System.lineSeparator() + - " rectangleBorderColor #707070" + System.lineSeparator() + - " noteBackgroundColor #ffffff" + System.lineSeparator() + - " noteBorderColor #707070" + System.lineSeparator() + - "}" + System.lineSeparator() + - "rectangle 4 <> #dddddd [" + System.lineSeparator() + - " E-mail System" + System.lineSeparator() + - " --" + System.lineSeparator() + - " An SMTP relay configured to" + System.lineSeparator() + - " send emails to the users." + System.lineSeparator() + - "]" + System.lineSeparator() + - "package \"Some Enterprise\" {" + System.lineSeparator() + - " rectangle 1 <> #dddddd [" + System.lineSeparator() + - " User" + System.lineSeparator() + - " --" + System.lineSeparator() + - " A detailed description of the" + System.lineSeparator() + - " user to be displayed on the" + System.lineSeparator() + - " diagrams" + System.lineSeparator() + - " ]" + System.lineSeparator() + - " rectangle \"Software System\" <> as 2 #dddddd" + System.lineSeparator() + - "}" + System.lineSeparator() + - "4 .[#707070].> 1 : Delivers e-mails to" + System.lineSeparator() + - "2 .[#707070].> 4 : Sends e-mail using" + System.lineSeparator() + - "1 .[#707070].> 2 : Uses" + System.lineSeparator() + - "@enduml" + System.lineSeparator(); - - private static final String CONTAINER_VIEW = "@startuml(id=containers)" + System.lineSeparator() + - "scale max 1999x1413" + System.lineSeparator() + - "title Software System - Containers" + System.lineSeparator() + - "" + System.lineSeparator() + - "skinparam {" + System.lineSeparator() + - " shadowing false" + System.lineSeparator() + - " arrowColor #707070" + System.lineSeparator() + - " actorBorderColor #707070" + System.lineSeparator() + - " componentBorderColor #707070" + System.lineSeparator() + - " rectangleBorderColor #707070" + System.lineSeparator() + - " noteBackgroundColor #ffffff" + System.lineSeparator() + - " noteBorderColor #707070" + System.lineSeparator() + - "}" + System.lineSeparator() + - "rectangle 4 <> #dddddd [" + System.lineSeparator() + - " E-mail System" + System.lineSeparator() + - " --" + System.lineSeparator() + - " An SMTP relay configured to" + System.lineSeparator() + - " send emails to the users." + System.lineSeparator() + - "]" + System.lineSeparator() + - "rectangle 1 <> #dddddd [" + System.lineSeparator() + - " User" + System.lineSeparator() + - " --" + System.lineSeparator() + - " A detailed description of the" + System.lineSeparator() + - " user to be displayed on the" + System.lineSeparator() + - " diagrams" + System.lineSeparator() + - "]" + System.lineSeparator() + - "package \"Software System\" <> {" + System.lineSeparator() + - " database 8 <> #dddddd [" + System.lineSeparator() + - " Database" + System.lineSeparator() + - " --" + System.lineSeparator() + - " A relational database" + System.lineSeparator() + - " management system, likely" + System.lineSeparator() + - " PostgreSQL or MySQL but" + System.lineSeparator() + - " anything with JDBC drivers" + System.lineSeparator() + - " would be suitable." + System.lineSeparator() + - " ]" + System.lineSeparator() + - " rectangle \"Web Application\" <> as 7 #dddddd" + System.lineSeparator() + - "}" + System.lineSeparator() + - "4 .[#707070].> 1 : Delivers e-mails to" + System.lineSeparator() + - "1 .[#707070].> 7 : <>\\nUses" + System.lineSeparator() + - "7 .[#707070].> 8 : <>\\nReads from and writes to" + System.lineSeparator() + - "7 .[#707070].> 4 : Sends e-mail using" + System.lineSeparator() + - "@enduml" + System.lineSeparator(); - - private static final String COMPONENT_VIEW = "@startuml(id=components)" + System.lineSeparator() + - "scale max 1240x1748" + System.lineSeparator() + - "title Software System - Web Application - Components" + System.lineSeparator() + - "" + System.lineSeparator() + - "skinparam {" + System.lineSeparator() + - " shadowing false" + System.lineSeparator() + - " arrowColor #707070" + System.lineSeparator() + - " actorBorderColor #707070" + System.lineSeparator() + - " componentBorderColor #707070" + System.lineSeparator() + - " rectangleBorderColor #707070" + System.lineSeparator() + - " noteBackgroundColor #ffffff" + System.lineSeparator() + - " noteBorderColor #707070" + System.lineSeparator() + - "}" + System.lineSeparator() + - "database 8 <> #dddddd [" + System.lineSeparator() + - " Database" + System.lineSeparator() + - " --" + System.lineSeparator() + - " A relational database" + System.lineSeparator() + - " management system, likely" + System.lineSeparator() + - " PostgreSQL or MySQL but" + System.lineSeparator() + - " anything with JDBC drivers" + System.lineSeparator() + - " would be suitable." + System.lineSeparator() + - "]" + System.lineSeparator() + - "rectangle 4 <> #dddddd [" + System.lineSeparator() + - " E-mail System" + System.lineSeparator() + - " --" + System.lineSeparator() + - " An SMTP relay configured to" + System.lineSeparator() + - " send emails to the users." + System.lineSeparator() + - "]" + System.lineSeparator() + - "rectangle 1 <> #dddddd [" + System.lineSeparator() + - " User" + System.lineSeparator() + - " --" + System.lineSeparator() + - " A detailed description of the" + System.lineSeparator() + - " user to be displayed on the" + System.lineSeparator() + - " diagrams" + System.lineSeparator() + - "]" + System.lineSeparator() + - "package \"Web Application\" <> {" + System.lineSeparator() + - " component \"EmailComponent\" <> as 13 #dddddd" + System.lineSeparator() + - " component \"SomeController\" <> as 12 #dddddd" + System.lineSeparator() + - " component \"SomeRepository\" <> as 14 #dddddd" + System.lineSeparator() + - "}" + System.lineSeparator() + - "4 .[#707070].> 1 : Delivers e-mails to" + System.lineSeparator() + - "13 .[#707070].> 4 : <>\\nSends e-mails using" + System.lineSeparator() + - "12 .[#707070].> 13 : Sends e-mail using" + System.lineSeparator() + - "12 .[#707070].> 14 : Uses" + System.lineSeparator() + - "14 .[#707070].> 8 : <>\\nReads from and writes to" + System.lineSeparator() + - "1 .[#707070].> 12 : <>\\nUses" + System.lineSeparator() + - "@enduml" + System.lineSeparator(); - - private static final String DYNAMIC_VIEW = "@startuml(id=dynamic)" + System.lineSeparator() + - "scale max 1999x1999" + System.lineSeparator() + - "title Web Application - Dynamic" + System.lineSeparator() + - "" + System.lineSeparator() + - "skinparam {" + System.lineSeparator() + - " shadowing false" + System.lineSeparator() + - " arrowColor #707070" + System.lineSeparator() + - " actorBorderColor #707070" + System.lineSeparator() + - " componentBorderColor #707070" + System.lineSeparator() + - " rectangleBorderColor #707070" + System.lineSeparator() + - " noteBackgroundColor #ffffff" + System.lineSeparator() + - " noteBorderColor #707070" + System.lineSeparator() + - "}" + System.lineSeparator() + - "database 8 <> #dddddd [" + System.lineSeparator() + - " Database" + System.lineSeparator() + - " --" + System.lineSeparator() + - " A relational database" + System.lineSeparator() + - " management system, likely" + System.lineSeparator() + - " PostgreSQL or MySQL but" + System.lineSeparator() + - " anything with JDBC drivers" + System.lineSeparator() + - " would be suitable." + System.lineSeparator() + - "]" + System.lineSeparator() + - "component \"SomeController\" <> as 12 #dddddd" + System.lineSeparator() + - "component \"SomeRepository\" <> as 14 #dddddd" + System.lineSeparator() + - "rectangle 1 <> #dddddd [" + System.lineSeparator() + - " User" + System.lineSeparator() + - " --" + System.lineSeparator() + - " A detailed description of the" + System.lineSeparator() + - " user to be displayed on the" + System.lineSeparator() + - " diagrams" + System.lineSeparator() + - "]" + System.lineSeparator() + - "1 -[#707070]> 12 : 1. Requests /something" + System.lineSeparator() + - "12 -[#707070]> 14 : 2. Uses" + System.lineSeparator() + - "14 -[#707070]> 8 : 3. select * from something" + System.lineSeparator() + - "@enduml" + System.lineSeparator(); - - private static final String DYNAMIC_VIEW_SEQUENCE = "@startuml(id=dynamic)" + System.lineSeparator() + - "scale max 1999x1999" + System.lineSeparator() + - "title Web Application - Dynamic" + System.lineSeparator() + - "" + System.lineSeparator() + - "skinparam {" + System.lineSeparator() + - " shadowing false" + System.lineSeparator() + - " arrowColor #707070" + System.lineSeparator() + - " actorBorderColor #707070" + System.lineSeparator() + - " componentBorderColor #707070" + System.lineSeparator() + - " rectangleBorderColor #707070" + System.lineSeparator() + - " noteBackgroundColor #ffffff" + System.lineSeparator() + - " noteBorderColor #707070" + System.lineSeparator() + - "}" + System.lineSeparator() + - "participant \"User\" as 1 <> #dddddd" + System.lineSeparator() + - "participant \"SomeController\" as 12 <> #dddddd" + System.lineSeparator() + - "participant \"SomeRepository\" as 14 <> #dddddd" + System.lineSeparator() + - "database \"Database\" as 8 <> #dddddd" + System.lineSeparator() + - "1 -[#707070]> 12 : 1. Requests /something" + System.lineSeparator() + - "12 -[#707070]> 14 : 2. Uses" + System.lineSeparator() + - "14 -[#707070]> 8 : 3. select * from something" + System.lineSeparator() + - "@enduml" + System.lineSeparator(); - - - private static final String DEPLOYMENT_VIEW = "@startuml(id=deployment)" + System.lineSeparator() + - "scale max 1999x1999" + System.lineSeparator() + - "title Software System - Deployment" + System.lineSeparator() + - "" + System.lineSeparator() + - "skinparam {" + System.lineSeparator() + - " shadowing false" + System.lineSeparator() + - " arrowColor #707070" + System.lineSeparator() + - " actorBorderColor #707070" + System.lineSeparator() + - " componentBorderColor #707070" + System.lineSeparator() + - " rectangleBorderColor #707070" + System.lineSeparator() + - " noteBackgroundColor #ffffff" + System.lineSeparator() + - " noteBorderColor #707070" + System.lineSeparator() + - "}" + System.lineSeparator() + - "node \"Database Server\" <> as 23 {" + System.lineSeparator() + - " node \"MySQL\" <> as 24 {" + System.lineSeparator() + - " database \"Database\" <> as 25 #dddddd" + System.lineSeparator() + - " }" + System.lineSeparator() + - "}" + System.lineSeparator() + - "node \"Web Server\" <> as 20 {" + System.lineSeparator() + - " node \"Apache Tomcat\" <> as 21 {" + System.lineSeparator() + - " rectangle \"Web Application\" <> as 22 #dddddd" + System.lineSeparator() + - " }" + System.lineSeparator() + - "}" + System.lineSeparator() + - "22 .[#707070].> 25 : <>\\nReads from and writes to" + System.lineSeparator() + - "@enduml" + System.lineSeparator(); - -} diff --git a/structurizr-spring/build.gradle b/structurizr-spring/build.gradle deleted file mode 100644 index c55d23ac3..000000000 --- a/structurizr-spring/build.gradle +++ /dev/null @@ -1,11 +0,0 @@ -dependencies { - compile project(':structurizr-core') - compile project(':structurizr-analysis') - - compile 'org.springframework:spring-web:4.2.9.RELEASE' - compile 'org.springframework.data:spring-data-jpa:1.9.4.RELEASE' - compile 'org.springframework.ws:spring-ws-core:2.4.2.RELEASE' - - testCompile 'junit:junit:4.12' - -} \ No newline at end of file diff --git a/structurizr-spring/src/com/structurizr/analysis/AbstractSpringComponentFinderStrategy.java b/structurizr-spring/src/com/structurizr/analysis/AbstractSpringComponentFinderStrategy.java deleted file mode 100644 index aa9690ae5..000000000 --- a/structurizr-spring/src/com/structurizr/analysis/AbstractSpringComponentFinderStrategy.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.structurizr.analysis; - -import com.structurizr.model.Component; -import com.structurizr.model.Container; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Modifier; -import java.util.HashSet; -import java.util.Set; - -public abstract class AbstractSpringComponentFinderStrategy extends AbstractComponentFinderStrategy { - - public static final String SPRING_MVC_CONTROLLER = "Spring MVC Controller"; - public static final String SPRING_SERVICE = "Spring Service"; - public static final String SPRING_REPOSITORY = "Spring Repository"; - public static final String SPRING_COMPONENT = "Spring Component"; - public static final String SPRING_REST_CONTROLLER = "Spring REST Controller"; - public static final String SPRING_WEB_SERVICE_ENDPOINT = "Spring Web Service"; - - protected boolean includePublicTypesOnly = true; - - public AbstractSpringComponentFinderStrategy(SupportingTypesStrategy... strategies) { - super(strategies); - } - - protected Set findInterfacesForImplementationClassesWithAnnotation(Class type, String technology) { - Set components = new HashSet<>(); - - Container container = getComponentFinder().getContainer(); - Set> annotatedTypes = findTypesAnnotatedWith(type); - for (Class annotatedType : annotatedTypes) { - if (container.getComponentWithName(annotatedType.getSimpleName()) != null) { - continue; - } else if (annotatedType.isInterface()) { - // the annotated type is an interface, so we're done - Component newComponent = addComponent(container, annotatedType.getSimpleName(), annotatedType.getCanonicalName(), "", technology); - - if (newComponent != null) { - components.add(newComponent); - } - } else { - // The Spring @Component, @Service and @Repository annotations are typically used to annotate implementation - // classes, but we really want to find the interface type and use that to represent the component. Why? - // Well, for example, a Spring MVC controller may have a dependency on a "SomeRepository" interface, but - // it's the "JdbcSomeRepositoryImpl" implementation class that gets annotated with @Repository. - // - // This next bit of code tries to find the "SomeRepository" interface... - String componentName = annotatedType.getSimpleName(); // e.g. JdbcSomeRepositoryImpl - Class componentType = annotatedType; - boolean foundInterface = false; - - if (annotatedType.getInterfaces().length > 0) { - for (Class interfaceType : annotatedType.getInterfaces()) { - String interfaceName = interfaceType.getSimpleName(); - if (componentName.startsWith(interfaceName) || // <***> - componentName.endsWith(interfaceName) || // <***> - componentName.contains(interfaceName)) { // <***><***> - componentName = interfaceName; - componentType = interfaceType; - foundInterface = true; - break; - } - } - } - - if (!includePublicTypesOnly || Modifier.isPublic(componentType.getModifiers())) { - Component newComponent = addComponent(container, componentName, componentType.getCanonicalName(), "", technology); - if (newComponent != null) { - components.add(newComponent); - - if (foundInterface) { - // the primary component type is now an interface, so add the type we originally found as a supporting type - newComponent.addSupportingType(annotatedType.getCanonicalName()); - } - } - } - } - } - - return components; - } - - /** - * Sets whether this component finder strategy only finds components that are based upon public types. - * - * @param includePublicTypesOnly true for public types only, false otherwise - */ - public void setIncludePublicTypesOnly(boolean includePublicTypesOnly) { - this.includePublicTypesOnly = includePublicTypesOnly; - } - -} \ No newline at end of file diff --git a/structurizr-spring/src/com/structurizr/analysis/SpringComponentComponentFinderStrategy.java b/structurizr-spring/src/com/structurizr/analysis/SpringComponentComponentFinderStrategy.java deleted file mode 100644 index 2d0b5a8ac..000000000 --- a/structurizr-spring/src/com/structurizr/analysis/SpringComponentComponentFinderStrategy.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.structurizr.analysis; - -import com.structurizr.model.Component; - -import java.util.Set; - -/** - * A component finder strategy that finds Spring components (classes annotated @Component). - */ -public final class SpringComponentComponentFinderStrategy extends AbstractSpringComponentFinderStrategy { - - public SpringComponentComponentFinderStrategy(SupportingTypesStrategy... strategies) { - super(strategies); - } - - @Override - protected Set doFindComponents() { - return findInterfacesForImplementationClassesWithAnnotation( - org.springframework.stereotype.Component.class, - SPRING_COMPONENT - ); - } - -} \ No newline at end of file diff --git a/structurizr-spring/src/com/structurizr/analysis/SpringComponentFinderStrategy.java b/structurizr-spring/src/com/structurizr/analysis/SpringComponentFinderStrategy.java deleted file mode 100644 index 1b06ed11e..000000000 --- a/structurizr-spring/src/com/structurizr/analysis/SpringComponentFinderStrategy.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.structurizr.analysis; - -import com.structurizr.model.Component; - -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; - -/** - *

- * This component finder strategy knows how to find the following Spring components: - *

- * - *
    - *
  • Spring MVC controllers (classes annotated @Controller)
  • - *
  • Spring REST controllers (classes annotated @RestController)
  • - *
  • Spring services (classes annotated @Service)
  • - *
  • Spring components (classes annotated @Component)
  • - *
  • Spring repositories (classes annotated @Repository, plus those that extend JpaRepository or CrudRepository)
  • - *
- * - *

- * By default, non-public types will be ignored so that, for example, you can - * hide repository implementations behind services, as described at - * Whoops! Where did my architecture go. - * You can change this behaviour by passing false to {@link #setIncludePublicTypesOnly(boolean)}. - *

- */ -public class SpringComponentFinderStrategy extends AbstractSpringComponentFinderStrategy { - - private List componentFinderStrategies = new LinkedList<>(); - - public SpringComponentFinderStrategy(SupportingTypesStrategy... strategies) { - super(strategies); - } - - @Override - public void beforeFindComponents() { - super.beforeFindComponents(); - - componentFinderStrategies.add(new SpringRestControllerComponentFinderStrategy()); - componentFinderStrategies.add(new SpringMvcControllerComponentFinderStrategy()); - componentFinderStrategies.add(new SpringServiceComponentFinderStrategy()); - componentFinderStrategies.add(new SpringComponentComponentFinderStrategy()); - componentFinderStrategies.add(new SpringRepositoryComponentFinderStrategy()); - - for (AbstractSpringComponentFinderStrategy componentFinderStrategy : componentFinderStrategies) { - componentFinderStrategy.setIncludePublicTypesOnly(includePublicTypesOnly); - componentFinderStrategy.setComponentFinder(getComponentFinder()); - supportingTypesStrategies.forEach(componentFinderStrategy::addSupportingTypesStrategy); - componentFinderStrategy.setDuplicateComponentStrategy(getDuplicateComponentStrategy()); - componentFinderStrategy.beforeFindComponents(); - } - } - - @Override - protected Set doFindComponents() { - Set components = new HashSet<>(); - - for (AbstractComponentFinderStrategy componentFinderStrategy : componentFinderStrategies) { - components.addAll(componentFinderStrategy.findComponents()); - } - - return components; - } - -} \ No newline at end of file diff --git a/structurizr-spring/src/com/structurizr/analysis/SpringMvcControllerComponentFinderStrategy.java b/structurizr-spring/src/com/structurizr/analysis/SpringMvcControllerComponentFinderStrategy.java deleted file mode 100644 index 6ad9ea99f..000000000 --- a/structurizr-spring/src/com/structurizr/analysis/SpringMvcControllerComponentFinderStrategy.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.structurizr.analysis; - -import com.structurizr.model.Component; - -import java.util.Set; - -/** - * A component finder strategy that finds Spring MVC controllers (classes annotated @Controller). - */ -public final class SpringMvcControllerComponentFinderStrategy extends AbstractSpringComponentFinderStrategy { - - public SpringMvcControllerComponentFinderStrategy(SupportingTypesStrategy... strategies) { - super(strategies); - } - - @Override - protected Set doFindComponents() { - return findClassesWithAnnotation( - org.springframework.stereotype.Controller.class, - SPRING_MVC_CONTROLLER, - includePublicTypesOnly - ); - } - -} \ No newline at end of file diff --git a/structurizr-spring/src/com/structurizr/analysis/SpringRepositoryComponentFinderStrategy.java b/structurizr-spring/src/com/structurizr/analysis/SpringRepositoryComponentFinderStrategy.java deleted file mode 100644 index d38d469e4..000000000 --- a/structurizr-spring/src/com/structurizr/analysis/SpringRepositoryComponentFinderStrategy.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.structurizr.analysis; - -import com.structurizr.model.Component; -import com.structurizr.model.Container; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.repository.CrudRepository; -import org.springframework.data.repository.Repository; - -import java.lang.reflect.Modifier; -import java.util.HashSet; -import java.util.Set; - -/** - * A component finder strategy for Spring repositories (classes annotated @Repository, - * plus those that extend JpaRepository or CrudRepository). - */ -public final class SpringRepositoryComponentFinderStrategy extends AbstractSpringComponentFinderStrategy { - - public SpringRepositoryComponentFinderStrategy(SupportingTypesStrategy... strategies) { - super(strategies); - } - - @Override - protected Set doFindComponents() { - Set components = new HashSet<>(); - - components.addAll(findAnnotatedSpringRepositories()); - components.addAll(findSpringRepositoryInterfaces()); - - return components; - } - - private Set findSpringRepositoryInterfaces() { - Set componentsFound = new HashSet<>(); - Set> componentTypes = new HashSet<>(); - - Set> types = getTypeRepository().getAllTypes(); - for (Class type : types) { - if (type.isInterface()) { - if ( - Repository.class.isAssignableFrom(type) || - JpaRepository.class.isAssignableFrom(type) || - CrudRepository.class.isAssignableFrom(type) - ) { - componentTypes.add(type); - } - } - } - - for (Class componentType : componentTypes) { - if (!includePublicTypesOnly || Modifier.isPublic(componentType.getModifiers())) { - final Container container = getComponentFinder().getContainer(); - Component newComponent = addComponent( - container, - componentType.getSimpleName(), - componentType.getCanonicalName(), - "", - SPRING_REPOSITORY); - - - if (newComponent != null) { - componentsFound.add(newComponent); - } - } - } - - return componentsFound; - } - - private Set findAnnotatedSpringRepositories() { - return findInterfacesForImplementationClassesWithAnnotation( - org.springframework.stereotype.Repository.class, SPRING_REPOSITORY); - } - -} \ No newline at end of file diff --git a/structurizr-spring/src/com/structurizr/analysis/SpringRestControllerComponentFinderStrategy.java b/structurizr-spring/src/com/structurizr/analysis/SpringRestControllerComponentFinderStrategy.java deleted file mode 100644 index 7459ceb4a..000000000 --- a/structurizr-spring/src/com/structurizr/analysis/SpringRestControllerComponentFinderStrategy.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.structurizr.analysis; - -import com.structurizr.model.Component; - -import java.util.Set; - -/** - * A component finder strategy that finds Spring REST controllers (classes annotated @RestController). - */ -public final class SpringRestControllerComponentFinderStrategy extends AbstractSpringComponentFinderStrategy { - - public SpringRestControllerComponentFinderStrategy(SupportingTypesStrategy... strategies) { - super(strategies); - } - - @Override - protected Set doFindComponents() { - return findClassesWithAnnotation( - org.springframework.web.bind.annotation.RestController.class, - SPRING_REST_CONTROLLER, - includePublicTypesOnly - ); - } - -} \ No newline at end of file diff --git a/structurizr-spring/src/com/structurizr/analysis/SpringServiceComponentFinderStrategy.java b/structurizr-spring/src/com/structurizr/analysis/SpringServiceComponentFinderStrategy.java deleted file mode 100644 index 48211cc02..000000000 --- a/structurizr-spring/src/com/structurizr/analysis/SpringServiceComponentFinderStrategy.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.structurizr.analysis; - -import com.structurizr.model.Component; - -import java.util.Set; - -/** - * A component finder strategy that finds Spring Services (classes annotated @Service). - */ -public final class SpringServiceComponentFinderStrategy extends AbstractSpringComponentFinderStrategy { - - public SpringServiceComponentFinderStrategy(SupportingTypesStrategy... strategies) { - super(strategies); - } - - @Override - protected Set doFindComponents() { - return findInterfacesForImplementationClassesWithAnnotation( - org.springframework.stereotype.Service.class, - SPRING_SERVICE - ); - } - -} \ No newline at end of file diff --git a/structurizr-spring/src/com/structurizr/analysis/SpringWebServiceEndpointComponentFinderStrategy.java b/structurizr-spring/src/com/structurizr/analysis/SpringWebServiceEndpointComponentFinderStrategy.java deleted file mode 100644 index 9d60ad5df..000000000 --- a/structurizr-spring/src/com/structurizr/analysis/SpringWebServiceEndpointComponentFinderStrategy.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.structurizr.analysis; - -import com.structurizr.model.Component; - -import java.util.Set; - -/** - * A component finder strategy that finds Spring web service endpoints (classes annotated @Endpoint). - */ -public final class SpringWebServiceEndpointComponentFinderStrategy extends AbstractSpringComponentFinderStrategy { - - public SpringWebServiceEndpointComponentFinderStrategy(SupportingTypesStrategy... strategies) { - super(strategies); - } - - @Override - protected Set doFindComponents() { - return findClassesWithAnnotation( - org.springframework.ws.server.endpoint.annotation.Endpoint.class, - SPRING_WEB_SERVICE_ENDPOINT, - includePublicTypesOnly - ); - } - -} \ No newline at end of file diff --git a/structurizr-spring/test/unit/com/structurizr/analysis/AbstractSpringComponentFinderStrategyTests.java b/structurizr-spring/test/unit/com/structurizr/analysis/AbstractSpringComponentFinderStrategyTests.java deleted file mode 100644 index 67e315f1a..000000000 --- a/structurizr-spring/test/unit/com/structurizr/analysis/AbstractSpringComponentFinderStrategyTests.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.structurizr.analysis; - -import com.structurizr.Workspace; -import com.structurizr.model.Component; -import com.structurizr.model.Container; -import com.structurizr.model.Model; -import com.structurizr.model.SoftwareSystem; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; - -public class AbstractSpringComponentFinderStrategyTests { - - @Test - public void test_findComponents_IgnoresNonPublicTypesByDefault() throws Exception { - Workspace workspace = new Workspace("Name", "Description"); - Model model = workspace.getModel(); - SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); - Container container = softwareSystem.addContainer("Name", "Description", "Technology"); - - ComponentFinder componentFinder = new ComponentFinder( - container, - "test.AbstractSpringComponentFinderStrategy", - new SpringComponentFinderStrategy() - ); - componentFinder.findComponents(); - - assertEquals(2, container.getComponents().size()); - - Component component = container.getComponentWithName("SomeController"); - assertEquals("test.AbstractSpringComponentFinderStrategy.SomeController", component.getType().getType()); - - component = container.getComponentWithName("SomePublicRepository"); - assertEquals("test.AbstractSpringComponentFinderStrategy.SomePublicRepository", component.getType().getType()); - } - - @Test - public void test_findComponents_DoesNotIgnoreNonPublicTypes_WhenConfiguredToIncludeNonPublicTypes() throws Exception { - Workspace workspace = new Workspace("Name", "Description"); - Model model = workspace.getModel(); - SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); - Container container = softwareSystem.addContainer("Name", "Description", "Technology"); - - SpringComponentFinderStrategy springComponentFinderStrategy = new SpringComponentFinderStrategy(); - springComponentFinderStrategy.setIncludePublicTypesOnly(false); - ComponentFinder componentFinder = new ComponentFinder( - container, - "test.AbstractSpringComponentFinderStrategy", - springComponentFinderStrategy - ); - componentFinder.findComponents(); - - assertEquals(3, container.getComponents().size()); - - Component component = container.getComponentWithName("SomeController"); - assertEquals("test.AbstractSpringComponentFinderStrategy.SomeController", component.getType().getType()); - - component = container.getComponentWithName("SomePublicRepository"); - assertEquals("test.AbstractSpringComponentFinderStrategy.SomePublicRepository", component.getType().getType()); - - component = container.getComponentWithName("SomeNonPublicRepository"); - assertEquals("test.AbstractSpringComponentFinderStrategy.SomeNonPublicRepository", component.getType().getType()); - } - -} diff --git a/structurizr-spring/test/unit/com/structurizr/analysis/SpringComponentComponentFinderStrategyTests.java b/structurizr-spring/test/unit/com/structurizr/analysis/SpringComponentComponentFinderStrategyTests.java deleted file mode 100644 index 9cf78040f..000000000 --- a/structurizr-spring/test/unit/com/structurizr/analysis/SpringComponentComponentFinderStrategyTests.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.structurizr.analysis; - -import com.structurizr.Workspace; -import com.structurizr.model.Component; -import com.structurizr.model.Container; -import com.structurizr.model.Model; -import com.structurizr.model.SoftwareSystem; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; - -public class SpringComponentComponentFinderStrategyTests { - - @Test - public void test_findComponents_FindsSpringComponents() throws Exception { - Workspace workspace = new Workspace("Name", "Description"); - Model model = workspace.getModel(); - SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); - Container container = softwareSystem.addContainer("Name", "Description", "Technology"); - - ComponentFinder componentFinder = new ComponentFinder( - container, - "test.SpringComponentComponentFinderStrategy", - new SpringComponentComponentFinderStrategy() - ); - componentFinder.findComponents(); - - assertEquals(1, container.getComponents().size()); - Component component = container.getComponentWithName("SomeComponent"); - assertEquals("test.SpringComponentComponentFinderStrategy.SomeComponent", component.getType().getType()); - assertEquals("", component.getDescription()); - assertEquals("Spring Component", component.getTechnology()); - } - -} diff --git a/structurizr-spring/test/unit/com/structurizr/analysis/SpringComponentFinderStrategyTests.java b/structurizr-spring/test/unit/com/structurizr/analysis/SpringComponentFinderStrategyTests.java deleted file mode 100644 index 53de12f51..000000000 --- a/structurizr-spring/test/unit/com/structurizr/analysis/SpringComponentFinderStrategyTests.java +++ /dev/null @@ -1,123 +0,0 @@ -package com.structurizr.analysis; - -import com.structurizr.Workspace; -import com.structurizr.model.*; -import org.junit.Before; -import org.junit.Test; - -import java.util.Set; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; - -public class SpringComponentFinderStrategyTests { - - private Container webApplication; - - @Before - public void setUp() { - Workspace workspace = new Workspace("Name", "Description"); - Model model = workspace.getModel(); - SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); - webApplication = softwareSystem.addContainer("Name", "Description", "Technology"); - } - - @Test - public void test_findComponents() throws Exception { - ComponentFinder componentFinder = new ComponentFinder( - webApplication, - "com.structurizr.analysis.myapp", - new SpringComponentFinderStrategy() - ); - componentFinder.findComponents(); - - assertEquals(5, webApplication.getComponents().size()); - assertEquals(0, webApplication.getComponents().stream().filter(c -> "SomeNonPublicRepository".equals(c.getName())).count()); - - Component someMvcController = webApplication.getComponentWithName("SomeController"); - assertNotNull(someMvcController); - assertEquals("SomeController", someMvcController.getName()); - assertEquals("com.structurizr.analysis.myapp.web.SomeController", someMvcController.getType().getType()); - assertEquals(1, someMvcController.getCode().size()); - - Component someRestController = webApplication.getComponentWithName("SomeApiController"); - assertNotNull(someRestController); - assertEquals("SomeApiController", someRestController.getName()); - assertEquals("com.structurizr.analysis.myapp.api.SomeApiController", someRestController.getType().getType()); - assertEquals(1, someRestController.getCode().size()); - - Component someService = webApplication.getComponentWithName("SomeService"); - assertNotNull(someService); - assertEquals("SomeService", someService.getName()); - assertEquals("com.structurizr.analysis.myapp.service.SomeService", someService.getType().getType()); - assertEquals(2, someService.getCode().size()); - assertCodeElementInComponent(someService, "com.structurizr.analysis.myapp.service.SomeService", CodeElementRole.Primary); - assertCodeElementInComponent(someService, "com.structurizr.analysis.myapp.service.SomeServiceImpl", CodeElementRole.Supporting); - - Component someRepository = webApplication.getComponentWithName("SomeRepository"); - assertNotNull(someRepository); - assertEquals("SomeRepository", someRepository.getName()); - assertEquals("com.structurizr.analysis.myapp.data.SomeRepository", someRepository.getType().getType()); - assertEquals(2, someRepository.getCode().size()); - assertCodeElementInComponent(someRepository, "com.structurizr.analysis.myapp.data.SomeRepository", CodeElementRole.Primary); - assertCodeElementInComponent(someRepository, "com.structurizr.analysis.myapp.data.JdbcSomeRepository", CodeElementRole.Supporting); - - - Component someOtherRepository = webApplication.getComponentWithName("SomeOtherRepository"); - assertNotNull(someOtherRepository); - assertEquals("SomeOtherRepository", someOtherRepository.getName()); - assertEquals("com.structurizr.analysis.myapp.data.SomeOtherRepository", someOtherRepository.getType().getType()); - - assertEquals(1, someMvcController.getRelationships().size()); - Relationship relationship = someMvcController.getRelationships().iterator().next(); - assertEquals(someMvcController, relationship.getSource()); - assertEquals(someService, relationship.getDestination()); - - assertEquals(1, someRestController.getRelationships().size()); - relationship = someRestController.getRelationships().iterator().next(); - assertEquals(someRestController, relationship.getSource()); - assertEquals(someService, relationship.getDestination()); - - assertEquals(2, someService.getRelationships().size()); - - Set relationships = someService.getRelationships(); - assertNotNull(relationships.stream().filter(r -> r.getDestination() == someRepository).findFirst().get()); - assertNotNull(relationships.stream().filter(r -> r.getDestination() == someOtherRepository).findFirst().get()); - } - - private void assertCodeElementInComponent(Component component, String type, CodeElementRole role) { - for (CodeElement codeElement : component.getCode()) { - if (codeElement.getType().equals(type) && codeElement.getRole() == role) { - return; - } - } - - fail("Component " + component.getName() + " does not have a " + role + " code element of type " + type); - } - - @Test - public void test_findComponents_UsesTheConfiguredDuplicateComponentFinderStrategy() throws Exception { - try { - SpringComponentFinderStrategy springComponentFinderStrategy = new SpringComponentFinderStrategy(); - springComponentFinderStrategy.setDuplicateComponentStrategy(new DuplicateComponentStrategy() { - @Override - public Component duplicateComponentFound(Component component, String name, String type, String description, String technology) { - return null; - } - }); - - ComponentFinder componentFinder = new ComponentFinder( - webApplication, - "com.structurizr.analysis.myapp", - springComponentFinderStrategy - ); - - componentFinder.findComponents(); - componentFinder.findComponents(); - } catch (DuplicateComponentException dce) { - fail(); - } - } - -} diff --git a/structurizr-spring/test/unit/com/structurizr/analysis/SpringMvcControllerComponentFinderStrategyTests.java b/structurizr-spring/test/unit/com/structurizr/analysis/SpringMvcControllerComponentFinderStrategyTests.java deleted file mode 100644 index 81258bc4c..000000000 --- a/structurizr-spring/test/unit/com/structurizr/analysis/SpringMvcControllerComponentFinderStrategyTests.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.structurizr.analysis; - -import com.structurizr.Workspace; -import com.structurizr.model.Component; -import com.structurizr.model.Container; -import com.structurizr.model.Model; -import com.structurizr.model.SoftwareSystem; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; - -public class SpringMvcControllerComponentFinderStrategyTests { - - @Test - public void test_findComponents_FindsSpringMvcControllers() throws Exception { - Workspace workspace = new Workspace("Name", "Description"); - Model model = workspace.getModel(); - SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); - Container container = softwareSystem.addContainer("Name", "Description", "Technology"); - - ComponentFinder componentFinder = new ComponentFinder( - container, - "test.SpringMvcControllerComponentFinderStrategy", - new SpringMvcControllerComponentFinderStrategy() - ); - componentFinder.findComponents(); - - assertEquals(1, container.getComponents().size()); - Component component = container.getComponentWithName("SomeController"); - assertEquals("test.SpringMvcControllerComponentFinderStrategy.SomeController", component.getType().getType()); - assertEquals("", component.getDescription()); - assertEquals("Spring MVC Controller", component.getTechnology()); - } - -} diff --git a/structurizr-spring/test/unit/com/structurizr/analysis/SpringRepositoryComponentFinderStrategyTests.java b/structurizr-spring/test/unit/com/structurizr/analysis/SpringRepositoryComponentFinderStrategyTests.java deleted file mode 100644 index 557af7da0..000000000 --- a/structurizr-spring/test/unit/com/structurizr/analysis/SpringRepositoryComponentFinderStrategyTests.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.structurizr.analysis; - -import com.structurizr.Workspace; -import com.structurizr.model.Component; -import com.structurizr.model.Container; -import com.structurizr.model.Model; -import com.structurizr.model.SoftwareSystem; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; - -public class SpringRepositoryComponentFinderStrategyTests { - - @Test - public void test_findComponents_FindsSpringRepositoriesDefinedWithAnAnnotation() throws Exception { - Workspace workspace = new Workspace("Name", "Description"); - Model model = workspace.getModel(); - SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); - Container container = softwareSystem.addContainer("Name", "Description", "Technology"); - - ComponentFinder componentFinder = new ComponentFinder( - container, - "test.SpringRepositoryComponentFinderStrategy.annotation", - new SpringRepositoryComponentFinderStrategy() - ); - componentFinder.findComponents(); - - // finding the components again should be idempotent - componentFinder.findComponents(); - - assertEquals(1, container.getComponents().size()); - Component component = container.getComponentWithName("SomeRepository"); - assertEquals("test.SpringRepositoryComponentFinderStrategy.annotation.SomeRepository", component.getType().getType()); - assertEquals("", component.getDescription()); - assertEquals("Spring Repository", component.getTechnology()); - } - - @Test - public void test_findComponents_FindsSpringRepositoriesThatExtendJpaRepository() throws Exception { - Workspace workspace = new Workspace("Name", "Description"); - Model model = workspace.getModel(); - SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); - Container container = softwareSystem.addContainer("Name", "Description", "Technology"); - - ComponentFinder componentFinder = new ComponentFinder( - container, - "test.SpringRepositoryComponentFinderStrategy.jpaRepository", - new SpringRepositoryComponentFinderStrategy() - ); - componentFinder.findComponents(); - - assertEquals(1, container.getComponents().size()); - Component component = container.getComponentWithName("SomeJpaRepository"); - assertEquals("test.SpringRepositoryComponentFinderStrategy.jpaRepository.SomeJpaRepository", component.getType().getType()); - assertEquals("", component.getDescription()); - assertEquals("Spring Repository", component.getTechnology()); - } - - @Test - public void test_findComponents_FindsSpringRepositoriesThatExtendCrudRepository() throws Exception { - Workspace workspace = new Workspace("Name", "Description"); - Model model = workspace.getModel(); - SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); - Container container = softwareSystem.addContainer("Name", "Description", "Technology"); - - ComponentFinder componentFinder = new ComponentFinder( - container, - "test.SpringRepositoryComponentFinderStrategy.crudRepository", - new SpringRepositoryComponentFinderStrategy() - ); - componentFinder.findComponents(); - - assertEquals(1, container.getComponents().size()); - Component component = container.getComponentWithName("SomeCrudRepository"); - assertEquals("test.SpringRepositoryComponentFinderStrategy.crudRepository.SomeCrudRepository", component.getType().getType()); - assertEquals("", component.getDescription()); - assertEquals("Spring Repository", component.getTechnology()); - } - -} diff --git a/structurizr-spring/test/unit/com/structurizr/analysis/SpringRestControllerComponentFinderStrategyTests.java b/structurizr-spring/test/unit/com/structurizr/analysis/SpringRestControllerComponentFinderStrategyTests.java deleted file mode 100644 index 9debfcfbf..000000000 --- a/structurizr-spring/test/unit/com/structurizr/analysis/SpringRestControllerComponentFinderStrategyTests.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.structurizr.analysis; - -import com.structurizr.Workspace; -import com.structurizr.model.Component; -import com.structurizr.model.Container; -import com.structurizr.model.Model; -import com.structurizr.model.SoftwareSystem; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; - -public class SpringRestControllerComponentFinderStrategyTests { - - @Test - public void test_findComponents_FindsSpringRestControllers() throws Exception { - Workspace workspace = new Workspace("Name", "Description"); - Model model = workspace.getModel(); - SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); - Container container = softwareSystem.addContainer("Name", "Description", "Technology"); - - ComponentFinder componentFinder = new ComponentFinder( - container, - "test.SpringRestControllerComponentFinderStrategy", - new SpringRestControllerComponentFinderStrategy() - ); - componentFinder.findComponents(); - - assertEquals(1, container.getComponents().size()); - Component component = container.getComponentWithName("SomeController"); - assertEquals("test.SpringRestControllerComponentFinderStrategy.SomeController", component.getType().getType()); - assertEquals("", component.getDescription()); - assertEquals("Spring REST Controller", component.getTechnology()); - } - -} diff --git a/structurizr-spring/test/unit/com/structurizr/analysis/SpringServiceComponentFinderStrategyTests.java b/structurizr-spring/test/unit/com/structurizr/analysis/SpringServiceComponentFinderStrategyTests.java deleted file mode 100644 index eb9a262b2..000000000 --- a/structurizr-spring/test/unit/com/structurizr/analysis/SpringServiceComponentFinderStrategyTests.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.structurizr.analysis; - -import com.structurizr.Workspace; -import com.structurizr.model.Component; -import com.structurizr.model.Container; -import com.structurizr.model.Model; -import com.structurizr.model.SoftwareSystem; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; - -public class SpringServiceComponentFinderStrategyTests { - - @Test - public void test_findComponents_FindsSpringServices() throws Exception { - Workspace workspace = new Workspace("Name", "Description"); - Model model = workspace.getModel(); - SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); - Container container = softwareSystem.addContainer("Name", "Description", "Technology"); - - ComponentFinder componentFinder = new ComponentFinder( - container, - "test.SpringServiceComponentFinderStrategy", - new SpringServiceComponentFinderStrategy() - ); - componentFinder.findComponents(); - - assertEquals(1, container.getComponents().size()); - Component component = container.getComponentWithName("SomeService"); - assertEquals("test.SpringServiceComponentFinderStrategy.SomeService", component.getType().getType()); - assertEquals("", component.getDescription()); - assertEquals("Spring Service", component.getTechnology()); - } - -} diff --git a/structurizr-spring/test/unit/com/structurizr/analysis/SpringWebServiceEndpointComponentFinderStrategyTests.java b/structurizr-spring/test/unit/com/structurizr/analysis/SpringWebServiceEndpointComponentFinderStrategyTests.java deleted file mode 100644 index 1525b6200..000000000 --- a/structurizr-spring/test/unit/com/structurizr/analysis/SpringWebServiceEndpointComponentFinderStrategyTests.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.structurizr.analysis; - -import com.structurizr.Workspace; -import com.structurizr.model.Component; -import com.structurizr.model.Container; -import com.structurizr.model.Model; -import com.structurizr.model.SoftwareSystem; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; - -public class SpringWebServiceEndpointComponentFinderStrategyTests { - - @Test - public void test_findComponents_FindsSpringWebServiceEndpoints() throws Exception { - Workspace workspace = new Workspace("Name", "Description"); - Model model = workspace.getModel(); - SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); - Container container = softwareSystem.addContainer("Name", "Description", "Technology"); - - ComponentFinder componentFinder = new ComponentFinder( - container, - "test.SpringWebServiceEndpointComponentFinderStrategy", - new SpringWebServiceEndpointComponentFinderStrategy() - ); - componentFinder.findComponents(); - - assertEquals(1, container.getComponents().size()); - Component component = container.getComponentWithName("SomeWebService"); - assertEquals("test.SpringWebServiceEndpointComponentFinderStrategy.SomeWebService", component.getType().getType()); - assertEquals("", component.getDescription()); - assertEquals("Spring Web Service", component.getTechnology()); - } - -} \ No newline at end of file diff --git a/structurizr-spring/test/unit/com/structurizr/analysis/myapp/api/SomeApiController.java b/structurizr-spring/test/unit/com/structurizr/analysis/myapp/api/SomeApiController.java deleted file mode 100644 index c59a5b51d..000000000 --- a/structurizr-spring/test/unit/com/structurizr/analysis/myapp/api/SomeApiController.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.structurizr.analysis.myapp.api; - -import com.structurizr.analysis.myapp.service.SomeService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.RestController; - -@RestController -public class SomeApiController { - - @Autowired - private SomeService someService; - - //@RequestMapping(value = "/do/something") - commenting this out removes a dependency on Spring MVC - public String findSomething() { - someService.doSomething(); - - return "{some json}"; - } - -} diff --git a/structurizr-spring/test/unit/com/structurizr/analysis/myapp/data/JdbcSomeRepository.java b/structurizr-spring/test/unit/com/structurizr/analysis/myapp/data/JdbcSomeRepository.java deleted file mode 100644 index 1ed484624..000000000 --- a/structurizr-spring/test/unit/com/structurizr/analysis/myapp/data/JdbcSomeRepository.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.structurizr.analysis.myapp.data; - -import com.structurizr.analysis.myapp.domain.Something; -import org.springframework.stereotype.Repository; - -@Repository -public class JdbcSomeRepository implements SomeRepository { - - @Override - public Something findSomething() { - return new Something(); - } - -} diff --git a/structurizr-spring/test/unit/com/structurizr/analysis/myapp/data/SomeNonPublicRepository.java b/structurizr-spring/test/unit/com/structurizr/analysis/myapp/data/SomeNonPublicRepository.java deleted file mode 100644 index 31cc0fcd2..000000000 --- a/structurizr-spring/test/unit/com/structurizr/analysis/myapp/data/SomeNonPublicRepository.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.structurizr.analysis.myapp.data; - -import org.springframework.data.jpa.repository.JpaRepository; - -interface SomeNonPublicRepository extends JpaRepository { -} diff --git a/structurizr-spring/test/unit/com/structurizr/analysis/myapp/data/SomeOtherRepository.java b/structurizr-spring/test/unit/com/structurizr/analysis/myapp/data/SomeOtherRepository.java deleted file mode 100644 index 406519a0b..000000000 --- a/structurizr-spring/test/unit/com/structurizr/analysis/myapp/data/SomeOtherRepository.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.structurizr.analysis.myapp.data; - -import org.springframework.data.jpa.repository.JpaRepository; - -public interface SomeOtherRepository extends JpaRepository { -} diff --git a/structurizr-spring/test/unit/com/structurizr/analysis/myapp/data/SomeRepository.java b/structurizr-spring/test/unit/com/structurizr/analysis/myapp/data/SomeRepository.java deleted file mode 100644 index 2b75e9824..000000000 --- a/structurizr-spring/test/unit/com/structurizr/analysis/myapp/data/SomeRepository.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.structurizr.analysis.myapp.data; - -import com.structurizr.analysis.myapp.domain.Something; - -public interface SomeRepository { - - Something findSomething(); - -} diff --git a/structurizr-spring/test/unit/com/structurizr/analysis/myapp/domain/Something.java b/structurizr-spring/test/unit/com/structurizr/analysis/myapp/domain/Something.java deleted file mode 100644 index 24e1990bc..000000000 --- a/structurizr-spring/test/unit/com/structurizr/analysis/myapp/domain/Something.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.structurizr.analysis.myapp.domain; - -/** - * Created by structurizr on 15/03/16. - */ -public class Something { -} diff --git a/structurizr-spring/test/unit/com/structurizr/analysis/myapp/service/SomeService.java b/structurizr-spring/test/unit/com/structurizr/analysis/myapp/service/SomeService.java deleted file mode 100644 index c3f7c39ae..000000000 --- a/structurizr-spring/test/unit/com/structurizr/analysis/myapp/service/SomeService.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.structurizr.analysis.myapp.service; - -public interface SomeService { - - void doSomething(); - -} diff --git a/structurizr-spring/test/unit/com/structurizr/analysis/myapp/service/SomeServiceImpl.java b/structurizr-spring/test/unit/com/structurizr/analysis/myapp/service/SomeServiceImpl.java deleted file mode 100644 index efc025ac4..000000000 --- a/structurizr-spring/test/unit/com/structurizr/analysis/myapp/service/SomeServiceImpl.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.structurizr.analysis.myapp.service; - -import com.structurizr.analysis.myapp.data.SomeOtherRepository; -import com.structurizr.analysis.myapp.data.SomeRepository; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -@Service -public class SomeServiceImpl implements SomeService { - - @Autowired - private SomeRepository someRepository; - - @Autowired - private SomeOtherRepository someOtherRepository; - - public void doSomething() { - someRepository.findSomething(); - } - -} diff --git a/structurizr-spring/test/unit/com/structurizr/analysis/myapp/web/SomeController.java b/structurizr-spring/test/unit/com/structurizr/analysis/myapp/web/SomeController.java deleted file mode 100644 index 13d27cad4..000000000 --- a/structurizr-spring/test/unit/com/structurizr/analysis/myapp/web/SomeController.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.structurizr.analysis.myapp.web; - -import com.structurizr.analysis.myapp.service.SomeService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Controller; - -@Controller -public class SomeController { - - @Autowired - private SomeService someService; - - //@RequestMapping(value = "/do/something") - commenting this out removes a dependency on Spring MVC - public String showHomePage() { - someService.doSomething(); - - return "/did/something"; - } - -} diff --git a/structurizr-spring/test/unit/test/AbstractSpringComponentFinderStrategy/SomeController.java b/structurizr-spring/test/unit/test/AbstractSpringComponentFinderStrategy/SomeController.java deleted file mode 100644 index fd9a86573..000000000 --- a/structurizr-spring/test/unit/test/AbstractSpringComponentFinderStrategy/SomeController.java +++ /dev/null @@ -1,7 +0,0 @@ -package test.AbstractSpringComponentFinderStrategy; - -import org.springframework.stereotype.Controller; - -@Controller -public class SomeController { -} diff --git a/structurizr-spring/test/unit/test/AbstractSpringComponentFinderStrategy/SomeNonPublicRepository.java b/structurizr-spring/test/unit/test/AbstractSpringComponentFinderStrategy/SomeNonPublicRepository.java deleted file mode 100644 index df5e39039..000000000 --- a/structurizr-spring/test/unit/test/AbstractSpringComponentFinderStrategy/SomeNonPublicRepository.java +++ /dev/null @@ -1,6 +0,0 @@ -package test.AbstractSpringComponentFinderStrategy; - -import org.springframework.data.jpa.repository.JpaRepository; - -interface SomeNonPublicRepository extends JpaRepository { -} diff --git a/structurizr-spring/test/unit/test/AbstractSpringComponentFinderStrategy/SomePublicRepository.java b/structurizr-spring/test/unit/test/AbstractSpringComponentFinderStrategy/SomePublicRepository.java deleted file mode 100644 index 698dc06a1..000000000 --- a/structurizr-spring/test/unit/test/AbstractSpringComponentFinderStrategy/SomePublicRepository.java +++ /dev/null @@ -1,6 +0,0 @@ -package test.AbstractSpringComponentFinderStrategy; - -import org.springframework.data.jpa.repository.JpaRepository; - -public interface SomePublicRepository extends JpaRepository { -} diff --git a/structurizr-spring/test/unit/test/SpringComponentComponentFinderStrategy/SomeComponent.java b/structurizr-spring/test/unit/test/SpringComponentComponentFinderStrategy/SomeComponent.java deleted file mode 100644 index 40bd7b768..000000000 --- a/structurizr-spring/test/unit/test/SpringComponentComponentFinderStrategy/SomeComponent.java +++ /dev/null @@ -1,4 +0,0 @@ -package test.SpringComponentComponentFinderStrategy; - -public interface SomeComponent { -} diff --git a/structurizr-spring/test/unit/test/SpringComponentComponentFinderStrategy/TheSomeComponentImpl.java b/structurizr-spring/test/unit/test/SpringComponentComponentFinderStrategy/TheSomeComponentImpl.java deleted file mode 100644 index 13160d237..000000000 --- a/structurizr-spring/test/unit/test/SpringComponentComponentFinderStrategy/TheSomeComponentImpl.java +++ /dev/null @@ -1,7 +0,0 @@ -package test.SpringComponentComponentFinderStrategy; - -import org.springframework.stereotype.Component; - -@Component -public class TheSomeComponentImpl implements SomeComponent { -} diff --git a/structurizr-spring/test/unit/test/SpringMvcControllerComponentFinderStrategy/SomeController.java b/structurizr-spring/test/unit/test/SpringMvcControllerComponentFinderStrategy/SomeController.java deleted file mode 100644 index bab504c48..000000000 --- a/structurizr-spring/test/unit/test/SpringMvcControllerComponentFinderStrategy/SomeController.java +++ /dev/null @@ -1,7 +0,0 @@ -package test.SpringMvcControllerComponentFinderStrategy; - -import org.springframework.stereotype.Controller; - -@Controller -public class SomeController { -} diff --git a/structurizr-spring/test/unit/test/SpringRepositoryComponentFinderStrategy/annotation/JdbcSomeRepository.java b/structurizr-spring/test/unit/test/SpringRepositoryComponentFinderStrategy/annotation/JdbcSomeRepository.java deleted file mode 100644 index 9293f71d8..000000000 --- a/structurizr-spring/test/unit/test/SpringRepositoryComponentFinderStrategy/annotation/JdbcSomeRepository.java +++ /dev/null @@ -1,4 +0,0 @@ -package test.SpringRepositoryComponentFinderStrategy.annotation; - -public class JdbcSomeRepository implements SomeRepository { -} diff --git a/structurizr-spring/test/unit/test/SpringRepositoryComponentFinderStrategy/annotation/SomeRepository.java b/structurizr-spring/test/unit/test/SpringRepositoryComponentFinderStrategy/annotation/SomeRepository.java deleted file mode 100644 index 5a32fdab7..000000000 --- a/structurizr-spring/test/unit/test/SpringRepositoryComponentFinderStrategy/annotation/SomeRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package test.SpringRepositoryComponentFinderStrategy.annotation; - -import org.springframework.stereotype.Repository; - -@Repository -public interface SomeRepository { -} diff --git a/structurizr-spring/test/unit/test/SpringRepositoryComponentFinderStrategy/crudRepository/SomeCrudRepository.java b/structurizr-spring/test/unit/test/SpringRepositoryComponentFinderStrategy/crudRepository/SomeCrudRepository.java deleted file mode 100644 index 9bb604e7a..000000000 --- a/structurizr-spring/test/unit/test/SpringRepositoryComponentFinderStrategy/crudRepository/SomeCrudRepository.java +++ /dev/null @@ -1,6 +0,0 @@ -package test.SpringRepositoryComponentFinderStrategy.crudRepository; - -import org.springframework.data.repository.CrudRepository; - -public interface SomeCrudRepository extends CrudRepository { -} diff --git a/structurizr-spring/test/unit/test/SpringRepositoryComponentFinderStrategy/jpaRepository/SomeJpaRepository.java b/structurizr-spring/test/unit/test/SpringRepositoryComponentFinderStrategy/jpaRepository/SomeJpaRepository.java deleted file mode 100644 index 714e8908c..000000000 --- a/structurizr-spring/test/unit/test/SpringRepositoryComponentFinderStrategy/jpaRepository/SomeJpaRepository.java +++ /dev/null @@ -1,6 +0,0 @@ -package test.SpringRepositoryComponentFinderStrategy.jpaRepository; - -import org.springframework.data.jpa.repository.JpaRepository; - -public interface SomeJpaRepository extends JpaRepository { -} diff --git a/structurizr-spring/test/unit/test/SpringRestControllerComponentFinderStrategy/SomeController.java b/structurizr-spring/test/unit/test/SpringRestControllerComponentFinderStrategy/SomeController.java deleted file mode 100644 index d40d58202..000000000 --- a/structurizr-spring/test/unit/test/SpringRestControllerComponentFinderStrategy/SomeController.java +++ /dev/null @@ -1,7 +0,0 @@ -package test.SpringRestControllerComponentFinderStrategy; - -import org.springframework.web.bind.annotation.RestController; - -@RestController -public class SomeController { -} diff --git a/structurizr-spring/test/unit/test/SpringServiceComponentFinderStrategy/SomeService.java b/structurizr-spring/test/unit/test/SpringServiceComponentFinderStrategy/SomeService.java deleted file mode 100644 index 1ff518047..000000000 --- a/structurizr-spring/test/unit/test/SpringServiceComponentFinderStrategy/SomeService.java +++ /dev/null @@ -1,4 +0,0 @@ -package test.SpringServiceComponentFinderStrategy; - -public interface SomeService { -} diff --git a/structurizr-spring/test/unit/test/SpringServiceComponentFinderStrategy/SomeServiceImpl.java b/structurizr-spring/test/unit/test/SpringServiceComponentFinderStrategy/SomeServiceImpl.java deleted file mode 100644 index ee551d589..000000000 --- a/structurizr-spring/test/unit/test/SpringServiceComponentFinderStrategy/SomeServiceImpl.java +++ /dev/null @@ -1,7 +0,0 @@ -package test.SpringServiceComponentFinderStrategy; - -import org.springframework.stereotype.Service; - -@Service -public class SomeServiceImpl implements SomeService { -} diff --git a/structurizr-spring/test/unit/test/SpringWebServiceEndpointComponentFinderStrategy/SomeWebService.java b/structurizr-spring/test/unit/test/SpringWebServiceEndpointComponentFinderStrategy/SomeWebService.java deleted file mode 100644 index 7ca5767af..000000000 --- a/structurizr-spring/test/unit/test/SpringWebServiceEndpointComponentFinderStrategy/SomeWebService.java +++ /dev/null @@ -1,7 +0,0 @@ -package test.SpringWebServiceEndpointComponentFinderStrategy; - -import org.springframework.ws.server.endpoint.annotation.Endpoint; - -@Endpoint -public class SomeWebService { -} \ No newline at end of file diff --git a/structurizr-websequencediagrams/build.gradle b/structurizr-websequencediagrams/build.gradle deleted file mode 100644 index 9a20eb465..000000000 --- a/structurizr-websequencediagrams/build.gradle +++ /dev/null @@ -1,21 +0,0 @@ -dependencies { - - compile project(':structurizr-core') - - testCompile 'junit:junit:4.12' - testCompile 'org.assertj:assertj-core:3.4.0' - -} - -sourceSets { - main { - java { - srcDir 'src' - } - } - test { - java { - srcDir 'test/unit' - } - } -} \ No newline at end of file diff --git a/structurizr-websequencediagrams/src/com/structurizr/io/websequencediagrams/WebSequenceDiagramsEncoder.java b/structurizr-websequencediagrams/src/com/structurizr/io/websequencediagrams/WebSequenceDiagramsEncoder.java deleted file mode 100644 index 82ec34253..000000000 --- a/structurizr-websequencediagrams/src/com/structurizr/io/websequencediagrams/WebSequenceDiagramsEncoder.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.structurizr.io.websequencediagrams; - -import java.nio.charset.StandardCharsets; -import java.util.Base64; - -public class WebSequenceDiagramsEncoder { - - public String encode(String webSequenceDiagramsDefinition) throws Exception { - String base64 = Base64.getEncoder().encodeToString(webSequenceDiagramsDefinition.getBytes(StandardCharsets.UTF_8)); - - // this encoded version is used in URLs so: - // - strip trailing = characters - base64 = base64.replaceAll("=", ""); - - // and switch + characters - base64 = base64.replaceAll("\\+", "-"); - - return base64; - } - -} \ No newline at end of file diff --git a/structurizr-websequencediagrams/src/com/structurizr/io/websequencediagrams/WebSequenceDiagramsWriter.java b/structurizr-websequencediagrams/src/com/structurizr/io/websequencediagrams/WebSequenceDiagramsWriter.java deleted file mode 100644 index 316c25d59..000000000 --- a/structurizr-websequencediagrams/src/com/structurizr/io/websequencediagrams/WebSequenceDiagramsWriter.java +++ /dev/null @@ -1,90 +0,0 @@ -package com.structurizr.io.websequencediagrams; - -import com.structurizr.Workspace; -import com.structurizr.model.InteractionStyle; -import com.structurizr.model.Relationship; -import com.structurizr.view.DynamicView; -import com.structurizr.view.RelationshipView; - -import java.io.IOException; -import java.io.StringWriter; -import java.io.Writer; -import java.util.List; -import java.util.Set; -import java.util.TreeSet; - -/** - * A simple writer that outputs dynamic diagram definitions that can be copy-pasted - * into https://www.websequencediagrams.com - * - * This implementation only supports a basic sequence of interactions, - * both synchronous and asynchronous. It doesn't support return messages, - * parallel behaviour, etc. - */ -public final class WebSequenceDiagramsWriter { - - private static final String SYNCHRONOUS_INTERACTION = "->"; - private static final String ASYNCHRONOUS_INTERACTION = "->>"; - - /** - * Gets a single view as a WebSequenceDiagrams diagram definition. - * - * @param view the view to write - * @return the WebSequenceDiagrams definition as a String - */ - public String toString(DynamicView view) { - StringWriter stringWriter = new StringWriter(); - write(view, stringWriter); - return stringWriter.toString(); - } - - /** - * Writes the dynamic views in the given workspace as WebSequenceDiagrams definitions, to the specified writer. - * - * @param workspace the workspace containing the views to be written - * @param writer the Writer to write to - */ - public void write(Workspace workspace, Writer writer) { - if (workspace != null && writer != null) { - for (DynamicView view : workspace.getViews().getDynamicViews()) { - write(view, writer); - } - } - } - - /** - * Writes the dynamic views in the given workspace as WebSequenceDiagrams definitions, to stdout. - * - * @param workspace the workspace containing the views to be written - */ - public void write(Workspace workspace) { - StringWriter stringWriter = new StringWriter(); - write(workspace, stringWriter); - System.out.println(stringWriter.toString()); - } - - private void write(DynamicView view, Writer writer) { - try { - writer.write("title " + view.getName() + " - " + view.getKey()); - writer.write(System.lineSeparator()); - writer.write(System.lineSeparator()); - - for (RelationshipView relationshipView : view.getRelationships()) { - Relationship r = relationshipView.getRelationship(); - // Thing A->Thing B: Description - writer.write(String.format("%s%s%s: %s", - r.getSource().getName(), - r.getInteractionStyle() == InteractionStyle.Synchronous ? SYNCHRONOUS_INTERACTION : ASYNCHRONOUS_INTERACTION, - r.getDestination().getName(), - relationshipView.getDescription() - )); - writer.write(System.lineSeparator()); - } - - writer.write(System.lineSeparator()); - } catch (IOException ioe) { - ioe.printStackTrace(); - } - } - -} \ No newline at end of file diff --git a/structurizr-websequencediagrams/test/unit/com/structurizr/io/websequencediagrams/WebSequenceDiagramsEncoderTests.java b/structurizr-websequencediagrams/test/unit/com/structurizr/io/websequencediagrams/WebSequenceDiagramsEncoderTests.java deleted file mode 100644 index 5c48389d1..000000000 --- a/structurizr-websequencediagrams/test/unit/com/structurizr/io/websequencediagrams/WebSequenceDiagramsEncoderTests.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.structurizr.io.websequencediagrams; - -import org.junit.Ignore; -import org.junit.Test; - -import java.util.Base64; - -import static org.junit.Assert.assertEquals; - -public class WebSequenceDiagramsEncoderTests { - - @Test - @Ignore - public void test_encode() throws Exception { - // this test does not work, and requires a proper implementation of the algorithm used to encode diagram definitions - // TODO - // - String decoded = new String(Base64.getDecoder().decode("dGl0bGUgQXV0aGVudGljYXRpb24gU2VxdWVuY2UKCkFsaWNlLT5Cb2I6ABUQUmVxdWVzdApub3RlIHJpZ2h0IG9mIAAlBUJvYiB0aGlua3MgYWJvdXQgaXQKQm9iLT4ASgUANxNzcG9uc2UK")); - System.out.println(decoded); - - for (char c : decoded.toCharArray()) { - System.out.println(c + " -> " + (int)c); - } - - String encoded = new WebSequenceDiagramsEncoder().encode( - "title Authentication Sequence\n" + - "\n" + - "Alice->Bob: Authentication Request\n" + - "note right of Bob: Bob thinks about it\n" + - "Bob->Alice: Authentication Response\n"); - - System.out.println(encoded); - - assertEquals("dGl0bGUgQXV0aGVudGljYXRpb24gU2VxdWVuY2UKCkFsaWNlLT5Cb2I6ABUQUmVxdWVzdApub3RlIHJpZ2h0IG9mIAAlBUJvYiB0aGlua3MgYWJvdXQgaXQKQm9iLT4ASgUANxNzcG9uc2UK", encoded); - } - -} \ No newline at end of file diff --git a/structurizr-websequencediagrams/test/unit/com/structurizr/io/websequencediagrams/WebSequenceDiagramsWriterTests.java b/structurizr-websequencediagrams/test/unit/com/structurizr/io/websequencediagrams/WebSequenceDiagramsWriterTests.java deleted file mode 100644 index 9297011cc..000000000 --- a/structurizr-websequencediagrams/test/unit/com/structurizr/io/websequencediagrams/WebSequenceDiagramsWriterTests.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.structurizr.io.websequencediagrams; - -import com.structurizr.Workspace; -import com.structurizr.model.InteractionStyle; -import com.structurizr.model.Model; -import com.structurizr.model.Relationship; -import com.structurizr.model.SoftwareSystem; -import com.structurizr.view.DynamicView; -import org.junit.Before; -import org.junit.Test; - -import java.io.StringWriter; - -import static org.junit.Assert.assertEquals; - -public class WebSequenceDiagramsWriterTests { - - private WebSequenceDiagramsWriter webSequenceDiagramsWriter; - private Workspace workspace; - private StringWriter stringWriter; - - @Before - public void setUp() { - webSequenceDiagramsWriter = new WebSequenceDiagramsWriter(); - workspace = new Workspace("Name", "Description"); - stringWriter = new StringWriter(); - } - - @Test - public void test_write_DoesNotThrowAnExceptionWhenPassedNullParameters() throws Exception { - webSequenceDiagramsWriter.write(null, null); - webSequenceDiagramsWriter.write(workspace, null); - webSequenceDiagramsWriter.write(null, stringWriter); - } - - @Test - public void test_write_createsAWebSequenceDiagram() throws Exception { - Model model = workspace.getModel(); - SoftwareSystem a = model.addSoftwareSystem("System A", ""); - SoftwareSystem b = model.addSoftwareSystem("System B", ""); - SoftwareSystem c = model.addSoftwareSystem("System C", ""); - - a.uses(b, ""); - b.uses(c, "", "", InteractionStyle.Asynchronous); - DynamicView view = workspace.getViews().createDynamicView("Some Key", "A description of the diagram"); - view.add(a, "Does something using", b); - view.add(b, "Does something then using", c); - - webSequenceDiagramsWriter.write(workspace, stringWriter); - assertEquals("title Dynamic - Some Key" + System.lineSeparator() + - System.lineSeparator() + - "System A->System B: Does something using" + System.lineSeparator() + - "System B->>System C: Does something then using" + System.lineSeparator() + - System.lineSeparator(), stringWriter.toString()); - } - -} From 3bb86afb2ebed14ae5cf20c8616b638d6c8a759a Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 22 Nov 2019 13:45:00 +0000 Subject: [PATCH 047/717] Updated docs to reflect repo split. --- README.md | 2 +- docs/getting-started.md | 2 +- docs/images/graphviz-getting-started.png | Bin 27996 -> 0 bytes .../graphviz-spring-petclinic-components.png | Bin 81956 -> 0 bytes docs/images/plantuml-getting-started.png | Bin 11919 -> 0 bytes .../plantuml-spring-petclinic-components.png | Bin 91517 -> 0 bytes .../plantuml-spring-petclinic-containers.png | Bin 27541 -> 0 bytes ...pring-petclinic-deployment-development.png | Bin 30461 -> 0 bytes ...ntuml-spring-petclinic-deployment-live.png | Bin 44352 -> 0 bytes ...ml-spring-petclinic-deployment-staging.png | Bin 23187 -> 0 bytes .../plantuml-spring-petclinic-dynamic.png | Bin 44236 -> 0 bytes ...antuml-spring-petclinic-system-context.png | Bin 15133 -> 0 bytes docs/images/spring-petclinic-1.png | Bin 124626 -> 0 bytes docs/images/spring-petclinic-plantuml.png | Bin 52037 -> 0 bytes docs/images/structurizr-annotations-1.png | Bin 223168 -> 0 bytes docs/images/supporting-types-1.png | Bin 25623 -> 0 bytes docs/images/supporting-types-2.png | Bin 34656 -> 0 bytes docs/images/supporting-types-3.png | Bin 97705 -> 0 bytes docs/images/supporting-types-4.png | Bin 48643 -> 0 bytes docs/images/supporting-types-5.png | Bin 75433 -> 0 bytes docs/images/supporting-types-6.gif | Bin 1606481 -> 0 bytes docs/images/supporting-types-7.png | Bin 103949 -> 0 bytes docs/images/supporting-types-8.png | Bin 99623 -> 0 bytes .../visualising-software-architecture.png | Bin 973363 -> 0 bytes docs/images/websequencediagrams-1.png | Bin 49694 -> 0 bytes 25 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 docs/images/graphviz-getting-started.png delete mode 100644 docs/images/graphviz-spring-petclinic-components.png delete mode 100644 docs/images/plantuml-getting-started.png delete mode 100644 docs/images/plantuml-spring-petclinic-components.png delete mode 100644 docs/images/plantuml-spring-petclinic-containers.png delete mode 100644 docs/images/plantuml-spring-petclinic-deployment-development.png delete mode 100644 docs/images/plantuml-spring-petclinic-deployment-live.png delete mode 100644 docs/images/plantuml-spring-petclinic-deployment-staging.png delete mode 100644 docs/images/plantuml-spring-petclinic-dynamic.png delete mode 100644 docs/images/plantuml-spring-petclinic-system-context.png delete mode 100644 docs/images/spring-petclinic-1.png delete mode 100644 docs/images/spring-petclinic-plantuml.png delete mode 100644 docs/images/structurizr-annotations-1.png delete mode 100644 docs/images/supporting-types-1.png delete mode 100644 docs/images/supporting-types-2.png delete mode 100644 docs/images/supporting-types-3.png delete mode 100644 docs/images/supporting-types-4.png delete mode 100644 docs/images/supporting-types-5.png delete mode 100644 docs/images/supporting-types-6.gif delete mode 100644 docs/images/supporting-types-7.png delete mode 100644 docs/images/supporting-types-8.png delete mode 100644 docs/images/visualising-software-architecture.png delete mode 100644 docs/images/websequencediagrams-1.png diff --git a/README.md b/README.md index 12fe995a7..19556fb42 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ The view can then be exported to be visualised using the [Structurizr service](h * [C4 model](https://c4model.com) * [Examples](https://structurizr.com/help/examples) * [Binaries](docs/binaries.md) + * [Building from source](docs/building.md) * [API client](docs/api-client.md) * [Usage patterns](docs/usage-patterns.md) * Model @@ -64,7 +65,6 @@ The view can then be exported to be visualised using the [Structurizr service](h * [HTTP-based health checks](docs/health-checks.md) * [Client-side encryption](docs/client-side-encryption.md) * [Corporate branding](docs/corporate-branding.md) - * [Building from source](docs/building.md) * Related projects * [java-quickstart](https://github.com/structurizr/java-quickstart): A simple starting point for using Structurizr for Java * [java-extensions](https://github.com/structurizr/java-extensions): A collection of Structurizr for Java extensions; including the ability to extract software architecture information from code, export views to PlantUML, etc. diff --git a/docs/getting-started.md b/docs/getting-started.md index d6491d3bd..e1d9cb010 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -12,7 +12,7 @@ The Structurizr for Java binaries are hosted on [Maven Central](https://repo1.ma Name | Description ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-client:1.3.1 | The Structurizr API client library. +com.structurizr:structurizr-client:1.3.2 | The Structurizr API client library. ## 2. Create a Java program diff --git a/docs/images/graphviz-getting-started.png b/docs/images/graphviz-getting-started.png deleted file mode 100644 index bd30bbdfb3ee43b3d674a91b604416330162903a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27996 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 AKmY&$ diff --git a/docs/images/graphviz-spring-petclinic-components.png b/docs/images/graphviz-spring-petclinic-components.png deleted file mode 100644 index a8b00fdd4e739c254faa2de540838be4e94be4a4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 81956 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 diff --git a/docs/images/plantuml-getting-started.png b/docs/images/plantuml-getting-started.png deleted file mode 100644 index 777f66e52a0efdcde5851bf005f6eb5dd10843f8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11919 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%0D)A)>9;0XDatT|reVO02ahot%gf~qlsf0xD0 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 TFTn3?p*@mUdsy|r?8W~BDYZd* diff --git a/docs/images/plantuml-spring-petclinic-deployment-development.png b/docs/images/plantuml-spring-petclinic-deployment-development.png deleted file mode 100644 index 318e579e7492e2d1a1a58bee354179daf9057432..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30461 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 diff --git a/docs/images/plantuml-spring-petclinic-deployment-live.png b/docs/images/plantuml-spring-petclinic-deployment-live.png deleted file mode 100644 index 8948c85b42a677bfb280a6ee8d4c310fd4818165..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44352 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�T+Ky;2$Q=`vkmw%Dl-!yXHYdh<+It5qs(>8P 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 diff --git a/docs/images/plantuml-spring-petclinic-dynamic.png b/docs/images/plantuml-spring-petclinic-dynamic.png deleted file mode 100644 index 5877893b47d92b06e1c63b48c1eb57b8cc053091..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44236 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

RoOe0nY>?bh$)n2mcBLp2Jb&+I6eY;Rlp4{y` zVZAPLnpd8@QH?Q{2v0v#NS-`8`{gY>{G`UfX%n_%9st4>a%NQEL3c++4lm1=aNcA`0f%pl8xI_=&#iTdwH%zc^;&+d4cUX5am%e9w~N zbAF=0W=Yh1?j5U9>sZ+KOfs4R8qoD! z6{reaZVmF5NA2s4<=1_r?weIXcmMP^2-VIbly^Z$wXYhwMYcs0DoV$N2TaF6(jJb~ zi)@LHw>h#Mi_eL^%F6p{e633rKHSLk@BW1cBOAjj-iZVy@BMc$z@Zg;BuSByO&Q7L zvyx=|w(*=sbNTF-WYcGiJ~?x#`8ugj-_D-Xn3r6tI6n9A41BXzX!g0~YTsrO&)XU_ zO_F~gQx9QQL2;WTs=Ke0xGt$Iux8_ak3xmsk58f3)RYAtNTfT;l$9+n25>S=8(+Ou zf^vWw=grn%w)4k7lNB4y1U2(FuY5aulIWg3V~Iu>eYABoE2~J>=I9G%!Xou9Hw3sg z2AoznH9Mz>n0Ah7B-5CH8H8vwnrF8>OxEU~)*q8(3~Yz#>nR_ew^HN{qQ7`9gcbbP}8IW6kpmbgU5DWiAP2v z(V1om&+3quRw(iI{P1y*UEsbS=AF{76u)?q6$q=c#{I>5P5+=WBR5cEXBfJmzbxLv zyApO*dbBN%X^);$o8Kb49OWz!^6=qn6VJAcd`mEO|LP+k;E3!(-Jouv zR%ZCE{kE*3M9ZJXx5gjwt`TRFXt0hZfy6`D2K&4k6^6t*9M-VAxfT5 zxaC{2DB*(WLAkA#Xza8;WGM}4nRage=z29QzDORAE+6g&f!BqHzL1GnJ$L=~%K%oI z&ZvIQwQ2h=QWN!ge7u8(kruOaqx8zL3C>pfQwia#TET7x=4vV;1|%BH7v7G0oS^$0 zfVk*if17DLrZ@65CJ<#{zU5{h9%sw{A$-(KzBd;cVV)k`NDt7n?#L_~On~+HE?qo) zYjnyOPlTv`LLp;n6oN=?Ta6)ng^# zJs!#~BL&QU`UB3C2BOYT)Lcro%2ummnOfO;s#Q;iAfxyFlg8vLYLko)PS{M8Alxyi z$-SUvD14%nGp`L`)Z9xq^Iq2PX5}j6u{UJyZVdG;P@Q9bWYfT+n0MPrN zZD&5W`&N?y5Y}^1Ta{RkkY64$*C&HaM>}4_EYoBDY!x2&)ieQj-e!Zg8Oqz!5FLw; zkiBXa|6HL9WUnGp4s;^tyng5seIVl1pL=iqBy(#j5;^)s%jL%> zmT|@0*XAc|)_c@LfrMq1zV!#R;{`)n!S^?pE(x!UaA~@dvf)N#wP1DHvvF+U(@)fDvq=bRFdqbBBLmigh6cNFfXmrN}h+fKiO| z6m+Do=8)nh`aR$Vm^)C5eX0_@_Vy~lK+t%UZEMnyhW;{UFGwZ6R`B@!?Tg;#>2H~Q zhLu0PW={zFXbBDEsot7~>E=^6kW*^hZGHa#6!PWqP_2LbC0$7(im{Ykl9IBdmMqq-Bk3w-YVOYOX4u`XXR7Vk`m}HY>_hE0Lk;590i%tUD~_c}Lk=8?3V?W&9S z#@wH_nh)&@B!_xk9gE;Qtkik-n3*Fwl2renS{)WT(I|3~_oK_<)a?SRI)p>Rv~ip{ zJUSTQRT5|}1$UBDPnow)XwVQ< zs~_mFsRar)>1%6w6>FNID={@V%*2?lD~?~6&mNe2yzVlrf*)7^W!Hr0I`C9iotBO+ zdxmw1j#D{K_Zh%r$10}F1`2%c;`5QIldx442YHu+cU8FQhl8jkgQ{JU^F7NX{PfOw z)IO|ed|Dg1%~=^eIvly=`nV|eELLjtH~<7+De*Qox*co2pfh6DbsJT0Bh`2%OkXu; zhfsWB*wU-9FV+@^fTA%T63-%5($yC5(>g{khK6ZI^wpO!B{+#Kp%$HVo2rJuW0w;@ z>{Hs-!9mF}gKu2odC$2QCNR(3oykUbe8?o13~Ni}#dd@uDz*E)oo_HHC;p>JA7i<4 zc2oZ0P)FWzjl|B}E|A+8k~uwImv5z7K4Ge2c()Ycb{A=A@r>bKbNGCSY<}&xSo6nowx$`iM2K*ZZ*W8_eohlGlohbQ+s3M(BA) zJ8@n@j-iWu9zEP;$NU%BaMhu@TmBl~d&EW^ukxOSuZ{*t zM0u+vUfUI1-U!Q=c=ysa90js31jrx;?B!$d`6|VRrV`(h?0Am-sg()Xy=hs{ zL{({n(y&`~=liJ>()YC1iN`OCe#hp`4)=G2n|uZY@n-|BwiFCPfVXb{nD~{%4DEWY zVG>B`N3Nga+P0VXI_~6R{{=_?sm{-$r0)jbyB&Qaq5k(RIkgA^5-HIH^(cS96CRqX zR=m*xSU5I_ZrhvFS^?aHv9*OZRmj!Ae7h}bG?_|Ae?C+`Xcb0(PbPq#U9>jsi^C$y z#V5Qv&HDkgioWVVIv!8m|DwVg7g^!)6HJn@rO`C{j_tPzuP93S0TMDM(`>sC0+D4N%46%;YsB%da52Kp@MThEcbhbMq-xx+-X>nguNi*`#rfy z4ePUNh8Tbh&5($x3&0r-^i8D(*j;d`s3ujT? z^%T@n>^FqD_Sge|0AGaLqNukSX-svxZ>z~R8NL-rmxp@2)(3mhR=aJz?u&D4KqDTSJG(u%T3$*wIV?2j|&A9IMSA-%6Jmy+HL#SlFfX5P;h zJj`ScQa#f=vQzFZp6sEc&~;+&4*}!mSo$4M}QK*Bd7hVz~~Os&c7{1%JSFyW1NfB?X_y z$A4k!{P-xmg!zDFc1NiyW(0j7EG{D~I}b{FAZDKNSev2!2+tZv1~H-_H^{jc<$5y74YId!wMvY1J3eBwNvZsi3dnM*%u_8!o5$RuUoQ zKgOUHsshAD6Dg@)XL3bc)PiF*x|N78w2KoeR=!8~Y-L0Wz~4^2=`=6ZH;UjR`qXR{ zRgq%$)=zQ;^(W)ltIbmExw}WZY^=LB)tswhbX~9=0oKT?nn;y-3*DtJV;#m)hd8U5 z9DC8xh2cwqL$*H|4%#g}pgR$p?pMp}TGr?GibV#Tt%=Q?b&QmWN3esY-3!ECk{+df z{dBH!o{$Ll1j2pw-Zwvy)1p={xl|H!jBxzQgY9BsjyiUZp-cYr zjr)@p4~&3mbE&y4Y`G+O$VlRvjR7x-2$7)Gj`%Pe_)?Aq!6vq$LiogaSAuPmkg3Y= zCc;+8Thg=pkKo(&avyjXeG<_KO7s-d4g@F4r)FpJ?ri~uQy75*pV3Qcv@?@^BmFN( z5CqrV0Om^%OsMZNQEDS6_YKCEhOOtpE(u@=j}REYJ!mODqK zRBsVbfegQt>vdu@cQGuN3Phx@y)Va27*{%=!9xs)z{8!Svw-ECN1hJx-McY^et8kI z%B6fIW)+=v_wP5ui!MmxSY!toS-tS3Zim7UaI<;Q-8rZ6o?=q0abvIZGBA4;s5jBR zuYje7GT_Q_O1Qn~ZBq2b%G9<*C9Dt_iLQ7YT(0WFJmh36}w|xZ=e` zLH!wgf&tv3SB^EIt2Khm{Cu;Z&nc4M6P6Xo7avjyIV*w&`rC41iohharMGnSdlg=) zPugV2R!B&&G|Ej4`7WIKl3EC+M8KNb89(XnbB>__0ZqL^`x=>uwQmAmFJ8c{EF3E0A0%n0Ii;1^1tmK>UjZieZ z9bGB0sAEDtnh|wj{*os}<{Lhl`aV(62tA4-y8XqkmA5$ws=LNn{2662(*=8qGOHn( zL04UPpjkfX-^>_VnQ})s)Z=G!BmWkC8$<%U2!A5;ShlV`d4I+F^9XJjNk?~STV0lx z`6G(2cqQZZtd4IS@BT-x?`$u=iW|6AP5^o{M-uSN(#caI71kZ!n+_J{IasK?8-Gf2 z4baoIRTlxiRU$U)yFo50%O6}Q>rJ{G{fuFpO2U4>cF75MMlv0UrYG>OgF;s0L;7;j zD;3w=24cAP3cWx!Wc59LN(|g53?Ij-cj*bay#?^%)fHvafRqvvL1w0g(Ggm7Pw!)u zHr@@rpL-|`RI~SHnf@p)Y3<-P5{>dmr>)~8yI0;X;k90b0I77sk)nJ#1+t!9yKK)* zdmS4=GD$FYSWgwln~}k!pm@Vwu3|^GW4+cnL3NA<3 zJZq@M=GsZ()_#(y@QW6Om{dZ*zH=g4amt$=U3wxV&zzu$XjnYnfT4IZbRT9(#B z!seL2?m=O@{^YkGZxcCSyi*|5g^%ziZBiH*LK<|iRBUj_JyE*!!IHH#Dx%QAZ}i=u zD$67jAo_&((d013M02MFVknS2xbWFjHGjZWUV08tmL6s4zB8i$7+w)}2M^5ZmW!G0 z!`3JDkN}YX$eOH^xhrWkD@A^xk3{?IvnI(6T+b3ha()y7O}JD7s6eSt&#n?l(Mvae zAA;O5$aq2pLa^&?avwv2rKydsQ7{Y1vxad|;;7K2F8@B)q^?_MD@C{L1*>9Gj1(v{ z3=CaW&#t&d_f9h{R`zl#R&$yXp!}!gjdr~;&}$Bt@RKzaoK49KEK@&Og00XzvX}Zj zTJ-wDI zb=jJ30Vcj1rM>g?RM|EU8gg9PmJ7^f-C};jh+X!1VGu|5b4t2&Gbj41S-@RN>Y!Zo zn0;i++9%d9Hf|i#-yj=Yay+Bj7|jVUkRWLCygOHS`ah|PV& zay1D8llQEJ?}<%i%m%cc8fn7j6yuAAKw6<;S@coJbkTMgmX}XqdF$Oj^2z+yBT&ir43CUo>b<&09o`31JjEf6)GJ? zX6j^WZ`>EVl#AE=y9*6hI6Pvt zNC&x*MHF?1dciQ=YQp99>NH{)loa2h(Q~o%yHP(GNSw6z-$JLZl?0;v^*ETli;67x zc!fK>DHL7E9cGSTEsC#rlzsAm(<%&Smnzz@Z9g8ZFR))UW%!f*P`gVzrmOgO7mUGk zU9jLKg{+#OGLvwiG++gC1 zu=k8MJfXSE+jekfn>suxaVKY#Ae6v>kk=UF6$=T3vmhdL1>w`aoHB@}&)-F?J@Gkk zbnJn3_aXTLU*C?eJ5CK^*ayHwBHvo@;B4e^u}<7v!06 zjTpSsUzzO1Y=-?z(vcd#j;$lNVbbMTv}yN+uzj88a8o~-@INSPRqUn0fE{Lz3t`9Z zP#yu6PH9*Gj!Hg+e~>wlPu&NwQA0ZM_32s_DDwd{cr{aezCmMx3Rx`%;%YwnFkUN& z2fBhZtWa?Rhl^g+&?M4jqgAY0PtfxEIgkPzUI3xLsc>jLqd_}(q0H7I6(F>ZrPLV(9J+S*K zhJB+Cl_~wq5Wi%@ZGFI@{WE}g|88gRN+@pE=OaKm$1V=}_WMD{)iXpkgXU%XN0dqT zdFNP9eEVOXukyP^cQ^L>XKhDvDe+zyW%JrR0(j>NnEyc#{&^ASYWZjwUR}tQP(4dl?*}LL9Dh!9+y`=NTLYnw z4MgqF^`Ar?Kfo=v#z^wkjqcm|udO2{A8I!F$Gk4CHyau01j~=9UIq3$b>^zYuOMCT zP#S8(tZTl3K>rF9xYKIG^aT$VyuAYLpYTeS?r~|3wXsKTlinjQ#ND7N-?uE^b=ZG2 z%xo!UP)p{{qqJN4Wy8qxPqnQ=uYlUo_=4nH@CitLDd78?*aQ2Rc5@#uTK+2@OGOcJ zwcGmo<)^a~Pq$jkN$bUOAO4(eQ;)nQ>x?|X5c^I^xEGiZVpky6G6u#VuiRuQp4-WP6oZS7GN=S=sE7Idsd2<1 zv9`A(aSr8%9+c<9Ux%cKZTwn;mgOf+(k8HF2l_(kLtaM|vY!`Lpa-!P)L%OE##|PJ zIe@2M)jdp6Umx9S!9B@f<4m*9`)Rx4OGf(KAgzBESOb3&9wnM8w>e6ZSJ34;V$T5) z5F#I)e$&}aKX<|7dE&K`Lf*)Gvc@w#Q36&evX^61+){H|K*Hhrhr&Z85ba(B96i;~ zKp8C=y4Vp~bH_U~QI2ln2zJrb`A-$qKLn}Ki^|L%-mo=b`v#@}=gP}cR%&$6yA8j| zpmv9LswHxUu*ooDN9Nk-{$?P3?KA<92b*)M4Y?7aG)h0vHOE#}C@(G{0ZHqO|)E?wTW! z{BzsM;qX{*+oQesB8V;pNapzagP_H($ELnFn3m7;zJ>YkGi`tyB@TJfZ?CVs6cHOR za?{y3PO9Kk*<)C`9O1B4xU60j1-W!@xQE27h?hIDpCGkUdo%$`ITmdnUo9}hnh|0? zB=zLfb|6$b9+5d}4tN)4LQUM_LAq25bIJGQw|JYPpu!@0S-O{%tMln;{%$J!4vTsJ zrct(q8Rzp1h$nPvD>bOW>!M9i`iLU$6p@(EVHpQ z?S_r?z#mf}4GiFmj}wAvt|Qg@RsZK2*P^)-YhZyvV&mw$<+#{a-Qs zTuJ*Q)n)*R{?RaZx(@IB*S(nV|32p5R?1wVAaYq&EarOW3cZTMa^%OY{HS^=*Z z{y#r^$MtZjs!1I@i{EwXy7FHW9tBvsG|p1(&ewR64( zh#_|Kw?Zu`b_nxZe3ck`_Qf|2N}S!M_dl2MFGDh(d#YHt(Q=J4boLXa@w~iO2j4tS YX_KSac}cCtP2k7G(EN0PzEi~i0qLZ)ZU6uP diff --git a/docs/images/spring-petclinic-plantuml.png b/docs/images/spring-petclinic-plantuml.png deleted file mode 100644 index 8abf389197220b779139968555ca56c04a4a1cf8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 52037 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

  • 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 L=&2X0*@pZV)S@e4 diff --git a/docs/images/supporting-types-1.png b/docs/images/supporting-types-1.png deleted file mode 100644 index 868effabf3481028acae736336b5ccd5b676ecea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25623 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 diff --git a/docs/images/supporting-types-2.png b/docs/images/supporting-types-2.png deleted file mode 100644 index 361ef737713b56afeec6a9397d7f3b134dfcb292..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34656 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/bM1&-rnhS%Xam7ggrd-f{k^l)J)AJwjPQ)DN)I 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%0AvprBm|L84EZ@0&31_~vXoeft= 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 diff --git a/docs/images/supporting-types-3.png b/docs/images/supporting-types-3.png deleted file mode 100644 index 2fc019eeaf4777943641caa41849680bfaabd7e3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 97705 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 diff --git a/docs/images/supporting-types-4.png b/docs/images/supporting-types-4.png deleted file mode 100644 index a493ecae1cf4f5041d3602a92f15fdcdb89224e8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48643 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 diff --git a/docs/images/supporting-types-5.png b/docs/images/supporting-types-5.png deleted file mode 100644 index 1caa44f190211d372e3c35ced9a3997549241d4f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 75433 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�CJSQ4N4WcXt5l(|vmdWo#ta0C2Uy@J#}o>ws*+O!8|z z3)rY1rwwCyb=&CWl|(|d9%~pI5UJ~OPpM8g=-gC{pSnK_Gjjz2;)G{INc?q+khZ`9 zSRe3OgnjeW3u>m0SG7RvS=HTpe3_9R6TUUEk~k`e>#d(p5@ExKJUG||s4A;cs0&e; zbZH6Jjk}(RgRa*uwC;zkgqJ=d4Jekf9MYw9_+A%Ay6eXz$Z+Av8*PG)!HoYM}N#202-?RiYuI3rZdo5)K== zOxoU7UhpcQEu-^)eWiIh zN8E+oIbDObN*RxoogV>59?pwXdgNro)4eWS9CHE8J*1EW03_kF(aI+OsXp7&R47|p z+ok&&>Y!HI+-0xpi$Rr*X<_@SHZJ$}dp4W?_Rh=7)M;F(>};DLokiuY{%qs) z%$xkuXIOP-B0lxH>R$K6+WaIJ_sKGEOPosF@)q>+*~<`gH=bI2TiVlSwQwOAGSVJO7Idch zCYj7I&DnGk(^sMZ9MNq;@Hgy6u~( z+IJ_c4uBlO7;GG5Z%uTlIuHN8d$;}Kz1i!+vMOa9$uyECowcTWvKeG#zx(zp?pIvM zUM@b<9{f9>q!Aw`d`%!qM1h;a-8f9F-?Q2gAF&Z9^7hw(^^4&}d*duS3U`wz)d5b^ zg}bF3-x1F9qomctR(Ol{Hs@PnwhNd9(<33HlrA`Z4NFe~WJrkEumy8&#W^mb%n z`fRe3(PVSnVEJr&Ugy~R?dfJKCUV5Q*t-4jUZ}Uny<+B=mW1UhvUq5gej}3j=-f1<&xsBLe{7bj)fZF4BIFMbJ zb*;m&*u=G=l2+ZOV{%L7i^P!twVI)qaPW#2Qu=7q|!rJ;|%1CJaL3Lb(PtQl# zh&#f0VyXF=3j#jXH?i~e6|3_b>EHAN%qVs)pQ|}dK6QUWw==QgBENvJ$?7{kqKcq=<#?0h_N#gM?ZcTdynJB|hmIB$Bk4M~2@l8Z&G?_@; zCY#kzO_2W0?vjV4Q=PdgF1XrRY2px_g^qN7MkP=oFxf4qsZqu&7ultuSXk)u8KoA^ zy`_-+D9bN@IK!$M-_~h-tX!5nr}>9;w=kvVMm_9EJyDi7X~N9;ae{fPmf*+@!+@ekg|oLeTOLDeGK_0)u%{;Ks8EjZf+}Q(g-N;@;=@lkBKq%Qr=n~=SRCO zMS%VmLp7eI*bX&NCi3@wT9}m2fiAO*_bn_I>kM*A)u8K@pL z(vqX<=xEuKYhlVANk`BN4j#&*&vD%W-{!*aTMpMml3~3!W~_Q7#T|x4Zqv!$GyB=S zwyPl%Wk0Ldv%YV2WIFtO09(KR4wuk5J({U>SIWFX-o;4nLXFEqiQnTvA>%%C62?

    faz0V7Qne?kn#{O zzRCNCgxlt+_wgx(Dh{m3X_lJ)){M&Bg|c9;e|CBGb~THMpqb-fpp!2V&Xl6C6r_c| zKvgi-<_N{cR|p0&!D`X(q!}O}g_dJyjTliae{Az%@$%d8n&eADq*x(FQf>QF;_kQa z)c($3+GL=jdt7&~`Tzk$8&_XLS~+ka4r)=&d}T6io_8c+l8M2CR9pBeY~qm5I}U+hB!wyzZnpRS+br5)T(l#*&b&~=LI5dhfmk-yS>DvJEPrI7 zIKFd0V<&pt2fqctxckbRSqh!}uVlD?zjQ!Ktyl{Pz_N+;DC^~XJ*R9LfjdM$xGYim z!&}g2E0*D956Zto_Al%x$P8n$jj<-g2%ljj4*~RTC{PH1>^1nz)}3Sddee0L*wIIE z)6>C|-us`@aIUgPT-N4X;UxNU{R?5en<~lupR=oMgR9v`cT1AXztR0pBzLa{&v$cH zQxMA*7)+B%^23X7061Qmf4GS4JHXiFVxhjb7GrvZJFy z8cPMqwsy&r&mALmvXq*6FmKK9qjO;)bL7!1${&H@WERDvk-rCH=#4J_rl-U@4VRVPq}&$T?pM#H491xM0p+g_Td>Z0NtzmrBecN-TBC zbg!1#>zs4BTH*)5th~#cjwrT+RDG+lllv%kdlQ*zco+!V4oD1vRF%O%-<4jE>44!Y zzQLeNwPQ*EPx1A6TVnWjGFB#&hR~e1I}_lW>#RuNhI`B_4A}8NOp>#;x`%+&mmTe) z8=YvaIRrmCR72#_)V#r@Sg`h-f_6RXYU8zQ@z#x@k($BQ+VX6bPL`rb4#X6@+Hp-; zFmMxH^^gbG_GOchVAXb3fMR1d>3UlS=ynHRky)bH&Bs!CfuxIfVyJ?nyub*(j?hnc z(y!n3x!&n>9k$4=r2G4K@Ky9_;_Xb*5`aG+fiK&t#Oi~T=iZQbOP5~QwZBu_n{$bhX+C#4%_N2)b%u3=};*n#R`lDCB zGbKm)eoh}A4t&u&PLt)bFvlxvz`WoRF-Z?Vq^?pvLk!o6H1-%)@Bp7)iVA4~4XarD z#@Jt3-uDR!j<&K^CkxVbVtkv#7G7)(gk)(E`d-!iihfrpE7b>pLPLNjyy)50cR2OAfJAM)`{Md_{mwj<`8({|O85dPctV zjK`pB_+RKOB`{CB2_L3Hg%Uu6Kke-!<&^=dp@(B%X!iU&7O_(&O!{@1rXbVQ`Y84y z-)-Q_)bLV=9jGP7cj%^wTnLh*fL``bj`aij6iALe*SImHnKl2V!S^Ej;SsJ(GheBh zA3p3)vuA&EHw5~4fX5Z&g|X8h=QQ{SUS9UQESFH8;SmA=CD3FlmFFL44TGrq&9~4f zu>9wIEM)XwI!LO1iwD}nEC=(T&b&`91E0)RkBFh5eOcdQ2KolF6go747O*TBkcnN9 zPnk!FR8FgM)n~?H`vlchb77ML#WVI70VdLxy6FW1ge$(yv|f76rB;7my0wE{S@~gb*Z{{2`GF$v~wiK9$gD?@~ z*W%qYIr(d0U#*S7RHl30+iZ98QAB_NlsLnxc ze4Cvu;b(YY!0Pv-f7yHq;cakBjM3gHvN448$d3G|U;jN!3D5qc+P?2}_HzBIt&UBZ zRb6;A08J!izlB@6PTN(C$!HoY-{i}RZd;4r_+P&4GyJ~jEPue&equKhoJ-Aa5FPYa z4%`|Ty2m$AGdl)40mDNk=QK4V=l-+)wRJ^q zaxF}gx4fGP2Utq$B(LjuqSehl&%~hDD4A|P8qZ{$HVrukQVq)^If9QTzdXH%LBk`?4l><}Kes6m$up$(8{i z6CPuqM`PY`-)J8-?F|mFwzQIfCK~JS|1TW*1Kh${B2&|c> zGR~|F3#$kf%BH@{MPhLtL}I&>bVd^n$!vXcd) z@=o+jWGp4yzYrA#{Fbnn&*YJ!BXx%lu(JwKv2GB{%+qgSD9v5moenFmM zH@+0ehT%FdDfw{k;>Y+j=DuH{DK{z!mQiHzpAC@n@{QBz&_nx%4KnLR0@>x}uTU~i z*Of1$hrQUd@FjdoU7H7;w#Ync*`%h7%KcTQ4;Z@ND;KFqH`yomrRwdcLi+$I;jf!= zT_MspWKPl)@OKatYReuAX;-FX(O508wT2Rm462D2U>c)lbS2U z+6McVEdkKPSICs3tOpH2H&6yUtC+Ptf6!oy}XspKL7*|T9iaN>JI%mwTBneO_M1oKi0mBg4rue0D)|QzQKM- z(Amk5ospY}@qU@8GUG(G-A1st+K*O|66gOv~Y;RLYOn5$c`fv z;GY}wZ~x!WOqv}2w=aRJ^L@dQ{cxkYr4Dh@5jAM|rhh@o4=vSm$@T|q+>>N*nltqt z4KOdo^!)jO;s3C(F8&osEqPShAu}yQodW^fmm-0RgqBAqC%`60OiCx!ytua4EE1mn zbg{873n8vdW_Sx2mAW!+wVG6hX!v0mFi>Vu8oK|&j=1?r6b~h!1SDCSK!hXb${g@k z1w1A#OD1_B+yp5~gmDk5>E5rWY`Lj7qu+#2ll$3OC~0MK0U$&&O@W7LH^ON#N4k<% z=bv}oovkubcz++)ef!mZadjojN}jYeLN}^jtjw4r?|<*8^4l+wv4s_#kQ>22Sv+tS zs{9Cee@YdbZ|0S#*!x?rrf#$;Zo%ah^~acj=l$HX-KC}63*yQKo1IiJOYP9ZvSLg5 zumD@g4{vzc!D3s0IT`+E>3kj%B{k-1{`SpGSO&_+hDoo{K6!o^fa*T8chlT@`vt>! zfHDR|3*E5VTJ%izGaaH>+eV zRvjr8*=u*E45U@F)xCAWZLrH{I!-+V7`Ne5F22IGI4C&{Gi=47w1d=ORXLJT^0(EV2aXZyPydIXB(zCIh|E1QV4TbT??2(n$ zj`uM3$h!P$Re2X4q`22)V~=lEZKKIBk`H1 zp0xq7&O7Vu^Bh&W0T{{|vcBJm_iWn#N=p~Y91hJ|l5m2vN3q*Y0>Loo#2GFdXA)v6 zU|s8c;SyA!<8UsJe|-3PETW{)HV9>NO8HB*IzdU!~ip zYMD>}4#e%?m{WJ7B0h55tm4Nutu>#Y^v7i!#dq+YQ0Q-*DF?+A>yZ7R7^@YIB z6dYKg{ft%DoBd|po#w0hww*FIfZ`=`955n}l-r3Ldo$<;ckjF2gvjdvnkRgy9=y3l=GbXp(>|g#~5g9xCg-{9MyN6kj4$$DsN$iRvIrt9RO*1%oHd{Zv|Vw1Dls;1>n45 z%Mq3MY;6exgXr838pF@e-889{$dq@0kGo%&VwarrH_A{<@wO}W<~p0XjUAfIa8!*H zOHDT)QvS#0G)El8xu*1fm9D?z#WuzmaVw=>+3-Vjp-DkV4Pdmy_L3FPr12^hec7u+ z!U^Oh9>EQJR{>H30E`Eva;^ZJAFTW#!1C%7lb^MH366wq6hWA^M-AfW(meR2vI8+n zlBcK6fFB}A%5}ChN7%PUcVM0Hjmk$## zV3RK{``4`jq0lsAQVt?aHAmx*hRlw7yBSJ1ppVJ(kz36<#R-1V>h~&@yz1%j#@(6q z#19n?TPR7C`8Cc@#?yirpJ(?cor2`Xc~XfmAkSd?8MjP~Y$GR2Ay|k!##wg1lB$gx z)(rE0yq};@QuhI>zbz&3Ds3eIUtC6Seq|$BqD%!Of&*|E6F9^vYM$Jh7K6~8Yotqj!58X;}8{^uqzzzCg$(LKN z{WIyhu1_FBG{|y*^&2IE@uxs54;!V+^DrF;7Dj3Kvump9b^q~sVz-IIweD}ZWwwm# z3$?umA&FCO=RdY$S^$tcFVi$JuU?}7D>ogxm5E^rkqJog#G?yDQL&b+b*NWmz6DBJ z=OT<-C|T!PGR>v~a6J}|#}EST5(=~R-qi)?KOd@HYye;-0)y!p>+%3DM0JRXFdOGL7@i8zGn|Ql*CQQ$ZeKNq9w1&xbpWp~giYuiG*$!`*GuT@+oZ`EcobA&iAxdv-)U`k48DaVw zo{8*7yS27W-UxiXOwEJUGndYB2g~YzMCr)hp*Uz`T*s*}0+Xrc0SbUfWN05L!$>}6 zA-AP$_8pB)Y%qKa_fDdT%ioB+dc7-=eAo59a_qN1H;D(v@7&$rL(SUpC*MS7ky;uz zU&lcH#X8Z=ag`4e@9ACInkkL}NlcH&i5i#7@?F)#?r_jgH2H{5Jriyp+bekWn~vNN zD5cZOk@XFZ-sTX`MB9Sx#6T1!#axW4min}4_j<)VNUW_p6Tk!G(s3roubKzdk$$*1icdKYDNte3UyQH?H+}@;}JQ3^Z`ld&6P3 zF&8S};kG&%*wJHwwL?|qdg6`}lfQT- zfTt4N5;b>H$7ALTJX3~JdydE^A)+&f-E*bef~!Z>=!t?s6MG{&y*H|-MLNox!C(I2PCmH z*PC5dS(qzTdQ-{OjlGp1a43{U0aeDH?A zI3bjAi5&>B1TJ6$w^@%RQg|n%0|Ca9$Yyt~!nW;iu3kE$yG_Cx;)4O;)PEE$hF{S? zJQbQI6mw>yM#EY6n<$}hKRmDL!B(lB`Ten0gSDf42fjG1|g z>e9+g*$v8nPj`C(0NYsWHJErafgs{mJFNLx!2s)-ygw*n%onj~s=b!=d4PvIBSG}Z z2Mj(YPS}B#KWXT4!E{~_x3}}Nk>V>Gjd3n6H5zUU0LiW(aQMnYYqR^03E2VN(krA# z7+`?8=*#>a<4##{L4-3R z%K{65fw+kgLb{MZU;-rq+Wx&YoOKb)6qd#ypFjq4+;Nudeq>dXuCIi3vHM(D6fY?PMBltnfTnwV{+UVVsWg2t?O+w%~827Zhni666p zjIiGFD5x<3g1-fs|KKWPW3dSaAp&zlLAhAT$8Z7ewZu9n#^s~St1_-ODvV&sJ+LCU zN3pvYSeumFO2!3}w*GkNeEGl`5s-Y3qH%$4QBImMOCa(ch!=noaAg#7J#3ma64c`e zBd;gc5LP@$ITQa71_TBWsv-zvCP=fyFlmTDog*ZH6yxs@f4O6P00i?mID-i<7m9r? zBhqhs(gUqKzoHGklIrU$#b>ugO91xV7UOR&?J{L!HsB6)NJu+z(uU~L%UzDDQjPjy zGVg`_h$!UxM=mB+(ycroN4D9=ZT2N~BCs8yI1b~cF;)EQ9pHMc0y;=BbL_ki*mQ+_ ztX_ysv!6~6C1m4D04~Chuq5mCRg|Mm1aR{_l2#`0@EK`i$U#bQwuvgPGb`ziB_cr~ zTlRyZ(s!47yuot^X2o}9!9)-cG9`R5G>D$(ePIA_d6!pKiO6kVzJ6t# zVDGP2HzSds?tQ^sIAaIkD`LfU>~%aLW(0T|a3j~)FLn^k&`5b#8}FxmZ-jTeX;pig zfSDGeI6QQE}=%DU#BjN1J>p$+n z3%J+yDQ9T6mJ$gQzD(q2#;zHGu~yRF4twI98MT?FO0E<|i-8GwBnLpIF<|ZtsHG?c z$Ac6`8ok;?t$WihGmG9vWU_>aPy)=73)v`jM-jjiFpKDB+3aT!8Fhsc(BH-x>B1%v zdsdYCF$6J=o7ZkFKCFd zj$RGQ`j7g8Np=+kooq&rC&OR-02?rq7dtr%DTP}*ct7Sc+;5iwWpnaHMp(}w0+SZb zwkN5uyEg=WeL%}>Mec#w!4VI5v~ay}?BG&i^q&7FuIZzN0PZWVLV8y0Gmogt_20jj zW=4XH%%~-vSO#LIq!%#M#@MNEVgPqOI0YILLy{GmxJq4+(7-~5K=KTNbe-us0B9Z} z^;nq8fTdp5x^=hwe zu5M?9;W`%3f++%22GE#5>o6c=0{xqw&e<%3CyE#S)!)vk-2qa%Fx)9CaW6;t_zO zcxa9IFE07z)eI9J`f(swpPPNR>hNR5n~qAf@PZ4t*&AVCogRi*%)zqb2Y<&>Ukk_J z(k}2ag7`0?ge6TvwdS#h9nm;wCwy-Ma~7%k?ViF#q8KKu#&ZXfR*eILIwJioj)Ub7 zYpHI!Fu^)%8rXI!c1uXbXgR|ZsWX0z{?-CytfKe^S5h*dBm5CTmzd3iNu_?c!??$n z_)$Vb5N&nTHLY8)Y=Nopd*r=yCbQcnWdY+FDeC||Ap_Oyi!#^}teej?qgAstI@?2^UC%b~@9Ux3);w}^)f z*jDXhrHi`)5__NgLCqAsZJ8Z7mZsjJUW>3EjT5w=%}nfIIMoLzoMFkww@BVlmliT{ zuZ8NPfY=73zhHWBe}+@x-KQ#~q>1hwVjyv`S7}p0ANxGY;P<85rl4?yh?rBS7GVN4J)?mub28oDv?_glj#cs4HSReHYp4r-Q&H zH`ooYGVe&Sci})%jc>Ik%&&T$c?Sq{3oZ!lR1KZ?%O(r#UHAahl$gybjZ#m#tgBT5 zl7CyZUpjX7jb?ZOO+K@bhyr~`m<8|lS&km{6g;z$XTI5c+2>_*FQV4O5v~>Pc_KMb zwvYNW%KFtMGxKLq743g%mVJ1rlHs(ltr6uDnf0{7@3bu=aJjzp_c(#TZF-8(Vk+xx zWrsr}*r)XZCvkxpL+M|1z-oyYBAWUIwrBNYAjV5eS7vLq;_@W+3cAcxYw@3pH&Kh& z6V7D<+3DZD)xLDnHun8VGG>mdvHv?>7j-sxCu^hHOln=t@jt!!I~SLvuL(ZNsRN8z zS@a8CGdyXIARVrM++Se6)W7#46ZO#RdH0cxn)g}{d9p^#O=I4)Hu#gAHKx5jY>2eu+@xvfz z;33}V?0H$IXsRZd*Jn2zi9(p-%p1p8{CF5|= zzC83|{MdQrlSB&;NVLUq2&?vSnGfUM`pIwPYU)fhBxM_ngx3|K2j=P&MNF)Q^82Ak zwF=dtS}H0A9obatb>@9j^gKE^R2!zCz|0vItTSP9NRRpU*Q$$KQ8#a)fCHv*+m)pby(1Xl;{44w z{*B;QuWBu8THi=kAFF>V_2Ac;U_I|5tJ>V#82~ef_^@N3YS+zy^|^;)d(sG%;3LK> zY3jVv8tPV4pL;LesAHPO=pzo%zTo{r^`ge$k*B^BbQm3^5ob0<)#4jFHig^M5#Nmwz zQ0H`^EXjU?UG<$dfUocOOBg#d4oSJvxB5!_7Os4dZ3>p%Yxow-Jc1!g6<2@L?E_A4 zOeH$)O~2rnXAhTwb=tc|Giq$vm~xc23}CtCWz3rcGcQC$GNY|k;yO?>p2F4IM zfTAuPKy)v7g?nZjRAJ#;D^a?5siXxcfuv+^1Gr37F13QLx(zEx2#QZ67(S8$F&|uOZ}m>t?XxnWy*;M^rpW-sf+}_un3ZFhZI_K$6*B&i9OlGF z=??zh=*w$bxsq|YDt14M-S(2?_b-&sYlKkLIZBv$`kP}$a;z8dzos-`T%q+ zp(=mtDx{N4brsvVDRH(X#PjA!tp^gC-B2S3nST}%r^{=wN16srh%^2QC4p9i8NsrE^BuP=6{&mDzds9- zbL&b{sM76Es&~lFy2>0YF>7b!Hvl)sl#cfVUBj!7+4{`uYUg zIsxAiPD9x)varEih{8G(=_ra6jm2hJ->AW!T6w*oa*FE(v*A$a`K{)q=r^>!}IAO(bV@yn3w2r| zVL|J5{$Y>zrriP9!{gS{B6CZZE6ZNj`}`K&)rNOtega%HNLGGg`G@;xp)1J<&1+5=F=;jCAK>G4Xr@8E_h zG+cCSQu*E*T#!RA!63k|>H>)z#`}HtH6@2r)D5FFfy8WYL6mqx*fu#ErFo?bV~K)} zb6i`0HSjHeppkU^$%u+rO34RiKtkDF$y*>^*7RL2+>*y%dghn0enlB>A|mULx%?sd zZhiln;7GCea(By(50tN>nI-kU#>4m3W&0Y-lFQDMTyC3m$`qeMnA)nX?Z%u&?Y*3m->h z(@!T>kRBT0@pc(xBmb!hxDPcxY;U?HCmn&zIVPW`kAO$gGcUzjE6gC}Iy3d%4!$6s zNEAdx0>{9}oSfWTNJ^5<6xfy*t(BR1d#`*v_jE{#B)ShC*pl=yC$(lrYV~H`l3eCL zIo63v2|2Q@Q`<3iXJ7&s+BBH$iQGDK8+m5w<%xaN)5YO=wP3_9fE`|u-tLgUgvk6p zfVkMA|D-}I&<5|xf&G|NJ5VnnMUR0Rk+x|ej5!d#cEQ_l_8w%WEdz0SNjime0dtIM8jeNj14y2RF}Z+=ZqwQNxRAl6=%egQg|a3f^{HLBjP#CEQ;K z-^XD?Q;H<6rdN|8KCy^5!P^h1@BS6wI}etQNQn8F#R⁢$1>Q1%>! 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

    ?>f^vyGwS`3iY#E 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

    +2}o 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

    bpBl?=E)e!w9pb- zU8EOcl#V@w02L#HreKaum-Ed*Oznbr5C9cjhFUOKX7Mw-Zf5W_y2o-SQWt7EXw$whX4tn4ke{&)l$1MB>zHUz zbp`{~VlRa_PSMiOQX9PHMR2Eeu~J5ipVnx4j@GksmM)1PgY{xe)oz-onuxU~+pg$< zJBe&w{}Y*kWiw@x6cpay2$1sp3|MeyKb_PEaa+|bW-yY=V4LFiJS@EZ)?J6I2RK^Z z^z0-jk9Ta14k;I`h-gNEBy?gtuGBD6LY6>ad4As54f&v|#1B1wB|^vO^d@NJ5zH%%8~YlV zls@(J4~?!t6~&kSeks)k&yjG)c=qF4=zS}>${1kR!@r7gb%b}_OcGo7Cp)|YJXv{g z&QE}>*Q%6m!uKZ)_Cnt(PYa#(aym$dn9Dt8hMN&jL&Jyw)1MK>1lwaB$2G2ab}wTo zetyklWo!Y6k+WjZfAt6hqN||g?=)GipM{AwGSQ<} z&Ne9BlT@EzlZ1=q1unC&SF_W$9)53h<%rD||86dRmohoO{}6F-lVeB9+>*87ofm^f z*LIRmBX@!l6sZ(ZO>r0#9@O)ga(ctI`~2MHgXMRo511oBVVK%8?OI#6(>cw%%iuby z;Qy&;sPMTRaM*8eJ=(gJ)u9W4_+cT;77(3df&YDIIsJ;cI=E@#W!ENxZDVb$I%LjM>I$CY&v7nM=|HTpbs&@6a5maT0~WW0jM8Cga>uA6T)6&bm|LnRc%f| z3X@10kzH?d88=>jjdP&Kbjv|9^o&H>=%nzm|H7f42k^Cree6BGdu~HiH8p}!6p_hZ zkKg}G3=6I!-#Ve_JOVQVR|q@L2mLPlq&WiQ(M(?HYlE3u_K*rk-izS$0GHVZqnq(2 zApR56@TlOqT^+M2R~uf+Wp8-Iv7kIzO!x0ES5z~2dvou{A8cL=T0f7u6s!d>6CoL! z%gfX&>{qDX?Ln(ePhx6#C`Y(j)6b z8-IUoY`h@l!BwE`C(A+bAQ4smGTr~Xp=8CGo1?;QybC1^i=0g_uFe38iz3u8b;|9{ zz3vIm(Os`BVnB21LV_p=A-X;_7?>FHzn@bDY5lDlZ0$dfgD5$kCgX{r{f5FA&edI? zreGjv_La5C zz_urmQSq`m$wcv)$lh3DHBE&m%kO02p3Xd95gRR%bfI9qLyaB2$3s4QmkR%?S@4!Nxc=0^z%S~R`)YR z(#Gqg%~C^*(^*PIqUC-hI~j}n8&21v^wV<mG(-C_!Tg5HixNU1^h)~m!pEOpxc)55nsAz*#q`)RkZBA*J3g-GQ ztM(_YJx`*}7(~(3`T-DyDMt`%x&?byJWh8kJJ=AJf21E%@1v6_Sv9Gtek$u&8c_%q zR)L0T%CYF=xHk2aAdDb+T{JkF$n}X#nHt!Tymz3oy zn}z$ENf7TZsS!LaX8`A&&(8ZTpsSgtnH+fqF`7@cZ|!r9U%CoUtesq{b1~zFHF0!W z?$@MV8tiimGI1OA8k#k`248X=wRkw$`tXg#qxsfHr_3IIZ*^T8=O zcs?klYbnSx!N#IzgldpdW~e%H+A8+Uc}$E-xUx@UI9oj%N{-OcU zKU6io|5HI7J@B5f8YQ4SL@?dtA;^~hWTq>Bwn9pm!3>Qc*@145BvGmaNR_Z zQamaUeWhCM%4EizPonSL`O-%fBhjY;3Eg{Y9s)4lAAo99%;z>5a;pkxO5Mz(u~#D_ zKAz{R-%ra`N2D6W`&~DJCW8R5jrU?2R38u4q31VSPWTe~5nH|K+WwnKOc;?ir*mHd zYg8ZAS<+5F+Z5>zV&~Io#3FWdlR?yUJ{r%$a+wZAeUu6>djZ>d+3SI1`RbdNVfm{c zOP23%N<12{sPuvSM6%KM_}4XMYa84vi{Vdg4}97`nOT2gnAWiO>#@R-`;?nQw|H31PF7KO zKyv3>-(LOTp!p4qLDMMLAk-grRD@eDf=!N6);- zY@s^RAr9+(?$do9iG5CsUTC=`CfXXvYW+~RYZm=S50$pv37v`7>y;LO;$kb9Rhyd17V81&z!oTdJRRoen{3Jv{51(mI zST>Y@SFkXhms3o8T_Q%_h=|O@Dy&hL#``={Wu6ELh2BT`!^s8#eiIOYX$-`hk=4Cj zdo%lcGi~Hr9xU~F{NZdGz=cYgVvi5ViYW--tZ%@o_9&-8`?tAp=Bd@QwgRZU7l+&MV-E(s`-$uaz z#Ivl<6n;={2#7THE(Yg;O@LShsgLk;^hIx3pa&rlG#UM@=*z)h7*G#`Q(~X%D97a_gwoA%toF*1JDU*Ildo9IH1vO{dtd zCpj*E#KjX`E=_&^bK#!6Yrm{>P}#$&67uoMsn*O1v%|Y@=rh`b*H{ETZk4*HS~R!X zNmWTK_u@4<34l?sA*vyK^Pf&!rky>Ak|oit=A>PrCWd=? z`~gw^cm-SchkqcC?se`?Ire4A>gw^|=vx-NbA#nB-woG5I+zWX20TW1=YHV5c%>qx z%@zhVCzJ-4Pk65NYpT>JN~!4dAJs8ieM=jH{lxQgBgtyeMdJEd_;^r^uDoHRUh6nX zh0TW5w!avl#z=@OEP|jq=QE8^EkZv9sYG&)&tKbA#IZaHDG!Hu&^dPAzjfC+jsmtv zmc&Wrj&T;-e7URi_igi{KF21CHS@dE8ppUv_Sw9gvn0-cKqs$cdbV}8zFD>W+}2}v zsJqntq3z9{^LNg25)Bd%i12>E-7~q3rqT-pO5UrN!-7gCl&8fiM%*!z+J9d8^q%zg zgKlE_n`1kfk#29>tdzD{vWV*;ugtF)x}B6=d@JKN#*B|SY@T~PmyPThvAG*28K+B< z(&bAO`kx3#pC~|MdT1)CG!1UBu5ivIvd=~8^+`rz{mn7wikzUX?N>OD!#S3JPLBPr zj{-1F@pnf{Av#HYUtUQZS0u8-^2|5eLm<(m_jv?42dejX8FMr3zMd)1t~~k^*XV93 zA?xcu`;rrKW?(da5O~&lN6XV0Ht#&E!fpQPvC}wUGw?@vU)c*co*}s`k3vvh3dxtJ&pMbp+9<|yInfyaIn6v`ifCaSN0*8XtFCUcu)p7 z?tXI%v$Qr)ZhW2Co3#=-l{U#^-r%H(%#-Y{rJ>QC2dRJ zTuq0L+Y@6K?9=9k+iE$6iUj+_?oOLG@PG;z#xVbqk0PF&+-tEp%XEC=!jCTtyLn8s z^Z!uAbgKSe#M7j*nsvdRe6>-|eX(4M$#pqXfb4zxz5Hmpel)^U&hKdVh=E$lN*Vbifz6RbIXHUqxRJtpRHQZ2Ikv`v=6WTY^Xje z4c%8`_!R`y{BQ7ukM{pYe)?QE@l<2s{2_ooq;q)szFhU8r_GHuX)1OKUC-hm(wlQD zZs?jx zpPH+gh4zJq?lh6HY4A-Kb8_hnW<=7~#4`RbffrJJj5D@f5Y1yNQe8s1gT*pEE2*@^ z-Mi0OKqbMk<+qiJ9TBqzIL@OMS=R$3dI?)h{Jlw6bj0^D)S5+h!6vKsmrIGSiyHlo z&$IGsvgdyK>Yi9^_pM=m*Z{4M+?#tcqCLrEqK;%Jv&z7t!{6I==;PCOq z>U|8C&zH-;zI4gWxaq#-U!2b4_fb1_PJBEZeBtPF-OwABwB4@uY~ia3?brYPx!@%I ztFp=IHP?7o_w$GYRYwK?Y~22KFXU5tW^wgLCI_K@YTionB@>|wSC3w#mAEt9y#Idt zM{gKyHObUp!rj&JyZq};1F&)Jz~Oj)`-6Gy09iu}vuD(&kAV%RK|3I!r)l4PFl(j; z%CM?e{~^GZN`It^+b8nXmE+_#Db%rdf|x`^T#kcKoCFc?@Z5j+AJ>zyieE-z_$!-@ zv(op6Tz@b4;4|cWXF30v@sP>A2jhpqxd>A!zVw7dOx zspkx#r~2P&FRx#48$q~P-S#SEWXfqi%o=#!1}XjoT+(2)r2Fdp-%j{?Mx#;#honEHgD`%;-627+Ub*w_s$2bivFARxP&qx-TA zs<$R|Bl9mS##6b5q8T)YULA2eCpc7-tC+K=-%?u{&UORZP|b1xMYebt8?VfHSh_Xy zy|%FV;r6#0do{}>Fw?eJG*q_jJ{QGz=$^5JIP=2BD-nu4ec0PoIkpVPPPlV` zI((Ij0~6K$nS$91L9($q1Q=z&0Y>QL(WN0wF23K^JRaeXsI&SalmoynB^hgP38~TL z$(_GT4yG;>;xh7%IJLa?Xhj0=>V1zr_>Dfc7NH$F9_KvxW#UkG-5uSDYc8VDjeozC zsOxMT-t6@ORgy7w>9Ul9qb$x89c+UT{!RB`J^-PQrb2Y&OMG2Aca&sMR3%GOxu)`? z=rsCU-Qp*5){+QxmQ?*88Pc+hE_6*e9mS=bSa&g+_zQI*h152R&+4bY!WE?v{ zYdHCz--`W8{BenLTQAgENTOYUG4;{d3wbJGn0k9|vi1p#SdH736}_O$byOZUh*)zo1X^HuKfXk~zyjQs;I8sYhMeTd}!A1DFS(if**7 z1jK@88H^W&xTOL4GGI-AUARluNsTZEt&Z66O{&FJ+EJ%}A=-mHHt@5UGARVTqp>ut zTcY;wy6jFEi@KNChg)cawcq@bcYw}OZ)nSLY}xrS?GLH1WJLJPEQleYUMTG zGS`88VcwCbtT+ycH)2!M65kTuZfZ9lzFOsBpfzi3C3iQC_BrVlTfJ-QfbI`2n!ZAd zaxCP|5jyk^?2N-e4o(_c&0Kw->1i34)~7w6F6U&Ct&rtMm~EMjNKT^RXfs-3wD35!O{CoM0iatpL);>u}dqgV2MB*vLFL*l1eNxF!Ls{wSMav z79SMCIrK!I#KXS{;qOxE$M@lLZHj6Y5-j@gcgb+zNNs`JvVlucqPHx05J3(EW1pqH z_J{#VD=xiJd$&GR+HP6OOu3}nl|JZvuRT4vx=&-Gglye3qi~qMXT2eP(2>6X4}jD9 z)_APh4qcO)T)}xeysO+?aA7qgX2;Y^hVNaakmAhlI^CjLANwxx@5IY*cB{YfIh!?& z-m)?~unsSBB&%uWUf)=MH#JDnguGgOJY5aaF<71(%uQDjcJr%3q zA8D+yG`mReL^Cfc9!~%HOyF-JchQD}Xj6ypi&gD2qwEnB~I2gL^rj$^0vyhC**Maw_U|^l6J@srue^*p5$%ghG&> z9vH!t`wR)45}{=wBSwXfcY)n9rLan&Vn1+w1m^WU_sV*Sq8kF>&!W;(OylYDCYReb zK^6KKT_tPFin} z{715S4FW2m4I8`qnuPIN$PIvotVoIpk7@}oS=xL{ZE3CcAWeIU)I=%J9_1A-B5d-^ zoZgVMXF&}WbN-7nJ z#C=W;^-)AV2tQ7;l|t}R5-H4B)T)?SpW2)Ec<^nia#Q%cznN#)%_iJIVR$E>EiVIy zjZhW(g%}2Dl8XWY(ugHjQd}4ld&ej4iHsEEP~YCFBRo-3;Q~>QU55kUu?n9=*bPnb z^CijQT|;rpIXWHv+UYHK=RtR(ea&wsYKdxNBRKh11tDxp2bYG!-^Dshv3^${#O3-i zVmO&ob3DHVpI73+QI?o5e>ii=<_FYC$1d>Jnqxrt?*693nFTt+xq_+TVPU^xZNHAN zKTcZf=RbIxVx*%sc4SmpJAMW!Z?8B!;=3|p+*D_*g8-J{fE%)k=!X@e54y0NqY7PX zLtux%F1>bMR<$`q38$I?)?AqKvwop9e#iY znTG{d;BQ`O^9-e>P+v(6V;|yqwZ_`ljf=&`NIeX*j@kkM_EPXhHC4d|h&=OqwG~dlQR(U@5zX8T~gWcQq z?e>s!EJ@T6Ts@o@op-Fvw>0dlqvp2ar^AI^O-@C|#|$P5OBS5Ca{2O; zrEcuHa;mR7AS^g` zv-?70Crc*`YrQcOcTcSSXR{EVt8))@sF~)YI{Hn86Q?k;`@eU)W@={uc-${fUCfg1 zEKAwf;>hvF_}qPa9JVE-{@6m7I}#0#-!tUo(GkD91NIsT$cyj4`LwLCmY`3|R*MUJ zwl8Tw7^64@c4s@6zr$G3oWEUSl~q2p@=QT+!%$b0pv5=Jn6yU_cz=oE$l%#5qPuLN zL`QkFQQ>lNvUk5x>~Ys|9Zbxba18@N|0}R>lWL$q4!mbC=Ya}*{ytD??mDLe$;wHvYX@vriR*2 z#oIh(y{&knvn_O5hkPVD*DinkNHHaW=f}G!moM0}L#CX8{WrL2jL6LDD5%4|L zdB)h4O+y*Lh$1jws)!KWlnA3Sl&uUJTD}$+_C5tJVV?+5oFtmU#mc9ymbw{lq_Q$V z%%SN0Q;UjrF-osHX*h=lU6n_#GlSHRk=_iNV|QLtd(w`@zWsMzX+0)HYgv-N+JBVE z*;2AD_#fmjVt^uuocB}d*(fN~-~}f96czZ&T*=^69k3ThZpXcEn`+|F-A*1x&c;W` z#oyj4y3qDcqK=v03Opo^-4sC9@cvT2qAhza4*s@rR*ly zR}p1^hkI(kJyDT|*|&0h9@VkWt=un@#Df?dPQ2f*0k!LvZ{;zwl(#Pi=d`0oWjOa= zzr@o`54G>nWY6?}p9`9Pr~xovmOWGx&nOLw@jo6AI%&FXiAQfm9OEBr&gpZgz&D?- zQhVCmEo7}$h-**8;sesulWTEPz&=I$^C;W&=Gkl4_P0K9_J}6|_>T5ZecNq#38-2A zy7creYKO!)e8Z?7KG-Pth`LEp%*}YFF9EonOw}VZ_&NUntY=Gaj@2CTgcvaa%CM)k zO8O)N^%PJCgwNJ4nVbQkum7TXJT+Y(Gc}8){B0bs1fT9=D%t1hpg)j1#oB(SxqlzO z{GRfwN!&92`9+tx-QG}bWz2BcBh|qf-Q0(E@tQV+HveR?)W=!UhL1eHW^k3uo%5PU zD!X~#xz4>Lb)7AWXIZNN^_kgYd?jg;*%cjff^<6)J1%)Z-|W?spg+%49CW^F%~kSJx` ze}=mJrP}~wcUiJ7z|j5vPDV-AP4peCm===dfwq`t#C*M2eEV!Sk8`Qeaa;50s!_}D zPP-OvpIEr_Ay&IZte#tIA{VDou=+^%oH3^mSI4~h-RFcg`@)BX^QgGuEeRr027n=Y z$TLZ$!m=5chsswKrvY>&5xKN(Fn&p{Oe%5xfJ(d?=@(Y&tfv%0(_A}hEG5bxo>fe2 zb8j4wpZkRVVXR&jxNl0@+F-Hv3)pcFQ1bOme9ly?lwK-*$~)R`&i>fW+0OQUy1A}@v z8l1`P9=M~PAo*)sT6|LAsI@@a@Jg(1rw(N08Jx-6gXKDc*fhlC^ZrYvWEWpK?qh3S z;gOFZsQ!347~a;KcS80qSh|-+mlIK4A}paA)5aXaf)3PN7~at4uziSn`v@dF z0-(*agc56^13%J#>}YH!+=Ow8K$ zQD$l$-8S#!e7qv^(w)-_@5?lklCyc2yz-J0%}3r&k2h|h5t2wRj;s= zyKKzS(kHewoqrHuNlZKd1J=LnGfzSv@s_Zs;;~ti#c9Mteg4VHf@8-=?KVH_F zPu}+PBlP2)$3Gnoj8*ZU)|_3aKYJo2;AG}C2H1GRJm}d45HN?+PT~+FWe>>F7sz}C z_A64%91+^L6d@8J5!0D>=y6um1p&^{G1dM@ z+{!Djz+X5{Fr!IH)2)w?EgZ^aHNHPYZ*PWEiTQb#^=PBBI7ftjW>oX zzg9jt-7S0QWyRNwF2$hy($S=qSBVdQxD%ZC*fUsnLwWFcJ6ba9Yr6&EAd!LDYz4p-=C&1PZH}AUDCp*wp@lk*4uGMaeYSn>+)LcCw?ScD z?lUG#d3|ZUzYRt_*%ukyQ>XIjzI?J?zGI48mwAX?9Ed1QSwG3p_lzE{&LbZBIhX#Q z?6%flUw^h;w%94R_0;#7&KoD6@B5pO_?g>PyWzHGz_q`BzHSJ*>iq{czv0|+g2DL! z(?RR!34ca!ugh&+6Z&S8+i{MVaCtpwcl@QNmcg`S9%*EMjpYmiNCw!cst0&Q5`Zk* z4F=X%U*JGcA^-we|7$5$M$a~3fB_p`CN0S>7oQHSb~)2-fLtRE1hnKzpmH7*@sT5} zZ+i-2*UB-#DL@z;iKvZ`dv$#;vc5}wVtQFEgd6>ZP;LK&8Q z9yqljYs8Ss(P4y59e$-Kvk7O%; zFZA9`%MG4#$w<2YXSd!(zP6i*;%?JEgR3)mmu{_-e|tN%&wIr7D*GnI_Ug%i-L$SI z4On$sH4`BSGd8h(!1~3&_W`@_S>K=0?g1-L$>T}4vGh*^)`Kp8vYMY-B~hT=TFJ4; ziO>DwJT?aJ*1=k~MRsK|xrC`hdU8t3Hyyc7)Z~(c2U8QyC zLV#2P_YlUs9rMD2b^~V;RKk(@0)@!Z0SaVqVS{rVu=7ji&6V=VizWG2a@K!;JQu=E z*S}2Ux{&HrxM>=9`^!?45B8Aw`j6egt@;geUmZSmZMcQhZwizD^X=I^^DWxvncS`~ zp?>HcKZ7bb9WWIfAco2F&$DypuD-vPvsD{M1>=bV%G#VGj{2Yc@CFVHsmpr2h8*7j z`aSs^j1%q$DdeTv43Mxa0Yqb4yUY=i4&e>h*(zx=v+;Apu}ojSMwdk2=)dcvi!HAl z`AP=ElvMKZJy;z@?aKqLWl+S#&`j$0doS&PeO0|YIcsrYJBUJ1vq{CDFB05|l)}aM zZEAN}Xxer}jjG*_nXE+-rSe)}pY1SMq!4B7FRF?!%b@5_J?SS+BnnR4#bH{T~3w>>J2nq=8 zK>=UM0+iz*Zsy)VX2n1`Sbmx9cO$l-R?1ooK=ub5i2~oOV8ahEQq1!~c?)2bdwHON zaGbZ5x(_1k%L#?_8=IsCltKf5Tm64Vb8&FqHvaeEcmA`b9(;k&ml@eF#XBzem#H-FOT=`43u zp2?`%kQ6wuB9=QAIH)BN;2hYIBpPiuE$O;5&{1IK4XzFj4uQn?a;+*||M#^g0Xs++0lJF)FZJV=Ay8<5$Vj2BcLHVGlGv#YXtnc95++f*MQw`E zH+Jb5YpWM;jVGPOy3pkzQp>vGZd%?7?#`WTUSg*^sL$l0&Rg^&!F_c5BJp07X}ra< zO{im5k2qs#()w)3R~Ds707K*pp?s=Rk}n|znw&MT>@=;ISN?6A^vZKfNr{>d(y3@p z-U2GJ-FZ)d9;r#8#1^^BT^BN^0jx@7fv#;Tc6FZs4K!r8<|s|32A|7JD6dZQ~CNTzu>jk2sq~ z&D}c;mOCNIJnq~V(Yd0O`2;JJDV5|7)|`A0`a9*6f_4a>94gV&swq_`D;FX4n7h)6Hj&7H5)AlnU*-`Ck(uF5T)8kk_ zGJ+c?_9q;xB}D}E7_u7CkqAg~=vjWNa;VI9U<${x8rQ-?aXYlv}9)3*ql06BPK!G2aNDWG>G=D1C zoJEGRf*Zs(pp>nQvYb=Hm_z`gR?bNo04$Un zM1Qnz`wbwsV)whe$m?u|Uo(KJwR!V#->fzzawabW6SN`F4n%wSpW!Dzju!oA^5SJ^ z%{%#$yj#_+O;e}x`&$lduX&`6wrPVcX(@Aq#eaQ zF{F)ci9JJx^6cC(7f}*B*QC%yrjmLc<{($d% zsG!?*6BPJ+8|Nj0NjaXMdSwQb76N~%LLOjZ`h;gXbTM9) zhdI+b2?Vv%Wqy$X`!Gy!5L*W*pyY;m)F~aL=le&EoWX!>%V#}9m~qH5ckDOxCA$N% zB+MhCMGG?`uhA4kO8G?ttXYi5-(WGUBfUgCUK*n3ucw)4>xo09aNwOl!>;=T^5a;~ z(G%oDBB&zU4NrLdJn{8EbY!4-=u1saf zYNz!*T-FIkUpRh0Kvo2zZ*}d%h(Npq?vEb6yRL2AAlzpHwqJ4{fA27g6kwl54fIcQ z{O!pYa;Qd?#C8a7 z=YEfHEjep;)--UplZqhp2LeCzay95;*b?A zSq^|{(36{lMK2ZXh3P1^q=#z5(m(76)!=+D?Z&VYXMe!`De*t6sc_f0gkvz|Fx(S? zf3ZT_;gf@+>JJEXV{v+RcU3qigXPI8B_}~fOK@L-`@d?OoPIJzUeAeOb~*uBVVQH` zh0LR7{2?Mt4FHu~bV7TpZbt$h6m2H!^!CuWyfxSYttfz^J(3WNvUEq4!S@l*sMh3T z22WAs1NohVADh;Xby^38+5~E;9(f+KBF_(4B?~VsicamzZ4*=oX3o zNhabv5PIrT=1BoAjU45_tP;^JlRGSpe-V_I$}9B`J%K^J49&i#!$Msw_A%0bB7q(x z;806Z+wFA3I3DsC%9}+vdOYdhEGHl$sgy z9?qZ5sLh3kk_9zFR45o^W*iMk3CuZ#TSNeP6XExo>_GM^?ctcGIF3&Pyuvc?Oq@#h z@_I#MN@Ti(Oj@%j>4R$HQm*YPJvVZGh;WQ7FMw08=t<#)c#s+kZ9IRU_|R)+tprRkEjVy^&x&_imF?AaawLN8chVD7`Y{{<70@r4<(q~JGrrg)TwgZl}4GPN@PYN(*4)POOpZT z2FYgxXJx2TPbq|7l4`Yn|YC$=@f z?+nIUG{J)yhzAX~`x_eKj>X(~9eMJanyS8;dzwtzM8p?bR0a;2!iGn}eD_nX>Hwg! z3ZQ>EWtS-Dp04=mDsUy)M(XiK$1p)HPsBz`tDF9uR&EAYTN?Fx!)H%_F#5f$_ma z*stP(Y;{LyE#Cj3>7@_wT}#?*6mUH4HlA7q2+nU&d9aAmp+ndAad$`&H1%v>mGk1* zdXvB@_lL3=AQb1?5>;(e9UE)O*c1TE>%~Q0#|?$1(kFBbQA=R#0=_0c*p znh8P>!J2=hpc3dAIBYO$Sbssz5FW+jqhlpJ|s>; z$L`=VeMNu>?jy=MNXFUxC+J*tUoC$#&rlWkq>bR0c3BmwkxumGL`di zu|M(Uq4wGWi>_w1D{ivHi$^Gr-!`yT!6U&_0L3VH15j(NyJzZ+{lF`x{&Uc6V= z_ijHfTOan|LdRztm8-YCD?(U9Tus)rOwyj9R1NL3BG^JKZr8+F1cd@H8EOSlmv*Il zTntAZqlUNnzsbWqDBo8lfNS{oLHpM=20&~XNow}pj-9%2zk4`XD>8JcJ4m3dI{d_c zYG`N8i|entriN`%M6bi_J1X@B`Bj7;AAq6)SnoH_Hg)yGYH-133o`v-<=D%Gm`@Lm6jkGbmNu8Dl^Nc} zGn;Vr_i-MrxHv(_D%vp}RAp;}B2JM9Lp62MBQEA2(|!~|jMPDWlk+ed@eda#n7ysA zix$p-c1XmkW#+{tO=;h%Gc4gaB|GeeQ_wvsTznAURMp90H^91#tO)V}nv|9`%PtM@Ksm9*ar=-{PNH>Z1`>!vy6Dp6H5g+ z#=y8swe-F!aYAlgf_t7lSQ~Qk#a_L&64jL|bv*&>%0dZ{-joqiGB|}u7Q`bWj^~hF z3LmMwQXWT3)BjAk{}NP<0WuQ6j!IxIwkjbuWu#bSD08AgQ#<$bvs&?+@L;D?gKhrh zE{bQ0d-m?DbOvb4)^I-UL+h4{=1E}tV)|sXPkhSB=hUag3`nts%&U{$b>-rMsA

    mr3UzD2^Ic$EG@)TIYV~@4$610N{uNF6&^_r+`tvn9?`&`{JaOV=-d)xGxb8K*}*vJ3oRTx6B`3Kzk$PQukdKMF7>4kfl$xmWJpUNR2@P#a>-GC|@j(?flB2a@7(bKEP4N|KpfL?3rw_~|nF)bun$pxwe5F&4(iQ+CU9NcLd$y8TPoXT^Bn@LU6k)hmc!njBo#oL{H>-9Hmuex*C| z_=F!*LMsV5C++Y9>R1}EB>dNg)RzE!Tjx(VwtZ}+G!U;ouAa)q(-Xq(H7V<+6PV^! zVeKWro-piRHnXAjovBTaapjfYvns$0Fp+8Xs;A~L!h7n_0As;@QrTD3 zo6R?qp0lm}V5H6bM7pWVUrVyi7{oXglT(DaZ{%tv+nT=(N=!koo^G!-eu(kO+H`i= zl?rHpD7ogJIx=NJi@HW#@|QueJ0kKx%tB81ZHYwXMKFU47zim~ykf2EU~4rwGSBrL zY9MQqXY1EYhoqi~G}F4((Dqj=P^Joyly#^*?{Sch;X2wBV5V+*?Q+M9DwD@G(9t0o z=H8vKGOTcv^_C8N0GS5TR07!S2$|xg&RfCFgIh_kx8Z%A#^V!Q?%yyu3P-<%$s^yO z$a)W(AEV>#)9aOW=%UOSjt{CgXXU*B*Ba4kxciDu#($V63IVN=ZI>&9yrZp90M%Vi>I0@8)~@euYfCD~ASck_FA2L7P zNEI9J8a6`t;CLiMj#XCtiuN`&HP=!{08S=?r7d=aa;Rz7=rExJ(KRMX7%tKTlztMB zcSK{(MX^~#QAid!Pc7xurTNV}K4ZWPT-)rKvRl=g_wSzcYc&NXQ}FB%`7r4|XHx~2 zc*w|^oHU6BppL3%n_g(%v_-ZaN!fQ1N7p+)r2Y*lIme_L1+_TkF`>!jci4+_XI*b+ zLMae4DN^W)&$~>v)phV#F->{s87iKLIa0)pem?E-XsoyP=&BeN?ZlbE(lMa~*vYco zy_Kt6BZsXP)aYDP+5=L_WFiWfVD5G+8ZcdLf3I1|QU>SeB%?a`j_C46(I&~oAeYoBsM9_$Hh=@kmx47Lw>o`f7&6YvXcgmN~Ig;KC zR@E_5OzT8X6ou?~ndN$;*i0k8nhX{yb`wZWSH&cB12ANZ*uh$F3+OuxU4rMq--$kH zXW~VU8X{^IYvXnR=cs?ZF>4><-W8hhcb5s4o2YJ6x21VHa5O6y*!dv4SBEBFmHW13 zF`u|QJw<=vyJfR$>JT�kxsr$m--L);{Yw)JK8Yl0h&;0|5IMV{3*1tB7CTJa{2U zpsB)ZgmEUwIaq?Cs+C-c9bMn5(W)Y=PX5G~XUh0=yfwjqGG|{Neq}RGrL%zblnl2L zSQfzLD#9R0X;Moe=uveLWLVp_-ntw(w(s?O#M&2XGn$=nVaB*eqetUZmzME>8X!{* z3>iMc0Ra>H1H5E`T@FhxE5v^P6(5Fb`3sKzM-#7?JNIx^VwA)%2nz^kGs2`7-~J2& zN(8~POUs(7AJ??2c!0Vq*Y=R7)hUHiZR-jU5VhsMIhnwnXQ!v&y#JWNz<#5i9pEL- zK&N`F4I;uwlY>C;`Zw?4QrV!H+?9p7GznPk_gx~G(*GWv%Z8Xz0Q{CUzkMHy6&qT? zqhq?&+M+{V=0=hE+`>s} z=t(GlE*cw5SkfYMaJ#a&#N9<;rQf!Kb91mDw*D4p4WSNqxQ_Ts*xVf5{PQDx)%5$j z`>q~}py z&3uR{f9Mi>+T`)MICcRT#1rZOB0%sWcS|60DhLgzm1rJ2>-zuLE^ z;wq2=06Gfj_JD11+4I7tXsWE?sceg$JE{#jD8sg=0c?>r{}LJ#5^$AcmogBRWzDY? zB|uxjyode#Yc3oaUH<2Oe!rW1Q2^u`BKZ_8^L)q+9d*RFsI6S4?u@k<#$tcg0ZVT` z&XW0wWO3$v@}2dzl)Z$znPd_mbS$OueNH8lDcprqjyp8?PX&PU$7@OnV6T{q#v(_+ z`11ElO1h(LRJ9VCeFKkf4`DkDu!#q7O~qFH1rWwYgMY8_!D?>ShvfF#kygMeXx24v zV*t(aTRE3f!b?ov24xdQ0iYUKxd5!UnC_XxJVSIoWGMn<5O-Ck$q`|J?$lc4WZ%~Z zL=ej!Qzx!XGvc0^aq|05S9Kg8{6jeLh@F5d0G+Z&u-o?0rOxaGj5TID^{!_h?L@ zSU-IbkYWsED8(S0wA=d-9?4FjxZsyKwgJi66Wo(V>im@q2THOku^&M`uWj0vX$X%X z!h0Pv`yhZxAL5zxC59edML2f}2J+YnU<9r;p&}?9C@E{n1d6x>&=Czj*MzT=Agj+P z9bR-xw%go~=)kJAz;*cy0{?6Pog0huKN+k{@=s^8~&1hE%lOHn?U7Wya$jW&?ML z6VrD&`99rrHE)yH*|x}?sm6alpc>^W@(!1O!nbq|G?EYK&a~tFu%^D!;XfrmfXH)N z46i4PE~9Z98cg^wf%36FPgT9{6QKzBlD^^+M^*`Ih_++D>Rx2%B;mAZKOGBg^aSw9Ik`_u1HZa*? zUPZBojgbKOdA17bD!hfZBi)fZpYmrolNd+__$k6b%-ajTK=t-yL#R9U{c#*A2;qEvL`P#uXYUs)mIc|w>%T2hj+<#}o z5+E1Az2}|6ov_G2;(XQ@ZOw*mUNZ4*WoF#VOrb12DwcqMOX`z_9kg6 zZOulXqPFL9N+<94XSq-w@r?(+IV}6MjeYOCrJLRqiDZV};8gRCR>?Umh<}*TmQ&r= zZWM3`+~fwgx$oXtKfHg1DrQx8+F|Mnf*jZF5@P27fD=K?LgBAArtWXqEM26RcerqI8@_0#hR%DZ5Oo)}&e)8I1Pa}eyNzGi*e=Tr*zgB!5Pwma z0{m9jS#j~Ys4IEteAipG>hOva8vIj{9Z3KXgpfL-#s>smS!9=}>+j<%FMe@!KvOuX zEbdxR+f75gQ}z?)jQSJU>crxz@2bq{UO#*b0AX+gn5nlv!uBq~7=K`HrW)Qn@zdA2 zO`_y&6TDA`h%J#V!BH0o>BFs(2DrRh{oC7|B{bA2lKyiv07^z#GlDt&eW0G7qpD}dZ-IxO2 z-~!Tje1b{H+4v{L>PH~J-lnpa(oV6(|n@b4SIUh36(zXqqd22W)u8x^=qIj@6Ks3}YL}YMvk<&5igGTJ7# z@Kck(#D;lUsYtS#U2q~#mLG#{VE3MgdPisbYXOnRfXuDippl?9Wa?k~rb74E95S$b zt6Vsad^38>kNbSPt^Buy>^KNR3X`;9CVOqpUZ~OMt`^vg`P?RU78C;|MtdH)jFlxW zxQRB2g^JQw(Ri#fYt28!6hC_QMTGE|t}G^Wy?oajxv@XEcT2d%!gSYOS%7Wmu@_YE zv#S;2n+a=4iIl9t9pa*%9B@f}a%^)R;flrWooG`2FJaW5Yt{RNz8fP;247~nNXo)r zALK3h(l_z@H4$~D>7R8j%82VlF6ow(B6k)#J=3IH+Gp6)ak{aC+99KRLs!iz zg<;(4%77izN)2JH%h#;|c;?8Ddmxi%dh!Sm z{_^J1YggXD<=*S?Aq_y4pVIMG#N6rkYU}&s^I9@hUtCjA>f?DUK0AIva{PiE>1(a? zom2XJf{^X+T#Q82LC0yyh*1mb<0MA4;V8Y(lxutb!*JKE4lY%Qn z!rk(nt9=@8A^;n-_-?nuYJvBwnX@V>j+vf5zv~%exjnbsdwBcb zua@4EFO>+F)m}b37{9b$Be}g%`%!~^pgsCSVb+4x0jSUOrjg9??dPMjKeh_Re~A2T zb}0dZ7kh^f<=sHWx6DKWkhc?3u z2CR}X`Tbm5q_&KD(?|KNNOirq34ivi6JH&av*M&4M$o;QA!ljZ2E3}T)}pUb{6G*O z{%(1W!*UBADowRhRJZnp_vod1{DLVcMUZjkb?HAZGuj2Zdcdxr#95nbk;h1YUq-$j zUEyqEp@qibo&m~{Y7ko|;4NMkfU)vnN|bC8F4uThBiQMi>cmruRZc!7@w9faVUpi^ z_+Y0Xqc1$=Bvg}(E3(r2&LH?Fd{FWdxj-mdI?gm0EN=?#l0J!Nd`%_aI8c%GM>Fugz~`=x^L^bEAj#0?|rTUj-Z3K4tUTFC*{X zP=gf|v6o99tuEoOpX?6^ESu^9Fq;_4#s^mgFVsT>KED8*`ih6k`zeVaBABAh71rM0{Ee-CVAWb;w#QDKI6i_hxBbyD{po{`ax z9P%;+t~qLw%%_JD0I8`aB1Jds9E4(>QA?Afj+b`}6*eC^ zSb9jOP_JL)24fHdHV@8wwv9eLzl#Un9)wEjtI^rMA|UQg2y>EVr@n2eC?1<40={lI zQ)XF&eMF_(KLk1)EVNSt^*T=Z=e~Y=<75&*Q3HyVmY^H=bs}HNZxzuR%B(`f2~rBA?ar^vOIG1BtM!$F~bB`$)84Kv`# zv*Yt(MR9gV4t_@arcBSE*k+KZdH@_;fP2Z6~X@5{O zX7ym{K7^zWqKp7V8PJ24DK)P*d9=>bQPJ+;g!t27jnFn+kokPVxWsfI5|fu|QJ#9}58TrOu?*sJl+0Vk8y zpdaeryA46p;X+rtZa8A7>*LH6)cK80`zJm+zV6H24m$Zz>Np^2;M1Q*>ynt8&_F!( za^UqYX?H%gc)20fp(O-+thAjl$EG@&Dt4t8eWkG6Rj48{=}6DVY)hjH`9X>LI|F6h zGqwnlMw5y1LP}o27W=saqzBo9D;a%N+po9?U9WT%rCNtX7tD~9S{T03Zg((IylBN3 zoTFl)khjNzgknXuYZ4WaTde(bxlG6CuNRoQ^(3dQp;iFS9o65I6%B|e8 zsj^Xn@9)1d>rONsz)B&Pb$9)^9y)&gY$xNNQeK0UR%(_2!pyt{tKvwgxFtu&#q!+y zb{*p3SNPcze(T$`_wIV__LDlEA7goFJG5|@?%2hhnlU-!v$&k4WI{6$4iLEtNxXLX zQ%s23j0kQo;b3B=vGrjgj66?O{3(*52he7;tG-eu7(0Eu%TQ62V6NsB1vqm=|AQfF zNa?2i;NxzIZgPXMR}0b=#l(Vnn!7dG7Q;I=jV^P>Vf;6D~#^N{#prfPSM7Pq`&r zi&;M^qd!ihy5-$moxaF|UR6q08;PvHwE^iGyws{rdmcMUnl>i^)P=DuMB_fM;wFA8Ee)=~k}yMS=LV51;l}GhGkNRs|#)h8(m=1iGm? zZZrX|EFCuztH)C%Acpz?2q?I!-jjZ3bx-Q7Q%h_5do$-y=-6pd2L%HxjqOL>5Qh4J z_YOPhC5`#wX^kyn`5p~SbtyLHy==J_ra=Qk6l?Tg0VIx5%~9iV)cHVU$^I}VSU>3! zX_UHk%?TsDF=*Ye1G8(tZ~(OrW0E-O*^)MahySaA{sytt2B>lxLb zG&(g*IqH@BG){%emhMBFg4h!wOMvK|1mo*H7T~J6t6@lx(g77|zo2SYH0(Az!h34y zHuACqiUey(eWmjNR>J;?pI$gqF59PxZ{c)Kyij2ZE9W=l4!`O&f|Dx5@0Fb7%-FoW zd-mI@nSbeph!9E$fgKGBC7_U-xx`2gMp4J|!Il1!eLuemKQ>;$@Tr{>7c@$EYma0) zoS?+z9^*WE24S~R3KTLB>X}bO+I2vLM0@kpGcH%!R4dj)uS|z_1bFDu?wd(Jt1DZV zC$=syu^JQd&AmpaK)~PtY8<3ALCP)SsLrITWr5-vKqMYo&jxJqik@{}R&PqqJDO)p z<0=0*&&X#=SuS*+@Fbz9bKj1&kBD>Ev9Oq|bB0~lbhF|UD&i0{d6CFPe>(8hG};hE zz(D&2+3Fr3sr0*_AygNQ?Uh&Ojl-@ELeRi*w1b}EU5-pKMTmSU5q_~d9Dnlv z)$JVX@t)c`3!zY;gaM8wAiDT%))U~a3${Q3XMH-R%A<}dnRH1c&W6rAazN~jc9-eS z%3<47`dJ_sI~priMsAW9W*qL{EEXTu(Qx9HCRV)iZzHj_qDl`WMPJ1xY|uJOJ%NEj zlL}81dW=>b@CrP!IwIe3d7xfHj`^`qlZhr6ZqS{lu{1|= z=`D5VN%S^aC$Wu)NezXq2NLuX{a&TzNxUM$mGY@XY_CUKntmi$Z>2l_B`@O!T{%2( zA8y8FQMyAOm-cVvfhdF0Sp#_^j3Jf46s!F0hP#N)Q;4TG8o4)kr|cD17hY6t=4e25 zu+`(B1!C=M%9}CDqx+-7T9%_Ahqx=l3}F5O1HxDA^m3I6SFje?!jJD|9G|94~FGQ!|y1BUiq<+l&&2# z4Fj)MDv|=Mhkywc0~dStf;VKPXaGjCf(pUScOZL?8X47X%7z2%=Sx+de6hbB_16Y04%iT-WQ@Q%P27l4UGf@AV6W@ zU4I@lP>MGF`%yBwyL)m+WampYo|f#4NOqB()OG{g!$uqapZBU-0aC}jrHq4a9~rBA z1Z!H2ZZT||?(L4FL0|5w?-hP=8P;vgGWL3^A7jt^$1gksVxRnika7CtOwoW<;MR8( z?eJv{sa-$hJ@Lh5{1pWQ{(7VqedWrH(u$Te9V2)1WV;-qf}eOg(jl{M$e z4NaP(;8qtk>%@pHtb?ZP6->v4r%-D`au6R%#^5i2Ve9Inpej9N>+7#t&yhIzesWZ?YJDWb|bO6Ea!b7v~M7oaVt3Y2o ze8v*wnCjJDL9einzVm?+R~&H>mltJ<3?vrszi>PMJA#UhN81u2odqW+qz~{AS}X z@Pi*3k!c}{fZ^8bW+Tha^X@wJJhH}4#Rks9wTaicClfE*Cbo|kKC?n9@H#c3_t|yN z4oIzU1pjk)-6}-ticR`sVu!oznb7y@+C;g5z3Kr=pEY<%Z#oD9^wA3)<_;YlIZ)!L z*vY<&h%QO~(3!?!kamjksampyrGHL@zPYb~X_bBjpx};3qyN&korjw8oVAr4&B%^Y zfR&a&?@fA4Mc3q;g3`Y?L3{N3q#ZJoAiC4vV_tugUGk7qrahD=5NV#X2kVK%#SSYRYraN}X%{2=>{Wm-RtA;j4hQ59zQm`OmrB~BE zM^6)hAC&8f<1XKtc>6m$QgO+P07d$yb9Q|BQ(W5fYR{~Xo{I+Za?T1>0Cn%~-cGPi zce(E=$S_HyCca8U57h2d*ReD|`34`>Y8iHKzW4WAUTPZj{dL6QI|oSu-j^Ku*Hqre z02w9GTDbK)#Y@st9regEknxbhsNE~)cSLC~1;PMe9u$?2<;*F%wjRVP|CK6^Blkel zRG|jjOqtE-aI@bO7F@KUVbB>6lfcU|32X;e4)Cz8jQne(SS#}2DZ0LKm?WghlmQSUW~56|gu zG>~%VIqEA~P4G^Sn^AvG_xQ^(%C=+ z(~T+jz~36vQ&+GdpsaVqXY*q_o~EUE?kkH2@n&wDfGTJK@GYi?1gHuqc1{Suk`rQ{ka z)y!Sg+)9$>8d4$MRdXw(LU*Y~l1hD$O1k*%`}<>m@0`8Qd7sx_ujljecw=8-w4H{D ztrAdKsn>grlGYy@J)(Y~DHpa|oMwIf{VrthAMwW>f33tEndLtp?#H$c@#W`XUt7*P zv&(lg@V>_?U+GReIy+?qh>1^bBW2|U2r<=?I2V_t1nZ*%2%Nl$FZhy^O~|Kwdw)bE zzh*=HbW;K))V4?|;Yl0J3SDLDEiYf_Ney=_*Y&%Sd~)&aHo;m2n(sU{(&hB#eLwmX zgCRPwvy1j8ap``gslUHu#H}urq-3(e`$1D(cF9(>oc5UFM-8Rpfjz*o;2y|4!<0LX zWJT~sGvn}E(?*g6NI~3eG1 zqHpKQFEz3fiw7ozK1+3)9j_1XH`826&aCn3U&PLKM{RoA-Ii`M(;N8|O#pQfHZ zp~bTwX7GLA6`AslmqbH9l(pbUG*99Is418Vl%l77ffm+)6ifZ2*i)dDcSVxNtWLUCaumr%t0wciy*0N8$YDaO zUQUjD?QC7C-aaEF->7i=sza;O5c2D=-gEm)A|sc_V!JaMZ888xO~{AiKsI|of4DXU zq5*tS21bDgxtxag$usBnC(?U&*1Ita49B4ZHE7eQELmdu4Bt!Z)f5k&~q;bOQEM6?8d%5UHUXDdq*Rf=$J`I(WwB2 z%(|umMBt4neC^cAeV)KzRu(|Pzeo3%kBF-jL&_)-qg7=uRV$sY#48^4Grh~1ZmB9O z$ukv;1o)CxeXHhA<T0WWb-&{hF1mn4inAo_-}jg{R4e>@j`0 z4>EWlVA7%_^nC8jYYnHlU=g22+E6CL!MH7HuzO*=`*)v8P~SA9q{~pOow>kyr((o9 zY$eVz*?_bg5Hqi&GXMR}BaL|4UxG&hgg@=&ikHFgC-GH{Y^IKT)sy53J3Xnu!fD-0 z9R1k`HfcQAD_)xlR7IQ^Id21#SmVaWh;~rJsZ`xz4<6i z1d%2=lcm0GxlnoqVy)zVi!gqye!Mic^DOh=R~T?gNkLtqT{Ux+l??9YTlqf-z1~gQ z3pM!5Z5uiPBiXZqmE`7}(pyRZoITk{;#?|!@0z^cB-u!%S5xJEl>*{h0m+3&JuZ+`*2vwHVBLi^9>3dsB$K;V!>RK>P zz>aJ^tr-))_}&~$;;pCS3D7g5X4X{lR>olXssh;Uz>+Vy^JiZ_B~yY>{>pu~xpnex z^^Ql38}GcYR)*cwLh%`06<76=*?oY}?w9voQLt$>QV(o+^N-0;ZXbZ>n0TAn&wiXcOq3;=|wm)R9Hq;y4ldE#;= zjhF?bDEf~!{*tVUX}}FA?qw*I2Sn)f=MlUW9mZqG$rrOu`X^q-hsHnDqT=uH*rq!MT@9eFM+N(Q~+*fey zaV8}HDev3J-sNtmtZePHNkXE+0Lyo{BuJrt_Jrv7X<>-4!Sx&Szz*)4xnxmH z^U&vLQMLQSw&ueG#Fsn1Xp@u-0-2O03k)o?Tb;dN zAPHLY$IaVO=_`bxx!1RQan{%$AHl%v;ID>Uku!8h>{i(@+D)yyE-atqt3TrH{_ybP z-0i$LW}`NoKZJwlYG{43J8CKQem&WgzlatnVyBh>9xca`G=qRSM9Zgpb!p!$Zp+56dqB?-9O;2#%*xB7`}Dn-{u>S@4JFXE+d9eE-g}f+0Nw5H;|dDh z;e^<{k}DyQeLR?)03mCyb^)7RVwpM76@ci>Gr?h#IA~MGkun)2FVtIX&mbIg5okx< zKP^0 zcJc4>o1iz`I^IkKgzbR%z#zbHK2+y|ZkmB#gvc;(hDY>&gx7MFq4y}hd08a049Ijs*GrrTouWD^G;UY~s?_N2)Kf42sPRXX z(pW<;gh>*oXp8(B6}`B zds8&%Y0KQONL>LMo!waYI_SqeO)bW+WI@ryr1}k zcI4w6foOSm<{J^avq2;5Q^_@raL*jk>~<;;ofBKAAZi+@oQQT$cJwXF>h&;B{I%FCESc!-ElVo`NUx zmV4`Yy6%nCHJr^a#-PUk^nvy2uie6re`E57X{9c;I=Rqz$TlyynO_LMWbTL-yYkuv z3xsAldI~>qUS4KO!*}~Xd=D1=_Ef9gKO6uh@cPFS-8ZiNk@>j*ub)k6>X_!amk z_u3S2J;7AuN}Pv~)|QysjjEG39NphlEzKnM&<|Q#!z2-1Jn|p*mUNpfpGJS_Fy=ok zNdrCdQ;fQYc@_uCyp|&fO|kZJJ=<|TdN|9PVcrE#8O^x}sA)*E(6tl0WP>KkY1f4` zwrjNTaRa13&qR1C4gWF=n!BW7Z=yNBXQq~-nS9j6ttST!?}~ESVsT+A-D(4VYQsPG zki`tBB1TuvG_G}!IPj%>S`e=G6w+fj$Ueru;F2;?uaBTG9n&fq%^@NHY|w18W^8is8xfHv;+#wd zOOWN79RO~3@A`8IAyMykBtP&Y%qS&A@IAdYQfio%`Oq~s@5@(fdaaCzf!NyMM^x?(L#wnwCWD%D5^{-amw%6e6N{M>N}dssEJ~7g zW}&)dcO4txnr(=WLIU&ljHyPm;AH0h-O!4NOi8kV0p}m?5vEU(uK)%*9EZc1H3m4K zfVutqr~&tqU5#DFn6zb^!J*0YC!NPJ)$QM@ZV41TE0ykKgNVoP-36wBBq*0i4jo!2azjC~6S zB3x{qPwiPT(frg3&B!VLm6*&~%VGpkv_)5REncJ3$}5yiG)^N;S@%nKsZgxYFAPaEO5PSOkbopv|3SaBv7m;h7q{;ST-sq(u zIiy2mAm^xNpNRC2pnu$(b%g+0{si7>z^fv({M&p~hL0UG*}GCm*x+kgFFjK!FkeVA zPd~Ouhm$Ik#46qT|59)6ewgKviJkDNi;_L;_a25n0?lcV9G4GkK4yBZ50?v(dA7=i zVmOMdBFzmPyoMF&69{~|vO&7*$*nErABVSU6IdT20zoxGZI{H_r2}%_6&ZIxfWIvv zXSG86?Hztf@Z$*W#IKb32&^CchEaa{b1RS4tLd40T`{$C`c_J4%e57Hbx8K!R0Vsu zQ~u`GsYPL4pkm|(%sAzV0RI?a5=Iaaa$aBU7zX-D#$%AoiJgBhsF8jlcTJ6^% zDdkC9w00%Q1VC@#d-2d?85k_<`Gxr&ELFf5tJ(cvK`*e4P$$;9}w zbb6&MUE&os-mwVc)jSXxzp5AGCPG1-$t|4dXc7l~@ZIp;6U_MxNa6Znp;see9RbXU-JnerACV8%vKB)vH>=rJ$=ob-OJ06q)dB7Ql% z=c;B$xn||nX)UPH=l3=hQK3u^C89CW;RmI})thoEU9|?hrlVb^klt96fB&)u>s}Ia z8hXJm$lZPT#@PW&+#SX>A7lz+&E+F7$aeWgIZ)(!?9ofTnW7p7-d_h?DP%lUPTRmRET*4}l{l1H`braX^$=>8rr9o|p<(Xz7Gj zpl;78cXjeN1B0hnwI)CK{w55ti66J;@^nFG|3SFt{?CNIkkSM}_!yhCd;zll^_Ezh zdXS-rj;4nM)2^_IWrF-CNzDf{6RSM%Jys2LxSeDEbCyGB!i1Sh7R*qY_yya9zYGw^RE8`VjP(awo{KJm9YB3ddm3k;ZQ#=JuPWH{}t;8vXJ4G9TK@a zCk{^tm-$KXMUh7+B*WTvMG+m(^%tK8-PDYm346ZlyO1T@7&Q#GdtVY|V z>L>m<*p@R0{t=Oow>9%>miE7&p0n28|A(-jIpxwL_R0Bn<(5Wg=k%OZStlCn-eRf} z_M0{9^bC=>L-2GDwhULRxWS`0Ggw$zqxeQu+V`{ZeT-`WGGM%+06a9vQ@kwhuE30 zm@PB$S5nn{T?D50Rh3DPqRWQt5{EwR>a`84bJ243eIBx<@yi3W<%Dl5qj8CzlkuT+ z9`~3>WZB`NH=6S>r1*uv^3P$mpN$*B) z@h522550OH*^0aOZLe_w0andBl)CJ-0~uI>2Dy~7^AKbI?90CDIW}dy<}}lSLaG1l z(z%Tp4u?co(LBKK&cw><1W2Vwy}6*eW0iezFefWVO}oX@jbUen{-WTSBZZdc^<=4r zMT|I~%Gr(G=>EiT>O>{UIL$l{b@tI2nXAAx&yN(o13-lpHPQGr6xHgm=#MmhZ6i(B z%du<#WUK-$l`!u0Cp^av;F(^R|M8H@+_+amf#Vm?Yz0=qomKa*UVMA!G|jD&a3s7v zBucfB*f=L?&I$degxpv9pEBSHcrK`2H|`lI1}uyU290~1FzXfOsEymhfFdPruT8!o z^&`89Q!Az?uC9NBe$?77h(8xypay%f`JqY(3{9wXI&+)We`+J)x_doNxyCT`4u7q8 z)v`npDA&D%2J_YT7Rb3Pv=$y(IWZJqe%P;fbt~|2CvxEB0dn*QTFf?uX|yF@VF}Ar z(e62IRmO!=fx%zy&OzF+TwDTL)hIBE`0FY+5V?Qm5eEf;fd#C)vmwc{3dw$68Ywj8 zRm;bK(AmiR*VK{1q%ZY~Krol{)*4Q>r^x$G_~a-jb!OPo-1IV-N=e+4IZ7}kTBBtq zAi-vw;6D*@99N!8G^*kkvEF3E5aC+1!{hsDhhnc0fM`7|UpfZvS1G`|NyRrg8+1-1 zvE7EA8t4^ILR>n$zkqf$rA)H@ul`^aJ*JbAP2Dm8Ac!qJBM6FPL7l2NhUsVqM9!hr-xGxjtRHT>ET8w>e3GblL+_ibeEQ~# zT(VlH8*@g@M9D5u#blAyJABzFDF9&3_S+dhnXcILpgpT)1JQy-_9e7t5;v%*pi(fL zgCjTpd!3>TczoZ#pcFNcP-dOfl|ld>I39N&1?CyrFNB)Oz&paJcte#dRu6^53Z`lc#Z7x?^LRfU+d+C zqA~>WkM8bIyYwm`YIyD0jev(n*-OqJ=(eXR`iWi!BIo|aJC=nz&h#tCokT$EKIEkq zN7zX%-3+@BBd(oZfovd?vu|-SGs{2UrO6J}4)0 zLmz&lilzEkF?dRedgpN3!rH^H)>{9gWM{r`E{rn4BFT%{>)?C=lz^1cHBmS!(Kd6C zJsD=6ZNBzTuG;T}Kr*JhxW;)#H;L7oPD{+z9p>N+R(*p>8rN?+UT{n`5plZx+mL3y zb72!sZWp55^Nm!kw?-SP?yt-Njgem<_a!GsS_j5zgQgGuqkvNa%49D09*J(np=j@G zd9`jZX%k9m3_QWHo&w9YMCy3TZ5ucnf2OH_V3|t%wY?wV#d~i#K7w8kEkF@RkncB9 zl+2(b#d~}O(&He{*+fYu(^dZmw(w0O|Wb29VWFUxCfRvth+EK-!% zbA4s(!4Wtai-C<=Cni-oP0WDy+hbN?>uIg;UfYq9W?@96x2eL^x9V%X-Ov2lNVAH8 zBBT-%h(V~4G`^8zO%R`sUeS1tg8%OrM5~Lc3}&gPD>fWeZJqCa!qh0#$+lmQIAx1Z zo8RW@G4Q!dlC}L$!zl$dJbyjz2O*P=N1w$p6HEWnSauW~DdvS(%4R-?G2p|<5!g(}+w-@j zJ^%R$*5Q(I5aH?J46}ebkSE#BD3O~D?7 z?w<(A%A?vQl&L*lH;caq zVYbe!(NJ)cWykC~BFbrI^V^zP=GeUxqfwT!b=S)|fAT)%0pFmPN7?^AebK^%=`x~c z4FEy-#C0h2eh&XqmQblqrm+U^rvQVue(=gIbRdNeY$mxv6NHw9ly;P$rZo= z@mV%_;U^NURYVzwAaiIS$lrWo_|?JZMN+} z%Nqd~!=muElUDC-%{ud9DpSn_AGg^zCN{nLo;9Hy0kHI*?~tkctV0HrM~2hrwI~j| z_iXxTX1!83_Ue8633LMB1dF#gav+!(%B|=4cB!8s!l$+LI=xWGT&=#{pU*p!nMHHe z&~EA`nw@-&2c!k(!Y!O|@xFXfm~ajAn(*~`01IbmW$+tf&vLP#U2WRpzl*CXC6qA0JM197>)}zA>krJ!`EA0 zJ0s_N&Ay2wx#SL-9vY}INqHue2~eOkB7vMz_p{4uo};2E8JoyVdK3e-ON=_ik~KpI z-<#aDK@f;;r^8QxpS^d8@|2vQpiSc3*C&Fs>k;lFou(e(~n{!$-Zy_7fx0Q(>vfiV7zuRL9C{rOp0s<_~ry(*at+kPA++Xyz5$|s63 zc(lg*SumGkliUFE;!(VK>~(=>cACmSD(r0!5M)mIV<^bw<{kqJ{h(3K)5r{#uf{m42}9gpPtcO zhq~oT#aMbt;7|L#P&P>6ySw4kDFAGdg5J^&y5@jl063vV5!ax${X{t!%i@<(kR9pl z_ox7&9l5yJs=avcvMeM81PhS{+>%%Ka#wSXBP=8WLfjD!&Ah9!rf^H7=L`@f)2V({CC^xadROm3A=6X&I zkrHoZ7+ztRh)rVI;|)=N8`6VPDFn&3bE8R^1j6A&(7N|jQnwv}v%hNoC@lf2Xd?|Q z0}gF6siT$Xj9|DUL}0<>g|W`_g3dwPpm|s6vQ6t5_yMc2PhEvoi_K~Cu}cJ%c)l$N zEy^g0T<%h5SVc|1tqR1bfyUk4nf}kOZ`QwtOya5CG*cA-8Tiq1IbO943&t)0?dHKf z=rg@sGwXcWk6Kc|;jVj}2r?^MBp-t+(DxcrDMZv_7LJufaHixl)ki4?GaAD+Hn|YC z$4yb2T})U&#EGrpA95l>L9G>V{}rWO0H&oD@oaFj<+?E4X8A_rBLINiSFX0b4u+7Z zZNmemMlk;yU`!1vKHMldb5CqL;SvDMHPjK99*HN5%CMT{S3G@oVf8>#7-QDI!#rVf|n#SkUY4!bL?xCuG`_(A`xrINDcE z?J6Kbna;y~dvks3T+QdQi{r_N~g)=iY2uHxf~O8 zalQFuJ+Vf93`4`SL0pZ@A6_szINNIz__{}{WrcqKDt)vAr5=_^p7a=E%74rQ*H3EP z9#@R)KwXC+)lzJ6?XZ&z$5az=6bDLOI|bm@1pyfMP{ew!sfSJCHni2mrV68Y^zQjn zdn9OQn!ZC{cw;xVkb7`y8ug_Vz6+^6w;wG07KJWGkxeaq#diAz7ipCJXhY&nVS#yf zPG6o9Tke3Yuz?WV)>6O~Zh}$gI|;Aor1+TJ_b~v3X+#pPimqb1x2(F)qh?~Xp3^f7KYVa#wRFjDb9Jt*Z$mw^d zzqIjGx{5V~)#GnDNm-tT`}_a_X#wRfLWMxuDF7x4Lc54jTeKBlK#~VVYtK59|hsH$H+4B5)OSTxEBoH$Jgue-5OJ&j^P=kLF<1@2-|1 zce2y1^x{m}nv1t*Wm%@)+59u%#Sm5wCcFo-dDr}ew@Ja9PJ^YU42JFI?IS-?j#PAv z7@fkxNGC@QD{9r9buN+R5RXn5omq^yfhksQ{-l`Fw$|Y@Dp=$(NKe60RFgJ4@1h}` z=g~pz^I8ai?qrniF2upQQl7G;=HAbfA)~JV>c|;9%r|C7gKcf^Y0g!&Ap-mfHYM%L z`yA8s&Szz#w-ZI#7z@aOFlt-ex^nlk$59iF7hjEvK~8^44+ftI)@{a0owMl>#z~0W z5BiL(YH&!2AiX^FFtN83Bw}aj#@+s)Tp?iC;((e81vWWJ~m+=nQ_kO+K_BKX#0X^9T`kHQ=C%-#jw#rDAzT&*6lKOIPV0{QhxWiYKI>Gm>HoYb13W z7V)r!5*Ho|bupcUF76)#+tVhJFqvWs@hWY?7!|tRudWJg3!s4f(vvmlB^+>Pgcy*#?-cyw_S75k3J2PqHRM?E}&8h!B#$4 z3t{(3kS>y!Y_~{k#T)E`8YCj~WkCR7{6eBI()O92^dp6kM@SuMs<5hb{Sl?7P03{Zj_j+G;o;Ptj7z$_lQn77Pyw#zKX-^$8sEK&g;2p-U) zhAS-1Hp?EzId(RU*1;cRyXL{>$7i{y#Um@!wNH;O0LWR8(ew18N+8}7`7E@-KI}ih zw>JVJ1Ln!>rH(66JI=Cvqm8|0nx_ghPl3;oHKjsSC~cq88TESg9E&d@Y=@! zSHy)D!|(5$jnL5bqeR;%zii5fi)~22rfHqI?i*Sk&)v@YyJFh@;!VbuTo`NVWL@3V z=FPZ(Z%a;-T?2J=Rc_mvrO*Ew;j@f3{`1e_R+m;`p7~34cHy$ku30NbPcu<_UsN0$ zxl1^kzJ_jUts>qNbbEOE6zd=-loBF&IwhMn7Fd?!PPEmU%#urx7ENh_h5^4LnQ*vAxgc(CHJy)%j> zZX2nCrK*C4HJtT*XgWELQ}*61X(_`~ZJ#8I=pIT&X0hYSx+k0-ISkakq0})_Z{~7F}qMKS-R3#TK zYXN;KskS}q+T0}YiL^X&+_cV(g*I5hXvmNdZASmyOM^PeFd&XAb!$Gw_uV_!9AP+u*h_x(<+{W#cMo?2He#=yHqH-s zi(;G4I`K6&2#${KEx`dHypH=u_lf4wT>lBfb8bH!8})(O3#Tf!QFW6)ab~>rD3?7= z50vdrng2A)I$_;A2S#RR9>?Cn1q7@XYhaW4N>w|?RY zEx8J#l0BzKda*R#PdD1!>aCw0@NC1&a^V}F8wB{Mh6vRQ=$$jpDw^J~wAGZcTrCs@ zdwdkkrHTkVd(ZsN-|$ZtnHFZXArZ?Jx!17$Ax2<1F5a)XSNPAx^QwD$Ns zd{ybzAiFXs+^E{C>>o|XTT{%2H-x%XeNK$^EBg=wUrzSMLN&yv&bfs6&oah2#a**vXQ=2vzPfqj zwvWJtkbfdy6ikR>rQ4eB(ROUTE8fB;_GqOmUTq7@&NCxn-Lp4vGJ^VzdF`n zxukUCt|>+#qx;gVS0OhBlU_s8Rr6836JMX<_?}^oA}2Z8mrkxbfR> zM!OCI7;~6I$^yW5M;}Eqi#g-*S8sgqkB`g3CDePtr`$b*p|c9@c9+3AC}`{w=c_aH zK=&wdXHprkjtrb;J;>#jf);FMk#kRf9C6Sg95;>l3g)37Wjm~78+e(fJ;7n3}iaeJLssp>HN8~==Yy|l35q?tQWxD2L@w-73x%H zO*)QZwS)TcDVFp!1rwYp57nyV6Q6ou`4zsnL{7+;#Scx|$x@J9PHh6@SfxU%h4|hFMM7@#&wARdS#D0G*5i zJMBj|-!vG8uPhxKEVz(dXGEJx8z$|Uf1z|`yl%&#X{zlzx@D`KM*k%2=6ohra{x{G zEtpoPCS7nBExnEW)#}zbTjmnb10I}(AMDLhsddokXx?tG2_&E{mESWdk8fau za}xQR%l(-AzU;sAsB%Y5+$+Cg;a{tR-)wK)dU3t3v-H$|VGr}6U^Tw7cOP-lG$T1}1$UB9{&C zPB%SQR<}EomGOcsKjv^w^^3Wo_+`T;OHN;dP;a^t%FnXTM1^GFRGN{yYLXj{?4?78 zvqxnuf3SYa<5Dz{H<8$Lna8YmkNxc}Ot4&vJYKdZbDp5(vswXvaY6j{Fab&My_WZ# zuUd@bil(FCKOc?9Dx}?uzRs=-uc8=bim5B|_VDXNH`P^9=AwF;D5FF;nU4hlPZaJM ziZ4=k`Rz%HQ0`TzjLhKH`AEyMPNdx|HK)eQVblL!Y6s2nt3RsmS{q44;SZcd0Y^76 zSDoc%Pw@xp-9Fb`sl0zD5lLxnSCf@00OXIB*_ls3Wh#h(N0*;Qs~?9QB$%25@xFK* z0LWJCO!jU7kZhhMHW_gTGe-O1e(QFM<*+Qkg%RB}z9SM4?~jCFHhYJuo1R?0;Vs&e z@#39J@tuc46E0k1{w7{#hlP8qWAWsz3>=`4vFd|2*!#lF4np3>!yj&?ID7L@d$^jO zGai{1x{5#{&)UW^$%=jE96pW($4d5%ndYAHCeHvxb2n8UNE1tqYwe}>+(wHH-Hy2)HZk9KZjxCn5J72kvrW4W2QRj zgjD-6Gs=8gez9$hF?_4gK2UVfzV-kDX1?-FJy5cj(!RZyPJ59Jr$^C{d}L1~TF2bN zZ3%A9KFrdh7(~&!`waqE+5O$AtQ0|-Ak!y-V|xM>?T)^BLdVibBeLT_zfp4f_Y^7z zAc8o%!FJj#n(j>Bze1I6EqIK6Z#}+tmB7oNyF}{4h6iZv zu^^%{ow^T<+U7A|c7`IrQ;xavP^k`J={K~>Iz#3tI+hh=aZ)#2LT&P+C1`_Fqw&gW4rQ=0RXjJC|b^)s!LcGwF|a z0}dl$Gsr`G_*r&tC?y;~e_`f!HNWbb?BkEdn>z`52rKj`OmQU3M?Iz5n+y8L`(c5U z=HMV9bz>WKqtLPW2_@(UO`uit^Pb86U>di>=?U#fNwNLQG(`ypkQV|}UeaB-TWG!t z#8#yWYummP@&=WWlfe&5cU`3K-FIDOS6g+^h@RG1o@vVwMDUp?fB9W)!s{c%yDTES zfW}RfTaEx_c&-62DTOa7jtt>WO0}~)c`QK(CW0xnXcud!9$mEtuftSOFv9vsGrW(2 z#wNv`mvsz`gRG?ci{PW*C@r;0EnX;~I!W#^pMXTjD^8&aq^DQTv?vMG?rJN(SoCcS60di7zpqPN95JxI)(8Yx7>tMOWhNw}^Nc*2L)7k`l(jPiz10W6e3 zx5~AZ^D!pJ8oZh%-MW}b^lFkp2UBtPEDfAT0APo4uu<{+dU$(GDMi6zlWdL9_|<-7 zumpW+FeXU6o-P8^_g#h?9+GFn@SA0`lXG@}Hk;<*KN<8zn6fYml>UE5kJavJq$ z(`VU3csb?x*px(!)N;k5@6ODIua&aKI95TyM&*251$2d%fEo3r-ILwWj5XO=oInXa z$DIVIZ8rby%lZ4xIXoMT2C(_MMaKx&k=JGAAz0FY=AFGci1w3{k;#e$VD&HBPNy3s zqdp;Sgv1p$y9|Zb8$cI;7O;9wTOKS$Fbv{&1v_-*>6G|Cwd!_!^cX_Z_xTyIW6)5wbAQJ^`&cI6hQ_A~lt7cbc+;0c30FuI=n zZqQ%vF1}bhZgr%`a~>Th3QnJ+z0a>l8pdu&zk7nCa)vMa^TsvHbnuD;xXZI#))HQU zkXH;-1TE>?{hW6^h2cTH4(ICJ)-JaN)V@iy>pVi8U+PZ?dXqC4YfSlMp603$yy=9( z^mlg9v`f>N#pHbr`{UprYAjej_MP;jSH33YqJ`JzbF7DBm zEMl~UYDLuXkoOkH0lPr&z{)aZ^d3Ko{_8RLB*I@81o=HL)J#hnwU>?zUkOlB?IGs< z8ia^Imbw85WKnu}0Pm?=_X7YT2L+>Kbqo)5#7Wx_ zQv4o>6pr~tS|9XyuFAd**1$ks^2O)0oT5`f^VZk*X?`=VWhXo@U3HUY4v6e?+M{}g zl89&hQJOcm$a*<#YHinBR;d0dzgM)87C#dbBt~}J)YN)qym$LC#siIPN|8K-I=D$u z!n9ve)O^d4_I?tK0GWIk93=)wfb-((hl71ON_7pTlT>#6)yZ*COIH(!2b8$yBLC_d zEZE}Mz)n3-$y_l~GTFXG7WVV)K)dF_QrJ5HPS+d;ZJ&t&hp7`*7q?#aF^vE} zO-u61clnK2I0ULVjy44uY-1f%{mcgQ{!&-sb*$^n=&L&mqYQe<;3i%G_^wJXY&5X7 zoglQF;BVY!;qdB+3(fWNDfEu_{R@v((#(wwC>O$RP=igF3#bgD_D+LM0&c!@;>kVR zOG+0!KL=7)=4F0KWHQ=QuT_JEGbdlzd~SvpI%&Q!c6-=f&1jgm1wX1_oW+9{(RwSa zhk+Ek#!r-BY|2QC(}%lgt8I#{+P%t*n-3|u{fabkADDnF`eK|{gWouLZ>H*k{8tED^qhY4BZ)EQg?MFq=w>`6%x(L$cj z`}1%k5&|`7%!YA3%ll1!ca%suHvA2xA<}yO5V&*H;$pr+PMhRz$GUXwQR|wG7K^@m zaWvnHy4%C_P=~>4zc-;HPLxSz>ZtY3cvPg2+RG&dlH|^^wI#Ds|IF7a&o5)`8b7%E zHz)W$`$M%Sn4B2lq9_Ah3zuYFDeJnIT-#s|kI?>nQ5>g!+N$1Z`T6z9#1HVv*H`C| zwV=YoLRxPdVH=K;Z?bXgJo+2i70dk|&H&Ew$@Zf%e?%N<{^#@sa3?hJxnrF^%wag5 zWt$*DxS{BAGzxpSCMW)I1*;L=|$ zR^hA*Evrc1ix>Ign8Ba+)nYOL1ZzH!2T-s-X|i`EmN_KVt-I|ThE8rq9pLm;xV@{) z#zUU0Jezm+CCu&B)+gQHJ092msi+a$#&N1Njos>8ru9zAH0E~tWQfp@6VhLBGVHh` zu~Lu(CGH+TI%HtXNR3*)cO{S3d*3?rz&tNFTh3FwwNGGEROWiK@O1x+%Mus{{o1o8 zME>i}+Xw$DEk$~)7e{6P}x@*`J$Y0S`$T^~zlLcU@Pwb9%hu=5KXcBTL&ua_RFaFNGb`to{P6`U_4UH4}oW-pDr_)yr3-!mqV zap8VC+K7pGMdq7a(~)waA!()CNhuoHU{!%&w+hq%C`TPw?jfvi6Ip7sb@@h}to%A+ zSyYUL_;5Tv@c~CUZpf)d0L6>D{*$j5%~Z~vyI;Iw)Y8XrS(i{Rd*A)BYenH4Ypfo< zD^X;S*}{VZItm(NI*!z6VuQ6-O&)nkL>q|5URAiAc5M@@HD`_K^+Rd1L zB%i-MCb4>jul*iSdUsg+>~Ug#m^k-Fm{!-N2*n63IT*I_-O+rQSRu+?jpq-jRE&yD zXc7Brx_}X#9PHxK!wk%o-?>|k_i5(|;LiTyfS7%ub-KC*vFl4+h*R+BTx2lLN_E$M z-m2|dpyEP!_7@BB~JtmS7cH^%f3f}6QaX= zDkVvUN)L<)vW7kJ$+xw(4T1spr1Z~malG3PWd>iw1?07&Mhp(b<)!3V2H5NBRGXjw zcFO;bTk_jxeV#3IW#j90=)Co~KgGAPr*8kJmhtMzsT(Q)bDIxsO1RX+b zc+IjubZW@gN?}jtsmlwGAW-YIrL+9Hy$_DL62PGC2E5WO8$azNcjAx(UM13=(W=ob zg-7pUv0l2>T3$4g>QPzx3b^fjfc<|Aop~UWe;mi3T{djYHMh38?>TdonKLA3&NOo0 zxrN$hwh?mYDsv{O5K^f&w~`b}k|d#0sVH4Pzy0_8_x*mK=lMRz=ktEQUv7gj?l4i~ zVYqBR0G1cELjKs&C3WZVB9n_z7j1NB2fdu?jBm$WPzpV6{rJeU>0EUOZd#cM&Q5K* zO(fuvQ9Q*_vc|-y6c7-E3_lW;25au?>naqhns6+dzm$5bOPOT;T&9DB z$aHA{uEccV$m0#y`id8tLj*D=9!j#^n;5Iwis`Fj-xeb-{7hg88R0G}4~@y^f+mZU zxF{8A`~%rzc%uUd+Ou>RVAzmXjB!;4=p&Rr%F7y-1St){Z3f-P7TnP>cgH1Td~r5c z?!RUDVGR3rS@GBM?|adBp!gQLRwUeLDbiI);2U_@NK53L3APh4;>;Ky_d*aUR(vC+ zLWWA`&j*mNCfr0bO$oIdrUmF$9_pj8dEavzwZqR`8CoV(YYVOMSGzVjW#iN|`~!z} zv_~t=2A}uQ;)tA6BjD2|g5R0tWUNdSKYrz*Xq)UQRg}&eU8OfA{32TgreYApZQW$q z38%&v7ui{c4Ozd#9(FKAjR`RKlo(+%$wURg)fr7l#Op&stL_XRN}=cyLp}N&+b<4ep9+tJWmr3jYFjk|AOvR#Yi{>(o@ zQQ1~%fM}7HA@Z2(lD*zZQPO0In9Y1ADuz;u^iLP}91r`VGmLQRxg>HUx5f&?I^pR3 z+a*1p(qeQs(j~g)(dq}omYK^~?K9y+tI6YSKKk(0r45iutTcrFcdfLup&TY{<0|vi zfnh;(dZu+~K#Q)Ev%%(5a_eS;&~)q;<;Ag&B58<+O-)H$=|;iIz;FMmGQJid-=n0 z9YXGOx5I-J$SqV`&kq;a&qTz-Vkk|B{5IC7QLJW}JgscnC+JM1m7KE>it_=BHg-Z) zwHlDw8@yoMDQ5L^i?#jdO{XFizF%0kSSP^E9>aJ%6)XrLjivzL;_VjA3-4WypPH>* z+HUrK^}&1W_e_(}PKV`%_c95jC%)GbyX_k`ZTJ5X<4H+iGfG2JA|^mNn$aP#iWYrV z6CkkJr+Ny`&6G}ZR1@^TP+8i6&+i!!ucMhz$qi$$^D% z_>wtrItOOK0Ros{B!}OIBM2ped4R8GqA-*zEY3wMaYYK4fH+5Nlf$n?L~0R}*)Syp zgg}(a*?=Iv3rWut%D2Gg2(gWmwYKV4@A=9QW%$Pvx_&wg+z3WkMo1cbKl{47A^mps z{PI5H1Va3z$j|OP&)oubeDzJB&0(N4Az#Js{{3nG zY!JnKx|i7xAz}?4=;t{RNHuLD0>yv;t0`QaU*-w|nkDY;zQ^d!q;^Wzq zV=2yKbm(yI=CCClo?M)(m=0;J5)O%B?X|qmxi5B~Noh)wE}$i7-P?kk2w{!(tFscI z1}{~I0V%@>h}_>cOM!vXW`6t-V!s-u5PE@B0~~K`F5RGK1P$Jprp7K(acD5cG zI@5K?RcXvDVBFHsCZp#;Y90Jk9Xw7j>d(g>49Bxn7kQl)J{IXV7&xFa=0h%rUxZzJ^b0xPOp#Ev2()7i-&_C5{B zjdi8s(O?N8?2?jwqK$oOh<#?k*y8Nr19fAhfy15250h6Py>6Zs*s*^b@bKpB^qWan zkJahFz?Wj+4L?CvkKPc#pQBiu>)^(q43=&1qp2FwWg5HX zbv<|}k5O~)a(}KXYYylecV&reRPvw7nfWcWG*2gNj>@5FcvtF4HFzun?%@M-m2*v> zW+peoPq{i>qcit9EoM?@t+Uzi%w6UgwV8ui9)?%P;M!9sd!`TeP8(i$@Iu^^Np~P( z=_6Ipmoj;VT`vAk;Fu78>d*wuHOGs${Rc`uBnb17ne|S@(l_tot0I$k-FhP4VP<_ z$&AW|Z}-Cd3Ayovg_CTCiYr_q0Uo#uIkO8%hycg71?~kVyf6(ik-NBdk#;i;=)=>+7!^`LT zukgz!&`=B?27&G}PM<6E5kf#q(NG}*(#N_^Z`y$y#h;WQ%yqR3c@qUm>{_NFytAd?9-Oz9^Lz(e85Td_vCFVC z<0~p`zQDGyge|zY6#Vyr(vo&_&anBS|AW-5!4gcEu|=;F6JQ?m zj3naQGt++ITl6%a*OmmoC73#xE zfep(aLOhxA08l3nPa)){r7~lunTc)3V_o5CCLc~N!;OUe69};8i<@WAQE7zSle>&G zHMqw(Gckh3L6exo08$KM@A@;DYbtp+3kmKMjg>&ppXNN%&RGUI;;UfIW&n#>8F9q~ zI&n4cF99KamA^}a-_RsDsFlC#DP6+|p|A{+Cmuz;1W6*m(LT&xr1z;^ct#uCEOw!_ z@MwA`JnbLTM4P35;KF|wJFmC(;S0`q z?a!6zpw-qq;cuD+`gEHMaTV$P@8j;vzJRoDJ>;3>_fI=ZC6cdG3d}bk2ip{*H$kXO zkYoZ#9!oQsh8|4#l7V(HIPAa=^L#Zh#JjI7=#DA$Z`2le!aO7M-SJn(tFQ})Gv9?* zem%-uoS`0Ly}Zi57x2DLW~=|4bzbady{P3`!SQy{aD8dS=$Z?1{I5Wfk5VqevV7~~ z^?4q&RN|W*)Mg&)LWCaNs7oQQnsvaTDfS1Nx3hb#Eekx(&kBMd}nHk`IW~@i9g|FD0gUdhe8Qo}0(`b5P!01@JdI2tSp)bBQ2fUH<%bkf&_0 z$@R1F;?JcV6BAWy>0*$;jS`!!jdBOkmo-rTwKx^BFNYyGv`RABvT~t9v{^KZX*pQn zOQT-B)9WRqL@nPhXODRJRlh>zS^oTy3&g9PKm?7>GUve)z5+V)G?Hs? z#+G9yYdbr^J2RcR*uxu>$@e-wN|^)RPHR58;HvfV%GfDT!Lwdn&c9SfQvYwGfE^4n z6I_=TI1NvY*ebe|mZ6rBL564cZ<@(5BLMg*VeeQ%2J`&1c?kTJC)|H}__MrUY$xpE z=iSUgf5YsoOtk-ODs|M0tY%_8GJx&LheI2JeiL&c`-J2Jj|mrH!*?FJAt9kM~MgEfEwJ058-|7 zUSX1L>`Rlt^`5W#DN}5N*xCOi{{BdKx!(hPp>Srm&~GdU@+rm8E3Wo(?3uPm17(pW zyM%?&jHf?@TZrBt0;=`xAC+B7yjIus_tIfo)2Q{j7azuF=1iknNBRl%3F7J8zdcOQOe`^Z-+c74?LW$! zeUFfG-PKzik3HrPsQLE63+oDy+QMeWXQ`g}gZ0YIn3)B;Lh+Qimm4wozII`G_aPNs zg&7(!pJOk)Wa*PH(SD<)#}IzWQ0 z-q$lzZ1?^BOojSX`HZL$N?~<>Q&X$)^AJ)7B1CIKL*hxjB4=I)%cEnI&fEKz+q{uWQG{~S4W__+Ug`X3*R{?5N z4l3s%P%2|``>KnR0P?usIg7+fMWzX9ttPDIMP=O$Q?Qau#8wbXIk(Q}u?1&h%!#>I z6`XH|F*RsR(wc(=9$oHeUNK&1`wq8p(;gP|T=?5{SnMsu9;LfyI7{}t7!e}q0CF@G zr?yhQPkSyb|Gr?_7jYxsc*}OE)O1QsQ_f^+yys2lgKKy%Lyr`PImThnk*6lFP1FLd z-u|l5GI<+dEAO;!c(UjKFm86 z5B$J>+O+)fH%aWk-ei+7@O$3i^lQhU_;VJoU!FMfw}MuuIqdsvEKJ>+TL{Ft8l9Lbm9-znB{7aDgV73tu4-y&oWLJb&pr>GRGcR2s)Tn z*dqUhZEvw8c`luklPfdJeAZnsg!pTrmg%R+VOugG@a z&1U)SB$UXM3Oi-jUl1+o=JT?c*4(1QABKECPczNYU$x94xPs*K z81s1$FIWvJC)B_?6mU|qXLK)lMSv^P${`HllVi_Z1nN%yeIo}X zj2L6+=qv52(R(c4u#eQXwFg^1TV=5zkb?( znbXk~XUz{t+s|EMZ%f8TBw>0lh9;Gep{i)sZDTvM?_bc-|sC|ThGDUrPU?MxBn1~F4=1otmO&5FPS?t zB6cSaWBqZ9%#2XM`o9pnhR-K2rw9U#_>|=VqjZn#{0aG=>Fq~tABsJt)$~r^HfYyz zj<#SZJ2^MLC0)dipocyp|H{lq9~fFMxLY~-?j6#PNY?uRB_aSlH! zyKT4*>{ggwW$V^z@@fCdlP60B*7As$+u^UDTgBhdmGAJhKQP;MF6l~6!o?N;C8@~= ztvB5Ft?%09j)8z6{?0e%VQ&frbrR^3Da*-6GzL-E(r6!F$qO*uRBeCFq;E=T_Yg_a z0Dpoi|)l3r7`%_Cu<1sIIz&gaa1yF5~$)9owDZQs`KEP=4j zf3A=G*RD0-*;LUF*J(*=l|70bRGG{0S%?)c)@kU8IiCGsliU96of+D#cfZ~G|6Yp(c?GaCJE zRzM`Mt*aP%b^4Cdn>)xo1z&6f8vo`_K&kRSM$v`bjFH=MT%HX+K!7FcY6P>xo@3PZUJU*lFdSL&zBNBBXMShzk zLb^AmoC~Tf(qF9cwYjUq+UxartPSJgMq1|x&^vY)>L z_}*|48aC9(;XT@o!`5Fw04i`$&H;2>LXmiS6q2rRtRu$=N}MQR?2{VA(WNbeCX@rb z1G;eX&qVx^seV30aGkBpHQR$`ho&l|gAx>q0gCmtedvpgcM2N$Dw>>%-d+YR`4*3e zeyG>{Cu+Xh?DBc_@VDk8pOy4qt!n;La$8fMu#}#O`NPRBlpHKpvAv_#~f%Ds1 z)0b9H$Z8y(S9CRC(#!VLJ2HFF7rh!*zIe|f#L|}UeASXKbYJ1Xb6;oKAn~K2&0HC} zmBgkD2Owg`mHh}1g2{v-m>ASLa;VXEla}-Px`8Q7#W*4a{%d5egv@d3+?f zkcixaYIOHDx=O@;@{k@t$?mevZh+|-zD*LUu4{l@@meoB$N*!BB+@82T>-7Ow^o_$ zjCOuvh*XJWg$x25^N}Dw0ync-a!@JUuZj@~P)a+vUGGLDcO^=Cc$G>LZVQCxAzQjH zV`BzYwzzeDm$Bni%(Qwf4=M3Oz5c&QES7{7=S5$0Rkx@Y@q_gb)c39O5LDbI9joRj zuBht=(N(T+77>W~0D@9LP~xSy_O`eWmx|gi)o8R=wzrplxO}?(%EhQF3KYs`bhzBpoxIxd?<73XlV zhM0wWnhU8UW#pi|TrE^T>^8I)UYkqIfv#OGyJ zQe^=y%$WmrAVP_8P#X%w^V++k*RF?NTZ_|KPu;t=emd@*J^%?I@(+L{LR_TOYClv` zB_>Ha+cdrYYCWl(E=)mPK%TNQZ)V;nT5PRg+Wu0YV@OJ{^Q96UVmUzc{(W`{9#7ehxAFONV0{|V zAVc67mKa>;Qa%X)Y`OazI3W2+%+?+^ivhyaxv7BT{g+AIhekQ9&!2g5-t{9ot9AFa zhKkv+ik2s}P#r(C9x-K5S&oL45|-WK$}fT@6=a1oKyu4gN*3VgMLmV}E`=|8`(A(j z7C^)G2Ms@)I+kT6*GV5M^1~9rL;x)H<%FC% zfCU6XKr+cFe(Sh~gS`bV8NfYCGP^ zcy0U)I}egc;9_E|aQpUZB|EM5InSE@)H}GIr0|gc`y$9tgC8vUEwB;*nRF`eGbAZ) zLwo>Uk5ZP{RM-}skxdnACSmpuY1AiXALJuAHv1_(UNejd@D;lk$2a%EBdaeL##-bN zb0B|fDn=7(dbr!Iwg%`S-wib#%P(Q##H_@4^INJTRiirHBL_0wmQ&rfce$v1szoOC zc1^6R#o&RDH@tp+#Y8?)Mt}n~CP3#BBs{r%XbzE=?^18%cd6I!x{?14qt!-0tOH~t z{7tfeE5#vUev$~{IqKeaZfAV!Ji%A;l8wGTk?>t~*@MB9;+>Af@c|UsW!p7eK~fD{ z7a2{Kiw=hDF|J2qCzO`=YYxSOIi+;II^cak@)OCFCz#7K%IHBk4z=oFFZ8J7%#v~3 zdSBcZVqN-t{Y8q_cC^VVCQxbW<9+9cz;s_9(UGV9zJL;07 z`dSc|`jORtu!u{!PY;e340ngqzl6;=>}wwZDZC9cyskK~-`dp^! zHWc7w9dMRZlf}47lm3yiFL!HL+GQKPjjyEczXYpJpq_&KII&2rEdMF4XZXGZa59*1 zg6t6`9==E_fKB@)S8=LKf-9TWitE2n_!81w!SwczWBsv1jahM!v_*=N2PjEYkgL4fDlD|z*(u~~XN~38h2Lj1JN%zf}^y@GvdyhC= zcl&+ul%^o#_LVyZ_b0G6eWe3=_L9iz;ah%&0K_68^X3RMP2t*piV(!kA8wZ<+&m^q z6aav}9IL};__~(VTrSbX)O4#DfAx(aXW@3ZzZ1v5ugDiKT{! zEbF`BM#S0sCLy!WH^IcIPas>2-GyxL!b`T$kgMaGnj#;A@ZzMZT*jvWFzU>6mh3>w zb=VMu!-8;v9Eihjc`kzyD6)A}ZD-rqfVC+SiMJ`TUOVwoMM9h?qQjXvB-n(q?(My7) zyVqg7?vF~dMW+!Z={~pbo=fPZJQbf{wV5pHpNAMDu<}NnWW1f@rCg{IfNh0=(SY#V z8DO_(qfh7>gs=5+cn#u?zQCZC^N{{ZT7PdRVU*cW82yx|P;m*I`==n$=T|YkY=~`x4`kpVK=g)hoxXu%J<;fgjq>3kE z*=i`4|9+O6&t{PASxL!jJmsh~C2*)fdMN$tb7k!)yH?LziSHzGg?vWKP0PX_u|%TR zW^aiRl`dLF>afWPhb3~3-3AIX>@GVxWj7iY{@QqAT_-3q>dEHmAHg}w0M;~^An3av zSh6tm)1!Ibv=?*vjv)K!UbwZiHh1T|u3etUWmoS3&u=h51ofDYUt7r{xJPS&bL?3M*C%(UR;#6jFS@;>@z{wWUa+J?NB87$`Tb6h zWa`pg=0-|1oj;A7wbD2wiP2PXuCfh%P#_`3zx?dQO%wx64&Hr}0(7@5c=;G*B+gBO zis78)6=;r_0vedM3aXqH9YR>&dh34skkRwesBKH7J%w!z4Bd82x7$q0I)LD9rayIW>sYxf~r; zJ?RBp%T=M3s zYf%5H_lbKd5gbTM@(Ys|{pMmwlV)@`9!Jv4=UmaM5hOGmqBPAeRmXky2)Xb;4KfKX zK8D~|SsXWPt(~8HpYQy$JRU}uXb1QKSJ4yKw4w!m*?I`h-@1;R{>>kKUGoDiKbL0( zR*Ar|i&c#}Mq$9!2^ZG6hvIdlmL9?ZnfJ|S7H;J4_wi`cP8Q?Qh}4aRswGqhPLzUf zeoWQWY}0&=v~Q6^EVRapyf5VjozUp3BsH!ipdF3P=_nI$xoHbwn=eaAaqq^3)|PX( zZ{+JkExPgLCLrQc^|enG0V%+cIC!`-x?8+Q_iyb#% zIqv-ryny9f^^08L?2RG$Ef}CvPBrR&%b!%-Uuyj~N+pr?T8rU!Z>SD09Me55ph2b% zRl91GUjgs!+7^795O&>n_Gw`LdGzM7;cUZv8>H+^+0gU{LCwLLm!jRw+)c1#FHg*W z7p-%)E>v3$N;D9s6@*k>I@Hy+=lU2})>JxKQtEg}pa-zscQ;Eg^k%xmN`SkrSMj3Cwq;sLQ+yOX+~;~nbtxO0XBQ$egsb|8g6wK$S$1t9d5N>@ zE*v)BNfjFqf=G2#5Fh~b!Snow!p*!zuN97lv`FOD)w`=Ve_O$Dk z8;m1U8E2z0J;XC^*3Nq$Q&|oHjbaz7a7P9tMAkN9`5;CDl8)(?z^CDQbXQB+B%5Ku z8-GLug3C{0(gP>~t)xm9$-$X>ol#fu4`Es~huK!FSBMlcKr(KEE^-a3N`^Wv_}y-P z>tLNfax3CEf;uRao{LL7R(eF&zf3Z517jU43R$yY$r8YO(l|`GdpOdDV8~SziwoxH9j}iHz!;Zg@s; zkWh1&J7(b(yti-otl!V#=PL4XCpG;sXGzv2@L2MIz# zIqB`5j=TjexeDqQj!2 zVS7;KuxoJRJD8)a?lizRe+g9oRVG07=Eo|Y0IZ8xoeHWe?jZsy;!nS;>~EDGMZQbN-2jIYSOQ#4u?*^og|JD|E{XDg$cdakDe@!50TIV;r zVO47FsAfV1^uI^vFS{-H7{>qh$BQy<6cX2{_){buH#htq9cYI2@iVbWr3UZ!=8Jq8 z4&*Ms$pBcXsK6uWK}=`XecML&ub-a3%q@xeVnaN>TrpRxWCuh9E1kY@!%Ev%WaYnLC9S%5U>%P##RGp};nGTs1L|`guls*7ow0_U`Z#gB#}ciI2c=m(*(Yyj z)Lqm(MA}?`xK>lc?>xD;GZDNvrP36dXLAgf15ozy4gb_45tG9V(uexyBb6rk0D{ig z^k-2_)8)3uSRK$|o0uvdvr4~H2uuM#D6%e!Tj*>pD$XII!_?@H^!7wiYu_3N>n^F9 zH2l}tqGiZ_mfDK?4WQeR1B0q}Pt#DvLpB(EHHLI>6Hpv<%Jx|Y_qLVlyW$2<{6(Kr zD}B2uMIxz@&n{39EqIbp!{zobc%2})<|-tO51bvWCqxy|SP}f;1eiA8(_eY>^hzxf z49FAs_mQsZV?k=m1>=mwY0$Nc1RS2M*+2LXc!&8f#^6*eiZ5RGsEq@f2zDo!h?8B8 zl7$31)is2Kn)8(ZQf68Ss-p?W*AY`XIONCm<*;=e$KL877!Uw8D>WYRY!d$a7|Yjy z=A-WM2<=Eb)Zh{tg@00N`@C5R=M!hZiiagUc&#xUyVp}J_$8u8K36 zI)b8KdN5yH`S}P5^R{q%8r$<2yLjmfFSSbOu*x$|h}lYC2+gdrLAr)3MSqhXHt66G zL2mwxV~4piE-QSn6GS)o5fm3_IRf`uHg!Zy#VKHpoi*m1Zb#>VDrZyQ<7~Tc% zR`mefb$b9IYOo(?45X2GXo*Ppy<)OCO{!8Pv|+XN0Wexw@FIC47n$>BdiFIC{=JaL_>0R{5EZ=uQ6M!)`V`mZL9???u-S9sE+tqy_-j4M1@|8= zvnQfDiWV3*DpYe)*HtlUw@?ah;7i9zX?fxZv)21ag43nCuFC6<%?`BlL(~& z{8rzi9|L4mlm;##NU{nfvn<}sb~FkjLjj_n<^AiN4;s6etimf2jSi}6 z@`H*?{MukjHBYgpsqQ*plMNMDQjE|%$6r7Rw~-T?MPChxv%Sox+)5OXvVV)ARU(}< zh7;8;c9d4d?fV^s+XxC5f;$SSDpGS)WSq+hkex)6+6KyZ|kPew?Cghg%wW?Dom!C zm2>|F_SBc-W)i`85HTbTj4nPx9vPaRRu1JaTldk_j!E@6=7d9xVcj_{Nk=XB^pm9W z1aL~6g9Hw*KsiBx!-|(-V!^)+yVcY6ab#;KdKp-Yo3H@d3I>Cklq1<|WU&>=o*$}G z6WC@e?Ym(~S+-iQDqR)X5Ug0-30~~fJ2=Hr6$fO+B`GiR`5tc5R^vcNqn>NA!IPbF z)Iowpv~&L;Xk1yWdh$<*GI>||x+gwrM1>Xy0vd~P#J&})Bu~yF<*~w`k#*H^t!1wYcM6IIP3=!yT(kdT zh&i^3cN}5?#8l1T^jmn)#VXpZHZq32xwzamihVlC&e?BWtUv?E@9bsp1}Ki&e`Tti z4l|w-wuk7~!u=aFi_fA;_k`N*;u-v)EPp;V!pA)B9N3QYpGrU zmNo$tuMLf)T^vP{qz=+%`nKACGNkF^(@R*FIR|76g*#hSC*`RB2Yyf7OtQ+W}WqoOFW)d12qbKiBx;rxw?R9Dd*t z%PiGqjZe=$M$p8o55HFM1R*HEe6<@n7bxIhS2A$lsAaWN7xz&>jRr+Gr5&{>fBa%i zm>&7jQ&TdWgMEV__F7Yd)~{VK_3{js0n*d(#@YQMN+|r)5s*5~}eQaz&w3hm8WWMQWA%8ehrX0gokKh59n(t%*?rA-Z6)`&-m~VsrS4e(46z7`fB$ zt9-PNWTSOjX|VQy8sv;jo1}Sv=t`Lc4V#$C)*vONte}in;Xj5n(JhMj1$=YQHPK)* zbz0B

    #(6zjn(q?{<)uWoShwE|I5@>ZK0w~!aNJH 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

    h937N*EUHbuZ--q3> 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

    8HwO=)EjO47g$M_354Do?ie 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

    4QYb=jj>jSs(Bw31v} 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

    jI-1)%X5x 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 O_$e!>%NNU<`28P@MhtrZ diff --git a/docs/images/visualising-software-architecture.png b/docs/images/visualising-software-architecture.png deleted file mode 100644 index 155f4bfecfe3222b357dff4004cc9307df3f1d79..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 973363 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∓IJ8x-7C3YLuaVF11R7wF-VKFiIZME}F=9!ETNa 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%0Dp2kp{g!lwtS~(2N8K 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 z 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

    FZDdvH0phh?@V|IJ=JNX2aiT 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
    feBw5E(4}zGfws#tkm`WSe94hS!BS?#a{B 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 Ms4EoQH+}v;0NVy=MF0Q* diff --git a/docs/images/websequencediagrams-1.png b/docs/images/websequencediagrams-1.png deleted file mode 100644 index 70c20f385959004cf0d678e14658852ec4b0840f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49694 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 KEv_?IiT?wxF}9)r From a8cb5d89d13557801947d71686fdeb231a64e983 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 22 Nov 2019 15:14:19 +0000 Subject: [PATCH 048/717] Some tweaks to the example. --- .../src/com/structurizr/example/BigBankPlc.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/structurizr-examples/src/com/structurizr/example/BigBankPlc.java b/structurizr-examples/src/com/structurizr/example/BigBankPlc.java index 628d0216c..6c5ef622e 100644 --- a/structurizr-examples/src/com/structurizr/example/BigBankPlc.java +++ b/structurizr-examples/src/com/structurizr/example/BigBankPlc.java @@ -27,7 +27,7 @@ public class BigBankPlc { private static final String DATABASE_TAG = "Database"; private static final String FAILOVER_TAG = "Failover"; - private static Workspace create() { + public static void main(String[] args) throws Exception { Workspace workspace = new Workspace("Big Bank plc", "This is an example workspace to illustrate the key features of Structurizr, based around a fictional online banking system."); Model model = workspace.getModel(); ViewSet views = workspace.getViews(); @@ -70,7 +70,7 @@ private static Workspace create() { mobileApp.addTags(MOBILE_APP_TAG); Container webApplication = internetBankingSystem.addContainer("Web Application", "Delivers the static content and the Internet banking single page application.", "Java and Spring MVC"); Container apiApplication = internetBankingSystem.addContainer("API Application", "Provides Internet banking functionality via a JSON/HTTPS API.", "Java and Spring MVC"); - Container database = internetBankingSystem.addContainer("Database", "Stores user registration information, hashed authentication credentials, access logs, etc.", "Relational Database Schema"); + Container database = internetBankingSystem.addContainer("Database", "Stores user registration information, hashed authentication credentials, access logs, etc.", "Oracle Database Schema"); database.addTags(DATABASE_TAG); customer.uses(webApplication, "Visits bigbank.com/ib using", "HTTPS"); @@ -253,12 +253,8 @@ private static Workspace create() { "Here is some information about the live deployment environment for the Internet Banking System...\n" + "image::embed:LiveDeployment[]"); - return workspace; - } - - public static void main(String[] args) throws Exception { StructurizrClient structurizrClient = new StructurizrClient(API_KEY, API_SECRET); - structurizrClient.putWorkspace(WORKSPACE_ID, create()); + structurizrClient.putWorkspace(WORKSPACE_ID, workspace); } } \ No newline at end of file From 48e9c8812b0a04dd013efcffbaf0e438dc6c9b39 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 23 Nov 2019 07:36:06 +0000 Subject: [PATCH 049/717] Wording tweak. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 19556fb42..2d920a09e 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # Structurizr for Java -This GitHub repository is the official client library for the [Structurizr](https://structurizr.com) cloud service and on-premises installation, both of which are web-based publishing platforms for software architecture models based upon the [C4 model](https://c4model.com). __This repository is supported by Structurizr Limited__, as a part of the Structurizr service. +This GitHub repository is an official client library for the [Structurizr](https://structurizr.com) cloud service and on-premises installation, both of which are web-based publishing platforms for software architecture models based upon the [C4 model](https://c4model.com). __This repository is supported by Structurizr Limited__, as a part of the Structurizr service. ## A quick example From e73cd5b302bd9d5094e59673bb486dd8e2c3c30d Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 25 Nov 2019 17:50:58 +0000 Subject: [PATCH 050/717] Added a link to the extensions project. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 2d920a09e..3d6fb3c48 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ This GitHub repository is an official client library for the [Structurizr](https://structurizr.com) cloud service and on-premises installation, both of which are web-based publishing platforms for software architecture models based upon the [C4 model](https://c4model.com). __This repository is supported by Structurizr Limited__, as a part of the Structurizr service. +The component finder, adr-tools importer, and alternative diagram export formats (e.g. PlantUML) can be found at [Structurizr for Java extensions](https://github.com/structurizr/java-extensions). + ## A quick example As an example, the following Java code can be used to create a software architecture __model__ and an associated __view__ that describes a user using a software system. From bfdb7e4a4f57bfbaa80a2de0b83b5b2d7b254829 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 1 Dec 2019 08:25:17 +1100 Subject: [PATCH 051/717] Made the getting started guide clearer about diagram layout. --- docs/getting-started.md | 8 ++++++-- docs/images/getting-started-1.png | Bin 0 -> 130986 bytes docs/images/getting-started-2.png | Bin 0 -> 133350 bytes docs/images/getting-started.png | Bin 65314 -> 0 bytes 4 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 docs/images/getting-started-1.png create mode 100644 docs/images/getting-started-2.png delete mode 100644 docs/images/getting-started.png diff --git a/docs/getting-started.md b/docs/getting-started.md index e1d9cb010..733f8a0f9 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -81,10 +81,14 @@ structurizrClient.putWorkspace(25441, workspace); Once you've run your program to create and upload the workspace, you can now sign in to your Structurizr account, and open the workspace from [your dashboard](https://structurizr.com/dashboard). The result should be a diagram like this: -![Getting Started with Structurizr for Java](images/getting-started.png) +![Getting Started with Structurizr for Java](images/getting-started-1.png) + +By default, Structurizr does not auto-layout your diagram elements. The diagram layout can be modified by dragging the elements around the diagram canvas in the diagram editor, and the layout saved using the "Save workspace" button. See [Structurizr - Help - Diagram layout](https://structurizr.com/help/diagram-layout) for more information. + +![Getting Started with Structurizr for Java](images/getting-started-2.png) A diagram key is automatically generated based upon the styles in the model. Click the "i" button on the toolbar (or press the 'i' key) to display the diagram key. ![A diagram key](images/getting-started-diagram-key.png) -The diagram layout can be modified by dragging the elements around the diagram canvas, and the layout saved using the "save" button. When you upload a new version of the same workspace, the Structurizr client will try to retain this diagram layout information. See [API client](api-client.md) for more details. \ No newline at end of file +When you upload a new version of the same workspace, the Structurizr client will try to retain the diagram layout information. See [API client](api-client.md) for more details. \ No newline at end of file diff --git a/docs/images/getting-started-1.png b/docs/images/getting-started-1.png new file mode 100644 index 0000000000000000000000000000000000000000..1d57f32f8240be780c645935bfe4cc4647d92635 GIT binary patch literal 130986 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∑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

    aL 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 MeNE*eB}Bmg06*xwf&c&j literal 0 HcmV?d00001 diff --git a/docs/images/getting-started-2.png b/docs/images/getting-started-2.png new file mode 100644 index 0000000000000000000000000000000000000000..43cb96307851fd2621684c21e02ff7bcd39e497d GIT binary patch literal 133350 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 Sia!$Y^Gs6VY4H=ocmE&aG{2Al From c73d0030e6c27137f0f3c5a02880636f67df18d5 Mon Sep 17 00:00:00 2001 From: Karl Brown Date: Tue, 10 Dec 2019 18:31:49 -0500 Subject: [PATCH 052/717] [#125] Change semantics of hasTag to consider requiredTags as well Update removeTag method to return a boolean, similar to HashSet semantics Update tests to demonstrate the new behavior --- .../structurizr/model/ContainerInstance.java | 3 ++- .../src/com/structurizr/model/ModelItem.java | 19 ++++++++++++++++--- .../com/structurizr/model/ModelItemTests.java | 18 ++++++++++++++++++ 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/structurizr-core/src/com/structurizr/model/ContainerInstance.java b/structurizr-core/src/com/structurizr/model/ContainerInstance.java index 59e8d816d..25e72f846 100644 --- a/structurizr-core/src/com/structurizr/model/ContainerInstance.java +++ b/structurizr-core/src/com/structurizr/model/ContainerInstance.java @@ -78,8 +78,9 @@ protected Set getRequiredTags() { } @Override - public void removeTag(String tag) { + public boolean removeTag(String tag) { // do nothing ... tags cannot be removed from container instances (they should reflect the container they are based upon) + return false; } @Override diff --git a/structurizr-core/src/com/structurizr/model/ModelItem.java b/structurizr-core/src/com/structurizr/model/ModelItem.java index 99458d907..48b498244 100644 --- a/structurizr-core/src/com/structurizr/model/ModelItem.java +++ b/structurizr-core/src/com/structurizr/model/ModelItem.java @@ -83,14 +83,27 @@ public void addTags(String... tags) { } } - public void removeTag(String tag) { + /** + * + * @param tag + * @return True if the tag was removed. Will return false if a non-existent tag is passed, or if an attempt is + * made to remove requiredTags, which cannot be removed. + */ + public boolean removeTag(String tag) { if (tag != null) { - this.tags.remove(tag); + return this.tags.remove(tag); } + return false; } + /** + * + * @param tag + * @return True if tag is present as a tag on this item, or if it is one of the + * required tags defined by the model in getRequiredTags() + */ public boolean hasTag(String tag) { - return this.tags.contains(tag); + return getTagsAsSet().contains(tag); } /** diff --git a/structurizr-core/test/unit/com/structurizr/model/ModelItemTests.java b/structurizr-core/test/unit/com/structurizr/model/ModelItemTests.java index b7f5adeff..da1df410c 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ModelItemTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ModelItemTests.java @@ -8,6 +8,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.fail; public class ModelItemTests extends AbstractWorkspaceTestBase { @@ -25,6 +26,13 @@ public void test_getTags_WhenThereAreNoTags() { assertEquals("Element,Software System", element.getTags()); } + @Test + public void test_hasTag_ChecksRequiredTags() { + SoftwareSystem system = model.addSoftwareSystem("Name", "Description"); + assertTrue("hasTag returns true for Software System", system.hasTag("Software System")); + assertTrue("hasTag returns true for Element", system.hasTag("Element")); + } + @Test public void test_getTags_ReturnsTheListOfTags_WhenThereAreSomeTags() { Element element = model.addSoftwareSystem("Name", "Description"); @@ -63,6 +71,16 @@ public void test_addTags_AddsTags_WhenPassedSomeTagsAndThereAreDuplicateTags() { assertEquals("Element,Software System,tag1,tag2", element.getTags()); } + @Test + public void test_removeTags() { + Element element = model.addSoftwareSystem("Name", "Description"); + element.addTags("tag1", "tag2"); + assertTrue("Remove an existing tag returns true", element.removeTag("tag1")); + assertFalse("Tag has been removed", element.hasTag("tag1")); + + assertFalse("Remove a non-existing tag returns false", element.removeTag("no-such-tag")); + assertFalse("Remove a required tag returns false", element.removeTag("Element")); + } @Test public void test_getProperties_ReturnsAnEmptyList_WhenNoPropertiesHaveBeenAdded() { Element element = model.addSoftwareSystem("Name", "Description"); From 32f06066dd7997ec3db2e0a4c28f7510fd590ec3 Mon Sep 17 00:00:00 2001 From: Karl Brown Date: Tue, 10 Dec 2019 19:44:01 -0500 Subject: [PATCH 053/717] [#127] * Change the model.addImplicitRelationships() method to set up implicit relationships with InteractionStyle.Asynchronous if any of the lower-level relationships are asynchronous; otherwise, default to synchronous * Update test cases * Reformat some or the other test cases around implicit relationships to make a bit more clear --- .../src/com/structurizr/model/Model.java | 16 ++++++- .../com/structurizr/model/ModelTests.java | 46 +++++++++++++++++-- 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/structurizr-core/src/com/structurizr/model/Model.java b/structurizr-core/src/com/structurizr/model/Model.java index 1e304c56f..628cc9852 100644 --- a/structurizr-core/src/com/structurizr/model/Model.java +++ b/structurizr-core/src/com/structurizr/model/Model.java @@ -537,6 +537,7 @@ public Set addImplicitRelationships() { String descriptionKey = "D"; String technologyKey = "T"; + String interactionStyleKey = "I"; Map>>> candidateRelationships = new HashMap<>(); for (Relationship relationship : getRelationships()) { @@ -556,6 +557,7 @@ public Set addImplicitRelationships() { candidateRelationships.get(source).put(destination, new HashMap<>()); candidateRelationships.get(source).get(destination).put(descriptionKey, new HashSet<>()); candidateRelationships.get(source).get(destination).put(technologyKey, new HashSet<>()); + candidateRelationships.get(source).get(destination).put(interactionStyleKey, new HashSet<>()); } if (relationship.getDescription() != null) { @@ -565,6 +567,10 @@ public Set addImplicitRelationships() { if (relationship.getTechnology() != null) { candidateRelationships.get(source).get(destination).get(technologyKey).add(relationship.getTechnology()); } + + if (relationship.getInteractionStyle() != null) { + candidateRelationships.get(source).get(destination).get(interactionStyleKey).add(relationship.getInteractionStyle().toString()); + } } } @@ -580,6 +586,7 @@ public Set addImplicitRelationships() { for (Element destination : candidateRelationships.get(source).keySet()) { Set possibleDescriptions = candidateRelationships.get(source).get(destination).get(descriptionKey); Set possibleTechnologies = candidateRelationships.get(source).get(destination).get(technologyKey); + Set possibleInteractionStyles = candidateRelationships.get(source).get(destination).get(interactionStyleKey); String description = ""; if (possibleDescriptions.size() == 1) { @@ -591,8 +598,13 @@ public Set addImplicitRelationships() { technology = possibleTechnologies.iterator().next(); } - // todo ... this defaults to being a synchronous relationship - Relationship implicitRelationship = addRelationship(source, destination, description, technology, InteractionStyle.Synchronous); + InteractionStyle interactionStyle = InteractionStyle.Synchronous; + // If any async relationships are present, mark the relationship as asynchronous + if (possibleInteractionStyles.contains(InteractionStyle.Asynchronous.toString())) { + interactionStyle = InteractionStyle.Asynchronous; + } + + Relationship implicitRelationship = addRelationship(source, destination, description, technology, interactionStyle); if (implicitRelationship != null) { implicitRelationships.add(implicitRelationship); } diff --git a/structurizr-core/test/unit/com/structurizr/model/ModelTests.java b/structurizr-core/test/unit/com/structurizr/model/ModelTests.java index 454df337b..de8354679 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ModelTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ModelTests.java @@ -357,12 +357,15 @@ public void test_addImplicitRelationships_WhenSourceAndDestinationAreComponentsI Set implicitRelationships = model.addImplicitRelationships(); assertEquals(9, model.getRelationships().size()); assertEquals(8, implicitRelationships.size()); + assertTrue(aaa.hasEfferentRelationshipWith(bb)); + assertTrue(aaa.hasEfferentRelationshipWith(b)); + assertTrue(aa.hasEfferentRelationshipWith(bbb)); assertTrue(aa.hasEfferentRelationshipWith(bb)); - assertTrue(aaa.hasEfferentRelationshipWith(b)); - assertTrue(a.hasEfferentRelationshipWith(bbb)); assertTrue(aa.hasEfferentRelationshipWith(b)); + + assertTrue(a.hasEfferentRelationshipWith(bbb)); assertTrue(a.hasEfferentRelationshipWith(bb)); assertTrue(a.hasEfferentRelationshipWith(b)); } @@ -384,9 +387,12 @@ public void test_addImplicitRelationships_WhenSourceIsAComponentAndDestinationIs Set implicitRelationships = model.addImplicitRelationships(); assertEquals(6, model.getRelationships().size()); assertEquals(5, implicitRelationships.size()); - assertTrue(aa.hasEfferentRelationshipWith(bb)); + assertTrue(aaa.hasEfferentRelationshipWith(b)); + + assertTrue(aa.hasEfferentRelationshipWith(bb)); assertTrue(aa.hasEfferentRelationshipWith(b)); + assertTrue(a.hasEfferentRelationshipWith(bb)); assertTrue(a.hasEfferentRelationshipWith(b)); } @@ -805,6 +811,40 @@ public void test_addImplicitRelationships_DoeNotSetTheTechnologyOfThePropagatedR assertEquals("", relationship.getTechnology()); } + @Test + public void test_addImplicitRelationships_PropagatesAsyncRelationship_IfThereAreAMixOfInteractionStyles() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); + SoftwareSystem softwareSystemExternal = model.addSoftwareSystem("External system", ""); + Container service1 = softwareSystem.addContainer("Service1", "", "Technology1"); + Container service2 = softwareSystem.addContainer("Service2", "", "Technology2"); + + softwareSystemExternal.uses(service1, "Sends an event to", "AMQP", InteractionStyle.Asynchronous); + softwareSystemExternal.uses(service2, "Sends a command to", "HTTPS", InteractionStyle.Synchronous); + + model.addImplicitRelationships(); + assertEquals(3, softwareSystemExternal.getRelationships().size()); + Relationship relationship = softwareSystemExternal.getRelationships().stream().filter(r -> r.getDestination() == + softwareSystem).findFirst().get(); + assertEquals(InteractionStyle.Asynchronous, relationship.getInteractionStyle()); + } + + @Test + public void test_addImplicitRelationships_PropagatesAsyncRelationship_IfThereAreOnlyAsyncInteractionStyles() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); + SoftwareSystem softwareSystemExternal = model.addSoftwareSystem("External system", ""); + Container service1 = softwareSystem.addContainer("Service1", "", "Technology1"); + Container service2 = softwareSystem.addContainer("Service2", "", "Technology2"); + + softwareSystemExternal.uses(service1, "Sends an event to", "AMQP", InteractionStyle.Asynchronous); + softwareSystemExternal.uses(service2, "Sends an event to", "Kafka", InteractionStyle.Asynchronous); + + model.addImplicitRelationships(); + assertEquals(3, softwareSystemExternal.getRelationships().size()); + Relationship relationship = softwareSystemExternal.getRelationships().stream().filter(r -> r.getDestination() == + softwareSystem).findFirst().get(); + assertEquals(InteractionStyle.Asynchronous, relationship.getInteractionStyle()); + } + @Test public void test_addDeploymentNode_ThrowsAnException_WhenADeploymentNodeWithTheSameNameAlreadyExists() { model.addDeploymentNode("Amazon AWS", "Description", "Technology"); From 31467dbb608be9ab6f73027e682e8b8d722e0eb1 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 20 Dec 2019 11:51:43 +0000 Subject: [PATCH 054/717] Propagate all exceptions. --- structurizr-core/src/com/structurizr/Workspace.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/structurizr-core/src/com/structurizr/Workspace.java b/structurizr-core/src/com/structurizr/Workspace.java index 2eb458636..ad883194f 100644 --- a/structurizr-core/src/com/structurizr/Workspace.java +++ b/structurizr-core/src/com/structurizr/Workspace.java @@ -114,6 +114,8 @@ private void hydrateModel() { } catch (InvocationTargetException ite) { if (ite.getCause() != null && ite.getCause() instanceof WorkspaceValidationException) { throw (WorkspaceValidationException)ite.getCause(); + } else { + throw new RuntimeException(ite.getCause()); } } catch (Exception e) { throw new RuntimeException(e); @@ -128,6 +130,8 @@ private void hydrateViewSet() { } catch (InvocationTargetException ite) { if (ite.getCause() != null && ite.getCause() instanceof WorkspaceValidationException) { throw (WorkspaceValidationException)ite.getCause(); + } else { + throw new RuntimeException(ite.getCause()); } } catch (Exception e) { throw new RuntimeException(e); @@ -142,6 +146,8 @@ private void hydrateDocumentation() { } catch (InvocationTargetException ite) { if (ite.getCause() != null && ite.getCause() instanceof WorkspaceValidationException) { throw (WorkspaceValidationException)ite.getCause(); + } else { + throw new RuntimeException(ite.getCause()); } } catch (Exception e) { throw new RuntimeException(e); From bdd8bb22a3a0ed20f2b3228fa39116bbaeb0a458 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 20 Dec 2019 12:12:07 +0000 Subject: [PATCH 055/717] Ignore the softwareSystemId property when serializing component views ... it's unnecessary since the view is already associated with a container (which itself belongs to a software system). --- .../src/com/structurizr/view/ComponentView.java | 6 ++++++ .../src/com/structurizr/view/ViewSet.java | 13 ++----------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/structurizr-core/src/com/structurizr/view/ComponentView.java b/structurizr-core/src/com/structurizr/view/ComponentView.java index 519176ed2..0b2418903 100644 --- a/structurizr-core/src/com/structurizr/view/ComponentView.java +++ b/structurizr-core/src/com/structurizr/view/ComponentView.java @@ -24,6 +24,12 @@ public final class ComponentView extends StaticView { this.container = container; } + @JsonIgnore + @Override + public String getSoftwareSystemId() { + return super.getSoftwareSystemId(); + } + /** * Gets the ID of the container associated with this view. * diff --git a/structurizr-core/src/com/structurizr/view/ViewSet.java b/structurizr-core/src/com/structurizr/view/ViewSet.java index 2ce0314e7..ab4dc0805 100644 --- a/structurizr-core/src/com/structurizr/view/ViewSet.java +++ b/structurizr-core/src/com/structurizr/view/ViewSet.java @@ -471,17 +471,7 @@ void hydrate(Model model) { } for (ComponentView view : componentViews) { - SoftwareSystem softwareSystem = model.getSoftwareSystemWithId(view.getSoftwareSystemId()); - if (softwareSystem == null) { - throw new WorkspaceValidationException( - String.format("The component view with key \"%s\" is associated with a software system (id=%s), but that element does not exist in the model.", - view.getKey(), view.getSoftwareSystemId()) - ); - } - - view.setSoftwareSystem(softwareSystem); - - Container container = softwareSystem.getContainerWithId(view.getContainerId()); + Container container = (Container)model.getElement(view.getContainerId()); if (container == null) { throw new WorkspaceValidationException( String.format("The component view with key \"%s\" is associated with a container (id=%s), but that element does not exist in the model.", @@ -490,6 +480,7 @@ void hydrate(Model model) { } view.setContainer(container); + view.setSoftwareSystem(container.getSoftwareSystem()); hydrateView(view); } From 28199bd2ced9558fe24240432ec3dcb84888f71c Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 20 Dec 2019 12:14:27 +0000 Subject: [PATCH 056/717] Ignore the softwareSystemId property when serializing component views ... it's unnecessary since the view is already associated with a container (which itself belongs to a software system). --- .../api/WorkspaceRulesValidationTests.java | 10 ------ ...ithComponentViewIsMissingFromTheModel.json | 33 ------------------- 2 files changed, 43 deletions(-) delete mode 100644 structurizr-client/test/integration/workspaceValidation/SoftwareSystemAssociatedWithComponentViewIsMissingFromTheModel.json diff --git a/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java b/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java index 7773f698f..d751a11bd 100644 --- a/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java +++ b/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java @@ -129,16 +129,6 @@ public void test_exceptionThrown_WhenSoftwareSystemAssociatedWithContainerViewIs } } - @Test - public void test_exceptionThrown_WhenSoftwareSystemAssociatedWithComponentViewIsMissingFromTheModel() throws Exception { - try { - WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "SoftwareSystemAssociatedWithComponentViewIsMissingFromTheModel.json")); - fail(); - } catch (WorkspaceValidationException we) { - assertEquals("The component view with key \"Components\" is associated with a software system (id=3), but that element does not exist in the model.", we.getMessage()); - } - } - @Test public void test_exceptionThrown_WhenContainerAssociatedWithComponentViewIsMissingFromTheModel() throws Exception { try { diff --git a/structurizr-client/test/integration/workspaceValidation/SoftwareSystemAssociatedWithComponentViewIsMissingFromTheModel.json b/structurizr-client/test/integration/workspaceValidation/SoftwareSystemAssociatedWithComponentViewIsMissingFromTheModel.json deleted file mode 100644 index 30d75416f..000000000 --- a/structurizr-client/test/integration/workspaceValidation/SoftwareSystemAssociatedWithComponentViewIsMissingFromTheModel.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "id" : 0, - "name" : "Name", - "description" : "Description", - "configuration" : { }, - "model" : { - "softwareSystems" : [ { - "id" : "1", - "tags" : "Element,Software System", - "name" : "Software System", - "location" : "Unspecified", - "containers" : [ { - "id" : "2", - "tags" : "Element,Container", - "name" : "Container" - } ] - } ] - }, - "documentation" : { }, - "views" : { - "componentViews" : [ { - "softwareSystemId" : "3", - "description" : "Description", - "key" : "Components", - "containerId" : "2" - } ], - "configuration" : { - "branding" : { }, - "styles" : { }, - "terminology" : { } - } - } -} \ No newline at end of file From 244614f9c9f68cfa1d69665509caef4e6ed8ba71 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 24 Dec 2019 15:06:52 +0000 Subject: [PATCH 057/717] Fixes a deserialization issue with component views. --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index f150a8369..a5681cd50 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.3.2' + version = '1.3.3' repositories { mavenCentral() From 5b16167ccbd52c05bcdfbf971239dfe8aac286fa Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 24 Dec 2019 15:13:34 +0000 Subject: [PATCH 058/717] Updated docs to reflect new version. --- docs/binaries.md | 4 ++-- docs/changelog.md | 4 ++++ docs/getting-started.md | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/binaries.md b/docs/binaries.md index d22d25ebb..cf55ea1d4 100644 --- a/docs/binaries.md +++ b/docs/binaries.md @@ -3,5 +3,5 @@ The "Structurizr for Java" binaries are hosted on [Maven Central](https://repo1. Name | Description ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-core:1.3.2 | The core library that can used to create software architecture models. -com.structurizr:structurizr-client:1.3.2 | The API client for publishing models on the Structurizr cloud service and on-premises installation. \ No newline at end of file +com.structurizr:structurizr-core:1.3.3 | The core library that can used to create software architecture models. +com.structurizr:structurizr-client:1.3.3 | The API client for publishing models on the Structurizr cloud service and on-premises installation. \ No newline at end of file diff --git a/docs/changelog.md b/docs/changelog.md index 89ddabd62..bd8fe5be4 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.3.3 (24th December 2019) + +- Fixes a deserialization issue with component views. + ## 1.3.2 (22nd November 2019) - Added support for element stroke colours. diff --git a/docs/getting-started.md b/docs/getting-started.md index 733f8a0f9..2a2541799 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -12,7 +12,7 @@ The Structurizr for Java binaries are hosted on [Maven Central](https://repo1.ma Name | Description ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-client:1.3.2 | The Structurizr API client library. +com.structurizr:structurizr-client:1.3.3 | The Structurizr API client library. ## 2. Create a Java program From c2ebfb0b5f65219bdfdae11bc4f95efba5fd0509 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 24 Dec 2019 15:42:30 +0000 Subject: [PATCH 059/717] Split View.setAutomaticLayout(boolean) to enableAutomaticLayout() and disableAutomaticLayout() (breaking change). --- docs/changelog.md | 4 ++++ .../src/com/structurizr/view/View.java | 21 ++++++++++--------- .../unit/com/structurizr/view/ViewTests.java | 14 ++++++------- 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index bd8fe5be4..1b06f4a06 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.3.4 (unreleased) + +- Split View.setAutomaticLayout(boolean) to enableAutomaticLayout() and disableAutomaticLayout() (__breaking change__). + ## 1.3.3 (24th December 2019) - Fixes a deserialization issue with component views. diff --git a/structurizr-core/src/com/structurizr/view/View.java b/structurizr-core/src/com/structurizr/view/View.java index baadff42f..984af725f 100644 --- a/structurizr-core/src/com/structurizr/view/View.java +++ b/structurizr-core/src/com/structurizr/view/View.java @@ -147,16 +147,10 @@ void setAutomaticLayout(AutomaticLayout automaticLayout) { } /** - * Enables the automatic layout for this view, with some default settings. - * - * @param enable a boolean + * Enables automatic layout for this view, with some default settings. */ - public void setAutomaticLayout(boolean enable) { - if (enable) { - this.setAutomaticLayout(AutomaticLayout.RankDirection.TopBottom, 300, 600, 200, false); - } else { - this.automaticLayout = null; - } + public void enableAutomaticLayout() { + enableAutomaticLayout(AutomaticLayout.RankDirection.TopBottom, 300, 600, 200, false); } /** @@ -168,10 +162,17 @@ public void setAutomaticLayout(boolean enable) { * @param edgeSeparation the separation between edges (in pixels, a positive integer) * @param vertices whether vertices should be created during automatic layout */ - public void setAutomaticLayout(AutomaticLayout.RankDirection rankDirection, int rankSeparation, int nodeSeparation, int edgeSeparation, boolean vertices) { + public void enableAutomaticLayout(AutomaticLayout.RankDirection rankDirection, int rankSeparation, int nodeSeparation, int edgeSeparation, boolean vertices) { this.automaticLayout = new AutomaticLayout(rankDirection, rankSeparation, nodeSeparation, edgeSeparation, vertices); } + /** + * Disables automatic layout for this view. + */ + public void disableAutomaticLayout() { + this.automaticLayout = null; + } + /** * Gets the title of this view, if one has been set. * diff --git a/structurizr-core/test/unit/com/structurizr/view/ViewTests.java b/structurizr-core/test/unit/com/structurizr/view/ViewTests.java index c38d29b46..e3942bd9c 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ViewTests.java @@ -397,9 +397,9 @@ public void test_addElement_ThrowsAnException_WhenTheSpecifiedElementDoesNotExis } @Test - public void test_setAutomaticLayout_EnablesAutoLayoutWithSomeDefaultValues_WhenTrueIsSpecified() { + public void test_enableAutomaticLayout_EnablesAutoLayoutWithSomeDefaultValues_WhenTrueIsSpecified() { SystemLandscapeView view = new Workspace("", "").getViews().createSystemLandscapeView("key", "Description"); - view.setAutomaticLayout(true); + view.enableAutomaticLayout(); assertNotNull(view.getAutomaticLayout()); assertEquals(AutomaticLayout.RankDirection.TopBottom, view.getAutomaticLayout().getRankDirection()); @@ -410,19 +410,19 @@ public void test_setAutomaticLayout_EnablesAutoLayoutWithSomeDefaultValues_WhenT } @Test - public void test_setAutomaticLayout_DisablesAutoLayout_WhenFalseIsSpecified() { + public void test_enableAutomaticLayout_DisablesAutoLayout_WhenFalseIsSpecified() { SystemLandscapeView view = new Workspace("", "").getViews().createSystemLandscapeView("key", "Description"); - view.setAutomaticLayout(true); + view.enableAutomaticLayout(); assertNotNull(view.getAutomaticLayout()); - view.setAutomaticLayout(false); + view.disableAutomaticLayout(); assertNull(view.getAutomaticLayout()); } @Test - public void test_setAutomaticLayout() { + public void test_enableAutomaticLayout() { SystemLandscapeView view = new Workspace("", "").getViews().createSystemLandscapeView("key", "Description"); - view.setAutomaticLayout(AutomaticLayout.RankDirection.LeftRight, 100, 200, 300, true); + view.enableAutomaticLayout(AutomaticLayout.RankDirection.LeftRight, 100, 200, 300, true); assertNotNull(view.getAutomaticLayout()); assertEquals(AutomaticLayout.RankDirection.LeftRight, view.getAutomaticLayout().getRankDirection()); From 80075878f3b092c53186e66d0edc436b7605bc7e Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 31 Dec 2019 12:36:27 +0000 Subject: [PATCH 060/717] Added A1 and A0 paper sizes. --- build.gradle | 2 +- docs/changelog.md | 4 +++ .../src/com/structurizr/view/PaperSize.java | 27 +++++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index a5681cd50..141b38be0 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.3.3' + version = '1.3.4' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 1b06f4a06..62313b444 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -4,6 +4,10 @@ - Split View.setAutomaticLayout(boolean) to enableAutomaticLayout() and disableAutomaticLayout() (__breaking change__). +## 1.3.4 (unreleased) + +- Added A1 and A0 paper sizes. + ## 1.3.3 (24th December 2019) - Fixes a deserialization issue with component views. diff --git a/structurizr-core/src/com/structurizr/view/PaperSize.java b/structurizr-core/src/com/structurizr/view/PaperSize.java index 5ac705eab..6fb7cf35d 100644 --- a/structurizr-core/src/com/structurizr/view/PaperSize.java +++ b/structurizr-core/src/com/structurizr/view/PaperSize.java @@ -1,5 +1,8 @@ package com.structurizr.view; +import java.util.ArrayList; +import java.util.List; + /** * These represent paper sizes in pixels at 300dpi. */ @@ -20,6 +23,12 @@ public enum PaperSize { A2_Portrait("A2", Orientation.Portrait, 4961, 7016), A2_Landscape("A2", Orientation.Landscape, 7016, 4961), + A1_Portrait("A1", Orientation.Portrait, 7016, 9933), + A1_Landscape("A1", Orientation.Landscape, 9933, 7016), + + A0_Portrait("A0", Orientation.Portrait, 9933, 14043), + A0_Landscape("A0", Orientation.Landscape, 14043, 9933), + Letter_Portrait("Letter", Orientation.Portrait, 2550, 3300), Letter_Landscape("Letter", Orientation.Landscape, 3300, 2550), @@ -62,4 +71,22 @@ enum Orientation { Landscape } + public static final List getOrderedPaperSizes() { + List paperSizes = new ArrayList<>(); + + for (PaperSize paperSize : values()) { + if (paperSize.getOrientation() == Orientation.Landscape) { + paperSizes.add(paperSize); + } + } + + for (PaperSize paperSize : values()) { + if (paperSize.getOrientation() == Orientation.Portrait) { + paperSizes.add(paperSize); + } + } + + return paperSizes; + } + } From 664a98881539427ec59b46cb2938136332d92ddc Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 5 Jan 2020 13:29:34 +0000 Subject: [PATCH 061/717] Adds support for themes. --- docs/changelog.md | 4 +-- .../com/structurizr/view/Configuration.java | 26 ++++++++++++++ .../structurizr/view/ConfigurationTests.java | 36 ++++++++++++++++++- 3 files changed, 62 insertions(+), 4 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 62313b444..cebc17dc5 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -3,10 +3,8 @@ ## 1.3.4 (unreleased) - Split View.setAutomaticLayout(boolean) to enableAutomaticLayout() and disableAutomaticLayout() (__breaking change__). - -## 1.3.4 (unreleased) - - Added A1 and A0 paper sizes. +- Adds support for themes. ## 1.3.3 (24th December 2019) diff --git a/structurizr-core/src/com/structurizr/view/Configuration.java b/structurizr-core/src/com/structurizr/view/Configuration.java index c14c59c94..39955888e 100644 --- a/structurizr-core/src/com/structurizr/view/Configuration.java +++ b/structurizr-core/src/com/structurizr/view/Configuration.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.annotation.JsonGetter; import com.fasterxml.jackson.annotation.JsonSetter; +import com.structurizr.util.Url; /** * Configuration associated with how information in the workspace is rendered. @@ -10,6 +11,7 @@ public final class Configuration { private Branding branding = new Branding(); private Styles styles = new Styles(); + private String theme; private Terminology terminology = new Terminology(); private String defaultView; @@ -25,6 +27,30 @@ public Styles getStyles() { return styles; } + /** + * Gets the URL of the theme used to render views. + * + * @return the URL of the theme + */ + public String getTheme() { + return theme; + } + + /** + * Sets the theme used to render views. + * + * @param url the URL of theme + */ + public void setTheme(String url) { + if (url != null && url.trim().length() > 0) { + if (Url.isUrl(url)) { + this.theme = url.trim(); + } else { + throw new IllegalArgumentException(url + " is not a valid URL."); + } + } + } + /** * Gets the key of the view that should be shown by default. * diff --git a/structurizr-core/test/unit/com/structurizr/view/ConfigurationTests.java b/structurizr-core/test/unit/com/structurizr/view/ConfigurationTests.java index 1324a7a35..c7e0ac2aa 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ConfigurationTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ConfigurationTests.java @@ -33,4 +33,38 @@ public void test_copyConfigurationFrom() { assertEquals("someKey", destination.getLastSavedView()); } -} + @Test + public void test_setTheme_WithAUrl() { + Configuration configuration = new Configuration(); + configuration.setTheme("https://example.com/theme.json"); + assertEquals("https://example.com/theme.json", configuration.getTheme()); + } + + @Test + public void test_setTheme_WithAUrlThatHasATrailingSpace() { + Configuration configuration = new Configuration(); + configuration.setTheme("https://example.com/theme.json "); + assertEquals("https://example.com/theme.json", configuration.getTheme()); + } + + @Test(expected = IllegalArgumentException.class) + public void test_setTheme_ThrowsAnIllegalArgumentException_WhenAnInvalidUrlIsSpecified() { + Configuration configuration = new Configuration(); + configuration.setTheme("htt://blah"); + } + + @Test + public void test_setTheme_DoesNothing_WhenANullUrlIsSpecified() { + Configuration configuration = new Configuration(); + configuration.setTheme(null); + assertNull(configuration.getTheme()); + } + + @Test + public void test_setTheme_DoesNothing_WhenAnEmptyUrlIsSpecified() { + Configuration configuration = new Configuration(); + configuration.setTheme(" "); + assertNull(configuration.getTheme()); + } + +} \ No newline at end of file From 923e2945e4a222daf73ca51265ae7633b6c97308 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 5 Jan 2020 15:04:38 +0000 Subject: [PATCH 062/717] Added an example theme JSON file. --- .../src/com/structurizr/example/theme/theme.json | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 structurizr-examples/src/com/structurizr/example/theme/theme.json diff --git a/structurizr-examples/src/com/structurizr/example/theme/theme.json b/structurizr-examples/src/com/structurizr/example/theme/theme.json new file mode 100644 index 000000000..00cd74ec4 --- /dev/null +++ b/structurizr-examples/src/com/structurizr/example/theme/theme.json @@ -0,0 +1,16 @@ +{ + "elements": [ + { + "tag": "Software System", + "background": "#1168bd", + "color": "#ffffff" + }, + { + "tag": "Person", + "background": "#08427b", + "color": "#ffffff", + "shape": "Person" + } + ], + "relationships": [] +} \ No newline at end of file From 4404b72a7283f113cb03363d581e8460711e0fd8 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 5 Jan 2020 15:11:49 +0000 Subject: [PATCH 063/717] Added an example of how to use an external theme. --- .../src/com/structurizr/example/Theme.java | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 structurizr-examples/src/com/structurizr/example/Theme.java diff --git a/structurizr-examples/src/com/structurizr/example/Theme.java b/structurizr-examples/src/com/structurizr/example/Theme.java new file mode 100644 index 000000000..cbdb502b0 --- /dev/null +++ b/structurizr-examples/src/com/structurizr/example/Theme.java @@ -0,0 +1,42 @@ +package com.structurizr.example; + +import com.structurizr.Workspace; +import com.structurizr.api.StructurizrClient; +import com.structurizr.model.Model; +import com.structurizr.model.Person; +import com.structurizr.model.SoftwareSystem; +import com.structurizr.view.SystemContextView; +import com.structurizr.view.ViewSet; + +/** + * This is an example of how to use an external theme. + * + * The live workspace is available to view at https://structurizr.com/share/38898 + */ +public class Theme { + + private static final long WORKSPACE_ID = 38898; + private static final String API_KEY = ""; + private static final String API_SECRET = ""; + + public static void main(String[] args) throws Exception { + Workspace workspace = new Workspace("Theme", "This is a model of my software system."); + Model model = workspace.getModel(); + + Person user = model.addPerson("User", "A user of my software system."); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "My software system."); + user.uses(softwareSystem, "Uses"); + + ViewSet views = workspace.getViews(); + SystemContextView contextView = views.createSystemContextView(softwareSystem, "SystemContext", "An example of a System Context diagram."); + contextView.addAllSoftwareSystems(); + contextView.addAllPeople(); + + // add a theme + views.getConfiguration().setTheme("https://raw.githubusercontent.com/structurizr/java/master/structurizr-examples/src/com/structurizr/example/theme/theme.json"); + + StructurizrClient structurizrClient = new StructurizrClient(API_KEY, API_SECRET); + structurizrClient.putWorkspace(WORKSPACE_ID, workspace); + } + +} \ No newline at end of file From 5212e89f7754be678b7735f7ba07bc92c27c16d8 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 6 Jan 2020 13:00:16 +0000 Subject: [PATCH 064/717] Change behaviour of ContainerInstance.getName() so that "null" isn't returned in error messages when validating the model. --- .../src/com/structurizr/model/ContainerInstance.java | 2 +- .../unit/com/structurizr/model/ContainerInstanceTests.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/structurizr-core/src/com/structurizr/model/ContainerInstance.java b/structurizr-core/src/com/structurizr/model/ContainerInstance.java index 59e8d816d..56bb8b111 100644 --- a/structurizr-core/src/com/structurizr/model/ContainerInstance.java +++ b/structurizr-core/src/com/structurizr/model/ContainerInstance.java @@ -97,7 +97,7 @@ public Element getParent() { @Override @JsonIgnore public String getName() { - return null; + return container.getName() + " (container instance)"; } @Override diff --git a/structurizr-core/test/unit/com/structurizr/model/ContainerInstanceTests.java b/structurizr-core/test/unit/com/structurizr/model/ContainerInstanceTests.java index dbd62f239..d5a80c574 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ContainerInstanceTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ContainerInstanceTests.java @@ -34,10 +34,10 @@ public void test_getContainerId() { public void test_getName() { ContainerInstance containerInstance = deploymentNode.add(database); - assertNull(containerInstance.getName()); + assertEquals("Database Schema (container instance)", containerInstance.getName()); containerInstance.setName("foo"); - assertNull(containerInstance.getName()); + assertEquals("Database Schema (container instance)", containerInstance.getName()); } @Test From 0b722f4a72dabe568d99a45dd2e304aeb8ea57bb Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 9 Jan 2020 13:33:39 +0000 Subject: [PATCH 065/717] Added a ThemeUtils class to export element/relationship styles from an existing workspace as a theme. --- .../src/com/structurizr/util/ThemeUtils.java | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 structurizr-client/src/com/structurizr/util/ThemeUtils.java diff --git a/structurizr-client/src/com/structurizr/util/ThemeUtils.java b/structurizr-client/src/com/structurizr/util/ThemeUtils.java new file mode 100644 index 000000000..fc5afb17c --- /dev/null +++ b/structurizr-client/src/com/structurizr/util/ThemeUtils.java @@ -0,0 +1,69 @@ +package com.structurizr.util; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.structurizr.Workspace; +import com.structurizr.io.WorkspaceWriterException; + +import java.io.*; +import java.nio.charset.StandardCharsets; + +/** + * Some utility methods for exporting themes to JSON. + */ +public final class ThemeUtils { + + /** + * Serializes the theme (element and relationship styles) in the specified workspace to a file, as a JSON string. + * + * @param workspace a Workspace object + * @param file a File representing the JSON definition + * @throws Exception if something goes wrong + */ + public static void toJson(Workspace workspace, File file) throws Exception { + if (workspace == null) { + throw new IllegalArgumentException("A workspace must be provided."); + } else if (file == null) { + throw new IllegalArgumentException("The path to a file must be specified."); + } + + OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8); + write(workspace, writer); + } + + /** + * Serializes the theme (element and relationship styles) in the specified workspace to a JSON string. + * + * @param workspace a Workspace instance + * @return a JSON string + * @throws Exception if something goes wrong + */ + public static String toJson(Workspace workspace) throws Exception { + if (workspace == null) { + throw new IllegalArgumentException("A workspace must be provided."); + } + + StringWriter writer = new StringWriter(); + write(workspace, writer); + + return writer.toString(); + } + + private static void write(Workspace workspace, Writer writer) throws Exception { + try { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.enable(SerializationFeature.INDENT_OUTPUT); + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); + + writer.write(objectMapper.writeValueAsString(workspace.getViews().getConfiguration().getStyles())); + } catch (IOException ioe) { + throw new WorkspaceWriterException("Could not write the theme as JSON", ioe); + } + + writer.flush(); + writer.close(); + } + +} \ No newline at end of file From 79805e53e3f1aed909cdcc12c6720485d56897a9 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 9 Jan 2020 15:27:18 +0000 Subject: [PATCH 066/717] Fixes failing javadoc task. --- .../src/com/structurizr/model/ModelItem.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/structurizr-core/src/com/structurizr/model/ModelItem.java b/structurizr-core/src/com/structurizr/model/ModelItem.java index 48b498244..0ad9d3039 100644 --- a/structurizr-core/src/com/structurizr/model/ModelItem.java +++ b/structurizr-core/src/com/structurizr/model/ModelItem.java @@ -84,10 +84,11 @@ public void addTags(String... tags) { } /** + * Removes the given tag. * - * @param tag - * @return True if the tag was removed. Will return false if a non-existent tag is passed, or if an attempt is - * made to remove requiredTags, which cannot be removed. + * @param tag the tag to remove + * @return true if the tag was removed; will return false if a non-existent tag is passed, or if an attempt is + * made to remove required tags, which cannot be removed. */ public boolean removeTag(String tag) { if (tag != null) { @@ -97,10 +98,11 @@ public boolean removeTag(String tag) { } /** + * Determines whether this model item has the given tag. * - * @param tag - * @return True if tag is present as a tag on this item, or if it is one of the - * required tags defined by the model in getRequiredTags() + * @param tag the tag to check for + * @return true if tag is present as a tag on this item, or if it is one of the + * required tags defined by the model in getRequiredTags(), false otherwise */ public boolean hasTag(String tag) { return getTagsAsSet().contains(tag); From c2f6eb49aa2b81dbad4f976ab6f3a9bb9a6bd176 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 11 Jan 2020 13:01:39 +0000 Subject: [PATCH 067/717] Support for tags (and therefore, some degree of styling) on deployment nodes. --- .../src/com/structurizr/model/DeploymentNode.java | 13 ++----------- .../com/structurizr/model/DeploymentNodeTests.java | 7 +++++-- .../src/com/structurizr/example/BigBankPlc.java | 6 ++++-- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/structurizr-core/src/com/structurizr/model/DeploymentNode.java b/structurizr-core/src/com/structurizr/model/DeploymentNode.java index aadbb9f2d..ecbb0cd60 100644 --- a/structurizr-core/src/com/structurizr/model/DeploymentNode.java +++ b/structurizr-core/src/com/structurizr/model/DeploymentNode.java @@ -2,9 +2,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; +import java.util.*; /** *

    @@ -210,14 +208,7 @@ public void setInstances(int instances) { @JsonIgnore protected Set getRequiredTags() { - // deployment nodes don't have any tags - return new HashSet<>(); - } - - @Override - public String getTags() { - // deployment nodes don't have any tags - return ""; + return new LinkedHashSet<>(Arrays.asList(Tags.ELEMENT, Tags.DEPLOYMENT_NODE)); } @Override diff --git a/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java b/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java index b3356fc9a..04aff9f00 100644 --- a/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java @@ -36,13 +36,16 @@ public void test_getParent_ReturnsTheParentDeploymentNode() { @Test public void test_getRequiredTags() { DeploymentNode deploymentNode = new DeploymentNode(); - assertEquals(0, deploymentNode.getRequiredTags().size()); + assertEquals(2, deploymentNode.getRequiredTags().size()); + assertTrue(deploymentNode.getRequiredTags().contains(Tags.ELEMENT)); + assertTrue(deploymentNode.getRequiredTags().contains(Tags.DEPLOYMENT_NODE)); } @Test public void test_getTags() { DeploymentNode deploymentNode = new DeploymentNode(); - assertEquals("", deploymentNode.getTags()); + deploymentNode.addTags("Tag 1", "Tag 2"); + assertEquals("Element,Deployment Node,Tag 1,Tag 2", deploymentNode.getTags()); } @Test diff --git a/structurizr-examples/src/com/structurizr/example/BigBankPlc.java b/structurizr-examples/src/com/structurizr/example/BigBankPlc.java index 6c5ef622e..e725a096a 100644 --- a/structurizr-examples/src/com/structurizr/example/BigBankPlc.java +++ b/structurizr-examples/src/com/structurizr/example/BigBankPlc.java @@ -136,8 +136,10 @@ public static void main(String[] args) throws Exception { .addDeploymentNode("Oracle - Primary", "The primary, live database server.", "Oracle 12c"); primaryDatabaseServer.add(database); - DeploymentNode secondaryDatabaseServer = bigBankDataCenter.addDeploymentNode("bigbank-db02", "The secondary database server.", "Ubuntu 16.04 LTS", 1, MapUtils.create("Location=Reading")) - .addDeploymentNode("Oracle - Secondary", "A secondary, standby database server, used for failover purposes only.", "Oracle 12c"); + DeploymentNode bigBankdb02 = bigBankDataCenter.addDeploymentNode("bigbank-db02", "The secondary database server.", "Ubuntu 16.04 LTS", 1, MapUtils.create("Location=Reading")); + bigBankdb02.addTags(FAILOVER_TAG); + DeploymentNode secondaryDatabaseServer = bigBankdb02.addDeploymentNode("Oracle - Secondary", "A secondary, standby database server, used for failover purposes only.", "Oracle 12c"); + secondaryDatabaseServer.addTags(FAILOVER_TAG); ContainerInstance secondaryDatabase = secondaryDatabaseServer.add(database); model.getRelationships().stream().filter(r -> r.getDestination().equals(secondaryDatabase)).forEach(r -> r.addTags(FAILOVER_TAG)); From 8bb571efb28b150b98a84952d17461a8bcf5b623 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 11 Jan 2020 14:17:37 +0000 Subject: [PATCH 068/717] Updated changelog. --- docs/changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.md b/docs/changelog.md index cebc17dc5..f6e4465dc 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -5,6 +5,7 @@ - Split View.setAutomaticLayout(boolean) to enableAutomaticLayout() and disableAutomaticLayout() (__breaking change__). - Added A1 and A0 paper sizes. - Adds support for themes. +- Adds support for tags on deployment nodes. ## 1.3.3 (24th December 2019) From 9ea861c8505c8b73ed3af1f9d16c381429bb9157 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 11 Jan 2020 21:07:15 +0000 Subject: [PATCH 069/717] Example tweak. --- .../src/com/structurizr/example/BigBankPlc.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/structurizr-examples/src/com/structurizr/example/BigBankPlc.java b/structurizr-examples/src/com/structurizr/example/BigBankPlc.java index e725a096a..a6821db33 100644 --- a/structurizr-examples/src/com/structurizr/example/BigBankPlc.java +++ b/structurizr-examples/src/com/structurizr/example/BigBankPlc.java @@ -114,13 +114,13 @@ public static void main(String[] args) throws Exception { .addDeploymentNode("Database Server", "A development database.", "Oracle 12c") .add(database); - developerLaptop.addDeploymentNode("Web Browser", "", "Google Chrome, Mozilla Firefox, Apple Safari or Microsoft Edge").add(singlePageApplication); + developerLaptop.addDeploymentNode("Web Browser", "", "Chrome, Firefox, Safari, or Edge").add(singlePageApplication); DeploymentNode customerMobileDevice = model.addDeploymentNode("Live", "Customer's mobile device", "", "Apple iOS or Android"); customerMobileDevice.add(mobileApp); DeploymentNode customerComputer = model.addDeploymentNode("Live", "Customer's computer", "", "Microsoft Windows or Apple macOS"); - customerComputer.addDeploymentNode("Web Browser", "", "Google Chrome, Mozilla Firefox, Apple Safari or Microsoft Edge").add(singlePageApplication); + customerComputer.addDeploymentNode("Web Browser", "", "Chrome, Firefox, Safari, or Edge").add(singlePageApplication); DeploymentNode bigBankDataCenter = model.addDeploymentNode("Live", "Big Bank plc", "", "Big Bank plc data center"); From c9b900b1a162019d909e4f43df0bd4f108fc86ff Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 14 Jan 2020 12:21:36 +0000 Subject: [PATCH 070/717] Adds support for animations on deployment views. --- docs/changelog.md | 1 + .../com/structurizr/view/DeploymentView.java | 82 ++++++++++++++++ .../src/com/structurizr/view/StaticView.java | 15 +-- .../structurizr/view/DeploymentViewTests.java | 94 +++++++++++++++++++ .../com/structurizr/example/BigBankPlc.java | 34 ++++--- 5 files changed, 208 insertions(+), 18 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index f6e4465dc..003852ef3 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -6,6 +6,7 @@ - Added A1 and A0 paper sizes. - Adds support for themes. - Adds support for tags on deployment nodes. +- Adds support for animations on deployment views. ## 1.3.3 (24th December 2019) diff --git a/structurizr-core/src/com/structurizr/view/DeploymentView.java b/structurizr-core/src/com/structurizr/view/DeploymentView.java index 317e3f5b5..73be008ba 100644 --- a/structurizr-core/src/com/structurizr/view/DeploymentView.java +++ b/structurizr-core/src/com/structurizr/view/DeploymentView.java @@ -5,6 +5,10 @@ import com.structurizr.util.StringUtils; import javax.annotation.Nonnull; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; /** * A deployment view, used to show the mapping of container instances to deployment nodes. @@ -14,6 +18,8 @@ public final class DeploymentView extends View { private Model model; private String environment; + private List animations = new ArrayList<>(); + DeploymentView() { } @@ -150,4 +156,80 @@ protected boolean canBeRemoved(Element element) { return true; } + /** + * Adds an animation step, with the specified container instances. + * + * @param containerInstances the container instances that should be shown in the animation step + */ + public void addAnimation(ContainerInstance... containerInstances) { + if (containerInstances == null || containerInstances.length == 0) { + throw new IllegalArgumentException("One or more container instances must be specified."); + } + + Set elementIdsInPreviousAnimationSteps = new HashSet<>(); + for (Animation animationStep : animations) { + elementIdsInPreviousAnimationSteps.addAll(animationStep.getElements()); + } + + Set elementsInThisAnimationStep = new HashSet<>(); + Set relationshipsInThisAnimationStep = new HashSet<>(); + + for (ContainerInstance containerInstance : containerInstances) { + if (isElementInView(containerInstance) && !elementIdsInPreviousAnimationSteps.contains(containerInstance.getId())) { + elementIdsInPreviousAnimationSteps.add(containerInstance.getId()); + elementsInThisAnimationStep.add(containerInstance); + + Element deploymentNode = findDeploymentNode(containerInstance); + while (deploymentNode != null) { + if (!elementIdsInPreviousAnimationSteps.contains(deploymentNode.getId())) { + elementIdsInPreviousAnimationSteps.add(deploymentNode.getId()); + elementsInThisAnimationStep.add(deploymentNode); + } + + deploymentNode = deploymentNode.getParent(); + } + } + } + + if (elementsInThisAnimationStep.size() == 0) { + throw new IllegalArgumentException("None of the specified container instances exist in this view."); + } + + for (RelationshipView relationshipView : this.getRelationships()) { + if ( + (elementsInThisAnimationStep.contains(relationshipView.getRelationship().getSource()) && elementIdsInPreviousAnimationSteps.contains(relationshipView.getRelationship().getDestination().getId())) || + (elementIdsInPreviousAnimationSteps.contains(relationshipView.getRelationship().getSource().getId()) && elementsInThisAnimationStep.contains(relationshipView.getRelationship().getDestination())) + ) { + relationshipsInThisAnimationStep.add(relationshipView.getRelationship()); + } + } + + animations.add(new Animation(animations.size() + 1, elementsInThisAnimationStep, relationshipsInThisAnimationStep)); + } + + private DeploymentNode findDeploymentNode(ContainerInstance containerInstance) { + for (Element element : getModel().getElements()) { + if (element instanceof DeploymentNode) { + DeploymentNode deploymentNode = (DeploymentNode)element; + if (deploymentNode.getContainerInstances().contains(containerInstance)) { + return deploymentNode; + } + } + } + + return null; + } + + public List getAnimations() { + return new ArrayList<>(animations); + } + + void setAnimations(List animations) { + if (animations != null) { + this.animations = new ArrayList<>(animations); + } else { + this.animations = new ArrayList<>(); + } + } + } \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/view/StaticView.java b/structurizr-core/src/com/structurizr/view/StaticView.java index cfc291395..c74881260 100644 --- a/structurizr-core/src/com/structurizr/view/StaticView.java +++ b/structurizr-core/src/com/structurizr/view/StaticView.java @@ -201,13 +201,20 @@ public void addAnimation(Element... elements) { } Set elementIdsInPreviousAnimationSteps = new HashSet<>(); + + for (Animation animationStep : animations) { + elementIdsInPreviousAnimationSteps.addAll(animationStep.getElements()); + } + Set elementsInThisAnimationStep = new HashSet<>(); Set relationshipsInThisAnimationStep = new HashSet<>(); for (Element element : elements) { if (isElementInView(element)) { - elementIdsInPreviousAnimationSteps.add(element.getId()); - elementsInThisAnimationStep.add(element); + if (!elementIdsInPreviousAnimationSteps.contains(element.getId())) { + elementIdsInPreviousAnimationSteps.add(element.getId()); + elementsInThisAnimationStep.add(element); + } } } @@ -215,10 +222,6 @@ public void addAnimation(Element... elements) { throw new IllegalArgumentException("None of the specified elements exist in this view."); } - for (Animation animationStep : animations) { - elementIdsInPreviousAnimationSteps.addAll(animationStep.getElements()); - } - for (RelationshipView relationshipView : this.getRelationships()) { if ( (elementsInThisAnimationStep.contains(relationshipView.getRelationship().getSource()) && elementIdsInPreviousAnimationSteps.contains(relationshipView.getRelationship().getDestination().getId())) || diff --git a/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java b/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java index df24c72af..20054bd78 100644 --- a/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java @@ -152,4 +152,98 @@ public void test_addDeploymentNode_AddsTheParentToo() { assertTrue(deploymentView.getElements().contains(new ElementView(containerInstance))); } + @Test + public void test_addAnimationStep_ThrowsAnException_WhenNoContainerInstancesAreSpecified() { + try { + deploymentView = views.createDeploymentView("deployment", "Description"); + deploymentView.addAnimation(); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("One or more container instances must be specified.", iae.getMessage()); + } + } + + @Test + public void test_addAnimationStep() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); + Container webApplication = softwareSystem.addContainer("Web Application", "Description", "Technology"); + Container database = softwareSystem.addContainer("Database", "Description", "Technology"); + webApplication.uses(database, "Reads from and writes to", "JDBC/HTTPS"); + + DeploymentNode developerLaptop = model.addDeploymentNode("Developer Laptop", "Description", "Technology"); + DeploymentNode apacheTomcat = developerLaptop.addDeploymentNode("Apache Tomcat", "Description", "Technology"); + DeploymentNode oracle = developerLaptop.addDeploymentNode("Oracle", "Description", "Technology"); + ContainerInstance webApplicationInstance = apacheTomcat.add(webApplication); + ContainerInstance databaseInstance = oracle.add(database); + + deploymentView = views.createDeploymentView(softwareSystem, "deployment", "Description"); + deploymentView.add(developerLaptop); + + deploymentView.addAnimation(webApplicationInstance); + deploymentView.addAnimation(databaseInstance); + + Animation step1 = deploymentView.getAnimations().stream().filter(step -> step.getOrder() == 1).findFirst().get(); + assertEquals(3, step1.getElements().size()); + assertTrue(step1.getElements().contains(developerLaptop.getId())); + assertTrue(step1.getElements().contains(apacheTomcat.getId())); + assertTrue(step1.getElements().contains(webApplicationInstance.getId())); + assertEquals(0, step1.getRelationships().size()); + + Animation step2 = deploymentView.getAnimations().stream().filter(step -> step.getOrder() == 2).findFirst().get(); + assertEquals(2, step2.getElements().size()); + assertTrue(step2.getElements().contains(oracle.getId())); + assertTrue(step2.getElements().contains(databaseInstance.getId())); + assertEquals(1, step2.getRelationships().size()); + assertTrue(step2.getRelationships().contains(webApplicationInstance.getRelationships().stream().findFirst().get().getId())); + } + + @Test + public void test_addAnimationStep_IgnoresContainerInstancesThatDoNotExistInTheView() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); + Container webApplication = softwareSystem.addContainer("Web Application", "Description", "Technology"); + Container database = softwareSystem.addContainer("Database", "Description", "Technology"); + webApplication.uses(database, "Reads from and writes to", "JDBC/HTTPS"); + + DeploymentNode developerLaptop = model.addDeploymentNode("Developer Laptop", "Description", "Technology"); + DeploymentNode apacheTomcat = developerLaptop.addDeploymentNode("Apache Tomcat", "Description", "Technology"); + DeploymentNode oracle = developerLaptop.addDeploymentNode("Oracle", "Description", "Technology"); + ContainerInstance webApplicationInstance = apacheTomcat.add(webApplication); + ContainerInstance databaseInstance = oracle.add(database); + + deploymentView = views.createDeploymentView(softwareSystem, "deployment", "Description"); + deploymentView.add(apacheTomcat); + + deploymentView.addAnimation(webApplicationInstance, databaseInstance); + + Animation step1 = deploymentView.getAnimations().stream().filter(step -> step.getOrder() == 1).findFirst().get(); + assertEquals(3, step1.getElements().size()); + assertTrue(step1.getElements().contains(developerLaptop.getId())); + assertTrue(step1.getElements().contains(apacheTomcat.getId())); + assertTrue(step1.getElements().contains(webApplicationInstance.getId())); + assertEquals(0, step1.getRelationships().size()); + } + + @Test + public void test_addAnimationStep_ThrowsAnException_WhenContainerInstancesAreSpecifiedButNoneOfThemExistInTheView() { + try { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); + Container webApplication = softwareSystem.addContainer("Web Application", "Description", "Technology"); + Container database = softwareSystem.addContainer("Database", "Description", "Technology"); + webApplication.uses(database, "Reads from and writes to", "JDBC/HTTPS"); + + DeploymentNode developerLaptop = model.addDeploymentNode("Developer Laptop", "Description", "Technology"); + DeploymentNode apacheTomcat = developerLaptop.addDeploymentNode("Apache Tomcat", "Description", "Technology"); + DeploymentNode oracle = developerLaptop.addDeploymentNode("Oracle", "Description", "Technology"); + ContainerInstance webApplicationInstance = apacheTomcat.add(webApplication); + ContainerInstance databaseInstance = oracle.add(database); + + deploymentView = views.createDeploymentView(softwareSystem, "deployment", "Description"); + + deploymentView.addAnimation(webApplicationInstance, databaseInstance); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("None of the specified container instances exist in this view.", iae.getMessage()); + } + } + } \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/BigBankPlc.java b/structurizr-examples/src/com/structurizr/example/BigBankPlc.java index a6821db33..9829fa696 100644 --- a/structurizr-examples/src/com/structurizr/example/BigBankPlc.java +++ b/structurizr-examples/src/com/structurizr/example/BigBankPlc.java @@ -107,44 +107,44 @@ public static void main(String[] args) throws Exception { DeploymentNode developerLaptop = model.addDeploymentNode("Development", "Developer Laptop", "A developer laptop.", "Microsoft Windows 10 or Apple macOS"); DeploymentNode apacheTomcat = developerLaptop.addDeploymentNode("Docker Container - Web Server", "A Docker container.", "Docker") .addDeploymentNode("Apache Tomcat", "An open source Java EE web server.", "Apache Tomcat 8.x", 1, MapUtils.create("Xmx=512M", "Xms=1024M", "Java Version=8")); - apacheTomcat.add(webApplication); - apacheTomcat.add(apiApplication); + ContainerInstance developmentWebApplication = apacheTomcat.add(webApplication); + ContainerInstance developmentApiApplication = apacheTomcat.add(apiApplication); - developerLaptop.addDeploymentNode("Docker Container - Database Server", "A Docker container.", "Docker") + ContainerInstance developmentDatabase = developerLaptop.addDeploymentNode("Docker Container - Database Server", "A Docker container.", "Docker") .addDeploymentNode("Database Server", "A development database.", "Oracle 12c") .add(database); - developerLaptop.addDeploymentNode("Web Browser", "", "Chrome, Firefox, Safari, or Edge").add(singlePageApplication); + ContainerInstance developmentSinglePageApplication = developerLaptop.addDeploymentNode("Web Browser", "", "Chrome, Firefox, Safari, or Edge").add(singlePageApplication); DeploymentNode customerMobileDevice = model.addDeploymentNode("Live", "Customer's mobile device", "", "Apple iOS or Android"); - customerMobileDevice.add(mobileApp); + ContainerInstance liveMobileApp = customerMobileDevice.add(mobileApp); DeploymentNode customerComputer = model.addDeploymentNode("Live", "Customer's computer", "", "Microsoft Windows or Apple macOS"); - customerComputer.addDeploymentNode("Web Browser", "", "Chrome, Firefox, Safari, or Edge").add(singlePageApplication); + ContainerInstance liveSinglePageApplication = customerComputer.addDeploymentNode("Web Browser", "", "Chrome, Firefox, Safari, or Edge").add(singlePageApplication); DeploymentNode bigBankDataCenter = model.addDeploymentNode("Live", "Big Bank plc", "", "Big Bank plc data center"); DeploymentNode liveWebServer = bigBankDataCenter.addDeploymentNode("bigbank-web***", "A web server residing in the web server farm, accessed via F5 BIG-IP LTMs.", "Ubuntu 16.04 LTS", 4, MapUtils.create("Location=London and Reading")); - liveWebServer.addDeploymentNode("Apache Tomcat", "An open source Java EE web server.", "Apache Tomcat 8.x", 1, MapUtils.create("Xmx=512M", "Xms=1024M", "Java Version=8")) + ContainerInstance liveWebApplication = liveWebServer.addDeploymentNode("Apache Tomcat", "An open source Java EE web server.", "Apache Tomcat 8.x", 1, MapUtils.create("Xmx=512M", "Xms=1024M", "Java Version=8")) .add(webApplication); DeploymentNode liveApiServer = bigBankDataCenter.addDeploymentNode("bigbank-api***", "A web server residing in the web server farm, accessed via F5 BIG-IP LTMs.", "Ubuntu 16.04 LTS", 8, MapUtils.create("Location=London and Reading")); - liveApiServer.addDeploymentNode("Apache Tomcat", "An open source Java EE web server.", "Apache Tomcat 8.x", 1, MapUtils.create("Xmx=512M", "Xms=1024M", "Java Version=8")) + ContainerInstance liveApiApplication = liveApiServer.addDeploymentNode("Apache Tomcat", "An open source Java EE web server.", "Apache Tomcat 8.x", 1, MapUtils.create("Xmx=512M", "Xms=1024M", "Java Version=8")) .add(apiApplication); DeploymentNode primaryDatabaseServer = bigBankDataCenter.addDeploymentNode("bigbank-db01", "The primary database server.", "Ubuntu 16.04 LTS", 1, MapUtils.create("Location=London")) .addDeploymentNode("Oracle - Primary", "The primary, live database server.", "Oracle 12c"); - primaryDatabaseServer.add(database); + ContainerInstance livePrimaryDatabase = primaryDatabaseServer.add(database); DeploymentNode bigBankdb02 = bigBankDataCenter.addDeploymentNode("bigbank-db02", "The secondary database server.", "Ubuntu 16.04 LTS", 1, MapUtils.create("Location=Reading")); bigBankdb02.addTags(FAILOVER_TAG); DeploymentNode secondaryDatabaseServer = bigBankdb02.addDeploymentNode("Oracle - Secondary", "A secondary, standby database server, used for failover purposes only.", "Oracle 12c"); secondaryDatabaseServer.addTags(FAILOVER_TAG); - ContainerInstance secondaryDatabase = secondaryDatabaseServer.add(database); + ContainerInstance liveSecondaryDatabase = secondaryDatabaseServer.add(database); - model.getRelationships().stream().filter(r -> r.getDestination().equals(secondaryDatabase)).forEach(r -> r.addTags(FAILOVER_TAG)); + model.getRelationships().stream().filter(r -> r.getDestination().equals(liveSecondaryDatabase)).forEach(r -> r.addTags(FAILOVER_TAG)); Relationship dataReplicationRelationship = primaryDatabaseServer.uses(secondaryDatabaseServer, "Replicates data to", ""); - secondaryDatabase.addTags(FAILOVER_TAG); + liveSecondaryDatabase.addTags(FAILOVER_TAG); // views/diagrams SystemLandscapeView systemLandscapeView = views.createSystemLandscapeView("SystemLandscape", "The system landscape diagram for Big Bank plc."); @@ -205,6 +205,10 @@ public static void main(String[] args) throws Exception { developmentDeploymentView.add(developerLaptop); developmentDeploymentView.setPaperSize(PaperSize.A5_Landscape); + developmentDeploymentView.addAnimation(developmentSinglePageApplication); + developmentDeploymentView.addAnimation(developmentWebApplication, developmentApiApplication); + developmentDeploymentView.addAnimation(developmentDatabase); + DeploymentView liveDeploymentView = views.createDeploymentView(internetBankingSystem, "LiveDeployment", "An example live deployment scenario for the Internet Banking System."); liveDeploymentView.setEnvironment("Live"); liveDeploymentView.add(bigBankDataCenter); @@ -213,6 +217,12 @@ public static void main(String[] args) throws Exception { liveDeploymentView.add(dataReplicationRelationship); liveDeploymentView.setPaperSize(PaperSize.A5_Landscape); + liveDeploymentView.addAnimation(liveSinglePageApplication); + liveDeploymentView.addAnimation(liveMobileApp); + liveDeploymentView.addAnimation(liveWebApplication, liveApiApplication); + liveDeploymentView.addAnimation(livePrimaryDatabase); + liveDeploymentView.addAnimation(liveSecondaryDatabase); + // colours, shapes and other diagram styling Styles styles = views.getConfiguration().getStyles(); styles.addElementStyle(Tags.ELEMENT).color("#ffffff"); From 31a78115fedb058250782f9b40aaac88bb1c584a Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 14 Jan 2020 22:00:41 +0000 Subject: [PATCH 071/717] Added a link to arch-as-code. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3d6fb3c48..7e261a546 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,7 @@ The view can then be exported to be visualised using the [Structurizr service](h * Related projects * [java-quickstart](https://github.com/structurizr/java-quickstart): A simple starting point for using Structurizr for Java * [java-extensions](https://github.com/structurizr/java-extensions): A collection of Structurizr for Java extensions; including the ability to extract software architecture information from code, export views to PlantUML, etc. + * [arch-as-code](https://github.com/nahknarmi/arch-as-code): A tool to store software architecture diagrams/documentation as YAML, and publish it to Structurizr. * [structurizr-kotlin](https://github.com/Catalysts/structurizr-extensions/tree/master/structurizr-kotlin): An extension for Structurizr that lets you create your models in a fluent way. * [structurizr-spring-boot](https://github.com/Catalysts/structurizr-extensions/tree/master/structurizr-spring-boot): A way to apply dependency management to help modularise Structurizr code. * [structurizr-groovy](https://github.com/tidyjava/structurizr-groovy): An initial version of a Groovy wrapper around Structurizr for Java. From c4cc3df8c462802fd3731fca7f619290aece8449 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 25 Feb 2020 13:05:13 +0000 Subject: [PATCH 072/717] Adds support for URLs on relationships (issue #141). --- docs/changelog.md | 1 + .../com/structurizr/model/Relationship.java | 27 +++++++++++++++++++ .../structurizr/model/RelationshipTests.java | 27 +++++++++++++++++++ 3 files changed, 55 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 003852ef3..e8678fa1f 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -7,6 +7,7 @@ - Adds support for themes. - Adds support for tags on deployment nodes. - Adds support for animations on deployment views. +- Adds support for URLs on relationships. ## 1.3.3 (24th December 2019) diff --git a/structurizr-core/src/com/structurizr/model/Relationship.java b/structurizr-core/src/com/structurizr/model/Relationship.java index 4517bcdc5..22b9e3765 100644 --- a/structurizr-core/src/com/structurizr/model/Relationship.java +++ b/structurizr-core/src/com/structurizr/model/Relationship.java @@ -1,6 +1,7 @@ package com.structurizr.model; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.structurizr.util.Url; import java.util.Collections; import java.util.LinkedHashSet; @@ -18,6 +19,7 @@ public final class Relationship extends ModelItem { private String description; private String technology; private InteractionStyle interactionStyle = InteractionStyle.Synchronous; + private String url; private String linkedRelationshipId; @@ -145,6 +147,31 @@ protected Set getRequiredTags() { } } + /** + * Gets the URL where more information about this relationship can be found. + * + * @return a URL as a String + */ + public String getUrl() { + return url; + } + + /** + * Sets the URL where more information about this relationship can be found. + * + * @param url the URL as a String + * @throws IllegalArgumentException if the URL is not a well-formed URL + */ + public void setUrl(String url) { + if (url != null && url.trim().length() > 0) { + if (Url.isUrl(url)) { + this.url = url; + } else { + throw new IllegalArgumentException(url + " is not a valid URL."); + } + } + } + @Override public String toString() { return source.toString() + " ---[" + description + "]---> " + destination.toString(); diff --git a/structurizr-core/test/unit/com/structurizr/model/RelationshipTests.java b/structurizr-core/test/unit/com/structurizr/model/RelationshipTests.java index 1cb56e4e5..1c96dc712 100644 --- a/structurizr-core/test/unit/com/structurizr/model/RelationshipTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/RelationshipTests.java @@ -78,4 +78,31 @@ public void test_getTags_IncludesTheInteractionStyleWhenSpecified() { assertTrue(relationship.getTags().contains(Tags.ASYNCHRONOUS)); } + @Test + public void test_setUrl() { + Relationship relationship = softwareSystem1.uses(softwareSystem2, "Uses 1", "Technology"); + relationship.setUrl("https://structurizr.com"); + assertEquals("https://structurizr.com", relationship.getUrl()); + } + + @Test(expected = IllegalArgumentException.class) + public void test_setUrl_ThrowsAnIllegalArgumentException_WhenAnInvalidUrlIsSpecified() { + Relationship relationship = softwareSystem1.uses(softwareSystem2, "Uses 1", "Technology"); + relationship.setUrl("htt://blah"); + } + + @Test + public void test_setUrl_DoesNothing_WhenANullUrlIsSpecified() { + Relationship relationship = softwareSystem1.uses(softwareSystem2, "Uses 1", "Technology"); + relationship.setUrl(null); + assertNull(relationship.getUrl()); + } + + @Test + public void test_setUrl_DoesNothing_WhenAnEmptyUrlIsSpecified() { + Relationship relationship = softwareSystem1.uses(softwareSystem2, "Uses 1", "Technology"); + relationship.setUrl(" "); + assertNull(relationship.getUrl()); + } + } \ No newline at end of file From 6fae8ae414ac348c0bc2a8c9b8cd26cb68d6c954 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 28 Feb 2020 13:57:48 +0000 Subject: [PATCH 073/717] Upgrade Jackson (issue #143), plus some refactoring to remove duplicate code. --- structurizr-client/build.gradle | 2 +- .../io/json/AbstractJsonReader.java | 16 +++++++++++ .../io/json/AbstractJsonWriter.java | 28 +++++++++++++++++++ .../io/json/EncryptedJsonReader.java | 7 ++--- .../io/json/EncryptedJsonWriter.java | 18 ++---------- .../com/structurizr/io/json/JsonReader.java | 6 ++-- .../com/structurizr/io/json/JsonWriter.java | 18 ++---------- 7 files changed, 55 insertions(+), 40 deletions(-) create mode 100644 structurizr-client/src/com/structurizr/io/json/AbstractJsonReader.java create mode 100644 structurizr-client/src/com/structurizr/io/json/AbstractJsonWriter.java diff --git a/structurizr-client/build.gradle b/structurizr-client/build.gradle index 082dd536d..8d5fb5575 100644 --- a/structurizr-client/build.gradle +++ b/structurizr-client/build.gradle @@ -2,7 +2,7 @@ dependencies { compile project(':structurizr-core') - compile 'com.fasterxml.jackson.core:jackson-databind:2.9.5' + compile 'com.fasterxml.jackson.core:jackson-databind:2.10.2' compile 'org.apache.httpcomponents.client5:httpclient5:5.0-beta4' diff --git a/structurizr-client/src/com/structurizr/io/json/AbstractJsonReader.java b/structurizr-client/src/com/structurizr/io/json/AbstractJsonReader.java new file mode 100644 index 000000000..5d2405b00 --- /dev/null +++ b/structurizr-client/src/com/structurizr/io/json/AbstractJsonReader.java @@ -0,0 +1,16 @@ +package com.structurizr.io.json; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; + +class AbstractJsonReader { + + ObjectMapper createObjectMapper() { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + + return objectMapper; + } + +} \ No newline at end of file diff --git a/structurizr-client/src/com/structurizr/io/json/AbstractJsonWriter.java b/structurizr-client/src/com/structurizr/io/json/AbstractJsonWriter.java new file mode 100644 index 000000000..0368b3861 --- /dev/null +++ b/structurizr-client/src/com/structurizr/io/json/AbstractJsonWriter.java @@ -0,0 +1,28 @@ +package com.structurizr.io.json; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; + +import java.text.SimpleDateFormat; + +class AbstractJsonWriter { + + private static final String ISO_DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'"; + + ObjectMapper createObjectMapper(boolean indentOutput) { + ObjectMapper objectMapper = new ObjectMapper(); + + if (indentOutput) { + objectMapper.enable(SerializationFeature.INDENT_OUTPUT); + } + + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); + + objectMapper.setDateFormat(new SimpleDateFormat(AbstractJsonWriter.ISO_DATE_TIME_FORMAT)); + + return objectMapper; + } + +} \ No newline at end of file diff --git a/structurizr-client/src/com/structurizr/io/json/EncryptedJsonReader.java b/structurizr-client/src/com/structurizr/io/json/EncryptedJsonReader.java index 9565f9250..c26786891 100644 --- a/structurizr-client/src/com/structurizr/io/json/EncryptedJsonReader.java +++ b/structurizr-client/src/com/structurizr/io/json/EncryptedJsonReader.java @@ -1,6 +1,5 @@ package com.structurizr.io.json; -import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.structurizr.encryption.EncryptedWorkspace; import com.structurizr.io.WorkspaceReaderException; @@ -8,7 +7,7 @@ import java.io.IOException; import java.io.Reader; -public final class EncryptedJsonReader { +public final class EncryptedJsonReader extends AbstractJsonReader { public EncryptedJsonReader() { } @@ -22,9 +21,7 @@ public EncryptedJsonReader() { */ public EncryptedWorkspace read(Reader reader) throws WorkspaceReaderException { try { - ObjectMapper objectMapper = new ObjectMapper(); - objectMapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT); - objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + ObjectMapper objectMapper = createObjectMapper(); return objectMapper.readValue(reader, EncryptedWorkspace.class); } catch (IOException ioe) { diff --git a/structurizr-client/src/com/structurizr/io/json/EncryptedJsonWriter.java b/structurizr-client/src/com/structurizr/io/json/EncryptedJsonWriter.java index 560838503..972294112 100644 --- a/structurizr-client/src/com/structurizr/io/json/EncryptedJsonWriter.java +++ b/structurizr-client/src/com/structurizr/io/json/EncryptedJsonWriter.java @@ -1,15 +1,12 @@ package com.structurizr.io.json; -import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; -import com.fasterxml.jackson.databind.util.ISO8601DateFormat; import com.structurizr.encryption.EncryptedWorkspace; import com.structurizr.io.WorkspaceWriterException; import java.io.Writer; -public final class EncryptedJsonWriter { +public final class EncryptedJsonWriter extends AbstractJsonWriter { private boolean indentOutput = true; @@ -33,20 +30,11 @@ public void write(EncryptedWorkspace workspace, Writer writer) throws WorkspaceW } try { - ObjectMapper objectMapper = new ObjectMapper(); - if (indentOutput) { - objectMapper.enable(SerializationFeature.INDENT_OUTPUT); - } - objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); - objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); - - objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); - objectMapper.setDateFormat(new ISO8601DateFormat()); - + ObjectMapper objectMapper = createObjectMapper(indentOutput); writer.write(objectMapper.writeValueAsString(workspace)); } catch (Exception e) { throw new WorkspaceWriterException("Could not write as JSON", e); } } -} +} \ No newline at end of file diff --git a/structurizr-client/src/com/structurizr/io/json/JsonReader.java b/structurizr-client/src/com/structurizr/io/json/JsonReader.java index 0dd5cf6c3..83ef6724d 100644 --- a/structurizr-client/src/com/structurizr/io/json/JsonReader.java +++ b/structurizr-client/src/com/structurizr/io/json/JsonReader.java @@ -13,7 +13,7 @@ /** * Reads a workspace definition as JSON. */ -public final class JsonReader implements WorkspaceReader { +public final class JsonReader extends AbstractJsonReader implements WorkspaceReader { /** * Reads and parses a workspace definition from a JSON document. @@ -24,9 +24,7 @@ public final class JsonReader implements WorkspaceReader { */ public Workspace read(Reader reader) throws WorkspaceReaderException { try { - ObjectMapper objectMapper = new ObjectMapper(); - objectMapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT); - objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + ObjectMapper objectMapper = createObjectMapper(); Workspace workspace = objectMapper.readValue(reader, Workspace.class); workspace.hydrate(); diff --git a/structurizr-client/src/com/structurizr/io/json/JsonWriter.java b/structurizr-client/src/com/structurizr/io/json/JsonWriter.java index 9f33a8a00..c8e946355 100644 --- a/structurizr-client/src/com/structurizr/io/json/JsonWriter.java +++ b/structurizr-client/src/com/structurizr/io/json/JsonWriter.java @@ -1,9 +1,6 @@ package com.structurizr.io.json; -import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; -import com.fasterxml.jackson.databind.util.ISO8601DateFormat; import com.structurizr.Workspace; import com.structurizr.io.WorkspaceWriter; import com.structurizr.io.WorkspaceWriterException; @@ -14,7 +11,7 @@ /** * Writes a workspace definition as a JSON string. */ -public final class JsonWriter implements WorkspaceWriter { +public final class JsonWriter extends AbstractJsonWriter implements WorkspaceWriter { private boolean indentOutput = true; @@ -38,20 +35,11 @@ public void write(Workspace workspace, Writer writer) throws WorkspaceWriterExce } try { - ObjectMapper objectMapper = new ObjectMapper(); - if (indentOutput) { - objectMapper.enable(SerializationFeature.INDENT_OUTPUT); - } - objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); - objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); - - objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); - objectMapper.setDateFormat(new ISO8601DateFormat()); - + ObjectMapper objectMapper = createObjectMapper(indentOutput); writer.write(objectMapper.writeValueAsString(workspace)); } catch (IOException ioe) { throw new WorkspaceWriterException("Could not write as JSON", ioe); } } -} +} \ No newline at end of file From 1e655484b0f447fb50321005b20e39f27a0be715 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 28 Feb 2020 13:58:26 +0000 Subject: [PATCH 074/717] Upgrade HttpClient to release version. --- structurizr-client/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structurizr-client/build.gradle b/structurizr-client/build.gradle index 8d5fb5575..c2f300b40 100644 --- a/structurizr-client/build.gradle +++ b/structurizr-client/build.gradle @@ -4,7 +4,7 @@ dependencies { compile 'com.fasterxml.jackson.core:jackson-databind:2.10.2' - compile 'org.apache.httpcomponents.client5:httpclient5:5.0-beta4' + compile 'org.apache.httpcomponents.client5:httpclient5:5.0' compile 'javax.xml.bind:jaxb-api:2.3.0' From 8f4348074e8d6f48f55637cbdf61c5d58525bc7a Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 29 Feb 2020 10:07:19 +0000 Subject: [PATCH 075/717] Updated docs to reflect release. --- docs/binaries.md | 4 ++-- docs/changelog.md | 2 +- docs/getting-started.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/binaries.md b/docs/binaries.md index cf55ea1d4..fe5aa933a 100644 --- a/docs/binaries.md +++ b/docs/binaries.md @@ -3,5 +3,5 @@ The "Structurizr for Java" binaries are hosted on [Maven Central](https://repo1. Name | Description ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-core:1.3.3 | The core library that can used to create software architecture models. -com.structurizr:structurizr-client:1.3.3 | The API client for publishing models on the Structurizr cloud service and on-premises installation. \ No newline at end of file +com.structurizr:structurizr-core:1.3.4 | The core library that can used to create software architecture models. +com.structurizr:structurizr-client:1.3.4 | The API client for publishing models on the Structurizr cloud service and on-premises installation. \ No newline at end of file diff --git a/docs/changelog.md b/docs/changelog.md index e8678fa1f..8a8e7d06f 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 1.3.4 (unreleased) +## 1.3.4 (29th February 2020) - Split View.setAutomaticLayout(boolean) to enableAutomaticLayout() and disableAutomaticLayout() (__breaking change__). - Added A1 and A0 paper sizes. diff --git a/docs/getting-started.md b/docs/getting-started.md index 2a2541799..d8b264fb2 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -12,7 +12,7 @@ The Structurizr for Java binaries are hosted on [Maven Central](https://repo1.ma Name | Description ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-client:1.3.3 | The Structurizr API client library. +com.structurizr:structurizr-client:1.3.4 | The Structurizr API client library. ## 2. Create a Java program From fa2fb74c18a580877f8c1e0ce3685104206f2024 Mon Sep 17 00:00:00 2001 From: Fynn L <19202953+LyteFM@users.noreply.github.com> Date: Mon, 2 Mar 2020 19:27:21 +0200 Subject: [PATCH 076/717] Fix dead link Link to the Diagram Editor - Page instead. --- docs/getting-started.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index d8b264fb2..95f588e90 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -83,7 +83,7 @@ Once you've run your program to create and upload the workspace, you can now sig ![Getting Started with Structurizr for Java](images/getting-started-1.png) -By default, Structurizr does not auto-layout your diagram elements. The diagram layout can be modified by dragging the elements around the diagram canvas in the diagram editor, and the layout saved using the "Save workspace" button. See [Structurizr - Help - Diagram layout](https://structurizr.com/help/diagram-layout) for more information. +By default, Structurizr does not auto-layout your diagram elements. The diagram layout can be modified by dragging the elements around the diagram canvas in the diagram editor, and the layout saved using the "Save workspace" button. See [Structurizr - Help - Diagram layout](https://structurizr.com/help/diagram-editor) for more information. ![Getting Started with Structurizr for Java](images/getting-started-2.png) @@ -91,4 +91,4 @@ A diagram key is automatically generated based upon the styles in the model. Cli ![A diagram key](images/getting-started-diagram-key.png) -When you upload a new version of the same workspace, the Structurizr client will try to retain the diagram layout information. See [API client](api-client.md) for more details. \ No newline at end of file +When you upload a new version of the same workspace, the Structurizr client will try to retain the diagram layout information. See [API client](api-client.md) for more details. From 563c3c875efd8376c92513a20ed047a797d9e3eb Mon Sep 17 00:00:00 2001 From: h1dden-da3m0n <33120068+h1dden-da3m0n@users.noreply.github.com> Date: Wed, 4 Mar 2020 22:51:06 +0100 Subject: [PATCH 077/717] fix findRelationshipStyle method adds all missing styling characteristics according to documentation --- .../src/com/structurizr/view/Styles.java | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/structurizr-core/src/com/structurizr/view/Styles.java b/structurizr-core/src/com/structurizr/view/Styles.java index 62f731b2b..a020ca341 100644 --- a/structurizr-core/src/com/structurizr/view/Styles.java +++ b/structurizr-core/src/com/structurizr/view/Styles.java @@ -138,9 +138,37 @@ public RelationshipStyle findRelationshipStyle(Relationship relationship) { for (String tag : relationship.getTagsAsSet()) { RelationshipStyle relationshipStyle = findRelationshipStyle(tag); if (relationshipStyle != null) { - if (relationshipStyle.getColor() != null && relationshipStyle.getColor().trim().length() > 0) { + if (relationshipStyle.getThickness() != null) { + style.setThickness(relationshipStyle.getThickness()); + } + + if (!StringUtils.isNullOrEmpty(relationshipStyle.getColor())) { style.setColor(relationshipStyle.getColor()); } + + if (relationshipStyle.getDashed() != null) { + style.setDashed(relationshipStyle.getDashed()); + } + + if (relationshipStyle.getRouting() != null) { + style.setRouting(relationshipStyle.getRouting()); + } + + if (relationshipStyle.getFontSize() != null) { + style.setFontSize(relationshipStyle.getFontSize()); + } + + if (relationshipStyle.getWidth() != null) { + style.setWidth(relationshipStyle.getWidth()); + } + + if (relationshipStyle.getPosition() != null) { + style.setPosition(relationshipStyle.getPosition()); + } + + if (relationshipStyle.getOpacity() != null) { + style.setOpacity(relationshipStyle.getOpacity()); + } } } } From b942bad2bd92c59658864c718f221263671d3887 Mon Sep 17 00:00:00 2001 From: h1dden-da3m0n <33120068+h1dden-da3m0n@users.noreply.github.com> Date: Wed, 4 Mar 2020 22:58:18 +0100 Subject: [PATCH 078/717] fix findElementStyle method add all missing styling characteristics according to documentation --- .../src/com/structurizr/view/Styles.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/structurizr-core/src/com/structurizr/view/Styles.java b/structurizr-core/src/com/structurizr/view/Styles.java index a020ca341..ad38ad420 100644 --- a/structurizr-core/src/com/structurizr/view/Styles.java +++ b/structurizr-core/src/com/structurizr/view/Styles.java @@ -109,6 +109,14 @@ public ElementStyle findElementStyle(Element element) { for (String tag : element.getTagsAsSet()) { ElementStyle elementStyle = findElementStyle(tag); if (elementStyle != null) { + if (elementStyle.getWidth() != null) { + style.setWidth(elementStyle.getWidth()); + } + + if (elementStyle.getHeight() != null) { + style.setHeight(elementStyle.getHeight()); + } + if (!StringUtils.isNullOrEmpty(elementStyle.getBackground())) { style.setBackground(elementStyle.getBackground()); } @@ -121,9 +129,21 @@ public ElementStyle findElementStyle(Element element) { style.setStroke(elementStyle.getStroke()); } + if (elementStyle.getFontSize() != null) { + style.setFontSize(elementStyle.getFontSize()); + } + if (elementStyle.getShape() != null) { style.setShape(elementStyle.getShape()); } + + if (elementStyle.getBorder() != null) { + style.setBorder(elementStyle.getBorder()); + } + + if (elementStyle.getOpacity() != null) { + style.setOpacity(elementStyle.getOpacity()); + } } } } From 0bd9b462577eaf8a6565957eef5cc2ab1c59ae4f Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 11 Mar 2020 08:47:21 +0000 Subject: [PATCH 079/717] Added an externalSoftwareSystemBoundariesVisible property to ContainerView, to set whether software system boundaries should be visible for "external" containers (those outside the software system in scope). --- build.gradle | 2 +- docs/changelog.md | 4 ++++ .../com/structurizr/view/ContainerView.java | 20 +++++++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 141b38be0..c082d36da 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.3.4' + version = '1.3.5' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 8a8e7d06f..441d7ef67 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.3.5 (unreleased) + +- Added an externalSoftwareSystemBoundariesVisible property to ContainerView, to set whether software system boundaries should be visible for "external" containers (those outside the software system in scope). + ## 1.3.4 (29th February 2020) - Split View.setAutomaticLayout(boolean) to enableAutomaticLayout() and disableAutomaticLayout() (__breaking change__). diff --git a/structurizr-core/src/com/structurizr/view/ContainerView.java b/structurizr-core/src/com/structurizr/view/ContainerView.java index 4da0e9855..1b97bb494 100644 --- a/structurizr-core/src/com/structurizr/view/ContainerView.java +++ b/structurizr-core/src/com/structurizr/view/ContainerView.java @@ -12,6 +12,8 @@ */ public final class ContainerView extends StaticView { + private boolean externalSoftwareSystemBoundariesVisible = true; + ContainerView() { } @@ -173,4 +175,22 @@ protected boolean canBeRemoved(Element element) { return true; } + /** + * Determines whether software system boundaries should be visible for "external" containers (those outside the software system in scope). + * + * @return true if external software system boundaries are visible, false otherwise + */ + public boolean getExternalSoftwareSystemBoundariesVisible() { + return externalSoftwareSystemBoundariesVisible; + } + + /** + * Sets whether software system boundaries should be visible for "external" containers (those outside the software system in scope). + * + * @param externalSoftwareSystemBoundariesVisible true if external software system boundaries should be visible, false otherwise + */ + public void setExternalSoftwareSystemBoundariesVisible(boolean externalSoftwareSystemBoundariesVisible) { + this.externalSoftwareSystemBoundariesVisible = externalSoftwareSystemBoundariesVisible; + } + } \ No newline at end of file From b487caa47c3a4d1addcb7d1fef7614333c372a2e Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 11 Mar 2020 09:17:03 +0000 Subject: [PATCH 080/717] External software system boundaries should be not visible by default. --- structurizr-core/src/com/structurizr/view/ContainerView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structurizr-core/src/com/structurizr/view/ContainerView.java b/structurizr-core/src/com/structurizr/view/ContainerView.java index 1b97bb494..5990167f1 100644 --- a/structurizr-core/src/com/structurizr/view/ContainerView.java +++ b/structurizr-core/src/com/structurizr/view/ContainerView.java @@ -12,7 +12,7 @@ */ public final class ContainerView extends StaticView { - private boolean externalSoftwareSystemBoundariesVisible = true; + private boolean externalSoftwareSystemBoundariesVisible = false; ContainerView() { } From 96228968e1696d75bddb614198c9e8c2f1e52b0d Mon Sep 17 00:00:00 2001 From: mitchell-ata <59654069+mitchell-ata@users.noreply.github.com> Date: Fri, 13 Mar 2020 21:42:56 -0400 Subject: [PATCH 081/717] Fix grammar errors --- docs/decisions.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/decisions.md b/docs/decisions.md index 7b71f7f0c..bff92c907 100644 --- a/docs/decisions.md +++ b/docs/decisions.md @@ -1,7 +1,7 @@ # Decisions -Although architecture decisions can be included in supplementary documentation, Structurizr also provides support for publishing architecture decision records, as described by [as described by Michael Nygard](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions). +Although architecture decisions can be included in supplementary documentation, Structurizr also provides support for publishing architecture decision records, [as described by Michael Nygard](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions). -Decision records can either created manually using the API on the [Documentation class](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/documentation/Documentation.java), or using the [AdrToolsImporter](https://github.com/structurizr/java-extensions/blob/master/structurizr-adr-tools/src/com/structurizr/documentation/AdrToolsImporter.java) to import ADRs from Nat Pryce's popular [adr-tools](https://github.com/npryce/adr-tools) tooling. Here is [an example](https://github.com/structurizr/java/blob/master/structurizr-examples/src/com/structurizr/example/AdrTools.java). +Decision records can either be created manually using the API on the [Documentation class](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/documentation/Documentation.java), or using the [AdrToolsImporter](https://github.com/structurizr/java-extensions/blob/master/structurizr-adr-tools/src/com/structurizr/documentation/AdrToolsImporter.java) to import ADRs from Nat Pryce's popular [adr-tools](https://github.com/npryce/adr-tools) tooling. Here is [an example](https://github.com/structurizr/java/blob/master/structurizr-examples/src/com/structurizr/example/AdrTools.java). -See [Structurizr - Decision Log](https://structurizr.com/help/decision-log) for more details. \ No newline at end of file +See [Structurizr - Decision Log](https://structurizr.com/help/decision-log) for more details. From 53dbc05667d883761277b53017e6da77e4058fd1 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 19 Mar 2020 09:40:51 +0000 Subject: [PATCH 082/717] Added a 16:10 ratio paper size. --- docs/changelog.md | 1 + structurizr-core/src/com/structurizr/view/PaperSize.java | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 441d7ef67..0842749e6 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -3,6 +3,7 @@ ## 1.3.5 (unreleased) - Added an externalSoftwareSystemBoundariesVisible property to ContainerView, to set whether software system boundaries should be visible for "external" containers (those outside the software system in scope). +- Added a 16:10 ratio paper size. ## 1.3.4 (29th February 2020) diff --git a/structurizr-core/src/com/structurizr/view/PaperSize.java b/structurizr-core/src/com/structurizr/view/PaperSize.java index 6fb7cf35d..dd873e4da 100644 --- a/structurizr-core/src/com/structurizr/view/PaperSize.java +++ b/structurizr-core/src/com/structurizr/view/PaperSize.java @@ -36,7 +36,8 @@ public enum PaperSize { Legal_Landscape("Legal", Orientation.Landscape, 4200, 2550), Slide_4_3("Slide 4:3", Orientation.Landscape, 3306, 2480), - Slide_16_9("Slide 16:9", Orientation.Landscape, 3508, 1973); + Slide_16_9("Slide 16:9", Orientation.Landscape, 3508, 1973), + Slide_16_10("Slide 16:10", Orientation.Landscape, 3508, 2193); private String name; private Orientation orientation; From 1d1079bd83c84553930d0602b5d815cc062ccf87 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 19 Mar 2020 10:24:32 +0000 Subject: [PATCH 083/717] Addresses issue #148. --- structurizr-core/src/com/structurizr/Workspace.java | 13 ++++++++++++- .../src/com/structurizr/model/Model.java | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/structurizr-core/src/com/structurizr/Workspace.java b/structurizr-core/src/com/structurizr/Workspace.java index ad883194f..5f88a0142 100644 --- a/structurizr-core/src/com/structurizr/Workspace.java +++ b/structurizr-core/src/com/structurizr/Workspace.java @@ -22,7 +22,7 @@ public final class Workspace extends AbstractWorkspace { private static final Log log = LogFactory.getLog(Workspace.class); - private Model model = new Model(); + private Model model; private ViewSet viewSet; private Documentation documentation; @@ -38,6 +38,7 @@ public final class Workspace extends AbstractWorkspace { public Workspace(String name, String description) { super(name, description); + model = createModel(); viewSet = createViewSet(); documentation = createDocumentation(); } @@ -68,6 +69,16 @@ void setViews(ViewSet viewSet) { this.viewSet = viewSet; } + private Model createModel() { + try { + Constructor constructor = Model.class.getDeclaredConstructor(); + constructor.setAccessible(true); + return (Model)constructor.newInstance(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + private ViewSet createViewSet() { try { Constructor constructor = ViewSet.class.getDeclaredConstructor(Model.class); diff --git a/structurizr-core/src/com/structurizr/model/Model.java b/structurizr-core/src/com/structurizr/model/Model.java index 628cc9852..8b09f8d2f 100644 --- a/structurizr-core/src/com/structurizr/model/Model.java +++ b/structurizr-core/src/com/structurizr/model/Model.java @@ -24,7 +24,7 @@ public final class Model { private Set softwareSystems = new LinkedHashSet<>(); private Set deploymentNodes = new LinkedHashSet<>(); - public Model() { + Model() { } /** From 409b32dd5a211435e4141ed8b16205fde65451fd Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 24 Mar 2020 18:31:14 +0000 Subject: [PATCH 084/717] Modified element text colors. --- .../src/com/structurizr/example/BigBankPlc.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/structurizr-examples/src/com/structurizr/example/BigBankPlc.java b/structurizr-examples/src/com/structurizr/example/BigBankPlc.java index 9829fa696..ee25b520f 100644 --- a/structurizr-examples/src/com/structurizr/example/BigBankPlc.java +++ b/structurizr-examples/src/com/structurizr/example/BigBankPlc.java @@ -225,13 +225,13 @@ public static void main(String[] args) throws Exception { // colours, shapes and other diagram styling Styles styles = views.getConfiguration().getStyles(); - styles.addElementStyle(Tags.ELEMENT).color("#ffffff"); - styles.addElementStyle(Tags.SOFTWARE_SYSTEM).background("#1168bd"); - styles.addElementStyle(Tags.CONTAINER).background("#438dd5"); + styles.addElementStyle(Tags.ELEMENT); + styles.addElementStyle(Tags.SOFTWARE_SYSTEM).background("#1168bd").color("#ffffff"); + styles.addElementStyle(Tags.CONTAINER).background("#438dd5").color("#ffffff"); styles.addElementStyle(Tags.COMPONENT).background("#85bbf0").color("#000000"); - styles.addElementStyle(Tags.PERSON).background("#08427b").shape(Shape.Person).fontSize(22); - styles.addElementStyle(EXISTING_SYSTEM_TAG).background("#999999"); - styles.addElementStyle(BANK_STAFF_TAG).background("#999999"); + styles.addElementStyle(Tags.PERSON).background("#08427b").color("#ffffff").shape(Shape.Person).fontSize(22); + styles.addElementStyle(EXISTING_SYSTEM_TAG).background("#999999").color("#ffffff"); + styles.addElementStyle(BANK_STAFF_TAG).background("#999999").color("#ffffff"); styles.addElementStyle(WEB_BROWSER_TAG).shape(Shape.WebBrowser); styles.addElementStyle(MOBILE_APP_TAG).shape(Shape.MobileDeviceLandscape); styles.addElementStyle(DATABASE_TAG).shape(Shape.Cylinder); From e07e8ea20ce60db6e01e4d599133c7d140454604 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 24 Mar 2020 18:33:26 +0000 Subject: [PATCH 085/717] Removed redundant style. --- structurizr-examples/src/com/structurizr/example/BigBankPlc.java | 1 - 1 file changed, 1 deletion(-) diff --git a/structurizr-examples/src/com/structurizr/example/BigBankPlc.java b/structurizr-examples/src/com/structurizr/example/BigBankPlc.java index ee25b520f..1f7636a0e 100644 --- a/structurizr-examples/src/com/structurizr/example/BigBankPlc.java +++ b/structurizr-examples/src/com/structurizr/example/BigBankPlc.java @@ -225,7 +225,6 @@ public static void main(String[] args) throws Exception { // colours, shapes and other diagram styling Styles styles = views.getConfiguration().getStyles(); - styles.addElementStyle(Tags.ELEMENT); styles.addElementStyle(Tags.SOFTWARE_SYSTEM).background("#1168bd").color("#ffffff"); styles.addElementStyle(Tags.CONTAINER).background("#438dd5").color("#ffffff"); styles.addElementStyle(Tags.COMPONENT).background("#85bbf0").color("#000000"); From 2d1a91ddb9cafe2f71b0ca7f20ef1461f882b8bc Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 26 Mar 2020 07:53:49 +0000 Subject: [PATCH 086/717] Some changes to remove duplicate vertices on lines. --- .../structurizr/view/RelationshipView.java | 6 +++-- .../src/com/structurizr/view/Vertex.java | 23 ++++++++++++++++- .../com/structurizr/view/VertexTests.java | 25 +++++++++++++++++++ 3 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 structurizr-core/test/unit/com/structurizr/view/VertexTests.java diff --git a/structurizr-core/src/com/structurizr/view/RelationshipView.java b/structurizr-core/src/com/structurizr/view/RelationshipView.java index 0c03e72cd..d2e12edcd 100644 --- a/structurizr-core/src/com/structurizr/view/RelationshipView.java +++ b/structurizr-core/src/com/structurizr/view/RelationshipView.java @@ -5,7 +5,9 @@ import com.structurizr.model.Relationship; import java.util.Collection; +import java.util.LinkedHashSet; import java.util.LinkedList; +import java.util.Set; /** * This class represents an instance of a Relationship on a View. @@ -19,7 +21,7 @@ public final class RelationshipView { private String id; private String description; private String order; - private Collection vertices = new LinkedList<>(); + private Set vertices = new LinkedHashSet<>(); @JsonInclude(value = JsonInclude.Include.NON_NULL) private Routing routing; @@ -118,7 +120,7 @@ public Collection getVertices() { */ public void setVertices(Collection vertices) { if (vertices != null) { - this.vertices = new LinkedList<>(vertices); + this.vertices = new LinkedHashSet<>(vertices); } } diff --git a/structurizr-core/src/com/structurizr/view/Vertex.java b/structurizr-core/src/com/structurizr/view/Vertex.java index 8d85f2566..eda796e23 100644 --- a/structurizr-core/src/com/structurizr/view/Vertex.java +++ b/structurizr-core/src/com/structurizr/view/Vertex.java @@ -1,5 +1,7 @@ package com.structurizr.view; +import java.util.Objects; + /** * The X, Y coordinate of a bend in a line. */ @@ -42,4 +44,23 @@ public void setY(int y) { this.y = y; } -} + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (o == null || getClass() != o.getClass()) { + return false; + } + + Vertex vertex = (Vertex)o; + return x == vertex.x && y == vertex.y; + } + + @Override + public int hashCode() { + return Objects.hash(x, y); + } + +} \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/view/VertexTests.java b/structurizr-core/test/unit/com/structurizr/view/VertexTests.java new file mode 100644 index 000000000..a7f3be379 --- /dev/null +++ b/structurizr-core/test/unit/com/structurizr/view/VertexTests.java @@ -0,0 +1,25 @@ +package com.structurizr.view; + +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class VertexTests { + + @Test + public void test_equals() { + Vertex vertex1 = new Vertex(123, 456); + Vertex vertex2 = new Vertex(123, 456); + Vertex vertex3 = new Vertex(456, 123); + + assertFalse(vertex1.equals(null)); + assertFalse(vertex1.equals("hello world")); + + assertTrue(vertex1.equals(vertex1)); + assertTrue(vertex1.equals(vertex2)); + assertTrue(vertex2.equals(vertex1)); + assertFalse(vertex2.equals(vertex3)); + } + +} \ No newline at end of file From 9a50a562bf73b19fb1e907c06d3b27f8725f6c2f Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 26 Mar 2020 08:07:20 +0000 Subject: [PATCH 087/717] Made the orientation enum public, and added some tests. --- .../src/com/structurizr/view/PaperSize.java | 21 ++--- .../com/structurizr/view/PaperSizeTests.java | 79 +++++++++++++++++++ 2 files changed, 91 insertions(+), 9 deletions(-) create mode 100644 structurizr-core/test/unit/com/structurizr/view/PaperSizeTests.java diff --git a/structurizr-core/src/com/structurizr/view/PaperSize.java b/structurizr-core/src/com/structurizr/view/PaperSize.java index dd873e4da..e58ebc2e8 100644 --- a/structurizr-core/src/com/structurizr/view/PaperSize.java +++ b/structurizr-core/src/com/structurizr/view/PaperSize.java @@ -67,22 +67,25 @@ public int getHeight() { return height; } - enum Orientation { + public enum Orientation { Portrait, Landscape } - public static final List getOrderedPaperSizes() { + public static List getOrderedPaperSizes() { List paperSizes = new ArrayList<>(); - for (PaperSize paperSize : values()) { - if (paperSize.getOrientation() == Orientation.Landscape) { - paperSizes.add(paperSize); - } - } + paperSizes.addAll(getOrderedPaperSizes(Orientation.Landscape)); + paperSizes.addAll(getOrderedPaperSizes(Orientation.Portrait)); + + return paperSizes; + } + + public static List getOrderedPaperSizes(Orientation orientation) { + List paperSizes = new ArrayList<>(); for (PaperSize paperSize : values()) { - if (paperSize.getOrientation() == Orientation.Portrait) { + if (paperSize.getOrientation() == orientation) { paperSizes.add(paperSize); } } @@ -90,4 +93,4 @@ public static final List getOrderedPaperSizes() { return paperSizes; } -} +} \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/view/PaperSizeTests.java b/structurizr-core/test/unit/com/structurizr/view/PaperSizeTests.java new file mode 100644 index 000000000..70f4e1ac3 --- /dev/null +++ b/structurizr-core/test/unit/com/structurizr/view/PaperSizeTests.java @@ -0,0 +1,79 @@ +package com.structurizr.view; + +import org.junit.Test; + +import java.util.List; + +import static org.junit.Assert.*; + +public class PaperSizeTests { + + @Test + public void test_getOrderedPaperSizes_WhenOrientationIsLandscape() { + List paperSizes = PaperSize.getOrderedPaperSizes(PaperSize.Orientation.Landscape); + assertEquals(12, paperSizes.size()); + + assertEquals(PaperSize.A6_Landscape, paperSizes.get(0)); + assertEquals(PaperSize.A5_Landscape, paperSizes.get(1)); + assertEquals(PaperSize.A4_Landscape, paperSizes.get(2)); + assertEquals(PaperSize.A3_Landscape, paperSizes.get(3)); + assertEquals(PaperSize.A2_Landscape, paperSizes.get(4)); + assertEquals(PaperSize.A1_Landscape, paperSizes.get(5)); + assertEquals(PaperSize.A0_Landscape, paperSizes.get(6)); + + assertEquals(PaperSize.Letter_Landscape, paperSizes.get(7)); + assertEquals(PaperSize.Legal_Landscape, paperSizes.get(8)); + assertEquals(PaperSize.Slide_4_3, paperSizes.get(9)); + assertEquals(PaperSize.Slide_16_9, paperSizes.get(10)); + assertEquals(PaperSize.Slide_16_10, paperSizes.get(11)); + } + + @Test + public void test_getOrderedPaperSizes_WhenOrientationIsPortrait() { + List paperSizes = PaperSize.getOrderedPaperSizes(PaperSize.Orientation.Portrait); + assertEquals(9, paperSizes.size()); + + assertEquals(PaperSize.A6_Portrait, paperSizes.get(0)); + assertEquals(PaperSize.A5_Portrait, paperSizes.get(1)); + assertEquals(PaperSize.A4_Portrait, paperSizes.get(2)); + assertEquals(PaperSize.A3_Portrait, paperSizes.get(3)); + assertEquals(PaperSize.A2_Portrait, paperSizes.get(4)); + assertEquals(PaperSize.A1_Portrait, paperSizes.get(5)); + assertEquals(PaperSize.A0_Portrait, paperSizes.get(6)); + + assertEquals(PaperSize.Letter_Portrait, paperSizes.get(7)); + assertEquals(PaperSize.Legal_Portrait, paperSizes.get(8)); + } + + @Test + public void test_getOrderedPaperSizes() { + List paperSizes = PaperSize.getOrderedPaperSizes(); + assertEquals(21, paperSizes.size()); + + assertEquals(PaperSize.A6_Landscape, paperSizes.get(0)); + assertEquals(PaperSize.A5_Landscape, paperSizes.get(1)); + assertEquals(PaperSize.A4_Landscape, paperSizes.get(2)); + assertEquals(PaperSize.A3_Landscape, paperSizes.get(3)); + assertEquals(PaperSize.A2_Landscape, paperSizes.get(4)); + assertEquals(PaperSize.A1_Landscape, paperSizes.get(5)); + assertEquals(PaperSize.A0_Landscape, paperSizes.get(6)); + + assertEquals(PaperSize.Letter_Landscape, paperSizes.get(7)); + assertEquals(PaperSize.Legal_Landscape, paperSizes.get(8)); + assertEquals(PaperSize.Slide_4_3, paperSizes.get(9)); + assertEquals(PaperSize.Slide_16_9, paperSizes.get(10)); + assertEquals(PaperSize.Slide_16_10, paperSizes.get(11)); + + assertEquals(PaperSize.A6_Portrait, paperSizes.get(12)); + assertEquals(PaperSize.A5_Portrait, paperSizes.get(13)); + assertEquals(PaperSize.A4_Portrait, paperSizes.get(14)); + assertEquals(PaperSize.A3_Portrait, paperSizes.get(15)); + assertEquals(PaperSize.A2_Portrait, paperSizes.get(16)); + assertEquals(PaperSize.A1_Portrait, paperSizes.get(17)); + assertEquals(PaperSize.A0_Portrait, paperSizes.get(18)); + + assertEquals(PaperSize.Letter_Portrait, paperSizes.get(19)); + assertEquals(PaperSize.Legal_Portrait, paperSizes.get(20)); + } + +} \ No newline at end of file From 10ac463065e9589cb056ae90aedf7900bf80752b Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 26 Mar 2020 11:27:01 +0000 Subject: [PATCH 088/717] Made method public, so it can be used from the graphviz extension. --- structurizr-core/src/com/structurizr/view/View.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/structurizr-core/src/com/structurizr/view/View.java b/structurizr-core/src/com/structurizr/view/View.java index 984af725f..775c6c193 100644 --- a/structurizr-core/src/com/structurizr/view/View.java +++ b/structurizr-core/src/com/structurizr/view/View.java @@ -272,8 +272,8 @@ protected RelationshipView addRelationship(Relationship relationship) { return null; } - protected boolean isElementInView(Element element) { - return this.elementViews.stream().filter(ev -> ev.getElement().equals(element)).count() > 0; + public boolean isElementInView(Element element) { + return this.elementViews.stream().anyMatch(ev -> ev.getElement().equals(element)); } protected RelationshipView addRelationship(Relationship relationship, String description, String order) { From 5083ca272c9b343acfd8a776b6777d73a051da00 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 26 Mar 2020 12:03:40 +0000 Subject: [PATCH 089/717] Set some default element style dimensions. --- .../src/com/structurizr/view/Styles.java | 22 ++++++++++++ .../unit/com/structurizr/view/StyleTests.java | 36 ++++++++++++++++++- 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/structurizr-core/src/com/structurizr/view/Styles.java b/structurizr-core/src/com/structurizr/view/Styles.java index ad38ad420..05cb1fb01 100644 --- a/structurizr-core/src/com/structurizr/view/Styles.java +++ b/structurizr-core/src/com/structurizr/view/Styles.java @@ -9,6 +9,12 @@ public final class Styles { + private static final Integer DEFAULT_WIDTH_OF_ELEMENT = 450; + private static final Integer DEFAULT_HEIGHT_OF_ELEMENT = 300; + + private static final Integer DEFAULT_WIDTH_OF_PERSON = 400; + private static final Integer DEFAULT_HEIGHT_OF_PERSON = 400; + private Collection elements = new LinkedList<>(); private Collection relationships = new LinkedList<>(); @@ -148,6 +154,22 @@ public ElementStyle findElementStyle(Element element) { } } + if (style.getWidth() == null) { + if (style.getShape() == Shape.Person) { + style.setWidth(DEFAULT_WIDTH_OF_PERSON); + } else { + style.setWidth(DEFAULT_WIDTH_OF_ELEMENT); + } + } + + if (style.getHeight() == null) { + if (style.getShape() == Shape.Person) { + style.setHeight(DEFAULT_HEIGHT_OF_PERSON); + } else { + style.setHeight(DEFAULT_HEIGHT_OF_ELEMENT); + } + } + return style; } diff --git a/structurizr-core/test/unit/com/structurizr/view/StyleTests.java b/structurizr-core/test/unit/com/structurizr/view/StyleTests.java index 165ea6c09..e5ef46cfd 100644 --- a/structurizr-core/test/unit/com/structurizr/view/StyleTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/StyleTests.java @@ -19,6 +19,8 @@ public void test_findElementStyle_ReturnsTheDefaultStyle_WhenPassedNull() { assertEquals("#dddddd", style.getBackground()); assertEquals("#000000", style.getColor()); assertEquals(Shape.Box, style.getShape()); + assertEquals(new Integer(450), style.getWidth()); + assertEquals(new Integer(300), style.getHeight()); } @Test @@ -28,6 +30,8 @@ public void test_findElementStyle_ReturnsTheDefaultStyle_WhenNoStylesAreDefined( assertEquals("#dddddd", style.getBackground()); assertEquals("#000000", style.getColor()); assertEquals(Shape.Box, style.getShape()); + assertEquals(new Integer(450), style.getWidth()); + assertEquals(new Integer(300), style.getHeight()); } @Test @@ -36,13 +40,43 @@ public void test_findElementStyle_ReturnsTheCorrectStyle_WhenStylesAreDefined() element.addTags("Some Tag"); styles.addElementStyle(Tags.SOFTWARE_SYSTEM).background("#ff0000").color("#ffffff"); - styles.addElementStyle("Some Tag").color("#0000ff").stroke("#00ff00").shape(Shape.RoundedBox); + styles.addElementStyle("Some Tag").color("#0000ff").stroke("#00ff00").shape(Shape.RoundedBox).width(123).height(456); ElementStyle style = styles.findElementStyle(element); assertEquals("#ff0000", style.getBackground()); assertEquals("#0000ff", style.getColor()); assertEquals("#00ff00", style.getStroke()); assertEquals(Shape.RoundedBox, style.getShape()); + assertEquals(new Integer(123), style.getWidth()); + assertEquals(new Integer(456), style.getHeight()); + } + + @Test + public void test_findElementStyle_ReturnsTheDefaultElementSize_WhenTheShapeIsABox() { + SoftwareSystem element = model.addSoftwareSystem("Name", "Description"); + element.addTags("Some Tag"); + + styles.addElementStyle(Tags.SOFTWARE_SYSTEM).background("#ff0000").color("#ffffff"); + styles.addElementStyle("Some Tag").shape(Shape.Box); + + ElementStyle style = styles.findElementStyle(element); + assertEquals(Shape.Box, style.getShape()); + assertEquals(new Integer(450), style.getWidth()); + assertEquals(new Integer(300), style.getHeight()); + } + + @Test + public void test_findElementStyle_ReturnsTheDefaultElementSize_WhenTheShapeIsAPerson() { + SoftwareSystem element = model.addSoftwareSystem("Name", "Description"); + element.addTags("Some Tag"); + + styles.addElementStyle(Tags.SOFTWARE_SYSTEM).background("#ff0000").color("#ffffff"); + styles.addElementStyle("Some Tag").shape(Shape.Person); + + ElementStyle style = styles.findElementStyle(element); + assertEquals(Shape.Person, style.getShape()); + assertEquals(new Integer(400), style.getWidth()); + assertEquals(new Integer(400), style.getHeight()); } @Test From 66b78802f4fb356e82d00260e2444bd199251423 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 26 Mar 2020 14:11:04 +0000 Subject: [PATCH 090/717] Added release date. --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 0842749e6..70b34e14e 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 1.3.5 (unreleased) +## 1.3.5 (26th March 2020) - Added an externalSoftwareSystemBoundariesVisible property to ContainerView, to set whether software system boundaries should be visible for "external" containers (those outside the software system in scope). - Added a 16:10 ratio paper size. From 0eb001a1590fb9db7afd8cc0c70c5870c7fffa14 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 26 Mar 2020 14:19:17 +0000 Subject: [PATCH 091/717] Updated client library versions. --- docs/binaries.md | 4 ++-- docs/getting-started.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/binaries.md b/docs/binaries.md index fe5aa933a..44ad85d80 100644 --- a/docs/binaries.md +++ b/docs/binaries.md @@ -3,5 +3,5 @@ The "Structurizr for Java" binaries are hosted on [Maven Central](https://repo1. Name | Description ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-core:1.3.4 | The core library that can used to create software architecture models. -com.structurizr:structurizr-client:1.3.4 | The API client for publishing models on the Structurizr cloud service and on-premises installation. \ No newline at end of file +com.structurizr:structurizr-core:1.3.5 | The core library that can used to create software architecture models. +com.structurizr:structurizr-client:1.3.5 | The API client for publishing models on the Structurizr cloud service and on-premises installation. \ No newline at end of file diff --git a/docs/getting-started.md b/docs/getting-started.md index 95f588e90..7c84c5df4 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -12,7 +12,7 @@ The Structurizr for Java binaries are hosted on [Maven Central](https://repo1.ma Name | Description ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-client:1.3.4 | The Structurizr API client library. +com.structurizr:structurizr-client:1.3.5 | The Structurizr API client library. ## 2. Create a Java program From f6806d7a3afc1c154eb466133cb402861fa1cf72 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 26 Mar 2020 15:07:07 +0000 Subject: [PATCH 092/717] Doc updates. --- README.md | 1 + docs/getting-started.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7e261a546..d78b2da89 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ The view can then be exported to be visualised using the [Structurizr service](h * [Styling elements](docs/styling-elements.md) * [Styling relationships](docs/styling-relationships.md) * [Filtered views](docs/filtered-views.md) + * [Graphviz automatic layout](https://github.com/structurizr/java-extensions/blob/master/docs/graphviz.md) * Documentation * [Documentation overview](docs/documentation.md) * [Structurizr](docs/documentation-structurizr.md) diff --git a/docs/getting-started.md b/docs/getting-started.md index 7c84c5df4..de3153924 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -83,7 +83,7 @@ Once you've run your program to create and upload the workspace, you can now sig ![Getting Started with Structurizr for Java](images/getting-started-1.png) -By default, Structurizr does not auto-layout your diagram elements. The diagram layout can be modified by dragging the elements around the diagram canvas in the diagram editor, and the layout saved using the "Save workspace" button. See [Structurizr - Help - Diagram layout](https://structurizr.com/help/diagram-editor) for more information. +By default, Structurizr does not auto-layout your diagram elements. The diagram layout can be modified by dragging the elements around the diagram canvas in the diagram editor, and the layout saved using the "Save workspace" button. See [Structurizr - Help - Diagram editor](https://structurizr.com/help/diagram-editor) for more information. ![Getting Started with Structurizr for Java](images/getting-started-2.png) From f70754b8c0992ee64fb8e816027f006a9e15f583 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 27 Mar 2020 22:02:09 +0000 Subject: [PATCH 093/717] Fixes #149. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d78b2da89..d40317519 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ The view can then be exported to be visualised using the [Structurizr service](h * Introduction * [Getting started](docs/getting-started.md) * [About Structurizr and how it compares to other tooling](https://structurizr.com/help/about) - * [Why use code?](https://structurizr.com/help/why-use-code) + * [Why use code?](https://structurizr.com/help/code) * [Basic concepts](https://structurizr.com/help/concepts) (workspaces, models, views and documentation) * [C4 model](https://c4model.com) * [Examples](https://structurizr.com/help/examples) From c4c56a91abf26a488260c18cb14a742e12d6e3c4 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 30 Mar 2020 13:26:48 +0100 Subject: [PATCH 094/717] Dates need to be formatted as UTC. --- .../src/com/structurizr/io/json/AbstractJsonWriter.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/structurizr-client/src/com/structurizr/io/json/AbstractJsonWriter.java b/structurizr-client/src/com/structurizr/io/json/AbstractJsonWriter.java index 0368b3861..689039c0e 100644 --- a/structurizr-client/src/com/structurizr/io/json/AbstractJsonWriter.java +++ b/structurizr-client/src/com/structurizr/io/json/AbstractJsonWriter.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.SerializationFeature; import java.text.SimpleDateFormat; +import java.util.TimeZone; class AbstractJsonWriter { @@ -20,7 +21,9 @@ ObjectMapper createObjectMapper(boolean indentOutput) { objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); - objectMapper.setDateFormat(new SimpleDateFormat(AbstractJsonWriter.ISO_DATE_TIME_FORMAT)); + SimpleDateFormat sdf = new SimpleDateFormat(AbstractJsonWriter.ISO_DATE_TIME_FORMAT); + sdf.setTimeZone(TimeZone.getTimeZone("UTC")); + objectMapper.setDateFormat(sdf); return objectMapper; } From a4f45a8aa947f56219bc1f8246157e7ae1800773 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 30 Mar 2020 22:36:07 +0100 Subject: [PATCH 095/717] Added some methods to remove views and documentation. --- .../structurizr/documentation/Documentation.java | 10 ++++++++++ .../src/com/structurizr/view/ViewSet.java | 14 ++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/structurizr-core/src/com/structurizr/documentation/Documentation.java b/structurizr-core/src/com/structurizr/documentation/Documentation.java index 553e19ad3..082e49fe5 100644 --- a/structurizr-core/src/com/structurizr/documentation/Documentation.java +++ b/structurizr-core/src/com/structurizr/documentation/Documentation.java @@ -245,6 +245,16 @@ public boolean isEmpty() { return sections.isEmpty() && images.isEmpty() && decisions.isEmpty(); } + /** + * Removes all documentation, decisions, and images. + */ + public void clear() { + sections = new HashSet<>(); + decisions = new HashSet<>(); + images = new HashSet<>(); + template = null; + } + /** * Gets the template metadata associated with this documentation. * diff --git a/structurizr-core/src/com/structurizr/view/ViewSet.java b/structurizr-core/src/com/structurizr/view/ViewSet.java index ab4dc0805..da29af4b1 100644 --- a/structurizr-core/src/com/structurizr/view/ViewSet.java +++ b/structurizr-core/src/com/structurizr/view/ViewSet.java @@ -678,4 +678,18 @@ public boolean isEmpty() { return systemLandscapeViews.isEmpty() && systemContextViews.isEmpty() && containerViews.isEmpty() && componentViews.isEmpty() && dynamicViews.isEmpty() && deploymentViews.isEmpty() && filteredViews.isEmpty(); } + /** + * Removes all views and configuration. + */ + public void clear() { + systemLandscapeViews = new HashSet<>(); + systemContextViews = new HashSet<>(); + containerViews = new HashSet<>(); + componentViews = new HashSet<>(); + dynamicViews = new HashSet<>(); + deploymentViews = new HashSet<>(); + filteredViews = new HashSet<>(); + configuration = new Configuration(); + } + } \ No newline at end of file From f932fab3967f507436bbf5a57dbfa07b6562606b Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 30 Mar 2020 23:25:59 +0100 Subject: [PATCH 096/717] Adds an originId property, which is used for workspace linking on the Structurizr cloud service/on-premises installation. --- .../src/com/structurizr/model/ModelItem.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/structurizr-core/src/com/structurizr/model/ModelItem.java b/structurizr-core/src/com/structurizr/model/ModelItem.java index 0ad9d3039..9db11faa7 100644 --- a/structurizr-core/src/com/structurizr/model/ModelItem.java +++ b/structurizr-core/src/com/structurizr/model/ModelItem.java @@ -11,6 +11,7 @@ abstract class ModelItem { private String id = ""; + private String originId = ""; private Set tags = new LinkedHashSet<>(); private Map properties = new HashMap<>(); private Set perspectives = new HashSet<>(); @@ -30,6 +31,14 @@ void setId(String id) { this.id = id; } + public String getOriginId() { + return originId; + } + + void setOriginId(String originId) { + this.originId = originId; + } + /** * Gets the comma separated list of tags. * From 4ab780386bb8ff235579ca82f35a78b47c87c5b0 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 30 Mar 2020 23:26:24 +0100 Subject: [PATCH 097/717] Bump the version number. --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index c082d36da..e326fecef 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.3.5' + version = '1.3.6' repositories { mavenCentral() From 5d1380c6e1d8aac2720042c769ba12d8767d6e1d Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 14 Apr 2020 10:41:05 +0100 Subject: [PATCH 098/717] Added a docs page to explain the new implied relationships functionality. --- docs/implied-relationships.md | 91 +++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 docs/implied-relationships.md diff --git a/docs/implied-relationships.md b/docs/implied-relationships.md new file mode 100644 index 000000000..58878820c --- /dev/null +++ b/docs/implied-relationships.md @@ -0,0 +1,91 @@ +# Implied relationships + +By default, the Structurizr for Java client library will not create implied relationships. For example, let's say that you have two components in different containers, and you create a relationship between them. + +``` +SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); +Container container1 = softwareSystem.addContainer("Container 1", "", ""); +Component component1 = container1.addComponent("Component 1", "", ""); + +Container container2 = softwareSystem.addContainer("Container 2", "", ""); +Component component2 = container2.addComponent("Component 2", "", ""); + +component1.uses(component2, "Sends data X to"); +``` + +At this point, the model contains a single relationship between the two components, but there are three other implied relationships that could be added: + +- Component 1 Sends data X to Container 2 +- Container 1 Sends data X to Component 2 +- Container 1 Sends data X to Container 2 + +To have the client library create these for you, set a ```ImpliedRelationshipsStrategy``` on your model. Possible implementations are as follows. + +## DefaultImpliedRelationshipsStrategy + +This strategy does not create any implied relationships. + +``` +SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); +Container container1 = softwareSystem.addContainer("Container 1", "", ""); +Component component1 = container1.addComponent("Component 1", "", ""); + +Container container2 = softwareSystem.addContainer("Container 2", "", ""); +Component component2 = container2.addComponent("Component 2", "", ""); + +model.setImpliedRelationshipsStrategy(new DefaultImpliedRelationshipsStrategy()); // default +component1.uses(component2, "Sends data X to"); +``` + +Relationships that exist in the model: + +- Component 1 Sends data X to Component 2 + +## CreateImpliedRelationshipUnlessSameRelationshipExistsStrategy + +This strategy creates implied relationships between all valid combinations of the parent elements, unless the same relationship already exists between them. + +``` +SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); +Container container1 = softwareSystem.addContainer("Container 1", "", ""); +Component component1 = container1.addComponent("Component 1", "", ""); + +Container container2 = softwareSystem.addContainer("Container 2", "", ""); +Component component2 = container2.addComponent("Component 2", "", ""); + +model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipUnlessAnyRelationshipExistsStrategy()); +component1.uses(component2, "Sends data X to"); +``` + +Relationships that exist in the model: + +- Component 1 Sends data X to Component 2 +- Component 1 Sends data X to Container 2 +- Container 1 Sends data X to Component 2 +- Container 1 Sends data X to Container 2 + +## CreateImpliedRelationshipUnlessAnyRelationshipExistsStrategy + +This strategy creates implied relationships between all valid combinations of the parent elements, unless *any* relationship already exists between them. + +``` +SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); +Container container1 = softwareSystem.addContainer("Container 1", "", ""); +Component component1 = container1.addComponent("Component 1", "", ""); + +Container container2 = softwareSystem.addContainer("Container 2", "", ""); +Component component2 = container2.addComponent("Component 2", "", ""); + +model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipUnlessAnyRelationshipExistsStrategy()); +container1.uses(container2, "Sends data to"); +component1.uses(component2, "Sends data X to"); +``` + +Relationships that exist in the model: + +- Component 1 Sends data X to Component 2 +- Component 1 Sends data X to Container 2 +- Container 1 Sends data X to Component 2 +- Container 1 Sends data to Container 2 + +This strategy is useful when you want to show a summary relationship at higher levels in the model, especially when multiple implied relationships could be created between elements. \ No newline at end of file From 3169a7ff6f9ed975038f2b0b7a7ca13ecb3c2690 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 14 Apr 2020 11:07:56 +0100 Subject: [PATCH 099/717] Updated docs. --- docs/images/implied-relationships-1.png | Bin 0 -> 54035 bytes docs/implied-relationships.md | 14 ++++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) create mode 100644 docs/images/implied-relationships-1.png diff --git a/docs/images/implied-relationships-1.png b/docs/images/implied-relationships-1.png new file mode 100644 index 0000000000000000000000000000000000000000..dd2f86f08ff5bafdb2bdd3576ee4d291ef48204c GIT binary patch literal 54035 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 Ii0S$N4?l6|?*IS* literal 0 HcmV?d00001 diff --git a/docs/implied-relationships.md b/docs/implied-relationships.md index 58878820c..baff56730 100644 --- a/docs/implied-relationships.md +++ b/docs/implied-relationships.md @@ -15,11 +15,13 @@ component1.uses(component2, "Sends data X to"); At this point, the model contains a single relationship between the two components, but there are three other implied relationships that could be added: -- Component 1 Sends data X to Container 2 +![Implied relationships](images/implied-relationships-1.png) + - Container 1 Sends data X to Component 2 +- Component 1 Sends data X to Container 2 - Container 1 Sends data X to Container 2 -To have the client library create these for you, set a ```ImpliedRelationshipsStrategy``` on your model. Possible implementations are as follows. +To have the client library create these for you, set an ```ImpliedRelationshipsStrategy``` implementation on your model. Possible implementations are as follows. ## DefaultImpliedRelationshipsStrategy @@ -41,7 +43,7 @@ Relationships that exist in the model: - Component 1 Sends data X to Component 2 -## CreateImpliedRelationshipUnlessSameRelationshipExistsStrategy +## CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategy This strategy creates implied relationships between all valid combinations of the parent elements, unless the same relationship already exists between them. @@ -53,7 +55,7 @@ Component component1 = container1.addComponent("Component 1", "", ""); Container container2 = softwareSystem.addContainer("Container 2", "", ""); Component component2 = container2.addComponent("Component 2", "", ""); -model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipUnlessAnyRelationshipExistsStrategy()); +model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); component1.uses(component2, "Sends data X to"); ``` @@ -64,7 +66,7 @@ Relationships that exist in the model: - Container 1 Sends data X to Component 2 - Container 1 Sends data X to Container 2 -## CreateImpliedRelationshipUnlessAnyRelationshipExistsStrategy +## CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy This strategy creates implied relationships between all valid combinations of the parent elements, unless *any* relationship already exists between them. @@ -76,7 +78,7 @@ Component component1 = container1.addComponent("Component 1", "", ""); Container container2 = softwareSystem.addContainer("Container 2", "", ""); Component component2 = container2.addComponent("Component 2", "", ""); -model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipUnlessAnyRelationshipExistsStrategy()); +model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); container1.uses(container2, "Sends data to"); component1.uses(component2, "Sends data X to"); ``` From 66f45cb3cd32e6e9fce2b9d32d36e97921b391ea Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 19 Apr 2020 12:39:41 +0100 Subject: [PATCH 100/717] Added a "Component" shape. --- structurizr-core/src/com/structurizr/view/Shape.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/structurizr-core/src/com/structurizr/view/Shape.java b/structurizr-core/src/com/structurizr/view/Shape.java index 10eb0f452..51c5e8918 100644 --- a/structurizr-core/src/com/structurizr/view/Shape.java +++ b/structurizr-core/src/com/structurizr/view/Shape.java @@ -14,6 +14,7 @@ public enum Shape { Folder, WebBrowser, MobileDevicePortrait, - MobileDeviceLandscape + MobileDeviceLandscape, + Component } From 1fbd7a506b7df482b7ae5f7650d31a3ad144835a Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 19 Apr 2020 12:40:05 +0100 Subject: [PATCH 101/717] Added a "Dotted" border style. --- structurizr-core/src/com/structurizr/view/Border.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/structurizr-core/src/com/structurizr/view/Border.java b/structurizr-core/src/com/structurizr/view/Border.java index fd92bc94c..b834be030 100644 --- a/structurizr-core/src/com/structurizr/view/Border.java +++ b/structurizr-core/src/com/structurizr/view/Border.java @@ -3,6 +3,7 @@ public enum Border { Solid, - Dashed + Dashed, + Dotted } From faf3f14135d490c7d46196a390dc86a2faef5550 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 19 Apr 2020 12:42:42 +0100 Subject: [PATCH 102/717] Updated changelog. --- docs/changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 70b34e14e..756ca97ee 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,10 @@ # Changelog +## 1.3.6 (unreleased) + +- Added a "Component" element shape. +- Added a "Dotted" element border style. + ## 1.3.5 (26th March 2020) - Added an externalSoftwareSystemBoundariesVisible property to ContainerView, to set whether software system boundaries should be visible for "external" containers (those outside the software system in scope). From 24986b4f011c7115c1489046d2c6051a5a02648f Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 26 Apr 2020 13:07:21 +0100 Subject: [PATCH 103/717] Components from any container can now be added to a component view. --- docs/changelog.md | 1 + structurizr-core/src/com/structurizr/view/ComponentView.java | 4 ---- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 756ca97ee..29e34f06c 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -4,6 +4,7 @@ - Added a "Component" element shape. - Added a "Dotted" element border style. +- Components from any container can now be added to a component view. ## 1.3.5 (26th March 2020) diff --git a/structurizr-core/src/com/structurizr/view/ComponentView.java b/structurizr-core/src/com/structurizr/view/ComponentView.java index 0b2418903..46b21c7f9 100644 --- a/structurizr-core/src/com/structurizr/view/ComponentView.java +++ b/structurizr-core/src/com/structurizr/view/ComponentView.java @@ -138,10 +138,6 @@ public void add(Component component) { */ public void add(Component component, boolean addRelationships) { if (component != null) { - if (!component.getContainer().equals(getContainer())) { - throw new IllegalArgumentException("Only components belonging to " + container.getName() + " can be added to this view."); - } - addElement(component, addRelationships); } } From 0cbb277de56a549a61dba62f41047ebfe7c97e9d Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 26 Apr 2020 13:07:42 +0100 Subject: [PATCH 104/717] Added an externalContainersBoundariesVisible property to ComponentView, to set whether container boundaries should be visible for "external" components (those outside the container in scope). --- .../com/structurizr/view/ComponentView.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/structurizr-core/src/com/structurizr/view/ComponentView.java b/structurizr-core/src/com/structurizr/view/ComponentView.java index 46b21c7f9..86666aa2a 100644 --- a/structurizr-core/src/com/structurizr/view/ComponentView.java +++ b/structurizr-core/src/com/structurizr/view/ComponentView.java @@ -15,6 +15,8 @@ public final class ComponentView extends StaticView { private Container container; private String containerId; + private boolean externalContainerBoundariesVisible = false; + ComponentView() { } @@ -261,4 +263,22 @@ protected boolean canBeRemoved(Element element) { return true; } + /** + * Determines whether container boundaries should be visible for "external" components (those outside the container in scope). + * + * @return true if external container boundaries are visible, false otherwise + */ + public boolean getExternalContainerBoundariesVisible() { + return externalContainerBoundariesVisible; + } + + /** + * Sets whether container boundaries should be visible for "external" components (those outside the container in scope). + * + * @param externalContainerBoundariesVisible true if external container boundaries should be visible, false otherwise + */ + public void setExternalSoftwareSystemBoundariesVisible(boolean externalContainerBoundariesVisible) { + this.externalContainerBoundariesVisible = externalContainerBoundariesVisible; + } + } \ No newline at end of file From 6d682704d0e4ab191b752f296dd982ed0644d011 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 26 Apr 2020 13:39:38 +0100 Subject: [PATCH 105/717] Updated changelog. --- docs/changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.md b/docs/changelog.md index 29e34f06c..4b1416d2c 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -5,6 +5,7 @@ - Added a "Component" element shape. - Added a "Dotted" element border style. - Components from any container can now be added to a component view. +- Added an externalContainersBoundariesVisible property to ComponentView, to set whether container boundaries should be visible for "external" components (those outside the container in scope). ## 1.3.5 (26th March 2020) From 15bc0e760431111526f96389d0cb9a1295eee3eb Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 28 Apr 2020 11:50:11 +0100 Subject: [PATCH 106/717] Improved the support for creating implied relationships. --- README.md | 1 + docs/changelog.md | 1 + .../AbstractImpliedRelationshipsStrategy.java | 33 +++++++ ...psUnlessAnyRelationshipExistsStrategy.java | 34 ++++++++ ...sUnlessSameRelationshipExistsStrategy.java | 34 ++++++++ .../DefaultImpliedRelationshipsStrategy.java | 13 +++ .../src/com/structurizr/model/Element.java | 60 +++++++++++++ .../model/ImpliedRelationshipsStrategy.java | 17 ++++ .../src/com/structurizr/model/Model.java | 43 +++++++++ ...essAnyRelationshipExistsStrategyTests.java | 87 +++++++++++++++++++ ...ssSameRelationshipExistsStrategyTests.java | 59 +++++++++++++ ...aultImpliedRelationshipsStrategyTests.java | 31 +++++++ .../com/structurizr/model/ElementTests.java | 24 +++++ .../com/structurizr/model/ModelTests.java | 17 ++++ .../structurizr/view/ContainerViewTests.java | 2 +- .../com/structurizr/example/BigBankPlc.java | 3 +- 16 files changed, 456 insertions(+), 3 deletions(-) create mode 100644 structurizr-core/src/com/structurizr/model/AbstractImpliedRelationshipsStrategy.java create mode 100644 structurizr-core/src/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy.java create mode 100644 structurizr-core/src/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategy.java create mode 100644 structurizr-core/src/com/structurizr/model/DefaultImpliedRelationshipsStrategy.java create mode 100644 structurizr-core/src/com/structurizr/model/ImpliedRelationshipsStrategy.java create mode 100644 structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategyTests.java create mode 100644 structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategyTests.java create mode 100644 structurizr-core/test/unit/com/structurizr/model/DefaultImpliedRelationshipsStrategyTests.java diff --git a/README.md b/README.md index d40317519..d258b90bb 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,7 @@ The view can then be exported to be visualised using the [Structurizr service](h * [Usage patterns](docs/usage-patterns.md) * Model * [Creating your model](docs/model.md) + * [Implied relationships](docs/implied-relationships.md) * Views * [Creating views](docs/views.md) * [System Context diagram](docs/system-context-diagram.md) diff --git a/docs/changelog.md b/docs/changelog.md index 4b1416d2c..ed8c81d2e 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -6,6 +6,7 @@ - Added a "Dotted" element border style. - Components from any container can now be added to a component view. - Added an externalContainersBoundariesVisible property to ComponentView, to set whether container boundaries should be visible for "external" components (those outside the container in scope). +- Improved the support for creating [implied relationships](docs/implied-relationships.md). ## 1.3.5 (26th March 2020) diff --git a/structurizr-core/src/com/structurizr/model/AbstractImpliedRelationshipsStrategy.java b/structurizr-core/src/com/structurizr/model/AbstractImpliedRelationshipsStrategy.java new file mode 100644 index 000000000..10391fa1d --- /dev/null +++ b/structurizr-core/src/com/structurizr/model/AbstractImpliedRelationshipsStrategy.java @@ -0,0 +1,33 @@ +package com.structurizr.model; + +/** + * Abstract base class for supplied ImpliedRelationshipsStrategy implementations. + */ +public abstract class AbstractImpliedRelationshipsStrategy implements ImpliedRelationshipsStrategy { + + protected boolean impliedRelationshipIsAllowed(Element source, Element destination) { + if (source.equals(destination)) { + return false; + } + + return !(isChildOf(source, destination) || isChildOf(destination, source)); + } + + private boolean isChildOf(Element e1, Element e2) { + if (e1 instanceof Person || e2 instanceof Person) { + return false; + } + + Element parent = e2.getParent(); + while (parent != null) { + if (parent.getId().equals(e1.getId())) { + return true; + } + + parent = parent.getParent(); + } + + return false; + } + +} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy.java b/structurizr-core/src/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy.java new file mode 100644 index 000000000..0d03b5c55 --- /dev/null +++ b/structurizr-core/src/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy.java @@ -0,0 +1,34 @@ +package com.structurizr.model; + +/** + * This strategy creates implied relationships between all valid combinations of the parent elements, + * unless any relationship already exists between them. + */ +public class CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy extends AbstractImpliedRelationshipsStrategy { + + @Override + public void createImpliedRelationships(Relationship relationship) { + Element source = relationship.getSource(); + Element destination = relationship.getDestination(); + + Model model = source.getModel(); + + while (source != null) { + while (destination != null) { + if (impliedRelationshipIsAllowed(source, destination)) { + boolean createRelationship = !source.hasEfferentRelationshipWith(destination); + + if (createRelationship) { + model.addRelationship(source, destination, relationship.getDescription(), relationship.getTechnology(), relationship.getInteractionStyle(), false); + } + } + + destination = destination.getParent(); + } + + destination = relationship.getDestination(); + source = source.getParent(); + } + } + +} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategy.java b/structurizr-core/src/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategy.java new file mode 100644 index 000000000..251ec046e --- /dev/null +++ b/structurizr-core/src/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategy.java @@ -0,0 +1,34 @@ +package com.structurizr.model; + +/** + * This strategy creates implied relationships between all valid combinations of the parent elements, + * unless the same relationship already exists between them. + */ +public class CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategy extends AbstractImpliedRelationshipsStrategy { + + @Override + public void createImpliedRelationships(Relationship relationship) { + Element source = relationship.getSource(); + Element destination = relationship.getDestination(); + + Model model = source.getModel(); + + while (source != null) { + while (destination != null) { + if (impliedRelationshipIsAllowed(source, destination)) { + boolean createRelationship = !source.hasEfferentRelationshipWith(destination, relationship.getDescription()); + + if (createRelationship) { + model.addRelationship(source, destination, relationship.getDescription(), relationship.getTechnology(), relationship.getInteractionStyle(), false); + } + } + + destination = destination.getParent(); + } + + destination = relationship.getDestination(); + source = source.getParent(); + } + } + +} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/model/DefaultImpliedRelationshipsStrategy.java b/structurizr-core/src/com/structurizr/model/DefaultImpliedRelationshipsStrategy.java new file mode 100644 index 000000000..82e0d06b5 --- /dev/null +++ b/structurizr-core/src/com/structurizr/model/DefaultImpliedRelationshipsStrategy.java @@ -0,0 +1,13 @@ +package com.structurizr.model; + +/** + * The default strategy is to NOT create implied relationships. + */ +public class DefaultImpliedRelationshipsStrategy extends AbstractImpliedRelationshipsStrategy { + + @Override + public void createImpliedRelationships(Relationship relationship) { + // do nothing + } + +} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/model/Element.java b/structurizr-core/src/com/structurizr/model/Element.java index 02df5657f..6d6efd6b1 100644 --- a/structurizr-core/src/com/structurizr/model/Element.java +++ b/structurizr-core/src/com/structurizr/model/Element.java @@ -147,6 +147,19 @@ public boolean hasEfferentRelationshipWith(Element element) { return getEfferentRelationshipWith(element) != null; } + /** + * Determines whether this element has an efferent (outgoing) relationship with + * the specified element and description. + * + * @param element the element to look for + * @param description the relationship description + * @return true if this element has an efferent relationship with the specified element and description, + * false otherwise + */ + public boolean hasEfferentRelationshipWith(Element element, String description) { + return getEfferentRelationshipWith(element, description) != null; + } + /** * Gets the efferent (outgoing) relationship with the specified element. * @@ -167,6 +180,53 @@ public Relationship getEfferentRelationshipWith(Element element) { return null; } + /** + * Gets the efferent (outgoing) relationship with the specified element. + * + * @param element the element to look for + * @return a Set of Relationship objects; empty if no relationships exist + */ + public Set getEfferentRelationshipsWith(Element element) { + Set set = new HashSet<>(); + + if (element != null) { + for (Relationship relationship : relationships) { + if (relationship.getDestination().equals(element)) { + set.add(relationship); + } + } + } + + return set; + } + + /** + * Determines whether this element has an efferent (outgoing) relationship with + * the specified element and description. + * + * @param element the element to look for + * @param description the relationship description + * @return true if this element has an efferent relationship with the specified element and description, + * false otherwise + */ + public Relationship getEfferentRelationshipWith(Element element, String description) { + if (element == null) { + return null; + } + + if (description == null) { + description = ""; + } + + for (Relationship relationship : relationships) { + if (relationship.getDestination().equals(element) && relationship.getDescription().equals(description)) { + return relationship; + } + } + + return null; + } + boolean has(Relationship relationship) { return relationships.stream().anyMatch(r -> r.getDestination().equals(relationship.getDestination()) && r.getDescription().equalsIgnoreCase(relationship.getDescription())); } diff --git a/structurizr-core/src/com/structurizr/model/ImpliedRelationshipsStrategy.java b/structurizr-core/src/com/structurizr/model/ImpliedRelationshipsStrategy.java new file mode 100644 index 000000000..f192d2132 --- /dev/null +++ b/structurizr-core/src/com/structurizr/model/ImpliedRelationshipsStrategy.java @@ -0,0 +1,17 @@ +package com.structurizr.model; + +/** + * Defines the interface for strategies to create implied relationships in the model, + * after a relationship has been created. + */ +public interface ImpliedRelationshipsStrategy { + + /** + * Called after a relationship has been created in the model, + * providing an opportunity to create any resulting implied relationships. + * + * @param relationship the newly created Relationship + */ + void createImpliedRelationships(Relationship relationship); + +} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/model/Model.java b/structurizr-core/src/com/structurizr/model/Model.java index 8b09f8d2f..b0896400c 100644 --- a/structurizr-core/src/com/structurizr/model/Model.java +++ b/structurizr-core/src/com/structurizr/model/Model.java @@ -24,6 +24,8 @@ public final class Model { private Set softwareSystems = new LinkedHashSet<>(); private Set deploymentNodes = new LinkedHashSet<>(); + private ImpliedRelationshipsStrategy impliedRelationshipsStrategy = new DefaultImpliedRelationshipsStrategy(); + Model() { } @@ -171,6 +173,11 @@ Component addComponentOfType(Container parent, String name, String type, String @Nullable Relationship addRelationship(Element source, @Nonnull Element destination, String description, String technology, InteractionStyle interactionStyle) { + return addRelationship(source, destination, description, technology, interactionStyle, true); + } + + @Nullable + Relationship addRelationship(Element source, @Nonnull Element destination, String description, String technology, InteractionStyle interactionStyle, boolean createImpliedRelationships) { if (destination == null) { throw new IllegalArgumentException("The destination must be specified."); } @@ -181,6 +188,17 @@ Relationship addRelationship(Element source, @Nonnull Element destination, Strin Relationship relationship = new Relationship(source, destination, description, technology, interactionStyle); if (addRelationship(relationship)) { + + if (createImpliedRelationships) { + if + ( + (source instanceof Person || source instanceof SoftwareSystem || source instanceof Container || source instanceof Component) && + (destination instanceof Person || destination instanceof SoftwareSystem || destination instanceof Container || destination instanceof Component) + ) { + impliedRelationshipsStrategy.createImpliedRelationships(relationship); + } + } + return relationship; } @@ -530,8 +548,10 @@ public Person getPersonWithName(@Nonnull String name) { * additional implied relationships to the model: AAA->BB AAA-->B AA->BBB AA->BB AA->B A->BBB A->BB A->B.

    * * @return a set of all implicit relationships that were added to the model + * @deprecated use {@link #setImpliedRelationshipsStrategy(ImpliedRelationshipsStrategy)} ()} instead to set a strategy, before creating relationships */ @Nonnull + @Deprecated public Set addImplicitRelationships() { Set implicitRelationships = new HashSet<>(); @@ -892,4 +912,27 @@ public void modifyRelationship(Relationship relationship, String description, St } } + /** + * Gets the strategy in use for creating implied relationships. + * + * @return an ImpliedRelationshipStrategy implementation + */ + @JsonIgnore + public ImpliedRelationshipsStrategy getImpliedRelationshipsStrategy() { + return impliedRelationshipsStrategy; + } + + /** + * Sets the strategy is use for creating implied relationships. + * + * @param impliedRelationshipStrategy an ImpliedRelationshipStrategy implementation + */ + public void setImpliedRelationshipsStrategy(ImpliedRelationshipsStrategy impliedRelationshipStrategy) { + if (impliedRelationshipStrategy != null) { + this.impliedRelationshipsStrategy = impliedRelationshipStrategy; + } else { + this.impliedRelationshipsStrategy = new DefaultImpliedRelationshipsStrategy(); + } + } + } \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategyTests.java b/structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategyTests.java new file mode 100644 index 000000000..08255fec0 --- /dev/null +++ b/structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategyTests.java @@ -0,0 +1,87 @@ +package com.structurizr.model; + +import com.structurizr.AbstractWorkspaceTestBase; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategyTests extends AbstractWorkspaceTestBase { + + @Test + public void test_impliedRelationshipsAreCreated() { + SoftwareSystem a = model.addSoftwareSystem("A", ""); + Container aa = a.addContainer("AA", "", ""); + Component aaa = aa.addComponent("AAA", "", ""); + + SoftwareSystem b = model.addSoftwareSystem("B", ""); + Container bb = b.addContainer("BB", "", ""); + Component bbb = bb.addComponent("BBB", "", ""); + + model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); + + aaa.uses(bbb, "Uses 1"); + + assertEquals(9, model.getRelationships().size()); + assertTrue(aaa.hasEfferentRelationshipWith(bbb, "Uses 1")); + + // AAA->BBB implies AAA->BB AAA->B AA->BBB AA->BB AA->B A->BBB A->BB A->B + assertTrue(aaa.hasEfferentRelationshipWith(bb, "Uses 1")); + assertTrue(aaa.hasEfferentRelationshipWith(b, "Uses 1")); + + assertTrue(aa.hasEfferentRelationshipWith(bbb, "Uses 1")); + assertTrue(aa.hasEfferentRelationshipWith(bb, "Uses 1")); + assertTrue(aa.hasEfferentRelationshipWith(b, "Uses 1")); + + assertTrue(a.hasEfferentRelationshipWith(bbb, "Uses 1")); + assertTrue(a.hasEfferentRelationshipWith(bb, "Uses 1")); + assertTrue(a.hasEfferentRelationshipWith(b, "Uses 1")); + + // and add another relationship with a different description + aaa.uses(bbb, "Uses 2"); + assertEquals(10, model.getRelationships().size()); // no change + } + + @Test + public void test_impliedRelationshipsAreCreated_UnlessAnyRelationshipExists() { + SoftwareSystem a = model.addSoftwareSystem("A", ""); + Container aa = a.addContainer("AA", "", ""); + Component aaa = aa.addComponent("AAA", "", ""); + + SoftwareSystem b = model.addSoftwareSystem("B", ""); + Container bb = b.addContainer("BB", "", ""); + Component bbb = bb.addComponent("BBB", "", ""); + + model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); + + // add some higher level relationships + aa.uses(bb, "Uses"); + + assertEquals(4, model.getRelationships().size()); + assertTrue(aa.hasEfferentRelationshipWith(bb, "Uses")); + + // AA->BB implies AA->B A->BB A->B + assertTrue(aa.hasEfferentRelationshipWith(b, "Uses")); + assertTrue(a.hasEfferentRelationshipWith(bb, "Uses")); + assertTrue(a.hasEfferentRelationshipWith(b, "Uses")); + + // and now a lower level relationship, which will be propagated to parents that don't already have relationships between them + aaa.uses(bbb, "Uses 1"); + + assertEquals(9, model.getRelationships().size()); + assertTrue(aaa.hasEfferentRelationshipWith(bbb, "Uses 1")); + + // AAA->BBB implies AAA->BB AAA->B AA->BBB AA->BB AA->B A->BBB A->BB A->B + assertTrue(aaa.hasEfferentRelationshipWith(bb, "Uses 1")); + assertTrue(aaa.hasEfferentRelationshipWith(b, "Uses 1")); + + assertTrue(aa.hasEfferentRelationshipWith(bbb, "Uses 1")); + assertTrue(aa.hasEfferentRelationshipWith(bb, "Uses")); // existing relationship + assertTrue(aa.hasEfferentRelationshipWith(b, "Uses")); // existing relationship + + assertTrue(a.hasEfferentRelationshipWith(bbb, "Uses 1")); + assertTrue(a.hasEfferentRelationshipWith(bb, "Uses")); // existing relationship + assertTrue(a.hasEfferentRelationshipWith(b, "Uses")); // existing relationship + } + +} \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategyTests.java b/structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategyTests.java new file mode 100644 index 000000000..e48b04748 --- /dev/null +++ b/structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategyTests.java @@ -0,0 +1,59 @@ +package com.structurizr.model; + +import com.structurizr.AbstractWorkspaceTestBase; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategyTests extends AbstractWorkspaceTestBase { + + @Test + public void test_impliedRelationships_WhenNoSummaryRelationshipsExist() { + SoftwareSystem a = model.addSoftwareSystem("A", ""); + Container aa = a.addContainer("AA", "", ""); + Component aaa = aa.addComponent("AAA", "", ""); + + SoftwareSystem b = model.addSoftwareSystem("B", ""); + Container bb = b.addContainer("BB", "", ""); + Component bbb = bb.addComponent("BBB", "", ""); + + model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategy()); + + aaa.uses(bbb, "Uses 1"); + + assertEquals(9, model.getRelationships().size()); + assertTrue(aaa.hasEfferentRelationshipWith(bbb, "Uses 1")); + + // AAA->BBB implies AAA->BB AAA->B AA->BBB AA->BB AA->B A->BBB A->BB A->B + assertTrue(aaa.hasEfferentRelationshipWith(bb, "Uses 1")); + assertTrue(aaa.hasEfferentRelationshipWith(b, "Uses 1")); + + assertTrue(aa.hasEfferentRelationshipWith(bbb, "Uses 1")); + assertTrue(aa.hasEfferentRelationshipWith(bb, "Uses 1")); + assertTrue(aa.hasEfferentRelationshipWith(b, "Uses 1")); + + assertTrue(a.hasEfferentRelationshipWith(bbb, "Uses 1")); + assertTrue(a.hasEfferentRelationshipWith(bb, "Uses 1")); + assertTrue(a.hasEfferentRelationshipWith(b, "Uses 1")); + + // and add another relationship with a different description + aaa.uses(bbb, "Uses 2"); + + assertEquals(18, model.getRelationships().size()); + assertTrue(aaa.hasEfferentRelationshipWith(bbb, "Uses 2")); + + // AAA->BBB implies AAA->BB AAA->B AA->BBB AA->BB AA->B A->BBB A->BB A->B + assertTrue(aaa.hasEfferentRelationshipWith(bb, "Uses 2")); + assertTrue(aaa.hasEfferentRelationshipWith(b, "Uses 2")); + + assertTrue(aa.hasEfferentRelationshipWith(bbb, "Uses 2")); + assertTrue(aa.hasEfferentRelationshipWith(bb, "Uses 2")); + assertTrue(aa.hasEfferentRelationshipWith(b, "Uses 2")); + + assertTrue(a.hasEfferentRelationshipWith(bbb, "Uses 2")); + assertTrue(a.hasEfferentRelationshipWith(bb, "Uses 2")); + assertTrue(a.hasEfferentRelationshipWith(b, "Uses 2")); + } + +} \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/model/DefaultImpliedRelationshipsStrategyTests.java b/structurizr-core/test/unit/com/structurizr/model/DefaultImpliedRelationshipsStrategyTests.java new file mode 100644 index 000000000..26ca1ff02 --- /dev/null +++ b/structurizr-core/test/unit/com/structurizr/model/DefaultImpliedRelationshipsStrategyTests.java @@ -0,0 +1,31 @@ +package com.structurizr.model; + +import com.structurizr.AbstractWorkspaceTestBase; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class DefaultImpliedRelationshipsStrategyTests extends AbstractWorkspaceTestBase { + + @Test + public void test_createImpliedRelationships_DoesNothing() { + SoftwareSystem a = model.addSoftwareSystem("A", ""); + Container aa = a.addContainer("AA", "", ""); + Component aaa = aa.addComponent("AAA", "", ""); + + SoftwareSystem b = model.addSoftwareSystem("B", ""); + Container bb = b.addContainer("BB", "", ""); + Component bbb = bb.addComponent("BBB", "", ""); + + model.setImpliedRelationshipsStrategy(new DefaultImpliedRelationshipsStrategy()); + + aaa.uses(bbb, "Uses 1"); + aaa.uses(bbb, "Uses 2"); + + assertEquals(2, model.getRelationships().size()); + assertTrue(aaa.hasEfferentRelationshipWith(bbb, "Uses 1")); + assertTrue(aaa.hasEfferentRelationshipWith(bbb, "Uses 2")); + } + +} \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/model/ElementTests.java b/structurizr-core/test/unit/com/structurizr/model/ElementTests.java index 1d55cb1a6..0650a78bb 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ElementTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ElementTests.java @@ -67,6 +67,30 @@ public void test_hasEfferentRelationshipWith_ReturnsTrue_WhenThereIsARelationshi assertTrue(softwareSystem1.hasEfferentRelationshipWith(softwareSystem2)); } + @Test + public void test_hasEfferentRelationshipWithElementAndDescription_ReturnsFalse_WhenANullElementIsSpecified() { + SoftwareSystem softwareSystem1 = model.addSoftwareSystem("System 1", ""); + assertFalse(softwareSystem1.hasEfferentRelationshipWith(null, null)); + } + + @Test + public void test_hasEfferentRelationshipWithElementAndDescription_ReturnsFalse_WhenThereIsNotAMatchingRelationship() { + SoftwareSystem softwareSystem1 = model.addSoftwareSystem("System 1", ""); + SoftwareSystem softwareSystem2 = model.addSoftwareSystem("System 2", ""); + softwareSystem1.uses(softwareSystem2, "Uses"); + + assertFalse(softwareSystem1.hasEfferentRelationshipWith(softwareSystem2, "Does something with")); + } + + @Test + public void test_hasEfferentRelationshipWithElementAndDescription_ReturnsTrue_WhenThereIsAMatchingRelationship() { + SoftwareSystem softwareSystem1 = model.addSoftwareSystem("System 1", ""); + SoftwareSystem softwareSystem2 = model.addSoftwareSystem("System 2", ""); + softwareSystem1.uses(softwareSystem2, "Uses"); + + assertTrue(softwareSystem1.hasEfferentRelationshipWith(softwareSystem2, "Uses")); + } + @Test public void test_getEfferentRelationshipWith_ReturnsNull_WhenANullElementIsSpecified() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("System 1", ""); diff --git a/structurizr-core/test/unit/com/structurizr/model/ModelTests.java b/structurizr-core/test/unit/com/structurizr/model/ModelTests.java index de8354679..9b95f8d34 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ModelTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ModelTests.java @@ -946,4 +946,21 @@ public void test_hydrate() { assertEquals("9", element.getId()); } + @Test + public void test_impliedRelationshipStrategy() { + // default strategy initially + assertTrue(model.getImpliedRelationshipsStrategy() instanceof DefaultImpliedRelationshipsStrategy); + + model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); + assertTrue(model.getImpliedRelationshipsStrategy() instanceof CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy); + } + + @Test + public void test_setImpliedRelationshipStrategy_ResetsToTheDefaultStrategy_WhenPassedNull() { + model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); + model.setImpliedRelationshipsStrategy(null); + + assertTrue(model.getImpliedRelationshipsStrategy() instanceof DefaultImpliedRelationshipsStrategy); + } + } \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java b/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java index 229c9d583..9df1efa4e 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java @@ -255,9 +255,9 @@ public void test_addDependentSoftwareSystem2() { SoftwareSystem softwareSystem2 = model.addSoftwareSystem(Location.External, "SoftwareSystem 2", ""); Container container2a = softwareSystem2.addContainer("Container 2-A", "", ""); + model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); container2a.uses(container1a, ""); - model.addImplicitRelationships(); view.addDependentSoftwareSystems(); view.addAllContainers(); diff --git a/structurizr-examples/src/com/structurizr/example/BigBankPlc.java b/structurizr-examples/src/com/structurizr/example/BigBankPlc.java index 1f7636a0e..3cbecf3f1 100644 --- a/structurizr-examples/src/com/structurizr/example/BigBankPlc.java +++ b/structurizr-examples/src/com/structurizr/example/BigBankPlc.java @@ -30,6 +30,7 @@ public class BigBankPlc { public static void main(String[] args) throws Exception { Workspace workspace = new Workspace("Big Bank plc", "This is an example workspace to illustrate the key features of Structurizr, based around a fictional online banking system."); Model model = workspace.getModel(); + model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); ViewSet views = workspace.getViews(); model.setEnterprise(new Enterprise("Big Bank plc")); @@ -101,8 +102,6 @@ public static void main(String[] args) throws Exception { mainframeBankingSystemFacade.uses(mainframeBankingSystem, "Uses", "XML/HTTPS"); emailComponent.uses(emailSystem, "Sends e-mail using"); - model.addImplicitRelationships(); - // deployment nodes and container instances DeploymentNode developerLaptop = model.addDeploymentNode("Development", "Developer Laptop", "A developer laptop.", "Microsoft Windows 10 or Apple macOS"); DeploymentNode apacheTomcat = developerLaptop.addDeploymentNode("Docker Container - Web Server", "A Docker container.", "Docker") From faf1bce8167b639b37c8c492d109bc5edb52a1c8 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 28 Apr 2020 15:24:07 +0100 Subject: [PATCH 107/717] Added the ability to customize the symbols used when rendering metadata. --- docs/changelog.md | 1 + .../com/structurizr/view/Configuration.java | 20 +++++++++++++++++++ .../com/structurizr/view/MetadataSymbols.java | 15 ++++++++++++++ 3 files changed, 36 insertions(+) create mode 100644 structurizr-core/src/com/structurizr/view/MetadataSymbols.java diff --git a/docs/changelog.md b/docs/changelog.md index ed8c81d2e..0a27746d2 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -7,6 +7,7 @@ - Components from any container can now be added to a component view. - Added an externalContainersBoundariesVisible property to ComponentView, to set whether container boundaries should be visible for "external" components (those outside the container in scope). - Improved the support for creating [implied relationships](docs/implied-relationships.md). +- Added the ability to customize the symbols used when rendering metadata. ## 1.3.5 (26th March 2020) diff --git a/structurizr-core/src/com/structurizr/view/Configuration.java b/structurizr-core/src/com/structurizr/view/Configuration.java index 39955888e..834a6e073 100644 --- a/structurizr-core/src/com/structurizr/view/Configuration.java +++ b/structurizr-core/src/com/structurizr/view/Configuration.java @@ -14,6 +14,8 @@ public final class Configuration { private String theme; private Terminology terminology = new Terminology(); + private MetadataSymbols metadataSymbols; + private String defaultView; private String lastSavedView; private ViewSortOrder viewSortOrder; @@ -126,6 +128,24 @@ void setTerminology(Terminology terminology) { this.terminology = terminology; } + /** + * Gets the type of symbols to use when rendering metadata. + * + * @return a MetadataSymbols enum value + */ + public MetadataSymbols getMetadataSymbols() { + return metadataSymbols; + } + + /** + * Sets the type of symbols to use when rendering metadata. + * + * @param metadataSymbols a MetadataSymbols enum value + */ + public void setMetadataSymbols(MetadataSymbols metadataSymbols) { + this.metadataSymbols = metadataSymbols; + } + /** * Gets the sort order used when displaying the list of views. * diff --git a/structurizr-core/src/com/structurizr/view/MetadataSymbols.java b/structurizr-core/src/com/structurizr/view/MetadataSymbols.java new file mode 100644 index 000000000..f435c0c10 --- /dev/null +++ b/structurizr-core/src/com/structurizr/view/MetadataSymbols.java @@ -0,0 +1,15 @@ +package com.structurizr.view; + +/** + * The type of symbols to use when rendering metadata. + */ +public enum MetadataSymbols { + + SquareBrackets, + RoundBrackets, + CurlyBrackets, + AngledBrackets, + DoubleAngledBrackets, + None + +} \ No newline at end of file From f209b6c54096759b0f3e3fea5ca6c9ee14a0c138 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 28 Apr 2020 15:38:32 +0100 Subject: [PATCH 108/717] With the additional of the implied relationships functionality, let's bump the minor version number. --- build.gradle | 2 +- docs/changelog.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index e326fecef..e8d8eca3a 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.3.6' + version = '1.4.0' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 0a27746d2..1b235da86 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 1.3.6 (unreleased) +## 1.4.0 (unreleased) - Added a "Component" element shape. - Added a "Dotted" element border style. From 1e54e6623209ba4dd7c44ac8cac85426f84eb26e Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 2 May 2020 14:48:02 +0100 Subject: [PATCH 109/717] Fixed typo. --- .../src/com/structurizr/view/MetadataSymbols.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/structurizr-core/src/com/structurizr/view/MetadataSymbols.java b/structurizr-core/src/com/structurizr/view/MetadataSymbols.java index f435c0c10..a8f6f247a 100644 --- a/structurizr-core/src/com/structurizr/view/MetadataSymbols.java +++ b/structurizr-core/src/com/structurizr/view/MetadataSymbols.java @@ -8,8 +8,8 @@ public enum MetadataSymbols { SquareBrackets, RoundBrackets, CurlyBrackets, - AngledBrackets, - DoubleAngledBrackets, + AngleBrackets, + DoubleAngleBrackets, None } \ No newline at end of file From 65016649d91e8c70e8183c11da76c953d9e0a5b9 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 23 May 2020 21:33:42 +0100 Subject: [PATCH 110/717] Adds a name and description to the theme. --- .../src/com/structurizr/util/ThemeUtils.java | 48 ++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/structurizr-client/src/com/structurizr/util/ThemeUtils.java b/structurizr-client/src/com/structurizr/util/ThemeUtils.java index fc5afb17c..3145154e4 100644 --- a/structurizr-client/src/com/structurizr/util/ThemeUtils.java +++ b/structurizr-client/src/com/structurizr/util/ThemeUtils.java @@ -1,13 +1,18 @@ package com.structurizr.util; +import com.fasterxml.jackson.annotation.JsonGetter; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.structurizr.Workspace; import com.structurizr.io.WorkspaceWriterException; +import com.structurizr.view.ElementStyle; +import com.structurizr.view.RelationshipStyle; import java.io.*; import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.LinkedList; /** * Some utility methods for exporting themes to JSON. @@ -57,7 +62,13 @@ private static void write(Workspace workspace, Writer writer) throws Exception { objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); - writer.write(objectMapper.writeValueAsString(workspace.getViews().getConfiguration().getStyles())); + writer.write(objectMapper.writeValueAsString( + new Theme( + workspace.getName(), + workspace.getDescription(), + workspace.getViews().getConfiguration().getStyles().getElements(), + workspace.getViews().getConfiguration().getStyles().getRelationships() + ))); } catch (IOException ioe) { throw new WorkspaceWriterException("Could not write the theme as JSON", ioe); } @@ -66,4 +77,39 @@ private static void write(Workspace workspace, Writer writer) throws Exception { writer.close(); } + static class Theme { + + private String name; + private String description; + private Collection elements = new LinkedList<>(); + private Collection relationships = new LinkedList<>(); + + Theme(String name, String description, Collection elements, Collection relationships) { + this.name = name; + this.description = description; + this.elements = elements; + this.relationships = relationships; + } + + @JsonGetter + public String getName() { + return name; + } + + @JsonGetter + public String getDescription() { + return description; + } + + @JsonGetter + public Collection getElements() { + return elements; + } + + @JsonGetter + public Collection getRelationships() { + return relationships; + } + } + } \ No newline at end of file From 206ea891cb8ce67362d8a9821df1880bfd1423ea Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 25 May 2020 16:07:45 +0100 Subject: [PATCH 111/717] Adds support for infrastructure nodes. --- docs/changelog.md | 1 + .../structurizr/model/DeploymentElement.java | 2 +- .../com/structurizr/model/DeploymentNode.java | 75 +++++++++++++++++ .../structurizr/model/InfrastructureNode.java | 82 +++++++++++++++++++ .../src/com/structurizr/model/Model.java | 35 +++++++- .../src/com/structurizr/model/Tags.java | 1 + .../com/structurizr/view/DeploymentView.java | 19 +++-- .../src/com/structurizr/view/Terminology.java | 11 +++ .../model/DeploymentNodeTests.java | 18 +++- .../com/structurizr/model/ModelTests.java | 50 ++++++++++- 10 files changed, 280 insertions(+), 14 deletions(-) create mode 100644 structurizr-core/src/com/structurizr/model/InfrastructureNode.java diff --git a/docs/changelog.md b/docs/changelog.md index 1b235da86..ce07cd987 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -8,6 +8,7 @@ - Added an externalContainersBoundariesVisible property to ComponentView, to set whether container boundaries should be visible for "external" components (those outside the container in scope). - Improved the support for creating [implied relationships](docs/implied-relationships.md). - Added the ability to customize the symbols used when rendering metadata. +- Adds support for infrastructure nodes. ## 1.3.5 (26th March 2020) diff --git a/structurizr-core/src/com/structurizr/model/DeploymentElement.java b/structurizr-core/src/com/structurizr/model/DeploymentElement.java index ea7666a63..3577d940e 100644 --- a/structurizr-core/src/com/structurizr/model/DeploymentElement.java +++ b/structurizr-core/src/com/structurizr/model/DeploymentElement.java @@ -1,7 +1,7 @@ package com.structurizr.model; /** - * This is the superclass for model elements that describe deployment nodes and container instances. + * This is the superclass for model elements that describe deployment nodes, infrastructure nodes, and container instances. */ public abstract class DeploymentElement extends Element { diff --git a/structurizr-core/src/com/structurizr/model/DeploymentNode.java b/structurizr-core/src/com/structurizr/model/DeploymentNode.java index ecbb0cd60..f436f8cbf 100644 --- a/structurizr-core/src/com/structurizr/model/DeploymentNode.java +++ b/structurizr-core/src/com/structurizr/model/DeploymentNode.java @@ -26,6 +26,7 @@ public final class DeploymentNode extends DeploymentElement { private int instances = 1; private Set children = new HashSet<>(); + private Set infrastructureNodes = new HashSet<>(); private Set containerInstances = new HashSet<>(); /** @@ -115,6 +116,65 @@ public DeploymentNode getDeploymentNodeWithName(String name) { return null; } + /** + * Gets the infrastructure node with the specified name. + * + * @param name the name of the infrastructure node + * @return the InfrastructureNode instance with the specified name (or null if it doesn't exist). + */ + public InfrastructureNode getInfrastructureNodeWithName(String name) { + if (name == null || name.trim().length() == 0) { + throw new IllegalArgumentException("A name must be specified."); + } + + for (InfrastructureNode infrastructureNode : getInfrastructureNodes()) { + if (infrastructureNode.getName().equals(name)) { + return infrastructureNode; + } + } + + return null; + } + + /** + * Adds a child infrastructure node. + * + * @param name the name of the infrastructure node + * @return an InfrastructureNode object + */ + public InfrastructureNode addInfrastructureNode(String name) { + return addInfrastructureNode(name, null, null); + } + + /** + * Adds a child infrastructure node. + * + * @param name the name of the infrastructure node + * @param description a short description + * @param technology the technology + * @return an InfrastructureNode object + */ + public InfrastructureNode addInfrastructureNode(String name, String description, String technology) { + return addInfrastructureNode(name, description, technology, null); + } + + /** + * Adds a child infrastructure node. + * + * @param name the name of the infrastructure node + * @param description a short description + * @param technology the technology + * @param properties a Map (String,String) describing name=value properties + * @return an InfrastructureNode object + */ + public InfrastructureNode addInfrastructureNode(String name, String description, String technology, Map properties) { + InfrastructureNode infrastructureNode = getModel().addInfrastructureNode(this, name, description, technology, properties); + if (infrastructureNode != null) { + infrastructureNodes.add(infrastructureNode); + } + return infrastructureNode; + } + /** * Adds a relationship between this and another deployment node. * @@ -155,6 +215,21 @@ void setChildren(Set children) { } } + /** + * Gets the set of child infrastructure nodes. + * + * @return a Set of InfrastructureNode objects + */ + public Set getInfrastructureNodes() { + return new HashSet<>(infrastructureNodes); + } + + void setInfrastructureNodes(Set infrastructureNodes) { + if (infrastructureNodes != null) { + this.infrastructureNodes = new HashSet<>(infrastructureNodes); + } + } + @JsonIgnore public boolean hasChildren() { return !children.isEmpty(); diff --git a/structurizr-core/src/com/structurizr/model/InfrastructureNode.java b/structurizr-core/src/com/structurizr/model/InfrastructureNode.java new file mode 100644 index 000000000..46490d859 --- /dev/null +++ b/structurizr-core/src/com/structurizr/model/InfrastructureNode.java @@ -0,0 +1,82 @@ +package com.structurizr.model; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +import java.util.*; + +/** + *

    + * Represents an infrastructure node, which is something like: + *

    + * + *
      + *
    • Load balancer
    • + *
    • Firewall
    • + *
    • DNS service
    • + *
    • etc
    • + *
    + */ +public final class InfrastructureNode extends DeploymentElement { + + private DeploymentNode parent; + private String technology; + + /** + * Adds a relationship between this and another deployment element (deployment node, infrastructure node, or container instance). + * + * @param destination the destination DeploymentElement + * @param description a short description of the relationship + * @param technology the technology + * @return a Relationship object + */ + public Relationship uses(DeploymentElement destination, String description, String technology) { + return uses(destination, description, technology, InteractionStyle.Synchronous); + } + + /** + * Adds a relationship between this and another deployment element (deployment node, infrastructure node, or container instance). + * + * @param destination the destination DeploymentElement + * @param description a short description of the relationship + * @param technology the technology + * @param interactionStyle the interaction style (Synchronous vs Asynchronous) + * @return a Relationship object + */ + public Relationship uses(DeploymentElement destination, String description, String technology, InteractionStyle interactionStyle) { + return getModel().addRelationship(this, destination, description, technology, interactionStyle); + } + + /** + * Gets the parent deployment node. + * + * @return the parent DeploymentNode, or null if there is no parent + */ + @Override + @JsonIgnore + public Element getParent() { + return parent; + } + + void setParent(DeploymentNode parent) { + this.parent = parent; + } + + public String getTechnology() { + return technology; + } + + public void setTechnology(String technology) { + this.technology = technology; + } + + @JsonIgnore + protected Set getRequiredTags() { + return new LinkedHashSet<>(Arrays.asList(Tags.ELEMENT, Tags.INFRASTRUCTURE_NODE)); + } + + @Override + public String getCanonicalName() { + return getParent().getCanonicalName() + CANONICAL_NAME_SEPARATOR + formatForCanonicalName(getName()); + } + +} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/model/Model.java b/structurizr-core/src/com/structurizr/model/Model.java index b0896400c..7203f95be 100644 --- a/structurizr-core/src/com/structurizr/model/Model.java +++ b/structurizr-core/src/com/structurizr/model/Model.java @@ -422,6 +422,11 @@ private void hydrateDeploymentNode(DeploymentNode deploymentNode, DeploymentNode containerInstance.setContainer((Container)getElement(containerInstance.getContainerId())); addElementToInternalStructures(containerInstance); } + + for (InfrastructureNode infrastructureNode : deploymentNode.getInfrastructureNodes()) { + infrastructureNode.setParent(deploymentNode); + addElementToInternalStructures(infrastructureNode); + } } private void checkNameIsUnique(Collection elements, String name, String errorMessage) { @@ -751,7 +756,7 @@ DeploymentNode addDeploymentNode(DeploymentNode parent, @Nullable String environ throw new IllegalArgumentException("A name must be specified."); } - if ((parent == null && getDeploymentNodeWithName(name, environment) == null) || (parent != null && parent.getDeploymentNodeWithName(name) == null)) { + if ((parent == null && getDeploymentNodeWithName(name, environment) == null) || (parent != null && parent.getDeploymentNodeWithName(name) == null && parent.getInfrastructureNodeWithName(name) == null)) { DeploymentNode deploymentNode = new DeploymentNode(); deploymentNode.setName(name); deploymentNode.setDescription(description); @@ -772,7 +777,33 @@ DeploymentNode addDeploymentNode(DeploymentNode parent, @Nullable String environ return deploymentNode; } else { - throw new IllegalArgumentException("A deployment node named '" + name + "' already exists."); + throw new IllegalArgumentException("A deployment/infrastructure node named '" + name + "' already exists."); + } + } + + @Nonnull + InfrastructureNode addInfrastructureNode(DeploymentNode parent, @Nonnull String name, String description, String technology, Map properties) { + if (name == null || name.trim().length() == 0) { + throw new IllegalArgumentException("A name must be specified."); + } + + if (parent.getDeploymentNodeWithName(name) == null && parent.getInfrastructureNodeWithName(name) == null) { + InfrastructureNode infrastructureNode = new InfrastructureNode(); + infrastructureNode.setName(name); + infrastructureNode.setDescription(description); + infrastructureNode.setTechnology(technology); + infrastructureNode.setParent(parent); + infrastructureNode.setEnvironment(parent.getEnvironment()); + if (properties != null) { + infrastructureNode.setProperties(properties); + } + + infrastructureNode.setId(idGenerator.generateId(infrastructureNode)); + addElementToInternalStructures(infrastructureNode); + + return infrastructureNode; + } else { + throw new IllegalArgumentException("A deployment/infrastructure node named '" + name + "' already exists."); } } diff --git a/structurizr-core/src/com/structurizr/model/Tags.java b/structurizr-core/src/com/structurizr/model/Tags.java index 88b72de0d..4f5e9d823 100644 --- a/structurizr-core/src/com/structurizr/model/Tags.java +++ b/structurizr-core/src/com/structurizr/model/Tags.java @@ -11,6 +11,7 @@ public class Tags { public static final String COMPONENT = "Component"; public static final String DEPLOYMENT_NODE = "Deployment Node"; + public static final String INFRASTRUCTURE_NODE = "Infrastructure Node"; public static final String CONTAINER_INSTANCE = "Container Instance"; /** diff --git a/structurizr-core/src/com/structurizr/view/DeploymentView.java b/structurizr-core/src/com/structurizr/view/DeploymentView.java index 73be008ba..4cc4a7b35 100644 --- a/structurizr-core/src/com/structurizr/view/DeploymentView.java +++ b/structurizr-core/src/com/structurizr/view/DeploymentView.java @@ -72,7 +72,7 @@ public void add(@Nonnull DeploymentNode deploymentNode, boolean addRelationships throw new IllegalArgumentException("A deployment node must be specified."); } - if (addContainerInstancesAndDeploymentNodes(deploymentNode, addRelationships)) { + if (addContainerInstancesAndDeploymentNodesAndInfrastructureNodes(deploymentNode, addRelationships)) { Element parent = deploymentNode.getParent(); while (parent != null) { addElement(parent, addRelationships); @@ -81,25 +81,30 @@ public void add(@Nonnull DeploymentNode deploymentNode, boolean addRelationships } } - private boolean addContainerInstancesAndDeploymentNodes(DeploymentNode deploymentNode, boolean addRelationships) { - boolean hasContainers = false; + private boolean addContainerInstancesAndDeploymentNodesAndInfrastructureNodes(DeploymentNode deploymentNode, boolean addRelationships) { + boolean hasContainersOrInfrastructureNodes = false; for (ContainerInstance containerInstance : deploymentNode.getContainerInstances()) { Container container = containerInstance.getContainer(); if (getSoftwareSystem() == null || container.getParent().equals(getSoftwareSystem())) { addElement(containerInstance, addRelationships); - hasContainers = true; + hasContainersOrInfrastructureNodes = true; } } + for (InfrastructureNode infrastructureNode : deploymentNode.getInfrastructureNodes()) { + addElement(infrastructureNode, addRelationships); + hasContainersOrInfrastructureNodes = true; + } + for (DeploymentNode child : deploymentNode.getChildren()) { - hasContainers = hasContainers | addContainerInstancesAndDeploymentNodes(child, addRelationships); + hasContainersOrInfrastructureNodes = hasContainersOrInfrastructureNodes | addContainerInstancesAndDeploymentNodesAndInfrastructureNodes(child, addRelationships); } - if (hasContainers) { + if (hasContainersOrInfrastructureNodes) { addElement(deploymentNode, addRelationships); } - return hasContainers; + return hasContainersOrInfrastructureNodes; } /** diff --git a/structurizr-core/src/com/structurizr/view/Terminology.java b/structurizr-core/src/com/structurizr/view/Terminology.java index a55a39f1b..6993b281a 100644 --- a/structurizr-core/src/com/structurizr/view/Terminology.java +++ b/structurizr-core/src/com/structurizr/view/Terminology.java @@ -28,6 +28,9 @@ public final class Terminology { @JsonInclude(value = JsonInclude.Include.NON_EMPTY) private String deploymentNode = ""; + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + private String infrastructureNode = ""; + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) private String relationship = ""; @@ -87,6 +90,14 @@ public void setDeploymentNode(String deploymentNode) { this.deploymentNode = deploymentNode; } + public String getInfrastructureNode() { + return infrastructureNode; + } + + public void setInfrastructureNode(String infrastructureNode) { + this.infrastructureNode = infrastructureNode; + } + public String getRelationship() { return relationship; } diff --git a/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java b/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java index 04aff9f00..7d7685c6e 100644 --- a/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java @@ -69,7 +69,6 @@ public void test_add_AddsAContainerInstance_WhenAContainerIsSpecified() { assertNotNull(containerInstance); assertSame(container, containerInstance.getContainer()); assertTrue(deploymentNode.getContainerInstances().contains(containerInstance)); - assertEquals("Default", containerInstance.getEnvironment()); } @Test @@ -155,16 +154,29 @@ public void test_getDeploymentNodeWithName_ThrowsAnException_WhenANameIsNotSpeci } @Test - public void test_getDeploymentNodeWithName_ReturnsNull_WhenThereIsNoDeploymentWithTheSpecifiedName() { + public void test_getDeploymentNodeWithName_ReturnsNull_WhenThereIsNoDeploymentNodeWithTheSpecifiedName() { DeploymentNode deploymentNode = new DeploymentNode(); assertNull(deploymentNode.getDeploymentNodeWithName("foo")); } @Test - public void test_getDeploymentNodeWithName_ReturnsTheNamedDeploymentNode_WhenThereIsADeploymentWithTheSpecifiedName() { + public void test_getDeploymentNodeWithName_ReturnsTheNamedDeploymentNode_WhenThereIsADeploymentNodeWithTheSpecifiedName() { DeploymentNode parent = model.addDeploymentNode("parent", "", ""); DeploymentNode child = parent.addDeploymentNode("child", "", ""); assertSame(child, parent.getDeploymentNodeWithName("child")); } + @Test + public void test_getInfrastructureNodeWithName_ReturnsNull_WhenThereIsNoInfrastructureNodeWithTheSpecifiedName() { + DeploymentNode deploymentNode = new DeploymentNode(); + assertNull(deploymentNode.getInfrastructureNodeWithName("foo")); + } + + @Test + public void test_getInfrastructureNodeWithName_ReturnsTheNamedDeploymentNode_WhenThereIsAInfrastructureNodeWithTheSpecifiedName() { + DeploymentNode parent = model.addDeploymentNode("parent", "", ""); + InfrastructureNode child = parent.addInfrastructureNode("child", "", ""); + assertSame(child, parent.getInfrastructureNodeWithName("child")); + } + } \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/model/ModelTests.java b/structurizr-core/test/unit/com/structurizr/model/ModelTests.java index 9b95f8d34..1e65fee4c 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ModelTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ModelTests.java @@ -852,7 +852,55 @@ public void test_addDeploymentNode_ThrowsAnException_WhenADeploymentNodeWithTheS model.addDeploymentNode("Amazon AWS", "Description", "Technology"); fail(); } catch (IllegalArgumentException iae) { - assertEquals("A deployment node named 'Amazon AWS' already exists.", iae.getMessage()); + assertEquals("A deployment/infrastructure node named 'Amazon AWS' already exists.", iae.getMessage()); + } + } + + @Test + public void test_addDeploymentNode_ThrowsAnException_WhenAChildDeploymentNodeWithTheSameNameAlreadyExists() { + DeploymentNode deploymentNode = model.addDeploymentNode("Amazon Web Services"); + deploymentNode.addDeploymentNode("AWS Region"); + try { + deploymentNode.addDeploymentNode("AWS Region"); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("A deployment/infrastructure node named 'AWS Region' already exists.", iae.getMessage()); + } + } + + @Test + public void test_addDeploymentNode_ThrowsAnException_WhenAChildInfrastructureNodeWithTheSameNameAlreadyExists() { + DeploymentNode deploymentNode = model.addDeploymentNode("Amazon Web Services"); + deploymentNode.addInfrastructureNode("Node"); + try { + deploymentNode.addDeploymentNode("Node"); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("A deployment/infrastructure node named 'Node' already exists.", iae.getMessage()); + } + } + + @Test + public void test_addInfrastructureNode_ThrowsAnException_WhenAChildDeploymentNodeWithTheSameNameAlreadyExists() { + DeploymentNode deploymentNode = model.addDeploymentNode("Amazon Web Services"); + deploymentNode.addDeploymentNode("Node"); + try { + deploymentNode.addInfrastructureNode("Node"); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("A deployment/infrastructure node named 'Node' already exists.", iae.getMessage()); + } + } + + @Test + public void test_addInfrastructureNode_ThrowsAnException_WhenAChildInfrastructureNodeWithTheSameNameAlreadyExists() { + DeploymentNode deploymentNode = model.addDeploymentNode("Amazon Web Services"); + deploymentNode.addInfrastructureNode("Node"); + try { + deploymentNode.addInfrastructureNode("Node"); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("A deployment/infrastructure node named 'Node' already exists.", iae.getMessage()); } } From da0d0f389b578ea798911db65a81c30d4d1f80a2 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 25 May 2020 16:08:29 +0100 Subject: [PATCH 112/717] Added some shorter methods for adding deployment nodes. --- .../src/com/structurizr/model/DeploymentNode.java | 12 +++++++++++- .../src/com/structurizr/model/Model.java | 12 ++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/structurizr-core/src/com/structurizr/model/DeploymentNode.java b/structurizr-core/src/com/structurizr/model/DeploymentNode.java index f436f8cbf..7b7544d17 100644 --- a/structurizr-core/src/com/structurizr/model/DeploymentNode.java +++ b/structurizr-core/src/com/structurizr/model/DeploymentNode.java @@ -53,6 +53,16 @@ public ContainerInstance add(Container container, boolean replicateContainerRela return containerInstance; } + /** + * Adds a child deployment node. + * + * @param name the name of the deployment node + * @return a DeploymentNode object + */ + public DeploymentNode addDeploymentNode(String name) { + return addDeploymentNode(name, null, null); + } + /** * Adds a child deployment node. * @@ -193,7 +203,7 @@ public Relationship uses(DeploymentNode destination, String description, String * @param destination the destination DeploymentNode * @param description a short description of the relationship * @param technology the technology - * @param interactionStyle the interation style (Synchronous vs Asynchronous) + * @param interactionStyle the interaction style (Synchronous vs Asynchronous) * @return a Relationship object */ public Relationship uses(DeploymentNode destination, String description, String technology, InteractionStyle interactionStyle) { diff --git a/structurizr-core/src/com/structurizr/model/Model.java b/structurizr-core/src/com/structurizr/model/Model.java index 7203f95be..2191c067a 100644 --- a/structurizr-core/src/com/structurizr/model/Model.java +++ b/structurizr-core/src/com/structurizr/model/Model.java @@ -657,6 +657,18 @@ public boolean isEmpty() { return people.isEmpty() && softwareSystems.isEmpty() && deploymentNodes.isEmpty(); } + /** + * Adds a top-level deployment node to this model. + * + * @param name the name of the deployment node + * @return a DeploymentNode instance + * @throws IllegalArgumentException if the name is not specified, or a top-level deployment node with the same name already exists in the model + */ + @Nonnull + public DeploymentNode addDeploymentNode(@Nonnull String name) { + return addDeploymentNode(DeploymentNode.DEFAULT_DEPLOYMENT_ENVIRONMENT, name, null, null); + } + /** * Adds a top-level deployment node to this model. * From 1df246609847f8579f05594cc5edee1b1786a59d Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 25 May 2020 16:10:49 +0100 Subject: [PATCH 113/717] Adds support for multiple themes. --- docs/changelog.md | 1 + .../com/structurizr/view/Configuration.java | 56 +++++++++++++++---- 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index ce07cd987..de9c049a4 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -9,6 +9,7 @@ - Improved the support for creating [implied relationships](docs/implied-relationships.md). - Added the ability to customize the symbols used when rendering metadata. - Adds support for infrastructure nodes. +- Adds support for multiple themes. ## 1.3.5 (26th March 2020) diff --git a/structurizr-core/src/com/structurizr/view/Configuration.java b/structurizr-core/src/com/structurizr/view/Configuration.java index 834a6e073..4b7e83cf2 100644 --- a/structurizr-core/src/com/structurizr/view/Configuration.java +++ b/structurizr-core/src/com/structurizr/view/Configuration.java @@ -1,9 +1,13 @@ package com.structurizr.view; import com.fasterxml.jackson.annotation.JsonGetter; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonSetter; import com.structurizr.util.Url; +import java.util.ArrayList; +import java.util.List; + /** * Configuration associated with how information in the workspace is rendered. */ @@ -11,7 +15,7 @@ public final class Configuration { private Branding branding = new Branding(); private Styles styles = new Styles(); - private String theme; + private String[] themes; private Terminology terminology = new Terminology(); private MetadataSymbols metadataSymbols; @@ -29,13 +33,14 @@ public Styles getStyles() { return styles; } - /** - * Gets the URL of the theme used to render views. - * - * @return the URL of the theme - */ + @JsonIgnore + @Deprecated public String getTheme() { - return theme; + if (themes == null || themes.length == 0) { + return null; + } + + return themes[0]; } /** @@ -43,14 +48,41 @@ public String getTheme() { * * @param url the URL of theme */ + @JsonSetter public void setTheme(String url) { - if (url != null && url.trim().length() > 0) { - if (Url.isUrl(url)) { - this.theme = url.trim(); - } else { - throw new IllegalArgumentException(url + " is not a valid URL."); + setThemes(url); + } + + /** + * Gets the URLs of the themes used to render views. + * + * @return an array of URLs + */ + public String[] getThemes() { + return themes; + } + + /** + * Sets the themes used to render views. + * + * @param themes an array of URLs + */ + public void setThemes(String... themes) { + List list = new ArrayList<>(); + + if (themes != null) { + for (String url : themes) { + if (url != null && url.trim().length() > 0) { + if (Url.isUrl(url)) { + list.add(url.trim()); + } else { + throw new IllegalArgumentException(url + " is not a valid URL."); + } + } } } + + this.themes = list.toArray(new String[0]); } /** From cf62c5591644f77c253dc03cebaeba2afd921205 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 25 May 2020 16:33:43 +0100 Subject: [PATCH 114/717] Adds support for animation steps for infrastructure nodes. --- .../com/structurizr/view/DeploymentView.java | 45 +++++++++++++++---- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/structurizr-core/src/com/structurizr/view/DeploymentView.java b/structurizr-core/src/com/structurizr/view/DeploymentView.java index 4cc4a7b35..58356d650 100644 --- a/structurizr-core/src/com/structurizr/view/DeploymentView.java +++ b/structurizr-core/src/com/structurizr/view/DeploymentView.java @@ -161,6 +161,19 @@ protected boolean canBeRemoved(Element element) { return true; } + /** + * Adds an animation step, with the specified infrastructure nodes. + * + * @param infrastructureNodes the infrastructure nodes that should be shown in the animation step + */ + public void addAnimation(InfrastructureNode... infrastructureNodes) { + if (infrastructureNodes == null || infrastructureNodes.length == 0) { + throw new IllegalArgumentException("One or more infrastructure nodes must be specified."); + } + + addAnimationStep(infrastructureNodes); + } + /** * Adds an animation step, with the specified container instances. * @@ -171,6 +184,11 @@ public void addAnimation(ContainerInstance... containerInstances) { throw new IllegalArgumentException("One or more container instances must be specified."); } + addAnimationStep(containerInstances); + } + + private void addAnimationStep(Element... elements) { + Set elementIdsInPreviousAnimationSteps = new HashSet<>(); for (Animation animationStep : animations) { elementIdsInPreviousAnimationSteps.addAll(animationStep.getElements()); @@ -179,12 +197,12 @@ public void addAnimation(ContainerInstance... containerInstances) { Set elementsInThisAnimationStep = new HashSet<>(); Set relationshipsInThisAnimationStep = new HashSet<>(); - for (ContainerInstance containerInstance : containerInstances) { - if (isElementInView(containerInstance) && !elementIdsInPreviousAnimationSteps.contains(containerInstance.getId())) { - elementIdsInPreviousAnimationSteps.add(containerInstance.getId()); - elementsInThisAnimationStep.add(containerInstance); + for (Element element : elements) { + if (isElementInView(element) && !elementIdsInPreviousAnimationSteps.contains(element.getId())) { + elementIdsInPreviousAnimationSteps.add(element.getId()); + elementsInThisAnimationStep.add(element); - Element deploymentNode = findDeploymentNode(containerInstance); + Element deploymentNode = findDeploymentNode(element); while (deploymentNode != null) { if (!elementIdsInPreviousAnimationSteps.contains(deploymentNode.getId())) { elementIdsInPreviousAnimationSteps.add(deploymentNode.getId()); @@ -212,12 +230,21 @@ public void addAnimation(ContainerInstance... containerInstances) { animations.add(new Animation(animations.size() + 1, elementsInThisAnimationStep, relationshipsInThisAnimationStep)); } - private DeploymentNode findDeploymentNode(ContainerInstance containerInstance) { + private DeploymentNode findDeploymentNode(Element e) { for (Element element : getModel().getElements()) { if (element instanceof DeploymentNode) { - DeploymentNode deploymentNode = (DeploymentNode)element; - if (deploymentNode.getContainerInstances().contains(containerInstance)) { - return deploymentNode; + DeploymentNode deploymentNode = (DeploymentNode) element; + + if (e instanceof ContainerInstance) { + if (deploymentNode.getContainerInstances().contains(e)) { + return deploymentNode; + } + } + + if (e instanceof InfrastructureNode) { + if (deploymentNode.getInfrastructureNodes().contains(e)) { + return deploymentNode; + } } } } From 0fbb61daed2f593e89197abcfee0f76ee0c09f62 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 26 May 2020 07:44:00 +0100 Subject: [PATCH 115/717] Fixes test. --- .../com/structurizr/view/DeploymentViewTests.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java b/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java index 20054bd78..04d0b4a3d 100644 --- a/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java @@ -156,13 +156,24 @@ public void test_addDeploymentNode_AddsTheParentToo() { public void test_addAnimationStep_ThrowsAnException_WhenNoContainerInstancesAreSpecified() { try { deploymentView = views.createDeploymentView("deployment", "Description"); - deploymentView.addAnimation(); + deploymentView.addAnimation((ContainerInstance[])null); fail(); } catch (IllegalArgumentException iae) { assertEquals("One or more container instances must be specified.", iae.getMessage()); } } + @Test + public void test_addAnimationStep_ThrowsAnException_WhenNoInfrastructureNodesAreSpecified() { + try { + deploymentView = views.createDeploymentView("deployment", "Description"); + deploymentView.addAnimation((InfrastructureNode[])null); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("One or more infrastructure nodes must be specified.", iae.getMessage()); + } + } + @Test public void test_addAnimationStep() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); From 9e71d6c24632b30edf4cd10709c7bace7c231a3f Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 26 May 2020 08:13:13 +0100 Subject: [PATCH 116/717] Adds support for automatic layout to be done with Graphviz. --- .../com/structurizr/view/AutomaticLayout.java | 22 ++++++++++++++++++- .../src/com/structurizr/view/View.java | 15 +++++++++++-- .../view/AutomaticLayoutTests.java | 2 +- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/structurizr-core/src/com/structurizr/view/AutomaticLayout.java b/structurizr-core/src/com/structurizr/view/AutomaticLayout.java index d907c9b36..8cf63a1c6 100644 --- a/structurizr-core/src/com/structurizr/view/AutomaticLayout.java +++ b/structurizr-core/src/com/structurizr/view/AutomaticLayout.java @@ -5,6 +5,7 @@ */ public final class AutomaticLayout { + private Implementation implementation; private RankDirection rankDirection; private int rankSeparation; private int nodeSeparation; @@ -14,7 +15,8 @@ public final class AutomaticLayout { AutomaticLayout() { } - AutomaticLayout(RankDirection rankDirection, int rankSeparation, int nodeSeparation, int edgeSeparation, boolean vertices) { + AutomaticLayout(Implementation implementation, RankDirection rankDirection, int rankSeparation, int nodeSeparation, int edgeSeparation, boolean vertices) { + setImplementation(implementation); setRankDirection(rankDirection); setRankSeparation(rankSeparation); setNodeSeparation(nodeSeparation); @@ -22,6 +24,19 @@ public final class AutomaticLayout { setVertices(vertices); } + /** + * Gets the name of the implementation to use. + * + * @return an enum representing Graphviz or Dagre + */ + public Implementation getImplementation() { + return implementation; + } + + void setImplementation(Implementation implementation) { + this.implementation = implementation; + } + /** * Gets the rank direction. * @@ -103,6 +118,11 @@ void setVertices(boolean vertices) { this.vertices = vertices; } + public enum Implementation { + Graphviz, + Dagre + } + public enum RankDirection { TopBottom, BottomTop, diff --git a/structurizr-core/src/com/structurizr/view/View.java b/structurizr-core/src/com/structurizr/view/View.java index 775c6c193..33d0a3b9b 100644 --- a/structurizr-core/src/com/structurizr/view/View.java +++ b/structurizr-core/src/com/structurizr/view/View.java @@ -154,7 +154,7 @@ public void enableAutomaticLayout() { } /** - * Enables the automatic layout for this view, with the specified settings. + * Enables automatic layout for this view, with the specified settings, using the Dagre implementation. * * @param rankDirection the rank direction * @param rankSeparation the separation between ranks (in pixels, a positive integer) @@ -163,7 +163,18 @@ public void enableAutomaticLayout() { * @param vertices whether vertices should be created during automatic layout */ public void enableAutomaticLayout(AutomaticLayout.RankDirection rankDirection, int rankSeparation, int nodeSeparation, int edgeSeparation, boolean vertices) { - this.automaticLayout = new AutomaticLayout(rankDirection, rankSeparation, nodeSeparation, edgeSeparation, vertices); + this.automaticLayout = new AutomaticLayout(AutomaticLayout.Implementation.Dagre, rankDirection, rankSeparation, nodeSeparation, edgeSeparation, vertices); + } + + /** + * Enables automatic layout for this view, with the specified settings, using the Graphviz implementation. + * + * @param rankDirection the rank direction + * @param rankSeparation the separation between ranks (in pixels, a positive integer) + * @param nodeSeparation the separation between nodes within the same rank (in pixels, a positive integer) + */ + public void enableAutomaticLayout(AutomaticLayout.RankDirection rankDirection, int rankSeparation, int nodeSeparation) { + this.automaticLayout = new AutomaticLayout(AutomaticLayout.Implementation.Graphviz, rankDirection, rankSeparation, nodeSeparation, 0, false); } /** diff --git a/structurizr-core/test/unit/com/structurizr/view/AutomaticLayoutTests.java b/structurizr-core/test/unit/com/structurizr/view/AutomaticLayoutTests.java index 128328e41..2ae1d4e7e 100644 --- a/structurizr-core/test/unit/com/structurizr/view/AutomaticLayoutTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/AutomaticLayoutTests.java @@ -8,7 +8,7 @@ public class AutomaticLayoutTests { @Test public void test_setAutomaticLayout() { - AutomaticLayout automaticLayout = new AutomaticLayout(AutomaticLayout.RankDirection.LeftRight, 100, 200, 300, true); + AutomaticLayout automaticLayout = new AutomaticLayout(AutomaticLayout.Implementation.Dagre, AutomaticLayout.RankDirection.LeftRight, 100, 200, 300, true); assertEquals(AutomaticLayout.RankDirection.LeftRight, automaticLayout.getRankDirection()); assertEquals(100, automaticLayout.getRankSeparation()); From 6059f030a1a402ac6b9f9a07be598b631d2ca4b5 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 26 May 2020 08:13:44 +0100 Subject: [PATCH 117/717] Suppress warnings about infrastructure nodes without descriptions. --- structurizr-core/src/com/structurizr/Workspace.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structurizr-core/src/com/structurizr/Workspace.java b/structurizr-core/src/com/structurizr/Workspace.java index 5f88a0142..bda1874f9 100644 --- a/structurizr-core/src/com/structurizr/Workspace.java +++ b/structurizr-core/src/com/structurizr/Workspace.java @@ -203,7 +203,7 @@ public int countAndLogWarnings() { // find elements with a missing description getModel().getElements().stream() - .filter(e -> !(e instanceof ContainerInstance) && !(e instanceof DeploymentNode)) + .filter(e -> !(e instanceof ContainerInstance) && !(e instanceof DeploymentNode) && !(e instanceof InfrastructureNode)) .filter(e -> e.getDescription() == null || e.getDescription().trim().length() == 0) .forEach(e -> warnings.add("The " + typeof(e) + " \"" + e.getCanonicalName().substring(1) + "\" is missing a description.")); From 031ee3660011c8088c86f11c46f22000033dce00 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 26 May 2020 14:08:25 +0100 Subject: [PATCH 118/717] Added an example of how to document an AWS deployment. --- .../example/AmazonWebServicesExample.java | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 structurizr-examples/src/com/structurizr/example/AmazonWebServicesExample.java diff --git a/structurizr-examples/src/com/structurizr/example/AmazonWebServicesExample.java b/structurizr-examples/src/com/structurizr/example/AmazonWebServicesExample.java new file mode 100644 index 000000000..d6255594d --- /dev/null +++ b/structurizr-examples/src/com/structurizr/example/AmazonWebServicesExample.java @@ -0,0 +1,78 @@ +package com.structurizr.example; + +import com.structurizr.Workspace; +import com.structurizr.api.StructurizrClient; +import com.structurizr.model.*; +import com.structurizr.view.*; + +/** + * An example of how to document an AWS deployment. + * + * The live workspace is available to view at https://structurizr.com/share/54915 + */ +public class AmazonWebServicesExample { + + private static final long WORKSPACE_ID = 54915; + private static final String API_KEY = ""; + private static final String API_SECRET = ""; + + private static final String SPRING_BOOT_TAG = "Spring Boot Application"; + private static final String DATABASE_TAG = "Database"; + + public static void main(String[] args) throws Exception { + Workspace workspace = new Workspace("Amazon Web Services Example", "An example AWS deployment architecture."); + Model model = workspace.getModel(); + + SoftwareSystem softwareSystem = model.addSoftwareSystem("Spring PetClinic", "Allows employees to view and manage information regarding the veterinarians, the clients, and their pets."); + Container webApplication = softwareSystem.addContainer("Web Application", "Allows employees to view and manage information regarding the veterinarians, the clients, and their pets.", "Java and Spring Boot"); + webApplication.addTags(SPRING_BOOT_TAG); + Container database = softwareSystem.addContainer("Database", "Stores information regarding the veterinarians, the clients, and their pets.", "Relational database schema"); + database.addTags(DATABASE_TAG); + + webApplication.uses(database, "Reads from and writes to", "JDBC/SSL"); + + DeploymentNode amazonWebServices = model.addDeploymentNode("Amazon Web Services"); + amazonWebServices.addTags("Amazon Web Services - Cloud"); + DeploymentNode amazonRegion = amazonWebServices.addDeploymentNode("US-East-1"); + amazonRegion.addTags("Amazon Web Services - Region"); + DeploymentNode ec2 = amazonRegion.addDeploymentNode("Amazon EC2"); + ec2.setInstances(10); + ec2.addTags("Amazon Web Services - EC2"); + ContainerInstance webApplicationInstance = ec2.add(webApplication); + + InfrastructureNode route53 = amazonRegion.addInfrastructureNode("Route 53"); + route53.addTags("Amazon Web Services - Route 53"); + + InfrastructureNode elb = amazonRegion.addInfrastructureNode("Elastic Load Balancer"); + elb.addTags("Amazon Web Services - Elastic Load Balancing"); + + route53.uses(elb, "Forwards requests to", "HTTPS"); + elb.uses(webApplicationInstance, "Forwards requests to", "HTTPS"); + + DeploymentNode rds = amazonRegion.addDeploymentNode("Amazon RDS"); + rds.addTags("Amazon Web Services - RDS"); + DeploymentNode mySql = rds.addDeploymentNode("MySQL"); + mySql.addTags("Amazon Web Services - RDS_MySQL_instance"); + ContainerInstance databaseInstance = mySql.add(database); + + ViewSet views = workspace.getViews(); + DeploymentView deploymentView = views.createDeploymentView(softwareSystem, "AmazonWebServicesDeployment", "An example deployment diagram."); + deploymentView.addAllDeploymentNodes(); + + deploymentView.addAnimation(route53); + deploymentView.addAnimation(elb); + deploymentView.addAnimation(webApplicationInstance); + deploymentView.addAnimation(databaseInstance); + + Styles styles = views.getConfiguration().getStyles(); + styles.addElementStyle(SPRING_BOOT_TAG).shape(Shape.RoundedBox).background("#ffffff"); + styles.addElementStyle(DATABASE_TAG).shape(Shape.Cylinder).background("#ffffff"); + styles.addElementStyle(Tags.INFRASTRUCTURE_NODE).shape(Shape.RoundedBox).background("#ffffff"); + + views.getConfiguration().setThemes("https://raw.githubusercontent.com/structurizr/themes/master/amazon-web-services/theme.json"); + + StructurizrClient structurizrClient = new StructurizrClient(API_KEY, API_SECRET); + structurizrClient.putWorkspace(WORKSPACE_ID, workspace); + } + +} \ No newline at end of file From 5efbb57021f25fb463d65791e4cd52361b25106d Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 28 May 2020 08:38:20 +0100 Subject: [PATCH 119/717] Updated example to include an autoscaling group. --- .../com/structurizr/example/AmazonWebServicesExample.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/structurizr-examples/src/com/structurizr/example/AmazonWebServicesExample.java b/structurizr-examples/src/com/structurizr/example/AmazonWebServicesExample.java index d6255594d..0f54412a2 100644 --- a/structurizr-examples/src/com/structurizr/example/AmazonWebServicesExample.java +++ b/structurizr-examples/src/com/structurizr/example/AmazonWebServicesExample.java @@ -35,8 +35,9 @@ public static void main(String[] args) throws Exception { amazonWebServices.addTags("Amazon Web Services - Cloud"); DeploymentNode amazonRegion = amazonWebServices.addDeploymentNode("US-East-1"); amazonRegion.addTags("Amazon Web Services - Region"); - DeploymentNode ec2 = amazonRegion.addDeploymentNode("Amazon EC2"); - ec2.setInstances(10); + DeploymentNode autoscalingGroup = amazonRegion.addDeploymentNode("Autoscaling group"); + autoscalingGroup.addTags("Amazon Web Services - Auto Scaling"); + DeploymentNode ec2 = autoscalingGroup.addDeploymentNode("Amazon EC2"); ec2.addTags("Amazon Web Services - EC2"); ContainerInstance webApplicationInstance = ec2.add(webApplication); From 7aa1b6dad6c9a06dc8cb8b64052312b173144996 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 28 May 2020 16:30:56 +0100 Subject: [PATCH 120/717] Adds support for curved relationship routing. --- docs/changelog.md | 1 + structurizr-core/src/com/structurizr/view/Routing.java | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index de9c049a4..d7d907cb9 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -10,6 +10,7 @@ - Added the ability to customize the symbols used when rendering metadata. - Adds support for infrastructure nodes. - Adds support for multiple themes. +- Adds support for curved relationship routing. ## 1.3.5 (26th March 2020) diff --git a/structurizr-core/src/com/structurizr/view/Routing.java b/structurizr-core/src/com/structurizr/view/Routing.java index 118618a4a..0e83e8fa4 100644 --- a/structurizr-core/src/com/structurizr/view/Routing.java +++ b/structurizr-core/src/com/structurizr/view/Routing.java @@ -3,6 +3,7 @@ public enum Routing { Direct, + Curved, Orthogonal -} +} \ No newline at end of file From 1689bf44fb907df7a375ab2c0f570938b8adf2e6 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 5 Jun 2020 11:24:27 +0100 Subject: [PATCH 121/717] Added the "component" shape to the example workspace. --- structurizr-examples/src/com/structurizr/example/Shapes.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/structurizr-examples/src/com/structurizr/example/Shapes.java b/structurizr-examples/src/com/structurizr/example/Shapes.java index 3e8838160..0ee7dc838 100644 --- a/structurizr-examples/src/com/structurizr/example/Shapes.java +++ b/structurizr-examples/src/com/structurizr/example/Shapes.java @@ -34,6 +34,7 @@ public static void main(String[] args) throws Exception { model.addSoftwareSystem("Folder", "Description").addTags("Folder"); model.addSoftwareSystem("Robot", "Description").addTags("Robot"); model.addPerson("Person", "Description").addTags("Person"); + model.addSoftwareSystem("Component", "Description").addTags("Component"); ViewSet views = workspace.getViews(); SystemLandscapeView view = views.createSystemLandscapeView("shapes", "An example of all shapes available in Structurizr."); @@ -42,7 +43,7 @@ public static void main(String[] args) throws Exception { Styles styles = views.getConfiguration().getStyles(); - styles.addElementStyle(Tags.ELEMENT).color("#ffffff").background("#438dd5").fontSize(34).width(650).height(400); + styles.addElementStyle(Tags.ELEMENT).color("#ffffff").background("#438dd5").fontSize(34).width(650).height(400).description(false).metadata(false); styles.addElementStyle("Box").shape(Shape.Box); styles.addElementStyle("RoundedBox").shape(Shape.RoundedBox); styles.addElementStyle("Ellipse").shape(Shape.Ellipse); @@ -56,6 +57,7 @@ public static void main(String[] args) throws Exception { styles.addElementStyle("Hexagon").shape(Shape.Hexagon); styles.addElementStyle("Robot").shape(Shape.Robot).width(550); styles.addElementStyle("Person").shape(Shape.Person).width(550); + styles.addElementStyle("Component").shape(Shape.Component); StructurizrClient structurizrClient = new StructurizrClient(API_KEY, API_SECRET); structurizrClient.putWorkspace(WORKSPACE_ID, workspace); From 7465e24414379f2ff20ba41dcfc39ae69a8b7028 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 5 Jun 2020 11:24:46 +0100 Subject: [PATCH 122/717] And updated the images. --- docs/images/styling-elements-5.png | Bin 248634 -> 382973 bytes docs/images/styling-elements-6.png | Bin 114441 -> 75001 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/images/styling-elements-5.png b/docs/images/styling-elements-5.png index 889bf86498299b460116494458127515e91d1f43..9fb1cace7f3495b6e565fddacefd58c3e53b991c 100644 GIT binary patch literal 382973 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 z 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

    K+%Pw@9RBno}V70m314o087c)YEZ%9T%R`_ZJs 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 From f3168aed7bd77af30e3dece764cd304987c8da9c Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 5 Jun 2020 11:25:47 +0100 Subject: [PATCH 123/717] Ignore directories and filenames starting with a dot. --- .../AutomaticDocumentationTemplate.java | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/structurizr-core/src/com/structurizr/documentation/AutomaticDocumentationTemplate.java b/structurizr-core/src/com/structurizr/documentation/AutomaticDocumentationTemplate.java index 6fb8e89aa..5abb2eaa8 100644 --- a/structurizr-core/src/com/structurizr/documentation/AutomaticDocumentationTemplate.java +++ b/structurizr-core/src/com/structurizr/documentation/AutomaticDocumentationTemplate.java @@ -66,24 +66,26 @@ private List

    add(SoftwareSystem softwareSystem, File directory) throws Arrays.sort(filesInDirectory); for (File file : filesInDirectory) { - Format format = FormatFinder.findFormat(file); - String sectionDefinition = ""; - - if (format == Format.Markdown) { - sectionDefinition = "##"; - } else if (format == Format.AsciiDoc) { - sectionDefinition = "=="; - } - - String content = new String(Files.readAllBytes(file.toPath()), "UTF-8"); - String sectionName = file.getName(); - Matcher matcher = Pattern.compile("^" + sectionDefinition + " (.*?)$", Pattern.MULTILINE).matcher(content); - if (matcher.find()) { - sectionName = matcher.group(1); + if (!file.isDirectory() && !file.getName().startsWith(".")) { + Format format = FormatFinder.findFormat(file); + String sectionDefinition = ""; + + if (format == Format.Markdown) { + sectionDefinition = "##"; + } else if (format == Format.AsciiDoc) { + sectionDefinition = "=="; + } + + String content = new String(Files.readAllBytes(file.toPath()), "UTF-8"); + String sectionName = file.getName(); + Matcher matcher = Pattern.compile("^" + sectionDefinition + " (.*?)$", Pattern.MULTILINE).matcher(content); + if (matcher.find()) { + sectionName = matcher.group(1); + } + + Section section = addSection(softwareSystem, sectionName, format, content); + sections.add(section); } - - Section section = addSection(softwareSystem, sectionName, format, content); - sections.add(section); } } From 729039405d452f6241bb2e00a3172294af603e15 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 5 Jun 2020 11:53:13 +0100 Subject: [PATCH 124/717] Updated version/release date. --- docs/binaries.md | 4 ++-- docs/changelog.md | 2 +- docs/getting-started.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/binaries.md b/docs/binaries.md index 44ad85d80..b043c2a1c 100644 --- a/docs/binaries.md +++ b/docs/binaries.md @@ -3,5 +3,5 @@ The "Structurizr for Java" binaries are hosted on [Maven Central](https://repo1. Name | Description ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-core:1.3.5 | The core library that can used to create software architecture models. -com.structurizr:structurizr-client:1.3.5 | The API client for publishing models on the Structurizr cloud service and on-premises installation. \ No newline at end of file +com.structurizr:structurizr-core:1.4.0 | The core library that can used to create software architecture models. +com.structurizr:structurizr-client:1.4.0 | The API client for publishing models on the Structurizr cloud service and on-premises installation. \ No newline at end of file diff --git a/docs/changelog.md b/docs/changelog.md index d7d907cb9..349d1b1b5 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 1.4.0 (unreleased) +## 1.4.0 (5th June 2020) - Added a "Component" element shape. - Added a "Dotted" element border style. diff --git a/docs/getting-started.md b/docs/getting-started.md index de3153924..eec7df1d2 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -12,7 +12,7 @@ The Structurizr for Java binaries are hosted on [Maven Central](https://repo1.ma Name | Description ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-client:1.3.5 | The Structurizr API client library. +com.structurizr:structurizr-client:1.4.0 | The Structurizr API client library. ## 2. Create a Java program From 3090bb706a76d2fea3267ca798c00df9a1edb867 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 14 Jun 2020 15:00:07 +0100 Subject: [PATCH 125/717] Fixes a bug that defaults the relationship interaction style to Synchronous, when it's specifically set to null. --- build.gradle | 2 +- docs/changelog.md | 4 ++++ .../src/com/structurizr/model/Relationship.java | 4 ++-- .../unit/com/structurizr/model/RelationshipTests.java | 9 +++++++++ 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index e8d8eca3a..c17df1dc5 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.4.0' + version = '1.4.1' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 349d1b1b5..3489372a9 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.4.1 (14th June 2020) + +Fixes a bug that defaults the relationship interaction style to Synchronous, when it's specifically set to null. + ## 1.4.0 (5th June 2020) - Added a "Component" element shape. diff --git a/structurizr-core/src/com/structurizr/model/Relationship.java b/structurizr-core/src/com/structurizr/model/Relationship.java index 22b9e3765..a503ba96b 100644 --- a/structurizr-core/src/com/structurizr/model/Relationship.java +++ b/structurizr-core/src/com/structurizr/model/Relationship.java @@ -18,7 +18,7 @@ public final class Relationship extends ModelItem { private String destinationId; private String description; private String technology; - private InteractionStyle interactionStyle = InteractionStyle.Synchronous; + private InteractionStyle interactionStyle; private String url; private String linkedRelationshipId; @@ -37,7 +37,7 @@ public final class Relationship extends ModelItem { if (interactionStyle == InteractionStyle.Synchronous) { addTags(Tags.SYNCHRONOUS); - } else { + } else if (interactionStyle == InteractionStyle.Asynchronous) { addTags(Tags.ASYNCHRONOUS); } } diff --git a/structurizr-core/test/unit/com/structurizr/model/RelationshipTests.java b/structurizr-core/test/unit/com/structurizr/model/RelationshipTests.java index 1c96dc712..9af70a713 100644 --- a/structurizr-core/test/unit/com/structurizr/model/RelationshipTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/RelationshipTests.java @@ -105,4 +105,13 @@ public void test_setUrl_DoesNothing_WhenAnEmptyUrlIsSpecified() { assertNull(relationship.getUrl()); } + @Test + public void test_interactionStyle_CanBeSetToNull() { + Relationship relationship = softwareSystem1.uses(softwareSystem2, "Uses 1", "Technology", null); + + assertNull(relationship.getInteractionStyle()); + assertFalse(relationship.getTagsAsSet().contains(Tags.ASYNCHRONOUS)); + assertFalse(relationship.getTagsAsSet().contains(Tags.SYNCHRONOUS)); + } + } \ No newline at end of file From db252b0e4eecf9e77b663f6567026645c522ca20 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 17 Jun 2020 14:20:19 +0100 Subject: [PATCH 126/717] Updated banner. --- docs/images/structurizr-banner.png | Bin 74492 -> 53460 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/images/structurizr-banner.png b/docs/images/structurizr-banner.png index 8edc28cf131ba7fd3bce60bbe44ca8b0a7943761..a9a0b0abc1be158cdfb68820cca61d401ef6ebde 100644 GIT binary patch literal 53460 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 literal 74492 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 From 4ebd74d6d6e1e5a6589815fa7c2c976437815053 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 17 Jun 2020 14:20:46 +0100 Subject: [PATCH 127/717] Updated docs. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d258b90bb..bcc30c04c 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ public static void main(String[] args) throws Exception { } ``` -The view can then be exported to be visualised using the [Structurizr service](https://structurizr.com), or other formats including PlantUML, WebSequenceDiagrams and Graphviz via the [Structurizr for Java extensions](https://github.com/structurizr/java-extensions). +The view can then be exported to be visualised using the [Structurizr cloud service/on-premises installation](https://structurizr.com), or other formats including PlantUML and WebSequenceDiagrams via the [Structurizr for Java extensions](https://github.com/structurizr/java-extensions). ![Views can be exported and visualised in many ways; e.g. PlantUML, Structurizr and Graphviz](docs/images/readme-1.png) From 27c9333fbf67bcf458442583a14756ab9af516de Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 17 Jun 2020 14:22:29 +0100 Subject: [PATCH 128/717] Allows the Structurizr client agent string to be changed ... useful for frameworks wrapping this one. --- .../structurizr/api/StructurizrClient.java | 32 +++++++++++++-- .../api/StructurizrClientTests.java | 40 ++++++++++++++++++- 2 files changed, 66 insertions(+), 6 deletions(-) diff --git a/structurizr-client/src/com/structurizr/api/StructurizrClient.java b/structurizr-client/src/com/structurizr/api/StructurizrClient.java index b40e33039..f73a664dd 100644 --- a/structurizr-client/src/com/structurizr/api/StructurizrClient.java +++ b/structurizr-client/src/com/structurizr/api/StructurizrClient.java @@ -56,6 +56,8 @@ public final class StructurizrClient { private static final String WORKSPACE_PATH = "/workspace/"; + private String agent = STRUCTURIZR_FOR_JAVA_AGENT; + private String url; private String apiKey; private String apiSecret; @@ -117,6 +119,28 @@ public StructurizrClient(String url, String apiKey, String apiSecret) { setApiSecret(apiSecret); } + /** + * Gets the agent string used to identify this client instance. + * + * @return "structurizr-java/{version}", unless overridden + */ + public String getAgent() { + return agent; + } + + /** + * Sets the agent string used to identify this client instance. + * + * @param agent + */ + public void setAgent(String agent) { + if (StringUtils.isNullOrEmpty(agent)) { + throw new IllegalArgumentException("An agent must be provided."); + } + + this.agent = agent.trim(); + } + /** * Gets the API URL that this client is for. * @@ -233,10 +257,10 @@ private boolean manageLockForWorkspace(long workspaceId, boolean lock) throws St if (lock) { log.info("Locking workspace with ID " + workspaceId); - httpRequest = new HttpPut(url + WORKSPACE_PATH + workspaceId + "/lock?user=" + getUser() + "&agent=" + STRUCTURIZR_FOR_JAVA_AGENT); + httpRequest = new HttpPut(url + WORKSPACE_PATH + workspaceId + "/lock?user=" + getUser() + "&agent=" + agent); } else { log.info("Unlocking workspace with ID " + workspaceId); - httpRequest = new HttpDelete(url + WORKSPACE_PATH + workspaceId + "/lock?user=" + getUser() + "&agent=" + STRUCTURIZR_FOR_JAVA_AGENT); + httpRequest = new HttpDelete(url + WORKSPACE_PATH + workspaceId + "/lock?user=" + getUser() + "&agent=" + agent); } addHeaders(httpRequest, "", ""); @@ -336,7 +360,7 @@ public void putWorkspace(long workspaceId, Workspace workspace) throws Structuri workspace.setId(workspaceId); workspace.setThumbnail(null); workspace.setLastModifiedDate(new Date()); - workspace.setLastModifiedAgent(STRUCTURIZR_FOR_JAVA_AGENT); + workspace.setLastModifiedAgent(agent); workspace.setLastModifiedUser(getUser()); workspace.countAndLogWarnings(); @@ -402,7 +426,7 @@ private void addHeaders(HttpUriRequestBase httpRequest, String content, String c HashBasedMessageAuthenticationCode hmac = new HashBasedMessageAuthenticationCode(apiSecret); HmacContent hmacContent = new HmacContent(httpMethod, path, contentMd5, contentType, nonce); - httpRequest.addHeader(HttpHeaders.USER_AGENT, STRUCTURIZR_FOR_JAVA_AGENT); + httpRequest.addHeader(HttpHeaders.USER_AGENT, agent); httpRequest.addHeader(HttpHeaders.AUTHORIZATION, new HmacAuthorizationHeader(apiKey, hmac.generate(hmacContent.toString())).format()); httpRequest.addHeader(HttpHeaders.NONCE, nonce); diff --git a/structurizr-client/test/unit/com/structurizr/api/StructurizrClientTests.java b/structurizr-client/test/unit/com/structurizr/api/StructurizrClientTests.java index a5c37e972..d0a6caab3 100644 --- a/structurizr-client/test/unit/com/structurizr/api/StructurizrClientTests.java +++ b/structurizr-client/test/unit/com/structurizr/api/StructurizrClientTests.java @@ -3,8 +3,7 @@ import com.structurizr.Workspace; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; public class StructurizrClientTests { @@ -137,4 +136,41 @@ public void test_constructionWithAPropertiesFile_ThrowsAnException_WhenNoPropert } } + @Test + public void test_getAgent() { + structurizrClient = new StructurizrClient("key", "secret"); + assertTrue(structurizrClient.getAgent().startsWith("structurizr-java/")); + } + + @Test + public void test_setAgent() { + structurizrClient = new StructurizrClient("key", "secret"); + structurizrClient.setAgent("new_agent"); + assertEquals("new_agent", structurizrClient.getAgent()); + } + + @Test + public void test_setAgent_ThrowsAnException_WhenPassedNull() { + structurizrClient = new StructurizrClient("key", "secret"); + + try { + structurizrClient.setAgent(null); + fail(); + } catch (Exception e) { + assertEquals("An agent must be provided.", e.getMessage()); + } + } + + @Test + public void test_setAgent_ThrowsAnException_WhenPassedAnEmptyString() { + structurizrClient = new StructurizrClient("key", "secret"); + + try { + structurizrClient.setAgent(" "); + fail(); + } catch (Exception e) { + assertEquals("An agent must be provided.", e.getMessage()); + } + } + } \ No newline at end of file From f3864d3a61a13d552e399572c41f4dc6906d205a Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 18 Jun 2020 12:14:33 +0100 Subject: [PATCH 129/717] Fixed javadoc warning. --- .../src/com/structurizr/api/StructurizrClient.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structurizr-client/src/com/structurizr/api/StructurizrClient.java b/structurizr-client/src/com/structurizr/api/StructurizrClient.java index f73a664dd..77a98dc53 100644 --- a/structurizr-client/src/com/structurizr/api/StructurizrClient.java +++ b/structurizr-client/src/com/structurizr/api/StructurizrClient.java @@ -131,7 +131,7 @@ public String getAgent() { /** * Sets the agent string used to identify this client instance. * - * @param agent + * @param agent the agent string */ public void setAgent(String agent) { if (StringUtils.isNullOrEmpty(agent)) { From ed74de5da61d713f68fa974a4eff0a963ca88662 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 18 Jun 2020 12:16:00 +0100 Subject: [PATCH 130/717] Adds the ability to add container instances and infrastructure nodes to the same animation step on a deployment view. --- .../com/structurizr/view/DeploymentView.java | 31 +++++++++++++++---- .../structurizr/view/DeploymentViewTests.java | 12 ++++++- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/structurizr-core/src/com/structurizr/view/DeploymentView.java b/structurizr-core/src/com/structurizr/view/DeploymentView.java index 58356d650..b0c1739d0 100644 --- a/structurizr-core/src/com/structurizr/view/DeploymentView.java +++ b/structurizr-core/src/com/structurizr/view/DeploymentView.java @@ -5,10 +5,7 @@ import com.structurizr.util.StringUtils; import javax.annotation.Nonnull; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; /** * A deployment view, used to show the mapping of container instances to deployment nodes. @@ -171,7 +168,7 @@ public void addAnimation(InfrastructureNode... infrastructureNodes) { throw new IllegalArgumentException("One or more infrastructure nodes must be specified."); } - addAnimationStep(infrastructureNodes); + addAnimation(new ContainerInstance[0], infrastructureNodes); } /** @@ -184,7 +181,29 @@ public void addAnimation(ContainerInstance... containerInstances) { throw new IllegalArgumentException("One or more container instances must be specified."); } - addAnimationStep(containerInstances); + addAnimation(containerInstances, new InfrastructureNode[0]); + } + + /** + * Adds an animation step, with the specified container instances and infrastructure nodes. + * + * @param containerInstances the container instances that should be shown in the animation step + * @param infrastructureNodes the container infrastructure nodes that should be shown in the animation step + */ + public void addAnimation(ContainerInstance[] containerInstances, InfrastructureNode[] infrastructureNodes) { + if ((containerInstances == null || containerInstances.length == 0) && (infrastructureNodes == null || infrastructureNodes.length == 0)) { + throw new IllegalArgumentException("One or more container instances/infrastructure nodes must be specified."); + } + + List elements = new ArrayList<>(); + if (containerInstances != null) { + Collections.addAll(elements, containerInstances); + } + if (infrastructureNodes != null) { + Collections.addAll(elements, infrastructureNodes); + } + + addAnimationStep(elements.toArray(new Element[0])); } private void addAnimationStep(Element... elements) { diff --git a/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java b/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java index 04d0b4a3d..7fe692472 100644 --- a/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java @@ -1,7 +1,6 @@ package com.structurizr.view; import com.structurizr.AbstractWorkspaceTestBase; -import com.structurizr.Workspace; import com.structurizr.model.*; import org.junit.Before; import org.junit.Test; @@ -174,6 +173,17 @@ public void test_addAnimationStep_ThrowsAnException_WhenNoInfrastructureNodesAre } } + @Test + public void test_addAnimationStep_ThrowsAnException_WhenNoContainerInstancesOrInfrastructureNodesAreSpecified() { + try { + deploymentView = views.createDeploymentView("deployment", "Description"); + deploymentView.addAnimation(null, null); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("One or more container instances/infrastructure nodes must be specified.", iae.getMessage()); + } + } + @Test public void test_addAnimationStep() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); From b524ef7212646a54190b100c8b9a6a8488c1d67f Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 18 Jun 2020 12:18:32 +0100 Subject: [PATCH 131/717] Updated changelog. --- docs/changelog.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 3489372a9..02d3c22a4 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,8 +1,13 @@ # Changelog +## 1.4.2 (18th June 2020) + +- Adds the ability to add container instances and infrastructure nodes to the same animation step on a deployment view. +- Adds the ability to override the Structurizr client agent string. + ## 1.4.1 (14th June 2020) -Fixes a bug that defaults the relationship interaction style to Synchronous, when it's specifically set to null. +- Fixes a bug that defaults the relationship interaction style to Synchronous, when it's specifically set to null. ## 1.4.0 (5th June 2020) From 05d88580bc199ed60b13a1631a0b2222f00c0207 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 18 Jun 2020 12:19:05 +0100 Subject: [PATCH 132/717] Updated version number. --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index c17df1dc5..fcde94cc1 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.4.1' + version = '1.4.2' repositories { mavenCentral() From 6172248b6bb985ccbfaaa9a59b8a9c0e5ceccc6e Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 18 Jun 2020 17:24:07 +0100 Subject: [PATCH 133/717] Fixes a bug where all deployment nodes would be added to a deployment view, even if that view had an environment set. --- build.gradle | 2 +- docs/changelog.md | 4 ++++ .../src/com/structurizr/view/DeploymentView.java | 10 ++++++++-- .../com/structurizr/view/DeploymentViewTests.java | 13 +++++++++++++ 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index fcde94cc1..6ea31e4dc 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.4.2' + version = '1.4.3' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 02d3c22a4..e0887adf6 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.4.3 (unreleased) + +- Fixes a bug where all deployment nodes would be added to a deployment view, even if that view had an environment set. + ## 1.4.2 (18th June 2020) - Adds the ability to add container instances and infrastructure nodes to the same animation step on a deployment view. diff --git a/structurizr-core/src/com/structurizr/view/DeploymentView.java b/structurizr-core/src/com/structurizr/view/DeploymentView.java index b0c1739d0..5fecf3687 100644 --- a/structurizr-core/src/com/structurizr/view/DeploymentView.java +++ b/structurizr-core/src/com/structurizr/view/DeploymentView.java @@ -43,10 +43,16 @@ void setModel(Model model) { } /** - * Adds all of the top-level deployment nodes to this view. + * Adds all of the top-level deployment nodes to this view, for the same deployment environment (if set). */ public void addAllDeploymentNodes() { - getModel().getDeploymentNodes().forEach(this::add); + for (DeploymentNode deploymentNode : getModel().getDeploymentNodes()) { + if (deploymentNode.getParent() == null) { + if (this.getEnvironment() == null || this.getEnvironment().equals(deploymentNode.getEnvironment())) { + add(deploymentNode); + } + } + } } /** diff --git a/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java b/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java index 7fe692472..9d4c6cbbf 100644 --- a/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java @@ -82,6 +82,19 @@ public void test_addAllDeploymentNodes_DoesNothing_WhenThereAreTopLevelDeploymen assertEquals(0, deploymentView.getElements().size()); } + @Test + public void test_addAllDeploymentNodes_DoesNothing_WhenThereNoDeploymentNodesForTheDeploymentEnvironment() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); + Container container = softwareSystem.addContainer("Container", "Description", "Technology"); + DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node", "Description", "Technology"); + ContainerInstance containerInstance = deploymentNode.add(container); + + deploymentView = views.createDeploymentView(softwareSystem, "deployment", "Description"); + deploymentView.setEnvironment("Live"); + deploymentView.addAllDeploymentNodes(); + assertEquals(0, deploymentView.getElements().size()); + } + @Test public void test_addAllDeploymentNodes_AddsDeploymentNodesAndContainerInstances_WhenThereAreTopLevelDeploymentNodesWithContainerInstances() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); From 07e32580e2dc90f82da4d4c331447af6ca9f1b97 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 19 Jun 2020 14:27:44 +0100 Subject: [PATCH 134/717] Tags should be trimmed before use. --- structurizr-core/src/com/structurizr/model/ModelItem.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/structurizr-core/src/com/structurizr/model/ModelItem.java b/structurizr-core/src/com/structurizr/model/ModelItem.java index 9db11faa7..617a7b5d5 100644 --- a/structurizr-core/src/com/structurizr/model/ModelItem.java +++ b/structurizr-core/src/com/structurizr/model/ModelItem.java @@ -87,7 +87,7 @@ public void addTags(String... tags) { for (String tag : tags) { if (tag != null) { - this.tags.add(tag); + this.tags.add(tag.trim()); } } } @@ -101,7 +101,7 @@ public void addTags(String... tags) { */ public boolean removeTag(String tag) { if (tag != null) { - return this.tags.remove(tag); + return this.tags.remove(tag.trim()); } return false; } @@ -114,7 +114,7 @@ public boolean removeTag(String tag) { * required tags defined by the model in getRequiredTags(), false otherwise */ public boolean hasTag(String tag) { - return getTagsAsSet().contains(tag); + return getTagsAsSet().contains(tag.trim()); } /** From dbba21dd92030315e5c18d2d2b9e55912092318a Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 19 Jun 2020 15:09:49 +0100 Subject: [PATCH 135/717] Adds support for removing deployment nodes, infrastructure nodes, and container instances from deployment views. --- docs/changelog.md | 1 + .../com/structurizr/view/DeploymentView.java | 39 ++++++++++ .../structurizr/view/DeploymentViewTests.java | 75 +++++++++++++++++++ 3 files changed, 115 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index e0887adf6..28630bc10 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -3,6 +3,7 @@ ## 1.4.3 (unreleased) - Fixes a bug where all deployment nodes would be added to a deployment view, even if that view had an environment set. +- Adds support for removing deployment nodes, infrastructure nodes, and container instances from deployment views. ## 1.4.2 (18th June 2020) diff --git a/structurizr-core/src/com/structurizr/view/DeploymentView.java b/structurizr-core/src/com/structurizr/view/DeploymentView.java index 5fecf3687..063f3fac9 100644 --- a/structurizr-core/src/com/structurizr/view/DeploymentView.java +++ b/structurizr-core/src/com/structurizr/view/DeploymentView.java @@ -84,6 +84,45 @@ public void add(@Nonnull DeploymentNode deploymentNode, boolean addRelationships } } + /** + * Removes the given deployment node from this view. + * + * @param deploymentNode the DeploymentNode to be removed + */ + public void remove(@Nonnull DeploymentNode deploymentNode) { + for (ContainerInstance containerInstance : deploymentNode.getContainerInstances()) { + remove(containerInstance); + } + + for (InfrastructureNode infrastructureNode : deploymentNode.getInfrastructureNodes()) { + remove(infrastructureNode); + } + + for (DeploymentNode child : deploymentNode.getChildren()) { + remove(child); + } + + removeElement(deploymentNode); + } + + /** + * Removes the given infrastructure node from this view. + * + * @param infrastructureNode the InfrastructureNode to be removed + */ + public void remove(@Nonnull InfrastructureNode infrastructureNode) { + removeElement(infrastructureNode); + } + + /** + * Removes the given infrastructure node from this view. + * + * @param containerInstance the ContainerInstance to be removed + */ + public void remove(@Nonnull ContainerInstance containerInstance) { + removeElement(containerInstance); + } + private boolean addContainerInstancesAndDeploymentNodesAndInfrastructureNodes(DeploymentNode deploymentNode, boolean addRelationships) { boolean hasContainersOrInfrastructureNodes = false; for (ContainerInstance containerInstance : deploymentNode.getContainerInstances()) { diff --git a/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java b/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java index 9d4c6cbbf..13be7cee4 100644 --- a/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java @@ -280,4 +280,79 @@ public void test_addAnimationStep_ThrowsAnException_WhenContainerInstancesAreSpe } } + @Test + public void test_remove_RemovesTheInfrastructureNode() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); + Container container = softwareSystem.addContainer("Container", "Description", "Technology"); + DeploymentNode deploymentNodeParent = model.addDeploymentNode("Deployment Node", "Description", "Technology"); + DeploymentNode deploymentNodeChild = deploymentNodeParent.addDeploymentNode("Deployment Node", "Description", "Technology"); + InfrastructureNode infrastructureNode = deploymentNodeChild.addInfrastructureNode("Infrastructure Node"); + ContainerInstance containerInstance = deploymentNodeChild.add(container); + + deploymentView = views.createDeploymentView(softwareSystem, "deployment", "Description"); + deploymentView.addAllDeploymentNodes(); + assertEquals(4, deploymentView.getElements().size()); + + deploymentView.remove(infrastructureNode); + assertEquals(3, deploymentView.getElements().size()); + assertTrue(deploymentView.getElements().contains(new ElementView(deploymentNodeParent))); + assertTrue(deploymentView.getElements().contains(new ElementView(deploymentNodeChild))); + assertTrue(deploymentView.getElements().contains(new ElementView(containerInstance))); + } + + @Test + public void test_remove_RemovesTheContainerInstance() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); + Container container = softwareSystem.addContainer("Container", "Description", "Technology"); + DeploymentNode deploymentNodeParent = model.addDeploymentNode("Deployment Node", "Description", "Technology"); + DeploymentNode deploymentNodeChild = deploymentNodeParent.addDeploymentNode("Deployment Node", "Description", "Technology"); + InfrastructureNode infrastructureNode = deploymentNodeChild.addInfrastructureNode("Infrastructure Node"); + ContainerInstance containerInstance = deploymentNodeChild.add(container); + + deploymentView = views.createDeploymentView(softwareSystem, "deployment", "Description"); + deploymentView.addAllDeploymentNodes(); + assertEquals(4, deploymentView.getElements().size()); + + deploymentView.remove(containerInstance); + assertEquals(3, deploymentView.getElements().size()); + assertTrue(deploymentView.getElements().contains(new ElementView(deploymentNodeParent))); + assertTrue(deploymentView.getElements().contains(new ElementView(deploymentNodeChild))); + assertTrue(deploymentView.getElements().contains(new ElementView(infrastructureNode))); + } + + @Test + public void test_remove_RemovesTheDeploymentNodeAndChildren() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); + Container container = softwareSystem.addContainer("Container", "Description", "Technology"); + DeploymentNode deploymentNodeParent = model.addDeploymentNode("Deployment Node", "Description", "Technology"); + DeploymentNode deploymentNodeChild = deploymentNodeParent.addDeploymentNode("Deployment Node", "Description", "Technology"); + InfrastructureNode infrastructureNode = deploymentNodeChild.addInfrastructureNode("Infrastructure Node"); + ContainerInstance containerInstance = deploymentNodeChild.add(container); + + deploymentView = views.createDeploymentView(softwareSystem, "deployment", "Description"); + deploymentView.addAllDeploymentNodes(); + assertEquals(4, deploymentView.getElements().size()); + + deploymentView.remove(deploymentNodeChild); + assertEquals(1, deploymentView.getElements().size()); + assertTrue(deploymentView.getElements().contains(new ElementView(deploymentNodeParent))); + } + + @Test + public void test_remove_RemovesTheChildDeploymentNodeAndChildren() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); + Container container = softwareSystem.addContainer("Container", "Description", "Technology"); + DeploymentNode deploymentNodeParent = model.addDeploymentNode("Deployment Node", "Description", "Technology"); + DeploymentNode deploymentNodeChild = deploymentNodeParent.addDeploymentNode("Deployment Node", "Description", "Technology"); + InfrastructureNode infrastructureNode = deploymentNodeChild.addInfrastructureNode("Infrastructure Node"); + ContainerInstance containerInstance = deploymentNodeChild.add(container); + + deploymentView = views.createDeploymentView(softwareSystem, "deployment", "Description"); + deploymentView.addAllDeploymentNodes(); + assertEquals(4, deploymentView.getElements().size()); + + deploymentView.remove(deploymentNodeParent); + assertEquals(0, deploymentView.getElements().size()); + } + } \ No newline at end of file From eae8eab39c2d296b3c36ba24997018fd94954e8c Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 19 Jun 2020 15:18:29 +0100 Subject: [PATCH 136/717] Updated release date and version. --- docs/binaries.md | 4 ++-- docs/changelog.md | 2 +- docs/getting-started.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/binaries.md b/docs/binaries.md index b043c2a1c..d8750639c 100644 --- a/docs/binaries.md +++ b/docs/binaries.md @@ -3,5 +3,5 @@ The "Structurizr for Java" binaries are hosted on [Maven Central](https://repo1. Name | Description ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-core:1.4.0 | The core library that can used to create software architecture models. -com.structurizr:structurizr-client:1.4.0 | The API client for publishing models on the Structurizr cloud service and on-premises installation. \ No newline at end of file +com.structurizr:structurizr-core:1.4.3 | The core library that can used to create software architecture models. +com.structurizr:structurizr-client:1.4.3 | The API client for publishing models on the Structurizr cloud service and on-premises installation. \ No newline at end of file diff --git a/docs/changelog.md b/docs/changelog.md index 28630bc10..0f73f5301 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 1.4.3 (unreleased) +## 1.4.3 (19th June 2020) - Fixes a bug where all deployment nodes would be added to a deployment view, even if that view had an environment set. - Adds support for removing deployment nodes, infrastructure nodes, and container instances from deployment views. diff --git a/docs/getting-started.md b/docs/getting-started.md index eec7df1d2..f7a334734 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -12,7 +12,7 @@ The Structurizr for Java binaries are hosted on [Maven Central](https://repo1.ma Name | Description ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-client:1.4.0 | The Structurizr API client library. +com.structurizr:structurizr-client:1.4.3 | The Structurizr API client library. ## 2. Create a Java program From d4d4609f1f0420a274a9da49baece1e67c3395b3 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 19 Jun 2020 16:34:07 +0100 Subject: [PATCH 137/717] Fixes a bug where deployment node instances could set to a non-positive integer. --- docs/changelog.md | 4 +++ .../com/structurizr/model/DeploymentNode.java | 4 +++ .../model/DeploymentNodeTests.java | 30 +++++++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 0f73f5301..0b3a7cdeb 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +# 1.4.4 (unreleased) + +- Fixes a bug where deployment node instances could set to a non-positive integer. + ## 1.4.3 (19th June 2020) - Fixes a bug where all deployment nodes would be added to a deployment view, even if that view had an environment set. diff --git a/structurizr-core/src/com/structurizr/model/DeploymentNode.java b/structurizr-core/src/com/structurizr/model/DeploymentNode.java index 7b7544d17..2733c4377 100644 --- a/structurizr-core/src/com/structurizr/model/DeploymentNode.java +++ b/structurizr-core/src/com/structurizr/model/DeploymentNode.java @@ -288,6 +288,10 @@ public int getInstances() { } public void setInstances(int instances) { + if (instances < 1) { + throw new IllegalArgumentException("Number of instances must be a positive integer."); + } + this.instances = instances; } diff --git a/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java b/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java index 7d7685c6e..80b131e35 100644 --- a/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java @@ -179,4 +179,34 @@ public void test_getInfrastructureNodeWithName_ReturnsTheNamedDeploymentNode_Whe assertSame(child, parent.getInfrastructureNodeWithName("child")); } + @Test + public void test_setInstances() { + DeploymentNode deploymentNode = new DeploymentNode(); + deploymentNode.setInstances(8); + + assertEquals(8, deploymentNode.getInstances()); + } + + @Test + public void test_setInstances_ThrowsAnException_WhenZeroIsSpecified() { + try { + DeploymentNode deploymentNode = new DeploymentNode(); + deploymentNode.setInstances(0); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("Number of instances must be a positive integer.", iae.getMessage()); + } + } + + @Test + public void test_setInstances_ThrowsAnException_WhenANegativeNumberIsSpecified() { + try { + DeploymentNode deploymentNode = new DeploymentNode(); + deploymentNode.setInstances(-1); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("Number of instances must be a positive integer.", iae.getMessage()); + } + } + } \ No newline at end of file From af2b16df55bc74edd8e5bd8820cd9bf2e4fd90fe Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 19 Jun 2020 17:04:52 +0100 Subject: [PATCH 138/717] The original 1.4.3 release wasn't able to be released on Nexus, so I made a new release. --- docs/changelog.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 0b3a7cdeb..072873ae7 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,13 +1,10 @@ # Changelog -# 1.4.4 (unreleased) - -- Fixes a bug where deployment node instances could set to a non-positive integer. - ## 1.4.3 (19th June 2020) - Fixes a bug where all deployment nodes would be added to a deployment view, even if that view had an environment set. - Adds support for removing deployment nodes, infrastructure nodes, and container instances from deployment views. +- Fixes a bug where deployment node instances could set to a non-positive integer. ## 1.4.2 (18th June 2020) From 012a7c78b8039e8bfe666326c4a55ea7b64fde9d Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 21 Jun 2020 07:33:48 +0100 Subject: [PATCH 139/717] Adds an "addDefaultElements()" method to the static/deployment views. --- .../com/structurizr/view/ComponentView.java | 19 ++++++++++++ .../com/structurizr/view/ContainerView.java | 12 +++++++ .../com/structurizr/view/DeploymentView.java | 7 +++++ .../src/com/structurizr/view/StaticView.java | 5 +++ .../structurizr/view/SystemContextView.java | 9 ++++++ .../structurizr/view/SystemLandscapeView.java | 9 ++++++ .../structurizr/view/ComponentViewTests.java | 31 +++++++++++++++++++ .../structurizr/view/ContainerViewTests.java | 29 +++++++++++++++++ .../view/SystemContextViewTests.java | 21 +++++++++++++ .../view/SystemLandscapeViewTests.java | 14 +++++++++ 10 files changed, 156 insertions(+) diff --git a/structurizr-core/src/com/structurizr/view/ComponentView.java b/structurizr-core/src/com/structurizr/view/ComponentView.java index 86666aa2a..45b864d0b 100644 --- a/structurizr-core/src/com/structurizr/view/ComponentView.java +++ b/structurizr-core/src/com/structurizr/view/ComponentView.java @@ -172,6 +172,25 @@ public String getName() { return getSoftwareSystem().getName() + " - " + getContainer().getName() + " - Components"; } + /** + * Adds the default set of elements to this view. + */ + @Override + public void addDefaultElements() { + for (Component component : getContainer().getComponents()) { + add(component); + + for (Container container : getSoftwareSystem().getContainers()) { + if (container.hasEfferentRelationshipWith(component) || component.hasEfferentRelationshipWith(container)) { + add(container); + } + }; + + addNearestNeighbours(component, Person.class); + addNearestNeighbours(component, SoftwareSystem.class); + } + } + /** * Adds all people, software systems, sibling containers and components belonging to the container in scope. */ diff --git a/structurizr-core/src/com/structurizr/view/ContainerView.java b/structurizr-core/src/com/structurizr/view/ContainerView.java index 5990167f1..9c4313481 100644 --- a/structurizr-core/src/com/structurizr/view/ContainerView.java +++ b/structurizr-core/src/com/structurizr/view/ContainerView.java @@ -91,6 +91,18 @@ public String getName() { return getSoftwareSystem().getName() + " - Containers"; } + /** + * Adds the default set of elements to this view. + */ + @Override + public void addDefaultElements() { + for (Container container : getSoftwareSystem().getContainers()) { + add(container); + addNearestNeighbours(container, Person.class); + addNearestNeighbours(container, SoftwareSystem.class); + } + } + /** * Adds all people, software systems and containers that belong to the software system in scope. */ diff --git a/structurizr-core/src/com/structurizr/view/DeploymentView.java b/structurizr-core/src/com/structurizr/view/DeploymentView.java index 063f3fac9..2e2982551 100644 --- a/structurizr-core/src/com/structurizr/view/DeploymentView.java +++ b/structurizr-core/src/com/structurizr/view/DeploymentView.java @@ -42,6 +42,13 @@ void setModel(Model model) { this.model = model; } + /** + * Adds the default set of elements to this view. + */ + public void addDefaultElements() { + addAllDeploymentNodes(); + } + /** * Adds all of the top-level deployment nodes to this view, for the same deployment environment (if set). */ diff --git a/structurizr-core/src/com/structurizr/view/StaticView.java b/structurizr-core/src/com/structurizr/view/StaticView.java index c74881260..37b3b5dfb 100644 --- a/structurizr-core/src/com/structurizr/view/StaticView.java +++ b/structurizr-core/src/com/structurizr/view/StaticView.java @@ -25,6 +25,11 @@ public abstract class StaticView extends View { super(softwareSystem, key, description); } + /** + * Adds the default set of elements to this view. + */ + public abstract void addDefaultElements(); + /** * Adds all software systems in the model to this view. */ diff --git a/structurizr-core/src/com/structurizr/view/SystemContextView.java b/structurizr-core/src/com/structurizr/view/SystemContextView.java index 1d4675e61..c8f6e804b 100644 --- a/structurizr-core/src/com/structurizr/view/SystemContextView.java +++ b/structurizr-core/src/com/structurizr/view/SystemContextView.java @@ -33,6 +33,15 @@ public String getName() { return getSoftwareSystem().getName() + " - System Context"; } + /** + * Adds the default set of elements to this view. + */ + @Override + public void addDefaultElements() { + addNearestNeighbours(getSoftwareSystem(), Person.class); + addNearestNeighbours(getSoftwareSystem(), SoftwareSystem.class); + } + /** * Adds all software systems and all people. */ diff --git a/structurizr-core/src/com/structurizr/view/SystemLandscapeView.java b/structurizr-core/src/com/structurizr/view/SystemLandscapeView.java index 366a81bd2..bdde6c34e 100644 --- a/structurizr-core/src/com/structurizr/view/SystemLandscapeView.java +++ b/structurizr-core/src/com/structurizr/view/SystemLandscapeView.java @@ -50,6 +50,15 @@ void setModel(Model model) { this.model = model; } + /** + * Adds the default set of elements to this view. + */ + @Override + public void addDefaultElements() { + addAllSoftwareSystems(); + addAllPeople(); + } + /** * Adds all software systems and all people to this view. */ diff --git a/structurizr-core/test/unit/com/structurizr/view/ComponentViewTests.java b/structurizr-core/test/unit/com/structurizr/view/ComponentViewTests.java index 3accb53eb..71afc5e71 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ComponentViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ComponentViewTests.java @@ -574,4 +574,35 @@ public void test_addExternalDependencies_AddsTheParentSoftwareSystem_WhenACompon assertTrue(view.getElements().contains(new ElementView(softwareSystemB))); } + @Test + public void test_addDefaultElements() { + model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); + + Person user1 = model.addPerson("User 1"); + Person user2 = model.addPerson("User 2"); + SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1"); + Container container1 = softwareSystem1.addContainer("Container 1", "", ""); + Component component1 = container1.addComponent("Component 1", "", ""); + SoftwareSystem softwareSystem2 = model.addSoftwareSystem("Software System 2"); + Container container2 = softwareSystem2.addContainer("Container 2", "", ""); + Component component2 = container2.addComponent("Component 2", "", ""); + + user1.uses(component1, "Uses"); + user2.uses(component2, "Uses"); + component1.uses(component2, "Uses"); + + view = new ComponentView(container1, "components", "Description"); + view.addDefaultElements(); + + assertEquals(3, view.getElements().size()); + assertTrue(view.getElements().contains(new ElementView(user1))); + assertFalse(view.getElements().contains(new ElementView(user2))); + assertFalse(view.getElements().contains(new ElementView(softwareSystem1))); + assertTrue(view.getElements().contains(new ElementView(softwareSystem2))); + assertFalse(view.getElements().contains(new ElementView(container1))); + assertFalse(view.getElements().contains(new ElementView(container2))); + assertTrue(view.getElements().contains(new ElementView(component1))); + assertFalse(view.getElements().contains(new ElementView(component2))); + } + } \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java b/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java index 9df1efa4e..d2eab3813 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java @@ -1,6 +1,7 @@ package com.structurizr.view; import com.structurizr.AbstractWorkspaceTestBase; +import com.structurizr.Workspace; import com.structurizr.model.*; import org.junit.Before; import org.junit.Test; @@ -264,4 +265,32 @@ public void test_addDependentSoftwareSystem2() { assertEquals(2, view.getElements().size()); assertEquals(1, view.getRelationships().size()); } + + @Test + public void test_addDefaultElements() { + model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); + + Person user1 = model.addPerson("User 1"); + Person user2 = model.addPerson("User 2"); + SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1"); + Container container1 = softwareSystem1.addContainer("Container 1", "", ""); + SoftwareSystem softwareSystem2 = model.addSoftwareSystem("Software System 2"); + Container container2 = softwareSystem2.addContainer("Container 2", "", ""); + + user1.uses(container1, "Uses"); + user2.uses(container2, "Uses"); + container1.uses(container2, "Uses"); + + view = new ContainerView(softwareSystem1, "containers", "Description"); + view.addDefaultElements(); + + assertEquals(3, view.getElements().size()); + assertTrue(view.getElements().contains(new ElementView(user1))); + assertFalse(view.getElements().contains(new ElementView(user2))); + assertFalse(view.getElements().contains(new ElementView(softwareSystem1))); + assertTrue(view.getElements().contains(new ElementView(softwareSystem2))); + assertTrue(view.getElements().contains(new ElementView(container1))); + assertFalse(view.getElements().contains(new ElementView(container2))); + } + } diff --git a/structurizr-core/test/unit/com/structurizr/view/SystemContextViewTests.java b/structurizr-core/test/unit/com/structurizr/view/SystemContextViewTests.java index bd1a5317e..9c6c221f5 100644 --- a/structurizr-core/test/unit/com/structurizr/view/SystemContextViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/SystemContextViewTests.java @@ -280,4 +280,25 @@ public void test_isEnterpriseBoundaryVisible() { assertFalse(view.isEnterpriseBoundaryVisible()); } + @Test + public void test_addDefaultElements() { + Person user1 = model.addPerson("User 1"); + Person user2 = model.addPerson("User 2"); + SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1"); + SoftwareSystem softwareSystem2 = model.addSoftwareSystem("Software System 2"); + + user1.uses(softwareSystem1, ""); + softwareSystem1.uses(softwareSystem2, ""); + user2.uses(softwareSystem2, ""); + + view = views.createSystemContextView(softwareSystem1, "key", "description"); + view.addDefaultElements(); + + assertEquals(3, view.getElements().size()); + assertTrue(view.getElements().contains(new ElementView(user1))); + assertFalse(view.getElements().contains(new ElementView(user2))); + assertTrue(view.getElements().contains(new ElementView(softwareSystem1))); + assertTrue(view.getElements().contains(new ElementView(softwareSystem2))); + } + } \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/view/SystemLandscapeViewTests.java b/structurizr-core/test/unit/com/structurizr/view/SystemLandscapeViewTests.java index 0a7950c18..207272634 100644 --- a/structurizr-core/test/unit/com/structurizr/view/SystemLandscapeViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/SystemLandscapeViewTests.java @@ -186,4 +186,18 @@ public void test_addNearestNeighbours_AddsNearestNeighbours_WhenThereAreSomeNear assertTrue(view.getElements().contains(new ElementView(softwareSystem))); } + @Test + public void test_addDefaultElements() { + Person user = model.addPerson("User"); + SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1"); + SoftwareSystem softwareSystem2 = model.addSoftwareSystem("Software System 2"); + + view.addDefaultElements(); + + assertEquals(3, view.getElements().size()); + assertTrue(view.getElements().contains(new ElementView(user))); + assertTrue(view.getElements().contains(new ElementView(softwareSystem1))); + assertTrue(view.getElements().contains(new ElementView(softwareSystem2))); + } + } \ No newline at end of file From 3bc685467b1d807336ea7c37b0573b4f5246e7f1 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 21 Jun 2020 07:55:48 +0100 Subject: [PATCH 140/717] Added some shortened add methods. --- .../src/com/structurizr/model/Model.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/structurizr-core/src/com/structurizr/model/Model.java b/structurizr-core/src/com/structurizr/model/Model.java index 2191c067a..60abc0e15 100644 --- a/structurizr-core/src/com/structurizr/model/Model.java +++ b/structurizr-core/src/com/structurizr/model/Model.java @@ -47,6 +47,17 @@ public void setEnterprise(Enterprise enterprise) { this.enterprise = enterprise; } + /** + * Creates a software system (with an unspecified location) and adds it to the model. + * + * @param name the name of the software system + * @return the SoftwareSystem instance created and added to the model (or null) + * @throws IllegalArgumentException if a software system with the same name already exists + */ + public SoftwareSystem addSoftwareSystem(@Nonnull String name) { + return addSoftwareSystem(name, ""); + } + /** * Creates a software system (with an unspecified location) and adds it to the model. * @@ -87,6 +98,18 @@ public SoftwareSystem addSoftwareSystem(@Nullable Location location, @Nonnull St } } + /** + * Creates a person (with an unspecified location) and adds it to the model. + * + * @param name the name of the person (e.g. "Admin User" or "Bob the Business User") + * @return the Person instance created and added to the model (or null) + * @throws IllegalArgumentException if a person with the same name already exists + */ + @Nonnull + public Person addPerson(@Nonnull String name) { + return addPerson(name, ""); + } + /** * Creates a person (with an unspecified location) and adds it to the model. * From 9445b4cbda6e0b88140208b5a51f520852396a48 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 21 Jun 2020 07:56:16 +0100 Subject: [PATCH 141/717] Adds an "addDefaultStyles()" method to Styles. --- structurizr-core/src/com/structurizr/view/Styles.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/structurizr-core/src/com/structurizr/view/Styles.java b/structurizr-core/src/com/structurizr/view/Styles.java index 05cb1fb01..3f50f8476 100644 --- a/structurizr-core/src/com/structurizr/view/Styles.java +++ b/structurizr-core/src/com/structurizr/view/Styles.java @@ -2,6 +2,7 @@ import com.structurizr.model.Element; import com.structurizr.model.Relationship; +import com.structurizr.model.Tags; import com.structurizr.util.StringUtils; import java.util.Collection; @@ -58,6 +59,14 @@ public void clearRelationshipStyles() { this.relationships = new LinkedList<>(); } + public void addDefaultStyles() { + addElementStyle(Tags.ELEMENT).shape(Shape.RoundedBox); + addElementStyle(Tags.SOFTWARE_SYSTEM).background("#1168bd").color("#ffffff"); + addElementStyle(Tags.CONTAINER).background("#438dd5").color("#ffffff"); + addElementStyle(Tags.COMPONENT).background("#85bbf0").color("#000000"); + addElementStyle(Tags.PERSON).background("#08427b").color("#ffffff").shape(Shape.Person); + } + public Collection getRelationships() { return relationships; } From 08d7b128900a4b3e4316270ef9450e203c3b870e Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 21 Jun 2020 07:56:41 +0100 Subject: [PATCH 142/717] Adds a "createDefaultViews()" method to Views. --- .../src/com/structurizr/view/ViewSet.java | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/structurizr-core/src/com/structurizr/view/ViewSet.java b/structurizr-core/src/com/structurizr/view/ViewSet.java index da29af4b1..fb8af14dc 100644 --- a/structurizr-core/src/com/structurizr/view/ViewSet.java +++ b/structurizr-core/src/com/structurizr/view/ViewSet.java @@ -2,12 +2,14 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonSetter; +import com.structurizr.Workspace; import com.structurizr.WorkspaceValidationException; import com.structurizr.model.*; import com.structurizr.util.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; @@ -678,6 +680,72 @@ public boolean isEmpty() { return systemLandscapeViews.isEmpty() && systemContextViews.isEmpty() && containerViews.isEmpty() && componentViews.isEmpty() && dynamicViews.isEmpty() && deploymentViews.isEmpty() && filteredViews.isEmpty(); } + public void createDefaultViews() { + // create a single System Landscape diagram containing all people and software systems + SystemLandscapeView systemLandscapeView = createSystemLandscapeView(generateViewKey("SystemLandscape"), ""); + systemLandscapeView.addDefaultElements(); + systemLandscapeView.enableAutomaticLayout(AutomaticLayout.RankDirection.TopBottom, 300, 300); + systemLandscapeView.setEnterpriseBoundaryVisible(true); + + // and a system context view plus container view for each software system + for (SoftwareSystem softwareSystem : model.getSoftwareSystems()) { + SystemContextView systemContextView = createSystemContextView(softwareSystem, generateViewKey("SystemContext"), ""); + systemContextView.addDefaultElements(); + systemContextView.enableAutomaticLayout(AutomaticLayout.RankDirection.TopBottom, 300, 300); + systemContextView.setEnterpriseBoundaryVisible(true); + + if (softwareSystem.getContainers().size() > 0) { + ContainerView containerView = createContainerView(softwareSystem, generateViewKey("Container"), ""); + containerView.addDefaultElements(); + containerView.enableAutomaticLayout(AutomaticLayout.RankDirection.TopBottom, 300, 300); + containerView.setExternalSoftwareSystemBoundariesVisible(true); + + for (Container container : softwareSystem.getContainers()) { + if (container.getComponents().size() > 0) { + ComponentView componentView = createComponentView(container, generateViewKey("Component"), ""); + componentView.addDefaultElements(); + componentView.enableAutomaticLayout(AutomaticLayout.RankDirection.TopBottom, 300, 300); + componentView.setExternalSoftwareSystemBoundariesVisible(true); + } + } + } + } + + // and deployment views for each environment and software system + Set deploymentEnvironments = new HashSet<>(); + for (DeploymentNode deploymentNode : model.getDeploymentNodes()) { + deploymentEnvironments.add(deploymentNode.getEnvironment()); + } + + for (String deploymentEnvironment : deploymentEnvironments) { + Set softwareSystems = new HashSet<>(); + for (DeploymentNode deploymentNode : model.getDeploymentNodes()) { + for (ContainerInstance containerInstance : deploymentNode.getContainerInstances()) { + softwareSystems.add(containerInstance.getContainer().getSoftwareSystem()); + } + } + + if (softwareSystems.isEmpty()) { + DeploymentView deploymentView = createDeploymentView(generateViewKey("Deployment"), ""); + deploymentView.setEnvironment(deploymentEnvironment); + deploymentView.addDefaultElements(); + deploymentView.enableAutomaticLayout(AutomaticLayout.RankDirection.TopBottom, 300, 300); + } else { + for (SoftwareSystem softwareSystem : softwareSystems) { + DeploymentView deploymentView = createDeploymentView(softwareSystem, generateViewKey("Deployment"), ""); + deploymentView.setEnvironment(deploymentEnvironment); + deploymentView.addDefaultElements(); + deploymentView.enableAutomaticLayout(AutomaticLayout.RankDirection.TopBottom, 300, 300); + } + } + } + } + + private String generateViewKey(String type) { + DecimalFormat format = new DecimalFormat("000"); + return format.format(getViews().size() + 1) + "-" + type; + } + /** * Removes all views and configuration. */ From fefab9a117f62f68ab16725282bd0bf6519a8ab6 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 21 Jun 2020 07:56:55 +0100 Subject: [PATCH 143/717] Updated. --- docs/changelog.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 072873ae7..d613764a0 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,11 @@ # Changelog +## 1.4.4 (unreleased) + +- Adds an "addDefaultElements()" method to the static/deployment views. +- Adds an "addDefaultStyles()" method to Styles. +- Adds a "createDefaultViews()" method to Views. + ## 1.4.3 (19th June 2020) - Fixes a bug where all deployment nodes would be added to a deployment view, even if that view had an environment set. From eaacf26df151bd4cf7cbad8e3a439ad42ab63502 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 21 Jun 2020 10:03:28 +0100 Subject: [PATCH 144/717] Improved key generation for default views. --- .../src/com/structurizr/view/ViewSet.java | 86 +++++++++++-------- .../com/structurizr/view/ViewSetTests.java | 62 +++++++++++++ 2 files changed, 114 insertions(+), 34 deletions(-) diff --git a/structurizr-core/src/com/structurizr/view/ViewSet.java b/structurizr-core/src/com/structurizr/view/ViewSet.java index fb8af14dc..8996a79b1 100644 --- a/structurizr-core/src/com/structurizr/view/ViewSet.java +++ b/structurizr-core/src/com/structurizr/view/ViewSet.java @@ -10,10 +10,7 @@ import org.apache.commons.logging.LogFactory; import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; +import java.util.*; import static com.structurizr.util.StringUtils.isNullOrEmpty; @@ -682,57 +679,79 @@ public boolean isEmpty() { public void createDefaultViews() { // create a single System Landscape diagram containing all people and software systems - SystemLandscapeView systemLandscapeView = createSystemLandscapeView(generateViewKey("SystemLandscape"), ""); + SystemLandscapeView systemLandscapeView = createSystemLandscapeView("SystemLandscape", ""); systemLandscapeView.addDefaultElements(); systemLandscapeView.enableAutomaticLayout(AutomaticLayout.RankDirection.TopBottom, 300, 300); systemLandscapeView.setEnterpriseBoundaryVisible(true); - // and a system context view plus container view for each software system - for (SoftwareSystem softwareSystem : model.getSoftwareSystems()) { - SystemContextView systemContextView = createSystemContextView(softwareSystem, generateViewKey("SystemContext"), ""); - systemContextView.addDefaultElements(); - systemContextView.enableAutomaticLayout(AutomaticLayout.RankDirection.TopBottom, 300, 300); - systemContextView.setEnterpriseBoundaryVisible(true); - - if (softwareSystem.getContainers().size() > 0) { - ContainerView containerView = createContainerView(softwareSystem, generateViewKey("Container"), ""); - containerView.addDefaultElements(); - containerView.enableAutomaticLayout(AutomaticLayout.RankDirection.TopBottom, 300, 300); - containerView.setExternalSoftwareSystemBoundariesVisible(true); - - for (Container container : softwareSystem.getContainers()) { - if (container.getComponents().size() > 0) { - ComponentView componentView = createComponentView(container, generateViewKey("Component"), ""); - componentView.addDefaultElements(); - componentView.enableAutomaticLayout(AutomaticLayout.RankDirection.TopBottom, 300, 300); - componentView.setExternalSoftwareSystemBoundariesVisible(true); + if (!model.getSoftwareSystems().isEmpty()) { + List softwareSystems = new ArrayList<>(model.getSoftwareSystems()); + softwareSystems.sort(Comparator.comparing(Element::getName)); + + // and a system context view plus container view for each software system + for (SoftwareSystem softwareSystem : softwareSystems) { + String systemContextViewKey = removeNonWordCharacters(softwareSystem.getName()) + "-SystemContext"; + SystemContextView systemContextView = createSystemContextView(softwareSystem, systemContextViewKey, ""); + systemContextView.addDefaultElements(); + systemContextView.enableAutomaticLayout(AutomaticLayout.RankDirection.TopBottom, 300, 300); + systemContextView.setEnterpriseBoundaryVisible(true); + + if (softwareSystem.getContainers().size() > 0) { + List containers = new ArrayList<>(softwareSystem.getContainers()); + containers.sort(Comparator.comparing(Element::getName)); + + String containerViewKey = removeNonWordCharacters(softwareSystem.getName()) + "-Container"; + ContainerView containerView = createContainerView(softwareSystem, containerViewKey, ""); + containerView.addDefaultElements(); + containerView.enableAutomaticLayout(AutomaticLayout.RankDirection.TopBottom, 300, 300); + containerView.setExternalSoftwareSystemBoundariesVisible(true); + + for (Container container : containers) { + if (container.getComponents().size() > 0) { + String componentViewKey = removeNonWordCharacters(softwareSystem.getName()) + "-" + removeNonWordCharacters(container.getName()) + "-Component"; + ComponentView componentView = createComponentView(container, componentViewKey, ""); + componentView.addDefaultElements(); + componentView.enableAutomaticLayout(AutomaticLayout.RankDirection.TopBottom, 300, 300); + componentView.setExternalSoftwareSystemBoundariesVisible(true); + } } } } } - // and deployment views for each environment and software system - Set deploymentEnvironments = new HashSet<>(); + // and deployment views for each environment and software system pair + List deploymentEnvironments = new ArrayList<>(); for (DeploymentNode deploymentNode : model.getDeploymentNodes()) { - deploymentEnvironments.add(deploymentNode.getEnvironment()); + String environment = deploymentNode.getEnvironment(); + if (!deploymentEnvironments.contains(environment)) { + deploymentEnvironments.add(environment); + } } + deploymentEnvironments.sort(String::compareTo); for (String deploymentEnvironment : deploymentEnvironments) { - Set softwareSystems = new HashSet<>(); + List softwareSystems = new ArrayList<>(); for (DeploymentNode deploymentNode : model.getDeploymentNodes()) { for (ContainerInstance containerInstance : deploymentNode.getContainerInstances()) { - softwareSystems.add(containerInstance.getContainer().getSoftwareSystem()); + SoftwareSystem softwareSystem = containerInstance.getContainer().getSoftwareSystem(); + if (!softwareSystems.contains(softwareSystem)) { + softwareSystems.add(softwareSystem); + } } } if (softwareSystems.isEmpty()) { - DeploymentView deploymentView = createDeploymentView(generateViewKey("Deployment"), ""); + String deploymentViewKey = removeNonWordCharacters(deploymentEnvironment) + "-Deployment"; + DeploymentView deploymentView = createDeploymentView(deploymentViewKey, ""); deploymentView.setEnvironment(deploymentEnvironment); deploymentView.addDefaultElements(); deploymentView.enableAutomaticLayout(AutomaticLayout.RankDirection.TopBottom, 300, 300); } else { + softwareSystems.sort(Comparator.comparing(Element::getName)); + for (SoftwareSystem softwareSystem : softwareSystems) { - DeploymentView deploymentView = createDeploymentView(softwareSystem, generateViewKey("Deployment"), ""); + String deploymentViewKey = removeNonWordCharacters(softwareSystem.getName()) + "-" + removeNonWordCharacters(deploymentEnvironment) + "-Deployment"; + DeploymentView deploymentView = createDeploymentView(softwareSystem, deploymentViewKey, ""); deploymentView.setEnvironment(deploymentEnvironment); deploymentView.addDefaultElements(); deploymentView.enableAutomaticLayout(AutomaticLayout.RankDirection.TopBottom, 300, 300); @@ -741,9 +760,8 @@ public void createDefaultViews() { } } - private String generateViewKey(String type) { - DecimalFormat format = new DecimalFormat("000"); - return format.format(getViews().size() + 1) + "-" + type; + private String removeNonWordCharacters(String name) { + return name.replaceAll("\\W", ""); } /** diff --git a/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java b/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java index 28f5af9b1..c4fdba8a9 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java @@ -962,4 +962,66 @@ public void test_setEnterpriseContextViews_IsSupportedForOlderWorkspaces() { assertSame(systemLandscapeView, views.getSystemLandscapeViews().iterator().next()); } + @Test + public void test_createDefaultViews() { + Workspace workspace = new Workspace("Name", "Description"); + Model model = workspace.getModel(); + ViewSet views = workspace.getViews(); + + SoftwareSystem ss1 = model.addSoftwareSystem("Software System 1"); + Container c1 = ss1.addContainer("Container 1", "", ""); + Component cc1 = c1.addComponent("Component 1", "", ""); + + SoftwareSystem ss2 = model.addSoftwareSystem("Software System 2"); + Container c2 = ss2.addContainer("Container 2", "", ""); + Component cc2 = c2.addComponent("Component 2", "", ""); + + DeploymentNode dev = model.addDeploymentNode("Development", "Deployment Node", "", ""); + DeploymentNode live = model.addDeploymentNode("Live", "Deployment Node", "", ""); + + views.createDefaultViews(); + + assertEquals(1, views.getSystemLandscapeViews().size()); + assertEquals("SystemLandscape", views.getSystemLandscapeViews().iterator().next().getKey()); + + assertEquals(2, views.getSystemContextViews().size()); + assertSame(ss1, views.getSystemContextViews().stream().filter(v -> v.getKey().equals("SoftwareSystem1-SystemContext")).findFirst().get().getSoftwareSystem()); + assertSame(ss2, views.getSystemContextViews().stream().filter(v -> v.getKey().equals("SoftwareSystem2-SystemContext")).findFirst().get().getSoftwareSystem()); + + assertEquals(2, views.getContainerViews().size()); + assertSame(ss1, views.getContainerViews().stream().filter(v -> v.getKey().equals("SoftwareSystem1-Container")).findFirst().get().getSoftwareSystem()); + assertSame(ss2, views.getContainerViews().stream().filter(v -> v.getKey().equals("SoftwareSystem2-Container")).findFirst().get().getSoftwareSystem()); + + assertEquals(2, views.getComponentViews().size()); + assertSame(c1, views.getComponentViews().stream().filter(v -> v.getKey().equals("SoftwareSystem1-Container1-Component")).findFirst().get().getContainer()); + assertSame(c2, views.getComponentViews().stream().filter(v -> v.getKey().equals("SoftwareSystem2-Container2-Component")).findFirst().get().getContainer()); + + assertEquals(0, views.getDynamicViews().size()); + + assertEquals(2, views.getDeploymentViews().size()); + assertNull(views.getDeploymentViews().stream().filter(v -> v.getKey().equals("Development-Deployment")).findFirst().get().getSoftwareSystem()); + assertSame("Development", views.getDeploymentViews().stream().filter(v -> v.getKey().equals("Development-Deployment")).findFirst().get().getEnvironment()); + assertNull(views.getDeploymentViews().stream().filter(v -> v.getKey().equals("Live-Deployment")).findFirst().get().getSoftwareSystem()); + assertSame("Live", views.getDeploymentViews().stream().filter(v -> v.getKey().equals("Live-Deployment")).findFirst().get().getEnvironment()); + + views.clear(); + + dev.add(c1); + dev.add(c2); + live.add(c1); + live.add(c2); + + views.createDefaultViews(); + + assertEquals(4, views.getDeploymentViews().size()); + assertSame(ss1, views.getDeploymentViews().stream().filter(v -> v.getKey().equals("SoftwareSystem1-Development-Deployment")).findFirst().get().getSoftwareSystem()); + assertSame("Development", views.getDeploymentViews().stream().filter(v -> v.getKey().equals("SoftwareSystem1-Development-Deployment")).findFirst().get().getEnvironment()); + assertSame(ss2, views.getDeploymentViews().stream().filter(v -> v.getKey().equals("SoftwareSystem2-Development-Deployment")).findFirst().get().getSoftwareSystem()); + assertSame("Development", views.getDeploymentViews().stream().filter(v -> v.getKey().equals("SoftwareSystem2-Development-Deployment")).findFirst().get().getEnvironment()); + assertSame(ss1, views.getDeploymentViews().stream().filter(v -> v.getKey().equals("SoftwareSystem1-Live-Deployment")).findFirst().get().getSoftwareSystem()); + assertSame("Live", views.getDeploymentViews().stream().filter(v -> v.getKey().equals("SoftwareSystem1-Live-Deployment")).findFirst().get().getEnvironment()); + assertSame(ss2, views.getDeploymentViews().stream().filter(v -> v.getKey().equals("SoftwareSystem2-Live-Deployment")).findFirst().get().getSoftwareSystem()); + assertSame("Live", views.getDeploymentViews().stream().filter(v -> v.getKey().equals("SoftwareSystem2-Live-Deployment")).findFirst().get().getEnvironment()); + } + } \ No newline at end of file From 327c462bae20112ea8705a2ea9339c66b5f20a07 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 21 Jun 2020 10:25:39 +0100 Subject: [PATCH 145/717] Don't create empty deployment views. --- structurizr-core/src/com/structurizr/view/ViewSet.java | 10 ++++++---- .../test/unit/com/structurizr/view/ViewSetTests.java | 5 +---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/structurizr-core/src/com/structurizr/view/ViewSet.java b/structurizr-core/src/com/structurizr/view/ViewSet.java index 8996a79b1..d067a55fa 100644 --- a/structurizr-core/src/com/structurizr/view/ViewSet.java +++ b/structurizr-core/src/com/structurizr/view/ViewSet.java @@ -732,10 +732,12 @@ public void createDefaultViews() { for (String deploymentEnvironment : deploymentEnvironments) { List softwareSystems = new ArrayList<>(); for (DeploymentNode deploymentNode : model.getDeploymentNodes()) { - for (ContainerInstance containerInstance : deploymentNode.getContainerInstances()) { - SoftwareSystem softwareSystem = containerInstance.getContainer().getSoftwareSystem(); - if (!softwareSystems.contains(softwareSystem)) { - softwareSystems.add(softwareSystem); + if (deploymentNode.getEnvironment().equals(deploymentEnvironment)) { + for (ContainerInstance containerInstance : deploymentNode.getContainerInstances()) { + SoftwareSystem softwareSystem = containerInstance.getContainer().getSoftwareSystem(); + if (!softwareSystems.contains(softwareSystem)) { + softwareSystems.add(softwareSystem); + } } } } diff --git a/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java b/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java index c4fdba8a9..93939fdb3 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java @@ -1007,17 +1007,14 @@ public void test_createDefaultViews() { views.clear(); dev.add(c1); - dev.add(c2); live.add(c1); live.add(c2); views.createDefaultViews(); - assertEquals(4, views.getDeploymentViews().size()); + assertEquals(3, views.getDeploymentViews().size()); assertSame(ss1, views.getDeploymentViews().stream().filter(v -> v.getKey().equals("SoftwareSystem1-Development-Deployment")).findFirst().get().getSoftwareSystem()); assertSame("Development", views.getDeploymentViews().stream().filter(v -> v.getKey().equals("SoftwareSystem1-Development-Deployment")).findFirst().get().getEnvironment()); - assertSame(ss2, views.getDeploymentViews().stream().filter(v -> v.getKey().equals("SoftwareSystem2-Development-Deployment")).findFirst().get().getSoftwareSystem()); - assertSame("Development", views.getDeploymentViews().stream().filter(v -> v.getKey().equals("SoftwareSystem2-Development-Deployment")).findFirst().get().getEnvironment()); assertSame(ss1, views.getDeploymentViews().stream().filter(v -> v.getKey().equals("SoftwareSystem1-Live-Deployment")).findFirst().get().getSoftwareSystem()); assertSame("Live", views.getDeploymentViews().stream().filter(v -> v.getKey().equals("SoftwareSystem1-Live-Deployment")).findFirst().get().getEnvironment()); assertSame(ss2, views.getDeploymentViews().stream().filter(v -> v.getKey().equals("SoftwareSystem2-Live-Deployment")).findFirst().get().getSoftwareSystem()); From e5c38298ee9aee5bff208006b50111194a6d72d3 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 21 Jun 2020 10:36:51 +0100 Subject: [PATCH 146/717] Updated to reflect release. --- build.gradle | 2 +- docs/binaries.md | 4 ++-- docs/changelog.md | 2 +- docs/getting-started.md | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index 6ea31e4dc..5dba23877 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.4.3' + version = '1.4.4' repositories { mavenCentral() diff --git a/docs/binaries.md b/docs/binaries.md index d8750639c..9f57d1156 100644 --- a/docs/binaries.md +++ b/docs/binaries.md @@ -3,5 +3,5 @@ The "Structurizr for Java" binaries are hosted on [Maven Central](https://repo1. Name | Description ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-core:1.4.3 | The core library that can used to create software architecture models. -com.structurizr:structurizr-client:1.4.3 | The API client for publishing models on the Structurizr cloud service and on-premises installation. \ No newline at end of file +com.structurizr:structurizr-core:1.4.4 | The core library that can used to create software architecture models. +com.structurizr:structurizr-client:1.4.4 | The API client for publishing models on the Structurizr cloud service and on-premises installation. \ No newline at end of file diff --git a/docs/changelog.md b/docs/changelog.md index d613764a0..71a50ed6c 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 1.4.4 (unreleased) +## 1.4.4 (21st June 2020) - Adds an "addDefaultElements()" method to the static/deployment views. - Adds an "addDefaultStyles()" method to Styles. diff --git a/docs/getting-started.md b/docs/getting-started.md index f7a334734..6ab468bbf 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -12,7 +12,7 @@ The Structurizr for Java binaries are hosted on [Maven Central](https://repo1.ma Name | Description ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-client:1.4.3 | The Structurizr API client library. +com.structurizr:structurizr-client:1.4.4 | The Structurizr API client library. ## 2. Create a Java program From f371263233fd11f6568819373826609045cccdb2 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 21 Jun 2020 11:41:47 +0100 Subject: [PATCH 147/717] Adds support for automatically creating deployment diagrams that only include infrastructure nodes. --- .../src/com/structurizr/view/ViewSet.java | 27 ++++++++++++++----- .../com/structurizr/view/ViewSetTests.java | 21 ++++++++------- 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/structurizr-core/src/com/structurizr/view/ViewSet.java b/structurizr-core/src/com/structurizr/view/ViewSet.java index d067a55fa..be337c7e2 100644 --- a/structurizr-core/src/com/structurizr/view/ViewSet.java +++ b/structurizr-core/src/com/structurizr/view/ViewSet.java @@ -733,7 +733,8 @@ public void createDefaultViews() { List softwareSystems = new ArrayList<>(); for (DeploymentNode deploymentNode : model.getDeploymentNodes()) { if (deploymentNode.getEnvironment().equals(deploymentEnvironment)) { - for (ContainerInstance containerInstance : deploymentNode.getContainerInstances()) { + Set containerInstances = getContainerInstances(deploymentNode); + for (ContainerInstance containerInstance : containerInstances) { SoftwareSystem softwareSystem = containerInstance.getContainer().getSoftwareSystem(); if (!softwareSystems.contains(softwareSystem)) { softwareSystems.add(softwareSystem); @@ -743,11 +744,14 @@ public void createDefaultViews() { } if (softwareSystems.isEmpty()) { - String deploymentViewKey = removeNonWordCharacters(deploymentEnvironment) + "-Deployment"; - DeploymentView deploymentView = createDeploymentView(deploymentViewKey, ""); - deploymentView.setEnvironment(deploymentEnvironment); - deploymentView.addDefaultElements(); - deploymentView.enableAutomaticLayout(AutomaticLayout.RankDirection.TopBottom, 300, 300); + // there are no container instances, but perhaps there are infrastructure nodes in this environment + if (model.getElements().stream().anyMatch(e -> e instanceof InfrastructureNode && ((InfrastructureNode)e).getEnvironment().equals(deploymentEnvironment))) { + String deploymentViewKey = removeNonWordCharacters(deploymentEnvironment) + "-Deployment"; + DeploymentView deploymentView = createDeploymentView(deploymentViewKey, ""); + deploymentView.setEnvironment(deploymentEnvironment); + deploymentView.addDefaultElements(); + deploymentView.enableAutomaticLayout(AutomaticLayout.RankDirection.TopBottom, 300, 300); + } } else { softwareSystems.sort(Comparator.comparing(Element::getName)); @@ -766,6 +770,17 @@ private String removeNonWordCharacters(String name) { return name.replaceAll("\\W", ""); } + private Set getContainerInstances(DeploymentNode deploymentNode) { + Set containerInstances = new HashSet<>(deploymentNode.getContainerInstances()); + + for (DeploymentNode child : deploymentNode.getChildren()) { + containerInstances.addAll(getContainerInstances(child)); + } + + return containerInstances; + } + + /** * Removes all views and configuration. */ diff --git a/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java b/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java index 93939fdb3..b62e5a198 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java @@ -976,8 +976,9 @@ public void test_createDefaultViews() { Container c2 = ss2.addContainer("Container 2", "", ""); Component cc2 = c2.addComponent("Component 2", "", ""); - DeploymentNode dev = model.addDeploymentNode("Development", "Deployment Node", "", ""); - DeploymentNode live = model.addDeploymentNode("Live", "Deployment Node", "", ""); + DeploymentNode dev = model.addDeploymentNode("Development", "Developer Laptop", "", ""); + DeploymentNode live = model.addDeploymentNode("Live", "Amazon Web Services", "", ""); + DeploymentNode liveEc2 = live.addDeploymentNode("EC2"); views.createDefaultViews(); @@ -997,19 +998,21 @@ public void test_createDefaultViews() { assertSame(c2, views.getComponentViews().stream().filter(v -> v.getKey().equals("SoftwareSystem2-Container2-Component")).findFirst().get().getContainer()); assertEquals(0, views.getDynamicViews().size()); + assertEquals(0, views.getDeploymentViews().size()); - assertEquals(2, views.getDeploymentViews().size()); - assertNull(views.getDeploymentViews().stream().filter(v -> v.getKey().equals("Development-Deployment")).findFirst().get().getSoftwareSystem()); - assertSame("Development", views.getDeploymentViews().stream().filter(v -> v.getKey().equals("Development-Deployment")).findFirst().get().getEnvironment()); - assertNull(views.getDeploymentViews().stream().filter(v -> v.getKey().equals("Live-Deployment")).findFirst().get().getSoftwareSystem()); - assertSame("Live", views.getDeploymentViews().stream().filter(v -> v.getKey().equals("Live-Deployment")).findFirst().get().getEnvironment()); + live.addInfrastructureNode("Route 53"); views.clear(); + views.createDefaultViews(); + + assertEquals(1, views.getDeploymentViews().size()); + assertSame("Live", views.getDeploymentViews().stream().filter(v -> v.getKey().equals("Live-Deployment")).findFirst().get().getEnvironment()); dev.add(c1); - live.add(c1); - live.add(c2); + liveEc2.add(c1); + liveEc2.add(c2); + views.clear(); views.createDefaultViews(); assertEquals(3, views.getDeploymentViews().size()); From 3d65158a64e8f18a4b8e7f3aa96a2bc094ca6f4c Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 21 Jun 2020 17:05:51 +0100 Subject: [PATCH 148/717] Make the default background white for infrastructure nodes. --- structurizr-core/src/com/structurizr/view/Styles.java | 1 + 1 file changed, 1 insertion(+) diff --git a/structurizr-core/src/com/structurizr/view/Styles.java b/structurizr-core/src/com/structurizr/view/Styles.java index 3f50f8476..265b5533a 100644 --- a/structurizr-core/src/com/structurizr/view/Styles.java +++ b/structurizr-core/src/com/structurizr/view/Styles.java @@ -65,6 +65,7 @@ public void addDefaultStyles() { addElementStyle(Tags.CONTAINER).background("#438dd5").color("#ffffff"); addElementStyle(Tags.COMPONENT).background("#85bbf0").color("#000000"); addElementStyle(Tags.PERSON).background("#08427b").color("#ffffff").shape(Shape.Person); + addElementStyle(Tags.INFRASTRUCTURE_NODE).background("#ffffff"); } public Collection getRelationships() { From fa5fc072557637e2a951f6086f815094ebd53ed2 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 21 Jun 2020 17:06:17 +0100 Subject: [PATCH 149/717] Updated to reflect release. --- build.gradle | 2 +- docs/binaries.md | 4 ++-- docs/changelog.md | 4 ++++ docs/getting-started.md | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 5dba23877..c13b5d8e4 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.4.4' + version = '1.4.5' repositories { mavenCentral() diff --git a/docs/binaries.md b/docs/binaries.md index 9f57d1156..35facee52 100644 --- a/docs/binaries.md +++ b/docs/binaries.md @@ -3,5 +3,5 @@ The "Structurizr for Java" binaries are hosted on [Maven Central](https://repo1. Name | Description ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-core:1.4.4 | The core library that can used to create software architecture models. -com.structurizr:structurizr-client:1.4.4 | The API client for publishing models on the Structurizr cloud service and on-premises installation. \ No newline at end of file +com.structurizr:structurizr-core:1.4.5 | The core library that can used to create software architecture models. +com.structurizr:structurizr-client:1.4.5 | The API client for publishing models on the Structurizr cloud service and on-premises installation. \ No newline at end of file diff --git a/docs/changelog.md b/docs/changelog.md index 71a50ed6c..ac1288b28 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.4.5 (21st June 2020) + +- Bug fixes. + ## 1.4.4 (21st June 2020) - Adds an "addDefaultElements()" method to the static/deployment views. diff --git a/docs/getting-started.md b/docs/getting-started.md index 6ab468bbf..bc78cf1a5 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -12,7 +12,7 @@ The Structurizr for Java binaries are hosted on [Maven Central](https://repo1.ma Name | Description ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-client:1.4.4 | The Structurizr API client library. +com.structurizr:structurizr-client:1.4.5 | The Structurizr API client library. ## 2. Create a Java program From 3fb4f4b5e20491e76446a87c3f7f810d228783aa Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 6 Jul 2020 07:46:19 +0100 Subject: [PATCH 150/717] Adds a way to load styles from external themes. --- build.gradle | 2 +- docs/changelog.md | 4 + .../src/com/structurizr/util/ThemeUtils.java | 78 ++++++++++++++++--- .../com/structurizr/util/ThemeUtilsTests.java | 21 +++++ .../src/com/structurizr/view/Styles.java | 14 +++- .../unit/com/structurizr/view/StyleTests.java | 28 ++++++- 6 files changed, 132 insertions(+), 15 deletions(-) create mode 100644 structurizr-client/test/unit/com/structurizr/util/ThemeUtilsTests.java diff --git a/build.gradle b/build.gradle index c13b5d8e4..1867efffd 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.4.5' + version = '1.4.6' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index ac1288b28..5c1be9072 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.4.6 (unreleased) + +- Adds a way to load styles from external themes. + ## 1.4.5 (21st June 2020) - Bug fixes. diff --git a/structurizr-client/src/com/structurizr/util/ThemeUtils.java b/structurizr-client/src/com/structurizr/util/ThemeUtils.java index 3145154e4..ded80b7d0 100644 --- a/structurizr-client/src/com/structurizr/util/ThemeUtils.java +++ b/structurizr-client/src/com/structurizr/util/ThemeUtils.java @@ -1,13 +1,18 @@ package com.structurizr.util; -import com.fasterxml.jackson.annotation.JsonGetter; import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.structurizr.Workspace; import com.structurizr.io.WorkspaceWriterException; import com.structurizr.view.ElementStyle; import com.structurizr.view.RelationshipStyle; +import org.apache.hc.client5.http.classic.methods.HttpGet; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.core5.http.io.entity.EntityUtils; import java.io.*; import java.nio.charset.StandardCharsets; @@ -19,6 +24,8 @@ */ public final class ThemeUtils { + private static final int HTTP_OK_STATUS = 200; + /** * Serializes the theme (element and relationship styles) in the specified workspace to a file, as a JSON string. * @@ -55,6 +62,43 @@ public static String toJson(Workspace workspace) throws Exception { return writer.toString(); } + /** + * Loads (and inlines) the element and relationship styles from the themes defined in the workspace, into the workspace itself. + * This implementation simply copies the styles from all themes into the workspace. + * + * @param workspace a Workspace object + * @throws Exception if something goes wrong + */ + public static void loadStylesFromThemes(Workspace workspace) throws Exception { + if (workspace.getViews().getConfiguration().getThemes() != null) { + for (String url : workspace.getViews().getConfiguration().getThemes()) { + CloseableHttpClient httpClient = HttpClients.createSystem(); + HttpGet httpGet = new HttpGet(url); + + CloseableHttpResponse response = httpClient.execute(httpGet); + if (response.getCode() == HTTP_OK_STATUS) { + String json = EntityUtils.toString(response.getEntity()); + + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + + Theme theme = objectMapper.readValue(json, Theme.class); + + for (ElementStyle elementStyle : theme.getElements()) { + workspace.getViews().getConfiguration().getStyles().add(elementStyle); + } + + for (RelationshipStyle relationshipStyle : theme.getRelationships()) { + workspace.getViews().getConfiguration().getStyles().add(relationshipStyle); + } + } + + httpClient.close(); + } + } + } + private static void write(Workspace workspace, Writer writer) throws Exception { try { ObjectMapper objectMapper = new ObjectMapper(); @@ -84,6 +128,9 @@ static class Theme { private Collection elements = new LinkedList<>(); private Collection relationships = new LinkedList<>(); + Theme() { + } + Theme(String name, String description, Collection elements, Collection relationships) { this.name = name; this.description = description; @@ -91,25 +138,38 @@ static class Theme { this.relationships = relationships; } - @JsonGetter - public String getName() { + String getName() { return name; } - @JsonGetter - public String getDescription() { + void setName(String name) { + this.name = name; + } + + String getDescription() { return description; } - @JsonGetter - public Collection getElements() { + void setDescription(String description) { + this.description = description; + } + + Collection getElements() { return elements; } - @JsonGetter - public Collection getRelationships() { + void setElements(Collection elements) { + this.elements = elements; + } + + Collection getRelationships() { return relationships; } + + void setRelationships(Collection relationships) { + this.relationships = relationships; + } + } } \ No newline at end of file diff --git a/structurizr-client/test/unit/com/structurizr/util/ThemeUtilsTests.java b/structurizr-client/test/unit/com/structurizr/util/ThemeUtilsTests.java new file mode 100644 index 000000000..8021a5b54 --- /dev/null +++ b/structurizr-client/test/unit/com/structurizr/util/ThemeUtilsTests.java @@ -0,0 +1,21 @@ +package com.structurizr.util; + +import com.structurizr.Workspace; +import org.junit.Test; + +import static junit.framework.TestCase.assertTrue; + +public class ThemeUtilsTests { + + @Test + public void test_loadStylesFromThemes() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getViews().getConfiguration().setTheme("https://raw.githubusercontent.com/structurizr/themes/master/amazon-web-services/theme.json"); + + ThemeUtils.loadStylesFromThemes(workspace); + + assertTrue(workspace.getViews().getConfiguration().getStyles().getElements().size() > 0); + assertTrue(workspace.getViews().getConfiguration().getStyles().getElements().stream().anyMatch(s -> s.getTag().equals("Amazon Web Services - Cloud"))); + } + +} diff --git a/structurizr-core/src/com/structurizr/view/Styles.java b/structurizr-core/src/com/structurizr/view/Styles.java index 265b5533a..65972a739 100644 --- a/structurizr-core/src/com/structurizr/view/Styles.java +++ b/structurizr-core/src/com/structurizr/view/Styles.java @@ -25,7 +25,11 @@ public Collection getElements() { public void add(ElementStyle elementStyle) { if (elementStyle != null) { - this.elements.add(elementStyle); + if (findElementStyle(elementStyle.getTag()) == null) { + this.elements.add(elementStyle); + } else { + throw new IllegalArgumentException("An element style for the tag \"" + elementStyle.getTag() + "\" already exists."); + } } } @@ -74,7 +78,11 @@ public Collection getRelationships() { public void add(RelationshipStyle relationshipStyle) { if (relationshipStyle != null) { - this.relationships.add(relationshipStyle); + if (findRelationshipStyle(relationshipStyle.getTag()) == null) { + this.relationships.add(relationshipStyle); + } else { + throw new IllegalArgumentException("A relationship style for the tag \"" + relationshipStyle.getTag() + "\" already exists."); + } } } @@ -119,7 +127,7 @@ private RelationshipStyle findRelationshipStyle(String tag) { } public ElementStyle findElementStyle(Element element) { - ElementStyle style = new ElementStyle("").background("#dddddd").color("#000000").shape(Shape.Box); + ElementStyle style = new ElementStyle("").background("#dddddd").stroke("#bbbbbb").color("#000000").shape(Shape.Box); if (element != null) { for (String tag : element.getTagsAsSet()) { diff --git a/structurizr-core/test/unit/com/structurizr/view/StyleTests.java b/structurizr-core/test/unit/com/structurizr/view/StyleTests.java index e5ef46cfd..1a4df4416 100644 --- a/structurizr-core/test/unit/com/structurizr/view/StyleTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/StyleTests.java @@ -107,7 +107,7 @@ public void test_findRelationshipStyle_ReturnsTheCorrectStyle_WhenStylesAreDefin } @Test - public void test_addElementStyle_ThrowsAnException_WhenAStyleWithTheSameTagExistsAlready() { + public void test_addElementStyleByTag_ThrowsAnException_WhenAStyleWithTheSameTagExistsAlready() { try { styles.addElementStyle(Tags.SOFTWARE_SYSTEM).color("#ff0000"); styles.addElementStyle(Tags.SOFTWARE_SYSTEM).color("#ff0000"); @@ -119,7 +119,19 @@ public void test_addElementStyle_ThrowsAnException_WhenAStyleWithTheSameTagExist } @Test - public void test_addRelationshipStyle_ThrowsAnException_WhenAStyleWithTheSameTagExistsAlready() { + public void test_addElementStyle_ThrowsAnException_WhenAStyleWithTheSameTagExistsAlready() { + try { + ElementStyle style = styles.addElementStyle(Tags.SOFTWARE_SYSTEM).color("#ff0000"); + styles.add(style); + + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("An element style for the tag \"Software System\" already exists.", iae.getMessage()); + } + } + + @Test + public void test_addRelationshipStyleByTag_ThrowsAnException_WhenAStyleWithTheSameTagExistsAlready() { try { styles.addRelationshipStyle(Tags.RELATIONSHIP).color("#ff0000"); styles.addRelationshipStyle(Tags.RELATIONSHIP).color("#ff0000"); @@ -130,6 +142,18 @@ public void test_addRelationshipStyle_ThrowsAnException_WhenAStyleWithTheSameTag } } + @Test + public void test_addRelationshipStyle_ThrowsAnException_WhenAStyleWithTheSameTagExistsAlready() { + try { + RelationshipStyle style = styles.addRelationshipStyle(Tags.RELATIONSHIP).color("#ff0000"); + styles.add(style); + + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("A relationship style for the tag \"Relationship\" already exists.", iae.getMessage()); + } + } + @Test public void test_clearElementStyles_RemovesAllElementStyles() { styles.addElementStyle(Tags.SOFTWARE_SYSTEM).color("#ff0000"); From 02bce0f8c8821a2ab1945e85acd2950a825b69f2 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 6 Jul 2020 08:25:35 +0100 Subject: [PATCH 151/717] Updated to reflect release. --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 5c1be9072..852a4c992 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 1.4.6 (unreleased) +## 1.4.6 (6th July 2020) - Adds a way to load styles from external themes. From 5ed1a124e9d246cce5cdcbf71a8b2fffdf0111b8 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 6 Jul 2020 08:41:56 +0100 Subject: [PATCH 152/717] Remove default stroke styling. --- structurizr-core/src/com/structurizr/view/Styles.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structurizr-core/src/com/structurizr/view/Styles.java b/structurizr-core/src/com/structurizr/view/Styles.java index 65972a739..d09707da3 100644 --- a/structurizr-core/src/com/structurizr/view/Styles.java +++ b/structurizr-core/src/com/structurizr/view/Styles.java @@ -127,7 +127,7 @@ private RelationshipStyle findRelationshipStyle(String tag) { } public ElementStyle findElementStyle(Element element) { - ElementStyle style = new ElementStyle("").background("#dddddd").stroke("#bbbbbb").color("#000000").shape(Shape.Box); + ElementStyle style = new ElementStyle("").background("#dddddd").color("#000000").shape(Shape.Box); if (element != null) { for (String tag : element.getTagsAsSet()) { From 246c2767348df3199321ac7f835baa7f84f9a30c Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 6 Jul 2020 08:42:50 +0100 Subject: [PATCH 153/717] Updated to reflect release. --- build.gradle | 2 +- docs/changelog.md | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 1867efffd..0826f1602 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.4.6' + version = '1.4.7' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 852a4c992..ef2541c49 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,8 +1,9 @@ # Changelog -## 1.4.6 (6th July 2020) +## 1.4.7 (6th July 2020) - Adds a way to load styles from external themes. +- Remove default stroke styling. ## 1.4.5 (21st June 2020) From 28dfe33bcd2aef58d6b0e4013ef94134968d1982 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 6 Jul 2020 08:43:28 +0100 Subject: [PATCH 154/717] no message --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index ef2541c49..cc3a6de84 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -3,6 +3,9 @@ ## 1.4.7 (6th July 2020) - Adds a way to load styles from external themes. + +## 1.4.6 (6th July 2020) + - Remove default stroke styling. ## 1.4.5 (21st June 2020) From 4463307a4f5995578353395319a84122c892780b Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 6 Jul 2020 08:43:57 +0100 Subject: [PATCH 155/717] Fix changelog. --- docs/changelog.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index cc3a6de84..27b86f407 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -2,11 +2,11 @@ ## 1.4.7 (6th July 2020) -- Adds a way to load styles from external themes. +- Remove default stroke styling. ## 1.4.6 (6th July 2020) -- Remove default stroke styling. +- Adds a way to load styles from external themes. ## 1.4.5 (21st June 2020) From eadc3f8d4210f62c7338a3dd15941f323f4dfcf7 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 7 Jul 2020 21:36:16 +0100 Subject: [PATCH 156/717] Fixes a bug where workspace styles would sometimes not serialize. --- structurizr-client/src/com/structurizr/util/ThemeUtils.java | 1 + 1 file changed, 1 insertion(+) diff --git a/structurizr-client/src/com/structurizr/util/ThemeUtils.java b/structurizr-client/src/com/structurizr/util/ThemeUtils.java index ded80b7d0..46c8e8a79 100644 --- a/structurizr-client/src/com/structurizr/util/ThemeUtils.java +++ b/structurizr-client/src/com/structurizr/util/ThemeUtils.java @@ -105,6 +105,7 @@ private static void write(Workspace workspace, Writer writer) throws Exception { objectMapper.enable(SerializationFeature.INDENT_OUTPUT); objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); + objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); writer.write(objectMapper.writeValueAsString( new Theme( From d878f2426b0b3522f48dbf5067dd3ff541af13a5 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 9 Jul 2020 10:48:02 +0100 Subject: [PATCH 157/717] Fixes a serialisation problem with themes. --- structurizr-client/src/com/structurizr/util/ThemeUtils.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/structurizr-client/src/com/structurizr/util/ThemeUtils.java b/structurizr-client/src/com/structurizr/util/ThemeUtils.java index 46c8e8a79..17b5137a6 100644 --- a/structurizr-client/src/com/structurizr/util/ThemeUtils.java +++ b/structurizr-client/src/com/structurizr/util/ThemeUtils.java @@ -1,5 +1,6 @@ package com.structurizr.util; +import com.fasterxml.jackson.annotation.JsonGetter; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; @@ -155,6 +156,7 @@ void setDescription(String description) { this.description = description; } + @JsonGetter Collection getElements() { return elements; } @@ -163,6 +165,7 @@ void setElements(Collection elements) { this.elements = elements; } + @JsonGetter Collection getRelationships() { return relationships; } From 750f56ff1011b1b38e258b853d97d93bae377fbf Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 9 Jul 2020 10:52:07 +0100 Subject: [PATCH 158/717] Added a test for the theme serialisation problem. --- .../com/structurizr/util/ThemeUtilsTests.java | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/structurizr-client/test/unit/com/structurizr/util/ThemeUtilsTests.java b/structurizr-client/test/unit/com/structurizr/util/ThemeUtilsTests.java index 8021a5b54..5eb77f3cf 100644 --- a/structurizr-client/test/unit/com/structurizr/util/ThemeUtilsTests.java +++ b/structurizr-client/test/unit/com/structurizr/util/ThemeUtilsTests.java @@ -1,9 +1,11 @@ package com.structurizr.util; import com.structurizr.Workspace; +import com.structurizr.model.Tags; import org.junit.Test; import static junit.framework.TestCase.assertTrue; +import static org.junit.Assert.assertEquals; public class ThemeUtilsTests { @@ -18,4 +20,23 @@ public void test_loadStylesFromThemes() throws Exception { assertTrue(workspace.getViews().getConfiguration().getStyles().getElements().stream().anyMatch(s -> s.getTag().equals("Amazon Web Services - Cloud"))); } -} + @Test + public void test_toJson() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + assertEquals("{ }", ThemeUtils.toJson(workspace)); + + workspace.getViews().getConfiguration().getStyles().addElementStyle(Tags.ELEMENT).background("#ff0000"); + workspace.getViews().getConfiguration().getStyles().addRelationshipStyle(Tags.RELATIONSHIP).color("#ff0000"); + assertEquals("{\n" + + " \"elements\" : [ {\n" + + " \"tag\" : \"Element\",\n" + + " \"background\" : \"#ff0000\"\n" + + " } ],\n" + + " \"relationships\" : [ {\n" + + " \"tag\" : \"Relationship\",\n" + + " \"color\" : \"#ff0000\"\n" + + " } ]\n" + + "}", ThemeUtils.toJson(workspace)); + } + +} \ No newline at end of file From 54572774b5cd3bb1729091b1ce84f98362d505c0 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 9 Jul 2020 11:04:50 +0100 Subject: [PATCH 159/717] Implied relationships now also copy the interaction style and relationships. --- ...psUnlessAnyRelationshipExistsStrategy.java | 2 +- ...sUnlessSameRelationshipExistsStrategy.java | 2 +- .../com/structurizr/model/DeploymentNode.java | 16 +++++++++++++- .../structurizr/model/InfrastructureNode.java | 16 +++++++++++++- .../src/com/structurizr/model/Model.java | 21 +++++++++++++------ .../com/structurizr/model/Relationship.java | 4 +++- .../model/StaticStructureElement.java | 15 +++++++++++++ ...essAnyRelationshipExistsStrategyTests.java | 9 +++++++- ...ssSameRelationshipExistsStrategyTests.java | 9 +++++++- .../com/structurizr/model/ModelTests.java | 2 +- 10 files changed, 82 insertions(+), 14 deletions(-) diff --git a/structurizr-core/src/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy.java b/structurizr-core/src/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy.java index 0d03b5c55..309b362ab 100644 --- a/structurizr-core/src/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy.java +++ b/structurizr-core/src/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy.java @@ -19,7 +19,7 @@ public void createImpliedRelationships(Relationship relationship) { boolean createRelationship = !source.hasEfferentRelationshipWith(destination); if (createRelationship) { - model.addRelationship(source, destination, relationship.getDescription(), relationship.getTechnology(), relationship.getInteractionStyle(), false); + model.addRelationship(source, destination, relationship.getDescription(), relationship.getTechnology(), relationship.getInteractionStyle(), relationship.getTagsAsSet().toArray(new String[0]), false); } } diff --git a/structurizr-core/src/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategy.java b/structurizr-core/src/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategy.java index 251ec046e..ceee5e87e 100644 --- a/structurizr-core/src/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategy.java +++ b/structurizr-core/src/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategy.java @@ -19,7 +19,7 @@ public void createImpliedRelationships(Relationship relationship) { boolean createRelationship = !source.hasEfferentRelationshipWith(destination, relationship.getDescription()); if (createRelationship) { - model.addRelationship(source, destination, relationship.getDescription(), relationship.getTechnology(), relationship.getInteractionStyle(), false); + model.addRelationship(source, destination, relationship.getDescription(), relationship.getTechnology(), relationship.getInteractionStyle(), relationship.getTagsAsSet().toArray(new String[0]), false); } } diff --git a/structurizr-core/src/com/structurizr/model/DeploymentNode.java b/structurizr-core/src/com/structurizr/model/DeploymentNode.java index 2733c4377..04395d86e 100644 --- a/structurizr-core/src/com/structurizr/model/DeploymentNode.java +++ b/structurizr-core/src/com/structurizr/model/DeploymentNode.java @@ -207,7 +207,21 @@ public Relationship uses(DeploymentNode destination, String description, String * @return a Relationship object */ public Relationship uses(DeploymentNode destination, String description, String technology, InteractionStyle interactionStyle) { - return getModel().addRelationship(this, destination, description, technology, interactionStyle); + return uses(destination, description, technology, interactionStyle, new String[0]); + } + + /** + * Adds a relationship between this and another deployment node. + * + * @param destination the destination DeploymentNode + * @param description a short description of the relationship + * @param technology the technology + * @param interactionStyle the interaction style (Synchronous vs Asynchronous) + * @param tags an array of tags + * @return a Relationship object + */ + public Relationship uses(DeploymentNode destination, String description, String technology, InteractionStyle interactionStyle, String[] tags) { + return getModel().addRelationship(this, destination, description, technology, interactionStyle, tags); } /** diff --git a/structurizr-core/src/com/structurizr/model/InfrastructureNode.java b/structurizr-core/src/com/structurizr/model/InfrastructureNode.java index 46490d859..337dd8e15 100644 --- a/structurizr-core/src/com/structurizr/model/InfrastructureNode.java +++ b/structurizr-core/src/com/structurizr/model/InfrastructureNode.java @@ -43,7 +43,21 @@ public Relationship uses(DeploymentElement destination, String description, Stri * @return a Relationship object */ public Relationship uses(DeploymentElement destination, String description, String technology, InteractionStyle interactionStyle) { - return getModel().addRelationship(this, destination, description, technology, interactionStyle); + return uses(destination, description, technology, interactionStyle, new String[0]); + } + + /** + * Adds a relationship between this and another deployment element (deployment node, infrastructure node, or container instance). + * + * @param destination the destination DeploymentElement + * @param description a short description of the relationship + * @param technology the technology + * @param interactionStyle the interaction style (Synchronous vs Asynchronous) + * @param tags an array of tags + * @return a Relationship object + */ + public Relationship uses(DeploymentElement destination, String description, String technology, InteractionStyle interactionStyle, String[] tags) { + return getModel().addRelationship(this, destination, description, technology, interactionStyle, tags); } /** diff --git a/structurizr-core/src/com/structurizr/model/Model.java b/structurizr-core/src/com/structurizr/model/Model.java index 60abc0e15..d8e8761dc 100644 --- a/structurizr-core/src/com/structurizr/model/Model.java +++ b/structurizr-core/src/com/structurizr/model/Model.java @@ -196,11 +196,16 @@ Component addComponentOfType(Container parent, String name, String type, String @Nullable Relationship addRelationship(Element source, @Nonnull Element destination, String description, String technology, InteractionStyle interactionStyle) { - return addRelationship(source, destination, description, technology, interactionStyle, true); + return addRelationship(source, destination, description, technology, interactionStyle, new String[0], true); } @Nullable - Relationship addRelationship(Element source, @Nonnull Element destination, String description, String technology, InteractionStyle interactionStyle, boolean createImpliedRelationships) { + Relationship addRelationship(Element source, @Nonnull Element destination, String description, String technology, InteractionStyle interactionStyle, String[] tags) { + return addRelationship(source, destination, description, technology, interactionStyle, tags, true); + } + + @Nullable + Relationship addRelationship(Element source, @Nonnull Element destination, String description, String technology, InteractionStyle interactionStyle, String[] tags, boolean createImpliedRelationships) { if (destination == null) { throw new IllegalArgumentException("The destination must be specified."); } @@ -209,7 +214,8 @@ Relationship addRelationship(Element source, @Nonnull Element destination, Strin throw new IllegalArgumentException("Relationships cannot be added between parents and children."); } - Relationship relationship = new Relationship(source, destination, description, technology, interactionStyle); + Relationship relationship = new Relationship(source, destination, description, technology, interactionStyle, tags); + if (addRelationship(relationship)) { if (createImpliedRelationships) { @@ -969,12 +975,15 @@ public void modifyRelationship(Relationship relationship, String description, St throw new IllegalArgumentException("A relationship must be specified."); } - Relationship newRelationship = new Relationship(relationship.getSource(), relationship.getDestination(), description, technology, relationship.getInteractionStyle()); - if (!relationship.getSource().has(newRelationship)) { + if (!relationship.getSource().hasEfferentRelationshipWith(relationship.getDestination(), description)) { relationship.setDescription(description); relationship.setTechnology(technology); } else { - throw new IllegalArgumentException("This relationship exists already: " + newRelationship); + throw new IllegalArgumentException( + String.format("A relationship named \"%s\" between \"%s\" and \"%s\" already exists.", + description, + relationship.getSource().getName(), + relationship.getDestination().getName())); } } diff --git a/structurizr-core/src/com/structurizr/model/Relationship.java b/structurizr-core/src/com/structurizr/model/Relationship.java index a503ba96b..317395cb8 100644 --- a/structurizr-core/src/com/structurizr/model/Relationship.java +++ b/structurizr-core/src/com/structurizr/model/Relationship.java @@ -26,7 +26,7 @@ public final class Relationship extends ModelItem { Relationship() { } - Relationship(Element source, Element destination, String description, String technology, InteractionStyle interactionStyle) { + Relationship(Element source, Element destination, String description, String technology, InteractionStyle interactionStyle, String[] tags) { this(); setSource(source); @@ -40,6 +40,8 @@ public final class Relationship extends ModelItem { } else if (interactionStyle == InteractionStyle.Asynchronous) { addTags(Tags.ASYNCHRONOUS); } + + addTags(tags); } @JsonIgnore diff --git a/structurizr-core/src/com/structurizr/model/StaticStructureElement.java b/structurizr-core/src/com/structurizr/model/StaticStructureElement.java index 976c897b7..83b8b84f7 100644 --- a/structurizr-core/src/com/structurizr/model/StaticStructureElement.java +++ b/structurizr-core/src/com/structurizr/model/StaticStructureElement.java @@ -182,4 +182,19 @@ public Relationship uses(@Nonnull StaticStructureElement destination, String des return getModel().addRelationship(this, destination, description, technology, interactionStyle); } + /** + * Adds a unidirectional "uses" style relationship between this element and the specified element. + * + * @param destination the target of the relationship + * @param description a description of the relationship (e.g. "uses", "gets data from", "sends data to") + * @param technology the technology details (e.g. JSON/HTTPS) + * @param interactionStyle the interaction style (sync vs async) + * @param tags an array of tags + * @return the relationship that has just been created and added to the model + */ + @Nullable + public Relationship uses(@Nonnull StaticStructureElement destination, String description, String technology, InteractionStyle interactionStyle, String[] tags) { + return getModel().addRelationship(this, destination, description, technology, interactionStyle, tags); + } + } \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategyTests.java b/structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategyTests.java index 08255fec0..5110550ab 100644 --- a/structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategyTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategyTests.java @@ -20,7 +20,7 @@ public void test_impliedRelationshipsAreCreated() { model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); - aaa.uses(bbb, "Uses 1"); + aaa.uses(bbb, "Uses 1", null, InteractionStyle.Asynchronous, new String[] { "Tag 1", "Tag 2" }); assertEquals(9, model.getRelationships().size()); assertTrue(aaa.hasEfferentRelationshipWith(bbb, "Uses 1")); @@ -37,6 +37,13 @@ public void test_impliedRelationshipsAreCreated() { assertTrue(a.hasEfferentRelationshipWith(bb, "Uses 1")); assertTrue(a.hasEfferentRelationshipWith(b, "Uses 1")); + // and all relationships should have the same interaction style and tags + for (Relationship r : model.getRelationships()) { + assertEquals(InteractionStyle.Asynchronous, r.getInteractionStyle()); + assertTrue(r.getTagsAsSet().contains("Tag 1")); + assertTrue(r.getTagsAsSet().contains("Tag 2")); + } + // and add another relationship with a different description aaa.uses(bbb, "Uses 2"); assertEquals(10, model.getRelationships().size()); // no change diff --git a/structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategyTests.java b/structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategyTests.java index e48b04748..26a5952d4 100644 --- a/structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategyTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategyTests.java @@ -20,11 +20,18 @@ public void test_impliedRelationships_WhenNoSummaryRelationshipsExist() { model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategy()); - aaa.uses(bbb, "Uses 1"); + aaa.uses(bbb, "Uses 1", null, InteractionStyle.Asynchronous, new String[] { "Tag 1", "Tag 2" }); assertEquals(9, model.getRelationships().size()); assertTrue(aaa.hasEfferentRelationshipWith(bbb, "Uses 1")); + // and all relationships should have the same interaction style and tags + for (Relationship r : model.getRelationships()) { + assertEquals(InteractionStyle.Asynchronous, r.getInteractionStyle()); + assertTrue(r.getTagsAsSet().contains("Tag 1")); + assertTrue(r.getTagsAsSet().contains("Tag 2")); + } + // AAA->BBB implies AAA->BB AAA->B AA->BBB AA->BB AA->B A->BBB A->BB A->B assertTrue(aaa.hasEfferentRelationshipWith(bb, "Uses 1")); assertTrue(aaa.hasEfferentRelationshipWith(b, "Uses 1")); diff --git a/structurizr-core/test/unit/com/structurizr/model/ModelTests.java b/structurizr-core/test/unit/com/structurizr/model/ModelTests.java index 1e65fee4c..bf7335e7b 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ModelTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ModelTests.java @@ -335,7 +335,7 @@ public void test_modifyRelationship_ThrowsAnException_WhenThatRelationshipDoesAl model.modifyRelationship(relationship, "Uses", "Technology"); fail(); } catch (IllegalArgumentException iae) { - assertEquals("This relationship exists already: {1 | Element 1 | Description} ---[Uses]---> {2 | Element 2 | Description}", iae.getMessage()); + assertEquals("A relationship named \"Uses\" between \"Element 1\" and \"Element 2\" already exists.", iae.getMessage()); } } From 17ee5ea811df3a295b280e62a2ce443fb45b38ce Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 15 Jul 2020 10:23:42 +0100 Subject: [PATCH 160/717] Adds a method to enable Graphviz auto-layout, with some defaults. --- structurizr-core/src/com/structurizr/view/View.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/structurizr-core/src/com/structurizr/view/View.java b/structurizr-core/src/com/structurizr/view/View.java index 33d0a3b9b..f7c2038b2 100644 --- a/structurizr-core/src/com/structurizr/view/View.java +++ b/structurizr-core/src/com/structurizr/view/View.java @@ -20,6 +20,9 @@ */ public abstract class View { + private static final int DEFAULT_RANK_SEPARATION = 300; + private static final int DEFAULT_NODE_SEPARATION = 300; + private SoftwareSystem softwareSystem; private String softwareSystemId; private String description = ""; @@ -166,6 +169,15 @@ public void enableAutomaticLayout(AutomaticLayout.RankDirection rankDirection, i this.automaticLayout = new AutomaticLayout(AutomaticLayout.Implementation.Dagre, rankDirection, rankSeparation, nodeSeparation, edgeSeparation, vertices); } + /** + * Enables automatic layout for this view, with the specified direction, using the Graphviz implementation. + * + * @param rankDirection the rank direction + */ + public void enableAutomaticLayout(AutomaticLayout.RankDirection rankDirection) { + enableAutomaticLayout(rankDirection, DEFAULT_RANK_SEPARATION, DEFAULT_NODE_SEPARATION); + } + /** * Enables automatic layout for this view, with the specified settings, using the Graphviz implementation. * From d2dfbfbcd994a71c291edc3788319579ac01f8e7 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 15 Jul 2020 10:25:42 +0100 Subject: [PATCH 161/717] Updated to reflect release. --- build.gradle | 2 +- docs/changelog.md | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 0826f1602..57a512a67 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.4.7' + version = '1.4.8' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 27b86f407..32f2b4b24 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,10 @@ # Changelog +## 1.4.8 (15th July 2020) + +- Implied relationships now also copy the interaction style and relationships. +- Fixes a serialisation problem with themes and styles. + ## 1.4.7 (6th July 2020) - Remove default stroke styling. From 8f02fd9d5d96ebf11d77040c28874025b98017e5 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 15 Jul 2020 10:50:43 +0100 Subject: [PATCH 162/717] Typo. --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 32f2b4b24..2741c4ecf 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -2,7 +2,7 @@ ## 1.4.8 (15th July 2020) -- Implied relationships now also copy the interaction style and relationships. +- Implied relationships now also copy the interaction style and tags. - Fixes a serialisation problem with themes and styles. ## 1.4.7 (6th July 2020) From 50599a8df2a1b021dc082e98664358c7eb39ca0a Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 16 Jul 2020 14:00:07 +0100 Subject: [PATCH 163/717] Fixes #151: linked relationship tags were not being taken into account when finding relationship styling. --- build.gradle | 2 +- docs/changelog.md | 4 +++ .../src/com/structurizr/model/Model.java | 1 + .../com/structurizr/model/Relationship.java | 11 ++++++++ .../src/com/structurizr/view/Styles.java | 16 +++++++++++- .../unit/com/structurizr/view/StyleTests.java | 26 ++++++++++++++++--- 6 files changed, 55 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index 57a512a67..edc9c28a7 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.4.8' + version = '1.4.9' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 2741c4ecf..96e77b7c9 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.4.9 (unreleased) + +- Fixes #151: linked relationship tags were not being taken into account when finding relationship styling. + ## 1.4.8 (15th July 2020) - Implied relationships now also copy the interaction style and tags. diff --git a/structurizr-core/src/com/structurizr/model/Model.java b/structurizr-core/src/com/structurizr/model/Model.java index d8e8761dc..8cea46b64 100644 --- a/structurizr-core/src/com/structurizr/model/Model.java +++ b/structurizr-core/src/com/structurizr/model/Model.java @@ -281,6 +281,7 @@ private void addRelationshipToInternalStructures(Relationship relationship) { } relationshipsById.put(relationship.getId(), relationship); + relationship.setModel(this); idGenerator.found(relationship.getId()); } diff --git a/structurizr-core/src/com/structurizr/model/Relationship.java b/structurizr-core/src/com/structurizr/model/Relationship.java index 317395cb8..7bf533f4a 100644 --- a/structurizr-core/src/com/structurizr/model/Relationship.java +++ b/structurizr-core/src/com/structurizr/model/Relationship.java @@ -12,6 +12,8 @@ */ public final class Relationship extends ModelItem { + private Model model; + private Element source; private String sourceId; private Element destination; @@ -44,6 +46,15 @@ public final class Relationship extends ModelItem { addTags(tags); } + @JsonIgnore + public Model getModel() { + return this.model; + } + + protected void setModel(Model model) { + this.model = model; + } + @JsonIgnore public Element getSource() { return source; diff --git a/structurizr-core/src/com/structurizr/view/Styles.java b/structurizr-core/src/com/structurizr/view/Styles.java index d09707da3..3927318c7 100644 --- a/structurizr-core/src/com/structurizr/view/Styles.java +++ b/structurizr-core/src/com/structurizr/view/Styles.java @@ -116,6 +116,7 @@ private ElementStyle findElementStyle(String tag) { private RelationshipStyle findRelationshipStyle(String tag) { if (tag != null) { + tag = tag.trim(); for (RelationshipStyle relationshipStyle : relationships) { if (relationshipStyle != null && relationshipStyle.getTag().equals(tag)) { return relationshipStyle; @@ -193,9 +194,22 @@ public ElementStyle findElementStyle(Element element) { public RelationshipStyle findRelationshipStyle(Relationship relationship) { RelationshipStyle style = new RelationshipStyle("").color("#707070"); + String tags; if (relationship != null) { - for (String tag : relationship.getTagsAsSet()) { + if (!StringUtils.isNullOrEmpty(relationship.getLinkedRelationshipId())) { + // the "linked relationship ID" is used for container instance -> container instance relationships + Relationship linkedRelationship = relationship.getModel().getRelationship(relationship.getLinkedRelationshipId()); + if (linkedRelationship != null) { + tags = linkedRelationship.getTags() + "," + relationship.getTags(); + } else { + tags = relationship.getTags(); + } + } else { + tags = relationship.getTags(); + } + + for (String tag : tags.split(",")) { RelationshipStyle relationshipStyle = findRelationshipStyle(tag); if (relationshipStyle != null) { if (relationshipStyle.getThickness() != null) { diff --git a/structurizr-core/test/unit/com/structurizr/view/StyleTests.java b/structurizr-core/test/unit/com/structurizr/view/StyleTests.java index 1a4df4416..a67ad52bd 100644 --- a/structurizr-core/test/unit/com/structurizr/view/StyleTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/StyleTests.java @@ -1,9 +1,7 @@ package com.structurizr.view; import com.structurizr.AbstractWorkspaceTestBase; -import com.structurizr.model.Relationship; -import com.structurizr.model.SoftwareSystem; -import com.structurizr.model.Tags; +import com.structurizr.model.*; import org.junit.Test; import static org.junit.Assert.assertEquals; @@ -106,6 +104,28 @@ public void test_findRelationshipStyle_ReturnsTheCorrectStyle_WhenStylesAreDefin assertEquals("#0000ff", style.getColor()); } + @Test + public void test_findRelationshipStyle_ReturnsTheCorrectStyle_WhenThereIsALinkedRelationship() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); + Container container1 = softwareSystem.addContainer("Container 1", "Description", "Technology"); + Container container2 = softwareSystem.addContainer("Container 2", "Description", "Technology"); + Relationship relationship = container1.uses(container2, "Uses"); + relationship.addTags("Tag"); + styles.addRelationshipStyle("Tag").color("#0000ff"); + + RelationshipStyle style = styles.findRelationshipStyle(relationship); + assertEquals("#0000ff", style.getColor()); + + DeploymentNode deploymentNode = model.addDeploymentNode("Server"); + ContainerInstance containerInstance1 = deploymentNode.add(container1); + ContainerInstance containerInstance2 = deploymentNode.add(container2); + + Relationship relationshipInstance = containerInstance1.getEfferentRelationshipWith(containerInstance2); + + style = styles.findRelationshipStyle(relationshipInstance); + assertEquals("#0000ff", style.getColor()); + } + @Test public void test_addElementStyleByTag_ThrowsAnException_WhenAStyleWithTheSameTagExistsAlready() { try { From bcd1e23a4ba4b244780c0fa33d14886ab650472f Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 26 Jul 2020 11:36:31 +0100 Subject: [PATCH 164/717] Themes are not serialising the name and description. --- .../src/com/structurizr/util/ThemeUtils.java | 4 ++-- .../test/unit/com/structurizr/util/ThemeUtilsTests.java | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/structurizr-client/src/com/structurizr/util/ThemeUtils.java b/structurizr-client/src/com/structurizr/util/ThemeUtils.java index 17b5137a6..a0dd13e51 100644 --- a/structurizr-client/src/com/structurizr/util/ThemeUtils.java +++ b/structurizr-client/src/com/structurizr/util/ThemeUtils.java @@ -140,7 +140,7 @@ static class Theme { this.relationships = relationships; } - String getName() { + public String getName() { return name; } @@ -148,7 +148,7 @@ void setName(String name) { this.name = name; } - String getDescription() { + public String getDescription() { return description; } diff --git a/structurizr-client/test/unit/com/structurizr/util/ThemeUtilsTests.java b/structurizr-client/test/unit/com/structurizr/util/ThemeUtilsTests.java index 5eb77f3cf..4570a34f4 100644 --- a/structurizr-client/test/unit/com/structurizr/util/ThemeUtilsTests.java +++ b/structurizr-client/test/unit/com/structurizr/util/ThemeUtilsTests.java @@ -23,11 +23,16 @@ public void test_loadStylesFromThemes() throws Exception { @Test public void test_toJson() throws Exception { Workspace workspace = new Workspace("Name", "Description"); - assertEquals("{ }", ThemeUtils.toJson(workspace)); + assertEquals("{\n" + + " \"name\" : \"Name\",\n" + + " \"description\" : \"Description\"\n" + + "}", ThemeUtils.toJson(workspace)); workspace.getViews().getConfiguration().getStyles().addElementStyle(Tags.ELEMENT).background("#ff0000"); workspace.getViews().getConfiguration().getStyles().addRelationshipStyle(Tags.RELATIONSHIP).color("#ff0000"); assertEquals("{\n" + + " \"name\" : \"Name\",\n" + + " \"description\" : \"Description\",\n" + " \"elements\" : [ {\n" + " \"tag\" : \"Element\",\n" + " \"background\" : \"#ff0000\"\n" + From f25969ca0c227624d6ccb41b8867a20ad36d64e1 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 3 Aug 2020 09:07:18 +0100 Subject: [PATCH 165/717] Addresses issue #153. --- docs/changelog.md | 1 + .../src/com/structurizr/view/DynamicView.java | 25 ++++++++++++++++-- .../structurizr/view/RelationshipView.java | 14 ++++++++++ .../src/com/structurizr/view/View.java | 10 ------- .../structurizr/view/DynamicViewTests.java | 26 +++++++++++++++++++ 5 files changed, 64 insertions(+), 12 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 96e77b7c9..bbad167a9 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -3,6 +3,7 @@ ## 1.4.9 (unreleased) - Fixes #151: linked relationship tags were not being taken into account when finding relationship styling. +- Fixes #153: Allow relationships in DynamicView to go both ways without two relationships between Elements in Model. ## 1.4.8 (15th July 2020) diff --git a/structurizr-core/src/com/structurizr/view/DynamicView.java b/structurizr-core/src/com/structurizr/view/DynamicView.java index e151abc52..f97b6ea8b 100644 --- a/structurizr-core/src/com/structurizr/view/DynamicView.java +++ b/structurizr-core/src/com/structurizr/view/DynamicView.java @@ -109,10 +109,31 @@ public RelationshipView add(@Nonnull Element source, String description, @Nonnul if (relationship != null) { addElement(source, false); addElement(destination, false); - return addRelationship(relationship, description, sequenceNumber.getNext()); + + return addRelationship(relationship, description, sequenceNumber.getNext(), false); } else { - throw new IllegalArgumentException("A relationship between " + source.getName() + " and " + destination.getName() + " does not exist in model."); + // perhaps model this as a return/reply/response message instead, if the reverse relationship exists + relationship = destination.getEfferentRelationshipWith(source); + if (relationship != null) { + addElement(source, false); + addElement(destination, false); + + return addRelationship(relationship, description, sequenceNumber.getNext(), true); + } else { + throw new IllegalArgumentException("A relationship between " + source.getName() + " and " + destination.getName() + " does not exist in model."); + } + } + } + + protected RelationshipView addRelationship(Relationship relationship, String description, String order, boolean response) { + RelationshipView relationshipView = addRelationship(relationship); + if (relationshipView != null) { + relationshipView.setDescription(description); + relationshipView.setOrder(order); + relationshipView.setResponse(response); } + + return relationshipView; } /** diff --git a/structurizr-core/src/com/structurizr/view/RelationshipView.java b/structurizr-core/src/com/structurizr/view/RelationshipView.java index d2e12edcd..f3d416a05 100644 --- a/structurizr-core/src/com/structurizr/view/RelationshipView.java +++ b/structurizr-core/src/com/structurizr/view/RelationshipView.java @@ -21,6 +21,7 @@ public final class RelationshipView { private String id; private String description; private String order; + private Boolean response; private Set vertices = new LinkedHashSet<>(); @JsonInclude(value = JsonInclude.Include.NON_NULL) @@ -104,6 +105,19 @@ public void setOrder(String order) { this.order = order; } + /** + * Gets whether this relationship view represents a response (used in dynamic views only). + * + * @return true if a response, false or null otherwise + */ + public Boolean isResponse() { + return response; + } + + void setResponse(Boolean response) { + this.response = response; + } + /** * Gets the set of vertices used to render the relationship. * diff --git a/structurizr-core/src/com/structurizr/view/View.java b/structurizr-core/src/com/structurizr/view/View.java index f7c2038b2..69965e3cc 100644 --- a/structurizr-core/src/com/structurizr/view/View.java +++ b/structurizr-core/src/com/structurizr/view/View.java @@ -299,16 +299,6 @@ public boolean isElementInView(Element element) { return this.elementViews.stream().anyMatch(ev -> ev.getElement().equals(element)); } - protected RelationshipView addRelationship(Relationship relationship, String description, String order) { - RelationshipView relationshipView = addRelationship(relationship); - if (relationshipView != null) { - relationshipView.setDescription(description); - relationshipView.setOrder(order); - } - - return relationshipView; - } - /** * Removes a relationship from this view. * diff --git a/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java b/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java index c14c15088..16d35b856 100644 --- a/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java @@ -6,6 +6,7 @@ import org.junit.Before; import org.junit.Test; +import java.util.ArrayList; import java.util.LinkedList; import java.util.List; @@ -345,4 +346,29 @@ public void test_getRelationships_WhenTheOrderPropertyIsAString() { assertEquals("1j", relationships.get(9).getOrder()); } + @Test + public void test_response() { + workspace = new Workspace("Name", "Description"); + model = workspace.getModel(); + + SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1", "Description"); + SoftwareSystem softwareSystem2 = model.addSoftwareSystem("Software System 2", "Description"); + Relationship relationship = softwareSystem1.uses(softwareSystem2, "Uses"); + + DynamicView view = workspace.getViews().createDynamicView("key", "Description"); + view.add(softwareSystem1, "Asks for X", softwareSystem2); + view.add(softwareSystem2, "Returns X", softwareSystem1); // this relationship doesn't exist, so is assumed to be a response + + List list = new ArrayList<>(view.getRelationships()); + RelationshipView relationshipView = list.get(0); + assertSame(relationship, relationshipView.getRelationship()); + assertEquals("Asks for X", relationshipView.getDescription()); + assertFalse(relationshipView.isResponse()); + + relationshipView = list.get(1); + assertSame(relationship, relationshipView.getRelationship()); + assertEquals("Returns X", relationshipView.getDescription()); + assertTrue(relationshipView.isResponse()); + } + } \ No newline at end of file From d7ab7f59e8aa1b1302390c931bca728224493059 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 3 Aug 2020 11:57:36 +0100 Subject: [PATCH 166/717] The interaction style on relationships no longer defaults to Synchronous. --- docs/changelog.md | 1 + .../com/structurizr/model/Relationship.java | 17 ++++++++------ .../model/StaticStructureElement.java | 8 +++---- .../structurizr/model/RelationshipTests.java | 22 +++++++++++-------- 4 files changed, 28 insertions(+), 20 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index bbad167a9..6939756ba 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -4,6 +4,7 @@ - Fixes #151: linked relationship tags were not being taken into account when finding relationship styling. - Fixes #153: Allow relationships in DynamicView to go both ways without two relationships between Elements in Model. +- The interaction style on relationships no longer defaults to Synchronous. ## 1.4.8 (15th July 2020) diff --git a/structurizr-core/src/com/structurizr/model/Relationship.java b/structurizr-core/src/com/structurizr/model/Relationship.java index 7bf533f4a..b01f7cc8f 100644 --- a/structurizr-core/src/com/structurizr/model/Relationship.java +++ b/structurizr-core/src/com/structurizr/model/Relationship.java @@ -37,12 +37,6 @@ public final class Relationship extends ModelItem { setTechnology(technology); setInteractionStyle(interactionStyle); - if (interactionStyle == InteractionStyle.Synchronous) { - addTags(Tags.SYNCHRONOUS); - } else if (interactionStyle == InteractionStyle.Asynchronous) { - addTags(Tags.ASYNCHRONOUS); - } - addTags(tags); } @@ -154,7 +148,16 @@ void setLinkedRelationshipId(String baseRelationshipId) { @Override protected Set getRequiredTags() { if (linkedRelationshipId == null) { - return new LinkedHashSet<>(Collections.singletonList(Tags.RELATIONSHIP)); + Set tags = new LinkedHashSet<>(); + tags.add(Tags.RELATIONSHIP); + + if (interactionStyle == InteractionStyle.Synchronous) { + tags.add(Tags.SYNCHRONOUS); + } else if (interactionStyle == InteractionStyle.Asynchronous) { + tags.add(Tags.ASYNCHRONOUS); + } + + return tags; } else { return Collections.emptySet(); } diff --git a/structurizr-core/src/com/structurizr/model/StaticStructureElement.java b/structurizr-core/src/com/structurizr/model/StaticStructureElement.java index 83b8b84f7..cac51ce61 100644 --- a/structurizr-core/src/com/structurizr/model/StaticStructureElement.java +++ b/structurizr-core/src/com/structurizr/model/StaticStructureElement.java @@ -34,7 +34,7 @@ public Relationship uses(@Nonnull SoftwareSystem destination, String description */ @Nullable public Relationship uses(@Nonnull SoftwareSystem destination, String description, String technology) { - return uses(destination, description, technology, InteractionStyle.Synchronous); + return uses(destination, description, technology, null); } /** @@ -73,7 +73,7 @@ public Relationship uses(@Nonnull Container destination, String description) { */ @Nullable public Relationship uses(@Nonnull Container destination, String description, String technology) { - return uses(destination, description, technology, InteractionStyle.Synchronous); + return uses(destination, description, technology, null); } /** @@ -112,7 +112,7 @@ public Relationship uses(@Nonnull Component destination, String description) { */ @Nullable public Relationship uses(@Nonnull Component destination, String description, String technology) { - return uses(destination, description, technology, InteractionStyle.Synchronous); + return uses(destination, description, technology, null); } /** @@ -151,7 +151,7 @@ public Relationship delivers(@Nonnull Person destination, String description) { */ @Nullable public Relationship delivers(@Nonnull Person destination, String description, String technology) { - return delivers(destination, description, technology, InteractionStyle.Synchronous); + return delivers(destination, description, technology, null); } /** diff --git a/structurizr-core/test/unit/com/structurizr/model/RelationshipTests.java b/structurizr-core/test/unit/com/structurizr/model/RelationshipTests.java index 9af70a713..e1ec02a3d 100644 --- a/structurizr-core/test/unit/com/structurizr/model/RelationshipTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/RelationshipTests.java @@ -25,21 +25,21 @@ public void test_getDescription_NeverReturnsNull() { @Test public void test_getTags_WhenThereAreNoTags() { Relationship relationship = softwareSystem1.uses(softwareSystem2, "uses"); - assertEquals("Relationship,Synchronous", relationship.getTags()); + assertEquals("Relationship", relationship.getTags()); } @Test public void test_getTags_ReturnsTheListOfTags_WhenThereAreSomeTags() { Relationship relationship = softwareSystem1.uses(softwareSystem2, "uses"); relationship.addTags("tag1", "tag2", "tag3"); - assertEquals("Relationship,Synchronous,tag1,tag2,tag3", relationship.getTags()); + assertEquals("Relationship,tag1,tag2,tag3", relationship.getTags()); } @Test public void test_setTags_ClearsTheTags_WhenPassedNull() { Relationship relationship = softwareSystem1.uses(softwareSystem2, "uses"); relationship.addTags("Tag 1", "Tag 2"); - assertEquals("Relationship,Synchronous,Tag 1,Tag 2", relationship.getTags()); + assertEquals("Relationship,Tag 1,Tag 2", relationship.getTags()); relationship.setTags(null); assertEquals("Relationship", relationship.getTags()); } @@ -48,32 +48,36 @@ public void test_setTags_ClearsTheTags_WhenPassedNull() { public void test_addTags_DoesNotDoAnything_WhenPassedNull() { Relationship relationship = softwareSystem1.uses(softwareSystem2, "uses"); relationship.addTags((String)null); - assertEquals("Relationship,Synchronous", relationship.getTags()); + assertEquals("Relationship", relationship.getTags()); relationship.addTags(null, null, null); - assertEquals("Relationship,Synchronous", relationship.getTags()); + assertEquals("Relationship", relationship.getTags()); } @Test public void test_addTags_AddsTags_WhenPassedSomeTags() { Relationship relationship = softwareSystem1.uses(softwareSystem2, "uses"); relationship.addTags(null, "tag1", null, "tag2"); - assertEquals("Relationship,Synchronous,tag1,tag2", relationship.getTags()); + assertEquals("Relationship,tag1,tag2", relationship.getTags()); } @Test - public void test_getInteractionStyle_ReturnsSynchronous_WhenNotExplicitlySet() { + public void test_getInteractionStyle_ReturnsNull_WhenNotExplicitlySet() { Relationship relationship = softwareSystem1.uses(softwareSystem2, "uses"); - assertEquals(InteractionStyle.Synchronous, relationship.getInteractionStyle()); + assertNull(relationship.getInteractionStyle()); } @Test public void test_getTags_IncludesTheInteractionStyleWhenSpecified() { Relationship relationship = softwareSystem1.uses(softwareSystem2, "Uses 1", "Technology"); + assertFalse(relationship.getTags().contains(Tags.SYNCHRONOUS)); + assertFalse(relationship.getTags().contains(Tags.ASYNCHRONOUS)); + + relationship.setInteractionStyle(InteractionStyle.Synchronous); assertTrue(relationship.getTags().contains(Tags.SYNCHRONOUS)); assertFalse(relationship.getTags().contains(Tags.ASYNCHRONOUS)); - relationship = softwareSystem1.uses(softwareSystem2, "Uses 2", "Technology", InteractionStyle.Asynchronous); + relationship.setInteractionStyle(InteractionStyle.Asynchronous); assertFalse(relationship.getTags().contains(Tags.SYNCHRONOUS)); assertTrue(relationship.getTags().contains(Tags.ASYNCHRONOUS)); } From f3c903e612db6b3f85aa478ed21b0bcc759f48cf Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 3 Aug 2020 19:00:51 +0100 Subject: [PATCH 167/717] Adds support for software system instances on deployment views. --- build.gradle | 2 +- docs/changelog.md | 3 +- .../structurizr/model/ContainerInstance.java | 136 +--------------- .../com/structurizr/model/DeploymentNode.java | 54 ++++++- .../src/com/structurizr/model/Model.java | 94 +++++++---- .../model/SoftwareSystemInstance.java | 54 +++++++ .../model/StaticStructureElementInstance.java | 146 ++++++++++++++++++ .../src/com/structurizr/model/Tags.java | 1 + .../com/structurizr/view/DeploymentView.java | 21 ++- .../model/ContainerInstanceTests.java | 4 +- .../model/DeploymentNodeTests.java | 13 +- .../com/structurizr/model/ModelTests.java | 33 +++- .../com/structurizr/example/BigBankPlc.java | 17 +- 13 files changed, 388 insertions(+), 190 deletions(-) create mode 100644 structurizr-core/src/com/structurizr/model/SoftwareSystemInstance.java create mode 100644 structurizr-core/src/com/structurizr/model/StaticStructureElementInstance.java diff --git a/build.gradle b/build.gradle index edc9c28a7..e45436344 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.4.9' + version = '1.5.0' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 6939756ba..47b9121d6 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,10 +1,11 @@ # Changelog -## 1.4.9 (unreleased) +## 1.5.0 (unreleased) - Fixes #151: linked relationship tags were not being taken into account when finding relationship styling. - Fixes #153: Allow relationships in DynamicView to go both ways without two relationships between Elements in Model. - The interaction style on relationships no longer defaults to Synchronous. +- Adds support for software system instances on deployment views. ## 1.4.8 (15th July 2020) diff --git a/structurizr-core/src/com/structurizr/model/ContainerInstance.java b/structurizr-core/src/com/structurizr/model/ContainerInstance.java index 53131f0e5..cf41b5b9d 100644 --- a/structurizr-core/src/com/structurizr/model/ContainerInstance.java +++ b/structurizr-core/src/com/structurizr/model/ContainerInstance.java @@ -1,35 +1,23 @@ package com.structurizr.model; import com.fasterxml.jackson.annotation.JsonIgnore; -import com.structurizr.util.Url; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; /** * Represents a deployment instance of a {@link Container}, which can be added to a {@link DeploymentNode}. */ -public final class ContainerInstance extends DeploymentElement { - - private static final int DEFAULT_HEALTH_CHECK_INTERVAL_IN_SECONDS = 60; - private static final long DEFAULT_HEALTH_CHECK_TIMEOUT_IN_MILLISECONDS = 0; +public final class ContainerInstance extends StaticStructureElementInstance { private Container container; private String containerId; - private int instanceId; - private Set healthChecks = new HashSet<>(); ContainerInstance() { } ContainerInstance(Container container, int instanceId, String environment) { + super(instanceId, environment); + setContainer(container); addTags(Tags.CONTAINER_INSTANCE); - setInstanceId(instanceId); - setEnvironment(environment); } @JsonIgnore @@ -41,6 +29,11 @@ void setContainer(Container container) { this.container = container; } + @Override + public StaticStructureElement getElement() { + return getContainer(); + } + /** * Gets the ID of the container that this object represents a deployment instance of. * @@ -58,117 +51,4 @@ void setContainerId(String containerId) { this.containerId = containerId; } - /** - * Gets the instance ID of this container. - * - * @return the instance ID, an integer greater than zero - */ - public int getInstanceId() { - return instanceId; - } - - void setInstanceId(int instanceId) { - this.instanceId = instanceId; - } - - @Override - @JsonIgnore - protected Set getRequiredTags() { - return Collections.emptySet(); - } - - @Override - public boolean removeTag(String tag) { - // do nothing ... tags cannot be removed from container instances (they should reflect the container they are based upon) - return false; - } - - @Override - @JsonIgnore - public String getCanonicalName() { - return container.getCanonicalName() + "[" + instanceId + "]"; - } - - @Override - @JsonIgnore - public Element getParent() { - return container.getParent(); - } - - @Override - @JsonIgnore - public String getName() { - return container.getName() + " (container instance)"; - } - - @Override - public void setName(String name) { - // no-op ... the name of a container instance is taken from the associated Container - } - - /** - * Gets the set of health checks associated with this container instance. - * - * @return a Set of HttpHealthCheck instances - */ - @Nonnull - public Set getHealthChecks() { - return new HashSet<>(healthChecks); - } - - void setHealthChecks(Set healthChecks) { - this.healthChecks = healthChecks; - } - - /** - * Adds a new health check, with the default interval (60 seconds) and timeout (0 milliseconds). - * - * @param name the name of the health check - * @param url the URL of the health check - * @return a HttpHealthCheck instance representing the health check that has been added - * @throws IllegalArgumentException if the name is empty, or the URL is not a well-formed URL - */ - @Nonnull - public HttpHealthCheck addHealthCheck(String name, String url) { - return addHealthCheck(name, url, DEFAULT_HEALTH_CHECK_INTERVAL_IN_SECONDS, DEFAULT_HEALTH_CHECK_TIMEOUT_IN_MILLISECONDS); - } - - /** - * Adds a new health check. - * - * @param name the name of the health check - * @param url the URL of the health check - * @param interval the polling interval, in seconds - * @param timeout the timeout, in milliseconds - * @return a HttpHealthCheck instance representing the health check that has been added - * @throws IllegalArgumentException if the name is empty, the URL is not a well-formed URL, or the interval/timeout is not zero/a positive integer - */ - @Nonnull - public HttpHealthCheck addHealthCheck(String name, String url, int interval, long timeout) { - if (name == null || name.trim().length() == 0) { - throw new IllegalArgumentException("The name must not be null or empty."); - } - - if (url == null || url.trim().length() == 0) { - throw new IllegalArgumentException("The URL must not be null or empty."); - } - - if (!Url.isUrl(url)) { - throw new IllegalArgumentException(url + " is not a valid URL."); - } - - if (interval < 0) { - throw new IllegalArgumentException("The polling interval must be zero or a positive integer."); - } - - if (timeout < 0) { - throw new IllegalArgumentException("The timeout must be zero or a positive integer."); - } - - HttpHealthCheck healthCheck = new HttpHealthCheck(name, url, interval, timeout); - healthChecks.add(healthCheck); - - return healthCheck; - } - } \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/model/DeploymentNode.java b/structurizr-core/src/com/structurizr/model/DeploymentNode.java index 04395d86e..069770ebe 100644 --- a/structurizr-core/src/com/structurizr/model/DeploymentNode.java +++ b/structurizr-core/src/com/structurizr/model/DeploymentNode.java @@ -27,27 +27,52 @@ public final class DeploymentNode extends DeploymentElement { private Set children = new HashSet<>(); private Set infrastructureNodes = new HashSet<>(); + private Set softwareSystemInstances = new HashSet<>(); private Set containerInstances = new HashSet<>(); /** - * Adds a container instance to this deployment node, replicating all of the container-container relationships. + * Adds a software system instance to this deployment node, replicating relationships. + * + * @param softwareSystem the SoftwareSystem to add an instance of + * @return a SoftwareSystemInstance object + */ + public SoftwareSystemInstance add(SoftwareSystem softwareSystem) { + return add(softwareSystem, true); + } + + /** + * Adds a software system instance to this deployment node, optionally replicating relationships. + * + * @param softwareSystem the SoftwareSystem to add an instance of + * @param replicateRelationships true if relationships should be replicated between the element instances in the same deployment environment, false otherwise + * @return a SoftwareSystemInstance object + */ + public SoftwareSystemInstance add(SoftwareSystem softwareSystem, boolean replicateRelationships) { + SoftwareSystemInstance softwareSystemInstance = getModel().addSoftwareSystemInstance(this, softwareSystem, replicateRelationships); + this.softwareSystemInstances.add(softwareSystemInstance); + + return softwareSystemInstance; + } + + /** + * Adds a container instance to this deployment node, replicating relationships. * * @param container the Container to add an instance of - * @return a ContainerInstance object + * @return a ContainerInstance object */ public ContainerInstance add(Container container) { return add(container, true); } /** - * Adds a container instance to this deployment node, optionally replicating all of the container-container relationships. + * Adds a container instance to this deployment node, optionally replicating relationships. * * @param container the Container to add an instance of - * @param replicateContainerRelationships true if the container-container relationships should be replicated between the container instances, false otherwise - * @return a ContainerInstance object + * @param replicateRelationships true if relationships should be replicated between the element instances in the same deployment environment, false otherwise + * @return a ContainerInstance object */ - public ContainerInstance add(Container container, boolean replicateContainerRelationships) { - ContainerInstance containerInstance = getModel().addContainerInstance(this, container, replicateContainerRelationships); + public ContainerInstance add(Container container, boolean replicateRelationships) { + ContainerInstance containerInstance = getModel().addContainerInstance(this, container, replicateRelationships); this.containerInstances.add(containerInstance); return containerInstance; @@ -259,6 +284,21 @@ public boolean hasChildren() { return !children.isEmpty(); } + /** + * Gets the set of software system instances associated with this deployment node. + * + * @return a Set of SoftwareSystemInstance objects + */ + public Set getSoftwareSystemInstances() { + return new HashSet<>(softwareSystemInstances); + } + + void setSoftwareSystemInstances(Set softwareSystemInstances) { + if (softwareSystemInstances != null) { + this.softwareSystemInstances = new HashSet<>(softwareSystemInstances); + } + } + /** * Gets the set of container instances associated with this deployment node. * diff --git a/structurizr-core/src/com/structurizr/model/Model.java b/structurizr-core/src/com/structurizr/model/Model.java index 8cea46b64..d75de2ee4 100644 --- a/structurizr-core/src/com/structurizr/model/Model.java +++ b/structurizr-core/src/com/structurizr/model/Model.java @@ -448,6 +448,11 @@ private void hydrateDeploymentNode(DeploymentNode deploymentNode, DeploymentNode deploymentNode.getChildren().forEach(child -> hydrateDeploymentNode(child, deploymentNode)); + for (SoftwareSystemInstance softwareSystemInstance : deploymentNode.getSoftwareSystemInstances()) { + softwareSystemInstance.setSoftwareSystem((SoftwareSystem)getElement(softwareSystemInstance.getSoftwareSystemId())); + addElementToInternalStructures(softwareSystemInstance); + } + for (ContainerInstance containerInstance : deploymentNode.getContainerInstances()) { containerInstance.setContainer((Container)getElement(containerInstance.getContainerId())); addElementToInternalStructures(containerInstance); @@ -876,52 +881,77 @@ public DeploymentNode getDeploymentNodeWithName(String name, String environment) return null; } - ContainerInstance addContainerInstance(DeploymentNode deploymentNode, Container container, boolean replicateContainerRelationships) { + SoftwareSystemInstance addSoftwareSystemInstance(DeploymentNode deploymentNode, SoftwareSystem softwareSystem, boolean replicateRelationships) { + if (softwareSystem == null) { + throw new IllegalArgumentException("A software system must be specified."); + } + + long instanceNumber = getElements().stream().filter(e -> e instanceof SoftwareSystemInstance && ((SoftwareSystemInstance)e).getSoftwareSystem().equals(softwareSystem)).count(); + instanceNumber++; + SoftwareSystemInstance softwareSystemInstance = new SoftwareSystemInstance(softwareSystem, (int)instanceNumber, deploymentNode.getEnvironment()); + softwareSystemInstance.setId(idGenerator.generateId(softwareSystemInstance)); + + if (replicateRelationships) { + replicateElementRelationships(deploymentNode.getEnvironment(), softwareSystemInstance); + } + + addElementToInternalStructures(softwareSystemInstance); + + return softwareSystemInstance; + } + + ContainerInstance addContainerInstance(DeploymentNode deploymentNode, Container container, boolean replicateRelationships) { if (container == null) { throw new IllegalArgumentException("A container must be specified."); } - long instanceNumber = getElements().stream().filter(e -> e instanceof ContainerInstance && ((ContainerInstance) e).getContainer().equals(container)).count(); + long instanceNumber = getElements().stream().filter(e -> e instanceof ContainerInstance && ((ContainerInstance)e).getContainer().equals(container)).count(); instanceNumber++; - ContainerInstance containerInstance = new ContainerInstance(container, (int) instanceNumber, deploymentNode.getEnvironment()); + ContainerInstance containerInstance = new ContainerInstance(container, (int)instanceNumber, deploymentNode.getEnvironment()); containerInstance.setId(idGenerator.generateId(containerInstance)); - if (replicateContainerRelationships) { - // find all ContainerInstance objects in the same deployment environment - Set containerInstances = getElements().stream() - .filter(e -> e instanceof ContainerInstance && ((ContainerInstance) e).getEnvironment().equals(deploymentNode.getEnvironment())) - .map(e -> (ContainerInstance) e) - .collect(Collectors.toSet()); - - // and replicate the container-container relationships within the same deployment environment - for (ContainerInstance ci : containerInstances) { - Container c = ci.getContainer(); - - for (Relationship relationship : container.getRelationships()) { - if (relationship.getDestination().equals(c)) { - Relationship newRelationship = addRelationship(containerInstance, ci, relationship.getDescription(), relationship.getTechnology(), relationship.getInteractionStyle()); - if (newRelationship != null) { - newRelationship.setTags(null); - newRelationship.setLinkedRelationshipId(relationship.getId()); - } + if (replicateRelationships) { + replicateElementRelationships(deploymentNode.getEnvironment(), containerInstance); + } + + addElementToInternalStructures(containerInstance); + + return containerInstance; + } + + private void replicateElementRelationships(String deploymentEnvironment, StaticStructureElementInstance elementInstance) { + StaticStructureElement element = elementInstance.getElement(); + + // find all StaticStructureElementInstance objects in the same deployment environment + Set elementInstances = getElements().stream() + .filter(e -> e instanceof StaticStructureElementInstance && ((StaticStructureElementInstance)e).getEnvironment().equals(deploymentEnvironment)) + .map(e -> (StaticStructureElementInstance)e) + .collect(Collectors.toSet()); + + // and replicate the relationships within the same deployment environment + for (StaticStructureElementInstance ssei : elementInstances) { + StaticStructureElement sse = ssei.getElement(); + + for (Relationship relationship : element.getRelationships()) { + if (relationship.getDestination().equals(sse)) { + Relationship newRelationship = addRelationship(elementInstance, ssei, relationship.getDescription(), relationship.getTechnology(), relationship.getInteractionStyle()); + if (newRelationship != null) { + newRelationship.setTags(null); + newRelationship.setLinkedRelationshipId(relationship.getId()); } } + } - for (Relationship relationship : c.getRelationships()) { - if (relationship.getDestination().equals(container)) { - Relationship newRelationship = addRelationship(ci, containerInstance, relationship.getDescription(), relationship.getTechnology(), relationship.getInteractionStyle()); - if (newRelationship != null) { - newRelationship.setTags(null); - newRelationship.setLinkedRelationshipId(relationship.getId()); - } + for (Relationship relationship : sse.getRelationships()) { + if (relationship.getDestination().equals(element)) { + Relationship newRelationship = addRelationship(ssei, elementInstance, relationship.getDescription(), relationship.getTechnology(), relationship.getInteractionStyle()); + if (newRelationship != null) { + newRelationship.setTags(null); + newRelationship.setLinkedRelationshipId(relationship.getId()); } } } } - - addElementToInternalStructures(containerInstance); - - return containerInstance; } /** diff --git a/structurizr-core/src/com/structurizr/model/SoftwareSystemInstance.java b/structurizr-core/src/com/structurizr/model/SoftwareSystemInstance.java new file mode 100644 index 000000000..49e8dc3c4 --- /dev/null +++ b/structurizr-core/src/com/structurizr/model/SoftwareSystemInstance.java @@ -0,0 +1,54 @@ +package com.structurizr.model; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +/** + * Represents a deployment instance of a {@link SoftwareSystem}, which can be added to a {@link DeploymentNode}. + */ +public final class SoftwareSystemInstance extends StaticStructureElementInstance { + + private SoftwareSystem softwareSystem; + private String softwareSystemId; + + SoftwareSystemInstance() { + } + + SoftwareSystemInstance(SoftwareSystem softwareSystem, int instanceId, String environment) { + super(instanceId, environment); + + setSoftwareSystem(softwareSystem); + addTags(Tags.SOFTWARE_SYSTEM_INSTANCE); + } + + @JsonIgnore + public SoftwareSystem getSoftwareSystem() { + return softwareSystem; + } + + void setSoftwareSystem(SoftwareSystem softwareSystem) { + this.softwareSystem = softwareSystem; + } + + @Override + public StaticStructureElement getElement() { + return getSoftwareSystem(); + } + + /** + * Gets the ID of the software system that this object represents a deployment instance of. + * + * @return the software system ID, as a String + */ + public String getSoftwareSystemId() { + if (softwareSystem != null) { + return softwareSystem.getId(); + } else { + return softwareSystemId; + } + } + + void setSoftwareSystemId(String softwareSystemId) { + this.softwareSystemId = softwareSystemId; + } + +} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/model/StaticStructureElementInstance.java b/structurizr-core/src/com/structurizr/model/StaticStructureElementInstance.java new file mode 100644 index 000000000..89e1d9844 --- /dev/null +++ b/structurizr-core/src/com/structurizr/model/StaticStructureElementInstance.java @@ -0,0 +1,146 @@ +package com.structurizr.model; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.structurizr.util.Url; + +import javax.annotation.Nonnull; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * Represents a deployment instance of a {@link SoftwareSystem} or {@link Container}, which can be added to a {@link DeploymentNode}. + */ +public abstract class StaticStructureElementInstance extends DeploymentElement { + + private static final int DEFAULT_HEALTH_CHECK_INTERVAL_IN_SECONDS = 60; + private static final long DEFAULT_HEALTH_CHECK_TIMEOUT_IN_MILLISECONDS = 0; + + private int instanceId; + private Set healthChecks = new HashSet<>(); + + StaticStructureElementInstance() { + } + + StaticStructureElementInstance(int instanceId, String environment) { + setInstanceId(instanceId); + setEnvironment(environment); + } + + @JsonIgnore + public abstract StaticStructureElement getElement(); + + /** + * Gets the instance ID of this container. + * + * @return the instance ID, an integer greater than zero + */ + public int getInstanceId() { + return instanceId; + } + + void setInstanceId(int instanceId) { + this.instanceId = instanceId; + } + + @Override + @JsonIgnore + protected Set getRequiredTags() { + return Collections.emptySet(); + } + + @Override + public boolean removeTag(String tag) { + // do nothing ... tags cannot be removed from element instances (they should reflect the element they are based upon) + return false; + } + + @Override + @JsonIgnore + public String getCanonicalName() { + return getElement().getCanonicalName() + "[" + instanceId + "]"; + } + + @Override + @JsonIgnore + public Element getParent() { + return getElement().getParent(); + } + + @Override + @JsonIgnore + public String getName() { + return getElement().getName(); + } + + @Override + public void setName(String name) { + // no-op ... the name of an element instance is taken from the associated element + } + + /** + * Gets the set of health checks associated with this element instance. + * + * @return a Set of HttpHealthCheck instances + */ + @Nonnull + public Set getHealthChecks() { + return new HashSet<>(healthChecks); + } + + void setHealthChecks(Set healthChecks) { + this.healthChecks = healthChecks; + } + + /** + * Adds a new health check, with the default interval (60 seconds) and timeout (0 milliseconds). + * + * @param name the name of the health check + * @param url the URL of the health check + * @return a HttpHealthCheck instance representing the health check that has been added + * @throws IllegalArgumentException if the name is empty, or the URL is not a well-formed URL + */ + @Nonnull + public HttpHealthCheck addHealthCheck(String name, String url) { + return addHealthCheck(name, url, DEFAULT_HEALTH_CHECK_INTERVAL_IN_SECONDS, DEFAULT_HEALTH_CHECK_TIMEOUT_IN_MILLISECONDS); + } + + /** + * Adds a new health check. + * + * @param name the name of the health check + * @param url the URL of the health check + * @param interval the polling interval, in seconds + * @param timeout the timeout, in milliseconds + * @return a HttpHealthCheck instance representing the health check that has been added + * @throws IllegalArgumentException if the name is empty, the URL is not a well-formed URL, or the interval/timeout is not zero/a positive integer + */ + @Nonnull + public HttpHealthCheck addHealthCheck(String name, String url, int interval, long timeout) { + if (name == null || name.trim().length() == 0) { + throw new IllegalArgumentException("The name must not be null or empty."); + } + + if (url == null || url.trim().length() == 0) { + throw new IllegalArgumentException("The URL must not be null or empty."); + } + + if (!Url.isUrl(url)) { + throw new IllegalArgumentException(url + " is not a valid URL."); + } + + if (interval < 0) { + throw new IllegalArgumentException("The polling interval must be zero or a positive integer."); + } + + if (timeout < 0) { + throw new IllegalArgumentException("The timeout must be zero or a positive integer."); + } + + HttpHealthCheck healthCheck = new HttpHealthCheck(name, url, interval, timeout); + healthChecks.add(healthCheck); + + return healthCheck; + } + +} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/model/Tags.java b/structurizr-core/src/com/structurizr/model/Tags.java index 4f5e9d823..46cbdc487 100644 --- a/structurizr-core/src/com/structurizr/model/Tags.java +++ b/structurizr-core/src/com/structurizr/model/Tags.java @@ -12,6 +12,7 @@ public class Tags { public static final String DEPLOYMENT_NODE = "Deployment Node"; public static final String INFRASTRUCTURE_NODE = "Infrastructure Node"; + public static final String SOFTWARE_SYSTEM_INSTANCE = "Software System Instance"; public static final String CONTAINER_INSTANCE = "Container Instance"; /** diff --git a/structurizr-core/src/com/structurizr/view/DeploymentView.java b/structurizr-core/src/com/structurizr/view/DeploymentView.java index 2e2982551..657bd7884 100644 --- a/structurizr-core/src/com/structurizr/view/DeploymentView.java +++ b/structurizr-core/src/com/structurizr/view/DeploymentView.java @@ -82,7 +82,7 @@ public void add(@Nonnull DeploymentNode deploymentNode, boolean addRelationships throw new IllegalArgumentException("A deployment node must be specified."); } - if (addContainerInstancesAndDeploymentNodesAndInfrastructureNodes(deploymentNode, addRelationships)) { + if (addElementInstancesAndDeploymentNodesAndInfrastructureNodes(deploymentNode, addRelationships)) { Element parent = deploymentNode.getParent(); while (parent != null) { addElement(parent, addRelationships); @@ -130,30 +130,35 @@ public void remove(@Nonnull ContainerInstance containerInstance) { removeElement(containerInstance); } - private boolean addContainerInstancesAndDeploymentNodesAndInfrastructureNodes(DeploymentNode deploymentNode, boolean addRelationships) { - boolean hasContainersOrInfrastructureNodes = false; + private boolean addElementInstancesAndDeploymentNodesAndInfrastructureNodes(DeploymentNode deploymentNode, boolean addRelationships) { + boolean hasElementsOrInfrastructureNodes = false; + for (SoftwareSystemInstance softwareSystemInstance : deploymentNode.getSoftwareSystemInstances()) { + addElement(softwareSystemInstance, addRelationships); + hasElementsOrInfrastructureNodes = true; + } + for (ContainerInstance containerInstance : deploymentNode.getContainerInstances()) { Container container = containerInstance.getContainer(); if (getSoftwareSystem() == null || container.getParent().equals(getSoftwareSystem())) { addElement(containerInstance, addRelationships); - hasContainersOrInfrastructureNodes = true; + hasElementsOrInfrastructureNodes = true; } } for (InfrastructureNode infrastructureNode : deploymentNode.getInfrastructureNodes()) { addElement(infrastructureNode, addRelationships); - hasContainersOrInfrastructureNodes = true; + hasElementsOrInfrastructureNodes = true; } for (DeploymentNode child : deploymentNode.getChildren()) { - hasContainersOrInfrastructureNodes = hasContainersOrInfrastructureNodes | addContainerInstancesAndDeploymentNodesAndInfrastructureNodes(child, addRelationships); + hasElementsOrInfrastructureNodes = hasElementsOrInfrastructureNodes | addElementInstancesAndDeploymentNodesAndInfrastructureNodes(child, addRelationships); } - if (hasContainersOrInfrastructureNodes) { + if (hasElementsOrInfrastructureNodes) { addElement(deploymentNode, addRelationships); } - return hasContainersOrInfrastructureNodes; + return hasElementsOrInfrastructureNodes; } /** diff --git a/structurizr-core/test/unit/com/structurizr/model/ContainerInstanceTests.java b/structurizr-core/test/unit/com/structurizr/model/ContainerInstanceTests.java index d5a80c574..8c5d14d6b 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ContainerInstanceTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ContainerInstanceTests.java @@ -34,10 +34,10 @@ public void test_getContainerId() { public void test_getName() { ContainerInstance containerInstance = deploymentNode.add(database); - assertEquals("Database Schema (container instance)", containerInstance.getName()); + assertEquals("Database Schema", containerInstance.getName()); containerInstance.setName("foo"); - assertEquals("Database Schema (container instance)", containerInstance.getName()); + assertEquals("Database Schema", containerInstance.getName()); } @Test diff --git a/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java b/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java index 80b131e35..a3a2bfcd5 100644 --- a/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java @@ -48,11 +48,22 @@ public void test_getTags() { assertEquals("Element,Deployment Node,Tag 1,Tag 2", deploymentNode.getTags()); } + @Test + public void test_add_ThrowsAnException_WhenASoftwareSystemIsNotSpecified() { + try { + DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node", "Description", "Technology"); + deploymentNode.add((SoftwareSystem) null); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("A software system must be specified.", iae.getMessage()); + } + } + @Test public void test_add_ThrowsAnException_WhenAContainerIsNotSpecified() { try { DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node", "Description", "Technology"); - deploymentNode.add(null); + deploymentNode.add((Container)null); fail(); } catch (IllegalArgumentException iae) { assertEquals("A container must be specified.", iae.getMessage()); diff --git a/structurizr-core/test/unit/com/structurizr/model/ModelTests.java b/structurizr-core/test/unit/com/structurizr/model/ModelTests.java index bf7335e7b..4dd993871 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ModelTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ModelTests.java @@ -585,11 +585,22 @@ public void test_addImplicitRelationships_WhenSourceAndDestinationAreComponentsI assertTrue(aa1.hasEfferentRelationshipWith(aa2)); } + @Test + public void test_addSoftwareSystemInstance_ThrowsAnException_WhenANullSoftwareSystemIsSpecified() { + try { + DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node", "Description", "Technology"); + deploymentNode.add((SoftwareSystem) null); + fail(); + } catch (Exception e) { + assertEquals("A software system must be specified.", e.getMessage()); + } + } + @Test public void test_addContainerInstance_ThrowsAnException_WhenANullContainerIsSpecified() { try { DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node", "Description", "Technology"); - deploymentNode.add(null); + deploymentNode.add((Container)null); fail(); } catch (Exception e) { assertEquals("A container must be specified.", e.getMessage()); @@ -617,7 +628,7 @@ public void test_addDeploymentNode_AddsADeploymentNode_WhenADeploymentEnvironmen } @Test - public void test_addContainerInstance_AddsAContainerInstanceAndReplicatesRelationshipsWithinTheDeploymentEnvironment_WhenAContainerIsSpecified() { + public void test_addElementInstance_AddsElementInstancesAndReplicatesRelationshipsWithinTheDeploymentEnvironment() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1", "Description"); Container container1 = softwareSystem1.addContainer("Container 1", "Description", "Technology"); @@ -627,19 +638,24 @@ public void test_addContainerInstance_AddsAContainerInstanceAndReplicatesRelatio SoftwareSystem softwareSystem3 = model.addSoftwareSystem("Software System 3", "Description"); Container container3 = softwareSystem3.addContainer("Container 3", "Description", "Technology"); + SoftwareSystem softwareSystem4 = model.addSoftwareSystem("Software System 4", "Description"); + container1.uses(container2, "Uses 1", "Technology 1", InteractionStyle.Synchronous); container2.uses(container3, "Uses 2", "Technology 2", InteractionStyle.Asynchronous); + container3.uses(softwareSystem4, "Uses"); DeploymentNode developmentDeploymentNode = model.addDeploymentNode("Development", "Deployment Node", "Description", "Technology"); ContainerInstance containerInstance1 = developmentDeploymentNode.add(container1); ContainerInstance containerInstance2 = developmentDeploymentNode.add(container2); ContainerInstance containerInstance3 = developmentDeploymentNode.add(container3); + SoftwareSystemInstance softwareSystemInstance = developmentDeploymentNode.add(softwareSystem4); - // the following live container instances should not affect the relationships of the development container instances + // the following live element instances should not affect the relationships of the development element instances DeploymentNode liveDeploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); liveDeploymentNode.add(container1); liveDeploymentNode.add(container2); liveDeploymentNode.add(container3); + liveDeploymentNode.add(softwareSystem4); assertSame(container2, containerInstance2.getContainer()); assertEquals(container2.getId(), containerInstance2.getContainerId()); @@ -665,10 +681,19 @@ public void test_addContainerInstance_AddsAContainerInstanceAndReplicatesRelatio assertEquals("Technology 2", relationship.getTechnology()); assertEquals(InteractionStyle.Asynchronous, relationship.getInteractionStyle()); assertEquals("", relationship.getTags()); + + assertEquals(1, containerInstance3.getRelationships().size()); + relationship = containerInstance3.getRelationships().iterator().next(); + assertSame(containerInstance3, relationship.getSource()); + assertSame(softwareSystemInstance, relationship.getDestination()); + assertEquals("Uses", relationship.getDescription()); + assertNull(relationship.getTechnology()); + assertNull(relationship.getInteractionStyle()); + assertEquals("", relationship.getTags()); } @Test - public void test_addContainerInstance_AddsAContainerInstanceAndDoesNotReplicateRelationships_WhenAContainerIsSpecified() { + public void test_addContainerInstance_AddsAContainerInstanceAndDoesNotReplicateRelationships() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1", "Description"); Container container1 = softwareSystem1.addContainer("Container 1", "Description", "Technology"); diff --git a/structurizr-examples/src/com/structurizr/example/BigBankPlc.java b/structurizr-examples/src/com/structurizr/example/BigBankPlc.java index 3cbecf3f1..82e80b610 100644 --- a/structurizr-examples/src/com/structurizr/example/BigBankPlc.java +++ b/structurizr-examples/src/com/structurizr/example/BigBankPlc.java @@ -109,6 +109,9 @@ public static void main(String[] args) throws Exception { ContainerInstance developmentWebApplication = apacheTomcat.add(webApplication); ContainerInstance developmentApiApplication = apacheTomcat.add(apiApplication); + DeploymentNode bigBankDataCenterForDevelopment = model.addDeploymentNode("Development", "Big Bank plc", "", "Big Bank plc data center"); + bigBankDataCenterForDevelopment.addDeploymentNode("bigbank-dev001").add(mainframeBankingSystem); + ContainerInstance developmentDatabase = developerLaptop.addDeploymentNode("Docker Container - Database Server", "A Docker container.", "Docker") .addDeploymentNode("Database Server", "A development database.", "Oracle 12c") .add(database); @@ -121,21 +124,22 @@ public static void main(String[] args) throws Exception { DeploymentNode customerComputer = model.addDeploymentNode("Live", "Customer's computer", "", "Microsoft Windows or Apple macOS"); ContainerInstance liveSinglePageApplication = customerComputer.addDeploymentNode("Web Browser", "", "Chrome, Firefox, Safari, or Edge").add(singlePageApplication); - DeploymentNode bigBankDataCenter = model.addDeploymentNode("Live", "Big Bank plc", "", "Big Bank plc data center"); + DeploymentNode bigBankDataCenterForLive = model.addDeploymentNode("Live", "Big Bank plc", "", "Big Bank plc data center"); + bigBankDataCenterForLive.addDeploymentNode("bigbank-prod001").add(mainframeBankingSystem); - DeploymentNode liveWebServer = bigBankDataCenter.addDeploymentNode("bigbank-web***", "A web server residing in the web server farm, accessed via F5 BIG-IP LTMs.", "Ubuntu 16.04 LTS", 4, MapUtils.create("Location=London and Reading")); + DeploymentNode liveWebServer = bigBankDataCenterForLive.addDeploymentNode("bigbank-web***", "A web server residing in the web server farm, accessed via F5 BIG-IP LTMs.", "Ubuntu 16.04 LTS", 4, MapUtils.create("Location=London and Reading")); ContainerInstance liveWebApplication = liveWebServer.addDeploymentNode("Apache Tomcat", "An open source Java EE web server.", "Apache Tomcat 8.x", 1, MapUtils.create("Xmx=512M", "Xms=1024M", "Java Version=8")) .add(webApplication); - DeploymentNode liveApiServer = bigBankDataCenter.addDeploymentNode("bigbank-api***", "A web server residing in the web server farm, accessed via F5 BIG-IP LTMs.", "Ubuntu 16.04 LTS", 8, MapUtils.create("Location=London and Reading")); + DeploymentNode liveApiServer = bigBankDataCenterForLive.addDeploymentNode("bigbank-api***", "A web server residing in the web server farm, accessed via F5 BIG-IP LTMs.", "Ubuntu 16.04 LTS", 8, MapUtils.create("Location=London and Reading")); ContainerInstance liveApiApplication = liveApiServer.addDeploymentNode("Apache Tomcat", "An open source Java EE web server.", "Apache Tomcat 8.x", 1, MapUtils.create("Xmx=512M", "Xms=1024M", "Java Version=8")) .add(apiApplication); - DeploymentNode primaryDatabaseServer = bigBankDataCenter.addDeploymentNode("bigbank-db01", "The primary database server.", "Ubuntu 16.04 LTS", 1, MapUtils.create("Location=London")) + DeploymentNode primaryDatabaseServer = bigBankDataCenterForLive.addDeploymentNode("bigbank-db01", "The primary database server.", "Ubuntu 16.04 LTS", 1, MapUtils.create("Location=London")) .addDeploymentNode("Oracle - Primary", "The primary, live database server.", "Oracle 12c"); ContainerInstance livePrimaryDatabase = primaryDatabaseServer.add(database); - DeploymentNode bigBankdb02 = bigBankDataCenter.addDeploymentNode("bigbank-db02", "The secondary database server.", "Ubuntu 16.04 LTS", 1, MapUtils.create("Location=Reading")); + DeploymentNode bigBankdb02 = bigBankDataCenterForLive.addDeploymentNode("bigbank-db02", "The secondary database server.", "Ubuntu 16.04 LTS", 1, MapUtils.create("Location=Reading")); bigBankdb02.addTags(FAILOVER_TAG); DeploymentNode secondaryDatabaseServer = bigBankdb02.addDeploymentNode("Oracle - Secondary", "A secondary, standby database server, used for failover purposes only.", "Oracle 12c"); secondaryDatabaseServer.addTags(FAILOVER_TAG); @@ -202,6 +206,7 @@ public static void main(String[] args) throws Exception { DeploymentView developmentDeploymentView = views.createDeploymentView(internetBankingSystem, "DevelopmentDeployment", "An example development deployment scenario for the Internet Banking System."); developmentDeploymentView.setEnvironment("Development"); developmentDeploymentView.add(developerLaptop); + developmentDeploymentView.add(bigBankDataCenterForDevelopment); developmentDeploymentView.setPaperSize(PaperSize.A5_Landscape); developmentDeploymentView.addAnimation(developmentSinglePageApplication); @@ -210,7 +215,7 @@ public static void main(String[] args) throws Exception { DeploymentView liveDeploymentView = views.createDeploymentView(internetBankingSystem, "LiveDeployment", "An example live deployment scenario for the Internet Banking System."); liveDeploymentView.setEnvironment("Live"); - liveDeploymentView.add(bigBankDataCenter); + liveDeploymentView.add(bigBankDataCenterForLive); liveDeploymentView.add(customerMobileDevice); liveDeploymentView.add(customerComputer); liveDeploymentView.add(dataReplicationRelationship); From 43609cc7d0f5f2227b5220728fc041b0fa59c1e6 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 3 Aug 2020 22:23:41 +0100 Subject: [PATCH 168/717] Prevents failing when enums can't be deserialized. --- .../src/com/structurizr/io/json/AbstractJsonReader.java | 1 + 1 file changed, 1 insertion(+) diff --git a/structurizr-client/src/com/structurizr/io/json/AbstractJsonReader.java b/structurizr-client/src/com/structurizr/io/json/AbstractJsonReader.java index 5d2405b00..89a099ed7 100644 --- a/structurizr-client/src/com/structurizr/io/json/AbstractJsonReader.java +++ b/structurizr-client/src/com/structurizr/io/json/AbstractJsonReader.java @@ -9,6 +9,7 @@ ObjectMapper createObjectMapper() { ObjectMapper objectMapper = new ObjectMapper(); objectMapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT); objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + objectMapper.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true); return objectMapper; } From e69e59d9fcc58c74272647a31cf29f8b2d51bb58 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 4 Aug 2020 09:49:07 +0100 Subject: [PATCH 169/717] Adds support for software system instances in animation steps. --- .../com/structurizr/view/DeploymentView.java | 26 +++++++++---------- .../structurizr/view/DeploymentViewTests.java | 8 +++--- .../com/structurizr/example/BigBankPlc.java | 13 +++++++--- 3 files changed, 26 insertions(+), 21 deletions(-) diff --git a/structurizr-core/src/com/structurizr/view/DeploymentView.java b/structurizr-core/src/com/structurizr/view/DeploymentView.java index 657bd7884..b77f78968 100644 --- a/structurizr-core/src/com/structurizr/view/DeploymentView.java +++ b/structurizr-core/src/com/structurizr/view/DeploymentView.java @@ -229,32 +229,32 @@ public void addAnimation(InfrastructureNode... infrastructureNodes) { } /** - * Adds an animation step, with the specified container instances. + * Adds an animation step, with the specified element instances. * - * @param containerInstances the container instances that should be shown in the animation step + * @param elementInstances the element instances that should be shown in the animation step */ - public void addAnimation(ContainerInstance... containerInstances) { - if (containerInstances == null || containerInstances.length == 0) { - throw new IllegalArgumentException("One or more container instances must be specified."); + public void addAnimation(StaticStructureElementInstance... elementInstances) { + if (elementInstances == null || elementInstances.length == 0) { + throw new IllegalArgumentException("One or more software system/container instances must be specified."); } - addAnimation(containerInstances, new InfrastructureNode[0]); + addAnimation(elementInstances, new InfrastructureNode[0]); } /** * Adds an animation step, with the specified container instances and infrastructure nodes. * - * @param containerInstances the container instances that should be shown in the animation step - * @param infrastructureNodes the container infrastructure nodes that should be shown in the animation step + * @param elementInstances the element instances that should be shown in the animation step + * @param infrastructureNodes the container infrastructure nodes that should be shown in the animation step */ - public void addAnimation(ContainerInstance[] containerInstances, InfrastructureNode[] infrastructureNodes) { - if ((containerInstances == null || containerInstances.length == 0) && (infrastructureNodes == null || infrastructureNodes.length == 0)) { - throw new IllegalArgumentException("One or more container instances/infrastructure nodes must be specified."); + public void addAnimation(StaticStructureElementInstance[] elementInstances, InfrastructureNode[] infrastructureNodes) { + if ((elementInstances == null || elementInstances.length == 0) && (infrastructureNodes == null || infrastructureNodes.length == 0)) { + throw new IllegalArgumentException("One or more software system/container instances and/or infrastructure nodes must be specified."); } List elements = new ArrayList<>(); - if (containerInstances != null) { - Collections.addAll(elements, containerInstances); + if (elementInstances != null) { + Collections.addAll(elements, elementInstances); } if (infrastructureNodes != null) { Collections.addAll(elements, infrastructureNodes); diff --git a/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java b/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java index 13be7cee4..64097a1d4 100644 --- a/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java @@ -165,13 +165,13 @@ public void test_addDeploymentNode_AddsTheParentToo() { } @Test - public void test_addAnimationStep_ThrowsAnException_WhenNoContainerInstancesAreSpecified() { + public void test_addAnimationStep_ThrowsAnException_WhenNoElementInstancesAreSpecified() { try { deploymentView = views.createDeploymentView("deployment", "Description"); deploymentView.addAnimation((ContainerInstance[])null); fail(); } catch (IllegalArgumentException iae) { - assertEquals("One or more container instances must be specified.", iae.getMessage()); + assertEquals("One or more software system/container instances must be specified.", iae.getMessage()); } } @@ -187,13 +187,13 @@ public void test_addAnimationStep_ThrowsAnException_WhenNoInfrastructureNodesAre } @Test - public void test_addAnimationStep_ThrowsAnException_WhenNoContainerInstancesOrInfrastructureNodesAreSpecified() { + public void test_addAnimationStep_ThrowsAnException_WhenNoElementInstancesOrInfrastructureNodesAreSpecified() { try { deploymentView = views.createDeploymentView("deployment", "Description"); deploymentView.addAnimation(null, null); fail(); } catch (IllegalArgumentException iae) { - assertEquals("One or more container instances/infrastructure nodes must be specified.", iae.getMessage()); + assertEquals("One or more software system/container instances and/or infrastructure nodes must be specified.", iae.getMessage()); } } diff --git a/structurizr-examples/src/com/structurizr/example/BigBankPlc.java b/structurizr-examples/src/com/structurizr/example/BigBankPlc.java index 82e80b610..ef586ee01 100644 --- a/structurizr-examples/src/com/structurizr/example/BigBankPlc.java +++ b/structurizr-examples/src/com/structurizr/example/BigBankPlc.java @@ -110,7 +110,7 @@ public static void main(String[] args) throws Exception { ContainerInstance developmentApiApplication = apacheTomcat.add(apiApplication); DeploymentNode bigBankDataCenterForDevelopment = model.addDeploymentNode("Development", "Big Bank plc", "", "Big Bank plc data center"); - bigBankDataCenterForDevelopment.addDeploymentNode("bigbank-dev001").add(mainframeBankingSystem); + SoftwareSystemInstance developmentMainframeBankingSystem = bigBankDataCenterForDevelopment.addDeploymentNode("bigbank-dev001").add(mainframeBankingSystem); ContainerInstance developmentDatabase = developerLaptop.addDeploymentNode("Docker Container - Database Server", "A Docker container.", "Docker") .addDeploymentNode("Database Server", "A development database.", "Oracle 12c") @@ -125,7 +125,7 @@ public static void main(String[] args) throws Exception { ContainerInstance liveSinglePageApplication = customerComputer.addDeploymentNode("Web Browser", "", "Chrome, Firefox, Safari, or Edge").add(singlePageApplication); DeploymentNode bigBankDataCenterForLive = model.addDeploymentNode("Live", "Big Bank plc", "", "Big Bank plc data center"); - bigBankDataCenterForLive.addDeploymentNode("bigbank-prod001").add(mainframeBankingSystem); + SoftwareSystemInstance liveMainframeBankingSystem = bigBankDataCenterForLive.addDeploymentNode("bigbank-prod001").add(mainframeBankingSystem); DeploymentNode liveWebServer = bigBankDataCenterForLive.addDeploymentNode("bigbank-web***", "A web server residing in the web server farm, accessed via F5 BIG-IP LTMs.", "Ubuntu 16.04 LTS", 4, MapUtils.create("Location=London and Reading")); ContainerInstance liveWebApplication = liveWebServer.addDeploymentNode("Apache Tomcat", "An open source Java EE web server.", "Apache Tomcat 8.x", 1, MapUtils.create("Xmx=512M", "Xms=1024M", "Java Version=8")) @@ -199,8 +199,11 @@ public static void main(String[] args) throws Exception { // dynamic diagrams and deployment diagrams are not available with the Free Plan DynamicView dynamicView = views.createDynamicView(apiApplication, "SignIn", "Summarises how the sign in feature works in the single-page application."); dynamicView.add(singlePageApplication, "Submits credentials to", signinController); - dynamicView.add(signinController, "Calls isAuthenticated() on", securityComponent); + dynamicView.add(signinController, "Validates credentials using", securityComponent); dynamicView.add(securityComponent, "select * from users where username = ?", database); + dynamicView.add(database, "Returns user data to", securityComponent); + dynamicView.add(securityComponent, "Returns true if the hashed password matches", signinController); + dynamicView.add(signinController, "Sends back an authentication token to", singlePageApplication); dynamicView.setPaperSize(PaperSize.A5_Landscape); DeploymentView developmentDeploymentView = views.createDeploymentView(internetBankingSystem, "DevelopmentDeployment", "An example development deployment scenario for the Internet Banking System."); @@ -212,6 +215,7 @@ public static void main(String[] args) throws Exception { developmentDeploymentView.addAnimation(developmentSinglePageApplication); developmentDeploymentView.addAnimation(developmentWebApplication, developmentApiApplication); developmentDeploymentView.addAnimation(developmentDatabase); + developmentDeploymentView.addAnimation(developmentMainframeBankingSystem); DeploymentView liveDeploymentView = views.createDeploymentView(internetBankingSystem, "LiveDeployment", "An example live deployment scenario for the Internet Banking System."); liveDeploymentView.setEnvironment("Live"); @@ -219,13 +223,14 @@ public static void main(String[] args) throws Exception { liveDeploymentView.add(customerMobileDevice); liveDeploymentView.add(customerComputer); liveDeploymentView.add(dataReplicationRelationship); - liveDeploymentView.setPaperSize(PaperSize.A5_Landscape); + liveDeploymentView.setPaperSize(PaperSize.A4_Landscape); liveDeploymentView.addAnimation(liveSinglePageApplication); liveDeploymentView.addAnimation(liveMobileApp); liveDeploymentView.addAnimation(liveWebApplication, liveApiApplication); liveDeploymentView.addAnimation(livePrimaryDatabase); liveDeploymentView.addAnimation(liveSecondaryDatabase); + liveDeploymentView.addAnimation(liveMainframeBankingSystem); // colours, shapes and other diagram styling Styles styles = views.getConfiguration().getStyles(); From fd8e0ca8952f71e20aee0c688dea3849e11b9e5e Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 4 Aug 2020 13:41:23 +0100 Subject: [PATCH 170/717] Updated to reflect release date. --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 47b9121d6..eb7a97190 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 1.5.0 (unreleased) +## 1.5.0 (4th August 2020) - Fixes #151: linked relationship tags were not being taken into account when finding relationship styling. - Fixes #153: Allow relationships in DynamicView to go both ways without two relationships between Elements in Model. From 6342e2d14aac25b75f4ffcaef3bed24a257e64ea Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 4 Aug 2020 13:49:52 +0100 Subject: [PATCH 171/717] Added #150. --- docs/changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.md b/docs/changelog.md index eb7a97190..a4eca65f4 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -4,6 +4,7 @@ - Fixes #151: linked relationship tags were not being taken into account when finding relationship styling. - Fixes #153: Allow relationships in DynamicView to go both ways without two relationships between Elements in Model. +- Adds support for software system instances on deployment views (#150: how do I provide tech details for an external system to show in Deployment View?) - The interaction style on relationships no longer defaults to Synchronous. - Adds support for software system instances on deployment views. From e2cfe353ac0cefc964a4d8b650d4f7d927c4a1f2 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 7 Sep 2020 13:05:48 +0100 Subject: [PATCH 172/717] Fixed broken link. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bcc30c04c..3851db046 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ The view can then be exported to be visualised using the [Structurizr cloud serv * [Styling elements](docs/styling-elements.md) * [Styling relationships](docs/styling-relationships.md) * [Filtered views](docs/filtered-views.md) - * [Graphviz automatic layout](https://github.com/structurizr/java-extensions/blob/master/docs/graphviz.md) + * [Graphviz automatic layout](https://github.com/structurizr/java-extensions/blob/master/structurizr-graphviz) * Documentation * [Documentation overview](docs/documentation.md) * [Structurizr](docs/documentation-structurizr.md) From cfbf3e82fe0d34f50fdd54bdbd6b5d31ce53e368 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 15 Sep 2020 15:38:56 +0100 Subject: [PATCH 173/717] Some changes to support setting URLs and properties from the Structurizr DSL. --- .../src/com/structurizr/model/Element.java | 26 ---------------- .../src/com/structurizr/model/ModelItem.java | 30 ++++++++++++++++++- .../com/structurizr/model/Relationship.java | 26 ---------------- 3 files changed, 29 insertions(+), 53 deletions(-) diff --git a/structurizr-core/src/com/structurizr/model/Element.java b/structurizr-core/src/com/structurizr/model/Element.java index 6d6efd6b1..97b013477 100644 --- a/structurizr-core/src/com/structurizr/model/Element.java +++ b/structurizr-core/src/com/structurizr/model/Element.java @@ -16,7 +16,6 @@ public abstract class Element extends ModelItem { private String name; private String description; - private String url; private Set relationships = new LinkedHashSet<>(); @@ -54,31 +53,6 @@ void setName(String name) { this.name = name; } - /** - * Gets the URL where more information about this element can be found. - * - * @return a URL as a String - */ - public String getUrl() { - return url; - } - - /** - * Sets the URL where more information about this element can be found. - * - * @param url the URL as a String - * @throws IllegalArgumentException if the URL is not a well-formed URL - */ - public void setUrl(String url) { - if (url != null && url.trim().length() > 0) { - if (Url.isUrl(url)) { - this.url = url; - } else { - throw new IllegalArgumentException(url + " is not a valid URL."); - } - } - } - @JsonIgnore public abstract String getCanonicalName(); diff --git a/structurizr-core/src/com/structurizr/model/ModelItem.java b/structurizr-core/src/com/structurizr/model/ModelItem.java index 617a7b5d5..928f9f3e4 100644 --- a/structurizr-core/src/com/structurizr/model/ModelItem.java +++ b/structurizr-core/src/com/structurizr/model/ModelItem.java @@ -2,17 +2,20 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.structurizr.util.StringUtils; +import com.structurizr.util.Url; import java.util.*; /** * The base class for elements and relationships. */ -abstract class ModelItem { +public abstract class ModelItem { private String id = ""; private String originId = ""; private Set tags = new LinkedHashSet<>(); + + private String url; private Map properties = new HashMap<>(); private Set perspectives = new HashSet<>(); @@ -117,6 +120,31 @@ public boolean hasTag(String tag) { return getTagsAsSet().contains(tag.trim()); } + /** + * Gets the URL where more information about this item can be found. + * + * @return a URL as a String + */ + public String getUrl() { + return url; + } + + /** + * Sets the URL where more information about this item can be found. + * + * @param url the URL as a String + * @throws IllegalArgumentException if the URL is not a well-formed URL + */ + public void setUrl(String url) { + if (url != null && url.trim().length() > 0) { + if (Url.isUrl(url)) { + this.url = url; + } else { + throw new IllegalArgumentException(url + " is not a valid URL."); + } + } + } + /** * Gets the collection of name-value property pairs associated with this element, as a Map. * diff --git a/structurizr-core/src/com/structurizr/model/Relationship.java b/structurizr-core/src/com/structurizr/model/Relationship.java index b01f7cc8f..e70a514df 100644 --- a/structurizr-core/src/com/structurizr/model/Relationship.java +++ b/structurizr-core/src/com/structurizr/model/Relationship.java @@ -21,7 +21,6 @@ public final class Relationship extends ModelItem { private String description; private String technology; private InteractionStyle interactionStyle; - private String url; private String linkedRelationshipId; @@ -163,31 +162,6 @@ protected Set getRequiredTags() { } } - /** - * Gets the URL where more information about this relationship can be found. - * - * @return a URL as a String - */ - public String getUrl() { - return url; - } - - /** - * Sets the URL where more information about this relationship can be found. - * - * @param url the URL as a String - * @throws IllegalArgumentException if the URL is not a well-formed URL - */ - public void setUrl(String url) { - if (url != null && url.trim().length() > 0) { - if (Url.isUrl(url)) { - this.url = url; - } else { - throw new IllegalArgumentException(url + " is not a valid URL."); - } - } - } - @Override public String toString() { return source.toString() + " ---[" + description + "]---> " + destination.toString(); From ec0bddfd878a59620a996f40c77c33b48264c894 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 15 Sep 2020 15:45:16 +0100 Subject: [PATCH 174/717] Don't default interaction style to synchronous. --- structurizr-core/src/com/structurizr/model/DeploymentNode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structurizr-core/src/com/structurizr/model/DeploymentNode.java b/structurizr-core/src/com/structurizr/model/DeploymentNode.java index 069770ebe..aa2ac7a73 100644 --- a/structurizr-core/src/com/structurizr/model/DeploymentNode.java +++ b/structurizr-core/src/com/structurizr/model/DeploymentNode.java @@ -219,7 +219,7 @@ public InfrastructureNode addInfrastructureNode(String name, String description, * @return a Relationship object */ public Relationship uses(DeploymentNode destination, String description, String technology) { - return uses(destination, description, technology, InteractionStyle.Synchronous); + return uses(destination, description, technology, null); } /** From f435d5b53d36c09e06ed88e990077f6a151e543a Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 15 Sep 2020 15:49:12 +0100 Subject: [PATCH 175/717] Adds the ability to create relationships from software system/container instances to infrastructure nodes. --- .../model/StaticStructureElementInstance.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/structurizr-core/src/com/structurizr/model/StaticStructureElementInstance.java b/structurizr-core/src/com/structurizr/model/StaticStructureElementInstance.java index 89e1d9844..bdd38f397 100644 --- a/structurizr-core/src/com/structurizr/model/StaticStructureElementInstance.java +++ b/structurizr-core/src/com/structurizr/model/StaticStructureElementInstance.java @@ -143,4 +143,43 @@ public HttpHealthCheck addHealthCheck(String name, String url, int interval, lon return healthCheck; } + /** + * Adds a relationship between this element instance and an infrastructure node. + * + * @param destination the destination InfrastructureNode + * @param description a short description of the relationship + * @param technology the technology + * @return a Relationship object + */ + public Relationship uses(InfrastructureNode destination, String description, String technology) { + return uses(destination, description, technology, null); + } + + /** + * Adds a relationship between this element instance and an infrastructure node. + * + * @param destination the destination InfrastructureNode + * @param description a short description of the relationship + * @param technology the technology + * @param interactionStyle the interaction style (Synchronous vs Asynchronous) + * @return a Relationship object + */ + public Relationship uses(InfrastructureNode destination, String description, String technology, InteractionStyle interactionStyle) { + return uses(destination, description, technology, interactionStyle, new String[0]); + } + + /** + * Adds a relationship between this element instance and an infrastructure node. + * + * @param destination the destination InfrastructureNode + * @param description a short description of the relationship + * @param technology the technology + * @param interactionStyle the interaction style (Synchronous vs Asynchronous) + * @param tags an array of tags + * @return a Relationship object + */ + public Relationship uses(InfrastructureNode destination, String description, String technology, InteractionStyle interactionStyle, String[] tags) { + return getModel().addRelationship(this, destination, description, technology, interactionStyle, tags); + } + } \ No newline at end of file From 344eea55b0ae6de8a5d94b62068324abca0000c0 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 17 Sep 2020 11:26:48 +0100 Subject: [PATCH 176/717] Changed the way that internal canonical element names are generated, to improve layout merging for deployment views. --- build.gradle | 2 +- docs/changelog.md | 6 + .../api/WorkspaceRulesValidationTests.java | 2 +- .../src/com/structurizr/Workspace.java | 8 +- .../model/CanonicalNameGenerator.java | 81 ++++++++ .../src/com/structurizr/model/Component.java | 2 +- .../src/com/structurizr/model/Container.java | 2 +- .../structurizr/model/ContainerInstance.java | 6 + .../structurizr/model/DeploymentElement.java | 18 ++ .../com/structurizr/model/DeploymentNode.java | 22 +-- .../structurizr/model/InfrastructureNode.java | 17 +- .../model/MessageDigestIdGenerator.java | 78 -------- .../src/com/structurizr/model/Model.java | 13 +- .../model/NormalizedNameIdGenerator.java | 71 ------- .../src/com/structurizr/model/Person.java | 2 +- .../com/structurizr/model/SoftwareSystem.java | 2 +- .../model/SoftwareSystemInstance.java | 8 +- .../model/StaticStructureElementInstance.java | 12 -- .../com/structurizr/model/ComponentTests.java | 8 +- .../model/ContainerInstanceTests.java | 90 ++++----- .../com/structurizr/model/ContainerTests.java | 8 +- .../model/DeploymentNodeTests.java | 5 +- .../model/InfrastructureNodeTests.java | 41 ++++ .../model/MessageDigestIdGeneratorTests.java | 104 ---------- .../com/structurizr/model/ModelTests.java | 55 ++++-- .../model/NormalizedNameIdGeneratorTests.java | 102 ---------- .../com/structurizr/model/PersonTests.java | 8 +- .../model/SoftwareSystemInstanceTests.java | 180 ++++++++++++++++++ .../model/SoftwareSystemTests.java | 8 +- 29 files changed, 457 insertions(+), 504 deletions(-) create mode 100644 structurizr-core/src/com/structurizr/model/CanonicalNameGenerator.java delete mode 100644 structurizr-core/src/com/structurizr/model/MessageDigestIdGenerator.java delete mode 100644 structurizr-core/src/com/structurizr/model/NormalizedNameIdGenerator.java create mode 100644 structurizr-core/test/unit/com/structurizr/model/InfrastructureNodeTests.java delete mode 100644 structurizr-core/test/unit/com/structurizr/model/MessageDigestIdGeneratorTests.java delete mode 100644 structurizr-core/test/unit/com/structurizr/model/NormalizedNameIdGeneratorTests.java create mode 100644 structurizr-core/test/unit/com/structurizr/model/SoftwareSystemInstanceTests.java diff --git a/build.gradle b/build.gradle index e45436344..8a0fad864 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.5.0' + version = '1.6.0' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index a4eca65f4..da1473501 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,11 @@ # Changelog +## 1.6.0 (unreleased) + +- Changed the way that internal canonical element names are generated, to improve layout merging for deployment views. +- getParent() of SoftwareSystemInstance and ContainerInstance now returns the parent deployment node. +- Refactoring and bug fixes. + ## 1.5.0 (4th August 2020) - Fixes #151: linked relationship tags were not being taken into account when finding relationship styling. diff --git a/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java b/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java index d751a11bd..d998cdb77 100644 --- a/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java +++ b/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java @@ -18,7 +18,7 @@ public void test_exceptionThrown_WhenElementIdsAreNotUnique() throws Exception { WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "ElementIdsAreNotUnique.json")); fail(); } catch (WorkspaceValidationException we) { - assertTrue(we.getMessage().startsWith("The element /Software System ")); + assertTrue(we.getMessage().startsWith("The element softwaresystem://Software System ")); assertTrue(we.getMessage().endsWith(" has a non-unique ID of 1.")); } } diff --git a/structurizr-core/src/com/structurizr/Workspace.java b/structurizr-core/src/com/structurizr/Workspace.java index bda1874f9..d8465e1b8 100644 --- a/structurizr-core/src/com/structurizr/Workspace.java +++ b/structurizr-core/src/com/structurizr/Workspace.java @@ -205,21 +205,21 @@ public int countAndLogWarnings() { getModel().getElements().stream() .filter(e -> !(e instanceof ContainerInstance) && !(e instanceof DeploymentNode) && !(e instanceof InfrastructureNode)) .filter(e -> e.getDescription() == null || e.getDescription().trim().length() == 0) - .forEach(e -> warnings.add("The " + typeof(e) + " \"" + e.getCanonicalName().substring(1) + "\" is missing a description.")); + .forEach(e -> warnings.add(e.getCanonicalName() + " is missing a description.")); // find containers with a missing technology getModel().getElements().stream() .filter(e -> e instanceof Container) .map(e -> (Container)e) .filter(c -> c.getTechnology() == null || c.getTechnology().trim().length() == 0) - .forEach(c -> warnings.add("The container \"" + c.getCanonicalName().substring(1) + "\" is missing a technology.")); + .forEach(c -> warnings.add(c.getCanonicalName() + " is missing a technology.")); // find components with a missing technology getModel().getElements().stream() .filter(e -> e instanceof Component) .map(e -> (Component)e) .filter(c -> c.getTechnology() == null || c.getTechnology().trim().length() == 0) - .forEach(c -> warnings.add("The component \"" + c.getCanonicalName().substring(1) + "\" is missing a technology.")); + .forEach(c -> warnings.add(c.getCanonicalName() + " is missing a technology.")); // find component relationships with a missing description for (Relationship relationship : getModel().getRelationships()) { @@ -230,7 +230,7 @@ public int countAndLogWarnings() { // (i.e. let's not flood the user with warnings) } else { if (relationship.getDescription() == null || relationship.getDescription().trim().length() == 0) { - warnings.add("The relationship between " + typeof(relationship.getSource()) + " \"" + relationship.getSource().getCanonicalName().substring(1) + "\" and " + typeof(relationship.getDestination()) + " \"" + relationship.getDestination().getCanonicalName().substring(1) + "\" is missing a description."); + warnings.add("The relationship between " + relationship.getSource().getCanonicalName() + " and " + relationship.getDestination().getCanonicalName() + " is missing a description."); } } } diff --git a/structurizr-core/src/com/structurizr/model/CanonicalNameGenerator.java b/structurizr-core/src/com/structurizr/model/CanonicalNameGenerator.java new file mode 100644 index 000000000..9d5613fee --- /dev/null +++ b/structurizr-core/src/com/structurizr/model/CanonicalNameGenerator.java @@ -0,0 +1,81 @@ +package com.structurizr.model; + +final class CanonicalNameGenerator { + + private static final String PERSON_TYPE = "Person://"; + private static final String SOFTWARE_SYSTEM_TYPE = "SoftwareSystem://"; + private static final String CONTAINER_TYPE = "Container://"; + private static final String COMPONENT_TYPE = "Component://"; + + private static final String DEPLOYMENT_NODE_TYPE = "DeploymentNode://"; + private static final String INFRASTRUCTURE_NODE_TYPE = "InfrastructureNode://"; + private static final String CONTAINER_INSTANCE_TYPE = "ContainerInstance://"; + private static final String SOFTWARE_SYSTEM_INSTANCE_TYPE = "SoftwareSystemInstance://"; + + private static final String STATIC_CANONICAL_NAME_SEPARATOR = "."; + private static final String DEPLOYMENT_CANONICAL_NAME_SEPARATOR = "/"; + + private String formatName(Element element) { + return formatName(element.getName()); + } + + private String formatName(String name) { + return name + .replace(STATIC_CANONICAL_NAME_SEPARATOR, "") + .replace(DEPLOYMENT_CANONICAL_NAME_SEPARATOR, ""); + } + + String generate(Person person) { + return PERSON_TYPE + formatName(person); + } + + String generate(SoftwareSystem softwareSystem) { + return SOFTWARE_SYSTEM_TYPE + formatName(softwareSystem); + } + + String generate(Container container) { + return CONTAINER_TYPE + formatName(container.getSoftwareSystem()) + STATIC_CANONICAL_NAME_SEPARATOR + formatName(container); + } + + String generate(Component component) { + return COMPONENT_TYPE + formatName(component.getContainer().getSoftwareSystem()) + STATIC_CANONICAL_NAME_SEPARATOR + formatName(component.getContainer()) + STATIC_CANONICAL_NAME_SEPARATOR + formatName(component); + } + + String generate(DeploymentNode deploymentNode) { + StringBuilder buf = new StringBuilder(); + buf.append(DEPLOYMENT_NODE_TYPE); + + buf.append(formatName(deploymentNode.getEnvironment())); + buf.append(DEPLOYMENT_CANONICAL_NAME_SEPARATOR); + + DeploymentNode parent = (DeploymentNode)deploymentNode.getParent(); + while (parent != null) { + buf.append(formatName(parent)); + buf.append(DEPLOYMENT_CANONICAL_NAME_SEPARATOR); + parent = (DeploymentNode)parent.getParent(); + } + + buf.append(formatName(deploymentNode)); + + return buf.toString(); + } + + String generate(InfrastructureNode infrastructureNode) { + String deploymentNodeCanonicalName = generate((DeploymentNode)infrastructureNode.getParent()).substring(DEPLOYMENT_NODE_TYPE.length()); + + return INFRASTRUCTURE_NODE_TYPE + deploymentNodeCanonicalName + DEPLOYMENT_CANONICAL_NAME_SEPARATOR + formatName(infrastructureNode); + } + + String generate(SoftwareSystemInstance softwareSystemInstance) { + String deploymentNodeCanonicalName = generate((DeploymentNode)softwareSystemInstance.getParent()).substring(DEPLOYMENT_NODE_TYPE.length()); + + return SOFTWARE_SYSTEM_INSTANCE_TYPE + deploymentNodeCanonicalName + DEPLOYMENT_CANONICAL_NAME_SEPARATOR + formatName(softwareSystemInstance.getSoftwareSystem()) + "[" + softwareSystemInstance.getInstanceId() + "]"; + } + + String generate(ContainerInstance containerInstance) { + String deploymentNodeCanonicalName = generate((DeploymentNode)containerInstance.getParent()).substring(DEPLOYMENT_NODE_TYPE.length()); + + return CONTAINER_INSTANCE_TYPE + deploymentNodeCanonicalName + DEPLOYMENT_CANONICAL_NAME_SEPARATOR + generate(containerInstance.getContainer()).substring(CONTAINER_TYPE.length()) + "[" + containerInstance.getInstanceId() + "]"; + } + +} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/model/Component.java b/structurizr-core/src/com/structurizr/model/Component.java index 147b41e10..2dde1abf2 100644 --- a/structurizr-core/src/com/structurizr/model/Component.java +++ b/structurizr-core/src/com/structurizr/model/Component.java @@ -133,7 +133,7 @@ public void setSize(long size) { */ @Override public String getCanonicalName() { - return getParent().getCanonicalName() + CANONICAL_NAME_SEPARATOR + formatForCanonicalName(getName()); + return new CanonicalNameGenerator().generate(this); } @Override diff --git a/structurizr-core/src/com/structurizr/model/Container.java b/structurizr-core/src/com/structurizr/model/Container.java index 770a1a205..be2622bf6 100644 --- a/structurizr-core/src/com/structurizr/model/Container.java +++ b/structurizr-core/src/com/structurizr/model/Container.java @@ -174,7 +174,7 @@ public Component getComponentOfType(String type) { */ @Override public String getCanonicalName() { - return getParent().getCanonicalName() + CANONICAL_NAME_SEPARATOR + formatForCanonicalName(getName()); + return new CanonicalNameGenerator().generate(this); } @Override diff --git a/structurizr-core/src/com/structurizr/model/ContainerInstance.java b/structurizr-core/src/com/structurizr/model/ContainerInstance.java index cf41b5b9d..883d9507f 100644 --- a/structurizr-core/src/com/structurizr/model/ContainerInstance.java +++ b/structurizr-core/src/com/structurizr/model/ContainerInstance.java @@ -51,4 +51,10 @@ void setContainerId(String containerId) { this.containerId = containerId; } + @Override + @JsonIgnore + public String getCanonicalName() { + return new CanonicalNameGenerator().generate(this); + } + } \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/model/DeploymentElement.java b/structurizr-core/src/com/structurizr/model/DeploymentElement.java index 3577d940e..d2d4d74c7 100644 --- a/structurizr-core/src/com/structurizr/model/DeploymentElement.java +++ b/structurizr-core/src/com/structurizr/model/DeploymentElement.java @@ -1,5 +1,7 @@ package com.structurizr.model; +import com.fasterxml.jackson.annotation.JsonIgnore; + /** * This is the superclass for model elements that describe deployment nodes, infrastructure nodes, and container instances. */ @@ -7,11 +9,27 @@ public abstract class DeploymentElement extends Element { static final String DEFAULT_DEPLOYMENT_ENVIRONMENT = "Default"; + private DeploymentNode parent; private String environment = DEFAULT_DEPLOYMENT_ENVIRONMENT; DeploymentElement() { } + /** + * Gets the parent deployment node. + * + * @return the parent DeploymentNode, or null if there is no parent + */ + @Override + @JsonIgnore + public final Element getParent() { + return parent; + } + + void setParent(DeploymentNode parent) { + this.parent = parent; + } + public String getEnvironment() { return environment; } diff --git a/structurizr-core/src/com/structurizr/model/DeploymentNode.java b/structurizr-core/src/com/structurizr/model/DeploymentNode.java index aa2ac7a73..330ce408f 100644 --- a/structurizr-core/src/com/structurizr/model/DeploymentNode.java +++ b/structurizr-core/src/com/structurizr/model/DeploymentNode.java @@ -21,7 +21,6 @@ */ public final class DeploymentNode extends DeploymentElement { - private DeploymentNode parent; private String technology; private int instances = 1; @@ -314,21 +313,6 @@ void setContainerInstances(Set containerInstances) { } } - /** - * Gets the parent deployment node. - * - * @return the parent DeploymentNode, or null if there is no parent - */ - @Override - @JsonIgnore - public Element getParent() { - return parent; - } - - void setParent(DeploymentNode parent) { - this.parent = parent; - } - public String getTechnology() { return technology; } @@ -356,11 +340,7 @@ protected Set getRequiredTags() { @Override public String getCanonicalName() { - if (getParent() != null) { - return getParent().getCanonicalName() + CANONICAL_NAME_SEPARATOR + formatForCanonicalName(getName()); - } else { - return CANONICAL_NAME_SEPARATOR + "Deployment" + CANONICAL_NAME_SEPARATOR + formatForCanonicalName(getEnvironment()) + CANONICAL_NAME_SEPARATOR + formatForCanonicalName(getName()); - } + return new CanonicalNameGenerator().generate(this); } } \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/model/InfrastructureNode.java b/structurizr-core/src/com/structurizr/model/InfrastructureNode.java index 337dd8e15..f5e32c7b4 100644 --- a/structurizr-core/src/com/structurizr/model/InfrastructureNode.java +++ b/structurizr-core/src/com/structurizr/model/InfrastructureNode.java @@ -60,21 +60,6 @@ public Relationship uses(DeploymentElement destination, String description, Stri return getModel().addRelationship(this, destination, description, technology, interactionStyle, tags); } - /** - * Gets the parent deployment node. - * - * @return the parent DeploymentNode, or null if there is no parent - */ - @Override - @JsonIgnore - public Element getParent() { - return parent; - } - - void setParent(DeploymentNode parent) { - this.parent = parent; - } - public String getTechnology() { return technology; } @@ -90,7 +75,7 @@ protected Set getRequiredTags() { @Override public String getCanonicalName() { - return getParent().getCanonicalName() + CANONICAL_NAME_SEPARATOR + formatForCanonicalName(getName()); + return new CanonicalNameGenerator().generate(this); } } \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/model/MessageDigestIdGenerator.java b/structurizr-core/src/com/structurizr/model/MessageDigestIdGenerator.java deleted file mode 100644 index f0075b8e3..000000000 --- a/structurizr-core/src/com/structurizr/model/MessageDigestIdGenerator.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.structurizr.model; - -import java.nio.charset.Charset; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - -import static java.lang.Math.min; - -/** - * An ID generator that uses a digest function when generating IDs for model elements and relationships. - * This allows IDs to be more stable than a sequential number, and allows models to be merged more easily. - */ -public class MessageDigestIdGenerator implements IdGenerator { - - private static final Charset UTF8 = Charset.forName("UTF-8"); - private static final char[] HEX_CHARS = "0123456789abcdef".toCharArray(); - - public static MessageDigestIdGenerator getInstance(final String algorithm) { - return getInstance(algorithm, Integer.MAX_VALUE); - } - - public static MessageDigestIdGenerator getInstance(final String algorithm, final int length) { - try { - return new MessageDigestIdGenerator(MessageDigest.getInstance(algorithm), length); - } catch (NoSuchAlgorithmException e) { - throw new IllegalArgumentException("Unknown algorithm: " + algorithm, e); - } - } - - private final MessageDigest digest; - private final int length; - - public MessageDigestIdGenerator(final MessageDigest digest, final int length) { - this.digest = digest; - this.length = length; - } - - @Override - public void found(String id) { - // nothing to do - } - - @Override - public String generateId(Element element) { - return generateId( - element.getCanonicalName()); - } - - @Override - public String generateId(Relationship relationship) { - return generateId( - relationship.getSource().getCanonicalName(), - relationship.getDescription(), - relationship.getDestination().getCanonicalName()); - } - - public synchronized String generateId(final String...terms) { - digest.reset(); - for (final String term : terms) { - if(term!=null) { - digest.update(term.getBytes(UTF8)); - } - } - - final byte[] bytes = this.digest.digest(); - final char[] chars = new char[min(bytes.length * 2, this.length)]; - - int b=0, c=0; - while(b < bytes.length && c < chars.length) { - int value = bytes[b++] & 0xFF; - chars[c++] = HEX_CHARS[value >>> 4]; - if(c < chars.length) { - chars[c++] = HEX_CHARS[value & 0x0F]; - } - } - return new String(chars); - } -} diff --git a/structurizr-core/src/com/structurizr/model/Model.java b/structurizr-core/src/com/structurizr/model/Model.java index d75de2ee4..b2c0853ea 100644 --- a/structurizr-core/src/com/structurizr/model/Model.java +++ b/structurizr-core/src/com/structurizr/model/Model.java @@ -450,11 +450,13 @@ private void hydrateDeploymentNode(DeploymentNode deploymentNode, DeploymentNode for (SoftwareSystemInstance softwareSystemInstance : deploymentNode.getSoftwareSystemInstances()) { softwareSystemInstance.setSoftwareSystem((SoftwareSystem)getElement(softwareSystemInstance.getSoftwareSystemId())); + softwareSystemInstance.setParent(deploymentNode); addElementToInternalStructures(softwareSystemInstance); } for (ContainerInstance containerInstance : deploymentNode.getContainerInstances()) { containerInstance.setContainer((Container)getElement(containerInstance.getContainerId())); + containerInstance.setParent(deploymentNode); addElementToInternalStructures(containerInstance); } @@ -886,9 +888,10 @@ SoftwareSystemInstance addSoftwareSystemInstance(DeploymentNode deploymentNode, throw new IllegalArgumentException("A software system must be specified."); } - long instanceNumber = getElements().stream().filter(e -> e instanceof SoftwareSystemInstance && ((SoftwareSystemInstance)e).getSoftwareSystem().equals(softwareSystem)).count(); + long instanceNumber = deploymentNode.getSoftwareSystemInstances().stream().filter(ssi -> ssi.getSoftwareSystem().equals(softwareSystem)).count(); instanceNumber++; SoftwareSystemInstance softwareSystemInstance = new SoftwareSystemInstance(softwareSystem, (int)instanceNumber, deploymentNode.getEnvironment()); + softwareSystemInstance.setParent(deploymentNode); softwareSystemInstance.setId(idGenerator.generateId(softwareSystemInstance)); if (replicateRelationships) { @@ -905,9 +908,10 @@ ContainerInstance addContainerInstance(DeploymentNode deploymentNode, Container throw new IllegalArgumentException("A container must be specified."); } - long instanceNumber = getElements().stream().filter(e -> e instanceof ContainerInstance && ((ContainerInstance)e).getContainer().equals(container)).count(); + long instanceNumber = deploymentNode.getContainerInstances().stream().filter(ci -> ci.getContainer().equals(container)).count(); instanceNumber++; ContainerInstance containerInstance = new ContainerInstance(container, (int)instanceNumber, deploymentNode.getEnvironment()); + containerInstance.setParent(deploymentNode); containerInstance.setId(idGenerator.generateId(containerInstance)); if (replicateRelationships) { @@ -966,11 +970,6 @@ public Element getElementWithCanonicalName(String canonicalName) { throw new IllegalArgumentException("A canonical name must be specified."); } - // canonical names start with a leading slash, so add this if it's missing - if (!canonicalName.startsWith("/")) { - canonicalName = "/" + canonicalName; - } - for (Element element : getElements()) { if (element.getCanonicalName().equals(canonicalName)) { return element; diff --git a/structurizr-core/src/com/structurizr/model/NormalizedNameIdGenerator.java b/structurizr-core/src/com/structurizr/model/NormalizedNameIdGenerator.java deleted file mode 100644 index 02cdb9f6a..000000000 --- a/structurizr-core/src/com/structurizr/model/NormalizedNameIdGenerator.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.structurizr.model; - -import java.util.HashSet; -import java.util.Set; - -/** - * An ID generator that uses the element name to generate IDs for model elements and relationships. - * The canonical name will be normalized by removing any characters other than letters and numbers. - */ -public class NormalizedNameIdGenerator implements IdGenerator { - - private final boolean canonical; - - private Set usedIds = new HashSet<>(); - - /** - * Creates a new ID generator, where element IDs are based upon the canonical name of the element. - */ - public NormalizedNameIdGenerator() { - this(true); - } - - /** - * Creates a new ID generator. - * - * @param canonical {@code true} will use the {@link Element#getCanonicalName()}, {@code false} will use {@link Element#getName()} - */ - public NormalizedNameIdGenerator(boolean canonical) { - this.canonical = canonical; - } - - @Override - public void found(String id) { - usedIds.add(id); // Remember as used to avoid clashes - } - - @Override - public String generateId(Element element) { - if (!canonical && element instanceof ContainerInstance) { // Special treatment - ContainerInstance containerInstance = (ContainerInstance) element; - return generateId(containerInstance.getContainerId() + containerInstance.getInstanceId()); - } else { - return generateId(canonical ? element.getCanonicalName() : element.getName()); - } - } - - @Override - public String generateId(Relationship relationship) { - return generateId( - canonical ? relationship.getSource().getCanonicalName() : relationship.getSource().getName(), - relationship.getDescription(), - canonical ? relationship.getDestination().getCanonicalName() : relationship.getDestination().getName()); - } - - private String generateId(final String...terms) { - final StringBuilder sb = new StringBuilder(); - for (final String term : terms) { - if (term != null) { - sb.append(term.replaceAll("[^a-zA-Z0-9]", "")); - } - } - - final String id = sb.toString(); - if (usedIds.contains(id)) { - throw new IllegalArgumentException("Non-unique ID generated: " + id); - } else { - usedIds.add(id); - return id; - } - } -} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/model/Person.java b/structurizr-core/src/com/structurizr/model/Person.java index 19dbc5936..eadf9ec08 100644 --- a/structurizr-core/src/com/structurizr/model/Person.java +++ b/structurizr-core/src/com/structurizr/model/Person.java @@ -42,7 +42,7 @@ public void setLocation(Location location) { @Override public String getCanonicalName() { - return CANONICAL_NAME_SEPARATOR + formatForCanonicalName(getName()); + return new CanonicalNameGenerator().generate(this); } @Override diff --git a/structurizr-core/src/com/structurizr/model/SoftwareSystem.java b/structurizr-core/src/com/structurizr/model/SoftwareSystem.java index 6ef721569..2781c0d37 100644 --- a/structurizr-core/src/com/structurizr/model/SoftwareSystem.java +++ b/structurizr-core/src/com/structurizr/model/SoftwareSystem.java @@ -139,7 +139,7 @@ public Container getContainerWithId(@Nonnull String id) { */ @Override public String getCanonicalName() { - return CANONICAL_NAME_SEPARATOR + formatForCanonicalName(getName()); + return new CanonicalNameGenerator().generate(this); } @Override diff --git a/structurizr-core/src/com/structurizr/model/SoftwareSystemInstance.java b/structurizr-core/src/com/structurizr/model/SoftwareSystemInstance.java index 49e8dc3c4..626b794bb 100644 --- a/structurizr-core/src/com/structurizr/model/SoftwareSystemInstance.java +++ b/structurizr-core/src/com/structurizr/model/SoftwareSystemInstance.java @@ -50,5 +50,11 @@ public String getSoftwareSystemId() { void setSoftwareSystemId(String softwareSystemId) { this.softwareSystemId = softwareSystemId; } - + + @Override + @JsonIgnore + public String getCanonicalName() { + return new CanonicalNameGenerator().generate(this); + } + } \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/model/StaticStructureElementInstance.java b/structurizr-core/src/com/structurizr/model/StaticStructureElementInstance.java index bdd38f397..66ac49ff7 100644 --- a/structurizr-core/src/com/structurizr/model/StaticStructureElementInstance.java +++ b/structurizr-core/src/com/structurizr/model/StaticStructureElementInstance.java @@ -55,18 +55,6 @@ public boolean removeTag(String tag) { return false; } - @Override - @JsonIgnore - public String getCanonicalName() { - return getElement().getCanonicalName() + "[" + instanceId + "]"; - } - - @Override - @JsonIgnore - public Element getParent() { - return getElement().getParent(); - } - @Override @JsonIgnore public String getName() { diff --git a/structurizr-core/test/unit/com/structurizr/model/ComponentTests.java b/structurizr-core/test/unit/com/structurizr/model/ComponentTests.java index 5266b5d1d..5d1bbefc8 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ComponentTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ComponentTests.java @@ -23,13 +23,13 @@ public void test_getName_ReturnsTheGivenName_WhenANameIsGiven() { @Test public void test_getCanonicalName() { Component component = container.addComponent("Component", "Description"); - assertEquals("/System/Container/Component", component.getCanonicalName()); + assertEquals("Component://System.Container.Component", component.getCanonicalName()); } @Test - public void test_getCanonicalName_WhenNameContainsASlashCharacter() { - Component component = container.addComponent("Name1/Name2", "Description"); - assertEquals("/System/Container/Name1Name2", component.getCanonicalName()); + public void test_getCanonicalName_WhenNameContainsSlashAndDotCharacters() { + Component component = container.addComponent("Name1/.Name2", "Description"); + assertEquals("Component://System.Container.Name1Name2", component.getCanonicalName()); } @Test diff --git a/structurizr-core/test/unit/com/structurizr/model/ContainerInstanceTests.java b/structurizr-core/test/unit/com/structurizr/model/ContainerInstanceTests.java index 8c5d14d6b..db2abba3e 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ContainerInstanceTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ContainerInstanceTests.java @@ -13,93 +13,93 @@ public class ContainerInstanceTests extends AbstractWorkspaceTestBase { @Test public void test_construction() { - ContainerInstance containerInstance = deploymentNode.add(database); + ContainerInstance instance = deploymentNode.add(database); - assertSame(database, containerInstance.getContainer()); - assertEquals(database.getId(), containerInstance.getContainerId()); - assertEquals(1, containerInstance.getInstanceId()); + assertSame(database, instance.getContainer()); + assertEquals(database.getId(), instance.getContainerId()); + assertEquals(1, instance.getInstanceId()); } @Test public void test_getContainerId() { - ContainerInstance containerInstance = deploymentNode.add(database); + ContainerInstance instance = deploymentNode.add(database); - assertEquals(database.getId(), containerInstance.getContainerId()); - containerInstance.setContainer(null); - containerInstance.setContainerId("1234"); - assertEquals("1234", containerInstance.getContainerId()); + assertEquals(database.getId(), instance.getContainerId()); + instance.setContainer(null); + instance.setContainerId("1234"); + assertEquals("1234", instance.getContainerId()); } @Test public void test_getName() { - ContainerInstance containerInstance = deploymentNode.add(database); + ContainerInstance instance = deploymentNode.add(database); - assertEquals("Database Schema", containerInstance.getName()); + assertEquals("Database Schema", instance.getName()); - containerInstance.setName("foo"); - assertEquals("Database Schema", containerInstance.getName()); + instance.setName("foo"); + assertEquals("Database Schema", instance.getName()); } @Test public void test_getCanonicalName() { - ContainerInstance containerInstance = deploymentNode.add(database); + ContainerInstance instance = deploymentNode.add(database); - assertEquals("/System/Database Schema[1]", containerInstance.getCanonicalName()); + assertEquals("ContainerInstance://Default/Deployment Node/System.Database Schema[1]", instance.getCanonicalName()); } @Test - public void test_getParent_ReturnsTheParentSoftwareSystem() { - ContainerInstance containerInstance = deploymentNode.add(database); + public void test_getParent_ReturnsTheParentDeploymentNode() { + ContainerInstance instance = deploymentNode.add(database); - assertEquals(softwareSystem, containerInstance.getParent()); + assertEquals(deploymentNode, instance.getParent()); } @Test public void test_getRequiredTags() { - ContainerInstance containerInstance = deploymentNode.add(database); + ContainerInstance instance = deploymentNode.add(database); - assertTrue(containerInstance.getRequiredTags().isEmpty()); + assertTrue(instance.getRequiredTags().isEmpty()); } @Test public void test_getTags() { database.addTags("Database"); - ContainerInstance containerInstance = deploymentNode.add(database); - containerInstance.addTags("Primary Instance"); + ContainerInstance instance = deploymentNode.add(database); + instance.addTags("Primary Instance"); - assertEquals("Container Instance,Primary Instance", containerInstance.getTags()); + assertEquals("Container Instance,Primary Instance", instance.getTags()); } @Test public void test_removeTags_DoesNotRemoveAnyTags() { - ContainerInstance containerInstance = deploymentNode.add(database); + ContainerInstance instance = deploymentNode.add(database); - assertTrue(containerInstance.getTags().contains(Tags.CONTAINER_INSTANCE)); + assertTrue(instance.getTags().contains(Tags.CONTAINER_INSTANCE)); - containerInstance.removeTag(Tags.CONTAINER_INSTANCE); + instance.removeTag(Tags.CONTAINER_INSTANCE); - assertTrue(containerInstance.getTags().contains(Tags.CONTAINER_INSTANCE)); + assertTrue(instance.getTags().contains(Tags.CONTAINER_INSTANCE)); } @Test public void test_addHealthCheck() { - ContainerInstance containerInstance = deploymentNode.add(database); - assertTrue(containerInstance.getHealthChecks().isEmpty()); + ContainerInstance instance = deploymentNode.add(database); + assertTrue(instance.getHealthChecks().isEmpty()); - HttpHealthCheck healthCheck = containerInstance.addHealthCheck("Test web application is working", "http://localhost:8080"); + HttpHealthCheck healthCheck = instance.addHealthCheck("Test web application is working", "http://localhost:8080"); assertEquals("Test web application is working", healthCheck.getName()); assertEquals("http://localhost:8080", healthCheck.getUrl()); assertEquals(60, healthCheck.getInterval()); assertEquals(0, healthCheck.getTimeout()); - assertEquals(1, containerInstance.getHealthChecks().size()); + assertEquals(1, instance.getHealthChecks().size()); } @Test public void test_addHealthCheck_ThrowsAnException_WhenTheNameIsNull() { - ContainerInstance containerInstance = deploymentNode.add(database); + ContainerInstance instance = deploymentNode.add(database); try { - containerInstance.addHealthCheck(null, "http://localhost"); + instance.addHealthCheck(null, "http://localhost"); fail(); } catch (IllegalArgumentException iae) { assertEquals("The name must not be null or empty.", iae.getMessage()); @@ -108,10 +108,10 @@ public void test_addHealthCheck_ThrowsAnException_WhenTheNameIsNull() { @Test public void test_addHealthCheck_ThrowsAnException_WhenTheNameIsEmpty() { - ContainerInstance containerInstance = deploymentNode.add(database); + ContainerInstance instance = deploymentNode.add(database); try { - containerInstance.addHealthCheck(" ", "http://localhost"); + instance.addHealthCheck(" ", "http://localhost"); fail(); } catch (IllegalArgumentException iae) { assertEquals("The name must not be null or empty.", iae.getMessage()); @@ -120,10 +120,10 @@ public void test_addHealthCheck_ThrowsAnException_WhenTheNameIsEmpty() { @Test public void test_addHealthCheck_ThrowsAnException_WhenTheUrlIsNull() { - ContainerInstance containerInstance = deploymentNode.add(database); + ContainerInstance instance = deploymentNode.add(database); try { - containerInstance.addHealthCheck("Name", null); + instance.addHealthCheck("Name", null); fail(); } catch (IllegalArgumentException iae) { assertEquals("The URL must not be null or empty.", iae.getMessage()); @@ -132,10 +132,10 @@ public void test_addHealthCheck_ThrowsAnException_WhenTheUrlIsNull() { @Test public void test_addHealthCheck_ThrowsAnException_WhenTheUrlIsEmpty() { - ContainerInstance containerInstance = deploymentNode.add(database); + ContainerInstance instance = deploymentNode.add(database); try { - containerInstance.addHealthCheck("Name", " "); + instance.addHealthCheck("Name", " "); fail(); } catch (IllegalArgumentException iae) { assertEquals("The URL must not be null or empty.", iae.getMessage()); @@ -144,10 +144,10 @@ public void test_addHealthCheck_ThrowsAnException_WhenTheUrlIsEmpty() { @Test public void test_addHealthCheck_ThrowsAnException_WhenTheUrlIsInvalid() { - ContainerInstance containerInstance = deploymentNode.add(database); + ContainerInstance instance = deploymentNode.add(database); try { - containerInstance.addHealthCheck("Name", "localhost"); + instance.addHealthCheck("Name", "localhost"); fail(); } catch (IllegalArgumentException iae) { assertEquals("localhost is not a valid URL.", iae.getMessage()); @@ -156,10 +156,10 @@ public void test_addHealthCheck_ThrowsAnException_WhenTheUrlIsInvalid() { @Test public void test_addHealthCheck_ThrowsAnException_WhenTheIntervalIsLessThanZero() { - ContainerInstance containerInstance = deploymentNode.add(database); + ContainerInstance instance = deploymentNode.add(database); try { - containerInstance.addHealthCheck("Name", "https://localhost", -1, 0); + instance.addHealthCheck("Name", "https://localhost", -1, 0); fail(); } catch (IllegalArgumentException iae) { assertEquals("The polling interval must be zero or a positive integer.", iae.getMessage()); @@ -168,10 +168,10 @@ public void test_addHealthCheck_ThrowsAnException_WhenTheIntervalIsLessThanZero( @Test public void test_addHealthCheck_ThrowsAnException_WhenTheTimeoutIsLessThanZero() { - ContainerInstance containerInstance = deploymentNode.add(database); + ContainerInstance instance = deploymentNode.add(database); try { - containerInstance.addHealthCheck("Name", "https://localhost", 60, -1); + instance.addHealthCheck("Name", "https://localhost", 60, -1); fail(); } catch (IllegalArgumentException iae) { assertEquals("The timeout must be zero or a positive integer.", iae.getMessage()); diff --git a/structurizr-core/test/unit/com/structurizr/model/ContainerTests.java b/structurizr-core/test/unit/com/structurizr/model/ContainerTests.java index 0fd46822c..07813325b 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ContainerTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ContainerTests.java @@ -20,14 +20,14 @@ public void test_technologyProperty() { @Test public void test_getCanonicalName() { - assertEquals("/System/Container", container.getCanonicalName()); + assertEquals("Container://System.Container", container.getCanonicalName()); } @Test - public void test_getCanonicalName_WhenNameContainsASlashCharacter() { - container = softwareSystem.addContainer("Name1/Name2", "Description", "Some technology"); + public void test_getCanonicalName_WhenNameContainsSlashAndDotCharacters() { + container = softwareSystem.addContainer("Name1/.Name2", "Description", "Some technology"); - assertEquals("/System/Name1Name2", container.getCanonicalName()); + assertEquals("Container://System.Name1Name2", container.getCanonicalName()); } @Test diff --git a/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java b/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java index a3a2bfcd5..721fb1108 100644 --- a/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java @@ -12,7 +12,7 @@ public class DeploymentNodeTests extends AbstractWorkspaceTestBase { public void test_getCanonicalName_WhenTheDeploymentNodeHasNoParent() { DeploymentNode deploymentNode = model.addDeploymentNode("Ubuntu Server", "", ""); - assertEquals("/Deployment/Default/Ubuntu Server", deploymentNode.getCanonicalName()); + assertEquals("DeploymentNode://Default/Ubuntu Server", deploymentNode.getCanonicalName()); } @Test @@ -20,7 +20,7 @@ public void test_getCanonicalName_WhenTheDeploymentNodeHasAParent() { DeploymentNode parent = model.addDeploymentNode("Windows Server", "", ""); DeploymentNode child = parent.addDeploymentNode("Apache Tomcat", "", ""); - assertEquals("/Deployment/Default/Windows Server/Apache Tomcat", child.getCanonicalName()); + assertEquals("DeploymentNode://Default/Windows Server/Apache Tomcat", child.getCanonicalName()); } @Test @@ -80,6 +80,7 @@ public void test_add_AddsAContainerInstance_WhenAContainerIsSpecified() { assertNotNull(containerInstance); assertSame(container, containerInstance.getContainer()); assertTrue(deploymentNode.getContainerInstances().contains(containerInstance)); + assertEquals("ContainerInstance://Default/Deployment Node/Software System.Container[1]", containerInstance.getCanonicalName()); } @Test diff --git a/structurizr-core/test/unit/com/structurizr/model/InfrastructureNodeTests.java b/structurizr-core/test/unit/com/structurizr/model/InfrastructureNodeTests.java new file mode 100644 index 000000000..ac27f756b --- /dev/null +++ b/structurizr-core/test/unit/com/structurizr/model/InfrastructureNodeTests.java @@ -0,0 +1,41 @@ +package com.structurizr.model; + +import com.structurizr.AbstractWorkspaceTestBase; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class InfrastructureNodeTests extends AbstractWorkspaceTestBase { + + @Test + public void test_getCanonicalName() { + DeploymentNode deploymentNode = model.addDeploymentNode("Amazon Web Services", "", ""); + InfrastructureNode infrastructureNode = deploymentNode.addInfrastructureNode("Route 53", "", ""); + + assertEquals("InfrastructureNode://Default/Amazon Web Services/Route 53", infrastructureNode.getCanonicalName()); + } + + @Test + public void test_getParent_ReturnsTheParentDeploymentNode() { + DeploymentNode parent = model.addDeploymentNode("Parent", "", ""); + InfrastructureNode child = parent.addInfrastructureNode("Child", "", ""); + child.setParent(parent); + assertSame(parent, child.getParent()); + } + + @Test + public void test_getRequiredTags() { + InfrastructureNode infrastructureNode = new InfrastructureNode(); + assertEquals(2, infrastructureNode.getRequiredTags().size()); + assertTrue(infrastructureNode.getRequiredTags().contains(Tags.ELEMENT)); + assertTrue(infrastructureNode.getRequiredTags().contains(Tags.INFRASTRUCTURE_NODE)); + } + + @Test + public void test_getTags() { + InfrastructureNode infrastructureNode = new InfrastructureNode(); + infrastructureNode.addTags("Tag 1", "Tag 2"); + assertEquals("Element,Infrastructure Node,Tag 1,Tag 2", infrastructureNode.getTags()); + } + +} \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/model/MessageDigestIdGeneratorTests.java b/structurizr-core/test/unit/com/structurizr/model/MessageDigestIdGeneratorTests.java deleted file mode 100644 index 233a0a674..000000000 --- a/structurizr-core/test/unit/com/structurizr/model/MessageDigestIdGeneratorTests.java +++ /dev/null @@ -1,104 +0,0 @@ -package com.structurizr.model; - -import com.structurizr.Workspace; -import com.structurizr.view.*; -import org.junit.Test; - -import static org.junit.Assert.*; - -public class MessageDigestIdGeneratorTests { - - @Test - public void createWorkspaceWithMD5IdsHaveStableIds() { - final MessageDigestIdGenerator ids = MessageDigestIdGenerator.getInstance("MD5"); - final String webappId = "4091a0564c0058b74bdd067373d02578"; - final String databaseId = "7f51a432eb63bedebd49fe41a41f9767"; - final String relationshipId = "c0a5a5ffcd3e87aba4f96e31bd90bef6"; - - createWorkspaceWithExpectedIds(ids, webappId, databaseId, relationshipId); - } - - @Test - public void createWorkspaceWithSHA1IdsHaveStableIds() { - final MessageDigestIdGenerator ids = MessageDigestIdGenerator.getInstance("SHA-1"); - final String webappId = "26f55141b08fa3e27467d2dd6ee0c313de7d82ff"; - final String databaseId = "f056f580ae4ffc36261086037f6ef7d5a735ef48"; - final String relationshipId = "63a3048b98311af9b25610284c21afedcb38dd3d"; - createWorkspaceWithExpectedIds(ids, webappId, databaseId, relationshipId); - } - - @Test - public void createWorkspaceWithShortSHA1IdsHaveStableIds() { - final MessageDigestIdGenerator ids = MessageDigestIdGenerator.getInstance("SHA-1", 7); - final String webappId = "26f5514"; - final String databaseId = "f056f58"; - final String relationshipId = "63a3048"; - createWorkspaceWithExpectedIds(ids, webappId, databaseId, relationshipId); - } - - private void createWorkspaceWithExpectedIds(MessageDigestIdGenerator ids, String webappId, String databaseId, String relationshipId) { - final Workspace ws = createWorkspace(ids); - - final Element webapp = ws.getModel().getElement(webappId); - final Element database = ws.getModel().getElement(databaseId); - final Relationship relationship = ws.getModel().getRelationship(relationshipId); - - assertNotNull(webapp); - assertNotNull(database); - assertNotNull(relationship); - assertEquals("Web Application", webapp.getName()); - assertEquals("Database", database.getName()); - assertEquals("Reads from and writes to", relationship.getDescription()); - assertSame(webapp, relationship.getSource()); - assertSame(database, relationship.getDestination()); - } - - private Workspace createWorkspace(IdGenerator ids) { - Workspace workspace = new Workspace("My workspace", "Description"); - Model model = workspace.getModel(); - model.setIdGenerator(ids); - - SoftwareSystem mySoftwareSystem = model.addSoftwareSystem(Location.Internal, "My Software System", "Description"); - mySoftwareSystem.addTags("Internal"); - - Person person = model.addPerson(Location.External, "A User", "Description"); - person.addTags("External"); - person.uses(mySoftwareSystem, "Uses"); - - Container webApplication = mySoftwareSystem.addContainer("Web Application", "Description", "Apache Tomcat"); - Container database = mySoftwareSystem.addContainer("Database", "Description", "MySQL"); - person.uses(webApplication, "Uses"); - Relationship webApplicationToDatabase = webApplication.uses(database, "Reads from and writes to"); - webApplicationToDatabase.addTags("JDBC"); - - Component componentA = webApplication.addComponent("ComponentA", "Description", "Technology A"); - Component componentB = webApplication.addComponent("com.somecompany.system.ComponentB", "com.somecompany.system.ComponentBImpl", "Description", "Technology B"); - person.uses(componentA, "Uses"); - componentA.uses(componentB, "Uses"); - componentB.uses(database, "Reads from and writes to"); - - ViewSet views = workspace.getViews(); - SystemContextView systemContextView = views.createSystemContextView(mySoftwareSystem, "context", "Description"); - systemContextView.addAllSoftwareSystems(); - systemContextView.addAllPeople(); - - ContainerView containerView = views.createContainerView(mySoftwareSystem, "containers", "Description"); - containerView.addAllSoftwareSystems(); - containerView.addAllPeople(); - containerView.addAllContainers(); - - ComponentView componentView = views.createComponentView(webApplication, "components", "Description"); - componentView.addAllSoftwareSystems(); - componentView.addAllPeople(); - componentView.addAllContainers(); - componentView.addAllComponents(); - - views.getConfiguration().getStyles().add(new ElementStyle(Tags.ELEMENT, 600, 450, "#dddddd", "#000000", 30)); - views.getConfiguration().getStyles().add(new ElementStyle("Internal", null, null, "#041F37", "#ffffff", null)); - views.getConfiguration().getStyles().add(new RelationshipStyle(Tags.RELATIONSHIP, 4, "#dddddd", true, Routing.Direct, 25, 300, null)); - views.getConfiguration().getStyles().add(new RelationshipStyle("JDBC", 4, "#ff0000", true, Routing.Direct, 25, 300, null)); - - return workspace; - } - -} diff --git a/structurizr-core/test/unit/com/structurizr/model/ModelTests.java b/structurizr-core/test/unit/com/structurizr/model/ModelTests.java index 4dd993871..77da30646 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ModelTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ModelTests.java @@ -657,13 +657,6 @@ public void test_addElementInstance_AddsElementInstancesAndReplicatesRelationshi liveDeploymentNode.add(container3); liveDeploymentNode.add(softwareSystem4); - assertSame(container2, containerInstance2.getContainer()); - assertEquals(container2.getId(), containerInstance2.getContainerId()); - assertSame(softwareSystem2, containerInstance2.getParent()); - assertEquals("/Software System 2/Container 2[1]", containerInstance2.getCanonicalName()); - assertEquals("Container Instance", containerInstance2.getTags()); - assertEquals("Development", containerInstance2.getEnvironment()); - assertEquals(1, containerInstance1.getRelationships().size()); Relationship relationship = containerInstance1.getRelationships().iterator().next(); assertSame(containerInstance1, relationship.getSource()); @@ -711,12 +704,6 @@ public void test_addContainerInstance_AddsAContainerInstanceAndDoesNotReplicateR ContainerInstance containerInstance2 = deploymentNode.add(container2, false); ContainerInstance containerInstance3 = deploymentNode.add(container3, false); - assertSame(container2, containerInstance2.getContainer()); - assertEquals(container2.getId(), containerInstance2.getContainerId()); - assertSame(softwareSystem2, containerInstance2.getParent()); - assertEquals("/Software System 2/Container 2[1]", containerInstance2.getCanonicalName()); - assertEquals("Container Instance", containerInstance2.getTags()); - assertEquals(0, containerInstance1.getRelationships().size()); assertEquals(0, containerInstance2.getRelationships().size()); } @@ -765,13 +752,10 @@ public void test_getElementWithCanonicalName_ReturnsNull_WhenAnElementWithTheSpe @Test public void test_getElementWithCanonicalName_ReturnsTheElement_WhenAnElementWithTheSpecifiedCanonicalNameExists() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); - - assertSame(model.getElementWithCanonicalName("/Software System"), softwareSystem); - assertSame(model.getElementWithCanonicalName("Software System"), softwareSystem); - Container container = softwareSystem.addContainer("Web Application", "Description", "Technology"); - assertSame(container, model.getElementWithCanonicalName("/Software System/Web Application")); - assertSame(container, model.getElementWithCanonicalName("Software System/Web Application")); + + assertSame(softwareSystem, model.getElementWithCanonicalName("SoftwareSystem://Software System")); + assertSame(container, model.getElementWithCanonicalName("Container://Software System.Web Application")); } @Test @@ -1036,4 +1020,37 @@ public void test_setImpliedRelationshipStrategy_ResetsToTheDefaultStrategy_WhenP assertTrue(model.getImpliedRelationshipsStrategy() instanceof DefaultImpliedRelationshipsStrategy); } + @Test + public void test_addSoftwareSystemInstance_AllocatesInstanceIdsPerDeploymentNode() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); + DeploymentNode deploymentNodeA = model.addDeploymentNode("Deployment Node A", "", ""); + DeploymentNode deploymentNodeB = model.addDeploymentNode("Deployment Node B", "", ""); + SoftwareSystemInstance softwareSystemInstanceA1 = deploymentNodeA.add(softwareSystem); + SoftwareSystemInstance softwareSystemInstanceA2 = deploymentNodeA.add(softwareSystem); + SoftwareSystemInstance softwareSystemInstanceB1 = deploymentNodeB.add(softwareSystem); + SoftwareSystemInstance softwareSystemInstanceB2 = deploymentNodeB.add(softwareSystem); + + assertEquals("SoftwareSystemInstance://Default/Deployment Node A/Software System[1]", softwareSystemInstanceA1.getCanonicalName()); + assertEquals("SoftwareSystemInstance://Default/Deployment Node A/Software System[2]", softwareSystemInstanceA2.getCanonicalName()); + assertEquals("SoftwareSystemInstance://Default/Deployment Node B/Software System[1]", softwareSystemInstanceB1.getCanonicalName()); + assertEquals("SoftwareSystemInstance://Default/Deployment Node B/Software System[2]", softwareSystemInstanceB2.getCanonicalName()); + } + + @Test + public void test_addContainerInstance_AllocatesInstanceIdsPerDeploymentNode() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); + Container container = softwareSystem.addContainer("Container", "", ""); + DeploymentNode deploymentNodeA = model.addDeploymentNode("Deployment Node A", "", ""); + DeploymentNode deploymentNodeB = model.addDeploymentNode("Deployment Node B", "", ""); + ContainerInstance containerInstanceA1 = deploymentNodeA.add(container); + ContainerInstance containerInstanceA2 = deploymentNodeA.add(container); + ContainerInstance containerInstanceB1 = deploymentNodeB.add(container); + ContainerInstance containerInstanceB2 = deploymentNodeB.add(container); + + assertEquals("ContainerInstance://Default/Deployment Node A/Software System.Container[1]", containerInstanceA1.getCanonicalName()); + assertEquals("ContainerInstance://Default/Deployment Node A/Software System.Container[2]", containerInstanceA2.getCanonicalName()); + assertEquals("ContainerInstance://Default/Deployment Node B/Software System.Container[1]", containerInstanceB1.getCanonicalName()); + assertEquals("ContainerInstance://Default/Deployment Node B/Software System.Container[2]", containerInstanceB2.getCanonicalName()); + } + } \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/model/NormalizedNameIdGeneratorTests.java b/structurizr-core/test/unit/com/structurizr/model/NormalizedNameIdGeneratorTests.java deleted file mode 100644 index 04a091736..000000000 --- a/structurizr-core/test/unit/com/structurizr/model/NormalizedNameIdGeneratorTests.java +++ /dev/null @@ -1,102 +0,0 @@ -package com.structurizr.model; - -import com.structurizr.Workspace; -import org.junit.Before; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -public class NormalizedNameIdGeneratorTests { - - private Model model; - - @Before - public void setUp() { - Workspace workspace = new Workspace("My workspace", "Description"); - model = workspace.getModel(); - } - - @Test - public void test_generateId_WhenUsingCanonicalName() { - model.setIdGenerator(new NormalizedNameIdGenerator(true)); - - SoftwareSystem mySoftwareSystem = model.addSoftwareSystem("My Software System", "Description"); - - assertEquals("AUser", model.addPerson("A User", "Description").getId()); - - Container webApplication = mySoftwareSystem.addContainer("Web-Application", "Description", "Technology"); // Dash to be removed - assertEquals("MySoftwareSystemWebApplication", webApplication.getId()); - - Container database = mySoftwareSystem.addContainer("Database", "Description", "Technology"); - assertEquals("MySoftwareSystemWebApplicationReadsfromandwritestoMySoftwareSystemDatabase", - webApplication.uses(database, "Reads from and writes to").getId()); - - DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node", "Description", "Technology"); - assertEquals("MySoftwareSystemWebApplication1", deploymentNode.add(webApplication).getId()); - - assertEquals("MySoftwareSystemWebApplicationComponentA", - webApplication.addComponent("ComponentA", "Description", "Technology A").getId()); - - DeploymentNode awsVm = model.addDeploymentNode("VM @ AWS", "Description", "Technology"); - assertEquals("DeploymentDefaultVMAWS", awsVm.getId()); - assertEquals("DeploymentDefaultVMAWSJBossAS711", awsVm.addDeploymentNode("JBoss AS 7.1.1", "Description", "Technology").getId()); - } - - @Test - public void test_generateId_WhenUsingName() { - model.setIdGenerator(new NormalizedNameIdGenerator(false)); - - SoftwareSystem mySoftwareSystem = model.addSoftwareSystem("My Software System", "Description"); - - assertEquals("AUser", model.addPerson("A User", "Description").getId()); - - Container webApplication = mySoftwareSystem.addContainer("Web-Application", "Description", "Technology"); // Dash to be removed - assertEquals("WebApplication", webApplication.getId()); - Container database = mySoftwareSystem.addContainer("Database", "Description", "Technology"); - assertEquals("WebApplicationReadsfromandwritestoDatabase", - webApplication.uses(database, "Reads from and writes to").getId()); - - DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node", "Description", "Technology"); - assertEquals("WebApplication1", deploymentNode.add(webApplication).getId()); - - assertEquals("ComponentA", - webApplication.addComponent("ComponentA", "Description", "Technology A").getId()); - - DeploymentNode awsVm = model.addDeploymentNode("VM @ AWS", "Description", "Technology"); - assertEquals("VMAWS", awsVm.getId()); - assertEquals("JBossAS711", awsVm.addDeploymentNode("JBoss AS 7.1.1", "Description", "Technology").getId()); - } - - @Test - public void test_generateId_ThrowsAnException_WhenElementCanonicalNamesAreNotUnique() { - model.setIdGenerator(new NormalizedNameIdGenerator(true)); - - SoftwareSystem mySoftwareSystem = model.addSoftwareSystem("My Software System", "Description"); - mySoftwareSystem.addContainer("My-Container 1.1", "Description", "Technology"); - - try { - mySoftwareSystem.addContainer("My Container/1,1", "Description", "Technology"); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("Non-unique ID generated: MySoftwareSystemMyContainer11", iae.getMessage()); - } - } - - @Test - public void test_generateId_ThrowsAnException_WhenElementNamesAreNotUnique() { - model.setIdGenerator(new NormalizedNameIdGenerator(false)); - - SoftwareSystem mySoftwareSystem = model.addSoftwareSystem("My Software System", "Description"); - mySoftwareSystem.addContainer("ContainerA", "Description", "Technology") - .addComponent("Component A", "Description"); - - try { - mySoftwareSystem.addContainer("ContainerB", "Description", "Technology") - .addComponent("Component-A", "Description"); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("Non-unique ID generated: ComponentA", iae.getMessage()); - } - } -} \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/model/PersonTests.java b/structurizr-core/test/unit/com/structurizr/model/PersonTests.java index ba8d73c94..c4c25e33c 100644 --- a/structurizr-core/test/unit/com/structurizr/model/PersonTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/PersonTests.java @@ -10,14 +10,14 @@ public class PersonTests extends AbstractWorkspaceTestBase { @Test public void test_getCanonicalName() { Person person = model.addPerson("Person", "Description"); - assertEquals("/Person", person.getCanonicalName()); + assertEquals("Person://Person", person.getCanonicalName()); } @Test - public void test_getCanonicalName_WhenNameContainsASlashCharacter() { + public void test_getCanonicalName_WhenNameContainsSlashAndDotCharacters() { Person person = model.addPerson("Person", "Description"); - person.setName("Name1/Name2"); - assertEquals("/Name1Name2", person.getCanonicalName()); + person.setName("Name1/.Name2"); + assertEquals("Person://Name1Name2", person.getCanonicalName()); } @Test diff --git a/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemInstanceTests.java b/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemInstanceTests.java new file mode 100644 index 000000000..0bebfe374 --- /dev/null +++ b/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemInstanceTests.java @@ -0,0 +1,180 @@ +package com.structurizr.model; + +import com.structurizr.AbstractWorkspaceTestBase; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class SoftwareSystemInstanceTests extends AbstractWorkspaceTestBase { + + private SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.External, "System", "Description"); + private DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node", "Description", "Technology"); + + @Test + public void test_construction() { + SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); + + assertSame(softwareSystem, instance.getSoftwareSystem()); + assertEquals(softwareSystem.getId(), instance.getSoftwareSystemId()); + assertEquals(1, instance.getInstanceId()); + } + + @Test + public void test_getSoftwareSystemId() { + SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); + + assertEquals(softwareSystem.getId(), instance.getSoftwareSystemId()); + instance.setSoftwareSystem(null); + instance.setSoftwareSystemId("1234"); + assertEquals("1234", instance.getSoftwareSystemId()); + } + + @Test + public void test_getName_CannotBeChanged() { + SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); + + assertEquals("System", instance.getName()); + + instance.setName("foo"); + assertEquals("System", instance.getName()); + } + + @Test + public void test_getCanonicalName() { + SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); + + assertEquals("SoftwareSystemInstance://Default/Deployment Node/System[1]", instance.getCanonicalName()); + } + + @Test + public void test_getParent_ReturnsTheParentDeploymentNode() { + SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); + + assertEquals(deploymentNode, instance.getParent()); + } + + @Test + public void test_getRequiredTags() { + SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); + + assertTrue(instance.getRequiredTags().isEmpty()); + } + + @Test + public void test_getTags() { + softwareSystem.addTags("Tag 1"); + SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); + instance.addTags("Primary Instance"); + + assertEquals("Software System Instance,Primary Instance", instance.getTags()); + } + + @Test + public void test_removeTags_DoesNotRemoveAnyTags() { + SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); + + assertTrue(instance.getTags().contains(Tags.SOFTWARE_SYSTEM_INSTANCE)); + + instance.removeTag(Tags.SOFTWARE_SYSTEM_INSTANCE); + + assertTrue(instance.getTags().contains(Tags.SOFTWARE_SYSTEM_INSTANCE)); + } + + @Test + public void test_addHealthCheck() { + SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); + assertTrue(instance.getHealthChecks().isEmpty()); + + HttpHealthCheck healthCheck = instance.addHealthCheck("Test web application is working", "http://localhost:8080"); + assertEquals("Test web application is working", healthCheck.getName()); + assertEquals("http://localhost:8080", healthCheck.getUrl()); + assertEquals(60, healthCheck.getInterval()); + assertEquals(0, healthCheck.getTimeout()); + assertEquals(1, instance.getHealthChecks().size()); + } + + @Test + public void test_addHealthCheck_ThrowsAnException_WhenTheNameIsNull() { + SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); + + try { + instance.addHealthCheck(null, "http://localhost"); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("The name must not be null or empty.", iae.getMessage()); + } + } + + @Test + public void test_addHealthCheck_ThrowsAnException_WhenTheNameIsEmpty() { + SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); + + try { + instance.addHealthCheck(" ", "http://localhost"); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("The name must not be null or empty.", iae.getMessage()); + } + } + + @Test + public void test_addHealthCheck_ThrowsAnException_WhenTheUrlIsNull() { + SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); + + try { + instance.addHealthCheck("Name", null); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("The URL must not be null or empty.", iae.getMessage()); + } + } + + @Test + public void test_addHealthCheck_ThrowsAnException_WhenTheUrlIsEmpty() { + SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); + + try { + instance.addHealthCheck("Name", " "); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("The URL must not be null or empty.", iae.getMessage()); + } + } + + @Test + public void test_addHealthCheck_ThrowsAnException_WhenTheUrlIsInvalid() { + SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); + + try { + instance.addHealthCheck("Name", "localhost"); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("localhost is not a valid URL.", iae.getMessage()); + } + } + + @Test + public void test_addHealthCheck_ThrowsAnException_WhenTheIntervalIsLessThanZero() { + SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); + + try { + instance.addHealthCheck("Name", "https://localhost", -1, 0); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("The polling interval must be zero or a positive integer.", iae.getMessage()); + } + } + + @Test + public void test_addHealthCheck_ThrowsAnException_WhenTheTimeoutIsLessThanZero() { + SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); + + try { + instance.addHealthCheck("Name", "https://localhost", 60, -1); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("The timeout must be zero or a positive integer.", iae.getMessage()); + } + } + +} \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemTests.java b/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemTests.java index bb1e64eb9..9d7e020bb 100644 --- a/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemTests.java @@ -165,13 +165,13 @@ public void test_getTags_IncludesSoftwareSystemByDefault() { @Test public void test_getCanonicalName() { SoftwareSystem system = model.addSoftwareSystem(Location.Internal, "System", "Description"); - assertEquals("/System", system.getCanonicalName()); + assertEquals("SoftwareSystem://System", system.getCanonicalName()); } @Test - public void test_getCanonicalName_WhenNameContainsASlashCharacter() { - SoftwareSystem softwareSystem = model.addSoftwareSystem("Name1/Name2", "Description"); - assertEquals("/Name1Name2", softwareSystem.getCanonicalName()); + public void test_getCanonicalName_WhenNameContainsSlashAndDotCharacters() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Name1/.Name2", "Description"); + assertEquals("SoftwareSystem://Name1Name2", softwareSystem.getCanonicalName()); } @Test From e5f7611d3dbe5cc133ad60da9d66c5b6a2a9d1da Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 17 Sep 2020 11:29:14 +0100 Subject: [PATCH 177/717] Fixed test. --- .../com/structurizr/api/WorkspaceRulesValidationTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java b/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java index d998cdb77..0baedba6d 100644 --- a/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java +++ b/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java @@ -18,7 +18,7 @@ public void test_exceptionThrown_WhenElementIdsAreNotUnique() throws Exception { WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "ElementIdsAreNotUnique.json")); fail(); } catch (WorkspaceValidationException we) { - assertTrue(we.getMessage().startsWith("The element softwaresystem://Software System ")); + assertTrue(we.getMessage().startsWith("The element SoftwareSystem://Software System ")); assertTrue(we.getMessage().endsWith(" has a non-unique ID of 1.")); } } From 2c6a322f67d659b7c4c6c05aae22b30574e79370 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 18 Sep 2020 10:41:24 +0100 Subject: [PATCH 178/717] Updated to reflect release. --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index da1473501..347044337 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 1.6.0 (unreleased) +## 1.6.0 (18th September 2020) - Changed the way that internal canonical element names are generated, to improve layout merging for deployment views. - getParent() of SoftwareSystemInstance and ContainerInstance now returns the parent deployment node. From d2272b726c39151770ec8aa3274bc582626c6e68 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 27 Sep 2020 14:13:44 +0100 Subject: [PATCH 179/717] Added a "recursive" option to the AutomaticDocumentationTemplate, so that sub-directories can optionally be scanned too. --- build.gradle | 2 +- docs/changelog.md | 4 +++ .../AutomaticDocumentationTemplate.java | 27 ++++++++++++++++++- .../AutomaticDocumentTemplateTests.java | 19 +++++++++++-- .../automatic/07-subdirectory/01-section-1.md | 1 + 5 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 structurizr-core/test/unit/com/structurizr/documentation/automatic/07-subdirectory/01-section-1.md diff --git a/build.gradle b/build.gradle index 8a0fad864..8252f0172 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.6.0' + version = '1.6.1' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 347044337..52df73a25 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.6.1 (27th September 2020) + +- Added a "recursive" option to the AutomaticDocumentationTemplate, so that sub-directories can optionally be scanned too. + ## 1.6.0 (18th September 2020) - Changed the way that internal canonical element names are generated, to improve layout merging for deployment views. diff --git a/structurizr-core/src/com/structurizr/documentation/AutomaticDocumentationTemplate.java b/structurizr-core/src/com/structurizr/documentation/AutomaticDocumentationTemplate.java index 5abb2eaa8..46712dfb6 100644 --- a/structurizr-core/src/com/structurizr/documentation/AutomaticDocumentationTemplate.java +++ b/structurizr-core/src/com/structurizr/documentation/AutomaticDocumentationTemplate.java @@ -5,6 +5,7 @@ import java.io.File; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; @@ -19,10 +20,30 @@ */ public class AutomaticDocumentationTemplate extends DocumentationTemplate { + private boolean recursive = false; + public AutomaticDocumentationTemplate(Workspace workspace) { super(workspace); } + /** + * Determines whether this template will scan directories recursively. + * + * @return true if this template will scan directories recursively, false otherwise + */ + public boolean isRecursive() { + return recursive; + } + + /** + * Sets whether this template will scan directories recursively. + * + * @param recursive true if this template should scan directories recursively, false otherwise + */ + public void setRecursive(boolean recursive) { + this.recursive = recursive; + } + /** * Adds all files in the specified directory, each in its own section. * @@ -76,7 +97,7 @@ private List
    add(SoftwareSystem softwareSystem, File directory) throws sectionDefinition = "=="; } - String content = new String(Files.readAllBytes(file.toPath()), "UTF-8"); + String content = new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8); String sectionName = file.getName(); Matcher matcher = Pattern.compile("^" + sectionDefinition + " (.*?)$", Pattern.MULTILINE).matcher(content); if (matcher.find()) { @@ -85,6 +106,10 @@ private List
    add(SoftwareSystem softwareSystem, File directory) throws Section section = addSection(softwareSystem, sectionName, format, content); sections.add(section); + } else if (file.isDirectory()) { + if (isRecursive()) { + sections.addAll(add(softwareSystem, file)); + } } } } diff --git a/structurizr-core/test/unit/com/structurizr/documentation/AutomaticDocumentTemplateTests.java b/structurizr-core/test/unit/com/structurizr/documentation/AutomaticDocumentTemplateTests.java index 8e620a993..cfe5c6ebe 100644 --- a/structurizr-core/test/unit/com/structurizr/documentation/AutomaticDocumentTemplateTests.java +++ b/structurizr-core/test/unit/com/structurizr/documentation/AutomaticDocumentTemplateTests.java @@ -56,8 +56,7 @@ public void test_addSections_ThrowsAnException_WhenTheDirectoryIsNotADirectory() } @Test - public void test_addSections() throws IOException { - Section section; + public void test_addSections_WithRecursiveSetToFalse() throws IOException { File root = new File(".//test/unit/com/structurizr/documentation/automatic"); List
    sections = template.addSections(softwareSystem, root); @@ -71,6 +70,22 @@ public void test_addSections() throws IOException { assertSection(softwareSystem, "Section 6", Format.AsciiDoc, "== Section 6", 6, sections.get(5)); } + @Test + public void test_addSections_WithRecursiveSetToTrue() throws IOException { + File root = new File(".//test/unit/com/structurizr/documentation/automatic"); + + template.setRecursive(true); + List
    sections = template.addSections(softwareSystem, root); + assertEquals(7, sections.size()); + + assertSection(softwareSystem, "Section 1", Format.Markdown, "## Section 1", 1, sections.get(0)); + assertSection(softwareSystem, "Section 2", Format.Markdown, "## Section 2", 2, sections.get(1)); + assertSection(softwareSystem, "Section 3", Format.Markdown, "## Section 3", 3, sections.get(2)); + assertSection(softwareSystem, "Section 4", Format.AsciiDoc, "== Section 4", 4, sections.get(3)); + assertSection(softwareSystem, "Section 5", Format.AsciiDoc, "== Section 5", 5, sections.get(4)); + assertSection(softwareSystem, "Section 6", Format.AsciiDoc, "== Section 6", 6, sections.get(5)); + } + private void assertSection(Element element, String title, Format format, String content, int order, Section section) { assertTrue(workspace.getDocumentation().getSections().contains(section)); assertEquals(element, section.getElement()); diff --git a/structurizr-core/test/unit/com/structurizr/documentation/automatic/07-subdirectory/01-section-1.md b/structurizr-core/test/unit/com/structurizr/documentation/automatic/07-subdirectory/01-section-1.md new file mode 100644 index 000000000..af328a67d --- /dev/null +++ b/structurizr-core/test/unit/com/structurizr/documentation/automatic/07-subdirectory/01-section-1.md @@ -0,0 +1 @@ +## Section 7 \ No newline at end of file From d8af09346dd2ea4a03f01979c57820c87097bd5c Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 9 Oct 2020 16:45:55 +0100 Subject: [PATCH 180/717] Resolves an issue with the AutomaticDocumentationTemplate, where images were being included as documentation content. --- build.gradle | 2 +- docs/changelog.md | 4 +++ .../AutomaticDocumentationTemplate.java | 36 ++++++++++--------- .../documentation/FormatFinder.java | 6 ++++ 4 files changed, 30 insertions(+), 18 deletions(-) diff --git a/build.gradle b/build.gradle index 8252f0172..f0aebcb71 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.6.1' + version = '1.6.2' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 52df73a25..ce7b01ca1 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.6.2 (unreleased) + +- Resolves an issue with the AutomaticDocumentationTemplate, where images were being included as documentation content. + ## 1.6.1 (27th September 2020) - Added a "recursive" option to the AutomaticDocumentationTemplate, so that sub-directories can optionally be scanned too. diff --git a/structurizr-core/src/com/structurizr/documentation/AutomaticDocumentationTemplate.java b/structurizr-core/src/com/structurizr/documentation/AutomaticDocumentationTemplate.java index 46712dfb6..529890fe1 100644 --- a/structurizr-core/src/com/structurizr/documentation/AutomaticDocumentationTemplate.java +++ b/structurizr-core/src/com/structurizr/documentation/AutomaticDocumentationTemplate.java @@ -88,24 +88,26 @@ private List
    add(SoftwareSystem softwareSystem, File directory) throws for (File file : filesInDirectory) { if (!file.isDirectory() && !file.getName().startsWith(".")) { - Format format = FormatFinder.findFormat(file); - String sectionDefinition = ""; - - if (format == Format.Markdown) { - sectionDefinition = "##"; - } else if (format == Format.AsciiDoc) { - sectionDefinition = "=="; - } - - String content = new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8); - String sectionName = file.getName(); - Matcher matcher = Pattern.compile("^" + sectionDefinition + " (.*?)$", Pattern.MULTILINE).matcher(content); - if (matcher.find()) { - sectionName = matcher.group(1); + if (FormatFinder.isMarkdownOrAsciiDoc(file)) { + Format format = FormatFinder.findFormat(file); + String sectionDefinition = ""; + + if (format == Format.Markdown) { + sectionDefinition = "##"; + } else if (format == Format.AsciiDoc) { + sectionDefinition = "=="; + } + + String content = new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8); + String sectionName = file.getName(); + Matcher matcher = Pattern.compile("^" + sectionDefinition + " (.*?)$", Pattern.MULTILINE).matcher(content); + if (matcher.find()) { + sectionName = matcher.group(1); + } + + Section section = addSection(softwareSystem, sectionName, format, content); + sections.add(section); } - - Section section = addSection(softwareSystem, sectionName, format, content); - sections.add(section); } else if (file.isDirectory()) { if (isRecursive()) { sections.addAll(add(softwareSystem, file)); diff --git a/structurizr-core/src/com/structurizr/documentation/FormatFinder.java b/structurizr-core/src/com/structurizr/documentation/FormatFinder.java index 426b1e4fd..35a2ef2bc 100644 --- a/structurizr-core/src/com/structurizr/documentation/FormatFinder.java +++ b/structurizr-core/src/com/structurizr/documentation/FormatFinder.java @@ -11,6 +11,12 @@ class FormatFinder { private static Set ASCIIDOC_EXTENSIONS = new HashSet<>(Arrays.asList(".asciidoc", ".adoc", ".asc")); + static boolean isMarkdownOrAsciiDoc(File file) { + String extension = file.getName().substring(file.getName().lastIndexOf(".")); + + return MARKDOWN_EXTENSIONS.contains(extension) || ASCIIDOC_EXTENSIONS.contains(extension); + } + static Format findFormat(File file) { if (file == null) { throw new IllegalArgumentException("A file must be specified."); From 5432db38741ecc56d757831fd1cd5c25ded16f15 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 10 Oct 2020 08:13:56 +0100 Subject: [PATCH 181/717] Updated to reflect release. --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index ce7b01ca1..41f952501 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 1.6.2 (unreleased) +## 1.6.2 (10th October 2020) - Resolves an issue with the AutomaticDocumentationTemplate, where images were being included as documentation content. From e09744687d7ace5a5d65ddf5a0d95f80c84e5b3e Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 27 Nov 2020 11:01:17 +0000 Subject: [PATCH 182/717] Suppress warnings for software system instances. --- structurizr-core/src/com/structurizr/Workspace.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structurizr-core/src/com/structurizr/Workspace.java b/structurizr-core/src/com/structurizr/Workspace.java index d8465e1b8..cc08e904f 100644 --- a/structurizr-core/src/com/structurizr/Workspace.java +++ b/structurizr-core/src/com/structurizr/Workspace.java @@ -203,7 +203,7 @@ public int countAndLogWarnings() { // find elements with a missing description getModel().getElements().stream() - .filter(e -> !(e instanceof ContainerInstance) && !(e instanceof DeploymentNode) && !(e instanceof InfrastructureNode)) + .filter(e -> !(e instanceof SoftwareSystemInstance) && !(e instanceof ContainerInstance) && !(e instanceof DeploymentNode) && !(e instanceof InfrastructureNode)) .filter(e -> e.getDescription() == null || e.getDescription().trim().length() == 0) .forEach(e -> warnings.add(e.getCanonicalName() + " is missing a description.")); From 6e17c257f6148c9bf9bcc4eca5b51c9ae1fff32b Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 27 Nov 2020 11:21:48 +0000 Subject: [PATCH 183/717] Added support a group property (experimental). --- .../model/StaticStructureElement.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/structurizr-core/src/com/structurizr/model/StaticStructureElement.java b/structurizr-core/src/com/structurizr/model/StaticStructureElement.java index cac51ce61..c05732c01 100644 --- a/structurizr-core/src/com/structurizr/model/StaticStructureElement.java +++ b/structurizr-core/src/com/structurizr/model/StaticStructureElement.java @@ -9,9 +9,29 @@ */ public abstract class StaticStructureElement extends Element { + private String group; + protected StaticStructureElement() { } + /** + * Gets the group in which this element should be included in (experimental feature). + * + * @return the group name, or null if not set + */ + public String getGroup() { + return group; + } + + /** + * Sets the group in which this element should be included in (experimental feature). + * + * @param group the group name + */ + public void setGroup(String group) { + this.group = group; + } + /** * Adds a unidirectional "uses" style relationship between this element and software system. * From 8fc50621720fba1723bff80c6e849a3bd5205737 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 27 Nov 2020 12:07:05 +0000 Subject: [PATCH 184/717] When adding a relationship to a dynamic view, the first relationship between the source and destination would be chosen, even if there are multiple relationships with different technologies. This commit resolves this, adding a way to indicate which relationship (based upon technology) should be chosen. --- .../src/com/structurizr/view/DynamicView.java | 40 ++++++++++++++++-- .../structurizr/view/DynamicViewTests.java | 42 ++++++++++++++++++- 2 files changed, 78 insertions(+), 4 deletions(-) diff --git a/structurizr-core/src/com/structurizr/view/DynamicView.java b/structurizr-core/src/com/structurizr/view/DynamicView.java index f97b6ea8b..0b2bfb11d 100644 --- a/structurizr-core/src/com/structurizr/view/DynamicView.java +++ b/structurizr-core/src/com/structurizr/view/DynamicView.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.structurizr.model.*; +import com.structurizr.util.StringUtils; import javax.annotation.Nonnull; import java.util.*; @@ -93,6 +94,10 @@ public RelationshipView add(@Nonnull Element source, @Nonnull Element destinatio } public RelationshipView add(@Nonnull Element source, String description, @Nonnull Element destination) { + return add(source, description, "", destination); + } + + public RelationshipView add(@Nonnull Element source, String description, String technology, @Nonnull Element destination) { if (source == null) { throw new IllegalArgumentException("A source element must be specified."); } @@ -105,7 +110,20 @@ public RelationshipView add(@Nonnull Element source, String description, @Nonnul checkElement(destination); // check that the relationship is in the model before adding it - Relationship relationship = source.getEfferentRelationshipWith(destination); + Relationship relationship = null; + + if (StringUtils.isNullOrEmpty(technology)) { + // no technology is specified, so just pick the first relationship we find + relationship = source.getEfferentRelationshipWith(destination); + } else { + Set relationships = source.getEfferentRelationshipsWith(destination); + for (Relationship rel : relationships) { + if (technology.equals(rel.getTechnology())) { + relationship = rel; + } + } + } + if (relationship != null) { addElement(source, false); addElement(destination, false); @@ -113,14 +131,30 @@ public RelationshipView add(@Nonnull Element source, String description, @Nonnul return addRelationship(relationship, description, sequenceNumber.getNext(), false); } else { // perhaps model this as a return/reply/response message instead, if the reverse relationship exists - relationship = destination.getEfferentRelationshipWith(source); + + if (StringUtils.isNullOrEmpty(technology)) { + // no technology is specified, so just pick the first relationship we find + relationship = destination.getEfferentRelationshipWith(source); + } else { + Set relationships = destination.getEfferentRelationshipsWith(source); + for (Relationship rel : relationships) { + if (technology.equals(rel.getTechnology())) { + relationship = rel; + } + } + } + if (relationship != null) { addElement(source, false); addElement(destination, false); return addRelationship(relationship, description, sequenceNumber.getNext(), true); } else { - throw new IllegalArgumentException("A relationship between " + source.getName() + " and " + destination.getName() + " does not exist in model."); + if (StringUtils.isNullOrEmpty(technology)) { + throw new IllegalArgumentException("A relationship between " + source.getName() + " and " + destination.getName() + " does not exist in model."); + } else { + throw new IllegalArgumentException("A relationship between " + source.getName() + " and " + destination.getName() + " with technology " + technology + " does not exist in model."); + } } } } diff --git a/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java b/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java index 16d35b856..2f63c58f3 100644 --- a/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java @@ -44,7 +44,7 @@ public void setup() { } @Test - public void test_add_ThrowsAnException_WhenPassedANullSouceElement() { + public void test_add_ThrowsAnException_WhenPassedANullSourceElement() { try { DynamicView dynamicView = workspace.getViews().createDynamicView("key", "Description"); dynamicView.add(null, softwareSystemA); @@ -204,6 +204,26 @@ public void test_add_ThrowsAnException_WhenARelationshipBetweenTheSourceAndDesti } } + @Test + public void test_add_ThrowsAnException_WhenARelationshipBetweenTheSourceAndDestinationElementsWithTheSpecifiedTechnologyDoesNotExist() { + try { + workspace = new Workspace("Name", "Description"); + model = workspace.getModel(); + + SoftwareSystem ss1 = workspace.getModel().addSoftwareSystem("Software System 1", ""); + SoftwareSystem ss2 = workspace.getModel().addSoftwareSystem("Software System 2", ""); + ss1.uses(ss2, "Uses 1", "Tech 1"); + + DynamicView view = workspace.getViews().createDynamicView("key", "Description"); + + view.add(ss1, "Uses", "Tech 1", ss2); + view.add(ss1, "Uses", "Tech 2", ss2); + fail(); + } catch (Exception e) { + assertEquals("A relationship between Software System 1 and Software System 2 with technology Tech 2 does not exist in model.", e.getMessage()); + } + } + @Test public void test_add_AddsTheSourceAndDestinationElements_WhenARelationshipBetweenThemExists() { final DynamicView dynamicView = workspace.getViews().createDynamicView(softwareSystemA, "key", "Description"); @@ -241,6 +261,26 @@ public void test_normalSequence() { assertSame(container3, view.getRelationships().stream().filter(r -> r.getOrder().equals("2")).findFirst().get().getRelationship().getDestination()); } + @Test + public void test_normalSequence_WhenThereAreMultipleTechnologies() { + workspace = new Workspace("Name", "Description"); + model = workspace.getModel(); + + SoftwareSystem ss1 = workspace.getModel().addSoftwareSystem("Software System 1", ""); + SoftwareSystem ss2 = workspace.getModel().addSoftwareSystem("Software System 2", ""); + + Relationship r1 = ss1.uses(ss2, "Uses 1", "Tech 1"); + Relationship r2 = ss1.uses(ss2, "Uses 2", "Tech 2"); + + DynamicView view = workspace.getViews().createDynamicView("key", "Description"); + + RelationshipView rv1 = view.add(ss1, "Uses", "Tech 1", ss2); + RelationshipView rv2 = view.add(ss1, "Uses", "Tech 2", ss2); + + assertSame(r1, rv1.getRelationship()); + assertSame(r2, rv2.getRelationship()); + } + @Test public void test_parallelSequence() { workspace = new Workspace("Name", "Description"); From a0ea678bf61f1debe92038672c4c3297d724053a Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 27 Nov 2020 12:07:20 +0000 Subject: [PATCH 185/717] Bumped version. --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index f0aebcb71..72f77becf 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.6.2' + version = '1.6.3' repositories { mavenCentral() From 1cdf8596bd7b40b8f9c92b572c65ff94ca5d2095 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 30 Nov 2020 14:23:20 +0000 Subject: [PATCH 186/717] Updated changelog with release details. --- docs/changelog.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 41f952501..b499868c2 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,11 @@ # Changelog +## 1.6.3 (30th November 2020) + +- When adding a relationship to a dynamic view, the first relationship between the source and destination would be chosen, even if there are multiple relationships with different technologies. This release adds a way to indicate which relationship (based upon technology) should be chosen. +- Suppress description warnings for software system instances. + + ## 1.6.2 (10th October 2020) - Resolves an issue with the AutomaticDocumentationTemplate, where images were being included as documentation content. From cf7ae40a120abb14b45433976df77b24ea52de8f Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 30 Nov 2020 14:25:51 +0000 Subject: [PATCH 187/717] Updated docs. --- docs/binaries.md | 4 ++-- docs/getting-started.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/binaries.md b/docs/binaries.md index 35facee52..ba3633c8f 100644 --- a/docs/binaries.md +++ b/docs/binaries.md @@ -3,5 +3,5 @@ The "Structurizr for Java" binaries are hosted on [Maven Central](https://repo1. Name | Description ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-core:1.4.5 | The core library that can used to create software architecture models. -com.structurizr:structurizr-client:1.4.5 | The API client for publishing models on the Structurizr cloud service and on-premises installation. \ No newline at end of file +com.structurizr:structurizr-core:1.6.3 | The core library that can used to create software architecture models. +com.structurizr:structurizr-client:1.6.3 | The API client for publishing models on the Structurizr cloud service and on-premises installation. \ No newline at end of file diff --git a/docs/getting-started.md b/docs/getting-started.md index bc78cf1a5..d491b4868 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -12,7 +12,7 @@ The Structurizr for Java binaries are hosted on [Maven Central](https://repo1.ma Name | Description ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-client:1.4.5 | The Structurizr API client library. +com.structurizr:structurizr-client:1.6.3 | The Structurizr API client library. ## 2. Create a Java program From de1c7c71444f0da6adabd3fb111dbbeb6ab5b922 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 2 Dec 2020 09:05:16 +0000 Subject: [PATCH 188/717] Removes the restrictions related to adding containers/components outside the scoped software system/container. --- build.gradle | 2 +- docs/changelog.md | 4 +++ .../src/com/structurizr/view/DynamicView.java | 18 +++------- .../structurizr/view/DynamicViewTests.java | 33 ------------------- 4 files changed, 10 insertions(+), 47 deletions(-) diff --git a/build.gradle b/build.gradle index 72f77becf..e2c2ea177 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.6.3' + version = '1.6.4' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index b499868c2..02affe25e 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.6.4 (unreleased) + +Removes the restrictions related to adding containers/components outside the scoped software system/container. + ## 1.6.3 (30th November 2020) - When adding a relationship to a dynamic view, the first relationship between the source and destination would be chosen, even if there are multiple relationships with different technologies. This release adds a way to indicate which relationship (based upon technology) should be chosen. diff --git a/structurizr-core/src/com/structurizr/view/DynamicView.java b/structurizr-core/src/com/structurizr/view/DynamicView.java index 0b2bfb11d..bc7041054 100644 --- a/structurizr-core/src/com/structurizr/view/DynamicView.java +++ b/structurizr-core/src/com/structurizr/view/DynamicView.java @@ -184,33 +184,25 @@ private void checkElement(Element elementToBeAdded) { } // if the scope of this dynamic view is a software system, we only want: - // - containers inside that software system + // - containers // - other software systems if (element instanceof SoftwareSystem) { if (elementToBeAdded.equals(element)) { throw new IllegalArgumentException(elementToBeAdded.getName() + " is already the scope of this view and cannot be added to it."); } - if (elementToBeAdded instanceof Container && !elementToBeAdded.getParent().equals(element)) { - throw new IllegalArgumentException("Only containers that reside inside " + element.getName() + " can be added to this view."); - } + if (elementToBeAdded instanceof Component) { throw new IllegalArgumentException("Components can't be added to a dynamic view when the scope is a software system."); } } - // if the scope of this dynamic view is a container, we only want other containers inside the same software system - // and other components inside the container + // if the scope of this dynamic view is a container, we only want: + // - other containers + // - components if (element instanceof Container) { if (elementToBeAdded.equals(element) || elementToBeAdded.equals(element.getParent())) { throw new IllegalArgumentException(elementToBeAdded.getName() + " is already the scope of this view and cannot be added to it."); } - if (elementToBeAdded instanceof Container && !elementToBeAdded.getParent().equals(element.getParent())) { - throw new IllegalArgumentException("Only containers that reside inside " + element.getParent().getName() + " can be added to this view."); - } - - if (elementToBeAdded instanceof Component && !elementToBeAdded.getParent().equals(element)) { - throw new IllegalArgumentException("Only components that reside inside " + element.getName() + " can be added to this view."); - } } if (element == null) { diff --git a/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java b/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java index 2f63c58f3..80541b3da 100644 --- a/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java @@ -114,17 +114,6 @@ public void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsNotSpecifie } } - @Test - public void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsASoftwareSystemButAContainerInAnotherSoftwareSystemIsAdded() { - try { - DynamicView dynamicView = workspace.getViews().createDynamicView(softwareSystemA, "key", "Description"); - dynamicView.add(containerB1, containerA1); - fail(); - } catch (Exception e) { - assertEquals("Only containers that reside inside Software System A can be added to this view.", e.getMessage()); - } - } - @Test public void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsASoftwareSystemButAComponentIsAdded() { try { @@ -169,28 +158,6 @@ public void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsAContainerA } } - @Test - public void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsAContainerAndAContainerInAnotherSoftwareSystemIsAdded() { - try { - DynamicView dynamicView = workspace.getViews().createDynamicView(containerA1, "key", "Description"); - dynamicView.add(containerB1, containerA2); - fail(); - } catch (Exception e) { - assertEquals("Only containers that reside inside Software System A can be added to this view.", e.getMessage()); - } - } - - @Test - public void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsAContainerAndAComponentInAnotherContainerIsAdded() { - try { - DynamicView dynamicView = workspace.getViews().createDynamicView(containerA1, "key", "Description"); - dynamicView.add(componentA2, containerA2); - fail(); - } catch (Exception e) { - assertEquals("Only components that reside inside Container A1 can be added to this view.", e.getMessage()); - } - } - @Test public void test_add_ThrowsAnException_WhenARelationshipBetweenTheSourceAndDestinationElementsDoesNotExist() { try { From d21943609be36d5e34e89c67fb74c4ab16f143fe Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 2 Dec 2020 09:05:40 +0000 Subject: [PATCH 189/717] Adds an "externalBoundariesVisible" property to DynamicView, so that external software system/container boundaries can be shown/hidden. --- docs/changelog.md | 1 + .../src/com/structurizr/view/DynamicView.java | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 02affe25e..a6c2a2b71 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -3,6 +3,7 @@ ## 1.6.4 (unreleased) Removes the restrictions related to adding containers/components outside the scoped software system/container. +Adds an "externalBoundariesVisible" property to DynamicView, so that external software system/container boundaries can be shown/hidden. ## 1.6.3 (30th November 2020) diff --git a/structurizr-core/src/com/structurizr/view/DynamicView.java b/structurizr-core/src/com/structurizr/view/DynamicView.java index bc7041054..513a98355 100644 --- a/structurizr-core/src/com/structurizr/view/DynamicView.java +++ b/structurizr-core/src/com/structurizr/view/DynamicView.java @@ -17,6 +17,8 @@ public final class DynamicView extends View { private Element element; private String elementId; + private boolean externalBoundariesVisible = false; + private SequenceNumber sequenceNumber = new SequenceNumber(); DynamicView() { @@ -285,4 +287,22 @@ private boolean isNumeric(String str) { } } + /** + * Determines whether software system/container boundaries should be visible for "external" containers/components (those outside the element in scope). + * + * @return true if external boundaries are visible, false otherwise + */ + public boolean getExternalBoundariesVisible() { + return externalBoundariesVisible; + } + + /** + * Sets whether software system/container boundaries should be visible for "external" containers/components (those outside the element in scope). + * + * @param externalBoundariesVisible true if external boundaries should be visible, false otherwise + */ + public void setExternalBoundariesVisible(boolean externalBoundariesVisible) { + this.externalBoundariesVisible = externalBoundariesVisible; + } + } \ No newline at end of file From 04902b230cb2a0c375f1d2397bc42df9d079d38a Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 22 Dec 2020 22:11:11 +0000 Subject: [PATCH 190/717] Enhanced the rules relating to whether elements can be added to a view or not. --- docs/changelog.md | 8 +- .../com/structurizr/view/ComponentView.java | 41 +++++++- .../com/structurizr/view/ContainerView.java | 44 ++++++--- .../com/structurizr/view/DeploymentView.java | 5 + .../src/com/structurizr/view/DynamicView.java | 93 ++++++++++--------- .../ElementNotPermittedInViewException.java | 9 ++ .../src/com/structurizr/view/StaticView.java | 44 ++++++--- .../structurizr/view/SystemContextView.java | 9 ++ .../structurizr/view/SystemLandscapeView.java | 9 ++ .../src/com/structurizr/view/View.java | 26 +++++- .../structurizr/view/ComponentViewTests.java | 47 ++++------ .../structurizr/view/ContainerViewTests.java | 12 +-- .../structurizr/view/DynamicViewTests.java | 57 ++++++++++-- 13 files changed, 282 insertions(+), 122 deletions(-) create mode 100644 structurizr-core/src/com/structurizr/view/ElementNotPermittedInViewException.java diff --git a/docs/changelog.md b/docs/changelog.md index a6c2a2b71..354e34da0 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,16 +1,16 @@ # Changelog -## 1.6.4 (unreleased) +## 1.7.0 (unreleased) -Removes the restrictions related to adding containers/components outside the scoped software system/container. -Adds an "externalBoundariesVisible" property to DynamicView, so that external software system/container boundaries can be shown/hidden. +- Removes the dynamic view restrictions related to adding containers/components outside the scoped software system/container. +- Adds an "externalBoundariesVisible" property to DynamicView, so that external software system/container boundaries can be shown/hidden. +- Enhanced the rules relating to whether elements can be added to a view or not. ## 1.6.3 (30th November 2020) - When adding a relationship to a dynamic view, the first relationship between the source and destination would be chosen, even if there are multiple relationships with different technologies. This release adds a way to indicate which relationship (based upon technology) should be chosen. - Suppress description warnings for software system instances. - ## 1.6.2 (10th October 2020) - Resolves an issue with the AutomaticDocumentationTemplate, where images were being included as documentation content. diff --git a/structurizr-core/src/com/structurizr/view/ComponentView.java b/structurizr-core/src/com/structurizr/view/ComponentView.java index 45b864d0b..c43b3a1a7 100644 --- a/structurizr-core/src/com/structurizr/view/ComponentView.java +++ b/structurizr-core/src/com/structurizr/view/ComponentView.java @@ -92,7 +92,13 @@ public void add(@Nonnull SoftwareSystem softwareSystem, boolean addRelationships * Adds all other containers in the software system to this view. */ public void addAllContainers() { - getSoftwareSystem().getContainers().forEach(this::add); + getSoftwareSystem().getContainers().forEach(c -> { + try { + add(c); + } catch (ElementNotPermittedInViewException e) { + // ignore + } + }); } /** @@ -111,9 +117,7 @@ public void add(Container container) { * @param addRelationships whether to add relationships to/from the container */ public void add(Container container, boolean addRelationships) { - if (container != null && !container.equals(getContainer())) { - addElement(container, addRelationships); - } + addElement(container, addRelationships); } /** @@ -277,6 +281,35 @@ private void addExternalDependency(Element element, Set components) { } } + @Override + protected void checkElementCanBeAdded(Element element) { + if (element instanceof Person) { + return; + } + + if (element instanceof SoftwareSystem) { + if (element.equals(getContainer().getParent())) { + throw new ElementNotPermittedInViewException("The software system in scope cannot be added to a component view."); + } else { + return; + } + } + + if (element instanceof Container) { + if (element.equals(getContainer())) { + throw new ElementNotPermittedInViewException("The container in scope cannot be added to a component view."); + } else { + return; + } + } + + if (element instanceof Component) { + return; + } + + throw new ElementNotPermittedInViewException("Only people, software systems, containers, and components can be added to a component view."); + } + @Override protected boolean canBeRemoved(Element element) { return true; diff --git a/structurizr-core/src/com/structurizr/view/ContainerView.java b/structurizr-core/src/com/structurizr/view/ContainerView.java index 9c4313481..b02858168 100644 --- a/structurizr-core/src/com/structurizr/view/ContainerView.java +++ b/structurizr-core/src/com/structurizr/view/ContainerView.java @@ -32,25 +32,17 @@ public void add(@Nonnull SoftwareSystem softwareSystem) { add(softwareSystem, true); } - /** - * Adds a software system to this view. Please note that you cannot add the software system - * that is the scope of this view. - * - * @param softwareSystem the SoftwareSystem to add - * @param addRelationships whether to add relationships to/from the software system - */ - @Override - public void add(@Nonnull SoftwareSystem softwareSystem, boolean addRelationships) { - if (softwareSystem != null && !softwareSystem.equals(getSoftwareSystem())) { - addElement(softwareSystem, addRelationships); - } - } - /** * Adds all containers within the software system in scope to this view. */ public void addAllContainers() { - getSoftwareSystem().getContainers().forEach(this::add); + getSoftwareSystem().getContainers().forEach(c -> { + try { + add(c); + } catch (ElementNotPermittedInViewException e) { + // ignore + } + }); } /** @@ -182,6 +174,28 @@ public final void addDependentSoftwareSystems() { .forEach(this::add); } + @Override + protected void checkElementCanBeAdded(Element element) { + if (element instanceof Person) { + return; + } + + if (element instanceof SoftwareSystem) { + if (element.equals(getSoftwareSystem())) { + throw new ElementNotPermittedInViewException("The software system in scope cannot be added to a container view."); + } else { + return; + } + } + + if (element instanceof Container) { + return; + } + + throw new ElementNotPermittedInViewException("Only people, software systems, and containers can be added to a container view."); + } + + @Override protected boolean canBeRemoved(Element element) { return true; diff --git a/structurizr-core/src/com/structurizr/view/DeploymentView.java b/structurizr-core/src/com/structurizr/view/DeploymentView.java index b77f78968..8241e9858 100644 --- a/structurizr-core/src/com/structurizr/view/DeploymentView.java +++ b/structurizr-core/src/com/structurizr/view/DeploymentView.java @@ -210,6 +210,11 @@ public void setEnvironment(String environment) { this.environment = environment; } + @Override + protected void checkElementCanBeAdded(Element element) { + + } + @Override protected boolean canBeRemoved(Element element) { return true; diff --git a/structurizr-core/src/com/structurizr/view/DynamicView.java b/structurizr-core/src/com/structurizr/view/DynamicView.java index 513a98355..186c33c35 100644 --- a/structurizr-core/src/com/structurizr/view/DynamicView.java +++ b/structurizr-core/src/com/structurizr/view/DynamicView.java @@ -6,6 +6,7 @@ import javax.annotation.Nonnull; import java.util.*; +import java.util.stream.Collectors; /** * A dynamic view, used to describe behaviour between static elements at runtime. @@ -108,8 +109,8 @@ public RelationshipView add(@Nonnull Element source, String description, String throw new IllegalArgumentException("A destination element must be specified."); } - checkElement(source); - checkElement(destination); + checkElementCanBeAdded(source); + checkElementCanBeAdded(destination); // check that the relationship is in the model before adding it Relationship relationship = null; @@ -172,48 +173,6 @@ protected RelationshipView addRelationship(Relationship relationship, String des return relationshipView; } - /** - * This checks that only appropriate elements can be added to the view. - */ - private void checkElement(Element elementToBeAdded) { - if (!(elementToBeAdded instanceof Person) && !(elementToBeAdded instanceof SoftwareSystem) && !(elementToBeAdded instanceof Container) && !(elementToBeAdded instanceof Component)) { - throw new IllegalArgumentException("Only people, software systems, containers and components can be added to dynamic views."); - } - - // people can always be added - if (elementToBeAdded instanceof Person) { - return; - } - - // if the scope of this dynamic view is a software system, we only want: - // - containers - // - other software systems - if (element instanceof SoftwareSystem) { - if (elementToBeAdded.equals(element)) { - throw new IllegalArgumentException(elementToBeAdded.getName() + " is already the scope of this view and cannot be added to it."); - } - - if (elementToBeAdded instanceof Component) { - throw new IllegalArgumentException("Components can't be added to a dynamic view when the scope is a software system."); - } - } - - // if the scope of this dynamic view is a container, we only want: - // - other containers - // - components - if (element instanceof Container) { - if (elementToBeAdded.equals(element) || elementToBeAdded.equals(element.getParent())) { - throw new IllegalArgumentException(elementToBeAdded.getName() + " is already the scope of this view and cannot be added to it."); - } - } - - if (element == null) { - if (!(elementToBeAdded instanceof SoftwareSystem)) { - throw new IllegalArgumentException("Only people and software systems can be added to this dynamic view."); - } - } - } - /** * Gets the (computed) name of this view. * @@ -250,6 +209,52 @@ public void endParallelSequence(boolean endAllParallelSequencesAndContinueNumber sequenceNumber.endParallelSequence(endAllParallelSequencesAndContinueNumbering); } + @Override + protected void checkElementCanBeAdded(Element elementToBeAdded) { + if (!(elementToBeAdded instanceof Person) && !(elementToBeAdded instanceof SoftwareSystem) && !(elementToBeAdded instanceof Container) && !(elementToBeAdded instanceof Component)) { + throw new ElementNotPermittedInViewException("Only people, software systems, containers and components can be added to dynamic views."); + } + + // people can always be added + if (elementToBeAdded instanceof Person) { + return; + } + + // if the scope of this dynamic view is a software system, we only want: + // - containers + // - other software systems + if (element instanceof SoftwareSystem) { + if (elementToBeAdded.equals(element)) { + throw new ElementNotPermittedInViewException(elementToBeAdded.getName() + " is already the scope of this view and cannot be added to it."); + } + + if (elementToBeAdded instanceof SoftwareSystem || elementToBeAdded instanceof Container) { + checkParentAndChildrenHaveNotAlreadyBeenAdded(elementToBeAdded); + } else if (elementToBeAdded instanceof Component) { + throw new ElementNotPermittedInViewException("Components can't be added to a dynamic view when the scope is a software system."); + } + } + + // dynamic view with container scope: + // - other containers + // - components + if (element instanceof Container) { + if (elementToBeAdded.equals(element) || elementToBeAdded.equals(element.getParent())) { + throw new ElementNotPermittedInViewException(elementToBeAdded.getName() + " is already the scope of this view and cannot be added to it."); + } + + checkParentAndChildrenHaveNotAlreadyBeenAdded(elementToBeAdded); + } + + // dynamic view with no scope + // - software systems + if (element == null) { + if (!(elementToBeAdded instanceof SoftwareSystem)) { + throw new ElementNotPermittedInViewException("Only people and software systems can be added to this dynamic view."); + } + } + } + @Override protected boolean canBeRemoved(Element element) { return true; diff --git a/structurizr-core/src/com/structurizr/view/ElementNotPermittedInViewException.java b/structurizr-core/src/com/structurizr/view/ElementNotPermittedInViewException.java new file mode 100644 index 000000000..aae65d5a7 --- /dev/null +++ b/structurizr-core/src/com/structurizr/view/ElementNotPermittedInViewException.java @@ -0,0 +1,9 @@ +package com.structurizr.view; + +public class ElementNotPermittedInViewException extends RuntimeException { + + ElementNotPermittedInViewException(String message) { + super(message); + } + +} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/view/StaticView.java b/structurizr-core/src/com/structurizr/view/StaticView.java index 37b3b5dfb..980f3e5c9 100644 --- a/structurizr-core/src/com/structurizr/view/StaticView.java +++ b/structurizr-core/src/com/structurizr/view/StaticView.java @@ -34,7 +34,13 @@ public abstract class StaticView extends View { * Adds all software systems in the model to this view. */ public void addAllSoftwareSystems() { - getModel().getSoftwareSystems().forEach(this::add); + getModel().getSoftwareSystems().forEach(ss -> { + try { + add(ss); + } catch (ElementNotPermittedInViewException e) { + // ignore + } + }); } /** @@ -127,16 +133,32 @@ protected void addNearestNeighbours(Element element, Class relationships = getModel().getRelationships(); - relationships.stream().filter(r -> r.getSource().equals(element) && typeOfElement.isInstance(r.getDestination())) - .map(Relationship::getDestination) - .forEach(d -> addElement(d, true)); - - relationships.stream().filter(r -> r.getDestination().equals(element) && typeOfElement.isInstance(r.getSource())) - .map(Relationship::getSource) - .forEach(s -> addElement(s, true)); + try { + addElement(element, true); + + Set relationships = getModel().getRelationships(); + relationships.stream().filter(r -> r.getSource().equals(element) && typeOfElement.isInstance(r.getDestination())) + .map(Relationship::getDestination) + .forEach(d -> { + try { + addElement(d, true); + } catch (ElementNotPermittedInViewException e) { + System.out.println(e.getMessage() + " (ignoring " + d.getName() + ")"); + } + }); + + relationships.stream().filter(r -> r.getDestination().equals(element) && typeOfElement.isInstance(r.getSource())) + .map(Relationship::getSource) + .forEach(s -> { + try { + addElement(s, true); + } catch (ElementNotPermittedInViewException e) { + System.out.println(e.getMessage() + " (ignoring " + s.getName() + ")"); + } + }); + } catch (ElementNotPermittedInViewException e) { + System.out.println(e.getMessage() + " (ignoring " + element.getName() + ")"); + } } /** diff --git a/structurizr-core/src/com/structurizr/view/SystemContextView.java b/structurizr-core/src/com/structurizr/view/SystemContextView.java index c8f6e804b..e82d192a5 100644 --- a/structurizr-core/src/com/structurizr/view/SystemContextView.java +++ b/structurizr-core/src/com/structurizr/view/SystemContextView.java @@ -88,6 +88,15 @@ public void setEnterpriseBoundaryVisible(boolean enterpriseBoundaryVisible) { this.enterpriseBoundaryVisible = enterpriseBoundaryVisible; } + @Override + protected void checkElementCanBeAdded(Element element) { + if (element instanceof Person || element instanceof SoftwareSystem) { + // all good + } else { + throw new ElementNotPermittedInViewException("Only people and software systems can be added to a system context view."); + } + } + @Override protected boolean canBeRemoved(Element element) { return !getSoftwareSystem().equals(element); diff --git a/structurizr-core/src/com/structurizr/view/SystemLandscapeView.java b/structurizr-core/src/com/structurizr/view/SystemLandscapeView.java index bdde6c34e..bb8e12699 100644 --- a/structurizr-core/src/com/structurizr/view/SystemLandscapeView.java +++ b/structurizr-core/src/com/structurizr/view/SystemLandscapeView.java @@ -105,6 +105,15 @@ public void setEnterpriseBoundaryVisible(boolean enterpriseBoundaryVisible) { this.enterpriseBoundaryVisible = enterpriseBoundaryVisible; } + @Override + protected void checkElementCanBeAdded(Element element) { + if (element instanceof Person || element instanceof SoftwareSystem) { + // all good + } else { + throw new ElementNotPermittedInViewException("Only people and software systems can be added to a system landscape view."); + } + } + @Override protected boolean canBeRemoved(Element element) { return true; diff --git a/structurizr-core/src/com/structurizr/view/View.java b/structurizr-core/src/com/structurizr/view/View.java index 69965e3cc..e1768a105 100644 --- a/structurizr-core/src/com/structurizr/view/View.java +++ b/structurizr-core/src/com/structurizr/view/View.java @@ -2,10 +2,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonSetter; -import com.structurizr.model.Element; -import com.structurizr.model.Model; -import com.structurizr.model.Relationship; -import com.structurizr.model.SoftwareSystem; +import com.structurizr.model.*; import com.structurizr.util.StringUtils; import javax.annotation.Nonnull; @@ -228,6 +225,7 @@ protected final void addElement(Element element, boolean addRelationships) { } if (getModel().contains(element)) { + checkElementCanBeAdded(element); elementViews.add(new ElementView(element)); if (addRelationships) { @@ -238,6 +236,8 @@ protected final void addElement(Element element, boolean addRelationships) { } } + protected abstract void checkElementCanBeAdded(Element element); + private void addRelationships(Element element) { Set elements = getElements().stream() .map(ElementView::getElement) @@ -434,4 +434,22 @@ public ViewSet getViewSet() { protected abstract boolean canBeRemoved(Element element); + protected void checkParentAndChildrenHaveNotAlreadyBeenAdded(Element elementToBeAdded) { + // check the parent hasn't been added already + Set elementIds = getElements().stream().map(ElementView::getElement).map(Element::getId).collect(Collectors.toSet()); + + if (elementToBeAdded.getParent() != null) { + if (elementIds.contains(elementToBeAdded.getParent().getId())) { + throw new ElementNotPermittedInViewException("The parent of " + elementToBeAdded.getName() + " is already in this view."); + } + } + + // and now check a child hasn't been added already + Set elementParentIds = getElements().stream().map(ElementView::getElement).filter(e -> e.getParent() != null).map(e -> e.getParent().getId()).collect(Collectors.toSet()); + + if (elementParentIds.contains(elementToBeAdded.getId())) { + throw new ElementNotPermittedInViewException("The child of " + elementToBeAdded.getName() + " is already in this view."); + } + } + } \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/view/ComponentViewTests.java b/structurizr-core/test/unit/com/structurizr/view/ComponentViewTests.java index 71afc5e71..7f5c9136c 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ComponentViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ComponentViewTests.java @@ -140,10 +140,15 @@ public void test_addAllComponents_AddsAllComponents_WhenThereAreSomeComponents() } @Test - public void test_add_DoesNothing_WhenANullContainerIsSpecified() { - assertEquals(0, view.getElements().size()); - view.add((Container) null); + public void test_add_ThrowsAnException_WhenANullContainerIsSpecified() { assertEquals(0, view.getElements().size()); + + try { + view.add((Container) null); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("An element must be specified.", iae.getMessage()); + } } @Test @@ -248,10 +253,13 @@ public void test_add_ThrowsAnException_WhenTheSpecifiedComponentIsInADifferentCo } @Test - public void test_add_DoesNothing_WhenTheContainerOfTheViewIsAdded() { - assertEquals("the container itself is not added to the view", 0, view.getElements().stream().map(e -> e.getElement()).filter(e -> e.equals(webApplication)).count()); - view.add(webApplication); - assertEquals("the container itself is not added to the view", 0, view.getElements().stream().map(e -> e.getElement()).filter(e -> e.equals(webApplication)).count()); + public void test_add_ThrowsAnException_WhenTheContainerOfTheViewIsAdded() { + try { + view.add(webApplication); + fail(); + } catch (ElementNotPermittedInViewException e) { + assertEquals("The container in scope cannot be added to a component view.", e.getMessage()); + } } @Test @@ -325,8 +333,11 @@ public void test_addNearestNeightbours_DoesNothing_WhenANullElementIsSpecified() @Test public void test_addNearestNeighbours_DoesNothing_WhenThereAreNoNeighbours() { - view.addNearestNeighbours(softwareSystem); + Component component = webApplication.addComponent("Component", "", ""); + view.add(component); + assertEquals(1, view.getElements().size()); + view.addNearestNeighbours(component); assertEquals(1, view.getElements().size()); } @@ -362,32 +373,14 @@ public void test_addNearestNeighbours_AddsNearestNeighbours_WhenThereAreSomeNear // userA -> systemA -> controller -> service -> systemB -> userB service.uses(softwareSystemB, ""); - view.addNearestNeighbours(softwareSystem); - - assertEquals(3, view.getElements().size()); - assertTrue(view.getElements().contains(new ElementView(softwareSystemA))); - assertTrue(view.getElements().contains(new ElementView(softwareSystem))); - assertTrue(view.getElements().contains(new ElementView(softwareSystemB))); - view = new ComponentView(webApplication, "components", "Description"); view.addNearestNeighbours(softwareSystemA); - assertEquals(5, view.getElements().size()); + assertEquals(3, view.getElements().size()); assertTrue(view.getElements().contains(new ElementView(userA))); assertTrue(view.getElements().contains(new ElementView(softwareSystemA))); - assertTrue(view.getElements().contains(new ElementView(softwareSystem))); - assertTrue(view.getElements().contains(new ElementView(webApplication))); assertTrue(view.getElements().contains(new ElementView(controller))); - view = new ComponentView(webApplication, "components", "Description"); - view.addNearestNeighbours(webApplication); - - assertEquals(4, view.getElements().size()); - assertTrue(view.getElements().contains(new ElementView(softwareSystemA))); - assertTrue(view.getElements().contains(new ElementView(webApplication))); - assertTrue(view.getElements().contains(new ElementView(database))); - assertTrue(view.getElements().contains(new ElementView(softwareSystemB))); - view = new ComponentView(webApplication, "components", "Description"); view.addNearestNeighbours(controller); diff --git a/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java b/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java index d2eab3813..9ae2baecf 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java @@ -124,7 +124,7 @@ public void test_addNearestNeightbours_DoesNothing_WhenANullElementIsSpecified() public void test_addNearestNeighbours_DoesNothing_WhenThereAreNoNeighbours() { view.addNearestNeighbours(softwareSystem); - assertEquals(1, view.getElements().size()); + assertEquals(0, view.getElements().size()); } @Test @@ -160,20 +160,20 @@ public void test_addNearestNeighbours_AddsNearestNeighbours_WhenThereAreSomeNear // userA -> systemA -> controller -> service -> systemB -> userB service.uses(softwareSystemB, ""); - view.addNearestNeighbours(softwareSystem); + view.addNearestNeighbours(webApplication); - assertEquals(3, view.getElements().size()); + assertEquals(4, view.getElements().size()); assertTrue(view.getElements().contains(new ElementView(softwareSystemA))); - assertTrue(view.getElements().contains(new ElementView(softwareSystem))); assertTrue(view.getElements().contains(new ElementView(softwareSystemB))); + assertTrue(view.getElements().contains(new ElementView(webApplication))); + assertTrue(view.getElements().contains(new ElementView(database))); view = new ContainerView(softwareSystem, "containers", "Description"); view.addNearestNeighbours(softwareSystemA); - assertEquals(4, view.getElements().size()); + assertEquals(3, view.getElements().size()); assertTrue(view.getElements().contains(new ElementView(userA))); assertTrue(view.getElements().contains(new ElementView(softwareSystemA))); - assertTrue(view.getElements().contains(new ElementView(softwareSystem))); assertTrue(view.getElements().contains(new ElementView(webApplication))); view = new ContainerView(softwareSystem, "containers", "Description"); diff --git a/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java b/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java index 80541b3da..b21e8edd3 100644 --- a/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java @@ -72,8 +72,8 @@ public void test_add_ThrowsAnException_WhenADeploymentNodeIsAdded() { DeploymentNode deploymentNode = workspace.getModel().addDeploymentNode("Deployment node", "Description", "Technology"); dynamicView.add(deploymentNode, softwareSystemA); fail(); - } catch (IllegalArgumentException iae) { - assertEquals("Only people, software systems, containers and components can be added to dynamic views.", iae.getMessage()); + } catch (ElementNotPermittedInViewException e) { + assertEquals("Only people, software systems, containers and components can be added to dynamic views.", e.getMessage()); } } @@ -85,8 +85,8 @@ public void test_add_ThrowsAnException_WhenAContainerInstanceIsAdded() { ContainerInstance containerInstance = deploymentNode.add(containerA1); dynamicView.add(containerInstance, softwareSystemA); fail(); - } catch (IllegalArgumentException iae) { - assertEquals("Only people, software systems, containers and components can be added to dynamic views.", iae.getMessage()); + } catch (ElementNotPermittedInViewException e) { + assertEquals("Only people, software systems, containers and components can be added to dynamic views.", e.getMessage()); } } @@ -94,10 +94,9 @@ public void test_add_ThrowsAnException_WhenAContainerInstanceIsAdded() { public void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsNotSpecifiedButAContainerIsAdded() { try { DynamicView dynamicView = workspace.getViews().createDynamicView("key", "Description"); - DeploymentNode deploymentNode = workspace.getModel().addDeploymentNode("Deployment node", "Description", "Technology"); dynamicView.add(containerA1, containerA1); fail(); - } catch (IllegalArgumentException iae) { + } catch (ElementNotPermittedInViewException iae) { assertEquals("Only people and software systems can be added to this dynamic view.", iae.getMessage()); } } @@ -109,7 +108,7 @@ public void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsNotSpecifie DeploymentNode deploymentNode = workspace.getModel().addDeploymentNode("Deployment node", "Description", "Technology"); dynamicView.add(componentA1, componentA1); fail(); - } catch (IllegalArgumentException iae) { + } catch (ElementNotPermittedInViewException iae) { assertEquals("Only people and software systems can be added to this dynamic view.", iae.getMessage()); } } @@ -158,6 +157,50 @@ public void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsAContainerA } } + @Test + public void test_add_ThrowsAnException_WhenTheParentOfAnElementHasAlreadyBeenAdded() { + try { + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", ""); + Container container1 = softwareSystem.addContainer("Container 1", "", ""); + Component component1 = container1.addComponent("Component 1", "", ""); + + Container container2 = softwareSystem.addContainer("Container 2", "", ""); + Component component2 = container2.addComponent("Component 2", "", ""); + + component1.uses(container2, "Uses"); + component1.uses(component2, "Uses"); + + DynamicView dynamicView = workspace.getViews().createDynamicView(container1, "key", "Description"); + dynamicView.add(component1, container2); + dynamicView.add(component1, component2); + fail(); + } catch (Exception e) { + assertEquals("The parent of Component 2 is already in this view.", e.getMessage()); + } + } + + @Test + public void test_add_ThrowsAnException_WhenTheChildOfAnElementHasAlreadyBeenAdded() { + try { + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", ""); + Container container1 = softwareSystem.addContainer("Container 1", "", ""); + Component component1 = container1.addComponent("Component 1", "", ""); + + Container container2 = softwareSystem.addContainer("Container 2", "", ""); + Component component2 = container2.addComponent("Component 2", "", ""); + + component1.uses(component2, "Uses"); + component1.uses(container2, "Uses"); + + DynamicView dynamicView = workspace.getViews().createDynamicView(container1, "key", "Description"); + dynamicView.add(component1, component2); + dynamicView.add(component1, container2); + fail(); + } catch (Exception e) { + assertEquals("The child of Container 2 is already in this view.", e.getMessage()); + } + } + @Test public void test_add_ThrowsAnException_WhenARelationshipBetweenTheSourceAndDestinationElementsDoesNotExist() { try { From 2aa957d23d77a8dfa79f12ed222821c4bdbf1fae Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 22 Dec 2020 22:13:33 +0000 Subject: [PATCH 191/717] Enhanced the logic to merge layout information of elements on views. --- docs/changelog.md | 1 + .../view/DefaultLayoutMergeStrategy.java | 108 ++++++++++----- .../view/DefaultLayoutMergeStrategyTests.java | 128 ++++++++++++++++++ 3 files changed, 203 insertions(+), 34 deletions(-) create mode 100644 structurizr-core/test/unit/com/structurizr/view/DefaultLayoutMergeStrategyTests.java diff --git a/docs/changelog.md b/docs/changelog.md index 354e34da0..568d9f20a 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -5,6 +5,7 @@ - Removes the dynamic view restrictions related to adding containers/components outside the scoped software system/container. - Adds an "externalBoundariesVisible" property to DynamicView, so that external software system/container boundaries can be shown/hidden. - Enhanced the rules relating to whether elements can be added to a view or not. +- Enhanced the logic to merge layout information of elements on views. ## 1.6.3 (30th November 2020) diff --git a/structurizr-core/src/com/structurizr/view/DefaultLayoutMergeStrategy.java b/structurizr-core/src/com/structurizr/view/DefaultLayoutMergeStrategy.java index e18c258de..85f052162 100644 --- a/structurizr-core/src/com/structurizr/view/DefaultLayoutMergeStrategy.java +++ b/structurizr-core/src/com/structurizr/view/DefaultLayoutMergeStrategy.java @@ -2,10 +2,13 @@ import com.structurizr.model.Element; import com.structurizr.model.Relationship; +import com.structurizr.util.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import javax.annotation.Nonnull; +import java.util.HashMap; +import java.util.Map; /** * A default implementation of a LayoutMergeStrategy that: @@ -14,8 +17,10 @@ * - Copies element x,y positions. * - Copies relationship vertices. * - * Elements are matched by the full canonical name. The downside of this approach is that if an element is renamed - * between versions of a workspace, it won't be possible to find/copy the layout information associated with an element. + * Elements are matched using the following properties, in order: + * - the element's full canonical name + * - the element's name + * - the element's description */ public class DefaultLayoutMergeStrategy implements LayoutMergeStrategy { @@ -25,31 +30,40 @@ public class DefaultLayoutMergeStrategy implements LayoutMergeStrategy { * Attempts to copy the visual layout information (e.g. x,y coordinates) of elements and relationships * from the specified source view into the specified destination view. * - * @param sourceView the source view (e.g. the version stored by the Structurizr service) - * @param destinationView the destination View (e.g. the new version, created locally with code) + * @param viewWithLayoutInformation the source view (e.g. the version stored by the Structurizr service) + * @param viewWithoutLayoutInformation the destination View (e.g. the new version, created locally with code) */ - public void copyLayoutInformation(@Nonnull View sourceView, @Nonnull View destinationView) { - setPaperSizeIfNotSpecified(sourceView, destinationView); + public void copyLayoutInformation(@Nonnull View viewWithLayoutInformation, @Nonnull View viewWithoutLayoutInformation) { + setPaperSizeIfNotSpecified(viewWithLayoutInformation, viewWithoutLayoutInformation); - for (ElementView destinationElementView : destinationView.getElements()) { - ElementView sourceElementView = findElementView(sourceView, destinationElementView.getElement()); - if (sourceElementView != null) { - destinationElementView.copyLayoutInformationFrom(sourceElementView); + Map elementViewMap = new HashMap<>(); + Map elementMap = new HashMap<>(); + + for (ElementView elementViewWithoutLayoutInformation : viewWithoutLayoutInformation.getElements()) { + ElementView elementViewWithLayoutInformation = findElementView(viewWithLayoutInformation, elementViewWithoutLayoutInformation.getElement()); + if (elementViewWithLayoutInformation != null) { + elementViewMap.put(elementViewWithoutLayoutInformation, elementViewWithLayoutInformation); + elementMap.put(elementViewWithoutLayoutInformation.getElement(), elementViewWithLayoutInformation.getElement()); } else { - log.warn("There is no layout information for the element named " + destinationElementView.getElement().getName() + " on view " + sourceView.getKey()); + log.warn("There is no layout information for the element named " + elementViewWithoutLayoutInformation.getElement().getName() + " on view " + viewWithLayoutInformation.getKey()); } } - for (RelationshipView destinationRelationshipView : destinationView.getRelationships()) { - RelationshipView sourceRelationshipView; - if (destinationView instanceof DynamicView) { - sourceRelationshipView = findRelationshipView(sourceView, destinationRelationshipView); + for (ElementView elementViewWithoutLayoutInformation : elementViewMap.keySet()) { + ElementView elementViewWithLayoutInformation = elementViewMap.get(elementViewWithoutLayoutInformation); + elementViewWithoutLayoutInformation.copyLayoutInformationFrom(elementViewWithLayoutInformation); + } + + for (RelationshipView relationshipViewWithoutLayoutInformation : viewWithoutLayoutInformation.getRelationships()) { + RelationshipView relationshipViewWithLayoutInformation; + if (viewWithoutLayoutInformation instanceof DynamicView) { + relationshipViewWithLayoutInformation = findRelationshipView(viewWithLayoutInformation, relationshipViewWithoutLayoutInformation, elementMap); } else { - sourceRelationshipView = findRelationshipView(sourceView, destinationRelationshipView.getRelationship()); + relationshipViewWithLayoutInformation = findRelationshipView(viewWithLayoutInformation, relationshipViewWithoutLayoutInformation.getRelationship(), elementMap); } - if (sourceRelationshipView != null) { - destinationRelationshipView.copyLayoutInformationFrom(sourceRelationshipView); + if (relationshipViewWithLayoutInformation != null) { + relationshipViewWithoutLayoutInformation.copyLayoutInformationFrom(relationshipViewWithLayoutInformation); } } } @@ -61,22 +75,45 @@ private void setPaperSizeIfNotSpecified(@Nonnull View remoteView, @Nonnull View } /** - * Finds an element by canonical name. Override this to change the behaviour. - - * @param view the view to search - * @param element the Element to find + * Finds an element. Override this to change the behaviour. + * + * @param viewWithLayoutInformation the view to search + * @param elementWithoutLayoutInformation the Element to find * @return an ElementView */ - protected ElementView findElementView(View view, Element element) { - return view.getElements().stream().filter(ev -> ev.getElement().getCanonicalName().equals(element.getCanonicalName())).findFirst().orElse(null); + protected ElementView findElementView(View viewWithLayoutInformation, Element elementWithoutLayoutInformation) { + // see if we can find an element with the same canonical name in the source view + ElementView elementView = viewWithLayoutInformation.getElements().stream().filter(ev -> ev.getElement().getCanonicalName().equals(elementWithoutLayoutInformation.getCanonicalName())).findFirst().orElse(null); + + if (elementView == null) { + // no element was found, so try finding an element of the same type with the same name (in this situation, the parent element may have been renamed) + elementView = viewWithLayoutInformation.getElements().stream().filter(ev -> ev.getElement().getName().equals(elementWithoutLayoutInformation.getName()) && ev.getElement().getClass().equals(elementWithoutLayoutInformation.getClass())).findFirst().orElse(null); + } + + if (elementView == null) { + // no element was found, so try finding an element of the same type with the same description if set (in this situation, the element itself may have been renamed) + if (!StringUtils.isNullOrEmpty(elementWithoutLayoutInformation.getDescription())) { + elementView = viewWithLayoutInformation.getElements().stream().filter(ev -> ev.getElement().getDescription().equals(elementWithoutLayoutInformation.getDescription()) && ev.getElement().getClass().equals(elementWithoutLayoutInformation.getClass())).findFirst().orElse(null); + } + } + + if (elementView == null) { + // no element was found, so try finding an element of the same type with the same ID (in this situation, the name and description may have changed) + elementView = viewWithLayoutInformation.getElements().stream().filter(ev -> ev.getElement().getId().equals(elementWithoutLayoutInformation.getId()) && ev.getElement().getClass().equals(elementWithoutLayoutInformation.getClass())).findFirst().orElse(null); + } + + return elementView; } - protected RelationshipView findRelationshipView(View view, Relationship relationship) { - for (RelationshipView rv : view.getRelationships()) { + private RelationshipView findRelationshipView(View viewWithLayoutInformation, Relationship relationshipWithoutLayoutInformation, Map elementMap) { + Element sourceElementWithLayoutInformation = elementMap.get(relationshipWithoutLayoutInformation.getSource()); + Element destinationElementWithLayoutInformation = elementMap.get(relationshipWithoutLayoutInformation.getDestination()); + + for (RelationshipView rv : viewWithLayoutInformation.getRelationships()) { if ( - rv.getRelationship().getSource().getCanonicalName().equals(relationship.getSource().getCanonicalName()) && - rv.getRelationship().getDestination().getCanonicalName().equals(relationship.getDestination().getCanonicalName()) && - rv.getRelationship().getDescription().equals(relationship.getDescription()) + rv.getRelationship().getSource().equals(sourceElementWithLayoutInformation) && + rv.getRelationship().getDestination().equals(destinationElementWithLayoutInformation) && + rv.getRelationship().getDescription().equals(relationshipWithoutLayoutInformation.getDescription()) ) { return rv; } @@ -85,13 +122,16 @@ protected RelationshipView findRelationshipView(View view, Relationship relation return null; } - protected RelationshipView findRelationshipView(View view, RelationshipView relationshipView) { + private RelationshipView findRelationshipView(View view, RelationshipView relationshipWithoutLayoutInformation, Map elementMap) { + Element sourceElementWithLayoutInformation = elementMap.get(relationshipWithoutLayoutInformation.getRelationship().getSource()); + Element destinationElementWithLayoutInformation = elementMap.get(relationshipWithoutLayoutInformation.getRelationship().getDestination()); + for (RelationshipView rv : view.getRelationships()) { if ( - rv.getRelationship().getSource().getCanonicalName().equals(relationshipView.getRelationship().getSource().getCanonicalName()) && - rv.getRelationship().getDestination().getCanonicalName().equals(relationshipView.getRelationship().getDestination().getCanonicalName()) && - rv.getDescription().equals(relationshipView.getDescription()) && - rv.getOrder().equals(relationshipView.getOrder())) { + rv.getRelationship().getSource().equals(sourceElementWithLayoutInformation) && + rv.getRelationship().getDestination().equals(destinationElementWithLayoutInformation) && + rv.getDescription().equals(relationshipWithoutLayoutInformation.getDescription()) && + rv.getOrder().equals(relationshipWithoutLayoutInformation.getOrder())) { return rv; } } diff --git a/structurizr-core/test/unit/com/structurizr/view/DefaultLayoutMergeStrategyTests.java b/structurizr-core/test/unit/com/structurizr/view/DefaultLayoutMergeStrategyTests.java new file mode 100644 index 000000000..cb5a1187a --- /dev/null +++ b/structurizr-core/test/unit/com/structurizr/view/DefaultLayoutMergeStrategyTests.java @@ -0,0 +1,128 @@ +package com.structurizr.view; + +import com.structurizr.Workspace; +import com.structurizr.model.Container; +import com.structurizr.model.SoftwareSystem; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class DefaultLayoutMergeStrategyTests { + + @Test + public void test_copyLayoutInformation_WhenCanonicalNamesHaveNotChanged() { + Workspace workspace1 = new Workspace("1", ""); + SoftwareSystem softwareSystem1 = workspace1.getModel().addSoftwareSystem("Software System"); + Container container1 = softwareSystem1.addContainer("Container", "", ""); + ContainerView view1 = workspace1.getViews().createContainerView(softwareSystem1, "key", ""); + view1.add(container1); + view1.getElementView(container1).setX(123); + view1.getElementView(container1).setY(456); + + Workspace workspace2 = new Workspace("2", ""); + SoftwareSystem softwareSystem2 = workspace2.getModel().addSoftwareSystem("Software System"); + Container container2 = softwareSystem2.addContainer("Container", "", ""); + ContainerView view2 = workspace2.getViews().createContainerView(softwareSystem2, "key", ""); + view2.add(container2); + + DefaultLayoutMergeStrategy strategy = new DefaultLayoutMergeStrategy(); + strategy.copyLayoutInformation(view1, view2); + + assertEquals(123, view2.getElementView(container2).getX()); + assertEquals(456, view2.getElementView(container2).getY()); + } + + @Test + public void test_copyLayoutInformation_WhenAParentElementNameHasChanged() { + Workspace workspace1 = new Workspace("1", ""); + SoftwareSystem softwareSystem1 = workspace1.getModel().addSoftwareSystem("Software System"); + Container container1 = softwareSystem1.addContainer("Container", "", ""); + ContainerView view1 = workspace1.getViews().createContainerView(softwareSystem1, "key", ""); + view1.add(container1); + view1.getElementView(container1).setX(123); + view1.getElementView(container1).setY(456); + + Workspace workspace2 = new Workspace("2", ""); + SoftwareSystem softwareSystem2 = workspace2.getModel().addSoftwareSystem("Software System with a new name"); + Container container2 = softwareSystem2.addContainer("Container", "", ""); + ContainerView view2 = workspace2.getViews().createContainerView(softwareSystem2, "key", ""); + view2.add(container2); + + DefaultLayoutMergeStrategy strategy = new DefaultLayoutMergeStrategy(); + strategy.copyLayoutInformation(view1, view2); + + assertEquals(123, view2.getElementView(container2).getX()); + assertEquals(456, view2.getElementView(container2).getY()); + } + + @Test + public void test_copyLayoutInformation_WhenAnElementNameHasChangedButTheDescriptionHasNotChanged() { + Workspace workspace1 = new Workspace("1", ""); + SoftwareSystem softwareSystem1 = workspace1.getModel().addSoftwareSystem("Software System"); + Container container1 = softwareSystem1.addContainer("Container", "Container description", ""); + ContainerView view1 = workspace1.getViews().createContainerView(softwareSystem1, "key", ""); + view1.add(container1); + view1.getElementView(container1).setX(123); + view1.getElementView(container1).setY(456); + + Workspace workspace2 = new Workspace("2", ""); + SoftwareSystem softwareSystem2 = workspace2.getModel().addSoftwareSystem("Software System"); + Container container2 = softwareSystem2.addContainer("Container with a new name", "Container description", ""); + ContainerView view2 = workspace2.getViews().createContainerView(softwareSystem2, "key", ""); + view2.add(container2); + + DefaultLayoutMergeStrategy strategy = new DefaultLayoutMergeStrategy(); + strategy.copyLayoutInformation(view1, view2); + + assertEquals(123, view2.getElementView(container2).getX()); + assertEquals(456, view2.getElementView(container2).getY()); + } + + @Test + public void test_copyLayoutInformation_WhenAnElementNameAndDescriptionHaveChangedButTheIdHasNotChanged() { + Workspace workspace1 = new Workspace("1", ""); + SoftwareSystem softwareSystem1 = workspace1.getModel().addSoftwareSystem("Software System"); + Container container1 = softwareSystem1.addContainer("Container", "Container description", ""); + ContainerView view1 = workspace1.getViews().createContainerView(softwareSystem1, "key", ""); + view1.add(container1); + view1.getElementView(container1).setX(123); + view1.getElementView(container1).setY(456); + + Workspace workspace2 = new Workspace("2", ""); + SoftwareSystem softwareSystem2 = workspace2.getModel().addSoftwareSystem("Software System"); + Container container2 = softwareSystem2.addContainer("Container with a new name", "Container with a new description", ""); + ContainerView view2 = workspace2.getViews().createContainerView(softwareSystem2, "key", ""); + view2.add(container2); + + DefaultLayoutMergeStrategy strategy = new DefaultLayoutMergeStrategy(); + strategy.copyLayoutInformation(view1, view2); + + assertEquals(123, view2.getElementView(container2).getX()); + assertEquals(456, view2.getElementView(container2).getY()); + } + + @Test + public void test_copyLayoutInformation_WhenAnElementNameAndDescriptionAndIdHaveChanged() { + Workspace workspace1 = new Workspace("1", ""); + SoftwareSystem softwareSystem1 = workspace1.getModel().addSoftwareSystem("Software System"); + Container container1 = softwareSystem1.addContainer("Container", "Container description", ""); + ContainerView view1 = workspace1.getViews().createContainerView(softwareSystem1, "key", ""); + view1.add(container1); + view1.getElementView(container1).setX(123); + view1.getElementView(container1).setY(456); + + Workspace workspace2 = new Workspace("2", ""); + SoftwareSystem softwareSystem2 = workspace2.getModel().addSoftwareSystem("Software System"); + softwareSystem2.addContainer("Web Application", "Description", ""); // this element has ID 2 + Container container2 = softwareSystem2.addContainer("Database", "Description", ""); + ContainerView view2 = workspace2.getViews().createContainerView(softwareSystem2, "key", ""); + view2.add(container2); + + DefaultLayoutMergeStrategy strategy = new DefaultLayoutMergeStrategy(); + strategy.copyLayoutInformation(view1, view2); + + assertEquals(0, view2.getElementView(container2).getX()); + assertEquals(0, view2.getElementView(container2).getY()); + } + +} \ No newline at end of file From df03c3e91b7fa6e153dea932d8efcdd094b26ede Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 22 Dec 2020 22:15:34 +0000 Subject: [PATCH 192/717] Dynamic views now try to find a relationship with the same description. --- structurizr-core/src/com/structurizr/view/DynamicView.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/structurizr-core/src/com/structurizr/view/DynamicView.java b/structurizr-core/src/com/structurizr/view/DynamicView.java index 186c33c35..d9819a118 100644 --- a/structurizr-core/src/com/structurizr/view/DynamicView.java +++ b/structurizr-core/src/com/structurizr/view/DynamicView.java @@ -117,7 +117,10 @@ public RelationshipView add(@Nonnull Element source, String description, String if (StringUtils.isNullOrEmpty(technology)) { // no technology is specified, so just pick the first relationship we find - relationship = source.getEfferentRelationshipWith(destination); + relationship = source.getEfferentRelationshipWith(destination, description); + if (relationship == null) { + relationship = source.getEfferentRelationshipWith(destination); + } } else { Set relationships = source.getEfferentRelationshipsWith(destination); for (Relationship rel : relationships) { From 0a825782de3f6a481b4ac0fc7056fccb44544757 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 22 Dec 2020 22:19:39 +0000 Subject: [PATCH 193/717] Bump version number. --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index e2c2ea177..547f66d88 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.6.4' + version = '1.7.0' repositories { mavenCentral() From 171fb365f354f11c106241e4b289d25ae3e4c482 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 6 Jan 2021 12:34:14 +0000 Subject: [PATCH 194/717] Added some logic to enforce rules about not adding parents/children to the same view. --- .../structurizr/model/DeploymentElement.java | 2 +- .../com/structurizr/model/SoftwareSystem.java | 14 +++- .../com/structurizr/view/DeploymentView.java | 57 ++++++++++++-- .../src/com/structurizr/view/DynamicView.java | 30 ++++---- .../src/com/structurizr/view/View.java | 2 +- .../structurizr/view/DeploymentViewTests.java | 75 ++++++++++++++++++- .../structurizr/view/DynamicViewTests.java | 25 ------- 7 files changed, 155 insertions(+), 50 deletions(-) diff --git a/structurizr-core/src/com/structurizr/model/DeploymentElement.java b/structurizr-core/src/com/structurizr/model/DeploymentElement.java index d2d4d74c7..dd8240938 100644 --- a/structurizr-core/src/com/structurizr/model/DeploymentElement.java +++ b/structurizr-core/src/com/structurizr/model/DeploymentElement.java @@ -7,7 +7,7 @@ */ public abstract class DeploymentElement extends Element { - static final String DEFAULT_DEPLOYMENT_ENVIRONMENT = "Default"; + public static final String DEFAULT_DEPLOYMENT_ENVIRONMENT = "Default"; private DeploymentNode parent; private String environment = DEFAULT_DEPLOYMENT_ENVIRONMENT; diff --git a/structurizr-core/src/com/structurizr/model/SoftwareSystem.java b/structurizr-core/src/com/structurizr/model/SoftwareSystem.java index 2781c0d37..43a819902 100644 --- a/structurizr-core/src/com/structurizr/model/SoftwareSystem.java +++ b/structurizr-core/src/com/structurizr/model/SoftwareSystem.java @@ -75,7 +75,19 @@ void setContainers(Set containers) { } /** - * Adds a container with the specified name, description and technology + * Adds a container with the specified name. + * + * @param name the name of the container (e.g. "Web Application") + * @return the newly created Container instance added to the model (or null) + * @throws IllegalArgumentException if a container with the same name exists already + */ + @Nonnull + public Container addContainer(@Nonnull String name) { + return addContainer(name, "", ""); + } + + /** + * Adds a container with the specified name, description and technology. * * @param name the name of the container (e.g. "Web Application") * @param description a short description/list of responsibilities diff --git a/structurizr-core/src/com/structurizr/view/DeploymentView.java b/structurizr-core/src/com/structurizr/view/DeploymentView.java index 8241e9858..1d079e561 100644 --- a/structurizr-core/src/com/structurizr/view/DeploymentView.java +++ b/structurizr-core/src/com/structurizr/view/DeploymentView.java @@ -6,6 +6,7 @@ import javax.annotation.Nonnull; import java.util.*; +import java.util.stream.Collectors; /** * A deployment view, used to show the mapping of container instances to deployment nodes. @@ -13,7 +14,7 @@ public final class DeploymentView extends View { private Model model; - private String environment; + private String environment = DeploymentElement.DEFAULT_DEPLOYMENT_ENVIRONMENT; private List animations = new ArrayList<>(); @@ -133,15 +134,23 @@ public void remove(@Nonnull ContainerInstance containerInstance) { private boolean addElementInstancesAndDeploymentNodesAndInfrastructureNodes(DeploymentNode deploymentNode, boolean addRelationships) { boolean hasElementsOrInfrastructureNodes = false; for (SoftwareSystemInstance softwareSystemInstance : deploymentNode.getSoftwareSystemInstances()) { - addElement(softwareSystemInstance, addRelationships); - hasElementsOrInfrastructureNodes = true; + try { + addElement(softwareSystemInstance, addRelationships); + hasElementsOrInfrastructureNodes = true; + } catch (ElementNotPermittedInViewException e) { + // the element can't be added, so ignore it + } } for (ContainerInstance containerInstance : deploymentNode.getContainerInstances()) { Container container = containerInstance.getContainer(); if (getSoftwareSystem() == null || container.getParent().equals(getSoftwareSystem())) { - addElement(containerInstance, addRelationships); - hasElementsOrInfrastructureNodes = true; + try { + addElement(containerInstance, addRelationships); + hasElementsOrInfrastructureNodes = true; + } catch (ElementNotPermittedInViewException e) { + // the element can't be added, so ignore it + } } } @@ -211,8 +220,44 @@ public void setEnvironment(String environment) { } @Override - protected void checkElementCanBeAdded(Element element) { + protected void checkElementCanBeAdded(Element elementToBeAdded) { + if (!(elementToBeAdded instanceof DeploymentElement)) { + throw new ElementNotPermittedInViewException("Only deployment nodes, infrastructure nodes, software system instances, and container instances can be added to deployment views."); + } + + DeploymentElement deploymentElementToBeAdded = (DeploymentElement) elementToBeAdded; + if (!deploymentElementToBeAdded.getEnvironment().equals(this.getEnvironment())) { + throw new ElementNotPermittedInViewException("Only elements in the " + this.getEnvironment() + " deployment environment can be added to this view."); + } + if (this.getSoftwareSystem() != null && elementToBeAdded instanceof SoftwareSystemInstance) { + SoftwareSystemInstance ssi = (SoftwareSystemInstance) elementToBeAdded; + + if (ssi.getSoftwareSystem().equals(this.getSoftwareSystem())) { + // adding an instance of the scoped software system isn't permitted + throw new ElementNotPermittedInViewException("The software system in scope cannot be added to a deployment view."); + } + } + + if (elementToBeAdded instanceof SoftwareSystemInstance) { + // check that a child container instance hasn't been added already + SoftwareSystemInstance softwareSystemInstanceToBeAdded = (SoftwareSystemInstance) elementToBeAdded; + Set softwareSystemIds = getElements().stream().map(ElementView::getElement).filter(e -> e instanceof ContainerInstance).map(e -> (ContainerInstance)e).map(ci -> ci.getContainer().getSoftwareSystem().getId()).collect(Collectors.toSet()); + + if (softwareSystemIds.contains(softwareSystemInstanceToBeAdded.getSoftwareSystemId())) { + throw new ElementNotPermittedInViewException("A child of " + elementToBeAdded.getName() + " is already in this view."); + } + } + + if (elementToBeAdded instanceof ContainerInstance) { + // check that the parent software system instance hasn't been added already + ContainerInstance containerInstanceToBeAdded = (ContainerInstance)elementToBeAdded; + Set softwareSystemIds = getElements().stream().map(ElementView::getElement).filter(e -> e instanceof SoftwareSystemInstance).map(e -> (SoftwareSystemInstance)e).map(SoftwareSystemInstance::getSoftwareSystemId).collect(Collectors.toSet()); + + if (softwareSystemIds.contains(containerInstanceToBeAdded.getContainer().getSoftwareSystem().getId())) { + throw new ElementNotPermittedInViewException("The parent of " + elementToBeAdded.getName() + " is already in this view."); + } + } } @Override diff --git a/structurizr-core/src/com/structurizr/view/DynamicView.java b/structurizr-core/src/com/structurizr/view/DynamicView.java index d9819a118..9a29dd37b 100644 --- a/structurizr-core/src/com/structurizr/view/DynamicView.java +++ b/structurizr-core/src/com/structurizr/view/DynamicView.java @@ -92,15 +92,15 @@ void setElement(Element element) { } } - public RelationshipView add(@Nonnull Element source, @Nonnull Element destination) { + public RelationshipView add(@Nonnull StaticStructureElement source, @Nonnull StaticStructureElement destination) { return add(source, "", destination); } - public RelationshipView add(@Nonnull Element source, String description, @Nonnull Element destination) { + public RelationshipView add(@Nonnull StaticStructureElement source, String description, @Nonnull StaticStructureElement destination) { return add(source, description, "", destination); } - public RelationshipView add(@Nonnull Element source, String description, String technology, @Nonnull Element destination) { + public RelationshipView add(@Nonnull StaticStructureElement source, String description, String technology, @Nonnull StaticStructureElement destination) { if (source == null) { throw new IllegalArgumentException("A source element must be specified."); } @@ -214,12 +214,14 @@ public void endParallelSequence(boolean endAllParallelSequencesAndContinueNumber @Override protected void checkElementCanBeAdded(Element elementToBeAdded) { - if (!(elementToBeAdded instanceof Person) && !(elementToBeAdded instanceof SoftwareSystem) && !(elementToBeAdded instanceof Container) && !(elementToBeAdded instanceof Component)) { + if (!(elementToBeAdded instanceof StaticStructureElement)) { throw new ElementNotPermittedInViewException("Only people, software systems, containers and components can be added to dynamic views."); } + StaticStructureElement staticStructureElementToBeAdded = (StaticStructureElement)elementToBeAdded; + // people can always be added - if (elementToBeAdded instanceof Person) { + if (staticStructureElementToBeAdded instanceof Person) { return; } @@ -227,13 +229,13 @@ protected void checkElementCanBeAdded(Element elementToBeAdded) { // - containers // - other software systems if (element instanceof SoftwareSystem) { - if (elementToBeAdded.equals(element)) { - throw new ElementNotPermittedInViewException(elementToBeAdded.getName() + " is already the scope of this view and cannot be added to it."); + if (staticStructureElementToBeAdded.equals(element)) { + throw new ElementNotPermittedInViewException(staticStructureElementToBeAdded.getName() + " is already the scope of this view and cannot be added to it."); } - if (elementToBeAdded instanceof SoftwareSystem || elementToBeAdded instanceof Container) { - checkParentAndChildrenHaveNotAlreadyBeenAdded(elementToBeAdded); - } else if (elementToBeAdded instanceof Component) { + if (staticStructureElementToBeAdded instanceof SoftwareSystem || staticStructureElementToBeAdded instanceof Container) { + checkParentAndChildrenHaveNotAlreadyBeenAdded(staticStructureElementToBeAdded); + } else if (staticStructureElementToBeAdded instanceof Component) { throw new ElementNotPermittedInViewException("Components can't be added to a dynamic view when the scope is a software system."); } } @@ -242,17 +244,17 @@ protected void checkElementCanBeAdded(Element elementToBeAdded) { // - other containers // - components if (element instanceof Container) { - if (elementToBeAdded.equals(element) || elementToBeAdded.equals(element.getParent())) { - throw new ElementNotPermittedInViewException(elementToBeAdded.getName() + " is already the scope of this view and cannot be added to it."); + if (staticStructureElementToBeAdded.equals(element) || staticStructureElementToBeAdded.equals(element.getParent())) { + throw new ElementNotPermittedInViewException(staticStructureElementToBeAdded.getName() + " is already the scope of this view and cannot be added to it."); } - checkParentAndChildrenHaveNotAlreadyBeenAdded(elementToBeAdded); + checkParentAndChildrenHaveNotAlreadyBeenAdded(staticStructureElementToBeAdded); } // dynamic view with no scope // - software systems if (element == null) { - if (!(elementToBeAdded instanceof SoftwareSystem)) { + if (!(staticStructureElementToBeAdded instanceof SoftwareSystem)) { throw new ElementNotPermittedInViewException("Only people and software systems can be added to this dynamic view."); } } diff --git a/structurizr-core/src/com/structurizr/view/View.java b/structurizr-core/src/com/structurizr/view/View.java index e1768a105..ee3475d2f 100644 --- a/structurizr-core/src/com/structurizr/view/View.java +++ b/structurizr-core/src/com/structurizr/view/View.java @@ -434,7 +434,7 @@ public ViewSet getViewSet() { protected abstract boolean canBeRemoved(Element element); - protected void checkParentAndChildrenHaveNotAlreadyBeenAdded(Element elementToBeAdded) { + final void checkParentAndChildrenHaveNotAlreadyBeenAdded(StaticStructureElement elementToBeAdded) { // check the parent hasn't been added already Set elementIds = getElements().stream().map(ElementView::getElement).map(Element::getId).collect(Collectors.toSet()); diff --git a/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java b/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java index 64097a1d4..f64cffeef 100644 --- a/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java @@ -18,7 +18,7 @@ public void setup() { @Test public void test_getName_WithNoSoftwareSystemAndNoEnvironment() { deploymentView = views.createDeploymentView("deployment", "Description"); - assertEquals("Deployment", deploymentView.getName()); + assertEquals("Deployment - Default", deploymentView.getName()); } @Test @@ -32,7 +32,7 @@ public void test_getName_WithNoSoftwareSystemAndAnEnvironment() { public void test_getName_WithASoftwareSystemAndNoEnvironment() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); deploymentView = views.createDeploymentView(softwareSystem, "deployment", "Description"); - assertEquals("Software System - Deployment", deploymentView.getName()); + assertEquals("Software System - Deployment - Default", deploymentView.getName()); } @Test @@ -164,6 +164,77 @@ public void test_addDeploymentNode_AddsTheParentToo() { assertTrue(deploymentView.getElements().contains(new ElementView(containerInstance))); } + @Test + public void test_addDeploymentNode_ThrowsAnException_WhenAddingADeploymentNodeFromAnotherDeploymentEnvironment() { + DeploymentNode devDeploymentNode = model.addDeploymentNode("Dev", "Deployment Node", "Description", "Technology"); + devDeploymentNode.addInfrastructureNode("Load Balancer"); + DeploymentNode liveDeploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + liveDeploymentNode.addInfrastructureNode("Load Balancer"); + + deploymentView = views.createDeploymentView("deployment", "Description"); + deploymentView.setEnvironment("Dev"); + deploymentView.add(devDeploymentNode); // should work + + try { + deploymentView.add(liveDeploymentNode); // should fail + fail(); + } catch (Exception e) { + assertEquals("Only elements in the Dev deployment environment can be added to this view.", e.getMessage()); + } + } + + @Test + public void test_addSoftwareSystemInstance_ThrowsAnException_WhenTheSoftwareSystemInstanceIsTheSoftwareSystemInScope() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node", "Description", "Technology"); + SoftwareSystemInstance softwareSystemInstance = deploymentNode.add(softwareSystem); + + deploymentView = views.createDeploymentView(softwareSystem, "deployment", "Description"); + deploymentView.add(deploymentNode); + + // the software system instance won't have been added (neither will the empty parent deployment node) + assertEquals(0, deploymentView.getElements().size()); + assertNull(deploymentView.getElementView(softwareSystemInstance)); + } + + @Test + public void test_addSoftwareSystemInstance_DoesNotAddTheSoftwareSystemInstance_WhenAChildContainerInstanceHasAlreadyBeenAdded() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + DeploymentNode deploymentNode1 = model.addDeploymentNode("Deployment Node 1", "Description", "Technology"); + DeploymentNode deploymentNode2 = model.addDeploymentNode("Deployment Node 2", "Description", "Technology"); + ContainerInstance containerInstance = deploymentNode1.add(container); + SoftwareSystemInstance softwareSystemInstance = deploymentNode2.add(softwareSystem); + + deploymentView = views.createDeploymentView("deployment", "Description"); + deploymentView.add(deploymentNode1); + deploymentView.add(deploymentNode2); + + // the software system instance won't have been added (neither will the empty parent deployment node) + assertEquals(2, deploymentView.getElements().size()); + assertNotNull(deploymentView.getElementView(containerInstance)); + assertNull(deploymentView.getElementView(softwareSystemInstance)); + } + + @Test + public void test_addContainerInstance_DoesNotAddTheContainerInstance_WhenTheParentSoftwareSystemInstanceHasAlreadyBeenAdded() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + DeploymentNode deploymentNode1 = model.addDeploymentNode("Deployment Node 1", "Description", "Technology"); + DeploymentNode deploymentNode2 = model.addDeploymentNode("Deployment Node 2", "Description", "Technology"); + SoftwareSystemInstance softwareSystemInstance = deploymentNode1.add(softwareSystem); + ContainerInstance containerInstance = deploymentNode2.add(container); + + deploymentView = views.createDeploymentView("deployment", "Description"); + deploymentView.add(deploymentNode1); + deploymentView.add(deploymentNode2); + + // the container instance won't have been added (neither will the empty parent deployment node) + assertEquals(2, deploymentView.getElements().size()); + assertNotNull(deploymentView.getElementView(softwareSystemInstance)); + assertNull(deploymentView.getElementView(containerInstance)); + } + @Test public void test_addAnimationStep_ThrowsAnException_WhenNoElementInstancesAreSpecified() { try { diff --git a/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java b/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java index b21e8edd3..ba9d42ff4 100644 --- a/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java @@ -65,31 +65,6 @@ public void test_add_ThrowsAnException_WhenPassedANullDestinationElement() { } } - @Test - public void test_add_ThrowsAnException_WhenADeploymentNodeIsAdded() { - try { - DynamicView dynamicView = workspace.getViews().createDynamicView("key", "Description"); - DeploymentNode deploymentNode = workspace.getModel().addDeploymentNode("Deployment node", "Description", "Technology"); - dynamicView.add(deploymentNode, softwareSystemA); - fail(); - } catch (ElementNotPermittedInViewException e) { - assertEquals("Only people, software systems, containers and components can be added to dynamic views.", e.getMessage()); - } - } - - @Test - public void test_add_ThrowsAnException_WhenAContainerInstanceIsAdded() { - try { - DynamicView dynamicView = workspace.getViews().createDynamicView("key", "Description"); - DeploymentNode deploymentNode = workspace.getModel().addDeploymentNode("Deployment node", "Description", "Technology"); - ContainerInstance containerInstance = deploymentNode.add(containerA1); - dynamicView.add(containerInstance, softwareSystemA); - fail(); - } catch (ElementNotPermittedInViewException e) { - assertEquals("Only people, software systems, containers and components can be added to dynamic views.", e.getMessage()); - } - } - @Test public void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsNotSpecifiedButAContainerIsAdded() { try { From e876d1db07f6979cabdf825edd490383ed543ed3 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 6 Jan 2021 14:06:21 +0000 Subject: [PATCH 195/717] Added release date. --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 568d9f20a..8f3d7c0f8 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 1.7.0 (unreleased) +## 1.7.0 (6th January 2021) - Removes the dynamic view restrictions related to adding containers/components outside the scoped software system/container. - Adds an "externalBoundariesVisible" property to DynamicView, so that external software system/container boundaries can be shown/hidden. From 26f3d6e811a9d4e11fc115065512791415e442ee Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 8 Jan 2021 17:15:33 +0000 Subject: [PATCH 196/717] Only copy layout information when automatic layout is not enabled. --- .../src/com/structurizr/view/ViewSet.java | 72 +++++++++++-------- 1 file changed, 42 insertions(+), 30 deletions(-) diff --git a/structurizr-core/src/com/structurizr/view/ViewSet.java b/structurizr-core/src/com/structurizr/view/ViewSet.java index be337c7e2..1290d4ed4 100644 --- a/structurizr-core/src/com/structurizr/view/ViewSet.java +++ b/structurizr-core/src/com/structurizr/view/ViewSet.java @@ -596,56 +596,68 @@ public Configuration getConfiguration() { public void copyLayoutInformationFrom(ViewSet source) { for (SystemLandscapeView view : systemLandscapeViews) { - SystemLandscapeView sourceView = findView(source.getSystemLandscapeViews(), view); - if (sourceView != null) { - view.copyLayoutInformationFrom(sourceView); - } else { - log.warn("Could not find a matching view for \"" + view.getName() + "\" ... diagram layout information may be lost."); + if (view.getAutomaticLayout() == null) { + SystemLandscapeView sourceView = findView(source.getSystemLandscapeViews(), view); + if (sourceView != null) { + view.copyLayoutInformationFrom(sourceView); + } else { + log.warn("Could not find a matching view for \"" + view.getName() + "\" ... diagram layout information may be lost."); + } } } for (SystemContextView view : systemContextViews) { - SystemContextView sourceView = findView(source.getSystemContextViews(), view); - if (sourceView != null) { - view.copyLayoutInformationFrom(sourceView); - } else { - log.warn("Could not find a matching view for \"" + view.getName() + "\" ... diagram layout information may be lost."); + if (view.getAutomaticLayout() == null) { + SystemContextView sourceView = findView(source.getSystemContextViews(), view); + if (sourceView != null) { + view.copyLayoutInformationFrom(sourceView); + } else { + log.warn("Could not find a matching view for \"" + view.getName() + "\" ... diagram layout information may be lost."); + } } } for (ContainerView view : containerViews) { - ContainerView sourceView = findView(source.getContainerViews(), view); - if (sourceView != null) { - view.copyLayoutInformationFrom(sourceView); - } else { - log.warn("Could not find a matching view for \"" + view.getName() + "\" ... diagram layout information may be lost."); + if (view.getAutomaticLayout() == null) { + ContainerView sourceView = findView(source.getContainerViews(), view); + if (sourceView != null) { + view.copyLayoutInformationFrom(sourceView); + } else { + log.warn("Could not find a matching view for \"" + view.getName() + "\" ... diagram layout information may be lost."); + } } } for (ComponentView view : componentViews) { - ComponentView sourceView = findView(source.getComponentViews(), view); - if (sourceView != null) { - view.copyLayoutInformationFrom(sourceView); - } else { - log.warn("Could not find a matching view for \"" + view.getName() + "\" ... diagram layout information may be lost."); + if (view.getAutomaticLayout() == null) { + ComponentView sourceView = findView(source.getComponentViews(), view); + if (sourceView != null) { + view.copyLayoutInformationFrom(sourceView); + } else { + log.warn("Could not find a matching view for \"" + view.getName() + "\" ... diagram layout information may be lost."); + } } } for (DynamicView view : dynamicViews) { - DynamicView sourceView = findView(source.getDynamicViews(), view); - if (sourceView != null) { - view.copyLayoutInformationFrom(sourceView); - } else { - log.warn("Could not find a matching view for \"" + view.getName() + "\" ... diagram layout information may be lost."); + if (view.getAutomaticLayout() == null) { + DynamicView sourceView = findView(source.getDynamicViews(), view); + if (sourceView != null) { + view.copyLayoutInformationFrom(sourceView); + } else { + log.warn("Could not find a matching view for \"" + view.getName() + "\" ... diagram layout information may be lost."); + } } } for (DeploymentView view : deploymentViews) { - DeploymentView sourceView = findView(source.getDeploymentViews(), view); - if (sourceView != null) { - view.copyLayoutInformationFrom(sourceView); - } else { - log.warn("Could not find a matching view for \"" + view.getName() + "\" ... diagram layout information may be lost."); + if (view.getAutomaticLayout() == null) { + DeploymentView sourceView = findView(source.getDeploymentViews(), view); + if (sourceView != null) { + view.copyLayoutInformationFrom(sourceView); + } else { + log.warn("Could not find a matching view for \"" + view.getName() + "\" ... diagram layout information may be lost."); + } } } } From c9297fb10df59d12ce342380e49319ce15cedf46 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 13 Jan 2021 13:34:49 +0000 Subject: [PATCH 197/717] Added a method to add a component without a description. --- .../src/com/structurizr/model/Container.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/structurizr-core/src/com/structurizr/model/Container.java b/structurizr-core/src/com/structurizr/model/Container.java index be2622bf6..8b5d9a93c 100644 --- a/structurizr-core/src/com/structurizr/model/Container.java +++ b/structurizr-core/src/com/structurizr/model/Container.java @@ -61,6 +61,17 @@ public void setTechnology(String technology) { this.technology = technology; } + /** + * Adds a component to this container. + * + * @param name the name of the component + * @return the resulting Component instance + * @throws IllegalArgumentException if the component name is null or empty, or a component with the same name already exists + */ + public Component addComponent(String name) { + return this.addComponent(name, ""); + } + /** * Adds a component to this container. * From b757c669d63ee2d68707de31489e36f51610d1d3 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 13 Jan 2021 13:39:49 +0000 Subject: [PATCH 198/717] Added some rules about what values the group property can be set to. --- .../model/StaticStructureElement.java | 12 ++++- .../model/StaticStructureElementTests.java | 48 +++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 structurizr-core/test/unit/com/structurizr/model/StaticStructureElementTests.java diff --git a/structurizr-core/src/com/structurizr/model/StaticStructureElement.java b/structurizr-core/src/com/structurizr/model/StaticStructureElement.java index c05732c01..8b7e57fd7 100644 --- a/structurizr-core/src/com/structurizr/model/StaticStructureElement.java +++ b/structurizr-core/src/com/structurizr/model/StaticStructureElement.java @@ -1,5 +1,7 @@ package com.structurizr.model; +import com.structurizr.util.StringUtils; + import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -29,7 +31,15 @@ public String getGroup() { * @param group the group name */ public void setGroup(String group) { - this.group = group; + if (group == null) { + this.group = null; + } else { + this.group = group.trim(); + + if (StringUtils.isNullOrEmpty(this.group)) { + this.group = null; + } + } } /** diff --git a/structurizr-core/test/unit/com/structurizr/model/StaticStructureElementTests.java b/structurizr-core/test/unit/com/structurizr/model/StaticStructureElementTests.java new file mode 100644 index 000000000..60dafabd8 --- /dev/null +++ b/structurizr-core/test/unit/com/structurizr/model/StaticStructureElementTests.java @@ -0,0 +1,48 @@ +package com.structurizr.model; + +import com.structurizr.AbstractWorkspaceTestBase; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class StaticStructureElementTests extends AbstractWorkspaceTestBase { + + @Test + public void test_getGroup_ReturnsNullByDefault() { + Person element = model.addPerson("Person"); + assertNull(element.getGroup()); + } + + @Test + public void test_setGroup() { + Person element = model.addPerson("Person"); + element.setGroup("Group"); + assertEquals("Group", element.getGroup()); + } + + @Test + public void test_setGroup_TrimsWhiteSpace() { + Person element = model.addPerson("Person"); + element.setGroup(" Group "); + assertEquals("Group", element.getGroup()); + } + + @Test + public void test_setGroup_HandlesEmptyAndNullValues() { + Person element = model.addPerson("Person"); + element.setGroup("Group"); + + element.setGroup(null); + assertNull(element.getGroup()); + + element.setGroup("Group"); + element.setGroup(""); + assertNull(element.getGroup()); + + element.setGroup("Group"); + element.setGroup(" "); + assertNull(element.getGroup()); + } + +} \ No newline at end of file From 503f1e9e3d27a580be935664fa0eac1fd91a5131 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 13 Jan 2021 13:40:53 +0000 Subject: [PATCH 199/717] Bumped version number. --- build.gradle | 2 +- docs/changelog.md | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 547f66d88..8b9b1b8e8 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.7.0' + version = '1.7.1' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 8f3d7c0f8..6350e2431 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.7.1 (unreleased) + +- Bug fixes. + ## 1.7.0 (6th January 2021) - Removes the dynamic view restrictions related to adding containers/components outside the scoped software system/container. From 9baf14d18b32b06b1d4ef759847cbd3d56924739 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 26 Jan 2021 12:05:06 +0000 Subject: [PATCH 200/717] Remove forward slash characters from view keys. --- structurizr-core/src/com/structurizr/view/View.java | 4 ++++ .../test/unit/com/structurizr/view/ViewTests.java | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/structurizr-core/src/com/structurizr/view/View.java b/structurizr-core/src/com/structurizr/view/View.java index ee3475d2f..bdf1c062a 100644 --- a/structurizr-core/src/com/structurizr/view/View.java +++ b/structurizr-core/src/com/structurizr/view/View.java @@ -116,6 +116,10 @@ public String getKey() { } void setKey(String key) { + if (key != null) { + key = key.replaceAll("/", "_"); + } + this.key = key; } diff --git a/structurizr-core/test/unit/com/structurizr/view/ViewTests.java b/structurizr-core/test/unit/com/structurizr/view/ViewTests.java index e3942bd9c..21581359c 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ViewTests.java @@ -21,6 +21,13 @@ public void test_construction() { assertNull(view.getAutomaticLayout()); } + @Test + public void test_construction_WhenTheViewKeyContainsAForwardSlashCharacter() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + StaticView view = new SystemContextView(softwareSystem, "key/1", "Description"); + assertEquals("key_1", view.getKey()); + } + @Test public void test_addAllSoftwareSystems_DoesNothing_WhenThereAreNoOtherSoftwareSystemsInTheModel() { SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); From d805abfd02e1946d2b9498e637943435286cccd4 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 28 Jan 2021 10:38:04 +0000 Subject: [PATCH 201/717] Adds name-value properties to the workspace. --- .../com/structurizr/AbstractWorkspace.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/structurizr-core/src/com/structurizr/AbstractWorkspace.java b/structurizr-core/src/com/structurizr/AbstractWorkspace.java index 310ce75cb..0375ba917 100644 --- a/structurizr-core/src/com/structurizr/AbstractWorkspace.java +++ b/structurizr-core/src/com/structurizr/AbstractWorkspace.java @@ -4,6 +4,8 @@ import java.lang.reflect.Constructor; import java.util.Date; +import java.util.HashMap; +import java.util.Map; /** * The superclass for regular and encrypted workspaces. @@ -20,6 +22,8 @@ public abstract class AbstractWorkspace { private String lastModifiedAgent; private String thumbnail; + private Map properties = new HashMap<>(); + private WorkspaceConfiguration configuration; protected AbstractWorkspace() { @@ -227,4 +231,37 @@ public void clearConfiguration() { this.configuration = null; } + /** + * Gets the collection of name-value property pairs associated with this workspace, as a Map. + * + * @return a Map (String, String) (empty if there are no properties) + */ + public Map getProperties() { + return new HashMap<>(properties); + } + + /** + * Adds a name-value pair property to this workspace. + * + * @param name the name of the property + * @param value the value of the property + */ + public void addProperty(String name, String value) { + if (name == null || name.trim().length() == 0) { + throw new IllegalArgumentException("A property name must be specified."); + } + + if (value == null || value.trim().length() == 0) { + throw new IllegalArgumentException("A property value must be specified."); + } + + properties.put(name, value); + } + + void setProperties(Map properties) { + if (properties != null) { + this.properties = new HashMap<>(properties); + } + } + } \ No newline at end of file From 7faa1dd8190188e10c690936bb1a29cff2764f33 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 28 Jan 2021 10:38:20 +0000 Subject: [PATCH 202/717] Docs typo. --- structurizr-core/src/com/structurizr/model/ModelItem.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/structurizr-core/src/com/structurizr/model/ModelItem.java b/structurizr-core/src/com/structurizr/model/ModelItem.java index 928f9f3e4..c4295275d 100644 --- a/structurizr-core/src/com/structurizr/model/ModelItem.java +++ b/structurizr-core/src/com/structurizr/model/ModelItem.java @@ -146,7 +146,7 @@ public void setUrl(String url) { } /** - * Gets the collection of name-value property pairs associated with this element, as a Map. + * Gets the collection of name-value property pairs associated with this model item, as a Map. * * @return a Map (String, String) (empty if there are no properties) */ @@ -155,7 +155,7 @@ public Map getProperties() { } /** - * Adds a name-value pair property to this element. + * Adds a name-value pair property to this model item. * * @param name the name of the property * @param value the value of the property From 78820d86e55199329859e9f37dc87ba2ffb7c199 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 28 Jan 2021 10:39:15 +0000 Subject: [PATCH 203/717] Updated to reflect release date. --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 6350e2431..b3e3a621a 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 1.7.1 (unreleased) +## 1.7.1 (28th January 2021) - Bug fixes. From e5b420657762a97aa2cddd6ce3758c0124e9a452 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 2 Feb 2021 11:59:34 +0000 Subject: [PATCH 204/717] Fixes a NullPointerException when trying to match layout information with elements that have a null description property. --- build.gradle | 2 +- docs/changelog.md | 4 +++ .../view/DefaultLayoutMergeStrategy.java | 2 +- .../view/DefaultLayoutMergeStrategyTests.java | 25 +++++++++++++++++++ 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 8b9b1b8e8..8f46068e8 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.7.1' + version = '1.7.2' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index b3e3a621a..32ead5562 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.7.2 (2nd February 2021) + +- Bug fixes. + ## 1.7.1 (28th January 2021) - Bug fixes. diff --git a/structurizr-core/src/com/structurizr/view/DefaultLayoutMergeStrategy.java b/structurizr-core/src/com/structurizr/view/DefaultLayoutMergeStrategy.java index 85f052162..8f86c1097 100644 --- a/structurizr-core/src/com/structurizr/view/DefaultLayoutMergeStrategy.java +++ b/structurizr-core/src/com/structurizr/view/DefaultLayoutMergeStrategy.java @@ -93,7 +93,7 @@ protected ElementView findElementView(View viewWithLayoutInformation, Element el if (elementView == null) { // no element was found, so try finding an element of the same type with the same description if set (in this situation, the element itself may have been renamed) if (!StringUtils.isNullOrEmpty(elementWithoutLayoutInformation.getDescription())) { - elementView = viewWithLayoutInformation.getElements().stream().filter(ev -> ev.getElement().getDescription().equals(elementWithoutLayoutInformation.getDescription()) && ev.getElement().getClass().equals(elementWithoutLayoutInformation.getClass())).findFirst().orElse(null); + elementView = viewWithLayoutInformation.getElements().stream().filter(ev -> elementWithoutLayoutInformation.getDescription().equals(ev.getElement().getDescription()) && ev.getElement().getClass().equals(elementWithoutLayoutInformation.getClass())).findFirst().orElse(null); } } diff --git a/structurizr-core/test/unit/com/structurizr/view/DefaultLayoutMergeStrategyTests.java b/structurizr-core/test/unit/com/structurizr/view/DefaultLayoutMergeStrategyTests.java index cb5a1187a..d518c8cb2 100644 --- a/structurizr-core/test/unit/com/structurizr/view/DefaultLayoutMergeStrategyTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/DefaultLayoutMergeStrategyTests.java @@ -125,4 +125,29 @@ public void test_copyLayoutInformation_WhenAnElementNameAndDescriptionAndIdHaveC assertEquals(0, view2.getElementView(container2).getY()); } + @Test + public void test_copyLayoutInformation_WhenAnElementNameAndDescriptionAndIdHaveChangedAndDescriptionWasNull() { + Workspace workspace1 = new Workspace("1", ""); + SoftwareSystem softwareSystem1 = workspace1.getModel().addSoftwareSystem("Software System"); + Container container1 = softwareSystem1.addContainer("Container"); + container1.setDescription(null); + ContainerView view1 = workspace1.getViews().createContainerView(softwareSystem1, "key", ""); + view1.add(container1); + view1.getElementView(container1).setX(123); + view1.getElementView(container1).setY(456); + + Workspace workspace2 = new Workspace("2", ""); + SoftwareSystem softwareSystem2 = workspace2.getModel().addSoftwareSystem("Software System"); + softwareSystem2.addContainer("Web Application", "Description", ""); // this element has ID 2 + Container container2 = softwareSystem2.addContainer("Database", "Description", ""); + ContainerView view2 = workspace2.getViews().createContainerView(softwareSystem2, "key", ""); + view2.add(container2); + + DefaultLayoutMergeStrategy strategy = new DefaultLayoutMergeStrategy(); + strategy.copyLayoutInformation(view1, view2); + + assertEquals(0, view2.getElementView(container2).getX()); + assertEquals(0, view2.getElementView(container2).getY()); + } + } \ No newline at end of file From 89294091f921e438458fbea4d02f90419c84ea1f Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 2 Feb 2021 12:55:57 +0000 Subject: [PATCH 205/717] Adds some initial support for custom elements ... this is work in-progress, and may change. --- .../model/CanonicalNameGenerator.java | 5 + .../com/structurizr/model/CustomElement.java | 98 ++++++++++++ .../src/com/structurizr/model/Element.java | 42 ++++++ .../structurizr/model/GroupableElement.java | 47 ++++++ .../src/com/structurizr/model/Model.java | 86 ++++++++++- .../model/StaticStructureElement.java | 32 +--- .../com/structurizr/view/ComponentView.java | 2 +- .../com/structurizr/view/ContainerView.java | 7 +- .../src/com/structurizr/view/CustomView.java | 139 ++++++++++++++++++ .../com/structurizr/view/DeploymentView.java | 4 + .../structurizr/view/SystemContextView.java | 3 +- .../structurizr/view/SystemLandscapeView.java | 2 +- .../src/com/structurizr/view/View.java | 30 ++++ .../src/com/structurizr/view/ViewSet.java | 59 +++++++- .../structurizr/model/CustomElementTests.java | 112 ++++++++++++++ .../model/DeploymentNodeTests.java | 2 +- .../com/structurizr/model/ModelTests.java | 4 +- .../com/structurizr/view/ViewSetTests.java | 50 +++++++ .../unit/com/structurizr/view/ViewTests.java | 52 ++++++- 19 files changed, 728 insertions(+), 48 deletions(-) create mode 100644 structurizr-core/src/com/structurizr/model/CustomElement.java create mode 100644 structurizr-core/src/com/structurizr/model/GroupableElement.java create mode 100644 structurizr-core/src/com/structurizr/view/CustomView.java create mode 100644 structurizr-core/test/unit/com/structurizr/model/CustomElementTests.java diff --git a/structurizr-core/src/com/structurizr/model/CanonicalNameGenerator.java b/structurizr-core/src/com/structurizr/model/CanonicalNameGenerator.java index 9d5613fee..89f5dd220 100644 --- a/structurizr-core/src/com/structurizr/model/CanonicalNameGenerator.java +++ b/structurizr-core/src/com/structurizr/model/CanonicalNameGenerator.java @@ -2,6 +2,7 @@ final class CanonicalNameGenerator { + private static final String CUSTOM_ELEMENT_TYPE = "Custom://"; private static final String PERSON_TYPE = "Person://"; private static final String SOFTWARE_SYSTEM_TYPE = "SoftwareSystem://"; private static final String CONTAINER_TYPE = "Container://"; @@ -25,6 +26,10 @@ private String formatName(String name) { .replace(DEPLOYMENT_CANONICAL_NAME_SEPARATOR, ""); } + String generate(CustomElement customElement) { + return CUSTOM_ELEMENT_TYPE + formatName(customElement); + } + String generate(Person person) { return PERSON_TYPE + formatName(person); } diff --git a/structurizr-core/src/com/structurizr/model/CustomElement.java b/structurizr-core/src/com/structurizr/model/CustomElement.java new file mode 100644 index 000000000..9e1e42645 --- /dev/null +++ b/structurizr-core/src/com/structurizr/model/CustomElement.java @@ -0,0 +1,98 @@ +package com.structurizr.model; + +import com.structurizr.util.StringUtils; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Set; + +/** + * Represents a custom element. + */ +public final class CustomElement extends GroupableElement { + + private String metadata; + + protected CustomElement() { + } + + @Override + public Element getParent() { + return null; + } + + @Override + protected Set getRequiredTags() { + return new LinkedHashSet<>(Collections.singletonList(Tags.ELEMENT)); + } + + @Override + public String getCanonicalName() { + return new CanonicalNameGenerator().generate(this); + } + + public String getMetadata() { + return metadata; + } + + public void setMetadata(String metadata) { + this.metadata = metadata; + } + + /** + * Adds a unidirectional "uses" style relationship between this custom element and the specified element. + * + * @param destination the target of the relationship + * @param description a description of the relationship (e.g. "uses", "gets data from", "sends data to") + * @return the relationship that has just been created and added to the model + */ + @Nullable + public Relationship uses(@Nonnull Element destination, String description) { + return uses(destination, description, null); + } + + /** + * Adds a unidirectional "uses" style relationship between this custom element and the specified element. + * + * @param destination the target of the relationship + * @param description a description of the relationship (e.g. "uses", "gets data from", "sends data to") + * @param technology the technology details (e.g. JSON/HTTPS) + * @return the relationship that has just been created and added to the model + */ + @Nullable + public Relationship uses(@Nonnull Element destination, String description, String technology) { + return uses(destination, description, technology, null); + } + + /** + * Adds a unidirectional "uses" style relationship between this custom element and the specified element. + * + * @param destination the target of the relationship + * @param description a description of the relationship (e.g. "uses", "gets data from", "sends data to") + * @param technology the technology details (e.g. JSON/HTTPS) + * @param interactionStyle the interaction style (sync vs async) + * @return the relationship that has just been created and added to the model + */ + @Nullable + public Relationship uses(@Nonnull Element destination, String description, String technology, InteractionStyle interactionStyle) { + return uses(destination, description, technology, interactionStyle, null); + } + + /** + * Adds a unidirectional "uses" style relationship between this custom element and the specified element. + * + * @param destination the target of the relationship + * @param description a description of the relationship (e.g. "uses", "gets data from", "sends data to") + * @param technology the technology details (e.g. JSON/HTTPS) + * @param interactionStyle the interaction style (sync vs async) + * @param tags an array of tags + * @return the relationship that has just been created and added to the model + */ + @Nullable + public Relationship uses(@Nonnull Element destination, String description, String technology, InteractionStyle interactionStyle, String[] tags) { + return getModel().addRelationship(this, destination, description, technology, interactionStyle, tags); + } + +} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/model/Element.java b/structurizr-core/src/com/structurizr/model/Element.java index 97b013477..72bbde02a 100644 --- a/structurizr-core/src/com/structurizr/model/Element.java +++ b/structurizr-core/src/com/structurizr/model/Element.java @@ -3,6 +3,8 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.structurizr.util.Url; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.util.*; /** @@ -209,6 +211,46 @@ void addRelationship(Relationship relationship) { relationships.add(relationship); } + /** + * Adds a unidirectional "uses" style relationship between this element and the specified custom element. + * + * @param destination the target of the relationship + * @param description a description of the relationship (e.g. "uses", "gets data from", "sends data to") + * @return the relationship that has just been created and added to the model + */ + @Nullable + public Relationship uses(@Nonnull CustomElement destination, String description) { + return uses(destination, description, null); + } + + /** + * Adds a unidirectional "uses" style relationship between this element and the specified custom element. + * + * @param destination the target of the relationship + * @param description a description of the relationship (e.g. "uses", "gets data from", "sends data to") + * @param technology the technology details (e.g. JSON/HTTPS) + * @return the relationship that has just been created and added to the model + */ + @Nullable + public Relationship uses(@Nonnull CustomElement destination, String description, String technology) { + return getModel().addRelationship(this, destination, description, technology, null); + } + + /** + * Adds a unidirectional "uses" style relationship between this element and the specified custom element. + * + * @param destination the target of the relationship + * @param description a description of the relationship (e.g. "uses", "gets data from", "sends data to") + * @param technology the technology details (e.g. JSON/HTTPS) + * @param interactionStyle the interaction style (sync vs async) + * @param tags an array of tags + * @return the relationship that has just been created and added to the model + */ + @Nullable + public Relationship uses(@Nonnull CustomElement destination, String description, String technology, InteractionStyle interactionStyle, String[] tags) { + return getModel().addRelationship(this, destination, description, technology, interactionStyle, tags); + } + @Override public String toString() { return "{" + getId() + " | " + getName() + " | " + getDescription() + "}"; diff --git a/structurizr-core/src/com/structurizr/model/GroupableElement.java b/structurizr-core/src/com/structurizr/model/GroupableElement.java new file mode 100644 index 000000000..bb37c1e1b --- /dev/null +++ b/structurizr-core/src/com/structurizr/model/GroupableElement.java @@ -0,0 +1,47 @@ +package com.structurizr.model; + +import com.structurizr.util.StringUtils; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Set; + +/** + * Represents a custom element. + */ +public abstract class GroupableElement extends Element { + + private String group; + + GroupableElement() { + } + + /** + * Gets the group in which this element should be included in. + * + * @return the group name, or null if not set + */ + public String getGroup() { + return group; + } + + /** + * Sets the group in which this element should be included in. + * + * @param group the group name + */ + public void setGroup(String group) { + if (group == null) { + this.group = null; + } else { + this.group = group.trim(); + + if (StringUtils.isNullOrEmpty(this.group)) { + this.group = null; + } + } + } + +} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/model/Model.java b/structurizr-core/src/com/structurizr/model/Model.java index b2c0853ea..eb310923f 100644 --- a/structurizr-core/src/com/structurizr/model/Model.java +++ b/structurizr-core/src/com/structurizr/model/Model.java @@ -23,6 +23,7 @@ public final class Model { private Set people = new LinkedHashSet<>(); private Set softwareSystems = new LinkedHashSet<>(); private Set deploymentNodes = new LinkedHashSet<>(); + private Set customElements = new LinkedHashSet<>(); private ImpliedRelationshipsStrategy impliedRelationshipsStrategy = new DefaultImpliedRelationshipsStrategy(); @@ -94,7 +95,7 @@ public SoftwareSystem addSoftwareSystem(@Nullable Location location, @Nonnull St return softwareSystem; } else { - throw new IllegalArgumentException("A software system named '" + name + "' already exists."); + throw new IllegalArgumentException("A top-level element named '" + name + "' already exists."); } } @@ -147,10 +148,52 @@ public Person addPerson(Location location, @Nonnull String name, @Nullable Strin return person; } else { - throw new IllegalArgumentException("A person named '" + name + "' already exists."); + throw new IllegalArgumentException("A top-level element named '" + name + "' already exists."); } } + /** + * Creates a custom element and adds it to the model. + * + * @param name the name of the custom element + * @return the CustomElement instance created and added to the model (or null) + * @throws IllegalArgumentException if a custom element/person/software system with the same name already exists + */ + @Nonnull + public CustomElement addCustomElement(@Nonnull String name) { + return addCustomElement(name, "", ""); + } + + /** + * Creates a custom element and adds it to the model. + * + * @param name the name of the custom element + * @param description a short description of the custom element + * @param metadata the metadata of the custom element + * @return the CustomElement instance created and added to the model (or null) + * @throws IllegalArgumentException if a custom element/person/software system with the same name already exists + */ + @Nonnull + public CustomElement addCustomElement(@Nonnull String name, @Nullable String metadata, @Nullable String description) { + if (getCustomElementWithName(name) == null) { + CustomElement customElement = new CustomElement(); + customElement.setName(name); + customElement.setMetadata(metadata); + customElement.setDescription(description); + + customElements.add(customElement); + + customElement.setId(idGenerator.generateId(customElement)); + addElementToInternalStructures(customElement); + + return customElement; + } else { + throw new IllegalArgumentException("A top-level element named '" + name + "' already exists."); + } + } + + + @Nonnull Container addContainer(SoftwareSystem parent, @Nonnull String name, @Nullable String description, @Nullable String technology) { if (parent.getContainerWithName(name) == null) { @@ -339,6 +382,22 @@ public Relationship getRelationship(@Nonnull String id) { return relationshipsById.get(id); } + /** + * Gets the set of all custom elements in this model. + * + * @return a Set of CustomElement instances + */ + @Nonnull + public Set getCustomElements() { + return new LinkedHashSet<>(customElements); + } + + void setCustomElements(Set customElements) { + if (customElements != null) { + this.customElements = new LinkedHashSet<>(customElements); + } + } + /** * Gets the set of all people in this model. * @@ -389,6 +448,7 @@ void setDeploymentNodes(Set deploymentNodes) { void hydrate() { // add all of the elements to the model + customElements.forEach(this::addElementToInternalStructures); people.forEach(this::addElementToInternalStructures); for (SoftwareSystem softwareSystem : softwareSystems) { @@ -584,6 +644,28 @@ public Person getPersonWithName(@Nonnull String name) { return null; } + /** + * Gets the custom element with the specified name. + * + * @param name the name of the custom element + * @return the CustomElement instance with the specified name (or null if it doesn't exist) + * @throws IllegalArgumentException if the name is null or empty + */ + @Nullable + public CustomElement getCustomElementWithName(@Nonnull String name) { + if (name == null || name.trim().length() == 0) { + throw new IllegalArgumentException("A custom element name must be specified."); + } + + for (CustomElement customElement : getCustomElements()) { + if (customElement.getName().equals(name)) { + return customElement; + } + } + + return null; + } + /** *

    Propagates all relationships from children to their parents. For example, if you have two components (AAA and BBB) * in different software systems that have a relationship, calling this method will add the following diff --git a/structurizr-core/src/com/structurizr/model/StaticStructureElement.java b/structurizr-core/src/com/structurizr/model/StaticStructureElement.java index 8b7e57fd7..1528dcb84 100644 --- a/structurizr-core/src/com/structurizr/model/StaticStructureElement.java +++ b/structurizr-core/src/com/structurizr/model/StaticStructureElement.java @@ -9,37 +9,9 @@ * This is the superclass for model elements that describe the static structure * of a software system, namely Person, SoftwareSystem, Container and Component. */ -public abstract class StaticStructureElement extends Element { +public abstract class StaticStructureElement extends GroupableElement { - private String group; - - protected StaticStructureElement() { - } - - /** - * Gets the group in which this element should be included in (experimental feature). - * - * @return the group name, or null if not set - */ - public String getGroup() { - return group; - } - - /** - * Sets the group in which this element should be included in (experimental feature). - * - * @param group the group name - */ - public void setGroup(String group) { - if (group == null) { - this.group = null; - } else { - this.group = group.trim(); - - if (StringUtils.isNullOrEmpty(this.group)) { - this.group = null; - } - } + StaticStructureElement() { } /** diff --git a/structurizr-core/src/com/structurizr/view/ComponentView.java b/structurizr-core/src/com/structurizr/view/ComponentView.java index c43b3a1a7..6ba0b4d1b 100644 --- a/structurizr-core/src/com/structurizr/view/ComponentView.java +++ b/structurizr-core/src/com/structurizr/view/ComponentView.java @@ -283,7 +283,7 @@ private void addExternalDependency(Element element, Set components) { @Override protected void checkElementCanBeAdded(Element element) { - if (element instanceof Person) { + if (element instanceof CustomElement || element instanceof Person) { return; } diff --git a/structurizr-core/src/com/structurizr/view/ContainerView.java b/structurizr-core/src/com/structurizr/view/ContainerView.java index b02858168..420e3bcdd 100644 --- a/structurizr-core/src/com/structurizr/view/ContainerView.java +++ b/structurizr-core/src/com/structurizr/view/ContainerView.java @@ -1,9 +1,6 @@ package com.structurizr.view; -import com.structurizr.model.Container; -import com.structurizr.model.Element; -import com.structurizr.model.Person; -import com.structurizr.model.SoftwareSystem; +import com.structurizr.model.*; import javax.annotation.Nonnull; @@ -176,7 +173,7 @@ public final void addDependentSoftwareSystems() { @Override protected void checkElementCanBeAdded(Element element) { - if (element instanceof Person) { + if (element instanceof CustomElement || element instanceof Person) { return; } diff --git a/structurizr-core/src/com/structurizr/view/CustomView.java b/structurizr-core/src/com/structurizr/view/CustomView.java new file mode 100644 index 000000000..ede9b6f4c --- /dev/null +++ b/structurizr-core/src/com/structurizr/view/CustomView.java @@ -0,0 +1,139 @@ +package com.structurizr.view; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.structurizr.model.CustomElement; +import com.structurizr.model.Element; +import com.structurizr.model.Model; +import com.structurizr.model.Relationship; + +import javax.annotation.Nonnull; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Represents a custom view, containing custom elements. + */ +public final class CustomView extends View { + + private List animations = new ArrayList<>(); + + private Model model; + + CustomView() { + } + + CustomView(Model model, String key, String title, String description) { + super(null, key, description); + + setTitle(title); + this.model = model; + } + + /** + * Gets the (computed) name of this view. + * + * @return the name, as a String + */ + @Override + public String getName() { + return "Custom - " + getTitle(); + } + + /** + * Gets the model that this view belongs to. + * + * @return a Model object + */ + @JsonIgnore + @Override + public Model getModel() { + return this.model; + } + + void setModel(Model model) { + this.model = model; + } + + @Override + protected void checkElementCanBeAdded(Element element) { + if (element instanceof CustomElement) { + // all good + } else { + throw new ElementNotPermittedInViewException("Only custom elements can be added to a custom view."); + } + } + + @Override + protected boolean canBeRemoved(Element element) { + return true; + } + + /** + * Adds a specific relationship to this view. + * + * @param relationship the Relationship to be added + * @return a RelationshipView object representing the relationship added + */ + public RelationshipView add(@Nonnull Relationship relationship) { + return addRelationship(relationship); + } + + /** + * Adds an animation step, with the specified elements. + * + * @param elements the elements that should be shown in the animation step + */ + public void addAnimation(CustomElement... elements) { + if (elements == null || elements.length == 0) { + throw new IllegalArgumentException("One or more elements must be specified."); + } + + Set elementIdsInPreviousAnimationSteps = new HashSet<>(); + + for (Animation animationStep : animations) { + elementIdsInPreviousAnimationSteps.addAll(animationStep.getElements()); + } + + Set elementsInThisAnimationStep = new HashSet<>(); + Set relationshipsInThisAnimationStep = new HashSet<>(); + + for (CustomElement element : elements) { + if (isElementInView(element)) { + if (!elementIdsInPreviousAnimationSteps.contains(element.getId())) { + elementIdsInPreviousAnimationSteps.add(element.getId()); + elementsInThisAnimationStep.add(element); + } + } + } + + if (elementsInThisAnimationStep.size() == 0) { + throw new IllegalArgumentException("None of the specified elements exist in this view."); + } + + for (RelationshipView relationshipView : this.getRelationships()) { + if ( + (elementsInThisAnimationStep.contains(relationshipView.getRelationship().getSource()) && elementIdsInPreviousAnimationSteps.contains(relationshipView.getRelationship().getDestination().getId())) || + (elementIdsInPreviousAnimationSteps.contains(relationshipView.getRelationship().getSource().getId()) && elementsInThisAnimationStep.contains(relationshipView.getRelationship().getDestination())) + ) { + relationshipsInThisAnimationStep.add(relationshipView.getRelationship()); + } + } + + animations.add(new Animation(animations.size() + 1, elementsInThisAnimationStep, relationshipsInThisAnimationStep)); + } + + public List getAnimations() { + return new ArrayList<>(animations); + } + + void setAnimations(List animations) { + if (animations != null) { + this.animations = new ArrayList<>(animations); + } else { + this.animations = new ArrayList<>(); + } + } + +} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/view/DeploymentView.java b/structurizr-core/src/com/structurizr/view/DeploymentView.java index 1d079e561..603e08749 100644 --- a/structurizr-core/src/com/structurizr/view/DeploymentView.java +++ b/structurizr-core/src/com/structurizr/view/DeploymentView.java @@ -221,6 +221,10 @@ public void setEnvironment(String environment) { @Override protected void checkElementCanBeAdded(Element elementToBeAdded) { + if (elementToBeAdded instanceof CustomElement) { + return; + } + if (!(elementToBeAdded instanceof DeploymentElement)) { throw new ElementNotPermittedInViewException("Only deployment nodes, infrastructure nodes, software system instances, and container instances can be added to deployment views."); } diff --git a/structurizr-core/src/com/structurizr/view/SystemContextView.java b/structurizr-core/src/com/structurizr/view/SystemContextView.java index e82d192a5..b2db64da6 100644 --- a/structurizr-core/src/com/structurizr/view/SystemContextView.java +++ b/structurizr-core/src/com/structurizr/view/SystemContextView.java @@ -1,5 +1,6 @@ package com.structurizr.view; +import com.structurizr.model.CustomElement; import com.structurizr.model.Element; import com.structurizr.model.Person; import com.structurizr.model.SoftwareSystem; @@ -90,7 +91,7 @@ public void setEnterpriseBoundaryVisible(boolean enterpriseBoundaryVisible) { @Override protected void checkElementCanBeAdded(Element element) { - if (element instanceof Person || element instanceof SoftwareSystem) { + if (element instanceof CustomElement || element instanceof Person || element instanceof SoftwareSystem) { // all good } else { throw new ElementNotPermittedInViewException("Only people and software systems can be added to a system context view."); diff --git a/structurizr-core/src/com/structurizr/view/SystemLandscapeView.java b/structurizr-core/src/com/structurizr/view/SystemLandscapeView.java index bb8e12699..72ac6edd8 100644 --- a/structurizr-core/src/com/structurizr/view/SystemLandscapeView.java +++ b/structurizr-core/src/com/structurizr/view/SystemLandscapeView.java @@ -107,7 +107,7 @@ public void setEnterpriseBoundaryVisible(boolean enterpriseBoundaryVisible) { @Override protected void checkElementCanBeAdded(Element element) { - if (element instanceof Person || element instanceof SoftwareSystem) { + if (element instanceof CustomElement || element instanceof Person || element instanceof SoftwareSystem) { // all good } else { throw new ElementNotPermittedInViewException("Only people and software systems can be added to a system landscape view."); diff --git a/structurizr-core/src/com/structurizr/view/View.java b/structurizr-core/src/com/structurizr/view/View.java index bdf1c062a..725b70981 100644 --- a/structurizr-core/src/com/structurizr/view/View.java +++ b/structurizr-core/src/com/structurizr/view/View.java @@ -456,4 +456,34 @@ final void checkParentAndChildrenHaveNotAlreadyBeenAdded(StaticStructureElement } } + /** + * Adds the given custom element to this view, including relationships to/from that custom element. + * + * @param customElement the CustomElement to add + */ + public void add(@Nonnull CustomElement customElement) { + add(customElement, true); + } + + /** + * Adds the given custom element to this view. + * + * @param customElement the CustomElement to add + * @param addRelationships whether to add relationships to/from the custom element + */ + public void add(@Nonnull CustomElement customElement, boolean addRelationships) { + addElement(customElement, addRelationships); + } + + /** + * Removes the given custom element from this view. + * + * @param customElement the CustomElement to add + */ + public void remove(@Nonnull CustomElement customElement) { + removeElement(customElement); + } + + + } \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/view/ViewSet.java b/structurizr-core/src/com/structurizr/view/ViewSet.java index 1290d4ed4..63fc68f65 100644 --- a/structurizr-core/src/com/structurizr/view/ViewSet.java +++ b/structurizr-core/src/com/structurizr/view/ViewSet.java @@ -2,14 +2,12 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonSetter; -import com.structurizr.Workspace; import com.structurizr.WorkspaceValidationException; import com.structurizr.model.*; import com.structurizr.util.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import java.text.DecimalFormat; import java.util.*; import static com.structurizr.util.StringUtils.isNullOrEmpty; @@ -23,6 +21,7 @@ public final class ViewSet { private Model model; + private Collection customViews = new HashSet<>(); private Collection systemLandscapeViews = new HashSet<>(); private Collection systemContextViews = new HashSet<>(); private Collection containerViews = new HashSet<>(); @@ -41,12 +40,30 @@ public final class ViewSet { this.model = model; } + /** + * Creates a custom view view. + * + * @param key the key for the view (must be unique) + * @param title a title of the view + * @param description a description of the view + * @return a CustomView object + * @throws IllegalArgumentException if the key is not unique + */ + public CustomView createCustomView(String key, String title, String description) { + assertThatTheViewKeyIsSpecifiedAndUnique(key); + + CustomView view = new CustomView(model, key, title, description); + view.setViewSet(this); + customViews.add(view); + return view; + } + /** * Creates a system landscape view. * * @param key the key for the view (must be unique) * @param description a description of the view - * @return an SystemLandscapeView object + * @return a SystemLandscapeView object * @throws IllegalArgumentException if the key is not unique */ public SystemLandscapeView createSystemLandscapeView(String key, String description) { @@ -280,6 +297,7 @@ View getViewWithKey(String key) { } Set views = new HashSet<>(); + views.addAll(customViews); views.addAll(systemLandscapeViews); views.addAll(systemContextViews); views.addAll(containerViews); @@ -304,6 +322,21 @@ FilteredView getFilteredViewWithKey(String key) { return filteredViews.stream().filter(v -> key.equals(v.getKey())).findFirst().orElse(null); } + /** + * Gets the set of custom views. + * + * @return a Collection of CustomView objects + */ + public Collection getCustomViews() { + return new HashSet<>(customViews); + } + + void setCustomViews(Set customViews) { + if (customViews != null) { + this.customViews = new HashSet<>(customViews); + } + } + /** * Gets the set of system landscape views. * @@ -438,6 +471,11 @@ void hydrate(Model model) { checkViewKeysAreUnique(); + for (CustomView view : customViews) { + view.setModel(model); + hydrateView(view); + } + for (SystemLandscapeView view : systemLandscapeViews) { view.setModel(model); hydrateView(view); @@ -561,6 +599,7 @@ private void hydrateView(View view) { private void checkViewKeysAreUnique() { Set keys = new HashSet<>(); Collection views = new ArrayList<>(); + views.addAll(customViews); views.addAll(systemLandscapeViews); views.addAll(systemContextViews); views.addAll(containerViews); @@ -595,6 +634,17 @@ public Configuration getConfiguration() { } public void copyLayoutInformationFrom(ViewSet source) { + for (CustomView view : customViews) { + if (view.getAutomaticLayout() == null) { + CustomView sourceView = findView(source.getCustomViews(), view); + if (sourceView != null) { + view.copyLayoutInformationFrom(sourceView); + } else { + log.warn("Could not find a matching view for \"" + view.getName() + "\" ... diagram layout information may be lost."); + } + } + } + for (SystemLandscapeView view : systemLandscapeViews) { if (view.getAutomaticLayout() == null) { SystemLandscapeView sourceView = findView(source.getSystemLandscapeViews(), view); @@ -686,7 +736,7 @@ private T findView(Collection views, T sourceView) { @JsonIgnore public boolean isEmpty() { - return systemLandscapeViews.isEmpty() && systemContextViews.isEmpty() && containerViews.isEmpty() && componentViews.isEmpty() && dynamicViews.isEmpty() && deploymentViews.isEmpty() && filteredViews.isEmpty(); + return customViews.isEmpty() && systemLandscapeViews.isEmpty() && systemContextViews.isEmpty() && containerViews.isEmpty() && componentViews.isEmpty() && dynamicViews.isEmpty() && deploymentViews.isEmpty() && filteredViews.isEmpty(); } public void createDefaultViews() { @@ -797,6 +847,7 @@ private Set getContainerInstances(DeploymentNode deploymentNo * Removes all views and configuration. */ public void clear() { + customViews = new HashSet<>(); systemLandscapeViews = new HashSet<>(); systemContextViews = new HashSet<>(); containerViews = new HashSet<>(); diff --git a/structurizr-core/test/unit/com/structurizr/model/CustomElementTests.java b/structurizr-core/test/unit/com/structurizr/model/CustomElementTests.java new file mode 100644 index 000000000..6637c088c --- /dev/null +++ b/structurizr-core/test/unit/com/structurizr/model/CustomElementTests.java @@ -0,0 +1,112 @@ +package com.structurizr.model; + +import com.structurizr.AbstractWorkspaceTestBase; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class CustomElementTests extends AbstractWorkspaceTestBase { + + @Test + public void test_basicProperties() { + CustomElement element = model.addCustomElement("Name", "Type", "Description"); + assertEquals("Name", element.getName()); + assertEquals("Type", element.getMetadata()); + assertEquals("Description", element.getDescription()); + } + + @Test + public void test_getCanonicalName() { + CustomElement element = model.addCustomElement("Name", "Type", "Description"); + assertEquals("Custom://Name", element.getCanonicalName()); + } + + @Test + public void test_getCanonicalName_WhenNameContainsSlashAndDotCharacters() { + CustomElement element = model.addCustomElement("Name", "Type", "Description"); + element.setName("Name1/.Name2"); + assertEquals("Custom://Name1Name2", element.getCanonicalName()); + } + + @Test + public void test_getParent_ReturnsNull() { + CustomElement element = model.addCustomElement("Name", "Type", "Description"); + assertNull(element.getParent()); + } + + @Test + public void test_removeTags_DoesNotRemoveRequiredTags() { + CustomElement element = model.addCustomElement("Name", "Type", "Description"); + assertTrue(element.getTags().contains(Tags.ELEMENT)); + + element.removeTag(Tags.ELEMENT); + + assertTrue(element.getTags().contains(Tags.ELEMENT)); + } + + @Test + public void test_uses_AddsARelationshipWhenTheDescriptionIsSpecified() { + CustomElement element1 = model.addCustomElement("Box 1"); + CustomElement element2 = model.addCustomElement("Box 2"); + + element1.uses(element2, "Uses"); + assertEquals(1, element1.getRelationships().size()); + + Relationship relationship = element1.getRelationships().iterator().next(); + assertSame(element1, relationship.getSource()); + assertSame(element2, relationship.getDestination()); + assertEquals("Uses", relationship.getDescription()); + assertNull(relationship.getTechnology()); + assertNull(relationship.getInteractionStyle()); + } + + @Test + public void test_uses_AddsARelationshipWhenTheDescriptionAndTechnologyAreSpecified() { + CustomElement element1 = model.addCustomElement("Box 1"); + CustomElement element2 = model.addCustomElement("Box 2"); + + element1.uses(element2, "Uses", "Technology"); + assertEquals(1, element1.getRelationships().size()); + + Relationship relationship = element1.getRelationships().iterator().next(); + assertSame(element1, relationship.getSource()); + assertSame(element2, relationship.getDestination()); + assertEquals("Uses", relationship.getDescription()); + assertEquals("Technology", relationship.getTechnology()); + assertEquals(null, relationship.getInteractionStyle()); + } + + @Test + public void test_uses_AddsARelationshipWhenTheDescriptionAndTechnologyAndInteractionStyleAreSpecified() { + CustomElement element1 = model.addCustomElement("Box 1"); + CustomElement element2 = model.addCustomElement("Box 2"); + + element1.uses(element2, "Uses", "Technology", InteractionStyle.Asynchronous); + assertEquals(1, element1.getRelationships().size()); + + Relationship relationship = element1.getRelationships().iterator().next(); + assertSame(element1, relationship.getSource()); + assertSame(element2, relationship.getDestination()); + assertEquals("Uses", relationship.getDescription()); + assertEquals("Technology", relationship.getTechnology()); + assertEquals(InteractionStyle.Asynchronous, relationship.getInteractionStyle()); + } + + @Test + public void test_uses_AddsARelationshipWhenTheDescriptionAndTechnologyAndInteractionStyleAndTagsAreSpecified() { + CustomElement element1 = model.addCustomElement("Box 1"); + CustomElement element2 = model.addCustomElement("Box 2"); + + element1.uses(element2, "Uses", "Technology", InteractionStyle.Asynchronous, new String[] { "Tag 1", "Tag 2" }); + assertEquals(1, element1.getRelationships().size()); + + Relationship relationship = element1.getRelationships().iterator().next(); + assertSame(element1, relationship.getSource()); + assertSame(element2, relationship.getDestination()); + assertEquals("Uses", relationship.getDescription()); + assertEquals("Technology", relationship.getTechnology()); + assertEquals(InteractionStyle.Asynchronous, relationship.getInteractionStyle()); + assertEquals("Relationship,Asynchronous,Tag 1,Tag 2", relationship.getTags()); + } + +} \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java b/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java index 721fb1108..4e7f4e2b0 100644 --- a/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java @@ -134,7 +134,7 @@ public void test_addDeploymentNode_AddsAChildDeploymentNode_WhenANameIsSpecified public void test_uses_ThrowsAnException_WhenANullDestinationIsSpecified() { try { DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node", "", ""); - deploymentNode.uses(null, "", ""); + deploymentNode.uses((DeploymentNode)null, "", ""); fail(); } catch (IllegalArgumentException iae) { assertEquals("The destination must be specified.", iae.getMessage()); diff --git a/structurizr-core/test/unit/com/structurizr/model/ModelTests.java b/structurizr-core/test/unit/com/structurizr/model/ModelTests.java index 77da30646..0a0b07aab 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ModelTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ModelTests.java @@ -52,7 +52,7 @@ public void test_addSoftwareSystem_ThrowsAnException_WhenASoftwareSystemExistsWi model.addSoftwareSystem(Location.External, "System A", "Description"); fail(); } catch (Exception e) { - assertEquals("A software system named 'System A' already exists.", e.getMessage()); + assertEquals("A top-level element named 'System A' already exists.", e.getMessage()); } } @@ -91,7 +91,7 @@ public void test_addPerson_ThrowsAnException_WhenAPersonExistsWithTheSameName() model.addPerson(Location.External, "Admin User", "Description"); fail(); } catch (Exception e) { - assertEquals("A person named 'Admin User' already exists.", e.getMessage()); + assertEquals("A top-level element named 'Admin User' already exists.", e.getMessage()); } } diff --git a/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java b/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java index b62e5a198..d7f905b6f 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java @@ -27,6 +27,56 @@ private Workspace createWorkspace() { return workspace; } + @Test + public void test_createCustomView_ThrowsAnException_WhenANullKeyIsSpecified() { + try { + new Workspace("", "").getViews().createCustomView(null, "Title", "Description"); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("A key must be specified.", iae.getMessage()); + } + } + + @Test + public void test_createCustomView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { + try { + new Workspace("", "").getViews().createCustomView(" ", "Title", "Description"); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("A key must be specified.", iae.getMessage()); + } + } + + @Test + public void test_createCustomView_ThrowsAnException_WhenADuplicateKeyIsSpecified() { + try { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getViews().createCustomView("key", "Title", "Description"); + workspace.getViews().createCustomView("key", "Title", "Description"); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("A view with the key key already exists.", iae.getMessage()); + } + } + + @Test + public void test_createCustomView_DoesNotThrowAnException_WhenUniqueKeysAreSpecified() { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getViews().createCustomView("key1", "Title", "Description"); + workspace.getViews().createCustomView("key2", "Title", "Description"); + } + + @Test + public void test_createCustomView() { + Workspace workspace = new Workspace("Name", "Description"); + CustomView customView = workspace.getViews().createCustomView("key", "Title", "Description"); + assertEquals("key", customView.getKey()); + assertEquals("Title", customView.getTitle()); + assertEquals("Description", customView.getDescription()); + + assertEquals(1, workspace.getViews().getCustomViews().size()); + } + @Test public void test_createSystemLandscapeView_ThrowsAnException_WhenANullKeyIsSpecified() { try { diff --git a/structurizr-core/test/unit/com/structurizr/view/ViewTests.java b/structurizr-core/test/unit/com/structurizr/view/ViewTests.java index 21581359c..77ad00f5a 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ViewTests.java @@ -439,4 +439,54 @@ public void test_enableAutomaticLayout() { assertTrue(view.getAutomaticLayout().isVertices()); } -} + @Test + public void test_addCustomElement_AddsTheCustomElementToTheView() { + Workspace workspace = new Workspace("", ""); + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "Description"); + + CustomElement box1 = workspace.getModel().addCustomElement("Box 1"); + CustomElement box2 = workspace.getModel().addCustomElement("Box 2"); + box1.uses(box2, "Uses"); + + view.add(box1); + assertEquals(1, view.getElements().size()); + assertEquals(0, view.getRelationships().size()); + + view.add(box2); + assertEquals(2, view.getElements().size()); + assertEquals(1, view.getRelationships().size()); + } + + @Test + public void test_addCustomElementWithoutRelationships_AddsTheCustomElementToTheView() { + Workspace workspace = new Workspace("", ""); + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "Description"); + + CustomElement box1 = workspace.getModel().addCustomElement("Box 1"); + CustomElement box2 = workspace.getModel().addCustomElement("Box 2"); + box1.uses(box2, "Uses"); + + view.add(box1); + assertEquals(1, view.getElements().size()); + assertEquals(0, view.getRelationships().size()); + + view.add(box2, false); + assertEquals(2, view.getElements().size()); + assertEquals(0, view.getRelationships().size()); + } + + @Test + public void test_removeCustomElement_RemovesTheCustomElementFromTheView() { + Workspace workspace = new Workspace("", ""); + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "Description"); + + CustomElement box1 = workspace.getModel().addCustomElement("Box 1"); + + view.add(box1); + assertEquals(1, view.getElements().size()); + + view.remove(box1); + assertEquals(0, view.getElements().size()); + } + +} \ No newline at end of file From 5a42d5bc0131dbed1480232a4fff232f75bb6db4 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 6 Feb 2021 06:44:37 +0000 Subject: [PATCH 206/717] Minor tweaks. --- build.gradle | 2 +- docs/changelog.md | 4 ++++ .../src/com/structurizr/model/GroupableElement.java | 6 +++--- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 8f46068e8..0fd41badf 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.7.2' + version = '1.8.0' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 32ead5562..78f02f64a 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.8.0 (unreleased) + +- Adds support for custom elements and custom views. + ## 1.7.2 (2nd February 2021) - Bug fixes. diff --git a/structurizr-core/src/com/structurizr/model/GroupableElement.java b/structurizr-core/src/com/structurizr/model/GroupableElement.java index bb37c1e1b..3ddcf516b 100644 --- a/structurizr-core/src/com/structurizr/model/GroupableElement.java +++ b/structurizr-core/src/com/structurizr/model/GroupableElement.java @@ -9,7 +9,7 @@ import java.util.Set; /** - * Represents a custom element. + * Represents an element that can be included in a group. */ public abstract class GroupableElement extends Element { @@ -19,7 +19,7 @@ public abstract class GroupableElement extends Element { } /** - * Gets the group in which this element should be included in. + * Gets the name of the group in which this element should be included in. * * @return the group name, or null if not set */ @@ -28,7 +28,7 @@ public String getGroup() { } /** - * Sets the group in which this element should be included in. + * Sets the name of the group in which this element should be included in. * * @param group the group name */ From b9232db5ab7fd926af338e0fb0cfcb38c73a4471 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 16 Feb 2021 17:34:04 +0000 Subject: [PATCH 207/717] Fixes a bug with canonical names on deployment nodes. --- .../com/structurizr/model/CanonicalNameGenerator.java | 5 +++-- .../unit/com/structurizr/model/DeploymentNodeTests.java | 9 ++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/structurizr-core/src/com/structurizr/model/CanonicalNameGenerator.java b/structurizr-core/src/com/structurizr/model/CanonicalNameGenerator.java index 89f5dd220..7c11624a1 100644 --- a/structurizr-core/src/com/structurizr/model/CanonicalNameGenerator.java +++ b/structurizr-core/src/com/structurizr/model/CanonicalNameGenerator.java @@ -53,13 +53,14 @@ String generate(DeploymentNode deploymentNode) { buf.append(formatName(deploymentNode.getEnvironment())); buf.append(DEPLOYMENT_CANONICAL_NAME_SEPARATOR); + String parents = ""; DeploymentNode parent = (DeploymentNode)deploymentNode.getParent(); while (parent != null) { - buf.append(formatName(parent)); - buf.append(DEPLOYMENT_CANONICAL_NAME_SEPARATOR); + parents = formatName(parent) + DEPLOYMENT_CANONICAL_NAME_SEPARATOR + parents; parent = (DeploymentNode)parent.getParent(); } + buf.append(parents); buf.append(formatName(deploymentNode)); return buf.toString(); diff --git a/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java b/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java index 4e7f4e2b0..52892fbad 100644 --- a/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java @@ -17,10 +17,13 @@ public void test_getCanonicalName_WhenTheDeploymentNodeHasNoParent() { @Test public void test_getCanonicalName_WhenTheDeploymentNodeHasAParent() { - DeploymentNode parent = model.addDeploymentNode("Windows Server", "", ""); - DeploymentNode child = parent.addDeploymentNode("Apache Tomcat", "", ""); + DeploymentNode l1 = model.addDeploymentNode("Level 1", "", ""); + DeploymentNode l2 = l1.addDeploymentNode("Level 2", "", ""); + DeploymentNode l3 = l2.addDeploymentNode("Level 3", "", ""); - assertEquals("DeploymentNode://Default/Windows Server/Apache Tomcat", child.getCanonicalName()); + assertEquals("DeploymentNode://Default/Level 1", l1.getCanonicalName()); + assertEquals("DeploymentNode://Default/Level 1/Level 2", l2.getCanonicalName()); + assertEquals("DeploymentNode://Default/Level 1/Level 2/Level 3", l3.getCanonicalName()); } @Test From ca9c0db446f26b2a843023073ad65e1076b28124 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 16 Feb 2021 17:37:52 +0000 Subject: [PATCH 208/717] Make Animation class public. --- structurizr-core/src/com/structurizr/view/Animation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structurizr-core/src/com/structurizr/view/Animation.java b/structurizr-core/src/com/structurizr/view/Animation.java index 474f9be0a..c15f89dd0 100644 --- a/structurizr-core/src/com/structurizr/view/Animation.java +++ b/structurizr-core/src/com/structurizr/view/Animation.java @@ -9,7 +9,7 @@ /** * A wrapper for a collection of animation steps. */ -final class Animation { +public final class Animation { private int order; private Set elements = new HashSet<>(); From bc272960bba29427fc2b0a91999dbf8c7538227d Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 16 Feb 2021 17:39:36 +0000 Subject: [PATCH 209/717] Added a shorter version of addContainer. --- .../src/com/structurizr/model/SoftwareSystem.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/structurizr-core/src/com/structurizr/model/SoftwareSystem.java b/structurizr-core/src/com/structurizr/model/SoftwareSystem.java index 43a819902..401dfb58c 100644 --- a/structurizr-core/src/com/structurizr/model/SoftwareSystem.java +++ b/structurizr-core/src/com/structurizr/model/SoftwareSystem.java @@ -83,7 +83,20 @@ void setContainers(Set containers) { */ @Nonnull public Container addContainer(@Nonnull String name) { - return addContainer(name, "", ""); + return addContainer(name, ""); + } + + /** + * Adds a container with the specified name and description. + * + * @param name the name of the container (e.g. "Web Application") + * @param description a short description/list of responsibilities + * @return the newly created Container instance added to the model (or null) + * @throws IllegalArgumentException if a container with the same name exists already + */ + @Nonnull + public Container addContainer(@Nonnull String name, String description) { + return addContainer(name, description, ""); } /** From 54db8e00cc640df7d1c2b428c5806d85567e0f03 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 16 Feb 2021 17:42:03 +0000 Subject: [PATCH 210/717] More tests. --- .../com/structurizr/view/ComponentView.java | 25 ----------------- .../com/structurizr/view/ContainerView.java | 1 - ...tTests.java => GroupableElementTests.java} | 2 +- .../structurizr/view/ComponentViewTests.java | 28 +++++++++++++++++++ .../structurizr/view/ContainerViewTests.java | 15 +++++++++- .../structurizr/view/DynamicViewTests.java | 1 - .../unit/com/structurizr/view/ViewTests.java | 2 +- 7 files changed, 44 insertions(+), 30 deletions(-) rename structurizr-core/test/unit/com/structurizr/model/{StaticStructureElementTests.java => GroupableElementTests.java} (94%) diff --git a/structurizr-core/src/com/structurizr/view/ComponentView.java b/structurizr-core/src/com/structurizr/view/ComponentView.java index 6ba0b4d1b..7160cd6e2 100644 --- a/structurizr-core/src/com/structurizr/view/ComponentView.java +++ b/structurizr-core/src/com/structurizr/view/ComponentView.java @@ -63,31 +63,6 @@ void setContainer(Container container) { this.container = container; } - /** - * Adds the specified software system, including relationships to/from that software system. - * Please note that the parent software system of the container in scope cannot be added to this view. - * - * @param softwareSystem the SoftwareSystem to add to this view - */ - @Override - public void add(@Nonnull SoftwareSystem softwareSystem) { - add(softwareSystem, true); - } - - /** - * Adds the specified software system. - * Please note that the parent software system of the container in scope cannot be added to this view. - * - * @param softwareSystem the SoftwareSystem to add to this view - * @param addRelationships whether to add relationships to/from the component - */ - @Override - public void add(@Nonnull SoftwareSystem softwareSystem, boolean addRelationships) { - if (softwareSystem != null && !softwareSystem.equals(getSoftwareSystem())) { - addElement(softwareSystem, addRelationships); - } - } - /** * Adds all other containers in the software system to this view. */ diff --git a/structurizr-core/src/com/structurizr/view/ContainerView.java b/structurizr-core/src/com/structurizr/view/ContainerView.java index 420e3bcdd..539b1403c 100644 --- a/structurizr-core/src/com/structurizr/view/ContainerView.java +++ b/structurizr-core/src/com/structurizr/view/ContainerView.java @@ -192,7 +192,6 @@ protected void checkElementCanBeAdded(Element element) { throw new ElementNotPermittedInViewException("Only people, software systems, and containers can be added to a container view."); } - @Override protected boolean canBeRemoved(Element element) { return true; diff --git a/structurizr-core/test/unit/com/structurizr/model/StaticStructureElementTests.java b/structurizr-core/test/unit/com/structurizr/model/GroupableElementTests.java similarity index 94% rename from structurizr-core/test/unit/com/structurizr/model/StaticStructureElementTests.java rename to structurizr-core/test/unit/com/structurizr/model/GroupableElementTests.java index 60dafabd8..abd70a3e3 100644 --- a/structurizr-core/test/unit/com/structurizr/model/StaticStructureElementTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/GroupableElementTests.java @@ -6,7 +6,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; -public class StaticStructureElementTests extends AbstractWorkspaceTestBase { +public class GroupableElementTests extends AbstractWorkspaceTestBase { @Test public void test_getGroup_ReturnsNullByDefault() { diff --git a/structurizr-core/test/unit/com/structurizr/view/ComponentViewTests.java b/structurizr-core/test/unit/com/structurizr/view/ComponentViewTests.java index 7f5c9136c..c34ea6edf 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ComponentViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ComponentViewTests.java @@ -598,4 +598,32 @@ public void test_addDefaultElements() { assertFalse(view.getElements().contains(new ElementView(component2))); } + @Test + public void test_addSoftwareSystem_ThrowsAnException_WhenTheSoftwareSystemIsTheScopeOfTheView() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + + view = new ComponentView(container, "components", "Description"); + try { + view.add(softwareSystem); + fail(); + } catch (ElementNotPermittedInViewException e) { + assertEquals("The software system in scope cannot be added to a component view.", e.getMessage()); + } + } + + @Test + public void test_addContainer_ThrowsAnException_WhenTheContainerIsTheScopeOfTheView() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + + view = new ComponentView(container, "components", "Description"); + try { + view.add(container); + fail(); + } catch (ElementNotPermittedInViewException e) { + assertEquals("The container in scope cannot be added to a component view.", e.getMessage()); + } + } + } \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java b/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java index 9ae2baecf..0416ed83d 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java @@ -293,4 +293,17 @@ public void test_addDefaultElements() { assertFalse(view.getElements().contains(new ElementView(container2))); } -} + @Test + public void test_addSoftwareSystem_ThrowsAnException_WhenTheSoftwareSystemIsTheScopeOfTheView() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + + view = new ContainerView(softwareSystem, "containers", "Description"); + try { + view.add(softwareSystem); + fail(); + } catch (ElementNotPermittedInViewException e) { + assertEquals("The software system in scope cannot be added to a container view.", e.getMessage()); + } + } + +} \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java b/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java index ba9d42ff4..49fdb36d7 100644 --- a/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java @@ -80,7 +80,6 @@ public void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsNotSpecifie public void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsNotSpecifiedButAComponentIsAdded() { try { DynamicView dynamicView = workspace.getViews().createDynamicView("key", "Description"); - DeploymentNode deploymentNode = workspace.getModel().addDeploymentNode("Deployment node", "Description", "Technology"); dynamicView.add(componentA1, componentA1); fail(); } catch (ElementNotPermittedInViewException iae) { diff --git a/structurizr-core/test/unit/com/structurizr/view/ViewTests.java b/structurizr-core/test/unit/com/structurizr/view/ViewTests.java index 77ad00f5a..acb6941ac 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ViewTests.java @@ -393,7 +393,7 @@ public void test_setKey_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { public void test_addElement_ThrowsAnException_WhenTheSpecifiedElementDoesNotExistInTheModel() { try { Workspace workspace = new Workspace("1", ""); - SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); SystemLandscapeView view = new Workspace("", "").getViews().createSystemLandscapeView("key", "Description"); view.add(softwareSystem); From cab761ef7a9bb705f8f2b4b3b7c7ef343226251e Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 16 Feb 2021 17:43:35 +0000 Subject: [PATCH 211/717] Adds better support for custom ID generators. --- .../structurizr/api/StructurizrClient.java | 19 +++++++- .../com/structurizr/io/json/JsonReader.java | 20 +++++++- .../com/structurizr/io/json/JsonTests.java | 46 +++++++++++++++++++ .../com/structurizr/model/IdGenerator.java | 2 +- .../SequentialIntegerIdGeneratorStrategy.java | 7 ++- 5 files changed, 85 insertions(+), 9 deletions(-) diff --git a/structurizr-client/src/com/structurizr/api/StructurizrClient.java b/structurizr-client/src/com/structurizr/api/StructurizrClient.java index 77a98dc53..b24ebbe96 100644 --- a/structurizr-client/src/com/structurizr/api/StructurizrClient.java +++ b/structurizr-client/src/com/structurizr/api/StructurizrClient.java @@ -8,6 +8,7 @@ import com.structurizr.io.json.EncryptedJsonWriter; import com.structurizr.io.json.JsonReader; import com.structurizr.io.json.JsonWriter; +import com.structurizr.model.IdGenerator; import com.structurizr.util.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -64,6 +65,7 @@ public final class StructurizrClient { private EncryptionStrategy encryptionStrategy; + private IdGenerator idGenerator = null; private boolean mergeFromRemote = true; private File workspaceArchiveLocation = new File("."); @@ -119,6 +121,15 @@ public StructurizrClient(String url, String apiKey, String apiSecret) { setApiSecret(apiSecret); } + /** + * Sets the ID generator to use when parsing a JSON workspace definition. + * + * @param idGenerator an IdGenerator implementation + */ + public void setIdGenerator(IdGenerator idGenerator) { + this.idGenerator = idGenerator; + } + /** * Gets the agent string used to identify this client instance. * @@ -311,7 +322,9 @@ public Workspace getWorkspace(long workspaceId) throws StructurizrClientExceptio archiveWorkspace(workspaceId, json); if (encryptionStrategy == null) { - return new JsonReader().read(new StringReader(json)); + JsonReader jsonReader = new JsonReader(); + jsonReader.setIdGenerator(idGenerator); + return jsonReader.read(new StringReader(json)); } else { EncryptedWorkspace encryptedWorkspace = new EncryptedJsonReader().read(new StringReader(json)); @@ -320,7 +333,9 @@ public Workspace getWorkspace(long workspaceId) throws StructurizrClientExceptio return encryptedWorkspace.getWorkspace(); } else { // this workspace isn't encrypted, even though the client has an encryption strategy set - return new JsonReader().read(new StringReader(json)); + JsonReader jsonReader = new JsonReader(); + jsonReader.setIdGenerator(idGenerator); + return jsonReader.read(new StringReader(json)); } } } else { diff --git a/structurizr-client/src/com/structurizr/io/json/JsonReader.java b/structurizr-client/src/com/structurizr/io/json/JsonReader.java index 83ef6724d..c85ee6665 100644 --- a/structurizr-client/src/com/structurizr/io/json/JsonReader.java +++ b/structurizr-client/src/com/structurizr/io/json/JsonReader.java @@ -1,11 +1,11 @@ package com.structurizr.io.json; -import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.structurizr.Workspace; -import com.structurizr.WorkspaceValidationException; import com.structurizr.io.WorkspaceReader; import com.structurizr.io.WorkspaceReaderException; +import com.structurizr.model.IdGenerator; +import com.structurizr.model.SequentialIntegerIdGeneratorStrategy; import java.io.IOException; import java.io.Reader; @@ -15,6 +15,17 @@ */ public final class JsonReader extends AbstractJsonReader implements WorkspaceReader { + private IdGenerator idGenerator = null; + + /** + * Sets the ID generator to use when parsing a JSON workspace definition. + * + * @param idGenerator an IdGenerator implementation + */ + public void setIdGenerator(IdGenerator idGenerator) { + this.idGenerator = idGenerator; + } + /** * Reads and parses a workspace definition from a JSON document. * @@ -27,6 +38,11 @@ public Workspace read(Reader reader) throws WorkspaceReaderException { ObjectMapper objectMapper = createObjectMapper(); Workspace workspace = objectMapper.readValue(reader, Workspace.class); + + if (idGenerator != null) { + workspace.getModel().setIdGenerator(idGenerator); + } + workspace.hydrate(); return workspace; diff --git a/structurizr-client/test/unit/com/structurizr/io/json/JsonTests.java b/structurizr-client/test/unit/com/structurizr/io/json/JsonTests.java index 0baa9cdac..baab4d7fc 100644 --- a/structurizr-client/test/unit/com/structurizr/io/json/JsonTests.java +++ b/structurizr-client/test/unit/com/structurizr/io/json/JsonTests.java @@ -1,12 +1,15 @@ package com.structurizr.io.json; import com.structurizr.Workspace; +import com.structurizr.model.*; import org.junit.Test; import java.io.StringReader; import java.io.StringWriter; +import java.util.UUID; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; public class JsonTests { @@ -44,4 +47,47 @@ public void test_backwardsCompatibilityOfRenamingEnterpriseContextViewsToSystemL assertEquals(1, workspace.getViews().getSystemLandscapeViews().size()); } + @Test + public void test_write_and_read_withCustomIdGenerator() throws Exception { + Workspace workspace1 = new Workspace("Name", "Description"); + workspace1.getModel().setIdGenerator(new CustomIdGenerator()); + Person user = workspace1.getModel().addPerson("User"); + SoftwareSystem softwareSystem = workspace1.getModel().addSoftwareSystem("Software System"); + user.uses(softwareSystem, "Uses"); + + // output the model as JSON + JsonWriter jsonWriter = new JsonWriter(true); + StringWriter stringWriter = new StringWriter(); + jsonWriter.write(workspace1, stringWriter); + + // and read it back again + JsonReader jsonReader = new JsonReader(); + jsonReader.setIdGenerator(new CustomIdGenerator()); + StringReader stringReader = new StringReader(stringWriter.toString()); + + Workspace workspace2 = jsonReader.read(stringReader); + assertEquals(user.getId(), workspace2.getModel().getPersonWithName("User").getId()); + assertNotNull(workspace2.getModel().getElement(user.getId())); + } + + class CustomIdGenerator implements IdGenerator { + + @Override + public String generateId(Element element) { + return UUID.randomUUID().toString(); + } + + @Override + public String generateId(Relationship relationship) { + return UUID.randomUUID().toString(); + } + + @Override + public void found(String id) { + + } + + } + + } \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/model/IdGenerator.java b/structurizr-core/src/com/structurizr/model/IdGenerator.java index 8e1b2786e..002d84150 100644 --- a/structurizr-core/src/com/structurizr/model/IdGenerator.java +++ b/structurizr-core/src/com/structurizr/model/IdGenerator.java @@ -1,7 +1,7 @@ package com.structurizr.model; /** - * The interface that ID generators, used when creating IDs for model elements, must implement. + * The interface that ID generators, used when creating IDs for model elements/relationships, must implement. */ public interface IdGenerator { diff --git a/structurizr-core/src/com/structurizr/model/SequentialIntegerIdGeneratorStrategy.java b/structurizr-core/src/com/structurizr/model/SequentialIntegerIdGeneratorStrategy.java index 4b8e171ee..e8f57950c 100644 --- a/structurizr-core/src/com/structurizr/model/SequentialIntegerIdGeneratorStrategy.java +++ b/structurizr-core/src/com/structurizr/model/SequentialIntegerIdGeneratorStrategy.java @@ -2,7 +2,7 @@ /** * An ID generator that simply uses a sequential number when generating IDs for model elements and relationships. - * This is the default ID generator. + * This is the default ID generator; any non-numeric IDs are ignored. */ public class SequentialIntegerIdGeneratorStrategy implements IdGenerator { @@ -25,9 +25,8 @@ public void found(String id) { if (idAsInt > ID) { ID = idAsInt; } - } - catch(NumberFormatException e) { - // ignore non-numeric ids + } catch (NumberFormatException e) { + // ignore non-numeric IDs } } From a75b737f6783db4c19cfc743ab00eda1e80fbaf4 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 16 Feb 2021 17:44:33 +0000 Subject: [PATCH 212/717] Provide canonical names for relationships. --- .../structurizr/model/CanonicalNameGenerator.java | 13 +++++++++++++ .../src/com/structurizr/model/Element.java | 9 --------- .../src/com/structurizr/model/ModelItem.java | 3 +++ .../src/com/structurizr/model/Relationship.java | 5 +++++ 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/structurizr-core/src/com/structurizr/model/CanonicalNameGenerator.java b/structurizr-core/src/com/structurizr/model/CanonicalNameGenerator.java index 7c11624a1..b998e7d9d 100644 --- a/structurizr-core/src/com/structurizr/model/CanonicalNameGenerator.java +++ b/structurizr-core/src/com/structurizr/model/CanonicalNameGenerator.java @@ -1,5 +1,7 @@ package com.structurizr.model; +import com.structurizr.util.StringUtils; + final class CanonicalNameGenerator { private static final String CUSTOM_ELEMENT_TYPE = "Custom://"; @@ -16,6 +18,8 @@ final class CanonicalNameGenerator { private static final String STATIC_CANONICAL_NAME_SEPARATOR = "."; private static final String DEPLOYMENT_CANONICAL_NAME_SEPARATOR = "/"; + private static final String RELATIONSHIP_TYPE = "Relationship://"; + private String formatName(Element element) { return formatName(element.getName()); } @@ -84,4 +88,13 @@ String generate(ContainerInstance containerInstance) { return CONTAINER_INSTANCE_TYPE + deploymentNodeCanonicalName + DEPLOYMENT_CANONICAL_NAME_SEPARATOR + generate(containerInstance.getContainer()).substring(CONTAINER_TYPE.length()) + "[" + containerInstance.getInstanceId() + "]"; } + String generate(Relationship relationship) { + if (StringUtils.isNullOrEmpty(relationship.getDescription())) { + return RELATIONSHIP_TYPE + relationship.getSource().getCanonicalName() + " -> " + relationship.getDestination().getCanonicalName(); + } else { + return RELATIONSHIP_TYPE + relationship.getSource().getCanonicalName() + " -> " + relationship.getDestination().getCanonicalName() + " (" + relationship.getDescription() + ")"; + } + } + + } \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/model/Element.java b/structurizr-core/src/com/structurizr/model/Element.java index 72bbde02a..640cc66ec 100644 --- a/structurizr-core/src/com/structurizr/model/Element.java +++ b/structurizr-core/src/com/structurizr/model/Element.java @@ -12,8 +12,6 @@ */ public abstract class Element extends ModelItem { - static final String CANONICAL_NAME_SEPARATOR = "/"; - private Model model; private String name; @@ -55,13 +53,6 @@ void setName(String name) { this.name = name; } - @JsonIgnore - public abstract String getCanonicalName(); - - String formatForCanonicalName(String name) { - return name.replace(CANONICAL_NAME_SEPARATOR, ""); - } - /** * Gets a description of this element. * diff --git a/structurizr-core/src/com/structurizr/model/ModelItem.java b/structurizr-core/src/com/structurizr/model/ModelItem.java index c4295275d..874dfb4d4 100644 --- a/structurizr-core/src/com/structurizr/model/ModelItem.java +++ b/structurizr-core/src/com/structurizr/model/ModelItem.java @@ -19,6 +19,9 @@ public abstract class ModelItem { private Map properties = new HashMap<>(); private Set perspectives = new HashSet<>(); + @JsonIgnore + public abstract String getCanonicalName(); + protected abstract Set getRequiredTags(); /** diff --git a/structurizr-core/src/com/structurizr/model/Relationship.java b/structurizr-core/src/com/structurizr/model/Relationship.java index e70a514df..093c232f1 100644 --- a/structurizr-core/src/com/structurizr/model/Relationship.java +++ b/structurizr-core/src/com/structurizr/model/Relationship.java @@ -48,6 +48,11 @@ protected void setModel(Model model) { this.model = model; } + @Override + public String getCanonicalName() { + return new CanonicalNameGenerator().generate(this); + } + @JsonIgnore public Element getSource() { return source; From 164e916a544f0bbb69bfde11adc627a436692132 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 16 Feb 2021 17:49:12 +0000 Subject: [PATCH 213/717] Tidied up the relationship handling methods. --- .../src/com/structurizr/model/Element.java | 8 +-- .../src/com/structurizr/model/Model.java | 5 ++ .../src/com/structurizr/model/Person.java | 8 +-- .../model/StaticStructureElement.java | 69 +++++++++++++++++-- .../com/structurizr/model/PersonTests.java | 4 +- 5 files changed, 78 insertions(+), 16 deletions(-) diff --git a/structurizr-core/src/com/structurizr/model/Element.java b/structurizr-core/src/com/structurizr/model/Element.java index 640cc66ec..01937349a 100644 --- a/structurizr-core/src/com/structurizr/model/Element.java +++ b/structurizr-core/src/com/structurizr/model/Element.java @@ -168,13 +168,11 @@ public Set getEfferentRelationshipsWith(Element element) { } /** - * Determines whether this element has an efferent (outgoing) relationship with - * the specified element and description. + * Gets the efferent (outgoing) relationship with the specified element and description. * * @param element the element to look for * @param description the relationship description - * @return true if this element has an efferent relationship with the specified element and description, - * false otherwise + * @return a Relationship object, or null if the specified relationship doesn't exist */ public Relationship getEfferentRelationshipWith(Element element, String description) { if (element == null) { @@ -186,7 +184,7 @@ public Relationship getEfferentRelationshipWith(Element element, String descript } for (Relationship relationship : relationships) { - if (relationship.getDestination().equals(element) && relationship.getDescription().equals(description)) { + if (relationship.getDestination().equals(element) && description.equals(relationship.getDescription())) { return relationship; } } diff --git a/structurizr-core/src/com/structurizr/model/Model.java b/structurizr-core/src/com/structurizr/model/Model.java index eb310923f..d5d27434e 100644 --- a/structurizr-core/src/com/structurizr/model/Model.java +++ b/structurizr-core/src/com/structurizr/model/Model.java @@ -237,6 +237,11 @@ Component addComponentOfType(Container parent, String name, String type, String } } + @Nullable + Relationship addRelationship(Element source, @Nonnull Element destination, String description, String technology) { + return addRelationship(source, destination, description, technology, null); + } + @Nullable Relationship addRelationship(Element source, @Nonnull Element destination, String description, String technology, InteractionStyle interactionStyle) { return addRelationship(source, destination, description, technology, interactionStyle, new String[0], true); diff --git a/structurizr-core/src/com/structurizr/model/Person.java b/structurizr-core/src/com/structurizr/model/Person.java index eadf9ec08..3cdae2738 100644 --- a/structurizr-core/src/com/structurizr/model/Person.java +++ b/structurizr-core/src/com/structurizr/model/Person.java @@ -70,7 +70,7 @@ public Relationship delivers(Person destination, String description, String tech * * @param destination the Person being interacted with * @param description a description of the interaction - * @return the resulting Relatioship + * @return the resulting Relationship */ public Relationship interactsWith(Person destination, String description) { return interactsWith(destination, description, null); @@ -82,10 +82,10 @@ public Relationship interactsWith(Person destination, String description) { * @param destination the Person being interacted with * @param description a description of the interaction * @param technology the technology of the interaction (e.g. Telephone) - * @return the resulting Relatioship + * @return the resulting Relationship */ public Relationship interactsWith(Person destination, String description, String technology) { - return interactsWith(destination, description, technology, InteractionStyle.Synchronous); + return interactsWith(destination, description, technology, null); } /** @@ -95,7 +95,7 @@ public Relationship interactsWith(Person destination, String description, String * @param description a description of the interaction * @param technology the technology of the interaction (e.g. Telephone) * @param interactionStyle the interaction style (e.g. Synchronous or Asynchronous) - * @return the resulting Relatioship + * @return the resulting Relationship */ public Relationship interactsWith(Person destination, String description, String technology, InteractionStyle interactionStyle) { return getModel().addRelationship(this, destination, description, technology, interactionStyle); diff --git a/structurizr-core/src/com/structurizr/model/StaticStructureElement.java b/structurizr-core/src/com/structurizr/model/StaticStructureElement.java index 1528dcb84..930ac6140 100644 --- a/structurizr-core/src/com/structurizr/model/StaticStructureElement.java +++ b/structurizr-core/src/com/structurizr/model/StaticStructureElement.java @@ -50,7 +50,22 @@ public Relationship uses(@Nonnull SoftwareSystem destination, String description */ @Nullable public Relationship uses(@Nonnull SoftwareSystem destination, String description, String technology, InteractionStyle interactionStyle) { - return uses((StaticStructureElement) destination, description, technology, interactionStyle); + return uses(destination, description, technology, interactionStyle, new String[0]); + } + + /** + * Adds a unidirectional "uses" style relationship between this element and a software system. + * + * @param destination the target of the relationship + * @param description a description of the relationship (e.g. "uses", "gets data from", "sends data to") + * @param technology the technology details (e.g. JSON/HTTPS) + * @param interactionStyle the interaction style (sync vs async) + * @param tags an array of tags + * @return the relationship that has just been created and added to the model + */ + @Nullable + public Relationship uses(@Nonnull SoftwareSystem destination, String description, String technology, InteractionStyle interactionStyle, String[] tags) { + return uses((StaticStructureElement)destination, description, technology, interactionStyle, tags); } /** @@ -89,7 +104,22 @@ public Relationship uses(@Nonnull Container destination, String description, Str */ @Nullable public Relationship uses(@Nonnull Container destination, String description, String technology, InteractionStyle interactionStyle) { - return uses((StaticStructureElement) destination, description, technology, interactionStyle); + return uses(destination, description, technology, interactionStyle, new String[0]); + } + + /** + * Adds a unidirectional "uses" style relationship between this element and a container. + * + * @param destination the target of the relationship + * @param description a description of the relationship (e.g. "uses", "gets data from", "sends data to") + * @param technology the technology details (e.g. JSON/HTTPS) + * @param interactionStyle the interaction style (sync vs async) + * @param tags an array of tags + * @return the relationship that has just been created and added to the model + */ + @Nullable + public Relationship uses(@Nonnull Container destination, String description, String technology, InteractionStyle interactionStyle, String[] tags) { + return uses((StaticStructureElement)destination, description, technology, interactionStyle, tags); } /** @@ -128,7 +158,22 @@ public Relationship uses(@Nonnull Component destination, String description, Str */ @Nullable public Relationship uses(@Nonnull Component destination, String description, String technology, InteractionStyle interactionStyle) { - return uses((StaticStructureElement) destination, description, technology, interactionStyle); + return uses(destination, description, technology, interactionStyle, new String[0]); + } + + /** + * Adds a unidirectional "uses" style relationship between this element and a component. + * + * @param destination the target of the relationship + * @param description a description of the relationship (e.g. "uses", "gets data from", "sends data to") + * @param technology the technology details (e.g. JSON/HTTPS) + * @param interactionStyle the interaction style (sync vs async) + * @param tags an array of tags + * @return the relationship that has just been created and added to the model + */ + @Nullable + public Relationship uses(@Nonnull Component destination, String description, String technology, InteractionStyle interactionStyle, String[] tags) { + return uses((StaticStructureElement)destination, description, technology, interactionStyle, tags); } /** @@ -167,7 +212,21 @@ public Relationship delivers(@Nonnull Person destination, String description, St */ @Nullable public Relationship delivers(@Nonnull Person destination, String description, String technology, InteractionStyle interactionStyle) { - return uses(destination, description, technology, interactionStyle); + return delivers(destination, description, technology, interactionStyle, new String[0]); + } + + /** + * Adds a unidirectional relationship between this element and a person. + * + * @param destination the target of the relationship + * @param description a description of the relationship (e.g. "sends e-mail to") + * @param technology the technology details (e.g. JSON/HTTPS) + * @param interactionStyle the interaction style (sync vs async) + * @return the relationship that has just been created and added to the model + */ + @Nullable + public Relationship delivers(@Nonnull Person destination, String description, String technology, InteractionStyle interactionStyle, String[] tags) { + return getModel().addRelationship(this, destination, description, technology, interactionStyle, tags); } /** @@ -181,7 +240,7 @@ public Relationship delivers(@Nonnull Person destination, String description, St */ @Nullable public Relationship uses(@Nonnull StaticStructureElement destination, String description, String technology, InteractionStyle interactionStyle) { - return getModel().addRelationship(this, destination, description, technology, interactionStyle); + return uses(destination, description, technology, interactionStyle, new String[0]); } /** diff --git a/structurizr-core/test/unit/com/structurizr/model/PersonTests.java b/structurizr-core/test/unit/com/structurizr/model/PersonTests.java index c4c25e33c..f51836712 100644 --- a/structurizr-core/test/unit/com/structurizr/model/PersonTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/PersonTests.java @@ -52,7 +52,7 @@ public void test_interactsWith_AddsARelationshipWhenTheDescriptionIsSpecified() assertSame(person2, relationship.getDestination()); assertEquals("Sends an e-mail to", relationship.getDescription()); assertNull(relationship.getTechnology()); - assertEquals(InteractionStyle.Synchronous, relationship.getInteractionStyle()); + assertNull(relationship.getInteractionStyle()); } @Test @@ -68,7 +68,7 @@ public void test_interactsWith_AddsARelationshipWhenTheDescriptionAndTechnologyA assertSame(person2, relationship.getDestination()); assertEquals("Sends a message to", relationship.getDescription()); assertEquals("E-mail", relationship.getTechnology()); - assertEquals(InteractionStyle.Synchronous, relationship.getInteractionStyle()); + assertNull(relationship.getInteractionStyle()); } @Test From b1ba4b5adbeee70c58cd52dfa2391ca786e43be9 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 16 Feb 2021 21:16:01 +0000 Subject: [PATCH 214/717] Added more validation to software system/container instances. --- .../src/com/structurizr/model/Model.java | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/structurizr-core/src/com/structurizr/model/Model.java b/structurizr-core/src/com/structurizr/model/Model.java index d5d27434e..df84302c4 100644 --- a/structurizr-core/src/com/structurizr/model/Model.java +++ b/structurizr-core/src/com/structurizr/model/Model.java @@ -514,13 +514,25 @@ private void hydrateDeploymentNode(DeploymentNode deploymentNode, DeploymentNode deploymentNode.getChildren().forEach(child -> hydrateDeploymentNode(child, deploymentNode)); for (SoftwareSystemInstance softwareSystemInstance : deploymentNode.getSoftwareSystemInstances()) { - softwareSystemInstance.setSoftwareSystem((SoftwareSystem)getElement(softwareSystemInstance.getSoftwareSystemId())); + Element softwareSystem = getElement(softwareSystemInstance.getSoftwareSystemId()); + if (!(softwareSystem instanceof SoftwareSystem)) { + throw new WorkspaceValidationException( + String.format("A software system instance is associated with a software system (id=%s) that does not exist in the model.", softwareSystemInstance.getSoftwareSystemId())); + } + + softwareSystemInstance.setSoftwareSystem((SoftwareSystem)softwareSystem); softwareSystemInstance.setParent(deploymentNode); addElementToInternalStructures(softwareSystemInstance); } for (ContainerInstance containerInstance : deploymentNode.getContainerInstances()) { - containerInstance.setContainer((Container)getElement(containerInstance.getContainerId())); + Element container = getElement(containerInstance.getContainerId()); + if (!(container instanceof Container)) { + throw new WorkspaceValidationException( + String.format("A container instance is associated with a container (id=%s) that does not exist in the model.", containerInstance.getContainerId())); + } + + containerInstance.setContainer((Container)container); containerInstance.setParent(deploymentNode); addElementToInternalStructures(containerInstance); } From 0bd7e41820ee430fcf827c1e28dccfc648ba38bb Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 17 Feb 2021 08:45:16 +0000 Subject: [PATCH 215/717] Added an FAQ. --- README.md | 5 ++--- docs/faq.md | 13 +++++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 docs/faq.md diff --git a/README.md b/README.md index 3851db046..b24f4795f 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,7 @@ # Structurizr for Java -This GitHub repository is an official client library for the [Structurizr](https://structurizr.com) cloud service and on-premises installation, both of which are web-based publishing platforms for software architecture models based upon the [C4 model](https://c4model.com). __This repository is supported by Structurizr Limited__, as a part of the Structurizr service. - -The component finder, adr-tools importer, and alternative diagram export formats (e.g. PlantUML) can be found at [Structurizr for Java extensions](https://github.com/structurizr/java-extensions). +This GitHub repository is an official client library for the [Structurizr](https://structurizr.com) cloud service and on-premises installation, both of which are web-based publishing platforms for software architecture models based upon the [C4 model](https://c4model.com). The component finder, adr-tools importer, and alternative diagram export formats (e.g. PlantUML) can be found at [Structurizr for Java extensions](https://github.com/structurizr/java-extensions). ## A quick example @@ -43,6 +41,7 @@ The view can then be exported to be visualised using the [Structurizr cloud serv * [Building from source](docs/building.md) * [API client](docs/api-client.md) * [Usage patterns](docs/usage-patterns.md) + * [FAQ](docs/faq.md) * Model * [Creating your model](docs/model.md) * [Implied relationships](docs/implied-relationships.md) diff --git a/docs/faq.md b/docs/faq.md new file mode 100644 index 000000000..cae05f899 --- /dev/null +++ b/docs/faq.md @@ -0,0 +1,13 @@ +# Frequently asked questions + +## Why are many classes final with package-protected members, and not open to extension? + +First and foremost, this repo is a client library for the [Structurizr cloud service and on-premises installation](https://structurizr.com). It allows you to write Java code to create an in-memory object graph representing a software architecture model and views (a "workspace"), serialize that to JSON, and upload it via a web API. The workspace has an [OpenAPI definition](https://github.com/structurizr/json/blob/master/structurizr.yaml), but this library also implements a number of rules (think of them as the "business logic") to ensure that the workspace is valid. These rules include, for example, ensuring that all containers with a software system have unique names, and that you can't add components to a system context view. + +Removing the `final` modifier from the classes and leaving the them open for extension allows you to bypass/break these rules, which will likely lead to the serialized workspace definitions being incompatible with the Structurizr cloud service and on-premises installation. The output of this library also needs to be compatible with all of the other client libraries. + +You are welcome to fork this library for your own purposes. Alternatively, you can build a thin wrapper around the library, to provide your own custom functionality, or perhaps a more fluent API ... many teams have done this. + +## Can I submit a pull request? + +It depends on the nature of the change. Please open an issue first to discuss it. From 77f1c620a7020222f27591382a7df5fdf38740b5 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 17 Feb 2021 10:16:00 +0000 Subject: [PATCH 216/717] Person and Robot shapes are the same aspect ratio, so treat them the same. --- structurizr-core/src/com/structurizr/view/Styles.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structurizr-core/src/com/structurizr/view/Styles.java b/structurizr-core/src/com/structurizr/view/Styles.java index 3927318c7..f2690ea53 100644 --- a/structurizr-core/src/com/structurizr/view/Styles.java +++ b/structurizr-core/src/com/structurizr/view/Styles.java @@ -182,7 +182,7 @@ public ElementStyle findElementStyle(Element element) { } if (style.getHeight() == null) { - if (style.getShape() == Shape.Person) { + if (style.getShape() == Shape.Person || style.getShape() == Shape.Robot) { style.setHeight(DEFAULT_HEIGHT_OF_PERSON); } else { style.setHeight(DEFAULT_HEIGHT_OF_ELEMENT); From f294a8907fd74762896a7aee717336b7663a2bd5 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 20 Feb 2021 15:15:22 +0000 Subject: [PATCH 217/717] Fixes a javadoc warning. --- .../src/com/structurizr/model/StaticStructureElement.java | 1 + 1 file changed, 1 insertion(+) diff --git a/structurizr-core/src/com/structurizr/model/StaticStructureElement.java b/structurizr-core/src/com/structurizr/model/StaticStructureElement.java index 930ac6140..56d7b055e 100644 --- a/structurizr-core/src/com/structurizr/model/StaticStructureElement.java +++ b/structurizr-core/src/com/structurizr/model/StaticStructureElement.java @@ -222,6 +222,7 @@ public Relationship delivers(@Nonnull Person destination, String description, St * @param description a description of the relationship (e.g. "sends e-mail to") * @param technology the technology details (e.g. JSON/HTTPS) * @param interactionStyle the interaction style (sync vs async) + * @param tags an array of tags * @return the relationship that has just been created and added to the model */ @Nullable From 0a9eb4a3bb2c676733aa531b363d2845e94ea4aa Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 20 Feb 2021 15:22:15 +0000 Subject: [PATCH 218/717] Updated to reflect release. --- docs/changelog.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 78f02f64a..d1caa21a9 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,8 +1,9 @@ # Changelog -## 1.8.0 (unreleased) +## 1.8.0 (20th February 2021) -- Adds support for custom elements and custom views. +- Adds support for custom elements and custom views (experimental). +- Bug fixes and improved workspace validation. ## 1.7.2 (2nd February 2021) From 12610c3652780ff995c61a4d6a587e24a8224ce9 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 20 Feb 2021 15:38:33 +0000 Subject: [PATCH 219/717] Updated docs re: new version. --- docs/binaries.md | 4 ++-- docs/getting-started.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/binaries.md b/docs/binaries.md index ba3633c8f..76f14dec6 100644 --- a/docs/binaries.md +++ b/docs/binaries.md @@ -3,5 +3,5 @@ The "Structurizr for Java" binaries are hosted on [Maven Central](https://repo1. Name | Description ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-core:1.6.3 | The core library that can used to create software architecture models. -com.structurizr:structurizr-client:1.6.3 | The API client for publishing models on the Structurizr cloud service and on-premises installation. \ No newline at end of file +com.structurizr:structurizr-core:1.8.0 | The core library that can used to create software architecture models. +com.structurizr:structurizr-client:1.8.0 | The API client for publishing models on the Structurizr cloud service and on-premises installation. \ No newline at end of file diff --git a/docs/getting-started.md b/docs/getting-started.md index d491b4868..d51dd247d 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -12,7 +12,7 @@ The Structurizr for Java binaries are hosted on [Maven Central](https://repo1.ma Name | Description ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-client:1.6.3 | The Structurizr API client library. +com.structurizr:structurizr-client:1.8.0 | The Structurizr API client library. ## 2. Create a Java program From bd2b7edd5ccd95d73e47dc6182fd193ae4897b5e Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 21 Feb 2021 17:49:15 +0000 Subject: [PATCH 220/717] Added some missing functionality re: removing software system instances. --- .../com/structurizr/view/DeploymentView.java | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/structurizr-core/src/com/structurizr/view/DeploymentView.java b/structurizr-core/src/com/structurizr/view/DeploymentView.java index 603e08749..346ae1584 100644 --- a/structurizr-core/src/com/structurizr/view/DeploymentView.java +++ b/structurizr-core/src/com/structurizr/view/DeploymentView.java @@ -98,6 +98,10 @@ public void add(@Nonnull DeploymentNode deploymentNode, boolean addRelationships * @param deploymentNode the DeploymentNode to be removed */ public void remove(@Nonnull DeploymentNode deploymentNode) { + for (SoftwareSystemInstance softwareSystemInstance : deploymentNode.getSoftwareSystemInstances()) { + remove(softwareSystemInstance); + } + for (ContainerInstance containerInstance : deploymentNode.getContainerInstances()) { remove(containerInstance); } @@ -114,7 +118,7 @@ public void remove(@Nonnull DeploymentNode deploymentNode) { } /** - * Removes the given infrastructure node from this view. + * Removes an infrastructure node from this view. * * @param infrastructureNode the InfrastructureNode to be removed */ @@ -123,7 +127,16 @@ public void remove(@Nonnull InfrastructureNode infrastructureNode) { } /** - * Removes the given infrastructure node from this view. + * Removes a software system instance from this view. + * + * @param softwareSystemInstance the SoftwareSystemInstance to be removed + */ + public void remove(@Nonnull SoftwareSystemInstance softwareSystemInstance) { + removeElement(softwareSystemInstance); + } + + /** + * Removes a container instance from this view. * * @param containerInstance the ContainerInstance to be removed */ From a82474d1a14b27295412aa52ca761d344b0ea56c Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 21 Feb 2021 21:24:33 +0000 Subject: [PATCH 221/717] Allows individual deployment elements to be added, plus added some missing tests. --- .../com/structurizr/view/DeploymentView.java | 42 +++++++ .../structurizr/view/DeploymentViewTests.java | 112 +++++++++++++++++- 2 files changed, 153 insertions(+), 1 deletion(-) diff --git a/structurizr-core/src/com/structurizr/view/DeploymentView.java b/structurizr-core/src/com/structurizr/view/DeploymentView.java index 346ae1584..7d5831c61 100644 --- a/structurizr-core/src/com/structurizr/view/DeploymentView.java +++ b/structurizr-core/src/com/structurizr/view/DeploymentView.java @@ -92,6 +92,48 @@ public void add(@Nonnull DeploymentNode deploymentNode, boolean addRelationships } } + /** + * Adds an infrastructure node (and its parent deployment nodes) to this view. + * + * @param infrastructureNode the InfrastructureNode to add + */ + public void add(@Nonnull InfrastructureNode infrastructureNode) { + addElement(infrastructureNode, true); + DeploymentNode parent = (DeploymentNode)infrastructureNode.getParent(); + while (parent != null) { + addElement(parent, true); + parent = (DeploymentNode)parent.getParent(); + } + } + + /** + * Adds a software system instance (and its parent deployment nodes) to this view. + * + * @param softwareSystemInstance the SoftwareSystemInstance to add + */ + public void add(@Nonnull SoftwareSystemInstance softwareSystemInstance) { + addElement(softwareSystemInstance, true); + DeploymentNode parent = (DeploymentNode)softwareSystemInstance.getParent(); + while (parent != null) { + addElement(parent, true); + parent = (DeploymentNode)parent.getParent(); + } + } + + /** + * Adds a container instance (and its parent deployment nodes) to this view. + * + * @param containerInstance the ContainerInstance to add + */ + public void add(@Nonnull ContainerInstance containerInstance) { + addElement(containerInstance, true); + DeploymentNode parent = (DeploymentNode)containerInstance.getParent(); + while (parent != null) { + addElement(parent, true); + parent = (DeploymentNode)parent.getParent(); + } + } + /** * Removes the given deployment node from this view. * diff --git a/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java b/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java index f64cffeef..dd38ba656 100644 --- a/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java @@ -371,9 +371,28 @@ public void test_remove_RemovesTheInfrastructureNode() { assertTrue(deploymentView.getElements().contains(new ElementView(containerInstance))); } + @Test + public void test_remove_RemovesTheSoftwareSystemInstance() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + DeploymentNode deploymentNodeParent = model.addDeploymentNode("Deployment Node", "Description", "Technology"); + DeploymentNode deploymentNodeChild = deploymentNodeParent.addDeploymentNode("Deployment Node", "Description", "Technology"); + InfrastructureNode infrastructureNode = deploymentNodeChild.addInfrastructureNode("Infrastructure Node"); + SoftwareSystemInstance softwareSystemInstance = deploymentNodeChild.add(softwareSystem); + + deploymentView = views.createDeploymentView("deployment", "Description"); + deploymentView.addAllDeploymentNodes(); + assertEquals(4, deploymentView.getElements().size()); + + deploymentView.remove(softwareSystemInstance); + assertEquals(3, deploymentView.getElements().size()); + assertTrue(deploymentView.getElements().contains(new ElementView(deploymentNodeParent))); + assertTrue(deploymentView.getElements().contains(new ElementView(deploymentNodeChild))); + assertTrue(deploymentView.getElements().contains(new ElementView(infrastructureNode))); + } + @Test public void test_remove_RemovesTheContainerInstance() { - SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); Container container = softwareSystem.addContainer("Container", "Description", "Technology"); DeploymentNode deploymentNodeParent = model.addDeploymentNode("Deployment Node", "Description", "Technology"); DeploymentNode deploymentNodeChild = deploymentNodeParent.addDeploymentNode("Deployment Node", "Description", "Technology"); @@ -426,4 +445,95 @@ public void test_remove_RemovesTheChildDeploymentNodeAndChildren() { assertEquals(0, deploymentView.getElements().size()); } + @Test + public void test_add_AddsTheInfrastructureNode() { + DeploymentNode deploymentNodeParent = model.addDeploymentNode("Deployment Node", "Description", "Technology"); + DeploymentNode deploymentNodeChild = deploymentNodeParent.addDeploymentNode("Deployment Node", "Description", "Technology"); + InfrastructureNode infrastructureNode1 = deploymentNodeChild.addInfrastructureNode("Infrastructure Node 1"); + InfrastructureNode infrastructureNode2 = deploymentNodeChild.addInfrastructureNode("Infrastructure Node 2"); + + deploymentView = views.createDeploymentView("deployment", "Description"); + deploymentView.add(infrastructureNode1); + + assertEquals(3, deploymentView.getElements().size()); + assertTrue(deploymentView.getElements().contains(new ElementView(deploymentNodeParent))); + assertTrue(deploymentView.getElements().contains(new ElementView(deploymentNodeChild))); + assertTrue(deploymentView.getElements().contains(new ElementView(infrastructureNode1))); + } + + @Test + public void test_add_AddsTheSoftwareSystemInstance() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + DeploymentNode deploymentNodeParent = model.addDeploymentNode("Deployment Node", "Description", "Technology"); + DeploymentNode deploymentNodeChild = deploymentNodeParent.addDeploymentNode("Deployment Node", "Description", "Technology"); + InfrastructureNode infrastructureNode = deploymentNodeChild.addInfrastructureNode("Infrastructure Node "); + SoftwareSystemInstance softwareSystemInstance = deploymentNodeChild.add(softwareSystem); + + deploymentView = views.createDeploymentView("deployment", "Description"); + deploymentView.add(softwareSystemInstance); + + assertEquals(3, deploymentView.getElements().size()); + assertTrue(deploymentView.getElements().contains(new ElementView(deploymentNodeParent))); + assertTrue(deploymentView.getElements().contains(new ElementView(deploymentNodeChild))); + assertTrue(deploymentView.getElements().contains(new ElementView(softwareSystemInstance))); + } + + @Test + public void test_addSoftwareSystemInstance_ThrowsAnException_WhenAChildContainerInstanceHasAlreadyBeenAdded() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + DeploymentNode deploymentNodeParent = model.addDeploymentNode("Deployment Node", "Description", "Technology"); + DeploymentNode deploymentNodeChild = deploymentNodeParent.addDeploymentNode("Deployment Node", "Description", "Technology"); + SoftwareSystemInstance softwareSystemInstance = deploymentNodeChild.add(softwareSystem); + ContainerInstance containerInstance = deploymentNodeChild.add(container); + + deploymentView = views.createDeploymentView("deployment", "Description"); + deploymentView.add(containerInstance); + + try { + deploymentView.add(softwareSystemInstance); + fail(); + } catch (ElementNotPermittedInViewException e) { + assertEquals("A child of Software System is already in this view.", e.getMessage()); + } + } + + @Test + public void test_add_AddsTheContainerInstance() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + DeploymentNode deploymentNodeParent = model.addDeploymentNode("Deployment Node", "Description", "Technology"); + DeploymentNode deploymentNodeChild = deploymentNodeParent.addDeploymentNode("Deployment Node", "Description", "Technology"); + InfrastructureNode infrastructureNode = deploymentNodeChild.addInfrastructureNode("Infrastructure Node "); + ContainerInstance containerInstance = deploymentNodeChild.add(container); + + deploymentView = views.createDeploymentView("deployment", "Description"); + deploymentView.add(containerInstance); + + assertEquals(3, deploymentView.getElements().size()); + assertTrue(deploymentView.getElements().contains(new ElementView(deploymentNodeParent))); + assertTrue(deploymentView.getElements().contains(new ElementView(deploymentNodeChild))); + assertTrue(deploymentView.getElements().contains(new ElementView(containerInstance))); + } + + @Test + public void test_addContainerInstance_ThrowsAnException_WhenTheParentSoftwareSystemInstanceHasAlreadyBeenAdded() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + DeploymentNode deploymentNodeParent = model.addDeploymentNode("Deployment Node", "Description", "Technology"); + DeploymentNode deploymentNodeChild = deploymentNodeParent.addDeploymentNode("Deployment Node", "Description", "Technology"); + SoftwareSystemInstance softwareSystemInstance = deploymentNodeChild.add(softwareSystem); + ContainerInstance containerInstance = deploymentNodeChild.add(container); + + deploymentView = views.createDeploymentView("deployment", "Description"); + deploymentView.add(softwareSystemInstance); + + try { + deploymentView.add(containerInstance); + fail(); + } catch (ElementNotPermittedInViewException e) { + assertEquals("The parent of Container is already in this view.", e.getMessage()); + } + } + } \ No newline at end of file From e794930fe48d5b76d3641c6c8b0b5d8a0d1e3cb2 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 23 Feb 2021 18:03:48 +0000 Subject: [PATCH 222/717] Fixes a typo. --- structurizr-core/src/com/structurizr/view/View.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structurizr-core/src/com/structurizr/view/View.java b/structurizr-core/src/com/structurizr/view/View.java index 725b70981..f57d24f58 100644 --- a/structurizr-core/src/com/structurizr/view/View.java +++ b/structurizr-core/src/com/structurizr/view/View.java @@ -415,7 +415,7 @@ public ElementView getElementView(@Nonnull Element element) { * Gets the relationship view for the given relationship. * * @param relationship the Relationship to find the RelationshipView for - * @return an RelationshipView object, or null if the relationship doesn't exist in the view + * @return a RelationshipView object, or null if the relationship doesn't exist in the view */ public RelationshipView getRelationshipView(@Nonnull Relationship relationship) { Optional relationshipView = this.relationshipViews.stream().filter(rv -> rv.getId().equals(relationship.getId())).findFirst(); From bd48c2ab1a04b9f6741b582d9be4dc7554a50ff2 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 23 Feb 2021 18:07:34 +0000 Subject: [PATCH 223/717] Adds some missing functionality to deployment views. --- build.gradle | 2 +- docs/changelog.md | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 0fd41badf..9d0454998 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.8.0' + version = '1.8.1' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index d1caa21a9..0a58ec009 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,10 @@ # Changelog +## 1.8.1 (unreleased) + +- Adds support for adding individual infrastructure nodes, software system instances, and container instances to a deployment view. +- Adds support for removing software system instances from deployment views. + ## 1.8.0 (20th February 2021) - Adds support for custom elements and custom views (experimental). From 8d5713178ad24a5c4dca41ed28a93bd1cbb966d9 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 9 Mar 2021 11:08:51 +0000 Subject: [PATCH 224/717] Improved support for themes (e.g. when exporting to PlantUML), which now works the same as described at https://structurizr.com/help/themes --- docs/changelog.md | 1 + .../{util => view}/ThemeUtils.java | 70 +------ .../com/structurizr/util/ThemeUtilsTests.java | 47 ----- .../com/structurizr/view/ThemeUtilsTests.java | 133 +++++++++++++ .../com/structurizr/view/Configuration.java | 2 +- .../com/structurizr/view/ElementStyle.java | 51 +++++ .../structurizr/view/RelationshipStyle.java | 35 ++++ .../src/com/structurizr/view/Styles.java | 175 +++++++----------- .../src/com/structurizr/view/Theme.java | 64 +++++++ .../{StyleTests.java => StylesTests.java} | 108 ++++++++++- .../src/com/structurizr/example/Theme.java | 2 +- 11 files changed, 451 insertions(+), 237 deletions(-) rename structurizr-client/src/com/structurizr/{util => view}/ThemeUtils.java (68%) delete mode 100644 structurizr-client/test/unit/com/structurizr/util/ThemeUtilsTests.java create mode 100644 structurizr-client/test/unit/com/structurizr/view/ThemeUtilsTests.java create mode 100644 structurizr-core/src/com/structurizr/view/Theme.java rename structurizr-core/test/unit/com/structurizr/view/{StyleTests.java => StylesTests.java} (68%) diff --git a/docs/changelog.md b/docs/changelog.md index 0a58ec009..29bfa374e 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -4,6 +4,7 @@ - Adds support for adding individual infrastructure nodes, software system instances, and container instances to a deployment view. - Adds support for removing software system instances from deployment views. +- Improved support for themes (e.g. when exporting to PlantUML), which now works the same as described at [Structurizr - Themes](https://structurizr.com/help/themes). ## 1.8.0 (20th February 2021) diff --git a/structurizr-client/src/com/structurizr/util/ThemeUtils.java b/structurizr-client/src/com/structurizr/view/ThemeUtils.java similarity index 68% rename from structurizr-client/src/com/structurizr/util/ThemeUtils.java rename to structurizr-client/src/com/structurizr/view/ThemeUtils.java index a0dd13e51..4a13de295 100644 --- a/structurizr-client/src/com/structurizr/util/ThemeUtils.java +++ b/structurizr-client/src/com/structurizr/view/ThemeUtils.java @@ -1,14 +1,11 @@ -package com.structurizr.util; +package com.structurizr.view; -import com.fasterxml.jackson.annotation.JsonGetter; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.structurizr.Workspace; import com.structurizr.io.WorkspaceWriterException; -import com.structurizr.view.ElementStyle; -import com.structurizr.view.RelationshipStyle; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; @@ -17,8 +14,6 @@ import java.io.*; import java.nio.charset.StandardCharsets; -import java.util.Collection; -import java.util.LinkedList; /** * Some utility methods for exporting themes to JSON. @@ -70,7 +65,7 @@ public static String toJson(Workspace workspace) throws Exception { * @param workspace a Workspace object * @throws Exception if something goes wrong */ - public static void loadStylesFromThemes(Workspace workspace) throws Exception { + public static void loadThemes(Workspace workspace) throws Exception { if (workspace.getViews().getConfiguration().getThemes() != null) { for (String url : workspace.getViews().getConfiguration().getThemes()) { CloseableHttpClient httpClient = HttpClients.createSystem(); @@ -86,13 +81,7 @@ public static void loadStylesFromThemes(Workspace workspace) throws Exception { Theme theme = objectMapper.readValue(json, Theme.class); - for (ElementStyle elementStyle : theme.getElements()) { - workspace.getViews().getConfiguration().getStyles().add(elementStyle); - } - - for (RelationshipStyle relationshipStyle : theme.getRelationships()) { - workspace.getViews().getConfiguration().getStyles().add(relationshipStyle); - } + workspace.getViews().getConfiguration().getStyles().addStylesFromTheme(url, theme.getElements(), theme.getRelationships()); } httpClient.close(); @@ -123,57 +112,4 @@ private static void write(Workspace workspace, Writer writer) throws Exception { writer.close(); } - static class Theme { - - private String name; - private String description; - private Collection elements = new LinkedList<>(); - private Collection relationships = new LinkedList<>(); - - Theme() { - } - - Theme(String name, String description, Collection elements, Collection relationships) { - this.name = name; - this.description = description; - this.elements = elements; - this.relationships = relationships; - } - - public String getName() { - return name; - } - - void setName(String name) { - this.name = name; - } - - public String getDescription() { - return description; - } - - void setDescription(String description) { - this.description = description; - } - - @JsonGetter - Collection getElements() { - return elements; - } - - void setElements(Collection elements) { - this.elements = elements; - } - - @JsonGetter - Collection getRelationships() { - return relationships; - } - - void setRelationships(Collection relationships) { - this.relationships = relationships; - } - - } - } \ No newline at end of file diff --git a/structurizr-client/test/unit/com/structurizr/util/ThemeUtilsTests.java b/structurizr-client/test/unit/com/structurizr/util/ThemeUtilsTests.java deleted file mode 100644 index 4570a34f4..000000000 --- a/structurizr-client/test/unit/com/structurizr/util/ThemeUtilsTests.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.structurizr.util; - -import com.structurizr.Workspace; -import com.structurizr.model.Tags; -import org.junit.Test; - -import static junit.framework.TestCase.assertTrue; -import static org.junit.Assert.assertEquals; - -public class ThemeUtilsTests { - - @Test - public void test_loadStylesFromThemes() throws Exception { - Workspace workspace = new Workspace("Name", "Description"); - workspace.getViews().getConfiguration().setTheme("https://raw.githubusercontent.com/structurizr/themes/master/amazon-web-services/theme.json"); - - ThemeUtils.loadStylesFromThemes(workspace); - - assertTrue(workspace.getViews().getConfiguration().getStyles().getElements().size() > 0); - assertTrue(workspace.getViews().getConfiguration().getStyles().getElements().stream().anyMatch(s -> s.getTag().equals("Amazon Web Services - Cloud"))); - } - - @Test - public void test_toJson() throws Exception { - Workspace workspace = new Workspace("Name", "Description"); - assertEquals("{\n" + - " \"name\" : \"Name\",\n" + - " \"description\" : \"Description\"\n" + - "}", ThemeUtils.toJson(workspace)); - - workspace.getViews().getConfiguration().getStyles().addElementStyle(Tags.ELEMENT).background("#ff0000"); - workspace.getViews().getConfiguration().getStyles().addRelationshipStyle(Tags.RELATIONSHIP).color("#ff0000"); - assertEquals("{\n" + - " \"name\" : \"Name\",\n" + - " \"description\" : \"Description\",\n" + - " \"elements\" : [ {\n" + - " \"tag\" : \"Element\",\n" + - " \"background\" : \"#ff0000\"\n" + - " } ],\n" + - " \"relationships\" : [ {\n" + - " \"tag\" : \"Relationship\",\n" + - " \"color\" : \"#ff0000\"\n" + - " } ]\n" + - "}", ThemeUtils.toJson(workspace)); - } - -} \ No newline at end of file diff --git a/structurizr-client/test/unit/com/structurizr/view/ThemeUtilsTests.java b/structurizr-client/test/unit/com/structurizr/view/ThemeUtilsTests.java new file mode 100644 index 000000000..cb730df89 --- /dev/null +++ b/structurizr-client/test/unit/com/structurizr/view/ThemeUtilsTests.java @@ -0,0 +1,133 @@ +package com.structurizr.view; + +import com.structurizr.Workspace; +import com.structurizr.model.Relationship; +import com.structurizr.model.SoftwareSystem; +import com.structurizr.model.Tags; +import org.junit.Assert; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Collection; + +import static org.junit.Assert.*; + +public class ThemeUtilsTests { + + @Test + public void test_loadThemes_DoesNothingWhenNoThemesAreDefined() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + ThemeUtils.loadThemes(workspace); + + // there should still be zero styles in the workspace + assertEquals(0, workspace.getViews().getConfiguration().getStyles().getElements().size()); + } + + @Test + public void test_loadThemes_LoadsThemesWhenThemesAreDefined() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name"); + softwareSystem.addTags("Amazon Web Services - Alexa For Business"); + workspace.getViews().getConfiguration().setThemes("https://static.structurizr.com/themes/amazon-web-services-2020.04.30/theme.json"); + + ThemeUtils.loadThemes(workspace); + + // there should still be zero styles in the workspace + assertEquals(0, workspace.getViews().getConfiguration().getStyles().getElements().size()); + + // but we should be able to find a style included in the theme + ElementStyle style = workspace.getViews().getConfiguration().getStyles().findElementStyle(softwareSystem); + assertNotNull(style); + assertEquals("#d6242d", style.getStroke()); + assertEquals("#d6242d", style.getColor()); + assertNotNull(style.getIcon()); + } + + @Test + public void test_toJson() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + assertEquals("{\n" + + " \"name\" : \"Name\",\n" + + " \"description\" : \"Description\"\n" + + "}", ThemeUtils.toJson(workspace)); + + workspace.getViews().getConfiguration().getStyles().addElementStyle(Tags.ELEMENT).background("#ff0000"); + workspace.getViews().getConfiguration().getStyles().addRelationshipStyle(Tags.RELATIONSHIP).color("#ff0000"); + assertEquals("{\n" + + " \"name\" : \"Name\",\n" + + " \"description\" : \"Description\",\n" + + " \"elements\" : [ {\n" + + " \"tag\" : \"Element\",\n" + + " \"background\" : \"#ff0000\"\n" + + " } ],\n" + + " \"relationships\" : [ {\n" + + " \"tag\" : \"Relationship\",\n" + + " \"color\" : \"#ff0000\"\n" + + " } ]\n" + + "}", ThemeUtils.toJson(workspace)); + } + + @Test + public void test_findElementStyle_WithThemes() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name"); + workspace.getViews().getConfiguration().getStyles().addElementStyle("Element").shape(Shape.RoundedBox); + + // theme 1 + Collection elementStyles = new ArrayList<>(); + Collection relationshipStyles = new ArrayList<>(); + elementStyles.add(new ElementStyle("Element").shape(Shape.Box).background("#000000").color("#ffffff")); + workspace.getViews().getConfiguration().getStyles().addStylesFromTheme("url1", elementStyles, relationshipStyles); + + // theme 2 + elementStyles = new ArrayList<>(); + relationshipStyles = new ArrayList<>(); + elementStyles.add(new ElementStyle("Element").background("#ff0000")); + workspace.getViews().getConfiguration().getStyles().addStylesFromTheme("url2", elementStyles, relationshipStyles); + + ElementStyle style = workspace.getViews().getConfiguration().getStyles().findElementStyle(softwareSystem); + assertEquals(new Integer(450), style.getWidth()); + assertEquals(new Integer(300), style.getHeight()); + assertEquals("#ff0000", style.getBackground()); // from theme 2 + assertEquals("#ffffff", style.getColor()); // from theme 1 + assertEquals(new Integer(24), style.getFontSize()); + assertEquals(Shape.RoundedBox, style.getShape()); // from workspace + assertNull(style.getIcon()); + assertEquals(Border.Solid, style.getBorder()); + assertEquals("#dddddd", style.getStroke()); + assertEquals(new Integer(100), style.getOpacity()); + assertEquals(true, style.getMetadata()); + assertEquals(true, style.getDescription()); + } + + @Test + public void test_findRelationshipStyle_WithThemes() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name"); + Relationship relationship = softwareSystem.uses(softwareSystem, "Uses"); + workspace.getViews().getConfiguration().getStyles().addRelationshipStyle("Relationship").dashed(false); + + // theme 1 + Collection elementStyles = new ArrayList<>(); + Collection relationshipStyles = new ArrayList<>(); + relationshipStyles.add(new RelationshipStyle("Relationship").color("#ff0000").thickness(4)); + workspace.getViews().getConfiguration().getStyles().addStylesFromTheme("url1", elementStyles, relationshipStyles); + + // theme 2 + elementStyles = new ArrayList<>(); + relationshipStyles = new ArrayList<>(); + relationshipStyles.add(new RelationshipStyle("Relationship").color("#0000ff")); + workspace.getViews().getConfiguration().getStyles().addStylesFromTheme("url2", elementStyles, relationshipStyles); + + RelationshipStyle style = workspace.getViews().getConfiguration().getStyles().findRelationshipStyle(relationship); + assertEquals(new Integer(4), style.getThickness()); // from theme 1 + assertEquals("#0000ff", style.getColor()); // from theme 2 + Assert.assertFalse(style.getDashed()); // from workspace + assertEquals(Routing.Direct, style.getRouting()); + assertEquals(new Integer(24), style.getFontSize()); + assertEquals(new Integer(200), style.getWidth()); + assertEquals(new Integer(50), style.getPosition()); + assertEquals(new Integer(100), style.getOpacity()); + } + +} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/view/Configuration.java b/structurizr-core/src/com/structurizr/view/Configuration.java index 4b7e83cf2..8445b1654 100644 --- a/structurizr-core/src/com/structurizr/view/Configuration.java +++ b/structurizr-core/src/com/structurizr/view/Configuration.java @@ -49,7 +49,7 @@ public String getTheme() { * @param url the URL of theme */ @JsonSetter - public void setTheme(String url) { + void setTheme(String url) { setThemes(url); } diff --git a/structurizr-core/src/com/structurizr/view/ElementStyle.java b/structurizr-core/src/com/structurizr/view/ElementStyle.java index 071a130b0..2e420f542 100644 --- a/structurizr-core/src/com/structurizr/view/ElementStyle.java +++ b/structurizr-core/src/com/structurizr/view/ElementStyle.java @@ -1,6 +1,7 @@ package com.structurizr.view; import com.fasterxml.jackson.annotation.JsonInclude; +import com.structurizr.util.StringUtils; import com.structurizr.util.Url; /** @@ -335,4 +336,54 @@ public ElementStyle description(boolean description) { return this; } + void copyFrom(ElementStyle elementStyle) { + if (elementStyle.getWidth() != null) { + this.setWidth(elementStyle.getWidth()); + } + + if (elementStyle.getHeight() != null) { + this.setHeight(elementStyle.getHeight()); + } + + if (!StringUtils.isNullOrEmpty(elementStyle.getBackground())) { + this.setBackground(elementStyle.getBackground()); + } + + if (!StringUtils.isNullOrEmpty(elementStyle.getStroke())) { + this.setStroke(elementStyle.getStroke()); + } + + if (!StringUtils.isNullOrEmpty(elementStyle.getColor())) { + this.setColor(elementStyle.getColor()); + } + + if (elementStyle.getFontSize() != null) { + this.setFontSize(elementStyle.getFontSize()); + } + + if (elementStyle.getShape() != null) { + this.setShape(elementStyle.getShape()); + } + + if (!StringUtils.isNullOrEmpty(elementStyle.getIcon())) { + this.setIcon(elementStyle.getIcon()); + } + + if (elementStyle.getBorder() != null) { + this.setBorder(elementStyle.getBorder()); + } + + if (elementStyle.getOpacity() != null) { + this.setOpacity(elementStyle.getOpacity()); + } + + if (elementStyle.getMetadata() != null) { + this.setMetadata(elementStyle.getMetadata()); + } + + if (elementStyle.getDescription() != null) { + this.setDescription(elementStyle.getDescription()); + } + } + } \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/view/RelationshipStyle.java b/structurizr-core/src/com/structurizr/view/RelationshipStyle.java index 46de05239..dde9186e3 100644 --- a/structurizr-core/src/com/structurizr/view/RelationshipStyle.java +++ b/structurizr-core/src/com/structurizr/view/RelationshipStyle.java @@ -1,6 +1,7 @@ package com.structurizr.view; import com.fasterxml.jackson.annotation.JsonInclude; +import com.structurizr.util.StringUtils; public final class RelationshipStyle { @@ -197,4 +198,38 @@ public RelationshipStyle opacity(int opacity) { return this; } + void copyFrom(RelationshipStyle relationshipStyle) { + if (relationshipStyle.getThickness() != null) { + this.setThickness(relationshipStyle.getThickness()); + } + + if (!StringUtils.isNullOrEmpty(relationshipStyle.getColor())) { + this.setColor(relationshipStyle.getColor()); + } + + if (relationshipStyle.getDashed() != null) { + this.setDashed(relationshipStyle.getDashed()); + } + + if (relationshipStyle.getRouting() != null) { + this.setRouting(relationshipStyle.getRouting()); + } + + if (relationshipStyle.getFontSize() != null) { + this.setFontSize(relationshipStyle.getFontSize()); + } + + if (relationshipStyle.getWidth() != null) { + this.setWidth(relationshipStyle.getWidth()); + } + + if (relationshipStyle.getPosition() != null) { + this.setPosition(relationshipStyle.getPosition()); + } + + if (relationshipStyle.getOpacity() != null) { + this.setOpacity(relationshipStyle.getOpacity()); + } + } + } diff --git a/structurizr-core/src/com/structurizr/view/Styles.java b/structurizr-core/src/com/structurizr/view/Styles.java index f2690ea53..411a27f07 100644 --- a/structurizr-core/src/com/structurizr/view/Styles.java +++ b/structurizr-core/src/com/structurizr/view/Styles.java @@ -1,12 +1,12 @@ package com.structurizr.view; +import com.structurizr.Workspace; import com.structurizr.model.Element; import com.structurizr.model.Relationship; import com.structurizr.model.Tags; import com.structurizr.util.StringUtils; -import java.util.Collection; -import java.util.LinkedList; +import java.util.*; public final class Styles { @@ -19,32 +19,29 @@ public final class Styles { private Collection elements = new LinkedList<>(); private Collection relationships = new LinkedList<>(); + private Map themes = new LinkedHashMap<>(); + public Collection getElements() { return elements; } public void add(ElementStyle elementStyle) { if (elementStyle != null) { - if (findElementStyle(elementStyle.getTag()) == null) { - this.elements.add(elementStyle); - } else { + if (StringUtils.isNullOrEmpty(elementStyle.getTag())) { + throw new IllegalArgumentException("A tag must be specified."); + } + + if (elements.stream().anyMatch(es -> es.getTag().equals(elementStyle.getTag()))) { throw new IllegalArgumentException("An element style for the tag \"" + elementStyle.getTag() + "\" already exists."); } + + this.elements.add(elementStyle); } } public ElementStyle addElementStyle(String tag) { - ElementStyle elementStyle = null; - - if (tag != null) { - if (elements.stream().anyMatch(es -> es.getTag().equals(tag))) { - throw new IllegalArgumentException("An element style for the tag \"" + tag + "\" already exists."); - } - - elementStyle = new ElementStyle(); - elementStyle.setTag(tag); - add(elementStyle); - } + ElementStyle elementStyle = new ElementStyle(tag); + add(elementStyle); return elementStyle; } @@ -78,97 +75,79 @@ public Collection getRelationships() { public void add(RelationshipStyle relationshipStyle) { if (relationshipStyle != null) { - if (findRelationshipStyle(relationshipStyle.getTag()) == null) { - this.relationships.add(relationshipStyle); - } else { + if (StringUtils.isNullOrEmpty(relationshipStyle.getTag())) { + throw new IllegalArgumentException("A tag must be specified."); + } + + if (relationships.stream().anyMatch(es -> es.getTag().equals(relationshipStyle.getTag()))) { throw new IllegalArgumentException("A relationship style for the tag \"" + relationshipStyle.getTag() + "\" already exists."); } + + this.relationships.add(relationshipStyle); } } public RelationshipStyle addRelationshipStyle(String tag) { - RelationshipStyle relationshipStyle = null; - - if (tag != null) { - if (relationships.stream().anyMatch(rs -> rs.getTag().equals(tag))) { - throw new IllegalArgumentException("A relationship style for the tag \"" + tag + "\" already exists."); - } - - relationshipStyle = new RelationshipStyle(); - relationshipStyle.setTag(tag); - add(relationshipStyle); - } + RelationshipStyle relationshipStyle = new RelationshipStyle(tag); + add(relationshipStyle); return relationshipStyle; } private ElementStyle findElementStyle(String tag) { - if (tag != null) { - for (ElementStyle elementStyle : elements) { - if (elementStyle != null && elementStyle.getTag().equals(tag)) { - return elementStyle; - } + if (tag == null) { + return null; + } + + tag = tag.trim(); + ElementStyle style = new ElementStyle(tag); + + Collection elementStyles = new ArrayList<>(); + for (Theme theme : themes.values()) { + elementStyles.addAll(theme.getElements()); + } + elementStyles.addAll(elements); + + for (ElementStyle elementStyle : elementStyles) { + if (elementStyle != null && elementStyle.getTag().equals(tag)) { + style.copyFrom(elementStyle); } } - return null; + return style; } private RelationshipStyle findRelationshipStyle(String tag) { - if (tag != null) { - tag = tag.trim(); - for (RelationshipStyle relationshipStyle : relationships) { - if (relationshipStyle != null && relationshipStyle.getTag().equals(tag)) { - return relationshipStyle; - } + if (tag == null) { + return null; + } + + tag = tag.trim(); + RelationshipStyle style = new RelationshipStyle(tag); + + Collection relationshipStyles= new ArrayList<>(); + for (Theme theme : themes.values()) { + relationshipStyles.addAll(theme.getRelationships()); + } + relationshipStyles.addAll(relationships); + + for (RelationshipStyle relationshipStyle : relationshipStyles) { + if (relationshipStyle != null && relationshipStyle.getTag().equals(tag)) { + style.copyFrom(relationshipStyle); } } - return null; + return style; } public ElementStyle findElementStyle(Element element) { - ElementStyle style = new ElementStyle("").background("#dddddd").color("#000000").shape(Shape.Box); + ElementStyle style = new ElementStyle("").background("#dddddd").stroke("#dddddd").color("#000000").shape(Shape.Box).fontSize(24).border(Border.Solid).opacity(100).metadata(true).description(true); if (element != null) { for (String tag : element.getTagsAsSet()) { ElementStyle elementStyle = findElementStyle(tag); if (elementStyle != null) { - if (elementStyle.getWidth() != null) { - style.setWidth(elementStyle.getWidth()); - } - - if (elementStyle.getHeight() != null) { - style.setHeight(elementStyle.getHeight()); - } - - if (!StringUtils.isNullOrEmpty(elementStyle.getBackground())) { - style.setBackground(elementStyle.getBackground()); - } - - if (!StringUtils.isNullOrEmpty(elementStyle.getColor())) { - style.setColor(elementStyle.getColor()); - } - - if (!StringUtils.isNullOrEmpty(elementStyle.getStroke())) { - style.setStroke(elementStyle.getStroke()); - } - - if (elementStyle.getFontSize() != null) { - style.setFontSize(elementStyle.getFontSize()); - } - - if (elementStyle.getShape() != null) { - style.setShape(elementStyle.getShape()); - } - - if (elementStyle.getBorder() != null) { - style.setBorder(elementStyle.getBorder()); - } - - if (elementStyle.getOpacity() != null) { - style.setOpacity(elementStyle.getOpacity()); - } + style.copyFrom(elementStyle); } } } @@ -193,7 +172,7 @@ public ElementStyle findElementStyle(Element element) { } public RelationshipStyle findRelationshipStyle(Relationship relationship) { - RelationshipStyle style = new RelationshipStyle("").color("#707070"); + RelationshipStyle style = new RelationshipStyle("").thickness(2).color("#707070").dashed(true).routing(Routing.Direct).fontSize(24).width(200).position(50).opacity(100); String tags; if (relationship != null) { @@ -212,37 +191,7 @@ public RelationshipStyle findRelationshipStyle(Relationship relationship) { for (String tag : tags.split(",")) { RelationshipStyle relationshipStyle = findRelationshipStyle(tag); if (relationshipStyle != null) { - if (relationshipStyle.getThickness() != null) { - style.setThickness(relationshipStyle.getThickness()); - } - - if (!StringUtils.isNullOrEmpty(relationshipStyle.getColor())) { - style.setColor(relationshipStyle.getColor()); - } - - if (relationshipStyle.getDashed() != null) { - style.setDashed(relationshipStyle.getDashed()); - } - - if (relationshipStyle.getRouting() != null) { - style.setRouting(relationshipStyle.getRouting()); - } - - if (relationshipStyle.getFontSize() != null) { - style.setFontSize(relationshipStyle.getFontSize()); - } - - if (relationshipStyle.getWidth() != null) { - style.setWidth(relationshipStyle.getWidth()); - } - - if (relationshipStyle.getPosition() != null) { - style.setPosition(relationshipStyle.getPosition()); - } - - if (relationshipStyle.getOpacity() != null) { - style.setOpacity(relationshipStyle.getOpacity()); - } + style.copyFrom(relationshipStyle); } } } @@ -250,4 +199,8 @@ public RelationshipStyle findRelationshipStyle(Relationship relationship) { return style; } + void addStylesFromTheme(String url, Collection elements, Collection relationships) { + themes.put(url, new Theme(elements, relationships)); + } + } diff --git a/structurizr-core/src/com/structurizr/view/Theme.java b/structurizr-core/src/com/structurizr/view/Theme.java new file mode 100644 index 000000000..07484b986 --- /dev/null +++ b/structurizr-core/src/com/structurizr/view/Theme.java @@ -0,0 +1,64 @@ +package com.structurizr.view; + +import com.fasterxml.jackson.annotation.JsonGetter; + +import java.util.Collection; +import java.util.LinkedList; + +final class Theme { + + private String name; + private String description; + private Collection elements = new LinkedList<>(); + private Collection relationships = new LinkedList<>(); + + Theme() { + } + + Theme(Collection elements, Collection relationships) { + this.elements = elements; + this.relationships = relationships; + } + + Theme(String name, String description, Collection elements, Collection relationships) { + this.name = name; + this.description = description; + this.elements = elements; + this.relationships = relationships; + } + + public String getName() { + return name; + } + + void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + void setDescription(String description) { + this.description = description; + } + + @JsonGetter + Collection getElements() { + return elements; + } + + void setElements(Collection elements) { + this.elements = elements; + } + + @JsonGetter + Collection getRelationships() { + return relationships; + } + + void setRelationships(Collection relationships) { + this.relationships = relationships; + } + +} \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/view/StyleTests.java b/structurizr-core/test/unit/com/structurizr/view/StylesTests.java similarity index 68% rename from structurizr-core/test/unit/com/structurizr/view/StyleTests.java rename to structurizr-core/test/unit/com/structurizr/view/StylesTests.java index a67ad52bd..8a8fb8847 100644 --- a/structurizr-core/test/unit/com/structurizr/view/StyleTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/StylesTests.java @@ -4,32 +4,45 @@ import com.structurizr.model.*; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; -public class StyleTests extends AbstractWorkspaceTestBase { +public class StylesTests extends AbstractWorkspaceTestBase { private Styles styles = new Styles(); @Test public void test_findElementStyle_ReturnsTheDefaultStyle_WhenPassedNull() { ElementStyle style = styles.findElementStyle(null); + assertEquals(new Integer(450), style.getWidth()); + assertEquals(new Integer(300), style.getHeight()); assertEquals("#dddddd", style.getBackground()); assertEquals("#000000", style.getColor()); + assertEquals(new Integer(24), style.getFontSize()); assertEquals(Shape.Box, style.getShape()); - assertEquals(new Integer(450), style.getWidth()); - assertEquals(new Integer(300), style.getHeight()); + assertNull(style.getIcon()); + assertEquals(Border.Solid, style.getBorder()); + assertEquals("#dddddd", style.getStroke()); + assertEquals(new Integer(100), style.getOpacity()); + assertEquals(true, style.getMetadata()); + assertEquals(true, style.getDescription()); } @Test public void test_findElementStyle_ReturnsTheDefaultStyle_WhenNoStylesAreDefined() { SoftwareSystem element = model.addSoftwareSystem("Name", "Description"); ElementStyle style = styles.findElementStyle(element); + assertEquals(new Integer(450), style.getWidth()); + assertEquals(new Integer(300), style.getHeight()); assertEquals("#dddddd", style.getBackground()); assertEquals("#000000", style.getColor()); + assertEquals(new Integer(24), style.getFontSize()); assertEquals(Shape.Box, style.getShape()); - assertEquals(new Integer(450), style.getWidth()); - assertEquals(new Integer(300), style.getHeight()); + assertNull(style.getIcon()); + assertEquals(Border.Solid, style.getBorder()); + assertEquals("#dddddd", style.getStroke()); + assertEquals(new Integer(100), style.getOpacity()); + assertEquals(true, style.getMetadata()); + assertEquals(true, style.getDescription()); } @Test @@ -41,12 +54,18 @@ public void test_findElementStyle_ReturnsTheCorrectStyle_WhenStylesAreDefined() styles.addElementStyle("Some Tag").color("#0000ff").stroke("#00ff00").shape(Shape.RoundedBox).width(123).height(456); ElementStyle style = styles.findElementStyle(element); + assertEquals(new Integer(123), style.getWidth()); + assertEquals(new Integer(456), style.getHeight()); assertEquals("#ff0000", style.getBackground()); assertEquals("#0000ff", style.getColor()); - assertEquals("#00ff00", style.getStroke()); + assertEquals(new Integer(24), style.getFontSize()); assertEquals(Shape.RoundedBox, style.getShape()); - assertEquals(new Integer(123), style.getWidth()); - assertEquals(new Integer(456), style.getHeight()); + assertNull(style.getIcon()); + assertEquals(Border.Solid, style.getBorder()); + assertEquals("#00ff00", style.getStroke()); + assertEquals(new Integer(100), style.getOpacity()); + assertEquals(true, style.getMetadata()); + assertEquals(true, style.getDescription()); } @Test @@ -80,7 +99,14 @@ public void test_findElementStyle_ReturnsTheDefaultElementSize_WhenTheShapeIsAPe @Test public void test_findRelationshipStyle_ReturnsTheDefaultStyle_WhenPassedNull() { RelationshipStyle style = styles.findRelationshipStyle(null); + assertEquals(new Integer(2), style.getThickness()); assertEquals("#707070", style.getColor()); + assertTrue(style.getDashed()); + assertEquals(Routing.Direct, style.getRouting()); + assertEquals(new Integer(24), style.getFontSize()); + assertEquals(new Integer(200), style.getWidth()); + assertEquals(new Integer(50), style.getPosition()); + assertEquals(new Integer(100), style.getOpacity()); } @Test @@ -88,7 +114,14 @@ public void test_findRelationshipStyle_ReturnsTheDefaultStyle_WhenNoStylesAreDef SoftwareSystem element = model.addSoftwareSystem("Name", "Description"); Relationship relationship = element.uses(element, "Uses"); RelationshipStyle style = styles.findRelationshipStyle(relationship); + assertEquals(new Integer(2), style.getThickness()); assertEquals("#707070", style.getColor()); + assertTrue(style.getDashed()); + assertEquals(Routing.Direct, style.getRouting()); + assertEquals(new Integer(24), style.getFontSize()); + assertEquals(new Integer(200), style.getWidth()); + assertEquals(new Integer(50), style.getPosition()); + assertEquals(new Integer(100), style.getOpacity()); } @Test @@ -101,7 +134,14 @@ public void test_findRelationshipStyle_ReturnsTheCorrectStyle_WhenStylesAreDefin styles.addRelationshipStyle("Some Tag").color("#0000ff"); RelationshipStyle style = styles.findRelationshipStyle(relationship); + assertEquals(new Integer(2), style.getThickness()); assertEquals("#0000ff", style.getColor()); + assertTrue(style.getDashed()); + assertEquals(Routing.Direct, style.getRouting()); + assertEquals(new Integer(24), style.getFontSize()); + assertEquals(new Integer(200), style.getWidth()); + assertEquals(new Integer(50), style.getPosition()); + assertEquals(new Integer(100), style.getOpacity()); } @Test @@ -126,6 +166,30 @@ public void test_findRelationshipStyle_ReturnsTheCorrectStyle_WhenThereIsALinked assertEquals("#0000ff", style.getColor()); } + @Test + public void test_addElementStyle_ThrowsAnException_WhenATagIsNotSpecified() { + try { + styles.addElementStyle(""); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("A tag must be specified.", iae.getMessage()); + } + + try { + styles.addElementStyle(" "); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("A tag must be specified.", iae.getMessage()); + } + + try { + styles.addElementStyle(null); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("A tag must be specified.", iae.getMessage()); + } + } + @Test public void test_addElementStyleByTag_ThrowsAnException_WhenAStyleWithTheSameTagExistsAlready() { try { @@ -150,6 +214,30 @@ public void test_addElementStyle_ThrowsAnException_WhenAStyleWithTheSameTagExist } } + @Test + public void test_addRelationshipStyle_ThrowsAnException_WhenATagIsNotSpecified() { + try { + styles.addRelationshipStyle(""); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("A tag must be specified.", iae.getMessage()); + } + + try { + styles.addRelationshipStyle(" "); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("A tag must be specified.", iae.getMessage()); + } + + try { + styles.addRelationshipStyle(null); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("A tag must be specified.", iae.getMessage()); + } + } + @Test public void test_addRelationshipStyleByTag_ThrowsAnException_WhenAStyleWithTheSameTagExistsAlready() { try { diff --git a/structurizr-examples/src/com/structurizr/example/Theme.java b/structurizr-examples/src/com/structurizr/example/Theme.java index cbdb502b0..882b02593 100644 --- a/structurizr-examples/src/com/structurizr/example/Theme.java +++ b/structurizr-examples/src/com/structurizr/example/Theme.java @@ -33,7 +33,7 @@ public static void main(String[] args) throws Exception { contextView.addAllPeople(); // add a theme - views.getConfiguration().setTheme("https://raw.githubusercontent.com/structurizr/java/master/structurizr-examples/src/com/structurizr/example/theme/theme.json"); + views.getConfiguration().setThemes("https://raw.githubusercontent.com/structurizr/java/master/structurizr-examples/src/com/structurizr/example/theme/theme.json"); StructurizrClient structurizrClient = new StructurizrClient(API_KEY, API_SECRET); structurizrClient.putWorkspace(WORKSPACE_ID, workspace); From bac064eee38f6ad41d1b619b970c7a786528a4dc Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 18 Mar 2021 16:54:16 +0000 Subject: [PATCH 225/717] Adds support for "deployment groups", providing a way to scope how software system/container instance relationships are replicated when added to deployment nodes. --- build.gradle | 2 +- docs/changelog.md | 3 +- .../structurizr/model/ContainerInstance.java | 4 +- .../structurizr/model/DeploymentElement.java | 1 + .../com/structurizr/model/DeploymentNode.java | 20 +++--- .../src/com/structurizr/model/Model.java | 26 ++++--- .../model/SoftwareSystemInstance.java | 4 +- .../model/StaticStructureElementInstance.java | 19 ++++- .../com/structurizr/model/ModelTests.java | 70 +++++++++++++++---- 9 files changed, 102 insertions(+), 47 deletions(-) diff --git a/build.gradle b/build.gradle index 9d0454998..000b6d007 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.8.1' + version = '1.9.0' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 29bfa374e..9fe833c28 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,10 +1,11 @@ # Changelog -## 1.8.1 (unreleased) +## 1.9.0 (unreleased) - Adds support for adding individual infrastructure nodes, software system instances, and container instances to a deployment view. - Adds support for removing software system instances from deployment views. - Improved support for themes (e.g. when exporting to PlantUML), which now works the same as described at [Structurizr - Themes](https://structurizr.com/help/themes). +- Adds support for "deployment groups", providing a way to scope how software system/container instance relationships are replicated when added to deployment nodes. __breaking change__ ## 1.8.0 (20th February 2021) diff --git a/structurizr-core/src/com/structurizr/model/ContainerInstance.java b/structurizr-core/src/com/structurizr/model/ContainerInstance.java index 883d9507f..140157aa2 100644 --- a/structurizr-core/src/com/structurizr/model/ContainerInstance.java +++ b/structurizr-core/src/com/structurizr/model/ContainerInstance.java @@ -13,8 +13,8 @@ public final class ContainerInstance extends StaticStructureElementInstance { ContainerInstance() { } - ContainerInstance(Container container, int instanceId, String environment) { - super(instanceId, environment); + ContainerInstance(Container container, int instanceId, String environment, String deploymentGroup) { + super(instanceId, environment, deploymentGroup); setContainer(container); addTags(Tags.CONTAINER_INSTANCE); diff --git a/structurizr-core/src/com/structurizr/model/DeploymentElement.java b/structurizr-core/src/com/structurizr/model/DeploymentElement.java index dd8240938..120a63af7 100644 --- a/structurizr-core/src/com/structurizr/model/DeploymentElement.java +++ b/structurizr-core/src/com/structurizr/model/DeploymentElement.java @@ -8,6 +8,7 @@ public abstract class DeploymentElement extends Element { public static final String DEFAULT_DEPLOYMENT_ENVIRONMENT = "Default"; + public static final String DEFAULT_DEPLOYMENT_GROUP = "Default"; private DeploymentNode parent; private String environment = DEFAULT_DEPLOYMENT_ENVIRONMENT; diff --git a/structurizr-core/src/com/structurizr/model/DeploymentNode.java b/structurizr-core/src/com/structurizr/model/DeploymentNode.java index 330ce408f..e4c5b6a1b 100644 --- a/structurizr-core/src/com/structurizr/model/DeploymentNode.java +++ b/structurizr-core/src/com/structurizr/model/DeploymentNode.java @@ -36,18 +36,18 @@ public final class DeploymentNode extends DeploymentElement { * @return a SoftwareSystemInstance object */ public SoftwareSystemInstance add(SoftwareSystem softwareSystem) { - return add(softwareSystem, true); + return add(softwareSystem, DEFAULT_DEPLOYMENT_GROUP); } /** - * Adds a software system instance to this deployment node, optionally replicating relationships. + * Adds a software system instance to this deployment node, replicating relationships. * - * @param softwareSystem the SoftwareSystem to add an instance of - * @param replicateRelationships true if relationships should be replicated between the element instances in the same deployment environment, false otherwise + * @param softwareSystem the SoftwareSystem to add an instance of + * @param deploymentGroup the deployment group * @return a SoftwareSystemInstance object */ - public SoftwareSystemInstance add(SoftwareSystem softwareSystem, boolean replicateRelationships) { - SoftwareSystemInstance softwareSystemInstance = getModel().addSoftwareSystemInstance(this, softwareSystem, replicateRelationships); + public SoftwareSystemInstance add(SoftwareSystem softwareSystem, String deploymentGroup) { + SoftwareSystemInstance softwareSystemInstance = getModel().addSoftwareSystemInstance(this, softwareSystem, deploymentGroup); this.softwareSystemInstances.add(softwareSystemInstance); return softwareSystemInstance; @@ -60,18 +60,18 @@ public SoftwareSystemInstance add(SoftwareSystem softwareSystem, boolean replica * @return a ContainerInstance object */ public ContainerInstance add(Container container) { - return add(container, true); + return add(container, DEFAULT_DEPLOYMENT_GROUP); } /** * Adds a container instance to this deployment node, optionally replicating relationships. * * @param container the Container to add an instance of - * @param replicateRelationships true if relationships should be replicated between the element instances in the same deployment environment, false otherwise + * @param deploymentGroup the deployment group * @return a ContainerInstance object */ - public ContainerInstance add(Container container, boolean replicateRelationships) { - ContainerInstance containerInstance = getModel().addContainerInstance(this, container, replicateRelationships); + public ContainerInstance add(Container container, String deploymentGroup) { + ContainerInstance containerInstance = getModel().addContainerInstance(this, container, deploymentGroup); this.containerInstances.add(containerInstance); return containerInstance; diff --git a/structurizr-core/src/com/structurizr/model/Model.java b/structurizr-core/src/com/structurizr/model/Model.java index df84302c4..4d70f1fca 100644 --- a/structurizr-core/src/com/structurizr/model/Model.java +++ b/structurizr-core/src/com/structurizr/model/Model.java @@ -982,56 +982,54 @@ public DeploymentNode getDeploymentNodeWithName(String name, String environment) return null; } - SoftwareSystemInstance addSoftwareSystemInstance(DeploymentNode deploymentNode, SoftwareSystem softwareSystem, boolean replicateRelationships) { + SoftwareSystemInstance addSoftwareSystemInstance(DeploymentNode deploymentNode, SoftwareSystem softwareSystem, String deploymentGroup) { if (softwareSystem == null) { throw new IllegalArgumentException("A software system must be specified."); } long instanceNumber = deploymentNode.getSoftwareSystemInstances().stream().filter(ssi -> ssi.getSoftwareSystem().equals(softwareSystem)).count(); instanceNumber++; - SoftwareSystemInstance softwareSystemInstance = new SoftwareSystemInstance(softwareSystem, (int)instanceNumber, deploymentNode.getEnvironment()); + SoftwareSystemInstance softwareSystemInstance = new SoftwareSystemInstance(softwareSystem, (int)instanceNumber, deploymentNode.getEnvironment(), deploymentGroup); softwareSystemInstance.setParent(deploymentNode); softwareSystemInstance.setId(idGenerator.generateId(softwareSystemInstance)); - if (replicateRelationships) { - replicateElementRelationships(deploymentNode.getEnvironment(), softwareSystemInstance); - } + replicateElementRelationships(softwareSystemInstance); addElementToInternalStructures(softwareSystemInstance); return softwareSystemInstance; } - ContainerInstance addContainerInstance(DeploymentNode deploymentNode, Container container, boolean replicateRelationships) { + ContainerInstance addContainerInstance(DeploymentNode deploymentNode, Container container, String deploymentGroup) { if (container == null) { throw new IllegalArgumentException("A container must be specified."); } long instanceNumber = deploymentNode.getContainerInstances().stream().filter(ci -> ci.getContainer().equals(container)).count(); instanceNumber++; - ContainerInstance containerInstance = new ContainerInstance(container, (int)instanceNumber, deploymentNode.getEnvironment()); + ContainerInstance containerInstance = new ContainerInstance(container, (int)instanceNumber, deploymentNode.getEnvironment(), deploymentGroup); containerInstance.setParent(deploymentNode); containerInstance.setId(idGenerator.generateId(containerInstance)); - if (replicateRelationships) { - replicateElementRelationships(deploymentNode.getEnvironment(), containerInstance); - } + replicateElementRelationships(containerInstance); addElementToInternalStructures(containerInstance); return containerInstance; } - private void replicateElementRelationships(String deploymentEnvironment, StaticStructureElementInstance elementInstance) { + private void replicateElementRelationships(StaticStructureElementInstance elementInstance) { StaticStructureElement element = elementInstance.getElement(); - // find all StaticStructureElementInstance objects in the same deployment environment + // find all StaticStructureElementInstance objects in the same deployment environment and deployment group Set elementInstances = getElements().stream() - .filter(e -> e instanceof StaticStructureElementInstance && ((StaticStructureElementInstance)e).getEnvironment().equals(deploymentEnvironment)) + .filter(e -> e instanceof StaticStructureElementInstance) .map(e -> (StaticStructureElementInstance)e) + .filter(ssei -> ssei.getEnvironment().equals(elementInstance.getEnvironment())) + .filter(ssei -> ssei.getDeploymentGroup().equals(elementInstance.getDeploymentGroup())) .collect(Collectors.toSet()); - // and replicate the relationships within the same deployment environment + // and replicate the relationships to/from the element instance for (StaticStructureElementInstance ssei : elementInstances) { StaticStructureElement sse = ssei.getElement(); diff --git a/structurizr-core/src/com/structurizr/model/SoftwareSystemInstance.java b/structurizr-core/src/com/structurizr/model/SoftwareSystemInstance.java index 626b794bb..1ec030668 100644 --- a/structurizr-core/src/com/structurizr/model/SoftwareSystemInstance.java +++ b/structurizr-core/src/com/structurizr/model/SoftwareSystemInstance.java @@ -13,8 +13,8 @@ public final class SoftwareSystemInstance extends StaticStructureElementInstance SoftwareSystemInstance() { } - SoftwareSystemInstance(SoftwareSystem softwareSystem, int instanceId, String environment) { - super(instanceId, environment); + SoftwareSystemInstance(SoftwareSystem softwareSystem, int instanceId, String environment, String deploymentGroup) { + super(instanceId, environment, deploymentGroup); setSoftwareSystem(softwareSystem); addTags(Tags.SOFTWARE_SYSTEM_INSTANCE); diff --git a/structurizr-core/src/com/structurizr/model/StaticStructureElementInstance.java b/structurizr-core/src/com/structurizr/model/StaticStructureElementInstance.java index 66ac49ff7..98dfa51a3 100644 --- a/structurizr-core/src/com/structurizr/model/StaticStructureElementInstance.java +++ b/structurizr-core/src/com/structurizr/model/StaticStructureElementInstance.java @@ -16,22 +16,37 @@ public abstract class StaticStructureElementInstance extends DeploymentElement { private static final int DEFAULT_HEALTH_CHECK_INTERVAL_IN_SECONDS = 60; private static final long DEFAULT_HEALTH_CHECK_TIMEOUT_IN_MILLISECONDS = 0; + private String deploymentGroup = DEFAULT_DEPLOYMENT_GROUP; private int instanceId; private Set healthChecks = new HashSet<>(); StaticStructureElementInstance() { } - StaticStructureElementInstance(int instanceId, String environment) { + StaticStructureElementInstance(int instanceId, String environment, String deploymentGroup) { setInstanceId(instanceId); setEnvironment(environment); + setDeploymentGroup(deploymentGroup); } @JsonIgnore public abstract StaticStructureElement getElement(); /** - * Gets the instance ID of this container. + * Gets the deployment group of this element instance. + * + * @return a deployment group name + */ + public String getDeploymentGroup() { + return deploymentGroup; + } + + void setDeploymentGroup(String deploymentGroup) { + this.deploymentGroup = deploymentGroup; + } + + /** + * Gets the instance ID of this element instance. * * @return the instance ID, an integer greater than zero */ diff --git a/structurizr-core/test/unit/com/structurizr/model/ModelTests.java b/structurizr-core/test/unit/com/structurizr/model/ModelTests.java index 0a0b07aab..f703c7fea 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ModelTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ModelTests.java @@ -686,26 +686,66 @@ public void test_addElementInstance_AddsElementInstancesAndReplicatesRelationshi } @Test - public void test_addContainerInstance_AddsAContainerInstanceAndDoesNotReplicateRelationships() { - SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1", "Description"); - Container container1 = softwareSystem1.addContainer("Container 1", "Description", "Technology"); + public void test_addElementInstance_AddsElementInstancesAndReplicatesRelationshipsWithinTheDeploymentEnvironmentAndDefaultGroup() { + SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System"); + Container api = softwareSystem1.addContainer("API"); + Container database = softwareSystem1.addContainer("Database"); + api.uses(database, "Uses"); - SoftwareSystem softwareSystem2 = model.addSoftwareSystem("Software System 2", "Description"); - Container container2 = softwareSystem2.addContainer("Container 2", "Description", "Technology"); + DeploymentNode liveDeploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + ContainerInstance apiInstance1 = liveDeploymentNode.add(api); + ContainerInstance databaseInstance1 = liveDeploymentNode.add(database); - SoftwareSystem softwareSystem3 = model.addSoftwareSystem("Software System 3", "Description"); - Container container3 = softwareSystem3.addContainer("Container 3", "Description", "Technology"); + ContainerInstance apiInstance2 = liveDeploymentNode.add(api); + ContainerInstance databaseInstance2 = liveDeploymentNode.add(database); - container1.uses(container2, "Uses 1", "Technology 1", InteractionStyle.Synchronous); - container2.uses(container3, "Uses 2", "Technology 2", InteractionStyle.Asynchronous); + assertEquals(2, apiInstance1.getRelationships().size()); + assertEquals(2, apiInstance2.getRelationships().size()); - DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node", "Description", "Technology"); - ContainerInstance containerInstance1 = deploymentNode.add(container1, false); - ContainerInstance containerInstance2 = deploymentNode.add(container2, false); - ContainerInstance containerInstance3 = deploymentNode.add(container3, false); + // apiInstance1 -> databaseInstance1 + Relationship relationship = apiInstance1.getEfferentRelationshipWith(databaseInstance1); + assertEquals("Uses", relationship.getDescription()); + + // apiInstance1 -> databaseInstance2 + relationship = apiInstance1.getEfferentRelationshipWith(databaseInstance2); + assertEquals("Uses", relationship.getDescription()); + + // apiInstance2 -> databaseInstance1 + relationship = apiInstance2.getEfferentRelationshipWith(databaseInstance1); + assertEquals("Uses", relationship.getDescription()); + + // apiInstance2 -> databaseInstance2 + relationship = apiInstance2.getEfferentRelationshipWith(databaseInstance2); + assertEquals("Uses", relationship.getDescription()); + } + + @Test + public void test_addElementInstance_AddsElementInstancesAndReplicatesRelationshipsWithinTheDeploymentEnvironmentAndSpecifiedGroup() { + // in this test, container instances are added to two deployment groups: "Service 1" and "Service 2" + // relationships are not replicated between element instances in other groups + + SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System"); + Container api = softwareSystem1.addContainer("API"); + Container database = softwareSystem1.addContainer("Database"); + api.uses(database, "Uses"); - assertEquals(0, containerInstance1.getRelationships().size()); - assertEquals(0, containerInstance2.getRelationships().size()); + DeploymentNode liveDeploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + ContainerInstance apiInstance1 = liveDeploymentNode.add(api, "Service 1"); + ContainerInstance databaseInstance1 = liveDeploymentNode.add(database, "Service 1"); + + ContainerInstance apiInstance2 = liveDeploymentNode.add(api, "Service 2"); + ContainerInstance databaseInstance2 = liveDeploymentNode.add(database, "Service 2"); + + assertEquals(1, apiInstance1.getRelationships().size()); + assertEquals(1, apiInstance2.getRelationships().size()); + + // apiInstance1 -> databaseInstance1 + Relationship relationship = apiInstance1.getEfferentRelationshipWith(databaseInstance1); + assertEquals("Uses", relationship.getDescription()); + + // apiInstance2 -> databaseInstance2 + relationship = apiInstance2.getEfferentRelationshipWith(databaseInstance2); + assertEquals("Uses", relationship.getDescription()); } @Test From 04af66cb447a5405e50cfcca7056a638f7351df6 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 20 Mar 2021 14:26:53 +0000 Subject: [PATCH 226/717] Updated docs to reflect new release. --- docs/binaries.md | 4 ++-- docs/changelog.md | 2 +- docs/getting-started.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/binaries.md b/docs/binaries.md index 76f14dec6..c9ad0147b 100644 --- a/docs/binaries.md +++ b/docs/binaries.md @@ -3,5 +3,5 @@ The "Structurizr for Java" binaries are hosted on [Maven Central](https://repo1. Name | Description ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-core:1.8.0 | The core library that can used to create software architecture models. -com.structurizr:structurizr-client:1.8.0 | The API client for publishing models on the Structurizr cloud service and on-premises installation. \ No newline at end of file +com.structurizr:structurizr-core:1.9.0 | The core library that can used to create software architecture models. +com.structurizr:structurizr-client:1.9.0 | The API client for publishing models on the Structurizr cloud service and on-premises installation. \ No newline at end of file diff --git a/docs/changelog.md b/docs/changelog.md index 9fe833c28..e45b853e9 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 1.9.0 (unreleased) +## 1.9.0 (20th March 2021) - Adds support for adding individual infrastructure nodes, software system instances, and container instances to a deployment view. - Adds support for removing software system instances from deployment views. diff --git a/docs/getting-started.md b/docs/getting-started.md index d51dd247d..83ebcd1cd 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -12,7 +12,7 @@ The Structurizr for Java binaries are hosted on [Maven Central](https://repo1.ma Name | Description ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-client:1.8.0 | The Structurizr API client library. +com.structurizr:structurizr-client:1.9.0 | The Structurizr API client library. ## 2. Create a Java program From c79d3a8a8d05a11c188cc7c80caf3edd1e48907f Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 27 Mar 2021 15:25:44 +0000 Subject: [PATCH 227/717] Adds a findTerminology() method on the Terminology class. --- build.gradle | 2 +- docs/changelog.md | 4 ++ .../src/com/structurizr/view/Terminology.java | 32 +++++++++++ .../structurizr/view/TerminologyTests.java | 54 +++++++++++++++++++ 4 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 structurizr-core/test/unit/com/structurizr/view/TerminologyTests.java diff --git a/build.gradle b/build.gradle index 000b6d007..7a730925b 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.9.0' + version = '1.9.1' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index e45b853e9..30902c63c 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.9.1 (unreleased) + +- Adds a `findTerminology` method on the `Terminology` class. + ## 1.9.0 (20th March 2021) - Adds support for adding individual infrastructure nodes, software system instances, and container instances to a deployment view. diff --git a/structurizr-core/src/com/structurizr/view/Terminology.java b/structurizr-core/src/com/structurizr/view/Terminology.java index 6993b281a..6233ff6c6 100644 --- a/structurizr-core/src/com/structurizr/view/Terminology.java +++ b/structurizr-core/src/com/structurizr/view/Terminology.java @@ -1,6 +1,8 @@ package com.structurizr.view; import com.fasterxml.jackson.annotation.JsonInclude; +import com.structurizr.model.*; +import com.structurizr.util.StringUtils; /** * Provides a way for the terminology on diagrams, etc to be modified (e.g. language translations). @@ -106,4 +108,34 @@ public void setRelationship(String relationship) { this.relationship = relationship; } + /** + * Finds the terminology that can be used to describe/label the specified model item. + * + * @param modelItem an Element or Relationship + * @return the default or overridden terminology for the specified model item + */ + public String findTerminology(ModelItem modelItem) { + if (modelItem instanceof StaticStructureElementInstance) { + modelItem = ((StaticStructureElementInstance)modelItem).getElement(); + } + + if (modelItem instanceof Relationship) { + return !StringUtils.isNullOrEmpty(getRelationship()) ? getRelationship() : "Relationship"; + } else if (modelItem instanceof Person) { + return !StringUtils.isNullOrEmpty(getPerson()) ? getPerson() : "Person"; + } else if (modelItem instanceof SoftwareSystem) { + return !StringUtils.isNullOrEmpty(getSoftwareSystem()) ? getSoftwareSystem() : "Software System"; + } else if (modelItem instanceof Container) { + return !StringUtils.isNullOrEmpty(getContainer()) ? getContainer() : "Container"; + } else if (modelItem instanceof Component) { + return !StringUtils.isNullOrEmpty(getComponent()) ? getComponent() : "Component"; + } else if (modelItem instanceof DeploymentNode) { + return !StringUtils.isNullOrEmpty(getDeploymentNode()) ? getDeploymentNode() : "Deployment Node"; + } else if (modelItem instanceof InfrastructureNode) { + return !StringUtils.isNullOrEmpty(getInfrastructureNode()) ? getInfrastructureNode() : "Infrastructure Node"; + } + + throw new IllegalArgumentException("Unknown model item type."); + } + } \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/view/TerminologyTests.java b/structurizr-core/test/unit/com/structurizr/view/TerminologyTests.java new file mode 100644 index 000000000..7c6bb38ea --- /dev/null +++ b/structurizr-core/test/unit/com/structurizr/view/TerminologyTests.java @@ -0,0 +1,54 @@ +package com.structurizr.view; + +import com.structurizr.Workspace; +import com.structurizr.model.*; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class TerminologyTests { + + @Test + public void test_findTerminology() { + Workspace workspace = new Workspace("Name", "Description"); + Terminology terminology = workspace.getViews().getConfiguration().getTerminology(); + Person person = workspace.getModel().addPerson("Name"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name"); + Container container = softwareSystem.addContainer("Container"); + Component component = container.addComponent("Component"); + DeploymentNode deploymentNode = workspace.getModel().addDeploymentNode("Deployment Node"); + InfrastructureNode infrastructureNode = deploymentNode.addInfrastructureNode("Infrastructure Node"); + SoftwareSystemInstance softwareSystemInstance = deploymentNode.add(softwareSystem); + ContainerInstance containerInstance = deploymentNode.add(container); + Relationship relationship = person.uses(softwareSystem, "Uses"); + + assertEquals("Person", terminology.findTerminology(person)); + assertEquals("Software System", terminology.findTerminology(softwareSystem)); + assertEquals("Container", terminology.findTerminology(container)); + assertEquals("Component", terminology.findTerminology(component)); + assertEquals("Deployment Node", terminology.findTerminology(deploymentNode)); + assertEquals("Infrastructure Node", terminology.findTerminology(infrastructureNode)); + assertEquals("Software System", terminology.findTerminology(softwareSystemInstance)); + assertEquals("Container", terminology.findTerminology(containerInstance)); + assertEquals("Relationship", terminology.findTerminology(relationship)); + + terminology.setPerson("PERSON"); + terminology.setSoftwareSystem("SOFTWARE SYSTEM"); + terminology.setContainer("CONTAINER"); + terminology.setComponent("COMPONENT"); + terminology.setDeploymentNode("DEPLOYMENT NODE"); + terminology.setInfrastructureNode("INFRASTRUCTURE NODE"); + terminology.setRelationship("RELATIONSHIP"); + + assertEquals("PERSON", terminology.findTerminology(person)); + assertEquals("SOFTWARE SYSTEM", terminology.findTerminology(softwareSystem)); + assertEquals("CONTAINER", terminology.findTerminology(container)); + assertEquals("COMPONENT", terminology.findTerminology(component)); + assertEquals("DEPLOYMENT NODE", terminology.findTerminology(deploymentNode)); + assertEquals("INFRASTRUCTURE NODE", terminology.findTerminology(infrastructureNode)); + assertEquals("SOFTWARE SYSTEM", terminology.findTerminology(softwareSystemInstance)); + assertEquals("CONTAINER", terminology.findTerminology(containerInstance)); + assertEquals("RELATIONSHIP", terminology.findTerminology(relationship)); + } + +} \ No newline at end of file From 44d197dbc66a595fa1bdbe6ebafc6debf2a2fd3e Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 28 Mar 2021 14:08:49 +0100 Subject: [PATCH 228/717] Better mirror how the Structurizr web renderer deals with element styling. --- .../unit/com/structurizr/view/ThemeUtilsTests.java | 2 +- .../src/com/structurizr/view/Styles.java | 14 +++++++++++++- .../unit/com/structurizr/view/StylesTests.java | 4 ++-- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/structurizr-client/test/unit/com/structurizr/view/ThemeUtilsTests.java b/structurizr-client/test/unit/com/structurizr/view/ThemeUtilsTests.java index cb730df89..143c8ce1a 100644 --- a/structurizr-client/test/unit/com/structurizr/view/ThemeUtilsTests.java +++ b/structurizr-client/test/unit/com/structurizr/view/ThemeUtilsTests.java @@ -94,7 +94,7 @@ public void test_findElementStyle_WithThemes() { assertEquals(Shape.RoundedBox, style.getShape()); // from workspace assertNull(style.getIcon()); assertEquals(Border.Solid, style.getBorder()); - assertEquals("#dddddd", style.getStroke()); + assertEquals("#b20000", style.getStroke()); assertEquals(new Integer(100), style.getOpacity()); assertEquals(true, style.getMetadata()); assertEquals(true, style.getDescription()); diff --git a/structurizr-core/src/com/structurizr/view/Styles.java b/structurizr-core/src/com/structurizr/view/Styles.java index 411a27f07..944efc2fd 100644 --- a/structurizr-core/src/com/structurizr/view/Styles.java +++ b/structurizr-core/src/com/structurizr/view/Styles.java @@ -1,6 +1,7 @@ package com.structurizr.view; import com.structurizr.Workspace; +import com.structurizr.model.DeploymentNode; import com.structurizr.model.Element; import com.structurizr.model.Relationship; import com.structurizr.model.Tags; @@ -141,7 +142,13 @@ private RelationshipStyle findRelationshipStyle(String tag) { } public ElementStyle findElementStyle(Element element) { - ElementStyle style = new ElementStyle("").background("#dddddd").stroke("#dddddd").color("#000000").shape(Shape.Box).fontSize(24).border(Border.Solid).opacity(100).metadata(true).description(true); + ElementStyle style = new ElementStyle("").background("#dddddd").color("#000000").shape(Shape.Box).fontSize(24).border(Border.Solid).opacity(100).metadata(true).description(true); + + if (element instanceof DeploymentNode) { + style.setBackground("#ffffff"); + style.setColor("#000000"); + style.setStroke("#888888"); + } if (element != null) { for (String tag : element.getTagsAsSet()) { @@ -168,6 +175,11 @@ public ElementStyle findElementStyle(Element element) { } } + if (style.getStroke() == null) { + java.awt.Color color = java.awt.Color.decode(style.getBackground()); + style.setStroke(String.format("#%06X", (0xFFFFFF & color.darker().getRGB()))); + } + return style; } diff --git a/structurizr-core/test/unit/com/structurizr/view/StylesTests.java b/structurizr-core/test/unit/com/structurizr/view/StylesTests.java index 8a8fb8847..758eee9f4 100644 --- a/structurizr-core/test/unit/com/structurizr/view/StylesTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/StylesTests.java @@ -21,7 +21,7 @@ public void test_findElementStyle_ReturnsTheDefaultStyle_WhenPassedNull() { assertEquals(Shape.Box, style.getShape()); assertNull(style.getIcon()); assertEquals(Border.Solid, style.getBorder()); - assertEquals("#dddddd", style.getStroke()); + assertEquals("#9a9a9a", style.getStroke()); assertEquals(new Integer(100), style.getOpacity()); assertEquals(true, style.getMetadata()); assertEquals(true, style.getDescription()); @@ -39,7 +39,7 @@ public void test_findElementStyle_ReturnsTheDefaultStyle_WhenNoStylesAreDefined( assertEquals(Shape.Box, style.getShape()); assertNull(style.getIcon()); assertEquals(Border.Solid, style.getBorder()); - assertEquals("#dddddd", style.getStroke()); + assertEquals("#9a9a9a", style.getStroke()); assertEquals(new Integer(100), style.getOpacity()); assertEquals(true, style.getMetadata()); assertEquals(true, style.getDescription()); From 81ac817d89e9bff2fb2b6684d4c5c48a207422fa Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 28 Mar 2021 14:53:03 +0100 Subject: [PATCH 229/717] Updated to reflect release. --- docs/binaries.md | 4 ++-- docs/changelog.md | 3 ++- docs/getting-started.md | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/binaries.md b/docs/binaries.md index c9ad0147b..7939ded9f 100644 --- a/docs/binaries.md +++ b/docs/binaries.md @@ -3,5 +3,5 @@ The "Structurizr for Java" binaries are hosted on [Maven Central](https://repo1. Name | Description ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-core:1.9.0 | The core library that can used to create software architecture models. -com.structurizr:structurizr-client:1.9.0 | The API client for publishing models on the Structurizr cloud service and on-premises installation. \ No newline at end of file +com.structurizr:structurizr-core:1.9.1 | The core library that can used to create software architecture models. +com.structurizr:structurizr-client:1.9.1 | The API client for publishing models on the Structurizr cloud service and on-premises installation. \ No newline at end of file diff --git a/docs/changelog.md b/docs/changelog.md index 30902c63c..a087d82ca 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,8 +1,9 @@ # Changelog -## 1.9.1 (unreleased) +## 1.9.1 (28th March 2021) - Adds a `findTerminology` method on the `Terminology` class. +- `Styles.findElementStyle` better mirrors how the Structurizr web renderer deals with element styling. ## 1.9.0 (20th March 2021) diff --git a/docs/getting-started.md b/docs/getting-started.md index 83ebcd1cd..cbd0752c6 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -12,7 +12,7 @@ The Structurizr for Java binaries are hosted on [Maven Central](https://repo1.ma Name | Description ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-client:1.9.0 | The Structurizr API client library. +com.structurizr:structurizr-client:1.9.1 | The Structurizr API client library. ## 2. Create a Java program From 1b289238645ffd2d9048804bd9b63d76b07bf89e Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 3 Apr 2021 15:49:26 +0100 Subject: [PATCH 230/717] Split the Big Bank plc example into two workspaces ... "System Landscape" and "Internet Banking System". --- structurizr-examples/build.gradle | 1 + .../example/bigbankplc/BigBankPlc.java | 74 ++++++++++++++ .../InternetBankingSystem.java} | 97 ++++--------------- .../example/bigbankplc/SystemLandscape.java | 39 ++++++++ .../0001-record-architecture-decisions.md | 19 ++++ .../internetbankingsystem/docs/01-context.md | 11 +++ .../docs/02-containers.md | 21 ++++ .../docs/03-development-environment.adoc | 5 + .../docs/04-deployment.adoc | 5 + 9 files changed, 194 insertions(+), 78 deletions(-) create mode 100644 structurizr-examples/src/com/structurizr/example/bigbankplc/BigBankPlc.java rename structurizr-examples/src/com/structurizr/example/{BigBankPlc.java => bigbankplc/InternetBankingSystem.java} (73%) create mode 100644 structurizr-examples/src/com/structurizr/example/bigbankplc/SystemLandscape.java create mode 100644 structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/adrs/0001-record-architecture-decisions.md create mode 100644 structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/docs/01-context.md create mode 100644 structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/docs/02-containers.md create mode 100644 structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/docs/03-development-environment.adoc create mode 100644 structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/docs/04-deployment.adoc diff --git a/structurizr-examples/build.gradle b/structurizr-examples/build.gradle index ee4f739d1..fb8c88399 100644 --- a/structurizr-examples/build.gradle +++ b/structurizr-examples/build.gradle @@ -1,5 +1,6 @@ dependencies { compile project(':structurizr-client') + compile 'com.structurizr:structurizr-adr-tools:1.3.7' compile 'org.slf4j:slf4j-api:1.7.21' compile 'org.slf4j:slf4j-simple:1.7.21' diff --git a/structurizr-examples/src/com/structurizr/example/bigbankplc/BigBankPlc.java b/structurizr-examples/src/com/structurizr/example/bigbankplc/BigBankPlc.java new file mode 100644 index 000000000..8c28a9129 --- /dev/null +++ b/structurizr-examples/src/com/structurizr/example/bigbankplc/BigBankPlc.java @@ -0,0 +1,74 @@ +package com.structurizr.example.bigbankplc; + +import com.structurizr.Workspace; +import com.structurizr.api.StructurizrClient; +import com.structurizr.documentation.Format; +import com.structurizr.documentation.StructurizrDocumentationTemplate; +import com.structurizr.model.*; +import com.structurizr.util.MapUtils; +import com.structurizr.view.*; + +/** + * This is an example workspace to illustrate the key features of Structurizr, based around a fictional bank. + */ +public abstract class BigBankPlc { + + static final String EXISTING_SYSTEM_TAG = "Existing System"; + static final String CUSTOMER_TAG = "Customer"; + static final String BANK_STAFF_TAG = "Bank Staff"; + + protected Workspace workspace; + protected Model model; + protected ViewSet views; + + Person customer; + Person customerServiceStaff; + Person backOfficeStaff; + SoftwareSystem internetBankingSystem; + SoftwareSystem mainframeBankingSystem; + SoftwareSystem emailSystem; + SoftwareSystem atm; + + BigBankPlc() { + workspace = new Workspace("", ""); + model = workspace.getModel(); + model.setEnterprise(new Enterprise("Big Bank plc")); + model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); + + customer = model.addPerson(Location.External, "Personal Banking Customer", "A customer of the bank, with personal bank accounts."); + customer.addTags(CUSTOMER_TAG); + + internetBankingSystem = model.addSoftwareSystem(Location.Internal, "Internet Banking System", "Allows customers to view information about their bank accounts, and make payments."); + customer.uses(internetBankingSystem, "Views account balances, and makes payments using"); + + mainframeBankingSystem = model.addSoftwareSystem(Location.Internal, "Mainframe Banking System", "Stores all of the core banking information about customers, accounts, transactions, etc."); + mainframeBankingSystem.addTags(EXISTING_SYSTEM_TAG); + internetBankingSystem.uses(mainframeBankingSystem, "Gets account information from, and makes payments using"); + + emailSystem = model.addSoftwareSystem(Location.Internal, "E-mail System", "The internal Microsoft Exchange e-mail system."); + internetBankingSystem.uses(emailSystem, "Sends e-mail using"); + emailSystem.addTags(EXISTING_SYSTEM_TAG); + emailSystem.delivers(customer, "Sends e-mails to"); + + atm = model.addSoftwareSystem(Location.Internal, "ATM", "Allows customers to withdraw cash."); + atm.addTags(EXISTING_SYSTEM_TAG); + atm.uses(mainframeBankingSystem, "Uses"); + customer.uses(atm, "Withdraws cash using"); + + customerServiceStaff = model.addPerson(Location.Internal, "Customer Service Staff", "Customer service staff within the bank."); + customerServiceStaff.addTags(BANK_STAFF_TAG); + customerServiceStaff.uses(mainframeBankingSystem, "Uses"); + customer.interactsWith(customerServiceStaff, "Asks questions to", "Telephone"); + + backOfficeStaff = model.addPerson(Location.Internal, "Back Office Staff", "Administration and support staff within the bank."); + backOfficeStaff.addTags(BANK_STAFF_TAG); + backOfficeStaff.uses(mainframeBankingSystem, "Uses"); + + views = workspace.getViews(); + Styles styles = views.getConfiguration().getStyles(); + styles.addElementStyle(Tags.PERSON).color("#ffffff").shape(Shape.Person).fontSize(22); + styles.addElementStyle(CUSTOMER_TAG).background("#08427b"); + styles.addElementStyle(BANK_STAFF_TAG).background("#999999"); + } + +} \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/BigBankPlc.java b/structurizr-examples/src/com/structurizr/example/bigbankplc/InternetBankingSystem.java similarity index 73% rename from structurizr-examples/src/com/structurizr/example/BigBankPlc.java rename to structurizr-examples/src/com/structurizr/example/bigbankplc/InternetBankingSystem.java index ef586ee01..8d67acafe 100644 --- a/structurizr-examples/src/com/structurizr/example/BigBankPlc.java +++ b/structurizr-examples/src/com/structurizr/example/bigbankplc/InternetBankingSystem.java @@ -1,68 +1,34 @@ -package com.structurizr.example; +package com.structurizr.example.bigbankplc; -import com.structurizr.Workspace; import com.structurizr.api.StructurizrClient; -import com.structurizr.documentation.Format; -import com.structurizr.documentation.StructurizrDocumentationTemplate; +import com.structurizr.documentation.AdrToolsImporter; +import com.structurizr.documentation.AutomaticDocumentationTemplate; import com.structurizr.model.*; import com.structurizr.util.MapUtils; import com.structurizr.view.*; +import java.io.File; + /** * This is an example workspace to illustrate the key features of Structurizr, * based around a fictional Internet Banking System for Big Bank plc. * * The live workspace is available to view at https://structurizr.com/share/36141 */ -public class BigBankPlc { +public final class InternetBankingSystem extends BigBankPlc { private static final long WORKSPACE_ID = 36141; private static final String API_KEY = "key"; private static final String API_SECRET = "secret"; - private static final String EXISTING_SYSTEM_TAG = "Existing System"; - private static final String BANK_STAFF_TAG = "Bank Staff"; private static final String WEB_BROWSER_TAG = "Web Browser"; private static final String MOBILE_APP_TAG = "Mobile App"; private static final String DATABASE_TAG = "Database"; private static final String FAILOVER_TAG = "Failover"; - public static void main(String[] args) throws Exception { - Workspace workspace = new Workspace("Big Bank plc", "This is an example workspace to illustrate the key features of Structurizr, based around a fictional online banking system."); - Model model = workspace.getModel(); - model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); - ViewSet views = workspace.getViews(); - - model.setEnterprise(new Enterprise("Big Bank plc")); - - // people and software systems - Person customer = model.addPerson(Location.External, "Personal Banking Customer", "A customer of the bank, with personal bank accounts."); - - SoftwareSystem internetBankingSystem = model.addSoftwareSystem(Location.Internal, "Internet Banking System", "Allows customers to view information about their bank accounts, and make payments."); - customer.uses(internetBankingSystem, "Views account balances, and makes payments using"); - - SoftwareSystem mainframeBankingSystem = model.addSoftwareSystem(Location.Internal, "Mainframe Banking System", "Stores all of the core banking information about customers, accounts, transactions, etc."); - mainframeBankingSystem.addTags(EXISTING_SYSTEM_TAG); - internetBankingSystem.uses(mainframeBankingSystem, "Gets account information from, and makes payments using"); - - SoftwareSystem emailSystem = model.addSoftwareSystem(Location.Internal, "E-mail System", "The internal Microsoft Exchange e-mail system."); - internetBankingSystem.uses(emailSystem, "Sends e-mail using"); - emailSystem.addTags(EXISTING_SYSTEM_TAG); - emailSystem.delivers(customer, "Sends e-mails to"); - - SoftwareSystem atm = model.addSoftwareSystem(Location.Internal, "ATM", "Allows customers to withdraw cash."); - atm.addTags(EXISTING_SYSTEM_TAG); - atm.uses(mainframeBankingSystem, "Uses"); - customer.uses(atm, "Withdraws cash using"); - - Person customerServiceStaff = model.addPerson(Location.Internal, "Customer Service Staff", "Customer service staff within the bank."); - customerServiceStaff.addTags(BANK_STAFF_TAG); - customerServiceStaff.uses(mainframeBankingSystem, "Uses"); - customer.interactsWith(customerServiceStaff, "Asks questions to", "Telephone"); - - Person backOfficeStaff = model.addPerson(Location.Internal, "Back Office Staff", "Administration and support staff within the bank."); - backOfficeStaff.addTags(BANK_STAFF_TAG); - backOfficeStaff.uses(mainframeBankingSystem, "Uses"); + private InternetBankingSystem() throws Exception { + workspace.setName("Big Bank plc - Internet Banking System"); + workspace.setDescription("The software architecture of the Big Bank plc Internet Banking System."); // containers Container singlePageApplication = internetBankingSystem.addContainer("Single-Page Application", "Provides all of the Internet banking functionality to customers via their web browser.", "JavaScript and Angular"); @@ -150,10 +116,6 @@ public static void main(String[] args) throws Exception { liveSecondaryDatabase.addTags(FAILOVER_TAG); // views/diagrams - SystemLandscapeView systemLandscapeView = views.createSystemLandscapeView("SystemLandscape", "The system landscape diagram for Big Bank plc."); - systemLandscapeView.addAllElements(); - systemLandscapeView.setPaperSize(PaperSize.A5_Landscape); - SystemContextView systemContextView = views.createSystemContextView(internetBankingSystem, "SystemContext", "The system context diagram for the Internet Banking System."); systemContextView.setEnterpriseBoundaryVisible(false); systemContextView.addNearestNeighbours(internetBankingSystem); @@ -175,10 +137,6 @@ public static void main(String[] args) throws Exception { componentView.add(emailSystem); componentView.setPaperSize(PaperSize.A5_Landscape); - systemLandscapeView.addAnimation(internetBankingSystem, customer, mainframeBankingSystem, emailSystem); - systemLandscapeView.addAnimation(atm); - systemLandscapeView.addAnimation(customerServiceStaff, backOfficeStaff); - systemContextView.addAnimation(internetBankingSystem); systemContextView.addAnimation(customer); systemContextView.addAnimation(mainframeBankingSystem); @@ -237,9 +195,7 @@ public static void main(String[] args) throws Exception { styles.addElementStyle(Tags.SOFTWARE_SYSTEM).background("#1168bd").color("#ffffff"); styles.addElementStyle(Tags.CONTAINER).background("#438dd5").color("#ffffff"); styles.addElementStyle(Tags.COMPONENT).background("#85bbf0").color("#000000"); - styles.addElementStyle(Tags.PERSON).background("#08427b").color("#ffffff").shape(Shape.Person).fontSize(22); styles.addElementStyle(EXISTING_SYSTEM_TAG).background("#999999").color("#ffffff"); - styles.addElementStyle(BANK_STAFF_TAG).background("#999999").color("#ffffff"); styles.addElementStyle(WEB_BROWSER_TAG).shape(Shape.WebBrowser); styles.addElementStyle(MOBILE_APP_TAG).shape(Shape.MobileDeviceLandscape); styles.addElementStyle(DATABASE_TAG).shape(Shape.Cylinder); @@ -247,34 +203,19 @@ public static void main(String[] args) throws Exception { styles.addRelationshipStyle(FAILOVER_TAG).opacity(25).position(70); // documentation - // - usually the documentation would be included from separate Markdown/AsciiDoc files, but this is just an example - StructurizrDocumentationTemplate template = new StructurizrDocumentationTemplate(workspace); - template.addContextSection(internetBankingSystem, Format.Markdown, - "Here is some context about the Internet Banking System...\n" + - "![](embed:SystemLandscape)\n" + - "![](embed:SystemContext)\n" + - "### Internet Banking System\n...\n" + - "### Mainframe Banking System\n...\n"); - template.addContainersSection(internetBankingSystem, Format.Markdown, - "Here is some information about the containers within the Internet Banking System...\n" + - "![](embed:Containers)\n" + - "### Web Application\n...\n" + - "### Database\n...\n"); - template.addComponentsSection(webApplication, Format.Markdown, - "Here is some information about the API Application...\n" + - "![](embed:Components)\n" + - "### Sign in process\n" + - "Here is some information about the Sign In Controller, including how the sign in process works...\n" + - "![](embed:SignIn)"); - template.addDevelopmentEnvironmentSection(internetBankingSystem, Format.AsciiDoc, - "Here is some information about how to set up a development environment for the Internet Banking System...\n" + - "image::embed:DevelopmentDeployment[]"); - template.addDeploymentSection(internetBankingSystem, Format.AsciiDoc, - "Here is some information about the live deployment environment for the Internet Banking System...\n" + - "image::embed:LiveDeployment[]"); + AutomaticDocumentationTemplate template = new AutomaticDocumentationTemplate(workspace); + template.addSections(internetBankingSystem, new File("./structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/docs")); + + // ADRs + AdrToolsImporter adrToolsImporter = new AdrToolsImporter(workspace, new File("./structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/adrs")); + adrToolsImporter.importArchitectureDecisionRecords(internetBankingSystem); StructurizrClient structurizrClient = new StructurizrClient(API_KEY, API_SECRET); structurizrClient.putWorkspace(WORKSPACE_ID, workspace); } + public static void main(String[] args) throws Exception { + new InternetBankingSystem(); + } + } \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/bigbankplc/SystemLandscape.java b/structurizr-examples/src/com/structurizr/example/bigbankplc/SystemLandscape.java new file mode 100644 index 000000000..e2fd61d96 --- /dev/null +++ b/structurizr-examples/src/com/structurizr/example/bigbankplc/SystemLandscape.java @@ -0,0 +1,39 @@ +package com.structurizr.example.bigbankplc; + +import com.structurizr.api.StructurizrClient; +import com.structurizr.model.*; +import com.structurizr.view.*; + +/** + * This is an example workspace to illustrate the key features of Structurizr, + * based around a fictional bank. This workspace contains the system landscape. + * + * The live workspace is available to view at https://structurizr.com/share/28201 + */ +public final class SystemLandscape extends BigBankPlc { + + private static final long WORKSPACE_ID = 28201; + private static final String API_KEY = "key"; + private static final String API_SECRET = "secret"; + + private SystemLandscape() throws Exception { + workspace.setName("Big Bank plc - System Landscape"); + workspace.setDescription("The system landscape for Big Bank plc."); + + internetBankingSystem.setUrl("https://structurizr.com/share/36141/diagrams#SystemContext"); + + SystemLandscapeView systemLandscapeView = views.createSystemLandscapeView("SystemLandscape", "The system landscape diagram for Big Bank plc."); + systemLandscapeView.addAllElements(); + + Styles styles = views.getConfiguration().getStyles(); + styles.addElementStyle(Tags.SOFTWARE_SYSTEM).background("#999999").color("#ffffff"); + + StructurizrClient structurizrClient = new StructurizrClient(API_KEY, API_SECRET); + structurizrClient.putWorkspace(WORKSPACE_ID, workspace); + } + + public static void main(String[] args) throws Exception { + new SystemLandscape(); + } + +} \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/adrs/0001-record-architecture-decisions.md b/structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/adrs/0001-record-architecture-decisions.md new file mode 100644 index 000000000..7461d99d3 --- /dev/null +++ b/structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/adrs/0001-record-architecture-decisions.md @@ -0,0 +1,19 @@ +# 1. Record architecture decisions + +Date: 2020-06-05 + +## Status + +Accepted + +## Context + +We need to record the architectural decisions made on this project. + +## Decision + +We will use Architecture Decision Records, as described by Michael Nygard in this article: [http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions) + +## Consequences + +See Michael Nygard's article, linked above. \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/docs/01-context.md b/structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/docs/01-context.md new file mode 100644 index 000000000..5440e9435 --- /dev/null +++ b/structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/docs/01-context.md @@ -0,0 +1,11 @@ +## Context + +Here is some context about the Internet Banking System... + +![](embed:SystemContext) + +### Internet Banking System +... + +### Mainframe Banking System +... diff --git a/structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/docs/02-containers.md b/structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/docs/02-containers.md new file mode 100644 index 000000000..d4d8d9aab --- /dev/null +++ b/structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/docs/02-containers.md @@ -0,0 +1,21 @@ +## Software Architecture + +Here is some information about the software architecture of the Internet Banking System... + +![](embed:Containers) + +### Web Application +... + +### Database +... + +Here is some information about the API Application... + +![](embed:Components) + +### Sign in process + +Here is some information about the Sign In Controller, including how the sign in process works... + +![](embed:SignIn) \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/docs/03-development-environment.adoc b/structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/docs/03-development-environment.adoc new file mode 100644 index 000000000..9f9b6d664 --- /dev/null +++ b/structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/docs/03-development-environment.adoc @@ -0,0 +1,5 @@ +== Development Environment + +Here is some information about how to set up a development environment for the Internet Banking System... + +image::embed:DevelopmentDeployment[] \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/docs/04-deployment.adoc b/structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/docs/04-deployment.adoc new file mode 100644 index 000000000..be82ea565 --- /dev/null +++ b/structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/docs/04-deployment.adoc @@ -0,0 +1,5 @@ +== Deployment + +Here is some information about the live deployment environment for the Internet Banking System... + +image::embed:LiveDeployment[] \ No newline at end of file From 7112b1826d9f5153e31073273f2f4ff2134b30cf Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 7 Apr 2021 12:39:12 +0100 Subject: [PATCH 231/717] Mirrors a similar fix added to the .NET port (the bug isn't a problem in the Java client library, because hash maps return null rather than throwing an "element does not exist" exception ... but it keeps the code consistent). --- .../view/DefaultLayoutMergeStrategy.java | 8 +++++++ .../view/DefaultLayoutMergeStrategyTests.java | 21 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/structurizr-core/src/com/structurizr/view/DefaultLayoutMergeStrategy.java b/structurizr-core/src/com/structurizr/view/DefaultLayoutMergeStrategy.java index 8f86c1097..40ebd2883 100644 --- a/structurizr-core/src/com/structurizr/view/DefaultLayoutMergeStrategy.java +++ b/structurizr-core/src/com/structurizr/view/DefaultLayoutMergeStrategy.java @@ -106,6 +106,10 @@ protected ElementView findElementView(View viewWithLayoutInformation, Element el } private RelationshipView findRelationshipView(View viewWithLayoutInformation, Relationship relationshipWithoutLayoutInformation, Map elementMap) { + if (!elementMap.containsKey(relationshipWithoutLayoutInformation.getSource()) || !elementMap.containsKey(relationshipWithoutLayoutInformation.getDestination())) { + return null; + } + Element sourceElementWithLayoutInformation = elementMap.get(relationshipWithoutLayoutInformation.getSource()); Element destinationElementWithLayoutInformation = elementMap.get(relationshipWithoutLayoutInformation.getDestination()); @@ -123,6 +127,10 @@ private RelationshipView findRelationshipView(View viewWithLayoutInformation, Re } private RelationshipView findRelationshipView(View view, RelationshipView relationshipWithoutLayoutInformation, Map elementMap) { + if (!elementMap.containsKey(relationshipWithoutLayoutInformation.getRelationship().getSource()) || !elementMap.containsKey(relationshipWithoutLayoutInformation.getRelationship().getDestination())) { + return null; + } + Element sourceElementWithLayoutInformation = elementMap.get(relationshipWithoutLayoutInformation.getRelationship().getSource()); Element destinationElementWithLayoutInformation = elementMap.get(relationshipWithoutLayoutInformation.getRelationship().getDestination()); diff --git a/structurizr-core/test/unit/com/structurizr/view/DefaultLayoutMergeStrategyTests.java b/structurizr-core/test/unit/com/structurizr/view/DefaultLayoutMergeStrategyTests.java index d518c8cb2..e09e4462d 100644 --- a/structurizr-core/test/unit/com/structurizr/view/DefaultLayoutMergeStrategyTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/DefaultLayoutMergeStrategyTests.java @@ -150,4 +150,25 @@ public void test_copyLayoutInformation_WhenAnElementNameAndDescriptionAndIdHaveC assertEquals(0, view2.getElementView(container2).getY()); } + @Test + public void test_copyLayoutInformation_DoesNotThrowAnExceptionWhenAddingAnElementToAView() { + Workspace workspace1 = new Workspace("1", ""); + SoftwareSystem softwareSystem1A = workspace1.getModel().addSoftwareSystem("Software System A"); + SoftwareSystem softwareSystem1B = workspace1.getModel().addSoftwareSystem("Software System B"); + softwareSystem1A.uses(softwareSystem1B, "Uses"); + SystemLandscapeView view1 = workspace1.getViews().createSystemLandscapeView("key", "description"); + view1.add(softwareSystem1A); + + Workspace workspace2 = new Workspace("2", ""); + SoftwareSystem softwareSystem2A = workspace2.getModel().addSoftwareSystem("Software System A"); + SoftwareSystem softwareSystem2B = workspace2.getModel().addSoftwareSystem("Software System B"); + softwareSystem2A.uses(softwareSystem2B, "Uses"); + SystemLandscapeView view2 = workspace2.getViews().createSystemLandscapeView("key", "description"); + view2.add(softwareSystem2A); + view2.add(softwareSystem2B); + + DefaultLayoutMergeStrategy strategy = new DefaultLayoutMergeStrategy(); + strategy.copyLayoutInformation(view1, view2); + } + } \ No newline at end of file From 6b7c7da31f07207d41f9fb37a1e2e0c984ecb83c Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 7 Apr 2021 16:49:48 +0100 Subject: [PATCH 232/717] Adds a Diamond shape. --- structurizr-core/src/com/structurizr/view/Shape.java | 1 + 1 file changed, 1 insertion(+) diff --git a/structurizr-core/src/com/structurizr/view/Shape.java b/structurizr-core/src/com/structurizr/view/Shape.java index 51c5e8918..b53c35446 100644 --- a/structurizr-core/src/com/structurizr/view/Shape.java +++ b/structurizr-core/src/com/structurizr/view/Shape.java @@ -7,6 +7,7 @@ public enum Shape { Circle, Ellipse, Hexagon, + Diamond, Cylinder, Pipe, Person, From 3cbc488cf408a42c8c565e093bdaacaab6dd3329 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 27 Apr 2021 08:45:37 +0100 Subject: [PATCH 233/717] Updated to reflect release. --- build.gradle | 2 +- docs/binaries.md | 4 ++-- docs/changelog.md | 4 ++++ docs/getting-started.md | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 7a730925b..e996d9cfe 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.9.1' + version = '1.9.2' repositories { mavenCentral() diff --git a/docs/binaries.md b/docs/binaries.md index 7939ded9f..0a60c7fbe 100644 --- a/docs/binaries.md +++ b/docs/binaries.md @@ -3,5 +3,5 @@ The "Structurizr for Java" binaries are hosted on [Maven Central](https://repo1. Name | Description ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-core:1.9.1 | The core library that can used to create software architecture models. -com.structurizr:structurizr-client:1.9.1 | The API client for publishing models on the Structurizr cloud service and on-premises installation. \ No newline at end of file +com.structurizr:structurizr-core:1.9.2 | The core library that can used to create software architecture models. +com.structurizr:structurizr-client:1.9.2 | The API client for publishing models on the Structurizr cloud service and on-premises installation. \ No newline at end of file diff --git a/docs/changelog.md b/docs/changelog.md index a087d82ca..ae2599fc6 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.9.2 (27th April 2021) + +- Adds a `Diamond` shape. + ## 1.9.1 (28th March 2021) - Adds a `findTerminology` method on the `Terminology` class. diff --git a/docs/getting-started.md b/docs/getting-started.md index cbd0752c6..32da98e3e 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -12,7 +12,7 @@ The Structurizr for Java binaries are hosted on [Maven Central](https://repo1.ma Name | Description ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-client:1.9.1 | The Structurizr API client library. +com.structurizr:structurizr-client:1.9.2 | The Structurizr API client library. ## 2. Create a Java program From 30b69e28ee32169254f22b566d6751696af9dcb0 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 27 Apr 2021 09:54:00 +0100 Subject: [PATCH 234/717] Create gradle.yml --- .github/workflows/gradle.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .github/workflows/gradle.yml diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml new file mode 100644 index 000000000..15bdca638 --- /dev/null +++ b/.github/workflows/gradle.yml @@ -0,0 +1,27 @@ +# This workflow will build a Java project with Gradle +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle + +name: Java CI with Gradle + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 11 + uses: actions/setup-java@v2 + with: + java-version: '11' + distribution: 'adopt' + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Build with Gradle + run: ./gradlew build From 13112b40307847ec5e9b55bd371e646190e84e81 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 27 Apr 2021 10:02:42 +0100 Subject: [PATCH 235/717] Use default Gradle tasks. --- .github/workflows/gradle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 15bdca638..9432fa591 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -24,4 +24,4 @@ jobs: - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build with Gradle - run: ./gradlew build + run: ./gradlew From 637b8fa26e440c9c4338042366aff4ef77579dd6 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 11 May 2021 09:54:34 +0100 Subject: [PATCH 236/717] Added an addTheme() method on Configuration. --- build.gradle | 2 +- docs/changelog.md | 4 +++ .../src/com/structurizr/view/ThemeUtils.java | 28 +++++++-------- .../com/structurizr/view/Configuration.java | 35 +++++++++++-------- 4 files changed, 39 insertions(+), 30 deletions(-) diff --git a/build.gradle b/build.gradle index e996d9cfe..8ae8907b7 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.9.2' + version = '1.9.3' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index ae2599fc6..cfe408567 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.9.3 (unreleased) + +- Added an `addTheme` method on `Configuration`. + ## 1.9.2 (27th April 2021) - Adds a `Diamond` shape. diff --git a/structurizr-client/src/com/structurizr/view/ThemeUtils.java b/structurizr-client/src/com/structurizr/view/ThemeUtils.java index 4a13de295..045e71579 100644 --- a/structurizr-client/src/com/structurizr/view/ThemeUtils.java +++ b/structurizr-client/src/com/structurizr/view/ThemeUtils.java @@ -66,26 +66,24 @@ public static String toJson(Workspace workspace) throws Exception { * @throws Exception if something goes wrong */ public static void loadThemes(Workspace workspace) throws Exception { - if (workspace.getViews().getConfiguration().getThemes() != null) { - for (String url : workspace.getViews().getConfiguration().getThemes()) { - CloseableHttpClient httpClient = HttpClients.createSystem(); - HttpGet httpGet = new HttpGet(url); + for (String url : workspace.getViews().getConfiguration().getThemes()) { + CloseableHttpClient httpClient = HttpClients.createSystem(); + HttpGet httpGet = new HttpGet(url); - CloseableHttpResponse response = httpClient.execute(httpGet); - if (response.getCode() == HTTP_OK_STATUS) { - String json = EntityUtils.toString(response.getEntity()); + CloseableHttpResponse response = httpClient.execute(httpGet); + if (response.getCode() == HTTP_OK_STATUS) { + String json = EntityUtils.toString(response.getEntity()); - ObjectMapper objectMapper = new ObjectMapper(); - objectMapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT); - objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - Theme theme = objectMapper.readValue(json, Theme.class); + Theme theme = objectMapper.readValue(json, Theme.class); - workspace.getViews().getConfiguration().getStyles().addStylesFromTheme(url, theme.getElements(), theme.getRelationships()); - } - - httpClient.close(); + workspace.getViews().getConfiguration().getStyles().addStylesFromTheme(url, theme.getElements(), theme.getRelationships()); } + + httpClient.close(); } } diff --git a/structurizr-core/src/com/structurizr/view/Configuration.java b/structurizr-core/src/com/structurizr/view/Configuration.java index 8445b1654..5dbda9ca1 100644 --- a/structurizr-core/src/com/structurizr/view/Configuration.java +++ b/structurizr-core/src/com/structurizr/view/Configuration.java @@ -15,7 +15,7 @@ public final class Configuration { private Branding branding = new Branding(); private Styles styles = new Styles(); - private String[] themes; + private List themes = new ArrayList<>(); private Terminology terminology = new Terminology(); private MetadataSymbols metadataSymbols; @@ -36,11 +36,11 @@ public Styles getStyles() { @JsonIgnore @Deprecated public String getTheme() { - if (themes == null || themes.length == 0) { + if (themes == null || themes.size() == 0) { return null; } - return themes[0]; + return themes.get(0); } /** @@ -59,7 +59,7 @@ void setTheme(String url) { * @return an array of URLs */ public String[] getThemes() { - return themes; + return themes.toArray(new String[0]); } /** @@ -68,21 +68,28 @@ public String[] getThemes() { * @param themes an array of URLs */ public void setThemes(String... themes) { - List list = new ArrayList<>(); - if (themes != null) { for (String url : themes) { - if (url != null && url.trim().length() > 0) { - if (Url.isUrl(url)) { - list.add(url.trim()); - } else { - throw new IllegalArgumentException(url + " is not a valid URL."); - } - } + addTheme(url); } } + } - this.themes = list.toArray(new String[0]); + /** + * Adds a theme. + * + * @param url the URL of the theme to be added + */ + public void addTheme(String url) { + if (url != null && url.trim().length() > 0) { + if (Url.isUrl(url)) { + if (!themes.contains(url)) { + themes.add(url.trim()); + } + } else { + throw new IllegalArgumentException(url + " is not a valid URL."); + } + } } /** From 1ad22c0aeddacb3b7ec4ef945a8fad7712f50981 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 11 May 2021 10:27:10 +0100 Subject: [PATCH 237/717] Removed the addDefaultStyles() method on Styles. --- docs/changelog.md | 1 + structurizr-core/src/com/structurizr/view/Styles.java | 9 --------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index cfe408567..23cb5b956 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -3,6 +3,7 @@ ## 1.9.3 (unreleased) - Added an `addTheme` method on `Configuration`. +- Removed the `addDefaultStyles` method on `Styles`. ## 1.9.2 (27th April 2021) diff --git a/structurizr-core/src/com/structurizr/view/Styles.java b/structurizr-core/src/com/structurizr/view/Styles.java index 944efc2fd..de734dfc4 100644 --- a/structurizr-core/src/com/structurizr/view/Styles.java +++ b/structurizr-core/src/com/structurizr/view/Styles.java @@ -61,15 +61,6 @@ public void clearRelationshipStyles() { this.relationships = new LinkedList<>(); } - public void addDefaultStyles() { - addElementStyle(Tags.ELEMENT).shape(Shape.RoundedBox); - addElementStyle(Tags.SOFTWARE_SYSTEM).background("#1168bd").color("#ffffff"); - addElementStyle(Tags.CONTAINER).background("#438dd5").color("#ffffff"); - addElementStyle(Tags.COMPONENT).background("#85bbf0").color("#000000"); - addElementStyle(Tags.PERSON).background("#08427b").color("#ffffff").shape(Shape.Person); - addElementStyle(Tags.INFRASTRUCTURE_NODE).background("#ffffff"); - } - public Collection getRelationships() { return relationships; } From d8e6c88f7d45750d000c68f92e62d60da0ebd945 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 11 May 2021 12:48:30 +0100 Subject: [PATCH 238/717] Updated to reflect release. --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 23cb5b956..30c60b0d1 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 1.9.3 (unreleased) +## 1.9.3 (11th May 2021) - Added an `addTheme` method on `Configuration`. - Removed the `addDefaultStyles` method on `Styles`. From b585eeddd673bf134a08daf85749c09896871820 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 22 May 2021 11:41:11 +0100 Subject: [PATCH 239/717] Bug fixes to prevent parents and children to both be added to container/component views. --- build.gradle | 2 +- docs/changelog.md | 4 + .../com/structurizr/view/ComponentView.java | 3 + .../com/structurizr/view/ContainerView.java | 2 + .../com/structurizr/view/DeploymentView.java | 2 +- .../src/com/structurizr/view/View.java | 25 ++-- .../structurizr/view/ComponentViewTests.java | 110 ++++++++++++++++++ .../structurizr/view/ContainerViewTests.java | 45 +++++++ .../structurizr/view/DeploymentViewTests.java | 2 +- .../structurizr/view/DynamicViewTests.java | 4 +- 10 files changed, 187 insertions(+), 12 deletions(-) diff --git a/build.gradle b/build.gradle index 8ae8907b7..a4569d654 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.9.3' + version = '1.9.4' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 30c60b0d1..2e29c75ad 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.9.4 (22nd May 2021) + +- Bug fixes to prevent parents and children to both be added to container/component views. + ## 1.9.3 (11th May 2021) - Added an `addTheme` method on `Configuration`. diff --git a/structurizr-core/src/com/structurizr/view/ComponentView.java b/structurizr-core/src/com/structurizr/view/ComponentView.java index 7160cd6e2..8707f5ca3 100644 --- a/structurizr-core/src/com/structurizr/view/ComponentView.java +++ b/structurizr-core/src/com/structurizr/view/ComponentView.java @@ -266,6 +266,7 @@ protected void checkElementCanBeAdded(Element element) { if (element.equals(getContainer().getParent())) { throw new ElementNotPermittedInViewException("The software system in scope cannot be added to a component view."); } else { + checkParentAndChildrenHaveNotAlreadyBeenAdded((SoftwareSystem)element); return; } } @@ -274,11 +275,13 @@ protected void checkElementCanBeAdded(Element element) { if (element.equals(getContainer())) { throw new ElementNotPermittedInViewException("The container in scope cannot be added to a component view."); } else { + checkParentAndChildrenHaveNotAlreadyBeenAdded((Container)element); return; } } if (element instanceof Component) { + checkParentAndChildrenHaveNotAlreadyBeenAdded((Component)element); return; } diff --git a/structurizr-core/src/com/structurizr/view/ContainerView.java b/structurizr-core/src/com/structurizr/view/ContainerView.java index 539b1403c..2bfdadc9b 100644 --- a/structurizr-core/src/com/structurizr/view/ContainerView.java +++ b/structurizr-core/src/com/structurizr/view/ContainerView.java @@ -181,11 +181,13 @@ protected void checkElementCanBeAdded(Element element) { if (element.equals(getSoftwareSystem())) { throw new ElementNotPermittedInViewException("The software system in scope cannot be added to a container view."); } else { + checkParentAndChildrenHaveNotAlreadyBeenAdded((SoftwareSystem)element); return; } } if (element instanceof Container) { + checkParentAndChildrenHaveNotAlreadyBeenAdded((Container)element); return; } diff --git a/structurizr-core/src/com/structurizr/view/DeploymentView.java b/structurizr-core/src/com/structurizr/view/DeploymentView.java index 7d5831c61..b1669cbed 100644 --- a/structurizr-core/src/com/structurizr/view/DeploymentView.java +++ b/structurizr-core/src/com/structurizr/view/DeploymentView.java @@ -314,7 +314,7 @@ protected void checkElementCanBeAdded(Element elementToBeAdded) { Set softwareSystemIds = getElements().stream().map(ElementView::getElement).filter(e -> e instanceof SoftwareSystemInstance).map(e -> (SoftwareSystemInstance)e).map(SoftwareSystemInstance::getSoftwareSystemId).collect(Collectors.toSet()); if (softwareSystemIds.contains(containerInstanceToBeAdded.getContainer().getSoftwareSystem().getId())) { - throw new ElementNotPermittedInViewException("The parent of " + elementToBeAdded.getName() + " is already in this view."); + throw new ElementNotPermittedInViewException("A parent of " + elementToBeAdded.getName() + " is already in this view."); } } } diff --git a/structurizr-core/src/com/structurizr/view/View.java b/structurizr-core/src/com/structurizr/view/View.java index f57d24f58..e72220604 100644 --- a/structurizr-core/src/com/structurizr/view/View.java +++ b/structurizr-core/src/com/structurizr/view/View.java @@ -439,20 +439,31 @@ public ViewSet getViewSet() { protected abstract boolean canBeRemoved(Element element); final void checkParentAndChildrenHaveNotAlreadyBeenAdded(StaticStructureElement elementToBeAdded) { - // check the parent hasn't been added already - Set elementIds = getElements().stream().map(ElementView::getElement).map(Element::getId).collect(Collectors.toSet()); + // check a parent hasn't been added already + Set idsOfElementsInView = getElements().stream().map(ElementView::getElement).map(Element::getId).collect(Collectors.toSet()); - if (elementToBeAdded.getParent() != null) { - if (elementIds.contains(elementToBeAdded.getParent().getId())) { - throw new ElementNotPermittedInViewException("The parent of " + elementToBeAdded.getName() + " is already in this view."); + Element parent = elementToBeAdded.getParent(); + while (parent != null) { + if (idsOfElementsInView.contains(parent.getId())) { + throw new ElementNotPermittedInViewException("A parent of " + elementToBeAdded.getName() + " is already in this view."); } + + parent = parent.getParent(); } // and now check a child hasn't been added already - Set elementParentIds = getElements().stream().map(ElementView::getElement).filter(e -> e.getParent() != null).map(e -> e.getParent().getId()).collect(Collectors.toSet()); + Set elementParentIds = new HashSet<>(); + for (ElementView elementView : getElements()) { + Element element = elementView.getElement(); + parent = element.getParent(); + while (parent != null) { + elementParentIds.add(parent.getId()); + parent = parent.getParent(); + } + } if (elementParentIds.contains(elementToBeAdded.getId())) { - throw new ElementNotPermittedInViewException("The child of " + elementToBeAdded.getName() + " is already in this view."); + throw new ElementNotPermittedInViewException("A child of " + elementToBeAdded.getName() + " is already in this view."); } } diff --git a/structurizr-core/test/unit/com/structurizr/view/ComponentViewTests.java b/structurizr-core/test/unit/com/structurizr/view/ComponentViewTests.java index c34ea6edf..34f5aa5f8 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ComponentViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ComponentViewTests.java @@ -626,4 +626,114 @@ public void test_addContainer_ThrowsAnException_WhenTheContainerIsTheScopeOfTheV } } + @Test + public void test_addSoftwareSystem_ThrowsAnException_WhenAChildContainerIsAlreadyAdded() { + try { + SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1"); + Container container1 = softwareSystem1.addContainer("Container 1"); + Component component1 = container1.addComponent("Component 1"); + + SoftwareSystem softwareSystem2 = model.addSoftwareSystem("Software System 2"); + Container container2 = softwareSystem2.addContainer("Container 2"); + Component component2 = container2.addComponent("Component 2"); + + ComponentView view = views.createComponentView(container1, "key", "Description"); + + view.add(container2); + view.add(softwareSystem2); + + fail(); + } catch (ElementNotPermittedInViewException e) { + assertEquals("A child of Software System 2 is already in this view.", e.getMessage()); + } + } + + @Test + public void test_addSoftwareSystem_ThrowsAnException_WhenAChildComponentIsAlreadyAdded() { + try { + SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1"); + Container container1 = softwareSystem1.addContainer("Container 1"); + Component component1 = container1.addComponent("Component 1"); + + SoftwareSystem softwareSystem2 = model.addSoftwareSystem("Software System 2"); + Container container2 = softwareSystem2.addContainer("Container 2"); + Component component2 = container2.addComponent("Component 2"); + + ComponentView view = views.createComponentView(container1, "key", "Description"); + + view.add(component2); + view.add(softwareSystem2); + + fail(); + } catch (ElementNotPermittedInViewException e) { + assertEquals("A child of Software System 2 is already in this view.", e.getMessage()); + } + } + + @Test + public void test_addContainer_ThrowsAnException_WhenAChildComponentIsAlreadyAdded() { + try { + SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1"); + Container container1 = softwareSystem1.addContainer("Container 1"); + Component component1 = container1.addComponent("Component 1"); + + SoftwareSystem softwareSystem2 = model.addSoftwareSystem("Software System 2"); + Container container2 = softwareSystem2.addContainer("Container 2"); + Component component2 = container2.addComponent("Component 2"); + + ComponentView view = views.createComponentView(container1, "key", "Description"); + + view.add(component2); + view.add(container2); + + fail(); + } catch (ElementNotPermittedInViewException e) { + assertEquals("A child of Container 2 is already in this view.", e.getMessage()); + } + } + + @Test + public void test_addContainer_ThrowsAnException_WhenTheParentIsAlreadyAdded() { + try { + SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1"); + Container container1 = softwareSystem1.addContainer("Container 1"); + Component component1 = container1.addComponent("Component 1"); + + SoftwareSystem softwareSystem2 = model.addSoftwareSystem("Software System 2"); + Container container2 = softwareSystem2.addContainer("Container 2"); + Component component2 = container2.addComponent("Component 2"); + + ComponentView view = views.createComponentView(container1, "key", "Description"); + + view.add(softwareSystem2); + view.add(container2); + + fail(); + } catch (ElementNotPermittedInViewException e) { + assertEquals("A parent of Container 2 is already in this view.", e.getMessage()); + } + } + + @Test + public void test_addComponent_ThrowsAnException_WhenTheParentIsAlreadyAdded() { + try { + SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1"); + Container container1 = softwareSystem1.addContainer("Container 1"); + Component component1 = container1.addComponent("Component 1"); + + SoftwareSystem softwareSystem2 = model.addSoftwareSystem("Software System 2"); + Container container2 = softwareSystem2.addContainer("Container 2"); + Component component2 = container2.addComponent("Component 2"); + + ComponentView view = views.createComponentView(container1, "key", "Description"); + + view.add(softwareSystem2); + view.add(component2); + + fail(); + } catch (ElementNotPermittedInViewException e) { + assertEquals("A parent of Component 2 is already in this view.", e.getMessage()); + } + } + } \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java b/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java index 0416ed83d..c9ec0b7b0 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java @@ -6,6 +6,9 @@ import org.junit.Before; import org.junit.Test; +import java.util.HashMap; +import java.util.Map; + import static org.junit.Assert.*; public class ContainerViewTests extends AbstractWorkspaceTestBase { @@ -306,4 +309,46 @@ public void test_addSoftwareSystem_ThrowsAnException_WhenTheSoftwareSystemIsTheS } } + @Test + public void test_addSoftwareSystem_ThrowsAnException_WhenAChildContainerIsAlreadyAdded() { + try { + SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1"); + Container container1 = softwareSystem1.addContainer("Container 1"); + + SoftwareSystem softwareSystem2 = model.addSoftwareSystem("Software System 2"); + Container container2 = softwareSystem2.addContainer("Container 2"); + + ContainerView view = views.createContainerView(softwareSystem1, "key", "Description"); + + view.add(container1); + view.add(container2); + view.add(softwareSystem2); + + fail(); + } catch (ElementNotPermittedInViewException e) { + assertEquals("A child of Software System 2 is already in this view.", e.getMessage()); + } + } + + @Test + public void test_addContainer_ThrowsAnException_WhenTheParentIsAlreadyAdded() { + try { + SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1"); + Container container1 = softwareSystem1.addContainer("Container 1"); + + SoftwareSystem softwareSystem2 = model.addSoftwareSystem("Software System 2"); + Container container2 = softwareSystem2.addContainer("Container 2"); + + ContainerView view = views.createContainerView(softwareSystem1, "key", "Description"); + + view.add(container1); + view.add(softwareSystem2); + view.add(container2); + + fail(); + } catch (ElementNotPermittedInViewException e) { + assertEquals("A parent of Container 2 is already in this view.", e.getMessage()); + } + } + } \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java b/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java index dd38ba656..7d35e9220 100644 --- a/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java @@ -532,7 +532,7 @@ public void test_addContainerInstance_ThrowsAnException_WhenTheParentSoftwareSys deploymentView.add(containerInstance); fail(); } catch (ElementNotPermittedInViewException e) { - assertEquals("The parent of Container is already in this view.", e.getMessage()); + assertEquals("A parent of Container is already in this view.", e.getMessage()); } } diff --git a/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java b/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java index 49fdb36d7..f26997d7d 100644 --- a/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java @@ -149,7 +149,7 @@ public void test_add_ThrowsAnException_WhenTheParentOfAnElementHasAlreadyBeenAdd dynamicView.add(component1, component2); fail(); } catch (Exception e) { - assertEquals("The parent of Component 2 is already in this view.", e.getMessage()); + assertEquals("A parent of Component 2 is already in this view.", e.getMessage()); } } @@ -171,7 +171,7 @@ public void test_add_ThrowsAnException_WhenTheChildOfAnElementHasAlreadyBeenAdde dynamicView.add(component1, container2); fail(); } catch (Exception e) { - assertEquals("The child of Container 2 is already in this view.", e.getMessage()); + assertEquals("A child of Container 2 is already in this view.", e.getMessage()); } } From e2f3b8453fe8790137517a0eccefeda5f9c3cfcc Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 6 Jun 2021 16:38:19 +0100 Subject: [PATCH 240/717] Provides a way to store view dimensions. --- build.gradle | 2 +- docs/changelog.md | 4 +++ .../view/DefaultLayoutMergeStrategy.java | 7 ++++ .../src/com/structurizr/view/Dimensions.java | 32 +++++++++++++++++++ .../src/com/structurizr/view/View.java | 9 ++++++ 5 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 structurizr-core/src/com/structurizr/view/Dimensions.java diff --git a/build.gradle b/build.gradle index a4569d654..0a3f4fdaf 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.9.4' + version = '1.9.5' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 2e29c75ad..631dc2b54 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.9.5 (unreleased) + +- Provides a way to store view dimensions. + ## 1.9.4 (22nd May 2021) - Bug fixes to prevent parents and children to both be added to container/component views. diff --git a/structurizr-core/src/com/structurizr/view/DefaultLayoutMergeStrategy.java b/structurizr-core/src/com/structurizr/view/DefaultLayoutMergeStrategy.java index 40ebd2883..0353269e0 100644 --- a/structurizr-core/src/com/structurizr/view/DefaultLayoutMergeStrategy.java +++ b/structurizr-core/src/com/structurizr/view/DefaultLayoutMergeStrategy.java @@ -35,6 +35,7 @@ public class DefaultLayoutMergeStrategy implements LayoutMergeStrategy { */ public void copyLayoutInformation(@Nonnull View viewWithLayoutInformation, @Nonnull View viewWithoutLayoutInformation) { setPaperSizeIfNotSpecified(viewWithLayoutInformation, viewWithoutLayoutInformation); + setDimensionsIfNotSpecified(viewWithLayoutInformation, viewWithoutLayoutInformation); Map elementViewMap = new HashMap<>(); Map elementMap = new HashMap<>(); @@ -74,6 +75,12 @@ private void setPaperSizeIfNotSpecified(@Nonnull View remoteView, @Nonnull View } } + private void setDimensionsIfNotSpecified(@Nonnull View remoteView, @Nonnull View localView) { + if (localView.getDimensions() == null) { + localView.setDimensions(remoteView.getDimensions()); + } + } + /** * Finds an element. Override this to change the behaviour. * diff --git a/structurizr-core/src/com/structurizr/view/Dimensions.java b/structurizr-core/src/com/structurizr/view/Dimensions.java new file mode 100644 index 000000000..a84a20ac3 --- /dev/null +++ b/structurizr-core/src/com/structurizr/view/Dimensions.java @@ -0,0 +1,32 @@ +package com.structurizr.view; + +public final class Dimensions { + + private int width; + private int height; + + Dimensions() { + } + + public Dimensions(int width, int height) { + this.width = width; + this.height = height; + } + + public int getWidth() { + return width; + } + + public void setWidth(int width) { + this.width = width; + } + + public int getHeight() { + return height; + } + + public void setHeight(int height) { + this.height = height; + } + +} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/view/View.java b/structurizr-core/src/com/structurizr/view/View.java index e72220604..1232ddbc5 100644 --- a/structurizr-core/src/com/structurizr/view/View.java +++ b/structurizr-core/src/com/structurizr/view/View.java @@ -25,6 +25,7 @@ public abstract class View { private String description = ""; private String key; private PaperSize paperSize = null; + private Dimensions dimensions = null; private AutomaticLayout automaticLayout = null; private String title; @@ -136,6 +137,14 @@ public void setPaperSize(PaperSize paperSize) { this.paperSize = paperSize; } + public Dimensions getDimensions() { + return dimensions; + } + + public void setDimensions(Dimensions dimensions) { + this.dimensions = dimensions; + } + /** * Gets the automatic layout settings for this view. * From c3b9f5c2cfb8d4136356c2296ec97a33493bb9d6 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 6 Jun 2021 17:05:48 +0100 Subject: [PATCH 241/717] Adds more testing of the dimensions functionality. --- .../src/com/structurizr/view/Dimensions.java | 12 +++++- .../com/structurizr/view/DimensionsTests.java | 39 +++++++++++++++++++ .../unit/com/structurizr/view/ViewTests.java | 5 +++ 3 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 structurizr-core/test/unit/com/structurizr/view/DimensionsTests.java diff --git a/structurizr-core/src/com/structurizr/view/Dimensions.java b/structurizr-core/src/com/structurizr/view/Dimensions.java index a84a20ac3..e5d9ba408 100644 --- a/structurizr-core/src/com/structurizr/view/Dimensions.java +++ b/structurizr-core/src/com/structurizr/view/Dimensions.java @@ -9,8 +9,8 @@ public final class Dimensions { } public Dimensions(int width, int height) { - this.width = width; - this.height = height; + setWidth(width); + setHeight(height); } public int getWidth() { @@ -18,6 +18,10 @@ public int getWidth() { } public void setWidth(int width) { + if (width < 0) { + throw new IllegalArgumentException("The width must be a positive integer."); + } + this.width = width; } @@ -26,6 +30,10 @@ public int getHeight() { } public void setHeight(int height) { + if (height < 0) { + throw new IllegalArgumentException("The height must be a positive integer."); + } + this.height = height; } diff --git a/structurizr-core/test/unit/com/structurizr/view/DimensionsTests.java b/structurizr-core/test/unit/com/structurizr/view/DimensionsTests.java new file mode 100644 index 000000000..fa2f7ff0b --- /dev/null +++ b/structurizr-core/test/unit/com/structurizr/view/DimensionsTests.java @@ -0,0 +1,39 @@ +package com.structurizr.view; + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class DimensionsTests { + + @Test + public void test_construction() { + Dimensions dimensions = new Dimensions(123, 456); + + assertEquals(123, dimensions.getWidth()); + assertEquals(456, dimensions.getHeight()); + } + + @Test + public void test_setWidth_ThrowsAnException_WhenANegativeIntegerIsSpecified() { + try { + Dimensions dimensions = new Dimensions(); + dimensions.setWidth(-100); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("The width must be a positive integer.", iae.getMessage()); + } + } + + @Test + public void test_setHeight_ThrowsAnException_WhenANegativeIntegerIsSpecified() { + try { + Dimensions dimensions = new Dimensions(); + dimensions.setHeight(-100); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("The height must be a positive integer.", iae.getMessage()); + } + } + +} \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/view/ViewTests.java b/structurizr-core/test/unit/com/structurizr/view/ViewTests.java index acb6941ac..e093e84ec 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ViewTests.java @@ -180,6 +180,8 @@ public void test_copyLayoutInformationFrom() { // create a view with SystemA and Person (locations are set for both, relationship has vertices) StaticView staticView1 = new SystemContextView(softwareSystem1A, "context", "Description"); + staticView1.setPaperSize(PaperSize.A3_Landscape); + staticView1.setDimensions(new Dimensions(123, 456)); staticView1.add(softwareSystem1B); staticView1.getElementView(softwareSystem1B).setX(123); staticView1.getElementView(softwareSystem1B).setY(321); @@ -226,6 +228,9 @@ public void test_copyLayoutInformationFrom() { dynamicView2.add(person2, "Overridden description", softwareSystem2A); staticView2.copyLayoutInformationFrom(staticView1); + assertEquals(PaperSize.A3_Landscape, staticView2.getPaperSize()); + assertEquals(123, staticView2.getDimensions().getWidth()); + assertEquals(456, staticView2.getDimensions().getHeight()); assertEquals(0, staticView2.getElementView(softwareSystem2A).getX()); assertEquals(0, staticView2.getElementView(softwareSystem2A).getY()); assertEquals(123, staticView2.getElementView(softwareSystem2B).getX()); From 4c7dac2a0c7fa795d05e7cfb7a3dbccf4dfa259d Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 7 Jun 2021 09:24:21 +0100 Subject: [PATCH 242/717] Updated to reflect release. --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 631dc2b54..a90975bf6 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 1.9.5 (unreleased) +## 1.9.5 (7th June 2021) - Provides a way to store view dimensions. From 64163277ca2d4269935b85b4e555e286b7771363 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 8 Jul 2021 07:24:36 +0100 Subject: [PATCH 243/717] Added validation logic to reject unsupported image data URIs. --- build.gradle | 2 +- docs/changelog.md | 4 +++ .../src/com/structurizr/util/ImageUtils.java | 29 +++++++++++++++++++ .../src/com/structurizr/view/Branding.java | 18 ++++++------ .../com/structurizr/view/ElementStyle.java | 13 ++++----- .../com/structurizr/util/ImageUtilsTests.java | 28 ++++++++++++++++-- 6 files changed, 74 insertions(+), 20 deletions(-) diff --git a/build.gradle b/build.gradle index 0a3f4fdaf..2b54ab9c4 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.9.5' + version = '1.9.6' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index a90975bf6..205c831ad 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.9.6 (unreleased) + +- Added validation logic to reject unsupported image data URIs. + ## 1.9.5 (7th June 2021) - Provides a way to store view dimensions. diff --git a/structurizr-core/src/com/structurizr/util/ImageUtils.java b/structurizr-core/src/com/structurizr/util/ImageUtils.java index 94c5598ea..7849f7fe6 100644 --- a/structurizr-core/src/com/structurizr/util/ImageUtils.java +++ b/structurizr-core/src/com/structurizr/util/ImageUtils.java @@ -69,4 +69,33 @@ public static String getImageAsDataUri(File file) throws IOException { return "data:" + contentType + ";base64," + base64Content; } + public static void validateImage(String url) { + if (StringUtils.isNullOrEmpty(url)) { + return; + } + + url = url.trim(); + + if (Url.isUrl(url)) { + // all good + return; + } + + if (url.startsWith("data:image")) { + if (ImageUtils.isSupportedDataUri(url)) { + // all good + return; + } else { + // it's a data URI, but not supported + throw new IllegalArgumentException("Only PNG and JPG data URIs are supported: " + url); + } + } + + throw new IllegalArgumentException("Expected a URL or data URI"); + } + + public static boolean isSupportedDataUri(String uri) { + return uri.startsWith("data:image/png;base64,") || uri.startsWith("data:image/jpeg;base64,"); + } + } \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/view/Branding.java b/structurizr-core/src/com/structurizr/view/Branding.java index 1fa041199..f0de5a11b 100644 --- a/structurizr-core/src/com/structurizr/view/Branding.java +++ b/structurizr-core/src/com/structurizr/view/Branding.java @@ -1,6 +1,7 @@ package com.structurizr.view; -import com.structurizr.util.Url; +import com.structurizr.util.ImageUtils; +import com.structurizr.util.StringUtils; /** * A wrapper for the font, logo and color scheme associated with a corporate branding. @@ -21,15 +22,14 @@ public String getLogo() { /** * Sets the URL of an image representing a logo. * - * @param url a URL as a String + * @param logo a URL or data URI as a String */ - public void setLogo(String url) { - if (url != null && url.trim().length() > 0) { - if (Url.isUrl(url) || url.startsWith("data:image/")) { - this.logo = url.trim(); - } else { - throw new IllegalArgumentException(url + " is not a valid URL."); - } + public void setLogo(String logo) { + if (StringUtils.isNullOrEmpty(logo)) { + this.logo = null; + } else { + ImageUtils.validateImage(logo); + this.logo = logo.trim(); } } diff --git a/structurizr-core/src/com/structurizr/view/ElementStyle.java b/structurizr-core/src/com/structurizr/view/ElementStyle.java index 2e420f542..78a638858 100644 --- a/structurizr-core/src/com/structurizr/view/ElementStyle.java +++ b/structurizr-core/src/com/structurizr/view/ElementStyle.java @@ -1,8 +1,8 @@ package com.structurizr.view; import com.fasterxml.jackson.annotation.JsonInclude; +import com.structurizr.util.ImageUtils; import com.structurizr.util.StringUtils; -import com.structurizr.util.Url; /** * A definition of an element style. @@ -232,12 +232,11 @@ public String getIcon() { } public void setIcon(String icon) { - if (icon != null && icon.trim().length() > 0) { - if (Url.isUrl(icon) || icon.startsWith("data:image/")) { - this.icon = icon.trim(); - } else { - throw new IllegalArgumentException(icon + " is not a valid URL."); - } + if (StringUtils.isNullOrEmpty(icon)) { + this.icon = null; + } else { + ImageUtils.validateImage(icon); + this.icon = icon.trim(); } } diff --git a/structurizr-core/test/unit/com/structurizr/util/ImageUtilsTests.java b/structurizr-core/test/unit/com/structurizr/util/ImageUtilsTests.java index f82a00b75..0d5ee691b 100644 --- a/structurizr-core/test/unit/com/structurizr/util/ImageUtilsTests.java +++ b/structurizr-core/test/unit/com/structurizr/util/ImageUtilsTests.java @@ -4,9 +4,7 @@ import java.io.File; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; public class ImageUtilsTests { @@ -155,4 +153,28 @@ public void test_getImageAsDataUri_ReturnsTheImageAsADataUri_WhenAFileIsSpecifie assertTrue(imageAsDataUri.startsWith("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMQAAADECAYAAADA")); // the actual base64 encoded string varies between Java 8 and 9 } + @Test + public void test_validateImage() { + // allowed + ImageUtils.validateImage("https://structurizr.com/image.png"); + ImageUtils.validateImage("data:image/png;base64,iVBORw0KGg"); + ImageUtils.validateImage("data:image/jpeg;base64,iVBORw0KGg"); + + //disallowed + try { + ImageUtils.validateImage("data:image/svg+xml;base64,iVBORw0KGg"); + fail(); + } catch (Exception e) { + assertEquals("Only PNG and JPG data URIs are supported: data:image/svg+xml;base64,iVBORw0KGg", e.getMessage()); + } + } + + @Test + public void test_isSupportedDataUri() { + assertTrue(ImageUtils.isSupportedDataUri("data:image/png;base64,iVBORw0KGg")); + assertTrue(ImageUtils.isSupportedDataUri("data:image/jpeg;base64,iVBORw0KGg")); + assertFalse(ImageUtils.isSupportedDataUri("data:image/svg+xml;base64,iVBORw0KGg")); + assertFalse(ImageUtils.isSupportedDataUri("hello world")); + } + } From 6cd4c3a906a25e644d305e1360519293606a9a77 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 27 Aug 2021 12:16:24 +0100 Subject: [PATCH 244/717] Fixes #166. --- .../src/com/structurizr/view/ViewSet.java | 19 ++++++++++++++++++- .../com/structurizr/view/ViewSetTests.java | 2 +- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/structurizr-core/src/com/structurizr/view/ViewSet.java b/structurizr-core/src/com/structurizr/view/ViewSet.java index 63fc68f65..d845be15c 100644 --- a/structurizr-core/src/com/structurizr/view/ViewSet.java +++ b/structurizr-core/src/com/structurizr/view/ViewSet.java @@ -795,6 +795,14 @@ public void createDefaultViews() { List softwareSystems = new ArrayList<>(); for (DeploymentNode deploymentNode : model.getDeploymentNodes()) { if (deploymentNode.getEnvironment().equals(deploymentEnvironment)) { + Set softwareSystemInstances = getSoftwareSystemInstances(deploymentNode); + for (SoftwareSystemInstance softwareSystemInstance : softwareSystemInstances) { + SoftwareSystem softwareSystem = softwareSystemInstance.getSoftwareSystem(); + if (!softwareSystems.contains(softwareSystem)) { + softwareSystems.add(softwareSystem); + } + } + Set containerInstances = getContainerInstances(deploymentNode); for (ContainerInstance containerInstance : containerInstances) { SoftwareSystem softwareSystem = containerInstance.getContainer().getSoftwareSystem(); @@ -832,6 +840,16 @@ private String removeNonWordCharacters(String name) { return name.replaceAll("\\W", ""); } + private Set getSoftwareSystemInstances(DeploymentNode deploymentNode) { + Set softwareSystemInstances = new HashSet<>(deploymentNode.getSoftwareSystemInstances()); + + for (DeploymentNode child : deploymentNode.getChildren()) { + softwareSystemInstances.addAll(getSoftwareSystemInstances(child)); + } + + return softwareSystemInstances; + } + private Set getContainerInstances(DeploymentNode deploymentNode) { Set containerInstances = new HashSet<>(deploymentNode.getContainerInstances()); @@ -842,7 +860,6 @@ private Set getContainerInstances(DeploymentNode deploymentNo return containerInstances; } - /** * Removes all views and configuration. */ diff --git a/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java b/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java index d7f905b6f..80d1ef59d 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java @@ -1058,7 +1058,7 @@ public void test_createDefaultViews() { assertEquals(1, views.getDeploymentViews().size()); assertSame("Live", views.getDeploymentViews().stream().filter(v -> v.getKey().equals("Live-Deployment")).findFirst().get().getEnvironment()); - dev.add(c1); + dev.add(ss1); liveEc2.add(c1); liveEc2.add(c2); From dcbdbe917556888567f9d830f2ace80e5b98a638 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 31 Aug 2021 14:28:08 +0100 Subject: [PATCH 245/717] Upgrade Jackson. --- structurizr-client/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structurizr-client/build.gradle b/structurizr-client/build.gradle index c2f300b40..de307d1dc 100644 --- a/structurizr-client/build.gradle +++ b/structurizr-client/build.gradle @@ -2,7 +2,7 @@ dependencies { compile project(':structurizr-core') - compile 'com.fasterxml.jackson.core:jackson-databind:2.10.2' + compile 'com.fasterxml.jackson.core:jackson-databind:2.10.5.1' compile 'org.apache.httpcomponents.client5:httpclient5:5.0' From d86914bac6cc8bce58c97d27fc39495b31bcb16a Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 31 Aug 2021 14:31:55 +0100 Subject: [PATCH 246/717] Updated changelog and release date. --- docs/changelog.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 205c831ad..2a47a6d75 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,8 +1,9 @@ # Changelog -## 1.9.6 (unreleased) +## 1.9.6 (31st August 2021) - Added validation logic to reject unsupported image data URIs. +- Fixes #166 (ContainerInstance/SoftwareSystemInstance and auto-generation of deployment diagram). ## 1.9.5 (7th June 2021) From 9c66bb387cf7bdf2e15a73a7b389c5d8f8c4316e Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 31 Aug 2021 14:37:38 +0100 Subject: [PATCH 247/717] Changed version numbers in docs. --- docs/binaries.md | 4 ++-- docs/getting-started.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/binaries.md b/docs/binaries.md index 0a60c7fbe..74ef946e0 100644 --- a/docs/binaries.md +++ b/docs/binaries.md @@ -3,5 +3,5 @@ The "Structurizr for Java" binaries are hosted on [Maven Central](https://repo1. Name | Description ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-core:1.9.2 | The core library that can used to create software architecture models. -com.structurizr:structurizr-client:1.9.2 | The API client for publishing models on the Structurizr cloud service and on-premises installation. \ No newline at end of file +com.structurizr:structurizr-core | The core library that can used to create software architecture models. +com.structurizr:structurizr-client | The API client for publishing models on the Structurizr cloud service and on-premises installation. \ No newline at end of file diff --git a/docs/getting-started.md b/docs/getting-started.md index 32da98e3e..6f9941701 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -12,7 +12,7 @@ The Structurizr for Java binaries are hosted on [Maven Central](https://repo1.ma Name | Description ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-client:1.9.2 | The Structurizr API client library. +com.structurizr:structurizr-client:1.9.6 | The Structurizr API client library. ## 2. Create a Java program From 17ef306473b08994c832ce73156ede73ed9a399a Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 8 Sep 2021 14:22:57 +0100 Subject: [PATCH 248/717] Adds support for software system/container instances in multiple deployment groups. --- build.gradle | 2 +- docs/changelog.md | 4 ++ .../structurizr/model/ContainerInstance.java | 4 +- .../com/structurizr/model/DeploymentNode.java | 12 ++-- .../src/com/structurizr/model/Model.java | 10 ++-- .../model/SoftwareSystemInstance.java | 4 +- .../model/StaticStructureElementInstance.java | 48 ++++++++++++++-- .../model/ContainerInstanceTests.java | 25 +++++++++ .../com/structurizr/model/ModelTests.java | 56 ++++++++++++++++--- .../model/SoftwareSystemInstanceTests.java | 25 +++++++++ 10 files changed, 160 insertions(+), 30 deletions(-) diff --git a/build.gradle b/build.gradle index 2b54ab9c4..d24e785d2 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.9.6' + version = '1.9.7' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 2a47a6d75..5d10231f8 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +### 1.9.7 (unreleased) + +- Adds support for software system/container instances in multiple deployment groups. + ## 1.9.6 (31st August 2021) - Added validation logic to reject unsupported image data URIs. diff --git a/structurizr-core/src/com/structurizr/model/ContainerInstance.java b/structurizr-core/src/com/structurizr/model/ContainerInstance.java index 140157aa2..e9cb7d4bb 100644 --- a/structurizr-core/src/com/structurizr/model/ContainerInstance.java +++ b/structurizr-core/src/com/structurizr/model/ContainerInstance.java @@ -13,8 +13,8 @@ public final class ContainerInstance extends StaticStructureElementInstance { ContainerInstance() { } - ContainerInstance(Container container, int instanceId, String environment, String deploymentGroup) { - super(instanceId, environment, deploymentGroup); + ContainerInstance(Container container, int instanceId, String environment, String... deploymentGroups) { + super(instanceId, environment, deploymentGroups); setContainer(container); addTags(Tags.CONTAINER_INSTANCE); diff --git a/structurizr-core/src/com/structurizr/model/DeploymentNode.java b/structurizr-core/src/com/structurizr/model/DeploymentNode.java index e4c5b6a1b..ff5f4e296 100644 --- a/structurizr-core/src/com/structurizr/model/DeploymentNode.java +++ b/structurizr-core/src/com/structurizr/model/DeploymentNode.java @@ -43,11 +43,11 @@ public SoftwareSystemInstance add(SoftwareSystem softwareSystem) { * Adds a software system instance to this deployment node, replicating relationships. * * @param softwareSystem the SoftwareSystem to add an instance of - * @param deploymentGroup the deployment group + * @param deploymentGroups the deployment group(s) * @return a SoftwareSystemInstance object */ - public SoftwareSystemInstance add(SoftwareSystem softwareSystem, String deploymentGroup) { - SoftwareSystemInstance softwareSystemInstance = getModel().addSoftwareSystemInstance(this, softwareSystem, deploymentGroup); + public SoftwareSystemInstance add(SoftwareSystem softwareSystem, String... deploymentGroups) { + SoftwareSystemInstance softwareSystemInstance = getModel().addSoftwareSystemInstance(this, softwareSystem, deploymentGroups); this.softwareSystemInstances.add(softwareSystemInstance); return softwareSystemInstance; @@ -67,11 +67,11 @@ public ContainerInstance add(Container container) { * Adds a container instance to this deployment node, optionally replicating relationships. * * @param container the Container to add an instance of - * @param deploymentGroup the deployment group + * @param deploymentGroups the deployment group(s) * @return a ContainerInstance object */ - public ContainerInstance add(Container container, String deploymentGroup) { - ContainerInstance containerInstance = getModel().addContainerInstance(this, container, deploymentGroup); + public ContainerInstance add(Container container, String... deploymentGroups) { + ContainerInstance containerInstance = getModel().addContainerInstance(this, container, deploymentGroups); this.containerInstances.add(containerInstance); return containerInstance; diff --git a/structurizr-core/src/com/structurizr/model/Model.java b/structurizr-core/src/com/structurizr/model/Model.java index 4d70f1fca..4a562edb9 100644 --- a/structurizr-core/src/com/structurizr/model/Model.java +++ b/structurizr-core/src/com/structurizr/model/Model.java @@ -982,14 +982,14 @@ public DeploymentNode getDeploymentNodeWithName(String name, String environment) return null; } - SoftwareSystemInstance addSoftwareSystemInstance(DeploymentNode deploymentNode, SoftwareSystem softwareSystem, String deploymentGroup) { + SoftwareSystemInstance addSoftwareSystemInstance(DeploymentNode deploymentNode, SoftwareSystem softwareSystem, String... deploymentGroups) { if (softwareSystem == null) { throw new IllegalArgumentException("A software system must be specified."); } long instanceNumber = deploymentNode.getSoftwareSystemInstances().stream().filter(ssi -> ssi.getSoftwareSystem().equals(softwareSystem)).count(); instanceNumber++; - SoftwareSystemInstance softwareSystemInstance = new SoftwareSystemInstance(softwareSystem, (int)instanceNumber, deploymentNode.getEnvironment(), deploymentGroup); + SoftwareSystemInstance softwareSystemInstance = new SoftwareSystemInstance(softwareSystem, (int)instanceNumber, deploymentNode.getEnvironment(), deploymentGroups); softwareSystemInstance.setParent(deploymentNode); softwareSystemInstance.setId(idGenerator.generateId(softwareSystemInstance)); @@ -1000,14 +1000,14 @@ SoftwareSystemInstance addSoftwareSystemInstance(DeploymentNode deploymentNode, return softwareSystemInstance; } - ContainerInstance addContainerInstance(DeploymentNode deploymentNode, Container container, String deploymentGroup) { + ContainerInstance addContainerInstance(DeploymentNode deploymentNode, Container container, String... deploymentGroups) { if (container == null) { throw new IllegalArgumentException("A container must be specified."); } long instanceNumber = deploymentNode.getContainerInstances().stream().filter(ci -> ci.getContainer().equals(container)).count(); instanceNumber++; - ContainerInstance containerInstance = new ContainerInstance(container, (int)instanceNumber, deploymentNode.getEnvironment(), deploymentGroup); + ContainerInstance containerInstance = new ContainerInstance(container, (int)instanceNumber, deploymentNode.getEnvironment(), deploymentGroups); containerInstance.setParent(deploymentNode); containerInstance.setId(idGenerator.generateId(containerInstance)); @@ -1026,7 +1026,7 @@ private void replicateElementRelationships(StaticStructureElementInstance elemen .filter(e -> e instanceof StaticStructureElementInstance) .map(e -> (StaticStructureElementInstance)e) .filter(ssei -> ssei.getEnvironment().equals(elementInstance.getEnvironment())) - .filter(ssei -> ssei.getDeploymentGroup().equals(elementInstance.getDeploymentGroup())) + .filter(ssei -> ssei.inSameDeploymentGroup(elementInstance)) .collect(Collectors.toSet()); // and replicate the relationships to/from the element instance diff --git a/structurizr-core/src/com/structurizr/model/SoftwareSystemInstance.java b/structurizr-core/src/com/structurizr/model/SoftwareSystemInstance.java index 1ec030668..1c60f085d 100644 --- a/structurizr-core/src/com/structurizr/model/SoftwareSystemInstance.java +++ b/structurizr-core/src/com/structurizr/model/SoftwareSystemInstance.java @@ -13,8 +13,8 @@ public final class SoftwareSystemInstance extends StaticStructureElementInstance SoftwareSystemInstance() { } - SoftwareSystemInstance(SoftwareSystem softwareSystem, int instanceId, String environment, String deploymentGroup) { - super(instanceId, environment, deploymentGroup); + SoftwareSystemInstance(SoftwareSystem softwareSystem, int instanceId, String environment, String... deploymentGroups) { + super(instanceId, environment, deploymentGroups); setSoftwareSystem(softwareSystem); addTags(Tags.SOFTWARE_SYSTEM_INSTANCE); diff --git a/structurizr-core/src/com/structurizr/model/StaticStructureElementInstance.java b/structurizr-core/src/com/structurizr/model/StaticStructureElementInstance.java index 98dfa51a3..2b713993c 100644 --- a/structurizr-core/src/com/structurizr/model/StaticStructureElementInstance.java +++ b/structurizr-core/src/com/structurizr/model/StaticStructureElementInstance.java @@ -1,9 +1,11 @@ package com.structurizr.model; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.structurizr.util.StringUtils; import com.structurizr.util.Url; import javax.annotation.Nonnull; +import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Set; @@ -16,17 +18,28 @@ public abstract class StaticStructureElementInstance extends DeploymentElement { private static final int DEFAULT_HEALTH_CHECK_INTERVAL_IN_SECONDS = 60; private static final long DEFAULT_HEALTH_CHECK_TIMEOUT_IN_MILLISECONDS = 0; - private String deploymentGroup = DEFAULT_DEPLOYMENT_GROUP; + private Set deploymentGroups = new HashSet<>(); private int instanceId; private Set healthChecks = new HashSet<>(); StaticStructureElementInstance() { } - StaticStructureElementInstance(int instanceId, String environment, String deploymentGroup) { + StaticStructureElementInstance(int instanceId, String environment, String... deploymentGroups) { setInstanceId(instanceId); setEnvironment(environment); - setDeploymentGroup(deploymentGroup); + + if (deploymentGroups != null) { + for (String deploymentGroup : deploymentGroups) { + if (!StringUtils.isNullOrEmpty(deploymentGroup)) { + this.deploymentGroups.add(deploymentGroup.trim()); + } + } + } + + if (this.deploymentGroups.isEmpty()) { + this.deploymentGroups.add(DEFAULT_DEPLOYMENT_GROUP); + } } @JsonIgnore @@ -37,12 +50,35 @@ public abstract class StaticStructureElementInstance extends DeploymentElement { * * @return a deployment group name */ - public String getDeploymentGroup() { - return deploymentGroup; + public Set getDeploymentGroups() { + if (deploymentGroups.isEmpty()) { + return Collections.singleton(DEFAULT_DEPLOYMENT_GROUP); + } else { + return new HashSet<>(deploymentGroups); + } } + void setDeploymentGroups(Set deploymentGroups) { + if (deploymentGroups != null) { + this.deploymentGroups = new HashSet<>(deploymentGroups); + } else { + this.deploymentGroups = new HashSet<>(); + } + } + + // provided for backwards compatibility void setDeploymentGroup(String deploymentGroup) { - this.deploymentGroup = deploymentGroup; + this.deploymentGroups = Collections.singleton(deploymentGroup); + } + + boolean inSameDeploymentGroup(StaticStructureElementInstance ssei) { + for (String deploymentGroup : getDeploymentGroups()) { + if (ssei.getDeploymentGroups().contains(deploymentGroup)) { + return true; + } + } + + return false; } /** diff --git a/structurizr-core/test/unit/com/structurizr/model/ContainerInstanceTests.java b/structurizr-core/test/unit/com/structurizr/model/ContainerInstanceTests.java index db2abba3e..313ac51e4 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ContainerInstanceTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ContainerInstanceTests.java @@ -81,6 +81,31 @@ public void test_removeTags_DoesNotRemoveAnyTags() { assertTrue(instance.getTags().contains(Tags.CONTAINER_INSTANCE)); } + @Test + public void test_getDeploymentGroups_WhenNoGroupsHaveBeenSpecified() { + ContainerInstance instance = deploymentNode.add(database); + + assertEquals(1, instance.getDeploymentGroups().size()); + assertTrue(instance.getDeploymentGroups().contains("Default")); + } + + @Test + public void test_getDeploymentGroups_WhenOneGroupHasBeenSpecified() { + ContainerInstance instance = deploymentNode.add(database, "Group 1"); + + assertEquals(1, instance.getDeploymentGroups().size()); + assertTrue(instance.getDeploymentGroups().contains("Group 1")); + } + + @Test + public void test_getDeploymentGroups_WhenMultipleGroupsAreSpecified() { + ContainerInstance instance = deploymentNode.add(database, "Group 1", "Group 2"); + + assertEquals(2, instance.getDeploymentGroups().size()); + assertTrue(instance.getDeploymentGroups().contains("Group 1")); + assertTrue(instance.getDeploymentGroups().contains("Group 2")); + } + @Test public void test_addHealthCheck() { ContainerInstance instance = deploymentNode.add(database); diff --git a/structurizr-core/test/unit/com/structurizr/model/ModelTests.java b/structurizr-core/test/unit/com/structurizr/model/ModelTests.java index f703c7fea..ebe1700b9 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ModelTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ModelTests.java @@ -721,20 +721,20 @@ public void test_addElementInstance_AddsElementInstancesAndReplicatesRelationshi @Test public void test_addElementInstance_AddsElementInstancesAndReplicatesRelationshipsWithinTheDeploymentEnvironmentAndSpecifiedGroup() { - // in this test, container instances are added to two deployment groups: "Service 1" and "Service 2" + // in this test, container instances are added to two deployment groups: "Instance 1" and "Instance 2" // relationships are not replicated between element instances in other groups - SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System"); - Container api = softwareSystem1.addContainer("API"); - Container database = softwareSystem1.addContainer("Database"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + Container api = softwareSystem.addContainer("API"); + Container database = softwareSystem.addContainer("Database"); api.uses(database, "Uses"); DeploymentNode liveDeploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); - ContainerInstance apiInstance1 = liveDeploymentNode.add(api, "Service 1"); - ContainerInstance databaseInstance1 = liveDeploymentNode.add(database, "Service 1"); + ContainerInstance apiInstance1 = liveDeploymentNode.add(api, "Instance 1"); + ContainerInstance databaseInstance1 = liveDeploymentNode.add(database, "Instance 1"); - ContainerInstance apiInstance2 = liveDeploymentNode.add(api, "Service 2"); - ContainerInstance databaseInstance2 = liveDeploymentNode.add(database, "Service 2"); + ContainerInstance apiInstance2 = liveDeploymentNode.add(api, "Instance 2"); + ContainerInstance databaseInstance2 = liveDeploymentNode.add(database, "Instance 2"); assertEquals(1, apiInstance1.getRelationships().size()); assertEquals(1, apiInstance2.getRelationships().size()); @@ -748,6 +748,46 @@ public void test_addElementInstance_AddsElementInstancesAndReplicatesRelationshi assertEquals("Uses", relationship.getDescription()); } + @Test + public void test_addElementInstance_AddsElementInstancesAndReplicatesRelationshipsWithinTheDeploymentEnvironmentAndSpecifiedGroups() { + // in this test: + // - API container instances are added to "Instance 1", "Instance 2" and "Shared" + // - database container instances are added to "Instance 1" and "Instance 2" + // - a shared container is added to "Shared" + + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + Container api = softwareSystem.addContainer("API"); + Container database = softwareSystem.addContainer("Database"); + Container cache = softwareSystem.addContainer("Cache"); + api.uses(database, "Uses"); + api.uses(cache, "Uses"); + + DeploymentNode liveDeploymentNode = model.addDeploymentNode("Live"); + ContainerInstance apiInstance1 = liveDeploymentNode.add(api, "Instance 1", "Shared"); + ContainerInstance databaseInstance1 = liveDeploymentNode.add(database, "Instance 1"); + + ContainerInstance apiInstance2 = liveDeploymentNode.add(api, "Instance 2", "Shared"); + ContainerInstance databaseInstance2 = liveDeploymentNode.add(database, "Instance 2"); + + ContainerInstance cacheInstance = liveDeploymentNode.add(cache, "Shared"); + + assertEquals(2, apiInstance1.getRelationships().size()); + assertTrue(apiInstance1.hasEfferentRelationshipWith(databaseInstance1)); + assertTrue(apiInstance1.hasEfferentRelationshipWith(cacheInstance)); + + assertEquals(2, apiInstance2.getRelationships().size()); + assertTrue(apiInstance2.hasEfferentRelationshipWith(databaseInstance2)); + assertTrue(apiInstance2.hasEfferentRelationshipWith(cacheInstance)); + + // apiInstance1 -> databaseInstance1 + Relationship relationship = apiInstance1.getEfferentRelationshipWith(databaseInstance1); + assertEquals("Uses", relationship.getDescription()); + + // apiInstance2 -> databaseInstance2 + relationship = apiInstance2.getEfferentRelationshipWith(databaseInstance2); + assertEquals("Uses", relationship.getDescription()); + } + @Test public void test_getElement_ThrowsAnException_WhenANullIdIsSpecified() { try { diff --git a/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemInstanceTests.java b/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemInstanceTests.java index 0bebfe374..b791fc11d 100644 --- a/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemInstanceTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemInstanceTests.java @@ -177,4 +177,29 @@ public void test_addHealthCheck_ThrowsAnException_WhenTheTimeoutIsLessThanZero() } } + @Test + public void test_getDeploymentGroups_WhenNoGroupsHaveBeenSpecified() { + SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); + + assertEquals(1, instance.getDeploymentGroups().size()); + assertTrue(instance.getDeploymentGroups().contains("Default")); + } + + @Test + public void test_getDeploymentGroups_WhenOneGroupHasBeenSpecified() { + SoftwareSystemInstance instance = deploymentNode.add(softwareSystem, "Group 1"); + + assertEquals(1, instance.getDeploymentGroups().size()); + assertTrue(instance.getDeploymentGroups().contains("Group 1")); + } + + @Test + public void test_getDeploymentGroups_WhenMultipleGroupsAreSpecified() { + SoftwareSystemInstance instance = deploymentNode.add(softwareSystem, "Group 1", "Group 2"); + + assertEquals(2, instance.getDeploymentGroups().size()); + assertTrue(instance.getDeploymentGroups().contains("Group 1")); + assertTrue(instance.getDeploymentGroups().contains("Group 2")); + } + } \ No newline at end of file From 8f5abb657e39bf78a4a89043ab02baad4d478e6e Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 9 Sep 2021 12:37:59 +0100 Subject: [PATCH 249/717] Updated release date. --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 5d10231f8..90280e327 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -### 1.9.7 (unreleased) +### 1.9.7 (9th September 2021) - Adds support for software system/container instances in multiple deployment groups. From ad1c19cbaad618c63d249476ac95ca5276f8dc01 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 30 Sep 2021 14:10:35 +0100 Subject: [PATCH 250/717] Adds support for relationships from deployment nodes to infrastructure nodes (https://github.com/structurizr/dsl/issues/81). --- build.gradle | 2 +- docs/changelog.md | 6 ++- .../com/structurizr/model/DeploymentNode.java | 39 +++++++++++++++++++ 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index d24e785d2..f34af6a50 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.9.7' + version = '1.9.8' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 90280e327..eebc7401b 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,10 @@ # Changelog -### 1.9.7 (9th September 2021) +## 1.9.8 (unreleased) + +- Adds support for relationships from deployment nodes to infrastructure nodes. + +## 1.9.7 (9th September 2021) - Adds support for software system/container instances in multiple deployment groups. diff --git a/structurizr-core/src/com/structurizr/model/DeploymentNode.java b/structurizr-core/src/com/structurizr/model/DeploymentNode.java index ff5f4e296..148433d58 100644 --- a/structurizr-core/src/com/structurizr/model/DeploymentNode.java +++ b/structurizr-core/src/com/structurizr/model/DeploymentNode.java @@ -248,6 +248,45 @@ public Relationship uses(DeploymentNode destination, String description, String return getModel().addRelationship(this, destination, description, technology, interactionStyle, tags); } + /** + * Adds a relationship between this deployment node and an infrastructure node. + * + * @param destination the destination InfrastructureNode + * @param description a short description of the relationship + * @param technology the technology + * @return a Relationship object + */ + public Relationship uses(InfrastructureNode destination, String description, String technology) { + return uses(destination, description, technology, null); + } + + /** + * Adds a relationship between this deployment node and an infrastructure node. + * + * @param destination the destination InfrastructureNode + * @param description a short description of the relationship + * @param technology the technology + * @param interactionStyle the interaction style (Synchronous vs Asynchronous) + * @return a Relationship object + */ + public Relationship uses(InfrastructureNode destination, String description, String technology, InteractionStyle interactionStyle) { + return uses(destination, description, technology, interactionStyle, new String[0]); + } + + /** + * Adds a relationship between this deployment node and an infrastructure node. + * + * @param destination the destination InfrastructureNode + * @param description a short description of the relationship + * @param technology the technology + * @param interactionStyle the interaction style (Synchronous vs Asynchronous) + * @param tags an array of tags + * @return a Relationship object + */ + public Relationship uses(InfrastructureNode destination, String description, String technology, InteractionStyle interactionStyle, String[] tags) { + return getModel().addRelationship(this, destination, description, technology, interactionStyle, tags); + } + /** * Gets the set of child deployment nodes. * From 4623b1c81f9e59f8d70dbbcc9c3fc5b537de3a51 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 30 Sep 2021 14:11:27 +0100 Subject: [PATCH 251/717] Adds support to explicitly set the "last modified" username. --- .../structurizr/api/StructurizrClient.java | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/structurizr-client/src/com/structurizr/api/StructurizrClient.java b/structurizr-client/src/com/structurizr/api/StructurizrClient.java index b24ebbe96..c5f6da1a1 100644 --- a/structurizr-client/src/com/structurizr/api/StructurizrClient.java +++ b/structurizr-client/src/com/structurizr/api/StructurizrClient.java @@ -58,6 +58,7 @@ public final class StructurizrClient { private static final String WORKSPACE_PATH = "/workspace/"; private String agent = STRUCTURIZR_FOR_JAVA_AGENT; + private String user; private String url; private String apiKey; @@ -482,20 +483,28 @@ private String createArchiveFileName(long workspaceId) { return "structurizr-" + workspaceId + "-" + sdf.format(new Date()) + ".json"; } - private String getUser() { - String username = System.getProperty("user.name"); + public void setUser(String user) { + this.user = user; + } - if (username.contains("@")) { - return username; + private String getUser() { + if (!StringUtils.isNullOrEmpty(user)) { + return user; } else { - String hostname = null; - try { - hostname = InetAddress.getLocalHost().getHostName(); - } catch (UnknownHostException uhe) { - // ignore - } + String username = System.getProperty("user.name"); - return username + (!StringUtils.isNullOrEmpty(hostname) ? "@" + hostname : ""); + if (username.contains("@")) { + return username; + } else { + String hostname = null; + try { + hostname = InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException uhe) { + // ignore + } + + return username + (!StringUtils.isNullOrEmpty(hostname) ? "@" + hostname : ""); + } } } From 6ab224c0c59e6df3ddaccaceee2d8ae6f4fe0d17 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 1 Oct 2021 07:50:09 +0100 Subject: [PATCH 252/717] Added some methods to add all custom elements to a view. --- .../src/com/structurizr/view/CustomView.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/structurizr-core/src/com/structurizr/view/CustomView.java b/structurizr-core/src/com/structurizr/view/CustomView.java index ede9b6f4c..025a37b25 100644 --- a/structurizr-core/src/com/structurizr/view/CustomView.java +++ b/structurizr-core/src/com/structurizr/view/CustomView.java @@ -136,4 +136,24 @@ void setAnimations(List animations) { } } + /** + * Adds the default set of elements to this view. + */ + public void addDefaultElements() { + addAllCustomElements(); + } + + /** + * Adds all custom elements to this view. + */ + public void addAllCustomElements() { + getModel().getCustomElements().forEach(ce -> { + try { + add(ce); + } catch (ElementNotPermittedInViewException e) { + // ignore + } + }); + } + } \ No newline at end of file From 9f342d0baea06763f10b66e87d4468678b7b9863 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 1 Oct 2021 16:08:09 +0100 Subject: [PATCH 253/717] Updated to reflect release. --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index eebc7401b..21ac4b571 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 1.9.8 (unreleased) +## 1.9.8 (1st October 2021) - Adds support for relationships from deployment nodes to infrastructure nodes. From c03775c01497190fc3f867e0ea33a9dd543c5d3d Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 7 Oct 2021 08:09:07 +0100 Subject: [PATCH 254/717] - Adds the implied relationships functionality for custom elements. --- build.gradle | 2 +- docs/changelog.md | 4 ++++ structurizr-core/src/com/structurizr/model/Model.java | 4 ++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index f34af6a50..e1d6eb076 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.9.8' + version = '1.9.9' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 21ac4b571..7d70e3ea5 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.9.9 (unreleased) + +- Adds the implied relationships functionality for custom elements. + ## 1.9.8 (1st October 2021) - Adds support for relationships from deployment nodes to infrastructure nodes. diff --git a/structurizr-core/src/com/structurizr/model/Model.java b/structurizr-core/src/com/structurizr/model/Model.java index 4a562edb9..996bd821f 100644 --- a/structurizr-core/src/com/structurizr/model/Model.java +++ b/structurizr-core/src/com/structurizr/model/Model.java @@ -269,8 +269,8 @@ Relationship addRelationship(Element source, @Nonnull Element destination, Strin if (createImpliedRelationships) { if ( - (source instanceof Person || source instanceof SoftwareSystem || source instanceof Container || source instanceof Component) && - (destination instanceof Person || destination instanceof SoftwareSystem || destination instanceof Container || destination instanceof Component) + (source instanceof CustomElement || source instanceof Person || source instanceof SoftwareSystem || source instanceof Container || source instanceof Component) && + (destination instanceof CustomElement || destination instanceof Person || destination instanceof SoftwareSystem || destination instanceof Container || destination instanceof Component) ) { impliedRelationshipsStrategy.createImpliedRelationships(relationship); } From ee764027576410905f8c3935eaea712a3d6d37cf Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 7 Oct 2021 08:51:42 +0100 Subject: [PATCH 255/717] Adds a warning when pulling a client-side workspace without an encryption passphrase set. --- .../src/com/structurizr/api/StructurizrClient.java | 4 ++++ structurizr-core/src/com/structurizr/Workspace.java | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/structurizr-client/src/com/structurizr/api/StructurizrClient.java b/structurizr-client/src/com/structurizr/api/StructurizrClient.java index c5f6da1a1..1368170b6 100644 --- a/structurizr-client/src/com/structurizr/api/StructurizrClient.java +++ b/structurizr-client/src/com/structurizr/api/StructurizrClient.java @@ -323,6 +323,10 @@ public Workspace getWorkspace(long workspaceId) throws StructurizrClientExceptio archiveWorkspace(workspaceId, json); if (encryptionStrategy == null) { + if (json.contains("\"encryptionStrategy\"") && json.contains("\"ciphertext\"")) { + log.warn("The JSON may contain a client-side encrypted workspace, but no passphrase has been specified."); + } + JsonReader jsonReader = new JsonReader(); jsonReader.setIdGenerator(idGenerator); return jsonReader.read(new StringReader(json)); diff --git a/structurizr-core/src/com/structurizr/Workspace.java b/structurizr-core/src/com/structurizr/Workspace.java index cc08e904f..9f4150235 100644 --- a/structurizr-core/src/com/structurizr/Workspace.java +++ b/structurizr-core/src/com/structurizr/Workspace.java @@ -22,7 +22,7 @@ public final class Workspace extends AbstractWorkspace { private static final Log log = LogFactory.getLog(Workspace.class); - private Model model; + private Model model = createModel(); private ViewSet viewSet; private Documentation documentation; From 55affefffdc0e935205c63fcbd3374aaade34aaa Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 10 Oct 2021 08:21:06 +0100 Subject: [PATCH 256/717] "addDefaultElements" will now also add any connected custom elements. --- docs/changelog.md | 1 + .../com/structurizr/view/ComponentView.java | 1 + .../com/structurizr/view/ContainerView.java | 1 + .../com/structurizr/view/DeploymentView.java | 2 ++ .../src/com/structurizr/view/StaticView.java | 33 ----------------- .../structurizr/view/SystemContextView.java | 1 + .../structurizr/view/SystemLandscapeView.java | 2 ++ .../src/com/structurizr/view/View.java | 35 +++++++++++++++++-- 8 files changed, 41 insertions(+), 35 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 7d70e3ea5..4b49599c1 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -3,6 +3,7 @@ ## 1.9.9 (unreleased) - Adds the implied relationships functionality for custom elements. +- "addDefaultElements" will now also add any connected custom elements. ## 1.9.8 (1st October 2021) diff --git a/structurizr-core/src/com/structurizr/view/ComponentView.java b/structurizr-core/src/com/structurizr/view/ComponentView.java index 8707f5ca3..4dcf9a35c 100644 --- a/structurizr-core/src/com/structurizr/view/ComponentView.java +++ b/structurizr-core/src/com/structurizr/view/ComponentView.java @@ -165,6 +165,7 @@ public void addDefaultElements() { } }; + addNearestNeighbours(component, CustomElement.class); addNearestNeighbours(component, Person.class); addNearestNeighbours(component, SoftwareSystem.class); } diff --git a/structurizr-core/src/com/structurizr/view/ContainerView.java b/structurizr-core/src/com/structurizr/view/ContainerView.java index 2bfdadc9b..8c8cad219 100644 --- a/structurizr-core/src/com/structurizr/view/ContainerView.java +++ b/structurizr-core/src/com/structurizr/view/ContainerView.java @@ -87,6 +87,7 @@ public String getName() { public void addDefaultElements() { for (Container container : getSoftwareSystem().getContainers()) { add(container); + addNearestNeighbours(container, CustomElement.class); addNearestNeighbours(container, Person.class); addNearestNeighbours(container, SoftwareSystem.class); } diff --git a/structurizr-core/src/com/structurizr/view/DeploymentView.java b/structurizr-core/src/com/structurizr/view/DeploymentView.java index b1669cbed..425c38829 100644 --- a/structurizr-core/src/com/structurizr/view/DeploymentView.java +++ b/structurizr-core/src/com/structurizr/view/DeploymentView.java @@ -48,6 +48,8 @@ void setModel(Model model) { */ public void addDefaultElements() { addAllDeploymentNodes(); + + getElements().stream().map(ElementView::getElement).forEach(e -> addNearestNeighbours(e, CustomElement.class)); } /** diff --git a/structurizr-core/src/com/structurizr/view/StaticView.java b/structurizr-core/src/com/structurizr/view/StaticView.java index 980f3e5c9..dd4947dae 100644 --- a/structurizr-core/src/com/structurizr/view/StaticView.java +++ b/structurizr-core/src/com/structurizr/view/StaticView.java @@ -128,39 +128,6 @@ public RelationshipView add(@Nonnull Relationship relationship) { */ public abstract void addNearestNeighbours(@Nonnull Element element); - protected void addNearestNeighbours(Element element, Class typeOfElement) { - if (element == null) { - return; - } - - try { - addElement(element, true); - - Set relationships = getModel().getRelationships(); - relationships.stream().filter(r -> r.getSource().equals(element) && typeOfElement.isInstance(r.getDestination())) - .map(Relationship::getDestination) - .forEach(d -> { - try { - addElement(d, true); - } catch (ElementNotPermittedInViewException e) { - System.out.println(e.getMessage() + " (ignoring " + d.getName() + ")"); - } - }); - - relationships.stream().filter(r -> r.getDestination().equals(element) && typeOfElement.isInstance(r.getSource())) - .map(Relationship::getSource) - .forEach(s -> { - try { - addElement(s, true); - } catch (ElementNotPermittedInViewException e) { - System.out.println(e.getMessage() + " (ignoring " + s.getName() + ")"); - } - }); - } catch (ElementNotPermittedInViewException e) { - System.out.println(e.getMessage() + " (ignoring " + element.getName() + ")"); - } - } - /** * Removes all elements that cannot be reached by traversing the graph of relationships * starting with the specified element. diff --git a/structurizr-core/src/com/structurizr/view/SystemContextView.java b/structurizr-core/src/com/structurizr/view/SystemContextView.java index b2db64da6..7d4ef11a2 100644 --- a/structurizr-core/src/com/structurizr/view/SystemContextView.java +++ b/structurizr-core/src/com/structurizr/view/SystemContextView.java @@ -39,6 +39,7 @@ public String getName() { */ @Override public void addDefaultElements() { + addNearestNeighbours(getSoftwareSystem(), CustomElement.class); addNearestNeighbours(getSoftwareSystem(), Person.class); addNearestNeighbours(getSoftwareSystem(), SoftwareSystem.class); } diff --git a/structurizr-core/src/com/structurizr/view/SystemLandscapeView.java b/structurizr-core/src/com/structurizr/view/SystemLandscapeView.java index 72ac6edd8..4cfbcd23d 100644 --- a/structurizr-core/src/com/structurizr/view/SystemLandscapeView.java +++ b/structurizr-core/src/com/structurizr/view/SystemLandscapeView.java @@ -57,6 +57,8 @@ void setModel(Model model) { public void addDefaultElements() { addAllSoftwareSystems(); addAllPeople(); + + getElements().stream().map(ElementView::getElement).forEach(e -> addNearestNeighbours(e, CustomElement.class)); } /** diff --git a/structurizr-core/src/com/structurizr/view/View.java b/structurizr-core/src/com/structurizr/view/View.java index 1232ddbc5..e1b1dbc57 100644 --- a/structurizr-core/src/com/structurizr/view/View.java +++ b/structurizr-core/src/com/structurizr/view/View.java @@ -495,6 +495,39 @@ public void add(@Nonnull CustomElement customElement, boolean addRelationships) addElement(customElement, addRelationships); } + protected void addNearestNeighbours(Element element, Class typeOfElement) { + if (element == null) { + return; + } + + try { + addElement(element, true); + + Set relationships = getModel().getRelationships(); + relationships.stream().filter(r -> r.getSource().equals(element) && typeOfElement.isInstance(r.getDestination())) + .map(Relationship::getDestination) + .forEach(d -> { + try { + addElement(d, true); + } catch (ElementNotPermittedInViewException e) { + System.out.println(e.getMessage() + " (ignoring " + d.getName() + ")"); + } + }); + + relationships.stream().filter(r -> r.getDestination().equals(element) && typeOfElement.isInstance(r.getSource())) + .map(Relationship::getSource) + .forEach(s -> { + try { + addElement(s, true); + } catch (ElementNotPermittedInViewException e) { + System.out.println(e.getMessage() + " (ignoring " + s.getName() + ")"); + } + }); + } catch (ElementNotPermittedInViewException e) { + System.out.println(e.getMessage() + " (ignoring " + element.getName() + ")"); + } + } + /** * Removes the given custom element from this view. * @@ -504,6 +537,4 @@ public void remove(@Nonnull CustomElement customElement) { removeElement(customElement); } - - } \ No newline at end of file From 061959ab770506186bdf3c9d48841d78edeeae03 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 11 Oct 2021 07:46:47 +0100 Subject: [PATCH 257/717] Adds some tests for including custom elements via the addDefaultElements methods. --- .../com/structurizr/view/ComponentViewTests.java | 16 ++++++++++++++++ .../com/structurizr/view/ContainerViewTests.java | 14 ++++++++++++++ .../structurizr/view/SystemContextViewTests.java | 12 ++++++++++++ .../view/SystemLandscapeViewTests.java | 11 +++++++++++ 4 files changed, 53 insertions(+) diff --git a/structurizr-core/test/unit/com/structurizr/view/ComponentViewTests.java b/structurizr-core/test/unit/com/structurizr/view/ComponentViewTests.java index 34f5aa5f8..0527ad3e4 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ComponentViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ComponentViewTests.java @@ -571,6 +571,7 @@ public void test_addExternalDependencies_AddsTheParentSoftwareSystem_WhenACompon public void test_addDefaultElements() { model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); + CustomElement element = model.addCustomElement("Custom"); Person user1 = model.addPerson("User 1"); Person user2 = model.addPerson("User 2"); SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1"); @@ -588,6 +589,21 @@ public void test_addDefaultElements() { view.addDefaultElements(); assertEquals(3, view.getElements().size()); + assertFalse(view.getElements().contains(new ElementView(element))); + assertTrue(view.getElements().contains(new ElementView(user1))); + assertFalse(view.getElements().contains(new ElementView(user2))); + assertFalse(view.getElements().contains(new ElementView(softwareSystem1))); + assertTrue(view.getElements().contains(new ElementView(softwareSystem2))); + assertFalse(view.getElements().contains(new ElementView(container1))); + assertFalse(view.getElements().contains(new ElementView(container2))); + assertTrue(view.getElements().contains(new ElementView(component1))); + assertFalse(view.getElements().contains(new ElementView(component2))); + + element.uses(component1, "Uses"); + view.addDefaultElements(); + + assertEquals(4, view.getElements().size()); + assertTrue(view.getElements().contains(new ElementView(element))); assertTrue(view.getElements().contains(new ElementView(user1))); assertFalse(view.getElements().contains(new ElementView(user2))); assertFalse(view.getElements().contains(new ElementView(softwareSystem1))); diff --git a/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java b/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java index c9ec0b7b0..8f41c6886 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java @@ -273,6 +273,7 @@ public void test_addDependentSoftwareSystem2() { public void test_addDefaultElements() { model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); + CustomElement element = model.addCustomElement("Custom"); Person user1 = model.addPerson("User 1"); Person user2 = model.addPerson("User 2"); SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1"); @@ -288,6 +289,19 @@ public void test_addDefaultElements() { view.addDefaultElements(); assertEquals(3, view.getElements().size()); + assertFalse(view.getElements().contains(new ElementView(element))); + assertTrue(view.getElements().contains(new ElementView(user1))); + assertFalse(view.getElements().contains(new ElementView(user2))); + assertFalse(view.getElements().contains(new ElementView(softwareSystem1))); + assertTrue(view.getElements().contains(new ElementView(softwareSystem2))); + assertTrue(view.getElements().contains(new ElementView(container1))); + assertFalse(view.getElements().contains(new ElementView(container2))); + + element.uses(container1, "Uses"); + view.addDefaultElements(); + + assertEquals(4, view.getElements().size()); + assertTrue(view.getElements().contains(new ElementView(element))); assertTrue(view.getElements().contains(new ElementView(user1))); assertFalse(view.getElements().contains(new ElementView(user2))); assertFalse(view.getElements().contains(new ElementView(softwareSystem1))); diff --git a/structurizr-core/test/unit/com/structurizr/view/SystemContextViewTests.java b/structurizr-core/test/unit/com/structurizr/view/SystemContextViewTests.java index 9c6c221f5..61517f64b 100644 --- a/structurizr-core/test/unit/com/structurizr/view/SystemContextViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/SystemContextViewTests.java @@ -282,6 +282,7 @@ public void test_isEnterpriseBoundaryVisible() { @Test public void test_addDefaultElements() { + CustomElement element = model.addCustomElement("Custom"); Person user1 = model.addPerson("User 1"); Person user2 = model.addPerson("User 2"); SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1"); @@ -295,6 +296,17 @@ public void test_addDefaultElements() { view.addDefaultElements(); assertEquals(3, view.getElements().size()); + assertFalse(view.getElements().contains(new ElementView(element))); + assertTrue(view.getElements().contains(new ElementView(user1))); + assertFalse(view.getElements().contains(new ElementView(user2))); + assertTrue(view.getElements().contains(new ElementView(softwareSystem1))); + assertTrue(view.getElements().contains(new ElementView(softwareSystem2))); + + element.uses(softwareSystem1, "Uses"); + view.addDefaultElements(); + + assertEquals(4, view.getElements().size()); + assertTrue(view.getElements().contains(new ElementView(element))); assertTrue(view.getElements().contains(new ElementView(user1))); assertFalse(view.getElements().contains(new ElementView(user2))); assertTrue(view.getElements().contains(new ElementView(softwareSystem1))); diff --git a/structurizr-core/test/unit/com/structurizr/view/SystemLandscapeViewTests.java b/structurizr-core/test/unit/com/structurizr/view/SystemLandscapeViewTests.java index 207272634..833bda34d 100644 --- a/structurizr-core/test/unit/com/structurizr/view/SystemLandscapeViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/SystemLandscapeViewTests.java @@ -188,6 +188,7 @@ public void test_addNearestNeighbours_AddsNearestNeighbours_WhenThereAreSomeNear @Test public void test_addDefaultElements() { + CustomElement element = model.addCustomElement("Custom"); Person user = model.addPerson("User"); SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1"); SoftwareSystem softwareSystem2 = model.addSoftwareSystem("Software System 2"); @@ -195,6 +196,16 @@ public void test_addDefaultElements() { view.addDefaultElements(); assertEquals(3, view.getElements().size()); + assertFalse(view.getElements().contains(new ElementView(element))); + assertTrue(view.getElements().contains(new ElementView(user))); + assertTrue(view.getElements().contains(new ElementView(softwareSystem1))); + assertTrue(view.getElements().contains(new ElementView(softwareSystem2))); + + element.uses(softwareSystem1, "Uses"); + view.addDefaultElements(); + + assertEquals(4, view.getElements().size()); + assertTrue(view.getElements().contains(new ElementView(element))); assertTrue(view.getElements().contains(new ElementView(user))); assertTrue(view.getElements().contains(new ElementView(softwareSystem1))); assertTrue(view.getElements().contains(new ElementView(softwareSystem2))); From 1b401c21ca9fcbd01d9acc768765600665a73ee0 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 16 Oct 2021 11:26:09 +0100 Subject: [PATCH 258/717] Updated to reflect new release. --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 4b49599c1..dcb2297c8 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 1.9.9 (unreleased) +## 1.9.9 (16th October 2021) - Adds the implied relationships functionality for custom elements. - "addDefaultElements" will now also add any connected custom elements. From b5a5eaf0eed3c20cc831a9fbee12620c7a595f09 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 17 Nov 2021 11:33:57 -0500 Subject: [PATCH 259/717] Changes the visibility of some utility methods to find styles for a tag. --- build.gradle | 2 +- structurizr-core/src/com/structurizr/view/Styles.java | 4 ++-- .../test/unit/com/structurizr/view/StylesTests.java | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index e1d6eb076..8ee5810f5 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.9.9' + version = '1.9.10' repositories { mavenCentral() diff --git a/structurizr-core/src/com/structurizr/view/Styles.java b/structurizr-core/src/com/structurizr/view/Styles.java index de734dfc4..9d378f254 100644 --- a/structurizr-core/src/com/structurizr/view/Styles.java +++ b/structurizr-core/src/com/structurizr/view/Styles.java @@ -86,7 +86,7 @@ public RelationshipStyle addRelationshipStyle(String tag) { return relationshipStyle; } - private ElementStyle findElementStyle(String tag) { + public ElementStyle findElementStyle(String tag) { if (tag == null) { return null; } @@ -109,7 +109,7 @@ private ElementStyle findElementStyle(String tag) { return style; } - private RelationshipStyle findRelationshipStyle(String tag) { + public RelationshipStyle findRelationshipStyle(String tag) { if (tag == null) { return null; } diff --git a/structurizr-core/test/unit/com/structurizr/view/StylesTests.java b/structurizr-core/test/unit/com/structurizr/view/StylesTests.java index 758eee9f4..94dae1fac 100644 --- a/structurizr-core/test/unit/com/structurizr/view/StylesTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/StylesTests.java @@ -12,7 +12,7 @@ public class StylesTests extends AbstractWorkspaceTestBase { @Test public void test_findElementStyle_ReturnsTheDefaultStyle_WhenPassedNull() { - ElementStyle style = styles.findElementStyle(null); + ElementStyle style = styles.findElementStyle((Element)null); assertEquals(new Integer(450), style.getWidth()); assertEquals(new Integer(300), style.getHeight()); assertEquals("#dddddd", style.getBackground()); @@ -98,7 +98,7 @@ public void test_findElementStyle_ReturnsTheDefaultElementSize_WhenTheShapeIsAPe @Test public void test_findRelationshipStyle_ReturnsTheDefaultStyle_WhenPassedNull() { - RelationshipStyle style = styles.findRelationshipStyle(null); + RelationshipStyle style = styles.findRelationshipStyle((Relationship)null); assertEquals(new Integer(2), style.getThickness()); assertEquals("#707070", style.getColor()); assertTrue(style.getDashed()); From b42faa68fe3bc28627654652a4ac305036e8a45a Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 26 Nov 2021 11:48:31 +0000 Subject: [PATCH 260/717] Added some javadoc for the newly public methods. --- docs/changelog.md | 4 ++++ .../src/com/structurizr/view/Styles.java | 16 ++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index dcb2297c8..f82258b8d 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.9.10 (26th November 2021) + +- Promotes a couple of methods to be public; no functional changes. + ## 1.9.9 (16th October 2021) - Adds the implied relationships functionality for custom elements. diff --git a/structurizr-core/src/com/structurizr/view/Styles.java b/structurizr-core/src/com/structurizr/view/Styles.java index 9d378f254..8c1a1ff78 100644 --- a/structurizr-core/src/com/structurizr/view/Styles.java +++ b/structurizr-core/src/com/structurizr/view/Styles.java @@ -86,6 +86,14 @@ public RelationshipStyle addRelationshipStyle(String tag) { return relationshipStyle; } + /** + * Finds the element style for the given tag. This method creates an empty style, + * and copies properties from any element styles (from the workspace and any themes) for the given tag. + * + * + * @param tag the tag (a String) + * @return an ElementStyle instance + */ public ElementStyle findElementStyle(String tag) { if (tag == null) { return null; @@ -109,6 +117,14 @@ public ElementStyle findElementStyle(String tag) { return style; } + /** + * Finds the relationship style for the given tag. This method creates an empty style, + * and copies properties from any relationship styles (from the workspace and any themes) for the given tag. + * + * + * @param tag the tag (a String) + * @return a RelationshipStyle instance + */ public RelationshipStyle findRelationshipStyle(String tag) { if (tag == null) { return null; From 20c5bcc0a71b3b0e7fca982f0602c0248b5c760d Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 28 Nov 2021 08:34:30 +0000 Subject: [PATCH 261/717] Adds support to close #169 (ability to reset URL to null). --- .../src/com/structurizr/model/ModelItem.java | 12 ++++++------ .../unit/com/structurizr/model/ElementTests.java | 6 ++++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/structurizr-core/src/com/structurizr/model/ModelItem.java b/structurizr-core/src/com/structurizr/model/ModelItem.java index 874dfb4d4..b8e9b115f 100644 --- a/structurizr-core/src/com/structurizr/model/ModelItem.java +++ b/structurizr-core/src/com/structurizr/model/ModelItem.java @@ -139,12 +139,12 @@ public String getUrl() { * @throws IllegalArgumentException if the URL is not a well-formed URL */ public void setUrl(String url) { - if (url != null && url.trim().length() > 0) { - if (Url.isUrl(url)) { - this.url = url; - } else { - throw new IllegalArgumentException(url + " is not a valid URL."); - } + if (StringUtils.isNullOrEmpty(url)) { + this.url = null; + } else if (Url.isUrl(url)) { + this.url = url; + } else { + throw new IllegalArgumentException(url + " is not a valid URL."); } } diff --git a/structurizr-core/test/unit/com/structurizr/model/ElementTests.java b/structurizr-core/test/unit/com/structurizr/model/ElementTests.java index 0650a78bb..cf2b8b212 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ElementTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ElementTests.java @@ -269,15 +269,17 @@ public void test_setUrl_ThrowsAnIllegalArgumentException_WhenAnInvalidUrlIsSpeci } @Test - public void test_setUrl_DoesNothing_WhenANullUrlIsSpecified() { + public void test_setUrl_ResetsTheUrl_WhenANullUrlIsSpecified() { Element element = model.addSoftwareSystem("Name", "Description"); + element.setUrl("https://structurizr.com"); element.setUrl(null); assertNull(element.getUrl()); } @Test - public void test_setUrl_DoesNothing_WhenAnEmptyUrlIsSpecified() { + public void test_setUrl_ResetsTheUrl_WhenAnEmptyUrlIsSpecified() { Element element = model.addSoftwareSystem("Name", "Description"); + element.setUrl("https://structurizr.com"); element.setUrl(" "); assertNull(element.getUrl()); } From 44fb487510954b0dbcd044575f90b38187aa7ceb Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 28 Nov 2021 08:36:00 +0000 Subject: [PATCH 262/717] Adds support to close #169 (ability to reset URL to null). --- .../test/unit/com/structurizr/model/RelationshipTests.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/structurizr-core/test/unit/com/structurizr/model/RelationshipTests.java b/structurizr-core/test/unit/com/structurizr/model/RelationshipTests.java index e1ec02a3d..da5fba715 100644 --- a/structurizr-core/test/unit/com/structurizr/model/RelationshipTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/RelationshipTests.java @@ -96,15 +96,17 @@ public void test_setUrl_ThrowsAnIllegalArgumentException_WhenAnInvalidUrlIsSpeci } @Test - public void test_setUrl_DoesNothing_WhenANullUrlIsSpecified() { + public void test_setUrl_ResetsTheUrl_WhenANullUrlIsSpecified() { Relationship relationship = softwareSystem1.uses(softwareSystem2, "Uses 1", "Technology"); + relationship.setUrl("https://structurizr.com"); relationship.setUrl(null); assertNull(relationship.getUrl()); } @Test - public void test_setUrl_DoesNothing_WhenAnEmptyUrlIsSpecified() { + public void test_setUrl_ResetsTheUrl_WhenAnEmptyUrlIsSpecified() { Relationship relationship = softwareSystem1.uses(softwareSystem2, "Uses 1", "Technology"); + relationship.setUrl("https://structurizr.com"); relationship.setUrl(" "); assertNull(relationship.getUrl()); } From c16c80db25116ab9150259ff5aac4553155fb322 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 17 Dec 2021 09:40:44 +0000 Subject: [PATCH 263/717] Adds the ability to indicate that individual views should not merge layout from remotes. --- build.gradle | 2 +- docs/changelog.md | 4 ++++ .../src/com/structurizr/view/View.java | 20 +++++++++++++++++++ .../src/com/structurizr/view/ViewSet.java | 14 ++++++------- .../com/structurizr/view/ViewSetTests.java | 20 +++++++++++++++++++ 5 files changed, 52 insertions(+), 8 deletions(-) diff --git a/build.gradle b/build.gradle index 8ee5810f5..24745c7b3 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.9.10' + version = '1.9.11' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index f82258b8d..5ae4fe668 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.9.11 (unreleased) + +- Adds the ability to indicate that individual views should not merge layout from remotes. + ## 1.9.10 (26th November 2021) - Promotes a couple of methods to be public; no functional changes. diff --git a/structurizr-core/src/com/structurizr/view/View.java b/structurizr-core/src/com/structurizr/view/View.java index e1b1dbc57..aee7e3011 100644 --- a/structurizr-core/src/com/structurizr/view/View.java +++ b/structurizr-core/src/com/structurizr/view/View.java @@ -27,6 +27,7 @@ public abstract class View { private PaperSize paperSize = null; private Dimensions dimensions = null; private AutomaticLayout automaticLayout = null; + private boolean mergeFromRemote = true; private String title; private Set elementViews = new LinkedHashSet<>(); @@ -206,6 +207,25 @@ public void disableAutomaticLayout() { this.automaticLayout = null; } + /** + * Gets whether layout information for this view should be merged from a remote version of the workspace. + * + * @return true if layout information should be merged from the remote workspace, false otherwise + */ + public boolean getMergeFromRemote() { + return mergeFromRemote; + } + + /** + * Specifies whether layout information for this view should be merged from a remote version of the workspace. + * + * @param mergeFromRemote true if layout information should be merged from the remote workspace, false otherwise + */ + @JsonIgnore + public void setMergeFromRemote(boolean mergeFromRemote) { + this.mergeFromRemote = mergeFromRemote; + } + /** * Gets the title of this view, if one has been set. * diff --git a/structurizr-core/src/com/structurizr/view/ViewSet.java b/structurizr-core/src/com/structurizr/view/ViewSet.java index d845be15c..dcdbcd5b7 100644 --- a/structurizr-core/src/com/structurizr/view/ViewSet.java +++ b/structurizr-core/src/com/structurizr/view/ViewSet.java @@ -635,7 +635,7 @@ public Configuration getConfiguration() { public void copyLayoutInformationFrom(ViewSet source) { for (CustomView view : customViews) { - if (view.getAutomaticLayout() == null) { + if (view.getAutomaticLayout() == null && view.getMergeFromRemote() == true) { CustomView sourceView = findView(source.getCustomViews(), view); if (sourceView != null) { view.copyLayoutInformationFrom(sourceView); @@ -646,7 +646,7 @@ public void copyLayoutInformationFrom(ViewSet source) { } for (SystemLandscapeView view : systemLandscapeViews) { - if (view.getAutomaticLayout() == null) { + if (view.getAutomaticLayout() == null && view.getMergeFromRemote() == true) { SystemLandscapeView sourceView = findView(source.getSystemLandscapeViews(), view); if (sourceView != null) { view.copyLayoutInformationFrom(sourceView); @@ -657,7 +657,7 @@ public void copyLayoutInformationFrom(ViewSet source) { } for (SystemContextView view : systemContextViews) { - if (view.getAutomaticLayout() == null) { + if (view.getAutomaticLayout() == null && view.getMergeFromRemote() == true) { SystemContextView sourceView = findView(source.getSystemContextViews(), view); if (sourceView != null) { view.copyLayoutInformationFrom(sourceView); @@ -668,7 +668,7 @@ public void copyLayoutInformationFrom(ViewSet source) { } for (ContainerView view : containerViews) { - if (view.getAutomaticLayout() == null) { + if (view.getAutomaticLayout() == null && view.getMergeFromRemote() == true) { ContainerView sourceView = findView(source.getContainerViews(), view); if (sourceView != null) { view.copyLayoutInformationFrom(sourceView); @@ -679,7 +679,7 @@ public void copyLayoutInformationFrom(ViewSet source) { } for (ComponentView view : componentViews) { - if (view.getAutomaticLayout() == null) { + if (view.getAutomaticLayout() == null && view.getMergeFromRemote() == true) { ComponentView sourceView = findView(source.getComponentViews(), view); if (sourceView != null) { view.copyLayoutInformationFrom(sourceView); @@ -690,7 +690,7 @@ public void copyLayoutInformationFrom(ViewSet source) { } for (DynamicView view : dynamicViews) { - if (view.getAutomaticLayout() == null) { + if (view.getAutomaticLayout() == null && view.getMergeFromRemote() == true) { DynamicView sourceView = findView(source.getDynamicViews(), view); if (sourceView != null) { view.copyLayoutInformationFrom(sourceView); @@ -701,7 +701,7 @@ public void copyLayoutInformationFrom(ViewSet source) { } for (DeploymentView view : deploymentViews) { - if (view.getAutomaticLayout() == null) { + if (view.getAutomaticLayout() == null && view.getMergeFromRemote() == true) { DeploymentView sourceView = findView(source.getDeploymentViews(), view); if (sourceView != null) { view.copyLayoutInformationFrom(sourceView); diff --git a/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java b/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java index 80d1ef59d..1d4623c8d 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java @@ -1074,4 +1074,24 @@ public void test_createDefaultViews() { assertSame("Live", views.getDeploymentViews().stream().filter(v -> v.getKey().equals("SoftwareSystem2-Live-Deployment")).findFirst().get().getEnvironment()); } + @Test + public void test_copyLayoutInformationFrom_DoesNothing_WhenMergeFromRemoteIsSetToFalse() { + Workspace workspace1 = createWorkspace(); + SoftwareSystem softwareSystem1 = workspace1.getModel().getSoftwareSystemWithName("Software System"); + SystemLandscapeView view1 = workspace1.getViews().createSystemLandscapeView("landscape", "Description"); + view1.addAllElements(); + view1.getElements().iterator().next().setX(100); + view1.setPaperSize(PaperSize.A3_Landscape); + + Workspace workspace2 = createWorkspace(); + SoftwareSystem softwareSystem2 = workspace2.getModel().getSoftwareSystemWithName("Software System"); + SystemLandscapeView view2 = workspace2.getViews().createSystemLandscapeView("context", "Description"); + view2.addAllElements(); + view2.setMergeFromRemote(false); + + workspace2.getViews().copyLayoutInformationFrom(workspace1.getViews()); + assertEquals(0, view2.getElements().iterator().next().getX()); + assertNull(view2.getPaperSize()); + } + } \ No newline at end of file From 3d851d78f1f3bc5580d4092a6da4dfe21f3ec226 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 17 Dec 2021 09:42:31 +0000 Subject: [PATCH 264/717] Make it easier to find out the default set of tags for an element/relationship. --- structurizr-core/src/com/structurizr/model/Component.java | 2 +- structurizr-core/src/com/structurizr/model/Container.java | 2 +- .../src/com/structurizr/model/CustomElement.java | 2 +- .../src/com/structurizr/model/DeploymentNode.java | 2 +- .../src/com/structurizr/model/InfrastructureNode.java | 2 +- structurizr-core/src/com/structurizr/model/ModelItem.java | 4 ++-- structurizr-core/src/com/structurizr/model/Person.java | 2 +- .../src/com/structurizr/model/Relationship.java | 2 +- .../src/com/structurizr/model/SoftwareSystem.java | 2 +- .../structurizr/model/StaticStructureElementInstance.java | 2 +- .../unit/com/structurizr/model/ContainerInstanceTests.java | 2 +- .../unit/com/structurizr/model/DeploymentNodeTests.java | 6 +++--- .../unit/com/structurizr/model/InfrastructureNodeTests.java | 6 +++--- .../com/structurizr/model/SoftwareSystemInstanceTests.java | 2 +- 14 files changed, 19 insertions(+), 19 deletions(-) diff --git a/structurizr-core/src/com/structurizr/model/Component.java b/structurizr-core/src/com/structurizr/model/Component.java index 2dde1abf2..64c4127d7 100644 --- a/structurizr-core/src/com/structurizr/model/Component.java +++ b/structurizr-core/src/com/structurizr/model/Component.java @@ -137,7 +137,7 @@ public String getCanonicalName() { } @Override - protected Set getRequiredTags() { + public Set getDefaultTags() { return new LinkedHashSet<>(Arrays.asList(Tags.ELEMENT, Tags.COMPONENT)); } diff --git a/structurizr-core/src/com/structurizr/model/Container.java b/structurizr-core/src/com/structurizr/model/Container.java index 8b5d9a93c..a00e5a538 100644 --- a/structurizr-core/src/com/structurizr/model/Container.java +++ b/structurizr-core/src/com/structurizr/model/Container.java @@ -189,7 +189,7 @@ public String getCanonicalName() { } @Override - protected Set getRequiredTags() { + public Set getDefaultTags() { return new LinkedHashSet<>(Arrays.asList(Tags.ELEMENT, Tags.CONTAINER)); } diff --git a/structurizr-core/src/com/structurizr/model/CustomElement.java b/structurizr-core/src/com/structurizr/model/CustomElement.java index 9e1e42645..0b745c3f7 100644 --- a/structurizr-core/src/com/structurizr/model/CustomElement.java +++ b/structurizr-core/src/com/structurizr/model/CustomElement.java @@ -24,7 +24,7 @@ public Element getParent() { } @Override - protected Set getRequiredTags() { + public Set getDefaultTags() { return new LinkedHashSet<>(Collections.singletonList(Tags.ELEMENT)); } diff --git a/structurizr-core/src/com/structurizr/model/DeploymentNode.java b/structurizr-core/src/com/structurizr/model/DeploymentNode.java index 148433d58..020b8fff2 100644 --- a/structurizr-core/src/com/structurizr/model/DeploymentNode.java +++ b/structurizr-core/src/com/structurizr/model/DeploymentNode.java @@ -373,7 +373,7 @@ public void setInstances(int instances) { } @JsonIgnore - protected Set getRequiredTags() { + public Set getDefaultTags() { return new LinkedHashSet<>(Arrays.asList(Tags.ELEMENT, Tags.DEPLOYMENT_NODE)); } diff --git a/structurizr-core/src/com/structurizr/model/InfrastructureNode.java b/structurizr-core/src/com/structurizr/model/InfrastructureNode.java index f5e32c7b4..1910241be 100644 --- a/structurizr-core/src/com/structurizr/model/InfrastructureNode.java +++ b/structurizr-core/src/com/structurizr/model/InfrastructureNode.java @@ -69,7 +69,7 @@ public void setTechnology(String technology) { } @JsonIgnore - protected Set getRequiredTags() { + public Set getDefaultTags() { return new LinkedHashSet<>(Arrays.asList(Tags.ELEMENT, Tags.INFRASTRUCTURE_NODE)); } diff --git a/structurizr-core/src/com/structurizr/model/ModelItem.java b/structurizr-core/src/com/structurizr/model/ModelItem.java index b8e9b115f..c0d8f27d0 100644 --- a/structurizr-core/src/com/structurizr/model/ModelItem.java +++ b/structurizr-core/src/com/structurizr/model/ModelItem.java @@ -22,7 +22,7 @@ public abstract class ModelItem { @JsonIgnore public abstract String getCanonicalName(); - protected abstract Set getRequiredTags(); + public abstract Set getDefaultTags(); /** * Gets the ID of this item in the model. @@ -70,7 +70,7 @@ public String getTags() { @JsonIgnore public Set getTagsAsSet() { - Set setOfTags = new LinkedHashSet<>(getRequiredTags()); + Set setOfTags = new LinkedHashSet<>(getDefaultTags()); setOfTags.addAll(tags); return setOfTags; diff --git a/structurizr-core/src/com/structurizr/model/Person.java b/structurizr-core/src/com/structurizr/model/Person.java index 3cdae2738..d4f67d3ba 100644 --- a/structurizr-core/src/com/structurizr/model/Person.java +++ b/structurizr-core/src/com/structurizr/model/Person.java @@ -46,7 +46,7 @@ public String getCanonicalName() { } @Override - protected Set getRequiredTags() { + public Set getDefaultTags() { return new LinkedHashSet<>(Arrays.asList(Tags.ELEMENT, Tags.PERSON)); } diff --git a/structurizr-core/src/com/structurizr/model/Relationship.java b/structurizr-core/src/com/structurizr/model/Relationship.java index 093c232f1..61b3521e0 100644 --- a/structurizr-core/src/com/structurizr/model/Relationship.java +++ b/structurizr-core/src/com/structurizr/model/Relationship.java @@ -150,7 +150,7 @@ void setLinkedRelationshipId(String baseRelationshipId) { } @Override - protected Set getRequiredTags() { + public Set getDefaultTags() { if (linkedRelationshipId == null) { Set tags = new LinkedHashSet<>(); tags.add(Tags.RELATIONSHIP); diff --git a/structurizr-core/src/com/structurizr/model/SoftwareSystem.java b/structurizr-core/src/com/structurizr/model/SoftwareSystem.java index 401dfb58c..9f1fc119a 100644 --- a/structurizr-core/src/com/structurizr/model/SoftwareSystem.java +++ b/structurizr-core/src/com/structurizr/model/SoftwareSystem.java @@ -168,7 +168,7 @@ public String getCanonicalName() { } @Override - protected Set getRequiredTags() { + public Set getDefaultTags() { return new LinkedHashSet<>(Arrays.asList(Tags.ELEMENT, Tags.SOFTWARE_SYSTEM)); } diff --git a/structurizr-core/src/com/structurizr/model/StaticStructureElementInstance.java b/structurizr-core/src/com/structurizr/model/StaticStructureElementInstance.java index 2b713993c..e79c7dab4 100644 --- a/structurizr-core/src/com/structurizr/model/StaticStructureElementInstance.java +++ b/structurizr-core/src/com/structurizr/model/StaticStructureElementInstance.java @@ -96,7 +96,7 @@ void setInstanceId(int instanceId) { @Override @JsonIgnore - protected Set getRequiredTags() { + public Set getDefaultTags() { return Collections.emptySet(); } diff --git a/structurizr-core/test/unit/com/structurizr/model/ContainerInstanceTests.java b/structurizr-core/test/unit/com/structurizr/model/ContainerInstanceTests.java index 313ac51e4..96b306f9a 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ContainerInstanceTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ContainerInstanceTests.java @@ -58,7 +58,7 @@ public void test_getParent_ReturnsTheParentDeploymentNode() { public void test_getRequiredTags() { ContainerInstance instance = deploymentNode.add(database); - assertTrue(instance.getRequiredTags().isEmpty()); + assertTrue(instance.getDefaultTags().isEmpty()); } @Test diff --git a/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java b/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java index 52892fbad..694741de9 100644 --- a/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java @@ -39,9 +39,9 @@ public void test_getParent_ReturnsTheParentDeploymentNode() { @Test public void test_getRequiredTags() { DeploymentNode deploymentNode = new DeploymentNode(); - assertEquals(2, deploymentNode.getRequiredTags().size()); - assertTrue(deploymentNode.getRequiredTags().contains(Tags.ELEMENT)); - assertTrue(deploymentNode.getRequiredTags().contains(Tags.DEPLOYMENT_NODE)); + assertEquals(2, deploymentNode.getDefaultTags().size()); + assertTrue(deploymentNode.getDefaultTags().contains(Tags.ELEMENT)); + assertTrue(deploymentNode.getDefaultTags().contains(Tags.DEPLOYMENT_NODE)); } @Test diff --git a/structurizr-core/test/unit/com/structurizr/model/InfrastructureNodeTests.java b/structurizr-core/test/unit/com/structurizr/model/InfrastructureNodeTests.java index ac27f756b..11ec4c204 100644 --- a/structurizr-core/test/unit/com/structurizr/model/InfrastructureNodeTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/InfrastructureNodeTests.java @@ -26,9 +26,9 @@ public void test_getParent_ReturnsTheParentDeploymentNode() { @Test public void test_getRequiredTags() { InfrastructureNode infrastructureNode = new InfrastructureNode(); - assertEquals(2, infrastructureNode.getRequiredTags().size()); - assertTrue(infrastructureNode.getRequiredTags().contains(Tags.ELEMENT)); - assertTrue(infrastructureNode.getRequiredTags().contains(Tags.INFRASTRUCTURE_NODE)); + assertEquals(2, infrastructureNode.getDefaultTags().size()); + assertTrue(infrastructureNode.getDefaultTags().contains(Tags.ELEMENT)); + assertTrue(infrastructureNode.getDefaultTags().contains(Tags.INFRASTRUCTURE_NODE)); } @Test diff --git a/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemInstanceTests.java b/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemInstanceTests.java index b791fc11d..8930aac5e 100644 --- a/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemInstanceTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemInstanceTests.java @@ -57,7 +57,7 @@ public void test_getParent_ReturnsTheParentDeploymentNode() { public void test_getRequiredTags() { SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); - assertTrue(instance.getRequiredTags().isEmpty()); + assertTrue(instance.getDefaultTags().isEmpty()); } @Test From 693b095760adc1f809f58a4a555d772e2850ac37 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 17 Dec 2021 09:57:41 +0000 Subject: [PATCH 265/717] Adds support for different relationship line styles (solid, dashed, dotted). --- docs/changelog.md | 1 + .../src/com/structurizr/view/LineStyle.java | 9 ++++++ .../structurizr/view/RelationshipStyle.java | 32 ++++++++++++------- 3 files changed, 31 insertions(+), 11 deletions(-) create mode 100644 structurizr-core/src/com/structurizr/view/LineStyle.java diff --git a/docs/changelog.md b/docs/changelog.md index 5ae4fe668..46745f422 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -2,6 +2,7 @@ ## 1.9.11 (unreleased) +- Adds support for different relationship line styles (solid, dashed, dotted). - Adds the ability to indicate that individual views should not merge layout from remotes. ## 1.9.10 (26th November 2021) diff --git a/structurizr-core/src/com/structurizr/view/LineStyle.java b/structurizr-core/src/com/structurizr/view/LineStyle.java new file mode 100644 index 000000000..56fc365a2 --- /dev/null +++ b/structurizr-core/src/com/structurizr/view/LineStyle.java @@ -0,0 +1,9 @@ +package com.structurizr.view; + +public enum LineStyle { + + Dashed, + Dotted, + Solid + +} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/view/RelationshipStyle.java b/structurizr-core/src/com/structurizr/view/RelationshipStyle.java index dde9186e3..ba10d57cf 100644 --- a/structurizr-core/src/com/structurizr/view/RelationshipStyle.java +++ b/structurizr-core/src/com/structurizr/view/RelationshipStyle.java @@ -31,6 +31,10 @@ public final class RelationshipStyle { @JsonInclude(value = JsonInclude.Include.NON_NULL) private Boolean dashed; + /** the line style used when rendering lines */ + @JsonInclude(value = JsonInclude.Include.NON_NULL) + private LineStyle style; + /** the routing algorithm used when rendering lines */ @JsonInclude(value = JsonInclude.Include.NON_NULL) private Routing routing; @@ -50,17 +54,6 @@ public final class RelationshipStyle { this.tag = tag; } - public RelationshipStyle(String tag, Integer thickness, String color, Boolean dashed, Routing routing, Integer fontSize, Integer width, Integer position) { - this.tag = tag; - this.thickness = thickness; - setColor(color); - this.dashed = dashed; - this.routing = routing; - this.fontSize = fontSize; - this.width = width; - this.position = position; - } - public String getTag() { return tag; } @@ -112,6 +105,19 @@ public RelationshipStyle dashed(boolean dashed) { return this; } + public LineStyle getStyle() { + return style; + } + + public void setStyle(LineStyle style) { + this.style = style; + } + + public RelationshipStyle style(LineStyle style) { + setStyle(style); + return this; + } + public Routing getRouting() { return routing; } @@ -211,6 +217,10 @@ void copyFrom(RelationshipStyle relationshipStyle) { this.setDashed(relationshipStyle.getDashed()); } + if (relationshipStyle.getStyle() != null) { + this.setStyle(relationshipStyle.getStyle()); + } + if (relationshipStyle.getRouting() != null) { this.setRouting(relationshipStyle.getRouting()); } From 02ebe0af1d179846286eaa881c721045570a0ed4 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 21 Dec 2021 14:01:53 +0000 Subject: [PATCH 266/717] Adds name/value properties to the view set configuration. --- .../src/com/structurizr/PropertyHolder.java | 22 ++++++++++ .../src/com/structurizr/model/ModelItem.java | 3 +- .../com/structurizr/view/Configuration.java | 40 ++++++++++++++++++- 3 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 structurizr-core/src/com/structurizr/PropertyHolder.java diff --git a/structurizr-core/src/com/structurizr/PropertyHolder.java b/structurizr-core/src/com/structurizr/PropertyHolder.java new file mode 100644 index 000000000..ec22c5567 --- /dev/null +++ b/structurizr-core/src/com/structurizr/PropertyHolder.java @@ -0,0 +1,22 @@ +package com.structurizr; + +import java.util.Map; + +public interface PropertyHolder { + + /** + * Gets the collection of name-value property pairs associated with this workspace, as a Map. + * + * @return a Map (String, String) (empty if there are no properties) + */ + public Map getProperties(); + + /** + * Adds a name-value pair property to this workspace. + * + * @param name the name of the property + * @param value the value of the property + */ + public void addProperty(String name, String value); + +} diff --git a/structurizr-core/src/com/structurizr/model/ModelItem.java b/structurizr-core/src/com/structurizr/model/ModelItem.java index c0d8f27d0..d249f2865 100644 --- a/structurizr-core/src/com/structurizr/model/ModelItem.java +++ b/structurizr-core/src/com/structurizr/model/ModelItem.java @@ -1,6 +1,7 @@ package com.structurizr.model; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.structurizr.PropertyHolder; import com.structurizr.util.StringUtils; import com.structurizr.util.Url; @@ -9,7 +10,7 @@ /** * The base class for elements and relationships. */ -public abstract class ModelItem { +public abstract class ModelItem implements PropertyHolder { private String id = ""; private String originId = ""; diff --git a/structurizr-core/src/com/structurizr/view/Configuration.java b/structurizr-core/src/com/structurizr/view/Configuration.java index 5dbda9ca1..dc5dfdd75 100644 --- a/structurizr-core/src/com/structurizr/view/Configuration.java +++ b/structurizr-core/src/com/structurizr/view/Configuration.java @@ -3,15 +3,18 @@ import com.fasterxml.jackson.annotation.JsonGetter; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonSetter; +import com.structurizr.PropertyHolder; import com.structurizr.util.Url; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * Configuration associated with how information in the workspace is rendered. */ -public final class Configuration { +public final class Configuration implements PropertyHolder { private Branding branding = new Branding(); private Styles styles = new Styles(); @@ -24,6 +27,8 @@ public final class Configuration { private String lastSavedView; private ViewSortOrder viewSortOrder; + private Map properties = new HashMap<>(); + /** * Gets the styles associated with this set of views. * @@ -203,4 +208,37 @@ public void setViewSortOrder(ViewSortOrder viewSortOrder) { this.viewSortOrder = viewSortOrder; } + /** + * Gets the collection of name-value property pairs associated with this workspace, as a Map. + * + * @return a Map (String, String) (empty if there are no properties) + */ + public Map getProperties() { + return new HashMap<>(properties); + } + + /** + * Adds a name-value pair property to this workspace. + * + * @param name the name of the property + * @param value the value of the property + */ + public void addProperty(String name, String value) { + if (name == null || name.trim().length() == 0) { + throw new IllegalArgumentException("A property name must be specified."); + } + + if (value == null || value.trim().length() == 0) { + throw new IllegalArgumentException("A property value must be specified."); + } + + properties.put(name, value); + } + + void setProperties(Map properties) { + if (properties != null) { + this.properties = new HashMap<>(properties); + } + } + } \ No newline at end of file From 218566df8268d7823f5c067cbf36ea70cb0afd11 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 21 Dec 2021 15:34:06 +0000 Subject: [PATCH 267/717] Bump the version number. --- build.gradle | 2 +- docs/changelog.md | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 24745c7b3..327dccd28 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.9.11' + version = '1.10.0' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 46745f422..86e70f80e 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,9 +1,10 @@ # Changelog -## 1.9.11 (unreleased) +## 1.10.0 (unreleased) - Adds support for different relationship line styles (solid, dashed, dotted). - Adds the ability to indicate that individual views should not merge layout from remotes. +- Adds name/value properties to the view set configuration. ## 1.9.10 (26th November 2021) From 8b9a751499048e28ce352db7ea0bfc45087ccfb4 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 29 Dec 2021 11:03:07 +0000 Subject: [PATCH 268/717] Added release date. --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 86e70f80e..74ac7b4e7 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 1.10.0 (unreleased) +## 1.10.0 (29th December 2021) - Adds support for different relationship line styles (solid, dashed, dotted). - Adds the ability to indicate that individual views should not merge layout from remotes. From 22f7509c7306e877e733046367f188e241820f6c Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 29 Jan 2022 12:18:29 +0000 Subject: [PATCH 269/717] Updated version. --- docs/getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index 6f9941701..761e4725a 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -12,7 +12,7 @@ The Structurizr for Java binaries are hosted on [Maven Central](https://repo1.ma Name | Description ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-client:1.9.6 | The Structurizr API client library. +com.structurizr:structurizr-client:1.9.9 | The Structurizr API client library. ## 2. Create a Java program From 9f517da14c1bb89dd855e3a5d791a6f5bc446afb Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 31 Jan 2022 13:49:32 +0000 Subject: [PATCH 270/717] Makes the `Section.setContent()` method public, to allow pre-processing of content before workspace upload/rendering. --- build.gradle | 2 +- docs/changelog.md | 4 ++++ .../src/com/structurizr/documentation/Section.java | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 327dccd28..d45445455 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.10.0' + version = '1.10.1' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 74ac7b4e7..6bab84230 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.10.1 (unreleased) + +- Makes the `Section.setContent()` method public, to allow pre-processing of content before workspace upload/rendering. + ## 1.10.0 (29th December 2021) - Adds support for different relationship line styles (solid, dashed, dotted). diff --git a/structurizr-core/src/com/structurizr/documentation/Section.java b/structurizr-core/src/com/structurizr/documentation/Section.java index a81318eca..d1dd9207b 100644 --- a/structurizr-core/src/com/structurizr/documentation/Section.java +++ b/structurizr-core/src/com/structurizr/documentation/Section.java @@ -88,7 +88,7 @@ public String getContent() { return content; } - void setContent(String content) { + public void setContent(String content) { this.content = content; } From c678e196f7f32409eb6a87d6e749d437b7a31e3a Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 31 Jan 2022 13:51:30 +0000 Subject: [PATCH 271/717] Switch to automatic documentation template. --- .../example/FinancialRiskSystem.java | 9 +++------ .../{context.md => 01-context.md} | 0 ...l-overview.md => 02-functional-overview.md} | 0 ...-attributes.md => 03-quality-attributes.md} | 0 .../example/financialrisksystem/context.adoc | 18 ------------------ 5 files changed, 3 insertions(+), 24 deletions(-) rename structurizr-examples/src/com/structurizr/example/financialrisksystem/{context.md => 01-context.md} (100%) rename structurizr-examples/src/com/structurizr/example/financialrisksystem/{functional-overview.md => 02-functional-overview.md} (100%) rename structurizr-examples/src/com/structurizr/example/financialrisksystem/{quality-attributes.md => 03-quality-attributes.md} (100%) delete mode 100644 structurizr-examples/src/com/structurizr/example/financialrisksystem/context.adoc diff --git a/structurizr-examples/src/com/structurizr/example/FinancialRiskSystem.java b/structurizr-examples/src/com/structurizr/example/FinancialRiskSystem.java index 2c57657e4..ccbe910d4 100644 --- a/structurizr-examples/src/com/structurizr/example/FinancialRiskSystem.java +++ b/structurizr-examples/src/com/structurizr/example/FinancialRiskSystem.java @@ -2,8 +2,7 @@ import com.structurizr.Workspace; import com.structurizr.api.StructurizrClient; -import com.structurizr.documentation.Format; -import com.structurizr.documentation.StructurizrDocumentationTemplate; +import com.structurizr.documentation.AutomaticDocumentationTemplate; import com.structurizr.model.*; import com.structurizr.view.*; @@ -76,11 +75,9 @@ public static void main(String[] args) throws Exception { styles.addElementStyle("Future State").opacity(30).border(Border.Dashed); styles.addRelationshipStyle("Future State").opacity(30).dashed(true); - StructurizrDocumentationTemplate template = new StructurizrDocumentationTemplate(workspace); + AutomaticDocumentationTemplate template = new AutomaticDocumentationTemplate(workspace); File documentationRoot = new File("./structurizr-examples/src/com/structurizr/example/financialrisksystem"); - template.addContextSection(financialRiskSystem, new File(documentationRoot, "context.adoc")); - template.addFunctionalOverviewSection(financialRiskSystem, new File(documentationRoot, "functional-overview.md")); - template.addQualityAttributesSection(financialRiskSystem, new File(documentationRoot, "quality-attributes.md")); + template.addSections(documentationRoot); template.addImages(documentationRoot); StructurizrClient structurizrClient = new StructurizrClient(API_KEY, API_SECRET); diff --git a/structurizr-examples/src/com/structurizr/example/financialrisksystem/context.md b/structurizr-examples/src/com/structurizr/example/financialrisksystem/01-context.md similarity index 100% rename from structurizr-examples/src/com/structurizr/example/financialrisksystem/context.md rename to structurizr-examples/src/com/structurizr/example/financialrisksystem/01-context.md diff --git a/structurizr-examples/src/com/structurizr/example/financialrisksystem/functional-overview.md b/structurizr-examples/src/com/structurizr/example/financialrisksystem/02-functional-overview.md similarity index 100% rename from structurizr-examples/src/com/structurizr/example/financialrisksystem/functional-overview.md rename to structurizr-examples/src/com/structurizr/example/financialrisksystem/02-functional-overview.md diff --git a/structurizr-examples/src/com/structurizr/example/financialrisksystem/quality-attributes.md b/structurizr-examples/src/com/structurizr/example/financialrisksystem/03-quality-attributes.md similarity index 100% rename from structurizr-examples/src/com/structurizr/example/financialrisksystem/quality-attributes.md rename to structurizr-examples/src/com/structurizr/example/financialrisksystem/03-quality-attributes.md diff --git a/structurizr-examples/src/com/structurizr/example/financialrisksystem/context.adoc b/structurizr-examples/src/com/structurizr/example/financialrisksystem/context.adoc deleted file mode 100644 index f1c965507..000000000 --- a/structurizr-examples/src/com/structurizr/example/financialrisksystem/context.adoc +++ /dev/null @@ -1,18 +0,0 @@ -== Context - -A global investment bank based in London, New York and Singapore trades (buys and sells) financial products with other banks (counterparties). When share prices on the stock markets move up or down, the bank either makes money or loses it. At the end of the working day, the bank needs to gain a view of how much risk they are exposed to (e.g. of losing money) by running some calculations on the data held about their trades. The bank has an existing Trade Data System (TDS) and Reference Data System (RDS) but need a new Risk System. - -image::embed:Context[] - -=== Trade Data System - -The Trade Data System maintains a store of all trades made by the bank. It is already configured to generate a file-based XML export of trade data at the close of business (5pm) in New York. The export includes the following information for every trade made by the bank: - -* Trade ID -* Date -* Current trade value in US dollars -* Counterparty ID - -=== Reference Data System - -The Reference Data System maintains all of the reference data needed by the bank. This includes information about counterparties; each of which represents an individual, a bank, etc. A file-based XML export is also available and includes basic information about each counterparty. A new organisation-wide reference data system is due for completion in the next 3 months, with the current system eventually being decommissioned. \ No newline at end of file From 45bce990a282cb4268c08ce15c06de31d2e769f2 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 1 Feb 2022 07:26:51 +0000 Subject: [PATCH 272/717] Updated to reflect release. --- docs/changelog.md | 2 +- docs/getting-started.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 6bab84230..c5c121e70 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 1.10.1 (unreleased) +## 1.10.1 (1st February 2022) - Makes the `Section.setContent()` method public, to allow pre-processing of content before workspace upload/rendering. diff --git a/docs/getting-started.md b/docs/getting-started.md index 761e4725a..d005c2e1c 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -12,7 +12,7 @@ The Structurizr for Java binaries are hosted on [Maven Central](https://repo1.ma Name | Description ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-client:1.9.9 | The Structurizr API client library. +com.structurizr:structurizr-client:1.10.1 | The Structurizr API client library. ## 2. Create a Java program From edb1cc62a1f5b85a8802adbf80945b378ca868de Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 2 Feb 2022 08:55:18 +0000 Subject: [PATCH 273/717] Allows decision content to be processed too. --- .../src/com/structurizr/documentation/Decision.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structurizr-core/src/com/structurizr/documentation/Decision.java b/structurizr-core/src/com/structurizr/documentation/Decision.java index c463b83c5..2209a56c3 100644 --- a/structurizr-core/src/com/structurizr/documentation/Decision.java +++ b/structurizr-core/src/com/structurizr/documentation/Decision.java @@ -89,7 +89,7 @@ public String getContent() { return content; } - void setContent(String content) { + public void setContent(String content) { this.content = content; } From 0d8635598c8a43d2a8e4e440065596dcfd69c581 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 18 Feb 2022 15:35:34 +0000 Subject: [PATCH 274/717] Fixes #167 (ImpliedRelationship Strategy replication of URL and perspectives). --- build.gradle | 2 +- docs/changelog.md | 4 ++++ ...psUnlessAnyRelationshipExistsStrategy.java | 5 ++++- ...sUnlessSameRelationshipExistsStrategy.java | 5 ++++- .../src/com/structurizr/model/Model.java | 4 ++-- ...essAnyRelationshipExistsStrategyTests.java | 20 +++++++++++-------- ...ssSameRelationshipExistsStrategyTests.java | 20 +++++++++++-------- 7 files changed, 39 insertions(+), 21 deletions(-) diff --git a/build.gradle b/build.gradle index d45445455..241c3bc03 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.10.1' + version = '1.11.0' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index c5c121e70..36a92f4ae 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.11.0 (unreleased to Maven Central) + +- Fixes #167 (ImpliedRelationship Strategy replication of URL and perspectives). + ## 1.10.1 (1st February 2022) - Makes the `Section.setContent()` method public, to allow pre-processing of content before workspace upload/rendering. diff --git a/structurizr-core/src/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy.java b/structurizr-core/src/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy.java index 309b362ab..6d2388074 100644 --- a/structurizr-core/src/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy.java +++ b/structurizr-core/src/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy.java @@ -19,7 +19,10 @@ public void createImpliedRelationships(Relationship relationship) { boolean createRelationship = !source.hasEfferentRelationshipWith(destination); if (createRelationship) { - model.addRelationship(source, destination, relationship.getDescription(), relationship.getTechnology(), relationship.getInteractionStyle(), relationship.getTagsAsSet().toArray(new String[0]), false); + Relationship impliedRelationship = model.addRelationship(source, destination, relationship.getDescription(), relationship.getTechnology(), false); + if (impliedRelationship != null) { + impliedRelationship.setLinkedRelationshipId(relationship.getId()); + } } } diff --git a/structurizr-core/src/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategy.java b/structurizr-core/src/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategy.java index ceee5e87e..4abaf94f1 100644 --- a/structurizr-core/src/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategy.java +++ b/structurizr-core/src/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategy.java @@ -19,7 +19,10 @@ public void createImpliedRelationships(Relationship relationship) { boolean createRelationship = !source.hasEfferentRelationshipWith(destination, relationship.getDescription()); if (createRelationship) { - model.addRelationship(source, destination, relationship.getDescription(), relationship.getTechnology(), relationship.getInteractionStyle(), relationship.getTagsAsSet().toArray(new String[0]), false); + Relationship impliedRelationship = model.addRelationship(source, destination, relationship.getDescription(), relationship.getTechnology(), false); + if (impliedRelationship != null) { + impliedRelationship.setLinkedRelationshipId(relationship.getId()); + } } } diff --git a/structurizr-core/src/com/structurizr/model/Model.java b/structurizr-core/src/com/structurizr/model/Model.java index 996bd821f..53deec472 100644 --- a/structurizr-core/src/com/structurizr/model/Model.java +++ b/structurizr-core/src/com/structurizr/model/Model.java @@ -238,8 +238,8 @@ Component addComponentOfType(Container parent, String name, String type, String } @Nullable - Relationship addRelationship(Element source, @Nonnull Element destination, String description, String technology) { - return addRelationship(source, destination, description, technology, null); + Relationship addRelationship(Element source, @Nonnull Element destination, String description, String technology, boolean createImpliedRelationships) { + return addRelationship(source, destination, description, technology, null, new String[0], createImpliedRelationships); } @Nullable diff --git a/structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategyTests.java b/structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategyTests.java index 5110550ab..d61147232 100644 --- a/structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategyTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategyTests.java @@ -3,8 +3,9 @@ import com.structurizr.AbstractWorkspaceTestBase; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import java.util.Set; + +import static org.junit.Assert.*; public class CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategyTests extends AbstractWorkspaceTestBase { @@ -20,7 +21,7 @@ public void test_impliedRelationshipsAreCreated() { model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); - aaa.uses(bbb, "Uses 1", null, InteractionStyle.Asynchronous, new String[] { "Tag 1", "Tag 2" }); + Relationship explicitRelationship = aaa.uses(bbb, "Uses 1", "Technology", InteractionStyle.Asynchronous, new String[] { "Tag 1", "Tag 2" }); assertEquals(9, model.getRelationships().size()); assertTrue(aaa.hasEfferentRelationshipWith(bbb, "Uses 1")); @@ -37,11 +38,14 @@ public void test_impliedRelationshipsAreCreated() { assertTrue(a.hasEfferentRelationshipWith(bb, "Uses 1")); assertTrue(a.hasEfferentRelationshipWith(b, "Uses 1")); - // and all relationships should have the same interaction style and tags - for (Relationship r : model.getRelationships()) { - assertEquals(InteractionStyle.Asynchronous, r.getInteractionStyle()); - assertTrue(r.getTagsAsSet().contains("Tag 1")); - assertTrue(r.getTagsAsSet().contains("Tag 2")); + // all implied relationships with have a linked relationship, technology, and other properties unset + Set impliedRelationships = model.getRelationships(); + impliedRelationships.remove(explicitRelationship); + for (Relationship r : impliedRelationships) { + assertEquals(explicitRelationship.getId(), r.getLinkedRelationshipId()); + assertEquals("Technology", r.getTechnology()); + assertNull(r.getInteractionStyle()); + assertTrue(r.getTagsAsSet().isEmpty()); } // and add another relationship with a different description diff --git a/structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategyTests.java b/structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategyTests.java index 26a5952d4..0fa35ee89 100644 --- a/structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategyTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategyTests.java @@ -3,8 +3,9 @@ import com.structurizr.AbstractWorkspaceTestBase; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import java.util.Set; + +import static org.junit.Assert.*; public class CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategyTests extends AbstractWorkspaceTestBase { @@ -20,16 +21,19 @@ public void test_impliedRelationships_WhenNoSummaryRelationshipsExist() { model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategy()); - aaa.uses(bbb, "Uses 1", null, InteractionStyle.Asynchronous, new String[] { "Tag 1", "Tag 2" }); + Relationship explicitRelationship = aaa.uses(bbb, "Uses 1", "Technology", InteractionStyle.Asynchronous, new String[] { "Tag 1", "Tag 2" }); assertEquals(9, model.getRelationships().size()); assertTrue(aaa.hasEfferentRelationshipWith(bbb, "Uses 1")); - // and all relationships should have the same interaction style and tags - for (Relationship r : model.getRelationships()) { - assertEquals(InteractionStyle.Asynchronous, r.getInteractionStyle()); - assertTrue(r.getTagsAsSet().contains("Tag 1")); - assertTrue(r.getTagsAsSet().contains("Tag 2")); + // all implied relationships with have a linked relationship, technology, and other properties unset + Set impliedRelationships = model.getRelationships(); + impliedRelationships.remove(explicitRelationship); + for (Relationship r : impliedRelationships) { + assertEquals(explicitRelationship.getId(), r.getLinkedRelationshipId()); + assertEquals("Technology", r.getTechnology()); + assertNull(r.getInteractionStyle()); + assertTrue(r.getTagsAsSet().isEmpty()); } // AAA->BBB implies AAA->BB AAA->B AA->BBB AA->BB AA->B A->BBB A->BB A->B From a847e4d10ed1f6367e78a3b4cddf9d305b027494 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 18 Feb 2022 15:59:51 +0000 Subject: [PATCH 275/717] Updated changelog. --- docs/changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.md b/docs/changelog.md index 36a92f4ae..f799f8033 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -3,6 +3,7 @@ ## 1.11.0 (unreleased to Maven Central) - Fixes #167 (ImpliedRelationship Strategy replication of URL and perspectives). +- Makes the `Decision.setContent()` method public, to allow pre-processing of content before workspace upload/rendering. ## 1.10.1 (1st February 2022) From 48824151dfa9e6d992e5f023721df14717755102 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 18 Feb 2022 17:23:55 +0000 Subject: [PATCH 276/717] Use linked elements/relationships when finding tags and styles. --- .../src/com/structurizr/view/Styles.java | 54 +++++++++++-------- .../com/structurizr/view/StylesTests.java | 53 ++++++++++++++++++ 2 files changed, 85 insertions(+), 22 deletions(-) diff --git a/structurizr-core/src/com/structurizr/view/Styles.java b/structurizr-core/src/com/structurizr/view/Styles.java index 8c1a1ff78..e5e33813b 100644 --- a/structurizr-core/src/com/structurizr/view/Styles.java +++ b/structurizr-core/src/com/structurizr/view/Styles.java @@ -1,10 +1,7 @@ package com.structurizr.view; import com.structurizr.Workspace; -import com.structurizr.model.DeploymentNode; -import com.structurizr.model.Element; -import com.structurizr.model.Relationship; -import com.structurizr.model.Tags; +import com.structurizr.model.*; import com.structurizr.util.StringUtils; import java.util.*; @@ -158,10 +155,22 @@ public ElementStyle findElementStyle(Element element) { } if (element != null) { - for (String tag : element.getTagsAsSet()) { - ElementStyle elementStyle = findElementStyle(tag); - if (elementStyle != null) { - style.copyFrom(elementStyle); + String tags = element.getTags(); + + if (element instanceof SoftwareSystemInstance) { + SoftwareSystem ss = ((SoftwareSystemInstance)element).getSoftwareSystem(); + tags = ss.getTags() + "," + tags; + } else if (element instanceof ContainerInstance) { + Container c = ((ContainerInstance)element).getContainer(); + tags = c.getTags() + "," + tags; + } + + for (String tag : tags.split(",")) { + if (!StringUtils.isNullOrEmpty(tag)) { + ElementStyle elementStyle = findElementStyle(tag); + if (elementStyle != null) { + style.copyFrom(elementStyle); + } } } } @@ -192,25 +201,26 @@ public ElementStyle findElementStyle(Element element) { public RelationshipStyle findRelationshipStyle(Relationship relationship) { RelationshipStyle style = new RelationshipStyle("").thickness(2).color("#707070").dashed(true).routing(Routing.Direct).fontSize(24).width(200).position(50).opacity(100); - String tags; if (relationship != null) { - if (!StringUtils.isNullOrEmpty(relationship.getLinkedRelationshipId())) { - // the "linked relationship ID" is used for container instance -> container instance relationships - Relationship linkedRelationship = relationship.getModel().getRelationship(relationship.getLinkedRelationshipId()); - if (linkedRelationship != null) { - tags = linkedRelationship.getTags() + "," + relationship.getTags(); - } else { - tags = relationship.getTags(); - } - } else { - tags = relationship.getTags(); + String tags = relationship.getTags(); + String linkedRelationshipId = relationship.getLinkedRelationshipId(); + + while (!StringUtils.isNullOrEmpty(linkedRelationshipId)) { + // the "linked relationship ID" is used for: + // - container instance -> container instance relationships + // - implied relationships + Relationship linkedRelationship = relationship.getModel().getRelationship(linkedRelationshipId); + tags = linkedRelationship.getTags() + "," + tags; + linkedRelationshipId = linkedRelationship.getLinkedRelationshipId(); } for (String tag : tags.split(",")) { - RelationshipStyle relationshipStyle = findRelationshipStyle(tag); - if (relationshipStyle != null) { - style.copyFrom(relationshipStyle); + if (!StringUtils.isNullOrEmpty(tag)) { + RelationshipStyle relationshipStyle = findRelationshipStyle(tag); + if (relationshipStyle != null) { + style.copyFrom(relationshipStyle); + } } } } diff --git a/structurizr-core/test/unit/com/structurizr/view/StylesTests.java b/structurizr-core/test/unit/com/structurizr/view/StylesTests.java index 94dae1fac..881dcc5f7 100644 --- a/structurizr-core/test/unit/com/structurizr/view/StylesTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/StylesTests.java @@ -68,6 +68,32 @@ public void test_findElementStyle_ReturnsTheCorrectStyle_WhenStylesAreDefined() assertEquals(true, style.getDescription()); } + @Test + public void test_findElementStyle_ReturnsTheCorrectStyleForAnElementInstance_WhenStylesAreDefined() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Name"); + softwareSystem.addTags("Some Tag"); + + DeploymentNode deploymentNode = model.addDeploymentNode("Server"); + SoftwareSystemInstance softwareSystemInstance = deploymentNode.add(softwareSystem); + + styles.addElementStyle(Tags.SOFTWARE_SYSTEM).background("#ff0000").color("#ffffff"); + styles.addElementStyle("Some Tag").color("#0000ff").stroke("#00ff00").shape(Shape.RoundedBox).width(123).height(456); + + ElementStyle style = styles.findElementStyle(softwareSystemInstance); + assertEquals(new Integer(123), style.getWidth()); + assertEquals(new Integer(456), style.getHeight()); + assertEquals("#ff0000", style.getBackground()); + assertEquals("#0000ff", style.getColor()); + assertEquals(new Integer(24), style.getFontSize()); + assertEquals(Shape.RoundedBox, style.getShape()); + assertNull(style.getIcon()); + assertEquals(Border.Solid, style.getBorder()); + assertEquals("#00ff00", style.getStroke()); + assertEquals(new Integer(100), style.getOpacity()); + assertEquals(true, style.getMetadata()); + assertEquals(true, style.getDescription()); + } + @Test public void test_findElementStyle_ReturnsTheDefaultElementSize_WhenTheShapeIsABox() { SoftwareSystem element = model.addSoftwareSystem("Name", "Description"); @@ -149,6 +175,7 @@ public void test_findRelationshipStyle_ReturnsTheCorrectStyle_WhenThereIsALinked SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); Container container1 = softwareSystem.addContainer("Container 1", "Description", "Technology"); Container container2 = softwareSystem.addContainer("Container 2", "Description", "Technology"); + Relationship relationship = container1.uses(container2, "Uses"); relationship.addTags("Tag"); styles.addRelationshipStyle("Tag").color("#0000ff"); @@ -166,6 +193,32 @@ public void test_findRelationshipStyle_ReturnsTheCorrectStyle_WhenThereIsALinked assertEquals("#0000ff", style.getColor()); } + @Test + public void test_findRelationshipStyle_ReturnsTheCorrectStyle_WhenThereIsALinkedRelationshipBasedUponAnImpliedRelationship() { + model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); + Container container1 = softwareSystem.addContainer("Container 1"); + Component component1 = container1.addComponent("Component 1"); + Container container2 = softwareSystem.addContainer("Container 2"); + Component component2 = container2.addComponent("Component 2"); + + Relationship relationship = component1.uses(component2, "Uses"); + relationship.addTags("Tag"); + styles.addRelationshipStyle("Tag").color("#0000ff"); + + RelationshipStyle style = styles.findRelationshipStyle(relationship); + assertEquals("#0000ff", style.getColor()); + + DeploymentNode deploymentNode = model.addDeploymentNode("Server"); + ContainerInstance containerInstance1 = deploymentNode.add(container1); + ContainerInstance containerInstance2 = deploymentNode.add(container2); + + Relationship relationshipInstance = containerInstance1.getEfferentRelationshipWith(containerInstance2); + + style = styles.findRelationshipStyle(relationshipInstance); + assertEquals("#0000ff", style.getColor()); + } + @Test public void test_addElementStyle_ThrowsAnException_WhenATagIsNotSpecified() { try { From 075db91cbee4bb95f21e768bff8bbfb589870e60 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 18 Feb 2022 17:47:37 +0000 Subject: [PATCH 277/717] Updated to reflect release. --- docs/changelog.md | 2 +- docs/getting-started.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index f799f8033..45128ae6c 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 1.11.0 (unreleased to Maven Central) +## 1.11.0 (18th February 2022) - Fixes #167 (ImpliedRelationship Strategy replication of URL and perspectives). - Makes the `Decision.setContent()` method public, to allow pre-processing of content before workspace upload/rendering. diff --git a/docs/getting-started.md b/docs/getting-started.md index d005c2e1c..674d4a3fb 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -12,7 +12,7 @@ The Structurizr for Java binaries are hosted on [Maven Central](https://repo1.ma Name | Description ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-client:1.10.1 | The Structurizr API client library. +com.structurizr:structurizr-client:1.11.0 | The Structurizr API client library. ## 2. Create a Java program From ae1da0f94b8e5beb3ee6f0f0485fb2b68a7f5d77 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 22 Feb 2022 17:47:14 +0000 Subject: [PATCH 278/717] Default tags are not needed in the JSON representation. --- structurizr-core/src/com/structurizr/model/ModelItem.java | 1 + 1 file changed, 1 insertion(+) diff --git a/structurizr-core/src/com/structurizr/model/ModelItem.java b/structurizr-core/src/com/structurizr/model/ModelItem.java index d249f2865..764264e3e 100644 --- a/structurizr-core/src/com/structurizr/model/ModelItem.java +++ b/structurizr-core/src/com/structurizr/model/ModelItem.java @@ -23,6 +23,7 @@ public abstract class ModelItem implements PropertyHolder { @JsonIgnore public abstract String getCanonicalName(); + @JsonIgnore public abstract Set getDefaultTags(); /** From f85ed233c565c9c2da40b4aeed78f2a539ea1c97 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 23 Feb 2022 14:31:03 +0000 Subject: [PATCH 279/717] Removed deprecated "addImplicitRelationships()" method. --- .../src/com/structurizr/model/Model.java | 100 ----- .../com/structurizr/model/ModelTests.java | 342 ------------------ 2 files changed, 442 deletions(-) diff --git a/structurizr-core/src/com/structurizr/model/Model.java b/structurizr-core/src/com/structurizr/model/Model.java index 53deec472..dcb001a87 100644 --- a/structurizr-core/src/com/structurizr/model/Model.java +++ b/structurizr-core/src/com/structurizr/model/Model.java @@ -683,106 +683,6 @@ public CustomElement getCustomElementWithName(@Nonnull String name) { return null; } - /** - *

    Propagates all relationships from children to their parents. For example, if you have two components (AAA and BBB) - * in different software systems that have a relationship, calling this method will add the following - * additional implied relationships to the model: AAA->BB AAA-->B AA->BBB AA->BB AA->B A->BBB A->BB A->B.

    - * - * @return a set of all implicit relationships that were added to the model - * @deprecated use {@link #setImpliedRelationshipsStrategy(ImpliedRelationshipsStrategy)} ()} instead to set a strategy, before creating relationships - */ - @Nonnull - @Deprecated - public Set addImplicitRelationships() { - Set implicitRelationships = new HashSet<>(); - - String descriptionKey = "D"; - String technologyKey = "T"; - String interactionStyleKey = "I"; - Map>>> candidateRelationships = new HashMap<>(); - - for (Relationship relationship : getRelationships()) { - Element source = relationship.getSource(); - Element destination = relationship.getDestination(); - - while (source != null) { - while (destination != null) { - if (!source.hasEfferentRelationshipWith(destination)) { - if (propagatedRelationshipIsAllowed(source, destination)) { - - if (!candidateRelationships.containsKey(source)) { - candidateRelationships.put(source, new HashMap<>()); - } - - if (!candidateRelationships.get(source).containsKey(destination)) { - candidateRelationships.get(source).put(destination, new HashMap<>()); - candidateRelationships.get(source).get(destination).put(descriptionKey, new HashSet<>()); - candidateRelationships.get(source).get(destination).put(technologyKey, new HashSet<>()); - candidateRelationships.get(source).get(destination).put(interactionStyleKey, new HashSet<>()); - } - - if (relationship.getDescription() != null) { - candidateRelationships.get(source).get(destination).get(descriptionKey).add(relationship.getDescription()); - } - - if (relationship.getTechnology() != null) { - candidateRelationships.get(source).get(destination).get(technologyKey).add(relationship.getTechnology()); - } - - if (relationship.getInteractionStyle() != null) { - candidateRelationships.get(source).get(destination).get(interactionStyleKey).add(relationship.getInteractionStyle().toString()); - } - } - } - - destination = destination.getParent(); - } - - destination = relationship.getDestination(); - source = source.getParent(); - } - } - - for (Element source : candidateRelationships.keySet()) { - for (Element destination : candidateRelationships.get(source).keySet()) { - Set possibleDescriptions = candidateRelationships.get(source).get(destination).get(descriptionKey); - Set possibleTechnologies = candidateRelationships.get(source).get(destination).get(technologyKey); - Set possibleInteractionStyles = candidateRelationships.get(source).get(destination).get(interactionStyleKey); - - String description = ""; - if (possibleDescriptions.size() == 1) { - description = possibleDescriptions.iterator().next(); - } - - String technology = ""; - if (possibleTechnologies.size() == 1) { - technology = possibleTechnologies.iterator().next(); - } - - InteractionStyle interactionStyle = InteractionStyle.Synchronous; - // If any async relationships are present, mark the relationship as asynchronous - if (possibleInteractionStyles.contains(InteractionStyle.Asynchronous.toString())) { - interactionStyle = InteractionStyle.Asynchronous; - } - - Relationship implicitRelationship = addRelationship(source, destination, description, technology, interactionStyle); - if (implicitRelationship != null) { - implicitRelationships.add(implicitRelationship); - } - } - } - - return implicitRelationships; - } - - private boolean propagatedRelationshipIsAllowed(Element source, Element destination) { - if (source.equals(destination)) { - return false; - } - - return !(isChildOf(source, destination) || isChildOf(destination, source)); - } - /** * Determines whether this model is empty. * diff --git a/structurizr-core/test/unit/com/structurizr/model/ModelTests.java b/structurizr-core/test/unit/com/structurizr/model/ModelTests.java index ebe1700b9..83c4b49cf 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ModelTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ModelTests.java @@ -339,252 +339,6 @@ public void test_modifyRelationship_ThrowsAnException_WhenThatRelationshipDoesAl } } - @Test - public void test_addImplicitRelationships_WhenSourceAndDestinationAreComponentsInDifferentSoftwareSystems() { - SoftwareSystem a = model.addSoftwareSystem("A", ""); - Container aa = a.addContainer("AA", "", ""); - Component aaa = aa.addComponent("AAA", "", ""); - - SoftwareSystem b = model.addSoftwareSystem("B", ""); - Container bb = b.addContainer("BB", "", ""); - Component bbb = bb.addComponent("BBB", "", ""); - - aaa.uses(bbb, "Uses"); - assertEquals(1, model.getRelationships().size()); - assertTrue(aaa.hasEfferentRelationshipWith(bbb)); - - // AAA->BBB implies AAA->BB AAA->B AA->BBB AA->BB AA->B A->BBB A->BB A->B - Set implicitRelationships = model.addImplicitRelationships(); - assertEquals(9, model.getRelationships().size()); - assertEquals(8, implicitRelationships.size()); - - assertTrue(aaa.hasEfferentRelationshipWith(bb)); - assertTrue(aaa.hasEfferentRelationshipWith(b)); - - assertTrue(aa.hasEfferentRelationshipWith(bbb)); - assertTrue(aa.hasEfferentRelationshipWith(bb)); - assertTrue(aa.hasEfferentRelationshipWith(b)); - - assertTrue(a.hasEfferentRelationshipWith(bbb)); - assertTrue(a.hasEfferentRelationshipWith(bb)); - assertTrue(a.hasEfferentRelationshipWith(b)); - } - - @Test - public void test_addImplicitRelationships_WhenSourceIsAComponentAndDestinationIsAContainerInADifferentSoftwareSystem() { - SoftwareSystem a = model.addSoftwareSystem("A", ""); - Container aa = a.addContainer("AA", "", ""); - Component aaa = aa.addComponent("AAA", "", ""); - - SoftwareSystem b = model.addSoftwareSystem("B", ""); - Container bb = b.addContainer("BB", "", ""); - - aaa.uses(bb, "Uses"); - assertEquals(1, model.getRelationships().size()); - assertTrue(aaa.hasEfferentRelationshipWith(bb)); - - // AAA->BB implies AAA->B AA->BB AA->B A->BB A->B - Set implicitRelationships = model.addImplicitRelationships(); - assertEquals(6, model.getRelationships().size()); - assertEquals(5, implicitRelationships.size()); - - assertTrue(aaa.hasEfferentRelationshipWith(b)); - - assertTrue(aa.hasEfferentRelationshipWith(bb)); - assertTrue(aa.hasEfferentRelationshipWith(b)); - - assertTrue(a.hasEfferentRelationshipWith(bb)); - assertTrue(a.hasEfferentRelationshipWith(b)); - } - - @Test - public void test_addImplicitRelationships_WhenSourceIsAComponentAndDestinationIsADifferentSoftwareSystem() { - SoftwareSystem a = model.addSoftwareSystem("A", ""); - Container aa = a.addContainer("AA", "", ""); - Component aaa = aa.addComponent("AAA", "", ""); - - SoftwareSystem b = model.addSoftwareSystem("B", ""); - - aaa.uses(b, "Uses"); - assertEquals(1, model.getRelationships().size()); - assertTrue(aaa.hasEfferentRelationshipWith(b)); - - // AAA->B implies AA->B A->B - Set implicitRelationships = model.addImplicitRelationships(); - assertEquals(3, model.getRelationships().size()); - assertEquals(2, implicitRelationships.size()); - assertTrue(aa.hasEfferentRelationshipWith(b)); - assertTrue(a.hasEfferentRelationshipWith(b)); - } - - @Test - public void test_addImplicitRelationships_WhenSourceIsAContainerAndDestinationIsAComponentInADifferentSoftwareSystem() { - SoftwareSystem a = model.addSoftwareSystem("A", ""); - Container aa = a.addContainer("AA", "", ""); - - SoftwareSystem b = model.addSoftwareSystem("B", ""); - Container bb = b.addContainer("BB", "", ""); - Component bbb = bb.addComponent("BBB", "", ""); - - aa.uses(bbb, "Uses"); - assertEquals(1, model.getRelationships().size()); - assertTrue(aa.hasEfferentRelationshipWith(bbb)); - - // AA->BBB implies AA->BB AA->B A->BBB A->BB A->B - Set implicitRelationships = model.addImplicitRelationships(); - assertEquals(6, model.getRelationships().size()); - assertEquals(5, implicitRelationships.size()); - assertTrue(aa.hasEfferentRelationshipWith(bb)); - assertTrue(aa.hasEfferentRelationshipWith(b)); - assertTrue(a.hasEfferentRelationshipWith(bbb)); - assertTrue(a.hasEfferentRelationshipWith(bb)); - assertTrue(a.hasEfferentRelationshipWith(b)); - } - - @Test - public void test_addImplicitRelationships_WhenSourceAndDestinationAreContainersInDifferentSoftwareSystems() { - SoftwareSystem a = model.addSoftwareSystem("A", ""); - Container aa = a.addContainer("AA", "", ""); - - SoftwareSystem b = model.addSoftwareSystem("B", ""); - Container bb = b.addContainer("BB", "", ""); - - aa.uses(bb, "Uses"); - assertEquals(1, model.getRelationships().size()); - assertTrue(aa.hasEfferentRelationshipWith(bb)); - - // AA->BB implies AA->B A->BB A->B - Set implicitRelationships = model.addImplicitRelationships(); - assertEquals(4, model.getRelationships().size()); - assertEquals(3, implicitRelationships.size()); - assertTrue(aa.hasEfferentRelationshipWith(b)); - assertTrue(a.hasEfferentRelationshipWith(bb)); - assertTrue(a.hasEfferentRelationshipWith(b)); - } - - @Test - public void test_addImplicitRelationships_WhenSourceIsAContainerAndDestinationIsADifferentSoftwareSystem() { - SoftwareSystem a = model.addSoftwareSystem("A", ""); - Container aa = a.addContainer("AA", "", ""); - - SoftwareSystem b = model.addSoftwareSystem("B", ""); - - aa.uses(b, "Uses"); - assertEquals(1, model.getRelationships().size()); - assertTrue(aa.hasEfferentRelationshipWith(b)); - - // AA->B implies A->B - Set implicitRelationships = model.addImplicitRelationships(); - assertEquals(2, model.getRelationships().size()); - assertEquals(1, implicitRelationships.size()); - assertTrue(a.hasEfferentRelationshipWith(b)); - } - - @Test - public void test_addImplicitRelationships_WhenSourceIsASoftwareSystemAndDestinationIsAComponentInADifferentSoftwareSystem() { - SoftwareSystem a = model.addSoftwareSystem("A", ""); - - SoftwareSystem b = model.addSoftwareSystem("B", ""); - Container bb = b.addContainer("BB", "", ""); - Component bbb = bb.addComponent("BBB", "", ""); - - a.uses(bbb, "Uses"); - assertEquals(1, model.getRelationships().size()); - assertTrue(a.hasEfferentRelationshipWith(bbb)); - - // A->BBB implies A->BB A->B - Set implicitRelationships = model.addImplicitRelationships(); - assertEquals(3, model.getRelationships().size()); - assertEquals(2, implicitRelationships.size()); - assertTrue(a.hasEfferentRelationshipWith(bb)); - assertTrue(a.hasEfferentRelationshipWith(b)); - } - - @Test - public void test_addImplicitRelationships_WhenSourceIsASoftwareSystemAndDestinationIsAContainerInADifferentSoftwareSystem() { - SoftwareSystem a = model.addSoftwareSystem("A", ""); - - SoftwareSystem b = model.addSoftwareSystem("B", ""); - Container bb = b.addContainer("BB", "", ""); - - a.uses(bb, "Uses"); - assertEquals(1, model.getRelationships().size()); - assertTrue(a.hasEfferentRelationshipWith(bb)); - - // A->BB implies A->B - Set implicitRelationships = model.addImplicitRelationships(); - assertEquals(2, model.getRelationships().size()); - assertEquals(1, implicitRelationships.size()); - assertTrue(a.hasEfferentRelationshipWith(b)); - } - - @Test - public void test_addImplicitRelationships_WhenSourceAndDestinationAreDifferentSoftwareSystems() { - SoftwareSystem a = model.addSoftwareSystem("A", ""); - - SoftwareSystem b = model.addSoftwareSystem("B", ""); - - a.uses(b, "Uses"); - assertEquals(1, model.getRelationships().size()); - assertTrue(a.hasEfferentRelationshipWith(b)); - - Set implicitRelationships = model.addImplicitRelationships(); - assertEquals(1, model.getRelationships().size()); - assertEquals(0, implicitRelationships.size()); - } - - @Test - public void test_addImplicitRelationships_WhenSourceAndDestinationAreComponentsInTheSameContainer() { - SoftwareSystem a = model.addSoftwareSystem("A", ""); - Container aa = a.addContainer("AA", "", ""); - Component aaa1 = aa.addComponent("AAA1", "", ""); - Component aaa2 = aa.addComponent("AAA2", "", ""); - - aaa1.uses(aaa2, "Uses"); - assertEquals(1, model.getRelationships().size()); - assertTrue(aaa1.hasEfferentRelationshipWith(aaa2)); - - Set implicitRelationships = model.addImplicitRelationships(); - assertEquals(1, model.getRelationships().size()); - assertEquals(0, implicitRelationships.size()); - } - - @Test - public void test_addImplicitRelationships_WhenSourceAndDestinationAreContainersInTheSameContainer() { - SoftwareSystem a = model.addSoftwareSystem("A", ""); - Container aa1 = a.addContainer("AA1", "", ""); - Container aa2 = a.addContainer("AA2", "", ""); - - aa1.uses(aa2, "Uses"); - assertEquals(1, model.getRelationships().size()); - assertTrue(aa1.hasEfferentRelationshipWith(aa2)); - - Set implicitRelationships = model.addImplicitRelationships(); - assertEquals(1, model.getRelationships().size()); - assertEquals(0, implicitRelationships.size()); - } - - @Test - public void test_addImplicitRelationships_WhenSourceAndDestinationAreComponentsInTheDifferentContainersInTheSameSoftwareSystem() { - SoftwareSystem a = model.addSoftwareSystem("A", ""); - Container aa1 = a.addContainer("AA1", "", ""); - Container aa2 = a.addContainer("AA2", "", ""); - Component aaa1 = aa1.addComponent("AAA1", "", ""); - Component aaa2 = aa2.addComponent("AAA2", "", ""); - - aaa1.uses(aaa2, "Uses"); - assertEquals(1, model.getRelationships().size()); - assertTrue(aaa1.hasEfferentRelationshipWith(aaa2)); - - // AAA1->AAA2 implies AAA1->AA2 AA1->AAA2 AA1->AA2 - Set implicitRelationships = model.addImplicitRelationships(); - assertEquals(4, model.getRelationships().size()); - assertEquals(3, implicitRelationships.size()); - assertTrue(aaa1.hasEfferentRelationshipWith(aa2)); - assertTrue(aa1.hasEfferentRelationshipWith(aaa2)); - assertTrue(aa1.hasEfferentRelationshipWith(aa2)); - } - @Test public void test_addSoftwareSystemInstance_ThrowsAnException_WhenANullSoftwareSystemIsSpecified() { try { @@ -838,102 +592,6 @@ public void test_getElementWithCanonicalName_ReturnsTheElement_WhenAnElementWith assertSame(container, model.getElementWithCanonicalName("Container://Software System.Web Application")); } - @Test - public void test_addImplicitRelationships_SetsTheDescriptionOfThePropagatedRelationship_WhenThereIsOnlyOnePossibleDescription() { - Person user = model.addPerson("Person", "Description"); - SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); - Container webApplication = softwareSystem.addContainer("Web Application", "Description", "Technology"); - - user.uses(webApplication, "Uses", ""); - model.addImplicitRelationships(); - - assertEquals(2, user.getRelationships().size()); - - Relationship relationship = user.getRelationships().stream().filter(r -> r.getDestination() == softwareSystem).findFirst().get(); - assertEquals("Uses", relationship.getDescription()); - } - - @Test - public void test_addImplicitRelationships_DoeNotSetTheDescriptionOfThePropagatedRelationship_WhenThereIsMoreThanOnePossibleDescription() { - Person user = model.addPerson("Person", "Description"); - SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); - Container webApplication = softwareSystem.addContainer("Web Application", "Description", "Technology"); - - user.uses(webApplication, "Does something", ""); - user.uses(webApplication, "Does something else", ""); - model.addImplicitRelationships(); - - assertEquals(3, user.getRelationships().size()); - - Relationship relationship = user.getRelationships().stream().filter(r -> r.getDestination() == softwareSystem).findFirst().get(); - assertEquals("", relationship.getDescription()); - } - - @Test - public void test_addImplicitRelationships_SetsTheTechnologyOfThePropagatedRelationship_WhenThereIsOnlyOnePossibleTechnology() { - Person user = model.addPerson("Person", "Description"); - SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); - Container webApplication = softwareSystem.addContainer("Web Application", "Description", "Technology"); - - user.uses(webApplication, "Uses", "HTTPS"); - model.addImplicitRelationships(); - - assertEquals(2, user.getRelationships().size()); - - Relationship relationship = user.getRelationships().stream().filter(r -> r.getDestination() == softwareSystem).findFirst().get(); - assertEquals("HTTPS", relationship.getTechnology()); - } - - @Test - public void test_addImplicitRelationships_DoeNotSetTheTechnologyOfThePropagatedRelationship_WhenThereIsMoreThanOnePossibleTechnology() { - Person user = model.addPerson("Person", "Description"); - SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); - Container webApplication = softwareSystem.addContainer("Web Application", "Description", "Technology"); - - user.uses(webApplication, "Does something", "Some technology"); - user.uses(webApplication, "Does something else", "Some other technology"); - model.addImplicitRelationships(); - - assertEquals(3, user.getRelationships().size()); - - Relationship relationship = user.getRelationships().stream().filter(r -> r.getDestination() == softwareSystem).findFirst().get(); - assertEquals("", relationship.getTechnology()); - } - - @Test - public void test_addImplicitRelationships_PropagatesAsyncRelationship_IfThereAreAMixOfInteractionStyles() { - SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); - SoftwareSystem softwareSystemExternal = model.addSoftwareSystem("External system", ""); - Container service1 = softwareSystem.addContainer("Service1", "", "Technology1"); - Container service2 = softwareSystem.addContainer("Service2", "", "Technology2"); - - softwareSystemExternal.uses(service1, "Sends an event to", "AMQP", InteractionStyle.Asynchronous); - softwareSystemExternal.uses(service2, "Sends a command to", "HTTPS", InteractionStyle.Synchronous); - - model.addImplicitRelationships(); - assertEquals(3, softwareSystemExternal.getRelationships().size()); - Relationship relationship = softwareSystemExternal.getRelationships().stream().filter(r -> r.getDestination() == - softwareSystem).findFirst().get(); - assertEquals(InteractionStyle.Asynchronous, relationship.getInteractionStyle()); - } - - @Test - public void test_addImplicitRelationships_PropagatesAsyncRelationship_IfThereAreOnlyAsyncInteractionStyles() { - SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); - SoftwareSystem softwareSystemExternal = model.addSoftwareSystem("External system", ""); - Container service1 = softwareSystem.addContainer("Service1", "", "Technology1"); - Container service2 = softwareSystem.addContainer("Service2", "", "Technology2"); - - softwareSystemExternal.uses(service1, "Sends an event to", "AMQP", InteractionStyle.Asynchronous); - softwareSystemExternal.uses(service2, "Sends an event to", "Kafka", InteractionStyle.Asynchronous); - - model.addImplicitRelationships(); - assertEquals(3, softwareSystemExternal.getRelationships().size()); - Relationship relationship = softwareSystemExternal.getRelationships().stream().filter(r -> r.getDestination() == - softwareSystem).findFirst().get(); - assertEquals(InteractionStyle.Asynchronous, relationship.getInteractionStyle()); - } - @Test public void test_addDeploymentNode_ThrowsAnException_WhenADeploymentNodeWithTheSameNameAlreadyExists() { model.addDeploymentNode("Amazon AWS", "Description", "Technology"); From f270ad4d19b5f5d602e7f79f71ff876e475f0e59 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 23 Feb 2022 14:32:16 +0000 Subject: [PATCH 280/717] Removed examples ... they're moving to a separate repo. --- structurizr-examples/build.gradle | 7 - structurizr-examples/etc/logging.properties | 4 - .../example/AmazonWebServicesExample.java | 79 ------- .../example/Arc42DocumentationExample.java | 81 ------- ...AutomaticDocumentationTemplateExample.java | 55 ----- .../example/ClientSideEncryption.java | 49 ---- .../example/CorporateBranding.java | 61 ----- .../structurizr/example/EmptyWorkspace.java | 22 -- .../structurizr/example/FilteredViews.java | 54 ----- .../example/FinancialRiskSystem.java | 87 ------- .../structurizr/example/GettingStarted.java | 52 ----- .../structurizr/example/HttpHealthChecks.java | 57 ----- .../example/MicroservicesExample.java | 98 -------- .../src/com/structurizr/example/Shapes.java | 66 ------ .../StructurizrDocumentationExample.java | 81 ------- .../structurizr/example/StylingElements.java | 60 ----- .../example/StylingRelationships.java | 54 ----- .../src/com/structurizr/example/Theme.java | 42 ---- ...tsAndPerspectivesDocumentationExample.java | 71 ------ .../structurizr/example/WidgetsLimited.java | 86 ------- .../example/bigbankplc/BigBankPlc.java | 74 ------ .../bigbankplc/InternetBankingSystem.java | 221 ------------------ .../example/bigbankplc/SystemLandscape.java | 39 ---- .../0001-record-architecture-decisions.md | 19 -- .../internetbankingsystem/docs/01-context.md | 11 - .../docs/02-containers.md | 21 -- .../docs/03-development-environment.adoc | 5 - .../docs/04-deployment.adoc | 5 - .../adr/0001-record-architecture-decisions.md | 22 -- .../adr/0002-implement-as-shell-scripts.md | 31 --- .../0003-single-command-with-subcommands.md | 48 ---- .../documentation/adr/0004-markdown-format.md | 43 ---- .../documentation/adr/0005-help-comments.md | 43 ---- ...n-in-other-version-control-repositories.md | 44 ---- ...-config-executable-to-get-configuration.md | 34 --- .../adr/0008-use-iso-8601-format-for-dates.md | 46 ---- .../asciidoc/01-introduction-and-goals.adoc | 7 - .../asciidoc/02-architecture-constraints.adoc | 1 - .../asciidoc/03-system-scope-and-context.adoc | 8 - .../arc42/asciidoc/04-solution-strategy.adoc | 1 - .../asciidoc/05-building-block-view.adoc | 7 - .../arc42/asciidoc/06-runtime-view.adoc | 2 - .../arc42/asciidoc/07-deployment-view.adoc | 1 - .../asciidoc/08-crosscutting-concepts.adoc | 2 - .../asciidoc/09-architecture-decisions.adoc | 2 - .../asciidoc/10-quality-requirements.adoc | 6 - .../asciidoc/11-risks-and-technical-debt.adoc | 1 - .../arc42/asciidoc/12-glossary.adoc | 1 - .../markdown/01-introduction-and-goals.md | 7 - .../markdown/02-architecture-constraints.md | 1 - .../markdown/03-system-scope-and-context.md | 8 - .../arc42/markdown/04-solution-strategy.md | 1 - .../arc42/markdown/05-building-block-view.md | 7 - .../arc42/markdown/06-runtime-view.md | 2 - .../arc42/markdown/07-deployment-view.md | 1 - .../markdown/08-crosscutting-concepts.md | 2 - .../markdown/09-architecture-decisions.md | 2 - .../arc42/markdown/10-quality-requirements.md | 6 - .../markdown/11-risks-and-technical-debt.md | 1 - .../arc42/markdown/12-glossary.md | 1 - .../documentation/automatic/01-context.md | 3 - .../automatic/02-functional-overview.md | 1 - .../automatic/03-quality-attributes.md | 1 - .../documentation/automatic/04-constraints.md | 1 - .../documentation/automatic/05-principles.md | 1 - .../automatic/06-software-architecture.md | 1 - .../documentation/automatic/07-data.adoc | 1 - .../08-infrastructure-architecture.adoc | 1 - .../automatic/09-deployment.adoc | 1 - .../automatic/10-development-environment.adoc | 1 - .../automatic/11-operation-and-support.adoc | 1 - .../automatic/12-decision-log.adoc | 1 - .../structurizr/asciidoc/01-context.adoc | 3 - .../asciidoc/02-functional-overview.adoc | 1 - .../asciidoc/03-quality-attributes.adoc | 1 - .../structurizr/asciidoc/04-constraints.adoc | 1 - .../structurizr/asciidoc/05-principles.adoc | 1 - .../asciidoc/06-software-architecture.adoc | 1 - .../structurizr/asciidoc/07-data.adoc | 1 - .../08-infrastructure-architecture.adoc | 1 - .../structurizr/asciidoc/09-deployment.adoc | 1 - .../asciidoc/10-development-environment.adoc | 1 - .../asciidoc/11-operation-and-support.adoc | 1 - .../structurizr/asciidoc/12-decision-log.adoc | 1 - .../structurizr/markdown/01-context.md | 3 - .../markdown/02-functional-overview.md | 1 - .../markdown/03-quality-attributes.md | 1 - .../structurizr/markdown/04-constraints.md | 1 - .../structurizr/markdown/05-principles.md | 1 - .../markdown/06-software-architecture.md | 1 - .../structurizr/markdown/07-data.md | 1 - .../08-infrastructure-architecture.md | 1 - .../structurizr/markdown/09-deployment.md | 1 - .../markdown/10-development-environment.md | 1 - .../markdown/11-operation-and-support.md | 1 - .../structurizr/markdown/12-decision-log.md | 1 - .../asciidoc/01-introduction.adoc | 9 - .../asciidoc/02-glossary.adoc | 1 - ...-system-stakeholders-and-requirements.adoc | 7 - .../asciidoc/04-architectural-forces.adoc | 7 - .../01-architectural-views.adoc | 1 - .../02-context-view.adoc | 3 - .../03-functional-view.adoc | 1 - .../04-information-view.adoc | 1 - .../05-concurrency-view.adoc | 1 - .../06-deployment-view.adoc | 1 - .../07-development-view.adoc | 1 - .../08-operational-view.adoc | 1 - .../asciidoc/06-system-qualities.adoc | 11 - .../asciidoc/07-appendices.adoc | 7 - .../markdown/01-introduction.md | 9 - .../markdown/02-glossary.md | 1 - ...03-system-stakeholders-and-requirements.md | 7 - .../markdown/04-architectural-forces.md | 7 - .../01-architectural-views.md | 1 - .../05-architectural-views/02-context-view.md | 3 - .../03-functional-view.md | 1 - .../04-information-view.md | 1 - .../05-concurrency-view.md | 1 - .../06-deployment-view.md | 1 - .../07-development-view.md | 1 - .../08-operational-view.md | 1 - .../markdown/06-system-qualities.md | 11 - .../markdown/07-appendices.md | 7 - .../example/financialrisksystem/01-context.md | 18 -- .../02-functional-overview.md | 13 -- .../03-quality-attributes.md | 61 ----- .../images/functional-overview.png | Bin 38234 -> 0 bytes .../com/structurizr/example/theme/theme.json | 16 -- 129 files changed, 2204 deletions(-) delete mode 100644 structurizr-examples/build.gradle delete mode 100644 structurizr-examples/etc/logging.properties delete mode 100644 structurizr-examples/src/com/structurizr/example/AmazonWebServicesExample.java delete mode 100644 structurizr-examples/src/com/structurizr/example/Arc42DocumentationExample.java delete mode 100644 structurizr-examples/src/com/structurizr/example/AutomaticDocumentationTemplateExample.java delete mode 100644 structurizr-examples/src/com/structurizr/example/ClientSideEncryption.java delete mode 100644 structurizr-examples/src/com/structurizr/example/CorporateBranding.java delete mode 100644 structurizr-examples/src/com/structurizr/example/EmptyWorkspace.java delete mode 100644 structurizr-examples/src/com/structurizr/example/FilteredViews.java delete mode 100644 structurizr-examples/src/com/structurizr/example/FinancialRiskSystem.java delete mode 100644 structurizr-examples/src/com/structurizr/example/GettingStarted.java delete mode 100644 structurizr-examples/src/com/structurizr/example/HttpHealthChecks.java delete mode 100644 structurizr-examples/src/com/structurizr/example/MicroservicesExample.java delete mode 100644 structurizr-examples/src/com/structurizr/example/Shapes.java delete mode 100644 structurizr-examples/src/com/structurizr/example/StructurizrDocumentationExample.java delete mode 100644 structurizr-examples/src/com/structurizr/example/StylingElements.java delete mode 100644 structurizr-examples/src/com/structurizr/example/StylingRelationships.java delete mode 100644 structurizr-examples/src/com/structurizr/example/Theme.java delete mode 100644 structurizr-examples/src/com/structurizr/example/ViewpointsAndPerspectivesDocumentationExample.java delete mode 100644 structurizr-examples/src/com/structurizr/example/WidgetsLimited.java delete mode 100644 structurizr-examples/src/com/structurizr/example/bigbankplc/BigBankPlc.java delete mode 100644 structurizr-examples/src/com/structurizr/example/bigbankplc/InternetBankingSystem.java delete mode 100644 structurizr-examples/src/com/structurizr/example/bigbankplc/SystemLandscape.java delete mode 100644 structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/adrs/0001-record-architecture-decisions.md delete mode 100644 structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/docs/01-context.md delete mode 100644 structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/docs/02-containers.md delete mode 100644 structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/docs/03-development-environment.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/docs/04-deployment.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/adr/0001-record-architecture-decisions.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/adr/0002-implement-as-shell-scripts.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/adr/0003-single-command-with-subcommands.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/adr/0004-markdown-format.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/adr/0005-help-comments.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/adr/0006-packaging-and-distribution-in-other-version-control-repositories.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/adr/0007-invoke-adr-config-executable-to-get-configuration.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/adr/0008-use-iso-8601-format-for-dates.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/01-introduction-and-goals.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/02-architecture-constraints.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/03-system-scope-and-context.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/04-solution-strategy.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/05-building-block-view.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/06-runtime-view.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/07-deployment-view.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/08-crosscutting-concepts.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/09-architecture-decisions.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/10-quality-requirements.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/11-risks-and-technical-debt.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/12-glossary.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/01-introduction-and-goals.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/02-architecture-constraints.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/03-system-scope-and-context.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/04-solution-strategy.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/05-building-block-view.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/06-runtime-view.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/07-deployment-view.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/08-crosscutting-concepts.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/09-architecture-decisions.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/10-quality-requirements.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/11-risks-and-technical-debt.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/12-glossary.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/automatic/01-context.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/automatic/02-functional-overview.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/automatic/03-quality-attributes.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/automatic/04-constraints.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/automatic/05-principles.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/automatic/06-software-architecture.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/automatic/07-data.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/automatic/08-infrastructure-architecture.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/automatic/09-deployment.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/automatic/10-development-environment.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/automatic/11-operation-and-support.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/automatic/12-decision-log.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/01-context.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/02-functional-overview.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/03-quality-attributes.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/04-constraints.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/05-principles.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/06-software-architecture.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/07-data.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/08-infrastructure-architecture.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/09-deployment.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/10-development-environment.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/11-operation-and-support.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/12-decision-log.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/01-context.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/02-functional-overview.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/03-quality-attributes.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/04-constraints.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/05-principles.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/06-software-architecture.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/07-data.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/08-infrastructure-architecture.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/09-deployment.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/10-development-environment.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/11-operation-and-support.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/12-decision-log.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/01-introduction.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/02-glossary.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/03-system-stakeholders-and-requirements.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/04-architectural-forces.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/05-architectural-views/01-architectural-views.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/05-architectural-views/02-context-view.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/05-architectural-views/03-functional-view.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/05-architectural-views/04-information-view.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/05-architectural-views/05-concurrency-view.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/05-architectural-views/06-deployment-view.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/05-architectural-views/07-development-view.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/05-architectural-views/08-operational-view.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/06-system-qualities.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/07-appendices.adoc delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/01-introduction.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/02-glossary.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/03-system-stakeholders-and-requirements.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/04-architectural-forces.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/05-architectural-views/01-architectural-views.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/05-architectural-views/02-context-view.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/05-architectural-views/03-functional-view.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/05-architectural-views/04-information-view.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/05-architectural-views/05-concurrency-view.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/05-architectural-views/06-deployment-view.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/05-architectural-views/07-development-view.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/05-architectural-views/08-operational-view.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/06-system-qualities.md delete mode 100644 structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/07-appendices.md delete mode 100644 structurizr-examples/src/com/structurizr/example/financialrisksystem/01-context.md delete mode 100644 structurizr-examples/src/com/structurizr/example/financialrisksystem/02-functional-overview.md delete mode 100644 structurizr-examples/src/com/structurizr/example/financialrisksystem/03-quality-attributes.md delete mode 100644 structurizr-examples/src/com/structurizr/example/financialrisksystem/images/functional-overview.png delete mode 100644 structurizr-examples/src/com/structurizr/example/theme/theme.json diff --git a/structurizr-examples/build.gradle b/structurizr-examples/build.gradle deleted file mode 100644 index fb8c88399..000000000 --- a/structurizr-examples/build.gradle +++ /dev/null @@ -1,7 +0,0 @@ -dependencies { - compile project(':structurizr-client') - compile 'com.structurizr:structurizr-adr-tools:1.3.7' - - compile 'org.slf4j:slf4j-api:1.7.21' - compile 'org.slf4j:slf4j-simple:1.7.21' -} \ No newline at end of file diff --git a/structurizr-examples/etc/logging.properties b/structurizr-examples/etc/logging.properties deleted file mode 100644 index cb7270348..000000000 --- a/structurizr-examples/etc/logging.properties +++ /dev/null @@ -1,4 +0,0 @@ -handlers=java.util.logging.ConsoleHandler -java.util.logging.ConsoleHandler.level=ALL - -com.structurizr.level=ALL \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/AmazonWebServicesExample.java b/structurizr-examples/src/com/structurizr/example/AmazonWebServicesExample.java deleted file mode 100644 index 0f54412a2..000000000 --- a/structurizr-examples/src/com/structurizr/example/AmazonWebServicesExample.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.structurizr.example; - -import com.structurizr.Workspace; -import com.structurizr.api.StructurizrClient; -import com.structurizr.model.*; -import com.structurizr.view.*; - -/** - * An example of how to document an AWS deployment. - * - * The live workspace is available to view at https://structurizr.com/share/54915 - */ -public class AmazonWebServicesExample { - - private static final long WORKSPACE_ID = 54915; - private static final String API_KEY = ""; - private static final String API_SECRET = ""; - - private static final String SPRING_BOOT_TAG = "Spring Boot Application"; - private static final String DATABASE_TAG = "Database"; - - public static void main(String[] args) throws Exception { - Workspace workspace = new Workspace("Amazon Web Services Example", "An example AWS deployment architecture."); - Model model = workspace.getModel(); - - SoftwareSystem softwareSystem = model.addSoftwareSystem("Spring PetClinic", "Allows employees to view and manage information regarding the veterinarians, the clients, and their pets."); - Container webApplication = softwareSystem.addContainer("Web Application", "Allows employees to view and manage information regarding the veterinarians, the clients, and their pets.", "Java and Spring Boot"); - webApplication.addTags(SPRING_BOOT_TAG); - Container database = softwareSystem.addContainer("Database", "Stores information regarding the veterinarians, the clients, and their pets.", "Relational database schema"); - database.addTags(DATABASE_TAG); - - webApplication.uses(database, "Reads from and writes to", "JDBC/SSL"); - - DeploymentNode amazonWebServices = model.addDeploymentNode("Amazon Web Services"); - amazonWebServices.addTags("Amazon Web Services - Cloud"); - DeploymentNode amazonRegion = amazonWebServices.addDeploymentNode("US-East-1"); - amazonRegion.addTags("Amazon Web Services - Region"); - DeploymentNode autoscalingGroup = amazonRegion.addDeploymentNode("Autoscaling group"); - autoscalingGroup.addTags("Amazon Web Services - Auto Scaling"); - DeploymentNode ec2 = autoscalingGroup.addDeploymentNode("Amazon EC2"); - ec2.addTags("Amazon Web Services - EC2"); - ContainerInstance webApplicationInstance = ec2.add(webApplication); - - InfrastructureNode route53 = amazonRegion.addInfrastructureNode("Route 53"); - route53.addTags("Amazon Web Services - Route 53"); - - InfrastructureNode elb = amazonRegion.addInfrastructureNode("Elastic Load Balancer"); - elb.addTags("Amazon Web Services - Elastic Load Balancing"); - - route53.uses(elb, "Forwards requests to", "HTTPS"); - elb.uses(webApplicationInstance, "Forwards requests to", "HTTPS"); - - DeploymentNode rds = amazonRegion.addDeploymentNode("Amazon RDS"); - rds.addTags("Amazon Web Services - RDS"); - DeploymentNode mySql = rds.addDeploymentNode("MySQL"); - mySql.addTags("Amazon Web Services - RDS_MySQL_instance"); - ContainerInstance databaseInstance = mySql.add(database); - - ViewSet views = workspace.getViews(); - DeploymentView deploymentView = views.createDeploymentView(softwareSystem, "AmazonWebServicesDeployment", "An example deployment diagram."); - deploymentView.addAllDeploymentNodes(); - - deploymentView.addAnimation(route53); - deploymentView.addAnimation(elb); - deploymentView.addAnimation(webApplicationInstance); - deploymentView.addAnimation(databaseInstance); - - Styles styles = views.getConfiguration().getStyles(); - styles.addElementStyle(SPRING_BOOT_TAG).shape(Shape.RoundedBox).background("#ffffff"); - styles.addElementStyle(DATABASE_TAG).shape(Shape.Cylinder).background("#ffffff"); - styles.addElementStyle(Tags.INFRASTRUCTURE_NODE).shape(Shape.RoundedBox).background("#ffffff"); - - views.getConfiguration().setThemes("https://raw.githubusercontent.com/structurizr/themes/master/amazon-web-services/theme.json"); - - StructurizrClient structurizrClient = new StructurizrClient(API_KEY, API_SECRET); - structurizrClient.putWorkspace(WORKSPACE_ID, workspace); - } - -} \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/Arc42DocumentationExample.java b/structurizr-examples/src/com/structurizr/example/Arc42DocumentationExample.java deleted file mode 100644 index 2284b8f49..000000000 --- a/structurizr-examples/src/com/structurizr/example/Arc42DocumentationExample.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.structurizr.example; - -import com.structurizr.Workspace; -import com.structurizr.api.StructurizrClient; -import com.structurizr.documentation.Arc42DocumentationTemplate; -import com.structurizr.documentation.Format; -import com.structurizr.model.Model; -import com.structurizr.model.Person; -import com.structurizr.model.SoftwareSystem; -import com.structurizr.model.Tags; -import com.structurizr.view.Shape; -import com.structurizr.view.Styles; -import com.structurizr.view.SystemContextView; -import com.structurizr.view.ViewSet; - -import java.io.File; - -/** - * An empty software architecture document using the arc42 template. - * - * See https://structurizr.com/share/27791/documentation for the live version. - */ -public class Arc42DocumentationExample { - - private static final long WORKSPACE_ID = 27791; - private static final String API_KEY = "key"; - private static final String API_SECRET = "secret"; - - public static void main(String[] args) throws Exception { - Workspace workspace = new Workspace("Documentation - arc42", "An empty software architecture document using the arc42 template."); - Model model = workspace.getModel(); - ViewSet views = workspace.getViews(); - - Person user = model.addPerson("User", "A user of my software system."); - SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "My software system."); - user.uses(softwareSystem, "Uses"); - - SystemContextView contextView = views.createSystemContextView(softwareSystem, "SystemContext", "An example of a System Context diagram."); - contextView.addAllSoftwareSystems(); - contextView.addAllPeople(); - - Styles styles = views.getConfiguration().getStyles(); - styles.addElementStyle(Tags.PERSON).shape(Shape.Person); - - Arc42DocumentationTemplate template = new Arc42DocumentationTemplate(workspace); - - // this is the Markdown version - File documentationRoot = new File("./structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown"); - template.addIntroductionAndGoalsSection(softwareSystem, new File(documentationRoot, "01-introduction-and-goals.md")); - template.addConstraintsSection(softwareSystem, new File(documentationRoot, "02-architecture-constraints.md")); - template.addContextAndScopeSection(softwareSystem, new File(documentationRoot, "03-system-scope-and-context.md")); - template.addSolutionStrategySection(softwareSystem, new File(documentationRoot, "04-solution-strategy.md")); - template.addBuildingBlockViewSection(softwareSystem, new File(documentationRoot, "05-building-block-view.md")); - template.addRuntimeViewSection(softwareSystem, new File(documentationRoot, "06-runtime-view.md")); - template.addDeploymentViewSection(softwareSystem, new File(documentationRoot, "07-deployment-view.md")); - template.addCrosscuttingConceptsSection(softwareSystem, new File(documentationRoot, "08-crosscutting-concepts.md")); - template.addArchitecturalDecisionsSection(softwareSystem, new File(documentationRoot, "09-architecture-decisions.md")); - template.addRisksAndTechnicalDebtSection(softwareSystem, new File(documentationRoot, "10-quality-requirements.md")); - template.addQualityRequirementsSection(softwareSystem, new File(documentationRoot, "11-risks-and-technical-debt.md")); - template.addGlossarySection(softwareSystem, new File(documentationRoot, "12-glossary.md")); - - // this is the AsciiDoc version -// File documentationRoot = new File("./structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc"); -// template.addIntroductionAndGoalsSection(softwareSystem, new File(documentationRoot, "01-introduction-and-goals.adoc")); -// template.addConstraintsSection(softwareSystem, new File(documentationRoot, "02-architecture-constraints.adoc")); -// template.addContextAndScopeSection(softwareSystem, new File(documentationRoot, "03-system-scope-and-context.adoc")); -// template.addSolutionStrategySection(softwareSystem, new File(documentationRoot, "04-solution-strategy.adoc")); -// template.addBuildingBlockViewSection(softwareSystem, new File(documentationRoot, "05-building-block-view.adoc")); -// template.addRuntimeViewSection(softwareSystem, new File(documentationRoot, "06-runtime-view.adoc")); -// template.addDeploymentViewSection(softwareSystem, new File(documentationRoot, "07-deployment-view.adoc")); -// template.addCrosscuttingConceptsSection(softwareSystem, new File(documentationRoot, "08-crosscutting-concepts.adoc")); -// template.addArchitecturalDecisionsSection(softwareSystem, new File(documentationRoot, "09-architecture-decisions.adoc")); -// template.addRisksAndTechnicalDebtSection(softwareSystem, new File(documentationRoot, "10-quality-requirements.adoc")); -// template.addQualityRequirementsSection(softwareSystem, new File(documentationRoot, "11-risks-and-technical-debt.adoc")); -// template.addGlossarySection(softwareSystem, new File(documentationRoot, "12-glossary.adoc")); - - StructurizrClient structurizrClient = new StructurizrClient(API_KEY, API_SECRET); - structurizrClient.putWorkspace(WORKSPACE_ID, workspace); - } - -} \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/AutomaticDocumentationTemplateExample.java b/structurizr-examples/src/com/structurizr/example/AutomaticDocumentationTemplateExample.java deleted file mode 100644 index 0a29a5f39..000000000 --- a/structurizr-examples/src/com/structurizr/example/AutomaticDocumentationTemplateExample.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.structurizr.example; - -import com.structurizr.Workspace; -import com.structurizr.api.StructurizrClient; -import com.structurizr.documentation.AutomaticDocumentationTemplate; -import com.structurizr.model.Model; -import com.structurizr.model.Person; -import com.structurizr.model.SoftwareSystem; -import com.structurizr.model.Tags; -import com.structurizr.view.Shape; -import com.structurizr.view.Styles; -import com.structurizr.view.SystemContextView; -import com.structurizr.view.ViewSet; - -import java.io.File; - -/** - * An empty software architecture document based upon the Structurizr template, - * created with the automatic document template. - * - * See https://structurizr.com/share/35971/documentation for the live version. - */ -public class AutomaticDocumentationTemplateExample { - - private static final long WORKSPACE_ID = 35971; - private static final String API_KEY = "key"; - private static final String API_SECRET = "secret"; - - public static void main(String[] args) throws Exception { - Workspace workspace = new Workspace("Documentation - Automatic", "An empty software architecture document using the Structurizr template."); - Model model = workspace.getModel(); - ViewSet views = workspace.getViews(); - - Person user = model.addPerson("User", "A user of my software system."); - SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "My software system."); - user.uses(softwareSystem, "Uses"); - - SystemContextView contextView = views.createSystemContextView(softwareSystem, "SystemContext", "An example of a System Context diagram."); - contextView.addAllSoftwareSystems(); - contextView.addAllPeople(); - - Styles styles = views.getConfiguration().getStyles(); - styles.addElementStyle(Tags.PERSON).shape(Shape.Person); - - // this directory includes a mix of Markdown and AsciiDoc files - File documentationRoot = new File("./structurizr-examples/src/com/structurizr/example/documentation/automatic"); - - AutomaticDocumentationTemplate template = new AutomaticDocumentationTemplate(workspace); - template.addSections(softwareSystem, documentationRoot); - - StructurizrClient structurizrClient = new StructurizrClient(API_KEY, API_SECRET); - structurizrClient.putWorkspace(WORKSPACE_ID, workspace); - } - -} \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/ClientSideEncryption.java b/structurizr-examples/src/com/structurizr/example/ClientSideEncryption.java deleted file mode 100644 index 34bccf231..000000000 --- a/structurizr-examples/src/com/structurizr/example/ClientSideEncryption.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.structurizr.example; - -import com.structurizr.Workspace; -import com.structurizr.api.StructurizrClient; -import com.structurizr.encryption.AesEncryptionStrategy; -import com.structurizr.model.Model; -import com.structurizr.model.Person; -import com.structurizr.model.SoftwareSystem; -import com.structurizr.model.Tags; -import com.structurizr.view.Shape; -import com.structurizr.view.Styles; -import com.structurizr.view.SystemContextView; -import com.structurizr.view.ViewSet; - -/** - * This is an example of how to use client-side encryption. - * - * You can see the workspace online at https://structurizr.com/share/41 - * (the passphrase is "password") - */ -public class ClientSideEncryption { - - private static final long WORKSPACE_ID = 41; - private static final String API_KEY = "key"; - private static final String API_SECRET = "secret"; - - public static void main(String[] args) throws Exception { - Workspace workspace = new Workspace("Client-side encrypted workspace", "This is a client-side encrypted workspace. The passphrase is 'password'."); - Model model = workspace.getModel(); - - Person user = model.addPerson("User", "A user of my software system."); - SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "My software system."); - user.uses(softwareSystem, "Uses"); - - ViewSet views = workspace.getViews(); - SystemContextView contextView = views.createSystemContextView(softwareSystem, "SystemContext", "An example of a System Context diagram."); - contextView.addAllSoftwareSystems(); - contextView.addAllPeople(); - - Styles styles = views.getConfiguration().getStyles(); - styles.addElementStyle(Tags.SOFTWARE_SYSTEM).background("#d34407").color("#ffffff"); - styles.addElementStyle(Tags.PERSON).background("#f86628").color("#ffffff").shape(Shape.Person); - - StructurizrClient structurizrClient = new StructurizrClient(API_KEY, API_SECRET); - structurizrClient.setEncryptionStrategy(new AesEncryptionStrategy("password")); - structurizrClient.putWorkspace(WORKSPACE_ID, workspace); - } - -} \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/CorporateBranding.java b/structurizr-examples/src/com/structurizr/example/CorporateBranding.java deleted file mode 100644 index 4240b0024..000000000 --- a/structurizr-examples/src/com/structurizr/example/CorporateBranding.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.structurizr.example; - -import com.structurizr.Workspace; -import com.structurizr.api.StructurizrClient; -import com.structurizr.documentation.Format; -import com.structurizr.documentation.StructurizrDocumentationTemplate; -import com.structurizr.model.Model; -import com.structurizr.model.Person; -import com.structurizr.model.SoftwareSystem; -import com.structurizr.model.Tags; -import com.structurizr.util.ImageUtils; -import com.structurizr.view.*; - -import java.io.File; - -/** - *

    - * This is a simple example that illustrates the corporate branding features of Structurizr, including: - *

    - * - *
      - *
    • A logo, which is included in diagrams and documentation.
    • - *
    - * - *

    - * You can see the live workspace at https://structurizr.com/share/35031 - *

    - */ -public class CorporateBranding { - - private static final long WORKSPACE_ID = 35031; - private static final String API_KEY = ""; - private static final String API_SECRET = ""; - - public static void main(String[] args) throws Exception { - Workspace workspace = new Workspace("Corporate Branding", "This is a model of my software system."); - Model model = workspace.getModel(); - - Person user = model.addPerson("User", "A user of my software system."); - SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "My software system."); - user.uses(softwareSystem, "Uses"); - - ViewSet views = workspace.getViews(); - SystemContextView contextView = views.createSystemContextView(softwareSystem, "SystemContext", "An example of a System Context diagram."); - contextView.addAllSoftwareSystems(); - contextView.addAllPeople(); - - Styles styles = views.getConfiguration().getStyles(); - styles.addElementStyle(Tags.PERSON).shape(Shape.Person); - - StructurizrDocumentationTemplate template = new StructurizrDocumentationTemplate(workspace); - template.addContextSection(softwareSystem, Format.Markdown, "Here is some context about the software system...\n\n![](embed:SystemContext)"); - - Branding branding = views.getConfiguration().getBranding(); - branding.setLogo(ImageUtils.getImageAsDataUri(new File("./docs/images/structurizr-logo.png"))); - - StructurizrClient structurizrClient = new StructurizrClient(API_KEY, API_SECRET); - structurizrClient.putWorkspace(WORKSPACE_ID, workspace); - } - -} \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/EmptyWorkspace.java b/structurizr-examples/src/com/structurizr/example/EmptyWorkspace.java deleted file mode 100644 index 17d44fcb7..000000000 --- a/structurizr-examples/src/com/structurizr/example/EmptyWorkspace.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.structurizr.example; - -import com.structurizr.Workspace; -import com.structurizr.api.StructurizrClient; - -/** - * An example of uploading an empty workspace, for when you want to use - * Structurizr's local storage mode. - */ -public class EmptyWorkspace { - - private static final long WORKSPACE_ID = 123456; - private static final String API_KEY = ""; - private static final String API_SECRET = ""; - - public static void main(String[] args) throws Exception { - Workspace workspace = new Workspace("Name", "Description"); - StructurizrClient structurizrClient = new StructurizrClient(API_KEY, API_SECRET); - structurizrClient.putWorkspace(WORKSPACE_ID, workspace); - } - -} diff --git a/structurizr-examples/src/com/structurizr/example/FilteredViews.java b/structurizr-examples/src/com/structurizr/example/FilteredViews.java deleted file mode 100644 index a605f7783..000000000 --- a/structurizr-examples/src/com/structurizr/example/FilteredViews.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.structurizr.example; - -import com.structurizr.Workspace; -import com.structurizr.api.StructurizrClient; -import com.structurizr.model.Model; -import com.structurizr.model.Person; -import com.structurizr.model.SoftwareSystem; -import com.structurizr.model.Tags; -import com.structurizr.view.*; - -/** - * An example of how to use filtered views to show "before" and "after" views of a software system. - * - * You can see the live diagrams at https://structurizr.com/public/19911 - */ -public class FilteredViews { - - private static final long WORKSPACE_ID = 19911; - private static final String API_KEY = "key"; - private static final String API_SECRET = "secret"; - - private static final String CURRENT_STATE = "Current State"; - private static final String FUTURE_STATE = "Future State"; - - public static void main(String[] args) throws Exception { - Workspace workspace = new Workspace("Filtered Views", "An example of using filtered views."); - Model model = workspace.getModel(); - - Person user = model.addPerson("User", "A description of the user."); - SoftwareSystem softwareSystemA = model.addSoftwareSystem("Software System A", "A description of software system A."); - SoftwareSystem softwareSystemB = model.addSoftwareSystem("Software System B", "A description of software system B."); - softwareSystemB.addTags(FUTURE_STATE); - - user.uses(softwareSystemA, "Uses for tasks 1 and 2").addTags(CURRENT_STATE); - user.uses(softwareSystemA, "Uses for task 1").addTags(FUTURE_STATE); - user.uses(softwareSystemB, "Uses for task 2").addTags(FUTURE_STATE); - - ViewSet views = workspace.getViews(); - SystemLandscapeView systemLandscapeView = views.createSystemLandscapeView("SystemLandscape", "An example System Landscape diagram."); - systemLandscapeView.addAllElements(); - - views.createFilteredView(systemLandscapeView, "CurrentState", "The current system landscape.", FilterMode.Exclude, FUTURE_STATE); - views.createFilteredView(systemLandscapeView, "FutureState", "The future state system landscape after Software System B is live.", FilterMode.Exclude, CURRENT_STATE); - - Styles styles = views.getConfiguration().getStyles(); - styles.addElementStyle(Tags.ELEMENT).color("#ffffff"); - styles.addElementStyle(Tags.SOFTWARE_SYSTEM).background("#91a437").shape(Shape.RoundedBox); - styles.addElementStyle(Tags.PERSON).background("#6a7b15").shape(Shape.Person); - - StructurizrClient structurizrClient = new StructurizrClient(API_KEY, API_SECRET); - structurizrClient.putWorkspace(WORKSPACE_ID, workspace); - } - -} \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/FinancialRiskSystem.java b/structurizr-examples/src/com/structurizr/example/FinancialRiskSystem.java deleted file mode 100644 index ccbe910d4..000000000 --- a/structurizr-examples/src/com/structurizr/example/FinancialRiskSystem.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.structurizr.example; - -import com.structurizr.Workspace; -import com.structurizr.api.StructurizrClient; -import com.structurizr.documentation.AutomaticDocumentationTemplate; -import com.structurizr.model.*; -import com.structurizr.view.*; - -import java.io.File; - -/** - * This is a simple (incomplete) example C4 model based upon the financial risk system - * architecture kata, which can be found at http://bit.ly/sa4d-risksystem - * - * You can see the workspace online at https://structurizr.com/public/31 - */ -public class FinancialRiskSystem { - - private static final long WORKSPACE_ID = 31; - private static final String API_KEY = ""; - private static final String API_SECRET = ""; - - private static final String TAG_ALERT = "Alert"; - - public static void main(String[] args) throws Exception { - Workspace workspace = new Workspace("Financial Risk System", "This is a simple (incomplete) example C4 model based upon the financial risk system architecture kata, which can be found at http://bit.ly/sa4d-risksystem"); - Model model = workspace.getModel(); - - SoftwareSystem financialRiskSystem = model.addSoftwareSystem("Financial Risk System", "Calculates the bank's exposure to risk for product X."); - - Person businessUser = model.addPerson("Business User", "A regular business user."); - businessUser.uses(financialRiskSystem, "Views reports using"); - - Person configurationUser = model.addPerson("Configuration User", "A regular business user who can also configure the parameters used in the risk calculations."); - configurationUser.uses(financialRiskSystem, "Configures parameters using"); - - SoftwareSystem tradeDataSystem = model.addSoftwareSystem("Trade Data System", "The system of record for trades of type X."); - financialRiskSystem.uses(tradeDataSystem, "Gets trade data from"); - - SoftwareSystem referenceDataSystem = model.addSoftwareSystem("Reference Data System", "Manages reference data for all counterparties the bank interacts with."); - financialRiskSystem.uses(referenceDataSystem, "Gets counterparty data from"); - - SoftwareSystem referenceDataSystemV2 = model.addSoftwareSystem("Reference Data System v2.0", "Manages reference data for all counterparties the bank interacts with."); - referenceDataSystemV2.addTags("Future State"); - financialRiskSystem.uses(referenceDataSystemV2, "Gets counterparty data from").addTags("Future State"); - - SoftwareSystem emailSystem = model.addSoftwareSystem("E-mail system", "The bank's Microsoft Exchange system."); - financialRiskSystem.uses(emailSystem, "Sends a notification that a report is ready to"); - emailSystem.delivers(businessUser, "Sends a notification that a report is ready to", "E-mail message", InteractionStyle.Asynchronous); - - SoftwareSystem centralMonitoringService = model.addSoftwareSystem("Central Monitoring Service", "The bank's central monitoring and alerting dashboard."); - financialRiskSystem.uses(centralMonitoringService, "Sends critical failure alerts to", "SNMP", InteractionStyle.Asynchronous).addTags(TAG_ALERT); - - SoftwareSystem activeDirectory = model.addSoftwareSystem("Active Directory", "The bank's authentication and authorisation system."); - financialRiskSystem.uses(activeDirectory, "Uses for user authentication and authorisation"); - - ViewSet views = workspace.getViews(); - SystemContextView contextView = views.createSystemContextView(financialRiskSystem, "Context", "An example System Context diagram for the Financial Risk System architecture kata."); - contextView.addAllSoftwareSystems(); - contextView.addAllPeople(); - - Styles styles = views.getConfiguration().getStyles(); - financialRiskSystem.addTags("Risk System"); - - styles.addElementStyle(Tags.ELEMENT).color("#ffffff").fontSize(34); - styles.addElementStyle("Risk System").background("#550000").color("#ffffff"); - styles.addElementStyle(Tags.SOFTWARE_SYSTEM).width(650).height(400).background("#801515").shape(Shape.RoundedBox); - styles.addElementStyle(Tags.PERSON).width(550).background("#d46a6a").shape(Shape.Person); - - styles.addRelationshipStyle(Tags.RELATIONSHIP).thickness(4).dashed(false).fontSize(32).width(400); - styles.addRelationshipStyle(Tags.SYNCHRONOUS).dashed(false); - styles.addRelationshipStyle(Tags.ASYNCHRONOUS).dashed(true); - styles.addRelationshipStyle(TAG_ALERT).color("#ff0000"); - - styles.addElementStyle("Future State").opacity(30).border(Border.Dashed); - styles.addRelationshipStyle("Future State").opacity(30).dashed(true); - - AutomaticDocumentationTemplate template = new AutomaticDocumentationTemplate(workspace); - File documentationRoot = new File("./structurizr-examples/src/com/structurizr/example/financialrisksystem"); - template.addSections(documentationRoot); - template.addImages(documentationRoot); - - StructurizrClient structurizrClient = new StructurizrClient(API_KEY, API_SECRET); - structurizrClient.putWorkspace(WORKSPACE_ID, workspace); - } - -} \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/GettingStarted.java b/structurizr-examples/src/com/structurizr/example/GettingStarted.java deleted file mode 100644 index 6177216b9..000000000 --- a/structurizr-examples/src/com/structurizr/example/GettingStarted.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.structurizr.example; - -import com.structurizr.Workspace; -import com.structurizr.api.StructurizrClient; -import com.structurizr.model.Model; -import com.structurizr.model.Person; -import com.structurizr.model.SoftwareSystem; -import com.structurizr.model.Tags; -import com.structurizr.view.Shape; -import com.structurizr.view.Styles; -import com.structurizr.view.SystemContextView; -import com.structurizr.view.ViewSet; - -/** - * A "getting started" example that illustrates how to - * create a software architecture diagram using code. - * - * The live workspace is available to view at https://structurizr.com/share/25441 - */ -public class GettingStarted { - - private static final long WORKSPACE_ID = 25441; - private static final String API_KEY = ""; - private static final String API_SECRET = ""; - - public static void main(String[] args) throws Exception { - // all software architecture models belong to a workspace - Workspace workspace = new Workspace("Getting Started", "This is a model of my software system."); - Model model = workspace.getModel(); - - // create a model to describe a user using a software system - Person user = model.addPerson("User", "A user of my software system."); - SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "My software system."); - user.uses(softwareSystem, "Uses"); - - // create a system context diagram showing people and software systems - ViewSet views = workspace.getViews(); - SystemContextView contextView = views.createSystemContextView(softwareSystem, "SystemContext", "An example of a System Context diagram."); - contextView.addAllSoftwareSystems(); - contextView.addAllPeople(); - - // add some styling to the diagram elements - Styles styles = views.getConfiguration().getStyles(); - styles.addElementStyle(Tags.SOFTWARE_SYSTEM).background("#1168bd").color("#ffffff"); - styles.addElementStyle(Tags.PERSON).background("#08427b").color("#ffffff").shape(Shape.Person); - - // upload to structurizr.com (you'll need your own workspace ID, API key and API secret) - StructurizrClient structurizrClient = new StructurizrClient(API_KEY, API_SECRET); - structurizrClient.putWorkspace(WORKSPACE_ID, workspace); - } - -} \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/HttpHealthChecks.java b/structurizr-examples/src/com/structurizr/example/HttpHealthChecks.java deleted file mode 100644 index b3eac5051..000000000 --- a/structurizr-examples/src/com/structurizr/example/HttpHealthChecks.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.structurizr.example; - -import com.structurizr.Workspace; -import com.structurizr.api.StructurizrClient; -import com.structurizr.model.*; -import com.structurizr.view.DeploymentView; -import com.structurizr.view.Shape; -import com.structurizr.view.ViewSet; - -/** - * This is an example of how to use the HTTP-based health checks feature. - * - * You can see the health checks running at https://structurizr.com/share/39441/health - */ -public class HttpHealthChecks { - - private static final long WORKSPACE_ID = 39441; - private static final String API_KEY = "key"; - private static final String API_SECRET = "secret"; - - private static final String DATABASE_TAG = "Database"; - - public static void main(String[] args) throws Exception { - Workspace workspace = new Workspace("HTTP-based health checks example", "An example of how to use the HTTP-based health checks feature"); - Model model = workspace.getModel(); - ViewSet views = workspace.getViews(); - - SoftwareSystem structurizr = model.addSoftwareSystem("Structurizr", "A publishing platform for software architecture diagrams and documentation based upon the C4 model."); - Container webApplication = structurizr.addContainer("structurizr.com", "Provides all of the server-side functionality of Structurizr, serving static and dynamic content to users.", "Java and Spring MVC"); - Container database = structurizr.addContainer("Database", "Stores information about users, workspaces, etc.", "Relational Database Schema"); - database.addTags(DATABASE_TAG); - webApplication.uses(database, "Reads from and writes to", "JDBC"); - - DeploymentNode amazonWebServices = model.addDeploymentNode("Amazon Web Services", "", "us-east-1"); - DeploymentNode pivotalWebServices = amazonWebServices.addDeploymentNode("Pivotal Web Services", "Platform as a Service provider.", "Cloud Foundry"); - ContainerInstance liveWebApplication = pivotalWebServices.addDeploymentNode("www.structurizr.com", "An open source Java EE web server.", "Apache Tomcat") - .add(webApplication); - ContainerInstance liveDatabaseInstance = amazonWebServices.addDeploymentNode("Amazon RDS", "Database as a Service provider.", "MySQL") - .add(database); - - // add health checks to the container instances, which return a simple HTTP 200 to say everything is okay - liveWebApplication.addHealthCheck("Web Application is running", "https://www.structurizr.com/health"); - liveDatabaseInstance.addHealthCheck("Database is accessible from Web Application", "https://www.structurizr.com/health/database"); - - // the pass/fail status from the health checks is used to supplement any deployment views that include the container instances that have health checks defined - DeploymentView deploymentView = views.createDeploymentView(structurizr, "Deployment", "A deployment diagram showing the live environment."); - deploymentView.setEnvironment("Live"); - deploymentView.addAllDeploymentNodes(); - - views.getConfiguration().getStyles().addElementStyle(Tags.ELEMENT).color("#ffffff"); - views.getConfiguration().getStyles().addElementStyle(DATABASE_TAG).shape(Shape.Cylinder); - - StructurizrClient structurizrClient = new StructurizrClient(API_KEY, API_SECRET); - structurizrClient.putWorkspace(WORKSPACE_ID, workspace); - } - -} \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/MicroservicesExample.java b/structurizr-examples/src/com/structurizr/example/MicroservicesExample.java deleted file mode 100644 index 8d05411c9..000000000 --- a/structurizr-examples/src/com/structurizr/example/MicroservicesExample.java +++ /dev/null @@ -1,98 +0,0 @@ -package com.structurizr.example; - -import com.structurizr.Workspace; -import com.structurizr.api.StructurizrClient; -import com.structurizr.model.*; -import com.structurizr.view.*; - -/** - * A simple example of what a microservices architecture might look like. This workspace also - * includes a dynamic view that demonstrates parallel sequences of events. - * - * The live version of the diagrams can be found at https://structurizr.com/public/4241 - */ -public class MicroservicesExample { - - private static final String MICROSERVICE_TAG = "Microservice"; - private static final String MESSAGE_BUS_TAG = "Message Bus"; - private static final String DATASTORE_TAG = "Database"; - - public static void main(String[] args) throws Exception { - Workspace workspace = new Workspace("Microservices example", "An example of a microservices architecture, which includes asynchronous and parallel behaviour."); - Model model = workspace.getModel(); - - SoftwareSystem mySoftwareSystem = model.addSoftwareSystem("Customer Information System", "Stores information "); - Person customer = model.addPerson("Customer", "A customer"); - Container customerApplication = mySoftwareSystem.addContainer("Customer Application", "Allows customers to manage their profile.", "Angular"); - - Container customerService = mySoftwareSystem.addContainer("Customer Service", "The point of access for customer information.", "Java and Spring Boot"); - customerService.addTags(MICROSERVICE_TAG); - Container customerDatabase = mySoftwareSystem.addContainer("Customer Database", "Stores customer information.", "Oracle 12c"); - customerDatabase.addTags(DATASTORE_TAG); - - Container reportingService = mySoftwareSystem.addContainer("Reporting Service", "Creates normalised data for reporting purposes.", "Ruby"); - reportingService.addTags(MICROSERVICE_TAG); - Container reportingDatabase = mySoftwareSystem.addContainer("Reporting Database", "Stores a normalised version of all business data for ad hoc reporting purposes.", "MySQL"); - reportingDatabase.addTags(DATASTORE_TAG); - - Container auditService = mySoftwareSystem.addContainer("Audit Service", "Provides organisation-wide auditing facilities.", "C# .NET"); - auditService.addTags(MICROSERVICE_TAG); - Container auditStore = mySoftwareSystem.addContainer("Audit Store", "Stores information about events that have happened.", "Event Store"); - auditStore.addTags(DATASTORE_TAG); - - Container messageBus = mySoftwareSystem.addContainer("Message Bus", "Transport for business events.", "RabbitMQ"); - messageBus.addTags(MESSAGE_BUS_TAG); - - customer.uses(customerApplication, "Uses"); - customerApplication.uses(customerService, "Updates customer information using", "JSON/HTTPS", InteractionStyle.Synchronous); - customerService.uses(messageBus, "Sends customer update events to", "", InteractionStyle.Asynchronous); - customerService.uses(customerDatabase, "Stores data in", "JDBC", InteractionStyle.Synchronous); - customerService.uses(customerApplication, "Sends events to", "WebSocket", InteractionStyle.Asynchronous); - messageBus.uses(reportingService, "Sends customer update events to", "", InteractionStyle.Asynchronous); - messageBus.uses(auditService, "Sends customer update events to", "", InteractionStyle.Asynchronous); - reportingService.uses(reportingDatabase, "Stores data in", "", InteractionStyle.Synchronous); - auditService.uses(auditStore, "Stores events in", "", InteractionStyle.Synchronous); - - ViewSet views = workspace.getViews(); - - ContainerView containerView = views.createContainerView(mySoftwareSystem, "Containers", null); - containerView.addAllElements(); - - DynamicView dynamicView = views.createDynamicView(mySoftwareSystem, "CustomerUpdateEvent", "This diagram shows what happens when a customer updates their details."); - dynamicView.add(customer, customerApplication); - dynamicView.add(customerApplication, customerService); - - dynamicView.add(customerService, customerDatabase); - dynamicView.add(customerService, messageBus); - - dynamicView.startParallelSequence(); - dynamicView.add(messageBus, reportingService); - dynamicView.add(reportingService, reportingDatabase); - dynamicView.endParallelSequence(); - - dynamicView.startParallelSequence(); - dynamicView.add(messageBus, auditService); - dynamicView.add(auditService, auditStore); - dynamicView.endParallelSequence(); - - dynamicView.startParallelSequence(); - dynamicView.add(customerService, "Confirms update to", customerApplication); - dynamicView.endParallelSequence(); - - Styles styles = views.getConfiguration().getStyles(); - styles.addElementStyle(Tags.ELEMENT).color("#000000"); - styles.addElementStyle(Tags.PERSON).background("#ffbf00").shape(Shape.Person); - styles.addElementStyle(Tags.CONTAINER).background("#facc2E"); - styles.addElementStyle(MESSAGE_BUS_TAG).width(1600).shape(Shape.Pipe); - styles.addElementStyle(MICROSERVICE_TAG).shape(Shape.Hexagon); - styles.addElementStyle(DATASTORE_TAG).background("#f5da81").shape(Shape.Cylinder); - styles.addRelationshipStyle(Tags.RELATIONSHIP).routing(Routing.Orthogonal); - - styles.addRelationshipStyle(Tags.ASYNCHRONOUS).dashed(true); - styles.addRelationshipStyle(Tags.SYNCHRONOUS).dashed(false); - - StructurizrClient client = new StructurizrClient("key", "secret"); - client.putWorkspace(4241, workspace); - } - -} diff --git a/structurizr-examples/src/com/structurizr/example/Shapes.java b/structurizr-examples/src/com/structurizr/example/Shapes.java deleted file mode 100644 index 0ee7dc838..000000000 --- a/structurizr-examples/src/com/structurizr/example/Shapes.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.structurizr.example; - -import com.structurizr.Workspace; -import com.structurizr.api.StructurizrClient; -import com.structurizr.model.Model; -import com.structurizr.model.Tags; -import com.structurizr.view.*; - -/** - * An example illustrating all of the shapes available in Structurizr. - * - * The live workspace is available to view at https://structurizr.com/share/12541 - */ -public class Shapes { - - private static final long WORKSPACE_ID = 12541; - private static final String API_KEY = ""; - private static final String API_SECRET = ""; - - public static void main(String[] args) throws Exception { - Workspace workspace = new Workspace("Shapes", "An example of all shapes available in Structurizr."); - Model model = workspace.getModel(); - - model.addSoftwareSystem("Box", "Description").addTags("Box"); - model.addSoftwareSystem("RoundedBox", "Description").addTags("RoundedBox"); - model.addSoftwareSystem("Ellipse", "Description").addTags("Ellipse"); - model.addSoftwareSystem("Circle", "Description").addTags("Circle"); - model.addSoftwareSystem("Hexagon", "Description").addTags("Hexagon"); - model.addSoftwareSystem("Cylinder", "Description").addTags("Cylinder"); - model.addSoftwareSystem("WebBrowser", "Description").addTags("Web Browser"); - model.addSoftwareSystem("Mobile Device Portrait", "Description").addTags("Mobile Device Portrait"); - model.addSoftwareSystem("Mobile Device Landscape", "Description").addTags("Mobile Device Landscape"); - model.addSoftwareSystem("Pipe", "Description").addTags("Pipe"); - model.addSoftwareSystem("Folder", "Description").addTags("Folder"); - model.addSoftwareSystem("Robot", "Description").addTags("Robot"); - model.addPerson("Person", "Description").addTags("Person"); - model.addSoftwareSystem("Component", "Description").addTags("Component"); - - ViewSet views = workspace.getViews(); - SystemLandscapeView view = views.createSystemLandscapeView("shapes", "An example of all shapes available in Structurizr."); - view.addAllElements(); - view.setPaperSize(PaperSize.A5_Landscape); - - Styles styles = views.getConfiguration().getStyles(); - - styles.addElementStyle(Tags.ELEMENT).color("#ffffff").background("#438dd5").fontSize(34).width(650).height(400).description(false).metadata(false); - styles.addElementStyle("Box").shape(Shape.Box); - styles.addElementStyle("RoundedBox").shape(Shape.RoundedBox); - styles.addElementStyle("Ellipse").shape(Shape.Ellipse); - styles.addElementStyle("Circle").shape(Shape.Circle); - styles.addElementStyle("Cylinder").shape(Shape.Cylinder); - styles.addElementStyle("Web Browser").shape(Shape.WebBrowser); - styles.addElementStyle("Mobile Device Portrait").shape(Shape.MobileDevicePortrait).width(400).height(650); - styles.addElementStyle("Mobile Device Landscape").shape(Shape.MobileDeviceLandscape); - styles.addElementStyle("Pipe").shape(Shape.Pipe); - styles.addElementStyle("Folder").shape(Shape.Folder); - styles.addElementStyle("Hexagon").shape(Shape.Hexagon); - styles.addElementStyle("Robot").shape(Shape.Robot).width(550); - styles.addElementStyle("Person").shape(Shape.Person).width(550); - styles.addElementStyle("Component").shape(Shape.Component); - - StructurizrClient structurizrClient = new StructurizrClient(API_KEY, API_SECRET); - structurizrClient.putWorkspace(WORKSPACE_ID, workspace); - } - -} \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/StructurizrDocumentationExample.java b/structurizr-examples/src/com/structurizr/example/StructurizrDocumentationExample.java deleted file mode 100644 index 12d2a8fa7..000000000 --- a/structurizr-examples/src/com/structurizr/example/StructurizrDocumentationExample.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.structurizr.example; - -import com.structurizr.Workspace; -import com.structurizr.api.StructurizrClient; -import com.structurizr.documentation.Format; -import com.structurizr.documentation.StructurizrDocumentationTemplate; -import com.structurizr.model.Model; -import com.structurizr.model.Person; -import com.structurizr.model.SoftwareSystem; -import com.structurizr.model.Tags; -import com.structurizr.view.Shape; -import com.structurizr.view.Styles; -import com.structurizr.view.SystemContextView; -import com.structurizr.view.ViewSet; - -import java.io.File; - -/** - * An empty software architecture document using the Structurizr template. - * - * See https://structurizr.com/share/14181/documentation for the live version. - */ -public class StructurizrDocumentationExample { - - private static final long WORKSPACE_ID = 14181; - private static final String API_KEY = "key"; - private static final String API_SECRET = "secret"; - - public static void main(String[] args) throws Exception { - Workspace workspace = new Workspace("Documentation - Structurizr", "An empty software architecture document using the Structurizr template."); - Model model = workspace.getModel(); - ViewSet views = workspace.getViews(); - - Person user = model.addPerson("User", "A user of my software system."); - SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "My software system."); - user.uses(softwareSystem, "Uses"); - - SystemContextView contextView = views.createSystemContextView(softwareSystem, "SystemContext", "An example of a System Context diagram."); - contextView.addAllSoftwareSystems(); - contextView.addAllPeople(); - - Styles styles = views.getConfiguration().getStyles(); - styles.addElementStyle(Tags.PERSON).shape(Shape.Person); - - StructurizrDocumentationTemplate template = new StructurizrDocumentationTemplate(workspace); - - // this is the Markdown version - File documentationRoot = new File("./structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown"); - template.addContextSection(softwareSystem, new File(documentationRoot, "01-context.md")); - template.addFunctionalOverviewSection(softwareSystem, new File(documentationRoot, "02-functional-overview.md")); - template.addQualityAttributesSection(softwareSystem, new File(documentationRoot, "03-quality-attributes.md")); - template.addConstraintsSection(softwareSystem, new File(documentationRoot, "04-constraints.md")); - template.addPrinciplesSection(softwareSystem, new File(documentationRoot, "05-principles.md")); - template.addSoftwareArchitectureSection(softwareSystem, new File(documentationRoot, "06-software-architecture.md")); - template.addDataSection(softwareSystem, new File(documentationRoot, "07-data.md")); - template.addInfrastructureArchitectureSection(softwareSystem, new File(documentationRoot, "08-infrastructure-architecture.md")); - template.addDeploymentSection(softwareSystem, new File(documentationRoot, "09-deployment.md")); - template.addDevelopmentEnvironmentSection(softwareSystem, new File(documentationRoot, "10-development-environment.md")); - template.addOperationAndSupportSection(softwareSystem, new File(documentationRoot, "11-operation-and-support.md")); - template.addDecisionLogSection(softwareSystem, new File(documentationRoot, "12-decision-log.md")); - - // this is the AsciiDoc version -// File documentationRoot = new File("./structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc"); -// template.addContextSection(softwareSystem, new File(documentationRoot, "01-context.adoc")); -// template.addFunctionalOverviewSection(softwareSystem, new File(documentationRoot, "02-functional-overview.adoc")); -// template.addQualityAttributesSection(softwareSystem, new File(documentationRoot, "03-quality-attributes.adoc")); -// template.addConstraintsSection(softwareSystem, new File(documentationRoot, "04-constraints.adoc")); -// template.addPrinciplesSection(softwareSystem, new File(documentationRoot, "05-principles.adoc")); -// template.addSoftwareArchitectureSection(softwareSystem, new File(documentationRoot, "06-software-architecture.adoc")); -// template.addDataSection(softwareSystem, new File(documentationRoot, "07-data.adoc")); -// template.addInfrastructureArchitectureSection(softwareSystem, new File(documentationRoot, "08-infrastructure-architecture.adoc")); -// template.addDeploymentSection(softwareSystem, new File(documentationRoot, "09-deployment.adoc")); -// template.addDevelopmentEnvironmentSection(softwareSystem, new File(documentationRoot, "10-development-environment.adoc")); -// template.addOperationAndSupportSection(softwareSystem, new File(documentationRoot, "11-operation-and-support.adoc")); -// template.addDecisionLogSection(softwareSystem, new File(documentationRoot, "12-decision-log.adoc")); - - StructurizrClient structurizrClient = new StructurizrClient(API_KEY, API_SECRET); - structurizrClient.putWorkspace(WORKSPACE_ID, workspace); - } - -} \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/StylingElements.java b/structurizr-examples/src/com/structurizr/example/StylingElements.java deleted file mode 100644 index 6b8c215c0..000000000 --- a/structurizr-examples/src/com/structurizr/example/StylingElements.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.structurizr.example; - -import com.structurizr.Workspace; -import com.structurizr.api.StructurizrClient; -import com.structurizr.model.Container; -import com.structurizr.model.Model; -import com.structurizr.model.Person; -import com.structurizr.model.SoftwareSystem; -import com.structurizr.view.ContainerView; -import com.structurizr.view.Styles; -import com.structurizr.view.ViewSet; - -/** - * An example of how to style elements on diagrams. - * - * The live workspace is available to view at https://structurizr.com/share/36111 - */ -public class StylingElements { - - private static final long WORKSPACE_ID = 36111; - private static final String API_KEY = ""; - private static final String API_SECRET = ""; - - public static void main(String[] args) throws Exception { - Workspace workspace = new Workspace("Styling Elements", "This is a model of my software system."); - Model model = workspace.getModel(); - - Person user = model.addPerson("User", "A user of my software system."); - SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "My software system."); - Container webApplication = softwareSystem.addContainer("Web Application", "My web application.", "Java and Spring MVC"); - Container database = softwareSystem.addContainer("Database", "My database.", "Relational database schema"); - user.uses(webApplication, "Uses", "HTTPS"); - webApplication.uses(database, "Reads from and writes to", "JDBC"); - - ViewSet views = workspace.getViews(); - ContainerView containerView = views.createContainerView(softwareSystem, "containers", "An example of a container diagram."); - containerView.addAllElements(); - - Styles styles = workspace.getViews().getConfiguration().getStyles(); - - // example 1 -// styles.addElementStyle(Tags.ELEMENT).background("#438dd5").color("#ffffff"); - - // example 2 -// styles.addElementStyle(Tags.ELEMENT).color("#ffffff"); -// styles.addElementStyle(Tags.PERSON).background("#08427b"); -// styles.addElementStyle(Tags.CONTAINER).background("#438dd5"); - - // example 3 -// styles.addElementStyle(Tags.ELEMENT).color("#ffffff"); -// styles.addElementStyle(Tags.PERSON).background("#08427b").shape(Shape.Person); -// styles.addElementStyle(Tags.CONTAINER).background("#438dd5"); -// database.addTags("Database"); -// styles.addElementStyle("Database").shape(Shape.Cylinder); - - StructurizrClient structurizrClient = new StructurizrClient(API_KEY, API_SECRET); - structurizrClient.putWorkspace(WORKSPACE_ID, workspace); - } - -} \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/StylingRelationships.java b/structurizr-examples/src/com/structurizr/example/StylingRelationships.java deleted file mode 100644 index 7aaa97ffb..000000000 --- a/structurizr-examples/src/com/structurizr/example/StylingRelationships.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.structurizr.example; - -import com.structurizr.Workspace; -import com.structurizr.api.StructurizrClient; -import com.structurizr.model.Container; -import com.structurizr.model.Model; -import com.structurizr.model.Person; -import com.structurizr.model.SoftwareSystem; -import com.structurizr.view.ContainerView; -import com.structurizr.view.Styles; -import com.structurizr.view.ViewSet; - -/** - * An example of how to style relationships on diagrams. - * - * The live workspace is available to view at https://structurizr.com/share/36131 - */ -public class StylingRelationships { - - private static final long WORKSPACE_ID = 36131; - private static final String API_KEY = ""; - private static final String API_SECRET = ""; - - public static void main(String[] args) throws Exception { - Workspace workspace = new Workspace("Styling Relationships", "This is a model of my software system."); - Model model = workspace.getModel(); - - Person user = model.addPerson("User", "A user of my software system."); - SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "My software system."); - Container webApplication = softwareSystem.addContainer("Web Application", "My web application.", "Java and Spring MVC"); - Container database = softwareSystem.addContainer("Database", "My database.", "Relational database schema"); - user.uses(webApplication, "Uses", "HTTPS"); - webApplication.uses(database, "Reads from and writes to", "JDBC"); - - ViewSet views = workspace.getViews(); - ContainerView containerView = views.createContainerView(softwareSystem, "containers", "An example of a container diagram."); - containerView.addAllElements(); - - Styles styles = workspace.getViews().getConfiguration().getStyles(); - - // example 1 -// styles.addRelationshipStyle(Tags.RELATIONSHIP).color("#ff0000"); - - // example 2 -// model.getRelationships().stream().filter(r -> "HTTPS".equals(r.getTechnology())).forEach(r -> r.addTags("HTTPS")); -// model.getRelationships().stream().filter(r -> "JDBC".equals(r.getTechnology())).forEach(r -> r.addTags("JDBC")); -// styles.addRelationshipStyle("HTTPS").color("#ff0000"); -// styles.addRelationshipStyle("JDBC").color("#0000ff"); - - StructurizrClient structurizrClient = new StructurizrClient(API_KEY, API_SECRET); - structurizrClient.putWorkspace(WORKSPACE_ID, workspace); - } - -} \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/Theme.java b/structurizr-examples/src/com/structurizr/example/Theme.java deleted file mode 100644 index 882b02593..000000000 --- a/structurizr-examples/src/com/structurizr/example/Theme.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.structurizr.example; - -import com.structurizr.Workspace; -import com.structurizr.api.StructurizrClient; -import com.structurizr.model.Model; -import com.structurizr.model.Person; -import com.structurizr.model.SoftwareSystem; -import com.structurizr.view.SystemContextView; -import com.structurizr.view.ViewSet; - -/** - * This is an example of how to use an external theme. - * - * The live workspace is available to view at https://structurizr.com/share/38898 - */ -public class Theme { - - private static final long WORKSPACE_ID = 38898; - private static final String API_KEY = ""; - private static final String API_SECRET = ""; - - public static void main(String[] args) throws Exception { - Workspace workspace = new Workspace("Theme", "This is a model of my software system."); - Model model = workspace.getModel(); - - Person user = model.addPerson("User", "A user of my software system."); - SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "My software system."); - user.uses(softwareSystem, "Uses"); - - ViewSet views = workspace.getViews(); - SystemContextView contextView = views.createSystemContextView(softwareSystem, "SystemContext", "An example of a System Context diagram."); - contextView.addAllSoftwareSystems(); - contextView.addAllPeople(); - - // add a theme - views.getConfiguration().setThemes("https://raw.githubusercontent.com/structurizr/java/master/structurizr-examples/src/com/structurizr/example/theme/theme.json"); - - StructurizrClient structurizrClient = new StructurizrClient(API_KEY, API_SECRET); - structurizrClient.putWorkspace(WORKSPACE_ID, workspace); - } - -} \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/ViewpointsAndPerspectivesDocumentationExample.java b/structurizr-examples/src/com/structurizr/example/ViewpointsAndPerspectivesDocumentationExample.java deleted file mode 100644 index 0371aaf37..000000000 --- a/structurizr-examples/src/com/structurizr/example/ViewpointsAndPerspectivesDocumentationExample.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.structurizr.example; - -import com.structurizr.Workspace; -import com.structurizr.api.StructurizrClient; -import com.structurizr.documentation.Format; -import com.structurizr.documentation.ViewpointsAndPerspectivesDocumentationTemplate; -import com.structurizr.model.Model; -import com.structurizr.model.Person; -import com.structurizr.model.SoftwareSystem; -import com.structurizr.model.Tags; -import com.structurizr.view.Shape; -import com.structurizr.view.Styles; -import com.structurizr.view.SystemContextView; -import com.structurizr.view.ViewSet; - -import java.io.File; - -/** - * An empty software architecture document using the "Viewpoints and Perspectives" template. - * - * See https://structurizr.com/share/36371/documentation for the live version. - */ -public class ViewpointsAndPerspectivesDocumentationExample { - - private static final long WORKSPACE_ID = 36371; - private static final String API_KEY = "key"; - private static final String API_SECRET = "secret"; - - public static void main(String[] args) throws Exception { - Workspace workspace = new Workspace("Documentation - Viewpoints and Perspectives", "An empty software architecture document using the Viewpoints and Perspectives template."); - Model model = workspace.getModel(); - ViewSet views = workspace.getViews(); - - Person user = model.addPerson("User", "A user of my software system."); - SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "My software system."); - user.uses(softwareSystem, "Uses"); - - SystemContextView contextView = views.createSystemContextView(softwareSystem, "SystemContext", "An example of a System Context diagram."); - contextView.addAllSoftwareSystems(); - contextView.addAllPeople(); - - Styles styles = views.getConfiguration().getStyles(); - styles.addElementStyle(Tags.PERSON).shape(Shape.Person); - - ViewpointsAndPerspectivesDocumentationTemplate template = new ViewpointsAndPerspectivesDocumentationTemplate(workspace); - - // this is the Markdown version - File documentationRoot = new File("./structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown"); - template.addIntroductionSection(softwareSystem, new File(documentationRoot, "01-introduction.md")); - template.addGlossarySection(softwareSystem, new File(documentationRoot, "02-glossary.md")); - template.addSystemStakeholdersAndRequirementsSection(softwareSystem, new File(documentationRoot, "03-system-stakeholders-and-requirements.md")); - template.addArchitecturalForcesSection(softwareSystem, new File(documentationRoot, "04-architectural-forces.md")); - template.addArchitecturalViewsSection(softwareSystem, new File(documentationRoot, "05-architectural-views")); - template.addSystemQualitiesSection(softwareSystem, new File(documentationRoot, "06-system-qualities.md")); - template.addAppendicesSection(softwareSystem, new File(documentationRoot, "07-appendices.md")); - - // this is the AsciiDoc version -// File documentationRoot = new File("./structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc"); -// template.addIntroductionSection(softwareSystem, new File(documentationRoot, "01-introduction.adoc")); -// template.addGlossarySection(softwareSystem, new File(documentationRoot, "02-glossary.adoc")); -// template.addSystemStakeholdersAndRequirementsSection(softwareSystem, new File(documentationRoot, "03-system-stakeholders-and-requirements.adoc")); -// template.addArchitecturalForcesSection(softwareSystem, new File(documentationRoot, "04-architectural-forces.adoc")); -// template.addArchitecturalViewsSection(softwareSystem, new File(documentationRoot, "05-architectural-views")); -// template.addSystemQualitiesSection(softwareSystem, new File(documentationRoot, "06-system-qualities.adoc")); -// template.addAppendicesSection(softwareSystem, new File(documentationRoot, "07-appendices.adoc")); - - StructurizrClient structurizrClient = new StructurizrClient(API_KEY, API_SECRET); - structurizrClient.putWorkspace(WORKSPACE_ID, workspace); - } - -} \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/WidgetsLimited.java b/structurizr-examples/src/com/structurizr/example/WidgetsLimited.java deleted file mode 100644 index 516f870e2..000000000 --- a/structurizr-examples/src/com/structurizr/example/WidgetsLimited.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.structurizr.example; - -import com.structurizr.Workspace; -import com.structurizr.api.StructurizrClient; -import com.structurizr.documentation.Format; -import com.structurizr.documentation.StructurizrDocumentationTemplate; -import com.structurizr.model.*; -import com.structurizr.view.*; - -/** - * This workspace contains a number of diagrams for a fictional reseller of widgets online. - * - * You can see the workspace online at https://structurizr.com/public/14471 - */ -public class WidgetsLimited { - - private static final long WORKSPACE_ID = 14471; - private static final String API_KEY = ""; - private static final String API_SECRET = ""; - - private static final String EXTERNAL_TAG = "External"; - private static final String INTERNAL_TAG = "Internal"; - - public static void main(String[] args) throws Exception { - Workspace workspace = new Workspace("Widgets Limited", "Sells widgets to customers online."); - Model model = workspace.getModel(); - ViewSet views = workspace.getViews(); - Styles styles = views.getConfiguration().getStyles(); - - model.setEnterprise(new Enterprise("Widgets Limited")); - - Person customer = model.addPerson(Location.External, "Customer", "A customer of Widgets Limited."); - Person customerServiceUser = model.addPerson(Location.Internal, "Customer Service Agent", "Deals with customer enquiries."); - SoftwareSystem ecommerceSystem = model.addSoftwareSystem(Location.Internal, "E-commerce System", "Allows customers to buy widgets online via the widgets.com website."); - SoftwareSystem fulfilmentSystem = model.addSoftwareSystem(Location.Internal, "Fulfilment System", "Responsible for processing and shipping of customer orders."); - SoftwareSystem taxamo = model.addSoftwareSystem(Location.External, "Taxamo", "Calculates local tax (for EU B2B customers) and acts as a front-end for Braintree Payments."); - taxamo.setUrl("https://www.taxamo.com"); - SoftwareSystem braintreePayments = model.addSoftwareSystem(Location.External, "Braintree Payments", "Processes credit card payments on behalf of Widgets Limited."); - braintreePayments.setUrl("https://www.braintreepayments.com"); - SoftwareSystem jerseyPost = model.addSoftwareSystem(Location.External, "Jersey Post", "Calculates worldwide shipping costs for packages."); - - model.getPeople().stream().filter(p -> p.getLocation() == Location.External).forEach(p -> p.addTags(EXTERNAL_TAG)); - model.getPeople().stream().filter(p -> p.getLocation() == Location.Internal).forEach(p -> p.addTags(INTERNAL_TAG)); - - model.getSoftwareSystems().stream().filter(ss -> ss.getLocation() == Location.External).forEach(ss -> ss.addTags(EXTERNAL_TAG)); - model.getSoftwareSystems().stream().filter(ss -> ss.getLocation() == Location.Internal).forEach(ss -> ss.addTags(INTERNAL_TAG)); - - customer.interactsWith(customerServiceUser, "Asks questions to", "Telephone"); - customerServiceUser.uses(ecommerceSystem, "Looks up order information using"); - customer.uses(ecommerceSystem, "Places orders for widgets using"); - ecommerceSystem.uses(fulfilmentSystem, "Sends order information to"); - fulfilmentSystem.uses(jerseyPost, "Gets shipping charges from"); - ecommerceSystem.uses(taxamo, "Delegates credit card processing to"); - taxamo.uses(braintreePayments, "Uses for credit card processing"); - - SystemLandscapeView systemLandscapeView = views.createSystemLandscapeView("SystemLandscape", "The system landscape for Widgets Limited."); - systemLandscapeView.addAllElements(); - - SystemContextView ecommerceSystemContext = views.createSystemContextView(ecommerceSystem, "EcommerceSystemContext", "The system context diagram for the Widgets Limited e-commerce system."); - ecommerceSystemContext.addNearestNeighbours(ecommerceSystem); - ecommerceSystemContext.remove(customer.getEfferentRelationshipWith(customerServiceUser)); - - SystemContextView fulfilmentSystemContext = views.createSystemContextView(fulfilmentSystem, "FulfilmentSystemContext", "The system context diagram for the Widgets Limited fulfilment system."); - fulfilmentSystemContext.addNearestNeighbours(fulfilmentSystem); - - DynamicView dynamicView = views.createDynamicView("CustomerSupportCall", "A high-level overview of the customer support call process."); - dynamicView.add(customer, customerServiceUser); - dynamicView.add(customerServiceUser, ecommerceSystem); - - StructurizrDocumentationTemplate template = new StructurizrDocumentationTemplate(workspace); - template.addSection("System Landscape", Format.Markdown, "Here is some information about the Widgets Limited system landscape... ![](embed:SystemLandscape)"); - template.addContextSection(ecommerceSystem, Format.Markdown, "This is the context section for the E-commerce System... ![](embed:EcommerceSystemContext)"); - template.addContextSection(fulfilmentSystem, Format.Markdown, "This is the context section for the Fulfilment System... ![](embed:FulfilmentSystemContext)"); - - styles.addElementStyle(Tags.SOFTWARE_SYSTEM).shape(Shape.RoundedBox); - styles.addElementStyle(Tags.PERSON).shape(Shape.Person); - - styles.addElementStyle(Tags.ELEMENT).color("#ffffff"); - styles.addElementStyle(EXTERNAL_TAG).background("#EC5381").border(Border.Dashed); - styles.addElementStyle(INTERNAL_TAG).background("#B60037"); - - StructurizrClient structurizrClient = new StructurizrClient(API_KEY, API_SECRET); - structurizrClient.putWorkspace(WORKSPACE_ID, workspace); - } - -} \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/bigbankplc/BigBankPlc.java b/structurizr-examples/src/com/structurizr/example/bigbankplc/BigBankPlc.java deleted file mode 100644 index 8c28a9129..000000000 --- a/structurizr-examples/src/com/structurizr/example/bigbankplc/BigBankPlc.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.structurizr.example.bigbankplc; - -import com.structurizr.Workspace; -import com.structurizr.api.StructurizrClient; -import com.structurizr.documentation.Format; -import com.structurizr.documentation.StructurizrDocumentationTemplate; -import com.structurizr.model.*; -import com.structurizr.util.MapUtils; -import com.structurizr.view.*; - -/** - * This is an example workspace to illustrate the key features of Structurizr, based around a fictional bank. - */ -public abstract class BigBankPlc { - - static final String EXISTING_SYSTEM_TAG = "Existing System"; - static final String CUSTOMER_TAG = "Customer"; - static final String BANK_STAFF_TAG = "Bank Staff"; - - protected Workspace workspace; - protected Model model; - protected ViewSet views; - - Person customer; - Person customerServiceStaff; - Person backOfficeStaff; - SoftwareSystem internetBankingSystem; - SoftwareSystem mainframeBankingSystem; - SoftwareSystem emailSystem; - SoftwareSystem atm; - - BigBankPlc() { - workspace = new Workspace("", ""); - model = workspace.getModel(); - model.setEnterprise(new Enterprise("Big Bank plc")); - model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); - - customer = model.addPerson(Location.External, "Personal Banking Customer", "A customer of the bank, with personal bank accounts."); - customer.addTags(CUSTOMER_TAG); - - internetBankingSystem = model.addSoftwareSystem(Location.Internal, "Internet Banking System", "Allows customers to view information about their bank accounts, and make payments."); - customer.uses(internetBankingSystem, "Views account balances, and makes payments using"); - - mainframeBankingSystem = model.addSoftwareSystem(Location.Internal, "Mainframe Banking System", "Stores all of the core banking information about customers, accounts, transactions, etc."); - mainframeBankingSystem.addTags(EXISTING_SYSTEM_TAG); - internetBankingSystem.uses(mainframeBankingSystem, "Gets account information from, and makes payments using"); - - emailSystem = model.addSoftwareSystem(Location.Internal, "E-mail System", "The internal Microsoft Exchange e-mail system."); - internetBankingSystem.uses(emailSystem, "Sends e-mail using"); - emailSystem.addTags(EXISTING_SYSTEM_TAG); - emailSystem.delivers(customer, "Sends e-mails to"); - - atm = model.addSoftwareSystem(Location.Internal, "ATM", "Allows customers to withdraw cash."); - atm.addTags(EXISTING_SYSTEM_TAG); - atm.uses(mainframeBankingSystem, "Uses"); - customer.uses(atm, "Withdraws cash using"); - - customerServiceStaff = model.addPerson(Location.Internal, "Customer Service Staff", "Customer service staff within the bank."); - customerServiceStaff.addTags(BANK_STAFF_TAG); - customerServiceStaff.uses(mainframeBankingSystem, "Uses"); - customer.interactsWith(customerServiceStaff, "Asks questions to", "Telephone"); - - backOfficeStaff = model.addPerson(Location.Internal, "Back Office Staff", "Administration and support staff within the bank."); - backOfficeStaff.addTags(BANK_STAFF_TAG); - backOfficeStaff.uses(mainframeBankingSystem, "Uses"); - - views = workspace.getViews(); - Styles styles = views.getConfiguration().getStyles(); - styles.addElementStyle(Tags.PERSON).color("#ffffff").shape(Shape.Person).fontSize(22); - styles.addElementStyle(CUSTOMER_TAG).background("#08427b"); - styles.addElementStyle(BANK_STAFF_TAG).background("#999999"); - } - -} \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/bigbankplc/InternetBankingSystem.java b/structurizr-examples/src/com/structurizr/example/bigbankplc/InternetBankingSystem.java deleted file mode 100644 index 8d67acafe..000000000 --- a/structurizr-examples/src/com/structurizr/example/bigbankplc/InternetBankingSystem.java +++ /dev/null @@ -1,221 +0,0 @@ -package com.structurizr.example.bigbankplc; - -import com.structurizr.api.StructurizrClient; -import com.structurizr.documentation.AdrToolsImporter; -import com.structurizr.documentation.AutomaticDocumentationTemplate; -import com.structurizr.model.*; -import com.structurizr.util.MapUtils; -import com.structurizr.view.*; - -import java.io.File; - -/** - * This is an example workspace to illustrate the key features of Structurizr, - * based around a fictional Internet Banking System for Big Bank plc. - * - * The live workspace is available to view at https://structurizr.com/share/36141 - */ -public final class InternetBankingSystem extends BigBankPlc { - - private static final long WORKSPACE_ID = 36141; - private static final String API_KEY = "key"; - private static final String API_SECRET = "secret"; - - private static final String WEB_BROWSER_TAG = "Web Browser"; - private static final String MOBILE_APP_TAG = "Mobile App"; - private static final String DATABASE_TAG = "Database"; - private static final String FAILOVER_TAG = "Failover"; - - private InternetBankingSystem() throws Exception { - workspace.setName("Big Bank plc - Internet Banking System"); - workspace.setDescription("The software architecture of the Big Bank plc Internet Banking System."); - - // containers - Container singlePageApplication = internetBankingSystem.addContainer("Single-Page Application", "Provides all of the Internet banking functionality to customers via their web browser.", "JavaScript and Angular"); - singlePageApplication.addTags(WEB_BROWSER_TAG); - Container mobileApp = internetBankingSystem.addContainer("Mobile App", "Provides a limited subset of the Internet banking functionality to customers via their mobile device.", "Xamarin"); - mobileApp.addTags(MOBILE_APP_TAG); - Container webApplication = internetBankingSystem.addContainer("Web Application", "Delivers the static content and the Internet banking single page application.", "Java and Spring MVC"); - Container apiApplication = internetBankingSystem.addContainer("API Application", "Provides Internet banking functionality via a JSON/HTTPS API.", "Java and Spring MVC"); - Container database = internetBankingSystem.addContainer("Database", "Stores user registration information, hashed authentication credentials, access logs, etc.", "Oracle Database Schema"); - database.addTags(DATABASE_TAG); - - customer.uses(webApplication, "Visits bigbank.com/ib using", "HTTPS"); - customer.uses(singlePageApplication, "Views account balances, and makes payments using", ""); - customer.uses(mobileApp, "Views account balances, and makes payments using", ""); - webApplication.uses(singlePageApplication, "Delivers to the customer's web browser", ""); - apiApplication.uses(database, "Reads from and writes to", "JDBC"); - apiApplication.uses(mainframeBankingSystem, "Makes API calls to", "XML/HTTPS"); - apiApplication.uses(emailSystem, "Sends e-mail using", "SMTP"); - - // components - // - for a real-world software system, you would probably want to extract the components using - // - static analysis/reflection rather than manually specifying them all - Component signinController = apiApplication.addComponent("Sign In Controller", "Allows users to sign in to the Internet Banking System.", "Spring MVC Rest Controller"); - Component accountsSummaryController = apiApplication.addComponent("Accounts Summary Controller", "Provides customers with a summary of their bank accounts.", "Spring MVC Rest Controller"); - Component resetPasswordController = apiApplication.addComponent("Reset Password Controller", "Allows users to reset their passwords with a single use URL.", "Spring MVC Rest Controller"); - Component securityComponent = apiApplication.addComponent("Security Component", "Provides functionality related to signing in, changing passwords, etc.", "Spring Bean"); - Component mainframeBankingSystemFacade = apiApplication.addComponent("Mainframe Banking System Facade", "A facade onto the mainframe banking system.", "Spring Bean"); - Component emailComponent = apiApplication.addComponent("E-mail Component", "Sends e-mails to users.", "Spring Bean"); - - apiApplication.getComponents().stream().filter(c -> "Spring MVC Rest Controller".equals(c.getTechnology())).forEach(c -> singlePageApplication.uses(c, "Makes API calls to", "JSON/HTTPS")); - apiApplication.getComponents().stream().filter(c -> "Spring MVC Rest Controller".equals(c.getTechnology())).forEach(c -> mobileApp.uses(c, "Makes API calls to", "JSON/HTTPS")); - signinController.uses(securityComponent, "Uses"); - accountsSummaryController.uses(mainframeBankingSystemFacade, "Uses"); - resetPasswordController.uses(securityComponent, "Uses"); - resetPasswordController.uses(emailComponent, "Uses"); - securityComponent.uses(database, "Reads from and writes to", "JDBC"); - mainframeBankingSystemFacade.uses(mainframeBankingSystem, "Uses", "XML/HTTPS"); - emailComponent.uses(emailSystem, "Sends e-mail using"); - - // deployment nodes and container instances - DeploymentNode developerLaptop = model.addDeploymentNode("Development", "Developer Laptop", "A developer laptop.", "Microsoft Windows 10 or Apple macOS"); - DeploymentNode apacheTomcat = developerLaptop.addDeploymentNode("Docker Container - Web Server", "A Docker container.", "Docker") - .addDeploymentNode("Apache Tomcat", "An open source Java EE web server.", "Apache Tomcat 8.x", 1, MapUtils.create("Xmx=512M", "Xms=1024M", "Java Version=8")); - ContainerInstance developmentWebApplication = apacheTomcat.add(webApplication); - ContainerInstance developmentApiApplication = apacheTomcat.add(apiApplication); - - DeploymentNode bigBankDataCenterForDevelopment = model.addDeploymentNode("Development", "Big Bank plc", "", "Big Bank plc data center"); - SoftwareSystemInstance developmentMainframeBankingSystem = bigBankDataCenterForDevelopment.addDeploymentNode("bigbank-dev001").add(mainframeBankingSystem); - - ContainerInstance developmentDatabase = developerLaptop.addDeploymentNode("Docker Container - Database Server", "A Docker container.", "Docker") - .addDeploymentNode("Database Server", "A development database.", "Oracle 12c") - .add(database); - - ContainerInstance developmentSinglePageApplication = developerLaptop.addDeploymentNode("Web Browser", "", "Chrome, Firefox, Safari, or Edge").add(singlePageApplication); - - DeploymentNode customerMobileDevice = model.addDeploymentNode("Live", "Customer's mobile device", "", "Apple iOS or Android"); - ContainerInstance liveMobileApp = customerMobileDevice.add(mobileApp); - - DeploymentNode customerComputer = model.addDeploymentNode("Live", "Customer's computer", "", "Microsoft Windows or Apple macOS"); - ContainerInstance liveSinglePageApplication = customerComputer.addDeploymentNode("Web Browser", "", "Chrome, Firefox, Safari, or Edge").add(singlePageApplication); - - DeploymentNode bigBankDataCenterForLive = model.addDeploymentNode("Live", "Big Bank plc", "", "Big Bank plc data center"); - SoftwareSystemInstance liveMainframeBankingSystem = bigBankDataCenterForLive.addDeploymentNode("bigbank-prod001").add(mainframeBankingSystem); - - DeploymentNode liveWebServer = bigBankDataCenterForLive.addDeploymentNode("bigbank-web***", "A web server residing in the web server farm, accessed via F5 BIG-IP LTMs.", "Ubuntu 16.04 LTS", 4, MapUtils.create("Location=London and Reading")); - ContainerInstance liveWebApplication = liveWebServer.addDeploymentNode("Apache Tomcat", "An open source Java EE web server.", "Apache Tomcat 8.x", 1, MapUtils.create("Xmx=512M", "Xms=1024M", "Java Version=8")) - .add(webApplication); - - DeploymentNode liveApiServer = bigBankDataCenterForLive.addDeploymentNode("bigbank-api***", "A web server residing in the web server farm, accessed via F5 BIG-IP LTMs.", "Ubuntu 16.04 LTS", 8, MapUtils.create("Location=London and Reading")); - ContainerInstance liveApiApplication = liveApiServer.addDeploymentNode("Apache Tomcat", "An open source Java EE web server.", "Apache Tomcat 8.x", 1, MapUtils.create("Xmx=512M", "Xms=1024M", "Java Version=8")) - .add(apiApplication); - - DeploymentNode primaryDatabaseServer = bigBankDataCenterForLive.addDeploymentNode("bigbank-db01", "The primary database server.", "Ubuntu 16.04 LTS", 1, MapUtils.create("Location=London")) - .addDeploymentNode("Oracle - Primary", "The primary, live database server.", "Oracle 12c"); - ContainerInstance livePrimaryDatabase = primaryDatabaseServer.add(database); - - DeploymentNode bigBankdb02 = bigBankDataCenterForLive.addDeploymentNode("bigbank-db02", "The secondary database server.", "Ubuntu 16.04 LTS", 1, MapUtils.create("Location=Reading")); - bigBankdb02.addTags(FAILOVER_TAG); - DeploymentNode secondaryDatabaseServer = bigBankdb02.addDeploymentNode("Oracle - Secondary", "A secondary, standby database server, used for failover purposes only.", "Oracle 12c"); - secondaryDatabaseServer.addTags(FAILOVER_TAG); - ContainerInstance liveSecondaryDatabase = secondaryDatabaseServer.add(database); - - model.getRelationships().stream().filter(r -> r.getDestination().equals(liveSecondaryDatabase)).forEach(r -> r.addTags(FAILOVER_TAG)); - Relationship dataReplicationRelationship = primaryDatabaseServer.uses(secondaryDatabaseServer, "Replicates data to", ""); - liveSecondaryDatabase.addTags(FAILOVER_TAG); - - // views/diagrams - SystemContextView systemContextView = views.createSystemContextView(internetBankingSystem, "SystemContext", "The system context diagram for the Internet Banking System."); - systemContextView.setEnterpriseBoundaryVisible(false); - systemContextView.addNearestNeighbours(internetBankingSystem); - systemContextView.setPaperSize(PaperSize.A5_Landscape); - - ContainerView containerView = views.createContainerView(internetBankingSystem, "Containers", "The container diagram for the Internet Banking System."); - containerView.add(customer); - containerView.addAllContainers(); - containerView.add(mainframeBankingSystem); - containerView.add(emailSystem); - containerView.setPaperSize(PaperSize.A5_Landscape); - - ComponentView componentView = views.createComponentView(apiApplication, "Components", "The component diagram for the API Application."); - componentView.add(mobileApp); - componentView.add(singlePageApplication); - componentView.add(database); - componentView.addAllComponents(); - componentView.add(mainframeBankingSystem); - componentView.add(emailSystem); - componentView.setPaperSize(PaperSize.A5_Landscape); - - systemContextView.addAnimation(internetBankingSystem); - systemContextView.addAnimation(customer); - systemContextView.addAnimation(mainframeBankingSystem); - systemContextView.addAnimation(emailSystem); - - containerView.addAnimation(customer, mainframeBankingSystem, emailSystem); - containerView.addAnimation(webApplication); - containerView.addAnimation(singlePageApplication); - containerView.addAnimation(mobileApp); - containerView.addAnimation(apiApplication); - containerView.addAnimation(database); - - componentView.addAnimation(singlePageApplication, mobileApp, database, emailSystem, mainframeBankingSystem); - componentView.addAnimation(signinController, securityComponent); - componentView.addAnimation(accountsSummaryController, mainframeBankingSystemFacade); - componentView.addAnimation(resetPasswordController, emailComponent); - - // dynamic diagrams and deployment diagrams are not available with the Free Plan - DynamicView dynamicView = views.createDynamicView(apiApplication, "SignIn", "Summarises how the sign in feature works in the single-page application."); - dynamicView.add(singlePageApplication, "Submits credentials to", signinController); - dynamicView.add(signinController, "Validates credentials using", securityComponent); - dynamicView.add(securityComponent, "select * from users where username = ?", database); - dynamicView.add(database, "Returns user data to", securityComponent); - dynamicView.add(securityComponent, "Returns true if the hashed password matches", signinController); - dynamicView.add(signinController, "Sends back an authentication token to", singlePageApplication); - dynamicView.setPaperSize(PaperSize.A5_Landscape); - - DeploymentView developmentDeploymentView = views.createDeploymentView(internetBankingSystem, "DevelopmentDeployment", "An example development deployment scenario for the Internet Banking System."); - developmentDeploymentView.setEnvironment("Development"); - developmentDeploymentView.add(developerLaptop); - developmentDeploymentView.add(bigBankDataCenterForDevelopment); - developmentDeploymentView.setPaperSize(PaperSize.A5_Landscape); - - developmentDeploymentView.addAnimation(developmentSinglePageApplication); - developmentDeploymentView.addAnimation(developmentWebApplication, developmentApiApplication); - developmentDeploymentView.addAnimation(developmentDatabase); - developmentDeploymentView.addAnimation(developmentMainframeBankingSystem); - - DeploymentView liveDeploymentView = views.createDeploymentView(internetBankingSystem, "LiveDeployment", "An example live deployment scenario for the Internet Banking System."); - liveDeploymentView.setEnvironment("Live"); - liveDeploymentView.add(bigBankDataCenterForLive); - liveDeploymentView.add(customerMobileDevice); - liveDeploymentView.add(customerComputer); - liveDeploymentView.add(dataReplicationRelationship); - liveDeploymentView.setPaperSize(PaperSize.A4_Landscape); - - liveDeploymentView.addAnimation(liveSinglePageApplication); - liveDeploymentView.addAnimation(liveMobileApp); - liveDeploymentView.addAnimation(liveWebApplication, liveApiApplication); - liveDeploymentView.addAnimation(livePrimaryDatabase); - liveDeploymentView.addAnimation(liveSecondaryDatabase); - liveDeploymentView.addAnimation(liveMainframeBankingSystem); - - // colours, shapes and other diagram styling - Styles styles = views.getConfiguration().getStyles(); - styles.addElementStyle(Tags.SOFTWARE_SYSTEM).background("#1168bd").color("#ffffff"); - styles.addElementStyle(Tags.CONTAINER).background("#438dd5").color("#ffffff"); - styles.addElementStyle(Tags.COMPONENT).background("#85bbf0").color("#000000"); - styles.addElementStyle(EXISTING_SYSTEM_TAG).background("#999999").color("#ffffff"); - styles.addElementStyle(WEB_BROWSER_TAG).shape(Shape.WebBrowser); - styles.addElementStyle(MOBILE_APP_TAG).shape(Shape.MobileDeviceLandscape); - styles.addElementStyle(DATABASE_TAG).shape(Shape.Cylinder); - styles.addElementStyle(FAILOVER_TAG).opacity(25); - styles.addRelationshipStyle(FAILOVER_TAG).opacity(25).position(70); - - // documentation - AutomaticDocumentationTemplate template = new AutomaticDocumentationTemplate(workspace); - template.addSections(internetBankingSystem, new File("./structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/docs")); - - // ADRs - AdrToolsImporter adrToolsImporter = new AdrToolsImporter(workspace, new File("./structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/adrs")); - adrToolsImporter.importArchitectureDecisionRecords(internetBankingSystem); - - StructurizrClient structurizrClient = new StructurizrClient(API_KEY, API_SECRET); - structurizrClient.putWorkspace(WORKSPACE_ID, workspace); - } - - public static void main(String[] args) throws Exception { - new InternetBankingSystem(); - } - -} \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/bigbankplc/SystemLandscape.java b/structurizr-examples/src/com/structurizr/example/bigbankplc/SystemLandscape.java deleted file mode 100644 index e2fd61d96..000000000 --- a/structurizr-examples/src/com/structurizr/example/bigbankplc/SystemLandscape.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.structurizr.example.bigbankplc; - -import com.structurizr.api.StructurizrClient; -import com.structurizr.model.*; -import com.structurizr.view.*; - -/** - * This is an example workspace to illustrate the key features of Structurizr, - * based around a fictional bank. This workspace contains the system landscape. - * - * The live workspace is available to view at https://structurizr.com/share/28201 - */ -public final class SystemLandscape extends BigBankPlc { - - private static final long WORKSPACE_ID = 28201; - private static final String API_KEY = "key"; - private static final String API_SECRET = "secret"; - - private SystemLandscape() throws Exception { - workspace.setName("Big Bank plc - System Landscape"); - workspace.setDescription("The system landscape for Big Bank plc."); - - internetBankingSystem.setUrl("https://structurizr.com/share/36141/diagrams#SystemContext"); - - SystemLandscapeView systemLandscapeView = views.createSystemLandscapeView("SystemLandscape", "The system landscape diagram for Big Bank plc."); - systemLandscapeView.addAllElements(); - - Styles styles = views.getConfiguration().getStyles(); - styles.addElementStyle(Tags.SOFTWARE_SYSTEM).background("#999999").color("#ffffff"); - - StructurizrClient structurizrClient = new StructurizrClient(API_KEY, API_SECRET); - structurizrClient.putWorkspace(WORKSPACE_ID, workspace); - } - - public static void main(String[] args) throws Exception { - new SystemLandscape(); - } - -} \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/adrs/0001-record-architecture-decisions.md b/structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/adrs/0001-record-architecture-decisions.md deleted file mode 100644 index 7461d99d3..000000000 --- a/structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/adrs/0001-record-architecture-decisions.md +++ /dev/null @@ -1,19 +0,0 @@ -# 1. Record architecture decisions - -Date: 2020-06-05 - -## Status - -Accepted - -## Context - -We need to record the architectural decisions made on this project. - -## Decision - -We will use Architecture Decision Records, as described by Michael Nygard in this article: [http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions) - -## Consequences - -See Michael Nygard's article, linked above. \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/docs/01-context.md b/structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/docs/01-context.md deleted file mode 100644 index 5440e9435..000000000 --- a/structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/docs/01-context.md +++ /dev/null @@ -1,11 +0,0 @@ -## Context - -Here is some context about the Internet Banking System... - -![](embed:SystemContext) - -### Internet Banking System -... - -### Mainframe Banking System -... diff --git a/structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/docs/02-containers.md b/structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/docs/02-containers.md deleted file mode 100644 index d4d8d9aab..000000000 --- a/structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/docs/02-containers.md +++ /dev/null @@ -1,21 +0,0 @@ -## Software Architecture - -Here is some information about the software architecture of the Internet Banking System... - -![](embed:Containers) - -### Web Application -... - -### Database -... - -Here is some information about the API Application... - -![](embed:Components) - -### Sign in process - -Here is some information about the Sign In Controller, including how the sign in process works... - -![](embed:SignIn) \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/docs/03-development-environment.adoc b/structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/docs/03-development-environment.adoc deleted file mode 100644 index 9f9b6d664..000000000 --- a/structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/docs/03-development-environment.adoc +++ /dev/null @@ -1,5 +0,0 @@ -== Development Environment - -Here is some information about how to set up a development environment for the Internet Banking System... - -image::embed:DevelopmentDeployment[] \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/docs/04-deployment.adoc b/structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/docs/04-deployment.adoc deleted file mode 100644 index be82ea565..000000000 --- a/structurizr-examples/src/com/structurizr/example/bigbankplc/internetbankingsystem/docs/04-deployment.adoc +++ /dev/null @@ -1,5 +0,0 @@ -== Deployment - -Here is some information about the live deployment environment for the Internet Banking System... - -image::embed:LiveDeployment[] \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/documentation/adr/0001-record-architecture-decisions.md b/structurizr-examples/src/com/structurizr/example/documentation/adr/0001-record-architecture-decisions.md deleted file mode 100644 index 69845120c..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/adr/0001-record-architecture-decisions.md +++ /dev/null @@ -1,22 +0,0 @@ -# 1. Record architecture decisions - -Date: 2016-02-12 - -## Status - -Accepted - -## Context - -We need to record the architectural decisions made on this project. - -## Decision - -We will use Architecture Decision Records, as described by Michael Nygard in this article: [http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions) - -## Consequences - -See Michael Nygard's article, linked above. - ---- -This Architecture Decision Record (ADR) was written by Nat Pryce as a part of [adr-tools](https://github.com/npryce/adr-tools), and is reproduced here under the [Creative Commons Attribution 4.0 International (CC BY 4.0) license](https://creativecommons.org/licenses/by/4.0/). \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/documentation/adr/0002-implement-as-shell-scripts.md b/structurizr-examples/src/com/structurizr/example/documentation/adr/0002-implement-as-shell-scripts.md deleted file mode 100644 index 6d5ecabdc..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/adr/0002-implement-as-shell-scripts.md +++ /dev/null @@ -1,31 +0,0 @@ -# 2. Implement as shell scripts - -Date: 2016-02-12 - -## Status - -Accepted - -## Context - -ADRs are plain text files stored in a subdirectory of the project. - -The tool needs to create new files and apply small edits to -the Status section of existing files. - -## Decision - -The tool is implemented as shell scripts that use standard Unix -tools -- grep, sed, awk, etc. - -## Consequences - -The tool won't support Windows. Being plain text files, ADRs can -be created by hand and edited in any text editor. This tool just -makes the process more convenient. - -Development will have to cope with differences between Unix -variants, particularly Linux and MacOS X. - ---- -This Architecture Decision Record (ADR) was written by Nat Pryce as a part of [adr-tools](https://github.com/npryce/adr-tools), and is reproduced here under the [Creative Commons Attribution 4.0 International (CC BY 4.0) license](https://creativecommons.org/licenses/by/4.0/). \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/documentation/adr/0003-single-command-with-subcommands.md b/structurizr-examples/src/com/structurizr/example/documentation/adr/0003-single-command-with-subcommands.md deleted file mode 100644 index 6ee2ef327..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/adr/0003-single-command-with-subcommands.md +++ /dev/null @@ -1,48 +0,0 @@ -# 3. Single command with subcommands - -Date: 2016-02-12 - -## Status - -Accepted - -## Context - -The tool provides a number of related commands to create -and manipulate architecture decision records. - -How can the user find out about the commands that are available? - -## Decision - -The tool defines a single command, called `adr`. - -The first argument to `adr` (the subcommand) specifies the -action to perform. Further arguments are interpreted by the -subcommand. - -Running `adr` without any arguments lists the available -subcommands. - -Subcommands are implemented as scripts in the same -directory as the `adr` script. E.g. the subcommand `new` is -implemented as the script `adr-new`, the subcommand `help` -as the script `adr-help` and so on. - -Helper scripts that are part of the implementation but not -subcommands follow a different naming convention, so that -subcommands can be listed by filtering and transforming script -file names. - -## Consequences - -Users can more easily explore the capabilities of the tool. - -Users are already used to this style of command-line tool. For -example, Git works this way. - -Each subcommand can be implemented in the most appropriate -language. - ---- -This Architecture Decision Record (ADR) was written by Nat Pryce as a part of [adr-tools](https://github.com/npryce/adr-tools), and is reproduced here under the [Creative Commons Attribution 4.0 International (CC BY 4.0) license](https://creativecommons.org/licenses/by/4.0/). diff --git a/structurizr-examples/src/com/structurizr/example/documentation/adr/0004-markdown-format.md b/structurizr-examples/src/com/structurizr/example/documentation/adr/0004-markdown-format.md deleted file mode 100644 index dd72f9988..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/adr/0004-markdown-format.md +++ /dev/null @@ -1,43 +0,0 @@ -# 4. Markdown format - -Date: 2016-02-12 - -## Status - -Accepted - -## Context - -The decision records must be stored in a plain text format: - -* This works well with version control systems. - -* It allows the tool to modify the status of records and insert - hyperlinks when one decision supercedes another. - -* Decisions can be read in the terminal, IDE, version control - browser, etc. - -People will want to use some formatting: lists, code examples, -and so on. - -People will want to view the decision records in a more readable -format than plain text, and maybe print them out. - - -## Decision - -Record architecture decisions in [Markdown format](https://daringfireball.net/projects/markdown/). - -## Consequences - -Decisions can be read in the terminal. - -Decisions will be formatted nicely and hyperlinked by the -browsers of project hosting sites like GitHub and Bitbucket. - -Tools like [Pandoc](http://pandoc.org/) can be used to convert -the decision records into HTML or PDF. - ---- -This Architecture Decision Record (ADR) was written by Nat Pryce as a part of [adr-tools](https://github.com/npryce/adr-tools), and is reproduced here under the [Creative Commons Attribution 4.0 International (CC BY 4.0) license](https://creativecommons.org/licenses/by/4.0/). \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/documentation/adr/0005-help-comments.md b/structurizr-examples/src/com/structurizr/example/documentation/adr/0005-help-comments.md deleted file mode 100644 index f9ecba7bb..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/adr/0005-help-comments.md +++ /dev/null @@ -1,43 +0,0 @@ -# 5. Help comments - -Date: 2016-02-13 - -## Status - -Accepted - -## Context - -The tool will have a `help` subcommand to provide documentation -for users. - -It's nice to have usage documentation in the script files -themselves, in comments. When reading the code, that's the first -place to look for information about how to run a script. - -## Decision - -Write usage documentation in comments in the source file. - -Distinguish between documentation comments and normal comments. -Documentation comments have two hash characters at the start of -the line. - -The `adr help` command can parse comments out from the script -using the standard Unix tools `grep` and `cut`. - -## Consequences - -No need to maintain help text in a separate file. - -Help text can easily be kept up to date as the script is edited. - -There's no automated check that the help text is up to date. The -tests do not work well as documentation for users, and the help -text is not easily cross-checked against the code. - -This won't work if any subcommands are not implemented as scripts -that use '#' as a comment character. - ---- -This Architecture Decision Record (ADR) was written by Nat Pryce as a part of [adr-tools](https://github.com/npryce/adr-tools), and is reproduced here under the [Creative Commons Attribution 4.0 International (CC BY 4.0) license](https://creativecommons.org/licenses/by/4.0/). \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/documentation/adr/0006-packaging-and-distribution-in-other-version-control-repositories.md b/structurizr-examples/src/com/structurizr/example/documentation/adr/0006-packaging-and-distribution-in-other-version-control-repositories.md deleted file mode 100644 index 4bd74661d..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/adr/0006-packaging-and-distribution-in-other-version-control-repositories.md +++ /dev/null @@ -1,44 +0,0 @@ -# 6. Packaging and distribution in other version control repositories - -Date: 2016-02-16 - -## Status - -Accepted - -## Context - -Users want to install adr-tools with their preferred package -manager. For example, Ubuntu users use `apt`, RedHat users use -`yum` and Mac OS X users use [Homebrew](http://brew.sh). - -The developers of `adr-tools` don't know how, nor have permissions, -to use all these packaging and distribution systems. Therefore packaging -and distribution must be done by "downstream" parties. - -The developers of the tool should not favour any one particular -packaging and distribution solution. - -## Decision - -The `adr-tools` project will not contain any packaging or -distribution scripts and config. - -Packaging and distribution will be managed by other projects in -separate version control repositories. - -## Consequences - -The git repo of this project will be simpler. - -Eventually, users will not have to use Git to get the software. - -We will have to tag releases in the `adr-tools` repository so that -packaging projects know what can be published and how it should be -identified. - -We will document how users can install the software in this -project's README file. - ---- -This Architecture Decision Record (ADR) was written by Nat Pryce as a part of [adr-tools](https://github.com/npryce/adr-tools), and is reproduced here under the [Creative Commons Attribution 4.0 International (CC BY 4.0) license](https://creativecommons.org/licenses/by/4.0/). \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/documentation/adr/0007-invoke-adr-config-executable-to-get-configuration.md b/structurizr-examples/src/com/structurizr/example/documentation/adr/0007-invoke-adr-config-executable-to-get-configuration.md deleted file mode 100644 index 3d55e3673..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/adr/0007-invoke-adr-config-executable-to-get-configuration.md +++ /dev/null @@ -1,34 +0,0 @@ -# 7. Invoke adr-config executable to get configuration - -Date: 2016-12-17 - -## Status - -Accepted - -## Context - -Packagers (e.g. Homebrew developers) want to configure adr-tools to match the conventions of their installation. - -Currently, this is done by sourcing a file `config.sh`, which should sit beside the `adr` executable. - -This name is too common. - -The `config.sh` file is not executable, and so doesn't belong in a bin directory. - -## Decision - -Replace `config.sh` with an executable, named `adr-config` that outputs configuration. - -Each script in ADR Tools will eval the output of `adr-config` to configure itself. - -## Consequences - -Configuration within ADR Tools is a little more complicated. - -Packagers can write their own implementation of `adr-config` that outputs configuration that matches the platform's installation conventions, and deploy it next to the `adr` script. - -To make development easier, the implementation of `adr-config` in the project's src/ directory will output configuration that lets the tool to run from the src/ directory without any installation step. (Packagers should not include this script in a deployable package.) - ---- -This Architecture Decision Record (ADR) was written by Nat Pryce as a part of [adr-tools](https://github.com/npryce/adr-tools), and is reproduced here under the [Creative Commons Attribution 4.0 International (CC BY 4.0) license](https://creativecommons.org/licenses/by/4.0/). \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/documentation/adr/0008-use-iso-8601-format-for-dates.md b/structurizr-examples/src/com/structurizr/example/documentation/adr/0008-use-iso-8601-format-for-dates.md deleted file mode 100644 index 1167e23f4..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/adr/0008-use-iso-8601-format-for-dates.md +++ /dev/null @@ -1,46 +0,0 @@ -# 8. Use ISO 8601 Format for Dates - -Date: 2017-02-21 - -## Status - -Accepted - -## Context - -`adr-tools` seeks to communicate the history of architectural decisions of a -project. An important component of the history is the time at which a decision -was made. - -To communicate effectively, `adr-tools` should present information as -unambiguously as possible. That means that culture-neutral data formats should -be preferred over culture-specific formats. - -Existing `adr-tools` deployments format dates as `dd/mm/yyyy` by default. That -formatting is common formatting in the United Kingdom (where the `adr-tools` -project was originally written), but is easily confused with the `mm/dd/yyyy` -format preferred in the United States. - -The default date format may be overridden by setting `ADR_DATE` in `config.sh`. - -## Decision - -`adr-tools` will use the ISO 8601 format for dates: `yyyy-mm-dd` - -## Consequences - -Dates are displayed in a standard, culture-neutral format. - -The UK-style and ISO 8601 formats can be distinguished by their separator -character. The UK-style dates used a slash (`/`), while the ISO dates use a -hyphen (`-`). - -Prior to this decision, `adr-tools` was deployed using the UK format for dates. -After adopting the ISO 8601 format, existing deployments of `adr-tools` must do -one of the following: - - * Accept mixed formatting of dates within their documentation library. - * Update existing documents to use ISO 8601 dates by running `adr upgrade-repository` - ---- -This Architecture Decision Record (ADR) was written by Nat Pryce as a part of [adr-tools](https://github.com/npryce/adr-tools), and is reproduced here under the [Creative Commons Attribution 4.0 International (CC BY 4.0) license](https://creativecommons.org/licenses/by/4.0/). \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/01-introduction-and-goals.adoc b/structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/01-introduction-and-goals.adoc deleted file mode 100644 index 4ff008131..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/01-introduction-and-goals.adoc +++ /dev/null @@ -1,7 +0,0 @@ -== Introduction and Goals - -=== Requirements Overview - -=== Quality Goals - -=== Stakeholders diff --git a/structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/02-architecture-constraints.adoc b/structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/02-architecture-constraints.adoc deleted file mode 100644 index e0637beb3..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/02-architecture-constraints.adoc +++ /dev/null @@ -1 +0,0 @@ -== Architecture Constraints diff --git a/structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/03-system-scope-and-context.adoc b/structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/03-system-scope-and-context.adoc deleted file mode 100644 index 06cfd6537..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/03-system-scope-and-context.adoc +++ /dev/null @@ -1,8 +0,0 @@ -== Context and Scope - -image::embed:SystemContext[] - -=== Business Context - -=== Technical Context - diff --git a/structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/04-solution-strategy.adoc b/structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/04-solution-strategy.adoc deleted file mode 100644 index 3d425dfac..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/04-solution-strategy.adoc +++ /dev/null @@ -1 +0,0 @@ -== Solution Strategy diff --git a/structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/05-building-block-view.adoc b/structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/05-building-block-view.adoc deleted file mode 100644 index 4053cea52..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/05-building-block-view.adoc +++ /dev/null @@ -1,7 +0,0 @@ -== Building Block View - -=== Level 1 - -=== Level 2 - -=== Level 3 diff --git a/structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/06-runtime-view.adoc b/structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/06-runtime-view.adoc deleted file mode 100644 index 85b36e5d4..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/06-runtime-view.adoc +++ /dev/null @@ -1,2 +0,0 @@ -== Runtime View - diff --git a/structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/07-deployment-view.adoc b/structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/07-deployment-view.adoc deleted file mode 100644 index de1b72ca7..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/07-deployment-view.adoc +++ /dev/null @@ -1 +0,0 @@ -== Deployment View diff --git a/structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/08-crosscutting-concepts.adoc b/structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/08-crosscutting-concepts.adoc deleted file mode 100644 index 01f6c0c0a..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/08-crosscutting-concepts.adoc +++ /dev/null @@ -1,2 +0,0 @@ -== Crosscutting Concepts - diff --git a/structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/09-architecture-decisions.adoc b/structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/09-architecture-decisions.adoc deleted file mode 100644 index ffabd62e9..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/09-architecture-decisions.adoc +++ /dev/null @@ -1,2 +0,0 @@ -== Architecture Decisions - diff --git a/structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/10-quality-requirements.adoc b/structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/10-quality-requirements.adoc deleted file mode 100644 index 6930d6a20..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/10-quality-requirements.adoc +++ /dev/null @@ -1,6 +0,0 @@ -== Quality Requirements - -=== Quality Tree - -=== Quality Scenarios - diff --git a/structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/11-risks-and-technical-debt.adoc b/structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/11-risks-and-technical-debt.adoc deleted file mode 100644 index 196489aa6..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/11-risks-and-technical-debt.adoc +++ /dev/null @@ -1 +0,0 @@ -== Risks and Technical Debt diff --git a/structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/12-glossary.adoc b/structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/12-glossary.adoc deleted file mode 100644 index 0c393c5a8..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/arc42/asciidoc/12-glossary.adoc +++ /dev/null @@ -1 +0,0 @@ -== Glossary diff --git a/structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/01-introduction-and-goals.md b/structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/01-introduction-and-goals.md deleted file mode 100644 index 30f9e0aa6..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/01-introduction-and-goals.md +++ /dev/null @@ -1,7 +0,0 @@ -## Introduction and Goals - -### Requirements Overview - -### Quality Goals - -### Stakeholders diff --git a/structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/02-architecture-constraints.md b/structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/02-architecture-constraints.md deleted file mode 100644 index 38ca06000..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/02-architecture-constraints.md +++ /dev/null @@ -1 +0,0 @@ -## Architecture Constraints diff --git a/structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/03-system-scope-and-context.md b/structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/03-system-scope-and-context.md deleted file mode 100644 index 275ed2030..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/03-system-scope-and-context.md +++ /dev/null @@ -1,8 +0,0 @@ -## Context and Scope - -![](embed:SystemContext) - -### Business Context - -### Technical Context - diff --git a/structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/04-solution-strategy.md b/structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/04-solution-strategy.md deleted file mode 100644 index ae7819aa8..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/04-solution-strategy.md +++ /dev/null @@ -1 +0,0 @@ -## Solution Strategy diff --git a/structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/05-building-block-view.md b/structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/05-building-block-view.md deleted file mode 100644 index c6e769d56..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/05-building-block-view.md +++ /dev/null @@ -1,7 +0,0 @@ -## Building Block View - -### Level 1 - -### Level 2 - -### Level 3 diff --git a/structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/06-runtime-view.md b/structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/06-runtime-view.md deleted file mode 100644 index bff782219..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/06-runtime-view.md +++ /dev/null @@ -1,2 +0,0 @@ -## Runtime View - diff --git a/structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/07-deployment-view.md b/structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/07-deployment-view.md deleted file mode 100644 index 0600694ef..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/07-deployment-view.md +++ /dev/null @@ -1 +0,0 @@ -## Deployment View diff --git a/structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/08-crosscutting-concepts.md b/structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/08-crosscutting-concepts.md deleted file mode 100644 index 52b1d38eb..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/08-crosscutting-concepts.md +++ /dev/null @@ -1,2 +0,0 @@ -## Crosscutting Concepts - diff --git a/structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/09-architecture-decisions.md b/structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/09-architecture-decisions.md deleted file mode 100644 index 4d176d1a0..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/09-architecture-decisions.md +++ /dev/null @@ -1,2 +0,0 @@ -## Architecture Decisions - diff --git a/structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/10-quality-requirements.md b/structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/10-quality-requirements.md deleted file mode 100644 index 4e98604cf..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/10-quality-requirements.md +++ /dev/null @@ -1,6 +0,0 @@ -## Quality Requirements - -### Quality Tree - -### Quality Scenarios - diff --git a/structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/11-risks-and-technical-debt.md b/structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/11-risks-and-technical-debt.md deleted file mode 100644 index 96f39bb62..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/11-risks-and-technical-debt.md +++ /dev/null @@ -1 +0,0 @@ -## Risks and Technical Debt diff --git a/structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/12-glossary.md b/structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/12-glossary.md deleted file mode 100644 index a4ea737a8..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown/12-glossary.md +++ /dev/null @@ -1 +0,0 @@ -## Glossary diff --git a/structurizr-examples/src/com/structurizr/example/documentation/automatic/01-context.md b/structurizr-examples/src/com/structurizr/example/documentation/automatic/01-context.md deleted file mode 100644 index fb2565d41..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/automatic/01-context.md +++ /dev/null @@ -1,3 +0,0 @@ -## Context - -![](embed:SystemContext) \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/documentation/automatic/02-functional-overview.md b/structurizr-examples/src/com/structurizr/example/documentation/automatic/02-functional-overview.md deleted file mode 100644 index 2997b6e7b..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/automatic/02-functional-overview.md +++ /dev/null @@ -1 +0,0 @@ -## Functional Overview diff --git a/structurizr-examples/src/com/structurizr/example/documentation/automatic/03-quality-attributes.md b/structurizr-examples/src/com/structurizr/example/documentation/automatic/03-quality-attributes.md deleted file mode 100644 index 45b817d4f..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/automatic/03-quality-attributes.md +++ /dev/null @@ -1 +0,0 @@ -## Quality Attributes diff --git a/structurizr-examples/src/com/structurizr/example/documentation/automatic/04-constraints.md b/structurizr-examples/src/com/structurizr/example/documentation/automatic/04-constraints.md deleted file mode 100644 index f6f45c85a..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/automatic/04-constraints.md +++ /dev/null @@ -1 +0,0 @@ -## Constraints diff --git a/structurizr-examples/src/com/structurizr/example/documentation/automatic/05-principles.md b/structurizr-examples/src/com/structurizr/example/documentation/automatic/05-principles.md deleted file mode 100644 index 497b190ce..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/automatic/05-principles.md +++ /dev/null @@ -1 +0,0 @@ -## Principles diff --git a/structurizr-examples/src/com/structurizr/example/documentation/automatic/06-software-architecture.md b/structurizr-examples/src/com/structurizr/example/documentation/automatic/06-software-architecture.md deleted file mode 100644 index ffd3ad203..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/automatic/06-software-architecture.md +++ /dev/null @@ -1 +0,0 @@ -## Software Architecture \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/documentation/automatic/07-data.adoc b/structurizr-examples/src/com/structurizr/example/documentation/automatic/07-data.adoc deleted file mode 100644 index 1fa400652..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/automatic/07-data.adoc +++ /dev/null @@ -1 +0,0 @@ -== Data diff --git a/structurizr-examples/src/com/structurizr/example/documentation/automatic/08-infrastructure-architecture.adoc b/structurizr-examples/src/com/structurizr/example/documentation/automatic/08-infrastructure-architecture.adoc deleted file mode 100644 index a119ffd2e..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/automatic/08-infrastructure-architecture.adoc +++ /dev/null @@ -1 +0,0 @@ -== Infrastructure Architecture diff --git a/structurizr-examples/src/com/structurizr/example/documentation/automatic/09-deployment.adoc b/structurizr-examples/src/com/structurizr/example/documentation/automatic/09-deployment.adoc deleted file mode 100644 index 2cabaa3be..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/automatic/09-deployment.adoc +++ /dev/null @@ -1 +0,0 @@ -== Deployment diff --git a/structurizr-examples/src/com/structurizr/example/documentation/automatic/10-development-environment.adoc b/structurizr-examples/src/com/structurizr/example/documentation/automatic/10-development-environment.adoc deleted file mode 100644 index 05f84c570..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/automatic/10-development-environment.adoc +++ /dev/null @@ -1 +0,0 @@ -== Development Environment diff --git a/structurizr-examples/src/com/structurizr/example/documentation/automatic/11-operation-and-support.adoc b/structurizr-examples/src/com/structurizr/example/documentation/automatic/11-operation-and-support.adoc deleted file mode 100644 index fe570a9d3..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/automatic/11-operation-and-support.adoc +++ /dev/null @@ -1 +0,0 @@ -== Operation and Support diff --git a/structurizr-examples/src/com/structurizr/example/documentation/automatic/12-decision-log.adoc b/structurizr-examples/src/com/structurizr/example/documentation/automatic/12-decision-log.adoc deleted file mode 100644 index 0b98ca9df..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/automatic/12-decision-log.adoc +++ /dev/null @@ -1 +0,0 @@ -== Decision Log diff --git a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/01-context.adoc b/structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/01-context.adoc deleted file mode 100644 index 079aeba93..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/01-context.adoc +++ /dev/null @@ -1,3 +0,0 @@ -== Context - -image::embed:SystemContext[] diff --git a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/02-functional-overview.adoc b/structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/02-functional-overview.adoc deleted file mode 100644 index f859f1774..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/02-functional-overview.adoc +++ /dev/null @@ -1 +0,0 @@ -== Functional Overview diff --git a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/03-quality-attributes.adoc b/structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/03-quality-attributes.adoc deleted file mode 100644 index 18bf9fc77..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/03-quality-attributes.adoc +++ /dev/null @@ -1 +0,0 @@ -== Quality Attributes diff --git a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/04-constraints.adoc b/structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/04-constraints.adoc deleted file mode 100644 index 17efd4403..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/04-constraints.adoc +++ /dev/null @@ -1 +0,0 @@ -== Constraints diff --git a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/05-principles.adoc b/structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/05-principles.adoc deleted file mode 100644 index 6602f92a9..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/05-principles.adoc +++ /dev/null @@ -1 +0,0 @@ -== Principles diff --git a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/06-software-architecture.adoc b/structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/06-software-architecture.adoc deleted file mode 100644 index 48dafb257..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/06-software-architecture.adoc +++ /dev/null @@ -1 +0,0 @@ -== Software Architecture \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/07-data.adoc b/structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/07-data.adoc deleted file mode 100644 index 1fa400652..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/07-data.adoc +++ /dev/null @@ -1 +0,0 @@ -== Data diff --git a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/08-infrastructure-architecture.adoc b/structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/08-infrastructure-architecture.adoc deleted file mode 100644 index a119ffd2e..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/08-infrastructure-architecture.adoc +++ /dev/null @@ -1 +0,0 @@ -== Infrastructure Architecture diff --git a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/09-deployment.adoc b/structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/09-deployment.adoc deleted file mode 100644 index 2cabaa3be..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/09-deployment.adoc +++ /dev/null @@ -1 +0,0 @@ -== Deployment diff --git a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/10-development-environment.adoc b/structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/10-development-environment.adoc deleted file mode 100644 index 05f84c570..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/10-development-environment.adoc +++ /dev/null @@ -1 +0,0 @@ -== Development Environment diff --git a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/11-operation-and-support.adoc b/structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/11-operation-and-support.adoc deleted file mode 100644 index fe570a9d3..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/11-operation-and-support.adoc +++ /dev/null @@ -1 +0,0 @@ -== Operation and Support diff --git a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/12-decision-log.adoc b/structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/12-decision-log.adoc deleted file mode 100644 index 0b98ca9df..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/asciidoc/12-decision-log.adoc +++ /dev/null @@ -1 +0,0 @@ -== Decision Log diff --git a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/01-context.md b/structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/01-context.md deleted file mode 100644 index fb2565d41..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/01-context.md +++ /dev/null @@ -1,3 +0,0 @@ -## Context - -![](embed:SystemContext) \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/02-functional-overview.md b/structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/02-functional-overview.md deleted file mode 100644 index 2997b6e7b..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/02-functional-overview.md +++ /dev/null @@ -1 +0,0 @@ -## Functional Overview diff --git a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/03-quality-attributes.md b/structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/03-quality-attributes.md deleted file mode 100644 index 45b817d4f..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/03-quality-attributes.md +++ /dev/null @@ -1 +0,0 @@ -## Quality Attributes diff --git a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/04-constraints.md b/structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/04-constraints.md deleted file mode 100644 index f6f45c85a..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/04-constraints.md +++ /dev/null @@ -1 +0,0 @@ -## Constraints diff --git a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/05-principles.md b/structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/05-principles.md deleted file mode 100644 index 497b190ce..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/05-principles.md +++ /dev/null @@ -1 +0,0 @@ -## Principles diff --git a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/06-software-architecture.md b/structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/06-software-architecture.md deleted file mode 100644 index ffd3ad203..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/06-software-architecture.md +++ /dev/null @@ -1 +0,0 @@ -## Software Architecture \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/07-data.md b/structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/07-data.md deleted file mode 100644 index 834b75ed8..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/07-data.md +++ /dev/null @@ -1 +0,0 @@ -## Data diff --git a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/08-infrastructure-architecture.md b/structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/08-infrastructure-architecture.md deleted file mode 100644 index 5ee3fd094..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/08-infrastructure-architecture.md +++ /dev/null @@ -1 +0,0 @@ -## Infrastructure Architecture diff --git a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/09-deployment.md b/structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/09-deployment.md deleted file mode 100644 index 9a331e2ef..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/09-deployment.md +++ /dev/null @@ -1 +0,0 @@ -## Deployment diff --git a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/10-development-environment.md b/structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/10-development-environment.md deleted file mode 100644 index 86976762e..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/10-development-environment.md +++ /dev/null @@ -1 +0,0 @@ -## Development Environment diff --git a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/11-operation-and-support.md b/structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/11-operation-and-support.md deleted file mode 100644 index 6de7d37b7..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/11-operation-and-support.md +++ /dev/null @@ -1 +0,0 @@ -## Operation and Support diff --git a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/12-decision-log.md b/structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/12-decision-log.md deleted file mode 100644 index 31371f635..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown/12-decision-log.md +++ /dev/null @@ -1 +0,0 @@ -## Decision Log diff --git a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/01-introduction.adoc b/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/01-introduction.adoc deleted file mode 100644 index 0f2b68f0a..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/01-introduction.adoc +++ /dev/null @@ -1,9 +0,0 @@ -== Introduction - -=== Purpose and Scope - -=== Audience - -=== Status - -=== Architectural Design Approach diff --git a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/02-glossary.adoc b/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/02-glossary.adoc deleted file mode 100644 index 0c393c5a8..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/02-glossary.adoc +++ /dev/null @@ -1 +0,0 @@ -== Glossary diff --git a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/03-system-stakeholders-and-requirements.adoc b/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/03-system-stakeholders-and-requirements.adoc deleted file mode 100644 index 96e0b9b85..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/03-system-stakeholders-and-requirements.adoc +++ /dev/null @@ -1,7 +0,0 @@ -== System Stakeholders and Requirements - -=== Stakeholders - -=== Overview of Requirements - -=== System Scenarios diff --git a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/04-architectural-forces.adoc b/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/04-architectural-forces.adoc deleted file mode 100644 index 35c1b2d69..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/04-architectural-forces.adoc +++ /dev/null @@ -1,7 +0,0 @@ -== Architectural Forces - -=== Goals - -=== Constraints - -=== Architectural Principles diff --git a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/05-architectural-views/01-architectural-views.adoc b/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/05-architectural-views/01-architectural-views.adoc deleted file mode 100644 index b7ef607d9..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/05-architectural-views/01-architectural-views.adoc +++ /dev/null @@ -1 +0,0 @@ -== Architectural Views diff --git a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/05-architectural-views/02-context-view.adoc b/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/05-architectural-views/02-context-view.adoc deleted file mode 100644 index c8d31fc94..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/05-architectural-views/02-context-view.adoc +++ /dev/null @@ -1,3 +0,0 @@ -=== Context View - -image::embed:SystemContext[] diff --git a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/05-architectural-views/03-functional-view.adoc b/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/05-architectural-views/03-functional-view.adoc deleted file mode 100644 index 929b81481..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/05-architectural-views/03-functional-view.adoc +++ /dev/null @@ -1 +0,0 @@ -=== Functional View diff --git a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/05-architectural-views/04-information-view.adoc b/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/05-architectural-views/04-information-view.adoc deleted file mode 100644 index 92aef8ac3..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/05-architectural-views/04-information-view.adoc +++ /dev/null @@ -1 +0,0 @@ -=== Information View diff --git a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/05-architectural-views/05-concurrency-view.adoc b/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/05-architectural-views/05-concurrency-view.adoc deleted file mode 100644 index 5bd240ab1..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/05-architectural-views/05-concurrency-view.adoc +++ /dev/null @@ -1 +0,0 @@ -=== Concurrency View diff --git a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/05-architectural-views/06-deployment-view.adoc b/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/05-architectural-views/06-deployment-view.adoc deleted file mode 100644 index 52a5994a4..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/05-architectural-views/06-deployment-view.adoc +++ /dev/null @@ -1 +0,0 @@ -=== Deployment View diff --git a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/05-architectural-views/07-development-view.adoc b/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/05-architectural-views/07-development-view.adoc deleted file mode 100644 index 169dfc1e8..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/05-architectural-views/07-development-view.adoc +++ /dev/null @@ -1 +0,0 @@ -=== Development View diff --git a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/05-architectural-views/08-operational-view.adoc b/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/05-architectural-views/08-operational-view.adoc deleted file mode 100644 index 8e2f48706..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/05-architectural-views/08-operational-view.adoc +++ /dev/null @@ -1 +0,0 @@ -=== Operational View diff --git a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/06-system-qualities.adoc b/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/06-system-qualities.adoc deleted file mode 100644 index d58efa14d..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/06-system-qualities.adoc +++ /dev/null @@ -1,11 +0,0 @@ -== System Qualities - -=== Performance and Scalability - -=== Security - -=== Availability and Resilience - -=== Evolution - -=== Other Qualities diff --git a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/07-appendices.adoc b/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/07-appendices.adoc deleted file mode 100644 index d9e93d4a8..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/asciidoc/07-appendices.adoc +++ /dev/null @@ -1,7 +0,0 @@ -== Appendices - -=== Decisions and Alternatives - -=== Questions and Answers - -=== References diff --git a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/01-introduction.md b/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/01-introduction.md deleted file mode 100644 index c2a8dc7f5..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/01-introduction.md +++ /dev/null @@ -1,9 +0,0 @@ -## Introduction - -### Purpose and Scope - -### Audience - -### Status - -### Architectural Design Approach diff --git a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/02-glossary.md b/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/02-glossary.md deleted file mode 100644 index a4ea737a8..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/02-glossary.md +++ /dev/null @@ -1 +0,0 @@ -## Glossary diff --git a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/03-system-stakeholders-and-requirements.md b/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/03-system-stakeholders-and-requirements.md deleted file mode 100644 index 484c2cf23..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/03-system-stakeholders-and-requirements.md +++ /dev/null @@ -1,7 +0,0 @@ -## System Stakeholders and Requirements - -### Stakeholders - -### Overview of Requirements - -### System Scenarios diff --git a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/04-architectural-forces.md b/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/04-architectural-forces.md deleted file mode 100644 index 87d89f279..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/04-architectural-forces.md +++ /dev/null @@ -1,7 +0,0 @@ -## Architectural Forces - -### Goals - -### Constraints - -### Architectural Principles diff --git a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/05-architectural-views/01-architectural-views.md b/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/05-architectural-views/01-architectural-views.md deleted file mode 100644 index b1de7b571..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/05-architectural-views/01-architectural-views.md +++ /dev/null @@ -1 +0,0 @@ -## Architectural Views diff --git a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/05-architectural-views/02-context-view.md b/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/05-architectural-views/02-context-view.md deleted file mode 100644 index f72cc3a6c..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/05-architectural-views/02-context-view.md +++ /dev/null @@ -1,3 +0,0 @@ -### Context View - -![](embed:SystemContext) diff --git a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/05-architectural-views/03-functional-view.md b/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/05-architectural-views/03-functional-view.md deleted file mode 100644 index 09b72c0bd..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/05-architectural-views/03-functional-view.md +++ /dev/null @@ -1 +0,0 @@ -### Functional View diff --git a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/05-architectural-views/04-information-view.md b/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/05-architectural-views/04-information-view.md deleted file mode 100644 index 801d78fbe..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/05-architectural-views/04-information-view.md +++ /dev/null @@ -1 +0,0 @@ -### Information View diff --git a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/05-architectural-views/05-concurrency-view.md b/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/05-architectural-views/05-concurrency-view.md deleted file mode 100644 index 7831cac26..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/05-architectural-views/05-concurrency-view.md +++ /dev/null @@ -1 +0,0 @@ -### Concurrency View diff --git a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/05-architectural-views/06-deployment-view.md b/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/05-architectural-views/06-deployment-view.md deleted file mode 100644 index f978bd2c8..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/05-architectural-views/06-deployment-view.md +++ /dev/null @@ -1 +0,0 @@ -### Deployment View diff --git a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/05-architectural-views/07-development-view.md b/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/05-architectural-views/07-development-view.md deleted file mode 100644 index b2fe7847d..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/05-architectural-views/07-development-view.md +++ /dev/null @@ -1 +0,0 @@ -### Development View diff --git a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/05-architectural-views/08-operational-view.md b/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/05-architectural-views/08-operational-view.md deleted file mode 100644 index fc13034df..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/05-architectural-views/08-operational-view.md +++ /dev/null @@ -1 +0,0 @@ -### Operational View diff --git a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/06-system-qualities.md b/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/06-system-qualities.md deleted file mode 100644 index 4ee8995ab..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/06-system-qualities.md +++ /dev/null @@ -1,11 +0,0 @@ -## System Qualities - -### Performance and Scalability - -### Security - -### Availability and Resilience - -### Evolution - -### Other Qualities diff --git a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/07-appendices.md b/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/07-appendices.md deleted file mode 100644 index 29e1d7fbc..000000000 --- a/structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown/07-appendices.md +++ /dev/null @@ -1,7 +0,0 @@ -## Appendices - -### Decisions and Alternatives - -### Questions and Answers - -### References diff --git a/structurizr-examples/src/com/structurizr/example/financialrisksystem/01-context.md b/structurizr-examples/src/com/structurizr/example/financialrisksystem/01-context.md deleted file mode 100644 index 4841c7408..000000000 --- a/structurizr-examples/src/com/structurizr/example/financialrisksystem/01-context.md +++ /dev/null @@ -1,18 +0,0 @@ -## Context - -A global investment bank based in London, New York and Singapore trades (buys and sells) financial products with other banks (counterparties). When share prices on the stock markets move up or down, the bank either makes money or loses it. At the end of the working day, the bank needs to gain a view of how much risk they are exposed to (e.g. of losing money) by running some calculations on the data held about their trades. The bank has an existing Trade Data System (TDS) and Reference Data System (RDS) but need a new Risk System. - -![](embed:Context) - -### Trade Data System - -The Trade Data System maintains a store of all trades made by the bank. It is already configured to generate a file-based XML export of trade data at the close of business (5pm) in New York. The export includes the following information for every trade made by the bank: - -- Trade ID -- Date -- Current trade value in US dollars -- Counterparty ID - -### Reference Data System - -The Reference Data System maintains all of the reference data needed by the bank. This includes information about counterparties; each of which represents an individual, a bank, etc. A file-based XML export is also available and includes basic information about each counterparty. A new organisation-wide reference data system is due for completion in the next 3 months, with the current system eventually being decommissioned. \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/financialrisksystem/02-functional-overview.md b/structurizr-examples/src/com/structurizr/example/financialrisksystem/02-functional-overview.md deleted file mode 100644 index ee62c2f3e..000000000 --- a/structurizr-examples/src/com/structurizr/example/financialrisksystem/02-functional-overview.md +++ /dev/null @@ -1,13 +0,0 @@ -## Functional Overview - -The high-level functional requirements for the new Risk System are as follows. - -![Functional overview](images/functional-overview.png) - -1. Import trade data from the Trade Data System. -2. Import counterparty data from the Reference Data System. -3. Join the two sets of data together, enriching the trade data with information about the counterparty. -4. For each counterparty, calculate the risk that the bank is exposed to. -5. Generate a report that can be imported into Microsoft Excel containing the risk figures for all counterparties known by the bank. -6. Distribute the report to the business users before the start of the next trading day (9am) in Singapore. -7. Provide a way for a subset of the business users to configure and maintain the external parameters used by the risk calculations. diff --git a/structurizr-examples/src/com/structurizr/example/financialrisksystem/03-quality-attributes.md b/structurizr-examples/src/com/structurizr/example/financialrisksystem/03-quality-attributes.md deleted file mode 100644 index a9af187ca..000000000 --- a/structurizr-examples/src/com/structurizr/example/financialrisksystem/03-quality-attributes.md +++ /dev/null @@ -1,61 +0,0 @@ -## Quality Attributes - -The quality attributes for the new Financial Risk System are as follows. - -### Performance - -- Risk reports must be generated before 9am the following business day in Singapore. - -### Scalability -- The system must be able to cope with trade volumes for the next 5 years. -- The Trade Data System export includes approximately 5000 trades now and it is anticipated that there will be an additional 10 trades per day. -- The Reference Data System counterparty export includes approximately 20,000 counterparties and growth will be negligible. -- There are 40-50 business users around the world that need access to the report. - -### Availability - -- Risk reports should be available to users 24x7, but a small amount of downtime (less than 30 minutes per day) can be tolerated. - -### Failover - -- Manual failover is sufficient for all system components, provided that the availability targets can be met. - -### Security - -- This system must follow bank policy that states system access is restricted to authenticated and authorised users only. -- Reports must only be distributed to authorised users. -- Only a subset of the authorised users are permitted to modify the parameters used in the risk calculations. -- Although desirable, there are no single sign-on requirements (e.g. integration with Active Directory, LDAP, etc). -- All access to the system and reports will be within the confines of the bank's global network. - -### Audit - -- The following events must be recorded in the system audit logs: - - Report generation. - - Modification of risk calculation parameters. -- It must be possible to understand the input data that was used in calculating risk. - -### Fault Tolerance and Resilience - -- The system should take appropriate steps to recover from an error if possible, but all errors should be logged. -- Errors preventing a counterparty risk calculation being completed should be logged and the process should continue. - -### Internationalization and Localization - -- All user interfaces will be presented in English only. -- All reports will be presented in English only. -- All trading values and risk figures will be presented in US dollars only. - -### Monitoring and Management - -- A Simple Network Management Protocol (SNMP) trap should be sent to the bank's Central Monitoring Service in the following circumstances: - - When there is a fatal error with a system component. - - When reports have not been generated before 9am Singapore time. - -### Data Retention and Archiving - -- Input files used in the risk calculation process must be retained for 1 year. - -### Interoperability - -- Interfaces with existing data systems should conform to and use existing data formats. \ No newline at end of file diff --git a/structurizr-examples/src/com/structurizr/example/financialrisksystem/images/functional-overview.png b/structurizr-examples/src/com/structurizr/example/financialrisksystem/images/functional-overview.png deleted file mode 100644 index 258545639e6db7b64aee6a18393605c417c2bc7b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 38234 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 Date: Wed, 23 Feb 2022 14:52:16 +0000 Subject: [PATCH 281/717] Change examples link. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b24f4795f..bb3c09cce 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ The view can then be exported to be visualised using the [Structurizr cloud serv * [Why use code?](https://structurizr.com/help/code) * [Basic concepts](https://structurizr.com/help/concepts) (workspaces, models, views and documentation) * [C4 model](https://c4model.com) - * [Examples](https://structurizr.com/help/examples) + * [Examples](https://github.com/structurizr/examples) * [Binaries](docs/binaries.md) * [Building from source](docs/building.md) * [API client](docs/api-client.md) From 3a3c4a4a883c77992c0eed737d6eaeb976fb08cf Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 23 Feb 2022 15:59:54 +0000 Subject: [PATCH 282/717] Breaking API changes to how documentation and decisions are managed. --- build.gradle | 2 +- docs/changelog.md | 5 + .../structurizr-31-workspace.json} | 214 +- .../structurizr-36141-workspace.json | 2010 +++++++++++++++++ .../structurizr-39459-workspace.json | 278 +++ .../api/BackwardsCompatibilityTests.java | 19 + .../api/WorkspaceRulesValidationTests.java | 20 - ...atedWithDecisionIsMissingFromTheModel.json | 32 - ...mentationSectionIsMissingFromTheModel.json | 35 - .../integration/workspaces/bigbankplc.json | 1452 ------------ .../workspaces/spring-petclinic.json | 1719 -------------- .../structurizr/util/WorkspaceUtilsTests.java | 16 - .../src/com/structurizr/Workspace.java | 33 +- .../Arc42DocumentationTemplate.java | 334 --- .../AutomaticDocumentationTemplate.java | 127 -- .../structurizr/documentation/Decision.java | 60 +- .../documentation/DecisionStatus.java | 14 - .../documentation/Documentation.java | 174 +- .../documentation/DocumentationTemplate.java | 257 --- .../documentation/FormatFinder.java | 37 - .../com/structurizr/documentation/Link.java | 52 + .../structurizr/documentation/Section.java | 38 +- .../StructurizrDocumentationTemplate.java | 444 ---- .../documentation/TemplateMetadata.java | 45 - ...sAndPerspectivesDocumentationTemplate.java | 209 -- .../com/structurizr/model/SoftwareSystem.java | 21 + .../unit/com/structurizr/WorkspaceTests.java | 11 +- .../Arc42DocumentationTemplateTests.java | 128 -- .../AutomaticDocumentTemplateTests.java | 99 - .../documentation/DecisionTests.java | 23 + .../DocumentationTemplateTests.java | 237 -- .../documentation/DocumentationTests.java | 147 +- .../documentation/FormatFinderTests.java | 36 - .../documentation/SectionTests.java | 8 +- ...StructurizrDocumentationTemplateTests.java | 170 -- ...erspectivesDocumentationTemplateTests.java | 99 - .../arc42/architectural-decisions.md | 1 - .../arc42/building-block-view.md | 1 - .../documentation/arc42/constraints.md | 1 - .../documentation/arc42/context-and-scope.md | 1 - .../arc42/crosscutting-concepts.md | 1 - .../documentation/arc42/deployment-view.md | 1 - .../documentation/arc42/glossary.md | 1 - .../arc42/introduction-and-goals.md | 1 - .../arc42/quality-requirements.md | 1 - .../arc42/risks-and-technical-debt.md | 1 - .../documentation/arc42/runtime-view.md | 1 - .../documentation/arc42/solution-strategy.md | 1 - .../documentation/automatic/01-section-1.md | 1 - .../automatic/02-section-2.markdown | 1 - .../documentation/automatic/03-section-3.text | 1 - .../documentation/automatic/04-section-4.adoc | 1 - .../automatic/05-section-5.asciidoc | 1 - .../documentation/automatic/06-section-6.asc | 1 - .../automatic/07-subdirectory/01-section-1.md | 1 - .../com/structurizr/documentation/image.gif | Bin 4682 -> 0 bytes .../com/structurizr/documentation/image.jpeg | Bin 1467 -> 0 bytes .../com/structurizr/documentation/image.jpg | Bin 1467 -> 0 bytes .../com/structurizr/documentation/image.png | Bin 1563 -> 0 bytes .../documentation/images/image.gif | Bin 4682 -> 0 bytes .../documentation/images/image.jpeg | Bin 1467 -> 0 bytes .../documentation/images/image.jpg | Bin 1467 -> 0 bytes .../documentation/images/image.png | Bin 1563 -> 0 bytes .../structurizr/documentation/markdown/1.md | 1 - .../documentation/markdown/subdirectory/2.md | 1 - .../documentation/noimages/readme.md | 1 - .../structurizr/code-for-componentA1.md | 1 - .../structurizr/code-for-componentA2.md | 1 - .../structurizr/components-for-containerA.md | 1 - .../structurizr/components-for-containerB.md | 1 - .../documentation/structurizr/constraints.md | 1 - .../documentation/structurizr/containers.md | 1 - .../documentation/structurizr/context.md | 1 - .../documentation/structurizr/data.md | 1 - .../documentation/structurizr/decision-log.md | 1 - .../documentation/structurizr/deployment.md | 1 - .../structurizr/development-environment.md | 1 - .../structurizr/functional-overview.md | 1 - .../infrastructure-architecture.md | 1 - .../structurizr/operation-and-support.md | 1 - .../documentation/structurizr/principles.md | 1 - .../structurizr/quality-attributes.md | 1 - .../structurizr/software-architecture.md | 1 - .../01-introduction.md | 1 - .../viewpointsandperspectives/02-glossary.md | 1 - ...03-system-stakeholders-and-requirements.md | 1 - .../04-architectural-forces.md | 1 - .../05-architectural-views.md | 1 - .../06-system-qualities.md | 1 - .../07-appendices.adoc | 1 - 90 files changed, 2684 insertions(+), 5967 deletions(-) rename structurizr-client/test/integration/{workspaces/financial-risk-system.json => backwardsCompatibility/structurizr-31-workspace.json} (95%) create mode 100644 structurizr-client/test/integration/backwardsCompatibility/structurizr-36141-workspace.json create mode 100644 structurizr-client/test/integration/backwardsCompatibility/structurizr-39459-workspace.json create mode 100644 structurizr-client/test/integration/com/structurizr/api/BackwardsCompatibilityTests.java delete mode 100644 structurizr-client/test/integration/workspaceValidation/ElementAssociatedWithDecisionIsMissingFromTheModel.json delete mode 100644 structurizr-client/test/integration/workspaceValidation/ElementAssociatedWithDocumentationSectionIsMissingFromTheModel.json delete mode 100644 structurizr-client/test/integration/workspaces/bigbankplc.json delete mode 100644 structurizr-client/test/integration/workspaces/spring-petclinic.json delete mode 100644 structurizr-core/src/com/structurizr/documentation/Arc42DocumentationTemplate.java delete mode 100644 structurizr-core/src/com/structurizr/documentation/AutomaticDocumentationTemplate.java delete mode 100644 structurizr-core/src/com/structurizr/documentation/DecisionStatus.java delete mode 100644 structurizr-core/src/com/structurizr/documentation/DocumentationTemplate.java delete mode 100644 structurizr-core/src/com/structurizr/documentation/FormatFinder.java create mode 100644 structurizr-core/src/com/structurizr/documentation/Link.java delete mode 100644 structurizr-core/src/com/structurizr/documentation/StructurizrDocumentationTemplate.java delete mode 100644 structurizr-core/src/com/structurizr/documentation/TemplateMetadata.java delete mode 100644 structurizr-core/src/com/structurizr/documentation/ViewpointsAndPerspectivesDocumentationTemplate.java delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/Arc42DocumentationTemplateTests.java delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/AutomaticDocumentTemplateTests.java create mode 100644 structurizr-core/test/unit/com/structurizr/documentation/DecisionTests.java delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/DocumentationTemplateTests.java delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/FormatFinderTests.java delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/StructurizrDocumentationTemplateTests.java delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/ViewpointsAndPerspectivesDocumentationTemplateTests.java delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/arc42/architectural-decisions.md delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/arc42/building-block-view.md delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/arc42/constraints.md delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/arc42/context-and-scope.md delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/arc42/crosscutting-concepts.md delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/arc42/deployment-view.md delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/arc42/glossary.md delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/arc42/introduction-and-goals.md delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/arc42/quality-requirements.md delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/arc42/risks-and-technical-debt.md delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/arc42/runtime-view.md delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/arc42/solution-strategy.md delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/automatic/01-section-1.md delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/automatic/02-section-2.markdown delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/automatic/03-section-3.text delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/automatic/04-section-4.adoc delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/automatic/05-section-5.asciidoc delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/automatic/06-section-6.asc delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/automatic/07-subdirectory/01-section-1.md delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/image.gif delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/image.jpeg delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/image.jpg delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/image.png delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/images/image.gif delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/images/image.jpeg delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/images/image.jpg delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/images/image.png delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/markdown/1.md delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/markdown/subdirectory/2.md delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/noimages/readme.md delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/structurizr/code-for-componentA1.md delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/structurizr/code-for-componentA2.md delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/structurizr/components-for-containerA.md delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/structurizr/components-for-containerB.md delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/structurizr/constraints.md delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/structurizr/containers.md delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/structurizr/context.md delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/structurizr/data.md delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/structurizr/decision-log.md delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/structurizr/deployment.md delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/structurizr/development-environment.md delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/structurizr/functional-overview.md delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/structurizr/infrastructure-architecture.md delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/structurizr/operation-and-support.md delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/structurizr/principles.md delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/structurizr/quality-attributes.md delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/structurizr/software-architecture.md delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/viewpointsandperspectives/01-introduction.md delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/viewpointsandperspectives/02-glossary.md delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/viewpointsandperspectives/03-system-stakeholders-and-requirements.md delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/viewpointsandperspectives/04-architectural-forces.md delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/viewpointsandperspectives/05-architectural-views.md delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/viewpointsandperspectives/06-system-qualities.md delete mode 100644 structurizr-core/test/unit/com/structurizr/documentation/viewpointsandperspectives/07-appendices.adoc diff --git a/build.gradle b/build.gradle index 241c3bc03..e66982caa 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.11.0' + version = '1.12.0' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 45128ae6c..ae8b3d09e 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,10 @@ # Changelog +## 1.12.0 (unreleased) + +- Breaking API changes to how documentation and decisions are managed. +- Moved documentation importers/templates to [structurizr-documentation](https://github.com/structurizr/documentation). + ## 1.11.0 (18th February 2022) - Fixes #167 (ImpliedRelationship Strategy replication of URL and perspectives). diff --git a/structurizr-client/test/integration/workspaces/financial-risk-system.json b/structurizr-client/test/integration/backwardsCompatibility/structurizr-31-workspace.json similarity index 95% rename from structurizr-client/test/integration/workspaces/financial-risk-system.json rename to structurizr-client/test/integration/backwardsCompatibility/structurizr-31-workspace.json index 48ca45b3c..1d02a645b 100644 --- a/structurizr-client/test/integration/workspaces/financial-risk-system.json +++ b/structurizr-client/test/integration/backwardsCompatibility/structurizr-31-workspace.json @@ -2,37 +2,41 @@ "id": 31, "name": "Financial Risk System", "description": "This is a simple (incomplete) example C4 model based upon the financial risk system architecture kata, which can be found at http://bit.ly/sa4d-risksystem", + "revision": 3, + "lastModifiedDate": "2019-07-23T06:42:29Z", + "lastModifiedUser": "", + "lastModifiedAgent": "structurizr-web/1602", "model": { "people": [ { + "id": "2", "tags": "Element,Person", - "id": "4", - "name": "Configuration User", - "description": "A regular business user who can also configure the parameters used in the risk calculations.", + "name": "Business User", + "description": "A regular business user.", "relationships": [ { + "id": "3", "tags": "Relationship,Synchronous", - "id": "5", - "sourceId": "4", + "sourceId": "2", "destinationId": "1", - "description": "Configures parameters using", + "description": "Views reports using", "interactionStyle": "Synchronous" } ], "location": "Unspecified" }, { + "id": "4", "tags": "Element,Person", - "id": "2", - "name": "Business User", - "description": "A regular business user.", + "name": "Configuration User", + "description": "A regular business user who can also configure the parameters used in the risk calculations.", "relationships": [ { + "id": "5", "tags": "Relationship,Synchronous", - "id": "3", - "sourceId": "2", + "sourceId": "4", "destinationId": "1", - "description": "Views reports using", + "description": "Configures parameters using", "interactionStyle": "Synchronous" } ], @@ -41,14 +45,28 @@ ], "softwareSystems": [ { + "id": "17", "tags": "Element,Software System", + "name": "Active Directory", + "description": "The bank's authentication and authorisation system.", + "location": "Unspecified" + }, + { + "id": "15", + "tags": "Element,Software System", + "name": "Central Monitoring Service", + "description": "The bank's central monitoring and alerting dashboard.", + "location": "Unspecified" + }, + { "id": "12", + "tags": "Element,Software System", "name": "E-mail system", "description": "The bank's Microsoft Exchange system.", "relationships": [ { - "tags": "Relationship,Asynchronous", "id": "14", + "tags": "Relationship,Asynchronous", "sourceId": "12", "destinationId": "2", "description": "Sends a notification that a report is ready to", @@ -59,54 +77,54 @@ "location": "Unspecified" }, { - "tags": "Element,Software System,Risk System", "id": "1", + "tags": "Element,Software System,Risk System", "name": "Financial Risk System", "description": "Calculates the bank's exposure to risk for product X.", "relationships": [ { - "tags": "Relationship,Synchronous", "id": "7", + "tags": "Relationship,Synchronous", "sourceId": "1", "destinationId": "6", "description": "Gets trade data from", "interactionStyle": "Synchronous" }, { - "tags": "Relationship,Synchronous,Future State", "id": "11", + "tags": "Relationship,Synchronous,Future State", "sourceId": "1", "destinationId": "10", "description": "Gets counterparty data from", "interactionStyle": "Synchronous" }, { - "tags": "Relationship,Synchronous", "id": "13", + "tags": "Relationship,Synchronous", "sourceId": "1", "destinationId": "12", "description": "Sends a notification that a report is ready to", "interactionStyle": "Synchronous" }, { - "tags": "Relationship,Synchronous", "id": "18", + "tags": "Relationship,Synchronous", "sourceId": "1", "destinationId": "17", "description": "Uses for user authentication and authorisation", "interactionStyle": "Synchronous" }, { - "tags": "Relationship,Synchronous", "id": "9", + "tags": "Relationship,Synchronous", "sourceId": "1", "destinationId": "8", "description": "Gets counterparty data from", "interactionStyle": "Synchronous" }, { - "tags": "Relationship,Asynchronous,Alert", "id": "16", + "tags": "Relationship,Asynchronous,Alert", "sourceId": "1", "destinationId": "15", "description": "Sends critical failure alerts to", @@ -117,84 +135,70 @@ "location": "Unspecified" }, { + "id": "8", "tags": "Element,Software System", - "id": "15", - "name": "Central Monitoring Service", - "description": "The bank's central monitoring and alerting dashboard.", - "location": "Unspecified" - }, - { - "tags": "Element,Software System", - "id": "6", - "name": "Trade Data System", - "description": "The system of record for trades of type X.", + "name": "Reference Data System", + "description": "Manages reference data for all counterparties the bank interacts with.", "location": "Unspecified" }, { - "tags": "Element,Software System,Future State", "id": "10", + "tags": "Element,Software System,Future State", "name": "Reference Data System v2.0", "description": "Manages reference data for all counterparties the bank interacts with.", "location": "Unspecified" }, { + "id": "6", "tags": "Element,Software System", - "id": "8", - "name": "Reference Data System", - "description": "Manages reference data for all counterparties the bank interacts with.", - "location": "Unspecified" - }, - { - "tags": "Element,Software System", - "id": "17", - "name": "Active Directory", - "description": "The bank's authentication and authorisation system.", + "name": "Trade Data System", + "description": "The system of record for trades of type X.", "location": "Unspecified" } - ] + ], + "customElements": [], + "deploymentNodes": [] }, "documentation": { "sections": [ { "elementId": "1", "type": "Context", + "title": "Context", "order": 1, "format": "AsciiDoc", - "content": "== Context\n\nA global investment bank based in London, New York and Singapore trades (buys and sells) financial products with other banks (counterparties). When share prices on the stock markets move up or down, the bank either makes money or loses it. At the end of the working day, the bank needs to gain a view of how much risk they are exposed to (e.g. of losing money) by running some calculations on the data held about their trades. The bank has an existing Trade Data System (TDS) and Reference Data System (RDS) but need a new Risk System.\n\nimage::embed:Context[]\n\n=== Trade Data System\n\nThe Trade Data System maintains a store of all trades made by the bank. It is already configured to generate a file-based XML export of trade data at the close of business (5pm) in New York. The export includes the following information for every trade made by the bank:\n\n* Trade ID\n* Date\n* Current trade value in US dollars\n* Counterparty ID\n\n=== Reference Data System\n\nThe Reference Data System maintains all of the reference data needed by the bank. This includes information about counterparties; each of which represents an individual, a bank, etc. A file-based XML export is also available and includes basic information about each counterparty. A new organisation-wide reference data system is due for completion in the next 3 months, with the current system eventually being decommissioned.", - "group": "1", - "number": 1 + "content": "== Context\n\nA global investment bank based in London, New York and Singapore trades (buys and sells) financial products with other banks (counterparties). When share prices on the stock markets move up or down, the bank either makes money or loses it. At the end of the working day, the bank needs to gain a view of how much risk they are exposed to (e.g. of losing money) by running some calculations on the data held about their trades. The bank has an existing Trade Data System (TDS) and Reference Data System (RDS) but need a new Risk System.\n\nimage::https://structurizr.com/share/31/images/Context.png[]\n\n=== Trade Data System\n\nThe Trade Data System maintains a store of all trades made by the bank. It is already configured to generate a file-based XML export of trade data at the close of business (5pm) in New York. The export includes the following information for every trade made by the bank:\n\n* Trade ID\n* Date\n* Current trade value in US dollars\n* Counterparty ID\n\n=== Reference Data System\n\nThe Reference Data System maintains all of the reference data needed by the bank. This includes information about counterparties; each of which represents an individual, a bank, etc. A file-based XML export is also available and includes basic information about each counterparty. A new organisation-wide reference data system is due for completion in the next 3 months, with the current system eventually being decommissioned." }, { "elementId": "1", "type": "Functional Overview", + "title": "Functional Overview", "order": 2, "format": "Markdown", - "content": "## Functional Overview\n\nThe high-level functional requirements for the new Risk System are as follows.\n\n![Functional overview](images/functional-overview.png)\n\n1. Import trade data from the Trade Data System.\n2. Import counterparty data from the Reference Data System.\n3. Join the two sets of data together, enriching the trade data with information about the counterparty.\n4. For each counterparty, calculate the risk that the bank is exposed to.\n5. Generate a report that can be imported into Microsoft Excel containing the risk figures for all counterparties known by the bank.\n6. Distribute the report to the business users before the start of the next trading day (9am) in Singapore.\n7. Provide a way for a subset of the business users to configure and maintain the external parameters used by the risk calculations.\n", - "group": "2", - "number": 2 + "content": "## Functional Overview\n\nThe high-level functional requirements for the new Risk System are as follows.\n\n![Functional overview](images/functional-overview.png)\n\n1. Import trade data from the Trade Data System.\n2. Import counterparty data from the Reference Data System.\n3. Join the two sets of data together, enriching the trade data with information about the counterparty.\n4. For each counterparty, calculate the risk that the bank is exposed to.\n5. Generate a report that can be imported into Microsoft Excel containing the risk figures for all counterparties known by the bank.\n6. Distribute the report to the business users before the start of the next trading day (9am) in Singapore.\n7. Provide a way for a subset of the business users to configure and maintain the external parameters used by the risk calculations.\n" }, { "elementId": "1", "type": "Quality Attributes", + "title": "Quality Attributes", "order": 3, "format": "Markdown", - "content": "## Quality Attributes\n\nThe quality attributes for the new Financial Risk System are as follows.\n\n### Performance\n\n- Risk reports must be generated before 9am the following business day in Singapore.\n \n### Scalability\n- The system must be able to cope with trade volumes for the next 5 years.\n- The Trade Data System export includes approximately 5000 trades now and it is anticipated that there will be an additional 10 trades per day.\n- The Reference Data System counterparty export includes approximately 20,000 counterparties and growth will be negligible.\n- There are 40-50 business users around the world that need access to the report.\n\n### Availability\n\n- Risk reports should be available to users 24x7, but a small amount of downtime (less than 30 minutes per day) can be tolerated.\n\n### Failover\n\n- Manual failover is sufficient for all system components, provided that the availability targets can be met.\n\n### Security\n\n- This system must follow bank policy that states system access is restricted to authenticated and authorised users only.\n- Reports must only be distributed to authorised users.\n- Only a subset of the authorised users are permitted to modify the parameters used in the risk calculations.\n- Although desirable, there are no single sign-on requirements (e.g. integration with Active Directory, LDAP, etc).\n- All access to the system and reports will be within the confines of the bank's global network.\n\n### Audit\n\n- The following events must be recorded in the system audit logs:\n - Report generation.\n - Modification of risk calculation parameters.\n- It must be possible to understand the input data that was used in calculating risk.\n\n### Fault Tolerance and Resilience\n\n- The system should take appropriate steps to recover from an error if possible, but all errors should be logged.\n- Errors preventing a counterparty risk calculation being completed should be logged and the process should continue.\n\n### Internationalization and Localization\n\n- All user interfaces will be presented in English only.\n- All reports will be presented in English only.\n- All trading values and risk figures will be presented in US dollars only.\n\n### Monitoring and Management\n\n- A Simple Network Management Protocol (SNMP) trap should be sent to the bank's Central Monitoring Service in the following circumstances:\n - When there is a fatal error with a system component.\n - When reports have not been generated before 9am Singapore time.\n\n### Data Retention and Archiving\n\n- Input files used in the risk calculation process must be retained for 1 year.\n\n### Interoperability\n\n- Interfaces with existing data systems should conform to and use existing data formats.", - "group": "2", - "number": 3 + "content": "## Quality Attributes\n\nThe quality attributes for the new Financial Risk System are as follows.\n\n### Performance\n\n- Risk reports must be generated before 9am the following business day in Singapore.\n \n### Scalability\n- The system must be able to cope with trade volumes for the next 5 years.\n- The Trade Data System export includes approximately 5000 trades now and it is anticipated that there will be an additional 10 trades per day.\n- The Reference Data System counterparty export includes approximately 20,000 counterparties and growth will be negligible.\n- There are 40-50 business users around the world that need access to the report.\n\n### Availability\n\n- Risk reports should be available to users 24x7, but a small amount of downtime (less than 30 minutes per day) can be tolerated.\n\n### Failover\n\n- Manual failover is sufficient for all system components, provided that the availability targets can be met.\n\n### Security\n\n- This system must follow bank policy that states system access is restricted to authenticated and authorised users only.\n- Reports must only be distributed to authorised users.\n- Only a subset of the authorised users are permitted to modify the parameters used in the risk calculations.\n- Although desirable, there are no single sign-on requirements (e.g. integration with Active Directory, LDAP, etc).\n- All access to the system and reports will be within the confines of the bank's global network.\n\n### Audit\n\n- The following events must be recorded in the system audit logs:\n - Report generation.\n - Modification of risk calculation parameters.\n- It must be possible to understand the input data that was used in calculating risk.\n\n### Fault Tolerance and Resilience\n\n- The system should take appropriate steps to recover from an error if possible, but all errors should be logged.\n- Errors preventing a counterparty risk calculation being completed should be logged and the process should continue.\n\n### Internationalization and Localization\n\n- All user interfaces will be presented in English only.\n- All reports will be presented in English only.\n- All trading values and risk figures will be presented in US dollars only.\n\n### Monitoring and Management\n\n- A Simple Network Management Protocol (SNMP) trap should be sent to the bank's Central Monitoring Service in the following circumstances:\n - When there is a fatal error with a system component.\n - When reports have not been generated before 9am Singapore time.\n\n### Data Retention and Archiving\n\n- Input files used in the risk calculation process must be retained for 1 year.\n\n### Interoperability\n\n- Interfaces with existing data systems should conform to and use existing data formats." } ], "images": [ { - "name": "codingthearchitecture.png", - "content": "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", + "name": "images/functional-overview.png", + "content": "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V1G1v86ZM4fBywvnQspHHnmExgDBGfBGrzTr5vY6WC5atIgGKaQWLVqYthswYADhhKKCvPr372/6a8uWLRm8clm6dKlpG937Ojk5mQYBwRnw5cMPP7RuD83CmYLTvU3LlCkjpUuXNns4E0woKrhL+2lERISULVuWvYltjh49au3vP2jQIBoEBGcgL7oA5IYbbjCD5quvvkqDFND06dNNm919992EEooKkdL+qv12xowZDGI5XnzxRdMmN910EwsCQXAGCuKnn34yV0/1asyqVatokALo3LmzOdn873//I5BQVIiU9lftt9p/IRIZGSmlSpWScuXKSVxcHA0CgjNQUH379rW2IdK9iZE350Kj77//nkBCUSFSS5Yssca5kk6nq1x22WXWOg2A4AwUwokTJ+T66683g2iXLl1okDzoHxbaTjpXcv/+/QQSigqR0i3X9NM17b8l/QKBLpLUdrjjjjuYogGCM+CPn3/+WcqXL8+WTQVoJ22jf/zjH4QRigqx0n6r/XfTpk0ldgxz3k2xWrVq7KIBgjNwJoYPH24G1PPPP1/27NlDg3ihC4u0jZo1a0YQoagQK+232n9nzpxZIscvXdPivEBSUtsABGegSDVv3twMqrfddpuZwgF3I0eONO3z/PPPE0QoKsSqQ4cOpv9qPy5p9P0773aqu2kABGegCBw8eFCuvPJKM7h26tSJBslF9zrVtnn55ZcJIhQVYvV///d/JXLP4j///FOaNm1qzWvm1uMgOANFKDY2VipUqGAG2c8//5wGsXHuQKL/JYhQVGhVnz59TP994403StS45dyv+aKLLpLU1FQGchCcgaI2adIkM9DqHp9RUVE0SI7XXnvNtMs777xDEKGoEKu3337b9F/txyXFJ598Yt6zXgxZvXo1gzgIzkBx6datmxlwq1evLjt27KBBCM4URXAOIfPmzTM3t9IbnegdTwGCM1CM/vrrL3nkkUes7dd0/jPBmeBMUQTn4LdixQqpWLEiNzkBwRkIpGPHjsmNN95oBt+GDRua/09wJjhTFME5eOk+1bpPM4u8QXAGzoJ9+/bJ1VdfbQbh+++/v0RvU0dwpiiCczDbtm2bWQSo77N169bmk0OA4AwEWGJiotSqVcsMxo8//niJvU0rwZmiCM7BavPmzdY4XdIvcoDgDJx1ervpqlWrmkG5ZcuWJXJQJjhTFME5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", "type": "image/png" }, { - "name": "images/functional-overview.png", - "content": "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V1G1v86ZM4fBywvnQspHHnmExgDBGfBGrzTr5vY6WC5atIgGKaQWLVqYthswYADhhKKCvPr372/6a8uWLRm8clm6dKlpG937Ojk5mQYBwRnw5cMPP7RuD83CmYLTvU3LlCkjpUuXNns4E0woKrhL+2lERISULVuWvYltjh49au3vP2jQIBoEBGcgL7oA5IYbbjCD5quvvkqDFND06dNNm919992EEooKkdL+qv12xowZDGI5XnzxRdMmN910EwsCQXAGCuKnn34yV0/1asyqVatokALo3LmzOdn873//I5BQVIiU9lftt9p/IRIZGSmlSpWScuXKSVxcHA0CgjNQUH379rW2IdK9iZE350Kj77//nkBCUSFSS5Yssca5kk6nq1x22WXWOg2A4AwUwokTJ+T66683g2iXLl1okDzoHxbaTjpXcv/+/QQSigqR0i3X9NM17b8l/QKBLpLUdrjjjjuYogGCM+CPn3/+WcqXL8+WTQVoJ22jf/zjH4QRigqx0n6r/XfTpk0ldgxz3k2xWrVq7KIBgjNwJoYPH24G1PPPP1/27NlDg3ihC4u0jZo1a0YQoagQK+232n9nzpxZIscvXdPivEBSUtsABGegSDVv3twMqrfddpuZwgF3I0eONO3z/PPPE0QoKsSqQ4cOpv9qPy5p9P0773aqu2kABGegCBw8eFCuvPJKM7h26tSJBslF9zrVtnn55ZcJIhQVYvV///d/JXLP4j///FOaNm1qzWvm1uMgOANFKDY2VipUqGAG2c8//5wGsXHuQKL/JYhQVGhVnz59TP994403StS45dyv+aKLLpLU1FQGchCcgaI2adIkM9DqHp9RUVE0SI7XXnvNtMs777xDEKGoEKu3337b9F/txyXFJ598Yt6zXgxZvXo1gzgIzkBx6datmxlwq1evLjt27KBBCM4URXAOIfPmzTM3t9IbnegdTwGCM1CM/vrrL3nkkUes7dd0/jPBmeBMUQTn4LdixQqpWLEiNzkBwRkIpGPHjsmNN95oBt+GDRua/09wJjhTFME5eOk+1bpPM4u8QXAGzoJ9+/bJ1VdfbQbh+++/v0RvU0dwpiiCczDbtm2bWQSo77N169bmk0OA4AwEWGJiotSqVcsMxo8//niJvU0rwZmiCM7BavPmzdY4XdIvcoDgDJx1ervpqlWrmkG5ZcuWJXJQJjhTFME5GG3cuFFq1Khh3l/jxo3l999/56QFgjNwtq1du9YKzw8//LA4HA6CM0VRBOezaN26dXL++eeb93bPPfcQmkFwBoJJTEyMnHfeeWaQvu+++0rUIE1wpiiCczBZtWqVNR7rleaSvoAbBGcgKK1Zs8a68qxXOI4ePUpwpiiK4BxAy5Ytk8qVK5v31Lx58xL3CSAIzkBI0Tl1NWvWNIP27bffLhkZGQRniqIIzgEwfvx4KVu2rHk/Tz/9dIldsA2CMxBStm/fLldccYUZvGvXrh32dxgkOFMUwfls0u3lnOOQ872w5RwIzkAISUlJkQYNGphBXBeo6MeHBGeKogjORUunxDnv5qpXm/WqM0BwBkKQLkjRLeqcA/q4ceMIzhRFEZyLyO7du6VevXrm9VevXl2WL1/OiQcEZyCUnTp1Snr16mV9hNi9e/ew+wiR4ExRBOdAi4qKsm5s8s9//tPckAogOANhYuLEiVKuXDlrr+dDhw4RnCmKIjgX0p9//mnGmtKlS1t3Azx8+DAnGRCcgXATHR1t3cXqsssuM1dMCM4URRGcC0bXjtx9993m9UZERMgbb7xhgjRAcAbClM7Ju+OOO8zAr1dM9KQV6gM/wZmiCM7Fbe7cuXLBBReY13rJJZeE9YJrgOAM2GhQfvPNN80VEz0JNGrUSPbs2UNwpiiK4JyL3sDkxRdftNaJ6E1N9HUDBGeghPnxxx/NlRM9GeiVFL2iQnCmKIrgfNqKFSukTp065vWVL19ehg4dyokDIDijJDt48KC1B6lW27Ztzb8RnCmKKqnBOTMz01xlLlWqlHltdevWNXdlBUBwBozhw4dLpUqVzEniwgsvlGnTphGcKYoqccH522+/lcsvv9za//6tt96SEydOcJIACM6AO92HtGnTptbVZ70SravICc4URYV7cNbxz/7pW8OGDSU+Pp4TA0BwBvL22WefSbVq1czJ49xzz5WBAwfKH3/8QXCmiq/SUiQhO6RszC4NKwk7kySNdiE4B4DeMrt3795mDrO+jvPOO08++eSTsLtRFEBwBorRvn37pE2bNtbVl2uuuSZoFw+Ge3DeMOd9ad2stTz33HN5VrdeA2XinKWyM83LY8wYJM2aNZM23T+XJL9eR5osGt5DWrd++Awew1tg3ixfvttV6p5TyjrWnFUq4k7p/eF0iU8hXBKci57uLvT5559bC6R1PnOHDh1k//79nAAAgjPgH91544YbbrDCzH333Sfr168nOAewlg581CNU5lWlyjwkMzfucX+MD1qYr5W7e4gk+vU6kuSDu6qc4WO417747+ShUhH5v6eIa2Vs1C8BbfM9G7+Vt3u9bWpurrakQjs4nzp1SmbMmCG1a9e2jrHbbrtN1q1bx4APEJyBorky8+mnn1qb/+uVmdatW0tCQgLBOQC1/MOnrBN8s9bPer3a3Oyu69zCZkS5thKdkm4L3/eafy9Tb4DfwXlYs2pWcD7zK87bZGBOEHeG/fcmTJfIqNWyOmqxjH+3a57vp7jrl6XvW889KHIHATeIg7NOLUtKSipQYF6wYIHceOON1u/2H//4h1kIrV8DQHAGitShQ4fk9ddfl4oVK1p3HtSPNnft2lWgn58wYYJZgENw9i84lyrbSqLT8gh7a79yu4I7KHKX9bW0lCTZuXNndqX4+TqKNjjvWfWp9TrLPzxANnt5X2kJ30unuuWt7+sxc1PggvPyT7y2IxVcwfnjjz826zDymo+clZUlU6ZMcfvk7LLLLpNx48aZrwEgOAPFSuc/d+3a1WzV5AzQTz31lPz88895/tzMmTPl4osvls2bNxOc/QnOZR6SJUl5X3VdNew/VjjoOjHGPYimpZnyCKhJmyVywRzz8fWMyZNlxoLlkpBS2OCcIkk7k8yVv5S0/N/T2vEvWa/z1W+3+/y+HYv6W993zzsLzVxr5/MkJfn+IyAtSf9Q8Hwt+b/X7MdPSZG4+a4rzv0WbJCUlDSv874T1kWbq5hakVHrZKeP+din/3DJfj3Of0tKkKjIxebnFkeuliT760zZJqutr0VJfFIawdkL7e/6c7fccovXr+uiv5EjR8pVV13lFpj1JiZ6N0AABGcgoPRK8/PPPy/lypVzTSVo1kyWLVvm9fu3bt1qvqd69ery008/EZz9CM7L8gnO9iu5p4NmzmPkBOpyd9unaiTJ1x929Tmv+KO5GwoWnNN+ktfvOtf62WrdZ+e7I4Z93naPuVt8f29StLzSrJE0u6uZfLx4qxzYFyPPRpSxFg96/UNiz4/WlXfXay3ge036Ue70Me963Oo9tvnZS+SVuyp5/b4uH33t8YeFc6pMlS6jZPH0QV7npc/duENWTejr9TF7TFxJcLbp0aOH1Tbt27d3+9r27dvllVdekapVq1rfo3f/Gz9+vJw8eZKBGyA4A2eX7vXcvXt385Gp80R13XXXyejRo81VHyf9WNQZsnXLp6ioKIJzEV9xXmQLpP0WbPF4jAr/doXe5TkLBq3f2V13eexuMWjx9ryDc0p2aK5b0fr+Sm2GyrYCvCf7lWR9X2MXrHK/6lrA9+ht+sbW+a7Hbv7ewsK91zyCc7+ctvhl1WdedwGxV5Uu7juP2Oep+1vj1u0p8cFZp2R07NjRrV0+/PBDOXbsmEyaNEnuvvtut681atRIvv76a7aWAwjOQPDROdADBgywtnfSqly5srkqvXLlSvM99erVs76mc6UXLVpEcC7MFWdfC+TSkmTp5H5uYfTbnekej+EKvT/LKxGn/4gp2+hlWRb/izX9YNWcQVYwrPDvodYVW4/gnLJGXqlbwfW7bjeqwAsP07d+4zWgtun8ugyfMF2i1sVLko9pD/vWfmZ7zs9d0x9yXv+XnV2LvyZt3FfI95pmpoFssIXvrpNXSkrSztPBPv0n6RRR1vraE+9Mk/ikFHOFfevySW7vqcfcTT6Dc/cx82VnSpqkxH9nXUF31t3dR8m6hJ2yM2GjjO/V3PZHwA8lOjjrH946JSz3MXPPPfe4/dGuY84LL7yQ79QxAARnICjox6HTp0/3uPqje0HXrVvX7d/0CvSsWbMIzgXcVSOi7p1y11132eo671dH527y+hhW6E1yTWeo33ue51XdXqfvIBlRoaP8lOYenCv8e5Qk7fvJIzQXdsHgjqUj871y2+jhTjJixvJc4XibvFW3kver8HtWyOOlSlvTUrb59V4PyB7b4sCPbYsD475+wzYVxvOx9qydZL0nDfVpXn6H7ce6T7vYanvMSk+Mcn+vtmknJTk465zk5s2b+96CsVQpufPOO82+zPZPuQAQnIGQsmPHDnNXrksvvdTnSU8XF06cOJHgXIDgXKB9nCPu9JgLnVdwNldAP/xS1sXvdJubrAsJdRpOWq4rzlp33VnO7aqv37tspGyTBZNHSOfWD+f5nsrc0FN+sl1tty+C7GP7I2GH7UqxFVAL/V5z76qxw1oAOf65GnnPr87+Hmc7aRBfk5b/dJs9Xp/LFZydfwiU1OCsQbhp06Y+jw2d9rVixQoGW4DgDIQPnWPoDLi+rhgNHz6c4JxXcI64Vrq/+6GZ0+le70q3zm3cg2au/Zo9p2qkyJeda3pdKNem81vmDoTuu024B2d71e89u+huu71xdXaQHitv5Xo/WvW6z7ZN9ZjldmU3Jdc0DQ22cxPSrTBbuPfqKzgnyQfNXAvOytz1kFkM616N3KbLOEOys/0jKr0sP6cV5LkIziojI0P+9a9/5fvH4tVXXy2pqakMtADBGQgPU6dOlYiI/O8SN3DgQIKzn/s471n7lTXHNveVTc/grJUo09/tmufvo8+UlfkG58LfJCQtZzu5nT7nMTuvRn/9YQdXEM1+/6453q7XY/5d36ttmoZrbrY/79V3cM6rDbztlLEsV3C2L84kOOcfnPXr8+fPl/fee0+efvpps0bCvouPva699lrz/QAIzkBI0xNfmTJlvJ7s9G6EOhf6xRdflFGjRkl0dLS5UyHB2Z/t6NJcUwncQqav4OxaWLhx9RKZMPwdedZ2xdRZU8wCO/fQ+MRH82T5dNfWafb5wfmWbeeK87pMy2fruhQZ1vp8r38MbPjS9QlGn8XbZc/SwbbdNmJ9LqLM/70WIDhHXCsTI6MkcnGkLLZqsfmv7hajtXrjzgK1P8G5cNvR6SJB3dpS94V/6623pFWrVub22Trl66abbpLffvuNQRcgOAOhaenSpVK+fHmz0r1hw4Zmhw2929f3339vbqRSFAjOBzz2azbBzrZ9We7glpYQbe7kOGHC15Lg5SYh9t0cToc5+64aAzx22vC1WM5r7Vlh7SShc5fz275uercbvc8P/uV7a+6y7kQx/vUmXtup8O81/6kaee1wsnbBTJk8ebJ8u3orwTkAt9x20gWEGzZskC1btjDwAgRnIDTpzVD0Y/lTp04V23MQnL0vJLSHsNzBzb6P8sjVnvsD/zK/p8/gbJ9ukB43y21njJGrfynU7bvNIr4RC31vW5fwvRUcc19FN3Oan7vQyx7KX7hdxS78e3UPsx9H/eL5h0l2PTlipddt9pztYQ/JBOfiD84ACM4ACM5+B2e3wJcruO1bO8m1q8n1HWXuOldgS0qIlrda18t14w3fdw5c+sFTbrtfbC7Ae7LfpETrhnZDJDpnp4u0tBSzh/GCyYPc9kX2tghxR67H8RY+C/9e3cPsv3Wv5p1Jp39mx3dur6n3hKXW9nFxUV9ZITf3bh8EZ4IzQHAGQHAOsuC8avTzrqkTvWd77CNs31XDOR/adTe9h9y2mXPfscI1TUEfw/1GJ64bjJiA99EPBVog+PXbjxR4oZ3Z2s3b1AjbLbh9z7Uu7Hs9PQ0k9w1anH+IrP3yVY/Xl3s/ar1zYIqPXU0SCc4EZ4DgDIDgXEzBOWd6gLV7RB7fu2fVp153oXA+htuuDmmbZVjne3yG1X/3/tw2/1jDZ4PT28Llmgphwt/ST9yeNzolvUDvbcOCcfJ43XJ57knde7jn3GR7ze/VNP+76xXqvZ5+v7mDvT3Uxi0e6bY3tL3avzfb46qys/31luR5XXH+OI/g/MRHPxKcARCcARCcz2btjF8jixfMkRkzZphFbTPmLJaNO1MC+BrSJH7dMpkzWRfwTc55DQvMbbdTCvDzVnCOuFa+it9XpO81ZedOSUhIkJ07k7zs/pEk66IWy5w5c7JrgURGrZOdKRxPBGeA4AyA4EwFY9muyHrcrpoiOAMgOAMgOJf0Sli9TBZHRbpNv7AvhqQIzgAIzgAIzpQuVLyritu8Ys87BVIEZwAEZwAEZ4KzW3Au/3BPid6ZTrsQnAEQnAEQnKncpXs9JyXtNIv2aA+CMwCCMwCCM0URnAEQnAEQnCmKIjgDBGcABGeKogjOAMEZAMGZoiiCM0BwBkBwpiiK4AyA4AwQnCmKIjgDIDgDBGeCM0URnAEQnAEQnCmK4AyA4AyA4ExRBGcGM4DgDIDgTFEUwRkgOAMgOFMURXAGCM4ACM4URRGcARCcAYIzRVEEZwAEZ4DgTFEUwRkAwRkAwZmiCM4ACM4ACM4URXAGQHAGQHCmKIrgDBCcARCcKYoiOAMEZwAEZ4qiCM4ACM4AwZmiKIIzAIIzEOb69u1rTrx9+vQhiFBUiJX2W+2/b7zxBoMZQHAGUNwGDRpkTrwvv/wyQYSiQqz+7//+z/Tf999/n8EMIDgDKG4jR440J97nn3+eIEJRIVYdOnQw/ffTTz9lMAMIzgCK28yZM82Jt1mzZgQRigqxeuihh0z/nTVrFoMZQHAGUNw2bdpkTrz/+Mc/CCIUFWL197//3fRf7ccACM4Aitnx48fNibdMmTKSlpZGGKGoECntr9pvS5UqJQ6Hg8EMIDgDCIQ6deqY8LxkyRICCUWFSC1evNj0W+2/AAjOAAKkS5cu5gT85ptvEkgoKkRKt6DTfqv9FwDBGUCAzJgxw5yAGzVqRCChqBAp7a/ab3WBLwCCM4AAycjIkLJly0pERIRs3ryZUEJRQV7aT7W/ar/V/guA4AwggB577DFz9apfv34EE4oK8tJ+qv21VatWDF4AwRlAoM2dO9fali49PZ1wQlFBWto/tZ9qf9V+C4DgDCDATp48KVdeeaU5GU+aNImAQlFBWhMnTjT9VPtrVlYWgxdAcAZwNuhte/WEXL9+fQIKRQVp1atXj9tsAwRnAGeb3kThoosuMiflkSNHElIoKshqxIgRpn9qP+WmJwDBGcBZNnnyZHNirlGjhiQmJhJWKCpISvuj9kvtn9pPARCcAQSBe+65x5ycn3zySQILRQVJPfHEE6ZfNm7cmEEKIDgDCBYJCQlSsWJFc5LWj4YJLRQVHFM0tF9u27aNQQogOAMIJs6V+5UqVZJly5YRXijqLJX2P+cfsrrjDQCCM4Ag1L59e3Oyrlmzpvz000+EGIoKcGm/0/6n/bBDhw4MSgDBGUCwOnHihNx///3mpH311VfLpk2bCDMUFaDS/nbVVVeZ/qf9UPdaB0BwBhDEjh49Krfccos5eV9yySWyatUqQg1FFXNpP9P+pv1O+5/2QwAEZwAh4PDhw9KoUSNzEq9WrZrMnj2bcENRxVTav7SfaX/Tfqf9DwDBGUAI0ZsttGjRwpzMS5UqJa+++qrs37+foENRRVTan3r06GH6l/azli1bcpMTgOAMIFT99ddf0r9/f4mIiDAn9gYNGsjSpUsJPRR1hqX9SPuT9ivtXwMGDDD9DQDBGUCIW758uVx++eXW1ed27dqxcJCi/FwAqP3HeZVZ+5X2LwAEZwBh5NixY9KzZ08pU6aMOeGXLVtWnn32WRYPUlQBF/9pf9F+o/1H+9Hrr79u+hUAgjOAMLV161b5z3/+Y03f0Kpfv76899577P1MUbn2ZNZ+of3D2Ve032j/0bt1AiA4AyghduzYIZ07d5aqVataoUDrsssukyeffFLeeecdmTJlirnSFh8fL0lJSZKenk6gosKm9HjW41qPbz3O9XjX416Pf+0H9n6h/aRLly6m3wAgOAMooXQXgJkzZ0rr1q2levXqbmGBokpyaX/QfqH9448//mCwAAjOAOBy6tQp+fnnn2XUqFHy8ssvy4MPPih16tSRiy++WCpXrmwtiKKocCg9nvW41uNbj3M93vW41+Nf+4H2BwAgOAMAAAAEZwAAAIDgDAAAABCcAQAAAIIzAAAAQHAGAAAACM4AAAAAwRkAAAAAwRkAAAAgOAMAAAAEZwAAAIDgDAAAABCcAQAAAIIzAAAAQHAGAAAAQHAGAAAACM4AAAAAwRkAAAAgOAMAAAAEZwAAAIDgDAAAABCcAQAAAIIzAAAAAIIzAAAAQHAGAAAACM4AAAAAwRkAAAAgOAMAAAAEZwAAAIDgDAAAAIDgDAAAAPjl/wNKhOOAfZScygAAAABJRU5ErkJggg==", + "name": "codingthearchitecture.png", + "content": "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", "type": "image/png" } - ] + ], + "decisions": [] }, "views": { "systemContextViews": [ @@ -203,36 +207,34 @@ "description": "An example System Context diagram for the Financial Risk System architecture kata.", "key": "Context", "paperSize": "A4_Landscape", - "enterpriseBoundaryVisible": true, - "relationships": [ - { - "id": "18" - }, - { - "id": "16" - }, - { - "id": "3" - }, - { - "id": "14" - }, - { - "id": "13" - }, - { - "id": "5" - }, - { - "id": "11" - }, - { - "id": "7" - }, - { - "id": "9" + "animations": [ + { + "order": 1, + "elements": [ + "1", + "12", + "2", + "15", + "4", + "6", + "17", + "8", + "10" + ], + "relationships": [ + "11", + "13", + "3", + "14", + "16", + "5", + "18", + "7", + "9" + ] } ], + "enterpriseBoundaryVisible": true, "elements": [ { "id": "1", @@ -279,6 +281,35 @@ "x": 2791, "y": 1637 } + ], + "relationships": [ + { + "id": "18" + }, + { + "id": "16" + }, + { + "id": "3" + }, + { + "id": "14" + }, + { + "id": "13" + }, + { + "id": "5" + }, + { + "id": "11" + }, + { + "id": "7" + }, + { + "id": "9" + } ] } ], @@ -343,7 +374,16 @@ ] }, "terminology": {}, - "lastSavedView": "Context" - } - } + "lastSavedView": "Context", + "themes": [] + }, + "customViews": [], + "systemLandscapeViews": [], + "containerViews": [], + "componentViews": [], + "dynamicViews": [], + "deploymentViews": [], + "filteredViews": [] + }, + "properties": {} } \ No newline at end of file diff --git a/structurizr-client/test/integration/backwardsCompatibility/structurizr-36141-workspace.json b/structurizr-client/test/integration/backwardsCompatibility/structurizr-36141-workspace.json new file mode 100644 index 000000000..92083dfc8 --- /dev/null +++ b/structurizr-client/test/integration/backwardsCompatibility/structurizr-36141-workspace.json @@ -0,0 +1,2010 @@ +{ + "id": 36141, + "name": "Big Bank plc - Internet Banking System", + "description": "The software architecture of the Big Bank plc Internet Banking System.", + "revision": 51, + "lastModifiedDate": "2022-01-31T08:53:06Z", + "lastModifiedUser": "", + "lastModifiedAgent": "structurizr-cli/1.17.0", + "properties": { + "structurizr.dsl": "d29ya3NwYWNlIGV4dGVuZHMgLi4vbW9kZWwuZHNsIHsKICAgIG5hbWUgIkJpZyBCYW5rIHBsYyAtIEludGVybmV0IEJhbmtpbmcgU3lzdGVtIgogICAgZGVzY3JpcHRpb24gIlRoZSBzb2Z0d2FyZSBhcmNoaXRlY3R1cmUgb2YgdGhlIEJpZyBCYW5rIHBsYyBJbnRlcm5ldCBCYW5raW5nIFN5c3RlbS4iCgogICAgbW9kZWwgewogICAgICAgICFyZWYgaW50ZXJuZXRiYW5raW5nc3lzdGVtIHsKICAgICAgICAgICAgc2luZ2xlUGFnZUFwcGxpY2F0aW9uID0gY29udGFpbmVyICJTaW5nbGUtUGFnZSBBcHBsaWNhdGlvbiIgIlByb3ZpZGVzIGFsbCBvZiB0aGUgSW50ZXJuZXQgYmFua2luZyBmdW5jdGlvbmFsaXR5IHRvIGN1c3RvbWVycyB2aWEgdGhlaXIgd2ViIGJyb3dzZXIuIiAiSmF2YVNjcmlwdCBhbmQgQW5ndWxhciIgIldlYiBCcm93c2VyIgogICAgICAgICAgICBtb2JpbGVBcHAgPSBjb250YWluZXIgIk1vYmlsZSBBcHAiICJQcm92aWRlcyBhIGxpbWl0ZWQgc3Vic2V0IG9mIHRoZSBJbnRlcm5ldCBiYW5raW5nIGZ1bmN0aW9uYWxpdHkgdG8gY3VzdG9tZXJzIHZpYSB0aGVpciBtb2JpbGUgZGV2aWNlLiIgIlhhbWFyaW4iICJNb2JpbGUgQXBwIgogICAgICAgICAgICB3ZWJBcHBsaWNhdGlvbiA9IGNvbnRhaW5lciAiV2ViIEFwcGxpY2F0aW9uIiAiRGVsaXZlcnMgdGhlIHN0YXRpYyBjb250ZW50IGFuZCB0aGUgSW50ZXJuZXQgYmFua2luZyBzaW5nbGUgcGFnZSBhcHBsaWNhdGlvbi4iICJKYXZhIGFuZCBTcHJpbmcgTVZDIgogICAgICAgICAgICBhcGlBcHBsaWNhdGlvbiA9IGNvbnRhaW5lciAiQVBJIEFwcGxpY2F0aW9uIiAiUHJvdmlkZXMgSW50ZXJuZXQgYmFua2luZyBmdW5jdGlvbmFsaXR5IHZpYSBhIEpTT04vSFRUUFMgQVBJLiIgIkphdmEgYW5kIFNwcmluZyBNVkMiIHsKICAgICAgICAgICAgICAgIHNpZ25pbkNvbnRyb2xsZXIgPSBjb21wb25lbnQgIlNpZ24gSW4gQ29udHJvbGxlciIgIkFsbG93cyB1c2VycyB0byBzaWduIGluIHRvIHRoZSBJbnRlcm5ldCBCYW5raW5nIFN5c3RlbS4iICJTcHJpbmcgTVZDIFJlc3QgQ29udHJvbGxlciIKICAgICAgICAgICAgICAgIGFjY291bnRzU3VtbWFyeUNvbnRyb2xsZXIgPSBjb21wb25lbnQgIkFjY291bnRzIFN1bW1hcnkgQ29udHJvbGxlciIgIlByb3ZpZGVzIGN1c3RvbWVycyB3aXRoIGEgc3VtbWFyeSBvZiB0aGVpciBiYW5rIGFjY291bnRzLiIgIlNwcmluZyBNVkMgUmVzdCBDb250cm9sbGVyIgogICAgICAgICAgICAgICAgcmVzZXRQYXNzd29yZENvbnRyb2xsZXIgPSBjb21wb25lbnQgIlJlc2V0IFBhc3N3b3JkIENvbnRyb2xsZXIiICJBbGxvd3MgdXNlcnMgdG8gcmVzZXQgdGhlaXIgcGFzc3dvcmRzIHdpdGggYSBzaW5nbGUgdXNlIFVSTC4iICJTcHJpbmcgTVZDIFJlc3QgQ29udHJvbGxlciIKICAgICAgICAgICAgICAgIHNlY3VyaXR5Q29tcG9uZW50ID0gY29tcG9uZW50ICJTZWN1cml0eSBDb21wb25lbnQiICJQcm92aWRlcyBmdW5jdGlvbmFsaXR5IHJlbGF0ZWQgdG8gc2lnbmluZyBpbiwgY2hhbmdpbmcgcGFzc3dvcmRzLCBldGMuIiAiU3ByaW5nIEJlYW4iCiAgICAgICAgICAgICAgICBtYWluZnJhbWVCYW5raW5nU3lzdGVtRmFjYWRlID0gY29tcG9uZW50ICJNYWluZnJhbWUgQmFua2luZyBTeXN0ZW0gRmFjYWRlIiAiQSBmYWNhZGUgb250byB0aGUgbWFpbmZyYW1lIGJhbmtpbmcgc3lzdGVtLiIgIlNwcmluZyBCZWFuIgogICAgICAgICAgICAgICAgZW1haWxDb21wb25lbnQgPSBjb21wb25lbnQgIkUtbWFpbCBDb21wb25lbnQiICJTZW5kcyBlLW1haWxzIHRvIHVzZXJzLiIgIlNwcmluZyBCZWFuIgogICAgICAgICAgICB9CiAgICAgICAgICAgIGRhdGFiYXNlID0gY29udGFpbmVyICJEYXRhYmFzZSIgIlN0b3JlcyB1c2VyIHJlZ2lzdHJhdGlvbiBpbmZvcm1hdGlvbiwgaGFzaGVkIGF1dGhlbnRpY2F0aW9uIGNyZWRlbnRpYWxzLCBhY2Nlc3MgbG9ncywgZXRjLiIgIk9yYWNsZSBEYXRhYmFzZSBTY2hlbWEiICJEYXRhYmFzZSIKCiAgICAgICAgICAgICFkb2NzIGRvY3MKICAgICAgICAgICAgIWFkcnMgYWRycwogICAgICAgIH0KCiAgICAgICAgIyByZWxhdGlvbnNoaXBzIHRvL2Zyb20gY29udGFpbmVycwogICAgICAgIGN1c3RvbWVyIC0+IHdlYkFwcGxpY2F0aW9uICJWaXNpdHMgYmlnYmFuay5jb20vaWIgdXNpbmciICJIVFRQUyIKICAgICAgICBjdXN0b21lciAtPiBzaW5nbGVQYWdlQXBwbGljYXRpb24gIlZpZXdzIGFjY291bnQgYmFsYW5jZXMsIGFuZCBtYWtlcyBwYXltZW50cyB1c2luZyIKICAgICAgICBjdXN0b21lciAtPiBtb2JpbGVBcHAgIlZpZXdzIGFjY291bnQgYmFsYW5jZXMsIGFuZCBtYWtlcyBwYXltZW50cyB1c2luZyIKICAgICAgICB3ZWJBcHBsaWNhdGlvbiAtPiBzaW5nbGVQYWdlQXBwbGljYXRpb24gIkRlbGl2ZXJzIHRvIHRoZSBjdXN0b21lcidzIHdlYiBicm93c2VyIgoKICAgICAgICAjIHJlbGF0aW9uc2hpcHMgdG8vZnJvbSBjb21wb25lbnRzCiAgICAgICAgc2luZ2xlUGFnZUFwcGxpY2F0aW9uIC0+IHNpZ25pbkNvbnRyb2xsZXIgIk1ha2VzIEFQSSBjYWxscyB0byIgIkpTT04vSFRUUFMiCiAgICAgICAgc2luZ2xlUGFnZUFwcGxpY2F0aW9uIC0+IGFjY291bnRzU3VtbWFyeUNvbnRyb2xsZXIgIk1ha2VzIEFQSSBjYWxscyB0byIgIkpTT04vSFRUUFMiCiAgICAgICAgc2luZ2xlUGFnZUFwcGxpY2F0aW9uIC0+IHJlc2V0UGFzc3dvcmRDb250cm9sbGVyICJNYWtlcyBBUEkgY2FsbHMgdG8iICJKU09OL0hUVFBTIgogICAgICAgIG1vYmlsZUFwcCAtPiBzaWduaW5Db250cm9sbGVyICJNYWtlcyBBUEkgY2FsbHMgdG8iICJKU09OL0hUVFBTIgogICAgICAgIG1vYmlsZUFwcCAtPiBhY2NvdW50c1N1bW1hcnlDb250cm9sbGVyICJNYWtlcyBBUEkgY2FsbHMgdG8iICJKU09OL0hUVFBTIgogICAgICAgIG1vYmlsZUFwcCAtPiByZXNldFBhc3N3b3JkQ29udHJvbGxlciAiTWFrZXMgQVBJIGNhbGxzIHRvIiAiSlNPTi9IVFRQUyIKICAgICAgICBzaWduaW5Db250cm9sbGVyIC0+IHNlY3VyaXR5Q29tcG9uZW50ICJVc2VzIgogICAgICAgIGFjY291bnRzU3VtbWFyeUNvbnRyb2xsZXIgLT4gbWFpbmZyYW1lQmFua2luZ1N5c3RlbUZhY2FkZSAiVXNlcyIKICAgICAgICByZXNldFBhc3N3b3JkQ29udHJvbGxlciAtPiBzZWN1cml0eUNvbXBvbmVudCAiVXNlcyIKICAgICAgICByZXNldFBhc3N3b3JkQ29udHJvbGxlciAtPiBlbWFpbENvbXBvbmVudCAiVXNlcyIKICAgICAgICBzZWN1cml0eUNvbXBvbmVudCAtPiBkYXRhYmFzZSAiUmVhZHMgZnJvbSBhbmQgd3JpdGVzIHRvIiAiSkRCQyIKICAgICAgICBtYWluZnJhbWVCYW5raW5nU3lzdGVtRmFjYWRlIC0+IG1haW5mcmFtZSAiTWFrZXMgQVBJIGNhbGxzIHRvIiAiWE1ML0hUVFBTIgogICAgICAgIGVtYWlsQ29tcG9uZW50IC0+IGVtYWlsICJTZW5kcyBlLW1haWwgdXNpbmciCgogICAgICAgIGRlcGxveW1lbnRFbnZpcm9ubWVudCAiRGV2ZWxvcG1lbnQiIHsKICAgICAgICAgICAgZGVwbG95bWVudE5vZGUgIkRldmVsb3BlciBMYXB0b3AiICIiICJNaWNyb3NvZnQgV2luZG93cyAxMCBvciBBcHBsZSBtYWNPUyIgewogICAgICAgICAgICAgICAgZGVwbG95bWVudE5vZGUgIldlYiBCcm93c2VyIiAiIiAiQ2hyb21lLCBGaXJlZm94LCBTYWZhcmksIG9yIEVkZ2UiIHsKICAgICAgICAgICAgICAgICAgICBkZXZlbG9wZXJTaW5nbGVQYWdlQXBwbGljYXRpb25JbnN0YW5jZSA9IGNvbnRhaW5lckluc3RhbmNlIHNpbmdsZVBhZ2VBcHBsaWNhdGlvbgogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgZGVwbG95bWVudE5vZGUgIkRvY2tlciBDb250YWluZXIgLSBXZWIgU2VydmVyIiAiIiAiRG9ja2VyIiB7CiAgICAgICAgICAgICAgICAgICAgZGVwbG95bWVudE5vZGUgIkFwYWNoZSBUb21jYXQiICIiICJBcGFjaGUgVG9tY2F0IDgueCIgewogICAgICAgICAgICAgICAgICAgICAgICBkZXZlbG9wZXJXZWJBcHBsaWNhdGlvbkluc3RhbmNlID0gY29udGFpbmVySW5zdGFuY2Ugd2ViQXBwbGljYXRpb24KICAgICAgICAgICAgICAgICAgICAgICAgZGV2ZWxvcGVyQXBpQXBwbGljYXRpb25JbnN0YW5jZSA9IGNvbnRhaW5lckluc3RhbmNlIGFwaUFwcGxpY2F0aW9uCiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgZGVwbG95bWVudE5vZGUgIkRvY2tlciBDb250YWluZXIgLSBEYXRhYmFzZSBTZXJ2ZXIiICIiICJEb2NrZXIiIHsKICAgICAgICAgICAgICAgICAgICBkZXBsb3ltZW50Tm9kZSAiRGF0YWJhc2UgU2VydmVyIiAiIiAiT3JhY2xlIDEyYyIgewogICAgICAgICAgICAgICAgICAgICAgICBkZXZlbG9wZXJEYXRhYmFzZUluc3RhbmNlID0gY29udGFpbmVySW5zdGFuY2UgZGF0YWJhc2UKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgICAgZGVwbG95bWVudE5vZGUgIkJpZyBCYW5rIHBsYyIgIiIgIkJpZyBCYW5rIHBsYyBkYXRhIGNlbnRlciIgIiIgewogICAgICAgICAgICAgICAgZGVwbG95bWVudE5vZGUgImJpZ2JhbmstZGV2MDAxIiAiIiAiIiAiIiB7CiAgICAgICAgICAgICAgICAgICAgc29mdHdhcmVTeXN0ZW1JbnN0YW5jZSBtYWluZnJhbWUKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgIH0KCiAgICAgICAgZGVwbG95bWVudEVudmlyb25tZW50ICJMaXZlIiB7CiAgICAgICAgICAgIGRlcGxveW1lbnROb2RlICJDdXN0b21lcidzIG1vYmlsZSBkZXZpY2UiICIiICJBcHBsZSBpT1Mgb3IgQW5kcm9pZCIgewogICAgICAgICAgICAgICAgbGl2ZU1vYmlsZUFwcEluc3RhbmNlID0gY29udGFpbmVySW5zdGFuY2UgbW9iaWxlQXBwCiAgICAgICAgICAgIH0KICAgICAgICAgICAgZGVwbG95bWVudE5vZGUgIkN1c3RvbWVyJ3MgY29tcHV0ZXIiICIiICJNaWNyb3NvZnQgV2luZG93cyBvciBBcHBsZSBtYWNPUyIgewogICAgICAgICAgICAgICAgZGVwbG95bWVudE5vZGUgIldlYiBCcm93c2VyIiAiIiAiQ2hyb21lLCBGaXJlZm94LCBTYWZhcmksIG9yIEVkZ2UiIHsKICAgICAgICAgICAgICAgICAgICBsaXZlU2luZ2xlUGFnZUFwcGxpY2F0aW9uSW5zdGFuY2UgPSBjb250YWluZXJJbnN0YW5jZSBzaW5nbGVQYWdlQXBwbGljYXRpb24KICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQoKICAgICAgICAgICAgZGVwbG95bWVudE5vZGUgIkJpZyBCYW5rIHBsYyIgIiIgIkJpZyBCYW5rIHBsYyBkYXRhIGNlbnRlciIgewogICAgICAgICAgICAgICAgZGVwbG95bWVudE5vZGUgImJpZ2Jhbmstd2ViKioqIiAiIiAiVWJ1bnR1IDE2LjA0IExUUyIgIiIgNCB7CiAgICAgICAgICAgICAgICAgICAgZGVwbG95bWVudE5vZGUgIkFwYWNoZSBUb21jYXQiICIiICJBcGFjaGUgVG9tY2F0IDgueCIgewogICAgICAgICAgICAgICAgICAgICAgICBsaXZlV2ViQXBwbGljYXRpb25JbnN0YW5jZSA9IGNvbnRhaW5lckluc3RhbmNlIHdlYkFwcGxpY2F0aW9uCiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgZGVwbG95bWVudE5vZGUgImJpZ2JhbmstYXBpKioqIiAiIiAiVWJ1bnR1IDE2LjA0IExUUyIgIiIgOCB7CiAgICAgICAgICAgICAgICAgICAgZGVwbG95bWVudE5vZGUgIkFwYWNoZSBUb21jYXQiICIiICJBcGFjaGUgVG9tY2F0IDgueCIgewogICAgICAgICAgICAgICAgICAgICAgICBsaXZlQXBpQXBwbGljYXRpb25JbnN0YW5jZSA9IGNvbnRhaW5lckluc3RhbmNlIGFwaUFwcGxpY2F0aW9uCiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIGRlcGxveW1lbnROb2RlICJiaWdiYW5rLWRiMDEiICIiICJVYnVudHUgMTYuMDQgTFRTIiB7CiAgICAgICAgICAgICAgICAgICAgcHJpbWFyeURhdGFiYXNlU2VydmVyID0gZGVwbG95bWVudE5vZGUgIk9yYWNsZSAtIFByaW1hcnkiICIiICJPcmFjbGUgMTJjIiB7CiAgICAgICAgICAgICAgICAgICAgICAgIGxpdmVQcmltYXJ5RGF0YWJhc2VJbnN0YW5jZSA9IGNvbnRhaW5lckluc3RhbmNlIGRhdGFiYXNlCiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgZGVwbG95bWVudE5vZGUgImJpZ2JhbmstZGIwMiIgIiIgIlVidW50dSAxNi4wNCBMVFMiICJGYWlsb3ZlciIgewogICAgICAgICAgICAgICAgICAgIHNlY29uZGFyeURhdGFiYXNlU2VydmVyID0gZGVwbG95bWVudE5vZGUgIk9yYWNsZSAtIFNlY29uZGFyeSIgIiIgIk9yYWNsZSAxMmMiICJGYWlsb3ZlciIgewogICAgICAgICAgICAgICAgICAgICAgICBsaXZlU2Vjb25kYXJ5RGF0YWJhc2VJbnN0YW5jZSA9IGNvbnRhaW5lckluc3RhbmNlIGRhdGFiYXNlICJGYWlsb3ZlciIKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICBkZXBsb3ltZW50Tm9kZSAiYmlnYmFuay1wcm9kMDAxIiAiIiAiIiAiIiB7CiAgICAgICAgICAgICAgICAgICAgc29mdHdhcmVTeXN0ZW1JbnN0YW5jZSBtYWluZnJhbWUKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQoKICAgICAgICAgICAgcHJpbWFyeURhdGFiYXNlU2VydmVyIC0+IHNlY29uZGFyeURhdGFiYXNlU2VydmVyICJSZXBsaWNhdGVzIGRhdGEgdG8iCiAgICAgICAgfQogICAgfQoKICAgIHZpZXdzIHsKICAgICAgICBzeXN0ZW1jb250ZXh0IGludGVybmV0QmFua2luZ1N5c3RlbSAiU3lzdGVtQ29udGV4dCIgewogICAgICAgICAgICBpbmNsdWRlICoKICAgICAgICAgICAgYW5pbWF0aW9uIHsKICAgICAgICAgICAgICAgIGludGVybmV0QmFua2luZ1N5c3RlbQogICAgICAgICAgICAgICAgY3VzdG9tZXIKICAgICAgICAgICAgICAgIG1haW5mcmFtZQogICAgICAgICAgICAgICAgZW1haWwKICAgICAgICAgICAgfQogICAgICAgIH0KCiAgICAgICAgY29udGFpbmVyIGludGVybmV0QmFua2luZ1N5c3RlbSAiQ29udGFpbmVycyIgewogICAgICAgICAgICBpbmNsdWRlICoKICAgICAgICAgICAgYW5pbWF0aW9uIHsKICAgICAgICAgICAgICAgIGN1c3RvbWVyIG1haW5mcmFtZSBlbWFpbAogICAgICAgICAgICAgICAgd2ViQXBwbGljYXRpb24KICAgICAgICAgICAgICAgIHNpbmdsZVBhZ2VBcHBsaWNhdGlvbgogICAgICAgICAgICAgICAgbW9iaWxlQXBwCiAgICAgICAgICAgICAgICBhcGlBcHBsaWNhdGlvbgogICAgICAgICAgICAgICAgZGF0YWJhc2UKICAgICAgICAgICAgfQogICAgICAgIH0KCiAgICAgICAgY29tcG9uZW50IGFwaUFwcGxpY2F0aW9uICJDb21wb25lbnRzIiB7CiAgICAgICAgICAgIGluY2x1ZGUgKgogICAgICAgICAgICBhbmltYXRpb24gewogICAgICAgICAgICAgICAgc2luZ2xlUGFnZUFwcGxpY2F0aW9uIG1vYmlsZUFwcCBkYXRhYmFzZSBlbWFpbCBtYWluZnJhbWUKICAgICAgICAgICAgICAgIHNpZ25pbkNvbnRyb2xsZXIgc2VjdXJpdHlDb21wb25lbnQKICAgICAgICAgICAgICAgIGFjY291bnRzU3VtbWFyeUNvbnRyb2xsZXIgbWFpbmZyYW1lQmFua2luZ1N5c3RlbUZhY2FkZQogICAgICAgICAgICAgICAgcmVzZXRQYXNzd29yZENvbnRyb2xsZXIgZW1haWxDb21wb25lbnQKICAgICAgICAgICAgfQogICAgICAgIH0KCiAgICAgICAgZHluYW1pYyBhcGlBcHBsaWNhdGlvbiAiU2lnbkluIiAiU3VtbWFyaXNlcyBob3cgdGhlIHNpZ24gaW4gZmVhdHVyZSB3b3JrcyBpbiB0aGUgc2luZ2xlLXBhZ2UgYXBwbGljYXRpb24uIiB7CiAgICAgICAgICAgIHNpbmdsZVBhZ2VBcHBsaWNhdGlvbiAtPiBzaWduaW5Db250cm9sbGVyICJTdWJtaXRzIGNyZWRlbnRpYWxzIHRvIgogICAgICAgICAgICBzaWduaW5Db250cm9sbGVyIC0+IHNlY3VyaXR5Q29tcG9uZW50ICJWYWxpZGF0ZXMgY3JlZGVudGlhbHMgdXNpbmciCiAgICAgICAgICAgIHNlY3VyaXR5Q29tcG9uZW50IC0+IGRhdGFiYXNlICJzZWxlY3QgKiBmcm9tIHVzZXJzIHdoZXJlIHVzZXJuYW1lID0gPyIKICAgICAgICAgICAgZGF0YWJhc2UgLT4gc2VjdXJpdHlDb21wb25lbnQgIlJldHVybnMgdXNlciBkYXRhIHRvIgogICAgICAgICAgICBzZWN1cml0eUNvbXBvbmVudCAtPiBzaWduaW5Db250cm9sbGVyICJSZXR1cm5zIHRydWUgaWYgdGhlIGhhc2hlZCBwYXNzd29yZCBtYXRjaGVzIgogICAgICAgICAgICBzaWduaW5Db250cm9sbGVyIC0+IHNpbmdsZVBhZ2VBcHBsaWNhdGlvbiAiU2VuZHMgYmFjayBhbiBhdXRoZW50aWNhdGlvbiB0b2tlbiB0byIKICAgICAgICB9CgogICAgICAgIGRlcGxveW1lbnQgaW50ZXJuZXRCYW5raW5nU3lzdGVtICJEZXZlbG9wbWVudCIgIkRldmVsb3BtZW50RGVwbG95bWVudCIgewogICAgICAgICAgICBpbmNsdWRlICoKICAgICAgICAgICAgYW5pbWF0aW9uIHsKICAgICAgICAgICAgICAgIGRldmVsb3BlclNpbmdsZVBhZ2VBcHBsaWNhdGlvbkluc3RhbmNlCiAgICAgICAgICAgICAgICBkZXZlbG9wZXJXZWJBcHBsaWNhdGlvbkluc3RhbmNlIGRldmVsb3BlckFwaUFwcGxpY2F0aW9uSW5zdGFuY2UKICAgICAgICAgICAgICAgIGRldmVsb3BlckRhdGFiYXNlSW5zdGFuY2UKICAgICAgICAgICAgfQogICAgICAgIH0KCiAgICAgICAgZGVwbG95bWVudCBpbnRlcm5ldEJhbmtpbmdTeXN0ZW0gIkxpdmUiICJMaXZlRGVwbG95bWVudCIgewogICAgICAgICAgICBpbmNsdWRlICoKICAgICAgICAgICAgYW5pbWF0aW9uIHsKICAgICAgICAgICAgICAgIGxpdmVTaW5nbGVQYWdlQXBwbGljYXRpb25JbnN0YW5jZQogICAgICAgICAgICAgICAgbGl2ZU1vYmlsZUFwcEluc3RhbmNlCiAgICAgICAgICAgICAgICBsaXZlV2ViQXBwbGljYXRpb25JbnN0YW5jZSBsaXZlQXBpQXBwbGljYXRpb25JbnN0YW5jZQogICAgICAgICAgICAgICAgbGl2ZVByaW1hcnlEYXRhYmFzZUluc3RhbmNlCiAgICAgICAgICAgICAgICBsaXZlU2Vjb25kYXJ5RGF0YWJhc2VJbnN0YW5jZQogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICBzdHlsZXMgewogICAgICAgICAgICBlbGVtZW50ICJQZXJzb24iIHsKICAgICAgICAgICAgICAgIGNvbG9yICNmZmZmZmYKICAgICAgICAgICAgICAgIGZvbnRTaXplIDIyCiAgICAgICAgICAgICAgICBzaGFwZSBQZXJzb24KICAgICAgICAgICAgfQogICAgICAgICAgICBlbGVtZW50ICJDdXN0b21lciIgewogICAgICAgICAgICAgICAgYmFja2dyb3VuZCAjMDg0MjdiCiAgICAgICAgICAgIH0KICAgICAgICAgICAgZWxlbWVudCAiQmFuayBTdGFmZiIgewogICAgICAgICAgICAgICAgYmFja2dyb3VuZCAjOTk5OTk5CiAgICAgICAgICAgIH0KICAgICAgICAgICAgZWxlbWVudCAiU29mdHdhcmUgU3lzdGVtIiB7CiAgICAgICAgICAgICAgICBiYWNrZ3JvdW5kICMxMTY4YmQKICAgICAgICAgICAgICAgIGNvbG9yICNmZmZmZmYKICAgICAgICAgICAgfQogICAgICAgICAgICBlbGVtZW50ICJFeGlzdGluZyBTeXN0ZW0iIHsKICAgICAgICAgICAgICAgIGJhY2tncm91bmQgIzk5OTk5OQogICAgICAgICAgICAgICAgY29sb3IgI2ZmZmZmZgogICAgICAgICAgICB9CiAgICAgICAgICAgIGVsZW1lbnQgIkNvbnRhaW5lciIgewogICAgICAgICAgICAgICAgYmFja2dyb3VuZCAjNDM4ZGQ1CiAgICAgICAgICAgICAgICBjb2xvciAjZmZmZmZmCiAgICAgICAgICAgIH0KICAgICAgICAgICAgZWxlbWVudCAiV2ViIEJyb3dzZXIiIHsKICAgICAgICAgICAgICAgIHNoYXBlIFdlYkJyb3dzZXIKICAgICAgICAgICAgfQogICAgICAgICAgICBlbGVtZW50ICJNb2JpbGUgQXBwIiB7CiAgICAgICAgICAgICAgICBzaGFwZSBNb2JpbGVEZXZpY2VMYW5kc2NhcGUKICAgICAgICAgICAgfQogICAgICAgICAgICBlbGVtZW50ICJEYXRhYmFzZSIgewogICAgICAgICAgICAgICAgc2hhcGUgQ3lsaW5kZXIKICAgICAgICAgICAgfQogICAgICAgICAgICBlbGVtZW50ICJDb21wb25lbnQiIHsKICAgICAgICAgICAgICAgIGJhY2tncm91bmQgIzg1YmJmMAogICAgICAgICAgICAgICAgY29sb3IgIzAwMDAwMAogICAgICAgICAgICB9CiAgICAgICAgICAgIGVsZW1lbnQgIkZhaWxvdmVyIiB7CiAgICAgICAgICAgICAgICBvcGFjaXR5IDI1CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICB9Cgp9Cg==" + }, + "model": { + "enterprise": { + "name": "Big Bank plc" + }, + "people": [ + { + "id": "3", + "tags": "Element,Person,Bank Staff", + "name": "Back Office Staff", + "description": "Administration and support staff within the bank.", + "relationships": [ + { + "id": "16", + "tags": "Relationship", + "sourceId": "3", + "destinationId": "4", + "description": "Uses", + "defaultTags": [ + "Relationship" + ] + } + ], + "location": "Internal", + "defaultTags": [ + "Element", + "Person" + ] + }, + { + "id": "2", + "tags": "Element,Person,Bank Staff", + "name": "Customer Service Staff", + "description": "Customer service staff within the bank.", + "relationships": [ + { + "id": "13", + "tags": "Relationship", + "sourceId": "2", + "destinationId": "4", + "description": "Uses", + "defaultTags": [ + "Relationship" + ] + } + ], + "location": "Internal", + "defaultTags": [ + "Element", + "Person" + ] + }, + { + "id": "1", + "tags": "Element,Person,Customer", + "name": "Personal Banking Customer", + "description": "A customer of the bank, with personal bank accounts.", + "relationships": [ + { + "id": "30", + "tags": "Relationship", + "sourceId": "1", + "destinationId": "18", + "description": "Views account balances, and makes payments using", + "defaultTags": [ + "Relationship" + ] + }, + { + "id": "14", + "tags": "Relationship", + "sourceId": "1", + "destinationId": "6", + "description": "Withdraws cash using", + "defaultTags": [ + "Relationship" + ] + }, + { + "id": "12", + "tags": "Relationship", + "sourceId": "1", + "destinationId": "2", + "description": "Asks questions to", + "technology": "Telephone", + "defaultTags": [ + "Relationship" + ] + }, + { + "id": "8", + "tags": "Relationship", + "sourceId": "1", + "destinationId": "7", + "description": "Views account balances, and makes payments using", + "defaultTags": [ + "Relationship" + ] + }, + { + "id": "28", + "tags": "Relationship", + "sourceId": "1", + "destinationId": "19", + "description": "Visits bigbank.com/ib using", + "technology": "HTTPS", + "defaultTags": [ + "Relationship" + ] + }, + { + "id": "29", + "tags": "Relationship", + "sourceId": "1", + "destinationId": "17", + "description": "Views account balances, and makes payments using", + "defaultTags": [ + "Relationship" + ] + } + ], + "location": "External", + "defaultTags": [ + "Element", + "Person" + ] + } + ], + "softwareSystems": [ + { + "id": "6", + "tags": "Element,Software System,Existing System", + "name": "ATM", + "description": "Allows customers to withdraw cash.", + "relationships": [ + { + "id": "15", + "tags": "Relationship", + "sourceId": "6", + "destinationId": "4", + "description": "Uses", + "defaultTags": [ + "Relationship" + ] + } + ], + "location": "Internal", + "defaultTags": [ + "Element", + "Software System" + ] + }, + { + "id": "5", + "tags": "Element,Software System,Existing System", + "name": "E-mail System", + "description": "The internal Microsoft Exchange e-mail system.", + "relationships": [ + { + "id": "11", + "tags": "Relationship", + "sourceId": "5", + "destinationId": "1", + "description": "Sends e-mails to", + "defaultTags": [ + "Relationship" + ] + } + ], + "location": "Internal", + "defaultTags": [ + "Element", + "Software System" + ] + }, + { + "id": "7", + "tags": "Element,Software System", + "name": "Internet Banking System", + "description": "Allows customers to view information about their bank accounts, and make payments.", + "relationships": [ + { + "id": "10", + "tags": "Relationship", + "sourceId": "7", + "destinationId": "5", + "description": "Sends e-mail using", + "defaultTags": [ + "Relationship" + ] + }, + { + "id": "9", + "tags": "Relationship", + "sourceId": "7", + "destinationId": "4", + "description": "Gets account information from, and makes payments using", + "defaultTags": [ + "Relationship" + ] + } + ], + "location": "Internal", + "containers": [ + { + "id": "20", + "tags": "Element,Container", + "name": "API Application", + "description": "Provides Internet banking functionality via a JSON/HTTPS API.", + "relationships": [ + { + "id": "45", + "tags": "Relationship", + "sourceId": "20", + "destinationId": "27", + "description": "Reads from and writes to", + "technology": "JDBC", + "defaultTags": [ + "Relationship" + ] + }, + { + "id": "49", + "tags": "Relationship", + "sourceId": "20", + "destinationId": "5", + "description": "Sends e-mail using", + "defaultTags": [ + "Relationship" + ] + }, + { + "id": "47", + "tags": "Relationship", + "sourceId": "20", + "destinationId": "4", + "description": "Makes API calls to", + "technology": "XML/HTTPS", + "defaultTags": [ + "Relationship" + ] + } + ], + "technology": "Java and Spring MVC", + "components": [ + { + "id": "22", + "tags": "Element,Component", + "name": "Accounts Summary Controller", + "description": "Provides customers with a summary of their bank accounts.", + "relationships": [ + { + "id": "41", + "tags": "Relationship", + "sourceId": "22", + "destinationId": "25", + "description": "Uses", + "defaultTags": [ + "Relationship" + ] + } + ], + "technology": "Spring MVC Rest Controller", + "size": 0, + "defaultTags": [ + "Element", + "Component" + ] + }, + { + "id": "26", + "tags": "Element,Component", + "name": "E-mail Component", + "description": "Sends e-mails to users.", + "relationships": [ + { + "id": "48", + "tags": "Relationship", + "sourceId": "26", + "destinationId": "5", + "description": "Sends e-mail using", + "defaultTags": [ + "Relationship" + ] + } + ], + "technology": "Spring Bean", + "size": 0, + "defaultTags": [ + "Element", + "Component" + ] + }, + { + "id": "25", + "tags": "Element,Component", + "name": "Mainframe Banking System Facade", + "description": "A facade onto the mainframe banking system.", + "relationships": [ + { + "id": "46", + "tags": "Relationship", + "sourceId": "25", + "destinationId": "4", + "description": "Makes API calls to", + "technology": "XML/HTTPS", + "defaultTags": [ + "Relationship" + ] + } + ], + "technology": "Spring Bean", + "size": 0, + "defaultTags": [ + "Element", + "Component" + ] + }, + { + "id": "23", + "tags": "Element,Component", + "name": "Reset Password Controller", + "description": "Allows users to reset their passwords with a single use URL.", + "relationships": [ + { + "id": "42", + "tags": "Relationship", + "sourceId": "23", + "destinationId": "24", + "description": "Uses", + "defaultTags": [ + "Relationship" + ] + }, + { + "id": "43", + "tags": "Relationship", + "sourceId": "23", + "destinationId": "26", + "description": "Uses", + "defaultTags": [ + "Relationship" + ] + } + ], + "technology": "Spring MVC Rest Controller", + "size": 0, + "defaultTags": [ + "Element", + "Component" + ] + }, + { + "id": "24", + "tags": "Element,Component", + "name": "Security Component", + "description": "Provides functionality related to signing in, changing passwords, etc.", + "relationships": [ + { + "id": "44", + "tags": "Relationship", + "sourceId": "24", + "destinationId": "27", + "description": "Reads from and writes to", + "technology": "JDBC", + "defaultTags": [ + "Relationship" + ] + } + ], + "technology": "Spring Bean", + "size": 0, + "defaultTags": [ + "Element", + "Component" + ] + }, + { + "id": "21", + "tags": "Element,Component", + "name": "Sign In Controller", + "description": "Allows users to sign in to the Internet Banking System.", + "relationships": [ + { + "id": "40", + "tags": "Relationship", + "sourceId": "21", + "destinationId": "24", + "description": "Uses", + "defaultTags": [ + "Relationship" + ] + } + ], + "technology": "Spring MVC Rest Controller", + "size": 0, + "defaultTags": [ + "Element", + "Component" + ] + } + ], + "defaultTags": [ + "Element", + "Container" + ] + }, + { + "id": "27", + "tags": "Element,Container,Database", + "name": "Database", + "description": "Stores user registration information, hashed authentication credentials, access logs, etc.", + "technology": "Oracle Database Schema", + "defaultTags": [ + "Element", + "Container" + ] + }, + { + "id": "18", + "tags": "Element,Container,Mobile App", + "name": "Mobile App", + "description": "Provides a limited subset of the Internet banking functionality to customers via their mobile device.", + "relationships": [ + { + "id": "37", + "tags": "Relationship", + "sourceId": "18", + "destinationId": "20", + "description": "Makes API calls to", + "technology": "JSON/HTTPS", + "defaultTags": [ + "Relationship" + ] + }, + { + "id": "38", + "tags": "Relationship", + "sourceId": "18", + "destinationId": "22", + "description": "Makes API calls to", + "technology": "JSON/HTTPS", + "defaultTags": [ + "Relationship" + ] + }, + { + "id": "39", + "tags": "Relationship", + "sourceId": "18", + "destinationId": "23", + "description": "Makes API calls to", + "technology": "JSON/HTTPS", + "defaultTags": [ + "Relationship" + ] + }, + { + "id": "36", + "tags": "Relationship", + "sourceId": "18", + "destinationId": "21", + "description": "Makes API calls to", + "technology": "JSON/HTTPS", + "defaultTags": [ + "Relationship" + ] + } + ], + "technology": "Xamarin", + "defaultTags": [ + "Element", + "Container" + ] + }, + { + "id": "17", + "tags": "Element,Container,Web Browser", + "name": "Single-Page Application", + "description": "Provides all of the Internet banking functionality to customers via their web browser.", + "relationships": [ + { + "id": "33", + "tags": "Relationship", + "sourceId": "17", + "destinationId": "20", + "description": "Makes API calls to", + "technology": "JSON/HTTPS", + "defaultTags": [ + "Relationship" + ] + }, + { + "id": "34", + "tags": "Relationship", + "sourceId": "17", + "destinationId": "22", + "description": "Makes API calls to", + "technology": "JSON/HTTPS", + "defaultTags": [ + "Relationship" + ] + }, + { + "id": "35", + "tags": "Relationship", + "sourceId": "17", + "destinationId": "23", + "description": "Makes API calls to", + "technology": "JSON/HTTPS", + "defaultTags": [ + "Relationship" + ] + }, + { + "id": "32", + "tags": "Relationship", + "sourceId": "17", + "destinationId": "21", + "description": "Makes API calls to", + "technology": "JSON/HTTPS", + "defaultTags": [ + "Relationship" + ] + } + ], + "technology": "JavaScript and Angular", + "defaultTags": [ + "Element", + "Container" + ] + }, + { + "id": "19", + "tags": "Element,Container", + "name": "Web Application", + "description": "Delivers the static content and the Internet banking single page application.", + "relationships": [ + { + "id": "31", + "tags": "Relationship", + "sourceId": "19", + "destinationId": "17", + "description": "Delivers to the customer's web browser", + "defaultTags": [ + "Relationship" + ] + } + ], + "technology": "Java and Spring MVC", + "defaultTags": [ + "Element", + "Container" + ] + } + ], + "defaultTags": [ + "Element", + "Software System" + ] + }, + { + "id": "4", + "tags": "Element,Software System,Existing System", + "name": "Mainframe Banking System", + "description": "Stores all of the core banking information about customers, accounts, transactions, etc.", + "location": "Internal", + "defaultTags": [ + "Element", + "Software System" + ] + } + ], + "deploymentNodes": [ + { + "id": "63", + "tags": "Element,Deployment Node", + "name": "Big Bank plc", + "environment": "Development", + "technology": "Big Bank plc data center", + "instances": 1, + "children": [ + { + "id": "64", + "tags": "Element,Deployment Node", + "name": "bigbank-dev001", + "environment": "Development", + "instances": 1, + "softwareSystemInstances": [ + { + "id": "65", + "tags": "Software System Instance", + "environment": "Development", + "deploymentGroups": [ + "Default" + ], + "instanceId": 1, + "softwareSystemId": "4" + } + ], + "children": [], + "containerInstances": [], + "infrastructureNodes": [] + } + ], + "softwareSystemInstances": [], + "containerInstances": [], + "infrastructureNodes": [] + }, + { + "id": "50", + "tags": "Element,Deployment Node", + "name": "Developer Laptop", + "environment": "Development", + "technology": "Microsoft Windows 10 or Apple macOS", + "instances": 1, + "children": [ + { + "id": "59", + "tags": "Element,Deployment Node", + "name": "Docker Container - Database Server", + "environment": "Development", + "technology": "Docker", + "instances": 1, + "children": [ + { + "id": "60", + "tags": "Element,Deployment Node", + "name": "Database Server", + "environment": "Development", + "technology": "Oracle 12c", + "instances": 1, + "containerInstances": [ + { + "id": "61", + "tags": "Container Instance", + "environment": "Development", + "deploymentGroups": [ + "Default" + ], + "instanceId": 1, + "containerId": "27" + } + ], + "children": [], + "softwareSystemInstances": [], + "infrastructureNodes": [] + } + ], + "softwareSystemInstances": [], + "containerInstances": [], + "infrastructureNodes": [] + }, + { + "id": "53", + "tags": "Element,Deployment Node", + "name": "Docker Container - Web Server", + "environment": "Development", + "technology": "Docker", + "instances": 1, + "children": [ + { + "id": "54", + "tags": "Element,Deployment Node", + "name": "Apache Tomcat", + "environment": "Development", + "technology": "Apache Tomcat 8.x", + "instances": 1, + "containerInstances": [ + { + "id": "57", + "tags": "Container Instance", + "relationships": [ + { + "id": "66", + "sourceId": "57", + "destinationId": "65", + "description": "Makes API calls to", + "technology": "XML/HTTPS", + "linkedRelationshipId": "47" + }, + { + "id": "62", + "sourceId": "57", + "destinationId": "61", + "description": "Reads from and writes to", + "technology": "JDBC", + "linkedRelationshipId": "45" + } + ], + "environment": "Development", + "deploymentGroups": [ + "Default" + ], + "instanceId": 1, + "containerId": "20" + }, + { + "id": "55", + "tags": "Container Instance", + "relationships": [ + { + "id": "56", + "sourceId": "55", + "destinationId": "52", + "description": "Delivers to the customer's web browser", + "linkedRelationshipId": "31" + } + ], + "environment": "Development", + "deploymentGroups": [ + "Default" + ], + "instanceId": 1, + "containerId": "19" + } + ], + "children": [], + "softwareSystemInstances": [], + "infrastructureNodes": [] + } + ], + "softwareSystemInstances": [], + "containerInstances": [], + "infrastructureNodes": [] + }, + { + "id": "51", + "tags": "Element,Deployment Node", + "name": "Web Browser", + "environment": "Development", + "technology": "Chrome, Firefox, Safari, or Edge", + "instances": 1, + "containerInstances": [ + { + "id": "52", + "tags": "Container Instance", + "relationships": [ + { + "id": "58", + "sourceId": "52", + "destinationId": "57", + "description": "Makes API calls to", + "technology": "JSON/HTTPS", + "linkedRelationshipId": "33" + } + ], + "environment": "Development", + "deploymentGroups": [ + "Default" + ], + "instanceId": 1, + "containerId": "17" + } + ], + "children": [], + "softwareSystemInstances": [], + "infrastructureNodes": [] + } + ], + "softwareSystemInstances": [], + "containerInstances": [], + "infrastructureNodes": [] + }, + { + "id": "72", + "tags": "Element,Deployment Node", + "name": "Big Bank plc", + "environment": "Live", + "technology": "Big Bank plc data center", + "instances": 1, + "children": [ + { + "id": "77", + "tags": "Element,Deployment Node", + "name": "bigbank-api***", + "environment": "Live", + "technology": "Ubuntu 16.04 LTS", + "instances": 8, + "children": [ + { + "id": "78", + "tags": "Element,Deployment Node", + "name": "Apache Tomcat", + "environment": "Live", + "technology": "Apache Tomcat 8.x", + "instances": 1, + "containerInstances": [ + { + "id": "79", + "tags": "Container Instance", + "relationships": [ + { + "id": "89", + "sourceId": "79", + "destinationId": "88", + "description": "Reads from and writes to", + "technology": "JDBC", + "linkedRelationshipId": "45" + }, + { + "id": "85", + "sourceId": "79", + "destinationId": "84", + "description": "Reads from and writes to", + "technology": "JDBC", + "linkedRelationshipId": "45" + }, + { + "id": "92", + "sourceId": "79", + "destinationId": "91", + "description": "Makes API calls to", + "technology": "XML/HTTPS", + "linkedRelationshipId": "47" + } + ], + "environment": "Live", + "deploymentGroups": [ + "Default" + ], + "instanceId": 1, + "containerId": "20" + } + ], + "children": [], + "softwareSystemInstances": [], + "infrastructureNodes": [] + } + ], + "softwareSystemInstances": [], + "containerInstances": [], + "infrastructureNodes": [] + }, + { + "id": "82", + "tags": "Element,Deployment Node", + "name": "bigbank-db01", + "environment": "Live", + "technology": "Ubuntu 16.04 LTS", + "instances": 1, + "children": [ + { + "id": "83", + "tags": "Element,Deployment Node", + "name": "Oracle - Primary", + "relationships": [ + { + "id": "93", + "tags": "Relationship", + "sourceId": "83", + "destinationId": "87", + "description": "Replicates data to", + "defaultTags": [ + "Relationship" + ] + } + ], + "environment": "Live", + "technology": "Oracle 12c", + "instances": 1, + "containerInstances": [ + { + "id": "84", + "tags": "Container Instance", + "environment": "Live", + "deploymentGroups": [ + "Default" + ], + "instanceId": 1, + "containerId": "27" + } + ], + "children": [], + "softwareSystemInstances": [], + "infrastructureNodes": [] + } + ], + "softwareSystemInstances": [], + "containerInstances": [], + "infrastructureNodes": [] + }, + { + "id": "86", + "tags": "Element,Deployment Node,Failover", + "name": "bigbank-db02", + "environment": "Live", + "technology": "Ubuntu 16.04 LTS", + "instances": 1, + "children": [ + { + "id": "87", + "tags": "Element,Deployment Node,Failover", + "name": "Oracle - Secondary", + "environment": "Live", + "technology": "Oracle 12c", + "instances": 1, + "containerInstances": [ + { + "id": "88", + "tags": "Container Instance", + "environment": "Live", + "deploymentGroups": [ + "Default" + ], + "instanceId": 1, + "containerId": "27" + } + ], + "children": [], + "softwareSystemInstances": [], + "infrastructureNodes": [] + } + ], + "softwareSystemInstances": [], + "containerInstances": [], + "infrastructureNodes": [] + }, + { + "id": "90", + "tags": "Element,Deployment Node", + "name": "bigbank-prod001", + "environment": "Live", + "instances": 1, + "softwareSystemInstances": [ + { + "id": "91", + "tags": "Software System Instance", + "environment": "Live", + "deploymentGroups": [ + "Default" + ], + "instanceId": 1, + "softwareSystemId": "4" + } + ], + "children": [], + "containerInstances": [], + "infrastructureNodes": [] + }, + { + "id": "73", + "tags": "Element,Deployment Node", + "name": "bigbank-web***", + "environment": "Live", + "technology": "Ubuntu 16.04 LTS", + "instances": 4, + "children": [ + { + "id": "74", + "tags": "Element,Deployment Node", + "name": "Apache Tomcat", + "environment": "Live", + "technology": "Apache Tomcat 8.x", + "instances": 1, + "containerInstances": [ + { + "id": "75", + "tags": "Container Instance", + "relationships": [ + { + "id": "76", + "sourceId": "75", + "destinationId": "71", + "description": "Delivers to the customer's web browser", + "linkedRelationshipId": "31" + } + ], + "environment": "Live", + "deploymentGroups": [ + "Default" + ], + "instanceId": 1, + "containerId": "19" + } + ], + "children": [], + "softwareSystemInstances": [], + "infrastructureNodes": [] + } + ], + "softwareSystemInstances": [], + "containerInstances": [], + "infrastructureNodes": [] + } + ], + "softwareSystemInstances": [], + "containerInstances": [], + "infrastructureNodes": [] + }, + { + "id": "69", + "tags": "Element,Deployment Node", + "name": "Customer's computer", + "environment": "Live", + "technology": "Microsoft Windows or Apple macOS", + "instances": 1, + "children": [ + { + "id": "70", + "tags": "Element,Deployment Node", + "name": "Web Browser", + "environment": "Live", + "technology": "Chrome, Firefox, Safari, or Edge", + "instances": 1, + "containerInstances": [ + { + "id": "71", + "tags": "Container Instance", + "relationships": [ + { + "id": "80", + "sourceId": "71", + "destinationId": "79", + "description": "Makes API calls to", + "technology": "JSON/HTTPS", + "linkedRelationshipId": "33" + } + ], + "environment": "Live", + "deploymentGroups": [ + "Default" + ], + "instanceId": 1, + "containerId": "17" + } + ], + "children": [], + "softwareSystemInstances": [], + "infrastructureNodes": [] + } + ], + "softwareSystemInstances": [], + "containerInstances": [], + "infrastructureNodes": [] + }, + { + "id": "67", + "tags": "Element,Deployment Node", + "name": "Customer's mobile device", + "environment": "Live", + "technology": "Apple iOS or Android", + "instances": 1, + "containerInstances": [ + { + "id": "68", + "tags": "Container Instance", + "relationships": [ + { + "id": "81", + "sourceId": "68", + "destinationId": "79", + "description": "Makes API calls to", + "technology": "JSON/HTTPS", + "linkedRelationshipId": "37" + } + ], + "environment": "Live", + "deploymentGroups": [ + "Default" + ], + "instanceId": 1, + "containerId": "18" + } + ], + "children": [], + "softwareSystemInstances": [], + "infrastructureNodes": [] + } + ], + "customElements": [] + }, + "documentation": { + "sections": [ + { + "elementId": "7", + "title": "Development Environment", + "order": 3, + "format": "AsciiDoc", + "content": "== Development Environment\n\nHere is some information about how to set up a development environment for the Internet Banking System...\n\nimage::embed:DevelopmentDeployment[]" + }, + { + "elementId": "7", + "title": "Deployment", + "order": 4, + "format": "AsciiDoc", + "content": "== Deployment\n\nHere is some information about the live deployment environment for the Internet Banking System...\n\nimage::embed:LiveDeployment[]" + }, + { + "elementId": "7", + "title": "Context", + "order": 1, + "format": "Markdown", + "content": "## Context\n\nHere is some context about the Internet Banking System...\n\n![](embed:SystemContext)\n\n### Internet Banking System\n...\n\n### Mainframe Banking System\n...\n" + }, + { + "elementId": "7", + "title": "Software Architecture", + "order": 2, + "format": "Markdown", + "content": "## Software Architecture\n\nHere is some information about the software architecture of the Internet Banking System...\n\n![](embed:Containers)\n\n### Web Application\n...\n\n### API Application\n...\n\nHere is some information about the API Application...\n\n![](embed:Components)\n\n### Sign in process\n\nHere is some information about the Sign In Controller, including how the sign in process works...\n\n![](embed:SignIn)" + } + ], + "decisions": [ + { + "elementId": "7", + "id": "1", + "date": "2020-06-05T00:00:00Z", + "title": "Record architecture decisions", + "status": "Accepted", + "content": "# 1. Record architecture decisions\n\nDate: 2020-06-05\n\n## Status\n\nAccepted\n\n## Context\n\nWe need to record the architectural decisions made on this project.\n\n## Decision\n\nWe will use Architecture Decision Records, as described by Michael Nygard in this article: [http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions)\n\n## Consequences\n\nSee Michael Nygard's article, linked above.", + "format": "Markdown" + } + ], + "images": [] + }, + "views": { + "systemContextViews": [ + { + "softwareSystemId": "7", + "key": "SystemContext", + "paperSize": "A5_Landscape", + "animations": [ + { + "order": 1, + "elements": [ + "7" + ], + "relationships": [] + }, + { + "order": 2, + "elements": [ + "1" + ], + "relationships": [ + "8" + ] + }, + { + "order": 3, + "elements": [ + "4" + ], + "relationships": [ + "9" + ] + }, + { + "order": 4, + "elements": [ + "5" + ], + "relationships": [ + "11", + "10" + ] + } + ], + "enterpriseBoundaryVisible": true, + "elements": [ + { + "id": "1", + "x": 632, + "y": 69 + }, + { + "id": "4", + "x": 607, + "y": 1259 + }, + { + "id": "5", + "x": 1422, + "y": 714 + }, + { + "id": "7", + "x": 607, + "y": 714 + } + ], + "relationships": [ + { + "id": "11" + }, + { + "id": "8" + }, + { + "id": "10" + }, + { + "id": "9" + } + ] + } + ], + "containerViews": [ + { + "softwareSystemId": "7", + "key": "Containers", + "paperSize": "A5_Landscape", + "animations": [ + { + "order": 1, + "elements": [ + "1", + "4", + "5" + ], + "relationships": [ + "11" + ] + }, + { + "order": 2, + "elements": [ + "19" + ], + "relationships": [ + "28" + ] + }, + { + "order": 3, + "elements": [ + "17" + ], + "relationships": [ + "29", + "31" + ] + }, + { + "order": 4, + "elements": [ + "18" + ], + "relationships": [ + "30" + ] + }, + { + "order": 5, + "elements": [ + "20" + ], + "relationships": [ + "33", + "47", + "37", + "49" + ] + }, + { + "order": 6, + "elements": [ + "27" + ], + "relationships": [ + "45" + ] + } + ], + "externalSoftwareSystemBoundariesVisible": true, + "elements": [ + { + "id": "1", + "x": 1056, + "y": 24 + }, + { + "id": "4", + "x": 2012, + "y": 1214 + }, + { + "id": "27", + "x": 37, + "y": 1214 + }, + { + "id": "5", + "x": 2012, + "y": 664 + }, + { + "id": "17", + "x": 780, + "y": 664 + }, + { + "id": "18", + "x": 1283, + "y": 664 + }, + { + "id": "19", + "x": 37, + "y": 664 + }, + { + "id": "20", + "x": 1031, + "y": 1214 + } + ], + "relationships": [ + { + "id": "29" + }, + { + "id": "28" + }, + { + "id": "37" + }, + { + "id": "11" + }, + { + "id": "33" + }, + { + "id": "31" + }, + { + "id": "45" + }, + { + "id": "30" + }, + { + "id": "47" + }, + { + "id": "49" + } + ] + } + ], + "componentViews": [ + { + "key": "Components", + "paperSize": "A5_Landscape", + "animations": [ + { + "order": 1, + "elements": [ + "4", + "27", + "5", + "17", + "18" + ], + "relationships": [] + }, + { + "order": 2, + "elements": [ + "24", + "21" + ], + "relationships": [ + "44", + "36", + "40", + "32" + ] + }, + { + "order": 3, + "elements": [ + "22", + "25" + ], + "relationships": [ + "34", + "46", + "38", + "41" + ] + }, + { + "order": 4, + "elements": [ + "23", + "26" + ], + "relationships": [ + "35", + "48", + "39", + "42", + "43" + ] + } + ], + "containerId": "20", + "externalContainerBoundariesVisible": true, + "elements": [ + { + "id": "22", + "x": 1925, + "y": 436 + }, + { + "id": "23", + "x": 1015, + "y": 436 + }, + { + "id": "24", + "x": 105, + "y": 817 + }, + { + "id": "25", + "x": 1925, + "y": 817 + }, + { + "id": "26", + "x": 1015, + "y": 817 + }, + { + "id": "4", + "x": 1925, + "y": 1307 + }, + { + "id": "5", + "x": 1015, + "y": 1307 + }, + { + "id": "27", + "x": 105, + "y": 1307 + }, + { + "id": "17", + "x": 560, + "y": 10 + }, + { + "id": "18", + "x": 1470, + "y": 11 + }, + { + "id": "21", + "x": 105, + "y": 436 + } + ], + "relationships": [ + { + "id": "40", + "position": 55 + }, + { + "id": "41", + "position": 50 + }, + { + "id": "42" + }, + { + "id": "43" + }, + { + "id": "32", + "position": 35 + }, + { + "id": "36", + "position": 85 + }, + { + "id": "35", + "position": 45 + }, + { + "id": "34", + "position": 85 + }, + { + "id": "44", + "position": 60 + }, + { + "id": "46" + }, + { + "id": "48" + }, + { + "id": "38", + "position": 40 + }, + { + "id": "39", + "position": 40 + } + ] + } + ], + "dynamicViews": [ + { + "description": "Summarises how the sign in feature works in the single-page application.", + "key": "SignIn", + "paperSize": "A5_Landscape", + "elementId": "20", + "externalBoundariesVisible": true, + "relationships": [ + { + "id": "32", + "description": "Submits credentials to", + "order": "1", + "response": false, + "vertices": [ + { + "x": 1238, + "y": 236 + } + ], + "routing": "Curved", + "position": 50 + }, + { + "id": "40", + "description": "Validates credentials using", + "order": "2", + "response": false, + "vertices": [ + { + "x": 2065, + "y": 845 + } + ], + "routing": "Curved" + }, + { + "id": "44", + "description": "select * from users where username = ?", + "order": "3", + "response": false, + "vertices": [ + { + "x": 1218, + "y": 1416 + } + ], + "routing": "Curved" + }, + { + "id": "44", + "description": "Returns user data to", + "order": "4", + "response": true, + "vertices": [ + { + "x": 1240, + "y": 1220 + } + ], + "routing": "Curved" + }, + { + "id": "40", + "description": "Returns true if the hashed password matches", + "order": "5", + "response": true, + "vertices": [ + { + "x": 1828, + "y": 841 + } + ], + "routing": "Curved" + }, + { + "id": "32", + "description": "Sends back an authentication token to", + "order": "6", + "response": true, + "vertices": [ + { + "x": 1210, + "y": 450 + } + ], + "routing": "Curved" + } + ], + "elements": [ + { + "id": "24", + "x": 1720, + "y": 1182 + }, + { + "id": "27", + "x": 290, + "y": 1182 + }, + { + "id": "17", + "x": 290, + "y": 192 + }, + { + "id": "21", + "x": 1720, + "y": 192 + } + ] + } + ], + "deploymentViews": [ + { + "softwareSystemId": "7", + "key": "LiveDeployment", + "paperSize": "A4_Landscape", + "environment": "Live", + "animations": [ + { + "order": 1, + "elements": [ + "69", + "70", + "71" + ] + }, + { + "order": 2, + "elements": [ + "67", + "68" + ] + }, + { + "order": 3, + "elements": [ + "77", + "78", + "79", + "72", + "73", + "74", + "75" + ], + "relationships": [ + "80", + "81", + "76" + ] + }, + { + "order": 4, + "elements": [ + "82", + "83", + "84" + ], + "relationships": [ + "85" + ] + }, + { + "order": 5, + "elements": [ + "88", + "86", + "87" + ], + "relationships": [ + "89", + "93" + ] + } + ], + "elements": [ + { + "id": "88", + "x": 2584, + "y": 184 + }, + { + "id": "77", + "x": 0, + "y": 0 + }, + { + "id": "67", + "x": 0, + "y": 0 + }, + { + "id": "78", + "x": 0, + "y": 0 + }, + { + "id": "68", + "x": 424, + "y": 1071 + }, + { + "id": "79", + "x": 1504, + "y": 1071 + }, + { + "id": "69", + "x": 0, + "y": 0 + }, + { + "id": "90", + "x": 0, + "y": 0 + }, + { + "id": "91", + "x": 2584, + "y": 1959 + }, + { + "id": "70", + "x": 0, + "y": 0 + }, + { + "id": "71", + "x": 424, + "y": 184 + }, + { + "id": "82", + "x": 0, + "y": 0 + }, + { + "id": "83", + "x": 0, + "y": 0 + }, + { + "id": "72", + "x": 0, + "y": 0 + }, + { + "id": "84", + "x": 2584, + "y": 1071 + }, + { + "id": "73", + "x": 0, + "y": 0 + }, + { + "id": "74", + "x": 0, + "y": 0 + }, + { + "id": "86", + "x": 0, + "y": 0 + }, + { + "id": "75", + "x": 1504, + "y": 184 + }, + { + "id": "87", + "x": 0, + "y": 0 + } + ], + "relationships": [ + { + "id": "93" + }, + { + "id": "80" + }, + { + "id": "81" + }, + { + "id": "92" + }, + { + "id": "76" + }, + { + "id": "85" + }, + { + "id": "89" + } + ] + }, + { + "softwareSystemId": "7", + "key": "DevelopmentDeployment", + "paperSize": "A5_Landscape", + "environment": "Development", + "animations": [ + { + "order": 1, + "elements": [ + "50", + "51", + "52" + ] + }, + { + "order": 2, + "elements": [ + "55", + "57", + "53", + "54" + ], + "relationships": [ + "56", + "58" + ] + }, + { + "order": 3, + "elements": [ + "59", + "60", + "61" + ], + "relationships": [ + "62" + ] + } + ], + "elements": [ + { + "id": "55", + "x": 989, + "y": 176 + }, + { + "id": "57", + "x": 989, + "y": 516 + }, + { + "id": "59", + "x": 0, + "y": 0 + }, + { + "id": "60", + "x": 0, + "y": 0 + }, + { + "id": "61", + "x": 1827, + "y": 176 + }, + { + "id": "50", + "x": 0, + "y": 0 + }, + { + "id": "51", + "x": 0, + "y": 0 + }, + { + "id": "52", + "x": 152, + "y": 346 + }, + { + "id": "63", + "x": 0, + "y": 0 + }, + { + "id": "53", + "x": 0, + "y": 0 + }, + { + "id": "64", + "x": 0, + "y": 0 + }, + { + "id": "54", + "x": 0, + "y": 0 + }, + { + "id": "65", + "x": 1827, + "y": 1236 + } + ], + "relationships": [ + { + "id": "62", + "position": 50 + }, + { + "id": "56" + }, + { + "id": "66" + }, + { + "id": "58" + } + ] + } + ], + "configuration": { + "branding": {}, + "styles": { + "elements": [ + { + "tag": "Person", + "color": "#ffffff", + "fontSize": 22, + "shape": "Person" + }, + { + "tag": "Customer", + "background": "#08427b" + }, + { + "tag": "Bank Staff", + "background": "#999999" + }, + { + "tag": "Software System", + "background": "#1168bd", + "color": "#ffffff" + }, + { + "tag": "Existing System", + "background": "#999999", + "color": "#ffffff" + }, + { + "tag": "Container", + "background": "#438dd5", + "color": "#ffffff" + }, + { + "tag": "Web Browser", + "shape": "WebBrowser" + }, + { + "tag": "Mobile App", + "shape": "MobileDeviceLandscape" + }, + { + "tag": "Database", + "shape": "Cylinder" + }, + { + "tag": "Component", + "background": "#85bbf0", + "color": "#000000" + }, + { + "tag": "Failover", + "opacity": 25 + } + ], + "relationships": [] + }, + "terminology": {}, + "lastSavedView": "LiveDeployment", + "themes": [] + }, + "customViews": [], + "systemLandscapeViews": [], + "filteredViews": [] + } +} \ No newline at end of file diff --git a/structurizr-client/test/integration/backwardsCompatibility/structurizr-39459-workspace.json b/structurizr-client/test/integration/backwardsCompatibility/structurizr-39459-workspace.json new file mode 100644 index 000000000..d681a67ac --- /dev/null +++ b/structurizr-client/test/integration/backwardsCompatibility/structurizr-39459-workspace.json @@ -0,0 +1,278 @@ +{ + "id": 39459, + "name": "adr-tools", + "description": "A description of the adr-tools command line utility.", + "revision": 9, + "lastModifiedDate": "2021-10-17T15:02:07Z", + "lastModifiedUser": "", + "lastModifiedAgent": "structurizr-web/2539/diagram", + "model": { + "people": [ + { + "id": "1", + "tags": "Element,Person", + "name": "User", + "description": "Somebody on a software development team.", + "relationships": [ + { + "id": "5", + "tags": "Relationship,Synchronous", + "sourceId": "1", + "destinationId": "3", + "description": "Manages ADRs using", + "interactionStyle": "Synchronous" + }, + { + "id": "7", + "tags": "Relationship,Synchronous", + "sourceId": "1", + "destinationId": "2", + "description": "Manages ADRs using", + "interactionStyle": "Synchronous" + } + ], + "location": "Unspecified" + } + ], + "softwareSystems": [ + { + "id": "2", + "tags": "Element,Software System", + "url": "https://github.com/npryce/adr-tools", + "name": "adr-tools", + "description": "A command-line tool for working with Architecture Decision Records (ADRs).", + "location": "Unspecified", + "containers": [ + { + "id": "3", + "tags": "Element,Container", + "url": "https://github.com/npryce/adr-tools/tree/master/src", + "name": "adr", + "description": "A command-line tool for working with Architecture Decision Records (ADRs).", + "relationships": [ + { + "id": "6", + "tags": "Relationship,Synchronous", + "sourceId": "3", + "destinationId": "4", + "description": "Reads from and writes to", + "interactionStyle": "Synchronous" + } + ], + "technology": "Shell Scripts" + }, + { + "id": "4", + "tags": "Element,Container,File System", + "name": "File System", + "description": "Stores ADRs, templates, etc.", + "technology": "File System" + } + ] + } + ], + "customElements": [], + "deploymentNodes": [] + }, + "documentation": { + "decisions": [ + { + "elementId": "2", + "id": "8", + "date": "2017-02-21T00:00:00Z", + "title": "Use ISO 8601 Format for Dates", + "status": "Accepted", + "content": "# 8. Use ISO 8601 Format for Dates\n\nDate: 2017-02-21\n\n## Status\n\nAccepted\n\n## Context\n\n`adr-tools` seeks to communicate the history of architectural decisions of a\nproject. An important component of the history is the time at which a decision\nwas made.\n\nTo communicate effectively, `adr-tools` should present information as\nunambiguously as possible. That means that culture-neutral data formats should\nbe preferred over culture-specific formats.\n\nExisting `adr-tools` deployments format dates as `dd/mm/yyyy` by default. That\nformatting is common formatting in the United Kingdom (where the `adr-tools`\nproject was originally written), but is easily confused with the `mm/dd/yyyy`\nformat preferred in the United States.\n\nThe default date format may be overridden by setting `ADR_DATE` in `config.sh`.\n\n## Decision\n\n`adr-tools` will use the ISO 8601 format for dates: `yyyy-mm-dd`\n\n## Consequences\n\nDates are displayed in a standard, culture-neutral format.\n\nThe UK-style and ISO 8601 formats can be distinguished by their separator\ncharacter. The UK-style dates used a slash (`/`), while the ISO dates use a\nhyphen (`-`).\n\nPrior to this decision, `adr-tools` was deployed using the UK format for dates.\nAfter adopting the ISO 8601 format, existing deployments of `adr-tools` must do\none of the following:\n\n * Accept mixed formatting of dates within their documentation library.\n * Update existing documents to use ISO 8601 dates by running `adr upgrade-repository`\n\n---\nThis Architecture Decision Record (ADR) was written by Nat Pryce as a part of [adr-tools](https://github.com/npryce/adr-tools), and is reproduced here under the [Creative Commons Attribution 4.0 International (CC BY 4.0) license](https://creativecommons.org/licenses/by/4.0/).", + "format": "Markdown" + }, + { + "elementId": "2", + "id": "3", + "date": "2016-02-12T00:00:00Z", + "title": "Single command with subcommands", + "status": "Accepted", + "content": "# 3. Single command with subcommands\n\nDate: 2016-02-12\n\n## Status\n\nAccepted\n\n## Context\n\nThe tool provides a number of related commands to create\nand manipulate architecture decision records.\n\nHow can the user find out about the commands that are available?\n\n## Decision\n\nThe tool defines a single command, called `adr`.\n\nThe first argument to `adr` (the subcommand) specifies the\naction to perform. Further arguments are interpreted by the\nsubcommand.\n\nRunning `adr` without any arguments lists the available\nsubcommands.\n\nSubcommands are implemented as scripts in the same\ndirectory as the `adr` script. E.g. the subcommand `new` is\nimplemented as the script `adr-new`, the subcommand `help`\nas the script `adr-help` and so on.\n\nHelper scripts that are part of the implementation but not\nsubcommands follow a different naming convention, so that\nsubcommands can be listed by filtering and transforming script\nfile names.\n\n## Consequences\n\nUsers can more easily explore the capabilities of the tool.\n\nUsers are already used to this style of command-line tool. For\nexample, Git works this way.\n\nEach subcommand can be implemented in the most appropriate\nlanguage.\n\n---\nThis Architecture Decision Record (ADR) was written by Nat Pryce as a part of [adr-tools](https://github.com/npryce/adr-tools), and is reproduced here under the [Creative Commons Attribution 4.0 International (CC BY 4.0) license](https://creativecommons.org/licenses/by/4.0/).\n", + "format": "Markdown" + }, + { + "elementId": "2", + "id": "4", + "date": "2016-02-12T00:00:00Z", + "title": "Markdown format", + "status": "Accepted", + "content": "# 4. Markdown format\n\nDate: 2016-02-12\n\n## Status\n\nAccepted\n\n## Context\n\nThe decision records must be stored in a plain text format:\n\n* This works well with version control systems.\n\n* It allows the tool to modify the status of records and insert\n hyperlinks when one decision supercedes another.\n\n* Decisions can be read in the terminal, IDE, version control\n browser, etc.\n\nPeople will want to use some formatting: lists, code examples,\nand so on.\n\nPeople will want to view the decision records in a more readable\nformat than plain text, and maybe print them out.\n\n\n## Decision\n\nRecord architecture decisions in [Markdown format](https://daringfireball.net/projects/markdown/).\n\n## Consequences\n\nDecisions can be read in the terminal.\n\nDecisions will be formatted nicely and hyperlinked by the\nbrowsers of project hosting sites like GitHub and Bitbucket.\n\nTools like [Pandoc](http://pandoc.org/) can be used to convert\nthe decision records into HTML or PDF.\n\n---\nThis Architecture Decision Record (ADR) was written by Nat Pryce as a part of [adr-tools](https://github.com/npryce/adr-tools), and is reproduced here under the [Creative Commons Attribution 4.0 International (CC BY 4.0) license](https://creativecommons.org/licenses/by/4.0/).", + "format": "Markdown" + }, + { + "elementId": "2", + "id": "2", + "date": "2016-02-12T00:00:00Z", + "title": "Implement as shell scripts", + "status": "Accepted", + "content": "# 2. Implement as shell scripts\n\nDate: 2016-02-12\n\n## Status\n\nAccepted\n\n## Context\n\nADRs are plain text files stored in a subdirectory of the project.\n\nThe tool needs to create new files and apply small edits to\nthe Status section of existing files.\n\n## Decision\n\nThe tool is implemented as shell scripts that use standard Unix\ntools -- grep, sed, awk, etc.\n\n## Consequences\n\nThe tool won't support Windows. Being plain text files, ADRs can\nbe created by hand and edited in any text editor. This tool just\nmakes the process more convenient.\n\nDevelopment will have to cope with differences between Unix\nvariants, particularly Linux and MacOS X.\n\n---\nThis Architecture Decision Record (ADR) was written by Nat Pryce as a part of [adr-tools](https://github.com/npryce/adr-tools), and is reproduced here under the [Creative Commons Attribution 4.0 International (CC BY 4.0) license](https://creativecommons.org/licenses/by/4.0/).", + "format": "Markdown" + }, + { + "elementId": "2", + "id": "6", + "date": "2016-02-16T00:00:00Z", + "title": "Packaging and distribution in other version control repositories", + "status": "Accepted", + "content": "# 6. Packaging and distribution in other version control repositories\n\nDate: 2016-02-16\n\n## Status\n\nAccepted\n\n## Context\n\nUsers want to install adr-tools with their preferred package\nmanager. For example, Ubuntu users use `apt`, RedHat users use\n`yum` and Mac OS X users use [Homebrew](http://brew.sh).\n\nThe developers of `adr-tools` don't know how, nor have permissions,\nto use all these packaging and distribution systems. Therefore packaging\nand distribution must be done by \"downstream\" parties.\n\nThe developers of the tool should not favour any one particular\npackaging and distribution solution.\n\n## Decision\n\nThe `adr-tools` project will not contain any packaging or\ndistribution scripts and config.\n\nPackaging and distribution will be managed by other projects in\nseparate version control repositories.\n\n## Consequences\n\nThe git repo of this project will be simpler.\n\nEventually, users will not have to use Git to get the software.\n\nWe will have to tag releases in the `adr-tools` repository so that\npackaging projects know what can be published and how it should be\nidentified.\n\nWe will document how users can install the software in this\nproject's README file.\n\n---\nThis Architecture Decision Record (ADR) was written by Nat Pryce as a part of [adr-tools](https://github.com/npryce/adr-tools), and is reproduced here under the [Creative Commons Attribution 4.0 International (CC BY 4.0) license](https://creativecommons.org/licenses/by/4.0/).", + "format": "Markdown" + }, + { + "elementId": "2", + "id": "1", + "date": "2016-02-12T00:00:00Z", + "title": "Record architecture decisions", + "status": "Accepted", + "content": "# 1. Record architecture decisions\n\nDate: 2016-02-12\n\n## Status\n\nAccepted\n\n## Context\n\nWe need to record the architectural decisions made on this project.\n\n## Decision\n\nWe will use Architecture Decision Records, as described by Michael Nygard in this article: [http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions)\n\n## Consequences\n\nSee Michael Nygard's article, linked above.\n\n---\nThis Architecture Decision Record (ADR) was written by Nat Pryce as a part of [adr-tools](https://github.com/npryce/adr-tools), and is reproduced here under the [Creative Commons Attribution 4.0 International (CC BY 4.0) license](https://creativecommons.org/licenses/by/4.0/).", + "format": "Markdown" + }, + { + "elementId": "2", + "id": "5", + "date": "2016-02-13T00:00:00Z", + "title": "Help comments", + "status": "Accepted", + "content": "# 5. Help comments\n\nDate: 2016-02-13\n\n## Status\n\nAccepted\n\n## Context\n\nThe tool will have a `help` subcommand to provide documentation\nfor users.\n\nIt's nice to have usage documentation in the script files\nthemselves, in comments. When reading the code, that's the first\nplace to look for information about how to run a script.\n\n## Decision\n\nWrite usage documentation in comments in the source file.\n\nDistinguish between documentation comments and normal comments.\nDocumentation comments have two hash characters at the start of\nthe line.\n\nThe `adr help` command can parse comments out from the script\nusing the standard Unix tools `grep` and `cut`.\n\n## Consequences\n\nNo need to maintain help text in a separate file.\n\nHelp text can easily be kept up to date as the script is edited.\n\nThere's no automated check that the help text is up to date. The\ntests do not work well as documentation for users, and the help\ntext is not easily cross-checked against the code.\n\nThis won't work if any subcommands are not implemented as scripts\nthat use '#' as a comment character.\n\n---\nThis Architecture Decision Record (ADR) was written by Nat Pryce as a part of [adr-tools](https://github.com/npryce/adr-tools), and is reproduced here under the [Creative Commons Attribution 4.0 International (CC BY 4.0) license](https://creativecommons.org/licenses/by/4.0/).", + "format": "Markdown" + }, + { + "elementId": "2", + "id": "7", + "date": "2016-12-17T00:00:00Z", + "title": "Invoke adr-config executable to get configuration", + "status": "Accepted", + "content": "# 7. Invoke adr-config executable to get configuration\n\nDate: 2016-12-17\n\n## Status\n\nAccepted\n\n## Context\n\nPackagers (e.g. Homebrew developers) want to configure adr-tools to match the conventions of their installation. \n\nCurrently, this is done by sourcing a file `config.sh`, which should sit beside the `adr` executable.\n\nThis name is too common.\n\nThe `config.sh` file is not executable, and so doesn't belong in a bin directory.\n\n## Decision\n\nReplace `config.sh` with an executable, named `adr-config` that outputs configuration.\n\nEach script in ADR Tools will eval the output of `adr-config` to configure itself.\n\n## Consequences\n\nConfiguration within ADR Tools is a little more complicated.\n\nPackagers can write their own implementation of `adr-config` that outputs configuration that matches the platform's installation conventions, and deploy it next to the `adr` script.\n\nTo make development easier, the implementation of `adr-config` in the project's src/ directory will output configuration that lets the tool to run from the src/ directory without any installation step. (Packagers should not include this script in a deployable package.)\n\n---\nThis Architecture Decision Record (ADR) was written by Nat Pryce as a part of [adr-tools](https://github.com/npryce/adr-tools), and is reproduced here under the [Creative Commons Attribution 4.0 International (CC BY 4.0) license](https://creativecommons.org/licenses/by/4.0/).", + "format": "Markdown" + } + ], + "sections": [], + "images": [] + }, + "views": { + "systemContextViews": [ + { + "softwareSystemId": "2", + "description": "The system context diagram for adr-tools.", + "key": "SystemContext", + "paperSize": "A5_Landscape", + "animations": [ + { + "order": 1, + "elements": [ + "1", + "2" + ], + "relationships": [ + "7" + ] + } + ], + "enterpriseBoundaryVisible": true, + "elements": [ + { + "id": "1", + "x": 1040, + "y": 224 + }, + { + "id": "2", + "x": 1015, + "y": 1104 + } + ], + "relationships": [ + { + "id": "7" + } + ] + } + ], + "containerViews": [ + { + "softwareSystemId": "2", + "description": "The container diagram for adr-tools.", + "key": "Containers", + "paperSize": "A5_Landscape", + "dimensions": { + "width": 2480, + "height": 1748 + }, + "externalSoftwareSystemBoundariesVisible": false, + "elements": [ + { + "id": "1", + "x": 1040, + "y": 75 + }, + { + "id": "3", + "x": 1015, + "y": 713 + }, + { + "id": "4", + "x": 1015, + "y": 1251 + } + ], + "relationships": [ + { + "id": "5", + "routing": "Orthogonal" + }, + { + "id": "6", + "routing": "Orthogonal" + } + ], + "animations": [] + } + ], + "configuration": { + "branding": {}, + "styles": { + "elements": [ + { + "tag": "Element", + "color": "#ffffff", + "shape": "RoundedBox" + }, + { + "tag": "Software System", + "background": "#18adad", + "color": "#ffffff" + }, + { + "tag": "Person", + "background": "#008282", + "color": "#ffffff", + "shape": "Person" + }, + { + "tag": "Container", + "background": "#6dbfbf" + }, + { + "tag": "File System", + "shape": "Folder" + } + ], + "relationships": [] + }, + "terminology": {}, + "lastSavedView": "Containers", + "themes": [] + }, + "customViews": [], + "systemLandscapeViews": [], + "componentViews": [], + "dynamicViews": [], + "deploymentViews": [], + "filteredViews": [] + }, + "properties": {} +} \ No newline at end of file diff --git a/structurizr-client/test/integration/com/structurizr/api/BackwardsCompatibilityTests.java b/structurizr-client/test/integration/com/structurizr/api/BackwardsCompatibilityTests.java new file mode 100644 index 000000000..b5c9959da --- /dev/null +++ b/structurizr-client/test/integration/com/structurizr/api/BackwardsCompatibilityTests.java @@ -0,0 +1,19 @@ +package com.structurizr.api; + +import com.structurizr.util.WorkspaceUtils; +import org.junit.Test; + +import java.io.File; + +public class BackwardsCompatibilityTests { + + private static final File PATH_TO_WORKSPACE_FILES = new File("test/integration/backwardsCompatibility"); + + @Test + public void test() throws Exception { + for (File file : PATH_TO_WORKSPACE_FILES.listFiles(f -> f.getName().endsWith(".json"))) { + WorkspaceUtils.loadWorkspaceFromJson(file); + } + } + +} \ No newline at end of file diff --git a/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java b/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java index 0baedba6d..88f8b9c08 100644 --- a/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java +++ b/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java @@ -189,24 +189,4 @@ public void test_exceptionThrown_WhenRelationshipReferencedByViewIsMissingFromTh } } - @Test - public void test_exceptionThrown_WhenElementAssociatedWithDocumentationSectionIsMissingFromTheModel() throws Exception { - try { - WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "ElementAssociatedWithDocumentationSectionIsMissingFromTheModel.json")); - fail(); - } catch (WorkspaceValidationException we) { - assertEquals("The documentation section with title \"Context\" is associated with an element (id=2), but that element does not exist in the model.", we.getMessage()); - } - } - - @Test - public void test_exceptionThrown_WhenElementAssociatedWithDecisionIsMissingFromTheModel() throws Exception { - try { - WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "ElementAssociatedWithDecisionIsMissingFromTheModel.json")); - fail(); - } catch (WorkspaceValidationException we) { - assertEquals("The decision record with title \"Use Java\" is associated with an element (id=2), but that element does not exist in the model.", we.getMessage()); - } - } - } \ No newline at end of file diff --git a/structurizr-client/test/integration/workspaceValidation/ElementAssociatedWithDecisionIsMissingFromTheModel.json b/structurizr-client/test/integration/workspaceValidation/ElementAssociatedWithDecisionIsMissingFromTheModel.json deleted file mode 100644 index ce89370a5..000000000 --- a/structurizr-client/test/integration/workspaceValidation/ElementAssociatedWithDecisionIsMissingFromTheModel.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "id" : 0, - "name" : "Name", - "description" : "Description", - "configuration" : { }, - "model" : { - "softwareSystems" : [ { - "id" : "1", - "tags" : "Element,Software System", - "name" : "Software System", - "location" : "Unspecified" - } ] - }, - "documentation" : { - "decisions" : [ { - "elementId" : "2", - "id" : "1", - "date" : "2019-10-24T19:17:46Z", - "title" : "Use Java", - "status" : "Proposed", - "content" : "Content", - "format" : "Markdown" - } ] - }, - "views" : { - "configuration" : { - "branding" : { }, - "styles" : { }, - "terminology" : { } - } - } -} \ No newline at end of file diff --git a/structurizr-client/test/integration/workspaceValidation/ElementAssociatedWithDocumentationSectionIsMissingFromTheModel.json b/structurizr-client/test/integration/workspaceValidation/ElementAssociatedWithDocumentationSectionIsMissingFromTheModel.json deleted file mode 100644 index 036e40fad..000000000 --- a/structurizr-client/test/integration/workspaceValidation/ElementAssociatedWithDocumentationSectionIsMissingFromTheModel.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "id" : 0, - "name" : "Name", - "description" : "Description", - "configuration" : { }, - "model" : { - "softwareSystems" : [ { - "id" : "1", - "tags" : "Element,Software System", - "name" : "Software System", - "location" : "Unspecified" - } ] - }, - "documentation" : { - "sections" : [ { - "elementId" : "2", - "title" : "Context", - "order" : 1, - "format" : "Markdown", - "content" : "Content" - } ], - "template" : { - "name" : "Software Guidebook", - "author" : "Simon Brown", - "url" : "https://leanpub.com/visualising-software-architecture" - } - }, - "views" : { - "configuration" : { - "branding" : { }, - "styles" : { }, - "terminology" : { } - } - } -} \ No newline at end of file diff --git a/structurizr-client/test/integration/workspaces/bigbankplc.json b/structurizr-client/test/integration/workspaces/bigbankplc.json deleted file mode 100644 index 19a4f2d9e..000000000 --- a/structurizr-client/test/integration/workspaces/bigbankplc.json +++ /dev/null @@ -1,1452 +0,0 @@ -{ - "id": 36141, - "name": "Big Bank plc", - "description": "This is an example workspace to illustrate the key features of Structurizr, based around a fictional online banking system.", - "model": { - "enterprise": { - "name": "Big Bank plc" - }, - "people": [ - { - "tags": "Element,Person,Bank Staff", - "id": "11", - "name": "Customer Service Staff", - "description": "Customer service staff within the bank.", - "relationships": [ - { - "tags": "Relationship,Synchronous", - "id": "12", - "sourceId": "11", - "destinationId": "4", - "description": "Uses", - "interactionStyle": "Synchronous" - } - ], - "location": "Internal" - }, - { - "tags": "Element,Person,Bank Staff", - "id": "14", - "name": "Back Office Staff", - "description": "Administration and support staff within the bank.", - "relationships": [ - { - "tags": "Relationship,Synchronous", - "id": "15", - "sourceId": "14", - "destinationId": "4", - "description": "Uses", - "interactionStyle": "Synchronous" - } - ], - "location": "Internal" - }, - { - "tags": "Element,Person", - "id": "1", - "name": "Personal Banking Customer", - "description": "A customer of the bank, with personal bank accounts.", - "relationships": [ - { - "tags": "Relationship,Synchronous", - "id": "22", - "sourceId": "1", - "destinationId": "16", - "description": "Uses", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous", - "id": "3", - "sourceId": "1", - "destinationId": "2", - "description": "Uses", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous", - "id": "10", - "sourceId": "1", - "destinationId": "8", - "description": "Withdraws cash using", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous", - "id": "13", - "sourceId": "1", - "destinationId": "11", - "description": "Asks questions to", - "technology": "Telephone", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous", - "id": "23", - "sourceId": "1", - "destinationId": "17", - "description": "Uses", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous", - "id": "21", - "sourceId": "1", - "destinationId": "18", - "description": "Uses", - "technology": "HTTPS", - "interactionStyle": "Synchronous" - } - ], - "location": "External" - } - ], - "softwareSystems": [ - { - "tags": "Element,Software System,Existing System", - "id": "4", - "name": "Mainframe Banking System", - "description": "Stores all of the core banking information about customers, accounts, transactions, etc.", - "location": "Internal" - }, - { - "tags": "Element,Software System,Existing System", - "id": "6", - "name": "E-mail System", - "description": "The internal Microsoft Exchange e-mail system.", - "relationships": [ - { - "tags": "Relationship,Synchronous", - "id": "7", - "sourceId": "6", - "destinationId": "1", - "description": "Sends e-mails to", - "interactionStyle": "Synchronous" - } - ], - "location": "Internal" - }, - { - "tags": "Element,Software System,Existing System", - "id": "8", - "name": "ATM", - "description": "Allows customers to withdraw cash.", - "relationships": [ - { - "tags": "Relationship,Synchronous", - "id": "9", - "sourceId": "8", - "destinationId": "4", - "description": "Uses", - "interactionStyle": "Synchronous" - } - ], - "location": "Internal" - }, - { - "tags": "Element,Software System", - "id": "2", - "name": "Internet Banking System", - "description": "Allows customers to view information about their bank accounts, and make payments.", - "relationships": [ - { - "tags": "Relationship,Synchronous", - "id": "5", - "sourceId": "2", - "destinationId": "4", - "description": "Uses", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous", - "id": "40", - "sourceId": "2", - "destinationId": "6", - "description": "Sends e-mail using", - "technology": "SMTP", - "interactionStyle": "Synchronous" - } - ], - "location": "Internal", - "containers": [ - { - "tags": "Element,Container", - "id": "18", - "name": "Web Application", - "description": "Delivers the static content and the Internet banking single page application.", - "relationships": [ - { - "tags": "Relationship,Synchronous", - "id": "24", - "sourceId": "18", - "destinationId": "16", - "description": "Delivers", - "interactionStyle": "Synchronous" - } - ], - "technology": "Java and Spring MVC" - }, - { - "tags": "Element,Container,Database", - "id": "20", - "name": "Database", - "description": "Stores user registration information, hashed authentication credentials, access logs, etc.", - "technology": "Relational Database Schema" - }, - { - "tags": "Element,Container,Web Browser", - "id": "16", - "name": "Single-Page Application", - "description": "Provides all of the Internet banking functionality to customers via their web browser.", - "relationships": [ - { - "tags": "Relationship,Synchronous", - "id": "41", - "sourceId": "16", - "destinationId": "19", - "description": "Uses", - "technology": "JSON/HTTPS", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous", - "id": "33", - "sourceId": "16", - "destinationId": "29", - "description": "Uses", - "technology": "JSON/HTTPS", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous", - "id": "32", - "sourceId": "16", - "destinationId": "28", - "description": "Uses", - "technology": "JSON/HTTPS", - "interactionStyle": "Synchronous" - } - ], - "technology": "JavaScript and Angular" - }, - { - "tags": "Element,Container,Mobile App", - "id": "17", - "name": "Mobile App", - "description": "Provides a limited subset of the Internet banking functionality to customers via their mobile device.", - "relationships": [ - { - "tags": "Relationship,Synchronous", - "id": "42", - "sourceId": "17", - "destinationId": "19", - "description": "Uses", - "technology": "JSON/HTTPS", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous", - "id": "35", - "sourceId": "17", - "destinationId": "29", - "description": "Uses", - "technology": "JSON/HTTPS", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous", - "id": "34", - "sourceId": "17", - "destinationId": "28", - "description": "Uses", - "technology": "JSON/HTTPS", - "interactionStyle": "Synchronous" - } - ], - "technology": "Xamarin" - }, - { - "tags": "Element,Container", - "id": "19", - "name": "API Application", - "description": "Provides Internet banking functionality via a JSON/HTTPS API.", - "relationships": [ - { - "tags": "Relationship,Synchronous", - "id": "27", - "sourceId": "19", - "destinationId": "6", - "description": "Sends e-mail using", - "technology": "SMTP", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous", - "id": "25", - "sourceId": "19", - "destinationId": "20", - "description": "Reads from and writes to", - "technology": "JDBC", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous", - "id": "26", - "sourceId": "19", - "destinationId": "4", - "description": "Uses", - "technology": "XML/HTTPS", - "interactionStyle": "Synchronous" - } - ], - "technology": "Java and Spring MVC", - "components": [ - { - "tags": "Element,Component", - "id": "29", - "name": "Accounts Summary Controller", - "description": "Provides customers with a summary of their bank accounts.", - "relationships": [ - { - "tags": "Relationship,Synchronous", - "id": "37", - "sourceId": "29", - "destinationId": "31", - "description": "Uses", - "interactionStyle": "Synchronous" - } - ], - "technology": "Spring MVC Rest Controller", - "size": 0 - }, - { - "tags": "Element,Component", - "id": "30", - "name": "Security Component", - "description": "Provides functionality related to signing in, changing passwords, etc.", - "relationships": [ - { - "tags": "Relationship,Synchronous", - "id": "38", - "sourceId": "30", - "destinationId": "20", - "description": "Reads from and writes to", - "technology": "JDBC", - "interactionStyle": "Synchronous" - } - ], - "technology": "Spring Bean", - "size": 0 - }, - { - "tags": "Element,Component", - "id": "28", - "name": "Sign In Controller", - "description": "Allows users to sign in to the Internet Banking System.", - "relationships": [ - { - "tags": "Relationship,Synchronous", - "id": "36", - "sourceId": "28", - "destinationId": "30", - "description": "Uses", - "interactionStyle": "Synchronous" - } - ], - "technology": "Spring MVC Rest Controller", - "size": 0 - }, - { - "tags": "Element,Component", - "id": "31", - "name": "Mainframe Banking System Facade", - "description": "A facade onto the mainframe banking system.", - "relationships": [ - { - "tags": "Relationship,Synchronous", - "id": "39", - "sourceId": "31", - "destinationId": "4", - "description": "Uses", - "technology": "XML/HTTPS", - "interactionStyle": "Synchronous" - } - ], - "technology": "Spring Bean", - "size": 0 - } - ] - } - ] - } - ], - "deploymentNodes": [ - { - "id": "43", - "name": "Developer Laptop", - "description": "A developer laptop.", - "technology": "Microsoft Windows 10 or Apple macOS", - "instances": 1, - "children": [ - { - "id": "44", - "name": "Docker Container - Web Server", - "description": "A Docker container.", - "technology": "Docker", - "instances": 1, - "children": [ - { - "id": "45", - "name": "Apache Tomcat", - "description": "An open source Java EE web server.", - "properties": { - "Xmx": "512M", - "Java Version": "8", - "Xms": "1024M" - }, - "technology": "Apache Tomcat 8.x", - "instances": 1, - "containerInstances": [ - { - "tags": "Element,Container,Container Instance", - "id": "47", - "relationships": [ - { - "tags": "Relationship,Synchronous", - "id": "51", - "sourceId": "47", - "destinationId": "50", - "description": "Reads from and writes to", - "technology": "JDBC", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous,Failover", - "id": "86", - "sourceId": "47", - "destinationId": "84", - "description": "Reads from and writes to", - "technology": "JDBC", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous", - "id": "81", - "sourceId": "47", - "destinationId": "79", - "description": "Reads from and writes to", - "technology": "JDBC", - "interactionStyle": "Synchronous" - } - ], - "containerId": "19", - "instanceId": 1, - "properties": {} - }, - { - "tags": "Element,Container,Container Instance", - "id": "46", - "relationships": [ - { - "tags": "Relationship,Synchronous", - "id": "54", - "sourceId": "46", - "destinationId": "53", - "description": "Delivers", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous", - "id": "62", - "sourceId": "46", - "destinationId": "61", - "description": "Delivers", - "interactionStyle": "Synchronous" - } - ], - "containerId": "18", - "instanceId": 1, - "properties": {} - } - ] - } - ] - }, - { - "id": "48", - "name": "Docker Container - Database Server", - "description": "A Docker container.", - "technology": "Docker", - "instances": 1, - "children": [ - { - "id": "49", - "name": "Database Server", - "description": "A development database.", - "technology": "Oracle 12c", - "instances": 1, - "containerInstances": [ - { - "tags": "Element,Container,Database,Container Instance", - "id": "50", - "containerId": "20", - "instanceId": 1, - "properties": {} - } - ] - } - ] - }, - { - "id": "52", - "name": "Web Browser", - "technology": "Google Chrome, Mozilla Firefox, Apple Safari or Microsoft Edge", - "instances": 1, - "containerInstances": [ - { - "tags": "Element,Container,Web Browser,Container Instance", - "id": "53", - "relationships": [ - { - "tags": "Relationship,Synchronous", - "id": "55", - "sourceId": "53", - "destinationId": "47", - "description": "Uses", - "technology": "JSON/HTTPS", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous", - "id": "74", - "sourceId": "53", - "destinationId": "72", - "description": "Uses", - "technology": "JSON/HTTPS", - "interactionStyle": "Synchronous" - } - ], - "containerId": "16", - "instanceId": 1, - "properties": {} - } - ] - } - ] - }, - { - "id": "64", - "name": "Big Bank plc", - "technology": "Big Bank plc data center", - "instances": 1, - "children": [ - { - "id": "70", - "name": "bigbank-api***", - "description": "A web server residing in the web server farm, accessed via F5 BIG-IP LTMs.", - "properties": { - "Location": "London and Reading" - }, - "technology": "Ubuntu 16.04 LTS", - "instances": 8, - "children": [ - { - "id": "71", - "name": "Apache Tomcat", - "description": "An open source Java EE web server.", - "properties": { - "Xmx": "512M", - "Java Version": "8", - "Xms": "1024M" - }, - "technology": "Apache Tomcat 8.x", - "instances": 1, - "containerInstances": [ - { - "tags": "Element,Container,Container Instance", - "id": "72", - "relationships": [ - { - "tags": "Relationship,Synchronous", - "id": "75", - "sourceId": "72", - "destinationId": "50", - "description": "Reads from and writes to", - "technology": "JDBC", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous,Failover", - "id": "85", - "sourceId": "72", - "destinationId": "84", - "description": "Reads from and writes to", - "technology": "JDBC", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous", - "id": "80", - "sourceId": "72", - "destinationId": "79", - "description": "Reads from and writes to", - "technology": "JDBC", - "interactionStyle": "Synchronous" - } - ], - "containerId": "19", - "instanceId": 2, - "properties": {} - } - ] - } - ] - }, - { - "id": "77", - "name": "bigbank-db01", - "description": "The primary database server.", - "properties": { - "Location": "London" - }, - "technology": "Ubuntu 16.04 LTS", - "instances": 1, - "children": [ - { - "id": "78", - "name": "Oracle - Primary", - "description": "The primary, live database server.", - "relationships": [ - { - "tags": "Relationship,Synchronous", - "id": "87", - "sourceId": "78", - "destinationId": "83", - "description": "Replicates data to", - "interactionStyle": "Synchronous" - } - ], - "technology": "Oracle 12c", - "instances": 1, - "containerInstances": [ - { - "tags": "Element,Container,Database,Container Instance", - "id": "79", - "containerId": "20", - "instanceId": 2, - "properties": {} - } - ] - } - ] - }, - { - "id": "82", - "name": "bigbank-db02", - "description": "The secondary database server.", - "properties": { - "Location": "Reading" - }, - "technology": "Ubuntu 16.04 LTS", - "instances": 1, - "children": [ - { - "id": "83", - "name": "Oracle - Secondary", - "description": "A secondary, standby database server, used for failover purposes only.", - "technology": "Oracle 12c", - "instances": 1, - "containerInstances": [ - { - "tags": "Element,Container,Database,Container Instance,Failover", - "id": "84", - "containerId": "20", - "instanceId": 3, - "properties": {} - } - ] - } - ] - }, - { - "id": "65", - "name": "bigbank-web***", - "description": "A web server residing in the web server farm, accessed via F5 BIG-IP LTMs.", - "properties": { - "Location": "London and Reading" - }, - "technology": "Ubuntu 16.04 LTS", - "instances": 4, - "children": [ - { - "id": "66", - "name": "Apache Tomcat", - "description": "An open source Java EE web server.", - "properties": { - "Xmx": "512M", - "Java Version": "8", - "Xms": "1024M" - }, - "technology": "Apache Tomcat 8.x", - "instances": 1, - "containerInstances": [ - { - "tags": "Element,Container,Container Instance", - "id": "67", - "relationships": [ - { - "tags": "Relationship,Synchronous", - "id": "69", - "sourceId": "67", - "destinationId": "53", - "description": "Delivers", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous", - "id": "68", - "sourceId": "67", - "destinationId": "61", - "description": "Delivers", - "interactionStyle": "Synchronous" - } - ], - "containerId": "18", - "instanceId": 2, - "properties": {} - } - ] - } - ] - } - ] - }, - { - "id": "59", - "name": "Customer's computer", - "technology": "Microsoft Windows or Apple macOS", - "instances": 1, - "children": [ - { - "id": "60", - "name": "Web Browser", - "technology": "Google Chrome, Mozilla Firefox, Apple Safari or Microsoft Edge", - "instances": 1, - "containerInstances": [ - { - "tags": "Element,Container,Web Browser,Container Instance", - "id": "61", - "relationships": [ - { - "tags": "Relationship,Synchronous", - "id": "63", - "sourceId": "61", - "destinationId": "47", - "description": "Uses", - "technology": "JSON/HTTPS", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous", - "id": "73", - "sourceId": "61", - "destinationId": "72", - "description": "Uses", - "technology": "JSON/HTTPS", - "interactionStyle": "Synchronous" - } - ], - "containerId": "16", - "instanceId": 2, - "properties": {} - } - ] - } - ] - }, - { - "id": "56", - "name": "Customer's mobile device", - "technology": "Apple iOS or Android", - "instances": 1, - "containerInstances": [ - { - "tags": "Element,Container,Mobile App,Container Instance", - "id": "57", - "relationships": [ - { - "tags": "Relationship,Synchronous", - "id": "76", - "sourceId": "57", - "destinationId": "72", - "description": "Uses", - "technology": "JSON/HTTPS", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous", - "id": "58", - "sourceId": "57", - "destinationId": "47", - "description": "Uses", - "technology": "JSON/HTTPS", - "interactionStyle": "Synchronous" - } - ], - "containerId": "17", - "instanceId": 1, - "properties": {} - } - ] - } - ] - }, - "documentation": { - "sections": [ - { - "elementId": "2", - "type": "Context", - "order": 1, - "format": "Markdown", - "content": "Here is some context about the Internet Banking System...\n![](embed:SystemLandscape)\n![](embed:SystemContext)\n### Internet Banking System\n...\n### Mainframe Banking System\n...\n" - }, - { - "elementId": "18", - "type": "Components", - "order": 3, - "format": "Markdown", - "content": "Here is some information about the Web Application...\n![](embed:Components)\n### Sign in process\nHere is some information about the Sign In Controller, including how the sign in process works...\n![](embed:SignIn)" - }, - { - "elementId": "2", - "type": "Development Environment", - "order": 4, - "format": "AsciiDoc", - "content": "Here is some information about how to set up a development environment for the Internet Banking System...\nimage::embed:DevelopmentDeployment[]" - }, - { - "elementId": "2", - "type": "Containers", - "order": 2, - "format": "Markdown", - "content": "Here is some information about the containers within the Internet Banking System...\n![](embed:Containers)\n### Web Application\n...\n### Database\n...\n" - }, - { - "elementId": "2", - "type": "Deployment", - "order": 5, - "format": "AsciiDoc", - "content": "Here is some information about the live deployment environment for the Internet Banking System...\nimage::embed:LiveDeployment[]" - } - ], - "template": { - "name": "Software Guidebook", - "author": "Simon Brown", - "url": "https://leanpub.com/visualising-software-architecture" - } - }, - "views": { - "systemLandscapeViews": [ - { - "description": "The system landscape diagram for Big Bank plc.", - "key": "SystemLandscape", - "paperSize": "A5_Landscape", - "enterpriseBoundaryVisible": true, - "elements": [ - { - "id": "11", - "x": 1947, - "y": 36 - }, - { - "id": "1", - "x": 87, - "y": 643 - }, - { - "id": "2", - "x": 1012, - "y": 813 - }, - { - "id": "14", - "x": 1947, - "y": 1241 - }, - { - "id": "4", - "x": 1922, - "y": 693 - }, - { - "id": "6", - "x": 1012, - "y": 1326 - }, - { - "id": "8", - "x": 1012, - "y": 301 - } - ], - "relationships": [ - { - "id": "3" - }, - { - "id": "15" - }, - { - "id": "40" - }, - { - "id": "5" - }, - { - "id": "13", - "vertices": [ - { - "x": 285, - "y": 240 - } - ] - }, - { - "id": "12" - }, - { - "id": "7" - }, - { - "id": "9" - }, - { - "id": "10" - } - ] - } - ], - "systemContextViews": [ - { - "softwareSystemId": "2", - "description": "The system context diagram for the Internet Banking System.", - "key": "SystemContext", - "paperSize": "A5_Landscape", - "enterpriseBoundaryVisible": false, - "elements": [ - { - "id": "1", - "x": 632, - "y": 69 - }, - { - "id": "2", - "x": 607, - "y": 714 - }, - { - "id": "4", - "x": 607, - "y": 1259 - }, - { - "id": "6", - "x": 1422, - "y": 714 - } - ], - "relationships": [ - { - "id": "3" - }, - { - "id": "40" - }, - { - "id": "5" - }, - { - "id": "7" - } - ] - } - ], - "containerViews": [ - { - "softwareSystemId": "2", - "description": "The container diagram for the Internet Banking System.", - "key": "Containers", - "paperSize": "A5_Landscape", - "elements": [ - { - "id": "1", - "x": 1038, - "y": 24 - }, - { - "id": "4", - "x": 1962, - "y": 1214 - }, - { - "id": "16", - "x": 698, - "y": 664 - }, - { - "id": "17", - "x": 1329, - "y": 664 - }, - { - "id": "6", - "x": 1962, - "y": 664 - }, - { - "id": "18", - "x": 67, - "y": 664 - }, - { - "id": "19", - "x": 1013, - "y": 1214 - }, - { - "id": "20", - "x": 67, - "y": 1214 - } - ], - "relationships": [ - { - "id": "27" - }, - { - "id": "26" - }, - { - "id": "25" - }, - { - "id": "24" - }, - { - "id": "41" - }, - { - "id": "23" - }, - { - "id": "42" - }, - { - "id": "22" - }, - { - "id": "7" - }, - { - "id": "21" - } - ] - } - ], - "componentViews": [ - { - "softwareSystemId": "2", - "description": "The component diagram for the API Application.", - "key": "Components", - "paperSize": "A5_Landscape", - "containerId": "19", - "elements": [ - { - "id": "4", - "x": 1915, - "y": 825 - }, - { - "id": "16", - "x": 65, - "y": 25 - }, - { - "id": "17", - "x": 1350, - "y": 25 - }, - { - "id": "28", - "x": 345, - "y": 445 - }, - { - "id": "29", - "x": 1105, - "y": 445 - }, - { - "id": "30", - "x": 345, - "y": 825 - }, - { - "id": "20", - "x": 345, - "y": 1300 - }, - { - "id": "31", - "x": 1105, - "y": 825 - } - ], - "relationships": [ - { - "id": "37", - "vertices": [ - { - "x": 1325, - "y": 755 - } - ], - "position": 70 - }, - { - "id": "36", - "position": 65 - }, - { - "id": "35" - }, - { - "id": "34" - }, - { - "id": "33" - }, - { - "id": "32" - }, - { - "id": "38", - "position": 55 - }, - { - "id": "39" - } - ] - } - ], - "dynamicViews": [ - { - "description": "Summarises how the sign in feature works in the single-page application.", - "key": "SignIn", - "paperSize": "A5_Landscape", - "elementId": "19", - "elements": [ - { - "id": "16", - "x": 552, - "y": 211 - }, - { - "id": "28", - "x": 1477, - "y": 211 - }, - { - "id": "30", - "x": 1477, - "y": 1116 - }, - { - "id": "20", - "x": 552, - "y": 1116 - } - ], - "relationships": [ - { - "id": "38", - "description": "select * from users where username = ?", - "order": "3", - "position": 50 - }, - { - "id": "36", - "description": "Calls isAuthenticated() on", - "order": "2", - "position": 65 - }, - { - "id": "32", - "description": "Submits credentials to", - "order": "1" - } - ] - } - ], - "deploymentViews": [ - { - "softwareSystemId": "2", - "description": "An example development deployment scenario for the Internet Banking System.", - "key": "DevelopmentDeployment", - "paperSize": "A5_Landscape", - "environment": "Development", - "elements": [ - { - "id": "44", - "x": 0, - "y": 0 - }, - { - "id": "45", - "x": 0, - "y": 0 - }, - { - "id": "46", - "x": 990, - "y": 494 - }, - { - "id": "47", - "x": 990, - "y": 834 - }, - { - "id": "48", - "x": 0, - "y": 0 - }, - { - "id": "49", - "x": 0, - "y": 0 - }, - { - "id": "50", - "x": 1840, - "y": 834 - }, - { - "id": "52", - "x": 0, - "y": 0 - }, - { - "id": "53", - "x": 140, - "y": 664 - }, - { - "id": "43", - "x": 0, - "y": 0 - } - ], - "relationships": [ - { - "id": "51", - "position": 50 - }, - { - "id": "54" - }, - { - "id": "55", - "routing": "Direct", - "position": 50 - } - ] - }, - { - "softwareSystemId": "2", - "description": "An example live deployment scenario for the Internet Banking System.", - "key": "LiveDeployment", - "paperSize": "A5_Landscape", - "environment": "Live", - "elements": [ - { - "id": "77", - "x": 0, - "y": 0 - }, - { - "id": "66", - "x": 0, - "y": 0 - }, - { - "id": "78", - "x": 0, - "y": 0 - }, - { - "id": "67", - "x": 985, - "y": 1026 - }, - { - "id": "56", - "x": 0, - "y": 0 - }, - { - "id": "79", - "x": 1820, - "y": 176 - }, - { - "id": "57", - "x": 150, - "y": 176 - }, - { - "id": "59", - "x": 0, - "y": 0 - }, - { - "id": "70", - "x": 0, - "y": 0 - }, - { - "id": "71", - "x": 0, - "y": 0 - }, - { - "id": "82", - "x": 0, - "y": 0 - }, - { - "id": "60", - "x": 0, - "y": 0 - }, - { - "id": "72", - "x": 985, - "y": 176 - }, - { - "id": "83", - "x": 0, - "y": 0 - }, - { - "id": "61", - "x": 150, - "y": 1026 - }, - { - "id": "84", - "x": 1820, - "y": 1026 - }, - { - "id": "64", - "x": 0, - "y": 0 - }, - { - "id": "65", - "x": 0, - "y": 0 - } - ], - "relationships": [ - { - "id": "80" - }, - { - "id": "73", - "routing": "Direct", - "position": 30 - }, - { - "id": "87" - }, - { - "id": "76", - "position": 40 - }, - { - "id": "85" - }, - { - "id": "68" - } - ] - } - ], - "configuration": { - "branding": {}, - "styles": { - "elements": [ - { - "tag": "Element", - "color": "#ffffff" - }, - { - "tag": "Software System", - "background": "#1168bd" - }, - { - "tag": "Container", - "background": "#438dd5" - }, - { - "tag": "Component", - "background": "#85bbf0", - "color": "#000000" - }, - { - "tag": "Person", - "background": "#08427b", - "fontSize": 22, - "shape": "Person" - }, - { - "tag": "Existing System", - "background": "#999999" - }, - { - "tag": "Bank Staff", - "background": "#999999" - }, - { - "tag": "Web Browser", - "shape": "WebBrowser" - }, - { - "tag": "Mobile App", - "shape": "MobileDeviceLandscape" - }, - { - "tag": "Database", - "shape": "Cylinder" - }, - { - "tag": "Failover", - "opacity": 25 - } - ], - "relationships": [ - { - "tag": "Failover", - "position": 70, - "opacity": 25 - } - ] - }, - "terminology": {}, - "lastSavedView": "SystemContext" - } - } -} \ No newline at end of file diff --git a/structurizr-client/test/integration/workspaces/spring-petclinic.json b/structurizr-client/test/integration/workspaces/spring-petclinic.json deleted file mode 100644 index 5563866d4..000000000 --- a/structurizr-client/test/integration/workspaces/spring-petclinic.json +++ /dev/null @@ -1,1719 +0,0 @@ -{ - "id": 1, - "name": "Spring PetClinic", - "description": "This is a C4 representation of the Spring PetClinic sample app (https://github.com/spring-projects/spring-petclinic/)", - "version": "95de1d9f8bf63560915331664b27a4a75ce1f1f6", - "model": { - "people": [ - { - "tags": "Element,Person", - "id": "2", - "name": "Clinic Employee", - "description": "An employee of the clinic", - "relationships": [ - { - "tags": "Relationship,Synchronous", - "id": "6", - "sourceId": "2", - "destinationId": "4", - "description": "Uses", - "technology": "HTTPS", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous", - "id": "3", - "sourceId": "2", - "destinationId": "1", - "description": "Uses", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous", - "id": "32", - "sourceId": "2", - "destinationId": "12", - "description": "Uses", - "technology": "HTTP", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous", - "id": "30", - "sourceId": "2", - "destinationId": "10", - "description": "Uses", - "technology": "HTTP", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous", - "id": "31", - "sourceId": "2", - "destinationId": "11", - "description": "Uses", - "technology": "HTTP", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous", - "id": "29", - "sourceId": "2", - "destinationId": "9", - "description": "Uses", - "technology": "HTTP", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous", - "id": "28", - "sourceId": "2", - "destinationId": "8", - "description": "Uses", - "technology": "HTTP", - "interactionStyle": "Synchronous" - } - ], - "location": "Unspecified" - } - ], - "softwareSystems": [ - { - "tags": "Element,Software System,Spring PetClinic", - "id": "1", - "name": "Spring PetClinic", - "description": "Allows employees to view and manage information regarding the veterinarians, the clients, and their pets.", - "location": "Unspecified", - "containers": [ - { - "tags": "Element,Container", - "id": "4", - "name": "Web Application", - "description": "Allows employees to view and manage information regarding the veterinarians, the clients, and their pets.", - "properties": { - "Deployable artifact name": "petclinic.war" - }, - "relationships": [ - { - "tags": "Relationship,Synchronous", - "id": "7", - "sourceId": "4", - "destinationId": "5", - "description": "Reads from and writes to", - "technology": "JDBC", - "interactionStyle": "Synchronous" - } - ], - "technology": "Java and Spring", - "components": [ - { - "tags": "Element,Component,Spring MVC Controller", - "id": "8", - "name": "VisitController", - "relationships": [ - { - "tags": "Relationship,Synchronous", - "id": "18", - "sourceId": "8", - "destinationId": "13", - "interactionStyle": "Synchronous" - } - ], - "technology": "Spring MVC Controller", - "size": 334, - "code": [ - { - "role": "Primary", - "name": "VisitController", - "type": "org.springframework.samples.petclinic.web.VisitController", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/web/VisitController.java", - "language": "Java", - "category": "class", - "visibility": "public", - "size": 98 - }, - { - "role": "Supporting", - "name": "Pet", - "type": "org.springframework.samples.petclinic.model.Pet", - "description": "Simple business object representing a pet.", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/model/Pet.java", - "language": "Java", - "category": "class", - "visibility": "public", - "size": 113 - }, - { - "role": "Supporting", - "name": "Visit", - "type": "org.springframework.samples.petclinic.model.Visit", - "description": "Simple JavaBean domain object representing a visit.", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/model/Visit.java", - "language": "Java", - "category": "class", - "visibility": "public", - "size": 123 - } - ] - }, - { - "tags": "Element,Component,Spring MVC Controller", - "id": "12", - "name": "PetController", - "relationships": [ - { - "tags": "Relationship,Synchronous", - "id": "21", - "sourceId": "12", - "destinationId": "13", - "interactionStyle": "Synchronous" - } - ], - "technology": "Spring MVC Controller", - "size": 441, - "code": [ - { - "role": "Primary", - "name": "PetController", - "type": "org.springframework.samples.petclinic.web.PetController", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/web/PetController.java", - "language": "Java", - "category": "class", - "visibility": "public", - "size": 112 - }, - { - "role": "Supporting", - "name": "PetValidator", - "type": "org.springframework.samples.petclinic.web.PetValidator", - "description": "Validator for Pet forms. We're not using Bean Validation annotations here because it is easier to define such validation rule in Java. ", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/web/PetValidator.java", - "language": "Java", - "category": "class", - "visibility": "public", - "size": 63 - }, - { - "role": "Supporting", - "name": "Pet", - "type": "org.springframework.samples.petclinic.model.Pet", - "description": "Simple business object representing a pet.", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/model/Pet.java", - "language": "Java", - "category": "class", - "visibility": "public", - "size": 113 - }, - { - "role": "Supporting", - "name": "Owner", - "type": "org.springframework.samples.petclinic.model.Owner", - "description": "Simple JavaBean domain object representing an owner.", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/model/Owner.java", - "language": "Java", - "category": "class", - "visibility": "public", - "size": 153 - } - ] - }, - { - "tags": "Element,Component,Spring MVC Controller", - "id": "10", - "name": "OwnerController", - "relationships": [ - { - "tags": "Relationship,Synchronous", - "id": "19", - "sourceId": "10", - "destinationId": "13", - "interactionStyle": "Synchronous" - } - ], - "technology": "Spring MVC Controller", - "size": 290, - "code": [ - { - "role": "Primary", - "name": "OwnerController", - "type": "org.springframework.samples.petclinic.web.OwnerController", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/web/OwnerController.java", - "language": "Java", - "category": "class", - "visibility": "public", - "size": 137 - }, - { - "role": "Supporting", - "name": "Owner", - "type": "org.springframework.samples.petclinic.model.Owner", - "description": "Simple JavaBean domain object representing an owner.", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/model/Owner.java", - "language": "Java", - "category": "class", - "visibility": "public", - "size": 153 - } - ] - }, - { - "tags": "Element,Component,Spring Service", - "id": "13", - "name": "ClinicService", - "description": "Mostly used as a facade so all controllers have a single point of entry", - "relationships": [ - { - "tags": "Relationship,Synchronous", - "id": "23", - "sourceId": "13", - "destinationId": "14", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous", - "id": "22", - "sourceId": "13", - "destinationId": "15", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous", - "id": "25", - "sourceId": "13", - "destinationId": "16", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous", - "id": "24", - "sourceId": "13", - "destinationId": "17", - "interactionStyle": "Synchronous" - } - ], - "technology": "Spring Service", - "size": 549, - "code": [ - { - "role": "Supporting", - "name": "Owner", - "type": "org.springframework.samples.petclinic.model.Owner", - "description": "Simple JavaBean domain object representing an owner.", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/model/Owner.java", - "language": "Java", - "category": "class", - "visibility": "public", - "size": 153 - }, - { - "role": "Supporting", - "name": "Pet", - "type": "org.springframework.samples.petclinic.model.Pet", - "description": "Simple business object representing a pet.", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/model/Pet.java", - "language": "Java", - "category": "class", - "visibility": "public", - "size": 113 - }, - { - "role": "Supporting", - "name": "ClinicServiceImpl", - "type": "org.springframework.samples.petclinic.service.ClinicServiceImpl", - "description": "Mostly used as a facade for all Petclinic controllers Also a placeholder for @Transactional and @Cacheable annotations", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/service/ClinicServiceImpl.java", - "language": "Java", - "category": "class", - "visibility": "public", - "size": 109 - }, - { - "role": "Supporting", - "name": "Visit", - "type": "org.springframework.samples.petclinic.model.Visit", - "description": "Simple JavaBean domain object representing a visit.", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/model/Visit.java", - "language": "Java", - "category": "class", - "visibility": "public", - "size": 123 - }, - { - "role": "Primary", - "name": "ClinicService", - "type": "org.springframework.samples.petclinic.service.ClinicService", - "description": "Mostly used as a facade so all controllers have a single point of entry", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/service/ClinicService.java", - "language": "Java", - "category": "interface", - "visibility": "public", - "size": 51 - } - ] - }, - { - "tags": "Element,Component,Spring Repository", - "id": "15", - "name": "PetRepository", - "description": "Repository class for Pet domain objects All method names are compliant with Spring Data naming conventions so this interface can easily be extende...", - "relationships": [ - { - "tags": "Relationship,Synchronous", - "id": "26", - "sourceId": "15", - "destinationId": "14", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous", - "id": "27", - "sourceId": "15", - "destinationId": "16", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous", - "id": "34", - "sourceId": "15", - "destinationId": "5", - "description": "Reads from and writes to", - "technology": "JDBC", - "interactionStyle": "Synchronous" - } - ], - "technology": "Spring Repository", - "size": 803, - "code": [ - { - "role": "Supporting", - "name": "BaseEntity", - "type": "org.springframework.samples.petclinic.model.BaseEntity", - "description": "Simple JavaBean domain object with an id property. Used as a base class for objects needing this property.", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/model/BaseEntity.java", - "language": "Java", - "category": "class", - "visibility": "public", - "size": 47 - }, - { - "role": "Supporting", - "name": "JdbcPet", - "type": "org.springframework.samples.petclinic.repository.jdbc.JdbcPet", - "description": "Subclass of Pet that carries temporary id properties which are only relevant for a JDBC implementation of the PetRepository.", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcPet.java", - "language": "Java", - "category": "class", - "visibility": "package", - "size": 48 - }, - { - "role": "Supporting", - "name": "JdbcPetRepositoryImpl", - "type": "org.springframework.samples.petclinic.repository.jdbc.JdbcPetRepositoryImpl", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcPetRepositoryImpl.java", - "language": "Java", - "category": "class", - "visibility": "public", - "size": 133 - }, - { - "role": "Supporting", - "name": "JdbcPetRowMapper", - "type": "org.springframework.samples.petclinic.repository.jdbc.JdbcPetRowMapper", - "description": "RowMapper implementation mapping data from a ResultSet to the corresponding properties of the JdbcPet class.", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcPetRowMapper.java", - "language": "Java", - "category": "class", - "visibility": "package", - "size": 43 - }, - { - "role": "Supporting", - "name": "PetType", - "type": "org.springframework.samples.petclinic.model.PetType", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/model/PetType.java", - "language": "Java", - "category": "class", - "visibility": "public", - "size": 29 - }, - { - "role": "Supporting", - "name": "Pet", - "type": "org.springframework.samples.petclinic.model.Pet", - "description": "Simple business object representing a pet.", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/model/Pet.java", - "language": "Java", - "category": "class", - "visibility": "public", - "size": 113 - }, - { - "role": "Supporting", - "name": "Owner", - "type": "org.springframework.samples.petclinic.model.Owner", - "description": "Simple JavaBean domain object representing an owner.", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/model/Owner.java", - "language": "Java", - "category": "class", - "visibility": "public", - "size": 153 - }, - { - "role": "Primary", - "name": "PetRepository", - "type": "org.springframework.samples.petclinic.repository.PetRepository", - "description": "Repository class for Pet domain objects All method names are compliant with Spring Data naming conventions so this interface can easily be extende...", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/repository/PetRepository.java", - "language": "Java", - "category": "interface", - "visibility": "public", - "size": 60 - }, - { - "role": "Supporting", - "name": "EntityUtils", - "type": "org.springframework.samples.petclinic.util.EntityUtils", - "description": "Utility methods for handling entities. Separate from the BaseEntity class mainly because of dependency on the ORM-associated ObjectRetrievalFailur...", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/util/EntityUtils.java", - "language": "Java", - "category": "abstract class", - "visibility": "public", - "size": 54 - }, - { - "role": "Supporting", - "name": "Visit", - "type": "org.springframework.samples.petclinic.model.Visit", - "description": "Simple JavaBean domain object representing a visit.", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/model/Visit.java", - "language": "Java", - "category": "class", - "visibility": "public", - "size": 123 - } - ] - }, - { - "tags": "Element,Component,Spring MVC Controller", - "id": "11", - "name": "VetController", - "relationships": [ - { - "tags": "Relationship,Synchronous", - "id": "20", - "sourceId": "11", - "destinationId": "13", - "interactionStyle": "Synchronous" - } - ], - "technology": "Spring MVC Controller", - "size": 109, - "code": [ - { - "role": "Primary", - "name": "VetController", - "type": "org.springframework.samples.petclinic.web.VetController", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/web/VetController.java", - "language": "Java", - "category": "class", - "visibility": "public", - "size": 66 - }, - { - "role": "Supporting", - "name": "Vets", - "type": "org.springframework.samples.petclinic.model.Vets", - "description": "Simple domain object representing a list of veterinarians. Mostly here to be used for the 'vets' org.springframework.web.servlet.view.xml.Marshalli...", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/model/Vets.java", - "language": "Java", - "category": "class", - "visibility": "public", - "size": 43 - } - ] - }, - { - "tags": "Element,Component,Spring Repository", - "id": "14", - "name": "VisitRepository", - "description": "Repository class for Visit domain objects All method names are compliant with Spring Data naming conventions so this interface can easily be exten...", - "relationships": [ - { - "tags": "Relationship,Synchronous", - "id": "33", - "sourceId": "14", - "destinationId": "5", - "description": "Reads from and writes to", - "technology": "JDBC", - "interactionStyle": "Synchronous" - } - ], - "technology": "Spring Repository", - "size": 411, - "code": [ - { - "role": "Primary", - "name": "VisitRepository", - "type": "org.springframework.samples.petclinic.repository.VisitRepository", - "description": "Repository class for Visit domain objects All method names are compliant with Spring Data naming conventions so this interface can easily be exten...", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/repository/VisitRepository.java", - "language": "Java", - "category": "interface", - "visibility": "public", - "size": 45 - }, - { - "role": "Supporting", - "name": "JdbcVisitRepositoryImpl", - "type": "org.springframework.samples.petclinic.repository.jdbc.JdbcVisitRepositoryImpl", - "description": "A simple JDBC-based implementation of the VisitRepository interface.", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcVisitRepositoryImpl.java", - "language": "Java", - "category": "class", - "visibility": "public", - "size": 88 - }, - { - "role": "Supporting", - "name": "JdbcVisitRowMapper", - "type": "org.springframework.samples.petclinic.repository.jdbc.JdbcVisitRowMapper", - "description": "RowMapper implementation mapping data from a ResultSet to the corresponding properties of the Visit class.", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcVisitRowMapper.java", - "language": "Java", - "category": "class", - "visibility": "package", - "size": 42 - }, - { - "role": "Supporting", - "name": "Pet", - "type": "org.springframework.samples.petclinic.model.Pet", - "description": "Simple business object representing a pet.", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/model/Pet.java", - "language": "Java", - "category": "class", - "visibility": "public", - "size": 113 - }, - { - "role": "Supporting", - "name": "Visit", - "type": "org.springframework.samples.petclinic.model.Visit", - "description": "Simple JavaBean domain object representing a visit.", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/model/Visit.java", - "language": "Java", - "category": "class", - "visibility": "public", - "size": 123 - } - ] - }, - { - "tags": "Element,Component,Spring Repository", - "id": "17", - "name": "VetRepository", - "description": "Repository class for Vet domain objects All method names are compliant with Spring Data naming conventions so this interface can easily be extende...", - "relationships": [ - { - "tags": "Relationship,Synchronous", - "id": "36", - "sourceId": "17", - "destinationId": "5", - "description": "Reads from and writes to", - "technology": "JDBC", - "interactionStyle": "Synchronous" - } - ], - "technology": "Spring Repository", - "size": 340, - "code": [ - { - "role": "Supporting", - "name": "Vet", - "type": "org.springframework.samples.petclinic.model.Vet", - "description": "Simple JavaBean domain object representing a veterinarian.", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/model/Vet.java", - "language": "Java", - "category": "class", - "visibility": "public", - "size": 78 - }, - { - "role": "Supporting", - "name": "JdbcVetRepositoryImpl", - "type": "org.springframework.samples.petclinic.repository.jdbc.JdbcVetRepositoryImpl", - "description": "A simple JDBC-based implementation of the VetRepository interface.", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcVetRepositoryImpl.java", - "language": "Java", - "category": "class", - "visibility": "public", - "size": 89 - }, - { - "role": "Supporting", - "name": "BaseEntity", - "type": "org.springframework.samples.petclinic.model.BaseEntity", - "description": "Simple JavaBean domain object with an id property. Used as a base class for objects needing this property.", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/model/BaseEntity.java", - "language": "Java", - "category": "class", - "visibility": "public", - "size": 47 - }, - { - "role": "Supporting", - "name": "JdbcVetRepositoryImpl$1", - "type": "org.springframework.samples.petclinic.repository.jdbc.JdbcVetRepositoryImpl$1", - "language": "Java", - "category": "class", - "visibility": "package", - "size": 0 - }, - { - "role": "Primary", - "name": "VetRepository", - "type": "org.springframework.samples.petclinic.repository.VetRepository", - "description": "Repository class for Vet domain objects All method names are compliant with Spring Data naming conventions so this interface can easily be extende...", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/repository/VetRepository.java", - "language": "Java", - "category": "interface", - "visibility": "public", - "size": 42 - }, - { - "role": "Supporting", - "name": "EntityUtils", - "type": "org.springframework.samples.petclinic.util.EntityUtils", - "description": "Utility methods for handling entities. Separate from the BaseEntity class mainly because of dependency on the ORM-associated ObjectRetrievalFailur...", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/util/EntityUtils.java", - "language": "Java", - "category": "abstract class", - "visibility": "public", - "size": 54 - }, - { - "role": "Supporting", - "name": "Specialty", - "type": "org.springframework.samples.petclinic.model.Specialty", - "description": "Models a Vet's specialty (for example, dentistry).", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/model/Specialty.java", - "language": "Java", - "category": "class", - "visibility": "public", - "size": 30 - } - ] - }, - { - "tags": "Element,Component,Spring Repository", - "id": "16", - "name": "OwnerRepository", - "description": "Repository class for Owner domain objects All method names are compliant with Spring Data naming conventions so this interface can easily be exten...", - "relationships": [ - { - "tags": "Relationship,Synchronous", - "id": "35", - "sourceId": "16", - "destinationId": "5", - "description": "Reads from and writes to", - "technology": "JDBC", - "interactionStyle": "Synchronous" - } - ], - "technology": "Spring Repository", - "size": 720, - "code": [ - { - "role": "Supporting", - "name": "JdbcPetVisitExtractor", - "type": "org.springframework.samples.petclinic.repository.jdbc.JdbcPetVisitExtractor", - "description": "ResultSetExtractor implementation by using the OneToManyResultSetExtractor of Spring Data Core JDBC Extensions.", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcPetVisitExtractor.java", - "language": "Java", - "category": "class", - "visibility": "public", - "size": 54 - }, - { - "role": "Supporting", - "name": "BaseEntity", - "type": "org.springframework.samples.petclinic.model.BaseEntity", - "description": "Simple JavaBean domain object with an id property. Used as a base class for objects needing this property.", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/model/BaseEntity.java", - "language": "Java", - "category": "class", - "visibility": "public", - "size": 47 - }, - { - "role": "Supporting", - "name": "JdbcPet", - "type": "org.springframework.samples.petclinic.repository.jdbc.JdbcPet", - "description": "Subclass of Pet that carries temporary id properties which are only relevant for a JDBC implementation of the PetRepository.", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcPet.java", - "language": "Java", - "category": "class", - "visibility": "package", - "size": 48 - }, - { - "role": "Primary", - "name": "OwnerRepository", - "type": "org.springframework.samples.petclinic.repository.OwnerRepository", - "description": "Repository class for Owner domain objects All method names are compliant with Spring Data naming conventions so this interface can easily be exten...", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/repository/OwnerRepository.java", - "language": "Java", - "category": "interface", - "visibility": "public", - "size": 64 - }, - { - "role": "Supporting", - "name": "JdbcOwnerRepositoryImpl", - "type": "org.springframework.samples.petclinic.repository.jdbc.JdbcOwnerRepositoryImpl", - "description": "A simple JDBC-based implementation of the OwnerRepository interface.", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/repository/jdbc/JdbcOwnerRepositoryImpl.java", - "language": "Java", - "category": "class", - "visibility": "public", - "size": 158 - }, - { - "role": "Supporting", - "name": "PetType", - "type": "org.springframework.samples.petclinic.model.PetType", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/model/PetType.java", - "language": "Java", - "category": "class", - "visibility": "public", - "size": 29 - }, - { - "role": "Supporting", - "name": "Owner", - "type": "org.springframework.samples.petclinic.model.Owner", - "description": "Simple JavaBean domain object representing an owner.", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/model/Owner.java", - "language": "Java", - "category": "class", - "visibility": "public", - "size": 153 - }, - { - "role": "Supporting", - "name": "Pet", - "type": "org.springframework.samples.petclinic.model.Pet", - "description": "Simple business object representing a pet.", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/model/Pet.java", - "language": "Java", - "category": "class", - "visibility": "public", - "size": 113 - }, - { - "role": "Supporting", - "name": "EntityUtils", - "type": "org.springframework.samples.petclinic.util.EntityUtils", - "description": "Utility methods for handling entities. Separate from the BaseEntity class mainly because of dependency on the ORM-associated ObjectRetrievalFailur...", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/util/EntityUtils.java", - "language": "Java", - "category": "abstract class", - "visibility": "public", - "size": 54 - } - ] - }, - { - "tags": "Element,Component,Spring MVC Controller", - "id": "9", - "name": "CrashController", - "description": "Controller used to showcase what happens when an exception is thrown", - "technology": "Spring MVC Controller", - "size": 40, - "code": [ - { - "role": "Primary", - "name": "CrashController", - "type": "org.springframework.samples.petclinic.web.CrashController", - "description": "Controller used to showcase what happens when an exception is thrown", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/java/org/springframework/samples/petclinic/web/CrashController.java", - "language": "Java", - "category": "class", - "visibility": "public", - "size": 40 - } - ] - } - ] - }, - { - "tags": "Element,Container,Database", - "id": "5", - "name": "Database", - "description": "Stores information regarding the veterinarians, the clients, and their pets.", - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/resources/db/hsqldb", - "properties": { - "Schema name": "petclinic" - }, - "technology": "Relational Database Schema" - } - ] - } - ], - "deploymentNodes": [ - { - "id": "45", - "name": "Staging Server", - "description": "A server hosted at Amazon AWS EC2", - "properties": { - "AWS instance type": "t2.medium", - "AWS region": "us-west-1" - }, - "technology": "Ubuntu 12.04 LTS", - "instances": 1, - "children": [ - { - "id": "49", - "name": "MySQL", - "description": "The staging database server.", - "technology": "MySQL 5.5.x", - "instances": 1, - "containerInstances": [ - { - "tags": "Element,Container,Database,Container Instance", - "id": "50", - "properties": { - "Schema name": "petclinic" - }, - "containerId": "5", - "instanceId": 1, - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/resources/db/hsqldb" - } - ] - }, - { - "id": "46", - "name": "Apache Tomcat", - "description": "An open source Java EE web server.", - "properties": { - "Xmx": "512M", - "Java Version": "8", - "Xms": "1024M" - }, - "technology": "Apache Tomcat 7.x", - "instances": 1, - "containerInstances": [ - { - "tags": "Element,Container,Container Instance", - "id": "47", - "properties": { - "Deployable artifact name": "petclinic.war" - }, - "relationships": [ - { - "tags": "Relationship,Synchronous", - "id": "63", - "sourceId": "47", - "destinationId": "60", - "description": "Reads from and writes to", - "technology": "JDBC", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous", - "id": "52", - "sourceId": "47", - "destinationId": "50", - "description": "Reads from and writes to", - "technology": "JDBC", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous", - "id": "48", - "sourceId": "47", - "destinationId": "43", - "description": "Reads from and writes to", - "technology": "JDBC", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous,Failover", - "id": "69", - "sourceId": "47", - "destinationId": "66", - "description": "Reads from and writes to", - "technology": "JDBC", - "interactionStyle": "Synchronous" - } - ], - "containerId": "4", - "instanceId": 1 - } - ] - } - ] - }, - { - "id": "37", - "name": "Developer Laptop", - "description": "A developer laptop.", - "technology": "Windows 7+ or macOS", - "instances": 1, - "children": [ - { - "id": "38", - "name": "Docker Container - Web Server", - "description": "A Docker container.", - "technology": "Docker", - "instances": 1, - "children": [ - { - "id": "39", - "name": "Apache Tomcat", - "description": "An open source Java EE web server.", - "properties": { - "Xmx": "256M", - "Java Version": "8", - "Xms": "512M" - }, - "technology": "Apache Tomcat 7.x", - "instances": 1, - "containerInstances": [ - { - "tags": "Element,Container,Container Instance", - "id": "40", - "properties": { - "Deployable artifact name": "petclinic.war" - }, - "relationships": [ - { - "tags": "Relationship,Synchronous,Failover", - "id": "68", - "sourceId": "40", - "destinationId": "66", - "description": "Reads from and writes to", - "technology": "JDBC", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous", - "id": "62", - "sourceId": "40", - "destinationId": "60", - "description": "Reads from and writes to", - "technology": "JDBC", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous", - "id": "51", - "sourceId": "40", - "destinationId": "50", - "description": "Reads from and writes to", - "technology": "JDBC", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous", - "id": "44", - "sourceId": "40", - "destinationId": "43", - "description": "Reads from and writes to", - "technology": "JDBC", - "interactionStyle": "Synchronous" - } - ], - "containerId": "4", - "instanceId": 0 - } - ] - } - ] - }, - { - "id": "41", - "name": "Docker Container - Database Server", - "description": "A Docker container.", - "technology": "Docker", - "instances": 1, - "children": [ - { - "id": "42", - "name": "Database Server", - "description": "A development database.", - "technology": "HSQLDB", - "instances": 1, - "containerInstances": [ - { - "tags": "Element,Container,Database,Container Instance", - "id": "43", - "properties": { - "Schema name": "petclinic" - }, - "containerId": "5", - "instanceId": 0, - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/resources/db/hsqldb" - } - ] - } - ] - } - ] - }, - { - "id": "64", - "name": "Database Server - Secondary", - "description": "A server hosted at Amazon AWS EC2.", - "properties": { - "AWS instance type": "t2.small", - "AWS region": "us-east-1" - }, - "technology": "Ubuntu 12.04 LTS", - "instances": 1, - "children": [ - { - "id": "65", - "name": "MySQL - Secondary", - "description": "A secondary database server, used for failover purposes.", - "technology": "MySQL 5.5.x", - "instances": 1, - "containerInstances": [ - { - "tags": "Element,Container,Database,Container Instance,Failover", - "id": "66", - "properties": { - "Schema name": "petclinic" - }, - "containerId": "5", - "instanceId": 3, - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/resources/db/hsqldb" - } - ] - } - ] - }, - { - "id": "53", - "name": "Web Server", - "description": "A server hosted at Amazon AWS EC2, accessed via Elastic Load Balancing.", - "properties": { - "AWS instance type": "t2.small", - "AWS region": "us-west-1" - }, - "technology": "Ubuntu 12.04 LTS", - "instances": 2, - "children": [ - { - "id": "54", - "name": "Apache Tomcat", - "description": "An open source Java EE web server.", - "properties": { - "Xmx": "512M", - "Java Version": "8", - "Xms": "1024M" - }, - "technology": "Apache Tomcat 7.x", - "instances": 1, - "containerInstances": [ - { - "tags": "Element,Container,Container Instance", - "id": "55", - "properties": { - "Deployable artifact name": "petclinic.war" - }, - "relationships": [ - { - "tags": "Relationship,Synchronous,Failover", - "id": "67", - "sourceId": "55", - "destinationId": "66", - "description": "Reads from and writes to", - "technology": "JDBC", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous", - "id": "57", - "sourceId": "55", - "destinationId": "43", - "description": "Reads from and writes to", - "technology": "JDBC", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous", - "id": "56", - "sourceId": "55", - "destinationId": "50", - "description": "Reads from and writes to", - "technology": "JDBC", - "interactionStyle": "Synchronous" - }, - { - "tags": "Relationship,Synchronous", - "id": "61", - "sourceId": "55", - "destinationId": "60", - "description": "Reads from and writes to", - "technology": "JDBC", - "interactionStyle": "Synchronous" - } - ], - "containerId": "4", - "instanceId": 2 - } - ] - } - ] - }, - { - "id": "58", - "name": "Database Server - Primary", - "description": "A server hosted at Amazon AWS EC2.", - "properties": { - "AWS instance type": "t2.medium", - "AWS region": "us-west-1" - }, - "technology": "Ubuntu 12.04 LTS", - "instances": 1, - "children": [ - { - "id": "59", - "name": "MySQL - Primary", - "description": "The primary, live database server.", - "relationships": [ - { - "tags": "Relationship,Synchronous", - "id": "70", - "sourceId": "59", - "destinationId": "65", - "description": "Replicates data to", - "interactionStyle": "Synchronous" - } - ], - "technology": "MySQL 5.5.x", - "instances": 1, - "containerInstances": [ - { - "tags": "Element,Container,Database,Container Instance", - "id": "60", - "properties": { - "Schema name": "petclinic" - }, - "containerId": "5", - "instanceId": 2, - "url": "https://github.com/spring-projects/spring-petclinic/tree/95de1d9f8bf63560915331664b27a4a75ce1f1f6/src/main/resources/db/hsqldb" - } - ] - } - ] - } - ] - }, - "documentation": { - "sections": [ - { - "elementId": "1", - "type": "Context", - "order": 1, - "format": "Markdown", - "content": "This is the context section for the Spring PetClinic System...\n![](embed:context)", - "group": "1", - "number": 1 - }, - { - "elementId": "4", - "type": "Components", - "order": 3, - "format": "Markdown", - "content": "This is the components section for the Spring PetClinic web application...\n![](embed:components)", - "group": "3", - "number": 3 - }, - { - "elementId": "1", - "type": "Deployment", - "order": 4, - "format": "Markdown", - "content": "This is the deployment section for the Spring PetClinic web application...\n### Staging environment\n![](embed:stagingDeployment)\n### Live environment\n![](embed:liveDeployment)", - "group": "4", - "number": 4 - }, - { - "elementId": "1", - "type": "Development Environment", - "order": 5, - "format": "Markdown", - "content": "This is the development environment section for the Spring PetClinic web application...\n![](embed:developmentDeployment)", - "group": "5", - "number": 5 - }, - { - "elementId": "1", - "type": "Containers", - "order": 2, - "format": "Markdown", - "content": "This is the containers section for the Spring PetClinic System...\n![](embed:containers)", - "group": "3", - "number": 2 - } - ] - }, - "views": { - "systemContextViews": [ - { - "softwareSystemId": "1", - "description": "The System Context diagram for the Spring PetClinic system.", - "key": "context", - "paperSize": "A5_Landscape", - "enterpriseBoundaryVisible": true, - "relationships": [ - { - "id": "3" - } - ], - "elements": [ - { - "id": "1", - "x": 1015, - "y": 1191 - }, - { - "id": "2", - "x": 1040, - "y": 256 - } - ] - } - ], - "containerViews": [ - { - "softwareSystemId": "1", - "description": "The Containers diagram for the Spring PetClinic system.", - "key": "containers", - "paperSize": "A5_Landscape", - "relationships": [ - { - "id": "6" - }, - { - "id": "7" - } - ], - "elements": [ - { - "id": "2", - "x": 405, - "y": 269 - }, - { - "id": "4", - "x": 1625, - "y": 319 - }, - { - "id": "5", - "x": 1625, - "y": 1058 - } - ] - } - ], - "componentViews": [ - { - "softwareSystemId": "1", - "description": "The Components diagram for the Spring PetClinic web application.", - "key": "components", - "paperSize": "A4_Portrait", - "containerId": "4", - "relationships": [ - { - "id": "21" - }, - { - "id": "32" - }, - { - "id": "20" - }, - { - "id": "31" - }, - { - "id": "30" - }, - { - "id": "18" - }, - { - "id": "29" - }, - { - "id": "28" - }, - { - "id": "27" - }, - { - "id": "26" - }, - { - "id": "25" - }, - { - "id": "36" - }, - { - "id": "24" - }, - { - "id": "35" - }, - { - "id": "23" - }, - { - "id": "34" - }, - { - "id": "22" - }, - { - "id": "33" - }, - { - "id": "19" - } - ], - "elements": [ - { - "id": "11", - "x": 40, - "y": 900 - }, - { - "id": "12", - "x": 1014, - "y": 900 - }, - { - "id": "13", - "x": 1014, - "y": 1590 - }, - { - "id": "14", - "x": 690, - "y": 2280 - }, - { - "id": "15", - "x": 1340, - "y": 2280 - }, - { - "id": "16", - "x": 1990, - "y": 2280 - }, - { - "id": "17", - "x": 40, - "y": 2280 - }, - { - "id": "2", - "x": 1039, - "y": 110 - }, - { - "id": "5", - "x": 1014, - "y": 3025 - }, - { - "id": "8", - "x": 527, - "y": 900 - }, - { - "id": "9", - "x": 1990, - "y": 900 - }, - { - "id": "10", - "x": 1501, - "y": 900 - } - ] - } - ], - "dynamicViews": [ - { - "description": "Shows how the \"view list of vets\" feature works.", - "key": "viewListOfVets", - "paperSize": "A5_Landscape", - "elementId": "4", - "relationships": [ - { - "id": "20", - "description": "Calls findVets", - "order": "2" - }, - { - "id": "24", - "description": "Calls findAll", - "order": "3" - }, - { - "id": "36", - "description": "select * from vets", - "order": "4" - }, - { - "id": "31", - "description": "Requests list of vets from /vets", - "order": "1" - } - ], - "elements": [ - { - "id": "11", - "x": 1609, - "y": 164 - }, - { - "id": "2", - "x": 446, - "y": 114 - }, - { - "id": "13", - "x": 1609, - "y": 689 - }, - { - "id": "5", - "x": 421, - "y": 1214 - }, - { - "id": "17", - "x": 1609, - "y": 1214 - } - ] - } - ], - "deploymentViews": [ - { - "softwareSystemId": "1", - "description": "An example staging deployment scenario for the Spring PetClinic software system.", - "key": "stagingDeployment", - "paperSize": "A5_Landscape", - "relationships": [ - { - "id": "52" - } - ], - "elements": [ - { - "id": "45", - "x": 0, - "y": 0 - }, - { - "id": "46", - "x": 0, - "y": 0 - }, - { - "id": "47", - "x": 1015, - "y": 261 - }, - { - "id": "49", - "x": 0, - "y": 0 - }, - { - "id": "50", - "x": 1015, - "y": 996 - } - ] - }, - { - "softwareSystemId": "1", - "description": "An example development deployment scenario for the Spring PetClinic software system.", - "key": "developmentDeployment", - "paperSize": "A5_Landscape", - "relationships": [ - { - "id": "44" - } - ], - "elements": [ - { - "id": "37", - "x": 0, - "y": 0 - }, - { - "id": "38", - "x": 0, - "y": 0 - }, - { - "id": "39", - "x": 0, - "y": 0 - }, - { - "id": "40", - "x": 1015, - "y": 216 - }, - { - "id": "41", - "x": 0, - "y": 0 - }, - { - "id": "42", - "x": 0, - "y": 0 - }, - { - "id": "43", - "x": 1015, - "y": 1011 - } - ] - }, - { - "softwareSystemId": "1", - "description": "An example live deployment scenario for the Spring PetClinic software system.", - "key": "liveDeployment", - "paperSize": "A5_Landscape", - "relationships": [ - { - "id": "70" - }, - { - "id": "61" - }, - { - "id": "67" - } - ], - "elements": [ - { - "id": "55", - "x": 1014, - "y": 221 - }, - { - "id": "66", - "x": 1742, - "y": 1046 - }, - { - "id": "58", - "x": 0, - "y": 0 - }, - { - "id": "59", - "x": 0, - "y": 0 - }, - { - "id": "60", - "x": 287, - "y": 1046 - }, - { - "id": "53", - "x": 0, - "y": 0 - }, - { - "id": "64", - "x": 0, - "y": 0 - }, - { - "id": "54", - "x": 0, - "y": 0 - }, - { - "id": "65", - "x": 0, - "y": 0 - } - ] - } - ], - "configuration": { - "branding": {}, - "styles": { - "elements": [ - { - "tag": "Spring PetClinic", - "background": "#6cb33e", - "color": "#ffffff" - }, - { - "tag": "Person", - "background": "#519823", - "color": "#ffffff", - "shape": "Person" - }, - { - "tag": "Container", - "background": "#91d366", - "color": "#ffffff" - }, - { - "tag": "Database", - "shape": "Cylinder" - }, - { - "tag": "Spring MVC Controller", - "background": "#d4f3c0", - "color": "#000000" - }, - { - "tag": "Spring Service", - "background": "#6cb33e", - "color": "#000000" - }, - { - "tag": "Spring Repository", - "background": "#95d46c", - "color": "#000000" - }, - { - "tag": "Failover", - "opacity": 25 - } - ], - "relationships": [ - { - "tag": "Failover", - "position": 70, - "opacity": 25 - } - ] - }, - "terminology": {}, - "lastSavedView": "context" - } - } -} \ No newline at end of file diff --git a/structurizr-client/test/unit/com/structurizr/util/WorkspaceUtilsTests.java b/structurizr-client/test/unit/com/structurizr/util/WorkspaceUtilsTests.java index 46932138d..0ef0bcac0 100644 --- a/structurizr-client/test/unit/com/structurizr/util/WorkspaceUtilsTests.java +++ b/structurizr-client/test/unit/com/structurizr/util/WorkspaceUtilsTests.java @@ -62,22 +62,6 @@ public void test_saveWorkspaceToJson_and_loadWorkspaceFromJson() throws Exceptio assertEquals("Name", workspace.getName()); } - @Test - public void test_loadWorkspaceFromJson_WorksWithSomeWorkspaceExamples() throws Exception { - File[] files = new File("test/integration/workspaces").listFiles(new FilenameFilter() { - @Override - public boolean accept(File dir, String name) { - return name.endsWith(".json"); - } - }); - - assertTrue(files != null && files.length > 0); - - for (File file : files) { - WorkspaceUtils.loadWorkspaceFromJson(file); - } - } - @Test public void test_toJson_ThrowsAnException_WhenANullWorkspaceIsProvided() throws Exception { try { diff --git a/structurizr-core/src/com/structurizr/Workspace.java b/structurizr-core/src/com/structurizr/Workspace.java index 9f4150235..9e3854f50 100644 --- a/structurizr-core/src/com/structurizr/Workspace.java +++ b/structurizr-core/src/com/structurizr/Workspace.java @@ -40,7 +40,7 @@ public Workspace(String name, String description) { model = createModel(); viewSet = createViewSet(); - documentation = createDocumentation(); + documentation = new Documentation(); } /** @@ -89,16 +89,6 @@ private ViewSet createViewSet() { } } - private Documentation createDocumentation() { - try { - Constructor constructor = Documentation.class.getDeclaredConstructor(Model.class); - constructor.setAccessible(true); - return (Documentation)constructor.newInstance(model); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - /** * Called when deserialising JSON, to re-create the object graph * based upon element/relationship IDs. @@ -108,13 +98,8 @@ public void hydrate() { viewSet = createViewSet(); } - if (documentation == null) { - documentation = createDocumentation(); - } - hydrateModel(); hydrateViewSet(); - hydrateDocumentation(); } private void hydrateModel() { @@ -149,22 +134,6 @@ private void hydrateViewSet() { } } - private void hydrateDocumentation() { - try { - Method hydrateMethod = Documentation.class.getDeclaredMethod("hydrate", Model.class); - hydrateMethod.setAccessible(true); - hydrateMethod.invoke(documentation, model); - } catch (InvocationTargetException ite) { - if (ite.getCause() != null && ite.getCause() instanceof WorkspaceValidationException) { - throw (WorkspaceValidationException)ite.getCause(); - } else { - throw new RuntimeException(ite.getCause()); - } - } catch (Exception e) { - throw new RuntimeException(e); - } - } - /** * Gets the documentation associated with this workspace. * diff --git a/structurizr-core/src/com/structurizr/documentation/Arc42DocumentationTemplate.java b/structurizr-core/src/com/structurizr/documentation/Arc42DocumentationTemplate.java deleted file mode 100644 index 1f6e03efc..000000000 --- a/structurizr-core/src/com/structurizr/documentation/Arc42DocumentationTemplate.java +++ /dev/null @@ -1,334 +0,0 @@ -package com.structurizr.documentation; - -import com.structurizr.Workspace; -import com.structurizr.model.SoftwareSystem; - -import java.io.File; -import java.io.IOException; - -/** - *

    - * An implementation of the arc42 documentation template, - * consisting of the following sections: - *

    - * - *
      - *
    1. Introduction and Goals (1)
    2. - *
    3. Constraints (2)
    4. - *
    5. Context and Scope (2)
    6. - *
    7. Solution Strategy (3)
    8. - *
    9. Building Block View (3)
    10. - *
    11. Runtime View (3)
    12. - *
    13. Deployment View (3)
    14. - *
    15. Crosscutting Concepts (3)
    16. - *
    17. Architectural Decisions (3)
    18. - *
    19. Quality Requirements (2)
    20. - *
    21. Risks and Technical Debt (4)
    22. - *
    23. Glossary (5)
    24. - *
    - * - *

    - * The number in parentheses () represents the grouping, which is simply used to colour code - * section navigation buttons when rendered. - *

    - */ -public class Arc42DocumentationTemplate extends DocumentationTemplate { - - public Arc42DocumentationTemplate(Workspace workspace) { - super(workspace); - } - - /** - * Adds a "Introduction and Goals" section relating to a {@link SoftwareSystem} from one or more files. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param files one or more File objects that point to the documentation content - * @return a documentation {@link Section} - * @throws IOException if there is an error reading the files - */ - public Section addIntroductionAndGoalsSection(SoftwareSystem softwareSystem, File... files) throws IOException { - return addSection(softwareSystem, "Introduction and Goals", files); - } - - /** - * Adds a "Introduction and Goals" section relating to a {@link SoftwareSystem}. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param format the {@link Format} of the documentation content - * @param content a String containing the documentation content - * @return a documentation {@link Section} - */ - public Section addIntroductionAndGoalsSection(SoftwareSystem softwareSystem, Format format, String content) { - return addSection(softwareSystem, "Introduction and Goals", format, content); - } - - /** - * Adds a "Constraints" section relating to a {@link SoftwareSystem} from one or more files. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param files one or more File objects that point to the documentation content - * @return a documentation {@link Section} - * @throws IOException if there is an error reading the files - */ - public Section addConstraintsSection(SoftwareSystem softwareSystem, File... files) throws IOException { - return addSection(softwareSystem, "Constraints", files); - } - - /** - * Adds a "Constraints" section relating to a {@link SoftwareSystem}. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param format the {@link Format} of the documentation content - * @param content a String containing the documentation content - * @return a documentation {@link Section} - */ - public Section addConstraintsSection(SoftwareSystem softwareSystem, Format format, String content) { - return addSection(softwareSystem, "Constraints", format, content); - } - - /** - * Adds a "Context and Scope" section relating to a {@link SoftwareSystem} from one or more files. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param files one or more File objects that point to the documentation content - * @return a documentation {@link Section} - * @throws IOException if there is an error reading the files - */ - public Section addContextAndScopeSection(SoftwareSystem softwareSystem, File... files) throws IOException { - return addSection(softwareSystem, "Context and Scope", files); - } - - /** - * Adds a "Context and Scope" section relating to a {@link SoftwareSystem}. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param format the {@link Format} of the documentation content - * @param content a String containing the documentation content - * @return a documentation {@link Section} - */ - public Section addContextAndScopeSection(SoftwareSystem softwareSystem, Format format, String content) { - return addSection(softwareSystem, "Context and Scope", format, content); - } - - /** - * Adds a "Solution Strategy" section relating to a {@link SoftwareSystem} from one or more files. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param files one or more File objects that point to the documentation content - * @return a documentation {@link Section} - * @throws IOException if there is an error reading the files - */ - public Section addSolutionStrategySection(SoftwareSystem softwareSystem, File... files) throws IOException { - return addSection(softwareSystem, "Solution Strategy", files); - } - - /** - * Adds a "Solution Strategy" section relating to a {@link SoftwareSystem}. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param format the {@link Format} of the documentation content - * @param content a String containing the documentation content - * @return a documentation {@link Section} - */ - public Section addSolutionStrategySection(SoftwareSystem softwareSystem, Format format, String content) { - return addSection(softwareSystem, "Solution Strategy", format, content); - } - - /** - * Adds a "Building Block View" section relating to a {@link SoftwareSystem} from one or more files. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param files one or more File objects that point to the documentation content - * @return a documentation {@link Section} - * @throws IOException if there is an error reading the files - */ - public Section addBuildingBlockViewSection(SoftwareSystem softwareSystem, File... files) throws IOException { - return addSection(softwareSystem, "Building Block View", files); - } - - /** - * Adds a "Building Block View" section relating to a {@link SoftwareSystem}. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param format the {@link Format} of the documentation content - * @param content a String containing the documentation content - * @return a documentation {@link Section} - */ - public Section addBuildingBlockViewSection(SoftwareSystem softwareSystem, Format format, String content) { - return addSection(softwareSystem, "Building Block View", format, content); - } - - /** - * Adds a "Runtime View" section relating to a {@link SoftwareSystem} from one or more files. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param files one or more File objects that point to the documentation content - * @return a documentation {@link Section} - * @throws IOException if there is an error reading the files - */ - public Section addRuntimeViewSection(SoftwareSystem softwareSystem, File... files) throws IOException { - return addSection(softwareSystem, "Runtime View", files); - } - - /** - * Adds a "Runtime View" section relating to a {@link SoftwareSystem}. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param format the {@link Format} of the documentation content - * @param content a String containing the documentation content - * @return a documentation {@link Section} - */ - public Section addRuntimeViewSection(SoftwareSystem softwareSystem, Format format, String content) { - return addSection(softwareSystem, "Runtime View", format, content); - } - - /** - * Adds a "Deployment View" section relating to a {@link SoftwareSystem} from one or more files. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param files one or more File objects that point to the documentation content - * @return a documentation {@link Section} - * @throws IOException if there is an error reading the files - */ - public Section addDeploymentViewSection(SoftwareSystem softwareSystem, File... files) throws IOException { - return addSection(softwareSystem, "Deployment View", files); - } - - /** - * Adds a "Deployment View" section relating to a {@link SoftwareSystem}. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param format the {@link Format} of the documentation content - * @param content a String containing the documentation content - * @return a documentation {@link Section} - */ - public Section addDeploymentViewSection(SoftwareSystem softwareSystem, Format format, String content) { - return addSection(softwareSystem, "Deployment View", format, content); - } - - /** - * Adds a "Crosscutting Concepts" section relating to a {@link SoftwareSystem} from one or more files. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param files one or more File objects that point to the documentation content - * @return a documentation {@link Section} - * @throws IOException if there is an error reading the files - */ - public Section addCrosscuttingConceptsSection(SoftwareSystem softwareSystem, File... files) throws IOException { - return addSection(softwareSystem, "Crosscutting Concepts", files); - } - - /** - * Adds a "Crosscutting Concepts" section relating to a {@link SoftwareSystem}. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param format the {@link Format} of the documentation content - * @param content a String containing the documentation content - * @return a documentation {@link Section} - */ - public Section addCrosscuttingConceptsSection(SoftwareSystem softwareSystem, Format format, String content) { - return addSection(softwareSystem, "Crosscutting Concepts", format, content); - } - - /** - * Adds an "Architectural Decisions" section relating to a {@link SoftwareSystem} from one or more files. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param files one or more File objects that point to the documentation content - * @return a documentation {@link Section} - * @throws IOException if there is an error reading the files - */ - public Section addArchitecturalDecisionsSection(SoftwareSystem softwareSystem, File... files) throws IOException { - return addSection(softwareSystem, "Architectural Decisions", files); - } - - /** - * Adds an "Architectural Decisions" section relating to a {@link SoftwareSystem}. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param format the {@link Format} of the documentation content - * @param content a String containing the documentation content - * @return a documentation {@link Section} - */ - public Section addArchitecturalDecisionsSection(SoftwareSystem softwareSystem, Format format, String content) { - return addSection(softwareSystem, "Architectural Decisions", format, content); - } - - /** - * Adds a "Quality Requirements" section relating to a {@link SoftwareSystem} from one or more files. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param files one or more File objects that point to the documentation content - * @return a documentation {@link Section} - * @throws IOException if there is an error reading the files - */ - public Section addQualityRequirementsSection(SoftwareSystem softwareSystem, File... files) throws IOException { - return addSection(softwareSystem, "Quality Requirements", files); - } - - /** - * Adds a "Quality Requirements" section relating to a {@link SoftwareSystem}. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param format the {@link Format} of the documentation content - * @param content a String containing the documentation content - * @return a documentation {@link Section} - */ - public Section addQualityRequirementsSection(SoftwareSystem softwareSystem, Format format, String content) { - return addSection(softwareSystem, "Quality Requirements", format, content); - } - - /** - * Adds a "Risks and Technical Debt" section relating to a {@link SoftwareSystem} from one or more files. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param files one or more File objects that point to the documentation content - * @return a documentation {@link Section} - * @throws IOException if there is an error reading the files - */ - public Section addRisksAndTechnicalDebtSection(SoftwareSystem softwareSystem, File... files) throws IOException { - return addSection(softwareSystem, "Risks and Technical Debt", files); - } - - /** - * Adds a "Risks and Technical Debt" section relating to a {@link SoftwareSystem}. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param format the {@link Format} of the documentation content - * @param content a String containing the documentation content - * @return a documentation {@link Section} - */ - public Section addRisksAndTechnicalDebtSection(SoftwareSystem softwareSystem, Format format, String content) { - return addSection(softwareSystem, "Risks and Technical Debt", format, content); - } - - /** - * Adds a "Glossary" section relating to a {@link SoftwareSystem} from one or more files. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param files one or more File objects that point to the documentation content - * @return a documentation {@link Section} - * @throws IOException if there is an error reading the files - */ - public Section addGlossarySection(SoftwareSystem softwareSystem, File... files) throws IOException { - return addSection(softwareSystem, "Glossary", files); - } - - /** - * Adds a "Glossary" section relating to a {@link SoftwareSystem}. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param format the {@link Format} of the documentation content - * @param content a String containing the documentation content - * @return a documentation {@link Section} - */ - public Section addGlossarySection(SoftwareSystem softwareSystem, Format format, String content) { - return addSection(softwareSystem, "Glossary", format, content); - } - - @Override - protected TemplateMetadata getMetadata() { - return new TemplateMetadata("arc42", "Dr. Gernot Starke and Dr. Peter Hruschka", "http://arc42.org"); - } - -} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/documentation/AutomaticDocumentationTemplate.java b/structurizr-core/src/com/structurizr/documentation/AutomaticDocumentationTemplate.java deleted file mode 100644 index 529890fe1..000000000 --- a/structurizr-core/src/com/structurizr/documentation/AutomaticDocumentationTemplate.java +++ /dev/null @@ -1,127 +0,0 @@ -package com.structurizr.documentation; - -import com.structurizr.Workspace; -import com.structurizr.model.SoftwareSystem; - -import java.io.File; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * This template allows you to scan a given directory and automatically add all Markdown or AsciiDoc - * files in that directory. Each file must represent a separate section, and the second level heading - * ("## Section Title" in Markdown and "== Section Title" in AsciiDoc) will be used as the section name. - */ -public class AutomaticDocumentationTemplate extends DocumentationTemplate { - - private boolean recursive = false; - - public AutomaticDocumentationTemplate(Workspace workspace) { - super(workspace); - } - - /** - * Determines whether this template will scan directories recursively. - * - * @return true if this template will scan directories recursively, false otherwise - */ - public boolean isRecursive() { - return recursive; - } - - /** - * Sets whether this template will scan directories recursively. - * - * @param recursive true if this template should scan directories recursively, false otherwise - */ - public void setRecursive(boolean recursive) { - this.recursive = recursive; - } - - /** - * Adds all files in the specified directory, each in its own section. - * - * @param directory the directory to scan - * @return a List of Section objects - * @throws IOException if there is an error reading the files in the directory - */ - public List
    addSections(File directory) throws IOException { - return add(null, directory); - } - - /** - * Adds all files in the specified directory, each in its own section, related to a software system. - * - * @param directory the directory to scan - * @param softwareSystem the SoftwareSystem to associate the documentation with - * @return a List of Section objects - * @throws IOException if there is an error reading the files in the directory - */ - public List
    addSections(SoftwareSystem softwareSystem, File directory) throws IOException { - if (softwareSystem == null) { - throw new IllegalArgumentException("A software system must be specified."); - } - - return add(softwareSystem, directory); - } - - private List
    add(SoftwareSystem softwareSystem, File directory) throws IOException { - List
    sections = new ArrayList<>(); - - if (!directory.exists()) { - throw new IllegalArgumentException(directory.getCanonicalPath() + " does not exist."); - } - - if (!directory.isDirectory()) { - throw new IllegalArgumentException(directory.getCanonicalPath() + " is not a directory."); - } - - File[] filesInDirectory = directory.listFiles(); - if (filesInDirectory != null) { - Arrays.sort(filesInDirectory); - - for (File file : filesInDirectory) { - if (!file.isDirectory() && !file.getName().startsWith(".")) { - if (FormatFinder.isMarkdownOrAsciiDoc(file)) { - Format format = FormatFinder.findFormat(file); - String sectionDefinition = ""; - - if (format == Format.Markdown) { - sectionDefinition = "##"; - } else if (format == Format.AsciiDoc) { - sectionDefinition = "=="; - } - - String content = new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8); - String sectionName = file.getName(); - Matcher matcher = Pattern.compile("^" + sectionDefinition + " (.*?)$", Pattern.MULTILINE).matcher(content); - if (matcher.find()) { - sectionName = matcher.group(1); - } - - Section section = addSection(softwareSystem, sectionName, format, content); - sections.add(section); - } - } else if (file.isDirectory()) { - if (isRecursive()) { - sections.addAll(add(softwareSystem, file)); - } - } - } - } - - return sections; - } - - @Override - protected TemplateMetadata getMetadata() { - return null; - } - -} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/documentation/Decision.java b/structurizr-core/src/com/structurizr/documentation/Decision.java index 2209a56c3..056926a91 100644 --- a/structurizr-core/src/com/structurizr/documentation/Decision.java +++ b/structurizr-core/src/com/structurizr/documentation/Decision.java @@ -4,49 +4,35 @@ import com.structurizr.model.Element; import java.util.Date; +import java.util.HashSet; +import java.util.Set; /** * Represents a single (architecture) decision, as described at http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions */ public final class Decision { - private Element element; + // elementId is here for backwards compatibility private String elementId; + private String id; private Date date; private String title; - private DecisionStatus status; + private String status; private String content; private Format format; + private Set links = new HashSet<>(); + Decision() { } - Decision(Element element, String id, Date date, String title, DecisionStatus status, Format format, String content) { - this.element = element; + public Decision(String id) { this.id = id; - this.date = date; - this.title = title; - this.status = status; - this.format = format; - this.content = content; - } - - @JsonIgnore - public Element getElement() { - return element; - } - - void setElement(Element element) { - this.element = element; } public String getElementId() { - if (this.element != null) { - return this.element.getId(); - } else { - return elementId; - } + return elementId; } void setElementId(String elementId) { @@ -65,7 +51,7 @@ public Date getDate() { return date; } - void setDate(Date date) { + public void setDate(Date date) { this.date = date; } @@ -73,15 +59,15 @@ public String getTitle() { return title; } - void setTitle(String title) { + public void setTitle(String title) { this.title = title; } - public DecisionStatus getStatus() { + public String getStatus() { return status; } - void setStatus(DecisionStatus status) { + public void setStatus(String status) { this.status = status; } @@ -97,8 +83,26 @@ public Format getFormat() { return format; } - void setFormat(Format format) { + public void setFormat(Format format) { this.format = format; } + public Set getLinks() { + return new HashSet<>(links); + } + + void setLinks(Set links) { + this.links = links; + } + + public void addLink(Decision decision, String type) { + if (!decision.getId().equals(this.getId())) { + links.add(new Link(decision.getId(), type)); + } + } + + public boolean hasLinkTo(Decision decision) { + return links.stream().anyMatch(l -> l.getId().equals(decision.getId())); + } + } \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/documentation/DecisionStatus.java b/structurizr-core/src/com/structurizr/documentation/DecisionStatus.java deleted file mode 100644 index 82a483513..000000000 --- a/structurizr-core/src/com/structurizr/documentation/DecisionStatus.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.structurizr.documentation; - -/** - * Represents the status of a decision. - */ -public enum DecisionStatus { - - Proposed, - Accepted, - Superseded, - Deprecated, - Rejected; - -} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/documentation/Documentation.java b/structurizr-core/src/com/structurizr/documentation/Documentation.java index 082e49fe5..0155113f5 100644 --- a/structurizr-core/src/com/structurizr/documentation/Documentation.java +++ b/structurizr-core/src/com/structurizr/documentation/Documentation.java @@ -1,19 +1,14 @@ package com.structurizr.documentation; import com.fasterxml.jackson.annotation.JsonIgnore; -import com.structurizr.WorkspaceValidationException; -import com.structurizr.model.Element; -import com.structurizr.model.Model; -import com.structurizr.model.SoftwareSystem; import com.structurizr.util.StringUtils; -import javax.annotation.Nonnull; -import java.util.Date; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.Set; /** - * Represents the documentation within a workspace - a collection of + * Represents the documentation within a workspace or software system - a collection of * content in Markdown or AsciiDoc format, optionally with attached images. * * See Documentation @@ -21,33 +16,26 @@ */ public final class Documentation { - private Model model; private Set
    sections = new HashSet<>(); private Set decisions = new HashSet<>(); private Set images = new HashSet<>(); - private TemplateMetadata template; - Documentation() { + public Documentation() { } - Documentation(@Nonnull Model model) { - this.model = model; - } - - @Nonnull - final Section addSection(Element element, String title, Format format, String content) { - if (element != null && !model.contains(element)) { - throw new IllegalArgumentException("The element named " + element.getName() + " does not exist in the model associated with this documentation."); - } - - checkTitleIsSpecified(title); - checkContentIsSpecified(content); - checkSectionIsUnique(element, title); - checkFormatIsSpecified(format); + /** + * Adds a section to this documentation. + * + * @param section a Section object + */ + public void addSection(Section section) { + checkTitleIsSpecified(section.getTitle()); + checkContentIsSpecified(section.getContent()); + checkSectionIsUnique(section.getTitle()); + checkFormatIsSpecified(section.getFormat()); - Section section = new Section(element, title, calculateOrder(), format, content); + section.setOrder(calculateOrder()); sections.add(section); - return section; } private void checkTitleIsSpecified(String title) { @@ -68,18 +56,10 @@ private void checkFormatIsSpecified(Format format) { } } - private void checkSectionIsUnique(Element element, String title) { - if (element == null) { - for (Section section : sections) { - if (section.getElement() == null && title.equals(section.getTitle())) { - throw new IllegalArgumentException("A section with a title of " + title + " already exists for this workspace."); - } - } - } else { - for (Section section : sections) { - if (element.getId().equals(section.getElementId()) && title.equals(section.getTitle())) { - throw new IllegalArgumentException("A section with a title of " + title + " already exists for the element named " + element.getName() + "."); - } + private void checkSectionIsUnique(String title) { + for (Section section : sections) { + if (title.equals(section.getTitle())) { + throw new IllegalArgumentException("A section with a title of " + title + " already exists in this scope."); } } } @@ -99,7 +79,7 @@ public Set
    getSections() { void setSections(Set
    sections) { if (sections != null) { - this.sections = new HashSet<>(sections); + this.sections = new LinkedHashSet<>(sections); } } @@ -119,44 +99,19 @@ void setDecisions(Set decisions) { } /** - * Adds a new decision to this workspace. + * Adds a new decision to this documentation. * - * @param id the ID of the decision - * @param date the date of the decision - * @param title the title of the decision - * @param status the status of the decision - * @param format the format of the decision content - * @param content the content of the decision - * @return a Decision object + * @param decision the Decision object */ - public Decision addDecision(String id, Date date, String title, DecisionStatus status, Format format, String content) { - return addDecision(null, id, date, title, status, format, content); - } + public void addDecision(Decision decision) { + checkIdIsSpecified(decision.getId()); + checkTitleIsSpecified(decision.getTitle()); + checkContentIsSpecified(decision.getContent()); + checkDecisionStatusIsSpecified(decision.getStatus()); + checkFormatIsSpecified(decision.getFormat()); + checkDecisionIsUnique(decision.getId()); - /** - * Adds a new decision to this workspace. - * - * @param softwareSystem the SoftwareSystem to associate the decision with - * @param id the ID of the decision - * @param date the date of the decision - * @param title the title of the decision - * @param status the status of the decision - * @param format the format of the decision content - * @param content the content of the decision - * @return a Decision object - */ - public Decision addDecision(SoftwareSystem softwareSystem, String id, Date date, String title, DecisionStatus status, Format format, String content) { - checkIdIsSpecified(id); - checkTitleIsSpecified(title); - checkContentIsSpecified(content); - checkDecisionStatusIsSpecified(status); - checkFormatIsSpecified(format); - checkDecisionIsUnique(softwareSystem, id); - - Decision decision = new Decision(softwareSystem, id, date, title, status, format, content); this.decisions.add(decision); - - return decision; } private void checkIdIsSpecified(String id) { @@ -165,29 +120,26 @@ private void checkIdIsSpecified(String id) { } } - private void checkDecisionStatusIsSpecified(DecisionStatus status) { + private void checkDecisionStatusIsSpecified(String status) { if (status == null) { throw new IllegalArgumentException("A status must be specified."); } } - private void checkDecisionIsUnique(Element element, String id) { - if (element == null) { - for (Decision decision : decisions) { - if (decision.getElement() == null && id.equals(decision.getId())) { - throw new IllegalArgumentException("A decision with an ID of " + id + " already exists for this workspace."); - } - } - } else { - for (Decision decision : decisions) { - if (element.getId().equals(decision.getElementId()) && id.equals(decision.getId())) { - throw new IllegalArgumentException("A decision with an ID of " + id + " already exists for the element named " + element.getName() + "."); - } + private void checkDecisionIsUnique(String id) { + for (Decision decision : decisions) { + if (id.equals(decision.getId())) { + throw new IllegalArgumentException("A decision with an ID of " + id + " already exists in this scope."); } } } - void addImage(Image image) { + /** + * Adds an image to the documentation. + * + * @param image an Image object + */ + public void addImage(Image image) { images.add(image); } @@ -206,40 +158,6 @@ void setImages(Set images) { } } - void hydrate(Model model) { - this.model = model; - - for (Section section : sections) { - if (!StringUtils.isNullOrEmpty(section.getElementId())) { - Element element = model.getElement(section.getElementId()); - - if (element == null) { - throw new WorkspaceValidationException( - String.format("The documentation section with title \"%s\" is associated with an element (id=%s), but that element does not exist in the model.", - section.getTitle(), section.getElementId()) - ); - } - - section.setElement(element); - } - } - - for (Decision decision : decisions) { - if (!StringUtils.isNullOrEmpty(decision.getElementId())) { - Element element = model.getElement(decision.getElementId()); - - if (element == null) { - throw new WorkspaceValidationException( - String.format("The decision record with title \"%s\" is associated with an element (id=%s), but that element does not exist in the model.", - decision.getTitle(), decision.getElementId()) - ); - } - - decision.setElement(element); - } - } - } - @JsonIgnore public boolean isEmpty() { return sections.isEmpty() && images.isEmpty() && decisions.isEmpty(); @@ -252,20 +170,6 @@ public void clear() { sections = new HashSet<>(); decisions = new HashSet<>(); images = new HashSet<>(); - template = null; - } - - /** - * Gets the template metadata associated with this documentation. - * - * @return a TemplateMetadata object, or null if there is none - */ - public TemplateMetadata getTemplate() { - return template; - } - - void setTemplate(TemplateMetadata template) { - this.template = template; } } \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/documentation/DocumentationTemplate.java b/structurizr-core/src/com/structurizr/documentation/DocumentationTemplate.java deleted file mode 100644 index 8e215a139..000000000 --- a/structurizr-core/src/com/structurizr/documentation/DocumentationTemplate.java +++ /dev/null @@ -1,257 +0,0 @@ -package com.structurizr.documentation; - -import com.structurizr.Workspace; -import com.structurizr.model.Component; -import com.structurizr.model.Container; -import com.structurizr.model.Element; -import com.structurizr.model.SoftwareSystem; -import com.structurizr.util.ImageUtils; - -import javax.annotation.Nonnull; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; - -/** - * The superclass for all documentation templates. - */ -public abstract class DocumentationTemplate { - - private Documentation documentation; - - /** - * Creates a new documentation template for the given workspace. - * - * @param workspace the Workspace instance to create documentation for - */ - public DocumentationTemplate(@Nonnull Workspace workspace) { - if (workspace == null) { - throw new IllegalArgumentException("A workspace must be specified."); - } - - this.documentation = workspace.getDocumentation(); - documentation.setTemplate(getMetadata()); - } - - /** - * Adds a custom section from one or more files, that isn't related to any element in the model. - * - * @param title the section title - * @param files one or more File objects that point to the documentation content - * @return a documentation {@link Section} - * @throws IOException if there is an error reading the files - */ - public Section addSection(String title, File... files) throws IOException { - return add(null, title, files); - } - - /** - * Adds a custom section, that isn't related to any element in the model. - * - * @param title the section title - * @param format the {@link Format} of the documentation content - * @param content a String containing the documentation content - * @return a documentation {@link Section} - */ - @Nonnull - public Section addSection(String title, Format format, String content) { - return add(null, title, format, content); - } - - /** - * Adds a section relating to a {@link SoftwareSystem} from one or more files. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param title the section title - * @param files one or more File objects that point to the documentation content - * @return a documentation {@link Section} - * @throws IOException if there is an error reading the files - */ - @Nonnull - public Section addSection(SoftwareSystem softwareSystem, String title, File... files) throws IOException { - return add(softwareSystem, title, files); - } - - /** - * Adds a section relating to a {@link SoftwareSystem}. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param title the section title - * @param format the {@link Format} of the documentation content - * @param content a String containing the documentation content - * @return a documentation {@link Section} - */ - public Section addSection(SoftwareSystem softwareSystem, String title, Format format, String content) { - return add(softwareSystem, title, format, content); - } - - /** - * Adds a section relating to a {@link Container} from one or more files. - * - * @param container the {@link Container} the documentation content relates to - * @param title the section title - * @param files one or more File objects that point to the documentation content - * @return a documentation {@link Section} - * @throws IOException if there is an error reading the files - */ - public Section addSection(Container container, String title, File... files) throws IOException { - return add(container, title, files); - } - - /** - * Adds a section relating to a {@link Container}. - * - * @param container the {@link Container} the documentation content relates to - * @param title the section title - * @param format the {@link Format} of the documentation content - * @param content a String containing the documentation content - * @return a documentation {@link Section} - */ - public Section addSection(Container container, String title, Format format, String content) { - return add(container, title, format, content); - } - - /** - * Adds a section relating to a {@link Component} from one or more files. - * - * @param component the {@link Component} the documentation content relates to - * @param title the section title - * @param files one or more File objects that point to the documentation content - * @return a documentation {@link Section} - * @throws IOException if there is an error reading the files - */ - public Section addSection(Component component, String title, File... files) throws IOException { - return add(component, title, files); - } - - /** - * Adds a section relating to a {@link Component}. - * - * @param component the {@link Component} the documentation content relates to - * @param title the section title - * @param format the {@link Format} of the documentation content - * @param content a String containing the documentation content - * @return a documentation {@link Section} - */ - public Section addSection(Component component, String title, Format format, String content) { - return add(component, title, format, content); - } - - private Section add(Element element, @Nonnull String title, @Nonnull Format format, @Nonnull String content) { - return documentation.addSection(element, title, format, content); - } - - @Nonnull - private Section add(Element element, @Nonnull String title, File... files) throws IOException { - FormattedContent content = readFiles(files); - return documentation.addSection(element, title, content.getFormat(), content.getContent()); - } - - private FormattedContent readFiles(File... files) throws IOException { - if (files == null || files.length == 0) { - throw new IllegalArgumentException("One or more files must be specified."); - } - - Format format = Format.Markdown; - StringBuilder content = new StringBuilder(); - for (File file : files) { - if (file == null) { - throw new IllegalArgumentException("One or more files must be specified."); - } - - if (!file.exists()) { - throw new IllegalArgumentException(file.getCanonicalPath() + " does not exist."); - } - - if (content.length() > 0) { - content.append(System.lineSeparator()); - } - - if (file.isFile()) { - format = FormatFinder.findFormat(file); - content.append(new String(Files.readAllBytes(file.toPath()), "UTF-8")); - } else if (file.isDirectory()) { - File[] filesInDirectory = file.listFiles(); - if (filesInDirectory != null) { - Arrays.sort(filesInDirectory); - content.append(readFiles(filesInDirectory).getContent()); - } - } - } - - return new FormattedContent(content.toString(), format); - } - - /** - * Adds png/jpg/jpeg/gif images in the given directory to the workspace. - * - * @param path a File descriptor representing a directory on disk - * @return a Collection of Image objects - * @throws IOException if the path can't be accessed - */ - public Collection addImages(File path) throws IOException { - if (path == null) { - throw new IllegalArgumentException("Directory path must not be null."); - } else if (!path.exists()) { - throw new IllegalArgumentException("The directory " + path.getCanonicalPath() + " does not exist."); - } else if (!path.isDirectory()) { - throw new IllegalArgumentException(path.getCanonicalPath() + " is not a directory."); - } - - return addImagesFromPath("", path); - } - - private Collection addImagesFromPath(String root, File path) throws IOException { - Collection images = new HashSet<>(); - - File[] files = path.listFiles(); - if (files != null) { - for (File file : files) { - String name = file.getName().toLowerCase(); - if (file.isDirectory()) { - images.addAll(addImagesFromPath(file.getName() + "/", file)); - } else { - if (name.endsWith(".png") || name.endsWith(".jpg") || name.endsWith(".jpeg") || name.endsWith(".gif")) { - Image image = addImage(file); - - if (!root.isEmpty()) { - image.setName(root + image.getName()); - } - - images.add(image); - } - } - } - } - - return images; - } - - /** - * Adds an image from the given file to the workspace. - * - * @param file a File descriptor representing an image file on disk - * @return an Image object representing the image added - * @throws IOException if there is an error reading the image - */ - public Image addImage(File file) throws IOException { - String contentType = ImageUtils.getContentType(file); - String base64Content = ImageUtils.getImageAsBase64(file); - - Image image = new Image(file.getName(), contentType, base64Content); - documentation.addImage(image); - - return image; - } - - /** - * Gets the metadata associated with this template. - * - * @return a TemplateMetadata object, or null if there is none - */ - protected abstract TemplateMetadata getMetadata(); - -} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/documentation/FormatFinder.java b/structurizr-core/src/com/structurizr/documentation/FormatFinder.java deleted file mode 100644 index 35a2ef2bc..000000000 --- a/structurizr-core/src/com/structurizr/documentation/FormatFinder.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.structurizr.documentation; - -import java.io.File; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -class FormatFinder { - - private static Set MARKDOWN_EXTENSIONS = new HashSet<>(Arrays.asList(".md", ".markdown", ".text")); - - private static Set ASCIIDOC_EXTENSIONS = new HashSet<>(Arrays.asList(".asciidoc", ".adoc", ".asc")); - - static boolean isMarkdownOrAsciiDoc(File file) { - String extension = file.getName().substring(file.getName().lastIndexOf(".")); - - return MARKDOWN_EXTENSIONS.contains(extension) || ASCIIDOC_EXTENSIONS.contains(extension); - } - - static Format findFormat(File file) { - if (file == null) { - throw new IllegalArgumentException("A file must be specified."); - } - - String extension = file.getName().substring(file.getName().lastIndexOf(".")); - if (MARKDOWN_EXTENSIONS.contains(extension)) { - return Format.Markdown; - } else if (ASCIIDOC_EXTENSIONS.contains(extension)) { - return Format.AsciiDoc; - } else { - // just assume Markdown - return Format.Markdown; - } - - } - -} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/documentation/Link.java b/structurizr-core/src/com/structurizr/documentation/Link.java new file mode 100644 index 000000000..10c3ad396 --- /dev/null +++ b/structurizr-core/src/com/structurizr/documentation/Link.java @@ -0,0 +1,52 @@ +package com.structurizr.documentation; + +import com.structurizr.util.StringUtils; + +import java.util.Objects; + +public final class Link { + + private String type; + private String id; + + public Link() { + } + + Link(String id, String type) { + if (StringUtils.isNullOrEmpty(id)) { + throw new IllegalArgumentException("Link ID must be specfied"); + } + this.id = id; + this.type = type; + } + + public String getId() { + return id; + } + + void setId(String id) { + this.id = id; + } + + public String getType() { + return type; + } + + void setType(String type) { + this.type = type; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Link link = (Link) o; + return Objects.equals(type, link.type) && id.equals(link.id); + } + + @Override + public int hashCode() { + return Objects.hash(type, id); + } + +} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/documentation/Section.java b/structurizr-core/src/com/structurizr/documentation/Section.java index d1dd9207b..fe57983d3 100644 --- a/structurizr-core/src/com/structurizr/documentation/Section.java +++ b/structurizr-core/src/com/structurizr/documentation/Section.java @@ -1,18 +1,13 @@ package com.structurizr.documentation; -import com.fasterxml.jackson.annotation.JsonGetter; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.structurizr.model.Element; - /** * A documentation section. */ public final class Section { - private Element element; + // elementId is here for backwards compatibility private String elementId; - private String type; private String title; private int order; private Format format; @@ -21,29 +16,14 @@ public final class Section { Section() { } - Section(Element element, String title, int order, Format format, String content) { - this.element = element; + Section(String title, Format format, String content) { this.title = title; - this.order = order; this.format = format; this.content = content; } - @JsonIgnore - public Element getElement() { - return element; - } - - void setElement(Element element) { - this.element = element; - } - public String getElementId() { - if (this.element != null) { - return this.element.getId(); - } else { - return elementId; - } + return elementId; } void setElementId(String elementId) { @@ -58,21 +38,11 @@ void setTitle(String title) { this.title = title; } - @JsonGetter - String getType() { - return this.type; - } - - void setType(String type) { - this.type = type; - setTitle(type); // backwards compatibility for older clients - } - public int getOrder() { return order; } - public void setOrder(int order) { + void setOrder(int order) { this.order = order; } diff --git a/structurizr-core/src/com/structurizr/documentation/StructurizrDocumentationTemplate.java b/structurizr-core/src/com/structurizr/documentation/StructurizrDocumentationTemplate.java deleted file mode 100644 index ad4f36a06..000000000 --- a/structurizr-core/src/com/structurizr/documentation/StructurizrDocumentationTemplate.java +++ /dev/null @@ -1,444 +0,0 @@ -package com.structurizr.documentation; - -import com.structurizr.Workspace; -import com.structurizr.model.Component; -import com.structurizr.model.Container; -import com.structurizr.model.SoftwareSystem; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import java.io.File; -import java.io.IOException; - -/** - *

    - * A simple documentation template, based upon the "software guidebook" concept in Simon Brown's - * Software Architecture for Developers - * book, with the following sections: - *

    - * - *
      - *
    • Context (1)
    • - *
    • Functional Overview (2)
    • - *
    • Quality Attributes (2)
    • - *
    • Constraints (2)
    • - *
    • Principles (2)
    • - *
    • Software Architecture (3)
    • - *
    • Containers (3)
    • - *
    • Components (3)
    • - *
    • Code (3)
    • - *
    • Data (3)
    • - *
    • Infrastructure Architecture (4)
    • - *
    • Deployment (4)
    • - *
    • Development Environment (4)
    • - *
    • Operation and Support (4)
    • - *
    • Decision Log (5)
    • - *
    - * - *

    - * The number in parentheses () represents the grouping, which is simply used to colour code - * section navigation buttons when rendered. - *

    - */ -public class StructurizrDocumentationTemplate extends DocumentationTemplate { - - public StructurizrDocumentationTemplate(Workspace workspace) { - super(workspace); - } - - /** - * Adds a "Context" section relating to a {@link SoftwareSystem} from one or more files. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to, or null if it relates to the whole workspace - * @param files one or more File objects that point to the documentation content - * @return a documentation {@link Section} - * @throws IOException if there is an error reading the files - */ - @Nonnull - public Section addContextSection(@Nullable SoftwareSystem softwareSystem, File... files) throws IOException { - return addSection(softwareSystem, "Context", files); - } - - /** - * Adds a "Context" section relating to a {@link SoftwareSystem}. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param format the {@link Format} of the documentation content - * @param content a String containing the documentation content - * @return a documentation {@link Section} - */ - @Nonnull - public Section addContextSection(@Nullable SoftwareSystem softwareSystem, @Nonnull Format format, @Nonnull String content) { - return addSection(softwareSystem, "Context", format, content); - } - - /** - * Adds a "Functional Overview" section relating to a {@link SoftwareSystem} from one or more files. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param files one or more File objects that point to the documentation content - * @return a documentation {@link Section} - * @throws IOException if there is an error reading the files - */ - @Nonnull - public Section addFunctionalOverviewSection(@Nullable SoftwareSystem softwareSystem, File... files) throws IOException { - return addSection(softwareSystem, "Functional Overview", files); - } - - /** - * Adds a "Functional Overview" section relating to a {@link SoftwareSystem}. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param format the {@link Format} of the documentation content - * @param content a String containing the documentation content - * @return a documentation {@link Section} - */ - @Nonnull - public Section addFunctionalOverviewSection(@Nullable SoftwareSystem softwareSystem, @Nonnull Format format, @Nonnull String content) { - return addSection(softwareSystem, "Functional Overview", format, content); - } - - /** - * Adds a "Quality Attributes" section relating to a {@link SoftwareSystem} from one or more files. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param files one or more File objects that point to the documentation content - * @return a documentation {@link Section} - * @throws IOException if there is an error reading the files - */ - @Nonnull - public Section addQualityAttributesSection(@Nullable SoftwareSystem softwareSystem, File... files) throws IOException { - return addSection(softwareSystem, "Quality Attributes", files); - } - - /** - * Adds a "Quality Attributes" section relating to a {@link SoftwareSystem}. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param format the {@link Format} of the documentation content - * @param content a String containing the documentation content - * @return a documentation {@link Section} - */ - @Nonnull - public Section addQualityAttributesSection(@Nullable SoftwareSystem softwareSystem, @Nonnull Format format, @Nonnull String content) { - return addSection(softwareSystem, "Quality Attributes", format, content); - } - - /** - * Adds a "Constraints" section relating to a {@link SoftwareSystem} from one or more files. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param files one or more File objects that point to the documentation content - * @return a documentation {@link Section} - * @throws IOException if there is an error reading the files - */ - @Nonnull - public Section addConstraintsSection(@Nullable SoftwareSystem softwareSystem, File... files) throws IOException { - return addSection(softwareSystem, "Constraints", files); - } - - /** - * Adds a "Constraints" section relating to a {@link SoftwareSystem}. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param format the {@link Format} of the documentation content - * @param content a String containing the documentation content - * @return a documentation {@link Section} - */ - @Nonnull - public Section addConstraintsSection(@Nullable SoftwareSystem softwareSystem, @Nonnull Format format, @Nonnull String content) { - return addSection(softwareSystem, "Constraints", format, content); - } - - /** - * Adds a "Principles" section relating to a {@link SoftwareSystem} from one or more files. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param files one or more File objects that point to the documentation content - * @return a documentation {@link Section} - * @throws IOException if there is an error reading the files - */ - @Nonnull - public Section addPrinciplesSection(@Nullable SoftwareSystem softwareSystem, File... files) throws IOException { - return addSection(softwareSystem, "Principles", files); - } - - /** - * Adds a "Principles" section relating to a {@link SoftwareSystem}. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param format the {@link Format} of the documentation content - * @param content a String containing the documentation content - * @return a documentation {@link Section} - */ - @Nonnull - public Section addPrinciplesSection(@Nullable SoftwareSystem softwareSystem, @Nonnull Format format, @Nonnull String content) { - return addSection(softwareSystem, "Principles", format, content); - } - - /** - * Adds a "Software Architecture" section relating to a {@link SoftwareSystem} from one or more files. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param files one or more File objects that point to the documentation content - * @return a documentation {@link Section} - * @throws IOException if there is an error reading the files - */ - @Nonnull - public Section addSoftwareArchitectureSection(@Nullable SoftwareSystem softwareSystem, File... files) throws IOException { - return addSection(softwareSystem, "Software Architecture", files); - } - - /** - * Adds a "Software Architecture" section relating to a {@link SoftwareSystem}. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param format the {@link Format} of the documentation content - * @param content a String containing the documentation content - * @return a documentation {@link Section} - */ - @Nonnull - public Section addSoftwareArchitectureSection(@Nullable SoftwareSystem softwareSystem, @Nonnull Format format, @Nonnull String content) { - return addSection(softwareSystem, "Software Architecture", format, content); - } - - /** - * Adds a "Containers" section relating to a {@link SoftwareSystem} from one or more files. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param files one or more File objects that point to the documentation content - * @return a documentation {@link Section} - * @throws IOException if there is an error reading the files - */ - @Nonnull - public Section addContainersSection(@Nullable SoftwareSystem softwareSystem, File... files) throws IOException { - return addSection(softwareSystem, "Containers", files); - } - - /** - * Adds a "Containers" section relating to a {@link SoftwareSystem}. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param format the {@link Format} of the documentation content - * @param content a String containing the documentation content - * @return a documentation {@link Section} - */ - @Nonnull - public Section addContainersSection(@Nullable SoftwareSystem softwareSystem, @Nonnull Format format, @Nonnull String content) { - return addSection(softwareSystem, "Containers", format, content); - } - - /** - * Adds a "Components" section relating to a {@link Container} from one or more files. - * - * @param container the {@link Container} the documentation content relates to - * @param files one or more File objects that point to the documentation content - * @return a documentation {@link Section} - * @throws IOException if there is an error reading the files - */ - @Nonnull - public Section addComponentsSection(@Nullable Container container, File... files) throws IOException { - return addSection(container, "Components", files); - } - - /** - * Adds a "Components" section relating to a {@link Container}. - * - * @param container the {@link Container} the documentation content relates to - * @param format the {@link Format} of the documentation content - * @param content a String containing the documentation content - * @return a documentation {@link Section} - */ - @Nonnull - public Section addComponentsSection(@Nullable Container container, @Nonnull Format format, @Nonnull String content) { - return addSection(container, "Components", format, content); - } - - /** - * Adds a "Code" section relating to a {@link Component} from one or more files. - * - * @param component the {@link Component} the documentation content relates to - * @param files one or more File objects that point to the documentation content - * @return a documentation {@link Section} - * @throws IOException if there is an error reading the files - */ - @Nonnull - public Section addCodeSection(@Nullable Component component, File... files) throws IOException { - return addSection(component, "Code", files); - } - - /** - * Adds a "Code" section relating to a {@link Component}. - * - * @param component the {@link Component} the documentation content relates to - * @param format the {@link Format} of the documentation content - * @param content a String containing the documentation content - * @return a documentation {@link Section} - */ - @Nonnull - public Section addCodeSection(@Nullable Component component, @Nonnull Format format, @Nonnull String content) { - return addSection(component, "Code", format, content); - } - - /** - * Adds a "Data" section relating to a {@link SoftwareSystem} from one or more files. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param files one or more File objects that point to the documentation content - * @return a documentation {@link Section} - * @throws IOException if there is an error reading the files - */ - @Nonnull - public Section addDataSection(@Nullable SoftwareSystem softwareSystem, File... files) throws IOException { - return addSection(softwareSystem, "Data", files); - } - - /** - * Adds a "Data" section relating to a {@link SoftwareSystem}. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param format the {@link Format} of the documentation content - * @param content a String containing the documentation content - * @return a documentation {@link Section} - */ - @Nonnull - public Section addDataSection(@Nullable SoftwareSystem softwareSystem, @Nonnull Format format, @Nonnull String content) { - return addSection(softwareSystem, "Data", format, content); - } - - /** - * Adds an "Infrastructure Architecture" section relating to a {@link SoftwareSystem} from one or more files. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param files one or more File objects that point to the documentation content - * @return a documentation {@link Section} - * @throws IOException if there is an error reading the files - */ - @Nonnull - public Section addInfrastructureArchitectureSection(@Nullable SoftwareSystem softwareSystem, File... files) throws IOException { - return addSection(softwareSystem, "Infrastructure Architecture", files); - } - - /** - * Adds a "Infrastructure Architecture" section relating to a {@link SoftwareSystem}. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param format the {@link Format} of the documentation content - * @param content a String containing the documentation content - * @return a documentation {@link Section} - */ - @Nonnull - public Section addInfrastructureArchitectureSection(@Nullable SoftwareSystem softwareSystem, @Nonnull Format format, @Nonnull String content) { - return addSection(softwareSystem, "Infrastructure Architecture", format, content); - } - - /** - * Adds a "Deployment" section relating to a {@link SoftwareSystem} from one or more files. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param files one or more File objects that point to the documentation content - * @return a documentation {@link Section} - * @throws IOException if there is an error reading the files - */ - @Nonnull - public Section addDeploymentSection(@Nullable SoftwareSystem softwareSystem, File... files) throws IOException { - return addSection(softwareSystem, "Deployment", files); - } - - /** - * Adds a "Deployment" section relating to a {@link SoftwareSystem}. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param format the {@link Format} of the documentation content - * @param content a String containing the documentation content - * @return a documentation {@link Section} - */ - @Nonnull - public Section addDeploymentSection(@Nullable SoftwareSystem softwareSystem, @Nonnull Format format, @Nonnull String content) { - return addSection(softwareSystem, "Deployment", format, content); - } - - /** - * Adds a "Development Environment" section relating to a {@link SoftwareSystem} from one or more files. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param files one or more File objects that point to the documentation content - * @return a documentation {@link Section} - * @throws IOException if there is an error reading the files - */ - @Nonnull - public Section addDevelopmentEnvironmentSection(@Nullable SoftwareSystem softwareSystem, File... files) throws IOException { - return addSection(softwareSystem, "Development Environment", files); - } - - /** - * Adds a "Development Environment" section relating to a {@link SoftwareSystem}. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param format the {@link Format} of the documentation content - * @param content a String containing the documentation content - * @return a documentation {@link Section} - */ - @Nonnull - public Section addDevelopmentEnvironmentSection(@Nullable SoftwareSystem softwareSystem, @Nonnull Format format, @Nonnull String content) { - return addSection(softwareSystem, "Development Environment", format, content); - } - - /** - * Adds an "Operation and Support" section relating to a {@link SoftwareSystem} from one or more files. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param files one or more File objects that point to the documentation content - * @return a documentation {@link Section} - * @throws IOException if there is an error reading the files - */ - @Nonnull - public Section addOperationAndSupportSection(@Nullable SoftwareSystem softwareSystem, File... files) throws IOException { - return addSection(softwareSystem, "Operation and Support", files); - } - - /** - * Adds a "Operation and Support" section relating to a {@link SoftwareSystem}. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param format the {@link Format} of the documentation content - * @param content a String containing the documentation content - * @return a documentation {@link Section} - */ - @Nonnull - public Section addOperationAndSupportSection(@Nullable SoftwareSystem softwareSystem, @Nonnull Format format, @Nonnull String content) { - return addSection(softwareSystem, "Operation and Support", format, content); - } - - /** - * Adds a "Decision Log" section relating to a {@link SoftwareSystem} from one or more files. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param files one or more File objects that point to the documentation content - * @return a documentation {@link Section} - * @throws IOException if there is an error reading the files - */ - @Nonnull - public Section addDecisionLogSection(@Nullable SoftwareSystem softwareSystem, File... files) throws IOException { - return addSection(softwareSystem, "Decision Log", files); - } - - /** - * Adds a "Decision Log" section relating to a {@link SoftwareSystem}. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param format the {@link Format} of the documentation content - * @param content a String containing the documentation content - * @return a documentation {@link Section} - */ - @Nonnull - public Section addDecisionLogSection(@Nullable SoftwareSystem softwareSystem, @Nonnull Format format, @Nonnull String content) { - return addSection(softwareSystem, "Decision Log", format, content); - } - - @Override - protected TemplateMetadata getMetadata() { - return new TemplateMetadata("Software Guidebook", "Simon Brown", "https://leanpub.com/visualising-software-architecture"); - } - -} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/documentation/TemplateMetadata.java b/structurizr-core/src/com/structurizr/documentation/TemplateMetadata.java deleted file mode 100644 index aab8a44fb..000000000 --- a/structurizr-core/src/com/structurizr/documentation/TemplateMetadata.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.structurizr.documentation; - -/** - * Represents some basic metadata about the documentation template being used. - */ -public class TemplateMetadata { - - private String name; - private String author; - private String url; - - TemplateMetadata() { - } - - public TemplateMetadata(String name, String author, String url) { - this.name = name; - this.author = author; - this.url = url; - } - - public String getName() { - return name; - } - - void setName(String name) { - this.name = name; - } - - public String getAuthor() { - return author; - } - - void setAuthor(String author) { - this.author = author; - } - - public String getUrl() { - return url; - } - - void setUrl(String url) { - this.url = url; - } - -} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/documentation/ViewpointsAndPerspectivesDocumentationTemplate.java b/structurizr-core/src/com/structurizr/documentation/ViewpointsAndPerspectivesDocumentationTemplate.java deleted file mode 100644 index f91caf2e3..000000000 --- a/structurizr-core/src/com/structurizr/documentation/ViewpointsAndPerspectivesDocumentationTemplate.java +++ /dev/null @@ -1,209 +0,0 @@ -package com.structurizr.documentation; - -import com.structurizr.Workspace; -import com.structurizr.model.SoftwareSystem; - -import java.io.File; -import java.io.IOException; - -/** - *

    - * An implementation of the "Viewpoints and Perspectives" documentation template, - * from the "Software Systems Architecture" book by Nick Rozanski and Eoin Woods, consisting of the following sections: - *

    - * - *
      - *
    1. Introduction (1)
    2. - *
    3. Glossary (1)
    4. - *
    5. System Stakeholders and Requirements (2)
    6. - *
    7. Architectural Forces (2)
    8. - *
    9. Architectural Views (3)
    10. - *
    11. System Qualities (4)
    12. - *
    13. Appendices (5)
    14. - *
    - * - *

    - * The number in parentheses () represents the grouping, which is simply used to colour code - * section navigation buttons when rendered. - *

    - */ -public class ViewpointsAndPerspectivesDocumentationTemplate extends DocumentationTemplate { - - public ViewpointsAndPerspectivesDocumentationTemplate(Workspace workspace) { - super(workspace); - } - - /** - * Adds a "Introduction" section relating to a {@link SoftwareSystem} from one or more files. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param files one or more File objects that point to the documentation content - * @return a documentation {@link Section} - * @throws IOException if there is an error reading the files - */ - public Section addIntroductionSection(SoftwareSystem softwareSystem, File... files) throws IOException { - return addSection(softwareSystem, "Introduction", files); - } - - /** - * Adds a "Introduction" section relating to a {@link SoftwareSystem}. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param format the {@link Format} of the documentation content - * @param content a String containing the documentation content - * @return a documentation {@link Section} - */ - public Section addIntroductionSection(SoftwareSystem softwareSystem, Format format, String content) { - return addSection(softwareSystem, "Introduction", format, content); - } - - /** - * Adds a "Glossary" section relating to a {@link SoftwareSystem} from one or more files. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param files one or more File objects that point to the documentation content - * @return a documentation {@link Section} - * @throws IOException if there is an error reading the files - */ - public Section addGlossarySection(SoftwareSystem softwareSystem, File... files) throws IOException { - return addSection(softwareSystem, "Glossary", files); - } - - /** - * Adds a "Glossary" section relating to a {@link SoftwareSystem}. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param format the {@link Format} of the documentation content - * @param content a String containing the documentation content - * @return a documentation {@link Section} - */ - public Section addGlossarySection(SoftwareSystem softwareSystem, Format format, String content) { - return addSection(softwareSystem, "Glossary", format, content); - } - - /** - * Adds a "System Stakeholders and Requirements" section relating to a {@link SoftwareSystem} from one or more files. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param files one or more File objects that point to the documentation content - * @return a documentation {@link Section} - * @throws IOException if there is an error reading the files - */ - public Section addSystemStakeholdersAndRequirementsSection(SoftwareSystem softwareSystem, File... files) throws IOException { - return addSection(softwareSystem, "System Stakeholders and Requirements", files); - } - - /** - * Adds a "System Stakeholders and Requirements" section relating to a {@link SoftwareSystem}. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param format the {@link Format} of the documentation content - * @param content a String containing the documentation content - * @return a documentation {@link Section} - */ - public Section addSystemStakeholdersAndRequirementsSection(SoftwareSystem softwareSystem, Format format, String content) { - return addSection(softwareSystem, "System Stakeholders and Requirements", format, content); - } - - /** - * Adds an "Architectural Forces" section relating to a {@link SoftwareSystem} from one or more files. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param files one or more File objects that point to the documentation content - * @return a documentation {@link Section} - * @throws IOException if there is an error reading the files - */ - public Section addArchitecturalForcesSection(SoftwareSystem softwareSystem, File... files) throws IOException { - return addSection(softwareSystem, "Architectural Forces", files); - } - - /** - * Adds an "Architectural Forces" section relating to a {@link SoftwareSystem}. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param format the {@link Format} of the documentation content - * @param content a String containing the documentation content - * @return a documentation {@link Section} - */ - public Section addArchitecturalForcesSection(SoftwareSystem softwareSystem, Format format, String content) { - return addSection(softwareSystem, "Architectural Forces", format, content); - } - - /** - * Adds an "Architectural Views" section relating to a {@link SoftwareSystem} from one or more files. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param files one or more File objects that point to the documentation content - * @return a documentation {@link Section} - * @throws IOException if there is an error reading the files - */ - public Section addArchitecturalViewsSection(SoftwareSystem softwareSystem, File... files) throws IOException { - return addSection(softwareSystem, "Architectural Views", files); - } - - /** - * Adds an "Architectural Views" section relating to a {@link SoftwareSystem}. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param format the {@link Format} of the documentation content - * @param content a String containing the documentation content - * @return a documentation {@link Section} - */ - public Section addArchitecturalViewsSection(SoftwareSystem softwareSystem, Format format, String content) { - return addSection(softwareSystem, "Architectural Views", format, content); - } - - /** - * Adds a "System Qualities" section relating to a {@link SoftwareSystem} from one or more files. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param files one or more File objects that point to the documentation content - * @return a documentation {@link Section} - * @throws IOException if there is an error reading the files - */ - public Section addSystemQualitiesSection(SoftwareSystem softwareSystem, File... files) throws IOException { - return addSection(softwareSystem, "System Qualities", files); - } - - /** - * Adds a "System Qualities" section relating to a {@link SoftwareSystem}. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param format the {@link Format} of the documentation content - * @param content a String containing the documentation content - * @return a documentation {@link Section} - */ - public Section addSystemQualitiesSection(SoftwareSystem softwareSystem, Format format, String content) { - return addSection(softwareSystem, "System Qualities", format, content); - } - - /** - * Adds an "Appendices" section relating to a {@link SoftwareSystem} from one or more files. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param files one or more File objects that point to the documentation content - * @return a documentation {@link Section} - * @throws IOException if there is an error reading the files - */ - public Section addAppendicesSection(SoftwareSystem softwareSystem, File... files) throws IOException { - return addSection(softwareSystem, "Appendices", files); - } - - /** - * Adds an "Appendices" section relating to a {@link SoftwareSystem}. - * - * @param softwareSystem the {@link SoftwareSystem} the documentation content relates to - * @param format the {@link Format} of the documentation content - * @param content a String containing the documentation content - * @return a documentation {@link Section} - */ - public Section addAppendicesSection(SoftwareSystem softwareSystem, Format format, String content) { - return addSection(softwareSystem, "Appendices", format, content); - } - - @Override - protected TemplateMetadata getMetadata() { - return new TemplateMetadata("Viewpoints and Perspectives", "Nick Rozanski and Eoin Woods", "https://www.viewpoints-and-perspectives.info"); - } - -} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/model/SoftwareSystem.java b/structurizr-core/src/com/structurizr/model/SoftwareSystem.java index 9f1fc119a..f1ff2b57c 100644 --- a/structurizr-core/src/com/structurizr/model/SoftwareSystem.java +++ b/structurizr-core/src/com/structurizr/model/SoftwareSystem.java @@ -1,6 +1,7 @@ package com.structurizr.model; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.structurizr.documentation.Documentation; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -18,6 +19,8 @@ public final class SoftwareSystem extends StaticStructureElement { private Set containers = new LinkedHashSet<>(); + private Documentation documentation = new Documentation(); + /** * Gets the parent of this software system. * @@ -172,4 +175,22 @@ public Set getDefaultTags() { return new LinkedHashSet<>(Arrays.asList(Tags.ELEMENT, Tags.SOFTWARE_SYSTEM)); } + /** + * Gets the documentation associated with this software system. + * + * @return a Documentation object + */ + public Documentation getDocumentation() { + return documentation; + } + + /** + * Sets the documentation associated with this software system. + * + * @param documentation a Documentation object + */ + void setDocumentation(@Nonnull Documentation documentation) { + this.documentation = documentation; + } + } \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/WorkspaceTests.java b/structurizr-core/test/unit/com/structurizr/WorkspaceTests.java index 5838130c1..e969b6495 100644 --- a/structurizr-core/test/unit/com/structurizr/WorkspaceTests.java +++ b/structurizr-core/test/unit/com/structurizr/WorkspaceTests.java @@ -1,6 +1,7 @@ package com.structurizr; -import com.structurizr.documentation.StructurizrDocumentationTemplate; +import com.structurizr.documentation.Decision; +import com.structurizr.documentation.Format; import com.structurizr.model.Component; import com.structurizr.model.Container; import com.structurizr.model.SoftwareSystem; @@ -37,8 +38,12 @@ public void test_isEmpty_ReturnsFalse_WhenThereAreViews() { @Test public void test_isEmpty_ReturnsFalse_WhenThereIsDocumentation() throws Exception { workspace = new Workspace("Name", "Description"); - StructurizrDocumentationTemplate template = new StructurizrDocumentationTemplate(workspace); - template.addImage(new File("../docs/images/structurizr-logo.png")); + Decision d = new Decision("1"); + d.setTitle("Title"); + d.setContent("Content"); + d.setStatus("Proposed"); + d.setFormat(Format.Markdown); + workspace.getDocumentation().addDecision(d); assertFalse(workspace.isEmpty()); } diff --git a/structurizr-core/test/unit/com/structurizr/documentation/Arc42DocumentationTemplateTests.java b/structurizr-core/test/unit/com/structurizr/documentation/Arc42DocumentationTemplateTests.java deleted file mode 100644 index 2345164d9..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/Arc42DocumentationTemplateTests.java +++ /dev/null @@ -1,128 +0,0 @@ -package com.structurizr.documentation; - -import com.structurizr.AbstractWorkspaceTestBase; -import com.structurizr.model.Element; -import com.structurizr.model.SoftwareSystem; -import org.junit.Before; -import org.junit.Test; - -import java.io.File; -import java.io.IOException; - -import static org.junit.Assert.*; - -public class Arc42DocumentationTemplateTests extends AbstractWorkspaceTestBase { - - private SoftwareSystem softwareSystem; - private Arc42DocumentationTemplate template; - - @Before - public void setUp() { - softwareSystem = workspace.getModel().addSoftwareSystem("Name", "Description"); - template = new Arc42DocumentationTemplate(workspace); - } - - @Test - public void test_construction_ThrowsAnException_WhenANullWorkspaceIsSpecified() { - try { - new Arc42DocumentationTemplate(null); - fail(); - } catch (Exception e) { - assertEquals("A workspace must be specified.", e.getMessage()); - } - } - - @Test - public void test_addAllSectionsWithContentAsStrings() { - Section section; - - section = template.addIntroductionAndGoalsSection(softwareSystem, Format.Markdown, "Section 1"); - assertSection(softwareSystem, "Introduction and Goals", Format.Markdown, "Section 1", 1, section); - - section = template.addConstraintsSection(softwareSystem, Format.Markdown, "Section 2"); - assertSection(softwareSystem, "Constraints", Format.Markdown, "Section 2", 2, section); - - section = template.addContextAndScopeSection(softwareSystem, Format.Markdown, "Section 3"); - assertSection(softwareSystem, "Context and Scope", Format.Markdown, "Section 3", 3, section); - - section = template.addSolutionStrategySection(softwareSystem, Format.Markdown, "Section 4"); - assertSection(softwareSystem, "Solution Strategy", Format.Markdown, "Section 4", 4, section); - - section = template.addBuildingBlockViewSection(softwareSystem, Format.Markdown, "Section 5"); - assertSection(softwareSystem, "Building Block View", Format.Markdown, "Section 5", 5, section); - - section = template.addRuntimeViewSection(softwareSystem, Format.Markdown, "Section 6"); - assertSection(softwareSystem, "Runtime View", Format.Markdown, "Section 6", 6, section); - - section = template.addDeploymentViewSection(softwareSystem, Format.Markdown, "Section 7"); - assertSection(softwareSystem, "Deployment View", Format.Markdown, "Section 7", 7, section); - - section = template.addCrosscuttingConceptsSection(softwareSystem, Format.Markdown, "Section 8"); - assertSection(softwareSystem, "Crosscutting Concepts", Format.Markdown, "Section 8", 8, section); - - section = template.addArchitecturalDecisionsSection(softwareSystem, Format.Markdown, "Section 9"); - assertSection(softwareSystem, "Architectural Decisions", Format.Markdown, "Section 9", 9, section); - - section = template.addQualityRequirementsSection(softwareSystem, Format.Markdown, "Section 10"); - assertSection(softwareSystem, "Quality Requirements", Format.Markdown, "Section 10", 10, section); - - section = template.addRisksAndTechnicalDebtSection(softwareSystem, Format.Markdown, "Section 11"); - assertSection(softwareSystem, "Risks and Technical Debt", Format.Markdown, "Section 11", 11, section); - - section = template.addGlossarySection(softwareSystem, Format.Markdown, "Section 12"); - assertSection(softwareSystem, "Glossary", Format.Markdown, "Section 12", 12, section); - } - - @Test - public void test_addAllSectionsWithContentFromFiles() throws IOException { - Section section; - File root = new File(".//test/unit/com/structurizr/documentation/arc42"); - - section = template.addIntroductionAndGoalsSection(softwareSystem, new File(root, "introduction-and-goals.md")); - assertSection(softwareSystem, "Introduction and Goals", Format.Markdown, "Section 1", 1, section); - - section = template.addConstraintsSection(softwareSystem, new File(root, "constraints.md")); - assertSection(softwareSystem, "Constraints", Format.Markdown, "Section 2", 2, section); - - section = template.addContextAndScopeSection(softwareSystem, new File(root, "context-and-scope.md")); - assertSection(softwareSystem, "Context and Scope", Format.Markdown, "Section 3", 3, section); - - section = template.addSolutionStrategySection(softwareSystem, new File(root, "solution-strategy.md")); - assertSection(softwareSystem, "Solution Strategy", Format.Markdown, "Section 4", 4, section); - - section = template.addBuildingBlockViewSection(softwareSystem, new File(root, "building-block-view.md")); - assertSection(softwareSystem, "Building Block View", Format.Markdown, "Section 5", 5, section); - - section = template.addRuntimeViewSection(softwareSystem, new File(root, "runtime-view.md")); - assertSection(softwareSystem, "Runtime View", Format.Markdown, "Section 6", 6, section); - - section = template.addDeploymentViewSection(softwareSystem, new File(root, "deployment-view.md")); - assertSection(softwareSystem, "Deployment View", Format.Markdown, "Section 7", 7, section); - - section = template.addCrosscuttingConceptsSection(softwareSystem, new File(root, "crosscutting-concepts.md")); - assertSection(softwareSystem, "Crosscutting Concepts", Format.Markdown, "Section 8", 8, section); - - section = template.addArchitecturalDecisionsSection(softwareSystem, new File(root, "architectural-decisions.md")); - assertSection(softwareSystem, "Architectural Decisions", Format.Markdown, "Section 9", 9, section); - - section = template.addQualityRequirementsSection(softwareSystem, new File(root, "quality-requirements.md")); - assertSection(softwareSystem, "Quality Requirements", Format.Markdown, "Section 10", 10, section); - - section = template.addRisksAndTechnicalDebtSection(softwareSystem, new File(root, "risks-and-technical-debt.md")); - assertSection(softwareSystem, "Risks and Technical Debt", Format.Markdown, "Section 11", 11, section); - - section = template.addGlossarySection(softwareSystem, new File(root, "glossary.md")); - assertSection(softwareSystem, "Glossary", Format.Markdown, "Section 12", 12, section); - } - - private void assertSection(Element element, String title, Format format, String content, int order, Section section) { - assertTrue(workspace.getDocumentation().getSections().contains(section)); - assertEquals(element, section.getElement()); - assertEquals(element.getId(), section.getElementId()); - assertEquals(title, section.getTitle()); - assertEquals(format, section.getFormat()); - assertEquals(content, section.getContent()); - assertEquals(order, section.getOrder()); - } - -} \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/AutomaticDocumentTemplateTests.java b/structurizr-core/test/unit/com/structurizr/documentation/AutomaticDocumentTemplateTests.java deleted file mode 100644 index cfe5c6ebe..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/AutomaticDocumentTemplateTests.java +++ /dev/null @@ -1,99 +0,0 @@ -package com.structurizr.documentation; - -import com.structurizr.AbstractWorkspaceTestBase; -import com.structurizr.model.Element; -import com.structurizr.model.SoftwareSystem; -import org.junit.Before; -import org.junit.Test; - -import java.io.File; -import java.io.IOException; -import java.util.List; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -public class AutomaticDocumentTemplateTests extends AbstractWorkspaceTestBase { - - private SoftwareSystem softwareSystem; - private AutomaticDocumentationTemplate template; - - @Before - public void setUp() { - softwareSystem = workspace.getModel().addSoftwareSystem("Name", "Description"); - template = new AutomaticDocumentationTemplate(workspace); - } - - @Test - public void test_construction_ThrowsAnException_WhenANullWorkspaceIsSpecified() { - try { - new AutomaticDocumentationTemplate(null); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("A workspace must be specified.", iae.getMessage()); - } - } - - @Test - public void test_addSections_ThrowsAnException_WhenTheDirectoryDoesNotExist() throws Exception { - try { - new AutomaticDocumentationTemplate(workspace).addSections(new File(".//test/unit/com/structurizr/documentation/foo")); - fail(); - } catch (IllegalArgumentException iae) { - assertTrue(iae.getMessage().endsWith("foo does not exist.")); - } - } - - @Test - public void test_addSections_ThrowsAnException_WhenTheDirectoryIsNotADirectory() throws Exception { - try { - new AutomaticDocumentationTemplate(workspace).addSections(new File(".//test/unit/com/structurizr/documentation/automatic/01-section-1.md")); - fail(); - } catch (IllegalArgumentException iae) { - assertTrue(iae.getMessage().endsWith("01-section-1.md is not a directory.")); - } - } - - @Test - public void test_addSections_WithRecursiveSetToFalse() throws IOException { - File root = new File(".//test/unit/com/structurizr/documentation/automatic"); - - List
    sections = template.addSections(softwareSystem, root); - assertEquals(6, sections.size()); - - assertSection(softwareSystem, "Section 1", Format.Markdown, "## Section 1", 1, sections.get(0)); - assertSection(softwareSystem, "Section 2", Format.Markdown, "## Section 2", 2, sections.get(1)); - assertSection(softwareSystem, "Section 3", Format.Markdown, "## Section 3", 3, sections.get(2)); - assertSection(softwareSystem, "Section 4", Format.AsciiDoc, "== Section 4", 4, sections.get(3)); - assertSection(softwareSystem, "Section 5", Format.AsciiDoc, "== Section 5", 5, sections.get(4)); - assertSection(softwareSystem, "Section 6", Format.AsciiDoc, "== Section 6", 6, sections.get(5)); - } - - @Test - public void test_addSections_WithRecursiveSetToTrue() throws IOException { - File root = new File(".//test/unit/com/structurizr/documentation/automatic"); - - template.setRecursive(true); - List
    sections = template.addSections(softwareSystem, root); - assertEquals(7, sections.size()); - - assertSection(softwareSystem, "Section 1", Format.Markdown, "## Section 1", 1, sections.get(0)); - assertSection(softwareSystem, "Section 2", Format.Markdown, "## Section 2", 2, sections.get(1)); - assertSection(softwareSystem, "Section 3", Format.Markdown, "## Section 3", 3, sections.get(2)); - assertSection(softwareSystem, "Section 4", Format.AsciiDoc, "== Section 4", 4, sections.get(3)); - assertSection(softwareSystem, "Section 5", Format.AsciiDoc, "== Section 5", 5, sections.get(4)); - assertSection(softwareSystem, "Section 6", Format.AsciiDoc, "== Section 6", 6, sections.get(5)); - } - - private void assertSection(Element element, String title, Format format, String content, int order, Section section) { - assertTrue(workspace.getDocumentation().getSections().contains(section)); - assertEquals(element, section.getElement()); - assertEquals(element.getId(), section.getElementId()); - assertEquals(title, section.getTitle()); - assertEquals(format, section.getFormat()); - assertEquals(content, section.getContent()); - assertEquals(order, section.getOrder()); - } - -} diff --git a/structurizr-core/test/unit/com/structurizr/documentation/DecisionTests.java b/structurizr-core/test/unit/com/structurizr/documentation/DecisionTests.java new file mode 100644 index 000000000..395d33a6e --- /dev/null +++ b/structurizr-core/test/unit/com/structurizr/documentation/DecisionTests.java @@ -0,0 +1,23 @@ +package com.structurizr.documentation; + +import com.structurizr.AbstractWorkspaceTestBase; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class DecisionTests extends AbstractWorkspaceTestBase { + + @Test + public void test_hasLinkTo() { + Decision d1 = new Decision("1"); + Decision d2 = new Decision("2"); + Decision d3 = new Decision("3"); + + d1.addLink(d2, "Type"); + + assertTrue(d1.hasLinkTo(d2)); + assertFalse(d1.hasLinkTo(d3)); + } + +} \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/DocumentationTemplateTests.java b/structurizr-core/test/unit/com/structurizr/documentation/DocumentationTemplateTests.java deleted file mode 100644 index 3184809b9..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/DocumentationTemplateTests.java +++ /dev/null @@ -1,237 +0,0 @@ -package com.structurizr.documentation; - -import com.structurizr.AbstractWorkspaceTestBase; -import com.structurizr.Workspace; -import com.structurizr.model.SoftwareSystem; -import org.junit.Before; -import org.junit.Test; - -import java.io.File; -import java.io.IOException; -import java.util.Collection; - -import static org.junit.Assert.*; - -public class DocumentationTemplateTests extends AbstractWorkspaceTestBase { - - private SoftwareSystem softwareSystem; - private StructurizrDocumentationTemplate template; - private Documentation documentation; - - @Before - public void setUp() { - Workspace workspace = new Workspace("Name", "Description"); - softwareSystem = workspace.getModel().addSoftwareSystem("Name", "Description"); - - template = new StructurizrDocumentationTemplate(workspace); - documentation = workspace.getDocumentation(); - } - - @Test - public void test_addSection_ThrowsAnException_WhenThatSectionAlreadyExists() { - template.addContextSection(softwareSystem, Format.Markdown, "Some Markdown content"); - assertEquals(1, documentation.getSections().size()); - - try { - template.addContextSection(softwareSystem, Format.Markdown, "Some Markdown content"); - fail(); - } catch (IllegalArgumentException iae) { - // this is the expected exception - assertEquals("A section with a title of Context already exists for the element named Name.", iae.getMessage()); - assertEquals(1, documentation.getSections().size()); - } - } - - @Test - public void test_addImages_DoesNothing_WhenThereAreNoImageFilesInTheSpecifiedDirectory() throws IOException { - template.addImages(new File(".//test/unit/com/structurizr/documentation/noimages")); - assertTrue(documentation.getImages().isEmpty()); - } - - @Test - public void test_addImages_ThrowsAnException_WhenTheSpecifiedDirectoryIsNull() throws IOException { - try { - template.addImages(null); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("Directory path must not be null.", iae.getMessage()); - } - } - - @Test - public void test_addImages_ThrowsAnException_WhenTheSpecifiedDirectoryIsNotADirectory() throws IOException { - try { - template.addImages(new File(".//test/unit/com/structurizr/documentation/structurizr/context.md")); - fail(); - } catch (IllegalArgumentException iae) { - assertTrue(iae.getMessage().endsWith("context.md is not a directory.")); - } - } - - @Test - public void test_addImages_ThrowsAnException_WhenTheSpecifiedDirectoryDoesNotExist() throws IOException { - try { - template.addImages(new File(".//test/unit/com/structurizr/documentation/blah")); - fail(); - } catch (IllegalArgumentException iae) { - assertTrue(iae.getMessage().endsWith("blah does not exist.")); - } - } - - @Test - public void test_addImages_AddsAllImagesFromTheSpecifiedDirectory_WhenThereAreImageFilesInTheSpecifiedDirectory() throws IOException { - assertTrue(documentation.getImages().isEmpty()); - Collection images = template.addImages(new File(".//test/unit/com/structurizr/documentation/images")); - assertEquals(4, documentation.getImages().size()); - assertEquals(4, images.size()); - - Image png = documentation.getImages().stream().filter(i -> i.getName().equals("image.png")).findFirst().get(); - assertEquals("image/png", png.getType()); - assertTrue(png.getContent().startsWith("iVBORw0KGgoAAAANSUhEUgAAACAAAAAaCAYAAADWm14/AAAD")); - assertTrue(images.contains(png)); - - Image jpg = documentation.getImages().stream().filter(i -> i.getName().equals("image.jpg")).findFirst().get(); - assertEquals("image/jpeg", jpg.getType()); - assertEquals("/9j/4AAQSkZJRgABAgAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAAaACADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDxzUdRu9Vv5r69nea4mcu7uxJyTnv2GeBT49I1CWNXS1cqwyDkDP5mqLfdP0NeueF9Pt9Q1AR3Kb40h3begJ4HNerhqEal+boebiK0qdra3PNP7D1L/nzf/vpf8afbnVvD17BqEPnWs0UgZJVbHIOccHvjoete8/8ACP6R/wA+EP5V598R9Ot9PttttHsSRUbaOgIcDitZ4anytpszhXqcyTS1PNHVl3KykMMgg8c16n4b1q20+6W5dt8MkWwlCCR05x+Fc78UraC1+IGqx28EcKeYW2xoFGSTk4Hc1x5RSCSoz9KwoV/Zpu17m9ah7RpXtY99/wCEy0j+9N/3x/8AXrhfH+swavEotwf4I0U43Md2ScCvPPLT+4v5V2XwttLa5+IOlRz28UqeaG2yIGGQRg4NVPFrlaUfxFHC2km5bH//2Q==", jpg.getContent()); - assertTrue(images.contains(jpg)); - - Image jpeg = documentation.getImages().stream().filter(i -> i.getName().equals("image.jpeg")).findFirst().get(); - assertEquals("image/jpeg", jpeg.getType()); - assertEquals("/9j/4AAQSkZJRgABAgAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAAaACADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDxzUdRu9Vv5r69nea4mcu7uxJyTnv2GeBT49I1CWNXS1cqwyDkDP5mqLfdP0NeueF9Pt9Q1AR3Kb40h3begJ4HNerhqEal+boebiK0qdra3PNP7D1L/nzf/vpf8afbnVvD17BqEPnWs0UgZJVbHIOccHvjoete8/8ACP6R/wA+EP5V598R9Ot9PttttHsSRUbaOgIcDitZ4anytpszhXqcyTS1PNHVl3KykMMgg8c16n4b1q20+6W5dt8MkWwlCCR05x+Fc78UraC1+IGqx28EcKeYW2xoFGSTk4Hc1x5RSCSoz9KwoV/Zpu17m9ah7RpXtY99/wCEy0j+9N/3x/8AXrhfH+swavEotwf4I0U43Md2ScCvPPLT+4v5V2XwttLa5+IOlRz28UqeaG2yIGGQRg4NVPFrlaUfxFHC2km5bH//2Q==", jpeg.getContent()); - assertTrue(images.contains(jpeg)); - - Image gif = documentation.getImages().stream().filter(i -> i.getName().equals("image.gif")).findFirst().get(); - assertEquals("image/gif", gif.getType()); - assertEquals("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", gif.getContent()); - assertTrue(images.contains(gif)); - } - - @Test - public void test_addImages_AddsAllImagesFromTheSpecifiedDirectory_WhenThereAreImageFilesInTheSpecifiedDirectoryAndSubDirectories() throws IOException { - assertTrue(documentation.getImages().isEmpty()); - template.addImages(new File(".//test/unit/com/structurizr/documentation")); - assertEquals(8, documentation.getImages().size()); - - Image pngInDirectory = documentation.getImages().stream().filter(i -> i.getName().equals("image.png")).findFirst().get(); - assertEquals("image/png", pngInDirectory.getType()); - assertTrue(pngInDirectory.getContent().startsWith("iVBORw0KGgoAAAANSUhEUgAAACAAAAAaCAYAAADWm14/AAAD")); - - Image jpgInDirectory = documentation.getImages().stream().filter(i -> i.getName().equals("image.jpg")).findFirst().get(); - assertEquals("image/jpeg", jpgInDirectory.getType()); - assertEquals("/9j/4AAQSkZJRgABAgAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAAaACADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDxzUdRu9Vv5r69nea4mcu7uxJyTnv2GeBT49I1CWNXS1cqwyDkDP5mqLfdP0NeueF9Pt9Q1AR3Kb40h3begJ4HNerhqEal+boebiK0qdra3PNP7D1L/nzf/vpf8afbnVvD17BqEPnWs0UgZJVbHIOccHvjoete8/8ACP6R/wA+EP5V598R9Ot9PttttHsSRUbaOgIcDitZ4anytpszhXqcyTS1PNHVl3KykMMgg8c16n4b1q20+6W5dt8MkWwlCCR05x+Fc78UraC1+IGqx28EcKeYW2xoFGSTk4Hc1x5RSCSoz9KwoV/Zpu17m9ah7RpXtY99/wCEy0j+9N/3x/8AXrhfH+swavEotwf4I0U43Md2ScCvPPLT+4v5V2XwttLa5+IOlRz28UqeaG2yIGGQRg4NVPFrlaUfxFHC2km5bH//2Q==", jpgInDirectory.getContent()); - - Image jpegInDirectory = documentation.getImages().stream().filter(i -> i.getName().equals("image.jpeg")).findFirst().get(); - assertEquals("image/jpeg", jpegInDirectory.getType()); - assertEquals("/9j/4AAQSkZJRgABAgAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAAaACADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDxzUdRu9Vv5r69nea4mcu7uxJyTnv2GeBT49I1CWNXS1cqwyDkDP5mqLfdP0NeueF9Pt9Q1AR3Kb40h3begJ4HNerhqEal+boebiK0qdra3PNP7D1L/nzf/vpf8afbnVvD17BqEPnWs0UgZJVbHIOccHvjoete8/8ACP6R/wA+EP5V598R9Ot9PttttHsSRUbaOgIcDitZ4anytpszhXqcyTS1PNHVl3KykMMgg8c16n4b1q20+6W5dt8MkWwlCCR05x+Fc78UraC1+IGqx28EcKeYW2xoFGSTk4Hc1x5RSCSoz9KwoV/Zpu17m9ah7RpXtY99/wCEy0j+9N/3x/8AXrhfH+swavEotwf4I0U43Md2ScCvPPLT+4v5V2XwttLa5+IOlRz28UqeaG2yIGGQRg4NVPFrlaUfxFHC2km5bH//2Q==", jpegInDirectory.getContent()); - - Image gifInDirectory = documentation.getImages().stream().filter(i -> i.getName().equals("image.gif")).findFirst().get(); - assertEquals("image/gif", gifInDirectory.getType()); - assertEquals("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", gifInDirectory.getContent()); - - - Image pngInSubDirectory = documentation.getImages().stream().filter(i -> i.getName().equals("image.png")).findFirst().get(); - assertEquals("image/png", pngInSubDirectory.getType()); - assertTrue(pngInSubDirectory.getContent().startsWith("iVBORw0KGgoAAAANSUhEUgAAACAAAAAaCAYAAADWm14/AAAD")); - - Image jpgInSubDirectory = documentation.getImages().stream().filter(i -> i.getName().equals("image.jpg")).findFirst().get(); - assertEquals("image/jpeg", jpgInSubDirectory.getType()); - assertEquals("/9j/4AAQSkZJRgABAgAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAAaACADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDxzUdRu9Vv5r69nea4mcu7uxJyTnv2GeBT49I1CWNXS1cqwyDkDP5mqLfdP0NeueF9Pt9Q1AR3Kb40h3begJ4HNerhqEal+boebiK0qdra3PNP7D1L/nzf/vpf8afbnVvD17BqEPnWs0UgZJVbHIOccHvjoete8/8ACP6R/wA+EP5V598R9Ot9PttttHsSRUbaOgIcDitZ4anytpszhXqcyTS1PNHVl3KykMMgg8c16n4b1q20+6W5dt8MkWwlCCR05x+Fc78UraC1+IGqx28EcKeYW2xoFGSTk4Hc1x5RSCSoz9KwoV/Zpu17m9ah7RpXtY99/wCEy0j+9N/3x/8AXrhfH+swavEotwf4I0U43Md2ScCvPPLT+4v5V2XwttLa5+IOlRz28UqeaG2yIGGQRg4NVPFrlaUfxFHC2km5bH//2Q==", jpgInSubDirectory.getContent()); - - Image jpegInSubDirectory = documentation.getImages().stream().filter(i -> i.getName().equals("image.jpeg")).findFirst().get(); - assertEquals("image/jpeg", jpegInSubDirectory.getType()); - assertEquals("/9j/4AAQSkZJRgABAgAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAAaACADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDxzUdRu9Vv5r69nea4mcu7uxJyTnv2GeBT49I1CWNXS1cqwyDkDP5mqLfdP0NeueF9Pt9Q1AR3Kb40h3begJ4HNerhqEal+boebiK0qdra3PNP7D1L/nzf/vpf8afbnVvD17BqEPnWs0UgZJVbHIOccHvjoete8/8ACP6R/wA+EP5V598R9Ot9PttttHsSRUbaOgIcDitZ4anytpszhXqcyTS1PNHVl3KykMMgg8c16n4b1q20+6W5dt8MkWwlCCR05x+Fc78UraC1+IGqx28EcKeYW2xoFGSTk4Hc1x5RSCSoz9KwoV/Zpu17m9ah7RpXtY99/wCEy0j+9N/3x/8AXrhfH+swavEotwf4I0U43Md2ScCvPPLT+4v5V2XwttLa5+IOlRz28UqeaG2yIGGQRg4NVPFrlaUfxFHC2km5bH//2Q==", jpegInSubDirectory.getContent()); - - Image gifInSubDirectory = documentation.getImages().stream().filter(i -> i.getName().equals("image.gif")).findFirst().get(); - assertEquals("image/gif", gifInSubDirectory.getType()); - assertEquals("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", gifInSubDirectory.getContent()); - } - - @Test - public void test_addImage_AddsTheSpecifiedImage_WhenTheSpecifiedFileExists() throws IOException { - assertTrue(documentation.getImages().isEmpty()); - template.addImage(new File(".//test/unit/com/structurizr/documentation/image.png")); - assertEquals(1, documentation.getImages().size()); - - Image png = documentation.getImages().stream().filter(i -> i.getName().equals("image.png")).findFirst().get(); - assertEquals("image/png", png.getType()); - assertTrue(png.getContent().startsWith("iVBORw0KGgoAAAANSUhEUgAAACAAAAAaCAYAAADWm14/AAAD")); - } - - @Test - public void test_addImage_ThrowsAnException_WhenTheSpecifiedFileIsNull() throws IOException { - try { - template.addImage(null); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("A file must be specified.", iae.getMessage()); - } - } - - @Test - public void test_addImage_ThrowsAnException_WhenTheSpecifiedFileIsNotAFile() throws IOException { - try { - template.addImage(new File(".//test/unit/com/structurizr/documentation/")); - fail(); - } catch (IllegalArgumentException iae) { - assertTrue(iae.getMessage().endsWith("documentation is not a file.")); - } - } - - @Test - public void test_addImage_ThrowsAnException_WhenTheSpecifiedFileDoesNotExist() throws IOException { - try { - template.addImage(new File(".//test/unit/com/structurizr/documentation/some-other-image.png")); - fail(); - } catch (IllegalArgumentException iae) { - assertTrue(iae.getMessage().endsWith("some-other-image.png does not exist.")); - } - } - - @Test - public void test_addImage_ThrowsAnException_WhenTheSpecifiedFileIsNotAnImage() throws IOException { - try { - template.addImage(new File(".//test/unit/com/structurizr/documentation/structurizr/context.md")); - fail(); - } catch (IllegalArgumentException iae) { - assertTrue(iae.getMessage().endsWith("context.md is not a supported image file.")); - } - } - - @Test - public void test_readFiles_ThrowsAnException_WhenPassedAFileThatDoesNotExist() throws IOException { - try { - template.addContextSection(softwareSystem, new File("./no-such-file.txt")); - fail(); - } catch (IllegalArgumentException iae) { - assertTrue(iae.getMessage().endsWith("no-such-file.txt does not exist.")); - } - } - - @Test - public void test_readFiles_ThrowsAnException_WhenPassedANullFile() throws IOException { - try { - template.addContextSection(softwareSystem, (File)null); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("One or more files must be specified.", iae.getMessage()); - } - } - - @Test - public void test_readFiles_ThrowsAnException_WhenPassedAnEmptyCollection() throws IOException { - try { - template.addContextSection(softwareSystem, new File[]{}); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("One or more files must be specified.", iae.getMessage()); - } - } - - @Test - public void test_readFiles_AddsAllFiles_WhenPassedADirectory() throws IOException { - Section section = template.addContextSection(softwareSystem, new File(".//test/unit/com/structurizr/documentation/markdown")); - assertEquals("File 1" + System.lineSeparator() + - "File 2", section.getContent()); - } - -} \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/DocumentationTests.java b/structurizr-core/test/unit/com/structurizr/documentation/DocumentationTests.java index 178b5e001..1f1ee97fe 100644 --- a/structurizr-core/test/unit/com/structurizr/documentation/DocumentationTests.java +++ b/structurizr-core/test/unit/com/structurizr/documentation/DocumentationTests.java @@ -1,14 +1,9 @@ package com.structurizr.documentation; import com.structurizr.AbstractWorkspaceTestBase; -import com.structurizr.Workspace; -import com.structurizr.model.SoftwareSystem; import org.junit.Before; import org.junit.Test; -import java.util.Collections; -import java.util.Date; - import static org.junit.Assert.*; public class DocumentationTests extends AbstractWorkspaceTestBase { @@ -20,21 +15,12 @@ public void setUp() { documentation = workspace.getDocumentation(); } - @Test - public void test_addSection_ThrowsAnException_WhenTheRelatedElementIsNotPresentInTheAssociatedModel() { - try { - SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); - new Workspace("", "").getDocumentation().addSection(softwareSystem, "Title", Format.Markdown, "Content"); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("The element named Software System does not exist in the model associated with this documentation.", iae.getMessage()); - } - } - @Test public void test_addSection_ThrowsAnException_WhenTheTitleIsNotSpecified() { try { - documentation.addSection(null, null, Format.Markdown, "Content"); + Section section = new Section(); + + documentation.addSection(section); fail(); } catch (IllegalArgumentException iae) { assertEquals("A title must be specified.", iae.getMessage()); @@ -44,7 +30,10 @@ public void test_addSection_ThrowsAnException_WhenTheTitleIsNotSpecified() { @Test public void test_addSection_ThrowsAnException_WhenTheContentIsNotSpecified() { try { - documentation.addSection(null, "Title", Format.Markdown, null); + Section section = new Section(); + section.setTitle("Title"); + + documentation.addSection(section); fail(); } catch (IllegalArgumentException iae) { assertEquals("Content must be specified.", iae.getMessage()); @@ -54,7 +43,11 @@ public void test_addSection_ThrowsAnException_WhenTheContentIsNotSpecified() { @Test public void test_addSection_ThrowsAnException_WhenTheFormatIsNotSpecified() { try { - documentation.addSection(null, "Title", null, "Content"); + Section section = new Section(); + section.setTitle("Title"); + section.setContent("Content"); + + documentation.addSection(section); fail(); } catch (IllegalArgumentException iae) { assertEquals("A format must be specified.", iae.getMessage()); @@ -64,34 +57,30 @@ public void test_addSection_ThrowsAnException_WhenTheFormatIsNotSpecified() { @Test public void test_addSection_ThrowsAnException_WhenASectionExistsWithTheSameTitle() { try { - documentation.addSection(null, "Title", Format.Markdown, "Content"); - documentation.addSection(null, "Title", Format.Markdown, "Content"); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("A section with a title of Title already exists for this workspace.", iae.getMessage()); - } - } + Section section = new Section(); + section.setTitle("Title"); + section.setContent("Content"); + section.setFormat(Format.Markdown); - @Test - public void test_addSection_ThrowsAnException_WhenASectionExistsWithTheSameTitleForAnElement() { - try { - SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); - documentation.addSection(softwareSystem, "Title", Format.Markdown, "Content"); - documentation.addSection(softwareSystem, "Title", Format.Markdown, "Content"); + documentation.addSection(section); + documentation.addSection(section); fail(); } catch (IllegalArgumentException iae) { - assertEquals("A section with a title of Title already exists for the element named Software System.", iae.getMessage()); + assertEquals("A section with a title of Title already exists in this scope.", iae.getMessage()); } } @Test public void test_addSection() { - SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); - Section section = documentation.addSection(softwareSystem, "Title", Format.Markdown, "Content"); + Section section = new Section(); + section.setTitle("Title"); + section.setContent("Content"); + section.setFormat(Format.Markdown); + + documentation.addSection(section); assertEquals(1, documentation.getSections().size()); assertTrue(documentation.getSections().contains(section)); - assertSame(softwareSystem, section.getElement()); assertEquals("Title", section.getTitle()); assertEquals(Format.Markdown, section.getFormat()); assertEquals("Content", section.getContent()); @@ -100,30 +89,25 @@ public void test_addSection() { @Test public void test_addSection_IncrementsTheSectionOrderNumber() { - SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); - Section section1 = documentation.addSection(softwareSystem, "Section 1", Format.Markdown, "Content"); - Section section2 = documentation.addSection(softwareSystem, "Section 2", Format.Markdown, "Content"); - Section section3 = documentation.addSection(softwareSystem, "Section 3", Format.Markdown, "Content"); + Section section1 = new Section("Title 1", Format.Markdown, "Content"); + Section section2 = new Section("Title 2", Format.Markdown, "Content"); + Section section3 = new Section("Title 3", Format.Markdown, "Content"); + + documentation.addSection(section1); + documentation.addSection(section2); + documentation.addSection(section3); assertEquals(1, section1.getOrder()); assertEquals(2, section2.getOrder()); assertEquals(3, section3.getOrder()); } - @Test - public void test_addDecision_ThrowsAnException_WhenTheIdIsNotSpecified() { - try { - documentation.addDecision(null, new Date(), "Title", DecisionStatus.Accepted, Format.Markdown, "Content"); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("An ID must be specified.", iae.getMessage()); - } - } - @Test public void test_addDecision_ThrowsAnException_WhenTheTitleIsNotSpecified() { try { - documentation.addDecision("1", new Date(), null, DecisionStatus.Accepted, Format.Markdown, "Content"); + Decision decision = new Decision("1"); + + documentation.addDecision(decision); fail(); } catch (IllegalArgumentException iae) { assertEquals("A title must be specified.", iae.getMessage()); @@ -133,7 +117,10 @@ public void test_addDecision_ThrowsAnException_WhenTheTitleIsNotSpecified() { @Test public void test_addDecision_ThrowsAnException_WhenTheContentIsNotSpecified() { try { - documentation.addDecision("1", new Date(), "Title", DecisionStatus.Accepted, Format.Markdown, null); + Decision decision = new Decision("1"); + decision.setTitle("Title"); + + documentation.addDecision(decision); fail(); } catch (IllegalArgumentException iae) { assertEquals("Content must be specified.", iae.getMessage()); @@ -143,7 +130,11 @@ public void test_addDecision_ThrowsAnException_WhenTheContentIsNotSpecified() { @Test public void test_addDecision_ThrowsAnException_WhenTheStatusIsNotSpecified() { try { - documentation.addDecision("1", new Date(), "Title", null, Format.Markdown, "Content"); + Decision decision = new Decision("1"); + decision.setTitle("Title"); + decision.setContent("Content"); + + documentation.addDecision(decision); fail(); } catch (IllegalArgumentException iae) { assertEquals("A status must be specified.", iae.getMessage()); @@ -153,7 +144,12 @@ public void test_addDecision_ThrowsAnException_WhenTheStatusIsNotSpecified() { @Test public void test_addDecision_ThrowsAnException_WhenTheFormatIsNotSpecified() { try { - documentation.addDecision("1", new Date(), "Title", DecisionStatus.Accepted, null, "Content"); + Decision decision = new Decision("1"); + decision.setTitle("Title"); + decision.setContent("Content"); + decision.setStatus("Accepted"); + + documentation.addDecision(decision); fail(); } catch (IllegalArgumentException iae) { assertEquals("A format must be specified.", iae.getMessage()); @@ -163,43 +159,18 @@ public void test_addDecision_ThrowsAnException_WhenTheFormatIsNotSpecified() { @Test public void test_addDecision_ThrowsAnException_WhenADecisionExistsWithTheSameId() { try { - documentation.addDecision("1", new Date(), "Title", DecisionStatus.Accepted, Format.Markdown, "Content"); - documentation.addDecision("1", new Date(), "Title", DecisionStatus.Accepted, Format.Markdown, "Content"); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("A decision with an ID of 1 already exists for this workspace.", iae.getMessage()); - } - } - - @Test - public void test_addDecision_ThrowsAnException_WhenADecisionExistsWithTheSameIdForAnElement() { - try { - SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); - documentation.addDecision(softwareSystem, "1", new Date(), "Title", DecisionStatus.Accepted, Format.Markdown, "Content"); - documentation.addDecision(softwareSystem, "1", new Date(), "Title", DecisionStatus.Accepted, Format.Markdown, "Content"); + Decision decision = new Decision("1"); + decision.setTitle("Title"); + decision.setContent("Content"); + decision.setFormat(Format.Markdown); + decision.setStatus("Accepted"); + + documentation.addDecision(decision); + documentation.addDecision(decision); fail(); } catch (IllegalArgumentException iae) { - assertEquals("A decision with an ID of 1 already exists for the element named Software System.", iae.getMessage()); + assertEquals("A decision with an ID of 1 already exists in this scope.", iae.getMessage()); } } - @Test - public void test_hydrate() { - SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); - - Section section = new Section(); - section.setElementId(softwareSystem.getId()); - section.setTitle("Title"); - documentation.setSections(Collections.singleton(section)); - - Decision decision = new Decision(); - decision.setElementId(softwareSystem.getId()); - documentation.setDecisions(Collections.singleton(decision)); - - documentation.hydrate(model); - - assertSame(softwareSystem, section.getElement()); - assertSame(softwareSystem, decision.getElement()); - } - } \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/FormatFinderTests.java b/structurizr-core/test/unit/com/structurizr/documentation/FormatFinderTests.java deleted file mode 100644 index c4d1a7df6..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/FormatFinderTests.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.structurizr.documentation; - -import org.junit.Test; - -import java.io.File; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -public class FormatFinderTests { - - @Test - public void test_findFormat_ThrowsAnException_WhenAFileIsNotSpecified() { - try { - FormatFinder.findFormat(null); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("A file must be specified.", iae.getMessage()); - } - } - - @Test - public void test_findFormat_ReturnsMarkdown_WhenAMarkdownFileIsSpecified() { - assertEquals(Format.Markdown, FormatFinder.findFormat(new File("foo.md"))); - assertEquals(Format.Markdown, FormatFinder.findFormat(new File("foo.markdown"))); - assertEquals(Format.Markdown, FormatFinder.findFormat(new File("foo.text"))); - } - - @Test - public void test_findFormat_ReturnsAsciiDoc_WhenAnAsciiDocFileIsSpecified() { - assertEquals(Format.AsciiDoc, FormatFinder.findFormat(new File("foo.adoc"))); - assertEquals(Format.AsciiDoc, FormatFinder.findFormat(new File("foo.asciidoc"))); - assertEquals(Format.AsciiDoc, FormatFinder.findFormat(new File("foo.asc"))); - } - -} diff --git a/structurizr-core/test/unit/com/structurizr/documentation/SectionTests.java b/structurizr-core/test/unit/com/structurizr/documentation/SectionTests.java index 9b0bc76b0..bddc2cf47 100644 --- a/structurizr-core/test/unit/com/structurizr/documentation/SectionTests.java +++ b/structurizr-core/test/unit/com/structurizr/documentation/SectionTests.java @@ -7,10 +7,12 @@ public class SectionTests { @Test - public void test_setType_ProvidesBackwardsCompatibility() { - Section section = new Section(); - section.setType("Title"); // older clients use the type property + public void test_construction() { + Section section = new Section("Title", Format.Markdown, "Content"); + assertEquals("Title", section.getTitle()); + assertEquals(Format.Markdown, section.getFormat()); + assertEquals("Content", section.getContent()); } } \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/StructurizrDocumentationTemplateTests.java b/structurizr-core/test/unit/com/structurizr/documentation/StructurizrDocumentationTemplateTests.java deleted file mode 100644 index 466bb7ece..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/StructurizrDocumentationTemplateTests.java +++ /dev/null @@ -1,170 +0,0 @@ -package com.structurizr.documentation; - -import com.structurizr.AbstractWorkspaceTestBase; -import com.structurizr.model.Component; -import com.structurizr.model.Container; -import com.structurizr.model.Element; -import com.structurizr.model.SoftwareSystem; -import org.junit.Before; -import org.junit.Test; - -import java.io.File; -import java.io.IOException; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -public class StructurizrDocumentationTemplateTests extends AbstractWorkspaceTestBase { - - private SoftwareSystem softwareSystem; - private Container containerA; - private Container containerB; - private Component componentA1; - private Component componentA2; - private StructurizrDocumentationTemplate template; - - @Before - public void setUp() { - softwareSystem = workspace.getModel().addSoftwareSystem("Name", "Description"); - containerA = softwareSystem.addContainer("Container A", "Description", "Technology"); - containerB = softwareSystem.addContainer("Container B", "Description", "Technology"); - componentA1 = containerA.addComponent("Component A1", "Description", "Technology"); - componentA2 = containerA.addComponent("Component A2", "Description", "Technology"); - template = new StructurizrDocumentationTemplate(workspace); - } - - @Test - public void test_construction_ThrowsAnException_WhenANullWorkspaceIsSpecified() { - try { - new StructurizrDocumentationTemplate(null); - fail(); - } catch (Exception e) { - assertEquals("A workspace must be specified.", e.getMessage()); - } - } - - @Test - public void test_addAllSectionsWithContentAsStrings() { - Section section; - - section = template.addContextSection(softwareSystem, Format.Markdown, "Context section"); - assertSection(softwareSystem, "Context", Format.Markdown, "Context section", 1, section); - - section = template.addFunctionalOverviewSection(softwareSystem, Format.Markdown, "Functional overview section"); - assertSection(softwareSystem, "Functional Overview", Format.Markdown, "Functional overview section", 2, section); - - section = template.addQualityAttributesSection(softwareSystem, Format.Markdown, "Quality attributes section"); - assertSection(softwareSystem, "Quality Attributes", Format.Markdown, "Quality attributes section", 3, section); - - section = template.addConstraintsSection(softwareSystem, Format.Markdown, "Constraints section"); - assertSection(softwareSystem, "Constraints", Format.Markdown, "Constraints section", 4, section); - - section = template.addPrinciplesSection(softwareSystem, Format.Markdown, "Principles section"); - assertSection(softwareSystem, "Principles", Format.Markdown, "Principles section", 5, section); - - section = template.addSoftwareArchitectureSection(softwareSystem, Format.Markdown, "Software architecture section"); - assertSection(softwareSystem, "Software Architecture", Format.Markdown, "Software architecture section", 6, section); - - section = template.addContainersSection(softwareSystem, Format.Markdown, "Containers section"); - assertSection(softwareSystem, "Containers", Format.Markdown, "Containers section", 7, section); - - section = template.addComponentsSection(containerA, Format.Markdown, "Components section for container A"); - assertSection(containerA, "Components", Format.Markdown, "Components section for container A", 8, section); - - section = template.addComponentsSection(containerB, Format.Markdown, "Components section for container B"); - assertSection(containerB, "Components", Format.Markdown, "Components section for container B", 9, section); - - section = template.addCodeSection(componentA1, Format.Markdown, "Code section for component A1"); - assertSection(componentA1, "Code", Format.Markdown, "Code section for component A1", 10, section); - - section = template.addCodeSection(componentA2, Format.Markdown, "Code section for component A2"); - assertSection(componentA2, "Code", Format.Markdown, "Code section for component A2", 11, section); - - section = template.addDataSection(softwareSystem, Format.Markdown, "Data section"); - assertSection(softwareSystem, "Data", Format.Markdown, "Data section", 12, section); - - section = template.addInfrastructureArchitectureSection(softwareSystem, Format.Markdown, "Infrastructure architecture section"); - assertSection(softwareSystem, "Infrastructure Architecture", Format.Markdown, "Infrastructure architecture section", 13, section); - - section = template.addDeploymentSection(softwareSystem, Format.Markdown, "Deployment section"); - assertSection(softwareSystem, "Deployment", Format.Markdown, "Deployment section", 14, section); - - section = template.addDevelopmentEnvironmentSection(softwareSystem, Format.Markdown, "Development environment section"); - assertSection(softwareSystem, "Development Environment", Format.Markdown, "Development environment section", 15, section); - - section = template.addOperationAndSupportSection(softwareSystem, Format.Markdown, "Operation and support section"); - assertSection(softwareSystem, "Operation and Support", Format.Markdown, "Operation and support section", 16, section); - - section = template.addDecisionLogSection(softwareSystem, Format.Markdown, "Decision log section"); - assertSection(softwareSystem, "Decision Log", Format.Markdown, "Decision log section", 17, section); - } - - @Test - public void test_addAllSectionsWithContentFromFiles() throws IOException { - Section section; - File root = new File(".//test/unit/com/structurizr/documentation/structurizr"); - - section = template.addContextSection(softwareSystem, new File(root, "context.md")); - assertSection(softwareSystem, "Context", Format.Markdown, "Context section", 1, section); - - section = template.addFunctionalOverviewSection(softwareSystem, new File(root, "functional-overview.md")); - assertSection(softwareSystem, "Functional Overview", Format.Markdown, "Functional overview section", 2, section); - - section = template.addQualityAttributesSection(softwareSystem, new File(root, "quality-attributes.md")); - assertSection(softwareSystem, "Quality Attributes", Format.Markdown, "Quality attributes section", 3, section); - - section = template.addConstraintsSection(softwareSystem, new File(root, "constraints.md")); - assertSection(softwareSystem, "Constraints", Format.Markdown, "Constraints section", 4, section); - - section = template.addPrinciplesSection(softwareSystem, new File(root, "principles.md")); - assertSection(softwareSystem, "Principles", Format.Markdown, "Principles section", 5, section); - - section = template.addSoftwareArchitectureSection(softwareSystem, new File(root, "software-architecture.md")); - assertSection(softwareSystem, "Software Architecture", Format.Markdown, "Software architecture section", 6, section); - - section = template.addContainersSection(softwareSystem, new File(root, "containers.md")); - assertSection(softwareSystem, "Containers", Format.Markdown, "Containers section", 7, section); - - section = template.addComponentsSection(containerA, new File(root, "components-for-containerA.md")); - assertSection(containerA, "Components", Format.Markdown, "Components section for container A", 8, section); - - section = template.addComponentsSection(containerB, new File(root, "components-for-containerB.md")); - assertSection(containerB, "Components", Format.Markdown, "Components section for container B", 9, section); - - section = template.addCodeSection(componentA1, new File(root, "code-for-componentA1.md")); - assertSection(componentA1, "Code", Format.Markdown, "Code section for component A1", 10, section); - - section = template.addCodeSection(componentA2, new File(root, "code-for-componentA2.md")); - assertSection(componentA2, "Code", Format.Markdown, "Code section for component A2", 11, section); - - section = template.addDataSection(softwareSystem, new File(root, "data.md")); - assertSection(softwareSystem, "Data", Format.Markdown, "Data section", 12, section); - - section = template.addInfrastructureArchitectureSection(softwareSystem, new File(root, "infrastructure-architecture.md")); - assertSection(softwareSystem, "Infrastructure Architecture", Format.Markdown, "Infrastructure architecture section", 13, section); - - section = template.addDeploymentSection(softwareSystem, new File(root, "deployment.md")); - assertSection(softwareSystem, "Deployment", Format.Markdown, "Deployment section", 14, section); - - section = template.addDevelopmentEnvironmentSection(softwareSystem, new File(root, "development-environment.md")); - assertSection(softwareSystem, "Development Environment", Format.Markdown, "Development environment section", 15, section); - - section = template.addOperationAndSupportSection(softwareSystem, new File(root, "operation-and-support.md")); - assertSection(softwareSystem, "Operation and Support", Format.Markdown, "Operation and support section", 16, section); - - section = template.addDecisionLogSection(softwareSystem, new File(root, "decision-log.md")); - assertSection(softwareSystem, "Decision Log", Format.Markdown, "Decision log section", 17, section); - } - - private void assertSection(Element element, String title, Format format, String content, int order, Section section) { - assertTrue(workspace.getDocumentation().getSections().contains(section)); - assertEquals(element, section.getElement()); - assertEquals(element.getId(), section.getElementId()); - assertEquals(title, section.getTitle()); - assertEquals(format, section.getFormat()); - assertEquals(content, section.getContent()); - assertEquals(order, section.getOrder()); - } - -} \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/ViewpointsAndPerspectivesDocumentationTemplateTests.java b/structurizr-core/test/unit/com/structurizr/documentation/ViewpointsAndPerspectivesDocumentationTemplateTests.java deleted file mode 100644 index 5b985bdac..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/ViewpointsAndPerspectivesDocumentationTemplateTests.java +++ /dev/null @@ -1,99 +0,0 @@ -package com.structurizr.documentation; - -import com.structurizr.AbstractWorkspaceTestBase; -import com.structurizr.Workspace; -import com.structurizr.model.Element; -import com.structurizr.model.SoftwareSystem; -import org.junit.Before; -import org.junit.Test; - -import java.io.File; -import java.io.IOException; - -import static org.junit.Assert.*; - -public class ViewpointsAndPerspectivesDocumentationTemplateTests extends AbstractWorkspaceTestBase { - - private SoftwareSystem softwareSystem; - private ViewpointsAndPerspectivesDocumentationTemplate template; - - @Before - public void setUp() { - softwareSystem = workspace.getModel().addSoftwareSystem("Name", "Description"); - template = new ViewpointsAndPerspectivesDocumentationTemplate(workspace); - } - - @Test - public void test_construction_ThrowsAnException_WhenANullWorkspaceIsSpecified() { - try { - new ViewpointsAndPerspectivesDocumentationTemplate(null); - fail(); - } catch (Exception e) { - assertEquals("A workspace must be specified.", e.getMessage()); - } - } - - @Test - public void test_addAllSectionsWithContentAsStrings() { - Section section; - - section = template.addIntroductionSection(softwareSystem, Format.Markdown, "Section 1"); - assertSection(softwareSystem, "Introduction", Format.Markdown, "Section 1", 1, section); - - section = template.addGlossarySection(softwareSystem, Format.Markdown, "Section 2"); - assertSection(softwareSystem, "Glossary", Format.Markdown, "Section 2", 2, section); - - section = template.addSystemStakeholdersAndRequirementsSection(softwareSystem, Format.Markdown, "Section 3"); - assertSection(softwareSystem, "System Stakeholders and Requirements", Format.Markdown, "Section 3", 3, section); - - section = template.addArchitecturalForcesSection(softwareSystem, Format.Markdown, "Section 4"); - assertSection(softwareSystem, "Architectural Forces", Format.Markdown, "Section 4", 4, section); - - section = template.addArchitecturalViewsSection(softwareSystem, Format.Markdown, "Section 5"); - assertSection(softwareSystem, "Architectural Views", Format.Markdown, "Section 5", 5, section); - - section = template.addSystemQualitiesSection(softwareSystem, Format.Markdown, "Section 6"); - assertSection(softwareSystem, "System Qualities", Format.Markdown, "Section 6", 6, section); - - section = template.addAppendicesSection(softwareSystem, Format.Markdown, "Section 7"); - assertSection(softwareSystem, "Appendices", Format.Markdown, "Section 7", 7, section); - } - - @Test - public void test_addAllSectionsWithContentFromFiles() throws IOException { - Section section; - File root = new File(".//test/unit/com/structurizr/documentation/viewpointsandperspectives"); - - section = template.addIntroductionSection(softwareSystem, new File(root, "01-introduction.md")); - assertSection(softwareSystem, "Introduction", Format.Markdown, "Section 1", 1, section); - - section = template.addGlossarySection(softwareSystem, new File(root, "02-glossary.md")); - assertSection(softwareSystem, "Glossary", Format.Markdown, "Section 2", 2, section); - - section = template.addSystemStakeholdersAndRequirementsSection(softwareSystem, new File(root, "03-system-stakeholders-and-requirements.md")); - assertSection(softwareSystem, "System Stakeholders and Requirements", Format.Markdown, "Section 3", 3, section); - - section = template.addArchitecturalForcesSection(softwareSystem, new File(root, "04-architectural-forces.md")); - assertSection(softwareSystem, "Architectural Forces", Format.Markdown, "Section 4", 4, section); - - section = template.addArchitecturalViewsSection(softwareSystem, new File(root, "05-architectural-views.md")); - assertSection(softwareSystem, "Architectural Views", Format.Markdown, "Section 5", 5, section); - - section = template.addSystemQualitiesSection(softwareSystem, new File(root, "06-system-qualities.md")); - assertSection(softwareSystem, "System Qualities", Format.Markdown, "Section 6", 6, section); - - section = template.addAppendicesSection(softwareSystem, new File(root, "07-appendices.adoc")); - assertSection(softwareSystem, "Appendices", Format.AsciiDoc, "Section 7", 7, section); - } - - private void assertSection(Element element, String title, Format format, String content, int order, Section section) { - assertTrue(workspace.getDocumentation().getSections().contains(section)); - assertEquals(element, section.getElement()); - assertEquals(element.getId(), section.getElementId()); - assertEquals(title, section.getTitle()); - assertEquals(format, section.getFormat()); - assertEquals(content, section.getContent()); - assertEquals(order, section.getOrder()); - } - -} \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/arc42/architectural-decisions.md b/structurizr-core/test/unit/com/structurizr/documentation/arc42/architectural-decisions.md deleted file mode 100644 index 5373ebb11..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/arc42/architectural-decisions.md +++ /dev/null @@ -1 +0,0 @@ -Section 9 \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/arc42/building-block-view.md b/structurizr-core/test/unit/com/structurizr/documentation/arc42/building-block-view.md deleted file mode 100644 index ad5da1b79..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/arc42/building-block-view.md +++ /dev/null @@ -1 +0,0 @@ -Section 5 \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/arc42/constraints.md b/structurizr-core/test/unit/com/structurizr/documentation/arc42/constraints.md deleted file mode 100644 index e21243ae1..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/arc42/constraints.md +++ /dev/null @@ -1 +0,0 @@ -Section 2 \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/arc42/context-and-scope.md b/structurizr-core/test/unit/com/structurizr/documentation/arc42/context-and-scope.md deleted file mode 100644 index bfda5c524..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/arc42/context-and-scope.md +++ /dev/null @@ -1 +0,0 @@ -Section 3 \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/arc42/crosscutting-concepts.md b/structurizr-core/test/unit/com/structurizr/documentation/arc42/crosscutting-concepts.md deleted file mode 100644 index f2cb8a961..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/arc42/crosscutting-concepts.md +++ /dev/null @@ -1 +0,0 @@ -Section 8 \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/arc42/deployment-view.md b/structurizr-core/test/unit/com/structurizr/documentation/arc42/deployment-view.md deleted file mode 100644 index df62d0d99..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/arc42/deployment-view.md +++ /dev/null @@ -1 +0,0 @@ -Section 7 \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/arc42/glossary.md b/structurizr-core/test/unit/com/structurizr/documentation/arc42/glossary.md deleted file mode 100644 index 4f640e7c6..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/arc42/glossary.md +++ /dev/null @@ -1 +0,0 @@ -Section 12 \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/arc42/introduction-and-goals.md b/structurizr-core/test/unit/com/structurizr/documentation/arc42/introduction-and-goals.md deleted file mode 100644 index 08d597adb..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/arc42/introduction-and-goals.md +++ /dev/null @@ -1 +0,0 @@ -Section 1 \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/arc42/quality-requirements.md b/structurizr-core/test/unit/com/structurizr/documentation/arc42/quality-requirements.md deleted file mode 100644 index 822a2f582..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/arc42/quality-requirements.md +++ /dev/null @@ -1 +0,0 @@ -Section 10 \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/arc42/risks-and-technical-debt.md b/structurizr-core/test/unit/com/structurizr/documentation/arc42/risks-and-technical-debt.md deleted file mode 100644 index 84f6ede49..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/arc42/risks-and-technical-debt.md +++ /dev/null @@ -1 +0,0 @@ -Section 11 \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/arc42/runtime-view.md b/structurizr-core/test/unit/com/structurizr/documentation/arc42/runtime-view.md deleted file mode 100644 index 185ac75f2..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/arc42/runtime-view.md +++ /dev/null @@ -1 +0,0 @@ -Section 6 \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/arc42/solution-strategy.md b/structurizr-core/test/unit/com/structurizr/documentation/arc42/solution-strategy.md deleted file mode 100644 index 276e6c9c2..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/arc42/solution-strategy.md +++ /dev/null @@ -1 +0,0 @@ -Section 4 \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/automatic/01-section-1.md b/structurizr-core/test/unit/com/structurizr/documentation/automatic/01-section-1.md deleted file mode 100644 index 4721380c0..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/automatic/01-section-1.md +++ /dev/null @@ -1 +0,0 @@ -## Section 1 \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/automatic/02-section-2.markdown b/structurizr-core/test/unit/com/structurizr/documentation/automatic/02-section-2.markdown deleted file mode 100644 index 2fc0e3f87..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/automatic/02-section-2.markdown +++ /dev/null @@ -1 +0,0 @@ -## Section 2 \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/automatic/03-section-3.text b/structurizr-core/test/unit/com/structurizr/documentation/automatic/03-section-3.text deleted file mode 100644 index e847bfa94..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/automatic/03-section-3.text +++ /dev/null @@ -1 +0,0 @@ -## Section 3 \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/automatic/04-section-4.adoc b/structurizr-core/test/unit/com/structurizr/documentation/automatic/04-section-4.adoc deleted file mode 100644 index 062d5dd9a..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/automatic/04-section-4.adoc +++ /dev/null @@ -1 +0,0 @@ -== Section 4 \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/automatic/05-section-5.asciidoc b/structurizr-core/test/unit/com/structurizr/documentation/automatic/05-section-5.asciidoc deleted file mode 100644 index 1a501bdbf..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/automatic/05-section-5.asciidoc +++ /dev/null @@ -1 +0,0 @@ -== Section 5 \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/automatic/06-section-6.asc b/structurizr-core/test/unit/com/structurizr/documentation/automatic/06-section-6.asc deleted file mode 100644 index 8728c5661..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/automatic/06-section-6.asc +++ /dev/null @@ -1 +0,0 @@ -== Section 6 \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/automatic/07-subdirectory/01-section-1.md b/structurizr-core/test/unit/com/structurizr/documentation/automatic/07-subdirectory/01-section-1.md deleted file mode 100644 index af328a67d..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/automatic/07-subdirectory/01-section-1.md +++ /dev/null @@ -1 +0,0 @@ -## Section 7 \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/image.gif b/structurizr-core/test/unit/com/structurizr/documentation/image.gif deleted file mode 100644 index c06542adaf2a63a53499dfe7bedc85e442e8d2b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4682 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 fQ@dnt$vnQ|8D{SZ|*E1 diff --git a/structurizr-core/test/unit/com/structurizr/documentation/image.jpg b/structurizr-core/test/unit/com/structurizr/documentation/image.jpg deleted file mode 100644 index 0b9e9c39da3f93ae710234a051f94428f6c66ff3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1467 zcmex=kR1VRQz zDF#+Bn}NZLQ5wz;V$^`DVPart&tzbMs)+*9AOJKOq#H^zFJMHNxPS?+T44b*oGk^? z_Wvz|MLoA#DyHnP8$!323`E1Vw_ae#K|QlE+HwU zs-~`?sbyknW^Q3==I-I?6&w;879J59m7J2AmY$KBRa{b9R$ftA)!fqB*51+C zHEHscsne#9glAUcUPH>GPMb-@gC&`3vMPV0c2j1tcLpL-Us)&|gd}EX*wIAb&A3m4iGk z$ik{<$R^|%$evgztYp;4A>uS~;l_iU%Emz-M3agxa*3&!JXHM%@*3D@#CfcVET6$W zhVa*I24@B)Fkof^#u76#Fs4|5fR&vU1lT#)!GHq_xWIrL1bE@#KhO|Hpcz2TY(T`z z$<7IvBbxF5E-+a#{lCS)!^{XwOw57|_6&cDK2Go1Zs%jMqV6FfsF9&`z~p)gfaUG|*z zH#xBr1v`FRo2C@hleuDl+1>oRcE_LAc+P!P5VTlIFlegVKkYBu({|6FCI4adKE?kG z7q}k(`{($d;ds`&UiBmQo9`)Hw#i?n`>(NndF)}+wQr1bk8Ipg(33dfX?&if{L@mE z@b~(EG~c($f6=}1pW#05e}<#s&#%AT{NOD6!CUT=AO0@P&aIkO&c>>;$YXj&%Q8;| z1LeMkFR$aa8MhgoUv+h^=J{EMQ>OmRd9yw}W#;q5if+SSXEm<$Sf;AX-MMbv+tTu9 z_5UyY@!Hc=F{!#zIdy87i^kz>_L!9t76*Aals8J4{+j;5{ZW1UAH_9}kEA@3&nOcS=vSIAPQE=4jzo&BRrgmbY)7lCti3b!_Cz fQ@dnt$vnQ|8D{SZ|*E1 diff --git a/structurizr-core/test/unit/com/structurizr/documentation/image.png b/structurizr-core/test/unit/com/structurizr/documentation/image.png deleted file mode 100644 index 644aef77b1dfbe2bad3a82b5e15421d22f95a5e1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1563 zcmeAS@N?(olHy`uVBq!ia0vp^3P3Ey!3HF+&5pANQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fIRt zPXT0ZVp4u-iLH_n$Rap^xU(cP4PjGWG1OZ?59)(t^bPe4^x1XJ8hJbBT*g_UUO;IJRJu zciBOKlo>nx&c7+~FP(n><-D1)WiOV6-}S0mwru&ipZkhe1}_hhXnS^AJN)C%pQnHP z+8rNID$;Z!!MrUx=*6)EC%21zJh{`@qWYQ1mDcX*XLk0VV|rzvwb_*Amcz=s4eUH` zi#(Kaa+vn+RxtC5js5lIyS1#0jag@!bxZ17A*)=W!zDQe|1a6)nB+*kc1}z?IYsw_ zdkn*${gb?%r6;=_-8QM<#?(m-vug@YtJQAmQNE?VWi}obe2sd^`_rXPRSRmpQ(R*E^A<}@vgo%*nN#>>+lxFC zGWJ#|mS!5-v8iP!@A$6!CI9+@Z|5dYIj88bUHZS`o5jl??EP8DrKh-HH_NnhEVehk z$CY_-aJSib+8MBXdvZEMIO6_p#?bjE6&G+^tYRr|oMs(5V}r9v_bVQmNjEx@S`^K< zzFB+6v8b zyX{HJ?Y|e_JV;yI%sHLy!(5@cLN(_$7d!AwZc%=q@a9RgJ!f4~8UMZ7=NwsEi`KpU zwA@0Y%j3s4E4}-59g^o=HLSHdqvRS5uV%hcWxQASSHWg|m%@v$YvrT=T;aa0m-zCk z<)1D0j(7sL7u1G+?+$KoH?2ka*sMZd))i?wCohc>9OqFGzBZ${SO=xQoq$Tal#>G=TFMAmlAXH*nT{`W)=2VfW4yW<&O1}U*~3u%)iTi$2A{8!q| z+xOwi8&5g4kEX6sH5R<`&yq#^biuOt#?YJWuK59cmrd8D>(zYj;BmiW$z?a^@+l+l zk3He)JDm3X6t^m$9yl+;Vfxi8b?<*<|9JK4yJv9K{+su*&feI#z^XIjEnCg81GXhh zVK==eb!^aCexPMXO~;ovofVEn=cmUheYm*LvH0Nn9siFUTlrml{`G0WlVSy}c+1@Q zgIWSzF2<#({G9S^!tX;ihxn{lzmtvm@kr>(gQQQA`fArV>#1FuqQ<*aLCp2;IiG{N q8-<@;tohWdyk|zuiSCo^KiE&kES&#X{o@T#5$x&e=d#Wzp$P!Pn~`<^ diff --git a/structurizr-core/test/unit/com/structurizr/documentation/images/image.gif b/structurizr-core/test/unit/com/structurizr/documentation/images/image.gif deleted file mode 100644 index c06542adaf2a63a53499dfe7bedc85e442e8d2b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4682 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 fQ@dnt$vnQ|8D{SZ|*E1 diff --git a/structurizr-core/test/unit/com/structurizr/documentation/images/image.jpg b/structurizr-core/test/unit/com/structurizr/documentation/images/image.jpg deleted file mode 100644 index 0b9e9c39da3f93ae710234a051f94428f6c66ff3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1467 zcmex=kR1VRQz zDF#+Bn}NZLQ5wz;V$^`DVPart&tzbMs)+*9AOJKOq#H^zFJMHNxPS?+T44b*oGk^? z_Wvz|MLoA#DyHnP8$!323`E1Vw_ae#K|QlE+HwU zs-~`?sbyknW^Q3==I-I?6&w;879J59m7J2AmY$KBRa{b9R$ftA)!fqB*51+C zHEHscsne#9glAUcUPH>GPMb-@gC&`3vMPV0c2j1tcLpL-Us)&|gd}EX*wIAb&A3m4iGk z$ik{<$R^|%$evgztYp;4A>uS~;l_iU%Emz-M3agxa*3&!JXHM%@*3D@#CfcVET6$W zhVa*I24@B)Fkof^#u76#Fs4|5fR&vU1lT#)!GHq_xWIrL1bE@#KhO|Hpcz2TY(T`z z$<7IvBbxF5E-+a#{lCS)!^{XwOw57|_6&cDK2Go1Zs%jMqV6FfsF9&`z~p)gfaUG|*z zH#xBr1v`FRo2C@hleuDl+1>oRcE_LAc+P!P5VTlIFlegVKkYBu({|6FCI4adKE?kG z7q}k(`{($d;ds`&UiBmQo9`)Hw#i?n`>(NndF)}+wQr1bk8Ipg(33dfX?&if{L@mE z@b~(EG~c($f6=}1pW#05e}<#s&#%AT{NOD6!CUT=AO0@P&aIkO&c>>;$YXj&%Q8;| z1LeMkFR$aa8MhgoUv+h^=J{EMQ>OmRd9yw}W#;q5if+SSXEm<$Sf;AX-MMbv+tTu9 z_5UyY@!Hc=F{!#zIdy87i^kz>_L!9t76*Aals8J4{+j;5{ZW1UAH_9}kEA@3&nOcS=vSIAPQE=4jzo&BRrgmbY)7lCti3b!_Cz fQ@dnt$vnQ|8D{SZ|*E1 diff --git a/structurizr-core/test/unit/com/structurizr/documentation/images/image.png b/structurizr-core/test/unit/com/structurizr/documentation/images/image.png deleted file mode 100644 index 644aef77b1dfbe2bad3a82b5e15421d22f95a5e1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1563 zcmeAS@N?(olHy`uVBq!ia0vp^3P3Ey!3HF+&5pANQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fIRt zPXT0ZVp4u-iLH_n$Rap^xU(cP4PjGWG1OZ?59)(t^bPe4^x1XJ8hJbBT*g_UUO;IJRJu zciBOKlo>nx&c7+~FP(n><-D1)WiOV6-}S0mwru&ipZkhe1}_hhXnS^AJN)C%pQnHP z+8rNID$;Z!!MrUx=*6)EC%21zJh{`@qWYQ1mDcX*XLk0VV|rzvwb_*Amcz=s4eUH` zi#(Kaa+vn+RxtC5js5lIyS1#0jag@!bxZ17A*)=W!zDQe|1a6)nB+*kc1}z?IYsw_ zdkn*${gb?%r6;=_-8QM<#?(m-vug@YtJQAmQNE?VWi}obe2sd^`_rXPRSRmpQ(R*E^A<}@vgo%*nN#>>+lxFC zGWJ#|mS!5-v8iP!@A$6!CI9+@Z|5dYIj88bUHZS`o5jl??EP8DrKh-HH_NnhEVehk z$CY_-aJSib+8MBXdvZEMIO6_p#?bjE6&G+^tYRr|oMs(5V}r9v_bVQmNjEx@S`^K< zzFB+6v8b zyX{HJ?Y|e_JV;yI%sHLy!(5@cLN(_$7d!AwZc%=q@a9RgJ!f4~8UMZ7=NwsEi`KpU zwA@0Y%j3s4E4}-59g^o=HLSHdqvRS5uV%hcWxQASSHWg|m%@v$YvrT=T;aa0m-zCk z<)1D0j(7sL7u1G+?+$KoH?2ka*sMZd))i?wCohc>9OqFGzBZ${SO=xQoq$Tal#>G=TFMAmlAXH*nT{`W)=2VfW4yW<&O1}U*~3u%)iTi$2A{8!q| z+xOwi8&5g4kEX6sH5R<`&yq#^biuOt#?YJWuK59cmrd8D>(zYj;BmiW$z?a^@+l+l zk3He)JDm3X6t^m$9yl+;Vfxi8b?<*<|9JK4yJv9K{+su*&feI#z^XIjEnCg81GXhh zVK==eb!^aCexPMXO~;ovofVEn=cmUheYm*LvH0Nn9siFUTlrml{`G0WlVSy}c+1@Q zgIWSzF2<#({G9S^!tX;ihxn{lzmtvm@kr>(gQQQA`fArV>#1FuqQ<*aLCp2;IiG{N q8-<@;tohWdyk|zuiSCo^KiE&kES&#X{o@T#5$x&e=d#Wzp$P!Pn~`<^ diff --git a/structurizr-core/test/unit/com/structurizr/documentation/markdown/1.md b/structurizr-core/test/unit/com/structurizr/documentation/markdown/1.md deleted file mode 100644 index 49351eb5b..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/markdown/1.md +++ /dev/null @@ -1 +0,0 @@ -File 1 \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/markdown/subdirectory/2.md b/structurizr-core/test/unit/com/structurizr/documentation/markdown/subdirectory/2.md deleted file mode 100644 index 9fbb45ed0..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/markdown/subdirectory/2.md +++ /dev/null @@ -1 +0,0 @@ -File 2 \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/noimages/readme.md b/structurizr-core/test/unit/com/structurizr/documentation/noimages/readme.md deleted file mode 100644 index 6e5b3dd54..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/noimages/readme.md +++ /dev/null @@ -1 +0,0 @@ -These are not the images you are looking for... diff --git a/structurizr-core/test/unit/com/structurizr/documentation/structurizr/code-for-componentA1.md b/structurizr-core/test/unit/com/structurizr/documentation/structurizr/code-for-componentA1.md deleted file mode 100644 index 21243abd8..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/structurizr/code-for-componentA1.md +++ /dev/null @@ -1 +0,0 @@ -Code section for component A1 \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/structurizr/code-for-componentA2.md b/structurizr-core/test/unit/com/structurizr/documentation/structurizr/code-for-componentA2.md deleted file mode 100644 index 24b5bae70..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/structurizr/code-for-componentA2.md +++ /dev/null @@ -1 +0,0 @@ -Code section for component A2 \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/structurizr/components-for-containerA.md b/structurizr-core/test/unit/com/structurizr/documentation/structurizr/components-for-containerA.md deleted file mode 100644 index 6fa1b4642..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/structurizr/components-for-containerA.md +++ /dev/null @@ -1 +0,0 @@ -Components section for container A \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/structurizr/components-for-containerB.md b/structurizr-core/test/unit/com/structurizr/documentation/structurizr/components-for-containerB.md deleted file mode 100644 index 2a3018378..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/structurizr/components-for-containerB.md +++ /dev/null @@ -1 +0,0 @@ -Components section for container B \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/structurizr/constraints.md b/structurizr-core/test/unit/com/structurizr/documentation/structurizr/constraints.md deleted file mode 100644 index d74f7cb80..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/structurizr/constraints.md +++ /dev/null @@ -1 +0,0 @@ -Constraints section \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/structurizr/containers.md b/structurizr-core/test/unit/com/structurizr/documentation/structurizr/containers.md deleted file mode 100644 index 6650fca08..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/structurizr/containers.md +++ /dev/null @@ -1 +0,0 @@ -Containers section \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/structurizr/context.md b/structurizr-core/test/unit/com/structurizr/documentation/structurizr/context.md deleted file mode 100644 index a0a45438a..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/structurizr/context.md +++ /dev/null @@ -1 +0,0 @@ -Context section \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/structurizr/data.md b/structurizr-core/test/unit/com/structurizr/documentation/structurizr/data.md deleted file mode 100644 index 414410448..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/structurizr/data.md +++ /dev/null @@ -1 +0,0 @@ -Data section \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/structurizr/decision-log.md b/structurizr-core/test/unit/com/structurizr/documentation/structurizr/decision-log.md deleted file mode 100644 index 8164d68ab..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/structurizr/decision-log.md +++ /dev/null @@ -1 +0,0 @@ -Decision log section \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/structurizr/deployment.md b/structurizr-core/test/unit/com/structurizr/documentation/structurizr/deployment.md deleted file mode 100644 index 7a132359f..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/structurizr/deployment.md +++ /dev/null @@ -1 +0,0 @@ -Deployment section \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/structurizr/development-environment.md b/structurizr-core/test/unit/com/structurizr/documentation/structurizr/development-environment.md deleted file mode 100644 index c5bfe01c5..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/structurizr/development-environment.md +++ /dev/null @@ -1 +0,0 @@ -Development environment section \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/structurizr/functional-overview.md b/structurizr-core/test/unit/com/structurizr/documentation/structurizr/functional-overview.md deleted file mode 100644 index 206fe2125..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/structurizr/functional-overview.md +++ /dev/null @@ -1 +0,0 @@ -Functional overview section \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/structurizr/infrastructure-architecture.md b/structurizr-core/test/unit/com/structurizr/documentation/structurizr/infrastructure-architecture.md deleted file mode 100644 index b4b913086..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/structurizr/infrastructure-architecture.md +++ /dev/null @@ -1 +0,0 @@ -Infrastructure architecture section \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/structurizr/operation-and-support.md b/structurizr-core/test/unit/com/structurizr/documentation/structurizr/operation-and-support.md deleted file mode 100644 index 16cd2b445..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/structurizr/operation-and-support.md +++ /dev/null @@ -1 +0,0 @@ -Operation and support section \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/structurizr/principles.md b/structurizr-core/test/unit/com/structurizr/documentation/structurizr/principles.md deleted file mode 100644 index 28b7bee70..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/structurizr/principles.md +++ /dev/null @@ -1 +0,0 @@ -Principles section \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/structurizr/quality-attributes.md b/structurizr-core/test/unit/com/structurizr/documentation/structurizr/quality-attributes.md deleted file mode 100644 index 53e4c95aa..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/structurizr/quality-attributes.md +++ /dev/null @@ -1 +0,0 @@ -Quality attributes section \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/structurizr/software-architecture.md b/structurizr-core/test/unit/com/structurizr/documentation/structurizr/software-architecture.md deleted file mode 100644 index 412625abf..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/structurizr/software-architecture.md +++ /dev/null @@ -1 +0,0 @@ -Software architecture section \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/viewpointsandperspectives/01-introduction.md b/structurizr-core/test/unit/com/structurizr/documentation/viewpointsandperspectives/01-introduction.md deleted file mode 100644 index 08d597adb..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/viewpointsandperspectives/01-introduction.md +++ /dev/null @@ -1 +0,0 @@ -Section 1 \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/viewpointsandperspectives/02-glossary.md b/structurizr-core/test/unit/com/structurizr/documentation/viewpointsandperspectives/02-glossary.md deleted file mode 100644 index e21243ae1..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/viewpointsandperspectives/02-glossary.md +++ /dev/null @@ -1 +0,0 @@ -Section 2 \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/viewpointsandperspectives/03-system-stakeholders-and-requirements.md b/structurizr-core/test/unit/com/structurizr/documentation/viewpointsandperspectives/03-system-stakeholders-and-requirements.md deleted file mode 100644 index bfda5c524..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/viewpointsandperspectives/03-system-stakeholders-and-requirements.md +++ /dev/null @@ -1 +0,0 @@ -Section 3 \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/viewpointsandperspectives/04-architectural-forces.md b/structurizr-core/test/unit/com/structurizr/documentation/viewpointsandperspectives/04-architectural-forces.md deleted file mode 100644 index 276e6c9c2..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/viewpointsandperspectives/04-architectural-forces.md +++ /dev/null @@ -1 +0,0 @@ -Section 4 \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/viewpointsandperspectives/05-architectural-views.md b/structurizr-core/test/unit/com/structurizr/documentation/viewpointsandperspectives/05-architectural-views.md deleted file mode 100644 index ad5da1b79..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/viewpointsandperspectives/05-architectural-views.md +++ /dev/null @@ -1 +0,0 @@ -Section 5 \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/viewpointsandperspectives/06-system-qualities.md b/structurizr-core/test/unit/com/structurizr/documentation/viewpointsandperspectives/06-system-qualities.md deleted file mode 100644 index 185ac75f2..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/viewpointsandperspectives/06-system-qualities.md +++ /dev/null @@ -1 +0,0 @@ -Section 6 \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/viewpointsandperspectives/07-appendices.adoc b/structurizr-core/test/unit/com/structurizr/documentation/viewpointsandperspectives/07-appendices.adoc deleted file mode 100644 index df62d0d99..000000000 --- a/structurizr-core/test/unit/com/structurizr/documentation/viewpointsandperspectives/07-appendices.adoc +++ /dev/null @@ -1 +0,0 @@ -Section 7 \ No newline at end of file From 1948cc9efc8773854cdcc793f5b3a342bfa908ca Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 24 Feb 2022 12:23:49 +0000 Subject: [PATCH 283/717] Tidy up of documentation API. --- .../structurizr/documentation/Decision.java | 100 ++++++++++-------- .../documentation/DocumentationContent.java | 71 +++++++++++++ .../com/structurizr/documentation/Image.java | 2 +- .../structurizr/documentation/Section.java | 47 ++------ 4 files changed, 136 insertions(+), 84 deletions(-) create mode 100644 structurizr-core/src/com/structurizr/documentation/DocumentationContent.java diff --git a/structurizr-core/src/com/structurizr/documentation/Decision.java b/structurizr-core/src/com/structurizr/documentation/Decision.java index 056926a91..432969e76 100644 --- a/structurizr-core/src/com/structurizr/documentation/Decision.java +++ b/structurizr-core/src/com/structurizr/documentation/Decision.java @@ -1,8 +1,5 @@ package com.structurizr.documentation; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.structurizr.model.Element; - import java.util.Date; import java.util.HashSet; import java.util.Set; @@ -10,17 +7,11 @@ /** * Represents a single (architecture) decision, as described at http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions */ -public final class Decision { - - // elementId is here for backwards compatibility - private String elementId; +public final class Decision extends DocumentationContent { private String id; private Date date; - private String title; private String status; - private String content; - private Format format; private Set links = new HashSet<>(); @@ -31,14 +22,11 @@ public Decision(String id) { this.id = id; } - public String getElementId() { - return elementId; - } - - void setElementId(String elementId) { - this.elementId = elementId; - } - + /** + * Gets the ID of this decision. + * + * @return the ID, as a String + */ public String getId() { return id; } @@ -47,6 +35,11 @@ void setId(String id) { this.id = id; } + /** + * Gets the date of this decision. + * + * @return a Date object + */ public Date getDate() { return date; } @@ -55,14 +48,11 @@ public void setDate(Date date) { this.date = date; } - public String getTitle() { - return title; - } - - public void setTitle(String title) { - this.title = title; - } - + /** + * Gets the status of this decision. + * + * @return the status, as a String + */ public String getStatus() { return status; } @@ -71,22 +61,11 @@ public void setStatus(String status) { this.status = status; } - public String getContent() { - return content; - } - - public void setContent(String content) { - this.content = content; - } - - public Format getFormat() { - return format; - } - - public void setFormat(Format format) { - this.format = format; - } - + /** + * Gets the set of links from this decision. + * + * @return a Set of Link objects + */ public Set getLinks() { return new HashSet<>(links); } @@ -95,14 +74,51 @@ void setLinks(Set links) { this.links = links; } + /** + * Adds a link between this decision and another. + * + * @param decision the Decision to link to + * @param type the "type" of the link (e.g. "superseded by") + */ public void addLink(Decision decision, String type) { if (!decision.getId().equals(this.getId())) { links.add(new Link(decision.getId(), type)); } } + /** + * Determines whether a decision already has a link to another decision + * + * @param decision the Decision to check against + * @return true if a link exists, false otherwise + */ public boolean hasLinkTo(Decision decision) { return links.stream().anyMatch(l -> l.getId().equals(decision.getId())); } + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + + if (object == null || getClass() != object.getClass()) { + return false; + } + + Decision decision = (Decision)object; + if (getElementId() != null) { + return getElementId().equals(decision.getElementId()) && getId().equals(decision.getId()); + } else { + return getId().equals(decision.getId()); + } + } + + @Override + public int hashCode() { + int result = getElementId() != null ? getElementId().hashCode() : 0; + result = 31 * result + getId().hashCode(); + return result; + } + } \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/documentation/DocumentationContent.java b/structurizr-core/src/com/structurizr/documentation/DocumentationContent.java new file mode 100644 index 000000000..5013ce3a7 --- /dev/null +++ b/structurizr-core/src/com/structurizr/documentation/DocumentationContent.java @@ -0,0 +1,71 @@ +package com.structurizr.documentation; + +/** + * Represents a piece of documentation content ... a section or a decision. + */ +public abstract class DocumentationContent { + + // elementId is here for backwards compatibility + private String elementId; + + private String title; + private String content; + private Format format; + + DocumentationContent() { + } + + /** + * Gets the ID of the element that this documentation content is associated with. + * Please note this is unused, and only here for backwards compatibility. + * + * @return the element ID, as a String + */ + public String getElementId() { + return elementId; + } + + void setElementId(String elementId) { + this.elementId = elementId; + } + + /** + * Gets the title. + * + * @return the title, as a String + */ + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + /** + * Gets the content. + * + * @return the content, as a String + */ + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + /** + * Gets the format of this content. + * + * @return Markdown or AsciiDoc + */ + public Format getFormat() { + return format; + } + + public void setFormat(Format format) { + this.format = format; + } + +} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/documentation/Image.java b/structurizr-core/src/com/structurizr/documentation/Image.java index b750faeea..b76845f13 100644 --- a/structurizr-core/src/com/structurizr/documentation/Image.java +++ b/structurizr-core/src/com/structurizr/documentation/Image.java @@ -12,7 +12,7 @@ public final class Image { Image() { } - Image(String name, String type, String content) { + public Image(String name, String type, String content) { this.name = name; this.type = type; this.content = content; diff --git a/structurizr-core/src/com/structurizr/documentation/Section.java b/structurizr-core/src/com/structurizr/documentation/Section.java index fe57983d3..8cbb4ac2e 100644 --- a/structurizr-core/src/com/structurizr/documentation/Section.java +++ b/structurizr-core/src/com/structurizr/documentation/Section.java @@ -3,39 +3,20 @@ /** * A documentation section. */ -public final class Section { - - // elementId is here for backwards compatibility - private String elementId; +public final class Section extends DocumentationContent { private String title; private int order; private Format format; private String content; - Section() { - } - - Section(String title, Format format, String content) { - this.title = title; - this.format = format; - this.content = content; - } - - public String getElementId() { - return elementId; - } - - void setElementId(String elementId) { - this.elementId = elementId; - } - - public String getTitle() { - return title; + public Section() { } - void setTitle(String title) { - this.title = title; + public Section(String title, Format format, String content) { + setTitle(title); + setFormat(format); + setContent(content); } public int getOrder() { @@ -46,22 +27,6 @@ void setOrder(int order) { this.order = order; } - public Format getFormat() { - return format; - } - - void setFormat(Format format) { - this.format = format; - } - - public String getContent() { - return content; - } - - public void setContent(String content) { - this.content = content; - } - @Override public boolean equals(Object object) { if (this == object) { From 79fb2d77ed4a6163048fdaee9d57abb941b1776e Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 24 Feb 2022 22:14:42 +0000 Subject: [PATCH 284/717] Tidy up of documentation API. --- structurizr-core/src/com/structurizr/Workspace.java | 2 +- .../src/com/structurizr/model/Documentable.java | 9 +++++++++ .../src/com/structurizr/model/SoftwareSystem.java | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 structurizr-core/src/com/structurizr/model/Documentable.java diff --git a/structurizr-core/src/com/structurizr/Workspace.java b/structurizr-core/src/com/structurizr/Workspace.java index 9e3854f50..285bafe44 100644 --- a/structurizr-core/src/com/structurizr/Workspace.java +++ b/structurizr-core/src/com/structurizr/Workspace.java @@ -18,7 +18,7 @@ * Represents a Structurizr workspace, which is a wrapper for a * software architecture model, views and documentation. */ -public final class Workspace extends AbstractWorkspace { +public final class Workspace extends AbstractWorkspace implements Documentable { private static final Log log = LogFactory.getLog(Workspace.class); diff --git a/structurizr-core/src/com/structurizr/model/Documentable.java b/structurizr-core/src/com/structurizr/model/Documentable.java new file mode 100644 index 000000000..2568aa120 --- /dev/null +++ b/structurizr-core/src/com/structurizr/model/Documentable.java @@ -0,0 +1,9 @@ +package com.structurizr.model; + +import com.structurizr.documentation.Documentation; + +public interface Documentable { + + Documentation getDocumentation(); + +} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/model/SoftwareSystem.java b/structurizr-core/src/com/structurizr/model/SoftwareSystem.java index f1ff2b57c..07a0e84fd 100644 --- a/structurizr-core/src/com/structurizr/model/SoftwareSystem.java +++ b/structurizr-core/src/com/structurizr/model/SoftwareSystem.java @@ -13,7 +13,7 @@ /** * Represents a "software system" in the C4 model. */ -public final class SoftwareSystem extends StaticStructureElement { +public final class SoftwareSystem extends StaticStructureElement implements Documentable { private Location location = Location.Unspecified; From 1846f9bf86fff0ca0aabf32a8cc59f166dbe1034 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 28 Feb 2022 15:20:41 +0000 Subject: [PATCH 285/717] Removes support for the "origin ID" concept (this was used by the workspace editor, which has now been discontinued). --- .../src/com/structurizr/model/ModelItem.java | 9 --------- 1 file changed, 9 deletions(-) diff --git a/structurizr-core/src/com/structurizr/model/ModelItem.java b/structurizr-core/src/com/structurizr/model/ModelItem.java index 764264e3e..3ebc76107 100644 --- a/structurizr-core/src/com/structurizr/model/ModelItem.java +++ b/structurizr-core/src/com/structurizr/model/ModelItem.java @@ -13,7 +13,6 @@ public abstract class ModelItem implements PropertyHolder { private String id = ""; - private String originId = ""; private Set tags = new LinkedHashSet<>(); private String url; @@ -39,14 +38,6 @@ void setId(String id) { this.id = id; } - public String getOriginId() { - return originId; - } - - void setOriginId(String originId) { - this.originId = originId; - } - /** * Gets the comma separated list of tags. * From 23da5f32070899f82d79745795a384aab217afc0 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 28 Feb 2022 15:38:13 +0000 Subject: [PATCH 286/717] Tidy up of documentation API. --- .../structurizr/documentation/Decision.java | 56 +++++++++++++++++++ .../com/structurizr/documentation/Format.java | 2 +- .../documentation/FormattedContent.java | 21 ------- .../com/structurizr/documentation/Link.java | 52 ----------------- .../structurizr/documentation/Section.java | 3 - 5 files changed, 57 insertions(+), 77 deletions(-) delete mode 100644 structurizr-core/src/com/structurizr/documentation/FormattedContent.java delete mode 100644 structurizr-core/src/com/structurizr/documentation/Link.java diff --git a/structurizr-core/src/com/structurizr/documentation/Decision.java b/structurizr-core/src/com/structurizr/documentation/Decision.java index 432969e76..4f1c60a2c 100644 --- a/structurizr-core/src/com/structurizr/documentation/Decision.java +++ b/structurizr-core/src/com/structurizr/documentation/Decision.java @@ -1,5 +1,7 @@ package com.structurizr.documentation; +import com.structurizr.util.StringUtils; + import java.util.Date; import java.util.HashSet; import java.util.Set; @@ -121,4 +123,58 @@ public int hashCode() { return result; } + /** + * Represents a link between two decisions. + */ + public static final class Link { + + private String type; + private String id; + + Link() { + } + + Link(String id, String type) { + if (StringUtils.isNullOrEmpty(id)) { + throw new IllegalArgumentException("Link ID must be specfied"); + } + this.id = id; + this.type = type; + } + + public String getId() { + return id; + } + + void setId(String id) { + this.id = id; + } + + public String getType() { + return type; + } + + void setType(String type) { + this.type = type; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Link link = (Link) o; + + if (!type.equals(link.type)) return false; + return id.equals(link.id); + } + + @Override + public int hashCode() { + int result = type.hashCode(); + result = 31 * result + id.hashCode(); + return result; + } + } + } \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/documentation/Format.java b/structurizr-core/src/com/structurizr/documentation/Format.java index 628eb37e0..0eb970408 100644 --- a/structurizr-core/src/com/structurizr/documentation/Format.java +++ b/structurizr-core/src/com/structurizr/documentation/Format.java @@ -5,4 +5,4 @@ public enum Format { Markdown, AsciiDoc -} +} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/documentation/FormattedContent.java b/structurizr-core/src/com/structurizr/documentation/FormattedContent.java deleted file mode 100644 index c73b8aa81..000000000 --- a/structurizr-core/src/com/structurizr/documentation/FormattedContent.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.structurizr.documentation; - -class FormattedContent { - - private String content; - private Format format; - - FormattedContent(String content, Format format) { - this.content = content; - this.format = format; - } - - String getContent() { - return content; - } - - Format getFormat() { - return format; - } - -} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/documentation/Link.java b/structurizr-core/src/com/structurizr/documentation/Link.java deleted file mode 100644 index 10c3ad396..000000000 --- a/structurizr-core/src/com/structurizr/documentation/Link.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.structurizr.documentation; - -import com.structurizr.util.StringUtils; - -import java.util.Objects; - -public final class Link { - - private String type; - private String id; - - public Link() { - } - - Link(String id, String type) { - if (StringUtils.isNullOrEmpty(id)) { - throw new IllegalArgumentException("Link ID must be specfied"); - } - this.id = id; - this.type = type; - } - - public String getId() { - return id; - } - - void setId(String id) { - this.id = id; - } - - public String getType() { - return type; - } - - void setType(String type) { - this.type = type; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - Link link = (Link) o; - return Objects.equals(type, link.type) && id.equals(link.id); - } - - @Override - public int hashCode() { - return Objects.hash(type, id); - } - -} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/documentation/Section.java b/structurizr-core/src/com/structurizr/documentation/Section.java index 8cbb4ac2e..3a84e26c3 100644 --- a/structurizr-core/src/com/structurizr/documentation/Section.java +++ b/structurizr-core/src/com/structurizr/documentation/Section.java @@ -5,10 +5,7 @@ */ public final class Section extends DocumentationContent { - private String title; private int order; - private Format format; - private String content; public Section() { } From b031903b893d550e380a9945f9313b34d0765ed5 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 28 Feb 2022 15:49:03 +0000 Subject: [PATCH 287/717] Tidy up of documentation API. --- structurizr-core/src/com/structurizr/Workspace.java | 1 + .../com/structurizr/documentation/Documentable.java | 12 ++++++++++++ .../src/com/structurizr/model/Documentable.java | 9 --------- .../src/com/structurizr/model/SoftwareSystem.java | 1 + 4 files changed, 14 insertions(+), 9 deletions(-) create mode 100644 structurizr-core/src/com/structurizr/documentation/Documentable.java delete mode 100644 structurizr-core/src/com/structurizr/model/Documentable.java diff --git a/structurizr-core/src/com/structurizr/Workspace.java b/structurizr-core/src/com/structurizr/Workspace.java index 285bafe44..3c2dc0334 100644 --- a/structurizr-core/src/com/structurizr/Workspace.java +++ b/structurizr-core/src/com/structurizr/Workspace.java @@ -1,6 +1,7 @@ package com.structurizr; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.structurizr.documentation.Documentable; import com.structurizr.documentation.Documentation; import com.structurizr.model.*; import com.structurizr.view.ViewSet; diff --git a/structurizr-core/src/com/structurizr/documentation/Documentable.java b/structurizr-core/src/com/structurizr/documentation/Documentable.java new file mode 100644 index 000000000..9ed7a6705 --- /dev/null +++ b/structurizr-core/src/com/structurizr/documentation/Documentable.java @@ -0,0 +1,12 @@ +package com.structurizr.documentation; + +import com.structurizr.documentation.Documentation; + +/** + * Marker interface for items that can have documentation attached (i.e. workspaces and software systems). + */ +public interface Documentable { + + Documentation getDocumentation(); + +} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/model/Documentable.java b/structurizr-core/src/com/structurizr/model/Documentable.java deleted file mode 100644 index 2568aa120..000000000 --- a/structurizr-core/src/com/structurizr/model/Documentable.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.structurizr.model; - -import com.structurizr.documentation.Documentation; - -public interface Documentable { - - Documentation getDocumentation(); - -} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/model/SoftwareSystem.java b/structurizr-core/src/com/structurizr/model/SoftwareSystem.java index 07a0e84fd..b59ae5075 100644 --- a/structurizr-core/src/com/structurizr/model/SoftwareSystem.java +++ b/structurizr-core/src/com/structurizr/model/SoftwareSystem.java @@ -1,6 +1,7 @@ package com.structurizr.model; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.structurizr.documentation.Documentable; import com.structurizr.documentation.Documentation; import javax.annotation.Nonnull; From 06a9b738919a1648568ce4c68815daacf9cfdb02 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 1 Mar 2022 14:04:23 +0000 Subject: [PATCH 288/717] Updated to reflect release. --- docs/changelog.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index ae8b3d09e..d57bd0f00 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,9 +1,11 @@ # Changelog -## 1.12.0 (unreleased) +## 1.12.0 (1st March 2022) - Breaking API changes to how documentation and decisions are managed. - Moved documentation importers/templates to [structurizr-documentation](https://github.com/structurizr/documentation). +- Moved examples to [structurizr-examples](https://github.com/structurizr/examples) +- Removal of deprecated `Model.addImplicitRelationships()` method. ## 1.11.0 (18th February 2022) From 402afa3804c81660875d1e8cdf14b33d43ee7385 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 1 Mar 2022 14:06:44 +0000 Subject: [PATCH 289/717] Removed structurizr-examples reference. --- settings.gradle | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/settings.gradle b/settings.gradle index b719b46af..8872b5782 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,5 +1,4 @@ rootProject.name = 'structurizr' include 'structurizr-client' -include 'structurizr-core' -include 'structurizr-examples' \ No newline at end of file +include 'structurizr-core' \ No newline at end of file From 0db7f1a96abd8f8ca6259d8bcea87328cf2e654a Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 1 Mar 2022 14:07:00 +0000 Subject: [PATCH 290/717] Updated version. --- docs/getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index 674d4a3fb..963ec0c1b 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -12,7 +12,7 @@ The Structurizr for Java binaries are hosted on [Maven Central](https://repo1.ma Name | Description ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-client:1.11.0 | The Structurizr API client library. +com.structurizr:structurizr-client:1.12.0 | The Structurizr API client library. ## 2. Create a Java program From 9d8ccbff823d6095fb5e1dbde2e55e69d872b15f Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 2 Mar 2022 13:50:12 +0000 Subject: [PATCH 291/717] Renamed `Decision.Link.type` to `Decision.Link.description`. --- build.gradle | 2 +- docs/changelog.md | 4 +++ docs/getting-started.md | 2 +- .../structurizr/documentation/Decision.java | 32 ++++++++++++------- 4 files changed, 27 insertions(+), 13 deletions(-) diff --git a/build.gradle b/build.gradle index e66982caa..f3df6a529 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.12.0' + version = '1.12.1' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index d57bd0f00..909387b8e 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.12.1 (2nd March 2022) + +- Renamed `Decision.Link.type` to `Decision.Link.description`. + ## 1.12.0 (1st March 2022) - Breaking API changes to how documentation and decisions are managed. diff --git a/docs/getting-started.md b/docs/getting-started.md index 963ec0c1b..43673ba73 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -12,7 +12,7 @@ The Structurizr for Java binaries are hosted on [Maven Central](https://repo1.ma Name | Description ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-client:1.12.0 | The Structurizr API client library. +com.structurizr:structurizr-client:1.12.1 | The Structurizr API client library. ## 2. Create a Java program diff --git a/structurizr-core/src/com/structurizr/documentation/Decision.java b/structurizr-core/src/com/structurizr/documentation/Decision.java index 4f1c60a2c..bb0ee574b 100644 --- a/structurizr-core/src/com/structurizr/documentation/Decision.java +++ b/structurizr-core/src/com/structurizr/documentation/Decision.java @@ -128,18 +128,19 @@ public int hashCode() { */ public static final class Link { - private String type; private String id; + private String description = ""; Link() { } - Link(String id, String type) { + Link(String id, String description) { if (StringUtils.isNullOrEmpty(id)) { - throw new IllegalArgumentException("Link ID must be specfied"); + throw new IllegalArgumentException("Link ID must be specified"); } - this.id = id; - this.type = type; + + setId(id); + setDescription(description); } public String getId() { @@ -150,12 +151,21 @@ void setId(String id) { this.id = id; } - public String getType() { - return type; + /** + * Gets the description of this link. + * + * @return a String description + */ + public String getDescription() { + return description; } - void setType(String type) { - this.type = type; + void setDescription(String description) { + if (!StringUtils.isNullOrEmpty(description)) { + this.description = description; + } else { + this.description = ""; + } } @Override @@ -165,13 +175,13 @@ public boolean equals(Object o) { Link link = (Link) o; - if (!type.equals(link.type)) return false; + if (!description.equals(link.description)) return false; return id.equals(link.id); } @Override public int hashCode() { - int result = type.hashCode(); + int result = description.hashCode(); result = 31 * result + id.hashCode(); return result; } From 59176bab726cd5a001c2dd2e38820afdd99b684d Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 5 Mar 2022 10:59:09 +0000 Subject: [PATCH 292/717] Update dependencies. --- structurizr-client/build.gradle | 4 ++-- structurizr-core/build.gradle | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/structurizr-client/build.gradle b/structurizr-client/build.gradle index de307d1dc..81f110d70 100644 --- a/structurizr-client/build.gradle +++ b/structurizr-client/build.gradle @@ -2,9 +2,9 @@ dependencies { compile project(':structurizr-core') - compile 'com.fasterxml.jackson.core:jackson-databind:2.10.5.1' + compile 'com.fasterxml.jackson.core:jackson-databind:2.13.1' - compile 'org.apache.httpcomponents.client5:httpclient5:5.0' + compile 'org.apache.httpcomponents.client5:httpclient5:5.1.3' compile 'javax.xml.bind:jaxb-api:2.3.0' diff --git a/structurizr-core/build.gradle b/structurizr-core/build.gradle index 3dab30197..4099e71dd 100644 --- a/structurizr-core/build.gradle +++ b/structurizr-core/build.gradle @@ -1,6 +1,6 @@ dependencies { - compile 'com.fasterxml.jackson.core:jackson-annotations:2.9.5' + compile 'com.fasterxml.jackson.core:jackson-annotations:2.13.1' compile 'commons-logging:commons-logging:1.2' From 109c8917eb051fa8f8cb6fbb0635712db5cfa414 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 30 Mar 2022 07:41:13 +0100 Subject: [PATCH 293/717] Added support for sorting views by the order in which they are created. --- build.gradle | 2 +- docs/changelog.md | 4 +++ .../com/structurizr/view/FilteredView.java | 14 ++++++++++ .../src/com/structurizr/view/View.java | 14 ++++++++++ .../src/com/structurizr/view/ViewSet.java | 26 +++++++++++++++++ .../com/structurizr/view/ViewSetTests.java | 28 +++++++++++++++++++ 6 files changed, 87 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index f3df6a529..a5764f13d 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.12.1' + version = '1.12.2' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 909387b8e..538d80866 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.12.2 (30th March 2022) + +- Added support for sorting views by the order in which they are created. + ## 1.12.1 (2nd March 2022) - Renamed `Decision.Link.type` to `Decision.Link.description`. diff --git a/structurizr-core/src/com/structurizr/view/FilteredView.java b/structurizr-core/src/com/structurizr/view/FilteredView.java index 2b77735c9..2a9b9a50c 100644 --- a/structurizr-core/src/com/structurizr/view/FilteredView.java +++ b/structurizr-core/src/com/structurizr/view/FilteredView.java @@ -15,6 +15,7 @@ public final class FilteredView { private String baseViewKey; private String key; + private int order; private String description = ""; private FilterMode mode = FilterMode.Exclude; @@ -60,6 +61,19 @@ void setKey(String key) { this.key = key; } + /** + * Gets the order of this view. + * + * @return a positive integer + */ + public int getOrder() { + return order; + } + + void setOrder(int order) { + this.order = Math.max(1, order); + } + public String getDescription() { return description; } diff --git a/structurizr-core/src/com/structurizr/view/View.java b/structurizr-core/src/com/structurizr/view/View.java index aee7e3011..419163a68 100644 --- a/structurizr-core/src/com/structurizr/view/View.java +++ b/structurizr-core/src/com/structurizr/view/View.java @@ -24,6 +24,7 @@ public abstract class View { private String softwareSystemId; private String description = ""; private String key; + private int order; private PaperSize paperSize = null; private Dimensions dimensions = null; private AutomaticLayout automaticLayout = null; @@ -125,6 +126,19 @@ void setKey(String key) { this.key = key; } + /** + * Gets the order of this view. + * + * @return a positive integer + */ + public int getOrder() { + return order; + } + + void setOrder(int order) { + this.order = Math.max(1, order); + } + /** * Gets the paper size that should be used to render this view. * diff --git a/structurizr-core/src/com/structurizr/view/ViewSet.java b/structurizr-core/src/com/structurizr/view/ViewSet.java index dcdbcd5b7..d68273004 100644 --- a/structurizr-core/src/com/structurizr/view/ViewSet.java +++ b/structurizr-core/src/com/structurizr/view/ViewSet.java @@ -53,6 +53,7 @@ public CustomView createCustomView(String key, String title, String description) assertThatTheViewKeyIsSpecifiedAndUnique(key); CustomView view = new CustomView(model, key, title, description); + view.setOrder(getNextOrder()); view.setViewSet(this); customViews.add(view); return view; @@ -70,6 +71,7 @@ public SystemLandscapeView createSystemLandscapeView(String key, String descript assertThatTheViewKeyIsSpecifiedAndUnique(key); SystemLandscapeView view = new SystemLandscapeView(model, key, description); + view.setOrder(getNextOrder()); view.setViewSet(this); systemLandscapeViews.add(view); return view; @@ -89,6 +91,7 @@ public SystemContextView createSystemContextView(SoftwareSystem softwareSystem, assertThatTheViewKeyIsSpecifiedAndUnique(key); SystemContextView view = new SystemContextView(softwareSystem, key, description); + view.setOrder(getNextOrder()); view.setViewSet(this); systemContextViews.add(view); return view; @@ -108,6 +111,7 @@ public ContainerView createContainerView(SoftwareSystem softwareSystem, String k assertThatTheViewKeyIsSpecifiedAndUnique(key); ContainerView view = new ContainerView(softwareSystem, key, description); + view.setOrder(getNextOrder()); view.setViewSet(this); containerViews.add(view); return view; @@ -127,6 +131,7 @@ public ComponentView createComponentView(Container container, String key, String assertThatTheViewKeyIsSpecifiedAndUnique(key); ComponentView view = new ComponentView(container, key, description); + view.setOrder(getNextOrder()); view.setViewSet(this); componentViews.add(view); return view; @@ -144,6 +149,7 @@ public DynamicView createDynamicView(String key, String description) { assertThatTheViewKeyIsSpecifiedAndUnique(key); DynamicView view = new DynamicView(model, key, description); + view.setOrder(getNextOrder()); view.setViewSet(this); dynamicViews.add(view); return view; @@ -170,6 +176,7 @@ public DynamicView createDynamicView(SoftwareSystem softwareSystem, String key, assertThatTheViewKeyIsSpecifiedAndUnique(key); DynamicView view = new DynamicView(softwareSystem, key, description); + view.setOrder(getNextOrder()); view.setViewSet(this); dynamicViews.add(view); return view; @@ -197,6 +204,7 @@ public DynamicView createDynamicView(Container container, String key, String des assertThatTheViewKeyIsSpecifiedAndUnique(key); DynamicView view = new DynamicView(container, key, description); + view.setOrder(getNextOrder()); view.setViewSet(this); dynamicViews.add(view); return view; @@ -214,6 +222,7 @@ public DeploymentView createDeploymentView(String key, String description) { assertThatTheViewKeyIsSpecifiedAndUnique(key); DeploymentView view = new DeploymentView(model, key, description); + view.setOrder(getNextOrder()); view.setViewSet(this); deploymentViews.add(view); return view; @@ -233,6 +242,7 @@ public DeploymentView createDeploymentView(SoftwareSystem softwareSystem, String assertThatTheViewKeyIsSpecifiedAndUnique(key); DeploymentView view = new DeploymentView(softwareSystem, key, description); + view.setOrder(getNextOrder()); view.setViewSet(this); deploymentViews.add(view); return view; @@ -253,6 +263,7 @@ public FilteredView createFilteredView(StaticView view, String key, String descr assertThatTheViewKeyIsSpecifiedAndUnique(key); FilteredView filteredView = new FilteredView(view, key, description, mode, tags); + filteredView.setOrder(getNextOrder()); filteredViews.add(filteredView); return filteredView; } @@ -624,6 +635,21 @@ private void checkViewKeysAreUnique() { } } + private synchronized int getNextOrder() { + int order = 0; + + order = Math.max(order, customViews.stream().max(Comparator.comparingInt(View::getOrder)).map(View::getOrder).orElse(0)); + order = Math.max(order, systemLandscapeViews.stream().max(Comparator.comparingInt(View::getOrder)).map(View::getOrder).orElse(0)); + order = Math.max(order, systemContextViews.stream().max(Comparator.comparingInt(View::getOrder)).map(View::getOrder).orElse(0)); + order = Math.max(order, containerViews.stream().max(Comparator.comparingInt(View::getOrder)).map(View::getOrder).orElse(0)); + order = Math.max(order, componentViews.stream().max(Comparator.comparingInt(View::getOrder)).map(View::getOrder).orElse(0)); + order = Math.max(order, dynamicViews.stream().max(Comparator.comparingInt(View::getOrder)).map(View::getOrder).orElse(0)); + order = Math.max(order, deploymentViews.stream().max(Comparator.comparingInt(View::getOrder)).map(View::getOrder).orElse(0)); + order = Math.max(order, filteredViews.stream().max(Comparator.comparingInt(FilteredView::getOrder)).map(FilteredView::getOrder).orElse(0)); + + return order + 1; + } + /** * Gets the configuration object associated with this set of views. * diff --git a/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java b/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java index 1d4623c8d..086d5b280 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java @@ -1094,4 +1094,32 @@ public void test_copyLayoutInformationFrom_DoesNothing_WhenMergeFromRemoteIsSetT assertNull(view2.getPaperSize()); } + @Test + public void test_view_ordering() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + ViewSet views = workspace.getViews(); + + CustomView customView = views.createCustomView("custom1", "Title", "Description"); + SystemLandscapeView systemLandscapeView1 = views.createSystemLandscapeView("landscape1", "Description"); + FilteredView filteredView = views.createFilteredView(systemLandscapeView1, "filtered1", "Description", FilterMode.Include, "Tag 1"); + SystemContextView systemContextView = views.createSystemContextView(softwareSystem, "context1", "Description"); + ContainerView containerView = views.createContainerView(softwareSystem, "container1", "Description"); + ComponentView componentView = views.createComponentView(container, "component1", "Description"); + DynamicView dynamicView = views.createDynamicView("dynamic1", "Description"); + DeploymentView deploymentView = views.createDeploymentView("deployment1", "Description"); + SystemLandscapeView systemLandscapeView2 = views.createSystemLandscapeView("landscape2", "Description"); + + assertEquals(1, customView.getOrder()); + assertEquals(2, systemLandscapeView1.getOrder()); + assertEquals(3, filteredView.getOrder()); + assertEquals(4, systemContextView.getOrder()); + assertEquals(5, containerView.getOrder()); + assertEquals(6, componentView.getOrder()); + assertEquals(7, dynamicView.getOrder()); + assertEquals(8, deploymentView.getOrder()); + assertEquals(9, systemLandscapeView2.getOrder()); + } + } \ No newline at end of file From 87d42ae0a494ab1e61460ffc65a4a8a8ffb57956 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 30 Mar 2022 07:44:26 +0100 Subject: [PATCH 294/717] Wording tweak. --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 538d80866..7778ab2a8 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -2,7 +2,7 @@ ## 1.12.2 (30th March 2022) -- Added support for sorting views by the order in which they are created. +- Adds support for sorting views by the order in which they are created. ## 1.12.1 (2nd March 2022) From 783ff48f0251ceea966e6fd61397913b5837e441 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 6 Apr 2022 08:31:25 +0100 Subject: [PATCH 295/717] Adds support for name/value properties on element and relationship styles. --- build.gradle | 2 +- docs/changelog.md | 4 + .../com/structurizr/view/AbstractStyle.java | 45 ++++++++++ .../com/structurizr/view/ElementStyle.java | 3 +- .../structurizr/view/RelationshipStyle.java | 2 +- .../structurizr/view/ElementStyleTests.java | 82 ++++++++++++++++++- .../view/RelationshipStyleTests.java | 80 +++++++++++++++++- 7 files changed, 210 insertions(+), 8 deletions(-) create mode 100644 structurizr-core/src/com/structurizr/view/AbstractStyle.java diff --git a/build.gradle b/build.gradle index a5764f13d..3a321eaac 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.12.2' + version = '1.13.0' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 7778ab2a8..79ae8e40f 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.13.0 (unreleased to Maven Central) + +- Adds support for name/value properties on element and relationship styles. + ## 1.12.2 (30th March 2022) - Adds support for sorting views by the order in which they are created. diff --git a/structurizr-core/src/com/structurizr/view/AbstractStyle.java b/structurizr-core/src/com/structurizr/view/AbstractStyle.java new file mode 100644 index 000000000..28ed28f22 --- /dev/null +++ b/structurizr-core/src/com/structurizr/view/AbstractStyle.java @@ -0,0 +1,45 @@ +package com.structurizr.view; + +import com.structurizr.PropertyHolder; + +import java.util.HashMap; +import java.util.Map; + +public abstract class AbstractStyle implements PropertyHolder { + + private Map properties = new HashMap<>(); + + /** + * Gets the collection of name-value property pairs associated with this workspace, as a Map. + * + * @return a Map (String, String) (empty if there are no properties) + */ + public Map getProperties() { + return new HashMap<>(properties); + } + + /** + * Adds a name-value pair property to this workspace. + * + * @param name the name of the property + * @param value the value of the property + */ + public void addProperty(String name, String value) { + if (name == null || name.trim().length() == 0) { + throw new IllegalArgumentException("A property name must be specified."); + } + + if (value == null || value.trim().length() == 0) { + throw new IllegalArgumentException("A property value must be specified."); + } + + properties.put(name, value); + } + + void setProperties(Map properties) { + if (properties != null) { + this.properties = new HashMap<>(properties); + } + } + +} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/view/ElementStyle.java b/structurizr-core/src/com/structurizr/view/ElementStyle.java index 78a638858..fc61528e5 100644 --- a/structurizr-core/src/com/structurizr/view/ElementStyle.java +++ b/structurizr-core/src/com/structurizr/view/ElementStyle.java @@ -1,13 +1,14 @@ package com.structurizr.view; import com.fasterxml.jackson.annotation.JsonInclude; +import com.structurizr.PropertyHolder; import com.structurizr.util.ImageUtils; import com.structurizr.util.StringUtils; /** * A definition of an element style. */ -public final class ElementStyle { +public final class ElementStyle extends AbstractStyle { public static final int DEFAULT_WIDTH = 450; public static final int DEFAULT_HEIGHT = 300; diff --git a/structurizr-core/src/com/structurizr/view/RelationshipStyle.java b/structurizr-core/src/com/structurizr/view/RelationshipStyle.java index ba10d57cf..92219414d 100644 --- a/structurizr-core/src/com/structurizr/view/RelationshipStyle.java +++ b/structurizr-core/src/com/structurizr/view/RelationshipStyle.java @@ -3,7 +3,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.structurizr.util.StringUtils; -public final class RelationshipStyle { +public final class RelationshipStyle extends AbstractStyle { private static final int START_OF_LINE = 0; private static final int END_OF_LINE = 100; diff --git a/structurizr-core/test/unit/com/structurizr/view/ElementStyleTests.java b/structurizr-core/test/unit/com/structurizr/view/ElementStyleTests.java index 2f85d4d6a..40ec16c6a 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ElementStyleTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ElementStyleTests.java @@ -2,8 +2,10 @@ import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.*; public class ElementStyleTests { @@ -203,5 +205,79 @@ public void test_Stroke_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() ElementStyle style = new ElementStyle(); style.stroke("white"); } - + + @Test + public void test_getProperties_ReturnsAnEmptyList_WhenNoPropertiesHaveBeenAdded() { + ElementStyle style = new ElementStyle(); + assertEquals(0, style.getProperties().size()); + } + + @Test + public void test_addProperty_ThrowsAnException_WhenTheNameIsNull() { + try { + ElementStyle style = new ElementStyle(); + style.addProperty(null, "value"); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("A property name must be specified.", e.getMessage()); + } + } + + @Test + public void test_addProperty_ThrowsAnException_WhenTheNameIsEmpty() { + try { + ElementStyle style = new ElementStyle(); + style.addProperty(" ", "value"); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("A property name must be specified.", e.getMessage()); + } + } + + @Test + public void test_addProperty_ThrowsAnException_WhenTheValueIsNull() { + try { + ElementStyle style = new ElementStyle(); + style.addProperty("name", null); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("A property value must be specified.", e.getMessage()); + } + } + + @Test + public void test_addProperty_ThrowsAnException_WhenTheValueIsEmpty() { + try { + ElementStyle style = new ElementStyle(); + style.addProperty("name", " "); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("A property value must be specified.", e.getMessage()); + } + } + + @Test + public void test_addProperty_AddsTheProperty_WhenANameAndValueAreSpecified() { + ElementStyle style = new ElementStyle(); + style.addProperty("name", "value"); + assertEquals("value", style.getProperties().get("name")); + } + + @Test + public void test_setProperties_DoesNothing_WhenNullIsSpecified() { + ElementStyle style = new ElementStyle(); + style.setProperties(null); + assertEquals(0, style.getProperties().size()); + } + + @Test + public void test_setProperties_SetsTheProperties_WhenANonEmptyMapIsSpecified() { + ElementStyle style = new ElementStyle(); + Map properties = new HashMap<>(); + properties.put("name", "value"); + style.setProperties(properties); + assertEquals(1, style.getProperties().size()); + assertEquals("value", style.getProperties().get("name")); + } + } \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/view/RelationshipStyleTests.java b/structurizr-core/test/unit/com/structurizr/view/RelationshipStyleTests.java index 0acad8082..4aa67bcdb 100644 --- a/structurizr-core/test/unit/com/structurizr/view/RelationshipStyleTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/RelationshipStyleTests.java @@ -2,8 +2,10 @@ import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.*; public class RelationshipStyleTests { @@ -126,4 +128,78 @@ public void test_color_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() style.color("white"); } + @Test + public void test_getProperties_ReturnsAnEmptyList_WhenNoPropertiesHaveBeenAdded() { + RelationshipStyle style = new RelationshipStyle(); + assertEquals(0, style.getProperties().size()); + } + + @Test + public void test_addProperty_ThrowsAnException_WhenTheNameIsNull() { + try { + RelationshipStyle style = new RelationshipStyle(); + style.addProperty(null, "value"); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("A property name must be specified.", e.getMessage()); + } + } + + @Test + public void test_addProperty_ThrowsAnException_WhenTheNameIsEmpty() { + try { + RelationshipStyle style = new RelationshipStyle(); + style.addProperty(" ", "value"); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("A property name must be specified.", e.getMessage()); + } + } + + @Test + public void test_addProperty_ThrowsAnException_WhenTheValueIsNull() { + try { + RelationshipStyle style = new RelationshipStyle(); + style.addProperty("name", null); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("A property value must be specified.", e.getMessage()); + } + } + + @Test + public void test_addProperty_ThrowsAnException_WhenTheValueIsEmpty() { + try { + RelationshipStyle style = new RelationshipStyle(); + style.addProperty("name", " "); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("A property value must be specified.", e.getMessage()); + } + } + + @Test + public void test_addProperty_AddsTheProperty_WhenANameAndValueAreSpecified() { + RelationshipStyle style = new RelationshipStyle(); + style.addProperty("name", "value"); + assertEquals("value", style.getProperties().get("name")); + } + + @Test + public void test_setProperties_DoesNothing_WhenNullIsSpecified() { + RelationshipStyle style = new RelationshipStyle(); + style.setProperties(null); + assertEquals(0, style.getProperties().size()); + } + + @Test + public void test_setProperties_SetsTheProperties_WhenANonEmptyMapIsSpecified() { + RelationshipStyle style = new RelationshipStyle(); + Map properties = new HashMap<>(); + properties.put("name", "value"); + style.setProperties(properties); + assertEquals(1, style.getProperties().size()); + assertEquals("value", style.getProperties().get("name")); + } + } From 706b8bae1d11feb2563eded3aec58d20e70c147f Mon Sep 17 00:00:00 2001 From: Guillaume Grossetie Date: Fri, 13 May 2022 09:08:29 +0200 Subject: [PATCH 296/717] resolves #172 upgrade jacksonxml to 2.13.2 --- structurizr-client/build.gradle | 2 +- structurizr-core/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/structurizr-client/build.gradle b/structurizr-client/build.gradle index 81f110d70..8feb4b4a9 100644 --- a/structurizr-client/build.gradle +++ b/structurizr-client/build.gradle @@ -2,7 +2,7 @@ dependencies { compile project(':structurizr-core') - compile 'com.fasterxml.jackson.core:jackson-databind:2.13.1' + compile 'com.fasterxml.jackson.core:jackson-databind:2.13.2.2' compile 'org.apache.httpcomponents.client5:httpclient5:5.1.3' diff --git a/structurizr-core/build.gradle b/structurizr-core/build.gradle index 4099e71dd..7862807e7 100644 --- a/structurizr-core/build.gradle +++ b/structurizr-core/build.gradle @@ -1,6 +1,6 @@ dependencies { - compile 'com.fasterxml.jackson.core:jackson-annotations:2.13.1' + compile 'com.fasterxml.jackson.core:jackson-annotations:2.13.2' compile 'commons-logging:commons-logging:1.2' From 8e677dc63b7723f0f5d67853061fadf5e71d6b31 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 24 May 2022 13:51:10 +0200 Subject: [PATCH 297/717] Further upgraded Jackson. --- structurizr-client/build.gradle | 2 +- structurizr-core/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/structurizr-client/build.gradle b/structurizr-client/build.gradle index 8feb4b4a9..5d9efad7b 100644 --- a/structurizr-client/build.gradle +++ b/structurizr-client/build.gradle @@ -2,7 +2,7 @@ dependencies { compile project(':structurizr-core') - compile 'com.fasterxml.jackson.core:jackson-databind:2.13.2.2' + compile 'com.fasterxml.jackson.core:jackson-databind:2.13.3' compile 'org.apache.httpcomponents.client5:httpclient5:5.1.3' diff --git a/structurizr-core/build.gradle b/structurizr-core/build.gradle index 7862807e7..74f64d844 100644 --- a/structurizr-core/build.gradle +++ b/structurizr-core/build.gradle @@ -1,6 +1,6 @@ dependencies { - compile 'com.fasterxml.jackson.core:jackson-annotations:2.13.2' + compile 'com.fasterxml.jackson.core:jackson-annotations:2.13.3' compile 'commons-logging:commons-logging:1.2' From 5d5dccfc5ead1d5a3fd1320a28d2ec2cd1423dd3 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 25 Jun 2022 21:07:20 +0100 Subject: [PATCH 298/717] Upgrade to Java 17 and Gradle 7. --- build.gradle | 124 +++++------------- gradle/gradle.iml | 14 -- gradle/wrapper/gradle-wrapper.properties | 3 +- structurizr-client/build.gradle | 26 +--- .../com/structurizr/view/ThemeUtilsTests.java | 18 +-- structurizr-core/build.gradle | 24 +--- .../view/RelationshipStyleTests.java | 14 +- .../com/structurizr/view/StylesTests.java | 70 +++++----- 8 files changed, 98 insertions(+), 195 deletions(-) delete mode 100644 gradle/gradle.iml diff --git a/build.gradle b/build.gradle index 3a321eaac..c4587869b 100644 --- a/build.gradle +++ b/build.gradle @@ -1,15 +1,9 @@ -task wrapper(type: Wrapper) { - gradleVersion = '4.0' -} - defaultTasks 'clean', 'compileJava', 'test' subprojects { proj -> - apply plugin: 'idea' apply plugin: 'java' apply plugin: 'maven-publish' - apply plugin: 'maven' apply plugin: 'signing' description = 'Structurizr' @@ -18,7 +12,6 @@ subprojects { proj -> repositories { mavenCentral() - mavenLocal() } sourceSets { @@ -34,80 +27,65 @@ subprojects { proj -> } } - dependencies { - compile 'com.google.code.findbugs:jsr305:3.0.2' - } - compileJava.options.encoding = 'UTF-8' compileTestJava.options.encoding = 'UTF-8' sourceCompatibility = 1.8 targetCompatibility = 1.8 + java { + withJavadocJar() + withSourcesJar() + } + jar { manifest { attributes( - "Implementation-Title": "Structurizr for Java", - "Implementation-Version": version + "Implementation-Title": "Structurizr for Java", + "Implementation-Version": version ) } } - task sourcesJar(type: Jar) { - classifier = 'sources' - from sourceSets.main.allJava - } - - task javadocJar(type: Jar) { - classifier = 'javadoc' - from javadoc - } - - artifacts { - archives javadocJar, sourcesJar - } - - signing { - sign configurations.archives - } - - uploadArchives { + publishing { repositories { - mavenDeployer { - beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } - - repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") { - authentication(userName: ossrhUsername, password: ossrhPassword) + maven { + name = "ossrh" + url = "https://oss.sonatype.org/service/local/staging/deploy/maven2/" + credentials { + username = findProperty('ossrhUsername') + password = findProperty('ossrhPassword') } + } + } - snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/") { - authentication(userName: ossrhUsername, password: ossrhPassword) - } + publications { + mavenJava(MavenPublication) { + from components.java - pom.project { - name 'Structurizr for Java' - packaging 'jar' - description 'Structurizr for Java' - url 'https://github.com/structurizr/java' + pom { + name = 'Structurizr for Java' + description = 'Structurizr for Java' + url = 'https://github.com/structurizr/java' scm { - connection 'scm:git:git://github.com/structurizr/structurizr-java.git' - developerConnection 'scm:git:git@github.com:structurizr/structurizr-java.git' - url 'https://github.com/structurizr/java' + connection = 'scm:git:git://github.com/structurizr/structurizr-java.git' + developerConnection = 'scm:git:git@github.com:structurizr/structurizr-java.git' + url = 'https://github.com/structurizr/java' } licenses { license { - name 'The Apache License, Version 2.0' - url 'http://www.apache.org/licenses/LICENSE-2.0.txt' + name = 'The Apache License, Version 2.0' + url = 'http://www.apache.org/licenses/LICENSE-2.0.txt' } } developers { developer { - id "simon" - name "Simon Brown" - email "simon@structurizr.com" + id = "simon" + name = "Simon Brown" + email = "simon@structurizr.com" } } } @@ -115,44 +93,8 @@ subprojects { proj -> } } - def pomConfig = { - licenses { - license { - name "The Apache Software License, Version 2.0" - url "http://www.apache.org/licenses/LICENSE-2.0.txt" - distribution "repo" - } - } - developers { - developer { - id "simon" - name "Simon Brown" - email "simon@structurizr.com" - } - } - } - - publishing { - publications { - mavenJava(MavenPublication) { - from components.java - artifact sourcesJar - artifact javadocJar - pom.withXml { - def root = asNode() - root.appendNode('description', 'Visualise, document and explore your software architecture with the C4 model.') - root.appendNode('name', 'Structurizr for Java') - root.appendNode('url', 'https://github.com/structurizr/java') - root.children().last() + pomConfig - } - } - } - - repositories { - maven { - url "$buildDir/repo" - } - } + signing { + sign publishing.publications.mavenJava } } \ No newline at end of file diff --git a/gradle/gradle.iml b/gradle/gradle.iml deleted file mode 100644 index d2f85ef06..000000000 --- a/gradle/gradle.iml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 5c53047ee..aa991fcea 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Mon Nov 19 10:27:00 CET 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip diff --git a/structurizr-client/build.gradle b/structurizr-client/build.gradle index 5d9efad7b..6c5bd755d 100644 --- a/structurizr-client/build.gradle +++ b/structurizr-client/build.gradle @@ -1,26 +1,14 @@ dependencies { - compile project(':structurizr-core') + implementation project(':structurizr-core') - compile 'com.fasterxml.jackson.core:jackson-databind:2.13.3' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.3' - compile 'org.apache.httpcomponents.client5:httpclient5:5.1.3' + implementation 'org.apache.httpcomponents.client5:httpclient5:5.1.3' - compile 'javax.xml.bind:jaxb-api:2.3.0' + implementation 'javax.xml.bind:jaxb-api:2.3.0' - testCompile 'junit:junit:4.12' -} + implementation 'commons-logging:commons-logging:1.2' -sourceSets { - main { - java { - srcDir 'src' - } - } - test { - java { - srcDir 'test/unit' - srcDir 'test/integration' - } - } -} + testImplementation 'junit:junit:4.12' +} \ No newline at end of file diff --git a/structurizr-client/test/unit/com/structurizr/view/ThemeUtilsTests.java b/structurizr-client/test/unit/com/structurizr/view/ThemeUtilsTests.java index 143c8ce1a..119d10390 100644 --- a/structurizr-client/test/unit/com/structurizr/view/ThemeUtilsTests.java +++ b/structurizr-client/test/unit/com/structurizr/view/ThemeUtilsTests.java @@ -86,16 +86,16 @@ public void test_findElementStyle_WithThemes() { workspace.getViews().getConfiguration().getStyles().addStylesFromTheme("url2", elementStyles, relationshipStyles); ElementStyle style = workspace.getViews().getConfiguration().getStyles().findElementStyle(softwareSystem); - assertEquals(new Integer(450), style.getWidth()); - assertEquals(new Integer(300), style.getHeight()); + assertEquals(Integer.valueOf(450), style.getWidth()); + assertEquals(Integer.valueOf(300), style.getHeight()); assertEquals("#ff0000", style.getBackground()); // from theme 2 assertEquals("#ffffff", style.getColor()); // from theme 1 - assertEquals(new Integer(24), style.getFontSize()); + assertEquals(Integer.valueOf(24), style.getFontSize()); assertEquals(Shape.RoundedBox, style.getShape()); // from workspace assertNull(style.getIcon()); assertEquals(Border.Solid, style.getBorder()); assertEquals("#b20000", style.getStroke()); - assertEquals(new Integer(100), style.getOpacity()); + assertEquals(Integer.valueOf(100), style.getOpacity()); assertEquals(true, style.getMetadata()); assertEquals(true, style.getDescription()); } @@ -120,14 +120,14 @@ public void test_findRelationshipStyle_WithThemes() { workspace.getViews().getConfiguration().getStyles().addStylesFromTheme("url2", elementStyles, relationshipStyles); RelationshipStyle style = workspace.getViews().getConfiguration().getStyles().findRelationshipStyle(relationship); - assertEquals(new Integer(4), style.getThickness()); // from theme 1 + assertEquals(Integer.valueOf(4), style.getThickness()); // from theme 1 assertEquals("#0000ff", style.getColor()); // from theme 2 Assert.assertFalse(style.getDashed()); // from workspace assertEquals(Routing.Direct, style.getRouting()); - assertEquals(new Integer(24), style.getFontSize()); - assertEquals(new Integer(200), style.getWidth()); - assertEquals(new Integer(50), style.getPosition()); - assertEquals(new Integer(100), style.getOpacity()); + assertEquals(Integer.valueOf(24), style.getFontSize()); + assertEquals(Integer.valueOf(200), style.getWidth()); + assertEquals(Integer.valueOf(50), style.getPosition()); + assertEquals(Integer.valueOf(100), style.getOpacity()); } } \ No newline at end of file diff --git a/structurizr-core/build.gradle b/structurizr-core/build.gradle index 74f64d844..4f395e47f 100644 --- a/structurizr-core/build.gradle +++ b/structurizr-core/build.gradle @@ -1,22 +1,10 @@ dependencies { - compile 'com.fasterxml.jackson.core:jackson-annotations:2.13.3' + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.13.3' + implementation 'com.google.code.findbugs:jsr305:3.0.2' - compile 'commons-logging:commons-logging:1.2' + implementation 'commons-logging:commons-logging:1.2' - testCompile 'junit:junit:4.12' - testCompile 'org.assertj:assertj-core:3.9.1' -} - -sourceSets { - main { - java { - srcDir 'src' - } - } - test { - java { - srcDir 'test/unit' - } - } -} + testImplementation 'junit:junit:4.12' + testImplementation 'org.assertj:assertj-core:3.9.1' +} \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/view/RelationshipStyleTests.java b/structurizr-core/test/unit/com/structurizr/view/RelationshipStyleTests.java index 4aa67bcdb..939770e33 100644 --- a/structurizr-core/test/unit/com/structurizr/view/RelationshipStyleTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/RelationshipStyleTests.java @@ -20,32 +20,32 @@ public void test_setPosition_SetsPositionToNull_WhenNullIsSpecified() { @Test public void test_setPosition_SetsPositionToZero_WhenANegativeNumberIsSpecified() { relationshipStyle.setPosition(-1); - assertEquals(new Integer(0), relationshipStyle.getPosition()); + assertEquals(Integer.valueOf(0), relationshipStyle.getPosition()); } @Test public void test_setPosition_SetsPositionToOneHundred_WhenANumberGreaterThanOneHundredIsSpecified() { relationshipStyle.setPosition(101); - assertEquals(new Integer(100), relationshipStyle.getPosition()); + assertEquals(Integer.valueOf(100), relationshipStyle.getPosition()); } @Test public void test_setPosition_SetsPosition_WhenANumberBetweenZeroAndOneHundredIsSpecified() { relationshipStyle.setPosition(0); - assertEquals(new Integer(0), relationshipStyle.getPosition()); + assertEquals(Integer.valueOf(0), relationshipStyle.getPosition()); relationshipStyle.setPosition(1); - assertEquals(new Integer(1), relationshipStyle.getPosition()); + assertEquals(Integer.valueOf(1), relationshipStyle.getPosition()); relationshipStyle.setPosition(50); - assertEquals(new Integer(50), relationshipStyle.getPosition()); + assertEquals(Integer.valueOf(50), relationshipStyle.getPosition()); relationshipStyle.setPosition(99); - assertEquals(new Integer(99), relationshipStyle.getPosition()); + assertEquals(Integer.valueOf(99), relationshipStyle.getPosition()); relationshipStyle.setPosition(100); - assertEquals(new Integer(100), relationshipStyle.getPosition()); + assertEquals(Integer.valueOf(100), relationshipStyle.getPosition()); } @Test diff --git a/structurizr-core/test/unit/com/structurizr/view/StylesTests.java b/structurizr-core/test/unit/com/structurizr/view/StylesTests.java index 881dcc5f7..5857a8076 100644 --- a/structurizr-core/test/unit/com/structurizr/view/StylesTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/StylesTests.java @@ -13,16 +13,16 @@ public class StylesTests extends AbstractWorkspaceTestBase { @Test public void test_findElementStyle_ReturnsTheDefaultStyle_WhenPassedNull() { ElementStyle style = styles.findElementStyle((Element)null); - assertEquals(new Integer(450), style.getWidth()); - assertEquals(new Integer(300), style.getHeight()); + assertEquals(Integer.valueOf(450), style.getWidth()); + assertEquals(Integer.valueOf(300), style.getHeight()); assertEquals("#dddddd", style.getBackground()); assertEquals("#000000", style.getColor()); - assertEquals(new Integer(24), style.getFontSize()); + assertEquals(Integer.valueOf(24), style.getFontSize()); assertEquals(Shape.Box, style.getShape()); assertNull(style.getIcon()); assertEquals(Border.Solid, style.getBorder()); assertEquals("#9a9a9a", style.getStroke()); - assertEquals(new Integer(100), style.getOpacity()); + assertEquals(Integer.valueOf(100), style.getOpacity()); assertEquals(true, style.getMetadata()); assertEquals(true, style.getDescription()); } @@ -31,16 +31,16 @@ public void test_findElementStyle_ReturnsTheDefaultStyle_WhenPassedNull() { public void test_findElementStyle_ReturnsTheDefaultStyle_WhenNoStylesAreDefined() { SoftwareSystem element = model.addSoftwareSystem("Name", "Description"); ElementStyle style = styles.findElementStyle(element); - assertEquals(new Integer(450), style.getWidth()); - assertEquals(new Integer(300), style.getHeight()); + assertEquals(Integer.valueOf(450), style.getWidth()); + assertEquals(Integer.valueOf(300), style.getHeight()); assertEquals("#dddddd", style.getBackground()); assertEquals("#000000", style.getColor()); - assertEquals(new Integer(24), style.getFontSize()); + assertEquals(Integer.valueOf(24), style.getFontSize()); assertEquals(Shape.Box, style.getShape()); assertNull(style.getIcon()); assertEquals(Border.Solid, style.getBorder()); assertEquals("#9a9a9a", style.getStroke()); - assertEquals(new Integer(100), style.getOpacity()); + assertEquals(Integer.valueOf(100), style.getOpacity()); assertEquals(true, style.getMetadata()); assertEquals(true, style.getDescription()); } @@ -54,16 +54,16 @@ public void test_findElementStyle_ReturnsTheCorrectStyle_WhenStylesAreDefined() styles.addElementStyle("Some Tag").color("#0000ff").stroke("#00ff00").shape(Shape.RoundedBox).width(123).height(456); ElementStyle style = styles.findElementStyle(element); - assertEquals(new Integer(123), style.getWidth()); - assertEquals(new Integer(456), style.getHeight()); + assertEquals(Integer.valueOf(123), style.getWidth()); + assertEquals(Integer.valueOf(456), style.getHeight()); assertEquals("#ff0000", style.getBackground()); assertEquals("#0000ff", style.getColor()); - assertEquals(new Integer(24), style.getFontSize()); + assertEquals(Integer.valueOf(24), style.getFontSize()); assertEquals(Shape.RoundedBox, style.getShape()); assertNull(style.getIcon()); assertEquals(Border.Solid, style.getBorder()); assertEquals("#00ff00", style.getStroke()); - assertEquals(new Integer(100), style.getOpacity()); + assertEquals(Integer.valueOf(100), style.getOpacity()); assertEquals(true, style.getMetadata()); assertEquals(true, style.getDescription()); } @@ -80,16 +80,16 @@ public void test_findElementStyle_ReturnsTheCorrectStyleForAnElementInstance_Whe styles.addElementStyle("Some Tag").color("#0000ff").stroke("#00ff00").shape(Shape.RoundedBox).width(123).height(456); ElementStyle style = styles.findElementStyle(softwareSystemInstance); - assertEquals(new Integer(123), style.getWidth()); - assertEquals(new Integer(456), style.getHeight()); + assertEquals(Integer.valueOf(123), style.getWidth()); + assertEquals(Integer.valueOf(456), style.getHeight()); assertEquals("#ff0000", style.getBackground()); assertEquals("#0000ff", style.getColor()); - assertEquals(new Integer(24), style.getFontSize()); + assertEquals(Integer.valueOf(24), style.getFontSize()); assertEquals(Shape.RoundedBox, style.getShape()); assertNull(style.getIcon()); assertEquals(Border.Solid, style.getBorder()); assertEquals("#00ff00", style.getStroke()); - assertEquals(new Integer(100), style.getOpacity()); + assertEquals(Integer.valueOf(100), style.getOpacity()); assertEquals(true, style.getMetadata()); assertEquals(true, style.getDescription()); } @@ -104,8 +104,8 @@ public void test_findElementStyle_ReturnsTheDefaultElementSize_WhenTheShapeIsABo ElementStyle style = styles.findElementStyle(element); assertEquals(Shape.Box, style.getShape()); - assertEquals(new Integer(450), style.getWidth()); - assertEquals(new Integer(300), style.getHeight()); + assertEquals(Integer.valueOf(450), style.getWidth()); + assertEquals(Integer.valueOf(300), style.getHeight()); } @Test @@ -118,21 +118,21 @@ public void test_findElementStyle_ReturnsTheDefaultElementSize_WhenTheShapeIsAPe ElementStyle style = styles.findElementStyle(element); assertEquals(Shape.Person, style.getShape()); - assertEquals(new Integer(400), style.getWidth()); - assertEquals(new Integer(400), style.getHeight()); + assertEquals(Integer.valueOf(400), style.getWidth()); + assertEquals(Integer.valueOf(400), style.getHeight()); } @Test public void test_findRelationshipStyle_ReturnsTheDefaultStyle_WhenPassedNull() { RelationshipStyle style = styles.findRelationshipStyle((Relationship)null); - assertEquals(new Integer(2), style.getThickness()); + assertEquals(Integer.valueOf(2), style.getThickness()); assertEquals("#707070", style.getColor()); assertTrue(style.getDashed()); assertEquals(Routing.Direct, style.getRouting()); - assertEquals(new Integer(24), style.getFontSize()); - assertEquals(new Integer(200), style.getWidth()); - assertEquals(new Integer(50), style.getPosition()); - assertEquals(new Integer(100), style.getOpacity()); + assertEquals(Integer.valueOf(24), style.getFontSize()); + assertEquals(Integer.valueOf(200), style.getWidth()); + assertEquals(Integer.valueOf(50), style.getPosition()); + assertEquals(Integer.valueOf(100), style.getOpacity()); } @Test @@ -140,14 +140,14 @@ public void test_findRelationshipStyle_ReturnsTheDefaultStyle_WhenNoStylesAreDef SoftwareSystem element = model.addSoftwareSystem("Name", "Description"); Relationship relationship = element.uses(element, "Uses"); RelationshipStyle style = styles.findRelationshipStyle(relationship); - assertEquals(new Integer(2), style.getThickness()); + assertEquals(Integer.valueOf(2), style.getThickness()); assertEquals("#707070", style.getColor()); assertTrue(style.getDashed()); assertEquals(Routing.Direct, style.getRouting()); - assertEquals(new Integer(24), style.getFontSize()); - assertEquals(new Integer(200), style.getWidth()); - assertEquals(new Integer(50), style.getPosition()); - assertEquals(new Integer(100), style.getOpacity()); + assertEquals(Integer.valueOf(24), style.getFontSize()); + assertEquals(Integer.valueOf(200), style.getWidth()); + assertEquals(Integer.valueOf(50), style.getPosition()); + assertEquals(Integer.valueOf(100), style.getOpacity()); } @Test @@ -160,14 +160,14 @@ public void test_findRelationshipStyle_ReturnsTheCorrectStyle_WhenStylesAreDefin styles.addRelationshipStyle("Some Tag").color("#0000ff"); RelationshipStyle style = styles.findRelationshipStyle(relationship); - assertEquals(new Integer(2), style.getThickness()); + assertEquals(Integer.valueOf(2), style.getThickness()); assertEquals("#0000ff", style.getColor()); assertTrue(style.getDashed()); assertEquals(Routing.Direct, style.getRouting()); - assertEquals(new Integer(24), style.getFontSize()); - assertEquals(new Integer(200), style.getWidth()); - assertEquals(new Integer(50), style.getPosition()); - assertEquals(new Integer(100), style.getOpacity()); + assertEquals(Integer.valueOf(24), style.getFontSize()); + assertEquals(Integer.valueOf(200), style.getWidth()); + assertEquals(Integer.valueOf(50), style.getPosition()); + assertEquals(Integer.valueOf(100), style.getOpacity()); } @Test From c03d063fe6c408596c14768f03611a85227b433a Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 15 Jul 2022 15:14:47 +0100 Subject: [PATCH 299/717] Adds a helper method (AbstractImpliedRelationshipsStrategy.createImpliedRelationship) to create implied relationships, which can then be used by custom implementations ... see https://github.com/structurizr/structurizr/issues/97 for why. --- build.gradle | 2 +- docs/changelog.md | 6 +++++- .../AbstractImpliedRelationshipsStrategy.java | 18 ++++++++++++++++++ ...ipsUnlessAnyRelationshipExistsStrategy.java | 5 +---- ...psUnlessSameRelationshipExistsStrategy.java | 5 +---- 5 files changed, 26 insertions(+), 10 deletions(-) diff --git a/build.gradle b/build.gradle index c4587869b..eb6c17aec 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.13.0' + version = '1.13.1' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 79ae8e40f..d1566a0f2 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,10 @@ # Changelog -## 1.13.0 (unreleased to Maven Central) +## 1.13.1 (unreleased to Maven Central) + +- Adds a helper method (`AbstractImpliedRelationshipsStrategy.createImpliedRelationship`) to create implied relationships, which can then be used by custom implementations. + +## 1.13.0 (25th June 2022) - Adds support for name/value properties on element and relationship styles. diff --git a/structurizr-core/src/com/structurizr/model/AbstractImpliedRelationshipsStrategy.java b/structurizr-core/src/com/structurizr/model/AbstractImpliedRelationshipsStrategy.java index 10391fa1d..ba051fbe7 100644 --- a/structurizr-core/src/com/structurizr/model/AbstractImpliedRelationshipsStrategy.java +++ b/structurizr-core/src/com/structurizr/model/AbstractImpliedRelationshipsStrategy.java @@ -30,4 +30,22 @@ private boolean isChildOf(Element e1, Element e2) { return false; } + /** + * Creates an implied relationship based upon the specified relationship, between the specified source and destination elements. + * + * @param relationship the Relationship on which the implied relationship is based + * @param source the implied relationship source + * @param destination the implied relationship destination + * @return a Relationship object representing the implied relationship, or null if one wasn't created + */ + protected Relationship createImpliedRelationship(Relationship relationship, Element source, Element destination) { + Model model = relationship.getModel(); + Relationship impliedRelationship = model.addRelationship(source, destination, relationship.getDescription(), relationship.getTechnology(), false); + if (impliedRelationship != null) { + impliedRelationship.setLinkedRelationshipId(relationship.getId()); + } + + return impliedRelationship; + } + } \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy.java b/structurizr-core/src/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy.java index 6d2388074..1c75952c4 100644 --- a/structurizr-core/src/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy.java +++ b/structurizr-core/src/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy.java @@ -19,10 +19,7 @@ public void createImpliedRelationships(Relationship relationship) { boolean createRelationship = !source.hasEfferentRelationshipWith(destination); if (createRelationship) { - Relationship impliedRelationship = model.addRelationship(source, destination, relationship.getDescription(), relationship.getTechnology(), false); - if (impliedRelationship != null) { - impliedRelationship.setLinkedRelationshipId(relationship.getId()); - } + createImpliedRelationship(relationship, source, destination); } } diff --git a/structurizr-core/src/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategy.java b/structurizr-core/src/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategy.java index 4abaf94f1..e849294db 100644 --- a/structurizr-core/src/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategy.java +++ b/structurizr-core/src/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategy.java @@ -19,10 +19,7 @@ public void createImpliedRelationships(Relationship relationship) { boolean createRelationship = !source.hasEfferentRelationshipWith(destination, relationship.getDescription()); if (createRelationship) { - Relationship impliedRelationship = model.addRelationship(source, destination, relationship.getDescription(), relationship.getTechnology(), false); - if (impliedRelationship != null) { - impliedRelationship.setLinkedRelationshipId(relationship.getId()); - } + createImpliedRelationship(relationship, source, destination); } } From 0beaf2d6431e519e912422b43e881ef5486caf27 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 22 Jul 2022 12:09:48 +0100 Subject: [PATCH 300/717] Fixes #177 (Revise class and method visibilities related to styles to use them programmatically). --- .../src/com/structurizr/view/ThemeUtils.java | 2 +- .../src/com/structurizr/view/Styles.java | 19 +++++++++++++------ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/structurizr-client/src/com/structurizr/view/ThemeUtils.java b/structurizr-client/src/com/structurizr/view/ThemeUtils.java index 045e71579..ce1eef78b 100644 --- a/structurizr-client/src/com/structurizr/view/ThemeUtils.java +++ b/structurizr-client/src/com/structurizr/view/ThemeUtils.java @@ -80,7 +80,7 @@ public static void loadThemes(Workspace workspace) throws Exception { Theme theme = objectMapper.readValue(json, Theme.class); - workspace.getViews().getConfiguration().getStyles().addStylesFromTheme(url, theme.getElements(), theme.getRelationships()); + workspace.getViews().getConfiguration().getStyles().addStylesFromTheme(theme); } httpClient.close(); diff --git a/structurizr-core/src/com/structurizr/view/Styles.java b/structurizr-core/src/com/structurizr/view/Styles.java index e5e33813b..44a290ddc 100644 --- a/structurizr-core/src/com/structurizr/view/Styles.java +++ b/structurizr-core/src/com/structurizr/view/Styles.java @@ -17,7 +17,7 @@ public final class Styles { private Collection elements = new LinkedList<>(); private Collection relationships = new LinkedList<>(); - private Map themes = new LinkedHashMap<>(); + private List themes = new ArrayList<>(); public Collection getElements() { return elements; @@ -100,7 +100,7 @@ public ElementStyle findElementStyle(String tag) { ElementStyle style = new ElementStyle(tag); Collection elementStyles = new ArrayList<>(); - for (Theme theme : themes.values()) { + for (Theme theme : themes) { elementStyles.addAll(theme.getElements()); } elementStyles.addAll(elements); @@ -131,7 +131,7 @@ public RelationshipStyle findRelationshipStyle(String tag) { RelationshipStyle style = new RelationshipStyle(tag); Collection relationshipStyles= new ArrayList<>(); - for (Theme theme : themes.values()) { + for (Theme theme : themes) { relationshipStyles.addAll(theme.getRelationships()); } relationshipStyles.addAll(relationships); @@ -228,8 +228,15 @@ public RelationshipStyle findRelationshipStyle(Relationship relationship) { return style; } - void addStylesFromTheme(String url, Collection elements, Collection relationships) { - themes.put(url, new Theme(elements, relationships)); + /** + * Adds the element/relationship styles from the given theme. + * + * @param theme a Theme object + */ + public void addStylesFromTheme(Theme theme) { + if (theme != null) { + themes.add(theme); + } } -} +} \ No newline at end of file From 1e0f098494d6e1e4b060aa33cba3f621e41b1c2e Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 22 Jul 2022 12:13:46 +0100 Subject: [PATCH 301/717] Fixes tests. --- .../test/unit/com/structurizr/view/ThemeUtilsTests.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/structurizr-client/test/unit/com/structurizr/view/ThemeUtilsTests.java b/structurizr-client/test/unit/com/structurizr/view/ThemeUtilsTests.java index 119d10390..e1621d658 100644 --- a/structurizr-client/test/unit/com/structurizr/view/ThemeUtilsTests.java +++ b/structurizr-client/test/unit/com/structurizr/view/ThemeUtilsTests.java @@ -77,13 +77,13 @@ public void test_findElementStyle_WithThemes() { Collection elementStyles = new ArrayList<>(); Collection relationshipStyles = new ArrayList<>(); elementStyles.add(new ElementStyle("Element").shape(Shape.Box).background("#000000").color("#ffffff")); - workspace.getViews().getConfiguration().getStyles().addStylesFromTheme("url1", elementStyles, relationshipStyles); + workspace.getViews().getConfiguration().getStyles().addStylesFromTheme(new Theme(elementStyles, relationshipStyles)); // theme 2 elementStyles = new ArrayList<>(); relationshipStyles = new ArrayList<>(); elementStyles.add(new ElementStyle("Element").background("#ff0000")); - workspace.getViews().getConfiguration().getStyles().addStylesFromTheme("url2", elementStyles, relationshipStyles); + workspace.getViews().getConfiguration().getStyles().addStylesFromTheme(new Theme(elementStyles, relationshipStyles)); ElementStyle style = workspace.getViews().getConfiguration().getStyles().findElementStyle(softwareSystem); assertEquals(Integer.valueOf(450), style.getWidth()); @@ -111,13 +111,13 @@ public void test_findRelationshipStyle_WithThemes() { Collection elementStyles = new ArrayList<>(); Collection relationshipStyles = new ArrayList<>(); relationshipStyles.add(new RelationshipStyle("Relationship").color("#ff0000").thickness(4)); - workspace.getViews().getConfiguration().getStyles().addStylesFromTheme("url1", elementStyles, relationshipStyles); + workspace.getViews().getConfiguration().getStyles().addStylesFromTheme(new Theme(elementStyles, relationshipStyles)); // theme 2 elementStyles = new ArrayList<>(); relationshipStyles = new ArrayList<>(); relationshipStyles.add(new RelationshipStyle("Relationship").color("#0000ff")); - workspace.getViews().getConfiguration().getStyles().addStylesFromTheme("url2", elementStyles, relationshipStyles); + workspace.getViews().getConfiguration().getStyles().addStylesFromTheme(new Theme(elementStyles, relationshipStyles)); RelationshipStyle style = workspace.getViews().getConfiguration().getStyles().findRelationshipStyle(relationship); assertEquals(Integer.valueOf(4), style.getThickness()); // from theme 1 From 11416d1db4e0e79b46da79ec84aeb3a1bb550e3e Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 28 Jul 2022 17:28:00 +0100 Subject: [PATCH 302/717] Adds the PropertyHolder interface, to make it easier to set properties via the DSL. --- structurizr-core/src/com/structurizr/AbstractWorkspace.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structurizr-core/src/com/structurizr/AbstractWorkspace.java b/structurizr-core/src/com/structurizr/AbstractWorkspace.java index 0375ba917..c78f9c0fd 100644 --- a/structurizr-core/src/com/structurizr/AbstractWorkspace.java +++ b/structurizr-core/src/com/structurizr/AbstractWorkspace.java @@ -10,7 +10,7 @@ /** * The superclass for regular and encrypted workspaces. */ -public abstract class AbstractWorkspace { +public abstract class AbstractWorkspace implements PropertyHolder { private long id; private String name; From cd3fa5dda81e1a0ffd69237cc114dc3c9a6ac2c1 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 30 Jul 2022 13:34:15 +0100 Subject: [PATCH 303/717] Provides a way to add specific relationships to dynamic views. --- build.gradle | 2 +- docs/changelog.md | 3 +- .../src/com/structurizr/view/DynamicView.java | 31 +++++++++++ .../structurizr/view/DynamicViewTests.java | 54 +++++++++++++++++++ 4 files changed, 88 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index eb6c17aec..5200d34b9 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.13.1' + version = '1.14.0' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index d1566a0f2..10c2116f9 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,8 +1,9 @@ # Changelog -## 1.13.1 (unreleased to Maven Central) +## 1.14.0 (unreleased to Maven Central) - Adds a helper method (`AbstractImpliedRelationshipsStrategy.createImpliedRelationship`) to create implied relationships, which can then be used by custom implementations. +- Provides a way to add specific relationships to dynamic views. ## 1.13.0 (25th June 2022) diff --git a/structurizr-core/src/com/structurizr/view/DynamicView.java b/structurizr-core/src/com/structurizr/view/DynamicView.java index 9a29dd37b..f2b566454 100644 --- a/structurizr-core/src/com/structurizr/view/DynamicView.java +++ b/structurizr-core/src/com/structurizr/view/DynamicView.java @@ -165,6 +165,37 @@ public RelationshipView add(@Nonnull StaticStructureElement source, String descr } } + /** + * Adds a specific relationship to this dynamic view, with the original description. + * + * @param relationship the Relationship to add + * @return a RelationshipView + */ + public RelationshipView add(Relationship relationship) { + return add(relationship, ""); + } + + /** + * Adds a specific relationship to this dynamic view, with an overidden description. + * + * @param relationship the Relationship to add + * @param description the overidden description + * @return a RelationshipView + */ + public RelationshipView add(Relationship relationship, String description) { + if (relationship == null) { + throw new IllegalArgumentException("A relationship must be specified."); + } + + checkElementCanBeAdded(relationship.getSource()); + checkElementCanBeAdded(relationship.getDestination()); + + addElement(relationship.getSource(), false); + addElement(relationship.getDestination(), false); + + return addRelationship(relationship, description, sequenceNumber.getNext(), false); + } + protected RelationshipView addRelationship(Relationship relationship, String description, String order, boolean response) { RelationshipView relationshipView = addRelationship(relationship); if (relationshipView != null) { diff --git a/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java b/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java index f26997d7d..065c0befc 100644 --- a/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java @@ -208,6 +208,40 @@ public void test_add_ThrowsAnException_WhenARelationshipBetweenTheSourceAndDesti } } + @Test + public void test_addRelationshipWithOriginalDescription() { + workspace = new Workspace("Name", "Description"); + model = workspace.getModel(); + + SoftwareSystem a = workspace.getModel().addSoftwareSystem("A"); + SoftwareSystem b = workspace.getModel().addSoftwareSystem("B"); + Relationship relationship = a.uses(b, "Uses"); + + DynamicView view = workspace.getViews().createDynamicView("key", "Description"); + view.add(relationship); + + assertEquals(2, view.getElements().size()); + assertSame(relationship, view.getRelationships().iterator().next().getRelationship()); + assertEquals("", view.getRelationships().iterator().next().getDescription()); + } + + @Test + public void test_addRelationshipWithOveriddenDescription() { + workspace = new Workspace("Name", "Description"); + model = workspace.getModel(); + + SoftwareSystem a = workspace.getModel().addSoftwareSystem("A"); + SoftwareSystem b = workspace.getModel().addSoftwareSystem("B"); + Relationship relationship = a.uses(b, "Uses"); + + DynamicView view = workspace.getViews().createDynamicView("key", "Description"); + view.add(relationship, "New description"); + + assertEquals(2, view.getElements().size()); + assertSame(relationship, view.getRelationships().iterator().next().getRelationship()); + assertEquals("New description", view.getRelationships().iterator().next().getDescription()); + } + @Test public void test_add_AddsTheSourceAndDestinationElements_WhenARelationshipBetweenThemExists() { final DynamicView dynamicView = workspace.getViews().createDynamicView(softwareSystemA, "key", "Description"); @@ -245,6 +279,26 @@ public void test_normalSequence() { assertSame(container3, view.getRelationships().stream().filter(r -> r.getOrder().equals("2")).findFirst().get().getRelationship().getDestination()); } + @Test + public void test_normalSequence_WhenThereAreMultipleDescriptions() { + workspace = new Workspace("Name", "Description"); + model = workspace.getModel(); + + SoftwareSystem ss1 = workspace.getModel().addSoftwareSystem("Software System 1", ""); + SoftwareSystem ss2 = workspace.getModel().addSoftwareSystem("Software System 2", ""); + + Relationship r1 = ss1.uses(ss2, "Uses 1"); + Relationship r2 = ss1.uses(ss2, "Uses 2"); + + DynamicView view = workspace.getViews().createDynamicView("key", "Description"); + + RelationshipView rv1 = view.add(ss1, "Uses 1", ss2); + RelationshipView rv2 = view.add(ss1, "Uses 2", ss2); + + assertSame(r1, rv1.getRelationship()); + assertSame(r2, rv2.getRelationship()); + } + @Test public void test_normalSequence_WhenThereAreMultipleTechnologies() { workspace = new Workspace("Name", "Description"); From 3f2ed13b7fda1895ec57bea0a3fe191c9ee85a3e Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Mon, 1 Aug 2022 20:25:12 +0200 Subject: [PATCH 304/717] JUnit 5 --- structurizr-client/build.gradle | 6 +- .../api/BackwardsCompatibilityTests.java | 6 +- .../StructurizrClientIntegrationTests.java | 24 +-- .../api/WorkspaceRulesValidationTests.java | 40 ++--- .../com/structurizr/api/ApiResponseTests.java | 6 +- ...shBasedMessageAuthenticationCodeTests.java | 6 +- .../api/HmacAuthorizationHeaderTests.java | 8 +- .../com/structurizr/api/HmacContentTests.java | 8 +- .../com/structurizr/api/Md5DigestTests.java | 8 +- .../api/StructurizrClientTests.java | 38 ++--- .../AesEncryptionStrategyTests.java | 69 ++++---- .../encryption/EncryptedWorkspaceTests.java | 18 +- .../io/json/EncryptedJsonTests.java | 6 +- .../io/json/EncryptedJsonWriterTests.java | 10 +- .../com/structurizr/io/json/JsonTests.java | 12 +- .../structurizr/io/json/JsonWriterTests.java | 10 +- .../structurizr/util/WorkspaceUtilsTests.java | 27 ++- .../com/structurizr/view/ThemeUtilsTests.java | 17 +- structurizr-core/build.gradle | 6 +- .../unit/com/structurizr/WorkspaceTests.java | 16 +- .../WorkspaceConfigurationTests.java | 14 +- .../documentation/DecisionTests.java | 8 +- .../documentation/DocumentationTests.java | 30 ++-- .../documentation/SectionTests.java | 6 +- .../structurizr/model/CodeElementTests.java | 69 ++++---- .../com/structurizr/model/ComponentTests.java | 37 ++--- .../model/ContainerInstanceTests.java | 42 ++--- .../com/structurizr/model/ContainerTests.java | 54 +++--- ...essAnyRelationshipExistsStrategyTests.java | 10 +- ...ssSameRelationshipExistsStrategyTests.java | 8 +- .../structurizr/model/CustomElementTests.java | 26 +-- ...aultImpliedRelationshipsStrategyTests.java | 8 +- .../model/DeploymentNodeTests.java | 48 +++--- .../com/structurizr/model/ElementTests.java | 90 +++++----- .../model/GroupableElementTests.java | 14 +- .../model/HttpHealthCheckTests.java | 20 +-- .../model/InfrastructureNodeTests.java | 12 +- .../com/structurizr/model/ModelItemTests.java | 79 +++++---- .../com/structurizr/model/ModelTests.java | 144 ++++++++-------- .../com/structurizr/model/PersonTests.java | 20 +-- .../structurizr/model/RelationshipTests.java | 44 ++--- .../model/SoftwareSystemInstanceTests.java | 42 ++--- .../model/SoftwareSystemTests.java | 62 +++---- .../com/structurizr/util/ImageUtilsTests.java | 38 ++--- .../structurizr/util/StringUtilsTests.java | 12 +- .../unit/com/structurizr/util/UrlTests.java | 14 +- .../view/AutomaticLayoutTests.java | 14 +- .../com/structurizr/view/BrandingTests.java | 27 +-- .../com/structurizr/view/ColorPairTests.java | 24 +-- .../unit/com/structurizr/view/ColorTests.java | 14 +- .../structurizr/view/ComponentViewTests.java | 110 ++++++------ .../structurizr/view/ConfigurationTests.java | 31 ++-- .../structurizr/view/ContainerViewTests.java | 50 +++--- .../view/DefaultLayoutMergeStrategyTests.java | 18 +- .../structurizr/view/DeploymentViewTests.java | 82 ++++----- .../com/structurizr/view/DimensionsTests.java | 11 +- .../structurizr/view/DynamicViewTests.java | 58 +++---- .../structurizr/view/ElementStyleTests.java | 116 +++++++------ .../structurizr/view/ElementViewTests.java | 8 +- .../structurizr/view/FilteredViewTests.java | 6 +- .../unit/com/structurizr/view/FontTests.java | 27 +-- .../com/structurizr/view/PaperSizeTests.java | 10 +- .../view/RelationshipStyleTests.java | 56 ++++--- .../view/SequenceCounterTests.java | 6 +- .../structurizr/view/SequenceNumberTests.java | 8 +- .../com/structurizr/view/StaticViewTests.java | 12 +- .../com/structurizr/view/StylesTests.java | 46 +++--- .../view/SystemContextViewTests.java | 56 +++---- .../view/SystemLandscapeViewTests.java | 54 +++--- .../structurizr/view/TerminologyTests.java | 6 +- .../com/structurizr/view/VertexTests.java | 20 +-- .../com/structurizr/view/ViewSetTests.java | 156 +++++++++--------- .../unit/com/structurizr/view/ViewTests.java | 86 +++++----- 73 files changed, 1234 insertions(+), 1170 deletions(-) diff --git a/structurizr-client/build.gradle b/structurizr-client/build.gradle index 6c5bd755d..2c4936daf 100644 --- a/structurizr-client/build.gradle +++ b/structurizr-client/build.gradle @@ -10,5 +10,9 @@ dependencies { implementation 'commons-logging:commons-logging:1.2' - testImplementation 'junit:junit:4.12' + testImplementation(platform('org.junit:junit-bom:5.9.0')) + testImplementation('org.junit.jupiter:junit-jupiter') +} +test { + useJUnitPlatform() } \ No newline at end of file diff --git a/structurizr-client/test/integration/com/structurizr/api/BackwardsCompatibilityTests.java b/structurizr-client/test/integration/com/structurizr/api/BackwardsCompatibilityTests.java index b5c9959da..e4177ab26 100644 --- a/structurizr-client/test/integration/com/structurizr/api/BackwardsCompatibilityTests.java +++ b/structurizr-client/test/integration/com/structurizr/api/BackwardsCompatibilityTests.java @@ -1,16 +1,16 @@ package com.structurizr.api; import com.structurizr.util.WorkspaceUtils; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.File; -public class BackwardsCompatibilityTests { +class BackwardsCompatibilityTests { private static final File PATH_TO_WORKSPACE_FILES = new File("test/integration/backwardsCompatibility"); @Test - public void test() throws Exception { + void test() throws Exception { for (File file : PATH_TO_WORKSPACE_FILES.listFiles(f -> f.getName().endsWith(".json"))) { WorkspaceUtils.loadWorkspaceFromJson(file); } diff --git a/structurizr-client/test/integration/com/structurizr/api/StructurizrClientIntegrationTests.java b/structurizr-client/test/integration/com/structurizr/api/StructurizrClientIntegrationTests.java index d0c16ef78..3c0fd2ba7 100644 --- a/structurizr-client/test/integration/com/structurizr/api/StructurizrClientIntegrationTests.java +++ b/structurizr-client/test/integration/com/structurizr/api/StructurizrClientIntegrationTests.java @@ -8,22 +8,22 @@ import com.structurizr.model.Person; import com.structurizr.model.SoftwareSystem; import com.structurizr.view.SystemContextView; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.io.File; import java.io.FileReader; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class StructurizrClientIntegrationTests { private StructurizrClient structurizrClient; private File workspaceArchiveLocation = new File(System.getProperty("java.io.tmpdir"), "structurizr"); - @Before - public void setUp() { + @BeforeEach + void setUp() { structurizrClient = new StructurizrClient("81ace434-94a1-486f-a786-37bbeaa44e08", "a8673e21-7b6f-4f52-be65-adb7248be86b"); structurizrClient.setWorkspaceArchiveLocation(workspaceArchiveLocation); workspaceArchiveLocation.mkdirs(); @@ -32,8 +32,8 @@ public void setUp() { structurizrClient.setMergeFromRemote(false); } - @After - public void tearDown() { + @AfterEach + void tearDown() { clearWorkspaceArchive(); workspaceArchiveLocation.delete(); } @@ -51,7 +51,7 @@ private File getArchivedWorkspace() { } @Test - public void test_putAndGetWorkspace_WithoutEncryption() throws Exception { + void test_putAndGetWorkspace_WithoutEncryption() throws Exception { Workspace workspace = new Workspace("Structurizr client library tests - without encryption", "A test workspace for the Structurizr client library"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); Person person = workspace.getModel().addPerson("Person", "Description"); @@ -77,7 +77,7 @@ public void test_putAndGetWorkspace_WithoutEncryption() throws Exception { } @Test - public void test_putAndGetWorkspace_WithEncryption() throws Exception { + void test_putAndGetWorkspace_WithEncryption() throws Exception { structurizrClient.setEncryptionStrategy(new AesEncryptionStrategy("password")); Workspace workspace = new Workspace("Structurizr client library tests - with encryption", "A test workspace for the Structurizr client library"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); @@ -104,14 +104,14 @@ public void test_putAndGetWorkspace_WithEncryption() throws Exception { } @Test - public void test_lockWorkspace() throws Exception { + void test_lockWorkspace() throws Exception { structurizrClient.unlockWorkspace(20081); assertTrue(structurizrClient.lockWorkspace(20081)); } @Test - public void test_unlockWorkspace() throws Exception { + void test_unlockWorkspace() throws Exception { structurizrClient.lockWorkspace(20081); assertTrue(structurizrClient.unlockWorkspace(20081)); } diff --git a/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java b/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java index 88f8b9c08..4319f7128 100644 --- a/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java +++ b/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java @@ -2,18 +2,18 @@ import com.structurizr.WorkspaceValidationException; import com.structurizr.util.WorkspaceUtils; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.File; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class WorkspaceRulesValidationTests { private static final File PATH_TO_WORKSPACE_FILES = new File("test/integration/workspaceValidation"); @Test - public void test_exceptionThrown_WhenElementIdsAreNotUnique() throws Exception { + void test_exceptionThrown_WhenElementIdsAreNotUnique() throws Exception { try { WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "ElementIdsAreNotUnique.json")); fail(); @@ -24,7 +24,7 @@ public void test_exceptionThrown_WhenElementIdsAreNotUnique() throws Exception { } @Test - public void test_exceptionThrown_WhenRelationshipIdsAreNotUnique() throws Exception { + void test_exceptionThrown_WhenRelationshipIdsAreNotUnique() throws Exception { try { WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "RelationshipIdsAreNotUnique.json")); fail(); @@ -35,7 +35,7 @@ public void test_exceptionThrown_WhenRelationshipIdsAreNotUnique() throws Except } @Test - public void test_exceptionThrown_WhenViewKeysAreNotUnique() throws Exception { + void test_exceptionThrown_WhenViewKeysAreNotUnique() throws Exception { try { WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "ViewKeysAreNotUnique.json")); fail(); @@ -45,7 +45,7 @@ public void test_exceptionThrown_WhenViewKeysAreNotUnique() throws Exception { } @Test - public void test_exceptionThrown_WhenPeopleAndSoftwareSystemNamesAreNotUnique() throws Exception { + void test_exceptionThrown_WhenPeopleAndSoftwareSystemNamesAreNotUnique() throws Exception { try { WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "PeopleAndSoftwareSystemNamesAreNotUnique.json")); fail(); @@ -55,7 +55,7 @@ public void test_exceptionThrown_WhenPeopleAndSoftwareSystemNamesAreNotUnique() } @Test - public void test_exceptionThrown_WhenContainerNamesAreNotUnique() throws Exception { + void test_exceptionThrown_WhenContainerNamesAreNotUnique() throws Exception { try { WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "ContainerNamesAreNotUnique.json")); fail(); @@ -65,7 +65,7 @@ public void test_exceptionThrown_WhenContainerNamesAreNotUnique() throws Excepti } @Test - public void test_exceptionThrown_WhenComponentNamesAreNotUnique() throws Exception { + void test_exceptionThrown_WhenComponentNamesAreNotUnique() throws Exception { try { WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "ComponentNamesAreNotUnique.json")); fail(); @@ -75,7 +75,7 @@ public void test_exceptionThrown_WhenComponentNamesAreNotUnique() throws Excepti } @Test - public void test_exceptionThrown_WhenTopLevelDeploymentNodeNamesAreNotUnique() throws Exception { + void test_exceptionThrown_WhenTopLevelDeploymentNodeNamesAreNotUnique() throws Exception { try { WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "TopLevelDeploymentNodeNamesAreNotUnique.json")); fail(); @@ -85,12 +85,12 @@ public void test_exceptionThrown_WhenTopLevelDeploymentNodeNamesAreNotUnique() t } @Test - public void test_exceptionNotThrown_WhenTopLevelDeploymentNodeNamesAreNotUniqueButTheyExistInDifferentEnvironments() throws Exception { + void test_exceptionNotThrown_WhenTopLevelDeploymentNodeNamesAreNotUniqueButTheyExistInDifferentEnvironments() throws Exception { WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "TopLevelDeploymentNodeNamesAreNotUniqueButTheyExistInDifferentEnvironments.json")); } @Test - public void test_exceptionThrown_WhenChildDeploymentNodeNamesAreNotUnique() throws Exception { + void test_exceptionThrown_WhenChildDeploymentNodeNamesAreNotUnique() throws Exception { try { WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "ChildDeploymentNodeNamesAreNotUnique.json")); fail(); @@ -100,7 +100,7 @@ public void test_exceptionThrown_WhenChildDeploymentNodeNamesAreNotUnique() thro } @Test - public void test_exceptionThrown_WhenRelationshipDescriptionsAreNotUnique() throws Exception { + void test_exceptionThrown_WhenRelationshipDescriptionsAreNotUnique() throws Exception { try { WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "RelationshipDescriptionsAreNotUnique.json")); fail(); @@ -110,7 +110,7 @@ public void test_exceptionThrown_WhenRelationshipDescriptionsAreNotUnique() thro } @Test - public void test_exceptionThrown_WhenSoftwareSystemAssociatedWithSystemContextViewIsMissingFromTheModel() throws Exception { + void test_exceptionThrown_WhenSoftwareSystemAssociatedWithSystemContextViewIsMissingFromTheModel() throws Exception { try { WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "SoftwareSystemAssociatedWithSystemContextViewIsMissingFromTheModel.json")); fail(); @@ -120,7 +120,7 @@ public void test_exceptionThrown_WhenSoftwareSystemAssociatedWithSystemContextVi } @Test - public void test_exceptionThrown_WhenSoftwareSystemAssociatedWithContainerViewIsMissingFromTheModel() throws Exception { + void test_exceptionThrown_WhenSoftwareSystemAssociatedWithContainerViewIsMissingFromTheModel() throws Exception { try { WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "SoftwareSystemAssociatedWithContainerViewIsMissingFromTheModel.json")); fail(); @@ -130,7 +130,7 @@ public void test_exceptionThrown_WhenSoftwareSystemAssociatedWithContainerViewIs } @Test - public void test_exceptionThrown_WhenContainerAssociatedWithComponentViewIsMissingFromTheModel() throws Exception { + void test_exceptionThrown_WhenContainerAssociatedWithComponentViewIsMissingFromTheModel() throws Exception { try { WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "ContainerAssociatedWithComponentViewIsMissingFromTheModel.json")); fail(); @@ -140,7 +140,7 @@ public void test_exceptionThrown_WhenContainerAssociatedWithComponentViewIsMissi } @Test - public void test_exceptionThrown_WhenElementAssociatedWithDynamicViewIsMissingFromTheModel() throws Exception { + void test_exceptionThrown_WhenElementAssociatedWithDynamicViewIsMissingFromTheModel() throws Exception { try { WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "ElementAssociatedWithDynamicViewIsMissingFromTheModel.json")); fail(); @@ -150,7 +150,7 @@ public void test_exceptionThrown_WhenElementAssociatedWithDynamicViewIsMissingFr } @Test - public void test_exceptionThrown_WhenSoftwareSystemAssociatedWithDeploymentViewIsMissingFromTheModel() throws Exception { + void test_exceptionThrown_WhenSoftwareSystemAssociatedWithDeploymentViewIsMissingFromTheModel() throws Exception { try { WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "SoftwareSystemAssociatedWithDeploymentViewIsMissingFromTheModel.json")); fail(); @@ -160,7 +160,7 @@ public void test_exceptionThrown_WhenSoftwareSystemAssociatedWithDeploymentViewI } @Test - public void test_exceptionThrown_WhenViewAssociatedWithFilteredViewIsMissingFromTheWorkspace() throws Exception { + void test_exceptionThrown_WhenViewAssociatedWithFilteredViewIsMissingFromTheWorkspace() throws Exception { try { WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "ViewAssociatedWithFilteredViewIsMissingFromTheWorkspace.json")); fail(); @@ -170,7 +170,7 @@ public void test_exceptionThrown_WhenViewAssociatedWithFilteredViewIsMissingFrom } @Test - public void test_exceptionThrown_WhenElementReferencedByViewIsMissingFromTheModel() throws Exception { + void test_exceptionThrown_WhenElementReferencedByViewIsMissingFromTheModel() throws Exception { try { WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "ElementReferencedByViewIsMissingFromTheModel.json")); fail(); @@ -180,7 +180,7 @@ public void test_exceptionThrown_WhenElementReferencedByViewIsMissingFromTheMode } @Test - public void test_exceptionThrown_WhenRelationshipReferencedByViewIsMissingFromTheModel() throws Exception { + void test_exceptionThrown_WhenRelationshipReferencedByViewIsMissingFromTheModel() throws Exception { try { WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "RelationshipReferencedByViewIsMissingFromTheModel.json")); fail(); diff --git a/structurizr-client/test/unit/com/structurizr/api/ApiResponseTests.java b/structurizr-client/test/unit/com/structurizr/api/ApiResponseTests.java index b0f088049..0c416cc28 100644 --- a/structurizr-client/test/unit/com/structurizr/api/ApiResponseTests.java +++ b/structurizr-client/test/unit/com/structurizr/api/ApiResponseTests.java @@ -1,13 +1,13 @@ package com.structurizr.api; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class ApiResponseTests { @Test - public void test_parse_createsAnApiErrorObjectWithTheSpecifiedErrorMessage() throws Exception { + void test_parse_createsAnApiErrorObjectWithTheSpecifiedErrorMessage() throws Exception { ApiResponse apiResponse = ApiResponse.parse("{\"message\": \"Hello\"}"); assertEquals("Hello", apiResponse.getMessage()); } diff --git a/structurizr-client/test/unit/com/structurizr/api/HashBasedMessageAuthenticationCodeTests.java b/structurizr-client/test/unit/com/structurizr/api/HashBasedMessageAuthenticationCodeTests.java index 4ac054a91..08a930ccd 100644 --- a/structurizr-client/test/unit/com/structurizr/api/HashBasedMessageAuthenticationCodeTests.java +++ b/structurizr-client/test/unit/com/structurizr/api/HashBasedMessageAuthenticationCodeTests.java @@ -1,15 +1,15 @@ package com.structurizr.api; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class HashBasedMessageAuthenticationCodeTests { private HashBasedMessageAuthenticationCode code; @Test - public void test_generate() throws Exception { + void test_generate() throws Exception { // this example is taken from http://en.wikipedia.org/wiki/Hash-based_message_authentication_code code = new HashBasedMessageAuthenticationCode("key"); assertEquals("f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8", code.generate("The quick brown fox jumps over the lazy dog")); diff --git a/structurizr-client/test/unit/com/structurizr/api/HmacAuthorizationHeaderTests.java b/structurizr-client/test/unit/com/structurizr/api/HmacAuthorizationHeaderTests.java index abf23d95c..df5ff9166 100644 --- a/structurizr-client/test/unit/com/structurizr/api/HmacAuthorizationHeaderTests.java +++ b/structurizr-client/test/unit/com/structurizr/api/HmacAuthorizationHeaderTests.java @@ -1,21 +1,21 @@ package com.structurizr.api; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class HmacAuthorizationHeaderTests { private HmacAuthorizationHeader header; @Test - public void test_format() { + void test_format() { header = new HmacAuthorizationHeader("apiKey", "f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8"); assertEquals("apiKey:ZjdiYzgzZjQzMDUzODQyNGIxMzI5OGU2YWE2ZmIxNDNlZjRkNTlhMTQ5NDYxNzU5OTc0NzlkYmMyZDFhM2NkOA==", header.format()); } @Test - public void test_parse() { + void test_parse() { header = HmacAuthorizationHeader.parse("apiKey:ZjdiYzgzZjQzMDUzODQyNGIxMzI5OGU2YWE2ZmIxNDNlZjRkNTlhMTQ5NDYxNzU5OTc0NzlkYmMyZDFhM2NkOA=="); assertEquals("apiKey", header.getApiKey()); assertEquals("f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8", header.getHmac()); diff --git a/structurizr-client/test/unit/com/structurizr/api/HmacContentTests.java b/structurizr-client/test/unit/com/structurizr/api/HmacContentTests.java index 7397d0ed0..3ee024bdd 100644 --- a/structurizr-client/test/unit/com/structurizr/api/HmacContentTests.java +++ b/structurizr-client/test/unit/com/structurizr/api/HmacContentTests.java @@ -1,19 +1,19 @@ package com.structurizr.api; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class HmacContentTests { @Test - public void test_toString_WhenThereAreNoStrings() { + void test_toString_WhenThereAreNoStrings() { assertEquals("", new HmacContent().toString()); } @Test - public void test_toString_WhenThereAreSomeStrings() { + void test_toString_WhenThereAreSomeStrings() { assertEquals("String1\nString2\nString3\n", new HmacContent("String1", "String2", "String3").toString()); } diff --git a/structurizr-client/test/unit/com/structurizr/api/Md5DigestTests.java b/structurizr-client/test/unit/com/structurizr/api/Md5DigestTests.java index 6cf3d737f..c880e38b3 100644 --- a/structurizr-client/test/unit/com/structurizr/api/Md5DigestTests.java +++ b/structurizr-client/test/unit/com/structurizr/api/Md5DigestTests.java @@ -1,20 +1,20 @@ package com.structurizr.api; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class Md5DigestTests { private Md5Digest md5 = new Md5Digest(); @Test - public void test_generate_TreatsNullAsEmptyContent() throws Exception { + void test_generate_TreatsNullAsEmptyContent() throws Exception { assertEquals(md5.generate(null), md5.generate("")); } @Test - public void test_generate() throws Exception { + void test_generate() throws Exception { assertEquals("ed076287532e86365e841e92bfc50d8c", md5.generate("Hello World!")); assertEquals("d41d8cd98f00b204e9800998ecf8427e", md5.generate("")); } diff --git a/structurizr-client/test/unit/com/structurizr/api/StructurizrClientTests.java b/structurizr-client/test/unit/com/structurizr/api/StructurizrClientTests.java index d0a6caab3..c7d4ecd2a 100644 --- a/structurizr-client/test/unit/com/structurizr/api/StructurizrClientTests.java +++ b/structurizr-client/test/unit/com/structurizr/api/StructurizrClientTests.java @@ -1,16 +1,16 @@ package com.structurizr.api; import com.structurizr.Workspace; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class StructurizrClientTests { private StructurizrClient structurizrClient; @Test - public void test_construction_WithTwoParameters() { + void test_construction_WithTwoParameters() { structurizrClient = new StructurizrClient("key", "secret"); assertEquals("https://api.structurizr.com", structurizrClient.getUrl()); assertEquals("key", structurizrClient.getApiKey()); @@ -18,7 +18,7 @@ public void test_construction_WithTwoParameters() { } @Test - public void test_construction_WithThreeParameters() { + void test_construction_WithThreeParameters() { structurizrClient = new StructurizrClient("https://localhost", "key", "secret"); assertEquals("https://localhost", structurizrClient.getUrl()); assertEquals("key", structurizrClient.getApiKey()); @@ -26,7 +26,7 @@ public void test_construction_WithThreeParameters() { } @Test - public void test_construction_WithThreeParameters_TruncatesTheApiUrl_WhenTheApiUrlHasATrailingSlashCharacter() { + void test_construction_WithThreeParameters_TruncatesTheApiUrl_WhenTheApiUrlHasATrailingSlashCharacter() { structurizrClient = new StructurizrClient("https://localhost/", "key", "secret"); assertEquals("https://localhost", structurizrClient.getUrl()); assertEquals("key", structurizrClient.getApiKey()); @@ -34,7 +34,7 @@ public void test_construction_WithThreeParameters_TruncatesTheApiUrl_WhenTheApiU } @Test - public void test_construction_ThrowsAnException_WhenANullApiKeyIsUsed() { + void test_construction_ThrowsAnException_WhenANullApiKeyIsUsed() { try { structurizrClient = new StructurizrClient(null, "secret"); fail(); @@ -44,7 +44,7 @@ public void test_construction_ThrowsAnException_WhenANullApiKeyIsUsed() { } @Test - public void test_construction_ThrowsAnException_WhenAnEmptyApiKeyIsUsed() { + void test_construction_ThrowsAnException_WhenAnEmptyApiKeyIsUsed() { try { structurizrClient = new StructurizrClient(" ", "secret"); fail(); @@ -54,7 +54,7 @@ public void test_construction_ThrowsAnException_WhenAnEmptyApiKeyIsUsed() { } @Test - public void test_construction_ThrowsAnException_WhenANullApiSecretIsUsed() { + void test_construction_ThrowsAnException_WhenANullApiSecretIsUsed() { try { structurizrClient = new StructurizrClient("key", null); fail(); @@ -64,7 +64,7 @@ public void test_construction_ThrowsAnException_WhenANullApiSecretIsUsed() { } @Test - public void test_construction_ThrowsAnException_WhenAnEmptyApiSecretIsUsed() { + void test_construction_ThrowsAnException_WhenAnEmptyApiSecretIsUsed() { try { structurizrClient = new StructurizrClient("key", " "); fail(); @@ -74,7 +74,7 @@ public void test_construction_ThrowsAnException_WhenAnEmptyApiSecretIsUsed() { } @Test - public void test_construction_ThrowsAnException_WhenANullApiUrlIsUsed() { + void test_construction_ThrowsAnException_WhenANullApiUrlIsUsed() { try { structurizrClient = new StructurizrClient(null, "key", "secret"); fail(); @@ -84,7 +84,7 @@ public void test_construction_ThrowsAnException_WhenANullApiUrlIsUsed() { } @Test - public void test_construction_ThrowsAnException_WhenAnEmptyApiUrlIsUsed() { + void test_construction_ThrowsAnException_WhenAnEmptyApiUrlIsUsed() { try { structurizrClient = new StructurizrClient(" ", "key", "secret"); fail(); @@ -94,7 +94,7 @@ public void test_construction_ThrowsAnException_WhenAnEmptyApiUrlIsUsed() { } @Test - public void test_getWorkspace_ThrowsAnException_WhenTheWorkspaceIdIsNotValid() throws Exception { + void test_getWorkspace_ThrowsAnException_WhenTheWorkspaceIdIsNotValid() throws Exception { try { structurizrClient = new StructurizrClient("key", "secret"); structurizrClient.getWorkspace(0); @@ -105,7 +105,7 @@ public void test_getWorkspace_ThrowsAnException_WhenTheWorkspaceIdIsNotValid() t } @Test - public void test_putWorkspace_ThrowsAnException_WhenTheWorkspaceIdIsNotValid() throws Exception { + void test_putWorkspace_ThrowsAnException_WhenTheWorkspaceIdIsNotValid() throws Exception { try { structurizrClient = new StructurizrClient("key", "secret"); structurizrClient.putWorkspace(0, new Workspace("Name", "Description")); @@ -116,7 +116,7 @@ public void test_putWorkspace_ThrowsAnException_WhenTheWorkspaceIdIsNotValid() t } @Test - public void test_putWorkspace_ThrowsAnException_WhenANullWorkspaceIsSpecified() throws Exception { + void test_putWorkspace_ThrowsAnException_WhenANullWorkspaceIsSpecified() throws Exception { try { structurizrClient = new StructurizrClient("key", "secret"); structurizrClient.putWorkspace(1234, null); @@ -127,7 +127,7 @@ public void test_putWorkspace_ThrowsAnException_WhenANullWorkspaceIsSpecified() } @Test - public void test_constructionWithAPropertiesFile_ThrowsAnException_WhenNoPropertiesAreFound() { + void test_constructionWithAPropertiesFile_ThrowsAnException_WhenNoPropertiesAreFound() { try { structurizrClient = new StructurizrClient(); fail(); @@ -137,20 +137,20 @@ public void test_constructionWithAPropertiesFile_ThrowsAnException_WhenNoPropert } @Test - public void test_getAgent() { + void test_getAgent() { structurizrClient = new StructurizrClient("key", "secret"); assertTrue(structurizrClient.getAgent().startsWith("structurizr-java/")); } @Test - public void test_setAgent() { + void test_setAgent() { structurizrClient = new StructurizrClient("key", "secret"); structurizrClient.setAgent("new_agent"); assertEquals("new_agent", structurizrClient.getAgent()); } @Test - public void test_setAgent_ThrowsAnException_WhenPassedNull() { + void test_setAgent_ThrowsAnException_WhenPassedNull() { structurizrClient = new StructurizrClient("key", "secret"); try { @@ -162,7 +162,7 @@ public void test_setAgent_ThrowsAnException_WhenPassedNull() { } @Test - public void test_setAgent_ThrowsAnException_WhenPassedAnEmptyString() { + void test_setAgent_ThrowsAnException_WhenPassedAnEmptyString() { structurizrClient = new StructurizrClient("key", "secret"); try { diff --git a/structurizr-client/test/unit/com/structurizr/encryption/AesEncryptionStrategyTests.java b/structurizr-client/test/unit/com/structurizr/encryption/AesEncryptionStrategyTests.java index 8c8f4a8ec..8b390f8f6 100644 --- a/structurizr-client/test/unit/com/structurizr/encryption/AesEncryptionStrategyTests.java +++ b/structurizr-client/test/unit/com/structurizr/encryption/AesEncryptionStrategyTests.java @@ -1,17 +1,16 @@ package com.structurizr.encryption; -import org.junit.Test; +import org.junit.jupiter.api.Test; import javax.crypto.BadPaddingException; import java.security.InvalidKeyException; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.*; public class AesEncryptionStrategyTests { @Test - public void test_encrypt_EncryptsPlaintext() throws Exception { + void test_encrypt_EncryptsPlaintext() throws Exception { AesEncryptionStrategy strategy = new AesEncryptionStrategy(128, 1000, "06DC30A48ADEEE72D98E33C2CEAEAD3E", "ED124530AF64A5CAD8EF463CF5628434", "password"); String ciphertext = strategy.encrypt("Hello world"); @@ -19,7 +18,7 @@ public void test_encrypt_EncryptsPlaintext() throws Exception { } @Test - public void test_decrypt_decryptsTheCiphertext_WhenTheSameStrategyInstanceIsUsed() throws Exception { + void test_decrypt_decryptsTheCiphertext_WhenTheSameStrategyInstanceIsUsed() throws Exception { AesEncryptionStrategy strategy = new AesEncryptionStrategy(128, 1000, "password"); String ciphertext = strategy.encrypt("Hello world"); @@ -27,7 +26,7 @@ public void test_decrypt_decryptsTheCiphertext_WhenTheSameStrategyInstanceIsUsed } @Test - public void test_decrypt_decryptsTheCiphertext_WhenTheSameConfigurationIsUsed() throws Exception { + void test_decrypt_decryptsTheCiphertext_WhenTheSameConfigurationIsUsed() throws Exception { AesEncryptionStrategy strategy = new AesEncryptionStrategy(128, 1000, "password"); String ciphertext = strategy.encrypt("Hello world"); @@ -37,7 +36,7 @@ public void test_decrypt_decryptsTheCiphertext_WhenTheSameConfigurationIsUsed() } @Test - public void test_decrypt_doesNotDecryptTheCiphertext_WhenTheIncorrectKeySizeIsUsed() throws Exception { + void test_decrypt_doesNotDecryptTheCiphertext_WhenTheIncorrectKeySizeIsUsed() throws Exception { try { AesEncryptionStrategy strategy = new AesEncryptionStrategy(128, 1000, "password"); @@ -53,44 +52,52 @@ public void test_decrypt_doesNotDecryptTheCiphertext_WhenTheIncorrectKeySizeIsUs } } - @Test(expected = BadPaddingException.class) - public void test_decrypt_doesNotDecryptTheCiphertext_WhenTheIncorrectIterationCountIsUsed() throws Exception { - AesEncryptionStrategy strategy = new AesEncryptionStrategy(128, 1000, "password"); + @Test + void test_decrypt_doesNotDecryptTheCiphertext_WhenTheIncorrectIterationCountIsUsed() throws Exception { + assertThrows(BadPaddingException.class, () -> { + AesEncryptionStrategy strategy = new AesEncryptionStrategy(128, 1000, "password"); - String ciphertext = strategy.encrypt("Hello world"); + String ciphertext = strategy.encrypt("Hello world"); - strategy = new AesEncryptionStrategy(strategy.getKeySize(), 2000, strategy.getSalt(), strategy.getIv(), "password"); - strategy.decrypt(ciphertext); + strategy = new AesEncryptionStrategy(strategy.getKeySize(), 2000, strategy.getSalt(), strategy.getIv(), "password"); + strategy.decrypt(ciphertext); + }); } - @Test(expected = BadPaddingException.class) - public void test_decrypt_doesNotDecryptTheCiphertext_WhenTheIncorrectSaltIsUsed() throws Exception { - AesEncryptionStrategy strategy = new AesEncryptionStrategy(128, 1000, "password"); + @Test + void test_decrypt_doesNotDecryptTheCiphertext_WhenTheIncorrectSaltIsUsed() throws Exception { + assertThrows(BadPaddingException.class, () -> { + AesEncryptionStrategy strategy = new AesEncryptionStrategy(128, 1000, "password"); - String ciphertext = strategy.encrypt("Hello world"); + String ciphertext = strategy.encrypt("Hello world"); - strategy = new AesEncryptionStrategy(strategy.getKeySize(), strategy.getIterationCount(), "133D30C2A658B3081279A97FD3B1F7CDE10C4FB61D39EEA8", strategy.getIv(), "password"); - strategy.decrypt(ciphertext); + strategy = new AesEncryptionStrategy(strategy.getKeySize(), strategy.getIterationCount(), "133D30C2A658B3081279A97FD3B1F7CDE10C4FB61D39EEA8", strategy.getIv(), "password"); + strategy.decrypt(ciphertext); + }); } - @Test(expected = BadPaddingException.class) - public void test_decrypt_doesNotDecryptTheCiphertext_WhenTheIncorrectIvIsUsed() throws Exception { - AesEncryptionStrategy strategy = new AesEncryptionStrategy(128, 1000, "password"); + @Test + void test_decrypt_doesNotDecryptTheCiphertext_WhenTheIncorrectIvIsUsed() throws Exception { + assertThrows(BadPaddingException.class, () -> { + AesEncryptionStrategy strategy = new AesEncryptionStrategy(128, 1000, "password"); - String ciphertext = strategy.encrypt("Hello world"); + String ciphertext = strategy.encrypt("Hello world"); - strategy = new AesEncryptionStrategy(strategy.getKeySize(), strategy.getIterationCount(), strategy.getSalt(), "1DED89E4FB15F61DC6433E3BADA4A891", "password"); - strategy.decrypt(ciphertext); + strategy = new AesEncryptionStrategy(strategy.getKeySize(), strategy.getIterationCount(), strategy.getSalt(), "1DED89E4FB15F61DC6433E3BADA4A891", "password"); + strategy.decrypt(ciphertext); + }); } - @Test(expected = BadPaddingException.class) - public void test_decrypt_doesNotDecryptTheCiphertext_WhenTheIncorrectPassphraseIsUsed() throws Exception { - AesEncryptionStrategy strategy = new AesEncryptionStrategy(128, 1000, "password"); + @Test + void test_decrypt_doesNotDecryptTheCiphertext_WhenTheIncorrectPassphraseIsUsed() throws Exception { + assertThrows(BadPaddingException.class, () -> { + AesEncryptionStrategy strategy = new AesEncryptionStrategy(128, 1000, "password"); - String ciphertext = strategy.encrypt("Hello world"); + String ciphertext = strategy.encrypt("Hello world"); - strategy = new AesEncryptionStrategy(strategy.getKeySize(), strategy.getIterationCount(), strategy.getSalt(), strategy.getIv(), "The Wrong Password"); - strategy.decrypt(ciphertext); + strategy = new AesEncryptionStrategy(strategy.getKeySize(), strategy.getIterationCount(), strategy.getSalt(), strategy.getIv(), "The Wrong Password"); + strategy.decrypt(ciphertext); + }); } } diff --git a/structurizr-client/test/unit/com/structurizr/encryption/EncryptedWorkspaceTests.java b/structurizr-client/test/unit/com/structurizr/encryption/EncryptedWorkspaceTests.java index 1d88a6ff1..81a87b6a0 100644 --- a/structurizr-client/test/unit/com/structurizr/encryption/EncryptedWorkspaceTests.java +++ b/structurizr-client/test/unit/com/structurizr/encryption/EncryptedWorkspaceTests.java @@ -3,14 +3,12 @@ import com.structurizr.Workspace; import com.structurizr.configuration.Role; import com.structurizr.io.json.JsonWriter; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.io.StringWriter; -import static junit.framework.TestCase.assertSame; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.*; public class EncryptedWorkspaceTests { @@ -18,7 +16,7 @@ public class EncryptedWorkspaceTests { private Workspace workspace; private EncryptionStrategy encryptionStrategy; - @Before + @BeforeEach public void setUp() throws Exception { workspace = new Workspace("Name", "Description"); workspace.setVersion("1.2.3"); @@ -31,7 +29,7 @@ public void setUp() throws Exception { } @Test - public void test_construction_WhenTwoParametersAreSpecified() throws Exception { + void test_construction_WhenTwoParametersAreSpecified() throws Exception { encryptedWorkspace = new EncryptedWorkspace(workspace, encryptionStrategy); assertEquals("Name", encryptedWorkspace.getName()); @@ -55,7 +53,7 @@ public void test_construction_WhenTwoParametersAreSpecified() throws Exception { } @Test - public void test_construction_WhenThreeParametersAreSpecified() throws Exception { + void test_construction_WhenThreeParametersAreSpecified() throws Exception { JsonWriter jsonWriter = new JsonWriter(false); StringWriter stringWriter = new StringWriter(); jsonWriter.write(workspace, stringWriter); @@ -77,7 +75,7 @@ public void test_construction_WhenThreeParametersAreSpecified() throws Exception } @Test - public void test_getPlaintext_ReturnsTheDecryptedVersionOfTheCiphertext() throws Exception { + void test_getPlaintext_ReturnsTheDecryptedVersionOfTheCiphertext() throws Exception { encryptedWorkspace = new EncryptedWorkspace(workspace, encryptionStrategy); String cipherText = encryptedWorkspace.getCiphertext(); @@ -89,7 +87,7 @@ public void test_getPlaintext_ReturnsTheDecryptedVersionOfTheCiphertext() throws } @Test - public void test_getWorkspace_ReturnsTheWorkspace_WhenACipherextIsSpecified() throws Exception { + void test_getWorkspace_ReturnsTheWorkspace_WhenACipherextIsSpecified() throws Exception { JsonWriter jsonWriter = new JsonWriter(false); StringWriter stringWriter = new StringWriter(); jsonWriter.write(workspace, stringWriter); diff --git a/structurizr-client/test/unit/com/structurizr/io/json/EncryptedJsonTests.java b/structurizr-client/test/unit/com/structurizr/io/json/EncryptedJsonTests.java index 4b38c5638..14ed4f11e 100644 --- a/structurizr-client/test/unit/com/structurizr/io/json/EncryptedJsonTests.java +++ b/structurizr-client/test/unit/com/structurizr/io/json/EncryptedJsonTests.java @@ -3,17 +3,17 @@ import com.structurizr.Workspace; import com.structurizr.encryption.AesEncryptionStrategy; import com.structurizr.encryption.EncryptedWorkspace; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.StringReader; import java.io.StringWriter; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class EncryptedJsonTests { @Test - public void test_write_and_read() throws Exception { + void test_write_and_read() throws Exception { final Workspace workspace1 = new Workspace("Name", "Description"); // output the model as JSON diff --git a/structurizr-client/test/unit/com/structurizr/io/json/EncryptedJsonWriterTests.java b/structurizr-client/test/unit/com/structurizr/io/json/EncryptedJsonWriterTests.java index 9a9efae49..eccb79d45 100644 --- a/structurizr-client/test/unit/com/structurizr/io/json/EncryptedJsonWriterTests.java +++ b/structurizr-client/test/unit/com/structurizr/io/json/EncryptedJsonWriterTests.java @@ -3,17 +3,17 @@ import com.structurizr.Workspace; import com.structurizr.encryption.AesEncryptionStrategy; import com.structurizr.encryption.EncryptedWorkspace; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.StringWriter; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; public class EncryptedJsonWriterTests { @Test - public void test_write_ThrowsAnIllegalArgumentException_WhenANullEncryptedWorkspaceIsSpecified() throws Exception { + void test_write_ThrowsAnIllegalArgumentException_WhenANullEncryptedWorkspaceIsSpecified() throws Exception { try { EncryptedJsonWriter writer = new EncryptedJsonWriter(true); writer.write(null, new StringWriter()); @@ -24,7 +24,7 @@ public void test_write_ThrowsAnIllegalArgumentException_WhenANullEncryptedWorksp } @Test - public void test_write_ThrowsAnIllegalArgumentException_WhenANullWriterIsSpecified() throws Exception { + void test_write_ThrowsAnIllegalArgumentException_WhenANullWriterIsSpecified() throws Exception { try { EncryptedJsonWriter writer = new EncryptedJsonWriter(true); Workspace workspace = new Workspace("Name", "Description"); diff --git a/structurizr-client/test/unit/com/structurizr/io/json/JsonTests.java b/structurizr-client/test/unit/com/structurizr/io/json/JsonTests.java index baab4d7fc..2c436c2a1 100644 --- a/structurizr-client/test/unit/com/structurizr/io/json/JsonTests.java +++ b/structurizr-client/test/unit/com/structurizr/io/json/JsonTests.java @@ -2,19 +2,19 @@ import com.structurizr.Workspace; import com.structurizr.model.*; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.StringReader; import java.io.StringWriter; import java.util.UUID; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; public class JsonTests { @Test - public void test_write_and_read() throws Exception { + void test_write_and_read() throws Exception { final Workspace workspace1 = new Workspace("Name", "Description"); // output the model as JSON @@ -31,7 +31,7 @@ public void test_write_and_read() throws Exception { } @Test - public void test_backwardsCompatibilityOfRenamingEnterpriseContextViewsToSystemLandscapeViews() throws Exception { + void test_backwardsCompatibilityOfRenamingEnterpriseContextViewsToSystemLandscapeViews() throws Exception { Workspace workspace = new Workspace("Name", "Description"); workspace.getViews().createSystemLandscapeView("key", "description"); @@ -48,7 +48,7 @@ public void test_backwardsCompatibilityOfRenamingEnterpriseContextViewsToSystemL } @Test - public void test_write_and_read_withCustomIdGenerator() throws Exception { + void test_write_and_read_withCustomIdGenerator() throws Exception { Workspace workspace1 = new Workspace("Name", "Description"); workspace1.getModel().setIdGenerator(new CustomIdGenerator()); Person user = workspace1.getModel().addPerson("User"); diff --git a/structurizr-client/test/unit/com/structurizr/io/json/JsonWriterTests.java b/structurizr-client/test/unit/com/structurizr/io/json/JsonWriterTests.java index 48049ebbc..bb7a8301a 100644 --- a/structurizr-client/test/unit/com/structurizr/io/json/JsonWriterTests.java +++ b/structurizr-client/test/unit/com/structurizr/io/json/JsonWriterTests.java @@ -1,17 +1,17 @@ package com.structurizr.io.json; import com.structurizr.Workspace; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.StringWriter; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; public class JsonWriterTests { @Test - public void test_write_ThrowsAnIllegalArgumentException_WhenANullWorkspaceIsSpecified() throws Exception { + void test_write_ThrowsAnIllegalArgumentException_WhenANullWorkspaceIsSpecified() throws Exception { try { JsonWriter writer = new JsonWriter(true); writer.write(null, new StringWriter()); @@ -22,7 +22,7 @@ public void test_write_ThrowsAnIllegalArgumentException_WhenANullWorkspaceIsSpec } @Test - public void test_write_ThrowsAnIllegalArgumentException_WhenANullWriterIsSpecified() throws Exception { + void test_write_ThrowsAnIllegalArgumentException_WhenANullWriterIsSpecified() throws Exception { try { JsonWriter writer = new JsonWriter(true); Workspace workspace = new Workspace("Name", "Description"); diff --git a/structurizr-client/test/unit/com/structurizr/util/WorkspaceUtilsTests.java b/structurizr-client/test/unit/com/structurizr/util/WorkspaceUtilsTests.java index 0ef0bcac0..0657d4ebf 100644 --- a/structurizr-client/test/unit/com/structurizr/util/WorkspaceUtilsTests.java +++ b/structurizr-client/test/unit/com/structurizr/util/WorkspaceUtilsTests.java @@ -1,19 +1,18 @@ package com.structurizr.util; import com.structurizr.Workspace; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.File; import java.io.FilenameFilter; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; public class WorkspaceUtilsTests { @Test - public void test_loadWorkspaceFromJson_ThrowsAnException_WhenANullFileIsSpecified() { + void test_loadWorkspaceFromJson_ThrowsAnException_WhenANullFileIsSpecified() { try { WorkspaceUtils.loadWorkspaceFromJson(null); fail(); @@ -23,7 +22,7 @@ public void test_loadWorkspaceFromJson_ThrowsAnException_WhenANullFileIsSpecifie } @Test - public void test_loadWorkspaceFromJson_ThrowsAnException_WhenTheFileDoesNotExist() { + void test_loadWorkspaceFromJson_ThrowsAnException_WhenTheFileDoesNotExist() { try { WorkspaceUtils.loadWorkspaceFromJson(new File("test/unit/com/structurizr/util/other-workspace.json")); fail(); @@ -33,7 +32,7 @@ public void test_loadWorkspaceFromJson_ThrowsAnException_WhenTheFileDoesNotExist } @Test - public void test_saveWorkspaceToJson_ThrowsAnException_WhenANullWorkspaceIsSpecified() { + void test_saveWorkspaceToJson_ThrowsAnException_WhenANullWorkspaceIsSpecified() { try { WorkspaceUtils.saveWorkspaceToJson(null, null); fail(); @@ -43,7 +42,7 @@ public void test_saveWorkspaceToJson_ThrowsAnException_WhenANullWorkspaceIsSpeci } @Test - public void test_saveWorkspaceToJson_ThrowsAnException_WhenANullFileIsSpecified() { + void test_saveWorkspaceToJson_ThrowsAnException_WhenANullFileIsSpecified() { try { WorkspaceUtils.saveWorkspaceToJson(new Workspace("Name", "Description"), null); fail(); @@ -53,7 +52,7 @@ public void test_saveWorkspaceToJson_ThrowsAnException_WhenANullFileIsSpecified( } @Test - public void test_saveWorkspaceToJson_and_loadWorkspaceFromJson() throws Exception { + void test_saveWorkspaceToJson_and_loadWorkspaceFromJson() throws Exception { File file = new File("build/workspace-utils.json"); Workspace workspace = new Workspace("Name", "Description"); WorkspaceUtils.saveWorkspaceToJson(workspace, file); @@ -63,7 +62,7 @@ public void test_saveWorkspaceToJson_and_loadWorkspaceFromJson() throws Exceptio } @Test - public void test_toJson_ThrowsAnException_WhenANullWorkspaceIsProvided() throws Exception { + void test_toJson_ThrowsAnException_WhenANullWorkspaceIsProvided() throws Exception { try { WorkspaceUtils.toJson(null, true); fail(); @@ -73,7 +72,7 @@ public void test_toJson_ThrowsAnException_WhenANullWorkspaceIsProvided() throws } @Test - public void test_toJson() throws Exception { + void test_toJson() throws Exception { Workspace workspace = new Workspace("Name", "Description"); String indentedOutput = WorkspaceUtils.toJson(workspace, true); String unindentedOutput = WorkspaceUtils.toJson(workspace, false); @@ -97,7 +96,7 @@ public void test_toJson() throws Exception { } @Test - public void test_fromJson_ThrowsAnException_WhenANullJsonStringIsProvided() throws Exception { + void test_fromJson_ThrowsAnException_WhenANullJsonStringIsProvided() throws Exception { try { WorkspaceUtils.fromJson(null); fail(); @@ -107,7 +106,7 @@ public void test_fromJson_ThrowsAnException_WhenANullJsonStringIsProvided() thro } @Test - public void test_fromJson_ThrowsAnException_WhenAnEmptyJsonStringIsProvided() throws Exception { + void test_fromJson_ThrowsAnException_WhenAnEmptyJsonStringIsProvided() throws Exception { try { WorkspaceUtils.fromJson(" "); fail(); @@ -117,7 +116,7 @@ public void test_fromJson_ThrowsAnException_WhenAnEmptyJsonStringIsProvided() th } @Test - public void test_fromJson() throws Exception { + void test_fromJson() throws Exception { Workspace workspace = WorkspaceUtils.fromJson("{\"id\":0,\"name\":\"Name\",\"description\":\"Description\",\"model\":{},\"documentation\":{},\"views\":{\"configuration\":{\"branding\":{},\"styles\":{},\"terminology\":{}}}}"); assertEquals("Name", workspace.getName()); assertEquals("Description", workspace.getDescription()); diff --git a/structurizr-client/test/unit/com/structurizr/view/ThemeUtilsTests.java b/structurizr-client/test/unit/com/structurizr/view/ThemeUtilsTests.java index e1621d658..0714d6a7f 100644 --- a/structurizr-client/test/unit/com/structurizr/view/ThemeUtilsTests.java +++ b/structurizr-client/test/unit/com/structurizr/view/ThemeUtilsTests.java @@ -4,18 +4,17 @@ import com.structurizr.model.Relationship; import com.structurizr.model.SoftwareSystem; import com.structurizr.model.Tags; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.Collection; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class ThemeUtilsTests { @Test - public void test_loadThemes_DoesNothingWhenNoThemesAreDefined() throws Exception { + void test_loadThemes_DoesNothingWhenNoThemesAreDefined() throws Exception { Workspace workspace = new Workspace("Name", "Description"); ThemeUtils.loadThemes(workspace); @@ -24,7 +23,7 @@ public void test_loadThemes_DoesNothingWhenNoThemesAreDefined() throws Exception } @Test - public void test_loadThemes_LoadsThemesWhenThemesAreDefined() throws Exception { + void test_loadThemes_LoadsThemesWhenThemesAreDefined() throws Exception { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name"); softwareSystem.addTags("Amazon Web Services - Alexa For Business"); @@ -44,7 +43,7 @@ public void test_loadThemes_LoadsThemesWhenThemesAreDefined() throws Exception { } @Test - public void test_toJson() throws Exception { + void test_toJson() throws Exception { Workspace workspace = new Workspace("Name", "Description"); assertEquals("{\n" + " \"name\" : \"Name\",\n" + @@ -68,7 +67,7 @@ public void test_toJson() throws Exception { } @Test - public void test_findElementStyle_WithThemes() { + void test_findElementStyle_WithThemes() { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name"); workspace.getViews().getConfiguration().getStyles().addElementStyle("Element").shape(Shape.RoundedBox); @@ -101,7 +100,7 @@ public void test_findElementStyle_WithThemes() { } @Test - public void test_findRelationshipStyle_WithThemes() { + void test_findRelationshipStyle_WithThemes() { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name"); Relationship relationship = softwareSystem.uses(softwareSystem, "Uses"); @@ -122,7 +121,7 @@ public void test_findRelationshipStyle_WithThemes() { RelationshipStyle style = workspace.getViews().getConfiguration().getStyles().findRelationshipStyle(relationship); assertEquals(Integer.valueOf(4), style.getThickness()); // from theme 1 assertEquals("#0000ff", style.getColor()); // from theme 2 - Assert.assertFalse(style.getDashed()); // from workspace + assertFalse(style.getDashed()); // from workspace assertEquals(Routing.Direct, style.getRouting()); assertEquals(Integer.valueOf(24), style.getFontSize()); assertEquals(Integer.valueOf(200), style.getWidth()); diff --git a/structurizr-core/build.gradle b/structurizr-core/build.gradle index 4f395e47f..0fdf5097c 100644 --- a/structurizr-core/build.gradle +++ b/structurizr-core/build.gradle @@ -5,6 +5,10 @@ dependencies { implementation 'commons-logging:commons-logging:1.2' - testImplementation 'junit:junit:4.12' + testImplementation(platform('org.junit:junit-bom:5.9.0')) + testImplementation('org.junit.jupiter:junit-jupiter') testImplementation 'org.assertj:assertj-core:3.9.1' +} +test { + useJUnitPlatform() } \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/WorkspaceTests.java b/structurizr-core/test/unit/com/structurizr/WorkspaceTests.java index e969b6495..447687bca 100644 --- a/structurizr-core/test/unit/com/structurizr/WorkspaceTests.java +++ b/structurizr-core/test/unit/com/structurizr/WorkspaceTests.java @@ -5,38 +5,38 @@ import com.structurizr.model.Component; import com.structurizr.model.Container; import com.structurizr.model.SoftwareSystem; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.File; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class WorkspaceTests { private Workspace workspace = new Workspace("Name", "Description"); @Test - public void test_isEmpty_ReturnsTrue_WhenThereAreNoElementsViewsOrDocumentation() { + void test_isEmpty_ReturnsTrue_WhenThereAreNoElementsViewsOrDocumentation() { workspace = new Workspace("Name", "Description"); assertTrue(workspace.isEmpty()); } @Test - public void test_isEmpty_ReturnsFalse_WhenThereAreElements() { + void test_isEmpty_ReturnsFalse_WhenThereAreElements() { workspace = new Workspace("Name", "Description"); workspace.getModel().addPerson("Name", "Description"); assertFalse(workspace.isEmpty()); } @Test - public void test_isEmpty_ReturnsFalse_WhenThereAreViews() { + void test_isEmpty_ReturnsFalse_WhenThereAreViews() { workspace = new Workspace("Name", "Description"); workspace.getViews().createSystemLandscapeView("key", "Description"); assertFalse(workspace.isEmpty()); } @Test - public void test_isEmpty_ReturnsFalse_WhenThereIsDocumentation() throws Exception { + void test_isEmpty_ReturnsFalse_WhenThereIsDocumentation() throws Exception { workspace = new Workspace("Name", "Description"); Decision d = new Decision("1"); d.setTitle("Title"); @@ -48,7 +48,7 @@ public void test_isEmpty_ReturnsFalse_WhenThereIsDocumentation() throws Exceptio } @Test - public void test_countAndLogWarnings() { + void test_countAndLogWarnings() { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem1 = workspace.getModel().addSoftwareSystem("Software System 1", null); SoftwareSystem softwareSystem2 = workspace.getModel().addSoftwareSystem("Software System 2", " "); @@ -66,7 +66,7 @@ public void test_countAndLogWarnings() { } @Test - public void test_hydrate_DoesNotCrash() { + void test_hydrate_DoesNotCrash() { Workspace workspace = new Workspace("Name", "Description"); assertNotNull(workspace.getViews()); assertNotNull(workspace.getDocumentation()); diff --git a/structurizr-core/test/unit/com/structurizr/configuration/WorkspaceConfigurationTests.java b/structurizr-core/test/unit/com/structurizr/configuration/WorkspaceConfigurationTests.java index 0658abd18..23c39802e 100644 --- a/structurizr-core/test/unit/com/structurizr/configuration/WorkspaceConfigurationTests.java +++ b/structurizr-core/test/unit/com/structurizr/configuration/WorkspaceConfigurationTests.java @@ -1,14 +1,14 @@ package com.structurizr.configuration; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; public class WorkspaceConfigurationTests { @Test - public void test_addUser_ThrowsAnException_WhenANullUsernameIsSpecified() { + void test_addUser_ThrowsAnException_WhenANullUsernameIsSpecified() { try { WorkspaceConfiguration configuration = new WorkspaceConfiguration(); configuration.addUser(null, Role.ReadWrite); @@ -19,7 +19,7 @@ public void test_addUser_ThrowsAnException_WhenANullUsernameIsSpecified() { } @Test - public void test_addUser_ThrowsAnException_WhenAnEmptyUsernameIsSpecified() { + void test_addUser_ThrowsAnException_WhenAnEmptyUsernameIsSpecified() { try { WorkspaceConfiguration configuration = new WorkspaceConfiguration(); configuration.addUser(" ", Role.ReadWrite); @@ -30,7 +30,7 @@ public void test_addUser_ThrowsAnException_WhenAnEmptyUsernameIsSpecified() { } @Test - public void test_addUser_ThrowsAnException_WhenANullRoleIsSpecified() { + void test_addUser_ThrowsAnException_WhenANullRoleIsSpecified() { try { WorkspaceConfiguration configuration = new WorkspaceConfiguration(); configuration.addUser("user@domain.com", null); @@ -41,7 +41,7 @@ public void test_addUser_ThrowsAnException_WhenANullRoleIsSpecified() { } @Test - public void test_addUser_AddsAUser() { + void test_addUser_AddsAUser() { WorkspaceConfiguration configuration = new WorkspaceConfiguration(); configuration.addUser("user@domain.com", Role.ReadOnly); diff --git a/structurizr-core/test/unit/com/structurizr/documentation/DecisionTests.java b/structurizr-core/test/unit/com/structurizr/documentation/DecisionTests.java index 395d33a6e..b107f7568 100644 --- a/structurizr-core/test/unit/com/structurizr/documentation/DecisionTests.java +++ b/structurizr-core/test/unit/com/structurizr/documentation/DecisionTests.java @@ -1,15 +1,15 @@ package com.structurizr.documentation; import com.structurizr.AbstractWorkspaceTestBase; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; public class DecisionTests extends AbstractWorkspaceTestBase { @Test - public void test_hasLinkTo() { + void test_hasLinkTo() { Decision d1 = new Decision("1"); Decision d2 = new Decision("2"); Decision d3 = new Decision("3"); diff --git a/structurizr-core/test/unit/com/structurizr/documentation/DocumentationTests.java b/structurizr-core/test/unit/com/structurizr/documentation/DocumentationTests.java index 1f1ee97fe..e290f5bdd 100644 --- a/structurizr-core/test/unit/com/structurizr/documentation/DocumentationTests.java +++ b/structurizr-core/test/unit/com/structurizr/documentation/DocumentationTests.java @@ -1,22 +1,22 @@ package com.structurizr.documentation; import com.structurizr.AbstractWorkspaceTestBase; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class DocumentationTests extends AbstractWorkspaceTestBase { private Documentation documentation; - @Before + @BeforeEach public void setUp() { documentation = workspace.getDocumentation(); } @Test - public void test_addSection_ThrowsAnException_WhenTheTitleIsNotSpecified() { + void test_addSection_ThrowsAnException_WhenTheTitleIsNotSpecified() { try { Section section = new Section(); @@ -28,7 +28,7 @@ public void test_addSection_ThrowsAnException_WhenTheTitleIsNotSpecified() { } @Test - public void test_addSection_ThrowsAnException_WhenTheContentIsNotSpecified() { + void test_addSection_ThrowsAnException_WhenTheContentIsNotSpecified() { try { Section section = new Section(); section.setTitle("Title"); @@ -41,7 +41,7 @@ public void test_addSection_ThrowsAnException_WhenTheContentIsNotSpecified() { } @Test - public void test_addSection_ThrowsAnException_WhenTheFormatIsNotSpecified() { + void test_addSection_ThrowsAnException_WhenTheFormatIsNotSpecified() { try { Section section = new Section(); section.setTitle("Title"); @@ -55,7 +55,7 @@ public void test_addSection_ThrowsAnException_WhenTheFormatIsNotSpecified() { } @Test - public void test_addSection_ThrowsAnException_WhenASectionExistsWithTheSameTitle() { + void test_addSection_ThrowsAnException_WhenASectionExistsWithTheSameTitle() { try { Section section = new Section(); section.setTitle("Title"); @@ -71,7 +71,7 @@ public void test_addSection_ThrowsAnException_WhenASectionExistsWithTheSameTitle } @Test - public void test_addSection() { + void test_addSection() { Section section = new Section(); section.setTitle("Title"); section.setContent("Content"); @@ -88,7 +88,7 @@ public void test_addSection() { } @Test - public void test_addSection_IncrementsTheSectionOrderNumber() { + void test_addSection_IncrementsTheSectionOrderNumber() { Section section1 = new Section("Title 1", Format.Markdown, "Content"); Section section2 = new Section("Title 2", Format.Markdown, "Content"); Section section3 = new Section("Title 3", Format.Markdown, "Content"); @@ -103,7 +103,7 @@ public void test_addSection_IncrementsTheSectionOrderNumber() { } @Test - public void test_addDecision_ThrowsAnException_WhenTheTitleIsNotSpecified() { + void test_addDecision_ThrowsAnException_WhenTheTitleIsNotSpecified() { try { Decision decision = new Decision("1"); @@ -115,7 +115,7 @@ public void test_addDecision_ThrowsAnException_WhenTheTitleIsNotSpecified() { } @Test - public void test_addDecision_ThrowsAnException_WhenTheContentIsNotSpecified() { + void test_addDecision_ThrowsAnException_WhenTheContentIsNotSpecified() { try { Decision decision = new Decision("1"); decision.setTitle("Title"); @@ -128,7 +128,7 @@ public void test_addDecision_ThrowsAnException_WhenTheContentIsNotSpecified() { } @Test - public void test_addDecision_ThrowsAnException_WhenTheStatusIsNotSpecified() { + void test_addDecision_ThrowsAnException_WhenTheStatusIsNotSpecified() { try { Decision decision = new Decision("1"); decision.setTitle("Title"); @@ -142,7 +142,7 @@ public void test_addDecision_ThrowsAnException_WhenTheStatusIsNotSpecified() { } @Test - public void test_addDecision_ThrowsAnException_WhenTheFormatIsNotSpecified() { + void test_addDecision_ThrowsAnException_WhenTheFormatIsNotSpecified() { try { Decision decision = new Decision("1"); decision.setTitle("Title"); @@ -157,7 +157,7 @@ public void test_addDecision_ThrowsAnException_WhenTheFormatIsNotSpecified() { } @Test - public void test_addDecision_ThrowsAnException_WhenADecisionExistsWithTheSameId() { + void test_addDecision_ThrowsAnException_WhenADecisionExistsWithTheSameId() { try { Decision decision = new Decision("1"); decision.setTitle("Title"); diff --git a/structurizr-core/test/unit/com/structurizr/documentation/SectionTests.java b/structurizr-core/test/unit/com/structurizr/documentation/SectionTests.java index bddc2cf47..92016904a 100644 --- a/structurizr-core/test/unit/com/structurizr/documentation/SectionTests.java +++ b/structurizr-core/test/unit/com/structurizr/documentation/SectionTests.java @@ -1,13 +1,13 @@ package com.structurizr.documentation; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class SectionTests { @Test - public void test_construction() { + void test_construction() { Section section = new Section("Title", Format.Markdown, "Content"); assertEquals("Title", section.getTitle()); diff --git a/structurizr-core/test/unit/com/structurizr/model/CodeElementTests.java b/structurizr-core/test/unit/com/structurizr/model/CodeElementTests.java index 581b14516..d011785a0 100644 --- a/structurizr-core/test/unit/com/structurizr/model/CodeElementTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/CodeElementTests.java @@ -1,28 +1,27 @@ package com.structurizr.model; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static junit.framework.TestCase.assertNull; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class CodeElementTests { @Test - public void test_construction_WhenAFullyQualifiedNameIsSpecified() { + void test_construction_WhenAFullyQualifiedNameIsSpecified() { CodeElement codeElement = new CodeElement("com.structurizr.component.SomeComponent"); assertEquals("SomeComponent", codeElement.getName()); assertEquals("com.structurizr.component.SomeComponent", codeElement.getType()); } @Test - public void test_construction_WhenAFullyQualifiedNameIsSpecifiedInTheDefaultPackage() { + void test_construction_WhenAFullyQualifiedNameIsSpecifiedInTheDefaultPackage() { CodeElement codeElement = new CodeElement("SomeComponent"); assertEquals("SomeComponent", codeElement.getName()); assertEquals("SomeComponent", codeElement.getType()); } @Test - public void test_descriptionProperty() { + void test_descriptionProperty() { CodeElement codeElement = new CodeElement("com.structurizr.component.SomeComponent"); assertNull(codeElement.getDescription()); @@ -31,7 +30,7 @@ public void test_descriptionProperty() { } @Test - public void test_sizeProperty() { + void test_sizeProperty() { CodeElement codeElement = new CodeElement("com.structurizr.component.SomeComponent"); assertEquals(0, codeElement.getSize()); @@ -40,7 +39,7 @@ public void test_sizeProperty() { } @Test - public void test_languageProperty() { + void test_languageProperty() { CodeElement codeElement = new CodeElement("com.structurizr.component.SomeComponent"); assertEquals("Java", codeElement.getLanguage()); @@ -49,7 +48,7 @@ public void test_languageProperty() { } @Test - public void test_categoryProperty() { + void test_categoryProperty() { CodeElement codeElement = new CodeElement("com.structurizr.component.SomeComponent"); assertNull(codeElement.getCategory()); @@ -58,7 +57,7 @@ public void test_categoryProperty() { } @Test - public void test_visibilityProperty() { + void test_visibilityProperty() { CodeElement codeElement = new CodeElement("com.structurizr.component.SomeComponent"); assertNull(codeElement.getVisibility()); @@ -67,70 +66,76 @@ public void test_visibilityProperty() { } @Test - public void test_setUrl() { + void test_setUrl() { CodeElement codeElement = new CodeElement("com.structurizr.component.SomeComponent"); codeElement.setUrl("https://structurizr.com"); assertEquals("https://structurizr.com", codeElement.getUrl()); } - @Test(expected = IllegalArgumentException.class) - public void test_setUrl_ThrowsAnIllegalArgumentException_WhenAnInvalidUrlIsSpecified() { - CodeElement codeElement = new CodeElement("com.structurizr.component.SomeComponent"); - codeElement.setUrl("htt://blah"); + @Test + void test_setUrl_ThrowsAnIllegalArgumentException_WhenAnInvalidUrlIsSpecified() { + assertThrows(IllegalArgumentException.class, () -> { + CodeElement codeElement = new CodeElement("com.structurizr.component.SomeComponent"); + codeElement.setUrl("htt://blah"); + }); } @Test - public void test_setUrl_DoesNothing_WhenANullUrlIsSpecified() { + void test_setUrl_DoesNothing_WhenANullUrlIsSpecified() { CodeElement codeElement = new CodeElement("com.structurizr.component.SomeComponent"); codeElement.setUrl(null); assertNull(codeElement.getUrl()); } @Test - public void test_setUrl_DoesNothing_WhenAnEmptyUrlIsSpecified() { + void test_setUrl_DoesNothing_WhenAnEmptyUrlIsSpecified() { CodeElement codeElement = new CodeElement("com.structurizr.component.SomeComponent"); codeElement.setUrl(" "); assertNull(codeElement.getUrl()); } - @Test(expected = IllegalArgumentException.class) - public void test_construction_ThrowsAnIllegalArgumentException_WhenANullFullyQualifiedNameIsSpecified() { - new CodeElement(null); + @Test + void test_construction_ThrowsAnIllegalArgumentException_WhenANullFullyQualifiedNameIsSpecified() { + assertThrows(IllegalArgumentException.class, () -> { + new CodeElement(null); + }); } - @Test(expected = IllegalArgumentException.class) - public void test_construction_ThrowsAnIllegalArgumentException_WhenAnEmptyFullyQualifiedNameIsSpecified() { - new CodeElement(" "); + @Test + void test_construction_ThrowsAnIllegalArgumentException_WhenAnEmptyFullyQualifiedNameIsSpecified() { + assertThrows(IllegalArgumentException.class, () -> { + new CodeElement(" "); + }); } @Test - public void test_equals_ReturnsFalse_WhenComparedToNull() { + void test_equals_ReturnsFalse_WhenComparedToNull() { CodeElement codeElement = new CodeElement("com.structurizr.component.SomeComponent"); - assertFalse(codeElement.equals(null)); + assertNotEquals(codeElement, null); } @Test - public void test_equals_ReturnsFalse_WhenComparedToDifferentTypeOfObject() { + void test_equals_ReturnsFalse_WhenComparedToDifferentTypeOfObject() { CodeElement codeElement = new CodeElement("com.structurizr.component.SomeComponent"); - assertFalse(codeElement.equals("hello")); + assertNotEquals(codeElement, "hello"); } @Test - public void test_equals_ReturnsFalse_WhenComparedToAnotherCodeElementWithADifferentType() { + void test_equals_ReturnsFalse_WhenComparedToAnotherCodeElementWithADifferentType() { CodeElement codeElement1 = new CodeElement("com.structurizr.component.SomeComponent1"); CodeElement codeElement2 = new CodeElement("com.structurizr.component.SomeComponent2"); - assertFalse(codeElement1.equals(codeElement2)); + assertNotEquals(codeElement1, codeElement2); } @Test - public void test_equals_ReturnsFalse_WhenComparedToAnotherCodeElementWithTheSameType() { + void test_equals_ReturnsFalse_WhenComparedToAnotherCodeElementWithTheSameType() { CodeElement codeElement1 = new CodeElement("com.structurizr.component.SomeComponent1"); CodeElement codeElement2 = new CodeElement("com.structurizr.component.SomeComponent1"); - assertTrue(codeElement1.equals(codeElement2)); + assertEquals(codeElement1, codeElement2); } @Test - public void test_getPackage_ReturnsThePackageName() { + void test_getPackage_ReturnsThePackageName() { CodeElement codeElement = new CodeElement("com.structurizr.component.SomeComponent"); assertEquals("com.structurizr.component", codeElement.getPackage()); } diff --git a/structurizr-core/test/unit/com/structurizr/model/ComponentTests.java b/structurizr-core/test/unit/com/structurizr/model/ComponentTests.java index 5d1bbefc8..0036db291 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ComponentTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ComponentTests.java @@ -1,12 +1,11 @@ package com.structurizr.model; import com.structurizr.AbstractWorkspaceTestBase; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Set; -import static junit.framework.TestCase.assertNull; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class ComponentTests extends AbstractWorkspaceTestBase { @@ -14,38 +13,38 @@ public class ComponentTests extends AbstractWorkspaceTestBase { private Container container = softwareSystem.addContainer("Container", "Description", "Some technology"); @Test - public void test_getName_ReturnsTheGivenName_WhenANameIsGiven() { + void test_getName_ReturnsTheGivenName_WhenANameIsGiven() { Component component = new Component(); component.setName("Some name"); assertEquals("Some name", component.getName()); } @Test - public void test_getCanonicalName() { + void test_getCanonicalName() { Component component = container.addComponent("Component", "Description"); assertEquals("Component://System.Container.Component", component.getCanonicalName()); } @Test - public void test_getCanonicalName_WhenNameContainsSlashAndDotCharacters() { + void test_getCanonicalName_WhenNameContainsSlashAndDotCharacters() { Component component = container.addComponent("Name1/.Name2", "Description"); assertEquals("Component://System.Container.Name1Name2", component.getCanonicalName()); } @Test - public void test_getParent_ReturnsTheParentContainer() { + void test_getParent_ReturnsTheParentContainer() { Component component = container.addComponent("Component", "Description"); assertEquals(container, component.getParent()); } @Test - public void test_getContainer_ReturnsTheParentContainer() { + void test_getContainer_ReturnsTheParentContainer() { Component component = container.addComponent("Name", "Description"); assertEquals(container, component.getContainer()); } @Test - public void test_removeTags_DoesNotRemoveRequiredTags() { + void test_removeTags_DoesNotRemoveRequiredTags() { Component component = new Component(); assertTrue(component.getTags().contains(Tags.ELEMENT)); assertTrue(component.getTags().contains(Tags.COMPONENT)); @@ -58,7 +57,7 @@ public void test_removeTags_DoesNotRemoveRequiredTags() { } @Test - public void test_technologyProperty() { + void test_technologyProperty() { Component component = new Component(); assertNull(component.getTechnology()); @@ -67,7 +66,7 @@ public void test_technologyProperty() { } @Test - public void test_sizeProperty() { + void test_sizeProperty() { Component component = new Component(); assertEquals(0, component.getSize()); @@ -76,7 +75,7 @@ public void test_sizeProperty() { } @Test - public void test_setType_ThrowsAnExceptionWhenPassedNull() { + void test_setType_ThrowsAnExceptionWhenPassedNull() { Component component = new Component(); try { component.setType(null); @@ -87,7 +86,7 @@ public void test_setType_ThrowsAnExceptionWhenPassedNull() { } @Test - public void test_setType_AddsAPrimaryCodeElement_WhenPassedAFullyQualifiedTypeName() { + void test_setType_AddsAPrimaryCodeElement_WhenPassedAFullyQualifiedTypeName() { Component component = new Component(); component.setType("com.structurizr.web.HomePageController"); @@ -100,7 +99,7 @@ public void test_setType_AddsAPrimaryCodeElement_WhenPassedAFullyQualifiedTypeNa } @Test - public void test_setType_OverwritesThePrimaryCodeElement_WhenCalledMoreThanOnce() { + void test_setType_OverwritesThePrimaryCodeElement_WhenCalledMoreThanOnce() { Component component = new Component(); component.setType("com.structurizr.web.HomePageController"); component.setType("com.structurizr.web.SomeOtherController"); @@ -115,7 +114,7 @@ public void test_setType_OverwritesThePrimaryCodeElement_WhenCalledMoreThanOnce( } @Test - public void test_addSupportingType_ThrowsAnExceptionWhenPassedNull() { + void test_addSupportingType_ThrowsAnExceptionWhenPassedNull() { Component component = new Component(); try { component.addSupportingType(null); @@ -126,7 +125,7 @@ public void test_addSupportingType_ThrowsAnExceptionWhenPassedNull() { } @Test - public void test_addSupportingType_AddsASupportingCodeElement_WhenPassedAFullyQualifiedTypeName() { + void test_addSupportingType_AddsASupportingCodeElement_WhenPassedAFullyQualifiedTypeName() { Component component = new Component(); component.addSupportingType("com.structurizr.web.HomePageViewModel"); @@ -139,20 +138,20 @@ public void test_addSupportingType_AddsASupportingCodeElement_WhenPassedAFullyQu } @Test - public void test_getType_ReturnsNull_WhenThereAreNoCodeElements() { + void test_getType_ReturnsNull_WhenThereAreNoCodeElements() { Component component = new Component(); assertNull(component.getType()); } @Test - public void test_getType_ReturnsNull_WhenThereAreNoPrimaryCodeElements() { + void test_getType_ReturnsNull_WhenThereAreNoPrimaryCodeElements() { Component component = new Component(); component.addSupportingType("com.structurizr.SomeType"); assertNull(component.getType()); } @Test - public void test_getType_ReturnsThePrimaryCodeElement_WhenThereIsAPrimaryCodeElement() { + void test_getType_ReturnsThePrimaryCodeElement_WhenThereIsAPrimaryCodeElement() { Component component = new Component(); component.setType("com.structurizr.SomeType"); CodeElement codeElement = component.getType(); diff --git a/structurizr-core/test/unit/com/structurizr/model/ContainerInstanceTests.java b/structurizr-core/test/unit/com/structurizr/model/ContainerInstanceTests.java index 96b306f9a..12e62a53c 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ContainerInstanceTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ContainerInstanceTests.java @@ -1,9 +1,9 @@ package com.structurizr.model; import com.structurizr.AbstractWorkspaceTestBase; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class ContainerInstanceTests extends AbstractWorkspaceTestBase { @@ -12,7 +12,7 @@ public class ContainerInstanceTests extends AbstractWorkspaceTestBase { private DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node", "Description", "Technology"); @Test - public void test_construction() { + void test_construction() { ContainerInstance instance = deploymentNode.add(database); assertSame(database, instance.getContainer()); @@ -21,7 +21,7 @@ public void test_construction() { } @Test - public void test_getContainerId() { + void test_getContainerId() { ContainerInstance instance = deploymentNode.add(database); assertEquals(database.getId(), instance.getContainerId()); @@ -31,7 +31,7 @@ public void test_getContainerId() { } @Test - public void test_getName() { + void test_getName() { ContainerInstance instance = deploymentNode.add(database); assertEquals("Database Schema", instance.getName()); @@ -41,28 +41,28 @@ public void test_getName() { } @Test - public void test_getCanonicalName() { + void test_getCanonicalName() { ContainerInstance instance = deploymentNode.add(database); assertEquals("ContainerInstance://Default/Deployment Node/System.Database Schema[1]", instance.getCanonicalName()); } @Test - public void test_getParent_ReturnsTheParentDeploymentNode() { + void test_getParent_ReturnsTheParentDeploymentNode() { ContainerInstance instance = deploymentNode.add(database); assertEquals(deploymentNode, instance.getParent()); } @Test - public void test_getRequiredTags() { + void test_getRequiredTags() { ContainerInstance instance = deploymentNode.add(database); assertTrue(instance.getDefaultTags().isEmpty()); } @Test - public void test_getTags() { + void test_getTags() { database.addTags("Database"); ContainerInstance instance = deploymentNode.add(database); instance.addTags("Primary Instance"); @@ -71,7 +71,7 @@ public void test_getTags() { } @Test - public void test_removeTags_DoesNotRemoveAnyTags() { + void test_removeTags_DoesNotRemoveAnyTags() { ContainerInstance instance = deploymentNode.add(database); assertTrue(instance.getTags().contains(Tags.CONTAINER_INSTANCE)); @@ -82,7 +82,7 @@ public void test_removeTags_DoesNotRemoveAnyTags() { } @Test - public void test_getDeploymentGroups_WhenNoGroupsHaveBeenSpecified() { + void test_getDeploymentGroups_WhenNoGroupsHaveBeenSpecified() { ContainerInstance instance = deploymentNode.add(database); assertEquals(1, instance.getDeploymentGroups().size()); @@ -90,7 +90,7 @@ public void test_getDeploymentGroups_WhenNoGroupsHaveBeenSpecified() { } @Test - public void test_getDeploymentGroups_WhenOneGroupHasBeenSpecified() { + void test_getDeploymentGroups_WhenOneGroupHasBeenSpecified() { ContainerInstance instance = deploymentNode.add(database, "Group 1"); assertEquals(1, instance.getDeploymentGroups().size()); @@ -98,7 +98,7 @@ public void test_getDeploymentGroups_WhenOneGroupHasBeenSpecified() { } @Test - public void test_getDeploymentGroups_WhenMultipleGroupsAreSpecified() { + void test_getDeploymentGroups_WhenMultipleGroupsAreSpecified() { ContainerInstance instance = deploymentNode.add(database, "Group 1", "Group 2"); assertEquals(2, instance.getDeploymentGroups().size()); @@ -107,7 +107,7 @@ public void test_getDeploymentGroups_WhenMultipleGroupsAreSpecified() { } @Test - public void test_addHealthCheck() { + void test_addHealthCheck() { ContainerInstance instance = deploymentNode.add(database); assertTrue(instance.getHealthChecks().isEmpty()); @@ -120,7 +120,7 @@ public void test_addHealthCheck() { } @Test - public void test_addHealthCheck_ThrowsAnException_WhenTheNameIsNull() { + void test_addHealthCheck_ThrowsAnException_WhenTheNameIsNull() { ContainerInstance instance = deploymentNode.add(database); try { @@ -132,7 +132,7 @@ public void test_addHealthCheck_ThrowsAnException_WhenTheNameIsNull() { } @Test - public void test_addHealthCheck_ThrowsAnException_WhenTheNameIsEmpty() { + void test_addHealthCheck_ThrowsAnException_WhenTheNameIsEmpty() { ContainerInstance instance = deploymentNode.add(database); try { @@ -144,7 +144,7 @@ public void test_addHealthCheck_ThrowsAnException_WhenTheNameIsEmpty() { } @Test - public void test_addHealthCheck_ThrowsAnException_WhenTheUrlIsNull() { + void test_addHealthCheck_ThrowsAnException_WhenTheUrlIsNull() { ContainerInstance instance = deploymentNode.add(database); try { @@ -156,7 +156,7 @@ public void test_addHealthCheck_ThrowsAnException_WhenTheUrlIsNull() { } @Test - public void test_addHealthCheck_ThrowsAnException_WhenTheUrlIsEmpty() { + void test_addHealthCheck_ThrowsAnException_WhenTheUrlIsEmpty() { ContainerInstance instance = deploymentNode.add(database); try { @@ -168,7 +168,7 @@ public void test_addHealthCheck_ThrowsAnException_WhenTheUrlIsEmpty() { } @Test - public void test_addHealthCheck_ThrowsAnException_WhenTheUrlIsInvalid() { + void test_addHealthCheck_ThrowsAnException_WhenTheUrlIsInvalid() { ContainerInstance instance = deploymentNode.add(database); try { @@ -180,7 +180,7 @@ public void test_addHealthCheck_ThrowsAnException_WhenTheUrlIsInvalid() { } @Test - public void test_addHealthCheck_ThrowsAnException_WhenTheIntervalIsLessThanZero() { + void test_addHealthCheck_ThrowsAnException_WhenTheIntervalIsLessThanZero() { ContainerInstance instance = deploymentNode.add(database); try { @@ -192,7 +192,7 @@ public void test_addHealthCheck_ThrowsAnException_WhenTheIntervalIsLessThanZero( } @Test - public void test_addHealthCheck_ThrowsAnException_WhenTheTimeoutIsLessThanZero() { + void test_addHealthCheck_ThrowsAnException_WhenTheTimeoutIsLessThanZero() { ContainerInstance instance = deploymentNode.add(database); try { diff --git a/structurizr-core/test/unit/com/structurizr/model/ContainerTests.java b/structurizr-core/test/unit/com/structurizr/model/ContainerTests.java index 07813325b..80bd6af86 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ContainerTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ContainerTests.java @@ -1,9 +1,9 @@ package com.structurizr.model; import com.structurizr.AbstractWorkspaceTestBase; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class ContainerTests extends AbstractWorkspaceTestBase { @@ -11,7 +11,7 @@ public class ContainerTests extends AbstractWorkspaceTestBase { private Container container = softwareSystem.addContainer("Container", "Description", "Some technology"); @Test - public void test_technologyProperty() { + void test_technologyProperty() { assertEquals("Some technology", container.getTechnology()); container.setTechnology("Some other technology"); @@ -19,29 +19,29 @@ public void test_technologyProperty() { } @Test - public void test_getCanonicalName() { + void test_getCanonicalName() { assertEquals("Container://System.Container", container.getCanonicalName()); } @Test - public void test_getCanonicalName_WhenNameContainsSlashAndDotCharacters() { + void test_getCanonicalName_WhenNameContainsSlashAndDotCharacters() { container = softwareSystem.addContainer("Name1/.Name2", "Description", "Some technology"); assertEquals("Container://System.Name1Name2", container.getCanonicalName()); } @Test - public void test_getParent_ReturnsTheParentSoftwareSystem() { + void test_getParent_ReturnsTheParentSoftwareSystem() { assertEquals(softwareSystem, container.getParent()); } @Test - public void test_getSoftwareSystem_ReturnsTheParentSoftwareSystem() { + void test_getSoftwareSystem_ReturnsTheParentSoftwareSystem() { assertEquals(softwareSystem, container.getSoftwareSystem()); } @Test - public void test_removeTags_DoesNotRemoveRequiredTags() { + void test_removeTags_DoesNotRemoveRequiredTags() { assertTrue(container.getTags().contains(Tags.ELEMENT)); assertTrue(container.getTags().contains(Tags.CONTAINER)); @@ -52,18 +52,22 @@ public void test_removeTags_DoesNotRemoveRequiredTags() { assertTrue(container.getTags().contains(Tags.CONTAINER)); } - @Test(expected = IllegalArgumentException.class) - public void test_addComponent_ThrowsAnException_WhenANullNameIsSpecified() { - container.addComponent(null, ""); + @Test + void test_addComponent_ThrowsAnException_WhenANullNameIsSpecified() { + assertThrows(IllegalArgumentException.class, () -> { + container.addComponent(null, ""); + }); } - @Test(expected = IllegalArgumentException.class) - public void test_addComponent_ThrowsAnException_WhenAnEmptyNameIsSpecified() { - container.addComponent(" ", ""); + @Test + void test_addComponent_ThrowsAnException_WhenAnEmptyNameIsSpecified() { + assertThrows(IllegalArgumentException.class, () -> { + container.addComponent(" ", ""); + }); } @Test - public void test_addComponent_ThrowsAnException_WhenAComponentWithTheSameNameAlreadyExists() { + void test_addComponent_ThrowsAnException_WhenAComponentWithTheSameNameAlreadyExists() { container.addComponent("Component 1", ""); try { container.addComponent("Component 1", ""); @@ -74,7 +78,7 @@ public void test_addComponent_ThrowsAnException_WhenAComponentWithTheSameNameAlr } @Test - public void test_addComponent_AddsAComponentWithTheSpecifiedNameAndDescription() { + void test_addComponent_AddsAComponentWithTheSpecifiedNameAndDescription() { Component component = container.addComponent("Name", "Description"); assertTrue(container.getComponents().contains(component)); assertEquals("Name", component.getName()); @@ -86,7 +90,7 @@ public void test_addComponent_AddsAComponentWithTheSpecifiedNameAndDescription() } @Test - public void test_addComponent_AddsAComponentWithTheSpecifiedNameAndDescriptionAndTechnology() { + void test_addComponent_AddsAComponentWithTheSpecifiedNameAndDescriptionAndTechnology() { Component component = container.addComponent("Name", "Description", "Technology"); assertTrue(container.getComponents().contains(component)); assertEquals("Name", component.getName()); @@ -98,7 +102,7 @@ public void test_addComponent_AddsAComponentWithTheSpecifiedNameAndDescriptionAn } @Test - public void test_addComponent_AddsAComponentWithTheSpecifiedNameAndDescriptionAndTechnologyAndStringType() { + void test_addComponent_AddsAComponentWithTheSpecifiedNameAndDescriptionAndTechnologyAndStringType() { Component component = container.addComponent("Name", "SomeType", "Description", "Technology"); assertTrue(container.getComponents().contains(component)); assertEquals("Name", component.getName()); @@ -110,7 +114,7 @@ public void test_addComponent_AddsAComponentWithTheSpecifiedNameAndDescriptionAn } @Test - public void test_addComponent_AddsAComponentWithTheSpecifiedNameAndDescriptionAndTechnologyAndClassType() { + void test_addComponent_AddsAComponentWithTheSpecifiedNameAndDescriptionAndTechnologyAndClassType() { Component component = container.addComponent("Name", this.getClass(), "Description", "Technology"); assertTrue(container.getComponents().contains(component)); assertEquals("Name", component.getName()); @@ -122,7 +126,7 @@ public void test_addComponent_AddsAComponentWithTheSpecifiedNameAndDescriptionAn } @Test - public void test_getComponentWithName_ThrowsAnException_WhenANullNameIsSpecified() { + void test_getComponentWithName_ThrowsAnException_WhenANullNameIsSpecified() { try { container.getComponentWithName(null); fail(); @@ -132,7 +136,7 @@ public void test_getComponentWithName_ThrowsAnException_WhenANullNameIsSpecified } @Test - public void test_getComponentWithName_ThrowsAnException_WhenAnEmptyNameIsSpecified() { + void test_getComponentWithName_ThrowsAnException_WhenAnEmptyNameIsSpecified() { try { container.getComponentWithName(" "); fail(); @@ -142,7 +146,7 @@ public void test_getComponentWithName_ThrowsAnException_WhenAnEmptyNameIsSpecifi } @Test - public void test_getComponentOfType_ThrowsAnException_WhenANullTypeIsSpecified() { + void test_getComponentOfType_ThrowsAnException_WhenANullTypeIsSpecified() { try { container.getComponentOfType(null); fail(); @@ -152,7 +156,7 @@ public void test_getComponentOfType_ThrowsAnException_WhenANullTypeIsSpecified() } @Test - public void test_getComponentOfType_ThrowsAnException_WhenAnEmptyTypeIsSpecified() { + void test_getComponentOfType_ThrowsAnException_WhenAnEmptyTypeIsSpecified() { try { container.getComponentOfType(" "); fail(); @@ -162,12 +166,12 @@ public void test_getComponentOfType_ThrowsAnException_WhenAnEmptyTypeIsSpecified } @Test - public void test_getComponentOfType_ReturnsNull_WhenNoComponentWithTheSpecifiedTypeExists() { + void test_getComponentOfType_ReturnsNull_WhenNoComponentWithTheSpecifiedTypeExists() { assertNull(container.getComponentOfType("SomeType")); } @Test - public void test_getComponentOfType_ReturnsAComponent_WhenAComponentWithTheSpecifiedTypeExists() { + void test_getComponentOfType_ReturnsAComponent_WhenAComponentWithTheSpecifiedTypeExists() { container.addComponent("Name", "SomeType", "Description", "Technology"); Component component = container.getComponentOfType("SomeType"); diff --git a/structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategyTests.java b/structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategyTests.java index d61147232..d02cd43b4 100644 --- a/structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategyTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategyTests.java @@ -1,16 +1,16 @@ package com.structurizr.model; import com.structurizr.AbstractWorkspaceTestBase; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Set; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategyTests extends AbstractWorkspaceTestBase { @Test - public void test_impliedRelationshipsAreCreated() { + void test_impliedRelationshipsAreCreated() { SoftwareSystem a = model.addSoftwareSystem("A", ""); Container aa = a.addContainer("AA", "", ""); Component aaa = aa.addComponent("AAA", "", ""); @@ -21,7 +21,7 @@ public void test_impliedRelationshipsAreCreated() { model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); - Relationship explicitRelationship = aaa.uses(bbb, "Uses 1", "Technology", InteractionStyle.Asynchronous, new String[] { "Tag 1", "Tag 2" }); + Relationship explicitRelationship = aaa.uses(bbb, "Uses 1", "Technology", InteractionStyle.Asynchronous, new String[]{"Tag 1", "Tag 2"}); assertEquals(9, model.getRelationships().size()); assertTrue(aaa.hasEfferentRelationshipWith(bbb, "Uses 1")); @@ -54,7 +54,7 @@ public void test_impliedRelationshipsAreCreated() { } @Test - public void test_impliedRelationshipsAreCreated_UnlessAnyRelationshipExists() { + void test_impliedRelationshipsAreCreated_UnlessAnyRelationshipExists() { SoftwareSystem a = model.addSoftwareSystem("A", ""); Container aa = a.addContainer("AA", "", ""); Component aaa = aa.addComponent("AAA", "", ""); diff --git a/structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategyTests.java b/structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategyTests.java index 0fa35ee89..38ebb887b 100644 --- a/structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategyTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategyTests.java @@ -1,16 +1,16 @@ package com.structurizr.model; import com.structurizr.AbstractWorkspaceTestBase; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Set; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategyTests extends AbstractWorkspaceTestBase { @Test - public void test_impliedRelationships_WhenNoSummaryRelationshipsExist() { + void test_impliedRelationships_WhenNoSummaryRelationshipsExist() { SoftwareSystem a = model.addSoftwareSystem("A", ""); Container aa = a.addContainer("AA", "", ""); Component aaa = aa.addComponent("AAA", "", ""); @@ -21,7 +21,7 @@ public void test_impliedRelationships_WhenNoSummaryRelationshipsExist() { model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategy()); - Relationship explicitRelationship = aaa.uses(bbb, "Uses 1", "Technology", InteractionStyle.Asynchronous, new String[] { "Tag 1", "Tag 2" }); + Relationship explicitRelationship = aaa.uses(bbb, "Uses 1", "Technology", InteractionStyle.Asynchronous, new String[]{"Tag 1", "Tag 2"}); assertEquals(9, model.getRelationships().size()); assertTrue(aaa.hasEfferentRelationshipWith(bbb, "Uses 1")); diff --git a/structurizr-core/test/unit/com/structurizr/model/CustomElementTests.java b/structurizr-core/test/unit/com/structurizr/model/CustomElementTests.java index 6637c088c..81fdbaf82 100644 --- a/structurizr-core/test/unit/com/structurizr/model/CustomElementTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/CustomElementTests.java @@ -1,14 +1,14 @@ package com.structurizr.model; import com.structurizr.AbstractWorkspaceTestBase; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class CustomElementTests extends AbstractWorkspaceTestBase { @Test - public void test_basicProperties() { + void test_basicProperties() { CustomElement element = model.addCustomElement("Name", "Type", "Description"); assertEquals("Name", element.getName()); assertEquals("Type", element.getMetadata()); @@ -16,26 +16,26 @@ public void test_basicProperties() { } @Test - public void test_getCanonicalName() { + void test_getCanonicalName() { CustomElement element = model.addCustomElement("Name", "Type", "Description"); assertEquals("Custom://Name", element.getCanonicalName()); } @Test - public void test_getCanonicalName_WhenNameContainsSlashAndDotCharacters() { + void test_getCanonicalName_WhenNameContainsSlashAndDotCharacters() { CustomElement element = model.addCustomElement("Name", "Type", "Description"); element.setName("Name1/.Name2"); assertEquals("Custom://Name1Name2", element.getCanonicalName()); } @Test - public void test_getParent_ReturnsNull() { + void test_getParent_ReturnsNull() { CustomElement element = model.addCustomElement("Name", "Type", "Description"); assertNull(element.getParent()); } @Test - public void test_removeTags_DoesNotRemoveRequiredTags() { + void test_removeTags_DoesNotRemoveRequiredTags() { CustomElement element = model.addCustomElement("Name", "Type", "Description"); assertTrue(element.getTags().contains(Tags.ELEMENT)); @@ -45,7 +45,7 @@ public void test_removeTags_DoesNotRemoveRequiredTags() { } @Test - public void test_uses_AddsARelationshipWhenTheDescriptionIsSpecified() { + void test_uses_AddsARelationshipWhenTheDescriptionIsSpecified() { CustomElement element1 = model.addCustomElement("Box 1"); CustomElement element2 = model.addCustomElement("Box 2"); @@ -61,7 +61,7 @@ public void test_uses_AddsARelationshipWhenTheDescriptionIsSpecified() { } @Test - public void test_uses_AddsARelationshipWhenTheDescriptionAndTechnologyAreSpecified() { + void test_uses_AddsARelationshipWhenTheDescriptionAndTechnologyAreSpecified() { CustomElement element1 = model.addCustomElement("Box 1"); CustomElement element2 = model.addCustomElement("Box 2"); @@ -73,11 +73,11 @@ public void test_uses_AddsARelationshipWhenTheDescriptionAndTechnologyAreSpecifi assertSame(element2, relationship.getDestination()); assertEquals("Uses", relationship.getDescription()); assertEquals("Technology", relationship.getTechnology()); - assertEquals(null, relationship.getInteractionStyle()); + assertNull(relationship.getInteractionStyle()); } @Test - public void test_uses_AddsARelationshipWhenTheDescriptionAndTechnologyAndInteractionStyleAreSpecified() { + void test_uses_AddsARelationshipWhenTheDescriptionAndTechnologyAndInteractionStyleAreSpecified() { CustomElement element1 = model.addCustomElement("Box 1"); CustomElement element2 = model.addCustomElement("Box 2"); @@ -93,11 +93,11 @@ public void test_uses_AddsARelationshipWhenTheDescriptionAndTechnologyAndInterac } @Test - public void test_uses_AddsARelationshipWhenTheDescriptionAndTechnologyAndInteractionStyleAndTagsAreSpecified() { + void test_uses_AddsARelationshipWhenTheDescriptionAndTechnologyAndInteractionStyleAndTagsAreSpecified() { CustomElement element1 = model.addCustomElement("Box 1"); CustomElement element2 = model.addCustomElement("Box 2"); - element1.uses(element2, "Uses", "Technology", InteractionStyle.Asynchronous, new String[] { "Tag 1", "Tag 2" }); + element1.uses(element2, "Uses", "Technology", InteractionStyle.Asynchronous, new String[]{"Tag 1", "Tag 2"}); assertEquals(1, element1.getRelationships().size()); Relationship relationship = element1.getRelationships().iterator().next(); diff --git a/structurizr-core/test/unit/com/structurizr/model/DefaultImpliedRelationshipsStrategyTests.java b/structurizr-core/test/unit/com/structurizr/model/DefaultImpliedRelationshipsStrategyTests.java index 26ca1ff02..afd611ab5 100644 --- a/structurizr-core/test/unit/com/structurizr/model/DefaultImpliedRelationshipsStrategyTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/DefaultImpliedRelationshipsStrategyTests.java @@ -1,15 +1,15 @@ package com.structurizr.model; import com.structurizr.AbstractWorkspaceTestBase; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class DefaultImpliedRelationshipsStrategyTests extends AbstractWorkspaceTestBase { @Test - public void test_createImpliedRelationships_DoesNothing() { + void test_createImpliedRelationships_DoesNothing() { SoftwareSystem a = model.addSoftwareSystem("A", ""); Container aa = a.addContainer("AA", "", ""); Component aaa = aa.addComponent("AAA", "", ""); diff --git a/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java b/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java index 694741de9..86ae6a609 100644 --- a/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java @@ -2,21 +2,21 @@ import com.structurizr.AbstractWorkspaceTestBase; import com.structurizr.util.MapUtils; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class DeploymentNodeTests extends AbstractWorkspaceTestBase { @Test - public void test_getCanonicalName_WhenTheDeploymentNodeHasNoParent() { + void test_getCanonicalName_WhenTheDeploymentNodeHasNoParent() { DeploymentNode deploymentNode = model.addDeploymentNode("Ubuntu Server", "", ""); assertEquals("DeploymentNode://Default/Ubuntu Server", deploymentNode.getCanonicalName()); } @Test - public void test_getCanonicalName_WhenTheDeploymentNodeHasAParent() { + void test_getCanonicalName_WhenTheDeploymentNodeHasAParent() { DeploymentNode l1 = model.addDeploymentNode("Level 1", "", ""); DeploymentNode l2 = l1.addDeploymentNode("Level 2", "", ""); DeploymentNode l3 = l2.addDeploymentNode("Level 3", "", ""); @@ -27,7 +27,7 @@ public void test_getCanonicalName_WhenTheDeploymentNodeHasAParent() { } @Test - public void test_getParent_ReturnsTheParentDeploymentNode() { + void test_getParent_ReturnsTheParentDeploymentNode() { DeploymentNode parent = model.addDeploymentNode("Parent", "", ""); assertNull(parent.getParent()); @@ -37,7 +37,7 @@ public void test_getParent_ReturnsTheParentDeploymentNode() { } @Test - public void test_getRequiredTags() { + void test_getRequiredTags() { DeploymentNode deploymentNode = new DeploymentNode(); assertEquals(2, deploymentNode.getDefaultTags().size()); assertTrue(deploymentNode.getDefaultTags().contains(Tags.ELEMENT)); @@ -45,14 +45,14 @@ public void test_getRequiredTags() { } @Test - public void test_getTags() { + void test_getTags() { DeploymentNode deploymentNode = new DeploymentNode(); deploymentNode.addTags("Tag 1", "Tag 2"); assertEquals("Element,Deployment Node,Tag 1,Tag 2", deploymentNode.getTags()); } @Test - public void test_add_ThrowsAnException_WhenASoftwareSystemIsNotSpecified() { + void test_add_ThrowsAnException_WhenASoftwareSystemIsNotSpecified() { try { DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node", "Description", "Technology"); deploymentNode.add((SoftwareSystem) null); @@ -63,10 +63,10 @@ public void test_add_ThrowsAnException_WhenASoftwareSystemIsNotSpecified() { } @Test - public void test_add_ThrowsAnException_WhenAContainerIsNotSpecified() { + void test_add_ThrowsAnException_WhenAContainerIsNotSpecified() { try { DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node", "Description", "Technology"); - deploymentNode.add((Container)null); + deploymentNode.add((Container) null); fail(); } catch (IllegalArgumentException iae) { assertEquals("A container must be specified.", iae.getMessage()); @@ -74,7 +74,7 @@ public void test_add_ThrowsAnException_WhenAContainerIsNotSpecified() { } @Test - public void test_add_AddsAContainerInstance_WhenAContainerIsSpecified() { + void test_add_AddsAContainerInstance_WhenAContainerIsSpecified() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); Container container = softwareSystem.addContainer("Container", "", ""); DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node", "", ""); @@ -87,7 +87,7 @@ public void test_add_AddsAContainerInstance_WhenAContainerIsSpecified() { } @Test - public void test_addDeploymentNode_ThrowsAnException_WhenANameIsNotSpecified() { + void test_addDeploymentNode_ThrowsAnException_WhenANameIsNotSpecified() { try { DeploymentNode parent = model.addDeploymentNode("Parent", "", ""); parent.addDeploymentNode(null, "", ""); @@ -98,7 +98,7 @@ public void test_addDeploymentNode_ThrowsAnException_WhenANameIsNotSpecified() { } @Test - public void test_addDeploymentNode_AddsAChildDeploymentNode_WhenANameIsSpecified() { + void test_addDeploymentNode_AddsAChildDeploymentNode_WhenANameIsSpecified() { DeploymentNode parent = model.addDeploymentNode("Parent", "", ""); DeploymentNode child = parent.addDeploymentNode("Child 1", "Description", "Technology"); @@ -134,10 +134,10 @@ public void test_addDeploymentNode_AddsAChildDeploymentNode_WhenANameIsSpecified } @Test - public void test_uses_ThrowsAnException_WhenANullDestinationIsSpecified() { + void test_uses_ThrowsAnException_WhenANullDestinationIsSpecified() { try { DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node", "", ""); - deploymentNode.uses((DeploymentNode)null, "", ""); + deploymentNode.uses((DeploymentNode) null, "", ""); fail(); } catch (IllegalArgumentException iae) { assertEquals("The destination must be specified.", iae.getMessage()); @@ -145,7 +145,7 @@ public void test_uses_ThrowsAnException_WhenANullDestinationIsSpecified() { } @Test - public void test_uses_AddsARelationship() { + void test_uses_AddsARelationship() { DeploymentNode primaryNode = model.addDeploymentNode("MySQL - Primary", "", ""); DeploymentNode secondaryNode = model.addDeploymentNode("MySQL - Secondary", "", ""); Relationship relationship = primaryNode.uses(secondaryNode, "Replicates data to", "Some technology"); @@ -158,7 +158,7 @@ public void test_uses_AddsARelationship() { } @Test - public void test_getDeploymentNodeWithName_ThrowsAnException_WhenANameIsNotSpecified() { + void test_getDeploymentNodeWithName_ThrowsAnException_WhenANameIsNotSpecified() { try { DeploymentNode deploymentNode = new DeploymentNode(); deploymentNode.getDeploymentNodeWithName(null); @@ -169,33 +169,33 @@ public void test_getDeploymentNodeWithName_ThrowsAnException_WhenANameIsNotSpeci } @Test - public void test_getDeploymentNodeWithName_ReturnsNull_WhenThereIsNoDeploymentNodeWithTheSpecifiedName() { + void test_getDeploymentNodeWithName_ReturnsNull_WhenThereIsNoDeploymentNodeWithTheSpecifiedName() { DeploymentNode deploymentNode = new DeploymentNode(); assertNull(deploymentNode.getDeploymentNodeWithName("foo")); } @Test - public void test_getDeploymentNodeWithName_ReturnsTheNamedDeploymentNode_WhenThereIsADeploymentNodeWithTheSpecifiedName() { + void test_getDeploymentNodeWithName_ReturnsTheNamedDeploymentNode_WhenThereIsADeploymentNodeWithTheSpecifiedName() { DeploymentNode parent = model.addDeploymentNode("parent", "", ""); DeploymentNode child = parent.addDeploymentNode("child", "", ""); assertSame(child, parent.getDeploymentNodeWithName("child")); } @Test - public void test_getInfrastructureNodeWithName_ReturnsNull_WhenThereIsNoInfrastructureNodeWithTheSpecifiedName() { + void test_getInfrastructureNodeWithName_ReturnsNull_WhenThereIsNoInfrastructureNodeWithTheSpecifiedName() { DeploymentNode deploymentNode = new DeploymentNode(); assertNull(deploymentNode.getInfrastructureNodeWithName("foo")); } @Test - public void test_getInfrastructureNodeWithName_ReturnsTheNamedDeploymentNode_WhenThereIsAInfrastructureNodeWithTheSpecifiedName() { + void test_getInfrastructureNodeWithName_ReturnsTheNamedDeploymentNode_WhenThereIsAInfrastructureNodeWithTheSpecifiedName() { DeploymentNode parent = model.addDeploymentNode("parent", "", ""); InfrastructureNode child = parent.addInfrastructureNode("child", "", ""); assertSame(child, parent.getInfrastructureNodeWithName("child")); } @Test - public void test_setInstances() { + void test_setInstances() { DeploymentNode deploymentNode = new DeploymentNode(); deploymentNode.setInstances(8); @@ -203,7 +203,7 @@ public void test_setInstances() { } @Test - public void test_setInstances_ThrowsAnException_WhenZeroIsSpecified() { + void test_setInstances_ThrowsAnException_WhenZeroIsSpecified() { try { DeploymentNode deploymentNode = new DeploymentNode(); deploymentNode.setInstances(0); @@ -214,7 +214,7 @@ public void test_setInstances_ThrowsAnException_WhenZeroIsSpecified() { } @Test - public void test_setInstances_ThrowsAnException_WhenANegativeNumberIsSpecified() { + void test_setInstances_ThrowsAnException_WhenANegativeNumberIsSpecified() { try { DeploymentNode deploymentNode = new DeploymentNode(); deploymentNode.setInstances(-1); diff --git a/structurizr-core/test/unit/com/structurizr/model/ElementTests.java b/structurizr-core/test/unit/com/structurizr/model/ElementTests.java index cf2b8b212..6dfbe398b 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ElementTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ElementTests.java @@ -2,24 +2,24 @@ import com.structurizr.AbstractWorkspaceTestBase; import com.structurizr.Workspace; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class ElementTests extends AbstractWorkspaceTestBase { - @Test - public void test_construction() { - Element element = model.addSoftwareSystem("Name", "Description"); - assertEquals("Name", element.getName()); - assertEquals("Description", element.getDescription()); - } + @Test + void test_construction() { + Element element = model.addSoftwareSystem("Name", "Description"); + assertEquals("Name", element.getName()); + assertEquals("Description", element.getDescription()); + } @Test - public void test_setName_ThrowsAnException_WhenANullValueIsSpecified() { + void test_setName_ThrowsAnException_WhenANullValueIsSpecified() { Element element = model.addSoftwareSystem("Name", "Description"); try { element.setName(null); @@ -30,7 +30,7 @@ public void test_setName_ThrowsAnException_WhenANullValueIsSpecified() { } @Test - public void test_setName_ThrowsAnException_WhenAnEmptyValueIsSpecified() { + void test_setName_ThrowsAnException_WhenAnEmptyValueIsSpecified() { Element element = model.addSoftwareSystem("Name", "Description"); try { element.setName(" "); @@ -41,26 +41,26 @@ public void test_setName_ThrowsAnException_WhenAnEmptyValueIsSpecified() { } @Test - public void test_hasEfferentRelationshipWith_ReturnsFalse_WhenANullElementIsSpecified() { + void test_hasEfferentRelationshipWith_ReturnsFalse_WhenANullElementIsSpecified() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("System 1", ""); assertFalse(softwareSystem1.hasEfferentRelationshipWith(null)); } @Test - public void test_hasEfferentRelationshipWith_ReturnsFalse_WhenTheSameElementIsSpecifiedAndNoCyclicRelationshipExists() { + void test_hasEfferentRelationshipWith_ReturnsFalse_WhenTheSameElementIsSpecifiedAndNoCyclicRelationshipExists() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("System 1", ""); assertFalse(softwareSystem1.hasEfferentRelationshipWith(softwareSystem1)); } @Test - public void test_hasEfferentRelationshipWith_ReturnsTrue_WhenTheSameElementIsSpecifiedAndACyclicRelationshipExists() { + void test_hasEfferentRelationshipWith_ReturnsTrue_WhenTheSameElementIsSpecifiedAndACyclicRelationshipExists() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("System 1", ""); softwareSystem1.uses(softwareSystem1, "uses"); assertTrue(softwareSystem1.hasEfferentRelationshipWith(softwareSystem1)); } @Test - public void test_hasEfferentRelationshipWith_ReturnsTrue_WhenThereIsARelationship() { + void test_hasEfferentRelationshipWith_ReturnsTrue_WhenThereIsARelationship() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("System 1", ""); SoftwareSystem softwareSystem2 = model.addSoftwareSystem("System 2", ""); softwareSystem1.uses(softwareSystem2, "uses"); @@ -68,13 +68,13 @@ public void test_hasEfferentRelationshipWith_ReturnsTrue_WhenThereIsARelationshi } @Test - public void test_hasEfferentRelationshipWithElementAndDescription_ReturnsFalse_WhenANullElementIsSpecified() { + void test_hasEfferentRelationshipWithElementAndDescription_ReturnsFalse_WhenANullElementIsSpecified() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("System 1", ""); assertFalse(softwareSystem1.hasEfferentRelationshipWith(null, null)); } @Test - public void test_hasEfferentRelationshipWithElementAndDescription_ReturnsFalse_WhenThereIsNotAMatchingRelationship() { + void test_hasEfferentRelationshipWithElementAndDescription_ReturnsFalse_WhenThereIsNotAMatchingRelationship() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("System 1", ""); SoftwareSystem softwareSystem2 = model.addSoftwareSystem("System 2", ""); softwareSystem1.uses(softwareSystem2, "Uses"); @@ -83,7 +83,7 @@ public void test_hasEfferentRelationshipWithElementAndDescription_ReturnsFalse_W } @Test - public void test_hasEfferentRelationshipWithElementAndDescription_ReturnsTrue_WhenThereIsAMatchingRelationship() { + void test_hasEfferentRelationshipWithElementAndDescription_ReturnsTrue_WhenThereIsAMatchingRelationship() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("System 1", ""); SoftwareSystem softwareSystem2 = model.addSoftwareSystem("System 2", ""); softwareSystem1.uses(softwareSystem2, "Uses"); @@ -92,19 +92,19 @@ public void test_hasEfferentRelationshipWithElementAndDescription_ReturnsTrue_Wh } @Test - public void test_getEfferentRelationshipWith_ReturnsNull_WhenANullElementIsSpecified() { + void test_getEfferentRelationshipWith_ReturnsNull_WhenANullElementIsSpecified() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("System 1", ""); assertNull(softwareSystem1.getEfferentRelationshipWith(null)); } @Test - public void test_getEfferentRelationshipWith_ReturnsNull_WhenTheSameElementIsSpecifiedAndNoCyclicRelationshipExists() { + void test_getEfferentRelationshipWith_ReturnsNull_WhenTheSameElementIsSpecifiedAndNoCyclicRelationshipExists() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("System 1", ""); assertNull(softwareSystem1.getEfferentRelationshipWith(softwareSystem1)); } @Test - public void test_getEfferentRelationshipWith_ReturnsCyclicRelationship_WhenTheSameElementIsSpecifiedAndACyclicRelationshipExists() { + void test_getEfferentRelationshipWith_ReturnsCyclicRelationship_WhenTheSameElementIsSpecifiedAndACyclicRelationshipExists() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("System 1", ""); softwareSystem1.uses(softwareSystem1, "uses"); @@ -115,7 +115,7 @@ public void test_getEfferentRelationshipWith_ReturnsCyclicRelationship_WhenTheSa } @Test - public void test_getEfferentRelationshipWith_ReturnsTheRelationship_WhenThereIsARelationship() { + void test_getEfferentRelationshipWith_ReturnsTheRelationship_WhenThereIsARelationship() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("System 1", ""); SoftwareSystem softwareSystem2 = model.addSoftwareSystem("System 2", ""); softwareSystem1.uses(softwareSystem2, "uses"); @@ -127,7 +127,7 @@ public void test_getEfferentRelationshipWith_ReturnsTheRelationship_WhenThereIsA } @Test - public void test_hasAfferentRelationships_ReturnsFalse_WhenThereAreNoIncomingRelationships() { + void test_hasAfferentRelationships_ReturnsFalse_WhenThereAreNoIncomingRelationships() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("System 1", ""); SoftwareSystem softwareSystem2 = model.addSoftwareSystem("System 2", ""); softwareSystem1.uses(softwareSystem2, "Uses"); @@ -136,7 +136,7 @@ public void test_hasAfferentRelationships_ReturnsFalse_WhenThereAreNoIncomingRel } @Test - public void test_hasAfferentRelationships_ReturnsTrue_WhenThereAreIncomingRelationships() { + void test_hasAfferentRelationships_ReturnsTrue_WhenThereAreIncomingRelationships() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("System 1", ""); SoftwareSystem softwareSystem2 = model.addSoftwareSystem("System 2", ""); softwareSystem1.uses(softwareSystem2, "Uses"); @@ -145,7 +145,7 @@ public void test_hasAfferentRelationships_ReturnsTrue_WhenThereAreIncomingRelati } @Test - public void test_addRelationship_DoesNothing_WhenTheSameRelationshipWithASoftwareSystemIsAddedMoreThanOnce() { + void test_addRelationship_DoesNothing_WhenTheSameRelationshipWithASoftwareSystemIsAddedMoreThanOnce() { SoftwareSystem a = model.addSoftwareSystem("A", ""); SoftwareSystem b = model.addSoftwareSystem("B", ""); @@ -164,7 +164,7 @@ public void test_addRelationship_DoesNothing_WhenTheSameRelationshipWithASoftwar } @Test - public void test_addRelationship_DoesNothing_WhenTheSameRelationshipWithAContainerIsAddedMoreThanOnce() { + void test_addRelationship_DoesNothing_WhenTheSameRelationshipWithAContainerIsAddedMoreThanOnce() { SoftwareSystem a = model.addSoftwareSystem("A", ""); SoftwareSystem b = model.addSoftwareSystem("B", ""); Container bb = b.addContainer("BB", "", ""); @@ -184,7 +184,7 @@ public void test_addRelationship_DoesNothing_WhenTheSameRelationshipWithAContain } @Test - public void test_addRelationship_DoesNothing_WhenTheSameRelationshipWithAComponentIsAddedMoreThanOnce() { + void test_addRelationship_DoesNothing_WhenTheSameRelationshipWithAComponentIsAddedMoreThanOnce() { SoftwareSystem a = model.addSoftwareSystem("A", ""); SoftwareSystem b = model.addSoftwareSystem("B", ""); Container bb = b.addContainer("BB", "", ""); @@ -205,7 +205,7 @@ public void test_addRelationship_DoesNothing_WhenTheSameRelationshipWithACompone } @Test - public void test_addRelationship_DoesNothing_WhenTheSameRelationshipWithAPersonIsAddedMoreThanOnce() { + void test_addRelationship_DoesNothing_WhenTheSameRelationshipWithAPersonIsAddedMoreThanOnce() { SoftwareSystem a = model.addSoftwareSystem("A", ""); Person b = model.addPerson("B", ""); @@ -224,52 +224,54 @@ public void test_addRelationship_DoesNothing_WhenTheSameRelationshipWithAPersonI } @Test - public void test_equals_ReturnsFalse_WhenTestedAgainstNull() { + void test_equals_ReturnsFalse_WhenTestedAgainstNull() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); - assertFalse(softwareSystem.equals(null)); + assertNotEquals(softwareSystem, null); } @Test - public void test_equals_ReturnsFalse_WhenTheTwoObjectsAreDifferentTypes() { + void test_equals_ReturnsFalse_WhenTheTwoObjectsAreDifferentTypes() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); - assertFalse(softwareSystem.equals("hello world")); + assertNotEquals(softwareSystem, "hello world"); } @Test - public void test_equals_ReturnsTrue_WhenTestedAgainstItself() { + void test_equals_ReturnsTrue_WhenTestedAgainstItself() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); - assertTrue(softwareSystem.equals(softwareSystem)); + assertEquals(softwareSystem, softwareSystem); } @Test - public void test_equals_ReturnsFalse_WhenTheTwoObjectsAreDifferent() { + void test_equals_ReturnsFalse_WhenTheTwoObjectsAreDifferent() { SoftwareSystem softwareSystemA = model.addSoftwareSystem("A", "Description"); SoftwareSystem softwareSystemB = model.addSoftwareSystem("B", "Description"); - assertFalse(softwareSystemA.equals(softwareSystemB)); + assertNotEquals(softwareSystemA, softwareSystemB); } @Test - public void test_equals_ReturnsFalse_WhenTheTwoElementsHaveTheSameCanonicalNameButAreDifferentTypes() { + void test_equals_ReturnsFalse_WhenTheTwoElementsHaveTheSameCanonicalNameButAreDifferentTypes() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); Person person = model.addPerson("Name", "Description"); - assertFalse(softwareSystem.equals(person)); + assertNotEquals(softwareSystem, person); } @Test - public void test_setUrl() { + void test_setUrl() { Element element = model.addSoftwareSystem("Name", "Description"); element.setUrl("https://structurizr.com"); assertEquals("https://structurizr.com", element.getUrl()); } - @Test(expected = IllegalArgumentException.class) - public void test_setUrl_ThrowsAnIllegalArgumentException_WhenAnInvalidUrlIsSpecified() { - Element element = model.addSoftwareSystem("Name", "Description"); - element.setUrl("htt://blah"); + @Test + void test_setUrl_ThrowsAnIllegalArgumentException_WhenAnInvalidUrlIsSpecified() { + assertThrows(IllegalArgumentException.class, () -> { + Element element = model.addSoftwareSystem("Name", "Description"); + element.setUrl("htt://blah"); + }); } @Test - public void test_setUrl_ResetsTheUrl_WhenANullUrlIsSpecified() { + void test_setUrl_ResetsTheUrl_WhenANullUrlIsSpecified() { Element element = model.addSoftwareSystem("Name", "Description"); element.setUrl("https://structurizr.com"); element.setUrl(null); @@ -277,7 +279,7 @@ public void test_setUrl_ResetsTheUrl_WhenANullUrlIsSpecified() { } @Test - public void test_setUrl_ResetsTheUrl_WhenAnEmptyUrlIsSpecified() { + void test_setUrl_ResetsTheUrl_WhenAnEmptyUrlIsSpecified() { Element element = model.addSoftwareSystem("Name", "Description"); element.setUrl("https://structurizr.com"); element.setUrl(" "); diff --git a/structurizr-core/test/unit/com/structurizr/model/GroupableElementTests.java b/structurizr-core/test/unit/com/structurizr/model/GroupableElementTests.java index abd70a3e3..bc91c32b9 100644 --- a/structurizr-core/test/unit/com/structurizr/model/GroupableElementTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/GroupableElementTests.java @@ -1,35 +1,35 @@ package com.structurizr.model; import com.structurizr.AbstractWorkspaceTestBase; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; public class GroupableElementTests extends AbstractWorkspaceTestBase { @Test - public void test_getGroup_ReturnsNullByDefault() { + void test_getGroup_ReturnsNullByDefault() { Person element = model.addPerson("Person"); assertNull(element.getGroup()); } @Test - public void test_setGroup() { + void test_setGroup() { Person element = model.addPerson("Person"); element.setGroup("Group"); assertEquals("Group", element.getGroup()); } @Test - public void test_setGroup_TrimsWhiteSpace() { + void test_setGroup_TrimsWhiteSpace() { Person element = model.addPerson("Person"); element.setGroup(" Group "); assertEquals("Group", element.getGroup()); } @Test - public void test_setGroup_HandlesEmptyAndNullValues() { + void test_setGroup_HandlesEmptyAndNullValues() { Person element = model.addPerson("Person"); element.setGroup("Group"); diff --git a/structurizr-core/test/unit/com/structurizr/model/HttpHealthCheckTests.java b/structurizr-core/test/unit/com/structurizr/model/HttpHealthCheckTests.java index 3610d812a..8b3ec7496 100644 --- a/structurizr-core/test/unit/com/structurizr/model/HttpHealthCheckTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/HttpHealthCheckTests.java @@ -1,22 +1,22 @@ package com.structurizr.model; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; public class HttpHealthCheckTests { private HttpHealthCheck healthCheck; @Test - public void test_defaultConstructorExists() { + void test_defaultConstructorExists() { // the default constructor is used when deserializing from JSON healthCheck = new HttpHealthCheck(); } @Test - public void test_construction() { + void test_construction() { healthCheck = new HttpHealthCheck("Name", "http://localhost", 120, 1000); assertEquals("Name", healthCheck.getName()); assertEquals("http://localhost", healthCheck.getUrl()); @@ -25,14 +25,14 @@ public void test_construction() { } @Test - public void test_addHeader() { + void test_addHeader() { healthCheck = new HttpHealthCheck(); healthCheck.addHeader("Name", "Value"); assertEquals("Value", healthCheck.getHeaders().get("Name")); } @Test - public void test_addHeader_ThrowsAnException_WhenTheHeaderNameIsNull() { + void test_addHeader_ThrowsAnException_WhenTheHeaderNameIsNull() { healthCheck = new HttpHealthCheck(); try { healthCheck.addHeader(null, "value"); @@ -43,7 +43,7 @@ public void test_addHeader_ThrowsAnException_WhenTheHeaderNameIsNull() { } @Test - public void test_addHeader_ThrowsAnException_WhenTheHeaderNameIsEmpty() { + void test_addHeader_ThrowsAnException_WhenTheHeaderNameIsEmpty() { healthCheck = new HttpHealthCheck(); try { healthCheck.addHeader(" ", "value"); @@ -54,7 +54,7 @@ public void test_addHeader_ThrowsAnException_WhenTheHeaderNameIsEmpty() { } @Test - public void test_addHeader_ThrowsAnException_WhenTheHeaderValueIsNull() { + void test_addHeader_ThrowsAnException_WhenTheHeaderValueIsNull() { healthCheck = new HttpHealthCheck(); try { healthCheck.addHeader("Name", null); @@ -65,7 +65,7 @@ public void test_addHeader_ThrowsAnException_WhenTheHeaderValueIsNull() { } @Test - public void test_addHeader_DoesNotThrowAnException_WhenTheHeaderValueIsEmpty() { + void test_addHeader_DoesNotThrowAnException_WhenTheHeaderValueIsEmpty() { healthCheck = new HttpHealthCheck(); healthCheck.addHeader("Name", ""); assertEquals("", healthCheck.getHeaders().get("Name")); diff --git a/structurizr-core/test/unit/com/structurizr/model/InfrastructureNodeTests.java b/structurizr-core/test/unit/com/structurizr/model/InfrastructureNodeTests.java index 11ec4c204..c910b2493 100644 --- a/structurizr-core/test/unit/com/structurizr/model/InfrastructureNodeTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/InfrastructureNodeTests.java @@ -1,14 +1,14 @@ package com.structurizr.model; import com.structurizr.AbstractWorkspaceTestBase; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class InfrastructureNodeTests extends AbstractWorkspaceTestBase { @Test - public void test_getCanonicalName() { + void test_getCanonicalName() { DeploymentNode deploymentNode = model.addDeploymentNode("Amazon Web Services", "", ""); InfrastructureNode infrastructureNode = deploymentNode.addInfrastructureNode("Route 53", "", ""); @@ -16,7 +16,7 @@ public void test_getCanonicalName() { } @Test - public void test_getParent_ReturnsTheParentDeploymentNode() { + void test_getParent_ReturnsTheParentDeploymentNode() { DeploymentNode parent = model.addDeploymentNode("Parent", "", ""); InfrastructureNode child = parent.addInfrastructureNode("Child", "", ""); child.setParent(parent); @@ -24,7 +24,7 @@ public void test_getParent_ReturnsTheParentDeploymentNode() { } @Test - public void test_getRequiredTags() { + void test_getRequiredTags() { InfrastructureNode infrastructureNode = new InfrastructureNode(); assertEquals(2, infrastructureNode.getDefaultTags().size()); assertTrue(infrastructureNode.getDefaultTags().contains(Tags.ELEMENT)); @@ -32,7 +32,7 @@ public void test_getRequiredTags() { } @Test - public void test_getTags() { + void test_getTags() { InfrastructureNode infrastructureNode = new InfrastructureNode(); infrastructureNode.addTags("Tag 1", "Tag 2"); assertEquals("Element,Infrastructure Node,Tag 1,Tag 2", infrastructureNode.getTags()); diff --git a/structurizr-core/test/unit/com/structurizr/model/ModelItemTests.java b/structurizr-core/test/unit/com/structurizr/model/ModelItemTests.java index da1df410c..8bdf44012 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ModelItemTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ModelItemTests.java @@ -1,56 +1,53 @@ package com.structurizr.model; import com.structurizr.AbstractWorkspaceTestBase; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.*; public class ModelItemTests extends AbstractWorkspaceTestBase { - @Test - public void test_construction() { - Element element = model.addSoftwareSystem("Name", "Description"); - assertEquals("Name", element.getName()); - assertEquals("Description", element.getDescription()); - } + @Test + void test_construction() { + Element element = model.addSoftwareSystem("Name", "Description"); + assertEquals("Name", element.getName()); + assertEquals("Description", element.getDescription()); + } @Test - public void test_getTags_WhenThereAreNoTags() { + void test_getTags_WhenThereAreNoTags() { Element element = model.addSoftwareSystem("Name", "Description"); assertEquals("Element,Software System", element.getTags()); } @Test - public void test_hasTag_ChecksRequiredTags() { + void test_hasTag_ChecksRequiredTags() { SoftwareSystem system = model.addSoftwareSystem("Name", "Description"); - assertTrue("hasTag returns true for Software System", system.hasTag("Software System")); - assertTrue("hasTag returns true for Element", system.hasTag("Element")); + assertTrue(system.hasTag("Software System"), "hasTag returns true for Software System"); + assertTrue(system.hasTag("Element"), "hasTag returns true for Element"); } @Test - public void test_getTags_ReturnsTheListOfTags_WhenThereAreSomeTags() { + void test_getTags_ReturnsTheListOfTags_WhenThereAreSomeTags() { Element element = model.addSoftwareSystem("Name", "Description"); element.addTags("tag1", "tag2", "tag3"); assertEquals("Element,Software System,tag1,tag2,tag3", element.getTags()); } @Test - public void test_setTags_DoesNotDoAnything_WhenPassedNull() { + void test_setTags_DoesNotDoAnything_WhenPassedNull() { Element element = model.addSoftwareSystem("Name", "Description"); element.setTags(null); assertEquals("Element,Software System", element.getTags()); } @Test - public void test_addTags_DoesNotDoAnything_WhenPassedNull() { + void test_addTags_DoesNotDoAnything_WhenPassedNull() { Element element = model.addSoftwareSystem("Name", "Description"); - element.addTags((String)null); + element.addTags((String) null); assertEquals("Element,Software System", element.getTags()); element.addTags(null, null, null); @@ -58,37 +55,38 @@ public void test_addTags_DoesNotDoAnything_WhenPassedNull() { } @Test - public void test_addTags_AddsTags_WhenPassedSomeTags() { + void test_addTags_AddsTags_WhenPassedSomeTags() { Element element = model.addSoftwareSystem("Name", "Description"); element.addTags(null, "tag1", null, "tag2"); assertEquals("Element,Software System,tag1,tag2", element.getTags()); } @Test - public void test_addTags_AddsTags_WhenPassedSomeTagsAndThereAreDuplicateTags() { + void test_addTags_AddsTags_WhenPassedSomeTagsAndThereAreDuplicateTags() { Element element = model.addSoftwareSystem("Name", "Description"); element.addTags(null, "tag1", null, "tag2", "tag2"); assertEquals("Element,Software System,tag1,tag2", element.getTags()); } @Test - public void test_removeTags() { + void test_removeTags() { Element element = model.addSoftwareSystem("Name", "Description"); element.addTags("tag1", "tag2"); - assertTrue("Remove an existing tag returns true", element.removeTag("tag1")); - assertFalse("Tag has been removed", element.hasTag("tag1")); + assertTrue(element.removeTag("tag1"), "Remove an existing tag returns true"); + assertFalse(element.hasTag("tag1"), "Tag has been removed"); - assertFalse("Remove a non-existing tag returns false", element.removeTag("no-such-tag")); - assertFalse("Remove a required tag returns false", element.removeTag("Element")); + assertFalse(element.removeTag("no-such-tag"), "Remove a non-existing tag returns false"); + assertFalse(element.removeTag("Element"), "Remove a required tag returns false"); } + @Test - public void test_getProperties_ReturnsAnEmptyList_WhenNoPropertiesHaveBeenAdded() { + void test_getProperties_ReturnsAnEmptyList_WhenNoPropertiesHaveBeenAdded() { Element element = model.addSoftwareSystem("Name", "Description"); assertEquals(0, element.getProperties().size()); } @Test - public void test_addProperty_ThrowsAnException_WhenTheNameIsNull() { + void test_addProperty_ThrowsAnException_WhenTheNameIsNull() { try { Element element = model.addSoftwareSystem("Name", "Description"); element.addProperty(null, "value"); @@ -99,7 +97,7 @@ public void test_addProperty_ThrowsAnException_WhenTheNameIsNull() { } @Test - public void test_addProperty_ThrowsAnException_WhenTheNameIsEmpty() { + void test_addProperty_ThrowsAnException_WhenTheNameIsEmpty() { try { Element element = model.addSoftwareSystem("Name", "Description"); element.addProperty(" ", "value"); @@ -110,7 +108,7 @@ public void test_addProperty_ThrowsAnException_WhenTheNameIsEmpty() { } @Test - public void test_addProperty_ThrowsAnException_WhenTheValueIsNull() { + void test_addProperty_ThrowsAnException_WhenTheValueIsNull() { try { Element element = model.addSoftwareSystem("Name", "Description"); element.addProperty("name", null); @@ -121,7 +119,7 @@ public void test_addProperty_ThrowsAnException_WhenTheValueIsNull() { } @Test - public void test_addProperty_ThrowsAnException_WhenTheValueIsEmpty() { + void test_addProperty_ThrowsAnException_WhenTheValueIsEmpty() { try { Element element = model.addSoftwareSystem("Name", "Description"); element.addProperty("name", " "); @@ -132,21 +130,21 @@ public void test_addProperty_ThrowsAnException_WhenTheValueIsEmpty() { } @Test - public void test_addProperty_AddsTheProperty_WhenANameAndValueAreSpecified() { + void test_addProperty_AddsTheProperty_WhenANameAndValueAreSpecified() { Element element = model.addSoftwareSystem("Name", "Description"); element.addProperty("AWS region", "us-east-1"); assertEquals("us-east-1", element.getProperties().get("AWS region")); } @Test - public void test_setProperties_DoesNothing_WhenNullIsSpecified() { + void test_setProperties_DoesNothing_WhenNullIsSpecified() { Element element = model.addSoftwareSystem("Name", "Description"); element.setProperties(null); assertEquals(0, element.getProperties().size()); } @Test - public void test_setProperties_SetsTheProperties_WhenANonEmptyMapIsSpecified() { + void test_setProperties_SetsTheProperties_WhenANonEmptyMapIsSpecified() { Element element = model.addSoftwareSystem("Name", "Description"); Map properties = new HashMap<>(); properties.put("name", "value"); @@ -156,7 +154,7 @@ public void test_setProperties_SetsTheProperties_WhenANonEmptyMapIsSpecified() { } @Test - public void test_addPerspective_ThrowsAnException_WhenANameIsNotSpecified() { + void test_addPerspective_ThrowsAnException_WhenANameIsNotSpecified() { try { Element element = model.addSoftwareSystem("Name", "Description"); element.addPerspective(null, null); @@ -167,7 +165,7 @@ public void test_addPerspective_ThrowsAnException_WhenANameIsNotSpecified() { } @Test - public void test_addPerspective_ThrowsAnException_WhenAnEmptyNameIsSpecified() { + void test_addPerspective_ThrowsAnException_WhenAnEmptyNameIsSpecified() { try { Element element = model.addSoftwareSystem("Name", "Description"); element.addPerspective(" ", null); @@ -176,8 +174,9 @@ public void test_addPerspective_ThrowsAnException_WhenAnEmptyNameIsSpecified() { assertEquals("A name must be specified.", iae.getMessage()); } } + @Test - public void test_addPerspective_ThrowsAnException_WhenADescriptionIsNotSpecified() { + void test_addPerspective_ThrowsAnException_WhenADescriptionIsNotSpecified() { try { Element element = model.addSoftwareSystem("Name", "Description"); element.addPerspective("Security", null); @@ -188,7 +187,7 @@ public void test_addPerspective_ThrowsAnException_WhenADescriptionIsNotSpecified } @Test - public void test_addPerspective_ThrowsAnException_WhenAnEmptyDescriptionIsSpecified() { + void test_addPerspective_ThrowsAnException_WhenAnEmptyDescriptionIsSpecified() { try { Element element = model.addSoftwareSystem("Name", "Description"); element.addPerspective("Security", " "); @@ -199,7 +198,7 @@ public void test_addPerspective_ThrowsAnException_WhenAnEmptyDescriptionIsSpecif } @Test - public void test_addPerspective_AddsAPerspective() { + void test_addPerspective_AddsAPerspective() { Element element = model.addSoftwareSystem("Name", "Description"); Perspective perspective = element.addPerspective("Security", "Data is encrypted at rest."); assertEquals("Security", perspective.getName()); @@ -208,7 +207,7 @@ public void test_addPerspective_AddsAPerspective() { } @Test - public void test_addPerspective_ThrowsAnException_WhenTheNamedPerspectiveAlreadyExists() { + void test_addPerspective_ThrowsAnException_WhenTheNamedPerspectiveAlreadyExists() { try { Element element = model.addSoftwareSystem("Name", "Description"); element.addPerspective("Security", "Data is encrypted at rest."); diff --git a/structurizr-core/test/unit/com/structurizr/model/ModelTests.java b/structurizr-core/test/unit/com/structurizr/model/ModelTests.java index 83c4b49cf..1fc5381a6 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ModelTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ModelTests.java @@ -1,37 +1,45 @@ package com.structurizr.model; import com.structurizr.AbstractWorkspaceTestBase; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Collections; import java.util.Set; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class ModelTests extends AbstractWorkspaceTestBase { - @Test(expected = IllegalArgumentException.class) - public void test_addSoftwareSystem_ThrowsAnException_WhenANullNameIsSpecified() { - model.addSoftwareSystem(null, ""); + @Test + void test_addSoftwareSystem_ThrowsAnException_WhenANullNameIsSpecified() { + assertThrows(IllegalArgumentException.class, () -> { + model.addSoftwareSystem(null, ""); + }); } - @Test(expected = IllegalArgumentException.class) - public void test_addSoftwareSystem_ThrowsAnException_WhenAnEmptyNameIsSpecified() { - model.addSoftwareSystem(" ", ""); + @Test + void test_addSoftwareSystem_ThrowsAnException_WhenAnEmptyNameIsSpecified() { + assertThrows(IllegalArgumentException.class, () -> { + model.addSoftwareSystem(" ", ""); + }); } - @Test(expected = IllegalArgumentException.class) - public void test_addPerson_ThrowsAnException_WhenANullNameIsSpecified() { - model.addPerson(null, ""); + @Test + void test_addPerson_ThrowsAnException_WhenANullNameIsSpecified() { + assertThrows(IllegalArgumentException.class, () -> { + model.addPerson(null, ""); + }); } - @Test(expected = IllegalArgumentException.class) - public void test_addPerson_ThrowsAnException_WhenAnEmptyNameIsSpecified() { - model.addPerson(" ", ""); + @Test + void test_addPerson_ThrowsAnException_WhenAnEmptyNameIsSpecified() { + assertThrows(IllegalArgumentException.class, () -> { + model.addPerson(" ", ""); + }); } @Test - public void test_addSoftwareSystem_AddsTheSoftwareSystem_WhenASoftwareSystemDoesNotExistWithTheSameName() { + void test_addSoftwareSystem_AddsTheSoftwareSystem_WhenASoftwareSystemDoesNotExistWithTheSameName() { assertTrue(model.getSoftwareSystems().isEmpty()); SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.External, "System A", "Some description"); assertEquals(1, model.getSoftwareSystems().size()); @@ -44,7 +52,7 @@ public void test_addSoftwareSystem_AddsTheSoftwareSystem_WhenASoftwareSystemDoes } @Test - public void test_addSoftwareSystem_ThrowsAnException_WhenASoftwareSystemExistsWithTheSameName() { + void test_addSoftwareSystem_ThrowsAnException_WhenASoftwareSystemExistsWithTheSameName() { SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.External, "System A", "Some description"); assertEquals(1, model.getSoftwareSystems().size()); @@ -57,7 +65,7 @@ public void test_addSoftwareSystem_ThrowsAnException_WhenASoftwareSystemExistsWi } @Test - public void test_addSoftwareSystemWithoutSpecifyingLocation_AddsTheSoftwareSystem_WhenASoftwareSystemDoesNotExistWithTheSameName() { + void test_addSoftwareSystemWithoutSpecifyingLocation_AddsTheSoftwareSystem_WhenASoftwareSystemDoesNotExistWithTheSameName() { assertTrue(model.getSoftwareSystems().isEmpty()); SoftwareSystem softwareSystem = model.addSoftwareSystem("System A", "Some description"); assertEquals(1, model.getSoftwareSystems().size()); @@ -70,7 +78,7 @@ public void test_addSoftwareSystemWithoutSpecifyingLocation_AddsTheSoftwareSyste } @Test - public void test_addPerson_AddsThePerson_WhenAPersonDoesNotExistWithTheSameName() { + void test_addPerson_AddsThePerson_WhenAPersonDoesNotExistWithTheSameName() { assertTrue(model.getPeople().isEmpty()); Person person = model.addPerson(Location.Internal, "Some internal user", "Some description"); assertEquals(1, model.getPeople().size()); @@ -83,7 +91,7 @@ public void test_addPerson_AddsThePerson_WhenAPersonDoesNotExistWithTheSameName( } @Test - public void test_addPerson_ThrowsAnException_WhenAPersonExistsWithTheSameName() { + void test_addPerson_ThrowsAnException_WhenAPersonExistsWithTheSameName() { Person person = model.addPerson(Location.Internal, "Admin User", "Description"); assertEquals(1, model.getPeople().size()); @@ -96,7 +104,7 @@ public void test_addPerson_ThrowsAnException_WhenAPersonExistsWithTheSameName() } @Test - public void test_addPerson_AddsThePersonWithoutSpecifyingTheLocation_WhenAPersonDoesNotExistWithTheSameName() { + void test_addPerson_AddsThePersonWithoutSpecifyingTheLocation_WhenAPersonDoesNotExistWithTheSameName() { assertTrue(model.getPeople().isEmpty()); Person person = model.addPerson("Some internal user", "Some description"); assertEquals(1, model.getPeople().size()); @@ -109,42 +117,42 @@ public void test_addPerson_AddsThePersonWithoutSpecifyingTheLocation_WhenAPerson } @Test - public void test_getElement_ReturnsNull_WhenAnElementWithTheSpecifiedIdDoesNotExist() { + void test_getElement_ReturnsNull_WhenAnElementWithTheSpecifiedIdDoesNotExist() { assertNull(model.getElement("100")); } @Test - public void test_getElement_ReturnsAnElement_WhenAnElementWithTheSpecifiedIdDoesExist() { + void test_getElement_ReturnsAnElement_WhenAnElementWithTheSpecifiedIdDoesExist() { Person person = model.addPerson(Location.Internal, "Name", "Description"); assertSame(person, model.getElement(person.getId())); } @Test - public void test_contains_ReturnsFalse_WhenTheSpecifiedElementIsNotInTheModel() { + void test_contains_ReturnsFalse_WhenTheSpecifiedElementIsNotInTheModel() { Model newModel = new Model(); SoftwareSystem softwareSystem = newModel.addSoftwareSystem(Location.Unspecified, "Name", "Description"); assertFalse(model.contains(softwareSystem)); } @Test - public void test_contains_ReturnsTrue_WhenTheSpecifiedElementIsInTheModel() { + void test_contains_ReturnsTrue_WhenTheSpecifiedElementIsInTheModel() { SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Unspecified, "Name", "Description"); assertTrue(model.contains(softwareSystem)); } @Test - public void test_getSoftwareSystemWithName_ReturnsNull_WhenASoftwareSystemWithTheSpecifiedNameDoesNotExist() { + void test_getSoftwareSystemWithName_ReturnsNull_WhenASoftwareSystemWithTheSpecifiedNameDoesNotExist() { assertNull(model.getSoftwareSystemWithName("System X")); } @Test - public void test_getSoftwareSystemWithName_ReturnsASoftwareSystem_WhenASoftwareSystemWithTheSpecifiedNameExists() { + void test_getSoftwareSystemWithName_ReturnsASoftwareSystem_WhenASoftwareSystemWithTheSpecifiedNameExists() { SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.External, "System A", "Description"); assertSame(softwareSystem, model.getSoftwareSystemWithName("System A")); } @Test - public void test_getSoftwareSystemWithId_ThrowsAnException_WhenPassedANullId() { + void test_getSoftwareSystemWithId_ThrowsAnException_WhenPassedANullId() { try { model.getSoftwareSystemWithId(null); fail(); @@ -154,7 +162,7 @@ public void test_getSoftwareSystemWithId_ThrowsAnException_WhenPassedANullId() { } @Test - public void test_getSoftwareSystemWithId_ThrowsAnException_WhenPassedAnEmptyId() { + void test_getSoftwareSystemWithId_ThrowsAnException_WhenPassedAnEmptyId() { try { model.getSoftwareSystemWithId(" "); fail(); @@ -164,29 +172,29 @@ public void test_getSoftwareSystemWithId_ThrowsAnException_WhenPassedAnEmptyId() } @Test - public void test_getSoftwareSystemWithId_ReturnsNull_WhenASoftwareSystemWithTheSpecifiedIdDoesNotExist() { + void test_getSoftwareSystemWithId_ReturnsNull_WhenASoftwareSystemWithTheSpecifiedIdDoesNotExist() { assertNull(model.getSoftwareSystemWithId("100")); } @Test - public void test_getSoftwareSystemWithId_ReturnsASoftwareSystem_WhenASoftwareSystemWithTheSpecifiedIdDoesExist() { + void test_getSoftwareSystemWithId_ReturnsASoftwareSystem_WhenASoftwareSystemWithTheSpecifiedIdDoesExist() { SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.External, "System A", "Description"); assertSame(softwareSystem, model.getSoftwareSystemWithId(softwareSystem.getId())); } @Test - public void test_getPersonWithName_ReturnsNull_WhenAPersonWithTheSpecifiedNameDoesNotExist() { + void test_getPersonWithName_ReturnsNull_WhenAPersonWithTheSpecifiedNameDoesNotExist() { assertNull(model.getPersonWithName("Admin User")); } @Test - public void test_getPersonWithName_ReturnsAPerson_WhenAPersonWithTheSpecifiedNameExists() { + void test_getPersonWithName_ReturnsAPerson_WhenAPersonWithTheSpecifiedNameExists() { Person person = model.addPerson(Location.External, "Admin User", "Description"); assertSame(person, model.getPersonWithName("Admin User")); } @Test - public void test_getRelationship_ThrowsAnException_WhenPassedANullId() { + void test_getRelationship_ThrowsAnException_WhenPassedANullId() { try { model.getRelationship(null); fail(); @@ -196,7 +204,7 @@ public void test_getRelationship_ThrowsAnException_WhenPassedANullId() { } @Test - public void test_getRelationship_ThrowsAnException_WhenPassedAnEmptyId() { + void test_getRelationship_ThrowsAnException_WhenPassedAnEmptyId() { try { model.getRelationship(" "); fail(); @@ -206,7 +214,7 @@ public void test_getRelationship_ThrowsAnException_WhenPassedAnEmptyId() { } @Test - public void test_addRelationship_AddsARelationshipWithTheSpecifiedDescriptionAndTechnologyAndInteractionStyle() { + void test_addRelationship_AddsARelationshipWithTheSpecifiedDescriptionAndTechnologyAndInteractionStyle() { SoftwareSystem a = model.addSoftwareSystem("A", ""); SoftwareSystem b = model.addSoftwareSystem("B", ""); Relationship relationship = model.addRelationship(a, b, "Uses", "HTTPS", InteractionStyle.Asynchronous); @@ -221,7 +229,7 @@ public void test_addRelationship_AddsARelationshipWithTheSpecifiedDescriptionAnd } @Test - public void test_addRelationship_DisallowsTheSameRelationshipToBeAddedMoreThanOnce() { + void test_addRelationship_DisallowsTheSameRelationshipToBeAddedMoreThanOnce() { SoftwareSystem element1 = model.addSoftwareSystem("Element 1", "Description"); SoftwareSystem element2 = model.addSoftwareSystem("Element 2", "Description"); Relationship relationship1 = element1.uses(element2, "Uses", ""); @@ -232,7 +240,7 @@ public void test_addRelationship_DisallowsTheSameRelationshipToBeAddedMoreThanOn } @Test - public void test_addRelationship_AllowsMultipleRelationshipsBetweenElements() { + void test_addRelationship_AllowsMultipleRelationshipsBetweenElements() { SoftwareSystem element1 = model.addSoftwareSystem("Element 1", "Description"); SoftwareSystem element2 = model.addSoftwareSystem("Element 2", "Description"); Relationship relationship1 = element1.uses(element2, "Uses in some way", ""); @@ -243,7 +251,7 @@ public void test_addRelationship_AllowsMultipleRelationshipsBetweenElements() { } @Test - public void test_addRelationship_ThrowsAnException_WhenTheDestinationIsAChildOfTheSource() { + void test_addRelationship_ThrowsAnException_WhenTheDestinationIsAChildOfTheSource() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); Container container = softwareSystem.addContainer("Container", "", ""); Component component = container.addComponent("Component", "", ""); @@ -274,7 +282,7 @@ public void test_addRelationship_ThrowsAnException_WhenTheDestinationIsAChildOfT } @Test - public void test_addRelationship_ThrowsAnException_WhenTheSourceIsAChildOfTheDestination() { + void test_addRelationship_ThrowsAnException_WhenTheSourceIsAChildOfTheDestination() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); Container container = softwareSystem.addContainer("Container", "", ""); Component component = container.addComponent("Component", "", ""); @@ -305,7 +313,7 @@ public void test_addRelationship_ThrowsAnException_WhenTheSourceIsAChildOfTheDes } @Test - public void test_modifyRelationship_ThrowsAnException_WhenARelationshipIsNotSpecified() { + void test_modifyRelationship_ThrowsAnException_WhenARelationshipIsNotSpecified() { try { model.modifyRelationship(null, "Uses", "Technology"); fail(); @@ -315,7 +323,7 @@ public void test_modifyRelationship_ThrowsAnException_WhenARelationshipIsNotSpec } @Test - public void test_modifyRelationship_ModifiesAnExistingRelationship_WhenThatRelationshipDoesNotAlreadyExist() { + void test_modifyRelationship_ModifiesAnExistingRelationship_WhenThatRelationshipDoesNotAlreadyExist() { SoftwareSystem element1 = model.addSoftwareSystem("Element 1", "Description"); SoftwareSystem element2 = model.addSoftwareSystem("Element 2", "Description"); Relationship relationship = element1.uses(element2, "", ""); @@ -326,7 +334,7 @@ public void test_modifyRelationship_ModifiesAnExistingRelationship_WhenThatRelat } @Test - public void test_modifyRelationship_ThrowsAnException_WhenThatRelationshipDoesAlreadyExist() { + void test_modifyRelationship_ThrowsAnException_WhenThatRelationshipDoesAlreadyExist() { SoftwareSystem element1 = model.addSoftwareSystem("Element 1", "Description"); SoftwareSystem element2 = model.addSoftwareSystem("Element 2", "Description"); Relationship relationship = element1.uses(element2, "Uses", "Technology"); @@ -340,7 +348,7 @@ public void test_modifyRelationship_ThrowsAnException_WhenThatRelationshipDoesAl } @Test - public void test_addSoftwareSystemInstance_ThrowsAnException_WhenANullSoftwareSystemIsSpecified() { + void test_addSoftwareSystemInstance_ThrowsAnException_WhenANullSoftwareSystemIsSpecified() { try { DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node", "Description", "Technology"); deploymentNode.add((SoftwareSystem) null); @@ -351,10 +359,10 @@ public void test_addSoftwareSystemInstance_ThrowsAnException_WhenANullSoftwareSy } @Test - public void test_addContainerInstance_ThrowsAnException_WhenANullContainerIsSpecified() { + void test_addContainerInstance_ThrowsAnException_WhenANullContainerIsSpecified() { try { DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node", "Description", "Technology"); - deploymentNode.add((Container)null); + deploymentNode.add((Container) null); fail(); } catch (Exception e) { assertEquals("A container must be specified.", e.getMessage()); @@ -362,7 +370,7 @@ public void test_addContainerInstance_ThrowsAnException_WhenANullContainerIsSpec } @Test - public void test_addDeploymentNode_AddsADeploymentNode_WhenADeploymentEnvironmentIsNotSpecified() { + void test_addDeploymentNode_AddsADeploymentNode_WhenADeploymentEnvironmentIsNotSpecified() { DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node", "Description", "Technology"); assertEquals("Deployment Node", deploymentNode.getName()); @@ -372,7 +380,7 @@ public void test_addDeploymentNode_AddsADeploymentNode_WhenADeploymentEnvironmen } @Test - public void test_addDeploymentNode_AddsADeploymentNode_WhenADeploymentEnvironmentIsSpecified() { + void test_addDeploymentNode_AddsADeploymentNode_WhenADeploymentEnvironmentIsSpecified() { DeploymentNode deploymentNode = model.addDeploymentNode("Development", "Deployment Node", "Description", "Technology"); assertEquals("Deployment Node", deploymentNode.getName()); @@ -382,7 +390,7 @@ public void test_addDeploymentNode_AddsADeploymentNode_WhenADeploymentEnvironmen } @Test - public void test_addElementInstance_AddsElementInstancesAndReplicatesRelationshipsWithinTheDeploymentEnvironment() { + void test_addElementInstance_AddsElementInstancesAndReplicatesRelationshipsWithinTheDeploymentEnvironment() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1", "Description"); Container container1 = softwareSystem1.addContainer("Container 1", "Description", "Technology"); @@ -440,7 +448,7 @@ public void test_addElementInstance_AddsElementInstancesAndReplicatesRelationshi } @Test - public void test_addElementInstance_AddsElementInstancesAndReplicatesRelationshipsWithinTheDeploymentEnvironmentAndDefaultGroup() { + void test_addElementInstance_AddsElementInstancesAndReplicatesRelationshipsWithinTheDeploymentEnvironmentAndDefaultGroup() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System"); Container api = softwareSystem1.addContainer("API"); Container database = softwareSystem1.addContainer("Database"); @@ -474,7 +482,7 @@ public void test_addElementInstance_AddsElementInstancesAndReplicatesRelationshi } @Test - public void test_addElementInstance_AddsElementInstancesAndReplicatesRelationshipsWithinTheDeploymentEnvironmentAndSpecifiedGroup() { + void test_addElementInstance_AddsElementInstancesAndReplicatesRelationshipsWithinTheDeploymentEnvironmentAndSpecifiedGroup() { // in this test, container instances are added to two deployment groups: "Instance 1" and "Instance 2" // relationships are not replicated between element instances in other groups @@ -503,7 +511,7 @@ public void test_addElementInstance_AddsElementInstancesAndReplicatesRelationshi } @Test - public void test_addElementInstance_AddsElementInstancesAndReplicatesRelationshipsWithinTheDeploymentEnvironmentAndSpecifiedGroups() { + void test_addElementInstance_AddsElementInstancesAndReplicatesRelationshipsWithinTheDeploymentEnvironmentAndSpecifiedGroups() { // in this test: // - API container instances are added to "Instance 1", "Instance 2" and "Shared" // - database container instances are added to "Instance 1" and "Instance 2" @@ -543,7 +551,7 @@ public void test_addElementInstance_AddsElementInstancesAndReplicatesRelationshi } @Test - public void test_getElement_ThrowsAnException_WhenANullIdIsSpecified() { + void test_getElement_ThrowsAnException_WhenANullIdIsSpecified() { try { model.getElement(null); } catch (IllegalArgumentException iae) { @@ -552,7 +560,7 @@ public void test_getElement_ThrowsAnException_WhenANullIdIsSpecified() { } @Test - public void test_getElement_ThrowsAnException_WhenAnEmptyIdIsSpecified() { + void test_getElement_ThrowsAnException_WhenAnEmptyIdIsSpecified() { try { model.getElement(" "); } catch (IllegalArgumentException iae) { @@ -561,7 +569,7 @@ public void test_getElement_ThrowsAnException_WhenAnEmptyIdIsSpecified() { } @Test - public void test_getElementWithCanonicalName_ThrowsAnException_WhenANullCanonicalNameIsSpecified() { + void test_getElementWithCanonicalName_ThrowsAnException_WhenANullCanonicalNameIsSpecified() { try { model.getElementWithCanonicalName(null); } catch (IllegalArgumentException iae) { @@ -570,7 +578,7 @@ public void test_getElementWithCanonicalName_ThrowsAnException_WhenANullCanonica } @Test - public void test_getElementWithCanonicalName_ThrowsAnException_WhenAnEmptyCanonicalNameIsSpecified() { + void test_getElementWithCanonicalName_ThrowsAnException_WhenAnEmptyCanonicalNameIsSpecified() { try { model.getElementWithCanonicalName(" "); } catch (IllegalArgumentException iae) { @@ -579,12 +587,12 @@ public void test_getElementWithCanonicalName_ThrowsAnException_WhenAnEmptyCanoni } @Test - public void test_getElementWithCanonicalName_ReturnsNull_WhenAnElementWithTheSpecifiedCanonicalNameDoesNotExist() { + void test_getElementWithCanonicalName_ReturnsNull_WhenAnElementWithTheSpecifiedCanonicalNameDoesNotExist() { assertNull(model.getElementWithCanonicalName("Software System")); } @Test - public void test_getElementWithCanonicalName_ReturnsTheElement_WhenAnElementWithTheSpecifiedCanonicalNameExists() { + void test_getElementWithCanonicalName_ReturnsTheElement_WhenAnElementWithTheSpecifiedCanonicalNameExists() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); Container container = softwareSystem.addContainer("Web Application", "Description", "Technology"); @@ -593,7 +601,7 @@ public void test_getElementWithCanonicalName_ReturnsTheElement_WhenAnElementWith } @Test - public void test_addDeploymentNode_ThrowsAnException_WhenADeploymentNodeWithTheSameNameAlreadyExists() { + void test_addDeploymentNode_ThrowsAnException_WhenADeploymentNodeWithTheSameNameAlreadyExists() { model.addDeploymentNode("Amazon AWS", "Description", "Technology"); try { model.addDeploymentNode("Amazon AWS", "Description", "Technology"); @@ -604,7 +612,7 @@ public void test_addDeploymentNode_ThrowsAnException_WhenADeploymentNodeWithTheS } @Test - public void test_addDeploymentNode_ThrowsAnException_WhenAChildDeploymentNodeWithTheSameNameAlreadyExists() { + void test_addDeploymentNode_ThrowsAnException_WhenAChildDeploymentNodeWithTheSameNameAlreadyExists() { DeploymentNode deploymentNode = model.addDeploymentNode("Amazon Web Services"); deploymentNode.addDeploymentNode("AWS Region"); try { @@ -616,7 +624,7 @@ public void test_addDeploymentNode_ThrowsAnException_WhenAChildDeploymentNodeWit } @Test - public void test_addDeploymentNode_ThrowsAnException_WhenAChildInfrastructureNodeWithTheSameNameAlreadyExists() { + void test_addDeploymentNode_ThrowsAnException_WhenAChildInfrastructureNodeWithTheSameNameAlreadyExists() { DeploymentNode deploymentNode = model.addDeploymentNode("Amazon Web Services"); deploymentNode.addInfrastructureNode("Node"); try { @@ -628,7 +636,7 @@ public void test_addDeploymentNode_ThrowsAnException_WhenAChildInfrastructureNod } @Test - public void test_addInfrastructureNode_ThrowsAnException_WhenAChildDeploymentNodeWithTheSameNameAlreadyExists() { + void test_addInfrastructureNode_ThrowsAnException_WhenAChildDeploymentNodeWithTheSameNameAlreadyExists() { DeploymentNode deploymentNode = model.addDeploymentNode("Amazon Web Services"); deploymentNode.addDeploymentNode("Node"); try { @@ -640,7 +648,7 @@ public void test_addInfrastructureNode_ThrowsAnException_WhenAChildDeploymentNod } @Test - public void test_addInfrastructureNode_ThrowsAnException_WhenAChildInfrastructureNodeWithTheSameNameAlreadyExists() { + void test_addInfrastructureNode_ThrowsAnException_WhenAChildInfrastructureNodeWithTheSameNameAlreadyExists() { DeploymentNode deploymentNode = model.addDeploymentNode("Amazon Web Services"); deploymentNode.addInfrastructureNode("Node"); try { @@ -652,7 +660,7 @@ public void test_addInfrastructureNode_ThrowsAnException_WhenAChildInfrastructur } @Test - public void test_setIdGenerator_ThrowsAnException_WhenANullIdGeneratorIsSpecified() { + void test_setIdGenerator_ThrowsAnException_WhenANullIdGeneratorIsSpecified() { try { model.setIdGenerator(null); fail(); @@ -662,7 +670,7 @@ public void test_setIdGenerator_ThrowsAnException_WhenANullIdGeneratorIsSpecifie } @Test - public void test_hydrate() { + void test_hydrate() { Person person = new Person(); person.setId("1"); person.setName("Person"); @@ -742,7 +750,7 @@ public void test_hydrate() { } @Test - public void test_impliedRelationshipStrategy() { + void test_impliedRelationshipStrategy() { // default strategy initially assertTrue(model.getImpliedRelationshipsStrategy() instanceof DefaultImpliedRelationshipsStrategy); @@ -751,7 +759,7 @@ public void test_impliedRelationshipStrategy() { } @Test - public void test_setImpliedRelationshipStrategy_ResetsToTheDefaultStrategy_WhenPassedNull() { + void test_setImpliedRelationshipStrategy_ResetsToTheDefaultStrategy_WhenPassedNull() { model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); model.setImpliedRelationshipsStrategy(null); @@ -759,7 +767,7 @@ public void test_setImpliedRelationshipStrategy_ResetsToTheDefaultStrategy_WhenP } @Test - public void test_addSoftwareSystemInstance_AllocatesInstanceIdsPerDeploymentNode() { + void test_addSoftwareSystemInstance_AllocatesInstanceIdsPerDeploymentNode() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); DeploymentNode deploymentNodeA = model.addDeploymentNode("Deployment Node A", "", ""); DeploymentNode deploymentNodeB = model.addDeploymentNode("Deployment Node B", "", ""); @@ -775,7 +783,7 @@ public void test_addSoftwareSystemInstance_AllocatesInstanceIdsPerDeploymentNode } @Test - public void test_addContainerInstance_AllocatesInstanceIdsPerDeploymentNode() { + void test_addContainerInstance_AllocatesInstanceIdsPerDeploymentNode() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); Container container = softwareSystem.addContainer("Container", "", ""); DeploymentNode deploymentNodeA = model.addDeploymentNode("Deployment Node A", "", ""); diff --git a/structurizr-core/test/unit/com/structurizr/model/PersonTests.java b/structurizr-core/test/unit/com/structurizr/model/PersonTests.java index f51836712..6334a04f9 100644 --- a/structurizr-core/test/unit/com/structurizr/model/PersonTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/PersonTests.java @@ -1,33 +1,33 @@ package com.structurizr.model; import com.structurizr.AbstractWorkspaceTestBase; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class PersonTests extends AbstractWorkspaceTestBase { @Test - public void test_getCanonicalName() { + void test_getCanonicalName() { Person person = model.addPerson("Person", "Description"); assertEquals("Person://Person", person.getCanonicalName()); } @Test - public void test_getCanonicalName_WhenNameContainsSlashAndDotCharacters() { + void test_getCanonicalName_WhenNameContainsSlashAndDotCharacters() { Person person = model.addPerson("Person", "Description"); person.setName("Name1/.Name2"); assertEquals("Person://Name1Name2", person.getCanonicalName()); } @Test - public void test_getParent_ReturnsNull() { + void test_getParent_ReturnsNull() { Person person = model.addPerson("Person", "Description"); assertNull(person.getParent()); } @Test - public void test_removeTags_DoesNotRemoveRequiredTags() { + void test_removeTags_DoesNotRemoveRequiredTags() { Person person = model.addPerson("Person", "Description"); assertTrue(person.getTags().contains(Tags.ELEMENT)); assertTrue(person.getTags().contains(Tags.PERSON)); @@ -40,7 +40,7 @@ public void test_removeTags_DoesNotRemoveRequiredTags() { } @Test - public void test_interactsWith_AddsARelationshipWhenTheDescriptionIsSpecified() { + void test_interactsWith_AddsARelationshipWhenTheDescriptionIsSpecified() { Person person1 = model.addPerson("Person 1", "Description"); Person person2 = model.addPerson("Person 2", "Description"); @@ -56,7 +56,7 @@ public void test_interactsWith_AddsARelationshipWhenTheDescriptionIsSpecified() } @Test - public void test_interactsWith_AddsARelationshipWhenTheDescriptionAndTechnologyAreSpecified() { + void test_interactsWith_AddsARelationshipWhenTheDescriptionAndTechnologyAreSpecified() { Person person1 = model.addPerson("Person 1", "Description"); Person person2 = model.addPerson("Person 2", "Description"); @@ -72,7 +72,7 @@ public void test_interactsWith_AddsARelationshipWhenTheDescriptionAndTechnologyA } @Test - public void test_interactsWith_AddsARelationshipWhenTheDescriptionAndTechnologyAndInteractionStyleAreSpecified() { + void test_interactsWith_AddsARelationshipWhenTheDescriptionAndTechnologyAndInteractionStyleAreSpecified() { Person person1 = model.addPerson("Person 1", "Description"); Person person2 = model.addPerson("Person 2", "Description"); @@ -88,7 +88,7 @@ public void test_interactsWith_AddsARelationshipWhenTheDescriptionAndTechnologyA } @Test - public void test_setLocation_SetsTheLocationToUnspecified_WhenNullIsPassed() { + void test_setLocation_SetsTheLocationToUnspecified_WhenNullIsPassed() { Person person = model.addPerson("Person", "Description"); person.setLocation(null); assertEquals(Location.Unspecified, person.getLocation()); diff --git a/structurizr-core/test/unit/com/structurizr/model/RelationshipTests.java b/structurizr-core/test/unit/com/structurizr/model/RelationshipTests.java index da5fba715..c854ff1cb 100644 --- a/structurizr-core/test/unit/com/structurizr/model/RelationshipTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/RelationshipTests.java @@ -1,42 +1,42 @@ package com.structurizr.model; import com.structurizr.AbstractWorkspaceTestBase; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class RelationshipTests extends AbstractWorkspaceTestBase { private SoftwareSystem softwareSystem1, softwareSystem2; - @Before + @BeforeEach public void setUp() { softwareSystem1 = model.addSoftwareSystem(Location.Internal, "Name1", "Description"); softwareSystem2 = model.addSoftwareSystem(Location.Internal, "Name2", "Description"); } @Test - public void test_getDescription_NeverReturnsNull() { + void test_getDescription_NeverReturnsNull() { Relationship relationship = softwareSystem1.uses(softwareSystem2, null); assertEquals("", relationship.getDescription()); } @Test - public void test_getTags_WhenThereAreNoTags() { + void test_getTags_WhenThereAreNoTags() { Relationship relationship = softwareSystem1.uses(softwareSystem2, "uses"); assertEquals("Relationship", relationship.getTags()); } @Test - public void test_getTags_ReturnsTheListOfTags_WhenThereAreSomeTags() { + void test_getTags_ReturnsTheListOfTags_WhenThereAreSomeTags() { Relationship relationship = softwareSystem1.uses(softwareSystem2, "uses"); relationship.addTags("tag1", "tag2", "tag3"); assertEquals("Relationship,tag1,tag2,tag3", relationship.getTags()); } @Test - public void test_setTags_ClearsTheTags_WhenPassedNull() { + void test_setTags_ClearsTheTags_WhenPassedNull() { Relationship relationship = softwareSystem1.uses(softwareSystem2, "uses"); relationship.addTags("Tag 1", "Tag 2"); assertEquals("Relationship,Tag 1,Tag 2", relationship.getTags()); @@ -45,9 +45,9 @@ public void test_setTags_ClearsTheTags_WhenPassedNull() { } @Test - public void test_addTags_DoesNotDoAnything_WhenPassedNull() { + void test_addTags_DoesNotDoAnything_WhenPassedNull() { Relationship relationship = softwareSystem1.uses(softwareSystem2, "uses"); - relationship.addTags((String)null); + relationship.addTags((String) null); assertEquals("Relationship", relationship.getTags()); relationship.addTags(null, null, null); @@ -55,20 +55,20 @@ public void test_addTags_DoesNotDoAnything_WhenPassedNull() { } @Test - public void test_addTags_AddsTags_WhenPassedSomeTags() { + void test_addTags_AddsTags_WhenPassedSomeTags() { Relationship relationship = softwareSystem1.uses(softwareSystem2, "uses"); relationship.addTags(null, "tag1", null, "tag2"); assertEquals("Relationship,tag1,tag2", relationship.getTags()); } @Test - public void test_getInteractionStyle_ReturnsNull_WhenNotExplicitlySet() { + void test_getInteractionStyle_ReturnsNull_WhenNotExplicitlySet() { Relationship relationship = softwareSystem1.uses(softwareSystem2, "uses"); assertNull(relationship.getInteractionStyle()); } @Test - public void test_getTags_IncludesTheInteractionStyleWhenSpecified() { + void test_getTags_IncludesTheInteractionStyleWhenSpecified() { Relationship relationship = softwareSystem1.uses(softwareSystem2, "Uses 1", "Technology"); assertFalse(relationship.getTags().contains(Tags.SYNCHRONOUS)); assertFalse(relationship.getTags().contains(Tags.ASYNCHRONOUS)); @@ -83,20 +83,22 @@ public void test_getTags_IncludesTheInteractionStyleWhenSpecified() { } @Test - public void test_setUrl() { + void test_setUrl() { Relationship relationship = softwareSystem1.uses(softwareSystem2, "Uses 1", "Technology"); relationship.setUrl("https://structurizr.com"); assertEquals("https://structurizr.com", relationship.getUrl()); } - @Test(expected = IllegalArgumentException.class) - public void test_setUrl_ThrowsAnIllegalArgumentException_WhenAnInvalidUrlIsSpecified() { - Relationship relationship = softwareSystem1.uses(softwareSystem2, "Uses 1", "Technology"); - relationship.setUrl("htt://blah"); + @Test + void test_setUrl_ThrowsAnIllegalArgumentException_WhenAnInvalidUrlIsSpecified() { + assertThrows(IllegalArgumentException.class, () -> { + Relationship relationship = softwareSystem1.uses(softwareSystem2, "Uses 1", "Technology"); + relationship.setUrl("htt://blah"); + }); } @Test - public void test_setUrl_ResetsTheUrl_WhenANullUrlIsSpecified() { + void test_setUrl_ResetsTheUrl_WhenANullUrlIsSpecified() { Relationship relationship = softwareSystem1.uses(softwareSystem2, "Uses 1", "Technology"); relationship.setUrl("https://structurizr.com"); relationship.setUrl(null); @@ -104,7 +106,7 @@ public void test_setUrl_ResetsTheUrl_WhenANullUrlIsSpecified() { } @Test - public void test_setUrl_ResetsTheUrl_WhenAnEmptyUrlIsSpecified() { + void test_setUrl_ResetsTheUrl_WhenAnEmptyUrlIsSpecified() { Relationship relationship = softwareSystem1.uses(softwareSystem2, "Uses 1", "Technology"); relationship.setUrl("https://structurizr.com"); relationship.setUrl(" "); @@ -112,7 +114,7 @@ public void test_setUrl_ResetsTheUrl_WhenAnEmptyUrlIsSpecified() { } @Test - public void test_interactionStyle_CanBeSetToNull() { + void test_interactionStyle_CanBeSetToNull() { Relationship relationship = softwareSystem1.uses(softwareSystem2, "Uses 1", "Technology", null); assertNull(relationship.getInteractionStyle()); diff --git a/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemInstanceTests.java b/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemInstanceTests.java index 8930aac5e..0e7c352ec 100644 --- a/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemInstanceTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemInstanceTests.java @@ -1,9 +1,9 @@ package com.structurizr.model; import com.structurizr.AbstractWorkspaceTestBase; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class SoftwareSystemInstanceTests extends AbstractWorkspaceTestBase { @@ -11,7 +11,7 @@ public class SoftwareSystemInstanceTests extends AbstractWorkspaceTestBase { private DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node", "Description", "Technology"); @Test - public void test_construction() { + void test_construction() { SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); assertSame(softwareSystem, instance.getSoftwareSystem()); @@ -20,7 +20,7 @@ public void test_construction() { } @Test - public void test_getSoftwareSystemId() { + void test_getSoftwareSystemId() { SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); assertEquals(softwareSystem.getId(), instance.getSoftwareSystemId()); @@ -30,7 +30,7 @@ public void test_getSoftwareSystemId() { } @Test - public void test_getName_CannotBeChanged() { + void test_getName_CannotBeChanged() { SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); assertEquals("System", instance.getName()); @@ -40,28 +40,28 @@ public void test_getName_CannotBeChanged() { } @Test - public void test_getCanonicalName() { + void test_getCanonicalName() { SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); assertEquals("SoftwareSystemInstance://Default/Deployment Node/System[1]", instance.getCanonicalName()); } @Test - public void test_getParent_ReturnsTheParentDeploymentNode() { + void test_getParent_ReturnsTheParentDeploymentNode() { SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); assertEquals(deploymentNode, instance.getParent()); } @Test - public void test_getRequiredTags() { + void test_getRequiredTags() { SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); assertTrue(instance.getDefaultTags().isEmpty()); } @Test - public void test_getTags() { + void test_getTags() { softwareSystem.addTags("Tag 1"); SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); instance.addTags("Primary Instance"); @@ -70,7 +70,7 @@ public void test_getTags() { } @Test - public void test_removeTags_DoesNotRemoveAnyTags() { + void test_removeTags_DoesNotRemoveAnyTags() { SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); assertTrue(instance.getTags().contains(Tags.SOFTWARE_SYSTEM_INSTANCE)); @@ -81,7 +81,7 @@ public void test_removeTags_DoesNotRemoveAnyTags() { } @Test - public void test_addHealthCheck() { + void test_addHealthCheck() { SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); assertTrue(instance.getHealthChecks().isEmpty()); @@ -94,7 +94,7 @@ public void test_addHealthCheck() { } @Test - public void test_addHealthCheck_ThrowsAnException_WhenTheNameIsNull() { + void test_addHealthCheck_ThrowsAnException_WhenTheNameIsNull() { SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); try { @@ -106,7 +106,7 @@ public void test_addHealthCheck_ThrowsAnException_WhenTheNameIsNull() { } @Test - public void test_addHealthCheck_ThrowsAnException_WhenTheNameIsEmpty() { + void test_addHealthCheck_ThrowsAnException_WhenTheNameIsEmpty() { SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); try { @@ -118,7 +118,7 @@ public void test_addHealthCheck_ThrowsAnException_WhenTheNameIsEmpty() { } @Test - public void test_addHealthCheck_ThrowsAnException_WhenTheUrlIsNull() { + void test_addHealthCheck_ThrowsAnException_WhenTheUrlIsNull() { SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); try { @@ -130,7 +130,7 @@ public void test_addHealthCheck_ThrowsAnException_WhenTheUrlIsNull() { } @Test - public void test_addHealthCheck_ThrowsAnException_WhenTheUrlIsEmpty() { + void test_addHealthCheck_ThrowsAnException_WhenTheUrlIsEmpty() { SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); try { @@ -142,7 +142,7 @@ public void test_addHealthCheck_ThrowsAnException_WhenTheUrlIsEmpty() { } @Test - public void test_addHealthCheck_ThrowsAnException_WhenTheUrlIsInvalid() { + void test_addHealthCheck_ThrowsAnException_WhenTheUrlIsInvalid() { SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); try { @@ -154,7 +154,7 @@ public void test_addHealthCheck_ThrowsAnException_WhenTheUrlIsInvalid() { } @Test - public void test_addHealthCheck_ThrowsAnException_WhenTheIntervalIsLessThanZero() { + void test_addHealthCheck_ThrowsAnException_WhenTheIntervalIsLessThanZero() { SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); try { @@ -166,7 +166,7 @@ public void test_addHealthCheck_ThrowsAnException_WhenTheIntervalIsLessThanZero( } @Test - public void test_addHealthCheck_ThrowsAnException_WhenTheTimeoutIsLessThanZero() { + void test_addHealthCheck_ThrowsAnException_WhenTheTimeoutIsLessThanZero() { SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); try { @@ -178,7 +178,7 @@ public void test_addHealthCheck_ThrowsAnException_WhenTheTimeoutIsLessThanZero() } @Test - public void test_getDeploymentGroups_WhenNoGroupsHaveBeenSpecified() { + void test_getDeploymentGroups_WhenNoGroupsHaveBeenSpecified() { SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); assertEquals(1, instance.getDeploymentGroups().size()); @@ -186,7 +186,7 @@ public void test_getDeploymentGroups_WhenNoGroupsHaveBeenSpecified() { } @Test - public void test_getDeploymentGroups_WhenOneGroupHasBeenSpecified() { + void test_getDeploymentGroups_WhenOneGroupHasBeenSpecified() { SoftwareSystemInstance instance = deploymentNode.add(softwareSystem, "Group 1"); assertEquals(1, instance.getDeploymentGroups().size()); @@ -194,7 +194,7 @@ public void test_getDeploymentGroups_WhenOneGroupHasBeenSpecified() { } @Test - public void test_getDeploymentGroups_WhenMultipleGroupsAreSpecified() { + void test_getDeploymentGroups_WhenMultipleGroupsAreSpecified() { SoftwareSystemInstance instance = deploymentNode.add(softwareSystem, "Group 1", "Group 2"); assertEquals(2, instance.getDeploymentGroups().size()); diff --git a/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemTests.java b/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemTests.java index 9d7e020bb..415172f98 100644 --- a/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemTests.java @@ -1,28 +1,32 @@ package com.structurizr.model; import com.structurizr.AbstractWorkspaceTestBase; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Iterator; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class SoftwareSystemTests extends AbstractWorkspaceTestBase { private SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "Name", "Description"); - @Test(expected = IllegalArgumentException.class) - public void test_addContainer_ThrowsAnException_WhenANullNameIsSpecified() { - softwareSystem.addContainer(null, "", ""); + @Test + void test_addContainer_ThrowsAnException_WhenANullNameIsSpecified() { + assertThrows(IllegalArgumentException.class, () -> { + softwareSystem.addContainer(null, "", ""); + }); } - @Test(expected = IllegalArgumentException.class) - public void test_addContainer_ThrowsAnException_WhenAnEmptyNameIsSpecified() { - softwareSystem.addContainer(" ", "", ""); + @Test + void test_addContainer_ThrowsAnException_WhenAnEmptyNameIsSpecified() { + assertThrows(IllegalArgumentException.class, () -> { + softwareSystem.addContainer(" ", "", ""); + }); } @Test - public void test_addContainer_AddsAContainer_WhenAContainerWithTheSameNameDoesNotExist() { + void test_addContainer_AddsAContainer_WhenAContainerWithTheSameNameDoesNotExist() { Container container = softwareSystem.addContainer("Web Application", "Description", "Spring MVC"); assertEquals("Web Application", container.getName()); assertEquals("Description", container.getDescription()); @@ -33,7 +37,7 @@ public void test_addContainer_AddsAContainer_WhenAContainerWithTheSameNameDoesNo } @Test - public void test_addContainer_ThrowsAnException_WhenAContainerWithTheSameNameAlreadyExists() { + void test_addContainer_ThrowsAnException_WhenAContainerWithTheSameNameAlreadyExists() { Container container = softwareSystem.addContainer("Web Application", "Description", "Spring MVC"); assertEquals(1, softwareSystem.getContainers().size()); @@ -46,29 +50,29 @@ public void test_addContainer_ThrowsAnException_WhenAContainerWithTheSameNameAlr } @Test - public void test_getContainerWithName_ReturnsNull_WhenAContainerWithTheSpecifiedNameDoesNotExist() { + void test_getContainerWithName_ReturnsNull_WhenAContainerWithTheSpecifiedNameDoesNotExist() { assertNull(softwareSystem.getContainerWithName("Web Application")); } @Test - public void test_GetContainerWithName_ReturnsAContainer_WhenAContainerWithTheSpecifiedNameDoesExist() { + void test_GetContainerWithName_ReturnsAContainer_WhenAContainerWithTheSpecifiedNameDoesExist() { Container container = softwareSystem.addContainer("Web Application", "Description", "Spring MVC"); assertSame(container, softwareSystem.getContainerWithName("Web Application")); } @Test - public void test_getContainerWithId_ReturnsNull_WhenAContainerWithTheSpecifiedIdDoesNotExist() { + void test_getContainerWithId_ReturnsNull_WhenAContainerWithTheSpecifiedIdDoesNotExist() { assertNull(softwareSystem.getContainerWithId("100")); } @Test - public void test_GetContainerWithId_ReturnsAContainer_WhenAContainerWithTheSpecifiedIdDoesExist() { + void test_GetContainerWithId_ReturnsAContainer_WhenAContainerWithTheSpecifiedIdDoesExist() { Container container = softwareSystem.addContainer("Web Application", "Description", "Spring MVC"); assertSame(container, softwareSystem.getContainerWithId(container.getId())); } @Test - public void test_uses_AddsAUnidirectionalRelationshipBetweenTwoSoftwareSystems() { + void test_uses_AddsAUnidirectionalRelationshipBetweenTwoSoftwareSystems() { SoftwareSystem systemA = model.addSoftwareSystem(Location.Internal, "System A", "Description"); SoftwareSystem systemB = model.addSoftwareSystem(Location.Internal, "System B", "Description"); systemA.uses(systemB, "Gets some data from"); @@ -82,7 +86,7 @@ public void test_uses_AddsAUnidirectionalRelationshipBetweenTwoSoftwareSystems() } @Test - public void test_uses_AddsAUnidirectionalRelationshipBetweenTwoSoftwareSystems_WhenADifferentRelationshipAlreadyExists() { + void test_uses_AddsAUnidirectionalRelationshipBetweenTwoSoftwareSystems_WhenADifferentRelationshipAlreadyExists() { SoftwareSystem systemA = model.addSoftwareSystem(Location.Internal, "System A", "Description"); SoftwareSystem systemB = model.addSoftwareSystem(Location.Internal, "System B", "Description"); systemA.uses(systemB, "Gets data using the REST API"); @@ -102,7 +106,7 @@ public void test_uses_AddsAUnidirectionalRelationshipBetweenTwoSoftwareSystems_W } @Test - public void test_uses_DoesNotAddAUnidirectionalRelationshipBetweenTwoSoftwareSystems_WhenTheSameRelationshipAlreadyExists() { + void test_uses_DoesNotAddAUnidirectionalRelationshipBetweenTwoSoftwareSystems_WhenTheSameRelationshipAlreadyExists() { SoftwareSystem systemA = model.addSoftwareSystem(Location.Internal, "System A", "Description"); SoftwareSystem systemB = model.addSoftwareSystem(Location.Internal, "System B", "Description"); systemA.uses(systemB, "Gets data using the REST API"); @@ -112,7 +116,7 @@ public void test_uses_DoesNotAddAUnidirectionalRelationshipBetweenTwoSoftwareSys } @Test - public void test_delivers_AddsAUnidirectionalRelationshipBetweenASoftwareSystemAndAPerson() { + void test_delivers_AddsAUnidirectionalRelationshipBetweenASoftwareSystemAndAPerson() { SoftwareSystem system = model.addSoftwareSystem(Location.Internal, "System", "Description"); Person person = model.addPerson(Location.Internal, "User", "Description"); system.delivers(person, "E-mails results to"); @@ -126,7 +130,7 @@ public void test_delivers_AddsAUnidirectionalRelationshipBetweenASoftwareSystemA } @Test - public void test_delivers_AddsAUnidirectionalRelationshipBetweenASoftwareSystemAndAPerson_WhenADifferentRelationshipAlreadyExists() { + void test_delivers_AddsAUnidirectionalRelationshipBetweenASoftwareSystemAndAPerson_WhenADifferentRelationshipAlreadyExists() { SoftwareSystem system = model.addSoftwareSystem(Location.Internal, "System", "Description"); Person person = model.addPerson(Location.Internal, "User", "Description"); system.delivers(person, "E-mails results to"); @@ -147,7 +151,7 @@ public void test_delivers_AddsAUnidirectionalRelationshipBetweenASoftwareSystemA } @Test - public void test_delivers_DoesNotAddAUnidirectionalRelationshipBetweenASoftwareSystemAndAPerson_WhenTheSameRelationshipAlreadyExists() { + void test_delivers_DoesNotAddAUnidirectionalRelationshipBetweenASoftwareSystemAndAPerson_WhenTheSameRelationshipAlreadyExists() { SoftwareSystem system = model.addSoftwareSystem(Location.Internal, "System", "Description"); Person person = model.addPerson(Location.Internal, "User", "Description"); system.delivers(person, "E-mails results to"); @@ -157,30 +161,30 @@ public void test_delivers_DoesNotAddAUnidirectionalRelationshipBetweenASoftwareS } @Test - public void test_getTags_IncludesSoftwareSystemByDefault() { + void test_getTags_IncludesSoftwareSystemByDefault() { SoftwareSystem system = model.addSoftwareSystem(Location.Internal, "System", "Description"); assertEquals("Element,Software System", system.getTags()); } @Test - public void test_getCanonicalName() { + void test_getCanonicalName() { SoftwareSystem system = model.addSoftwareSystem(Location.Internal, "System", "Description"); assertEquals("SoftwareSystem://System", system.getCanonicalName()); } @Test - public void test_getCanonicalName_WhenNameContainsSlashAndDotCharacters() { + void test_getCanonicalName_WhenNameContainsSlashAndDotCharacters() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Name1/.Name2", "Description"); assertEquals("SoftwareSystem://Name1Name2", softwareSystem.getCanonicalName()); } @Test - public void test_getParent_ReturnsNull() { + void test_getParent_ReturnsNull() { assertNull(softwareSystem.getParent()); } @Test - public void test_removeTags_DoesNotRemoveRequiredTags() { + void test_removeTags_DoesNotRemoveRequiredTags() { assertTrue(softwareSystem.getTags().contains(Tags.ELEMENT)); assertTrue(softwareSystem.getTags().contains(Tags.SOFTWARE_SYSTEM)); @@ -192,7 +196,7 @@ public void test_removeTags_DoesNotRemoveRequiredTags() { } @Test - public void test_getContainerWithName_ThrowsAnException_WhenANullNameIsSpecified() { + void test_getContainerWithName_ThrowsAnException_WhenANullNameIsSpecified() { try { softwareSystem.getContainerWithName(null); fail(); @@ -202,7 +206,7 @@ public void test_getContainerWithName_ThrowsAnException_WhenANullNameIsSpecified } @Test - public void test_getContainerWithName_ThrowsAnException_WhenAnEmptyNameIsSpecified() { + void test_getContainerWithName_ThrowsAnException_WhenAnEmptyNameIsSpecified() { try { softwareSystem.getContainerWithName(" "); fail(); @@ -212,7 +216,7 @@ public void test_getContainerWithName_ThrowsAnException_WhenAnEmptyNameIsSpecifi } @Test - public void test_getContainerWithId_ThrowsAnException_WhenANullIdIsSpecified() { + void test_getContainerWithId_ThrowsAnException_WhenANullIdIsSpecified() { try { softwareSystem.getContainerWithId(null); fail(); @@ -222,7 +226,7 @@ public void test_getContainerWithId_ThrowsAnException_WhenANullIdIsSpecified() { } @Test - public void test_getContainerWithId_ThrowsAnException_WhenAnEmptyIdIsSpecified() { + void test_getContainerWithId_ThrowsAnException_WhenAnEmptyIdIsSpecified() { try { softwareSystem.getContainerWithId(" "); fail(); diff --git a/structurizr-core/test/unit/com/structurizr/util/ImageUtilsTests.java b/structurizr-core/test/unit/com/structurizr/util/ImageUtilsTests.java index 0d5ee691b..2c6c753e6 100644 --- a/structurizr-core/test/unit/com/structurizr/util/ImageUtilsTests.java +++ b/structurizr-core/test/unit/com/structurizr/util/ImageUtilsTests.java @@ -1,15 +1,15 @@ package com.structurizr.util; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.File; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class ImageUtilsTests { @Test - public void test_getContentType_ThrowsAnException_WhenANullFileIsSpecified() throws Exception { + void test_getContentType_ThrowsAnException_WhenANullFileIsSpecified() throws Exception { try { ImageUtils.getContentType(null); fail(); @@ -19,7 +19,7 @@ public void test_getContentType_ThrowsAnException_WhenANullFileIsSpecified() thr } @Test - public void test_getContentType_ThrowsAnException_WhenAFileIsSpecifiedButItIsNotAFile() throws Exception { + void test_getContentType_ThrowsAnException_WhenAFileIsSpecifiedButItIsNotAFile() throws Exception { try { ImageUtils.getContentType(new File("../structurizr-core")); fail(); @@ -30,7 +30,7 @@ public void test_getContentType_ThrowsAnException_WhenAFileIsSpecifiedButItIsNot } @Test - public void test_getContentType_ThrowsAnException_WhenAFileIsSpecifiedButItIsNotAnImage() throws Exception { + void test_getContentType_ThrowsAnException_WhenAFileIsSpecifiedButItIsNotAnImage() throws Exception { try { ImageUtils.getContentType(new File("../build.gradle")); fail(); @@ -41,7 +41,7 @@ public void test_getContentType_ThrowsAnException_WhenAFileIsSpecifiedButItIsNot } @Test - public void test_getContentType_ThrowsAnException_WhenAFileIsSpecifiedButItDoesNotExist() throws Exception { + void test_getContentType_ThrowsAnException_WhenAFileIsSpecifiedButItDoesNotExist() throws Exception { try { ImageUtils.getContentType(new File("./foo.xml")); fail(); @@ -51,13 +51,13 @@ public void test_getContentType_ThrowsAnException_WhenAFileIsSpecifiedButItDoesN } @Test - public void test_getContentType_ReturnsTheContentType_WhenAFileIsSpecified() throws Exception { + void test_getContentType_ReturnsTheContentType_WhenAFileIsSpecified() throws Exception { String contentType = ImageUtils.getContentType(new File("../structurizr-core/test/unit/com/structurizr/util/structurizr-logo.png")); assertEquals("image/png", contentType); } @Test - public void test_getImageAsBase64_ThrowsAnException_WhenANullFileIsSpecified() throws Exception { + void test_getImageAsBase64_ThrowsAnException_WhenANullFileIsSpecified() throws Exception { try { ImageUtils.getImageAsBase64(null); fail(); @@ -67,7 +67,7 @@ public void test_getImageAsBase64_ThrowsAnException_WhenANullFileIsSpecified() t } @Test - public void test_getImageAsBase64_ThrowsAnException_WhenAFileIsSpecifiedButItIsNotAFile() throws Exception { + void test_getImageAsBase64_ThrowsAnException_WhenAFileIsSpecifiedButItIsNotAFile() throws Exception { try { ImageUtils.getImageAsBase64(new File("../structurizr-core")); fail(); @@ -78,7 +78,7 @@ public void test_getImageAsBase64_ThrowsAnException_WhenAFileIsSpecifiedButItIsN } @Test - public void test_getImageAsBase64_ThrowsAnException_WhenAFileIsSpecifiedButItIsNotAnImage() throws Exception { + void test_getImageAsBase64_ThrowsAnException_WhenAFileIsSpecifiedButItIsNotAnImage() throws Exception { try { ImageUtils.getImageAsBase64(new File("../build.gradle")); fail(); @@ -89,7 +89,7 @@ public void test_getImageAsBase64_ThrowsAnException_WhenAFileIsSpecifiedButItIsN } @Test - public void test_getImageAsBase64_ThrowsAnException_WhenAFileIsSpecifiedButItDoesNotExist() throws Exception { + void test_getImageAsBase64_ThrowsAnException_WhenAFileIsSpecifiedButItDoesNotExist() throws Exception { try { ImageUtils.getImageAsBase64(new File("./foo.xml")); fail(); @@ -99,13 +99,13 @@ public void test_getImageAsBase64_ThrowsAnException_WhenAFileIsSpecifiedButItDoe } @Test - public void test_getImageAsBase64_ReturnsTheImageAsABase64EncodedString_WhenAFileIsSpecified() throws Exception { + void test_getImageAsBase64_ReturnsTheImageAsABase64EncodedString_WhenAFileIsSpecified() throws Exception { String imageAsBase64 = ImageUtils.getImageAsBase64(new File("../structurizr-core/test/unit/com/structurizr/util/structurizr-logo.png")); assertTrue(imageAsBase64.startsWith("iVBORw0KGgoAAAANSUhEUgAAAMQAAADECAYAAADApo5rAAA")); // the actual base64 encoded string varies between Java 8 and 9 } @Test - public void test_getImageAsDataUri_ThrowsAnException_WhenANullFileIsSpecified() throws Exception { + void test_getImageAsDataUri_ThrowsAnException_WhenANullFileIsSpecified() throws Exception { try { ImageUtils.getImageAsDataUri(null); fail(); @@ -115,7 +115,7 @@ public void test_getImageAsDataUri_ThrowsAnException_WhenANullFileIsSpecified() } @Test - public void test_getImageAsDataUri_ThrowsAnException_WhenAFileIsSpecifiedButItIsNotAFile() throws Exception { + void test_getImageAsDataUri_ThrowsAnException_WhenAFileIsSpecifiedButItIsNotAFile() throws Exception { try { ImageUtils.getImageAsDataUri(new File("../structurizr-core")); fail(); @@ -126,7 +126,7 @@ public void test_getImageAsDataUri_ThrowsAnException_WhenAFileIsSpecifiedButItIs } @Test - public void test_getImageAsDataUri_ThrowsAnException_WhenAFileIsSpecifiedButItIsNotAnImage() throws Exception { + void test_getImageAsDataUri_ThrowsAnException_WhenAFileIsSpecifiedButItIsNotAnImage() throws Exception { try { ImageUtils.getImageAsDataUri(new File("../build.gradle")); fail(); @@ -137,7 +137,7 @@ public void test_getImageAsDataUri_ThrowsAnException_WhenAFileIsSpecifiedButItIs } @Test - public void test_getImageAsDataUri_ThrowsAnException_WhenAFileIsSpecifiedButItDoesNotExist() throws Exception { + void test_getImageAsDataUri_ThrowsAnException_WhenAFileIsSpecifiedButItDoesNotExist() throws Exception { try { ImageUtils.getImageAsDataUri(new File("./foo.xml")); fail(); @@ -147,14 +147,14 @@ public void test_getImageAsDataUri_ThrowsAnException_WhenAFileIsSpecifiedButItDo } @Test - public void test_getImageAsDataUri_ReturnsTheImageAsADataUri_WhenAFileIsSpecified() throws Exception { + void test_getImageAsDataUri_ReturnsTheImageAsADataUri_WhenAFileIsSpecified() throws Exception { String imageAsDataUri = ImageUtils.getImageAsDataUri(new File("../structurizr-core/test/unit/com/structurizr/util/structurizr-logo.png")); System.out.println(imageAsDataUri); assertTrue(imageAsDataUri.startsWith("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMQAAADECAYAAADA")); // the actual base64 encoded string varies between Java 8 and 9 } @Test - public void test_validateImage() { + void test_validateImage() { // allowed ImageUtils.validateImage("https://structurizr.com/image.png"); ImageUtils.validateImage("data:image/png;base64,iVBORw0KGg"); @@ -170,7 +170,7 @@ public void test_validateImage() { } @Test - public void test_isSupportedDataUri() { + void test_isSupportedDataUri() { assertTrue(ImageUtils.isSupportedDataUri("data:image/png;base64,iVBORw0KGg")); assertTrue(ImageUtils.isSupportedDataUri("data:image/jpeg;base64,iVBORw0KGg")); assertFalse(ImageUtils.isSupportedDataUri("data:image/svg+xml;base64,iVBORw0KGg")); diff --git a/structurizr-core/test/unit/com/structurizr/util/StringUtilsTests.java b/structurizr-core/test/unit/com/structurizr/util/StringUtilsTests.java index 6f7b9532c..98bf0a7f6 100644 --- a/structurizr-core/test/unit/com/structurizr/util/StringUtilsTests.java +++ b/structurizr-core/test/unit/com/structurizr/util/StringUtilsTests.java @@ -1,25 +1,25 @@ package com.structurizr.util; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; public class StringUtilsTests { @Test - public void test_isNullOrEmpty_ReturnsTrue_WhenPassedNull() { + void test_isNullOrEmpty_ReturnsTrue_WhenPassedNull() { assertTrue(StringUtils.isNullOrEmpty(null)); } @Test - public void test_isNullOrEmpty_ReturnsTrue_WhenPassedAnEmptyString() { + void test_isNullOrEmpty_ReturnsTrue_WhenPassedAnEmptyString() { assertTrue(StringUtils.isNullOrEmpty("")); assertTrue(StringUtils.isNullOrEmpty(" ")); } @Test - public void test_isNullOrEmpty_ReturnsFalse_WhenPassedANonEmptyString() { + void test_isNullOrEmpty_ReturnsFalse_WhenPassedANonEmptyString() { assertFalse(StringUtils.isNullOrEmpty("Hello World!")); } diff --git a/structurizr-core/test/unit/com/structurizr/util/UrlTests.java b/structurizr-core/test/unit/com/structurizr/util/UrlTests.java index 6669463ba..70ba4f6db 100644 --- a/structurizr-core/test/unit/com/structurizr/util/UrlTests.java +++ b/structurizr-core/test/unit/com/structurizr/util/UrlTests.java @@ -1,30 +1,30 @@ package com.structurizr.util; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; public class UrlTests { @Test - public void test_isUrl_ReturnsFalse_WhenPassedNull() { + void test_isUrl_ReturnsFalse_WhenPassedNull() { assertFalse(Url.isUrl(null)); } @Test - public void test_isUrl_ReturnsFalse_WhenPassedAnEmptyString() { + void test_isUrl_ReturnsFalse_WhenPassedAnEmptyString() { assertFalse(Url.isUrl("")); assertFalse(Url.isUrl(" ")); } @Test - public void test_isUrl_ReturnsFalse_WhenPassedAnInvalidUrl() { + void test_isUrl_ReturnsFalse_WhenPassedAnInvalidUrl() { assertFalse(Url.isUrl("www.google.com")); } @Test - public void test_isUrl_ReturnsTrue_WhenPassedAValidUrl() { + void test_isUrl_ReturnsTrue_WhenPassedAValidUrl() { assertTrue(Url.isUrl("https://www.google.com")); } diff --git a/structurizr-core/test/unit/com/structurizr/view/AutomaticLayoutTests.java b/structurizr-core/test/unit/com/structurizr/view/AutomaticLayoutTests.java index 2ae1d4e7e..cb2efd64c 100644 --- a/structurizr-core/test/unit/com/structurizr/view/AutomaticLayoutTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/AutomaticLayoutTests.java @@ -1,13 +1,13 @@ package com.structurizr.view; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class AutomaticLayoutTests { @Test - public void test_setAutomaticLayout() { + void test_setAutomaticLayout() { AutomaticLayout automaticLayout = new AutomaticLayout(AutomaticLayout.Implementation.Dagre, AutomaticLayout.RankDirection.LeftRight, 100, 200, 300, true); assertEquals(AutomaticLayout.RankDirection.LeftRight, automaticLayout.getRankDirection()); @@ -18,7 +18,7 @@ public void test_setAutomaticLayout() { } @Test - public void test_setRankDirection_ThrowsAnException_WhenNullIsSpecified() { + void test_setRankDirection_ThrowsAnException_WhenNullIsSpecified() { try { AutomaticLayout automaticLayout = new AutomaticLayout(); automaticLayout.setRankDirection(null); @@ -29,7 +29,7 @@ public void test_setRankDirection_ThrowsAnException_WhenNullIsSpecified() { } @Test - public void test_setRankSeparation_ThrowsAnException_WhenANegativeIntegerIsSpecified() { + void test_setRankSeparation_ThrowsAnException_WhenANegativeIntegerIsSpecified() { try { AutomaticLayout automaticLayout = new AutomaticLayout(); automaticLayout.setRankSeparation(-100); @@ -40,7 +40,7 @@ public void test_setRankSeparation_ThrowsAnException_WhenANegativeIntegerIsSpeci } @Test - public void test_setNodeSeparation_ThrowsAnException_WhenANegativeIntegerIsSpecified() { + void test_setNodeSeparation_ThrowsAnException_WhenANegativeIntegerIsSpecified() { try { AutomaticLayout automaticLayout = new AutomaticLayout(); automaticLayout.setNodeSeparation(-100); @@ -51,7 +51,7 @@ public void test_setNodeSeparation_ThrowsAnException_WhenANegativeIntegerIsSpeci } @Test - public void test_setEdgeSeparation_ThrowsAnException_WhenANegativeIntegerIsSpecified() { + void test_setEdgeSeparation_ThrowsAnException_WhenANegativeIntegerIsSpecified() { try { AutomaticLayout automaticLayout = new AutomaticLayout(); automaticLayout.setEdgeSeparation(-100); diff --git a/structurizr-core/test/unit/com/structurizr/view/BrandingTests.java b/structurizr-core/test/unit/com/structurizr/view/BrandingTests.java index 9d07a52c5..dc01bf8b4 100644 --- a/structurizr-core/test/unit/com/structurizr/view/BrandingTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/BrandingTests.java @@ -1,51 +1,52 @@ package com.structurizr.view; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.*; public class BrandingTests { private Branding branding; - @Before + @BeforeEach public void setUp() { this.branding = new Branding(); } @Test - public void test_setLogo_WithAUrl() { + void test_setLogo_WithAUrl() { branding.setLogo("https://structurizr.com/static/img/structurizr-logo.png"); assertEquals("https://structurizr.com/static/img/structurizr-logo.png", branding.getLogo()); } @Test - public void test_setLogo_WithAUrlThatHasATrailingSpace() { + void test_setLogo_WithAUrlThatHasATrailingSpace() { branding.setLogo("https://structurizr.com/static/img/structurizr-logo.png "); assertEquals("https://structurizr.com/static/img/structurizr-logo.png", branding.getLogo()); } @Test - public void test_setLogo_WithADataUri() { + void test_setLogo_WithADataUri() { branding.setLogo("data:image/png;base64,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"); assertEquals("data:image/png;base64,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", branding.getLogo()); } - @Test(expected = IllegalArgumentException.class) - public void test_setLogo_ThrowsAnIllegalArgumentException_WhenAnInvalidUrlIsSpecified() { - branding.setLogo("htt://blah"); + @Test + void test_setLogo_ThrowsAnIllegalArgumentException_WhenAnInvalidUrlIsSpecified() { + assertThrows(IllegalArgumentException.class, () -> { + branding.setLogo("htt://blah"); + }); } @Test - public void test_setLogo_DoesNothing_WhenANullUrlIsSpecified() { + void test_setLogo_DoesNothing_WhenANullUrlIsSpecified() { branding.setLogo(null); assertNull(branding.getLogo()); } @Test - public void test_setLogo_DoesNothing_WhenAnEmptyUrlIsSpecified() { + void test_setLogo_DoesNothing_WhenAnEmptyUrlIsSpecified() { branding.setLogo(" "); assertNull(branding.getLogo()); } diff --git a/structurizr-core/test/unit/com/structurizr/view/ColorPairTests.java b/structurizr-core/test/unit/com/structurizr/view/ColorPairTests.java index efc760b82..f85ec7794 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ColorPairTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ColorPairTests.java @@ -1,28 +1,28 @@ package com.structurizr.view; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; public class ColorPairTests { @Test - public void test_construction() { + void test_construction() { ColorPair colorPair = new ColorPair("#ffffff", "#000000"); assertEquals("#ffffff", colorPair.getBackground()); assertEquals("#000000", colorPair.getForeground()); } @Test - public void test_setBackground_WithAValidHtmlColorCode() { + void test_setBackground_WithAValidHtmlColorCode() { ColorPair colorPair = new ColorPair(); colorPair.setBackground("#ffffff"); assertEquals("#ffffff", colorPair.getBackground()); } @Test - public void test_setBackground_ThrowsAnException_WhenANullHtmlColorCodeIsSpecified() { + void test_setBackground_ThrowsAnException_WhenANullHtmlColorCodeIsSpecified() { try { ColorPair colorPair = new ColorPair(); colorPair.setBackground(null); @@ -33,7 +33,7 @@ public void test_setBackground_ThrowsAnException_WhenANullHtmlColorCodeIsSpecifi } @Test - public void test_setBackground_ThrowsAnException_WhenAnEmptyHtmlColorCodeIsSpecified() { + void test_setBackground_ThrowsAnException_WhenAnEmptyHtmlColorCodeIsSpecified() { try { ColorPair colorPair = new ColorPair(); colorPair.setBackground(""); @@ -44,7 +44,7 @@ public void test_setBackground_ThrowsAnException_WhenAnEmptyHtmlColorCodeIsSpeci } @Test - public void test_setBackground_ThrowsAnException_WhenAnInvalidHtmlColorCodeIsSpecified() { + void test_setBackground_ThrowsAnException_WhenAnInvalidHtmlColorCodeIsSpecified() { try { ColorPair colorPair = new ColorPair(); colorPair.setBackground("ffffff"); @@ -55,14 +55,14 @@ public void test_setBackground_ThrowsAnException_WhenAnInvalidHtmlColorCodeIsSpe } @Test - public void test_setForeground_WithAValidHtmlColorCode() { + void test_setForeground_WithAValidHtmlColorCode() { ColorPair colorPair = new ColorPair(); colorPair.setForeground("#000000"); assertEquals("#000000", colorPair.getForeground()); } @Test - public void test_setForeground_ThrowsAnException_WhenANullHtmlColorCodeIsSpecified() { + void test_setForeground_ThrowsAnException_WhenANullHtmlColorCodeIsSpecified() { try { ColorPair colorPair = new ColorPair(); colorPair.setForeground(null); @@ -73,7 +73,7 @@ public void test_setForeground_ThrowsAnException_WhenANullHtmlColorCodeIsSpecifi } @Test - public void test_setForeground_ThrowsAnException_WhenAnEmptyHtmlColorCodeIsSpecified() { + void test_setForeground_ThrowsAnException_WhenAnEmptyHtmlColorCodeIsSpecified() { try { ColorPair colorPair = new ColorPair(); colorPair.setForeground(""); @@ -84,7 +84,7 @@ public void test_setForeground_ThrowsAnException_WhenAnEmptyHtmlColorCodeIsSpeci } @Test - public void test_setForeground_ThrowsAnException_WhenAnInvalidHtmlColorCodeIsSpecified() { + void test_setForeground_ThrowsAnException_WhenAnInvalidHtmlColorCodeIsSpecified() { try { ColorPair colorPair = new ColorPair(); colorPair.setForeground("000000"); diff --git a/structurizr-core/test/unit/com/structurizr/view/ColorTests.java b/structurizr-core/test/unit/com/structurizr/view/ColorTests.java index 074f8170d..c76263ce2 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ColorTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ColorTests.java @@ -1,31 +1,31 @@ package com.structurizr.view; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; public class ColorTests { @Test - public void test_isHexColorCode_ReturnsFalse_WhenPassedNull() { + void test_isHexColorCode_ReturnsFalse_WhenPassedNull() { assertFalse(Color.isHexColorCode(null)); } @Test - public void test_isHexColorCode_ReturnsFalse_WhenPassedAnEmptyString() { + void test_isHexColorCode_ReturnsFalse_WhenPassedAnEmptyString() { assertFalse(Color.isHexColorCode("")); } @Test - public void test_isHexColorCode_ReturnsFalse_WhenPassedAnInvalidString() { + void test_isHexColorCode_ReturnsFalse_WhenPassedAnInvalidString() { assertFalse(Color.isHexColorCode("ffffff")); assertFalse(Color.isHexColorCode("#fffff")); assertFalse(Color.isHexColorCode("#gggggg")); } @Test - public void test_isHexColorCode_ReturnsTrue_WhenPassedAnValidString() { + void test_isHexColorCode_ReturnsTrue_WhenPassedAnValidString() { assertTrue(Color.isHexColorCode("#abcdef")); assertTrue(Color.isHexColorCode("#ABCDEF")); assertTrue(Color.isHexColorCode("#123456")); diff --git a/structurizr-core/test/unit/com/structurizr/view/ComponentViewTests.java b/structurizr-core/test/unit/com/structurizr/view/ComponentViewTests.java index 0527ad3e4..eaeeb4300 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ComponentViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ComponentViewTests.java @@ -2,13 +2,13 @@ import com.structurizr.AbstractWorkspaceTestBase; import com.structurizr.model.*; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.util.Set; import java.util.stream.Collectors; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class ComponentViewTests extends AbstractWorkspaceTestBase { @@ -16,7 +16,7 @@ public class ComponentViewTests extends AbstractWorkspaceTestBase { private Container webApplication; private ComponentView view; - @Before + @BeforeEach public void setUp() { softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); webApplication = softwareSystem.addContainer("Web Application", "Does something", "Apache Tomcat"); @@ -24,7 +24,7 @@ public void setUp() { } @Test - public void test_construction() { + void test_construction() { assertEquals("The System - Web Application - Components", view.getName()); assertEquals("Some description", view.getDescription()); assertEquals(0, view.getElements().size()); @@ -35,14 +35,14 @@ public void test_construction() { } @Test - public void test_addAllSoftwareSystems_DoesNothing_WhenThereAreNoOtherSoftwareSystems() { + void test_addAllSoftwareSystems_DoesNothing_WhenThereAreNoOtherSoftwareSystems() { assertEquals(0, view.getElements().size()); view.addAllSoftwareSystems(); assertEquals(0, view.getElements().size()); } @Test - public void test_addAllSoftwareSystems_AddsAllSoftwareSystems_WhenThereAreSomeSoftwareSystemsInTheModel() { + void test_addAllSoftwareSystems_AddsAllSoftwareSystems_WhenThereAreSomeSoftwareSystemsInTheModel() { SoftwareSystem softwareSystemA = model.addSoftwareSystem(Location.External, "System A", "Description"); SoftwareSystem softwareSystemB = model.addSoftwareSystem(Location.External, "System B", "Description"); @@ -54,14 +54,14 @@ public void test_addAllSoftwareSystems_AddsAllSoftwareSystems_WhenThereAreSomeSo } @Test - public void test_addAllPeople_DoesNothing_WhenThereAreNoPeople() { + void test_addAllPeople_DoesNothing_WhenThereAreNoPeople() { assertEquals(0, view.getElements().size()); view.addAllPeople(); assertEquals(0, view.getElements().size()); } @Test - public void test_addAllPeople_AddsAllPeople_WhenThereAreSomePeopleInTheModel() { + void test_addAllPeople_AddsAllPeople_WhenThereAreSomePeopleInTheModel() { Person userA = model.addPerson(Location.External, "User A", "Description"); Person userB = model.addPerson(Location.External, "User B", "Description"); @@ -73,14 +73,14 @@ public void test_addAllPeople_AddsAllPeople_WhenThereAreSomePeopleInTheModel() { } @Test - public void test_addAllElements_DoesNothing_WhenThereAreNoSoftwareSystemsOrPeople() { + void test_addAllElements_DoesNothing_WhenThereAreNoSoftwareSystemsOrPeople() { assertEquals(0, view.getElements().size()); view.addAllElements(); assertEquals(0, view.getElements().size()); } @Test - public void test_addAllElements_AddsAllSoftwareSystemsAndPeopleAndContainersAndComponents_WhenThereAreSomeSoftwareSystemsAndPeopleAndContainersAndComponentsInTheModel() { + void test_addAllElements_AddsAllSoftwareSystemsAndPeopleAndContainersAndComponents_WhenThereAreSomeSoftwareSystemsAndPeopleAndContainersAndComponentsInTheModel() { SoftwareSystem softwareSystemA = model.addSoftwareSystem(Location.External, "System A", "Description"); SoftwareSystem softwareSystemB = model.addSoftwareSystem(Location.External, "System B", "Description"); Person userA = model.addPerson(Location.External, "User A", "Description"); @@ -102,14 +102,14 @@ public void test_addAllElements_AddsAllSoftwareSystemsAndPeopleAndContainersAndC } @Test - public void test_addAllContainers_DoesNothing_WhenThereAreNoContainers() { + void test_addAllContainers_DoesNothing_WhenThereAreNoContainers() { assertEquals(0, view.getElements().size()); view.addAllContainers(); assertEquals(0, view.getElements().size()); } @Test - public void test_addAllContainers_AddsAllContainers_WhenThereAreSomeContainers() { + void test_addAllContainers_AddsAllContainers_WhenThereAreSomeContainers() { Container database = softwareSystem.addContainer("Database", "Stores something", "MySQL"); Container fileSystem = softwareSystem.addContainer("File System", "Stores something else", ""); @@ -121,14 +121,14 @@ public void test_addAllContainers_AddsAllContainers_WhenThereAreSomeContainers() } @Test - public void test_addAllComponents_DoesNothing_WhenThereAreNoComponents() { + void test_addAllComponents_DoesNothing_WhenThereAreNoComponents() { assertEquals(0, view.getElements().size()); view.addAllComponents(); assertEquals(0, view.getElements().size()); } @Test - public void test_addAllComponents_AddsAllComponents_WhenThereAreSomeComponents() { + void test_addAllComponents_AddsAllComponents_WhenThereAreSomeComponents() { Component componentA = webApplication.addComponent("Component A", "Does something", "Java"); Component componentB = webApplication.addComponent("Component B", "Does something", "Java"); @@ -140,7 +140,7 @@ public void test_addAllComponents_AddsAllComponents_WhenThereAreSomeComponents() } @Test - public void test_add_ThrowsAnException_WhenANullContainerIsSpecified() { + void test_add_ThrowsAnException_WhenANullContainerIsSpecified() { assertEquals(0, view.getElements().size()); try { @@ -152,7 +152,7 @@ public void test_add_ThrowsAnException_WhenANullContainerIsSpecified() { } @Test - public void test_add_AddsTheContainer_WhenTheContainerIsNoInTheViewAlready() { + void test_add_AddsTheContainer_WhenTheContainerIsNoInTheViewAlready() { Container database = softwareSystem.addContainer("Database", "Stores something", "MySQL"); assertEquals(0, view.getElements().size()); @@ -162,7 +162,7 @@ public void test_add_AddsTheContainer_WhenTheContainerIsNoInTheViewAlready() { } @Test - public void test_add_DoesNothing_WhenTheSpecifiedContainerIsAlreadyInTheView() { + void test_add_DoesNothing_WhenTheSpecifiedContainerIsAlreadyInTheView() { Container database = softwareSystem.addContainer("Database", "Stores something", "MySQL"); view.add(database); assertEquals(1, view.getElements().size()); @@ -173,9 +173,9 @@ public void test_add_DoesNothing_WhenTheSpecifiedContainerIsAlreadyInTheView() { } @Test - public void test_remove_ThrowsAndException_WhenANullContainerIsPassed() { + void test_remove_ThrowsAndException_WhenANullContainerIsPassed() { try { - view.remove((Container)null); + view.remove((Container) null); fail(); } catch (IllegalArgumentException iae) { assertEquals("An element must be specified.", iae.getMessage()); @@ -183,7 +183,7 @@ public void test_remove_ThrowsAndException_WhenANullContainerIsPassed() { } @Test - public void test_remove_RemovesTheContainer_WhenTheContainerIsInTheView() { + void test_remove_RemovesTheContainer_WhenTheContainerIsInTheView() { Container database = softwareSystem.addContainer("Database", "Stores something", "MySQL"); view.add(database); assertEquals(1, view.getElements().size()); @@ -194,7 +194,7 @@ public void test_remove_RemovesTheContainer_WhenTheContainerIsInTheView() { } @Test - public void test_remove_DoesNothing_WhenTheContainerIsNotInTheView() { + void test_remove_DoesNothing_WhenTheContainerIsNotInTheView() { Container database = softwareSystem.addContainer("Database", "Stores something", "MySQL"); Container fileSystem = softwareSystem.addContainer("File System", "Stores something else", ""); @@ -208,14 +208,14 @@ public void test_remove_DoesNothing_WhenTheContainerIsNotInTheView() { } @Test - public void test_add_DoesNothing_WhenANullComponentIsSpecified() { + void test_add_DoesNothing_WhenANullComponentIsSpecified() { assertEquals(0, view.getElements().size()); view.add((Component) null); assertEquals(0, view.getElements().size()); } @Test - public void test_add_AddsTheComponent_WhenTheComponentIsNotInTheViewAlready() { + void test_add_AddsTheComponent_WhenTheComponentIsNotInTheViewAlready() { Component componentA = webApplication.addComponent("Component A", "Does something", "Java"); assertEquals(0, view.getElements().size()); @@ -225,7 +225,7 @@ public void test_add_AddsTheComponent_WhenTheComponentIsNotInTheViewAlready() { } @Test - public void test_add_DoesNothing_WhenTheSpecifiedComponentIsAlreadyInTheView() { + void test_add_DoesNothing_WhenTheSpecifiedComponentIsAlreadyInTheView() { Component componentA = webApplication.addComponent("Component A", "Does something", "Java"); view.add(componentA); assertEquals(1, view.getElements().size()); @@ -236,7 +236,7 @@ public void test_add_DoesNothing_WhenTheSpecifiedComponentIsAlreadyInTheView() { } @Test - public void test_add_ThrowsAnException_WhenTheSpecifiedComponentIsInADifferentContainer() { + void test_add_ThrowsAnException_WhenTheSpecifiedComponentIsInADifferentContainer() { try { SoftwareSystem softwareSystemA = model.addSoftwareSystem("System A", "Description"); @@ -253,7 +253,7 @@ public void test_add_ThrowsAnException_WhenTheSpecifiedComponentIsInADifferentCo } @Test - public void test_add_ThrowsAnException_WhenTheContainerOfTheViewIsAdded() { + void test_add_ThrowsAnException_WhenTheContainerOfTheViewIsAdded() { try { view.add(webApplication); fail(); @@ -263,20 +263,20 @@ public void test_add_ThrowsAnException_WhenTheContainerOfTheViewIsAdded() { } @Test - public void test_add_DoesNothing_WhenTheContainerOfTheViewIsAddedViaDependency() { + void test_add_DoesNothing_WhenTheContainerOfTheViewIsAddedViaDependency() { final SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.External, "Some other system", "external system that uses our web application"); final Relationship relationshipFromExternalSystem = softwareSystem.uses(webApplication, ""); - assertEquals("the container itself is not added to the view", 0, view.getElements().stream().map(e -> e.getElement()).filter(e -> e.equals(webApplication)).count()); + assertEquals(0, view.getElements().stream().map(e -> e.getElement()).filter(e -> e.equals(webApplication)).count(), "the container itself is not added to the view"); view.add(relationshipFromExternalSystem); - assertEquals("the container itself is not added to the view", 0, view.getElements().stream().map(e -> e.getElement()).filter(e -> e.equals(webApplication)).count()); + assertEquals(0, view.getElements().stream().map(e -> e.getElement()).filter(e -> e.equals(webApplication)).count(), "the container itself is not added to the view"); } @Test - public void test_remove_DoesNothing_WhenANullComponentIsPassed() { + void test_remove_DoesNothing_WhenANullComponentIsPassed() { try { - view.remove((Component)null); + view.remove((Component) null); fail(); } catch (IllegalArgumentException iae) { assertEquals("An element must be specified.", iae.getMessage()); @@ -284,7 +284,7 @@ public void test_remove_DoesNothing_WhenANullComponentIsPassed() { } @Test - public void test_remove_RemovesTheComponent_WhenTheComponentIsInTheView() { + void test_remove_RemovesTheComponent_WhenTheComponentIsInTheView() { Component componentA = webApplication.addComponent("Component A", "Does something", "Java"); view.add(componentA); assertEquals(1, view.getElements().size()); @@ -295,7 +295,7 @@ public void test_remove_RemovesTheComponent_WhenTheComponentIsInTheView() { } @Test - public void test_remove_RemovesTheComponentAndRelationships_WhenTheComponentIsInTheViewAndHasArelationshipToAnotherElement() { + void test_remove_RemovesTheComponentAndRelationships_WhenTheComponentIsInTheViewAndHasArelationshipToAnotherElement() { Component componentA = webApplication.addComponent("Component A", "Does something", "Java"); Component componentB = webApplication.addComponent("Component B", "Does something", "Java"); componentA.uses(componentB, "uses"); @@ -311,7 +311,7 @@ public void test_remove_RemovesTheComponentAndRelationships_WhenTheComponentIsIn } @Test - public void test_remove_DoesNothing_WhenTheComponentIsNotInTheView() { + void test_remove_DoesNothing_WhenTheComponentIsNotInTheView() { Component componentA = webApplication.addComponent("Component A", "Does something", "Java"); Component componentB = webApplication.addComponent("Component B", "Does something", "Java"); @@ -325,14 +325,14 @@ public void test_remove_DoesNothing_WhenTheComponentIsNotInTheView() { } @Test - public void test_addNearestNeightbours_DoesNothing_WhenANullElementIsSpecified() { + void test_addNearestNeightbours_DoesNothing_WhenANullElementIsSpecified() { view.addNearestNeighbours(null); assertEquals(0, view.getElements().size()); } @Test - public void test_addNearestNeighbours_DoesNothing_WhenThereAreNoNeighbours() { + void test_addNearestNeighbours_DoesNothing_WhenThereAreNoNeighbours() { Component component = webApplication.addComponent("Component", "", ""); view.add(component); assertEquals(1, view.getElements().size()); @@ -342,7 +342,7 @@ public void test_addNearestNeighbours_DoesNothing_WhenThereAreNoNeighbours() { } @Test - public void test_addNearestNeighbours_AddsNearestNeighbours_WhenThereAreSomeNearestNeighbours() { + void test_addNearestNeighbours_AddsNearestNeighbours_WhenThereAreSomeNearestNeighbours() { SoftwareSystem softwareSystemA = model.addSoftwareSystem("System A", "Description"); SoftwareSystem softwareSystemB = model.addSoftwareSystem("System B", "Description"); Person userA = model.addPerson("User A", "Description"); @@ -400,7 +400,7 @@ public void test_addNearestNeighbours_AddsNearestNeighbours_WhenThereAreSomeNear } @Test - public void test_addExternalDependencies_AddsOrphanedElements_WhenThereAreNoDirectRelationshipsWithAComponent() { + void test_addExternalDependencies_AddsOrphanedElements_WhenThereAreNoDirectRelationshipsWithAComponent() { SoftwareSystem source = model.addSoftwareSystem("Source", ""); SoftwareSystem destination = model.addSoftwareSystem("Destination", ""); @@ -424,7 +424,7 @@ public void test_addExternalDependencies_AddsOrphanedElements_WhenThereAreNoDire } @Test - public void test_addExternalDependencies_AddsTheContainer_WhenAComponentHasARelationshipToAContainerInTheSameSoftwareSystem() { + void test_addExternalDependencies_AddsTheContainer_WhenAComponentHasARelationshipToAContainerInTheSameSoftwareSystem() { SoftwareSystem softwareSystemA = model.addSoftwareSystem("Software System A", ""); Container containerA = softwareSystemA.addContainer("Container A", "", ""); Component componentA = containerA.addComponent("Component A", "", ""); @@ -441,7 +441,7 @@ public void test_addExternalDependencies_AddsTheContainer_WhenAComponentHasARela } @Test - public void test_addExternalDependencies_AddsTheContainer_WhenAComponentHasARelationshipFromAContainerInTheSameSoftwareSystem() { + void test_addExternalDependencies_AddsTheContainer_WhenAComponentHasARelationshipFromAContainerInTheSameSoftwareSystem() { SoftwareSystem softwareSystemA = model.addSoftwareSystem("Software System A", ""); Container containerA = softwareSystemA.addContainer("Container A", "", ""); Component componentA = containerA.addComponent("Component A", "", ""); @@ -458,7 +458,7 @@ public void test_addExternalDependencies_AddsTheContainer_WhenAComponentHasARela } @Test - public void test_addExternalDependencies_AddsTheParentContainer_WhenAComponentHasARelationshipToAComponentInADifferentContainerInTheSameSoftwareSystem() { + void test_addExternalDependencies_AddsTheParentContainer_WhenAComponentHasARelationshipToAComponentInADifferentContainerInTheSameSoftwareSystem() { SoftwareSystem softwareSystemA = model.addSoftwareSystem("Software System A", ""); Container containerA = softwareSystemA.addContainer("Container A", "", ""); Component componentA = containerA.addComponent("Component A", "", ""); @@ -476,7 +476,7 @@ public void test_addExternalDependencies_AddsTheParentContainer_WhenAComponentHa } @Test - public void test_addExternalDependencies_AddsTheParentContainer_WhenAComponentHasARelationshipFromAComponentInADifferentContainerInTheSameSoftwareSystem() { + void test_addExternalDependencies_AddsTheParentContainer_WhenAComponentHasARelationshipFromAComponentInADifferentContainerInTheSameSoftwareSystem() { SoftwareSystem softwareSystemA = model.addSoftwareSystem("Software System A", ""); Container containerA = softwareSystemA.addContainer("Container A", "", ""); Component componentA = containerA.addComponent("Component A", "", ""); @@ -494,7 +494,7 @@ public void test_addExternalDependencies_AddsTheParentContainer_WhenAComponentHa } @Test - public void test_addExternalDependencies_AddsTheParentSoftwareSystem_WhenAComponentHasARelationshipToAContainerInAnotherSoftwareSystem() { + void test_addExternalDependencies_AddsTheParentSoftwareSystem_WhenAComponentHasARelationshipToAContainerInAnotherSoftwareSystem() { SoftwareSystem softwareSystemA = model.addSoftwareSystem("Software System A", ""); Container containerA = softwareSystemA.addContainer("Container A", "", ""); Component componentA = containerA.addComponent("Component A", "", ""); @@ -512,7 +512,7 @@ public void test_addExternalDependencies_AddsTheParentSoftwareSystem_WhenACompon } @Test - public void test_addExternalDependencies_AddsTheParentSoftwareSystem_WhenAComponentHasARelationshipFromAContainerInAnotherSoftwareSystem() { + void test_addExternalDependencies_AddsTheParentSoftwareSystem_WhenAComponentHasARelationshipFromAContainerInAnotherSoftwareSystem() { SoftwareSystem softwareSystemA = model.addSoftwareSystem("Software System A", ""); Container containerA = softwareSystemA.addContainer("Container A", "", ""); Component componentA = containerA.addComponent("Component A", "", ""); @@ -530,7 +530,7 @@ public void test_addExternalDependencies_AddsTheParentSoftwareSystem_WhenACompon } @Test - public void test_addExternalDependencies_AddsTheParentSoftwareSystem_WhenAComponentHasARelationshipToAComponentInAnotherSoftwareSystem() { + void test_addExternalDependencies_AddsTheParentSoftwareSystem_WhenAComponentHasARelationshipToAComponentInAnotherSoftwareSystem() { SoftwareSystem softwareSystemA = model.addSoftwareSystem("Software System A", ""); Container containerA = softwareSystemA.addContainer("Container A", "", ""); Component componentA = containerA.addComponent("Component A", "", ""); @@ -549,7 +549,7 @@ public void test_addExternalDependencies_AddsTheParentSoftwareSystem_WhenACompon } @Test - public void test_addExternalDependencies_AddsTheParentSoftwareSystem_WhenAComponentHasARelationshipFromAComponentInAnotherSoftwareSystem() { + void test_addExternalDependencies_AddsTheParentSoftwareSystem_WhenAComponentHasARelationshipFromAComponentInAnotherSoftwareSystem() { SoftwareSystem softwareSystemA = model.addSoftwareSystem("Software System A", ""); Container containerA = softwareSystemA.addContainer("Container A", "", ""); Component componentA = containerA.addComponent("Component A", "", ""); @@ -568,7 +568,7 @@ public void test_addExternalDependencies_AddsTheParentSoftwareSystem_WhenACompon } @Test - public void test_addDefaultElements() { + void test_addDefaultElements() { model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); CustomElement element = model.addCustomElement("Custom"); @@ -615,7 +615,7 @@ public void test_addDefaultElements() { } @Test - public void test_addSoftwareSystem_ThrowsAnException_WhenTheSoftwareSystemIsTheScopeOfTheView() { + void test_addSoftwareSystem_ThrowsAnException_WhenTheSoftwareSystemIsTheScopeOfTheView() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); Container container = softwareSystem.addContainer("Container"); @@ -629,7 +629,7 @@ public void test_addSoftwareSystem_ThrowsAnException_WhenTheSoftwareSystemIsTheS } @Test - public void test_addContainer_ThrowsAnException_WhenTheContainerIsTheScopeOfTheView() { + void test_addContainer_ThrowsAnException_WhenTheContainerIsTheScopeOfTheView() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); Container container = softwareSystem.addContainer("Container"); @@ -643,7 +643,7 @@ public void test_addContainer_ThrowsAnException_WhenTheContainerIsTheScopeOfTheV } @Test - public void test_addSoftwareSystem_ThrowsAnException_WhenAChildContainerIsAlreadyAdded() { + void test_addSoftwareSystem_ThrowsAnException_WhenAChildContainerIsAlreadyAdded() { try { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1"); Container container1 = softwareSystem1.addContainer("Container 1"); @@ -665,7 +665,7 @@ public void test_addSoftwareSystem_ThrowsAnException_WhenAChildContainerIsAlread } @Test - public void test_addSoftwareSystem_ThrowsAnException_WhenAChildComponentIsAlreadyAdded() { + void test_addSoftwareSystem_ThrowsAnException_WhenAChildComponentIsAlreadyAdded() { try { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1"); Container container1 = softwareSystem1.addContainer("Container 1"); @@ -687,7 +687,7 @@ public void test_addSoftwareSystem_ThrowsAnException_WhenAChildComponentIsAlread } @Test - public void test_addContainer_ThrowsAnException_WhenAChildComponentIsAlreadyAdded() { + void test_addContainer_ThrowsAnException_WhenAChildComponentIsAlreadyAdded() { try { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1"); Container container1 = softwareSystem1.addContainer("Container 1"); @@ -709,7 +709,7 @@ public void test_addContainer_ThrowsAnException_WhenAChildComponentIsAlreadyAdde } @Test - public void test_addContainer_ThrowsAnException_WhenTheParentIsAlreadyAdded() { + void test_addContainer_ThrowsAnException_WhenTheParentIsAlreadyAdded() { try { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1"); Container container1 = softwareSystem1.addContainer("Container 1"); @@ -731,7 +731,7 @@ public void test_addContainer_ThrowsAnException_WhenTheParentIsAlreadyAdded() { } @Test - public void test_addComponent_ThrowsAnException_WhenTheParentIsAlreadyAdded() { + void test_addComponent_ThrowsAnException_WhenTheParentIsAlreadyAdded() { try { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1"); Container container1 = softwareSystem1.addContainer("Container 1"); diff --git a/structurizr-core/test/unit/com/structurizr/view/ConfigurationTests.java b/structurizr-core/test/unit/com/structurizr/view/ConfigurationTests.java index c7e0ac2aa..ac004de8e 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ConfigurationTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ConfigurationTests.java @@ -1,22 +1,21 @@ package com.structurizr.view; import com.structurizr.AbstractWorkspaceTestBase; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.*; public class ConfigurationTests extends AbstractWorkspaceTestBase { @Test - public void test_defaultView_DoesNothing_WhenPassedNull() { + void test_defaultView_DoesNothing_WhenPassedNull() { Configuration configuration = new Configuration(); - configuration.setDefaultView((View)null); + configuration.setDefaultView((View) null); assertNull(configuration.getDefaultView()); } @Test - public void test_defaultView() { + void test_defaultView() { SystemLandscapeView view = views.createSystemLandscapeView("key", "Description"); Configuration configuration = new Configuration(); configuration.setDefaultView(view); @@ -24,7 +23,7 @@ public void test_defaultView() { } @Test - public void test_copyConfigurationFrom() { + void test_copyConfigurationFrom() { Configuration source = new Configuration(); source.setLastSavedView("someKey"); @@ -34,34 +33,36 @@ public void test_copyConfigurationFrom() { } @Test - public void test_setTheme_WithAUrl() { + void test_setTheme_WithAUrl() { Configuration configuration = new Configuration(); configuration.setTheme("https://example.com/theme.json"); assertEquals("https://example.com/theme.json", configuration.getTheme()); } @Test - public void test_setTheme_WithAUrlThatHasATrailingSpace() { + void test_setTheme_WithAUrlThatHasATrailingSpace() { Configuration configuration = new Configuration(); configuration.setTheme("https://example.com/theme.json "); assertEquals("https://example.com/theme.json", configuration.getTheme()); } - @Test(expected = IllegalArgumentException.class) - public void test_setTheme_ThrowsAnIllegalArgumentException_WhenAnInvalidUrlIsSpecified() { - Configuration configuration = new Configuration(); - configuration.setTheme("htt://blah"); + @Test + void test_setTheme_ThrowsAnIllegalArgumentException_WhenAnInvalidUrlIsSpecified() { + assertThrows(IllegalArgumentException.class, () -> { + Configuration configuration = new Configuration(); + configuration.setTheme("htt://blah"); + }); } @Test - public void test_setTheme_DoesNothing_WhenANullUrlIsSpecified() { + void test_setTheme_DoesNothing_WhenANullUrlIsSpecified() { Configuration configuration = new Configuration(); configuration.setTheme(null); assertNull(configuration.getTheme()); } @Test - public void test_setTheme_DoesNothing_WhenAnEmptyUrlIsSpecified() { + void test_setTheme_DoesNothing_WhenAnEmptyUrlIsSpecified() { Configuration configuration = new Configuration(); configuration.setTheme(" "); assertNull(configuration.getTheme()); diff --git a/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java b/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java index 8f41c6886..801e481bd 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java @@ -3,27 +3,27 @@ import com.structurizr.AbstractWorkspaceTestBase; import com.structurizr.Workspace; import com.structurizr.model.*; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class ContainerViewTests extends AbstractWorkspaceTestBase { private SoftwareSystem softwareSystem; private ContainerView view; - @Before + @BeforeEach public void setUp() { softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); view = new ContainerView(softwareSystem, "containers", "Description"); } @Test - public void test_construction() { + void test_construction() { assertEquals("The System - Containers", view.getName()); assertEquals("Description", view.getDescription()); assertEquals(0, view.getElements().size()); @@ -33,14 +33,14 @@ public void test_construction() { } @Test - public void test_addAllSoftwareSystems_DoesNothing_WhenThereAreNoOtherSoftwareSystems() { + void test_addAllSoftwareSystems_DoesNothing_WhenThereAreNoOtherSoftwareSystems() { assertEquals(0, view.getElements().size()); view.addAllSoftwareSystems(); assertEquals(0, view.getElements().size()); } @Test - public void test_addAllSoftwareSystems_AddsAllSoftwareSystems_WhenThereAreSomeSoftwareSystemsInTheModel() { + void test_addAllSoftwareSystems_AddsAllSoftwareSystems_WhenThereAreSomeSoftwareSystemsInTheModel() { SoftwareSystem softwareSystemA = model.addSoftwareSystem(Location.External, "System A", "Description"); SoftwareSystem softwareSystemB = model.addSoftwareSystem(Location.External, "System B", "Description"); @@ -52,14 +52,14 @@ public void test_addAllSoftwareSystems_AddsAllSoftwareSystems_WhenThereAreSomeSo } @Test - public void test_addAllPeople_DoesNothing_WhenThereAreNoPeople() { + void test_addAllPeople_DoesNothing_WhenThereAreNoPeople() { assertEquals(0, view.getElements().size()); view.addAllPeople(); assertEquals(0, view.getElements().size()); } @Test - public void test_addAllPeople_AddsAllPeople_WhenThereAreSomePeopleInTheModel() { + void test_addAllPeople_AddsAllPeople_WhenThereAreSomePeopleInTheModel() { Person userA = model.addPerson(Location.External, "User A", "Description"); Person userB = model.addPerson(Location.External, "User B", "Description"); @@ -71,14 +71,14 @@ public void test_addAllPeople_AddsAllPeople_WhenThereAreSomePeopleInTheModel() { } @Test - public void test_addAllElements_DoesNothing_WhenThereAreNoSoftwareSystemsOrPeople() { + void test_addAllElements_DoesNothing_WhenThereAreNoSoftwareSystemsOrPeople() { assertEquals(0, view.getElements().size()); view.addAllElements(); assertEquals(0, view.getElements().size()); } @Test - public void test_addAllElements_AddsAllSoftwareSystemsAndPeopleAndContainers_WhenThereAreSomeSoftwareSystemsAndPeopleAndContainersInTheModel() { + void test_addAllElements_AddsAllSoftwareSystemsAndPeopleAndContainers_WhenThereAreSomeSoftwareSystemsAndPeopleAndContainersInTheModel() { SoftwareSystem softwareSystemA = model.addSoftwareSystem(Location.External, "System A", "Description"); SoftwareSystem softwareSystemB = model.addSoftwareSystem(Location.External, "System B", "Description"); Person userA = model.addPerson(Location.External, "User A", "Description"); @@ -98,14 +98,14 @@ public void test_addAllElements_AddsAllSoftwareSystemsAndPeopleAndContainers_Whe } @Test - public void test_addAllContainers_DoesNothing_WhenThereAreNoContainers() { + void test_addAllContainers_DoesNothing_WhenThereAreNoContainers() { assertEquals(0, view.getElements().size()); view.addAllContainers(); assertEquals(0, view.getElements().size()); } @Test - public void test_addAllContainers_AddsAllContainers_WhenThereAreSomeContainers() { + void test_addAllContainers_AddsAllContainers_WhenThereAreSomeContainers() { Container webApplication = softwareSystem.addContainer("Web Application", "Does something", "Apache Tomcat"); Container database = softwareSystem.addContainer("Database", "Does something", "MySQL"); @@ -117,21 +117,21 @@ public void test_addAllContainers_AddsAllContainers_WhenThereAreSomeContainers() } @Test - public void test_addNearestNeightbours_DoesNothing_WhenANullElementIsSpecified() { + void test_addNearestNeightbours_DoesNothing_WhenANullElementIsSpecified() { view.addNearestNeighbours(null); assertEquals(0, view.getElements().size()); } @Test - public void test_addNearestNeighbours_DoesNothing_WhenThereAreNoNeighbours() { + void test_addNearestNeighbours_DoesNothing_WhenThereAreNoNeighbours() { view.addNearestNeighbours(softwareSystem); assertEquals(0, view.getElements().size()); } @Test - public void test_addNearestNeighbours_AddsNearestNeighbours_WhenThereAreSomeNearestNeighbours() { + void test_addNearestNeighbours_AddsNearestNeighbours_WhenThereAreSomeNearestNeighbours() { SoftwareSystem softwareSystemA = model.addSoftwareSystem("System A", "Description"); SoftwareSystem softwareSystemB = model.addSoftwareSystem("System B", "Description"); Person userA = model.addPerson("User A", "Description"); @@ -190,7 +190,7 @@ public void test_addNearestNeighbours_AddsNearestNeighbours_WhenThereAreSomeNear } @Test - public void test_remove_RemovesContainer() { + void test_remove_RemovesContainer() { Container webApplication = softwareSystem.addContainer("Web Application", "", ""); Container database = softwareSystem.addContainer("Database", "", ""); @@ -203,7 +203,7 @@ public void test_remove_RemovesContainer() { } @Test - public void test_remove_ElementsWithTag() { + void test_remove_ElementsWithTag() { final String TAG = "myTag"; Container webApplication = softwareSystem.addContainer("Web Application", "", ""); Container database = softwareSystem.addContainer("Database", "", ""); @@ -218,7 +218,7 @@ public void test_remove_ElementsWithTag() { } @Test - public void test_remove_RelationshipWithTag() { + void test_remove_RelationshipWithTag() { final String TAG = "myTag"; Container webApplication = softwareSystem.addContainer("Web Application", "", ""); Container database = softwareSystem.addContainer("Database", "", ""); @@ -234,7 +234,7 @@ public void test_remove_RelationshipWithTag() { } @Test - public void test_addDependentSoftwareSystem() { + void test_addDependentSoftwareSystem() { assertEquals(0, view.getElements().size()); assertEquals(0, view.getRelationships().size()); @@ -253,7 +253,7 @@ public void test_addDependentSoftwareSystem() { } @Test - public void test_addDependentSoftwareSystem2() { + void test_addDependentSoftwareSystem2() { Container container1a = softwareSystem.addContainer("Container 1A", "", ""); SoftwareSystem softwareSystem2 = model.addSoftwareSystem(Location.External, "SoftwareSystem 2", ""); @@ -270,7 +270,7 @@ public void test_addDependentSoftwareSystem2() { } @Test - public void test_addDefaultElements() { + void test_addDefaultElements() { model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); CustomElement element = model.addCustomElement("Custom"); @@ -311,7 +311,7 @@ public void test_addDefaultElements() { } @Test - public void test_addSoftwareSystem_ThrowsAnException_WhenTheSoftwareSystemIsTheScopeOfTheView() { + void test_addSoftwareSystem_ThrowsAnException_WhenTheSoftwareSystemIsTheScopeOfTheView() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); view = new ContainerView(softwareSystem, "containers", "Description"); @@ -324,7 +324,7 @@ public void test_addSoftwareSystem_ThrowsAnException_WhenTheSoftwareSystemIsTheS } @Test - public void test_addSoftwareSystem_ThrowsAnException_WhenAChildContainerIsAlreadyAdded() { + void test_addSoftwareSystem_ThrowsAnException_WhenAChildContainerIsAlreadyAdded() { try { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1"); Container container1 = softwareSystem1.addContainer("Container 1"); @@ -345,7 +345,7 @@ public void test_addSoftwareSystem_ThrowsAnException_WhenAChildContainerIsAlread } @Test - public void test_addContainer_ThrowsAnException_WhenTheParentIsAlreadyAdded() { + void test_addContainer_ThrowsAnException_WhenTheParentIsAlreadyAdded() { try { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1"); Container container1 = softwareSystem1.addContainer("Container 1"); diff --git a/structurizr-core/test/unit/com/structurizr/view/DefaultLayoutMergeStrategyTests.java b/structurizr-core/test/unit/com/structurizr/view/DefaultLayoutMergeStrategyTests.java index e09e4462d..26e329dfd 100644 --- a/structurizr-core/test/unit/com/structurizr/view/DefaultLayoutMergeStrategyTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/DefaultLayoutMergeStrategyTests.java @@ -3,14 +3,14 @@ import com.structurizr.Workspace; import com.structurizr.model.Container; import com.structurizr.model.SoftwareSystem; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class DefaultLayoutMergeStrategyTests { @Test - public void test_copyLayoutInformation_WhenCanonicalNamesHaveNotChanged() { + void test_copyLayoutInformation_WhenCanonicalNamesHaveNotChanged() { Workspace workspace1 = new Workspace("1", ""); SoftwareSystem softwareSystem1 = workspace1.getModel().addSoftwareSystem("Software System"); Container container1 = softwareSystem1.addContainer("Container", "", ""); @@ -33,7 +33,7 @@ public void test_copyLayoutInformation_WhenCanonicalNamesHaveNotChanged() { } @Test - public void test_copyLayoutInformation_WhenAParentElementNameHasChanged() { + void test_copyLayoutInformation_WhenAParentElementNameHasChanged() { Workspace workspace1 = new Workspace("1", ""); SoftwareSystem softwareSystem1 = workspace1.getModel().addSoftwareSystem("Software System"); Container container1 = softwareSystem1.addContainer("Container", "", ""); @@ -56,7 +56,7 @@ public void test_copyLayoutInformation_WhenAParentElementNameHasChanged() { } @Test - public void test_copyLayoutInformation_WhenAnElementNameHasChangedButTheDescriptionHasNotChanged() { + void test_copyLayoutInformation_WhenAnElementNameHasChangedButTheDescriptionHasNotChanged() { Workspace workspace1 = new Workspace("1", ""); SoftwareSystem softwareSystem1 = workspace1.getModel().addSoftwareSystem("Software System"); Container container1 = softwareSystem1.addContainer("Container", "Container description", ""); @@ -79,7 +79,7 @@ public void test_copyLayoutInformation_WhenAnElementNameHasChangedButTheDescript } @Test - public void test_copyLayoutInformation_WhenAnElementNameAndDescriptionHaveChangedButTheIdHasNotChanged() { + void test_copyLayoutInformation_WhenAnElementNameAndDescriptionHaveChangedButTheIdHasNotChanged() { Workspace workspace1 = new Workspace("1", ""); SoftwareSystem softwareSystem1 = workspace1.getModel().addSoftwareSystem("Software System"); Container container1 = softwareSystem1.addContainer("Container", "Container description", ""); @@ -102,7 +102,7 @@ public void test_copyLayoutInformation_WhenAnElementNameAndDescriptionHaveChange } @Test - public void test_copyLayoutInformation_WhenAnElementNameAndDescriptionAndIdHaveChanged() { + void test_copyLayoutInformation_WhenAnElementNameAndDescriptionAndIdHaveChanged() { Workspace workspace1 = new Workspace("1", ""); SoftwareSystem softwareSystem1 = workspace1.getModel().addSoftwareSystem("Software System"); Container container1 = softwareSystem1.addContainer("Container", "Container description", ""); @@ -126,7 +126,7 @@ public void test_copyLayoutInformation_WhenAnElementNameAndDescriptionAndIdHaveC } @Test - public void test_copyLayoutInformation_WhenAnElementNameAndDescriptionAndIdHaveChangedAndDescriptionWasNull() { + void test_copyLayoutInformation_WhenAnElementNameAndDescriptionAndIdHaveChangedAndDescriptionWasNull() { Workspace workspace1 = new Workspace("1", ""); SoftwareSystem softwareSystem1 = workspace1.getModel().addSoftwareSystem("Software System"); Container container1 = softwareSystem1.addContainer("Container"); @@ -151,7 +151,7 @@ public void test_copyLayoutInformation_WhenAnElementNameAndDescriptionAndIdHaveC } @Test - public void test_copyLayoutInformation_DoesNotThrowAnExceptionWhenAddingAnElementToAView() { + void test_copyLayoutInformation_DoesNotThrowAnExceptionWhenAddingAnElementToAView() { Workspace workspace1 = new Workspace("1", ""); SoftwareSystem softwareSystem1A = workspace1.getModel().addSoftwareSystem("Software System A"); SoftwareSystem softwareSystem1B = workspace1.getModel().addSoftwareSystem("Software System B"); diff --git a/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java b/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java index 7d35e9220..36394293b 100644 --- a/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java @@ -2,41 +2,41 @@ import com.structurizr.AbstractWorkspaceTestBase; import com.structurizr.model.*; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class DeploymentViewTests extends AbstractWorkspaceTestBase { private DeploymentView deploymentView; - @Before + @BeforeEach public void setup() { } @Test - public void test_getName_WithNoSoftwareSystemAndNoEnvironment() { + void test_getName_WithNoSoftwareSystemAndNoEnvironment() { deploymentView = views.createDeploymentView("deployment", "Description"); assertEquals("Deployment - Default", deploymentView.getName()); } @Test - public void test_getName_WithNoSoftwareSystemAndAnEnvironment() { + void test_getName_WithNoSoftwareSystemAndAnEnvironment() { deploymentView = views.createDeploymentView("deployment", "Description"); deploymentView.setEnvironment("Live"); assertEquals("Deployment - Live", deploymentView.getName()); } @Test - public void test_getName_WithASoftwareSystemAndNoEnvironment() { + void test_getName_WithASoftwareSystemAndNoEnvironment() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); deploymentView = views.createDeploymentView(softwareSystem, "deployment", "Description"); assertEquals("Software System - Deployment - Default", deploymentView.getName()); } @Test - public void test_getName_WithASoftwareSystemAndAnEnvironment() { + void test_getName_WithASoftwareSystemAndAnEnvironment() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); deploymentView = views.createDeploymentView(softwareSystem, "deployment", "Description"); deploymentView.setEnvironment("Live"); @@ -44,10 +44,10 @@ public void test_getName_WithASoftwareSystemAndAnEnvironment() { } @Test - public void test_addDeploymentNode_ThrowsAnException_WhenPassedNull() { + void test_addDeploymentNode_ThrowsAnException_WhenPassedNull() { try { deploymentView = views.createDeploymentView("key", "Description"); - deploymentView.add((DeploymentNode)null); + deploymentView.add((DeploymentNode) null); fail(); } catch (IllegalArgumentException iae) { assertEquals("A deployment node must be specified.", iae.getMessage()); @@ -55,10 +55,10 @@ public void test_addDeploymentNode_ThrowsAnException_WhenPassedNull() { } @Test - public void test_addRelationship_ThrowsAnException_WhenPassedNull() { + void test_addRelationship_ThrowsAnException_WhenPassedNull() { try { deploymentView = views.createDeploymentView("key", "Description"); - deploymentView.add((Relationship)null); + deploymentView.add((Relationship) null); fail(); } catch (IllegalArgumentException iae) { assertEquals("A relationship must be specified.", iae.getMessage()); @@ -66,7 +66,7 @@ public void test_addRelationship_ThrowsAnException_WhenPassedNull() { } @Test - public void test_addAllDeploymentNodes_DoesNothing_WhenThereAreNoTopLevelDeploymentNodes() { + void test_addAllDeploymentNodes_DoesNothing_WhenThereAreNoTopLevelDeploymentNodes() { deploymentView = views.createDeploymentView("deployment", "Description"); deploymentView.addAllDeploymentNodes(); @@ -74,7 +74,7 @@ public void test_addAllDeploymentNodes_DoesNothing_WhenThereAreNoTopLevelDeploym } @Test - public void test_addAllDeploymentNodes_DoesNothing_WhenThereAreTopLevelDeploymentNodesButNoContainerInstances() { + void test_addAllDeploymentNodes_DoesNothing_WhenThereAreTopLevelDeploymentNodesButNoContainerInstances() { deploymentView = views.createDeploymentView("deployment", "Description"); model.addDeploymentNode("Deployment Node", "Description", "Technology"); @@ -83,7 +83,7 @@ public void test_addAllDeploymentNodes_DoesNothing_WhenThereAreTopLevelDeploymen } @Test - public void test_addAllDeploymentNodes_DoesNothing_WhenThereNoDeploymentNodesForTheDeploymentEnvironment() { + void test_addAllDeploymentNodes_DoesNothing_WhenThereNoDeploymentNodesForTheDeploymentEnvironment() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); Container container = softwareSystem.addContainer("Container", "Description", "Technology"); DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node", "Description", "Technology"); @@ -96,7 +96,7 @@ public void test_addAllDeploymentNodes_DoesNothing_WhenThereNoDeploymentNodesFor } @Test - public void test_addAllDeploymentNodes_AddsDeploymentNodesAndContainerInstances_WhenThereAreTopLevelDeploymentNodesWithContainerInstances() { + void test_addAllDeploymentNodes_AddsDeploymentNodesAndContainerInstances_WhenThereAreTopLevelDeploymentNodesWithContainerInstances() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); Container container = softwareSystem.addContainer("Container", "Description", "Technology"); DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node", "Description", "Technology"); @@ -110,7 +110,7 @@ public void test_addAllDeploymentNodes_AddsDeploymentNodesAndContainerInstances_ } @Test - public void test_addAllDeploymentNodes_AddsDeploymentNodesAndContainerInstances_WhenThereAreChildDeploymentNodesWithContainerInstances() { + void test_addAllDeploymentNodes_AddsDeploymentNodesAndContainerInstances_WhenThereAreChildDeploymentNodesWithContainerInstances() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); Container container = softwareSystem.addContainer("Container", "Description", "Technology"); DeploymentNode deploymentNodeParent = model.addDeploymentNode("Deployment Node", "Description", "Technology"); @@ -126,7 +126,7 @@ public void test_addAllDeploymentNodes_AddsDeploymentNodesAndContainerInstances_ } @Test - public void test_addAllDeploymentNodes_AddsDeploymentNodesAndContainerInstancesOnlyForTheSoftwareSystemInScope() { + void test_addAllDeploymentNodes_AddsDeploymentNodesAndContainerInstancesOnlyForTheSoftwareSystemInScope() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1", ""); Container container1 = softwareSystem1.addContainer("Container 1", "Description", "Technology"); DeploymentNode deploymentNode1 = model.addDeploymentNode("Deployment Node 1", "Description", "Technology"); @@ -149,7 +149,7 @@ public void test_addAllDeploymentNodes_AddsDeploymentNodesAndContainerInstancesO } @Test - public void test_addDeploymentNode_AddsTheParentToo() { + void test_addDeploymentNode_AddsTheParentToo() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); Container container = softwareSystem.addContainer("Container", "Description", "Technology"); DeploymentNode deploymentNodeParent = model.addDeploymentNode("Deployment Node", "Description", "Technology"); @@ -165,7 +165,7 @@ public void test_addDeploymentNode_AddsTheParentToo() { } @Test - public void test_addDeploymentNode_ThrowsAnException_WhenAddingADeploymentNodeFromAnotherDeploymentEnvironment() { + void test_addDeploymentNode_ThrowsAnException_WhenAddingADeploymentNodeFromAnotherDeploymentEnvironment() { DeploymentNode devDeploymentNode = model.addDeploymentNode("Dev", "Deployment Node", "Description", "Technology"); devDeploymentNode.addInfrastructureNode("Load Balancer"); DeploymentNode liveDeploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); @@ -184,7 +184,7 @@ public void test_addDeploymentNode_ThrowsAnException_WhenAddingADeploymentNodeFr } @Test - public void test_addSoftwareSystemInstance_ThrowsAnException_WhenTheSoftwareSystemInstanceIsTheSoftwareSystemInScope() { + void test_addSoftwareSystemInstance_ThrowsAnException_WhenTheSoftwareSystemInstanceIsTheSoftwareSystemInScope() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node", "Description", "Technology"); SoftwareSystemInstance softwareSystemInstance = deploymentNode.add(softwareSystem); @@ -198,7 +198,7 @@ public void test_addSoftwareSystemInstance_ThrowsAnException_WhenTheSoftwareSyst } @Test - public void test_addSoftwareSystemInstance_DoesNotAddTheSoftwareSystemInstance_WhenAChildContainerInstanceHasAlreadyBeenAdded() { + void test_addSoftwareSystemInstance_DoesNotAddTheSoftwareSystemInstance_WhenAChildContainerInstanceHasAlreadyBeenAdded() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); Container container = softwareSystem.addContainer("Container"); DeploymentNode deploymentNode1 = model.addDeploymentNode("Deployment Node 1", "Description", "Technology"); @@ -217,7 +217,7 @@ public void test_addSoftwareSystemInstance_DoesNotAddTheSoftwareSystemInstance_W } @Test - public void test_addContainerInstance_DoesNotAddTheContainerInstance_WhenTheParentSoftwareSystemInstanceHasAlreadyBeenAdded() { + void test_addContainerInstance_DoesNotAddTheContainerInstance_WhenTheParentSoftwareSystemInstanceHasAlreadyBeenAdded() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); Container container = softwareSystem.addContainer("Container"); DeploymentNode deploymentNode1 = model.addDeploymentNode("Deployment Node 1", "Description", "Technology"); @@ -236,10 +236,10 @@ public void test_addContainerInstance_DoesNotAddTheContainerInstance_WhenThePare } @Test - public void test_addAnimationStep_ThrowsAnException_WhenNoElementInstancesAreSpecified() { + void test_addAnimationStep_ThrowsAnException_WhenNoElementInstancesAreSpecified() { try { deploymentView = views.createDeploymentView("deployment", "Description"); - deploymentView.addAnimation((ContainerInstance[])null); + deploymentView.addAnimation((ContainerInstance[]) null); fail(); } catch (IllegalArgumentException iae) { assertEquals("One or more software system/container instances must be specified.", iae.getMessage()); @@ -247,10 +247,10 @@ public void test_addAnimationStep_ThrowsAnException_WhenNoElementInstancesAreSpe } @Test - public void test_addAnimationStep_ThrowsAnException_WhenNoInfrastructureNodesAreSpecified() { + void test_addAnimationStep_ThrowsAnException_WhenNoInfrastructureNodesAreSpecified() { try { deploymentView = views.createDeploymentView("deployment", "Description"); - deploymentView.addAnimation((InfrastructureNode[])null); + deploymentView.addAnimation((InfrastructureNode[]) null); fail(); } catch (IllegalArgumentException iae) { assertEquals("One or more infrastructure nodes must be specified.", iae.getMessage()); @@ -258,7 +258,7 @@ public void test_addAnimationStep_ThrowsAnException_WhenNoInfrastructureNodesAre } @Test - public void test_addAnimationStep_ThrowsAnException_WhenNoElementInstancesOrInfrastructureNodesAreSpecified() { + void test_addAnimationStep_ThrowsAnException_WhenNoElementInstancesOrInfrastructureNodesAreSpecified() { try { deploymentView = views.createDeploymentView("deployment", "Description"); deploymentView.addAnimation(null, null); @@ -269,7 +269,7 @@ public void test_addAnimationStep_ThrowsAnException_WhenNoElementInstancesOrInfr } @Test - public void test_addAnimationStep() { + void test_addAnimationStep() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); Container webApplication = softwareSystem.addContainer("Web Application", "Description", "Technology"); Container database = softwareSystem.addContainer("Database", "Description", "Technology"); @@ -303,7 +303,7 @@ public void test_addAnimationStep() { } @Test - public void test_addAnimationStep_IgnoresContainerInstancesThatDoNotExistInTheView() { + void test_addAnimationStep_IgnoresContainerInstancesThatDoNotExistInTheView() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); Container webApplication = softwareSystem.addContainer("Web Application", "Description", "Technology"); Container database = softwareSystem.addContainer("Database", "Description", "Technology"); @@ -329,7 +329,7 @@ public void test_addAnimationStep_IgnoresContainerInstancesThatDoNotExistInTheVi } @Test - public void test_addAnimationStep_ThrowsAnException_WhenContainerInstancesAreSpecifiedButNoneOfThemExistInTheView() { + void test_addAnimationStep_ThrowsAnException_WhenContainerInstancesAreSpecifiedButNoneOfThemExistInTheView() { try { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); Container webApplication = softwareSystem.addContainer("Web Application", "Description", "Technology"); @@ -352,7 +352,7 @@ public void test_addAnimationStep_ThrowsAnException_WhenContainerInstancesAreSpe } @Test - public void test_remove_RemovesTheInfrastructureNode() { + void test_remove_RemovesTheInfrastructureNode() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); Container container = softwareSystem.addContainer("Container", "Description", "Technology"); DeploymentNode deploymentNodeParent = model.addDeploymentNode("Deployment Node", "Description", "Technology"); @@ -372,7 +372,7 @@ public void test_remove_RemovesTheInfrastructureNode() { } @Test - public void test_remove_RemovesTheSoftwareSystemInstance() { + void test_remove_RemovesTheSoftwareSystemInstance() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); DeploymentNode deploymentNodeParent = model.addDeploymentNode("Deployment Node", "Description", "Technology"); DeploymentNode deploymentNodeChild = deploymentNodeParent.addDeploymentNode("Deployment Node", "Description", "Technology"); @@ -391,7 +391,7 @@ public void test_remove_RemovesTheSoftwareSystemInstance() { } @Test - public void test_remove_RemovesTheContainerInstance() { + void test_remove_RemovesTheContainerInstance() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); Container container = softwareSystem.addContainer("Container", "Description", "Technology"); DeploymentNode deploymentNodeParent = model.addDeploymentNode("Deployment Node", "Description", "Technology"); @@ -411,7 +411,7 @@ public void test_remove_RemovesTheContainerInstance() { } @Test - public void test_remove_RemovesTheDeploymentNodeAndChildren() { + void test_remove_RemovesTheDeploymentNodeAndChildren() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); Container container = softwareSystem.addContainer("Container", "Description", "Technology"); DeploymentNode deploymentNodeParent = model.addDeploymentNode("Deployment Node", "Description", "Technology"); @@ -429,7 +429,7 @@ public void test_remove_RemovesTheDeploymentNodeAndChildren() { } @Test - public void test_remove_RemovesTheChildDeploymentNodeAndChildren() { + void test_remove_RemovesTheChildDeploymentNodeAndChildren() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); Container container = softwareSystem.addContainer("Container", "Description", "Technology"); DeploymentNode deploymentNodeParent = model.addDeploymentNode("Deployment Node", "Description", "Technology"); @@ -446,7 +446,7 @@ public void test_remove_RemovesTheChildDeploymentNodeAndChildren() { } @Test - public void test_add_AddsTheInfrastructureNode() { + void test_add_AddsTheInfrastructureNode() { DeploymentNode deploymentNodeParent = model.addDeploymentNode("Deployment Node", "Description", "Technology"); DeploymentNode deploymentNodeChild = deploymentNodeParent.addDeploymentNode("Deployment Node", "Description", "Technology"); InfrastructureNode infrastructureNode1 = deploymentNodeChild.addInfrastructureNode("Infrastructure Node 1"); @@ -462,7 +462,7 @@ public void test_add_AddsTheInfrastructureNode() { } @Test - public void test_add_AddsTheSoftwareSystemInstance() { + void test_add_AddsTheSoftwareSystemInstance() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); DeploymentNode deploymentNodeParent = model.addDeploymentNode("Deployment Node", "Description", "Technology"); DeploymentNode deploymentNodeChild = deploymentNodeParent.addDeploymentNode("Deployment Node", "Description", "Technology"); @@ -479,7 +479,7 @@ public void test_add_AddsTheSoftwareSystemInstance() { } @Test - public void test_addSoftwareSystemInstance_ThrowsAnException_WhenAChildContainerInstanceHasAlreadyBeenAdded() { + void test_addSoftwareSystemInstance_ThrowsAnException_WhenAChildContainerInstanceHasAlreadyBeenAdded() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); Container container = softwareSystem.addContainer("Container"); DeploymentNode deploymentNodeParent = model.addDeploymentNode("Deployment Node", "Description", "Technology"); @@ -499,7 +499,7 @@ public void test_addSoftwareSystemInstance_ThrowsAnException_WhenAChildContainer } @Test - public void test_add_AddsTheContainerInstance() { + void test_add_AddsTheContainerInstance() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); Container container = softwareSystem.addContainer("Container"); DeploymentNode deploymentNodeParent = model.addDeploymentNode("Deployment Node", "Description", "Technology"); @@ -517,7 +517,7 @@ public void test_add_AddsTheContainerInstance() { } @Test - public void test_addContainerInstance_ThrowsAnException_WhenTheParentSoftwareSystemInstanceHasAlreadyBeenAdded() { + void test_addContainerInstance_ThrowsAnException_WhenTheParentSoftwareSystemInstanceHasAlreadyBeenAdded() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); Container container = softwareSystem.addContainer("Container"); DeploymentNode deploymentNodeParent = model.addDeploymentNode("Deployment Node", "Description", "Technology"); diff --git a/structurizr-core/test/unit/com/structurizr/view/DimensionsTests.java b/structurizr-core/test/unit/com/structurizr/view/DimensionsTests.java index fa2f7ff0b..33758c0f2 100644 --- a/structurizr-core/test/unit/com/structurizr/view/DimensionsTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/DimensionsTests.java @@ -1,13 +1,14 @@ package com.structurizr.view; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; public class DimensionsTests { @Test - public void test_construction() { + void test_construction() { Dimensions dimensions = new Dimensions(123, 456); assertEquals(123, dimensions.getWidth()); @@ -15,7 +16,7 @@ public void test_construction() { } @Test - public void test_setWidth_ThrowsAnException_WhenANegativeIntegerIsSpecified() { + void test_setWidth_ThrowsAnException_WhenANegativeIntegerIsSpecified() { try { Dimensions dimensions = new Dimensions(); dimensions.setWidth(-100); @@ -26,7 +27,7 @@ public void test_setWidth_ThrowsAnException_WhenANegativeIntegerIsSpecified() { } @Test - public void test_setHeight_ThrowsAnException_WhenANegativeIntegerIsSpecified() { + void test_setHeight_ThrowsAnException_WhenANegativeIntegerIsSpecified() { try { Dimensions dimensions = new Dimensions(); dimensions.setHeight(-100); diff --git a/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java b/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java index 065c0befc..81c98cbeb 100644 --- a/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java @@ -3,14 +3,14 @@ import com.structurizr.AbstractWorkspaceTestBase; import com.structurizr.Workspace; import com.structurizr.model.*; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class DynamicViewTests extends AbstractWorkspaceTestBase { @@ -28,7 +28,7 @@ public class DynamicViewTests extends AbstractWorkspaceTestBase { private Relationship relationship; - @Before + @BeforeEach public void setup() { person = model.addPerson("Person", ""); softwareSystemA = model.addSoftwareSystem("Software System A", ""); @@ -44,7 +44,7 @@ public void setup() { } @Test - public void test_add_ThrowsAnException_WhenPassedANullSourceElement() { + void test_add_ThrowsAnException_WhenPassedANullSourceElement() { try { DynamicView dynamicView = workspace.getViews().createDynamicView("key", "Description"); dynamicView.add(null, softwareSystemA); @@ -55,7 +55,7 @@ public void test_add_ThrowsAnException_WhenPassedANullSourceElement() { } @Test - public void test_add_ThrowsAnException_WhenPassedANullDestinationElement() { + void test_add_ThrowsAnException_WhenPassedANullDestinationElement() { try { DynamicView dynamicView = workspace.getViews().createDynamicView("key", "Description"); dynamicView.add(person, null); @@ -66,7 +66,7 @@ public void test_add_ThrowsAnException_WhenPassedANullDestinationElement() { } @Test - public void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsNotSpecifiedButAContainerIsAdded() { + void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsNotSpecifiedButAContainerIsAdded() { try { DynamicView dynamicView = workspace.getViews().createDynamicView("key", "Description"); dynamicView.add(containerA1, containerA1); @@ -77,7 +77,7 @@ public void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsNotSpecifie } @Test - public void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsNotSpecifiedButAComponentIsAdded() { + void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsNotSpecifiedButAComponentIsAdded() { try { DynamicView dynamicView = workspace.getViews().createDynamicView("key", "Description"); dynamicView.add(componentA1, componentA1); @@ -88,7 +88,7 @@ public void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsNotSpecifie } @Test - public void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsASoftwareSystemButAComponentIsAdded() { + void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsASoftwareSystemButAComponentIsAdded() { try { DynamicView dynamicView = workspace.getViews().createDynamicView(softwareSystemA, "key", "Description"); dynamicView.add(componentA1, containerA1); @@ -99,7 +99,7 @@ public void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsASoftwareSy } @Test - public void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsASoftwareSystemAndTheSameSoftwareSystemIsAdded() { + void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsASoftwareSystemAndTheSameSoftwareSystemIsAdded() { try { DynamicView dynamicView = workspace.getViews().createDynamicView(softwareSystemA, "key", "Description"); dynamicView.add(softwareSystemA, containerA1); @@ -110,7 +110,7 @@ public void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsASoftwareSy } @Test - public void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsAContainerAndTheSameContainerIsAdded() { + void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsAContainerAndTheSameContainerIsAdded() { try { DynamicView dynamicView = workspace.getViews().createDynamicView(containerA1, "key", "Description"); dynamicView.add(containerA1, containerA2); @@ -121,7 +121,7 @@ public void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsAContainerA } @Test - public void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsAContainerAndTheParentSoftwareSystemIsAdded() { + void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsAContainerAndTheParentSoftwareSystemIsAdded() { try { DynamicView dynamicView = workspace.getViews().createDynamicView(containerA1, "key", "Description"); dynamicView.add(softwareSystemA, containerA2); @@ -132,7 +132,7 @@ public void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsAContainerA } @Test - public void test_add_ThrowsAnException_WhenTheParentOfAnElementHasAlreadyBeenAdded() { + void test_add_ThrowsAnException_WhenTheParentOfAnElementHasAlreadyBeenAdded() { try { SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", ""); Container container1 = softwareSystem.addContainer("Container 1", "", ""); @@ -154,7 +154,7 @@ public void test_add_ThrowsAnException_WhenTheParentOfAnElementHasAlreadyBeenAdd } @Test - public void test_add_ThrowsAnException_WhenTheChildOfAnElementHasAlreadyBeenAdded() { + void test_add_ThrowsAnException_WhenTheChildOfAnElementHasAlreadyBeenAdded() { try { SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", ""); Container container1 = softwareSystem.addContainer("Container 1", "", ""); @@ -176,7 +176,7 @@ public void test_add_ThrowsAnException_WhenTheChildOfAnElementHasAlreadyBeenAdde } @Test - public void test_add_ThrowsAnException_WhenARelationshipBetweenTheSourceAndDestinationElementsDoesNotExist() { + void test_add_ThrowsAnException_WhenARelationshipBetweenTheSourceAndDestinationElementsDoesNotExist() { try { DynamicView dynamicView = workspace.getViews().createDynamicView("key", "Description"); SoftwareSystem ss1 = workspace.getModel().addSoftwareSystem("Software System 1", ""); @@ -189,7 +189,7 @@ public void test_add_ThrowsAnException_WhenARelationshipBetweenTheSourceAndDesti } @Test - public void test_add_ThrowsAnException_WhenARelationshipBetweenTheSourceAndDestinationElementsWithTheSpecifiedTechnologyDoesNotExist() { + void test_add_ThrowsAnException_WhenARelationshipBetweenTheSourceAndDestinationElementsWithTheSpecifiedTechnologyDoesNotExist() { try { workspace = new Workspace("Name", "Description"); model = workspace.getModel(); @@ -209,7 +209,7 @@ public void test_add_ThrowsAnException_WhenARelationshipBetweenTheSourceAndDesti } @Test - public void test_addRelationshipWithOriginalDescription() { + void test_addRelationshipWithOriginalDescription() { workspace = new Workspace("Name", "Description"); model = workspace.getModel(); @@ -226,7 +226,7 @@ public void test_addRelationshipWithOriginalDescription() { } @Test - public void test_addRelationshipWithOveriddenDescription() { + void test_addRelationshipWithOveriddenDescription() { workspace = new Workspace("Name", "Description"); model = workspace.getModel(); @@ -243,14 +243,14 @@ public void test_addRelationshipWithOveriddenDescription() { } @Test - public void test_add_AddsTheSourceAndDestinationElements_WhenARelationshipBetweenThemExists() { + void test_add_AddsTheSourceAndDestinationElements_WhenARelationshipBetweenThemExists() { final DynamicView dynamicView = workspace.getViews().createDynamicView(softwareSystemA, "key", "Description"); dynamicView.add(containerA1, containerA2); assertEquals(2, dynamicView.getElements().size()); } @Test - public void test_add_AddsTheSourceAndDestinationElements_WhenARelationshipBetweenThemExistsAndTheDestinationIsAnExternalSoftwareSystem() { + void test_add_AddsTheSourceAndDestinationElements_WhenARelationshipBetweenThemExistsAndTheDestinationIsAnExternalSoftwareSystem() { DynamicView dynamicView = workspace.getViews().createDynamicView(softwareSystemA, "key", "Description"); containerA2.uses(softwareSystemB, "", ""); dynamicView.add(containerA2, softwareSystemB); @@ -258,7 +258,7 @@ public void test_add_AddsTheSourceAndDestinationElements_WhenARelationshipBetwee } @Test - public void test_normalSequence() { + void test_normalSequence() { workspace = new Workspace("Name", "Description"); model = workspace.getModel(); @@ -280,7 +280,7 @@ public void test_normalSequence() { } @Test - public void test_normalSequence_WhenThereAreMultipleDescriptions() { + void test_normalSequence_WhenThereAreMultipleDescriptions() { workspace = new Workspace("Name", "Description"); model = workspace.getModel(); @@ -300,7 +300,7 @@ public void test_normalSequence_WhenThereAreMultipleDescriptions() { } @Test - public void test_normalSequence_WhenThereAreMultipleTechnologies() { + void test_normalSequence_WhenThereAreMultipleTechnologies() { workspace = new Workspace("Name", "Description"); model = workspace.getModel(); @@ -320,7 +320,7 @@ public void test_normalSequence_WhenThereAreMultipleTechnologies() { } @Test - public void test_parallelSequence() { + void test_parallelSequence() { workspace = new Workspace("Name", "Description"); model = workspace.getModel(); SoftwareSystem softwareSystemA = model.addSoftwareSystem("A", ""); @@ -359,7 +359,7 @@ public void test_parallelSequence() { } @Test - public void test_getRelationships_WhenTheOrderPropertyIsAnInteger() { + void test_getRelationships_WhenTheOrderPropertyIsAnInteger() { containerA1.uses(containerA2, "uses"); DynamicView view = workspace.getViews().createDynamicView(softwareSystemA, "key", "Description"); for (int i = 0; i < 10; i++) { @@ -380,7 +380,7 @@ public void test_getRelationships_WhenTheOrderPropertyIsAnInteger() { } @Test - public void test_getRelationships_WhenTheOrderPropertyIsADecimal() { + void test_getRelationships_WhenTheOrderPropertyIsADecimal() { containerA1.uses(containerA2, "uses"); DynamicView view = workspace.getViews().createDynamicView(softwareSystemA, "key", "Description"); for (int i = 0; i < 10; i++) { @@ -402,7 +402,7 @@ public void test_getRelationships_WhenTheOrderPropertyIsADecimal() { } @Test - public void test_getRelationships_WhenTheOrderPropertyIsAString() { + void test_getRelationships_WhenTheOrderPropertyIsAString() { String characters = "abcdefghij"; containerA1.uses(containerA2, "uses"); DynamicView view = workspace.getViews().createDynamicView(softwareSystemA, "key", "Description"); @@ -425,14 +425,14 @@ public void test_getRelationships_WhenTheOrderPropertyIsAString() { } @Test - public void test_response() { + void test_response() { workspace = new Workspace("Name", "Description"); model = workspace.getModel(); SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1", "Description"); SoftwareSystem softwareSystem2 = model.addSoftwareSystem("Software System 2", "Description"); Relationship relationship = softwareSystem1.uses(softwareSystem2, "Uses"); - + DynamicView view = workspace.getViews().createDynamicView("key", "Description"); view.add(softwareSystem1, "Asks for X", softwareSystem2); view.add(softwareSystem2, "Returns X", softwareSystem1); // this relationship doesn't exist, so is assumed to be a response diff --git a/structurizr-core/test/unit/com/structurizr/view/ElementStyleTests.java b/structurizr-core/test/unit/com/structurizr/view/ElementStyleTests.java index 40ec16c6a..2b2f0692b 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ElementStyleTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ElementStyleTests.java @@ -1,16 +1,16 @@ package com.structurizr.view; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class ElementStyleTests { @Test - public void test_setOpacity() { + void test_setOpacity() { ElementStyle style = new ElementStyle(); assertNull(style.getOpacity()); @@ -31,7 +31,7 @@ public void test_setOpacity() { } @Test - public void test_opacity() { + void test_opacity() { ElementStyle style = new ElementStyle(); assertNull(style.getOpacity()); @@ -52,7 +52,7 @@ public void test_opacity() { } @Test - public void test_setColor_SetsTheColorProperty_WhenAValidHexColorCodeIsSpecified() { + void test_setColor_SetsTheColorProperty_WhenAValidHexColorCodeIsSpecified() { ElementStyle style = new ElementStyle(); style.setColor("#ffffff"); assertEquals("#ffffff", style.getColor()); @@ -65,7 +65,7 @@ public void test_setColor_SetsTheColorProperty_WhenAValidHexColorCodeIsSpecified } @Test - public void test_color_SetsTheColorProperty_WhenAValidHexColorCodeIsSpecified() { + void test_color_SetsTheColorProperty_WhenAValidHexColorCodeIsSpecified() { ElementStyle style = new ElementStyle(); style.color("#ffffff"); assertEquals("#ffffff", style.getColor()); @@ -77,20 +77,24 @@ public void test_color_SetsTheColorProperty_WhenAValidHexColorCodeIsSpecified() assertEquals("#123456", style.getColor()); } - @Test(expected = IllegalArgumentException.class) - public void test_setColor_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { - ElementStyle style = new ElementStyle(); - style.setColor("white"); + @Test + void test_setColor_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { + assertThrows(IllegalArgumentException.class, () -> { + ElementStyle style = new ElementStyle(); + style.setColor("white"); + }); } - @Test(expected = IllegalArgumentException.class) - public void test_color_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { - ElementStyle style = new ElementStyle(); - style.color("white"); + @Test + void test_color_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { + assertThrows(IllegalArgumentException.class, () -> { + ElementStyle style = new ElementStyle(); + style.color("white"); + }); } @Test - public void test_setBackground_SetsTheBackgroundProperty_WhenAValidHexColorCodeIsSpecified() { + void test_setBackground_SetsTheBackgroundProperty_WhenAValidHexColorCodeIsSpecified() { ElementStyle style = new ElementStyle(); style.setBackground("#ffffff"); assertEquals("#ffffff", style.getBackground()); @@ -103,7 +107,7 @@ public void test_setBackground_SetsTheBackgroundProperty_WhenAValidHexColorCodeI } @Test - public void test_background_SetsTheBackgroundProperty_WhenAValidHexColorCodeIsSpecified() { + void test_background_SetsTheBackgroundProperty_WhenAValidHexColorCodeIsSpecified() { ElementStyle style = new ElementStyle(); style.background("#ffffff"); assertEquals("#ffffff", style.getBackground()); @@ -115,61 +119,67 @@ public void test_background_SetsTheBackgroundProperty_WhenAValidHexColorCodeIsSp assertEquals("#123456", style.getBackground()); } - @Test(expected = IllegalArgumentException.class) - public void test_setBackground_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { - ElementStyle style = new ElementStyle(); - style.setBackground("white"); + @Test + void test_setBackground_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { + assertThrows(IllegalArgumentException.class, () -> { + ElementStyle style = new ElementStyle(); + style.setBackground("white"); + }); } - @Test(expected = IllegalArgumentException.class) - public void test_background_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { - ElementStyle style = new ElementStyle(); - style.background("white"); + @Test + void test_background_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { + assertThrows(IllegalArgumentException.class, () -> { + ElementStyle style = new ElementStyle(); + style.background("white"); + }); } @Test - public void test_setIcon_WithAUrl() { + void test_setIcon_WithAUrl() { ElementStyle style = new ElementStyle(); style.setIcon("https://structurizr.com/static/img/structurizr-logo.png"); assertEquals("https://structurizr.com/static/img/structurizr-logo.png", style.getIcon()); } @Test - public void test_setIcon_WithAUrlThatHasATrailingSpaceCharacter() { + void test_setIcon_WithAUrlThatHasATrailingSpaceCharacter() { ElementStyle style = new ElementStyle(); style.setIcon("https://structurizr.com/static/img/structurizr-logo.png "); assertEquals("https://structurizr.com/static/img/structurizr-logo.png", style.getIcon()); } @Test - public void test_setIcon_WithADataUri() { + void test_setIcon_WithADataUri() { ElementStyle style = new ElementStyle(); style.setIcon("data:image/png;base64,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"); assertEquals("data:image/png;base64,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", style.getIcon()); } - @Test(expected = IllegalArgumentException.class) - public void test_setIcon_ThrowsAnIllegalArgumentException_WhenAnInvalidUrlIsSpecified() { - ElementStyle style = new ElementStyle(); - style.setIcon("htt://blah"); + @Test + void test_setIcon_ThrowsAnIllegalArgumentException_WhenAnInvalidUrlIsSpecified() { + assertThrows(IllegalArgumentException.class, () -> { + ElementStyle style = new ElementStyle(); + style.setIcon("htt://blah"); + }); } @Test - public void test_setIcon_DoesNothing_WhenANullUrlIsSpecified() { + void test_setIcon_DoesNothing_WhenANullUrlIsSpecified() { ElementStyle style = new ElementStyle(); style.setIcon(null); assertNull(style.getIcon()); } @Test - public void test_setIcon_DoesNothing_WhenAnEmptyUrlIsSpecified() { + void test_setIcon_DoesNothing_WhenAnEmptyUrlIsSpecified() { ElementStyle style = new ElementStyle(); style.setIcon(" "); assertNull(style.getIcon()); } @Test - public void test_setStroke_SetsTheStrokeProperty_WhenAValidHexColorCodeIsSpecified() { + void test_setStroke_SetsTheStrokeProperty_WhenAValidHexColorCodeIsSpecified() { ElementStyle style = new ElementStyle(); style.setStroke("#ffffff"); assertEquals("#ffffff", style.getStroke()); @@ -182,7 +192,7 @@ public void test_setStroke_SetsTheStrokeProperty_WhenAValidHexColorCodeIsSpecifi } @Test - public void test_Stroke_SetsTheStrokeProperty_WhenAValidHexColorCodeIsSpecified() { + void test_Stroke_SetsTheStrokeProperty_WhenAValidHexColorCodeIsSpecified() { ElementStyle style = new ElementStyle(); style.stroke("#ffffff"); assertEquals("#ffffff", style.getStroke()); @@ -194,26 +204,30 @@ public void test_Stroke_SetsTheStrokeProperty_WhenAValidHexColorCodeIsSpecified( assertEquals("#123456", style.getStroke()); } - @Test(expected = IllegalArgumentException.class) - public void test_setStroke_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { - ElementStyle style = new ElementStyle(); - style.setStroke("white"); + @Test + void test_setStroke_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { + assertThrows(IllegalArgumentException.class, () -> { + ElementStyle style = new ElementStyle(); + style.setStroke("white"); + }); } - @Test(expected = IllegalArgumentException.class) - public void test_Stroke_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { - ElementStyle style = new ElementStyle(); - style.stroke("white"); + @Test + void test_Stroke_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { + assertThrows(IllegalArgumentException.class, () -> { + ElementStyle style = new ElementStyle(); + style.stroke("white"); + }); } @Test - public void test_getProperties_ReturnsAnEmptyList_WhenNoPropertiesHaveBeenAdded() { + void test_getProperties_ReturnsAnEmptyList_WhenNoPropertiesHaveBeenAdded() { ElementStyle style = new ElementStyle(); assertEquals(0, style.getProperties().size()); } @Test - public void test_addProperty_ThrowsAnException_WhenTheNameIsNull() { + void test_addProperty_ThrowsAnException_WhenTheNameIsNull() { try { ElementStyle style = new ElementStyle(); style.addProperty(null, "value"); @@ -224,7 +238,7 @@ public void test_addProperty_ThrowsAnException_WhenTheNameIsNull() { } @Test - public void test_addProperty_ThrowsAnException_WhenTheNameIsEmpty() { + void test_addProperty_ThrowsAnException_WhenTheNameIsEmpty() { try { ElementStyle style = new ElementStyle(); style.addProperty(" ", "value"); @@ -235,7 +249,7 @@ public void test_addProperty_ThrowsAnException_WhenTheNameIsEmpty() { } @Test - public void test_addProperty_ThrowsAnException_WhenTheValueIsNull() { + void test_addProperty_ThrowsAnException_WhenTheValueIsNull() { try { ElementStyle style = new ElementStyle(); style.addProperty("name", null); @@ -246,7 +260,7 @@ public void test_addProperty_ThrowsAnException_WhenTheValueIsNull() { } @Test - public void test_addProperty_ThrowsAnException_WhenTheValueIsEmpty() { + void test_addProperty_ThrowsAnException_WhenTheValueIsEmpty() { try { ElementStyle style = new ElementStyle(); style.addProperty("name", " "); @@ -257,21 +271,21 @@ public void test_addProperty_ThrowsAnException_WhenTheValueIsEmpty() { } @Test - public void test_addProperty_AddsTheProperty_WhenANameAndValueAreSpecified() { + void test_addProperty_AddsTheProperty_WhenANameAndValueAreSpecified() { ElementStyle style = new ElementStyle(); style.addProperty("name", "value"); assertEquals("value", style.getProperties().get("name")); } @Test - public void test_setProperties_DoesNothing_WhenNullIsSpecified() { + void test_setProperties_DoesNothing_WhenNullIsSpecified() { ElementStyle style = new ElementStyle(); style.setProperties(null); assertEquals(0, style.getProperties().size()); } @Test - public void test_setProperties_SetsTheProperties_WhenANonEmptyMapIsSpecified() { + void test_setProperties_SetsTheProperties_WhenANonEmptyMapIsSpecified() { ElementStyle style = new ElementStyle(); Map properties = new HashMap<>(); properties.put("name", "value"); diff --git a/structurizr-core/test/unit/com/structurizr/view/ElementViewTests.java b/structurizr-core/test/unit/com/structurizr/view/ElementViewTests.java index a1f1fcd63..a768f19f1 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ElementViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ElementViewTests.java @@ -3,21 +3,21 @@ import com.structurizr.AbstractWorkspaceTestBase; import com.structurizr.model.Element; import com.structurizr.model.Location; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class ElementViewTests extends AbstractWorkspaceTestBase { @Test - public void test_copyLayoutInformationFrom_DoesNothing_WhenNullIsPassed() { + void test_copyLayoutInformationFrom_DoesNothing_WhenNullIsPassed() { Element element = model.addSoftwareSystem(Location.External, "SystemA", ""); ElementView elementView = new ElementView(element); elementView.copyLayoutInformationFrom(null); } @Test - public void test_copyLayoutInformationFrom_CopiesXAndY_WhenANonNullElementViewIsPassed() { + void test_copyLayoutInformationFrom_CopiesXAndY_WhenANonNullElementViewIsPassed() { Element element = model.addSoftwareSystem(Location.External, "SystemA", ""); ElementView elementView1 = new ElementView(element); assertEquals(0, elementView1.getX()); diff --git a/structurizr-core/test/unit/com/structurizr/view/FilteredViewTests.java b/structurizr-core/test/unit/com/structurizr/view/FilteredViewTests.java index 83f4ee965..47837ceb9 100644 --- a/structurizr-core/test/unit/com/structurizr/view/FilteredViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/FilteredViewTests.java @@ -2,14 +2,14 @@ import com.structurizr.AbstractWorkspaceTestBase; import com.structurizr.model.SoftwareSystem; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class FilteredViewTests extends AbstractWorkspaceTestBase { @Test - public void test_construction() { + void test_construction() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); SystemContextView systemContextView = views.createSystemContextView(softwareSystem, "SystemContext", "Description"); FilteredView filteredView = views.createFilteredView( diff --git a/structurizr-core/test/unit/com/structurizr/view/FontTests.java b/structurizr-core/test/unit/com/structurizr/view/FontTests.java index 435c759df..4839fc961 100644 --- a/structurizr-core/test/unit/com/structurizr/view/FontTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/FontTests.java @@ -1,52 +1,53 @@ package com.structurizr.view; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.*; public class FontTests { private Font font; - @Before + @BeforeEach public void setUp() { this.font = new Font(); } @Test - public void construction_WithANameOnly() { + void construction_WithANameOnly() { this.font = new Font("Times New Roman"); assertEquals("Times New Roman", font.getName()); } @Test - public void construction_WithANameAndUrl() { + void construction_WithANameAndUrl() { this.font = new Font("Open Sans", "https://fonts.googleapis.com/css?family=Open+Sans:400,700"); assertEquals("Open Sans", font.getName()); assertEquals("https://fonts.googleapis.com/css?family=Open+Sans:400,700", font.getUrl()); } @Test - public void test_setUrl_WithAUrl() { + void test_setUrl_WithAUrl() { font.setUrl("https://fonts.googleapis.com/css?family=Open+Sans:400,700"); assertEquals("https://fonts.googleapis.com/css?family=Open+Sans:400,700", font.getUrl()); } - @Test(expected = IllegalArgumentException.class) - public void test_setUrl_ThrowsAnIllegalArgumentException_WhenAnInvalidUrlIsSpecified() { - font.setUrl("htt://blah"); + @Test + void test_setUrl_ThrowsAnIllegalArgumentException_WhenAnInvalidUrlIsSpecified() { + assertThrows(IllegalArgumentException.class, () -> { + font.setUrl("htt://blah"); + }); } @Test - public void test_setUrl_DoesNothing_WhenANullUrlIsSpecified() { + void test_setUrl_DoesNothing_WhenANullUrlIsSpecified() { font.setUrl(null); assertNull(font.getUrl()); } @Test - public void test_setUrl_DoesNothing_WhenAnEmptyUrlIsSpecified() { + void test_setUrl_DoesNothing_WhenAnEmptyUrlIsSpecified() { font.setUrl(" "); assertNull(font.getUrl()); } diff --git a/structurizr-core/test/unit/com/structurizr/view/PaperSizeTests.java b/structurizr-core/test/unit/com/structurizr/view/PaperSizeTests.java index 70f4e1ac3..52cd80477 100644 --- a/structurizr-core/test/unit/com/structurizr/view/PaperSizeTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/PaperSizeTests.java @@ -1,15 +1,15 @@ package com.structurizr.view; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.List; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.assertEquals; public class PaperSizeTests { @Test - public void test_getOrderedPaperSizes_WhenOrientationIsLandscape() { + void test_getOrderedPaperSizes_WhenOrientationIsLandscape() { List paperSizes = PaperSize.getOrderedPaperSizes(PaperSize.Orientation.Landscape); assertEquals(12, paperSizes.size()); @@ -29,7 +29,7 @@ public void test_getOrderedPaperSizes_WhenOrientationIsLandscape() { } @Test - public void test_getOrderedPaperSizes_WhenOrientationIsPortrait() { + void test_getOrderedPaperSizes_WhenOrientationIsPortrait() { List paperSizes = PaperSize.getOrderedPaperSizes(PaperSize.Orientation.Portrait); assertEquals(9, paperSizes.size()); @@ -46,7 +46,7 @@ public void test_getOrderedPaperSizes_WhenOrientationIsPortrait() { } @Test - public void test_getOrderedPaperSizes() { + void test_getOrderedPaperSizes() { List paperSizes = PaperSize.getOrderedPaperSizes(); assertEquals(21, paperSizes.size()); diff --git a/structurizr-core/test/unit/com/structurizr/view/RelationshipStyleTests.java b/structurizr-core/test/unit/com/structurizr/view/RelationshipStyleTests.java index 939770e33..bc95ba423 100644 --- a/structurizr-core/test/unit/com/structurizr/view/RelationshipStyleTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/RelationshipStyleTests.java @@ -1,36 +1,36 @@ package com.structurizr.view; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class RelationshipStyleTests { private RelationshipStyle relationshipStyle = new RelationshipStyle("tag"); @Test - public void test_setPosition_SetsPositionToNull_WhenNullIsSpecified() { + void test_setPosition_SetsPositionToNull_WhenNullIsSpecified() { relationshipStyle.setPosition(null); assertNull(relationshipStyle.getPosition()); } @Test - public void test_setPosition_SetsPositionToZero_WhenANegativeNumberIsSpecified() { + void test_setPosition_SetsPositionToZero_WhenANegativeNumberIsSpecified() { relationshipStyle.setPosition(-1); assertEquals(Integer.valueOf(0), relationshipStyle.getPosition()); } @Test - public void test_setPosition_SetsPositionToOneHundred_WhenANumberGreaterThanOneHundredIsSpecified() { + void test_setPosition_SetsPositionToOneHundred_WhenANumberGreaterThanOneHundredIsSpecified() { relationshipStyle.setPosition(101); assertEquals(Integer.valueOf(100), relationshipStyle.getPosition()); } @Test - public void test_setPosition_SetsPosition_WhenANumberBetweenZeroAndOneHundredIsSpecified() { + void test_setPosition_SetsPosition_WhenANumberBetweenZeroAndOneHundredIsSpecified() { relationshipStyle.setPosition(0); assertEquals(Integer.valueOf(0), relationshipStyle.getPosition()); @@ -49,7 +49,7 @@ public void test_setPosition_SetsPosition_WhenANumberBetweenZeroAndOneHundredIsS } @Test - public void test_setOpacity() { + void test_setOpacity() { RelationshipStyle style = new RelationshipStyle(); assertNull(style.getOpacity()); @@ -70,7 +70,7 @@ public void test_setOpacity() { } @Test - public void test_opacity() { + void test_opacity() { RelationshipStyle style = new RelationshipStyle(); assertNull(style.getOpacity()); @@ -91,7 +91,7 @@ public void test_opacity() { } @Test - public void test_setColor_SetsTheColorProperty_WhenAValidHexColorCodeIsSpecified() { + void test_setColor_SetsTheColorProperty_WhenAValidHexColorCodeIsSpecified() { RelationshipStyle style = new RelationshipStyle(); style.setColor("#ffffff"); assertEquals("#ffffff", style.getColor()); @@ -104,7 +104,7 @@ public void test_setColor_SetsTheColorProperty_WhenAValidHexColorCodeIsSpecified } @Test - public void test_color_SetsTheColorProperty_WhenAValidHexColorCodeIsSpecified() { + void test_color_SetsTheColorProperty_WhenAValidHexColorCodeIsSpecified() { RelationshipStyle style = new RelationshipStyle(); style.color("#ffffff"); assertEquals("#ffffff", style.getColor()); @@ -116,26 +116,30 @@ public void test_color_SetsTheColorProperty_WhenAValidHexColorCodeIsSpecified() assertEquals("#123456", style.getColor()); } - @Test(expected = IllegalArgumentException.class) - public void test_setColor_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { - RelationshipStyle style = new RelationshipStyle(); - style.setColor("white"); + @Test + void test_setColor_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { + assertThrows(IllegalArgumentException.class, () -> { + RelationshipStyle style = new RelationshipStyle(); + style.setColor("white"); + }); } - @Test(expected = IllegalArgumentException.class) - public void test_color_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { - RelationshipStyle style = new RelationshipStyle(); - style.color("white"); + @Test + void test_color_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { + assertThrows(IllegalArgumentException.class, () -> { + RelationshipStyle style = new RelationshipStyle(); + style.color("white"); + }); } @Test - public void test_getProperties_ReturnsAnEmptyList_WhenNoPropertiesHaveBeenAdded() { + void test_getProperties_ReturnsAnEmptyList_WhenNoPropertiesHaveBeenAdded() { RelationshipStyle style = new RelationshipStyle(); assertEquals(0, style.getProperties().size()); } @Test - public void test_addProperty_ThrowsAnException_WhenTheNameIsNull() { + void test_addProperty_ThrowsAnException_WhenTheNameIsNull() { try { RelationshipStyle style = new RelationshipStyle(); style.addProperty(null, "value"); @@ -146,7 +150,7 @@ public void test_addProperty_ThrowsAnException_WhenTheNameIsNull() { } @Test - public void test_addProperty_ThrowsAnException_WhenTheNameIsEmpty() { + void test_addProperty_ThrowsAnException_WhenTheNameIsEmpty() { try { RelationshipStyle style = new RelationshipStyle(); style.addProperty(" ", "value"); @@ -157,7 +161,7 @@ public void test_addProperty_ThrowsAnException_WhenTheNameIsEmpty() { } @Test - public void test_addProperty_ThrowsAnException_WhenTheValueIsNull() { + void test_addProperty_ThrowsAnException_WhenTheValueIsNull() { try { RelationshipStyle style = new RelationshipStyle(); style.addProperty("name", null); @@ -168,7 +172,7 @@ public void test_addProperty_ThrowsAnException_WhenTheValueIsNull() { } @Test - public void test_addProperty_ThrowsAnException_WhenTheValueIsEmpty() { + void test_addProperty_ThrowsAnException_WhenTheValueIsEmpty() { try { RelationshipStyle style = new RelationshipStyle(); style.addProperty("name", " "); @@ -179,21 +183,21 @@ public void test_addProperty_ThrowsAnException_WhenTheValueIsEmpty() { } @Test - public void test_addProperty_AddsTheProperty_WhenANameAndValueAreSpecified() { + void test_addProperty_AddsTheProperty_WhenANameAndValueAreSpecified() { RelationshipStyle style = new RelationshipStyle(); style.addProperty("name", "value"); assertEquals("value", style.getProperties().get("name")); } @Test - public void test_setProperties_DoesNothing_WhenNullIsSpecified() { + void test_setProperties_DoesNothing_WhenNullIsSpecified() { RelationshipStyle style = new RelationshipStyle(); style.setProperties(null); assertEquals(0, style.getProperties().size()); } @Test - public void test_setProperties_SetsTheProperties_WhenANonEmptyMapIsSpecified() { + void test_setProperties_SetsTheProperties_WhenANonEmptyMapIsSpecified() { RelationshipStyle style = new RelationshipStyle(); Map properties = new HashMap<>(); properties.put("name", "value"); diff --git a/structurizr-core/test/unit/com/structurizr/view/SequenceCounterTests.java b/structurizr-core/test/unit/com/structurizr/view/SequenceCounterTests.java index 69da2fd5c..1b77117a8 100644 --- a/structurizr-core/test/unit/com/structurizr/view/SequenceCounterTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/SequenceCounterTests.java @@ -1,13 +1,13 @@ package com.structurizr.view; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class SequenceCounterTests { @Test - public void test_increment_IncrementsTheCounter_WhenThereIsNoParent() { + void test_increment_IncrementsTheCounter_WhenThereIsNoParent() { SequenceCounter counter = new SequenceCounter(); assertEquals("0", counter.toString()); diff --git a/structurizr-core/test/unit/com/structurizr/view/SequenceNumberTests.java b/structurizr-core/test/unit/com/structurizr/view/SequenceNumberTests.java index 5984b6f39..bb5404cc2 100644 --- a/structurizr-core/test/unit/com/structurizr/view/SequenceNumberTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/SequenceNumberTests.java @@ -1,20 +1,20 @@ package com.structurizr.view; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class SequenceNumberTests { @Test - public void test_increment() { + void test_increment() { SequenceNumber sequenceNumber = new SequenceNumber(); assertEquals("1", sequenceNumber.getNext()); assertEquals("2", sequenceNumber.getNext()); } @Test - public void test_parallelSequences() { + void test_parallelSequences() { SequenceNumber sequenceNumber = new SequenceNumber(); assertEquals("1", sequenceNumber.getNext()); diff --git a/structurizr-core/test/unit/com/structurizr/view/StaticViewTests.java b/structurizr-core/test/unit/com/structurizr/view/StaticViewTests.java index b15f4aa05..c23526029 100644 --- a/structurizr-core/test/unit/com/structurizr/view/StaticViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/StaticViewTests.java @@ -3,14 +3,14 @@ import com.structurizr.AbstractWorkspaceTestBase; import com.structurizr.model.Relationship; import com.structurizr.model.SoftwareSystem; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class StaticViewTests extends AbstractWorkspaceTestBase { @Test - public void test_addAnimationStep_ThrowsAnException_WhenNoElementsAreSpecified() { + void test_addAnimationStep_ThrowsAnException_WhenNoElementsAreSpecified() { try { SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "Description"); view.addAnimation(); @@ -21,7 +21,7 @@ public void test_addAnimationStep_ThrowsAnException_WhenNoElementsAreSpecified() } @Test - public void test_addAnimationStep() { + void test_addAnimationStep() { SoftwareSystem element1 = model.addSoftwareSystem("Software System 1", ""); SoftwareSystem element2 = model.addSoftwareSystem("Software System 2", ""); SoftwareSystem element3 = model.addSoftwareSystem("Software System 3", ""); @@ -55,7 +55,7 @@ public void test_addAnimationStep() { } @Test - public void test_addAnimationStep_IgnoresElementsThatDoNotExistInTheView() { + void test_addAnimationStep_IgnoresElementsThatDoNotExistInTheView() { SoftwareSystem element1 = model.addSoftwareSystem("Software System 1", ""); SoftwareSystem element2 = model.addSoftwareSystem("Software System 2", ""); @@ -69,7 +69,7 @@ public void test_addAnimationStep_IgnoresElementsThatDoNotExistInTheView() { } @Test - public void test_addAnimationStep_ThrowsAnException_WhenElementsAreSpecifiedButNoneOfThemExistInTheView() { + void test_addAnimationStep_ThrowsAnException_WhenElementsAreSpecifiedButNoneOfThemExistInTheView() { try { SoftwareSystem element1 = model.addSoftwareSystem("Software System 1", ""); diff --git a/structurizr-core/test/unit/com/structurizr/view/StylesTests.java b/structurizr-core/test/unit/com/structurizr/view/StylesTests.java index 5857a8076..99182c7a3 100644 --- a/structurizr-core/test/unit/com/structurizr/view/StylesTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/StylesTests.java @@ -2,17 +2,17 @@ import com.structurizr.AbstractWorkspaceTestBase; import com.structurizr.model.*; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class StylesTests extends AbstractWorkspaceTestBase { private Styles styles = new Styles(); @Test - public void test_findElementStyle_ReturnsTheDefaultStyle_WhenPassedNull() { - ElementStyle style = styles.findElementStyle((Element)null); + void test_findElementStyle_ReturnsTheDefaultStyle_WhenPassedNull() { + ElementStyle style = styles.findElementStyle((Element) null); assertEquals(Integer.valueOf(450), style.getWidth()); assertEquals(Integer.valueOf(300), style.getHeight()); assertEquals("#dddddd", style.getBackground()); @@ -28,7 +28,7 @@ public void test_findElementStyle_ReturnsTheDefaultStyle_WhenPassedNull() { } @Test - public void test_findElementStyle_ReturnsTheDefaultStyle_WhenNoStylesAreDefined() { + void test_findElementStyle_ReturnsTheDefaultStyle_WhenNoStylesAreDefined() { SoftwareSystem element = model.addSoftwareSystem("Name", "Description"); ElementStyle style = styles.findElementStyle(element); assertEquals(Integer.valueOf(450), style.getWidth()); @@ -46,7 +46,7 @@ public void test_findElementStyle_ReturnsTheDefaultStyle_WhenNoStylesAreDefined( } @Test - public void test_findElementStyle_ReturnsTheCorrectStyle_WhenStylesAreDefined() { + void test_findElementStyle_ReturnsTheCorrectStyle_WhenStylesAreDefined() { SoftwareSystem element = model.addSoftwareSystem("Name", "Description"); element.addTags("Some Tag"); @@ -69,7 +69,7 @@ public void test_findElementStyle_ReturnsTheCorrectStyle_WhenStylesAreDefined() } @Test - public void test_findElementStyle_ReturnsTheCorrectStyleForAnElementInstance_WhenStylesAreDefined() { + void test_findElementStyle_ReturnsTheCorrectStyleForAnElementInstance_WhenStylesAreDefined() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Name"); softwareSystem.addTags("Some Tag"); @@ -95,7 +95,7 @@ public void test_findElementStyle_ReturnsTheCorrectStyleForAnElementInstance_Whe } @Test - public void test_findElementStyle_ReturnsTheDefaultElementSize_WhenTheShapeIsABox() { + void test_findElementStyle_ReturnsTheDefaultElementSize_WhenTheShapeIsABox() { SoftwareSystem element = model.addSoftwareSystem("Name", "Description"); element.addTags("Some Tag"); @@ -109,7 +109,7 @@ public void test_findElementStyle_ReturnsTheDefaultElementSize_WhenTheShapeIsABo } @Test - public void test_findElementStyle_ReturnsTheDefaultElementSize_WhenTheShapeIsAPerson() { + void test_findElementStyle_ReturnsTheDefaultElementSize_WhenTheShapeIsAPerson() { SoftwareSystem element = model.addSoftwareSystem("Name", "Description"); element.addTags("Some Tag"); @@ -123,8 +123,8 @@ public void test_findElementStyle_ReturnsTheDefaultElementSize_WhenTheShapeIsAPe } @Test - public void test_findRelationshipStyle_ReturnsTheDefaultStyle_WhenPassedNull() { - RelationshipStyle style = styles.findRelationshipStyle((Relationship)null); + void test_findRelationshipStyle_ReturnsTheDefaultStyle_WhenPassedNull() { + RelationshipStyle style = styles.findRelationshipStyle((Relationship) null); assertEquals(Integer.valueOf(2), style.getThickness()); assertEquals("#707070", style.getColor()); assertTrue(style.getDashed()); @@ -136,7 +136,7 @@ public void test_findRelationshipStyle_ReturnsTheDefaultStyle_WhenPassedNull() { } @Test - public void test_findRelationshipStyle_ReturnsTheDefaultStyle_WhenNoStylesAreDefined() { + void test_findRelationshipStyle_ReturnsTheDefaultStyle_WhenNoStylesAreDefined() { SoftwareSystem element = model.addSoftwareSystem("Name", "Description"); Relationship relationship = element.uses(element, "Uses"); RelationshipStyle style = styles.findRelationshipStyle(relationship); @@ -151,7 +151,7 @@ public void test_findRelationshipStyle_ReturnsTheDefaultStyle_WhenNoStylesAreDef } @Test - public void test_findRelationshipStyle_ReturnsTheCorrectStyle_WhenStylesAreDefined() { + void test_findRelationshipStyle_ReturnsTheCorrectStyle_WhenStylesAreDefined() { SoftwareSystem element = model.addSoftwareSystem("Name", "Description"); Relationship relationship = element.uses(element, "Uses"); relationship.addTags("Some Tag"); @@ -171,7 +171,7 @@ public void test_findRelationshipStyle_ReturnsTheCorrectStyle_WhenStylesAreDefin } @Test - public void test_findRelationshipStyle_ReturnsTheCorrectStyle_WhenThereIsALinkedRelationship() { + void test_findRelationshipStyle_ReturnsTheCorrectStyle_WhenThereIsALinkedRelationship() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); Container container1 = softwareSystem.addContainer("Container 1", "Description", "Technology"); Container container2 = softwareSystem.addContainer("Container 2", "Description", "Technology"); @@ -194,7 +194,7 @@ public void test_findRelationshipStyle_ReturnsTheCorrectStyle_WhenThereIsALinked } @Test - public void test_findRelationshipStyle_ReturnsTheCorrectStyle_WhenThereIsALinkedRelationshipBasedUponAnImpliedRelationship() { + void test_findRelationshipStyle_ReturnsTheCorrectStyle_WhenThereIsALinkedRelationshipBasedUponAnImpliedRelationship() { model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); Container container1 = softwareSystem.addContainer("Container 1"); @@ -220,7 +220,7 @@ public void test_findRelationshipStyle_ReturnsTheCorrectStyle_WhenThereIsALinked } @Test - public void test_addElementStyle_ThrowsAnException_WhenATagIsNotSpecified() { + void test_addElementStyle_ThrowsAnException_WhenATagIsNotSpecified() { try { styles.addElementStyle(""); fail(); @@ -244,7 +244,7 @@ public void test_addElementStyle_ThrowsAnException_WhenATagIsNotSpecified() { } @Test - public void test_addElementStyleByTag_ThrowsAnException_WhenAStyleWithTheSameTagExistsAlready() { + void test_addElementStyleByTag_ThrowsAnException_WhenAStyleWithTheSameTagExistsAlready() { try { styles.addElementStyle(Tags.SOFTWARE_SYSTEM).color("#ff0000"); styles.addElementStyle(Tags.SOFTWARE_SYSTEM).color("#ff0000"); @@ -256,7 +256,7 @@ public void test_addElementStyleByTag_ThrowsAnException_WhenAStyleWithTheSameTag } @Test - public void test_addElementStyle_ThrowsAnException_WhenAStyleWithTheSameTagExistsAlready() { + void test_addElementStyle_ThrowsAnException_WhenAStyleWithTheSameTagExistsAlready() { try { ElementStyle style = styles.addElementStyle(Tags.SOFTWARE_SYSTEM).color("#ff0000"); styles.add(style); @@ -268,7 +268,7 @@ public void test_addElementStyle_ThrowsAnException_WhenAStyleWithTheSameTagExist } @Test - public void test_addRelationshipStyle_ThrowsAnException_WhenATagIsNotSpecified() { + void test_addRelationshipStyle_ThrowsAnException_WhenATagIsNotSpecified() { try { styles.addRelationshipStyle(""); fail(); @@ -292,7 +292,7 @@ public void test_addRelationshipStyle_ThrowsAnException_WhenATagIsNotSpecified() } @Test - public void test_addRelationshipStyleByTag_ThrowsAnException_WhenAStyleWithTheSameTagExistsAlready() { + void test_addRelationshipStyleByTag_ThrowsAnException_WhenAStyleWithTheSameTagExistsAlready() { try { styles.addRelationshipStyle(Tags.RELATIONSHIP).color("#ff0000"); styles.addRelationshipStyle(Tags.RELATIONSHIP).color("#ff0000"); @@ -304,7 +304,7 @@ public void test_addRelationshipStyleByTag_ThrowsAnException_WhenAStyleWithTheSa } @Test - public void test_addRelationshipStyle_ThrowsAnException_WhenAStyleWithTheSameTagExistsAlready() { + void test_addRelationshipStyle_ThrowsAnException_WhenAStyleWithTheSameTagExistsAlready() { try { RelationshipStyle style = styles.addRelationshipStyle(Tags.RELATIONSHIP).color("#ff0000"); styles.add(style); @@ -316,7 +316,7 @@ public void test_addRelationshipStyle_ThrowsAnException_WhenAStyleWithTheSameTag } @Test - public void test_clearElementStyles_RemovesAllElementStyles() { + void test_clearElementStyles_RemovesAllElementStyles() { styles.addElementStyle(Tags.SOFTWARE_SYSTEM).color("#ff0000"); assertEquals(1, styles.getElements().size()); @@ -325,7 +325,7 @@ public void test_clearElementStyles_RemovesAllElementStyles() { } @Test - public void test_clearRelationshipStyles_RemovesAllRelationshipStyles() { + void test_clearRelationshipStyles_RemovesAllRelationshipStyles() { styles.addRelationshipStyle(Tags.RELATIONSHIP).color("#ff0000"); assertEquals(1, styles.getRelationships().size()); diff --git a/structurizr-core/test/unit/com/structurizr/view/SystemContextViewTests.java b/structurizr-core/test/unit/com/structurizr/view/SystemContextViewTests.java index 61517f64b..dc37a0cf1 100644 --- a/structurizr-core/test/unit/com/structurizr/view/SystemContextViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/SystemContextViewTests.java @@ -2,24 +2,24 @@ import com.structurizr.AbstractWorkspaceTestBase; import com.structurizr.model.*; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class SystemContextViewTests extends AbstractWorkspaceTestBase { private SoftwareSystem softwareSystem; private SystemContextView view; - @Before + @BeforeEach public void setUp() { softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); view = new SystemContextView(softwareSystem, "context", "Description"); } @Test - public void test_construction() { + void test_construction() { assertEquals("The System - System Context", view.getName()); assertEquals(1, view.getElements().size()); assertSame(view.getElements().iterator().next().getElement(), softwareSystem); @@ -29,14 +29,14 @@ public void test_construction() { } @Test - public void test_addAllSoftwareSystems_DoesNothing_WhenThereAreNoOtherSoftwareSystems() { + void test_addAllSoftwareSystems_DoesNothing_WhenThereAreNoOtherSoftwareSystems() { assertEquals(1, view.getElements().size()); view.addAllSoftwareSystems(); assertEquals(1, view.getElements().size()); } @Test - public void test_addAllSoftwareSystems_AddsAllSoftwareSystems_WhenThereAreSomeSoftwareSystemsInTheModel() { + void test_addAllSoftwareSystems_AddsAllSoftwareSystems_WhenThereAreSomeSoftwareSystemsInTheModel() { SoftwareSystem softwareSystemA = model.addSoftwareSystem(Location.External, "System A", "Description"); SoftwareSystem softwareSystemB = model.addSoftwareSystem(Location.External, "System B", "Description"); @@ -49,14 +49,14 @@ public void test_addAllSoftwareSystems_AddsAllSoftwareSystems_WhenThereAreSomeSo } @Test - public void test_addAllPeople_DoesNothing_WhenThereAreNoPeople() { + void test_addAllPeople_DoesNothing_WhenThereAreNoPeople() { assertEquals(1, view.getElements().size()); view.addAllPeople(); assertEquals(1, view.getElements().size()); } @Test - public void test_addAllPeople_AddsAllPeople_WhenThereAreSomePeopleInTheModel() { + void test_addAllPeople_AddsAllPeople_WhenThereAreSomePeopleInTheModel() { Person userA = model.addPerson(Location.External, "User A", "Description"); Person userB = model.addPerson(Location.External, "User B", "Description"); @@ -69,14 +69,14 @@ public void test_addAllPeople_AddsAllPeople_WhenThereAreSomePeopleInTheModel() { } @Test - public void test_addAllElements_DoesNothing_WhenThereAreNoSoftwareSystemsOrPeople() { + void test_addAllElements_DoesNothing_WhenThereAreNoSoftwareSystemsOrPeople() { assertEquals(1, view.getElements().size()); view.addAllElements(); assertEquals(1, view.getElements().size()); } @Test - public void test_addAllElements_AddsAllSoftwareSystemsAndPeople_WhenThereAreSomeSoftwareSystemsAndPeopleInTheModel() { + void test_addAllElements_AddsAllSoftwareSystemsAndPeople_WhenThereAreSomeSoftwareSystemsAndPeopleInTheModel() { SoftwareSystem softwareSystemA = model.addSoftwareSystem(Location.External, "System A", "Description"); SoftwareSystem softwareSystemB = model.addSoftwareSystem(Location.External, "System B", "Description"); Person userA = model.addPerson(Location.External, "User A", "Description"); @@ -93,7 +93,7 @@ public void test_addAllElements_AddsAllSoftwareSystemsAndPeople_WhenThereAreSome } @Test - public void test_addNearestNeighbours_ThrowsAnException_WhenANullElementIsSpecified() { + void test_addNearestNeighbours_ThrowsAnException_WhenANullElementIsSpecified() { try { view.addNearestNeighbours(null); fail(); @@ -103,7 +103,7 @@ public void test_addNearestNeighbours_ThrowsAnException_WhenANullElementIsSpecif } @Test - public void test_addNearestNeighbours_ThrowsAnException_WhenAnElementThatIsNotAPersonOrSoftwareSystemIsSpecified() { + void test_addNearestNeighbours_ThrowsAnException_WhenAnElementThatIsNotAPersonOrSoftwareSystemIsSpecified() { Container container = softwareSystem.addContainer("Container", "Description", "Technology"); try { view.addNearestNeighbours(container); @@ -114,14 +114,14 @@ public void test_addNearestNeighbours_ThrowsAnException_WhenAnElementThatIsNotAP } @Test - public void test_addNearestNeighbours_DoesNothing_WhenThereAreNoNeighbours() { + void test_addNearestNeighbours_DoesNothing_WhenThereAreNoNeighbours() { view.addNearestNeighbours(softwareSystem); assertEquals(1, view.getElements().size()); } @Test - public void test_addNearestNeighbours_AddsNearestNeighbours_WhenThereAreSomeNearestNeighbours() { + void test_addNearestNeighbours_AddsNearestNeighbours_WhenThereAreSomeNearestNeighbours() { SoftwareSystem softwareSystemA = model.addSoftwareSystem("A", "Description"); SoftwareSystem softwareSystemB = model.addSoftwareSystem("B", "Description"); Person userA = model.addPerson("User A", "Description"); @@ -170,9 +170,9 @@ public void test_addNearestNeighbours_AddsNearestNeighbours_WhenThereAreSomeNear } @Test - public void test_removeSoftwareSystem_ThrowsAnException_WhenPassedNull() { + void test_removeSoftwareSystem_ThrowsAnException_WhenPassedNull() { try { - view.remove((SoftwareSystem)null); + view.remove((SoftwareSystem) null); fail(); } catch (IllegalArgumentException iae) { assertEquals("An element must be specified.", iae.getMessage()); @@ -180,7 +180,7 @@ public void test_removeSoftwareSystem_ThrowsAnException_WhenPassedNull() { } @Test - public void test_removeSoftwareSystem_DoesNothing_WhenTheSoftwareSystemIsNotInTheView() { + void test_removeSoftwareSystem_DoesNothing_WhenTheSoftwareSystemIsNotInTheView() { SoftwareSystem anotherSoftwareSystem = model.addSoftwareSystem("Another software system", ""); assertEquals(1, view.getElements().size()); @@ -189,7 +189,7 @@ public void test_removeSoftwareSystem_DoesNothing_WhenTheSoftwareSystemIsNotInTh } @Test - public void test_removeSoftwareSystem_DoesNotRemoveTheSoftwareSystemInFocus() { + void test_removeSoftwareSystem_DoesNotRemoveTheSoftwareSystemInFocus() { try { view.remove(softwareSystem); fail(); @@ -199,7 +199,7 @@ public void test_removeSoftwareSystem_DoesNotRemoveTheSoftwareSystemInFocus() { } @Test - public void test_removeSoftwareSystem_RemovesTheSoftwareSystemAndRelationshipsFromTheView() { + void test_removeSoftwareSystem_RemovesTheSoftwareSystemAndRelationshipsFromTheView() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software system 1", ""); SoftwareSystem softwareSystem2 = model.addSoftwareSystem("Software system 2", ""); softwareSystem1.uses(softwareSystem2, "uses"); @@ -215,9 +215,9 @@ public void test_removeSoftwareSystem_RemovesTheSoftwareSystemAndRelationshipsFr } @Test - public void test_removePerson_ThrowsAnException_WhenPassedNull() { + void test_removePerson_ThrowsAnException_WhenPassedNull() { try { - view.remove((Person)null); + view.remove((Person) null); fail(); } catch (IllegalArgumentException iae) { assertEquals("An element must be specified.", iae.getMessage()); @@ -225,7 +225,7 @@ public void test_removePerson_ThrowsAnException_WhenPassedNull() { } @Test - public void test_removePerson_DoesNothing_WhenThePersonIsNotInTheView() { + void test_removePerson_DoesNothing_WhenThePersonIsNotInTheView() { Person person = model.addPerson("Person", ""); assertEquals(1, view.getElements().size()); @@ -234,7 +234,7 @@ public void test_removePerson_DoesNothing_WhenThePersonIsNotInTheView() { } @Test - public void test_removePerson_RemovesThePersonAndRelationshipsFromTheView() { + void test_removePerson_RemovesThePersonAndRelationshipsFromTheView() { Person person = model.addPerson("Person", ""); person.uses(softwareSystem, "uses"); softwareSystem.delivers(person, "delivers something to"); @@ -249,7 +249,7 @@ public void test_removePerson_RemovesThePersonAndRelationshipsFromTheView() { } @Test - public void test_addSoftwareSystemWithoutRelationships_DoesNotAddRelationships() { + void test_addSoftwareSystemWithoutRelationships_DoesNotAddRelationships() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software system 1", ""); SoftwareSystem softwareSystem2 = model.addSoftwareSystem("Software system 2", ""); softwareSystem1.uses(softwareSystem2, "uses"); @@ -261,7 +261,7 @@ public void test_addSoftwareSystemWithoutRelationships_DoesNotAddRelationships() } @Test - public void test_addPersonWithoutRelationships_DoesNotAddRelationships() { + void test_addPersonWithoutRelationships_DoesNotAddRelationships() { Person user = model.addPerson("User", ""); SoftwareSystem softwareSystem = model.addSoftwareSystem("Software system 2", ""); user.uses(softwareSystem, "uses"); @@ -273,7 +273,7 @@ public void test_addPersonWithoutRelationships_DoesNotAddRelationships() { } @Test - public void test_isEnterpriseBoundaryVisible() { + void test_isEnterpriseBoundaryVisible() { assertTrue(view.isEnterpriseBoundaryVisible()); // default is true view.setEnterpriseBoundaryVisible(false); @@ -281,7 +281,7 @@ public void test_isEnterpriseBoundaryVisible() { } @Test - public void test_addDefaultElements() { + void test_addDefaultElements() { CustomElement element = model.addCustomElement("Custom"); Person user1 = model.addPerson("User 1"); Person user2 = model.addPerson("User 2"); diff --git a/structurizr-core/test/unit/com/structurizr/view/SystemLandscapeViewTests.java b/structurizr-core/test/unit/com/structurizr/view/SystemLandscapeViewTests.java index 833bda34d..9e0838836 100644 --- a/structurizr-core/test/unit/com/structurizr/view/SystemLandscapeViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/SystemLandscapeViewTests.java @@ -2,56 +2,60 @@ import com.structurizr.AbstractWorkspaceTestBase; import com.structurizr.model.*; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class SystemLandscapeViewTests extends AbstractWorkspaceTestBase { private SystemLandscapeView view; - @Before + @BeforeEach public void setUp() { view = new SystemLandscapeView(model, "context", "Description"); } @Test - public void test_construction() { + void test_construction() { assertEquals("System Landscape", view.getName()); assertEquals(0, view.getElements().size()); assertSame(model, view.getModel()); } @Test - public void test_getName_WhenNoEnterpriseIsSpecified() { + void test_getName_WhenNoEnterpriseIsSpecified() { assertEquals("System Landscape", view.getName()); } @Test - public void test_getName_WhenAnEnterpriseIsSpecified() { + void test_getName_WhenAnEnterpriseIsSpecified() { model.setEnterprise(new Enterprise("Widgets Limited")); assertEquals("System Landscape for Widgets Limited", view.getName()); } - @Test(expected = IllegalArgumentException.class) - public void test_getName_WhenAnEmptyEnterpriseNameIsSpecified() { - model.setEnterprise(new Enterprise("")); + @Test + void test_getName_WhenAnEmptyEnterpriseNameIsSpecified() { + assertThrows(IllegalArgumentException.class, () -> { + model.setEnterprise(new Enterprise("")); + }); } - @Test(expected = IllegalArgumentException.class) - public void test_getName_WhenANullEnterpriseNameIsSpecified() { - model.setEnterprise(new Enterprise(null)); + @Test + void test_getName_WhenANullEnterpriseNameIsSpecified() { + assertThrows(IllegalArgumentException.class, () -> { + model.setEnterprise(new Enterprise(null)); + }); } @Test - public void test_addAllSoftwareSystems_DoesNothing_WhenThereAreNoOtherSoftwareSystems() { + void test_addAllSoftwareSystems_DoesNothing_WhenThereAreNoOtherSoftwareSystems() { view.addAllSoftwareSystems(); assertEquals(0, view.getElements().size()); } @Test - public void test_addAllSoftwareSystems_AddsAllSoftwareSystems_WhenThereAreSomeSoftwareSystemsInTheModel() { + void test_addAllSoftwareSystems_AddsAllSoftwareSystems_WhenThereAreSomeSoftwareSystemsInTheModel() { SoftwareSystem softwareSystemA = model.addSoftwareSystem(Location.External, "System A", "Description"); SoftwareSystem softwareSystemB = model.addSoftwareSystem(Location.External, "System B", "Description"); @@ -63,13 +67,13 @@ public void test_addAllSoftwareSystems_AddsAllSoftwareSystems_WhenThereAreSomeSo } @Test - public void test_addAllPeople_DoesNothing_WhenThereAreNoPeople() { + void test_addAllPeople_DoesNothing_WhenThereAreNoPeople() { view.addAllPeople(); assertEquals(0, view.getElements().size()); } @Test - public void test_addAllPeople_AddsAllPeople_WhenThereAreSomePeopleInTheModel() { + void test_addAllPeople_AddsAllPeople_WhenThereAreSomePeopleInTheModel() { Person userA = model.addPerson("User A", "Description"); Person userB = model.addPerson("User B", "Description"); @@ -81,13 +85,13 @@ public void test_addAllPeople_AddsAllPeople_WhenThereAreSomePeopleInTheModel() { } @Test - public void test_addAllElements_DoesNothing_WhenThereAreNoSoftwareSystemsOrPeople() { + void test_addAllElements_DoesNothing_WhenThereAreNoSoftwareSystemsOrPeople() { view.addAllElements(); assertEquals(0, view.getElements().size()); } @Test - public void test_addAllElements_AddsAllSoftwareSystemsAndPeople_WhenThereAreSomeSoftwareSystemsAndPeopleInTheModel() { + void test_addAllElements_AddsAllSoftwareSystemsAndPeople_WhenThereAreSomeSoftwareSystemsAndPeopleInTheModel() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); Person person = model.addPerson("Person", "Description"); @@ -99,7 +103,7 @@ public void test_addAllElements_AddsAllSoftwareSystemsAndPeople_WhenThereAreSome } @Test - public void test_isEnterpriseBoundaryVisible() { + void test_isEnterpriseBoundaryVisible() { assertTrue(view.isEnterpriseBoundaryVisible()); // default is true view.setEnterpriseBoundaryVisible(false); @@ -107,7 +111,7 @@ public void test_isEnterpriseBoundaryVisible() { } @Test - public void test_addNearestNeighbours_ThrowsAnException_WhenANullElementIsSpecified() { + void test_addNearestNeighbours_ThrowsAnException_WhenANullElementIsSpecified() { try { view.addNearestNeighbours(null); fail(); @@ -117,7 +121,7 @@ public void test_addNearestNeighbours_ThrowsAnException_WhenANullElementIsSpecif } @Test - public void test_addNearestNeighbours_ThrowsAnException_WhenAnElementThatIsNotAPersonOrSoftwareSystemIsSpecified() { + void test_addNearestNeighbours_ThrowsAnException_WhenAnElementThatIsNotAPersonOrSoftwareSystemIsSpecified() { SoftwareSystem softwareSystem = model.addSoftwareSystem("The System", "Description"); Container container = softwareSystem.addContainer("Container", "Description", "Technology"); try { @@ -129,7 +133,7 @@ public void test_addNearestNeighbours_ThrowsAnException_WhenAnElementThatIsNotAP } @Test - public void test_addNearestNeighbours_DoesNothing_WhenThereAreNoNeighbours() { + void test_addNearestNeighbours_DoesNothing_WhenThereAreNoNeighbours() { SoftwareSystem softwareSystem = model.addSoftwareSystem("The System", "Description"); view.addNearestNeighbours(softwareSystem); @@ -137,7 +141,7 @@ public void test_addNearestNeighbours_DoesNothing_WhenThereAreNoNeighbours() { } @Test - public void test_addNearestNeighbours_AddsNearestNeighbours_WhenThereAreSomeNearestNeighbours() { + void test_addNearestNeighbours_AddsNearestNeighbours_WhenThereAreSomeNearestNeighbours() { SoftwareSystem softwareSystem = model.addSoftwareSystem("The System", "Description"); SoftwareSystem softwareSystemA = model.addSoftwareSystem("System A", "Description"); SoftwareSystem softwareSystemB = model.addSoftwareSystem("System B", "Description"); @@ -187,7 +191,7 @@ public void test_addNearestNeighbours_AddsNearestNeighbours_WhenThereAreSomeNear } @Test - public void test_addDefaultElements() { + void test_addDefaultElements() { CustomElement element = model.addCustomElement("Custom"); Person user = model.addPerson("User"); SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1"); diff --git a/structurizr-core/test/unit/com/structurizr/view/TerminologyTests.java b/structurizr-core/test/unit/com/structurizr/view/TerminologyTests.java index 7c6bb38ea..13398c7f8 100644 --- a/structurizr-core/test/unit/com/structurizr/view/TerminologyTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/TerminologyTests.java @@ -2,14 +2,14 @@ import com.structurizr.Workspace; import com.structurizr.model.*; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class TerminologyTests { @Test - public void test_findTerminology() { + void test_findTerminology() { Workspace workspace = new Workspace("Name", "Description"); Terminology terminology = workspace.getViews().getConfiguration().getTerminology(); Person person = workspace.getModel().addPerson("Name"); diff --git a/structurizr-core/test/unit/com/structurizr/view/VertexTests.java b/structurizr-core/test/unit/com/structurizr/view/VertexTests.java index a7f3be379..94e81fec0 100644 --- a/structurizr-core/test/unit/com/structurizr/view/VertexTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/VertexTests.java @@ -1,25 +1,25 @@ package com.structurizr.view; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; public class VertexTests { @Test - public void test_equals() { + void test_equals() { Vertex vertex1 = new Vertex(123, 456); Vertex vertex2 = new Vertex(123, 456); Vertex vertex3 = new Vertex(456, 123); - assertFalse(vertex1.equals(null)); - assertFalse(vertex1.equals("hello world")); + assertNotEquals(vertex1, null); + assertNotEquals(vertex1, "hello world"); - assertTrue(vertex1.equals(vertex1)); - assertTrue(vertex1.equals(vertex2)); - assertTrue(vertex2.equals(vertex1)); - assertFalse(vertex2.equals(vertex3)); + assertEquals(vertex1, vertex1); + assertEquals(vertex1, vertex2); + assertEquals(vertex2, vertex1); + assertNotEquals(vertex2, vertex3); } } \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java b/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java index 086d5b280..b3741b231 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java @@ -2,12 +2,12 @@ import com.structurizr.Workspace; import com.structurizr.model.*; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Collections; import java.util.HashSet; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class ViewSetTests { @@ -28,7 +28,7 @@ private Workspace createWorkspace() { } @Test - public void test_createCustomView_ThrowsAnException_WhenANullKeyIsSpecified() { + void test_createCustomView_ThrowsAnException_WhenANullKeyIsSpecified() { try { new Workspace("", "").getViews().createCustomView(null, "Title", "Description"); fail(); @@ -38,7 +38,7 @@ public void test_createCustomView_ThrowsAnException_WhenANullKeyIsSpecified() { } @Test - public void test_createCustomView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { + void test_createCustomView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { try { new Workspace("", "").getViews().createCustomView(" ", "Title", "Description"); fail(); @@ -48,7 +48,7 @@ public void test_createCustomView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() } @Test - public void test_createCustomView_ThrowsAnException_WhenADuplicateKeyIsSpecified() { + void test_createCustomView_ThrowsAnException_WhenADuplicateKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); workspace.getViews().createCustomView("key", "Title", "Description"); @@ -60,14 +60,14 @@ public void test_createCustomView_ThrowsAnException_WhenADuplicateKeyIsSpecified } @Test - public void test_createCustomView_DoesNotThrowAnException_WhenUniqueKeysAreSpecified() { + void test_createCustomView_DoesNotThrowAnException_WhenUniqueKeysAreSpecified() { Workspace workspace = new Workspace("Name", "Description"); workspace.getViews().createCustomView("key1", "Title", "Description"); workspace.getViews().createCustomView("key2", "Title", "Description"); } @Test - public void test_createCustomView() { + void test_createCustomView() { Workspace workspace = new Workspace("Name", "Description"); CustomView customView = workspace.getViews().createCustomView("key", "Title", "Description"); assertEquals("key", customView.getKey()); @@ -78,7 +78,7 @@ public void test_createCustomView() { } @Test - public void test_createSystemLandscapeView_ThrowsAnException_WhenANullKeyIsSpecified() { + void test_createSystemLandscapeView_ThrowsAnException_WhenANullKeyIsSpecified() { try { new Workspace("", "").getViews().createSystemLandscapeView(null, "Description"); fail(); @@ -88,7 +88,7 @@ public void test_createSystemLandscapeView_ThrowsAnException_WhenANullKeyIsSpeci } @Test - public void test_createSystemLandscapeView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { + void test_createSystemLandscapeView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { try { new Workspace("", "").getViews().createSystemLandscapeView(" ", "Description"); fail(); @@ -98,7 +98,7 @@ public void test_createSystemLandscapeView_ThrowsAnException_WhenAnEmptyKeyIsSpe } @Test - public void test_createSystemLandscapeView_ThrowsAnException_WhenADuplicateKeyIsSpecified() { + void test_createSystemLandscapeView_ThrowsAnException_WhenADuplicateKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); workspace.getViews().createSystemLandscapeView("key", "Description"); @@ -110,14 +110,14 @@ public void test_createSystemLandscapeView_ThrowsAnException_WhenADuplicateKeyIs } @Test - public void test_createSystemLandscapeView_DoesNotThrowAnException_WhenUniqueKeysAreSpecified() { + void test_createSystemLandscapeView_DoesNotThrowAnException_WhenUniqueKeysAreSpecified() { Workspace workspace = new Workspace("Name", "Description"); workspace.getViews().createSystemLandscapeView("key1", "Description"); workspace.getViews().createSystemLandscapeView("key2", "Description"); } @Test - public void test_createSystemLandscapeView() { + void test_createSystemLandscapeView() { Workspace workspace = new Workspace("Name", "Description"); SystemLandscapeView systemLandscapeView = workspace.getViews().createSystemLandscapeView("key", "Description"); assertEquals("key", systemLandscapeView.getKey()); @@ -125,7 +125,7 @@ public void test_createSystemLandscapeView() { } @Test - public void test_createSystemContextView_ThrowsAnException_WhenASoftwareSystemIsSpecified() { + void test_createSystemContextView_ThrowsAnException_WhenASoftwareSystemIsSpecified() { try { new Workspace("", "").getViews().createSystemContextView(null, null, "Description"); fail(); @@ -135,7 +135,7 @@ public void test_createSystemContextView_ThrowsAnException_WhenASoftwareSystemIs } @Test - public void test_createSystemContextView_ThrowsAnException_WhenANullKeyIsSpecified() { + void test_createSystemContextView_ThrowsAnException_WhenANullKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); @@ -147,7 +147,7 @@ public void test_createSystemContextView_ThrowsAnException_WhenANullKeyIsSpecifi } @Test - public void test_createSystemContextView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { + void test_createSystemContextView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); @@ -159,7 +159,7 @@ public void test_createSystemContextView_ThrowsAnException_WhenAnEmptyKeyIsSpeci } @Test - public void test_createSystemContextView_ThrowsAnException_WhenADuplicateKeyIsSpecified() { + void test_createSystemContextView_ThrowsAnException_WhenADuplicateKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); @@ -172,7 +172,7 @@ public void test_createSystemContextView_ThrowsAnException_WhenADuplicateKeyIsSp } @Test - public void test_createSystemContextView() { + void test_createSystemContextView() { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); SystemContextView systemContextView = workspace.getViews().createSystemContextView(softwareSystem, "key", "Description"); @@ -182,7 +182,7 @@ public void test_createSystemContextView() { } @Test - public void test_createContainerView_ThrowsAnException_WhenASoftwareSystemIsSpecified() { + void test_createContainerView_ThrowsAnException_WhenASoftwareSystemIsSpecified() { try { new Workspace("", "").getViews().createContainerView(null, null, "Description"); fail(); @@ -192,7 +192,7 @@ public void test_createContainerView_ThrowsAnException_WhenASoftwareSystemIsSpec } @Test - public void test_createContainerView_ThrowsAnException_WhenANullKeyIsSpecified() { + void test_createContainerView_ThrowsAnException_WhenANullKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); @@ -204,7 +204,7 @@ public void test_createContainerView_ThrowsAnException_WhenANullKeyIsSpecified() } @Test - public void test_createContainerView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { + void test_createContainerView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); @@ -216,7 +216,7 @@ public void test_createContainerView_ThrowsAnException_WhenAnEmptyKeyIsSpecified } @Test - public void test_createContainerView_ThrowsAnException_WhenADuplicateKeyIsSpecified() { + void test_createContainerView_ThrowsAnException_WhenADuplicateKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); @@ -229,7 +229,7 @@ public void test_createContainerView_ThrowsAnException_WhenADuplicateKeyIsSpecif } @Test - public void test_createContainerView() { + void test_createContainerView() { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); ContainerView containerView = workspace.getViews().createContainerView(softwareSystem, "key", "Description"); @@ -239,7 +239,7 @@ public void test_createContainerView() { } @Test - public void test_createComponentView_ThrowsAnException_WhenASoftwareSystemIsSpecified() { + void test_createComponentView_ThrowsAnException_WhenASoftwareSystemIsSpecified() { try { new Workspace("", "").getViews().createComponentView(null, null, "Description"); fail(); @@ -249,7 +249,7 @@ public void test_createComponentView_ThrowsAnException_WhenASoftwareSystemIsSpec } @Test - public void test_createComponentView_ThrowsAnException_WhenANullKeyIsSpecified() { + void test_createComponentView_ThrowsAnException_WhenANullKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); @@ -262,7 +262,7 @@ public void test_createComponentView_ThrowsAnException_WhenANullKeyIsSpecified() } @Test - public void test_createComponentView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { + void test_createComponentView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); @@ -275,7 +275,7 @@ public void test_createComponentView_ThrowsAnException_WhenAnEmptyKeyIsSpecified } @Test - public void test_createComponentView_ThrowsAnException_WhenADuplicateKeyIsSpecified() { + void test_createComponentView_ThrowsAnException_WhenADuplicateKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); @@ -289,7 +289,7 @@ public void test_createComponentView_ThrowsAnException_WhenADuplicateKeyIsSpecif } @Test - public void test_createComponentView() { + void test_createComponentView() { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); Container container = softwareSystem.addContainer("Container", "Description", "Technology"); @@ -299,9 +299,9 @@ public void test_createComponentView() { assertSame(softwareSystem, componentView.getSoftwareSystem()); assertSame(container, componentView.getContainer()); } - + @Test - public void test_createDynamicView_ThrowsAnException_WhenANullKeyIsSpecified() { + void test_createDynamicView_ThrowsAnException_WhenANullKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); workspace.getViews().createDynamicView(null, "Description"); @@ -312,7 +312,7 @@ public void test_createDynamicView_ThrowsAnException_WhenANullKeyIsSpecified() { } @Test - public void test_createDynamicView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { + void test_createDynamicView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); workspace.getViews().createDynamicView(" ", "Description"); @@ -323,7 +323,7 @@ public void test_createDynamicView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() } @Test - public void test_createDynamicView() { + void test_createDynamicView() { Workspace workspace = new Workspace("Name", "Description"); DynamicView dynamicView = workspace.getViews().createDynamicView("key", "Description"); @@ -334,9 +334,9 @@ public void test_createDynamicView() { } @Test - public void test_createDynamicViewForASoftwareSystem_ThrowsAnException_WhenANullSoftwareSystemIsSpecified() { + void test_createDynamicViewForASoftwareSystem_ThrowsAnException_WhenANullSoftwareSystemIsSpecified() { try { - new Workspace("", "").getViews().createDynamicView((SoftwareSystem)null, "key", "Description"); + new Workspace("", "").getViews().createDynamicView((SoftwareSystem) null, "key", "Description"); fail(); } catch (IllegalArgumentException iae) { assertEquals("A software system must be specified.", iae.getMessage()); @@ -344,7 +344,7 @@ public void test_createDynamicViewForASoftwareSystem_ThrowsAnException_WhenANull } @Test - public void test_createDynamicViewForASoftwareSystem_ThrowsAnException_WhenANullKeyIsSpecified() { + void test_createDynamicViewForASoftwareSystem_ThrowsAnException_WhenANullKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); @@ -356,7 +356,7 @@ public void test_createDynamicViewForASoftwareSystem_ThrowsAnException_WhenANull } @Test - public void test_createDynamicViewForASoftwareSystem_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { + void test_createDynamicViewForASoftwareSystem_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); @@ -368,7 +368,7 @@ public void test_createDynamicViewForASoftwareSystem_ThrowsAnException_WhenAnEmp } @Test - public void test_createDynamicViewForASoftwareSystem_ThrowsAnException_WhenADuplicateKeyIsUsed() { + void test_createDynamicViewForASoftwareSystem_ThrowsAnException_WhenADuplicateKeyIsUsed() { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name", "Description"); @@ -382,7 +382,7 @@ public void test_createDynamicViewForASoftwareSystem_ThrowsAnException_WhenADupl } @Test - public void test_createDynamicViewForSoftwareSystem() { + void test_createDynamicViewForSoftwareSystem() { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name", "Description"); @@ -394,9 +394,9 @@ public void test_createDynamicViewForSoftwareSystem() { } @Test - public void test_createDynamicViewForAContainer_ThrowsAnException_WhenANullContainerIsSpecified() { + void test_createDynamicViewForAContainer_ThrowsAnException_WhenANullContainerIsSpecified() { try { - new Workspace("", "").getViews().createDynamicView((Container)null, "key", "Description"); + new Workspace("", "").getViews().createDynamicView((Container) null, "key", "Description"); fail(); } catch (IllegalArgumentException iae) { assertEquals("A container must be specified.", iae.getMessage()); @@ -404,7 +404,7 @@ public void test_createDynamicViewForAContainer_ThrowsAnException_WhenANullConta } @Test - public void test_createDynamicViewForAContainer_ThrowsAnException_WhenANullKeyIsSpecified() { + void test_createDynamicViewForAContainer_ThrowsAnException_WhenANullKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); @@ -417,7 +417,7 @@ public void test_createDynamicViewForAContainer_ThrowsAnException_WhenANullKeyIs } @Test - public void test_createDynamicViewForAContainer_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { + void test_createDynamicViewForAContainer_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); @@ -430,7 +430,7 @@ public void test_createDynamicViewForAContainer_ThrowsAnException_WhenAnEmptyKey } @Test - public void test_createDynamicViewForContainer() { + void test_createDynamicViewForContainer() { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name", "Description"); Container container = softwareSystem.addContainer("Container", "Description", "Technology"); @@ -443,7 +443,7 @@ public void test_createDynamicViewForContainer() { } @Test - public void test_createDeploymentView_ThrowsAnException_WhenANullKeyIsSpecified() { + void test_createDeploymentView_ThrowsAnException_WhenANullKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); workspace.getViews().createDeploymentView(null, "Description"); @@ -454,7 +454,7 @@ public void test_createDeploymentView_ThrowsAnException_WhenANullKeyIsSpecified( } @Test - public void test_createDeploymentView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { + void test_createDeploymentView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); workspace.getViews().createDeploymentView(" ", "Description"); @@ -465,7 +465,7 @@ public void test_createDeploymentView_ThrowsAnException_WhenAnEmptyKeyIsSpecifie } @Test - public void test_createDeploymentView_ThrowsAnException_WhenADuplicateKeyIsUsed() { + void test_createDeploymentView_ThrowsAnException_WhenADuplicateKeyIsUsed() { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name", "Description"); @@ -479,7 +479,7 @@ public void test_createDeploymentView_ThrowsAnException_WhenADuplicateKeyIsUsed( } @Test - public void test_createDeploymentView() { + void test_createDeploymentView() { Workspace workspace = new Workspace("Name", "Description"); DeploymentView deploymentView = workspace.getViews().createDeploymentView("key", "Description"); @@ -489,9 +489,9 @@ public void test_createDeploymentView() { } @Test - public void test_createDeploymentViewForASoftwareSystem_ThrowsAnException_WhenANullSoftwareSystemIsSpecified() { + void test_createDeploymentViewForASoftwareSystem_ThrowsAnException_WhenANullSoftwareSystemIsSpecified() { try { - new Workspace("", "").getViews().createDeploymentView((SoftwareSystem)null, "key", "Description"); + new Workspace("", "").getViews().createDeploymentView((SoftwareSystem) null, "key", "Description"); fail(); } catch (IllegalArgumentException iae) { assertEquals("A software system must be specified.", iae.getMessage()); @@ -499,7 +499,7 @@ public void test_createDeploymentViewForASoftwareSystem_ThrowsAnException_WhenAN } @Test - public void test_createDeploymentViewForASoftwareSystem_ThrowsAnException_WhenANullKeyIsSpecified() { + void test_createDeploymentViewForASoftwareSystem_ThrowsAnException_WhenANullKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); @@ -511,7 +511,7 @@ public void test_createDeploymentViewForASoftwareSystem_ThrowsAnException_WhenAN } @Test - public void test_createDeploymentViewForASoftwareSystem_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { + void test_createDeploymentViewForASoftwareSystem_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); @@ -523,7 +523,7 @@ public void test_createDeploymentViewForASoftwareSystem_ThrowsAnException_WhenAn } @Test - public void test_createDeploymentViewForASoftwareSystem_ThrowsAnException_WhenADuplicateKeyIsUsed() { + void test_createDeploymentViewForASoftwareSystem_ThrowsAnException_WhenADuplicateKeyIsUsed() { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name", "Description"); @@ -537,7 +537,7 @@ public void test_createDeploymentViewForASoftwareSystem_ThrowsAnException_WhenAD } @Test - public void test_createDeploymentViewForSoftwareSystem() { + void test_createDeploymentViewForSoftwareSystem() { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name", "Description"); @@ -548,7 +548,7 @@ public void test_createDeploymentViewForSoftwareSystem() { } @Test - public void test_createFilteredView_ThrowsAnException_WhenANullViewIsSpecified() { + void test_createFilteredView_ThrowsAnException_WhenANullViewIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); workspace.getViews().createFilteredView(null, "key", "Description", FilterMode.Include, "tag1", "tag2"); @@ -559,7 +559,7 @@ public void test_createFilteredView_ThrowsAnException_WhenANullViewIsSpecified() } @Test - public void test_createFilteredView_ThrowsAnException_WhenANullKeyIsSpecified() { + void test_createFilteredView_ThrowsAnException_WhenANullKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("systemLandscape", "Description"); @@ -571,7 +571,7 @@ public void test_createFilteredView_ThrowsAnException_WhenANullKeyIsSpecified() } @Test - public void test_createFilteredView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { + void test_createFilteredView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("systemLandscape", "Description"); @@ -583,7 +583,7 @@ public void test_createFilteredView_ThrowsAnException_WhenAnEmptyKeyIsSpecified( } @Test - public void test_createFilteredView_ThrowsAnException_WhenADuplicateKeyIsUsed() { + void test_createFilteredView_ThrowsAnException_WhenADuplicateKeyIsUsed() { Workspace workspace = new Workspace("Name", "Description"); SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("systemLandscape", "Description"); workspace.getViews().createFilteredView(view, "filtered", "Description", FilterMode.Include, "tag1", "tag2"); @@ -596,7 +596,7 @@ public void test_createFilteredView_ThrowsAnException_WhenADuplicateKeyIsUsed() } @Test - public void test_createFilteredView() { + void test_createFilteredView() { Workspace workspace = new Workspace("Name", "Description"); SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("systemLandscape", "Description"); FilteredView filteredView = workspace.getViews().createFilteredView(view, "key", "Description", FilterMode.Include, "tag1", "tag2"); @@ -610,7 +610,7 @@ public void test_createFilteredView() { } @Test - public void test_copyLayoutInformationFrom_WhenAViewKeyIsNotSetButTheViewTitlesMatch() { + void test_copyLayoutInformationFrom_WhenAViewKeyIsNotSetButTheViewTitlesMatch() { Workspace workspace1 = createWorkspace(); SoftwareSystem softwareSystem1 = workspace1.getModel().getSoftwareSystemWithName("Software System"); SystemContextView view1 = workspace1.getViews().createSystemContextView(softwareSystem1, "context", "Description"); @@ -631,7 +631,7 @@ public void test_copyLayoutInformationFrom_WhenAViewKeyIsNotSetButTheViewTitlesM } @Test - public void test_copyLayoutInformationFrom_WhenAViewKeyHasBeenIntroduced() { + void test_copyLayoutInformationFrom_WhenAViewKeyHasBeenIntroduced() { Workspace workspace1 = createWorkspace(); SoftwareSystem softwareSystem1 = workspace1.getModel().getSoftwareSystemWithName("Software System"); SystemContextView view1 = workspace1.getViews().createSystemContextView(softwareSystem1, "context", "Description"); @@ -651,7 +651,7 @@ public void test_copyLayoutInformationFrom_WhenAViewKeyHasBeenIntroduced() { } @Test - public void test_copyLayoutInformationFrom_IgnoresThePaperSize_WhenThePaperSizeIsSet() { + void test_copyLayoutInformationFrom_IgnoresThePaperSize_WhenThePaperSizeIsSet() { Workspace workspace1 = createWorkspace(); SoftwareSystem softwareSystem1 = workspace1.getModel().getSoftwareSystemWithName("Software System"); SystemContextView view1 = workspace1.getViews().createSystemContextView(softwareSystem1, "context", "Description"); @@ -667,7 +667,7 @@ public void test_copyLayoutInformationFrom_IgnoresThePaperSize_WhenThePaperSizeI } @Test - public void test_copyLayoutInformationFrom_CopiesThePaperSize_WhenThePaperSizeIsNotSet() { + void test_copyLayoutInformationFrom_CopiesThePaperSize_WhenThePaperSizeIsNotSet() { Workspace workspace1 = createWorkspace(); SoftwareSystem softwareSystem1 = workspace1.getModel().getSoftwareSystemWithName("Software System"); SystemContextView view1 = workspace1.getViews().createSystemContextView(softwareSystem1, "context", "Description"); @@ -682,7 +682,7 @@ public void test_copyLayoutInformationFrom_CopiesThePaperSize_WhenThePaperSizeIs } @Test - public void test_copyLayoutInformationFrom_WhenTheSystemLandscapeViewKeysMatch() { + void test_copyLayoutInformationFrom_WhenTheSystemLandscapeViewKeysMatch() { Workspace workspace1 = createWorkspace(); SoftwareSystem softwareSystem1 = workspace1.getModel().getSoftwareSystemWithName("Software System"); SystemLandscapeView view1 = workspace1.getViews().createSystemLandscapeView("landscape", "Description"); @@ -701,7 +701,7 @@ public void test_copyLayoutInformationFrom_WhenTheSystemLandscapeViewKeysMatch() } @Test - public void test_copyLayoutInformationFrom_DoesNotDoAnythingIfThereIsNoSystemLandscapeViewToCopyInformationFrom() { + void test_copyLayoutInformationFrom_DoesNotDoAnythingIfThereIsNoSystemLandscapeViewToCopyInformationFrom() { Workspace workspace1 = createWorkspace(); Workspace workspace2 = createWorkspace(); @@ -715,7 +715,7 @@ public void test_copyLayoutInformationFrom_DoesNotDoAnythingIfThereIsNoSystemLan } @Test - public void test_copyLayoutInformationFrom_WhenTheSystemContextViewKeysMatch() { + void test_copyLayoutInformationFrom_WhenTheSystemContextViewKeysMatch() { Workspace workspace1 = createWorkspace(); SoftwareSystem softwareSystem1 = workspace1.getModel().getSoftwareSystemWithName("Software System"); SystemContextView view1 = workspace1.getViews().createSystemContextView(softwareSystem1, "context", "Description"); @@ -734,7 +734,7 @@ public void test_copyLayoutInformationFrom_WhenTheSystemContextViewKeysMatch() { } @Test - public void test_copyLayoutInformationFrom_DoesNotDoAnythingIfThereIsNoSystemContextViewToCopyInformationFrom() { + void test_copyLayoutInformationFrom_DoesNotDoAnythingIfThereIsNoSystemContextViewToCopyInformationFrom() { Workspace workspace1 = createWorkspace(); Workspace workspace2 = createWorkspace(); @@ -748,7 +748,7 @@ public void test_copyLayoutInformationFrom_DoesNotDoAnythingIfThereIsNoSystemCon } @Test - public void test_copyLayoutInformationFrom_WhenTheContainerViewKeysMatch() { + void test_copyLayoutInformationFrom_WhenTheContainerViewKeysMatch() { Workspace workspace1 = createWorkspace(); SoftwareSystem softwareSystem1 = workspace1.getModel().getSoftwareSystemWithName("Software System"); ContainerView view1 = workspace1.getViews().createContainerView(softwareSystem1, "containers", "Description"); @@ -767,7 +767,7 @@ public void test_copyLayoutInformationFrom_WhenTheContainerViewKeysMatch() { } @Test - public void test_copyLayoutInformationFrom_DoesNotDoAnythingIfThereIsNoContainerViewToCopyInformationFrom() { + void test_copyLayoutInformationFrom_DoesNotDoAnythingIfThereIsNoContainerViewToCopyInformationFrom() { Workspace workspace1 = createWorkspace(); Workspace workspace2 = createWorkspace(); @@ -781,7 +781,7 @@ public void test_copyLayoutInformationFrom_DoesNotDoAnythingIfThereIsNoContainer } @Test - public void test_copyLayoutInformationFrom_WhenTheComponentViewKeysMatch() { + void test_copyLayoutInformationFrom_WhenTheComponentViewKeysMatch() { Workspace workspace1 = createWorkspace(); Container container1 = workspace1.getModel().getSoftwareSystemWithName("Software System").getContainerWithName("Container"); ComponentView view1 = workspace1.getViews().createComponentView(container1, "containers", "Description"); @@ -800,7 +800,7 @@ public void test_copyLayoutInformationFrom_WhenTheComponentViewKeysMatch() { } @Test - public void test_copyLayoutInformationFrom_DoesNotDoAnythingIfThereIsNoComponentViewToCopyInformationFrom() { + void test_copyLayoutInformationFrom_DoesNotDoAnythingIfThereIsNoComponentViewToCopyInformationFrom() { Workspace workspace1 = createWorkspace(); Workspace workspace2 = createWorkspace(); @@ -814,7 +814,7 @@ public void test_copyLayoutInformationFrom_DoesNotDoAnythingIfThereIsNoComponent } @Test - public void test_copyLayoutInformationFrom_WhenTheDynamicViewKeysMatch() { + void test_copyLayoutInformationFrom_WhenTheDynamicViewKeysMatch() { Workspace workspace1 = createWorkspace(); Person person1 = workspace1.getModel().getPersonWithName("Person"); SoftwareSystem softwareSystem1 = workspace1.getModel().getSoftwareSystemWithName("Software System"); @@ -835,7 +835,7 @@ public void test_copyLayoutInformationFrom_WhenTheDynamicViewKeysMatch() { } @Test - public void test_copyLayoutInformationFrom_DoesNotDoAnythingIfThereIsNoDynamicViewToCopyInformationFrom() { + void test_copyLayoutInformationFrom_DoesNotDoAnythingIfThereIsNoDynamicViewToCopyInformationFrom() { Workspace workspace1 = createWorkspace(); Workspace workspace2 = createWorkspace(); @@ -850,7 +850,7 @@ public void test_copyLayoutInformationFrom_DoesNotDoAnythingIfThereIsNoDynamicVi } @Test - public void test_copyLayoutInformationFrom_WhenTheDeploymentViewKeysMatch() { + void test_copyLayoutInformationFrom_WhenTheDeploymentViewKeysMatch() { Workspace workspace1 = createWorkspace(); DeploymentNode deploymentNode1 = workspace1.getModel().getDeploymentNodeWithName("Deployment Node"); DeploymentView view1 = workspace1.getViews().createDeploymentView("key", "Description"); @@ -871,7 +871,7 @@ public void test_copyLayoutInformationFrom_WhenTheDeploymentViewKeysMatch() { } @Test - public void test_copyLayoutInformationFrom_DoesNotDoAnythingIfThereIsNoDeploymentViewToCopyInformationFrom() { + void test_copyLayoutInformationFrom_DoesNotDoAnythingIfThereIsNoDeploymentViewToCopyInformationFrom() { Workspace workspace1 = createWorkspace(); Workspace workspace2 = createWorkspace(); @@ -910,7 +910,7 @@ private HashSet relationshipViewsFor(Relationship... relations } @Test - public void test_hydrate() { + void test_hydrate() { Workspace workspace = new Workspace("Name", "Description"); Model model = workspace.getModel(); ViewSet views = workspace.getViews(); @@ -1004,7 +1004,7 @@ public void test_hydrate() { } @Test - public void test_setEnterpriseContextViews_IsSupportedForOlderWorkspaces() { + void test_setEnterpriseContextViews_IsSupportedForOlderWorkspaces() { ViewSet views = new Workspace("", "").getViews(); SystemLandscapeView systemLandscapeView = views.createSystemLandscapeView("key", "Description"); views.setEnterpriseContextViews(Collections.singleton(systemLandscapeView)); @@ -1013,7 +1013,7 @@ public void test_setEnterpriseContextViews_IsSupportedForOlderWorkspaces() { } @Test - public void test_createDefaultViews() { + void test_createDefaultViews() { Workspace workspace = new Workspace("Name", "Description"); Model model = workspace.getModel(); ViewSet views = workspace.getViews(); @@ -1075,7 +1075,7 @@ public void test_createDefaultViews() { } @Test - public void test_copyLayoutInformationFrom_DoesNothing_WhenMergeFromRemoteIsSetToFalse() { + void test_copyLayoutInformationFrom_DoesNothing_WhenMergeFromRemoteIsSetToFalse() { Workspace workspace1 = createWorkspace(); SoftwareSystem softwareSystem1 = workspace1.getModel().getSoftwareSystemWithName("Software System"); SystemLandscapeView view1 = workspace1.getViews().createSystemLandscapeView("landscape", "Description"); @@ -1095,7 +1095,7 @@ public void test_copyLayoutInformationFrom_DoesNothing_WhenMergeFromRemoteIsSetT } @Test - public void test_view_ordering() { + void test_view_ordering() { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); Container container = softwareSystem.addContainer("Container"); diff --git a/structurizr-core/test/unit/com/structurizr/view/ViewTests.java b/structurizr-core/test/unit/com/structurizr/view/ViewTests.java index e093e84ec..8b2fde869 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ViewTests.java @@ -3,17 +3,17 @@ import com.structurizr.AbstractWorkspaceTestBase; import com.structurizr.Workspace; import com.structurizr.model.*; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.Iterator; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class ViewTests extends AbstractWorkspaceTestBase { @Test - public void test_construction() { + void test_construction() { SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); StaticView view = new SystemContextView(softwareSystem, "key", "Description"); assertEquals("key", view.getKey()); @@ -22,14 +22,14 @@ public void test_construction() { } @Test - public void test_construction_WhenTheViewKeyContainsAForwardSlashCharacter() { + void test_construction_WhenTheViewKeyContainsAForwardSlashCharacter() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); StaticView view = new SystemContextView(softwareSystem, "key/1", "Description"); assertEquals("key_1", view.getKey()); } @Test - public void test_addAllSoftwareSystems_DoesNothing_WhenThereAreNoOtherSoftwareSystemsInTheModel() { + void test_addAllSoftwareSystems_DoesNothing_WhenThereAreNoOtherSoftwareSystemsInTheModel() { SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); StaticView view = new SystemContextView(softwareSystem, "context", "Description"); assertEquals(1, view.getElements().size()); @@ -38,7 +38,7 @@ public void test_addAllSoftwareSystems_DoesNothing_WhenThereAreNoOtherSoftwareSy } @Test - public void test_addAllSoftwareSystems_DoesAddAllSoftwareSystems_WhenThereAreSoftwareSystemsInTheModel() { + void test_addAllSoftwareSystems_DoesAddAllSoftwareSystems_WhenThereAreSoftwareSystemsInTheModel() { SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); SoftwareSystem softwareSystemA = model.addSoftwareSystem(Location.Unspecified, "System A", "Description"); SoftwareSystem softwareSystemB = model.addSoftwareSystem(Location.Unspecified, "System B", "Description"); @@ -56,12 +56,12 @@ public void test_addAllSoftwareSystems_DoesAddAllSoftwareSystems_WhenThereAreSof } @Test - public void test_addSoftwareSystem_ThrowsAnException_WhenGivenNull() { + void test_addSoftwareSystem_ThrowsAnException_WhenGivenNull() { SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); StaticView view = new SystemContextView(softwareSystem, "context", "Description"); try { - view.add((SoftwareSystem)null); + view.add((SoftwareSystem) null); fail(); } catch (IllegalArgumentException iae) { assertEquals("An element must be specified.", iae.getMessage()); @@ -69,7 +69,7 @@ public void test_addSoftwareSystem_ThrowsAnException_WhenGivenNull() { } @Test - public void test_addSoftwareSystem_AddsTheSoftwareSystem_WhenTheSoftwareSystemIsInTheModel() { + void test_addSoftwareSystem_AddsTheSoftwareSystem_WhenTheSoftwareSystemIsInTheModel() { SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); SoftwareSystem softwareSystemA = model.addSoftwareSystem(Location.Unspecified, "System A", "Description"); @@ -82,7 +82,7 @@ public void test_addSoftwareSystem_AddsTheSoftwareSystem_WhenTheSoftwareSystemIs } @Test - public void test_addAllPeople_DoesNothing_WhenThereAreNoPeopleInTheModel() { + void test_addAllPeople_DoesNothing_WhenThereAreNoPeopleInTheModel() { SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); StaticView view = new SystemContextView(softwareSystem, "context", "Description"); @@ -93,7 +93,7 @@ public void test_addAllPeople_DoesNothing_WhenThereAreNoPeopleInTheModel() { } @Test - public void test_addAllPeople_DoesAddAllPeople_WhenThereArePeopleInTheModel() { + void test_addAllPeople_DoesAddAllPeople_WhenThereArePeopleInTheModel() { SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); Person person1 = model.addPerson(Location.Unspecified, "Person 1", "Description"); Person person2 = model.addPerson(Location.Unspecified, "Person 2", "Description"); @@ -111,11 +111,11 @@ public void test_addAllPeople_DoesAddAllPeople_WhenThereArePeopleInTheModel() { } @Test - public void test_addPerson_ThrowsAnException_WhenGivenNull() { + void test_addPerson_ThrowsAnException_WhenGivenNull() { SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); StaticView view = new SystemContextView(softwareSystem, "context", "Description"); try { - view.add((Person)null); + view.add((Person) null); fail(); } catch (IllegalArgumentException iae) { assertEquals("An element must be specified.", iae.getMessage()); @@ -123,7 +123,7 @@ public void test_addPerson_ThrowsAnException_WhenGivenNull() { } @Test - public void test_addPerson_AddsThePerson_WhenThPersonIsInTheModel() { + void test_addPerson_AddsThePerson_WhenThPersonIsInTheModel() { SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); StaticView view = new SystemContextView(softwareSystem, "context", "Description"); @@ -137,7 +137,7 @@ public void test_addPerson_AddsThePerson_WhenThPersonIsInTheModel() { } @Test - public void test_removeElementsWithNoRelationships_RemovesAllElements_WhenTheViewHasNoRelationshipsBetweenElements() { + void test_removeElementsWithNoRelationships_RemovesAllElements_WhenTheViewHasNoRelationshipsBetweenElements() { SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "Software System", "Description"); Person person = model.addPerson(Location.Unspecified, "Person", "Description"); @@ -150,7 +150,7 @@ public void test_removeElementsWithNoRelationships_RemovesAllElements_WhenTheVie } @Test - public void test_removeElementsWithNoRelationships_RemovesOnlyThoseElementsWithoutRelationships_WhenTheViewContainsSomeUnlinkedElements() { + void test_removeElementsWithNoRelationships_RemovesOnlyThoseElementsWithoutRelationships_WhenTheViewContainsSomeUnlinkedElements() { SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); SoftwareSystem softwareSystemA = model.addSoftwareSystem(Location.Unspecified, "System A", "Description"); SoftwareSystem softwareSystemB = model.addSoftwareSystem(Location.Unspecified, "System B", "Description"); @@ -170,7 +170,7 @@ public void test_removeElementsWithNoRelationships_RemovesOnlyThoseElementsWitho } @Test - public void test_copyLayoutInformationFrom() { + void test_copyLayoutInformationFrom() { Workspace workspace1 = new Workspace("", ""); Model model1 = workspace1.getModel(); SoftwareSystem softwareSystem1A = model1.addSoftwareSystem("System A", "Description"); @@ -256,21 +256,21 @@ public void test_copyLayoutInformationFrom() { } @Test - public void test_getName() { + void test_getName() { SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); SystemContextView systemContextView = new SystemContextView(softwareSystem, "context", "Description"); assertEquals("The System - System Context", systemContextView.getName()); } @Test - public void test_removeElementsThatAreUnreachableFrom_DoesNothing_WhenANullElementIsSpecified() { + void test_removeElementsThatAreUnreachableFrom_DoesNothing_WhenANullElementIsSpecified() { SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); StaticView view = new SystemContextView(softwareSystem, "context", "Description"); view.removeElementsThatAreUnreachableFrom(null); } @Test - public void test_removeElementsThatAreUnreachableFrom_DoesNothing_WhenAllElementsCanBeReached() { + void test_removeElementsThatAreUnreachableFrom_DoesNothing_WhenAllElementsCanBeReached() { SoftwareSystem softwareSystem = model.addSoftwareSystem("The System", "Description"); SoftwareSystem softwareSystemA = model.addSoftwareSystem("System A", ""); SoftwareSystem softwareSystemB = model.addSoftwareSystem("System B", ""); @@ -287,7 +287,7 @@ public void test_removeElementsThatAreUnreachableFrom_DoesNothing_WhenAllElement } @Test - public void test_removeElementsThatAreUnreachableFrom_RemovesOrphanedElements_WhenThereAreSomeOrphanedElements() { + void test_removeElementsThatAreUnreachableFrom_RemovesOrphanedElements_WhenThereAreSomeOrphanedElements() { SoftwareSystem softwareSystem = model.addSoftwareSystem("The System", "Description"); SoftwareSystem softwareSystemA = model.addSoftwareSystem("System A", ""); SoftwareSystem softwareSystemB = model.addSoftwareSystem("System B", ""); @@ -306,7 +306,7 @@ public void test_removeElementsThatAreUnreachableFrom_RemovesOrphanedElements_Wh } @Test - public void test_removeElementsThatAreUnreachableFrom_RemovesUnreachableElements_WhenThereAreSomeUnreachableElements() { + void test_removeElementsThatAreUnreachableFrom_RemovesUnreachableElements_WhenThereAreSomeUnreachableElements() { SoftwareSystem softwareSystem = model.addSoftwareSystem("The System", "Description"); SoftwareSystem softwareSystemA = model.addSoftwareSystem("System A", ""); SoftwareSystem softwareSystemB = model.addSoftwareSystem("System B", ""); @@ -324,7 +324,7 @@ public void test_removeElementsThatAreUnreachableFrom_RemovesUnreachableElements } @Test - public void test_removeElementsThatAreUnreachableFrom_DoesntIncludeAllElements_WhenThereIsACyclicGraph() { + void test_removeElementsThatAreUnreachableFrom_DoesntIncludeAllElements_WhenThereIsACyclicGraph() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1", "Description"); SoftwareSystem softwareSystem2 = model.addSoftwareSystem("Software System 2", "Description"); Person user = model.addPerson("User", ""); @@ -345,7 +345,7 @@ public void test_removeElementsThatAreUnreachableFrom_DoesntIncludeAllElements_W } @Test - public void test_removeRelationship_DoesNothing_WhenNullIsSpecified() { + void test_removeRelationship_DoesNothing_WhenNullIsSpecified() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1", "Description"); SoftwareSystem softwareSystem2 = model.addSoftwareSystem("Software System 2", "Description"); SoftwareSystem softwareSystem3 = model.addSoftwareSystem("Software System 3", "Description"); @@ -358,11 +358,11 @@ public void test_removeRelationship_DoesNothing_WhenNullIsSpecified() { view.addAllElements(); assertEquals(3, view.getRelationships().size()); - view.remove((Relationship)null); + view.remove((Relationship) null); } @Test - public void test_removeRelationship_RemovesARelationship_WhenAValidRelationshipIsSpecified() { + void test_removeRelationship_RemovesARelationship_WhenAValidRelationshipIsSpecified() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1", "Description"); SoftwareSystem softwareSystem2 = model.addSoftwareSystem("Software System 2", "Description"); SoftwareSystem softwareSystem3 = model.addSoftwareSystem("Software System 3", "Description"); @@ -382,20 +382,24 @@ public void test_removeRelationship_RemovesARelationship_WhenAValidRelationshipI assertTrue(view.getRelationships().contains(new RelationshipView(relationship23))); } - @Test(expected = IllegalArgumentException.class) - public void test_setKey_ThrowsAnException_WhenANullKeyIsSpecified() { - SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); - new SystemContextView(softwareSystem, null, "Description"); + @Test + void test_setKey_ThrowsAnException_WhenANullKeyIsSpecified() { + assertThrows(IllegalArgumentException.class, () -> { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); + new SystemContextView(softwareSystem, null, "Description"); + }); } - @Test(expected = IllegalArgumentException.class) - public void test_setKey_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { - SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); - new SystemContextView(softwareSystem, " ", "Description"); + @Test + void test_setKey_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { + assertThrows(IllegalArgumentException.class, () -> { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); + new SystemContextView(softwareSystem, " ", "Description"); + }); } @Test - public void test_addElement_ThrowsAnException_WhenTheSpecifiedElementDoesNotExistInTheModel() { + void test_addElement_ThrowsAnException_WhenTheSpecifiedElementDoesNotExistInTheModel() { try { Workspace workspace = new Workspace("1", ""); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); @@ -409,7 +413,7 @@ public void test_addElement_ThrowsAnException_WhenTheSpecifiedElementDoesNotExis } @Test - public void test_enableAutomaticLayout_EnablesAutoLayoutWithSomeDefaultValues_WhenTrueIsSpecified() { + void test_enableAutomaticLayout_EnablesAutoLayoutWithSomeDefaultValues_WhenTrueIsSpecified() { SystemLandscapeView view = new Workspace("", "").getViews().createSystemLandscapeView("key", "Description"); view.enableAutomaticLayout(); @@ -422,7 +426,7 @@ public void test_enableAutomaticLayout_EnablesAutoLayoutWithSomeDefaultValues_Wh } @Test - public void test_enableAutomaticLayout_DisablesAutoLayout_WhenFalseIsSpecified() { + void test_enableAutomaticLayout_DisablesAutoLayout_WhenFalseIsSpecified() { SystemLandscapeView view = new Workspace("", "").getViews().createSystemLandscapeView("key", "Description"); view.enableAutomaticLayout(); assertNotNull(view.getAutomaticLayout()); @@ -432,7 +436,7 @@ public void test_enableAutomaticLayout_DisablesAutoLayout_WhenFalseIsSpecified() } @Test - public void test_enableAutomaticLayout() { + void test_enableAutomaticLayout() { SystemLandscapeView view = new Workspace("", "").getViews().createSystemLandscapeView("key", "Description"); view.enableAutomaticLayout(AutomaticLayout.RankDirection.LeftRight, 100, 200, 300, true); @@ -445,7 +449,7 @@ public void test_enableAutomaticLayout() { } @Test - public void test_addCustomElement_AddsTheCustomElementToTheView() { + void test_addCustomElement_AddsTheCustomElementToTheView() { Workspace workspace = new Workspace("", ""); SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "Description"); @@ -463,7 +467,7 @@ public void test_addCustomElement_AddsTheCustomElementToTheView() { } @Test - public void test_addCustomElementWithoutRelationships_AddsTheCustomElementToTheView() { + void test_addCustomElementWithoutRelationships_AddsTheCustomElementToTheView() { Workspace workspace = new Workspace("", ""); SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "Description"); @@ -481,7 +485,7 @@ public void test_addCustomElementWithoutRelationships_AddsTheCustomElementToTheV } @Test - public void test_removeCustomElement_RemovesTheCustomElementFromTheView() { + void test_removeCustomElement_RemovesTheCustomElementFromTheView() { Workspace workspace = new Workspace("", ""); SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "Description"); From c8ab806d24b18129adca32a8bc12f751a1ee52b1 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Tue, 2 Aug 2022 15:05:51 +0200 Subject: [PATCH 305/717] Remove unused imports --- .../src/com/structurizr/io/json/JsonReader.java | 1 - .../test/unit/com/structurizr/util/WorkspaceUtilsTests.java | 1 - .../src/com/structurizr/documentation/Documentable.java | 2 -- .../src/com/structurizr/model/CustomElement.java | 2 -- structurizr-core/src/com/structurizr/model/Element.java | 1 - .../src/com/structurizr/model/GroupableElement.java | 6 ------ .../src/com/structurizr/model/Relationship.java | 1 - .../src/com/structurizr/model/StaticStructureElement.java | 2 -- .../structurizr/model/StaticStructureElementInstance.java | 1 - structurizr-core/src/com/structurizr/view/DynamicView.java | 1 - structurizr-core/src/com/structurizr/view/ElementStyle.java | 1 - structurizr-core/src/com/structurizr/view/Styles.java | 1 - .../test/unit/com/structurizr/WorkspaceTests.java | 2 -- .../test/unit/com/structurizr/model/ElementTests.java | 4 ---- .../test/unit/com/structurizr/model/ModelTests.java | 1 - .../test/unit/com/structurizr/view/ContainerViewTests.java | 4 ---- 16 files changed, 31 deletions(-) diff --git a/structurizr-client/src/com/structurizr/io/json/JsonReader.java b/structurizr-client/src/com/structurizr/io/json/JsonReader.java index c85ee6665..3cca175a8 100644 --- a/structurizr-client/src/com/structurizr/io/json/JsonReader.java +++ b/structurizr-client/src/com/structurizr/io/json/JsonReader.java @@ -5,7 +5,6 @@ import com.structurizr.io.WorkspaceReader; import com.structurizr.io.WorkspaceReaderException; import com.structurizr.model.IdGenerator; -import com.structurizr.model.SequentialIntegerIdGeneratorStrategy; import java.io.IOException; import java.io.Reader; diff --git a/structurizr-client/test/unit/com/structurizr/util/WorkspaceUtilsTests.java b/structurizr-client/test/unit/com/structurizr/util/WorkspaceUtilsTests.java index 0657d4ebf..34650ecd2 100644 --- a/structurizr-client/test/unit/com/structurizr/util/WorkspaceUtilsTests.java +++ b/structurizr-client/test/unit/com/structurizr/util/WorkspaceUtilsTests.java @@ -4,7 +4,6 @@ import org.junit.jupiter.api.Test; import java.io.File; -import java.io.FilenameFilter; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; diff --git a/structurizr-core/src/com/structurizr/documentation/Documentable.java b/structurizr-core/src/com/structurizr/documentation/Documentable.java index 9ed7a6705..495310feb 100644 --- a/structurizr-core/src/com/structurizr/documentation/Documentable.java +++ b/structurizr-core/src/com/structurizr/documentation/Documentable.java @@ -1,7 +1,5 @@ package com.structurizr.documentation; -import com.structurizr.documentation.Documentation; - /** * Marker interface for items that can have documentation attached (i.e. workspaces and software systems). */ diff --git a/structurizr-core/src/com/structurizr/model/CustomElement.java b/structurizr-core/src/com/structurizr/model/CustomElement.java index 0b745c3f7..53105607a 100644 --- a/structurizr-core/src/com/structurizr/model/CustomElement.java +++ b/structurizr-core/src/com/structurizr/model/CustomElement.java @@ -1,7 +1,5 @@ package com.structurizr.model; -import com.structurizr.util.StringUtils; - import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.Collections; diff --git a/structurizr-core/src/com/structurizr/model/Element.java b/structurizr-core/src/com/structurizr/model/Element.java index 01937349a..7d4c994cd 100644 --- a/structurizr-core/src/com/structurizr/model/Element.java +++ b/structurizr-core/src/com/structurizr/model/Element.java @@ -1,7 +1,6 @@ package com.structurizr.model; import com.fasterxml.jackson.annotation.JsonIgnore; -import com.structurizr.util.Url; import javax.annotation.Nonnull; import javax.annotation.Nullable; diff --git a/structurizr-core/src/com/structurizr/model/GroupableElement.java b/structurizr-core/src/com/structurizr/model/GroupableElement.java index 3ddcf516b..cae1cb4b8 100644 --- a/structurizr-core/src/com/structurizr/model/GroupableElement.java +++ b/structurizr-core/src/com/structurizr/model/GroupableElement.java @@ -2,12 +2,6 @@ import com.structurizr.util.StringUtils; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.Set; - /** * Represents an element that can be included in a group. */ diff --git a/structurizr-core/src/com/structurizr/model/Relationship.java b/structurizr-core/src/com/structurizr/model/Relationship.java index 61b3521e0..5c3dc2b2e 100644 --- a/structurizr-core/src/com/structurizr/model/Relationship.java +++ b/structurizr-core/src/com/structurizr/model/Relationship.java @@ -1,7 +1,6 @@ package com.structurizr.model; import com.fasterxml.jackson.annotation.JsonIgnore; -import com.structurizr.util.Url; import java.util.Collections; import java.util.LinkedHashSet; diff --git a/structurizr-core/src/com/structurizr/model/StaticStructureElement.java b/structurizr-core/src/com/structurizr/model/StaticStructureElement.java index 56d7b055e..ff6566249 100644 --- a/structurizr-core/src/com/structurizr/model/StaticStructureElement.java +++ b/structurizr-core/src/com/structurizr/model/StaticStructureElement.java @@ -1,7 +1,5 @@ package com.structurizr.model; -import com.structurizr.util.StringUtils; - import javax.annotation.Nonnull; import javax.annotation.Nullable; diff --git a/structurizr-core/src/com/structurizr/model/StaticStructureElementInstance.java b/structurizr-core/src/com/structurizr/model/StaticStructureElementInstance.java index e79c7dab4..6ed26d0c2 100644 --- a/structurizr-core/src/com/structurizr/model/StaticStructureElementInstance.java +++ b/structurizr-core/src/com/structurizr/model/StaticStructureElementInstance.java @@ -5,7 +5,6 @@ import com.structurizr.util.Url; import javax.annotation.Nonnull; -import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Set; diff --git a/structurizr-core/src/com/structurizr/view/DynamicView.java b/structurizr-core/src/com/structurizr/view/DynamicView.java index f2b566454..a8ca3eaec 100644 --- a/structurizr-core/src/com/structurizr/view/DynamicView.java +++ b/structurizr-core/src/com/structurizr/view/DynamicView.java @@ -6,7 +6,6 @@ import javax.annotation.Nonnull; import java.util.*; -import java.util.stream.Collectors; /** * A dynamic view, used to describe behaviour between static elements at runtime. diff --git a/structurizr-core/src/com/structurizr/view/ElementStyle.java b/structurizr-core/src/com/structurizr/view/ElementStyle.java index fc61528e5..75999d2ea 100644 --- a/structurizr-core/src/com/structurizr/view/ElementStyle.java +++ b/structurizr-core/src/com/structurizr/view/ElementStyle.java @@ -1,7 +1,6 @@ package com.structurizr.view; import com.fasterxml.jackson.annotation.JsonInclude; -import com.structurizr.PropertyHolder; import com.structurizr.util.ImageUtils; import com.structurizr.util.StringUtils; diff --git a/structurizr-core/src/com/structurizr/view/Styles.java b/structurizr-core/src/com/structurizr/view/Styles.java index 44a290ddc..181ee0ce7 100644 --- a/structurizr-core/src/com/structurizr/view/Styles.java +++ b/structurizr-core/src/com/structurizr/view/Styles.java @@ -1,6 +1,5 @@ package com.structurizr.view; -import com.structurizr.Workspace; import com.structurizr.model.*; import com.structurizr.util.StringUtils; diff --git a/structurizr-core/test/unit/com/structurizr/WorkspaceTests.java b/structurizr-core/test/unit/com/structurizr/WorkspaceTests.java index 447687bca..67e84dfd0 100644 --- a/structurizr-core/test/unit/com/structurizr/WorkspaceTests.java +++ b/structurizr-core/test/unit/com/structurizr/WorkspaceTests.java @@ -7,8 +7,6 @@ import com.structurizr.model.SoftwareSystem; import org.junit.jupiter.api.Test; -import java.io.File; - import static org.junit.jupiter.api.Assertions.*; public class WorkspaceTests { diff --git a/structurizr-core/test/unit/com/structurizr/model/ElementTests.java b/structurizr-core/test/unit/com/structurizr/model/ElementTests.java index 6dfbe398b..1bf94229e 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ElementTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ElementTests.java @@ -1,12 +1,8 @@ package com.structurizr.model; import com.structurizr.AbstractWorkspaceTestBase; -import com.structurizr.Workspace; import org.junit.jupiter.api.Test; -import java.util.HashMap; -import java.util.Map; - import static org.junit.jupiter.api.Assertions.*; public class ElementTests extends AbstractWorkspaceTestBase { diff --git a/structurizr-core/test/unit/com/structurizr/model/ModelTests.java b/structurizr-core/test/unit/com/structurizr/model/ModelTests.java index 1fc5381a6..3d429a746 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ModelTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ModelTests.java @@ -4,7 +4,6 @@ import org.junit.jupiter.api.Test; import java.util.Collections; -import java.util.Set; import static org.junit.jupiter.api.Assertions.*; diff --git a/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java b/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java index 801e481bd..2c0c52b9a 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java @@ -1,14 +1,10 @@ package com.structurizr.view; import com.structurizr.AbstractWorkspaceTestBase; -import com.structurizr.Workspace; import com.structurizr.model.*; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import java.util.HashMap; -import java.util.Map; - import static org.junit.jupiter.api.Assertions.*; public class ContainerViewTests extends AbstractWorkspaceTestBase { From 3b73e6a5b82bb0c434c81862d1128014b6a73062 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 14 Aug 2022 16:27:43 +0100 Subject: [PATCH 306/717] Updated the build instructions. --- docs/building.md | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/docs/building.md b/docs/building.md index f6f8b2402..fdb06e217 100644 --- a/docs/building.md +++ b/docs/building.md @@ -2,16 +2,10 @@ [![Build Status](https://travis-ci.org/structurizr/java.svg?branch=master)](https://travis-ci.org/structurizr/java) -To build "Structurizr for Java" from the sources (you'll need Java 8)... +To build "Structurizr for Java" from the sources (you'll need Java 11+ installed)... ``` -git clone https://github.com/structurizr/java.git -cd java +git clone https://github.com/structurizr/java.git structurizr-java +cd structurizr-java ./gradlew compileJava test -``` - -If necessary, after building, you can install "Structurizr for Java" into your local Maven repo using: - -``` -./gradlew publishToMavenLocal ``` \ No newline at end of file From 3e9fa2f7cbe9f7d40e0b399d8ac71e543d52d6bd Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 14 Aug 2022 16:28:08 +0100 Subject: [PATCH 307/717] Added some Javadoc. --- .../src/com/structurizr/view/Styles.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/structurizr-core/src/com/structurizr/view/Styles.java b/structurizr-core/src/com/structurizr/view/Styles.java index 44a290ddc..8d4059a62 100644 --- a/structurizr-core/src/com/structurizr/view/Styles.java +++ b/structurizr-core/src/com/structurizr/view/Styles.java @@ -145,6 +145,16 @@ public RelationshipStyle findRelationshipStyle(String tag) { return style; } + /** + * Finds the element style used to render the specified element, according to the following rules: + * + * 1. Start with a default style. + * 2. Calculate set of tags associated with the element. + * 3. Find the style properties for each tag (themes first, followed by workspace styles) + * + * @param element an Element object + * @return an ElementStyle object + */ public ElementStyle findElementStyle(Element element) { ElementStyle style = new ElementStyle("").background("#dddddd").color("#000000").shape(Shape.Box).fontSize(24).border(Border.Solid).opacity(100).metadata(true).description(true); @@ -199,6 +209,16 @@ public ElementStyle findElementStyle(Element element) { return style; } + /** + * Finds the relationship style used to render the specified relationship, according to the following rules: + * + * 1. Start with a default style. + * 2. Calculate set of tags associated with the relationship, and any linked relationship(s). + * 3. Find the style properties for each tag (themes first, followed by workspace styles) + * + * @param relationship a Relationship object + * @return a RelationshipStyle object + */ public RelationshipStyle findRelationshipStyle(Relationship relationship) { RelationshipStyle style = new RelationshipStyle("").thickness(2).color("#707070").dashed(true).routing(Routing.Direct).fontSize(24).width(200).position(50).opacity(100); From 440e5595303cd8f2704e29d23d239b498ec2fc62 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 14 Aug 2022 16:30:55 +0100 Subject: [PATCH 308/717] Removed the Travis build and status. --- .travis.yml | 9 --------- docs/building.md | 2 -- 2 files changed, 11 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 14e654e53..000000000 --- a/.travis.yml +++ /dev/null @@ -1,9 +0,0 @@ -language: java - -jdk: - - oraclejdk8 - -install: true - -script: - - ./gradlew --info \ No newline at end of file diff --git a/docs/building.md b/docs/building.md index fdb06e217..8e9891a34 100644 --- a/docs/building.md +++ b/docs/building.md @@ -1,7 +1,5 @@ # Building -[![Build Status](https://travis-ci.org/structurizr/java.svg?branch=master)](https://travis-ci.org/structurizr/java) - To build "Structurizr for Java" from the sources (you'll need Java 11+ installed)... ``` From 63442f4b0e064d4d52cfdb7bc83b9e6895824e73 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 14 Aug 2022 16:34:36 +0100 Subject: [PATCH 309/717] Updated build instructions again. --- docs/building.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/building.md b/docs/building.md index 8e9891a34..8eed1c9ae 100644 --- a/docs/building.md +++ b/docs/building.md @@ -1,6 +1,6 @@ # Building -To build "Structurizr for Java" from the sources (you'll need Java 11+ installed)... +To build this repo from the sources (you'll need `git` and Java 11+ installed)... ``` git clone https://github.com/structurizr/java.git structurizr-java From 6ac239077edac55b90225dd75d4743b793bd606e Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 14 Aug 2022 16:43:27 +0100 Subject: [PATCH 310/717] Updated build files. --- build.gradle | 2 +- docs/building.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 5200d34b9..4c6b854f2 100644 --- a/build.gradle +++ b/build.gradle @@ -30,7 +30,7 @@ subprojects { proj -> compileJava.options.encoding = 'UTF-8' compileTestJava.options.encoding = 'UTF-8' - sourceCompatibility = 1.8 + sourceCompatibility = 1.11 targetCompatibility = 1.8 java { diff --git a/docs/building.md b/docs/building.md index 8eed1c9ae..a68e36640 100644 --- a/docs/building.md +++ b/docs/building.md @@ -5,5 +5,5 @@ To build this repo from the sources (you'll need `git` and Java 11+ installed).. ``` git clone https://github.com/structurizr/java.git structurizr-java cd structurizr-java -./gradlew compileJava test +./gradlew ``` \ No newline at end of file From 2bbc461ccac077195900ea821475a0c1e545813e Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 14 Aug 2022 16:45:19 +0100 Subject: [PATCH 311/717] Fixed. --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 4c6b854f2..5200d34b9 100644 --- a/build.gradle +++ b/build.gradle @@ -30,7 +30,7 @@ subprojects { proj -> compileJava.options.encoding = 'UTF-8' compileTestJava.options.encoding = 'UTF-8' - sourceCompatibility = 1.11 + sourceCompatibility = 1.8 targetCompatibility = 1.8 java { From 702a4de94ad76ed79c7b30d385067d76d2d8d085 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 14 Aug 2022 16:49:37 +0100 Subject: [PATCH 312/717] Updated to reflect release. --- docs/changelog.md | 2 +- docs/getting-started.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 10c2116f9..ee2c041d4 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 1.14.0 (unreleased to Maven Central) +## 1.14.0 (14th August 2022) - Adds a helper method (`AbstractImpliedRelationshipsStrategy.createImpliedRelationship`) to create implied relationships, which can then be used by custom implementations. - Provides a way to add specific relationships to dynamic views. diff --git a/docs/getting-started.md b/docs/getting-started.md index 43673ba73..ff169ba21 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -12,7 +12,7 @@ The Structurizr for Java binaries are hosted on [Maven Central](https://repo1.ma Name | Description ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-client:1.12.1 | The Structurizr API client library. +com.structurizr:structurizr-client:1.14.0 | The Structurizr API client library. ## 2. Create a Java program From d0697c50794a4f705b8e5cb4705d39394649289c Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 15 Aug 2022 12:12:36 +0100 Subject: [PATCH 313/717] Enables `structurizr-core` to be used as a transitive dependency by consumers of `structurizr-client`. --- build.gradle | 4 ++-- docs/changelog.md | 4 ++++ docs/getting-started.md | 2 +- structurizr-client/build.gradle | 2 +- structurizr-core/build.gradle | 1 + 5 files changed, 9 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 5200d34b9..0136eac58 100644 --- a/build.gradle +++ b/build.gradle @@ -2,13 +2,13 @@ defaultTasks 'clean', 'compileJava', 'test' subprojects { proj -> - apply plugin: 'java' + apply plugin: 'java-library' apply plugin: 'maven-publish' apply plugin: 'signing' description = 'Structurizr' group = 'com.structurizr' - version = '1.14.0' + version = '1.14.1' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index ee2c041d4..e6fa45ba8 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.14.1 (15th August 2022) + +- Enables `structurizr-core` to be used as a transitive dependency by consumers of `structurizr-client`. + ## 1.14.0 (14th August 2022) - Adds a helper method (`AbstractImpliedRelationshipsStrategy.createImpliedRelationship`) to create implied relationships, which can then be used by custom implementations. diff --git a/docs/getting-started.md b/docs/getting-started.md index ff169ba21..79b918070 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -12,7 +12,7 @@ The Structurizr for Java binaries are hosted on [Maven Central](https://repo1.ma Name | Description ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-client:1.14.0 | The Structurizr API client library. +com.structurizr:structurizr-client:1.14.1 | The Structurizr API client library. ## 2. Create a Java program diff --git a/structurizr-client/build.gradle b/structurizr-client/build.gradle index 6c5bd755d..5b059708c 100644 --- a/structurizr-client/build.gradle +++ b/structurizr-client/build.gradle @@ -1,6 +1,6 @@ dependencies { - implementation project(':structurizr-core') + api project(':structurizr-core') implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.3' diff --git a/structurizr-core/build.gradle b/structurizr-core/build.gradle index 4f395e47f..8b7fdb3ce 100644 --- a/structurizr-core/build.gradle +++ b/structurizr-core/build.gradle @@ -7,4 +7,5 @@ dependencies { testImplementation 'junit:junit:4.12' testImplementation 'org.assertj:assertj-core:3.9.1' + } \ No newline at end of file From c312f03a930f19d1620280a72f13253ca6bacbdf Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 15 Aug 2022 12:34:16 +0100 Subject: [PATCH 314/717] Reintroduce client integration tests into the build ... these were removed during the Gradle 7 upgrade. --- build.gradle | 4 ++++ structurizr-client/build.gradle | 14 ++++++++++---- structurizr-core/build.gradle | 5 ++--- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/build.gradle b/build.gradle index 0136eac58..9099392bb 100644 --- a/build.gradle +++ b/build.gradle @@ -27,6 +27,10 @@ subprojects { proj -> } } + test { + useJUnitPlatform() + } + compileJava.options.encoding = 'UTF-8' compileTestJava.options.encoding = 'UTF-8' diff --git a/structurizr-client/build.gradle b/structurizr-client/build.gradle index 15e4035ee..2660a56ad 100644 --- a/structurizr-client/build.gradle +++ b/structurizr-client/build.gradle @@ -10,9 +10,15 @@ dependencies { implementation 'commons-logging:commons-logging:1.2' - testImplementation(platform('org.junit:junit-bom:5.9.0')) - testImplementation('org.junit.jupiter:junit-jupiter') + testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' + } -test { - useJUnitPlatform() + +sourceSets { + test { + java { + srcDir 'test/unit' + srcDir 'test/integration' + } + } } \ No newline at end of file diff --git a/structurizr-core/build.gradle b/structurizr-core/build.gradle index 25170ef20..08250abb3 100644 --- a/structurizr-core/build.gradle +++ b/structurizr-core/build.gradle @@ -5,8 +5,7 @@ dependencies { implementation 'commons-logging:commons-logging:1.2' - testImplementation(platform('org.junit:junit-bom:5.9.0')) - testImplementation('org.junit.jupiter:junit-jupiter') - testImplementation 'org.assertj:assertj-core:3.9.1' + testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' + testImplementation 'org.assertj:assertj-core:3.23.1' } \ No newline at end of file From 8a7d3552db815571504d95a62ad21162d0725970 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 15 Aug 2022 12:45:26 +0100 Subject: [PATCH 315/717] Remove `test_` prefix from method names. --- .../StructurizrClientIntegrationTests.java | 8 +- .../api/WorkspaceRulesValidationTests.java | 36 ++--- .../com/structurizr/api/ApiResponseTests.java | 2 +- ...shBasedMessageAuthenticationCodeTests.java | 2 +- .../api/HmacAuthorizationHeaderTests.java | 4 +- .../com/structurizr/api/HmacContentTests.java | 4 +- .../com/structurizr/api/Md5DigestTests.java | 4 +- .../api/StructurizrClientTests.java | 34 ++--- .../AesEncryptionStrategyTests.java | 16 +- .../encryption/EncryptedWorkspaceTests.java | 8 +- .../io/json/EncryptedJsonTests.java | 2 +- .../io/json/EncryptedJsonWriterTests.java | 4 +- .../com/structurizr/io/json/JsonTests.java | 6 +- .../structurizr/io/json/JsonWriterTests.java | 4 +- .../structurizr/util/WorkspaceUtilsTests.java | 20 +-- .../com/structurizr/view/ThemeUtilsTests.java | 10 +- .../unit/com/structurizr/WorkspaceTests.java | 12 +- .../WorkspaceConfigurationTests.java | 8 +- .../documentation/DecisionTests.java | 2 +- .../documentation/DocumentationTests.java | 22 +-- .../documentation/SectionTests.java | 2 +- .../structurizr/model/CodeElementTests.java | 36 ++--- .../com/structurizr/model/ComponentTests.java | 32 ++-- .../model/ContainerInstanceTests.java | 38 ++--- .../com/structurizr/model/ContainerTests.java | 38 ++--- ...essAnyRelationshipExistsStrategyTests.java | 4 +- ...ssSameRelationshipExistsStrategyTests.java | 2 +- .../structurizr/model/CustomElementTests.java | 18 +-- ...aultImpliedRelationshipsStrategyTests.java | 2 +- .../model/DeploymentNodeTests.java | 40 ++--- .../com/structurizr/model/ElementTests.java | 58 +++---- .../model/GroupableElementTests.java | 8 +- .../model/HttpHealthCheckTests.java | 14 +- .../model/InfrastructureNodeTests.java | 8 +- .../com/structurizr/model/ModelItemTests.java | 46 +++--- .../com/structurizr/model/ModelTests.java | 114 +++++++------- .../com/structurizr/model/PersonTests.java | 16 +- .../structurizr/model/RelationshipTests.java | 26 ++-- .../model/SoftwareSystemInstanceTests.java | 38 ++--- .../model/SoftwareSystemTests.java | 46 +++--- .../com/structurizr/util/ImageUtilsTests.java | 34 ++--- .../structurizr/util/StringUtilsTests.java | 6 +- .../unit/com/structurizr/util/UrlTests.java | 8 +- .../view/AutomaticLayoutTests.java | 10 +- .../com/structurizr/view/BrandingTests.java | 12 +- .../com/structurizr/view/ColorPairTests.java | 18 +-- .../unit/com/structurizr/view/ColorTests.java | 8 +- .../structurizr/view/ComponentViewTests.java | 94 ++++++------ .../structurizr/view/ConfigurationTests.java | 16 +- .../structurizr/view/ContainerViewTests.java | 42 ++--- .../view/DefaultLayoutMergeStrategyTests.java | 14 +- .../structurizr/view/DeploymentViewTests.java | 66 ++++---- .../com/structurizr/view/DimensionsTests.java | 6 +- .../structurizr/view/DynamicViewTests.java | 48 +++--- .../structurizr/view/ElementStyleTests.java | 56 +++---- .../structurizr/view/ElementViewTests.java | 4 +- .../structurizr/view/FilteredViewTests.java | 2 +- .../unit/com/structurizr/view/FontTests.java | 8 +- .../com/structurizr/view/PaperSizeTests.java | 6 +- .../view/RelationshipStyleTests.java | 36 ++--- .../view/SequenceCounterTests.java | 2 +- .../structurizr/view/SequenceNumberTests.java | 4 +- .../com/structurizr/view/StaticViewTests.java | 8 +- .../com/structurizr/view/StylesTests.java | 38 ++--- .../view/SystemContextViewTests.java | 44 +++--- .../view/SystemLandscapeViewTests.java | 34 ++--- .../structurizr/view/TerminologyTests.java | 2 +- .../com/structurizr/view/VertexTests.java | 2 +- .../com/structurizr/view/ViewSetTests.java | 144 +++++++++--------- .../unit/com/structurizr/view/ViewTests.java | 60 ++++---- 70 files changed, 813 insertions(+), 813 deletions(-) diff --git a/structurizr-client/test/integration/com/structurizr/api/StructurizrClientIntegrationTests.java b/structurizr-client/test/integration/com/structurizr/api/StructurizrClientIntegrationTests.java index 3c0fd2ba7..48712d3ce 100644 --- a/structurizr-client/test/integration/com/structurizr/api/StructurizrClientIntegrationTests.java +++ b/structurizr-client/test/integration/com/structurizr/api/StructurizrClientIntegrationTests.java @@ -51,7 +51,7 @@ private File getArchivedWorkspace() { } @Test - void test_putAndGetWorkspace_WithoutEncryption() throws Exception { + void putAndGetWorkspace_WithoutEncryption() throws Exception { Workspace workspace = new Workspace("Structurizr client library tests - without encryption", "A test workspace for the Structurizr client library"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); Person person = workspace.getModel().addPerson("Person", "Description"); @@ -77,7 +77,7 @@ void test_putAndGetWorkspace_WithoutEncryption() throws Exception { } @Test - void test_putAndGetWorkspace_WithEncryption() throws Exception { + void putAndGetWorkspace_WithEncryption() throws Exception { structurizrClient.setEncryptionStrategy(new AesEncryptionStrategy("password")); Workspace workspace = new Workspace("Structurizr client library tests - with encryption", "A test workspace for the Structurizr client library"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); @@ -104,14 +104,14 @@ void test_putAndGetWorkspace_WithEncryption() throws Exception { } @Test - void test_lockWorkspace() throws Exception { + void lockWorkspace() throws Exception { structurizrClient.unlockWorkspace(20081); assertTrue(structurizrClient.lockWorkspace(20081)); } @Test - void test_unlockWorkspace() throws Exception { + void unlockWorkspace() throws Exception { structurizrClient.lockWorkspace(20081); assertTrue(structurizrClient.unlockWorkspace(20081)); } diff --git a/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java b/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java index 4319f7128..efd07a640 100644 --- a/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java +++ b/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java @@ -13,7 +13,7 @@ public class WorkspaceRulesValidationTests { private static final File PATH_TO_WORKSPACE_FILES = new File("test/integration/workspaceValidation"); @Test - void test_exceptionThrown_WhenElementIdsAreNotUnique() throws Exception { + void exceptionThrown_WhenElementIdsAreNotUnique() throws Exception { try { WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "ElementIdsAreNotUnique.json")); fail(); @@ -24,7 +24,7 @@ void test_exceptionThrown_WhenElementIdsAreNotUnique() throws Exception { } @Test - void test_exceptionThrown_WhenRelationshipIdsAreNotUnique() throws Exception { + void exceptionThrown_WhenRelationshipIdsAreNotUnique() throws Exception { try { WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "RelationshipIdsAreNotUnique.json")); fail(); @@ -35,7 +35,7 @@ void test_exceptionThrown_WhenRelationshipIdsAreNotUnique() throws Exception { } @Test - void test_exceptionThrown_WhenViewKeysAreNotUnique() throws Exception { + void exceptionThrown_WhenViewKeysAreNotUnique() throws Exception { try { WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "ViewKeysAreNotUnique.json")); fail(); @@ -45,7 +45,7 @@ void test_exceptionThrown_WhenViewKeysAreNotUnique() throws Exception { } @Test - void test_exceptionThrown_WhenPeopleAndSoftwareSystemNamesAreNotUnique() throws Exception { + void exceptionThrown_WhenPeopleAndSoftwareSystemNamesAreNotUnique() throws Exception { try { WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "PeopleAndSoftwareSystemNamesAreNotUnique.json")); fail(); @@ -55,7 +55,7 @@ void test_exceptionThrown_WhenPeopleAndSoftwareSystemNamesAreNotUnique() throws } @Test - void test_exceptionThrown_WhenContainerNamesAreNotUnique() throws Exception { + void exceptionThrown_WhenContainerNamesAreNotUnique() throws Exception { try { WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "ContainerNamesAreNotUnique.json")); fail(); @@ -65,7 +65,7 @@ void test_exceptionThrown_WhenContainerNamesAreNotUnique() throws Exception { } @Test - void test_exceptionThrown_WhenComponentNamesAreNotUnique() throws Exception { + void exceptionThrown_WhenComponentNamesAreNotUnique() throws Exception { try { WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "ComponentNamesAreNotUnique.json")); fail(); @@ -75,7 +75,7 @@ void test_exceptionThrown_WhenComponentNamesAreNotUnique() throws Exception { } @Test - void test_exceptionThrown_WhenTopLevelDeploymentNodeNamesAreNotUnique() throws Exception { + void exceptionThrown_WhenTopLevelDeploymentNodeNamesAreNotUnique() throws Exception { try { WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "TopLevelDeploymentNodeNamesAreNotUnique.json")); fail(); @@ -85,12 +85,12 @@ void test_exceptionThrown_WhenTopLevelDeploymentNodeNamesAreNotUnique() throws E } @Test - void test_exceptionNotThrown_WhenTopLevelDeploymentNodeNamesAreNotUniqueButTheyExistInDifferentEnvironments() throws Exception { + void exceptionNotThrown_WhenTopLevelDeploymentNodeNamesAreNotUniqueButTheyExistInDifferentEnvironments() throws Exception { WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "TopLevelDeploymentNodeNamesAreNotUniqueButTheyExistInDifferentEnvironments.json")); } @Test - void test_exceptionThrown_WhenChildDeploymentNodeNamesAreNotUnique() throws Exception { + void exceptionThrown_WhenChildDeploymentNodeNamesAreNotUnique() throws Exception { try { WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "ChildDeploymentNodeNamesAreNotUnique.json")); fail(); @@ -100,7 +100,7 @@ void test_exceptionThrown_WhenChildDeploymentNodeNamesAreNotUnique() throws Exce } @Test - void test_exceptionThrown_WhenRelationshipDescriptionsAreNotUnique() throws Exception { + void exceptionThrown_WhenRelationshipDescriptionsAreNotUnique() throws Exception { try { WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "RelationshipDescriptionsAreNotUnique.json")); fail(); @@ -110,7 +110,7 @@ void test_exceptionThrown_WhenRelationshipDescriptionsAreNotUnique() throws Exce } @Test - void test_exceptionThrown_WhenSoftwareSystemAssociatedWithSystemContextViewIsMissingFromTheModel() throws Exception { + void exceptionThrown_WhenSoftwareSystemAssociatedWithSystemContextViewIsMissingFromTheModel() throws Exception { try { WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "SoftwareSystemAssociatedWithSystemContextViewIsMissingFromTheModel.json")); fail(); @@ -120,7 +120,7 @@ void test_exceptionThrown_WhenSoftwareSystemAssociatedWithSystemContextViewIsMis } @Test - void test_exceptionThrown_WhenSoftwareSystemAssociatedWithContainerViewIsMissingFromTheModel() throws Exception { + void exceptionThrown_WhenSoftwareSystemAssociatedWithContainerViewIsMissingFromTheModel() throws Exception { try { WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "SoftwareSystemAssociatedWithContainerViewIsMissingFromTheModel.json")); fail(); @@ -130,7 +130,7 @@ void test_exceptionThrown_WhenSoftwareSystemAssociatedWithContainerViewIsMissing } @Test - void test_exceptionThrown_WhenContainerAssociatedWithComponentViewIsMissingFromTheModel() throws Exception { + void exceptionThrown_WhenContainerAssociatedWithComponentViewIsMissingFromTheModel() throws Exception { try { WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "ContainerAssociatedWithComponentViewIsMissingFromTheModel.json")); fail(); @@ -140,7 +140,7 @@ void test_exceptionThrown_WhenContainerAssociatedWithComponentViewIsMissingFromT } @Test - void test_exceptionThrown_WhenElementAssociatedWithDynamicViewIsMissingFromTheModel() throws Exception { + void exceptionThrown_WhenElementAssociatedWithDynamicViewIsMissingFromTheModel() throws Exception { try { WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "ElementAssociatedWithDynamicViewIsMissingFromTheModel.json")); fail(); @@ -150,7 +150,7 @@ void test_exceptionThrown_WhenElementAssociatedWithDynamicViewIsMissingFromTheMo } @Test - void test_exceptionThrown_WhenSoftwareSystemAssociatedWithDeploymentViewIsMissingFromTheModel() throws Exception { + void exceptionThrown_WhenSoftwareSystemAssociatedWithDeploymentViewIsMissingFromTheModel() throws Exception { try { WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "SoftwareSystemAssociatedWithDeploymentViewIsMissingFromTheModel.json")); fail(); @@ -160,7 +160,7 @@ void test_exceptionThrown_WhenSoftwareSystemAssociatedWithDeploymentViewIsMissin } @Test - void test_exceptionThrown_WhenViewAssociatedWithFilteredViewIsMissingFromTheWorkspace() throws Exception { + void exceptionThrown_WhenViewAssociatedWithFilteredViewIsMissingFromTheWorkspace() throws Exception { try { WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "ViewAssociatedWithFilteredViewIsMissingFromTheWorkspace.json")); fail(); @@ -170,7 +170,7 @@ void test_exceptionThrown_WhenViewAssociatedWithFilteredViewIsMissingFromTheWork } @Test - void test_exceptionThrown_WhenElementReferencedByViewIsMissingFromTheModel() throws Exception { + void exceptionThrown_WhenElementReferencedByViewIsMissingFromTheModel() throws Exception { try { WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "ElementReferencedByViewIsMissingFromTheModel.json")); fail(); @@ -180,7 +180,7 @@ void test_exceptionThrown_WhenElementReferencedByViewIsMissingFromTheModel() thr } @Test - void test_exceptionThrown_WhenRelationshipReferencedByViewIsMissingFromTheModel() throws Exception { + void exceptionThrown_WhenRelationshipReferencedByViewIsMissingFromTheModel() throws Exception { try { WorkspaceUtils.loadWorkspaceFromJson(new File(PATH_TO_WORKSPACE_FILES, "RelationshipReferencedByViewIsMissingFromTheModel.json")); fail(); diff --git a/structurizr-client/test/unit/com/structurizr/api/ApiResponseTests.java b/structurizr-client/test/unit/com/structurizr/api/ApiResponseTests.java index 0c416cc28..d3546f95a 100644 --- a/structurizr-client/test/unit/com/structurizr/api/ApiResponseTests.java +++ b/structurizr-client/test/unit/com/structurizr/api/ApiResponseTests.java @@ -7,7 +7,7 @@ public class ApiResponseTests { @Test - void test_parse_createsAnApiErrorObjectWithTheSpecifiedErrorMessage() throws Exception { + void parse_createsAnApiErrorObjectWithTheSpecifiedErrorMessage() throws Exception { ApiResponse apiResponse = ApiResponse.parse("{\"message\": \"Hello\"}"); assertEquals("Hello", apiResponse.getMessage()); } diff --git a/structurizr-client/test/unit/com/structurizr/api/HashBasedMessageAuthenticationCodeTests.java b/structurizr-client/test/unit/com/structurizr/api/HashBasedMessageAuthenticationCodeTests.java index 08a930ccd..539d7a733 100644 --- a/structurizr-client/test/unit/com/structurizr/api/HashBasedMessageAuthenticationCodeTests.java +++ b/structurizr-client/test/unit/com/structurizr/api/HashBasedMessageAuthenticationCodeTests.java @@ -9,7 +9,7 @@ public class HashBasedMessageAuthenticationCodeTests { private HashBasedMessageAuthenticationCode code; @Test - void test_generate() throws Exception { + void generate() throws Exception { // this example is taken from http://en.wikipedia.org/wiki/Hash-based_message_authentication_code code = new HashBasedMessageAuthenticationCode("key"); assertEquals("f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8", code.generate("The quick brown fox jumps over the lazy dog")); diff --git a/structurizr-client/test/unit/com/structurizr/api/HmacAuthorizationHeaderTests.java b/structurizr-client/test/unit/com/structurizr/api/HmacAuthorizationHeaderTests.java index df5ff9166..86c79a6c6 100644 --- a/structurizr-client/test/unit/com/structurizr/api/HmacAuthorizationHeaderTests.java +++ b/structurizr-client/test/unit/com/structurizr/api/HmacAuthorizationHeaderTests.java @@ -9,13 +9,13 @@ public class HmacAuthorizationHeaderTests { private HmacAuthorizationHeader header; @Test - void test_format() { + void format() { header = new HmacAuthorizationHeader("apiKey", "f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8"); assertEquals("apiKey:ZjdiYzgzZjQzMDUzODQyNGIxMzI5OGU2YWE2ZmIxNDNlZjRkNTlhMTQ5NDYxNzU5OTc0NzlkYmMyZDFhM2NkOA==", header.format()); } @Test - void test_parse() { + void parse() { header = HmacAuthorizationHeader.parse("apiKey:ZjdiYzgzZjQzMDUzODQyNGIxMzI5OGU2YWE2ZmIxNDNlZjRkNTlhMTQ5NDYxNzU5OTc0NzlkYmMyZDFhM2NkOA=="); assertEquals("apiKey", header.getApiKey()); assertEquals("f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8", header.getHmac()); diff --git a/structurizr-client/test/unit/com/structurizr/api/HmacContentTests.java b/structurizr-client/test/unit/com/structurizr/api/HmacContentTests.java index 3ee024bdd..ba81a6b96 100644 --- a/structurizr-client/test/unit/com/structurizr/api/HmacContentTests.java +++ b/structurizr-client/test/unit/com/structurizr/api/HmacContentTests.java @@ -8,12 +8,12 @@ public class HmacContentTests { @Test - void test_toString_WhenThereAreNoStrings() { + void toString_WhenThereAreNoStrings() { assertEquals("", new HmacContent().toString()); } @Test - void test_toString_WhenThereAreSomeStrings() { + void toString_WhenThereAreSomeStrings() { assertEquals("String1\nString2\nString3\n", new HmacContent("String1", "String2", "String3").toString()); } diff --git a/structurizr-client/test/unit/com/structurizr/api/Md5DigestTests.java b/structurizr-client/test/unit/com/structurizr/api/Md5DigestTests.java index c880e38b3..220270830 100644 --- a/structurizr-client/test/unit/com/structurizr/api/Md5DigestTests.java +++ b/structurizr-client/test/unit/com/structurizr/api/Md5DigestTests.java @@ -9,12 +9,12 @@ public class Md5DigestTests { private Md5Digest md5 = new Md5Digest(); @Test - void test_generate_TreatsNullAsEmptyContent() throws Exception { + void generate_TreatsNullAsEmptyContent() throws Exception { assertEquals(md5.generate(null), md5.generate("")); } @Test - void test_generate() throws Exception { + void generate() throws Exception { assertEquals("ed076287532e86365e841e92bfc50d8c", md5.generate("Hello World!")); assertEquals("d41d8cd98f00b204e9800998ecf8427e", md5.generate("")); } diff --git a/structurizr-client/test/unit/com/structurizr/api/StructurizrClientTests.java b/structurizr-client/test/unit/com/structurizr/api/StructurizrClientTests.java index c7d4ecd2a..4b7fdef59 100644 --- a/structurizr-client/test/unit/com/structurizr/api/StructurizrClientTests.java +++ b/structurizr-client/test/unit/com/structurizr/api/StructurizrClientTests.java @@ -10,7 +10,7 @@ public class StructurizrClientTests { private StructurizrClient structurizrClient; @Test - void test_construction_WithTwoParameters() { + void construction_WithTwoParameters() { structurizrClient = new StructurizrClient("key", "secret"); assertEquals("https://api.structurizr.com", structurizrClient.getUrl()); assertEquals("key", structurizrClient.getApiKey()); @@ -18,7 +18,7 @@ void test_construction_WithTwoParameters() { } @Test - void test_construction_WithThreeParameters() { + void construction_WithThreeParameters() { structurizrClient = new StructurizrClient("https://localhost", "key", "secret"); assertEquals("https://localhost", structurizrClient.getUrl()); assertEquals("key", structurizrClient.getApiKey()); @@ -26,7 +26,7 @@ void test_construction_WithThreeParameters() { } @Test - void test_construction_WithThreeParameters_TruncatesTheApiUrl_WhenTheApiUrlHasATrailingSlashCharacter() { + void construction_WithThreeParameters_TruncatesTheApiUrl_WhenTheApiUrlHasATrailingSlashCharacter() { structurizrClient = new StructurizrClient("https://localhost/", "key", "secret"); assertEquals("https://localhost", structurizrClient.getUrl()); assertEquals("key", structurizrClient.getApiKey()); @@ -34,7 +34,7 @@ void test_construction_WithThreeParameters_TruncatesTheApiUrl_WhenTheApiUrlHasAT } @Test - void test_construction_ThrowsAnException_WhenANullApiKeyIsUsed() { + void construction_ThrowsAnException_WhenANullApiKeyIsUsed() { try { structurizrClient = new StructurizrClient(null, "secret"); fail(); @@ -44,7 +44,7 @@ void test_construction_ThrowsAnException_WhenANullApiKeyIsUsed() { } @Test - void test_construction_ThrowsAnException_WhenAnEmptyApiKeyIsUsed() { + void construction_ThrowsAnException_WhenAnEmptyApiKeyIsUsed() { try { structurizrClient = new StructurizrClient(" ", "secret"); fail(); @@ -54,7 +54,7 @@ void test_construction_ThrowsAnException_WhenAnEmptyApiKeyIsUsed() { } @Test - void test_construction_ThrowsAnException_WhenANullApiSecretIsUsed() { + void construction_ThrowsAnException_WhenANullApiSecretIsUsed() { try { structurizrClient = new StructurizrClient("key", null); fail(); @@ -64,7 +64,7 @@ void test_construction_ThrowsAnException_WhenANullApiSecretIsUsed() { } @Test - void test_construction_ThrowsAnException_WhenAnEmptyApiSecretIsUsed() { + void construction_ThrowsAnException_WhenAnEmptyApiSecretIsUsed() { try { structurizrClient = new StructurizrClient("key", " "); fail(); @@ -74,7 +74,7 @@ void test_construction_ThrowsAnException_WhenAnEmptyApiSecretIsUsed() { } @Test - void test_construction_ThrowsAnException_WhenANullApiUrlIsUsed() { + void construction_ThrowsAnException_WhenANullApiUrlIsUsed() { try { structurizrClient = new StructurizrClient(null, "key", "secret"); fail(); @@ -84,7 +84,7 @@ void test_construction_ThrowsAnException_WhenANullApiUrlIsUsed() { } @Test - void test_construction_ThrowsAnException_WhenAnEmptyApiUrlIsUsed() { + void construction_ThrowsAnException_WhenAnEmptyApiUrlIsUsed() { try { structurizrClient = new StructurizrClient(" ", "key", "secret"); fail(); @@ -94,7 +94,7 @@ void test_construction_ThrowsAnException_WhenAnEmptyApiUrlIsUsed() { } @Test - void test_getWorkspace_ThrowsAnException_WhenTheWorkspaceIdIsNotValid() throws Exception { + void getWorkspace_ThrowsAnException_WhenTheWorkspaceIdIsNotValid() throws Exception { try { structurizrClient = new StructurizrClient("key", "secret"); structurizrClient.getWorkspace(0); @@ -105,7 +105,7 @@ void test_getWorkspace_ThrowsAnException_WhenTheWorkspaceIdIsNotValid() throws E } @Test - void test_putWorkspace_ThrowsAnException_WhenTheWorkspaceIdIsNotValid() throws Exception { + void putWorkspace_ThrowsAnException_WhenTheWorkspaceIdIsNotValid() throws Exception { try { structurizrClient = new StructurizrClient("key", "secret"); structurizrClient.putWorkspace(0, new Workspace("Name", "Description")); @@ -116,7 +116,7 @@ void test_putWorkspace_ThrowsAnException_WhenTheWorkspaceIdIsNotValid() throws E } @Test - void test_putWorkspace_ThrowsAnException_WhenANullWorkspaceIsSpecified() throws Exception { + void putWorkspace_ThrowsAnException_WhenANullWorkspaceIsSpecified() throws Exception { try { structurizrClient = new StructurizrClient("key", "secret"); structurizrClient.putWorkspace(1234, null); @@ -127,7 +127,7 @@ void test_putWorkspace_ThrowsAnException_WhenANullWorkspaceIsSpecified() throws } @Test - void test_constructionWithAPropertiesFile_ThrowsAnException_WhenNoPropertiesAreFound() { + void constructionWithAPropertiesFile_ThrowsAnException_WhenNoPropertiesAreFound() { try { structurizrClient = new StructurizrClient(); fail(); @@ -137,20 +137,20 @@ void test_constructionWithAPropertiesFile_ThrowsAnException_WhenNoPropertiesAreF } @Test - void test_getAgent() { + void getAgent() { structurizrClient = new StructurizrClient("key", "secret"); assertTrue(structurizrClient.getAgent().startsWith("structurizr-java/")); } @Test - void test_setAgent() { + void setAgent() { structurizrClient = new StructurizrClient("key", "secret"); structurizrClient.setAgent("new_agent"); assertEquals("new_agent", structurizrClient.getAgent()); } @Test - void test_setAgent_ThrowsAnException_WhenPassedNull() { + void setAgent_ThrowsAnException_WhenPassedNull() { structurizrClient = new StructurizrClient("key", "secret"); try { @@ -162,7 +162,7 @@ void test_setAgent_ThrowsAnException_WhenPassedNull() { } @Test - void test_setAgent_ThrowsAnException_WhenPassedAnEmptyString() { + void setAgent_ThrowsAnException_WhenPassedAnEmptyString() { structurizrClient = new StructurizrClient("key", "secret"); try { diff --git a/structurizr-client/test/unit/com/structurizr/encryption/AesEncryptionStrategyTests.java b/structurizr-client/test/unit/com/structurizr/encryption/AesEncryptionStrategyTests.java index 8b390f8f6..3af514905 100644 --- a/structurizr-client/test/unit/com/structurizr/encryption/AesEncryptionStrategyTests.java +++ b/structurizr-client/test/unit/com/structurizr/encryption/AesEncryptionStrategyTests.java @@ -10,7 +10,7 @@ public class AesEncryptionStrategyTests { @Test - void test_encrypt_EncryptsPlaintext() throws Exception { + void encrypt_EncryptsPlaintext() throws Exception { AesEncryptionStrategy strategy = new AesEncryptionStrategy(128, 1000, "06DC30A48ADEEE72D98E33C2CEAEAD3E", "ED124530AF64A5CAD8EF463CF5628434", "password"); String ciphertext = strategy.encrypt("Hello world"); @@ -18,7 +18,7 @@ void test_encrypt_EncryptsPlaintext() throws Exception { } @Test - void test_decrypt_decryptsTheCiphertext_WhenTheSameStrategyInstanceIsUsed() throws Exception { + void decrypt_decryptsTheCiphertext_WhenTheSameStrategyInstanceIsUsed() throws Exception { AesEncryptionStrategy strategy = new AesEncryptionStrategy(128, 1000, "password"); String ciphertext = strategy.encrypt("Hello world"); @@ -26,7 +26,7 @@ void test_decrypt_decryptsTheCiphertext_WhenTheSameStrategyInstanceIsUsed() thro } @Test - void test_decrypt_decryptsTheCiphertext_WhenTheSameConfigurationIsUsed() throws Exception { + void decrypt_decryptsTheCiphertext_WhenTheSameConfigurationIsUsed() throws Exception { AesEncryptionStrategy strategy = new AesEncryptionStrategy(128, 1000, "password"); String ciphertext = strategy.encrypt("Hello world"); @@ -36,7 +36,7 @@ void test_decrypt_decryptsTheCiphertext_WhenTheSameConfigurationIsUsed() throws } @Test - void test_decrypt_doesNotDecryptTheCiphertext_WhenTheIncorrectKeySizeIsUsed() throws Exception { + void decrypt_doesNotDecryptTheCiphertext_WhenTheIncorrectKeySizeIsUsed() throws Exception { try { AesEncryptionStrategy strategy = new AesEncryptionStrategy(128, 1000, "password"); @@ -53,7 +53,7 @@ void test_decrypt_doesNotDecryptTheCiphertext_WhenTheIncorrectKeySizeIsUsed() th } @Test - void test_decrypt_doesNotDecryptTheCiphertext_WhenTheIncorrectIterationCountIsUsed() throws Exception { + void decrypt_doesNotDecryptTheCiphertext_WhenTheIncorrectIterationCountIsUsed() throws Exception { assertThrows(BadPaddingException.class, () -> { AesEncryptionStrategy strategy = new AesEncryptionStrategy(128, 1000, "password"); @@ -65,7 +65,7 @@ void test_decrypt_doesNotDecryptTheCiphertext_WhenTheIncorrectIterationCountIsUs } @Test - void test_decrypt_doesNotDecryptTheCiphertext_WhenTheIncorrectSaltIsUsed() throws Exception { + void decrypt_doesNotDecryptTheCiphertext_WhenTheIncorrectSaltIsUsed() throws Exception { assertThrows(BadPaddingException.class, () -> { AesEncryptionStrategy strategy = new AesEncryptionStrategy(128, 1000, "password"); @@ -77,7 +77,7 @@ void test_decrypt_doesNotDecryptTheCiphertext_WhenTheIncorrectSaltIsUsed() throw } @Test - void test_decrypt_doesNotDecryptTheCiphertext_WhenTheIncorrectIvIsUsed() throws Exception { + void decrypt_doesNotDecryptTheCiphertext_WhenTheIncorrectIvIsUsed() throws Exception { assertThrows(BadPaddingException.class, () -> { AesEncryptionStrategy strategy = new AesEncryptionStrategy(128, 1000, "password"); @@ -89,7 +89,7 @@ void test_decrypt_doesNotDecryptTheCiphertext_WhenTheIncorrectIvIsUsed() throws } @Test - void test_decrypt_doesNotDecryptTheCiphertext_WhenTheIncorrectPassphraseIsUsed() throws Exception { + void decrypt_doesNotDecryptTheCiphertext_WhenTheIncorrectPassphraseIsUsed() throws Exception { assertThrows(BadPaddingException.class, () -> { AesEncryptionStrategy strategy = new AesEncryptionStrategy(128, 1000, "password"); diff --git a/structurizr-client/test/unit/com/structurizr/encryption/EncryptedWorkspaceTests.java b/structurizr-client/test/unit/com/structurizr/encryption/EncryptedWorkspaceTests.java index 81a87b6a0..a7e508781 100644 --- a/structurizr-client/test/unit/com/structurizr/encryption/EncryptedWorkspaceTests.java +++ b/structurizr-client/test/unit/com/structurizr/encryption/EncryptedWorkspaceTests.java @@ -29,7 +29,7 @@ public void setUp() throws Exception { } @Test - void test_construction_WhenTwoParametersAreSpecified() throws Exception { + void construction_WhenTwoParametersAreSpecified() throws Exception { encryptedWorkspace = new EncryptedWorkspace(workspace, encryptionStrategy); assertEquals("Name", encryptedWorkspace.getName()); @@ -53,7 +53,7 @@ void test_construction_WhenTwoParametersAreSpecified() throws Exception { } @Test - void test_construction_WhenThreeParametersAreSpecified() throws Exception { + void construction_WhenThreeParametersAreSpecified() throws Exception { JsonWriter jsonWriter = new JsonWriter(false); StringWriter stringWriter = new StringWriter(); jsonWriter.write(workspace, stringWriter); @@ -75,7 +75,7 @@ void test_construction_WhenThreeParametersAreSpecified() throws Exception { } @Test - void test_getPlaintext_ReturnsTheDecryptedVersionOfTheCiphertext() throws Exception { + void getPlaintext_ReturnsTheDecryptedVersionOfTheCiphertext() throws Exception { encryptedWorkspace = new EncryptedWorkspace(workspace, encryptionStrategy); String cipherText = encryptedWorkspace.getCiphertext(); @@ -87,7 +87,7 @@ void test_getPlaintext_ReturnsTheDecryptedVersionOfTheCiphertext() throws Except } @Test - void test_getWorkspace_ReturnsTheWorkspace_WhenACipherextIsSpecified() throws Exception { + void getWorkspace_ReturnsTheWorkspace_WhenACipherextIsSpecified() throws Exception { JsonWriter jsonWriter = new JsonWriter(false); StringWriter stringWriter = new StringWriter(); jsonWriter.write(workspace, stringWriter); diff --git a/structurizr-client/test/unit/com/structurizr/io/json/EncryptedJsonTests.java b/structurizr-client/test/unit/com/structurizr/io/json/EncryptedJsonTests.java index 14ed4f11e..c598cd06c 100644 --- a/structurizr-client/test/unit/com/structurizr/io/json/EncryptedJsonTests.java +++ b/structurizr-client/test/unit/com/structurizr/io/json/EncryptedJsonTests.java @@ -13,7 +13,7 @@ public class EncryptedJsonTests { @Test - void test_write_and_read() throws Exception { + void write_and_read() throws Exception { final Workspace workspace1 = new Workspace("Name", "Description"); // output the model as JSON diff --git a/structurizr-client/test/unit/com/structurizr/io/json/EncryptedJsonWriterTests.java b/structurizr-client/test/unit/com/structurizr/io/json/EncryptedJsonWriterTests.java index eccb79d45..74b40614c 100644 --- a/structurizr-client/test/unit/com/structurizr/io/json/EncryptedJsonWriterTests.java +++ b/structurizr-client/test/unit/com/structurizr/io/json/EncryptedJsonWriterTests.java @@ -13,7 +13,7 @@ public class EncryptedJsonWriterTests { @Test - void test_write_ThrowsAnIllegalArgumentException_WhenANullEncryptedWorkspaceIsSpecified() throws Exception { + void write_ThrowsAnIllegalArgumentException_WhenANullEncryptedWorkspaceIsSpecified() throws Exception { try { EncryptedJsonWriter writer = new EncryptedJsonWriter(true); writer.write(null, new StringWriter()); @@ -24,7 +24,7 @@ void test_write_ThrowsAnIllegalArgumentException_WhenANullEncryptedWorkspaceIsSp } @Test - void test_write_ThrowsAnIllegalArgumentException_WhenANullWriterIsSpecified() throws Exception { + void write_ThrowsAnIllegalArgumentException_WhenANullWriterIsSpecified() throws Exception { try { EncryptedJsonWriter writer = new EncryptedJsonWriter(true); Workspace workspace = new Workspace("Name", "Description"); diff --git a/structurizr-client/test/unit/com/structurizr/io/json/JsonTests.java b/structurizr-client/test/unit/com/structurizr/io/json/JsonTests.java index 2c436c2a1..f394c1ace 100644 --- a/structurizr-client/test/unit/com/structurizr/io/json/JsonTests.java +++ b/structurizr-client/test/unit/com/structurizr/io/json/JsonTests.java @@ -14,7 +14,7 @@ public class JsonTests { @Test - void test_write_and_read() throws Exception { + void write_and_read() throws Exception { final Workspace workspace1 = new Workspace("Name", "Description"); // output the model as JSON @@ -31,7 +31,7 @@ void test_write_and_read() throws Exception { } @Test - void test_backwardsCompatibilityOfRenamingEnterpriseContextViewsToSystemLandscapeViews() throws Exception { + void backwardsCompatibilityOfRenamingEnterpriseContextViewsToSystemLandscapeViews() throws Exception { Workspace workspace = new Workspace("Name", "Description"); workspace.getViews().createSystemLandscapeView("key", "description"); @@ -48,7 +48,7 @@ void test_backwardsCompatibilityOfRenamingEnterpriseContextViewsToSystemLandscap } @Test - void test_write_and_read_withCustomIdGenerator() throws Exception { + void write_and_read_withCustomIdGenerator() throws Exception { Workspace workspace1 = new Workspace("Name", "Description"); workspace1.getModel().setIdGenerator(new CustomIdGenerator()); Person user = workspace1.getModel().addPerson("User"); diff --git a/structurizr-client/test/unit/com/structurizr/io/json/JsonWriterTests.java b/structurizr-client/test/unit/com/structurizr/io/json/JsonWriterTests.java index bb7a8301a..4db62acb8 100644 --- a/structurizr-client/test/unit/com/structurizr/io/json/JsonWriterTests.java +++ b/structurizr-client/test/unit/com/structurizr/io/json/JsonWriterTests.java @@ -11,7 +11,7 @@ public class JsonWriterTests { @Test - void test_write_ThrowsAnIllegalArgumentException_WhenANullWorkspaceIsSpecified() throws Exception { + void write_ThrowsAnIllegalArgumentException_WhenANullWorkspaceIsSpecified() throws Exception { try { JsonWriter writer = new JsonWriter(true); writer.write(null, new StringWriter()); @@ -22,7 +22,7 @@ void test_write_ThrowsAnIllegalArgumentException_WhenANullWorkspaceIsSpecified() } @Test - void test_write_ThrowsAnIllegalArgumentException_WhenANullWriterIsSpecified() throws Exception { + void write_ThrowsAnIllegalArgumentException_WhenANullWriterIsSpecified() throws Exception { try { JsonWriter writer = new JsonWriter(true); Workspace workspace = new Workspace("Name", "Description"); diff --git a/structurizr-client/test/unit/com/structurizr/util/WorkspaceUtilsTests.java b/structurizr-client/test/unit/com/structurizr/util/WorkspaceUtilsTests.java index 34650ecd2..c6e3c0188 100644 --- a/structurizr-client/test/unit/com/structurizr/util/WorkspaceUtilsTests.java +++ b/structurizr-client/test/unit/com/structurizr/util/WorkspaceUtilsTests.java @@ -11,7 +11,7 @@ public class WorkspaceUtilsTests { @Test - void test_loadWorkspaceFromJson_ThrowsAnException_WhenANullFileIsSpecified() { + void loadWorkspaceFromJson_ThrowsAnException_WhenANullFileIsSpecified() { try { WorkspaceUtils.loadWorkspaceFromJson(null); fail(); @@ -21,7 +21,7 @@ void test_loadWorkspaceFromJson_ThrowsAnException_WhenANullFileIsSpecified() { } @Test - void test_loadWorkspaceFromJson_ThrowsAnException_WhenTheFileDoesNotExist() { + void loadWorkspaceFromJson_ThrowsAnException_WhenTheFileDoesNotExist() { try { WorkspaceUtils.loadWorkspaceFromJson(new File("test/unit/com/structurizr/util/other-workspace.json")); fail(); @@ -31,7 +31,7 @@ void test_loadWorkspaceFromJson_ThrowsAnException_WhenTheFileDoesNotExist() { } @Test - void test_saveWorkspaceToJson_ThrowsAnException_WhenANullWorkspaceIsSpecified() { + void saveWorkspaceToJson_ThrowsAnException_WhenANullWorkspaceIsSpecified() { try { WorkspaceUtils.saveWorkspaceToJson(null, null); fail(); @@ -41,7 +41,7 @@ void test_saveWorkspaceToJson_ThrowsAnException_WhenANullWorkspaceIsSpecified() } @Test - void test_saveWorkspaceToJson_ThrowsAnException_WhenANullFileIsSpecified() { + void saveWorkspaceToJson_ThrowsAnException_WhenANullFileIsSpecified() { try { WorkspaceUtils.saveWorkspaceToJson(new Workspace("Name", "Description"), null); fail(); @@ -51,7 +51,7 @@ void test_saveWorkspaceToJson_ThrowsAnException_WhenANullFileIsSpecified() { } @Test - void test_saveWorkspaceToJson_and_loadWorkspaceFromJson() throws Exception { + void saveWorkspaceToJson_and_loadWorkspaceFromJson() throws Exception { File file = new File("build/workspace-utils.json"); Workspace workspace = new Workspace("Name", "Description"); WorkspaceUtils.saveWorkspaceToJson(workspace, file); @@ -61,7 +61,7 @@ void test_saveWorkspaceToJson_and_loadWorkspaceFromJson() throws Exception { } @Test - void test_toJson_ThrowsAnException_WhenANullWorkspaceIsProvided() throws Exception { + void toJson_ThrowsAnException_WhenANullWorkspaceIsProvided() throws Exception { try { WorkspaceUtils.toJson(null, true); fail(); @@ -71,7 +71,7 @@ void test_toJson_ThrowsAnException_WhenANullWorkspaceIsProvided() throws Excepti } @Test - void test_toJson() throws Exception { + void toJson() throws Exception { Workspace workspace = new Workspace("Name", "Description"); String indentedOutput = WorkspaceUtils.toJson(workspace, true); String unindentedOutput = WorkspaceUtils.toJson(workspace, false); @@ -95,7 +95,7 @@ void test_toJson() throws Exception { } @Test - void test_fromJson_ThrowsAnException_WhenANullJsonStringIsProvided() throws Exception { + void fromJson_ThrowsAnException_WhenANullJsonStringIsProvided() throws Exception { try { WorkspaceUtils.fromJson(null); fail(); @@ -105,7 +105,7 @@ void test_fromJson_ThrowsAnException_WhenANullJsonStringIsProvided() throws Exce } @Test - void test_fromJson_ThrowsAnException_WhenAnEmptyJsonStringIsProvided() throws Exception { + void fromJson_ThrowsAnException_WhenAnEmptyJsonStringIsProvided() throws Exception { try { WorkspaceUtils.fromJson(" "); fail(); @@ -115,7 +115,7 @@ void test_fromJson_ThrowsAnException_WhenAnEmptyJsonStringIsProvided() throws Ex } @Test - void test_fromJson() throws Exception { + void fromJson() throws Exception { Workspace workspace = WorkspaceUtils.fromJson("{\"id\":0,\"name\":\"Name\",\"description\":\"Description\",\"model\":{},\"documentation\":{},\"views\":{\"configuration\":{\"branding\":{},\"styles\":{},\"terminology\":{}}}}"); assertEquals("Name", workspace.getName()); assertEquals("Description", workspace.getDescription()); diff --git a/structurizr-client/test/unit/com/structurizr/view/ThemeUtilsTests.java b/structurizr-client/test/unit/com/structurizr/view/ThemeUtilsTests.java index 0714d6a7f..afe40f9d0 100644 --- a/structurizr-client/test/unit/com/structurizr/view/ThemeUtilsTests.java +++ b/structurizr-client/test/unit/com/structurizr/view/ThemeUtilsTests.java @@ -14,7 +14,7 @@ public class ThemeUtilsTests { @Test - void test_loadThemes_DoesNothingWhenNoThemesAreDefined() throws Exception { + void loadThemes_DoesNothingWhenNoThemesAreDefined() throws Exception { Workspace workspace = new Workspace("Name", "Description"); ThemeUtils.loadThemes(workspace); @@ -23,7 +23,7 @@ void test_loadThemes_DoesNothingWhenNoThemesAreDefined() throws Exception { } @Test - void test_loadThemes_LoadsThemesWhenThemesAreDefined() throws Exception { + void loadThemes_LoadsThemesWhenThemesAreDefined() throws Exception { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name"); softwareSystem.addTags("Amazon Web Services - Alexa For Business"); @@ -43,7 +43,7 @@ void test_loadThemes_LoadsThemesWhenThemesAreDefined() throws Exception { } @Test - void test_toJson() throws Exception { + void toJson() throws Exception { Workspace workspace = new Workspace("Name", "Description"); assertEquals("{\n" + " \"name\" : \"Name\",\n" + @@ -67,7 +67,7 @@ void test_toJson() throws Exception { } @Test - void test_findElementStyle_WithThemes() { + void findElementStyle_WithThemes() { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name"); workspace.getViews().getConfiguration().getStyles().addElementStyle("Element").shape(Shape.RoundedBox); @@ -100,7 +100,7 @@ void test_findElementStyle_WithThemes() { } @Test - void test_findRelationshipStyle_WithThemes() { + void findRelationshipStyle_WithThemes() { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name"); Relationship relationship = softwareSystem.uses(softwareSystem, "Uses"); diff --git a/structurizr-core/test/unit/com/structurizr/WorkspaceTests.java b/structurizr-core/test/unit/com/structurizr/WorkspaceTests.java index 67e84dfd0..651057bbe 100644 --- a/structurizr-core/test/unit/com/structurizr/WorkspaceTests.java +++ b/structurizr-core/test/unit/com/structurizr/WorkspaceTests.java @@ -14,27 +14,27 @@ public class WorkspaceTests { private Workspace workspace = new Workspace("Name", "Description"); @Test - void test_isEmpty_ReturnsTrue_WhenThereAreNoElementsViewsOrDocumentation() { + void isEmpty_ReturnsTrue_WhenThereAreNoElementsViewsOrDocumentation() { workspace = new Workspace("Name", "Description"); assertTrue(workspace.isEmpty()); } @Test - void test_isEmpty_ReturnsFalse_WhenThereAreElements() { + void isEmpty_ReturnsFalse_WhenThereAreElements() { workspace = new Workspace("Name", "Description"); workspace.getModel().addPerson("Name", "Description"); assertFalse(workspace.isEmpty()); } @Test - void test_isEmpty_ReturnsFalse_WhenThereAreViews() { + void isEmpty_ReturnsFalse_WhenThereAreViews() { workspace = new Workspace("Name", "Description"); workspace.getViews().createSystemLandscapeView("key", "Description"); assertFalse(workspace.isEmpty()); } @Test - void test_isEmpty_ReturnsFalse_WhenThereIsDocumentation() throws Exception { + void isEmpty_ReturnsFalse_WhenThereIsDocumentation() throws Exception { workspace = new Workspace("Name", "Description"); Decision d = new Decision("1"); d.setTitle("Title"); @@ -46,7 +46,7 @@ void test_isEmpty_ReturnsFalse_WhenThereIsDocumentation() throws Exception { } @Test - void test_countAndLogWarnings() { + void countAndLogWarnings() { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem1 = workspace.getModel().addSoftwareSystem("Software System 1", null); SoftwareSystem softwareSystem2 = workspace.getModel().addSoftwareSystem("Software System 2", " "); @@ -64,7 +64,7 @@ void test_countAndLogWarnings() { } @Test - void test_hydrate_DoesNotCrash() { + void hydrate_DoesNotCrash() { Workspace workspace = new Workspace("Name", "Description"); assertNotNull(workspace.getViews()); assertNotNull(workspace.getDocumentation()); diff --git a/structurizr-core/test/unit/com/structurizr/configuration/WorkspaceConfigurationTests.java b/structurizr-core/test/unit/com/structurizr/configuration/WorkspaceConfigurationTests.java index 23c39802e..16d067eb3 100644 --- a/structurizr-core/test/unit/com/structurizr/configuration/WorkspaceConfigurationTests.java +++ b/structurizr-core/test/unit/com/structurizr/configuration/WorkspaceConfigurationTests.java @@ -8,7 +8,7 @@ public class WorkspaceConfigurationTests { @Test - void test_addUser_ThrowsAnException_WhenANullUsernameIsSpecified() { + void addUser_ThrowsAnException_WhenANullUsernameIsSpecified() { try { WorkspaceConfiguration configuration = new WorkspaceConfiguration(); configuration.addUser(null, Role.ReadWrite); @@ -19,7 +19,7 @@ void test_addUser_ThrowsAnException_WhenANullUsernameIsSpecified() { } @Test - void test_addUser_ThrowsAnException_WhenAnEmptyUsernameIsSpecified() { + void addUser_ThrowsAnException_WhenAnEmptyUsernameIsSpecified() { try { WorkspaceConfiguration configuration = new WorkspaceConfiguration(); configuration.addUser(" ", Role.ReadWrite); @@ -30,7 +30,7 @@ void test_addUser_ThrowsAnException_WhenAnEmptyUsernameIsSpecified() { } @Test - void test_addUser_ThrowsAnException_WhenANullRoleIsSpecified() { + void addUser_ThrowsAnException_WhenANullRoleIsSpecified() { try { WorkspaceConfiguration configuration = new WorkspaceConfiguration(); configuration.addUser("user@domain.com", null); @@ -41,7 +41,7 @@ void test_addUser_ThrowsAnException_WhenANullRoleIsSpecified() { } @Test - void test_addUser_AddsAUser() { + void addUser_AddsAUser() { WorkspaceConfiguration configuration = new WorkspaceConfiguration(); configuration.addUser("user@domain.com", Role.ReadOnly); diff --git a/structurizr-core/test/unit/com/structurizr/documentation/DecisionTests.java b/structurizr-core/test/unit/com/structurizr/documentation/DecisionTests.java index b107f7568..82efda419 100644 --- a/structurizr-core/test/unit/com/structurizr/documentation/DecisionTests.java +++ b/structurizr-core/test/unit/com/structurizr/documentation/DecisionTests.java @@ -9,7 +9,7 @@ public class DecisionTests extends AbstractWorkspaceTestBase { @Test - void test_hasLinkTo() { + void hasLinkTo() { Decision d1 = new Decision("1"); Decision d2 = new Decision("2"); Decision d3 = new Decision("3"); diff --git a/structurizr-core/test/unit/com/structurizr/documentation/DocumentationTests.java b/structurizr-core/test/unit/com/structurizr/documentation/DocumentationTests.java index e290f5bdd..ee4ea8957 100644 --- a/structurizr-core/test/unit/com/structurizr/documentation/DocumentationTests.java +++ b/structurizr-core/test/unit/com/structurizr/documentation/DocumentationTests.java @@ -16,7 +16,7 @@ public void setUp() { } @Test - void test_addSection_ThrowsAnException_WhenTheTitleIsNotSpecified() { + void addSection_ThrowsAnException_WhenTheTitleIsNotSpecified() { try { Section section = new Section(); @@ -28,7 +28,7 @@ void test_addSection_ThrowsAnException_WhenTheTitleIsNotSpecified() { } @Test - void test_addSection_ThrowsAnException_WhenTheContentIsNotSpecified() { + void addSection_ThrowsAnException_WhenTheContentIsNotSpecified() { try { Section section = new Section(); section.setTitle("Title"); @@ -41,7 +41,7 @@ void test_addSection_ThrowsAnException_WhenTheContentIsNotSpecified() { } @Test - void test_addSection_ThrowsAnException_WhenTheFormatIsNotSpecified() { + void addSection_ThrowsAnException_WhenTheFormatIsNotSpecified() { try { Section section = new Section(); section.setTitle("Title"); @@ -55,7 +55,7 @@ void test_addSection_ThrowsAnException_WhenTheFormatIsNotSpecified() { } @Test - void test_addSection_ThrowsAnException_WhenASectionExistsWithTheSameTitle() { + void addSection_ThrowsAnException_WhenASectionExistsWithTheSameTitle() { try { Section section = new Section(); section.setTitle("Title"); @@ -71,7 +71,7 @@ void test_addSection_ThrowsAnException_WhenASectionExistsWithTheSameTitle() { } @Test - void test_addSection() { + void addSection() { Section section = new Section(); section.setTitle("Title"); section.setContent("Content"); @@ -88,7 +88,7 @@ void test_addSection() { } @Test - void test_addSection_IncrementsTheSectionOrderNumber() { + void addSection_IncrementsTheSectionOrderNumber() { Section section1 = new Section("Title 1", Format.Markdown, "Content"); Section section2 = new Section("Title 2", Format.Markdown, "Content"); Section section3 = new Section("Title 3", Format.Markdown, "Content"); @@ -103,7 +103,7 @@ void test_addSection_IncrementsTheSectionOrderNumber() { } @Test - void test_addDecision_ThrowsAnException_WhenTheTitleIsNotSpecified() { + void addDecision_ThrowsAnException_WhenTheTitleIsNotSpecified() { try { Decision decision = new Decision("1"); @@ -115,7 +115,7 @@ void test_addDecision_ThrowsAnException_WhenTheTitleIsNotSpecified() { } @Test - void test_addDecision_ThrowsAnException_WhenTheContentIsNotSpecified() { + void addDecision_ThrowsAnException_WhenTheContentIsNotSpecified() { try { Decision decision = new Decision("1"); decision.setTitle("Title"); @@ -128,7 +128,7 @@ void test_addDecision_ThrowsAnException_WhenTheContentIsNotSpecified() { } @Test - void test_addDecision_ThrowsAnException_WhenTheStatusIsNotSpecified() { + void addDecision_ThrowsAnException_WhenTheStatusIsNotSpecified() { try { Decision decision = new Decision("1"); decision.setTitle("Title"); @@ -142,7 +142,7 @@ void test_addDecision_ThrowsAnException_WhenTheStatusIsNotSpecified() { } @Test - void test_addDecision_ThrowsAnException_WhenTheFormatIsNotSpecified() { + void addDecision_ThrowsAnException_WhenTheFormatIsNotSpecified() { try { Decision decision = new Decision("1"); decision.setTitle("Title"); @@ -157,7 +157,7 @@ void test_addDecision_ThrowsAnException_WhenTheFormatIsNotSpecified() { } @Test - void test_addDecision_ThrowsAnException_WhenADecisionExistsWithTheSameId() { + void addDecision_ThrowsAnException_WhenADecisionExistsWithTheSameId() { try { Decision decision = new Decision("1"); decision.setTitle("Title"); diff --git a/structurizr-core/test/unit/com/structurizr/documentation/SectionTests.java b/structurizr-core/test/unit/com/structurizr/documentation/SectionTests.java index 92016904a..282e92c5e 100644 --- a/structurizr-core/test/unit/com/structurizr/documentation/SectionTests.java +++ b/structurizr-core/test/unit/com/structurizr/documentation/SectionTests.java @@ -7,7 +7,7 @@ public class SectionTests { @Test - void test_construction() { + void construction() { Section section = new Section("Title", Format.Markdown, "Content"); assertEquals("Title", section.getTitle()); diff --git a/structurizr-core/test/unit/com/structurizr/model/CodeElementTests.java b/structurizr-core/test/unit/com/structurizr/model/CodeElementTests.java index d011785a0..79cb47c86 100644 --- a/structurizr-core/test/unit/com/structurizr/model/CodeElementTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/CodeElementTests.java @@ -7,21 +7,21 @@ public class CodeElementTests { @Test - void test_construction_WhenAFullyQualifiedNameIsSpecified() { + void construction_WhenAFullyQualifiedNameIsSpecified() { CodeElement codeElement = new CodeElement("com.structurizr.component.SomeComponent"); assertEquals("SomeComponent", codeElement.getName()); assertEquals("com.structurizr.component.SomeComponent", codeElement.getType()); } @Test - void test_construction_WhenAFullyQualifiedNameIsSpecifiedInTheDefaultPackage() { + void construction_WhenAFullyQualifiedNameIsSpecifiedInTheDefaultPackage() { CodeElement codeElement = new CodeElement("SomeComponent"); assertEquals("SomeComponent", codeElement.getName()); assertEquals("SomeComponent", codeElement.getType()); } @Test - void test_descriptionProperty() { + void descriptionProperty() { CodeElement codeElement = new CodeElement("com.structurizr.component.SomeComponent"); assertNull(codeElement.getDescription()); @@ -30,7 +30,7 @@ void test_descriptionProperty() { } @Test - void test_sizeProperty() { + void sizeProperty() { CodeElement codeElement = new CodeElement("com.structurizr.component.SomeComponent"); assertEquals(0, codeElement.getSize()); @@ -39,7 +39,7 @@ void test_sizeProperty() { } @Test - void test_languageProperty() { + void languageProperty() { CodeElement codeElement = new CodeElement("com.structurizr.component.SomeComponent"); assertEquals("Java", codeElement.getLanguage()); @@ -48,7 +48,7 @@ void test_languageProperty() { } @Test - void test_categoryProperty() { + void categoryProperty() { CodeElement codeElement = new CodeElement("com.structurizr.component.SomeComponent"); assertNull(codeElement.getCategory()); @@ -57,7 +57,7 @@ void test_categoryProperty() { } @Test - void test_visibilityProperty() { + void visibilityProperty() { CodeElement codeElement = new CodeElement("com.structurizr.component.SomeComponent"); assertNull(codeElement.getVisibility()); @@ -66,14 +66,14 @@ void test_visibilityProperty() { } @Test - void test_setUrl() { + void setUrl() { CodeElement codeElement = new CodeElement("com.structurizr.component.SomeComponent"); codeElement.setUrl("https://structurizr.com"); assertEquals("https://structurizr.com", codeElement.getUrl()); } @Test - void test_setUrl_ThrowsAnIllegalArgumentException_WhenAnInvalidUrlIsSpecified() { + void setUrl_ThrowsAnIllegalArgumentException_WhenAnInvalidUrlIsSpecified() { assertThrows(IllegalArgumentException.class, () -> { CodeElement codeElement = new CodeElement("com.structurizr.component.SomeComponent"); codeElement.setUrl("htt://blah"); @@ -81,61 +81,61 @@ void test_setUrl_ThrowsAnIllegalArgumentException_WhenAnInvalidUrlIsSpecified() } @Test - void test_setUrl_DoesNothing_WhenANullUrlIsSpecified() { + void setUrl_DoesNothing_WhenANullUrlIsSpecified() { CodeElement codeElement = new CodeElement("com.structurizr.component.SomeComponent"); codeElement.setUrl(null); assertNull(codeElement.getUrl()); } @Test - void test_setUrl_DoesNothing_WhenAnEmptyUrlIsSpecified() { + void setUrl_DoesNothing_WhenAnEmptyUrlIsSpecified() { CodeElement codeElement = new CodeElement("com.structurizr.component.SomeComponent"); codeElement.setUrl(" "); assertNull(codeElement.getUrl()); } @Test - void test_construction_ThrowsAnIllegalArgumentException_WhenANullFullyQualifiedNameIsSpecified() { + void construction_ThrowsAnIllegalArgumentException_WhenANullFullyQualifiedNameIsSpecified() { assertThrows(IllegalArgumentException.class, () -> { new CodeElement(null); }); } @Test - void test_construction_ThrowsAnIllegalArgumentException_WhenAnEmptyFullyQualifiedNameIsSpecified() { + void construction_ThrowsAnIllegalArgumentException_WhenAnEmptyFullyQualifiedNameIsSpecified() { assertThrows(IllegalArgumentException.class, () -> { new CodeElement(" "); }); } @Test - void test_equals_ReturnsFalse_WhenComparedToNull() { + void equals_ReturnsFalse_WhenComparedToNull() { CodeElement codeElement = new CodeElement("com.structurizr.component.SomeComponent"); assertNotEquals(codeElement, null); } @Test - void test_equals_ReturnsFalse_WhenComparedToDifferentTypeOfObject() { + void equals_ReturnsFalse_WhenComparedToDifferentTypeOfObject() { CodeElement codeElement = new CodeElement("com.structurizr.component.SomeComponent"); assertNotEquals(codeElement, "hello"); } @Test - void test_equals_ReturnsFalse_WhenComparedToAnotherCodeElementWithADifferentType() { + void equals_ReturnsFalse_WhenComparedToAnotherCodeElementWithADifferentType() { CodeElement codeElement1 = new CodeElement("com.structurizr.component.SomeComponent1"); CodeElement codeElement2 = new CodeElement("com.structurizr.component.SomeComponent2"); assertNotEquals(codeElement1, codeElement2); } @Test - void test_equals_ReturnsFalse_WhenComparedToAnotherCodeElementWithTheSameType() { + void equals_ReturnsFalse_WhenComparedToAnotherCodeElementWithTheSameType() { CodeElement codeElement1 = new CodeElement("com.structurizr.component.SomeComponent1"); CodeElement codeElement2 = new CodeElement("com.structurizr.component.SomeComponent1"); assertEquals(codeElement1, codeElement2); } @Test - void test_getPackage_ReturnsThePackageName() { + void getPackage_ReturnsThePackageName() { CodeElement codeElement = new CodeElement("com.structurizr.component.SomeComponent"); assertEquals("com.structurizr.component", codeElement.getPackage()); } diff --git a/structurizr-core/test/unit/com/structurizr/model/ComponentTests.java b/structurizr-core/test/unit/com/structurizr/model/ComponentTests.java index 0036db291..82f72d2a4 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ComponentTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ComponentTests.java @@ -13,38 +13,38 @@ public class ComponentTests extends AbstractWorkspaceTestBase { private Container container = softwareSystem.addContainer("Container", "Description", "Some technology"); @Test - void test_getName_ReturnsTheGivenName_WhenANameIsGiven() { + void getName_ReturnsTheGivenName_WhenANameIsGiven() { Component component = new Component(); component.setName("Some name"); assertEquals("Some name", component.getName()); } @Test - void test_getCanonicalName() { + void getCanonicalName() { Component component = container.addComponent("Component", "Description"); assertEquals("Component://System.Container.Component", component.getCanonicalName()); } @Test - void test_getCanonicalName_WhenNameContainsSlashAndDotCharacters() { + void getCanonicalName_WhenNameContainsSlashAndDotCharacters() { Component component = container.addComponent("Name1/.Name2", "Description"); assertEquals("Component://System.Container.Name1Name2", component.getCanonicalName()); } @Test - void test_getParent_ReturnsTheParentContainer() { + void getParent_ReturnsTheParentContainer() { Component component = container.addComponent("Component", "Description"); assertEquals(container, component.getParent()); } @Test - void test_getContainer_ReturnsTheParentContainer() { + void getContainer_ReturnsTheParentContainer() { Component component = container.addComponent("Name", "Description"); assertEquals(container, component.getContainer()); } @Test - void test_removeTags_DoesNotRemoveRequiredTags() { + void removeTags_DoesNotRemoveRequiredTags() { Component component = new Component(); assertTrue(component.getTags().contains(Tags.ELEMENT)); assertTrue(component.getTags().contains(Tags.COMPONENT)); @@ -57,7 +57,7 @@ void test_removeTags_DoesNotRemoveRequiredTags() { } @Test - void test_technologyProperty() { + void technologyProperty() { Component component = new Component(); assertNull(component.getTechnology()); @@ -66,7 +66,7 @@ void test_technologyProperty() { } @Test - void test_sizeProperty() { + void sizeProperty() { Component component = new Component(); assertEquals(0, component.getSize()); @@ -75,7 +75,7 @@ void test_sizeProperty() { } @Test - void test_setType_ThrowsAnExceptionWhenPassedNull() { + void setType_ThrowsAnExceptionWhenPassedNull() { Component component = new Component(); try { component.setType(null); @@ -86,7 +86,7 @@ void test_setType_ThrowsAnExceptionWhenPassedNull() { } @Test - void test_setType_AddsAPrimaryCodeElement_WhenPassedAFullyQualifiedTypeName() { + void setType_AddsAPrimaryCodeElement_WhenPassedAFullyQualifiedTypeName() { Component component = new Component(); component.setType("com.structurizr.web.HomePageController"); @@ -99,7 +99,7 @@ void test_setType_AddsAPrimaryCodeElement_WhenPassedAFullyQualifiedTypeName() { } @Test - void test_setType_OverwritesThePrimaryCodeElement_WhenCalledMoreThanOnce() { + void setType_OverwritesThePrimaryCodeElement_WhenCalledMoreThanOnce() { Component component = new Component(); component.setType("com.structurizr.web.HomePageController"); component.setType("com.structurizr.web.SomeOtherController"); @@ -114,7 +114,7 @@ void test_setType_OverwritesThePrimaryCodeElement_WhenCalledMoreThanOnce() { } @Test - void test_addSupportingType_ThrowsAnExceptionWhenPassedNull() { + void addSupportingType_ThrowsAnExceptionWhenPassedNull() { Component component = new Component(); try { component.addSupportingType(null); @@ -125,7 +125,7 @@ void test_addSupportingType_ThrowsAnExceptionWhenPassedNull() { } @Test - void test_addSupportingType_AddsASupportingCodeElement_WhenPassedAFullyQualifiedTypeName() { + void addSupportingType_AddsASupportingCodeElement_WhenPassedAFullyQualifiedTypeName() { Component component = new Component(); component.addSupportingType("com.structurizr.web.HomePageViewModel"); @@ -138,20 +138,20 @@ void test_addSupportingType_AddsASupportingCodeElement_WhenPassedAFullyQualified } @Test - void test_getType_ReturnsNull_WhenThereAreNoCodeElements() { + void getType_ReturnsNull_WhenThereAreNoCodeElements() { Component component = new Component(); assertNull(component.getType()); } @Test - void test_getType_ReturnsNull_WhenThereAreNoPrimaryCodeElements() { + void getType_ReturnsNull_WhenThereAreNoPrimaryCodeElements() { Component component = new Component(); component.addSupportingType("com.structurizr.SomeType"); assertNull(component.getType()); } @Test - void test_getType_ReturnsThePrimaryCodeElement_WhenThereIsAPrimaryCodeElement() { + void getType_ReturnsThePrimaryCodeElement_WhenThereIsAPrimaryCodeElement() { Component component = new Component(); component.setType("com.structurizr.SomeType"); CodeElement codeElement = component.getType(); diff --git a/structurizr-core/test/unit/com/structurizr/model/ContainerInstanceTests.java b/structurizr-core/test/unit/com/structurizr/model/ContainerInstanceTests.java index 12e62a53c..445a11f22 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ContainerInstanceTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ContainerInstanceTests.java @@ -12,7 +12,7 @@ public class ContainerInstanceTests extends AbstractWorkspaceTestBase { private DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node", "Description", "Technology"); @Test - void test_construction() { + void construction() { ContainerInstance instance = deploymentNode.add(database); assertSame(database, instance.getContainer()); @@ -21,7 +21,7 @@ void test_construction() { } @Test - void test_getContainerId() { + void getContainerId() { ContainerInstance instance = deploymentNode.add(database); assertEquals(database.getId(), instance.getContainerId()); @@ -31,7 +31,7 @@ void test_getContainerId() { } @Test - void test_getName() { + void getName() { ContainerInstance instance = deploymentNode.add(database); assertEquals("Database Schema", instance.getName()); @@ -41,28 +41,28 @@ void test_getName() { } @Test - void test_getCanonicalName() { + void getCanonicalName() { ContainerInstance instance = deploymentNode.add(database); assertEquals("ContainerInstance://Default/Deployment Node/System.Database Schema[1]", instance.getCanonicalName()); } @Test - void test_getParent_ReturnsTheParentDeploymentNode() { + void getParent_ReturnsTheParentDeploymentNode() { ContainerInstance instance = deploymentNode.add(database); assertEquals(deploymentNode, instance.getParent()); } @Test - void test_getRequiredTags() { + void getRequiredTags() { ContainerInstance instance = deploymentNode.add(database); assertTrue(instance.getDefaultTags().isEmpty()); } @Test - void test_getTags() { + void getTags() { database.addTags("Database"); ContainerInstance instance = deploymentNode.add(database); instance.addTags("Primary Instance"); @@ -71,7 +71,7 @@ void test_getTags() { } @Test - void test_removeTags_DoesNotRemoveAnyTags() { + void removeTags_DoesNotRemoveAnyTags() { ContainerInstance instance = deploymentNode.add(database); assertTrue(instance.getTags().contains(Tags.CONTAINER_INSTANCE)); @@ -82,7 +82,7 @@ void test_removeTags_DoesNotRemoveAnyTags() { } @Test - void test_getDeploymentGroups_WhenNoGroupsHaveBeenSpecified() { + void getDeploymentGroups_WhenNoGroupsHaveBeenSpecified() { ContainerInstance instance = deploymentNode.add(database); assertEquals(1, instance.getDeploymentGroups().size()); @@ -90,7 +90,7 @@ void test_getDeploymentGroups_WhenNoGroupsHaveBeenSpecified() { } @Test - void test_getDeploymentGroups_WhenOneGroupHasBeenSpecified() { + void getDeploymentGroups_WhenOneGroupHasBeenSpecified() { ContainerInstance instance = deploymentNode.add(database, "Group 1"); assertEquals(1, instance.getDeploymentGroups().size()); @@ -98,7 +98,7 @@ void test_getDeploymentGroups_WhenOneGroupHasBeenSpecified() { } @Test - void test_getDeploymentGroups_WhenMultipleGroupsAreSpecified() { + void getDeploymentGroups_WhenMultipleGroupsAreSpecified() { ContainerInstance instance = deploymentNode.add(database, "Group 1", "Group 2"); assertEquals(2, instance.getDeploymentGroups().size()); @@ -107,7 +107,7 @@ void test_getDeploymentGroups_WhenMultipleGroupsAreSpecified() { } @Test - void test_addHealthCheck() { + void addHealthCheck() { ContainerInstance instance = deploymentNode.add(database); assertTrue(instance.getHealthChecks().isEmpty()); @@ -120,7 +120,7 @@ void test_addHealthCheck() { } @Test - void test_addHealthCheck_ThrowsAnException_WhenTheNameIsNull() { + void addHealthCheck_ThrowsAnException_WhenTheNameIsNull() { ContainerInstance instance = deploymentNode.add(database); try { @@ -132,7 +132,7 @@ void test_addHealthCheck_ThrowsAnException_WhenTheNameIsNull() { } @Test - void test_addHealthCheck_ThrowsAnException_WhenTheNameIsEmpty() { + void addHealthCheck_ThrowsAnException_WhenTheNameIsEmpty() { ContainerInstance instance = deploymentNode.add(database); try { @@ -144,7 +144,7 @@ void test_addHealthCheck_ThrowsAnException_WhenTheNameIsEmpty() { } @Test - void test_addHealthCheck_ThrowsAnException_WhenTheUrlIsNull() { + void addHealthCheck_ThrowsAnException_WhenTheUrlIsNull() { ContainerInstance instance = deploymentNode.add(database); try { @@ -156,7 +156,7 @@ void test_addHealthCheck_ThrowsAnException_WhenTheUrlIsNull() { } @Test - void test_addHealthCheck_ThrowsAnException_WhenTheUrlIsEmpty() { + void addHealthCheck_ThrowsAnException_WhenTheUrlIsEmpty() { ContainerInstance instance = deploymentNode.add(database); try { @@ -168,7 +168,7 @@ void test_addHealthCheck_ThrowsAnException_WhenTheUrlIsEmpty() { } @Test - void test_addHealthCheck_ThrowsAnException_WhenTheUrlIsInvalid() { + void addHealthCheck_ThrowsAnException_WhenTheUrlIsInvalid() { ContainerInstance instance = deploymentNode.add(database); try { @@ -180,7 +180,7 @@ void test_addHealthCheck_ThrowsAnException_WhenTheUrlIsInvalid() { } @Test - void test_addHealthCheck_ThrowsAnException_WhenTheIntervalIsLessThanZero() { + void addHealthCheck_ThrowsAnException_WhenTheIntervalIsLessThanZero() { ContainerInstance instance = deploymentNode.add(database); try { @@ -192,7 +192,7 @@ void test_addHealthCheck_ThrowsAnException_WhenTheIntervalIsLessThanZero() { } @Test - void test_addHealthCheck_ThrowsAnException_WhenTheTimeoutIsLessThanZero() { + void addHealthCheck_ThrowsAnException_WhenTheTimeoutIsLessThanZero() { ContainerInstance instance = deploymentNode.add(database); try { diff --git a/structurizr-core/test/unit/com/structurizr/model/ContainerTests.java b/structurizr-core/test/unit/com/structurizr/model/ContainerTests.java index 80bd6af86..3665f5194 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ContainerTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ContainerTests.java @@ -11,7 +11,7 @@ public class ContainerTests extends AbstractWorkspaceTestBase { private Container container = softwareSystem.addContainer("Container", "Description", "Some technology"); @Test - void test_technologyProperty() { + void technologyProperty() { assertEquals("Some technology", container.getTechnology()); container.setTechnology("Some other technology"); @@ -19,29 +19,29 @@ void test_technologyProperty() { } @Test - void test_getCanonicalName() { + void getCanonicalName() { assertEquals("Container://System.Container", container.getCanonicalName()); } @Test - void test_getCanonicalName_WhenNameContainsSlashAndDotCharacters() { + void getCanonicalName_WhenNameContainsSlashAndDotCharacters() { container = softwareSystem.addContainer("Name1/.Name2", "Description", "Some technology"); assertEquals("Container://System.Name1Name2", container.getCanonicalName()); } @Test - void test_getParent_ReturnsTheParentSoftwareSystem() { + void getParent_ReturnsTheParentSoftwareSystem() { assertEquals(softwareSystem, container.getParent()); } @Test - void test_getSoftwareSystem_ReturnsTheParentSoftwareSystem() { + void getSoftwareSystem_ReturnsTheParentSoftwareSystem() { assertEquals(softwareSystem, container.getSoftwareSystem()); } @Test - void test_removeTags_DoesNotRemoveRequiredTags() { + void removeTags_DoesNotRemoveRequiredTags() { assertTrue(container.getTags().contains(Tags.ELEMENT)); assertTrue(container.getTags().contains(Tags.CONTAINER)); @@ -53,21 +53,21 @@ void test_removeTags_DoesNotRemoveRequiredTags() { } @Test - void test_addComponent_ThrowsAnException_WhenANullNameIsSpecified() { + void addComponent_ThrowsAnException_WhenANullNameIsSpecified() { assertThrows(IllegalArgumentException.class, () -> { container.addComponent(null, ""); }); } @Test - void test_addComponent_ThrowsAnException_WhenAnEmptyNameIsSpecified() { + void addComponent_ThrowsAnException_WhenAnEmptyNameIsSpecified() { assertThrows(IllegalArgumentException.class, () -> { container.addComponent(" ", ""); }); } @Test - void test_addComponent_ThrowsAnException_WhenAComponentWithTheSameNameAlreadyExists() { + void addComponent_ThrowsAnException_WhenAComponentWithTheSameNameAlreadyExists() { container.addComponent("Component 1", ""); try { container.addComponent("Component 1", ""); @@ -78,7 +78,7 @@ void test_addComponent_ThrowsAnException_WhenAComponentWithTheSameNameAlreadyExi } @Test - void test_addComponent_AddsAComponentWithTheSpecifiedNameAndDescription() { + void addComponent_AddsAComponentWithTheSpecifiedNameAndDescription() { Component component = container.addComponent("Name", "Description"); assertTrue(container.getComponents().contains(component)); assertEquals("Name", component.getName()); @@ -90,7 +90,7 @@ void test_addComponent_AddsAComponentWithTheSpecifiedNameAndDescription() { } @Test - void test_addComponent_AddsAComponentWithTheSpecifiedNameAndDescriptionAndTechnology() { + void addComponent_AddsAComponentWithTheSpecifiedNameAndDescriptionAndTechnology() { Component component = container.addComponent("Name", "Description", "Technology"); assertTrue(container.getComponents().contains(component)); assertEquals("Name", component.getName()); @@ -102,7 +102,7 @@ void test_addComponent_AddsAComponentWithTheSpecifiedNameAndDescriptionAndTechno } @Test - void test_addComponent_AddsAComponentWithTheSpecifiedNameAndDescriptionAndTechnologyAndStringType() { + void addComponent_AddsAComponentWithTheSpecifiedNameAndDescriptionAndTechnologyAndStringType() { Component component = container.addComponent("Name", "SomeType", "Description", "Technology"); assertTrue(container.getComponents().contains(component)); assertEquals("Name", component.getName()); @@ -114,7 +114,7 @@ void test_addComponent_AddsAComponentWithTheSpecifiedNameAndDescriptionAndTechno } @Test - void test_addComponent_AddsAComponentWithTheSpecifiedNameAndDescriptionAndTechnologyAndClassType() { + void addComponent_AddsAComponentWithTheSpecifiedNameAndDescriptionAndTechnologyAndClassType() { Component component = container.addComponent("Name", this.getClass(), "Description", "Technology"); assertTrue(container.getComponents().contains(component)); assertEquals("Name", component.getName()); @@ -126,7 +126,7 @@ void test_addComponent_AddsAComponentWithTheSpecifiedNameAndDescriptionAndTechno } @Test - void test_getComponentWithName_ThrowsAnException_WhenANullNameIsSpecified() { + void getComponentWithName_ThrowsAnException_WhenANullNameIsSpecified() { try { container.getComponentWithName(null); fail(); @@ -136,7 +136,7 @@ void test_getComponentWithName_ThrowsAnException_WhenANullNameIsSpecified() { } @Test - void test_getComponentWithName_ThrowsAnException_WhenAnEmptyNameIsSpecified() { + void getComponentWithName_ThrowsAnException_WhenAnEmptyNameIsSpecified() { try { container.getComponentWithName(" "); fail(); @@ -146,7 +146,7 @@ void test_getComponentWithName_ThrowsAnException_WhenAnEmptyNameIsSpecified() { } @Test - void test_getComponentOfType_ThrowsAnException_WhenANullTypeIsSpecified() { + void getComponentOfType_ThrowsAnException_WhenANullTypeIsSpecified() { try { container.getComponentOfType(null); fail(); @@ -156,7 +156,7 @@ void test_getComponentOfType_ThrowsAnException_WhenANullTypeIsSpecified() { } @Test - void test_getComponentOfType_ThrowsAnException_WhenAnEmptyTypeIsSpecified() { + void getComponentOfType_ThrowsAnException_WhenAnEmptyTypeIsSpecified() { try { container.getComponentOfType(" "); fail(); @@ -166,12 +166,12 @@ void test_getComponentOfType_ThrowsAnException_WhenAnEmptyTypeIsSpecified() { } @Test - void test_getComponentOfType_ReturnsNull_WhenNoComponentWithTheSpecifiedTypeExists() { + void getComponentOfType_ReturnsNull_WhenNoComponentWithTheSpecifiedTypeExists() { assertNull(container.getComponentOfType("SomeType")); } @Test - void test_getComponentOfType_ReturnsAComponent_WhenAComponentWithTheSpecifiedTypeExists() { + void getComponentOfType_ReturnsAComponent_WhenAComponentWithTheSpecifiedTypeExists() { container.addComponent("Name", "SomeType", "Description", "Technology"); Component component = container.getComponentOfType("SomeType"); diff --git a/structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategyTests.java b/structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategyTests.java index d02cd43b4..aeb8b6839 100644 --- a/structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategyTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategyTests.java @@ -10,7 +10,7 @@ public class CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategyTests extends AbstractWorkspaceTestBase { @Test - void test_impliedRelationshipsAreCreated() { + void impliedRelationshipsAreCreated() { SoftwareSystem a = model.addSoftwareSystem("A", ""); Container aa = a.addContainer("AA", "", ""); Component aaa = aa.addComponent("AAA", "", ""); @@ -54,7 +54,7 @@ void test_impliedRelationshipsAreCreated() { } @Test - void test_impliedRelationshipsAreCreated_UnlessAnyRelationshipExists() { + void impliedRelationshipsAreCreated_UnlessAnyRelationshipExists() { SoftwareSystem a = model.addSoftwareSystem("A", ""); Container aa = a.addContainer("AA", "", ""); Component aaa = aa.addComponent("AAA", "", ""); diff --git a/structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategyTests.java b/structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategyTests.java index 38ebb887b..1b8fda1ce 100644 --- a/structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategyTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategyTests.java @@ -10,7 +10,7 @@ public class CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategyTests extends AbstractWorkspaceTestBase { @Test - void test_impliedRelationships_WhenNoSummaryRelationshipsExist() { + void impliedRelationships_WhenNoSummaryRelationshipsExist() { SoftwareSystem a = model.addSoftwareSystem("A", ""); Container aa = a.addContainer("AA", "", ""); Component aaa = aa.addComponent("AAA", "", ""); diff --git a/structurizr-core/test/unit/com/structurizr/model/CustomElementTests.java b/structurizr-core/test/unit/com/structurizr/model/CustomElementTests.java index 81fdbaf82..f90ef36a2 100644 --- a/structurizr-core/test/unit/com/structurizr/model/CustomElementTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/CustomElementTests.java @@ -8,7 +8,7 @@ public class CustomElementTests extends AbstractWorkspaceTestBase { @Test - void test_basicProperties() { + void basicProperties() { CustomElement element = model.addCustomElement("Name", "Type", "Description"); assertEquals("Name", element.getName()); assertEquals("Type", element.getMetadata()); @@ -16,26 +16,26 @@ void test_basicProperties() { } @Test - void test_getCanonicalName() { + void getCanonicalName() { CustomElement element = model.addCustomElement("Name", "Type", "Description"); assertEquals("Custom://Name", element.getCanonicalName()); } @Test - void test_getCanonicalName_WhenNameContainsSlashAndDotCharacters() { + void getCanonicalName_WhenNameContainsSlashAndDotCharacters() { CustomElement element = model.addCustomElement("Name", "Type", "Description"); element.setName("Name1/.Name2"); assertEquals("Custom://Name1Name2", element.getCanonicalName()); } @Test - void test_getParent_ReturnsNull() { + void getParent_ReturnsNull() { CustomElement element = model.addCustomElement("Name", "Type", "Description"); assertNull(element.getParent()); } @Test - void test_removeTags_DoesNotRemoveRequiredTags() { + void removeTags_DoesNotRemoveRequiredTags() { CustomElement element = model.addCustomElement("Name", "Type", "Description"); assertTrue(element.getTags().contains(Tags.ELEMENT)); @@ -45,7 +45,7 @@ void test_removeTags_DoesNotRemoveRequiredTags() { } @Test - void test_uses_AddsARelationshipWhenTheDescriptionIsSpecified() { + void uses_AddsARelationshipWhenTheDescriptionIsSpecified() { CustomElement element1 = model.addCustomElement("Box 1"); CustomElement element2 = model.addCustomElement("Box 2"); @@ -61,7 +61,7 @@ void test_uses_AddsARelationshipWhenTheDescriptionIsSpecified() { } @Test - void test_uses_AddsARelationshipWhenTheDescriptionAndTechnologyAreSpecified() { + void uses_AddsARelationshipWhenTheDescriptionAndTechnologyAreSpecified() { CustomElement element1 = model.addCustomElement("Box 1"); CustomElement element2 = model.addCustomElement("Box 2"); @@ -77,7 +77,7 @@ void test_uses_AddsARelationshipWhenTheDescriptionAndTechnologyAreSpecified() { } @Test - void test_uses_AddsARelationshipWhenTheDescriptionAndTechnologyAndInteractionStyleAreSpecified() { + void uses_AddsARelationshipWhenTheDescriptionAndTechnologyAndInteractionStyleAreSpecified() { CustomElement element1 = model.addCustomElement("Box 1"); CustomElement element2 = model.addCustomElement("Box 2"); @@ -93,7 +93,7 @@ void test_uses_AddsARelationshipWhenTheDescriptionAndTechnologyAndInteractionSty } @Test - void test_uses_AddsARelationshipWhenTheDescriptionAndTechnologyAndInteractionStyleAndTagsAreSpecified() { + void uses_AddsARelationshipWhenTheDescriptionAndTechnologyAndInteractionStyleAndTagsAreSpecified() { CustomElement element1 = model.addCustomElement("Box 1"); CustomElement element2 = model.addCustomElement("Box 2"); diff --git a/structurizr-core/test/unit/com/structurizr/model/DefaultImpliedRelationshipsStrategyTests.java b/structurizr-core/test/unit/com/structurizr/model/DefaultImpliedRelationshipsStrategyTests.java index afd611ab5..7f0e261ae 100644 --- a/structurizr-core/test/unit/com/structurizr/model/DefaultImpliedRelationshipsStrategyTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/DefaultImpliedRelationshipsStrategyTests.java @@ -9,7 +9,7 @@ public class DefaultImpliedRelationshipsStrategyTests extends AbstractWorkspaceTestBase { @Test - void test_createImpliedRelationships_DoesNothing() { + void createImpliedRelationships_DoesNothing() { SoftwareSystem a = model.addSoftwareSystem("A", ""); Container aa = a.addContainer("AA", "", ""); Component aaa = aa.addComponent("AAA", "", ""); diff --git a/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java b/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java index 86ae6a609..e7e0abd3f 100644 --- a/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java @@ -9,14 +9,14 @@ public class DeploymentNodeTests extends AbstractWorkspaceTestBase { @Test - void test_getCanonicalName_WhenTheDeploymentNodeHasNoParent() { + void getCanonicalName_WhenTheDeploymentNodeHasNoParent() { DeploymentNode deploymentNode = model.addDeploymentNode("Ubuntu Server", "", ""); assertEquals("DeploymentNode://Default/Ubuntu Server", deploymentNode.getCanonicalName()); } @Test - void test_getCanonicalName_WhenTheDeploymentNodeHasAParent() { + void getCanonicalName_WhenTheDeploymentNodeHasAParent() { DeploymentNode l1 = model.addDeploymentNode("Level 1", "", ""); DeploymentNode l2 = l1.addDeploymentNode("Level 2", "", ""); DeploymentNode l3 = l2.addDeploymentNode("Level 3", "", ""); @@ -27,7 +27,7 @@ void test_getCanonicalName_WhenTheDeploymentNodeHasAParent() { } @Test - void test_getParent_ReturnsTheParentDeploymentNode() { + void getParent_ReturnsTheParentDeploymentNode() { DeploymentNode parent = model.addDeploymentNode("Parent", "", ""); assertNull(parent.getParent()); @@ -37,7 +37,7 @@ void test_getParent_ReturnsTheParentDeploymentNode() { } @Test - void test_getRequiredTags() { + void getRequiredTags() { DeploymentNode deploymentNode = new DeploymentNode(); assertEquals(2, deploymentNode.getDefaultTags().size()); assertTrue(deploymentNode.getDefaultTags().contains(Tags.ELEMENT)); @@ -45,14 +45,14 @@ void test_getRequiredTags() { } @Test - void test_getTags() { + void getTags() { DeploymentNode deploymentNode = new DeploymentNode(); deploymentNode.addTags("Tag 1", "Tag 2"); assertEquals("Element,Deployment Node,Tag 1,Tag 2", deploymentNode.getTags()); } @Test - void test_add_ThrowsAnException_WhenASoftwareSystemIsNotSpecified() { + void add_ThrowsAnException_WhenASoftwareSystemIsNotSpecified() { try { DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node", "Description", "Technology"); deploymentNode.add((SoftwareSystem) null); @@ -63,7 +63,7 @@ void test_add_ThrowsAnException_WhenASoftwareSystemIsNotSpecified() { } @Test - void test_add_ThrowsAnException_WhenAContainerIsNotSpecified() { + void add_ThrowsAnException_WhenAContainerIsNotSpecified() { try { DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node", "Description", "Technology"); deploymentNode.add((Container) null); @@ -74,7 +74,7 @@ void test_add_ThrowsAnException_WhenAContainerIsNotSpecified() { } @Test - void test_add_AddsAContainerInstance_WhenAContainerIsSpecified() { + void add_AddsAContainerInstance_WhenAContainerIsSpecified() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); Container container = softwareSystem.addContainer("Container", "", ""); DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node", "", ""); @@ -87,7 +87,7 @@ void test_add_AddsAContainerInstance_WhenAContainerIsSpecified() { } @Test - void test_addDeploymentNode_ThrowsAnException_WhenANameIsNotSpecified() { + void addDeploymentNode_ThrowsAnException_WhenANameIsNotSpecified() { try { DeploymentNode parent = model.addDeploymentNode("Parent", "", ""); parent.addDeploymentNode(null, "", ""); @@ -98,7 +98,7 @@ void test_addDeploymentNode_ThrowsAnException_WhenANameIsNotSpecified() { } @Test - void test_addDeploymentNode_AddsAChildDeploymentNode_WhenANameIsSpecified() { + void addDeploymentNode_AddsAChildDeploymentNode_WhenANameIsSpecified() { DeploymentNode parent = model.addDeploymentNode("Parent", "", ""); DeploymentNode child = parent.addDeploymentNode("Child 1", "Description", "Technology"); @@ -134,7 +134,7 @@ void test_addDeploymentNode_AddsAChildDeploymentNode_WhenANameIsSpecified() { } @Test - void test_uses_ThrowsAnException_WhenANullDestinationIsSpecified() { + void uses_ThrowsAnException_WhenANullDestinationIsSpecified() { try { DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node", "", ""); deploymentNode.uses((DeploymentNode) null, "", ""); @@ -145,7 +145,7 @@ void test_uses_ThrowsAnException_WhenANullDestinationIsSpecified() { } @Test - void test_uses_AddsARelationship() { + void uses_AddsARelationship() { DeploymentNode primaryNode = model.addDeploymentNode("MySQL - Primary", "", ""); DeploymentNode secondaryNode = model.addDeploymentNode("MySQL - Secondary", "", ""); Relationship relationship = primaryNode.uses(secondaryNode, "Replicates data to", "Some technology"); @@ -158,7 +158,7 @@ void test_uses_AddsARelationship() { } @Test - void test_getDeploymentNodeWithName_ThrowsAnException_WhenANameIsNotSpecified() { + void getDeploymentNodeWithName_ThrowsAnException_WhenANameIsNotSpecified() { try { DeploymentNode deploymentNode = new DeploymentNode(); deploymentNode.getDeploymentNodeWithName(null); @@ -169,33 +169,33 @@ void test_getDeploymentNodeWithName_ThrowsAnException_WhenANameIsNotSpecified() } @Test - void test_getDeploymentNodeWithName_ReturnsNull_WhenThereIsNoDeploymentNodeWithTheSpecifiedName() { + void getDeploymentNodeWithName_ReturnsNull_WhenThereIsNoDeploymentNodeWithTheSpecifiedName() { DeploymentNode deploymentNode = new DeploymentNode(); assertNull(deploymentNode.getDeploymentNodeWithName("foo")); } @Test - void test_getDeploymentNodeWithName_ReturnsTheNamedDeploymentNode_WhenThereIsADeploymentNodeWithTheSpecifiedName() { + void getDeploymentNodeWithName_ReturnsTheNamedDeploymentNode_WhenThereIsADeploymentNodeWithTheSpecifiedName() { DeploymentNode parent = model.addDeploymentNode("parent", "", ""); DeploymentNode child = parent.addDeploymentNode("child", "", ""); assertSame(child, parent.getDeploymentNodeWithName("child")); } @Test - void test_getInfrastructureNodeWithName_ReturnsNull_WhenThereIsNoInfrastructureNodeWithTheSpecifiedName() { + void getInfrastructureNodeWithName_ReturnsNull_WhenThereIsNoInfrastructureNodeWithTheSpecifiedName() { DeploymentNode deploymentNode = new DeploymentNode(); assertNull(deploymentNode.getInfrastructureNodeWithName("foo")); } @Test - void test_getInfrastructureNodeWithName_ReturnsTheNamedDeploymentNode_WhenThereIsAInfrastructureNodeWithTheSpecifiedName() { + void getInfrastructureNodeWithName_ReturnsTheNamedDeploymentNode_WhenThereIsAInfrastructureNodeWithTheSpecifiedName() { DeploymentNode parent = model.addDeploymentNode("parent", "", ""); InfrastructureNode child = parent.addInfrastructureNode("child", "", ""); assertSame(child, parent.getInfrastructureNodeWithName("child")); } @Test - void test_setInstances() { + void setInstances() { DeploymentNode deploymentNode = new DeploymentNode(); deploymentNode.setInstances(8); @@ -203,7 +203,7 @@ void test_setInstances() { } @Test - void test_setInstances_ThrowsAnException_WhenZeroIsSpecified() { + void setInstances_ThrowsAnException_WhenZeroIsSpecified() { try { DeploymentNode deploymentNode = new DeploymentNode(); deploymentNode.setInstances(0); @@ -214,7 +214,7 @@ void test_setInstances_ThrowsAnException_WhenZeroIsSpecified() { } @Test - void test_setInstances_ThrowsAnException_WhenANegativeNumberIsSpecified() { + void setInstances_ThrowsAnException_WhenANegativeNumberIsSpecified() { try { DeploymentNode deploymentNode = new DeploymentNode(); deploymentNode.setInstances(-1); diff --git a/structurizr-core/test/unit/com/structurizr/model/ElementTests.java b/structurizr-core/test/unit/com/structurizr/model/ElementTests.java index 1bf94229e..05a151574 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ElementTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ElementTests.java @@ -8,14 +8,14 @@ public class ElementTests extends AbstractWorkspaceTestBase { @Test - void test_construction() { + void construction() { Element element = model.addSoftwareSystem("Name", "Description"); assertEquals("Name", element.getName()); assertEquals("Description", element.getDescription()); } @Test - void test_setName_ThrowsAnException_WhenANullValueIsSpecified() { + void setName_ThrowsAnException_WhenANullValueIsSpecified() { Element element = model.addSoftwareSystem("Name", "Description"); try { element.setName(null); @@ -26,7 +26,7 @@ void test_setName_ThrowsAnException_WhenANullValueIsSpecified() { } @Test - void test_setName_ThrowsAnException_WhenAnEmptyValueIsSpecified() { + void setName_ThrowsAnException_WhenAnEmptyValueIsSpecified() { Element element = model.addSoftwareSystem("Name", "Description"); try { element.setName(" "); @@ -37,26 +37,26 @@ void test_setName_ThrowsAnException_WhenAnEmptyValueIsSpecified() { } @Test - void test_hasEfferentRelationshipWith_ReturnsFalse_WhenANullElementIsSpecified() { + void hasEfferentRelationshipWith_ReturnsFalse_WhenANullElementIsSpecified() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("System 1", ""); assertFalse(softwareSystem1.hasEfferentRelationshipWith(null)); } @Test - void test_hasEfferentRelationshipWith_ReturnsFalse_WhenTheSameElementIsSpecifiedAndNoCyclicRelationshipExists() { + void hasEfferentRelationshipWith_ReturnsFalse_WhenTheSameElementIsSpecifiedAndNoCyclicRelationshipExists() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("System 1", ""); assertFalse(softwareSystem1.hasEfferentRelationshipWith(softwareSystem1)); } @Test - void test_hasEfferentRelationshipWith_ReturnsTrue_WhenTheSameElementIsSpecifiedAndACyclicRelationshipExists() { + void hasEfferentRelationshipWith_ReturnsTrue_WhenTheSameElementIsSpecifiedAndACyclicRelationshipExists() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("System 1", ""); softwareSystem1.uses(softwareSystem1, "uses"); assertTrue(softwareSystem1.hasEfferentRelationshipWith(softwareSystem1)); } @Test - void test_hasEfferentRelationshipWith_ReturnsTrue_WhenThereIsARelationship() { + void hasEfferentRelationshipWith_ReturnsTrue_WhenThereIsARelationship() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("System 1", ""); SoftwareSystem softwareSystem2 = model.addSoftwareSystem("System 2", ""); softwareSystem1.uses(softwareSystem2, "uses"); @@ -64,13 +64,13 @@ void test_hasEfferentRelationshipWith_ReturnsTrue_WhenThereIsARelationship() { } @Test - void test_hasEfferentRelationshipWithElementAndDescription_ReturnsFalse_WhenANullElementIsSpecified() { + void hasEfferentRelationshipWithElementAndDescription_ReturnsFalse_WhenANullElementIsSpecified() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("System 1", ""); assertFalse(softwareSystem1.hasEfferentRelationshipWith(null, null)); } @Test - void test_hasEfferentRelationshipWithElementAndDescription_ReturnsFalse_WhenThereIsNotAMatchingRelationship() { + void hasEfferentRelationshipWithElementAndDescription_ReturnsFalse_WhenThereIsNotAMatchingRelationship() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("System 1", ""); SoftwareSystem softwareSystem2 = model.addSoftwareSystem("System 2", ""); softwareSystem1.uses(softwareSystem2, "Uses"); @@ -79,7 +79,7 @@ void test_hasEfferentRelationshipWithElementAndDescription_ReturnsFalse_WhenTher } @Test - void test_hasEfferentRelationshipWithElementAndDescription_ReturnsTrue_WhenThereIsAMatchingRelationship() { + void hasEfferentRelationshipWithElementAndDescription_ReturnsTrue_WhenThereIsAMatchingRelationship() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("System 1", ""); SoftwareSystem softwareSystem2 = model.addSoftwareSystem("System 2", ""); softwareSystem1.uses(softwareSystem2, "Uses"); @@ -88,19 +88,19 @@ void test_hasEfferentRelationshipWithElementAndDescription_ReturnsTrue_WhenThere } @Test - void test_getEfferentRelationshipWith_ReturnsNull_WhenANullElementIsSpecified() { + void getEfferentRelationshipWith_ReturnsNull_WhenANullElementIsSpecified() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("System 1", ""); assertNull(softwareSystem1.getEfferentRelationshipWith(null)); } @Test - void test_getEfferentRelationshipWith_ReturnsNull_WhenTheSameElementIsSpecifiedAndNoCyclicRelationshipExists() { + void getEfferentRelationshipWith_ReturnsNull_WhenTheSameElementIsSpecifiedAndNoCyclicRelationshipExists() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("System 1", ""); assertNull(softwareSystem1.getEfferentRelationshipWith(softwareSystem1)); } @Test - void test_getEfferentRelationshipWith_ReturnsCyclicRelationship_WhenTheSameElementIsSpecifiedAndACyclicRelationshipExists() { + void getEfferentRelationshipWith_ReturnsCyclicRelationship_WhenTheSameElementIsSpecifiedAndACyclicRelationshipExists() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("System 1", ""); softwareSystem1.uses(softwareSystem1, "uses"); @@ -111,7 +111,7 @@ void test_getEfferentRelationshipWith_ReturnsCyclicRelationship_WhenTheSameEleme } @Test - void test_getEfferentRelationshipWith_ReturnsTheRelationship_WhenThereIsARelationship() { + void getEfferentRelationshipWith_ReturnsTheRelationship_WhenThereIsARelationship() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("System 1", ""); SoftwareSystem softwareSystem2 = model.addSoftwareSystem("System 2", ""); softwareSystem1.uses(softwareSystem2, "uses"); @@ -123,7 +123,7 @@ void test_getEfferentRelationshipWith_ReturnsTheRelationship_WhenThereIsARelatio } @Test - void test_hasAfferentRelationships_ReturnsFalse_WhenThereAreNoIncomingRelationships() { + void hasAfferentRelationships_ReturnsFalse_WhenThereAreNoIncomingRelationships() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("System 1", ""); SoftwareSystem softwareSystem2 = model.addSoftwareSystem("System 2", ""); softwareSystem1.uses(softwareSystem2, "Uses"); @@ -132,7 +132,7 @@ void test_hasAfferentRelationships_ReturnsFalse_WhenThereAreNoIncomingRelationsh } @Test - void test_hasAfferentRelationships_ReturnsTrue_WhenThereAreIncomingRelationships() { + void hasAfferentRelationships_ReturnsTrue_WhenThereAreIncomingRelationships() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("System 1", ""); SoftwareSystem softwareSystem2 = model.addSoftwareSystem("System 2", ""); softwareSystem1.uses(softwareSystem2, "Uses"); @@ -141,7 +141,7 @@ void test_hasAfferentRelationships_ReturnsTrue_WhenThereAreIncomingRelationships } @Test - void test_addRelationship_DoesNothing_WhenTheSameRelationshipWithASoftwareSystemIsAddedMoreThanOnce() { + void addRelationship_DoesNothing_WhenTheSameRelationshipWithASoftwareSystemIsAddedMoreThanOnce() { SoftwareSystem a = model.addSoftwareSystem("A", ""); SoftwareSystem b = model.addSoftwareSystem("B", ""); @@ -160,7 +160,7 @@ void test_addRelationship_DoesNothing_WhenTheSameRelationshipWithASoftwareSystem } @Test - void test_addRelationship_DoesNothing_WhenTheSameRelationshipWithAContainerIsAddedMoreThanOnce() { + void addRelationship_DoesNothing_WhenTheSameRelationshipWithAContainerIsAddedMoreThanOnce() { SoftwareSystem a = model.addSoftwareSystem("A", ""); SoftwareSystem b = model.addSoftwareSystem("B", ""); Container bb = b.addContainer("BB", "", ""); @@ -180,7 +180,7 @@ void test_addRelationship_DoesNothing_WhenTheSameRelationshipWithAContainerIsAdd } @Test - void test_addRelationship_DoesNothing_WhenTheSameRelationshipWithAComponentIsAddedMoreThanOnce() { + void addRelationship_DoesNothing_WhenTheSameRelationshipWithAComponentIsAddedMoreThanOnce() { SoftwareSystem a = model.addSoftwareSystem("A", ""); SoftwareSystem b = model.addSoftwareSystem("B", ""); Container bb = b.addContainer("BB", "", ""); @@ -201,7 +201,7 @@ void test_addRelationship_DoesNothing_WhenTheSameRelationshipWithAComponentIsAdd } @Test - void test_addRelationship_DoesNothing_WhenTheSameRelationshipWithAPersonIsAddedMoreThanOnce() { + void addRelationship_DoesNothing_WhenTheSameRelationshipWithAPersonIsAddedMoreThanOnce() { SoftwareSystem a = model.addSoftwareSystem("A", ""); Person b = model.addPerson("B", ""); @@ -220,46 +220,46 @@ void test_addRelationship_DoesNothing_WhenTheSameRelationshipWithAPersonIsAddedM } @Test - void test_equals_ReturnsFalse_WhenTestedAgainstNull() { + void equals_ReturnsFalse_WhenTestedAgainstNull() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); assertNotEquals(softwareSystem, null); } @Test - void test_equals_ReturnsFalse_WhenTheTwoObjectsAreDifferentTypes() { + void equals_ReturnsFalse_WhenTheTwoObjectsAreDifferentTypes() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); assertNotEquals(softwareSystem, "hello world"); } @Test - void test_equals_ReturnsTrue_WhenTestedAgainstItself() { + void equals_ReturnsTrue_WhenTestedAgainstItself() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); assertEquals(softwareSystem, softwareSystem); } @Test - void test_equals_ReturnsFalse_WhenTheTwoObjectsAreDifferent() { + void equals_ReturnsFalse_WhenTheTwoObjectsAreDifferent() { SoftwareSystem softwareSystemA = model.addSoftwareSystem("A", "Description"); SoftwareSystem softwareSystemB = model.addSoftwareSystem("B", "Description"); assertNotEquals(softwareSystemA, softwareSystemB); } @Test - void test_equals_ReturnsFalse_WhenTheTwoElementsHaveTheSameCanonicalNameButAreDifferentTypes() { + void equals_ReturnsFalse_WhenTheTwoElementsHaveTheSameCanonicalNameButAreDifferentTypes() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); Person person = model.addPerson("Name", "Description"); assertNotEquals(softwareSystem, person); } @Test - void test_setUrl() { + void setUrl() { Element element = model.addSoftwareSystem("Name", "Description"); element.setUrl("https://structurizr.com"); assertEquals("https://structurizr.com", element.getUrl()); } @Test - void test_setUrl_ThrowsAnIllegalArgumentException_WhenAnInvalidUrlIsSpecified() { + void setUrl_ThrowsAnIllegalArgumentException_WhenAnInvalidUrlIsSpecified() { assertThrows(IllegalArgumentException.class, () -> { Element element = model.addSoftwareSystem("Name", "Description"); element.setUrl("htt://blah"); @@ -267,7 +267,7 @@ void test_setUrl_ThrowsAnIllegalArgumentException_WhenAnInvalidUrlIsSpecified() } @Test - void test_setUrl_ResetsTheUrl_WhenANullUrlIsSpecified() { + void setUrl_ResetsTheUrl_WhenANullUrlIsSpecified() { Element element = model.addSoftwareSystem("Name", "Description"); element.setUrl("https://structurizr.com"); element.setUrl(null); @@ -275,7 +275,7 @@ void test_setUrl_ResetsTheUrl_WhenANullUrlIsSpecified() { } @Test - void test_setUrl_ResetsTheUrl_WhenAnEmptyUrlIsSpecified() { + void setUrl_ResetsTheUrl_WhenAnEmptyUrlIsSpecified() { Element element = model.addSoftwareSystem("Name", "Description"); element.setUrl("https://structurizr.com"); element.setUrl(" "); diff --git a/structurizr-core/test/unit/com/structurizr/model/GroupableElementTests.java b/structurizr-core/test/unit/com/structurizr/model/GroupableElementTests.java index bc91c32b9..3c981f390 100644 --- a/structurizr-core/test/unit/com/structurizr/model/GroupableElementTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/GroupableElementTests.java @@ -9,27 +9,27 @@ public class GroupableElementTests extends AbstractWorkspaceTestBase { @Test - void test_getGroup_ReturnsNullByDefault() { + void getGroup_ReturnsNullByDefault() { Person element = model.addPerson("Person"); assertNull(element.getGroup()); } @Test - void test_setGroup() { + void setGroup() { Person element = model.addPerson("Person"); element.setGroup("Group"); assertEquals("Group", element.getGroup()); } @Test - void test_setGroup_TrimsWhiteSpace() { + void setGroup_TrimsWhiteSpace() { Person element = model.addPerson("Person"); element.setGroup(" Group "); assertEquals("Group", element.getGroup()); } @Test - void test_setGroup_HandlesEmptyAndNullValues() { + void setGroup_HandlesEmptyAndNullValues() { Person element = model.addPerson("Person"); element.setGroup("Group"); diff --git a/structurizr-core/test/unit/com/structurizr/model/HttpHealthCheckTests.java b/structurizr-core/test/unit/com/structurizr/model/HttpHealthCheckTests.java index 8b3ec7496..57935806b 100644 --- a/structurizr-core/test/unit/com/structurizr/model/HttpHealthCheckTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/HttpHealthCheckTests.java @@ -10,13 +10,13 @@ public class HttpHealthCheckTests { private HttpHealthCheck healthCheck; @Test - void test_defaultConstructorExists() { + void defaultConstructorExists() { // the default constructor is used when deserializing from JSON healthCheck = new HttpHealthCheck(); } @Test - void test_construction() { + void construction() { healthCheck = new HttpHealthCheck("Name", "http://localhost", 120, 1000); assertEquals("Name", healthCheck.getName()); assertEquals("http://localhost", healthCheck.getUrl()); @@ -25,14 +25,14 @@ void test_construction() { } @Test - void test_addHeader() { + void addHeader() { healthCheck = new HttpHealthCheck(); healthCheck.addHeader("Name", "Value"); assertEquals("Value", healthCheck.getHeaders().get("Name")); } @Test - void test_addHeader_ThrowsAnException_WhenTheHeaderNameIsNull() { + void addHeader_ThrowsAnException_WhenTheHeaderNameIsNull() { healthCheck = new HttpHealthCheck(); try { healthCheck.addHeader(null, "value"); @@ -43,7 +43,7 @@ void test_addHeader_ThrowsAnException_WhenTheHeaderNameIsNull() { } @Test - void test_addHeader_ThrowsAnException_WhenTheHeaderNameIsEmpty() { + void addHeader_ThrowsAnException_WhenTheHeaderNameIsEmpty() { healthCheck = new HttpHealthCheck(); try { healthCheck.addHeader(" ", "value"); @@ -54,7 +54,7 @@ void test_addHeader_ThrowsAnException_WhenTheHeaderNameIsEmpty() { } @Test - void test_addHeader_ThrowsAnException_WhenTheHeaderValueIsNull() { + void addHeader_ThrowsAnException_WhenTheHeaderValueIsNull() { healthCheck = new HttpHealthCheck(); try { healthCheck.addHeader("Name", null); @@ -65,7 +65,7 @@ void test_addHeader_ThrowsAnException_WhenTheHeaderValueIsNull() { } @Test - void test_addHeader_DoesNotThrowAnException_WhenTheHeaderValueIsEmpty() { + void addHeader_DoesNotThrowAnException_WhenTheHeaderValueIsEmpty() { healthCheck = new HttpHealthCheck(); healthCheck.addHeader("Name", ""); assertEquals("", healthCheck.getHeaders().get("Name")); diff --git a/structurizr-core/test/unit/com/structurizr/model/InfrastructureNodeTests.java b/structurizr-core/test/unit/com/structurizr/model/InfrastructureNodeTests.java index c910b2493..8ec8bb364 100644 --- a/structurizr-core/test/unit/com/structurizr/model/InfrastructureNodeTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/InfrastructureNodeTests.java @@ -8,7 +8,7 @@ public class InfrastructureNodeTests extends AbstractWorkspaceTestBase { @Test - void test_getCanonicalName() { + void getCanonicalName() { DeploymentNode deploymentNode = model.addDeploymentNode("Amazon Web Services", "", ""); InfrastructureNode infrastructureNode = deploymentNode.addInfrastructureNode("Route 53", "", ""); @@ -16,7 +16,7 @@ void test_getCanonicalName() { } @Test - void test_getParent_ReturnsTheParentDeploymentNode() { + void getParent_ReturnsTheParentDeploymentNode() { DeploymentNode parent = model.addDeploymentNode("Parent", "", ""); InfrastructureNode child = parent.addInfrastructureNode("Child", "", ""); child.setParent(parent); @@ -24,7 +24,7 @@ void test_getParent_ReturnsTheParentDeploymentNode() { } @Test - void test_getRequiredTags() { + void getRequiredTags() { InfrastructureNode infrastructureNode = new InfrastructureNode(); assertEquals(2, infrastructureNode.getDefaultTags().size()); assertTrue(infrastructureNode.getDefaultTags().contains(Tags.ELEMENT)); @@ -32,7 +32,7 @@ void test_getRequiredTags() { } @Test - void test_getTags() { + void getTags() { InfrastructureNode infrastructureNode = new InfrastructureNode(); infrastructureNode.addTags("Tag 1", "Tag 2"); assertEquals("Element,Infrastructure Node,Tag 1,Tag 2", infrastructureNode.getTags()); diff --git a/structurizr-core/test/unit/com/structurizr/model/ModelItemTests.java b/structurizr-core/test/unit/com/structurizr/model/ModelItemTests.java index 8bdf44012..ae9c33852 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ModelItemTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ModelItemTests.java @@ -11,41 +11,41 @@ public class ModelItemTests extends AbstractWorkspaceTestBase { @Test - void test_construction() { + void construction() { Element element = model.addSoftwareSystem("Name", "Description"); assertEquals("Name", element.getName()); assertEquals("Description", element.getDescription()); } @Test - void test_getTags_WhenThereAreNoTags() { + void getTags_WhenThereAreNoTags() { Element element = model.addSoftwareSystem("Name", "Description"); assertEquals("Element,Software System", element.getTags()); } @Test - void test_hasTag_ChecksRequiredTags() { + void hasTag_ChecksRequiredTags() { SoftwareSystem system = model.addSoftwareSystem("Name", "Description"); assertTrue(system.hasTag("Software System"), "hasTag returns true for Software System"); assertTrue(system.hasTag("Element"), "hasTag returns true for Element"); } @Test - void test_getTags_ReturnsTheListOfTags_WhenThereAreSomeTags() { + void getTags_ReturnsTheListOfTags_WhenThereAreSomeTags() { Element element = model.addSoftwareSystem("Name", "Description"); element.addTags("tag1", "tag2", "tag3"); assertEquals("Element,Software System,tag1,tag2,tag3", element.getTags()); } @Test - void test_setTags_DoesNotDoAnything_WhenPassedNull() { + void setTags_DoesNotDoAnything_WhenPassedNull() { Element element = model.addSoftwareSystem("Name", "Description"); element.setTags(null); assertEquals("Element,Software System", element.getTags()); } @Test - void test_addTags_DoesNotDoAnything_WhenPassedNull() { + void addTags_DoesNotDoAnything_WhenPassedNull() { Element element = model.addSoftwareSystem("Name", "Description"); element.addTags((String) null); assertEquals("Element,Software System", element.getTags()); @@ -55,21 +55,21 @@ void test_addTags_DoesNotDoAnything_WhenPassedNull() { } @Test - void test_addTags_AddsTags_WhenPassedSomeTags() { + void addTags_AddsTags_WhenPassedSomeTags() { Element element = model.addSoftwareSystem("Name", "Description"); element.addTags(null, "tag1", null, "tag2"); assertEquals("Element,Software System,tag1,tag2", element.getTags()); } @Test - void test_addTags_AddsTags_WhenPassedSomeTagsAndThereAreDuplicateTags() { + void addTags_AddsTags_WhenPassedSomeTagsAndThereAreDuplicateTags() { Element element = model.addSoftwareSystem("Name", "Description"); element.addTags(null, "tag1", null, "tag2", "tag2"); assertEquals("Element,Software System,tag1,tag2", element.getTags()); } @Test - void test_removeTags() { + void removeTags() { Element element = model.addSoftwareSystem("Name", "Description"); element.addTags("tag1", "tag2"); assertTrue(element.removeTag("tag1"), "Remove an existing tag returns true"); @@ -80,13 +80,13 @@ void test_removeTags() { } @Test - void test_getProperties_ReturnsAnEmptyList_WhenNoPropertiesHaveBeenAdded() { + void getProperties_ReturnsAnEmptyList_WhenNoPropertiesHaveBeenAdded() { Element element = model.addSoftwareSystem("Name", "Description"); assertEquals(0, element.getProperties().size()); } @Test - void test_addProperty_ThrowsAnException_WhenTheNameIsNull() { + void addProperty_ThrowsAnException_WhenTheNameIsNull() { try { Element element = model.addSoftwareSystem("Name", "Description"); element.addProperty(null, "value"); @@ -97,7 +97,7 @@ void test_addProperty_ThrowsAnException_WhenTheNameIsNull() { } @Test - void test_addProperty_ThrowsAnException_WhenTheNameIsEmpty() { + void addProperty_ThrowsAnException_WhenTheNameIsEmpty() { try { Element element = model.addSoftwareSystem("Name", "Description"); element.addProperty(" ", "value"); @@ -108,7 +108,7 @@ void test_addProperty_ThrowsAnException_WhenTheNameIsEmpty() { } @Test - void test_addProperty_ThrowsAnException_WhenTheValueIsNull() { + void addProperty_ThrowsAnException_WhenTheValueIsNull() { try { Element element = model.addSoftwareSystem("Name", "Description"); element.addProperty("name", null); @@ -119,7 +119,7 @@ void test_addProperty_ThrowsAnException_WhenTheValueIsNull() { } @Test - void test_addProperty_ThrowsAnException_WhenTheValueIsEmpty() { + void addProperty_ThrowsAnException_WhenTheValueIsEmpty() { try { Element element = model.addSoftwareSystem("Name", "Description"); element.addProperty("name", " "); @@ -130,21 +130,21 @@ void test_addProperty_ThrowsAnException_WhenTheValueIsEmpty() { } @Test - void test_addProperty_AddsTheProperty_WhenANameAndValueAreSpecified() { + void addProperty_AddsTheProperty_WhenANameAndValueAreSpecified() { Element element = model.addSoftwareSystem("Name", "Description"); element.addProperty("AWS region", "us-east-1"); assertEquals("us-east-1", element.getProperties().get("AWS region")); } @Test - void test_setProperties_DoesNothing_WhenNullIsSpecified() { + void setProperties_DoesNothing_WhenNullIsSpecified() { Element element = model.addSoftwareSystem("Name", "Description"); element.setProperties(null); assertEquals(0, element.getProperties().size()); } @Test - void test_setProperties_SetsTheProperties_WhenANonEmptyMapIsSpecified() { + void setProperties_SetsTheProperties_WhenANonEmptyMapIsSpecified() { Element element = model.addSoftwareSystem("Name", "Description"); Map properties = new HashMap<>(); properties.put("name", "value"); @@ -154,7 +154,7 @@ void test_setProperties_SetsTheProperties_WhenANonEmptyMapIsSpecified() { } @Test - void test_addPerspective_ThrowsAnException_WhenANameIsNotSpecified() { + void addPerspective_ThrowsAnException_WhenANameIsNotSpecified() { try { Element element = model.addSoftwareSystem("Name", "Description"); element.addPerspective(null, null); @@ -165,7 +165,7 @@ void test_addPerspective_ThrowsAnException_WhenANameIsNotSpecified() { } @Test - void test_addPerspective_ThrowsAnException_WhenAnEmptyNameIsSpecified() { + void addPerspective_ThrowsAnException_WhenAnEmptyNameIsSpecified() { try { Element element = model.addSoftwareSystem("Name", "Description"); element.addPerspective(" ", null); @@ -176,7 +176,7 @@ void test_addPerspective_ThrowsAnException_WhenAnEmptyNameIsSpecified() { } @Test - void test_addPerspective_ThrowsAnException_WhenADescriptionIsNotSpecified() { + void addPerspective_ThrowsAnException_WhenADescriptionIsNotSpecified() { try { Element element = model.addSoftwareSystem("Name", "Description"); element.addPerspective("Security", null); @@ -187,7 +187,7 @@ void test_addPerspective_ThrowsAnException_WhenADescriptionIsNotSpecified() { } @Test - void test_addPerspective_ThrowsAnException_WhenAnEmptyDescriptionIsSpecified() { + void addPerspective_ThrowsAnException_WhenAnEmptyDescriptionIsSpecified() { try { Element element = model.addSoftwareSystem("Name", "Description"); element.addPerspective("Security", " "); @@ -198,7 +198,7 @@ void test_addPerspective_ThrowsAnException_WhenAnEmptyDescriptionIsSpecified() { } @Test - void test_addPerspective_AddsAPerspective() { + void addPerspective_AddsAPerspective() { Element element = model.addSoftwareSystem("Name", "Description"); Perspective perspective = element.addPerspective("Security", "Data is encrypted at rest."); assertEquals("Security", perspective.getName()); @@ -207,7 +207,7 @@ void test_addPerspective_AddsAPerspective() { } @Test - void test_addPerspective_ThrowsAnException_WhenTheNamedPerspectiveAlreadyExists() { + void addPerspective_ThrowsAnException_WhenTheNamedPerspectiveAlreadyExists() { try { Element element = model.addSoftwareSystem("Name", "Description"); element.addPerspective("Security", "Data is encrypted at rest."); diff --git a/structurizr-core/test/unit/com/structurizr/model/ModelTests.java b/structurizr-core/test/unit/com/structurizr/model/ModelTests.java index 3d429a746..3407988e6 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ModelTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ModelTests.java @@ -10,35 +10,35 @@ public class ModelTests extends AbstractWorkspaceTestBase { @Test - void test_addSoftwareSystem_ThrowsAnException_WhenANullNameIsSpecified() { + void addSoftwareSystem_ThrowsAnException_WhenANullNameIsSpecified() { assertThrows(IllegalArgumentException.class, () -> { model.addSoftwareSystem(null, ""); }); } @Test - void test_addSoftwareSystem_ThrowsAnException_WhenAnEmptyNameIsSpecified() { + void addSoftwareSystem_ThrowsAnException_WhenAnEmptyNameIsSpecified() { assertThrows(IllegalArgumentException.class, () -> { model.addSoftwareSystem(" ", ""); }); } @Test - void test_addPerson_ThrowsAnException_WhenANullNameIsSpecified() { + void addPerson_ThrowsAnException_WhenANullNameIsSpecified() { assertThrows(IllegalArgumentException.class, () -> { model.addPerson(null, ""); }); } @Test - void test_addPerson_ThrowsAnException_WhenAnEmptyNameIsSpecified() { + void addPerson_ThrowsAnException_WhenAnEmptyNameIsSpecified() { assertThrows(IllegalArgumentException.class, () -> { model.addPerson(" ", ""); }); } @Test - void test_addSoftwareSystem_AddsTheSoftwareSystem_WhenASoftwareSystemDoesNotExistWithTheSameName() { + void addSoftwareSystem_AddsTheSoftwareSystem_WhenASoftwareSystemDoesNotExistWithTheSameName() { assertTrue(model.getSoftwareSystems().isEmpty()); SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.External, "System A", "Some description"); assertEquals(1, model.getSoftwareSystems().size()); @@ -51,7 +51,7 @@ void test_addSoftwareSystem_AddsTheSoftwareSystem_WhenASoftwareSystemDoesNotExis } @Test - void test_addSoftwareSystem_ThrowsAnException_WhenASoftwareSystemExistsWithTheSameName() { + void addSoftwareSystem_ThrowsAnException_WhenASoftwareSystemExistsWithTheSameName() { SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.External, "System A", "Some description"); assertEquals(1, model.getSoftwareSystems().size()); @@ -64,7 +64,7 @@ void test_addSoftwareSystem_ThrowsAnException_WhenASoftwareSystemExistsWithTheSa } @Test - void test_addSoftwareSystemWithoutSpecifyingLocation_AddsTheSoftwareSystem_WhenASoftwareSystemDoesNotExistWithTheSameName() { + void addSoftwareSystemWithoutSpecifyingLocation_AddsTheSoftwareSystem_WhenASoftwareSystemDoesNotExistWithTheSameName() { assertTrue(model.getSoftwareSystems().isEmpty()); SoftwareSystem softwareSystem = model.addSoftwareSystem("System A", "Some description"); assertEquals(1, model.getSoftwareSystems().size()); @@ -77,7 +77,7 @@ void test_addSoftwareSystemWithoutSpecifyingLocation_AddsTheSoftwareSystem_WhenA } @Test - void test_addPerson_AddsThePerson_WhenAPersonDoesNotExistWithTheSameName() { + void addPerson_AddsThePerson_WhenAPersonDoesNotExistWithTheSameName() { assertTrue(model.getPeople().isEmpty()); Person person = model.addPerson(Location.Internal, "Some internal user", "Some description"); assertEquals(1, model.getPeople().size()); @@ -90,7 +90,7 @@ void test_addPerson_AddsThePerson_WhenAPersonDoesNotExistWithTheSameName() { } @Test - void test_addPerson_ThrowsAnException_WhenAPersonExistsWithTheSameName() { + void addPerson_ThrowsAnException_WhenAPersonExistsWithTheSameName() { Person person = model.addPerson(Location.Internal, "Admin User", "Description"); assertEquals(1, model.getPeople().size()); @@ -103,7 +103,7 @@ void test_addPerson_ThrowsAnException_WhenAPersonExistsWithTheSameName() { } @Test - void test_addPerson_AddsThePersonWithoutSpecifyingTheLocation_WhenAPersonDoesNotExistWithTheSameName() { + void addPerson_AddsThePersonWithoutSpecifyingTheLocation_WhenAPersonDoesNotExistWithTheSameName() { assertTrue(model.getPeople().isEmpty()); Person person = model.addPerson("Some internal user", "Some description"); assertEquals(1, model.getPeople().size()); @@ -116,42 +116,42 @@ void test_addPerson_AddsThePersonWithoutSpecifyingTheLocation_WhenAPersonDoesNot } @Test - void test_getElement_ReturnsNull_WhenAnElementWithTheSpecifiedIdDoesNotExist() { + void getElement_ReturnsNull_WhenAnElementWithTheSpecifiedIdDoesNotExist() { assertNull(model.getElement("100")); } @Test - void test_getElement_ReturnsAnElement_WhenAnElementWithTheSpecifiedIdDoesExist() { + void getElement_ReturnsAnElement_WhenAnElementWithTheSpecifiedIdDoesExist() { Person person = model.addPerson(Location.Internal, "Name", "Description"); assertSame(person, model.getElement(person.getId())); } @Test - void test_contains_ReturnsFalse_WhenTheSpecifiedElementIsNotInTheModel() { + void contains_ReturnsFalse_WhenTheSpecifiedElementIsNotInTheModel() { Model newModel = new Model(); SoftwareSystem softwareSystem = newModel.addSoftwareSystem(Location.Unspecified, "Name", "Description"); assertFalse(model.contains(softwareSystem)); } @Test - void test_contains_ReturnsTrue_WhenTheSpecifiedElementIsInTheModel() { + void contains_ReturnsTrue_WhenTheSpecifiedElementIsInTheModel() { SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Unspecified, "Name", "Description"); assertTrue(model.contains(softwareSystem)); } @Test - void test_getSoftwareSystemWithName_ReturnsNull_WhenASoftwareSystemWithTheSpecifiedNameDoesNotExist() { + void getSoftwareSystemWithName_ReturnsNull_WhenASoftwareSystemWithTheSpecifiedNameDoesNotExist() { assertNull(model.getSoftwareSystemWithName("System X")); } @Test - void test_getSoftwareSystemWithName_ReturnsASoftwareSystem_WhenASoftwareSystemWithTheSpecifiedNameExists() { + void getSoftwareSystemWithName_ReturnsASoftwareSystem_WhenASoftwareSystemWithTheSpecifiedNameExists() { SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.External, "System A", "Description"); assertSame(softwareSystem, model.getSoftwareSystemWithName("System A")); } @Test - void test_getSoftwareSystemWithId_ThrowsAnException_WhenPassedANullId() { + void getSoftwareSystemWithId_ThrowsAnException_WhenPassedANullId() { try { model.getSoftwareSystemWithId(null); fail(); @@ -161,7 +161,7 @@ void test_getSoftwareSystemWithId_ThrowsAnException_WhenPassedANullId() { } @Test - void test_getSoftwareSystemWithId_ThrowsAnException_WhenPassedAnEmptyId() { + void getSoftwareSystemWithId_ThrowsAnException_WhenPassedAnEmptyId() { try { model.getSoftwareSystemWithId(" "); fail(); @@ -171,29 +171,29 @@ void test_getSoftwareSystemWithId_ThrowsAnException_WhenPassedAnEmptyId() { } @Test - void test_getSoftwareSystemWithId_ReturnsNull_WhenASoftwareSystemWithTheSpecifiedIdDoesNotExist() { + void getSoftwareSystemWithId_ReturnsNull_WhenASoftwareSystemWithTheSpecifiedIdDoesNotExist() { assertNull(model.getSoftwareSystemWithId("100")); } @Test - void test_getSoftwareSystemWithId_ReturnsASoftwareSystem_WhenASoftwareSystemWithTheSpecifiedIdDoesExist() { + void getSoftwareSystemWithId_ReturnsASoftwareSystem_WhenASoftwareSystemWithTheSpecifiedIdDoesExist() { SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.External, "System A", "Description"); assertSame(softwareSystem, model.getSoftwareSystemWithId(softwareSystem.getId())); } @Test - void test_getPersonWithName_ReturnsNull_WhenAPersonWithTheSpecifiedNameDoesNotExist() { + void getPersonWithName_ReturnsNull_WhenAPersonWithTheSpecifiedNameDoesNotExist() { assertNull(model.getPersonWithName("Admin User")); } @Test - void test_getPersonWithName_ReturnsAPerson_WhenAPersonWithTheSpecifiedNameExists() { + void getPersonWithName_ReturnsAPerson_WhenAPersonWithTheSpecifiedNameExists() { Person person = model.addPerson(Location.External, "Admin User", "Description"); assertSame(person, model.getPersonWithName("Admin User")); } @Test - void test_getRelationship_ThrowsAnException_WhenPassedANullId() { + void getRelationship_ThrowsAnException_WhenPassedANullId() { try { model.getRelationship(null); fail(); @@ -203,7 +203,7 @@ void test_getRelationship_ThrowsAnException_WhenPassedANullId() { } @Test - void test_getRelationship_ThrowsAnException_WhenPassedAnEmptyId() { + void getRelationship_ThrowsAnException_WhenPassedAnEmptyId() { try { model.getRelationship(" "); fail(); @@ -213,7 +213,7 @@ void test_getRelationship_ThrowsAnException_WhenPassedAnEmptyId() { } @Test - void test_addRelationship_AddsARelationshipWithTheSpecifiedDescriptionAndTechnologyAndInteractionStyle() { + void addRelationship_AddsARelationshipWithTheSpecifiedDescriptionAndTechnologyAndInteractionStyle() { SoftwareSystem a = model.addSoftwareSystem("A", ""); SoftwareSystem b = model.addSoftwareSystem("B", ""); Relationship relationship = model.addRelationship(a, b, "Uses", "HTTPS", InteractionStyle.Asynchronous); @@ -228,7 +228,7 @@ void test_addRelationship_AddsARelationshipWithTheSpecifiedDescriptionAndTechnol } @Test - void test_addRelationship_DisallowsTheSameRelationshipToBeAddedMoreThanOnce() { + void addRelationship_DisallowsTheSameRelationshipToBeAddedMoreThanOnce() { SoftwareSystem element1 = model.addSoftwareSystem("Element 1", "Description"); SoftwareSystem element2 = model.addSoftwareSystem("Element 2", "Description"); Relationship relationship1 = element1.uses(element2, "Uses", ""); @@ -239,7 +239,7 @@ void test_addRelationship_DisallowsTheSameRelationshipToBeAddedMoreThanOnce() { } @Test - void test_addRelationship_AllowsMultipleRelationshipsBetweenElements() { + void addRelationship_AllowsMultipleRelationshipsBetweenElements() { SoftwareSystem element1 = model.addSoftwareSystem("Element 1", "Description"); SoftwareSystem element2 = model.addSoftwareSystem("Element 2", "Description"); Relationship relationship1 = element1.uses(element2, "Uses in some way", ""); @@ -250,7 +250,7 @@ void test_addRelationship_AllowsMultipleRelationshipsBetweenElements() { } @Test - void test_addRelationship_ThrowsAnException_WhenTheDestinationIsAChildOfTheSource() { + void addRelationship_ThrowsAnException_WhenTheDestinationIsAChildOfTheSource() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); Container container = softwareSystem.addContainer("Container", "", ""); Component component = container.addComponent("Component", "", ""); @@ -281,7 +281,7 @@ void test_addRelationship_ThrowsAnException_WhenTheDestinationIsAChildOfTheSourc } @Test - void test_addRelationship_ThrowsAnException_WhenTheSourceIsAChildOfTheDestination() { + void addRelationship_ThrowsAnException_WhenTheSourceIsAChildOfTheDestination() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); Container container = softwareSystem.addContainer("Container", "", ""); Component component = container.addComponent("Component", "", ""); @@ -312,7 +312,7 @@ void test_addRelationship_ThrowsAnException_WhenTheSourceIsAChildOfTheDestinatio } @Test - void test_modifyRelationship_ThrowsAnException_WhenARelationshipIsNotSpecified() { + void modifyRelationship_ThrowsAnException_WhenARelationshipIsNotSpecified() { try { model.modifyRelationship(null, "Uses", "Technology"); fail(); @@ -322,7 +322,7 @@ void test_modifyRelationship_ThrowsAnException_WhenARelationshipIsNotSpecified() } @Test - void test_modifyRelationship_ModifiesAnExistingRelationship_WhenThatRelationshipDoesNotAlreadyExist() { + void modifyRelationship_ModifiesAnExistingRelationship_WhenThatRelationshipDoesNotAlreadyExist() { SoftwareSystem element1 = model.addSoftwareSystem("Element 1", "Description"); SoftwareSystem element2 = model.addSoftwareSystem("Element 2", "Description"); Relationship relationship = element1.uses(element2, "", ""); @@ -333,7 +333,7 @@ void test_modifyRelationship_ModifiesAnExistingRelationship_WhenThatRelationship } @Test - void test_modifyRelationship_ThrowsAnException_WhenThatRelationshipDoesAlreadyExist() { + void modifyRelationship_ThrowsAnException_WhenThatRelationshipDoesAlreadyExist() { SoftwareSystem element1 = model.addSoftwareSystem("Element 1", "Description"); SoftwareSystem element2 = model.addSoftwareSystem("Element 2", "Description"); Relationship relationship = element1.uses(element2, "Uses", "Technology"); @@ -347,7 +347,7 @@ void test_modifyRelationship_ThrowsAnException_WhenThatRelationshipDoesAlreadyEx } @Test - void test_addSoftwareSystemInstance_ThrowsAnException_WhenANullSoftwareSystemIsSpecified() { + void addSoftwareSystemInstance_ThrowsAnException_WhenANullSoftwareSystemIsSpecified() { try { DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node", "Description", "Technology"); deploymentNode.add((SoftwareSystem) null); @@ -358,7 +358,7 @@ void test_addSoftwareSystemInstance_ThrowsAnException_WhenANullSoftwareSystemIsS } @Test - void test_addContainerInstance_ThrowsAnException_WhenANullContainerIsSpecified() { + void addContainerInstance_ThrowsAnException_WhenANullContainerIsSpecified() { try { DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node", "Description", "Technology"); deploymentNode.add((Container) null); @@ -369,7 +369,7 @@ void test_addContainerInstance_ThrowsAnException_WhenANullContainerIsSpecified() } @Test - void test_addDeploymentNode_AddsADeploymentNode_WhenADeploymentEnvironmentIsNotSpecified() { + void addDeploymentNode_AddsADeploymentNode_WhenADeploymentEnvironmentIsNotSpecified() { DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node", "Description", "Technology"); assertEquals("Deployment Node", deploymentNode.getName()); @@ -379,7 +379,7 @@ void test_addDeploymentNode_AddsADeploymentNode_WhenADeploymentEnvironmentIsNotS } @Test - void test_addDeploymentNode_AddsADeploymentNode_WhenADeploymentEnvironmentIsSpecified() { + void addDeploymentNode_AddsADeploymentNode_WhenADeploymentEnvironmentIsSpecified() { DeploymentNode deploymentNode = model.addDeploymentNode("Development", "Deployment Node", "Description", "Technology"); assertEquals("Deployment Node", deploymentNode.getName()); @@ -389,7 +389,7 @@ void test_addDeploymentNode_AddsADeploymentNode_WhenADeploymentEnvironmentIsSpec } @Test - void test_addElementInstance_AddsElementInstancesAndReplicatesRelationshipsWithinTheDeploymentEnvironment() { + void addElementInstance_AddsElementInstancesAndReplicatesRelationshipsWithinTheDeploymentEnvironment() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1", "Description"); Container container1 = softwareSystem1.addContainer("Container 1", "Description", "Technology"); @@ -447,7 +447,7 @@ void test_addElementInstance_AddsElementInstancesAndReplicatesRelationshipsWithi } @Test - void test_addElementInstance_AddsElementInstancesAndReplicatesRelationshipsWithinTheDeploymentEnvironmentAndDefaultGroup() { + void addElementInstance_AddsElementInstancesAndReplicatesRelationshipsWithinTheDeploymentEnvironmentAndDefaultGroup() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System"); Container api = softwareSystem1.addContainer("API"); Container database = softwareSystem1.addContainer("Database"); @@ -481,7 +481,7 @@ void test_addElementInstance_AddsElementInstancesAndReplicatesRelationshipsWithi } @Test - void test_addElementInstance_AddsElementInstancesAndReplicatesRelationshipsWithinTheDeploymentEnvironmentAndSpecifiedGroup() { + void addElementInstance_AddsElementInstancesAndReplicatesRelationshipsWithinTheDeploymentEnvironmentAndSpecifiedGroup() { // in this test, container instances are added to two deployment groups: "Instance 1" and "Instance 2" // relationships are not replicated between element instances in other groups @@ -510,7 +510,7 @@ void test_addElementInstance_AddsElementInstancesAndReplicatesRelationshipsWithi } @Test - void test_addElementInstance_AddsElementInstancesAndReplicatesRelationshipsWithinTheDeploymentEnvironmentAndSpecifiedGroups() { + void addElementInstance_AddsElementInstancesAndReplicatesRelationshipsWithinTheDeploymentEnvironmentAndSpecifiedGroups() { // in this test: // - API container instances are added to "Instance 1", "Instance 2" and "Shared" // - database container instances are added to "Instance 1" and "Instance 2" @@ -550,7 +550,7 @@ void test_addElementInstance_AddsElementInstancesAndReplicatesRelationshipsWithi } @Test - void test_getElement_ThrowsAnException_WhenANullIdIsSpecified() { + void getElement_ThrowsAnException_WhenANullIdIsSpecified() { try { model.getElement(null); } catch (IllegalArgumentException iae) { @@ -559,7 +559,7 @@ void test_getElement_ThrowsAnException_WhenANullIdIsSpecified() { } @Test - void test_getElement_ThrowsAnException_WhenAnEmptyIdIsSpecified() { + void getElement_ThrowsAnException_WhenAnEmptyIdIsSpecified() { try { model.getElement(" "); } catch (IllegalArgumentException iae) { @@ -568,7 +568,7 @@ void test_getElement_ThrowsAnException_WhenAnEmptyIdIsSpecified() { } @Test - void test_getElementWithCanonicalName_ThrowsAnException_WhenANullCanonicalNameIsSpecified() { + void getElementWithCanonicalName_ThrowsAnException_WhenANullCanonicalNameIsSpecified() { try { model.getElementWithCanonicalName(null); } catch (IllegalArgumentException iae) { @@ -577,7 +577,7 @@ void test_getElementWithCanonicalName_ThrowsAnException_WhenANullCanonicalNameIs } @Test - void test_getElementWithCanonicalName_ThrowsAnException_WhenAnEmptyCanonicalNameIsSpecified() { + void getElementWithCanonicalName_ThrowsAnException_WhenAnEmptyCanonicalNameIsSpecified() { try { model.getElementWithCanonicalName(" "); } catch (IllegalArgumentException iae) { @@ -586,12 +586,12 @@ void test_getElementWithCanonicalName_ThrowsAnException_WhenAnEmptyCanonicalName } @Test - void test_getElementWithCanonicalName_ReturnsNull_WhenAnElementWithTheSpecifiedCanonicalNameDoesNotExist() { + void getElementWithCanonicalName_ReturnsNull_WhenAnElementWithTheSpecifiedCanonicalNameDoesNotExist() { assertNull(model.getElementWithCanonicalName("Software System")); } @Test - void test_getElementWithCanonicalName_ReturnsTheElement_WhenAnElementWithTheSpecifiedCanonicalNameExists() { + void getElementWithCanonicalName_ReturnsTheElement_WhenAnElementWithTheSpecifiedCanonicalNameExists() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); Container container = softwareSystem.addContainer("Web Application", "Description", "Technology"); @@ -600,7 +600,7 @@ void test_getElementWithCanonicalName_ReturnsTheElement_WhenAnElementWithTheSpec } @Test - void test_addDeploymentNode_ThrowsAnException_WhenADeploymentNodeWithTheSameNameAlreadyExists() { + void addDeploymentNode_ThrowsAnException_WhenADeploymentNodeWithTheSameNameAlreadyExists() { model.addDeploymentNode("Amazon AWS", "Description", "Technology"); try { model.addDeploymentNode("Amazon AWS", "Description", "Technology"); @@ -611,7 +611,7 @@ void test_addDeploymentNode_ThrowsAnException_WhenADeploymentNodeWithTheSameName } @Test - void test_addDeploymentNode_ThrowsAnException_WhenAChildDeploymentNodeWithTheSameNameAlreadyExists() { + void addDeploymentNode_ThrowsAnException_WhenAChildDeploymentNodeWithTheSameNameAlreadyExists() { DeploymentNode deploymentNode = model.addDeploymentNode("Amazon Web Services"); deploymentNode.addDeploymentNode("AWS Region"); try { @@ -623,7 +623,7 @@ void test_addDeploymentNode_ThrowsAnException_WhenAChildDeploymentNodeWithTheSam } @Test - void test_addDeploymentNode_ThrowsAnException_WhenAChildInfrastructureNodeWithTheSameNameAlreadyExists() { + void addDeploymentNode_ThrowsAnException_WhenAChildInfrastructureNodeWithTheSameNameAlreadyExists() { DeploymentNode deploymentNode = model.addDeploymentNode("Amazon Web Services"); deploymentNode.addInfrastructureNode("Node"); try { @@ -635,7 +635,7 @@ void test_addDeploymentNode_ThrowsAnException_WhenAChildInfrastructureNodeWithTh } @Test - void test_addInfrastructureNode_ThrowsAnException_WhenAChildDeploymentNodeWithTheSameNameAlreadyExists() { + void addInfrastructureNode_ThrowsAnException_WhenAChildDeploymentNodeWithTheSameNameAlreadyExists() { DeploymentNode deploymentNode = model.addDeploymentNode("Amazon Web Services"); deploymentNode.addDeploymentNode("Node"); try { @@ -647,7 +647,7 @@ void test_addInfrastructureNode_ThrowsAnException_WhenAChildDeploymentNodeWithTh } @Test - void test_addInfrastructureNode_ThrowsAnException_WhenAChildInfrastructureNodeWithTheSameNameAlreadyExists() { + void addInfrastructureNode_ThrowsAnException_WhenAChildInfrastructureNodeWithTheSameNameAlreadyExists() { DeploymentNode deploymentNode = model.addDeploymentNode("Amazon Web Services"); deploymentNode.addInfrastructureNode("Node"); try { @@ -659,7 +659,7 @@ void test_addInfrastructureNode_ThrowsAnException_WhenAChildInfrastructureNodeWi } @Test - void test_setIdGenerator_ThrowsAnException_WhenANullIdGeneratorIsSpecified() { + void setIdGenerator_ThrowsAnException_WhenANullIdGeneratorIsSpecified() { try { model.setIdGenerator(null); fail(); @@ -669,7 +669,7 @@ void test_setIdGenerator_ThrowsAnException_WhenANullIdGeneratorIsSpecified() { } @Test - void test_hydrate() { + void hydrate() { Person person = new Person(); person.setId("1"); person.setName("Person"); @@ -749,7 +749,7 @@ void test_hydrate() { } @Test - void test_impliedRelationshipStrategy() { + void impliedRelationshipStrategy() { // default strategy initially assertTrue(model.getImpliedRelationshipsStrategy() instanceof DefaultImpliedRelationshipsStrategy); @@ -758,7 +758,7 @@ void test_impliedRelationshipStrategy() { } @Test - void test_setImpliedRelationshipStrategy_ResetsToTheDefaultStrategy_WhenPassedNull() { + void setImpliedRelationshipStrategy_ResetsToTheDefaultStrategy_WhenPassedNull() { model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); model.setImpliedRelationshipsStrategy(null); @@ -766,7 +766,7 @@ void test_setImpliedRelationshipStrategy_ResetsToTheDefaultStrategy_WhenPassedNu } @Test - void test_addSoftwareSystemInstance_AllocatesInstanceIdsPerDeploymentNode() { + void addSoftwareSystemInstance_AllocatesInstanceIdsPerDeploymentNode() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); DeploymentNode deploymentNodeA = model.addDeploymentNode("Deployment Node A", "", ""); DeploymentNode deploymentNodeB = model.addDeploymentNode("Deployment Node B", "", ""); @@ -782,7 +782,7 @@ void test_addSoftwareSystemInstance_AllocatesInstanceIdsPerDeploymentNode() { } @Test - void test_addContainerInstance_AllocatesInstanceIdsPerDeploymentNode() { + void addContainerInstance_AllocatesInstanceIdsPerDeploymentNode() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); Container container = softwareSystem.addContainer("Container", "", ""); DeploymentNode deploymentNodeA = model.addDeploymentNode("Deployment Node A", "", ""); diff --git a/structurizr-core/test/unit/com/structurizr/model/PersonTests.java b/structurizr-core/test/unit/com/structurizr/model/PersonTests.java index 6334a04f9..4a7460364 100644 --- a/structurizr-core/test/unit/com/structurizr/model/PersonTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/PersonTests.java @@ -8,26 +8,26 @@ public class PersonTests extends AbstractWorkspaceTestBase { @Test - void test_getCanonicalName() { + void getCanonicalName() { Person person = model.addPerson("Person", "Description"); assertEquals("Person://Person", person.getCanonicalName()); } @Test - void test_getCanonicalName_WhenNameContainsSlashAndDotCharacters() { + void getCanonicalName_WhenNameContainsSlashAndDotCharacters() { Person person = model.addPerson("Person", "Description"); person.setName("Name1/.Name2"); assertEquals("Person://Name1Name2", person.getCanonicalName()); } @Test - void test_getParent_ReturnsNull() { + void getParent_ReturnsNull() { Person person = model.addPerson("Person", "Description"); assertNull(person.getParent()); } @Test - void test_removeTags_DoesNotRemoveRequiredTags() { + void removeTags_DoesNotRemoveRequiredTags() { Person person = model.addPerson("Person", "Description"); assertTrue(person.getTags().contains(Tags.ELEMENT)); assertTrue(person.getTags().contains(Tags.PERSON)); @@ -40,7 +40,7 @@ void test_removeTags_DoesNotRemoveRequiredTags() { } @Test - void test_interactsWith_AddsARelationshipWhenTheDescriptionIsSpecified() { + void interactsWith_AddsARelationshipWhenTheDescriptionIsSpecified() { Person person1 = model.addPerson("Person 1", "Description"); Person person2 = model.addPerson("Person 2", "Description"); @@ -56,7 +56,7 @@ void test_interactsWith_AddsARelationshipWhenTheDescriptionIsSpecified() { } @Test - void test_interactsWith_AddsARelationshipWhenTheDescriptionAndTechnologyAreSpecified() { + void interactsWith_AddsARelationshipWhenTheDescriptionAndTechnologyAreSpecified() { Person person1 = model.addPerson("Person 1", "Description"); Person person2 = model.addPerson("Person 2", "Description"); @@ -72,7 +72,7 @@ void test_interactsWith_AddsARelationshipWhenTheDescriptionAndTechnologyAreSpeci } @Test - void test_interactsWith_AddsARelationshipWhenTheDescriptionAndTechnologyAndInteractionStyleAreSpecified() { + void interactsWith_AddsARelationshipWhenTheDescriptionAndTechnologyAndInteractionStyleAreSpecified() { Person person1 = model.addPerson("Person 1", "Description"); Person person2 = model.addPerson("Person 2", "Description"); @@ -88,7 +88,7 @@ void test_interactsWith_AddsARelationshipWhenTheDescriptionAndTechnologyAndInter } @Test - void test_setLocation_SetsTheLocationToUnspecified_WhenNullIsPassed() { + void setLocation_SetsTheLocationToUnspecified_WhenNullIsPassed() { Person person = model.addPerson("Person", "Description"); person.setLocation(null); assertEquals(Location.Unspecified, person.getLocation()); diff --git a/structurizr-core/test/unit/com/structurizr/model/RelationshipTests.java b/structurizr-core/test/unit/com/structurizr/model/RelationshipTests.java index c854ff1cb..f8edd18ec 100644 --- a/structurizr-core/test/unit/com/structurizr/model/RelationshipTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/RelationshipTests.java @@ -17,26 +17,26 @@ public void setUp() { } @Test - void test_getDescription_NeverReturnsNull() { + void getDescription_NeverReturnsNull() { Relationship relationship = softwareSystem1.uses(softwareSystem2, null); assertEquals("", relationship.getDescription()); } @Test - void test_getTags_WhenThereAreNoTags() { + void getTags_WhenThereAreNoTags() { Relationship relationship = softwareSystem1.uses(softwareSystem2, "uses"); assertEquals("Relationship", relationship.getTags()); } @Test - void test_getTags_ReturnsTheListOfTags_WhenThereAreSomeTags() { + void getTags_ReturnsTheListOfTags_WhenThereAreSomeTags() { Relationship relationship = softwareSystem1.uses(softwareSystem2, "uses"); relationship.addTags("tag1", "tag2", "tag3"); assertEquals("Relationship,tag1,tag2,tag3", relationship.getTags()); } @Test - void test_setTags_ClearsTheTags_WhenPassedNull() { + void setTags_ClearsTheTags_WhenPassedNull() { Relationship relationship = softwareSystem1.uses(softwareSystem2, "uses"); relationship.addTags("Tag 1", "Tag 2"); assertEquals("Relationship,Tag 1,Tag 2", relationship.getTags()); @@ -45,7 +45,7 @@ void test_setTags_ClearsTheTags_WhenPassedNull() { } @Test - void test_addTags_DoesNotDoAnything_WhenPassedNull() { + void addTags_DoesNotDoAnything_WhenPassedNull() { Relationship relationship = softwareSystem1.uses(softwareSystem2, "uses"); relationship.addTags((String) null); assertEquals("Relationship", relationship.getTags()); @@ -55,20 +55,20 @@ void test_addTags_DoesNotDoAnything_WhenPassedNull() { } @Test - void test_addTags_AddsTags_WhenPassedSomeTags() { + void addTags_AddsTags_WhenPassedSomeTags() { Relationship relationship = softwareSystem1.uses(softwareSystem2, "uses"); relationship.addTags(null, "tag1", null, "tag2"); assertEquals("Relationship,tag1,tag2", relationship.getTags()); } @Test - void test_getInteractionStyle_ReturnsNull_WhenNotExplicitlySet() { + void getInteractionStyle_ReturnsNull_WhenNotExplicitlySet() { Relationship relationship = softwareSystem1.uses(softwareSystem2, "uses"); assertNull(relationship.getInteractionStyle()); } @Test - void test_getTags_IncludesTheInteractionStyleWhenSpecified() { + void getTags_IncludesTheInteractionStyleWhenSpecified() { Relationship relationship = softwareSystem1.uses(softwareSystem2, "Uses 1", "Technology"); assertFalse(relationship.getTags().contains(Tags.SYNCHRONOUS)); assertFalse(relationship.getTags().contains(Tags.ASYNCHRONOUS)); @@ -83,14 +83,14 @@ void test_getTags_IncludesTheInteractionStyleWhenSpecified() { } @Test - void test_setUrl() { + void setUrl() { Relationship relationship = softwareSystem1.uses(softwareSystem2, "Uses 1", "Technology"); relationship.setUrl("https://structurizr.com"); assertEquals("https://structurizr.com", relationship.getUrl()); } @Test - void test_setUrl_ThrowsAnIllegalArgumentException_WhenAnInvalidUrlIsSpecified() { + void setUrl_ThrowsAnIllegalArgumentException_WhenAnInvalidUrlIsSpecified() { assertThrows(IllegalArgumentException.class, () -> { Relationship relationship = softwareSystem1.uses(softwareSystem2, "Uses 1", "Technology"); relationship.setUrl("htt://blah"); @@ -98,7 +98,7 @@ void test_setUrl_ThrowsAnIllegalArgumentException_WhenAnInvalidUrlIsSpecified() } @Test - void test_setUrl_ResetsTheUrl_WhenANullUrlIsSpecified() { + void setUrl_ResetsTheUrl_WhenANullUrlIsSpecified() { Relationship relationship = softwareSystem1.uses(softwareSystem2, "Uses 1", "Technology"); relationship.setUrl("https://structurizr.com"); relationship.setUrl(null); @@ -106,7 +106,7 @@ void test_setUrl_ResetsTheUrl_WhenANullUrlIsSpecified() { } @Test - void test_setUrl_ResetsTheUrl_WhenAnEmptyUrlIsSpecified() { + void setUrl_ResetsTheUrl_WhenAnEmptyUrlIsSpecified() { Relationship relationship = softwareSystem1.uses(softwareSystem2, "Uses 1", "Technology"); relationship.setUrl("https://structurizr.com"); relationship.setUrl(" "); @@ -114,7 +114,7 @@ void test_setUrl_ResetsTheUrl_WhenAnEmptyUrlIsSpecified() { } @Test - void test_interactionStyle_CanBeSetToNull() { + void interactionStyle_CanBeSetToNull() { Relationship relationship = softwareSystem1.uses(softwareSystem2, "Uses 1", "Technology", null); assertNull(relationship.getInteractionStyle()); diff --git a/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemInstanceTests.java b/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemInstanceTests.java index 0e7c352ec..e7019e231 100644 --- a/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemInstanceTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemInstanceTests.java @@ -11,7 +11,7 @@ public class SoftwareSystemInstanceTests extends AbstractWorkspaceTestBase { private DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node", "Description", "Technology"); @Test - void test_construction() { + void construction() { SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); assertSame(softwareSystem, instance.getSoftwareSystem()); @@ -20,7 +20,7 @@ void test_construction() { } @Test - void test_getSoftwareSystemId() { + void getSoftwareSystemId() { SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); assertEquals(softwareSystem.getId(), instance.getSoftwareSystemId()); @@ -30,7 +30,7 @@ void test_getSoftwareSystemId() { } @Test - void test_getName_CannotBeChanged() { + void getName_CannotBeChanged() { SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); assertEquals("System", instance.getName()); @@ -40,28 +40,28 @@ void test_getName_CannotBeChanged() { } @Test - void test_getCanonicalName() { + void getCanonicalName() { SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); assertEquals("SoftwareSystemInstance://Default/Deployment Node/System[1]", instance.getCanonicalName()); } @Test - void test_getParent_ReturnsTheParentDeploymentNode() { + void getParent_ReturnsTheParentDeploymentNode() { SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); assertEquals(deploymentNode, instance.getParent()); } @Test - void test_getRequiredTags() { + void getRequiredTags() { SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); assertTrue(instance.getDefaultTags().isEmpty()); } @Test - void test_getTags() { + void getTags() { softwareSystem.addTags("Tag 1"); SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); instance.addTags("Primary Instance"); @@ -70,7 +70,7 @@ void test_getTags() { } @Test - void test_removeTags_DoesNotRemoveAnyTags() { + void removeTags_DoesNotRemoveAnyTags() { SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); assertTrue(instance.getTags().contains(Tags.SOFTWARE_SYSTEM_INSTANCE)); @@ -81,7 +81,7 @@ void test_removeTags_DoesNotRemoveAnyTags() { } @Test - void test_addHealthCheck() { + void addHealthCheck() { SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); assertTrue(instance.getHealthChecks().isEmpty()); @@ -94,7 +94,7 @@ void test_addHealthCheck() { } @Test - void test_addHealthCheck_ThrowsAnException_WhenTheNameIsNull() { + void addHealthCheck_ThrowsAnException_WhenTheNameIsNull() { SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); try { @@ -106,7 +106,7 @@ void test_addHealthCheck_ThrowsAnException_WhenTheNameIsNull() { } @Test - void test_addHealthCheck_ThrowsAnException_WhenTheNameIsEmpty() { + void addHealthCheck_ThrowsAnException_WhenTheNameIsEmpty() { SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); try { @@ -118,7 +118,7 @@ void test_addHealthCheck_ThrowsAnException_WhenTheNameIsEmpty() { } @Test - void test_addHealthCheck_ThrowsAnException_WhenTheUrlIsNull() { + void addHealthCheck_ThrowsAnException_WhenTheUrlIsNull() { SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); try { @@ -130,7 +130,7 @@ void test_addHealthCheck_ThrowsAnException_WhenTheUrlIsNull() { } @Test - void test_addHealthCheck_ThrowsAnException_WhenTheUrlIsEmpty() { + void addHealthCheck_ThrowsAnException_WhenTheUrlIsEmpty() { SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); try { @@ -142,7 +142,7 @@ void test_addHealthCheck_ThrowsAnException_WhenTheUrlIsEmpty() { } @Test - void test_addHealthCheck_ThrowsAnException_WhenTheUrlIsInvalid() { + void addHealthCheck_ThrowsAnException_WhenTheUrlIsInvalid() { SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); try { @@ -154,7 +154,7 @@ void test_addHealthCheck_ThrowsAnException_WhenTheUrlIsInvalid() { } @Test - void test_addHealthCheck_ThrowsAnException_WhenTheIntervalIsLessThanZero() { + void addHealthCheck_ThrowsAnException_WhenTheIntervalIsLessThanZero() { SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); try { @@ -166,7 +166,7 @@ void test_addHealthCheck_ThrowsAnException_WhenTheIntervalIsLessThanZero() { } @Test - void test_addHealthCheck_ThrowsAnException_WhenTheTimeoutIsLessThanZero() { + void addHealthCheck_ThrowsAnException_WhenTheTimeoutIsLessThanZero() { SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); try { @@ -178,7 +178,7 @@ void test_addHealthCheck_ThrowsAnException_WhenTheTimeoutIsLessThanZero() { } @Test - void test_getDeploymentGroups_WhenNoGroupsHaveBeenSpecified() { + void getDeploymentGroups_WhenNoGroupsHaveBeenSpecified() { SoftwareSystemInstance instance = deploymentNode.add(softwareSystem); assertEquals(1, instance.getDeploymentGroups().size()); @@ -186,7 +186,7 @@ void test_getDeploymentGroups_WhenNoGroupsHaveBeenSpecified() { } @Test - void test_getDeploymentGroups_WhenOneGroupHasBeenSpecified() { + void getDeploymentGroups_WhenOneGroupHasBeenSpecified() { SoftwareSystemInstance instance = deploymentNode.add(softwareSystem, "Group 1"); assertEquals(1, instance.getDeploymentGroups().size()); @@ -194,7 +194,7 @@ void test_getDeploymentGroups_WhenOneGroupHasBeenSpecified() { } @Test - void test_getDeploymentGroups_WhenMultipleGroupsAreSpecified() { + void getDeploymentGroups_WhenMultipleGroupsAreSpecified() { SoftwareSystemInstance instance = deploymentNode.add(softwareSystem, "Group 1", "Group 2"); assertEquals(2, instance.getDeploymentGroups().size()); diff --git a/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemTests.java b/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemTests.java index 415172f98..03f0e9c2d 100644 --- a/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemTests.java @@ -12,21 +12,21 @@ public class SoftwareSystemTests extends AbstractWorkspaceTestBase { private SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "Name", "Description"); @Test - void test_addContainer_ThrowsAnException_WhenANullNameIsSpecified() { + void addContainer_ThrowsAnException_WhenANullNameIsSpecified() { assertThrows(IllegalArgumentException.class, () -> { softwareSystem.addContainer(null, "", ""); }); } @Test - void test_addContainer_ThrowsAnException_WhenAnEmptyNameIsSpecified() { + void addContainer_ThrowsAnException_WhenAnEmptyNameIsSpecified() { assertThrows(IllegalArgumentException.class, () -> { softwareSystem.addContainer(" ", "", ""); }); } @Test - void test_addContainer_AddsAContainer_WhenAContainerWithTheSameNameDoesNotExist() { + void addContainer_AddsAContainer_WhenAContainerWithTheSameNameDoesNotExist() { Container container = softwareSystem.addContainer("Web Application", "Description", "Spring MVC"); assertEquals("Web Application", container.getName()); assertEquals("Description", container.getDescription()); @@ -37,7 +37,7 @@ void test_addContainer_AddsAContainer_WhenAContainerWithTheSameNameDoesNotExist( } @Test - void test_addContainer_ThrowsAnException_WhenAContainerWithTheSameNameAlreadyExists() { + void addContainer_ThrowsAnException_WhenAContainerWithTheSameNameAlreadyExists() { Container container = softwareSystem.addContainer("Web Application", "Description", "Spring MVC"); assertEquals(1, softwareSystem.getContainers().size()); @@ -50,29 +50,29 @@ void test_addContainer_ThrowsAnException_WhenAContainerWithTheSameNameAlreadyExi } @Test - void test_getContainerWithName_ReturnsNull_WhenAContainerWithTheSpecifiedNameDoesNotExist() { + void getContainerWithName_ReturnsNull_WhenAContainerWithTheSpecifiedNameDoesNotExist() { assertNull(softwareSystem.getContainerWithName("Web Application")); } @Test - void test_GetContainerWithName_ReturnsAContainer_WhenAContainerWithTheSpecifiedNameDoesExist() { + void GetContainerWithName_ReturnsAContainer_WhenAContainerWithTheSpecifiedNameDoesExist() { Container container = softwareSystem.addContainer("Web Application", "Description", "Spring MVC"); assertSame(container, softwareSystem.getContainerWithName("Web Application")); } @Test - void test_getContainerWithId_ReturnsNull_WhenAContainerWithTheSpecifiedIdDoesNotExist() { + void getContainerWithId_ReturnsNull_WhenAContainerWithTheSpecifiedIdDoesNotExist() { assertNull(softwareSystem.getContainerWithId("100")); } @Test - void test_GetContainerWithId_ReturnsAContainer_WhenAContainerWithTheSpecifiedIdDoesExist() { + void GetContainerWithId_ReturnsAContainer_WhenAContainerWithTheSpecifiedIdDoesExist() { Container container = softwareSystem.addContainer("Web Application", "Description", "Spring MVC"); assertSame(container, softwareSystem.getContainerWithId(container.getId())); } @Test - void test_uses_AddsAUnidirectionalRelationshipBetweenTwoSoftwareSystems() { + void uses_AddsAUnidirectionalRelationshipBetweenTwoSoftwareSystems() { SoftwareSystem systemA = model.addSoftwareSystem(Location.Internal, "System A", "Description"); SoftwareSystem systemB = model.addSoftwareSystem(Location.Internal, "System B", "Description"); systemA.uses(systemB, "Gets some data from"); @@ -86,7 +86,7 @@ void test_uses_AddsAUnidirectionalRelationshipBetweenTwoSoftwareSystems() { } @Test - void test_uses_AddsAUnidirectionalRelationshipBetweenTwoSoftwareSystems_WhenADifferentRelationshipAlreadyExists() { + void uses_AddsAUnidirectionalRelationshipBetweenTwoSoftwareSystems_WhenADifferentRelationshipAlreadyExists() { SoftwareSystem systemA = model.addSoftwareSystem(Location.Internal, "System A", "Description"); SoftwareSystem systemB = model.addSoftwareSystem(Location.Internal, "System B", "Description"); systemA.uses(systemB, "Gets data using the REST API"); @@ -106,7 +106,7 @@ void test_uses_AddsAUnidirectionalRelationshipBetweenTwoSoftwareSystems_WhenADif } @Test - void test_uses_DoesNotAddAUnidirectionalRelationshipBetweenTwoSoftwareSystems_WhenTheSameRelationshipAlreadyExists() { + void uses_DoesNotAddAUnidirectionalRelationshipBetweenTwoSoftwareSystems_WhenTheSameRelationshipAlreadyExists() { SoftwareSystem systemA = model.addSoftwareSystem(Location.Internal, "System A", "Description"); SoftwareSystem systemB = model.addSoftwareSystem(Location.Internal, "System B", "Description"); systemA.uses(systemB, "Gets data using the REST API"); @@ -116,7 +116,7 @@ void test_uses_DoesNotAddAUnidirectionalRelationshipBetweenTwoSoftwareSystems_Wh } @Test - void test_delivers_AddsAUnidirectionalRelationshipBetweenASoftwareSystemAndAPerson() { + void delivers_AddsAUnidirectionalRelationshipBetweenASoftwareSystemAndAPerson() { SoftwareSystem system = model.addSoftwareSystem(Location.Internal, "System", "Description"); Person person = model.addPerson(Location.Internal, "User", "Description"); system.delivers(person, "E-mails results to"); @@ -130,7 +130,7 @@ void test_delivers_AddsAUnidirectionalRelationshipBetweenASoftwareSystemAndAPers } @Test - void test_delivers_AddsAUnidirectionalRelationshipBetweenASoftwareSystemAndAPerson_WhenADifferentRelationshipAlreadyExists() { + void delivers_AddsAUnidirectionalRelationshipBetweenASoftwareSystemAndAPerson_WhenADifferentRelationshipAlreadyExists() { SoftwareSystem system = model.addSoftwareSystem(Location.Internal, "System", "Description"); Person person = model.addPerson(Location.Internal, "User", "Description"); system.delivers(person, "E-mails results to"); @@ -151,7 +151,7 @@ void test_delivers_AddsAUnidirectionalRelationshipBetweenASoftwareSystemAndAPers } @Test - void test_delivers_DoesNotAddAUnidirectionalRelationshipBetweenASoftwareSystemAndAPerson_WhenTheSameRelationshipAlreadyExists() { + void delivers_DoesNotAddAUnidirectionalRelationshipBetweenASoftwareSystemAndAPerson_WhenTheSameRelationshipAlreadyExists() { SoftwareSystem system = model.addSoftwareSystem(Location.Internal, "System", "Description"); Person person = model.addPerson(Location.Internal, "User", "Description"); system.delivers(person, "E-mails results to"); @@ -161,30 +161,30 @@ void test_delivers_DoesNotAddAUnidirectionalRelationshipBetweenASoftwareSystemAn } @Test - void test_getTags_IncludesSoftwareSystemByDefault() { + void getTags_IncludesSoftwareSystemByDefault() { SoftwareSystem system = model.addSoftwareSystem(Location.Internal, "System", "Description"); assertEquals("Element,Software System", system.getTags()); } @Test - void test_getCanonicalName() { + void getCanonicalName() { SoftwareSystem system = model.addSoftwareSystem(Location.Internal, "System", "Description"); assertEquals("SoftwareSystem://System", system.getCanonicalName()); } @Test - void test_getCanonicalName_WhenNameContainsSlashAndDotCharacters() { + void getCanonicalName_WhenNameContainsSlashAndDotCharacters() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Name1/.Name2", "Description"); assertEquals("SoftwareSystem://Name1Name2", softwareSystem.getCanonicalName()); } @Test - void test_getParent_ReturnsNull() { + void getParent_ReturnsNull() { assertNull(softwareSystem.getParent()); } @Test - void test_removeTags_DoesNotRemoveRequiredTags() { + void removeTags_DoesNotRemoveRequiredTags() { assertTrue(softwareSystem.getTags().contains(Tags.ELEMENT)); assertTrue(softwareSystem.getTags().contains(Tags.SOFTWARE_SYSTEM)); @@ -196,7 +196,7 @@ void test_removeTags_DoesNotRemoveRequiredTags() { } @Test - void test_getContainerWithName_ThrowsAnException_WhenANullNameIsSpecified() { + void getContainerWithName_ThrowsAnException_WhenANullNameIsSpecified() { try { softwareSystem.getContainerWithName(null); fail(); @@ -206,7 +206,7 @@ void test_getContainerWithName_ThrowsAnException_WhenANullNameIsSpecified() { } @Test - void test_getContainerWithName_ThrowsAnException_WhenAnEmptyNameIsSpecified() { + void getContainerWithName_ThrowsAnException_WhenAnEmptyNameIsSpecified() { try { softwareSystem.getContainerWithName(" "); fail(); @@ -216,7 +216,7 @@ void test_getContainerWithName_ThrowsAnException_WhenAnEmptyNameIsSpecified() { } @Test - void test_getContainerWithId_ThrowsAnException_WhenANullIdIsSpecified() { + void getContainerWithId_ThrowsAnException_WhenANullIdIsSpecified() { try { softwareSystem.getContainerWithId(null); fail(); @@ -226,7 +226,7 @@ void test_getContainerWithId_ThrowsAnException_WhenANullIdIsSpecified() { } @Test - void test_getContainerWithId_ThrowsAnException_WhenAnEmptyIdIsSpecified() { + void getContainerWithId_ThrowsAnException_WhenAnEmptyIdIsSpecified() { try { softwareSystem.getContainerWithId(" "); fail(); diff --git a/structurizr-core/test/unit/com/structurizr/util/ImageUtilsTests.java b/structurizr-core/test/unit/com/structurizr/util/ImageUtilsTests.java index 2c6c753e6..7d9a25af6 100644 --- a/structurizr-core/test/unit/com/structurizr/util/ImageUtilsTests.java +++ b/structurizr-core/test/unit/com/structurizr/util/ImageUtilsTests.java @@ -9,7 +9,7 @@ public class ImageUtilsTests { @Test - void test_getContentType_ThrowsAnException_WhenANullFileIsSpecified() throws Exception { + void getContentType_ThrowsAnException_WhenANullFileIsSpecified() throws Exception { try { ImageUtils.getContentType(null); fail(); @@ -19,7 +19,7 @@ void test_getContentType_ThrowsAnException_WhenANullFileIsSpecified() throws Exc } @Test - void test_getContentType_ThrowsAnException_WhenAFileIsSpecifiedButItIsNotAFile() throws Exception { + void getContentType_ThrowsAnException_WhenAFileIsSpecifiedButItIsNotAFile() throws Exception { try { ImageUtils.getContentType(new File("../structurizr-core")); fail(); @@ -30,7 +30,7 @@ void test_getContentType_ThrowsAnException_WhenAFileIsSpecifiedButItIsNotAFile() } @Test - void test_getContentType_ThrowsAnException_WhenAFileIsSpecifiedButItIsNotAnImage() throws Exception { + void getContentType_ThrowsAnException_WhenAFileIsSpecifiedButItIsNotAnImage() throws Exception { try { ImageUtils.getContentType(new File("../build.gradle")); fail(); @@ -41,7 +41,7 @@ void test_getContentType_ThrowsAnException_WhenAFileIsSpecifiedButItIsNotAnImage } @Test - void test_getContentType_ThrowsAnException_WhenAFileIsSpecifiedButItDoesNotExist() throws Exception { + void getContentType_ThrowsAnException_WhenAFileIsSpecifiedButItDoesNotExist() throws Exception { try { ImageUtils.getContentType(new File("./foo.xml")); fail(); @@ -51,13 +51,13 @@ void test_getContentType_ThrowsAnException_WhenAFileIsSpecifiedButItDoesNotExist } @Test - void test_getContentType_ReturnsTheContentType_WhenAFileIsSpecified() throws Exception { + void getContentType_ReturnsTheContentType_WhenAFileIsSpecified() throws Exception { String contentType = ImageUtils.getContentType(new File("../structurizr-core/test/unit/com/structurizr/util/structurizr-logo.png")); assertEquals("image/png", contentType); } @Test - void test_getImageAsBase64_ThrowsAnException_WhenANullFileIsSpecified() throws Exception { + void getImageAsBase64_ThrowsAnException_WhenANullFileIsSpecified() throws Exception { try { ImageUtils.getImageAsBase64(null); fail(); @@ -67,7 +67,7 @@ void test_getImageAsBase64_ThrowsAnException_WhenANullFileIsSpecified() throws E } @Test - void test_getImageAsBase64_ThrowsAnException_WhenAFileIsSpecifiedButItIsNotAFile() throws Exception { + void getImageAsBase64_ThrowsAnException_WhenAFileIsSpecifiedButItIsNotAFile() throws Exception { try { ImageUtils.getImageAsBase64(new File("../structurizr-core")); fail(); @@ -78,7 +78,7 @@ void test_getImageAsBase64_ThrowsAnException_WhenAFileIsSpecifiedButItIsNotAFile } @Test - void test_getImageAsBase64_ThrowsAnException_WhenAFileIsSpecifiedButItIsNotAnImage() throws Exception { + void getImageAsBase64_ThrowsAnException_WhenAFileIsSpecifiedButItIsNotAnImage() throws Exception { try { ImageUtils.getImageAsBase64(new File("../build.gradle")); fail(); @@ -89,7 +89,7 @@ void test_getImageAsBase64_ThrowsAnException_WhenAFileIsSpecifiedButItIsNotAnIma } @Test - void test_getImageAsBase64_ThrowsAnException_WhenAFileIsSpecifiedButItDoesNotExist() throws Exception { + void getImageAsBase64_ThrowsAnException_WhenAFileIsSpecifiedButItDoesNotExist() throws Exception { try { ImageUtils.getImageAsBase64(new File("./foo.xml")); fail(); @@ -99,13 +99,13 @@ void test_getImageAsBase64_ThrowsAnException_WhenAFileIsSpecifiedButItDoesNotExi } @Test - void test_getImageAsBase64_ReturnsTheImageAsABase64EncodedString_WhenAFileIsSpecified() throws Exception { + void getImageAsBase64_ReturnsTheImageAsABase64EncodedString_WhenAFileIsSpecified() throws Exception { String imageAsBase64 = ImageUtils.getImageAsBase64(new File("../structurizr-core/test/unit/com/structurizr/util/structurizr-logo.png")); assertTrue(imageAsBase64.startsWith("iVBORw0KGgoAAAANSUhEUgAAAMQAAADECAYAAADApo5rAAA")); // the actual base64 encoded string varies between Java 8 and 9 } @Test - void test_getImageAsDataUri_ThrowsAnException_WhenANullFileIsSpecified() throws Exception { + void getImageAsDataUri_ThrowsAnException_WhenANullFileIsSpecified() throws Exception { try { ImageUtils.getImageAsDataUri(null); fail(); @@ -115,7 +115,7 @@ void test_getImageAsDataUri_ThrowsAnException_WhenANullFileIsSpecified() throws } @Test - void test_getImageAsDataUri_ThrowsAnException_WhenAFileIsSpecifiedButItIsNotAFile() throws Exception { + void getImageAsDataUri_ThrowsAnException_WhenAFileIsSpecifiedButItIsNotAFile() throws Exception { try { ImageUtils.getImageAsDataUri(new File("../structurizr-core")); fail(); @@ -126,7 +126,7 @@ void test_getImageAsDataUri_ThrowsAnException_WhenAFileIsSpecifiedButItIsNotAFil } @Test - void test_getImageAsDataUri_ThrowsAnException_WhenAFileIsSpecifiedButItIsNotAnImage() throws Exception { + void getImageAsDataUri_ThrowsAnException_WhenAFileIsSpecifiedButItIsNotAnImage() throws Exception { try { ImageUtils.getImageAsDataUri(new File("../build.gradle")); fail(); @@ -137,7 +137,7 @@ void test_getImageAsDataUri_ThrowsAnException_WhenAFileIsSpecifiedButItIsNotAnIm } @Test - void test_getImageAsDataUri_ThrowsAnException_WhenAFileIsSpecifiedButItDoesNotExist() throws Exception { + void getImageAsDataUri_ThrowsAnException_WhenAFileIsSpecifiedButItDoesNotExist() throws Exception { try { ImageUtils.getImageAsDataUri(new File("./foo.xml")); fail(); @@ -147,14 +147,14 @@ void test_getImageAsDataUri_ThrowsAnException_WhenAFileIsSpecifiedButItDoesNotEx } @Test - void test_getImageAsDataUri_ReturnsTheImageAsADataUri_WhenAFileIsSpecified() throws Exception { + void getImageAsDataUri_ReturnsTheImageAsADataUri_WhenAFileIsSpecified() throws Exception { String imageAsDataUri = ImageUtils.getImageAsDataUri(new File("../structurizr-core/test/unit/com/structurizr/util/structurizr-logo.png")); System.out.println(imageAsDataUri); assertTrue(imageAsDataUri.startsWith("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMQAAADECAYAAADA")); // the actual base64 encoded string varies between Java 8 and 9 } @Test - void test_validateImage() { + void validateImage() { // allowed ImageUtils.validateImage("https://structurizr.com/image.png"); ImageUtils.validateImage("data:image/png;base64,iVBORw0KGg"); @@ -170,7 +170,7 @@ void test_validateImage() { } @Test - void test_isSupportedDataUri() { + void isSupportedDataUri() { assertTrue(ImageUtils.isSupportedDataUri("data:image/png;base64,iVBORw0KGg")); assertTrue(ImageUtils.isSupportedDataUri("data:image/jpeg;base64,iVBORw0KGg")); assertFalse(ImageUtils.isSupportedDataUri("data:image/svg+xml;base64,iVBORw0KGg")); diff --git a/structurizr-core/test/unit/com/structurizr/util/StringUtilsTests.java b/structurizr-core/test/unit/com/structurizr/util/StringUtilsTests.java index 98bf0a7f6..dda79bdee 100644 --- a/structurizr-core/test/unit/com/structurizr/util/StringUtilsTests.java +++ b/structurizr-core/test/unit/com/structurizr/util/StringUtilsTests.java @@ -8,18 +8,18 @@ public class StringUtilsTests { @Test - void test_isNullOrEmpty_ReturnsTrue_WhenPassedNull() { + void isNullOrEmpty_ReturnsTrue_WhenPassedNull() { assertTrue(StringUtils.isNullOrEmpty(null)); } @Test - void test_isNullOrEmpty_ReturnsTrue_WhenPassedAnEmptyString() { + void isNullOrEmpty_ReturnsTrue_WhenPassedAnEmptyString() { assertTrue(StringUtils.isNullOrEmpty("")); assertTrue(StringUtils.isNullOrEmpty(" ")); } @Test - void test_isNullOrEmpty_ReturnsFalse_WhenPassedANonEmptyString() { + void isNullOrEmpty_ReturnsFalse_WhenPassedANonEmptyString() { assertFalse(StringUtils.isNullOrEmpty("Hello World!")); } diff --git a/structurizr-core/test/unit/com/structurizr/util/UrlTests.java b/structurizr-core/test/unit/com/structurizr/util/UrlTests.java index 70ba4f6db..c9e179095 100644 --- a/structurizr-core/test/unit/com/structurizr/util/UrlTests.java +++ b/structurizr-core/test/unit/com/structurizr/util/UrlTests.java @@ -8,23 +8,23 @@ public class UrlTests { @Test - void test_isUrl_ReturnsFalse_WhenPassedNull() { + void isUrl_ReturnsFalse_WhenPassedNull() { assertFalse(Url.isUrl(null)); } @Test - void test_isUrl_ReturnsFalse_WhenPassedAnEmptyString() { + void isUrl_ReturnsFalse_WhenPassedAnEmptyString() { assertFalse(Url.isUrl("")); assertFalse(Url.isUrl(" ")); } @Test - void test_isUrl_ReturnsFalse_WhenPassedAnInvalidUrl() { + void isUrl_ReturnsFalse_WhenPassedAnInvalidUrl() { assertFalse(Url.isUrl("www.google.com")); } @Test - void test_isUrl_ReturnsTrue_WhenPassedAValidUrl() { + void isUrl_ReturnsTrue_WhenPassedAValidUrl() { assertTrue(Url.isUrl("https://www.google.com")); } diff --git a/structurizr-core/test/unit/com/structurizr/view/AutomaticLayoutTests.java b/structurizr-core/test/unit/com/structurizr/view/AutomaticLayoutTests.java index cb2efd64c..3ffd208d9 100644 --- a/structurizr-core/test/unit/com/structurizr/view/AutomaticLayoutTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/AutomaticLayoutTests.java @@ -7,7 +7,7 @@ public class AutomaticLayoutTests { @Test - void test_setAutomaticLayout() { + void setAutomaticLayout() { AutomaticLayout automaticLayout = new AutomaticLayout(AutomaticLayout.Implementation.Dagre, AutomaticLayout.RankDirection.LeftRight, 100, 200, 300, true); assertEquals(AutomaticLayout.RankDirection.LeftRight, automaticLayout.getRankDirection()); @@ -18,7 +18,7 @@ void test_setAutomaticLayout() { } @Test - void test_setRankDirection_ThrowsAnException_WhenNullIsSpecified() { + void setRankDirection_ThrowsAnException_WhenNullIsSpecified() { try { AutomaticLayout automaticLayout = new AutomaticLayout(); automaticLayout.setRankDirection(null); @@ -29,7 +29,7 @@ void test_setRankDirection_ThrowsAnException_WhenNullIsSpecified() { } @Test - void test_setRankSeparation_ThrowsAnException_WhenANegativeIntegerIsSpecified() { + void setRankSeparation_ThrowsAnException_WhenANegativeIntegerIsSpecified() { try { AutomaticLayout automaticLayout = new AutomaticLayout(); automaticLayout.setRankSeparation(-100); @@ -40,7 +40,7 @@ void test_setRankSeparation_ThrowsAnException_WhenANegativeIntegerIsSpecified() } @Test - void test_setNodeSeparation_ThrowsAnException_WhenANegativeIntegerIsSpecified() { + void setNodeSeparation_ThrowsAnException_WhenANegativeIntegerIsSpecified() { try { AutomaticLayout automaticLayout = new AutomaticLayout(); automaticLayout.setNodeSeparation(-100); @@ -51,7 +51,7 @@ void test_setNodeSeparation_ThrowsAnException_WhenANegativeIntegerIsSpecified() } @Test - void test_setEdgeSeparation_ThrowsAnException_WhenANegativeIntegerIsSpecified() { + void setEdgeSeparation_ThrowsAnException_WhenANegativeIntegerIsSpecified() { try { AutomaticLayout automaticLayout = new AutomaticLayout(); automaticLayout.setEdgeSeparation(-100); diff --git a/structurizr-core/test/unit/com/structurizr/view/BrandingTests.java b/structurizr-core/test/unit/com/structurizr/view/BrandingTests.java index dc01bf8b4..47c383281 100644 --- a/structurizr-core/test/unit/com/structurizr/view/BrandingTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/BrandingTests.java @@ -15,38 +15,38 @@ public void setUp() { } @Test - void test_setLogo_WithAUrl() { + void setLogo_WithAUrl() { branding.setLogo("https://structurizr.com/static/img/structurizr-logo.png"); assertEquals("https://structurizr.com/static/img/structurizr-logo.png", branding.getLogo()); } @Test - void test_setLogo_WithAUrlThatHasATrailingSpace() { + void setLogo_WithAUrlThatHasATrailingSpace() { branding.setLogo("https://structurizr.com/static/img/structurizr-logo.png "); assertEquals("https://structurizr.com/static/img/structurizr-logo.png", branding.getLogo()); } @Test - void test_setLogo_WithADataUri() { + void setLogo_WithADataUri() { branding.setLogo("data:image/png;base64,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"); assertEquals("data:image/png;base64,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", branding.getLogo()); } @Test - void test_setLogo_ThrowsAnIllegalArgumentException_WhenAnInvalidUrlIsSpecified() { + void setLogo_ThrowsAnIllegalArgumentException_WhenAnInvalidUrlIsSpecified() { assertThrows(IllegalArgumentException.class, () -> { branding.setLogo("htt://blah"); }); } @Test - void test_setLogo_DoesNothing_WhenANullUrlIsSpecified() { + void setLogo_DoesNothing_WhenANullUrlIsSpecified() { branding.setLogo(null); assertNull(branding.getLogo()); } @Test - void test_setLogo_DoesNothing_WhenAnEmptyUrlIsSpecified() { + void setLogo_DoesNothing_WhenAnEmptyUrlIsSpecified() { branding.setLogo(" "); assertNull(branding.getLogo()); } diff --git a/structurizr-core/test/unit/com/structurizr/view/ColorPairTests.java b/structurizr-core/test/unit/com/structurizr/view/ColorPairTests.java index f85ec7794..215f1c36b 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ColorPairTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ColorPairTests.java @@ -8,21 +8,21 @@ public class ColorPairTests { @Test - void test_construction() { + void construction() { ColorPair colorPair = new ColorPair("#ffffff", "#000000"); assertEquals("#ffffff", colorPair.getBackground()); assertEquals("#000000", colorPair.getForeground()); } @Test - void test_setBackground_WithAValidHtmlColorCode() { + void setBackground_WithAValidHtmlColorCode() { ColorPair colorPair = new ColorPair(); colorPair.setBackground("#ffffff"); assertEquals("#ffffff", colorPair.getBackground()); } @Test - void test_setBackground_ThrowsAnException_WhenANullHtmlColorCodeIsSpecified() { + void setBackground_ThrowsAnException_WhenANullHtmlColorCodeIsSpecified() { try { ColorPair colorPair = new ColorPair(); colorPair.setBackground(null); @@ -33,7 +33,7 @@ void test_setBackground_ThrowsAnException_WhenANullHtmlColorCodeIsSpecified() { } @Test - void test_setBackground_ThrowsAnException_WhenAnEmptyHtmlColorCodeIsSpecified() { + void setBackground_ThrowsAnException_WhenAnEmptyHtmlColorCodeIsSpecified() { try { ColorPair colorPair = new ColorPair(); colorPair.setBackground(""); @@ -44,7 +44,7 @@ void test_setBackground_ThrowsAnException_WhenAnEmptyHtmlColorCodeIsSpecified() } @Test - void test_setBackground_ThrowsAnException_WhenAnInvalidHtmlColorCodeIsSpecified() { + void setBackground_ThrowsAnException_WhenAnInvalidHtmlColorCodeIsSpecified() { try { ColorPair colorPair = new ColorPair(); colorPair.setBackground("ffffff"); @@ -55,14 +55,14 @@ void test_setBackground_ThrowsAnException_WhenAnInvalidHtmlColorCodeIsSpecified( } @Test - void test_setForeground_WithAValidHtmlColorCode() { + void setForeground_WithAValidHtmlColorCode() { ColorPair colorPair = new ColorPair(); colorPair.setForeground("#000000"); assertEquals("#000000", colorPair.getForeground()); } @Test - void test_setForeground_ThrowsAnException_WhenANullHtmlColorCodeIsSpecified() { + void setForeground_ThrowsAnException_WhenANullHtmlColorCodeIsSpecified() { try { ColorPair colorPair = new ColorPair(); colorPair.setForeground(null); @@ -73,7 +73,7 @@ void test_setForeground_ThrowsAnException_WhenANullHtmlColorCodeIsSpecified() { } @Test - void test_setForeground_ThrowsAnException_WhenAnEmptyHtmlColorCodeIsSpecified() { + void setForeground_ThrowsAnException_WhenAnEmptyHtmlColorCodeIsSpecified() { try { ColorPair colorPair = new ColorPair(); colorPair.setForeground(""); @@ -84,7 +84,7 @@ void test_setForeground_ThrowsAnException_WhenAnEmptyHtmlColorCodeIsSpecified() } @Test - void test_setForeground_ThrowsAnException_WhenAnInvalidHtmlColorCodeIsSpecified() { + void setForeground_ThrowsAnException_WhenAnInvalidHtmlColorCodeIsSpecified() { try { ColorPair colorPair = new ColorPair(); colorPair.setForeground("000000"); diff --git a/structurizr-core/test/unit/com/structurizr/view/ColorTests.java b/structurizr-core/test/unit/com/structurizr/view/ColorTests.java index c76263ce2..ad295c6db 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ColorTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ColorTests.java @@ -8,24 +8,24 @@ public class ColorTests { @Test - void test_isHexColorCode_ReturnsFalse_WhenPassedNull() { + void isHexColorCode_ReturnsFalse_WhenPassedNull() { assertFalse(Color.isHexColorCode(null)); } @Test - void test_isHexColorCode_ReturnsFalse_WhenPassedAnEmptyString() { + void isHexColorCode_ReturnsFalse_WhenPassedAnEmptyString() { assertFalse(Color.isHexColorCode("")); } @Test - void test_isHexColorCode_ReturnsFalse_WhenPassedAnInvalidString() { + void isHexColorCode_ReturnsFalse_WhenPassedAnInvalidString() { assertFalse(Color.isHexColorCode("ffffff")); assertFalse(Color.isHexColorCode("#fffff")); assertFalse(Color.isHexColorCode("#gggggg")); } @Test - void test_isHexColorCode_ReturnsTrue_WhenPassedAnValidString() { + void isHexColorCode_ReturnsTrue_WhenPassedAnValidString() { assertTrue(Color.isHexColorCode("#abcdef")); assertTrue(Color.isHexColorCode("#ABCDEF")); assertTrue(Color.isHexColorCode("#123456")); diff --git a/structurizr-core/test/unit/com/structurizr/view/ComponentViewTests.java b/structurizr-core/test/unit/com/structurizr/view/ComponentViewTests.java index eaeeb4300..ddf7daf9f 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ComponentViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ComponentViewTests.java @@ -24,7 +24,7 @@ public void setUp() { } @Test - void test_construction() { + void construction() { assertEquals("The System - Web Application - Components", view.getName()); assertEquals("Some description", view.getDescription()); assertEquals(0, view.getElements().size()); @@ -35,14 +35,14 @@ void test_construction() { } @Test - void test_addAllSoftwareSystems_DoesNothing_WhenThereAreNoOtherSoftwareSystems() { + void addAllSoftwareSystems_DoesNothing_WhenThereAreNoOtherSoftwareSystems() { assertEquals(0, view.getElements().size()); view.addAllSoftwareSystems(); assertEquals(0, view.getElements().size()); } @Test - void test_addAllSoftwareSystems_AddsAllSoftwareSystems_WhenThereAreSomeSoftwareSystemsInTheModel() { + void addAllSoftwareSystems_AddsAllSoftwareSystems_WhenThereAreSomeSoftwareSystemsInTheModel() { SoftwareSystem softwareSystemA = model.addSoftwareSystem(Location.External, "System A", "Description"); SoftwareSystem softwareSystemB = model.addSoftwareSystem(Location.External, "System B", "Description"); @@ -54,14 +54,14 @@ void test_addAllSoftwareSystems_AddsAllSoftwareSystems_WhenThereAreSomeSoftwareS } @Test - void test_addAllPeople_DoesNothing_WhenThereAreNoPeople() { + void addAllPeople_DoesNothing_WhenThereAreNoPeople() { assertEquals(0, view.getElements().size()); view.addAllPeople(); assertEquals(0, view.getElements().size()); } @Test - void test_addAllPeople_AddsAllPeople_WhenThereAreSomePeopleInTheModel() { + void addAllPeople_AddsAllPeople_WhenThereAreSomePeopleInTheModel() { Person userA = model.addPerson(Location.External, "User A", "Description"); Person userB = model.addPerson(Location.External, "User B", "Description"); @@ -73,14 +73,14 @@ void test_addAllPeople_AddsAllPeople_WhenThereAreSomePeopleInTheModel() { } @Test - void test_addAllElements_DoesNothing_WhenThereAreNoSoftwareSystemsOrPeople() { + void addAllElements_DoesNothing_WhenThereAreNoSoftwareSystemsOrPeople() { assertEquals(0, view.getElements().size()); view.addAllElements(); assertEquals(0, view.getElements().size()); } @Test - void test_addAllElements_AddsAllSoftwareSystemsAndPeopleAndContainersAndComponents_WhenThereAreSomeSoftwareSystemsAndPeopleAndContainersAndComponentsInTheModel() { + void addAllElements_AddsAllSoftwareSystemsAndPeopleAndContainersAndComponents_WhenThereAreSomeSoftwareSystemsAndPeopleAndContainersAndComponentsInTheModel() { SoftwareSystem softwareSystemA = model.addSoftwareSystem(Location.External, "System A", "Description"); SoftwareSystem softwareSystemB = model.addSoftwareSystem(Location.External, "System B", "Description"); Person userA = model.addPerson(Location.External, "User A", "Description"); @@ -102,14 +102,14 @@ void test_addAllElements_AddsAllSoftwareSystemsAndPeopleAndContainersAndComponen } @Test - void test_addAllContainers_DoesNothing_WhenThereAreNoContainers() { + void addAllContainers_DoesNothing_WhenThereAreNoContainers() { assertEquals(0, view.getElements().size()); view.addAllContainers(); assertEquals(0, view.getElements().size()); } @Test - void test_addAllContainers_AddsAllContainers_WhenThereAreSomeContainers() { + void addAllContainers_AddsAllContainers_WhenThereAreSomeContainers() { Container database = softwareSystem.addContainer("Database", "Stores something", "MySQL"); Container fileSystem = softwareSystem.addContainer("File System", "Stores something else", ""); @@ -121,14 +121,14 @@ void test_addAllContainers_AddsAllContainers_WhenThereAreSomeContainers() { } @Test - void test_addAllComponents_DoesNothing_WhenThereAreNoComponents() { + void addAllComponents_DoesNothing_WhenThereAreNoComponents() { assertEquals(0, view.getElements().size()); view.addAllComponents(); assertEquals(0, view.getElements().size()); } @Test - void test_addAllComponents_AddsAllComponents_WhenThereAreSomeComponents() { + void addAllComponents_AddsAllComponents_WhenThereAreSomeComponents() { Component componentA = webApplication.addComponent("Component A", "Does something", "Java"); Component componentB = webApplication.addComponent("Component B", "Does something", "Java"); @@ -140,7 +140,7 @@ void test_addAllComponents_AddsAllComponents_WhenThereAreSomeComponents() { } @Test - void test_add_ThrowsAnException_WhenANullContainerIsSpecified() { + void add_ThrowsAnException_WhenANullContainerIsSpecified() { assertEquals(0, view.getElements().size()); try { @@ -152,7 +152,7 @@ void test_add_ThrowsAnException_WhenANullContainerIsSpecified() { } @Test - void test_add_AddsTheContainer_WhenTheContainerIsNoInTheViewAlready() { + void add_AddsTheContainer_WhenTheContainerIsNoInTheViewAlready() { Container database = softwareSystem.addContainer("Database", "Stores something", "MySQL"); assertEquals(0, view.getElements().size()); @@ -162,7 +162,7 @@ void test_add_AddsTheContainer_WhenTheContainerIsNoInTheViewAlready() { } @Test - void test_add_DoesNothing_WhenTheSpecifiedContainerIsAlreadyInTheView() { + void add_DoesNothing_WhenTheSpecifiedContainerIsAlreadyInTheView() { Container database = softwareSystem.addContainer("Database", "Stores something", "MySQL"); view.add(database); assertEquals(1, view.getElements().size()); @@ -173,7 +173,7 @@ void test_add_DoesNothing_WhenTheSpecifiedContainerIsAlreadyInTheView() { } @Test - void test_remove_ThrowsAndException_WhenANullContainerIsPassed() { + void remove_ThrowsAndException_WhenANullContainerIsPassed() { try { view.remove((Container) null); fail(); @@ -183,7 +183,7 @@ void test_remove_ThrowsAndException_WhenANullContainerIsPassed() { } @Test - void test_remove_RemovesTheContainer_WhenTheContainerIsInTheView() { + void remove_RemovesTheContainer_WhenTheContainerIsInTheView() { Container database = softwareSystem.addContainer("Database", "Stores something", "MySQL"); view.add(database); assertEquals(1, view.getElements().size()); @@ -194,7 +194,7 @@ void test_remove_RemovesTheContainer_WhenTheContainerIsInTheView() { } @Test - void test_remove_DoesNothing_WhenTheContainerIsNotInTheView() { + void remove_DoesNothing_WhenTheContainerIsNotInTheView() { Container database = softwareSystem.addContainer("Database", "Stores something", "MySQL"); Container fileSystem = softwareSystem.addContainer("File System", "Stores something else", ""); @@ -208,14 +208,14 @@ void test_remove_DoesNothing_WhenTheContainerIsNotInTheView() { } @Test - void test_add_DoesNothing_WhenANullComponentIsSpecified() { + void add_DoesNothing_WhenANullComponentIsSpecified() { assertEquals(0, view.getElements().size()); view.add((Component) null); assertEquals(0, view.getElements().size()); } @Test - void test_add_AddsTheComponent_WhenTheComponentIsNotInTheViewAlready() { + void add_AddsTheComponent_WhenTheComponentIsNotInTheViewAlready() { Component componentA = webApplication.addComponent("Component A", "Does something", "Java"); assertEquals(0, view.getElements().size()); @@ -225,7 +225,7 @@ void test_add_AddsTheComponent_WhenTheComponentIsNotInTheViewAlready() { } @Test - void test_add_DoesNothing_WhenTheSpecifiedComponentIsAlreadyInTheView() { + void add_DoesNothing_WhenTheSpecifiedComponentIsAlreadyInTheView() { Component componentA = webApplication.addComponent("Component A", "Does something", "Java"); view.add(componentA); assertEquals(1, view.getElements().size()); @@ -236,7 +236,7 @@ void test_add_DoesNothing_WhenTheSpecifiedComponentIsAlreadyInTheView() { } @Test - void test_add_ThrowsAnException_WhenTheSpecifiedComponentIsInADifferentContainer() { + void add_ThrowsAnException_WhenTheSpecifiedComponentIsInADifferentContainer() { try { SoftwareSystem softwareSystemA = model.addSoftwareSystem("System A", "Description"); @@ -253,7 +253,7 @@ void test_add_ThrowsAnException_WhenTheSpecifiedComponentIsInADifferentContainer } @Test - void test_add_ThrowsAnException_WhenTheContainerOfTheViewIsAdded() { + void add_ThrowsAnException_WhenTheContainerOfTheViewIsAdded() { try { view.add(webApplication); fail(); @@ -263,7 +263,7 @@ void test_add_ThrowsAnException_WhenTheContainerOfTheViewIsAdded() { } @Test - void test_add_DoesNothing_WhenTheContainerOfTheViewIsAddedViaDependency() { + void add_DoesNothing_WhenTheContainerOfTheViewIsAddedViaDependency() { final SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.External, "Some other system", "external system that uses our web application"); final Relationship relationshipFromExternalSystem = softwareSystem.uses(webApplication, ""); @@ -274,7 +274,7 @@ void test_add_DoesNothing_WhenTheContainerOfTheViewIsAddedViaDependency() { } @Test - void test_remove_DoesNothing_WhenANullComponentIsPassed() { + void remove_DoesNothing_WhenANullComponentIsPassed() { try { view.remove((Component) null); fail(); @@ -284,7 +284,7 @@ void test_remove_DoesNothing_WhenANullComponentIsPassed() { } @Test - void test_remove_RemovesTheComponent_WhenTheComponentIsInTheView() { + void remove_RemovesTheComponent_WhenTheComponentIsInTheView() { Component componentA = webApplication.addComponent("Component A", "Does something", "Java"); view.add(componentA); assertEquals(1, view.getElements().size()); @@ -295,7 +295,7 @@ void test_remove_RemovesTheComponent_WhenTheComponentIsInTheView() { } @Test - void test_remove_RemovesTheComponentAndRelationships_WhenTheComponentIsInTheViewAndHasArelationshipToAnotherElement() { + void remove_RemovesTheComponentAndRelationships_WhenTheComponentIsInTheViewAndHasArelationshipToAnotherElement() { Component componentA = webApplication.addComponent("Component A", "Does something", "Java"); Component componentB = webApplication.addComponent("Component B", "Does something", "Java"); componentA.uses(componentB, "uses"); @@ -311,7 +311,7 @@ void test_remove_RemovesTheComponentAndRelationships_WhenTheComponentIsInTheView } @Test - void test_remove_DoesNothing_WhenTheComponentIsNotInTheView() { + void remove_DoesNothing_WhenTheComponentIsNotInTheView() { Component componentA = webApplication.addComponent("Component A", "Does something", "Java"); Component componentB = webApplication.addComponent("Component B", "Does something", "Java"); @@ -325,14 +325,14 @@ void test_remove_DoesNothing_WhenTheComponentIsNotInTheView() { } @Test - void test_addNearestNeightbours_DoesNothing_WhenANullElementIsSpecified() { + void addNearestNeightbours_DoesNothing_WhenANullElementIsSpecified() { view.addNearestNeighbours(null); assertEquals(0, view.getElements().size()); } @Test - void test_addNearestNeighbours_DoesNothing_WhenThereAreNoNeighbours() { + void addNearestNeighbours_DoesNothing_WhenThereAreNoNeighbours() { Component component = webApplication.addComponent("Component", "", ""); view.add(component); assertEquals(1, view.getElements().size()); @@ -342,7 +342,7 @@ void test_addNearestNeighbours_DoesNothing_WhenThereAreNoNeighbours() { } @Test - void test_addNearestNeighbours_AddsNearestNeighbours_WhenThereAreSomeNearestNeighbours() { + void addNearestNeighbours_AddsNearestNeighbours_WhenThereAreSomeNearestNeighbours() { SoftwareSystem softwareSystemA = model.addSoftwareSystem("System A", "Description"); SoftwareSystem softwareSystemB = model.addSoftwareSystem("System B", "Description"); Person userA = model.addPerson("User A", "Description"); @@ -400,7 +400,7 @@ void test_addNearestNeighbours_AddsNearestNeighbours_WhenThereAreSomeNearestNeig } @Test - void test_addExternalDependencies_AddsOrphanedElements_WhenThereAreNoDirectRelationshipsWithAComponent() { + void addExternalDependencies_AddsOrphanedElements_WhenThereAreNoDirectRelationshipsWithAComponent() { SoftwareSystem source = model.addSoftwareSystem("Source", ""); SoftwareSystem destination = model.addSoftwareSystem("Destination", ""); @@ -424,7 +424,7 @@ void test_addExternalDependencies_AddsOrphanedElements_WhenThereAreNoDirectRelat } @Test - void test_addExternalDependencies_AddsTheContainer_WhenAComponentHasARelationshipToAContainerInTheSameSoftwareSystem() { + void addExternalDependencies_AddsTheContainer_WhenAComponentHasARelationshipToAContainerInTheSameSoftwareSystem() { SoftwareSystem softwareSystemA = model.addSoftwareSystem("Software System A", ""); Container containerA = softwareSystemA.addContainer("Container A", "", ""); Component componentA = containerA.addComponent("Component A", "", ""); @@ -441,7 +441,7 @@ void test_addExternalDependencies_AddsTheContainer_WhenAComponentHasARelationshi } @Test - void test_addExternalDependencies_AddsTheContainer_WhenAComponentHasARelationshipFromAContainerInTheSameSoftwareSystem() { + void addExternalDependencies_AddsTheContainer_WhenAComponentHasARelationshipFromAContainerInTheSameSoftwareSystem() { SoftwareSystem softwareSystemA = model.addSoftwareSystem("Software System A", ""); Container containerA = softwareSystemA.addContainer("Container A", "", ""); Component componentA = containerA.addComponent("Component A", "", ""); @@ -458,7 +458,7 @@ void test_addExternalDependencies_AddsTheContainer_WhenAComponentHasARelationshi } @Test - void test_addExternalDependencies_AddsTheParentContainer_WhenAComponentHasARelationshipToAComponentInADifferentContainerInTheSameSoftwareSystem() { + void addExternalDependencies_AddsTheParentContainer_WhenAComponentHasARelationshipToAComponentInADifferentContainerInTheSameSoftwareSystem() { SoftwareSystem softwareSystemA = model.addSoftwareSystem("Software System A", ""); Container containerA = softwareSystemA.addContainer("Container A", "", ""); Component componentA = containerA.addComponent("Component A", "", ""); @@ -476,7 +476,7 @@ void test_addExternalDependencies_AddsTheParentContainer_WhenAComponentHasARelat } @Test - void test_addExternalDependencies_AddsTheParentContainer_WhenAComponentHasARelationshipFromAComponentInADifferentContainerInTheSameSoftwareSystem() { + void addExternalDependencies_AddsTheParentContainer_WhenAComponentHasARelationshipFromAComponentInADifferentContainerInTheSameSoftwareSystem() { SoftwareSystem softwareSystemA = model.addSoftwareSystem("Software System A", ""); Container containerA = softwareSystemA.addContainer("Container A", "", ""); Component componentA = containerA.addComponent("Component A", "", ""); @@ -494,7 +494,7 @@ void test_addExternalDependencies_AddsTheParentContainer_WhenAComponentHasARelat } @Test - void test_addExternalDependencies_AddsTheParentSoftwareSystem_WhenAComponentHasARelationshipToAContainerInAnotherSoftwareSystem() { + void addExternalDependencies_AddsTheParentSoftwareSystem_WhenAComponentHasARelationshipToAContainerInAnotherSoftwareSystem() { SoftwareSystem softwareSystemA = model.addSoftwareSystem("Software System A", ""); Container containerA = softwareSystemA.addContainer("Container A", "", ""); Component componentA = containerA.addComponent("Component A", "", ""); @@ -512,7 +512,7 @@ void test_addExternalDependencies_AddsTheParentSoftwareSystem_WhenAComponentHasA } @Test - void test_addExternalDependencies_AddsTheParentSoftwareSystem_WhenAComponentHasARelationshipFromAContainerInAnotherSoftwareSystem() { + void addExternalDependencies_AddsTheParentSoftwareSystem_WhenAComponentHasARelationshipFromAContainerInAnotherSoftwareSystem() { SoftwareSystem softwareSystemA = model.addSoftwareSystem("Software System A", ""); Container containerA = softwareSystemA.addContainer("Container A", "", ""); Component componentA = containerA.addComponent("Component A", "", ""); @@ -530,7 +530,7 @@ void test_addExternalDependencies_AddsTheParentSoftwareSystem_WhenAComponentHasA } @Test - void test_addExternalDependencies_AddsTheParentSoftwareSystem_WhenAComponentHasARelationshipToAComponentInAnotherSoftwareSystem() { + void addExternalDependencies_AddsTheParentSoftwareSystem_WhenAComponentHasARelationshipToAComponentInAnotherSoftwareSystem() { SoftwareSystem softwareSystemA = model.addSoftwareSystem("Software System A", ""); Container containerA = softwareSystemA.addContainer("Container A", "", ""); Component componentA = containerA.addComponent("Component A", "", ""); @@ -549,7 +549,7 @@ void test_addExternalDependencies_AddsTheParentSoftwareSystem_WhenAComponentHasA } @Test - void test_addExternalDependencies_AddsTheParentSoftwareSystem_WhenAComponentHasARelationshipFromAComponentInAnotherSoftwareSystem() { + void addExternalDependencies_AddsTheParentSoftwareSystem_WhenAComponentHasARelationshipFromAComponentInAnotherSoftwareSystem() { SoftwareSystem softwareSystemA = model.addSoftwareSystem("Software System A", ""); Container containerA = softwareSystemA.addContainer("Container A", "", ""); Component componentA = containerA.addComponent("Component A", "", ""); @@ -568,7 +568,7 @@ void test_addExternalDependencies_AddsTheParentSoftwareSystem_WhenAComponentHasA } @Test - void test_addDefaultElements() { + void addDefaultElements() { model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); CustomElement element = model.addCustomElement("Custom"); @@ -615,7 +615,7 @@ void test_addDefaultElements() { } @Test - void test_addSoftwareSystem_ThrowsAnException_WhenTheSoftwareSystemIsTheScopeOfTheView() { + void addSoftwareSystem_ThrowsAnException_WhenTheSoftwareSystemIsTheScopeOfTheView() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); Container container = softwareSystem.addContainer("Container"); @@ -629,7 +629,7 @@ void test_addSoftwareSystem_ThrowsAnException_WhenTheSoftwareSystemIsTheScopeOfT } @Test - void test_addContainer_ThrowsAnException_WhenTheContainerIsTheScopeOfTheView() { + void addContainer_ThrowsAnException_WhenTheContainerIsTheScopeOfTheView() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); Container container = softwareSystem.addContainer("Container"); @@ -643,7 +643,7 @@ void test_addContainer_ThrowsAnException_WhenTheContainerIsTheScopeOfTheView() { } @Test - void test_addSoftwareSystem_ThrowsAnException_WhenAChildContainerIsAlreadyAdded() { + void addSoftwareSystem_ThrowsAnException_WhenAChildContainerIsAlreadyAdded() { try { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1"); Container container1 = softwareSystem1.addContainer("Container 1"); @@ -665,7 +665,7 @@ void test_addSoftwareSystem_ThrowsAnException_WhenAChildContainerIsAlreadyAdded( } @Test - void test_addSoftwareSystem_ThrowsAnException_WhenAChildComponentIsAlreadyAdded() { + void addSoftwareSystem_ThrowsAnException_WhenAChildComponentIsAlreadyAdded() { try { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1"); Container container1 = softwareSystem1.addContainer("Container 1"); @@ -687,7 +687,7 @@ void test_addSoftwareSystem_ThrowsAnException_WhenAChildComponentIsAlreadyAdded( } @Test - void test_addContainer_ThrowsAnException_WhenAChildComponentIsAlreadyAdded() { + void addContainer_ThrowsAnException_WhenAChildComponentIsAlreadyAdded() { try { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1"); Container container1 = softwareSystem1.addContainer("Container 1"); @@ -709,7 +709,7 @@ void test_addContainer_ThrowsAnException_WhenAChildComponentIsAlreadyAdded() { } @Test - void test_addContainer_ThrowsAnException_WhenTheParentIsAlreadyAdded() { + void addContainer_ThrowsAnException_WhenTheParentIsAlreadyAdded() { try { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1"); Container container1 = softwareSystem1.addContainer("Container 1"); @@ -731,7 +731,7 @@ void test_addContainer_ThrowsAnException_WhenTheParentIsAlreadyAdded() { } @Test - void test_addComponent_ThrowsAnException_WhenTheParentIsAlreadyAdded() { + void addComponent_ThrowsAnException_WhenTheParentIsAlreadyAdded() { try { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1"); Container container1 = softwareSystem1.addContainer("Container 1"); diff --git a/structurizr-core/test/unit/com/structurizr/view/ConfigurationTests.java b/structurizr-core/test/unit/com/structurizr/view/ConfigurationTests.java index ac004de8e..ff3490c42 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ConfigurationTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ConfigurationTests.java @@ -8,14 +8,14 @@ public class ConfigurationTests extends AbstractWorkspaceTestBase { @Test - void test_defaultView_DoesNothing_WhenPassedNull() { + void defaultView_DoesNothing_WhenPassedNull() { Configuration configuration = new Configuration(); configuration.setDefaultView((View) null); assertNull(configuration.getDefaultView()); } @Test - void test_defaultView() { + void defaultView() { SystemLandscapeView view = views.createSystemLandscapeView("key", "Description"); Configuration configuration = new Configuration(); configuration.setDefaultView(view); @@ -23,7 +23,7 @@ void test_defaultView() { } @Test - void test_copyConfigurationFrom() { + void copyConfigurationFrom() { Configuration source = new Configuration(); source.setLastSavedView("someKey"); @@ -33,21 +33,21 @@ void test_copyConfigurationFrom() { } @Test - void test_setTheme_WithAUrl() { + void setTheme_WithAUrl() { Configuration configuration = new Configuration(); configuration.setTheme("https://example.com/theme.json"); assertEquals("https://example.com/theme.json", configuration.getTheme()); } @Test - void test_setTheme_WithAUrlThatHasATrailingSpace() { + void setTheme_WithAUrlThatHasATrailingSpace() { Configuration configuration = new Configuration(); configuration.setTheme("https://example.com/theme.json "); assertEquals("https://example.com/theme.json", configuration.getTheme()); } @Test - void test_setTheme_ThrowsAnIllegalArgumentException_WhenAnInvalidUrlIsSpecified() { + void setTheme_ThrowsAnIllegalArgumentException_WhenAnInvalidUrlIsSpecified() { assertThrows(IllegalArgumentException.class, () -> { Configuration configuration = new Configuration(); configuration.setTheme("htt://blah"); @@ -55,14 +55,14 @@ void test_setTheme_ThrowsAnIllegalArgumentException_WhenAnInvalidUrlIsSpecified( } @Test - void test_setTheme_DoesNothing_WhenANullUrlIsSpecified() { + void setTheme_DoesNothing_WhenANullUrlIsSpecified() { Configuration configuration = new Configuration(); configuration.setTheme(null); assertNull(configuration.getTheme()); } @Test - void test_setTheme_DoesNothing_WhenAnEmptyUrlIsSpecified() { + void setTheme_DoesNothing_WhenAnEmptyUrlIsSpecified() { Configuration configuration = new Configuration(); configuration.setTheme(" "); assertNull(configuration.getTheme()); diff --git a/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java b/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java index 2c0c52b9a..fb97520f8 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java @@ -19,7 +19,7 @@ public void setUp() { } @Test - void test_construction() { + void construction() { assertEquals("The System - Containers", view.getName()); assertEquals("Description", view.getDescription()); assertEquals(0, view.getElements().size()); @@ -29,14 +29,14 @@ void test_construction() { } @Test - void test_addAllSoftwareSystems_DoesNothing_WhenThereAreNoOtherSoftwareSystems() { + void addAllSoftwareSystems_DoesNothing_WhenThereAreNoOtherSoftwareSystems() { assertEquals(0, view.getElements().size()); view.addAllSoftwareSystems(); assertEquals(0, view.getElements().size()); } @Test - void test_addAllSoftwareSystems_AddsAllSoftwareSystems_WhenThereAreSomeSoftwareSystemsInTheModel() { + void addAllSoftwareSystems_AddsAllSoftwareSystems_WhenThereAreSomeSoftwareSystemsInTheModel() { SoftwareSystem softwareSystemA = model.addSoftwareSystem(Location.External, "System A", "Description"); SoftwareSystem softwareSystemB = model.addSoftwareSystem(Location.External, "System B", "Description"); @@ -48,14 +48,14 @@ void test_addAllSoftwareSystems_AddsAllSoftwareSystems_WhenThereAreSomeSoftwareS } @Test - void test_addAllPeople_DoesNothing_WhenThereAreNoPeople() { + void addAllPeople_DoesNothing_WhenThereAreNoPeople() { assertEquals(0, view.getElements().size()); view.addAllPeople(); assertEquals(0, view.getElements().size()); } @Test - void test_addAllPeople_AddsAllPeople_WhenThereAreSomePeopleInTheModel() { + void addAllPeople_AddsAllPeople_WhenThereAreSomePeopleInTheModel() { Person userA = model.addPerson(Location.External, "User A", "Description"); Person userB = model.addPerson(Location.External, "User B", "Description"); @@ -67,14 +67,14 @@ void test_addAllPeople_AddsAllPeople_WhenThereAreSomePeopleInTheModel() { } @Test - void test_addAllElements_DoesNothing_WhenThereAreNoSoftwareSystemsOrPeople() { + void addAllElements_DoesNothing_WhenThereAreNoSoftwareSystemsOrPeople() { assertEquals(0, view.getElements().size()); view.addAllElements(); assertEquals(0, view.getElements().size()); } @Test - void test_addAllElements_AddsAllSoftwareSystemsAndPeopleAndContainers_WhenThereAreSomeSoftwareSystemsAndPeopleAndContainersInTheModel() { + void addAllElements_AddsAllSoftwareSystemsAndPeopleAndContainers_WhenThereAreSomeSoftwareSystemsAndPeopleAndContainersInTheModel() { SoftwareSystem softwareSystemA = model.addSoftwareSystem(Location.External, "System A", "Description"); SoftwareSystem softwareSystemB = model.addSoftwareSystem(Location.External, "System B", "Description"); Person userA = model.addPerson(Location.External, "User A", "Description"); @@ -94,14 +94,14 @@ void test_addAllElements_AddsAllSoftwareSystemsAndPeopleAndContainers_WhenThereA } @Test - void test_addAllContainers_DoesNothing_WhenThereAreNoContainers() { + void addAllContainers_DoesNothing_WhenThereAreNoContainers() { assertEquals(0, view.getElements().size()); view.addAllContainers(); assertEquals(0, view.getElements().size()); } @Test - void test_addAllContainers_AddsAllContainers_WhenThereAreSomeContainers() { + void addAllContainers_AddsAllContainers_WhenThereAreSomeContainers() { Container webApplication = softwareSystem.addContainer("Web Application", "Does something", "Apache Tomcat"); Container database = softwareSystem.addContainer("Database", "Does something", "MySQL"); @@ -113,21 +113,21 @@ void test_addAllContainers_AddsAllContainers_WhenThereAreSomeContainers() { } @Test - void test_addNearestNeightbours_DoesNothing_WhenANullElementIsSpecified() { + void addNearestNeightbours_DoesNothing_WhenANullElementIsSpecified() { view.addNearestNeighbours(null); assertEquals(0, view.getElements().size()); } @Test - void test_addNearestNeighbours_DoesNothing_WhenThereAreNoNeighbours() { + void addNearestNeighbours_DoesNothing_WhenThereAreNoNeighbours() { view.addNearestNeighbours(softwareSystem); assertEquals(0, view.getElements().size()); } @Test - void test_addNearestNeighbours_AddsNearestNeighbours_WhenThereAreSomeNearestNeighbours() { + void addNearestNeighbours_AddsNearestNeighbours_WhenThereAreSomeNearestNeighbours() { SoftwareSystem softwareSystemA = model.addSoftwareSystem("System A", "Description"); SoftwareSystem softwareSystemB = model.addSoftwareSystem("System B", "Description"); Person userA = model.addPerson("User A", "Description"); @@ -186,7 +186,7 @@ void test_addNearestNeighbours_AddsNearestNeighbours_WhenThereAreSomeNearestNeig } @Test - void test_remove_RemovesContainer() { + void remove_RemovesContainer() { Container webApplication = softwareSystem.addContainer("Web Application", "", ""); Container database = softwareSystem.addContainer("Database", "", ""); @@ -199,7 +199,7 @@ void test_remove_RemovesContainer() { } @Test - void test_remove_ElementsWithTag() { + void remove_ElementsWithTag() { final String TAG = "myTag"; Container webApplication = softwareSystem.addContainer("Web Application", "", ""); Container database = softwareSystem.addContainer("Database", "", ""); @@ -214,7 +214,7 @@ void test_remove_ElementsWithTag() { } @Test - void test_remove_RelationshipWithTag() { + void remove_RelationshipWithTag() { final String TAG = "myTag"; Container webApplication = softwareSystem.addContainer("Web Application", "", ""); Container database = softwareSystem.addContainer("Database", "", ""); @@ -230,7 +230,7 @@ void test_remove_RelationshipWithTag() { } @Test - void test_addDependentSoftwareSystem() { + void addDependentSoftwareSystem() { assertEquals(0, view.getElements().size()); assertEquals(0, view.getRelationships().size()); @@ -249,7 +249,7 @@ void test_addDependentSoftwareSystem() { } @Test - void test_addDependentSoftwareSystem2() { + void addDependentSoftwareSystem2() { Container container1a = softwareSystem.addContainer("Container 1A", "", ""); SoftwareSystem softwareSystem2 = model.addSoftwareSystem(Location.External, "SoftwareSystem 2", ""); @@ -266,7 +266,7 @@ void test_addDependentSoftwareSystem2() { } @Test - void test_addDefaultElements() { + void addDefaultElements() { model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); CustomElement element = model.addCustomElement("Custom"); @@ -307,7 +307,7 @@ void test_addDefaultElements() { } @Test - void test_addSoftwareSystem_ThrowsAnException_WhenTheSoftwareSystemIsTheScopeOfTheView() { + void addSoftwareSystem_ThrowsAnException_WhenTheSoftwareSystemIsTheScopeOfTheView() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); view = new ContainerView(softwareSystem, "containers", "Description"); @@ -320,7 +320,7 @@ void test_addSoftwareSystem_ThrowsAnException_WhenTheSoftwareSystemIsTheScopeOfT } @Test - void test_addSoftwareSystem_ThrowsAnException_WhenAChildContainerIsAlreadyAdded() { + void addSoftwareSystem_ThrowsAnException_WhenAChildContainerIsAlreadyAdded() { try { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1"); Container container1 = softwareSystem1.addContainer("Container 1"); @@ -341,7 +341,7 @@ void test_addSoftwareSystem_ThrowsAnException_WhenAChildContainerIsAlreadyAdded( } @Test - void test_addContainer_ThrowsAnException_WhenTheParentIsAlreadyAdded() { + void addContainer_ThrowsAnException_WhenTheParentIsAlreadyAdded() { try { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1"); Container container1 = softwareSystem1.addContainer("Container 1"); diff --git a/structurizr-core/test/unit/com/structurizr/view/DefaultLayoutMergeStrategyTests.java b/structurizr-core/test/unit/com/structurizr/view/DefaultLayoutMergeStrategyTests.java index 26e329dfd..18744e7d7 100644 --- a/structurizr-core/test/unit/com/structurizr/view/DefaultLayoutMergeStrategyTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/DefaultLayoutMergeStrategyTests.java @@ -10,7 +10,7 @@ public class DefaultLayoutMergeStrategyTests { @Test - void test_copyLayoutInformation_WhenCanonicalNamesHaveNotChanged() { + void copyLayoutInformation_WhenCanonicalNamesHaveNotChanged() { Workspace workspace1 = new Workspace("1", ""); SoftwareSystem softwareSystem1 = workspace1.getModel().addSoftwareSystem("Software System"); Container container1 = softwareSystem1.addContainer("Container", "", ""); @@ -33,7 +33,7 @@ void test_copyLayoutInformation_WhenCanonicalNamesHaveNotChanged() { } @Test - void test_copyLayoutInformation_WhenAParentElementNameHasChanged() { + void copyLayoutInformation_WhenAParentElementNameHasChanged() { Workspace workspace1 = new Workspace("1", ""); SoftwareSystem softwareSystem1 = workspace1.getModel().addSoftwareSystem("Software System"); Container container1 = softwareSystem1.addContainer("Container", "", ""); @@ -56,7 +56,7 @@ void test_copyLayoutInformation_WhenAParentElementNameHasChanged() { } @Test - void test_copyLayoutInformation_WhenAnElementNameHasChangedButTheDescriptionHasNotChanged() { + void copyLayoutInformation_WhenAnElementNameHasChangedButTheDescriptionHasNotChanged() { Workspace workspace1 = new Workspace("1", ""); SoftwareSystem softwareSystem1 = workspace1.getModel().addSoftwareSystem("Software System"); Container container1 = softwareSystem1.addContainer("Container", "Container description", ""); @@ -79,7 +79,7 @@ void test_copyLayoutInformation_WhenAnElementNameHasChangedButTheDescriptionHasN } @Test - void test_copyLayoutInformation_WhenAnElementNameAndDescriptionHaveChangedButTheIdHasNotChanged() { + void copyLayoutInformation_WhenAnElementNameAndDescriptionHaveChangedButTheIdHasNotChanged() { Workspace workspace1 = new Workspace("1", ""); SoftwareSystem softwareSystem1 = workspace1.getModel().addSoftwareSystem("Software System"); Container container1 = softwareSystem1.addContainer("Container", "Container description", ""); @@ -102,7 +102,7 @@ void test_copyLayoutInformation_WhenAnElementNameAndDescriptionHaveChangedButThe } @Test - void test_copyLayoutInformation_WhenAnElementNameAndDescriptionAndIdHaveChanged() { + void copyLayoutInformation_WhenAnElementNameAndDescriptionAndIdHaveChanged() { Workspace workspace1 = new Workspace("1", ""); SoftwareSystem softwareSystem1 = workspace1.getModel().addSoftwareSystem("Software System"); Container container1 = softwareSystem1.addContainer("Container", "Container description", ""); @@ -126,7 +126,7 @@ void test_copyLayoutInformation_WhenAnElementNameAndDescriptionAndIdHaveChanged( } @Test - void test_copyLayoutInformation_WhenAnElementNameAndDescriptionAndIdHaveChangedAndDescriptionWasNull() { + void copyLayoutInformation_WhenAnElementNameAndDescriptionAndIdHaveChangedAndDescriptionWasNull() { Workspace workspace1 = new Workspace("1", ""); SoftwareSystem softwareSystem1 = workspace1.getModel().addSoftwareSystem("Software System"); Container container1 = softwareSystem1.addContainer("Container"); @@ -151,7 +151,7 @@ void test_copyLayoutInformation_WhenAnElementNameAndDescriptionAndIdHaveChangedA } @Test - void test_copyLayoutInformation_DoesNotThrowAnExceptionWhenAddingAnElementToAView() { + void copyLayoutInformation_DoesNotThrowAnExceptionWhenAddingAnElementToAView() { Workspace workspace1 = new Workspace("1", ""); SoftwareSystem softwareSystem1A = workspace1.getModel().addSoftwareSystem("Software System A"); SoftwareSystem softwareSystem1B = workspace1.getModel().addSoftwareSystem("Software System B"); diff --git a/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java b/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java index 36394293b..dd4e3c5e1 100644 --- a/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java @@ -16,27 +16,27 @@ public void setup() { } @Test - void test_getName_WithNoSoftwareSystemAndNoEnvironment() { + void getName_WithNoSoftwareSystemAndNoEnvironment() { deploymentView = views.createDeploymentView("deployment", "Description"); assertEquals("Deployment - Default", deploymentView.getName()); } @Test - void test_getName_WithNoSoftwareSystemAndAnEnvironment() { + void getName_WithNoSoftwareSystemAndAnEnvironment() { deploymentView = views.createDeploymentView("deployment", "Description"); deploymentView.setEnvironment("Live"); assertEquals("Deployment - Live", deploymentView.getName()); } @Test - void test_getName_WithASoftwareSystemAndNoEnvironment() { + void getName_WithASoftwareSystemAndNoEnvironment() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); deploymentView = views.createDeploymentView(softwareSystem, "deployment", "Description"); assertEquals("Software System - Deployment - Default", deploymentView.getName()); } @Test - void test_getName_WithASoftwareSystemAndAnEnvironment() { + void getName_WithASoftwareSystemAndAnEnvironment() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); deploymentView = views.createDeploymentView(softwareSystem, "deployment", "Description"); deploymentView.setEnvironment("Live"); @@ -44,7 +44,7 @@ void test_getName_WithASoftwareSystemAndAnEnvironment() { } @Test - void test_addDeploymentNode_ThrowsAnException_WhenPassedNull() { + void addDeploymentNode_ThrowsAnException_WhenPassedNull() { try { deploymentView = views.createDeploymentView("key", "Description"); deploymentView.add((DeploymentNode) null); @@ -55,7 +55,7 @@ void test_addDeploymentNode_ThrowsAnException_WhenPassedNull() { } @Test - void test_addRelationship_ThrowsAnException_WhenPassedNull() { + void addRelationship_ThrowsAnException_WhenPassedNull() { try { deploymentView = views.createDeploymentView("key", "Description"); deploymentView.add((Relationship) null); @@ -66,7 +66,7 @@ void test_addRelationship_ThrowsAnException_WhenPassedNull() { } @Test - void test_addAllDeploymentNodes_DoesNothing_WhenThereAreNoTopLevelDeploymentNodes() { + void addAllDeploymentNodes_DoesNothing_WhenThereAreNoTopLevelDeploymentNodes() { deploymentView = views.createDeploymentView("deployment", "Description"); deploymentView.addAllDeploymentNodes(); @@ -74,7 +74,7 @@ void test_addAllDeploymentNodes_DoesNothing_WhenThereAreNoTopLevelDeploymentNode } @Test - void test_addAllDeploymentNodes_DoesNothing_WhenThereAreTopLevelDeploymentNodesButNoContainerInstances() { + void addAllDeploymentNodes_DoesNothing_WhenThereAreTopLevelDeploymentNodesButNoContainerInstances() { deploymentView = views.createDeploymentView("deployment", "Description"); model.addDeploymentNode("Deployment Node", "Description", "Technology"); @@ -83,7 +83,7 @@ void test_addAllDeploymentNodes_DoesNothing_WhenThereAreTopLevelDeploymentNodesB } @Test - void test_addAllDeploymentNodes_DoesNothing_WhenThereNoDeploymentNodesForTheDeploymentEnvironment() { + void addAllDeploymentNodes_DoesNothing_WhenThereNoDeploymentNodesForTheDeploymentEnvironment() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); Container container = softwareSystem.addContainer("Container", "Description", "Technology"); DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node", "Description", "Technology"); @@ -96,7 +96,7 @@ void test_addAllDeploymentNodes_DoesNothing_WhenThereNoDeploymentNodesForTheDepl } @Test - void test_addAllDeploymentNodes_AddsDeploymentNodesAndContainerInstances_WhenThereAreTopLevelDeploymentNodesWithContainerInstances() { + void addAllDeploymentNodes_AddsDeploymentNodesAndContainerInstances_WhenThereAreTopLevelDeploymentNodesWithContainerInstances() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); Container container = softwareSystem.addContainer("Container", "Description", "Technology"); DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node", "Description", "Technology"); @@ -110,7 +110,7 @@ void test_addAllDeploymentNodes_AddsDeploymentNodesAndContainerInstances_WhenThe } @Test - void test_addAllDeploymentNodes_AddsDeploymentNodesAndContainerInstances_WhenThereAreChildDeploymentNodesWithContainerInstances() { + void addAllDeploymentNodes_AddsDeploymentNodesAndContainerInstances_WhenThereAreChildDeploymentNodesWithContainerInstances() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); Container container = softwareSystem.addContainer("Container", "Description", "Technology"); DeploymentNode deploymentNodeParent = model.addDeploymentNode("Deployment Node", "Description", "Technology"); @@ -126,7 +126,7 @@ void test_addAllDeploymentNodes_AddsDeploymentNodesAndContainerInstances_WhenThe } @Test - void test_addAllDeploymentNodes_AddsDeploymentNodesAndContainerInstancesOnlyForTheSoftwareSystemInScope() { + void addAllDeploymentNodes_AddsDeploymentNodesAndContainerInstancesOnlyForTheSoftwareSystemInScope() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1", ""); Container container1 = softwareSystem1.addContainer("Container 1", "Description", "Technology"); DeploymentNode deploymentNode1 = model.addDeploymentNode("Deployment Node 1", "Description", "Technology"); @@ -149,7 +149,7 @@ void test_addAllDeploymentNodes_AddsDeploymentNodesAndContainerInstancesOnlyForT } @Test - void test_addDeploymentNode_AddsTheParentToo() { + void addDeploymentNode_AddsTheParentToo() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); Container container = softwareSystem.addContainer("Container", "Description", "Technology"); DeploymentNode deploymentNodeParent = model.addDeploymentNode("Deployment Node", "Description", "Technology"); @@ -165,7 +165,7 @@ void test_addDeploymentNode_AddsTheParentToo() { } @Test - void test_addDeploymentNode_ThrowsAnException_WhenAddingADeploymentNodeFromAnotherDeploymentEnvironment() { + void addDeploymentNode_ThrowsAnException_WhenAddingADeploymentNodeFromAnotherDeploymentEnvironment() { DeploymentNode devDeploymentNode = model.addDeploymentNode("Dev", "Deployment Node", "Description", "Technology"); devDeploymentNode.addInfrastructureNode("Load Balancer"); DeploymentNode liveDeploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); @@ -184,7 +184,7 @@ void test_addDeploymentNode_ThrowsAnException_WhenAddingADeploymentNodeFromAnoth } @Test - void test_addSoftwareSystemInstance_ThrowsAnException_WhenTheSoftwareSystemInstanceIsTheSoftwareSystemInScope() { + void addSoftwareSystemInstance_ThrowsAnException_WhenTheSoftwareSystemInstanceIsTheSoftwareSystemInScope() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node", "Description", "Technology"); SoftwareSystemInstance softwareSystemInstance = deploymentNode.add(softwareSystem); @@ -198,7 +198,7 @@ void test_addSoftwareSystemInstance_ThrowsAnException_WhenTheSoftwareSystemInsta } @Test - void test_addSoftwareSystemInstance_DoesNotAddTheSoftwareSystemInstance_WhenAChildContainerInstanceHasAlreadyBeenAdded() { + void addSoftwareSystemInstance_DoesNotAddTheSoftwareSystemInstance_WhenAChildContainerInstanceHasAlreadyBeenAdded() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); Container container = softwareSystem.addContainer("Container"); DeploymentNode deploymentNode1 = model.addDeploymentNode("Deployment Node 1", "Description", "Technology"); @@ -217,7 +217,7 @@ void test_addSoftwareSystemInstance_DoesNotAddTheSoftwareSystemInstance_WhenAChi } @Test - void test_addContainerInstance_DoesNotAddTheContainerInstance_WhenTheParentSoftwareSystemInstanceHasAlreadyBeenAdded() { + void addContainerInstance_DoesNotAddTheContainerInstance_WhenTheParentSoftwareSystemInstanceHasAlreadyBeenAdded() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); Container container = softwareSystem.addContainer("Container"); DeploymentNode deploymentNode1 = model.addDeploymentNode("Deployment Node 1", "Description", "Technology"); @@ -236,7 +236,7 @@ void test_addContainerInstance_DoesNotAddTheContainerInstance_WhenTheParentSoftw } @Test - void test_addAnimationStep_ThrowsAnException_WhenNoElementInstancesAreSpecified() { + void addAnimationStep_ThrowsAnException_WhenNoElementInstancesAreSpecified() { try { deploymentView = views.createDeploymentView("deployment", "Description"); deploymentView.addAnimation((ContainerInstance[]) null); @@ -247,7 +247,7 @@ void test_addAnimationStep_ThrowsAnException_WhenNoElementInstancesAreSpecified( } @Test - void test_addAnimationStep_ThrowsAnException_WhenNoInfrastructureNodesAreSpecified() { + void addAnimationStep_ThrowsAnException_WhenNoInfrastructureNodesAreSpecified() { try { deploymentView = views.createDeploymentView("deployment", "Description"); deploymentView.addAnimation((InfrastructureNode[]) null); @@ -258,7 +258,7 @@ void test_addAnimationStep_ThrowsAnException_WhenNoInfrastructureNodesAreSpecifi } @Test - void test_addAnimationStep_ThrowsAnException_WhenNoElementInstancesOrInfrastructureNodesAreSpecified() { + void addAnimationStep_ThrowsAnException_WhenNoElementInstancesOrInfrastructureNodesAreSpecified() { try { deploymentView = views.createDeploymentView("deployment", "Description"); deploymentView.addAnimation(null, null); @@ -269,7 +269,7 @@ void test_addAnimationStep_ThrowsAnException_WhenNoElementInstancesOrInfrastruct } @Test - void test_addAnimationStep() { + void addAnimationStep() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); Container webApplication = softwareSystem.addContainer("Web Application", "Description", "Technology"); Container database = softwareSystem.addContainer("Database", "Description", "Technology"); @@ -303,7 +303,7 @@ void test_addAnimationStep() { } @Test - void test_addAnimationStep_IgnoresContainerInstancesThatDoNotExistInTheView() { + void addAnimationStep_IgnoresContainerInstancesThatDoNotExistInTheView() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); Container webApplication = softwareSystem.addContainer("Web Application", "Description", "Technology"); Container database = softwareSystem.addContainer("Database", "Description", "Technology"); @@ -329,7 +329,7 @@ void test_addAnimationStep_IgnoresContainerInstancesThatDoNotExistInTheView() { } @Test - void test_addAnimationStep_ThrowsAnException_WhenContainerInstancesAreSpecifiedButNoneOfThemExistInTheView() { + void addAnimationStep_ThrowsAnException_WhenContainerInstancesAreSpecifiedButNoneOfThemExistInTheView() { try { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); Container webApplication = softwareSystem.addContainer("Web Application", "Description", "Technology"); @@ -352,7 +352,7 @@ void test_addAnimationStep_ThrowsAnException_WhenContainerInstancesAreSpecifiedB } @Test - void test_remove_RemovesTheInfrastructureNode() { + void remove_RemovesTheInfrastructureNode() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); Container container = softwareSystem.addContainer("Container", "Description", "Technology"); DeploymentNode deploymentNodeParent = model.addDeploymentNode("Deployment Node", "Description", "Technology"); @@ -372,7 +372,7 @@ void test_remove_RemovesTheInfrastructureNode() { } @Test - void test_remove_RemovesTheSoftwareSystemInstance() { + void remove_RemovesTheSoftwareSystemInstance() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); DeploymentNode deploymentNodeParent = model.addDeploymentNode("Deployment Node", "Description", "Technology"); DeploymentNode deploymentNodeChild = deploymentNodeParent.addDeploymentNode("Deployment Node", "Description", "Technology"); @@ -391,7 +391,7 @@ void test_remove_RemovesTheSoftwareSystemInstance() { } @Test - void test_remove_RemovesTheContainerInstance() { + void remove_RemovesTheContainerInstance() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); Container container = softwareSystem.addContainer("Container", "Description", "Technology"); DeploymentNode deploymentNodeParent = model.addDeploymentNode("Deployment Node", "Description", "Technology"); @@ -411,7 +411,7 @@ void test_remove_RemovesTheContainerInstance() { } @Test - void test_remove_RemovesTheDeploymentNodeAndChildren() { + void remove_RemovesTheDeploymentNodeAndChildren() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); Container container = softwareSystem.addContainer("Container", "Description", "Technology"); DeploymentNode deploymentNodeParent = model.addDeploymentNode("Deployment Node", "Description", "Technology"); @@ -429,7 +429,7 @@ void test_remove_RemovesTheDeploymentNodeAndChildren() { } @Test - void test_remove_RemovesTheChildDeploymentNodeAndChildren() { + void remove_RemovesTheChildDeploymentNodeAndChildren() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); Container container = softwareSystem.addContainer("Container", "Description", "Technology"); DeploymentNode deploymentNodeParent = model.addDeploymentNode("Deployment Node", "Description", "Technology"); @@ -446,7 +446,7 @@ void test_remove_RemovesTheChildDeploymentNodeAndChildren() { } @Test - void test_add_AddsTheInfrastructureNode() { + void add_AddsTheInfrastructureNode() { DeploymentNode deploymentNodeParent = model.addDeploymentNode("Deployment Node", "Description", "Technology"); DeploymentNode deploymentNodeChild = deploymentNodeParent.addDeploymentNode("Deployment Node", "Description", "Technology"); InfrastructureNode infrastructureNode1 = deploymentNodeChild.addInfrastructureNode("Infrastructure Node 1"); @@ -462,7 +462,7 @@ void test_add_AddsTheInfrastructureNode() { } @Test - void test_add_AddsTheSoftwareSystemInstance() { + void add_AddsTheSoftwareSystemInstance() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); DeploymentNode deploymentNodeParent = model.addDeploymentNode("Deployment Node", "Description", "Technology"); DeploymentNode deploymentNodeChild = deploymentNodeParent.addDeploymentNode("Deployment Node", "Description", "Technology"); @@ -479,7 +479,7 @@ void test_add_AddsTheSoftwareSystemInstance() { } @Test - void test_addSoftwareSystemInstance_ThrowsAnException_WhenAChildContainerInstanceHasAlreadyBeenAdded() { + void addSoftwareSystemInstance_ThrowsAnException_WhenAChildContainerInstanceHasAlreadyBeenAdded() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); Container container = softwareSystem.addContainer("Container"); DeploymentNode deploymentNodeParent = model.addDeploymentNode("Deployment Node", "Description", "Technology"); @@ -499,7 +499,7 @@ void test_addSoftwareSystemInstance_ThrowsAnException_WhenAChildContainerInstanc } @Test - void test_add_AddsTheContainerInstance() { + void add_AddsTheContainerInstance() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); Container container = softwareSystem.addContainer("Container"); DeploymentNode deploymentNodeParent = model.addDeploymentNode("Deployment Node", "Description", "Technology"); @@ -517,7 +517,7 @@ void test_add_AddsTheContainerInstance() { } @Test - void test_addContainerInstance_ThrowsAnException_WhenTheParentSoftwareSystemInstanceHasAlreadyBeenAdded() { + void addContainerInstance_ThrowsAnException_WhenTheParentSoftwareSystemInstanceHasAlreadyBeenAdded() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); Container container = softwareSystem.addContainer("Container"); DeploymentNode deploymentNodeParent = model.addDeploymentNode("Deployment Node", "Description", "Technology"); diff --git a/structurizr-core/test/unit/com/structurizr/view/DimensionsTests.java b/structurizr-core/test/unit/com/structurizr/view/DimensionsTests.java index 33758c0f2..da073721b 100644 --- a/structurizr-core/test/unit/com/structurizr/view/DimensionsTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/DimensionsTests.java @@ -8,7 +8,7 @@ public class DimensionsTests { @Test - void test_construction() { + void construction() { Dimensions dimensions = new Dimensions(123, 456); assertEquals(123, dimensions.getWidth()); @@ -16,7 +16,7 @@ void test_construction() { } @Test - void test_setWidth_ThrowsAnException_WhenANegativeIntegerIsSpecified() { + void setWidth_ThrowsAnException_WhenANegativeIntegerIsSpecified() { try { Dimensions dimensions = new Dimensions(); dimensions.setWidth(-100); @@ -27,7 +27,7 @@ void test_setWidth_ThrowsAnException_WhenANegativeIntegerIsSpecified() { } @Test - void test_setHeight_ThrowsAnException_WhenANegativeIntegerIsSpecified() { + void setHeight_ThrowsAnException_WhenANegativeIntegerIsSpecified() { try { Dimensions dimensions = new Dimensions(); dimensions.setHeight(-100); diff --git a/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java b/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java index 81c98cbeb..27621a210 100644 --- a/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java @@ -44,7 +44,7 @@ public void setup() { } @Test - void test_add_ThrowsAnException_WhenPassedANullSourceElement() { + void add_ThrowsAnException_WhenPassedANullSourceElement() { try { DynamicView dynamicView = workspace.getViews().createDynamicView("key", "Description"); dynamicView.add(null, softwareSystemA); @@ -55,7 +55,7 @@ void test_add_ThrowsAnException_WhenPassedANullSourceElement() { } @Test - void test_add_ThrowsAnException_WhenPassedANullDestinationElement() { + void add_ThrowsAnException_WhenPassedANullDestinationElement() { try { DynamicView dynamicView = workspace.getViews().createDynamicView("key", "Description"); dynamicView.add(person, null); @@ -66,7 +66,7 @@ void test_add_ThrowsAnException_WhenPassedANullDestinationElement() { } @Test - void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsNotSpecifiedButAContainerIsAdded() { + void add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsNotSpecifiedButAContainerIsAdded() { try { DynamicView dynamicView = workspace.getViews().createDynamicView("key", "Description"); dynamicView.add(containerA1, containerA1); @@ -77,7 +77,7 @@ void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsNotSpecifiedButACo } @Test - void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsNotSpecifiedButAComponentIsAdded() { + void add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsNotSpecifiedButAComponentIsAdded() { try { DynamicView dynamicView = workspace.getViews().createDynamicView("key", "Description"); dynamicView.add(componentA1, componentA1); @@ -88,7 +88,7 @@ void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsNotSpecifiedButACo } @Test - void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsASoftwareSystemButAComponentIsAdded() { + void add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsASoftwareSystemButAComponentIsAdded() { try { DynamicView dynamicView = workspace.getViews().createDynamicView(softwareSystemA, "key", "Description"); dynamicView.add(componentA1, containerA1); @@ -99,7 +99,7 @@ void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsASoftwareSystemBut } @Test - void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsASoftwareSystemAndTheSameSoftwareSystemIsAdded() { + void add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsASoftwareSystemAndTheSameSoftwareSystemIsAdded() { try { DynamicView dynamicView = workspace.getViews().createDynamicView(softwareSystemA, "key", "Description"); dynamicView.add(softwareSystemA, containerA1); @@ -110,7 +110,7 @@ void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsASoftwareSystemAnd } @Test - void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsAContainerAndTheSameContainerIsAdded() { + void add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsAContainerAndTheSameContainerIsAdded() { try { DynamicView dynamicView = workspace.getViews().createDynamicView(containerA1, "key", "Description"); dynamicView.add(containerA1, containerA2); @@ -121,7 +121,7 @@ void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsAContainerAndTheSa } @Test - void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsAContainerAndTheParentSoftwareSystemIsAdded() { + void add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsAContainerAndTheParentSoftwareSystemIsAdded() { try { DynamicView dynamicView = workspace.getViews().createDynamicView(containerA1, "key", "Description"); dynamicView.add(softwareSystemA, containerA2); @@ -132,7 +132,7 @@ void test_add_ThrowsAnException_WhenTheScopeOfTheDynamicViewIsAContainerAndThePa } @Test - void test_add_ThrowsAnException_WhenTheParentOfAnElementHasAlreadyBeenAdded() { + void add_ThrowsAnException_WhenTheParentOfAnElementHasAlreadyBeenAdded() { try { SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", ""); Container container1 = softwareSystem.addContainer("Container 1", "", ""); @@ -154,7 +154,7 @@ void test_add_ThrowsAnException_WhenTheParentOfAnElementHasAlreadyBeenAdded() { } @Test - void test_add_ThrowsAnException_WhenTheChildOfAnElementHasAlreadyBeenAdded() { + void add_ThrowsAnException_WhenTheChildOfAnElementHasAlreadyBeenAdded() { try { SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", ""); Container container1 = softwareSystem.addContainer("Container 1", "", ""); @@ -176,7 +176,7 @@ void test_add_ThrowsAnException_WhenTheChildOfAnElementHasAlreadyBeenAdded() { } @Test - void test_add_ThrowsAnException_WhenARelationshipBetweenTheSourceAndDestinationElementsDoesNotExist() { + void add_ThrowsAnException_WhenARelationshipBetweenTheSourceAndDestinationElementsDoesNotExist() { try { DynamicView dynamicView = workspace.getViews().createDynamicView("key", "Description"); SoftwareSystem ss1 = workspace.getModel().addSoftwareSystem("Software System 1", ""); @@ -189,7 +189,7 @@ void test_add_ThrowsAnException_WhenARelationshipBetweenTheSourceAndDestinationE } @Test - void test_add_ThrowsAnException_WhenARelationshipBetweenTheSourceAndDestinationElementsWithTheSpecifiedTechnologyDoesNotExist() { + void add_ThrowsAnException_WhenARelationshipBetweenTheSourceAndDestinationElementsWithTheSpecifiedTechnologyDoesNotExist() { try { workspace = new Workspace("Name", "Description"); model = workspace.getModel(); @@ -209,7 +209,7 @@ void test_add_ThrowsAnException_WhenARelationshipBetweenTheSourceAndDestinationE } @Test - void test_addRelationshipWithOriginalDescription() { + void addRelationshipWithOriginalDescription() { workspace = new Workspace("Name", "Description"); model = workspace.getModel(); @@ -226,7 +226,7 @@ void test_addRelationshipWithOriginalDescription() { } @Test - void test_addRelationshipWithOveriddenDescription() { + void addRelationshipWithOveriddenDescription() { workspace = new Workspace("Name", "Description"); model = workspace.getModel(); @@ -243,14 +243,14 @@ void test_addRelationshipWithOveriddenDescription() { } @Test - void test_add_AddsTheSourceAndDestinationElements_WhenARelationshipBetweenThemExists() { + void add_AddsTheSourceAndDestinationElements_WhenARelationshipBetweenThemExists() { final DynamicView dynamicView = workspace.getViews().createDynamicView(softwareSystemA, "key", "Description"); dynamicView.add(containerA1, containerA2); assertEquals(2, dynamicView.getElements().size()); } @Test - void test_add_AddsTheSourceAndDestinationElements_WhenARelationshipBetweenThemExistsAndTheDestinationIsAnExternalSoftwareSystem() { + void add_AddsTheSourceAndDestinationElements_WhenARelationshipBetweenThemExistsAndTheDestinationIsAnExternalSoftwareSystem() { DynamicView dynamicView = workspace.getViews().createDynamicView(softwareSystemA, "key", "Description"); containerA2.uses(softwareSystemB, "", ""); dynamicView.add(containerA2, softwareSystemB); @@ -258,7 +258,7 @@ void test_add_AddsTheSourceAndDestinationElements_WhenARelationshipBetweenThemEx } @Test - void test_normalSequence() { + void normalSequence() { workspace = new Workspace("Name", "Description"); model = workspace.getModel(); @@ -280,7 +280,7 @@ void test_normalSequence() { } @Test - void test_normalSequence_WhenThereAreMultipleDescriptions() { + void normalSequence_WhenThereAreMultipleDescriptions() { workspace = new Workspace("Name", "Description"); model = workspace.getModel(); @@ -300,7 +300,7 @@ void test_normalSequence_WhenThereAreMultipleDescriptions() { } @Test - void test_normalSequence_WhenThereAreMultipleTechnologies() { + void normalSequence_WhenThereAreMultipleTechnologies() { workspace = new Workspace("Name", "Description"); model = workspace.getModel(); @@ -320,7 +320,7 @@ void test_normalSequence_WhenThereAreMultipleTechnologies() { } @Test - void test_parallelSequence() { + void parallelSequence() { workspace = new Workspace("Name", "Description"); model = workspace.getModel(); SoftwareSystem softwareSystemA = model.addSoftwareSystem("A", ""); @@ -359,7 +359,7 @@ void test_parallelSequence() { } @Test - void test_getRelationships_WhenTheOrderPropertyIsAnInteger() { + void getRelationships_WhenTheOrderPropertyIsAnInteger() { containerA1.uses(containerA2, "uses"); DynamicView view = workspace.getViews().createDynamicView(softwareSystemA, "key", "Description"); for (int i = 0; i < 10; i++) { @@ -380,7 +380,7 @@ void test_getRelationships_WhenTheOrderPropertyIsAnInteger() { } @Test - void test_getRelationships_WhenTheOrderPropertyIsADecimal() { + void getRelationships_WhenTheOrderPropertyIsADecimal() { containerA1.uses(containerA2, "uses"); DynamicView view = workspace.getViews().createDynamicView(softwareSystemA, "key", "Description"); for (int i = 0; i < 10; i++) { @@ -402,7 +402,7 @@ void test_getRelationships_WhenTheOrderPropertyIsADecimal() { } @Test - void test_getRelationships_WhenTheOrderPropertyIsAString() { + void getRelationships_WhenTheOrderPropertyIsAString() { String characters = "abcdefghij"; containerA1.uses(containerA2, "uses"); DynamicView view = workspace.getViews().createDynamicView(softwareSystemA, "key", "Description"); @@ -425,7 +425,7 @@ void test_getRelationships_WhenTheOrderPropertyIsAString() { } @Test - void test_response() { + void response() { workspace = new Workspace("Name", "Description"); model = workspace.getModel(); diff --git a/structurizr-core/test/unit/com/structurizr/view/ElementStyleTests.java b/structurizr-core/test/unit/com/structurizr/view/ElementStyleTests.java index 2b2f0692b..779c0b984 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ElementStyleTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ElementStyleTests.java @@ -10,7 +10,7 @@ public class ElementStyleTests { @Test - void test_setOpacity() { + void setOpacity() { ElementStyle style = new ElementStyle(); assertNull(style.getOpacity()); @@ -31,7 +31,7 @@ void test_setOpacity() { } @Test - void test_opacity() { + void opacity() { ElementStyle style = new ElementStyle(); assertNull(style.getOpacity()); @@ -52,7 +52,7 @@ void test_opacity() { } @Test - void test_setColor_SetsTheColorProperty_WhenAValidHexColorCodeIsSpecified() { + void setColor_SetsTheColorProperty_WhenAValidHexColorCodeIsSpecified() { ElementStyle style = new ElementStyle(); style.setColor("#ffffff"); assertEquals("#ffffff", style.getColor()); @@ -65,7 +65,7 @@ void test_setColor_SetsTheColorProperty_WhenAValidHexColorCodeIsSpecified() { } @Test - void test_color_SetsTheColorProperty_WhenAValidHexColorCodeIsSpecified() { + void color_SetsTheColorProperty_WhenAValidHexColorCodeIsSpecified() { ElementStyle style = new ElementStyle(); style.color("#ffffff"); assertEquals("#ffffff", style.getColor()); @@ -78,7 +78,7 @@ void test_color_SetsTheColorProperty_WhenAValidHexColorCodeIsSpecified() { } @Test - void test_setColor_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { + void setColor_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { assertThrows(IllegalArgumentException.class, () -> { ElementStyle style = new ElementStyle(); style.setColor("white"); @@ -86,7 +86,7 @@ void test_setColor_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { } @Test - void test_color_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { + void color_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { assertThrows(IllegalArgumentException.class, () -> { ElementStyle style = new ElementStyle(); style.color("white"); @@ -94,7 +94,7 @@ void test_color_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { } @Test - void test_setBackground_SetsTheBackgroundProperty_WhenAValidHexColorCodeIsSpecified() { + void setBackground_SetsTheBackgroundProperty_WhenAValidHexColorCodeIsSpecified() { ElementStyle style = new ElementStyle(); style.setBackground("#ffffff"); assertEquals("#ffffff", style.getBackground()); @@ -107,7 +107,7 @@ void test_setBackground_SetsTheBackgroundProperty_WhenAValidHexColorCodeIsSpecif } @Test - void test_background_SetsTheBackgroundProperty_WhenAValidHexColorCodeIsSpecified() { + void background_SetsTheBackgroundProperty_WhenAValidHexColorCodeIsSpecified() { ElementStyle style = new ElementStyle(); style.background("#ffffff"); assertEquals("#ffffff", style.getBackground()); @@ -120,7 +120,7 @@ void test_background_SetsTheBackgroundProperty_WhenAValidHexColorCodeIsSpecified } @Test - void test_setBackground_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { + void setBackground_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { assertThrows(IllegalArgumentException.class, () -> { ElementStyle style = new ElementStyle(); style.setBackground("white"); @@ -128,7 +128,7 @@ void test_setBackground_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() } @Test - void test_background_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { + void background_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { assertThrows(IllegalArgumentException.class, () -> { ElementStyle style = new ElementStyle(); style.background("white"); @@ -136,28 +136,28 @@ void test_background_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { } @Test - void test_setIcon_WithAUrl() { + void setIcon_WithAUrl() { ElementStyle style = new ElementStyle(); style.setIcon("https://structurizr.com/static/img/structurizr-logo.png"); assertEquals("https://structurizr.com/static/img/structurizr-logo.png", style.getIcon()); } @Test - void test_setIcon_WithAUrlThatHasATrailingSpaceCharacter() { + void setIcon_WithAUrlThatHasATrailingSpaceCharacter() { ElementStyle style = new ElementStyle(); style.setIcon("https://structurizr.com/static/img/structurizr-logo.png "); assertEquals("https://structurizr.com/static/img/structurizr-logo.png", style.getIcon()); } @Test - void test_setIcon_WithADataUri() { + void setIcon_WithADataUri() { ElementStyle style = new ElementStyle(); style.setIcon("data:image/png;base64,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"); assertEquals("data:image/png;base64,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", style.getIcon()); } @Test - void test_setIcon_ThrowsAnIllegalArgumentException_WhenAnInvalidUrlIsSpecified() { + void setIcon_ThrowsAnIllegalArgumentException_WhenAnInvalidUrlIsSpecified() { assertThrows(IllegalArgumentException.class, () -> { ElementStyle style = new ElementStyle(); style.setIcon("htt://blah"); @@ -165,21 +165,21 @@ void test_setIcon_ThrowsAnIllegalArgumentException_WhenAnInvalidUrlIsSpecified() } @Test - void test_setIcon_DoesNothing_WhenANullUrlIsSpecified() { + void setIcon_DoesNothing_WhenANullUrlIsSpecified() { ElementStyle style = new ElementStyle(); style.setIcon(null); assertNull(style.getIcon()); } @Test - void test_setIcon_DoesNothing_WhenAnEmptyUrlIsSpecified() { + void setIcon_DoesNothing_WhenAnEmptyUrlIsSpecified() { ElementStyle style = new ElementStyle(); style.setIcon(" "); assertNull(style.getIcon()); } @Test - void test_setStroke_SetsTheStrokeProperty_WhenAValidHexColorCodeIsSpecified() { + void setStroke_SetsTheStrokeProperty_WhenAValidHexColorCodeIsSpecified() { ElementStyle style = new ElementStyle(); style.setStroke("#ffffff"); assertEquals("#ffffff", style.getStroke()); @@ -192,7 +192,7 @@ void test_setStroke_SetsTheStrokeProperty_WhenAValidHexColorCodeIsSpecified() { } @Test - void test_Stroke_SetsTheStrokeProperty_WhenAValidHexColorCodeIsSpecified() { + void Stroke_SetsTheStrokeProperty_WhenAValidHexColorCodeIsSpecified() { ElementStyle style = new ElementStyle(); style.stroke("#ffffff"); assertEquals("#ffffff", style.getStroke()); @@ -205,7 +205,7 @@ void test_Stroke_SetsTheStrokeProperty_WhenAValidHexColorCodeIsSpecified() { } @Test - void test_setStroke_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { + void setStroke_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { assertThrows(IllegalArgumentException.class, () -> { ElementStyle style = new ElementStyle(); style.setStroke("white"); @@ -213,7 +213,7 @@ void test_setStroke_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { } @Test - void test_Stroke_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { + void Stroke_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { assertThrows(IllegalArgumentException.class, () -> { ElementStyle style = new ElementStyle(); style.stroke("white"); @@ -221,13 +221,13 @@ void test_Stroke_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { } @Test - void test_getProperties_ReturnsAnEmptyList_WhenNoPropertiesHaveBeenAdded() { + void getProperties_ReturnsAnEmptyList_WhenNoPropertiesHaveBeenAdded() { ElementStyle style = new ElementStyle(); assertEquals(0, style.getProperties().size()); } @Test - void test_addProperty_ThrowsAnException_WhenTheNameIsNull() { + void addProperty_ThrowsAnException_WhenTheNameIsNull() { try { ElementStyle style = new ElementStyle(); style.addProperty(null, "value"); @@ -238,7 +238,7 @@ void test_addProperty_ThrowsAnException_WhenTheNameIsNull() { } @Test - void test_addProperty_ThrowsAnException_WhenTheNameIsEmpty() { + void addProperty_ThrowsAnException_WhenTheNameIsEmpty() { try { ElementStyle style = new ElementStyle(); style.addProperty(" ", "value"); @@ -249,7 +249,7 @@ void test_addProperty_ThrowsAnException_WhenTheNameIsEmpty() { } @Test - void test_addProperty_ThrowsAnException_WhenTheValueIsNull() { + void addProperty_ThrowsAnException_WhenTheValueIsNull() { try { ElementStyle style = new ElementStyle(); style.addProperty("name", null); @@ -260,7 +260,7 @@ void test_addProperty_ThrowsAnException_WhenTheValueIsNull() { } @Test - void test_addProperty_ThrowsAnException_WhenTheValueIsEmpty() { + void addProperty_ThrowsAnException_WhenTheValueIsEmpty() { try { ElementStyle style = new ElementStyle(); style.addProperty("name", " "); @@ -271,21 +271,21 @@ void test_addProperty_ThrowsAnException_WhenTheValueIsEmpty() { } @Test - void test_addProperty_AddsTheProperty_WhenANameAndValueAreSpecified() { + void addProperty_AddsTheProperty_WhenANameAndValueAreSpecified() { ElementStyle style = new ElementStyle(); style.addProperty("name", "value"); assertEquals("value", style.getProperties().get("name")); } @Test - void test_setProperties_DoesNothing_WhenNullIsSpecified() { + void setProperties_DoesNothing_WhenNullIsSpecified() { ElementStyle style = new ElementStyle(); style.setProperties(null); assertEquals(0, style.getProperties().size()); } @Test - void test_setProperties_SetsTheProperties_WhenANonEmptyMapIsSpecified() { + void setProperties_SetsTheProperties_WhenANonEmptyMapIsSpecified() { ElementStyle style = new ElementStyle(); Map properties = new HashMap<>(); properties.put("name", "value"); diff --git a/structurizr-core/test/unit/com/structurizr/view/ElementViewTests.java b/structurizr-core/test/unit/com/structurizr/view/ElementViewTests.java index a768f19f1..497131c33 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ElementViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ElementViewTests.java @@ -10,14 +10,14 @@ public class ElementViewTests extends AbstractWorkspaceTestBase { @Test - void test_copyLayoutInformationFrom_DoesNothing_WhenNullIsPassed() { + void copyLayoutInformationFrom_DoesNothing_WhenNullIsPassed() { Element element = model.addSoftwareSystem(Location.External, "SystemA", ""); ElementView elementView = new ElementView(element); elementView.copyLayoutInformationFrom(null); } @Test - void test_copyLayoutInformationFrom_CopiesXAndY_WhenANonNullElementViewIsPassed() { + void copyLayoutInformationFrom_CopiesXAndY_WhenANonNullElementViewIsPassed() { Element element = model.addSoftwareSystem(Location.External, "SystemA", ""); ElementView elementView1 = new ElementView(element); assertEquals(0, elementView1.getX()); diff --git a/structurizr-core/test/unit/com/structurizr/view/FilteredViewTests.java b/structurizr-core/test/unit/com/structurizr/view/FilteredViewTests.java index 47837ceb9..a21074f97 100644 --- a/structurizr-core/test/unit/com/structurizr/view/FilteredViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/FilteredViewTests.java @@ -9,7 +9,7 @@ public class FilteredViewTests extends AbstractWorkspaceTestBase { @Test - void test_construction() { + void construction() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); SystemContextView systemContextView = views.createSystemContextView(softwareSystem, "SystemContext", "Description"); FilteredView filteredView = views.createFilteredView( diff --git a/structurizr-core/test/unit/com/structurizr/view/FontTests.java b/structurizr-core/test/unit/com/structurizr/view/FontTests.java index 4839fc961..b5cee1446 100644 --- a/structurizr-core/test/unit/com/structurizr/view/FontTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/FontTests.java @@ -28,26 +28,26 @@ void construction_WithANameAndUrl() { } @Test - void test_setUrl_WithAUrl() { + void setUrl_WithAUrl() { font.setUrl("https://fonts.googleapis.com/css?family=Open+Sans:400,700"); assertEquals("https://fonts.googleapis.com/css?family=Open+Sans:400,700", font.getUrl()); } @Test - void test_setUrl_ThrowsAnIllegalArgumentException_WhenAnInvalidUrlIsSpecified() { + void setUrl_ThrowsAnIllegalArgumentException_WhenAnInvalidUrlIsSpecified() { assertThrows(IllegalArgumentException.class, () -> { font.setUrl("htt://blah"); }); } @Test - void test_setUrl_DoesNothing_WhenANullUrlIsSpecified() { + void setUrl_DoesNothing_WhenANullUrlIsSpecified() { font.setUrl(null); assertNull(font.getUrl()); } @Test - void test_setUrl_DoesNothing_WhenAnEmptyUrlIsSpecified() { + void setUrl_DoesNothing_WhenAnEmptyUrlIsSpecified() { font.setUrl(" "); assertNull(font.getUrl()); } diff --git a/structurizr-core/test/unit/com/structurizr/view/PaperSizeTests.java b/structurizr-core/test/unit/com/structurizr/view/PaperSizeTests.java index 52cd80477..bf4af3932 100644 --- a/structurizr-core/test/unit/com/structurizr/view/PaperSizeTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/PaperSizeTests.java @@ -9,7 +9,7 @@ public class PaperSizeTests { @Test - void test_getOrderedPaperSizes_WhenOrientationIsLandscape() { + void getOrderedPaperSizes_WhenOrientationIsLandscape() { List paperSizes = PaperSize.getOrderedPaperSizes(PaperSize.Orientation.Landscape); assertEquals(12, paperSizes.size()); @@ -29,7 +29,7 @@ void test_getOrderedPaperSizes_WhenOrientationIsLandscape() { } @Test - void test_getOrderedPaperSizes_WhenOrientationIsPortrait() { + void getOrderedPaperSizes_WhenOrientationIsPortrait() { List paperSizes = PaperSize.getOrderedPaperSizes(PaperSize.Orientation.Portrait); assertEquals(9, paperSizes.size()); @@ -46,7 +46,7 @@ void test_getOrderedPaperSizes_WhenOrientationIsPortrait() { } @Test - void test_getOrderedPaperSizes() { + void getOrderedPaperSizes() { List paperSizes = PaperSize.getOrderedPaperSizes(); assertEquals(21, paperSizes.size()); diff --git a/structurizr-core/test/unit/com/structurizr/view/RelationshipStyleTests.java b/structurizr-core/test/unit/com/structurizr/view/RelationshipStyleTests.java index bc95ba423..46e559ad3 100644 --- a/structurizr-core/test/unit/com/structurizr/view/RelationshipStyleTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/RelationshipStyleTests.java @@ -12,25 +12,25 @@ public class RelationshipStyleTests { private RelationshipStyle relationshipStyle = new RelationshipStyle("tag"); @Test - void test_setPosition_SetsPositionToNull_WhenNullIsSpecified() { + void setPosition_SetsPositionToNull_WhenNullIsSpecified() { relationshipStyle.setPosition(null); assertNull(relationshipStyle.getPosition()); } @Test - void test_setPosition_SetsPositionToZero_WhenANegativeNumberIsSpecified() { + void setPosition_SetsPositionToZero_WhenANegativeNumberIsSpecified() { relationshipStyle.setPosition(-1); assertEquals(Integer.valueOf(0), relationshipStyle.getPosition()); } @Test - void test_setPosition_SetsPositionToOneHundred_WhenANumberGreaterThanOneHundredIsSpecified() { + void setPosition_SetsPositionToOneHundred_WhenANumberGreaterThanOneHundredIsSpecified() { relationshipStyle.setPosition(101); assertEquals(Integer.valueOf(100), relationshipStyle.getPosition()); } @Test - void test_setPosition_SetsPosition_WhenANumberBetweenZeroAndOneHundredIsSpecified() { + void setPosition_SetsPosition_WhenANumberBetweenZeroAndOneHundredIsSpecified() { relationshipStyle.setPosition(0); assertEquals(Integer.valueOf(0), relationshipStyle.getPosition()); @@ -49,7 +49,7 @@ void test_setPosition_SetsPosition_WhenANumberBetweenZeroAndOneHundredIsSpecifie } @Test - void test_setOpacity() { + void setOpacity() { RelationshipStyle style = new RelationshipStyle(); assertNull(style.getOpacity()); @@ -70,7 +70,7 @@ void test_setOpacity() { } @Test - void test_opacity() { + void opacity() { RelationshipStyle style = new RelationshipStyle(); assertNull(style.getOpacity()); @@ -91,7 +91,7 @@ void test_opacity() { } @Test - void test_setColor_SetsTheColorProperty_WhenAValidHexColorCodeIsSpecified() { + void setColor_SetsTheColorProperty_WhenAValidHexColorCodeIsSpecified() { RelationshipStyle style = new RelationshipStyle(); style.setColor("#ffffff"); assertEquals("#ffffff", style.getColor()); @@ -104,7 +104,7 @@ void test_setColor_SetsTheColorProperty_WhenAValidHexColorCodeIsSpecified() { } @Test - void test_color_SetsTheColorProperty_WhenAValidHexColorCodeIsSpecified() { + void color_SetsTheColorProperty_WhenAValidHexColorCodeIsSpecified() { RelationshipStyle style = new RelationshipStyle(); style.color("#ffffff"); assertEquals("#ffffff", style.getColor()); @@ -117,7 +117,7 @@ void test_color_SetsTheColorProperty_WhenAValidHexColorCodeIsSpecified() { } @Test - void test_setColor_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { + void setColor_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { assertThrows(IllegalArgumentException.class, () -> { RelationshipStyle style = new RelationshipStyle(); style.setColor("white"); @@ -125,7 +125,7 @@ void test_setColor_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { } @Test - void test_color_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { + void color_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { assertThrows(IllegalArgumentException.class, () -> { RelationshipStyle style = new RelationshipStyle(); style.color("white"); @@ -133,13 +133,13 @@ void test_color_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { } @Test - void test_getProperties_ReturnsAnEmptyList_WhenNoPropertiesHaveBeenAdded() { + void getProperties_ReturnsAnEmptyList_WhenNoPropertiesHaveBeenAdded() { RelationshipStyle style = new RelationshipStyle(); assertEquals(0, style.getProperties().size()); } @Test - void test_addProperty_ThrowsAnException_WhenTheNameIsNull() { + void addProperty_ThrowsAnException_WhenTheNameIsNull() { try { RelationshipStyle style = new RelationshipStyle(); style.addProperty(null, "value"); @@ -150,7 +150,7 @@ void test_addProperty_ThrowsAnException_WhenTheNameIsNull() { } @Test - void test_addProperty_ThrowsAnException_WhenTheNameIsEmpty() { + void addProperty_ThrowsAnException_WhenTheNameIsEmpty() { try { RelationshipStyle style = new RelationshipStyle(); style.addProperty(" ", "value"); @@ -161,7 +161,7 @@ void test_addProperty_ThrowsAnException_WhenTheNameIsEmpty() { } @Test - void test_addProperty_ThrowsAnException_WhenTheValueIsNull() { + void addProperty_ThrowsAnException_WhenTheValueIsNull() { try { RelationshipStyle style = new RelationshipStyle(); style.addProperty("name", null); @@ -172,7 +172,7 @@ void test_addProperty_ThrowsAnException_WhenTheValueIsNull() { } @Test - void test_addProperty_ThrowsAnException_WhenTheValueIsEmpty() { + void addProperty_ThrowsAnException_WhenTheValueIsEmpty() { try { RelationshipStyle style = new RelationshipStyle(); style.addProperty("name", " "); @@ -183,21 +183,21 @@ void test_addProperty_ThrowsAnException_WhenTheValueIsEmpty() { } @Test - void test_addProperty_AddsTheProperty_WhenANameAndValueAreSpecified() { + void addProperty_AddsTheProperty_WhenANameAndValueAreSpecified() { RelationshipStyle style = new RelationshipStyle(); style.addProperty("name", "value"); assertEquals("value", style.getProperties().get("name")); } @Test - void test_setProperties_DoesNothing_WhenNullIsSpecified() { + void setProperties_DoesNothing_WhenNullIsSpecified() { RelationshipStyle style = new RelationshipStyle(); style.setProperties(null); assertEquals(0, style.getProperties().size()); } @Test - void test_setProperties_SetsTheProperties_WhenANonEmptyMapIsSpecified() { + void setProperties_SetsTheProperties_WhenANonEmptyMapIsSpecified() { RelationshipStyle style = new RelationshipStyle(); Map properties = new HashMap<>(); properties.put("name", "value"); diff --git a/structurizr-core/test/unit/com/structurizr/view/SequenceCounterTests.java b/structurizr-core/test/unit/com/structurizr/view/SequenceCounterTests.java index 1b77117a8..3b8242421 100644 --- a/structurizr-core/test/unit/com/structurizr/view/SequenceCounterTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/SequenceCounterTests.java @@ -7,7 +7,7 @@ public class SequenceCounterTests { @Test - void test_increment_IncrementsTheCounter_WhenThereIsNoParent() { + void increment_IncrementsTheCounter_WhenThereIsNoParent() { SequenceCounter counter = new SequenceCounter(); assertEquals("0", counter.toString()); diff --git a/structurizr-core/test/unit/com/structurizr/view/SequenceNumberTests.java b/structurizr-core/test/unit/com/structurizr/view/SequenceNumberTests.java index bb5404cc2..ed1eff62f 100644 --- a/structurizr-core/test/unit/com/structurizr/view/SequenceNumberTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/SequenceNumberTests.java @@ -7,14 +7,14 @@ public class SequenceNumberTests { @Test - void test_increment() { + void increment() { SequenceNumber sequenceNumber = new SequenceNumber(); assertEquals("1", sequenceNumber.getNext()); assertEquals("2", sequenceNumber.getNext()); } @Test - void test_parallelSequences() { + void parallelSequences() { SequenceNumber sequenceNumber = new SequenceNumber(); assertEquals("1", sequenceNumber.getNext()); diff --git a/structurizr-core/test/unit/com/structurizr/view/StaticViewTests.java b/structurizr-core/test/unit/com/structurizr/view/StaticViewTests.java index c23526029..722bd5c39 100644 --- a/structurizr-core/test/unit/com/structurizr/view/StaticViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/StaticViewTests.java @@ -10,7 +10,7 @@ public class StaticViewTests extends AbstractWorkspaceTestBase { @Test - void test_addAnimationStep_ThrowsAnException_WhenNoElementsAreSpecified() { + void addAnimationStep_ThrowsAnException_WhenNoElementsAreSpecified() { try { SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "Description"); view.addAnimation(); @@ -21,7 +21,7 @@ void test_addAnimationStep_ThrowsAnException_WhenNoElementsAreSpecified() { } @Test - void test_addAnimationStep() { + void addAnimationStep() { SoftwareSystem element1 = model.addSoftwareSystem("Software System 1", ""); SoftwareSystem element2 = model.addSoftwareSystem("Software System 2", ""); SoftwareSystem element3 = model.addSoftwareSystem("Software System 3", ""); @@ -55,7 +55,7 @@ void test_addAnimationStep() { } @Test - void test_addAnimationStep_IgnoresElementsThatDoNotExistInTheView() { + void addAnimationStep_IgnoresElementsThatDoNotExistInTheView() { SoftwareSystem element1 = model.addSoftwareSystem("Software System 1", ""); SoftwareSystem element2 = model.addSoftwareSystem("Software System 2", ""); @@ -69,7 +69,7 @@ void test_addAnimationStep_IgnoresElementsThatDoNotExistInTheView() { } @Test - void test_addAnimationStep_ThrowsAnException_WhenElementsAreSpecifiedButNoneOfThemExistInTheView() { + void addAnimationStep_ThrowsAnException_WhenElementsAreSpecifiedButNoneOfThemExistInTheView() { try { SoftwareSystem element1 = model.addSoftwareSystem("Software System 1", ""); diff --git a/structurizr-core/test/unit/com/structurizr/view/StylesTests.java b/structurizr-core/test/unit/com/structurizr/view/StylesTests.java index 99182c7a3..0a663da2a 100644 --- a/structurizr-core/test/unit/com/structurizr/view/StylesTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/StylesTests.java @@ -11,7 +11,7 @@ public class StylesTests extends AbstractWorkspaceTestBase { private Styles styles = new Styles(); @Test - void test_findElementStyle_ReturnsTheDefaultStyle_WhenPassedNull() { + void findElementStyle_ReturnsTheDefaultStyle_WhenPassedNull() { ElementStyle style = styles.findElementStyle((Element) null); assertEquals(Integer.valueOf(450), style.getWidth()); assertEquals(Integer.valueOf(300), style.getHeight()); @@ -28,7 +28,7 @@ void test_findElementStyle_ReturnsTheDefaultStyle_WhenPassedNull() { } @Test - void test_findElementStyle_ReturnsTheDefaultStyle_WhenNoStylesAreDefined() { + void findElementStyle_ReturnsTheDefaultStyle_WhenNoStylesAreDefined() { SoftwareSystem element = model.addSoftwareSystem("Name", "Description"); ElementStyle style = styles.findElementStyle(element); assertEquals(Integer.valueOf(450), style.getWidth()); @@ -46,7 +46,7 @@ void test_findElementStyle_ReturnsTheDefaultStyle_WhenNoStylesAreDefined() { } @Test - void test_findElementStyle_ReturnsTheCorrectStyle_WhenStylesAreDefined() { + void findElementStyle_ReturnsTheCorrectStyle_WhenStylesAreDefined() { SoftwareSystem element = model.addSoftwareSystem("Name", "Description"); element.addTags("Some Tag"); @@ -69,7 +69,7 @@ void test_findElementStyle_ReturnsTheCorrectStyle_WhenStylesAreDefined() { } @Test - void test_findElementStyle_ReturnsTheCorrectStyleForAnElementInstance_WhenStylesAreDefined() { + void findElementStyle_ReturnsTheCorrectStyleForAnElementInstance_WhenStylesAreDefined() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Name"); softwareSystem.addTags("Some Tag"); @@ -95,7 +95,7 @@ void test_findElementStyle_ReturnsTheCorrectStyleForAnElementInstance_WhenStyles } @Test - void test_findElementStyle_ReturnsTheDefaultElementSize_WhenTheShapeIsABox() { + void findElementStyle_ReturnsTheDefaultElementSize_WhenTheShapeIsABox() { SoftwareSystem element = model.addSoftwareSystem("Name", "Description"); element.addTags("Some Tag"); @@ -109,7 +109,7 @@ void test_findElementStyle_ReturnsTheDefaultElementSize_WhenTheShapeIsABox() { } @Test - void test_findElementStyle_ReturnsTheDefaultElementSize_WhenTheShapeIsAPerson() { + void findElementStyle_ReturnsTheDefaultElementSize_WhenTheShapeIsAPerson() { SoftwareSystem element = model.addSoftwareSystem("Name", "Description"); element.addTags("Some Tag"); @@ -123,7 +123,7 @@ void test_findElementStyle_ReturnsTheDefaultElementSize_WhenTheShapeIsAPerson() } @Test - void test_findRelationshipStyle_ReturnsTheDefaultStyle_WhenPassedNull() { + void findRelationshipStyle_ReturnsTheDefaultStyle_WhenPassedNull() { RelationshipStyle style = styles.findRelationshipStyle((Relationship) null); assertEquals(Integer.valueOf(2), style.getThickness()); assertEquals("#707070", style.getColor()); @@ -136,7 +136,7 @@ void test_findRelationshipStyle_ReturnsTheDefaultStyle_WhenPassedNull() { } @Test - void test_findRelationshipStyle_ReturnsTheDefaultStyle_WhenNoStylesAreDefined() { + void findRelationshipStyle_ReturnsTheDefaultStyle_WhenNoStylesAreDefined() { SoftwareSystem element = model.addSoftwareSystem("Name", "Description"); Relationship relationship = element.uses(element, "Uses"); RelationshipStyle style = styles.findRelationshipStyle(relationship); @@ -151,7 +151,7 @@ void test_findRelationshipStyle_ReturnsTheDefaultStyle_WhenNoStylesAreDefined() } @Test - void test_findRelationshipStyle_ReturnsTheCorrectStyle_WhenStylesAreDefined() { + void findRelationshipStyle_ReturnsTheCorrectStyle_WhenStylesAreDefined() { SoftwareSystem element = model.addSoftwareSystem("Name", "Description"); Relationship relationship = element.uses(element, "Uses"); relationship.addTags("Some Tag"); @@ -171,7 +171,7 @@ void test_findRelationshipStyle_ReturnsTheCorrectStyle_WhenStylesAreDefined() { } @Test - void test_findRelationshipStyle_ReturnsTheCorrectStyle_WhenThereIsALinkedRelationship() { + void findRelationshipStyle_ReturnsTheCorrectStyle_WhenThereIsALinkedRelationship() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); Container container1 = softwareSystem.addContainer("Container 1", "Description", "Technology"); Container container2 = softwareSystem.addContainer("Container 2", "Description", "Technology"); @@ -194,7 +194,7 @@ void test_findRelationshipStyle_ReturnsTheCorrectStyle_WhenThereIsALinkedRelatio } @Test - void test_findRelationshipStyle_ReturnsTheCorrectStyle_WhenThereIsALinkedRelationshipBasedUponAnImpliedRelationship() { + void findRelationshipStyle_ReturnsTheCorrectStyle_WhenThereIsALinkedRelationshipBasedUponAnImpliedRelationship() { model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); Container container1 = softwareSystem.addContainer("Container 1"); @@ -220,7 +220,7 @@ void test_findRelationshipStyle_ReturnsTheCorrectStyle_WhenThereIsALinkedRelatio } @Test - void test_addElementStyle_ThrowsAnException_WhenATagIsNotSpecified() { + void addElementStyle_ThrowsAnException_WhenATagIsNotSpecified() { try { styles.addElementStyle(""); fail(); @@ -244,7 +244,7 @@ void test_addElementStyle_ThrowsAnException_WhenATagIsNotSpecified() { } @Test - void test_addElementStyleByTag_ThrowsAnException_WhenAStyleWithTheSameTagExistsAlready() { + void addElementStyleByTag_ThrowsAnException_WhenAStyleWithTheSameTagExistsAlready() { try { styles.addElementStyle(Tags.SOFTWARE_SYSTEM).color("#ff0000"); styles.addElementStyle(Tags.SOFTWARE_SYSTEM).color("#ff0000"); @@ -256,7 +256,7 @@ void test_addElementStyleByTag_ThrowsAnException_WhenAStyleWithTheSameTagExistsA } @Test - void test_addElementStyle_ThrowsAnException_WhenAStyleWithTheSameTagExistsAlready() { + void addElementStyle_ThrowsAnException_WhenAStyleWithTheSameTagExistsAlready() { try { ElementStyle style = styles.addElementStyle(Tags.SOFTWARE_SYSTEM).color("#ff0000"); styles.add(style); @@ -268,7 +268,7 @@ void test_addElementStyle_ThrowsAnException_WhenAStyleWithTheSameTagExistsAlread } @Test - void test_addRelationshipStyle_ThrowsAnException_WhenATagIsNotSpecified() { + void addRelationshipStyle_ThrowsAnException_WhenATagIsNotSpecified() { try { styles.addRelationshipStyle(""); fail(); @@ -292,7 +292,7 @@ void test_addRelationshipStyle_ThrowsAnException_WhenATagIsNotSpecified() { } @Test - void test_addRelationshipStyleByTag_ThrowsAnException_WhenAStyleWithTheSameTagExistsAlready() { + void addRelationshipStyleByTag_ThrowsAnException_WhenAStyleWithTheSameTagExistsAlready() { try { styles.addRelationshipStyle(Tags.RELATIONSHIP).color("#ff0000"); styles.addRelationshipStyle(Tags.RELATIONSHIP).color("#ff0000"); @@ -304,7 +304,7 @@ void test_addRelationshipStyleByTag_ThrowsAnException_WhenAStyleWithTheSameTagEx } @Test - void test_addRelationshipStyle_ThrowsAnException_WhenAStyleWithTheSameTagExistsAlready() { + void addRelationshipStyle_ThrowsAnException_WhenAStyleWithTheSameTagExistsAlready() { try { RelationshipStyle style = styles.addRelationshipStyle(Tags.RELATIONSHIP).color("#ff0000"); styles.add(style); @@ -316,7 +316,7 @@ void test_addRelationshipStyle_ThrowsAnException_WhenAStyleWithTheSameTagExistsA } @Test - void test_clearElementStyles_RemovesAllElementStyles() { + void clearElementStyles_RemovesAllElementStyles() { styles.addElementStyle(Tags.SOFTWARE_SYSTEM).color("#ff0000"); assertEquals(1, styles.getElements().size()); @@ -325,7 +325,7 @@ void test_clearElementStyles_RemovesAllElementStyles() { } @Test - void test_clearRelationshipStyles_RemovesAllRelationshipStyles() { + void clearRelationshipStyles_RemovesAllRelationshipStyles() { styles.addRelationshipStyle(Tags.RELATIONSHIP).color("#ff0000"); assertEquals(1, styles.getRelationships().size()); diff --git a/structurizr-core/test/unit/com/structurizr/view/SystemContextViewTests.java b/structurizr-core/test/unit/com/structurizr/view/SystemContextViewTests.java index dc37a0cf1..2c68a8e72 100644 --- a/structurizr-core/test/unit/com/structurizr/view/SystemContextViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/SystemContextViewTests.java @@ -19,7 +19,7 @@ public void setUp() { } @Test - void test_construction() { + void construction() { assertEquals("The System - System Context", view.getName()); assertEquals(1, view.getElements().size()); assertSame(view.getElements().iterator().next().getElement(), softwareSystem); @@ -29,14 +29,14 @@ void test_construction() { } @Test - void test_addAllSoftwareSystems_DoesNothing_WhenThereAreNoOtherSoftwareSystems() { + void addAllSoftwareSystems_DoesNothing_WhenThereAreNoOtherSoftwareSystems() { assertEquals(1, view.getElements().size()); view.addAllSoftwareSystems(); assertEquals(1, view.getElements().size()); } @Test - void test_addAllSoftwareSystems_AddsAllSoftwareSystems_WhenThereAreSomeSoftwareSystemsInTheModel() { + void addAllSoftwareSystems_AddsAllSoftwareSystems_WhenThereAreSomeSoftwareSystemsInTheModel() { SoftwareSystem softwareSystemA = model.addSoftwareSystem(Location.External, "System A", "Description"); SoftwareSystem softwareSystemB = model.addSoftwareSystem(Location.External, "System B", "Description"); @@ -49,14 +49,14 @@ void test_addAllSoftwareSystems_AddsAllSoftwareSystems_WhenThereAreSomeSoftwareS } @Test - void test_addAllPeople_DoesNothing_WhenThereAreNoPeople() { + void addAllPeople_DoesNothing_WhenThereAreNoPeople() { assertEquals(1, view.getElements().size()); view.addAllPeople(); assertEquals(1, view.getElements().size()); } @Test - void test_addAllPeople_AddsAllPeople_WhenThereAreSomePeopleInTheModel() { + void addAllPeople_AddsAllPeople_WhenThereAreSomePeopleInTheModel() { Person userA = model.addPerson(Location.External, "User A", "Description"); Person userB = model.addPerson(Location.External, "User B", "Description"); @@ -69,14 +69,14 @@ void test_addAllPeople_AddsAllPeople_WhenThereAreSomePeopleInTheModel() { } @Test - void test_addAllElements_DoesNothing_WhenThereAreNoSoftwareSystemsOrPeople() { + void addAllElements_DoesNothing_WhenThereAreNoSoftwareSystemsOrPeople() { assertEquals(1, view.getElements().size()); view.addAllElements(); assertEquals(1, view.getElements().size()); } @Test - void test_addAllElements_AddsAllSoftwareSystemsAndPeople_WhenThereAreSomeSoftwareSystemsAndPeopleInTheModel() { + void addAllElements_AddsAllSoftwareSystemsAndPeople_WhenThereAreSomeSoftwareSystemsAndPeopleInTheModel() { SoftwareSystem softwareSystemA = model.addSoftwareSystem(Location.External, "System A", "Description"); SoftwareSystem softwareSystemB = model.addSoftwareSystem(Location.External, "System B", "Description"); Person userA = model.addPerson(Location.External, "User A", "Description"); @@ -93,7 +93,7 @@ void test_addAllElements_AddsAllSoftwareSystemsAndPeople_WhenThereAreSomeSoftwar } @Test - void test_addNearestNeighbours_ThrowsAnException_WhenANullElementIsSpecified() { + void addNearestNeighbours_ThrowsAnException_WhenANullElementIsSpecified() { try { view.addNearestNeighbours(null); fail(); @@ -103,7 +103,7 @@ void test_addNearestNeighbours_ThrowsAnException_WhenANullElementIsSpecified() { } @Test - void test_addNearestNeighbours_ThrowsAnException_WhenAnElementThatIsNotAPersonOrSoftwareSystemIsSpecified() { + void addNearestNeighbours_ThrowsAnException_WhenAnElementThatIsNotAPersonOrSoftwareSystemIsSpecified() { Container container = softwareSystem.addContainer("Container", "Description", "Technology"); try { view.addNearestNeighbours(container); @@ -114,14 +114,14 @@ void test_addNearestNeighbours_ThrowsAnException_WhenAnElementThatIsNotAPersonOr } @Test - void test_addNearestNeighbours_DoesNothing_WhenThereAreNoNeighbours() { + void addNearestNeighbours_DoesNothing_WhenThereAreNoNeighbours() { view.addNearestNeighbours(softwareSystem); assertEquals(1, view.getElements().size()); } @Test - void test_addNearestNeighbours_AddsNearestNeighbours_WhenThereAreSomeNearestNeighbours() { + void addNearestNeighbours_AddsNearestNeighbours_WhenThereAreSomeNearestNeighbours() { SoftwareSystem softwareSystemA = model.addSoftwareSystem("A", "Description"); SoftwareSystem softwareSystemB = model.addSoftwareSystem("B", "Description"); Person userA = model.addPerson("User A", "Description"); @@ -170,7 +170,7 @@ void test_addNearestNeighbours_AddsNearestNeighbours_WhenThereAreSomeNearestNeig } @Test - void test_removeSoftwareSystem_ThrowsAnException_WhenPassedNull() { + void removeSoftwareSystem_ThrowsAnException_WhenPassedNull() { try { view.remove((SoftwareSystem) null); fail(); @@ -180,7 +180,7 @@ void test_removeSoftwareSystem_ThrowsAnException_WhenPassedNull() { } @Test - void test_removeSoftwareSystem_DoesNothing_WhenTheSoftwareSystemIsNotInTheView() { + void removeSoftwareSystem_DoesNothing_WhenTheSoftwareSystemIsNotInTheView() { SoftwareSystem anotherSoftwareSystem = model.addSoftwareSystem("Another software system", ""); assertEquals(1, view.getElements().size()); @@ -189,7 +189,7 @@ void test_removeSoftwareSystem_DoesNothing_WhenTheSoftwareSystemIsNotInTheView() } @Test - void test_removeSoftwareSystem_DoesNotRemoveTheSoftwareSystemInFocus() { + void removeSoftwareSystem_DoesNotRemoveTheSoftwareSystemInFocus() { try { view.remove(softwareSystem); fail(); @@ -199,7 +199,7 @@ void test_removeSoftwareSystem_DoesNotRemoveTheSoftwareSystemInFocus() { } @Test - void test_removeSoftwareSystem_RemovesTheSoftwareSystemAndRelationshipsFromTheView() { + void removeSoftwareSystem_RemovesTheSoftwareSystemAndRelationshipsFromTheView() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software system 1", ""); SoftwareSystem softwareSystem2 = model.addSoftwareSystem("Software system 2", ""); softwareSystem1.uses(softwareSystem2, "uses"); @@ -215,7 +215,7 @@ void test_removeSoftwareSystem_RemovesTheSoftwareSystemAndRelationshipsFromTheVi } @Test - void test_removePerson_ThrowsAnException_WhenPassedNull() { + void removePerson_ThrowsAnException_WhenPassedNull() { try { view.remove((Person) null); fail(); @@ -225,7 +225,7 @@ void test_removePerson_ThrowsAnException_WhenPassedNull() { } @Test - void test_removePerson_DoesNothing_WhenThePersonIsNotInTheView() { + void removePerson_DoesNothing_WhenThePersonIsNotInTheView() { Person person = model.addPerson("Person", ""); assertEquals(1, view.getElements().size()); @@ -234,7 +234,7 @@ void test_removePerson_DoesNothing_WhenThePersonIsNotInTheView() { } @Test - void test_removePerson_RemovesThePersonAndRelationshipsFromTheView() { + void removePerson_RemovesThePersonAndRelationshipsFromTheView() { Person person = model.addPerson("Person", ""); person.uses(softwareSystem, "uses"); softwareSystem.delivers(person, "delivers something to"); @@ -249,7 +249,7 @@ void test_removePerson_RemovesThePersonAndRelationshipsFromTheView() { } @Test - void test_addSoftwareSystemWithoutRelationships_DoesNotAddRelationships() { + void addSoftwareSystemWithoutRelationships_DoesNotAddRelationships() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software system 1", ""); SoftwareSystem softwareSystem2 = model.addSoftwareSystem("Software system 2", ""); softwareSystem1.uses(softwareSystem2, "uses"); @@ -261,7 +261,7 @@ void test_addSoftwareSystemWithoutRelationships_DoesNotAddRelationships() { } @Test - void test_addPersonWithoutRelationships_DoesNotAddRelationships() { + void addPersonWithoutRelationships_DoesNotAddRelationships() { Person user = model.addPerson("User", ""); SoftwareSystem softwareSystem = model.addSoftwareSystem("Software system 2", ""); user.uses(softwareSystem, "uses"); @@ -273,7 +273,7 @@ void test_addPersonWithoutRelationships_DoesNotAddRelationships() { } @Test - void test_isEnterpriseBoundaryVisible() { + void isEnterpriseBoundaryVisible() { assertTrue(view.isEnterpriseBoundaryVisible()); // default is true view.setEnterpriseBoundaryVisible(false); @@ -281,7 +281,7 @@ void test_isEnterpriseBoundaryVisible() { } @Test - void test_addDefaultElements() { + void addDefaultElements() { CustomElement element = model.addCustomElement("Custom"); Person user1 = model.addPerson("User 1"); Person user2 = model.addPerson("User 2"); diff --git a/structurizr-core/test/unit/com/structurizr/view/SystemLandscapeViewTests.java b/structurizr-core/test/unit/com/structurizr/view/SystemLandscapeViewTests.java index 9e0838836..69b922957 100644 --- a/structurizr-core/test/unit/com/structurizr/view/SystemLandscapeViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/SystemLandscapeViewTests.java @@ -17,45 +17,45 @@ public void setUp() { } @Test - void test_construction() { + void construction() { assertEquals("System Landscape", view.getName()); assertEquals(0, view.getElements().size()); assertSame(model, view.getModel()); } @Test - void test_getName_WhenNoEnterpriseIsSpecified() { + void getName_WhenNoEnterpriseIsSpecified() { assertEquals("System Landscape", view.getName()); } @Test - void test_getName_WhenAnEnterpriseIsSpecified() { + void getName_WhenAnEnterpriseIsSpecified() { model.setEnterprise(new Enterprise("Widgets Limited")); assertEquals("System Landscape for Widgets Limited", view.getName()); } @Test - void test_getName_WhenAnEmptyEnterpriseNameIsSpecified() { + void getName_WhenAnEmptyEnterpriseNameIsSpecified() { assertThrows(IllegalArgumentException.class, () -> { model.setEnterprise(new Enterprise("")); }); } @Test - void test_getName_WhenANullEnterpriseNameIsSpecified() { + void getName_WhenANullEnterpriseNameIsSpecified() { assertThrows(IllegalArgumentException.class, () -> { model.setEnterprise(new Enterprise(null)); }); } @Test - void test_addAllSoftwareSystems_DoesNothing_WhenThereAreNoOtherSoftwareSystems() { + void addAllSoftwareSystems_DoesNothing_WhenThereAreNoOtherSoftwareSystems() { view.addAllSoftwareSystems(); assertEquals(0, view.getElements().size()); } @Test - void test_addAllSoftwareSystems_AddsAllSoftwareSystems_WhenThereAreSomeSoftwareSystemsInTheModel() { + void addAllSoftwareSystems_AddsAllSoftwareSystems_WhenThereAreSomeSoftwareSystemsInTheModel() { SoftwareSystem softwareSystemA = model.addSoftwareSystem(Location.External, "System A", "Description"); SoftwareSystem softwareSystemB = model.addSoftwareSystem(Location.External, "System B", "Description"); @@ -67,13 +67,13 @@ void test_addAllSoftwareSystems_AddsAllSoftwareSystems_WhenThereAreSomeSoftwareS } @Test - void test_addAllPeople_DoesNothing_WhenThereAreNoPeople() { + void addAllPeople_DoesNothing_WhenThereAreNoPeople() { view.addAllPeople(); assertEquals(0, view.getElements().size()); } @Test - void test_addAllPeople_AddsAllPeople_WhenThereAreSomePeopleInTheModel() { + void addAllPeople_AddsAllPeople_WhenThereAreSomePeopleInTheModel() { Person userA = model.addPerson("User A", "Description"); Person userB = model.addPerson("User B", "Description"); @@ -85,13 +85,13 @@ void test_addAllPeople_AddsAllPeople_WhenThereAreSomePeopleInTheModel() { } @Test - void test_addAllElements_DoesNothing_WhenThereAreNoSoftwareSystemsOrPeople() { + void addAllElements_DoesNothing_WhenThereAreNoSoftwareSystemsOrPeople() { view.addAllElements(); assertEquals(0, view.getElements().size()); } @Test - void test_addAllElements_AddsAllSoftwareSystemsAndPeople_WhenThereAreSomeSoftwareSystemsAndPeopleInTheModel() { + void addAllElements_AddsAllSoftwareSystemsAndPeople_WhenThereAreSomeSoftwareSystemsAndPeopleInTheModel() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); Person person = model.addPerson("Person", "Description"); @@ -103,7 +103,7 @@ void test_addAllElements_AddsAllSoftwareSystemsAndPeople_WhenThereAreSomeSoftwar } @Test - void test_isEnterpriseBoundaryVisible() { + void isEnterpriseBoundaryVisible() { assertTrue(view.isEnterpriseBoundaryVisible()); // default is true view.setEnterpriseBoundaryVisible(false); @@ -111,7 +111,7 @@ void test_isEnterpriseBoundaryVisible() { } @Test - void test_addNearestNeighbours_ThrowsAnException_WhenANullElementIsSpecified() { + void addNearestNeighbours_ThrowsAnException_WhenANullElementIsSpecified() { try { view.addNearestNeighbours(null); fail(); @@ -121,7 +121,7 @@ void test_addNearestNeighbours_ThrowsAnException_WhenANullElementIsSpecified() { } @Test - void test_addNearestNeighbours_ThrowsAnException_WhenAnElementThatIsNotAPersonOrSoftwareSystemIsSpecified() { + void addNearestNeighbours_ThrowsAnException_WhenAnElementThatIsNotAPersonOrSoftwareSystemIsSpecified() { SoftwareSystem softwareSystem = model.addSoftwareSystem("The System", "Description"); Container container = softwareSystem.addContainer("Container", "Description", "Technology"); try { @@ -133,7 +133,7 @@ void test_addNearestNeighbours_ThrowsAnException_WhenAnElementThatIsNotAPersonOr } @Test - void test_addNearestNeighbours_DoesNothing_WhenThereAreNoNeighbours() { + void addNearestNeighbours_DoesNothing_WhenThereAreNoNeighbours() { SoftwareSystem softwareSystem = model.addSoftwareSystem("The System", "Description"); view.addNearestNeighbours(softwareSystem); @@ -141,7 +141,7 @@ void test_addNearestNeighbours_DoesNothing_WhenThereAreNoNeighbours() { } @Test - void test_addNearestNeighbours_AddsNearestNeighbours_WhenThereAreSomeNearestNeighbours() { + void addNearestNeighbours_AddsNearestNeighbours_WhenThereAreSomeNearestNeighbours() { SoftwareSystem softwareSystem = model.addSoftwareSystem("The System", "Description"); SoftwareSystem softwareSystemA = model.addSoftwareSystem("System A", "Description"); SoftwareSystem softwareSystemB = model.addSoftwareSystem("System B", "Description"); @@ -191,7 +191,7 @@ void test_addNearestNeighbours_AddsNearestNeighbours_WhenThereAreSomeNearestNeig } @Test - void test_addDefaultElements() { + void addDefaultElements() { CustomElement element = model.addCustomElement("Custom"); Person user = model.addPerson("User"); SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1"); diff --git a/structurizr-core/test/unit/com/structurizr/view/TerminologyTests.java b/structurizr-core/test/unit/com/structurizr/view/TerminologyTests.java index 13398c7f8..7f19c8959 100644 --- a/structurizr-core/test/unit/com/structurizr/view/TerminologyTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/TerminologyTests.java @@ -9,7 +9,7 @@ public class TerminologyTests { @Test - void test_findTerminology() { + void findTerminology() { Workspace workspace = new Workspace("Name", "Description"); Terminology terminology = workspace.getViews().getConfiguration().getTerminology(); Person person = workspace.getModel().addPerson("Name"); diff --git a/structurizr-core/test/unit/com/structurizr/view/VertexTests.java b/structurizr-core/test/unit/com/structurizr/view/VertexTests.java index 94e81fec0..f9377d309 100644 --- a/structurizr-core/test/unit/com/structurizr/view/VertexTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/VertexTests.java @@ -8,7 +8,7 @@ public class VertexTests { @Test - void test_equals() { + void equals() { Vertex vertex1 = new Vertex(123, 456); Vertex vertex2 = new Vertex(123, 456); Vertex vertex3 = new Vertex(456, 123); diff --git a/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java b/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java index b3741b231..d490ae9fe 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java @@ -28,7 +28,7 @@ private Workspace createWorkspace() { } @Test - void test_createCustomView_ThrowsAnException_WhenANullKeyIsSpecified() { + void createCustomView_ThrowsAnException_WhenANullKeyIsSpecified() { try { new Workspace("", "").getViews().createCustomView(null, "Title", "Description"); fail(); @@ -38,7 +38,7 @@ void test_createCustomView_ThrowsAnException_WhenANullKeyIsSpecified() { } @Test - void test_createCustomView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { + void createCustomView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { try { new Workspace("", "").getViews().createCustomView(" ", "Title", "Description"); fail(); @@ -48,7 +48,7 @@ void test_createCustomView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { } @Test - void test_createCustomView_ThrowsAnException_WhenADuplicateKeyIsSpecified() { + void createCustomView_ThrowsAnException_WhenADuplicateKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); workspace.getViews().createCustomView("key", "Title", "Description"); @@ -60,14 +60,14 @@ void test_createCustomView_ThrowsAnException_WhenADuplicateKeyIsSpecified() { } @Test - void test_createCustomView_DoesNotThrowAnException_WhenUniqueKeysAreSpecified() { + void createCustomView_DoesNotThrowAnException_WhenUniqueKeysAreSpecified() { Workspace workspace = new Workspace("Name", "Description"); workspace.getViews().createCustomView("key1", "Title", "Description"); workspace.getViews().createCustomView("key2", "Title", "Description"); } @Test - void test_createCustomView() { + void createCustomView() { Workspace workspace = new Workspace("Name", "Description"); CustomView customView = workspace.getViews().createCustomView("key", "Title", "Description"); assertEquals("key", customView.getKey()); @@ -78,7 +78,7 @@ void test_createCustomView() { } @Test - void test_createSystemLandscapeView_ThrowsAnException_WhenANullKeyIsSpecified() { + void createSystemLandscapeView_ThrowsAnException_WhenANullKeyIsSpecified() { try { new Workspace("", "").getViews().createSystemLandscapeView(null, "Description"); fail(); @@ -88,7 +88,7 @@ void test_createSystemLandscapeView_ThrowsAnException_WhenANullKeyIsSpecified() } @Test - void test_createSystemLandscapeView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { + void createSystemLandscapeView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { try { new Workspace("", "").getViews().createSystemLandscapeView(" ", "Description"); fail(); @@ -98,7 +98,7 @@ void test_createSystemLandscapeView_ThrowsAnException_WhenAnEmptyKeyIsSpecified( } @Test - void test_createSystemLandscapeView_ThrowsAnException_WhenADuplicateKeyIsSpecified() { + void createSystemLandscapeView_ThrowsAnException_WhenADuplicateKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); workspace.getViews().createSystemLandscapeView("key", "Description"); @@ -110,14 +110,14 @@ void test_createSystemLandscapeView_ThrowsAnException_WhenADuplicateKeyIsSpecifi } @Test - void test_createSystemLandscapeView_DoesNotThrowAnException_WhenUniqueKeysAreSpecified() { + void createSystemLandscapeView_DoesNotThrowAnException_WhenUniqueKeysAreSpecified() { Workspace workspace = new Workspace("Name", "Description"); workspace.getViews().createSystemLandscapeView("key1", "Description"); workspace.getViews().createSystemLandscapeView("key2", "Description"); } @Test - void test_createSystemLandscapeView() { + void createSystemLandscapeView() { Workspace workspace = new Workspace("Name", "Description"); SystemLandscapeView systemLandscapeView = workspace.getViews().createSystemLandscapeView("key", "Description"); assertEquals("key", systemLandscapeView.getKey()); @@ -125,7 +125,7 @@ void test_createSystemLandscapeView() { } @Test - void test_createSystemContextView_ThrowsAnException_WhenASoftwareSystemIsSpecified() { + void createSystemContextView_ThrowsAnException_WhenASoftwareSystemIsSpecified() { try { new Workspace("", "").getViews().createSystemContextView(null, null, "Description"); fail(); @@ -135,7 +135,7 @@ void test_createSystemContextView_ThrowsAnException_WhenASoftwareSystemIsSpecifi } @Test - void test_createSystemContextView_ThrowsAnException_WhenANullKeyIsSpecified() { + void createSystemContextView_ThrowsAnException_WhenANullKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); @@ -147,7 +147,7 @@ void test_createSystemContextView_ThrowsAnException_WhenANullKeyIsSpecified() { } @Test - void test_createSystemContextView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { + void createSystemContextView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); @@ -159,7 +159,7 @@ void test_createSystemContextView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() } @Test - void test_createSystemContextView_ThrowsAnException_WhenADuplicateKeyIsSpecified() { + void createSystemContextView_ThrowsAnException_WhenADuplicateKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); @@ -172,7 +172,7 @@ void test_createSystemContextView_ThrowsAnException_WhenADuplicateKeyIsSpecified } @Test - void test_createSystemContextView() { + void createSystemContextView() { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); SystemContextView systemContextView = workspace.getViews().createSystemContextView(softwareSystem, "key", "Description"); @@ -182,7 +182,7 @@ void test_createSystemContextView() { } @Test - void test_createContainerView_ThrowsAnException_WhenASoftwareSystemIsSpecified() { + void createContainerView_ThrowsAnException_WhenASoftwareSystemIsSpecified() { try { new Workspace("", "").getViews().createContainerView(null, null, "Description"); fail(); @@ -192,7 +192,7 @@ void test_createContainerView_ThrowsAnException_WhenASoftwareSystemIsSpecified() } @Test - void test_createContainerView_ThrowsAnException_WhenANullKeyIsSpecified() { + void createContainerView_ThrowsAnException_WhenANullKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); @@ -204,7 +204,7 @@ void test_createContainerView_ThrowsAnException_WhenANullKeyIsSpecified() { } @Test - void test_createContainerView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { + void createContainerView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); @@ -216,7 +216,7 @@ void test_createContainerView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { } @Test - void test_createContainerView_ThrowsAnException_WhenADuplicateKeyIsSpecified() { + void createContainerView_ThrowsAnException_WhenADuplicateKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); @@ -229,7 +229,7 @@ void test_createContainerView_ThrowsAnException_WhenADuplicateKeyIsSpecified() { } @Test - void test_createContainerView() { + void createContainerView() { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); ContainerView containerView = workspace.getViews().createContainerView(softwareSystem, "key", "Description"); @@ -239,7 +239,7 @@ void test_createContainerView() { } @Test - void test_createComponentView_ThrowsAnException_WhenASoftwareSystemIsSpecified() { + void createComponentView_ThrowsAnException_WhenASoftwareSystemIsSpecified() { try { new Workspace("", "").getViews().createComponentView(null, null, "Description"); fail(); @@ -249,7 +249,7 @@ void test_createComponentView_ThrowsAnException_WhenASoftwareSystemIsSpecified() } @Test - void test_createComponentView_ThrowsAnException_WhenANullKeyIsSpecified() { + void createComponentView_ThrowsAnException_WhenANullKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); @@ -262,7 +262,7 @@ void test_createComponentView_ThrowsAnException_WhenANullKeyIsSpecified() { } @Test - void test_createComponentView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { + void createComponentView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); @@ -275,7 +275,7 @@ void test_createComponentView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { } @Test - void test_createComponentView_ThrowsAnException_WhenADuplicateKeyIsSpecified() { + void createComponentView_ThrowsAnException_WhenADuplicateKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); @@ -289,7 +289,7 @@ void test_createComponentView_ThrowsAnException_WhenADuplicateKeyIsSpecified() { } @Test - void test_createComponentView() { + void createComponentView() { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); Container container = softwareSystem.addContainer("Container", "Description", "Technology"); @@ -301,7 +301,7 @@ void test_createComponentView() { } @Test - void test_createDynamicView_ThrowsAnException_WhenANullKeyIsSpecified() { + void createDynamicView_ThrowsAnException_WhenANullKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); workspace.getViews().createDynamicView(null, "Description"); @@ -312,7 +312,7 @@ void test_createDynamicView_ThrowsAnException_WhenANullKeyIsSpecified() { } @Test - void test_createDynamicView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { + void createDynamicView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); workspace.getViews().createDynamicView(" ", "Description"); @@ -323,7 +323,7 @@ void test_createDynamicView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { } @Test - void test_createDynamicView() { + void createDynamicView() { Workspace workspace = new Workspace("Name", "Description"); DynamicView dynamicView = workspace.getViews().createDynamicView("key", "Description"); @@ -334,7 +334,7 @@ void test_createDynamicView() { } @Test - void test_createDynamicViewForASoftwareSystem_ThrowsAnException_WhenANullSoftwareSystemIsSpecified() { + void createDynamicViewForASoftwareSystem_ThrowsAnException_WhenANullSoftwareSystemIsSpecified() { try { new Workspace("", "").getViews().createDynamicView((SoftwareSystem) null, "key", "Description"); fail(); @@ -344,7 +344,7 @@ void test_createDynamicViewForASoftwareSystem_ThrowsAnException_WhenANullSoftwar } @Test - void test_createDynamicViewForASoftwareSystem_ThrowsAnException_WhenANullKeyIsSpecified() { + void createDynamicViewForASoftwareSystem_ThrowsAnException_WhenANullKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); @@ -356,7 +356,7 @@ void test_createDynamicViewForASoftwareSystem_ThrowsAnException_WhenANullKeyIsSp } @Test - void test_createDynamicViewForASoftwareSystem_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { + void createDynamicViewForASoftwareSystem_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); @@ -368,7 +368,7 @@ void test_createDynamicViewForASoftwareSystem_ThrowsAnException_WhenAnEmptyKeyIs } @Test - void test_createDynamicViewForASoftwareSystem_ThrowsAnException_WhenADuplicateKeyIsUsed() { + void createDynamicViewForASoftwareSystem_ThrowsAnException_WhenADuplicateKeyIsUsed() { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name", "Description"); @@ -382,7 +382,7 @@ void test_createDynamicViewForASoftwareSystem_ThrowsAnException_WhenADuplicateKe } @Test - void test_createDynamicViewForSoftwareSystem() { + void createDynamicViewForSoftwareSystem() { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name", "Description"); @@ -394,7 +394,7 @@ void test_createDynamicViewForSoftwareSystem() { } @Test - void test_createDynamicViewForAContainer_ThrowsAnException_WhenANullContainerIsSpecified() { + void createDynamicViewForAContainer_ThrowsAnException_WhenANullContainerIsSpecified() { try { new Workspace("", "").getViews().createDynamicView((Container) null, "key", "Description"); fail(); @@ -404,7 +404,7 @@ void test_createDynamicViewForAContainer_ThrowsAnException_WhenANullContainerIsS } @Test - void test_createDynamicViewForAContainer_ThrowsAnException_WhenANullKeyIsSpecified() { + void createDynamicViewForAContainer_ThrowsAnException_WhenANullKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); @@ -417,7 +417,7 @@ void test_createDynamicViewForAContainer_ThrowsAnException_WhenANullKeyIsSpecifi } @Test - void test_createDynamicViewForAContainer_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { + void createDynamicViewForAContainer_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); @@ -430,7 +430,7 @@ void test_createDynamicViewForAContainer_ThrowsAnException_WhenAnEmptyKeyIsSpeci } @Test - void test_createDynamicViewForContainer() { + void createDynamicViewForContainer() { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name", "Description"); Container container = softwareSystem.addContainer("Container", "Description", "Technology"); @@ -443,7 +443,7 @@ void test_createDynamicViewForContainer() { } @Test - void test_createDeploymentView_ThrowsAnException_WhenANullKeyIsSpecified() { + void createDeploymentView_ThrowsAnException_WhenANullKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); workspace.getViews().createDeploymentView(null, "Description"); @@ -454,7 +454,7 @@ void test_createDeploymentView_ThrowsAnException_WhenANullKeyIsSpecified() { } @Test - void test_createDeploymentView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { + void createDeploymentView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); workspace.getViews().createDeploymentView(" ", "Description"); @@ -465,7 +465,7 @@ void test_createDeploymentView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { } @Test - void test_createDeploymentView_ThrowsAnException_WhenADuplicateKeyIsUsed() { + void createDeploymentView_ThrowsAnException_WhenADuplicateKeyIsUsed() { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name", "Description"); @@ -479,7 +479,7 @@ void test_createDeploymentView_ThrowsAnException_WhenADuplicateKeyIsUsed() { } @Test - void test_createDeploymentView() { + void createDeploymentView() { Workspace workspace = new Workspace("Name", "Description"); DeploymentView deploymentView = workspace.getViews().createDeploymentView("key", "Description"); @@ -489,7 +489,7 @@ void test_createDeploymentView() { } @Test - void test_createDeploymentViewForASoftwareSystem_ThrowsAnException_WhenANullSoftwareSystemIsSpecified() { + void createDeploymentViewForASoftwareSystem_ThrowsAnException_WhenANullSoftwareSystemIsSpecified() { try { new Workspace("", "").getViews().createDeploymentView((SoftwareSystem) null, "key", "Description"); fail(); @@ -499,7 +499,7 @@ void test_createDeploymentViewForASoftwareSystem_ThrowsAnException_WhenANullSoft } @Test - void test_createDeploymentViewForASoftwareSystem_ThrowsAnException_WhenANullKeyIsSpecified() { + void createDeploymentViewForASoftwareSystem_ThrowsAnException_WhenANullKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); @@ -511,7 +511,7 @@ void test_createDeploymentViewForASoftwareSystem_ThrowsAnException_WhenANullKeyI } @Test - void test_createDeploymentViewForASoftwareSystem_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { + void createDeploymentViewForASoftwareSystem_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); @@ -523,7 +523,7 @@ void test_createDeploymentViewForASoftwareSystem_ThrowsAnException_WhenAnEmptyKe } @Test - void test_createDeploymentViewForASoftwareSystem_ThrowsAnException_WhenADuplicateKeyIsUsed() { + void createDeploymentViewForASoftwareSystem_ThrowsAnException_WhenADuplicateKeyIsUsed() { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name", "Description"); @@ -537,7 +537,7 @@ void test_createDeploymentViewForASoftwareSystem_ThrowsAnException_WhenADuplicat } @Test - void test_createDeploymentViewForSoftwareSystem() { + void createDeploymentViewForSoftwareSystem() { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name", "Description"); @@ -548,7 +548,7 @@ void test_createDeploymentViewForSoftwareSystem() { } @Test - void test_createFilteredView_ThrowsAnException_WhenANullViewIsSpecified() { + void createFilteredView_ThrowsAnException_WhenANullViewIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); workspace.getViews().createFilteredView(null, "key", "Description", FilterMode.Include, "tag1", "tag2"); @@ -559,7 +559,7 @@ void test_createFilteredView_ThrowsAnException_WhenANullViewIsSpecified() { } @Test - void test_createFilteredView_ThrowsAnException_WhenANullKeyIsSpecified() { + void createFilteredView_ThrowsAnException_WhenANullKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("systemLandscape", "Description"); @@ -571,7 +571,7 @@ void test_createFilteredView_ThrowsAnException_WhenANullKeyIsSpecified() { } @Test - void test_createFilteredView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { + void createFilteredView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("systemLandscape", "Description"); @@ -583,7 +583,7 @@ void test_createFilteredView_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { } @Test - void test_createFilteredView_ThrowsAnException_WhenADuplicateKeyIsUsed() { + void createFilteredView_ThrowsAnException_WhenADuplicateKeyIsUsed() { Workspace workspace = new Workspace("Name", "Description"); SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("systemLandscape", "Description"); workspace.getViews().createFilteredView(view, "filtered", "Description", FilterMode.Include, "tag1", "tag2"); @@ -596,7 +596,7 @@ void test_createFilteredView_ThrowsAnException_WhenADuplicateKeyIsUsed() { } @Test - void test_createFilteredView() { + void createFilteredView() { Workspace workspace = new Workspace("Name", "Description"); SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("systemLandscape", "Description"); FilteredView filteredView = workspace.getViews().createFilteredView(view, "key", "Description", FilterMode.Include, "tag1", "tag2"); @@ -610,7 +610,7 @@ void test_createFilteredView() { } @Test - void test_copyLayoutInformationFrom_WhenAViewKeyIsNotSetButTheViewTitlesMatch() { + void copyLayoutInformationFrom_WhenAViewKeyIsNotSetButTheViewTitlesMatch() { Workspace workspace1 = createWorkspace(); SoftwareSystem softwareSystem1 = workspace1.getModel().getSoftwareSystemWithName("Software System"); SystemContextView view1 = workspace1.getViews().createSystemContextView(softwareSystem1, "context", "Description"); @@ -631,7 +631,7 @@ void test_copyLayoutInformationFrom_WhenAViewKeyIsNotSetButTheViewTitlesMatch() } @Test - void test_copyLayoutInformationFrom_WhenAViewKeyHasBeenIntroduced() { + void copyLayoutInformationFrom_WhenAViewKeyHasBeenIntroduced() { Workspace workspace1 = createWorkspace(); SoftwareSystem softwareSystem1 = workspace1.getModel().getSoftwareSystemWithName("Software System"); SystemContextView view1 = workspace1.getViews().createSystemContextView(softwareSystem1, "context", "Description"); @@ -651,7 +651,7 @@ void test_copyLayoutInformationFrom_WhenAViewKeyHasBeenIntroduced() { } @Test - void test_copyLayoutInformationFrom_IgnoresThePaperSize_WhenThePaperSizeIsSet() { + void copyLayoutInformationFrom_IgnoresThePaperSize_WhenThePaperSizeIsSet() { Workspace workspace1 = createWorkspace(); SoftwareSystem softwareSystem1 = workspace1.getModel().getSoftwareSystemWithName("Software System"); SystemContextView view1 = workspace1.getViews().createSystemContextView(softwareSystem1, "context", "Description"); @@ -667,7 +667,7 @@ void test_copyLayoutInformationFrom_IgnoresThePaperSize_WhenThePaperSizeIsSet() } @Test - void test_copyLayoutInformationFrom_CopiesThePaperSize_WhenThePaperSizeIsNotSet() { + void copyLayoutInformationFrom_CopiesThePaperSize_WhenThePaperSizeIsNotSet() { Workspace workspace1 = createWorkspace(); SoftwareSystem softwareSystem1 = workspace1.getModel().getSoftwareSystemWithName("Software System"); SystemContextView view1 = workspace1.getViews().createSystemContextView(softwareSystem1, "context", "Description"); @@ -682,7 +682,7 @@ void test_copyLayoutInformationFrom_CopiesThePaperSize_WhenThePaperSizeIsNotSet( } @Test - void test_copyLayoutInformationFrom_WhenTheSystemLandscapeViewKeysMatch() { + void copyLayoutInformationFrom_WhenTheSystemLandscapeViewKeysMatch() { Workspace workspace1 = createWorkspace(); SoftwareSystem softwareSystem1 = workspace1.getModel().getSoftwareSystemWithName("Software System"); SystemLandscapeView view1 = workspace1.getViews().createSystemLandscapeView("landscape", "Description"); @@ -701,7 +701,7 @@ void test_copyLayoutInformationFrom_WhenTheSystemLandscapeViewKeysMatch() { } @Test - void test_copyLayoutInformationFrom_DoesNotDoAnythingIfThereIsNoSystemLandscapeViewToCopyInformationFrom() { + void copyLayoutInformationFrom_DoesNotDoAnythingIfThereIsNoSystemLandscapeViewToCopyInformationFrom() { Workspace workspace1 = createWorkspace(); Workspace workspace2 = createWorkspace(); @@ -715,7 +715,7 @@ void test_copyLayoutInformationFrom_DoesNotDoAnythingIfThereIsNoSystemLandscapeV } @Test - void test_copyLayoutInformationFrom_WhenTheSystemContextViewKeysMatch() { + void copyLayoutInformationFrom_WhenTheSystemContextViewKeysMatch() { Workspace workspace1 = createWorkspace(); SoftwareSystem softwareSystem1 = workspace1.getModel().getSoftwareSystemWithName("Software System"); SystemContextView view1 = workspace1.getViews().createSystemContextView(softwareSystem1, "context", "Description"); @@ -734,7 +734,7 @@ void test_copyLayoutInformationFrom_WhenTheSystemContextViewKeysMatch() { } @Test - void test_copyLayoutInformationFrom_DoesNotDoAnythingIfThereIsNoSystemContextViewToCopyInformationFrom() { + void copyLayoutInformationFrom_DoesNotDoAnythingIfThereIsNoSystemContextViewToCopyInformationFrom() { Workspace workspace1 = createWorkspace(); Workspace workspace2 = createWorkspace(); @@ -748,7 +748,7 @@ void test_copyLayoutInformationFrom_DoesNotDoAnythingIfThereIsNoSystemContextVie } @Test - void test_copyLayoutInformationFrom_WhenTheContainerViewKeysMatch() { + void copyLayoutInformationFrom_WhenTheContainerViewKeysMatch() { Workspace workspace1 = createWorkspace(); SoftwareSystem softwareSystem1 = workspace1.getModel().getSoftwareSystemWithName("Software System"); ContainerView view1 = workspace1.getViews().createContainerView(softwareSystem1, "containers", "Description"); @@ -767,7 +767,7 @@ void test_copyLayoutInformationFrom_WhenTheContainerViewKeysMatch() { } @Test - void test_copyLayoutInformationFrom_DoesNotDoAnythingIfThereIsNoContainerViewToCopyInformationFrom() { + void copyLayoutInformationFrom_DoesNotDoAnythingIfThereIsNoContainerViewToCopyInformationFrom() { Workspace workspace1 = createWorkspace(); Workspace workspace2 = createWorkspace(); @@ -781,7 +781,7 @@ void test_copyLayoutInformationFrom_DoesNotDoAnythingIfThereIsNoContainerViewToC } @Test - void test_copyLayoutInformationFrom_WhenTheComponentViewKeysMatch() { + void copyLayoutInformationFrom_WhenTheComponentViewKeysMatch() { Workspace workspace1 = createWorkspace(); Container container1 = workspace1.getModel().getSoftwareSystemWithName("Software System").getContainerWithName("Container"); ComponentView view1 = workspace1.getViews().createComponentView(container1, "containers", "Description"); @@ -800,7 +800,7 @@ void test_copyLayoutInformationFrom_WhenTheComponentViewKeysMatch() { } @Test - void test_copyLayoutInformationFrom_DoesNotDoAnythingIfThereIsNoComponentViewToCopyInformationFrom() { + void copyLayoutInformationFrom_DoesNotDoAnythingIfThereIsNoComponentViewToCopyInformationFrom() { Workspace workspace1 = createWorkspace(); Workspace workspace2 = createWorkspace(); @@ -814,7 +814,7 @@ void test_copyLayoutInformationFrom_DoesNotDoAnythingIfThereIsNoComponentViewToC } @Test - void test_copyLayoutInformationFrom_WhenTheDynamicViewKeysMatch() { + void copyLayoutInformationFrom_WhenTheDynamicViewKeysMatch() { Workspace workspace1 = createWorkspace(); Person person1 = workspace1.getModel().getPersonWithName("Person"); SoftwareSystem softwareSystem1 = workspace1.getModel().getSoftwareSystemWithName("Software System"); @@ -835,7 +835,7 @@ void test_copyLayoutInformationFrom_WhenTheDynamicViewKeysMatch() { } @Test - void test_copyLayoutInformationFrom_DoesNotDoAnythingIfThereIsNoDynamicViewToCopyInformationFrom() { + void copyLayoutInformationFrom_DoesNotDoAnythingIfThereIsNoDynamicViewToCopyInformationFrom() { Workspace workspace1 = createWorkspace(); Workspace workspace2 = createWorkspace(); @@ -850,7 +850,7 @@ void test_copyLayoutInformationFrom_DoesNotDoAnythingIfThereIsNoDynamicViewToCop } @Test - void test_copyLayoutInformationFrom_WhenTheDeploymentViewKeysMatch() { + void copyLayoutInformationFrom_WhenTheDeploymentViewKeysMatch() { Workspace workspace1 = createWorkspace(); DeploymentNode deploymentNode1 = workspace1.getModel().getDeploymentNodeWithName("Deployment Node"); DeploymentView view1 = workspace1.getViews().createDeploymentView("key", "Description"); @@ -871,7 +871,7 @@ void test_copyLayoutInformationFrom_WhenTheDeploymentViewKeysMatch() { } @Test - void test_copyLayoutInformationFrom_DoesNotDoAnythingIfThereIsNoDeploymentViewToCopyInformationFrom() { + void copyLayoutInformationFrom_DoesNotDoAnythingIfThereIsNoDeploymentViewToCopyInformationFrom() { Workspace workspace1 = createWorkspace(); Workspace workspace2 = createWorkspace(); @@ -910,7 +910,7 @@ private HashSet relationshipViewsFor(Relationship... relations } @Test - void test_hydrate() { + void hydrate() { Workspace workspace = new Workspace("Name", "Description"); Model model = workspace.getModel(); ViewSet views = workspace.getViews(); @@ -1004,7 +1004,7 @@ void test_hydrate() { } @Test - void test_setEnterpriseContextViews_IsSupportedForOlderWorkspaces() { + void setEnterpriseContextViews_IsSupportedForOlderWorkspaces() { ViewSet views = new Workspace("", "").getViews(); SystemLandscapeView systemLandscapeView = views.createSystemLandscapeView("key", "Description"); views.setEnterpriseContextViews(Collections.singleton(systemLandscapeView)); @@ -1013,7 +1013,7 @@ void test_setEnterpriseContextViews_IsSupportedForOlderWorkspaces() { } @Test - void test_createDefaultViews() { + void createDefaultViews() { Workspace workspace = new Workspace("Name", "Description"); Model model = workspace.getModel(); ViewSet views = workspace.getViews(); @@ -1075,7 +1075,7 @@ void test_createDefaultViews() { } @Test - void test_copyLayoutInformationFrom_DoesNothing_WhenMergeFromRemoteIsSetToFalse() { + void copyLayoutInformationFrom_DoesNothing_WhenMergeFromRemoteIsSetToFalse() { Workspace workspace1 = createWorkspace(); SoftwareSystem softwareSystem1 = workspace1.getModel().getSoftwareSystemWithName("Software System"); SystemLandscapeView view1 = workspace1.getViews().createSystemLandscapeView("landscape", "Description"); @@ -1095,7 +1095,7 @@ void test_copyLayoutInformationFrom_DoesNothing_WhenMergeFromRemoteIsSetToFalse( } @Test - void test_view_ordering() { + void view_ordering() { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); Container container = softwareSystem.addContainer("Container"); diff --git a/structurizr-core/test/unit/com/structurizr/view/ViewTests.java b/structurizr-core/test/unit/com/structurizr/view/ViewTests.java index 8b2fde869..8017e46db 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ViewTests.java @@ -13,7 +13,7 @@ public class ViewTests extends AbstractWorkspaceTestBase { @Test - void test_construction() { + void construction() { SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); StaticView view = new SystemContextView(softwareSystem, "key", "Description"); assertEquals("key", view.getKey()); @@ -22,14 +22,14 @@ void test_construction() { } @Test - void test_construction_WhenTheViewKeyContainsAForwardSlashCharacter() { + void construction_WhenTheViewKeyContainsAForwardSlashCharacter() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); StaticView view = new SystemContextView(softwareSystem, "key/1", "Description"); assertEquals("key_1", view.getKey()); } @Test - void test_addAllSoftwareSystems_DoesNothing_WhenThereAreNoOtherSoftwareSystemsInTheModel() { + void addAllSoftwareSystems_DoesNothing_WhenThereAreNoOtherSoftwareSystemsInTheModel() { SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); StaticView view = new SystemContextView(softwareSystem, "context", "Description"); assertEquals(1, view.getElements().size()); @@ -38,7 +38,7 @@ void test_addAllSoftwareSystems_DoesNothing_WhenThereAreNoOtherSoftwareSystemsIn } @Test - void test_addAllSoftwareSystems_DoesAddAllSoftwareSystems_WhenThereAreSoftwareSystemsInTheModel() { + void addAllSoftwareSystems_DoesAddAllSoftwareSystems_WhenThereAreSoftwareSystemsInTheModel() { SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); SoftwareSystem softwareSystemA = model.addSoftwareSystem(Location.Unspecified, "System A", "Description"); SoftwareSystem softwareSystemB = model.addSoftwareSystem(Location.Unspecified, "System B", "Description"); @@ -56,7 +56,7 @@ void test_addAllSoftwareSystems_DoesAddAllSoftwareSystems_WhenThereAreSoftwareSy } @Test - void test_addSoftwareSystem_ThrowsAnException_WhenGivenNull() { + void addSoftwareSystem_ThrowsAnException_WhenGivenNull() { SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); StaticView view = new SystemContextView(softwareSystem, "context", "Description"); @@ -69,7 +69,7 @@ void test_addSoftwareSystem_ThrowsAnException_WhenGivenNull() { } @Test - void test_addSoftwareSystem_AddsTheSoftwareSystem_WhenTheSoftwareSystemIsInTheModel() { + void addSoftwareSystem_AddsTheSoftwareSystem_WhenTheSoftwareSystemIsInTheModel() { SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); SoftwareSystem softwareSystemA = model.addSoftwareSystem(Location.Unspecified, "System A", "Description"); @@ -82,7 +82,7 @@ void test_addSoftwareSystem_AddsTheSoftwareSystem_WhenTheSoftwareSystemIsInTheMo } @Test - void test_addAllPeople_DoesNothing_WhenThereAreNoPeopleInTheModel() { + void addAllPeople_DoesNothing_WhenThereAreNoPeopleInTheModel() { SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); StaticView view = new SystemContextView(softwareSystem, "context", "Description"); @@ -93,7 +93,7 @@ void test_addAllPeople_DoesNothing_WhenThereAreNoPeopleInTheModel() { } @Test - void test_addAllPeople_DoesAddAllPeople_WhenThereArePeopleInTheModel() { + void addAllPeople_DoesAddAllPeople_WhenThereArePeopleInTheModel() { SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); Person person1 = model.addPerson(Location.Unspecified, "Person 1", "Description"); Person person2 = model.addPerson(Location.Unspecified, "Person 2", "Description"); @@ -111,7 +111,7 @@ void test_addAllPeople_DoesAddAllPeople_WhenThereArePeopleInTheModel() { } @Test - void test_addPerson_ThrowsAnException_WhenGivenNull() { + void addPerson_ThrowsAnException_WhenGivenNull() { SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); StaticView view = new SystemContextView(softwareSystem, "context", "Description"); try { @@ -123,7 +123,7 @@ void test_addPerson_ThrowsAnException_WhenGivenNull() { } @Test - void test_addPerson_AddsThePerson_WhenThPersonIsInTheModel() { + void addPerson_AddsThePerson_WhenThPersonIsInTheModel() { SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); StaticView view = new SystemContextView(softwareSystem, "context", "Description"); @@ -137,7 +137,7 @@ void test_addPerson_AddsThePerson_WhenThPersonIsInTheModel() { } @Test - void test_removeElementsWithNoRelationships_RemovesAllElements_WhenTheViewHasNoRelationshipsBetweenElements() { + void removeElementsWithNoRelationships_RemovesAllElements_WhenTheViewHasNoRelationshipsBetweenElements() { SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "Software System", "Description"); Person person = model.addPerson(Location.Unspecified, "Person", "Description"); @@ -150,7 +150,7 @@ void test_removeElementsWithNoRelationships_RemovesAllElements_WhenTheViewHasNoR } @Test - void test_removeElementsWithNoRelationships_RemovesOnlyThoseElementsWithoutRelationships_WhenTheViewContainsSomeUnlinkedElements() { + void removeElementsWithNoRelationships_RemovesOnlyThoseElementsWithoutRelationships_WhenTheViewContainsSomeUnlinkedElements() { SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); SoftwareSystem softwareSystemA = model.addSoftwareSystem(Location.Unspecified, "System A", "Description"); SoftwareSystem softwareSystemB = model.addSoftwareSystem(Location.Unspecified, "System B", "Description"); @@ -170,7 +170,7 @@ void test_removeElementsWithNoRelationships_RemovesOnlyThoseElementsWithoutRelat } @Test - void test_copyLayoutInformationFrom() { + void copyLayoutInformationFrom() { Workspace workspace1 = new Workspace("", ""); Model model1 = workspace1.getModel(); SoftwareSystem softwareSystem1A = model1.addSoftwareSystem("System A", "Description"); @@ -256,21 +256,21 @@ void test_copyLayoutInformationFrom() { } @Test - void test_getName() { + void getName() { SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); SystemContextView systemContextView = new SystemContextView(softwareSystem, "context", "Description"); assertEquals("The System - System Context", systemContextView.getName()); } @Test - void test_removeElementsThatAreUnreachableFrom_DoesNothing_WhenANullElementIsSpecified() { + void removeElementsThatAreUnreachableFrom_DoesNothing_WhenANullElementIsSpecified() { SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); StaticView view = new SystemContextView(softwareSystem, "context", "Description"); view.removeElementsThatAreUnreachableFrom(null); } @Test - void test_removeElementsThatAreUnreachableFrom_DoesNothing_WhenAllElementsCanBeReached() { + void removeElementsThatAreUnreachableFrom_DoesNothing_WhenAllElementsCanBeReached() { SoftwareSystem softwareSystem = model.addSoftwareSystem("The System", "Description"); SoftwareSystem softwareSystemA = model.addSoftwareSystem("System A", ""); SoftwareSystem softwareSystemB = model.addSoftwareSystem("System B", ""); @@ -287,7 +287,7 @@ void test_removeElementsThatAreUnreachableFrom_DoesNothing_WhenAllElementsCanBeR } @Test - void test_removeElementsThatAreUnreachableFrom_RemovesOrphanedElements_WhenThereAreSomeOrphanedElements() { + void removeElementsThatAreUnreachableFrom_RemovesOrphanedElements_WhenThereAreSomeOrphanedElements() { SoftwareSystem softwareSystem = model.addSoftwareSystem("The System", "Description"); SoftwareSystem softwareSystemA = model.addSoftwareSystem("System A", ""); SoftwareSystem softwareSystemB = model.addSoftwareSystem("System B", ""); @@ -306,7 +306,7 @@ void test_removeElementsThatAreUnreachableFrom_RemovesOrphanedElements_WhenThere } @Test - void test_removeElementsThatAreUnreachableFrom_RemovesUnreachableElements_WhenThereAreSomeUnreachableElements() { + void removeElementsThatAreUnreachableFrom_RemovesUnreachableElements_WhenThereAreSomeUnreachableElements() { SoftwareSystem softwareSystem = model.addSoftwareSystem("The System", "Description"); SoftwareSystem softwareSystemA = model.addSoftwareSystem("System A", ""); SoftwareSystem softwareSystemB = model.addSoftwareSystem("System B", ""); @@ -324,7 +324,7 @@ void test_removeElementsThatAreUnreachableFrom_RemovesUnreachableElements_WhenTh } @Test - void test_removeElementsThatAreUnreachableFrom_DoesntIncludeAllElements_WhenThereIsACyclicGraph() { + void removeElementsThatAreUnreachableFrom_DoesntIncludeAllElements_WhenThereIsACyclicGraph() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1", "Description"); SoftwareSystem softwareSystem2 = model.addSoftwareSystem("Software System 2", "Description"); Person user = model.addPerson("User", ""); @@ -345,7 +345,7 @@ void test_removeElementsThatAreUnreachableFrom_DoesntIncludeAllElements_WhenTher } @Test - void test_removeRelationship_DoesNothing_WhenNullIsSpecified() { + void removeRelationship_DoesNothing_WhenNullIsSpecified() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1", "Description"); SoftwareSystem softwareSystem2 = model.addSoftwareSystem("Software System 2", "Description"); SoftwareSystem softwareSystem3 = model.addSoftwareSystem("Software System 3", "Description"); @@ -362,7 +362,7 @@ void test_removeRelationship_DoesNothing_WhenNullIsSpecified() { } @Test - void test_removeRelationship_RemovesARelationship_WhenAValidRelationshipIsSpecified() { + void removeRelationship_RemovesARelationship_WhenAValidRelationshipIsSpecified() { SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1", "Description"); SoftwareSystem softwareSystem2 = model.addSoftwareSystem("Software System 2", "Description"); SoftwareSystem softwareSystem3 = model.addSoftwareSystem("Software System 3", "Description"); @@ -383,7 +383,7 @@ void test_removeRelationship_RemovesARelationship_WhenAValidRelationshipIsSpecif } @Test - void test_setKey_ThrowsAnException_WhenANullKeyIsSpecified() { + void setKey_ThrowsAnException_WhenANullKeyIsSpecified() { assertThrows(IllegalArgumentException.class, () -> { SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); new SystemContextView(softwareSystem, null, "Description"); @@ -391,7 +391,7 @@ void test_setKey_ThrowsAnException_WhenANullKeyIsSpecified() { } @Test - void test_setKey_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { + void setKey_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { assertThrows(IllegalArgumentException.class, () -> { SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); new SystemContextView(softwareSystem, " ", "Description"); @@ -399,7 +399,7 @@ void test_setKey_ThrowsAnException_WhenAnEmptyKeyIsSpecified() { } @Test - void test_addElement_ThrowsAnException_WhenTheSpecifiedElementDoesNotExistInTheModel() { + void addElement_ThrowsAnException_WhenTheSpecifiedElementDoesNotExistInTheModel() { try { Workspace workspace = new Workspace("1", ""); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); @@ -413,7 +413,7 @@ void test_addElement_ThrowsAnException_WhenTheSpecifiedElementDoesNotExistInTheM } @Test - void test_enableAutomaticLayout_EnablesAutoLayoutWithSomeDefaultValues_WhenTrueIsSpecified() { + void enableAutomaticLayout_EnablesAutoLayoutWithSomeDefaultValues_WhenTrueIsSpecified() { SystemLandscapeView view = new Workspace("", "").getViews().createSystemLandscapeView("key", "Description"); view.enableAutomaticLayout(); @@ -426,7 +426,7 @@ void test_enableAutomaticLayout_EnablesAutoLayoutWithSomeDefaultValues_WhenTrueI } @Test - void test_enableAutomaticLayout_DisablesAutoLayout_WhenFalseIsSpecified() { + void enableAutomaticLayout_DisablesAutoLayout_WhenFalseIsSpecified() { SystemLandscapeView view = new Workspace("", "").getViews().createSystemLandscapeView("key", "Description"); view.enableAutomaticLayout(); assertNotNull(view.getAutomaticLayout()); @@ -436,7 +436,7 @@ void test_enableAutomaticLayout_DisablesAutoLayout_WhenFalseIsSpecified() { } @Test - void test_enableAutomaticLayout() { + void enableAutomaticLayout() { SystemLandscapeView view = new Workspace("", "").getViews().createSystemLandscapeView("key", "Description"); view.enableAutomaticLayout(AutomaticLayout.RankDirection.LeftRight, 100, 200, 300, true); @@ -449,7 +449,7 @@ void test_enableAutomaticLayout() { } @Test - void test_addCustomElement_AddsTheCustomElementToTheView() { + void addCustomElement_AddsTheCustomElementToTheView() { Workspace workspace = new Workspace("", ""); SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "Description"); @@ -467,7 +467,7 @@ void test_addCustomElement_AddsTheCustomElementToTheView() { } @Test - void test_addCustomElementWithoutRelationships_AddsTheCustomElementToTheView() { + void addCustomElementWithoutRelationships_AddsTheCustomElementToTheView() { Workspace workspace = new Workspace("", ""); SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "Description"); @@ -485,7 +485,7 @@ void test_addCustomElementWithoutRelationships_AddsTheCustomElementToTheView() { } @Test - void test_removeCustomElement_RemovesTheCustomElementFromTheView() { + void removeCustomElement_RemovesTheCustomElementFromTheView() { Workspace workspace = new Workspace("", ""); SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "Description"); From 531975ed433db1b5d60a6c672b38a0742f510ec6 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 15 Aug 2022 12:53:35 +0100 Subject: [PATCH 316/717] Change GitHub action to run on Java 11 and 17. --- .github/workflows/gradle.yml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 9432fa591..ed7c421ef 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -13,14 +13,17 @@ jobs: build: runs-on: ubuntu-latest + strategy: + matrix: + java: [ '11', '17' ] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up JDK 11 - uses: actions/setup-java@v2 + uses: actions/setup-java@v3 with: - java-version: '11' - distribution: 'adopt' + java-version: ${{ matrix.java }} + distribution: 'temurin' - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build with Gradle From 9cc1373b43a3200698edf3fb8394334b0a496373 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 15 Aug 2022 13:40:37 +0100 Subject: [PATCH 317/717] Tweak action. --- .github/workflows/gradle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index ed7c421ef..5b5e99dbc 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -19,7 +19,7 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Set up JDK 11 + - name: Set up Java uses: actions/setup-java@v3 with: java-version: ${{ matrix.java }} From 2d635a98a149725428c4804c21417d560ac27b5d Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 15 Aug 2022 18:06:28 +0100 Subject: [PATCH 318/717] Fixes #171. --- README.md | 25 +++------ docs/client-side-encryption.md | 2 +- docs/component-diagram.md | 2 +- docs/container-diagram.md | 2 +- docs/corporate-branding.md | 17 ------ docs/decisions.md | 7 --- docs/deployment-diagram.md | 2 +- docs/documentation-arc42.md | 32 ----------- docs/documentation-automatic.md | 22 -------- docs/documentation-structurizr.md | 32 ----------- ...cumentation-viewpoints-and-perspectives.md | 27 ---------- docs/documentation.md | 53 ------------------- docs/dynamic-diagram.md | 2 +- docs/filtered-views.md | 2 +- docs/getting-started.md | 8 +-- docs/health-checks.md | 7 --- docs/styling-elements.md | 4 +- docs/styling-relationships.md | 5 +- docs/system-context-diagram.md | 2 +- docs/system-landscape-diagram.md | 2 +- 20 files changed, 22 insertions(+), 233 deletions(-) delete mode 100644 docs/corporate-branding.md delete mode 100644 docs/decisions.md delete mode 100644 docs/documentation-arc42.md delete mode 100644 docs/documentation-automatic.md delete mode 100644 docs/documentation-structurizr.md delete mode 100644 docs/documentation-viewpoints-and-perspectives.md delete mode 100644 docs/documentation.md delete mode 100644 docs/health-checks.md diff --git a/README.md b/README.md index bb3c09cce..49a6872b3 100644 --- a/README.md +++ b/README.md @@ -24,16 +24,15 @@ public static void main(String[] args) throws Exception { } ``` -The view can then be exported to be visualised using the [Structurizr cloud service/on-premises installation](https://structurizr.com), or other formats including PlantUML and WebSequenceDiagrams via the [Structurizr for Java extensions](https://github.com/structurizr/java-extensions). +The view can then be exported to be visualised using the [Structurizr cloud service/on-premises installation](https://structurizr.com), +or other formats including PlantUML, Mermaid, and WebSequenceDiagrams via the [structurizr-export library](https://github.com/structurizr/export). -![Views can be exported and visualised in many ways; e.g. PlantUML, Structurizr and Graphviz](docs/images/readme-1.png) +![Views can be exported and visualised via a number of tools](docs/images/readme-1.png) ## Table of contents * Introduction * [Getting started](docs/getting-started.md) - * [About Structurizr and how it compares to other tooling](https://structurizr.com/help/about) - * [Why use code?](https://structurizr.com/help/code) * [Basic concepts](https://structurizr.com/help/concepts) (workspaces, models, views and documentation) * [C4 model](https://c4model.com) * [Examples](https://github.com/structurizr/examples) @@ -57,26 +56,16 @@ The view can then be exported to be visualised using the [Structurizr cloud serv * [Styling relationships](docs/styling-relationships.md) * [Filtered views](docs/filtered-views.md) * [Graphviz automatic layout](https://github.com/structurizr/java-extensions/blob/master/structurizr-graphviz) -* Documentation - * [Documentation overview](docs/documentation.md) - * [Structurizr](docs/documentation-structurizr.md) - * [arc42](docs/documentation-arc42.md) - * [Viewpoints and Perspectives](docs/documentation-viewpoints-and-perspectives.md) - * [Automatic template](docs/documentation-automatic.md) - * [Architecture decision records](docs/decisions.md) * Other - * [HTTP-based health checks](docs/health-checks.md) * [Client-side encryption](docs/client-side-encryption.md) - * [Corporate branding](docs/corporate-branding.md) * Related projects + * [structurizr-dsl](https://github.com/structurizr/dsl): A text-based DSL for authoring Structurizr workspaces. * [java-quickstart](https://github.com/structurizr/java-quickstart): A simple starting point for using Structurizr for Java - * [java-extensions](https://github.com/structurizr/java-extensions): A collection of Structurizr for Java extensions; including the ability to extract software architecture information from code, export views to PlantUML, etc. - * [arch-as-code](https://github.com/nahknarmi/arch-as-code): A tool to store software architecture diagrams/documentation as YAML, and publish it to Structurizr. + * [structurizr-export](https://github.com/structurizr/export): Export model and views to external formats (e.g. PlantUML, Mermaid, etc). + * [structurizr-documentation](https://github.com/structurizr/documentation): Import Markdown/AsciiDoc documentation and ADRs into a Structurizr workspace. + * [java-extensions](https://github.com/structurizr/java-extensions): A collection of Structurizr for Java extensions; including the ability to extract software architecture information from code. * [structurizr-kotlin](https://github.com/Catalysts/structurizr-extensions/tree/master/structurizr-kotlin): An extension for Structurizr that lets you create your models in a fluent way. * [structurizr-spring-boot](https://github.com/Catalysts/structurizr-extensions/tree/master/structurizr-spring-boot): A way to apply dependency management to help modularise Structurizr code. * [structurizr-groovy](https://github.com/tidyjava/structurizr-groovy): An initial version of a Groovy wrapper around Structurizr for Java. - * [structurizr-dotnet](https://github.com/structurizr/dotnet): Structurizr for .NET * [changelog](docs/changelog.md) -[![Build Status](https://travis-ci.org/structurizr/java.svg?branch=master)](https://travis-ci.org/structurizr/java) - diff --git a/docs/client-side-encryption.md b/docs/client-side-encryption.md index 644c48052..cce9397e6 100644 --- a/docs/client-side-encryption.md +++ b/docs/client-side-encryption.md @@ -20,4 +20,4 @@ The default key size is 128 bits and the default iteration count is 1000. An alt In addition, a random salt and initialization vector are generated automatically for you, using Java's ```SecureRandom``` class. -See [ClientSideEncryption.java](https://github.com/structurizr/java/blob/master/structurizr-examples/src/com/structurizr/example/ClientSideEncryption.java) for a full example, and [https://structurizr.com/share/41](https://structurizr.com/share/41) to access the workspace. \ No newline at end of file +See [ClientSideEncryption.java](https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/ClientSideEncryption.java) for a full example, and [https://structurizr.com/share/41](https://structurizr.com/share/41) to access the workspace. \ No newline at end of file diff --git a/docs/component-diagram.md b/docs/component-diagram.md index 61d2b99c6..39c3dcd64 100644 --- a/docs/component-diagram.md +++ b/docs/component-diagram.md @@ -10,7 +10,7 @@ This is an example Component diagram for a fictional Internet Banking System, sh ![An example Component diagram](images/component-diagram-1.png) -See [BigBankPlc.java](https://github.com/structurizr/java/blob/master/structurizr-examples/src/com/structurizr/example/BigBankPlc.java) for the code, and [https://structurizr.com/share/36141#Components](https://structurizr.com/share/36141#Components) for the diagram. +See [InternetBankingSystem.java](https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/bigbankplc/InternetBankingSystem.java) for the code, and [https://structurizr.com/share/36141#Components](https://structurizr.com/share/36141#Components) for the diagram. ### Extracting components automatically diff --git a/docs/container-diagram.md b/docs/container-diagram.md index 437c648a2..34691c74b 100644 --- a/docs/container-diagram.md +++ b/docs/container-diagram.md @@ -12,4 +12,4 @@ Both the Single-Page Application and Mobile App use a JSON/HTTPS API, which is p ![An example Container diagram](images/container-diagram-1.png) -See [BigBankPlc.java](https://github.com/structurizr/java/blob/master/structurizr-examples/src/com/structurizr/example/BigBankPlc.java) for the code, and [https://structurizr.com/share/36141#Containers](https://structurizr.com/share/36141#Containers) for the diagram. \ No newline at end of file +See [InternetBankingSystem.java](https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/bigbankplc/InternetBankingSystem.java) for the code, and [https://structurizr.com/share/36141#Containers](https://structurizr.com/share/36141#Containers) for the diagram. \ No newline at end of file diff --git a/docs/corporate-branding.md b/docs/corporate-branding.md deleted file mode 100644 index d5bfb8874..000000000 --- a/docs/corporate-branding.md +++ /dev/null @@ -1,17 +0,0 @@ -# Corporate branding - -> Note: this page describes a feature that is not available to use with Structurizr's Free Plan. - -In addition to [styling diagram elements](styling-elements.md) and [relationships](styling-relationships.md), some corporate branding can be added to diagrams and documentation. This includes: - -- A font (font name and optional web font stylesheet URL). -- A logo (a URL to an image file or a data URI). - -You can add branding to an existing workspace, as follows: - -```java -Branding branding = views.getConfiguration().getBranding(); -branding.setLogo(ImageUtils.getImageAsDataUri(new File("./docs/images/structurizr-logo.png"))); -``` - -See [Help - Corporate Branding](https://structurizr.com/help/corporate-branding) for more details, [CorporateBranding.java](https://github.com/structurizr/java/blob/master/structurizr-examples/src/com/structurizr/example/CorporateBranding.java) for a full example, and [https://structurizr.com/share/35031](https://structurizr.com/share/35031) to access the workspace. diff --git a/docs/decisions.md b/docs/decisions.md deleted file mode 100644 index bff92c907..000000000 --- a/docs/decisions.md +++ /dev/null @@ -1,7 +0,0 @@ -# Decisions - -Although architecture decisions can be included in supplementary documentation, Structurizr also provides support for publishing architecture decision records, [as described by Michael Nygard](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions). - -Decision records can either be created manually using the API on the [Documentation class](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/documentation/Documentation.java), or using the [AdrToolsImporter](https://github.com/structurizr/java-extensions/blob/master/structurizr-adr-tools/src/com/structurizr/documentation/AdrToolsImporter.java) to import ADRs from Nat Pryce's popular [adr-tools](https://github.com/npryce/adr-tools) tooling. Here is [an example](https://github.com/structurizr/java/blob/master/structurizr-examples/src/com/structurizr/example/AdrTools.java). - -See [Structurizr - Decision Log](https://structurizr.com/help/decision-log) for more details. diff --git a/docs/deployment-diagram.md b/docs/deployment-diagram.md index 5abda112f..972b51363 100644 --- a/docs/deployment-diagram.md +++ b/docs/deployment-diagram.md @@ -8,4 +8,4 @@ As an example, a Deployment diagram for the live environment of a simplified, fi ![An example Deployment diagram](images/deployment-diagram-1.png) -See [BigBankPlc.java](https://github.com/structurizr/java/blob/master/structurizr-examples/src/com/structurizr/example/BigBankPlc.java) for the code, and [https://structurizr.com/share/36141#LiveDeployment](https://structurizr.com/share/36141#LiveDeployment) for the diagram. \ No newline at end of file +See [InternetBankingSystem.java](https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/bigbankplc/InternetBankingSystem.java) for the code, and [https://structurizr.com/share/36141#LiveDeployment](https://structurizr.com/share/36141#LiveDeployment) for the diagram. \ No newline at end of file diff --git a/docs/documentation-arc42.md b/docs/documentation-arc42.md deleted file mode 100644 index 99f2c1e0d..000000000 --- a/docs/documentation-arc42.md +++ /dev/null @@ -1,32 +0,0 @@ -# arc42 documentation template - -Structurizr for Java includes an implementation of the [arc42 documentation template](http://arc42.org), which can be used to document your software architecture. - -## Example - -To use this template, create an instance of the [Arc42DocumentationTemplate](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/documentation/Arc42DocumentationTemplate.java) class. -You can then add documentation sections as needed, each associated with a software system in your software architecture model, using Markdown or AsciiDoc. For example: - -```java -Arc42DocumentationTemplate template = new Arc42DocumentationTemplate(workspace); - -File documentationRoot = new File("./structurizr-examples/src/com/structurizr/example/documentation/arc42/markdown"); -template.addIntroductionAndGoalsSection(softwareSystem, new File(documentationRoot, "01-introduction-and-goals.md")); -template.addConstraintsSection(softwareSystem, new File(documentationRoot, "02-architecture-constraints.md")); -template.addContextAndScopeSection(softwareSystem, new File(documentationRoot, "03-system-scope-and-context.md")); -template.addSolutionStrategySection(softwareSystem, new File(documentationRoot, "04-solution-strategy.md")); -template.addBuildingBlockViewSection(softwareSystem, new File(documentationRoot, "05-building-block-view.md")); -template.addRuntimeViewSection(softwareSystem, new File(documentationRoot, "06-runtime-view.md")); -template.addDeploymentViewSection(softwareSystem, new File(documentationRoot, "07-deployment-view.md")); -template.addCrosscuttingConceptsSection(softwareSystem, new File(documentationRoot, "08-crosscutting-concepts.md")); -template.addArchitecturalDecisionsSection(softwareSystem, new File(documentationRoot, "09-architecture-decisions.md")); -template.addRisksAndTechnicalDebtSection(softwareSystem, new File(documentationRoot, "10-quality-requirements.md")); -template.addQualityRequirementsSection(softwareSystem, new File(documentationRoot, "11-risks-and-technical-debt.md")); -template.addGlossarySection(softwareSystem, new File(documentationRoot, "12-glossary.md")); -``` - -Structurizr will create navigation controls based upon the the sections in the documentation, and the software systems they have been associated with. See [Arc42DocumentationExample.java](https://github.com/structurizr/java/blob/master/structurizr-examples/src/com/structurizr/example/Arc42DocumentationExample.java) for the full code, and [https://structurizr.com/share/27791/documentation](https://structurizr.com/share/27791/documentation) to see the rendered documentation. - -## More information - -See [Help - Documentation](https://structurizr.com/help/documentation) for more information about how headings are rendered, and how to embed diagrams from you workspace into the documentation. \ No newline at end of file diff --git a/docs/documentation-automatic.md b/docs/documentation-automatic.md deleted file mode 100644 index 7424fc5e9..000000000 --- a/docs/documentation-automatic.md +++ /dev/null @@ -1,22 +0,0 @@ -# Automatic documentation template - -Structurizr for Java includes an automatic documentation template, which will scan a given directory and automatically add all Markdown or AsciiDoc -files in that directory. Each file must represent a separate section, and the second level heading ("## Section Title" in Markdown and "== Section Title" in AsciiDoc) will be used as the section name. - -## Example - -To use this template, create an instance of the [AutomaticDocumentationTemplate](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/documentation/AutomaticDocumentationTemplate.java) class. -You can then add documentation sections as needed, each associated with a software system in your software architecture model, using Markdown or AsciiDoc. For example: - -```java -File documentationRoot = new File("./structurizr-examples/src/com/structurizr/example/documentation/automatic"); - -AutomaticDocumentationTemplate template = new AutomaticDocumentationTemplate(workspace); -template.addSections(softwareSystem, documentationRoot); -``` - -Structurizr will create navigation controls based upon the the sections in the documentation, and the software systems they have been associated with. See [AutomaticDocumentationTemplateExample.java](https://github.com/structurizr/java/blob/master/structurizr-examples/src/com/structurizr/example/AutomaticDocumentationTemplateExample.java) for the full code, and [https://structurizr.com/share/35971/documentation](https://structurizr.com/share/35971/documentation) to see the rendered documentation. - -## More information - -See [Help - Documentation](https://structurizr.com/help/documentation) for more information about how headings are rendered, and how to embed diagrams from you workspace into the documentation. \ No newline at end of file diff --git a/docs/documentation-structurizr.md b/docs/documentation-structurizr.md deleted file mode 100644 index d6397403b..000000000 --- a/docs/documentation-structurizr.md +++ /dev/null @@ -1,32 +0,0 @@ -# Structurizr documentation template - -Structurizr for Java includes an implementation of the "software guidebook" from Simon Brown's [Software Architecture for Developers](https://leanpub.com/visualising-software-architecture) book, which can be used to document your software architecture. - -## Example - -To use this template, create an instance of the [StructurizrDocumentationTemplate](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/documentation/StructurizrDocumentationTemplate.java) class. -You can then add documentation sections as needed, each associated with a software system in your software architecture model, using Markdown or AsciiDoc. For example: - -```java -StructurizrDocumentationTemplate template = new StructurizrDocumentationTemplate(workspace); - -File documentationRoot = new File("./structurizr-examples/src/com/structurizr/example/documentation/structurizr/markdown"); -template.addContextSection(softwareSystem, new File(documentationRoot, "01-context.md")); -template.addFunctionalOverviewSection(softwareSystem, new File(documentationRoot, "02-functional-overview.md")); -template.addQualityAttributesSection(softwareSystem, new File(documentationRoot, "03-quality-attributes.md")); -template.addConstraintsSection(softwareSystem, new File(documentationRoot, "04-constraints.md")); -template.addPrinciplesSection(softwareSystem, new File(documentationRoot, "05-principles.md")); -template.addSoftwareArchitectureSection(softwareSystem, new File(documentationRoot, "06-software-architecture.md")); -template.addDataSection(softwareSystem, new File(documentationRoot, "07-data.md")); -template.addInfrastructureArchitectureSection(softwareSystem, new File(documentationRoot, "08-infrastructure-architecture.md")); -template.addDeploymentSection(softwareSystem, new File(documentationRoot, "09-deployment.md")); -template.addDevelopmentEnvironmentSection(softwareSystem, new File(documentationRoot, "10-development-environment.md")); -template.addOperationAndSupportSection(softwareSystem, new File(documentationRoot, "11-operation-and-support.md")); -template.addDecisionLogSection(softwareSystem, new File(documentationRoot, "12-decision-log.md")); -``` - -Structurizr will create navigation controls based upon the the sections in the documentation, and the software systems they have been associated with. See [StructurizrDocumentationExample.java](https://github.com/structurizr/java/blob/master/structurizr-examples/src/com/structurizr/example/StructurizrDocumentationExample.java) for the full code, and [https://structurizr.com/share/14181/documentation](https://structurizr.com/share/14181/documentation) to see the rendered documentation. - -## More information - -See [Help - Documentation](https://structurizr.com/help/documentation) for more information about how headings are rendered, and how to embed diagrams from you workspace into the documentation. \ No newline at end of file diff --git a/docs/documentation-viewpoints-and-perspectives.md b/docs/documentation-viewpoints-and-perspectives.md deleted file mode 100644 index ab8c39b90..000000000 --- a/docs/documentation-viewpoints-and-perspectives.md +++ /dev/null @@ -1,27 +0,0 @@ -# Viewpoints and Perspectives documentation template - -Structurizr for Java includes an implementation of the [Viewpoints and Perspectives documentation template](http://www.viewpoints-and-perspectives.info), which can be used to document your software architecture. - -## Example - -To use this template, create an instance of the [ViewpointsAndPerspectivesDocumentationTemplate](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/documentation/ViewpointsAndPerspectivesDocumentationTemplate.java) class. -You can then add documentation sections as needed, each associated with a software system in your software architecture model, using Markdown or AsciiDoc. For example: - -```java -ViewpointsAndPerspectivesDocumentationTemplate template = new ViewpointsAndPerspectivesDocumentationTemplate(workspace); - -File documentationRoot = new File("./structurizr-examples/src/com/structurizr/example/documentation/viewpointsandperspectives/markdown"); -template.addIntroductionSection(softwareSystem, new File(documentationRoot, "01-introduction.md")); -template.addGlossarySection(softwareSystem, new File(documentationRoot, "02-glossary.md")); -template.addSystemStakeholdersAndRequirementsSection(softwareSystem, new File(documentationRoot, "03-system-stakeholders-and-requirements.md")); -template.addArchitecturalForcesSection(softwareSystem, new File(documentationRoot, "04-architectural-forces.md")); -template.addArchitecturalViewsSection(softwareSystem, new File(documentationRoot, "05-architectural-views")); -template.addSystemQualitiesSection(softwareSystem, new File(documentationRoot, "06-system-qualities.md")); -template.addAppendicesSection(softwareSystem, new File(documentationRoot, "07-appendices.md")); -``` - -Structurizr will create navigation controls based upon the the sections in the documentation, and the software systems they have been associated with. See [ViewpointsAndPerspectivesDocumentationExample.java](https://github.com/structurizr/java/blob/master/structurizr-examples/src/com/structurizr/example/ViewpointsAndPerspectivesDocumentationExample.java) for the full code, and [https://structurizr.com/share/36371/documentation](https://structurizr.com/share/36371/documentation) to see the rendered documentation. - -## More information - -See [Help - Documentation](https://structurizr.com/help/documentation) for more information about how headings are rendered, and how to embed diagrams from you workspace into the documentation. \ No newline at end of file diff --git a/docs/documentation.md b/docs/documentation.md deleted file mode 100644 index 74869e488..000000000 --- a/docs/documentation.md +++ /dev/null @@ -1,53 +0,0 @@ -# Documentation - -In addition to diagrams, Structurizr lets you create supplementary documentation using the Markdown or AsciiDoc formats. - -![Example documentation](images/documentation-1.png) - -See [https://structurizr.com/share/31/documentation](https://structurizr.com/share/31/documentation) for an example. - -## Documentation templates - -The documentation is broken up into a number of sections, as defined by the template you are using, the following of which are included: - -- [Structurizr](documentation-structurizr.md) -- [arc42](documentation-arc42.md) -- [Viewpoints and Perspectives](documentation-viewpoints-and-perspectives.md) -- [Automatic template](documentation-automatic.md) - -## Custom sections - -You can add custom sections using the ```addSection``` method on the [DocumentationTemplate](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/documentation/DocumentationTemplate.java) class: - -```java -template.addSection(softwareSystem, "My custom section", Format.Markdown, ...); -``` - -## Images - -Images can be included using the regular Markdown/AsciiDoc syntax. - -![Including images](images/documentation-2.png) - -For this to work, the image files must be hosted externally (e.g. on your own web server, ideally accessible via HTTPS) or uploaded with your workspace using the ```addImages()``` or ```addImage()``` methods on the [DocumentationTemplate](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/documentation/DocumentationTemplate.java) class. - -```java -template.addImages(new File("...")); -``` - -See [functional-overview.md](https://raw.githubusercontent.com/structurizr/java/master/structurizr-examples/src/com/structurizr/example/financialrisksystem/functional-overview.md) and [FinancialRiskSystem](https://github.com/structurizr/java/blob/master/structurizr-examples/src/com/structurizr/example/FinancialRiskSystem.java) for an example. - -## Embedding diagrams - -Software architecture diagrams from your workspace can be embedded within the documentation sections using an additional special syntax. - -![Embedding diagrams](images/documentation-3.png) - -The syntax is similar to that used for including images, for example: - -``` -Markdown - ![](embed:DiagramKey) -AsciiDoc - image::embed:DiagramKey[] -``` - -See [context.md](https://raw.githubusercontent.com/structurizr/java/master/structurizr-examples/src/com/structurizr/example/financialrisksystem/context.md), [context.adoc](https://raw.githubusercontent.com/structurizr/java/master/structurizr-examples/src/com/structurizr/example/financialrisksystem/context.adoc) and [FinancialRiskSystem](https://github.com/structurizr/java/blob/master/structurizr-examples/src/com/structurizr/example/FinancialRiskSystem.java) for an example. \ No newline at end of file diff --git a/docs/dynamic-diagram.md b/docs/dynamic-diagram.md index 88374e8ea..673b6a700 100644 --- a/docs/dynamic-diagram.md +++ b/docs/dynamic-diagram.md @@ -8,7 +8,7 @@ As an example, a Dynamic diagram describing the customer sign in process for a s ![An example Dynamic diagram](images/dynamic-diagram-1.png) -See [BigBankPlc.java](https://github.com/structurizr/java/blob/master/structurizr-examples/src/com/structurizr/example/BigBankPlc.java) for the code, and [https://structurizr.com/share/36141#SignIn](https://structurizr.com/share/36141#SignIn) for the diagram. +See [InternetBankingSystem.java](https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/bigbankplc/InternetBankingSystem.java) for the code, and [https://structurizr.com/share/36141#SignIn](https://structurizr.com/share/36141#SignIn) for the diagram. ### Adding relationships diff --git a/docs/filtered-views.md b/docs/filtered-views.md index 6e1f104dc..7b749e566 100644 --- a/docs/filtered-views.md +++ b/docs/filtered-views.md @@ -36,4 +36,4 @@ views.createFilteredView(systemLandscapeView, "FutureState", "The future state s In summary, you create a view with all of the elements and relationships that you want to show, and then create one or more filtered views on top, specifying the tags that you'd like to include or exclude. -See [FilteredViews.java](https://github.com/structurizr/java/blob/master/structurizr-examples/src/com/structurizr/example/FilteredViews.java) for the full code, and [https://structurizr.com/share/19911](https://structurizr.com/share/19911) for the diagram. \ No newline at end of file +See [FilteredViews.java](https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/FilteredViews.java) for the full code, and [https://structurizr.com/share/19911](https://structurizr.com/share/19911) for the diagram. \ No newline at end of file diff --git a/docs/getting-started.md b/docs/getting-started.md index 79b918070..5e11b5f7f 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -1,14 +1,16 @@ # Getting started -Here is a quick overview of how to get started with Structurizr for Java so that you can create a software architecture model as code. You can find the code at [GettingStarted.java](https://github.com/structurizr/java/blob/master/structurizr-examples/src/com/structurizr/example/GettingStarted.java) and the live example workspace at [https://structurizr.com/share/25441](https://structurizr.com/share/25441). +Here is a quick overview of how to get started with Structurizr for Java so that you can create a software architecture model as code. +You can find the code at [GettingStarted.java](https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/GettingStarted.java) +and the live example workspace at [https://structurizr.com/share/25441](https://structurizr.com/share/25441). > See the [java-quickstart project](https://github.com/structurizr/java-quickstart) for a quick and simple way to get started with Structurizr for Java. -For more examples, please see [structurizr-examples](https://github.com/structurizr/java/tree/master/structurizr-examples/src/com/structurizr/example). +For more examples, please see [structurizr-examples](https://github.com/structurizr/examples/tree/main/java/src/main/java/com/structurizr/example). ## 1. Dependencies -The Structurizr for Java binaries are hosted on [Maven Central](https://repo1.maven.org/maven2/com/structurizr/) and the dependencies for use with Maven, Ivy, Gradle, etc are as follows. +The Structurizr for Java binaries are hosted on [Maven Central](https://repo1.maven.org/maven2/com/structurizr/structurizr-client/) and the dependencies for use with Maven, Ivy, Gradle, etc are as follows. Name | Description ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- diff --git a/docs/health-checks.md b/docs/health-checks.md deleted file mode 100644 index 6cf5f1b5a..000000000 --- a/docs/health-checks.md +++ /dev/null @@ -1,7 +0,0 @@ -# HTTP-based health checks - -Structurizr's health checks feature allows you to supplement your deployment models with HTTP-based health checks to get an "at a glance" view of the health of your software systems. See [Structurizr - Health Checks](https://structurizr.com/help/health-checks) for more details. - -When defining your software architecture model using the client library, HTTP-based health checks can be added to the Container Instances in your deployment model. Each health check is defined by a name, an endpoint URL, a polling interval (e.g. 60 seconds), a timeout (e.g. 1000 milliseconds), and optionally one or more HTTP headers. - -[HttpHealthChecks.java](https://github.com/structurizr/java/blob/master/structurizr-examples/src/com/structurizr/example/HttpHealthChecks.java) shows an example of how to setup the health checks, and the result can be see online at [https://structurizr.com/share/39441/health](https://structurizr.com/share/39441/health). \ No newline at end of file diff --git a/docs/styling-elements.md b/docs/styling-elements.md index d8e7674f3..c0ae0967f 100644 --- a/docs/styling-elements.md +++ b/docs/styling-elements.md @@ -75,6 +75,4 @@ The set of available shapes is as follows: Structurizr will automatically add all element styles to a diagram key, showing you which styles are associated with which tags. -![The diagram key](images/styling-elements-6.png) - -You can find the code for this example at [StylingElements.java](https://github.com/structurizr/java/blob/master/structurizr-examples/src/com/structurizr/example/StylingElements.java) and the live example workspace at [https://structurizr.com/share/36111](https://structurizr.com/share/36111). \ No newline at end of file +![The diagram key](images/styling-elements-6.png) \ No newline at end of file diff --git a/docs/styling-relationships.md b/docs/styling-relationships.md index 942a1c179..ae696bd34 100644 --- a/docs/styling-relationships.md +++ b/docs/styling-relationships.md @@ -45,7 +45,4 @@ styles.addRelationshipStyle("JDBC").color("#0000ff"); Structurizr will automatically add all relationship styles to a diagram key. -![The diagram key](images/styling-relationships-4.png) - - -You can find the code for this example at [StylingRelationships.java](https://github.com/structurizr/java/blob/master/structurizr-examples/src/com/structurizr/example/StylingRelationships.java) and the live example workspace at [https://structurizr.com/share/36131](https://structurizr.com/share/36131). \ No newline at end of file +![The diagram key](images/styling-relationships-4.png) \ No newline at end of file diff --git a/docs/system-context-diagram.md b/docs/system-context-diagram.md index 25331aade..1c265dc0d 100644 --- a/docs/system-context-diagram.md +++ b/docs/system-context-diagram.md @@ -10,4 +10,4 @@ This is an example System Context diagram for a fictional Internet Banking Syste ![An example System Context diagram](images/system-context-diagram-1.png) -See [BigBankPlc.java](https://github.com/structurizr/java/blob/master/structurizr-examples/src/com/structurizr/example/BigBankPlc.java) for the code, and [https://structurizr.com/share/36141#SystemContext](https://structurizr.com/share/36141#SystemContext) for the diagram. \ No newline at end of file +See [InternetBankingSystem.java](https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/bigbankplc/InternetBankingSystem.java) for the code, and [https://structurizr.com/share/36141#SystemContext](https://structurizr.com/share/36141#SystemContext) for the diagram. \ No newline at end of file diff --git a/docs/system-landscape-diagram.md b/docs/system-landscape-diagram.md index aed32f0a9..ad1caeee5 100644 --- a/docs/system-landscape-diagram.md +++ b/docs/system-landscape-diagram.md @@ -10,4 +10,4 @@ As an example, a System Landscape diagram for a simplified, fictional bank might ![An example System Landscape diagram](images/system-landscape-diagram-1.png) -See [BigBankPlc.java](https://github.com/structurizr/java/blob/master/structurizr-examples/src/com/structurizr/example/BigBankPlc.java) for the code, and [https://structurizr.com/share/36141#SystemLandscape](https://structurizr.com/share/36141#SystemLandscape) for the diagram. \ No newline at end of file +See [SystemLandscape.java](https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/bigbankplc/SystemLandscape.java) for the code, and [https://structurizr.com/share/36141#SystemLandscape](https://structurizr.com/share/36141#SystemLandscape) for the diagram. \ No newline at end of file From aa79b7c0d830e549ff6784e886d09a9d843ef986 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 15 Aug 2022 18:09:22 +0100 Subject: [PATCH 319/717] Fixes link. --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 9099392bb..0a3dcc9c2 100644 --- a/build.gradle +++ b/build.gradle @@ -73,8 +73,8 @@ subprojects { proj -> url = 'https://github.com/structurizr/java' scm { - connection = 'scm:git:git://github.com/structurizr/structurizr-java.git' - developerConnection = 'scm:git:git@github.com:structurizr/structurizr-java.git' + connection = 'scm:git:git://github.com/structurizr/java.git' + developerConnection = 'scm:git:git@github.com:structurizr/java.git' url = 'https://github.com/structurizr/java' } From 83f874b2a9f345b401afce4183959f953057891a Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 15 Aug 2022 18:09:35 +0100 Subject: [PATCH 320/717] Fixes link. --- docs/system-landscape-diagram.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/system-landscape-diagram.md b/docs/system-landscape-diagram.md index ad1caeee5..7e7b0d599 100644 --- a/docs/system-landscape-diagram.md +++ b/docs/system-landscape-diagram.md @@ -10,4 +10,4 @@ As an example, a System Landscape diagram for a simplified, fictional bank might ![An example System Landscape diagram](images/system-landscape-diagram-1.png) -See [SystemLandscape.java](https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/bigbankplc/SystemLandscape.java) for the code, and [https://structurizr.com/share/36141#SystemLandscape](https://structurizr.com/share/36141#SystemLandscape) for the diagram. \ No newline at end of file +See [SystemLandscape.java](https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/bigbankplc/SystemLandscape.java) for the code, and [https://structurizr.com/share/28201/diagrams#SystemLandscape](https://structurizr.com/share/28201/diagrams#SystemLandscape) for the diagram. \ No newline at end of file From 94228f918688f9fa865f6168ded73b768781c36c Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 15 Aug 2022 18:11:42 +0100 Subject: [PATCH 321/717] Fixes diagram links. --- docs/component-diagram.md | 2 +- docs/container-diagram.md | 2 +- docs/dynamic-diagram.md | 2 +- docs/filtered-views.md | 2 +- docs/system-context-diagram.md | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/component-diagram.md b/docs/component-diagram.md index 39c3dcd64..6b7f6a2d3 100644 --- a/docs/component-diagram.md +++ b/docs/component-diagram.md @@ -10,7 +10,7 @@ This is an example Component diagram for a fictional Internet Banking System, sh ![An example Component diagram](images/component-diagram-1.png) -See [InternetBankingSystem.java](https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/bigbankplc/InternetBankingSystem.java) for the code, and [https://structurizr.com/share/36141#Components](https://structurizr.com/share/36141#Components) for the diagram. +See [InternetBankingSystem.java](https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/bigbankplc/InternetBankingSystem.java) for the code, and [https://structurizr.com/share/36141/diagrams#Components](https://structurizr.com/share/36141/diagrams#Components) for the diagram. ### Extracting components automatically diff --git a/docs/container-diagram.md b/docs/container-diagram.md index 34691c74b..1592be0a2 100644 --- a/docs/container-diagram.md +++ b/docs/container-diagram.md @@ -12,4 +12,4 @@ Both the Single-Page Application and Mobile App use a JSON/HTTPS API, which is p ![An example Container diagram](images/container-diagram-1.png) -See [InternetBankingSystem.java](https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/bigbankplc/InternetBankingSystem.java) for the code, and [https://structurizr.com/share/36141#Containers](https://structurizr.com/share/36141#Containers) for the diagram. \ No newline at end of file +See [InternetBankingSystem.java](https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/bigbankplc/InternetBankingSystem.java) for the code, and [https://structurizr.com/share/36141/diagrams#Containers](https://structurizr.com/share/36141/diagrams#Containers) for the diagram. \ No newline at end of file diff --git a/docs/dynamic-diagram.md b/docs/dynamic-diagram.md index 673b6a700..6a5879e10 100644 --- a/docs/dynamic-diagram.md +++ b/docs/dynamic-diagram.md @@ -8,7 +8,7 @@ As an example, a Dynamic diagram describing the customer sign in process for a s ![An example Dynamic diagram](images/dynamic-diagram-1.png) -See [InternetBankingSystem.java](https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/bigbankplc/InternetBankingSystem.java) for the code, and [https://structurizr.com/share/36141#SignIn](https://structurizr.com/share/36141#SignIn) for the diagram. +See [InternetBankingSystem.java](https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/bigbankplc/InternetBankingSystem.java) for the code, and [https://structurizr.com/share/36141/diagrams#SignIn](https://structurizr.com/share/36141/diagrams#SignIn) for the diagram. ### Adding relationships diff --git a/docs/filtered-views.md b/docs/filtered-views.md index 7b749e566..c0e700872 100644 --- a/docs/filtered-views.md +++ b/docs/filtered-views.md @@ -36,4 +36,4 @@ views.createFilteredView(systemLandscapeView, "FutureState", "The future state s In summary, you create a view with all of the elements and relationships that you want to show, and then create one or more filtered views on top, specifying the tags that you'd like to include or exclude. -See [FilteredViews.java](https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/FilteredViews.java) for the full code, and [https://structurizr.com/share/19911](https://structurizr.com/share/19911) for the diagram. \ No newline at end of file +See [FilteredViews.java](https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/FilteredViews.java) for the full code, and [https://structurizr.com/share/19911/diagrams](https://structurizr.com/share/19911/diagrams) for the diagram. \ No newline at end of file diff --git a/docs/system-context-diagram.md b/docs/system-context-diagram.md index 1c265dc0d..0bef4d543 100644 --- a/docs/system-context-diagram.md +++ b/docs/system-context-diagram.md @@ -10,4 +10,4 @@ This is an example System Context diagram for a fictional Internet Banking Syste ![An example System Context diagram](images/system-context-diagram-1.png) -See [InternetBankingSystem.java](https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/bigbankplc/InternetBankingSystem.java) for the code, and [https://structurizr.com/share/36141#SystemContext](https://structurizr.com/share/36141#SystemContext) for the diagram. \ No newline at end of file +See [InternetBankingSystem.java](https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/bigbankplc/InternetBankingSystem.java) for the code, and [https://structurizr.com/share/36141/diagrams#SystemContext](https://structurizr.com/share/36141/diagrams#SystemContext) for the diagram. \ No newline at end of file From b78c5bf565d3c1d28a477db91a89768a887988be Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 15 Aug 2022 18:12:12 +0100 Subject: [PATCH 322/717] Fixes diagram links. --- docs/deployment-diagram.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/deployment-diagram.md b/docs/deployment-diagram.md index 972b51363..3facc2a88 100644 --- a/docs/deployment-diagram.md +++ b/docs/deployment-diagram.md @@ -8,4 +8,4 @@ As an example, a Deployment diagram for the live environment of a simplified, fi ![An example Deployment diagram](images/deployment-diagram-1.png) -See [InternetBankingSystem.java](https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/bigbankplc/InternetBankingSystem.java) for the code, and [https://structurizr.com/share/36141#LiveDeployment](https://structurizr.com/share/36141#LiveDeployment) for the diagram. \ No newline at end of file +See [InternetBankingSystem.java](https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/bigbankplc/InternetBankingSystem.java) for the code, and [https://structurizr.com/share/36141/diagrams#LiveDeployment](https://structurizr.com/share/36141/diagrams#LiveDeployment) for the diagram. \ No newline at end of file From 6e02d523717b4f91c4fed9abf9f7d9f43235f00a Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 15 Aug 2022 18:16:18 +0100 Subject: [PATCH 323/717] Use the published versions of the diagrams. --- docs/component-diagram.md | 2 +- docs/container-diagram.md | 2 +- docs/deployment-diagram.md | 2 +- docs/dynamic-diagram.md | 2 +- docs/images/component-diagram-1.png | Bin 390052 -> 0 bytes docs/images/container-diagram-1.png | Bin 386295 -> 0 bytes docs/images/deployment-diagram-1.png | Bin 424108 -> 0 bytes docs/images/dynamic-diagram-1.png | Bin 273565 -> 0 bytes docs/images/system-context-diagram-1.png | Bin 257184 -> 0 bytes docs/images/system-landscape-diagram-1.png | Bin 354820 -> 0 bytes docs/system-context-diagram.md | 2 +- docs/system-landscape-diagram.md | 2 +- 12 files changed, 6 insertions(+), 6 deletions(-) delete mode 100644 docs/images/component-diagram-1.png delete mode 100644 docs/images/container-diagram-1.png delete mode 100644 docs/images/deployment-diagram-1.png delete mode 100644 docs/images/dynamic-diagram-1.png delete mode 100644 docs/images/system-context-diagram-1.png delete mode 100644 docs/images/system-landscape-diagram-1.png diff --git a/docs/component-diagram.md b/docs/component-diagram.md index 6b7f6a2d3..2edbe47b7 100644 --- a/docs/component-diagram.md +++ b/docs/component-diagram.md @@ -8,7 +8,7 @@ The Component diagram shows how a container is made up of a number of "component This is an example Component diagram for a fictional Internet Banking System, showing some (rather than all) of the components within the API Application. Here, there are two Spring MVC Rest Controllers providing access points for the JSON/HTTPS API, with each controller subsequently using other components to access data from the Database and Mainframe Banking System. -![An example Component diagram](images/component-diagram-1.png) +![An example Component diagram](https://static.structurizr.com/workspace/36141/diagrams/Components.png) See [InternetBankingSystem.java](https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/bigbankplc/InternetBankingSystem.java) for the code, and [https://structurizr.com/share/36141/diagrams#Components](https://structurizr.com/share/36141/diagrams#Components) for the diagram. diff --git a/docs/container-diagram.md b/docs/container-diagram.md index 1592be0a2..ebc56d8d2 100644 --- a/docs/container-diagram.md +++ b/docs/container-diagram.md @@ -10,6 +10,6 @@ This is an example Container diagram for a fictional Internet Banking System. It Both the Single-Page Application and Mobile App use a JSON/HTTPS API, which is provided by another Java/Spring MVC application running on the server. The API Application gets user information from the Database (a relational database schema). The API Application also communicates with the existing Mainframe Banking System, using a propreitary XML/HTTPS interface, to get information about bank accounts or make transactions. The API Application also uses the existing E-mail System if it needs to send e-mails to customers. -![An example Container diagram](images/container-diagram-1.png) +![An example Container diagram](https://static.structurizr.com/workspace/36141/diagrams/Containers.png) See [InternetBankingSystem.java](https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/bigbankplc/InternetBankingSystem.java) for the code, and [https://structurizr.com/share/36141/diagrams#Containers](https://structurizr.com/share/36141/diagrams#Containers) for the diagram. \ No newline at end of file diff --git a/docs/deployment-diagram.md b/docs/deployment-diagram.md index 3facc2a88..39b80fc92 100644 --- a/docs/deployment-diagram.md +++ b/docs/deployment-diagram.md @@ -6,6 +6,6 @@ A deployment diagram allows you to illustrate how containers in the static model As an example, a Deployment diagram for the live environment of a simplified, fictional Internet Banking System might look something like this. In summary, it shows the deployment of the Web Application and the Database, with a secondary Database being used for failover purposes. -![An example Deployment diagram](images/deployment-diagram-1.png) +![An example Deployment diagram](https://static.structurizr.com/workspace/36141/diagrams/LiveDeployment.png) See [InternetBankingSystem.java](https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/bigbankplc/InternetBankingSystem.java) for the code, and [https://structurizr.com/share/36141/diagrams#LiveDeployment](https://structurizr.com/share/36141/diagrams#LiveDeployment) for the diagram. \ No newline at end of file diff --git a/docs/dynamic-diagram.md b/docs/dynamic-diagram.md index 6a5879e10..8443a8234 100644 --- a/docs/dynamic-diagram.md +++ b/docs/dynamic-diagram.md @@ -6,7 +6,7 @@ A simple dynamic diagram can be useful when you want to show how elements in a s As an example, a Dynamic diagram describing the customer sign in process for a simplified, fictional Internet Banking System might look something like this. In summary, it shows the components involved in the sign in process, and the interactions between them. -![An example Dynamic diagram](images/dynamic-diagram-1.png) +![An example Dynamic diagram](https://static.structurizr.com/workspace/36141/diagrams/SignIn.png) See [InternetBankingSystem.java](https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/bigbankplc/InternetBankingSystem.java) for the code, and [https://structurizr.com/share/36141/diagrams#SignIn](https://structurizr.com/share/36141/diagrams#SignIn) for the diagram. diff --git a/docs/images/component-diagram-1.png b/docs/images/component-diagram-1.png deleted file mode 100644 index a4046f7e8787540726bc70254172182e68f6f2fd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 390052 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�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≪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 -s-bgO{ace?b+t$D|+vS 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

    *(R?s)j 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
    !@rfBlFEkIiFl%K(0`_E`efG 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 diff --git a/docs/images/container-diagram-1.png b/docs/images/container-diagram-1.png deleted file mode 100644 index 7ae653de8957ea0ef464ae603b0b5cba3b7ce215..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 386295 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
    g@Ax0mBbv0kcvBB-|6m?DAf&Tdo-WGE5|c5T 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 hMGRN`Q+bwQf@R?F} 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�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 z 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 Z4pCQ@9y(#fr+EneDaxtgie*d#{s*geA2|R3 diff --git a/docs/images/dynamic-diagram-1.png b/docs/images/dynamic-diagram-1.png deleted file mode 100644 index a50cee470afcdcb3a4ce202cd28eece25820f353..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 273565 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 z%0D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]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Ð(u? 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

    fVx0*84x7~KD_ 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 WhccEDiFbE7ifFdB>ol55b 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 z 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%0A!u`t?ky#z>?W4KIaGrL#B!M3u^bedg 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

    l`^>d4*MlfBG@{KlHU*7`pAQ;pC_61pI_0F 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%0AdqoH-h> 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%0A>Fm2UobBY+5wYHMUd` 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∋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 diff --git a/docs/system-context-diagram.md b/docs/system-context-diagram.md index 0bef4d543..cc4bcfd58 100644 --- a/docs/system-context-diagram.md +++ b/docs/system-context-diagram.md @@ -8,6 +8,6 @@ Detail isn't important here as this is your zoomed out view showing a big pictur This is an example System Context diagram for a fictional Internet Banking System. It shows the people who use it, and the other software systems that the Internet Banking System has a relationship with. Personal Customers of the bank use the Internet Banking System to view information about their bank accounts, and to make payments. The Internet Banking System itself uses the bank's existing Mainframe Banking System to do this, and uses the bank's existing E-mail System to send e-mails to customers. -![An example System Context diagram](images/system-context-diagram-1.png) +![An example System Context diagram](https://static.structurizr.com/workspace/36141/diagrams/SystemContext.png) See [InternetBankingSystem.java](https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/bigbankplc/InternetBankingSystem.java) for the code, and [https://structurizr.com/share/36141/diagrams#SystemContext](https://structurizr.com/share/36141/diagrams#SystemContext) for the diagram. \ No newline at end of file diff --git a/docs/system-landscape-diagram.md b/docs/system-landscape-diagram.md index 7e7b0d599..457998bf7 100644 --- a/docs/system-landscape-diagram.md +++ b/docs/system-landscape-diagram.md @@ -8,6 +8,6 @@ Essentially this is a high-level map of the software systems at the enterprise l As an example, a System Landscape diagram for a simplified, fictional bank might look something like this. -![An example System Landscape diagram](images/system-landscape-diagram-1.png) +![An example System Landscape diagram](https://static.structurizr.com/workspace/28201/diagrams/SystemLandscape.png) See [SystemLandscape.java](https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/bigbankplc/SystemLandscape.java) for the code, and [https://structurizr.com/share/28201/diagrams#SystemLandscape](https://structurizr.com/share/28201/diagrams#SystemLandscape) for the diagram. \ No newline at end of file From 0dbfce7ec3a623cf0e1e132efbb629bcd59c210d Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 15 Aug 2022 18:24:11 +0100 Subject: [PATCH 324/717] Some readme tweaks. --- README.md | 11 +++++------ docs/images/documentation-1.png | Bin 614701 -> 0 bytes docs/images/documentation-2.png | Bin 542540 -> 0 bytes docs/images/documentation-3.png | Bin 413890 -> 0 bytes docs/images/readme-1.png | Bin 84348 -> 0 bytes 5 files changed, 5 insertions(+), 6 deletions(-) delete mode 100644 docs/images/documentation-1.png delete mode 100644 docs/images/documentation-2.png delete mode 100644 docs/images/documentation-3.png delete mode 100644 docs/images/readme-1.png diff --git a/README.md b/README.md index 49a6872b3..df19c8b72 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,12 @@ # Structurizr for Java -This GitHub repository is an official client library for the [Structurizr](https://structurizr.com) cloud service and on-premises installation, both of which are web-based publishing platforms for software architecture models based upon the [C4 model](https://c4model.com). The component finder, adr-tools importer, and alternative diagram export formats (e.g. PlantUML) can be found at [Structurizr for Java extensions](https://github.com/structurizr/java-extensions). +This GitHub repository is (1) a client library for the [Structurizr](https://structurizr.com) cloud service and on-premises installation +and (2) a way to create a Structurizr workspace using Java code. Looking for the [Structurizr DSL](https://github.com/structurizr/dsl) instead? ## A quick example -As an example, the following Java code can be used to create a software architecture __model__ and an associated __view__ that describes a user using a software system. +As an example, the following Java code can be used to create a software architecture __model__ and an associated __view__ that describes a user using a software system, based upon the [C4 model](https://c4model.com). ```java public static void main(String[] args) throws Exception { @@ -24,10 +25,8 @@ public static void main(String[] args) throws Exception { } ``` -The view can then be exported to be visualised using the [Structurizr cloud service/on-premises installation](https://structurizr.com), -or other formats including PlantUML, Mermaid, and WebSequenceDiagrams via the [structurizr-export library](https://github.com/structurizr/export). - -![Views can be exported and visualised via a number of tools](docs/images/readme-1.png) +The view can then be exported to be visualised using the [Structurizr cloud service/on-premises installation/Lite](https://structurizr.com), +or other formats including PlantUML, Mermaid, DOT, and WebSequenceDiagrams via the [structurizr-export library](https://github.com/structurizr/export). ## Table of contents diff --git a/docs/images/documentation-1.png b/docs/images/documentation-1.png deleted file mode 100644 index 4b354c86826e609dec949ecc1c08fa7a556b2afa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 614701 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

    xLiY8k7I?yJ5O< zlztjAul>TYZ|d9r9Ea|SaeD~;OzDsY@t?C}i1?3X?Qb15W&BuD!Na7T2PLuFB<9WT z{*kvvIKM2W=S#XJ%14}jv-uk$`+#E$7SpDO+SoZ8CGz?jd5A!`#7d?8#}79>@gHA) z(;t|i;4;{?abNsjeDK%f%|UN`?26tW#p$O1|NrnU)Wk^GWE?k8l0V-1U;IO~KZH=m zL#WN$+em3Z^Rw=M|J8r(4v=`| zUeYp1E^hnVYNrC~VkF727G*V?>iWO70gaG*_vfn3H-^8q0U0>q<4yTGgMZ34f9=y` z6c-Q`n(z95^YnW7!NkH?HZt3&iJigJfNDrL2kSg?7SNlH?hDRktPG9WZzZtk2o8aRN*d^u2|6!5;SndD( zgP1-9pGuLu&B^pXAM?L_xe+7yxL@fC`{u;|m*PisCWKCXMvcF@acy(p$NryV|2M+!Kga%`Z2vd&)&IBI{>xgn z!_-%Y>kjYALmx{YycFP5klUtrQ|n>jk&kcUQ!FSSR43I4`kLBNu#?5B+iLbV?@!=W zyl-`Y`((TVgTYw_PWj{5gsx;SnPv&;2Tt)Lg81_`%|Ssno#V^(i-Uxvhz7SvA(yC< z4(-qeb(bkT<9JwG8!2f(QPC3b|3!+23PbbnRv7>M`hOPxpRM;d?lwqm{m;SsC+z%h zuN81ZYy2nZ{u6Zn3A+CT-9I4?{|Vk~E}{QPIsYWwe-iFL3HN`CgZ@)){8MiH|F7JL zX50;`on_Z7O6D=hL~8)~f#cy1_a1%PuI13{_W6+Vna1ND9Xc?_Q`V-UQ!>XL`DDIB zM--RWWir7Q?LRaF7Ga6tuZFuZ@$dlSq_Bciid&2aagx_>j8KUBZ^elifr3J8EV8s+ zaSLR0yZRSG84T>en08CyaAa5rmsXLY2FODU+qSD2#@F$6-;FnwJ9s3Cv_Y7lxvoM5 z)7!<0RFb}Q!Syf+KbPoEOhjqXv$B{2`_UG}Zw{N(4}1)2v8rNFVZ~i_KREKxA;W6! z(W?E`|JFaV1rv<8$q5(EE!rk$b;aU3)Om*6IED@>cw*%#~=J-5hFJr@&t>eaJ+&ZJ#=J zzDO$(#%URst@W96O1nR4@uKt6M1Bh*3lEgCXWH-&b6`fEdbp-r-4quL z3m(zza;N1w|8@5{N%v>%I_@huT_&l)L7)F?9~Y}B20V!=rnXHKX}GkhkRc)n2;2BD zhiYT0a+g7^_R?w!y0kEd|j+&Ss%|B z*K+1_ObAlic5+Vjt)BJ6R4^aCB(XjqwnD&1f#+GMmM4W93=Imm07Wg(9(G#JTXOo_ z!(abvw>z3ZM#eNBnfr8$YIDxiL-peqMDE<1mV2_zei`OEt%#tTpu|ryitOx{d}it| zK`{2KGZwt`FeB~Ln9WX=xtO@dB}*}=woKT5sSLJ>iA=ilns6`!ffbhz?J>R zT_KTYKErsI%+!yGyzcbLr3;BZ=FWtLM^?{T9E7jadq}G2VC{-vQqTEi+irrS?hlY! z2|NTkRs`OX#-kp%7D_$3~NHacGI2kFIY%r|CwO9Lqqm6BdMe_iX>U~Cn6GsMU>$6tNbPu zm=wvF>}reiMSWte*X*4|fz>^S$QiU$#36cA44}kl3{QML6_=-8RjahQg#Y~~jmSo` zVP@On9UT10?NNH{o-Js`BuX-3b1X$y5YhK8SdHC@p#fsIep8W;@Ic}Nt`v_myVZR3uZ&v?h9#jo+L@u=qp*dbu72}5t{G@WJX8BZR)SV z%wK&WPSTiJb+Q=0Vot|*SJi*U%Ercnf=egwt{RAV%~e`zh|km4|7TegEYR zjK~2J*4sIyetmODV$z7tAZHT4VtwXOMsBpiMj>O!uj@?9MdI$xQc4~R)Vp8cTP}9^aho(Ue}#A%Trj(nc8_YC zRBA@4b4$(&=OuU%YHi!3ni37ett9AxH*Qa`|Fvnp*Vh(&1$sIP7ZPkGqwy5k*`px; zW2rIo^cvO!L=a9zSl6)GQSJQ@T~y?2w~5I7>&8g8n?lT_y8q->rLY0Ad#LS}I~&|W z{6vT&IkuzC*1;-Wj4%Zle=YAf@_p)~FR94LUzUeil)eA~{+DiZv5akIUGP|8h`SH+ z!dtJmL~+kODi16@c#j(E3SlS?@+Nz6aq-vQZpKOl`zo3@UxQV&uYr{b>Uk0NBNBOR z>v%g(;l~$VwAZj<@?V}akah+e5K}F#2r9&WcE^_I*ERyORrFuv$ihlDNLIIhjC{3 z8)83zxY;#>Oho`j!z|aWt7xcg65=lQG=u>V>T@V#}jRyuzuAAdjQx zAR8qj?8_jSr>d8%ewg);?irF~18x2-c@(sqjnP30u@do6xCoal z&(x|pfm?=vF{pDIR|lL>BU}sTww$fw4~<-saM&$o64y_+)m9)Yl7TSeFrVMP#e*BE zF|m-aojK+{Dx)I}>i^naG}O*nI5?!nVC}}(n3^;9p{j(8qLp?87OZfa6j0r-k7ty* zjrJB*(wRP^TdSnEh(l~^MnOULlKpGRcx`#irIb&G(F_Dw&0Vuo*eMs|t{h`EwW=|b z#BgirE*KMT21}yCX-nkJ-yqv|_3oq;avcm-NqBMI@Nm5V z>7)Ik%Rk<2d%x!}hZHh^L0kgK)0@XrdB~?ggp4|srBeX)1Cc-2J$L9{Yo}YlCQXg? z07(pjEx+2TF}4LMNtb5#02xyS**CC*WZK6l;!4wAFh$LP5`6=r+#uMjEse~6DyfF# ztA4y4be->7m|32har0XArQd2ooedc6yzhBFr&tA{jf4tsuse_mNxNY`Hw=##wyuMJ zfz5Spq{(;!>b?`#brzdKakUc2i84OMp;_?yHY3#(9;8-h26O5K#-Cvh(4(zycNxX{ zHj~reMe6G}%g*i>S@Wj{Lv^@vc`KC%3;g*$jIlQ59a)>^?$RjKlNRjigxqOO8erXr zqZB)*U+IJRYZ)lG_0!{}+^1>X1MTE3&bY0u5L!TPA_RnP@Nte0wLzjt@rSfiZpo1= z({5f&DMxg8(!Z_bLcNx&SqJYz-CQ$*{{3Q7te-SuC=o%8iadV)j%?9fvy2i7|9YfY zXZaiLXw>Rxlr+i(yI8AZ&K5;u+A8XX^P1KSDTKvq!R7a;Kk--k~qu9A19|v z5p89Z8r^Y5m+zUQATNCPd4g+Zr;Uaj?NYdd#UR#dISOpDN0SpHr3rX<_pbzFgX27v z1Vmn~44jgr5qUzHplMVFzu=^=6?Mb+9OD-0OJsFpN~Om=_6$cjXuq0LTa1iZqBto8 z#jNzDWbw%-y7?3N_)@9nkmjqpcYSJk+f`#5R}|J@5Ayj!;eCUs{^Z*tdg*(%%v%L< z($6uUrd)>wuj92+orS8Fh;UVu57o53hP%@tln+WA9YQ)z=kDvQ=T{gnz8P6B;OFL7 z76F4!k>AVYzX=|75B=b98;R9}7#8z|eZF~aL+7KeA&yS-xa4I5o-@Bkk$5KkS#;%- zpJ5h=4;)q0&#P>fSrSia=3_X|E%ccsci7IH zLTou0N%0#>1C%I-9DQ%y!7u~BZBufLUVEXI@Vgqn_o9=wgF{$=ov*nNS zZi>4Pf-@g2yF6jU5v&qrqLzAw70H;}=&R6gw`Zovk=cJR(Ja((IyASBzbC-O6jbJk zH82F%-1zEBSOP9Inx9m1ELdyKH84}B9%Z;AZbmrs@(r@5)NuX82!2da1!}xFrIU2X?vS0yA=f}xeNvM&6RJyxC|&1*Gh9z2 zx{7kwXXPI0rzJE7?HZzFI)qZ{alm7pJo5ttFzQdl9-Tyq-!v3Gn1<@6Mjcq`#jj|! z*PIU~(4zhBc~Uec=i1FU`C6@7tSwl?>*m2ER~}~COpvyroVP<$Nj*0qSXPkXVzYEa z$`sqVg<3++j5%Bt9Ar?Y)LOw~a08U0yn5qyZL*6wNi$qdL;VCN3*LDsiH~WZ>JU}n zm*4TMH(mwxvqmPWE*eet6y~O=5PIx7v^r*N6!=@b_7qM9s45sjs-i*Xtew4Yq~G>7 zMuW^)+pbO7ZaZ(-U{K3cUI#FFp z-9wFHtTa*J+w#>600LCg^fct(%aC8CpBU{P>Ik2sS8=jWr*@eoo_R}K$A6~49FB$A zK9Q8Uf>q+U%%Vyp%K8|U9y4oF3zZ*g{7|a+hqZdV(K&Ym;*6DhK36{T0s;%LolP3? zf;Vzl*e{rJ^38004uA^nZ{mX*#s#O|1R?(Rez<6b)Jv3~X$^+V(c8#yvf&LldEA_M zm0M4DO))P)IDm@vnGzw~mAQNZJQ$KI+7@hJ4A5e8!cSS0^3HG>96y@m_XcuGYI;@L{GnbAMEzFn8t+^~%FY$8S&K zX?FD=^|S6OR3>jk%pXv=uJ-z6<_shhPr%)Fq8{V6MI}wKR~N3C+Y4-XwGrn*U58UOTQ!1{w7WIXI%uB4_LT@XZT;X31f0V(F$i|lTpg9$;g^#g2y6SS6V?{+ zkk{|`%kU(ZDu~GyuAuF>>F!Z)bM3G4=ca9%UO}8v^E&T@Jsh+(IVS!^eA0_;INV0g zD>_-ug3`#b@zTzCuqUKuXUBvUg3waSkFW3d#9Qz-(Wqz!B1S?#7ceD==ojL)^==SR zU5cl9nDUziEU@3OD2CP8=Kc#{^(Fam%FBtMsD4zAZcJ2PVX9^CFV>25?8O( zAF}OMZ!*np6DKW57{-8rrr9OirnfYh%B|X=UO1f~rPqs@qccrY_*oB&O`R_0@c=#b z0@H_^DqS*Xqp(_CUff|4q$o#nORm?OjHv{mjCg`(ld|D%-lR)0C+)nt1$q1s+?2Y( zsve_lc6vAx(~>tcaYWeJ_9YDecj^ANPn|Pw6pPm9I%j$~O)aB5T*O}}K|U(7(vP9| zvOV^eP=2#N>cYuc$v~30q?QU=op89a(;x89Epb<#JOPLiz@ z1=kfEOyxqr(;p0IJC1W!rIv4x?y{+LLrt~AUEtX#K%~`^FzEfTVP__>tBb=jsZXd= zjOY*X?ioPQHceX1K`ohppp@a*S1W)#E6rUG3{Ku{pvu&Ql%4eudq~;V0#1?MfEU;^ z2L(XZkEOK(4*a5C(UGs8gXs9|hqd67kuTJ;@5Bc(OCFYHd~#B32+BNJ@$Fx~MI1|g zr<|eqnYPkKXRny^AIO_%SMXVu$GpSzUFk$zitD?~Zd4vt6yc0*J! zOlIGF^Yu_>3pjr?JNVj+u(3ul%iGZLr$H8rk#dtZzj4>tJ=s+alB6;=!}Xj|CI$Yx z1`wUW772Q@XNY}7T&Rv);Vq&%t&Pg1v8i?>HWq>Fnu$WrN)Kp0H5(^Jw4cTIIcnp< zCk>hOj)Qv^f11qVw}>L>ZG2ZU(j&M z*9K@#4dtr|fVDA54Rf~M4=&uHb@)8e5V`k+gxpO|hr!G5`Zip?;jxA}81e z##^2c2UwAdgr|il1fpgLC>j@65a=A54oVlXB@Wg|;j9PgU;wumqzUB!2Rw4KE z#|8tJdRd35rb$Xfu;-kENBGC*JPH>uMrv>wbcyerh$PcVx0Nd$bV(r~monm9qG*H& z=;ei+@m~X-b@m+ zidNI|Qtx;!7h;c^Q5e$>$wq~!F$;FV>bP!`NbmCZXq%yDrTM&6%MBmHEsRQBzX{T~ zuT}7$%~Lf%_>DNV)|&SGRLkg%P&zn-Yltj z@P%UUMJVr6xp|i(vh^hGBoyupvQs=dW-aBV%&T|*`i%zCNg))p$j|zkV82< z?(r2P6-()Q2ez?8?pGwrja}vG%y=a9?c!oCl~evinmBwl=LXwz&t?By;x1L<;Y-Pf zpQYzqzKObYIoL`3Mmw~tGMn)S0;ixtdrG5wc+v0Z`7>JZtvy$@Q_~*lZI7HNi1;lmgG;Mv7o0Hlv9RaQu>fh)>^FJ5HZO|Ne(cQOO-!|u zJ?F0+iM0RHp44VYHomXp$A`Cr?e^d>9m5Cq13boyh|2=;1B-})k#chQaIVn}nGyks zvsz@-AK29adrt{Xn_Sdx(sRl_5vmI8d5(GSL@>wI za*Py+$PHVa@-}E7b}IY6*|-NBF6B?0tm`WYetTD(Js|rK23^M5kDw+>X%vJCZnw-I zjuWF7)e9_djTYAJ5_nAWF{USG>WMYIVCEWt-a)0H^_X{h93bZ4`{(W9*dO0?E9o@eG0Dwg!yEumF5uC+iOou;}_gUX0Qw$6_ush>nOPqKoURDQZp z$vdmLDREk@o5Uqk&prFxF1U#D7qp$;^4-hAyAQQ17}^TTR*r7__#78POJIOUieFR3hzBCuk!n- z#8GqbyF>eIaAbLHsD7kTAuyhnt>}7QqtkkvI<*cl0=@oQJ-mb@l|atEhCa;A%q>$9mq-AldN_(uF>hl8$<%?+%_NsAp$sZI!BZb)lz9_jx7tcl+u8xa)SEX{@>Z zDe|zkZL_H$%UhLpGz)$Pd;ZQRDY%qk7Y$ohl;nk#9eG2m?<((PDuG+?DAH3FKa)E| zmN%y|4u&0ENW#9pR2cpRB#~sn$=YzyuxbGoTTaw2xinVa zE(1iPPt`?zLx65UrbD}|TN`C^JkVeQA-yCGd~l>5#Qmr12Xm77K_933z0WC#=|DjR4^1&cTV@_TlIKYNkev8lVFmfW)JB z^Qs6(BaM9F>jC$IAHpRs&SK`#NFfOh%>G58w5hMMKYmS)GLVN>qr@qx#g@FGE zhLLl(GwbsFW94qRf@z#L(>dNyXlGhg%)@dqDR|1o_Hw<$6k;9#7we2phe9WD;*s~_ zDy}|rO*;#>h1OMC3}_SzgB;u_sH~+6DHARZPvsW9DEI>!{kK1esUXZ9FY|V(E#iwX zZS{-KowLoWf{$W?w7ZeoVG_MqiIqHNb=6B?_-^LqN{^k#pF`-sF_JekuyFZ_U;r!T z?L|<`Lr!(rP~`bnxuWV-=P6M~s52?j5RXBqy>ZDJf``1(3uE$!S=);?8NLIUl+th%-n0W!VlI zwSX<-mgw}NB#u9(4d){s+WzkT|c-^S$E^Q2sOpO`b0@Xg4T@#UNVC{LfeCAok; zv(hAxUr+hrH5m8Ve<%`Y2_NQ7j7q6}g6Datzathd2yq$~C19qKmxjs8i>P&a`0GG) zwFj(yWHmgN8~a`LT0g`VnW*IfmTZ+H$y}oAFQHPAOdMbcY@2xQoM0E-rPIIw(?Pf^ zGEN_^i&rV6J+y&h#$e}=>mW*ptu1qKcX|wcY}vX)DiFeQ=L;LAA4dlv1sSUb2e6eWXQu z0z~;%(@LMC#_$9UDSGw0)x`Uv+B}EJ%ID{%?u})wY;{Y~{Q`IzT>&=LU<-h=i#a51+kiLbcd2QYJPm!uqi3eZthKa<`l z8!pl>K=_L&$&xBLXFaB!gSXy(X{-%LTvm`|2nMmrXAk!?sAH2yr^2mTFJz~XX_yxp z6oK%Y%dx$fV6%)>jBe z)SFp3M1FD)PO*Iqzs+o9x7t?yuf_%hSIFM4q_xH7PL*B{G-FPHW+X8C3uPH&BA$~m zdJYZH2k}ppt*z!jeZr5kFu33Iq_8kZv){d36z+#D4eqP6HF_?C7Y#3)y63ok@z^Pi za;JWK*UpvtV=?CC`i`zKKpLv?ABi%dT%1v3x!4kt`|32+FTV8Nz3hxz?zZAG{7%Su zGcvAfrw`5gQDaQ}lFe9Ti-o-Cimd=JTDldhYGn1q<0zglA$JC+qEV~*izYACe91aA zZLT8>LlezWoy2c0ho>H@R+s>1K{&Pg1TB4z;J1zVs@rKiMkFk&p~Iq~wj8 z!c;hoGs5dRBNtwaF-Uv32Dt1C<{*+gsqOrOuOa=3J|l^`bJnD4*2JXF2Xiljjo*I^&%{6c)(o&@ z_{lE@jnS8c>$wsq(JO~(TmU;)B70StAKlxHz+JnKa}Z1Gk4oZNc_ZHfXu*%PeS^D~ zdW5I7yp;)vNAYo-pZ)Jtr<5B@2p8%C*x_9^Zn)U*qbMeeo`$??s$llh3o@R46+D7rai~e})sh{Dl8WF8gf|LbDB#Lsq zf^Cv$4^H&_1gae!^oYOvZniD7x8jj@w^9#kAZ+%{&|J&+&g>Sr?UY17DzF6+wcb`P-z8@sfgkkpl?R2G)YmROHmAQuMK7w;T?#>yh+ zm1coC(cJR4zkmGDFTom7lb`hoGM6}MfE%G09}gJdSx(j0 z(OF5~`zh`|ra|k)Kl`aO{b(P3h~BZQudf7h8Y-H*=fDmsZb3|lADr|QN1;wp*SwL4 zkrT?ucrznS2j;@Q%`O~hlVzqC)P+y0%?_v$p{mL?Z#RWSBeCu0iW1} z7rauI?{={uBC@`QGtTP`mfEU(~_ zR_YeZdwb8^;1i#Smt^>4SJbo(3n_rvHEMOQvbcEImXIzPlEhmMYupV)ppu%}_Ls^C?!eI5HcQOeBSrZZ8ZL3RP6s(H5L;iwAaQY3RLOKdO>bkUR>^pQA;KFAN zrJYQygs(K9x$ne12b<|AHRTAAaQd$p%iO-le$NH7x*x)ar(owA3rNM}S z$l$25a}USo3@Uu%7&!z2_0iw@jIP*)EpJ^SAQ$hS*fh_rN`#Y*p)BW+_GaP??t1`i z<2up+^@tG{1kJ`SbS6@BD5{l>^@j)VrQ0EMbGVxkXEPSAuVB_vGi5IdkyJa~B4-il z`684ajFNBdz zN;(iid+GL02HP73hs}ij+OqTvsw6au%U+VML{b$YV+-Gm zgGOOuI+4|%ldt_iVuF%2%MjllF$6MW3rNcHxl@H?U^b~ZwW5N)WUEF-9MEMJZAV@` zI(!QizlgZ4=Dgo8NG*pCdN47W7~U2c(?@xg(qlWbgb!=0kKDX_B8wxfiDuAMSklAy zsHw&ez~un(hrGZxU&dXPVo13+3p)CTrF9}$6DJ4n`QM>hORY7U6U5-2kUXU@VLN22 zh{}MG5sqHyG#SHqL@rlo{h&|mnCwZTL8O!LO1Zy=AknCuM%^lxYTcLW%!t~LcmjK- zTETrO5h{XQOCl=0ASOo|lTH7Pffv~7NRUpT=s{&6II%I5WNc4!;xCl&M%t`R8lC6&9y|W=D51_Vgto;i}($-N9K^2z)YBWpk7x3w89p!gfZ1pAMJV0 zqc81oEA<>nMlZfGm~K&r#kvoLo4W*A4pSVy#SDtr{{+iHbT}MgdF%)}=!;;mg+bzg z9GUsyd(V`h&ok+@AH7JsD}+^JU?8mJ(crQ^-h3Pul4SyFU{4jkqav=3#udIurI4l0 zfHo)03b-tMw}mQ-Hf0>Z$)J;KuRXdX9+kW?{Nn4S z^|kAFT8_#^l>te)UK=7#sx!Cw>X%ArE`@@N((0@B!J^@pB`P+R3e<;IsHfTwU`MXh z*7_*h*F#Hru+00ICUgLeq4AD-nuCvX0!XSwXKh}GRy#gb4S$ngh&23+d}KIGzo=l1 zCd?M#N++%S|u+Mj+(B6|PQpzy>=ECmX9M2RE9Limy zS5IX}PX%L$(fnOEQnl*0;AS$NeU0$yzOS}zg>INpjnq;d73AGjsLq`oX3cI?fNR72OAt+v0F-9CFAijcfptYwm@n;{80q;_%$TBJ<0{wV!|_Z=3cDN zKY?4)4Wsz1B<(bE8>H0Z%=;X3%}nJ@xDug{LX5p*^6gGWS!56aa9M1&*r#x-8~_zU z?yDnXax^u*Rw4#9&*p%rmSI`iB>5onOG24^o)02q? zHg66Txcs&Dp5thIGL3oKEOTV>@16ekYfYUk6M*00(=qpTQp6$G=jK3{oJO1HHG=`B zpDISsvF zJ9C7#^CiR+StDPPIKZw%!QU!Ps)@~T@70Imznwm52I0gRTJv?rU`S)7YUF+I{yM)J zV9^`Iv8xturX|>pj368ZfFHeSz9f4CufYBeiiO9zaWNX}``&1AzPaK#Vvo>*7^ZW7+&yej zG@9_RL8<)0aC0sT@z%Z8ypm z-_M{#@;{y-P1PQ%Wv}*1eZYrMvlkL6at92x4o6L8h-fQgZ=_2WSJ@2EZ+nDP5b4{BBoRx(y8^z z_pRQ{$Z=%Qr=>%E3*vgTBq`SEq&{%o9N6(Se%1RXjeZ55Dqy-kg_+CL8um8f(%(4_ zcLfmNOCy5Es1l7@K?veMxR@xV1QVSKpw({Ozdo53M7Uk4zP!Y_CTM@B(O^X=!6U z)|ZBlDa~wdKNEKMDWdUy9LLu(X;>1`KY~zR+j~4xRJ#)#+(SixY#fi=)9!RZi=mkM3w7$v3h9Hv8Wua5dSMQA z^!^^Mq_oTLVf=_H^JNPVVnUD!BHmA_L_N71m!a%Y>A(f!f$=c>!m9`wD`>pEWL=^^ zAB$jqW=QWuX&(V?XVGpk&6T$IIaGWR&BKsnk$Nf)$kiFQgj2XnXKGOEy!P4<&eYPJ z=Bcy3R#~u^>9#o6n23JXE+-hwxU<7XLfHJxbY%|INzYPSr?G*2RqS^uKV|mXcO&Dr z95?`KQ9Ce|E_b1WItm!=HN*`HQ=+k}h?O?rJYOxLwW~_n1DS@~Uy5{jx?U{F;gNYt z#9EsAcy69!`@3Zzg2_TOQ>mL$_wl6A)&NU(90zb6hG*&VKP8AkqKiQJ zb)T#nX_*hhO*a6hI?`qu(y&J=9(-5zWcSm5XPefFjI8i#(j?_?4?r%RbNMhurDo=> ztbOu``AFj{R1a-X<9iO$*Xo_tkze7WU>?8PMi}r}2{%m9lrxWhI}~OSiO}SNNw3u` zDo@%V2ac{}4|C(7QWWtf8^x<*BZ??X?)yw9qbHDF4uEC2G?U)`ukRlvQq0hwyDimw zVdA)?Lc?@_RU*(LTRF3dmy0p4lI!#Pro#DDhA2HWp{kaGmXSRh$t294q`wKb>j{cZ zrPv=K%9Oh(zc)?uFqzDp;HQtl*@xixJNLSoAH#kX#?DBxQIH{}vkDvAT8Z$55Ch!d`Gq;pPYY8r(&CDg8 z+MF)XbGMC}uDHS7sChIoN_$-Ks|}9)(){4`G2f|=f+dvN*={aS!8Ak5JL;rrQ5z>l zk{&R7w(1{HK}&<}4)>m&TD1EHj0yZX7!8}q%$WIfjzoxvYLQ#>K&~KTZFAX}Ew}(9 znaHi~`T4-&&VkR;NpRaxlbAd;c*lb!kRZDJUNlZBKP4YI5++)F^)I9}OA}dsU%_Gw z)#$abS^_lp(JRe0WK-4=&RGpdm(UG24I=c!aVNKl2NR&$=Zp_7WN%Pr9ELu0*fXmJE3bwi&0o6JV|Ed>x2LnVabNu)C1gr9L(-G0IJV0Xx?;!I5XFEsmd^Sbf-@ zqFa9+lhYSGI442RUTd>u8`%MAbo#lqOK$-p3Li$w*7byuf+UPU!E1%pw-LH`{Dsk? ztZ-$ka4L=_9b7G-vY)i-%bf9dkJ)~6!fr4%wSu+Rv~J&bDO8Mz{G!DLs~4UbH7l#! zG3td%mxAtNnur#`flTM>!D@uc%YS{H4l!B$+C2Uo*M(FibgG1&{s039z;>&k4WO$PWE3}3^CJ#cxona*{ zcnh$_nF9e^@@x*dp1uii3kS2qQ|s*VplZRHHv>;)nqljztKIB|5Ne&qVFgq${!iyw zRAb82RY)Nr(Zj*>KFpaS)En3j2b8K|94@Y5XJnrT3^hdI7x*f?Sn$O5d})*e@19Cl zfw#i!op3rQ?p3evH@jYhonVX5#DQI^qLZ?;gj|7nrc3&MHRhMN(o_;-hTmKN-b=i0 zF*>=;f30h8!T*bO9g;m; z|KeCTAclg!FDwiUlF>h#GRyIQ@)fvrx7jELfp2;D*@Zy}c!uNB4=kf#(p~btEqkpo z-+qDlH^|*TenGiCo}mg4N+2x=|0!{U=(;calhZ>%;kU)N#P?`_T3=P%Hx0FlF<^Si z!#zn0_Z{GJVCuluyyJOJj?YBJjt_>3U?NTx8C|x^+~+<8VBjc1sD~_J@;trG8vZf13?AKIMfgMagj>8oON zcQwgTzb^Oss(XHVj#uz~J>^HpxNCG+iu;A{nuOyY(bDd0479cS^UA4j9L*u&!?Dh=D;z3RH3O_^eO4Kz z)c~ADH}9edaC3~m_aJz;N&ge01_TQbnR|7Brard?iaEFG3fe?f$}@(78>9srnXfsb z%U=()00N_W5i5Vl4-qx6?=%=?W?Yn#&q{W&2rRE03rqaP-q*ku{g{FsjH3TP?45N~ zm08>WDMdv^1wk4E5fG5>P#PqpK|(=VK)N}iA}Za|dFYVtP`ag&l!gO`?mEAHtMk0` zJmbvkJn#JVTdd`B&0;x+``r85SM7a$z85#kLAEQ^Aw0dS?E4M90>;vxoG_yYiRi{6 z3fy&dO0OfR%qA5cS$r+yD%7H9w^z|cVtT8n?q`e~x221~Mew;~!hhx>ZmfViEXkPX zIVmrn)_*Ip2F)v~L)VuT^*bZJ-6CoI7J=>`Mg6X;#)U5KFOkt`1$rynhVm%rI!3r0 zDLPj0!@OQa%leOkAF}e_PC{MtdFTur5=P#HhrqxX#1cIAIjE;{ACoh!a$-1uO&|)Z z6JENu+n+uM?tuj{fc6tKzkgd+{>Fs_HOiK?)}w`3&@`ZHG2Y}fSe)fm*hH*@%XmTc zq7@4T?G&~HkKq<770kd*du|NbRqc;#^k%z$KbdE9v}SlQqGOY3&tR!?4&iOF0gl-c zfyCY|k*|iG^n=!*L`%kn?bL{vH%StX9jZowex_No*4-5GyG+puRbYE|Eb`L?z__Hb z*vXw&KDv*oTuihD2^M@{`^iH3WQlUb68KJC+i86Tkyawn>*~98Wdbiop*GN`Lq)n6 z$61K~vB3db=P&|w+HKZ8?!$B5j<(+xEM*q?Q#?DSiXd87HS!_-Pa0^SmBB6Z&Wj(Q zCUp|XKZ^>^Z!+%#f=3u|&2r0YXJ)TIzpl)Fiy6HF1V`mtH%CPMn!(d3IFv3~YQ(a)@r#KxckM{4Gm67#ely}v^)d3I-b23L(}p%Mf9W^P1L>>2`FT1 zgBq9sR-=8ZLwP@qdmTzP@DG$+Do2-3S--tp0i6}!3y|S0T9r8fE3Tvtr>XBRcUV`x z#WE|_vkztN^IyJzZQ2dOxWCo@T7Cta4#B-C5f!6&$}aUp_n%sUCqmVP%EbekEu0 z0^6+@V<1NYqh)bg0j0B(T%Ntf*FIQ-(!-{kt?Yspa6^oece^jf2Ckc?w(>+DLbuWH zgF46Vok11z=K^NUipkNP*T51svdFpZ+|UpMFI1Sr3c`^bFo!0bwLr)oliv$j2dM zxSzu;h9y$_3JC=w_+^fG3Y~x>uo=Q3oD;eNla6?rD*;{R^KA*dyM3yb2LVD4op<_`hgFA2b19ULrOPxy$AjH3L~O>2 z?_fD+w6$Tjv_qe@ea67`hz)2u70hH5t+3s;D5>>p;VhX5;fYZvG@7fH8-@wa~0!xym`y=#;GfG0tT!6 zzOdB_M2{0+vrl!(6nd zlt6LoQee>I`EQp<|KpIrjr=q(xOOoQ`Yv=qVp!w#XC6nLNug0Z{q{fp;csgvNC91z z6yp&s_>+I|{kIuvQYtTn{a}&c=rR8|O}%GC^+!p??@}NmH^EWokfVmv&norb)IzM? zs-d}7a7jin1x~S3jop=@meyvjq#}Uojl-RG%0HOUml7 zHpL3AQ{AH{`++&=28f3EZ+J_ zN%yaZpG(Go_8R0GA-hutccep$beaOFzD-?ogWE##SXZZ?g0jhx8(hq^H1q$-jPAFu z_gB=JAFBoMKqxF%)c#m9)If#qn8KU00c-&-mN zXRg109yb|qMvln;aPU_<^aAJv@L>2s_iCZRFK9}OykG2Rh_G*8DFTWO3g#w%C6ndm zO8dDREfEZEFdVjx9Y_qynPmtmg_$A<0?gAvB@ljc6o2(jE z&W*(+oG@Ydf&H`L4gH=@c3GrXf^;vX$VVN2;Rn0y>N+JZ?O;FJSqMFT_oZ; zu40xPDRtA%o0|OlBJUE|gI~S6=S1~9=<8+iG8wBC{&wA%vgNwrHiM2Evx^1&N5~5P zg=eTWo~oTTb5}ed?aQ;G??Gm(TE1Pe>|N=yPkv?H{imA^yuL<8plmlL_j4%KpV-~+ zHn2O8mZx3pQ@`@NpZ%mj__K}ab^wIEcF!sOj~V>lzW>+Hlv4%5T%XJ`-T!J!{l#Y= zfBEB;|1;D4OXE8>`{O5gQ2t5CIu3QmasDS}@z>twTMRo+ za*mUn<0R*AxY}=f>NvwY&hU;iyk7_}{}z;v)7#_p_Bg%$yV3SIw?EG9|ILm5W$)?t z?bYMl{y4Y)yWZwFy*UY>o??|HZNX zNvU&O*c=x&f7ja_7dFR*&2eFKT-Y2JHphj{ujT5;Ro!t__iL8&A8G3USA`8X%?ITaIv$`FjbRHT*f6yozy}$oCa_LNHM2;^YkU zeY~Kdd;}kC1sirjT3XryP$?9ny1N+xF`bGWun<)N0wg~_-`bc9l9lu#;b4Furknsb z#G_Or{Wp9eSmdvJR%Kt(o-q*wwyF_=!J9*iHAA~i=dKSgku z`#@9`~@Fp*C?cas0 z{+xAY_0mY$b(`}hu#M$00Z4mrtET*Lcu4>}7Vo#a4j(wCXUT7RP24KpD%l?Dh+e`p_GiYCU`-(L1*(ygxr z1JgXMrHHh29e~;R{&3q>s6*8ckM4TJLyE_|fYiecv+-m4_3*uhbBc1BL-B_=WXkmq zF8fl-C1%$k?sB$ur3ecYsQkPV$nCf~gyr<>UZb?hh>j z(Bwrz7|1lrY)xqzRZ{0@k|QqxjKU?sv{$-bzkHQx3jcy&^XWO;?8?+dOfDq>{1Cn8f-CKiX8Ak%4cV`LVzTa;FTP2k43K zZlC;R(-90kb&AsFbi%don@`Zht-@)$9>97hfDXM8-|))zH7viQNr9v5fE?cad9b@Q zyJRMOXNp>Y2>iN5Hz&Krfs3i5vaV}^_CPuv*Q@Br5)44s0Uqa&W8*f%rIcP5YQB4D zsh+XvL0^EI(0KOogi=}RX-XwGmY<FYQ6JBY0ZbY+ zPm}&^Iw~%jTu_qiVdVy#u?`1}qPNE*oFjnZ<3XMFfyf5fXfv%LAt5`nu%p>x0Lj%I zDYb!@U}B&5x=g1myk~8lDOj<@Gj9|~E$ajLK6c2LN<5SwET@|RESKbbWD&{|uuJO! z4{zaU$Osl=_3LSTY$foUF4tLp>{2fbnp0L_n6Wp?1xOEySXo&aX`La0-;stVBxCy_ zY+8*fX7c;Mpmm)G01x!lgjJ$|6r>kqdXS8-JYn>G4yCZ5dyju}`s_~!E|74Tc_~(H zuDanm$8JO$MA;}uIwDdn01TzD60GJ%YtI)NcjrNh3dhZNaY7!-XM+#+`l}e+vmshL z)zK#4xm26e_S%CnLJHW92kgo`nJkcN;RNuvYrTlY9EbIpVgLq9KT=j00XsvWa>bpo zHk!{VjsK>54dl4e`b{*&<0|tR!K}4>`PQHbyuygDs+5j$ z;21W;&#_m)5b@7g^aT>(mWKq*9qT{gQ2j~1aGnZ`A_*B4Wd?t^6j7aI3 zv#2LB2ztRmv(pvk*8vEL#9)5jp|^oSE%$~D|LzoKfA;|teez68A?_Z%O~-CGfQw_= z$MnOr>}1Z|;{(3+GNw|nnM{W)N(N$2UR8LLY~u}0bbgCjtp@xG z+YXR>2U3N7NR1Fga;z*ubR&AZJb81H)>RgD9ukyPEwwhPTR>vN06nx82rdquMFBB7 z?`)tl5ccVTsaeJ|FzwCC##VPxjP${H+SR{d#Noh**@z~8I1C{S2v2q&!0WpC)@M5y zZ48LOpljPMAJ_9B3_fTk6s$92JAe|4dJ`-0^QFYlP+9^l^#;4j^{C+v>8ml&y8FKn z_a;r56AYDh3@7_+C22#oT{S9>>ua(W32s?pKuO<`8e!{ z!|UHRx1ieza{I4t#Xwa&J?qY40}6-W_AQ&eR6=eE5l2hauT9i3UO*u*CrxDc6G&5Z zdef9Tv$d<%KHuF37#$9&C{Hawi(_!nM;ut!T~>LLrM6u3teGac7SasXe9qk+mwM>w zj8KSq2XXBKnxX`8%f$_2wYxjf+?hZA^Ouv z;XmX}*no63|LB&}$#3t8Qy5F0BPxu=pdQhi&XchNpq-31kGX)3fK)%iU8XBBnwUWR zJP^0^1n!~qrO>{cUHX2f4;hc7rh9lq#88!dXk@aP>SVoli(>ky`PIYo{HWcIPz>>~ zGy3lpm(moC5WpUE0V_ara5v>3@-})R(2A-Z1M<+p=JQ=0j#Wi~2*`7^#)x8;-;Q$> zt1y$Hk%OkY9rYyp0SJVm9;C%mW8GCTfFH&m4bYtTx7nFflaHj0o_POEx$EamatO;L zh&Zpr_AT&MU%mY;K>T{o+rb&3$y0VVMZK#GftavkuLYe6ix`&!-=3cme7iIsr{ z5RtRNylv#-%fDxMmYlb^Ypu*Qql?_8p{8Fm%77{MXZA_+(?{~+J86coG4+mGYB%i0H0_5xUL!A zbp$C>tR8JwRjs`m^L{nR=<*v2Kz}n$EfJ3}-R7<&dLocH>pACVGvWV$?m2{!s5Jn> z>f$J~(sd|C>3qr6GsSR2#&7@>K{4>=Y!lqz9D&yJIzG~s8T`P7Nlj77e`lE(g8-QRWCLF>f0R%1*Hm-NC(dDSwQV4 zgyIpDs~`?|(Gwv-D|x>KK&JQX;WO;d(o~A+KDu`X6Cwl&T}U3jYP>vJfjzokb+k}5 zIHt9F0BL7)MxQJ)A0vlt8Nig!j4G||K%$w&VEjFObZqZaKR{z}q7K$4U6!4+jdp-6 zpcjNgft*NRcbaQ0r1d2}9(h}=&x})zXK(6s z5d2vefWONX?LRt~S z!C!#@C#)PCcJI4g9|5qI{}xk=KU`Nmb4gw;`tkWc2^T+cBXXLc9x3&v`w>_<&OA9i zztMAET&*v}uqDph?1`Cz<7|5rlTPiM`hH8fP9+Ih#NN8kU}fqDB)y^(!na` z;yot-$c6iYjjnXvBNM56B7-rK@CTaWTrG z&Bu9=zXQC}0{uhlRltaN6DY%-I#bRuYJVLPjO#ng0?$%Tt@#XqS=xEZeOdQuE(G^| z0KzO?C}V=dDo>%;XWs&Q5dr&ZL7HB(W6hjaDuuSY=Kh~iOMYSl+)yz>`CDB{de-f> z+ly#oL3NPK0GZ+Bo4SZ6-N5-l>SApUnhvX=%J2mFSr5GfP`<2#xI<}fah7iQhNU1y z)g3A{osN4K^RLW4!3Bh~BQ#)#r)?Tfd$}mui8BUXWJ$@zBiB zLzk0WjXImHWhL2X#)a)mO&+xvs)Mjg?7a0Z@D3@#N73z7R_%v3COqZLk4=!w!1?7ii)tt=g0 zC04VdzzOrTwh#+K((PVA&AR3=_+*!(m=CEM%k#jceV0>|db>q_qnnVzG0m%BSKR_1 ztqR9FU`C)5liHULM0;3tfM)#V?ZiWGHB3$9vI%Td+%B_kTc7EYpfUZ-ucxDP>Y&KU z+En!Z{tg;%`BBMz#={gh+6Cd+6R1}vCkZEznHm$cJj*F5DZv*sHZn@(asoImCbQw< za4U7~*1^?OWzHKWXh!3o+$|8P$%U@Yy~}(w&o=<$uom#%)okcTY~uQ&6&+{7*-W5j zQ80rZaReY<-hY4RYWMI~VD3SMp*0^eW<2uOS0V+hgyKm3>_Okjg+!%N!y79(|S#*P0(nhisIKuwm|b_tqka8 zO@#w=K7U)&-Q$CV_GA-g?+cb~R>WLJd>A;L}yLu}uGJW!W>7 zZIN6opeN}f=Zc+4A#Wn9-eGd+0)nOm5JIcX(kxTQ?LQa=xIlYARnrA()ZKL zA_jd53t?G*gQT$x{Az{nILY^5^z6cqmpTs;OAF`CbnVP^F3@O$3hfaGO{S|RjkBPV ze*>`JJnPpL(2NH2jaGr&Xn#4iId&pC=1I^+=Di4Uh<78`M9>p+g*Q9ev21OZhKT_5 z*pTzNHNdshLAJ%mQn+IBq4Lh8C|=r1>=(A>sAb9a~KM-y-PonVOIUD!yxz}Av_l-@aZ57>d;JZ11R zfi-_$VFXrz-STL7pBf`X)$2*6GeLu-ZJ;3cgnsO?7Gf*7)YWFD5xmnr^9+^_YusPY zFIIe>qjm8RRQ|9V><5%mv9X<>o9;RvhWidy!C=lyRxzFs`){V)+5$8mSHn7-la|WM z@c=oIOs{$LC8ZDBY?Wg0Ug0PP`{3pINV$W4q4QPln7)3>1s1TaEfCw$5NXB9T zX*PlONU)Ka0ks}dG3W(@EbpptVyt+GE^@HI0-&(^q994;dO(??ZRhF16jy`MMIp%* zFIKpH1Vs)*(*3ZmYO;dlr1K9`yOpntfe?rx?)@fQ>fcJWzj|U~ybFTHtu0S~_+3vd z$nRRB89|K&kZTSUTj<`*0ZsM>;77xt=LAGq1tw6^mKKcx5kNR3}nOamItT5CtPDGuf z1cdpjcaS1Zq3MwF>NG>;&cu5zEB%_g{_pCH&nCq-08C(dr5OAn5NbG(Pnm_ll5N{k zQAX_MqoknQ=G~jF(y+I=ATu;z}h)lxdMQOPmfZ%Xy9fk2SC7ORD-xvN9SC-}MtASruD@TfXV`(W6)0J>*rAK$DtV-hL*u>OhaC zI^e}^H6Q$IQclpxfl}1bJ~;7UjGo>OpP|YFJ)qE1{7uks!*6tej!8g+L=BL61>S-p zrf{mC{~&PFE+X0=5{wVpf?CptD7P~x6ga@Fha^P1mZvFDs}#DR%>Y+Zd*MR!)fPx_ z>7VH+Kovo`cq^*#hbPZo*)sLWJrHfSON8}95-*81Nnd-R?%nxR5_QC9YoLA70JJic zqnG|pE=~z@ar7qqAD(CdBrDvxSX6^mYKS#UKrJt#SbE0)sQS4*AU;ctN1rDwdluXI zNMcm1ABPeyD2g}=D!obObzLen;`VreC=3S_Z8SD9#%5+&C9R;@RQs*ulPN8B-d%go z=VUwLuz9mby#X}F2!XV5s!Dv@W{>H7q*XhO4U1%49DX6xD{J#p&$JV->o&iUxd}po z$$Ew)HH~6r znHHiAG7ZRoIb?ITlW~O*ZLWS>_2!0jMa?-5#qvJaJ1yo^CdD6IkXy4D z;z*ImzoZ@hzFY?f>losj-mO2X`qI0xWf13asmO8^JWYCtrpvz44XP zwhw^UVh7^D>XmSX7!2_!9V@(F-9V%hV7WYYaI4q8*dW19veN#uAyd+ z)PA!{o}i2XzJP0181xvK`$uwg#fCw-nFFN% zTDKL$?E$Ghh#L$3Tngk|ev$>B?-$JeNIKPuh7=PX2jSl<1qm8d)%TKXT9tR&B#t#n zbBri%Z)>c_Wilkq%0{qf;*%Sb$xCU5GL%dOlxn3aB616!jenZ!PL@q??yYK%;K+WW z>{8F8(q;Y(v@@5_D(-}Eu!fG#f?FkNa$hd|>if7rDF>jnso`E0KYZ)M6Vc#s_&vXi zW1B(Zk}#ce#v9Qxd{b~+2RddE15Kp5y#z5|A4q;MtPD%(_FC0q1+JlnitF~_%aOrF zg3h3`Hrl3yI;Ac*)-Dw=uscK;Nt+VzO54+GC|jl|y@cQ1d`fAF*++1DI?n&B=I%$| z%PTCd;x}H>Rn^*7i%aD)Cn_)4T|RZ`$pgtZ+H0}-aSC0{J=X6i>U&m>+FOTtX6=?r zwxfBh#E2!d-R+lR*0-(NccZL2b`RSn=EniUTp*CUSA{iqB`u&ambyiHfo8HHZ=6ahW)b%2K#;mWC0pf;ruN(2r5s+>v=3fdY9 zy%pQ3#*2HIV|$sxCv|8uK+o&N(bDpfPtf=%IO-J$`zSVw8q7XahkG5Z;G#ZUDl)U5 zaW#lhXWI~^gSeAjvl4fOTrfOz2E zr_tZazcqgih9<4f#BuK?(pKydSPZj6jgd?#qT(gQ0>7AFdN z!c2sin-hQ9IDvh|D(?MZjlh8r6+K8MP~&AsaP2*~Br)&>%b&ARd)a}^X9t^LX|3<- zuF<^A^uVPE;J!K!EvSTr{7JQU{(C4WIw;W5DY0wA4sE7kmy?+b<;PJiRR=hS;5^R~ zeqZrW4wcTDVlcERTVPeEO_Xy8ZC`iyP!%N(9v&V#QpE_jPJgkJRg`EL|IHu2NbrgR zSYF~@Ktum${tPf-J$jz6`;M6C#6R?jXl_&BBZDG|oc~KZ_@nhy`&>oCOe9n2X}tAc zS=Jvv5$)6sYVeU$tzR*Jv$pRx_kZ}?_AL4pO62=^QPI2qhfn!o``l#lj1x{DgF=s87l`* zCN2X$vMKB%_CIDpi&rr22o5rdczeD0nSuQ`)-PxYK2oy%EdRe*!T-gY9yMHej^5=% zWBdgFA2aj-yyxihmijB`|Cj~wyas;F_WpzG3ICX(AG^wb%+QZr1s!LVzixGuAFiJcq6nU2LQbIG*e;JsZ+MfvMGrgqXElQxnHZG8Wi|Rg z{w;%ZjQ9QedMJ&{Es`YQp}pnLToc0$i~ z{TaQj47|g3thT6*i|H1N?*;f!VT3mg_cTn_aUBXjDlJI(-;^bXN8qvq?S?yjSzl+h zO|*K6=QcO!FKA&A;1gZZlIv@KVDr8~`#K5xk+B%LINm8%vDo`KvyMg^gji}rq~cqe zNo9nu4X4ceC0xkNY6lYMZ(K*afaa!nI>^(`Xm0KlIEaqVW?{8(3bz~^ka9aKjck$yK`1$4NgDdU|m*6u#(*$!fn? z?GxS*fM19r+k4@CYZA%8!tr8!%NNsXu$TFyhq@-LTr`1kqf>G^!rgG9f~%P?FR)V0 z%YvYfw6N-e@cG{t6zowTS7NU?7H#Yj-auB@esXG+fu8%9*}Em5Lg!Itm~Ym2*0GWo4t4JkO%)FW6hhoWzZe?G4Y1?$7FOZOXbLyVXBE zamoAO+53>n8S<$CtjhzcnaIKB$xD~37?fC6yw4*RpO)bdj+=ez-sd+^|MISEw)&$n2wd#HsUBog0e$t`Sp^%QeCKrc;~g`e%~kwN;#E=oON zQ@*6xxZeyh*ZA3vo2MjdIdg;Laluomjh92QCn<3#<&L~&-^e&x2@4x!(RWp|M;Bc< z)WhDkk?nOIvujl&ec#$KI$H=eR%e$6yts~}eIUo z`PIT-AEo2RFrpha&@0QLBAP;dBJ2$p(m2&kD07Q{hHIM;VYM`L7(q3l4Ta<={MhFXc&DMKgvF4(-irkvi+pEX2g@{`TiFB z?o-eFiLVP?j5;s#xvQ=(+IGT^j}zTZn(7h56k`s$yymotLSW%le{<_2g+K$6dj=%J zpFHDg%CD%itneR;VcZ&H2;xmav#`h0J6uUz@r$Wuj@vd5xr4>Jqj z6w31TWa-9Ak2fRrpAUJf1S%5mF*1B^{P;vve7l4{I;FRvb)HDw9&VtJo2TZVrPbc~ zwYN0)$+8H)8}iW$yn%N(`KVhl0f<8z@6N8xQFdfP3v0tk@yR=aYm(`2wVUK=P;7kc zPKt@{>8O{Y*$kYCbV4PQFMD9|p^n_sZ=TU@ygX0z3h#oU6ULg;;|LPU95>j~+?|+h z<$WHl*x)?ua-K_*1*}tX6wC~T0&WBw$)+cyXF86e7QhCd) zvv>Cjg?T)cYa@A?Qk64A7ev>VJ3a$PU>+6un-h+)Fnl`ww5yzZ@5RbBiDmB~!c^y% ziJ_dYg8T$`S!nR`oFzvUzXp}Ra?7>i5*;a7H~cD>&Ql+`FT*PMiC5K<3*+C*`_E7x zflUb4^$LlOH1_FDeHm4A{b7km$sz3xCain+D0b8A zG!1t0L*2G`K3+Q=S!nq%X(CjFHqZF!tR0V05%=4(u+&R(WkQ4$%;V=r;rwug3$}ar zjznh#*c?KP+Xn01h&uwhvWV_svbTQxII~7&ZT-GTN`iUUktdpnY(k788n}2IH zErxHWSsFv$)6eX|jj_@O!}o_g#f_Y*XfwOM#@$6>vBztLqwKx7qw2-<)b07C61EX) zPsI`%91)4127PWXb9>#)zR{u|jB3_qkXyzTU1&b^NnM!1!!+>5!z{WdF!!F1b0`4I zw!J9nKx+Wl?*i+gyw4f7>lo}rvAe++yQFP4bAlUuEv zjQRqOCW=T2p<-8jBriXrNlGBxWv9tc9+C=sFMVYjV^!V{)czf zy7?qy)BdN`xnkDMCmF2k+wv=l?FV-&+6Cb(K;{&K>7ymx3!>CV=keX2dDT70UxpSQS~Dk+a)`IXnPYr~vT48wO`^*3*+^fxn?qu1T@ zT74DNGSq~2n)KR&!hn?S(w*{X_Y)s_W28!qC_GI!d9NM*+v0xK0&Cl&HSN~(qYYGD zcznw{CXY3JWsc}ju0c$sFZcoVq67H)V+|Kx({L=KgWMdka2D03R2XL2)>hCrqItl zsXr7=v=vg)zrQ3IZ)LrZaC(rWgzJSvn#t>=r4RDc-N;CjTUv=K+yQzl&9Ii`vvNo8 z^i_L6*dko(={9;W>LQ=CnZ1Vyu($GqEcrT-5Pfn4CqjB<$-P4~$9I@CRYmvF!b*v*K z4*6^~2-s4{?JmyW!{9P7+uCYhQl<4_**$*TlaP{?!QA1`xlk-SY(C0cwFa)mvRdW=BQfggHc}Hr&2<_$Le*w#?&KN zUY*DA#`8n!l)+4zm)(&@&!230Kh~qIH{nE2UA>uq-9@sb(;haMwesY<2Q`}RxMH{1 z)IG5;B;BSqQpGM#J(_09+BajhZ}dQz(-vb6XHlceg?75GFDQ%|_pz?0b%`nRbDtTm z!q_fc^5){*e56P$^>IA@ZM&dhD1(=qtMgIl zU)6&C=^%TRTA`z?H(azDhc3iCZ0ZVOH^Pf6giDr}%-F-9p?q5uX@gS^yhnn`9@@D$ z5h6#Y3A7dPwzbMh7tCl?CYI;sVn@bK$)+9EdpXUFp51gVTZ%tO5#Ep% zyh-nGu8?F2l?cnWgQX{=T0XF;+Nk0pHS6wOVJ6i&Lj|9udwi%iqcFNQN@Gq#s~v{3 zoAg*AOpgSI)iGmdNnRsjQqHy3@~cFFD-G8N9{5&5*RCp)o|^dBS8MKOIu?}F(}7;$ zHg9*`<~-#VO@dOztj3k_;lrCPZyEzR?&Zp*m)$RF_GmF942|+U%OP~X%q2CK(Sl#e ziApB)*1p)b(aP7;+U%xVIH0nH%O%m zuurb~D{n84J#sI^(aH|KZ}BO;CJ_;dunKx=9mHjEwr5{-I!tOn#_ZLlyBwS3SHsH` z1VpH;oZk_&>+8PkTF~t(oMn=!pzx8lp(DjNvP3;7oyQ&#N?P33UhQw$kUF5RNiJ4K z#Sc50A*#rWlzXLnkCes^-=FxBWximV=7P^SXd3x|kqr56|HbAYrvhK_q{bvxhG>Jc zK8jFxCkdVf4>f9b$gWh`gVjCgX{yid4A@Jkm)oX%v&t&gq_ppB+J%_nu_VW71MAxfzvUbW`tk5$%{?@UqF#(&W|b1yX}2`32_9 z?;>8UU+sIw5id|6qS4YY<4RKI1%ekAi|Ty3L!|t??#?Dd$ODFT_PaHosEF-D=wx5Q z^!Lp-HM|4XwRjZthB_CEU(`tHG1O0m+H%uvs(Xxy!QpCf)| zvQBh5#>r=}N$;(`ENQ&dcDm8Kp<5y)%(MAzY}^&zRz`4cSpv!S(8tC;;X@4B-b#I9 z1-n&pRl$VFj=0hDJ*E9K%{M>Fx00!|OUoVh!qUR4bA9#F15%|A3pA?+2RK{a-hHPl zoxJn))Nj7?TLDve3M9Iw{x9w^|HyP7nOyY4bFqH*LYYB7b*1ZScoxlaf${9hX2()j zD-VnHx|SwQw{GY>B2LCL{%BH67;_dU$SJAx6bXmLvU@Trq*4P&<`SknYfy;n9n-&M zw|Dk%&fbSxcgDJ{*xZFz$cZAe^AqwRtJCC7mnAxZzE5L;aDR%JxY3tR>1XB~^9vUm zs7g^ymqrd(3|JdPANBHWz-yv)9hzenlQGB6$!3JfSXl_YzG9;S+6HT^>!{UQ4mxvE zd%b`)5AJigHXIu%5%t+VineDMT^&PbMx)?HIP3RSeAcqhJy>4zVx0_?(c`L}IqrBE_Y&FCi zwhh|uLnoY#ewb#gyX;dckshlLp_XGEoy%3#Hv6gShc3mUp37}rbHQ9Mbvg8KmvrhF zmYK4d4?^$KM)EE8M@>Xwcxybo>DXvGVOby@RH+*OCB0j@{fn-F;nyeGDRgP{t(q}# zDUGVus@6&^;@x##GFRoOp|4xXgVy6!vR#qxceg*fHluFIepz*ba;3?JP0=U#Cee$Bp#NAmEU`+-JgFMr3KQ!xV?LA>~NWsl< zA5#ohxDV!!x$LU3kSpM@f_6g5)I>ylmpIJxfL#KFxRu<(VoqtH7R9@#G}^)7b>KP=tBHPoHS` z&#I_KljQc=)sSyTUrEj(EOC9NS+d{F6<)z^N`o8R$9pbn4qhQUpTlNLkFa`oQash0 zhxc{nl!AIf30!>oAaL+I80(k&ieK7YsJnNF?4=%krF6L z_E^iFNkxf-a52^O(>f5~W$v5A30G5-(;vMtBkMY6sG9Q?aqS#0VU$7ze`#Tb=(O6ns2dtjFIVrQDRG-;GxHB{%D&2mn-O-S# zA00Ds!Ff$t#VdKP-<6^$^^g*lce}k4Wp5A=;+2yl>9SoJEm^^Awf26eY{_!7Oa|89 zyr(eFlfBNdkNmpB(OsvTQ`M-j#I?PQAH39NzQA3v(O}DLy-DWZRiuB-VFrF}gvo}j zCykd(*Mazo#0sa*+!Wd-8PEMNv2wTeZ4Va?hLhsc*xU1c!7(d#79T1f5Oz!+d?7;2 zIz$!=TyFnV1_QTT1}d`$IFj?W>qw{1AM$Tn+ps6Tr?2vOV`<*rUF*5Ee97hZ-Bg$K zH1n;C(S#GOZ)V*~WqrCE{q5Q7cmjE0ag`?=TxaaoSa*#JnTsAAy{Ni)WP&95_*2o24(q;mVe7cu%o237 zxF%R+3~J+r_Ep?5OfVZ6FTQEg_r@MChtHc>dDT%7^FNFYAWm2r;P24g&)_PAmDevd zq&?nZ>u5R#ck|FA0j*hX!Pdq2~oxRm{xti94 z3%Qr|dI=b+6mpG28jiZ_OCKm!Mfa(8NQLi7%jzc$!4O9H{lU!R%d#P2ZUr>cAJ_@z z^}>2;kmX^kI#i>qQ!$Ot?G%!zj*e~}+;iDpD}BRgSH3;N2%}!--O7YLI7wp6c1tQy z$Q)GzaFCRIHX_UU4Z*BnRHy#!s8*!nHkt3?Xg>?-)n-E zJ@J?3Rk3z{)qe0nz>(ogHnx{yI?TKArb*nJ5}|m+>sP);Vg1+j9V*Od?%0Ko7c?_9 z6i8myE!$2%U9ZMF`Ya4v$|}Q%o`o0d_sw-Tdg%Vb!*Vo#=N~Dup%ezLD%YgACYSEq z+O_GJWy%~*kqqA5&Q}6zqt^V<8QpChCk@wR1Wjvc=6LB$&-VtgW8!W5C+`Otj<4PE z)?y3AUhJG-k|S?an%KT7i+!Se86 z_#`qjpD3|t<$X{Ep6R(x6B0cja;72JqJee`5hS5ZeZ+NXF?c()Pc+;BFfwBG6J;NuAeY*T+JBN($~cDim0R zv+1r}liCP-b**L)d$MQybvr!NY=k7C;-aX6sABL#nvSmEGCk$eCJO1#A--On6T4bf z((g9-^{+Ro_{zuKWUMRmi&)tURo+h%iHyZ<*N%*9jk-vjz1iO>xq6i-oi9YsKphp_oMITxZ!~h|#!4i0HC?2}%6!Wp=?0 zvnbc;tkS19@4Dj}%s;=w%i4Xp>%Pw{ct0S{44tmXGilWo9@fhP#ai+~CHOwaZ zJdDlMi=@K3am;KfFNts3XjgKyK>Fc!KyPI0*1fj})0MX}&D)-=c~$9jkt#TaDQ<>d zzB&m<^Sz#R?sJEtX7=tm3SAYljGV{!$GAL}ueCV4hn7!`nGFZ3%_<ck!i}C>87A zvQJuCMw&%dcr>i{TRHEE#Z;-i4I@3|H`8M3bxm-e-&o79C%Ly7WfhUv-+xC@`=UU0 zmX3r|BwOoxLqT}w+<+>TltIg;!OpU4k&opvYV&ig(`MV$LBpu~DLXIatnIEr z(`X@H^TT)7Osb;SfOA(bW$rFGs2E7EiaSiURxVL6A*(6x8_!(}9+&1LkXbS_E`OhN zkoUx6>OHko|FZN06;*A`a;be^pEr-vPLf?EfnS+CwOWzfFL6FcYqt2T?8@MKs~EakG^O?)B3u@jgt@wjnV{Ie;+s6 z?cCK2Ym%G)NPM^$B^824)bsXEhujQmK&k|<+)2ljT9nIzB8Rg@61_VEU+&a>UW1({ z#G@{`8_(@%HiI}3C9Tm>ajBCGulZ~QMuP|MhTE%H>fi-_^|9(~-Zr7|%IylymK;CP zR3>jQKYv~(J!e10@ucuZSn9Q?Bjk0`JPHU8ZT|&*{9U|g( zN9}GghS<-pKOUQ-NeRlUWNx9>)C`C&CbzXyTG*N0ZY%YRdpl$m(rxjMwMnu#sWgE5 zW4ehfHs8)3zl+(Da=n9`3Px9h^pkxqLo zHpqgw{g9|&2d1s-T3!WLI7E{bkoMV6q~4OR&`9YcsS$7oeM*~E8QtSR!hyiL$7`4#RwAgzZp(x!-*QH2t=uTR19wX0RK&5sEox!t zWuSOg#!6#ym@O_Cj+8pl>N0*>O1qIKcE;6%Fc-BQn*V5CyZlBS$z6MfmsPooi-!f) zcxP+qJiGd(TJP+*zK|tralhhuQWHDo&E9YZde==ap78O!qgB>vt#h>PHWaO$EMpVl z>z*eTvf`B9ZLMXL>rzdjvoErk@a`f%_Q|fkf9>72FLaKU?L^umCidGMx9_rTHOgJ% ztf}Y4eA;@w;pS~S*~^Aoy_j(pbDK=1NH`_6WclP?+@8+@pFxeR6Ag^3A-9W#Kb=Zj z;nCSAjdF``TkNV~NH-%52K-k8(*6zYVg)%?j3iV(Cu9&k_slIuTBD}hNcc(5aR^VZ zX|J|u`@j~bGi_VRbrM`N*;WUh`8T}2>iGY-dJC>LyR{3q1{5#F36xTbTXBaLcXuuB zF2QM`P%O9wOVQx&THM{;-Gf7blXri6pK;#v6Y`9ad);%cYtFT+V{VtEYm%0!(hA37 zSTr1m>GxwnK0BHU@3x3B00SPYAe+$@q0d5>B8j2ST+X2Fi7L>z#*-PjMN;Yn2uAfx zl6bL10}^JzLg9d3h3PCdG!l=%14aJ6>R+z#x8+@?}L3>n;yti1M#Eo}9El$e1e zpL0Ognmt%M-jm;7~&f2cZY1$nX`HJxCwT%x>4EK`f&X1ku-l)gUTNH2j~7x zVn{sA;)`#buzUmqYlbe2JpF}=uj6}Zm+?2Fuh!}W{0W!uLoh=?rzM{te}QWJKg&pB zZ6p(`}^Zq8VU#2dG~LG}aP{YHq$Gk4#}zpkZGR71do(}Cbt^<1Co8}0Tv zF`ZwwT)S_NVdEGTm-`%K8?93hMK=$~*(t9T{Uk=lJ$_Q%arW@GYC05qL)vuZ#3#m_ z<7^+x;pk-wyd%)Nr$WNiNr;NsQ<1%zs%(6(#bhgp%EnvJ-SOfF{z{Zo5HLa1-!Dj! zl5IE8rTN>$uZnLfAhZOHdIL!5tWA=ts$ko|3#&BD=roJJ_0M`6PpZ-sE^woS)E@3g z@-pCh0^Mj?na<9v%kEuuEv5VRo!SG4AWxO<8_RjHac3(|ev;?Ya$M%n>|swc#Olks z5|sX1tz#P1R#~E7(jZZ-%!v6d9&kg5;|t-)I^vJo=Te$jQT^d0!yv_jQ#9M*7qN%g z+?o3~l9k%;#Zbo_7IytXLjnnpXS(Ge2lHMx7wlR2Y?i`e%47A_x6)g|wOlck@VJJ8 zl?0*m?bfH*XL{@G_?krlMb{Or!?(P=+DMiIWEK1A1~l__`qa*gTcCUiUr}XYD+)sA z(mH0FX%gA*2|t=EWaiR#x8P_H0kZcAlbepjJ6F0lp86l7D+8N72fF?2Rx&}n0(yDv zr@OBuJeHdk)ccdneDiMLOe9t5I|Z|W5~iwp;JtO-B@NR;zQ=G?`?vz-)%WI)*8w*N zTCtp1VbW0Hw$%D(U`(j}#rOS^PLG^3^ndY#i*X2mqt>F~ljeUt@N&;L7~-D`-qF&$ zpmY5xpbZ-|ET7mIOqtmDw!(T^hWrZpvIM383|vqEw%ER4HD~>iueem?`ip@+Sq9Qf zDrcfeT>xz3vU#o-8?0kdUg2pGWBh74pWrlWc$t>+CseigPwsJF?NhftiNB}ViC8~> zQ|YhcqkGE)WGt+S-C{hy9d)h|uhvvPghlx5jkwNClep_59uASz6}VnL50bOspgV}PcOHi zDloID^~z=Cg2ib?kgBlvu=05b_@IT)E%Hir4OxWyYS*HorQZ@ns zp(p&hn$*fpGx)gybZ7B}Y!6=hLky4A&)QraQwND9wk{5pD!t(;dPPWp#F4LeRo?0b z*?I;kuBh_Rx3gJ&?>MacoBCP0Hd?( z-KMjn2kzIh)CS^8^wI}ofb_tbe&d9tlgl=na6RTFhFSN9vX!pq{>rKw@dHqwwhDFhR0IiXgy(j zOOVQ1BV$_B8a6=m`*wgnQNKSM!|=*SqNc@lODq()DYOM~mVVG`-?Gzh&l&klD>=o# zlKYGWa1zo7?&^b)WtmM#N6X-x_~+HE+5L$?GeZwy2eLY~&ZW7=f>vc}4^p4}-@Wat z-|t4lh^f3%_c>&}`=zKqcNbIH!fx#hAaq>@JE`uU{w zmio;cnG_G$49<1k5V+uQ{GonQXzpIcS-6ptBf7Vl@15Vyb5*@rE>7ddhgFv0Tf(+} z-UGp5VTw{|o2M`*!_3GYgb`(R4gZ3Ff}}>xnL94?<4HI# ztIe+1{9$FFe0(s#;cH<4JEnn$1>8nB@2Dz03zQ&xc)4xCzyaKRzs$HziTN8YmO@yp z#`{+$VI_pld*%sq-E*?{{R;cl;!>Tp4Y*!eaS*Rh=uI%v5wf!^Gm&^{8{|v1Q~Qj`mRJYq-d*5N&&?%q>h_Y4vEe=m(IU zsjWz2;{tE0uJzia9?vZ$ulf|O9*n}MHCNyygvNI`4wta)-OGy`3o971IAqOeNONJM zcs6{Gfz>=}f$?kZIl8A#(eq=Lq><0&Z5iV#&@{Cw&rb-+?eiAi6y84v&SX4G*f%rg zum?H^z0)M_(PyUP4}S_YFEMF04&!@k%r+TCpS;d_AA>7OhCDIP(vr9gFQ(b_H->k% zq(4x&ePmoBS!La6O4=0}G_#k*zGXOQ@_JrKsW7lH&GWuH+{T&qzdw=FE;2X*WOV?V zLm}M4hPn`~$35m@zol<+geY4#&K>8*2e8r62zR3)^`Jqqi!M_Zj10oH$t1f&Vw*b8 z3*pl#y$`Dd9xeUC$`ko<8sPk%`~^}O;IE3fBofsbuTlNBt08$Uf#=dTE@BO^D-o;1zn}I1?{Q$8 z542Iw7>UTBMg6}p(LeI!`{F;TkXkw!)BlC-76O0NC2!~UEOAOJ$Vf7L^fc;vEUm!# z3bAmEX&v>ONrOS!;$8wdJ z%YcpGOAF61*7;$Rv+q486jAP{YHvGJ-)_H7Uc8a5MUHe?ou`BC?mcCjR z40&aK!vYzotbr=uzinVA6xQWAkqV3|NY3CDl6JAcUVLCb>{oEtFn`WipFFj9S~ZS@ z3WODLOIV$Wv_R45Z#DD>oK;4%wo8iyE+)n=%P5kr5H-3-q@`c}?6OxM=hD(#-o^(s z_(rrU1SL6h9M?7Qm6Yobn?0hT*L_gfn$|?XeaY@BWH+7%TXJA`uJ`M>)xjbPV=C(o zddpjV9=06*LEO4ynCh>=@tfpT&6;JHlWV#u%9ezHV8c2#)uZld^*zE~UH&SZQMK2QNxOkzx8VvVoz{dKC~lEaEQlDm9E8;Qx) z>h>V~vD6`_J?hG@{$xf?Y3no|`;I@?8K%6feidfcj+%(nii0=0S#I1=mSatO{rW zsl4F+xn^_0*Yk1a)feqM?eVAAPI0EXsL=kqYHOWz!h}vI$_mEr{MAg6TlQCXVWU!8i3Q3D(s)=a*Uw$848Xh+vt*o@J-LM0l&C< z{)CMJT+R|5)n)W)WJU!>(*?bO z$@m)jYd;}hi!S$j1u8465ziDhMp4|;m8`U5H8%X4PIZ#Ur$4occ(sx9LZmVbNJ8S} ztQ%wO;L(YZ7G;mP3>{eBLH08#O~RT zv+<;UBrun2CW4W%`QI-6@i&A^znD!LgZ@949`{x7&nS7+6(tRKuvFAYT7;_R`|>-6 z3W|P;)#$ELHAb3~<1mEY=%4eJoqCYFd>^5fxD~q1P~2B1Wb*_3AxqkoP; z->ro#5L=VteGa|L&CD*ns#E#uSh901lprqAqY#rXc=%B!jtat!(SbCFtH zZxT$FnMHRJVAlmJrVJz03*5i~`Z}kz%?F4FqIL?3dPb6VaN@m8z=@D2)=I=3)c;)( zA1=UNHGk%73K=;L>m|#6ihL}tzgfT-&NxC9gYL2yALr8148?_wl;e#4u_ z@rF*M|0OJupS_AkLo5R_I@??ODQ#xSc7Z1l#Odra@!O%A%z2?G<8T8d+w~IENMYiF zEJe!Zk4>m>g+R=z-&XaGj(s(Z#1b%}Rk5~jmPM?qTjokHc@q4_Zu6ql{%m!H2{G0R zdN!ax+vz_V5xaJlktLpnfewG`EqbM}@u$^q%bT{DI*mqM3gsVk+?LjQ%U*Ms%ay#s zy^1+KmdVknwiV+ZkZ?`GD<}DwAhZd3uQ0`imM-f%jQ^x1F@88_3S zJ!r5*Pvn9=By8gu*IQju!SM-%XN_!!j7HM;tQ;HN_qYR)rS@|s)imnr*6a1^r<&@& zY&~>s7AH4;6y-j7<@R@-e}Ds{f_NIK0|ePD%_|l+X-VuK>KPEOWkSJN0xW$Trv1Hl zy?}OSF;%xvLP|cj3l9VK?C&)+ghdUtQy5^TWbZE&Bx}su`jCTD*TK$RBcD7yQ!2R< z90d7m|Ejtu%$a={UV8i24^5@XF`5R^Tcg=k;Efhl+*zpDG;(pH@8xe(jk?WX|0v(! z^o0KFbj%R!XUjIt`|`*U_hCBUNh8+t%X}#*5%v1(x_<$4<-`BPxKr_ID_{TDTS%4p zt0mXp!aEw)*FOEYjoxUiSS3T#({*zGsw7SDUD!8zkp)7ctvA{7F@s9QH5rY@(gCb@ z){8-gJ;S(T&6GDRoZ?_$-jg6=dUl8P-ITyES2CVB`zL{IH zfv8iF>0nHjZj?*%q=!jtur)m2`C!oStbip3YM898(Bzx2&&m+iC`T0eq)I*n#zD2(Q(8(?Q=!d&gW+Lw4P+^7IaUivI7W+-C~ zUv~yi)DC}~DnI(7&X7ecsa zh*-I1&L;Bxx^F<|$JtFTE%rH2ctUz_c|-F)6!8^M9S)M44OvA%xr@o2?GEV%kNCoF z8AJ(C+S&uYg*b1{GMhwxr)5>#nCPaVi9oRF>QXlTkuEfOkWW1^T5Z1wuH{yNZwjdvgEkxdrN+aMQ669 zvDd{0;nj=x??wH7M@gGO`fF5alhKHKl(_{Yafd|W6ipk&$zY<1_7f@C^FvS7f^8st z$Nq&}fdKmI`Dfi9tbc~ylsyj$r3>*hJ=|K_PbCQG+xOOoSyAa z?KV63$okt%y|$Rok$mrF7d)DZR4Qf{uZ1p~;bATfR7>08r^K9Tw6B*gGQCNG zS`=Fav%zM={VE00tp0}@y>4cWdKRHlgUv~q6`c}fQqL5{x8pPip5W(2 z`P@-+I^coIuu))yNEWJ)yS7raeMf@CvnKcMrzZ}ip4dJz_rgO zStF-JtPotlrRzB-03!s(mQWmykQMn*58Vr0<*%e!q7OIDsJu_axOX%Cew4H>FP#x_ zHt`ZbcP&1pk7~omXzFacJ2U+Pi23)L2gyMW586|PugHTsGf2Wi(QAcKxsdP4>YCJL zR6E;xLOAE)j_DYAYTun-mhCb^=v@Rd*LCEv^2apFb`RO8#45ycCO?g;@$5h@JNYej zl2N&fBU*2h(m8xA$5>48cjjuk^Aa4JtNXp0$DNa?$~H!>ct&a1rgA*SqDRyci$Axf z!ghZbJdeFR7z3(avY9{St+bjx7vWWIno3YfGp4%cZkJ!1QLnnQ436Zs754AZw)fyP z{IZdaItJ&D<~7%OmJ6~EjHGM7-o%>z&Dr0(G%oz{(9Uzq!iF2U;51+sEnwR!BMZ2* z@E%j(@S_{1-^(DF%u**Q@OUa6Z(CbRBTm?TZ*n;O!7hx{%eQ)lGH%*Rquwh>QN_BR zoC{PsTAoHU)X1C_UU57GvaNR^}*tv2TE*zIT=zY;716re~mY>UM;K~b!H-rAvokNV#Q)rth&x83hw*akXgNB z*?gPwsD5=I4c<+x$4Va=&s8jZxzHe8+nN?47kFuc4pe=Ztc7!FeBEqlsAt8*SL<7r zgmJ(-{e;L^cSPlRYvKL+&&Qic;dOtrZ@DyY-m`Gemy9RzlwcG2>9uhl(rp%fehh|L zc7YZ6HruAfQ$!u~4)pVP@59k>6B*!1K0;$cb(+`$8-+Nvq-h+{6;oD}db9EjI-D~$ z=|i@~@Xduvy+g-G^UC-tWh_LgD?)L_(kH&Ov5j8Y1>~UIOLU%@9<|^fJobSqv42!Qv~qavcO2I4&~jI+wP;`aD`=W9=;EwRuX<-Db4S%0fmaDE#%+ zec6dF5S>wu3G&pBW8ReT%t7?#m#UZ4di0E7`*1l-Q2BKh)4h5Efi{pN14Y)Pn79|? z^xtMx>OW=`7oC*p|GIj(AU~YYumQ$$dc=89JKW0chj@Q|RX-D?i`?*rVEh|RZ6n9A zRjWiy$jSNmwZ`jrCNwqgkW_h{9KenUi(A~t;}f1-Ar%n-DD`bnJo4=(%e5hW={T%c zw)*mUB{%n>sQSrG>Q6#%BK+b)Vv;yQS^;el4=@yJ(ga{P?R9iB0~Em`GVx56%ae|V zUF(-nhzSYrOf##l;(`t`T~(iLUQgYqBwh=VWqW*Q{Pbc)d$P z%pUN0!xGBjZ(02>qV5-z9@_XBLypTQ;`LbDN|@?-;A=$gaj}KMxkj#gRAYaaoM~dB zx5%}ve#H5^pnwyFr_JZ|jwsRkc~#1($8c!`D6L)~X3oMmHkEnXU3G@h&mDT`G~CpO zEQUo9%AoeK5W%#1S?pT4^b!`Q~}r5Dw=Y7|{^!fY%u> z;q*e`-K1uQ*Ldtp3F;eKoFvRqK9-3fJ?7~MJ-Hy~ZiTL6S|DOu^CZw<4ut@Ydkjz_ zoR}qJ1---bB2TZfCns&~ye0H61gY!4bTTnVUL^PM+jQ`Z@3rd>kQEHp92CfZRt(!x zP1YFMh2u$WmzXQ6Tm_nmj;WIf(1P^unt7pxTHQwLBt7$0{B7y->s0!CKp_vi0jDtaY|YTk}coPnDdlhw_84{1xB|DCm>w< z_0qN|S=Z+{s{6YZ$m9e|YtBaJDCO?y(GrOf;MAh(Ve$6KkMn#s;5Ds z{^tz^s`a#1nHw9s)tx5$huK7cY#CCaGS;;x#xgnJJCyPHa_s7`Nuc+Oa}%`%=v1Vk z0Ua>xFYwLV3M4Ua+0Va74fM;Pj`XmQAPL@+^uUd~=%J=oV2n+I+M?UTT5yEIbNIwT92;UlgtyZBnzBDeqga`l&0&xY&2>_q=AQ7~(FEGedy2Qd)-p6hNESZ?GI^X|Sk z0c$u!O|E%5fPW;gdD5H}n7HN2vDosilcd$NT%an*`0pY`T*kknO?oo7Jjk z8#aQD7cVzSZqM$qKJcm4l{9wOTvqw96|E-Rd)ce>pZ4u2Dw+com2zTRS~=1czmRT zsXfqpqKcH_d-X#qID}tp2Nm0AGK8ERW7j)-`PV?m??H~g(1K}6D&mwh!#|xhnc*4c zHjA>l3i%i$5)$|I+VpU&@eY}cX}{2%4#h;p{^YEdgt(O)W5u00wQj9`ut|hyre(K% zlcI~hr}zxM?z>|R?jo!BtChr)6>f*|x$t%eid5ZMi(|o&@02YUEgT1QDZC_r6dA@6 z)~|)Q8qiO}`lyj)jq_u`nJ<^1xL$fSLhh@%B28w$q3a13udqCW{sGnC4}?}HEGSlR z1>YsM9<;2M#(;c+JM&p`UF}0Cf33c*L(tI703~dq#&F`*_`{!^W7-nd8s0p#W5pWR zB3Z-`3A+@hZ+4B(6K!1vtsT_~JdGXdc8|xo=^!XJuW5e5vCEXqCl?crK0v&wo`M)> zOI7TOeqy*A($eknA%H8mP`0%51 zR=$hQw8>CQmEdzA-f!PED}0&Cw$I0gJyvn4q`=p{p+%7Ddk|WtMvf{m_=6ecq)wZW zLmTt)tG(pGcrOft2(PWQA{2xO(_biRqVRD_T|xGUT>5f zGsO+|Y{6m8D-hXuf|VKZ{9!V2Mk*wyc}T0{1c0uY$D2Q8krYRd2`>FG@Pu{xrwJMe z@^V*}H0Ne&V?8-NAi^k0f&Jf1LH&tm{*`1y09H{9bO&3Yf6ZgxD}z%3ik|-17wceC ziV8|rx_)<{aXT{JXaakCf_qjA0e#2>w1*iCnz?0(RwoOL`i*2+5s1=^y3%sJ@kVO! zsPzx|8kTIqrW&TmI%R+4*KQ0Kc#N+#xy{Fz*HipDPBssulZ77}%5~AG%Fig|y+>o9;>zJuP zByRQn$5I~7j;Mtzb!?s8LIwGSqegA{)Z!a2ud72^lXuJd@GA`>3nDqv#l`|gQZD8& zi})dNr;5MO-|{vYt|rX~CiCWjFs%k>r)+VR23ME~ZMu5rUf#;24`w!>oCt!_3Fpn8yFBh{vl)XZ~loq zvFILDgg;)^WSfY_f7^SJNL;qspKizI(*!dnH3&00Lf&Gdmq31K{FW}NevP;AobbMn z&AZo)UQ)2FDv6JON`thCHjZvQm-_&j-rXKl4!WRRZMq2u-G{Grmo028*2jv0+tdaE z729Kq0*4A6e)+Be>qVez^ZE{gC9ekN_TTcu51c?bA(#a6Ev} zFn;sA9@~HuogQCNy(`@QsoPRjlA8aQ%UNhF_VzD4x$A75&<4P6ow6n{U1m^ zN_+ls9+wx?XRIB<2wEz;(3=I{y>G5;B2NSaF)17?w zK1XbcNLB;6xfML_0PxTe_Rlf{6dp`Mq4l5UXZR>tCzSE0WmRdNn1B02YEFX7@8J+G zxt9&_aw*wn%B3x&@Ch8q+idrBT$&CJB}H+;Fr~HgkDp6XoUG8A4YPh)h?h3!gN=1V%Rx` z`0;K8;$rO7gL-9}J4bI~EEKkyU)<%36_Gzj1-)UjyqdX21P zfj1k^Ap^1Pjunh9~$D*5g%et0-kK-aZ{Nw_np5+( zJKN{f$7+K0BGw*iBRa+5eO~YMLwXxbOaD(Nc-vqCahIgfgC9d>VLZJw%W`MZ++IQ@ zb}a7{Nglt%5ZN66C#OHv4&~gwt)@QJ!}CPT9SXFl{)q2QqM3og6!Gbf^i;1(siE=4QJiSAeD!2zccf`hV9^gGej>tg^qhq z?;_?-1t=9Nn~RVOKfl)=D{@^eYEktE)r14QuVoBNCPr7%^ZEfR#ow0oPv8Rbq!o4* z>TV|V!`#mFWoXjz-g0-{gYsBo(ftam4NiVKtc19P7Wh?g?is~Rw{H@7+FYf@^0CGE(o3hGSS<&>EP%~dn>m!Dmf8_i&39Pr#!xqS41}m>7?L8Zj~MazZ1d74R-f z1_FE4PUrCRYx71bhFR2=@^gj3fDoWXwUM`hR4bX20FahYI*h7hC+Wpf!)6ccmdQ*r zXOt>wW&aweDexK~#Jj#Bu#FweA~(n}NAS{v{s!HE)`QnN^Y5VYQdt__ic}CPi=3?ILD-{Su%D}$BVf5Bx2dQmbcut>uhTc zVpTq|UAYU=?<<)SJe04Z+wuz|qVndFnI2p}zI*;DB`T%w=(LHV)#P|ixm5mMYrT_> z?<~GyG6(GoS+13K)MQLkuA--=+{)wl!=VtTcvYEs%i@}Y-s0Q)FBxYFOE>n)Pgk<7 zY;Dfb-f{Rb2F3jkQ;SKN)sE4a6f7jc(Gv6wbv^wqm8A;;zl7t=k_`N}-`jf>ZUM9* z$5!z|j6g(*6vwrUz=@-*Au0J?#sl<5i&$gzd@@%2N{UEGN5Lgt22HF~rgP_&&yCHj z)%BJ;*H`1@9{bjn0)f>#$K=S2GI}PRMT0-3x#QTWu+;6N7VU#4k2I|GtLqzVdod;W zk2c(uvsPT^X65!b3G+nRqwGE95!s@Ji4U8|W=>7h!`8vuTESS6W3K=gg=K%Xfyi|J z;_Ul^JUy_cS!<#VWQW5D_8hqx?hhdY1?_C6dU*IE*{2&@1wIZ*>xfPDlIpz5%TpNB zfKxPo`NFEHCBfm0)J>4g9IxW#Rj;Q26F!bS?>xd=5ohDi;n?0p-paM~k36PD42S0|Kvz9iV?ev=c>aR&v5Q!d>8#-RsbD|t1c@tngeY}nPNBcL*G|AKJ)5-|$$oe^ zoe`QRD;)bXJvAIaTqx@q;`OI>@Db!QRDS6VPON8}^rdcEiEuDU5COMc4v6j;q=&d4 ziexC3-VEJNKkTu+8#qrqWh558O{cvf;{CwF&6|OIAIl?La05V-M4u=)2>1 z!+Rl6^y`P@;K*;Z=Za|u-+aDgGDn=Hn;k%_nj@VG2#Sj(b)kj`NZt+VcJUp{WOz)Z z{`}bdS0*umLm4~V2sUmRKT3&#*`5S-_QHg*XXYSt zt^^VgR*`YKvGJKq(0j5eH^wHs`Op|{TyUD%9v-Vjln=3AL$R3RPD&T+eii*+DK!l>H+NW!$AQpnU*vw+LOkN z`N302F(dD|&Ci(qegU!-r46kX;w`ND^f#x7HO^3(^JLiaC~{=I6DX!mJb2F&QCLGbhO-l;DCeOVyKh>B ztZV0h0f6(wanOcTwMqzGln1xgz0LI}zM#EFm}_D+kqo#N_jC0cTo!ClT0ppCbrj9f zMc_;$?zuygH6LF8H2J=D>EjPFQsV56Co`ePQtK%bJMV(QP@N7LfgNEr@(V}E|3l>PO3C;!vsbQKr)+GGbIc}imE6OGXXtiKv? zP0XgG`f>>7abc`WlQW>OY5RZoyIAZw=!pKE;AU~Fc8~{@E~BB?lF2AlCyw&(qWRNU z-J%ncrAHbck^6wxs?MO!!4{CQl@O4RA|%mY+XGST*~5CvUesPZKY_>&2*a zZfk$s5B38TVe}y0GcgGw^r-i;OIDPg8u(~Di8A?uP9YKbQ<31iW8u=_P*9O=a4MNHXf^%_6|@tEnzqlD(hB&YGgvP49c@qtLm{hU8W?Mi>0d+&;B zT)NrF@LO(;46ko>mc_G;4)3zz=?a;lr^4IjAKM+?VQU^aWN*L2b$owEh#!Ac*YM%I zZm1(h$X~z1yt%^9A=bIfn}JXO+Y6e+Oh$aUYczV+3pn)&G@88 zPQ2dKulC7BNQ0clHr-GC8{2^b!*08ptFnpw)d&B7qb{%iL0wSr*ec)t*NQ3m{E^1` z&NY1yJ>FmF{uhOypzM9AadEP)R$AukL|(7usAC<-nZ3-<$k$1pl?=}P-HR!(fGIP~ z&sFt2Bf<153a9Q!TEyHa>yBB!3X*3K_ZF^agRNmEEPmPQ{G? zgBtlbeP+elnzM@7d$U&w2VGZxrQ+;fYSG)Df3lct8kAPJ<}*+HnM&S1_$8iIRL+^D z!z_xK-z7DC-ne~o?Sr>9fF%Q?94Fl0UMGa@r<6qqfPIzOFKbyVKF^cY>fqLZoqpF_ zJ8E8c(s`DHZ(V#YN-O5G2#ykoKrrnt&yG|XIx$y%8b5GOGZulofN6-mX!^vdrjLY| z7jgu}wB4^;`w+jzi_&lCOS88!MD!jZN~X&COR_i$LVZ$Kw>Gi(McI$Ego@t@j`ZXh zkjr#@S->c9cOc2t)dn9!9u5;&!`i=Au2@()`~AcMWQg(R(vFHar@|{JdhH%9z9_lK z(dhrG5$tCG`Dt}C;OA69N0fE~WjQoeNosGdCcX*)=@oN}kPIt&&GaZ;jvQy-?OH!w zs=iHlb!$E|Y^`9@!)9(cnn7Z2?Pzo86m0d~*(=!G@Vqtk$Sr3`;#u3D`@8!lE#*-r zl{bBx^6h1LxAu7nDMJ)5A0fAS;S#)$i?1hav}Ls3;eNfh0q5#Br3k%@ z-*}xfeYS0`9V`*fE-~ctH#IW6P5Z2FN>;pnSObi}2d!mNp!f*IuQ++Q1c~2bJWqBY zeUvtxgBUa3ve zwl%VvCG5+iK+}x-;kG(6=;k6J?iGWy^&a{B4f2kqaYuY5cdKC{&5yIAbaiFzy#A9O zx#U0y(*)$et!#R==Vk8f)IcXAWZ^N6lfWS-&A8Yg7%_9+$Br&k?@MjKL$=D#Ryj!|%N%30C`sp8H5tj+>D!R~@p{4w zC1Mi3LByMBKMOC`DUdm{o+==-Sd&Y-+%~Cp`0H!U<8vmcl$nJVjv z?4C$NDN2x^(qgwd|Nj#y;{OmSVY$H^f&WLOd0T*jbyF&iAQ+z77|!%nn1lWO%_h*Ob+CnuP9pBUr?XifVo@rt4+ zg{kp4#pn43QfL%!PpV)&Ky!)EhjD1`@|CukXAVMr*F_Rjmq9SHXA4H!oZ7Fa?@JT; zOwRM4FO3?#yLAQFz}8RJN&gqCx!RqtVz+&VQ!=ClT;9WH@Hj1FK#=O8Sd{mnSj}r+ zZaYxhMtJFvm)G=dS*F~TK+4og1qyoD5#sL-jZ+)m<+`grI|g70Ow&Stxn;fwrII(i z*esViNk$?rg~gj8tFB{wU>yG&v%c`Ez}X>f&g{s;n%`F*!SRA+JmDNU%ptD9_2(j2 ze){mP#tJ3^PA6D$aWA3=ga$Z_8=YkqVkK6n^4$*LlSNMKE5TU5w5pSHH|dne<) zYn1%^Xvm7o&ztUO2I#~+HT2T*AaGX@o6jeyyKi8u=Q|>@T6*LZI<^!2d&N%KZ4HMD z@7qwI%nQTdTES&Ss%Z0vb(xPUMiuw@*ABP)R?blMW!1zs-n)WkpP`+>ihC~!p7BW1Z8Fv+@ z^lEdTB&XKqkSTWFJpJvG!{55RFxBhV`w<25^A;pj$)1=IRC7Oli|vWNS2e{Hp-(-h zJs=ByvhS^E%6OxB@|AbgA}Uq)ka}Ha&ZBd9bEk;Ssi3T`Fm3-)JLO0mh8n^0{0> zzp3#YFq#GbArGlYDv>R7N|RF};gUV>B@7gdEY|(vdaj&pZk11E=G9(ykzp0^u%v@c_d^B zy&vWObn!#^tQ@iBMFmaiY>qE1-DanY{xRFsPIH~=bR-#<)`zJc-K0ASqgL}22OJJV z7i9@H=dahllN?O8cjsmX7yxI~FNj z|7y`PmJW@t?Vm4XIVOlCvxY=8DHJ~s6O;H=NHTvHXjTl;RJ81oJX&#f{_V#Xk5;ie z^;y2fON-6#pK9!E4*Gb1hEi=Dr{KoclF~zewsiY&fZOVQg5KV|RuC#UGRQ@nWI7@Q zUWe6mitO%SG71j#OyT4Hs$cBN`1B#ks^yldWGOXUwprBRNq>b2?(N2n0Ltdy<_GpL zV=22E5}Gl}gl((D>^xaVgN6$Wk?G-~o(ARPhX&uNzG3Ky0k0r$&cNwj>hvz;Ws<42T4d-1( z3+U|Qv?-sw@=GLok#n{uOnC3TwE79}`A>>RH45zZ*ymZy`6PlwW>iK`E&bqS3mI*~v~+L)rm z=Gmm3q1ePbFs0NOVd2rjcB>ceGtJ=03L)aU+r;`m*WD}!RVuD+T%v-B8y8CtZOvoz z|C8JUIpJm;*Kd2P`IK;Pj^aSer-~1n$ma*+K7P)TQ7RGKYC*-s^U|Bbf>mms zBGrsM!wpq2PP4t?!c=u+dlT>vy>+M4jl~uiKLx?T42gx5nQuY^21p;Ax$@XUbXa88 zc==p)!}Z5WUpM14fP;%nDXynG87lh$hfGI2n4z?tI!Y~y82saqIwFW7R#bQNJDrgp zS^1QCF^856dkn_SfV>V{{Wf(BhJC~T!`@p*wbiZtqAe9#C`Ah`TA(F3h2pNIl;ZA1 zLUDHsv`BGxcPK8wrMMH^rAP>FAxJ{t(EYvd{`Nj+k8#hv=lprcU@+ELSy`F$na_OY z<3E`^-{`>21nRpa6}4Xe;e)XBejsE1B?ZHp}!t<4{nU3eh{L+jv zw^&u9vp2|t3b4qG{$0&~J}b%hsmC%6UM2t=`MR=ypLd1}XV9kMq)x0ez8XS0!30 zmpM{G!$DUeIL$0D>7v_%XAr%7!HqBHevFQC8{o!**o{&%3Z*L1)ila6w$NIp(?~bP zo8-NOWliw<_qMfEOKQ1gbX0^n+@+^)=xovTF;P!yH7>VxjgOo7TeF))mSOKM;w`V| zLc!J%y*Ln9Ct>?@(p!fZ`IemLlvt}U^ULc>&$+OTRIR$2SWI#eM$o~M+YpKyQ7AI1 zw6$zXPPS-=jVeI4sp|Aw*jv-S717s|xsZ+hEF0kG7wWOh=j{!crbJS{HlJYFHa`6u9QrZD1IPEgrk z0->AuMpNFxa2`@&xhectY%8w}Rdr7%vAazMj2l~|Z1M;OkMnR96|$2W3i{&Cesc~P znefGy;?qKw!(s)Q>XPI8u?=u1iz6)@D|34iG$(>+0`fbs=ck1ihF77?HlQ?!~6HvEp1bqnvrVY{Uo5Vdeg)#0_aPwDZ%#Zb9( zjzyZAgLkmPs)j^ai%M6y&(V#H%IuZAg#tw$K%{((GZU}i<}$9z_A-v$deXmbPS?K6 zrz#-hJKJfrNdPdV8K6TE<;}&WTXez+qD8HPtlu--?Jr7kC!Uo@u_2ja*zyiVWEZ`~ zsE{Tl#TEtdSNbk&f_y+JJsi$2q^%)a-1a+~Ut7e7b5* z2KMG&FJJ1fmU_z7GQ7j8Ke~lP?BlasQuD3+D1^L)#1w3!Ptl)$Ia!_~|Ru*Ty>~hJm z2XQupWHD}4pyjCucuw{JqhTo`)cwV*SyoM?pb&wrxV$51>a;LT!xgqP+2h>VhLF7- zv#W4mzmVcy-PWJo_;AtHs4-XrA3L=@oFPEmDkl)fBnS*YLu@acvNN?gDMGF|JqJ$v zR9Nd>a|fuP4VV31$&+ZP+|T^=$1Ka*ibU@Nn)l<~Nhe8ehbS#l&5bP~oNrMdn7_=Q zXHK6Lfrg=Um4`jc@;{!~H}ei3nH>Aq=jlb|rEzdl(Dr4+l&LYmujS+_+SskGHP*Ia!GsZMMO znQ8)^dLucR(r=t2bK=t7uA*sQmzSK@MrBi?4N4yt$3GHrUqBQT@ACIC26HdUmEKBC z7>_vgvky(lTZD)VSM`+G!|IScQuFTt+X@W@;Ia18`y}Vp9wW5s!fgvuU9PRlK1jDZ zFW!NzSR4txqpVlLm-$^v$`eT1H4tXA!mSn=`1JnUo=h$K;Elo&EJZy>^9=6j7q8Uk zwOv`12Gz2xbYPlba$o!Jh5YU3KNh#Vy4gzz+}p;>G!j=)!-c#yLgqGKB6?3IuV=q_ z`->1chEd!uit%@E{Dk&zFzUFAMnspTn(ip10ERxAV7>twMp)o!dl$TZJZ#|vOPji#IiF!(@4G{lp&;F*$TtT&8fh)UzO%DKxr zvoA+yK9m!vP$S5?731DkNJnLX4(PcCt~(?^FSU9%^exktVAG+eFK^XYQMac*3K~`? zL5-<`Uma8vqpp}n3`zuJg%m_*WUZdXe7P;Ay3`PhTfgdmR>5L5(YxVn6KTN_8C=ua z@)Cpsd@WuDyDx|B%`5*}6H_>Z^KV9dmFKDS1~bR@1gT72`SG_on0%}&k-In&F6d~s z+G+Oqm9{XG()ZnCVb4AnMZHQk6wJA@aM?vrVX&=inSd)q4k!26zFhoF{wWeZg@`b= z9}A|w6&gM`BlFT+d9Os0VW+1!X_n?#!;Gm+KhDZ^>)aR5jlhfNo^{tV>QD&`x3(bI zU}woFsFR3n)@v@WqwN)>_aqm@EttVy%et>M8H*?QZz!mKty1@}K6)~VM3erF*7N~G zP^K>-bV;Sno4Zv<>Icus#VMW7*AQ-J(-;MDh$bHk=*%lNfdpp$`zJr2?Vev7Zq4L zUX;6>2%PTAWTN0zJLkt+$Xnrok{0}-G#|c<^)r58P1u0W>esg`_mb>+?xg?%tlP#P z6m^B$8(RhP94jcNdJ$Ap#yPcLp)qhWsGNtY&jv4)SHHCRG$)SgyIOp&s9C_S0 z{g}^r%=GxI%E)*1+(zgqE}W~ycVBI*_z_b%P2vN6T|5Avz{yix-QOOqzp%dPm%Owf zk8AEK^@7Kjd#Olk{eIMX?4b43{lJ9^q35cc*InQh+vXNz3XxjuW`cE%mANBhrG&#r zD}zS}7fz@L4fe9*ya@79O3}ou&J#rr%0(#UR=iI!ph<0YMux)SLgxOHH<`Aokv()E z!?lvZ&}HVD&N;CIT2GK%sMPxw^Ck&6`=vT-pM72PR(SYULzq{a*CPVR^;Sy-f z)v>>D7N?NbA_N(<3k-j`4xiiA;My;=kH? znyZKBmX|d=99{;mr{syyL02emArh%CJoxBA8CgqfeTtE$M8cM?LB&TF`WKyc5QpJJ zJx-K-`OjfuW}P26_s}i(f7R6wdslx%_o3@1JYGa~B1NL_y|tA)cy-ayx`f$meo_JW1@&ZQ@3xvnnvrddr8%PYlUwPIKUv}DgG+`RV)y-( z5#x)6I<2cR51~$Dj;0>#56;XlW^gN(DS%gfrj&h80V9r#HC&J0)N!wP>c&u~K`?Gi zE0zqU_RKcq`^KSC*W(_Qb*31_Fmb{E{e~qSWqPHqJ}Pzzs8D0jMDBw@VY6_ zS>YlVcikHCxm>X6{O-KJ%&YkkDulSEUFz2!X?VYZ2wry3i)TSzFP^Dowz@i?aZ4P3 zUawX7ng~g6OZFM*5_z^OL0)#yWXQ+lj6lrL0n^vOY6o>qExSqcCPnV-Li1xRSt|`d zDblYa)qPiJchY4x_N(^wdIExR1u$t!HxK#{ZGOfT;gC`W?P*vPVUa-F$PK=r4jB+n z{#@_#nP{R-J5i>_p@Q67OI~GBJJUXOKdItQ_PJ*-R~t=%eMps=cg!;T>-#XxPk5@| z@?7ORE}M{Nl#XvV3RgIr0Ga~Zi?QFwffRIdD_Jog<62V=+`y^n;71gBhmO0tS(!Xy z*1Ly08eN;r42b*iFXJ#9aT7TPP2GPYj{SkR`K3>L?@?ZBLheW0zX}JfNbXyIE8kn7 z33r-lCR7x7d;U&g4g_p`JxFFR9a*AIH__+0J(Wt~SEQ(ewUC=B33OXikr+#E4cSeS-4Gz1a7_l|jM=1sNK+6I-|! z=36zdQs8Ms>h|%i@8qd6M8sG=9Y6LpH^AjYUF32RuvnfwG8%8<6s^g4+sqqjb zy{n3qKV*zT{g?SHk=>?SwcP!h_;Bs9`x;)1({JlIRhIjWg~5|sWI|7>2I3Vc%WW8I zg$BICCzmzkU3=Qkt{76#zmpE#kI^%9%eQGSM#?=E@M9@l()J58Cbf12J7=mlTa1Tr zuA1STsGc;Z&iwsq2NrpANb9V^{Oo+|h4aBv6b~vDKcxX?<5%7~c`DEMO09$>hW3KP zf$#TZW6)}mmdMLBj+Gvu;5K0Jc4{>el_}0f4u9H>F7%iqRRVx1CeX5N*H*`-RnfA9 zV7V>jZ^J-s)OmqBgnj^ERBDSn2me5d1Us5?fRwJ^qJu~oubGN zd^%GoQ$JH%NVQt4FN=Fx5c&vOggP*W)-5?7IgY!bV}w{IP7j`v{;q?Z$x`g(bc8OM zqN~*yFy|xZ8Ao)i=>XY~%@+7xsMXymrK}(d^|K;n`NKh8CGF>N4|cx4 z*?cY%s9XshD)I^uVV&$=eLg1cdp!<0mEQBi2(JdGetKFR#Uk0-z$2}~Q{cs71sn|< z`yIe)flb^#729EK0Xf4dNAo1OIa+>{bebD&tz+mdxOGb72RR4K4@2Ek1PS#o)~x_o zvKvmjr)h+5tNENnm&v>2aW7PEO2|v7-yUy_kUl7-KhSh433B997trNqGEuz(l$NoS zk17||1naoj@%DXMMhmBEF;TnSE>k!Jk{`W(RZc^KQ?1*rZVVcn9G{#H7ci_2CinHy ztBV*)h;JMAEK43-jjxy|7`h@yw5M|Q*MTFgk-Vw47Q*V~oeNh4OixWTWkhd)qnq2a zw>KoGv^10(aTzufq>=Fmm5NplhheC6)x<9jSUM{s^T@bl*J*8BcR|sfvezZYD7Ow` z9>&^HRX}cJ#R1+gZXiBs7aklm2ao$-%k=G#Q@xvzr6|L6_o3b!WecmCKHO(QZhv$c z0A}{xhx35;_FSQs!hGm!hFuTTL&%F_Ass_5g_>KzJpo)*djo5xxfs1g*^Uz>y8Gc2HH7-1?ZR@y=6L5oz95swy3Z2WE?hG z2NtnjsH(!*JGKoGDI4pA)d&I_$cy~P?Kz|y;d)N{YT$z?N7ck!0nMAts;FC_$mU*n zg_hPyu0`WnGQ_@(q*{_>NIrf(lqlT0@wYXPdxV`Y>`6M(4A>(_MuM}v)d-($IWQa0EZYZ9hq^Co!lDI1KgXbc>k1*KK`8qF<19r@Y zEsZ=Sh4Ftu=DSl`Z#uAnYkI6#4TRiHPnkbxDVz&ObHDGnsZrI;ohRF9b4;bNrYJoY z>9jQHZ6uL(XCFOR*rIT0^O|+Q0?xwTy zpHR8k=x54Cs6OAPXE2bt5%6x?#w9Vm$*$s#e=tvh{<#2{5yDM~DlOi8@!#lm|7a{0 ziFfCepd9%4=I>g~bHIIlqOZ_+!`U3^K)Ry{r7h?JM_M$t!fRHqH<@nB9jT#TcUN?o zgOnJFcLrkR7||d}9UUD>%;#g-U1f0=C#->kho$A2FN%*vDB45!T|=~QFVro@oQcK zx|G)mQ7CF~_Em1_*Y)T%|3VDRVEbSk{W(Q;xL%Z|c~EWdx7aA4JQFFd^4$;nxk3gJ z6Lbw^J?@wITxoAew~gj%2pGFDxT3l~2+fARwJ9W>uO()<);qKRd{D09lkE2P1XHI7 z@eZcbeXhgHYM&OeRBmNYrowpg;LuXC)kbF8pfLTiN4eeYyK{D>Oe&==9^tgl@(niuzujx^rbMcaW}GLBlj^>+o!cDlOyW0_01zIA1|!(Le31HWOxWP%GDwE`0b^MimAz}w*y6zsXD*G5ra$AL*vYoakKFaRtsg0bLDcJD>w5n zD?$*%>8U|deUt)Uiv4Kz+Y}4@qfB?4$>f@?x%y*rH>RNuXLL%G1kfjT(L7_;%H3~q z2bLzn!kik))jk*}t|ucmrPL?~1#__z<2&M}u?6j#skj=qFFcujV>0q#{eNmFGiLQL z%Xi9(@7v{HaE14t%n`bE7LI0++bQ^yw}2&E+iO$%S-nN%UagoZUiB3EnozlWJ^h#3 zs7juvMW0TUcok%Wg`YOec$&bQtPM7Symgm8lrtw9m@7h4wzC={Am$e2@ zDo@W|)V=e8$k<|zFrNq}TR(D5<3g;`jAR_-1tTop;5w3>$9t2_Ovy-Wii*-!IqGrypK1h z_fYjwo{zfdpcdon7iP$Ph7&~?P=#iKnokcOPrm>aBmv`MUQPW1%wcK0f!xzxPk9>z;=pOjC==xzf{b@*rW8XHde9)T9g&bLi+h zXgOQ+Q-#dToO#^y(E=g@D3eQ0_F6cN8d~ z&a(Y|)L8dXBAuG-GV_I*%_UK|NYUHe+bsfU1l=sR`2FHuQjOPm0l#`@@0y}U8+S#U zMeSMGeskvfv_yJUcNYLyDkkUa!>dB#Q%Q0Le*Z!0hgCl!^V{98*FZ$d(PrPn%-;Ej zxQE4srLOOu)j#z7~t z^3SY%C{EPgzGpo?qaMtSO;&quol#)JI#xh1^t=iFC2+w&m2&;@f8rHUx?e%QXD8IJ z!E)D*wP)s$`Or6fb|_2w z*Yh=s3Bd<`bg%FDn6Ce! z|6ma*$rV#3G-2XfM^0KhlGqO}U%9KgQ`^b4bcvict&Gsx8vFPn| z?lFBju_mUC{7GVb`ysni=*d0-h8Whz`A`*3hr^d~kuEa&G%0t}^-TL!7AK93fqMqvCf{rkPhppcH&N zzplZ;(wkAKoTbs<40JdtvT;20AgO*8&1Ga-+|JS4cd3ROb zJ`ufdehcMOe)ca1*xxeeKY!={>C>6J1JooOi|XGTBmc6b|3>Ejo%_FT{xCT@5%!V& zKT-C7=N13UGn$0H7%^OoUVkUj{(qb5zkUl9`A0w-WYG5f>)rpO>o0!}xtXHle;#80 z%QXJq(sEw}Ls4`A@wU&TPQ7OBr~|TO*dQ3(7y!CZ4guu}MP%g$9^Pk%) z?}oFdw8gxwy*cL`U*Gn#{c=t0{egw8OX|?O9p#FL*pH z_jNte3d>ysgS#)|I5>;sDoJlsJVhinR@HJQ?N6SMq**FFzw|kGw@gQ_@XA`M zJ^o&wSaiWP)Upa$ase*L)}G#hf7-A*?oV<$c!o4Z%{FrY2qQTc0=n^!obHhsJX5^j z8Ye!k@cxl-c*fakr5e0Xc}3!E6>0J&=01jJfG_{KczfmIsoF^Uvaky)npZm6wXY<3 zOzb_bY0*Z{asmLS+ZDI}NkCA|)J4ZbrYL-8y+#Sz!i}V%c^znrA3-%w`k|jgJJ0*a z#TfVB-ya4fHNLrZN`&kQvrY8klBp6U=w{R_qSzK=l=40b(>`(jLiW0kz@Jfs`+~Q= zACtpn@>qykEEQA?SBbm*opc4vRiF;#`h<7*-TYd>S~l`uy@0xCkw z4U*W`Hbht-?_V2v^n_wRDD~akd`%Q(w7w9wB4`Y(+28`c=NJAkl=(Yu!gTm2MI_-u z00rb*9&MIwf~M)CyiK+ASovwzQz~aG5+HF776!`ij_?FzHDhf11zs9(+X{x)%Z88g z&bqgu1VRm}c(#ggIj6HQ9=i}tc&r{>~k&Xy1D?iTZ6|v$%%d;Q2Geqs# zki=rxw7;n>b*S3w4THZp{EpAf2<~WY5fdiL`PWYPVv9GIw^Luksg`a+vzy&BO>MDE z7Hm188dr4OAo_MCWFG0jL}BHY-69j)7@DibY&AwlHfnz5Y8{6)A69*Wc;^g?b5#*= z3uS4!<}kFT1StofJ8wM?$7Nm{6qXc;Q%k``iBTr>$*Rh2(j^BDxi=c;N> zAUGKZ@pjjL*{n*w2sDyjHMTdDKC!j@?U8OtWl{oYt@NlaORoIxR|Reg$H>gN6c1U@ zf4=izob}RFmFTK#HkQuKZ-hV~?P+sX#6f&@TNhvWZ?P|lc#3`cBQ5NfiTA7pQ%Sfg^_+B``rH4_*AM!dfuy;}zlm@)8g4 zsneHAo(-SsvyobbpVCHDr@DW2rC~5HxrLvtd)ZwWKCDnD9mw8 z4W#6QIdwQvaB56cb+cFQAZ{iw$aHk`t$rk!?3`c#S6(^mnTqJ=Rn?PA@2=k+8oHlP z48GV?`xK1qZaCTq7*y1z(}bAYh5f9;hO zGVYZXcS)u~ySU#w!4_pKMDVDuyYH52#$_#u(_(a^d6$xksz^&_e(0I{A)ucz_;g-r@H6bkddEy+v01Fs(Aj0x<_n+2LWd^ zIBFPP1bns+$=d)n0`zBZ+-tG7VpM{z4bRPBnMO6;&tYOUvsFT>Gp@Xa_=10|u-!TA zKVBMZd?+{l>jS629x|Vrp(otM-uiinX|4rkup;VqT#R9(cO9)aYW?o8#P(dgaaH40 zVE3UbMv}r@2f-Jv3Nmn{oUo8(CPHa-1gtGu>Mz;yc-q-I`U8LZgB^JS%e{X|3mk#$IO_+P?$=^Wi z=iK+F9kB7^4C`pyr)6Z`tv=rx?~Hn(ao%JUQvQ1qQfL4*;zbWeUJi!q4=y|Gzsfha zFi(hhL|*57YmpFmXrp|v$#0k)%+)Fm05@jn4UTO;0MO~xUfTI<8`vFAdo;|#=VcHP z*FEc<5nrUUURRbqtm8KO9d7fp$V*!jL$dkHa7ODJ(3cv2_R#tx!`RuXR@Iq1Tnl`H zC;O&ir z-)dLs)imPw0CW3yXhZ-$1lEgLO?B2HiHTdQC@ae;5=IHQR_yk)EFtdqOCaXoX5^X5 z@GQ?LS6ofya~RzqMS3NB=$8Rn-!clbDDO0WF~}i+iS6doH^PFGQCX^+wXMITe>h;^ zw)DGu{NnJ@Aus%jfz=1&8}CRU0>4zCasH0gN-xATJ_EM(j|k7}#*E*xGf!WVF6do7ormww+JUKL*%dB5F#oXDr7uwermM8xl<7P+2@cOHBd zf-+m#i&Zl4+4-0SrC@5(i>8wyw=lU&yT9(4DZh z*E5Qo3{c50(r&`%kmU7Y3)ADKi_A)UOeN^gidUPT9or)F(Izg7D{KMN?8Y78=Zfzb z9kfWQO&dqn?LQVRX+5V2tk&0{2u%>)m=$DY;C~=`SSa3CY9qK|K1*1<*y}$7f7dJA zknua#A$OCy9>gAIYn`KAVletL8hG8Xi8r1h9cOO6{#j6@dcaWv$QMCT1gDis@obOn zZ7)G~lf@+*`KLuOKT4s;Wk|DbWWFezu8MqFac$puS5mGN4XqR6bRp*bBr4XRrSYFi3LSx4bt z=adSPoG6$@Ncz`g4!P58I0N9D++mr;)~)d9v-&02M>qj!=?h5R)_Wn);i%sn2Xe&G z_Ng7vtLN2q%OeP20LqNzKdI+P>^0OLe#N`m$7RY`|I{emm}EG(J9$;iJ!VP(a8ot< z>SMHZEro@NF!f>W)7!eAdzm!Bh*FUHSU0bJjEQgcs=A|myk_@_?U^ulgd!TPh&wkl zF;&){6%FrNf6{zvyMM?5G}yYTsx+|LtMd>Yhl!r?S9Ti%+@bZltV5Pm$#1Mn>xBHqyU8K)*zNg$o3G8ue@sKiL+Zo7OvAA^52vJD zbN2f}InUKg)b_b4S;x*041*N{lMU51L_m30mDys0$zTH{s{Z61#OHGons-U85!Yb~ zKEn<)QhT27pqGQ14K&-j1p*1wi9cqNIM+ZkJJ8Y=jfiuzeLt%?~&tAL0hT zyi2|w|DCVD%WJ|u02#eA6Kl^8ju>v`GKeX82kE$OHRJu8Sf4518kiPyi7FM^*@nQ~ z0=5~Hjy8#aL!b1ev>OHPpxg^(f{T-0w<0XHby9MRnN-Rhk_avOPM#g<+r9t6lTDsG zPS)tK(8I&3pgu!z)N8x-Mpht#ZFhBq>TvQdi{|}%TZ&IqN7+jTPF=8cM5dKm7bRCTcKpZ<@OORzxw z;A+Ee{d)0QuPS%QpR|EdBDq(CxG!ZbItUU~`dWoII|;90lR?6<#R6 z#4h$lIQCMY+^eDL$v|hNSrGr)>Q>(vn>u}*8CopkZe*Hm?D4cWMH_A;z}4XigO+iz zGPZeyrGDoGA*+FV0t21b1RHW}=}~CYuTO~^RAH(MGa4R^Y(v%0ls0`1TsdJXkO{zD z+g!9h|9MEGZIscD=Z?J$TLTj_^% z-HcaRO~ePSCH-~*`CJ36#44F7XY^vge)@&Efq&^VDzVif zlXaJND7c2%n8_jMNf;`+c}E4u)=2U%zV7LvQ8RqKnmm3aFHOvvc4;U zRNi?2cCs&9zwylQLAeqJR^cga4ddaots0YKD|G=|NBOs8B*wmLi>Q!o2hbEgLNE%v*aC2W0MH=cNHipZD?r zK{Ci^cgmy786F>GC~S#pY2$=`2({AqI3akUhVyb3lwYUQLh5+_b- zy2Ar2I32D2a5Qhjf1(|tUJhiqkaFI)tPv{~qYS{D);A1>kqPUtPqBo9oJaGHg(BaJT)g_~u(HK^t}?qfjuhrOO(1a|vTnjqMnol; zk_#+mDZ;S$&2HA7frSV;ehH5FuuDhAUA;R$c^A1TLY~J;S&vG5!naBA!bdZAl?}>p zi}=uxoEg0pBuOaWwr*2jbea;e6{Ay-I(h0n7T6 z54O(&Uos8^rmi^Z4PJ-$W3tzCcciu!vGXIRCR{rj-w!K%flFQm!-?gapJ zq(MMkgHJUaIZ;XR8k*(h;2n#bmq%`;++K!hSS?$k$d-_CvE!}}y)UXEp9JvDR}{~C zZutkjzHP|rnlqvL-~>Y#ZfN-XNz$xK@lue>c*UxqSTgNF2j+1PxBkM)Nu{}d{f+R< zK>nMXpO%77USaAY17lN*>707;JKtt9CBuw#>ZfC8f06$vey(VkKKuP32o3T=L|?e()Pmi#{8{p|nEy3Z`AnI9spa9^Th*IOK`%7JgkZTI8q{GF$pkU$Y`i zhS;_ko@TE9ev9JiG^B?zSXf`ww zVM9JuUMF)`5#JqtGvY#0e38B|ptVlcZg2~ha-QT;C`X)%7fLKb(osu&&pb|~)k8d7 z=Q=aL*f=aOhLDt5Q51v*9=XRGx;1l+2qtAmR98n1H-86+^z94)T7?gnrsCL;6(jFs zvv9cHYH#7#Kd65eLi$r7<*KSA5YjXb%L{xvBidEHoSZ$*L}X zuJ;AxkXa1~(v?bhu3nmSqEz}WpMK#zqMHY+vZa9W147Zq*;l=>4OhTinD4`oMh~6m zk%Cs68WDa;5!e7IQ|8TJ(7utaYJV>M?YOTHK4T@~VXVAN7I7-dvL0qk5DYvFlEo!iq z=UX74QOAm9WNf8xhV7>IbK~R#%15dasy`zqmUu?hQ6FwAzO6{gfC5q9?b~leAD&vJ zBO4-_F0z79<|pzAWmnFVX>EQJc-ax~rP-H6RXCZ9ktSN=?rV;@aY1hu11v7>!5w$G zAe4!_ckQjej#iUfj#H{*s6ugh2&x8W$}4cDmGY^c&zZHIAc|&eoX4*ph(Hsym(G|P z1+f~vw+`g^doT(8(GxH&eB>SCeIx%+=mnu+CL!1{(|f2LQee81c*gJI8SG*3zJIu7 z&Ew3io`%ZRThM^dO{}m3YL)il>lX8%WdtB*LX-rsEIE}PVO6BW0-{5a&WpJUdrhvzl*pcx@A5sRQ=7~ zQ~bm@B$l?;2|VO=9J3VI!>|gVrlrriwq1{bKu;KD9Gg)2X+wATp!ydOva9fY5CkB2 zgfvMRo;lKe4O(MZJE{FA)RG2$+4fAe#e*Og+{&HQU` zmxT^(JynM&qph0OM6Xo(s-WTC2LPrnU-@k>!G#n{%MDXJ_29&Hz44*pw@)9JM7pbN zN-nk9*4V`!z`Z|RyR%rFn)p4ono0fyr_`qLflF5=Ga9abo!2d^7nCI74PhmA-}s` zmZ|HJU;jJ2RhjrF(8C5}o%p*c_M&)}=tfIN=W?fyiGPB~0f&5?z&JiO2;CB8qn!GY zLVkP;^7Hglpn_DoV6Kfa&3W$ZW}jgl#+1*G5Rr<>l#ZVhcx>foFG)01-(tW6?EsI! z?Sl`?Av7Q6@;pKmE^I}XI_>qhcxSI&%6l*4q%WK@VJ(eE&d!dR@br@-`_V{+sq1&P zVV{+nT8#$Q$D1qcIU*$CQa zYr2ypO)VK7tN9XQFdEf#yqkEd&Bw*aXTIu|a}?K1lPJ(-a=T-%-RjV}G3Z|D&>YF5 zYj(&Bnt(dlOwcL_^pTafLvd!Qf%7K@H%#&*0#nL)04w&-5a%3LZ7*JepUiJj0E0f| z(b}Fz^S7s96`N398yxkiCe3WeZb-=U`m(O2x-Dk~3X$1HZ zQ>At=^@2$xnYuo(Z7|H}JC4pOJHvIAV6>6DcsScE0&{7`^1#9iokK^crm}GcSJ31> znVP@%sQ)M2SL9~Lbr;S*`!)W)r)ye#tMCr{)+;VeNVUd1*46-?rJ8m~_s)P`;Q>)i zz~r-`d$AE}sgF0v=2-1C?dv+Ut8wbXGzp}Yt6*$NsR9ry2B0V{9gfps|y z^(!yjW5SGeW52Z!8RgJRVj_q)nL^sCjt3e4A26FlA_>E@|A`4W2HJKfu+dD=c1cb?bSY?XI- zV&lrcrX0WV?-tjPsZjS59n z775WTwsk@i9gm_}RFk&8x_MFL(d&5(V`d(ANFgD`kUiWMM5o5QoMo4j$iOQ&GA!2{ z>N&C+2z;o}J&d~^2SINRqm@UYXVPR9sqhaVMpQ-n2B~=Rh7bJ0d{+eYqrkR!P#Vh4 znhdq80H13AkeMm$9rE^qiv(Z_(lxz_41V~aP|sU@PtYeiU-rahUsm7Itw*6)-OKPM z01R5>HpO~r%MDBW`0Q!~4v==g>Are2@OG4^v*2uhUOvTcHr+#W&AsU7u`Y`7jIrE4 zfGBE7{>{qsNB!Yd6yU|*qqr)cc#N8rn(NCD-oyC(oqRl8w-Re3oSaMmt%A+Gm z+Twbj_|=W=ain1y71QhRY7FHAdQw;b#6TaQz2%(qZXm}l61Snz=zuBQtL?L1g}V22 z@9T^t|1j%svlvF(g+ln95B?=~uy~i^R9g=xDgH~1_uG7W{KlBgmd3Fz^ksZNoMsYT z(^cM>&Rw4e%Q7d$&!*KfN9&V-GqZz>+T&LW%z7{!_CXmNtKx6*8F6J?2SS{PRB{T> z#=2mgq!xn)_D+DjKoueWiH*lkNrmMlYK0JumDF2jfL)Jerqr``sk@>Kd{W@sn7TxZ zN&B9={5btXl`^}u43kH9IC&Tgbniq;o`-bNz%;(Hw0Ad`GpTU8?Y;!_5)MGHyf`MJ zEVTNH+_Ez-fBH+JscNJ`Zs){`zhf4kS;b}$;VN5}#ELK;rN|1m-kt|JeH;3QVj?zM z^Pmd1NVVuA(b(}nOGXJ95VdC($O{JxJLiQyUfs*it!Nc5P9TL5IA)lPo(}BIN4ig* z>FDFe3nn};e3V_4N;@?_-V03_x~pEHpP;sIhS&6~EAm^agIR~i_B|&y%holZA6S@f z$kqj8XM>u}cDJ&WZ?IS4M*Z&7gYk|IsbsZvzT#e3=jlvBQ$GCzfSZZp$ddNL`MnKE z*fT7@@*qje7Qa%+vglUk;{loFL}c)8#@W+MFlSRCn8c0xSk;<(OcT@)fm8Vw@9U5R z5<`c_O<|ROUn4=l%zsGwoIVj35C93S?M=`|3dis~yv$VB(uFz<8_xKcpF5^CMa&Eq z7c&<&?8Ee=B2p~dtCza0MGviQ9=yqatK<{dc-{X~p(7R!wiDFQrkv6ny(T7kb|4bJ zSY>F_w@LJntx^D3zj5|jr;<->M?*&s6UZhu|kSZZGNsI2xu23&v)v7vE-= z5in_s6inNan5{36BTg{1BWrw#Q&e^_J>1r<8-<6w`EC2>@3LQLsluA8Q!YoBGEPGM z_mZCoY!}w;`EaCeN_;yBzpFeovTm7z*W8>E>fPWmuAU6dv?$R9^8c)*eXQjPU*iOd zM4<+sjWEk82fZ15>4y?j1y(IctDia?IDX39V(;%MRlSw1^|1{cWt>^Kjw@uM0T02F(Kw6syi~eTu#I&0|(jOgNyVu zNZE5B$JU*pDPVz~$sT={__c2 zCc-aros~Yl5n_;JE?SWe4}gY2v+=5Bpm9W4OcmoQ82TViMb*j43}8^ zV0BOy5vdLk6MG!F=W5k7)Q}~k0E^8J-%FaiD^o8~Y|b4kKU_=tGWt+BxD}FnP{Zi8 zC%}koYCi(D8e~xTq@Ih)notoH7ap^w4w-b8YJaVbuX58b6M$zO}PL-vU ztU9W)oJo9BiBy)@LBxC9D0>;lBlKNQN-eY66YmI>6A_obm6nSda6=!Q7aBrgMObPz{@9mr2{M5PdSIi z$L}PL6_*8m`4g8EUcjH7RlqE;kXKAd{g^BSCR=dc^&5Kl5fx5FGV{q0+50Ii4ZS|i zu%CU=A}z-$v(mO0mI~pRq;hLC4H8)O5W8S;y&h6xO-lvt1q`^SJrgY4>KXuhB|;nr zvuH~)zd6+Yu;~LQTHaY#8E}29jT;nEH3Mfm6@AP7&Wj+u#~$z}M|pYXmY$pJX>XJ6 z4%^)H8^Zv;1me^0A&LD3`WM~d-ppTK>@-l?rXv??Mx^)IymJ-g;e795B11h{O`PNjl%0OY zkT(}gXrR!ISnaKxd6az^Mvd&pUMlGxX;AQdFf%kMvaVWr-S?@af4iU(%W$fesQl&j z`kBpUa-CQ~kKO{F(e8o+cqDEj_w;JzYsFIz&@#NexU03A#n2$OGDc_R1-_D|;i%!( zGu)_!F2Itz#-+R{ELEKF<59&>|^<8gL6d7EFFESLqUX%}<*dOLWn? zGA6HILDVgYWa^zg5HmNZqIII(v339xyROn~;Nmp00?1c4SDNx4AJ0D#=Yk@Cc$DS} z@%j!nb?tyywv^c+eDzFmSL54+RTD1D^cR;~G%EkSWlV^I|5*HItD_CxtMK2*`D>-O zskBG+JZmx^f&Ir}H|%G13;ryLUXp=o=boRqZYEgqDu9a#S3FDiGuI0_MI@RrOe}6V zhD-R+75sb1Fgat$5FAxBD`PZmK`CLB+rApKAC~zPjz|L<=(;~C<~B`R*;54YT0}3H zd@qB)3t4S27Z~eHcG^R^uC(l`E&Uq%n^9Vb-m%PTVuQ`V?V5YxFw3fJ&27eniW^LO zsFUChE6N`!P$>otVWmYMD{;|;WjK^UA+I^#7cE=cUC${l4XM<7h8kaW6>?-%WR|3? z54l>lr!T$3KMXQqv|g>0Cw>7}NA5eBA7e^UF+16Z2|o3Ek2I2~ z(_udfXR+raIM!lNy zsjv>&?eIwBEb;uK!GR$z%4$vL@3&iTwVo<)hv1LaL>iV@>na@@aB|NUb>BvnO0ShZ zEUp({p0so{PpypKOU6Ir)&IDx#2UR=+RZ@#_B`jYH5M&Bl{4N#DZR?jk4XYLM}Tk0 z2a5MbBS!p#0lJfkH3=U8<-MwpURdsw8!Uaw#^j!ZRZg{pzfoVj)@Z1Y&iV*Y6BIR0 zzE#)!sCQbMz|<@Dn$LkhrgsR6HptSRGhKF?>mj@mweuj;%F4x|+`+BI&Ap=bZ{F&% z>jAE5UD3a0ivatQ1gtD|&J|B{;q zh5qiB7Gh=r3iG#4QEf%oQ2(3;+dJz!t=bJO8BS|h-({D$m8`f{%LquBrHqM9LknN^ z?6Yqu;Rbe{dv|V8Gq;Ui9jx3jmQ%w$lunZ+*w2}_4Lpw)vlQY4OeQtpH0)>9!#8#w z=VY~S4&$WHC_M+WO`xvc+>>CMlQDVuIm4I>aX3$+Iq&M@Z?_cMyS(~GiZt66NsmI&;OSGF3%Rt;(`2%f&qI^y#D9YRu$^P0I z|JsRHtglJ{@O~fXoN~nG)_KnL1n?6LL{2B;&zd+V8<{wOL^P=RQw$Bnlok^m?zias z;#$iu3Kpn3Dc~6_?Axi)P`Av+HPjq%wa9Xl;5vOL9bfu6w}8*n{1aUr+04Mm6-$Nlf6zF2>UmCBrR!(~t>W1@uca?SjrqH%Z9;N+IoFsK4>oY~N=c%uCvja71S6`3%bD%S| zD4vejMOGgqiwKs-=%{IJP;OLukNh!VCz@v5yA6MLzzK5zh}AOi)_iI{D5}mF?2ynE zY+PZZNg=mVAA8or-!6t3x_3Y+Dg+w9Z&#xZC0zbBVmnwdpYepkwFKcV?-EixA%cJ+2 zoO+8xE;PZVqLdS=`Ym==w9DovxV)Z9!Z1hp#rR1$XN)d>T=)|p@y6+0B{VmYY1LQn zk_AC9X08kTpRC7*aOhU_6S;-5?fMghEm%`8 zL5I3Cwsg1Nuy?n|BNCw1rf3C~kEy>9b2Iqm-U{epEq1F&jl9+t?;7(i%U-wN#y?$` z6e$R(K+F`>EW16ve)+Yqm#7@tEr5tX!cV&Z6#th*{pa~U_Mg3B`XT+-|3C9H{4a#fe+<-3~Q1B3WoXOzEabBJR|yB#+2FofN3HZ1qm@C|)B zKKaU@$3I)wLXx$DchsJoATcSYu(6M-gDzT9V<)j*i z%R1oawP*w2({U)5YM`Fq){yw2@sL@K$WYs^(LA>0iEL@`MBs3BjXhFoM(s>Vg1K&$ ze+nzGhmwy(RwLlX^26Sv=l((>14m^tF|HN|R3uKf_O(^nOKD95l6tP2;bO4}*-CN$ z&kF$(SEpWDs7QD!;@VL^G>nL>C0H?56O4 z8_~;*MNUph`QN^9S(aM5B;4jK2L^Y!uWysZeOMM#45X#m>h6Y|1M2a{;pa{$=rEsf3HnQo%B(5q)Kz#J{m7R^a+o zYs`v!4EMQEQWx?>yqtZNxaW42F3R&?2RE`qMfg6_%vpz{(W?2XQ)y#ao6}8ntqq9}hYlQiNzxCZEPY&Mr ze%thRy`haZ9YSMBo~DvBbu}RO996W?D_ zcAS4>Ryp`$%0EN+{%n}H0(?r1gOG;$^gSJ%n)~rAU~`&NifO|TT*U88x|+{IOR+;J zEDOp<6!w*L1|JqN8D^3s6s~N-zbcZ$qS|7{+w~bbeUVfUZNf)RDD{HQ!`K1RApQJn zI7D-Go+j#V&T`~oOeTJQ`>G^WR)6NtvBahyf!%_8rcagmw-As%Rbv$s({_d73Pnoo z?5j1N{}1W@@^3<(^;C+5DdOK*@Hg6ze})*`r*WTaHtyi#V3P{}8ptFo$hN z-(^A>CO6NPct(yog$#L}MltAm*sMfMpfHYop3#{+B1HA>nu@E>E@QOzvZR6mP=gu2ll8k7G(z7u&S3rzcYUG zW~!O3k@;nghJk|DoBc-t7`Y(VsCO5`>RCJN1Cf9bxKhe3V0D53B;B_9_nup>YuPws zn@%3~AZhE20ty)j_EiZjjBQ-_)gI@9#cBKHW8h0Y#{KE2d_=^;@#4xyQSdT(Vj#k0 zW88BzjzC=9*GzEQKGy5f!_KAiVB1>Qo@QyUlQ2V3fH{DxxQYIXRyr@58l<3w?l#>f z#Lu0>cZC5jK*eCqv4i#11I>kB2hqqyv_B9Jz{eP(Nlis!397Lny-~@!D>8d8v650d z59x(SK1^T`x;9^DqMk8~mTvRXev`aokHJ66zj>oJro*g$2=?HHz%n(~o-U(qrhKVM zw`||d0l8hoXLuuu*u1_1g-kE*MVlqgHF6n2X;(cgLV`J_e!E^R`U^jhvF@ku)#}At zuu1Ceyw#U@%ClbMqFM4Sh-!*M%QYZNWq~KWjQ*WP9V#cLP1gTJwO7d>+hwp2Fy^$% z^)-EjBA>qKxwYexJo>oCXz1Ygp)G~IOX*T!Mq?_xJxPjCq`Y1)!IN(PzkMkgnF7KMo5pT~Cj79S^d z_3XChE(r-_F^Pw1V-^Q(LClilv#A6{=w8BE-)#XYv%nKu|F9E{92-PCg1q#wV=<|x ziLZi`aOE4f1cUFmoxBOp^#6J=Q~<=*1-`M3|S0x8`1?`_ z%nRhyvp?5Hdz3y-HQQ~HbQ)?0gxyDDMYOgU$L&@L;qFEA|;>b4)rVOnedPV$@_YR~~#DPKfbW96r zp-=Lw(O#pjIhrv_-|tK8|CA`n{}8}<`q^YC|7B#P!Ty5e9otVy4`Z&r5wm)IniId} z0+Yj@RBiX0>0bIy5TgXsAHj80zLXv~vqm|w^lqmrG`O#(+L0{&X$&k+zTfU^6*98{ zsO(7A0Y$e@^cpRREGI(%6d1dc(Z+y>Wqc2D((off9oMXvc)4j$x?Y?q;zABQFOHib zj6!Tm<+UhCqbxdFI;m+cK76a`b>{Ld+B%)A|9w7jdf;&&mK&IOijUXbIlN0uX zW*{~>c!9OuwJt-}=@`XV<#pVuFkq(k>xYo7R`#pwFnJ7~e84P3enmqQJI5+=;TCjJ zRv_T%QEi=C+NF%0>ojmG6YT~#mUwj?o2nWA@OpBU^T6de929LzUtkRv%UBtbWD1^; zPgOu=YQFQO?BCp3sK64~(5mjsrX^P@X)FiT*bwP`$IkoR?`Lv$M-f?yKhI4{4r{+X z+aVSoSLJuEGE2j5L?^?);d1Y#d~u@FqI;&HGt0`ozarcXXr|I9|FWZUur~1B`dUyL z`T8$MtSm?~n4RX*{bNn#-HjiZ5I2-5zjL^D5J~f~OGcGdvEQsO-55x4Q7$%JbCMf3 zSXGmzuuxG@fBtQKgM#)VwftL9zwU>1{_mL;3;)?&Gx(7NRmA?(eAF}W#zjuvP~uOL z>@tUcR&wVkK89!P!2rlaFtAcAM!*>?#1hHiV18E1JYzf&@6@fBX@5il@f$wGH07Dw z^7OmcCJ)b){+;lWLbboIeywL~_S`?ES<>C}HPzfv4+{IhXB`WLsp>%#5>jB6pb#IK!u#rz`)rW3Q?pcHR z4b2>Dc?AGDB7tzr4ayDpshO7e^|zO;f4mBq({Fj~Ob)$K#qX$OmXe;SjATb3&`#$l zsH{zSq7<^mM5zkge^_a3lysSg3C?oWyvDGREg7E5mE&K_O6bWI#Zt&APFNCD!BNvs zC(2VHbK2R1k$Amu4|_Xl71Vx4 zQkDb~NH;1>10pTGbDwn^lpzHkouh{_n4|GHr(mBUCX#yssty@D5ILxpg|bQNSm!>I z-{3a*q)C0$8e}Jzo-33<#!z{OfpdBG+yHX*&;zC}Ntg1W_1`JbKSU{scZ7Ivz-y*B z(*Lf_QQrnF`>{*sQj}zetB|u(Dz^5ySQ0BhlDFj}3zCjJ%gFELji<7s%)l&u3_(Y_ zBBsa%dD}`o!c003nHAy52{ebJV{EFsYQ7$O)X8|@)s-9!%zd%zyM&0|YL zRT|0x00wr!S>CUcw`93^S<)*D?z|_?WjrHzNwr_|J~r#agligU z%^CSWSn4t6drHc|k%=Y8k(yq6T31Tkw(4)61cIC+dh`M*Kh?V=y(mCu4IBg z3LtA4S1uH0?)G3Mrw5I<#ym9Y0y!r+5AxvQ1Rb+z&B>q8$^uCltU+KW%IigrTgR^p z@h=rLT2?yJW9_%?Y_EuF!wIPsE*Ke9wA`tPWRDZn-bX=GY*`}-oqB#zCaUwo;Hzi{ zs$|8?*lRAL)?2KM=YZ4FY8B(32$`AFzkJV{vxqdGZ{<0U5H@`?@D8>kitjL@1*PT@#O|MBa*6hC` zWbl89UuLCfX>vbei(Kx0w!JqP=Rx|l<(3Mkmc$@b!JJpKvYy_xm$ZPFVvbfezDSEx4|8fFwm?0A49DV)|&_EJ)-4n^eY!nmyRH=t2$Oj-~VCg1pEg^WSYe&`>#)_ioUBPcm@q4tV!Od=3mZJTNwvt z8zULEiEICJUxIoOi@6?;kV@}2s(#hQa)s@hL843=gLFx9O z3vqo0KTN8kh}|))98o^%;oTA>z=x>l(m`jYrw~7h=Jrp0xW3izL2`WL z5Wlyd^~`>6`M?f$&_hOIX(D*@;q$ENpW~7uwu4Q-wT+VOjamC~3yZc=5vf8R@kyso z&u>KGRb1ISX}e{ zT9o0o%{pQTw|*Wny0Xj_eKiF|dDb2F@Nf?YrXxKQ!b&3>Fzq>6{6FafMw8XAPFWNs zW~vq`H_7(5c_qzBI~M^@?0vO^@uXSzKpccoBR||<3_4VG5nkTFC68Fl$zMWHtcxT; zQ*sBJ4jr7xYElYmdo<9uf7?|O%*bAVYg+_UJB=9xP%h6nbGfm2|0NVykMrlPj4yGIs_;sJDGJ&L{hLzvgEyjqIdEBS%&X61?DU5wtbK+Jy1N zZrg6MKb>}iqgloLX8t_4eEBEqy>QEk!;cQ332L!p+liDy;S;4rTUcN9jS&CFA{Wxg z()C6=KbJY6qgPp0HLPRwW$ZyJ|K2O9zfrVNW26 z)7+P82zX$R~B0a_pVq0;-xVW31r0@5`$T^&#%linz6xWK~|D zWAro^p-JU`lU7-4Z#qSYRxajk|kn6sOtn0m}iPpHL&?`$mT zrwvrphul`6HSHs!x_=W{cl~}S@iO8cG-6+FUN$<{7gzZE?d?SeXL$E{S(f`#j2ybb z7vpm~-URwgV$U2Ld1h`ebnzG?=jSQ`)*G{>*+^Si@xyGna$)*Qv&w>hhL@X`4j*51 z87Zd6?(Rlt^@a37Ct*ErfLUk6{noFz{?PpEN(hd{n~4DN+Uqfv&iEIh^F89h{Dx|+ zv`;DP<)w;}=!gz0OGKD&?t4gA^rvk`jk}U{#Cd{{;bGy{EisVlb6<* zF##7NZoh5YTmQ2ro(VVsmriwacc*3w!or1nP4v3Gb0%@1rN@z>9X`PQOQ)0o7N zhaS1S!nllm-6(JU#Ry*K_Tr08(=9L7j{6U%V#Rn90=6W7p;5gU7Gi4=$g)!4#-yq}*12-B3UuJ6ZWXUg`Xa(aEL3{@J4@w0J?oO`u ztWijk>=AkSU_aAs%Wa5vt(W$;ENjIokDg7gvzA>KJys=jLa8V9W4Gu6AAxHve{D4-%{%3&ALMp1E}uBZHY=WL=$+W656^f}^r_;=2Um8YBnfdeSg z=c|EN-{??KT41PH4~_NN(~(KJ5dk&bo;vi4r-!5D zU&EOin5ER2+O;V7>nfSH#p3NryB`y8!5QUraG)~h1aP@&*u;~l-+X>Atn z8K$RGRq}`M>&L!i|LpGHNCq6-p_xkDxVZjKl}>E8)!m&k@lAeEnCY<{->q~|v-c26 zuu<5YnY4QTE0#3r$)wONWy-9?TgnCqX6c4jtKGMVR?pWVdh`~e@AKrFtshLT*x&n# z-<10Jh}*!*wDz8X)nw~!3g)ckD>a>k0QAAU$ALt{cGC4l>7%JDpX;Du>)!kYyX$tU%e; zEc-Fzy~lB~j}NpZ7Sk5m@`V^kjTo?~&%Nz3nwJ_!w7zZ+e%xc^Yq45xG*EV)TvwXB z9N{*nGP`Z6uo(CP#4W+2Is9j-H4!Ba`WX!K`GB)$BI|i=o={`_oAuxgnsv#SHFY{6 zdAuR#tNfbV??Nc^*k;a~gudf@$xXnfLBF3><|uwn*@mR3EL%RL)45PbnA!DWpc||A z3vn)%4QZ7|cGCzthL)pwn(|*L|Aml`vB`G3zA9T!SlPU^me+0>HXrP5Gw3qHMwH-a z9;M=rMwphsKnr7ozvf#3>n3VoJS4Iu&poMTs5CU=caQNKfoHn!s+U&i3WA^SzHzRc z8cHB0w=diV5^*1;8d;~q7k7i)FXAqoTnp=d%`qC;YRT(x$%-F^X>%D}Iem#_S6d#@r1P7( z=6?Z~Y$-9*O!$gp6#n)b8t`RZ8e*Xdk2>4_c%}z^8`{pd^nJm(Y+W=47i~+u&avg^ zPN#e%)dpbzYE07M2D@rzlbXv57pC#qZliJE`g6SrXfp2-3bnNY)@nBsn|s4xvyTGr z`Um@tReBnd8mH+~@26VEJba%sSd9m^HOjD_us>l^jkt>7cIQKP*>~YH)1?q@9ZC5W zb+JV9H8pb5ZPL&dpub~W%se0`uBQN`+bO^|sl8>Kx6ONLSK}7%D>!wsry$@AQF+eX zu4D$Eg%xDR&spINYShwviXICMRB7)EBu2x$6M)fD<*Np{ybVxG3?d4!3V3xVFYfVR z71RD)96E=tu8x(1gCA~!@jO!#^-&C#@fML#+X;4jS3+NUE;#;TyjDm{@qQ~g*w$WTO`5KT|i!?ppHLzA$ zNB(O)?@k1g_)5#eO}8R`PHya=OYFVa?@Jk=bLvvHOqXw+R?YXv(K6sYLh*4}wWO!u z^n9(kI$OiWqwsufabQ{X(UXBN3C(JV)}?*dM8ZwkN9HDbM@i@C$MpY?4*R4172)q4 z1WQzS@ZW?8>BplhMX;#VPYRH({aW|^Xry0SYn#~wJv5$ecUZo(jsIbfM))KFH=;zU*s!bvBF3ibjFIk7cc9; z1Z%Z*AtpR~t-zbzI4hqXjqCY6;=yk2!>aq`7ep%(g&e2+4)wRDpO~$O>OSPIHPguRH3{79_#D&qPdf8GYQDr_ORh zKFxBXXn%7wv(;5-1zfjwF}ljEi>Of_cP`)puCly`U2`BZsJ%c@-S=u+S?Y6bL~A)o zSvFjsrqY!z`N(aaOuYX&*ZoqC;|1V;cJy6gGiW?@nz-7jnR(|5jD0Rm_#{fhj}ty{ zB1Gw<_3B4Ig+LpbwcFzc;ckul{w*eqBo;T@zObxl%##!1p-Gu=$srv3#5MGRTFic>BMtPTOqGGwyh% zM;EjV!is$Pg5#q%Lo_VAd`_Jrd>yJ(;;)zji6xV+#p)|xLGl>9{iMVqd3TPCyjTY! z%gfSi+Ee%b%L2f5I`48@Kb~t8{Sq}Vly(&?q9V{65*R}WdxP0Ss_h@uZkBF0cqB+t zy|~5N4|Q^5eC9jtSW7=H4fCtWst2`~2V~$Yi|;0Xz&S2RSHR>$T1%*zC2ex89;d0m zltrHp!(3D>Pf2P8&s-p7r5pga4Iw6%ofqxSAnozI74K_2+d4EL`Z-Pd=yWf}z05$w zEDtplk8G0nq;?7m7I3kM>upci`xY`k`FS)iDra4--d!mpf!|pi^>`+cR1S2P1Z2e3vDk}Z6YlijbY0c-YPV!)qMYa11{eCN& zHX+)6di`ELg`)@ERjFoF?ajyfGxddX5YD;KBxI*=4c#aB`F$J&h z+srvn`aL6OYEs*+7mf4AG=TQLM5CTx+Sl;DNumF(71wjFork$m@e^rr3oR;qy!YYw zP%RCj4sPIeRy)LCM<1K=mZSSHzWA!Qp?ZM$F7oK)L1F{+%|(#rP%i7i;PI2F*y5K&xRn4e%NjD*--bT%DW~7i{S=?!x$$X{jPJY_I4-U?>Sw#2p z{7Nt8_qo|_SGSN&w8Yq6Utw~oIa{`cOj0 z2h|!zmbR8U`{OGzt^6p^dNAL@AD*1DlqiVy_fb79&)wcr{ny~2pJtndW-=nbSl_Qs zE6K_zOZaPKC(bdaT?AA((#hNXsg&F5f}A`{`|{wm#HkOLHc3f(eWjzGiM#wVn`>)? z-xAjRI)2CJh^ME9tA!mS+DOd3R>j{j#7uK;e@xOdl(23yuTO2OF--gX;X8BIMm|W| zi{E31JzZPAX|d>;FQ<(zG6TwA!&ZE+9K-Pwt(H_NW5Q;&~x=;_SmXkmC!dIj&Wdb>bH;zZ7T<6yHHY54m9#zd&)K<1F& zUTSqfv*Dk1G|6H(BkhNDevgV+rNxRye5wwgSoW}fkZVu$1e4Zv#FyNCL~51Ed|fu# zWbVQ&L7o(OgX?4w!$mPH2&Mul(N5>}1oU`Kxw79AYWgMl-DYV2tDtOHk-qu+3*c2Y zrPxq3=jQnf?jX=N-`gcWSY1Ywy_P{oEX6UhV&kS=8?~p|!KwO_SG+q^Jl6HxUPiIu zyuqd6qBM;kKGDyooG~A6T?Xc;X(PLMyz?lasFp}L?IC0}5-?Syoyd@Mev(V=wRJO- zpraz&Hrrbs<9hW7#(PqohHF0GVkARSms?v+&fNyMLqbWL#*$apQnt+$I%##nK9A;K z+wY}Wr`BpFMbLCMgv(33%c+PIsp&Xi0?T&rBa#X~Y{nb~9(7^Az*y6k4}RN3iC%}B zjrDMfik-JAO)%Yru`H?rSvT_EBBaUPjnYZKjpH)}-JOGWck}u7r&N$Q-HcH>_rc>Y z#$mRBei<9eU%fSx68{7&D37HSKfmVTf2(=OH%H^u@wcknud!!-3t@rJv7#2O4wzam zQf9_Ib&Bq@?Vg`QQ%T6^Jqf*U-!B#s&Y^tnA~}t zG-{U?UA%F{Up=rPQ#0%;xe#ccH3@AEpQQKSadoQ++gHSUwe&o*pNlxx4_j(JUrMSI zos?g3*Z56#r!A56SZ0)tn%8Y%6v$!cGCZ1Xwz?a?*B-)^I?^03_>!Xv1b+4^Hhdt# z-=asnj%VOXYu3vsKmjv$;_;Hl>j!Cfx9aJ!xD6NAS&PhbCCpg6TSchS`D3`cR|4ea z*-dj~ov&kx829S66%nr|67M#za(hT)&CIxcWG})H@)#G=qe1A0o;je0&w?&eB2Y1f<4k=JQebn zmHa|x*c+t2Ftop^-5@$WXC zy`l@yp^?|lD*IKrL_MPB?(o~ku*_Tkt@Xj$Ywynh%Tbj*gx8g6#L*&;eY9N!{MT!m zT5;37on)fooo{OpBb`|;$?RSVs?Ax}V{Lq3C@z>0=@wb|J*s#mRr<3ew1;a}Bb zzmROKmh*8ypJWVYw^MB7Hd)dgn0jM*o(067FP26*_(j{?Ts<1{4U<}&`&YnxP5YKU z(1c$IezzF3e_H`!FgBO23GlZQ0@=iAYER3y`Ou%i^%`Uv+WvEr-m zPO(SJJ{++bYW%f+@;Vjm9r9G*o{LMK=G%o>e5ife`2b}Cr86BY z*fjeHc3`(YAlmF**xCGHQpLcfN0qI9iLU(i`mO24oWXaZsWc(VGWA+%=h1yOmXQ6mmpXRJuYVTbI+?@On!qmACGd*({R$vEEW~Mw(p5v4)x)@l!BH#S9CpMEY7lB?A>l4n5+e> zh5I`r2oqa9s1+cuLn&TzyaFlUdZwu;P=FK4POd zbg~k+T6#GJ&xp@N503hE3%?>3)F*YMx)QOw+bIv(w|HN{xra?q&HIDpWr#u-qQf`V zH@~Ab`OVCw1x5YG7fT07FW+}=q!?)#Bq}d=KMC^PPZ3NjWpB7jlLHg?yv-`~rq;$Z zojE(M8Z9NSMXAsI+)y>+8JRA&bdvn)d1U4?%Dw2PZ6s;18|%8YdT5k5zq967Gac-)j3V+k+uxqWqBHG(-;`= zHYu1FN{pH$!Y2IZkk$L};CAZkH=Oh=2cQGjIEgs)qS zOuox3c$-W#Pmn7c{7mB?Kld)Ft6gmeX|>Qy+wOtwZ=1IjzUjnju($YRz`rjy5U#>< z08+y+2Bq52*0<90!Z^*p$BHACorc9Kxn8NS6bSCJfynCy9=BMly{^TR-(fnI0Uf|Z zF1jvz_8KHsBkT4)8ySaG&g0Qy&SW$BmMH~js!e@DlD=xtp8eu08UQ1d0o)#U;@=85ECo?wSYn>X6!PQQ+Z?mY}F z4%TQ4OfGnbawMCYRMY}%E&EDhue%?MHR@y^&~yJ9Wx!jD@lNAhKOaS9DEJUWddYa9 z3~Km&MU^nzE{5vDSQ_b&^c-6Vu(TkmjJ_Q6#JN{Veoy{lte16EhSv*1-RG1n z8}r#2HJg*kas;)#`Ly)M%J0vx<#3On5yYXzE+F94Z%HQ8|JkgcvgmJ5GJH2&qgPT~ zCOvk4^d0Iy#iieF_W+)ZH?pX%*d6-KqezKjUm|-XAn=`Dr`=#G>w%L<(6->vC5g6a zpXjz|s_ptyVft17x?!xsT28j{>rO4Gr`ELzU6i7X3r3S{kfduMZK!{&}W&>VGfV!=eChezE)lw1MW#m+Cm`}oL7Q8Pw(!; z{Xvaj#>o7e+vS!bYTnz<{-edw5$v_@m`3%N9S~1Ty$qc_C|XidA^e(p1l8teNEsON zX!l}4CG`PbTRhoD11ZEn$tw#^H}e39%?}XTr!0YIXHHhM{EJ_~)+*=eC0CwSeOO^n z8guUJ1#OXT_mFLLBOS@Myv#yhk=fHNox{!T?2erU&$Wr+Fw*J{JZXPdj<21K)O?#x zzsx{wG=X9n8ZMVoW*2z5{M2h^cA-nmGD20rg==w~fja8EK73)k^62=lvy7X)TM~ZG zUu}5cMVwy3l!#h@3j@DX2or4HH_rM(ku!Ka=NHRN^Mn{jV^cFKxLlC71G|St0h$CJ3X`>!d_- z>9D<#8U&j%$0h14CR<5;G zxr6q{$KR;3H`KGMVtrjI=LU$dTvR%JR`C8cm3)y_|6g2lWmQg0%uY$S+J6uA zn63(aP6$rj&|$-`T@QNH6k=p*P_Dd(xXNyI*)|v9Sg*>f9pSH>qXWY@DC11E z{P~9^oC6qtBfKB|Ym4*eG^)&TsBDfUJk5n%=3>K`sP2c!f|Ud^SN0Xgoz+T;nhprw z18ktCWfl(8`sV_VN~#O;cLf0nP=N1|nq&gUvM}YyJG)Ke@n5e$9|X!(NiElA|M<*fuPjmUj#O(>Kl5 z5^b%@{VFb;dc$_?V_k^nTr@6NhAVZy-tfKu*ul78_RF<0rA!0M`WERcAeP8)F&ig# zpzeHA`m2MeP4ME^wJt~pRvbL0$V89)f)`-b8QUXwg4<25W=|z+gUpj&|Yp4+XdeMeFLf9$<=P@GNkHkuG2A!rD0Atb>e zxG$FA8r(y04-lMX13ZBcJh(0r+}#!kw#eePxG%QY;84}s$Ej-{uf<0^-jd25af1Uh&GMAoDlTKdCng?M>TtuJ{Gy~v z!$q13(?8ZTaH%4m5cd_PqNMz1MkaVd#*h*0b2B`vv4A2fM_DZqLN*GSOPRiJCmp3) z+YYfjK7M&H6FX~)ai^kIB7&335uk1CfX4Yt_RwX6o(mM0extBN!|-6^38x!q0{>)h z*9s@7KzAA$^xc6Jy>eM1DSRcL%-;aE6bEO}`ovJSJAi&ci*Nmic zs_%Yzn!44hcih$p>V=c=zY%lbJFII+tM^JN1gD-LfMrU(PP2S1ahWgY9p*SumJG`p z>N|Ufz5$1y`ycmh!79X3f98n>wfZK%t{J0eFgl>_pEuF;;wa!+AG5#;&9?XQygP;a z&WwM{TJQ-)-(fA+Sg7LX7cOV%mgkI3dRO+}f*d^=3ZGHAd-e+WY4keySnt@s9P=)! z>$reb9qV9|ZSKhCZbc0+!&uMqHp!iSnO_whK!OAos+-|p=6mgXw?2Oi8kcMo)BqJm z60ZE3!m>5ak!%d-MBiF_6aXH(K4*499?BqJ$G5G@$X4ULto5pQ*7z2m7?GIUwJM*n zE#aGWj}X;ka#296ZF=jc3x20jC>4+w1&@IIz(h9;1?-m%e#I)0>?6lb(8qnyvw>K~ zjwi_78Ft!izo%j91CgDtYw!=jTj_>S{`BnHlQH8v=MVlVf`NW_=b_vct9Uf!Kdi_& zEy^AChoOdyd0VJB;$CMjqDJ%LM`%?@>8)A{IxwC4QbZL}8)Q2)DV z<_nNzfDGsHQE6**TMrqFu>eSURe)i9gkZzwc;!tRU|Jgcw*IZGYB+peHua+#+Ry*6 zHPQZ$=;FjIKyhWO*N{oyk^BSrE+a{P#7l&ZKWGy= z`%3J1+fyAt#NZ>x81aIA{%PEy0Vg`OEvLL}kFVPPhxq#)z%H&$^;t2T;(y(VMDekI>JTyJoSWnlEF$`ZOj#r;rddh3#8V3f;-m2!ecjiHYw% zdPJ4)$yayf8?wF-$*uk5%7VkQYyabJ!^US&)LHmKmOJZlOX)334j*7TKhqrUy2Z~b z*UfpU11=q#r)ss#j@k_Itm*Jt_N6;%1qMVklNuX4=CXwv-CbM~K1}&U6Kzv9XQ4Id ziCq~DPCGq<&*=?6`JV&U77E^}nzH*V3YRi>pgSZ3)S zxUIA^T#zL0<#%syYBgJPbSgYtP~VO&+{NP(HnwI!sktP`051WOGL?f~ITWHciCZVq z+B*p06u&8swoQ%aUA>oNz3}yVg)lr_gs34ywd6E(j*fG_Nzv6~y+$L!oNYX9W}@|c z?+wYqnjhbFV*z6vV-fFd%2QSznQ9`oCM9291vItgj)@3T&;Hk;1)>+ns7X}nN$vIc z)r!M+CsK!UrA!3J(cvlw=-X7abnW_`uC!PvQ1wD**>2Tai2s>6racq5szi|Dctyj^U z&s@8qOKMQr_mQoPb1}!{w%ab+Ot7mYWfs=rakfo%Q{2d}DisWuKvxt^@au{bci8F*h@(z>ri6MLHd&1<$yaYcUpCyv&mB&y z^pXkEB=pbnKA9#1M2A+Rry9Fm14M7YSDrsCd}c-IQY=%ca|px*jYNzL1Y8bQ=s!9f zBTo61>OWD*Qr%!mc#%7Y)DK7jRL9Qtx?R#=qlu;ouLMhOz0+PSkiI%RcH`9K2wWAF zT`OVPJStjX+^$PIVTQRZZeK_Kuv@=z{~Y$7j+EdXVC(zw7{&0{fy~Q-0t6KXtlb^E=a~kgE5lB7tnna z$VznRDfUW?F;r7@QMKFeX?iz7PR#^rl!KO_>9nG9*Hp>|9#<^{?1I;N8GrLxyBFVd zqA^BrB;#p9Nwv&%lsoCb&~a=mIOu<{QGmSDB;QIKi@1(RfUYg>#y@B#&q0jUI+v1V z{;?P?tH^sa&8L4U-MRA%Wtt2T>_LX~;yhbxVOXK+mhVraG>hto^27FI~jA! z)NbU^C(g_zqGsa!)9kkn4w$O%I#ZE@gPMMiZtGa6%ajXmK;e0}f8?)rr^VWt`m3CS zQOY&%a}ViQtKF9F&@9!{UOPt!gHlCp=Eb?fVV28@*H;$9hqd*h znp%7-!aw`o%MMmh$4!%$m6?Wh53W8VU6fy#qcLn&#O4h^X_A}e?;B#vix^8|HY8^kSY@!j&%)a`2g7`v1ypZCUm-!|2$ z7Zd9FE&@^OLVwsOn$vmd)hNAR#P%e=#Rqu@Mu?5vO|tEF;Xr4SZQx#Z+2*vP2|Og& zR+)s_6BO}l8cGYfukXP)j70`tH3T9Gj)D2uW>z3pHgIg5Z_(Lmw zKh4yXSaI%zk|d|sZoJIk)3Ga~8BU9k`qyW!O*LJW&fSsq1HPF-0c?y#&T=I@?zrsl z^oFV3CW0K->j1~u>Uh;ogWrplSS$~3w3rRl-toNSvZ=Bs8h)# z9UyQPiJjH5`(Di2Zg*)bpHrJVgoD)}J*O?>DEBO7P084DTGi?)y(iW`NLN!B2I+Fw z3wkd67t*EkxQEzh>U?R1Z6FOfUDPWHnN@jiELpVM#_*;8aj`LiSZvR;$r`bF2?TT@ z)z;g7Nn=7z_zB776+o|sZRI&w~1-9TUQ+@E5*L| z&PK7_zx!85BrK8vGqN`qVfc)NSd@SIWBhTBPSwHBbOf4+LVuZ$`tfHHfipmHM*u2F+mCu2%N?Pg=1!6PH>s^@1YTv6aPA&9DJbaL2#G04)iPKp;&RZ_3vk6$qC)3*K%W&U3tqQo#N)?(-x;Lqv#rKtQN z&_h#fcQ?Q9rHm8X*pm8A?df|m9J`gw42!NxY`So@ z|5nvEqEfhjm%nu9xf|x>;kx(S(G3fEwy|{vDkgNJ&h+PDe8l6%MDf?^fc79Ou`$im1hQQJmEkTS`3|S|Tv9e-fL+@%f7qV^ z&3mm4S5&?qQb+V9AU?d$*tgcZ>@Cb<(zI~m3=4ji-kgZrNpauU7VC|KcR>|h2=-t1 z0qdkY&^mU8Kd}ETpo;r_i^anpx5ztay$$i4+d7D(;;mOI>2*wG?AToZ+8Q^HuIqiL z;y=^$Pj~&<9{sw*{N($;YavQJe|4A1I_G}S&~G_qINbm6Wt_kIfc&p@vXt}u3u;#} zdO+^wUyJe1qaAX8mVxH9JUj8XvLCiXNmwB6Z!Yga?*7&Df11R+@eljmDnr!4zYEPj z$HV*w@W9w-v*4D!-?S7Cl|gxF!o}aSE}?yY(g|;5T6J=Jfrd5*!HIpL$#ebhg5>o? zn3We!+=3|}`FB&+5fWi{N0+hU9F>MEbWDB6sSWD?{rA5=D5r?oq&6YTKL338jz8`r zO`F|151X1|NZ}|LZT|(QOnP4IUira)O1-@&p1QUSA*DqV6c#W;E;(%u%yn$Vm;SYr zjE%9#7h52hy19Y>(FDI0!>?09CQy@tooZiogI?ygb{xL@HH90xIyjy={{|CbQf0O(F5#s+-h?p~16Rcw$ihlsYl!Byv zc{!qpdr$uFL*Vxp`~|TLvU{MF}sYw!_zXxUK1f$~k?V#X@ z={pWpkA3A~q8u9t<85K}=LRtJ)}QI@Ufz0whs{a}gAezs0U63&m8!Mcw)s$QWiT@P z!m7dh0#YeQ%qUVv|KY!;)7B$<7YTpCjHO}Z*;i844<3*(p;XvW0(Gm zOMEA$bQ+`XB;BlAWeZ2^sEN`@J4skd%pu9X^#oS0f0ZC12HE=`;U^Kt_gi6aVmZ!wqq+u?8aijcFn0X(7RCs$s5 zw;vmqO$Y`6sX6I7+(&*XVm}ZW8!HZ&lx{Tx&QsG%m3AKP{4`hG#~VoA&(~}vd{57~ z&}Y?fG1`aa%( z77q0tSC970MM#fdn30?AnyZ_%3dmcPvAvaD{uCYGO6{IZuz*A_yZ7>sl%pQrA0^J$ z=HJ??DrxMXb5paj;$HVxY2{(;bE>-g9!|?IF$!ygSuaKFHC62^-r&FdWS1u6 z=N?aKS#XRICQU|*vScOJwH|-KiE1eG@Vu-*qYGo<)+g)jm~(_XS8mN8YPRZ zx^tf`>=D$d)`kp`KpyyizTbD&U9f-nW8eDtHC5eBwcC^EW+O0=y52fk;$&8cKfPuB z>c=g@sPQJ=0K6p9{@~G$#&5aCn#Iu0q#+BVhE8oxBrE?h??31ef4MtaI!OB#|1V?i zLm6R@J(inYAQa@De4SHXst!=L8MZm=Z*DI<@l-%auMkHWTi*MLp>Xkg9YG_+#iSbU zn`Jd~sqg7anG1PR#HI1rc^F2oig;i(jovB1B>A!^tn{+qC`$WG_>gDsg1M!5bQIf* z60|8chs^8s+L~f-U20+%YCBwi1WQSeeyL8N4029WfoSWrU=R|ui&l&>I$;=J9@oxL zU5NMf*}6Afgs{JwsY&~BG8QKLytU<+gyN3GuaGbHL;3;|?-?J*fl+e#*)hTd4dgl^ z@2bM-97g+RAF%L+wTC>kMxn^vYPsHe(#U?XAf&yga%m=!wOr&;Fg2^j25K|g4QG5C z+|+X-5!XCV=LohZPGI%K#@c$EXdy79um(3+NVRis+;((A>Y?5__?_Co8tGPCSVsuaP31-|DlRL`Bgg!DN%g*aFhA6r;#yzU#@ zaH)KvhyUTru?yA8tAqrHn~wsN&&7ZevZ@gi63?92#}1Gs`-hDKnjZ3>w7fqFshXWp zjhHe_SpZH5d8_(p4+g%Qr;5L)ea^D5#J_CKnnMnBp>~46zW*|2x0;2Q_BJ*fDR6B; z8Ir#aq|*9x;%hen||XE zc6o#Qz57hsx4pu{-TLh7(}CvM85>QXCh8GviW6{+ygdc4xM}mk+R?eBp z`Xwa0p`sOwlXGOG#aUcATl7r$TZ0Z5o5R=<%g11k{)bu2ZnE7ncZ{tV_Te~6xp=N-5tcwH8XkG`~)uwv*q+cO8i=AN!twA+&EdPZ#={K zi)hAATF=z>Btmc68eZOM`5{ZS$4?iRe{iGJvB5?Y+-{u;FSCHvxz?st``!-V z$>tUgUOIK=K4l1HbJtl}o%@f;^nHRl`Pyk6#G{aDzWdtZG(T6A%Vah-FVV01=xhPr zJ}m$N@aKBoN18YFDt1&UpEIgLBhW9cAYJT_Advf~-XhH1ul4$~8{@u?BZZ=d=6%xA=zyJ4Dj3@Tsukzj!OBS!o;f!i$+6(fCj)dtuHmeJd(poCt!vJLWQqz)|oK!D< zAb!0Mk{@Dnjdjog5qLiD5BiJnA_u=DYWE`|L^VZI?OC4y|DGvpl-p z7GX8Cd&J>JDc_4p=_**&_*ma^<__^?$9%%jw-Lp+&sQ~~)W9ZLm4QDoH$~oeAIj=t zT?54K=4bb8bx7}-VOlWezsv(7wvCL~_(Yd_|eqLH&4Y7U$Y*QlXzK5N^ z8uuElKzwk7<8Z^U@p2>cQsntzh1Ajh$>=d1C2b{EZ=b^%jeU`!N^PNm8%WEK*8XS7 z^8CU@!SLL)abvA%O9)q=8Ujs@w(N2AfW)xDK2nZHv4<3k)ng zJt3=AOSS+TGtZ{}BQ{NI8aP<6^rHGC61rdviNoBG_N~a3Z(2|UI48(k{A-`p+a07^ zP|B-`g~MBHe3!J|mCtEbMZCvHS#;ovAsy=_#(^4yWb=A@Z9L~(x49Z)tURmR{dws{p6Cf^s8LI2C-6?+q}X8+m21}n+~=<9gS$4UK4R$VEJ({jy%Eoi&9 z*xlIYYn<7--}maU_mN8)j6|U1++Sw<+9nidtt-c}m20t<^&VPIx_s939@y}qzqNI1 z9hoe=oGIK{t|*-VzOtjUHU$Lx^N+>jOKgxg7JY?IEY9?0vn=+V@L`O+a$1!@C(2L?Fk{yINk31>j|TYdWt=1^|h z1mTHOl&%W8!o06i&*jpVP8PjdlcN9~$N-1pyxzJ9gvr|Mw47<#^{rU~@q6nG0h-y_ z>v;_sb-Z<3U7oX`3F_S^>7lFY&H8k9S~5c~ZuDyDUjXzHyq3M%mE%qA+>{6Iq#%*N8_%f^|85;w|B3nyqDOXkZNR)s=dR zPU(rq9BsM4C1pdjlCxcH%s`)5Qfb0Ba_C|;H+=Ni`*RRd2vtzjI^a!h8?@C6;Iqkk zkoJ|WyUvoj)L6}@O#@6aX=pa_l-YVIYWUJ9HG8Nr|bN# zfVhQUE(i7?Js{)~L##m%HC~B(Ri=nfXNf8=lIN<_2s)r+Zal8L2$fqw?b{$kFU!Df z?%SNJ^uoG5(+C_gnXR(HXZad%`%!TlLmiA8y{k2;Xr4w$M4s4bB3y_c!;!2pQ5pT7 zcSr3izyspjBVx=SyF8|pQM#Ct+soOqDi1o{^hy$5WF8r39{TiCfuNMs=CqZI(KuH4 zbdQ(BM}&$C*RStPk=NZWZj)kUDen~v1X@^&;M1=cP^}V3u;w|hcAik~=m(YWle@j; zCZN{B_@CQ%Kv`QNCu42_0={nbDc-{Dth>~Ud#*9O?xTvKpAet34=ay*-I77)Mw?gZ zF&(^~r@U#NVzNa#Pc$K8auz@Cj%t6Mye*d^Xt|_0(yJ(^-xkvPxzcb~__UDi<-~r2 z(zOoN`dg|JtaTxPHDM1#kv$uA_Mng-SuNl_< zSo*5hM^@4Oc;qx!@^!VGEafywG83p%fp~&j?7WrKdT$c9B!s&Z(yJ~G2QRm-U5+s! zTQ;J}wDe6{6kBg1;cPcVh@r5NO`m(8*Se!=1zX<4s!=`ErHYK-+)ER(qbLlXA2OA# z)AU}Nu(Hs>C|4#IinelkCLb z3Y5rcP0Rf%uB%_UdQbDZ=mi4w0$I2f><6v^odP7k_MitY=r6_nMpTW0S$GG&n;nG< zlURNEIWwCxbo(T(*g?R@r#|>{kwq=@%;deQjulIG%61i={>{e-s1w_!+<9ZlVK;&G zz=N|@5a4Z&;;CP}@uwzN@GQ-qQ~U1yJV=p5HVCbwDM-0j7WQRq-qHD@Q9!mzXM=f; zbuE7b&%l#~oh`-?2li4AzICt6UcKAonI51wcrC_#;_CCABF!83aKBc_Tg6tR@{M^a zH1FkpP)fguQLQ4Av}Gr_bhxMMsa8Gc6$T+UgvbOst=pP-?W~;R?EiZ=6VXIBG1-( zbph@99K9D##tv&LW>PaQO9<{td6K2#)*8K`>wOrgU{F2h@V3a=Za)S#m(Dqto=p)N zmTs53Q!_;xM>jY;1u`yxSq?SX9IhvFGE9%w3|7wlWQ2+BXJi(Qea_<14`Sl1^1Y$h z5QHi5O-EY1N$IhFq9}IM*TS)=(_)g@kiw%KbfJ;W>se~jQ&yPDv<}x@=evD&RME;Y zX>N0r=COZvqHxN$Cbyq5yg&J{E|AzTHx|;L>erQO9KQZ@uE=u`bv~fYKYo?Iiq<@@ zLys@cX4jO>Y7T1NX9EGK{POC^+#4h!NSYza_Hf-&{f1Tv9`N*<3rPVrLiJ><_2T03 z?|259LZQ(csY*bkPSXqX^S-(#ye?l6fiphZFR#ZWlN+AyiSX?Q!bqVE{1(3{ne&- zvVH&=Vk_@5{gDyqEb;bh&0bYlb0_yHUwlOe11U$94&Y~7 zjFJ8o!iUT&6I#IoHq6dBDUY-jjVh$Ao{I00;|J4?((RWT<1^M%2fgpxq6P@a<*BA+ z%pH-iT>Ez3zHYwzK8~N3b8K!gVob_xjFj`?`@LmA%2lc4UK0)xPzT1HNLJoaX5Srx z(`tyRh+}U?J62VE)O#%M+p2LCw4C@>wBVB%!mHGdXmuw&MpUMrFdcgwSaj{2rtPV+ z=@I%$f22>nv?y<~_2DSb&_zz{l|9=4^V6n8r38ww1; zwmSbJK4+F^vLV$my@~P~CMh(iZSmu3S%|hSXkDNlO@$U0|2fvod>Mh;=gA)4!Zze_ zbE)!mG9Dy*1QJkGtZfiV&!+Qoii&8Cml9rWC~Fe%8(4h?ySCLyhu+LH;ZX~~pJAY} z9649jaqp)?Lg%P4SH=~6x>f_|YHp_8ZQQ$L!L8ASpbp}b3%qo*^MN}Kd{7J5_?mBX zRQGsMnLtO^)w(fW=cX3NYQ!5$ypJ7JwWyh)mD}Q~@5x0tEdr&*hzu^r)tY1qvl5y8 zgh1!=D@c?{9_6aaY-7kRp6mo2$YLZ&cPv>l=?ooCL3XwHDeL+$RdPz-o#g_Ep^6}9 zzHPVK+lku1o2!@f_f$W(R;GaPCv|(vWZ9?y=3ZCg32cY_Lo~+JKif21@Vr)0PY2NR z)MZb;>N^;#VrV3ssq1`SrWOno7fxZeHNP6l^uKKujbBj@VV~ZnZW!O}KDL|Ahe;7^ zdYG^ef6o#BM3eW;o!drC=bcioSg5Ewq9J-;$}avo{YtsUcy#E9L2O3UXm%!h-^=w6 z6Q!a=lu5vd?Gxlk-+?apRxMt0Pb3FQq_e-Xsv%ITyno%h+PPz_MpW&+5IcQorkn9h%auq_1%tw68UxZ~*FiaKSnH;7>jr z#(5vR_jL>Mh`JLWBM&>Co&etvlbCOMEg!N`1=^sj9&EMuN^HII_zqjPsG<&nEI)S9 z^DD56jdvo*RZFfq*YoK_tg1#oQJglXXw*S_UDw!%Dw&>; zXyeUz>cPqxFDIm5q?5A%pr7?Y^oK(J#S@L#+hxiGCj(%dN=%3L!>D(Z&h`ee+?$;P zP2;y3SM5Gc+jj1|5o^S5c?onAuZPk~hnGZFTi)x&ik$eKZ)Z&;zHeOSPs4lDz3++s z?pw5YVpy+|8^@x5|@byciP3c<6smic<+uM zb00(JCx!8Dc8WF7r48S#^H_>|gbqVdQEl0L{j2X9Mfi$OY$nT6X&nLSd6N96a-}1r z`}nRYS2+voxfZ*|d2yqq4NWEA)tk1PU)4;?P8{NpT*=HGDqnnZ35YK{&fCfJl)S}Z z#hE|K8dlCh2nky{;WS6uwj$ZOQyqG^w%a<%SjTVmfg{FNWL@whrMRIN2}5hz465+y zJEEM=6a{R8@E#~GNV!X^Gby-bKt$z%37;tP-g6<%~o9~*45S3ks;@UL6|Lejs< z%G)2ZqTNXy)Dh|--iril?G*;Tlyk^vA(a@}E4d-m`Pgecp$Lmk)D>-xslIRjL%D^L zjxys@Y=C9M$`)?g$=W>mSqIof?8Cj52?GYr>z6_aK7+1wwp^*2lAFM0p@opJ0OxIR zX&9+{Os?ogekYDpceV2mze|f2JOmr{;!?=qYqTyGdDFWZyFQ81{lNExA4W;e+*s$} zTd#B@%~Ibhojk5nW$SnV5=y?j=AbGNh!7v{`(i_n#sY~sv%+E# zetGM7?CKSOnI+{E6%%}O08$oHS0&vb5r2SPZkm4(h)zrOQM@q@CzJIwuA{H(>bGNQ zt&9o`6wKLm+BqW3iqN|vV%D(~3y$6Om6TxIR@i@{sc*TsfJuA)2kTbZe<-NpGAWT8 zo`KbN@?c^&5*yTOrzuh{=&`7EbC?eRRmK6MTj%QE#Tr08{IC!tJd=F3IjnQD%7LEI+FyDhzgMIf!N^YfCc5VqgspPbyPYXYdV3h zWk;swiUEVQI@rtDetUpB+@waq=tX61!+5Jz7M=mnKv z$#FUDcFQ&~xzJFMy1n(WY3|PzM?nS~%brZ?Hq6&|r{G;X&?y6sr9v-vb1_T}CXjW^ zI)G`orgPtGAP{aD%4IiL6Xhg4HVOqb>6Gkzo%9#OGm zI!i>3%#P4@4b)UaXy;eFRSJ)-MDZs&2H*caSsa?nUtg!}l9xLG^`uz-o%pRb6hn5Y z4dreBlhNQ0l*>3Apgu__ARStXJ?Gy;wQrLY5W)wbEUrXQmfZK6sE4Xw=qBR7cw&7H zVw6zKe5?i_S{B@YXynx=!u>Ul7L!C_>yqMXB{cDoa93XSwI?&QzCaEZ8H|3%obY)3 z@Q|0a^b)3T_yWhin$LpVq4%9*(`aLbngg?ett?!chQzxsul5h-c|&|_`gKyuE%f|tUN__j z^wu6+GCDjIN^fztSGwmdhg|$BwbSa7;E}-VQ5SX~eylqgH*Dwjo{ZDe9u>MTXn$08 z0BeH8eA1lizqqIU0-v4RYZumN(OqZ#?(+yBk!bnK5?OR0kVRWxBN8vCNBaQLefWd! zRUDXk;e^Ll$3}~GV`25Hm4u+MI+N0;OhdlsGGfh6ca+d`bbZUkQQ zdEjvzl(uPvG(DvH4k$LS@fAr^%K2!jv(l&Tp)=pZICokVfNp*;f?qOEnU$$3Dt#(w z=Iuj)!#-^1`4e{~U!t&~%*gW9e&yZKmsHOi>agcp{B}H_DzIlg239WCYSrH!d)kGF z@hL}gf`@5)VEr>M79RNId-Nn|d2`I?PV*RXmqU$CdaN<})kXM|zaxwzeoY3br`c?7~M;wcHDfJ)DDdR|{${$XU){JA`!^AQ!qJJW!q zWW0_GhpnMQXy&?24o)@4iN;v5GX{f@wYTVPdl$V(67ZoV4I{#mhK1J8q%AKYsp)3M zTu&@QU4d)6I>9fZRmEpzlKZ5!G`uGJ`FMIAKP?J&VS3unb7!LcXw06byHawPf&bBW z;SqsAlpJPT6d4;InmrTfUIuCE<+LqiiSM~DRJWf=7)@OzN$}hIU8~o3uC|py3(Whb zjn73Lf&G0WZ6+85aLIK?>*0mY5m=y=`Cwf@k|HwwHp=A=c+%&B!{|&kue4ixvgi{| zE9sLLAC(G>EnZ`@4xMurc|A%vXRHA)O!3;{bGzU(A6*0pr__UDMzhwtvL|2Q zY+!Gq2E5ewwrvcEW|AJ;+zLmrQ_iXzyLeb&gB3 zy`cRaD-VZ3>zEDu0m|N#8sI=mjhBqBj9+*7azMS#)VIQf(hJvF_iUF2+L5rkUSe<6 z=>p_Nl3}ADDX=@dAemDhOr7!uz-ZQd1$Q{YNV@ojU>0r@gV&E(=eh3_c zwJrN4dyC7i(!HZ_Ig}yt9j&dq2$KUT`K$+bkzvvVb*CLt&AXu+xA@CB zY+&2?>ghnz<>E_XgvBH+)u5fn$=HD%tFM050{_GYvR9Zc z#w;5%gA9FdT(Hgg;%=C{zIpDw>UQPy-u8Ux5coF!n@HGsj4vl;FpniC3j3 z%YV>LKc$ZxGjAT+8<>#B;6ncKarp24W=`$?3@`uF@%y2b$a7G^x0~-30FY!l>7pMg z{8ftM((`$cO^C_QA^^%W%ESV>IjAReZY|#X zl;CxS33h3yQut=4@_oG9A09*3aIshsy7fh`-vb2s;g3i=;i2DmF4F06w*%AVl9 zAOE?xbLa*u?QwCL%dIZW!ccrWP0+Z89O|fDv+-s2QB>8TfW4fXak{Z7-?zuR5l)L1 z-bX19C`H97l-?f|NJJW`JQ!UmFu{Z&8g&gCjnSzuzYhV3c5EGhwQT}bwFC!-q(P($ zHv)z-*bQNR-7;9&`$qw=l=*Qlrh~Zi&C?v!?u$+mWs1{!%|<@F-Qu0=J4mws<}q53 zhTU&DE6iEM0mn~JrN)z(4R1Uk)`B(-DGk4*0#RQ$m#6A6myOKj&{x2cW*px5K217o zZqJiDewZhB5VNO2%nPiLNTW#yYw2I@x=~)r3Dkf1x>I&u9hQqrVs3;Q&EK9JFCi#6 zXtZRmsgWmeR34qeQ{~eJ(zKEzDWVYzn|UK1*qfV32>C|d*97OpA-DBx@TuncE7pb9 zoUgOrG!3>{uh_3^#_yfE=Mjzk0oH~QbXAkMjDQ*1L+5j-vOn)kWV964+K0_@8`(Hu zxV?ZA`g5qF)Z|S`RePsi=9}o%Lte#ujcqwKsS{?;9AS6}ZjQP!L&qaS$7afGLv7ZS zQ5V#13KS>UeqS&2b%zcm2X4`t=rEA1n;Vd?E`qa@={N1Pm=9!W3(EfyU2i+ zJD!Pn3N5&}3cliL9QBS>cQaa8b58EpTG{b>xywC)m#VEin64FeDDYuXQWtNQJ8)7& zSFfuczJeLgahP2Bli+hij7sdQdzbLYVgqv=3{v{PyGH8DPv5>4dKS;UR@&zLkP>gd zzqV6T_vy}y^B~Ug#o!SI5^qm)CiQm{*`K`D=L0}Oih<($1(8O^yqC@U@XfZxff0Y6G2VVgkq00L zHY>^J(Kp7JG$B>e{|-L^h`y^s$SShnr2PG@4D)T6^9Y_>DmF8YV!b@q%ZTuAPU`n}KklVeDQk!((v4Q`>AbqcFdtGJ?7SBTMwYmNi+ zY7pDLEPRwvZ)3zvC9GI<9j~$8@d0SLcAUSqx5D}OhZD6!DQnjddc2+GD3>4DX7g2D zMlpQ{(d87Y+Cvj2N)Nwcxn|uCJoP^BV{@9EU|2V`M^dfp2U3R~>Uu@10&!FYu<0w> zkJnOWwwfbk6#cP%Nwo0KwT3R3lb8Ii9}R`-6p6m}>Kk1xYP-&n18Mra#&RXE!(;sC@ffHF**m!&1x#n6Q--nJ5ISZl_ysV^r@Y zaUY~igE>4=r2JEDi2xJbHt2-E=-1Khf^YK=9jn{*V!>u!?|aEKQ4@kIwJlN;tIVa{ z3-v=_lO;ZQTwvhm`4{)Rg2Kh}WC}dD@4Map)aX~Af+E$S9V)wjb!c9G3txrs zWtFf{(Tio)l+g{(W#cE`c&hc=34L9)a9Y)=<%{=vb~A`bN%o*9mc1IW`zY4Uj-HN` z)=GUh<@aK4!@Ivl&C4{RH#v=UEQ{F3CMcncTrEEB`FZ?Rh`Zg&mIVg-`0PoF-PH{%-7y#HAF?djMq;_)^PG6ODE zSl*_@N!5Q23J%ueYRspo8(*Aa4468%sfew=N=P*1A=PJD>~}pM^E*($6dwx>?JjQm zD4%PZbJ8gA0y2~Bs4bV2NnGZwv8?}T`-cY(OX|4$-P#=)i(GNX5#@Ai3vevoM?z=T zu_6HF(pUgAOXbN*T2hc_@CPFs4#zw-gh`HJ?!C0O7yjO_Z~FxgR6>2FG&8<9u;ssv za~;x$Dm!ljK9qMfN|^4+Hn zkZW5GSQRhjnRHXa@WzdI zs^dd59g|l!Lr(C;?J8~unx!sNWvU3*F;XKn$GySWn|sX_A2w_|!V3tA+^}>e9irt#U~5&rl@_Y` z5%%x5Fu2P3p(>%**oQ$Mvp%RhsJU#nmD_PUB_Hp}IY~l=GiC$!9y9Svfm3orlJ4AQdD!c#A)&eaEAw(sB5q1yR$58bjK?e@o9SJu|!=(2tlzM>Gs_-)^^&2mQKM zBM(ac-e?;EojQhwmKN)r7k%=}aKj&CscwK*U8>CjNa?wASZuhV3b99s^sxYRj;1JE1?ZQ6BOa2k1 z9}4ExW0~2M&Yj&f$!2(VB@+w##-%#x^2S1tVEHMiw?EqP)rB41%Q5l13 zw%PQcd!hF-h7~J3VS(4fn_5%n<-meb>18jb2B_}#8wxM8&ZR{2!*ac_-l2UoQ0Z4Q zWu9W4n*_*3$HRiv2P_wir1H`RRYTQBk3UjyFD;MBt4vLr;x=@euOzV?qTAb>C%Fj?)_ZJV>^OSSfS1PIp0=WS@u){%j=2` zQyK}_>*)JZ+~d4DOzedCTGU5+vsxVHD6+EQK2IMq4k9CdFQZlajw}I`*kov zu!i((*d|!=d;bOEG6kzlNOc;XI-p_uLU8YoVp4wob>}E)F??%l9Y~wi#l_u4Yk8kl zOtmgvmq8_Q8pePGJ<@SXvk*8rgC)+j;3ip?Si~dQTVo>_Z+*4wsgo$kz$DV~+`O7> zlE3RoH46E|uS(ScNUcv`_=l0tvqiFGAkEj$q?J^Ql!-fa4wU!$4z&n`V_N#taYX+` z)phtCF6JN`ap%vFst+KdH)OF6eeRbp^!Lu+oQz=&@W#fLhVo3-l-6j3|Uc(`It_CP^N;=DMoodp^S^lOURk5I1wX ztgMYyY7_*@+uJ7aG6A~E(G%?SQMbhK88;pAkPzi$Ym~FnG*>KJoXhTRS(!#c1%rv; z>viHJzZ3qXnyuWEv4Tn63Sf_hecd=KNtydNJgQZHYHOATVQ^^T(?a=RWdp{|MkmIRcCX=3 zc`1(2q|8s@4;t0H;56jiv}vgr-l58-Ne}-R6wZ5$7@LS+QTfQbonko5yInLH|1f&R zY3ddQ?G3s(dY82rZ=IvQXMY0MtgZnPpF6eMCSdGpV#FA9iZ&-P&+?YiF{LBIt6Q1h zG^D@!4@y>j1a zGfNr4_ACPJZ=T&^<5$ddg;$_CqG0lRH6KA&7r5FzWUDawI0Z08q?_I+S}|$X5vMn3 zG@8O2G2kuwv39w_ZDeXQo8%t%J*_ndTfLlhjX&Coi4-PXs+WPC4k^$W!#&yhIcyn^ zwyB{VvtUd-Gr4IPBa(!%O_GCu<9_=Q-}|6fE<#`5Pu9&q`K6*oifUh8UBB^Sq=Zzx z-#AYh?U|gcd;2VZ*lc7^ut&_&w<_5c_g%l|^OauL;8zRLH;MFF_f;x~@_y}a7SSXL zghfPnnnlcZoT&I3H*w8gAOv?3gf*UkzK-Re{%2e(0tSO_sr4bwR z(xzqbU9FAtxb%_Do$Va+Kepiy(Dmv6H1>(OKK^H2^F=so5Yf$5MldoKTX{1!wpGH` zJ;`{#*5tdsoGgIaa9bZzMbX&P8ef^MOR&laXkkg=P^$={_&SG=#AtYGTo^aqru(}< z)qr+j$gDHrrFE3&VaJN+!7RGAM@}UP`;03wfh0)g&t)q+_N(Ss0ndiT08m;1aj4dx zy0h+lF1!ejAYFjmCDs*5YFtVDFMSLV(*)%4lU8q^fCgYB9cXI0jAE27gafd}elK(4~!KB6D zne7ahq}YXe2`bTPocL)NGyjq828cKNL|TBSmCrzMD{I8xUDvmJnpy89@TWoz zf*!9LFPCNfJr6mNPslbt0|j7tyCd&Nj+*H+o}0+^dtw?V^k?Sv#C;bxO6o7t$*i~# zNJfatI-;*tW{ZYoP1k5{Hcbyd%TPNDV-;aCsowa<%ty9^^hdi1PTGhr3rP(_ns3fJ zY59%zeB_SgZ>k7Z2jqVPuTW=h1cqqwMDT3Y3~`_Upr7DBA$<%od}+HFce|byhTi)E zs7%N`B1?DGDE0lGzSbS_(XW*d5m$ioJ*tNU*j$E75`Nm7aiQ{I0uSKT5_h-Orxo0S zG7u`S7k1ZP@JEB{&|Awoc+=za>Hw$Rt0$kNR;}Qahq|(?d>k8Dra7+bNAq=^%lLQ9 z^HQzsFYzAs;MMPsWwl2&b}PtrnwJSYcn*MPSIjtE4Yt0I9s~j5$aGTCzfTE&;t(tKc zZADps5#s>2z^@%pY(N74%cSB5q&vpPxr@0R{-4vy?Qpqcc45qT%a-;Lg1EQzXfphb zp3yR($TT(9&ZTl!T(mx`6kzQXq5u|R7$BMd*gsP&Uris+=wN6g3YmThO7x*0U;A9P zvNmq0zP|%+REV-iCtuOd{wOprmBr%0>uDFs5PTax;z&Z4~S58iY0w&YH9_U!>edDUpab#$TD)Vt34lHxV04mBA>+?uI#R3pr;xQ%Ksj*>NpC5v>fO1X8as-{6p!Vr89 zR2q;FV4aER;{QucK3ZA?ekS2I%X1E%tsSEVG4jyFd}G(+84RiY9mhdLziFz5jNa@- z#mI*DaLD#hoPEx`gF2M($L&q>rBhY0Do^;0`|>fsO=0H^ReooQi|#%|ydJ?&$TfNF zLVY`6O4@$odI2$WpJOv~pVG-9EKID1d3iA8w3%T`oab;>O9H3|u&WYgC;xjfBV?_~ z&aQy=9-efbj^%;8v-E!nNg8~23R5&Zhh8U6(SiJC)7Qb(G)LNi8>IloXRG2Bxi+e| zf$Au!2Uh3xo2RIv%iNp?kN7~hP_HMtN2uIL(**$#*{+(PS5r3WS!q~%=z@>{k42;b zN*$Z!V2xhvwlF>M_B>J#4p5-^0>Rq0$!kr|%)7SqYP z^4EQ#z@f(XL^I5nk$%u_P22qXB!^+1H_~lZKHZ5EYJl8+K8O6!;itR4ed429u9wmb z^IRc$=Fj~B`jPEqM4eMM0>{U24V{#3!Gtjp4NMjO#x=F(p28Vdt2(s;lw1*oDp9)E z^HFeQ#Z-qo#k6z2ZNU#QW41*UoGuA=jPUy5R!i!kX1XHEUSo%tv;1G^b?zYyMQ3{jSghZtvBLPS);{m1IF3Q2 zP5Q0=lbJ3AbqvnVTX zJ3lbHz2UJIQo3mlZ}FZ2T^e@HL|_WohPBY#+P3@q`@G9(JCis>(Pwt8=qT8>^)|Uw z1!O?Fqe=6I$Kw)Yw2OuBfESzM+KqV9RBeZCCBFNh*S(ORF8fUBY(Ir?6Y!L2>3m$> ze5Mh5p%lm?VGaVA1S84XlB4x9~>6z7y4w}A__WZys{>NT`i$2A+)U~PZMz*kEy zxOl^9$~|!BiDkIlroh5A^~u4J;&$xfw9Ut<1OqkxEk*seQk^|xH0l~M$fl?5_QYdA)lMV1_$p+3 zm^0~LnAnlinEBG3t7siy7(Kk7hJ5)v#j(X$eev`>L)El;RBe*Bv<)UER+}U@P=R28 z7(xYinM7ob0&=eRDJJi3)?N?h_05_wb|rhq39cOvDo99|H-l^dA~!o zCXf|>^KJ2&?3~d9OPi(Wp+iKXb$CtUG+Dp^$*`ZWTCw!~) z5#gU|n2yZ3k0pL{FIHtMRa9S}f9*N~c7nnHe1ztg7;?8Yx6HM=T#_}t;&`*T_&dM4 zC+yn?p{J~G|6m}rsI2tCnVvJtD7=PKOzYuuyOHESeA|pw+%sHakO}>RQeWNMtLJ-R zr2y*{n3EE(MS*TNCvC+FWQ(yXnp)C4qlm@6$Wf=ATHI1ne|>~1^K6w~>#5T_7L{KJ zh7ZIM2AcZI%$p`{pAV2GXvGS-T2#omEg2te^*r^%_q4JsRj^4BpM-K0=;(1dcgNcE z<&y|Kh~uW=h9scQkpE|64_At8!9I)wO+yy61aS0V*KHfQjqWAFDp==Rmjm@K%8Gnko51CBJi+$- z5qR6`t@mo|23V|FFNstumm^aPZ@kZk`ZKCzBwe)3Nvv7@1S1+F%S%-MN z7p8<heg0S0jkubfHDj#H;MtALu+=l`ChQHn-)0-XRS8BEIy+Srm_VTk#(erWheBBJqthk4fnhm=)Yi`MD zY-d$tw5w6#m1f(-%cwrzVzJc3Cr2^wr`~K>_r5k3E=YAUJv&Q)^%5uSFAXQf?G#%K zY0QlOAENLc+wwknxU~`aPd-a!1%em_g(vGzY15-n{vXK-I=Za!fQsr#W9!1Pib2H8 z2ZgOyPka~vYqHToczL`y(MAJF^E!E7NC;ACdrAvVQyL}6sS^Ot1xB~6WrW3xwX_5X z&q0!MTB?qm9xgtjXeC|CJXA48!DRPp**<_LWwtWpE4K3TqOli8BA}mEvjA8ZQpBPoB;ISxM=B8;3$~ z>~jBOMbNHSx)-bPLpuLU9WGv|uiP?#cywwTZtAs|RJ!;NP3RxTvCYO%f{XB;&v#CJ zoAZ}6#SE%xlrjkdM){D~j$%E4KupFT-r+@otvCTRWzBBFA-vZ4ynKS#cmFi|5)0qR zdk7)|z8jBBV?U9G6fnV^g8#xXVrh418L(zun+nKBmHbjCD#F>7`R)dg!r9VT5mIZI zN!{#oHoFINV3zW08Kjgu!F5HSi7oL<)<#>tIqPUgJII@F&3A8-@vs-iT z5L0kw7P7ZGl(cqVjkXn~Gmsv*u>X8S8%<(2*GVYF%rx=md-oNe%x0yXW0c6LO9)n5 z4JMABunt+`AtG+zM+KY9@Hhkq1Lejc+QKXT!YkR3{fokrIA^6_F}Q`HWsmB!8V&Vq z15yM_jx;T z$KL0v-?4s)Si17bRb1x1;UgqWJ8NS#9qkTVM$;}Gr?X|FN@nRYF9eO6muksvJMZR$ zefNO=Sac&osky_p%%xp~3Bdcrz&jzMf&dBcHzlyzOQNCNx&E=zE*?eJGnZw{(D;@! zZy%?*%ggRpy@oPE4~+#EH>S^vVDqJnfA7h-94P1-0sR^iHJKYS6ARs@b zPx-MVm8FCl93!M*RT(#44p2R0QO2jtca4Sy?({O^*$oMi<1O1^NJ5dJ3@{2-!b1M~ zL=!j4KpCKz8EWtQAsh8Be+0f?v@Fw7+DmJ_Ti8qUERlW@Q%AH-hrHaKyw%MuHcLKM zHq}$^KS%X(lr(*%%5tH%tLPLX_QbgCG|*(~OC+Hv+8hwT=#=Xxe6T*5E7AM#lAk3K70_}hK{7}VVeV1!PpYZMG5BDbU@x+Hv>Q$SDx!z8#%a$ed=Lb9Z zyZc0Wbp)U_P@kv&2jUNH*^sI$$m&JRvRim${utk$LLI|a+g3>Qox_&pvxQ{S)TdIM z%RiNNOVkADq14Ts>B9`LG;v08-bQW2Fc5i$j?WsN*j+WJdDW$2%wkuw_tbw`o=~X? zVo@$_V#}!dr0Pqf;#D$SOB|FuRK*`1593CGcBQq;iZRMWKtWFmYKbnLiJ}jDoT>gZ z1i9l(gasNtt#x>~$?Ww3-7^sJT~dIh_A?K`C#>bm2j?#FzJ+)Is!*1?ozZP#qY z);S_zDkxb_RiIoa!Dxs30I)KM!+cr!GQ%6D1q7T$3&^hV1RV6JB)MBuU@UE|V@0e4 zWR&*NLr}awX+_Q!J&AyOD^wmWxIW(?E(KG({oN~QdSE2Um~v#?^Jy{)iqxP2x!gHO zK?i?8BmMZ(2mXt%8#?KyJh#b$%Kv;KRQVW_6XMDiR%^imv0-L{&nDlWMgJ*B%0vtW z5)c%@Z21tjy69(ENGk4}#{mD{Zr9y9qC9u2$XR>Ej)Q>^>1R=o&tXI$ZJ04x9)Qtc zqVoNJHerlM@_-tMcXjucjQZhwd2e~`#M(Bil9Vfmv>88NFB5sNfBH>A54~MT@x_}S z@uYAm3kz#T(O;^A9e)ydxvU$Vg!yODp^!xBJ+c`*l315&3=EMuLwmE=M~e z9y*s~Zh((LV@hqb8GJ*6eKQ;xO!DdsD2oDW#hu#Pn6~m<#%rBGy8Qt2?9-Zkee2lNT>bE zRO#!DU^up$%xFo#i6}$VGx055wVWat;T)>&wDcHwUQIQzL)1-&!Mc&9Z;3)w>(?g; zf=~i?ACBgNLXBh?gmrB-=`!MKe)9}}4JR_@%B9jv)L$Dc%%|zMO75TJEBh$W{saD7pVdM~>zsUrZ`P(TazAF>7QEB# z`fR?NJ}vV1u-RH5c@O>5g3iR4d0c-F8`>`}_5&B~;Dhn?k%xVHYuliW^fE`)Hm=W0 z1<{Oqc-p8VWt~4=kGmjfTm6uYa;sDtn==oc$1w~YhQW&qJBk+OCIn-Ca{9|~ufXac z`yDhiI_Hsf@q}JW1=q_=z*ujX()NDwpv<=|y9MvUC-of?675s6+bfkYueN7c;2%BM ze(lumV9o2Jwj`#B^m``cgEC|XGnD!s?mhKWI!noJFst&+40Uv-7l~{!5C2-Y1(_7_ z7BSayR-dj&+A$=lfn-yJ=kuU7?J>tcg31Os0sCU#ndYy55 z>Z#(GXgtpp2}T^>o0d4=4Ta4@li;6+1bHZa-M8SBZ}|QcJITsiH0)S-O$6ik3#6ds z91w}e9M84pkQ9~Kj4j4A5SDUFrB7!WQ=~2fjX>sk4zZs`=SoC^nwtxe^a4~AEw&3^FL|^MgILCX)`5+V*74R z=#H5D?@#Tl&pBTq4Rav5ap#k;v2#Z zZVb`M2$=B2t<6N2z;uT>HzeZr{-)3*(zD(CIU#Fn=aTX5$`UScP1?`)^rQ7MS)3Lx z-{1))>gv#7g)pkLQNSG}hV67m#0%nooI~g2@$+M2!ld^J)9K~>#!1a|;7=;&%>lUK zW_ZfIUQ%BXi;d0B5L|!VxLw%_f4kH3@cq9TcHnjEiQf`SL|hIxt!Z-+H~#jg2O@)! zgEGQ2aQL;}^6nTjD^iLfLDDY_1?$34)sgjj4^D{6#Pc$sRIM(IT zj3@PY3ZyqDGutL0XP7|dLPF$f@Q=VyqX7c5B?R$aobZyVk6j;*TBDiz*7o37X826; zL^gw>+iY>WFxYcUI%C+7+Pc%@`bJUwY+(~s7Hp6k?tgF`e$XD!T5JT8rP~ZV9rcVJ z@tq~!)=fhQBz8NCrB|k0F7-MMX_#aQ2Ki&4LN5(JhO0i5*0e-wZ)x3Hog?S-)QlqmjN)Ia`%H zVz&#;Bev2Hq6`GrjTwl<#SJ@P7VkILPc#x7T^N~1X*{bsQC?ww!Fu;m*6p=v1yFy` zkq6_AXiQq)0#S^&KQ*DYgTRLDZrKtlIN(T$Dc}w&^0(DR$#+z;?y>9^ATqOjs?2a2A#1mf7ZGbBCYzM-{zDYScqkd==MPx{az zAg&6W#>Q`}2seVwZ6RUp9qHgdBtG9nvHGBGuy?RBH7*e;K#PzJ*Tdu3D6gQ~?@pdR zM-IPxxzoGU!_M4-d`%mRDa*J)evcx7`CHZ#QmD#bgDx;ZLOEpa-7C(KU5CdkwQ#M0 zTQve`gAqbR>-tt&ZiAUU?*x{Jvza6J4}(3o9kSNNyCu@U9n`xQuo&|Y1ueYUW9m;h zEsqbLl3(dj|B4UaPgiw+kH3GSMUr=b_U5>Z=K7q&S2EyM>$K89>XNU+l=x_%UNt5+ zBGlShuWzQMv>DiV%MPjgVc%HdQ@4>iM_7rz8xLmP6oUMZVif`36{`ejix~M|#iAlT z{-~q8xzx2{*f%@)@yr+VSo+qkSXA)q>8Tm~*JQS_({N(dMjxb87jncW3@0LHzG_;a z1^sVHM2q$lMQazWa9PnWaT}W|k4piY1Du!SXeA*XhriWuHrg_xRN-YbqPPQ*c#L+w z0fA%ASv5Rnx>YaL7}2nAkz~Qn&PfvB`bZ8E_yb)_V)E3)7Q)alMOZO+7rKsMTRJE= zUnJP*7^72r6&8qeG%o$pGcS2+eE;4C?fm2tHBM`(40dMMSsbOqQ2&-zeNNy=?(J zxB;+uUe1I1I(Uf!H*I%~!Q=#rt1qZnU$KzF?ArCcR0?VOF&S`pJ7OJ-1Nz1Pc84E}z6J^J3+Cc%G259zB~)~WX*^YoRqNe-_aLW} z)1XE`=uJT#qzaVLCiD;B+nsYZPg)!8>Tg6UxM*~xE8>>jE3B`##0s~Rq;BQn(YNkyll!)6q~pqJymtSD z(KTan)v_{O&4}K&uc}PKAhf-Ke67iT>q6ugzJjn(JADjP7=Nou-n#d~Ms$|7Jv3H( zH{U_dyC&Y@%c?w3Figbr?u7cQEI*Y9p~eQByz8tIY;DaI6MF}^>ZvnBv9tGlz`h2E2^z^C-x86&X&QSmm9`e0O$> z>rYk}?W;YJAann#R7Dx@3%~p_JmTb}>Ue^&R4$>~29F=&%1{O@M0wj`5nW@sB>i2= z=w1ithTFFI3eXx=fEDNFLJU7#sCs~6$+=%5Hh1|4WvP+h<3l!$)*teMBad>q2T`=P z^xEQ(!5%c5NIReQu<2m*s(8)A8i!rDbl^86tf1|chHNYCODkul@CTVP1M(+K8D4&`mY)MmNBJp5hN7+~Vp%^JctHbmTC2*Rgs;p;&O zdF&k?VGkR5;r;FS*v>AKPN@70>Sgs8Ps4r`5cF*oJi%==y}HnmJz(+(_Gl5#K64p2Ig( zCIZz$7;IFl--IU;%l%&cySgk2aZY4-1S2kRWKvwa#)3fhK99tBun=oMU&DuV`F;dvj9>Yj$U5&b`w+?F>CdXM z+(N~g;;lk0MNF0UPP4sxA|BVkrAt)`Z;5|68VSVJ0vzjocN(V0W0W4q5nAZbGosM6 zZOfAZ?!@~tbb~M^h%Rjy-8I>Kmq{I8dG0_F+3b;qxSlL+cI7wuhYJSxs8;%O>EX{} z*a#&{v(FxcR!D*`S%D7?-qy%69rcI5j$=_&?`7T)yTBVEwk50zB|{!3ypfnk{5}bO z70agsuq842nWu#65uqcT>QeK!({+pp+yZ&wcv;ri^Y;TsguJdTg@-kk2=*@P3xV*b z<_}~Q=Ksk8;DN>Pivjt&To$^CtzlXJ9;H^ol^#1}06K&OK5HsIPoHdcdm&qJTJrw< z4vG2t#v@`N# zN5gkD_oCN=KbX_}ixKuf1Sr1HI%CmR2hoZDjY!REi@rac2JZe`#-&8YXh_g4!YDfq zcWlM3m`EhF`Eqyqk6dw|0hFZEWh00lzpaLws9E%@O7XXfIrAFk{{qNf5dM}l@Kk#1 z@wnTh2i+??AawoUbhL;xuO%*5YV7;eh?pnht`wSjHbVs|^^t(pORhjg$;T?G-oL+MuTRa&jf0 z1x1vbkOnC?u(<73A2eh*Pl+?o=r2_I!bz{^_zr}Sd)LxyEwV4C=?D5g;y3sFG5~s* zVdRA%x5hj5D12_$n-~fSH2v@nx&aV3!0!;Los}~_%p?Zyl2-Jo;u>~qDx>8o6uOd; zET}(9&1pxVI$G?N&;ygR7L<8IU=d+k9kiT~wHErJJ6k;F$%#=Fra^iWb%B)3{po2% zrjsbDmgkuoHs+|qa(lZxLXY3 z=?F}je22Lj>E<~ZVa*FUDv)6&v_O&_SD^=!_t0s;%EQH~y`I|6pYvaA!`;<2jxbqp zC`#c1%sX6UDE0iro&%c^eJ`^SeXB1mXw^>@;C|xY7k|l;AZq1Ls7R5W-~$K3n6qs% z&*oBSO1!|PAdokG1BGC?vzk~&*vZ6&3;At@n^i_&YD&Fu4*9R+Nlt%Aa?nk6c3M+f z2z7inGZmRPj}<6meWBh>_BxfB+q3jb-t^a!!)8m%RZ;VatmI)em zfM`6p%2gimr4K9E7BPA_rr4I+tYzxZV{`03fm;_hL8 zhDx;wcjq~AX*7ndiY9xLpIIm5K_Q?DqM?EA;??i@9dnWs?u>1B{nl_3BU#4LKrtI- z0ylHU4!PhL=pox>GbPA_;@rb7H2Gc{_sx77Zo={8v^%^?_LL{cZjC=uoob98c05Y2 zdJbE>il`_O3Vqsv&{8tz#5a-fN0CYi7r~<{0+10XIDlkX?Btb&#k|@$4O!~zd+s|{ z$5)^QR28e?-4UsTl?M{IGK$R2D}T*x!T~0BhWi1~S*EP5m{|P_$)(nd#WK8;BBC2g zF~Yj`Y`dB)HUk|qyw>5~CCEK}>a}}hyfSkO*dOyLJ}llCvgji)d?QrSf+lkBfxlHo z+}E$owQ8|l9IH{acAX#um61b@`u&fUlLhZwN?YacY?pX*v7!QUB=J;>CpStE3D+Y zkaR1?V7+Q*K5}DPeCj^5m-i~B7O~^1Y;z?$#;g2(w;P1H>M}SO2g(c5Uzmv17&J)F zx|Z&C!yR50l3-dv|95-pot#KnJ{tO9;!0cw|3`zwsYwwQTnHn0(n3PKTQKGn{A})u zq@|;=xMSWk%ctvw@(Ylr1Og{9lIJ{k#gPrNZOXzvGI^WRN>6^nOD)8|QQqUj(hmFiee zGtykQ^6~f6TsE>9pEs(cK%6jfuT)W1y-{Bu05NtJzMXvNEL6`vuYK=~@u0M(ZIULk zMM~19N8Aiw7NB?JM-ZsV!8TEo+8e;z*CcrBYtaUF)5aJ7808ZadKQvILq4ah)q17m zFoUz3iBL*_RFh-1RmJyA&T%`AU8<~w7eW?KA_$uvwF?>gnd+ANB?P_%T{qN>D;1NQ zjK3lbGA4CS2~{=$Harl~Zn?VV_mY}RaxsiFB6wsE(`cxf3>85>-SkTPG(y3M5vDFT z3$S4s_R8xq@hOtj(45}PiCC}NiawEB2q-m+600o&yj#WSiG&B2hHukAYg1)c}$9NiQM=1 z7lFO0({Aowo)WJ)GctZu14RCxW&8=wr17>!o{4cw%Y8qNuX8yvyt>27Sa{rBk!%3Mq%+ z_j1A=xPCbD=HDFnf%b{$Uv-*qv=qS9%S5O+VwZP=XyV$eee#4?g4})g26f zL4P0*JrCi0TkDDS_lNi_#$;Uy5fYH2Dbi?)QhXlrkQWQ!E4x>pX$US)JM#xX@1^|x zY_cL#Teg8a%#~2Y!I=K*&~Z_D`~jNz(#6dMAN!3tZ6I(_@7}sM`_8dBUv=EB>o?pG z%bU#I#4!VOt2Y;F7vBjeNptm2Dh%s_wi`zH3gPY#pOB7F9N~wYsRDpL&$>18I;LK> zCU@AAIUDM%C3Va87w(G+|I~=o_H7vH+Gp};Bi>}+DIxk`Uv2~S0)(sn*nBFbfK8y`@Z2ymqyT59D4c>|CQ z8eocP2Vag;dH#_sivLPhlHm{z_p3zjlG0G_HfPc4r$ZU@$vHDz$3b!Q!zv-jaPtJM zg>U4{Jv^tDE3@fBwztesx?6+s$x6eIi z4xNBcj5;n&6P6||hDLVa!6*C?D91%aF8+9M^h=ie|MlGeRSr&ve!wH03FQb7`uBu! z9s0|cFgEaY6JyGd=wE`-mbhH!Hr&8%Mo{*q!^g=?neT~|Vk4*JUHvLUC&Tx<;Q-Md zwBDs78O>>9oglr(S#(Nn;SIajLZT!{8i#WM@I{FM{+y?+>MSQwzS1TiX9f=p*;DIZ z&hRT<>iw91AuaSCB_hU>wuqTJjI)De+Y!ZAsu?Z9KtIgPob5qnq&XC>FNbG=pLd$o zIY==%$v zOEOd$By4R%6c>joh+6rb4$DTGzAu$fy>c;q-LpU6o?t3nOAW%p4LTvP)gZvE^*3@_BkB2)QQ+_Jw7>n!nXPOxdx-PXUJ^`sGc!Gxp{S_PsEUHju%T{fcd zn14_ZXcb**WBhZ<3hvKe8vVn0Tujy^LSNO%-c1UkSCu0=b^tWw_agK_8ddVI5@M3 z<=$U@CtoH>RahbtCjr}BQ<+*1HMt_P<;1i?gj7U%{M9-8b|e8fkC=Nuk}nJ3N~FHk zZsHdCHuWb8&sgwpHl(o&)Gbdt@RM7obm%KR67&$=#&Yu<6QEi`Zkr~G9)i&7vic?h zA>|O?T^*>?F)AC7UieCUh<#BSaMrVfw?~>WGa5^+OcB|$8rtCOXCnX+GIHAf(>vx0 z1LAy4a=b&w%Yq{5o$3SdS`Iv!A5}+;v7hD2Wx#e4`m?@>Zk$AxVK@Av)Y!}VOrz5? zX%^B~dwD{1%g94;RbN{@{ueJ7(F*<%tF`vS0lmjt=Z`OM=HFm3grZL=1LPVm#3qc| zAL6`&FWc##pFf2EC1IKmtJ}tCD~Tb{&&2dj&AiJd=Znb3z>3H|x%^z`5j`_A4Hx4v zVWdD(_Np`}U7_)=%>6N>`?fYdcJS%SQDEqaC?HOIaOy=mJItH-i2 z4TRP%zMGJok_B$-6BSrq5?jAuKXM-nW$uwZZL?Z>hl~8V`%(Ewgdck8>EAx&l~QxD zSaZU`->u`SHMrVfN7Af`c+>IB^s%Nv;fuF|`r18OU&NV|ZLjEjmsfQNK?ZoJ_rz5P zb^n^iuuWPSf{_2mdK$D?z$LF50ZH&<_ldULD9Jx_Op$$NA%R<1VsVx%|+=)x1t$rYyZ-3qI)1#1R z?7d=cyARpKJU2sxU&^Yn&sD>bR&V54u$jH^0xt;6G)htzysc2Uus`eCm@ z#D^GCm-NZJ4okk}oi$tq8~&$!E+Y76R6$^l_+NCrpw3e65AB?McZsAME&zOW|HR(; z$zV^O-FA^vQyMT)tRU<^ypMf}b2dL_aMX-s#&z+x!|X6BQmP0S;%7%RXNAFNKGq;q zT2j~VBlJ0Tv3D9l+(6TsuHMIUXh~q!PeRonmR_^pV^_shdxT>I-Y|RwP&Sw(k19dO zY>ZDis$XOid*CV09sFk#<$iNEqtK^TZ$xiYxlIhYSUe9FGGk`)(Ba^f+o^)KG#iB} zYR|^uM~nn#Y>FT2=z}Y{zcAD3Y&}TWc%LAIRcuHP7*YI|7zCH6cE%!8dKn$}kM`U; zKyO-SqYYdUzV~+!-JjAnNWFGzX*wR(ZD?n)9&+V1d^U*rg##EZk+$nM?eG)U&SKJi ztxULYg+-AAy)NkdU#~F}dX#jGqPgb&x*tm($@GPf0*6J^VJ60qW zKK-sF-@#xP3%l(Zcln3{pTX<L<<^(pwoklJ_0;NL7BkIfoiRJUt&^gm*_TRk%IU(=zkBr>$mywL~<`U9L zcgoF>06m1mXhd;j-$(DdQFoo4N*1{j&lL1~1QS1@TorXlMbEOv!1P(&C8Re*;H+!Q zleG?H#(NJdBmAt&mVSua=VUy;IvUqC#P>Zv=ktzBk<3!9@a&bZo2K%JOCn71&^)`_ zE}VX*rN!0o**%gc@S8;_Vc3qQOua1bex;(4{b8zLk9C{rU6sfO`L!)YoVb&(7Efaa zOWycPUdOq!Rgvfx324EgF<`60vuc9U8idlC#zMHtNS?&h*TqMd7dC^Q0(4-4#p@u@ zdbG-YHN@6%biRC-$eUPjm5ifhwDZ=Pvo~tDu0*TMam!RiSJ5i0gQ|UJ^30<mTwiIugF2&aA?ue*v{Wcjb{>b1q)bzmOL_59vde$L@xhL}2QhvXaI^ZzQCJ%ruvM zw>GJt?y%Tkd3|>@czt^g3{?)Mxw`J=+i4uKf0&Yaf6!={v$FTQr=+7>0$jkCGwNNy z(+Pxe?2wdwAM1&;)p#{ZMZWILT&JOjep6RlBY!ArQNnGkb~8^IiJHYBa(@^Q*o(zbV;HR!(h(j@A`I)MsLLr#1$hQ}Erj?+PF z*!!HS_(;_*igZEaf4r%d9af;Jn01jIUf;cxk5pB+Omq70hgmWaTf&|q=nLeRP(bs( zA{`28a+MwZQ93nT?Ncv&E)4yj(icg(zP-``sPwFFc8vQ8Ww26cGwHe)p~f&S!Rrr_ z+5G3s^Nx)#TmdIzuV-=v&+Ta^ac$R8z@$}L6g=74-v)kzfLk_CCN0yw;_Y&eznau~ zTWXf>ODrb~o^KYcZAV1C!vA%_u;~+dCldtO<{ziqtVR0-=tOS8D)AeE->EJ${+4>! zD$A4TN_MKK=HBdqB%7Fimf*p9p-x%NU~R^81o>0XE%}PI4vz`j%l&t_1gyA3{q)*J zt+tUKCwFui%`NG4lLI5W{(XpbfC3n{`osjqpQ~3ut8I`URfSpKll3{VjQKRzvfEr# z>BWL#F;~4z^lvW@=!mN+EqyU`a5ww)5`0}3L11gyt*J;yzt!acQ694|iS9b8$MSfW z?ylxSi~q&**njXm77!r(A3O(R$kMjbVRHUNzFl!T*LBu3%(T%mWPbD&Z7CGM^%oY$ zle}JQ3eD~iL_|SNyUj@q;64m+8WMPwz&@tM+6+att8^hlHDel`u#l|u4`j_3xBxp9 z!ic_{m#Pvk6Zm!<8RZh>e<8xzBr(+8?ZR6cRZ3tURr%A1l7x*3NQ2Zt2D%=%Nr&g1 z+H1TKzzU~_?l8FB3gIE%fSvBitrvF#n~f$d*40xAI(*CUuD2_)(|wI)qI4Ya5(JQe zDeL?7EA6<;sZY2Fa+hhhZV5M-MhI^M{5FV4cU$2~_73mMiK&I|EcUkDUJ~(q>btS$ zJ5>EG)^XlfRlP7eDwWb@D1ycxx@K+gy3(IK>k;?G!WF9dEkG%Wm4>$=K4>3D(y?bJfGFXljtUXMrPSR)vLyG$~83KQCpXS$e1a zVzw<-E!HjI<{RchWaU+(-SMi9sWG^cdKKj*JDFmAqBYpXVZ!-DJO3VA^!V62eV{($ zo*0)}F^!n9Yq8;n7b^T}J*$%-0lQ`bPx9FMK>I2El9tt^Q19sqIuU3t^HMqVQi-~i zXk6;0bdrD0;p&bgvgROLF5A7Nklk0$LjxPMV}moKN9qj~>8zIa%rYzaX^q;z!tREN zLF?MQ;<>R{N+VCc^m`9`#Y+87tg0Le`9E`9Ac`JJl-E^{3dvBXC>2?~WYxrH&bd2Y zIz&`DEEuJMq&80+L?Clq-vkq^*f8sDK06M_`$7l*s!lR(PaQh2L~#)-%rBK5TNh78 zC=Dl$X~vc%qdHbdSuR-tH1HhvCkuIc;k1#S4`=-yjy9J}A7<)yjHRJibf)f(#3KE3EpO9DZWG{PR!<_zdr@d1% zyVJ~W(1F|mdE{2TAYp@AZT@HQ(!tW-4-Il6ipt1)OK7L;Cqu0~6fAaA`%ovNF6h2& z1-@iAMgjjLp}lI{RU?1P7By0R(GDmDD0U9l;0Fs6uwbK}=R#PEGN*hDx2Anz(z^QY zKh#z{JhJ${%w<|q-t5Y+U_>?YL+ia7eomAX@aL{%FI5=Gl-Im1IwY&Qn>BGt- zOyiNU8dO{-BB*+eaek9uDSv!#V37Q)fl)YobpQ7XWh`Fn$UofR?j&3ZH+4&fEsoEw z?QRuW-i|=mTmPO=RY&D|TqadL1WG582*}G$9tu%68oB4ss!G{NpRK5PNSof}w(KrI zOtcC+m+OmYobZX|9sjH&;29|2yOed?zbf)KxXOfoHrKO!?|y@DnYT7hT0oD$aM_6O zThbanRPzfgFWmKva@>~EAA$zT37-E|!kB-QFox;jKUU~J zg?moF5Z)nvOjlH5W8I5T*M82lD2!9$faJejV8$X>el`5{;Hs;fnxUTe%907yL+vEDiMiR6k6AZg#*d-oh<^dlt*UE0<_#&ACy zlEuT%ia%%*9-r(he7I!cte%=aF#jAYXbx*FseH_Ux#9ve8WltS`Z!`E7?$_NS@Ry5*eAx_f?%B#aE;COD@91(!nL(n^y+tHlHbYqm}4pe&qe5aODh4NBh?i8ksd+ zWybeTSwEI`r6-?)2^{5mfE=S)*kLNB`C9lQ-PNJAOxe?W+d&yWm5q297B(sw7Z42$ zK6%3V=pBh&S}izBhd>4!G`+JB3zCdb+Lsqf7pRrgyBUmAf%W+g1=L&4SZ^KK)&ZNXLt&y>Dv4JS)~^kc zaz&{^g&oZ)O|$2K7Newrj43ON+VAZW&mY=L#6HKbb;og(mnBmMO|Xgrf3<9~E;@b0BQv3O#jX1e&gn ze)1r(Ze;UDtqi$k*y9>E&fL@f={+rNyMYPC0< zPDRsDy3s@HRfO5qyv|iBlHgkDiz?y2l$N5wkDXviRoy^gGG=fVMki0s!TdP!dzk~ zrCJ6cM`TZc8_>iDdu{dABYC@E{Q54pGcu$t_N7}S9)0C$ zvdBweps4N9^UV`h zEMgLwVa^C7KP2}DH{`kwzr7#WHJo^bLX4?DEnL!6O7uTcY$V(crV$&;d>O$gb&q79 z-c)w=w;JlWs!7y+(ULdJcZ++WgzW&;%{QHa5(FJ3ag$+E9X<#((yg2 zXhgVb_!znqmg9S@(9^g+cI0(JoH6XYE?j>##?yj0l9^Asx6GuV9RE>%r@6!3dp7y5 z=-u&@@Vw*mw_smjHj{{FNR(~s7oob8jp{}BSNQjcrP>C-bhtE%jjGDzjG)k(4x+ zl2W;;eXC7ce_`-L64w`eOMjlyCYfUp1jO$i=}M z_F*+;=*w&qFE0rrIm+L`NEA0H_4Hzd{tpp@Vb9I66JmG!g|Q;mV!+1G=d^@lEFd)a zwfc3Lq{yeV-PAGG!yAe6#A-7}htl&1H<01b4g)oI0L=I*hzYOprD~Mi_?JY*Y&izO zmG&SwK13qY12yqj1MO2ul$W1FpWu)X>s*;c>1XDOH2?aA?zy_cy*Dfj&RgQ!d+=JW zBJWsy8NNz@vCj&;r6qM4rhlPM@VY*gG%L*pmbB*X4UsO*4MxasC zr3Yw_Zq;Ndrb77A)IRQjTLJVF%m6vTD3>2*Q7btV*T=jMrcuSi#a-=t z0^`_aRst@y(TObM_J*&e8~#gaCU_=-*t3ngNwU=YaZ;qFuy40+I7vIqUulQ4^t+po z$MrBi+snNGewUPh%;{PNiEaU=RExmsrqYf_gVg%BgU@t{#hDq~qlo*|r8ZV&ngW`g zZdk+eks!z@7u>74McjOSlgcMYhh#0z(W4iVU9~txQ0ATCzB2x1%t{s`K3?R(*LOU< zO^0`9qCc*+dwm{GG4i z;U-qcZXNrStI?0{MjY+ZLs!Vm26|%^p&)E;Bt_I!5Zy1d$gQ-t$v$2luNqsVAWTbOuceO9A+_Wu(}w; zssDxKn7MRpd+3JTio)W1C73;f%hFbgFzAoWLBd4vOOH|n_-#@9IQ|mQRcTDCY#JBW zKDuk3W6_glHoTg^+Fha%NPMT`$lS2xrt8QWPMtgo+%WRc>spqhabT?k;SWd2UQf;& zXLc>esS?@=@!6Ry=cp;e$jc)9>#i=!PH?u|&c7ccND?HfprH5`t}nQK6$X*6$UV*gJR0 z^C-aChi`&yzZm8SxYj@Hw;L+7JQDwH=dsjli&i^yZ+9S;qN@ zS*`P$j7wBjCxVo=ABrAxguvaTrT7pkDRXC7SeyhFH_@v+?gRMR%Q9!XgwYaj@vW|aILGrjg-T~_nI&9y^40Une zUHAkgfpOa_3&AOfD}CTYm*Fb$-ERhcEtIp4M_wj+ zVmwTT7rQZK#0%QF`)`+*va0F}G=e>9$IGNLLEf$iFh=@FU- zM0^5YAuD%Sw719}R`ESYY;j_>2p>ttRtxp_ZXqkoTh@!9<3*py)XB|$LrW7AHkr=; z9!m?4A%U%>F`|P^T^-MFXWCcRoB3X{qFDjb2HEQSLY%`E(9 z*?g(}2(-1@S23^maEO;&W(>9pNX#u5k)_QBPT+|s$ZF7p#E1AE%uoeLH1pwEvdXvXfbz3U3!W=$n@0( zja-1>S0CO156h&GaT#SlGgtHs_%J!%snO@-Or89{yR}yd)@D5r`@OzOUHM^>oPQuw-6k zhd-OLqMr0%fw3u65JNqqaCLP>0+}OTJUo$UfvM0vg1b~wHha^V3Eg*+iRMVnj69Cn zYirw3e6FSeo0~08$<3zpzLj~j<#wIlW1bGBO+VcH+X1k^U5PMBOwf#1;&nvn{qo() zV6m`6l_<)G$1orj-_pjw@LmlV6)7}&@sM!J+jY2E*3;tO*m{G~M)jW$8tNM^3VjE# zmN?sE3pB6{T77*W%M(#ZrDej`L(4OCsn*;}&LC5T?5OW@oOd#BQ4>zinR_(Ou*73S z67S$5+h-cxFow`$K4kOF|9I=xRW+_Z&}-_m7(DP=0t!={(p5NztSkob%PMsf; zX3{8*(mTk{5AFY=G!yhI#8eR7I->%oaEI;E;LYURRJ7wStgN?q3%4k-J&!f_dI-}E z^YYZKD&KPK@4Mc7H*Cui`Ck@}0>&FD7WyYZ=Z5(o3m44{ty}s4MZnmYp8oRI$-)-z z-D9f@@y(7or{`zaTC}Q&9FA733gJ};`)FrU+~i60KmVMC0^{7r?KMHQZ%*mO9F5|B zBmEy|fCZfimvP=@e;;aL|JLg1R*EhPR)3n9{jyRa#mjO>|)3T2UF zTeDQYNzds)CvHS_wbmjNXa3R_OyTg3R47>O;6x*^4M&jnbl-7Zo+<6ORBR<_76!s) zoOTYbeB5lnpH*~#GwYC(cy{`kcf^^?`d2Lc8}zHN4i zlr-m8CxiLlWmUFC?!{Nw*C@xvpy<8dqt6huWX=7+Z2K3!{bZ58_zZU1c?-c4c_S(k z#2D|>ZrZUtzu{lD=vsxx`3JgIks1jpf}Y+PT7ElN9QRnOOuKQ(k)we5gqcX`F1H7u z6{?_r;fIs4Es_XpFJ^}RNidse=LkA8_6;m-&9jr2Sg;!S#Bpxzi|cnep~B`F9WDH| zoMZWIm0L#8=;=o1>}jNaApb>t-u)|z_tK15D(@k7ZpHuN_(cC`C~%byu>Y~t3fz7$ zUoua6c3zlvGhU!EP(yrQMcbjUP=Y8t3&{BOV?>`mD&6$=rKX2?#J*(m$@xH|gp^$z zWjcx0)}ShgUnx2ZWzFJ1Q%zdHfKH(AB%c-$UZGbEPa>dJwgkb(ko4uy+2Y(HgtMYf z?D$huG<}fbe^5c{1p~k6(}<3^M7nF=s@%zEf1oDtY`5P$I0!m0x80+6ZAKh1INN-P zi~6u)bRfo^BKgbi4+VrF)adVbmeRsc7KnElo5YeV2BfKV!TLeb zJh_v%{g+(5^pO4Z6aqW=k`^FA8iB7PPWN==Uq0?k>|>Gi3+H*uh#cb%&;wS~evlBJ zNW2NZ4gRPmq2;ptie_3|k?B{j8J)DXjy&!l%l{9v{mZ&p*?+Q;><4=L|8;BqPt5&S zC;Tr2{o8f^uP-K7|GaV2b%^mlx6uC=7a^8~V1PJ06NdJ`(Eo3G^_MgMUq8|R<6p+) zA#eOkz=zKH|NU+MuNV10M#lONRQ`{V{|nClA0fGpQpq(VM8cx$6-wvxXK4N3^1Qo@ z|2J>+PX{xQ328Kty$TlK{>}cjkcA1NC6ammin95g@EcaZT%DzbIb#;>-nk^c3c-`n zo;G2(2(=%iLwaTx8KF1%Jy){5~#Qj<;{+I?C8*$e_8NUqRz+i<-Et@6>eAS z<$({a#wr5Kr;G>*mx43Jw|GOMc-6ZaW#+t=N3T7t2#JimbM|vU|!)QmeF!~S`gIR=OItFKFF8@ zBs2cx(~~D~)t~7PI-CJ}5x!UZ`tc%)X-ptav&pU=r{oSk*p(+^TS*B2^=_Ok67Bo@ z{89Bt=C!{cZi^y2!;-u-f=>fm7&>2$bJ;F896PxPyY0rhx4$@+D`#Fr=Ilb^*nfQx zS2Dzg?|EVAT>t(hZGz|)M3m8%#6T;80tw#~k5|Y!m!1HrX4cLOvCShrt>#4sf2c;< z+s$=CH2n=0uUU5J&n_VpyJvZnc2?@qs4se|8bFt9AxG*B5 zsE2?v`H9lPigS5$XAWcjHvkpElgzW&gT%dcSdr{os6SGy=-n1aq;87z9I|iku3j>^ zMMje}=Kymf6P@T;VWI1lD4P#+;WC9MDR4qwn3YAa=kmik^}+>QSYna{R`=^?#pwcu zE?m=*Bm(%sv-7TXF!b~Bj3>yAu{Ky73w&=g2?po~aoX7QE>byLSD;K(Yn=}dp{D!S zYfjqd1)rJ=rv^QvVy0T`dy#7PGs)83X4ExjwTZ#*Prk`Zd&w)8PhdR;4wx z&FiQ6EY{=mjY`~^!z!SrxiiZJQW%;3HpcpUGwaLbSHXQ%x*W zNwP@bjG!$Q7m{kK^-Uod)j(DPD4X<1BP3K0M?8H-_W2h%a!GC5{Y9caKnTXO4RuSG zb$7uz46_2j%wP)5x>Cj|qv<b@pLQkVXAePrk51X+3v*5kx&-hQ-9{BZkljRP>g6J1U*Yz~e@20y- zUIcpVw}2)g4c-80C1sl?YdSsOomq@<)3+$JUV<7=+&8WXfR6WJ?RIaW)iN4=rFMbi z_YPWKyr`$>nvte*zxZsSflJpyglFV_S?&cFdo8hU9d40>4DV>7Oj9BKktF$ZwO|3` z*n=mUo4ekuo%cPiODk(b;2bEA4W^#nfx{fXJc0;|4LY>8B;SYB@BXy}CDb6{7W7Ln zdkIsTV$WH^{Bm99A_97@^|;~+>0$rXy))}g`&?MRvM`PML``hd3rTWw`qW?sTkk|n z=VTDvhn3;2FPxK0F@-}(uQ{4Y_MEvz4xc(uVEjF?f2W?4Zjw4q4v@yxY4$AY-*OkV zEq4U;Yx1D)l)OoU3{dKxn0z?hpML30T4B3sp7>U}DHjtB87tM`A&n%TrCe6uhRMaT ztDTGd1(28h+9SzHERktyxUIX`%KF?GnfiV_^*La z%|uZ!>PhNQ`K;ox2SRb&xqvEICx%lU`)xw1$W7vG@V1PNPSIRC3;jke0EjrYl4$WR zD|{l*I}gQ2Rsj@8+y_4hREfXKRH}+pD~#P2xp`-=eE$aqT z8|6e3{?C1Jav5KyYK!m`9XfG7>7u6}ku^}v9QLqU+dZfKIEw(1#A@L}ok`lkaz5RcZO6~}LUCC_Khh}6 zSqUK@wvDDIdtZ#PQ1Q9rU%I+h3rE z4f~Z4OsqZOEj$w*ze2lD#7CN-x-xvaGx&z(ol7Q8ytg3z^>D_FR%yVw0-x@=RAi3I zlT0Mw>$y|+*Ipj3_REhiOBiS!>0D9iJ^KU{j?C{|sfK}pn&dpn$!#@Impr80!bK)t zn7>ySjMJ#E%50f7e^C0hL+IvuS;)2w6K}OeaVnH~@VLX#{u!4$!>Az-nzSq<^j&|u zkgt*ysGR99mC@@V4i17n%+V#{g1^NL)O8r#-yy#q7h5E({qh4_F%>@GkTJ+gj*%iq zJjyezmZ|gMztF(u{lcqd5L(YT4y<~A^VSfa*8nVQ^KpilqODVSTempIaM6_JH$zQ# z(sk}6!lP{L$N&T-jUV{4G!d_t`aeIR}bYrt8V0q zV=N}KE%#Qpt|6+om+s}(A3PPQ-y@50eV#`^bD4jwB{y(H5$<+s8Sl9Yg0S2b%@`3q z`B5Zb+a?`;!(S<8c6W7QSlgW;N8&h=u&rtOK$Z_su|02lFBX5wI-LTSotr|Eg_OLoDb1%J<~Wt`ZvsIEQS`E)XR;=%Wqolu0*gyNde5wj@NO2tX4x*LG zP$r~aC}~I3IST;nhkC|@pIjr=x@cev2W$Rh$_iU3ilOwBee*v6h)wmUyh2w)_xo=E z{`%FaNOVA&%pj#K4LLUh#S3;6`r|`I4^#4_Lx_W#a2f*xfV-knPYKJyc+2)rtYRgi zfLFbPz1xG&IB+cY%$b066TqHy$z+W$kBoIR1wS=9htPXlJn@~|MW9@p&~wsHyDqNC zs&c0g9u*P;0wAd#DV6TPz|+0dX7_D1N>+upl&){7!K)q#uQ8G3x`UXFN=*BF8@iDG zUZELCoO~g;GeYp>Jm-xMB^=qS0D}NQ6oVCxnzJKH!r-SuPKzk{Bf^H|JYFb+%pK&9 zYXow8-(^8D~OUOT=c6Aw!48U-K+68=XOFVSgi5N zA`_&#h*~40B?hS;o=qJ zLa~N>@~*kp_ZtR?^q3VsvPW->p+~GsTd}6DW&fT9U^WaBssA}B|N2h;?T01Oö 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 O@K0V^S*lvXIOu;lz)rsa diff --git a/docs/images/documentation-2.png b/docs/images/documentation-2.png deleted file mode 100644 index fb87ad77e83dfd9e2d2ccb898b72c5e6d6ad823e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 542540 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%0DQrSidIYe`&ewh=0#w?YI{1K*@JgM zrxg559nN8xGxYD-fWPGzf%~vI!Pt#{^M28<;o+r*FH#N(p+bc@@KoAN0mP@HrS-t@ z+rYHQjgm4@3V^QZ192=VXUx#gG`K$u3O{hVP9R4VdoQgb#NXfF-e2EKkdT5CZo5re zyA9An5N4}l*wM}f4J@7$NyyKWm0#1Sq8tb!V zziqgdS`U7^OGzW)kt31W*$NOa*(@~4+6|mjFjQbJFBE{@yGDq(uMn))fGP)~b6qtO zK&+|f?(lj|K1n=mMjAyM~puwCmbdkI#o+esuL$_QCxB0UiR%Z z&?-|qeaY0sL|R5>J0N#Rn}SkS28uoGp@qO>!06 zI@y!{0E`kY)=UyNpctOo4DH8c=QR}Y{1tAWL67@mSTuG&z5_4sQg-LsVR*+I*wI#v z9UWsF!kdtfHvkyB@!}rAWf;|1Cd+4o@&Fjbrmm?eb&&RVoMQ*x9jp-x!rUG6ugZxI z@XOi8z@ox+I6e^gEz)d4Q^YWEuD;8ylobjfS%2JBApU&=O=h^ck{ep1;m%~psM10+ z7_0h6*4_F^+kOt-@yX}&L{=XZEMrc!YYu55Jx%l27#9uMcQ?Z(o+efvg-hs6P z2!YXCYpLeYRZ2*e1H%9Ij5)x&1wou+JU;$XBJsq0)bG%e)v2-U(o*B2K8zk0oM*YS zdx53Vx{p>~V}b=Hb$Wf|<0VnQTGTMy!6yf)%}Y(1P>>eZ4L;`CRmFYdMw{W+caVFN z;*|8k&oe%`a}Ss|(6Q~Yy{4H`At-Y{U%~Ihty8Ndg@50B6erCC3Q`ZD z_{a&BT_~7NaY(ig`T{iErqt0At1!eN2{M*afRjl`OCLW?JEeNVH}I?@tFNY8?vBGo zo+w#&5XcQ-X=&^#E5oTR$_>CZHyfxR1#?+JM-{7-En^vFd&kJZqKYL42r1KGT#g6K zRY&XukbjP_lQ>+3<54U>GyXo*SWe7H9`C@1N+Q^o^ZPHdOC#l#w4hnQt#XgmFE3{h z(uhd&C)EHaQx?>Dz1;a94;mhJ22z&D#oOB!-J1>B@lt8I{4zQ!In^zY8HbAo!wVTZ zVhsAuMjy)pf(uov1I)Vyl;S)cRh4Vzx6g1A^AVJK@F35QVfoiwJ$Hu3;@)>l{(Z8& zh0jrs1hc=@3xNKUMf2%sMD$|T^)8(6;M4X(qAM5)i6G=ip>U=b<7b|1yL6OR9_zq71&|uyIS&cL%LjJLx32mFwTR5tP~I@K(58h$LGGd zgHE_(Gc1^sBjP7rzY3(nT^sH#06(?iRaNA!MTIO!1}t5pO+6a!Nu-4x{k*ZgJxYO| zMk&VzBXf}U(xp+p-k4!(dy*(syOW=u%pM>+ge(}fnY>c=>TLrU|L?4WA34p8PNhpr_Jg} z_=YCIIpWGdep?^(#$j^sLs*}g?iL<~HAj%+SI1CTh_=(x86=$R0i0lyI9SfE8usnm3Xvq}w)b-0{fUkGW|`$rp^ObKX&fx{ z8ITeIXl;Ep9XKsu-~dbXbeTT*3vbA_o*sSuEwESI zuZ~Uajm;R*o&G6YCkFrMuOshN17Edz=Aan5RLS>IDxY>R83ys7yc)EgV}R>HCF;YX z=?cFIO~^t3GZd4gplT1Ok1boM99u2(6a(DkJggJjGLQN1AlURFUd#??Y|epjGf8Av z(DjUjy81Vi(?Z4ITWIKI5IcMcqxkY*T*DT3K}fdAEOKbfNGh)?Y9u&$&l)d5;9y2Y zt-Q0v|Gq!IGThbzE{=JWF`yLP)sw{8$ z83Cc2HmFGgq`uJPP8n*~P4XwAAKvb|@kTJ!?#j4yYOlzvZr_hntOxG0zlc z&}2SpZ`>CzUNkl|gh+*puG|;05JYJ3!r~$w505e&+ZGs}JiwtrMoLP{#nr(}ML|JP zSrlv-2v_QtSiVC8{@CL%9ilRlC zg)*Ean5LMZid_(*K(m4ZS9~-YgNFI_VQh4d{#|6g-Y~cjzQ=8Mi2Z*~J$!8k+N`vb zA$iF3`=d%8+qG*TFThT9Gl{DR>E+vp4Bo+qdDvnQ=*(>!bvBSfa422oN-s+;I1l-s zx5l&F9d7{B8%6Cz3oEH^XJdM zOkCI8ri6K$z-vp1-hZ~|%&-9DsC{7O22xn~%k$*_hSwIUb_+pKmMTdCkt7%#1k@vv zo3qejY8RWmo}TbqGRAuH+-clA2~)^l(bP#GKf+MW(|Wgqc+=s8XW>4(vg44wFTx8n z?BDp(@NilSknXOouB00gKmq}tSqOL~G?#O66_~~dz67KSG_EUUZ^50a)|CIHfR@@x z9M1P~I+pV5f3G|2fUqOhu@>w-9}t6LVBK(6S7b*mk0%`YLdKe9UNi^>Zj3_@G<=#2 zTPw@UXHK(Qj<@!4=rO^xYbcyq>bYHdb{kBsQRTjz?d`?C0x!bz?Pe*_Mu87hkxNFkT z`I)V2%g@ixY!=A#cS#Pt3XGdN^VWXtVjb~NpB_Y)|w0lvM;0zihi&%wSx4NCt)N;raGrnEO1-TQj zSj+_T+joq*6D``*JB6|cn8pcBB8}+C07ZWc#&Otl>(Z=1Rc1IcJRs|20Qf&iXu=K- z2?q^W2mt>p+$F4E^*}=V`{eVfqS&|jb2HjTRe+-xP#g;mmXMm_ zx)I+{O`z=4r`dqCr06u!<$0I_U3ycuqf*<_6WA8#-yJ_pGQ0{`R_LO$!OoQhiSf{U zvGiD}*ii{f%k;`Mpjktt2tC1S829n3#6IZ>-*MexnnEN3?Td?7T2_!Q9nAhf!PmwO zjpw`H?>e%*;cXf*yC1(oj}s57Ux&x}O8XJHpskIzBgGWL5R8nB4nP8%Omk%H$SuKW z7i8s2sT>p`2%qug3<(aNtUoIz77p(^(l`iBY{(XLq9D1IzF?EPkO%0L(n?AbMYCxF zsYH?w9=zqX28J;NNFm5q0L{j}$|oQI(Lbm@5$X|YZzBxU4`JtUe+t+B!y^21Oq?Ro zi6niKve#IiL;m-<{|8hGV7d4Y@_Hm-DsL+n#)l7Z*a_UZqXo)pJkL`=!X>ZH6aevx zvNG|VJ=ct>@$)_;q_}VA{VP$Vogd-m<_2Ac!Z|rPJX&gAHyEwdXjVWM3veh}05{Xf zQt$hdxu-8^10^0bY3}mzFwgNv!%yI2MjJ5L7G${dI^A+`XX!7bYmr~xV6Hvb_M z;d~+aic??NX1bkeJ58g+#XFPg%K-1tkVTf^qz^SpTD;9*?sNP(mrxr_~WYZ&CylnDSAh^%X#5HLU;CN=3zul%6RFh zzp7_oAOoBtP-#Qbbz>l+xhXntA-e*Vg)yjwe%4=45eb9~YtIsax&63dh+B2vJRpEr z1@(U>u`R(4n-Tg6T9rEsf>;_%!Sjt%%={HiG0~R1&7Z7kJH-N=dk|%s!?@{DfUJ=I!#iI9bS0m6`*s|R zH%zw(y+_jB?1h-|w1&cFg#C%Oy9y^)eeYxolh6!#? zPS|q!U4?=&6T`!GA?-j(C!}h#spl7fYADZmF5Dr|kX?%sghm8HOaAWdhR;894|PuN zhO>!2!R0J8X+OE!U=p2z$NWzyPj1gEjlcnrI7vqcE}M?F_Do+01eh!DgOIv#S0oQy zH#jpqwt%+{P6_8hyDQW(XfIwIln|>jCZvH|MU!AfzjL9aAn?&PaZjs0bNcj$8|!=bO#(A*pb7%rfq`FO6N($7SYqtFzBcz02Pi=2N<&HpVX;Jb`$(nu4j>CuA{?)t2g$aInwq5= zU?Cj4ll68?np*@*JLwiPGY0ySkO{VbZw-BGDt0VD z4Mf2cTr73$B~TgAfi6kNd*%v46*~HLcdjd(^CNrC`EyS=lHxuhCA-1T=P+Y4l-bwl z1;QIUZ|84bJbd&h^r8OjiR@;7ixlxt{D4NSSSE_yyEoH>=oPHS;WiyNeLj7%aQkGf zodnx>k-ltZXK8Mt^!O!5^10QuHT8lJXqJZpsu+N@@I&XC*yEM52ltJWaVEI|Q7i(+ z{_tuxv4ImgPE0(bb!l*0dD_VxS+FFjpZUAOHKzmw)O>2FMsxW7vqDa_$l08lE4z#$ zy-2d*v9S-B+uQijcv}dVYo$z+;+G$wVv+*QNe*%Q3!i=9*MV%h_{d+m} z30P66r3^*uYN)Gox4yO_N2idl73Ja`k(#}`56qt#&FbR2A6p^&?`Ey>5gz9k9eU^L zx~hY0PL|C=lxV=sNK}EUNsDY$1_(C}I)@?BWgs{UViVqgQ+|gH@BY0Q~DV|W8 z(|m{O zj{jpz5zl|=Q(A3Ze!gS84JZ_#D-o5HRR7Wq@`_8*5nn!i8jPj{Rzh@23b3p}L%Xl# z4R9@%c(CJLGf2 zvN15k7&lZe1qp+49)*EQpw8r3q?mt=JsA5-0uVI7*|*2>0I3%6q6Gk zzP`~=8rB7LY1=RvEJ6N~3tMpQJ#g#P2n*+oAV9w{^=`Y~qN)L3)HTV=!^0G9qgF8Q z>U#7`4QR*M6^8)6wtVP|40i*antUyEMV8?K=#T>NrOJhdW@q3ArgKzU?tDh)F68Uq z>#HgQr_=}2a94BJrN-WG05;&?DoUrADjX$?^rr-8;Wpm|r!a5%Jw7rL%#q%cC|p-y zxurZVKIBlkHalygas=9O?}0Yy5*KcK&n>tU6yjDpit$cZ5SV3Av^G0|n}JF&28=Y= zB|9wb;8Bl_HGYs6h&P*Wed4w<+wIMm@CxQv&KXO_ro6 zA+noEsof~oPYnSVvPzA*L{yeU?Af(;5+^^{a+kSr7K>e1hI$?yuWr9zzeuF0FwbfNtPR=NVSTC4B z#<7{~;tm=GA7Kbev`V{=+aJQ}2b6lB} zso{2dnDLSivO6Zlj9z*gm?tj;`W6c`=d%@ZF%JMBg=huUN;0pS2cH5uS8$J?joV<+ z<^?!3YsXGdQ8W*$2F59wlAd3E!OD8gB81;H+H>x*+biE&pHCE*6w_AQ{U|NYs(2!i zbaASA^bYT8ypojc)Ylg!p%LD9E@T82yh}NM;YX?PZPmdnftUWb9NeNKj7998ojY~X z?PD;>M|Wob4J#t+Xe-t*kG@++-nd%M zd4`7mKCZ47nnfM9#(7oQnCyPiN@mA?ZlTXskYNf(^IbpSKHh^5)E&DFpt$|Sd#^|F zm}YKtR0sM`OJuWC7JMp{?KUz9m~}(TJK#?)wfoVi7}Ej$4r(#i)K}=;}{d^I874?OAxoNyuZ% zw8+0(KNmE6M?i5Ql;*D*1f71b5uIwsDK6|;)@nJjIV?OBx@)t6YTe?1Oq zUp+_an>VyO5aYgtd*11aFTb;ujx=$+-5adYI8tyQ}jYsYvZ@LzK3@& z4$xZmnJx4uZ)E*kY7tZ_4!O0E!V$Sycth!?oy;Y-reTxYZ#k8gwet1r(}~-jICmC4 zB9oZPQ1CZ2j2JLv`awg8FrACq@{!m@JfzP!OE zJxTPlZx$)V6poOD8`;7PcIXf3Z=qFnewi@j%)g&b{I|~L_cM%@Rnjk_Gf2qwZ+(9K zu<{7+nBZFRZB98mNkgjK(4R_I@0ZDiVB$F2=ERQc4tVMnXQUsP3^(jd<%z2nV;ZRs zvNY7w%j*ma3p!q`_DQRFbY!$NCq8_v|4H0*oqODZS;WEz`YidU<@&k2(LPd4j%MpI zaja=krTVp0Sqdz~ubGOyzWL#<$V(042+_Y)#G+MMd27=bab_w zT-s&K%@)>3E{18+=^A?^B`H0uR{H!pFq3It2fe(YstHaX5nqx6$uM-4`4hM)XsyF& zO+?=(q=2%)YKw4PvwoTelE{mgevr!*3P3BG{q1O6ci`8DfC&Z^w9QZ}wJHiKTk@b_ zLr5L_Fp(b;`pwMOM`9sjdZa@98d3Giz{N-Dy&d;~u{xZxf_g-Hi;evj1H1vn9ezE^Scr zB7F%Pr-V^hc=qMiL*VApWJ8p;sh1Vmg!D>t>}q$QL2XzQ0nQn|q)pd|zDi<> zm8LwMt=wle@o5UO@mItj7m&nJDa;2dGc}Z8oF+@Vn0&@2U5t;IW%r1E7KBX z%{$y0NuYD)v}}(#0ojGi1^Ov9Wx~cm!B)irf-Co>h_m91eSL$lsgm@j#+%q=50>#N zp14P>EiauS;Kn_70i&0AwYs5DPj+SG(@Lg7yvEPDC(o2ljed`LKz>EL#>q4)yL~X5 z$MTWWr_WR3ef9hUnm=f{e>lq~tJ@C`g&eFj!z#D$Sz27I!0DEGRJN)7(2It^L~QV? zL}GpWoN6Z>r5x^gf)5>>Rn1S*Wayz5;4}k~D+0BFjCg^W=gYm`Z5QbD z5_J%ZD}LW882=l4P?y1##a-E7$GztaWTOE0pCZ1nGU^NfrwBNZ1Vkr!*}3`#vCx}% zT9MID%~xxpdL^xZ4Z2%idc>TqN5Pmz3!sC=rKMuIEXf(T5S>`fDz-fJWs2!E45%^DuB%j?Ag z@6h?X71bRPM@W6=o8mu3r&t9^Dux&bPD)K_-yCul&s(|`v9&QN=dUdyqd0lg%djnK zYrX0{-Ia3xPgs&qsw61ZCi1eV8*k-24N_R>pED$&mLsAod9SxRwoi^vM<|vmux`Xs zr76hn<$U)Hr@!HNC&niIzOJjP@MTj`c#vVN_dw1sCnP7 z(sqX!fra-RIM&UmPmX9)h15I+~Q%z#&tF!g(dHgHo*0fXdFwbJy0Lb^Y zU-u=%m@g(#J;)^}(O;`bw<|MKm-#+;v2A>oPt^h+ZGW>UV&E1?MYF46&H^XQj%Y*z zccKSNPssvk<4~Rwg>-B>kkO2lm?(Y07dL?6xWQN=gusk2;tPPCSub}~2^*rDmg9T< zCopXQfJ>Y6(%HFbv_!FnmrDk!jJqC1sEI~AEqj-DGdZUIIKO7;x({z`gms`i~46ttgm9N zL6D~s7VRJpE+c~@t15PA8d(zuYgA;A+=Ph!=*UDKvHNS`&yUHX%^Jw;)_5J{6e?tO z?fFAJdG#~$Cf>-`*bO^uT2RHxD#Q@NbEki3@4(K$swf|yYU5YeR zQ!Hv}SYQ7Ls)HO`vtlW!kG)KkPF%XM$U{Pvu;ShRLU7fGtrc3CTUW!Wa=ukWz1`e@ z_nYs0<<^3ozOh%@eUoD!KL}%K>kMc!y5q2r@&Oz_OGDG4rv!~OK){miibFLO>tQU< z*u0ou2)mCDYlI&9Xes#);9l_%Da*=+1F74AQo&Hp9C_enbMM$vRQ~~BYAw}!!R4Rx zf?~C-;v0LrNU4$0ko=ss$DiOJrtsJAD1!n4jJJ*(Rf)ZBxbKk!fzl%5!zWGdF?62# z&J)OfQD-OTV(uhyO(~oxpZuPOm`b8aEey=8Utan<1PAj0A6MUUE>Jkzi4*ScSGDl) zt{Pl-9lX%to3SRoSt^^&Qu29eLfUb!?%4j$;V=g!jiS4h`uOsc%Dez7w`{3>T_xKrFkm0OWPRr+@<3w&O+ z3ae)v5`H^(?W4yh>al{Hr&muV_a3Ewf5conC0rEfxV3qk9foGT!`kMAoO3_)E-*4Q zz4>KsqcbKStK<=HZ7TIK;8=Vs5PY+(-_~Z7lnSukv=Yft($~?>ig^$oWo9--=^vLF z@xcG~=!vBJLigz8lO@C)`nlSZ)IT&c2uX@o{I(Ai(GUomZLQlRJCfpOQjoNPPC`%x z3AybY2ab@#hBg*$0JN+JH^}7UYS1|%juWW*tR*ah*w`>VmEBTyPZN}6De0K?b+4=? zEMP#eHr`B4mx;iUgO#e}S5x<7A?YO3x=hezpbYj*^spDSzi2HiJ2CZx=co*AgbUhpnj=>;OQ)FkN?D zQ^bli_<<>u3tJ#nnD3f$D2KlX-H;$QDSksae3iX3ysuk8iPQMbQ-_79qZEg~ryc8B=*k$#a zWb+c_b;&eotB=%@#%A^RuMRtwyyP@rQ1m*w)yFo$YbH$Pr(Zvi#p{*V1l{{;Ne|rRJq9c7ykL#W)HN*z0sAdXSX%lo#hYB+cmzWnaB? zj>pkVeywV%bZ|)o0oZDR9yW83qXK^ZNZv;J)}TYYm}0V}vu{R;{(JSY2#^iv35ZRPsR9sp)m#=- zmIAe%=gbs;r7+$dE>YFb?!vZ&t4ZvYgKD&=ap9FK^ph zUMMExR6aSrHJ)6vO83AbV0n3YE$ynmA-t)7yOcv5vVJd>up-6BD$+-5wtfa`Jt)4f zUVP_ror^o=Z{sdpiV{J*Ci|5brb&kfYlM{b^mtt*v(Xcq)9aYA)QM<^5gaN7jIwu- z%gI3H8nQP=RQ)YV4}Ucnx!@|DRdOsUB=-1pDyyg{&`?c$U(0G^Q(MTU?z2-SG@%mY zjnlMYWm>ZLXzv{?r=1+Ss#N}Z=8fr-725!9IvgP~BI6;-wEUNEtsWBnz_~1Ob+YJv zQEuKG^CWXpN8qXP?@`t5x0xJM#CVsR@Hj07lN^ei)paq8g@KuG?kc3%si%?Eh-4WD zbP#U!Teg&%*;Qhm>Y95+>MUjx72MkR{k|wF*XT*Czfy+4h3wpcj;9)3HyO+CoAn#_ zubSQDIAHWd`SD{t<;i1**ZNOx$nvPNKgMPm>MxS3F2bXm8o#z;o%$hTtQ^-xpfpZ5 zB3*8uNVs`#KK4c)ubqORsPg;<9YfpK@Z1@03{>0RL@ys7isLxl({*uFAUmY!*(o#9 zxz+g0D9L?Arz~!rf4a;kCj9*uiRZ_Uo-xZA4~x5JY~~E}RswksOvmIkyPFz2-5}_e zd(Kw4p{PW4vc9+1G0SGnOy8&s*FzEi-sI{VS1EqyHHD7>Q9qtvC+9KU2prCh7Phd{ z6S^I@iV+hFT*Z5#sG_7=VV|bRN5{k?@tSt>`jX`Zo{ROem04}Hx%f6tBS)!4_J^X@ z5TvOt8>cvv7sz4Q4eepkd1%J*q_zgH+qualJ^)VRP0XO)m)D&r(veo3bjY1-Hf`x) zuTA%NYxx|T)UAZtx*u5;&HQ9I>F$d;IATlVhp+6^I+$Hh^hfj|>~Dw`E{g_Y!@U#3 zruYlFMR&+b8aU#;$SAv`DOx};<76-FK+fN#cKx6)?Pl9LpScDaZUSf*)MQg||_Ca`#-dK8y!cAOYQ-`r<)017F zsX#xFEw(5|7?Orjr%)M*SwPSD7YET7DMc1}K^c*v0AGH}xlw z$h?z zGtbDQJ)+;j`t^FTrUpIZg?py`O71#e#YEn}k2@4dpf$f3-Gl2Mvs#~lIr!EUWBb^r z{#p`S%SH~g$w=!2bJZj~Rj~~Bk@xAUdASj5QgDHsFDchGyI1n#n!@ol2(X@1D^~gD z@qIZta$m%#;5<1~Kk+(F11Vn;o4d<124*#&oKnkCchH?}%hH67?bT%7H5L{BVVTs| z`5qKKV>nOAn3BvtEOa#AKrwftJ}C-*W8GqY)qyVIM}PkbLmo+}zDBcZk5NVATc301 zoO!y|oILY~2FddOfY=%lHe(|tzlTcphuc`Cy?yg0X;n{al`6Uyu-95-R*hQ>Z3?6l zuS2br$6f8TmVS|kejLan0f=kAZ(@CAF4b!0F_lo#krbKYnt){L`W(t;SN)PykzB;i zv+GDJumqUbW-@a~T3$X1U|uS#ELpCG1-N-5o<}RKCqa#GXrGWuos2BDb{qjUw-#>b zYh~AY_Q<@%3S9TaF^A3H`V?`wzf~%fC4-y9>>Hpb$HFe|Q?=Mk4WSu(M9$)?_!l#H zvR6k;EZ_Yaify7>N}gMH^^%uxzwjB)IPoC)a$STCmrtYB$U9O0o8P#-!slnaE*>FN zaG{WmEls)<=i|BNM)0%sRvnXGUzGZfDojDWS5#}J$c5qusn;*boxGPAXwmlVvEuiz znQ;jUhk+i&Ji<}`Ii)Bn-}oQSm!xye-{vlvd|{7M{(54m+IQyNSlr{rWj6A-8_`iY z+2*s2a{dM<#C3aK8FJ8M^L;7ik{*u==1$efJ!C3ri#3vr{ZWj|xJ8Uz?8v3=W_xns z_3}wNjEc_U6yxRYuM-Zd4r_cZVRvaOM0`)wDEZD_c}KXU6SGAEz2M(Cl&xjMs~aON z&)=uD{+YTyqlrn<$Yf`D)2vl=fns^V*FbT|tPNK-D=zyD#-oi+9uuRPxBA>vF?fC$ zzqp6diIrz~R<0|`v>-62xq5^^$B5MGfaf6Xqk;pm7ED(r6$b}7Zcpls21Z$n7&SQw zWSNc3)8@>LrDN=7-^m|%+SJ+u=d{JRR7^0Frwlll~rUlh(dObtYka(u}8=r$Chy% zDhV0c9D9!(dpk}k30cR=&K}2J2gi6m`hI`E-_!T`_xZ==I+t9!uJ`+W-}mc&jTJj> z_~wPwBGFO-UEjg+wbxQSo5D`R$>M%{Y`V8P$ew3gWC`)YLUdr`)8F+!4F>JR32=;r z_oHUVSWX;u2Yl9LQr(Al*0Xg79!D36KJ0|nJ<(-c47V-YEAreB%?V=}Zd`es>sC z`(lx1J=n0hLr2@a!TiQ{wfnr?nh(lI78FA>aab-P;Ux!yzxqjrl5nAok%K>m9WS`( zEVq*-8s#RDk#$x(jrB`BZ3Ocg>lc23+HQDqHzThlqKKkX7XkPJ3|MSX#Q_CpNCsH- zHiJKsMV;&K&-R{AtyTtwelo~m0_e8-EH5f7xO*L?@7V*oa-fJ-#{|`syv`wuc=Qtu zP(fHOR;fp)+GovNhekR<7yeLZ0$-eyNv>2%vHYic=AC1%+KSk#|JmJvWMBn=Y-X{lFyN_-@_4CdR;~<^^TD zr}blK1m^UlYZg4lT$N(vgG z;T13XWdH|Jgu!7!57fl&SdYGl3iovahO^nz9~7i2fIpDyQ@qu|X^ z$R!syCn_wRw-8GAYrE9of4sm4HFnsYO|5-Zt%TXkAJsO=YzsC1Z0nyqGiy#WOY%4# z-C*Bq>-U4q-Dki(SBK*&vm5P1GMtE0!aj3*$iW#3S>TkkP?-R@A&YI2gQq9*+9RVS zYN+}p=*##}5$z?lvlz1WAzO{mVuP708@o4I*87&K)wI_Z_N>pg5$+k=ISeJwpjMgu zx)LQ6BA6-f%;)wL(L(DB)n=g!9vZ6A(RaY^HQNFH`)ZQd<~JO>A!4wN=juzUsUAJt zrj7c%r-xGqI5%&d6#kN1i<0Nfq^t5sKXV$adHFe&}oG z2pOgi<%M#OHW%szd5bDJf2jzeP{)JghpkjudjXHPbsOldl9kzW_UX9a?ee0N=t4;qhaQ7K{ss5zcMJ_bI`h$>R+Po;hH68C>D-MQGt z>C12BbL=}X<9eCcUSxy%8^@p38R~#rfWsTWaipYllHVl(;miFU8n2+DGEl9A%-YxKzua8&^O!cM=y6lxbsD2p$u1F~N0boh{?m1%KG zN(w;%{Ncy1V+yV?$Xk_JMp_D7mZ z78QX+UQ-`F9JU|RJ;Ppl+5r-%dORzaOUoivR6&H5I3bL2xTEe$b1DO3#7-SJzLT-a zY(Bh0<|~u#p#O4)>TR(s-6_dT3n7%NighH4^2%Y_y*hQ%cZ7Opm%HKh>7B&2*PGjd zRNp;{rn#Q|_8X0*RnK6D2V?clVM3Mv8UuIx2VJ?WR%vEHA1Uz(XK`ZZ2~8~W#N=iN zM=pIn>}(hqFHHNwXRMZ18v8n&i)SonF|EisE`}mDA^!Jkjy^9cvWZq~8uMyH(RzbS zGYJ-RynTU596o{(lkg72F2dBeM)}20+>?e%ID3zO3g+H#Qg=H_Zn-97+ z8i+LOmE2-tV_X+Il<%IDGz|I41&=qKZnnV+w!e8(HXJH+8RoWdn-0>^OC4UcBdZ($Pvff`=E6` zon0ILy3<7l`8Z#i82+{(ZEC@`pp5VDE)I;_?YN)cJhjCYF4;N?Kb~L70+L$oA?skq zbuA3&t8lePVNcfvnt3avT?r|scAv3hZ@kk~ccejcKCg~KyRHjLkn8RDeAlu;@Q%JOOaYgAO7c&d<*jYFvg}6ajHgvw5_~ zcoDNc^d7)AdS_T1`f@5C&=UrHbgY9YC~P8~94kx7q5$>=^yBJA7Fpb*y4|@LwIV>0 z0G8azl zK8a_?5GJu-I==dB=%v~*68xmhlR&SY;@37Bez&uibSrA>Y=`6aD^H$`*%Ie)XqQ_8 zgJvA?_C9U>0i7!>?#rbz*$PaYy4{dd*>w^cs;1$pR8+B;!}U;1OBA&5kdMEGEGl_$ zYND{_i$rx}`qvtu?b@R{2vw->94?$`mBbS#mUJ%K^%?1g{6u5j|Jg{OPTu$|`X#Ki z85xqP)hVeM?xGVqjqbA#N8+*>)*W?qzqWzBj?4DZTk#R8PxF(zrK=f&CU^}zugzX()jeAK7aZ=JJH-XJQ>?(CkGJvx%v#Yoztpq$Yk^# z$BFVGsC=gv@x8^U>1PV-c%^8NltEne6J1$u;UTD{E8|(Vu}97WQ|LEH$UJI&YstO@ zy0eUjU)ydgWvRb)&#=s)q$7O=8C!v7VB@nw|*}o^;g8&lZd7Z=q$z-#E$R!M zC-GFWoI-93=|Pc_8pvLid@cVuW8ZrkK}?#Ij~mxU-^5J%g=Y;{e~GHTVN$#ITZ8ug({oqN^6 z9ndDrH#3jqK%kVXehVgNLiA2)dUMb665qa*ZF~NZ>(TQs!l^%c?$oh`fg6$yx!21I z1!_=ygoZ7;m(u!rRb+~NK&ndBR4}PQs7UTs9~&j!Yi#Fe)!IT`=B|@!VwJ6L2=nPW zXtJ3ukNbA~!~w(c=?=AC1W`MB*qQpncx*Os_0c=sVqm|6vhm$JB9zPDk;s*$nQUNP z@5}d*AV$wrPi1y)_~6SjN8%_ViiP;4q@Dsl02sUSf?bKRt8W$PS@_nLin`)(m4 zJV|uaE_cTRAZO0BuyhI|OE%(krz_Dwf=8-DS-banxQK))F|&!li6-4zx@uvRfoF{s@;xFxRLi^;9hI4^+RDE&2qM{ zc9O=$Z!o7NL8iggXG)bk_+4*eLkGJ&SI^1Z5;`@Sxg0KWJ|#A3_Q&8C{Y9w4={dzU z!p@Y((Xk~gUGDWW;EKK$-Wbp3U4tuG(&afSdlFCX&J`H?UtJ_sXvbAT_59Oo<_&LF zODKk1Kiy_o!|wIxyU(d@EDtP)H3$l$oCrPHjIcgMTs03y8(F(xJG|h&VQ?iHvWaU# z<$*X>C9}Jiq}D8r9oM$f7Fx=p<uh_GhF7@Ie^_{r?g5`iUBUn7<8!Zq=pNX0 z(4ShKC=c8_Pr%R+;I6ZLm*)N%reE*PRueBs6?3&B$*Jd7T9f27#N_YpUm9$9a-la+px%S@VD``ik7hS{fRd-QFvG;Sotq8FYE?i_uA4j!(VBIs7EOi?U-m8^F z&qE}@{X$u&q;~ms{jsY0Isdit&vVL+YZvg#E*1WrVUY_IJo7yOOOH;wdfBhb1%1+C z4}P^&r)mt-b(;#H|2CeIs!%S>-3Hmw$FM1LhS0Iz|Hj$kav%);lomPl#OeZJT+X`J;x5?za69d$gfB%lHS0Q6O5PGDelFyOqEO+_S4!? z2RJ&tN|5T_Ulq#N?U3!My$7M{Gz%*p=b&yY?P&38)=EEjP%V(?($6m!eD);$3ol%|YA&iv7JQ)!|?tMP357O0fV1Cd515pvz6N(g_Ho*FR6E+`8S zZ$%Kn>ps8P)+&a_o>H?OhI_6UOpr4juN8xxmlF3)?R71tG8Q*rxGNvt4nAt}GPz9Sb&83~` zPhO6aP+k)|GpKz>C7k)4edXJ|e9pTFB(^^$?#4>~d#cXp5h1-7SsHtI!>8EM+(3Vj z@S}rUER20in*lyPzJpjRJk?cSHHDsT-K`5#>Pch|O@AfjuI}C>Gc4m5)3aR%JCR3i z&(J&>%5;N5efFmOQE=!W*qYk(yQXV3YK|{1E&+%0g6WvWLmp6+DfC3F4GHi`6!96V zbq0Y`eKx3yDnevogTh6b97@D|zS`!v!#=8Y4ezasvImhxvntgQ=%XyiY9Ut4y3xbK zE;Y-QrNU#QZzpoz2Cg2wa(c`G9do(bts=2f$=U|9+6EjAB#yS>JbvX-k9nT;nSC*N z!9OSp>JeOO~2`=dI$`mBc@!5+H+ad2z#LXp_i62!ft9x5{N`n3$rb=p}7EwJe= z50RIibiXbV$lXg4xb*9JuBC&H+z`7ZtV3tvox5dDE0-|;YM!E;R?OF-I1fW-4+U8B zLG(G-sDTH0MetT>M&dKtINhZrYZjf6bx3HX{Y&8ol^-EBlg!3t>bSM^=2h{HI!KEh zm5RE}%2nFNML14+jZ;N;S37~>DtAwFJgp_yI84d^LJ30+ku4wuJgQBY?9HTJ6fcDY z-S=4%J9Cg$52EOAiM>JliBl30PFZE!S97uGf4l(7=5r!`eJf1xpJqm=UJ!QS61=Z< zOKd}{Z(URTC3Wq#$mq9Xg|OOZB=fK{9(z|KYKwgs0txc7TLp^(jFK~?U60BBKBwSU zNQ93jt?&r0kazCE6uW%AeKzLsb+T%cBW^4|VG4{IA>!n-h^dK}{50XKX?Z7 zR8V#e)VeBpiCunh7$D|xoGy@=VEk$gJOpA=Ut6p-bny&E%-j&)y6+|Kr2Z^c7A}0) zVfDt=uWpQ8$XF+w430S&*>JBricMx`=RSIPPpV;=qrLXbIiDnFL@|O#_}rMbNqqyL z)|xz7oAmuq|JgkaNx*+=m0K4N@#UR8Epgc}9V#{5fQY$fR)Hdo0_O508hAxoT|7rW zhQ-zWBjYJ!IPZPG^0;38A8Osdr1whTB3It<`%g5Io(oAO%Wq~DmIQ)xTw^`eW0!v& z$?lW?JY7sevN{7c#q}@RAkSS{G|we`BD94V%+#|8e_no97SiUeul4IP==FLYn})3g ziA`J{8(_(RTW{RhXJ@Ow%n1zdVKJ}Yyn*>0RGg#{*^byq#$4#3L|A>jbf%8Xvy3sh z!yOUUXr|dG9|kn?iN=*DbnW^!g3dd8hkdiF7Z&{4Mw=`mo41XTue9`w91E{2g*F`? zr8GtulH`<>Q`~m!Uk37DOwUfE$rRcKUi2q?rxu9Qd6gcjyuVc5xD8FTpbmSM{{~3} z`gqL!+%>RGYK*oz<+u^h^6Vxg%NaX>Vqc5cD*FdABg|_l95f?9QKLY66>dRJ(eq$% zpUJQaZ$h|sVpDk)bDC#o*S(b8E>EtMoaCo%P@zl}r=liNp?fe86Di@EayoIK9{H}3 z2WRA1a=N_;FA{@mILgSZ)Rbm^kp?xt8xVP%(?|9?aAa0ij^lwP9L3LX98jf^G@@_v zxlTe{7N==W!+3qA)$G*YDP4X*8(}nvA(+xxjoi!LacuZyNwABlpit`A?Lzn|#m$e! zU8l`Sd?Di90Zu8GfLt-sO(lDZIgK=3{yfbfH0lqzxA1^-ip)6fXg{IWzU})1vheEV zpA~Dx##HMJHys8Q#uN>KfC~)osm7(q^qrw3`{DU)wX?jszfJW%83XmLx)Glu z`WK8_HbZ75Sq2HHnyHPZKA$*;ngR$tf+EtNAvc^&feQi{5KfZb!?T%+(6$FXUGD_0 zaNkGJ$MIm_2`~J32su5;xStzhBah=g2S;&PnL5C!$N{Z{qqc&Bo zZka!%UvNW+y67h}Y#mI;$hvna=%FCHV!d}z!$u&l-EhP+%MxWEudnLPcuryl5wBG8 z&07Nsu*OzDal2(^^>j*y?S6jL|fV)LE=RpP3@z(@&bCj26!2d}Ly~Ts~SZ zWFRekeaXl;8ztgl@3Y+pmR*ePgh=@8Hs66^e=?=|34#Q7;C3yi+&7uD5nlNQC{tax zeh)+C7?i=Msb8<4lnJFPDR2+>DT&*cuTbdN=*70Q+@-yKE3@G371{g^?JvNJd;3Dr zXnai8WkxmHIZ1TSM7Xtk{fYbI*?SxIFM zJoRBE(A{Cm5?Z?<@Gz#Siw7N3D-HzZ+{F zjay-7?IKl>OrvHM-`>T%BG!~vN-UWoQ=ndHa@AZsI}P<}#)vuV@98>{d@yeFZd9tq zi_){MVe<~uTZ75EMlzQrRY%;j+P8>}3&lTCnRvIecih&2T3}F-_a8oJ9zZtjeYM`u zsfXCO1`a56rjRSpUuydNaEhH9esWmq8>ky^oL!zNqW0m|2CPrrtz>^>E{6rPyAT|c z%D?Sd=voRo<${)TN;A%UZ3?Qt+0zL8wfWpqM`%z~QG_pbD+zg&Rglg%yc#2GS2N}b zpw~E$P={@60MR4#@>Kc*b-i=h`nN^C{0Nd=7jL7-0FJ5aqq*m)C!FmwgGq%E>*ArH ztO)`wzV+XkZSZ_%WAKV|`-7DHFSas-A|l3)PWX2J?e3|!1=O5}{vgpmT>rqab3v8g z6OYei3hNg>^DMY%0Z2-y@q56@%EG1Xhhu~}<>#NlJ`=(IK;)%1;C%*!pS+^J4A|Jv z-nij9wul(T+><)0dY!(pv-nO@h~8i=i$YVuCehsdF0EDn+fOp(xK&SFN`-oN-q?rm z4!8}v7AAE!o6ezbOeG-Ju*@*he}}wL&Wj5M{}7!m)AZP8zszmeML8grzVvgslkq2s z0ulfHe;S_k>(y5yeR*S2n!n_2`AH*d=kx<&Kg}KJ1qI8#qE5M!eTU{ca~^P03Er1$ z9&oBVfum0l8Lt-SVJunr%HHoi>|i0_DDilzsCkCIzpSpbz1yRyjC&vSw*L$C`6yj3 z&DoodN4p2>Cx(t$YFY6X+aJB6MNZ8pwWw<%nw_eJOm+LKadu7S#Mn5wRP;c>ezy}u z^@DHVuSBwNi|o4$<(zgGgzp!&?OVU{L|8>Ev&83Q)cH9+9L+}iZqzX1bj{M~;gGnRgyR>rLZyl4={J3=)^PQfW4dG3hJA0z6l6q56@snUSsg3L z6(XOWyry5F$$R_oK9)>k)=t#4WK;yx9h@_rr9-y=`Ww#LTQ-a`@z-8sOgU{-(x8 zS3frmG0mc_Q6o#PqZ!o5@}1FX*3)DM;@Pm*wD49F_=`$U37tS;P_b_NPAe7Q{sy&i zwjXm*q4G?84Bh73_5^S-IuGT8kwhck&aw0JEC@5Qp64@ANgkIqZwB$&1j~K^^ve4% z2O{c7x;H;8{!BtTLBJ6&`X#4I^5-!B@0S-q@XP;4(*B~A;-~-PP;!Nbo*?}{u~MHM zNv$}jt{Bqv4i7KFe-Fc#hT*T4fe_EhOMz->PnRKz4P9nU7}x8XuU^#w>3MO0wK@>C zFclUBgm14v?i|!DB9bv2Qb&S5>1S?#j(_7LnsTWJLdIl`u5oGc8IQx5NlxeuaJz3) z_>hkUd&dgxhmqQdJZVQJVYaqL{Yj@t$l3$iRs>w$v@<_ZuA_-CzA2D`Z&us2W$9j`wtzstD{Uhpu_o`{Ai&yky~a2L=2DB-$}`EuRP!)lizI^9GstG7>Ql zvvyzU`2CYKlOM^t>xg8n>SwVvmeyvP|>Gzf0U}_`IxLw>Z>smSh|TnuFj5UtY5~<&dWQ=f2tb)fK}nek|HX6y+-Z4H$(=V{ln6VX(YdeyLC5h5D3yP%k+&f~jpbeVvt#4$ zd{bRPPpBCHV3NnUakLAL;Jvoj6K#i60&^MC*W1=H+(T@ zjL-P(oYOx~dwL$LZ|doSTZ7*!gh|q%#4FOMuRW~94p;Hp>Q#5n<1kqx1zKd(44FdG z2mq9dw}PUgZ_LJ}q~yfLYPYtvKHwT)m+(%HF?}MHY%*Vhkz=>!D%oU?(^Id&2TqKZ z+os<6` z>d`~0Fg)m7uOVd3vs4sh{`CSHkPxNrGcxSa(Ihn~a*{KpH9nlDeoEhN?8#5UTei&%h29d(W{ z3;k4Sq4@A#Q*RqwzwEl@+4VD3VUEoQqoO9YA~a<`Y~V0D@$>mD1SpEc?P;*J>969@ zZAZZ9!`4+bK-;S;&%G6L)^yyzv%jP1zSGmWihFblyvwXAQpxv?`lcjp1LjEXij;KA z$|H_vS+!z4{KCTf(_VOA`bYH$1YHB!rAvm#x8{T7&r?6f36bH9B0gs4 z5B4O|`QHgu2m~C?g^Brp&+xy^NMQB(;oRR(5%}8xR44rW{T@+L{bNS>;yQ`3aPBv_ zb74`D(s>U2xryxF?(VDp%XC}2R4g~>=oa8hB7hwwe%vh*@F=SWY&^rbH6{FhrXEug zV^ar?S0jZ)jtgzeqBv}_DZ-R@*A|&I6_mZkP(3S zTH*e}Lp!@CxeZ!-K4+V^jGHH?#U&pD`-NFqHQU~wttC8{}<0hQl}RkqT5R(vm=Q4E1mmKmDnWmC{O+Od#fO|U!*xt?;8XNdXZ?tec#{V zUz>sWf}+NE?CtFbfy65y=NyPX z^}QbLD>-9+4?jN{T-Vls(4q^C8mGyTB8(`gs>UOm^vtAZbep$n9mi^b@*4CCQ49Vi z3i{`fU7%-`N}dYx{p)QdCe;BfRIwf7BEW(DYsLiJS`>^f{sDFDrC;p7`3&^Xm#CPl zsG2tVr%v}pjGO(uzfcjt^dcHB_GK_mM^ftQQBKFrW?wJzN{m)oM*{O?Q@cVZUl1_a zTAJp%eN9hRwxDBm)cF{6NAe({jXUaB(UamwEfQCZoDj{_*>Hvpd+hREsL!kNC3k?(VoYfup zao4l*!jS8GUPAf-+W9MYgZJ8MCv_nwvL2Lrj|6O~eH_lb^woFAL6L=74#hXPX+TuY znZ0$RJVQmNYo=outxLN{<@Vq)yHbY4%$8_ZIDrL&3Y`$X-hqK8L^2PYow8sC0%bS` zo;Z~zRp>7_o=VTt4fySZ8A9v$G;7z>$!Q^B#Mdfc z3tlDQPrUE;!o(0%i!0~Y7~F4p)cWg4Zj7g3`P&$s=URU!jA?io^;mY~WuYrbG;PX0 zCU@Ja^o-H8>i3$Hzt40QbuAm4ki`$|$3oBIX)dVPT`Aj~LdL$`FQm+1d3|6BdA{ zW>Z_^vVt4Oy@>;Bk+lav)(rCPxzUjKhBP3c0E};OXjA7r`2ib*8?6I<8 zF@W$!(kPn%v8g*5zn5zl93&74pB3X8g=P$FlJ6Nm6@~@Sq^FgPM-yUWl8VBw8?Q7C z0bx#(C{O|tJ^QZpC8xwJ^IG|fIm4-6*;%hWDDYm?{!Tt4xo2jsdRi6}4bBy+u(M%g zk(hTkyV8hz_5o$+gij;|1!&K?4?LeI>r0GnYfh0BNUmsqn*D}>VO8^Mdp3CUd#T6f zCLa@eARmYRIC;9U%f73!fykl{tv{D`e*X{JrklH@R9?+#w0dNKS`-^Q7g zWuq|H@L4EN>XV$agJl+NkJYMot3uRno&~>1NgIHUL+ghOTRJgXA9G#L|-sJFZ~6-veZYPY%f8{l7sZU1Oq}f%sEjt&c|c)+THJ zx0|2#m&A9D4_ zNFHOL`ucgd8BYcLmJcArnlh0PV~TL7&g9jJlliu4`>~6T6dUS0hXyAJWYY5X-9`2` zMqgvfHJwouq4=djYv%Hu&i;3a>aA}ZL?zRYg@yPYgx3_KRs#HYV3NblMHUSX)IoC8 zBAlpA4VY_>0M%mg1CjPTnTaPFOS2~ZhmeB#W4^(rz3BZl$&6ans%1aJ_qAs{Qa;~7 zy)~Te`v_0XK?PXl5kn*{lLVTjAEk#0O zz%{asw%S$7Jhz-ll>bc5hh% z1pPAm@n`Xcz}n`LJs4cqRMIg%wp1|1Eek(6rv0mP4G4gNe)|7vhrD-YLAllfqukMcmOj%zKNCjd z+WVsWh@gd>%&Mi({8+xOb*BiVFP6~xOWfjd8@<(UK zP9q>_M_umShP*rzf(TcQdh5d0Y9&?ir&lrN4lB?lcSKIeM+s0eY6OGmtS*h?QX$uU zZ93dUtce;s+c*m|j*R0{AlGuuG5l3$z>}iBW_V^x-Bh&uJ;W*%=Ha2+U~Hdz^qs2N zjCjJ+@;m86PxhP!OVj*E?~pg4^8Em@M zc!I~p#Ox&T*9VN2d})<;VgNXr$0sLfJlDE-I4JsmH{)c$W_%Xqs`}Sv{PQR+tCT^b zZ%@+ShxwriFekOb*f{=ZIGh;+0lN}jDWLBh1R{AvQXla1V+k23q`_QE))<`X%9Y(b zhI%Tzf7&^%Q|qiBvp>GM*bTrQfE%E!A8bDfZkx9j$W=UPo+;0VV?ZWNgxOZ+{+oCni^rGw%l; zbU$2asGA~*yl`}e@Rb_e3um5eBU2d8!_vh0rguD2_x9g#TI+Tr#>Rg4O&%P3^-O;C z73O$w46Nw|I7=DCWL~ETgJ@<#e0)|5VfJcUs+P|$jpgJg%~%y0TCs)Yy>s82yAD0s zpzqp=lBmPxKRo$<>$c+}Mhh>#pTpG=3QbAP5kwYMCXNl%1wFrO+@KEE+c<4`hI}&! z9um!1=(7Pu3EL--$@HK%6Pdh=K+vbaJ=RxW2Xi*oM;7zWJ~H)J^^;!~J0I!m-a^W9v&4yd)3B!=COJI9Q|%wRN#* ztAvlu6-^7LO1K!6o*b!xHGJims68j1muMXv@jKSy_Ux$^I1v`nM*PCS1DDtt-}dwR z!6RS|=-obc+INEY!q4BiCvyLsytJK#{Jb>TU$^o9Pf) zOF!T*ymEonFTiB(!gSA5L^+AB2U)?0YgJS9Uk1e9$|zqjfa>?|!g$ezG92B$ZDVB~ z`RhK%x8mXxift^i_;0k(-oBafIVnVjMNOIf(4l$g8v3E{AOjVbqulVfH5U~F+S_BgY zLSB4er~*q*It_WN)l5L+^W5B9_U~;?Pi!)ZA;!ZzMW;VB z(D>k5sM}E!LmP+S&E%rqw|CZfN(b;51V+irQM+pJ8HiLmj3f@Pd{m0Z%!gMsa zu&LvZwHUD>Sx#>DfuF~MSPv)@JwQH$#2>b9?0bJ1eJ}bQHI*}t4X*Z8OIn{o%NkZ{ z($Lay;|mx99}zJOz*5y2v0X|Nrv+ZdeuQbq(dKh@ zY3I8}I?mtr#MnCtU0?sD*?F9|bWX~mY=5_OkaLW4d2Mm-@0Ko(8#ta7_{hOOPu94} z3qL<2K&?6XmPn+_W*@hcX5&tPP94>fD%2)_%JFp~4`PtZ`Fwkg%Y)iEP2JKv?Zz{w z#DK~lF^%(e0>s!7YU}Z1dfW9`oLCD@nq`a|*TdcHQu^WtV$ZdhthFtrf1K^~>q0$N zKj52wBZ|QT8wl-=;?HQf*jE4Csd^}|9dU)h+!MrAVrFc?f@JqJOPA^$1 zW@uR)fkSDEpB|sxr)PCk-RxhQrv~pAK^R7LbG^Dfi(U93*GK4BN5K9EfDe@e@@fMY9=Mv@_wf~gC(_;!hB00wbReY$J$QRZ}?|goI=ak zFNz7;5gK44`_(P`LeKePQW@;TmJd%3S|wL{x7;Sp`$cFA4yIwYYWhiBH94GO_YKUx z#3zP(+E?M4BG#r#&yJTjpitw|eMRJpm((cWzf@iSB4M3?&F116&Fs1bkm9fIkW_9> zOHjItEO)dsKrwr$2c5&9AjBIplqqPgJBD_GYHj zB7p*Uq<4q;AF<+R>pM~byGf6C?)+6IfhgCHz|3n++SU1gbk9HEje7%}7%^DK^?%N2 zT|aP?y$p6M#V5x%^B1lHD-Uk^L8F}rk&%UYk;vt5hwK~QDt5eZf%*P(WH(!r_=H{9 zzkwbvpe&pMs>Ft$g*V%SUFwu15r@NI=NuiT6AOl|NT|9IszL-dC{7#1HQpe-98w-i zPE|1*VdGF|dV)4?@Lzu2%`BFqmETjiuNgdgDW8TWDWhd#{^$swrla5PSrVCy{oFcR zltvyk=|PNrJ{rt6AZc*9nghWul{}L7rMuWp5}_VUwL1I|bJiF!XsRt05E29~=_T_+pk&tuVc1 ztJJ}%xjyt16jc$6?j}P9j=vKmjh6$#8%$e%L@ibfuwQ_4K1s z=L9M%xVb1a^ai|~6~Y*ky@G1%39*{id*QZ+o8!e);8!Zu+{SiHH}~Vy8rbB; zxLCCPrGb=oYHaCXDV>K0n`+2B5`LZzZvpSjVeVZ`IxX-H%El)J9 zt-WjAjk{g#PPL7vi;e3|93#B57fN>CL6M%TeRtI`zk^JJIH#3D+}#?T>L6F_>^>Ri z7hhc(L?A{;8Z5wK!l;0sB(zUAnLyiIdkQWMuKf#BXN(cwV3P8{k?Xmhp776(=Qog) zl*9P=;J@11Pyk^!*fRV<{`ZNs*9AJewRLgpKPGWyuIGfwZj-KBZ2k1tREnsJqYMMC z+r0hMzNfFAb95WyO(Z-lGCiLjIP!jY!)zN(RK~pm(HaRWxW|->h0TdhZpnv>nM#Mhzo`*+KZKt#4 zE=Zr~JQrtauNO=4zAsLu`uMkg*v!&?6PfNf>O@$2lkq*~=T)5B!04$tDJ9MZViuZv^vBGE7`yC~$t z?#gIu40!w05bV79ep1q3F#*|#Uc5*e%yo}nRb$~AJrn9O(x2w#R^RSi7H3#gp2KJ| z5Nv7djJ(a)%mepCKZKdMtDZ;CiFgT^~SJ^d2Rzb?FeDDW`#Hg5Qv zdW@>U=k2kxEoG)B+S$rodvs6bdbwg~XoRN2^8(>T*f4$baSjEj zhEu>G*#_1-B7*>G{>dp!2n?@pHGF0BD#x5!i-w7*q+s)!>?N`X-$`=XLtH$PV7jo# z27}STVaeZ+c#8L?rN6l6xr8HOPhU>eI;gAIWIVHex^Q}X^5*Y)BHMKjkuZe-!H4C- z7;4s%F8Jc7KTn&VS^!Zye*Z1)e+={efK<60HK#U#zlt<5sf6|UDfPba%KM+9PR9mR z?aTLA{1p(LWWb)^*GIF@unp?wqD|4!hr%j}<6Cs0`tkX1uSo`7((|Z%JEm@(4er_4 zA<0QUc1kb8mA>kUP#wwH6JyrUm(5K()(ix^KbkVE3-%T~>WJ>%ArQyu>MRADx)IAI zlgU!AH2UjM2A}1e?>5#yDuYLQ^H9xfl;IJ+ZB$|JHH|vJvd>8$*c2Imy#TkfWdbrO z-s17nY;=p??mgfXGfk4k+k3fuWaexiSlE3K7W#d_Zy~YB)K`&!*IiCMx@wG6(N$T0 zQxl;iP8dubMGHZPD*bM5W{p01wKYV}OLAb;O>PVWVuhpB zZM&n5P+*(@{!uC&9i`o`EYxzbDn+@)bHji+C$#5T)oUT+%whF44}Iy;o0&so3WU9F#y0K#7t)YkD^L{I?zp6+6EQYkjfORR zuhu4`P>z6K>&IY@Hw>WSJl3V4n=v@&Jx(T3H{)Tkqo&zQq!+n<<&Bc+-JdJf zfP9Fwefd&zcDCP4)xq^0Kd2L`uAPO&-kFDuW_&ofjNF~IVhAtZ^9#3|I*tnhc5_ya zG0AaxRZNjS#8|K9D#)0~eaI#5$>Ms`@o(wmAO%!GE~*2BGVz%WP)&jDGGD)K#-_II zv{s|2JVjWCbB!dGJP@0T3Tk2A&%N-N|U zUTFOaTxGCynPqarIwtWrCboh{Sxjj=E&&4-Wfb8Q6EN1t&C;?kEd=R+f;BxSwH7=w zRXNU%L)UhQPqv5^F5s^l^TaZ=>1p(;>De3M$<6aAVSnluL`zwP_ z*Yn5wyvxab&wI`>$M}t$b51ljgm%BH)9+{*IVkJ$QAOr(VjF`5mFp4@C#mC_+9Qr1 z*L?$RX1;_#P=!CoKh?y|74P!WtFtTZ*KhvZb=+RRJW#)$qQs9YPG8E)YGhA*)K8^YMn zqX;TF(hlnP%!MvRhZylju-#vbUYXjzFhadgaNJ)%$}6rro~bC`TDrLdlqzr%i2lE`FQ2oRBOOe>pZ~7;fgh zdW|G7jpnXt&C1FeD=n@3#m}GNn27pvDza|Nmva}gMVSo@GCUoVf=V00 zrNz55`$OwL&n1t}O3TlZzvL$%udbCTyyh!GdjY-tl>k#u~m$jisg3i68 z^Lxzrd&DQ4LWUTR_=-AI*SS+KWb-7q$h$C=84)GaO*$bGp&@maa%L$(s()tx%!Xf{F$s%hu6CqXeSy{ z#rR{DRPzooN{sPNDUPTXnb3i+TLTrMukqz_^4U9zYKn&EH)Z8~Gsa>Tqc+u@72c)T zxtt*Uq*eW`D<Z{S+VE`C;O7#I zs&nU9m|u*Gj?~%sN^ab09sF(hZgjG=@^$50Jkx@z+mHNidc}X|lJO==$cYtK4H9bX z;N#y=C6CSR7jK(U>Y9oCN_Ho`s;z@bb9y$FIxa3LX-`FE`|0b;dSgL>KZmEo zSM!A$+v*~IH>=p#+9am-#OxvRF>XA4=J;tDHTmA5bjK5 zCC%Wz+h54Yj}6GA&b%R1zSCf~kNam+LTISA&7#lUMou+41+$PkDGx%`$1*AJQ>)(x zrp-}Nr420%t4qU?=;k6iLnlS7iU{S%0}WPlbr?-E8kWC>@7wRt)YTM`QghhG+BPvU zzv9Ol55I6sx+G!#z3gVn2NykEb<-YRzD!0=9sm5>iK|!3SjwIH`}M}xAt!M(f%v6C zXZvrVij`(-X}88!b2;xigP@YRw^L%t9&i+xG!if!cM4ze?wiy|aSm}V9NDljFng$M zXZTKw&$g?B=fdP|9w9xuoeyYOl+Txh+(?bmH9ZJ`mb~%%9yS_c z>*hZd_bTathAiEhvgpEm-6o_^GcZ~672|m>-Vd)LM2UzFtIc1aqV@8UGw$q*l;ra- z|L_`0T`#etJfIT${AtZzZQolrY7%lkr5Q-+k-sKdScof5HJ5d}qZFCMG@J86n5LY| z;ONm?5t7OQ_*@t7zxdus`&-Y*0MEaf&?aGH&b4iC6icDy2ZiH#>!c@)l-@2X4{Ov4 zN6fx9l_DaPk4pUEZ=o^i9FjGrciXx##-MF4Jtyk?o+)+9HyJ<9l(=@wOLP}RS#r9K zjZNOza=WTa-*1I?=J-j6xKN4)l=e}_wemE*xu6&R+5Ud=x2*$_ zlE?jiINiK4tDts_G^zweXsPL|x0D*%;a8?&J88yJAArv@S)~~h6Eh_EAa+z>S;KSS zQWUE8R{QNd&zl+L18Fhkj@C1+M+?{Hwi=;;?M7E!thcZ7Ree=sxvNh?yERg}I(V7B z5X6R`^>$L9bY4g~QhWEediu-H=H=gNn@2ZXx(V+HF5ky(SPpp5!NX@dK`p#dVAMbI zi?yU&?vs+vjdvXd9Gxo9ZWrpvjV$Be z9MO1t!|aU@#0CeBqc>x{kx^Uq8yg%skc1EDmz`D>OpiuCDPn-mgZZVWus9iaFFB&v z)tIT#w~zff8H@Ip6|#0YXW0&*m(5)RaM+Bl_wZhokdozZX-^&Uxw(atK7VIku+>&= zDKl=O7K!28x_^CvsDs%=8>TGSy}uN8e!lc970u2;rUr zX7O$Ad4mw|7*FV5mkj($L?kiqLek&dqla*h|MUIZ-171HDaUUSG`0kH)h$s$k87^Bbdw7E-y^IF1uceJ+FDD{l}`lCzt zW)-sfT85ZS*7mS;DJ4}**4)rGD8QRIy4fAM3_bZ@23iRy3v9EzG!oUxJ6Zb&3o%2R zhOK#Ldb%i7o?>Zc+G#GJEzO=%9gHIXwFlw0w1aH8?)oIT&TgcnAyLm4R@xPFHI8&T z=137(WebQ%y-=XLpXn3enstxjtNT%6++)YLZ&oh?AAW#ht%L=O9=LlaJz?e{mIF+G z31ZkIXdiN`k;WWooOT7Md-mas#a3q@R0Y!CGm zN#81jS9cItn;S*&noFoyr-fa@lkib#^Ym}4QLOQlNFlTv5&E`@7R}~|FgW#A{Tj;&g6nPZaz;_b;v&f{a8X0FxBF2d z&+EpN=E&G)>W)+3$_Wn>ytxg2D*pQEbLZt8a_#WuxvY{ z@n>~Z9>YgGS{cY+V6V%$FlcvT>BOLcn>ldwO0x@WU-rMOYg~yhP7)Xpuo;;3S%L#K zyL3d}!on91Z~a}rKEei_*mVYB5zv+4^J?k80yAv+(4Ijuhea9?psKS#Ld^`y;~2mZ zf@%iqAvS?yctxz|^gq{$>zIYWN9U}L*3aAZXbG`i*0n)6xyE@g3BBwpq0nz-#|0Hn z>?x(CLHifpbt3E!y7_<*tt{_L7shzwa9G)kDvVp>UJF^~kxafJG7MG*K#0jmlxz~) z^&wV^2c~7U8B*i>tLLTS3{e4r_?a2YPe)Wgb(DKiyt=sUFwq>NMa@Q3>oTJ`zyv5} zRDF+!(E}OsuYN=biY~HY-++zMm4tTbzP`^`T!VvGbf#qQ8J%__U>IZoCF}6mT)qX| z)P^8q6~d9hd6X0wa+bkt*}FZ&F&zH~xt5FI{FbSyqpTP#-f`=Q)le`p+!K?SP;SXH z#0tNAL5%?Yp}?cSViz>3U!XhF?`)OdISXJp7rOH?C$9RNRB$(N{p(!&bC|L1OvH^7 z;jenLwCzhqv5T+OWI>7s8wXo4kFz1E{}~nGW9SL}`A^9baJZAJj~X3z-z9iJ4#E

    UlLChH^r$=hke|7O1rtM!^_UJE_=_Cmu$9J}m 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%0AwvtAO`)9OA? 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%0AY=BJuFnLu40n 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

    2||<{@4aEhcYG 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

    QyAgk3nA*p8K~j5(ZMJkYig5ubYIU?gjt=< zTEnwFV&M_s6ybNx|e|wiQtpOc&J?GE57|pvgp14G_ ze-G~hQsKB{ImRzpIF58;{l)6Eq7Ve9DRs07Aau;Iq@mvs?#$x0}mPx9w$nWty+}LqUE*VTu#i2c&dWs%cCpQoeI&Rrq;T| z>v4s?H8oBVv1Bx0<<2riuU4W~Mxf{sSg`gbuyS z1f82YKW5Nr^lh-xdj#?4paZ| zIK@Cjm#zFK3U+C~ilX?lqUQQUwNel{oKxvTU1lMmq&~i}b{v9Sl^ijyM;5c8z65_jholjq= zaXv?{8_E23DoAHV3Y<;Kc&Tn<0v2Q0B=p`t&=Bv~K z%K$gDs$KtmG%ua?p66UVOI>fQ(*jGU71cFBfYSAAx_qjgt<7A@doen5{4njPuceLw zz*eW0f~QBQ|AM|Yv!y|}X@t${_AF7XOY6KM^vEi>k8IT;g|GlWZ{R)Ly>W>Rl&~

    K({YsWNhD*o<&rF7pXrB^yvTSZ_5{|93D(hEg!<+Xjh0|;IQ`POSYtI-1+{o z9*{@$AZh*8=@+>rUV7~iqsbl)L2e;49nvsF!8r3&v^6~NzkubJ_c+D7jI}%gK46Fo zyuOFg1__G%&cGWJmn^6COu;VqtoCIVjIGh@`R0X|v#;*dUDLq!2BfFEkBrc~X)(jv z@YR#l!`4h_S7FO|mh)Zqc~Zf%&a%-<_hDIZwXYu$HxORsDTDU{%YL!3UaH*!?*wo6 z(9jqz$K9)p@}HQMnRR}h<5?viy zZ2s*Clm=lf16jl^dYu*OqsE|L?qWt}aQ@#iaO5(w%58G+mYV3HuG#<%?sUiEg@Y=K zPXginu~W^oi209uY1g}2Us%J|1}GfbM1S#ZxEckY&*O!Q$+;dnGr`g*gMQj|JOqV`oW-B% zOLXTkZr(kgOPq{JmZ;pZ0HJKiQx*Buzv@a4iwz4ewa_2pVx_wChK^B(f9siN6;dnM*lv9(n){j&gfGLIiHMT^DJ2yPD0 zf>U_#_z_iqTW8q2U7m3BT)wK2%`89Xqww2<)VO<|Ca+zg_dn3iQY{Z$_nqp%Ux50? zhAHgO6|a`N)ZYc}_V{6`r66BP2d#&;|F=Hs-Px0`c23S%1zVE6?P_XI6!p<6wg_G*prEboODAVYc*1i zw0?U8XG~rLHB<8{EW&B-iL;kk7A?YM5-|qjgUt20pBms&aC~^NEit%^O}D~gC#sdK z!8vvRV{Gm-itie`QSN)iV3BVVHLs<-HK;Lz5*z{U46aAMFq zxf!P^&^y%|G2;e{kFUKdUtTJA44#Sne_XH?lx6-8EGSDI5r=~kU`wg~^j=_r1XfHY z8g|j_RD?zc;da(=1`7tf_-i?Jg}O^};~Sw}e_uy8PlvxzGf*jdr0$39L>TYI2j|)x z&b3zrq{s7hk|Mb?K)m`|_vqH$L_|@~(}AnFjz>EjgG$XGuRir96DZ|ABogz$5}BnP zo}mI^E`D184WmCitj9VDJyOz4<-roy=fi;0IO*52rZKd4DS3|Rlzdv+jf3m+WLFvg zwhVkjUcF`5K({~o1}Lsn3=bBA_>}S?EazAH;5NTND*WkvdDhmruoz{)V*{{AOi*%j z_TX&%%he`}=)Uj5QwC_V9A}>1{|ziO!_t}d%xbcxY(xpx@H8I3R^SrwQno6 z*Aw;Thg@+L$s@5 z2%g*8NmyTAc=XQ{{ly5y`MzA+M#svPRhD_4RP?MHWioksXY4DQPs;x13GHoN}`n!SfUXyh|HF#)|G0W-VyNCP%DIJLpkIk zD@3cUqx5GpOqox%%;SA21eZEPx$kkfQ%zB@-Up%Sq~6c9trRp|fc3nvDQl%pBlK3U z(vueoWw>Yx=GcWpmdZUr>O0*+<41K@z_C+6nu0f}`NgMK;N@>RL1!N?(BT1H86Yto zp?>_CpAZdxG(aK49GTNu#lQvI_h9)7?r^eC5p!u(q86X|B5G3p3m@vE13N=h?aK*m zw8Y}~h3QrLGYQx;U4PJ8$BWILv9tH~%qi!cFkJ0ct!vVVjVC{!q0tNP zL4lqTzJNMCtqo96x~aPvbuu6&r7^(f&EGOHT`h}+g@Is*X~kcI{{Sfi&Eis2V-R5b!ixd&@=N+z(_cwXB_$kF`PR>@jqzL0_e!}{FN zO*enuV(wSLa^wWQAl#$Cg|T+N&wh@}EKu^eGj&~0hzk*$*pl9~1xka%{fpo4U7~nu z4{#z_gNy3Qse#w)@(N^nL4`v`SoZYdyM_BwdJ4l_y9$eZRQEb&Jm(%N@ypLB4cDID z$9L&GE+%MC3%5k>t)XU|2mQHqJcfC0+Ze*-r8uK^0@Pq6UbMo#!M~Y*z01bH+)?9t zetMkincMnQ#CW{0I0+?W{H4E))Ae7Rr~WY+N{=7kMPJc12zk_JAMZ!osebzRSGs9P zgTd3dn0ECkznI25;2>Nndc^Z$Bek#X&GR`%Fb@Y`Sv=P=iAhNya^Q!X9JExOp?#`*hl%YN)9jt0%gd3#>EWBR`F!( zKu22utL)l~3Or*OkZUx7fM|%kst8-lfFBBp1L1dhhl;6{t@7ytI5nscP#N z1KqxEjm_RLmLds#t7y%CFeX@z_e$cydViiSK7`(`LLoQ#1%>K*YQ8^fYuwP@Agu0= z>gYw_nF!h~0 zK%pQMkX53Q8$*(1$>QrZtOX@JEWT8yIoyCpm6At0?Ss>F^17@L9qP0(#ilEfzI9T~ za?*fK+P9KsWdxRL1paKPDq5-MSs4KzZTz85#3g3FVoGrK?-FFPMgHVe7YT{2aa9e*cW_v>XMY8 z?KA{r2g7RRKcvijEphy$ywc0_b+orhkBR+sDyN(R3lJaZ>q}UGP+OwX^L}GO($K}} z?)}WNbj|Gk$f6krD>aQ9IjIXb&i|7$ARru_xJ+$u`KVI<%hs?w-Bon%8HGD0m~L=q zq3Ee?l-uQ@GQPy)w-Pw1oIEOO*g?MMJmlZlNZqF=XliVb!ceB-PtW^INuGajIhRzVghjyPgLG|r0?L0@x168lJ#E=_+Yjk4 zWS2LA{ey5R2}Kf>@}DRPmIT~;e6;;Cyu&Cyh*YsvT{DkfTKTm%eW$UyB`-a1EFKzuHoAI&hgc+v?{PNO< z?Nsx;UhJPv$Z(s&*m1*pp8f2BF~!Qe#RzzXyYa3;x_RRYbvF5=RMF!@CYQ8Q0=#q% zPf&YPBelJM7MJhzCwnpN3pe4vUiPwtBy__~dc;fN4n`YiEx<4KaP~Kh-h}mx*Gj(R z_Y~nOhP^uZS($jb)5TD+`4bID(`ftQoPxi(LDA#;X;J`l$Ik+N<#6jfdc@9d;H}kM zkY(ueoqu&$XeOy&OhTKT8aXQpmKfSrHhphcSMkJCeg+uYyL3E9SWw|Efcbh+txvb- zNmYkU+_LcvZQ8_#B>vmG4(WE_a-Ydc_PiXM>MH)Jl6bJG@lOdK!qhIC2zU4eHWfUD zpZMB*7S_1b-swAz_hg}=uiLSgC5h2>7?luDY7Xux^Iv?_;>a2viNcF_!Pk zS_B-tI6-r#A-gJsYZ$XTc`x!jS<^vwU7HKTx+5H2k2hRB^Fk)At=it9ylbA426{T>H<8fm^`|)9$Dw&3k2)3(sZbG zL(2P76n*QYs%4`AoiuJG&B_QY-3Y8G0508FKQlGeys}+y>%$qEPtD_Yi7x6D3N-TL zp}HTm=uy+N$a9xs_w=akuP6litNhfsK~EF^?W{8b5@w4hHLC~6?)yvKT)JMGOQFq; zv?(Y2^VYV}*+X310?Twg5oc|7sX0Xz^8{9=>JR6$guBi{(Ql6CZq3onn>Vw+mrtLd z=6aW0<#4$woa`l%PB6Pilwfn2Z)`pwi1$SPjW%@BK?}=JTA~MWnc=6!t9~0{p)3F3 z=H09i7<@Sk2L$@lUWNq-y_9DoQDT)cd`oPdQ6cax64EIRor+0&MHTW z%8%yB>}agGzIa~@QQyZ?)V-PI6KACpFv+r%67;{cm1T?3o!>jF^uZk84?glgUn)Z{ zP0^zbuoIkn>bvRMO`JtIkHlGY6KB%I&j+auR$cve46A2(PiuWI@>f`p)Sn<7+b^uN zT0fa2a4+l6MSrP=RXlbjgrY(z4!ipLPxV@SI}cF6Zr%#i>Rl^A4XjVbBr1^C`m>j6 z!#ci}ip^8<%R9`vQ-H#7n<72WRnzvJxiZ|Kzrl1H-v3G?!+9WJ`Vx7jb%9YZ3@d__8H%U6)JG2(-mPjW ztmpFG6T_t}XQzs)us_W>VNqcZEIN!F;V>Pv)0cK58KOGPBBi%ZaWSon5=`j$ERSp@ z=@CC%#OtR+f+DxXxi(MZV>+)R1S`AxX;9=yK25kv1@LGmqfyEEON{l-(ff|E@9aTK3$)uVup9A`G$_# z=AoMJ>Qo5bo^*KmIz#c_O1p~I6u8&ms5veqsw5t4*SzKAL)f)T{^k#QrPBPAwYE?jN(w^@T7<3oC$%$M`h<*Ckp z*emgK^pv?!=7A)4H+^>)QRVfslrfsM<#n*6cEKfsN0pUF+jX(nZ_7o87TZpz>7CHG zj>=j_8qiVuRuZj@z!HtXiUQyg4fL~AjrYYMV~jtbnHy7-yN`r!gy=Pe0Mz$#17mv5 z(DmO$nZg7F7B8xpnQ)231hjO!-_ecjWNchwly2RkP%d<9z@Nw@TiwAy>STl|P4`mQ zm@@{iTTOCWPWtm0)bMgoeILEsnzMETmoy%ahX}uJKGm@B#GgwOg^9n+XOoh$9nY_Y zC4Sv1wA=Q9)rBjZ;b)teJTEm;3rjKsg}1~_X6+KUVz!oS0fH4=Y!JfLWqH0`LtIrT zJ}|UQ!o(2=gh7U8E;5D?pjL1_gEMe&&)%r2(3n2Pji~8m8Fh1|&nnyjBdeAv&@;2q(Z4wf`2#xh zTcqzleccshq8hj>&OdNDJvqfJZf=JA-`wSnd>RCTP0UW^r*H`$W%va0)Hq}|@iG^~ z&j7w_ZnWz$6@^7!xL#)p*0Y%J$(SywX>QnL?vJmITG#5)GuNULqca?;Abp`%TiMV8 zKZBjz22RuDZ~-{-+&bFY#F~A(*Ae~3a;yY%BM`(zBwYm%f*afO5cmpiF>8yKMzOf$T{JW!r3FdS8zzF%9Z}~y3SWYf*^QUoX#Y;mEXP% z(*Cso06+jqL_t(R&lw*W&kFPP7C zVhrJI`DuZ(#hAtE`zncBq{y{=I9L@fSL#oU5`VB+X>QMxUU9+DQu7T9HYk^%nqN!@ zlpg~s}?Ums}6s*>10=M`DPV0M0(miWyyk-mM z7RTt!uM1onay-sndhHN9$0y*mA{nP~#-D9@Ypc?lo~a60)}6ek$NFA&!(arw1KcVs z_nDUz_=vU}FC6O4qQL71*kzLl#qZ++`)_pK!*Y8fD%y(MlUW*KmuXm6SAu1Xz}+bw zcM2UJrk2_qo``cTE7vQd?9#K^pC`+E)|K8XM>&@TSA#2Ec%{=Hhxm}50hSQ>3)W7R zAwvF4>G%|*jSU}*c=d1OX9jq^zi=XpFjuf#@JJdZm*vq~FAL3umd^Hfndwm5ww3YG zmcDgV?()%qj+(cUXk`SJXarUi0GDW>pQ&nA)lhR@h?b%ep#-FLplQ3>q%;m2DbEM& z=2Cs5IuFVWY}=!ioc-A)$P$DU>-r{r1mV;zP#^OtJkOQ~&+L<6VG2BH_+uI#bF!_Ja6f9V3`R)6}obQ0xh8VGLN??lc)2`#X=q>uQUV86my7iYr79f&SBh+!Uhk8%-QV)J7ZToAgT#DfR zjK`hbq?ZbC;{}4E1E;uoc_B<>d)bXT9G21Z-+yImJuWqU&cNI~KtV2vfsmA|c0KsL z9m6<7(E)3BQVyG3!DP=4+-cnZjx-Ma`%cpAV=1br7q`pvOBT(DhK`-Jx59@r=Ol9p zZrOOdbBkqqG_S)K7F~3ZukC`aw;=4^!xb|px@jb9@y5E^*>gS2S{hLpZfA3>ChJTV zUov098W;PP;lPf?7Yu%*_H@DCW)n#6adEBU0iSq75kfG2Sh7xINKn$NheB_*>p3SP zbmc0?!CGkROF3;$Jymfbf0+84SQaP_ca4?5Pdwk*#1v~Fp!gtvC4#~~R0|NiFNO3A zFb|_NbU|mrhh7gl6FRW2@ZhtD@AB5W6s{;N0(0kkIzAV>aE2lewRSgCT|l)?y%hQ@ zT>&+EVTdt0ae3(lRUWO(XXDrN?P?}mB0B#Id!?8pT%vO8TIybUsIjeE`c#m9lT!Z) zyg0-O=nn~YVGhiAxL7LYQD02sCvGvNEvDs1^f#3rsoTZRwM+EgX{LlFEX+J#{J7+UMvnK;tbVt{obBr*jr?7Y6yLXR ztMtCCFO|ZB9l{B>_z;yCL)Z8Pm1tZA%Y4?>xWE2@r#crW`L6(&3)a8ANjxVUuViN1Trp0AmTQO_xzcR(;T(72H^s12>aMYMO= zMaxTl?@8`|m)jH3n)S7;!B9AgCE2?a2Sj7DOd* zH3$9jzq(iFQzCujco)SSd0opD&Ix=(-TxOG$?B7N&^<#ATIC?g3yrcDq^sRDG;I${ zRHZH#Jd%dZMR_#G#tX`GR0Cp)s}LP(s>h`Whj6A`UYTg^a?yY;jnqmpDCP1 z%Y~A25o&5WED>|zlTb=n>KmQU*U|7WXL{lm!@iznvXo{TrhJ4SR-f_@EER6~ejD|F zI6|WrM@@g@SEDrhdD50~n=i{U;H7XER~}1I&(1d}l64sH)>q-@0s(9Q)51lIkDdt6#8uo9KnL0GxPffK@Zv0%q*!ddjDR2=!I{_x| z_iL%+YUvDKQ+uHV((qX|UL;OUBZM&J$!WuBdU=%RFZTN8~;L8(=NsKS{9wY`G%|Dm|Kn^N{uxC%?ojnjUvz}{54MFP%BjK14Tu>#7+ zjYs-)IEOwAy}g5auPQ8_0G38hHPPW=&NdCxfo(>Kq-r44ah?|fOaUcIyX%{%2Nw%e zXVK_bgeGq#DE&lH);DcWE4pZmlHF&(t=->_nB89i4#z(pqAikO+*nfS?_OAV*0H;o zq!btI+Eub*C~r~Y+kN~M7c1JbFg~Gx|CGs4>VATv5NI~l)>7mP^BIvX%mbIcjH#|y z;G^xYSghw&)zqTQBX01o(~Br2^ep1LrS!8vo=U=^b#UpcdVH;6sO5cE~kYpj_97wWk7m$t&dyxS zl1an0GOQQ$pNpB#c&XHH%1eeKe_aclW{4(XSv+k4q696}bFhb57~V=X+`W&Z;ZR)= zfiSj=i)nuf^oVDDBfGSgpxAAl#%XhEVa95Ho;F;?Z9Lkl--dX1>B$UD4jhs!@U;vK z7!)-<6h3~LDGv~x+3%b`a|UkW`#(0jGta>4aYy}bscx!&hqfy85c~~?*}5VUN{CIT zB9t`Yv>BSc+DlDu%fhw3L&h`AJy_!Ie89?nZCLW6kb*$(jpOL65#Hf!QT5fZT*`cn zQtxnq^JSTEFwfc=NnrcQt$()dtK0LGp1VXHxR@Qk4IbBYgt}XMXkI~&yUI6wJWGd; zo&j7%op2F)2DC!tzHwpL4g>q>2TjWPu5mit)THDuioM$VhJnA>)JKg^*<}vb@!)0P zL$C>QE$AA*z_k`FRYA|#8W+kh1Dxj%S;+n->Nqz^Dbra84%m}4&BMy_=s*Y6f~P!h z2MR#D@7bT{%mZfW{HS8A;XOiq{Hgm6x5Uc76dR6#uL*qMGqdta`U~*5nmQx}xbVqB zd=mMhWdiUrkBu&uCn7c^zQ3$tJUl2Ib?v87#Uy|FgUX4`@Q3XvxKI-Doc~ ztvfhCZNJ>1yOetfD>geK6sT{e*EUy^C-W7}PL5FQUammx2MnEK-*@LWmsX_H-$l1e zpMW8ysfNH(+L3XZI;Y=6cUo4Z(s^rtKy7PB*ti0bx}X!$>UsmzeYy_<@Y2Rr+`jML z9HIW(TInh3Jp@MF<}ft}RulclZ&B)>A{1eQ7A>XKZ%xx)qGA0eZ~Q_xHCzZ#bL%Fm zS_NW0IYZ+Uh5BVxI^v-abfAf-+o%9mzgOuZP%kM>|15bqhP9aQa5PR&SvUl1DsQz> z@7uakn?Qo(lYHJ$L!GZu_!6TaeC162bNpmq<;LuscxZnoN zUp3Pi2qUy-1Pthr4IyfQBFDG>;~K2Rv7*+ZR?c$$J_~S1j7?+L6>5pWGHQOFQZ&@H zn}%>g1e>W47bEl^JS7Oh#8q_GQTysJ>TMSXt`N^!WT5zXT}T5cZ{ zLrcRIv=Z{aP*AvXDh`XhtC*+g%r-6|9zJd_q27tm^8hhf*RtR z2eDhqWvzDkV+=TxC3Sn^HBR|a)bm8oM7V%wq99(#-AT08{06GOtG|V{d z4cAyUT;#!w<9dnwgqv(GFUd)t@;5*2fo11$h6_bMI84zG1Rf38|Lw&yDuOdg-dTmZ z1;w-fF-X@XVONwoeuUA-o^8gSeS`}lcOksXm%E{x4j(k1@r7!7X1`Cvx)=V$6WAQ0 znm_>Ky_$qV!g&x7BwGN!FY+o#n zR0zu6mIs8x!F>8~6}RzdM+4y^J(-+7W3ZIhN1Y9Rb2A_u>w~h8z88cg`zYN!9X8Px z=FB(;-fhF*B3>K-UVJ|>so*gL>Fm2!<$0;?(jbiiE^2Tu1x3Fd_zUf9gH>6v|L5tG z3lS4d5I%lILCfY^=>{xuaXuDX1*MgX-88R@1le50{FHK3np| z*#@ec_K*+mptW(1U+ADMvBk5e3bfDNsR-3{T%+zLeVr@|4hDtb@MeM{++uf97>{IE zo%Q7zm*?qG%BZN4rtE!H!`a{_-!Stym7f=F64OttaCI$(^>3E{u^)WbymHARf3X+1 zMq6VYt@dPS{`MynnU?uduwPG?k6Z0k>-sAGr2=@c7mZ#rK18Jk$TfaJr5aeda-X#| z?r%6oeLH8UhgqDA?cNlwgkG>}pWy)eI(Clwok##?ACm(Pe zZH1+7M`;9qw$FxU8ho#lLNAEZp}5Dj^P~c2U2#3BU}~3fHK#2N?CztsUohD@R>hg4 zyXXwA`iFOd-zE5^cVi6h25IX%;4cUe=-otnv7dFr7jf$TQA1YNw&_1r(Hh7=W{Q-7 zIBmCg;~qM@|2o6tz_0tD1AjgWK|Tbo5#)=o+{*a8ov$B8@eFn^6FjPjJh~Klj-$b@ z5FP5M?h+EYLEk#8c3EgZhizL)wlV@sF#;b zVcxnHmW3oDuf>7T)KBdnT_HLMngx&^GTpJWm7`mCSAs+Aae?>pQI=z?uEa?{jTKwv@vINi#Dt%E_qH(?ddU3{R+p~w7Re<( zDA~6sWfi{`OAFK4t1T3TphSZ|wy@yHSsFB2|I0PY-_%Jb;Igup!;yP%U%=o@z6Ve! zp89(~bu_f)t&EECQIB>$mr`L(|Eh%w?N=?G{%cjY6S$+7;yh%v7VJu}%!Avz-M^lo zz)z0A%3OjKk)k-}!%|)MafYR3LAT5E25Iod2=#1hlN3eb6A;L=YPDea_*rVsjM8BV z25DdM`$s4=vR4s|WADcKx-)ziR!W-J7g}A0OzgF8-v9+K=xbGcib<|n;R4&hVD;6v zL&=JuWTh(_i<^Uyyg-FjkPU`t@G6ug%T#}&AoEV#;nz;^~m#USl%;G3AXi&t@ zW)f}^OVE?B8oTPIZkhn@d1QaDbnc}9vm+{)ool(uvZ&Un>WxClR%Vcfc7>Qysg_dz z^}*s{0P0{Ia?#F^OV*|f67kELu+M>(hd(Xzd}l@#uWP_5oMs zFEvYDM>)Q=K+=F=*6@ zK7-{!!x#WA{u&qQ_TwU5f_;iz#RWF@&V%izuff`e`pj_?mbfw>tl{35T4?G*75xZ! zn~vp+OFn$Q^b991fZumvmrR%IpNk7Y@p%B2P})9V_^#pXS@?qy&MXZMCU?Y3b+2QA z<%RXIbpClK?Rm)>FL8CA(#A26TetY;GT-p)ugsMgt-P31*v8boeS!L)?5A#yw_v_U z@TYw0JL%d_azbxq0&}F*`D#w-JjRF{XgOWQgDc2~uxh{75Aj>dxT*5E$uHpTD<-UJ zmodMS9mVrFDO04c#2PQQtpwhYvpmA$?gaT?>ZLYVx#f?Mf6c!+S}p!i)?K7-Pyi(e zYj{riW{>Z{y(Fu3CwTN0R^VBxg28T_U#qVj(v==<;SYEk0}c&CK#{1W3)rZ{XDsB2 zs9OBGQ*>V) zu6ZZrI!Pn-!%5v=r{Hnw4#Tme60BeDMQ}kpLVZ_nT3;?v<#zczarLarwbyFyRozj= ziTs7>R6VCveXinadUf_8z>9XsS=O24ELL(l-g?08zaOFN6U-0-YRk&n6t#+rNyh@S z)L`I4E0Ri4R^wau_GKFJ8_x`^Z^(yKkBY~;9Pp@8^5|0JIeeU~#+h8ELwTKA*s^Xd zLH5@kBDzW6O4VAvRGtRpyUns0h7YCWF%+@#x-tTeM&QpJrCLe!r#Au+IybiZq<**j zppZ2TFE#v?>^e9AcPeUjrc^W$+^)6%e2%Vs*oRxPa=kpIufC1Wel$gc1(p9`1u6gd z$ymoJ(d=VAc@>P_X^$5-$(0X}{&Tzf^xRd?GbZ*b zV++$1J^lvmI@+r%v*4CidsiRzoizTPpw9ixl50p&OXT=__U8HZrYJ&QYCbhVS59}D z%FQQU8>IGABXkp1tbJpYW!ti5+LcyiW~FVb(zb20(zYsX+qP|2+O}Z$&ro4o^`Uu2Tivs>(UIV z+CGP!ga2!EgP1jBK?e4=#Ur7fU*PSQfkwsP+;M;DBZ%b<np&#5;YObphYc?}lq%M*izP*jh zUAJ=C#i!}s#{s@FDoxL>1x$kBG&@g$G3S>k!tBoRY6HtJtgLef)aN!$>e=BJ2&|30 zvr}lA1eDNc*0V2x52}Cq7_5hzdNg2tR+~5)IeQ$`e`^oB$VVqb$N8byA6w6a`OmyC zacFX73!PPzcTZeDfkc+TQQykhYKG7A>00iLeNV@Nb6r|BS=|qDjV)vBzSegDV|&gi z)o;-X70s(P!ig>Z-N*!WTD6k_BmU-BOrTbBS`cgLON0$Na5QU~$E3!SQ3#@nQ`=uJ zWWW89iD+A!m_}1Ddlu2CghcJ!p^nt#aF%=MgNpu35Pn#&Y4xUEb-1;9vG*YtYBdJx(^*l*(84teUwyL%t&a8r%T{$=U>E67sthnW zPZqwWWKgaj7LLD!NB#+O%*1$wp)?PQf;XkBv#1Qi<+b_hzQiWmyYW@QQ?Z2}jevIw zsRMVw>pX6LfbA0~cYRw)!Rb-AkrV{q=ueuG_SF{@g;3&2PrNG8crv)QT4&n|#UFRY zd;%2e^4C-*&4SVc-aW9Ep-U4cl|l#PQ{?h(`eKQzO=3?ks@0zS!kUrJTIyJxheQ@D zp0jz@;sWbo_&CHOjO07ls8?6+5w8{F^#TYgc`po-P2nxV7EZlQaygvE2%srA9Kgyl}$86o0>Q=s-$68PljNDs~aSz^g zO}HEwJN`?kiGp|Gx8!Z-St1F331kW?reEPtZwC;gvdswY=~6sj)aJ~Nd${E zsm*-9KQ}rcE${6oXU*TH57%#4J~x1j%>4MhW8Ul8zD#9csvDsipTee>*DEABZq~C% z+O*%hNT>m> zkNCdfidM21o#_g{J&q!X$v(rG{c$$>jYVo2@qwlRw@bjH;K`GyFg4?;!+A{75lRrE z*dpa4$Sij)cZ?)z5Iia8M&gLw73yJQB<{*j*sfZRy&*Y~)}}vwl8mKkN8gBvsDz?_ ztwc{c@sdGiRB8s3VsQm`B+|yIFBxLzcim>FtkokZ>#KSmP1y8?$BJ@nV|WT^yN60KvyrL>MilXQz5 zQ^pJ1<^AmSRf#c}6iKe9KWH~YocQ@fhDNFlNvHR2X7UyKeqd$JP?q@-&*Go@S%Hl_ zB4OkpLK(n;6+*7vey-JwgPo2hLy19w%MNov zst$uP_EyS{jU;RYy?pH4FKv~z7JnQ*?}XzoOGKT$#>Z?NFcCOa8B@;c$c#MH7cboA z9AfqY!3lWt!0OxbgqO(!p9Hh|Q+S(HL^h;ph7>L-KY-rUaJ(hvNLe4lDs7%WFKYf7 zMm^&+#9%I&a7d2~1mqEGWfA#g?Z58SKynS~}f4 zHu0hnYgWOnOvuWNHfko;e;9Rwg4sk+AFG}@St&-@jKSnCOI8(h+24i)3!MOVa}1hl zWp{XoSgT7qU8iu;zv}cs43)^{LTIXANOWyJ>?VCeJ(1PnDyeX!oc#T_vS+KhmWn-h zX#&IR0+jRRnRv&nA6A<`d1*q8kR^ylHPmV#RHX-1Nb#n^vRWh;$%?Sr^{QQKl3^!P zQ__Uh`%Q>IHTnEQ*1S9FcYuk;wXf5h1!bsI*5nm=NhH0#VqGeT7d}V$+&)&o%B=> z#%HFan|;I9{QB*@DUNPwLl)DQn6SaHwy#!fs(kSBJhY=!`{uD` zQmoz7`;rFmIS+hQjEyhl6J}zYCNZ#`a63{9xCc{xHtj4d^M3~$pTSEtfXuEdYq|dpinCB_76=`P*Fq&Anf!7uZufXvwXGI)sO zl)(BhUGw1uWp`8Eqrb)O3I!?F*{>vlln}v&@BG&Nsjm7}s27UUxW17)!n8b>o0i4% z049Mai{wNgZd3V=mDIMBMDVGAC80AU%cUXe zm+OpMzsO26c{6Hz=~7iek$M+V0{moP7EP2siL|R?sG~>lUeFf6Epwjml1zY zZ6W%GlSR(&Od^i6rZv0%Y)2SK8JoliPQ2_sSEwJDgPh4S#p$&e)$ZPSGHe1}49rGW; z0V1k7MbZQBQQjKR;uq<`0okE5Efj`0rj)XC89VhMbXb}$)&dAEJT$-5Q{*B=Sc>_JcYYC2)Cc(PFp$@+32IeKLt^qPBmW%%=)kMC+-#U~AE%p> zC!8`YgzaDK_C=x(@CE(c`tmsI~2t{+P<5EW2WE9ntTwFTH>`^_n7V~}7mtszfYSFuCjC0zdHam=f_TCa!x z168B2V7=K-LfG%`>`Hqoc4B+>zm|g@i7L$syR(?urL!cY;xRp#{*q+PDWYx8tt|J5 zMxb~L#|`_N5vm(kIBnad0NU?5Fov&+&iBslplTnYzcqM!#wcORy)j4A0H)2}KGJPr!Vh zA>uGh(9vXwypPtY>iJb50~d~CG!mL zJj>#-@Eqkqlo16(?b8`?JSY6VX7i`)G!ZyrQ@NTCY4FZD!=RjtwHbwM>c4g_1C|FB zM-nmkfL(h7sOmAW#R4scU>@~ZUJH0VQU&BTFRXNFQHkVqAeMOUq0$O<1&j;=B5ihD zLU^F%!Br%R2JHK9Pr1wM8>s2jGB469RdVTyZ?=6nYTWJ|T$|{lYkO%UBu0Jsh&C(B z$CISaJ%~|zE#J~^Dj#~TD7yj#gT0D&(U|0zQ3nQ+!)K*zf6GNQ%_>iXt_kj(PidBWlFn z_Fsz77fB5kiW7!~qa zA8Zj9gqlsu!%D}U#l(&Oq%pLUB+TGBGV^%uJ3-9d`KJ-Yr?MkEk*hiHV|TUkbNtyE zuh+K?n-oRYguPl1G@@%q^oi0)_>|xdPB#ANn*uSJq4{{I)qzkO2 zb1_(G3?_xYEi@O*gvq&)*6(J3pbmZB8(G?_{59I+8#AC5w&H6g{+#^lTD~YKR|L47 zp+W2C6R`4?;pS=-RconT7%D(&U_}3a<10K=%PFV%#@MJ22VaxBKkXPsX)Ji+f+ABD zG*JxE7k%SM^zhc8Ak=g}f3 zY`TC-Ba)gY7}BGvn%iu*d3NIeQn_VFda%CbZEmgh5qzk*q6pXcn0>o*rXetXY^l&b zB5{{T8C!irM^rm6m#j){OFbhwg@`6sV2JK@Ay7Mrk(}G!y`VPAEQ>~bIhpDNH+Yvju` zAv%%41;${r*3{m?kA>^&-D-NXF%D;T-yPj?Bmf2b%+X9uH@eIr_gJfN>vkG-Iz z-o6mbU+M|GSCutZ@W~X(RmcN1JQmaD(6(*IZ6y2@W5ZZ6SPO=-3aIPjfD7HH9kbkK z{)UF@s#L^DndA!*~E-jw!&O2PU6koTB(cAe*Q|Oud}yS z1!>p)Z(aMhFTsZerUp#^jsAS%->~?XpYcz3<3C&y7(qP2Z7G`JlOz9O{r~9%{0|nf zfG^up1RT`)w|De!jsH*E^Pew%3L?H}K$5LaEx-O}ME#A0|No4t08$&R>^C<2FLU~z zlk~rRa~8h`#@F0e-{#+n=6}YmoP@u876t1cwAlY0(*F~t#Xtb707-$u{llRD&Aa~) zLH}mUULn~jaeCU2DKIet{!awy3IC-IsbB!7f4E-F;GC3| zOFZ)7m2EP5Jsq9q#~f+8;B${EDa$;x;ewg=2wo)3GvwLc_A0y5z=ZJyFfa)WU>%HP>owbvzh$Qe4)vSV1Nl~% zVC+=gd=RFK@Q0VIOnTPyB{)$(#&13!`m?k;pufIbBZURy(qfwMOczylem)9XX0JsZz z_wzo!-br&yB67W!n`g!8*Z*veWl?leM2m#tf=Va5X97gXlq8uw)s*A$Y75B$p<(0c zQ;SGI`k8hYGDuz^W4DG^{31;=H_j&i{c6j;sOmddID0~+p_P~NQ3Fa7Ft>lvJ0clS z;xkkFtK=Qt548t?5!Qz-Lm}Km# zhrj|kLOV%MZ5*$%@o(R+31KDB&N6LhvkhrRm>K!vmWr#*W#Bgj@!6y0o?SwTactQ$ zm!F5f=B$mFD79@w*y(I1sTLg1A&&4N8#`P7_5iJPu`0-pm4k|{uGbUV(~7u(IgN&= zswj#jY1;k(!u4c{wk0M|B>=glDZ4T`J-Vbxyq9!42}g^>Pe{O1(c42;wZQbsVEM## zyY)pB`-5?lL;q3_;bW*b=(%khli2wn)Ln5*HMEj)A3061$u`46OYB-Idwt1_u4^t} zc}n)EegIVgwV`>CQdFtabL|zi2Nrp)on2Vdn*&F+ioHwBK|Xb0;0X zVR7W}g$5?8vAbY6C~64vVAfg$tj}zc4$;h9`1jDi?h!vN#4in}HN9I?gowC=P*EBz z5?dxwQ)H2)YCKlxqoyJ=hCB*;84)YdV)lKkGmSS1oJKuuI56cl+bk4+(vv-MPHGKZwJf9RlhvrL%(}dak48pe0 zNG1)r51lBOz})j^7HcEjdAo0GKHB5Vlo_JvJu%MPC8>fu8OLo==YiJsBkEdFg?UL#Mx;Fp|v;V*o!+by};)MYU0 zm-jlSy>*FsiV=1Ag6X@f48ZO`gaBP(<&(-^hSQHF?8|xW5W(irY$VEc8|TfNE0MTOC{gr2E#R7E|uVb58~djnL|`^GzT zra&CRsdI?fD*bAxS?(BUMwF5X`+|%Ll;6>(*knq@4|OAAWf^6scoMqWO;=O_=5%F} zFs1dn(n9L8Gi;-8fBN9qj!GO1^cO8DOJO37qt2BPcr%8k-?W#zn<1PUMld|nxTCZQ zMh&F-gMYhkE{B@LkNSX=_dFxEZDx{(Q&ElOeLFw(bt}XkLev(=k{PlZh>Wu6F?mfr zDPR(B9w zdozh#YEngukxoV_f!S*r!niNE*&>v|BcS^5giIQ4fA}gv(z)}b4m=MFw%9wsGbzTm zINxA7^G?A9g3YGcp@(0t0Gn}~^QsG6T>wqxF{gHqApI$>KaNQNXuZ9%jQZTn+vgAN zv>P6uEJvr6nM7<%bquRk+*pfuoAQXxwT}92n!}9K{nL9ejs(&KqCPvUFTWDtjz)9$)`eH=dTM zeNtrxS~+L!?(x2mDoehqrdH&~>aQsw{&QEwMJs(CrP2N-!LmNy8QL{J z4I1s|mz~MsLbCN#qvJ1{CZrE58`Exg%QzXM}o-gJ&9 z7AUG?rSN2!u%c$W-Xg-u!iRgCGq-poLX%F`3B`MKUZwjQG_ReNMr@ zGT@`cOXpx1$5GktK=QO5H6hay5m@s$i-tX^_cK25eiI#vMl9haU5hWmAg@`tj`>{p zN;>vW}zfIcwMI6SdfPTU@_VO?EF< zD8MA40R;vFhRgQ`e%ggvZt5KtTY*?*dFwUVJo#Kd;eu=(&S|cMLp}$gbs$8z80D@f zdPWi|Fq!w<(Dly+QOxH& ztgeL1%f7W@O$J9J{7q`vpzE1=9>+^Yo=GE*waOK^juu81u;@=d@W$+#WhOgAAXD1W zyI#yT+J(JQ{W=Tv-(+=S5=0}zu-S}Rao%6CxLv4a;iq4{&2W#|6@^btIcjQo<6HZ` zb?2XOPmjJmILt11n+GlWmvRNxbg}<@p}6T6OcMmxuI#_w_%+!!)NI+I zxptL073%bxF~?;DehmKK0Qpk#lxZ`o65~@Fb*%g{6S!b`uq`AsoWD9>#t#VK5I49rBY3X{mwH?Dz(6;Qb3+$1_e2B z{Y)V##3-McpNan7c3W##&KesV6_5gD{&DU}70fai@H1Nyv1R&WB&YUPmoQ;Y8G36| zC1UC!zYPvTlB2HAwF%~KL7)f_(h%}Yj8UwZDLWQ#ciT|x0iMfYG3wA$~0E7 zXLie{E^G4$Ob!Wz$tNEBPcxm9I8uihplb#6CWr|RXCQpwd8d@>T9RUK1W4pc)Hpk1 z<0abV?2Y@5gYRUnJ)<2{^j|Ky9hg`{9|93R5n}H>%ZZYq@Xk+(nL_fUZWYW87 zU2?2YeB{W!TKEue>AsY2lPQ{2feY3wvV8$n-ShK12#)@pzHr8#l;l>!?hYxUAn|Eb z_>{LlCLHrv+B&4Hh$;D#z)sqbgZs6<-Rq`Q0mIpEScqLRUCdjdCAjMlh52>sh2!Itj&!@DwDCseVwtPV%EMI!SG1 z@y1T7K<1wNwY=m{=Y_(Vb#8QGI{iFZo)92GUe#865VOde?vONuK~xpn@);UdA5om* zKqU_C1856TNPAvn*C-CRnU@=4u3<}C z6AAMPdQN~o9MjQ9np5TN!gaH+bImVleGD2>^FQy7a+Qydf{rYOXmEaM1&LLjhkWI$ zJrY@30nj7sgbhVu+yX^$(ubr&nh{Hcn*x942Q;?oNMZNpyv@BjOVJ)ktSiSK;_y^S zH;&89lTa~4Z7)qgU$>^MF6n3tEmi!izt?d=s z?PV{+&#>aMx%exw%oriGrO7CAQ_4(D%ZXMpwq0`3u#lqD_VT$dV`}f*3Y8FF!$u`^ zd@E-8Ev+By_DSj$j%>n>PXwxn_sJUiv{)(T8Y`*AS50O(6;7g=*N!GcDa}#EA#xt$ z$Ez}qb&3i~ykADZD%O1J!kc>0-Ls*`LAnMPxyBe?#D8%X*P4kGnSb{&oYTq7K^x&F zLOE;M_auI{#&W3T?g!CZ5c1*-cNo3kzwdR5p$2G>2hO^D0+SISfAlDxl1)KEX87GNn)<<#RJ1<|}0~A^Na7s5cY^pR%uSPtBeg%A%L;1QW1)Y;Z=X9XK z@{kxgyuM+`z?oImLEW?FyhI_m7Qv_|$p>EgE#3>z60oSl=zon0FnB%^gc9qkbzRUZ z;k|{2?>v~=nKwY~6-VxQc>G=f`TXdV_dym}vaUztdgmfC^VDogZzTr_=oNH*6Xo?C zZbVu;Zm_oCjvON#_t;8rjjWv-qUS8sf+Go@h>+c%p{~+y_4PZmUk7>w2s3#$T#GfK zLOYgw&xWf6oP`ob2c%@Z;52_4qt3bX+aKCJki+GA^CRnoY^&bUYbvpmH+% zv_pBks0}j;^SZukLPJ>&=pv5`JBY8((aU6+9ntHI;MDI_Xvt*%5e_wDa{^< zV)b$*q^U53L}T3idNX;W@?5@dAtF*mu1z$*8puJQLJXeWJQb@P%B*@@GLw&a#_d0* zlo*z9gIGAHih{|r6*v=Y0mpUT+uJ{vAZbr5?=3T0y(HvK_~UFg(xwh}^@)O3_XWxj z!f-({iAe*VImbPm8ZqhAa2swbyQk;Hp&>VWm5RF^YsS3+ybFV|5MvN*=QWm~$|5ND z2H5dEL#B+k@bOjDq8dY}J2X0-2aB!T*%<0yy#Ug1llmJgU*`B4*VR^oE)jmpi!-Cd zx<$Z0?nGRERa|I)cM|q7Mx99Pr7OC5F>R)I_geMc1Zr&#^=c@%Zi;F)WyTq()YTgD zMVdh853ybXwBmiHsovBLYx^P_ado4;@$y3oGK=tZGDhUuxn)Hrk2ZGVUg2;gWSWR4 zB0fjLWeH}RY|4Nwp9&tuIx}C+TG2|}+&OU${+9gkIDci)`W%GvcgN}oz5Psge)qj) z6vnLJ%6H+#1@3&gDbm*Skd*nVsg8)n>YJv&nyb zk!qlTEFc8F?{J73Mcg{ogiNT{N zvS>87m%W>x{k?KT$ey;2Q!aX=x*KAP3YVOdXr|b4V}q>!7%L%xo!oJ%DBwN0}2Dld)pofa`DIU8JnyeyCiaN(7OJCCGs(wrsQEH4if@1OIG{i-(hG zAa_RxkBa$pE~8Qkp2tLu0q;=N^~XJH$3=D7XqFcvw;8>7GJp}ugHl%0uT8eS1E?GJM);-@x8##92V!YHt`zpl|~_RS$C(SS98CSMVdXUpqxt_9XOX zS!hqb+B1l&1LmI`Rb+Fk5sqR+V7LJ)P_5iFFyKI%_aLiz<1Y}}#mHelf zEs!C?!x?9sAC3pMw1@a1sWjW2gCXYUGvi1uGgYk`L8r&s&1I}eoK5N;%p5a#o~~CE zLFmyvAaTn+RiUi|_SVJS?$n)a=&@|kt4P-C_e-0#l1YXO@kBES%=V`acL)9_XROFp z;+h$!rY2t)>Ox+l<^2t2REGzLN|FRtGTC6uIN{v`-mk&jkB39!2J^*zLdCBbL*WCf zx7?oJTXb`5w7)`ynDEDyu@ARxEm^E7;$3%S&ruviY8P|hA4+p3Hq?mfPLYGNU_~Pp ze+(|6j-1i((!m+E*Y4>UY8RvK_O7N2qVbd= zL@77QE`uXg9?GqbzRlW?9e7@hmZv(-jEkMA-g*D^k)ig?DWd6Gek96Zs*CSEU`975 zS+`&G@h10{2b+kCB@5x9zdCzxtFrBxBB{a*pV-;5oTc)M2I_ZL!AH1m!Af@y_tIjM zV9A!gPFJGK+V7&7$2nl30d}Yv9zB7f`NW_PqMx{Ts7s8nuuf)6|lF zM20&o_ntd{88Sx%1M+J~_u`%4b%y|)&}xlD26+)LyMI$S3{~~>nGc%hX8%Zdc8<;} ze#BRlE6ppcMjYcxpU)6U&QsePhrb->{0tFdb2A_pPHo7a7g}#y3HIZK;a;7sLxn=+ z6G@whbu*v|g~-0N;nHZ6N2_Hf+`{D$O+$#f;K}&ZroeS(O~i-iUiT`Hu6#D7i9N_> zN8NFuu>_m%l}{-+cT?sy|EtMQZd%uot6Lh+;B$NA#Zd&JjA?Yvk$W3Y_8U8j)trb` zeCcp7UR3TG)W?OzVB_WC{V0Y{h{2^bed?mYZ*;>UA+dmsdOe`t-v?N3T;saj{y{o> zao|@)vt0FGJ?5+(_0d7e))=FfdLDoqrn9e>!S8WPCjG^Tix5|wSe*Jrfoa%T{8?Y` za8HJ}cbG@PP+b|mRY`E}y1bm=_zNWu+({+;q6lGXf1GOyx-l#ghAs57Zw=g(y)T#d z^x66zi6}~usl$TL%;L*EyfBv{!TwP_OZtf-c)WSG;Ir7ZCL&p3VHtmiKGPwewkBdO z!>p7szu0E9l`&R8{lt&a7N_zlkS$`LyvJ z@Q(J*ej1*5>aN>$l)Xyi9iehYQ8mv&d{Q{QgTXuJx)=$muZ#DOLtQifHjEgRcKP}oLk5Sdi3%8d4&1i#BWh1 z^A#-ooz^g(31CEVA*QsRY_1IGWhXbBjQKltO8BG8fW*zRPO_G3ReaiyVEym8%M(Mh7jajw@VD} zhD*8GI|IYp9*s{2y<96qx=3ZN){ajN{K9-vu+23h^+Ki7s`uH|&kx$y_K43Ls)8KU zq3avY`;(Jq`y@PF$}vTDN|#&k$?V- zFKnP`JZa!TpYed)kZRy#R*G%vb}>f(L^1kBw08HvbzvM)X35z0`O^)N601+{3>S`g zf8!($Cf?IS>|Gv6m}3d~bEt@+lMk|wXO&2EZ($)~-_(oj`0A(<6VQ_SJfz;xxfz$F z0<-CO{~_}_))h2=P~h=xZ*i)y)4%d!@tp3&KDH%tKweqf7erS&(k1-WOG2LJ&|&_* zGTw`pSN@6NH@KYoQ0`%|gE&{*bDdc1daYZot$Xnp#e;HI_C*@X2+(h|H!+Ew$}Buk z;y3e2{5R+roL0p@anTR?2kl80)3ZCuu*Ul^?CRbN+?C+J(Gm1NM>UT-%EqZnV+XU& zQooup+jr;YH05eGpqpofSEKvJ{=9Si!^StILfDFq z6r2(msOkL`2*&>j0^smKgs(+MbiM4}C(Lr{H!tC&oW&F@mia4v0lP(u*P*Exd;YTx z5+zp&P=LU2JHb*q>XTK8TZbd!GvaGE++&CAA2oI6>EKl;Jiq4j^`X(uA6-%OC}(rB zYQMdSgkny8<58O1#8|{$;bKUyYpbOt(g>yoe#{673QjR{+D;P(Yo&2lVo+M51k?yJ z?h*HRvQ3e6;_eiOh=w!KN{%z4*{oAZ`Omv)bLK7DYQH~@STr85h+*2&6?Cfa++1u@ zFtP3jycj|NM4#1Q8N%}qo*~>ZUkkw$ccQ?|kFS!&U8T%ynPtnh*jug`e)(yoAbb3` zRhu+a`>()#A7>arlrN^KHXBnk6F~x!RsPit4EOc%8&;sr@OF^6e481H zmDI`U8WH_+P^RhdY?RmcZ!497f>yOa6hR0-B&**cbqiY`X<{JCjJ12Hp{=3|Cqg zx7?^H!KU{LOMK_HVKqwtypvT8(4-oLm>kA8ehjz0bRF-xHr^)+(69mAfTc5O6ANWJeqIF(m)ebLswxU1l$wpQ! zPCaSr5G`w8JB`5I-i8uz!mn}-;(r2+eq4kst#vuok{Fi%iQL;1Mi(vM3g~`iEIKB2 zBgqJC7VNczp)$9#0C(bnckMu4$Z7p>3<4Ex`!ieKf;&#v8NL1Jg%TmZk4qa}KBMLF z)kXtTByijhyct;D-eXbM5&S_X^Hx{jut;}q1_`F&Q6G@dqFGj*&jEL|&e{&MHdb4T62 zUA%}lfTVv@{yfp>a+Xezns1gVelM~XxSB?p5Q358>p&BeeG8^kNx8V9^_!=#lFB&)+! zb!weasH1N-vE$UjpZu1a(xnZ^4Y{9nez&?Dw`}SsS&Z}4+cYp6e?@XAl;__ygs-$0 zT#IF-IXp8_@fb&Rkok$8$XJHJPJIMmd_!&KIw2Jk0jL}>Bk2lwtJTleT+Y4kk&%XN z{C{m9T2W-X9~kH7Pv5F$$59_>IE5F2+Msv2`XQM?@&MUqT#-Uhqb`UxW6CctlIKHT zy}e3IefucULZYBzZ==jRJC3Qcm1f^ze!nuo%R-YSn>1z*RpM%%v&e4bu71HGZ}1FL z9<=;ucm?4Jn;bCn4Z{G?HCenOh|sYWguZ%IcV4TEFyY^LwyP>&&0|Kz8yT~Fuju|Vl-xd!Q7(%}+)P58~%1K9!pDOaYE|P*7ss6k4 z78*E>^s77($MM!I;}Z-SDIz9LOm-J6_UI2{6%On2Gb4^F=KTtlW66 zWS`ZFS~I81QD%#u5i&f&^g|@;&T}lJQ9?S*!0W^W)1|RSoB3pI+goc2QM&?1+rrEestL~V9Jis8#Ur|$A4H-Ijz72aE$R8fWNN9vd}c#V zF{pMV76T`%PzxGU4$a_dp%-^rye<8j zZ7~KO>1g2mi(YI=sVP6Os$}C)Tj5w~y8ttnZUhUP0+-aT+asuoZ8h`8J%2s|&a)se z?s?U97ewPTL#oMi6q^V$%UYl~R(bdjb<~fOIU^-m^QJM?fEkMAS_Dqw!eaWiPkYR+ z^a(fGc{9&^=z}Eep`eAzd!XGGUeTR1n36FQVZ?o;!W23SV zA71D4Nb{GNqLoPfB-BgC)P-)3PWew_mCVk2szLXSO=96@3a_4)Q1Ncf(Qa}ZMwu2- zU!6KSRwF*bSok;x*Q7e%z#m-0>wcSkc*3hT;8V#8*l^l{Rdn&KGV zb{OtSj@>I}AwEfXUf$Ic_h4Tkg|D;l1oul z;e^OYDgxu7&9Fs&BZNUSa|BknzifsAtGry@!eQatEWYwYNjS#|Rqslls%^;O12+5$ z5H`hBvz9Ku0yMiWhjXOec~W{1Cd1F9AELb)2HKk7V+#?Ttie5x30AKuXCPX6X2dY> zo=h3sAGjm9EQK%bZdJWH#|}2aIW*Z6pZeX##{J|-G|Kl{I9}(lRgKbX> zHx$$X893*{odc2ulP?7cuS{u2krmKrK$%Z^Y;6d$XtrkyLVdc@+xJ~ECk6N9Sexa8 zT50jeQAa0bXsf9-HlP+D9zRo;Igu3ZdGD z+NVWTF`Q1T9#V^2n`JeEU4K^Pd?k?5MV&kU%BRrrC9uu`wT zPF}_g?twMG@3RU>PL!gzQJVz|ETIbL4kCLeI(kvKE3f=g^_biDLrdQv@{;GdY}Ajy&zdZb*% z053B|B%wy`3hmLp@P>T?!{C%T?~nu0op0HfZc)%Eh}&rIt`qhnLizP>0E2+JGXXE# z;$n57h0(;gOWSCne|mL60qBP)By(tc$Z)r7p1el0BazcaUZ}!+^4b!eSB_*u?p+xY<3LEyCNl+L8hC%4-IALv}kf@HC()rD3M5QEwu%M z-XN#_>pOp_$`FbT5BKk8-KU`=2F?uJc(3I zc;`)aFvrSbsqU+%y!UkU>PbvNT`&^(WILjMsD$nb5$$E}3y#wdk-){@mAkIMZy$Yo2l*^YGRHmY5-ulgQ zKdfGET(o-H-wMupr)Hgw;&+RnQ4;tH1oon@npu4c;!lA!!SXopK@# zDwj~%*w!&j*Sw%R+*UqVtlszXJT?N)yV*wi_<%|aJ_EoTPprDhi6SeR70x$ufkZVm zgtlscLjF6(opXzvM++X?ppn3n69B0_LrB8H z`h$KkWr{yhHKilo=6Ky+6#eAHfnnBtIrC${%0d#A0Cf`J#K zucZT00BXI5u(-Z}gYsjM$olhrfeJ%-oLvi-qthfiEl$GiL{>*BVpG%BqRDy=)ydbv zjXb5$Pw&Vo2c>NK$(bSF876K=je7bjUYcWIitJ;~^t%^}j1G>_ct!`&3x^jO?+ZgE zbS7{3R4ZbX*|c0^Qa@(>3bCd7YDgwLm!f-yf!9aG*C%rkSZi$cjQqJ&uiLzks>$!& zsjD_$VW!&^@Kr~}?A%ZJ3uvJSMQ_^CH;ByJC)hzs{?U=q7>5At%Y7DY4&rii@;hd^ z?0^(a6P{nf4|mYUq?9Rh(hc(6V!3B95H{;MXGp=voL?u+Z?nai7fA!yTeU~^Ezn^h zRHUr(NmQCxD1!bx(yr1ypp&mVs z&+cLok>=L;t1u<~EKuWqN^9=gFz0MwPD7uBb4BcZjab{k@zyt+`sbffpW4-d>((NU zX@2Zmg8c7VmDRg-zZWS&zkh~-(mD!H3O?iq4|xNCQ+G?ZZk^RufGiGOw_re4RQ_H98;zi6ZsJovcdvTQ`5r?^zuU2L^ zhJ1D^Mf0ogOckKPQ(2_CU~yc_CQZZ3%dIUAszg_I$WKu14n^MPCIaDd%{&db)+IH9 zk|b}X_33D2fMm5NEp}E)EKIHC1M<~=Tol(6LHWTt$UPi2Hz5kBM9 zp3`VXm9Sa;Q?yA)@Ni0ol>18J){!ES2L{4XyF!B&z*>jysMlf%pQ{{a9ySg7jg7x-gK*uUUQc9rZQi6S)C=mr$TH?!YTkW`u_d8K!T|l@I zUN;vN6=2XIs#5cETDfmFZKOmh@Ri0?9A7{c&1F=AZS4aBJuNU0`^-2oDy#qkeOd#b1i~BC(2%N^2*jK?)7r|+ZpAnvN zqZv2v??Y6QJo`hNy1CBh>zcUcKMsHqlhqv=nJro|Mi;y)$2ome^%q$olsc7U>;Foq(pA4U zUE6*Gz||_W8%ySM*I5Ap@4kENV@j3FSg0Ot%GT$ifvt>0a6&gg?XaLg_Jwn#?X(p> zHTxkhLL=|(rN|PK?(J`7Z)8J~oiDl0xl&B4FR61^;NrG#HS*7dGDDc=5nA9WyI3Ug z+hx-K0>wZ&zm@-D-VRjOG-K{YzhXT9yrjhX;pbg|+!-$R>y~PAHA9gEsrJDBCz**o}5c zxEA`Y@r0cwhm7974#M>L3=1V!Eir7;=E2f(YW-;;d_l3iK)Jcdaz}HOtO%toLiJnk zY8btN!HK*CsrSZ4GzoW;TlO|5`lW=UP#=9MoLd?TH+%i%s)I>7-QjZ(|)C9L}$^&N0 zA%&~!#t_Xd87PtzSSYb;1=^aAu@?tw+hoP?c|=m11a{F9JKH+X^8l$3mAFsmKei`N zMjI8?Mf1L}3X*(u*FXvX$o*TI0}S=O-5IPByU^6tnM_Vx)pjAv73)&q%ueFR4(lys z$uY>XMEgH#^B$fw5(va2IY?PIMKM-RkKC1SG{r2JDBg=k13>wh{I!PGC6h~WL@;_n zwQemwyY}_tVg|l4B?*%rR`j1_RyNG{#Tv)u+gFn4+cS=7%$>R zUPO;h36Sn1$H>_>?5Wv_{l|MSHkVzotolbXFCD<>uS3Qe3g7T7K6F?kI*jrlyt;^LR` z#*HWSDo{1Yl6F-*Cl@b-NQ%7Dh2~GMp?Rwuj@(bCapw4bQhcpKTkkM!e1&xZvb~b5 z?iER{B@UD2(GE&3R;^u?^pd|q=1bNN(?4cpzG2+@nRMuZBxi%iGsr&HxzT)1tH_Z) z|NOpGq`4z08lI=y7urxmR~5@och?V6$6zsJl^JPJab)TBXK#=KTsms2pj3>qShJ3t zrGs$tXiayBM5&q4Z@Kd~+38-q*2U&6jIg$iwi#tc(o<{by&Dbk0i=gLn9juB%&FFp zMX?V3P}&pI9EK*i7DBTFd7oQ@2?5nM}9r9M;TK^6zEyMNa!WR<~AU;$l zuCtds*~thn0%;In1;8}Guy_6lG#*GbgRf7`xmkM>IE4+4JizE(HI|6by=_bSSK{_%dx& zibP0|UZ1iFVwk$cp82#NSxd-z#T<>Z1C3Hyr&CUIgoCq^vKi2R(3gGNnKr~VO0N%Z z;lWcWn>Ffm?Q6=&$j4xDy0I-IJ`qg+ToP2&y@OXX6H|D0%56*rU<{NBGiI6QVMNZ(;q;{M*__$hAzOS4zUM)U)8|SDVUAwGwe#(&AYTd`oEkyb5wen2Mc2*V zOts>v1a**=V%fUd=+7t8@+R}2jY>)zbg1HWkCF_qVsoIyJh@sU)wQV=zKm-+Cn{w{ zGCB@fpG8@xvPxbTlmx&$IzWI&6sI5vG?fSsaGB@Qj^&=9}+K?eIK{T`xlPQ*ew}K3XILB ztl^`NZbRKMsmAU-4N!OWr!g6nWO}hCqU+y(CGp_9c4yx!EmG5RHjF?yAixTM<VCU9oymnZJ&b#VGw`mt$J_1{Y%9#(-3cm3Ho{k!f1G z(qy4?OCj8TqQ>=9CM?(~W5ei|t~HU{*zm-TnxsjW9}RzM!QeNUo2g*C-pL{;GL=d^ zPz=W?qVyYC%F-rw(GsiMnig*LbrJ?oM$!Cw6}mng!~D}Mb(Er&lhbpg+Hk`r>V z@vjmGv#Amr#b%b#SX2DgZN#>%YA%7Pd!%|%=;ML^6}O2*-0;iFNLj9wGE6X055Yb} zm!9R!KUhNR3<~C4zl`9eBj}Q^>Z&`5Pw3hh>y`h+#-$bg$DkrcUIKG7{937v;}Y#F zsSw*cj=cwZHP_gU(6xd4<*R@9_MkJ1;&5u2WgogCsZQoUgE(`b2K!G8r5xOlW38Cm zqV+*kUO&;sP)0hsyomfRfGRdh7V}SLX$Mu)o^`RkW2+dgquo#rBlL7R_iS@$hujl4 z5u-p_oGph+vX(qPGmZA;-4Nt=uq#K5T*x-PW~vtgr=>V&^dQmP`0dfKcX}6X(A<&Q z!>v`cP9LxIzBrxoah3;IOgqe-@Ez#J%=k5Q%N8u;Dv|7|>&PC|oftt_jJ<4+&dR8+ zS+d18_dP}fOs-v1QnX0s8+oQl+1zzrPSpHZmQ-kxDEx)4d!D{avSjf$HH~Td3rez- zSO%N?=>KFAooOu1tca{qs(CSF>As)rHnnChMp-ObM=mX5PHJb*kE*iLR|)cjv;6rR z^K>t@_Awuud5bi;YP}1hZQ7(#-z=c_8(zlSl8h}lIEs+bydZAYk&I$^X|3p%3M8y7 z$t%6^^)A%xpu-h(ePZ5M&`as3a~5Mv@6IX*-w2NchS&dc*72&jLY*p?^Sl~ez=YHu zn{`Gj(&44`57L~Hm|yP3=a;}kX%8|M*n{@-*KnmHgEd@jqItiWAMY0QdwnS_=W~iE z`_$K^#NMr$6RqAyvVMMyJk{*bqRPzPBhlab?L}&^BFu-LAHHge7t35@tJ_8=hkQ7J zdENHzgGp8W58Gcw<%UYUDk%+K9ebcH^3uG9{`z0{+9J=_X3B4j!0JK3DkIUW2NAAG zfe2LKxooF4&E%lt#s^3_X~0Z8O&B4i%WaES&erSSmUR)KWM@vrNeLZA=>9$2o|(k- zzuv=x&?3T5^i01J)}~*wkP-YsEI4lC_80S5cp9WK*p!!?Y(9zwa{Po_yLbztrKZXt zl*L{;3j(#K6e}z)gOZ#L?YDX3b4u!&f4@PNDx?t8UoN2WMb&DHL$+Q?8x8Jk8x4uH z99z6Gr8DLHqh7I4%5u#(9qVO^lxMYd{Wdt*jHM8^iniUsu{&#p$i z{Z<2f;`FN(^S+eFN;xYWCh1_t0#;}Rick7bQ`MI-M#{%r71UD_5FW&jnH+P@3W=(d zFmgWH2v3^0(MSmQ=4LTDGl6^44`@@YaQ?*-T9<->56fR%!2b7@Sw#f!WBhD}>tRyT z$j!r={-evY;Q4;##&P-d6&>cWSG&hCU3Kw~{kU4!ra8GBFNq)5Na$ba985`pwCNmFk@kh{4rntsbJcn-5j%SU2ON*vcG*6gYvv0qzxJPbqm^|&>Go6i%pfu z;X?HrO|ieo;~*x+FXPPMm^SK@F|^dpqvys&)Vj5dhx>btv^G?)zq>qs5B|fdeE^YA z^pVzrSgKXB6`BJr=gGCOmGay2GpTfz@Uv2SMRP_G3`mNMB>l)R%44N+xGLd4&`E#o zBIsCTgBh(6$);;u~I+Ri``%qF&PQixe1bJOWoJLV(m2x>`SjpcGS-Q`=Sj36pjv8oM?&do%!~)qv?mcQT>8^Dk4Lr%zi4um^NFKqK5ke8S0QTOnWFm z4wS3ghZ}SK8b!}sQ!pPsxA890Ev(;bOJ+5nW0&kxTk7(7vtv#yd~d<}`OzUOk>+qE zw~mBa(x@vnA67W5sEze)<9%~6~F9XFVIO( zIa@0xQaL?fp02$)bG%2{n$mtO#v57ooBpH^ZN17wT#QVmJS-9dhyJ~An;p5X*P_3r z(^^thuo4O{cD7%xfF!VpXs)WHMdaKA6{V-YQ{CBI6|$(6X^T9b96m@ID6m>A#9^1cC6ArRT9D#P$1ickJB(u|2Bgg)s_(;T^qrtTa{Bh2L_1k>sT9_J20&h*{(ko1zOCc(X-S$Gm$Pvrql8FzHfX z30b9FmSV`#qs!*jZpA1IwH8(1U>=r1$>v_1*T~Eqn(I}wMOMC-JAY%2?v(;>Q986O zKYH(cu@;TA3R5_tfsd!rL^tiIy*Pr&VR@a>YIt*rV@oZ@$FE@M>@f^XX=Rk+3VQK} zO+K9bn|8XAu~JH3>9e{27H3T7UM|~XkYcw`3)aqBx#j97OZ=@>8)<$KG>cT$sKIZ4 z(14zCC7o$}dMAw59mUuOF*n|ol4*bQ>-X9;zvQRcYM@64n5rTDrh`Y4d}7kf#& zcf^(X+&gONO}W;26t^F}i^dn?v*P@mG#^sRUe^ChZASKC1TrAN3V<2F;ZG}vz_b5Y z&}Hs{cWZoUulr#5y7&;-n5*?Hx_Y;8wKBuvkY^NQCw8JM$=IvmTwA?(^_41Y{`PaU zAalUEu>!NF&Zt5rHE3b3=seEsY)I_u4n(a}B+S#ZLVS)xXq$bWe*tECY;QPXe0MIVh}=bK#yS<-dkb+4kT zax+;QBo2lIqZP}uIP<=|KsSiCVqRf%y?Gj*nQn^|tZb@?9rJyW#__7@D@7W|3-@J3 zqIiFL@QBtxl4a$>dYJ>Bx@L^mG+>sl+8FMq%^)3pn!b95Hi*uk@$5Bp*5zaIq_7fN z8rxBccgf1smzZr7uIqbX~X~3odcxoKa@-^`2I(j|53krUNnz`rm-3gLrBoKV zTzYr(_jD_d##5}@bCiLVJAY$>?v(;>QL3~pH+t_z!wDbB&k{$LrY_^2tXLeH#(*>j zrCK+ZI;+cr=JVrdcsz^IOT*~9t{j6(gn2SeMkwfSYa_1xQzt4DER>W&PR?=}(>Ybj zOkh&lD|E*4dbh#7MN3y2JNog_xOyYN(9!p>`wq#f@|gF{R3jP2#CT7J&EUwb-)Bh3 z?)qlPTEC3T5H$TKv!dSgDU{R~%hxeB%#X|S(yVQEf6GFYOJP2=q&haV4QT+Ph7R$K zD2Mna<`6mTjUDt8hLMub*05_7erYD{b$DwEm>en_{c0>l z`Okl(8;AD#Q0X%0r>`uN0-&;4nVONlO{}6N`gE;sYsZGf)mC(6N$CD<3|~Bl5zR^C zAi8KVhU+u%>jYb=D6C>19WUWMMyIk5c*>~4x<0)$kJCEkKTlnI%wpKf&%09R-mkrw zAO%2$gIno}dk=P_v0?;WgNo3A#`E7)W9EFRD*V_x-`hHl%e=2>`f3rzapcYrS}rR6 zQq6trR0Cdp$znQ6SEN+e(N&!E7kM1OtzUeEo~znXI&z``&u|0Xb>)4e)?IfJ7oIN1 zHs}>pgHxmEp}*9}hyG7`;Zp>UsR7E!$70%{8v`*j6;`03OSjzS$mk>5luZbxV{q)M z#%9f?mBsCl59Vb+i-pyi!HeyvIY`!N63?Rr+FE@t$+sxbb-PBgx@727ht+G>)Eunw(aL;j{juV^6C{ly znUubVPj-a7t8Q1QdEJ(SmT*kBxp2A=CR{-acqM_x~ z4L-bo%!~a={$Px5UTMS?QkJpv$vWP1%-n3hSCTE1&r#AT{8uOY%rn7-FH7|Rbbb}m z&xr-^Em%H3>Ub@?^#$}WmcV?dFH+v`mbXp3B-?80=@4Jy@XW){@!)AQS(=kumUmS-A|tR?5%|w*wc=dw5=TID zdLn=abY)vH9y!~|1L9w9`uu$_U5{3L*>6%j^;8pQ@D7T$UR(^ z6%aQyWHfQ5gC>&vqH7b&Xo)Ue+uC{^UHR}*EjsC>@#NKh`013E z{B*rHA-}Fkw@kePQ};~r5>`V+S814>8zVB^rfbeBB>_t0t4WPTV$Z>TTscdML`KSg zq6%a+s#G4|u?rYF>WBYuKiaoSrJ6c}j_V~#3yUz{$B?jL9G7!nk$OkcY3n$S%%bh+ zKr%1Aqib&;Lb2=T^j`{pcW&`QSR0u6>>AqZw3=^YbXD-YP5BmHa`P!vf1d=786FQ= z1Uz~E3TEc|>3ZO7>$nUFmC`30XoqGDShgDMQmsX zO@=03%7qp?J=L+pbxM_<{Z8(2imwy?HfcP^tmMSM&HhT_&q=oNTH=}@C1%E78|FYw ze#PR+HJO&e3*96kt^igZiJw%wO8)ojd3+>a5l%>3F0{=Qz28ucx9iltIQ_H9gcVA%Zk$vGqYDzr zx^ZPLG;h5V-kL`A-<-j<^X)W)$W;GO&4~lop|=2 zgv=oX2g&Mefz6b$_9n)A#`tMIn(Ft7xP|gLsYS3x_L-WlNgq0FxvC!BoM^?~f=oIr zpC9iAuuw!9Rw>MfR`xeL+D5GNRWk@d%>IVWoGU!3|i>qv`Ni}9CBVZQ-tN>^ivYc)~2sk!u)GSoSilnqs@tjobsabOj#Ns?2 zNYPT9WQFW@a)JXq$4+IjG!aGcj@BwiN}4%(L?6$qSL{&S_J4Rgv&r1(0w$Cbt~usa zjNr~q^3p4SdxDX$`_U2o5+|frAk6(T4HOiPS**O%I=aa_)QMUROv|DsW1uyhYaPa)B z^lrUVSi)*LJ5E-K~>cIq&{ny)=IZ zF?vVriq0pm6%zRPezIF5JIVz!tL8vWo-4qUYw};gm&)%-avGXD7s_l(nH}uX9DKp9 zckrrKgCb9KwX%z6IvGl)MCFaSOyT0+Y*@Qy&osXf>$I5MzHjb=#n5juN?-FUOO{xU zEq+{}>&(<=6kVT4G6MAjRzy}Q<-bz0bg2kQXDO8>Stuh1-Tos>c&7g)kjDW&JJ$|I zg|^Pm4BcCK-lB|Xo6dq{I(lDAH)|xHCj*!j>(q#Ca(gr5T~ip- zFWNahUB5RTE334VSdysBT0s3>JZ5yf)?y` zul#&yRYQw(Y-k&itaBT7ZxxRCGxt>pp}?H-3T)bkYMJq(FAi}~Sq*{PqzkeTlw_h? z>#;4{R>ra5HD5`}*x|kkyC+y#ol^zJczbtFh#BH=~%m zpGFcb6*u_VFfwkj(^VGo{8W(*Q__gPJSAmd%1Jq7x-vt{ha=mSlaat=$!y>oRp0HA zxAB3V^S7*9d>LgZ{2ZIHyH5HzK7xB$yGMj9Maau{DSj@_qO{)-X0vYkMsfR+TpusI z&&$1=`udAi(k2bH%@s2z*h3iZm6fJm!!{iwD6V6X;hr#YDmQTbqs(K|s14TZz%~k# zkDp!XW-H4&H>&nZ!s@#{xOKnGc4D&CG4?$R4K$U8aUe~PSg%=S8blzAuh-8B`=#*)k=<~Nd^($TZj64V6*7DMUJcKp zwWI1pMJ8T@Ds=Bx@*H7z4Kt@7zh)&7hECb{54LI|^z7>>0&l6wGW&~$;1KIkq4UtC zU>(0!_&6@-zM_LQ_G;HSp1FEyh_nyyb3R6;EZoma zn@_3=An^-X=y$e0S$K&GOCfT9T2DJV1)GTiYgIZ--QSE9fAv`q1@; zzb%(_XB4xS+R$=Q%d>aCQyZT{)Jo;$(rjRnO^nJ->DH7{+@n68d^S~K>_ZX%Pin1cIO1bhY zB1@MGAh}DaEK;#joa;8@)jG-d?ioykq<53f^5bvJ)4ihb7FqU6%7wO>rT2~(Yg3dR zf-|FPZzJo*ujXRiSdFQ=NCwh=LR#r%n-j9}H{~*>RRk<^ReW2Ps|T={gCc*nLb!I( zRr6b=P$aj%Fl8{oNI`OUnji1Erwy&5j?H|IQTeQpeTsB*zSU}J76bY4C0Xd5Q5}os zXTv(i>iMy0*pXL1XuwuBRp&KqkQrr z5HM%u%yOiN**J7o76(mZf7eLv$~-w1%)PT-GEoZ*yn7+DC8O{xPHQaZ7-XnZTw3@| zCP_jRW4$dJYkdYO8hFu_8JUEua-&l0NB^GTbnVhS&K(&r$fJb3+l{S@a#y~U6UCboS%W2w!^N1I|OH#*xQ=)QL@7t zS_itt*liQK?a&8eV$4xb#MJkRj5iT$@_AO+`rF#?(6=)_j{km@t8xE zrh3WP_&=Gd3`*xkC+lfGpj3O3`n&b$ZubNH=+r6Nj;C4jXNn{e4$4)!31{Z+xoqu& zt{zSIKBA*%T)n11$0*0ZOISkJ?sxo@QXD|7IcA8s)7O%*`YuP3)| zb4=*dnmeO6>2`pt8Y@%Y@w+LK3%%7I_u|x%_=LB5OKY@iopbrjwdIEPl;^#1&$R1+ zlcnpmEbYgmSxlvE#V%_u_T$j04z1ko2oj%HtLA&7+vKv%yIoZPwXCaCPl|mmsM)yJ%gKGQ!OO+IHKuiv zPEbwIl#<(Aqva3maL>Y&2>s9w>HbAp|G4A)Y)193+hqdu?q1uG2wQ?p>X!XR+Np^Q~MP zebJ|rJG7%S^?l;eJv&?_C}%M@qbFsab8UUZ`N4QVb=y1Qp9fCJvr0$yC+JzaBL~se ze9B8SR|(oYT2@o<>fi96Ta#Av>>AP0kvG>jIXLd+>e|A2yc+HOlXF~+*Rd^^$9r>h zU+D-eAB^yvSLK35yHbmKIyZ~ESh22e*vSo%2n%lN7%c2vpP~1YGMi=`EN1z^J9d})%kFY z*}3G&vc7JHb5vaJxMn`ws+NP%J*9e4;*a-kGI1G^CeYccR!1OoRcYdBBIENH0R$2Y z5CE9iWm@-jT|0I*=*h#WFLrW;Rdk#C1-cG5siL$&hu%!^65ZaE1^RTf(*zmZQL4Iw zsUI9A&9k>mwa(zB(`{P$N83zM?C}ry`j54%q9To8y;((Cwjeskz`;7TBnW^A_4la7 zJa}yAjEmt?eXpxf;xI|n<`>T ziWn==xUE0)mD$p}o79uQu&=Wv?CV^H#(T@0og?kbHZ;1nx0VN%YumvDB~h8`vD>#- zX#MW!Lk^i&-}l{?;DwrbIC4|DZ)6@a-|ej3(x(2n%{U|*Y*ewlF72BzN1~>+L)&(D z$3Mt*k2Hp^T&0^vR_sIQ70&TGwEngVwRV{R?*tExLcxHkhP*>fn<`YgDZxW+XHt38 zp6VDl_H25z@b?@qCd_u%@I->r3Cyc>_QlPP5Z;PX6*)^-I&)O(HtkUN@gDO+%an$-2$fcdFKLO(s*qL&N^)gNTXF zYSz#9+1iiOfSGFBiY>t@tNXDDczN_Lcfd>2lQr&YD?P<}_$lWL$R5=^IF=yQklO#O z)SSaxbjVblkt|NZ20b*~-{APbYOxkx6Th;ZaS9y|Z&P>5hpX|^k7u7EQxf$^b9kE; z-C;^re4J%a;0{VnU9^rt0l= zw=Gra-3{7%@Q`{>^_$~Pf@?inuc_7fQzBi7l zV}Gma?k?5R73j?F>Y3b5MTD_@B?OjLJXC6o-+T25D`gLf3 zlNK$ncRiOV-?B1kP+!OdbocStj6MZ-UaAC*oP9bMYtSS-LX=j`n=Yiz8cct)A;|*GA_( z;>i{*zqLW#37(sW%=Xx0&Sgzc#ymI7lk1yY9QSf{ZIS-_r-?bXnBJHA z?9-;~`|3j1^O^R|Yqd8)p%$StecHRlJmZZiIXZdnZ1}CCnZWn$j$9;f&Gvmv*3XY# zRc>}Za}TIt#UHBuSbPkFede|8j^$|-){vaTIV4x-L+3okwLR9%EwKCm()c>HCq528n=F?~zsd!n7RA%H;I0;5#qm3BIeIAsLPmv3LM(Y6O#N0z8{ z({gn$snkNBsn$<}ddURWJ$P_W;;&&xrpiK#IRQQNY@=Q_FUz8r)g7w8*E|xN=U1iq z8n*D z6pLO`)e7@flEYd8Dl z2@UAzKeVcQL;TQO(J8a{##tJMm|#o zabzp$*;A)Q-Q}t>+mJ1l{X|#07S=td`iew>iOJK>Rl3RNn0&~x%?DK7y;5x>>oK5~ z&8ubh7ISP{tm0XN8tCiOiQ~N^4&l@moHa-NvF$C%bCaDR865*JVU`Ok?vZv!x4WJ_ z^;){eY|E=|QsFF9%~k))YU|AOWkl|dCD~lpUgOri#{_%q83HV^*8{N4+EarJZMqf}ff{V>v}7$#UDnHEMf!==kfO>(RhZ6CHm`wCmx@xY>Gd zSZYc$Y>AfAYOXa_E@=rLcdzwaX{sgb*Zw_OKIbJ_j_maIs-|U^Ub-#%0Y|qjq4CBp zRo`H@_z7-uaK6vRew>cV)Z11}No85wkGmgR8{Ll#Xyx`jT9)9l!gE8lDXG%jdqhex zwn3@~OSE%Era=H^Cgw}qdh@f5|K@Tn^qXo2ubDk`z0_^4mz)8>W}NaZrUYhZ0Jojc zV_rb5^_lZPWbTj5*TRwG z>ggV}jT$iESN&s+s$Mb%Bd1uRkKBLx0Uno;q_i;&`4CnfCTuoF9m}r?OqyItOO54kEe8<~4aF&Ak6>K7Osd zStF&`1AZ1M+Xn=u={;-$h0SlInP;~5 zBD1~ER)XvM;jxx%HTMU}&o%EJdtoFw+p_q^YhB&xbmZpVHJv$+VVt66+PAq}tIe~R zNYi1S8+B|iQT19|+LAf4oH6&>2Q$6bj-;0Dzdqgb=ti}!jg}(QvTlbeU)}1yewvVH zm5ybdpl9ih97J35DKF7nC1~?%&0p`-3l+`}i91wxmwEqSo-f;HgZAG2gsGxCaz(5K z^;&?=$ae>N{gLNmC{4P3HYm>L#Gp z?RDm7$MUPZVxj!S#hUFgAL;fcdPZqJwqF{5^O^DW&7|3an{WT*c-S+M==fmYkQoeu9#r__C5n^(huh%)L5HID~XEu4D#){A_@2&Tk6KJdB1Zpq(+%NeD zg2`Xr+aZ}Y$)@qluT*Dek+$7aqxQ&)9Gm!Q^JT1~=-Al#y&FoFV(gd+y1nKl+l#Gg zxUI(Z^=(h5c~O>N^vW%}R2AB*^^S*Dv7;4LY}D4ycJqMHG32BvN;>AnTX<+iqq_dQ zNOeo27yXH~;~zemHKlf^iwTe%n%1MrKX26wosPgpGqc{q?Qy{yHV>6s4(K8OQ7v@@ zx`p$Wh5r&SOKiePzF>~}o;bxgQ*&jD%AO82^V3{S5$H|sCDmsD!k!D@A^F#FkXhQiy1 zb9;4Uw0y%Z?MzboUt~1jl3Ej>DHEXSe6eP>&!k^Rm22lg6MUQGE0A!y_Ce|+N44_d zNG^xw)_t%o8n7G5fcYEVH-l#1jH%AYG0ITuoH8|UC{c~GI`5cmXz=~?wn>&Rw6J?7Yy=-3t>=}gEmZnQPJ?L_;H)|TGBKUylz z;h7u*ho&1#zU`No`!>JedNC0`)+Q`9%#7Rhpk9BlV&oi}Y@RtgmSk&Pe`?rqS*uATAK+K-HpxMeJB zEA=#4KHq0+KTf>(?!J?3TXAPTSyy)U+T@Al`kHN|>~n=Dn2G>=)Sl86jT z&nC;qj>yb&a9=!>bawWwNH){`Yc0prET~drL#--r@QpHgj@Cma_Y^rg7N=bB1m~Ke z@lC1XCg`s>*?qaaU;K+2^C42o)WZXde!tq3BGB%-=ku*ylREG?LB7UxEo*a-$vTKE zPPVSehtnJL_UoA5d7g-5Guhv<&Cjc{J57~O$0Oy~wVN6|Hfl{KA6Z<=@$(Xiz z_Fh*lzi$+!!Nm8+I&HRf`%3~^zP3q?4=hVqdg#z30ln}|=XttXC8?f`B$-dH?p>X; z8_TwlY#RTnooe+onER#3He=_MEO_7Cvpid+y?3o2ITj>K*O}ks?e05=bkg$PSgpp2 z9gdHy+tskIO0D<0gGO`ttkN!z&1Hl@Ft58Ith z&8o7L`?uoqWlBfxG(Tq|u6CccK4m_(Pjt{2!}ZNX@6+iVt6hs9(=podGiLOm&RGl1 z$71%G=F^nBRxP@Br1ag;nh9{YRT_BEW@Jh{$^ zrOVp0&*6lZ+2`7wt4H3Cg?oqJ;U?O{%yr3rb6v7(^y`vLj(4%kn5sMD^K-h}P?qmw z>{Uy4pOJf2{bZL4x7?*|ozZ7)0do)1IuaDn+ye1;ZnJ$(FpBxbfb&MqSVDR8?f$ z*wMZ;J36g>fper&jqA(<*(6PPnVvG^n6Hw(MbT=5y~AIu9fJN-Ap9xcfko2hh&>q~4CbW}cm02edK8m*FKVYSsGO z5mnt-timgzIh#Am@+4)+!TKwV?1nd&>89(FhU-IgDkSn0<|7|4FgS+1Zq=*s3F) z`%<1ShO*-~j`ilH*5jA(5HXsW@)Gj}xH(?h{XNw>tX$V*j=v`O&#_1jo^Y!TR<+Ae zTM@s0Bjc8s4;}V(nseUuUKL%xIMUk`NF2@6NKbR5a;!0B?^x$fH`V#pTUujES&Qta zHfP=1{M6`GtVEY#s4XYkPd3l@S=x_dmpfVasyYJ)lHddYW<5U zvlpalinQ@yqw9LC;qgQ6s%o7H#GPY3s`{H<+PJFRl}4gDmY8d`UHd!LxxLD@U%JLM z+hnV~?oE0Oh`DY~nYZDT(;3H=33Cold4CX8$Cf|T%1zs%A2paS^=r+wOyeW5zpdK1 zwkqzLuYJp-*{gzmrpHy{Rm$tLnB{bB9C7P5c4S9)Hjf>SWR>;%;%MTRwr@q#Nw>{) z{!TWLEyc@}m|3-|R*jGC(sSne=GZe^(+K_>PB2I7A(MNG>>rDhVGZ{;FGU-lXf&nB zmyCMMu~XRN&Q5dOIc7?XFLquKq?*kvziz+TJwolBH@SPnc4he-n8`XA$x1e^$%hlm z_?jcr<7|QRnl0wIKC(cgw-n#zKECeQm*Mr*r5rzF?v0b>)@N!Iu_c>VudCNi^L(i5 z@G-TTQgBBN{xb=Be!EJ$wr?~g9VWjnj?Q=ZKFM4o?@Sr|Cxza6DO!51Nub2k8d4NZ zD5JLQ-@A@s+sNRVZ;2^gwQGNh=bFR!+RvGL?Xvl%l*JK^v2Uj~3DMpJravQyZ&Uf;wlUo3s3`%;d3HjUg|TV(eBDf`E^h)&|axjNoE=jOR# zam@3wN{v>!vBy7j9x7ju;vtTmiP=tT%=7k6^Q^xrMIio|u5Tvr_>%eckYi6x@s7>8 zUgFz5D!;qd(Yr_O|F_@K9n z73ya5*=JbZ+;eO-?}T@4sd1g_9c#t3ns)#j8Wp}SxqhzWT#)KoBD3S2dw$8f#&CX) z*@jY{6SH_96Ip`no*&Ko_U)Z1-{;xmVD0u+^S-@G#d9JrLQQUp9q-H|nM?C;ZqA3J z&phnCWmH_*)&+_b4uPP-=@1}5kl+%aB1nRJaMBP6?k+_^l;DKm?hqisy$a~y?k<7g z?q2Uur2F>mZtl(f^Tv2@e18~KMb$ogFPUquwbwpp)+q6D@7}4=hxbjZ`P8bZt$}yD z7_IhTJMn^9TWzZ~$@k{t7KaMMzD!>KnyE++%7}#ryw8ZX#_QNNVIacILBxBj)_-6* zUpeKlf|H>b?KNA zcaqE0-LcdO>D6OCL`O}d?&d<6J!uU?+X1gOJX z)Nt^fJg;%@ZWMgPqF{%>$}c*@RQ1b#CT;rrS7k;uEr{YMm@r_T`NEKDG(9}R=Xi~W`mNYtOh&h5 zlS4ADcjbDg$! z`Ga(Gy?WfZ=EsafBy~!zv6RJ44?P0z1{uLJWE`NK(H7Bt8ES?OlY|7LYcBczasx}z z3As1NdN&9Bj(By3Vtkt8bRp>#FI3%&EfY4loFqDl+*_^LNh;_~%dfqb5^S<@(4*l? zFC)Ab8Dfoatv0ICCy1Uss@edN@hpHnBwlnuCFd)jrNq`QP|M-;^z;&&(~BC4liPXU z3>A7)gGF6Ds=VCYm>CpB_$mV~%of{pjYZG+q-)gc3*(l|D2ZK=qcLM{9#9I7o=}zh z?Te6c0cA%a;pnVFdH+K4id67{N%VT>(m#J!_fsd>S@MwZyPYO0UI@ zsTq68MVxRg6bg?KS`{dr$?VB{uesY9d<)BnCy{S-J_%^BT-X<9wGS#0l-;MHtW|8C zrn}qusV(aH%x+As$B=GbzKnOn5Tyz8(I&nK(A$98#b&f4W-J=i)oa8%z1W#qEmTO> zXTEQ<<5?jAWahCe6Av6vtrEFRPsI&!^`{re_rsRUW zL?Vnu8jefy9NmhIg&a6WpXW;!+2$yi&Xe#eke8k4>Mi_MXS_7Bpj7#AgR(2;fGL#u z^mJBbGI-=A&%H^>d9fY{W7GMki6-N{Q=!vZAhTGZ^v+Zl%9u4YX}uo)DS?o;?SXY; z=W=C>Jo#fXRFD(}1B$I9VA}p#|NW8KjH4ut1N9Fhu&%{qv=*qwLq2k#(g=t)SW^`z!<#~JG)&Q z#ik;S*?SYCQe9bD(4KR9q<2An=~q5%1|JkORh=Z5MeIkLAdS%SJuTJT`cu};yS=1( zR%i36Mc;#hI|bJ79s~(0ED^z1KRwQOD4dmHL;eukm;p=%r!>6FhIaY>VzkRwbMN34 zsI;QA9kNRV(IAYGuxv9TFvApvd&1O8bBck*N0Y)tQRZ|fUC_ND4pk#bM~#}`4W(dK zpBUyZ@SZ_wDPM$Ti9O*E(P`vU|5{*7xEe9`a<^b=9fXIem4oq+t3cCM`Kwos0lqX+k_;z6Ny2361L^fY3-v=as)_#0Rm(coe%G^ss)`6@Cg*)&6#T7Ha>32~lYh zabnL+ANJ_tV$JN(=`ZM`c04iZt(C3WdhwQy$}BcI_rK72eiDg6gp2O(=Jq(aI-*!H zqUkOKD0TZH#RPZ7B|;HO7;VS=(ZzLO>$yTC4}-oo`v0=r|IiD+H;E;{011FvI04FkNI5k2rUPD zczdizX@D6Ealep63f0gP!z5^9HH!Lg`eq0rKrMKJ_zfg?Ph9uG@RCNJZhu}($H9_C z8K+vPO+GGOT3vUNuj#Slw-Te#lMeDdp&WwJiY!E1#D~hzz=8Wz*a2D$X_F0H7KVem z9W|9o=rt4OZslLePBY2mb5h)^@8Sg?y!`5N?~`6#L6*gIb$-7zZv48S)@HDaz_yFG z>Eu8JdyYD9(_&neZUjI7%~M8*CWY`rVBU?lh?X^7hH%{c^9NRaiy&wT{)9rZw{tO<#i#%bC}Bpv&I`y$`2hRqN{3w03Fu1Ra8-CTGAJcc63@;gRF z+skV|yuH~J+8ygrvN%cGQuMSZi*bxy^3=fo=IejCk})&jWYKPS1^#x`xj-LHi-dbA zgVN@_w{===p5R#aI=l(Kei|vb5*ok|@)WMAY_qTKiy&U<2DZDA@WPx5{{C7Igph9} zlTP^EH=y?@l0fi#2KS8#|7ME4*Rit^{W$T9YeVqpB3y#kX-YDtSm`5^?EEg?Uru2) zN?#?_`aT3P&oXd(qh;VuYZ_=6Abc==e<>P!?U-&;Yh76)=e<9)r9;q;^JDZv7TbIS zX@fOo#1bk-hu7oVbILt?-Vq-@zH5tJy&F#a$4bF!J}B@k46DDw%fF=aPp{;tP;*cc z_;URNE|vBND*e-!|MVFV83PKp`Tc*e?*BAGxD+tQ$3;2KxBr;-?fApveP5H?EO|1IrLlKuab)-u?;4?O*z$g->1NoQ-=Y$)BHz@hxR)BPJ0 z_&1k~dW6QyqO9MtEQIs26U0YEBPU*lmi@V#y^HMCcIWmj`Y!%kX{n0VgU1sS83hJ9 ziKBTr)YbWS3kR>`$E2a*#t9%0i=S4*(qh{z=YDa_Feo_K&Dv0diBkoM@4DuHa9Ot` z(_0uS8Pdy55uS}swY)=S_9tQg7h?t2;s7l*HWtS9J0!glgBn>&#+8&?+4gH*Ir?^0 zPyAItyYVZ1`L>&h@@1N139Ex^!RkS>hWgJc-`l^NeRUjczG1Mg)HXBOqZ*@`vMiyO z$`2`*lPr>=kH34hO#T|-QJ_x&00<&W)_FyVk%rO#CG#8|%x?(f*qj z{&vNG{TG_e1jg}EweXrZF`GYZn-ygWFZh4Q-kyYLVvl>P`-E3}Qbo9)<`MRG)0e8_XP8L!qWQv$@Lz(JC|E?rMm` zIYx7_Rc!MW%kD|C?>Vf2I2-J@dk6Cu)Q+?h`Eq3hBghr6Pb6KtSKLr?=UhF|+&hWK zZlE?f>Db+4w?0AIarHGD8Cf}VM_bUj(Hp64ew=zT>FpEC=A2B7*TAr#ZFaX>JT<9 zAsW+^`;Gd|xb_B@om^Bw9`F>T?$Cm0MuVS@AaWL{Pf+3pb0unCxoAwpzVQD>Mwqi7 zOKbiPR3FXe>u2)FASIM{l>!%&jIZYOqgw+LR z^fyimOABRYDotvo8YZ~u0ey|*3H3dtuNTYL1$Z}j@4w=74N#OxMho_I`1Q#xD{A**|SZ@h|4yFw?BohLg%h}pIM zx{xtj6tM_n-YHGO)Jvj{4iOU8VGzN!-4e{I<<- zrq{u-o?qYEqK9{^hK&nmxw@H!By%0aL}79yeFA}`IH17qU~569_<6E&Fe zE)VI$4aeMUUY_`OUv++c0;lc_r7g9Vj8A!}r$xK8C_K)2!W&~QR;|dy>rt{_IrNB&t7K2;ZmUiY>#KhnyH5K{X@{kvs6fGB z5JrjnI;21A_G5uxSyHM0us~$CaP=SPJMwbN6khmg=4Oqssdb+mmfvggPlsH zGnOT)8SifXK^>^ia*l z_e2VPhH-Kpaus@&_wmiV@)*pWdB;3KOL8h`tV9eGi?{6Aj+ZtHPyTsXRFlaNWki&@ zF&VkBcNgq8(SC2|xUE=pti6D?XR(nXARTPaFSZv#K4qA5=(J0KfxhgqlQnt(0lF$J zN-S#~vW$Ks&4X&{lz0|-=;!GLZkygXrAT;{`E-EYb6NaGwtkrozwAnXF-*BF{~)7T zvOUT7*NLL!N3_?QW~X}F&IYvSv%U(L;4b;vO|4~h@6VqUy9(uV*}4|!%9iIpCd||R zbhDs=om6G|Oq^D%H{XV@Y^gS8#@th0zm!fiBhUHug1dT=@tzE;Z;3~xn#1hvHnMv; z1b6SdsOvbJ&2AUIP={so!-H4^a-TtNAyD~GNAx2k3{sU<=%3vXlg~C%RI7vinj0fH zzRt}(z~~~LJ)om4-P?aoD{{Q&#THAlCi(M>{D=(*zDJcN+qVr$-G|iVyUwNc>P(m- z@bvC`WM|M)I&)@`Fw*umX2z7<0k$u$?Yqje+LvkMoPSZE^(bVUJpxU=Q2mO|5hNV( zLUTbO0=U2Vd=yWw_PGxZU4Y|DgmbR~dK5$uW|Xa8bSw_yGxQW$OOb@-<1Tvca5tNPwDA1Y*ENYodd(c$hd z^$Jvq_l?2ZFzLk6nVkJHXP3;lc1Z- z3J$?kk-shE*}uQfJ&d^s;+J9vo(Qgme&-y2!r1JNZGG;!atnV@WDk+LA2k( z-#5Ih$5=raCzA6~o-D^~J!qE)!d~K3!kE=|_g1)uVqvsNSo3CiC`#kB&YE^*X-lZD zfqd7RhL-QBEiGfFH9bd%jMF*BinYvtf_w ztZU3Ys_(P2lfTN16+2ca1cQS=<~d*%l;qBK+)D3xB^Sdua80&WePEp}ApX6RXDprA z4T4r#m*_?w{ml*|#sQ46CHm^JJ5D@Ba%8P!LCT#3Y{Z|J7FW#k_|tJR%&R4LXqi}_ zFj333K(d*+-Lf1(`j}YZd5LQ8xbgqc^?xx3=+;F!^0b3xg2p4*C3nG9@o>niTxa5q z#~l7i5#C(xa2C4WN%5+P>6{kk`{fD^^-l2TzO|f+JT(Hvn6RBr-3Q>oND~Y0;Z!FV z{_^eWy=+r;VMu_ReCbo>5X->qm~saMr=2-JB#O~LiDqFG~0D#CQfu{Nd^`zhN@IT z{n_?(9fCjFIKPOQu6*r8Ya#-RZW7aWGp+FV%6~~~7L+%jw`W^0$i+dc^MEyjO6=(d zF`ATWd*uC}Cc<;EC`>5xV^l3wr)t@I(+=+IBj_TAkc>0a<~|vSR1LtWfRBmzh2t*xoVA4}Gt0 z?l~XDN@kmxLlJ^YZ| zT^Tr1OKn|{JRvBN%(biEQ-4*^FFt zO0%z*K~O8y5~~8$uPFn|uI%Ejobk~ZBGko4h99?MJMlY&YFFCS5$)I4bXQ#DzZfkf z9~wTI`gMTwUC`hb!lk7m?MzyvUzBG!^Te8+Zpq(jYeFeIzKj0u{n)^q+L14vuch&$ z;H{PFWZDzsGcnSZB1=wh_`0b(v|M8_ypMPfkI3(Qh_rv)H*0d|-!~7g^mnKk7dWe< zcnTziDim3Mue1;=M!gVbl4d>;^rkF(`9Q}gCx@14jnq%f!C&3YriM+^Mh$Q@i;V>7 z0q0>Y6}rB!Y@22bM?<>E=1YWEvr=w# zi<%AQtv1IN@z33n-ZXx5%*i2a+FQ){UrH|jS|~UG$m{PiIA(AuIX3y_*kR7me$pI^ z?QAvgQ)G|7TLUbiaiK~LQe?E+AgziOJy9(u*g;7(rK6pyxAKIB#O1~FhaR3})j@ad zdzaZhb7ysD;>@ckr7aWmRAcTtW=#bQ+T^STe)M~q9|03I--6%}3)bMuF?_2DTplgz z;XnN@IO*hMIyw_y)_wPJus*_Uy%eh{;C~<@+7}xl9GV`betD~PVbnjrgN*p{rqSp%|O?0Ir{pBCfY(_B3biY<4N#U(Y>YRb6GjUG2bizW7K9wN# z84=Z>V#3|MN7_xBL%IAMRdX(KBNNs3t4tILcx+Mk`OD7q6myb!MI5Fy-+vEut{n4x z?r!_Ic&INhxH1QkcQzXfaKCYZq=ea?cX%WqQjxaJl_lErm++J){K92#X62dT+bV;a zd^RK#gD$QG%j*To9cNCXWBiR)o+%W*M?Y&ra;!j2IBmkg2m6?` zV&Tv9AJpVjl&Co=4u`Fy;WFW4;%6hC#tvFr2Q%v{=cK4Hp-43alyQSar~rtW-1p^i?dDVnJH~GW>M_vkjp+XI`BN-+U1S( zcNgiM5szw7?aFV}R2;S9_lqZO(Y#J9)U>rT_Z!4;Z@p8($2b`%eV#`X!?xAsWLoDa zj7};_H<_{Sq9T2d(<7-TLdwM&^2e#UoX2G8>E;mjI-&f2=B(Lv<-D=t zhZ9X6a&Y4^XG{I;eO0%ZA)K4Pmi}yLoaN$nI5lmd$&B+U&<(b^-Lu@p$W*gu?&a_% z$g)6fo2bMUgZjnAX@{saN-B-QG!?u8guj0^UAz}?-r>L{OVFe+&%l7DkEKSz#w^e| z&*Slmi?qt>;4*>LvU}ku*`|QDdBOu~Zd0`3{S9u|;&82-x1J8)P#Rf34hbLm+iO&2 z>{Hc>>N~1AJlg#CdjUXH^7#cv!NtkU0uwTb%_&7J{^{jk_Gs3*VNnS|ovj?HSluH%;oo7a z=S}J!Sr0f^-jMJQ3=FOrWDTk_1y!V36A(1cyT3SvFX@jM6lR#5pblt|vQX2k832IS z@!i~p?%Et4;obXgvZ{*IkpV=t7o!M1{B5ed8fa510 zXusHgCEHuRvu)8ZYL%bV0dKJ`k*+2&W>#K$_a!xbJm|HwvxW|hs8`Cma$mKrfOjE? zNvtvW5&cg_Zd8yx3$PJbQ50Xy+Ev>~(Qy)KGG;$1WrOR#H%-EyzvaBT^sy+czkZ~r zPG2=AF*jO0WqtxU8S>3#FKA;EsKIN{x^BIt(!51E6zyo2b9$oIS18+Q3rm=n=FO=s zcXdmp670`1hBn+0?|bMqACYC!rr<;;{84{%%2woj3N{h{EzHBQkH)27m=l2;TVQ^) z@b#LV%ZYV<{~*h?#pWFyb>hGQ$KW{*Ws4zHt4}iD@@~43yd*xc{?uE`7;iqR#Q((X zmC9quKq?pd?igy~=k*mB%n6aA4Ro5UL6Z*_cGvGR>J_P4#hTpeinLL2GoITtfSnF8 zwdPvf68Y)!c60VcQ&h_mjSLM`4&%Wu7=HILy5a{6g?zcwx|490V|XfuPwJiTvgJ}l3~7Cm$>Yu@0t5Se9{Ax=vDXId@E@$6sX)Ph*XjgdIWJe ztw!@YhhJJ`?NQwH2i)cOKBcJ(*Q~z1)5PM+zPw$p;2chvc+3C0AqJqM(UmklnEe4{J z=?QASTEOi0`Yg#2A2cn&tJep~C!K*jMp+8{kI3{V3I^vR1u|r+d`EI&1n5}b^ASOC z75aHx#V5}>pO0k1C*40?e*jjicd^=9jL)ZToYO;%EHNeX8YzfvPsvcRpB8(go?)Y8 zU%-iO^e<@GNKJvSw=qY@Wj5qR%ssT6W8avm3a>WmrXzhB``(}5dbm>5q!|MveRNba?Z zIqt`zp!`n)30DRL(o#Oy%=^D6`~Q^o7nS~>(*9~b|1WzQG`de81qEov!-Kx3;F^~d zZwCE!M*rj&(b(-#cNxQbC@&hMnsa~{ruF^SUEt*-2I5^oA)zco?yQ}Ku9ZGT3H3Ax zv)NtFpBZ(Q$w5};71?dDW@M-=7Hgt(CBsMH`Km-TVm91yArW#k;t)|d{=oz?BeTNJbkqR(}Z z{e$z9jg{G8iECZ;&IV2c>XpaKx#~(vA@4`Rn15ysTGz{MXMDvEr_sF*XWsaOPW9}* zl2y*fSkzC1dTT{fzPo@pR)C4fK^qtVfz^U<9d>;@J?N39UkX1$hoJ`VOQr` z;`ve{aIAvfdH%AU@-Ghg?{7oUQFZnc-2Fnby9`V z#K~$==rpWs%3B9^`kgEE{!dUbLlwMX2Cxoef|Vsvl9wReQbIqT)ckcY!v+Ax?I-Gw zFt0~xqwS-yY?m~>KH0)_=Kdy8S0x{MToF8q+zs`M6wFUMvcmQ^gbbPi;Ys7i__f z#q>3{oFb}+9m$c`@lxiqmwlOvii-Fjss2)s_=mTKtvI?~djm7i3paja+-Sl9#o6aZ zV?1Ek0Hn}frT*YGQB);bC|+DMv3|MjFTJe>KchG167c=|bu#BhQ^CRbNf;MSRCwV; zm`uVTA{h?&5J9%r&s5{Mk1$*X#gB%5k@bNaUkBn};V0JZcmJ-$KgGy@`iIX$9~3Ya zX5PQV_%pBM$S-(ATg=VGpZWH`D2fmuHAlnl|AE{B)8OeoRMLhp4;bhZ9y=Xf<-(Q&IDL$BIZnb)Mf`#G=aGEXpY z&ryT?=tM;IJx%@WE9y{&XEv&QG1Tp_v%zg(>_xd0z83d1P^ z&wJcSbp{Cc#R)KXmLlT@CMK3sPc8M~YEd$N%);6J1o-~5WZeAn2Z_8JnBoh2t-Pi| zoQQgg`VCCZL_x*K6Hk&Na68bZk4!%sZvi#FsNpDV-H|ph`jWe9)#v6jEzq00B@GoIfvJdG)LrrpXT@U8VwSsa+Rn zN7fY)-wnnYpJ%s`(`+7D6bwX!x}X~z`B%@=q7wW7@VQLD*N!Umo5?OeU_>h=g%Nu1 zGJ0*!#~yz!(_EPJurVHXEk2cnz*aYQa|?pnJ26o^waS$=r)>T5&zCi&kNf@;enxWI zu1CV~Vbnr9v-B(NE#b$%eOMLL56n|vT-C981qpQd7;2aefN%^QX`u144vlEhpj| z#rL8OOzVnQzG&NKlA#nABSFnr0UZ|WuHE6gkQmUv<7sZ4pa|LU5IO5Y+{;LU`L=PY z)&)=&Lb!M#j4;X!uh&|JCw;ajl4IUmDXUZO=Qm74VHs!jrin}Sn^RrwQ#~t?~`axVYI)G2kx zNzGJvS-Gorfg(BG591^PF!W}BG|Q!7TthvPobq@>xKgujt;Z)gw8WRle!E+a#B$*@ zoj2Zs#$iW7+sNizq6CIVv76m8#VZH`DcLq*V@+oF^4*Jkog-VU`VhZPxcVd(aDBBke zOX3i`;pqGtqIuT2@e**_qp35|1>N84AyAr&efo#GVNysjGDP@rEEOQSlZ&666hXb| zNOnzaR@ZEHyKB@O6d+Fzq4k>&Up1@kGfz#fzIhF4u|Dp?LK7L7q&wGB)z7zxt!Gw>OBDa1ZhZr}{M)SRfSD>f^w9o_e zmSw`iS9HTuT?t+qlCqJ@qhr?Lru(ZhJg1juCQzU>*uebJ)h^G9fEgm-9`o;O4LlF3 zju+R;R0GMc?XnjO9`KsJe!r$Zdy~=zaRdyQk;6x1iyeD*w#FM#BbXV=lLk+R;1w_x&=C zUl+tF)}Q0y+@JxzVL{?=C60r+x3iGY*9AMXI%}jqh2{gGhzr|%HQcj!DSH&?or|?# zJPNz4Au!OQ>U^6TL!3Y|@lT5+_YJK5R@ysitP=6%%Ue-{r4t3COAO9OsLyxiDIir` z7mhzOMIkG@7wa;~YGb!${PfW_z*nz8MDRQsGxz$!d<}eT>L`daP9o9KU1vw}aKgt; zB1G{%Kb8AL5Z*ER$<${)gxHV`!?oiSvBRKK^z8m?Z{ zmnVW)FC&zU>~upoOT)ml_hu?R6YbRG?XiOS)v1>8M>)68J)b3aLbe|Z5p`?e5sjTL z58oY0$=jj}&#~0M?(@GD_{?;U_U-vu;Gu?VbE0vhCb*04{KYyC)KIrl;qQXueK{c!MCy zM`IA6t^EC@$B#(%r)p}H9GdBryQ~)kad14{AxL6!Qb@`BIw5ha_gwBo&D^p>DzS_o zGkU6NzQ*TF?=23fctgSa7o{-K?3Ih|`O{SYQn+fShbGbr5#_H7DhFA9PW0k3g!Co9BP9;GL&WX6{K5TvxuBjO zL(gWCfK=D}$kFf_<)svw-vL2W_!*NjG9WCmZ9eACN?aYw>?^1a3xSjS+1RXSYQKZv zPzYpX=1mTtW6Jj+7UUQV0r8r;d+hm1(9&;Px**shI@6#U+d*2WSiO z_fb3NJsFbd&-V;g1ka}l>!CiK&wk&QhA&?Dl=W{nF5Mx3EhOhD1h95J|6O{r*1NuE z%U$B9GLA)vx>%d#6U2Aei#o_@aOM2exlWT>@zuw3xVO;!=slCdJ@#)KJ}G!_g=?9N z($EDrdLzHE-X_Q23>@79*w7b#jI(c5Qv)MqlVOlGZLf1dZzB=aFRPc6F}`K1C1u3R z^JDEmanuYV7A%%g2HT<4gp6$otBy_$XzJeIG>DTcmYy?#lvd1?t#!`VPbYbw3PMs7 zOG<=2{zE?*{3Y(`@f(SL!t-xj#F&VY;3=5lB*8G@v>7qp@Mu_Ajl>sJpDDr8OanKj zt>1Qk4+lc1`6AzB7J<7a0n-G#qtTzwu^ewro#%BTmNo52l$H%>9=Fo%-+xOGb=pjN zCUHY`7jc0R4jVO@kUdjK1{%q286ac50)XI;aH>ywWsh6>6q`CGz305sv#-B}!-Qhe z#8#_FsqtS-5eUhmp90i-cYb|B1gH|k7^p`$x?}!GZQvn5+fO0W^d{ODKWH;(yq0ox+bh)W`e**ZTe#=! zG@Ly_)3A2UhiL-oZ6qI}T$iWs@0FICB-~d0an%kWw~8}ikNb+0S-B(B#lTb9W_^@N zuX^!GyLY-k6X}W#qBlSft`W@L1X3O~d}6xO(*?<&4Hjq9*}nNZJ^rzzKg(*C)%IHp z%X5oQ@2pIsO-GA5c&$!CMLUKusvqNGAAX-{a5rkYHll(G99O-h8(#phc&E4c{!(2J zpox%^vE!D>8oP#7f?AHFVCqw$7@!Hz;FJkB13z9kbtpDwcSUgWhH9nU%RgWE-_!-p zK$Gx1wW~SUZnFy?(YCVWFXbD#{uu*w%YBGg$XmBSP;ezYSiH3}K;|N)26!tgGr(I( zb&~H~Lz>Mx=vYp9=K{3tct5KT+WXNr*|&&Ua;Mky1as_9d4C_;(S)P}+_-AaE#%5+ zprEyYIKB5rx_1`oPvQLjUfxy~g~_c36tWs3^(7pXW6_Zn4=Kg(xv7Xjjv{fr4xeev zKfHOl5;-Q+11$$O4$8L6^Z`tcp%6U{1EOA2`U}{4kwIF|oC2Ku#76dMfR%T-L*}X` zGr-@00CMt21r*3B3?$x;B*<1nL|-DnZsW^4^>C% zXp6%2AU*P1&RO8NW23j5z7ocu2LVRhaGB<6H6A@YxZpUUr-L@@| zd6V#zGrc>rgU@nxf;e!xy}uu%ek2ES@J0^$_RIA3?@Rz}XpVcj8*RFKeo(uhT`sdp zpA6yrXtyFm3VLCIZr6s+`{2?o5-;jo3ndRHkrQB+n_g9fQ1b(9s{RtbF_Wu7k$m%014gVziCwO<(V=iH*u_i9WRhj|kQqKGPBu zZRIY&7{f8q3b1Ye$x#?&e5W4&M<5pjM6hEO7=qkGhvHBi-n!8rjM%qv+A8YOb$jgS zutWt7vLpF?-=~xhY`SxiF49AGVmtMJ4`2V_86h9x@BiVzSQ^9$cF!{!^L zO5b$5jn{pgBu$OuqU*n$>hNoSN6ZLaBPvYTyghg7N#Ekt8 z`vC*a+{EJ;N0IpT-4n;e9RpZjjp9a0r|_A~@xA@YDRrlNCrz?th@BhG`v;B8%W4GujwbUA~yDP;Q7tkjdIU0c;drdoHsriDT-?i``bBQ z8u1UH`%{hz1>@|Nl)T#~IDBt%+V>kuItX#3g)0(&kA<0`s9l%^3w9tT44DSsUumqn z)Wq#H03~1bFAO8EFUev%hb0jbq-jvHoE|+tZs{KuSYn+z>bNxV+IP9D%esa)p$`!Y2$`We zzd>fF09<1?(y;($Oj|#c6M1s!HR`0xctRuzm|d#tpc!(<3>*Xglqo%V+yp89Xo2f? zfZ03h6P-eIo7cv$(|&TF`ksrz#=SqQpBNaM{ooarUnOMrEsi)6bVq(nj z#13!K&0gBZec=_wTz?ltf1E7S`BJ}gV4xIClT;{+gq>-e>a(+rHbc`)bvpt!2i`U< zYHn3NH1&Bai34bRG=;$j)h;njKBu%F$o&sd@hfA}!}Hp`#Z-+~JT0#?Yt+m%4T8elvhO_TViF??nFA{C2S-{fDHPE+p+( zYQ~NXdNJ^3xDSo@>RpS9T8@l5;}6p~hpz=zxhP00^?k+7JJ%5>$a!*eU=oJ9=QKSJzJCFTw0%=a5htr#ckSOup z0*BNa0UwoQ$!>FTkjWoyaG83aS(r^#4Z+@;U8z=VJeu=5!cT)MMb8f~94ydH{I!#q z8?qFn+lA@Y*Ep8lRe>V;1f0w^>(%vb`cUnt3E z8i!*{o*Fgp9<1p%7$n`vJ`=Uj@UDA|6hSnsPveESFGKDTF>t!xpqB>gLdr>5pi#&U zweUgJl$KMD)9auT4IYx)P+W)90z#|P@58V&>ZS8D2Dz)Y+pm)dSr;O{zImgX$H~DO zDzczuZ_Y#lCF#CJxPDtPW-IguDH#UB6#fZN#Y4`Q5+ew4*fhAEQ^?ap$aD|wxI5#w zd(~U5fLWCaS*B9}O zhz>dk;0Tqfl{MzXhfM%_K^kCm6enz~f?MD0%+8h+YqV;wG@zRd-7d=?iqEzqHDm$) zA@qQMmKjUv;CQ9?sF6C^oH6hcKH*$olq3T`r0GMu@m}N{;7JEPU7KT``i4Cb;E_6Y zT$I=%1A#1#KK8qEc|3S=p7WFzub45q=TJV_;;KdA`&g5M52&zB+su#^ZLk`kzkucF z%!_gh_z{M}+3A8tTg$nr-Aj8Z^m}JoD6N74WoTow-#R7B?!3z zJVj|X&LfEb{-ds~*=Lq)R+oGRjEypP6Uq4B2Kbnq_wj9P1p1QTR!cU~wrn@UFVcD)U<)pTX~GNn=G?AqigG`FE~7 zf-phIyi_8wHkBweI};8Q`|k~!o2L!4 z#^h#^GEQLy_dn$863x4)*4<BfKZZz5%A5$r5)5pU-Fn>hP^fsu$VntBF(R{JiYtK^CQ=pyx|&>^xKh#d;V{9i<( z`H&e3Llr!lLO&!lIk_J*eg3tg&FSr)>$Yz^2C*dk7CI%dd_G>eERyb8R7lUXgx9k; zVp`NP8+Z+K3gdlQq_pdSJ|x4aE>0_?Gs)n@xov&zFIL!mo%r?oyJ%eAafb@txgrm} zM`*s6@&8Xt`F*?aI?;{wm2h6J8sm}=wW#kIiFvJg-sS@(gUN6f#vW%XWVMBe_9E0c zDP-lCa6bCd%3T`dxloD^_?l69bZ>TEv*la zZ;m+AqLudwZ>)LjKkD_BLPFU~O#tXO6LhqZuFB19yZ=3siV>j0q4}c_&LA(odTiF5=!` zZ|kar{fHnK_XM0eWQUMex`ZG%*1HXzTk`B__P%IE9# z7Rm!Ep@K8%caLsyyts7w!waXM5>A&y&QBbgkB;T(5ccuggm#OZF=!l!=OXX$K?GM( zqzBC1Q=SWx*rVUQ1oH^{P~r}=Z#|Xkq`$Y=v3=RK2u`{Pa=S~Z+sG@C z;NTzZKLOBfkeLi0Vd3`3sj=o-<)k}%4r%dKnJ0DxxNN7oEalT3wYAi{6kOB#c!;&5`k5D0~=&|*L%8ZvwI*%m&H|Lp2u zA&l`_)Pq->wxux|@{$ZjQ*br!W`Q49r~uGJ(%UzS?gE(LU-5K>?0-AHD|kdMoo4z( zp5a9_*A2eAJU1zV=mLvz0ls1@1;Lnb?zllpGe2hkO^O5UtpcMTk(mK!7(qyp z7Ne?pX#&jG7lrNWM^oZrJ=++UdDr)*^|_P}lFu%u^fxF06JfXq(U;|{VR~r+`uG60 zPrGgK9!pg8c~)*)*~`b5gZctHjE3A@)FnuijPeCvkTJ50h0K|buhiA(-y{V6L*9ur zS|V;}ZC@r6-TQR&ywtkIH@-eBO7q63X*Tfi8KmwQngMo-d9&N$;#&YeiLKdqQ1t2)4uHV z@{}ml7oaasmAtPZ8(Blxq314>U!3#m^s8r0LmzoJ5@4@6zm z-m*{rd0uIP2D@7@1YOM;iLpwy0LJ2Z<7Oe359S0NcO{fK>$J+(eyk%%|J;@m+ z1HQ7hNu&hz_Uc?=Uzg#oRhN@e0|G^u7y9**q!HvCW`MR4?t2SiACZbX1YwGbcsei- zw;7|PF-%i*v#0b|aAL--G{VIC{yOJBRQ;7>dY_=Y%jbR7em^HYnzcR3K}?G@tq|sA zxa8#ks&I1e3nh@!Pa(Rox>~$kCtv%uu06`fLk~Y*BKXSMBN&W<8E;x`{aF(nD_LTy z1Ye`DJqb72R9Z#m9VpQ1i+kq?nqNbf4MP;6PhHBKgbrSJUf>1B_{y)%71lTL9yVOT zLZB$Pzg1R#(yr?A$%|-zqyh-%S23}$w@r*@_fs!J18_vi(6y6^0~s1NaTW<;Vda93 z=T36cn(!LV@fGGuo?@E6nagE(z)tWv2`EcTyX6{~)F@1mWW%uV8x68%z|eJ(Fj2-e zAnDhfv|%}HN`wfocPw5bv~{Pu<3n$*mI(?50OG{-bP5By4-wc1Sv&3-k|Z1Wo4Q*U z0ter|z|gN|Y1feo%fE(gzH5@3r1I+01_eWTyf5LYdPS41JY6^S3d7 z#1=TnwU+ubi3WVR(o2d_AyEZ2w=wxrGv&7dE9deLq({nwv9eI)qDu(k6Z>LR9mr7@dedJv-v0F#D9saR8cg>dG}EQrgqHv}%aa!c zqyXK-9qze4W5vROq-}4bz-Df4ziG=km_G1XF<~Yh!MZ|YArNR-AoDpVb$+t0zhg*jkC%Ai&X-oH$lfQ9yI4+6ofP$B^0QCrN~N_ z9ZP&wNmz6k5xaZ{bcUu&j-2>2ph<-r-!xPJsjxI0wT@U*4hQs_ODC-9DTomjEF|Z< z5|b(cOp2_9CMEVn7eMY=6QZo{vvizZA7Q*SuL|H`0rx>84eG_Ll2Fjp0|N(@rQ@hY z074&1+C41+i9r-Dn?J*zmeI%MyW|LeTfl>x7ZHIA`X()!Z@;csdz6^q3{fja?yC4?QkD4QWGLE=%vmetc@YZZ)82J0)FivB>8Wa$m zy|RUi$Po2Fc!N6?9plh)K4aMo)n}Jr4l}0aQj_Wct*f?oKxQ;hPuzYEZDZOXYOnW< z+W;9-TG8bS#X|cxjQW!qVMqC##0?Sb<34rBvc3j-Enklf0Um99x)OCsJQ!{RMuYiX zPFWG@RAzYnCB;u+cIdSi+Eswp(2cJ!*)#Cn_?yHpuYp|-e*Vx;ts^?z{MBpsiY{MO z_UN~9K0)N)F<>SMU|yJeGEn;9{*d`2`Oku{GRfY-224MwxeGry;3?+%-miJa zm{(i_%s_-z^pUe<`SAsZUy6?6y|sHMTqdQs`d%+HAF`8*fd*K`>)Cs~xX4xDqZLJR zm3?fC^WnNMBI0f=@$*}Vi21EZ-};BEF$AFP61?2{)F~G`ZrDA-uF>8e zH1gxVXc*)SD4*dhJ`G6sol^kYY`5j)s9?P{m03&BZh4dj0)BGLp8r!XF(P2LN=;z$s%SSuu&NKC_XCDJEMS0s92vYCQ z@Rct~mn?CVLkIuhMny}R&`se~n?oi~8xM`nXvQKSkxs)1yo&YK-{uCOf;(YQ);v7+ ztgA+f(`@1N()gfg06ssRCV|f&UD}>q3HIwNwP+Nsf)~>i_k9wi_+VJ^cS3gZYXoA5 zkh$qU=@>iP=VUz68yb?ZXvX)x7HVer<)W701D?K!+~cgN^+-AOkwuj`=k1YL?*ca3 zQN94{sSv-3^}z2EP5w48Gz6#Lz%5;^BR42zZh5Zr|oHAU`zEPkWf`e#^EdE7qZ;9;iHzOOo;#CpIbn{U+Lp;DM!y8TmzbbIPhAx zX_LvRDC7w!rR^+p%RB}ml0uT@Z&0RqHjs($HDM`%?)9hfFIaa(9mc4mvj$cFcKYf> zE~EJofCXtdsK(t!k*PCB#9wokC*$F#MUmZfLD}F}v{Eww*TL?R`}>Q7Kj}8Ch&0XL zIDtGIfS+K+^HR&SWmdtKK-ciQ*!$0B?X?DQ+Cbb%TK4>`Rr8LJu6`SK5hro^+WMKv z{#%(+*pxJ!msyzEiednv=;T+druL@mUzn{-su2I}p>tE{FFiOH8gK8^^6Mo4B@Men zF5uJ9s7waf`YZ;3t?}NsnUiU9W>tmYJqSoAn9NiNzN|z{`v%OmY>f8QZQagls4K1Aa?EC6B<;jb{sUk@&8x~#W*g7Q34B4RYUu=$mPwn>|N7&B+t?pVs!<4DUSj?}FCI<}Uq!U=+>~xD$U<=Z!yK@0T)p*(z?ib)S+1 zTR#BkpMdHc&vUe_GCAwIzzU>vNUyx%Qk6%hXQO~7n4d^qrfdQT;8b~k`CID*ATZ6N}&RnRZ#E{7_`#U$}oO~~5snxp9he%z6sv~dO>u$?DjB&l@T zv&_qdNlTAB5NW1=8}cE;0%v!!6BK~Fqx(0PV!Pu2u-fxdq>v~U7%`nZx8;6P8*Ch> z1O%y2UoNadA|cI?dwBe!St{VHQGE z-z{3-eLe&$``2mGc^L)3p|4%LP6vxmx^l@s;^hN=G*W$#Rdc+LI+W~p?gzHApOIe! zThBahjt;2wzyOQ~Aunnn)OaPBCXK75a)|NWPj7}^18{=BSZB4hKVZK=J4idSJssNt z^y(x^O%G$A?JbeRi2d)s(ZuI$@a#>%7wQ%6`Tid1`1o5OaOlq=|5;(A7D7wCN%!RxwCHe`3ypY z@p%g(M!^O|N=0K!GU58UDpRz3j-%}b6{atL-LMUIB;Buh;Ry+GqXzlm{qQNpO3h%u z|Ea~vcd4yZi+_&z1jzH()&`Y-k4v&{7XPHeHYpJnRlS{BQIjpKuIx|iNojtRr|weN zsH^K(*Q49^_gGd=%Ciitc4FG0XSwku?2{knmVeyHHv-hJSPOvN3{)d~|8=GRe%vRM zQ+(RkIab}owU9p}zt1V76GI;7a=lAh_jATO<4qsh+&L zamW4&vA)w4V!WC2p)p7*IpW_Lnt({;7SPpRd{oMG78Q&s^nJVd2Y?hkd>%Fk4QGON zWjwbMe)zFwcdO1~B$A_5*JACjn@^t@`{!-d$n#kCR@#1I@2PI-&^==rY<&UimgGtP zdiObpmG<{+kj)5)KTFC9^Jms|wV~-5ZdFQgc&bM0;X#iOi*Ag1JyF60k|oBZnG)3g zlM43bB`>7s&vi54MHd9pqzQ{eVZ>PBNQDfG=)EGV+IuABIL@Vgr1cImKT|n4{ngO# zWZ?e*u&FqC`%7on@nni9`rx2+cgDLwiL*Pzx2ooyYfwzZUDGUMBB7{H{g7>=GUG=R zo_x@B;&;bf-yKa`$px=>3aMVGNhz)RTx3u7ab8kClO>`!4iQ=AY|+4_vq0 zulzyFv-_P-6k<2p`F?Y<;GkuRQW(_U4G=?Kowv4>qx|98l`Dl}zg<+|MlbT$i0=oM zBiy9#!xF+*L60oARz$D|D)e$-Vw_jS zF*I}L9l(63!8F8UW<>8+zJNp4M`*LunSaZ~{BK?X+PSx?WY5m5f?L496Xm6|w+t7+ zqad{G=lg!PPpSy-rIKS^SWCMRSd_UJuKw9;;=dwSueFSUB7{Fr60=%<=oY^`7(hap zRkPsb&q?wGE%WsfZZL!3!-bD_u){M`qP~i+)19-v{KgF*k%&5+RvaM(7b_}8eu_#l8ICfM4GfR3vxMyWeALoH(Tn^a&hde~e9E^@|0D<= zS1ARgfY_(62J|g${mGu&re*4ht|Q;GNKCbM|8rA3ft$h(Cnq?wQD9mc7|m<3Nsr*h zV^V6Oq%f;MHZePP%Pju1=4i%KdXHDU%SP~q`mUsNuW(tAPO86#$E@4M$0K?ci(FFi zhk1>Bk8Ew2N{w}i81-{ltN0M>0y@8bh1LCwEJr}d{v`CDtsWY*3UPe)(gp^?u2`R4 z?^`XDW12^_3!6aRgXpIE>XQ9Pfrv>59*N|l_;QSc6B|`ZrtpzJk z{Y%aUS+;!hhWjYl*7%Hjpa)9LAF%IVMfc4djghllZji9<0$lvNKgD_7lVVie0YLuGe16{4T*xf z;$sb+wPj5W(ImOu*#rAjAD?NfM*BqTK65!7S-=NcPtZ?fky*`yMX6A5f? z*s)~-WI}n8-NT8)E~EFp+>sXvR5&l}98g-b`g1<{VpKwO^DJ|`#TQ2XG-_q|st(~d zGv*POhHxori2c7^MJ&~6P{Peq2Or;S<$&_9|f9Qr%kDlF35NNE8TsX@pX_3vPHf z%+{n-N{F(F98BPU3f*z2=9bG<4r5b7a$atWo32;p*N*X$h842>J1;|K&l!S+e3)ktxS0%3|Am zD0rHCo(N@MH@Z)0jRR+?8|v~{EJ-uazfD}{8wTBJeEBu!vI}M9_IT9kN+2E@;b6r& z?RtM@Ej=SOu!RE!3)yb@Q#_3e@EUTLs2qPdsvo|irnSGM6fnF`)W%%XBuCL_c+y>D{V`H?|TP}IQ^{F_f; z_dzyt{JrJrgJT5FW0$fi(_`ue#mrHoePZcfV0|LKv=-ELWR1pz`(}bVZ@CfJsPhm- ze2UbVcq9WFkR)JPlncTg(6b!KNhPp>s&K$xYKPcGA4nNbt0Q9WW@!o|v z@>8b!aNn<#2W^rkLXT`ozCaYj<~nsaGizs()_NMfiSu%B2k9Ub>YXW>Yy`ZNVCZI-TUde<5@fl=hD3^YeYr ziV1UI`#U+I=y8~Ejo%@Ev{fgM4Jx}_jqOBeX8a0i5~c?#Bh>?%zNM@Z={K>o)Irtx zdk7;dNZkWBxr(v*^a{8cO_>xcOfZyXYU5mN&U4Tq=L*mJX6tx;OH-Zb8voHC&T;jz zbx5Hl`4C$ZW0}L%je{)1!IO3AP!(BLKATxq9h~s03OruYi3gP8wv=tC1>SB$PU``b z>fMABYl5_y4G_?%{5=v8_*avQN?4*uT}9bg7`Qe1t%3~uSmQ^8$qi10VQmE%;`ma+ zwI|K=t|T}1lJrvpApc zS1^3ozMx13LS80Dfd-RV92jK%xdsoT5PJrKHzFd!H-y0$9o{PBolheV;(bUJmM( zhTtOJhBg2S@Iw{LN;$5+Jb2dMYhkG{ODo~7FkJ7s#raQR@b9?<9C0D(@WACIBohn1+#K#$cc>LrZA@I}*np9+=q6cKD7wzI*cgs|u z)(vTS_8^3pSw1U_Ej^w%z`dmi8p<(g+$h#!Qu&!mXy0q-@aYBiwB@wMTqV;b=9Lz% zqXI;8=q1lZ|8wTU*;4#K1udY$A^xAQN<1qkRJiXr9p=}cByZ!@2R4&7a6ic)O1r?C zax(fkB9_|<#MAYSgs>Mg{3t$y)c!U3fO1zo$QA03SmsUg9n{^a(1N7}jj zwVM|Pd`h=-vN+?*9~X(UBup6nz#dlZP9jd2bbxkA*n3qS$CGu28NJu{OF6nJ^+*%{ z5~?^AwvkC*bPr|YrRi0=hNnY`w<)>PuLk)YHOU4Ll#vpxQEI39cHi}4&j+=7wHSQ> zKBc!wu@E5Qt#Jj#vF#My6Zlr5`x1&-6(3sf=^1-DTZfGH7qK!2a2P|)gDl}c79cVP zX?r0cfXfrG+^cu>K2dlmNk;GI+pRa!V4`I`6ysj1`{MRlU~oZ>Wa>dmzildb*9>*e z)jZaeXM%oqn<*~eapLHNc4mDQAR=1I>4?Wd=(W$sHeV}toylc-L zdO?HYtj1CV@U^6mZz=J3=}+2frv-)G%|?KGHOP}Q-q2ng)24ovB9h9uyQgqwV?CsC z#<*s6q~ZPAt_cfS6JH+)yB}y2P$_z?n3~YP;~aIC>P*1f4Nq)6oq}+pNrg?`NHPEt z%m5W-mM-)VvpDu6c>wx~;2;E{z6bXE%dqwh?Y)C37w4S_`2n6!y&o!|ei;NdqOv7T z6eL6WerL%5+r$g!XdsXf>a{eXAagWyH2D6-AHHq~(U=D3Q~HQD15RHkb>k zxsDNT6)W(MpNM^F;?&1@o7=Il7ly=E=8rWR#P}?*w?4D6J)1L!KlL-5`oPDB*D}91 zn(Yo2_;d|<*>J3z+R!+BNjgG_YjGCU~{5O;EeuB|?@!ReoGlwldrx!bN zlJ0xie<_T)PGg_zpM!Ni{`DQGW%DEtZsSbHFT)g?fpg$6;_jRG4Fx zHJxBXGh!(5fd`^{*|4dr>2BB54pk^AvF(#t84eT2uThitCRa~JneJ|&8cH zFa>lNqvVm<$J=@VIDxDc-@Im3aBe*bx#}2QZQoM@(59Y02S z#zzRGw&$ng)J7gp3!D%bmr`08H_Tkr3MD%ZiWG#D)$aRybgNfHfsh{)~9a0V@LMgik=G-Wrm8 zuRy$-MLbT9m}1dJd+$%U^g5uG1v}#9Hiqj@6L@d0jz#8#CGR5mi6W>SUkLj*A^ zAt}BWXkWf8*_`!=6-a|puX}C>x2j95-**BZ;)-w_dAOA@^{%)-JTUy-jHh3Dxk&4Y zMkA}-^Ly>Zz(LCubjsXTu0v@HJGB#_J6iR={^m%(B6yP?{5BHnOPH*BNEv5F&&ko8 zS^gSn_#Vhh;+*YecE;dS9qzF0soP={bnRbz5(O=ePS0i}Z0CUDZHXY-;3mF!qwX^S zp;c2W-62b2!R*c=hE9gjgU-tMt=zWzYl=8@VBkSo!8gX2g+FNo(NUe+iEc);>nUW2U988-C4 zaN9faO_P9zou#PJ)*&lra0-^>9^%Rd|RMNq6;j#>JLM#gd-9#VfYlbpuskn*y@!IpWB)9 z(F!p8BY;WmMaV+C0>m{f3Ac>UT<6`o=iJ=@j;Bh>qmr6Ib{*X{TjTN>MtmkP^#6Hz z*63}ki@+ChAnJv4I&D>q%9PPQ~CU*`iwbI8d>f>7Hh6Qda) z5Rel|4(30-NR#a~Q4oBGSbT3lS*t4-GHb-2PNc)Bk(@K>94p%3k*PT}q;&H-4a2(* z1&^SM%u`&+Xj9?s7Lbxf$haOnu^hfEw3@sY%3d|iqjQ|2mzg^w>Qk&&>n?dSxgbLN zt$Dz~cJEt;8(#!1v1kU~<)dwSmP;>zf^YMm2b1Q9Z!xG-rST6tikuk}3s;2X;{z;`#&Bs@J!r ziwScb4bCWfL^$cTcWQUT5+n5T@7W>g6#yg$?=a&XIZh?bVN_sM(zSg;B>>k6Arm;e zF$(^Sd*L$BeKZV;X2JamTim7c0p~9D@R|?b`_FeFAFGkJ=^}K3-6Q&;O3->_Dkv<=AI%OPTnuYf!op=OMi_ao<=2*J*_pRB1po_gbX;v7wv9Fau5{j zFy4iJO)8KVJkWqeU(#{iUv|;S)EYH2zt;+LcxPnMt?4fe$ z2DX<&Dp;zfUoaAO-?3E6(nwF#?kA^4CgN6`+30C-)F8Q)o?@_<3R*FP&N|Ew3)zkR z0YhhCK!{)e%r?^qh-4zK5+qCGV{jBXUeC8G;nq2kKVm-h8UIXLZ>7OwPo)V{eGYYW z_Pr5YZoxfv5&y`E|2^Bs#rUCLIlN)GtPn+fwa`ACcdKsvgNo=d4g))wx2Pd@X~9rKgktNhswmCMnRK&W-Cadmk!h&UlGd=tPd4iBz7Cl@L4c&U)vTzuf;TLI0)OfcD81~N3VMl5j zz#hh}dlst&hO$I#Ehp`56n*_kIZ*Ezq8aZJ#h3L^I{Q+>+*=WZ+ewl)Lv&8rc9 zSlHTiW|iTUWI*>Q%X;hwOyHmWJ#LryC0+xU*{^0hEn3ExwFS5D+Fp*DvBG9E!@g2z zO0YF}r>Yg&k2xoPtgvXuR`~|$*TB*>YsQbeix^dkb@#rjw%4#qvN?D+n5`U<=~QE` z3keyeXD<6`CLBQYv~>J!YfFI`7hmNT#*7vW%OKiVa>R+mLtjnsG$6w^vodUw`5NKV zEBf`2pWX-oU@u!%x8BBNF)Yj#wi!`k^i~sEqpiF3JociK3V)KZsc}U6Cw4pYxoIXJ zw-CkASx1|LiV~CcOFFL7d&em%F!RyTOp%&zHt-|ffs;iV=f!}@j9kl8u8I&?4nY&? z5lnH(z>J6w24O%thm_3lQ0N*(R6wi* z*74|U!oU?=owkjX86iG>-|^=WAyIs@$2NeG&?@eHVf@DyhKS8+DKmEA6pPVU!sV|4FH%$M5U(Rw_gG$DNKl(j5<2chyD7qgMSa&E>D15Y38RvC zdVlD0PzaOYJQF_(jwIC^_aWIVds;RCvI(zPgmO!EFh z0d)0sR3_ufn8+MwI&VR@zhy*D7_LtXy*}D5hAan)5H4v>;yLSawgVj?58f3rS`3B| zEa1@bL$6vT1_E&Eydo;!AS@V;xuRm77q^!uXFM9Z(bir&sUG$XmUKRI!RpKW?W0F1 z@5NocQsCU&?!@2jqUTq%C>qL-?rJo7r6NL#^F-u)iA7CHacTQFr&rxNdDvjrgt9TT zoI*SWhDigc!PKS2F=i5cUZC})~`_}ULMkViho}J**EU8&o(|uT+N(39Wp=V77Jbq>-_*ZXxMo^QMy6l z7$sYkVYSg!4+{~U!f@EM*E=gIcg0MxQP8W|4EBepIJb5LcG}grB}bL;a|WCa`_A|WeXJft}pw>Do9u~BWOW~snhbzD3Oifp;Co8%V zM&b9O(7iWJ0K)tGCrwPD=GNw?w0$Cijobhih0eqw=gJ?^gjRMZY!l(CxO;bQIQnZr zg|M248~?#!#7~w1G_dOCbB*Uw{6)tfWbdwvOl>0`-oVh>Wv-@kilo7>4d>FuLrXW^ z9yWeIiH24pAoo*VstB~}>KqF#A8{rXsmKpGOtWv|>|aHa(RV$Lg7rzgb^Z)XO`17* z|Is?8@wt&R_tb{YLU(bi>g-70%=d-OAbH|by8F`SxKuY|q^Uwk6$%fBJ+rvXgc$By zy~5bqT2w=QmX{m%^1IQK5}6s8V*Qt%MM-ck4`+o1vZyz|oVlpW>Yox5sI`x$UhB%W zw3%ph^QpEdvs|gLTI{xSu)JqNx%FN#6}s~C7yE>UxH$5c@9N1()b5W&AscL%L`I?F zx`?6DQnj>ZSW#E&$3%X0lv;bgpd?$vk};*;R%8vRFls`!Vqhs>IARwvAFf7oZC+TktOUj=9tEY(fUfM^0j$TsWxmMV*QJf_#vg|i}(9QZ$J?IinhNLB&` zGGIOIBh9m28vkbbY22hg#Mx~$V`G(TI`W^bm{N7v+KQMG^=9ek(B&9ykyy3f&)5`% z%xXBZp6d>8ZYf1g>J<<)h^|f`26;K$*4ugbmn?s1wG-yNH4dL>RJx*@lo)^M zK9fDBG1~;F>A0ly{h@! zs9)7HY-5F>MpQg7WB?Z>p{KWjV*SopvA)X1B%l9{Z!Yx3Js@}2liIUpR=HmqK2# ziz+OcMf5YeDhP8Qe4Z4Ic2}wInPrIjHZLquT#?bJ!zk%hBs%34yWd!=7wWs+F{H|v zFnF$_5WP~hm@sL!bo#+kTP5xjEL)$mfh?;&n_>D1&hmtqU>f^_;o&uj-?}88Cr!BE zZe1=de8mqat3al}#ez<-ba--^3B41}@kKi8p@=H_-)_v=R{>c7Pr1G7;85W{Cuvru zwe*5h@}50w!Dp>g_QMI@!R7iJj~c{ED%oeu*CZrUMD1P2oe=lrS&)@bACkCh~(3O5}!FdDU3_3S|1qs5_g35ww9nW64GT#Q1Q zybHxS}>K6-dbi>BH>>#XXzjPF@l30Y;`bU-@&gK6AX9Q2tZ8gEW zwjcR^%7fbgIn=bK<7o8V*~I-O;n8-CginVI!h+?Garr4t`$tUCw-2RHFD`z)4Wcq? z7@851T-bD7e{M^Z!O4IB^hHcapjvW!DW^xU+UW*uiCJs+ofbQgQp=iUNv}G>_2I9f zavotmozE?$8y3|K{9Or@&L^AeNvIEw3D-mkqo)TYRAT~Eynkh;Y|D5A?W>}{Xq-M8 z7A@U639KBR%L$7!$(J~);wV$Ie4zWROceVoHZq=1q%hTfj-;PyyvV=9Jpp~-i1b5O z@gIrJo<9RE#<7%dep3LilL}klcw-Vz9|${f18{3KIMWl%6%df41uPg-?0rWgoVWOs z^uQ1PQH|nzhlNEnkS_+v@<-uH?P(BW_$wS z6;l1n{@oV)z~Sb(Mg3A$e&<%CuVSu&9kS5M)VZzK!};x0{Five^6ZuyXr+!Rx_GOl z-jf&(F6|oOzs_5E!eBU z4#A@-_WmhZjN^rb*@183h7ktDE%PT52hhoA52V4{TqhMEX0VkK(R>S+A9;PIGxenB zUQ0Z!w{rpm_bt~?a^VH5dL-$cFIG`rX9d97*zmtU7S!2B2H63|&EWmlX!{=B_~rPS z@9-M|s=L*ZS(MJhMjP$VqiP|O8J=CaC2rp4vq_lw$SgaHPax4;#|o@rj$Ju>#ud!8 zPEbwH%RYUq<1Un1M2RGzv>J9XghmDRMi9;3WH z^Kc+~PK0lIpblPYcl(+D=L7}Lb34OdaLFDSz+e}7h4Xi_#t$rj(5EPa1NYSU3SLH^ zghU`GIx#_|6_E`1B>I+ge|xmA{kZ8`!Wz7T#pCLWkQ~N8df*AY6Akgl*chewldEdu z{02dl9j}HD`x>Hop)q1`?}B|6p=L-XXVmzVZcfr5HwR-Ey&e~)7iQqPGy4NHnK37f zYiAP^-Y%n38dk2eNHZwQ)7%bXG_#yhqb@z4<|AGghw3vpFUYEdyGwlzVo8=>}V6`Qf!x z3A3G*R+&>n(B@61Z;F1g?iWGcM#Be_<~qZS457oyZpsC&Qipy?C3PhOhflkg(cN}j z=8YX6SD;oWyZ!4gXUB9zx?Ku11)++)3tTJ%nBx>pJ$mdSj(soY?ZsrM03}+s;RCXd&jIdH&@L34IdYyCTQ2czXc6mom zgHh~{q00dxa~$W2QDiD~O*+aK0W2DC^n!R0|4n=V-jv5lMens4MS@N^u#?m{UHmA{ z|0mrS;T&vFS4hr)+fLd=H#bEKY3aYU*)j%AEaK^Lr8v*!EX*gaOl;zjLIqR4)-ZQP z-eA!Ffp0dlAxw&=VI6VH4QpKeo-t>#{Mp(mlh@!w&h!bIMz;GObExX$~iV|JP;$;5*^jNNV9U3)(p)`{Rv$;rv?k{ARh{ zE#c}T9h)P5K1De#O@+nJiydk$F_S)au&7^-dwR)8hf-)FV zy(dJ9;lT}gRxhQ%tlh!qiRDuJ{C3AkWwod$CKf(j&yk9Bhms|Fft=%QdIsvqn-p8P zspo#k>o(pdDCwHe$@q%_Gof7Q8Qhm7Xz~T;N*0#zGHqnPK{+Z!qz8wuD<=CLM;`2Z6n4XhTfP{)=T|ZC5iju z%DS!4D;#V8kwNlk_PRLgq^OIbCn=U^TKGX+f}-jzQyOL;`$3shcp}Xe7h-z(4VY!p zUTE2>p$=x#J41M0PKWM??v1_Cu)@Knq7kc0=68eLl#dWF8fqF;njZ*Mi;`T4vD7(KFZYOrXDN4AJEaG6iCN?F+*mQSlKCj4e-H@6d z)+h_4yW}7hH%2|Wumec(h=E06SrN=o^`TYZB7~{b!)G!glxE;yJFMLwcQ|(ZwIrvJ z#=!=12>rMMk|vYd`%-`4p>{&AJSh$52)YiEHmVyK{Ut)lXFV0Ro<;QhPOxVr^5F;`r^ycQRP^=`(@{IXRs zv5oGobE~S5TXjF!mTiK9NmFn7PQAq&q=d|#OInL$i+XEsKIwxFA4J^&X zQQ~Yh-337KXZ)#`;JZA5zcodi=-AYXJo-x7!THxun;JS;H`XqgiP-#^J5J9rOV(Q5 zJ1AhAeQHfcI${3uNk-?z%=PHbXeRRrU&%#&Mcnl%^vCjSK}&L?h|Bl`r>Du1y$Zx% zDJeBKf0K~-EqwLT6h{C$(Z2wMGdaQ|P9&x4C9}l898`nWDZq4U*+yfUYI0F&_BAu? z*pnet%?&5SFonyn$23f9U7(3+#f%+Ysra;n8@cb6nD_g)FMMgUJ_i&EL98@w;P@LJ?lji;qob%L+bK00atiFK zQ!@@rLe)Jykzkg8%r;Ausmcm;G6J(b=_q`_c}meop*NHr)CnAKjP&Ocr4an~?c2Y4 zh4IOfM5_s7w}fq=uy6T`j5d~=nw7Ei5DYArz>f?_G@B?T8D3yVNP$f&_@Vvz*M4Zw zJwVYOdFFHf-ybpte!wdld`bI!@fYYnbw0mQhYs#k@GX>#V^r z+_kv;$5Q*>F8?wuz{&p1m12qScKk0}8?w7V$JjWcw2rUSTpYX6ASlW4{W|jD>PXFW z+RcJSi;$k0hvwizNpu5K=3J?!d4@urIXQuSOkfXFW~cd+eI@*C7QXid)b-abum96{ zBj|VB^5kcTek-a?udc*M-JJJ z&AgT5kl9UtFK!XU_qRZpxH4R}-~I9wc~)hUu8E5I7Pc8vsD*sBU>@9EH!qiC zFvMoT`C9UNnBrIS!*z*DwI7@JjU66LRjR4Nac@d24o2=r(8br~a8%g0Gv{fCRlH0W zg&N)l){keDmp_)Pom}{O1ixjF!!M$yS%lD8GO+U17E8DxC^^Ojfa~ERs>vHhY7P7| z+l-a=QR8j3-OOC$?k-86%;YoBHPgbg4j+%USF6}{T3eYu95CkO7O@n(m?~c!dL+X9 zB5N+aEJ@JidSWH??r6*7+YAqQVcx0HB0t=sJuY4P>kEdS0P;rjt(aff4>G-T{f!s) z>b+*7&3bbymP+C-UY9UIQEvEtxapu7Xgj_sN*VcRFsv`pM49d%noCnRpa#Du61wrb zjR_-S&pzyk=Q_*?eO`r8F17D^2~Jjcqggc0UFsnkXNL`C0r&#Y<1u#{aP?8d(6;Q| z_OF{q?axp3olp7n?II#C*0?a;br3^L*fM-!*GyRd5l zId%DMdUvo5z5(5c`PTq*f!Ns+cWj!RV5ldaFzZ@LJ;47P10b5$v;jQeV;8fHH(#^ z%+@3Q)6x+}0<;0=$O%+TgnutV_@BP@kEa`x=Og;}IX)awzAabu*&daxoZ+N^T36nM z{PCdi+bQ2zDe9e$7#_F7mHk0745( z;EjKTm`UWC$FIOoVayh!G@tc{J=VxxtW;=Cj>_tt;dMKp3(bM6$f0CAZ8sK&LH1Fk$Vq-Hb^d)woN@0J zWIjmT^!mh;{~0L%cOH*ZVC#oH)@Ah-t#}7I{?XUWwfODmdl{zUbQqL5^TAOQHhuh7 z9+h6p@6n+jrJ45a0rI*e5UQjeN}5K-&Aq12&kGlk@KA0kkT5?SAd5)3D~`r+MPcY? zgfW{6RzdrXI~lGzy@%Qzuj-&5b9GSNQ@8Y9p6J*h*Wrf^N(o!~>@D8Dw~|Yr(m5K~ z$Wi5nzZRFv7MF95Lyk-jppyGOYCF2~;>71xtSp8!$* z-bS2%Y)U6{bu@;HrVxeR&}(cVoWp_PE-fTn<8Z6u}R5)3xe?DB|T3cF;!%~l8=$mq!{I;(>4 zEXYkRcf|?nwy26$hr0dL@N^>|Hk~jsSi95w{Tu^L-iN)p^4Rx!MI942oXfg(=F{T+ z8V0E_xQ%H_Ck+o@5UPm3E0Ukxgek!ORuj0GK8OP1psqsa9g}g-TRO{Ja6@%WGF&yv zu}u2wf(fUS$5+etC<=+NI_bwz(}9~b&K`4>cPNs-iuOT-!$-2-O^bS7ad-G?w{Xor zA|wQ9K0bm;rdO0NHScQUG;XvqU9Ugw#VxwXfaMW;=xz0E{>q1ke3nwWrgy4ZeyNZa zHn`c2JtDB| zMT=C1wu$yXl`;xrrZ+aKXN-B(_wWVzzQpVs zm+Wqyt9#)-?u$xehxT$V#Kr|vZ^ebps;;wO$W6xC7NBo}V+rmEbYIvm$lP>eb^KB|)ou0slUri7&5V}#1rrI;RCl_1zr0gWX}%I+&U4Sj|Z zS=-|w4=hkd7NdSiOib0;bS#+Xr=#XOBA?HtntFWwbu1SzIZN&e(+XDJW~OvoxZ4ow z%X}$Y#f7?T>^hv7f|Zg@7B-=tC@G$$@U_Q3cw6LH@Y5x#zZ&(`v?goPfeT9E8u7vV z54h|(j#?739~9376dpyN>kkf3=cHVDv`#VkLbl-as%|eK1xS(Fc#deC#?$!WibMbs zkvBnS}oYzwkrCT!+72%DptmqmFS_#lz@8c#vHH$IexKgTm2!=k9W(8-yDVdTFeFt zu4T;K+&^a~k)DCmPN~$uYH9m+N8S+JLIN*tRPGWC`z*09W28xC(PA3pU_-0qi@lQ6 zT5>LGDwIW8Jv+pg`7JDLWi#V-{$pRo3ab6PJ_+WEbm@Nz5dX5=4}8WveA3v7;r0T) z!B@Cj5|5U>ac_Acoi(bP5`N91V;);F5vVd^+1_^7dS_9KT&D3rPA|z&lB3&olSrum z-xGo61s>^lx30$FP6yk86}VOAcK+V5!F*~p)#LWB zXwb;uUp+^NRM8&e`5nO6#QlxG>y&q1E$8P7{d`g|+FyNAjcOi#S$TWXW~#p8ATYo> z{NsUvw)d5hs7P(1qu})`*IBVZikA|-WMp2Qu6XKDXi28IcJ1K#n$xdV18cjTn{i*L z=(wD5&b)z3W6G|tOb`|;u8o1|{dsMxRB{hPS8^jKlvWtQIB_-gXT{Wih@}6zQBVFK zdv6(4*Vbf#;sj4faJK-#-7UDw!JXhP!3i8BKoZ>D-3e|74elP?-QD4x*w?qa@4c`8 zzxQvCJ%GKWYSpZovlhfSK0diDW_NX{2NzDq4`*JKZQuHjH!yS6)-sysE zc#t~(_>HhQN0(mkX}t(XxZpuZ7^K{#i6>bCkZ(VC z0sL7!Ef{pqEl(|6Q?g&<@L@u56bC{K&XmJVaYE{T-j(Z3z{ALoJNu4~e5auT1v8p$ z`oWSDHtZh4I3DE5S7CyU8=JQuEZ<6D@uYg?;=!{VZ}0(8K6jPwL_$mi6sCw$w-V0! zsI&VD(m{X1(7po^&3%#^&whjMCc(f_$A@6E_#%_(1}I#&JgQ@_qOuo-t8%ZQk6I^>biI90Cz zcp{Z>AYSo&SH)DI8fr&sT`v#KP|7QPx&!Nvo4!^B2kn=8{op%R7dZ-n>NySk11ys>D|DG4`lOI2My)yG^B2j9QLGoQY_2`kl)ts4F@7YY59 zGvvY+eLF*&4fLZ|PK6zh)b0a`wPb(Z|zO|f9_@R9h@#vXQf9zu!#A?Nf&V5o2gZ9oYtK9^Db}nZSN|k8J1wERufI<@kUu>vT&y9xl^#Skk|k^0DkQ*X4&=u>p^& zXR%}8W*ZPPXwfF#A+Jp-Jg7O*lhcLR5zW)=$;z_!om>v!@WSK8v1BAH0{K943JEOq zcH@4;5&vb@`yExInFYIt5AYSk?h%>uEDQ>{aH#2L-;y+QJ~zWlQ~}M@>H>emJ|BeV z`Vh-7=@n8xw=3QZl4b0DAkyrf;GI@P-qP&4UtyMD#WhastDEOZXRhb`9R?kR1k_T> zX_w)%x9q+Mw6pNodt_w+u)`xAH|y05u&` z%|f*YC3M>|mE98IG))t|l&gspjVEBHou~fo|Aef6UD|6C822zx=R}YE z)WxtiZ<&Q63R#Kom8mu`8y+7WZ%RdO6tTFzb?uHk|x_V@MqzB(pYi<-7_tjP`e@5$;y@lY-V^yOZXWVf zWm(}c$aE&>FuvGVHgNQKts~-#uw_LHlSK=<(e~$gQ?!^hnm;k|C21=}D}sS>&d;GS z+CZf&?2a3eb!NbNd|(zmz^c2P5^_W_Z$jOwg{TCcdKzIMhd}My77C9?K+Grgk;&)5 zMxzb&t%@FD5RK_DZ`ce3Ivmu=1kSAr-5p&@2XKWo7ePOGNm`f&40^9MGK)k_3f&?-f(j$O=Xcy@1hj~$$l(oil&H)+O5J#bit zcDxs{K_mxFNG|P#gDy2>JnSIHSb`NIq~JafI(psnCx&*;>tm0zjqY9?&V=H{>@mGp z!70AHK7iZ!EC%dIkYMj-(kbiVtox1V{x-#W<^5RhCZ&rbwMZ3}-~=+8rELQmrkj`H z4D#JbK5>XsOka1%3)M$VA;M|L0NY~%fsBU9i6#(xfbn; zb*YTEpgqp%CeI{VYMUHUgZxzJy@A?Mfb38;CxwT0YoLBCAacVceQ$u;kxf=mKwKlI zqza#X2i)<6_S)@@tzg$?3oX(5%X8~0!>^cFBpo{mZHW*2L?m0bZx#NkyeyV-;Z2IrBs6!2(0FcK@NdZC_@XI_$W#1LF_Un3YOEyFf zTA@N2P>`~lAZXmLOm8l30(9IcB}SAd*@dIrwR6>~1!CAadUsg(wW3lH%Et$Cemmqt zBg=)>QhOwpUhtduWmejCc0vTZ0|_h9l&wI?MSjZ~ zk)J1$i?5(CfhKO(R0KU&t!405Xi4Y#@NS_zf4E|SQ5pXdHh(vEOgW5LgL1JJrHMm3I#Xyvl;l4qKG)A%FUo45<%H6pmcx3kRxy z@%sOj;{Wcf6z~&1$*-ufJoK_WHkOQ~zu1M=`!K3{Tl7T*O^O793x_vk8TTznL zHHCY!VEK4Z&23ksHTFKkpLp|EOZ}Sz7+i4yLmvV+jqlVy3_nVX7#o?CkrSJ($1#f9 z!lS}g=I)T2)8r!*girI^<^EwTVxV;b34{W`4h3%HuGc;pcz3PS&3y3n!fqX>VL{17 z9u@))4(>TCvYrNLdU21bW9#)l4aF`D@x1q8?7RP!w4rDnANUFZ-85vi2*V%4v~xjV zgg8EI(A}p4|GJMP*~sD?9-*|96k@KiNkHw9e~i!re_%@nD#(lFc33L5We9e5K16W6@9P`ZbGbdszbO0+LO2-#)&h9NUWP;TML` zw};j4$0Qa-M}V8-HRCoKc&`4jiH%~rj3>AzmwDZmN+=Qb75BDj;k*Fws=AQ52z*DJxk#0o2(I`)K`kH^22&KWx>6(YxDO<8mWuaj1oDZV*vP z2Gb;wH~{20S51f%10hKN|6&^dwHU!k5YQ0aA(0r0zut8bhhuEv1B@*6H_Y~0Slg)~ zy}Kw(J5<*h*x)AUeAe11>S~O!G^2fU`WYWqm|@~#^)&-v&~{ZWVTan4Z)hD554v`j z)pdRL+dVmf5(tcr9n}YJoZ2`6%t}wE4pomPJf&2So>UzJM5TTzyMtOtY~hT`-*q+N z?mSvrbI3w6bOcq;<4NKF9a;R<+*{RQmAc(YhyPS@^nGFANX=JbFs=GGNjtw!oaU`4 z`#nKz-(GW;N|-d4VscS-gft!CRY#^oVkk^0?Mof&K`dP4jc5Xb@S5KuoJTFUc%vO0 z5sIAo_?bP_y?De#V0{3VniwaxbqpuKY9EAI_X~$|3Jcm#xcJ`uoimHn`m<< zBoq~x<)?e$n^rQ*vEOcTM3(FBtOD{SHP#}cHuhm8M_8*}Mb|)!R^`^ZrLU zvQjG`fpw=pW6`8MfaMxT{s3)Q245G_s9}}gkfiiaA?ANAn1?iez34`2Pc0@;?V6=wquYn6Ms(^v5DxA_eEu zK@#wHvv0ahE1@pJWFsr0EE-6^dNm%|;mqXwrmNDkR)uZQEXA zU%vls?0-LbJ5xk;>HdhydDk+JcXtIfY8sG%mha{a4x$uVRRZwcj*Q|OC|8y_e?dEN zc5ecaY-TVe|Lee)P$j0R_nr3A3fi}(iD1t{Mk?CMygPLw9*2}}ewiDqdH<-=OBkPD zx_UvisEm5ubXN9I{PW8#ZshqO|+bT>C@eO|HyPoX{;mw{pn_C zc?^L9Vx%DT<&m0HQ_B25h8VD)yW|R*V`v8@W1I2&(NOLl4)BNOsFga!pDDx2`02t} z?vAj>a*MeQRUMW383y2ZUO-m0=V1wi;x2 z-yggfzlT1Rk> zs6-ltfP03|ru>SVlkIjiOc}xU6*Q)_Z^!%(rsThZ$$x)ziT2)WRIwPrg_3iWhS0ww zm~7PA4rT!~F7ZQLc-_!VM4uPc~-{o%6)TNJA`hy!dz>f_#5y ztK8hE7;Ae7%Bmh-`Z-5C7sMavp~+^S?Wgg{p!>WsOxGS4V!glCcOWYg2!4NRNJl4} zj|lW6a2Ga}4%hDpLkp}SEvtCyA9T>Q$6Lp6<|MTL97~6=KdLK8Y6*rvGYlUdIpWD| z$6n;SDBJXP$=0V6{$vkIUBQK8og+552NXTlT{GK`k@S{)s9UkP2_Q|7<(<;v{D$keLPtVT0AvD`K?U{Xj#Gop_{smE{{Ck z>=x@YSGB&|W)jGv(?=h8kSv)~D@qcQ0=mLw-(AQrub=n$L6=>Xa*50Q{G^Mw_tH4b zlVDO`O|u(KwX7HYWRyC$CxN+)6lhky;RYap%yU{F?h|w+BHpOq6Ir! zAmJr%`auevS!q}yzeR^?J>gK_ok4;{paW1>CmYnn6` z=zwX+NEz-bsEDaErfANL4Ti^a`*Gh>o7+xmVdaAfO)kBo_#%7-$M%uXQs>j#@ze){ z_XqnUN27P-bD=7W(1+c9nFNV=_7^G%1k@9}kse`zUwG1%eCA=D-JXcPnR>_vIFmgk zJi%W*NJ1(>>ALEi9} zWUNF*u^|;aKZ4g3$Aebl4S+A=un^n^y%DH6aL>61ztgO_Uw&a$zi4erSE@ay5xtGk zRS)1Az8g87RjhWU!XCXn7F)-od3HU*rhS|iT}?|LC`EGt58o-dxz4tMK?1yzX0-Ma zee%+eUekDFHQ{f9p|;S=)@>=4XqjxrTJOkqkM1ah@e}3Rz8k>l@l^JS65Wa8wlRB{ zy~n@T(iM+$e)V-)j>a+|rw$4U82Ndrr9qx2Pg}34pgF{Xj$O-n z%Kxk{!2-t(T7%rKT&;w9v264Py!%d*@8j=W6{EAPUDe>J;m4sN0XAOhk#M5W&>{LkT&c7eA)E3 zUngDs{*OY+zFPd+AC&%2QRAnQWN5*`g zSTlnIs;zyjGs5RVH11P!Y8YF+h|dMf08;q;8T5Fb0f7PzX*bj+PhVj~>L0N?X@LHl zK==wQ={_M1U$wpCJA%6jr)ZmR?RkCV5;WdJ!b$e^Vlz6gg)BeEt%TwXH$0B!mD5Hd zyLB&$Tl!%JtCZ*9&iLanV$eq{7iF1oq^1|6U8X;L7s%WZNn!8$zGh!@hif$NUc)$@Q;1zGZ#oH*H#t%nX;@_VwouQBL#Zi;Acl%ek4 zVXZYoG{_Nt%MsS}HXQ;mkhCcKLa}U;Honr-H%zw7o8Wv%hzqY(zf~xQsig7gr=%#i zD@1R7y#;DSiaT#*x3uSSP^A?-YQa|vGv+?nwFGw9v+l0JePQ(MV2-D6DI=6Vx7=1I zr7#Smn3lsaDIN2D@&zZXGo{A&l_imTXLZ^{L3rb-1%h*0k@a@jSFYJKyP9(eW#KH+ zyF<=~0sHm&`zh5>fZY7RNj;pPSjYyz5b1-vvzI?R!lE*?*hQwr(qd#YwNOgwn*k4l zYTV+a6K{uRObGRI1AokW%A>(hLyr}%jC~ifr<=TpN@A?naYpdk7bNs4%h9deGNr-W_ilyX>Dt z%i=gqsjgNMiM_9Af~DAry%3x*e4#(QX<4zHt~k`#;-zXN6Eb=fNJhng-o45w5~hi3 zPUo!v;!nBD?|0nndaf)BU~2Yb6Yh!HlVB0fY6-J!aU|zZ9eIQE z^rAa#$}JnyFg;5uR3%Nt!kj|TX6-jM1YH)|TB&Hk*$Dub-l z323ITNVEj(rF;mnXB^pIBfWz!rSU|%VIv5v)s=ix21W#)JFSeIEYRa-Zeg z#U6|if8CfX`t&bG6CEiZxFoZBVsG}-IsYE1|K+Xzb2}^bOxV}dcZgFR)osCTHj<8c zu}mE`R-l1*4NG=nnJN@n$+0_w>g~)rNH%Y$v|oXmp#3Z~6N%yO{K~njS}B60Kffyy z0)~CV?<-|xJC^FV$u+uTDH_j`O0zNZ(dfFhb>j0F6ywC?jEIdWkYh`>h@cL9se*gF zdfO`kzjlPS_zC4Zl%^apoAJ@OpPYoc$fkPG^g=Lt+!x*uiYIG=oBg_@TJ%9=EzlPgQM@YNnA3m-{f#r)G(| zEhfUGfsdclM55(PYT4b8BbP&`b#R<=nGdI7Dx#vcB36bcbmmt18*_@|VJJboh1p1S z34wIN8^A^1qXcHp^^tu?!R@Z%xwA5fbTBjKV_NHHVEZ|&+wRxYr`u-*!1M5?;r9k* z(fd)9Md7I>>V45OY2EiXI}y~m7r4RFATf!vE`$0X8=G=}b3{kzzSGGeBd-?`mNF#cBML#IxW-Uj2 z9NgtsI^$F|+C@xH{%_QS&q_k=+{-(8$cZgK0!CakQXtqH;tixU=th`tk3VU#dDyqn zwk3wewnZ8TP|Gv#^3e)lsF(rODj*60t*C0!5RU@ERtfjj(236XC~#>@p9;`r65b7Y zt3ctOZBQN+akDso#D`7WtQc8SJmiPyYXD7W3Fk@@tF?pznKDvKwYdPF^hf9Ms0)y6OW_9_5e zU^@!g8&Lc|Z30BB8VQ-O&$INSLI(HUIY@+&H$5@5PW5(HR8bHm4NRZ?ndf24rdCd$ zxanpd_P2?!1?5{X6SDDbOUeRuNKZ;Zm$+7qwWNS|Ib)_VJzinq365ndT7+m-hsO}3@1*8-y04=np&?#uaOGCVfN=Jm^dXwz$3-ONwn2s$9 zd=9NiU!gmS!zNIxU;<93u2yFm&@vflUJJ_IF6sd|HXo}}Pg=p{NNRvd#&&@=UH|6b z`(2Ok&Qn4AP#iLccM(fj)p%AH>1@utxeAnqXxKHv)1&)>0n!y1uj>wlzR^H05Ll! zm&CJ~HJ&&;#K)rq-{{NZK*z zzvSi- z)nOvD+_)W~ccbe_?v5jWp^b$QAoUf`cbxoFseV0-V#wAzhIU?r0VIZ;JtmNd<@@v< zcfGQfw)3Zr)SuFBm1R++zU6cvV?qQSdWzG!HTuC*g@)cX6m>j25ypZA(%{`;* zQx>Chg{;H?JsN5{ihh$j;K#>g0?}G|9+L{HZugOgQ7T{UEN(kU&xXqCRddb@@3%P; zLjfgr>pMQBKu!WD%wIIB{wX3O^zu5W1|R275$@ljvroAe z8&Rg|Ai9xByn%-qfx>&nT{|T_HjmLu1vUrrsPtZy05^|V{d5*@{RS8@jq2rWAUI3JR zSxS|uq#tJ8YrwsGXjVZpekjI9xkS3jBAPp6zAB0Fx@Q*cLmWeX*-ZZ%a^e!GnJ_OR zKX<5}a<@E}cCA2h{L;~JVl4uCQ*prUDor!U zPZ%>tnjtp=Wh1_8Mh#<-2wjK|`CF0^=c|YVT7c~7o3L%GsXY5?BVs;zq{qFuVs{<2 zmsg^E8eVKiyqTs3&`QkY;R+~^Na=TLfbEb0xnj(#z}z4L?D|E8hldm?eO(A0BH$N( zF1m>TT%)b4%!>oMW8B;w&}rp*_u7@tbj2oh?3nF_IKm^+<@Jb{*%70UW5!~qM~qbv zcizeNh$ZDgfyQ1oAHwV&0NknpGZE~3%ZVY+cFo}39OP7Y-@j%NiOicNwoY)<4w5Sc^;1sGCRPHe1F7m^+z)ZavEqsme5e4>% z`f;lk#GkvOr&?Zoujz|y029VwV?+{cu5^{6PNIttudSXf!f$20iw z(BZ=JxlGz-gB|fW5g56QiBzCRIjgzH9}hZsc8hE8cDJVpsrV((quZu8-X!2@?Sa#D zpifmhz{)fi#u|26KrM(~SV4z5bZX4ONGi8VKOK*c&FLj3m2s3at&~`eH_X!{c~4!w zqwA}+uQ5`X;z7YWg#^dC;j|7?n=PL;t1JWMZsf#mjxKd#i$8pwDkbGK*^+wAZJfNY z9lEy@Ae@URhY`f2QGC3C$rKYePLm{6u#3Uv6P3uRPlt+N_-WGB|_~JZ`gk#9CS3E(qy4vR5foNAAmMz1ym`%m-HwZYM?% zB%3d%rsdQ}(!L_;jLF5>i)UUaf&;g%?=ku$%i&$MC5KhDY=~JH3L?IPFU<7h(%bZR zWBgf`c}1^#_gIX0U8X{HPj*250%$H%X5aCC)|#<{e$ca-UGM)-8L`50EX-|V4E1~C z+p+dQCR5?pv%{{8RCBTIu;l{fFqn<#kW?Db!&8e&JiH12DZ|Q`fJ@MFn??O!e{;b9 z{;fJ;*rv%NlD(}WgRe%dHI6y=-<*|wpLWC23tl@5)c$2%--2sC$dbqZO7Q$w8e)^x zYM<<}f_yqvDl+wfQl>At<+VBg2=vp2SQR0sJ6>KRD5S##@Sh;3kN<=02#qiP@}HUf z%aI8ID@L{~)SuyLhBHV_`Y(s4O%_rAKcUMst@O!fuHik04O^=X9eay#WyNxkbHS|8;@? zV>Ydv2;U7F@&LPaOr{}(_gLFLExkdIK(vP8i`LyfQFkGP{hT$C2NYF99bNyOqQ1aO z;`9SVR8+oie7`5lBe|UVxYd{9Nb>zZOv!)#{Qp9aKSoto3FGUc>SAMS`$bLaQaGm< zg4?SSHdO&mWcnqQ=IciI562$1wsBo8Y#hm!svWT_!pkCbvt2EaQ@@+U;_ZHT0VfmQ zUl#G-9z}_ZjM(kYn4t#Gu|vEkRmeLVN9WdY2F+_#YXI^-SjQP;;DXA;cSr1yB-AC% z1(&{=QVh*-Lt^h{@>?UP`kZL-Qekv|jgP;q*DmP?wVRYagZzI&a~^>u)=SPth5QQrcqn7x(aCZgB^=j?bg<6C+lqWlH_`EKJ6Rkd>to@ZLh+p-6* z@1|R%6eVTi&N}Kso zfV99UDkrLt+jY=*t!Fk{DKM`_s~2_?7S|SuL5+PbjYoNzwIgDfIV5^8tb2!VLd)=_ zieh{e4w|6OZB^D)&g=nSNz4NK*lomJAFGs2q)>WsDCC}~BvAN+QuO$s+`v2tkHw&C zc~X;gzv+{kfEIjFZ%{5><47q-#<}6`X}tu?-kM73iMN+RIn+-whfwq}Hl>GpKybPd zEFLi&Mdcc9`gq@2zg;pDb_TyY5v2oe=Ie>6aR&OB3zYg!mQE5`m3AEIE#$@ax+%@- zS~w!M0&6h0F7cJay6<8t4QP^cY^;M{Q=0T+Pv$HIl^JWV7!$uN-O}^*Y<3O4)-4h| zDGF=j=VSSBtG|UY&v`Cj*NH8H!{3MT%Pm_=kUl=TH(VsDL;u>xWx^Lf^j&!sP^i-f zc;&O3rxkP4#9EnuYsA0pEjUQ<#peF~QO{j0;XMbj#zu5{xiX6qIb*5baCR&x6M2f4 z?wWyW=DV-dDHnCrPiCSF^-2VR9^gAh8??dinSptab-tIdJAhM8rx)1HCRNj95W8v| z-_Qf@m6O!Ey`jdbw*xBz#sj(7wh{<>bc*4h3LYkA<-jNUYGf#Sw z9Ui9n+Q}B9d}v6O4A2W6@eP@fwGhIi--nK}DvW3d0po<`UIj6Py7BIJeNJSy05f+U!s3dr8cC6Ii-*P0N)zORm_t8L5xIuPBjo*Mm6-=th9L1Ni6V+ z+}!m@{R>U`F(uw4^NE*+PJlOmr;0Bira7|%57k0>NSc~z)R!bjrUxtIYSu8|D$$bd za1KO|f6wu~2*jaOc^YvxahPxUqD7`A?bavlI9SKYyLpih;n~7blqGVPMZ&kIK$xsCAXseR6ULR0!ldJE4q7 z8pUtk@_fjWH_(8oBJdz8LD96+YKyfnlFsD>te|t+Irg}q2*nq@D)?dA3&*H#^FeByXRs$ey}wU2jkK7iI_o$jZ`-t?xLiWNo5%25M~V{pt0{YlDXcKprkSB4y2o`OKhHPU`q#uwtVn`^6XRg>=&`%f^uuH{1ODf0i zcWO?3TvqArt2In(KvS|=0XXnOZSz*)#93A?bUY9u8zUoQ*@A{_{@eu+ncj8W@GUf; ziyCmE8M6}}G#!X?JCxcUS7wg$lq>zZt*O%?1SwXcOJey{Qkde_;;blXG}$besq>{f zAMp_zUy#9LTG^}jTugQZdQT>3&Ad)H@fUo&*JBn&J!x)tYx2++9)=d1zZH?oQ?PF# zZ)$a4TK@KFY}j>S9bIECd}*5StM&ChZLHhl0)BH-{4ZNGC7wwQd~ zek%@IXP)Aq{_vFi=T`t?F+sN$b|a%@7gYG&r!xV=P=~00fIL^Jt&>G+CylwUKP1Qa zm(ijZu%MHh(JqqI=rT;i{ghM+Un zp{ap|93@4uZH1~Y1hqJ?<*BuUr02r2WV5-4%dC(Xa`;B~YC`(}PJEADffGoMpJ=tM(C zT_Jj1RY-~iS++TAWWkNhyVtw5*Q>P_=afqOavdJ83n6WO;~E=DFrc&j97g$5kp*_x zotP_2qPlEvQMWPF2(**WLo1IkP{RBYlvEr(jdwim6SID5d?Qn@%b8U9%wdMWLPGgM z=l*pfRDyQk!_iZSIFaY8O4H#~R>O6{f50!{!Ges~-x`magO(l+T~=20q{SHHmWUQp zlWF9#sVhN!V{__RCCFzyV7kMpaIz{`CcCGkQ#nWmAvym;nEar#DKBp!LrYNqN+GSk zQ-knqWGmeb(@p|nh*MXo3%Kz_I@~C>pT=h|WFdah5t2hb&HE~w(2kN)J=xt%KTL+A zsM8ClkSmnX7Go?%pDAYnCWu1I4@uWL*l00`8t)B@-9`Izq~zkjr{OR9iiw{>*a-X< zju8-fx8oSlqHD&I{m1lsGG$EqCc8LYJFv#@Ms08L0=%Y;5=xDYM1E5{8C3i`b`4&9 z<4BkihQh-k2SV*kuh&#|KWf;{H5cLnPY-$qIH?odGyx{?R-K>DQa>Fa{K+J;USdzS z_=u^oN&noHRuGK>WTW(xNEz|>twQ-YHtL=2`rsgM73Q6*b~<@hi*e#$>6{MbF{f-? z(@09L+BS>MX=V&NL&L1wvjbb877ENjIE2tYsb#^W^~J@7#j&2H zWfAuTHTt#7QV5=1Kd92fU^LejC4H-sB*HFYrBT)J5lIDGR_UAmTug?LZ3J$hPSPV; z0-@@#45w8aC%o2wu@RA$35Rdq9?p`k7oVnY5O@~q@kQvDci`2AWeBoF=_ECC$gw;d z6q|6ldDqmj+pLh_W!uD@QSg3HqCfWN?VwOeSV%TV(8^1I=PAO>WN#V`jP=?hDW5yN7$~zOy?_$sp40 zIOrjOoD0}utW+MG2~5?GLMzcBBI?Y6&TH?qvJ~e^9iwDCVv-!TJ^rQ-0c|~Yg?5>t zaciU1s1iJnZS{roW2s6ve6@s@cBo9pjZpn1N2vK$08efyxyu2*Wop;?28G0Q8nUu{ zjOFyS17x#{uDE5?zGUXosI{fmjg~WM9wW5Q(IZq`sDJ9)iO>z+GxSretxUCCaZaFM z3#Mc(i=&m_X>-$QQpxqH04blnbx{DB+Tt)6#a0oiwG|5ZVE@9YSk#=7Qx$M<3bT{` z0uTKy-#%Y03cr8P@_4ZHh?4f<6rr%N@OVTRO*S&Z0sB9x@qgD=e8RlmEh?L6Z3zgF zSe_#b5rHN_z!4zYZ zb+^lIoK!+2o+s_uTjHB+ore4nTfl13XI@lQx#VJk9G2?!=WL7`#Y$^0yyx@yA&dlS zUT{grwI*7|?&$oXcuKxy7D#{b#4WCvN=oPP<0C)_6>-kPplb6tY2lGzGv63HMUR7w zzI%vR7PtuhIxE`1W^^!XS=LtLLBnjkzf-yX_(%(q_(^C5$C8)^4~LFSQ?wOL!WC=sPmnA{aV@jqr#D-++e8x1-c)|X7 zS6NDo5f4IF?3<(h1cBKgR^AYSd?D|p0H=T|Tl$ZdNz5p3jSyJVLab!kt0Vo9PJs8L zL@R!1{0g@2&XNktw!phNJK|+u*uGcDznG){Zn{6flid~4`Q>HdRV)AfamTSfCWCJA z&=)4dE@amI1-wOp8#Qjm5r(X2t^Fz61qPZED2NTn3;YAZrWP*&dR)TjTK}WQu(W8y zW-kUkbhHl-4%G(k3#3NtA;&s`G90t(Lf18h;YW})J&x@T!_!uQk=G>~$yh8KUS3iG zF98%gLf8ED?#-uyps7%IdH&p?kxl&;I8%oqS!|K-8wcS#pWeTyl`*HfOa0JIvZNt% zy*d^poGuRaxag-x?u+jF-NB*mh<{z$KR<`o>SeV@dRG%v^aX>xvKi#{Hjf|;~+zoAN(V!A7t z)oFIpsffnb4j}RZZQG+t0qT-m&{p=vtXR}42*8is3__a5oiA^w${Rm@`j7)o3Xd}` zMs&3cA|CoCAkbf#MWD%FcBjR%Z*2CSVvLwzp`@>kn7GPW7$Wn5u+BZdEZI)mRB#ev zo7{g*<9mr=%^4CJbZYQ;Z(fDZmBTlv)a*GJ(!f_fLgtPQZy{Fd!}R3Z?1W=j-v zQa_o8^iqXru`KwGW9~vG@YhuV04<%!c<^ouiSo)&x2Z1F4*7Y@jy(SMlD>cQMLJ&Ty1SAFCgI*H2 zz7?iC3vb*B^+_O#`nqi!`HM-zgxqsBOw$U8`}!Je#|W*BI3lIZV)CNZrJWoJ2yG; zUNTxj5V?f44F%@YF)#m}EEQuRwp)?O%I4?8<2t2az-nx%aGL`~pAIZ z2KRa>er$**=!w=JWP*PVR;QcxBXW}0u~eRxos?|yU3Yi@pVe-vU%-rcI++#7A9}+tB~|0%_6Khdj5!&r)l-A56r5exsioW^nH_2G-H{$pry~Zy={Y%} zR>#U)+8eJIN(49I@Q))FW^cB6$i}6P#xHv=(3Zj(qZ5jYq{5fbypH|sh||$w@?oGK zw}6j%x38+u$$S2(*zFS48mH$~0QX3J)eub~YtmRySlHedsHX|M;dgK@0}!na zdtHmGD?Tof0}=ER&2jlj?#TG&7Cs3~#v( z(AD!ywxkC|7gHB{c4BvYX`RVg`iqoAFDnocP74JuvzEHkQ#Baor{&5XL0$H){B=ZU zg!Cd(HZL|Mcs!d?<8!5mvlqLY0|0fIi%)Wl@lm#!o}D(m$CUI)Zxkv#sNu3f$_X2< zMR&K4CGjX1MfNf+)-g>!El5S*H{p8{fT?Ing>+48Fg3X!2D5~+_3}uaij!UlE2Ob8 zR9o=aptsa4;^TA6X*@K@ijv@~em>3ThI3oGIB_s8CUq<5NjpsJqm*DVawV2EnhKez zYQTWlZ-yLD0i`*nABmG4O)>ah`)TNnoX!P5GB1TOd4bSS&?+4cEC9ZA&GIHxs#iO~ z?(_TN9irWE3V>>o5)0E>AOq>qXPXO9vPHj$Md8H6yEuqePlTfN-#i1jw+&2ksB-;o zVYG+oTZuPSyUM#B4}V%0bDO0(T_<$FN*9D`Xb$B)ewBx6tyW&I_!MqMr36l{ZoJ8Z z4kA+OZ0kuV?A#a_5eeh9l3CEnJC5mdNVAcezp1$Gv*W zDVK907R?=1$z^7rS^EH`oD_q$Kp7TR0nK-u7Uapi&)HC_TEGDjZSEirn$QzjLhGNv zW2h15cxSL-;Rt-B)?ADLJ17mF+2lAJs+!lU=~6CMHeaKVfrWFp+#1)Bn;COfNt4cF zW>m0Av&~B}A0TtECqQZH3;>u^2q&!A<_AtVL{d=+N|B;&;+YT1PQm}H`SHj?Owd?! zIZHe&02=bB<>|4P_d{sN-&R8O(Vtb-))x5y`=CoR{3~@nlD{p>v$J7g+{+6y=yOE) z*cVOopisWn;9k~jz4b!f87$)(V2mamzl)z(gLDLH2Pr2&cH?pjUEX=3m*>$!2OK@C z;yVs(#tVmN|0vzzCCL~bcE5q1oHyS+Ja$+D?-^p&NQJW}6*$cOed`QT$~#5l*1V2R zU}WssQrRLh$DBtt?x6XwLDwlMHKou8htLM(zB(i4^q`~Qdjg^H(3ZC4GkL>Z-}p=N zM%@WQ^UQDJR2c(uUA|U)wI}9u0EOSj*=X2+po{ zm0T|`-z~gR?tNCK=2Zg=J#5vAIiASdh%dobfX_>D{K*i@Gi3gc|mOzPJAC zUD9lfTMVil?85vXrCSrN3uLMr9bv0>K8rU)jvjrU=Y2NGy*|l#aq?^$6$LCeY$7Zb zr(jgHX^KCcx9b7;g?@Qc)zXM2ONpCvt95i0&fm2VA;KdWTJn_p74jw>7z^o55)nw~ z{tS5E4J?L(^txUNG_$ekT6%g|GVG0{un=qA8P5y+a{5oKr%P}vJkf`AxTlLN%QiWG ze}9W9fsG6``ma!t4+l9{qfAdi1Ie$iwm0lFwO-y1=}jV~ws#)n>C@4P=gwoH!ZnNn z0C~g7=n1!~)bk$2;2OtHUT4#Jsw;u1+_(MU${FW$TxeP6K4z8t?pqp-<&;)!>}e4EFh<>@8Bbx?D6~d6GFh-IXZ|Q4xQK_B z-jL_>6rY)wHBX`4SKok*rmb+F_JYTKUI#n z^|a%26oJr7H7)D!flE)WX$^(DEQWzK9T-VFTQw-Nh zd+;T618mPyz>-oB+-P@oiKXp?n0Fb+&+K+}^5g-1oEUE5CQNo)MPoYC{0T zkPXn3-77t-8#*N{T0cY0w5I9t2qrsdohK*$8V#G@EtOreKkic$>;3Z6K=o(e#eI3c zf09uqo7#lLhyGfAmYQgHY@@?^wuxD$v$2k!G;ZZ%hrbR*pip(4`VokMrSfG9|Jr_w{m&@ks6_xGK%_x^t8pScFF%b9o9T2I{f z{XFaWSbB{l_~ag?BGW^*-_c+rV??jMjItJQeZm)qwxjbc*s5Q0X5#c?_d}8L?`3Wm z6aA}Cv06z^3B>~$wqTP%$@;9ypOM@R(E6WfiyngsJBUs)UmH#&u?xOr z(6mW4&A4GFd&qbQ(}nz|VKFhz5vHd>?k?EK2^UwWu~3885qi~^D0bdr;jOEjJ0Lx=I4VJu|d zD~wpgo9Gv9zr3Apvm8!4uJ6Ld#l1S8RG!Wjy`L1-e1y!fm?H4Y z@B|Fq#GjURV8Ek>R2oR(zd57ooC()4Pcv^L;I-88+1p%c{ore=Eo{wg-uJ>#pMXP$ zvQyygAFv@;rMcB|m~pB(!&@@AjCs{LnEqNsa-HM zf-^3z2Dxvv<7Qfvld<4t$IAgq6M1E6ORm+z2>-RW|Khz5or89o!+gm@dW!!7BYpgj z72;Z17g&{ha~=jFWa`P}>?>MS%2@)jXDO(g6y!Y^f=;1XwnnlTc&YFCZ@_IS#GLL~ zZ>1Oq15xj;MNZ@vk}_q}@$n9ih?xQ|lUkzEI5PbHRT*|o(}{dDxd#pI@c zx>#DULuaK^3wgdqS_(GRaCj_~^+k`TA5Z1W_iTrRV%AOB?_{5Ag}>MlL5+6Y6K2`m z^t6Xn8ATZlL-TNnWaJgaZ~O#oc}QI$X2qe5;#(hQSyYAG!&a03Udz(q-&FSA(erF? zf4=TS8wArXDJxUwMrmwp{9_D<8TU-0u%x22{0{{DcgTS#qgYGFZ}x$e`i#nXf8?z% z*Qt4r`=I{vEkHfzFYo<;*T1t@L-rWfBXlQzcs5ugYm-^v^)bc#s4VK) zncAza^m<-7kXI5dgUKU&V;II-h zatXs50je~~PO}X#LX+B6t6HAi3okT0Zvrt!Y%LIJ=bAVWm49*0dtj~yM_GJB3U`U@ zTRBqcl^D!R{7Aa+Q3N8$_1WDEL~szn(MT43k5mUY_h|0@t^I9n#VGP~o2OY8>f4n; zd0kzyfYbQwwcnu)aO10u(PO88vls2K8U15g?t}A=h`ySC?%Iwh1L%AWfS&`J2Qq8k zgWH>%Nz?hAw5%FTj#ryq>T26my7cc~-UaM73p|30`g)>JYI?Ijz|;Ndgs8+&KJf;! zzW?4?fI?7OuMIGX4pkFIi$?H@gCiq&6KvDrKzUIQ1X@gkBiu>|R% z#{MLr&a?kAO-qW2MQ?5lA3Yn&h)?Z*Sv#a4GFsM?A>cYv*FO4YIO9xsmBF@~nXK-_ zQFhs@IAUnT7UyBmSMl4$&Dz=y*S~J#7kP!h|H!`}{O{%5M(ouR1YG2)r_Yu4k2pHo zUcTSGl_aPWG(?dyVo=whZwt;pz?d=Y(jBM7lRIxpmQgYJ^kbz6<=nB7H|Rr9>{Egq zPqxU|iT2mFzYC_oHeYFIG*ni~6S*vteD%!^8DmeAU&bn*P9?M+WhIQ`sA(XXefr_# z_uf=Qn`!t1r{#d@We0iH+gqKJLo%|m3p`APgji)^DlFax+lAkxiO$ve9aYYT5@``y z4zzo1yYGYP7>CAX&gT8q{tSJaEjuwEN%&N#3$2lm(gq8Vr^p%ubq-w0#Ww!7h_9 zQ|3Vp2i+nR@pXa&&bEUI@$8Xa;+z|b5>B?>{_95&kFwSt5->NNZNd#}8Gp}&`^C>? z3P(NzEp+DgbjMa%w;71TVqyIgY&!~ESesM+&^7yau2bhne z#Egux`frMzRHo^usJhMXCi$z?@Wr|aekH2IJv5|N8s+jB%JyFSjI6$b8JSf~Ws8C< z`%2~m$`!r;XG%a&0Ld$)>Qz}VJ}_;9^b1O=I^7iN*Xe<2enC8)QeTJcsVW6+-5HJJ zm0(F~X}tO#+fub|S3x@C{O$Ad5k=*8Mux0Xjrnmc*=9L1%A6w2F$k5SjZMN04xJ_E z^`29o`@=1$gbXV zmO|*gGOe7|V|%{oG|;|CsT-`QM8v<=vp&M8H2B4I^g@ocRhtFF=D@s+Y;_$$<8-e2 z@?~Sa(OV{MyEs~UL)hs2m&*L#KOC97pwo>|AMUI;E*lj?tm$;p<`=9qXIL$R#pxc{_;ZK394Ia ze4CmOsg@nBD`>5*%y6({1q{bC=)h;<95Kqkh5f$ODX}!LKeTknlKKd3}9-y=A@^M<;(r8T9lY_qepHj}l~$ z0zbzEYw6ZmMhZ|CIvEtc3Jfe3%T4HP|LOc>_3X6~;*NIUcyt9~qn0F0S3FukNksgU zoET`jzP?^YT|NF8i4e+%EYO3u*%_b=;OghAxo@wH!*AGq?>=;Q6L3GS)!iNK*-U%x zyM&C49QTaO26DLj-m8}&Ou=4*(}s#b$CzIGR7ud&kbN?1B-Vt;T>E}2+~u$#`hPcu#Ww-l&zdYXUKvIo%(*@|TSE+i5=6k*^36=tP3!P~7% zh3rm6O7T}G%wCE=$@sDtUK5QBfhaVmCr>_EnafJ#VVCTm0h8Q2&fGI8+Zz4zxaw$m zSSL6~;Ki`+>`5Ra3h`)1d%Vnf> z-aw6A{BNRu4Ngxp$JAA@!iSwSdIug-32%q_S9!>gVeqOQE}IEj*FWy38~qNrwV(d4 zr2&IpC{dwM=uk^>(`vxtiZ4(_x^)-dsoEeq5T=-3V18uOR{9j$uG`!yvUXHxWJpuW zbM`|}O3N?kLcFmAlWAg3oL5;&rf)bG6xE7SBYe zgP^bno4e*V#XcX>;lfP?xhHI>hX0^BN)bMH-~b+6d>!>Rsbkso${ykavrXl9%4&IZ zyVf1za&vCueY0C^aq{QqmV~{6!q9QINx^@6Cd4z}(^qSwZEkLD$!TgPf?dyd#%mpy z1ygzLv>*-dBrzXj1kLRy66m-~(dm)qNMQL(JRa2uqx=56(637R&sUKd{%38eoF!zL zt@8Ms&;0Wh{|>UrCER(7V!Cr9G5D{qG)NbdE!=50c}GAMhPT4-j(O7b;93ex!_!J? zIF*G{-F=2XF}2nJZQoE|ejgagBTgF=6Z6V*$vKn))9c~RdmaLUkPEu~%19U<8F`tP zHO~5fyewd9RanoSJxl2lWieBaN$VHKc&N~87?}O?9`uydC8%VvlN~&7LtTiWiL{oa zbQ}M1_MXc{Dq`xuTXh6pXvM$hsi&*FBD4I8kgqZ!CyBsD6FE@a-@l}4UHN7oJ&b~g z8rM5gt>#)UPVXEw(&6t&13$??Br#(FwR1wm1jL+e6v?WA!b_`x9RVf+9?LY^07tzx zfBiD-!j0dr4`%96kWu~QU(wKrpNlK^=N*kyR#YTb=r=6{1#(*s9akWa>mybV@lpTw z67-I+q@;vn^1k!-u+1YKhnSV4PT@yi-{PJQX(fsc*XdTvgd5bo#l^Jw`FX%n_XyZg z-@WJyo7~2g29{RpejW)c@$M{Amr|6zTmdTdp(M<=bytt1;}FtxBltkP{X=~D`odTl z49sH(ViE}|FgN*gz69k{#54H3R(aT`QOioD0f$$`SEpcNVY+CH`xQ#qihAkG`MiPI zRlTsp03s4W5D|;kue6}!1QO1E0nBN%dAz;N8|Y=C5+d9NFdf2C{VEe>Dk#Z4FiMpOr@a^{;(Ru zKOHnYEI)ub*rK1nAz@1~l9rN6>w;JeCgCnHq69CqkNd1P1o2=gM{2xeD6F&|(`m2F zz0w62DObQOjAr?(pem&|?I}-)(njUg@r1NbuO3Z6azRmFeBGBa{DGlM`yZ-qo$7*~ zgW$00OI~jy63p|f%02(c{lD{9#?+B7A#A>_B)}{;zyqF#i&@*3>kGd|MQZgvMjSBp zuCq2)T0uSZv(Nw^pVlScqmH~{$N5HwbfEF>{Jcw$R0RXY2y_qY#FDT2yUYJ^+>lXk zSU>Jg(79Ib9RKd|pcqVM=id%@!(THYc536}xbcma5B}y}w$!Qoo}@hH_y>M^e#3rw z!rR;Cgf?^(%Z*=G`bjcBN=UI>lrBp>t=WWGk^}Zy59CtMuh6lWmS1nLL?(dY33M}C zyte_bkKiK8d9_Cg3HH44$_5drI>U`Tay{2QYFAgpVAqQuP_yYpc+8r&Jnh@wg=W<> zc0bzBqTW)bo%fHzveT%MEwi*6%*-k@j{G%8I`&gLoC+1L#(lz`ll?9?ENCc|sBFLc z>K9k64Gs|X3160)3kD^U;Y`^kPUZ%{!(DC)Lo6Ymgbw33&?Xatyun+E4hc8W7yaPk zhYf?=>?Pb@mJpNe3mANkx;Tqa_IAaGEH-U!#;HNuPBV3?9quV`IpLVxYCz1*_p9Cs z@EQa0E?c3H`b={Djb9ta<;m4a@0nWRR%#!#EMd)=S&{PEtrGNi$68^}H~;A9GA3G4 zbnG0ij<5P^5cEpQEU)lQPGP)Eo{J??Yphd|G9OJG!^F`Bbt;vP&ay9yhC4>Oxh|b% zz9_o}NiVr)M|7^N&t(cSN-B5md`P7?>vWC&p{Uu{XInNon%EGu?mZwr`fJXa`X|Io z?zHGWV z7ag%x+MAvH=x;=K{0<8|*N2`EmZlY-vd13B*+s{Vc8}<3J^(#^TDpdQ(=#l>M3*A$B+WHhO(!!hV1HDb0!#Y-9>bXVwvx~479xJ4rR9v7^J1M z^tPNoP)VjuKi%;lqwn7}+-F zgKNa<_+{XZ1eUx#PPtv#Sj#gC8x*`prly6BXzrxgdK|e*44T}@d3`o z6nC?F#&%JFIG*-nMAl|@IZ6u^buWuWn}u0pnyfMet4EF2*)WL)bEH;NA3ZXIJ|E(n zE#a<_YBU?NZt0;3>l1v$4?A(vL=L*)CgIaW;|bW-sta4s<{^V0L&|d0q>r-&M11tm zT3XfVR~vpUb}wc8^p_L03l;)kg(yxNd})!B z&b75HWhh{?{@2nj^#CCF26$aUcg zbYz}#YEpT+_E*+T&))9-x=Ox-QT~!4x+CVWtc(~X7J$e*{Q01goXk?p52-BDQBt4W zWQ*-@5*Sg`a=J~;xD7YE`%NZK`{Abs*tcigYBFY9lnn+=;c1#U6eiLknGNyq;jFxh z6dx2`V-Y&gQ&Tb|Q(9*SqpH2v7)| zK2QBc@Jcy)&b9aP=$Xx<{D}~~GNlCoKCU4}R}*&PeOu9bpucgxuD`z~df)pQ;Az@nC~Fc3fuFx%FiM1mN7?&O^=pagMM;zeiWrtQ-H$!JD>i=C!B+gRk7@q+ ztLpskbHS!RbA5WeT)t^ED)PRF|LdEai&9S~`a{zem4+$7yU~V4 zj%=$Li<{en4pz6HZ=LJ3%RWQ-GrO}b3)7gd6B|ck8w}&PV`{GR4U#)E626BT$D_yd za!65Ya+o$1wrG5VAM`O_DLd(wMEUdkO8k&B_|l-5iygI0Ey>=LjosQnFM$SrXp@MZ zTDMOqg+E-!pu=}xHq|$W=KJ(CAj`*DA;Q@z+ApM?Bk7bAs=&ZmoE_1C7ga_Lw_wl? z26~FG-@ZF5!f|!du6ci-IQz7v6155RkCNQab^5pUW9q1?Qi1N)DJ55NI{{oJtE@~p za5&EQ4_RdwFXq(jYEJwkEi%GB+q-VVavIY5Tf9z_w_1n#U90 z*~11dE31}zb*_msJH(|b@1&XfP$VZ~Ux2#9GC~y_mFgm7uW6++cyLV7!S#^TM(1Js z4OP0NRf8tzgRR_}Qllf=SwvQCaQpMZ{@9)ImAWmzRN8`8a?iwfY(E6PmheUoBosy= zy{i*1pvljE9wPx>E5jx%x6s~f#828EdQ(zMp>#XmGhy0YGFPm!J=LAdES~pOenyNa zXL`jXdn(Z>h9gmt&25G`_4)?^Sm-|2;5z-uS2w%U=f>ydnOV{D!)*4o@UW?D+w5;u zr}n85!_GbzHQSMtpee`E#{Iy973k(SJ+SEh%fR{0U^;?7Z;r;4>zKLl z-)a@Vn!qJE%vdh+SsdNGyEyFKXua%W^$ke&4w%JND4Y6*2pu_Sm`EG0i1hk#P2lyq z_Hv{((Wq!t1?(-t33O7gT?=nTik{9^8nT>ikMh3@6uhd+$?1BV#Aax#gefg4d5z6~ zFY#YSj`&jr<@Vx$yBnmb8Fj5J9sSaw=yn=8a7F8_Qwr>DHlN15gG-Cnhs)gZ*D@oq zi*{)d@ZZ7&!pz@S3Kr3Lmu=mpksWNBer+7B(ta2*sdd~_eZgaB-Ig^Gf3V!>ocdgw z+LX3Fo~pjtaa${BT~fZTflDG-+Jk&&W@6`8hP4B@nEdcp&d%>_`9aUaq&GxJWcUPB zzsYpD^n|4X{8YZZT%?}%K$~qGi}W|QVH%AX2Y`<1b?(6&|DI2y5clf}+2cYksL zyYPcMHrkiRJRf~C;nCKtAWcq6l^aF@($Pz6Rrb`;b}KhLps1W{lO6fA#01`}rDYYA z2$m{U2rrv9vxw5LSJy>q@=bXY0(Ud)w$;xx{8$pc?U}mmxvX*-eSyFvAmUd@W7*Mc zX28t%Z2aT9^;y8(W|N|3B39J^NH6=|LbeU~C?#I(#n| z=8?3^bvO&|^PT_0&;Dyif)3G?G6gMA-~c;h#`gppn=1(9GhpoWItU=x2q<1hj+jU; zEBh-slvrV4E6HmMpZu~UKBy#xIEMiF)yIn*zg7^$VA)pJQnt4ja0inttx0|?xoR>g8EI1 zfOqKM49Q*1`kdrs!0!XFl$N4(~871f4 zTQD<@x~>v{f9>_pi3@w~pSv$tl58gUNW3;=r_nZ-)hNa0zzvI?8(pRumOe^WHV{o` z{#BpLH)o#tcyQ^J4dp@n_l`_0VMZsOvLnvfKau^&2Dg0($ze?yn0$LPId0AhypKo8 z0^Rwbk6dl!?C=2gV;7V}+}F`X9K3y*(XgVQWi^ySbyH(E%SMQ)qpi(Q)p3zVfteDU zyLFd?@S^|CPhJXrll9{`bl|K_?)Rl_qFyGp!xVx@xj7g{(;_9O^@a$dp#KeaV|v zG&QIdVbmX>G|$V6{3?ZF%YqFsssPYxcrLDCvRdD!CHFB0`#imT!4nIq9OM+A;iF_e z!G0M3ae6#uG2j)JFJyhe4bVeL_DELqgP$FQVy;d{sj!|z|e*7Fl4-aCDtO)lwbT}eRqT@NHp zJ_}`Dvl&to&fG;x$kQ$?oq9S%()s75>~PlBjLiOmzJ;gCtj-P;u+e>8d5VgZ8TT8`mdUKZ#a0iHh@uh+$Us*%NHapb zB8@y(={l1RIKMhMP_n$JE~T2c|MB%{?S~%pR8df8OUT{%?qpnYG6O=3I6UFCn|b{I z!>WmG?2PAcWhgp3*Zlq(5y7))8l#UmrY`dGqd=Gi93@^ zDRJ$)0n-3?xZz!$gsC=bZ1p}J*Qv)eUY$j{@8mUi=23c+v8~f6!L$RGu}{-bN}bX3 zJsLWm8XCyU#=55Mb!LCt>>t9JG`V>ckRvw@|L8^$?o7{RpW>#zskFG+1xVetQnY@6 zm2U}5ORR8^UYifXhQ+NJSj;zKz|Os0&ZmnUGUe=nYLz5&a1j#KrIUPL{`0VR`#NC% zI~@V!mFgeu4H?(ShkY=-v3#^JK^EpAT_ibrzwsmdw10(1Rx_v$nFhE>r)pcKyRJox zAN^qdY(XseCMy50yL+4^)&>WWwins_rC-Hxc7#l%3X0o4cs<1KU(mB7Py##g@!i7& z1DAh|LK=^3C#=FL)kKWqSxGT(zAIare7M-+b%QGsq$H*S&&_ z`-9cO&Y4}3%$>&%b_uO`PvkrFU4;?e*vAk%%f)8(4Rb~>7kJKhDwb+|dRfaszd9P+ z?B3|`sELl3&dG|Bz3D*XOD|hgy4mEDg+X3QYMn{>NDkKNXw?W?^(Hld~SZP|u=${W{ty)p&k723j)%!7M_2mKN+ z*?Z63yCZfDH0Gj$)<&K;g9nwkUVeSJO6=B%N#mrQD99D1k5_+ly8wT%BHwG~hcKk2 zrR`I0kBY+EZ60z%Up_B)^l}i|ez>z5$1D4Uh9R$@lLDJK%7ShGc8NR-jec$@CKgBw z7UE^Xyh;xgvAMJXt^+TR$$1rqgV$tyyB_@DvBb~ zI?+tI2Yqz+H>n;>!N`ue;-pf8Tqe6f3*!kATO>B&ghaLNeb;H#X=ULnHrCvG(YFvlCUuNB;nD)a;`59Pk_U)8K=9e=JU-REHDo~yB{y1ATRhQs7u}!dIy8K89_$x&!ycN2l0x1rGGaFQX z{?z-kKaL@b6zYg;b8D5cdSKQUm1qL%cu7iXT^ahsrr4Ss8TC%j-Z3}F{}atM_Ai=i+my|?)1Em}dpqqMHxj27yGg~sbV8l)Tcq8tZ5 zyI9QfqFWPXYRnTZGapQcSKe-`X;v)&d1vY6;oaDQQ$D|GM$O@8v5zGApNe%=0m$lnuV-)#fYsWlru0e)#|5-t*Q{S-U|ksHU@mIH z)c{pNHUPm;@9w1o^z_n}Tt|if_?%uNRN$)F3VjBEwDWuN@9fFHvU+6HO4Xd|>ak9& z$xS{JbcJysO9r84yNfa+VvAVarko3LBr)0GQ#8F`GVv2+m4q=Xm#j zGKCHriPU)ecYb1d>SHOgh)fd)4D{3H;+Ct-OvN|qDWnO?uM`48?sf)VU)E8jcqEQ7~`PQU+%m6VkFmMzTA8Cg>^jj1)a1lOE3FTRTIB`FN6T|_pK?-dO zAIaLa8gPe3%wr`Jw|jvWvs_%xY3n3rkZREzm)nPwPDUeEV0 zJ;!7;0KZrr*cmmSN06o?$$&;G;FJaX%S!=C<+E2vAMtB!un}9LWU{2l--3q3SC;eK ze_KI9M#i)4R0KGl9OfvzBSVE+7^V!$Nvh!_`s|_8XSn=Jug<2hqL!8nyf2qMH-VbP z(=y|GIbt>gYbBGupFeR~@X*8m0g(NL09#A4A!ivAKmnBE48&%#$lFoS-YB~5+VXm* zWqbbw${P3^+A3B(@Kxr-Kj?Ufj)3fMAb7H4f?k%1Oo5ts8xi>A?yQ|J2hfR3P5mq% zyCL-9V+Oz|?{}@ah=KRsL0?t#Y26R>m=;!&>{iW@HAK%b*ykam++0Y2{YnIV*|HqR zo6Q4l;BDuH+&Mz-J%sR8Fp!}pUE-lnQsd_ZWn%upLq6sY0-6xg%0WMW{ahT2;}>74aB>cQ*WrL)n1An0B$!g8u3$zw9uUJd z?^=+J=e%5fO=J3eb3k4+UY-Hn7HMkF4&_Fu^z~)7%&)Icxs2!pbN`j(MFN7f__|UV z0<8gkahhup54o#(4w{Amk)m0QnwB&pTa zX>}Aeh}yiuu#uLwPhW=j0E$}oko(<@5NnKGY9%czf(#2`76aO}Tqa2oxTYhpQ(Vew zRPb`a?xJ^wrz;3~1G!!m@TWmkBpeJ>!gX-xP~*1sJZ0os~MPXvFYBgdEs! zK*ZRBU`Ume9Gmc1{@uv`$J{~qzlMhs57@X@cGBr!*D(P8yy`i=aeoZhs%(wi6P1&3 zX^)PZtwkLaBnP}~gOcY3M#io}O#?fl6>&Ke646LqayF$72^>U7I-ZTM#W!C?ezR=X z=;G!S`y|+xnH0ZX*iont>`~V-K44c~_q=y8=g~#hsDY&_-~ct74ID3$SZrHsTjXJ3W@C)e*wdJu_xzH`Ofmb-J z9qRNTHu>*)nouFD@os0>(e}XZaEt9Q582tUzWEOgUg1imXe$SGefmVuJgsC~D;{YC z9B%`2cr;dV<7tMo0CRlt=zxb8W}r~ubB$sS#Kp~|;I zc&bKo$MysD?M%Ln&0M3Z7jiEN_$$81k5dk-$tUwR6UPsZ z;P4hD(_V&FSm6z2)nH4qn{fu+8K8rl5nxg7p0+sIBy6#i9%kxqyvMVzDD^r7A>U_i z=t-5huH5U8w#34j@ttul2A0sU-diM6?;J3H?b!s|u_xphNiazze35L5-4igo{V1^k~zoTC&AKfmJnPUXBX)vQor0Q$blAIq!wbTs-C?`_j zm#FA%4Mc}F=8>Wbk#N_`e%ei|M3aLeqff)SOf|bvYodel8pe4^jxp!@<*6200~>8x z^WC7L(KIa>3*)$eW0`3dhGCAJ|Lk6QLFMNkNR`k3IzDj*G4!t7x?&4N#-ktP3<%Lc ziQ)c%+i~_oE+xrJ$0{>KrbMcM?En3wv=6J##2uq{JFLHZKfbDwh?JC(9}Ir_-^Ijg z%bW-wh=nt0b&AMK{?nWS123`nS0R8PhG-SoHUHLHBMek3Z4ITacVu4t$@;03N}aD= z9;3Vxuy|P5LdE||EbqPefjjHt1hJ%o@+?b~nSxG<0iy+KI&bDz?3xwEv1~B*16LkD zQ;0^EVr<~#ZQ(g@;=EGTaJxF!p&s?SAWi~T?IZ)ALzBMDp=SNKBEjH8J@%K0e?qf4 zb??4&yV6n#OANRtbwo58E^mKO-79PLsycYx;HJWSN+Rzj6KmkJuE`r&6W|(FAnH*p zAr&q$qDyz0{MzMa^@-dRg=h?xym|rb&pUyOW8#k7I&*oG;}Rs2Qva5BH7}@Tc3)Jsc*82hl>PQrz9sKAE{!Js!J*?*nR?FCI^yv5 zqFQvzBKeXq+wz17Y04P+qS3ryRHwu%aJP$hn;>xl)zdNV#NALkSruJ*>C*J&|M7|J z2%kv#$Op0NZp<;YZr~|{=2w((Sk(nzBy{9Cg&%it4*udNG+ymqrlWrQ=@M4JW9H|^K-5B>yK+}v$+3w_4qH(^B=b6 zr?Cc5d-TW3qI-Ex9#cUeyP5Kn@u%L(O~wAOGBwweJdF(dbSWSaT|s49KQ>`Ida@CV zZQQ2weK?^#QtbpN&@)9@)CiZvDBPf%4eX3pJHHxTYKyzf(+J>`|0BJNtw48pBxD?E zu>P3C9$}y7`tIH>hwA$t&7MbENsbSN()cSPEpGOS3OYHKIoP&kW&GL9+W9O%u}!62 zcE;_82nBE34(vbbsYsgm=3sq%^FjlyNiyS_wATZg-HE#N$76ca7v;6jMl^#g1`Ly- z9n68wPK#}c{g{5z`qIE8U;$nHQeQBhqD8b%701i`myL3zaXDn zaBi}QHz>$^Uj&#Cc%n!h;n2DX#>iyc(Zy!DuH}v63ihfz4S%{&qFI);(ZSXMR9JtM z2D;u%J`n2yAkSrur3WAx{VzQD-*-34Vpcbk{G8AskXFAMS&rMW@%#6|4d1ryo}~ng z3ZJeh2T;Sb_dhbZx;n-Tppp`75VW{Q9#prriRs9dn|&U5@cfDW;JR9HpxH|xJ3Nwb zyrF}oqCS;oV{6t5aU>)@N||bc{gI8ouB!MRq?1P9?^z!6bX}d6L*Y{O=e=aFfx393 zszaDYJ@-K3LL503#rp9_yOKtQ(q*j@-3VbL`4wH8E0?5C;5}p2y~8B3`MtuekRIk3 z6*MFt$HBdKPpuD4v~rN#Bh?ttF~hXLwEsG@MpR>maP^KnZ8xCu4*)faVyo}ewe^^_ zM>FeYwabJY09?y)cu}W_$iadL$2>20+wKeo=QPU@iS?`|>t#q6bh^x9rQJI&J)H$Z z^SG%q{9(5^JLzXQNjl*!C+Dn3a3y7{4AK8_MjQ%g9tR5+0Nt07n$sIi=`^BkZr(hk zEvVpd<2&cm8~KkY>!6bAlHw`1mrEo z_rz_Svn(CnfEw|9Fa45_(~lCv_ACX9xDT%#mz`|LG?Uz{&6nT5e72slHCI{<8(^St zbm}#*y|LiLN^d&K(|({d(=YgzszO% zM*{f4h@2HrbrA`Ng^m)|FV#D%n>7y^m8pE|*noL{K7E!YWr919 z5ChQ?j%us1mOb(Ysz)sqN#Gw!X|aZOzI=9baJ1 zYU-am7Xv1X6i@HYMh78EV)xrC$%neo{yt z@@CFAOc}goTJ2Q-Zm8YdzTK*GdPvqc_|;AJb_9L4i8jC?>PKmgplY{c(We5L2@2g2 z5-5{v2(5AU#6>HHeE0g|4Y}{2KY=J)-o25B7G!+F!TMnN>i%~P9tR?kAz>gTG6PylP|!0O!!|A3X~X27PMy0m3W)<_h3atW z&@ZhO+X(S>Odt8)FfRlyDCnJ%8w@w4SA;qw+OOiF-2Av9fm}%<$iq~ZVxTr8BXHJp zZI8)wrW6kPC#8FqIcJo2ryL}Bo6Y9z?7X$q^ZheURfFvX1RdF5-O=B>MI*}@JsRAq zH|717h0w%{BiX+yXx3uZfN4=Ld;*|fAyKUw9zpZS z$Rsa9f5KEGRjuXiH4&xKff{1dT4z8q-4m%ovrg~7{=UYsPoBIyr7mp5pjDGK!Wo$tbs(#FLEBnl@Z+P4fR+~G2ZUl$cM}mrwa(YEkduonSHueSrogVxOlMRa)kxx zB5M25D|q;BhG7%9N=ViFsN?dchOYGe|0WiHzX4&TziD%<$ghBF3GFP&tApVPtZPa4 z>dI+H6e|e21>c)~{1l)m8uk>VxxMQbzY+-(Gp^o!HJI~NOLI?Z11g!EBAyIQ#@a?s zoZF=S3M1K!@35kHr*?C14b+s_&C3#f)tbxq`1bOl#o)SRNLBhP_Q!A!RJg2JA&=%R zv(mu)>VqLPGxZ21j}Y+qk!qLSA9e#gmVri~P5+6tjL+J$PC%$0Eci3rV|pspN*i6i z_#ofnq7|t~!mHI$fmQ8VZgc6hY!^NF*>FH+w(9YFX+Z$UA+)?I`EK>9#oMW5@S3=x zV2aV)Q?HU$ZvB$$PlFl$GnG$B2=8Wf%Aoi08LOMe4y6^qb-5@XB9rgqK8s*amDoij zQlP@=3_LTh53!Wxpk~AW;y^e^QIKt+GIEl zRGH_Wo`A==>@DXo0u#tVMQ zRP9nyyvu>04)KsPGtM&KK>C}HKkgOI3h%u#%~YJK+lc_(JXr^r#kZ?}VE8yZrDjDT za6}Wz<%Xd9dnqeaav_iMjwm3<|2LNT?{4RC2cf%*$YdLP?Nv1GD+2BK79;EtWZb<* zeZiNQ8U8R$0r)C>xl;TCGtS!AY7Q*x%P7zCvH72Ffw7uyATte6N6Qb(s)R0PcVTh& zI*+4wHbRSKzM1qV^Mr|>`8>(bxPL`7wcvj(k1Hyz006EfyoBe{YFt%k&H0i&kZ2P9f>30PH8W^9LU z74dgXQECElg*A)5d!_hK>2S^)>=03f{|=>GSZ{fknK2#|NM+<_+JDIUf2BzdT9n(%BmV7Y%q%R5 zxA{}JIeRk|@dB<}k7YsD_1h?PN9{BW8og4Z1m(dBZaJ|}D$g4Kgaeg!mNFne@h)u? zBv^#l%|c*j(r;aQFq!ql)U}1&D$x40pZ>y^Do|_{t^J4{(i#0(7!d_NFyRY3W(?hq z!g<~WCZC-xKR(BMoH;HJpPLN|MC2!3nqtZ%m7t})qp+xfPk>}czvLn>?Nl=|hb(pR`yr(vfR#iEs%w#qI(()hpub{i zwgiNP7NeyEc*KG3yi+=M1IA(7(s8+MtQTbrK3Sgk7=9f-JMnLB4n>_9Z$dw7x+5cV@F_Rp$^XqG%S8kxp7JL5m)Me7Z z(z3`8R%V>ISJ~#6lV1D~K#Zxq?JH$zA?yAjcPcTdb-Hp?5gOrpb7veQh*34F_{4pj zJ9fv8NNaqSrJYkfcUly9=rZ7;?c4+|D<3(Fx4$CQ22pB&fprU#sk@0fW)RTODJF&5z)3w zFRO}d`V3C+#$!K4pvAc45OtQ$z#kpfe-HE>Ud@V;;UW(9f50v`#+*@H`^84ZZ{NNR z-DbSliRow8(gF@!6^_9Y1OoIggIyD58iK^!9RoPRM`TK@=xV+_k&D2>sWUo_2wcV7 zdF)%s{Xr6()7qVdC2t%)Nyb0S`XSSRWoj+Vb$g&W-6?6VKmNXV^#yvK)#kXx{#*?~ zdFLL=w7V`Bl21=;E*|Dp6#z6JeC^4|P>kph^b&d0mv7i-W)SPXC^n%Sb@-l`*xvYN z@BJ}7a^QWS43P*GhX!(BrRY8^+V{S-j)0iNDqtzvu%i`#@c$2CUmZ}@7W4~;Qc}7@ zLOLX+k(TaG0TGap?oQVteIIeznR&k z1kJ7NQo@Dn|Fu$*At5gyO%D(nz3Y1sb=>o?jEDhg9 z&g`&;Kj>)dC6{89n|bOiuS*joq$h9nYsm#HoInVH%{ou_iAhr1McoK^h^->w?98}L zP!9sOeKqy2t`9)5Nzbk#V5N7_$jkOlnO}B3uxMI#rdeI(^{m1C`WpsfuCYy6TZq(S zp9SZg&1DEKZ1uwUtSOqhWWUfSn-qgkbXH24@hQ0@gNm>^0=IibHm?ecZ)YE?=FVSg z9697Bw@x1Wx>;_*%e6G-a?HQ=z7?z)t*>@g7-~MpMq7JMz>j=|*2P30<%7%?Abd|CgS4LrEXD&`vf&w@y(olNGW-*WDaG8D>7tunOlnn!nn+)$8qa(dUSAkc3j&BSS51~alt_2ScAj!5=Z(fN z$7v&gq_tlys^8&4KjV1QhjWL(a)cj?iAf-be5URxgRvN1*k_{&l#KA)ebkR`91+nyueH2R)@6KJOCZZCj~4B{gZfJj9lAg}ZDVV0|9Mx}v_GlXvT5^GzEhG|9_-&VRP4?S6)i zF=agPbb3Fk%{Nm{`u1HE1v+J~_x@6049}&f=gs#bkD>aT5)?$FW3S{<5nZ$wwrin} zp&MM!%hv51;su?*)!BtdMXgA-r569KLCye+hGq!BmLw|Hz(PaEf}8K+xRzag;7>AO zXeh2?gf&CRi^?4QKENBx_0o!t4Nr~l@rgQ1X@^O%N-d#@$CXXA}VHCHg z`gh195g6p=qY=q8C@4FUMMsb?Jx%i8Xipp^ue>~(Ll?J&CO7-~xVX4yXN=FE59@S+ zK-biIFho5y@2}PA?f&?Cj?y6jwd40n`S;2l^T7ZvaK^4s)W~jZbFx5vbca{vj%m53 z2H~N33Yh(pd7MUg05q`$2$GDHRN#MJgI}UeHM&$kzR3p;1bwh#i<)!Y0wB&%6j0v6 z-e;KxW#GbK$;*6%@at1AceJ7DTcF-9SAjGf#2aJd2$&KfLKRX^f&Ud9#K26aS%pac z7YhQeM***;t`I;Ewzdk6N;o>!oJ%(WkS)k}QF-_G3LJA+uk4`BRB!D!U8Q*ERgYZ4nz>OF+06*Ow6u`FM z;*9~)fV^+h($fJ4?!zxaLmc4#J~rSGn7ZB@W26K+&uHjDK*J<~e?fA9EEa5%dACz- zPM4E{s}0p=pzthBL_wQJ2E;X;j+W!cJ-{6C)Xo6IH-+4Xy7)5PDn2P3xW3MHGs7^ zkI-;B@A5ONUNVLuF3G^aKzeZ@P2{t)F0RY{m4Zu90R+Vqfhp{-d@80nXy@?jL1q9> zL(#$l03ekFWH0lHcN-3aYhQfHhH<|>p4=7?;5A~pyI0;H5E8l$n~H&fy}6M8YEQ_; zz|BV+ux&nk_%OA32h+RvWiB+JKoS||#Bc6igah&R0NWe!?;~gXd z2Bf8T@4BCI$8kFp0mepR;@m9F^j$dM{#(8DpAJn;y#@Vp5QL~M$Yn#_b_Nrg{SNK^ z`R1AgpiPQw9Z`P&o)~jY`h?%i&k(YL+!4@|^YFsL!rg_{^QIt38ylOIc7Js1vpxO8 zn*C0EX%aolO+%N5;{t$D^+m83)U&hVw6s=4#RY+CAS!Gll*AvBQ2OWi-U$-~Kn34s zjrslJNpL8_(X-2xarH6?4KyHEO+Xh0h|Dl9oON<614Utel8QPx)dFT}TQZiKF?_~- zirxL5DG}x_CCZ%z8v|nsk!w$OK|t&w3tW&2KKSyRkfs~HpcNp#`O!V~I8hOULf3MM1CN(cl0zg(i0}Q(I%OcCj(b4N?6cp2c zlhzrKqnGk! z5&uP3$YSg}G1YUSs;X*yk!Kk=s(za#o3eB4mhE%@ge_hJc8~Mz^EBVzpB||#YLwlo z2Zh26g)ag}>_UiyfGi|AHZ>TAAT14^|<<`>(sR1{{+- zn^0wLu^8y+SlvcBTGzq+ZUzJo*?x`j&hV5BcmBv~8L zwE3A>wOA(#DEvH)44ug1WbMH&rv3Mjh;ahy>T&1WEqGoA))#IU}BWJ zQH8>|{u}kC8#qBe|Bku<%2zFpb~^kSmm7ji#6txjE~B$k+%VjP9s4R^PoJUtR9Cp=@990qC1p{5e5E!GF?&{}2W0?jk7Aqi`3` z0XV{kYA6L*t;K(<)zIq*z+?tgzfFtb?tG(yk`j3WyE!v1$W9cTk&)rG13G@VeSi|6 z^~%b6cF1kNI*@eY&SE^k>g55z&@2BoKoKhdeGf%1b^Uubh$UbF!`t#YpnAgZCBOCh zQwj$Q14A5ur8zAxFL%#Wn!(i}jD`HA_yj_r?=%(qm8!hFew8ec>^^#ZS6GI3CMD?J zE(4o$1H=bQ_}Oy*(e_WD;P`EMOaRfW-wM}sUEB67tB%$c*}8pQGX9&n`&I6aVw&GkI zRJVFGXFqGMpsULWASpxde#S>zV!y3%7mB;{1_AY*GXKrhW(J7@x!PM(YcKxZ+P_9= z1k9Nd4vlxe0nwfz2zP$}E-``AyD@zQ6coY%WPt8-FVEWAS_L&VDworp%4`^&($_Fb zf4%ANKhGM0BkAtphf8vA3OBS&K#P>&5bb(S`MwPM zYwv->rqB4~#TE?2gMXx8cMl9i-$4NH#C7Mvei!lAyP~L0`yJSs4GnXvU0o1=lkvd| z0LBytjGSO4wFe@ekCN$i`=jX+eIM6N&4ut+5zpdoR##SfWZe(Y&u15Ie%dU3Y5RN4 zf6>yG5)l6mf(;ME3m-))kRARc6&ZHyr58Ot{Xr+bWw${H?=zrwe3NX@$ASN?3gf*O z4@Cj{s=dBPbZ=W9ofUW{Bdf_G6lB9qWCEoftKLnM0`*;^W%fgw+(#>9J1Dcc?W=9t(Ut|57io-D*@>#E+;3as{C*wBtx0t z@gE>gO%1cE$H6j@NB#yBX;o4I{Dl#VIr7Xj!IIClK?i_Dxwj%uFx%t3{KNROsRzcr z>q`=Pdg-0oRifOYqG(zEYb88Pr(NW|m)g-g5X|UDq4tYt(OT z@<%aBg}gm=_b8&e?7FoezRJdnZL$qh+Br+WNa$2DOuI+{tlX3%K4sPEeTU^FMI$tB z{V>BpycI>a?DG3FS;Pl4`bFpaw3z#!{x#iH=mB#V1(@5U%~n$RtU&xJpz_FBhmDB2 zo45w~>3q4ADFVEa(oZQ)_gn#*!Khb#QzFdKE}=7tpT3PKJ1w0Si#Nf8g1V(Bfg28? z2P|-5{C6<$BH*BS@nWFjciw@g>Z7}SX$2h}2B5xQC~yyU5lLBt{_pGm{cy;@OBSWO zoO__soQOw-?{=KAr$D|%LB~~V{$jV#+Cr(S&mN)9EZoL-AxwxEQzGbtgrTsvR7$n@ zn?S;0l@N_O=NmtQMm6fN$D>9k%WBkemjvf)X=AJb5wdv*wXfH_f74VM5Ua z1-nk9gFR8`j{^zZ=ZB)_TUxZ6S`ipmOY}YbsthAjBxn(&htck$S>!dkG^RAF+pEpR zC&h|U+!rriCW*xZ9Xob>SH0V0j%i7yvHJ07VO$nMp=cl!1W75}CYaUPzis$b8b@gH z*hvKwFoquV;>kD4@0c)qyswh-E3c9tYj>oTqHfyVJP0&>g; z`E_O?X@A~{C7kNxP>TBpcMlHoRM+l?iL~=yhE9Sf;ItMVNaCg>6Ac73lp@9aUqT!B z(-MM(e0CP^|fh`(+D`9xBw#a8i+(d=&gN7;~WmJrKmZ`0H9-7Qxk(r3kC#8(R` zcCg3ey(zb5^EoGSs_Nm4W#^+*W7PJ^$u0wZlyxvsdXREkln7{esyTT z=aGGr2O%JaqgmLHd*KLfC(Ls;9$-J7AaWC8zqRZ-Q4A={d^5gxPjEQB7*GNWVIg>9XI!ubF}Q%55N& zQB$)`wX*9WW=}s)bz9p~Yx$Ma>0lwn_RwZ5hH-amie~h7-TnMRLJX{ZY9JZe%)9T| z@P6JvFRvBK<>nbG3qK>utt23*BQ^{UwstwV`S|56C4}wbz8b$;?*0yCZRk|RnrD)s zon(8v5PJ@DzTQk%?yG&$qGCtq;6(5hs%*2kxVx;_UAKveRvww>)Zm=oSNTas`B<`t z7<>Lw@z-~Di5vstVwF-=eEWy?im!;}+#HQut6XpEQ?SwP*GEqMdODc1Pdo{z?1MQO z6!@ww0ve9nQMj$9C@gbSe!3lJA4I8}f6uQ&c>tH&BZk~eX1T>hC3|>{f2isCF><2F z!~(Y8Q!q4*Bj1#(*3n1yyum#BiF;*iuHdp^d9aoetj0*eJfm^+=%!qSgD?jBCeM2* z-gG6ca^R4YZYr#1P{(pbpDKdmW(eq0_T0XzcWX6FnZcrt4lmui!dqy&k%DUswc)20 z`n%KNJk`eUWo}-Qdnxlqy+^jk#ceHYInvF>Q8q5@UvJL^V7A7IBe1o6ZoPfJ%_Or} z22@Jcud>%at6tFxsRx25T%rtG^hfz>%(8(g?m)CuB)tlaI0J!lFAY)4E)l zU!6(UoSjkb7v?g>+`pT&S#xlrX-E^Pr5dCI4Z_OcEX=Js8*2}Dvxh>kl5UhY%B=To zfkWXc&N1ik3fN}b=e7W7D7G6;SvZ(2xnL%HeY^Dy$rgPzj^m1d07uMzr3S6T5Yx2w zbNCF#G#hmLYYpEZ6O!2%mW%#E^{kuk^fjldGqjC4UYWeegr0(O!yh18_u;?3HCHSiO3%qwR+q}sneTVyz+A&<}z-E}Td zt2uZcDZ2w6=ui00n@zlH8Y56k$1!1{6(Us6uDAL{ExGxJgzM@TT=5tax2PRqxi9i& z#!n#A8N*+d)WO$U51sgS1%eAln$>}p=iN%Jo$AcW#U^e;`S9>>7Cz3JuQS8E<09>v z7k@#;(DS+mB=ZVo_{1KGeP`ry!|hs{?qbY*MyX-{jYQKyq+w7wDZ$Z~oo#kHIvm3% z>MD?VO~;SmhrJ8&(bVA*;x*)+iwuI+)UTe*5NJ~+t$Jx!#uRwY-k3=CQGJMxkgv(g zaQ+a*!(Lf@!ueTlcplN$gloxdkX%dlTow|Ie9|~h&U<4cQ!dz|t5XeDq%aUinYWan z$cn7#w$I{m%2-UO^S05x;N1X8n1a){@JxV}x?Tw59ZAoOjK>1Ar;%jh9)Wc|ceuQWym{XS^^d{p|lsTf4g+ z6F_@LoNu6?zIZWx!=BWi2=^qbnxjd=1Id}37`bZABTCp6Zqc%9O!#n6SWZZ1T zo<8c$&QYw;c)!p{5uRJ9&i&-^83of1&JO~qX=7QJD6JN|#!1ZqvqP63QTz+(v77!ET>1HUE)uz2o z$h697@nykoy_P69+#U_7Z*EZ9Z7#%s;jABin+&gUv3|PwGR4$H@c|q(`qc*VpT=MiFrt|P#UY64)`0{V4+!}Wnf~cJ4p1yz z04$!y;a5r}4!tIFLjtMZ`C7;V`Na+=Fz?1ylj^Y^#n<3h z3h6|9D_tnMIYPPHyjy%kC^F^Xl`X=(=(X0=N$UVPQ}`RjK^E>sSBRj?8=65vs698T^~&zg9QV%9qq?K|ykayhdf1=Mh-7U?3-8w?8P;f0;_e9gllf>$ zK4(9IF1>aEdaYw*aKvtG)tC!5Xk|ffbWBowneX}$hm6@H_*TS#?yEt=hw}?xS=RLL z!dK!y?Bq^!Ye{Qa;!#+i(coD12aMDXWS~j3xKRyqe{jo2r-ut+)l0aVGFEzh9K-u+ zrS|22`*tU{0vWtALa@`#-Vz}FfA&s{giyt9W9-m5G-9`~4O~&??u)(MWJpl%PC5%z zpF-1D5KtBVCgH9%Vrk#fROL^vg<@X=3~NX-)~v1C(|e`WH0^QuoU&; zm&=6KLfj5mwl&6GkMD%TD7irh~6~{9Rr?H*yM`eBI>-l{_QgAW)f7RV3@A{}aVL551oA?V$TBv!p56bLib(-lutnuVI@XIrDRrQv z&+7Ex7t}R>633ssaYANj1n9*te@1*+XVd)h=XE#=kT9dWeO*m@0%mN^uQ{rk6+JUq zq*GXy-HdX`2u}!LY6oULh1?6@oqaI5m_8JHKi7?ut*20QaWjgsWuMeJrr#%-51WTc zstKZ)+im{07C-}Cm8)a#j<;k2Glxi<#!zAtzjG@B&?k#dWM8R%gx-`XKG}sSUeOPe z7CVb3Yx}cKq`$)*^2664&4j;!n5PXb_Gcmp0)On7Mx{r(Wb|&b(-7QYDAjGc7Xl@bc2A zJfJ1yNJuW2-pK(6Fmf%yDK^BnJ(Z@+W6NAfq$2JY?dZj{dUiry++&ApwH`Jk5vMc7 zsff5t>6(T*cw2*2_FMTw23(8}VXbjq)Abylar1he* zw{%29T0N3+iY_*`574)Bo5KCCkrb0FwDoFq+rBl#1tnstQzO=M*yD9913$L)=x08mK=*y^Do?UY zW4p@M_Em39boJ&id+n3V4w*u-_4O<vny*|e|yDo|b^#xaDLc9Kbfoa`*iUuJo zVxn0B!4B+zRNIPl`ZI?34Bb)HqOLSeC`VH}BL%nVn}B+ z&L;HD8+t#JXvMcL(%^{lI#|qW4>b9x;+fAsUcn88yWj_3FT^ZH2mzJ2rR}~8BDhk% zdutiJ70@}>tE#cI(o0K4Pb<*IaIFD*YT5EL*t*YQ;ZyL*=@Z0m<2U|JT2-ZZAH%h+ z4z?KchemRYS0w4KFP0sjWR;yBC)5RoG$i=b{7&b|uJD@;72nCc!j19M=QeK$uL&`;Rzrvklrky#^h8$*RXP!*8HUS53Mz9)_@Gio zCK!}$q5h{fCZBK4ML`gSDY1rJPrPnJ`CwSzh!}b>M7ws7om#)sW%YC8HkDeTcXaQ~^OECD-jyS4s2E5fswzK>*G@MghKnJnXo1X`aINaz%G$0v4$gxm;c`lg?M zDnpMk{|Y)KsGcLUPI)mGyRwy_eo&@d@`qZf;8@fW8O;yGuG#Pu$dU`sX|Y~>V~8_V z(bdu|=nS~zEjf|#4P9zY6Qejhs&IT2n`N&YYW_;TS|fAiO?$|i;$|nFL`dU_Qy7@e zgxgvJw6}piy?243=#Xe8oTQ6_sL8NovqBzICPObYe!Vj#!?p-yVP=I#osR9_bcEy= z@4%_1JHNu+Y9w5aH4=o(@=ze4v#)uU@j|mSZF$~EjgN(h#-ylrx31$m>s4jCx{Q)a z@duU@;oigU^u41xdn_^P=)wZOq-=0<;pvjKLHVE)bW}NEPm)}sT7@<4oce`DJ-w!` z{Bj*0#davP6Ls_lvt6G!>!v2P(zRLZ-9B1zS3Y+5{88>fL5oCrZ@HNMNhsnV(z0+| zvpKFpw1hlb`OsGF9&0@nc{;0IW;fYt&P6|m!LiWrrS{g9ko2)PGd;@x3BEf8s5kaF z@G^of-9m}r2@Dm}7WCL{{`&fQm?fVmDk5yj= zFu9{Ijs<9L9yrV;6%3r$nj(R1&nQ_Dqz5OLC z68!M+-~Fo7qd0B@(vH;-<~P>_Exg1o(4$Oh=i5sc)n9#=`yhfa*Xffy^q3JZLG~-T zbPSWuMY!q@FXI5tdT*Uc^_~y0>!BtQgz}E36J$g4ANSYorDzjETK(Np#&X;&xVFT^ zlW&7!d;^_eSQe?p-AgddEirvhGXxlTIv8>kStmJ&MhB>AbfHh%5vI| zX{r^KycTOIsBdSvF_h+3cJJY}%Rp^%m^GAc>7RJFJcE9!W$m%FdjBr1*AB0RNH#{j z+cDZH^r{*hK7QJ0=lU+}P~Ryer%An0q{(%}qt@}<>=dtKC!1JEXv@uMy^XeXC{xU^ zha8;aVL^71G+{Fv#Z!hQDgDylG-Z5{BM$5FRz#rn3w#^b$kOFGn(%!{{Oz zWN78R82R(NwJxWm-ZQ&={+&~+!h^liIfG{#V*6Z?S{|*M(lJ2hkItWX02P@#IOq+Ran+=z&A`Jq`q6EWhho%-qIUxhUyhQN zBBUV3*NmD-)UIlAdetoTWQLi-FU=2oUaufHQEDz52I5o6W!!H1_X@r$we&rrcLL@* zxI`Wmf+-SxO4xEJVk$T<6@LmBOI@!xIkM4wUYcgjm@ib=_18HjdJ7()!;a=H1$``) z!lHNScv)Uaa#jzjW$52XCpE{t&GnzE{EkUpBn~|v;~DNb`K2x;lTR(~ znT&w~UWl|?K8S(k;`R;r<+e+0XZUvK!b@ivLf$`)l7(j$gl1uuNVck$2PsWcB2Hb8 zP_-tWO1Bt&MH~u}L_IDKjE8Yw6q5@Gq`#ql>$w5zK-ETi+EN*fCp8LRQ1x<7b%bNn zXh%^5^b}5h6r6-7%nhXqPJgHq#WB{d8;inTz@EfCv|a=uSLs5qK4i1w^13~O?3qfX z&qisp(qMVii1JtSPUM21Qq}6N3)3iF& z)?v@#Q2QfokA#z%nYpyaykuiIawG4O^fkq69+GhPP8UMX;aH$g$Y#=y z5hAdY?i&6THW2n6bumBZ=W`oXidhDW!{2<4Ic<{j@nmOz?0Hq))Z(#ky@t40+g~Xv zr`yWzEvmSea)jnlRDrh=c(_DM{B^8MV#{fm;EFQtS>PsF$mZPWh1*c<+eugHX*A@YVwEpYW&TzLKl9#&k`g1-_-xw*pZw>3{2DiL*3`)}8 ziCSDKHQghjoSq-_i2i4otK5{w_U^k0XFm->zL#3MqszKm;=7jiImOC-s#oY=;u3B& zWO=ahfO){g3?midAo>$x%Iu+T^MiRe`kXAAogK;Q$^4RjjbT7u^fO+?gc*FcGX|%! znPooF;6_MYo=>vx^~d>(Jr$dh$9{X@pjI1-oBc(`9lnTXd5>p@!+)RZp@Qef^<0R2 zDO*@O?g1Yg@j1Ooc&9oI4ZE)XI_p!R+C{rY%}R^ro;V?%$LiRGGz_ekvJa}{xJlhR zG=YhAp>L>c`5)`3s61?;uzvP*<)h&3kHPW^FuLwZS1{PONUP&Q1J=o`~L843`Q<$(;jSR8&GrXwb$Efg|XR87q@TN`_s(@FO0jn z#9>>tihfi#SF&1)Hd>8uhOGokb+I(lXzfJ>VS+&%sNE!KZc}LBg`4~mNP=R`+aH#azP?U7;_obO{{0vEDm{*Bp=%)%&lO2Bs|s%< zz6z825``vFczKBa3|lS|K^|XBaHs6u(ew*|qYa|r+mLWpfKl~8A}2tTq&OR-@k*PB zJ567Xdc$hwm8uUt@Zr0BHZ}`eR{O!Xg+y^~e+F}!vJB++KKEsj5LxK=_K}TaHR~p% z-K0$cRflRUh}fj0&$9$4%OOZYqv>W-0WgQR7Pwhvx}Z9k>g?h%JL7c=NPF1 zWdbAVIm<$G_pVZRZoE(p@N*X~Iy8u!&T0|&Z80;Z08gwy3hqcy+>OIx2wngu-L|U$ z=;{d3JXgb}mLir33j&90z3uy&Nu$~TYv1jhFB-&}@+xWP_w=~Xk{538G1BCa%4lm) zR-AlG7_nYt6Co0rfZ5MW2Xopt7+sDIpl+(tY>QZGg-7;U-|`K`x;8A(!%Uktzm~MG z;?gh5!_n+Jw*T~=x=B}u()|Eyv4Y4DH`q2VcrAZ|`)dVVfE)5reVcaxV@zB9I4hEs zF)g0`y9i=9nv|SR#;=EuD#Bpo|3if@t)JnxIQ%P zYyq4Lg)b|{K6S=iU+AB2$8RuA)C&G#X8|w#r>V=!tOYe#0Dj!}d`x9yYSp ztANJ|krmy9;AFVZ^m2<_kCvsnY~$ipHOa|zzv&dCcD|uMLHyAoOIjh_V-05OfXTkf zDK6qU?s}XpRh|DaVg<7WlUQ(8f9zHe#k>5)O5!ZSYemD4a?PhX&p$?&*HGDiGMsa< zMOUGx_Gybem*K?;H?ecLAlr|Jb7vcTJzJmgdi=8Jgqt096@!w%>70ooWz$V1#k|n} zZ7Ynrea?5U&et1lnSLf@IwU4}-_8cSbfc&dN=^D{A~8>q0})=YqL>Alaaqx4rDOAf zVMeo>oP&y5NIF%k-sL_w(g5z#^|}_GVKLU;aHp8Fmq)?e_Z)Hm+ap*UYb_yJpFHG~ z+e1O|NgNCsB%s7Qt&ng*g${-bvggf&lD*BxlBzcRx?5r|!%cvAi`r_SK%nyKgDmThbK=P~ zw&+48jCV0~UX4Brug4Mh!js)?e!OMH$D~yS$4x~v*bg`0emZr0v=e7aqJiTpJ^GA0 zskY<=`P(P`7dYHdj}qbLG*XSIarU*UgK^V2bPt9aa}!~1e?>gN-hCOPQLbVBnMhhf{H zkagNggJF!%SxQ#f(3r`4aqVt~rxqc|=9-)R z$MmU$KIim@pJx?4)?>-Ekfv2RW024lBTvV88gVmqsf2UZrO$%Oup%*#nBP99C1&Vx z&8bH3l)OElyGp586m;MtI=4o(cip8vqhp91uqN{mWXDK|w<}d{W(WXfrYttwQlLWM zaw_=HM_bp_K}|VOp+3SR-aD>?-M{EBF3f^x&#^{0p!jU4s>(5Q9W;gAC)T6Iv2`8T zqlGg0ASC7W68;Ff#OauQEd_cjXQl>kC-7zR*VljBFILjfMLQ;wY$owh|6EZ1IR|7K zB-xM3pKS@Z)wWX=I1IF=9u!PVl-k-unIdnXSBU!b=eRX-%7+!$T|E!e7~r$xD%k6= zhV6aX@z{TRB-%CRLm%O!(9bc#32#zG;?A$hR3Klx3gT_(r5Mm@g_Z%WiAcOmZ(@wC z+_&zI@M6cku|u6}@n7Dca0&I+tkm`lGWjzUR`PAHbOOxq6}f|L{qtUCo+>tbtqt?J z2m&SJl*=bPJ_C9B9T|@m%;_AN(2m;jRVZJ2p-TB?*)Vv1;yoa%@BeJ=TfdN-sBMG> zb|S$7Q&UzF%~9(6T+{p1!iF`)K`AQ5)Q~9>>4xK5csCY@PT6h;_Nh<4d}9XUNqf=R(>Sg?h*1OFn~EfL28=lGc{&FS zycUySYMCC57`1>gl9H)#3mdRLAI@yCj7z=M`4UwS3~J|B{NnIynWPLg-7O|N?k4mJ zlc*^rZGZ2_&e4KPo&{9u$bB>tgj#E{;fald;j1r?tFiD5qmD49YhMa+43~K|6ACe~ z;ro@Gip7aKR@MJwM``2wMQMwUPl9w?KZb|ht9Vn+E}BuYY#rut1aie^mbdSPHb9;8 zB=*}T&#P+gt13u%S7EO}*FJjtR+`;O!VZV0DY{kXeyKc3_k{$ovr!0lUL#d^yumxY zZ)F79xE((_IwHXfBE=aiFT1O#lWz#*@~f)gO8zT&m^PqESKIvZM)&Q-^n0pKxR1A# zQLbJ?fA`BsTMKJ&$E^m(8wIe}Z%Ck1igLI4*?x_zHLXMV44oIpqN8=B|80qF$cAvH zbn7x-o=Tm$RY55%l>I9NN_SckYHqb;8EUYvHc^gSqzH=&t$6hju7~giG0=#XXr6Q% zQHL_X>Jo;;{A#6m)~*TGE2Xfl3aYNmMhBB9Zgr&VsSl^}EnV{LLf*dm8zueg81$Es zGcq^#%^NTd>Z1#{7wFMaZr$x;xE5+lCZsc(-+AAw@I^pIWH~P*ym}u>r4i!vp7lw) z?}jnXc|!#Ew7<8v;FLj%%Z$v~(1{qeM}+rP?njLz`P|)5jRi+;z*+PmLn>mBG36`6 z%hHv$!&-$G&Td!%Rd7R7)v8EUvFE=>*xg6@6?$gN?NS} z-?c(7acy6$`Wj3r)6HXcI5U#fg zyQfI6o9jfp#@TaTrBLiyTNyOfxpzl)9*{h5dW!bmh%1NC6$3T2Q-s|%~tLL6Tm$s)QULJ1P zw0RSSAMyF#O`GP~d~+@wz*2Ox+$trtW!O(9cq?k)Mu??SEaF6GRKIJSAa5gZ`@?ae zYK#}2CfmY0TXjfpqijy{o;`Nw^?di33Bu}~wqJ_6=wv#NY2H3=fm{mw+`rBq5|qS!LZ_?4o%Y7+B-C75 z_ZoG4PZJM|sZ?t(Z!mK%uFUstE>9^&^~8zA$unTcXpBLnslq*yFDlW-QCyZCsjJ*< zSU?)D;G*3tLA65Mu2rIc?RWblV87e7XzU{&2ojtykU9gMZj%%ONNTWeyxcIb+Y9kj zMpN!$#Wa!unauHoj=}a-mFD9nslg_gYH4e5E4cZ)JLEL~ABOV!PB*mPp}4>Uf7)$h*X;9ME-$9@0a3aJkpKl{UnHFYXJ_z?Pu zb6Q>KVtzunPfRKSFFwFL+7fCd{A-hoGCg4iyj#uGpKjubleEE$C0`*bed>B(xO#ns z$55gBc(}-3fk#2Mx4n>i9ZBazAZd)uK$&tJA(IZ{_t|Kx%_GNOowX6eAIOB@E-fPb2{;GCl(eL>SL7$dNO}veYjrRqoE;*y> z{~iKj!=bR|uL=FZZT!1Mfu@lDZS{tDsi+5&=$@y-?Y87q&hj#$h_lIzDm^Gq99<|) z?VY~FG`NS z53wcpR{iTyuK=D3zRYM#-~38{Ypw)odqoENWLt>oqrc^(#ux!^NcdpCx0y8q^Jt+Q zJaltlqlKa zRxZi!daVv~{CavLt13g&VnTJ5Sw(}e7Q5~8yJ?2k@qwFrj1 zx;bs0M5Mz)0LuLOgolSeH*59!*LwIBtm;Fs{e&9sS+|dirB)7`Oh+?=`!a(&K$2JB zO*Ce7pjnPXDa|p z@Y1KZI#_;H=z;7j)I{;+^{E*n-SnOD4BlxkD z>&sIEAa800n|aK`)AZfDcHYxDpIOUhw?2H-2YwR+83E8a;HJ(}EF%}+bB64}?quxP z94H|@RpF-Fm{aDJ|KJYviUW0Fb6-f4<1n_W{tFbg1?ZsqRN=h-3 z0urq2#d>xnqF1`j7y;%3vC8xgW+YMV{4ESYmMta#>kJ@FzKqd7#@24Lf3$`3;H!Y! zgkJAL+%CPo_2h4Hq(=okX5ukUY=@`EMy6+MOsNA@BqQK+#Q~bXfQ^BC#4l^GvwzJL zf}$94JfZi=4T)blA@!nBN~=(*X&5_Kx!|5))KaT0^72eD}6=@|QP7r<{Zsp#oD zfC9#)4!b%)R|^OrL5I)%D4qCAO^q4?bmEKmKi(2@b%{K9A0i9nrZC(XF~b9*{kEDD zL1(=UYVm&!lY>^GM(4Qb1-A!u1^*Guy))El4$;?_cmJEwJfNOOSDoEvMXpzImdxL>?ync$&DsqH z2g1?KO`ZF%SEEu`ttL7A01~0tcbYCO3Kzb}Jz{J+BTaTYQBUNwc_SS3G3oy;ya5ED zwgB!n)biQi>s#!F9vK<20&2j#zBpQ+VFmN0ptv{dUY=jA@`nJO5=qF&mU?N7Odjy~ z|D3ykml*>X0&FJJ3;&Cy^u(bDs=pj-37hpg=cAt#JTgVip^Vw(pVOe|chYawKmG@`|2Vz|$S& z)uoTAaC29K!8H+nBqjnND-B+Px9x?loq#TE6#`(Z7lA1^N7idogk<#@p~-q z^%VUDP@QgLb8~;EeizStAb|q~XdT;C^L{DNP3wNB;P*o3a=MXve~Edp_>;AFE#>&h zKKxkFMz-gn*q1u6TQ-1%fZ`zWl;T2@=5U5u$!foNt7UfXLfR=y8-?(+W z=zVY@y3^7)o1iQz?^2nT{a-sFU* zeK-^Z%fa*NDSxQKr-t0Cr2KqH2OaE*%&#voBI$I3ooZ1N1D4aMW<#(%;Fb{`-~7~# zVc3lNB!B8kAD>AajeFh$R6{wwmBnE9YV$4} z?;_wpn}w3;cV|H7OG9W-GM^hB&=8~jdz){iZ@7_>ki4%>gEvx_Y5f%@qoTu9oEU$f(3%O*u%MTd`)I}E(EJd0uDOS{L-jocGjkOKgI3v6^3ufgIDS`>PYt73-zhwEoPP< ztIw&tw~2dS!l-C6B*MT@*-l+zvF~y&z%INe2DR9)f2A;?M2|qol3a`*)uQQe#whI4 zFznMV!FF8ieca-Z`uL`biQ(1dd0DY-eEO~vZ>ja}$Be@Ln(1g?ToxYYLThXJg=E!= zMSeR=m(<5ix3#$1G0e~HKH*QW($3lJ2wH>@G`zaWIPErz$4NA2k-km{?mK!U=mEgU zi?r0}IS@R+{yNCvd_`WV}{Z1!5tVBttP+WyLLT#HNgK#rCaP9`t zWFQcOBOSlt#X@PFpgFvEqMsOc`PQ`#lrV>#jQf#UC;da9^3}h{5sRsRks~{( z9l_q}XmsmqHk5HRq2u<9I`TfZ6`6!VVy-pk3fRmx*{w-2S6I}F>w>o|KgAbs9cy+JQ2Ly00y3Ize6n0ti>!b{ml@ly%Tp2g7!$*#{)7s}#b|w`;hKxJT@X=5jFE38%Ln1944o|Mqcu~QYov1WS_|1 z*vo{&pdt_$*z6bT|?S22yr%xT{d7kfc&+8sn5zKSeCC@hCcHecbVp2K&i7dE& zl)m_k_QWpMT5jWy&Qy%*@WPSt@sQw(?JW-%C9+)l#gla%vC;Rm5t)OG{*=zSx-1F0 zP`IWRU-r-sT&=F?0?JxJ!`1m~Ybl8MK?bewG8Gsae-WL3w>nJ;EweXV<^%Jc>cHGT zUpdTIViGJO7>P6oG?777paHGt*{QvKk&iUXuEQVEss0QG*af1voK^qb_h2@psE9S_ zCdbp6%f5Ud))0#oK;!adR!JHr{?m8BSa&}uK1&g@Dlh)UWG^kovW?v6IAbh zjvGwlX3j5JqZ22V?2h@DI=P>DY1QEB@d_$P1W~Gh3nZM*l45)+)$2F3ra4-YYMmv@ zL=O8=nqS!ahP1H?OPhr(3AtA?nxHTx`XLGR*rz4+*@GPxRV9YAVGY|c)PjP7Jl9PF z&*_^JzHP~;?bc79HulO11P|)@9GAn}0{)D_nHAA?*C%qT^g_^2XXodqm-SA$Z@Vwt zVcxg-q^q(U^UrXmHW=^548hvXUan%vvoL`}vG!TT^~l=8N4!#+*QyUle&*MDtqZ4> z^StMh{V`T`2MXP6VDpJE%5!Knxym*%b~%CMi}QFOha6K~U8QUKekLKd8vn~5Eu=$b zZY-7a+s8Vq`Q{qN0Rt&R{5!9_#Hdc5NZ>(orD{KN4axD{8>Z@~RhX7)QH$>>fV}kM z@2eVn`2sCvGTrs#erEPHl|5%zJsWmBZ$)v*4o(a84eT|nxXaDqr7cRtO>(PS(bJ6q zDk{x2GYyS6Jb1V-EKmJ{Xgjec#@#7z;M>9q+^KlEtcQNqXK!5EAs1Tj;Qm&UgTKt7 zYIxWvLL|8N&f-2p!AGrvIgN*(W&^!6{XI4{vzP>)(xI?JQm$~b;&i5ri}|F<&Wd2@ zA7K1nlvyCUfd8z`Eb$V`I3oI8F%{tH(=qV)Q|)i4LcjvJ-f_yB)00EZnm(r9wDkxn zDJg#yf#Lsm>|dZ>H@G04lDzgggdPaF(r-pvX^_fag5=H4x(Y^&yuk4Eu-F(1eaNe; zgX}R+dyC^?e@jBdZhhLZ2Ep5$$?$x=f(oZJWR(^l=UZF1g??UREBzX9xF&;oQw-vb ziS8hb887+#3GGwQ%y~soBE<$WeLf) zjgI>v>>rD1pXWR!o?Y*xmW;=>Jbv}!Gu3XYI{jfxhI(xj`d)jQu*`jowZ2Uy!F=o! z0(bdX_Y9AgiOaWIY{X3KeV?v`RI@u`DbEt)x$zl& zAFR-L=1LCZ-a)%S-p><*AS!5aG5hNAW+ci_DQF-%(o2 z%nUR=Lw0KKU9blC>V(Bg;N#tzk?bEVpZ%H=(nOvL z5KM4X=Ust|OFI#>-BrD`hu#}nV>J88qE!>2!;^YV219!4%BxA?UdQ6y*Gpdb6`9->cTo&-sc6Vb zhzC`LQrP?Lavw&Ht|*!Rz?}0?k#NeD++1GT5~3Wiep_2J^Aw?Ju-ZJL$8vH_2qp%~ zs~c)H2ZU1sTXXHbw;eN#|8ZRUtk_2&_UeJ0f($MA34-`yU>tXTb|;Fl{$BmR5))Xh zKH6713_m{q*|SJXKMT`nHgV-M;k}rNXQm~*=sx$ibhTohQ%Xo{QUu9BDVYRlkutS# zvI3k96`dypxFU^4#>1sr_3v-}R6zBiPsj9N$U)1@EA9%^5%5B*hDGBr6vu8EEa#Z3 z#~3UPEi8@!V2DT@S<0+XK|KhR%SDYmEkN9jpWW1Oy?Z zOF@k&eTLkt(Yeua1QB{#sUlU4nmt}d5mug!c$oVvV>F?-@a#{z&M_f0vK0mJwl4|V zYr0j{{0e=?Uv3?l3VmKtERCzZ$+P?5-bQN0syI10KSh7FM~uUv?n zp@ED`#vcU02Elfsf8t|F3X@$Qg-1LldW|V7UvTQAli^ei;_Hw*0Q*dHycG<|zPYjoL3Xxnn@-p)~yclY1 zvCtr=|MONq_2pAJDkSEg)Dqo1Yly>Q=0)W{9PWH{><0EE!u?^_8MSMrn5sbLIX6F# z`zEB|`k(G%{}lvL(S+|+de=;(rB_yJw;e>jH<32fzcr2Z&yz>ko+JR=ZzL^jY9q4H zBuyBKIPRKo($PIG=h9fW$Lmjp+csejmR77VAGg_lW>FDZwd2D|{~krt{lMo=z*Cj3 zlM41qk4n6gz+^CmgryDW7urav@ZIMkkXW5C6HV$(TqS?t{qVqBAl>d;tXImke9Hw# zEyU7pZf||QO7QLp6A{v@?GsH7bc47CC?!MbYTbj%?H_Q!Iyw9!u0#R}hI-P1&#&y~ z)UEin;HA6|`6g~15wV{z{hg@s6!q_XpZwL>*FD8d>f(Z$Z7Sr;v#?$q<4EwhY}8I!*}189MX&;){-YIA-A^59L8N7VY0d8bu%dIA24 zpn5>82~xgK611m6U+y*ihl%RP+V-yVgzs$dp5Y5OtReX7*WNG$#N&SUx!Y&?-1%z) zjgW3ZIHN|uCCutxDUmL1J#4;hdgp*1WH!SX74Iph#*pp9#0V*eC*Q4j;(2aZR zsmPQc*uG2BvB)DVPNHDSeFgK_!|b&Ya}dL>c12bLWYXxUePlnoOLBYA+2?RNV%NL9 z;`(gJrIo;5$=xq6-T59XFl3bLribki%=z?%)cQ1MmT*h0J;zLMZ>`pSA^b+*I`)20 zU5$XoB?8HO*OaAhm&LtiD2g};uV~cqnT7c|IWMZAwgVmjl}Hdu6zk z*ZY`YXKXUhTM;bVp01nm>VfomR{8u+IpsE$2W=D@STvu~OG` z&dCE7^Ha&VTeIrydbn5F)S}aihxKY$uoS9H(0JXdnlfv-Jn#m5JVNcrl2a#Q)7YCv z)_1x`q{5|jr5vV0XJ^IYHvNh}v=NSF4))NDf{uJ=<2qwGBkoqD6V}MlXo`+G`3H zo%UT*C|hR2t5SNExBO;q8|BFzvB*m+uPJ))LW}z=J);zz-wG+%>+T<+k*{KPl=VE= zrp+y;CEj%Zl&j&a&T?>#+4)N@RHPHfTe@*rEw6`iaJ^Ey=5jwL>HG1)b;-CHZ;ZBM z7~}huwr@O|=|j0gZhqzSU*~oD-B-Aq%K}87VWO8_iOfFXJ{0Nnw-Y+y`TF(X)p6yD zpgQ6?YrC+Y*FUi?FY-gjB?$)3TDUFRpm6vvc)Hg1Dq_#&?g>0Sy=9>T47|bja=Ey; z8u|Y$&Ij>9!)W9z{G2&&y}K?fq^U^)s)roAv8AfM{P$Qo1y>E0H;wsyx)<_ntAW^U z3N+>h)EDg5+xY1@(mCcbk&Ol!ckGpk%=Z7sUE0DS7OG1J2kz6`JZq)G*hg(`Z7jQX ze*jLuzLpYE2BX{=X8*_UBg)>Y`afn`)Qhc%uaNO)8Kd&Ltlb)SoyM{D`1}m5bgP4h zNednOm$o4?*-1&%zP)&h4ZVBUdSnZStwyfP?~iXuUeh@5O}dC5#Ro`Ef9(Q5Jmbqp zK%Rs@er3P-|3M-0YgVG17El(ZJ?=hll*|>(q@7Pk)9&sZJ=rCnpG+%rb8J}Tot4Py zCG|lWP2OipLmiZf1ttXm({YfXpveOxSGewex3tZ{B5K zX#dX_V2gO|wq^J$2z(duR9L8x+^3Dj$3m-@msgC{5B0wj#`0AL7O#vvpTF`FE=$*- zGPIrN;p+0C>KysMb6uXSx)BBW%`L8Hh-&gk=eIHkIC>!wwqKL~QDUCZ|7}zq8l!Gy z8rsP*sQ+<@O*vP<_aZ^5?csmDDX@?m=-{^;jKlLj4ntMg?7S0(Bht^pxq3dkNrCP$ zJlY#tiJ@kmPT`dy9F0$iKYYH&@gahKlT_O4{`oI)sOnFBdF{IY-9K2c7H9z(`juOP zKwsF|w9L&HgICG5{^(5yKJ^qd+a;r&9)ta!eFvT(h_P6w?MD*)R}EMY`km1hhb$9! z?_+__g&IEr{Y>?NOzXlTME(VI9;0PsW&-X3vwc1|oV9>74YJl-8eSHKSZL)d~up#(ItX6tw4;(60?PE%r@ zf;_N~)~D=`&s_XfEC2}wNxU_}K;R7eBYz=?sCjJYieIfADku_GR*8?xt7I?ZwB!^Q@zp&lWYLx^rNirIPDn#j|t!LdQD>H$BDWK1z*|*fa$hMB3>5}P|jYX0TBOXp*+=}`0hkAiDqkNn{_FTS12RK2V|;Gz{{2XoNW zvD{8r0!QC*A6O3DU%m5GRyIN4QT@})D_|!30_*}l3q!lq7CE7d6F|a?|Ez=fjp!GHI!INHMxlujZb(&)9pqjAYD+(x=47m~ zBy9kNuB%`EqOF5uzIQu?`qjEM)9N=bdo7+;&4{+p*H6Dut<70u@2Beavo(5ww<68B zel|l2?-@a!$Q+d~6nLxsc(mZ6kRUo5aBDP#Cc$Q4K*yo2%L#^j;DgrlZXUVx(;-u{1amE`y2pnCZkZ|0Gg!u$X7F^WPXhZhH9;e$xw{O>p^g<+=RfHaBbh zvKC`?6L%7pTsziees=Jtj{5!=TzP(liJ*O#sTR}eQ}tY!eLJ=5*G*DBA*<8zxwgaB z{bK7JA=n_BnpQrT%5za=4EK0_-Xs`w4eB!CrRD+4yo8()n0QY;PanCDTq1NF_X8$~t}Rtlx<%#pRmuPdUTrl*R^fwIr(<|5mN zCB?$l9Y8dP?WXq@DL>GCvx|rA81*#0iE^l9=WwIJuO)o*K;j3FKIlw!Dd#ta3K)ZO z#WvATd06$XPWbkIvvF4bLnt3l7|=0&bi)?fXY``vADum6pb@ke+h4v{4t(Bw8A`Ik zON5;#?O-sVtdtg8hO0PmT5w}G&GdCcrob)ceK{s7p_@u`NniT*QW{0NX1iTN8UvBt zw!-{Q`zS-Z$US#9ld(>EGdS~cZ%*w=B(k07f3?g+@~OwijU3fb(Lka*^z>4P_^p|_ z)d4aw?B4;X)z7@fRq;o2scjYTolvmXwvj%!62i96#KJ;=%e#RDI4RLCo$4?-4g3)g zpKFrv9sPjIHv;B{E?qdP1FWG`clo6PWcH}rY6S=Vq_0g+lrDCb33#0^EyJIq`RI*4 zA+l`15nENBY~rQ0)Bi$#d@MM)lcV`d`4ax~L9&;bH(aM9)F)vpDz|R)9_&aCb#NFg zv%tH;uD;B(l*9gJv0KR>`0UxR%XeojZY#G2_3}+stBDWIiu1aP!O5cRj#3y&$u;I4 zhkdk#Kg9Z5oDHSkqswJyK8K5^G<#KDS?8M96S~9$#+iEYxOOjjZhrL%zy?)FwAden z&x%Vd^XwwmCJ~$CqN#OzDorw79II9;xo*t$$JsQ6*#^%LT z6&kgx{P6%GthDaX;y=P_!72|&{n zTM8P#?IhB;&#BkZg1EpVJG=b%c(zkt)Fp>+4vH54F}!3OhsM7@iNe6M&E)e_Dk&Ke z_Gz5+Jk_yB3NvnUJ$2RQXYC)P=H;vu)Xz2`a;a?v0mX~_A7f5gyW-_k04aN}S(_vUQBw9no* zhd1o2x%pxAe|+X5Zoy-$O&y{>W?+l#}U{`KLrhcYEGA6*~ZY`V}wkc#ErE8O)!&+$~zsqI3qRI z*jL46!sWbs&{Mi_sL(7`Rr3%za*n<|(4+^)evB)`MKb#D5CtZs)W{ZBj%l3xp zhyRtN@K2#4PeZh2tOMrmQ9R_6&wZ?*q>*!h)?WYBK6f;Ay{7A7ZBa={Qg!uhe_XPG zXGr}>|L_4x!h&9JQZJpx@wmv|>}nD?v0LSvh;td|;=a_Z@b7p_8Y1x#FXi~x^1L8k zWVW2PYo(E_NDXuoYw^z=*MC1Bm6%~8H#xas$ZK!R#Zu$+Y55|xA(`6m2!!mem55p!#YNex6jNf`t~x znvxbh`5GczqQ-8)8*2t$2g9{IuNUnjv>ZOJvCUp#<#<;Ve@oLuf}4FII8agQB_X19 z{f$wM$64>X;i{gXeD${O!VO1jvCocyB-c{qT5h(}ehG-c4mNC|$9o=}9*TUvLyhk0 z*$%Z2ypMa+@1V+xO)_>hd8NSH9q;F7%(M$jsG>yovnQSkc(_2wRGjvFj<98YucBNt zQ-?eXm&cbdB794J15G2NXc7xoQ^X2wbSdd%dH1p9*ocL-b-Gh>M0x+CKvluF7!U~L z35~_uJW+Cdv`5n9c&GEsw3DeK)XlXCYFlct#WwkFQi3q9liy}teX9owUQI|lVU!M67dTaNd*xP~#0 zmb*Usnck%TwLSdTI`HQRR<7@^%8LYuP2s+jlS}OKm^sPGQ1{`3=`lI8{eH?m332$k zesQr+p5_a`ltrlB=Iob;x*pn{t5OOx9c)y{rpdG;MTJ68MyEGUw1zGc>KX?VWQw21 z@hlu{gH^5QF~bX62o7l?<}y}Vg2D$GG&2VyW@LP(ajD&N3$b=Rv%}NFBFrFHG7GFu z28E;H`L7SOc?CD)M$_G%6UTKKePwLFrNI5;iAw7aSpor9n6|ltWFz}m-K>g)=Ki5J zrnk!rk=!4S(+9Fz*TPU{ZMQ&O0YqM3Lf$P{nsx^ zgq_ElWqt>AKLRf&uczHJdZ?LRKNWEzo095QJd{(fC<)o+)jC$A+gv3veHf04w6tNt zx7|EQ@&NW?pt2&7b)|d7nJPmr$fvAQ3_(YcwhmbxEg76?2{T4PT1A^iLBGOzJs6Nh z#sxN9*Kt(vTBleQ|HWHTr;BJzbQC*oYoq7NHK>^mCSj9gM|%5DULL@$@Dh;i=?inV zZsUPVV5lJz9ILRqS;AzCdv`c4_xUkpGY)ISW;f)v|B$#5rKXUlk^k^L(~d;u(07v2 z?3pBoq3kgDTKv3}-?H>b(a&o0(Ub)`Q&gde_T31d`>|uH`0DB;ydM&>w`bX{I)mm+ zT~wX&k-ZzrBsr`lm?E1{zo|U%&DK8DOf~C)B}c>Q25I4(JUR}#KeE3sJcyy!)G06j zy6~EcYQ|bCYrgH0$hKvu6lEh$U(dYcn_%KpX2X808rE68#w!vEHwlo`d+;j+G$Su( z80`y$ErnOlmC8zueX9AS;UMg4RybEPo#lQ)>{_p4G40lHM&=<{ia8WmzxyN=wHWHV zJGB{OQ%&nat`FF$>ayFmFIb_#+3!JkoL6iir~HR2EU8S+;ifnDO;eAI|K36kyMxL4 z^l@DBIrH8V>Ph<+Sy`=uA_XG}t9TE;ty^)9+1@ULa7nXep4H5;#ejf*U+j^epImBQ ztvbT(B4%6Yg9oW93;}kW{hV-FF(J{RBx%WhBzG9i@Ev;E4qR zj4#xhpl>hsT`_LuqB9F=t_?1wp%Sap!H^Bs|Mw!FUf})44jmqEXbf;~6ln1GQlHFVv%Lg37 z801$OA-e{f$9ogG0N3QvDYza6st)NsCtC^J?%QUij$z)vjmeH?tv8bg=sJ#FH(#3s z>(y7G=(oxDb9KoyNd7^hfkAGY0tv2WYl||WMi9cts~{hy&Hm=wM<#zHctKb+5)- z^?e!EA@-H|bj~`F)+lZYEz~uedgG8FeW|@Xc*LBQi424X8tC84M2r^4?=Sig98FodCiK-Ch9%b4}!J^!E(KzyyNoG(&s z?-s=w6*!Q*zH&k6oPdB-=9U+|Y^g=1CymAKSSS5C^FW61}g)(p5h*k_V12vu59zbe=0MOGPPhhWBXu8qQ;<0(0{O*&WKbUSAZ62!q25fxp` z$jteH3NbM;q2uEla|^n~r3l#1+;IA(^MCnK!FodVQrnLRYARg8y|uBp+8RJ>>0X?F z&dTxoUIB}OVv_i6wFY zq&|#-np%wgYM#sbIN^*wU_iXkWkD6Rm0%+0@(E%8D_k1YTXE~06L7}Ljk*jXA?`o8 zA0E-=In2wZo(v7CmuWMyJ^iK*92Ay$Oxi7E)H*F+Jf!-={T`k(@ub5R$*_E65TO*7 z=Zr>SXh3Ll`~Lam`p}@rD3@HtPyni;1XvVxy?+{azVhAuavS^i4b?7Uu8fv+VjhiF zdsWa(Cr9co1s%8yXqdYF*YQ;&*jC&(eoNs6`24&&LuYi7zvwT( z7YQaqNO_$5z{Ew7-L%+z9h?-WD+H_5``CRx5l+hRhXW_itIbf!j9r&^b90k@21<53 z9E#eupV6Q!d4#{T=;Hp7QCl&uBB_Tw$N6=uzhovhUNBd;%7_fG$=Nd+kOA_I+?tbe znybu=jBDFicYmoA`PD{9g4b@;R$L4JXfjx%Cp$T@@x8vjbkGs!uWLc05!6y9+O&V& z1PtuCzVYCZ*TJ?OqgiTdDm@EJuiNF@yhFrz%ye`a$QfP6zvKfZGP%&5qzOcRrP*gK z+2`~`c0&&w`&bMhpZxoBy;sSRHdyl>pB?1YY6!ZE)!=7}mSh7$@ggPr5@)*R2iI5H z#UeXXjfYQ02Aan>Zr`HaPat4i#pBmU(b0n_~xI6?ltw7lbSJN(^ zLbHjUpS@tMIP(1&MdkBk!O>!CHuIjlJFBCEn5f@MY!Uz~<03K61~4gC+Pr&de?7sZ zG6YcdIaS|`8;b-E}xb8U|Vz*oEe53^vf5KVgITjYw>d0`}g~5PLFNm$jPr) z)NhscUKq!#8-zo})-hW;&EwaeL^d}+rrCHCL*W_c`cH~8a^Ko0z?AKpVm~8{cnS=9 zC^hwmj{Yr8KGyVvuaoWthK*$uUz1M0gyOfivCGPY^X+x?a`-rB^Tz3D>%d}neBr)IVqV(v8abuO>q^VnS&Hce2e z@ji|CZt^Y*oW8EFhf-?k|s z$U9Pm9FcpXQfRjm4i9;d~{C#=I>xS>iD%Xxz0(Z^zc{ zJsfq83H^~sII)Z$TyuXo_3N{-%$5p2uNZ#V(?^;#kgb{Vx_V#`Z!_9aCZM-NsW{jCQ&@_G#_O!} zipsUI z3Z4nKsi~=N|E0wi`1dN_>B*oeUOu5(s{Iq=`A-dZADwcnul1~7`i3PMc%01_C&&f@)Qr#{ zD#tMDBwG0VWb}wI(kA?n`2k?&KlHxKBrZBb64B1xW-lNcw6@6Flxy z$;s5Y45QmUGg?P`mq~dyeTm1qwjcTw-adlI)p00d`W;Am4A6V|6Tj=euY=ye(-{Hp zl=vcWe#CtWP*7`@D;aZ+S$XUW24m%Qb>jiXw+;xwC!b+2nYEb{{c?r!WxiEqPrlc^ zC`dXTPFBfO?ReF6UDx%=Y?Q?R!<}EA$n>rt(j;+y=oyhB>#q_29<|vv6*dVe9niqrgzILgA2DOCpa=W=`>vUST#V0V+WJkLbYCJ z_rbO_0n&$+Yi#ZZd?Z)9WvwN}kJ@4n^G*tQ>x1e{pY42QR}*>uGxfESNo{!*NA%n< z`}NW(w$<Z==7_U>C3akgnT#EmvZv;%#bY7`4G2{|@jKMS z=|f@3af0Al%a0S_e(gD`Vd$aSd!xErd|65Trp)0O3LRz(%g!xEc_VrP_0nlD&=scm z^fa!~AR9jXTbp<{a(U`1zgsva!WK2qF+<^3Jx3F2UE zv{hV^r^P=rVU?)W-)cI8n$!xe&0bIM?WSl!MIwr+Ev8U=PN8gc5~%zM?AFtH$HWko zF;E>8vTg}kX3uPx#G?Xac!tZU4KZ@xr7PBSHKQhLrro$d#AQNQ9_a}M-Y7Ip@K`q& zNMPoo@JJ3vJstj*-7)x+`mL=~rj(@L=}M!s>zGuU)~6$TpGv44 ztEQZFv^HYL)!`b2Cwar$xxp`W_%sZef+8~8)W@8>^5ea%Uqp=zN}C_KMB*#bEK;*r z=eNgH7+1%;x#iopO~w`^Nui*R4s(UcsLyEmcnCA($UcDjWbO=HDNL6#H65AGDC(qr z>vQqY;t&935A>xg!9mPvIui>5`NgWeaR=Qu06_w45OT$soXu+imVnST$~=BDaqZVDfy>(EEq3q&XT>unKJ zcF~K^W)>GO+*pUz2mEk_XF6)e?~{E?Hb>Fop~QPX^Uz$o+ay-FqLg(b+MI-wT`oqA zsi)?o@R5|B$Dot*7`Um**_p<>EnYQ7ABw;X{b6L zqv|`$-(+(d%S^DQ{3FaSsW?iqBR`+6VCat}&D=R6$c(e@9{ajoBS12jl_|MOFVR|a zVWByoAj53sC%Uf=1I5loQ0D9bKTDXXE1(_IYZ-r4^gIxRGGE(YDeR1`tpV{rXO(4N zS}4Ijhv)tV^9qB`qu;^exs1Zr08P63-Wxmyk+T3SSx`yMyer0sQd_dp_5=PgN5c2z zL<62DHd&3j54xmVwF;~>D#o&0gYT-B9~Z^T;yK<(i*0rw=o6z4FLMqH@DJII8YtC1 zNa>cm&8^#lGVL_MR~Q~+xVF!M>`N6Wl`m1x%3$tmGU1ibvlk-~9;ns4I6L`(WGJ~P zl526~sR8y_nWKH=K+H0FXchHx_sy_26*tXQ`pB4zjH3@1nP(}1=c+B(x^XZQCKucE z_Kt9D;Bu(*wl2{x&4ay11N%*4ZxWt`{dzUt9eZH|*_rkzi9FXkf0@c(nG3mm97xtw zQ>l&5gYzigpC7*JWe+>(SbUFKDN)aH_F{Fm^~Bj`8x8tUqFnz@%W|-_q{!9(bx`JN zvUwuoGGRIgJL5o=+>FJ_?DaI!&ylU_6|!aXA^k-PAm;0&**>@)$Celw^@AhhMfJC$ z#~t@xXUAmfx7|G0`KGO7uThXl8Kk-9Ze5J*ovq2KbZ2zU$cI%DG^MKFdYk|t#cWQVR6D*=?(GMvzyQ)X1lP;10i$~-jod(sDB$}%mTb%-f zKW5t^;^T9`NDLhnRWfx(vh=SrB8MT`c;ggKL&yZyRs`l_9e~8#=5t4t?iBm=ncu>a z#s~PLEjy#5&vbh!PLQD2fTex)M{+d-NAFe$Gsr+2$6vq7Bg>u@&~3A!Whh|XV6gjo zm&)|0p^r^2s%^EJon+VVTK3P*l%D5`B8%H4YdASQm1~11orD9630=h{=EIFpj+2Df zLmsj=S(1aXkv)7o_|dRQ7MCkQab3tgDMi;0nO|#M`XI$KTqjm$~g<# z1jpK9dE|9NAD@NoUm8DG#|<*)Kua|+sKJ9A2AMOGsgxXo|$S5e|RQ{ z|KPM0M#{CVBc_Fg=Y|CUjUYP!9WcqsT$F1w^YfM+v0NZF1revel-eTtMF0e7cL9FR zGa&?0<@q8+R}4sE8?NhVqiacp{cZcrJ|JL5srnV}DlveNUCZ;akOj?D1}voJ&d$Y3 zJ7CE}+&nRhNuP;Q8a4=@Kqd)2EMh#-JYH*r(y?H`hbQGUb$Xfw7f$-h5yT*P`%X;UkD@i#+5+S6ODI%A$7yn_GSuxq z);9UsFAmtfw0nEh@qdy7gTxO3DrV>Bo3Jym)QSUT?oU%lLZTgh4%AZljbJ}IdPe-g zz4Iyql$+MJ4)vJO##7kkgMWCIe|)qyk(p{BgR`ip|EFVO)t zQt4V4jeKVfMi@uAif%iosq-$#A#kl9Lv=nCR|GRqmQ)N z$u5wdClp-;#!@;iV@~nr(87WOBR}N9T)lPeAUPQsoUr&FoD1E+NUp6|uU?kkTYf6+ z=~=m@L&?b4@t@9L7(%F5mMa|miT1*fX9<3K`OP+aN=m%Ka-6$^qbtjVKGm^AP`UEb z7fC6#_8!uhq<*;d%~{gn)Nl3d0ZfWXKCZ8Mj2k-f?8?^;EaL6pf^dNyGXP zD}w`$^ZUf}*NdSE0~F%KALspW`8UG*Gu4<(!{a}ExbX*a1r0zgC@cifBYIq-dbP5mCN(rGL;3ZK zR!L&Pv9$s`_WzfKUx^t;pB&=R=IxUD|9&*oYi8d{^BaSRrvWwvLg0rFAI3I;nYB3c z<7jqk5@x2G7rIQ&d*){qo(UXn2+N@#?kvxvp2E$08w-O8f8F{2eTWZmvja7TVu62J z0>po(DZ{)DccXdT3apS|q>J;lP!!E#tEe7CMe-O?kxVvz`|E3#YJ886N+!?{UdwSV z)}h0$8vE|uyM3n}-6;RM@5%G?y>VFC4 zbALn`-0s#Q+OL%S*GrASDtbDm2}X1_P$6sMRa%*<+3#?!-e9u`e~EKb)tne4<7Z+5 z)I86uo5J*qo(uN$^fXXLFk(IUU*-#g1xQK#if<3;`RU41ZEQA?(F+LZf#^~hn0ncW zaIXsi0@yB}`}SKe#FTCah7cMWS~S=+1~A|U4;}=0$PJ#K5QYF0=O#O6!XlWBkevrF zdJ%OG_Xach5x3Nvzt2#xR@@g!knD=?g@Yc4tXCiA01cYeMkPmLu6)B*b)JvG=pfhi zJo68BSC@mX71_^90uL?MDR5fJ@(4zXmj_IZ3hhhQU-wA?U0JcB*{}hCQ#{e9loaly z6HyTn*Z%_Yzte-V_Wo@x_m{up#q+}k__~6ELitigCNI=CDk@4+PR`{vrFcl+Y^0v< zA16IRc#n?G9(f&A8)vEffzHHGUK7#PHco_Opmscc0kq5Lf@ z7=2s_3-ImEN1?whz;jvsT3_V&ZlvCxp75}+Bv42iW}zTY!g{ebB!5<<5v(Tyh9{L^ z-&B0ri3ymQo>zF3-7&ELM6;nZAKlt211~I*;*y7$B$)X zoWxLrUcKT}!CU#2Xj|ZOIqwiv@-m=2>}_EBn7Aj4gjJp z|2776gbPFYfLbCd5zO0gL(!dC1$pQu5j8^?3XdlFFJIXgTU%h<7OQZIg^e92$?dYX z;(3V%*ca{O@IUD6FJxgy5)=&Gol4E_|9!S_rY_+7hMN*a17ihifmkf;4C?KnLK+(y z`n{9V5IqtALtrLqZf>^acL1fhg)u;7Q1qA~xfhhR#+3J9?9rMozI=n_G5d%F|lbY(uvqii8DFt&3Tn55DF(t2>q zf=Dp`Xa8ZvrC~ss4iZj(lF5<7NTbYVTO)v+fYohNtpWN17x#vGHIH(YQM*)r!uv40 zutclmDR{E7`S_dUY%%}RaU>`{kV7|`a~=O>%794Lnq%Lkrf%hbG6N`H-rXldf~ zq=e>b9-;uELrr}CO=hnkV22B$L#3s~u6!a<6dP#vWGxgY)Q|rhZ~4bpBI*s%`ghM8 zTwwgzKm>vAFJu*VWORv)YJ^zGt#q(OYcP4P=OWF0L>2AynjDu-t==mVIB1Z6zTu7 znv0<<3WO))Ug)L2bPySpj*xS`a?5elx}+rpwG=EJo1L9CKRNmdbP}7f6xQ?b@rAwP z#Lm(vZ2TF-3#zug?I5P3QLhG7+wb435B|1|NDwxJ(&J1ivi?h074*AcLTAES-VG8^ zr^#AmFq7@0WN%+UFaw0&|7G)|gtG{NBu`LP^*zCUWK`6mLSE7gsLIa94`%&&oELg% zN&!ITsI<`EZ8pD6rz~w(k-1VZDbD}_Myc1Kv-No8u$#8?u(2%pqy~WWS|1L*KKl|u zuj>diLvXbQI1f2Bgd_310*d$p{}svzPD-O;An_`3{kSL`{ogkLx9|t-MT0a29?2a9 zY_WsDMk=139MdyQOXFv0mB=0+?C^wT&764wGeHQC%UU1M5G_i+lSx0^3j_3H)ijZG zhv~tCe6gh@`M*utRgheQMuD%>vbp~XsZ@}8Mnb*rgJ=;{WP8-~ypq9BKZq8Sr6MgX zJ4+_Km%-)|QBm4EcRr4^CX=7pqYZp@xJ3kryVyW=)nZj$Ozh~ZM@WwJb`SaQw~f&k?Mu4TNs0kR6~w%Lw9s@IHov`7a7aN(>G!m) z?ge&4OiU)Yml&j47#l6ZEr9YO!dhmRjK3p|v)NhdC*ic!?;?dK@a?iRhdf%=Wh`Hy z0983|?vejATrf@WX0lXeu`b-l|K%OpiVFOpBWMnChad}_9yu`RRevLX(Gv1D{M|c6 z4Lrg%0oM%}OC3xm&*#+pc(3NPAN(9+^%kCm*03j7L*#aQeU#lZa6MPZ+@nIZaqKG;Zq80&Mz zTtEXcV)}AUMF$H*9u7nsU=J*hdI4HsH>Ui7BuR(q#UmHrH6SC{2LMU70gB?VwQc% z2si@Ng%{A~+GFN}q`PwU1a=mCX|{0^(S9j`Z-O8I09FhUj?f$m9G*3BjByV+y9`nf z#%g?gU^*SeKey(2l#rVm8&yBNBt84Z?+kfP%K-C+XVl4#90SFY2XP!qN+Zdb`v|&)h zqO^yTeKjXzB?&wy$X?FMnia=j#n;YV(A@EfvF6CW)ACqSZ+YtSq*3tgF)_V%vcA~qCHc4Bg7_2Oak0zDP~)!2cI`iKtb-s7L? zpe5+9;De4}p(i=e`)HcXa&-jWfgz1Z;t68&5fuxO+Bf_MX$2I#hpYE|L01{-Q&*9* zn7vMqoP|XR)Z()3#mAttZIMiBLPF56T%}Z5*-1cmdfo;7+XbCna_Jq2OPvGfEzO8xdtn?a-CQ{6Vyc_;W-oj4;Vw;M*!<|5vyA4PV%Rg{s3E8SYTR=YTi8IHXc-(G@Yh=emn}ynV$@CpEk_4IfrW0JMv~1eId6l`^ajp|*f5H& zG7m=A;a0;1uvYT_x0(IcDA48x(1{}E*?)zMf^e*r5x5k1&A8=Xot6j8!GQ}!3ZM5T zILUzx;4+}2Nuvv=Pzi&@z^W_g+TSJ&5e`jed@i6RK{z-|Xjnr7nDSLMjzPzU?1)Yd zUDxB387nal8x6X|@8%R4AA4^Z7IoY9ivrS!G$@U9Bi$e=lG2@0(j7x1pdg`i4k6v0Ln+eTIdpgT z>;d&&_j=bp-uk9dH@|bE_Ai%fb@{g40d0;oK7qU7dQXNi}E>OZ%LK@Y?SWiU&k6xzas3l}c_j^5>=+Il8q z*e`OQK^yXv;<8kjgmXqaLzI%k)MZ7j5c$4P=@ZGB4EY|0?+{sk7g=N98>QcJgD!69 zUr&)s696gFPtSiD54tY!`LNGIds%AK)Urtp?WZO4uf4yNzT@Q6A_4;jbBE74UJiks zfD-1H#?U=}+91!f7wa@ck4DEE*S&Yn{d)Oq5?awG!yvVRR!_?Cx(55l3u_k&czmu^ z@Q8&7Ywo_AJPJH28^YmM=vPu`1|X(&XMJGb#jfd{5zq$L#l)!=@xn^=&Ch30PLNyp zN9TNuHlvyr{h@v^U-sIUR7hLI{njCS&dX`Y<=t;*I*&Ki;@c9(q@`?vE@;2#hr{U@ zlxViVGPvNHypYIO*?zri2Xm3tF0uP)9RjAt9N~NGW$dJQ84YX%3c$2 zfgUPeqc8_-{=xnMkX=b(z{{Z4mLy^9mqq+MQNYq}w%^{_EYy|1?5l(r=bHW*L{jvJrpzoi z22wxZE*|*0T(}P;+ieF4?9*u!q2D^$!m|&qi3IgfoBvikF4U=(#`6nNl@nxK=c9o7 zO>=4J?dBam&%95Er5_yRRskK^1B4)#fa?W%tIrZJALq~eAWrYVonMQl4^jYsHvN7e zSJ)f3DMX`Lt0(uqAs30sehVI(86Qfc;h=Wh|C**1+3#%c)383&(%|{m?zez*NdcMD zKKWG!Y`+uCVvL3m8XiWWCGAa@PVhc--TsgqglpsrO#7o9#MFl)(6#s9{lNlFu@0>4 zl(xh{e(axs*M0QH=_xMA1oPHqWcZuS!jXnaE;puIe-Eg1!M~L2KhFeSKv%V!7(;ti z${A|&p)mf#LQ_379!oWE-1oV(JMpoZ{QVH^8jGS*!RuFgL}irs(~Hm@>rl7rKZG^~ zhCTNz2C*ouy4Wn<A-9`DFwk#EUx&9o4DyDiv9GHb=vi1w z*zAW8QDzp~`p}80oOaZqhI@2UON&1(nv%$(-OhhBljarFU?{D?ogY*PmFroPzMF5L zZi zFZu?4S4I;;cC7>U2!UyhB1UI;K)~BIe!XkT&dyaQd3o6vi2}baSasH?ZJx*>V;$K) zc4Upw!vc(F7cgU5OhQh|!ezNBVeb$38}}IgWF&K`qZ$FV>>wYsslKOA?X^ErJ)~Ga zsI}gQvm6p49JY3nS>?W1T$>AHDHwNEdhtLE4fwjxpb}z~8K@y~5nD{gp{?@0aRt^) zu}1wi)h;UJL#b2KW~(%R@aTX9xz&mk(AxF%51$(t7a?w*xnS^7!>ptBV}b{YCxtHr zfbZ?#jSc-8Tw^RAFsBlYVXnmmM4-RgpPg(1%D*Kj{+~LG;1hxxY!&27-&a+>R45Pu zgy)S9;=@lqG{vek3;Ae6V)0V`x3(a`0nN=AAYE9BvU>+GK4|W|k1%`tyB`E>2@&1r zpSB=;>>F01MYtWLOUQymaHk0%=$H^}5et?;)H8t$_FQ9YF=iqenxt3Yw_gjvT6bp@^f1BvzVH1vkjR%iORr6-;TCJ%8FvKwr3 zOYjX=j_;%0x)(XVYvD~(H+tk5W*!$Tgthv1sFcf-;lTs6OFe$?U|iW29mxYeX_spi zF`R`QcqF*Q?>~a}u_!-CJ@l`OC@exBn($NXY%?(v}hqo2=iRf@k9CQDC^U7180h4|Gy& zh0KLRz)mfzO}|qYMlW3fd8%c6!MF4Qayr8^qhsK8#qPY+3_`e`X0My>H zX}&)GKZ2J3mIs({&^SumPa*dI>R`z552diOfYpCyL;s^kC5MNyIv6IE;s40_{I};s zR)bIeOd-U%A?PL9$IsTZ) zP*%e^7yQ4Z`~Dl!kZVGzABacww^s5W%GLiU+JE8a|0vr3ZaDu((f*6({*R*l7t``T z9__ynI%6i*d($bo{clV5-Luds@8aFKm1U46Ow6gJZBweC^&Wb z?(9B5b~=I9JCE}3(MWP$|E1Bh%Er$8SUxA!D!W+ymL*tI1Px=M!PEl!(uLBg^Bkhi zXBHiSII%V5gbia>4f}U@5|VkX^hKXhWA9r`IBWTPN~%1@XLEaj0BP|Cu2GcSg*SM6 z?vj!7?3E+x|1@?%Jtj1`OxgDG+SJidVcvvnGIi7n7qy!eJ6m@jR9l?KB^87^{s9r` z!4FkFBg4=~EP|{~E+b9~k^$Q=;Atqw6iz#s3OPk;r0Y_@*go7Jwk$31RUF+Kp)PUz za(+Qv`ea~df7@#dv-<2BSELD0avC7O_3s)`4C-l8Iz3G~Q)U@eYthSin*Q(Jm39+z z9o!x8ck9I>j04t;TFQ8n&7!qm+<$^H+JqFR4)DMDbjR8Q4gi&Y~ONLTwck4F~jl8RQ#DVu)k{yzU8{|o1g$@_mM zC%#dt9qMT#TWHPp~_89_Tfx@uMyUKQ*zvVUGw$b0t9-op;T13oG({fy=W*} zA2pEm@cf)JmsCJk3KmUMzD8F**vZ{&$i>(hS!0FX zi3NHWL@4vmu+*5ee;uuz+Q`~`FBiyZ79bC^XtGv=)IuTSAg;sLfy<<`&)2!HXKT8# z0vZkWylae)&=?tCyYrgi+M>x8_LM-1Ys&Nxxi)PTuKlp(i#6W+g|R+zL>{}+bv`yjvCN= zI28Eq9ACLZ{2>h6(xY)7L_Cvxpjn|Ad+>YE8V|_s>c$1tWp^|bwGc2{AE$H?fx`Tl z8bvM^T^UkW{L_u2n(GH4u^QKM-HYS8=x(tvSSKav?H*n#V%EM{WF`EGjD2^vf6L^H zo#`6+r23yyH@XaYJI?7v=DOf{L4_O1(9#p6m`aCcGq9(IcqZNCWG+IdEJ zitrgs*4Z_&y3DiR(c5T2@}1C^H3vvdn-67~XS7K+t5pyuu|ISsYX7Enq~FGv?VTk% z^J7ibViT@|Z>P%jT-vePdR_Wm9!RZOoLL4$OARy9vkeLRyqz+n&_D2@U>Q0?3eC$i0|DDN*Z{CsDgT9V?swhXi531D zlS<+1aTR@2KvLFuJ#ahgW`F3Fxyl!>66#oLft6~i(X>mS`EjP6)1~B<9I_A>_aFQ= zzDszDfNwPNr%BK&L~hInk9HXg-9I@9=I&-|&5AS@Jv}2X9YcxFx9N<_VNy{v%0Rt1 z7+%PRj3Yv}yhDVu-GR{}{ipK|E90|5JX-f=Hpq)9&L^yRK;vsibr}xS$o+Q{BK@jE zy4m&QIY>d_A-!o&dsxSoTn;RU(VX6nCekX3Bz2oc0E`B5dzwz- z$GvzyXu13uJbrM9v`Tr3=7VFjNJ;sh!IN7!HzKF10Nxc6kb4*azVbY6kD3t_%U>O7 zt}`%0Q0Mt}BQL8ar=wC+n0Sowp6)KLv8p|qeduigi?2mh^CYvvJM(z%t}ha` zb~Z_7BI_k!9)rPr6k>oH zLtlq(jkR&;6_3VU$m^t)HZhwbq_$}r#i`%!tF6^Ws?0^Hx*ROI%_ULiZJa63SW%m5 zR4dYpcx!qiQuB6hNH}DKMtEnZR2NpQE3&4Y%fEH_MO#v+0q&z1e6^wf(D^!F3~_1w_|;9Sf$N588?q3|z$^4Y|2=HMsc}z$@s!2BfO?dx&O3-3^w}J>viXo8CI1 zf(8e>^I#v10)fa>y1S#ax3gn61d)u*8+_CoDJ`!8wKhn{7Qzn_b`X2LkM;SCJ}Kgr z;G6L;I`oefaMBHVvoyA!9EtZ|Z8-@ttC!bDlu@_FPu4l$2$}MuBipky-qj^LFMXom zpG}Fbm36qsBGJAo7`dI<-4!hMjj#eIuixan$ebImN}b7l`c4<)f|bVYwR|kw3dUQ{ zrI8A!86l^bt6R`al+L&HUGDuV>cPDb1y!VreCNI4;1SmvpmPWdVM^vffgZl#qEr{%AV7cp-A!6S`#}hW+4eHZ2k^8$@YN(vpIue^VE@@i zH?k8Uuy61r_A*W44oo}^-z0X*&dYP}%457@bo==>2Gw!Hiyq?CQ&i3~T|0&hraK-P znf0WY3!Uc7i)cmeKONRelKxWQr0h^jnTM2bO1a;iiWBs@Ydiu1Mgf6YdUNRZmw}Tn zFE|G3=C%heXR0iV{X587GNoCXDeI)d0a6zb zm!^$Q^`+4G7_2Mrv;7^&k12I0T`k3kiOhC@%L*%t3dezgMRqZ7jYEX~3x`4jzd3ik z$}aesX&fxr!Z^Wde00k`fO$=KC##?eV1j5JTfX)+h z^*INYEy`kpqG{CQcpDoQ2Xo+^H30+VZG(7=p*pI;BRZ?7$)rgCY2hsgd0+d?nVQp* z7N)!M;sXoY?f1ZFR2dEpi@EW6q0i@_LjpWKOBS(T@N|^Ek#PpYijr={O52+if@ys@ zrATr&z`Jf0(_~GwMA3|^6#C<9ZGofujWNJnT~u{Q=p!oH2DYBmf=g;4m(q$qDEqXx z?hw*!&KylI89hyh;Y%04ixoe|%FiVL7VzMlsnwMhlzUp9+yAZlcu5d9dh-ByS5)+fD#!wh2TSkBZ&`ZR=c8wAGw>zM&ur z-X4V{acn7rtl$Q2XH`y*3X>9)%y2J;u!U}Y?>o237WVH*`p;1{Pqy61fuaXs{rtCA zwn-?6u|9E@5o$cF1Wvp@q(0|yBTT2*@aKX97vTTXjcnht1_EffAybjspRHWbyFkR80MB;}4Ob*5j)x+1x;EWW7*I~_=GSp57> z@d1C)xg`4Z63g`diU!ocFDY_=$BFYs{he6yj5mu8s+X`&agOhO-Ha!pdBr6#%asA7 z;kG`v1i)!gZ1~(@w8n}@U>7h0$JD{6Q}y9G`>hVXp}C*DjAD}0Ty)c*MzW8t%|JI` z19%#<@8z61aK3Y&0i9J|1;)tv@A{VLb^y1FBj_E$HLF$?(smtk>bFQvu|q!^$y@;< ziliHtw_U_!b{vXia0X}&2c&oFsAr@fY)9Pe4goB}$NB1aY?2LoPfMu3a7hNQ%=$4+%zA%?;eSW~ANOETW^1f~+8JLXsaxjOtangOB_d>r>yXGlK?g*rScWWNa zt#&i)wz*O1{e&%dgibvw`|UUanG>@LSHuP8@^LqP&I=75VD(Q z;w(OaeWiQMR0ZgXQ{YiVgWfe3Es>fxuk>w!|y&Wk! z+mr0s&MNcuBL0VXvp^P0qW7^}?k!W()&D6JI${j!C?ogy7R$iganP~k@Fo|8iSxVUv6m*oUt=W8O%767ej!fBQ^m~ zeFx}=B=cl?+l42$s8qXzwTHHN(Gfjg14&df6CLfxc(cDwt6lI8T@=yDdtq6wHYl7n z{2a7HUwc2w2O9uEtzI|Jg)VXniUE{zs~mvibjwcjO-y7Cf%9?qK5>IAH>A5BJI-L6 zNyVe&ipi(&wmokI`h6g~TRi~~ufpmq9O1ea(RdA9C|P(a4{oTh`+HTq*l$`6I@K_5 z%{e-rwirNex$U8!X9o_OWacIS@1gzEYnNUFAFQBaUb-l<@#{w9Rx%_Kz}4y!@y-0{ z(Tq|Jzwy4hp2rD&0K|GEXI;Q+-!U9eRk7Go;kc*0cyh#Q33+z#EAgX)*++hGQ7GV) z9lR(~32FT{lAI8HuMF)Z&PNS|SidVtphEZHP^lpl^<5Jn%q%Y4D|0yh zg0t^dGPm$<>8`SKa+NrIAnAbM)_f$HK5-5W?i+Rwz($;4@7&VcRTSi+nx))Rm$0z-D=yTjCDnX`x|b zpLJjypk{z}IQxqS3))R*xE%s= z=`LGs^6q8k<6}R2yep%R-S9UbeDYlr@R84lji;UF1b2(43B5a$ip{Q z*P&N#?a~*W=@e{ulH^y46o{r8y3O-W#na>LYJs|8`P;cZ5rd{s)9X@5et@@6GUWQ^ zYAs+d6z;4OFPCwI^t@M3kG21km%gO@_1&do){ix7nTxmYj<#bUJSDmdqWLG2AmyV? z@BOEfE$Lc{@vL?`2fsGEd%QT#dub2*V_ul%4BVNwDa>E#fMNLDTo`RVB>5IJSo$Lp zC|0}z=6}hblYJhN3%@(IDVXqSBw9Ta+%MJ`s{}zT{DXQ#bniiS5MccPqg{1r4d~5E zjyfP8_n)|3m2L>Ke&4*28#$ZYEgv;c3O*Uyla`}1_%*m;q4c(+vfrJZcsjF+K(I7G zKT%+VvW49k7qaROv;nc6q3!ZFOK)Ao!h_`#_~eUkpDaftTfsN$aj#XYetR)rhT5X+ zwJp3BM&OfQ#CoSJQLTd1BCqDX#gN)CMeDuv^;`VF(NXHVu6;UT<&rGNGfr4veV%a) zj}r|%@b?2Xt)(2u2M;bi*+Yg2t8wB$+W1Y?+8ZS9ONlMn3-?$PbsO?R4YF)NFM5tr zlUvX_AaZ6?2kwvL@jgt?Dkgt<`e=VM@a?M=ln-QZVx7<2fzk&K^sjmP71jiJT$;Fx z4sY7lS*CB@!S>){&#Pv>W;dFdJNd@EO0Yet6XZ4xwIAd9N$)z{-oVkdTIYc0catyb zv5YO*+E=Nx_8MAxgQl>~+0;qV6s(;;H#OE@T{O4QS?!SGh??*7-|Pq}1gP10Z)>>7 zb||t4)W7nSs{s2%Uwn@L&V5f%)khM^n$SGkplml@z!SeycC}=1c?3vm8OJT(eFk!` zp1v;u)|czMY!rJ!C&oF!f(zIE1}WFq5SvWxt8uTze!%G)-KKJq(l2u0Xw)@0j=SR- z0eOt3tAsSrb(-Vr$o=mffOy9KCh*9D?uHY=_xXcui-pMjkALRsq4B7TetM!Z7@RL+ zekGhX+oFPT&&`!Lv0;~5+7hvu)pAc?tjm^?RDDcDZ=17l?-m>yVQRUvo2bxi^^(%1 zA7Xx>+geaC0)g1@)4?O_ixErk36V-Yee)C-eMISqEN4K}v}fo`JfU3{-wdl=)*9V* zg1B}hMn+nohL0S&PpmCv^@&Xd z*gBI$Xf@hLSISK~@K;So>*zqcS@X~IkY?5Jv0Z;H?QT?Cuyl)0tgC4fIg%|0UCGrF z@VoA~h3%Gntz$b^Vo*0xDwC0V-Hy25@xZ}fH<@fJ>d#P+E@jai3Ks(3q9$)vWS-Qok(T)Ey z6{k0<%wdC}2^)mkfmiVQTV0LscN47a0wUq}aB_YqY_K$~@Cl*>n4cu<>y*jOn0l}d zYEKjxCo{PC0gQk_VUao8r!+#v(*qf2D`&GAkRnZ8==Rja091yn?l{voIu#^pJ=w-% zr1J_}VI5~?M*UYf$7Z7+rW(lb3b?Poc)V2)`lzS9rgQwO-S;ia7o78NW%BAVnZMdq zZ>QtWk-b1s9;$qHr-TumI5BH>`*|?c28D=<=mmCX0t-mRdqkn{O!Fk}@MK^vcb&(t zGQ5KW@J!0}H)H{DGOinK=gN0-PXTfWpAl6hgAqq2?L^HS8CZ50>9bL;?@C>}^#k&Q zby-IBItutxe;28YD|!2Ko4@rb$7KBYs*w3 z#wI{ecAz6i9;B_xn3?5yl6*dmu{s5I#KeES@4r`Rs};KLqc%$VIzc}nC^J>&&FnP; zsw=2~&d@!a)X$ujzUbB?G>j-;HDV5e=uX!YW>&7ulJjU-9Af@*Fdexe_l=yn25)(mWHcAu3fJ$l7CaVngF_ z($?(Dmp3pehU}LHl(AFld+2YZ3Vn{J(DvOrihGK?vqA(8xo%huxBn^ zY<`cZSF5Iz$F}beWv%&h=~{%-DpoH;*ienW0FCql#hY(Dz|12mjh_WP-`*D!F6giM z)fV|N&B))SFTV&_TO+V{o#nTt6xOn<>E8^N36lp8&8gX_-Ls;M=;IDZ-n2J=br^~x zb1>wK+-{T+YmG6_S+UvEe4m&+ccH+4Pgd{iTH$Ks`ov4{h5h1v?eV28awPk^g7VC8 z52DQ^c0+y|n@U$IsU$0>uhmX1><%79Bw_6$=s$)}l*`-4aU9%l)qV!g`tSh|zhuf0 zR%VG#-ZkFkHAg{}@lN`imC?A1dW{{vI*2aZWIS3t>Km>3a^5=ylmDfNt;{c@*hm(7 z*51cNYbqimR>Ya7ki6F!%+?5jX9T>E{peyr!4zUAvgTP-M$>fvDolwH&!eiD83WHU z0Fy1^r|z+8#Tz#e3_{iV+3fw3P~&AByu zm$`IQKe@bTr4R+hi%KCypp!D9aDJ5myp&qghpPtD8WZ1HUlBU=!3^zmTLkrSeCt5B ze^!l;T|C42YPv{5nH%ZBNkJHW zdWVX?H`b?Th?dex_7>ybmGcrWL25F^u(rU|#hwa%9@=G0nm@07XU^|QlctA@0Ven5 z6>tloYqzQnD84I+@IG0?6V1gPv~0S?syeoT2b~f=-J*AQYu;@mq^IlAig=S~h2Y>2 zi0Eov1G+KTA+oaU$n!-jK)Tr9WSOxsV!~*!OxjMni$fGPe}Axhs0X0Uo<|EhD&ON?yPNV{B$V$mX#?Z{Ah@N zTj|!w7{AoB%4dC9yC&IF`F#U33En3DE#7G5tiMEeCNbtyQum5Wr!^cBM!b%`Xx*9* z;4>Y)8>6}Zrk0!&RJiTav|H!6TQNcssxTGz7Jor1!`D|a2@U05_0Bo3oQ0W5(_hC= z4Q*Cru=m=rVIvlW*HTbn0rt}yx!3SoGEBGNdCi;K0(I#ve$Ji&N0YT9W=+h-=5f#S z#21>gg9uLS%@SicJ+HKS_^%|5mc|wR(z2+q76&GeB$u7x&&y-Td0G{bZT+IL*loUV z&ydRoTTy@2)9BOL9`2YZ=flSu8f0N+JAy=2%+TqvzUTO^RkLTKp&3iAd*KMXN8o{o z4bu};Sxt8vy_*7T@hhZRw(r!9FF-k>_-(DBM6%bw`E=dEJHB^B{hV7%eak?S9yC;3 zX}L?THO&!LW{lLT<$|H&^0vBe&eoH}_Z96)@rrxOM2(Gj6jVPb{WDWI$DqrT@;2opDno+Aaaq~?-akax>({`o%#&PV^1aHAtEm4 z+8FLFuQ+w9A~V*itUdT>D}R*Ul7xjq2cZFHm6g(dud-upBik=zCZ znNhqk>v)%ijy(^fQ=%X%v(<=z7<1K~p@>yQHKIPZi^yv}%6r)}LxB1mO)>A6QQ@yw ze{~_1`Y?Jxebw_HO(1|(F{K%>mT6C1-K!WReZLtzBQv{;gEtFoT9PpSHR}N`B{ey@ntzW_V&Wv1 zEXQK9rEWzy@W2JY;}yEzMkM9%I5>LTQm;WH_o7y(-=a*8dk z@X0v!{Fj_*Z}PgOo3jdBH>4S;N=DI6a3^<3)V_0`2~}*NHp^ILSkrzRuWdQ=T+?nD z`u^EDA=I!b!c#I}H=P<$^}BI2sL|p8#oS`jS_o-Z0C@ zq`&MXqObnffC55OJz1Xi+KrDLm-5Cbg85DkMpzk@8Tt5YK{l%R-1dCGt>q(zTP&e4 zl$bcZIV{5Q-jDW9@id-I_DD^k{=6AsUe##(Q=svcaoLm4CX=H?ROR+ zg;1=xe>EvjWkafkk>gJY`}C|&`et@slehQ@-k9Vdtx0cJ2&SS~YkS!Cycj#obDFxo zSh0yP`aD%#*#YF~dLj`OJX)YgF^*8OEbp8K@#%hCFtsNmd zo2fRvpole{SG=BB-mvvQT%Ny?*dSbG+fb)YNP@H5-Sa*D9;s z-EAwWnq@AC`CUTUoa`CHLP7L{1tn_7oGxbZfj)en|2$Z$JhsJ_?~Xio?|9-L@H{65 zXM=o%dVKmRQ4m`F{=W5%lz9eet7;2yQ;#VBta1msz7qGlVExo>R1+2>qxnp1xm|vt z3ab5PA~LZUmGONA@oN+D*MpTki%S@KOiG46qx*ds#+&Q2q{>{)ucJIhG8;~R%?RJ{ zOfa}IOR;-bRr0+2AVqw=6O;KmV^r9rb6By`UgeDiM--8{OX3LSWe6$V^9-7ywwMi(=R2{I>#!?kC~6DfQ1#WlPz zNGqqPL{c3Bj6Nw5Ky-Bwn{k2YuY=|C6?qxl#Hi6vG8qNz%+C(Z*uT`tiS=EG>4*59 z@COx6w#KWl8J%GU^-k3QdVWM~QcU^N^}W>z`9v$-t|o*&2=kQkwZVjcjZXAiUdrWE z-Br@(H1*pyc3due*l;D|)Fz*su_zIz5!WOpP;88Z2GR`P?%ITklq!w8n6i_bWN0^e zGOo*KRiKaZ|7;2}aj5Gds=?B({Q1TvwkI@0u#s{S)I{3(mp#yvA3nkPk-T^oHB-R4-=$q&;d}P3uRPIJj9Y+Jdi!lLNo^JD%Hr)N-;qE_P~iMsx<4yC7>%%GO#^kzB~IrOn*@j#>*t$Jmf1s&WfHlcuj~ z{@RTU#xAwG=6SwQYUU|tcHcVXKOT^#6(2?*Jc5z^F^p}sp*)iGFe=iQ0$W5cG3(0c#6#v;;0-lWeFc3^d(dTEr}vsd~TfD|AzQ5I7&F5#31)QdTHxnmfS~V zc)+udBa(#;LCO3q3?bKXV^+6b07lmp4dVea=Kc3NTMnCa?hLtsJs9d*9lqp8y6JN1 z6K`ep5~@iB&I{nf2VmWWODWB0#0&+{R$ubC;+oaXh5QH^58$MgcH7BMx)D!rCon?A&&nY+Hs^fPD+f$~~s2IY@O*Mu1-Pz>dB#s+QTsl2FUr zD%HUj6j&q&unL`#p?v3VD1t(_CsKWitKzQfLb|8D2LG26P|q;81#c!sl17Ri`SL=B%ab|9WllgP9W!FPEffa#Xh^0~BJXU>>UH?IhEmC*aK5WW)1x zFEjj88i{56f~Ocm4=X(@L%1uk@kgs3Y-4cngoY(Wn{#o+$qhMiaHuX$Dn#Ac6H4VbLXCF_AHh7B9 z0*v@U1{-}Z5x4#xf3~5?w*uKbPcr~>Hu+VR0A4_li-GY^e`n4*%MA1vhVW7q)98O)DLR+de9#SFmApkeR0+${{}eerJVAQ;0mUIU)VkP{^X$9lkMg zbm=G?*?JCvY*vZGxlT0h1v=Vr9~^r0AKMpze$M@HTn{VuD!#7u9fEH=ASt`FwmHsiAOlgu~Rl<^HPs!!CC0+ z=XB7I!-WB7RogH%`$OWEX9ufK#kYaq-bs~OU0tI87)&D{-t5A}j>9+V^rt8`@GbDU ziu#u$cO!&7&`6Ua%Wg)k?u;|qDTRvcfI>%-M15DGfR{NdBDTJy$C}`faw&5Z+M?zU zKKSpce#0&>*9Zt!}2C0P{ILWgValTIxFUT3eBr~zum-1b=V0-ch`nv9Ze+q61wL3YzW* zbLoH8)N;q>v$LjsO^m|r@?=pqQxyDZX;XZo+)$x_H!f6OD^CyCva;xn-Uq}^qqS*& zI{qt!McEB*i9B&9cK5|878za(o(lYP>|+H2$~Uy&up`ka@ad*g6q77J62&UP*NPL$ zYHf$2GjrU=W;GNb@ip#i+11Hccx(o=IU`Kp!LT9H%f8t}TXFFOVAZg?GZ70?ECWyOzpUlpxuA<3BwGC|>S$AWFULGTshc z_=eVoxZ1DSo|jO&RE&xJOtkej{IvX_^EB0)F!z6h8*{=B6WCm7(D*uI5Q8J<;ym?r zS1)C3>eoZNpJ)ZiG7XTX45qckEI9aZHpNTvbnT4LfX$k=*RV4$?{@ODliD_qTRfa0bXx%BzeSXVBbwPIdb8 zt+^(~-BMU{W;kCXBJ-bLwA>FaZFd*=$O?UlGB?LKIPojbI~hiPK1l-1P6EBT6pa3r z)=HHyyp*U{jAlFfy9?m*?Q<=hw-|hw5EQM(h;-Lt<)`Z}j&QYd6vTcZZKNlYF~n3L zVBaH9rs)Uc7tQuJPw)V`~BOmWnkNznj$ z|6HJOn9*Hjc1Jbx4~1Y4hiTr&H^gL9)VUG z;Jq+Qr$mO_5scn1&ngLdyo)~uMN{G4nUhNOgwrdbgmdG*u8iH#8OWUnpfKtOP@Gd$ zf!=fM?Zeu%3nq3rY3a+;L^MqVbSbG9Ra^r-1PJ=2^^#kl$mThUYm>XnldQx=R}0-QwRsDbS2{q ziwsY2Tr(5+JeJ-5(yycWlecX$Jx^Q3+N(r>kT6kx2zn2NMU{KA=**&Zu}`h*0&QQAJkvo|P-k3zv{)7Uz}5KGm? zSVb!Hb)b*veO76)Rr>UtFNGGIpqH+Tf~rXwX-|m=MX_4*#M_=v4H4IxT1k=rik*kX z;p4;w-ed=SAeZ%sBYM-g-}No!b@k@Xc4QfR`f~O2RWEf*jUO!j6RkG26^WNr@|LlR zdJD(Lb;S`4A8&ba>T_AUar%yEo6pXvB`bE{eeXOfg(t*HGphie!6`IyP0 z-EAPhbT!0yZi&dMqa%+&E!;pqdffnUkNiwBq~K%l&)s;LyaEU4eXx@n&{cyQr-f77 z0{9-eXy0pjnYTg@Xr9ZWrHjzik;S$1#Fyw~j_`y_dOTy?dsi?u!NW%itS}Nb{+Ifb zh^2S=A+Dh-3LvWc7oHUu|B})Xd&d`3n>26{ABaO6nwzDB8!2?!_FlWlsY z_oZ9o!B^xmt)6$c)+Jp(02lKiU&N;G8g9L&%2ieE&eAJ%xoD8GM`=m}Uc0cQ!kLuY z3l7bsGx3TaM1Gl61v6 z^~!GjMCXXow~#EVqUcD;=>bn%pOWqPG^XP&k_vujvjs7tcCIL?I7AY?C+*eFv2*x3 z?aiAZhdu4{tD)LLG_oj;rq)Gjy1rFwHWvc<&HGkXYLidS3aiN9#G&f!k4*4&{#(@5 z;LVWQ#Ws+_ubh973ALfvSWsPQ%rBpUehv~zgtBqyfMg7Zu?f= zCC92>44Vu{KX!?y%240gIa)tHjXk**QM*Bn(M+5UwVIb}5@;vbx?z6@9vW%gItrXv zKikr(iO~M+?X>llu|7ufgk2qK>udxT?($h(qoqlt+iZ(Y=V*U5im9oo@d9)~=FbD@!YI0)g@M2)C+kaH?CM&IBqT@pz_}+8nnBLN7$=7WRk39<9bVj@ZKD9{ZvG zaY0iSVrjvMQT%%77h+aqDS=#e{9?`tQbAY-IJUq&Y+xdtIfr@@{SGjE`Jv0y!Oc_l zl>h?b_>bCRPRy7x&6LjZ($co#c%*IF!3;s>%6^|!8~fW%`PMfO^CZ*S>Z60Ez$i_m zk};pT(beHU&VZS!>?Awh-hD^i3PS;xas`Xn_7!YfwKW8(Tg}>pl0o*3y&K2GDL>zY zsN)^c=4|8#o@FT$<`1%=7Q`=7EtuhP;yTlF!_cZ#B6=dfeRXNZ>7RS8R<)9qPC_hN zbNwZc(=)4u)t--oM5}X;d2fE($f{E@#5WJWXEF8BD?SyrA| zaA(3zL)f_FB~}F;oR9;829e<6x@Y(oxJ^rp%xWj9FZ9S1m7N*pcI=98Sl4-Y zl!Uw$3IPNpVA%u|#Rk`p=J#flD6F+P&R4<#+(%Stw6I0YoUdEj32xk`m3b#z;I}Oy zS}g8b2Fd~ZwiQUNB|8P;%-&aP@x~rxWMmbG`E80HA&gBMa}>e42`Fs-5m<(>hMn#X zmQcISFXM;cmkQrGp3q7n)_N1G+NX_8?*tSb1aDjzs+?8`y#FZp1&|6&rXHLz`fUt8 zWK-)r;Fw^VJc8%oaO!YS1l=}ee=3YH1e>`#F=;LBeCbjdeo8h+0j~+;9+HAg`m2~i zNhkOgyRtWQUucmdUBt7opwDtXDXm_O#(og`D2qaFJs$0HixZg0x)G9O%rrj`E7pm+ z@XB4S3ny4jv-xITL%(mAoQ-7wKA}}l)(?n#Fr(6o^S3Bz?*yMYIzaL9%lV6B!No1c z%3E4)S-YY=!y|A%*80r9z{T&9xsd;sX0f7_Zw01%7KRq;Db)_eu_05t5k92*vwaH6 zTs8bQ%lb+sw84O4a>qOHW%^Eb$_K7B0wrNN!p-&{OO)U|4SW!@^i@c$v7OZCf`bKj zqZNjy^*??U5K@u-&htQtl#6UJ`F(lX^2s~JByYuv!#sRL)oeTSBe0N*h?D|0z0o=}ocAam)MXp>4f@WnFB!*P zTEnwg#og_~0!L)%?}wh_L*^q++PwchyG4+n4#te^7MGv!Gt{qVEvHxc&)9kYW>Q5b zv7z!adAvID*8|L*!OLYQ@^+Q@9$cUF;ry#@>Z*g)y@ z*+HsLlDpRf5&Rp29z-*aqxn{p_`IuVT#NJB~ZHWXT-SKf8!$lBpwihB@u01BrXB}A9y)RZd| zd2kv;P~{`eM%604HB!2TTES6yG>&(jEZ$asAo@`{IIFZrs5CzZe!3p=S}R;h;mL)* z_Ywi7AB8Ze!!yE1mp+T@g{(a_g^3B5%!?bb4U^fC{Cfs6a}h={E=%IZdR`MlQMz`^ z|A(!246JNhx`t!hwr$&X$2L3e*tTukw$-t1cWftL&b{|seDCvf@3nudHTRsgMvXD5 zD)N@#-s^JDglYzTSJ;{1hi-3OB~aZI@%}!-xrx=ZQcd*Q$8?hIP#fROlK}{Pqq-PN z-oZPk%0_PdW@i~Mzcea@(b(j2L2(>fEX&l%qMCGPaBoRU{l}k()zRM{P|Nnfn=`<` zAwk5R3VK%D_u|;3py+lluPrCo2y&{}vaQ!wF^USRfUW%gt>gho#{#Me|JGK1qWirT zpD{pw0Zo2A26iQtSNiO!_Z$BD@aT|(dK&*nhyaJA$mzZm1mH3GCwTn+cX}E9?#Y4U zE;%BU|BK@N*B{|R;rF4wX#0iB#fFFW{VsLk0-RSnRsS5ePkZ3C7%T}yMOwBasm_2x z|E?!`M!Kj*|27TP928uvxXak`Ul99BpTLj|gICjyO~qU9wr%3j^j7p79o@>yTRoPxT(@rH!mO0bad7c#Re3n_}RR0p@b!;`$h| z&K>TfcA$kBtW1cIfP@WR?_#qY)u;kTn3zIJE!0*Dncxf>9aOd-mY45a5IUdF(GGf%x0V%_bB6#vcFK!2gr7|MP=y zk}stACmH&eMP;&QAkk%?VE!|F)9Q*L`DX)*q0mxx)u?T*5z@qmEZrw@gMdQ>ObpDt z2Gv-1hN2B(=a^DasS3A8O4swWX5yefx1PLc7N?*?01XZcku=#S@?an!Fo;36SM6e+ z(k-B}VqaN3#zuRXBcqOtTId%`^(|D_&8V;v{;E$C`jib(wL=<=T{{|tNo^W=6{28!;`KA^?xCSX-<46upfS-bz(%#ZMK}2vbZ0dcaqhaYTZ_9ZJyHnotzkHuYyGvy zY6+}7E(=MA#2?#g2*^7UzQ_I+XKci;{NS}W0@W(3&KTbgY%F(0db(oI%Fmj~@a*Z) zveYjpj_OPg{4wx)(lhTS4GH@2J{9o`D|ccZDxj`RmJ<)=4&lkUne+^k-cdaL{@W(w zJOm2voajmie#U69^eeaVR91|yIj}*P!YS&8TK51F)k^L=MyYJL;2hW#)}^;yJCPH2 zzjr9D`F|&5e|zEn3aU~-?02Ch)?xphaQ@H2{Lcrw5X9K?#U@^gmte{Lwk~z;gAm39 zBZkA(vZLQo6jb#q%fuBv5>|`I#=!7K;j{c!+d?^+ zKGQJt(z|iq`8%cc*MMQA^cWj@yh;24z1og5Swt)97&Fe`CL-eLF+oxi1d1k6q@RRSz^?I|DxgkR%Tg2{!mFP5MFX~=JDGMmo;x) zrn@z>#to-4X_M6$Z*}w8sr5jqe#6Rm%d)V)U}Z0SmkySFQ74$)AHQzhOB>ej!fNbt zMI~ME6h7~hGqf$6xeXxJhHb-l8Z-R3 zFgh+}Rqgps##*$GZM{z=b05`ZV<1r__{Hy*l*J?qglAVCvF^nptlQju8qF1R zXJ*O_w(&WMS34!&_XQ6gY&1M)m#Z4{^i_|(swbJ>I$Q&q;4h4`V97Z)6cXq#iW(GZ zJ|ise2!VDLS8=1!{oh)&6fN^k0X-FB&mp99u)7;^)l{{wM>7Ib*tS6bw)t)c@V|on&J+Q|0VC387AGC-aN)?K z33VSu*fze@%=+B^>x?E!GPimq;W;aI-wTx%)$YNhp%!bMdTzuwhFm zBOu4a6H(n}ePosE$E&Ld_|wQk`Z&eBp1`AzA37V|5P~wFYRy^%I38XwA|Thjvll=Y z9!Z9mDIh^6*G=Stbj0UEgYJ13mdA2WbSR{-g4d%FO4^mUpv&t3-VLI((@{vmFxiR~ zPY=45&am)u<;acWoK9QkZ#eJEOpqACvjMI9MvWjn)1lg&zAD@t4r@R^VlHP8 z5n~yQmYz56eok1ioA$^dwMpxBjWve&)Ihm}LBz%Ms2&8|5VG`;$pJ}7rq*@BE zydRZ*v!6a)n%zrI`@RVl?Otx|U%%P1CY+~D_MiEEx8mq$g5^dkwVZOFR3m;h9BqLC zW;9*llR4}(&wCdGc|mQqWO(dXY1=m1Rk?bE zXL~vD&k)FsA&|y-)4rY<(NTSon~V+vhH168ekqY4SU>7jMa$AE-(6QKrs!j5|6$Kc zY9HLstt0P0ceRsV81-DUb6Gz~LF#-mgFd%-?FU>@n}T^VH}&^j%%YmS3KVtWja!?_ zAuz$2*3{CZziIO8ec~PD0L;AA?_rGYxDfYGx;}i%KkmOrHB1&Sn+!u^(%Bw{?7&O+k< zZHNVuag|ZRiyyt{QD0<@b$0{ofhwx=homA@sD!N}DxApkAF<#B#7`n{Vks_G3#vFB zM-#lcY9)lm9ku)OK4nwF12`!qmp>jv*kGjNCi{b}crGG2`{)5m*#PRBM7wkqc2SS2 zUgvIeBr9H_&Nsk(@KCUOox;SA3H(yp&@?c)=2h{ZG5p#`6F()`Jw1e`CD`rjiB##Xz4T*^CCz%a`l4?mM}a z^^S0NeE~k3`?NIy>%qS57YAKCaL46Spb)^x96dxIpF0NDPl30Ug4&f5E0PFoHj5ni zy(4+yZFgWSpH~WZ6rGUljqpvo#XUuS=_oC`OHEAnb}K~A1^Bds?A8!HlE#)vcO%@n zr$V9>{kA~M3lLu^s`?B#6S3oH&Hec{v^KQA@_!l44>Te$Ef8OULka!9d$G!;E3q8{ zt#*jR!z2lglt@eiXs$tX13!{at7_JrTa(<9VS!}ng6oZD3T@riKjM@ikt@HD9FN6T zF-yW_J#jH-dFuwO6)J>t&!N7qBgv{ZgF*%C{j@ixfdnw&qM7_U;sos8HE9;TLV-i% z@+K?^<=USkKgs2W;j%Vk727_IvqB`~(u;|-rK4oKecNj+9YC`vI| zK&_5F^yPmeCN8X99*wOs^yg%UmBqYhtUEf?HO&`?j@yu`2F$+qruJGAu3LRj98Ie0o?AIEeO5^4zu4=y3lbmMe z2K*ESo}w0;P<335tQ8TUzEp(p0nFd=mY)ph{rTRAON&SKpI@Dz0M5GzHCZv>Z~+om zd0>V6)q0JS-r#G1bb8)`{H_3$uj2O{cvq}x=>lEphTET!M?gxa+fAiI;XIOBqtytN zxfbu^L;@&oswv_wUO4|Sz0pH1=i~O9>(u0UgA?ev-nFBnzhn|D_%$AkpQ*T34BKn$ zv*YapA_Ea$DY?nG<3=E}Ga{9XC@MOK7R3PEGg|g+k_b0k&h$>6Qdx}JMxc3!^rN=> z<)-IOk?II)2k6izVheD0#!Z!&|BfcSdLd2}0=6*`4bk{& z>9>j_;D{VbZu<*Z`@@WZ_5pA`ED5QevMgl1<5xgERv%Y3*k#w~gPH(9@bkSYnhO4+ zuHdK7T^@Z!U&{h~CZ4ZtZvhF^opOo_o{zjEN6E;;5jX+^6GoTSSo!Fxr*;|f_^}0sV*=g7M9Ens`1|nEMKF9itprwODEGrU)*M%9{Yei zI9M=Gc-;qB)=YS)pX9%u5W+)urB+#`Q3z6u%`1!9UWJYifl_|PrJmn}rrL@Zh7Bju zVkda$nTN!V+T~Unb8|LvUFNhBKi84Zs9}J$ArCy=(`uSA!e>nFmihPuE44?r$9dH0 zdILhh;{v(3rA;}oyAXQ*$dN}M@-duacRxS}*q&=T{jlSBS2^AVoNS@s{$gbM)RHy& z1Ll%LAi~ck(!5K+s;yQw;&c1p^fAjyt6f9q1j~aZ3A0%Qbd*zdr-;&o>!}Cs_34xQ zRS&Zmd+8$@abItA@ z^C-8RYQ?MhE;3tqB_Ni<2I(F@!F5)Px1HF!Y7I~}VZ>lMKx8UP|1=PPvvk79_%})1 zYxaynQxsFy47YsZU5~v9-*#_D?$Z4SY^>|5CV4L8X9V^ieaqy^SvOZD4QEA%25i1l z(brT3wRs3xmnikEO=Y_JJ3$~wP~>j*yc>67-d%_%6L<$+F9U zVg1>3Mg~@v{JIw{%e2cU^UOcF8I35C2@7R5_)oY}L<4!q7C-MhuM>VU>F#34dsg;^ z4Ekpf`xc8Wu+H&sURgxnckW(V%FFB{N299+&!?W5PaI&W$I7+04g@7HROY=Q(8x;F z<2n4}rDdlKVvZ>a!wO2l;S8C6)t#U6=yys@dXrODjPaVld4N5NoDC>=jeG8AJ5`iU z-E?Q=6lnZXa}U85m1l=5~BdH(&Ry|O7bgiEDYO6U+jhAbq9jyioJi; z2dB{wFZMC|Ja`!L@1)H;5#&6l-#c-BNAPcqXCBb&q87_(R=v2Xb}l@ZG4zVcb-ukU zwmEx~%436*8I)3tU7dS!VL}nx0fd1|LJN{mn!lYt6pADgoI!=i>L+4q?!Rs{j#yT3 zvTVZe`CZ}UQ{_~EU2o2b=foz|6Q&KL9xPPtQm|L5qyfnEOELmZumL@xH*fwr%%aPx(A@+4SmpRdPEbS!EZ&7I5nqu(MiR7to zrLaRe#+JGxZEn(dv$sxJowU_N((o+=@{?4jgaG);V*1)7ly~m<1t`{ zps}d%5bNVg{aC&^l|3f*3htd+=YZ%BDwwpR^bHauXO7%~E@2m;&XC*8>SI3{Y@{af zh(BP9sTM)6jIxHR5?9RV>X}utGT^28)}pIM?jO^{L7;7!V=?fpbHpi0@okFSNq@QZ zyYe}~4(B&9Ph1Y1hv(k(oUVBIilCrq%RPu0{bak%Iu`~3B3L!;U&xr zW5q5ne1tu2|4zN;)BsZdn9?q;zvqt$*b`!Krkcsrg85$2lxl!xuR5RFyb=Y;;e0Nv z{<|pxd3D6nYNKy-WXbr>nIO_ihsZzZD*)vd-nQgS!Wk~;Y|j^Zc>0Ui5KJn)7rCvLD3iWtcNMCq$YZYfDc802Pjv6$y+ne-msgZG8oD_0N`℞?qV99C`SJ@G&q@^Y$E+Y14o|b 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%0ALp=e$GS5=81E)8~XKDc;(oCqPKP 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 diff --git a/docs/images/readme-1.png b/docs/images/readme-1.png deleted file mode 100644 index 40abf164543bb4798d30d15c17a0559c9833dc21..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 84348 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 Date: Tue, 30 Aug 2022 17:39:15 +0100 Subject: [PATCH 325/717] Adds documentation section filenames into the model. --- build.gradle | 2 +- docs/changelog.md | 4 ++++ .../structurizr/documentation/Section.java | 19 +++++++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 0a3dcc9c2..d98d7e68e 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.14.1' + version = '1.14.2' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index e6fa45ba8..83178a2c9 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.14.2 (unreleased to Maven Central) + +- Adds documentation section filenames into the model. + ## 1.14.1 (15th August 2022) - Enables `structurizr-core` to be used as a transitive dependency by consumers of `structurizr-client`. diff --git a/structurizr-core/src/com/structurizr/documentation/Section.java b/structurizr-core/src/com/structurizr/documentation/Section.java index 3a84e26c3..ea5cc0b42 100644 --- a/structurizr-core/src/com/structurizr/documentation/Section.java +++ b/structurizr-core/src/com/structurizr/documentation/Section.java @@ -5,6 +5,7 @@ */ public final class Section extends DocumentationContent { + private String filename; private int order; public Section() { @@ -16,6 +17,24 @@ public Section(String title, Format format, String content) { setContent(content); } + /** + * Gets the filename of this section. + * + * @return the filename, as a String + */ + public String getFilename() { + return filename; + } + + /** + * Sets the filename of this section (e.g. where this section was imported from). + * + * @param filename the filename, as a String + */ + public void setFilename(String filename) { + this.filename = filename; + } + public int getOrder() { return order; } From cfa3287022bfef6715a5e767dda642b553f9e527 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 31 Aug 2022 14:35:33 +0100 Subject: [PATCH 326/717] Adds support for custom elements on dynamic views. --- build.gradle | 2 +- docs/changelog.md | 3 +- .../src/com/structurizr/view/CustomView.java | 28 ++++++++++++ .../com/structurizr/view/DeploymentView.java | 28 ++++++++++++ .../src/com/structurizr/view/DynamicView.java | 45 +++++++++++++++++++ .../src/com/structurizr/view/StaticView.java | 33 ++++++++++++-- .../src/com/structurizr/view/View.java | 28 ------------ .../structurizr/view/DynamicViewTests.java | 4 +- 8 files changed, 135 insertions(+), 36 deletions(-) diff --git a/build.gradle b/build.gradle index d98d7e68e..d73b5e5f0 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.14.2' + version = '1.15.0' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 83178a2c9..673bfa458 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,8 +1,9 @@ # Changelog -## 1.14.2 (unreleased to Maven Central) +## 1.15.0 (unreleased to Maven Central) - Adds documentation section filenames into the model. +- Adds support for custom elements on dynamic views. ## 1.14.1 (15th August 2022) diff --git a/structurizr-core/src/com/structurizr/view/CustomView.java b/structurizr-core/src/com/structurizr/view/CustomView.java index 025a37b25..8a99a6017 100644 --- a/structurizr-core/src/com/structurizr/view/CustomView.java +++ b/structurizr-core/src/com/structurizr/view/CustomView.java @@ -136,6 +136,34 @@ void setAnimations(List animations) { } } + /** + * Adds the given custom element to this view, including relationships to/from that custom element. + * + * @param customElement the CustomElement to add + */ + public void add(@Nonnull CustomElement customElement) { + add(customElement, true); + } + + /** + * Adds the given custom element to this view. + * + * @param customElement the CustomElement to add + * @param addRelationships whether to add relationships to/from the custom element + */ + public void add(@Nonnull CustomElement customElement, boolean addRelationships) { + addElement(customElement, addRelationships); + } + + /** + * Removes the given custom element from this view. + * + * @param customElement the CustomElement to add + */ + public void remove(@Nonnull CustomElement customElement) { + removeElement(customElement); + } + /** * Adds the default set of elements to this view. */ diff --git a/structurizr-core/src/com/structurizr/view/DeploymentView.java b/structurizr-core/src/com/structurizr/view/DeploymentView.java index 425c38829..02580f0ec 100644 --- a/structurizr-core/src/com/structurizr/view/DeploymentView.java +++ b/structurizr-core/src/com/structurizr/view/DeploymentView.java @@ -451,4 +451,32 @@ void setAnimations(List animations) { } } + /** + * Adds the given custom element to this view, including relationships to/from that custom element. + * + * @param customElement the CustomElement to add + */ + public void add(@Nonnull CustomElement customElement) { + add(customElement, true); + } + + /** + * Adds the given custom element to this view. + * + * @param customElement the CustomElement to add + * @param addRelationships whether to add relationships to/from the custom element + */ + public void add(@Nonnull CustomElement customElement, boolean addRelationships) { + addElement(customElement, addRelationships); + } + + /** + * Removes the given custom element from this view. + * + * @param customElement the CustomElement to add + */ + public void remove(@Nonnull CustomElement customElement) { + removeElement(customElement); + } + } \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/view/DynamicView.java b/structurizr-core/src/com/structurizr/view/DynamicView.java index a8ca3eaec..14bec348b 100644 --- a/structurizr-core/src/com/structurizr/view/DynamicView.java +++ b/structurizr-core/src/com/structurizr/view/DynamicView.java @@ -100,6 +100,46 @@ public RelationshipView add(@Nonnull StaticStructureElement source, String descr } public RelationshipView add(@Nonnull StaticStructureElement source, String description, String technology, @Nonnull StaticStructureElement destination) { + return addRelationshipViaElements(source, description, technology, destination); + } + + public RelationshipView add(@Nonnull CustomElement source, @Nonnull StaticStructureElement destination) { + return add(source, "", destination); + } + + public RelationshipView add(@Nonnull CustomElement source, String description, @Nonnull StaticStructureElement destination) { + return add(source, description, "", destination); + } + + public RelationshipView add(@Nonnull CustomElement source, String description, String technology, @Nonnull StaticStructureElement destination) { + return addRelationshipViaElements(source, description, technology, destination); + } + + public RelationshipView add(@Nonnull StaticStructureElement source, @Nonnull CustomElement destination) { + return add(source, "", destination); + } + + public RelationshipView add(@Nonnull StaticStructureElement source, String description, @Nonnull CustomElement destination) { + return add(source, description, "", destination); + } + + public RelationshipView add(@Nonnull StaticStructureElement source, String description, String technology, @Nonnull CustomElement destination) { + return addRelationshipViaElements(source, description, technology, destination); + } + + public RelationshipView add(@Nonnull CustomElement source, @Nonnull CustomElement destination) { + return add(source, "", destination); + } + + public RelationshipView add(@Nonnull CustomElement source, String description, @Nonnull CustomElement destination) { + return add(source, description, "", destination); + } + + public RelationshipView add(@Nonnull CustomElement source, String description, String technology, @Nonnull CustomElement destination) { + return addRelationshipViaElements(source, description, technology, destination); + } + + private RelationshipView addRelationshipViaElements(@Nonnull Element source, String description, String technology, @Nonnull Element destination) { if (source == null) { throw new IllegalArgumentException("A source element must be specified."); } @@ -244,6 +284,11 @@ public void endParallelSequence(boolean endAllParallelSequencesAndContinueNumber @Override protected void checkElementCanBeAdded(Element elementToBeAdded) { + if (elementToBeAdded instanceof CustomElement) { + // all good + return; + } + if (!(elementToBeAdded instanceof StaticStructureElement)) { throw new ElementNotPermittedInViewException("Only people, software systems, containers and components can be added to dynamic views."); } diff --git a/structurizr-core/src/com/structurizr/view/StaticView.java b/structurizr-core/src/com/structurizr/view/StaticView.java index dd4947dae..b4199bfa5 100644 --- a/structurizr-core/src/com/structurizr/view/StaticView.java +++ b/structurizr-core/src/com/structurizr/view/StaticView.java @@ -1,9 +1,6 @@ package com.structurizr.view; -import com.structurizr.model.Element; -import com.structurizr.model.Person; -import com.structurizr.model.Relationship; -import com.structurizr.model.SoftwareSystem; +import com.structurizr.model.*; import javax.annotation.Nonnull; import java.util.ArrayList; @@ -240,4 +237,32 @@ void setAnimations(List animations) { } } + /** + * Adds the given custom element to this view, including relationships to/from that custom element. + * + * @param customElement the CustomElement to add + */ + public void add(@Nonnull CustomElement customElement) { + add(customElement, true); + } + + /** + * Adds the given custom element to this view. + * + * @param customElement the CustomElement to add + * @param addRelationships whether to add relationships to/from the custom element + */ + public void add(@Nonnull CustomElement customElement, boolean addRelationships) { + addElement(customElement, addRelationships); + } + + /** + * Removes the given custom element from this view. + * + * @param customElement the CustomElement to add + */ + public void remove(@Nonnull CustomElement customElement) { + removeElement(customElement); + } + } \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/view/View.java b/structurizr-core/src/com/structurizr/view/View.java index 419163a68..f12a6a08b 100644 --- a/structurizr-core/src/com/structurizr/view/View.java +++ b/structurizr-core/src/com/structurizr/view/View.java @@ -510,25 +510,6 @@ final void checkParentAndChildrenHaveNotAlreadyBeenAdded(StaticStructureElement } } - /** - * Adds the given custom element to this view, including relationships to/from that custom element. - * - * @param customElement the CustomElement to add - */ - public void add(@Nonnull CustomElement customElement) { - add(customElement, true); - } - - /** - * Adds the given custom element to this view. - * - * @param customElement the CustomElement to add - * @param addRelationships whether to add relationships to/from the custom element - */ - public void add(@Nonnull CustomElement customElement, boolean addRelationships) { - addElement(customElement, addRelationships); - } - protected void addNearestNeighbours(Element element, Class typeOfElement) { if (element == null) { return; @@ -562,13 +543,4 @@ protected void addNearestNeighbours(Element element, Class Date: Wed, 31 Aug 2022 14:59:53 +0100 Subject: [PATCH 327/717] Make Java builds sequential, to prevent client tests from failing when using the API at the same time. --- .github/workflows/gradle.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 5b5e99dbc..b449b38bd 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -16,6 +16,7 @@ jobs: strategy: matrix: java: [ '11', '17' ] + max-parallel: 1 steps: - uses: actions/checkout@v3 From c516d45483e08f6d60fb7886df6e2de8e818681d Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 13 Sep 2022 08:43:13 +0100 Subject: [PATCH 328/717] Updated to reflect release. --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 673bfa458..5356a1260 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 1.15.0 (unreleased to Maven Central) +## 1.15.0 (13th September 2022) - Adds documentation section filenames into the model. - Adds support for custom elements on dynamic views. From ccfe4dedde267585aed2bbe96b54a8ffa147d3a4 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 20 Sep 2022 23:20:32 +0100 Subject: [PATCH 329/717] Allows filenames to be used to specify element style icons (this is really for themes, where the images can be loaded relative to the theme definition). --- .../src/com/structurizr/util/ImageUtils.java | 21 ++++++++++++------- .../com/structurizr/util/ImageUtilsTests.java | 4 ++++ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/structurizr-core/src/com/structurizr/util/ImageUtils.java b/structurizr-core/src/com/structurizr/util/ImageUtils.java index 7849f7fe6..6b7fdeee2 100644 --- a/structurizr-core/src/com/structurizr/util/ImageUtils.java +++ b/structurizr-core/src/com/structurizr/util/ImageUtils.java @@ -69,25 +69,30 @@ public static String getImageAsDataUri(File file) throws IOException { return "data:" + contentType + ";base64," + base64Content; } - public static void validateImage(String url) { - if (StringUtils.isNullOrEmpty(url)) { + public static void validateImage(String imageDescriptor) { + if (StringUtils.isNullOrEmpty(imageDescriptor)) { return; } - url = url.trim(); + imageDescriptor = imageDescriptor.trim(); - if (Url.isUrl(url)) { + if (Url.isUrl(imageDescriptor)) { // all good return; } - if (url.startsWith("data:image")) { - if (ImageUtils.isSupportedDataUri(url)) { - // all good + if (imageDescriptor.toLowerCase().endsWith(".png") || imageDescriptor.toLowerCase().endsWith(".jpg") || imageDescriptor.toLowerCase().endsWith(".jpeg") || imageDescriptor.toLowerCase().endsWith(".gif")) { + // it's just a filename + return; + } + + if (imageDescriptor.startsWith("data:image")) { + if (ImageUtils.isSupportedDataUri(imageDescriptor)) { + // it's a PNG/JPG data URI return; } else { // it's a data URI, but not supported - throw new IllegalArgumentException("Only PNG and JPG data URIs are supported: " + url); + throw new IllegalArgumentException("Only PNG and JPG data URIs are supported: " + imageDescriptor); } } diff --git a/structurizr-core/test/unit/com/structurizr/util/ImageUtilsTests.java b/structurizr-core/test/unit/com/structurizr/util/ImageUtilsTests.java index 7d9a25af6..819201ab7 100644 --- a/structurizr-core/test/unit/com/structurizr/util/ImageUtilsTests.java +++ b/structurizr-core/test/unit/com/structurizr/util/ImageUtilsTests.java @@ -159,6 +159,10 @@ void validateImage() { ImageUtils.validateImage("https://structurizr.com/image.png"); ImageUtils.validateImage("data:image/png;base64,iVBORw0KGg"); ImageUtils.validateImage("data:image/jpeg;base64,iVBORw0KGg"); + ImageUtils.validateImage("image.png"); + ImageUtils.validateImage("image.jpg"); + ImageUtils.validateImage("image.jpeg"); + ImageUtils.validateImage("image.gif"); //disallowed try { From e9a340cc1ffd9b380fe254dacc538a98a0234cb0 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 20 Sep 2022 23:21:52 +0100 Subject: [PATCH 330/717] Add Jackson annotations as a dependency. --- structurizr-client/build.gradle | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/structurizr-client/build.gradle b/structurizr-client/build.gradle index 2660a56ad..e869bb738 100644 --- a/structurizr-client/build.gradle +++ b/structurizr-client/build.gradle @@ -2,7 +2,8 @@ dependencies { api project(':structurizr-core') - implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.3' + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.13.4' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.4' implementation 'org.apache.httpcomponents.client5:httpclient5:5.1.3' From aaaeacdb069c91eb868cced589cbfdf122c68155 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 22 Sep 2022 14:28:11 +0100 Subject: [PATCH 331/717] Some minor changes to support building a diagram legend with the PlantUML exporter. --- build.gradle | 2 +- .../src/com/structurizr/model/ModelItem.java | 16 ++------- .../src/com/structurizr/util/TagUtils.java | 22 ++++++++++++ .../src/com/structurizr/view/Styles.java | 35 +++++++++++++++---- 4 files changed, 54 insertions(+), 21 deletions(-) create mode 100644 structurizr-core/src/com/structurizr/util/TagUtils.java diff --git a/build.gradle b/build.gradle index d73b5e5f0..3d5478eb3 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.15.0' + version = '1.15.1' repositories { mavenCentral() diff --git a/structurizr-core/src/com/structurizr/model/ModelItem.java b/structurizr-core/src/com/structurizr/model/ModelItem.java index 3ebc76107..e95ea9148 100644 --- a/structurizr-core/src/com/structurizr/model/ModelItem.java +++ b/structurizr-core/src/com/structurizr/model/ModelItem.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.structurizr.PropertyHolder; import com.structurizr.util.StringUtils; +import com.structurizr.util.TagUtils; import com.structurizr.util.Url; import java.util.*; @@ -45,20 +46,7 @@ void setId(String id) { * or an empty string if there are no tags */ public String getTags() { - Set setOfTags = getTagsAsSet(); - - if (setOfTags.isEmpty()) { - return ""; - } - - StringBuilder buf = new StringBuilder(); - for (String tag : setOfTags) { - buf.append(tag); - buf.append(","); - } - - String tagsAsString = buf.toString(); - return tagsAsString.substring(0, tagsAsString.length()-1); + return TagUtils.toString(getTagsAsSet()); } @JsonIgnore diff --git a/structurizr-core/src/com/structurizr/util/TagUtils.java b/structurizr-core/src/com/structurizr/util/TagUtils.java new file mode 100644 index 000000000..c41056584 --- /dev/null +++ b/structurizr-core/src/com/structurizr/util/TagUtils.java @@ -0,0 +1,22 @@ +package com.structurizr.util; + +import java.util.Collection; + +public class TagUtils { + + public static String toString(Collection tags) { + if (tags.isEmpty()) { + return ""; + } + + StringBuilder buf = new StringBuilder(); + for (String tag : tags) { + buf.append(tag); + buf.append(","); + } + + String tagsAsString = buf.toString(); + return tagsAsString.substring(0, tagsAsString.length()-1); + } + +} diff --git a/structurizr-core/src/com/structurizr/view/Styles.java b/structurizr-core/src/com/structurizr/view/Styles.java index 2081559b0..ddf92ccb4 100644 --- a/structurizr-core/src/com/structurizr/view/Styles.java +++ b/structurizr-core/src/com/structurizr/view/Styles.java @@ -2,6 +2,7 @@ import com.structurizr.model.*; import com.structurizr.util.StringUtils; +import com.structurizr.util.TagUtils; import java.util.*; @@ -88,13 +89,14 @@ public RelationshipStyle addRelationshipStyle(String tag) { * * * @param tag the tag (a String) - * @return an ElementStyle instance + * @return an ElementStyle instance, or null if there is no style for the given tag */ public ElementStyle findElementStyle(String tag) { if (tag == null) { return null; } + boolean elementStyleExists = false; tag = tag.trim(); ElementStyle style = new ElementStyle(tag); @@ -106,11 +108,16 @@ public ElementStyle findElementStyle(String tag) { for (ElementStyle elementStyle : elementStyles) { if (elementStyle != null && elementStyle.getTag().equals(tag)) { + elementStyleExists = true; style.copyFrom(elementStyle); } } - return style; + if (elementStyleExists) { + return style; + } else { + return null; + } } /** @@ -119,13 +126,14 @@ public ElementStyle findElementStyle(String tag) { * * * @param tag the tag (a String) - * @return a RelationshipStyle instance + * @return a RelationshipStyle instance, or null if there is no style for the given tag */ public RelationshipStyle findRelationshipStyle(String tag) { if (tag == null) { return null; } + boolean relationshipStyleExists = false; tag = tag.trim(); RelationshipStyle style = new RelationshipStyle(tag); @@ -138,10 +146,15 @@ public RelationshipStyle findRelationshipStyle(String tag) { for (RelationshipStyle relationshipStyle : relationshipStyles) { if (relationshipStyle != null && relationshipStyle.getTag().equals(tag)) { style.copyFrom(relationshipStyle); + relationshipStyleExists = true; } } - return style; + if (relationshipStyleExists) { + return style; + } else { + return null; + } } /** @@ -155,7 +168,7 @@ public RelationshipStyle findRelationshipStyle(String tag) { * @return an ElementStyle object */ public ElementStyle findElementStyle(Element element) { - ElementStyle style = new ElementStyle("").background("#dddddd").color("#000000").shape(Shape.Box).fontSize(24).border(Border.Solid).opacity(100).metadata(true).description(true); + ElementStyle style = new ElementStyle(Tags.ELEMENT).background("#dddddd").color("#000000").shape(Shape.Box).fontSize(24).border(Border.Solid).opacity(100).metadata(true).description(true); if (element instanceof DeploymentNode) { style.setBackground("#ffffff"); @@ -164,6 +177,8 @@ public ElementStyle findElementStyle(Element element) { } if (element != null) { + Set tagsUsedToComposeStyle = new LinkedHashSet<>(); + tagsUsedToComposeStyle.add(Tags.ELEMENT); String tags = element.getTags(); if (element instanceof SoftwareSystemInstance) { @@ -179,9 +194,12 @@ public ElementStyle findElementStyle(Element element) { ElementStyle elementStyle = findElementStyle(tag); if (elementStyle != null) { style.copyFrom(elementStyle); + tagsUsedToComposeStyle.add(elementStyle.getTag()); } } } + + style.setTag(TagUtils.toString(tagsUsedToComposeStyle)); } if (style.getWidth() == null) { @@ -219,9 +237,11 @@ public ElementStyle findElementStyle(Element element) { * @return a RelationshipStyle object */ public RelationshipStyle findRelationshipStyle(Relationship relationship) { - RelationshipStyle style = new RelationshipStyle("").thickness(2).color("#707070").dashed(true).routing(Routing.Direct).fontSize(24).width(200).position(50).opacity(100); + RelationshipStyle style = new RelationshipStyle(Tags.RELATIONSHIP).thickness(2).color("#707070").dashed(true).routing(Routing.Direct).fontSize(24).width(200).position(50).opacity(100); if (relationship != null) { + Set tagsUsedToComposeStyle = new LinkedHashSet<>(); + tagsUsedToComposeStyle.add(Tags.RELATIONSHIP); String tags = relationship.getTags(); String linkedRelationshipId = relationship.getLinkedRelationshipId(); @@ -239,9 +259,12 @@ public RelationshipStyle findRelationshipStyle(Relationship relationship) { RelationshipStyle relationshipStyle = findRelationshipStyle(tag); if (relationshipStyle != null) { style.copyFrom(relationshipStyle); + tagsUsedToComposeStyle.add(relationshipStyle.getTag()); } } } + + style.setTag(TagUtils.toString(tagsUsedToComposeStyle)); } return style; From 190c6315e8b019a6041fe4e934ccfc6cb0ab4e9f Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 23 Sep 2022 15:39:46 +0100 Subject: [PATCH 332/717] Adds some additional functionality for getting and finding element/relationship styles. --- docs/changelog.md | 4 ++ .../src/com/structurizr/view/Styles.java | 28 +++++++++ .../com/structurizr/view/StylesTests.java | 62 +++++++++++++++++++ 3 files changed, 94 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 5356a1260..4752fe2a8 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.15.1 (23rd September 2022) + +- Adds some additional functionality for getting and finding element/relationship styles. + ## 1.15.0 (13th September 2022) - Adds documentation section filenames into the model. diff --git a/structurizr-core/src/com/structurizr/view/Styles.java b/structurizr-core/src/com/structurizr/view/Styles.java index ddf92ccb4..70e0dadf1 100644 --- a/structurizr-core/src/com/structurizr/view/Styles.java +++ b/structurizr-core/src/com/structurizr/view/Styles.java @@ -83,6 +83,20 @@ public RelationshipStyle addRelationshipStyle(String tag) { return relationshipStyle; } + /** + * Gets the element style that has been defined (in this workspace) for the given tag. + * + * @param tag the tag (a String) + * @return an ElementStyle instance, or null if no element style has been defined in this workspace + */ + public ElementStyle getElementStyle(String tag) { + if (StringUtils.isNullOrEmpty(tag)) { + throw new IllegalArgumentException("A tag must be specified."); + } + + return elements.stream().filter(es -> es.getTag().equals(tag)).findFirst().orElse(null); + } + /** * Finds the element style for the given tag. This method creates an empty style, * and copies properties from any element styles (from the workspace and any themes) for the given tag. @@ -120,6 +134,20 @@ public ElementStyle findElementStyle(String tag) { } } + /** + * Gets the relationship style that has been defined (in this workspace) for the given tag. + * + * @param tag the tag (a String) + * @return an RelationshipStyle instance, or null if no relationship style has been defined in this workspace + */ + public RelationshipStyle getRelationshipStyle(String tag) { + if (StringUtils.isNullOrEmpty(tag)) { + throw new IllegalArgumentException("A tag must be specified."); + } + + return relationships.stream().filter(rs -> rs.getTag().equals(tag)).findFirst().orElse(null); + } + /** * Finds the relationship style for the given tag. This method creates an empty style, * and copies properties from any relationship styles (from the workspace and any themes) for the given tag. diff --git a/structurizr-core/test/unit/com/structurizr/view/StylesTests.java b/structurizr-core/test/unit/com/structurizr/view/StylesTests.java index 0a663da2a..36bccbf94 100644 --- a/structurizr-core/test/unit/com/structurizr/view/StylesTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/StylesTests.java @@ -333,4 +333,66 @@ void clearRelationshipStyles_RemovesAllRelationshipStyles() { assertEquals(0, styles.getRelationships().size()); } + @Test + void getElementStyle_ThrowsAnException_WhenGivenNoTag() { + try { + styles.getElementStyle(null); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("A tag must be specified.", iae.getMessage()); + } + + try { + styles.getElementStyle(" "); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("A tag must be specified.", iae.getMessage()); + } + } + + @Test + void getElementStyle_ReturnsNull_WhenThereIsNoStyleForTheGivenTag() { + ElementStyle style = styles.getElementStyle("Tag"); + assertNull(style); + } + + @Test + void getElementStyle_ReturnsTheElementStyle_WhenThereIsAStyleForTheGivenTag() { + styles.addElementStyle("Tag").background("#ffffff"); + + ElementStyle style = styles.getElementStyle("Tag"); + assertEquals("#ffffff", style.getBackground()); + } + + @Test + void getRelationshipStyle_ThrowsAnException_WhenGivenNoTag() { + try { + styles.getRelationshipStyle(null); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("A tag must be specified.", iae.getMessage()); + } + + try { + styles.getRelationshipStyle(" "); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("A tag must be specified.", iae.getMessage()); + } + } + + @Test + void getRelationshipStyle_ReturnsNull_WhenThereIsNoStyleForTheGivenTag() { + RelationshipStyle style = styles.getRelationshipStyle("Tag"); + assertNull(style); + } + + @Test + void getRelationshipStyle_ReturnsTheRelationshipStyle_WhenThereIsAStyleForTheGivenTag() { + styles.addRelationshipStyle("Tag").color("#ffffff"); + + RelationshipStyle style = styles.getRelationshipStyle("Tag"); + assertEquals("#ffffff", style.getColor()); + } + } \ No newline at end of file From eb8112148b7a47766f0544d347d7629fd42c24ed Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 29 Sep 2022 22:50:09 +0200 Subject: [PATCH 333/717] Adds support for element icons being specified as filenames (rather than full URLs) in themes. --- .../src/com/structurizr/view/ThemeUtils.java | 16 ++++++++++++++ .../com/structurizr/view/ThemeUtilsTests.java | 22 ++++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/structurizr-client/src/com/structurizr/view/ThemeUtils.java b/structurizr-client/src/com/structurizr/view/ThemeUtils.java index ce1eef78b..6f7c79c8d 100644 --- a/structurizr-client/src/com/structurizr/view/ThemeUtils.java +++ b/structurizr-client/src/com/structurizr/view/ThemeUtils.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.databind.SerializationFeature; import com.structurizr.Workspace; import com.structurizr.io.WorkspaceWriterException; +import com.structurizr.util.StringUtils; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; @@ -79,6 +80,21 @@ public static void loadThemes(Workspace workspace) throws Exception { objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); Theme theme = objectMapper.readValue(json, Theme.class); + String baseUrl = url.substring(0, url.lastIndexOf('/') + 1); + + for (ElementStyle elementStyle : theme.getElements()) { + String icon = elementStyle.getIcon(); + if (!StringUtils.isNullOrEmpty(icon)) { + if (icon.startsWith("http")) { + // okay, image served over HTTP + } else if (icon.startsWith("data:image")) { + // also okay, data URI + } else { + // convert the relative icon filename into a full URL + elementStyle.setIcon(baseUrl + icon); + } + } + } workspace.getViews().getConfiguration().getStyles().addStylesFromTheme(theme); } diff --git a/structurizr-client/test/unit/com/structurizr/view/ThemeUtilsTests.java b/structurizr-client/test/unit/com/structurizr/view/ThemeUtilsTests.java index afe40f9d0..2d390a47c 100644 --- a/structurizr-client/test/unit/com/structurizr/view/ThemeUtilsTests.java +++ b/structurizr-client/test/unit/com/structurizr/view/ThemeUtilsTests.java @@ -39,7 +39,7 @@ void loadThemes_LoadsThemesWhenThemesAreDefined() throws Exception { assertNotNull(style); assertEquals("#d6242d", style.getStroke()); assertEquals("#d6242d", style.getColor()); - assertNotNull(style.getIcon()); + assertEquals("https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Alexa-For-Business_light-bg@4x.png", style.getIcon()); } @Test @@ -129,4 +129,24 @@ void findRelationshipStyle_WithThemes() { assertEquals(Integer.valueOf(100), style.getOpacity()); } + @Test + void loadThemes_ReplacesRelativeIconReferences() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name"); + softwareSystem.addTags("Amazon Web Services - Alexa For Business"); + workspace.getViews().getConfiguration().setThemes("https://raw.githubusercontent.com/structurizr/themes/master/amazon-web-services-2020.04.30/theme.json"); + + ThemeUtils.loadThemes(workspace); + + // there should still be zero styles in the workspace + assertEquals(0, workspace.getViews().getConfiguration().getStyles().getElements().size()); + + // but we should be able to find a style included in the theme + ElementStyle style = workspace.getViews().getConfiguration().getStyles().findElementStyle(softwareSystem); + assertNotNull(style); + assertEquals("#d6242d", style.getStroke()); + assertEquals("#d6242d", style.getColor()); + assertEquals("https://raw.githubusercontent.com/structurizr/themes/master/amazon-web-services-2020.04.30/Alexa-For-Business_light-bg@4x.png", style.getIcon()); + } + } \ No newline at end of file From 7cb174502432b94d10d680cad7f6a4b175efaa58 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 29 Sep 2022 22:51:41 +0200 Subject: [PATCH 334/717] Update version/changelog. --- build.gradle | 2 +- docs/changelog.md | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 3d5478eb3..f823f1730 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.15.1' + version = '1.15.2' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 4752fe2a8..86b1ced51 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.15.2 (unreleased to Maven Central) + +- Adds support for element icons being specified as filenames (rather than full URLs) in themes. + ## 1.15.1 (23rd September 2022) - Adds some additional functionality for getting and finding element/relationship styles. From ce6f36704cbef9b9a765a1281adfecbb7d3392ee Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 3 Oct 2022 07:53:42 +0100 Subject: [PATCH 335/717] Added release date. --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 86b1ced51..4bf540b6d 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 1.15.2 (unreleased to Maven Central) +## 1.15.2 (3rd October 2022) - Adds support for element icons being specified as filenames (rather than full URLs) in themes. From 0d53848be04d34c11c1efab8ce693fdfb32460d2 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 10 Oct 2022 09:23:52 +0100 Subject: [PATCH 336/717] Fixes some transitive dependencies. --- structurizr-client/build.gradle | 11 +++-------- structurizr-core/build.gradle | 7 +++---- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/structurizr-client/build.gradle b/structurizr-client/build.gradle index e869bb738..c47bdc04d 100644 --- a/structurizr-client/build.gradle +++ b/structurizr-client/build.gradle @@ -2,14 +2,9 @@ dependencies { api project(':structurizr-core') - implementation 'com.fasterxml.jackson.core:jackson-annotations:2.13.4' - implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.4' - - implementation 'org.apache.httpcomponents.client5:httpclient5:5.1.3' - - implementation 'javax.xml.bind:jaxb-api:2.3.0' - - implementation 'commons-logging:commons-logging:1.2' + api 'com.fasterxml.jackson.core:jackson-databind:2.13.4' + api 'org.apache.httpcomponents.client5:httpclient5:5.1.3' + api 'javax.xml.bind:jaxb-api:2.3.0' testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' diff --git a/structurizr-core/build.gradle b/structurizr-core/build.gradle index 08250abb3..406636d45 100644 --- a/structurizr-core/build.gradle +++ b/structurizr-core/build.gradle @@ -1,9 +1,8 @@ dependencies { - implementation 'com.fasterxml.jackson.core:jackson-annotations:2.13.3' - implementation 'com.google.code.findbugs:jsr305:3.0.2' - - implementation 'commons-logging:commons-logging:1.2' + api 'com.fasterxml.jackson.core:jackson-annotations:2.13.4' + api 'com.google.code.findbugs:jsr305:3.0.2' + api 'commons-logging:commons-logging:1.2' testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' testImplementation 'org.assertj:assertj-core:3.23.1' From f3d80e1919aae773eee8d076c49cb33a63c462bb Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 10 Oct 2022 09:24:19 +0100 Subject: [PATCH 337/717] Bump version number. --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index f823f1730..6e459fc46 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.15.2' + version = '1.15.3' repositories { mavenCentral() From 4a37a4edfb4ab2b9489db07b5b65df8818bc3a2c Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 11 Oct 2022 13:10:20 +0100 Subject: [PATCH 338/717] Updated to reflect release. --- docs/changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 4bf540b6d..576719449 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.15.3 (11th October 2022) + +- Updates some transitive dependencies. + ## 1.15.2 (3rd October 2022) - Adds support for element icons being specified as filenames (rather than full URLs) in themes. From 4015cf7f13500a9d8fa2245a85d7bd0655905398 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 18 Oct 2022 13:05:03 +0100 Subject: [PATCH 339/717] Adds support for element style stroke widths (re: https://github.com/structurizr/structurizr/issues/124). --- build.gradle | 2 +- docs/changelog.md | 4 +++ .../com/structurizr/view/ElementStyle.java | 31 ++++++++++++++++ .../structurizr/view/ElementStyleTests.java | 36 +++++++++++++++++++ .../com/structurizr/view/StylesTests.java | 6 +++- 5 files changed, 77 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 6e459fc46..9a351a4cc 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.15.3' + version = '1.16.0' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 576719449..efd7157e8 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.16.0 (unreleased to Maven Central) + +- Adds support for element style stroke widths. + ## 1.15.3 (11th October 2022) - Updates some transitive dependencies. diff --git a/structurizr-core/src/com/structurizr/view/ElementStyle.java b/structurizr-core/src/com/structurizr/view/ElementStyle.java index 75999d2ea..3302b1fc6 100644 --- a/structurizr-core/src/com/structurizr/view/ElementStyle.java +++ b/structurizr-core/src/com/structurizr/view/ElementStyle.java @@ -26,6 +26,9 @@ public final class ElementStyle extends AbstractStyle { @JsonInclude(value = JsonInclude.Include.NON_NULL) private String stroke; + @JsonInclude(value = JsonInclude.Include.NON_NULL) + private Integer strokeWidth; + @JsonInclude(value = JsonInclude.Include.NON_NULL) private String color; @@ -164,6 +167,30 @@ public ElementStyle stroke(String color) { return this; } + /** + * Gets the stroke width, in pixels, between 1 and 10. + * + * @return the stroke width + */ + public Integer getStrokeWidth() { + return strokeWidth; + } + + public void setStrokeWidth(Integer strokeWidth) { + if (strokeWidth == null) { + this.strokeWidth = null; + } else if (strokeWidth < 1) { + this.strokeWidth = 1; + } else { + this.strokeWidth = Math.min(10, strokeWidth); + } + } + + public ElementStyle strokeWidth(Integer strokeWidth) { + setStrokeWidth(strokeWidth); + return this; + } + /** * Gets the foreground (text) colour of the element, as a HTML RGB hex string (e.g. #123456). * @@ -352,6 +379,10 @@ void copyFrom(ElementStyle elementStyle) { this.setStroke(elementStyle.getStroke()); } + if (elementStyle.getStrokeWidth() != null) { + this.setStrokeWidth(elementStyle.getStrokeWidth()); + } + if (!StringUtils.isNullOrEmpty(elementStyle.getColor())) { this.setColor(elementStyle.getColor()); } diff --git a/structurizr-core/test/unit/com/structurizr/view/ElementStyleTests.java b/structurizr-core/test/unit/com/structurizr/view/ElementStyleTests.java index 779c0b984..83b6768ec 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ElementStyleTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ElementStyleTests.java @@ -294,4 +294,40 @@ void setProperties_SetsTheProperties_WhenANonEmptyMapIsSpecified() { assertEquals("value", style.getProperties().get("name")); } + @Test + void setStrokeWidth_WhenNullIsSpecified() { + ElementStyle style = new ElementStyle(); + style.setStrokeWidth(10); + style.setStrokeWidth(null); + assertNull(style.getStrokeWidth()); + } + + @Test + void setStrokeWidth_WhenANegativeIntegerIsSpecified() { + ElementStyle style = new ElementStyle(); + style.setStrokeWidth(-1); + assertEquals(1, style.getStrokeWidth()); + } + + @Test + void setStrokeWidth_WhenZeroIsSpecified() { + ElementStyle style = new ElementStyle(); + style.setStrokeWidth(0); + assertEquals(1, style.getStrokeWidth()); + } + + @Test + void setStrokeWidth_WhenAPositiveIntegerIsSpecified() { + ElementStyle style = new ElementStyle(); + style.setStrokeWidth(10); + assertEquals(10, style.getStrokeWidth()); + } + + @Test + void setStrokeWidth_WhenAPositiveIntegerOver10IsSpecified() { + ElementStyle style = new ElementStyle(); + style.setStrokeWidth(20); + assertEquals(10, style.getStrokeWidth()); + } + } \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/view/StylesTests.java b/structurizr-core/test/unit/com/structurizr/view/StylesTests.java index 36bccbf94..a47b3a213 100644 --- a/structurizr-core/test/unit/com/structurizr/view/StylesTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/StylesTests.java @@ -22,6 +22,7 @@ void findElementStyle_ReturnsTheDefaultStyle_WhenPassedNull() { assertNull(style.getIcon()); assertEquals(Border.Solid, style.getBorder()); assertEquals("#9a9a9a", style.getStroke()); + assertNull(style.getStrokeWidth()); assertEquals(Integer.valueOf(100), style.getOpacity()); assertEquals(true, style.getMetadata()); assertEquals(true, style.getDescription()); @@ -40,6 +41,7 @@ void findElementStyle_ReturnsTheDefaultStyle_WhenNoStylesAreDefined() { assertNull(style.getIcon()); assertEquals(Border.Solid, style.getBorder()); assertEquals("#9a9a9a", style.getStroke()); + assertNull(style.getStrokeWidth()); assertEquals(Integer.valueOf(100), style.getOpacity()); assertEquals(true, style.getMetadata()); assertEquals(true, style.getDescription()); @@ -51,7 +53,7 @@ void findElementStyle_ReturnsTheCorrectStyle_WhenStylesAreDefined() { element.addTags("Some Tag"); styles.addElementStyle(Tags.SOFTWARE_SYSTEM).background("#ff0000").color("#ffffff"); - styles.addElementStyle("Some Tag").color("#0000ff").stroke("#00ff00").shape(Shape.RoundedBox).width(123).height(456); + styles.addElementStyle("Some Tag").color("#0000ff").stroke("#00ff00").strokeWidth(2).shape(Shape.RoundedBox).width(123).height(456); ElementStyle style = styles.findElementStyle(element); assertEquals(Integer.valueOf(123), style.getWidth()); @@ -63,6 +65,7 @@ void findElementStyle_ReturnsTheCorrectStyle_WhenStylesAreDefined() { assertNull(style.getIcon()); assertEquals(Border.Solid, style.getBorder()); assertEquals("#00ff00", style.getStroke()); + assertEquals(2, style.getStrokeWidth()); assertEquals(Integer.valueOf(100), style.getOpacity()); assertEquals(true, style.getMetadata()); assertEquals(true, style.getDescription()); @@ -89,6 +92,7 @@ void findElementStyle_ReturnsTheCorrectStyleForAnElementInstance_WhenStylesAreDe assertNull(style.getIcon()); assertEquals(Border.Solid, style.getBorder()); assertEquals("#00ff00", style.getStroke()); + assertNull(style.getStrokeWidth()); assertEquals(Integer.valueOf(100), style.getOpacity()); assertEquals(true, style.getMetadata()); assertEquals(true, style.getDescription()); From a143f196825eb892acbe848f331d0fb59d7c849c Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 24 Oct 2022 13:59:55 +0100 Subject: [PATCH 340/717] Ensures that style properties are included when finding a style for an element/relationship. --- structurizr-core/src/com/structurizr/view/ElementStyle.java | 4 ++++ .../src/com/structurizr/view/RelationshipStyle.java | 4 ++++ .../test/unit/com/structurizr/view/StylesTests.java | 6 ++++-- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/structurizr-core/src/com/structurizr/view/ElementStyle.java b/structurizr-core/src/com/structurizr/view/ElementStyle.java index 3302b1fc6..bbe4b6438 100644 --- a/structurizr-core/src/com/structurizr/view/ElementStyle.java +++ b/structurizr-core/src/com/structurizr/view/ElementStyle.java @@ -414,6 +414,10 @@ void copyFrom(ElementStyle elementStyle) { if (elementStyle.getDescription() != null) { this.setDescription(elementStyle.getDescription()); } + + for (String name : elementStyle.getProperties().keySet()) { + this.addProperty(name, elementStyle.getProperties().get(name)); + } } } \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/view/RelationshipStyle.java b/structurizr-core/src/com/structurizr/view/RelationshipStyle.java index 92219414d..bea120e0a 100644 --- a/structurizr-core/src/com/structurizr/view/RelationshipStyle.java +++ b/structurizr-core/src/com/structurizr/view/RelationshipStyle.java @@ -240,6 +240,10 @@ void copyFrom(RelationshipStyle relationshipStyle) { if (relationshipStyle.getOpacity() != null) { this.setOpacity(relationshipStyle.getOpacity()); } + + for (String name : relationshipStyle.getProperties().keySet()) { + this.addProperty(name, relationshipStyle.getProperties().get(name)); + } } } diff --git a/structurizr-core/test/unit/com/structurizr/view/StylesTests.java b/structurizr-core/test/unit/com/structurizr/view/StylesTests.java index a47b3a213..238ba77a6 100644 --- a/structurizr-core/test/unit/com/structurizr/view/StylesTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/StylesTests.java @@ -80,7 +80,7 @@ void findElementStyle_ReturnsTheCorrectStyleForAnElementInstance_WhenStylesAreDe SoftwareSystemInstance softwareSystemInstance = deploymentNode.add(softwareSystem); styles.addElementStyle(Tags.SOFTWARE_SYSTEM).background("#ff0000").color("#ffffff"); - styles.addElementStyle("Some Tag").color("#0000ff").stroke("#00ff00").shape(Shape.RoundedBox).width(123).height(456); + styles.addElementStyle("Some Tag").color("#0000ff").stroke("#00ff00").shape(Shape.RoundedBox).width(123).height(456).addProperty("name", "value"); ElementStyle style = styles.findElementStyle(softwareSystemInstance); assertEquals(Integer.valueOf(123), style.getWidth()); @@ -96,6 +96,7 @@ void findElementStyle_ReturnsTheCorrectStyleForAnElementInstance_WhenStylesAreDe assertEquals(Integer.valueOf(100), style.getOpacity()); assertEquals(true, style.getMetadata()); assertEquals(true, style.getDescription()); + assertEquals("value", style.getProperties().get("name")); } @Test @@ -161,7 +162,7 @@ void findRelationshipStyle_ReturnsTheCorrectStyle_WhenStylesAreDefined() { relationship.addTags("Some Tag"); styles.addRelationshipStyle(Tags.RELATIONSHIP).color("#ff0000"); - styles.addRelationshipStyle("Some Tag").color("#0000ff"); + styles.addRelationshipStyle("Some Tag").color("#0000ff").addProperty("name", "value"); RelationshipStyle style = styles.findRelationshipStyle(relationship); assertEquals(Integer.valueOf(2), style.getThickness()); @@ -172,6 +173,7 @@ void findRelationshipStyle_ReturnsTheCorrectStyle_WhenStylesAreDefined() { assertEquals(Integer.valueOf(200), style.getWidth()); assertEquals(Integer.valueOf(50), style.getPosition()); assertEquals(Integer.valueOf(100), style.getOpacity()); + assertEquals("value", style.getProperties().get("name")); } @Test From 5ffc9fc556c802d849a98369527ec1e3a408144a Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 24 Oct 2022 14:12:00 +0100 Subject: [PATCH 341/717] Updated to reflect release. --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index efd7157e8..eeded6459 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 1.16.0 (unreleased to Maven Central) +## 1.16.0 (24th October 2022) - Adds support for element style stroke widths. From c249662fdf245aa5953fd1cd3dd9ae57656b3cb1 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 27 Oct 2022 07:49:15 +0200 Subject: [PATCH 342/717] Fixes comment typos. --- structurizr-core/src/com/structurizr/view/Configuration.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/structurizr-core/src/com/structurizr/view/Configuration.java b/structurizr-core/src/com/structurizr/view/Configuration.java index dc5dfdd75..dc9043da6 100644 --- a/structurizr-core/src/com/structurizr/view/Configuration.java +++ b/structurizr-core/src/com/structurizr/view/Configuration.java @@ -209,7 +209,7 @@ public void setViewSortOrder(ViewSortOrder viewSortOrder) { } /** - * Gets the collection of name-value property pairs associated with this workspace, as a Map. + * Gets the collection of name-value property pairs, as a Map. * * @return a Map (String, String) (empty if there are no properties) */ @@ -218,7 +218,7 @@ public Map getProperties() { } /** - * Adds a name-value pair property to this workspace. + * Adds a name-value pair property. * * @param name the name of the property * @param value the value of the property From a1420029bfb10043f2b920e38afcec4e7c991450 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 27 Oct 2022 07:49:58 +0200 Subject: [PATCH 343/717] Adds name-value properties to views. --- .../src/com/structurizr/view/View.java | 42 ++++++++++++++++--- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/structurizr-core/src/com/structurizr/view/View.java b/structurizr-core/src/com/structurizr/view/View.java index f12a6a08b..fdfbadfd9 100644 --- a/structurizr-core/src/com/structurizr/view/View.java +++ b/structurizr-core/src/com/structurizr/view/View.java @@ -2,20 +2,18 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonSetter; +import com.structurizr.PropertyHolder; import com.structurizr.model.*; import com.structurizr.util.StringUtils; import javax.annotation.Nonnull; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.Optional; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; /** * The superclass for all views (static views, dynamic views and deployment views). */ -public abstract class View { +public abstract class View implements PropertyHolder { private static final int DEFAULT_RANK_SEPARATION = 300; private static final int DEFAULT_NODE_SEPARATION = 300; @@ -30,6 +28,7 @@ public abstract class View { private AutomaticLayout automaticLayout = null; private boolean mergeFromRemote = true; private String title; + private Map properties = new HashMap<>(); private Set elementViews = new LinkedHashSet<>(); private Set relationshipViews = new LinkedHashSet<>(); @@ -543,4 +542,37 @@ protected void addNearestNeighbours(Element element, Class getProperties() { + return new HashMap<>(properties); + } + + /** + * Adds a name-value pair property to this view. + * + * @param name the name of the property + * @param value the value of the property + */ + public void addProperty(String name, String value) { + if (name == null || name.trim().length() == 0) { + throw new IllegalArgumentException("A property name must be specified."); + } + + if (value == null || value.trim().length() == 0) { + throw new IllegalArgumentException("A property value must be specified."); + } + + properties.put(name, value); + } + + void setProperties(Map properties) { + if (properties != null) { + this.properties = new HashMap<>(properties); + } + } + } \ No newline at end of file From 25001caeadf661af58ded4f66903415f9dc12ece Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 27 Oct 2022 21:25:13 +0200 Subject: [PATCH 344/717] Updated to reflect release. --- build.gradle | 2 +- docs/changelog.md | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 9a351a4cc..4dda6f156 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.16.0' + version = '1.16.1' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index eeded6459..2fe93e4cf 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.16.1 (27th October 2022) + +- Adds name-value properties to views. + ## 1.16.0 (24th October 2022) - Adds support for element style stroke widths. From d4e895e14ef8e9dcfd4a4b086a7910fe8fdb226a Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 22 Dec 2022 18:38:01 +0000 Subject: [PATCH 345/717] Upgrade dependencies. --- structurizr-client/build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/structurizr-client/build.gradle b/structurizr-client/build.gradle index c47bdc04d..881ac490f 100644 --- a/structurizr-client/build.gradle +++ b/structurizr-client/build.gradle @@ -2,9 +2,9 @@ dependencies { api project(':structurizr-core') - api 'com.fasterxml.jackson.core:jackson-databind:2.13.4' - api 'org.apache.httpcomponents.client5:httpclient5:5.1.3' - api 'javax.xml.bind:jaxb-api:2.3.0' + api 'com.fasterxml.jackson.core:jackson-databind:2.14.1' + api 'org.apache.httpcomponents.client5:httpclient5:5.2.1' + api 'javax.xml.bind:jaxb-api:2.3.1' testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' From 7774dd7027be59ab2cf90c4e97851028f9009a4f Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 22 Dec 2022 18:39:28 +0000 Subject: [PATCH 346/717] Updated to reflect release. --- build.gradle | 2 +- docs/changelog.md | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 4dda6f156..18a8e3c84 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.16.1' + version = '1.16.2' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 2fe93e4cf..47972e38d 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.16.2 (22nd December 2022) + +- Upgraded dependencies. + ## 1.16.1 (27th October 2022) - Adds name-value properties to views. From c5605ab2487c09b0bbc4f43663c6f6193c8e11c1 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 28 Dec 2022 09:50:25 +0000 Subject: [PATCH 347/717] Fixes #190. --- docs/views.md | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/docs/views.md b/docs/views.md index 035a6fd8f..b53c9e22f 100644 --- a/docs/views.md +++ b/docs/views.md @@ -2,14 +2,19 @@ Once you've [added elements to a model](model.md), you can create one or more views to visualise parts of the model, which can subsequently be rendered as diagrams by a number of different tools. -Structurizr for Java supports all of the view types described in the [C4 model](https://c4model.com), and the Java classes implementing these views can be found in the [com.structurizr.view](https://github.com/structurizr/java/tree/master/structurizr-core/src/com/structurizr/view) package as follows: - -* [SystemContextView](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/view/SystemContextView.java) -* [ContainerView](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/view/ContainerView.java) -* [ComponentView](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/view/ComponentView.java) -* [SystemLandscapeView](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/view/SystemLandscapeView.java) -* [DynamicView](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/view/DynamicView.java) -* [DeploymentView](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/view/DeploymentView.java) +Structurizr for Java supports the following view types described in the [C4 model](https://c4model.com), and the Java classes implementing these views can be found in the [com.structurizr.view](https://github.com/structurizr/java/tree/master/structurizr-core/src/com/structurizr/view) package as follows: + +* Core diagrams + * [SystemContextView](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/view/SystemContextView.java) + * [ContainerView](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/view/ContainerView.java) + * [ComponentView](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/view/ComponentView.java) + +* Supplementary diagrams + * [SystemLandscapeView](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/view/SystemLandscapeView.java) + * [DynamicView](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/view/DynamicView.java) + * [DeploymentView](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/view/DeploymentView.java) + +Please note that code diagrams (level 4 of the C4 model) are not supported. ## Creating views From 44df84ca745ecf272bfacb69c60b7f850a239575 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 4 Jan 2023 10:29:49 +0000 Subject: [PATCH 348/717] Fixes case-sensitivity inconsistencies related to element names and relationship descriptions (#183). --- build.gradle | 2 +- docs/changelog.md | 4 +++ .../structurizr/util/WorkspaceUtilsTests.java | 31 +++++++++++++++++++ .../src/com/structurizr/model/Element.java | 2 +- .../src/com/structurizr/model/Model.java | 6 ++-- .../structurizr/model/RelationshipTests.java | 10 ++++++ 6 files changed, 50 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index 18a8e3c84..404bebda5 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.16.2' + version = '1.17.0' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 47972e38d..ca2b973e1 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.17.0 (unreleased) + +- Fixes case-sensitivity inconsistencies related to element names and relationship descriptions (#183). + ## 1.16.2 (22nd December 2022) - Upgraded dependencies. diff --git a/structurizr-client/test/unit/com/structurizr/util/WorkspaceUtilsTests.java b/structurizr-client/test/unit/com/structurizr/util/WorkspaceUtilsTests.java index c6e3c0188..02155b905 100644 --- a/structurizr-client/test/unit/com/structurizr/util/WorkspaceUtilsTests.java +++ b/structurizr-client/test/unit/com/structurizr/util/WorkspaceUtilsTests.java @@ -1,6 +1,8 @@ package com.structurizr.util; import com.structurizr.Workspace; +import com.structurizr.model.Model; +import com.structurizr.model.SoftwareSystem; import org.junit.jupiter.api.Test; import java.io.File; @@ -121,4 +123,33 @@ void fromJson() throws Exception { assertEquals("Description", workspace.getDescription()); } + @Test + void elementNamesAreCaseSensitive() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + Model model = workspace.getModel(); + SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Name"); + SoftwareSystem softwareSystem2 = model.addSoftwareSystem("NAME"); + SoftwareSystem softwareSystem3 = model.addSoftwareSystem("name"); + + assertEquals(3, model.getSoftwareSystems().size()); + + WorkspaceUtils.fromJson(WorkspaceUtils.toJson(workspace, false)); // no exception thrown + } + + @Test + void relationshipDescriptionsAreCaseSensitive() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + Model model = workspace.getModel(); + SoftwareSystem softwareSystem1 = model.addSoftwareSystem("1"); + SoftwareSystem softwareSystem2 = model.addSoftwareSystem("2"); + + softwareSystem1.uses(softwareSystem2, "Uses"); + softwareSystem1.uses(softwareSystem2, "USES"); + softwareSystem1.uses(softwareSystem2, "uses"); + + assertEquals(3, softwareSystem1.getRelationships().size()); + + WorkspaceUtils.fromJson(WorkspaceUtils.toJson(workspace, false)); // no exception thrown + } + } \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/model/Element.java b/structurizr-core/src/com/structurizr/model/Element.java index 7d4c994cd..a22b8f895 100644 --- a/structurizr-core/src/com/structurizr/model/Element.java +++ b/structurizr-core/src/com/structurizr/model/Element.java @@ -192,7 +192,7 @@ public Relationship getEfferentRelationshipWith(Element element, String descript } boolean has(Relationship relationship) { - return relationships.stream().anyMatch(r -> r.getDestination().equals(relationship.getDestination()) && r.getDescription().equalsIgnoreCase(relationship.getDescription())); + return relationships.stream().anyMatch(r -> r.getDestination().equals(relationship.getDestination()) && r.getDescription().equals(relationship.getDescription())); } void addRelationship(Relationship relationship) { diff --git a/structurizr-core/src/com/structurizr/model/Model.java b/structurizr-core/src/com/structurizr/model/Model.java index dcb001a87..7c1e4b0c0 100644 --- a/structurizr-core/src/com/structurizr/model/Model.java +++ b/structurizr-core/src/com/structurizr/model/Model.java @@ -544,14 +544,14 @@ private void hydrateDeploymentNode(DeploymentNode deploymentNode, DeploymentNode } private void checkNameIsUnique(Collection elements, String name, String errorMessage) { - if (elements.stream().filter(e -> e.getName().equalsIgnoreCase(name)).count() != 1) { + if (elements.stream().filter(e -> e.getName().equals(name)).count() != 1) { throw new WorkspaceValidationException( String.format(errorMessage, name)); } } private void checkNameIsUnique(Collection deploymentNodes, String name, String environment, String errorMessage) { - if (deploymentNodes.stream().filter(dn -> dn.getName().equalsIgnoreCase(name) && dn.getEnvironment().equals(environment)).count() != 1) { + if (deploymentNodes.stream().filter(dn -> dn.getName().equals(name) && dn.getEnvironment().equals(environment)).count() != 1) { throw new WorkspaceValidationException( String.format(errorMessage, name)); } @@ -568,7 +568,7 @@ private void checkChildNamesAreUnique(DeploymentNode deploymentNode) { } private void checkDescriptionIsUnique(Collection relationships, Relationship relationship) { - if (relationships.stream().filter(r -> r.getDestination().equals(relationship.getDestination()) && r.getDescription().equalsIgnoreCase(relationship.getDescription())).count() != 1) { + if (relationships.stream().filter(r -> r.getDestination().equals(relationship.getDestination()) && r.getDescription().equals(relationship.getDescription())).count() != 1) { throw new WorkspaceValidationException( String.format( "A relationship with the description \"%s\" already exists between \"%s\" and \"%s\".", diff --git a/structurizr-core/test/unit/com/structurizr/model/RelationshipTests.java b/structurizr-core/test/unit/com/structurizr/model/RelationshipTests.java index f8edd18ec..f596d09fb 100644 --- a/structurizr-core/test/unit/com/structurizr/model/RelationshipTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/RelationshipTests.java @@ -122,4 +122,14 @@ void interactionStyle_CanBeSetToNull() { assertFalse(relationship.getTagsAsSet().contains(Tags.SYNCHRONOUS)); } + @Test + void relationshipDescriptionsAreCaseSensitive() { + softwareSystem1.uses(softwareSystem2, "Uses"); + softwareSystem1.uses(softwareSystem2, "Uses"); + softwareSystem1.uses(softwareSystem2, "USES"); + softwareSystem1.uses(softwareSystem2, "uses"); + + assertEquals(3, softwareSystem1.getRelationships().size()); + } + } \ No newline at end of file From 28c05ba4c13dd13ee35203b475ae4196d3dfc226 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 4 Jan 2023 13:29:40 +0000 Subject: [PATCH 349/717] Adds support for setting deployment node instances to positive integers or a range (e.g. 0..1, 0..N, 0..*, 1..N, 1..*, 5..10, etc). --- docs/changelog.md | 3 +- .../com/structurizr/model/DeploymentNode.java | 30 ++++++- .../model/DeploymentNodeTests.java | 81 +++++++++++++++---- 3 files changed, 95 insertions(+), 19 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index ca2b973e1..e1177aae0 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -2,7 +2,8 @@ ## 1.17.0 (unreleased) -- Fixes case-sensitivity inconsistencies related to element names and relationship descriptions (#183). +- Fixes case-sensitivity inconsistencies related to element names and relationship descriptions (#183). +- Adds support for setting deployment node instances to positive integers or a range (e.g. 0..1, 0..N, 0..*, 1..N, 1..*, 5..10, etc). ## 1.16.2 (22nd December 2022) diff --git a/structurizr-core/src/com/structurizr/model/DeploymentNode.java b/structurizr-core/src/com/structurizr/model/DeploymentNode.java index 020b8fff2..faa9c613a 100644 --- a/structurizr-core/src/com/structurizr/model/DeploymentNode.java +++ b/structurizr-core/src/com/structurizr/model/DeploymentNode.java @@ -1,6 +1,7 @@ package com.structurizr.model; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonSetter; import java.util.*; @@ -22,7 +23,7 @@ public final class DeploymentNode extends DeploymentElement { private String technology; - private int instances = 1; + private String instances = "1"; private Set children = new HashSet<>(); private Set infrastructureNodes = new HashSet<>(); @@ -360,13 +361,34 @@ public void setTechnology(String technology) { this.technology = technology; } - public int getInstances() { + public String getInstances() { return instances; } public void setInstances(int instances) { - if (instances < 1) { - throw new IllegalArgumentException("Number of instances must be a positive integer."); + setInstances("" + instances); + } + + @JsonSetter + public void setInstances(String instances) { + try { + int instancesAsInteger = Integer.parseInt(instances); + if (instancesAsInteger < 1) { + throw new IllegalArgumentException("Number of instances must be a positive integer or a range."); + } + } catch (NumberFormatException nfe) { + if (instances.matches("\\d*\\.\\.\\d*")) { + String[] range = instances.split("\\.\\."); + if (Integer.parseInt(range[0]) > Integer.parseInt(range[1])) { + throw new IllegalArgumentException("Range upper bound must be greater than the lower bound."); + } + } else if (instances.matches("\\d*..N")) { + // okay + } else if (instances.matches("\\d*..\\*")) { + // okay + } else { + throw new IllegalArgumentException("Number of instances must be a positive integer or a range."); + } } this.instances = instances; diff --git a/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java b/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java index e7e0abd3f..303d3ded6 100644 --- a/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java @@ -107,7 +107,7 @@ void addDeploymentNode_AddsAChildDeploymentNode_WhenANameIsSpecified() { assertEquals("Description", child.getDescription()); assertEquals("Technology", child.getTechnology()); assertEquals("Default", child.getEnvironment()); - assertEquals(1, child.getInstances()); + assertEquals("1", child.getInstances()); assertTrue(child.getProperties().isEmpty()); assertTrue(parent.getChildren().contains(child)); @@ -117,7 +117,7 @@ void addDeploymentNode_AddsAChildDeploymentNode_WhenANameIsSpecified() { assertEquals("Description", child.getDescription()); assertEquals("Technology", child.getTechnology()); assertEquals("Default", child.getEnvironment()); - assertEquals(4, child.getInstances()); + assertEquals("4", child.getInstances()); assertTrue(child.getProperties().isEmpty()); assertTrue(parent.getChildren().contains(child)); @@ -127,7 +127,7 @@ void addDeploymentNode_AddsAChildDeploymentNode_WhenANameIsSpecified() { assertEquals("Description", child.getDescription()); assertEquals("Technology", child.getTechnology()); assertEquals("Default", child.getEnvironment()); - assertEquals(4, child.getInstances()); + assertEquals("4", child.getInstances()); assertEquals(1, child.getProperties().size()); assertEquals("value", child.getProperties().get("name")); assertTrue(parent.getChildren().contains(child)); @@ -195,33 +195,86 @@ void getInfrastructureNodeWithName_ReturnsTheNamedDeploymentNode_WhenThereIsAInf } @Test - void setInstances() { - DeploymentNode deploymentNode = new DeploymentNode(); - deploymentNode.setInstances(8); - - assertEquals(8, deploymentNode.getInstances()); + void setInstances_ThrowsAnException_WhenAZeroIsSpecified() { + try { + DeploymentNode deploymentNode = new DeploymentNode(); + deploymentNode.setInstances("0"); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("Number of instances must be a positive integer or a range.", iae.getMessage()); + } } @Test - void setInstances_ThrowsAnException_WhenZeroIsSpecified() { + void setInstances_ThrowsAnException_WhenANegativeNumberIsSpecified() { try { DeploymentNode deploymentNode = new DeploymentNode(); - deploymentNode.setInstances(0); + deploymentNode.setInstances("-1"); fail(); } catch (IllegalArgumentException iae) { - assertEquals("Number of instances must be a positive integer.", iae.getMessage()); + assertEquals("Number of instances must be a positive integer or a range.", iae.getMessage()); } } @Test - void setInstances_ThrowsAnException_WhenANegativeNumberIsSpecified() { + void setInstancesAsPositiveInteger() { + DeploymentNode deploymentNode = new DeploymentNode(); + + deploymentNode.setInstances("8"); + assertEquals("8", deploymentNode.getInstances()); + + deploymentNode.setInstances(8); + assertEquals("8", deploymentNode.getInstances()); + } + + @Test + void setInstances_ThrowsAnException_WhenAnInvalidRangeIsSpecified() { try { DeploymentNode deploymentNode = new DeploymentNode(); - deploymentNode.setInstances(-1); + deploymentNode.setInstances("x..N"); fail(); } catch (IllegalArgumentException iae) { - assertEquals("Number of instances must be a positive integer.", iae.getMessage()); + assertEquals("Number of instances must be a positive integer or a range.", iae.getMessage()); } + + try { + DeploymentNode deploymentNode = new DeploymentNode(); + deploymentNode.setInstances("2..1"); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("Range upper bound must be greater than the lower bound.", iae.getMessage()); + } + } + + @Test + void setInstancesAsRangeWithBoundedUpperRange() { + DeploymentNode deploymentNode = new DeploymentNode(); + + deploymentNode.setInstances("0..2"); + assertEquals("0..2", deploymentNode.getInstances()); + + deploymentNode.setInstances("1..2"); + assertEquals("1..2", deploymentNode.getInstances()); + + deploymentNode.setInstances("5..10"); + assertEquals("5..10", deploymentNode.getInstances()); + } + + @Test + void setInstancesAsRangeWithUnboundedUpperRange() { + DeploymentNode deploymentNode = new DeploymentNode(); + + deploymentNode.setInstances("0..N"); + assertEquals("0..N", deploymentNode.getInstances()); + + deploymentNode.setInstances("1..N"); + assertEquals("1..N", deploymentNode.getInstances()); + + deploymentNode.setInstances("0..*"); + assertEquals("0..*", deploymentNode.getInstances()); + + deploymentNode.setInstances("1..*"); + assertEquals("1..*", deploymentNode.getInstances()); } } \ No newline at end of file From 68e728a448d8bc10a3244f45c5a1aaf3e1440b44 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 5 Jan 2023 15:58:23 +0000 Subject: [PATCH 350/717] Adds documentation to containers. --- .../src/com/structurizr/model/Container.java | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/structurizr-core/src/com/structurizr/model/Container.java b/structurizr-core/src/com/structurizr/model/Container.java index a00e5a538..17f34ef00 100644 --- a/structurizr-core/src/com/structurizr/model/Container.java +++ b/structurizr-core/src/com/structurizr/model/Container.java @@ -1,19 +1,24 @@ package com.structurizr.model; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.structurizr.documentation.Documentable; +import com.structurizr.documentation.Documentation; +import javax.annotation.Nonnull; import java.util.*; /** * Represents a "container" in the C4 model. */ -public final class Container extends StaticStructureElement { +public final class Container extends StaticStructureElement implements Documentable { private SoftwareSystem parent; private String technology; private Set components = new LinkedHashSet<>(); + private Documentation documentation = new Documentation(); + Container() { } @@ -193,4 +198,22 @@ public Set getDefaultTags() { return new LinkedHashSet<>(Arrays.asList(Tags.ELEMENT, Tags.CONTAINER)); } + /** + * Gets the documentation associated with this container. + * + * @return a Documentation object + */ + public Documentation getDocumentation() { + return documentation; + } + + /** + * Sets the documentation associated with this container. + * + * @param documentation a Documentation object + */ + void setDocumentation(@Nonnull Documentation documentation) { + this.documentation = documentation; + } + } \ No newline at end of file From 36df560e5af6f46245c30229d43ae8c9e97259f6 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 5 Jan 2023 16:00:14 +0000 Subject: [PATCH 351/717] Updated to reflect release. --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index e1177aae0..3a51e47b6 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 1.17.0 (unreleased) +## 1.17.0 (5th January 2023) - Fixes case-sensitivity inconsistencies related to element names and relationship descriptions (#183). - Adds support for setting deployment node instances to positive integers or a range (e.g. 0..1, 0..N, 0..*, 1..N, 1..*, 5..10, etc). From 9ba13f81fba1c5c90d6775fffe49b703807d5314 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 5 Jan 2023 16:29:50 +0000 Subject: [PATCH 352/717] Update Jackson Annotations. --- structurizr-core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structurizr-core/build.gradle b/structurizr-core/build.gradle index 406636d45..9bc594da4 100644 --- a/structurizr-core/build.gradle +++ b/structurizr-core/build.gradle @@ -1,6 +1,6 @@ dependencies { - api 'com.fasterxml.jackson.core:jackson-annotations:2.13.4' + api 'com.fasterxml.jackson.core:jackson-annotations:2.14.1' api 'com.google.code.findbugs:jsr305:3.0.2' api 'commons-logging:commons-logging:1.2' From 4dc1ac0550201dbdc7f425f778ed1f7e7e198f2d Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 11 Jan 2023 07:56:09 -0600 Subject: [PATCH 353/717] Fixes #191 (Layout of relationships is reset when changing the description). --- build.gradle | 2 +- docs/changelog.md | 4 + .../view/DefaultLayoutMergeStrategy.java | 22 ++++ .../view/DefaultLayoutMergeStrategyTests.java | 103 ++++++++++++++++++ 4 files changed, 130 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 404bebda5..ed26532ad 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.17.0' + version = '1.17.1' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 3a51e47b6..2cd623e81 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.17.1 (unreleased) + +- Fixes #191 (Layout of relationships is reset when changing the description). + ## 1.17.0 (5th January 2023) - Fixes case-sensitivity inconsistencies related to element names and relationship descriptions (#183). diff --git a/structurizr-core/src/com/structurizr/view/DefaultLayoutMergeStrategy.java b/structurizr-core/src/com/structurizr/view/DefaultLayoutMergeStrategy.java index 0353269e0..6961e1186 100644 --- a/structurizr-core/src/com/structurizr/view/DefaultLayoutMergeStrategy.java +++ b/structurizr-core/src/com/structurizr/view/DefaultLayoutMergeStrategy.java @@ -130,6 +130,17 @@ private RelationshipView findRelationshipView(View viewWithLayoutInformation, Re } } + // if we got this far, perhaps the relationship description was changed, so try matching on ID instead + for (RelationshipView rv : viewWithLayoutInformation.getRelationships()) { + if ( + rv.getRelationship().getSource().equals(sourceElementWithLayoutInformation) && + rv.getRelationship().getDestination().equals(destinationElementWithLayoutInformation) && + rv.getRelationship().getId().equals(relationshipWithoutLayoutInformation.getId()) + ) { + return rv; + } + } + return null; } @@ -151,6 +162,17 @@ private RelationshipView findRelationshipView(View view, RelationshipView relati } } + // if we got this far, perhaps the relationship description was changed, so try matching on ID instead + for (RelationshipView rv : view.getRelationships()) { + if ( + rv.getRelationship().getSource().equals(sourceElementWithLayoutInformation) && + rv.getRelationship().getDestination().equals(destinationElementWithLayoutInformation) && + rv.getRelationship().getId().equals(relationshipWithoutLayoutInformation.getId()) && + rv.getOrder().equals(relationshipWithoutLayoutInformation.getOrder())) { + return rv; + } + } + return null; } diff --git a/structurizr-core/test/unit/com/structurizr/view/DefaultLayoutMergeStrategyTests.java b/structurizr-core/test/unit/com/structurizr/view/DefaultLayoutMergeStrategyTests.java index 18744e7d7..fdb582f80 100644 --- a/structurizr-core/test/unit/com/structurizr/view/DefaultLayoutMergeStrategyTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/DefaultLayoutMergeStrategyTests.java @@ -2,9 +2,12 @@ import com.structurizr.Workspace; import com.structurizr.model.Container; +import com.structurizr.model.Relationship; import com.structurizr.model.SoftwareSystem; import org.junit.jupiter.api.Test; +import java.util.Collections; + import static org.junit.jupiter.api.Assertions.assertEquals; public class DefaultLayoutMergeStrategyTests { @@ -171,4 +174,104 @@ void copyLayoutInformation_DoesNotThrowAnExceptionWhenAddingAnElementToAView() { strategy.copyLayoutInformation(view1, view2); } + @Test + void copyLayoutInformation_FromStaticViewWhenRelationshipDescriptionHasNotChanged() { + Workspace workspace1 = new Workspace("1", ""); + SoftwareSystem a1 = workspace1.getModel().addSoftwareSystem("A"); + SoftwareSystem b1 = workspace1.getModel().addSoftwareSystem("B"); + Relationship relationship1 = a1.uses(b1, "Uses"); + SystemLandscapeView view1 = workspace1.getViews().createSystemLandscapeView("Key", "Description"); + view1.addAllElements(); + view1.getRelationshipView(relationship1).setVertices(Collections.singletonList(new Vertex(123, 456))); + + Workspace workspace2 = new Workspace("2", ""); + SoftwareSystem a2 = workspace2.getModel().addSoftwareSystem("A"); + SoftwareSystem b2 = workspace2.getModel().addSoftwareSystem("B"); + Relationship relationship2 = a2.uses(b2, "Uses"); + SystemLandscapeView view2 = workspace2.getViews().createSystemLandscapeView("Key", "Description"); + view2.addAllElements(); + + DefaultLayoutMergeStrategy strategy = new DefaultLayoutMergeStrategy(); + strategy.copyLayoutInformation(view1, view2); + + assertEquals(1, view2.getRelationshipView(relationship2).getVertices().size()); + assertEquals(123, view2.getRelationshipView(relationship2).getVertices().iterator().next().getX()); + assertEquals(456, view2.getRelationshipView(relationship2).getVertices().iterator().next().getY()); + } + + @Test + void copyLayoutInformation_FromStaticViewWhenRelationshipDescriptionHasChanged() { + Workspace workspace1 = new Workspace("1", ""); + SoftwareSystem a1 = workspace1.getModel().addSoftwareSystem("A"); + SoftwareSystem b1 = workspace1.getModel().addSoftwareSystem("B"); + Relationship relationship1 = a1.uses(b1, "Uses"); + SystemLandscapeView view1 = workspace1.getViews().createSystemLandscapeView("Key", "Description"); + view1.addAllElements(); + view1.getRelationshipView(relationship1).setVertices(Collections.singletonList(new Vertex(123, 456))); + + Workspace workspace2 = new Workspace("2", ""); + SoftwareSystem a2 = workspace2.getModel().addSoftwareSystem("A"); + SoftwareSystem b2 = workspace2.getModel().addSoftwareSystem("B"); + Relationship relationship2 = a2.uses(b2, "Reads from and writes to"); + SystemLandscapeView view2 = workspace2.getViews().createSystemLandscapeView("Key", "Description"); + view2.addAllElements(); + + DefaultLayoutMergeStrategy strategy = new DefaultLayoutMergeStrategy(); + strategy.copyLayoutInformation(view1, view2); + + assertEquals(1, view2.getRelationshipView(relationship2).getVertices().size()); + assertEquals(123, view2.getRelationshipView(relationship2).getVertices().iterator().next().getX()); + assertEquals(456, view2.getRelationshipView(relationship2).getVertices().iterator().next().getY()); + } + + @Test + void copyLayoutInformation_FromDynamicViewWhenRelationshipDescriptionHasNotChanged() { + Workspace workspace1 = new Workspace("1", ""); + SoftwareSystem a1 = workspace1.getModel().addSoftwareSystem("A"); + SoftwareSystem b1 = workspace1.getModel().addSoftwareSystem("B"); + Relationship relationship1 = a1.uses(b1, "Uses"); + DynamicView view1 = workspace1.getViews().createDynamicView("Key", "Description"); + RelationshipView rv1 = view1.add(a1, b1); + rv1.setVertices(Collections.singletonList(new Vertex(123, 456))); + + Workspace workspace2 = new Workspace("2", ""); + SoftwareSystem a2 = workspace2.getModel().addSoftwareSystem("A"); + SoftwareSystem b2 = workspace2.getModel().addSoftwareSystem("B"); + Relationship relationship2 = a2.uses(b2, "Uses"); + DynamicView view2 = workspace2.getViews().createDynamicView("Key", "Description"); + RelationshipView rv2 = view2.add(a2, b2); + + DefaultLayoutMergeStrategy strategy = new DefaultLayoutMergeStrategy(); + strategy.copyLayoutInformation(view1, view2); + + assertEquals(1, view2.getRelationshipView(relationship2).getVertices().size()); + assertEquals(123, view2.getRelationshipView(relationship2).getVertices().iterator().next().getX()); + assertEquals(456, view2.getRelationshipView(relationship2).getVertices().iterator().next().getY()); + } + + @Test + void copyLayoutInformation_FromDynamicViewWhenRelationshipDescriptionHasChanged() { + Workspace workspace1 = new Workspace("1", ""); + SoftwareSystem a1 = workspace1.getModel().addSoftwareSystem("A"); + SoftwareSystem b1 = workspace1.getModel().addSoftwareSystem("B"); + Relationship relationship1 = a1.uses(b1, "Uses"); + DynamicView view1 = workspace1.getViews().createDynamicView("Key", "Description"); + RelationshipView rv1 = view1.add(a1, b1); + rv1.setVertices(Collections.singletonList(new Vertex(123, 456))); + + Workspace workspace2 = new Workspace("2", ""); + SoftwareSystem a2 = workspace2.getModel().addSoftwareSystem("A"); + SoftwareSystem b2 = workspace2.getModel().addSoftwareSystem("B"); + Relationship relationship2 = a2.uses(b2, "Uses"); + DynamicView view2 = workspace2.getViews().createDynamicView("Key", "Description"); + RelationshipView rv2 = view2.add(a2, "Reads from and writes to", b2); + + DefaultLayoutMergeStrategy strategy = new DefaultLayoutMergeStrategy(); + strategy.copyLayoutInformation(view1, view2); + + assertEquals(1, view2.getRelationshipView(relationship2).getVertices().size()); + assertEquals(123, view2.getRelationshipView(relationship2).getVertices().iterator().next().getX()); + assertEquals(456, view2.getRelationshipView(relationship2).getVertices().iterator().next().getY()); + } + } \ No newline at end of file From c004e6f003bca8aea10fb72941dd61848400a24b Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 11 Jan 2023 08:18:39 -0600 Subject: [PATCH 354/717] Remove reference to quick-start project as it's going to be deleted. --- README.md | 1 - docs/getting-started.md | 8 +++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index df19c8b72..a8b396223 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,6 @@ or other formats including PlantUML, Mermaid, DOT, and WebSequenceDiagrams via t * [Client-side encryption](docs/client-side-encryption.md) * Related projects * [structurizr-dsl](https://github.com/structurizr/dsl): A text-based DSL for authoring Structurizr workspaces. - * [java-quickstart](https://github.com/structurizr/java-quickstart): A simple starting point for using Structurizr for Java * [structurizr-export](https://github.com/structurizr/export): Export model and views to external formats (e.g. PlantUML, Mermaid, etc). * [structurizr-documentation](https://github.com/structurizr/documentation): Import Markdown/AsciiDoc documentation and ADRs into a Structurizr workspace. * [java-extensions](https://github.com/structurizr/java-extensions): A collection of Structurizr for Java extensions; including the ability to extract software architecture information from code. diff --git a/docs/getting-started.md b/docs/getting-started.md index 5e11b5f7f..5a5c3425f 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -4,17 +4,15 @@ Here is a quick overview of how to get started with Structurizr for Java so that You can find the code at [GettingStarted.java](https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/GettingStarted.java) and the live example workspace at [https://structurizr.com/share/25441](https://structurizr.com/share/25441). -> See the [java-quickstart project](https://github.com/structurizr/java-quickstart) for a quick and simple way to get started with Structurizr for Java. - For more examples, please see [structurizr-examples](https://github.com/structurizr/examples/tree/main/java/src/main/java/com/structurizr/example). ## 1. Dependencies The Structurizr for Java binaries are hosted on [Maven Central](https://repo1.maven.org/maven2/com/structurizr/structurizr-client/) and the dependencies for use with Maven, Ivy, Gradle, etc are as follows. -Name | Description ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-client:1.14.1 | The Structurizr API client library. +Name | Description +---------------------------------- | --------------------------------------------------------------------------------------------------------------------------- +com.structurizr:structurizr-client | The Structurizr API client library. ## 2. Create a Java program From 4a92c8e3c6f2b9b16c3f65ccaa5592dab970da3b Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 14 Jan 2023 16:38:51 +0000 Subject: [PATCH 355/717] Remove unused class. --- .../src/com/structurizr/view/ColorPair.java | 43 -------- .../com/structurizr/view/ColorPairTests.java | 97 ------------------- 2 files changed, 140 deletions(-) delete mode 100644 structurizr-core/src/com/structurizr/view/ColorPair.java delete mode 100644 structurizr-core/test/unit/com/structurizr/view/ColorPairTests.java diff --git a/structurizr-core/src/com/structurizr/view/ColorPair.java b/structurizr-core/src/com/structurizr/view/ColorPair.java deleted file mode 100644 index 643501a25..000000000 --- a/structurizr-core/src/com/structurizr/view/ColorPair.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.structurizr.view; - -/** - * Represents a pair of colours: background and foreground. - */ -public final class ColorPair { - - private String background; - private String foreground; - - ColorPair() { - } - - public ColorPair(String background, String foreground) { - setBackground(background); - setForeground(foreground); - } - - public String getBackground() { - return background; - } - - public void setBackground(String background) { - if (Color.isHexColorCode(background)) { - this.background = background.toLowerCase(); - } else { - throw new IllegalArgumentException("'" + background + "' is not a valid hex color code."); - } - } - - public String getForeground() { - return foreground; - } - - public void setForeground(String foreground) { - if (Color.isHexColorCode(foreground)) { - this.foreground = foreground.toLowerCase(); - } else { - throw new IllegalArgumentException("'" + foreground + "' is not a valid hex color code."); - } - } - -} diff --git a/structurizr-core/test/unit/com/structurizr/view/ColorPairTests.java b/structurizr-core/test/unit/com/structurizr/view/ColorPairTests.java deleted file mode 100644 index 215f1c36b..000000000 --- a/structurizr-core/test/unit/com/structurizr/view/ColorPairTests.java +++ /dev/null @@ -1,97 +0,0 @@ -package com.structurizr.view; - -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; - -public class ColorPairTests { - - @Test - void construction() { - ColorPair colorPair = new ColorPair("#ffffff", "#000000"); - assertEquals("#ffffff", colorPair.getBackground()); - assertEquals("#000000", colorPair.getForeground()); - } - - @Test - void setBackground_WithAValidHtmlColorCode() { - ColorPair colorPair = new ColorPair(); - colorPair.setBackground("#ffffff"); - assertEquals("#ffffff", colorPair.getBackground()); - } - - @Test - void setBackground_ThrowsAnException_WhenANullHtmlColorCodeIsSpecified() { - try { - ColorPair colorPair = new ColorPair(); - colorPair.setBackground(null); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("'null' is not a valid hex color code.", iae.getMessage()); - } - } - - @Test - void setBackground_ThrowsAnException_WhenAnEmptyHtmlColorCodeIsSpecified() { - try { - ColorPair colorPair = new ColorPair(); - colorPair.setBackground(""); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("'' is not a valid hex color code.", iae.getMessage()); - } - } - - @Test - void setBackground_ThrowsAnException_WhenAnInvalidHtmlColorCodeIsSpecified() { - try { - ColorPair colorPair = new ColorPair(); - colorPair.setBackground("ffffff"); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("'ffffff' is not a valid hex color code.", iae.getMessage()); - } - } - - @Test - void setForeground_WithAValidHtmlColorCode() { - ColorPair colorPair = new ColorPair(); - colorPair.setForeground("#000000"); - assertEquals("#000000", colorPair.getForeground()); - } - - @Test - void setForeground_ThrowsAnException_WhenANullHtmlColorCodeIsSpecified() { - try { - ColorPair colorPair = new ColorPair(); - colorPair.setForeground(null); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("'null' is not a valid hex color code.", iae.getMessage()); - } - } - - @Test - void setForeground_ThrowsAnException_WhenAnEmptyHtmlColorCodeIsSpecified() { - try { - ColorPair colorPair = new ColorPair(); - colorPair.setForeground(""); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("'' is not a valid hex color code.", iae.getMessage()); - } - } - - @Test - void setForeground_ThrowsAnException_WhenAnInvalidHtmlColorCodeIsSpecified() { - try { - ColorPair colorPair = new ColorPair(); - colorPair.setForeground("000000"); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("'000000' is not a valid hex color code.", iae.getMessage()); - } - } - -} From de09f107d547d06e170aa29a1be7a19013a495ee Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 14 Jan 2023 20:46:16 +0000 Subject: [PATCH 356/717] Adds support for using (CSS/HTML) named colors instead of hex color codes (#192). --- build.gradle | 2 +- docs/changelog.md | 3 +- .../src/com/structurizr/view/Color.java | 160 ++++++++++++++++++ .../com/structurizr/view/ElementStyle.java | 30 +++- .../structurizr/view/RelationshipStyle.java | 8 +- .../unit/com/structurizr/view/ColorTests.java | 13 +- .../structurizr/view/ElementStyleTests.java | 54 +++++- .../view/RelationshipStyleTests.java | 22 ++- 8 files changed, 271 insertions(+), 21 deletions(-) diff --git a/build.gradle b/build.gradle index ed26532ad..a5eaf58a8 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.17.1' + version = '1.18.0' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 2cd623e81..91eecc57c 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,8 +1,9 @@ # Changelog -## 1.17.1 (unreleased) +## 1.18.0 (unreleased) - Fixes #191 (Layout of relationships is reset when changing the description). +- Adds support for using (CSS/HTML) named colors instead of hex color codes (#192). ## 1.17.0 (5th January 2023) diff --git a/structurizr-core/src/com/structurizr/view/Color.java b/structurizr-core/src/com/structurizr/view/Color.java index c224d3f40..414660fd2 100644 --- a/structurizr-core/src/com/structurizr/view/Color.java +++ b/structurizr-core/src/com/structurizr/view/Color.java @@ -1,9 +1,169 @@ package com.structurizr.view; +import java.util.HashMap; +import java.util.Map; + public class Color { + private static final Map NAMED_COLORS = new HashMap<>(); + public static boolean isHexColorCode(String colorAsString) { return colorAsString != null && colorAsString.matches("^#[A-Fa-f0-9]{6}"); } + public static String fromColorNameToHexColorCode(String name) { + return NAMED_COLORS.getOrDefault(name, null); + } + + static { + NAMED_COLORS.put("aliceblue", "#F0F8FF"); + NAMED_COLORS.put("antiquewhite", "#FAEBD7"); + NAMED_COLORS.put("aqua", "#00FFFF"); + NAMED_COLORS.put("aquamarine", "#7FFFD4"); + NAMED_COLORS.put("azure", "#F0FFFF"); + NAMED_COLORS.put("beige", "#F5F5DC"); + NAMED_COLORS.put("bisque", "#FFE4C4"); + NAMED_COLORS.put("black", "#000000"); + NAMED_COLORS.put("blanchedalmond", "#FFEBCD"); + NAMED_COLORS.put("blue", "#0000FF"); + NAMED_COLORS.put("blueviolet", "#8A2BE2"); + NAMED_COLORS.put("brown", "#A52A2A"); + NAMED_COLORS.put("burlywood", "#DEB887"); + NAMED_COLORS.put("cadetblue", "#5F9EA0"); + NAMED_COLORS.put("chartreuse", "#7FFF00"); + NAMED_COLORS.put("chocolate", "#D2691E"); + NAMED_COLORS.put("coral", "#FF7F50"); + NAMED_COLORS.put("cornflowerblue", "#6495ED"); + NAMED_COLORS.put("cornsilk", "#FFF8DC"); + NAMED_COLORS.put("crimson", "#DC143C"); + NAMED_COLORS.put("cyan", "#00FFFF"); + NAMED_COLORS.put("darkblue", "#00008B"); + NAMED_COLORS.put("darkcyan", "#008B8B"); + NAMED_COLORS.put("darkgoldenrod", "#B8860B"); + NAMED_COLORS.put("darkgray", "#A9A9A9"); + NAMED_COLORS.put("darkgreen", "#006400"); + NAMED_COLORS.put("darkgrey", "#A9A9A9"); + NAMED_COLORS.put("darkkhaki", "#BDB76B"); + NAMED_COLORS.put("darkmagenta", "#8B008B"); + NAMED_COLORS.put("darkolivegreen", "#556B2F"); + NAMED_COLORS.put("darkorange", "#FF8C00"); + NAMED_COLORS.put("darkorchid", "#9932CC"); + NAMED_COLORS.put("darkred", "#8B0000"); + NAMED_COLORS.put("darksalmon", "#E9967A"); + NAMED_COLORS.put("darkseagreen", "#8FBC8F"); + NAMED_COLORS.put("darkslateblue", "#483D8B"); + NAMED_COLORS.put("darkslategray", "#2F4F4F"); + NAMED_COLORS.put("darkslategrey", "#2F4F4F"); + NAMED_COLORS.put("darkturquoise", "#00CED1"); + NAMED_COLORS.put("darkviolet", "#9400D3"); + NAMED_COLORS.put("deeppink", "#FF1493"); + NAMED_COLORS.put("deepskyblue", "#00BFFF"); + NAMED_COLORS.put("dimgray", "#696969"); + NAMED_COLORS.put("dimgrey", "#696969"); + NAMED_COLORS.put("dodgerblue", "#1E90FF"); + NAMED_COLORS.put("firebrick", "#B22222"); + NAMED_COLORS.put("floralwhite", "#FFFAF0"); + NAMED_COLORS.put("forestgreen", "#228B22"); + NAMED_COLORS.put("fuchsia", "#FF00FF"); + NAMED_COLORS.put("gainsboro", "#DCDCDC"); + NAMED_COLORS.put("ghostwhite", "#F8F8FF"); + NAMED_COLORS.put("gold", "#FFD700"); + NAMED_COLORS.put("goldenrod", "#DAA520"); + NAMED_COLORS.put("gray", "#808080"); + NAMED_COLORS.put("green", "#008000"); + NAMED_COLORS.put("greenyellow", "#ADFF2F"); + NAMED_COLORS.put("grey", "#808080"); + NAMED_COLORS.put("honeydew", "#F0FFF0"); + NAMED_COLORS.put("hotpink", "#FF69B4"); + NAMED_COLORS.put("indianred", "#CD5C5C"); + NAMED_COLORS.put("indigo", "#4B0082"); + NAMED_COLORS.put("ivory", "#FFFFF0"); + NAMED_COLORS.put("khaki", "#F0E68C"); + NAMED_COLORS.put("lavender", "#E6E6FA"); + NAMED_COLORS.put("lavenderblush", "#FFF0F5"); + NAMED_COLORS.put("lawngreen", "#7CFC00"); + NAMED_COLORS.put("lemonchiffon", "#FFFACD"); + NAMED_COLORS.put("lightblue", "#ADD8E6"); + NAMED_COLORS.put("lightcoral", "#F08080"); + NAMED_COLORS.put("lightcyan", "#E0FFFF"); + NAMED_COLORS.put("lightgoldenrodyellow", "#FAFAD2"); + NAMED_COLORS.put("lightgray", "#D3D3D3"); + NAMED_COLORS.put("lightgreen", "#90EE90"); + NAMED_COLORS.put("lightgrey", "#D3D3D3"); + NAMED_COLORS.put("lightpink", "#FFB6C1"); + NAMED_COLORS.put("lightsalmon", "#FFA07A"); + NAMED_COLORS.put("lightseagreen", "#20B2AA"); + NAMED_COLORS.put("lightskyblue", "#87CEFA"); + NAMED_COLORS.put("lightslategray", "#778899"); + NAMED_COLORS.put("lightslategrey", "#778899"); + NAMED_COLORS.put("lightsteelblue", "#B0C4DE"); + NAMED_COLORS.put("lightyellow", "#FFFFE0"); + NAMED_COLORS.put("lime", "#00FF00"); + NAMED_COLORS.put("limegreen", "#32CD32"); + NAMED_COLORS.put("linen", "#FAF0E6"); + NAMED_COLORS.put("magenta", "#FF00FF"); + NAMED_COLORS.put("maroon", "#800000"); + NAMED_COLORS.put("mediumaquamarine", "#66CDAA"); + NAMED_COLORS.put("mediumblue", "#0000CD"); + NAMED_COLORS.put("mediumorchid", "#BA55D3"); + NAMED_COLORS.put("mediumpurple", "#9370DB"); + NAMED_COLORS.put("mediumseagreen", "#3CB371"); + NAMED_COLORS.put("mediumslateblue", "#7B68EE"); + NAMED_COLORS.put("mediumspringgreen", "#00FA9A"); + NAMED_COLORS.put("mediumturquoise", "#48D1CC"); + NAMED_COLORS.put("mediumvioletred", "#C71585"); + NAMED_COLORS.put("midnightblue", "#191970"); + NAMED_COLORS.put("mintcream", "#F5FFFA"); + NAMED_COLORS.put("mistyrose", "#FFE4E1"); + NAMED_COLORS.put("moccasin", "#FFE4B5"); + NAMED_COLORS.put("navajowhite", "#FFDEAD"); + NAMED_COLORS.put("navy", "#000080"); + NAMED_COLORS.put("oldlace", "#FDF5E6"); + NAMED_COLORS.put("olive", "#808000"); + NAMED_COLORS.put("olivedrab", "#6B8E23"); + NAMED_COLORS.put("orange", "#FFA500"); + NAMED_COLORS.put("orangered", "#FF4500"); + NAMED_COLORS.put("orchid", "#DA70D6"); + NAMED_COLORS.put("palegoldenrod", "#EEE8AA"); + NAMED_COLORS.put("palegreen", "#98FB98"); + NAMED_COLORS.put("paleturquoise", "#AFEEEE"); + NAMED_COLORS.put("palevioletred", "#DB7093"); + NAMED_COLORS.put("papayawhip", "#FFEFD5"); + NAMED_COLORS.put("peachpuff", "#FFDAB9"); + NAMED_COLORS.put("peru", "#CD853F"); + NAMED_COLORS.put("pink", "#FFC0CB"); + NAMED_COLORS.put("plum", "#DDA0DD"); + NAMED_COLORS.put("powderblue", "#B0E0E6"); + NAMED_COLORS.put("purple", "#800080"); + NAMED_COLORS.put("rebeccapurple", "#663399"); + NAMED_COLORS.put("red", "#FF0000"); + NAMED_COLORS.put("rosybrown", "#BC8F8F"); + NAMED_COLORS.put("royalblue", "#4169E1"); + NAMED_COLORS.put("saddlebrown", "#8B4513"); + NAMED_COLORS.put("salmon", "#FA8072"); + NAMED_COLORS.put("sandybrown", "#F4A460"); + NAMED_COLORS.put("seagreen", "#2E8B57"); + NAMED_COLORS.put("seashell", "#FFF5EE"); + NAMED_COLORS.put("sienna", "#A0522D"); + NAMED_COLORS.put("silver", "#C0C0C0"); + NAMED_COLORS.put("skyblue", "#87CEEB"); + NAMED_COLORS.put("slateblue", "#6A5ACD"); + NAMED_COLORS.put("slategray", "#708090"); + NAMED_COLORS.put("slategrey", "#708090"); + NAMED_COLORS.put("snow", "#FFFAFA"); + NAMED_COLORS.put("springgreen", "#00FF7F"); + NAMED_COLORS.put("steelblue", "#4682B4"); + NAMED_COLORS.put("tan", "#D2B48C"); + NAMED_COLORS.put("teal", "#008080"); + NAMED_COLORS.put("thistle", "#D8BFD8"); + NAMED_COLORS.put("tomato", "#FF6347"); + NAMED_COLORS.put("turquoise", "#40E0D0"); + NAMED_COLORS.put("violet", "#EE82EE"); + NAMED_COLORS.put("wheat", "#F5DEB3"); + NAMED_COLORS.put("white", "#FFFFFF"); + NAMED_COLORS.put("whitesmoke", "#F5F5F5"); + NAMED_COLORS.put("yellow", "#FFFF00"); + NAMED_COLORS.put("yellowgreen", "#9ACD32"); + } + } \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/view/ElementStyle.java b/structurizr-core/src/com/structurizr/view/ElementStyle.java index bbe4b6438..70299a5fe 100644 --- a/structurizr-core/src/com/structurizr/view/ElementStyle.java +++ b/structurizr-core/src/com/structurizr/view/ElementStyle.java @@ -132,11 +132,17 @@ public String getBackground() { return background; } - public void setBackground(String background) { - if (Color.isHexColorCode(background)) { - this.background = background.toLowerCase(); + public void setBackground(String color) { + if (Color.isHexColorCode(color)) { + this.background = color.toLowerCase(); } else { - throw new IllegalArgumentException(background + " is not a valid hex colour code."); + String hexColorCode = Color.fromColorNameToHexColorCode(color); + + if (hexColorCode != null) { + this.background = hexColorCode.toLowerCase(); + } else { + throw new IllegalArgumentException(color + " is not a valid hex colour code or HTML colour name."); + } } } @@ -158,7 +164,13 @@ public void setStroke(String color) { if (Color.isHexColorCode(color)) { this.stroke = color.toLowerCase(); } else { - throw new IllegalArgumentException(color + " is not a valid hex colour code."); + String hexColorCode = Color.fromColorNameToHexColorCode(color); + + if (hexColorCode != null) { + this.stroke = hexColorCode.toLowerCase(); + } else { + throw new IllegalArgumentException(color + " is not a valid hex colour code or HTML colour name."); + } } } @@ -204,7 +216,13 @@ public void setColor(String color) { if (Color.isHexColorCode(color)) { this.color = color.toLowerCase(); } else { - throw new IllegalArgumentException(color + " is not a valid hex colour code."); + String hexColorCode = Color.fromColorNameToHexColorCode(color); + + if (hexColorCode != null) { + this.color = hexColorCode.toLowerCase(); + } else { + throw new IllegalArgumentException(color + " is not a valid hex colour code or HTML colour name."); + } } } diff --git a/structurizr-core/src/com/structurizr/view/RelationshipStyle.java b/structurizr-core/src/com/structurizr/view/RelationshipStyle.java index bea120e0a..d32e6ee30 100644 --- a/structurizr-core/src/com/structurizr/view/RelationshipStyle.java +++ b/structurizr-core/src/com/structurizr/view/RelationshipStyle.java @@ -83,7 +83,13 @@ public void setColor(String color) { if (Color.isHexColorCode(color)) { this.color = color.toLowerCase(); } else { - throw new IllegalArgumentException(color + " is not a valid hex colour code."); + String hexColorCode = Color.fromColorNameToHexColorCode(color); + + if (hexColorCode != null) { + this.color = hexColorCode.toLowerCase(); + } else { + throw new IllegalArgumentException(color + " is not a valid hex colour code or HTML colour name."); + } } } diff --git a/structurizr-core/test/unit/com/structurizr/view/ColorTests.java b/structurizr-core/test/unit/com/structurizr/view/ColorTests.java index ad295c6db..4811d9643 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ColorTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ColorTests.java @@ -2,8 +2,7 @@ import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; public class ColorTests { @@ -31,4 +30,14 @@ void isHexColorCode_ReturnsTrue_WhenPassedAnValidString() { assertTrue(Color.isHexColorCode("#123456")); } + @Test + void fromColorNameToHexColorCode_ReturnsNull_WhenPassedAnInvalidName() { + assertNull(Color.fromColorNameToHexColorCode("hello world")); + } + + @Test + void fromColorNameToHexColorCode_ReturnsAHexColorCode_WhenPassedAnValidName() { + assertEquals("#FFFF00", Color.fromColorNameToHexColorCode("yellow")); + } + } \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/view/ElementStyleTests.java b/structurizr-core/test/unit/com/structurizr/view/ElementStyleTests.java index 83b6768ec..c26c44509 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ElementStyleTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ElementStyleTests.java @@ -77,11 +77,25 @@ void color_SetsTheColorProperty_WhenAValidHexColorCodeIsSpecified() { assertEquals("#123456", style.getColor()); } + @Test + void setColor_SetsTheColorProperty_WhenAValidColorNameIsSpecified() { + ElementStyle style = new ElementStyle(); + style.setColor("yellow"); + assertEquals("#ffff00", style.getColor()); + } + + @Test + void color_SetsTheColorProperty_WhenAValidColorNameIsSpecified() { + ElementStyle style = new ElementStyle(); + style.color("yellow"); + assertEquals("#ffff00", style.getColor()); + } + @Test void setColor_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { assertThrows(IllegalArgumentException.class, () -> { ElementStyle style = new ElementStyle(); - style.setColor("white"); + style.setColor("hello"); }); } @@ -89,7 +103,7 @@ void setColor_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { void color_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { assertThrows(IllegalArgumentException.class, () -> { ElementStyle style = new ElementStyle(); - style.color("white"); + style.color("hello"); }); } @@ -119,11 +133,25 @@ void background_SetsTheBackgroundProperty_WhenAValidHexColorCodeIsSpecified() { assertEquals("#123456", style.getBackground()); } + @Test + void setBackground_SetsTheBackgroundProperty_WhenAValidColorNameIsSpecified() { + ElementStyle style = new ElementStyle(); + style.setBackground("yellow"); + assertEquals("#ffff00", style.getBackground()); + } + + @Test + void background_SetsTheBackgroundProperty_WhenAValidColorNameIsSpecified() { + ElementStyle style = new ElementStyle(); + style.background("yellow"); + assertEquals("#ffff00", style.getBackground()); + } + @Test void setBackground_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { assertThrows(IllegalArgumentException.class, () -> { ElementStyle style = new ElementStyle(); - style.setBackground("white"); + style.setBackground("hello"); }); } @@ -131,7 +159,7 @@ void setBackground_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { void background_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { assertThrows(IllegalArgumentException.class, () -> { ElementStyle style = new ElementStyle(); - style.background("white"); + style.background("hello"); }); } @@ -204,11 +232,25 @@ void Stroke_SetsTheStrokeProperty_WhenAValidHexColorCodeIsSpecified() { assertEquals("#123456", style.getStroke()); } + @Test + void setStroke_SetsTheStrokeProperty_WhenAValidColorNameIsSpecified() { + ElementStyle style = new ElementStyle(); + style.setStroke("yellow"); + assertEquals("#ffff00", style.getStroke()); + } + + @Test + void Stroke_SetsTheStrokeProperty_WhenAValidColorNameIsSpecified() { + ElementStyle style = new ElementStyle(); + style.stroke("yellow"); + assertEquals("#ffff00", style.getStroke()); + } + @Test void setStroke_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { assertThrows(IllegalArgumentException.class, () -> { ElementStyle style = new ElementStyle(); - style.setStroke("white"); + style.setStroke("hello"); }); } @@ -216,7 +258,7 @@ void setStroke_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { void Stroke_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { assertThrows(IllegalArgumentException.class, () -> { ElementStyle style = new ElementStyle(); - style.stroke("white"); + style.stroke("hello"); }); } diff --git a/structurizr-core/test/unit/com/structurizr/view/RelationshipStyleTests.java b/structurizr-core/test/unit/com/structurizr/view/RelationshipStyleTests.java index 46e559ad3..e34cff717 100644 --- a/structurizr-core/test/unit/com/structurizr/view/RelationshipStyleTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/RelationshipStyleTests.java @@ -117,18 +117,32 @@ void color_SetsTheColorProperty_WhenAValidHexColorCodeIsSpecified() { } @Test - void setColor_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { + void setColor_SetsTheColorProperty_WhenAValidColorNameIsSpecified() { + RelationshipStyle style = new RelationshipStyle(); + style.setColor("yellow"); + assertEquals("#ffff00", style.getColor()); + } + + @Test + void color_SetsTheColorProperty_WhenAValidColorNameIsSpecified() { + RelationshipStyle style = new RelationshipStyle(); + style.color("yellow"); + assertEquals("#ffff00", style.getColor()); + } + + @Test + void setColor_ThrowsAnException_WhenAnInvalidColorIsSpecified() { assertThrows(IllegalArgumentException.class, () -> { RelationshipStyle style = new RelationshipStyle(); - style.setColor("white"); + style.setColor("hello"); }); } @Test - void color_ThrowsAnException_WhenAnInvalidHexColorCodeIsSpecified() { + void color_ThrowsAnException_WhenAnInvalidColorIsSpecified() { assertThrows(IllegalArgumentException.class, () -> { RelationshipStyle style = new RelationshipStyle(); - style.color("white"); + style.color("hello"); }); } From b259f166743e2282f0f7d044f53696d5e4d6982c Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 15 Jan 2023 12:32:43 +0000 Subject: [PATCH 357/717] Updated to reflect release. --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 91eecc57c..87fbd39b0 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 1.18.0 (unreleased) +## 1.18.0 (15th January 2023) - Fixes #191 (Layout of relationships is reset when changing the description). - Adds support for using (CSS/HTML) named colors instead of hex color codes (#192). From d466ec3e615787cac7d3385b2080b9c944456a8d Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 22 Jan 2023 11:57:40 +0000 Subject: [PATCH 358/717] Fixes #192 (Named colours are case-sensitive). --- docs/changelog.md | 4 ++++ structurizr-core/src/com/structurizr/view/Color.java | 6 +++++- .../test/unit/com/structurizr/view/ColorTests.java | 7 ++++++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 87fbd39b0..90e0d4913 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.19.0 (unreleased + +- Fixes #192 (Named colours are case-sensitive). + ## 1.18.0 (15th January 2023) - Fixes #191 (Layout of relationships is reset when changing the description). diff --git a/structurizr-core/src/com/structurizr/view/Color.java b/structurizr-core/src/com/structurizr/view/Color.java index 414660fd2..c876ca294 100644 --- a/structurizr-core/src/com/structurizr/view/Color.java +++ b/structurizr-core/src/com/structurizr/view/Color.java @@ -12,7 +12,11 @@ public static boolean isHexColorCode(String colorAsString) { } public static String fromColorNameToHexColorCode(String name) { - return NAMED_COLORS.getOrDefault(name, null); + if (name != null) { + return NAMED_COLORS.getOrDefault(name.toLowerCase(), null); + } else { + return null; + } } static { diff --git a/structurizr-core/test/unit/com/structurizr/view/ColorTests.java b/structurizr-core/test/unit/com/structurizr/view/ColorTests.java index 4811d9643..660cfd40e 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ColorTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ColorTests.java @@ -36,8 +36,13 @@ void fromColorNameToHexColorCode_ReturnsNull_WhenPassedAnInvalidName() { } @Test - void fromColorNameToHexColorCode_ReturnsAHexColorCode_WhenPassedAnValidName() { + void fromColorNameToHexColorCode_ReturnsAHexColorCode_WhenPassedAValidName() { assertEquals("#FFFF00", Color.fromColorNameToHexColorCode("yellow")); } + @Test + void fromColorNameToHexColorCode_ReturnsAHexColorCode_WhenPassedAValidNameInMixedCase() { + assertEquals("#FFFF00", Color.fromColorNameToHexColorCode("Yellow")); + } + } \ No newline at end of file From 77b1101a808ade3abd3a444ce539f0cbbd0f1005 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 22 Jan 2023 12:01:32 +0000 Subject: [PATCH 359/717] Fixes #196 (Named colours are case-sensitive). --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 90e0d4913..0d4a97247 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -2,7 +2,7 @@ ## 1.19.0 (unreleased -- Fixes #192 (Named colours are case-sensitive). +- Fixes #196 (Named colours are case-sensitive). ## 1.18.0 (15th January 2023) From ab8606d44c739a07aec1eb11d52eb7a83309d6a3 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 26 Jan 2023 16:02:31 +0000 Subject: [PATCH 360/717] Updated to reflect release. --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 0d4a97247..9969299c0 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 1.19.0 (unreleased +## 1.19.0 (26th January 2023) - Fixes #196 (Named colours are case-sensitive). From 3e6123959996fe2f17f3eae6012e60ffcd939791 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 26 Jan 2023 16:03:15 +0000 Subject: [PATCH 361/717] Updated to reflect release. --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index a5eaf58a8..8323b965f 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.18.0' + version = '1.19.0' repositories { mavenCentral() From 1804c56425922d82c621078141a53296d4fad754 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 27 Jan 2023 11:22:20 +0000 Subject: [PATCH 362/717] Fixes #186. --- structurizr-core/src/com/structurizr/model/Location.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/structurizr-core/src/com/structurizr/model/Location.java b/structurizr-core/src/com/structurizr/model/Location.java index 524bb405c..e216c7f14 100644 --- a/structurizr-core/src/com/structurizr/model/Location.java +++ b/structurizr-core/src/com/structurizr/model/Location.java @@ -2,7 +2,11 @@ /** * Represents the location of an element with regards to a specific viewpoint. - * For example, "our customers are external to our enterprise". + * + * Diagram renderers may use this information in a different way, but generally it will be used to mark + * an element as being outside of the enterprise boundary. For example, "our customers are external to our enterprise": + * - https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/bigbankplc/BigBankPlc.java#L36 + * - https://structurizr.com/share/28201/diagrams#SystemLandscape */ public enum Location { From 527275ed4b3b80f7fe0c1318aaf09c1814fa9cff Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 27 Jan 2023 11:25:08 +0000 Subject: [PATCH 363/717] Fixes #185. --- structurizr-core/src/com/structurizr/model/Model.java | 1 + 1 file changed, 1 insertion(+) diff --git a/structurizr-core/src/com/structurizr/model/Model.java b/structurizr-core/src/com/structurizr/model/Model.java index 7c1e4b0c0..d5c06d5ce 100644 --- a/structurizr-core/src/com/structurizr/model/Model.java +++ b/structurizr-core/src/com/structurizr/model/Model.java @@ -41,6 +41,7 @@ public Enterprise getEnterprise() { /** * Sets the enterprise associated with this model. + * This is typically used in conjunction with {@link Location}. * * @param enterprise an Enterprise instance */ From fd56d92e6aec5c5c98461987d3818d2c6e6b6547 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 9 Feb 2023 10:18:26 +0000 Subject: [PATCH 364/717] `ThemeUtils.toJson()` now includes the workspace branding logo and font in the resulting theme. --- docs/changelog.md | 4 +++ .../src/com/structurizr/view/ThemeUtils.java | 17 +++++---- .../com/structurizr/view/ThemeUtilsTests.java | 9 ++++- .../src/com/structurizr/view/Theme.java | 35 +++++++++++++++++++ 4 files changed, 57 insertions(+), 8 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 9969299c0..cd0fd9349 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.20.0 (unreleased) + +- `ThemeUtils.toJson()` now includes the workspace branding logo and font in the resulting theme. + ## 1.19.0 (26th January 2023) - Fixes #196 (Named colours are case-sensitive). diff --git a/structurizr-client/src/com/structurizr/view/ThemeUtils.java b/structurizr-client/src/com/structurizr/view/ThemeUtils.java index 6f7c79c8d..22c177e62 100644 --- a/structurizr-client/src/com/structurizr/view/ThemeUtils.java +++ b/structurizr-client/src/com/structurizr/view/ThemeUtils.java @@ -111,13 +111,16 @@ private static void write(Workspace workspace, Writer writer) throws Exception { objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); - writer.write(objectMapper.writeValueAsString( - new Theme( - workspace.getName(), - workspace.getDescription(), - workspace.getViews().getConfiguration().getStyles().getElements(), - workspace.getViews().getConfiguration().getStyles().getRelationships() - ))); + Theme theme = new Theme( + workspace.getName(), + workspace.getDescription(), + workspace.getViews().getConfiguration().getStyles().getElements(), + workspace.getViews().getConfiguration().getStyles().getRelationships() + ); + theme.setFont(workspace.getViews().getConfiguration().getBranding().getFont()); + theme.setLogo(workspace.getViews().getConfiguration().getBranding().getLogo()); + + writer.write(objectMapper.writeValueAsString(theme)); } catch (IOException ioe) { throw new WorkspaceWriterException("Could not write the theme as JSON", ioe); } diff --git a/structurizr-client/test/unit/com/structurizr/view/ThemeUtilsTests.java b/structurizr-client/test/unit/com/structurizr/view/ThemeUtilsTests.java index 2d390a47c..ae1f66800 100644 --- a/structurizr-client/test/unit/com/structurizr/view/ThemeUtilsTests.java +++ b/structurizr-client/test/unit/com/structurizr/view/ThemeUtilsTests.java @@ -52,6 +52,8 @@ void toJson() throws Exception { workspace.getViews().getConfiguration().getStyles().addElementStyle(Tags.ELEMENT).background("#ff0000"); workspace.getViews().getConfiguration().getStyles().addRelationshipStyle(Tags.RELATIONSHIP).color("#ff0000"); + workspace.getViews().getConfiguration().getBranding().setLogo("https://structurizr.com/static/img/structurizr-logo.png"); + workspace.getViews().getConfiguration().getBranding().setFont(new Font("Open Sans", "https://fonts.googleapis.com/css?family=Open+Sans:400,700")); assertEquals("{\n" + " \"name\" : \"Name\",\n" + " \"description\" : \"Description\",\n" + @@ -62,7 +64,12 @@ void toJson() throws Exception { " \"relationships\" : [ {\n" + " \"tag\" : \"Relationship\",\n" + " \"color\" : \"#ff0000\"\n" + - " } ]\n" + + " } ],\n" + + " \"logo\" : \"https://structurizr.com/static/img/structurizr-logo.png\",\n" + + " \"font\" : {\n" + + " \"name\" : \"Open Sans\",\n" + + " \"url\" : \"https://fonts.googleapis.com/css?family=Open+Sans:400,700\"\n" + + " }\n" + "}", ThemeUtils.toJson(workspace)); } diff --git a/structurizr-core/src/com/structurizr/view/Theme.java b/structurizr-core/src/com/structurizr/view/Theme.java index 07484b986..1d30657f6 100644 --- a/structurizr-core/src/com/structurizr/view/Theme.java +++ b/structurizr-core/src/com/structurizr/view/Theme.java @@ -1,6 +1,8 @@ package com.structurizr.view; import com.fasterxml.jackson.annotation.JsonGetter; +import com.structurizr.util.ImageUtils; +import com.structurizr.util.StringUtils; import java.util.Collection; import java.util.LinkedList; @@ -11,6 +13,8 @@ final class Theme { private String description; private Collection elements = new LinkedList<>(); private Collection relationships = new LinkedList<>(); + private String logo; + private Font font; Theme() { } @@ -61,4 +65,35 @@ void setRelationships(Collection relationships) { this.relationships = relationships; } + public String getLogo() { + return logo; + } + + /** + * Sets the URL of an image representing a logo. + * + * @param logo a URL or data URI as a String + */ + public void setLogo(String logo) { + if (StringUtils.isNullOrEmpty(logo)) { + this.logo = null; + } else { + ImageUtils.validateImage(logo); + this.logo = logo.trim(); + } + } + + public Font getFont() { + return font; + } + + /** + * Sets the font to use. + * + * @param font a Font object + */ + public void setFont(Font font) { + this.font = font; + } + } \ No newline at end of file From 66b5ccd835b2d1d0200f269b174dbacc1c2d75eb Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 9 Feb 2023 13:17:28 +0000 Subject: [PATCH 365/717] Adds a "Window" shape. --- structurizr-core/src/com/structurizr/view/Shape.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/structurizr-core/src/com/structurizr/view/Shape.java b/structurizr-core/src/com/structurizr/view/Shape.java index b53c35446..33232c99f 100644 --- a/structurizr-core/src/com/structurizr/view/Shape.java +++ b/structurizr-core/src/com/structurizr/view/Shape.java @@ -14,8 +14,9 @@ public enum Shape { Robot, Folder, WebBrowser, + Window, MobileDevicePortrait, MobileDeviceLandscape, Component -} +} \ No newline at end of file From 5cb02d09013c0f561e0282848ae776dbbe319118 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 12 Feb 2023 16:31:31 +0000 Subject: [PATCH 366/717] Adds support for "image views". --- docs/changelog.md | 2 + .../src/com/structurizr/view/ImageView.java | 176 ++++++++++++++++++ .../src/com/structurizr/view/ViewSet.java | 85 ++++++++- 3 files changed, 261 insertions(+), 2 deletions(-) create mode 100644 structurizr-core/src/com/structurizr/view/ImageView.java diff --git a/docs/changelog.md b/docs/changelog.md index cd0fd9349..d7d0c8b42 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -3,6 +3,8 @@ ## 1.20.0 (unreleased) - `ThemeUtils.toJson()` now includes the workspace branding logo and font in the resulting theme. +- Adds a `Window` shape. +- Adds support for "image views". ## 1.19.0 (26th January 2023) diff --git a/structurizr-core/src/com/structurizr/view/ImageView.java b/structurizr-core/src/com/structurizr/view/ImageView.java new file mode 100644 index 000000000..39da20628 --- /dev/null +++ b/structurizr-core/src/com/structurizr/view/ImageView.java @@ -0,0 +1,176 @@ +package com.structurizr.view; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.structurizr.model.Element; +import com.structurizr.util.ImageUtils; +import com.structurizr.util.StringUtils; + +/** + * A view that has been rendered elsewhere (e.g. PlantUML, Mermaid, Kroki, etc) as a image (e.g. PNG). + */ +public final class ImageView { + + private String key; + private int order; + + private Element element; + private String elementId; + private String content; + private String contentType; + + private String title; + private String description; + + ImageView() { + } + + ImageView(String key) { + setKey(key); + } + + ImageView(Element element, String key) { + this(key); + setElement(element); + } + + /** + * Gets the ID of the element associated with this view. + * + * @return the ID, as a String, or null if not set + */ + public String getElementId() { + if (this.element != null) { + return element.getId(); + } else { + return this.elementId; + } + } + + void setElementId(String elementId) { + this.elementId = elementId; + } + + @JsonIgnore + public Element getElement() { + return element; + } + + void setElement(Element element) { + this.element = element; + } + + /** + * Gets the content of this view (a URL or a data URI). + * + * @return the content, as a String + */ + public String getContent() { + return content; + } + + /** + * Sets the content of this image view, which needs to be a URL or a data URI. + * + * @param content the content of this view + */ + public void setContent(String content) { + if (StringUtils.isNullOrEmpty(content)) { + this.content = null; + } else { + ImageUtils.validateImage(content); + this.content = content.trim(); + } + } + + /** + * Gets the the content type of this view (e.g. "image/png"). + * + * @return the content type, as a String + */ + public String getContentType() { + return contentType; + } + + /** + * Sets the content type of this view (e.g. "image/png"). + * + * @param contentType the content type, as a String + */ + public void setContentType(String contentType) { + this.contentType = contentType; + } + + /** + * Gets the order of this view. + * + * @return a positive integer + */ + public int getOrder() { + return order; + } + + void setOrder(int order) { + this.order = Math.max(1, order); + } + + /** + * Gets the title of this view, if one has been set. + * + * @return the title, as a String + */ + public String getTitle() { + return title; + } + + /** + * Sets the title for this view. + * + * @param title the title, as a String + */ + public void setTitle(String title) { + if (StringUtils.isNullOrEmpty(title)) { + throw new IllegalArgumentException("A title must be specified"); + } + this.title = title; + } + + /** + * Gets the description of this view. + * + * @return the description, as a String + */ + public String getDescription() { + return description; + } + + /** + * Sets the description of this view. + * + * @param description the description, as a string + */ + public void setDescription(String description) { + if (description == null) { + this.description = ""; + } else { + this.description = description; + } + } + + /** + * Gets the identifier for this view. + * + * @return the identifier, as a String + */ + public String getKey() { + return key; + } + + void setKey(String key) { + if (key != null) { + key = key.replaceAll("/", "_"); + } + + this.key = key; + } + +} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/view/ViewSet.java b/structurizr-core/src/com/structurizr/view/ViewSet.java index d68273004..b24878268 100644 --- a/structurizr-core/src/com/structurizr/view/ViewSet.java +++ b/structurizr-core/src/com/structurizr/view/ViewSet.java @@ -28,6 +28,7 @@ public final class ViewSet { private Collection componentViews = new HashSet<>(); private Collection dynamicViews = new HashSet<>(); private Collection deploymentViews = new HashSet<>(); + private Collection imageViews = new HashSet<>(); private Collection filteredViews = new HashSet<>(); @@ -268,12 +269,48 @@ public FilteredView createFilteredView(StaticView view, String key, String descr return filteredView; } + /** + * Creates an image view. + * + * @param key the key for the view (must be unique) + * @return an ImageView object + * @throws IllegalArgumentException if the key is not unique + */ + public ImageView createImageView(String key) { + assertThatTheViewKeyIsSpecifiedAndUnique(key); + + ImageView view = new ImageView(key); + view.setOrder(getNextOrder()); + imageViews.add(view); + return view; + } + + /** + * Creates an image view, where the scope is the specified element. + * + * @param element the Element object representing the scope of the view + * @param key the key for the view (must be unique) + * @return an ImageView object + * @throws IllegalArgumentException if the element is null or the key is not unique + */ + public ImageView createImageView(Element element, String key) { + if (element == null) { + throw new IllegalArgumentException("An element must be specified."); + } + assertThatTheViewKeyIsSpecifiedAndUnique(key); + + ImageView view = new ImageView(element, key); + view.setOrder(getNextOrder()); + imageViews.add(view); + return view; + } + private void assertThatTheViewKeyIsSpecifiedAndUnique(String key) { if (StringUtils.isNullOrEmpty(key)) { throw new IllegalArgumentException("A key must be specified."); } - if (getViewWithKey(key) != null || getFilteredViewWithKey(key) != null) { + if (getViewWithKey(key) != null || getFilteredViewWithKey(key) != null || getImageViewWithKey(key) != null) { throw new IllegalArgumentException("A view with the key " + key + " already exists."); } } @@ -333,6 +370,20 @@ FilteredView getFilteredViewWithKey(String key) { return filteredViews.stream().filter(v -> key.equals(v.getKey())).findFirst().orElse(null); } + /** + * Finds the image view with the specified key, or null if the view does not exist. + * + * @param key the key + * @return a ImageView object, or null if a view with the specified key could not be found + */ + ImageView getImageViewWithKey(String key) { + if (key == null) { + throw new IllegalArgumentException("A key must be specified."); + } + + return imageViews.stream().filter(v -> key.equals(v.getKey())).findFirst().orElse(null); + } + /** * Gets the set of custom views. * @@ -459,7 +510,22 @@ void setDeploymentViews(Set deploymentViews) { } /** - * Gets the set of all views (except filtered views). + * Gets the set of image views. + * + * @return a Collection of ImageView objects + */ + public Collection getImageViews() { + return new HashSet<>(imageViews); + } + + void setImageView(Set imageViews) { + if (imageViews != null) { + this.imageViews = new HashSet<>(imageViews); + } + } + + /** + * Gets the set of all views (except filtered and image views). * * @return a Collection of View objects */ @@ -577,6 +643,20 @@ void hydrate(Model model) { filteredView.setView(view); } + + for (ImageView view : imageViews) { + if (!isNullOrEmpty(view.getElementId())) { + Element element = model.getElement(view.getElementId()); + if (element == null) { + throw new WorkspaceValidationException( + String.format("The image view with key \"%s\" is associated with an element (id=%s), but that element does not exist in the model.", + view.getKey(), view.getElementId()) + ); + } + + view.setElement(element); + } + } } private void hydrateView(View view) { @@ -646,6 +726,7 @@ private synchronized int getNextOrder() { order = Math.max(order, dynamicViews.stream().max(Comparator.comparingInt(View::getOrder)).map(View::getOrder).orElse(0)); order = Math.max(order, deploymentViews.stream().max(Comparator.comparingInt(View::getOrder)).map(View::getOrder).orElse(0)); order = Math.max(order, filteredViews.stream().max(Comparator.comparingInt(FilteredView::getOrder)).map(FilteredView::getOrder).orElse(0)); + order = Math.max(order, imageViews.stream().max(Comparator.comparingInt(ImageView::getOrder)).map(ImageView::getOrder).orElse(0)); return order + 1; } From 0ffd2f9083a319931399eef2a7fcc1c4ef998c14 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 12 Feb 2023 16:31:40 +0000 Subject: [PATCH 367/717] Typo. --- structurizr-core/src/com/structurizr/view/ViewSet.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/structurizr-core/src/com/structurizr/view/ViewSet.java b/structurizr-core/src/com/structurizr/view/ViewSet.java index b24878268..4234e7afa 100644 --- a/structurizr-core/src/com/structurizr/view/ViewSet.java +++ b/structurizr-core/src/com/structurizr/view/ViewSet.java @@ -495,9 +495,9 @@ void setFilteredViews(Set filteredViews) { } /** - * Gets the set of dynamic views. + * Gets the set of deployment views. * - * @return a Collection of DynamicView objects + * @return a Collection of DeploymentView objects */ public Collection getDeploymentViews() { return new HashSet<>(deploymentViews); From 44e15aaceb328fb19363253dbdcb6bd888dd4016 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 13 Feb 2023 08:40:49 +0000 Subject: [PATCH 368/717] Refactoring so that all views extend the View class. --- docs/changelog.md | 7 +- .../src/com/structurizr/view/CustomView.java | 2 +- .../view/DefaultLayoutMergeStrategy.java | 12 +- .../com/structurizr/view/DeploymentView.java | 2 +- .../src/com/structurizr/view/DynamicView.java | 2 +- .../com/structurizr/view/FilteredView.java | 48 +- .../src/com/structurizr/view/ImageView.java | 82 +-- .../structurizr/view/LayoutMergeStrategy.java | 2 +- .../src/com/structurizr/view/ModelView.java | 475 ++++++++++++++++++ .../src/com/structurizr/view/StaticView.java | 2 +- .../src/com/structurizr/view/View.java | 440 +--------------- .../src/com/structurizr/view/ViewSet.java | 49 +- 12 files changed, 531 insertions(+), 592 deletions(-) create mode 100644 structurizr-core/src/com/structurizr/view/ModelView.java diff --git a/docs/changelog.md b/docs/changelog.md index d7d0c8b42..6b8866b18 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -2,9 +2,10 @@ ## 1.20.0 (unreleased) -- `ThemeUtils.toJson()` now includes the workspace branding logo and font in the resulting theme. -- Adds a `Window` shape. -- Adds support for "image views". +- __Breaking change__: Renamed `View` to `ModelView`. +- Added support for "image views". +- Added a `Window` shape. +- `ThemeUtils.toJson()` now includes the workspace branding logo and font in the resulting theme. ## 1.19.0 (26th January 2023) diff --git a/structurizr-core/src/com/structurizr/view/CustomView.java b/structurizr-core/src/com/structurizr/view/CustomView.java index 8a99a6017..ebe20cfa2 100644 --- a/structurizr-core/src/com/structurizr/view/CustomView.java +++ b/structurizr-core/src/com/structurizr/view/CustomView.java @@ -15,7 +15,7 @@ /** * Represents a custom view, containing custom elements. */ -public final class CustomView extends View { +public final class CustomView extends ModelView { private List animations = new ArrayList<>(); diff --git a/structurizr-core/src/com/structurizr/view/DefaultLayoutMergeStrategy.java b/structurizr-core/src/com/structurizr/view/DefaultLayoutMergeStrategy.java index 6961e1186..e4faeb92c 100644 --- a/structurizr-core/src/com/structurizr/view/DefaultLayoutMergeStrategy.java +++ b/structurizr-core/src/com/structurizr/view/DefaultLayoutMergeStrategy.java @@ -33,7 +33,7 @@ public class DefaultLayoutMergeStrategy implements LayoutMergeStrategy { * @param viewWithLayoutInformation the source view (e.g. the version stored by the Structurizr service) * @param viewWithoutLayoutInformation the destination View (e.g. the new version, created locally with code) */ - public void copyLayoutInformation(@Nonnull View viewWithLayoutInformation, @Nonnull View viewWithoutLayoutInformation) { + public void copyLayoutInformation(@Nonnull ModelView viewWithLayoutInformation, @Nonnull ModelView viewWithoutLayoutInformation) { setPaperSizeIfNotSpecified(viewWithLayoutInformation, viewWithoutLayoutInformation); setDimensionsIfNotSpecified(viewWithLayoutInformation, viewWithoutLayoutInformation); @@ -69,13 +69,13 @@ public void copyLayoutInformation(@Nonnull View viewWithLayoutInformation, @Nonn } } - private void setPaperSizeIfNotSpecified(@Nonnull View remoteView, @Nonnull View localView) { + private void setPaperSizeIfNotSpecified(@Nonnull ModelView remoteView, @Nonnull ModelView localView) { if (localView.getPaperSize() == null) { localView.setPaperSize(remoteView.getPaperSize()); } } - private void setDimensionsIfNotSpecified(@Nonnull View remoteView, @Nonnull View localView) { + private void setDimensionsIfNotSpecified(@Nonnull ModelView remoteView, @Nonnull ModelView localView) { if (localView.getDimensions() == null) { localView.setDimensions(remoteView.getDimensions()); } @@ -88,7 +88,7 @@ private void setDimensionsIfNotSpecified(@Nonnull View remoteView, @Nonnull View * @param elementWithoutLayoutInformation the Element to find * @return an ElementView */ - protected ElementView findElementView(View viewWithLayoutInformation, Element elementWithoutLayoutInformation) { + protected ElementView findElementView(ModelView viewWithLayoutInformation, Element elementWithoutLayoutInformation) { // see if we can find an element with the same canonical name in the source view ElementView elementView = viewWithLayoutInformation.getElements().stream().filter(ev -> ev.getElement().getCanonicalName().equals(elementWithoutLayoutInformation.getCanonicalName())).findFirst().orElse(null); @@ -112,7 +112,7 @@ protected ElementView findElementView(View viewWithLayoutInformation, Element el return elementView; } - private RelationshipView findRelationshipView(View viewWithLayoutInformation, Relationship relationshipWithoutLayoutInformation, Map elementMap) { + private RelationshipView findRelationshipView(ModelView viewWithLayoutInformation, Relationship relationshipWithoutLayoutInformation, Map elementMap) { if (!elementMap.containsKey(relationshipWithoutLayoutInformation.getSource()) || !elementMap.containsKey(relationshipWithoutLayoutInformation.getDestination())) { return null; } @@ -144,7 +144,7 @@ private RelationshipView findRelationshipView(View viewWithLayoutInformation, Re return null; } - private RelationshipView findRelationshipView(View view, RelationshipView relationshipWithoutLayoutInformation, Map elementMap) { + private RelationshipView findRelationshipView(ModelView view, RelationshipView relationshipWithoutLayoutInformation, Map elementMap) { if (!elementMap.containsKey(relationshipWithoutLayoutInformation.getRelationship().getSource()) || !elementMap.containsKey(relationshipWithoutLayoutInformation.getRelationship().getDestination())) { return null; } diff --git a/structurizr-core/src/com/structurizr/view/DeploymentView.java b/structurizr-core/src/com/structurizr/view/DeploymentView.java index 02580f0ec..f58ae73cb 100644 --- a/structurizr-core/src/com/structurizr/view/DeploymentView.java +++ b/structurizr-core/src/com/structurizr/view/DeploymentView.java @@ -11,7 +11,7 @@ /** * A deployment view, used to show the mapping of container instances to deployment nodes. */ -public final class DeploymentView extends View { +public final class DeploymentView extends ModelView { private Model model; private String environment = DeploymentElement.DEFAULT_DEPLOYMENT_ENVIRONMENT; diff --git a/structurizr-core/src/com/structurizr/view/DynamicView.java b/structurizr-core/src/com/structurizr/view/DynamicView.java index 14bec348b..6dbb48908 100644 --- a/structurizr-core/src/com/structurizr/view/DynamicView.java +++ b/structurizr-core/src/com/structurizr/view/DynamicView.java @@ -10,7 +10,7 @@ /** * A dynamic view, used to describe behaviour between static elements at runtime. */ -public final class DynamicView extends View { +public final class DynamicView extends ModelView { private Model model; diff --git a/structurizr-core/src/com/structurizr/view/FilteredView.java b/structurizr-core/src/com/structurizr/view/FilteredView.java index 2a9b9a50c..64b70a507 100644 --- a/structurizr-core/src/com/structurizr/view/FilteredView.java +++ b/structurizr-core/src/com/structurizr/view/FilteredView.java @@ -9,15 +9,11 @@ /** * Represents a view on top of a view, which can be used to include or exclude specific elements. */ -public final class FilteredView { +public final class FilteredView extends View { - private View view; + private StaticView view; private String baseViewKey; - private String key; - private int order; - private String description = ""; - private FilterMode mode = FilterMode.Exclude; private Set tags = new HashSet<>(); @@ -26,8 +22,8 @@ public final class FilteredView { FilteredView(StaticView view, String key, String description, FilterMode mode, String... tags) { this.view = view; - this.key = key; - this.description = description; + setKey(key); + setDescription(description); this.mode = mode; this.tags.addAll(Arrays.asList(tags)); } @@ -37,7 +33,7 @@ public View getView() { return view; } - void setView(View view) { + void setView(StaticView view) { this.view = view; } @@ -53,35 +49,6 @@ void setBaseViewKey(String baseViewKey) { this.baseViewKey = baseViewKey; } - public String getKey() { - return key; - } - - void setKey(String key) { - this.key = key; - } - - /** - * Gets the order of this view. - * - * @return a positive integer - */ - public int getOrder() { - return order; - } - - void setOrder(int order) { - this.order = Math.max(1, order); - } - - public String getDescription() { - return description; - } - - void setDescription(String description) { - this.description = description; - } - public FilterMode getMode() { return mode; } @@ -94,4 +61,9 @@ public Set getTags() { return new HashSet<>(tags); } + @Override + public String getName() { + return "Filtered: " + view.getName(); + } + } \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/view/ImageView.java b/structurizr-core/src/com/structurizr/view/ImageView.java index 39da20628..861ce76c9 100644 --- a/structurizr-core/src/com/structurizr/view/ImageView.java +++ b/structurizr-core/src/com/structurizr/view/ImageView.java @@ -8,19 +8,13 @@ /** * A view that has been rendered elsewhere (e.g. PlantUML, Mermaid, Kroki, etc) as a image (e.g. PNG). */ -public final class ImageView { - - private String key; - private int order; +public final class ImageView extends View { private Element element; private String elementId; private String content; private String contentType; - private String title; - private String description; - ImageView() { } @@ -100,77 +94,9 @@ public void setContentType(String contentType) { this.contentType = contentType; } - /** - * Gets the order of this view. - * - * @return a positive integer - */ - public int getOrder() { - return order; - } - - void setOrder(int order) { - this.order = Math.max(1, order); - } - - /** - * Gets the title of this view, if one has been set. - * - * @return the title, as a String - */ - public String getTitle() { - return title; - } - - /** - * Sets the title for this view. - * - * @param title the title, as a String - */ - public void setTitle(String title) { - if (StringUtils.isNullOrEmpty(title)) { - throw new IllegalArgumentException("A title must be specified"); - } - this.title = title; - } - - /** - * Gets the description of this view. - * - * @return the description, as a String - */ - public String getDescription() { - return description; - } - - /** - * Sets the description of this view. - * - * @param description the description, as a string - */ - public void setDescription(String description) { - if (description == null) { - this.description = ""; - } else { - this.description = description; - } - } - - /** - * Gets the identifier for this view. - * - * @return the identifier, as a String - */ - public String getKey() { - return key; - } - - void setKey(String key) { - if (key != null) { - key = key.replaceAll("/", "_"); - } - - this.key = key; + @Override + public String getName() { + return getTitle(); } } \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/view/LayoutMergeStrategy.java b/structurizr-core/src/com/structurizr/view/LayoutMergeStrategy.java index 063f7bf84..ec17d9634 100644 --- a/structurizr-core/src/com/structurizr/view/LayoutMergeStrategy.java +++ b/structurizr-core/src/com/structurizr/view/LayoutMergeStrategy.java @@ -14,6 +14,6 @@ public interface LayoutMergeStrategy { * @param sourceView the source view (e.g. the version stored by the Structurizr service) * @param destinationView the destination View (e.g. the new version, created locally with code) */ - void copyLayoutInformation(@Nonnull View sourceView, @Nonnull View destinationView); + void copyLayoutInformation(@Nonnull ModelView sourceView, @Nonnull ModelView destinationView); } \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/view/ModelView.java b/structurizr-core/src/com/structurizr/view/ModelView.java new file mode 100644 index 000000000..b10a556f7 --- /dev/null +++ b/structurizr-core/src/com/structurizr/view/ModelView.java @@ -0,0 +1,475 @@ +package com.structurizr.view; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.structurizr.model.*; +import com.structurizr.util.StringUtils; + +import javax.annotation.Nonnull; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * The superclass for all views that show elements/relationships from the model, namely: + * + * - System landscape views + * - System context views + * - Container views + * - Dynamic views + * - Deployment views + */ +public abstract class ModelView extends View { + + private static final int DEFAULT_RANK_SEPARATION = 300; + private static final int DEFAULT_NODE_SEPARATION = 300; + + private SoftwareSystem softwareSystem; + private String softwareSystemId; + private PaperSize paperSize = null; + private Dimensions dimensions = null; + private AutomaticLayout automaticLayout = null; + private boolean mergeFromRemote = true; + + private Set elementViews = new LinkedHashSet<>(); + private Set relationshipViews = new LinkedHashSet<>(); + + private LayoutMergeStrategy layoutMergeStrategy = new DefaultLayoutMergeStrategy(); + + private ViewSet viewSet; + + ModelView() { + } + + ModelView(SoftwareSystem softwareSystem, String key, String description) { + this.softwareSystem = softwareSystem; + if (!StringUtils.isNullOrEmpty(key)) { + setKey(key); + } else { + throw new IllegalArgumentException("A key must be specified."); + } + setDescription(description); + } + + /** + * Gets the model that this view belongs to. + * + * @return a Model object + */ + @JsonIgnore + public Model getModel() { + return softwareSystem.getModel(); + } + + /** + * Gets the software system that this view is associated with. + * + * @return a SoftwareSystem object, or null if this view is not associated with a software system (e.g. it's a system landscape view) + */ + @JsonIgnore + public SoftwareSystem getSoftwareSystem() { + return softwareSystem; + } + + void setSoftwareSystem(SoftwareSystem softwareSystem) { + this.softwareSystem = softwareSystem; + } + + /** + * Gets the ID of the software system this view is associated with. + * + * @return the ID, as a String, or null if this view is not associated with a software system (e.g. it's a system landscape view) + */ + public String getSoftwareSystemId() { + if (this.softwareSystem != null) { + return this.softwareSystem.getId(); + } else { + return this.softwareSystemId; + } + } + + void setSoftwareSystemId(String softwareSystemId) { + this.softwareSystemId = softwareSystemId; + } + + /** + * Gets the paper size that should be used to render this view. + * + * @return a PaperSize + */ + public PaperSize getPaperSize() { + return paperSize; + } + + public void setPaperSize(PaperSize paperSize) { + this.paperSize = paperSize; + } + + public Dimensions getDimensions() { + return dimensions; + } + + public void setDimensions(Dimensions dimensions) { + this.dimensions = dimensions; + } + + /** + * Gets the automatic layout settings for this view. + * + * @return an AutomaticLayout object, or null if not enabled + */ + public AutomaticLayout getAutomaticLayout() { + return automaticLayout; + } + + @JsonSetter + void setAutomaticLayout(AutomaticLayout automaticLayout) { + this.automaticLayout = automaticLayout; + } + + /** + * Enables automatic layout for this view, with some default settings. + */ + public void enableAutomaticLayout() { + enableAutomaticLayout(AutomaticLayout.RankDirection.TopBottom, 300, 600, 200, false); + } + + /** + * Enables automatic layout for this view, with the specified settings, using the Dagre implementation. + * + * @param rankDirection the rank direction + * @param rankSeparation the separation between ranks (in pixels, a positive integer) + * @param nodeSeparation the separation between nodes within the same rank (in pixels, a positive integer) + * @param edgeSeparation the separation between edges (in pixels, a positive integer) + * @param vertices whether vertices should be created during automatic layout + */ + public void enableAutomaticLayout(AutomaticLayout.RankDirection rankDirection, int rankSeparation, int nodeSeparation, int edgeSeparation, boolean vertices) { + this.automaticLayout = new AutomaticLayout(AutomaticLayout.Implementation.Dagre, rankDirection, rankSeparation, nodeSeparation, edgeSeparation, vertices); + } + + /** + * Enables automatic layout for this view, with the specified direction, using the Graphviz implementation. + * + * @param rankDirection the rank direction + */ + public void enableAutomaticLayout(AutomaticLayout.RankDirection rankDirection) { + enableAutomaticLayout(rankDirection, DEFAULT_RANK_SEPARATION, DEFAULT_NODE_SEPARATION); + } + + /** + * Enables automatic layout for this view, with the specified settings, using the Graphviz implementation. + * + * @param rankDirection the rank direction + * @param rankSeparation the separation between ranks (in pixels, a positive integer) + * @param nodeSeparation the separation between nodes within the same rank (in pixels, a positive integer) + */ + public void enableAutomaticLayout(AutomaticLayout.RankDirection rankDirection, int rankSeparation, int nodeSeparation) { + this.automaticLayout = new AutomaticLayout(AutomaticLayout.Implementation.Graphviz, rankDirection, rankSeparation, nodeSeparation, 0, false); + } + + /** + * Disables automatic layout for this view. + */ + public void disableAutomaticLayout() { + this.automaticLayout = null; + } + + /** + * Gets whether layout information for this view should be merged from a remote version of the workspace. + * + * @return true if layout information should be merged from the remote workspace, false otherwise + */ + public boolean getMergeFromRemote() { + return mergeFromRemote; + } + + /** + * Specifies whether layout information for this view should be merged from a remote version of the workspace. + * + * @param mergeFromRemote true if layout information should be merged from the remote workspace, false otherwise + */ + @JsonIgnore + public void setMergeFromRemote(boolean mergeFromRemote) { + this.mergeFromRemote = mergeFromRemote; + } + + protected final void addElement(Element element, boolean addRelationships) { + if (element == null) { + throw new IllegalArgumentException("An element must be specified."); + } + + if (getModel().contains(element)) { + checkElementCanBeAdded(element); + elementViews.add(new ElementView(element)); + + if (addRelationships) { + addRelationships(element); + } + } else { + throw new IllegalArgumentException("The element named " + element.getName() + " does not exist in the model associated with this view."); + } + } + + protected abstract void checkElementCanBeAdded(Element element); + + private void addRelationships(Element element) { + Set elements = getElements().stream() + .map(ElementView::getElement) + .collect(Collectors.toSet()); + + // add relationships where the destination exists in the view already + for (Relationship relationship : element.getRelationships()) { + if (elements.contains(relationship.getDestination())) { + this.relationshipViews.add(new RelationshipView(relationship)); + } + } + + // add relationships where the source exists in the view already + for (Element e : elements) { + for (Relationship r : e.getRelationships()) { + if (r.getDestination().equals(element)) { + this.relationshipViews.add(new RelationshipView(r)); + } + } + } + } + + protected void removeElement(Element element) { + if (element == null) { + throw new IllegalArgumentException("An element must be specified."); + } + + if (!canBeRemoved(element)) { + throw new IllegalArgumentException("The element named '" + element.getName() + "' cannot be removed from this view."); + } + + ElementView elementView = new ElementView(element); + elementViews.remove(elementView); + + for (RelationshipView relationshipView : getRelationships()) { + if (relationshipView.getRelationship().getSource().equals(element) || + relationshipView.getRelationship().getDestination().equals(element)) { + remove(relationshipView.getRelationship()); + } + } + } + + protected RelationshipView addRelationship(Relationship relationship) { + if (relationship == null) { + throw new IllegalArgumentException("A relationship must be specified."); + } + + if (isElementInView(relationship.getSource()) && isElementInView(relationship.getDestination())) { + RelationshipView relationshipView = new RelationshipView(relationship); + relationshipViews.add(relationshipView); + + return relationshipView; + } + + return null; + } + + public boolean isElementInView(Element element) { + return this.elementViews.stream().anyMatch(ev -> ev.getElement().equals(element)); + } + + /** + * Removes a relationship from this view. + * + * @param relationship the Relationship to remove + */ + public void remove(Relationship relationship) { + if (relationship != null) { + RelationshipView relationshipView = new RelationshipView(relationship); + relationshipViews.remove(relationshipView); + } + } + + /** + * Removes relationships that are not connected to the specified element. + * + * @param element the Element to test against + */ + public void removeRelationshipsNotConnectedToElement(Element element) { + if (element != null) { + getRelationships().stream() + .map(RelationshipView::getRelationship) + .filter(r -> !r.getSource().equals(element) && !r.getDestination().equals(element)) + .forEach(this::remove); + } + } + + /** + * Gets the set of elements in this view. + * + * @return a Set of ElementView objects + */ + public Set getElements() { + return new HashSet<>(elementViews); + } + + void setElements(Set elementViews) { + if (elementViews != null) { + this.elementViews = new HashSet<>(elementViews); + } + } + + /** + * Gets the set of relationships in this view. + * + * @return a Set of RelationshipView objects + */ + public Set getRelationships() { + return new HashSet<>(this.relationshipViews); + } + + void setRelationships(Set relationshipViews) { + if (relationshipViews != null) { + this.relationshipViews = new HashSet<>(relationshipViews); + } + } + + /** + * Removes all elements that have no relationships to other elements in this view. + */ + public void removeElementsWithNoRelationships() { + Set relationships = getRelationships(); + + Set elementIds = new HashSet<>(); + relationships.forEach(rv -> elementIds.add(rv.getRelationship().getSourceId())); + relationships.forEach(rv -> elementIds.add(rv.getRelationship().getDestinationId())); + + for (ElementView elementView : getElements()) { + if (!elementIds.contains(elementView.getId())) { + removeElement(elementView.getElement()); + } + } + } + + /** + * Sets the strategy used for merging layout information (paper size, x/y positioning, etc) + * from one version of this view to another. + * + * @param layoutMergeStrategy an instance of LayoutMergeStrategy + */ + public void setLayoutMergeStrategy(LayoutMergeStrategy layoutMergeStrategy) { + if (layoutMergeStrategy == null) { + throw new IllegalArgumentException("A LayoutMergeStrategy object must be provided."); + } + + this.layoutMergeStrategy = layoutMergeStrategy; + } + + /** + * Attempts to copy the visual layout information (e.g. x,y coordinates) of elements and relationships + * from the specified source view into this view. + * + * @param source the source View + */ + void copyLayoutInformationFrom(@Nonnull ModelView source) { + layoutMergeStrategy.copyLayoutInformation(source, this); + } + + /** + * Gets the element view for the given element. + * + * @param element the Element to find the ElementView for + * @return an ElementView object, or null if the element doesn't exist in the view + */ + public ElementView getElementView(@Nonnull Element element) { + Optional elementView = this.elementViews.stream().filter(ev -> ev.getId().equals(element.getId())).findFirst(); + return elementView.orElse(null); + } + + /** + * Gets the relationship view for the given relationship. + * + * @param relationship the Relationship to find the RelationshipView for + * @return a RelationshipView object, or null if the relationship doesn't exist in the view + */ + public RelationshipView getRelationshipView(@Nonnull Relationship relationship) { + Optional relationshipView = this.relationshipViews.stream().filter(rv -> rv.getId().equals(relationship.getId())).findFirst(); + return relationshipView.orElse(null); + } + + void setViewSet(@Nonnull ViewSet viewSet) { + this.viewSet = viewSet; + } + + /** + * Gets the view set that this view belongs to. + * + * @return a ViewSet object + */ + @JsonIgnore + public ViewSet getViewSet() { + return viewSet; + } + + protected abstract boolean canBeRemoved(Element element); + + final void checkParentAndChildrenHaveNotAlreadyBeenAdded(StaticStructureElement elementToBeAdded) { + // check a parent hasn't been added already + Set idsOfElementsInView = getElements().stream().map(ElementView::getElement).map(Element::getId).collect(Collectors.toSet()); + + Element parent = elementToBeAdded.getParent(); + while (parent != null) { + if (idsOfElementsInView.contains(parent.getId())) { + throw new ElementNotPermittedInViewException("A parent of " + elementToBeAdded.getName() + " is already in this view."); + } + + parent = parent.getParent(); + } + + // and now check a child hasn't been added already + Set elementParentIds = new HashSet<>(); + for (ElementView elementView : getElements()) { + Element element = elementView.getElement(); + parent = element.getParent(); + while (parent != null) { + elementParentIds.add(parent.getId()); + parent = parent.getParent(); + } + } + + if (elementParentIds.contains(elementToBeAdded.getId())) { + throw new ElementNotPermittedInViewException("A child of " + elementToBeAdded.getName() + " is already in this view."); + } + } + + protected void addNearestNeighbours(Element element, Class typeOfElement) { + if (element == null) { + return; + } + + try { + addElement(element, true); + + Set relationships = getModel().getRelationships(); + relationships.stream().filter(r -> r.getSource().equals(element) && typeOfElement.isInstance(r.getDestination())) + .map(Relationship::getDestination) + .forEach(d -> { + try { + addElement(d, true); + } catch (ElementNotPermittedInViewException e) { + System.out.println(e.getMessage() + " (ignoring " + d.getName() + ")"); + } + }); + + relationships.stream().filter(r -> r.getDestination().equals(element) && typeOfElement.isInstance(r.getSource())) + .map(Relationship::getSource) + .forEach(s -> { + try { + addElement(s, true); + } catch (ElementNotPermittedInViewException e) { + System.out.println(e.getMessage() + " (ignoring " + s.getName() + ")"); + } + }); + } catch (ElementNotPermittedInViewException e) { + System.out.println(e.getMessage() + " (ignoring " + element.getName() + ")"); + } + } + +} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/view/StaticView.java b/structurizr-core/src/com/structurizr/view/StaticView.java index b4199bfa5..da0dfb569 100644 --- a/structurizr-core/src/com/structurizr/view/StaticView.java +++ b/structurizr-core/src/com/structurizr/view/StaticView.java @@ -11,7 +11,7 @@ /** * The superclass for all static views (system landscape, system context, container and component views). */ -public abstract class StaticView extends View { +public abstract class StaticView extends ModelView { private List animations = new ArrayList<>(); diff --git a/structurizr-core/src/com/structurizr/view/View.java b/structurizr-core/src/com/structurizr/view/View.java index fdfbadfd9..0d18cdc7b 100644 --- a/structurizr-core/src/com/structurizr/view/View.java +++ b/structurizr-core/src/com/structurizr/view/View.java @@ -1,96 +1,28 @@ package com.structurizr.view; import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonSetter; import com.structurizr.PropertyHolder; -import com.structurizr.model.*; -import com.structurizr.util.StringUtils; import javax.annotation.Nonnull; -import java.util.*; -import java.util.stream.Collectors; +import java.util.HashMap; +import java.util.Map; /** - * The superclass for all views (static views, dynamic views and deployment views). + * The superclass for all views. */ public abstract class View implements PropertyHolder { - private static final int DEFAULT_RANK_SEPARATION = 300; - private static final int DEFAULT_NODE_SEPARATION = 300; - - private SoftwareSystem softwareSystem; - private String softwareSystemId; - private String description = ""; private String key; private int order; - private PaperSize paperSize = null; - private Dimensions dimensions = null; - private AutomaticLayout automaticLayout = null; - private boolean mergeFromRemote = true; private String title; + private String description; private Map properties = new HashMap<>(); - private Set elementViews = new LinkedHashSet<>(); - private Set relationshipViews = new LinkedHashSet<>(); - - private LayoutMergeStrategy layoutMergeStrategy = new DefaultLayoutMergeStrategy(); - private ViewSet viewSet; View() { } - View(SoftwareSystem softwareSystem, String key, String description) { - this.softwareSystem = softwareSystem; - if (!StringUtils.isNullOrEmpty(key)) { - setKey(key); - } else { - throw new IllegalArgumentException("A key must be specified."); - } - setDescription(description); - } - - /** - * Gets the model that this view belongs to. - * - * @return a Model object - */ - @JsonIgnore - public Model getModel() { - return softwareSystem.getModel(); - } - - /** - * Gets the software system that this view is associated with. - * - * @return a SoftwareSystem object, or null if this view is not associated with a software system (e.g. it's a system landscape view) - */ - @JsonIgnore - public SoftwareSystem getSoftwareSystem() { - return softwareSystem; - } - - void setSoftwareSystem(SoftwareSystem softwareSystem) { - this.softwareSystem = softwareSystem; - } - - /** - * Gets the ID of the software system this view is associated with. - * - * @return the ID, as a String, or null if this view is not associated with a software system (e.g. it's a system landscape view) - */ - public String getSoftwareSystemId() { - if (this.softwareSystem != null) { - return this.softwareSystem.getId(); - } else { - return this.softwareSystemId; - } - } - - void setSoftwareSystemId(String softwareSystemId) { - this.softwareSystemId = softwareSystemId; - } - /** * Gets the description of this view. * @@ -138,107 +70,6 @@ void setOrder(int order) { this.order = Math.max(1, order); } - /** - * Gets the paper size that should be used to render this view. - * - * @return a PaperSize - */ - public PaperSize getPaperSize() { - return paperSize; - } - - public void setPaperSize(PaperSize paperSize) { - this.paperSize = paperSize; - } - - public Dimensions getDimensions() { - return dimensions; - } - - public void setDimensions(Dimensions dimensions) { - this.dimensions = dimensions; - } - - /** - * Gets the automatic layout settings for this view. - * - * @return an AutomaticLayout object, or null if not enabled - */ - public AutomaticLayout getAutomaticLayout() { - return automaticLayout; - } - - @JsonSetter - void setAutomaticLayout(AutomaticLayout automaticLayout) { - this.automaticLayout = automaticLayout; - } - - /** - * Enables automatic layout for this view, with some default settings. - */ - public void enableAutomaticLayout() { - enableAutomaticLayout(AutomaticLayout.RankDirection.TopBottom, 300, 600, 200, false); - } - - /** - * Enables automatic layout for this view, with the specified settings, using the Dagre implementation. - * - * @param rankDirection the rank direction - * @param rankSeparation the separation between ranks (in pixels, a positive integer) - * @param nodeSeparation the separation between nodes within the same rank (in pixels, a positive integer) - * @param edgeSeparation the separation between edges (in pixels, a positive integer) - * @param vertices whether vertices should be created during automatic layout - */ - public void enableAutomaticLayout(AutomaticLayout.RankDirection rankDirection, int rankSeparation, int nodeSeparation, int edgeSeparation, boolean vertices) { - this.automaticLayout = new AutomaticLayout(AutomaticLayout.Implementation.Dagre, rankDirection, rankSeparation, nodeSeparation, edgeSeparation, vertices); - } - - /** - * Enables automatic layout for this view, with the specified direction, using the Graphviz implementation. - * - * @param rankDirection the rank direction - */ - public void enableAutomaticLayout(AutomaticLayout.RankDirection rankDirection) { - enableAutomaticLayout(rankDirection, DEFAULT_RANK_SEPARATION, DEFAULT_NODE_SEPARATION); - } - - /** - * Enables automatic layout for this view, with the specified settings, using the Graphviz implementation. - * - * @param rankDirection the rank direction - * @param rankSeparation the separation between ranks (in pixels, a positive integer) - * @param nodeSeparation the separation between nodes within the same rank (in pixels, a positive integer) - */ - public void enableAutomaticLayout(AutomaticLayout.RankDirection rankDirection, int rankSeparation, int nodeSeparation) { - this.automaticLayout = new AutomaticLayout(AutomaticLayout.Implementation.Graphviz, rankDirection, rankSeparation, nodeSeparation, 0, false); - } - - /** - * Disables automatic layout for this view. - */ - public void disableAutomaticLayout() { - this.automaticLayout = null; - } - - /** - * Gets whether layout information for this view should be merged from a remote version of the workspace. - * - * @return true if layout information should be merged from the remote workspace, false otherwise - */ - public boolean getMergeFromRemote() { - return mergeFromRemote; - } - - /** - * Specifies whether layout information for this view should be merged from a remote version of the workspace. - * - * @param mergeFromRemote true if layout information should be merged from the remote workspace, false otherwise - */ - @JsonIgnore - public void setMergeFromRemote(boolean mergeFromRemote) { - this.mergeFromRemote = mergeFromRemote; - } - /** * Gets the title of this view, if one has been set. * @@ -265,205 +96,6 @@ public void setTitle(String title) { @JsonIgnore public abstract String getName(); - protected final void addElement(Element element, boolean addRelationships) { - if (element == null) { - throw new IllegalArgumentException("An element must be specified."); - } - - if (getModel().contains(element)) { - checkElementCanBeAdded(element); - elementViews.add(new ElementView(element)); - - if (addRelationships) { - addRelationships(element); - } - } else { - throw new IllegalArgumentException("The element named " + element.getName() + " does not exist in the model associated with this view."); - } - } - - protected abstract void checkElementCanBeAdded(Element element); - - private void addRelationships(Element element) { - Set elements = getElements().stream() - .map(ElementView::getElement) - .collect(Collectors.toSet()); - - // add relationships where the destination exists in the view already - for (Relationship relationship : element.getRelationships()) { - if (elements.contains(relationship.getDestination())) { - this.relationshipViews.add(new RelationshipView(relationship)); - } - } - - // add relationships where the source exists in the view already - for (Element e : elements) { - for (Relationship r : e.getRelationships()) { - if (r.getDestination().equals(element)) { - this.relationshipViews.add(new RelationshipView(r)); - } - } - } - } - - protected void removeElement(Element element) { - if (element == null) { - throw new IllegalArgumentException("An element must be specified."); - } - - if (!canBeRemoved(element)) { - throw new IllegalArgumentException("The element named '" + element.getName() + "' cannot be removed from this view."); - } - - ElementView elementView = new ElementView(element); - elementViews.remove(elementView); - - for (RelationshipView relationshipView : getRelationships()) { - if (relationshipView.getRelationship().getSource().equals(element) || - relationshipView.getRelationship().getDestination().equals(element)) { - remove(relationshipView.getRelationship()); - } - } - } - - protected RelationshipView addRelationship(Relationship relationship) { - if (relationship == null) { - throw new IllegalArgumentException("A relationship must be specified."); - } - - if (isElementInView(relationship.getSource()) && isElementInView(relationship.getDestination())) { - RelationshipView relationshipView = new RelationshipView(relationship); - relationshipViews.add(relationshipView); - - return relationshipView; - } - - return null; - } - - public boolean isElementInView(Element element) { - return this.elementViews.stream().anyMatch(ev -> ev.getElement().equals(element)); - } - - /** - * Removes a relationship from this view. - * - * @param relationship the Relationship to remove - */ - public void remove(Relationship relationship) { - if (relationship != null) { - RelationshipView relationshipView = new RelationshipView(relationship); - relationshipViews.remove(relationshipView); - } - } - - /** - * Removes relationships that are not connected to the specified element. - * - * @param element the Element to test against - */ - public void removeRelationshipsNotConnectedToElement(Element element) { - if (element != null) { - getRelationships().stream() - .map(RelationshipView::getRelationship) - .filter(r -> !r.getSource().equals(element) && !r.getDestination().equals(element)) - .forEach(this::remove); - } - } - - /** - * Gets the set of elements in this view. - * - * @return a Set of ElementView objects - */ - public Set getElements() { - return new HashSet<>(elementViews); - } - - void setElements(Set elementViews) { - if (elementViews != null) { - this.elementViews = new HashSet<>(elementViews); - } - } - - /** - * Gets the set of relationships in this view. - * - * @return a Set of RelationshipView objects - */ - public Set getRelationships() { - return new HashSet<>(this.relationshipViews); - } - - void setRelationships(Set relationshipViews) { - if (relationshipViews != null) { - this.relationshipViews = new HashSet<>(relationshipViews); - } - } - - /** - * Removes all elements that have no relationships to other elements in this view. - */ - public void removeElementsWithNoRelationships() { - Set relationships = getRelationships(); - - Set elementIds = new HashSet<>(); - relationships.forEach(rv -> elementIds.add(rv.getRelationship().getSourceId())); - relationships.forEach(rv -> elementIds.add(rv.getRelationship().getDestinationId())); - - for (ElementView elementView : getElements()) { - if (!elementIds.contains(elementView.getId())) { - removeElement(elementView.getElement()); - } - } - } - - /** - * Sets the strategy used for merging layout information (paper size, x/y positioning, etc) - * from one version of this view to another. - * - * @param layoutMergeStrategy an instance of LayoutMergeStrategy - */ - public void setLayoutMergeStrategy(LayoutMergeStrategy layoutMergeStrategy) { - if (layoutMergeStrategy == null) { - throw new IllegalArgumentException("A LayoutMergeStrategy object must be provided."); - } - - this.layoutMergeStrategy = layoutMergeStrategy; - } - - /** - * Attempts to copy the visual layout information (e.g. x,y coordinates) of elements and relationships - * from the specified source view into this view. - * - * @param source the source View - */ - void copyLayoutInformationFrom(@Nonnull View source) { - layoutMergeStrategy.copyLayoutInformation(source, this); - } - - /** - * Gets the element view for the given element. - * - * @param element the Element to find the ElementView for - * @return an ElementView object, or null if the element doesn't exist in the view - */ - public ElementView getElementView(@Nonnull Element element) { - Optional elementView = this.elementViews.stream().filter(ev -> ev.getId().equals(element.getId())).findFirst(); - return elementView.orElse(null); - } - - /** - * Gets the relationship view for the given relationship. - * - * @param relationship the Relationship to find the RelationshipView for - * @return a RelationshipView object, or null if the relationship doesn't exist in the view - */ - public RelationshipView getRelationshipView(@Nonnull Relationship relationship) { - Optional relationshipView = this.relationshipViews.stream().filter(rv -> rv.getId().equals(relationship.getId())).findFirst(); - return relationshipView.orElse(null); - } - void setViewSet(@Nonnull ViewSet viewSet) { this.viewSet = viewSet; } @@ -478,70 +110,6 @@ public ViewSet getViewSet() { return viewSet; } - protected abstract boolean canBeRemoved(Element element); - - final void checkParentAndChildrenHaveNotAlreadyBeenAdded(StaticStructureElement elementToBeAdded) { - // check a parent hasn't been added already - Set idsOfElementsInView = getElements().stream().map(ElementView::getElement).map(Element::getId).collect(Collectors.toSet()); - - Element parent = elementToBeAdded.getParent(); - while (parent != null) { - if (idsOfElementsInView.contains(parent.getId())) { - throw new ElementNotPermittedInViewException("A parent of " + elementToBeAdded.getName() + " is already in this view."); - } - - parent = parent.getParent(); - } - - // and now check a child hasn't been added already - Set elementParentIds = new HashSet<>(); - for (ElementView elementView : getElements()) { - Element element = elementView.getElement(); - parent = element.getParent(); - while (parent != null) { - elementParentIds.add(parent.getId()); - parent = parent.getParent(); - } - } - - if (elementParentIds.contains(elementToBeAdded.getId())) { - throw new ElementNotPermittedInViewException("A child of " + elementToBeAdded.getName() + " is already in this view."); - } - } - - protected void addNearestNeighbours(Element element, Class typeOfElement) { - if (element == null) { - return; - } - - try { - addElement(element, true); - - Set relationships = getModel().getRelationships(); - relationships.stream().filter(r -> r.getSource().equals(element) && typeOfElement.isInstance(r.getDestination())) - .map(Relationship::getDestination) - .forEach(d -> { - try { - addElement(d, true); - } catch (ElementNotPermittedInViewException e) { - System.out.println(e.getMessage() + " (ignoring " + d.getName() + ")"); - } - }); - - relationships.stream().filter(r -> r.getDestination().equals(element) && typeOfElement.isInstance(r.getSource())) - .map(Relationship::getSource) - .forEach(s -> { - try { - addElement(s, true); - } catch (ElementNotPermittedInViewException e) { - System.out.println(e.getMessage() + " (ignoring " + s.getName() + ")"); - } - }); - } catch (ElementNotPermittedInViewException e) { - System.out.println(e.getMessage() + " (ignoring " + element.getName() + ")"); - } - } - /** * Gets the collection of name-value property pairs associated with this view, as a Map. * diff --git a/structurizr-core/src/com/structurizr/view/ViewSet.java b/structurizr-core/src/com/structurizr/view/ViewSet.java index 4234e7afa..8644db9cf 100644 --- a/structurizr-core/src/com/structurizr/view/ViewSet.java +++ b/structurizr-core/src/com/structurizr/view/ViewSet.java @@ -265,6 +265,7 @@ public FilteredView createFilteredView(StaticView view, String key, String descr FilteredView filteredView = new FilteredView(view, key, description, mode, tags); filteredView.setOrder(getNextOrder()); + view.setViewSet(this); filteredViews.add(filteredView); return filteredView; } @@ -281,6 +282,7 @@ public ImageView createImageView(String key) { ImageView view = new ImageView(key); view.setOrder(getNextOrder()); + view.setViewSet(this); imageViews.add(view); return view; } @@ -301,6 +303,7 @@ public ImageView createImageView(Element element, String key) { ImageView view = new ImageView(element, key); view.setOrder(getNextOrder()); + view.setViewSet(this); imageViews.add(view); return view; } @@ -641,7 +644,14 @@ void hydrate(Model model) { ); } - filteredView.setView(view); + if (view instanceof StaticView) { + filteredView.setView((StaticView)view); + } else { + throw new WorkspaceValidationException( + String.format("The filtered view with key \"%s\" is based upon a view (key=%s), but that view is not a static view.", + filteredView.getKey(), filteredView.getBaseViewKey()) + ); + } } for (ImageView view : imageViews) { @@ -659,7 +669,7 @@ void hydrate(Model model) { } } - private void hydrateView(View view) { + private void hydrateView(ModelView view) { view.setViewSet(this); for (ElementView elementView : view.getElements()) { @@ -687,8 +697,7 @@ private void hydrateView(View view) { } } - private void checkViewKeysAreUnique() { - Set keys = new HashSet<>(); + private Collection getAllViews() { Collection views = new ArrayList<>(); views.addAll(customViews); views.addAll(systemLandscapeViews); @@ -697,38 +706,26 @@ private void checkViewKeysAreUnique() { views.addAll(componentViews); views.addAll(dynamicViews); views.addAll(deploymentViews); + views.addAll(filteredViews); + views.addAll(imageViews); - for (View view : views) { + return views; + } + + private void checkViewKeysAreUnique() { + Set keys = new HashSet<>(); + + for (View view : getAllViews()) { if (keys.contains(view.getKey())) { throw new WorkspaceValidationException("A view with the key " + view.getKey() + " already exists."); } else { keys.add(view.getKey()); } } - - for (FilteredView filteredView : filteredViews) { - if (keys.contains(filteredView.getKey())) { - throw new WorkspaceValidationException("A view with the key " + filteredView.getKey() + " already exists."); - } else { - keys.add(filteredView.getKey()); - } - } } private synchronized int getNextOrder() { - int order = 0; - - order = Math.max(order, customViews.stream().max(Comparator.comparingInt(View::getOrder)).map(View::getOrder).orElse(0)); - order = Math.max(order, systemLandscapeViews.stream().max(Comparator.comparingInt(View::getOrder)).map(View::getOrder).orElse(0)); - order = Math.max(order, systemContextViews.stream().max(Comparator.comparingInt(View::getOrder)).map(View::getOrder).orElse(0)); - order = Math.max(order, containerViews.stream().max(Comparator.comparingInt(View::getOrder)).map(View::getOrder).orElse(0)); - order = Math.max(order, componentViews.stream().max(Comparator.comparingInt(View::getOrder)).map(View::getOrder).orElse(0)); - order = Math.max(order, dynamicViews.stream().max(Comparator.comparingInt(View::getOrder)).map(View::getOrder).orElse(0)); - order = Math.max(order, deploymentViews.stream().max(Comparator.comparingInt(View::getOrder)).map(View::getOrder).orElse(0)); - order = Math.max(order, filteredViews.stream().max(Comparator.comparingInt(FilteredView::getOrder)).map(FilteredView::getOrder).orElse(0)); - order = Math.max(order, imageViews.stream().max(Comparator.comparingInt(ImageView::getOrder)).map(ImageView::getOrder).orElse(0)); - - return order + 1; + return getAllViews().stream().max(Comparator.comparingInt(View::getOrder)).map(View::getOrder).orElse(0) + 1; } /** From a7204923685483c42329fda0dd01e98a633d795d Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 13 Feb 2023 14:51:40 +0000 Subject: [PATCH 369/717] Added some basic construction tests. --- .../com/structurizr/view/ImageViewTests.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 structurizr-core/test/unit/com/structurizr/view/ImageViewTests.java diff --git a/structurizr-core/test/unit/com/structurizr/view/ImageViewTests.java b/structurizr-core/test/unit/com/structurizr/view/ImageViewTests.java new file mode 100644 index 000000000..b7b921099 --- /dev/null +++ b/structurizr-core/test/unit/com/structurizr/view/ImageViewTests.java @@ -0,0 +1,30 @@ +package com.structurizr.view; + +import com.structurizr.AbstractWorkspaceTestBase; +import com.structurizr.model.SoftwareSystem; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class ImageViewTests extends AbstractWorkspaceTestBase { + + @Test + void construction_WhenNoElementIsSpecified() { + ImageView view = views.createImageView("key"); + + assertEquals("key", view.getKey()); + assertNull(view.getElement()); + assertNull(view.getElementId()); + } + + @Test + void construction_WhenAnElementIsSpecified() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); + ImageView view = views.createImageView(softwareSystem, "key"); + + assertEquals("key", view.getKey()); + assertSame(softwareSystem, view.getElement()); + assertEquals(softwareSystem.getId(), view.getElementId()); + } + +} \ No newline at end of file From 94826f9dac58629a49b1e873652c93efedff0340 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 16 Feb 2023 11:06:49 +0000 Subject: [PATCH 370/717] Updated for build. --- build.gradle | 2 +- docs/changelog.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 8323b965f..a9925b673 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.19.0' + version = '1.20.0' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 6b8866b18..0878eb24f 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,8 +1,8 @@ # Changelog -## 1.20.0 (unreleased) +## 1.20.0 (16th February 2023) -- __Breaking change__: Renamed `View` to `ModelView`. +- __Breaking change__: Renamed `com.structurizr.view.View` to `com.structurizr.view.ModelView`. - Added support for "image views". - Added a `Window` shape. - `ThemeUtils.toJson()` now includes the workspace branding logo and font in the resulting theme. From 84a5664f2588cac464a8c3cbbe5b2f9fabb97759 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 16 Feb 2023 14:36:07 +0000 Subject: [PATCH 371/717] `ViewSet.getViews()` now includes all views. --- build.gradle | 2 +- docs/changelog.md | 3 ++ .../src/com/structurizr/view/ViewSet.java | 35 ++++--------------- 3 files changed, 11 insertions(+), 29 deletions(-) diff --git a/build.gradle b/build.gradle index a9925b673..bed9016fc 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.20.0' + version = '1.20.1' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 0878eb24f..f221f2dc4 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,8 @@ # Changelog +## 1.20.1 (unreleased) + +- `ViewSet.getViews()` now includes all views. ## 1.20.0 (16th February 2023) - __Breaking change__: Renamed `com.structurizr.view.View` to `com.structurizr.view.ModelView`. diff --git a/structurizr-core/src/com/structurizr/view/ViewSet.java b/structurizr-core/src/com/structurizr/view/ViewSet.java index 8644db9cf..88e2a6f67 100644 --- a/structurizr-core/src/com/structurizr/view/ViewSet.java +++ b/structurizr-core/src/com/structurizr/view/ViewSet.java @@ -347,16 +347,7 @@ View getViewWithKey(String key) { throw new IllegalArgumentException("A key must be specified."); } - Set views = new HashSet<>(); - views.addAll(customViews); - views.addAll(systemLandscapeViews); - views.addAll(systemContextViews); - views.addAll(containerViews); - views.addAll(componentViews); - views.addAll(dynamicViews); - views.addAll(deploymentViews); - - return views.stream().filter(v -> key.equals(v.getKey())).findFirst().orElse(null); + return getViews().stream().filter(v -> key.equals(v.getKey())).findFirst().orElse(null); } /** @@ -528,7 +519,7 @@ void setImageView(Set imageViews) { } /** - * Gets the set of all views (except filtered and image views). + * Gets the set of all views. * * @return a Collection of View objects */ @@ -536,12 +527,15 @@ void setImageView(Set imageViews) { public Collection getViews() { HashSet views = new HashSet<>(); + views.addAll(getCustomViews()); views.addAll(getSystemLandscapeViews()); views.addAll(getSystemContextViews()); views.addAll(getContainerViews()); views.addAll(getComponentViews()); views.addAll(getDynamicViews()); views.addAll(getDeploymentViews()); + views.addAll(getFilteredViews()); + views.addAll(getImageViews()); return views; } @@ -697,25 +691,10 @@ private void hydrateView(ModelView view) { } } - private Collection getAllViews() { - Collection views = new ArrayList<>(); - views.addAll(customViews); - views.addAll(systemLandscapeViews); - views.addAll(systemContextViews); - views.addAll(containerViews); - views.addAll(componentViews); - views.addAll(dynamicViews); - views.addAll(deploymentViews); - views.addAll(filteredViews); - views.addAll(imageViews); - - return views; - } - private void checkViewKeysAreUnique() { Set keys = new HashSet<>(); - for (View view : getAllViews()) { + for (View view : getViews()) { if (keys.contains(view.getKey())) { throw new WorkspaceValidationException("A view with the key " + view.getKey() + " already exists."); } else { @@ -725,7 +704,7 @@ private void checkViewKeysAreUnique() { } private synchronized int getNextOrder() { - return getAllViews().stream().max(Comparator.comparingInt(View::getOrder)).map(View::getOrder).orElse(0) + 1; + return getViews().stream().max(Comparator.comparingInt(View::getOrder)).map(View::getOrder).orElse(0) + 1; } /** From 35f968f0a1d993b2857b23ebc0264ee366424f5c Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 16 Feb 2023 14:36:35 +0000 Subject: [PATCH 372/717] `ViewSet.getViewWithKey()` is now public. --- docs/changelog.md | 2 ++ structurizr-core/src/com/structurizr/view/ViewSet.java | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index f221f2dc4..67924b149 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -3,6 +3,8 @@ ## 1.20.1 (unreleased) - `ViewSet.getViews()` now includes all views. +- `ViewSet.getViewWithKey()` is now public. + ## 1.20.0 (16th February 2023) - __Breaking change__: Renamed `com.structurizr.view.View` to `com.structurizr.view.ModelView`. diff --git a/structurizr-core/src/com/structurizr/view/ViewSet.java b/structurizr-core/src/com/structurizr/view/ViewSet.java index 88e2a6f67..2763aa03e 100644 --- a/structurizr-core/src/com/structurizr/view/ViewSet.java +++ b/structurizr-core/src/com/structurizr/view/ViewSet.java @@ -342,7 +342,7 @@ private void assertThatTheViewIsNotNull(View view) { * @param key the key * @return a View object, or null if a view with the specified key could not be found */ - View getViewWithKey(String key) { + public View getViewWithKey(String key) { if (key == null) { throw new IllegalArgumentException("A key must be specified."); } From 8b86aa33baac409afef19ee49d396c5c0ceea742 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 16 Feb 2023 14:44:31 +0000 Subject: [PATCH 373/717] Updated to reflect release. --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 67924b149..495158303 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 1.20.1 (unreleased) +## 1.20.1 (16th February 2023) - `ViewSet.getViews()` now includes all views. - `ViewSet.getViewWithKey()` is now public. From 8bf1c385c22e49fd239d0b707f9d5c6f1fd483e6 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 18 Feb 2023 15:49:33 +0000 Subject: [PATCH 374/717] Sets the viewset on new filtered views. --- structurizr-core/src/com/structurizr/view/ViewSet.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structurizr-core/src/com/structurizr/view/ViewSet.java b/structurizr-core/src/com/structurizr/view/ViewSet.java index 2763aa03e..02e799b3f 100644 --- a/structurizr-core/src/com/structurizr/view/ViewSet.java +++ b/structurizr-core/src/com/structurizr/view/ViewSet.java @@ -265,7 +265,7 @@ public FilteredView createFilteredView(StaticView view, String key, String descr FilteredView filteredView = new FilteredView(view, key, description, mode, tags); filteredView.setOrder(getNextOrder()); - view.setViewSet(this); + filteredView.setViewSet(this); filteredViews.add(filteredView); return filteredView; } From d30d24890c3e55c5de26198b000267c7ce3f22a8 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 24 Feb 2023 16:00:20 +0000 Subject: [PATCH 375/717] Removes the concept of "code elements" from `Component`. --- build.gradle | 2 +- docs/changelog.md | 4 + .../com/structurizr/model/CodeElement.java | 236 ------------------ .../structurizr/model/CodeElementRole.java | 13 - .../src/com/structurizr/model/Component.java | 80 +----- .../src/com/structurizr/model/Container.java | 46 +--- .../src/com/structurizr/model/Model.java | 6 +- .../structurizr/model/CodeElementTests.java | 143 ----------- .../com/structurizr/model/ComponentTests.java | 96 ------- .../com/structurizr/model/ContainerTests.java | 62 ----- 10 files changed, 10 insertions(+), 678 deletions(-) delete mode 100644 structurizr-core/src/com/structurizr/model/CodeElement.java delete mode 100644 structurizr-core/src/com/structurizr/model/CodeElementRole.java delete mode 100644 structurizr-core/test/unit/com/structurizr/model/CodeElementTests.java diff --git a/build.gradle b/build.gradle index bed9016fc..5a9142c6b 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.20.1' + version = '1.21.0' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 495158303..59fc17e5a 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.21.0 (unreleased) + +- __Breaking change__: Removes the concept of "code elements" from `Component`. + ## 1.20.1 (16th February 2023) - `ViewSet.getViews()` now includes all views. diff --git a/structurizr-core/src/com/structurizr/model/CodeElement.java b/structurizr-core/src/com/structurizr/model/CodeElement.java deleted file mode 100644 index a6935494a..000000000 --- a/structurizr-core/src/com/structurizr/model/CodeElement.java +++ /dev/null @@ -1,236 +0,0 @@ -package com.structurizr.model; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.structurizr.util.Url; - -/** - * Represents a code element, such as a Java class or interface, - * that is part of the implementation of a component. - */ -public final class CodeElement { - - /** the role of the code element ... Primary or Supporting */ - private CodeElementRole role = CodeElementRole.Supporting; - - /** the name of the code element ... typically the simple class/interface name */ - private String name; - - /** the fully qualified type of the code element **/ - private String type; - - /** a short description of the code element */ - private String description; - - /** a URL; e.g. a reference to the code element in source code control */ - private String url; - - /** the programming language used to create the code element */ - private String language = "Java"; - - /** the category of code element; e.g. class, interface, etc */ - private String category; - - /** the visibility of the code element; e.g. public, package, private */ - private String visibility; - - /** the size of the code element; e.g. the number of lines */ - private long size; - - CodeElement() { - } - - CodeElement(String fullyQualifiedTypeName) { - if (fullyQualifiedTypeName == null || fullyQualifiedTypeName.trim().isEmpty()) { - throw new IllegalArgumentException("A fully qualified name must be provided."); - } - - int dot = fullyQualifiedTypeName.lastIndexOf('.'); - if (dot > -1) { - setName(fullyQualifiedTypeName.substring(dot+1, fullyQualifiedTypeName.length())); - setType(fullyQualifiedTypeName); - } else { - setName(fullyQualifiedTypeName); - setType(fullyQualifiedTypeName); - } - } - - /** - * Gets the role of this code element; Primary or Supporting. - * - * @return a CodeElementRole enum - */ - public CodeElementRole getRole() { - return role; - } - - void setRole(CodeElementRole role) { - this.role = role; - } - - /** - * Gets the name of this code element. - * - * @return the name, as a String - */ - public String getName() { - return name; - } - - void setName(String name) { - this.name = name; - } - - /** - * Gets the type (fully qualified type name) of this code element. - * - * @return the type, as a String - */ - public String getType() { - return type; - } - - void setType(String type) { - this.type = type; - } - - /** - * Gets the Java package of this component (i.e. the package of the primary code element). - * - * @return the package name, as a String - */ - @JsonIgnore - public String getPackage() { - return type.substring(0, type.lastIndexOf('.')); - } - - /** - * Gets the description of this code element. - * - * @return the description, as a String - */ - public String getDescription() { - return description; - } - - /** - * Sets the description of this code element. - * - * @param description the description, as a String - */ - public void setDescription(String description) { - this.description = description; - } - - /** - * Gets the URL where more information about this code element can be found. - * - * @return the URL as a String, or null if not set - */ - public String getUrl() { - return url; - } - - /** - * Sets the URL where more information about this code element can be found. - * - * @param url the URL as a String - * @throws IllegalArgumentException if the URL is not a well-formed URL - */ - public void setUrl(String url) { - if (url != null && url.trim().length() > 0) { - if (Url.isUrl(url)) { - this.url = url; - } else { - throw new IllegalArgumentException(url + " is not a valid URL."); - } - } - } - - /** - * Gets the programming language of this code element. - * - * @return the programming language, as a String - */ - public String getLanguage() { - return language; - } - - /** - * Sets the programming language of this code element. - * - * @param language the programming language, as a String - */ - public void setLanguage(String language) { - this.language = language; - } - - /** - * Gets the category of this code element (interface, class, etc). - * - * @return the category, as a String - */ - public String getCategory() { - return category; - } - - /** - * Sets the category of this code element. - * - * @param category the category, as a String - */ - public void setCategory(String category) { - this.category = category; - } - - /** - * Gets the visibility of this code element (public, package, etc). - * - * @return the visibility, as a String - */ - public String getVisibility() { - return visibility; - } - - /** - * Sets the visibility of this code element. - * - * @param visibility the visibility, as a String - */ - public void setVisibility(String visibility) { - this.visibility = visibility; - } - - /** - * Gets the size of this code element (e.g. the number of lines of code). - * - * @return the size, as a long - */ - public long getSize() { - return size; - } - - /** - * Sets the size of this code element. - * - * @param size the size, as a long - */ - public void setSize(long size) { - this.size = size; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - CodeElement that = (CodeElement) o; - - return type.equals(that.type); - } - - @Override - public int hashCode() { - return type.hashCode(); - } - -} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/model/CodeElementRole.java b/structurizr-core/src/com/structurizr/model/CodeElementRole.java deleted file mode 100644 index 0cc30c106..000000000 --- a/structurizr-core/src/com/structurizr/model/CodeElementRole.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.structurizr.model; - -/** - * Used to represent the role of a code element. A component can have - * one primary code element, and zero or more supporting code elements - * associated with it. - */ -public enum CodeElementRole { - - Primary, - Supporting - -} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/model/Component.java b/structurizr-core/src/com/structurizr/model/Component.java index 64c4127d7..06e3e7975 100644 --- a/structurizr-core/src/com/structurizr/model/Component.java +++ b/structurizr-core/src/com/structurizr/model/Component.java @@ -2,7 +2,9 @@ import com.fasterxml.jackson.annotation.JsonIgnore; -import java.util.*; +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.Set; /** * Represents a "component" in the C4 model. @@ -12,8 +14,6 @@ public final class Component extends StaticStructureElement { private Container parent; private String technology; - private Set codeElements = new HashSet<>(); - private long size; Component() { } @@ -52,80 +52,6 @@ public void setTechnology(String technology) { this.technology = technology; } - /** - * Gets the type of this component (e.g. a fully qualified Java interface/class name). - * - * @return the type, as a String - */ - @JsonIgnore - public CodeElement getType() { - return codeElements.stream().filter(ce -> ce.getRole() == CodeElementRole.Primary).findFirst().orElse(null); - } - - /** - * Sets the type of this component (e.g. a fully qualified Java interface/class name). - * - * @param type the fully qualified type name - * @return the CodeElement that was created - * @throws IllegalArgumentException if the specified type is null - */ - public CodeElement setType(String type) { - Optional optional = codeElements.stream().filter(ce -> ce.getRole() == CodeElementRole.Primary).findFirst(); - optional.ifPresent(codeElement -> codeElements.remove(codeElement)); - - CodeElement codeElement = new CodeElement(type); - codeElement.setRole(CodeElementRole.Primary); - this.codeElements.add(codeElement); - - return codeElement; - } - - /** - * Gets the set of CodeElement objects. - * - * @return a Set, which could be empty - */ - public Set getCode() { - return new HashSet<>(codeElements); - } - - void setCode(Set codeElements) { - this.codeElements = codeElements; - } - - /** - * Adds a supporting type to this Component. - * - * @param type the fully qualified type name - * @return a CodeElement representing the supporting type - * @throws IllegalArgumentException if the specified type is null - */ - public CodeElement addSupportingType(String type) { - CodeElement codeElement = new CodeElement(type); - codeElement.setRole(CodeElementRole.Supporting); - this.codeElements.add(codeElement); - - return codeElement; - } - - /** - * Gets the size of this Component (e.g. number of lines). - * - * @return the size of this component, as a long - */ - public long getSize() { - return size; - } - - /** - * Sets the size of this component (e.g. number of lines). - * - * @param size the size - */ - public void setSize(long size) { - this.size = size; - } - /** * Gets the canonical name of this component, in the form "/Software System/Container/Component". * diff --git a/structurizr-core/src/com/structurizr/model/Container.java b/structurizr-core/src/com/structurizr/model/Container.java index 17f34ef00..a4c9fe29a 100644 --- a/structurizr-core/src/com/structurizr/model/Container.java +++ b/structurizr-core/src/com/structurizr/model/Container.java @@ -99,35 +99,7 @@ public Component addComponent(String name, String description) { * @throws IllegalArgumentException if the component name is null or empty, or a component with the same name already exists */ public Component addComponent(String name, String description, String technology) { - return this.addComponent(name, (String)null, description, technology); - } - - /** - * Adds a component to this container. - * - * @param name the name of the component - * @param type a Class instance representing the primary type of the component - * @param description a description of the component - * @param technology the technology of the component - * @return the resulting Component instance - * @throws IllegalArgumentException if the component name is null or empty, or a component with the same name already exists - */ - public Component addComponent(String name, Class type, String description, String technology) { - return this.addComponent(name, type.getCanonicalName(), description, technology); - } - - /** - * Adds a component to this container. - * - * @param name the name of the component - * @param type a String describing the fully qualified name of the primary type of the component - * @param description a description of the component - * @param technology the technology of the component - * @return the resulting Component instance - * @throws IllegalArgumentException if the component name is null or empty, or a component with the same name already exists - */ - public Component addComponent(String name, String type, String description, String technology) { - return getModel().addComponentOfType(this, name, type, description, technology); + return getModel().addComponent(this, name, description, technology); } void add(Component component) { @@ -167,22 +139,6 @@ public Component getComponentWithName(String name) { return component.orElse(null); } - /** - * Gets the component of the specified type. - * - * @param type the fully qualified type of the component - * @return the Component instance, or null if a component with the specified type does not exist - * @throws IllegalArgumentException if the type is null or empty - */ - public Component getComponentOfType(String type) { - if (type == null || type.trim().length() == 0) { - throw new IllegalArgumentException("A component type must be provided."); - } - - Optional component = components.stream().filter(c -> type.equals(c.getType().getType())).findFirst(); - return component.orElse(null); - } - /** * Gets the canonical name of this container, in the form "/Software System/Container". * diff --git a/structurizr-core/src/com/structurizr/model/Model.java b/structurizr-core/src/com/structurizr/model/Model.java index d5c06d5ce..b97c0d23c 100644 --- a/structurizr-core/src/com/structurizr/model/Model.java +++ b/structurizr-core/src/com/structurizr/model/Model.java @@ -215,17 +215,13 @@ Container addContainer(SoftwareSystem parent, @Nonnull String name, @Nullable St } } - Component addComponentOfType(Container parent, String name, String type, String description, String technology) { + Component addComponent(Container parent, String name, String description, String technology) { if (parent.getComponentWithName(name) == null) { Component component = new Component(); component.setName(name); component.setDescription(description); component.setTechnology(technology); - if (type != null && type.trim().length() > 0) { - component.setType(type); - } - component.setParent(parent); parent.add(component); diff --git a/structurizr-core/test/unit/com/structurizr/model/CodeElementTests.java b/structurizr-core/test/unit/com/structurizr/model/CodeElementTests.java deleted file mode 100644 index 79cb47c86..000000000 --- a/structurizr-core/test/unit/com/structurizr/model/CodeElementTests.java +++ /dev/null @@ -1,143 +0,0 @@ -package com.structurizr.model; - -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.*; - -public class CodeElementTests { - - @Test - void construction_WhenAFullyQualifiedNameIsSpecified() { - CodeElement codeElement = new CodeElement("com.structurizr.component.SomeComponent"); - assertEquals("SomeComponent", codeElement.getName()); - assertEquals("com.structurizr.component.SomeComponent", codeElement.getType()); - } - - @Test - void construction_WhenAFullyQualifiedNameIsSpecifiedInTheDefaultPackage() { - CodeElement codeElement = new CodeElement("SomeComponent"); - assertEquals("SomeComponent", codeElement.getName()); - assertEquals("SomeComponent", codeElement.getType()); - } - - @Test - void descriptionProperty() { - CodeElement codeElement = new CodeElement("com.structurizr.component.SomeComponent"); - assertNull(codeElement.getDescription()); - - codeElement.setDescription("Description"); - assertEquals("Description", codeElement.getDescription()); - } - - @Test - void sizeProperty() { - CodeElement codeElement = new CodeElement("com.structurizr.component.SomeComponent"); - assertEquals(0, codeElement.getSize()); - - codeElement.setSize(123456); - assertEquals(123456, codeElement.getSize()); - } - - @Test - void languageProperty() { - CodeElement codeElement = new CodeElement("com.structurizr.component.SomeComponent"); - assertEquals("Java", codeElement.getLanguage()); - - codeElement.setLanguage("Scala"); - assertEquals("Scala", codeElement.getLanguage()); - } - - @Test - void categoryProperty() { - CodeElement codeElement = new CodeElement("com.structurizr.component.SomeComponent"); - assertNull(codeElement.getCategory()); - - codeElement.setCategory("class"); - assertEquals("class", codeElement.getCategory()); - } - - @Test - void visibilityProperty() { - CodeElement codeElement = new CodeElement("com.structurizr.component.SomeComponent"); - assertNull(codeElement.getVisibility()); - - codeElement.setVisibility("package"); - assertEquals("package", codeElement.getVisibility()); - } - - @Test - void setUrl() { - CodeElement codeElement = new CodeElement("com.structurizr.component.SomeComponent"); - codeElement.setUrl("https://structurizr.com"); - assertEquals("https://structurizr.com", codeElement.getUrl()); - } - - @Test - void setUrl_ThrowsAnIllegalArgumentException_WhenAnInvalidUrlIsSpecified() { - assertThrows(IllegalArgumentException.class, () -> { - CodeElement codeElement = new CodeElement("com.structurizr.component.SomeComponent"); - codeElement.setUrl("htt://blah"); - }); - } - - @Test - void setUrl_DoesNothing_WhenANullUrlIsSpecified() { - CodeElement codeElement = new CodeElement("com.structurizr.component.SomeComponent"); - codeElement.setUrl(null); - assertNull(codeElement.getUrl()); - } - - @Test - void setUrl_DoesNothing_WhenAnEmptyUrlIsSpecified() { - CodeElement codeElement = new CodeElement("com.structurizr.component.SomeComponent"); - codeElement.setUrl(" "); - assertNull(codeElement.getUrl()); - } - - @Test - void construction_ThrowsAnIllegalArgumentException_WhenANullFullyQualifiedNameIsSpecified() { - assertThrows(IllegalArgumentException.class, () -> { - new CodeElement(null); - }); - } - - @Test - void construction_ThrowsAnIllegalArgumentException_WhenAnEmptyFullyQualifiedNameIsSpecified() { - assertThrows(IllegalArgumentException.class, () -> { - new CodeElement(" "); - }); - } - - @Test - void equals_ReturnsFalse_WhenComparedToNull() { - CodeElement codeElement = new CodeElement("com.structurizr.component.SomeComponent"); - assertNotEquals(codeElement, null); - } - - @Test - void equals_ReturnsFalse_WhenComparedToDifferentTypeOfObject() { - CodeElement codeElement = new CodeElement("com.structurizr.component.SomeComponent"); - assertNotEquals(codeElement, "hello"); - } - - @Test - void equals_ReturnsFalse_WhenComparedToAnotherCodeElementWithADifferentType() { - CodeElement codeElement1 = new CodeElement("com.structurizr.component.SomeComponent1"); - CodeElement codeElement2 = new CodeElement("com.structurizr.component.SomeComponent2"); - assertNotEquals(codeElement1, codeElement2); - } - - @Test - void equals_ReturnsFalse_WhenComparedToAnotherCodeElementWithTheSameType() { - CodeElement codeElement1 = new CodeElement("com.structurizr.component.SomeComponent1"); - CodeElement codeElement2 = new CodeElement("com.structurizr.component.SomeComponent1"); - assertEquals(codeElement1, codeElement2); - } - - @Test - void getPackage_ReturnsThePackageName() { - CodeElement codeElement = new CodeElement("com.structurizr.component.SomeComponent"); - assertEquals("com.structurizr.component", codeElement.getPackage()); - } - -} diff --git a/structurizr-core/test/unit/com/structurizr/model/ComponentTests.java b/structurizr-core/test/unit/com/structurizr/model/ComponentTests.java index 82f72d2a4..fa19b0dee 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ComponentTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ComponentTests.java @@ -3,8 +3,6 @@ import com.structurizr.AbstractWorkspaceTestBase; import org.junit.jupiter.api.Test; -import java.util.Set; - import static org.junit.jupiter.api.Assertions.*; public class ComponentTests extends AbstractWorkspaceTestBase { @@ -65,98 +63,4 @@ void technologyProperty() { assertEquals("Spring Bean", component.getTechnology()); } - @Test - void sizeProperty() { - Component component = new Component(); - assertEquals(0, component.getSize()); - - component.setSize(123456); - assertEquals(123456, component.getSize()); - } - - @Test - void setType_ThrowsAnExceptionWhenPassedNull() { - Component component = new Component(); - try { - component.setType(null); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("A fully qualified name must be provided.", iae.getMessage()); - } - } - - @Test - void setType_AddsAPrimaryCodeElement_WhenPassedAFullyQualifiedTypeName() { - Component component = new Component(); - component.setType("com.structurizr.web.HomePageController"); - - Set codeElements = component.getCode(); - assertEquals(1, codeElements.size()); - CodeElement codeElement = codeElements.iterator().next(); - assertEquals("HomePageController", codeElement.getName()); - assertEquals("com.structurizr.web.HomePageController", codeElement.getType()); - assertEquals(CodeElementRole.Primary, codeElement.getRole()); - } - - @Test - void setType_OverwritesThePrimaryCodeElement_WhenCalledMoreThanOnce() { - Component component = new Component(); - component.setType("com.structurizr.web.HomePageController"); - component.setType("com.structurizr.web.SomeOtherController"); - - Set codeElements = component.getCode(); - assertEquals(1, codeElements.size()); - CodeElement codeElement = codeElements.iterator().next(); - assertEquals("SomeOtherController", codeElement.getName()); - assertEquals("com.structurizr.web.SomeOtherController", codeElement.getType()); - assertEquals(CodeElementRole.Primary, codeElement.getRole()); - - } - - @Test - void addSupportingType_ThrowsAnExceptionWhenPassedNull() { - Component component = new Component(); - try { - component.addSupportingType(null); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("A fully qualified name must be provided.", iae.getMessage()); - } - } - - @Test - void addSupportingType_AddsASupportingCodeElement_WhenPassedAFullyQualifiedTypeName() { - Component component = new Component(); - component.addSupportingType("com.structurizr.web.HomePageViewModel"); - - Set codeElements = component.getCode(); - assertEquals(1, codeElements.size()); - CodeElement codeElement = codeElements.iterator().next(); - assertEquals("HomePageViewModel", codeElement.getName()); - assertEquals("com.structurizr.web.HomePageViewModel", codeElement.getType()); - assertEquals(CodeElementRole.Supporting, codeElement.getRole()); - } - - @Test - void getType_ReturnsNull_WhenThereAreNoCodeElements() { - Component component = new Component(); - assertNull(component.getType()); - } - - @Test - void getType_ReturnsNull_WhenThereAreNoPrimaryCodeElements() { - Component component = new Component(); - component.addSupportingType("com.structurizr.SomeType"); - assertNull(component.getType()); - } - - @Test - void getType_ReturnsThePrimaryCodeElement_WhenThereIsAPrimaryCodeElement() { - Component component = new Component(); - component.setType("com.structurizr.SomeType"); - CodeElement codeElement = component.getType(); - assertEquals(CodeElementRole.Primary, codeElement.getRole()); - assertEquals("com.structurizr.SomeType", codeElement.getType()); - } - } \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/model/ContainerTests.java b/structurizr-core/test/unit/com/structurizr/model/ContainerTests.java index 3665f5194..159f40805 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ContainerTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ContainerTests.java @@ -84,8 +84,6 @@ void addComponent_AddsAComponentWithTheSpecifiedNameAndDescription() { assertEquals("Name", component.getName()); assertEquals("Description", component.getDescription()); assertNull(component.getTechnology()); - assertNull(component.getType()); - assertEquals(0, component.getCode().size()); assertSame(container, component.getParent()); } @@ -96,32 +94,6 @@ void addComponent_AddsAComponentWithTheSpecifiedNameAndDescriptionAndTechnology( assertEquals("Name", component.getName()); assertEquals("Description", component.getDescription()); assertEquals("Technology", component.getTechnology()); - assertNull(component.getType()); - assertEquals(0, component.getCode().size()); - assertSame(container, component.getParent()); - } - - @Test - void addComponent_AddsAComponentWithTheSpecifiedNameAndDescriptionAndTechnologyAndStringType() { - Component component = container.addComponent("Name", "SomeType", "Description", "Technology"); - assertTrue(container.getComponents().contains(component)); - assertEquals("Name", component.getName()); - assertEquals("Description", component.getDescription()); - assertEquals("Technology", component.getTechnology()); - assertEquals("SomeType", component.getType().getType()); - assertEquals(1, component.getCode().size()); - assertSame(container, component.getParent()); - } - - @Test - void addComponent_AddsAComponentWithTheSpecifiedNameAndDescriptionAndTechnologyAndClassType() { - Component component = container.addComponent("Name", this.getClass(), "Description", "Technology"); - assertTrue(container.getComponents().contains(component)); - assertEquals("Name", component.getName()); - assertEquals("Description", component.getDescription()); - assertEquals("Technology", component.getTechnology()); - assertEquals("com.structurizr.model.ContainerTests", component.getType().getType()); - assertEquals(1, component.getCode().size()); assertSame(container, component.getParent()); } @@ -145,38 +117,4 @@ void getComponentWithName_ThrowsAnException_WhenAnEmptyNameIsSpecified() { } } - @Test - void getComponentOfType_ThrowsAnException_WhenANullTypeIsSpecified() { - try { - container.getComponentOfType(null); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("A component type must be provided.", iae.getMessage()); - } - } - - @Test - void getComponentOfType_ThrowsAnException_WhenAnEmptyTypeIsSpecified() { - try { - container.getComponentOfType(" "); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("A component type must be provided.", iae.getMessage()); - } - } - - @Test - void getComponentOfType_ReturnsNull_WhenNoComponentWithTheSpecifiedTypeExists() { - assertNull(container.getComponentOfType("SomeType")); - } - - @Test - void getComponentOfType_ReturnsAComponent_WhenAComponentWithTheSpecifiedTypeExists() { - container.addComponent("Name", "SomeType", "Description", "Technology"); - Component component = container.getComponentOfType("SomeType"); - - assertNotNull(component); - assertEquals("SomeType", component.getType().getType()); - } - } \ No newline at end of file From a16624d758ae0e19590688b1034e964cc155b53d Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 24 Feb 2023 16:26:51 +0000 Subject: [PATCH 376/717] Docs update. --- README.md | 10 ++-------- docs/client-side-encryption.md | 4 ++-- docs/faq.md | 12 +++++++++--- docs/model.md | 18 +++++++----------- docs/usage-patterns.md | 18 ------------------ 5 files changed, 20 insertions(+), 42 deletions(-) delete mode 100644 docs/usage-patterns.md diff --git a/README.md b/README.md index a8b396223..7f5d88274 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ or other formats including PlantUML, Mermaid, DOT, and WebSequenceDiagrams via t ## Table of contents +* [changelog](docs/changelog.md) * Introduction * [Getting started](docs/getting-started.md) * [Basic concepts](https://structurizr.com/help/concepts) (workspaces, models, views and documentation) @@ -38,7 +39,6 @@ or other formats including PlantUML, Mermaid, DOT, and WebSequenceDiagrams via t * [Binaries](docs/binaries.md) * [Building from source](docs/building.md) * [API client](docs/api-client.md) - * [Usage patterns](docs/usage-patterns.md) * [FAQ](docs/faq.md) * Model * [Creating your model](docs/model.md) @@ -54,16 +54,10 @@ or other formats including PlantUML, Mermaid, DOT, and WebSequenceDiagrams via t * [Styling elements](docs/styling-elements.md) * [Styling relationships](docs/styling-relationships.md) * [Filtered views](docs/filtered-views.md) - * [Graphviz automatic layout](https://github.com/structurizr/java-extensions/blob/master/structurizr-graphviz) * Other * [Client-side encryption](docs/client-side-encryption.md) * Related projects * [structurizr-dsl](https://github.com/structurizr/dsl): A text-based DSL for authoring Structurizr workspaces. * [structurizr-export](https://github.com/structurizr/export): Export model and views to external formats (e.g. PlantUML, Mermaid, etc). - * [structurizr-documentation](https://github.com/structurizr/documentation): Import Markdown/AsciiDoc documentation and ADRs into a Structurizr workspace. - * [java-extensions](https://github.com/structurizr/java-extensions): A collection of Structurizr for Java extensions; including the ability to extract software architecture information from code. - * [structurizr-kotlin](https://github.com/Catalysts/structurizr-extensions/tree/master/structurizr-kotlin): An extension for Structurizr that lets you create your models in a fluent way. - * [structurizr-spring-boot](https://github.com/Catalysts/structurizr-extensions/tree/master/structurizr-spring-boot): A way to apply dependency management to help modularise Structurizr code. - * [structurizr-groovy](https://github.com/tidyjava/structurizr-groovy): An initial version of a Groovy wrapper around Structurizr for Java. -* [changelog](docs/changelog.md) + * [structurizr-import](https://github.com/structurizr/import): Import Markdown/AsciiDoc documentation/ADRs into a Structurizr workspace. diff --git a/docs/client-side-encryption.md b/docs/client-side-encryption.md index cce9397e6..5115c7db1 100644 --- a/docs/client-side-encryption.md +++ b/docs/client-side-encryption.md @@ -1,8 +1,8 @@ # Client-side encryption -> Note: this page describes a feature that is not available to use with Structurizr's Free Plan. +> This feature is not available with a free Structurizr cloud service account. -The JSON representation of your workspace is stored on the Structurizr servers using AES encryption with a 128-bit key, a random salt and a server-side passphrase. For additional peace of mind, you can choose to encrypt your workspace with your own passphrase on the client before uploading it to Structurizr. In order to view a client-side encrypted workspace, you will be asked to enter your passphrase when you open the workspace in your web browser. The passphrase is then used to decrypt the workspace in your web browser - at no point does the passphrase leave your computer. +The JSON representation of your workspace is stored on the Structurizr cloud service using AES encryption with a 128-bit key, a random salt and a server-side passphrase. For additional peace of mind, you can choose to encrypt your workspace with your own passphrase on the client before uploading it to Structurizr. In order to view a client-side encrypted workspace, you will be asked to enter your passphrase when you open the workspace in your web browser. The passphrase is then used to decrypt the workspace in your web browser - at no point does the passphrase leave your computer. To use client-side encryption, simply create an instance of ```AesEncryptionStrategy``` and associate it with your ```StructurizrClient``` instance. For example: diff --git a/docs/faq.md b/docs/faq.md index cae05f899..29f78a937 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -2,11 +2,17 @@ ## Why are many classes final with package-protected members, and not open to extension? -First and foremost, this repo is a client library for the [Structurizr cloud service and on-premises installation](https://structurizr.com). It allows you to write Java code to create an in-memory object graph representing a software architecture model and views (a "workspace"), serialize that to JSON, and upload it via a web API. The workspace has an [OpenAPI definition](https://github.com/structurizr/json/blob/master/structurizr.yaml), but this library also implements a number of rules (think of them as the "business logic") to ensure that the workspace is valid. These rules include, for example, ensuring that all containers with a software system have unique names, and that you can't add components to a system context view. +First and foremost, this repo is a client library for the [Structurizr cloud service and on-premises installation](https://structurizr.com). +It allows you to write Java code to create an in-memory object graph representing a software architecture model and views (a "workspace"), serialize that to JSON, and upload it via a web API. +The workspace has an [OpenAPI definition](https://github.com/structurizr/json/blob/master/structurizr.yaml), but this library also implements a number of rules (think of them as the "business logic") to ensure that the workspace is valid. +These rules include, for example, ensuring that all containers with a software system have unique names, and that you can't add components to a system context view. -Removing the `final` modifier from the classes and leaving the them open for extension allows you to bypass/break these rules, which will likely lead to the serialized workspace definitions being incompatible with the Structurizr cloud service and on-premises installation. The output of this library also needs to be compatible with all of the other client libraries. +Removing the `final` modifier from the classes and leaving them open for extension allows you to bypass/break these rules, which will likely lead to the serialized workspace definitions being incompatible with the various diagram rendering tools +(i.e. the Structurizr cloud service/on-premises installation/Lite, plus the PlantUML/Mermaid exporters). +The output of this library also needs to be compatible with client libraries written in other languages. -You are welcome to fork this library for your own purposes. Alternatively, you can build a thin wrapper around the library, to provide your own custom functionality, or perhaps a more fluent API ... many teams have done this. +You are welcome to fork this library for your own purposes. +Alternatively, you can build a thin wrapper around the library, to provide your own custom functionality, or perhaps a more fluent API ... many teams have done this. ## Can I submit a pull request? diff --git a/docs/model.md b/docs/model.md index 7c5999e19..969755d6d 100644 --- a/docs/model.md +++ b/docs/model.md @@ -1,24 +1,20 @@ # Model -This is the definition of the software architecture model, consisting of people, software systems, containers, components, code elements and deployment nodes, plus the relationships between them. +This is the definition of the software architecture model, consisting of people, software systems, containers, components, deployment nodes, etc plus the relationships between them. All of the Java classes representing people, software systems, containers, components, etc, and the functionality related to creating a software architecture model can be found in the [com.structurizr.model](https://github.com/structurizr/java/tree/master/structurizr-core/src/com/structurizr/model) package. An empty model is created for you when you create a workspace. ```java -Workspace workspace = new Workspace("Getting Started", "This is a model of my software system."); +Workspace workspace = new Workspace("Name", "Description"); Model model = workspace.getModel(); ``` -Once you have a reference to a ```Model``` instance, you can add elements to it manually or automatically, using static analysis and reflection techniques. +Once you have a reference to a ```Model``` instance, you can add elements to it via the various public `add*` methods that you'll find on ```Model```, ```SoftwareSystem```, ```Container```, etc. -## 1. Manual model creation +## Automatic model generation -Manually adding elements to the model is the simplest way to use the Structurizr for Java client library. This can be done using the various public ```add*``` methods that you'll find on ```Model```, ```SoftwareSystem```, ```Container```, ```Component```, etc. - -## 2. Automatic extraction - -You can also extract components (and add them to a ```Container``` instance) automatically from a given codebase, using a number of different component finder strategies. See [Component finder](https://github.com/structurizr/java-extensions/blob/master/docs/component-finder.md) for more details. - -Although there is nothing included in the Structurizr for Java library to support this, you could also choose to parse an external definition of your software architecture (e.g. an AWS infrastructure topology, another Architecture Description Language, etc) and create model elements accordingly. \ No newline at end of file +Although there is nothing included in the Structurizr for Java library to support automatic model generation, +you could choose to parse an external definition of your software architecture (e.g. an AWS infrastructure topology, Terraform definition, another Architecture Description Language, your source code, etc) +and create model elements accordingly. \ No newline at end of file diff --git a/docs/usage-patterns.md b/docs/usage-patterns.md deleted file mode 100644 index a2958b185..000000000 --- a/docs/usage-patterns.md +++ /dev/null @@ -1,18 +0,0 @@ -# Usage patterns - -## Single program - -The simplest way to create a software architecture model is to write a single Java program that first creates the model elements (people, software systems, containers and components) before subsequently creating the required views and uploading the workspace to Structurizr. If you have a particularly large model, or you'd like to share common elements between models, then one approach is to modularise your program appropriately, perhaps using something like [Structurizr Extensions](https://github.com/Catalysts/structurizr-extensions). - -## Multiple programs - -Another approach is to write a collection of Java programs, that are each responsible for creating a different part of the software architecture model. This is especially useful if you need to use the component finder with different classpaths. You can then write a script, or use your build script, to run these Java programs in sequence. Intermediate versions of the workspace can be saved to and loaded from disk using the [WorkspaceUtils](https://github.com/structurizr/java/blob/master/structurizr-client/src/com/structurizr/util/WorkspaceUtils.java) class. For example: - -1. Program 1: Create the basic model elements (people, software systems and containers) and the relationships between them. -2. Program 2: Add components for container 1 (e.g. run the component finder). -3. Program 3: Add components for container 2 (e.g. run the component finder with a different classpath). -4. Program 4: Create views. -5. Program 5: Add styling. -6. Program 6: Upload to Structurizr. - -In this example, the first program would write the initial version of the workspace to a local file on disk, which subsequent programs then load and add to, before writing the workspace back to disk. From 4a648a90f68d64afea3318bd2c35a30dc6a42f7d Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 24 Feb 2023 16:28:59 +0000 Subject: [PATCH 377/717] Reorganise TOC. --- README.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 7f5d88274..b6069bca3 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ or other formats including PlantUML, Mermaid, DOT, and WebSequenceDiagrams via t ## Table of contents -* [changelog](docs/changelog.md) +* [Changelog](docs/changelog.md) * Introduction * [Getting started](docs/getting-started.md) * [Basic concepts](https://structurizr.com/help/concepts) (workspaces, models, views and documentation) @@ -38,24 +38,24 @@ or other formats including PlantUML, Mermaid, DOT, and WebSequenceDiagrams via t * [Examples](https://github.com/structurizr/examples) * [Binaries](docs/binaries.md) * [Building from source](docs/building.md) - * [API client](docs/api-client.md) * [FAQ](docs/faq.md) * Model * [Creating your model](docs/model.md) * [Implied relationships](docs/implied-relationships.md) * Views - * [Creating views](docs/views.md) - * [System Context diagram](docs/system-context-diagram.md) - * [Container diagram](docs/container-diagram.md) - * [Component diagram](docs/component-diagram.md) - * [Dynamic diagram](docs/dynamic-diagram.md) - * [Deployment diagram](docs/deployment-diagram.md) - * [System Landscape diagram](docs/system-landscape-diagram.md) - * [Styling elements](docs/styling-elements.md) - * [Styling relationships](docs/styling-relationships.md) - * [Filtered views](docs/filtered-views.md) -* Other - * [Client-side encryption](docs/client-side-encryption.md) + * [Creating views](docs/views.md) + * [System Context diagram](docs/system-context-diagram.md) + * [Container diagram](docs/container-diagram.md) + * [Component diagram](docs/component-diagram.md) + * [Dynamic diagram](docs/dynamic-diagram.md) + * [Deployment diagram](docs/deployment-diagram.md) + * [System Landscape diagram](docs/system-landscape-diagram.md) + * [Styling elements](docs/styling-elements.md) + * [Styling relationships](docs/styling-relationships.md) + * [Filtered views](docs/filtered-views.md) +* Cloud service/on-premises installation + * [API client](docs/api-client.md) + * [Client-side encryption](docs/client-side-encryption.md) * Related projects * [structurizr-dsl](https://github.com/structurizr/dsl): A text-based DSL for authoring Structurizr workspaces. * [structurizr-export](https://github.com/structurizr/export): Export model and views to external formats (e.g. PlantUML, Mermaid, etc). From 2c8c0b09549345c4f7d7445440b59b1943d391e1 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 25 Feb 2023 13:19:19 +0000 Subject: [PATCH 378/717] Adds a utility method to find the content type of a URL. --- .../src/com/structurizr/util/ImageUtils.java | 18 ++++++++++++++++++ .../com/structurizr/util/ImageUtilsTests.java | 18 +++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/structurizr-core/src/com/structurizr/util/ImageUtils.java b/structurizr-core/src/com/structurizr/util/ImageUtils.java index 6b7fdeee2..18115ed19 100644 --- a/structurizr-core/src/com/structurizr/util/ImageUtils.java +++ b/structurizr-core/src/com/structurizr/util/ImageUtils.java @@ -6,6 +6,7 @@ import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; +import java.net.URL; import java.net.URLConnection; import java.util.Base64; @@ -38,6 +39,23 @@ public static String getContentType(@Nonnull File file) throws IOException { return contentType; } + /** + * Gets the content type of the specified URL representing an image. + * + * @param url a URL pointing to an image + * @return a content type (e.g. "image/png") + * @throws IOException if there is an error reading the file + */ + public static String getContentType(String url) throws IOException { + if (StringUtils.isNullOrEmpty(url)) { + throw new IllegalArgumentException("A URL must be specified."); + } + + URLConnection connection = new URL(url).openConnection(); + connection.setConnectTimeout(1000 * 30); + return connection.getContentType(); + } + /** * Gets the content of an image as a Base64 encoded string. * diff --git a/structurizr-core/test/unit/com/structurizr/util/ImageUtilsTests.java b/structurizr-core/test/unit/com/structurizr/util/ImageUtilsTests.java index 819201ab7..a30598e1c 100644 --- a/structurizr-core/test/unit/com/structurizr/util/ImageUtilsTests.java +++ b/structurizr-core/test/unit/com/structurizr/util/ImageUtilsTests.java @@ -11,7 +11,7 @@ public class ImageUtilsTests { @Test void getContentType_ThrowsAnException_WhenANullFileIsSpecified() throws Exception { try { - ImageUtils.getContentType(null); + ImageUtils.getContentType((File)null); fail(); } catch (IllegalArgumentException iae) { assertEquals("A file must be specified.", iae.getMessage()); @@ -56,6 +56,22 @@ void getContentType_ReturnsTheContentType_WhenAFileIsSpecified() throws Exceptio assertEquals("image/png", contentType); } + @Test + void getContentType_ThrowsAnException_WhenANullUrlIsSpecified() throws Exception { + try { + ImageUtils.getContentType((String)null); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("A URL must be specified.", iae.getMessage()); + } + } + + @Test + void getContentType_ReturnsTheContentType_WhenAUrlIsSpecified() throws Exception { + String contentType = ImageUtils.getContentType(new File("../structurizr-core/test/unit/com/structurizr/util/structurizr-logo.png").toURI().toURL().toExternalForm()); + assertEquals("image/png", contentType); + } + @Test void getImageAsBase64_ThrowsAnException_WhenANullFileIsSpecified() throws Exception { try { From 9b31d5d3fd33075b565ed7c8a36b127dd400797c Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 25 Feb 2023 15:30:10 +0000 Subject: [PATCH 379/717] Adds some support for getting the content types from data URIs. Also adds support for allow SVG. --- .../src/com/structurizr/util/ImageUtils.java | 39 +++++++++++++++++-- .../com/structurizr/util/ImageUtilsTests.java | 25 ++++++++++-- 2 files changed, 58 insertions(+), 6 deletions(-) diff --git a/structurizr-core/src/com/structurizr/util/ImageUtils.java b/structurizr-core/src/com/structurizr/util/ImageUtils.java index 18115ed19..d656c143a 100644 --- a/structurizr-core/src/com/structurizr/util/ImageUtils.java +++ b/structurizr-core/src/com/structurizr/util/ImageUtils.java @@ -15,6 +15,15 @@ */ public class ImageUtils { + public static final String DATA_URI_PREFIX = "data:"; + public static final String DATA_URI_IMAGE_PNG = "data:image/png;base64,"; + public static final String DATA_URI_IMAGE_JPG = "data:image/jpeg;base64,"; + public static final String DATA_URI_IMAGE_SVG = "data:image/svg+xml;"; + + public static final String CONTENT_TYPE_IMAGE_PNG = "image/png"; + public static final String CONTENT_TYPE_IMAGE_JPG = "image/jpeg"; + public static final String CONTENT_TYPE_IMAGE_SVG = "image/svg+xml"; + /** * Gets the content type of the specified file representing an image. * @@ -56,6 +65,28 @@ public static String getContentType(String url) throws IOException { return connection.getContentType(); } + /** + * Gets the content type of the specified data URI representing an image. + * + * @param dataUri a data URI representing an image + * @return a content type (e.g. "image/png") + */ + public static String getContentTypeFromDataUri(String dataUri) { + if (StringUtils.isNullOrEmpty(dataUri)) { + throw new IllegalArgumentException("A data URI must be specified."); + } + + if (dataUri.startsWith(DATA_URI_IMAGE_PNG)) { + return CONTENT_TYPE_IMAGE_PNG; + } else if (dataUri.startsWith(DATA_URI_IMAGE_JPG)) { + return CONTENT_TYPE_IMAGE_JPG; + } else if (dataUri.startsWith(DATA_URI_IMAGE_SVG)) { + return CONTENT_TYPE_IMAGE_SVG; + } + + return null; + } + /** * Gets the content of an image as a Base64 encoded string. * @@ -84,7 +115,7 @@ public static String getImageAsDataUri(File file) throws IOException { String contentType = getContentType(file); String base64Content = getImageAsBase64(file); - return "data:" + contentType + ";base64," + base64Content; + return DATA_URI_PREFIX + contentType + ";base64," + base64Content; } public static void validateImage(String imageDescriptor) { @@ -104,7 +135,7 @@ public static void validateImage(String imageDescriptor) { return; } - if (imageDescriptor.startsWith("data:image")) { + if (imageDescriptor.startsWith(DATA_URI_PREFIX)) { if (ImageUtils.isSupportedDataUri(imageDescriptor)) { // it's a PNG/JPG data URI return; @@ -118,7 +149,9 @@ public static void validateImage(String imageDescriptor) { } public static boolean isSupportedDataUri(String uri) { - return uri.startsWith("data:image/png;base64,") || uri.startsWith("data:image/jpeg;base64,"); + return uri.startsWith(DATA_URI_IMAGE_PNG) || + uri.startsWith(DATA_URI_IMAGE_JPG) || + uri.startsWith(DATA_URI_IMAGE_SVG); } } \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/util/ImageUtilsTests.java b/structurizr-core/test/unit/com/structurizr/util/ImageUtilsTests.java index a30598e1c..81892cd78 100644 --- a/structurizr-core/test/unit/com/structurizr/util/ImageUtilsTests.java +++ b/structurizr-core/test/unit/com/structurizr/util/ImageUtilsTests.java @@ -72,6 +72,24 @@ void getContentType_ReturnsTheContentType_WhenAUrlIsSpecified() throws Exception assertEquals("image/png", contentType); } + @Test + void getContentTypeFromDataUri_ThrowsAnException_WhenANullDataUriIsSpecified() throws Exception { + try { + ImageUtils.getContentTypeFromDataUri(null); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("A data URI must be specified.", iae.getMessage()); + } + } + + @Test + void getContentTypeFromDataUri_ReturnsTheContentType_WhenAUrlIsSpecified() throws Exception { + assertEquals("image/png", ImageUtils.getContentTypeFromDataUri("data:image/png;base64,...")); + assertEquals("image/jpeg", ImageUtils.getContentTypeFromDataUri("data:image/jpeg;base64,...")); + assertEquals("image/svg+xml", ImageUtils.getContentTypeFromDataUri("data:image/svg+xml;utf8,...")); + assertNull(ImageUtils.getContentTypeFromDataUri("data:...")); + } + @Test void getImageAsBase64_ThrowsAnException_WhenANullFileIsSpecified() throws Exception { try { @@ -179,13 +197,14 @@ void validateImage() { ImageUtils.validateImage("image.jpg"); ImageUtils.validateImage("image.jpeg"); ImageUtils.validateImage("image.gif"); + ImageUtils.validateImage("data:image/svg+xml;utf8,iVBORw0KGg"); //disallowed try { - ImageUtils.validateImage("data:image/svg+xml;base64,iVBORw0KGg"); + ImageUtils.validateImage("data:image/other"); fail(); } catch (Exception e) { - assertEquals("Only PNG and JPG data URIs are supported: data:image/svg+xml;base64,iVBORw0KGg", e.getMessage()); + assertEquals("Only PNG and JPG data URIs are supported: data:image/other", e.getMessage()); } } @@ -193,7 +212,7 @@ void validateImage() { void isSupportedDataUri() { assertTrue(ImageUtils.isSupportedDataUri("data:image/png;base64,iVBORw0KGg")); assertTrue(ImageUtils.isSupportedDataUri("data:image/jpeg;base64,iVBORw0KGg")); - assertFalse(ImageUtils.isSupportedDataUri("data:image/svg+xml;base64,iVBORw0KGg")); + assertTrue(ImageUtils.isSupportedDataUri("data:image/svg+xml;utf8, Date: Sat, 25 Feb 2023 15:30:38 +0000 Subject: [PATCH 380/717] Refactor. --- structurizr-core/src/com/structurizr/util/Url.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structurizr-core/src/com/structurizr/util/Url.java b/structurizr-core/src/com/structurizr/util/Url.java index d77ef9f95..de4d7555e 100644 --- a/structurizr-core/src/com/structurizr/util/Url.java +++ b/structurizr-core/src/com/structurizr/util/Url.java @@ -15,7 +15,7 @@ public class Url { * @return true if the URL is valid, false otherwise */ public static boolean isUrl(String urlAsString) { - if (urlAsString != null && urlAsString.trim().length() > 0) { + if (!StringUtils.isNullOrEmpty(urlAsString)) { try { new URL(urlAsString); return true; From 97019b974dcb333d3ff1fa625c362b6e11d0ffff Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 26 Feb 2023 08:17:33 +0000 Subject: [PATCH 381/717] Adds support for element/relationship URLs of the form `{workspace}/...` for linking to diagrams/documentation/decisions in the same workspace. --- docs/changelog.md | 1 + .../src/com/structurizr/model/ModelItem.java | 10 +++++++--- structurizr-core/src/com/structurizr/util/Url.java | 2 ++ .../unit/com/structurizr/model/ModelItemTests.java | 14 ++++++++++++++ 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 59fc17e5a..1d947df19 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -3,6 +3,7 @@ ## 1.21.0 (unreleased) - __Breaking change__: Removes the concept of "code elements" from `Component`. +- Adds support for element/relationship URLs of the form `{workspace}/...` for linking to diagrams/documentation/decisions in the same workspace. ## 1.20.1 (16th February 2023) diff --git a/structurizr-core/src/com/structurizr/model/ModelItem.java b/structurizr-core/src/com/structurizr/model/ModelItem.java index e95ea9148..4cdf2d304 100644 --- a/structurizr-core/src/com/structurizr/model/ModelItem.java +++ b/structurizr-core/src/com/structurizr/model/ModelItem.java @@ -122,10 +122,14 @@ public String getUrl() { public void setUrl(String url) { if (StringUtils.isNullOrEmpty(url)) { this.url = null; - } else if (Url.isUrl(url)) { - this.url = url; } else { - throw new IllegalArgumentException(url + " is not a valid URL."); + if (url.startsWith(Url.WORKSPACE_URL_PREFIX)) { + this.url = url; + } else if (Url.isUrl(url)) { + this.url = url; + } else { + throw new IllegalArgumentException(url + " is not a valid URL."); + } } } diff --git a/structurizr-core/src/com/structurizr/util/Url.java b/structurizr-core/src/com/structurizr/util/Url.java index de4d7555e..49e69ab87 100644 --- a/structurizr-core/src/com/structurizr/util/Url.java +++ b/structurizr-core/src/com/structurizr/util/Url.java @@ -8,6 +8,8 @@ */ public class Url { + public static final String WORKSPACE_URL_PREFIX = "{workspace}"; + /** * Determines whether the supplied string is a valid URL. * diff --git a/structurizr-core/test/unit/com/structurizr/model/ModelItemTests.java b/structurizr-core/test/unit/com/structurizr/model/ModelItemTests.java index ae9c33852..7013a0227 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ModelItemTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ModelItemTests.java @@ -218,4 +218,18 @@ void addPerspective_ThrowsAnException_WhenTheNamedPerspectiveAlreadyExists() { } } + @Test + void setUrl_AcceptsAUrl() { + Element element = model.addSoftwareSystem("Name"); + element.setUrl("https://structurizr.com"); + assertEquals("https://structurizr.com", element.getUrl()); + } + + @Test + void setUrl_AcceptsAWorkspaceUrl() { + Element element = model.addSoftwareSystem("Name"); + element.setUrl("{workspace}/diagrams#key"); + assertEquals("{workspace}/diagrams#key", element.getUrl()); + } + } \ No newline at end of file From af6268978ca06ac27732cae2eea2edab1392bfbc Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 26 Feb 2023 08:18:25 +0000 Subject: [PATCH 382/717] Adds release date. --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 1d947df19..5bb997bc7 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 1.21.0 (unreleased) +## 1.21.0 (26th February 2023) - __Breaking change__: Removes the concept of "code elements" from `Component`. - Adds support for element/relationship URLs of the form `{workspace}/...` for linking to diagrams/documentation/decisions in the same workspace. From ae91d82b80beb3202eb55e851a74e33f05498c08 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 5 Mar 2023 11:53:52 +0000 Subject: [PATCH 383/717] Adds documentation to components. --- build.gradle | 2 +- docs/changelog.md | 4 ++++ .../src/com/structurizr/model/Component.java | 24 ++++++++++++++++++- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 5a9142c6b..242af024b 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.21.0' + version = '1.22.0' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 5bb997bc7..83af10b9e 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.22.0 (5th March 2023) + +- Adds documentation to components. + ## 1.21.0 (26th February 2023) - __Breaking change__: Removes the concept of "code elements" from `Component`. diff --git a/structurizr-core/src/com/structurizr/model/Component.java b/structurizr-core/src/com/structurizr/model/Component.java index 06e3e7975..a1b63db61 100644 --- a/structurizr-core/src/com/structurizr/model/Component.java +++ b/structurizr-core/src/com/structurizr/model/Component.java @@ -1,6 +1,8 @@ package com.structurizr.model; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.structurizr.documentation.Documentable; +import com.structurizr.documentation.Documentation; import java.util.Arrays; import java.util.LinkedHashSet; @@ -9,12 +11,14 @@ /** * Represents a "component" in the C4 model. */ -public final class Component extends StaticStructureElement { +public final class Component extends StaticStructureElement implements Documentable { private Container parent; private String technology; + private Documentation documentation = new Documentation(); + Component() { } @@ -67,4 +71,22 @@ public Set getDefaultTags() { return new LinkedHashSet<>(Arrays.asList(Tags.ELEMENT, Tags.COMPONENT)); } + /** + * Gets the documentation associated with this component. + * + * @return a Documentation object + */ + public Documentation getDocumentation() { + return documentation; + } + + /** + * Sets the documentation associated with this component. + * + * @param documentation a Documentation object + */ + void setDocumentation(Documentation documentation) { + this.documentation = documentation; + } + } \ No newline at end of file From 054e58f0480a5036e504daf5ccd638e643927335 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 5 Mar 2023 12:10:43 +0000 Subject: [PATCH 384/717] Removes unused documentation section title property. --- build.gradle | 2 +- docs/changelog.md | 6 ++- .../structurizr/documentation/Decision.java | 14 +++++++ .../documentation/Documentation.java | 10 ----- .../documentation/DocumentationContent.java | 14 ------- .../structurizr/documentation/Section.java | 9 ++--- .../documentation/DocumentationTests.java | 38 ++----------------- .../documentation/SectionTests.java | 3 +- 8 files changed, 28 insertions(+), 68 deletions(-) diff --git a/build.gradle b/build.gradle index 242af024b..9a4737aac 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.22.0' + version = '1.22.1' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 83af10b9e..5e26f191a 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,8 +1,12 @@ # Changelog +## 1.22.1 (5th March 2023) + +- Removes unused documentation section title property. + ## 1.22.0 (5th March 2023) -- Adds documentation to components. +- Adds documentation to components. ## 1.21.0 (26th February 2023) diff --git a/structurizr-core/src/com/structurizr/documentation/Decision.java b/structurizr-core/src/com/structurizr/documentation/Decision.java index bb0ee574b..96e1fd2ac 100644 --- a/structurizr-core/src/com/structurizr/documentation/Decision.java +++ b/structurizr-core/src/com/structurizr/documentation/Decision.java @@ -12,6 +12,7 @@ public final class Decision extends DocumentationContent { private String id; + private String title; private Date date; private String status; @@ -37,6 +38,19 @@ void setId(String id) { this.id = id; } + /** + * Gets the title. + * + * @return the title, as a String + */ + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + /** * Gets the date of this decision. * diff --git a/structurizr-core/src/com/structurizr/documentation/Documentation.java b/structurizr-core/src/com/structurizr/documentation/Documentation.java index 0155113f5..27a85bcee 100644 --- a/structurizr-core/src/com/structurizr/documentation/Documentation.java +++ b/structurizr-core/src/com/structurizr/documentation/Documentation.java @@ -29,9 +29,7 @@ public Documentation() { * @param section a Section object */ public void addSection(Section section) { - checkTitleIsSpecified(section.getTitle()); checkContentIsSpecified(section.getContent()); - checkSectionIsUnique(section.getTitle()); checkFormatIsSpecified(section.getFormat()); section.setOrder(calculateOrder()); @@ -56,14 +54,6 @@ private void checkFormatIsSpecified(Format format) { } } - private void checkSectionIsUnique(String title) { - for (Section section : sections) { - if (title.equals(section.getTitle())) { - throw new IllegalArgumentException("A section with a title of " + title + " already exists in this scope."); - } - } - } - private int calculateOrder() { return sections.size() + 1; } diff --git a/structurizr-core/src/com/structurizr/documentation/DocumentationContent.java b/structurizr-core/src/com/structurizr/documentation/DocumentationContent.java index 5013ce3a7..1759f81df 100644 --- a/structurizr-core/src/com/structurizr/documentation/DocumentationContent.java +++ b/structurizr-core/src/com/structurizr/documentation/DocumentationContent.java @@ -8,7 +8,6 @@ public abstract class DocumentationContent { // elementId is here for backwards compatibility private String elementId; - private String title; private String content; private Format format; @@ -29,19 +28,6 @@ void setElementId(String elementId) { this.elementId = elementId; } - /** - * Gets the title. - * - * @return the title, as a String - */ - public String getTitle() { - return title; - } - - public void setTitle(String title) { - this.title = title; - } - /** * Gets the content. * diff --git a/structurizr-core/src/com/structurizr/documentation/Section.java b/structurizr-core/src/com/structurizr/documentation/Section.java index ea5cc0b42..941f3aaba 100644 --- a/structurizr-core/src/com/structurizr/documentation/Section.java +++ b/structurizr-core/src/com/structurizr/documentation/Section.java @@ -11,8 +11,7 @@ public final class Section extends DocumentationContent { public Section() { } - public Section(String title, Format format, String content) { - setTitle(title); + public Section(Format format, String content) { setFormat(format); setContent(content); } @@ -55,16 +54,16 @@ public boolean equals(Object object) { Section section = (Section)object; if (getElementId() != null) { - return getElementId().equals(section.getElementId()) && getTitle().equals(section.getTitle()); + return getElementId().equals(section.getElementId()) && getContent().equals(section.getContent()); } else { - return getTitle().equals(section.getTitle()); + return getContent().equals(section.getContent()); } } @Override public int hashCode() { int result = getElementId() != null ? getElementId().hashCode() : 0; - result = 31 * result + getTitle().hashCode(); + result = 31 * result + getContent().hashCode(); return result; } diff --git a/structurizr-core/test/unit/com/structurizr/documentation/DocumentationTests.java b/structurizr-core/test/unit/com/structurizr/documentation/DocumentationTests.java index ee4ea8957..0526bed2a 100644 --- a/structurizr-core/test/unit/com/structurizr/documentation/DocumentationTests.java +++ b/structurizr-core/test/unit/com/structurizr/documentation/DocumentationTests.java @@ -15,23 +15,10 @@ public void setUp() { documentation = workspace.getDocumentation(); } - @Test - void addSection_ThrowsAnException_WhenTheTitleIsNotSpecified() { - try { - Section section = new Section(); - - documentation.addSection(section); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("A title must be specified.", iae.getMessage()); - } - } - @Test void addSection_ThrowsAnException_WhenTheContentIsNotSpecified() { try { Section section = new Section(); - section.setTitle("Title"); documentation.addSection(section); fail(); @@ -44,7 +31,6 @@ void addSection_ThrowsAnException_WhenTheContentIsNotSpecified() { void addSection_ThrowsAnException_WhenTheFormatIsNotSpecified() { try { Section section = new Section(); - section.setTitle("Title"); section.setContent("Content"); documentation.addSection(section); @@ -54,26 +40,9 @@ void addSection_ThrowsAnException_WhenTheFormatIsNotSpecified() { } } - @Test - void addSection_ThrowsAnException_WhenASectionExistsWithTheSameTitle() { - try { - Section section = new Section(); - section.setTitle("Title"); - section.setContent("Content"); - section.setFormat(Format.Markdown); - - documentation.addSection(section); - documentation.addSection(section); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("A section with a title of Title already exists in this scope.", iae.getMessage()); - } - } - @Test void addSection() { Section section = new Section(); - section.setTitle("Title"); section.setContent("Content"); section.setFormat(Format.Markdown); @@ -81,7 +50,6 @@ void addSection() { assertEquals(1, documentation.getSections().size()); assertTrue(documentation.getSections().contains(section)); - assertEquals("Title", section.getTitle()); assertEquals(Format.Markdown, section.getFormat()); assertEquals("Content", section.getContent()); assertEquals(1, section.getOrder()); @@ -89,9 +57,9 @@ void addSection() { @Test void addSection_IncrementsTheSectionOrderNumber() { - Section section1 = new Section("Title 1", Format.Markdown, "Content"); - Section section2 = new Section("Title 2", Format.Markdown, "Content"); - Section section3 = new Section("Title 3", Format.Markdown, "Content"); + Section section1 = new Section(Format.Markdown, "Content 1"); + Section section2 = new Section(Format.Markdown, "Content 2"); + Section section3 = new Section(Format.Markdown, "Content 3"); documentation.addSection(section1); documentation.addSection(section2); diff --git a/structurizr-core/test/unit/com/structurizr/documentation/SectionTests.java b/structurizr-core/test/unit/com/structurizr/documentation/SectionTests.java index 282e92c5e..0f1705d44 100644 --- a/structurizr-core/test/unit/com/structurizr/documentation/SectionTests.java +++ b/structurizr-core/test/unit/com/structurizr/documentation/SectionTests.java @@ -8,9 +8,8 @@ public class SectionTests { @Test void construction() { - Section section = new Section("Title", Format.Markdown, "Content"); + Section section = new Section(Format.Markdown, "Content"); - assertEquals("Title", section.getTitle()); assertEquals(Format.Markdown, section.getFormat()); assertEquals("Content", section.getContent()); } From fe12158c62634122ff96f57b5974e23cfa9e3906 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 10 Mar 2023 09:11:14 +0000 Subject: [PATCH 385/717] Updates Jackson library dependency. --- build.gradle | 2 +- docs/changelog.md | 4 ++++ structurizr-client/build.gradle | 2 +- structurizr-core/build.gradle | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 9a4737aac..fb84c5120 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.22.1' + version = '1.22.2' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 5e26f191a..45a7bb679 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.22.2 (10th March 2023) + +- Updates Jackson library dependency. + ## 1.22.1 (5th March 2023) - Removes unused documentation section title property. diff --git a/structurizr-client/build.gradle b/structurizr-client/build.gradle index 881ac490f..77d8bbcf7 100644 --- a/structurizr-client/build.gradle +++ b/structurizr-client/build.gradle @@ -2,7 +2,7 @@ dependencies { api project(':structurizr-core') - api 'com.fasterxml.jackson.core:jackson-databind:2.14.1' + api 'com.fasterxml.jackson.core:jackson-databind:2.14.2' api 'org.apache.httpcomponents.client5:httpclient5:5.2.1' api 'javax.xml.bind:jaxb-api:2.3.1' diff --git a/structurizr-core/build.gradle b/structurizr-core/build.gradle index 9bc594da4..54190e009 100644 --- a/structurizr-core/build.gradle +++ b/structurizr-core/build.gradle @@ -1,6 +1,6 @@ dependencies { - api 'com.fasterxml.jackson.core:jackson-annotations:2.14.1' + api 'com.fasterxml.jackson.core:jackson-annotations:2.14.2' api 'com.google.code.findbugs:jsr305:3.0.2' api 'commons-logging:commons-logging:1.2' From 864f5de25605827a59cb959613b64d50106647e0 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 11 Mar 2023 06:11:41 +0000 Subject: [PATCH 386/717] Adds better backwards compatibility for removal of documentation sections. --- build.gradle | 2 +- docs/changelog.md | 4 ++++ .../api/BackwardsCompatibilityTests.java | 14 ++++++++++++++ .../src/com/structurizr/documentation/Section.java | 12 ++++++++++++ 4 files changed, 31 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index fb84c5120..813ded0db 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.22.2' + version = '1.22.3' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 45a7bb679..ed55dbae3 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.22.3 (11th March 2023) + +- Adds better backwards compatibility for removal of documentation sections. + ## 1.22.2 (10th March 2023) - Updates Jackson library dependency. diff --git a/structurizr-client/test/integration/com/structurizr/api/BackwardsCompatibilityTests.java b/structurizr-client/test/integration/com/structurizr/api/BackwardsCompatibilityTests.java index e4177ab26..646f93a86 100644 --- a/structurizr-client/test/integration/com/structurizr/api/BackwardsCompatibilityTests.java +++ b/structurizr-client/test/integration/com/structurizr/api/BackwardsCompatibilityTests.java @@ -1,10 +1,16 @@ package com.structurizr.api; +import com.structurizr.Workspace; +import com.structurizr.documentation.Format; +import com.structurizr.documentation.Section; +import com.structurizr.model.Location; import com.structurizr.util.WorkspaceUtils; import org.junit.jupiter.api.Test; import java.io.File; +import static org.junit.jupiter.api.Assertions.assertEquals; + class BackwardsCompatibilityTests { private static final File PATH_TO_WORKSPACE_FILES = new File("test/integration/backwardsCompatibility"); @@ -16,4 +22,12 @@ void test() throws Exception { } } + @Test + void documentation() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getDocumentation().addSection(new Section(Format.Markdown, "## Heading 1")); + + assertEquals("{\"id\":0,\"name\":\"Name\",\"description\":\"Description\",\"configuration\":{},\"model\":{},\"documentation\":{\"sections\":[{\"content\":\"## Heading 1\",\"format\":\"Markdown\",\"order\":1,\"title\":\"\"}]},\"views\":{\"configuration\":{\"branding\":{},\"styles\":{},\"terminology\":{}}}}", WorkspaceUtils.toJson(workspace, false)); + } + } \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/documentation/Section.java b/structurizr-core/src/com/structurizr/documentation/Section.java index 941f3aaba..85fc28002 100644 --- a/structurizr-core/src/com/structurizr/documentation/Section.java +++ b/structurizr-core/src/com/structurizr/documentation/Section.java @@ -1,5 +1,8 @@ package com.structurizr.documentation; +import com.fasterxml.jackson.annotation.JsonGetter; +import com.fasterxml.jackson.annotation.JsonInclude; + /** * A documentation section. */ @@ -16,6 +19,15 @@ public Section(Format format, String content) { setContent(content); } + /** + * This method is retained for backwards compatibility. + */ + @JsonGetter + @JsonInclude(JsonInclude.Include.ALWAYS) + public String getTitle() { + return ""; + } + /** * Gets the filename of this section. * From 39d4a04f35d6ae4bc0b67134a623b70408226e85 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 11 Mar 2023 08:16:55 +0000 Subject: [PATCH 387/717] Deprecates Enterprise and Location concepts. --- build.gradle | 2 +- docs/changelog.md | 4 ++ .../api/BackwardsCompatibilityTests.java | 17 ++++++ .../src/com/structurizr/model/Enterprise.java | 1 + .../src/com/structurizr/model/Model.java | 39 ++++++++++++-- .../structurizr/view/SystemContextView.java | 2 + .../structurizr/view/SystemLandscapeView.java | 5 +- .../com/structurizr/model/ComponentTests.java | 2 +- .../model/ContainerInstanceTests.java | 2 +- .../com/structurizr/model/ContainerTests.java | 2 +- .../com/structurizr/model/ModelTests.java | 28 +++++----- .../structurizr/model/RelationshipTests.java | 4 +- .../model/SoftwareSystemInstanceTests.java | 2 +- .../model/SoftwareSystemTests.java | 30 +++++------ .../structurizr/view/ComponentViewTests.java | 20 +++---- .../structurizr/view/ContainerViewTests.java | 22 ++++---- .../structurizr/view/ElementViewTests.java | 5 +- .../view/SystemContextViewTests.java | 18 +++---- .../view/SystemLandscapeViewTests.java | 26 ++-------- .../unit/com/structurizr/view/ViewTests.java | 52 +++++++++---------- 20 files changed, 157 insertions(+), 126 deletions(-) diff --git a/build.gradle b/build.gradle index 813ded0db..cf3b08436 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.22.3' + version = '1.23.0' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index ed55dbae3..6554a4f65 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.23.0 (unreleased) + +- Deprecates `Enterprise` and `Location` concepts. + ## 1.22.3 (11th March 2023) - Adds better backwards compatibility for removal of documentation sections. diff --git a/structurizr-client/test/integration/com/structurizr/api/BackwardsCompatibilityTests.java b/structurizr-client/test/integration/com/structurizr/api/BackwardsCompatibilityTests.java index 646f93a86..7f30cc635 100644 --- a/structurizr-client/test/integration/com/structurizr/api/BackwardsCompatibilityTests.java +++ b/structurizr-client/test/integration/com/structurizr/api/BackwardsCompatibilityTests.java @@ -22,6 +22,23 @@ void test() throws Exception { } } + @Test + void enterprise_and_location() throws Exception { + File file = new File(PATH_TO_WORKSPACE_FILES, "structurizr-36141-workspace.json"); + Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(file); + + assertEquals("Big Bank plc", workspace.getModel().getEnterprise().getName()); + assertEquals(Location.Internal, workspace.getModel().getPersonWithName("Back Office Staff").getLocation()); + assertEquals(Location.External, workspace.getModel().getPersonWithName("Personal Banking Customer").getLocation()); + + // make sure enterprise and location information is not lost when going to/from JSON + workspace = WorkspaceUtils.fromJson(WorkspaceUtils.toJson(workspace, false)); + + assertEquals("Big Bank plc", workspace.getModel().getEnterprise().getName()); + assertEquals(Location.Internal, workspace.getModel().getPersonWithName("Back Office Staff").getLocation()); + assertEquals(Location.External, workspace.getModel().getPersonWithName("Personal Banking Customer").getLocation()); + } + @Test void documentation() throws Exception { Workspace workspace = new Workspace("Name", "Description"); diff --git a/structurizr-core/src/com/structurizr/model/Enterprise.java b/structurizr-core/src/com/structurizr/model/Enterprise.java index a5a6c0257..5a4b58f2a 100644 --- a/structurizr-core/src/com/structurizr/model/Enterprise.java +++ b/structurizr-core/src/com/structurizr/model/Enterprise.java @@ -16,6 +16,7 @@ public final class Enterprise { * @param name the name, as a String * @throws IllegalArgumentException if the name is not specified */ + @Deprecated public Enterprise(String name) { if (name == null || name.trim().length() == 0) { throw new IllegalArgumentException("Name must be specified."); diff --git a/structurizr-core/src/com/structurizr/model/Model.java b/structurizr-core/src/com/structurizr/model/Model.java index b97c0d23c..3c81fdfd9 100644 --- a/structurizr-core/src/com/structurizr/model/Model.java +++ b/structurizr-core/src/com/structurizr/model/Model.java @@ -45,6 +45,7 @@ public Enterprise getEnterprise() { * * @param enterprise an Enterprise instance */ + @Deprecated public void setEnterprise(Enterprise enterprise) { this.enterprise = enterprise; } @@ -61,7 +62,7 @@ public SoftwareSystem addSoftwareSystem(@Nonnull String name) { } /** - * Creates a software system (with an unspecified location) and adds it to the model. + * Creates a software system and adds it to the model. * * @param name the name of the software system * @param description a short description of the software system @@ -69,7 +70,21 @@ public SoftwareSystem addSoftwareSystem(@Nonnull String name) { * @throws IllegalArgumentException if a software system with the same name already exists */ public SoftwareSystem addSoftwareSystem(@Nonnull String name, @Nullable String description) { - return addSoftwareSystem(Location.Unspecified, name, description); + if (getSoftwareSystemWithName(name) == null) { + SoftwareSystem softwareSystem = new SoftwareSystem(); + softwareSystem.setLocation(Location.Unspecified); + softwareSystem.setName(name); + softwareSystem.setDescription(description); + + softwareSystems.add(softwareSystem); + + softwareSystem.setId(idGenerator.generateId(softwareSystem)); + addElementToInternalStructures(softwareSystem); + + return softwareSystem; + } else { + throw new IllegalArgumentException("A top-level element named '" + name + "' already exists."); + } } /** @@ -82,6 +97,7 @@ public SoftwareSystem addSoftwareSystem(@Nonnull String name, @Nullable String d * @throws IllegalArgumentException if a software system with the same name already exists */ @Nonnull + @Deprecated public SoftwareSystem addSoftwareSystem(@Nullable Location location, @Nonnull String name, @Nullable String description) { if (getSoftwareSystemWithName(name) == null) { SoftwareSystem softwareSystem = new SoftwareSystem(); @@ -113,7 +129,7 @@ public Person addPerson(@Nonnull String name) { } /** - * Creates a person (with an unspecified location) and adds it to the model. + * Creates a person and adds it to the model. * * @param name the name of the person (e.g. "Admin User" or "Bob the Business User") * @param description a short description of the person @@ -122,7 +138,21 @@ public Person addPerson(@Nonnull String name) { */ @Nonnull public Person addPerson(@Nonnull String name, @Nullable String description) { - return addPerson(Location.Unspecified, name, description); + if (getPersonWithName(name) == null) { + Person person = new Person(); + person.setLocation(Location.Unspecified); + person.setName(name); + person.setDescription(description); + + people.add(person); + + person.setId(idGenerator.generateId(person)); + addElementToInternalStructures(person); + + return person; + } else { + throw new IllegalArgumentException("A top-level element named '" + name + "' already exists."); + } } /** @@ -135,6 +165,7 @@ public Person addPerson(@Nonnull String name, @Nullable String description) { * @throws IllegalArgumentException if a person with the same name already exists */ @Nonnull + @Deprecated public Person addPerson(Location location, @Nonnull String name, @Nullable String description) { if (getPersonWithName(name) == null) { Person person = new Person(); diff --git a/structurizr-core/src/com/structurizr/view/SystemContextView.java b/structurizr-core/src/com/structurizr/view/SystemContextView.java index 7d4ef11a2..a96595596 100644 --- a/structurizr-core/src/com/structurizr/view/SystemContextView.java +++ b/structurizr-core/src/com/structurizr/view/SystemContextView.java @@ -77,6 +77,7 @@ public void addNearestNeighbours(@Nonnull Element element) { * * @return true if the enterprise boundary is visible, false otherwise */ + @Deprecated public boolean isEnterpriseBoundaryVisible() { return enterpriseBoundaryVisible; } @@ -86,6 +87,7 @@ public boolean isEnterpriseBoundaryVisible() { * * @param enterpriseBoundaryVisible true if the enterprise boundary should be visible, false otherwise */ + @Deprecated public void setEnterpriseBoundaryVisible(boolean enterpriseBoundaryVisible) { this.enterpriseBoundaryVisible = enterpriseBoundaryVisible; } diff --git a/structurizr-core/src/com/structurizr/view/SystemLandscapeView.java b/structurizr-core/src/com/structurizr/view/SystemLandscapeView.java index 4cfbcd23d..d040911fe 100644 --- a/structurizr-core/src/com/structurizr/view/SystemLandscapeView.java +++ b/structurizr-core/src/com/structurizr/view/SystemLandscapeView.java @@ -31,8 +31,7 @@ public final class SystemLandscapeView extends StaticView { */ @Override public String getName() { - Enterprise enterprise = model.getEnterprise(); - return "System Landscape" + (enterprise != null && enterprise.getName().trim().length() > 0 ? " for " + enterprise.getName() : ""); + return "System Landscape"; } /** @@ -94,6 +93,7 @@ public void addNearestNeighbours(@Nonnull Element element) { * * @return true if the enterprise boundary is visible, false otherwise */ + @Deprecated public boolean isEnterpriseBoundaryVisible() { return enterpriseBoundaryVisible; } @@ -103,6 +103,7 @@ public boolean isEnterpriseBoundaryVisible() { * * @param enterpriseBoundaryVisible true if the enterprise boundary should be visible, false otherwise */ + @Deprecated public void setEnterpriseBoundaryVisible(boolean enterpriseBoundaryVisible) { this.enterpriseBoundaryVisible = enterpriseBoundaryVisible; } diff --git a/structurizr-core/test/unit/com/structurizr/model/ComponentTests.java b/structurizr-core/test/unit/com/structurizr/model/ComponentTests.java index fa19b0dee..d38adc7ba 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ComponentTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ComponentTests.java @@ -7,7 +7,7 @@ public class ComponentTests extends AbstractWorkspaceTestBase { - private SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.External, "System", "Description"); + private SoftwareSystem softwareSystem = model.addSoftwareSystem("System", "Description"); private Container container = softwareSystem.addContainer("Container", "Description", "Some technology"); @Test diff --git a/structurizr-core/test/unit/com/structurizr/model/ContainerInstanceTests.java b/structurizr-core/test/unit/com/structurizr/model/ContainerInstanceTests.java index 445a11f22..1f0b150b7 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ContainerInstanceTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ContainerInstanceTests.java @@ -7,7 +7,7 @@ public class ContainerInstanceTests extends AbstractWorkspaceTestBase { - private SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.External, "System", "Description"); + private SoftwareSystem softwareSystem = model.addSoftwareSystem("System", "Description"); private Container database = softwareSystem.addContainer("Database Schema", "Stores data", "MySQL"); private DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node", "Description", "Technology"); diff --git a/structurizr-core/test/unit/com/structurizr/model/ContainerTests.java b/structurizr-core/test/unit/com/structurizr/model/ContainerTests.java index 159f40805..f9599c2c1 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ContainerTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ContainerTests.java @@ -7,7 +7,7 @@ public class ContainerTests extends AbstractWorkspaceTestBase { - private SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.External, "System", "Description"); + private SoftwareSystem softwareSystem = model.addSoftwareSystem("System", "Description"); private Container container = softwareSystem.addContainer("Container", "Description", "Some technology"); @Test diff --git a/structurizr-core/test/unit/com/structurizr/model/ModelTests.java b/structurizr-core/test/unit/com/structurizr/model/ModelTests.java index 3407988e6..36454db16 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ModelTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ModelTests.java @@ -40,10 +40,9 @@ void addPerson_ThrowsAnException_WhenAnEmptyNameIsSpecified() { @Test void addSoftwareSystem_AddsTheSoftwareSystem_WhenASoftwareSystemDoesNotExistWithTheSameName() { assertTrue(model.getSoftwareSystems().isEmpty()); - SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.External, "System A", "Some description"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("System A", "Some description"); assertEquals(1, model.getSoftwareSystems().size()); - assertEquals(Location.External, softwareSystem.getLocation()); assertEquals("System A", softwareSystem.getName()); assertEquals("Some description", softwareSystem.getDescription()); assertEquals("1", softwareSystem.getId()); @@ -52,11 +51,11 @@ void addSoftwareSystem_AddsTheSoftwareSystem_WhenASoftwareSystemDoesNotExistWith @Test void addSoftwareSystem_ThrowsAnException_WhenASoftwareSystemExistsWithTheSameName() { - SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.External, "System A", "Some description"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("System A", "Some description"); assertEquals(1, model.getSoftwareSystems().size()); try { - model.addSoftwareSystem(Location.External, "System A", "Description"); + model.addSoftwareSystem("System A", "Description"); fail(); } catch (Exception e) { assertEquals("A top-level element named 'System A' already exists.", e.getMessage()); @@ -69,7 +68,6 @@ void addSoftwareSystemWithoutSpecifyingLocation_AddsTheSoftwareSystem_WhenASoftw SoftwareSystem softwareSystem = model.addSoftwareSystem("System A", "Some description"); assertEquals(1, model.getSoftwareSystems().size()); - assertEquals(Location.Unspecified, softwareSystem.getLocation()); assertEquals("System A", softwareSystem.getName()); assertEquals("Some description", softwareSystem.getDescription()); assertEquals("1", softwareSystem.getId()); @@ -79,10 +77,9 @@ void addSoftwareSystemWithoutSpecifyingLocation_AddsTheSoftwareSystem_WhenASoftw @Test void addPerson_AddsThePerson_WhenAPersonDoesNotExistWithTheSameName() { assertTrue(model.getPeople().isEmpty()); - Person person = model.addPerson(Location.Internal, "Some internal user", "Some description"); + Person person = model.addPerson("Some internal user", "Some description"); assertEquals(1, model.getPeople().size()); - assertEquals(Location.Internal, person.getLocation()); assertEquals("Some internal user", person.getName()); assertEquals("Some description", person.getDescription()); assertEquals("1", person.getId()); @@ -91,11 +88,11 @@ void addPerson_AddsThePerson_WhenAPersonDoesNotExistWithTheSameName() { @Test void addPerson_ThrowsAnException_WhenAPersonExistsWithTheSameName() { - Person person = model.addPerson(Location.Internal, "Admin User", "Description"); + Person person = model.addPerson("Admin User", "Description"); assertEquals(1, model.getPeople().size()); try { - model.addPerson(Location.External, "Admin User", "Description"); + model.addPerson("Admin User", "Description"); fail(); } catch (Exception e) { assertEquals("A top-level element named 'Admin User' already exists.", e.getMessage()); @@ -108,7 +105,6 @@ void addPerson_AddsThePersonWithoutSpecifyingTheLocation_WhenAPersonDoesNotExist Person person = model.addPerson("Some internal user", "Some description"); assertEquals(1, model.getPeople().size()); - assertEquals(Location.Unspecified, person.getLocation()); assertEquals("Some internal user", person.getName()); assertEquals("Some description", person.getDescription()); assertEquals("1", person.getId()); @@ -122,20 +118,20 @@ void getElement_ReturnsNull_WhenAnElementWithTheSpecifiedIdDoesNotExist() { @Test void getElement_ReturnsAnElement_WhenAnElementWithTheSpecifiedIdDoesExist() { - Person person = model.addPerson(Location.Internal, "Name", "Description"); + Person person = model.addPerson("Name", "Description"); assertSame(person, model.getElement(person.getId())); } @Test void contains_ReturnsFalse_WhenTheSpecifiedElementIsNotInTheModel() { Model newModel = new Model(); - SoftwareSystem softwareSystem = newModel.addSoftwareSystem(Location.Unspecified, "Name", "Description"); + SoftwareSystem softwareSystem = newModel.addSoftwareSystem("Name", "Description"); assertFalse(model.contains(softwareSystem)); } @Test void contains_ReturnsTrue_WhenTheSpecifiedElementIsInTheModel() { - SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Unspecified, "Name", "Description"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); assertTrue(model.contains(softwareSystem)); } @@ -146,7 +142,7 @@ void getSoftwareSystemWithName_ReturnsNull_WhenASoftwareSystemWithTheSpecifiedNa @Test void getSoftwareSystemWithName_ReturnsASoftwareSystem_WhenASoftwareSystemWithTheSpecifiedNameExists() { - SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.External, "System A", "Description"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("System A", "Description"); assertSame(softwareSystem, model.getSoftwareSystemWithName("System A")); } @@ -177,7 +173,7 @@ void getSoftwareSystemWithId_ReturnsNull_WhenASoftwareSystemWithTheSpecifiedIdDo @Test void getSoftwareSystemWithId_ReturnsASoftwareSystem_WhenASoftwareSystemWithTheSpecifiedIdDoesExist() { - SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.External, "System A", "Description"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("System A", "Description"); assertSame(softwareSystem, model.getSoftwareSystemWithId(softwareSystem.getId())); } @@ -188,7 +184,7 @@ void getPersonWithName_ReturnsNull_WhenAPersonWithTheSpecifiedNameDoesNotExist() @Test void getPersonWithName_ReturnsAPerson_WhenAPersonWithTheSpecifiedNameExists() { - Person person = model.addPerson(Location.External, "Admin User", "Description"); + Person person = model.addPerson("Admin User", "Description"); assertSame(person, model.getPersonWithName("Admin User")); } diff --git a/structurizr-core/test/unit/com/structurizr/model/RelationshipTests.java b/structurizr-core/test/unit/com/structurizr/model/RelationshipTests.java index f596d09fb..be7c631dd 100644 --- a/structurizr-core/test/unit/com/structurizr/model/RelationshipTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/RelationshipTests.java @@ -12,8 +12,8 @@ public class RelationshipTests extends AbstractWorkspaceTestBase { @BeforeEach public void setUp() { - softwareSystem1 = model.addSoftwareSystem(Location.Internal, "Name1", "Description"); - softwareSystem2 = model.addSoftwareSystem(Location.Internal, "Name2", "Description"); + softwareSystem1 = model.addSoftwareSystem("Name1", "Description"); + softwareSystem2 = model.addSoftwareSystem("Name2", "Description"); } @Test diff --git a/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemInstanceTests.java b/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemInstanceTests.java index e7019e231..6795bee01 100644 --- a/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemInstanceTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemInstanceTests.java @@ -7,7 +7,7 @@ public class SoftwareSystemInstanceTests extends AbstractWorkspaceTestBase { - private SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.External, "System", "Description"); + private SoftwareSystem softwareSystem = model.addSoftwareSystem("System", "Description"); private DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node", "Description", "Technology"); @Test diff --git a/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemTests.java b/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemTests.java index 03f0e9c2d..51b97f3e7 100644 --- a/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemTests.java @@ -9,7 +9,7 @@ public class SoftwareSystemTests extends AbstractWorkspaceTestBase { - private SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "Name", "Description"); + private SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); @Test void addContainer_ThrowsAnException_WhenANullNameIsSpecified() { @@ -73,8 +73,8 @@ void GetContainerWithId_ReturnsAContainer_WhenAContainerWithTheSpecifiedIdDoesEx @Test void uses_AddsAUnidirectionalRelationshipBetweenTwoSoftwareSystems() { - SoftwareSystem systemA = model.addSoftwareSystem(Location.Internal, "System A", "Description"); - SoftwareSystem systemB = model.addSoftwareSystem(Location.Internal, "System B", "Description"); + SoftwareSystem systemA = model.addSoftwareSystem("System A", "Description"); + SoftwareSystem systemB = model.addSoftwareSystem("System B", "Description"); systemA.uses(systemB, "Gets some data from"); assertEquals(1, systemA.getRelationships().size()); @@ -87,8 +87,8 @@ void uses_AddsAUnidirectionalRelationshipBetweenTwoSoftwareSystems() { @Test void uses_AddsAUnidirectionalRelationshipBetweenTwoSoftwareSystems_WhenADifferentRelationshipAlreadyExists() { - SoftwareSystem systemA = model.addSoftwareSystem(Location.Internal, "System A", "Description"); - SoftwareSystem systemB = model.addSoftwareSystem(Location.Internal, "System B", "Description"); + SoftwareSystem systemA = model.addSoftwareSystem("System A", "Description"); + SoftwareSystem systemB = model.addSoftwareSystem("System B", "Description"); systemA.uses(systemB, "Gets data using the REST API"); systemA.uses(systemB, "Subscribes to updates using the Streaming API"); @@ -107,8 +107,8 @@ void uses_AddsAUnidirectionalRelationshipBetweenTwoSoftwareSystems_WhenADifferen @Test void uses_DoesNotAddAUnidirectionalRelationshipBetweenTwoSoftwareSystems_WhenTheSameRelationshipAlreadyExists() { - SoftwareSystem systemA = model.addSoftwareSystem(Location.Internal, "System A", "Description"); - SoftwareSystem systemB = model.addSoftwareSystem(Location.Internal, "System B", "Description"); + SoftwareSystem systemA = model.addSoftwareSystem("System A", "Description"); + SoftwareSystem systemB = model.addSoftwareSystem("System B", "Description"); systemA.uses(systemB, "Gets data using the REST API"); systemA.uses(systemB, "Gets data using the REST API"); @@ -117,8 +117,8 @@ void uses_DoesNotAddAUnidirectionalRelationshipBetweenTwoSoftwareSystems_WhenThe @Test void delivers_AddsAUnidirectionalRelationshipBetweenASoftwareSystemAndAPerson() { - SoftwareSystem system = model.addSoftwareSystem(Location.Internal, "System", "Description"); - Person person = model.addPerson(Location.Internal, "User", "Description"); + SoftwareSystem system = model.addSoftwareSystem("System", "Description"); + Person person = model.addPerson("User", "Description"); system.delivers(person, "E-mails results to"); assertEquals(1, system.getRelationships().size()); @@ -131,8 +131,8 @@ void delivers_AddsAUnidirectionalRelationshipBetweenASoftwareSystemAndAPerson() @Test void delivers_AddsAUnidirectionalRelationshipBetweenASoftwareSystemAndAPerson_WhenADifferentRelationshipAlreadyExists() { - SoftwareSystem system = model.addSoftwareSystem(Location.Internal, "System", "Description"); - Person person = model.addPerson(Location.Internal, "User", "Description"); + SoftwareSystem system = model.addSoftwareSystem("System", "Description"); + Person person = model.addPerson("User", "Description"); system.delivers(person, "E-mails results to"); system.delivers(person, "Text messages results to"); @@ -152,8 +152,8 @@ void delivers_AddsAUnidirectionalRelationshipBetweenASoftwareSystemAndAPerson_Wh @Test void delivers_DoesNotAddAUnidirectionalRelationshipBetweenASoftwareSystemAndAPerson_WhenTheSameRelationshipAlreadyExists() { - SoftwareSystem system = model.addSoftwareSystem(Location.Internal, "System", "Description"); - Person person = model.addPerson(Location.Internal, "User", "Description"); + SoftwareSystem system = model.addSoftwareSystem("System", "Description"); + Person person = model.addPerson("User", "Description"); system.delivers(person, "E-mails results to"); system.delivers(person, "E-mails results to"); @@ -162,13 +162,13 @@ void delivers_DoesNotAddAUnidirectionalRelationshipBetweenASoftwareSystemAndAPer @Test void getTags_IncludesSoftwareSystemByDefault() { - SoftwareSystem system = model.addSoftwareSystem(Location.Internal, "System", "Description"); + SoftwareSystem system = model.addSoftwareSystem("System", "Description"); assertEquals("Element,Software System", system.getTags()); } @Test void getCanonicalName() { - SoftwareSystem system = model.addSoftwareSystem(Location.Internal, "System", "Description"); + SoftwareSystem system = model.addSoftwareSystem("System", "Description"); assertEquals("SoftwareSystem://System", system.getCanonicalName()); } diff --git a/structurizr-core/test/unit/com/structurizr/view/ComponentViewTests.java b/structurizr-core/test/unit/com/structurizr/view/ComponentViewTests.java index ddf7daf9f..9c80529d7 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ComponentViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ComponentViewTests.java @@ -18,7 +18,7 @@ public class ComponentViewTests extends AbstractWorkspaceTestBase { @BeforeEach public void setUp() { - softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); + softwareSystem = model.addSoftwareSystem("The System", "Description"); webApplication = softwareSystem.addContainer("Web Application", "Does something", "Apache Tomcat"); view = new ComponentView(webApplication, "Key", "Some description"); } @@ -43,8 +43,8 @@ void addAllSoftwareSystems_DoesNothing_WhenThereAreNoOtherSoftwareSystems() { @Test void addAllSoftwareSystems_AddsAllSoftwareSystems_WhenThereAreSomeSoftwareSystemsInTheModel() { - SoftwareSystem softwareSystemA = model.addSoftwareSystem(Location.External, "System A", "Description"); - SoftwareSystem softwareSystemB = model.addSoftwareSystem(Location.External, "System B", "Description"); + SoftwareSystem softwareSystemA = model.addSoftwareSystem("System A", "Description"); + SoftwareSystem softwareSystemB = model.addSoftwareSystem("System B", "Description"); view.addAllSoftwareSystems(); @@ -62,8 +62,8 @@ void addAllPeople_DoesNothing_WhenThereAreNoPeople() { @Test void addAllPeople_AddsAllPeople_WhenThereAreSomePeopleInTheModel() { - Person userA = model.addPerson(Location.External, "User A", "Description"); - Person userB = model.addPerson(Location.External, "User B", "Description"); + Person userA = model.addPerson("User A", "Description"); + Person userB = model.addPerson("User B", "Description"); view.addAllPeople(); @@ -81,10 +81,10 @@ void addAllElements_DoesNothing_WhenThereAreNoSoftwareSystemsOrPeople() { @Test void addAllElements_AddsAllSoftwareSystemsAndPeopleAndContainersAndComponents_WhenThereAreSomeSoftwareSystemsAndPeopleAndContainersAndComponentsInTheModel() { - SoftwareSystem softwareSystemA = model.addSoftwareSystem(Location.External, "System A", "Description"); - SoftwareSystem softwareSystemB = model.addSoftwareSystem(Location.External, "System B", "Description"); - Person userA = model.addPerson(Location.External, "User A", "Description"); - Person userB = model.addPerson(Location.External, "User B", "Description"); + SoftwareSystem softwareSystemA = model.addSoftwareSystem("System A", "Description"); + SoftwareSystem softwareSystemB = model.addSoftwareSystem("System B", "Description"); + Person userA = model.addPerson("User A", "Description"); + Person userB = model.addPerson("User B", "Description"); Container database = softwareSystem.addContainer("Database", "Does something", "MySQL"); Component componentA = webApplication.addComponent("Component A", "Does something", "Java"); Component componentB = webApplication.addComponent("Component B", "Does something", "Java"); @@ -264,7 +264,7 @@ void add_ThrowsAnException_WhenTheContainerOfTheViewIsAdded() { @Test void add_DoesNothing_WhenTheContainerOfTheViewIsAddedViaDependency() { - final SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.External, "Some other system", "external system that uses our web application"); + final SoftwareSystem softwareSystem = model.addSoftwareSystem("Some other system", "external system that uses our web application"); final Relationship relationshipFromExternalSystem = softwareSystem.uses(webApplication, ""); diff --git a/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java b/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java index fb97520f8..356cbd0dc 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java @@ -14,7 +14,7 @@ public class ContainerViewTests extends AbstractWorkspaceTestBase { @BeforeEach public void setUp() { - softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); + softwareSystem = model.addSoftwareSystem("The System", "Description"); view = new ContainerView(softwareSystem, "containers", "Description"); } @@ -37,8 +37,8 @@ void addAllSoftwareSystems_DoesNothing_WhenThereAreNoOtherSoftwareSystems() { @Test void addAllSoftwareSystems_AddsAllSoftwareSystems_WhenThereAreSomeSoftwareSystemsInTheModel() { - SoftwareSystem softwareSystemA = model.addSoftwareSystem(Location.External, "System A", "Description"); - SoftwareSystem softwareSystemB = model.addSoftwareSystem(Location.External, "System B", "Description"); + SoftwareSystem softwareSystemA = model.addSoftwareSystem("System A", "Description"); + SoftwareSystem softwareSystemB = model.addSoftwareSystem("System B", "Description"); view.addAllSoftwareSystems(); @@ -56,8 +56,8 @@ void addAllPeople_DoesNothing_WhenThereAreNoPeople() { @Test void addAllPeople_AddsAllPeople_WhenThereAreSomePeopleInTheModel() { - Person userA = model.addPerson(Location.External, "User A", "Description"); - Person userB = model.addPerson(Location.External, "User B", "Description"); + Person userA = model.addPerson("User A", "Description"); + Person userB = model.addPerson("User B", "Description"); view.addAllPeople(); @@ -75,10 +75,10 @@ void addAllElements_DoesNothing_WhenThereAreNoSoftwareSystemsOrPeople() { @Test void addAllElements_AddsAllSoftwareSystemsAndPeopleAndContainers_WhenThereAreSomeSoftwareSystemsAndPeopleAndContainersInTheModel() { - SoftwareSystem softwareSystemA = model.addSoftwareSystem(Location.External, "System A", "Description"); - SoftwareSystem softwareSystemB = model.addSoftwareSystem(Location.External, "System B", "Description"); - Person userA = model.addPerson(Location.External, "User A", "Description"); - Person userB = model.addPerson(Location.External, "User B", "Description"); + SoftwareSystem softwareSystemA = model.addSoftwareSystem("System A", "Description"); + SoftwareSystem softwareSystemB = model.addSoftwareSystem("System B", "Description"); + Person userA = model.addPerson("User A", "Description"); + Person userB = model.addPerson("User B", "Description"); Container webApplication = softwareSystem.addContainer("Web Application", "Does something", "Apache Tomcat"); Container database = softwareSystem.addContainer("Database", "Does something", "MySQL"); @@ -236,7 +236,7 @@ void addDependentSoftwareSystem() { view.addDependentSoftwareSystems(); - SoftwareSystem softwareSystem2 = model.addSoftwareSystem(Location.External, "SoftwareSystem 2", ""); + SoftwareSystem softwareSystem2 = model.addSoftwareSystem("SoftwareSystem 2", ""); view.addDependentSoftwareSystems(); assertEquals(0, view.getElements().size()); @@ -252,7 +252,7 @@ void addDependentSoftwareSystem() { void addDependentSoftwareSystem2() { Container container1a = softwareSystem.addContainer("Container 1A", "", ""); - SoftwareSystem softwareSystem2 = model.addSoftwareSystem(Location.External, "SoftwareSystem 2", ""); + SoftwareSystem softwareSystem2 = model.addSoftwareSystem("SoftwareSystem 2", ""); Container container2a = softwareSystem2.addContainer("Container 2-A", "", ""); model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); diff --git a/structurizr-core/test/unit/com/structurizr/view/ElementViewTests.java b/structurizr-core/test/unit/com/structurizr/view/ElementViewTests.java index 497131c33..661308e4e 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ElementViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ElementViewTests.java @@ -2,7 +2,6 @@ import com.structurizr.AbstractWorkspaceTestBase; import com.structurizr.model.Element; -import com.structurizr.model.Location; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -11,14 +10,14 @@ public class ElementViewTests extends AbstractWorkspaceTestBase { @Test void copyLayoutInformationFrom_DoesNothing_WhenNullIsPassed() { - Element element = model.addSoftwareSystem(Location.External, "SystemA", ""); + Element element = model.addSoftwareSystem("SystemA", ""); ElementView elementView = new ElementView(element); elementView.copyLayoutInformationFrom(null); } @Test void copyLayoutInformationFrom_CopiesXAndY_WhenANonNullElementViewIsPassed() { - Element element = model.addSoftwareSystem(Location.External, "SystemA", ""); + Element element = model.addSoftwareSystem("SystemA", ""); ElementView elementView1 = new ElementView(element); assertEquals(0, elementView1.getX()); assertEquals(0, elementView1.getY()); diff --git a/structurizr-core/test/unit/com/structurizr/view/SystemContextViewTests.java b/structurizr-core/test/unit/com/structurizr/view/SystemContextViewTests.java index 2c68a8e72..9de221b2f 100644 --- a/structurizr-core/test/unit/com/structurizr/view/SystemContextViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/SystemContextViewTests.java @@ -14,7 +14,7 @@ public class SystemContextViewTests extends AbstractWorkspaceTestBase { @BeforeEach public void setUp() { - softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); + softwareSystem = model.addSoftwareSystem( "The System", "Description"); view = new SystemContextView(softwareSystem, "context", "Description"); } @@ -37,8 +37,8 @@ void addAllSoftwareSystems_DoesNothing_WhenThereAreNoOtherSoftwareSystems() { @Test void addAllSoftwareSystems_AddsAllSoftwareSystems_WhenThereAreSomeSoftwareSystemsInTheModel() { - SoftwareSystem softwareSystemA = model.addSoftwareSystem(Location.External, "System A", "Description"); - SoftwareSystem softwareSystemB = model.addSoftwareSystem(Location.External, "System B", "Description"); + SoftwareSystem softwareSystemA = model.addSoftwareSystem("System A", "Description"); + SoftwareSystem softwareSystemB = model.addSoftwareSystem("System B", "Description"); view.addAllSoftwareSystems(); @@ -57,8 +57,8 @@ void addAllPeople_DoesNothing_WhenThereAreNoPeople() { @Test void addAllPeople_AddsAllPeople_WhenThereAreSomePeopleInTheModel() { - Person userA = model.addPerson(Location.External, "User A", "Description"); - Person userB = model.addPerson(Location.External, "User B", "Description"); + Person userA = model.addPerson( "User A", "Description"); + Person userB = model.addPerson( "User B", "Description"); view.addAllPeople(); @@ -77,10 +77,10 @@ void addAllElements_DoesNothing_WhenThereAreNoSoftwareSystemsOrPeople() { @Test void addAllElements_AddsAllSoftwareSystemsAndPeople_WhenThereAreSomeSoftwareSystemsAndPeopleInTheModel() { - SoftwareSystem softwareSystemA = model.addSoftwareSystem(Location.External, "System A", "Description"); - SoftwareSystem softwareSystemB = model.addSoftwareSystem(Location.External, "System B", "Description"); - Person userA = model.addPerson(Location.External, "User A", "Description"); - Person userB = model.addPerson(Location.External, "User B", "Description"); + SoftwareSystem softwareSystemA = model.addSoftwareSystem("System A", "Description"); + SoftwareSystem softwareSystemB = model.addSoftwareSystem("System B", "Description"); + Person userA = model.addPerson( "User A", "Description"); + Person userB = model.addPerson( "User B", "Description"); view.addAllElements(); diff --git a/structurizr-core/test/unit/com/structurizr/view/SystemLandscapeViewTests.java b/structurizr-core/test/unit/com/structurizr/view/SystemLandscapeViewTests.java index 69b922957..dade9b288 100644 --- a/structurizr-core/test/unit/com/structurizr/view/SystemLandscapeViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/SystemLandscapeViewTests.java @@ -24,30 +24,10 @@ void construction() { } @Test - void getName_WhenNoEnterpriseIsSpecified() { + void getName() { assertEquals("System Landscape", view.getName()); } - @Test - void getName_WhenAnEnterpriseIsSpecified() { - model.setEnterprise(new Enterprise("Widgets Limited")); - assertEquals("System Landscape for Widgets Limited", view.getName()); - } - - @Test - void getName_WhenAnEmptyEnterpriseNameIsSpecified() { - assertThrows(IllegalArgumentException.class, () -> { - model.setEnterprise(new Enterprise("")); - }); - } - - @Test - void getName_WhenANullEnterpriseNameIsSpecified() { - assertThrows(IllegalArgumentException.class, () -> { - model.setEnterprise(new Enterprise(null)); - }); - } - @Test void addAllSoftwareSystems_DoesNothing_WhenThereAreNoOtherSoftwareSystems() { view.addAllSoftwareSystems(); @@ -56,8 +36,8 @@ void addAllSoftwareSystems_DoesNothing_WhenThereAreNoOtherSoftwareSystems() { @Test void addAllSoftwareSystems_AddsAllSoftwareSystems_WhenThereAreSomeSoftwareSystemsInTheModel() { - SoftwareSystem softwareSystemA = model.addSoftwareSystem(Location.External, "System A", "Description"); - SoftwareSystem softwareSystemB = model.addSoftwareSystem(Location.External, "System B", "Description"); + SoftwareSystem softwareSystemA = model.addSoftwareSystem("System A", "Description"); + SoftwareSystem softwareSystemB = model.addSoftwareSystem("System B", "Description"); view.addAllSoftwareSystems(); diff --git a/structurizr-core/test/unit/com/structurizr/view/ViewTests.java b/structurizr-core/test/unit/com/structurizr/view/ViewTests.java index 8017e46db..b4cb96bcd 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ViewTests.java @@ -14,7 +14,7 @@ public class ViewTests extends AbstractWorkspaceTestBase { @Test void construction() { - SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("The System", "Description"); StaticView view = new SystemContextView(softwareSystem, "key", "Description"); assertEquals("key", view.getKey()); assertEquals("Description", view.getDescription()); @@ -30,7 +30,7 @@ void construction_WhenTheViewKeyContainsAForwardSlashCharacter() { @Test void addAllSoftwareSystems_DoesNothing_WhenThereAreNoOtherSoftwareSystemsInTheModel() { - SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("The System", "Description"); StaticView view = new SystemContextView(softwareSystem, "context", "Description"); assertEquals(1, view.getElements().size()); view.addAllSoftwareSystems(); @@ -39,10 +39,10 @@ void addAllSoftwareSystems_DoesNothing_WhenThereAreNoOtherSoftwareSystemsInTheMo @Test void addAllSoftwareSystems_DoesAddAllSoftwareSystems_WhenThereAreSoftwareSystemsInTheModel() { - SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); - SoftwareSystem softwareSystemA = model.addSoftwareSystem(Location.Unspecified, "System A", "Description"); - SoftwareSystem softwareSystemB = model.addSoftwareSystem(Location.Unspecified, "System B", "Description"); - SoftwareSystem softwareSystemC = model.addSoftwareSystem(Location.Unspecified, "System C", "Description"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("The System", "Description"); + SoftwareSystem softwareSystemA = model.addSoftwareSystem("System A", "Description"); + SoftwareSystem softwareSystemB = model.addSoftwareSystem("System B", "Description"); + SoftwareSystem softwareSystemC = model.addSoftwareSystem("System C", "Description"); StaticView view = new SystemContextView(softwareSystem, "context", "Description"); view.addAllSoftwareSystems(); @@ -57,7 +57,7 @@ void addAllSoftwareSystems_DoesAddAllSoftwareSystems_WhenThereAreSoftwareSystems @Test void addSoftwareSystem_ThrowsAnException_WhenGivenNull() { - SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("The System", "Description"); StaticView view = new SystemContextView(softwareSystem, "context", "Description"); try { @@ -70,8 +70,8 @@ void addSoftwareSystem_ThrowsAnException_WhenGivenNull() { @Test void addSoftwareSystem_AddsTheSoftwareSystem_WhenTheSoftwareSystemIsInTheModel() { - SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); - SoftwareSystem softwareSystemA = model.addSoftwareSystem(Location.Unspecified, "System A", "Description"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("The System", "Description"); + SoftwareSystem softwareSystemA = model.addSoftwareSystem("System A", "Description"); StaticView view = new SystemContextView(softwareSystem, "context", "Description"); view.add(softwareSystemA); @@ -83,7 +83,7 @@ void addSoftwareSystem_AddsTheSoftwareSystem_WhenTheSoftwareSystemIsInTheModel() @Test void addAllPeople_DoesNothing_WhenThereAreNoPeopleInTheModel() { - SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("The System", "Description"); StaticView view = new SystemContextView(softwareSystem, "context", "Description"); assertEquals(1, view.getElements().size()); @@ -94,10 +94,10 @@ void addAllPeople_DoesNothing_WhenThereAreNoPeopleInTheModel() { @Test void addAllPeople_DoesAddAllPeople_WhenThereArePeopleInTheModel() { - SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); - Person person1 = model.addPerson(Location.Unspecified, "Person 1", "Description"); - Person person2 = model.addPerson(Location.Unspecified, "Person 2", "Description"); - Person person3 = model.addPerson(Location.Unspecified, "Person 3", "Description"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("The System", "Description"); + Person person1 = model.addPerson("Person 1", "Description"); + Person person2 = model.addPerson("Person 2", "Description"); + Person person3 = model.addPerson("Person 3", "Description"); StaticView view = new SystemContextView(softwareSystem, "context", "Description"); view.addAllPeople(); @@ -112,7 +112,7 @@ void addAllPeople_DoesAddAllPeople_WhenThereArePeopleInTheModel() { @Test void addPerson_ThrowsAnException_WhenGivenNull() { - SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("The System", "Description"); StaticView view = new SystemContextView(softwareSystem, "context", "Description"); try { view.add((Person) null); @@ -124,10 +124,10 @@ void addPerson_ThrowsAnException_WhenGivenNull() { @Test void addPerson_AddsThePerson_WhenThPersonIsInTheModel() { - SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("The System", "Description"); StaticView view = new SystemContextView(softwareSystem, "context", "Description"); - Person person1 = model.addPerson(Location.Unspecified, "Person 1", "Description"); + Person person1 = model.addPerson("Person 1", "Description"); view.add(person1); assertEquals(2, view.getElements().size()); @@ -138,8 +138,8 @@ void addPerson_AddsThePerson_WhenThPersonIsInTheModel() { @Test void removeElementsWithNoRelationships_RemovesAllElements_WhenTheViewHasNoRelationshipsBetweenElements() { - SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "Software System", "Description"); - Person person = model.addPerson(Location.Unspecified, "Person", "Description"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + Person person = model.addPerson("Person", "Description"); StaticView view = views.createSystemLandscapeView("context", "Description"); view.addAllSoftwareSystems(); @@ -151,11 +151,11 @@ void removeElementsWithNoRelationships_RemovesAllElements_WhenTheViewHasNoRelati @Test void removeElementsWithNoRelationships_RemovesOnlyThoseElementsWithoutRelationships_WhenTheViewContainsSomeUnlinkedElements() { - SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); - SoftwareSystem softwareSystemA = model.addSoftwareSystem(Location.Unspecified, "System A", "Description"); - SoftwareSystem softwareSystemB = model.addSoftwareSystem(Location.Unspecified, "System B", "Description"); - Person person1 = model.addPerson(Location.Unspecified, "Person 1", "Description"); - Person person2 = model.addPerson(Location.Unspecified, "Person 2", "Description"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("The System", "Description"); + SoftwareSystem softwareSystemA = model.addSoftwareSystem("System A", "Description"); + SoftwareSystem softwareSystemB = model.addSoftwareSystem("System B", "Description"); + Person person1 = model.addPerson("Person 1", "Description"); + Person person2 = model.addPerson("Person 2", "Description"); StaticView view = new SystemContextView(softwareSystem, "context", "Description"); softwareSystem.uses(softwareSystemA, "uses"); @@ -257,14 +257,14 @@ void copyLayoutInformationFrom() { @Test void getName() { - SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("The System", "Description"); SystemContextView systemContextView = new SystemContextView(softwareSystem, "context", "Description"); assertEquals("The System - System Context", systemContextView.getName()); } @Test void removeElementsThatAreUnreachableFrom_DoesNothing_WhenANullElementIsSpecified() { - SoftwareSystem softwareSystem = model.addSoftwareSystem(Location.Internal, "The System", "Description"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("The System", "Description"); StaticView view = new SystemContextView(softwareSystem, "context", "Description"); view.removeElementsThatAreUnreachableFrom(null); } From 4c001b40c1b6f60792ff9f45911f20c426f5f175 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 11 Mar 2023 08:24:58 +0000 Subject: [PATCH 388/717] Deprecates Enterprise concept. --- structurizr-core/src/com/structurizr/view/Terminology.java | 1 + 1 file changed, 1 insertion(+) diff --git a/structurizr-core/src/com/structurizr/view/Terminology.java b/structurizr-core/src/com/structurizr/view/Terminology.java index 6233ff6c6..f9c0fbc2b 100644 --- a/structurizr-core/src/com/structurizr/view/Terminology.java +++ b/structurizr-core/src/com/structurizr/view/Terminology.java @@ -40,6 +40,7 @@ public String getEnterprise() { return enterprise; } + @Deprecated public void setEnterprise(String enterprise) { this.enterprise = enterprise; } From 1074d96c8e2ed758a52c187f9bdc5e7757d845c5 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 11 Mar 2023 12:25:13 +0000 Subject: [PATCH 389/717] Adds properties to the model. --- docs/changelog.md | 1 + .../src/com/structurizr/model/Model.java | 38 ++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 6554a4f65..a8805bf47 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -3,6 +3,7 @@ ## 1.23.0 (unreleased) - Deprecates `Enterprise` and `Location` concepts. +- Adds properties to the model. ## 1.22.3 (11th March 2023) diff --git a/structurizr-core/src/com/structurizr/model/Model.java b/structurizr-core/src/com/structurizr/model/Model.java index 3c81fdfd9..04479e9a9 100644 --- a/structurizr-core/src/com/structurizr/model/Model.java +++ b/structurizr-core/src/com/structurizr/model/Model.java @@ -1,6 +1,7 @@ package com.structurizr.model; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.structurizr.PropertyHolder; import com.structurizr.WorkspaceValidationException; import javax.annotation.Nonnull; @@ -11,7 +12,7 @@ /** * Represents a software architecture model, into which all model elements are added. */ -public final class Model { +public final class Model implements PropertyHolder { private IdGenerator idGenerator = new SequentialIntegerIdGeneratorStrategy(); @@ -27,6 +28,8 @@ public final class Model { private ImpliedRelationshipsStrategy impliedRelationshipsStrategy = new DefaultImpliedRelationshipsStrategy(); + private Map properties = new HashMap<>(); + Model() { } @@ -1065,4 +1068,37 @@ public void setImpliedRelationshipsStrategy(ImpliedRelationshipsStrategy implied } } + /** + * Gets the collection of name-value property pairs, as a Map. + * + * @return a Map (String, String) (empty if there are no properties) + */ + public Map getProperties() { + return new HashMap<>(properties); + } + + /** + * Adds a name-value pair property. + * + * @param name the name of the property + * @param value the value of the property + */ + public void addProperty(String name, String value) { + if (name == null || name.trim().length() == 0) { + throw new IllegalArgumentException("A property name must be specified."); + } + + if (value == null || value.trim().length() == 0) { + throw new IllegalArgumentException("A property value must be specified."); + } + + properties.put(name, value); + } + + void setProperties(Map properties) { + if (properties != null) { + this.properties = new HashMap<>(properties); + } + } + } \ No newline at end of file From df577430fd372d653b417fae3a68dc2788a94e2b Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 11 Mar 2023 16:33:11 +0000 Subject: [PATCH 390/717] Updated to reflect release. --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index a8805bf47..6d2c79c40 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 1.23.0 (unreleased) +## 1.23.0 (11th March 2023) - Deprecates `Enterprise` and `Location` concepts. - Adds properties to the model. From f069caac1c142738a2a409df9fa494c252634921 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 12 Mar 2023 09:20:57 +0000 Subject: [PATCH 391/717] Adds constant. --- structurizr-core/src/com/structurizr/model/Constants.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 structurizr-core/src/com/structurizr/model/Constants.java diff --git a/structurizr-core/src/com/structurizr/model/Constants.java b/structurizr-core/src/com/structurizr/model/Constants.java new file mode 100644 index 000000000..54a2aa8b2 --- /dev/null +++ b/structurizr-core/src/com/structurizr/model/Constants.java @@ -0,0 +1,7 @@ +package com.structurizr.model; + +public final class Constants { + + public static final String GROUP_SEPARATOR_PROPERTY_NAME = "structurizr.groupSeparator"; + +} \ No newline at end of file From 47c4a9fffecfb414b95c0edb7b58b95e7c00b391 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 12 Mar 2023 14:30:26 +0000 Subject: [PATCH 392/717] Deprecates the `setExternalBoundariesVisible` methods on `ContainerView`, `ComponentView`, and `DynamicView`. --- build.gradle | 2 +- docs/changelog.md | 4 ++++ structurizr-core/src/com/structurizr/view/ComponentView.java | 1 + structurizr-core/src/com/structurizr/view/ContainerView.java | 1 + structurizr-core/src/com/structurizr/view/DynamicView.java | 1 + 5 files changed, 8 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index cf3b08436..2745db2e8 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.23.0' + version = '1.23.1' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 6d2c79c40..ef52c066f 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.23.1 (unreleased) + +- Deprecates the `setExternalBoundariesVisible` methods on `ContainerView`, `ComponentView`, and `DynamicView`. + ## 1.23.0 (11th March 2023) - Deprecates `Enterprise` and `Location` concepts. diff --git a/structurizr-core/src/com/structurizr/view/ComponentView.java b/structurizr-core/src/com/structurizr/view/ComponentView.java index 4dcf9a35c..f3eb65b7a 100644 --- a/structurizr-core/src/com/structurizr/view/ComponentView.java +++ b/structurizr-core/src/com/structurizr/view/ComponentView.java @@ -308,6 +308,7 @@ public boolean getExternalContainerBoundariesVisible() { * * @param externalContainerBoundariesVisible true if external container boundaries should be visible, false otherwise */ + @Deprecated public void setExternalSoftwareSystemBoundariesVisible(boolean externalContainerBoundariesVisible) { this.externalContainerBoundariesVisible = externalContainerBoundariesVisible; } diff --git a/structurizr-core/src/com/structurizr/view/ContainerView.java b/structurizr-core/src/com/structurizr/view/ContainerView.java index 8c8cad219..3536b96fd 100644 --- a/structurizr-core/src/com/structurizr/view/ContainerView.java +++ b/structurizr-core/src/com/structurizr/view/ContainerView.java @@ -214,6 +214,7 @@ public boolean getExternalSoftwareSystemBoundariesVisible() { * * @param externalSoftwareSystemBoundariesVisible true if external software system boundaries should be visible, false otherwise */ + @Deprecated public void setExternalSoftwareSystemBoundariesVisible(boolean externalSoftwareSystemBoundariesVisible) { this.externalSoftwareSystemBoundariesVisible = externalSoftwareSystemBoundariesVisible; } diff --git a/structurizr-core/src/com/structurizr/view/DynamicView.java b/structurizr-core/src/com/structurizr/view/DynamicView.java index 6dbb48908..80f220a03 100644 --- a/structurizr-core/src/com/structurizr/view/DynamicView.java +++ b/structurizr-core/src/com/structurizr/view/DynamicView.java @@ -386,6 +386,7 @@ public boolean getExternalBoundariesVisible() { * * @param externalBoundariesVisible true if external boundaries should be visible, false otherwise */ + @Deprecated public void setExternalBoundariesVisible(boolean externalBoundariesVisible) { this.externalBoundariesVisible = externalBoundariesVisible; } From 64f4209ddbd5d3f194e651d71f8ce5506ed7c85b Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 14 Mar 2023 09:00:04 +0000 Subject: [PATCH 393/717] Typo. --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index ef52c066f..febeed413 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -11,7 +11,7 @@ ## 1.22.3 (11th March 2023) -- Adds better backwards compatibility for removal of documentation sections. +- Adds better backwards compatibility for removal of documentation section titles. ## 1.22.2 (10th March 2023) From 87dc67754b644450d85e0a0ccfaca833acac2efe Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 15 Mar 2023 12:53:13 +0000 Subject: [PATCH 394/717] Removes the check for empty content when adding a documentation section. --- docs/changelog.md | 1 + .../documentation/Documentation.java | 21 +++++++--------- .../structurizr/documentation/Section.java | 25 ------------------- .../documentation/DocumentationTests.java | 12 --------- 4 files changed, 10 insertions(+), 49 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index febeed413..ede3dba00 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -3,6 +3,7 @@ ## 1.23.1 (unreleased) - Deprecates the `setExternalBoundariesVisible` methods on `ContainerView`, `ComponentView`, and `DynamicView`. +- Removes the check for empty content when adding a documentation section. ## 1.23.0 (11th March 2023) diff --git a/structurizr-core/src/com/structurizr/documentation/Documentation.java b/structurizr-core/src/com/structurizr/documentation/Documentation.java index 27a85bcee..8bbcf3baf 100644 --- a/structurizr-core/src/com/structurizr/documentation/Documentation.java +++ b/structurizr-core/src/com/structurizr/documentation/Documentation.java @@ -3,20 +3,18 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.structurizr.util.StringUtils; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.Set; +import java.util.*; /** - * Represents the documentation within a workspace or software system - a collection of - * content in Markdown or AsciiDoc format, optionally with attached images. + * Represents the documentation within a workspace, software system, container, or component; + * a collection of content in Markdown or AsciiDoc format, optionally with attached images. * * See Documentation * on the Structurizr website for more details. */ public final class Documentation { - private Set

    sections = new HashSet<>(); + private List
    sections = new ArrayList<>(); private Set decisions = new HashSet<>(); private Set images = new HashSet<>(); @@ -29,7 +27,6 @@ public Documentation() { * @param section a Section object */ public void addSection(Section section) { - checkContentIsSpecified(section.getContent()); checkFormatIsSpecified(section.getFormat()); section.setOrder(calculateOrder()); @@ -63,13 +60,13 @@ private int calculateOrder() { * * @return a Set of {@link Section} objects */ - public Set
    getSections() { - return new HashSet<>(sections); + public Collection
    getSections() { + return new ArrayList<>(sections); } - void setSections(Set
    sections) { + void setSections(Collection
    sections) { if (sections != null) { - this.sections = new LinkedHashSet<>(sections); + this.sections = new ArrayList<>(sections); } } @@ -157,7 +154,7 @@ public boolean isEmpty() { * Removes all documentation, decisions, and images. */ public void clear() { - sections = new HashSet<>(); + sections = new ArrayList<>(); decisions = new HashSet<>(); images = new HashSet<>(); } diff --git a/structurizr-core/src/com/structurizr/documentation/Section.java b/structurizr-core/src/com/structurizr/documentation/Section.java index 85fc28002..007e53fa3 100644 --- a/structurizr-core/src/com/structurizr/documentation/Section.java +++ b/structurizr-core/src/com/structurizr/documentation/Section.java @@ -54,29 +54,4 @@ void setOrder(int order) { this.order = order; } - @Override - public boolean equals(Object object) { - if (this == object) { - return true; - } - - if (object == null || getClass() != object.getClass()) { - return false; - } - - Section section = (Section)object; - if (getElementId() != null) { - return getElementId().equals(section.getElementId()) && getContent().equals(section.getContent()); - } else { - return getContent().equals(section.getContent()); - } - } - - @Override - public int hashCode() { - int result = getElementId() != null ? getElementId().hashCode() : 0; - result = 31 * result + getContent().hashCode(); - return result; - } - } \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/documentation/DocumentationTests.java b/structurizr-core/test/unit/com/structurizr/documentation/DocumentationTests.java index 0526bed2a..187a85870 100644 --- a/structurizr-core/test/unit/com/structurizr/documentation/DocumentationTests.java +++ b/structurizr-core/test/unit/com/structurizr/documentation/DocumentationTests.java @@ -15,18 +15,6 @@ public void setUp() { documentation = workspace.getDocumentation(); } - @Test - void addSection_ThrowsAnException_WhenTheContentIsNotSpecified() { - try { - Section section = new Section(); - - documentation.addSection(section); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("Content must be specified.", iae.getMessage()); - } - } - @Test void addSection_ThrowsAnException_WhenTheFormatIsNotSpecified() { try { From c1fdef685d5d093d89bda73b3f98217394d0d2d1 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 17 Mar 2023 14:48:27 +0000 Subject: [PATCH 395/717] Updated release date. --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index ede3dba00..469427bfe 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 1.23.1 (unreleased) +## 1.23.1 (17th March 2023) - Deprecates the `setExternalBoundariesVisible` methods on `ContainerView`, `ComponentView`, and `DynamicView`. - Removes the check for empty content when adding a documentation section. From 0db6d36850e2c73c1aee2b073b854948c15771ca Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 24 Mar 2023 09:56:34 +0000 Subject: [PATCH 396/717] `DynamicView.endParallelSequences(true)` will now increment the counter when no relationships have been defined in the parallel sequence. --- build.gradle | 2 +- docs/changelog.md | 4 ++ .../com/structurizr/view/SequenceCounter.java | 6 +++ .../com/structurizr/view/SequenceNumber.java | 15 ++++++-- .../structurizr/view/DynamicViewTests.java | 38 +++++++++++++++++++ .../structurizr/view/SequenceNumberTests.java | 22 ++++++++++- 6 files changed, 82 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index 2745db2e8..9f93c6964 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.23.1' + version = '1.23.2' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 469427bfe..92c4241e7 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.23.2 (unreleased) + +- `DynamicView.endParallelSequences(true)` will now increment the counter when no relationships have been defined in the parallel sequence. + ## 1.23.1 (17th March 2023) - Deprecates the `setExternalBoundariesVisible` methods on `ContainerView`, `ComponentView`, and `DynamicView`. diff --git a/structurizr-core/src/com/structurizr/view/SequenceCounter.java b/structurizr-core/src/com/structurizr/view/SequenceCounter.java index dcfd3590e..8f8d52314 100644 --- a/structurizr-core/src/com/structurizr/view/SequenceCounter.java +++ b/structurizr-core/src/com/structurizr/view/SequenceCounter.java @@ -4,6 +4,7 @@ class SequenceCounter implements Cloneable { private SequenceCounter parent; private int sequence = 0; + private boolean incremented = false; SequenceCounter() { } @@ -14,6 +15,11 @@ class SequenceCounter implements Cloneable { void increment() { this.sequence++; + incremented = true; + } + + boolean incremented() { + return incremented; } int getSequence() { diff --git a/structurizr-core/src/com/structurizr/view/SequenceNumber.java b/structurizr-core/src/com/structurizr/view/SequenceNumber.java index 027938b6e..259bb98a0 100644 --- a/structurizr-core/src/com/structurizr/view/SequenceNumber.java +++ b/structurizr-core/src/com/structurizr/view/SequenceNumber.java @@ -18,9 +18,18 @@ void startParallelSequence() { void endParallelSequence(boolean endAllParallelSequencesAndContinueNumbering) { if (endAllParallelSequencesAndContinueNumbering) { - int sequence = this.counter.getSequence(); - this.counter = this.counter.getParent(); - this.counter.setSequence(sequence); + if (counter.incremented()) { + // relationships were added in this parallel sequence + int sequence = this.counter.getSequence(); + this.counter = this.counter.getParent(); + this.counter.setSequence(sequence); + } else { + // no relationships were added in this parallel sequence, so treat this as a group of parallel sequences + int sequence = this.counter.getSequence(); + this.counter = this.counter.getParent(); + this.counter.setSequence(sequence); + this.counter.increment(); + } } else { this.counter = this.counter.getParent(); } diff --git a/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java b/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java index efe39030d..029224428 100644 --- a/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java @@ -358,6 +358,44 @@ void parallelSequence() { assertEquals(1, view.getRelationships().stream().filter(r -> r.getOrder().equals("4")).count()); } + @Test + void parallelSequence2() { + workspace = new Workspace("Name", "Description"); + model = workspace.getModel(); + SoftwareSystem a = model.addSoftwareSystem("A"); + SoftwareSystem b = model.addSoftwareSystem("B"); + SoftwareSystem c = model.addSoftwareSystem("C"); + SoftwareSystem d = model.addSoftwareSystem("D"); + + a.uses(b, ""); + b.uses(c, ""); + b.uses(d, ""); + b.uses(a, ""); + + DynamicView view = workspace.getViews().createDynamicView("key", "Description"); + + RelationshipView rv1 = view.add(a, b); + + view.startParallelSequence(); + + view.startParallelSequence(); + RelationshipView rv2 = view.add(b, c); + view.endParallelSequence(); + + view.startParallelSequence(); + RelationshipView rv3 = view.add(b, d); + view.endParallelSequence(); + + view.endParallelSequence(true); + + RelationshipView rv4 = view.add(b, a); + + assertEquals("1", rv1.getOrder()); + assertEquals("2", rv2.getOrder()); + assertEquals("2", rv3.getOrder()); + assertEquals("3", rv4.getOrder()); + } + @Test void getRelationships_WhenTheOrderPropertyIsAnInteger() { containerA1.uses(containerA2, "uses"); diff --git a/structurizr-core/test/unit/com/structurizr/view/SequenceNumberTests.java b/structurizr-core/test/unit/com/structurizr/view/SequenceNumberTests.java index ed1eff62f..6735450c6 100644 --- a/structurizr-core/test/unit/com/structurizr/view/SequenceNumberTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/SequenceNumberTests.java @@ -14,7 +14,7 @@ void increment() { } @Test - void parallelSequences() { + void parallelSequences_1() { SequenceNumber sequenceNumber = new SequenceNumber(); assertEquals("1", sequenceNumber.getNext()); @@ -29,4 +29,24 @@ void parallelSequences() { assertEquals("3", sequenceNumber.getNext()); } + @Test + void parallelSequences_2() { + SequenceNumber sequenceNumber = new SequenceNumber(); + assertEquals("1", sequenceNumber.getNext()); + + sequenceNumber.startParallelSequence(); + + sequenceNumber.startParallelSequence(); + assertEquals("2", sequenceNumber.getNext()); + sequenceNumber.endParallelSequence(false); + + sequenceNumber.startParallelSequence(); + assertEquals("2", sequenceNumber.getNext()); + sequenceNumber.endParallelSequence(false); + + sequenceNumber.endParallelSequence(true); + + assertEquals("3", sequenceNumber.getNext()); + } + } From 20951b597652187976abdbfe1329094cbfed2d31 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 24 Mar 2023 09:57:19 +0000 Subject: [PATCH 397/717] Added release date. --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 92c4241e7..63eb72923 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 1.23.2 (unreleased) +## 1.23.2 (24th March 2023) - `DynamicView.endParallelSequences(true)` will now increment the counter when no relationships have been defined in the parallel sequence. From f571764e471933081579144a7c43e401cabed29e Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 29 Mar 2023 11:52:30 +0100 Subject: [PATCH 398/717] Adds support for deployment elements to be grouped. --- build.gradle | 2 +- docs/changelog.md | 4 ++++ .../src/com/structurizr/model/DeploymentElement.java | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 9f93c6964..94eb57956 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.23.2' + version = '1.24.0' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 63eb72923..563895978 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.24.0 (unreleased) + +- Adds support for deployment elements to be grouped. + ## 1.23.2 (24th March 2023) - `DynamicView.endParallelSequences(true)` will now increment the counter when no relationships have been defined in the parallel sequence. diff --git a/structurizr-core/src/com/structurizr/model/DeploymentElement.java b/structurizr-core/src/com/structurizr/model/DeploymentElement.java index 120a63af7..91d55cd21 100644 --- a/structurizr-core/src/com/structurizr/model/DeploymentElement.java +++ b/structurizr-core/src/com/structurizr/model/DeploymentElement.java @@ -5,7 +5,7 @@ /** * This is the superclass for model elements that describe deployment nodes, infrastructure nodes, and container instances. */ -public abstract class DeploymentElement extends Element { +public abstract class DeploymentElement extends GroupableElement { public static final String DEFAULT_DEPLOYMENT_ENVIRONMENT = "Default"; public static final String DEFAULT_DEPLOYMENT_GROUP = "Default"; From 458f3c8c7830d04cb6788e44f54535db8e8f887c Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 30 Mar 2023 07:01:00 +0100 Subject: [PATCH 399/717] Updated for release. --- docs/changelog.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 563895978..41285ad7d 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,8 +1,8 @@ # Changelog -## 1.24.0 (unreleased) +## 1.24.0 (30th March 2023) -- Adds support for deployment elements to be grouped. +- Adds a `group` property to deployment nodes, infrastructure nodes, software system instances, and container instances. ## 1.23.2 (24th March 2023) From a39b18d05f2ddc57ef3443645d61a203bbceff99 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 1 Apr 2023 06:51:47 +0100 Subject: [PATCH 400/717] Reduces visibility of `setOrder()` and `setDescription()` on `RelationshipView`, as they should not be public. --- .../src/com/structurizr/view/RelationshipView.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/structurizr-core/src/com/structurizr/view/RelationshipView.java b/structurizr-core/src/com/structurizr/view/RelationshipView.java index f3d416a05..48b992c16 100644 --- a/structurizr-core/src/com/structurizr/view/RelationshipView.java +++ b/structurizr-core/src/com/structurizr/view/RelationshipView.java @@ -83,7 +83,7 @@ public String getDescription() { * * @param description the description, as a String */ - public void setDescription(String description) { + void setDescription(String description) { this.description = description; } @@ -101,7 +101,7 @@ public String getOrder() { * * @param order the order, as a String */ - public void setOrder(String order) { + void setOrder(String order) { this.order = order; } From 264fefc32fc96eeae1fd68d834902239f4f9b15d Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 1 Apr 2023 06:52:08 +0100 Subject: [PATCH 401/717] Fixes IDEA's "protected method on a final class" warning. --- structurizr-core/src/com/structurizr/view/DynamicView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structurizr-core/src/com/structurizr/view/DynamicView.java b/structurizr-core/src/com/structurizr/view/DynamicView.java index 80f220a03..79331d3ce 100644 --- a/structurizr-core/src/com/structurizr/view/DynamicView.java +++ b/structurizr-core/src/com/structurizr/view/DynamicView.java @@ -235,7 +235,7 @@ public RelationshipView add(Relationship relationship, String description) { return addRelationship(relationship, description, sequenceNumber.getNext(), false); } - protected RelationshipView addRelationship(Relationship relationship, String description, String order, boolean response) { + private RelationshipView addRelationship(Relationship relationship, String description, String order, boolean response) { RelationshipView relationshipView = addRelationship(relationship); if (relationshipView != null) { relationshipView.setDescription(description); From b3c55e7aa3ed15e1a2e0f9d52d6d0ad971d95141 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 1 Apr 2023 06:52:23 +0100 Subject: [PATCH 402/717] . --- docs/changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 41285ad7d..5375586b5 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## (unreleased) + +- Reduces visibility of `setOrder()` and `setDescription()` on `RelationshipView`, as they should not be public. + ## 1.24.0 (30th March 2023) - Adds a `group` property to deployment nodes, infrastructure nodes, software system instances, and container instances. From a646e80f2ae080649eb749d3fc6ac238e5fb459b Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 1 Apr 2023 06:52:44 +0100 Subject: [PATCH 403/717] Adds deprecation tags to the `setLocation()` methods. --- structurizr-core/src/com/structurizr/model/Person.java | 1 + structurizr-core/src/com/structurizr/model/SoftwareSystem.java | 1 + 2 files changed, 2 insertions(+) diff --git a/structurizr-core/src/com/structurizr/model/Person.java b/structurizr-core/src/com/structurizr/model/Person.java index d4f67d3ba..858463bae 100644 --- a/structurizr-core/src/com/structurizr/model/Person.java +++ b/structurizr-core/src/com/structurizr/model/Person.java @@ -32,6 +32,7 @@ public Location getLocation() { return location; } + @Deprecated public void setLocation(Location location) { if (location != null) { this.location = location; diff --git a/structurizr-core/src/com/structurizr/model/SoftwareSystem.java b/structurizr-core/src/com/structurizr/model/SoftwareSystem.java index b59ae5075..b700c5b55 100644 --- a/structurizr-core/src/com/structurizr/model/SoftwareSystem.java +++ b/structurizr-core/src/com/structurizr/model/SoftwareSystem.java @@ -50,6 +50,7 @@ public Location getLocation() { * * @param location a Location instance */ + @Deprecated public void setLocation(Location location) { if (location != null) { this.location = location; From b4946c5b0e04abc2b815168709a7a29cd2f14c0c Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 5 Apr 2023 12:27:11 +0100 Subject: [PATCH 404/717] Updated for release. --- build.gradle | 2 +- docs/changelog.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 94eb57956..11e342f6f 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.24.0' + version = '1.24.1' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 5375586b5..e90529616 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -## (unreleased) +## 1.24.1 (5th April 2023) - Reduces visibility of `setOrder()` and `setDescription()` on `RelationshipView`, as they should not be public. From 2e6bc733443d1e0b27b05e104a402a7275b149de Mon Sep 17 00:00:00 2001 From: goto1134 <1134togo@gmail.com> Date: Wed, 17 May 2023 13:53:20 +0200 Subject: [PATCH 405/717] Extract AnimatedView interface --- .../src/com/structurizr/view/AnimatedView.java | 11 +++++++++++ .../src/com/structurizr/view/CustomView.java | 10 +++++++--- .../src/com/structurizr/view/DeploymentView.java | 10 +++++++--- .../src/com/structurizr/view/StaticView.java | 10 +++++++--- 4 files changed, 32 insertions(+), 9 deletions(-) create mode 100644 structurizr-core/src/com/structurizr/view/AnimatedView.java diff --git a/structurizr-core/src/com/structurizr/view/AnimatedView.java b/structurizr-core/src/com/structurizr/view/AnimatedView.java new file mode 100644 index 000000000..6d0fa9e2c --- /dev/null +++ b/structurizr-core/src/com/structurizr/view/AnimatedView.java @@ -0,0 +1,11 @@ +package com.structurizr.view; + +import javax.annotation.Nonnull; +import java.util.List; + +public interface AnimatedView { + + @Nonnull + List getAnimations(); + +} diff --git a/structurizr-core/src/com/structurizr/view/CustomView.java b/structurizr-core/src/com/structurizr/view/CustomView.java index ebe20cfa2..f5b9447ef 100644 --- a/structurizr-core/src/com/structurizr/view/CustomView.java +++ b/structurizr-core/src/com/structurizr/view/CustomView.java @@ -7,6 +7,7 @@ import com.structurizr.model.Relationship; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -15,8 +16,9 @@ /** * Represents a custom view, containing custom elements. */ -public final class CustomView extends ModelView { +public final class CustomView extends ModelView implements AnimatedView { + @Nonnull private List animations = new ArrayList<>(); private Model model; @@ -124,11 +126,13 @@ public void addAnimation(CustomElement... elements) { animations.add(new Animation(animations.size() + 1, elementsInThisAnimationStep, relationshipsInThisAnimationStep)); } + @Nonnull + @Override public List getAnimations() { return new ArrayList<>(animations); } - void setAnimations(List animations) { + void setAnimations(@Nullable List animations) { if (animations != null) { this.animations = new ArrayList<>(animations); } else { @@ -184,4 +188,4 @@ public void addAllCustomElements() { }); } -} \ No newline at end of file +} diff --git a/structurizr-core/src/com/structurizr/view/DeploymentView.java b/structurizr-core/src/com/structurizr/view/DeploymentView.java index f58ae73cb..04638445e 100644 --- a/structurizr-core/src/com/structurizr/view/DeploymentView.java +++ b/structurizr-core/src/com/structurizr/view/DeploymentView.java @@ -5,17 +5,19 @@ import com.structurizr.util.StringUtils; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.util.*; import java.util.stream.Collectors; /** * A deployment view, used to show the mapping of container instances to deployment nodes. */ -public final class DeploymentView extends ModelView { +public final class DeploymentView extends ModelView implements AnimatedView { private Model model; private String environment = DeploymentElement.DEFAULT_DEPLOYMENT_ENVIRONMENT; + @Nonnull private List animations = new ArrayList<>(); DeploymentView() { @@ -439,11 +441,13 @@ private DeploymentNode findDeploymentNode(Element e) { return null; } + @Nonnull + @Override public List getAnimations() { return new ArrayList<>(animations); } - void setAnimations(List animations) { + void setAnimations(@Nullable List animations) { if (animations != null) { this.animations = new ArrayList<>(animations); } else { @@ -479,4 +483,4 @@ public void remove(@Nonnull CustomElement customElement) { removeElement(customElement); } -} \ No newline at end of file +} diff --git a/structurizr-core/src/com/structurizr/view/StaticView.java b/structurizr-core/src/com/structurizr/view/StaticView.java index da0dfb569..f7ea1d9dd 100644 --- a/structurizr-core/src/com/structurizr/view/StaticView.java +++ b/structurizr-core/src/com/structurizr/view/StaticView.java @@ -3,6 +3,7 @@ import com.structurizr.model.*; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -11,8 +12,9 @@ /** * The superclass for all static views (system landscape, system context, container and component views). */ -public abstract class StaticView extends ModelView { +public abstract class StaticView extends ModelView implements AnimatedView { + @Nonnull private List animations = new ArrayList<>(); StaticView() { @@ -225,11 +227,13 @@ public void addAnimation(Element... elements) { animations.add(new Animation(animations.size() + 1, elementsInThisAnimationStep, relationshipsInThisAnimationStep)); } + @Nonnull + @Override public List getAnimations() { return new ArrayList<>(animations); } - void setAnimations(List animations) { + void setAnimations(@Nullable List animations) { if (animations != null) { this.animations = new ArrayList<>(animations); } else { @@ -265,4 +269,4 @@ public void remove(@Nonnull CustomElement customElement) { removeElement(customElement); } -} \ No newline at end of file +} From e2bf73bad4391ceba6328298fce8b108dbac12b5 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 7 Jul 2023 10:27:47 +0100 Subject: [PATCH 406/717] Fixes #213. --- build.gradle | 2 +- docs/changelog.md | 4 ++ .../src/com/structurizr/view/ViewSet.java | 43 +++++++++++++---- .../com/structurizr/view/ViewSetTests.java | 48 +++++++++++++------ 4 files changed, 72 insertions(+), 25 deletions(-) diff --git a/build.gradle b/build.gradle index 11e342f6f..cfda4d6fe 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.24.1' + version = '1.25.0' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index e90529616..344175460 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.25.0 (unreleased to Maven Central) + +- Fixes https://github.com/structurizr/java/issues/213 (Views are not created automatically if non-English characters are used in software systems' names) + ## 1.24.1 (5th April 2023) - Reduces visibility of `setOrder()` and `setDescription()` on `RelationshipView`, as they should not be public. diff --git a/structurizr-core/src/com/structurizr/view/ViewSet.java b/structurizr-core/src/com/structurizr/view/ViewSet.java index 02e799b3f..79559dd62 100644 --- a/structurizr-core/src/com/structurizr/view/ViewSet.java +++ b/structurizr-core/src/com/structurizr/view/ViewSet.java @@ -8,6 +8,8 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import java.text.DecimalFormat; +import java.text.NumberFormat; import java.util.*; import static com.structurizr.util.StringUtils.isNullOrEmpty; @@ -19,6 +21,13 @@ public final class ViewSet { private static final Log log = LogFactory.getLog(ViewSet.class); + public static final String SYSTEM_LANDSCAPE_VIEW_TYPE = "SystemLandscape"; + public static final String SYSTEM_CONTEXT_VIEW_TYPE = "SystemContext"; + public static final String CONTAINER_VIEW_TYPE = "Container"; + public static final String COMPONENT_VIEW_TYPE = "Component"; + public static final String DYNAMIC_VIEW_TYPE = "Dynamic"; + public static final String DEPLOYMENT_VIEW_TYPE = "Deployment"; + private Model model; private Collection customViews = new HashSet<>(); @@ -822,9 +831,27 @@ public boolean isEmpty() { return customViews.isEmpty() && systemLandscapeViews.isEmpty() && systemContextViews.isEmpty() && containerViews.isEmpty() && componentViews.isEmpty() && dynamicViews.isEmpty() && deploymentViews.isEmpty() && filteredViews.isEmpty(); } + public String generateViewKey(String prefix) { + NumberFormat format = new DecimalFormat("000"); + int counter = 1; + String key = prefix + "-" + format.format(counter); + + while (hasViewWithKey(key)) { + counter++; + key = prefix + "-" + format.format(counter); + } + + log.warn(key + " is an automatically generated view key - you will likely lose manual layout information when using automatically generated view keys."); + return key; + } + + private boolean hasViewWithKey(String key) { + return getViews().stream().anyMatch(view -> view.getKey().equals(key)); + } + public void createDefaultViews() { // create a single System Landscape diagram containing all people and software systems - SystemLandscapeView systemLandscapeView = createSystemLandscapeView("SystemLandscape", ""); + SystemLandscapeView systemLandscapeView = createSystemLandscapeView(generateViewKey(SYSTEM_LANDSCAPE_VIEW_TYPE), ""); systemLandscapeView.addDefaultElements(); systemLandscapeView.enableAutomaticLayout(AutomaticLayout.RankDirection.TopBottom, 300, 300); systemLandscapeView.setEnterpriseBoundaryVisible(true); @@ -835,7 +862,7 @@ public void createDefaultViews() { // and a system context view plus container view for each software system for (SoftwareSystem softwareSystem : softwareSystems) { - String systemContextViewKey = removeNonWordCharacters(softwareSystem.getName()) + "-SystemContext"; + String systemContextViewKey = generateViewKey(SYSTEM_CONTEXT_VIEW_TYPE); SystemContextView systemContextView = createSystemContextView(softwareSystem, systemContextViewKey, ""); systemContextView.addDefaultElements(); systemContextView.enableAutomaticLayout(AutomaticLayout.RankDirection.TopBottom, 300, 300); @@ -845,7 +872,7 @@ public void createDefaultViews() { List containers = new ArrayList<>(softwareSystem.getContainers()); containers.sort(Comparator.comparing(Element::getName)); - String containerViewKey = removeNonWordCharacters(softwareSystem.getName()) + "-Container"; + String containerViewKey = generateViewKey(CONTAINER_VIEW_TYPE); ContainerView containerView = createContainerView(softwareSystem, containerViewKey, ""); containerView.addDefaultElements(); containerView.enableAutomaticLayout(AutomaticLayout.RankDirection.TopBottom, 300, 300); @@ -853,7 +880,7 @@ public void createDefaultViews() { for (Container container : containers) { if (container.getComponents().size() > 0) { - String componentViewKey = removeNonWordCharacters(softwareSystem.getName()) + "-" + removeNonWordCharacters(container.getName()) + "-Component"; + String componentViewKey = generateViewKey(COMPONENT_VIEW_TYPE); ComponentView componentView = createComponentView(container, componentViewKey, ""); componentView.addDefaultElements(); componentView.enableAutomaticLayout(AutomaticLayout.RankDirection.TopBottom, 300, 300); @@ -899,7 +926,7 @@ public void createDefaultViews() { if (softwareSystems.isEmpty()) { // there are no container instances, but perhaps there are infrastructure nodes in this environment if (model.getElements().stream().anyMatch(e -> e instanceof InfrastructureNode && ((InfrastructureNode)e).getEnvironment().equals(deploymentEnvironment))) { - String deploymentViewKey = removeNonWordCharacters(deploymentEnvironment) + "-Deployment"; + String deploymentViewKey = generateViewKey(DEPLOYMENT_VIEW_TYPE); DeploymentView deploymentView = createDeploymentView(deploymentViewKey, ""); deploymentView.setEnvironment(deploymentEnvironment); deploymentView.addDefaultElements(); @@ -909,7 +936,7 @@ public void createDefaultViews() { softwareSystems.sort(Comparator.comparing(Element::getName)); for (SoftwareSystem softwareSystem : softwareSystems) { - String deploymentViewKey = removeNonWordCharacters(softwareSystem.getName()) + "-" + removeNonWordCharacters(deploymentEnvironment) + "-Deployment"; + String deploymentViewKey = generateViewKey(DEPLOYMENT_VIEW_TYPE); DeploymentView deploymentView = createDeploymentView(softwareSystem, deploymentViewKey, ""); deploymentView.setEnvironment(deploymentEnvironment); deploymentView.addDefaultElements(); @@ -919,10 +946,6 @@ public void createDefaultViews() { } } - private String removeNonWordCharacters(String name) { - return name.replaceAll("\\W", ""); - } - private Set getSoftwareSystemInstances(DeploymentNode deploymentNode) { Set softwareSystemInstances = new HashSet<>(deploymentNode.getSoftwareSystemInstances()); diff --git a/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java b/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java index d490ae9fe..b1f1a036d 100644 --- a/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java @@ -1033,19 +1033,19 @@ void createDefaultViews() { views.createDefaultViews(); assertEquals(1, views.getSystemLandscapeViews().size()); - assertEquals("SystemLandscape", views.getSystemLandscapeViews().iterator().next().getKey()); + assertEquals("SystemLandscape-001", views.getSystemLandscapeViews().iterator().next().getKey()); assertEquals(2, views.getSystemContextViews().size()); - assertSame(ss1, views.getSystemContextViews().stream().filter(v -> v.getKey().equals("SoftwareSystem1-SystemContext")).findFirst().get().getSoftwareSystem()); - assertSame(ss2, views.getSystemContextViews().stream().filter(v -> v.getKey().equals("SoftwareSystem2-SystemContext")).findFirst().get().getSoftwareSystem()); + assertSame(ss1, views.getSystemContextViews().stream().filter(v -> v.getKey().equals("SystemContext-001")).findFirst().get().getSoftwareSystem()); + assertSame(ss2, views.getSystemContextViews().stream().filter(v -> v.getKey().equals("SystemContext-002")).findFirst().get().getSoftwareSystem()); assertEquals(2, views.getContainerViews().size()); - assertSame(ss1, views.getContainerViews().stream().filter(v -> v.getKey().equals("SoftwareSystem1-Container")).findFirst().get().getSoftwareSystem()); - assertSame(ss2, views.getContainerViews().stream().filter(v -> v.getKey().equals("SoftwareSystem2-Container")).findFirst().get().getSoftwareSystem()); + assertSame(ss1, views.getContainerViews().stream().filter(v -> v.getKey().equals("Container-001")).findFirst().get().getSoftwareSystem()); + assertSame(ss2, views.getContainerViews().stream().filter(v -> v.getKey().equals("Container-002")).findFirst().get().getSoftwareSystem()); assertEquals(2, views.getComponentViews().size()); - assertSame(c1, views.getComponentViews().stream().filter(v -> v.getKey().equals("SoftwareSystem1-Container1-Component")).findFirst().get().getContainer()); - assertSame(c2, views.getComponentViews().stream().filter(v -> v.getKey().equals("SoftwareSystem2-Container2-Component")).findFirst().get().getContainer()); + assertSame(c1, views.getComponentViews().stream().filter(v -> v.getKey().equals("Component-001")).findFirst().get().getContainer()); + assertSame(c2, views.getComponentViews().stream().filter(v -> v.getKey().equals("Component-002")).findFirst().get().getContainer()); assertEquals(0, views.getDynamicViews().size()); assertEquals(0, views.getDeploymentViews().size()); @@ -1056,7 +1056,7 @@ void createDefaultViews() { views.createDefaultViews(); assertEquals(1, views.getDeploymentViews().size()); - assertSame("Live", views.getDeploymentViews().stream().filter(v -> v.getKey().equals("Live-Deployment")).findFirst().get().getEnvironment()); + assertSame("Live", views.getDeploymentViews().stream().filter(v -> v.getKey().equals("Deployment-001")).findFirst().get().getEnvironment()); dev.add(ss1); liveEc2.add(c1); @@ -1066,12 +1066,12 @@ void createDefaultViews() { views.createDefaultViews(); assertEquals(3, views.getDeploymentViews().size()); - assertSame(ss1, views.getDeploymentViews().stream().filter(v -> v.getKey().equals("SoftwareSystem1-Development-Deployment")).findFirst().get().getSoftwareSystem()); - assertSame("Development", views.getDeploymentViews().stream().filter(v -> v.getKey().equals("SoftwareSystem1-Development-Deployment")).findFirst().get().getEnvironment()); - assertSame(ss1, views.getDeploymentViews().stream().filter(v -> v.getKey().equals("SoftwareSystem1-Live-Deployment")).findFirst().get().getSoftwareSystem()); - assertSame("Live", views.getDeploymentViews().stream().filter(v -> v.getKey().equals("SoftwareSystem1-Live-Deployment")).findFirst().get().getEnvironment()); - assertSame(ss2, views.getDeploymentViews().stream().filter(v -> v.getKey().equals("SoftwareSystem2-Live-Deployment")).findFirst().get().getSoftwareSystem()); - assertSame("Live", views.getDeploymentViews().stream().filter(v -> v.getKey().equals("SoftwareSystem2-Live-Deployment")).findFirst().get().getEnvironment()); + assertSame(ss1, views.getDeploymentViews().stream().filter(v -> v.getKey().equals("Deployment-001")).findFirst().get().getSoftwareSystem()); + assertSame("Development", views.getDeploymentViews().stream().filter(v -> v.getKey().equals("Deployment-001")).findFirst().get().getEnvironment()); + assertSame(ss1, views.getDeploymentViews().stream().filter(v -> v.getKey().equals("Deployment-002")).findFirst().get().getSoftwareSystem()); + assertSame("Live", views.getDeploymentViews().stream().filter(v -> v.getKey().equals("Deployment-002")).findFirst().get().getEnvironment()); + assertSame(ss2, views.getDeploymentViews().stream().filter(v -> v.getKey().equals("Deployment-003")).findFirst().get().getSoftwareSystem()); + assertSame("Live", views.getDeploymentViews().stream().filter(v -> v.getKey().equals("Deployment-003")).findFirst().get().getEnvironment()); } @Test @@ -1122,4 +1122,24 @@ void view_ordering() { assertEquals(9, systemLandscapeView2.getOrder()); } + @Test + public void createDefaultViews_ForSoftwareSystemsWithNamesUsingUTF8Characters() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem ss1 = workspace.getModel().addSoftwareSystem("English is fine"); + SoftwareSystem ss2 = workspace.getModel().addSoftwareSystem("Ðо люди говорÑÑ‚ и на других Ñзыках"); + SoftwareSystem ss3 = workspace.getModel().addSoftwareSystem("英語ã ã‘ãŒè¨€èªžã§ã¯ãªã„"); + + workspace.getViews().createDefaultViews(); + + assertEquals(4, workspace.getViews().getViews().size()); + + assertEquals(1, workspace.getViews().getSystemLandscapeViews().size()); + assertEquals("SystemLandscape-001", workspace.getViews().getSystemLandscapeViews().iterator().next().getKey()); + + assertEquals(3, workspace.getViews().getSystemContextViews().size()); + assertSame(ss1, workspace.getViews().getSystemContextViews().stream().filter(v -> v.getKey().equals("SystemContext-001")).findFirst().get().getSoftwareSystem()); + assertSame(ss2, workspace.getViews().getSystemContextViews().stream().filter(v -> v.getKey().equals("SystemContext-002")).findFirst().get().getSoftwareSystem()); + assertSame(ss3, workspace.getViews().getSystemContextViews().stream().filter(v -> v.getKey().equals("SystemContext-003")).findFirst().get().getSoftwareSystem()); + } + } \ No newline at end of file From e2a6fecb1a51f597d3185ffa85c4afbe8c8211dc Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 22 Jul 2023 12:01:44 +0100 Subject: [PATCH 407/717] Adds a way to load themes with a timeout. --- docs/changelog.md | 3 +- .../src/com/structurizr/view/ThemeUtils.java | 33 +++++++++++++++++-- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 344175460..a8f824ccf 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,8 +1,9 @@ # Changelog -## 1.25.0 (unreleased to Maven Central) +## 1.25.0 (22nd July 2023) - Fixes https://github.com/structurizr/java/issues/213 (Views are not created automatically if non-English characters are used in software systems' names) +- Adds a way to load themes with a timeout. ## 1.24.1 (5th April 2023) diff --git a/structurizr-client/src/com/structurizr/view/ThemeUtils.java b/structurizr-client/src/com/structurizr/view/ThemeUtils.java index 22c177e62..418787407 100644 --- a/structurizr-client/src/com/structurizr/view/ThemeUtils.java +++ b/structurizr-client/src/com/structurizr/view/ThemeUtils.java @@ -8,13 +8,16 @@ import com.structurizr.io.WorkspaceWriterException; import com.structurizr.util.StringUtils; import org.apache.hc.client5.http.classic.methods.HttpGet; +import org.apache.hc.client5.http.config.ConnectionConfig; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; -import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; +import org.apache.hc.client5.http.impl.io.BasicHttpClientConnectionManager; import org.apache.hc.core5.http.io.entity.EntityUtils; import java.io.*; import java.nio.charset.StandardCharsets; +import java.util.concurrent.TimeUnit; /** * Some utility methods for exporting themes to JSON. @@ -23,6 +26,8 @@ public final class ThemeUtils { private static final int HTTP_OK_STATUS = 200; + private static final int DEFAULT_TIMEOUT_IN_MILLISECONDS = 10000; + /** * Serializes the theme (element and relationship styles) in the specified workspace to a file, as a JSON string. * @@ -62,13 +67,37 @@ public static String toJson(Workspace workspace) throws Exception { /** * Loads (and inlines) the element and relationship styles from the themes defined in the workspace, into the workspace itself. * This implementation simply copies the styles from all themes into the workspace. + * This uses a default timeout value of 10000ms. * * @param workspace a Workspace object * @throws Exception if something goes wrong */ public static void loadThemes(Workspace workspace) throws Exception { + loadThemes(workspace, DEFAULT_TIMEOUT_IN_MILLISECONDS); + } + + /** + * Loads (and inlines) the element and relationship styles from the themes defined in the workspace, into the workspace itself. + * This implementation simply copies the styles from all themes into the workspace. + * + * @param workspace a Workspace object + * @param timeoutInMilliseconds the timeout in milliseconds + * @throws Exception if something goes wrong + */ + public static void loadThemes(Workspace workspace, int timeoutInMilliseconds) throws Exception { for (String url : workspace.getViews().getConfiguration().getThemes()) { - CloseableHttpClient httpClient = HttpClients.createSystem(); + ConnectionConfig connectionConfig = ConnectionConfig.custom() + .setConnectTimeout(timeoutInMilliseconds, TimeUnit.MILLISECONDS) + .setSocketTimeout(timeoutInMilliseconds, TimeUnit.MILLISECONDS) + .build(); + + BasicHttpClientConnectionManager cm = new BasicHttpClientConnectionManager(); + cm.setConnectionConfig(connectionConfig); + + CloseableHttpClient httpClient = HttpClientBuilder.create() + .setConnectionManager(cm) + .build(); + HttpGet httpGet = new HttpGet(url); CloseableHttpResponse response = httpClient.execute(httpGet); From 53bceb7627949e1d454c95c88c53f1ce1c82fbaa Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 26 Jul 2023 14:45:31 +0100 Subject: [PATCH 408/717] Adds a `clearUsers()` method to clear configured users on the workspace configuration. --- build.gradle | 2 +- docs/changelog.md | 4 ++++ .../configuration/WorkspaceConfiguration.java | 7 +++++++ .../configuration/WorkspaceConfigurationTests.java | 10 ++++++++++ 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index cfda4d6fe..a2810d4dd 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.25.0' + version = '1.25.1' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index a8f824ccf..0c89b4453 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.25.1 (unreleased to Maven Central) + +- Adds a `clearUsers()` method to clear configured users on the workspace configuration. + ## 1.25.0 (22nd July 2023) - Fixes https://github.com/structurizr/java/issues/213 (Views are not created automatically if non-English characters are used in software systems' names) diff --git a/structurizr-core/src/com/structurizr/configuration/WorkspaceConfiguration.java b/structurizr-core/src/com/structurizr/configuration/WorkspaceConfiguration.java index 63627feea..d46eb3ba0 100644 --- a/structurizr-core/src/com/structurizr/configuration/WorkspaceConfiguration.java +++ b/structurizr-core/src/com/structurizr/configuration/WorkspaceConfiguration.java @@ -48,4 +48,11 @@ public void addUser(String username, Role role) { users.add(new User(username, role)); } + /** + * Clears all configured users. + */ + public void clearUsers() { + this.users = new HashSet<>(); + } + } \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/configuration/WorkspaceConfigurationTests.java b/structurizr-core/test/unit/com/structurizr/configuration/WorkspaceConfigurationTests.java index 16d067eb3..23ba9deeb 100644 --- a/structurizr-core/test/unit/com/structurizr/configuration/WorkspaceConfigurationTests.java +++ b/structurizr-core/test/unit/com/structurizr/configuration/WorkspaceConfigurationTests.java @@ -50,4 +50,14 @@ void addUser_AddsAUser() { assertEquals(Role.ReadOnly, configuration.getUsers().iterator().next().getRole()); } + @Test + void clearUsers() { + WorkspaceConfiguration configuration = new WorkspaceConfiguration(); + configuration.addUser("user@domain.com", Role.ReadOnly); + assertEquals(1, configuration.getUsers().size()); + + configuration.clearUsers(); + assertEquals(0, configuration.getUsers().size()); + } + } \ No newline at end of file From 69fbc7a26b917d6cb18ad56c2b61bb2a95ccccac Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 26 Jul 2023 14:47:37 +0100 Subject: [PATCH 409/717] Updated for release. --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 0c89b4453..d5462431b 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 1.25.1 (unreleased to Maven Central) +## 1.25.1 (26th July 2023) - Adds a `clearUsers()` method to clear configured users on the workspace configuration. From 611c6f442af1efd6719180db4308be276ecde907 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 28 Jul 2023 10:18:01 +0100 Subject: [PATCH 410/717] Make User constructor public. --- .../com/structurizr/configuration/User.java | 12 ++++- .../configuration/WorkspaceConfiguration.java | 17 +++---- .../structurizr/configuration/UserTests.java | 48 +++++++++++++++++++ .../WorkspaceConfigurationTests.java | 17 ++++--- 4 files changed, 79 insertions(+), 15 deletions(-) create mode 100644 structurizr-core/test/unit/com/structurizr/configuration/UserTests.java diff --git a/structurizr-core/src/com/structurizr/configuration/User.java b/structurizr-core/src/com/structurizr/configuration/User.java index efad4930c..cdfd4e3c1 100644 --- a/structurizr-core/src/com/structurizr/configuration/User.java +++ b/structurizr-core/src/com/structurizr/configuration/User.java @@ -1,5 +1,7 @@ package com.structurizr.configuration; +import com.structurizr.util.StringUtils; + /** * Represents a user, and the role-based access they have to a workspace. */ @@ -11,7 +13,15 @@ public final class User { User() { } - User(String username, Role role) { + public User(String username, Role role) { + if (StringUtils.isNullOrEmpty(username)) { + throw new IllegalArgumentException("A username must be specified."); + } + + if (role == null) { + throw new IllegalArgumentException("A role must be specified."); + } + setUsername(username); setRole(role); } diff --git a/structurizr-core/src/com/structurizr/configuration/WorkspaceConfiguration.java b/structurizr-core/src/com/structurizr/configuration/WorkspaceConfiguration.java index d46eb3ba0..aacce39c0 100644 --- a/structurizr-core/src/com/structurizr/configuration/WorkspaceConfiguration.java +++ b/structurizr-core/src/com/structurizr/configuration/WorkspaceConfiguration.java @@ -30,6 +30,15 @@ void setUsers(Set users) { } } + /** + * Adds a user. + * + * @param user a User object representing the username and role + */ + public void addUser(User user) { + users.add(user); + } + /** * Adds a user, with the specified username and role. * @@ -37,14 +46,6 @@ void setUsers(Set users) { * @param role the user's role */ public void addUser(String username, Role role) { - if (StringUtils.isNullOrEmpty(username)) { - throw new IllegalArgumentException("A username must be specified."); - } - - if (role == null) { - throw new IllegalArgumentException("A role must be specified."); - } - users.add(new User(username, role)); } diff --git a/structurizr-core/test/unit/com/structurizr/configuration/UserTests.java b/structurizr-core/test/unit/com/structurizr/configuration/UserTests.java new file mode 100644 index 000000000..efacd7398 --- /dev/null +++ b/structurizr-core/test/unit/com/structurizr/configuration/UserTests.java @@ -0,0 +1,48 @@ +package com.structurizr.configuration; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +public class UserTests { + + @Test + void construct_ThrowsAnException_WhenANullUsernameIsSpecified() { + try { + new User(null, Role.ReadWrite); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("A username must be specified.", iae.getMessage()); + } + } + + @Test + void construct_ThrowsAnException_WhenAnEmptyUsernameIsSpecified() { + try { + new User(" ", Role.ReadWrite); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("A username must be specified.", iae.getMessage()); + } + } + + @Test + void construct_ThrowsAnException_WhenANullRoleIsSpecified() { + try { + new User("user@domain.com", null); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("A role must be specified.", iae.getMessage()); + } + } + + @Test + void comstruct() { + User user = new User("user@domain.com", Role.ReadOnly); + + assertEquals("user@domain.com", user.getUsername()); + assertEquals(Role.ReadOnly, user.getRole()); + } + +} \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/configuration/WorkspaceConfigurationTests.java b/structurizr-core/test/unit/com/structurizr/configuration/WorkspaceConfigurationTests.java index 23ba9deeb..2f2cd68fc 100644 --- a/structurizr-core/test/unit/com/structurizr/configuration/WorkspaceConfigurationTests.java +++ b/structurizr-core/test/unit/com/structurizr/configuration/WorkspaceConfigurationTests.java @@ -2,8 +2,7 @@ import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.*; public class WorkspaceConfigurationTests { @@ -33,7 +32,7 @@ void addUser_ThrowsAnException_WhenAnEmptyUsernameIsSpecified() { void addUser_ThrowsAnException_WhenANullRoleIsSpecified() { try { WorkspaceConfiguration configuration = new WorkspaceConfiguration(); - configuration.addUser("user@domain.com", null); + configuration.addUser("user@example.com", null); fail(); } catch (IllegalArgumentException iae) { assertEquals("A role must be specified.", iae.getMessage()); @@ -43,11 +42,17 @@ void addUser_ThrowsAnException_WhenANullRoleIsSpecified() { @Test void addUser_AddsAUser() { WorkspaceConfiguration configuration = new WorkspaceConfiguration(); - configuration.addUser("user@domain.com", Role.ReadOnly); + configuration.addUser("user1@example.com", Role.ReadOnly); assertEquals(1, configuration.getUsers().size()); - assertEquals("user@domain.com", configuration.getUsers().iterator().next().getUsername()); - assertEquals(Role.ReadOnly, configuration.getUsers().iterator().next().getRole()); + User user = configuration.getUsers().stream().filter(u -> u.getUsername().equals("user1@example.com")).findFirst().get(); + assertEquals(Role.ReadOnly, user.getRole()); + + configuration.addUser("user2@example.com", Role.ReadWrite); + + assertEquals(2, configuration.getUsers().size()); + user = configuration.getUsers().stream().filter(u -> u.getUsername().equals("user2@example.com")).findFirst().get(); + assertEquals(Role.ReadWrite, user.getRole()); } @Test From 826129c1ce1f657acfe47921940a52af93d63a06 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 28 Jul 2023 10:18:29 +0100 Subject: [PATCH 411/717] Adds the ability to specify the workspace visibility (private/public) via the workspace configuration. --- docs/changelog.md | 4 ++++ .../structurizr/configuration/Visibility.java | 8 ++++++++ .../configuration/WorkspaceConfiguration.java | 19 +++++++++++++++++++ .../WorkspaceConfigurationTests.java | 9 +++++++++ 4 files changed, 40 insertions(+) create mode 100644 structurizr-core/src/com/structurizr/configuration/Visibility.java diff --git a/docs/changelog.md b/docs/changelog.md index d5462431b..1898e9f4d 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.26.0 (unreleased to Maven Central) + +- Adds the ability to specify the workspace visibility (private/public) via the workspace configuration. + ## 1.25.1 (26th July 2023) - Adds a `clearUsers()` method to clear configured users on the workspace configuration. diff --git a/structurizr-core/src/com/structurizr/configuration/Visibility.java b/structurizr-core/src/com/structurizr/configuration/Visibility.java new file mode 100644 index 000000000..a61fff520 --- /dev/null +++ b/structurizr-core/src/com/structurizr/configuration/Visibility.java @@ -0,0 +1,8 @@ +package com.structurizr.configuration; + +public enum Visibility { + + Private, + Public + +} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/configuration/WorkspaceConfiguration.java b/structurizr-core/src/com/structurizr/configuration/WorkspaceConfiguration.java index aacce39c0..18a87c961 100644 --- a/structurizr-core/src/com/structurizr/configuration/WorkspaceConfiguration.java +++ b/structurizr-core/src/com/structurizr/configuration/WorkspaceConfiguration.java @@ -10,11 +10,30 @@ */ public final class WorkspaceConfiguration { + private Visibility visibility = null; private Set users = new HashSet<>(); WorkspaceConfiguration() { } + /** + * Gets the visibility of this workspace (private or public). + * + * @return a Visibility enum + */ + public Visibility getVisibility() { + return visibility; + } + + /** + * Gets the visibility of this workspace (private or public). + * + * @param visibility a Visibility enum + */ + void setVisibility(Visibility visibility) { + this.visibility = visibility; + } + /** * Gets the set of users should have read-write or read-only access to the workspace. * diff --git a/structurizr-core/test/unit/com/structurizr/configuration/WorkspaceConfigurationTests.java b/structurizr-core/test/unit/com/structurizr/configuration/WorkspaceConfigurationTests.java index 2f2cd68fc..529339033 100644 --- a/structurizr-core/test/unit/com/structurizr/configuration/WorkspaceConfigurationTests.java +++ b/structurizr-core/test/unit/com/structurizr/configuration/WorkspaceConfigurationTests.java @@ -55,6 +55,15 @@ void addUser_AddsAUser() { assertEquals(Role.ReadWrite, user.getRole()); } + @Test + void visibility() { + WorkspaceConfiguration configuration = new WorkspaceConfiguration(); + assertNull(configuration.getVisibility()); + + configuration.setVisibility(Visibility.Private); + assertEquals(Visibility.Private, configuration.getVisibility()); + } + @Test void clearUsers() { WorkspaceConfiguration configuration = new WorkspaceConfiguration(); From 2c0218be907fee0a748f99cf5092d271fcf3b5ff Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 28 Jul 2023 10:32:51 +0100 Subject: [PATCH 412/717] Updated for release. --- build.gradle | 2 +- docs/changelog.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index a2810d4dd..a362d07a6 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.25.1' + version = '1.26.0' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 1898e9f4d..fbbcfec75 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 1.26.0 (unreleased to Maven Central) +## 1.26.0 (28th July 2023) - Adds the ability to specify the workspace visibility (private/public) via the workspace configuration. From c7e33ab4effad9d97d0f75dffc64e85a852070de Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 28 Jul 2023 11:06:50 +0100 Subject: [PATCH 413/717] Setter needs to be public. --- build.gradle | 2 +- docs/changelog.md | 2 +- .../structurizr/configuration/WorkspaceConfiguration.java | 6 +++--- .../configuration/WorkspaceConfigurationTests.java | 3 +++ 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index a362d07a6..69fb87957 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.26.0' + version = '1.26.1' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index fbbcfec75..15f4e97b6 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 1.26.0 (28th July 2023) +## 1.26.1 (28th July 2023) - Adds the ability to specify the workspace visibility (private/public) via the workspace configuration. diff --git a/structurizr-core/src/com/structurizr/configuration/WorkspaceConfiguration.java b/structurizr-core/src/com/structurizr/configuration/WorkspaceConfiguration.java index 18a87c961..d937b3c33 100644 --- a/structurizr-core/src/com/structurizr/configuration/WorkspaceConfiguration.java +++ b/structurizr-core/src/com/structurizr/configuration/WorkspaceConfiguration.java @@ -26,11 +26,11 @@ public Visibility getVisibility() { } /** - * Gets the visibility of this workspace (private or public). + * Sets the visibility of this workspace (private or public). * - * @param visibility a Visibility enum + * @param visibility a Visibility enum, or null to indicate that no changes should be made */ - void setVisibility(Visibility visibility) { + public void setVisibility(Visibility visibility) { this.visibility = visibility; } diff --git a/structurizr-core/test/unit/com/structurizr/configuration/WorkspaceConfigurationTests.java b/structurizr-core/test/unit/com/structurizr/configuration/WorkspaceConfigurationTests.java index 529339033..c4a2f99f2 100644 --- a/structurizr-core/test/unit/com/structurizr/configuration/WorkspaceConfigurationTests.java +++ b/structurizr-core/test/unit/com/structurizr/configuration/WorkspaceConfigurationTests.java @@ -62,6 +62,9 @@ void visibility() { configuration.setVisibility(Visibility.Private); assertEquals(Visibility.Private, configuration.getVisibility()); + + configuration.setVisibility(null); + assertNull(configuration.getVisibility()); } @Test From 940a0e9b2533c5017c9fad9fef11e30a065af9f7 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 18 Sep 2023 16:56:27 +0100 Subject: [PATCH 414/717] - Upgrades dependencies, targets Java 17. --- build.gradle | 6 +++--- docs/changelog.md | 4 ++++ structurizr-client/build.gradle | 6 +++--- structurizr-core/build.gradle | 6 +++--- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/build.gradle b/build.gradle index 69fb87957..5a0c3fe9d 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.26.1' + version = '1.27.0' repositories { mavenCentral() @@ -34,8 +34,8 @@ subprojects { proj -> compileJava.options.encoding = 'UTF-8' compileTestJava.options.encoding = 'UTF-8' - sourceCompatibility = 1.8 - targetCompatibility = 1.8 + sourceCompatibility = 17 + targetCompatibility = 17 java { withJavadocJar() diff --git a/docs/changelog.md b/docs/changelog.md index 15f4e97b6..556c54910 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.27.0 (unreleased) + +- Upgrades dependencies, targets Java 17. + ## 1.26.1 (28th July 2023) - Adds the ability to specify the workspace visibility (private/public) via the workspace configuration. diff --git a/structurizr-client/build.gradle b/structurizr-client/build.gradle index 77d8bbcf7..300d750ed 100644 --- a/structurizr-client/build.gradle +++ b/structurizr-client/build.gradle @@ -2,11 +2,11 @@ dependencies { api project(':structurizr-core') - api 'com.fasterxml.jackson.core:jackson-databind:2.14.2' + api 'com.fasterxml.jackson.core:jackson-databind:2.15.1' api 'org.apache.httpcomponents.client5:httpclient5:5.2.1' - api 'javax.xml.bind:jaxb-api:2.3.1' + api 'javax.xml.bind:jaxb-api:2.4.0-b180830.0359' - testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' + testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2' } diff --git a/structurizr-core/build.gradle b/structurizr-core/build.gradle index 54190e009..3ffe4688a 100644 --- a/structurizr-core/build.gradle +++ b/structurizr-core/build.gradle @@ -1,10 +1,10 @@ dependencies { - api 'com.fasterxml.jackson.core:jackson-annotations:2.14.2' + api 'com.fasterxml.jackson.core:jackson-annotations:2.15.1' api 'com.google.code.findbugs:jsr305:3.0.2' api 'commons-logging:commons-logging:1.2' - testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' - testImplementation 'org.assertj:assertj-core:3.23.1' + testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2' + testImplementation 'org.assertj:assertj-core:3.24.2' } \ No newline at end of file From fae8d71a832fa22e52305c98ec462c973a5126a5 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 18 Sep 2023 16:58:16 +0100 Subject: [PATCH 415/717] Remove Java 11 build. --- .github/workflows/gradle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index b449b38bd..ab8bc8cd1 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - java: [ '11', '17' ] + java: [ '17' ] max-parallel: 1 steps: From fb58852a1b3e614c83696e1afc069460b25e5f3f Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 19 Sep 2023 13:13:19 +0100 Subject: [PATCH 416/717] Adds a 'url' property to 'RelationshipView' (see https://github.com/structurizr/java/issues/214). --- docs/changelog.md | 1 + .../structurizr/view/RelationshipView.java | 32 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 556c54910..f049cd2f2 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -3,6 +3,7 @@ ## 1.27.0 (unreleased) - Upgrades dependencies, targets Java 17. +- Adds a 'url' property to 'RelationshipView' (see https://github.com/structurizr/java/issues/214). ## 1.26.1 (28th July 2023) diff --git a/structurizr-core/src/com/structurizr/view/RelationshipView.java b/structurizr-core/src/com/structurizr/view/RelationshipView.java index 48b992c16..56f5c87a3 100644 --- a/structurizr-core/src/com/structurizr/view/RelationshipView.java +++ b/structurizr-core/src/com/structurizr/view/RelationshipView.java @@ -3,6 +3,8 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; import com.structurizr.model.Relationship; +import com.structurizr.util.StringUtils; +import com.structurizr.util.Url; import java.util.Collection; import java.util.LinkedHashSet; @@ -20,6 +22,7 @@ public final class RelationshipView { private Relationship relationship; private String id; private String description; + private String url; private String order; private Boolean response; private Set vertices = new LinkedHashSet<>(); @@ -87,6 +90,35 @@ void setDescription(String description) { this.description = description; } + /** + * Gets the URL where more information about this relationship instance can be found. + * + * @return a URL as a String + */ + public String getUrl() { + return url; + } + + /** + * Sets the URL where more information about this relationship instance can be found. + * + * @param url the URL as a String + * @throws IllegalArgumentException if the URL is not a well-formed URL + */ + public void setUrl(String url) { + if (StringUtils.isNullOrEmpty(url)) { + this.url = null; + } else { + if (url.startsWith(Url.WORKSPACE_URL_PREFIX)) { + this.url = url; + } else if (Url.isUrl(url)) { + this.url = url; + } else { + throw new IllegalArgumentException(url + " is not a valid URL."); + } + } + } + /** * Gets the order of this relationship (used in dynamic views only; e.g. 1.0, 1.1, 2.0, etc). * From 703be9071a7ec27938143d1fa30ef9e48bdcbc1c Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 23 Oct 2023 11:26:48 +0100 Subject: [PATCH 417/717] Adds release date. --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index f049cd2f2..7c290b7f6 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 1.27.0 (unreleased) +## 1.27.0 (23rd October 2023) - Upgrades dependencies, targets Java 17. - Adds a 'url' property to 'RelationshipView' (see https://github.com/structurizr/java/issues/214). From 843b364958d43ce7af49fa6c632db4ae1e125733 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 12 Nov 2023 13:22:04 +0000 Subject: [PATCH 418/717] Adds a flag to determine whether automatic layout has been applied or not. --- build.gradle | 2 +- docs/changelog.md | 4 ++++ .../com/structurizr/view/AutomaticLayout.java | 20 +++++++++++++++++++ .../view/AutomaticLayoutTests.java | 1 + 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 5a0c3fe9d..4fd305958 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.27.0' + version = '1.27.1' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 7c290b7f6..7386bb23b 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.27.1 (unreleased) + +- Adds a flag to determine whether automatic layout has been applied or not. + ## 1.27.0 (23rd October 2023) - Upgrades dependencies, targets Java 17. diff --git a/structurizr-core/src/com/structurizr/view/AutomaticLayout.java b/structurizr-core/src/com/structurizr/view/AutomaticLayout.java index 8cf63a1c6..5b373d038 100644 --- a/structurizr-core/src/com/structurizr/view/AutomaticLayout.java +++ b/structurizr-core/src/com/structurizr/view/AutomaticLayout.java @@ -11,6 +11,7 @@ public final class AutomaticLayout { private int nodeSeparation; private int edgeSeparation; private boolean vertices; + private boolean applied; AutomaticLayout() { } @@ -22,6 +23,7 @@ public final class AutomaticLayout { setNodeSeparation(nodeSeparation); setEdgeSeparation(edgeSeparation); setVertices(vertices); + setApplied(false); } /** @@ -118,6 +120,24 @@ void setVertices(boolean vertices) { this.vertices = vertices; } + /** + * Returns whether automatic layout has been applied. + * + * @return true if automatic layout has been applied, false otherwise + */ + public boolean isApplied() { + return applied; + } + + /** + * Sets whether automatic layout has been applied. + * + * @param applied true if automatic layout has been applied, false otherwise + */ + public void setApplied(boolean applied) { + this.applied = applied; + } + public enum Implementation { Graphviz, Dagre diff --git a/structurizr-core/test/unit/com/structurizr/view/AutomaticLayoutTests.java b/structurizr-core/test/unit/com/structurizr/view/AutomaticLayoutTests.java index 3ffd208d9..c4f9c6269 100644 --- a/structurizr-core/test/unit/com/structurizr/view/AutomaticLayoutTests.java +++ b/structurizr-core/test/unit/com/structurizr/view/AutomaticLayoutTests.java @@ -15,6 +15,7 @@ void setAutomaticLayout() { assertEquals(200, automaticLayout.getNodeSeparation()); assertEquals(300, automaticLayout.getEdgeSeparation()); assertTrue(automaticLayout.isVertices()); + assertFalse(automaticLayout.isApplied()); } @Test From 7d55bbbbabcea69bbaa2bb3ff11a11a2fb1258d5 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 19 Nov 2023 10:47:59 +0000 Subject: [PATCH 419/717] Adds support for perspective values. --- build.gradle | 2 +- docs/changelog.md | 3 ++- .../src/com/structurizr/model/ModelItem.java | 19 ++++++++++++++++--- .../com/structurizr/model/Perspective.java | 18 ++++++++++++++++-- .../com/structurizr/model/ModelItemTests.java | 16 ++++++++++++---- 5 files changed, 47 insertions(+), 11 deletions(-) diff --git a/build.gradle b/build.gradle index 4fd305958..7cba07ffa 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.27.1' + version = '1.28.0' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 7386bb23b..c17704247 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,8 +1,9 @@ # Changelog -## 1.27.1 (unreleased) +## 1.28.0 (unreleased) - Adds a flag to determine whether automatic layout has been applied or not. +- Adds support for perspective values. ## 1.27.0 (23rd October 2023) diff --git a/structurizr-core/src/com/structurizr/model/ModelItem.java b/structurizr-core/src/com/structurizr/model/ModelItem.java index 4cdf2d304..ac81b3918 100644 --- a/structurizr-core/src/com/structurizr/model/ModelItem.java +++ b/structurizr-core/src/com/structurizr/model/ModelItem.java @@ -189,11 +189,24 @@ void setPerspectives(Set perspectives) { * Adds a perspective to this model item. * * @param name the name of the perspective (e.g. "Security", must be unique) - * @param description a description of the perspective + * @param description the description of the perspective * @return a Perspective object * @throws IllegalArgumentException if perspective details are not specified, or the named perspective exists already */ public Perspective addPerspective(String name, String description) { + return addPerspective(name, description, ""); + } + + /** + * Adds a perspective to this model item. + * + * @param name the name of the perspective (e.g. "Technical Debt", must be unique) + * @param description the description of the perspective (e.g. "High") + * @param value the value of the perspective + * @return a Perspective object + * @throws IllegalArgumentException if perspective details are not specified, or the named perspective exists already + */ + public Perspective addPerspective(String name, String description, String value) { if (StringUtils.isNullOrEmpty(name)) { throw new IllegalArgumentException("A name must be specified."); } @@ -202,11 +215,11 @@ public Perspective addPerspective(String name, String description) { throw new IllegalArgumentException("A description must be specified."); } - if (perspectives.stream().filter(p -> p.getName().equals(name)).count() > 0) { + if (perspectives.stream().anyMatch(p -> p.getName().equals(name))) { throw new IllegalArgumentException("A perspective named \"" + name + "\" already exists."); } - Perspective perspective = new Perspective(name, description); + Perspective perspective = new Perspective(name, description, value); perspectives.add(perspective); return perspective; diff --git a/structurizr-core/src/com/structurizr/model/Perspective.java b/structurizr-core/src/com/structurizr/model/Perspective.java index 985944674..b20285392 100644 --- a/structurizr-core/src/com/structurizr/model/Perspective.java +++ b/structurizr-core/src/com/structurizr/model/Perspective.java @@ -8,14 +8,15 @@ public final class Perspective { private String name; private String description; - // todo link this perspective to architecture decision records + private String value; Perspective() { } - Perspective(String name, String description) { + Perspective(String name, String description, String value) { this.name = name; this.description = description; + this.value = value; } /** @@ -44,6 +45,19 @@ void setDescription(String description) { this.description = description; } + /** + * Gets the value of this perspective. + * + * @return the value of this perspective, as a String + */ + public String getValue() { + return value; + } + + void setValue(String value) { + this.value = value; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/structurizr-core/test/unit/com/structurizr/model/ModelItemTests.java b/structurizr-core/test/unit/com/structurizr/model/ModelItemTests.java index 7013a0227..9cb5565f7 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ModelItemTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ModelItemTests.java @@ -200,10 +200,18 @@ void addPerspective_ThrowsAnException_WhenAnEmptyDescriptionIsSpecified() { @Test void addPerspective_AddsAPerspective() { Element element = model.addSoftwareSystem("Name", "Description"); - Perspective perspective = element.addPerspective("Security", "Data is encrypted at rest."); - assertEquals("Security", perspective.getName()); - assertEquals("Data is encrypted at rest.", perspective.getDescription()); - assertTrue(element.getPerspectives().contains(perspective)); + + Perspective securityPerspective = element.addPerspective("Security", "Data is encrypted at rest."); + assertEquals("Security", securityPerspective.getName()); + assertEquals("Data is encrypted at rest.", securityPerspective.getDescription()); + assertEquals("", securityPerspective.getValue()); + assertTrue(element.getPerspectives().contains(securityPerspective)); + + Perspective technicalDebtPerspective = element.addPerspective("Technical Debt", "High tech debt due to feature X being delivered rapidly.", "High"); + assertEquals("Technical Debt", technicalDebtPerspective.getName()); + assertEquals("High tech debt due to feature X being delivered rapidly.", technicalDebtPerspective.getDescription()); + assertEquals("High", technicalDebtPerspective.getValue()); + assertTrue(element.getPerspectives().contains(technicalDebtPerspective)); } @Test From c73ecff9983bd5995f0a02f0587019d23e31de87 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 19 Nov 2023 12:23:53 +0000 Subject: [PATCH 420/717] Adds the ability to scope and validate a workspace. --- docs/changelog.md | 1 + .../src/com/structurizr/Workspace.java | 20 ++++++ .../src/com/structurizr/WorkspaceScope.java | 8 +++ .../LandscapeWorkspaceScopeValidator.java | 26 ++++++++ ...SoftwareSystemWorkspaceScopeValidator.java | 21 +++++++ .../UndefinedWorkspaceScopeValidator.java | 12 ++++ .../WorkspaceScopeValidationException.java | 9 +++ .../validation/WorkspaceScopeValidator.java | 9 +++ .../WorkspaceScopeValidatorFactory.java | 18 ++++++ .../unit/com/structurizr/WorkspaceTests.java | 12 ++++ ...LandscapeWorkspaceScopeValidatorTests.java | 56 +++++++++++++++++ ...areSystemWorkspaceScopeValidatorTests.java | 61 +++++++++++++++++++ .../WorkspaceScopeValidatorFactoryTests.java | 23 +++++++ 13 files changed, 276 insertions(+) create mode 100644 structurizr-core/src/com/structurizr/WorkspaceScope.java create mode 100644 structurizr-core/src/com/structurizr/validation/LandscapeWorkspaceScopeValidator.java create mode 100644 structurizr-core/src/com/structurizr/validation/SoftwareSystemWorkspaceScopeValidator.java create mode 100644 structurizr-core/src/com/structurizr/validation/UndefinedWorkspaceScopeValidator.java create mode 100644 structurizr-core/src/com/structurizr/validation/WorkspaceScopeValidationException.java create mode 100644 structurizr-core/src/com/structurizr/validation/WorkspaceScopeValidator.java create mode 100644 structurizr-core/src/com/structurizr/validation/WorkspaceScopeValidatorFactory.java create mode 100644 structurizr-core/test/unit/com/structurizr/validation/LandscapeWorkspaceScopeValidatorTests.java create mode 100644 structurizr-core/test/unit/com/structurizr/validation/SoftwareSystemWorkspaceScopeValidatorTests.java create mode 100644 structurizr-core/test/unit/com/structurizr/validation/WorkspaceScopeValidatorFactoryTests.java diff --git a/docs/changelog.md b/docs/changelog.md index c17704247..ad8485f4f 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -4,6 +4,7 @@ - Adds a flag to determine whether automatic layout has been applied or not. - Adds support for perspective values. +- Adds the ability to scope and validate a workspace. ## 1.27.0 (23rd October 2023) diff --git a/structurizr-core/src/com/structurizr/Workspace.java b/structurizr-core/src/com/structurizr/Workspace.java index 3c2dc0334..e95cdd75a 100644 --- a/structurizr-core/src/com/structurizr/Workspace.java +++ b/structurizr-core/src/com/structurizr/Workspace.java @@ -23,6 +23,8 @@ public final class Workspace extends AbstractWorkspace implements Documentable { private static final Log log = LogFactory.getLog(Workspace.class); + private WorkspaceScope scope = null; + private Model model = createModel(); private ViewSet viewSet; private Documentation documentation; @@ -44,6 +46,24 @@ public Workspace(String name, String description) { documentation = new Documentation(); } + /** + * Gets the scope of this workspace + * + * @return a WorkspaceScope enum, or null if undefined + */ + public WorkspaceScope getScope() { + return scope; + } + + /** + * Sets the workspace scope. + * + * @param scope a WorkspaceScope enum, or null if undefined + */ + public void setScope(WorkspaceScope scope) { + this.scope = scope; + } + /** * Gets the software architecture model. * diff --git a/structurizr-core/src/com/structurizr/WorkspaceScope.java b/structurizr-core/src/com/structurizr/WorkspaceScope.java new file mode 100644 index 000000000..2f6ebdadc --- /dev/null +++ b/structurizr-core/src/com/structurizr/WorkspaceScope.java @@ -0,0 +1,8 @@ +package com.structurizr; + +public enum WorkspaceScope { + + Landscape, + SoftwareSystem + +} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/validation/LandscapeWorkspaceScopeValidator.java b/structurizr-core/src/com/structurizr/validation/LandscapeWorkspaceScopeValidator.java new file mode 100644 index 000000000..feb6b4abd --- /dev/null +++ b/structurizr-core/src/com/structurizr/validation/LandscapeWorkspaceScopeValidator.java @@ -0,0 +1,26 @@ +package com.structurizr.validation; + +import com.structurizr.Workspace; +import com.structurizr.model.Model; +import com.structurizr.model.SoftwareSystem; + +/** + * Validates that the workspace does not define containers and software system level documentation. + */ +public class LandscapeWorkspaceScopeValidator implements WorkspaceScopeValidator { + + @Override + public void validate(Workspace workspace) throws WorkspaceScopeValidationException { + Model model = workspace.getModel(); + for (SoftwareSystem softwareSystem : model.getSoftwareSystems()) { + if (softwareSystem.getContainers().size() > 0) { + throw new WorkspaceScopeValidationException("Workspace is landscape scoped, but the software system named " + softwareSystem.getName() + " has containers."); + } + + if (!softwareSystem.getDocumentation().isEmpty()) { + throw new WorkspaceScopeValidationException("Workspace is landscape scoped, but the software system named " + softwareSystem.getName() + " has documentation."); + } + } + } + +} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/validation/SoftwareSystemWorkspaceScopeValidator.java b/structurizr-core/src/com/structurizr/validation/SoftwareSystemWorkspaceScopeValidator.java new file mode 100644 index 000000000..d2f7ce32b --- /dev/null +++ b/structurizr-core/src/com/structurizr/validation/SoftwareSystemWorkspaceScopeValidator.java @@ -0,0 +1,21 @@ +package com.structurizr.validation; + +import com.structurizr.Workspace; +import com.structurizr.model.Model; + +/** + * Validates that the workspace only defines detail (containers and documentation) for a single software system. + */ +public class SoftwareSystemWorkspaceScopeValidator implements WorkspaceScopeValidator { + + @Override + public void validate(Workspace workspace) throws WorkspaceScopeValidationException { + Model model = workspace.getModel(); + long softwareSystemsWithContainersOrDocumentation = model.getSoftwareSystems().stream().filter(ss -> ss.getContainers().size() > 0 || !ss.getDocumentation().isEmpty()).count(); + + if (softwareSystemsWithContainersOrDocumentation > 1) { + throw new WorkspaceScopeValidationException("Workspace is software system scoped, but multiple software systems have containers and/or documentation defined."); + } + } + +} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/validation/UndefinedWorkspaceScopeValidator.java b/structurizr-core/src/com/structurizr/validation/UndefinedWorkspaceScopeValidator.java new file mode 100644 index 000000000..ffd4d3865 --- /dev/null +++ b/structurizr-core/src/com/structurizr/validation/UndefinedWorkspaceScopeValidator.java @@ -0,0 +1,12 @@ +package com.structurizr.validation; + +import com.structurizr.Workspace; + +public class UndefinedWorkspaceScopeValidator implements WorkspaceScopeValidator { + + @Override + public void validate(Workspace workspace) throws WorkspaceScopeValidationException { + // no-op + } + +} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/validation/WorkspaceScopeValidationException.java b/structurizr-core/src/com/structurizr/validation/WorkspaceScopeValidationException.java new file mode 100644 index 000000000..5183f3d3b --- /dev/null +++ b/structurizr-core/src/com/structurizr/validation/WorkspaceScopeValidationException.java @@ -0,0 +1,9 @@ +package com.structurizr.validation; + +public class WorkspaceScopeValidationException extends Exception { + + public WorkspaceScopeValidationException(String message) { + super(message); + } + +} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/validation/WorkspaceScopeValidator.java b/structurizr-core/src/com/structurizr/validation/WorkspaceScopeValidator.java new file mode 100644 index 000000000..c54361736 --- /dev/null +++ b/structurizr-core/src/com/structurizr/validation/WorkspaceScopeValidator.java @@ -0,0 +1,9 @@ +package com.structurizr.validation; + +import com.structurizr.Workspace; + +public interface WorkspaceScopeValidator { + + void validate(Workspace workspace) throws WorkspaceScopeValidationException; + +} \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/validation/WorkspaceScopeValidatorFactory.java b/structurizr-core/src/com/structurizr/validation/WorkspaceScopeValidatorFactory.java new file mode 100644 index 000000000..b01491788 --- /dev/null +++ b/structurizr-core/src/com/structurizr/validation/WorkspaceScopeValidatorFactory.java @@ -0,0 +1,18 @@ +package com.structurizr.validation; + +import com.structurizr.Workspace; +import com.structurizr.WorkspaceScope; + +public final class WorkspaceScopeValidatorFactory { + + public static WorkspaceScopeValidator getValidator(Workspace workspace) { + if (workspace.getScope() == WorkspaceScope.Landscape) { + return new LandscapeWorkspaceScopeValidator(); + } else if (workspace.getScope() == WorkspaceScope.SoftwareSystem) { + return new SoftwareSystemWorkspaceScopeValidator(); + } else { + return new UndefinedWorkspaceScopeValidator(); + } + } + +} \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/WorkspaceTests.java b/structurizr-core/test/unit/com/structurizr/WorkspaceTests.java index 651057bbe..055868a2e 100644 --- a/structurizr-core/test/unit/com/structurizr/WorkspaceTests.java +++ b/structurizr-core/test/unit/com/structurizr/WorkspaceTests.java @@ -73,4 +73,16 @@ void hydrate_DoesNotCrash() { workspace.hydrate(); } + @Test + void scope() { + Workspace workspace = new Workspace("Name", "Description"); + assertNull(workspace.getScope()); // default scope is undefined + + workspace.setScope(WorkspaceScope.SoftwareSystem); + assertEquals(WorkspaceScope.SoftwareSystem, workspace.getScope()); + + workspace.setScope(null); + assertNull(workspace.getScope()); + } + } \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/validation/LandscapeWorkspaceScopeValidatorTests.java b/structurizr-core/test/unit/com/structurizr/validation/LandscapeWorkspaceScopeValidatorTests.java new file mode 100644 index 000000000..9e473da2e --- /dev/null +++ b/structurizr-core/test/unit/com/structurizr/validation/LandscapeWorkspaceScopeValidatorTests.java @@ -0,0 +1,56 @@ +package com.structurizr.validation; + +import com.structurizr.Workspace; +import com.structurizr.documentation.Format; +import com.structurizr.documentation.Section; +import com.structurizr.validation.LandscapeWorkspaceScopeValidator; +import com.structurizr.validation.WorkspaceScopeValidationException; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +public class LandscapeWorkspaceScopeValidatorTests { + + private final LandscapeWorkspaceScopeValidator validator = new LandscapeWorkspaceScopeValidator(); + + @Test + void validate() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + validator.validate(workspace); + + workspace.getModel().addPerson("User"); + validator.validate(workspace); + + workspace.getModel().addSoftwareSystem("A"); + validator.validate(workspace); + + workspace.getModel().addSoftwareSystem("B"); + validator.validate(workspace); + } + + @Test + void validate_ThrowsAnException_WhenContainersAreDefined() { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getModel().addSoftwareSystem("A").addContainer("AA"); + try { + validator.validate(workspace); + fail(); + } catch (WorkspaceScopeValidationException e) { + assertEquals("Workspace is landscape scoped, but the software system named A has containers.", e.getMessage()); + } + } + + @Test + void validate_ThrowsAnException_WhenSoftwareSystemDocumentationIsDefined() { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getModel().addSoftwareSystem("A").getDocumentation().addSection(new Section(Format.Markdown, "## Heading 1")); + try { + validator.validate(workspace); + fail(); + } catch (WorkspaceScopeValidationException e) { + assertEquals("Workspace is landscape scoped, but the software system named A has documentation.", e.getMessage()); + } + } + +} diff --git a/structurizr-core/test/unit/com/structurizr/validation/SoftwareSystemWorkspaceScopeValidatorTests.java b/structurizr-core/test/unit/com/structurizr/validation/SoftwareSystemWorkspaceScopeValidatorTests.java new file mode 100644 index 000000000..be87ea63a --- /dev/null +++ b/structurizr-core/test/unit/com/structurizr/validation/SoftwareSystemWorkspaceScopeValidatorTests.java @@ -0,0 +1,61 @@ +package com.structurizr.validation; + +import com.structurizr.Workspace; +import com.structurizr.documentation.Format; +import com.structurizr.documentation.Section; +import com.structurizr.model.SoftwareSystem; +import com.structurizr.validation.SoftwareSystemWorkspaceScopeValidator; +import com.structurizr.validation.WorkspaceScopeValidationException; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +public class SoftwareSystemWorkspaceScopeValidatorTests { + + private final SoftwareSystemWorkspaceScopeValidator validator = new SoftwareSystemWorkspaceScopeValidator(); + + @Test + void validate() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + validator.validate(workspace); + + workspace.getModel().addPerson("User"); + validator.validate(workspace); + + SoftwareSystem a = workspace.getModel().addSoftwareSystem("A"); + a.addContainer("AA"); + a.getDocumentation().addSection(new Section(Format.Markdown, "## Heading 1")); + validator.validate(workspace); + + workspace.getModel().addSoftwareSystem("B"); + validator.validate(workspace); + } + + @Test + void validate_ThrowsAnException_WhenMultipleSoftwareSystemsDefineContainers() { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getModel().addSoftwareSystem("A").addContainer("AA"); + workspace.getModel().addSoftwareSystem("B").addContainer("BB"); + try { + validator.validate(workspace); + fail(); + } catch (WorkspaceScopeValidationException e) { + assertEquals("Workspace is software system scoped, but multiple software systems have containers and/or documentation defined.", e.getMessage()); + } + } + + @Test + void validate_ThrowsAnException_WhenMultipleSoftwareSystemsDefineDocumentation() { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getModel().addSoftwareSystem("A").getDocumentation().addSection(new Section(Format.Markdown, "## Heading 1")); + workspace.getModel().addSoftwareSystem("B").getDocumentation().addSection(new Section(Format.Markdown, "## Heading 1")); + try { + validator.validate(workspace); + fail(); + } catch (WorkspaceScopeValidationException e) { + assertEquals("Workspace is software system scoped, but multiple software systems have containers and/or documentation defined.", e.getMessage()); + } + } + +} \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/validation/WorkspaceScopeValidatorFactoryTests.java b/structurizr-core/test/unit/com/structurizr/validation/WorkspaceScopeValidatorFactoryTests.java new file mode 100644 index 000000000..a8b9e4a84 --- /dev/null +++ b/structurizr-core/test/unit/com/structurizr/validation/WorkspaceScopeValidatorFactoryTests.java @@ -0,0 +1,23 @@ +package com.structurizr.validation; + +import com.structurizr.Workspace; +import com.structurizr.WorkspaceScope; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class WorkspaceScopeValidatorFactoryTests { + + @Test + void getValidator() { + Workspace workspace = new Workspace("Name", "Description"); + assertTrue(WorkspaceScopeValidatorFactory.getValidator(workspace) instanceof UndefinedWorkspaceScopeValidator); + + workspace.setScope(WorkspaceScope.Landscape); + assertTrue(WorkspaceScopeValidatorFactory.getValidator(workspace) instanceof LandscapeWorkspaceScopeValidator); + + workspace.setScope(WorkspaceScope.SoftwareSystem); + assertTrue(WorkspaceScopeValidatorFactory.getValidator(workspace) instanceof SoftwareSystemWorkspaceScopeValidator); + } + +} \ No newline at end of file From f976dac160914ccda7f7ae1fd9e4c580522e931b Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 19 Nov 2023 12:36:11 +0000 Subject: [PATCH 421/717] Move scope to workspace configuration. --- .../src/com/structurizr/Workspace.java | 20 ------------------ .../configuration/WorkspaceConfiguration.java | 21 +++++++++++++++++-- .../{ => configuration}/WorkspaceScope.java | 2 +- .../WorkspaceScopeValidatorFactory.java | 6 +++--- .../unit/com/structurizr/WorkspaceTests.java | 12 ----------- .../WorkspaceConfigurationTests.java | 12 +++++++++++ .../WorkspaceScopeValidatorFactoryTests.java | 6 +++--- 7 files changed, 38 insertions(+), 41 deletions(-) rename structurizr-core/src/com/structurizr/{ => configuration}/WorkspaceScope.java (63%) diff --git a/structurizr-core/src/com/structurizr/Workspace.java b/structurizr-core/src/com/structurizr/Workspace.java index e95cdd75a..3c2dc0334 100644 --- a/structurizr-core/src/com/structurizr/Workspace.java +++ b/structurizr-core/src/com/structurizr/Workspace.java @@ -23,8 +23,6 @@ public final class Workspace extends AbstractWorkspace implements Documentable { private static final Log log = LogFactory.getLog(Workspace.class); - private WorkspaceScope scope = null; - private Model model = createModel(); private ViewSet viewSet; private Documentation documentation; @@ -46,24 +44,6 @@ public Workspace(String name, String description) { documentation = new Documentation(); } - /** - * Gets the scope of this workspace - * - * @return a WorkspaceScope enum, or null if undefined - */ - public WorkspaceScope getScope() { - return scope; - } - - /** - * Sets the workspace scope. - * - * @param scope a WorkspaceScope enum, or null if undefined - */ - public void setScope(WorkspaceScope scope) { - this.scope = scope; - } - /** * Gets the software architecture model. * diff --git a/structurizr-core/src/com/structurizr/configuration/WorkspaceConfiguration.java b/structurizr-core/src/com/structurizr/configuration/WorkspaceConfiguration.java index d937b3c33..bd19777a4 100644 --- a/structurizr-core/src/com/structurizr/configuration/WorkspaceConfiguration.java +++ b/structurizr-core/src/com/structurizr/configuration/WorkspaceConfiguration.java @@ -1,7 +1,5 @@ package com.structurizr.configuration; -import com.structurizr.util.StringUtils; - import java.util.HashSet; import java.util.Set; @@ -10,12 +8,31 @@ */ public final class WorkspaceConfiguration { + private WorkspaceScope scope = null; private Visibility visibility = null; private Set users = new HashSet<>(); WorkspaceConfiguration() { } + /** + * Gets the scope of this workspace + * + * @return a WorkspaceScope enum, or null if undefined + */ + public WorkspaceScope getScope() { + return scope; + } + + /** + * Sets the workspace scope. + * + * @param scope a WorkspaceScope enum, or null if undefined + */ + public void setScope(WorkspaceScope scope) { + this.scope = scope; + } + /** * Gets the visibility of this workspace (private or public). * diff --git a/structurizr-core/src/com/structurizr/WorkspaceScope.java b/structurizr-core/src/com/structurizr/configuration/WorkspaceScope.java similarity index 63% rename from structurizr-core/src/com/structurizr/WorkspaceScope.java rename to structurizr-core/src/com/structurizr/configuration/WorkspaceScope.java index 2f6ebdadc..8d271c504 100644 --- a/structurizr-core/src/com/structurizr/WorkspaceScope.java +++ b/structurizr-core/src/com/structurizr/configuration/WorkspaceScope.java @@ -1,4 +1,4 @@ -package com.structurizr; +package com.structurizr.configuration; public enum WorkspaceScope { diff --git a/structurizr-core/src/com/structurizr/validation/WorkspaceScopeValidatorFactory.java b/structurizr-core/src/com/structurizr/validation/WorkspaceScopeValidatorFactory.java index b01491788..ce011e601 100644 --- a/structurizr-core/src/com/structurizr/validation/WorkspaceScopeValidatorFactory.java +++ b/structurizr-core/src/com/structurizr/validation/WorkspaceScopeValidatorFactory.java @@ -1,14 +1,14 @@ package com.structurizr.validation; import com.structurizr.Workspace; -import com.structurizr.WorkspaceScope; +import com.structurizr.configuration.WorkspaceScope; public final class WorkspaceScopeValidatorFactory { public static WorkspaceScopeValidator getValidator(Workspace workspace) { - if (workspace.getScope() == WorkspaceScope.Landscape) { + if (workspace.getConfiguration().getScope() == WorkspaceScope.Landscape) { return new LandscapeWorkspaceScopeValidator(); - } else if (workspace.getScope() == WorkspaceScope.SoftwareSystem) { + } else if (workspace.getConfiguration().getScope() == WorkspaceScope.SoftwareSystem) { return new SoftwareSystemWorkspaceScopeValidator(); } else { return new UndefinedWorkspaceScopeValidator(); diff --git a/structurizr-core/test/unit/com/structurizr/WorkspaceTests.java b/structurizr-core/test/unit/com/structurizr/WorkspaceTests.java index 055868a2e..651057bbe 100644 --- a/structurizr-core/test/unit/com/structurizr/WorkspaceTests.java +++ b/structurizr-core/test/unit/com/structurizr/WorkspaceTests.java @@ -73,16 +73,4 @@ void hydrate_DoesNotCrash() { workspace.hydrate(); } - @Test - void scope() { - Workspace workspace = new Workspace("Name", "Description"); - assertNull(workspace.getScope()); // default scope is undefined - - workspace.setScope(WorkspaceScope.SoftwareSystem); - assertEquals(WorkspaceScope.SoftwareSystem, workspace.getScope()); - - workspace.setScope(null); - assertNull(workspace.getScope()); - } - } \ No newline at end of file diff --git a/structurizr-core/test/unit/com/structurizr/configuration/WorkspaceConfigurationTests.java b/structurizr-core/test/unit/com/structurizr/configuration/WorkspaceConfigurationTests.java index c4a2f99f2..bdc982e9a 100644 --- a/structurizr-core/test/unit/com/structurizr/configuration/WorkspaceConfigurationTests.java +++ b/structurizr-core/test/unit/com/structurizr/configuration/WorkspaceConfigurationTests.java @@ -55,6 +55,18 @@ void addUser_AddsAUser() { assertEquals(Role.ReadWrite, user.getRole()); } + @Test + void scope() { + WorkspaceConfiguration configuration = new WorkspaceConfiguration(); + assertNull(configuration.getScope()); // default scope is undefined + + configuration.setScope(WorkspaceScope.SoftwareSystem); + assertEquals(WorkspaceScope.SoftwareSystem, configuration.getScope()); + + configuration.setScope(null); + assertNull(configuration.getScope()); + } + @Test void visibility() { WorkspaceConfiguration configuration = new WorkspaceConfiguration(); diff --git a/structurizr-core/test/unit/com/structurizr/validation/WorkspaceScopeValidatorFactoryTests.java b/structurizr-core/test/unit/com/structurizr/validation/WorkspaceScopeValidatorFactoryTests.java index a8b9e4a84..dd79b0b7e 100644 --- a/structurizr-core/test/unit/com/structurizr/validation/WorkspaceScopeValidatorFactoryTests.java +++ b/structurizr-core/test/unit/com/structurizr/validation/WorkspaceScopeValidatorFactoryTests.java @@ -1,7 +1,7 @@ package com.structurizr.validation; import com.structurizr.Workspace; -import com.structurizr.WorkspaceScope; +import com.structurizr.configuration.WorkspaceScope; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -13,10 +13,10 @@ void getValidator() { Workspace workspace = new Workspace("Name", "Description"); assertTrue(WorkspaceScopeValidatorFactory.getValidator(workspace) instanceof UndefinedWorkspaceScopeValidator); - workspace.setScope(WorkspaceScope.Landscape); + workspace.getConfiguration().setScope(WorkspaceScope.Landscape); assertTrue(WorkspaceScopeValidatorFactory.getValidator(workspace) instanceof LandscapeWorkspaceScopeValidator); - workspace.setScope(WorkspaceScope.SoftwareSystem); + workspace.getConfiguration().setScope(WorkspaceScope.SoftwareSystem); assertTrue(WorkspaceScopeValidatorFactory.getValidator(workspace) instanceof SoftwareSystemWorkspaceScopeValidator); } From 6dad755767ef24b463bdc2d06a76ce3d0e7d705a Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 19 Nov 2023 12:36:23 +0000 Subject: [PATCH 422/717] Bump dependencies. --- structurizr-client/build.gradle | 2 +- structurizr-core/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/structurizr-client/build.gradle b/structurizr-client/build.gradle index 300d750ed..def7a58e6 100644 --- a/structurizr-client/build.gradle +++ b/structurizr-client/build.gradle @@ -2,7 +2,7 @@ dependencies { api project(':structurizr-core') - api 'com.fasterxml.jackson.core:jackson-databind:2.15.1' + api 'com.fasterxml.jackson.core:jackson-databind:2.16.0' api 'org.apache.httpcomponents.client5:httpclient5:5.2.1' api 'javax.xml.bind:jaxb-api:2.4.0-b180830.0359' diff --git a/structurizr-core/build.gradle b/structurizr-core/build.gradle index 3ffe4688a..dae5405a4 100644 --- a/structurizr-core/build.gradle +++ b/structurizr-core/build.gradle @@ -1,6 +1,6 @@ dependencies { - api 'com.fasterxml.jackson.core:jackson-annotations:2.15.1' + api 'com.fasterxml.jackson.core:jackson-annotations:2.16.0' api 'com.google.code.findbugs:jsr305:3.0.2' api 'commons-logging:commons-logging:1.2' From a8a4e43954602c58ac0dd6db297d5e5b2d7a85d2 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 19 Nov 2023 12:52:56 +0000 Subject: [PATCH 423/717] Updated to reflect release. --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index ad8485f4f..43bd669e9 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 1.28.0 (unreleased) +## 1.28.0 (19th November 2023) - Adds a flag to determine whether automatic layout has been applied or not. - Adds support for perspective values. From c99d42ed1fc504fe181fdddc2039a35e71ed3aed Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 11 Dec 2023 09:20:14 +0000 Subject: [PATCH 424/717] `AbstractWorkspace.clearConfiguration()` creates a new instance rather than nulling it. --- build.gradle | 2 +- docs/changelog.md | 4 ++++ structurizr-core/src/com/structurizr/AbstractWorkspace.java | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 7cba07ffa..4adaad5f2 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.28.0' + version = '1.28.1' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 43bd669e9..23b40bc47 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.28.1 (11th December 2023) + +- `AbstractWorkspace.clearConfiguration()` creates a new instance rather than nulling it. + ## 1.28.0 (19th November 2023) - Adds a flag to determine whether automatic layout has been applied or not. diff --git a/structurizr-core/src/com/structurizr/AbstractWorkspace.java b/structurizr-core/src/com/structurizr/AbstractWorkspace.java index c78f9c0fd..82f750ace 100644 --- a/structurizr-core/src/com/structurizr/AbstractWorkspace.java +++ b/structurizr-core/src/com/structurizr/AbstractWorkspace.java @@ -228,7 +228,7 @@ private WorkspaceConfiguration createWorkspaceConfiguration() { * Clears the configuration associated with this workspace. */ public void clearConfiguration() { - this.configuration = null; + this.configuration = createWorkspaceConfiguration(); } /** From 8a7da17d58e82881ded40901ae9080363b0fe4ad Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 11 Dec 2023 09:22:55 +0000 Subject: [PATCH 425/717] Fix test. --- .../com/structurizr/encryption/EncryptedWorkspaceTests.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/structurizr-client/test/unit/com/structurizr/encryption/EncryptedWorkspaceTests.java b/structurizr-client/test/unit/com/structurizr/encryption/EncryptedWorkspaceTests.java index a7e508781..f52794996 100644 --- a/structurizr-client/test/unit/com/structurizr/encryption/EncryptedWorkspaceTests.java +++ b/structurizr-client/test/unit/com/structurizr/encryption/EncryptedWorkspaceTests.java @@ -39,7 +39,8 @@ void construction_WhenTwoParametersAreSpecified() throws Exception { assertEquals("structurizr-java", encryptedWorkspace.getLastModifiedAgent()); assertEquals(1234, encryptedWorkspace.getId()); assertEquals("user@domain.com", encryptedWorkspace.getConfiguration().getUsers().iterator().next().getUsername()); - assertNull(workspace.getConfiguration()); + assertNotNull(workspace.getConfiguration()); + assertTrue(workspace.getConfiguration().getUsers().isEmpty()); assertSame(workspace, encryptedWorkspace.getWorkspace()); assertSame(encryptionStrategy, encryptedWorkspace.getEncryptionStrategy()); From 1a90ec8420229030a27efba668d2c623499e5cf1 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 22 Dec 2023 13:34:34 +0000 Subject: [PATCH 426/717] Adds `com.structurizr.api.AdminApiClient` as a client for the cloud service/on-premises admin APIs. --- build.gradle | 2 +- docs/changelog.md | 4 + settings.gradle | 2 +- .../structurizr/api/AbstractApiClient.java | 54 ++ .../com/structurizr/api/AdminApiClient.java | 156 ++++++ .../structurizr/api/StructurizrClient.java | 505 +----------------- .../structurizr/api/WorkspaceApiClient.java | 457 ++++++++++++++++ .../structurizr/api/WorkspaceMetadata.java | 82 +++ .../src/com/structurizr/api/Workspaces.java | 28 + 9 files changed, 788 insertions(+), 502 deletions(-) create mode 100644 structurizr-client/src/com/structurizr/api/AbstractApiClient.java create mode 100644 structurizr-client/src/com/structurizr/api/AdminApiClient.java create mode 100644 structurizr-client/src/com/structurizr/api/WorkspaceApiClient.java create mode 100644 structurizr-client/src/com/structurizr/api/WorkspaceMetadata.java create mode 100644 structurizr-client/src/com/structurizr/api/Workspaces.java diff --git a/build.gradle b/build.gradle index 4adaad5f2..b1052523f 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.28.1' + version = '1.29.0' repositories { mavenCentral() diff --git a/docs/changelog.md b/docs/changelog.md index 23b40bc47..7e2e1a926 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.29.0 (unreleased) + +- Adds `com.structurizr.api.AdminApiClient` as a client for the cloud service/on-premises admin APIs. + ## 1.28.1 (11th December 2023) - `AbstractWorkspace.clearConfiguration()` creates a new instance rather than nulling it. diff --git a/settings.gradle b/settings.gradle index 8872b5782..62816092a 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,4 +1,4 @@ -rootProject.name = 'structurizr' +rootProject.name = 'structurizr-java' include 'structurizr-client' include 'structurizr-core' \ No newline at end of file diff --git a/structurizr-client/src/com/structurizr/api/AbstractApiClient.java b/structurizr-client/src/com/structurizr/api/AbstractApiClient.java new file mode 100644 index 000000000..df6086c1b --- /dev/null +++ b/structurizr-client/src/com/structurizr/api/AbstractApiClient.java @@ -0,0 +1,54 @@ +package com.structurizr.api; + +import com.structurizr.util.StringUtils; + +public abstract class AbstractApiClient { + + protected static final String VERSION = Package.getPackage("com.structurizr.api").getImplementationVersion(); + protected static final String STRUCTURIZR_FOR_JAVA_AGENT = "structurizr-java/" + VERSION; + + protected static final String STRUCTURIZR_CLOUD_SERVICE_API_URL = "https://api.structurizr.com"; + protected static final String WORKSPACE_PATH = "/workspace"; + + protected String url; + protected String agent = STRUCTURIZR_FOR_JAVA_AGENT; + + String getUrl() { + return url; + } + + protected void setUrl(String url) { + if (url == null || url.trim().length() == 0) { + throw new IllegalArgumentException("The API URL must not be null or empty."); + } + + if (url.endsWith("/")) { + this.url = url.substring(0, url.length() - 1); + } else { + this.url = url; + } + } + + /** + * Gets the agent string used to identify this client instance. + * + * @return "structurizr-java/{version}", unless overridden + */ + public String getAgent() { + return agent; + } + + /** + * Sets the agent string used to identify this client instance. + * + * @param agent the agent string + */ + public void setAgent(String agent) { + if (StringUtils.isNullOrEmpty(agent)) { + throw new IllegalArgumentException("An agent must be provided."); + } + + this.agent = agent.trim(); + } + +} \ No newline at end of file diff --git a/structurizr-client/src/com/structurizr/api/AdminApiClient.java b/structurizr-client/src/com/structurizr/api/AdminApiClient.java new file mode 100644 index 000000000..cb84ff595 --- /dev/null +++ b/structurizr-client/src/com/structurizr/api/AdminApiClient.java @@ -0,0 +1,156 @@ +package com.structurizr.api; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.structurizr.util.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hc.core5.http.HttpStatus; + +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.util.List; + +/** + * A client for the Structurizr Admin API. + */ +public class AdminApiClient extends AbstractApiClient { + + private static final Log log = LogFactory.getLog(AdminApiClient.class); + + private final String username; + private final String apiKey; + + /** + * Creates a new API client with the specified on-premises API URL and key. + * + * @param url the URL of your Structurizr instance + * @param username the username (only required for the Structurizr cloud service) + * @param apiKey the API key of your workspace + */ + public AdminApiClient(String url, String username, String apiKey) { + setUrl(url); + + this.username = username; + + if (apiKey == null || apiKey.trim().length() == 0) { + throw new IllegalArgumentException("The API key must not be null or empty."); + } + + this.apiKey = apiKey; + } + + /** + * Gets a list of all workspaces. + * + * @return a List of WorkspaceMetadata objects + * @throws StructurizrClientException if an error occurs + */ + public List getWorkspaces() throws StructurizrClientException { + try { + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(url + WORKSPACE_PATH)) + .header(HttpHeaders.AUTHORIZATION, createAuthorizationHeader()) + .header(HttpHeaders.USER_AGENT, agent) + .build(); + HttpClient client = HttpClient.newHttpClient(); + + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + String json = response.body(); + + if (response.statusCode() == HttpStatus.SC_OK) { + Workspaces workspaces = objectMapper.readValue(response.body(), Workspaces.class); + return workspaces.getWorkspaces(); + } else { + ApiResponse apiResponse = ApiResponse.parse(json); + throw new StructurizrClientException(apiResponse.getMessage()); + } + } catch (Exception e) { + log.error(e); + throw new StructurizrClientException(e); + } + } + + /** + * Creates a new workspace. + * + * @return a WorkspaceMetadata object representing the new workspace + * @throws StructurizrClientException if an error occurs + */ + public WorkspaceMetadata createWorkspace() throws StructurizrClientException { + try { + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(url + WORKSPACE_PATH)) + .POST(HttpRequest.BodyPublishers.noBody()) + .header(HttpHeaders.AUTHORIZATION, createAuthorizationHeader()) + .header(HttpHeaders.USER_AGENT, agent) + .build(); + HttpClient client = HttpClient.newHttpClient(); + + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + String json = response.body(); + + if (response.statusCode() == HttpStatus.SC_OK) { + return objectMapper.readValue(json, WorkspaceMetadata.class); + } else { + ApiResponse apiResponse = ApiResponse.parse(json); + throw new StructurizrClientException(apiResponse.getMessage()); + } + } catch (Exception e) { + log.error(e); + throw new StructurizrClientException(e); + } + } + + /** + * Deletes a workspace. + * + * @param workspaceId the ID of the workspace to delete + * @throws StructurizrClientException if an error occurs + */ + public void deleteWorkspace(int workspaceId) throws StructurizrClientException { + try { + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(url + WORKSPACE_PATH + "/" + workspaceId)) + .DELETE() + .header(HttpHeaders.AUTHORIZATION, createAuthorizationHeader()) + .header(HttpHeaders.USER_AGENT, agent) + .build(); + HttpClient client = HttpClient.newHttpClient(); + + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + String json = response.body(); + + if (response.statusCode() == HttpStatus.SC_OK) { + ApiResponse apiResponse = ApiResponse.parse(json); + log.debug(apiResponse.getMessage()); + } else { + ApiResponse apiResponse = ApiResponse.parse(json); + throw new StructurizrClientException(apiResponse.getMessage()); + } + } catch (Exception e) { + log.error(e); + throw new StructurizrClientException(e); + } + } + + private String createAuthorizationHeader() { + if (StringUtils.isNullOrEmpty(username)) { + return apiKey; + } else { + return username + ":" + apiKey; + } + } + +} \ No newline at end of file diff --git a/structurizr-client/src/com/structurizr/api/StructurizrClient.java b/structurizr-client/src/com/structurizr/api/StructurizrClient.java index 1368170b6..b7ce1fcd9 100644 --- a/structurizr-client/src/com/structurizr/api/StructurizrClient.java +++ b/structurizr-client/src/com/structurizr/api/StructurizrClient.java @@ -1,515 +1,20 @@ package com.structurizr.api; -import com.structurizr.Workspace; -import com.structurizr.encryption.EncryptedWorkspace; -import com.structurizr.encryption.EncryptionLocation; -import com.structurizr.encryption.EncryptionStrategy; -import com.structurizr.io.json.EncryptedJsonReader; -import com.structurizr.io.json.EncryptedJsonWriter; -import com.structurizr.io.json.JsonReader; -import com.structurizr.io.json.JsonWriter; -import com.structurizr.model.IdGenerator; -import com.structurizr.util.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.hc.client5.http.classic.methods.HttpDelete; -import org.apache.hc.client5.http.classic.methods.HttpGet; -import org.apache.hc.client5.http.classic.methods.HttpPut; -import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase; -import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; -import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; -import org.apache.hc.client5.http.impl.classic.HttpClients; -import org.apache.hc.core5.http.ContentType; -import org.apache.hc.core5.http.Header; -import org.apache.hc.core5.http.HttpStatus; -import org.apache.hc.core5.http.io.entity.EntityUtils; -import org.apache.hc.core5.http.io.entity.StringEntity; - -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.io.InputStream; -import java.io.StringReader; -import java.io.StringWriter; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.text.SimpleDateFormat; -import java.util.Base64; -import java.util.Date; -import java.util.Properties; - /** - * A client for the Structurizr API (https://api.structurizr.com) - * that allows you to get and put Structurizr workspaces in a JSON format. + * A client for the Structurizr workspace API that allows you to get and put Structurizr workspaces in a JSON format. */ -public final class StructurizrClient { - - private static final Log log = LogFactory.getLog(StructurizrClient.class); - - private static final String VERSION = Package.getPackage("com.structurizr.api").getImplementationVersion(); - private static final String STRUCTURIZR_FOR_JAVA_AGENT = "structurizr-java/" + VERSION; - - private static final String STRUCTURIZR_CLOUD_API_URL = "https://api.structurizr.com"; - - private static final String STRUCTURIZR_API_URL = "structurizr.api.url"; - private static final String STRUCTURIZR_API_KEY = "structurizr.api.key"; - private static final String STRUCTURIZR_API_SECRET = "structurizr.api.secret"; +public class StructurizrClient extends WorkspaceApiClient { - private static final String WORKSPACE_PATH = "/workspace/"; - - private String agent = STRUCTURIZR_FOR_JAVA_AGENT; - private String user; - - private String url; - private String apiKey; - private String apiSecret; - - private EncryptionStrategy encryptionStrategy; - - private IdGenerator idGenerator = null; - private boolean mergeFromRemote = true; - private File workspaceArchiveLocation = new File("."); - - /** - * Creates a new Structurizr client based upon configuration in a structurizr.properties file - * on the classpath with the following name-value pairs: - * - structurizr.api.url - * - structurizr.api.key - * - structurizr.api.secret - * - * @throws StructurizrClientException if something goes wrong - */ public StructurizrClient() throws StructurizrClientException { - try (InputStream in = - StructurizrClient.class.getClassLoader().getResourceAsStream("structurizr.properties")) { - Properties properties = new Properties(); - if (in != null) { - properties.load(in); - - setUrl(properties.getProperty(STRUCTURIZR_API_URL)); - setApiKey(properties.getProperty(STRUCTURIZR_API_KEY)); - setApiSecret(properties.getProperty(STRUCTURIZR_API_SECRET)); - } else { - throw new StructurizrClientException("Could not find a structurizr.properties file on the classpath."); - } - } catch (IOException e) { - log.error(e); - throw new StructurizrClientException(e); - } + super(); } - /** - * Creates a new Structurizr API client with the specified API key and secret, - * for the default API URL (https://api.structurizr.com). - * - * @param apiKey the API key of your workspace - * @param apiSecret the API secret of your workspace - */ public StructurizrClient(String apiKey, String apiSecret) { - this(STRUCTURIZR_CLOUD_API_URL, apiKey, apiSecret); + super(apiKey, apiSecret); } - /** - * Creates a new Structurizr client with the specified API URL, key and secret. - * - * @param url the URL of your Structurizr instance - * @param apiKey the API key of your workspace - * @param apiSecret the API secret of your workspace - */ public StructurizrClient(String url, String apiKey, String apiSecret) { - setUrl(url); - setApiKey(apiKey); - setApiSecret(apiSecret); - } - - /** - * Sets the ID generator to use when parsing a JSON workspace definition. - * - * @param idGenerator an IdGenerator implementation - */ - public void setIdGenerator(IdGenerator idGenerator) { - this.idGenerator = idGenerator; - } - - /** - * Gets the agent string used to identify this client instance. - * - * @return "structurizr-java/{version}", unless overridden - */ - public String getAgent() { - return agent; - } - - /** - * Sets the agent string used to identify this client instance. - * - * @param agent the agent string - */ - public void setAgent(String agent) { - if (StringUtils.isNullOrEmpty(agent)) { - throw new IllegalArgumentException("An agent must be provided."); - } - - this.agent = agent.trim(); - } - - /** - * Gets the API URL that this client is for. - * - * @return the API URL, as a String - */ - public String getUrl() { - return this.url; - } - - private void setUrl(String url) { - if (url == null || url.trim().length() == 0) { - throw new IllegalArgumentException("The API URL must not be null or empty."); - } - - if (url.endsWith("/")) { - this.url = url.substring(0, url.length() - 1); - } else { - this.url = url; - } - } - - String getApiKey() { - return apiKey; - } - - private void setApiKey(String apiKey) { - if (apiKey == null || apiKey.trim().length() == 0) { - throw new IllegalArgumentException("The API key must not be null or empty."); - } - - this.apiKey = apiKey; - } - - String getApiSecret() { - return apiSecret; - } - - private void setApiSecret(String apiSecret) { - if (apiSecret == null || apiSecret.trim().length() == 0) { - throw new IllegalArgumentException("The API secret must not be null or empty."); - } - - this.apiSecret = apiSecret; - } - - /** - * Gets the location where a copy of the workspace is archived when it is retrieved from the server. - * - * @return a File instance representing a directory, or null if this client instance is not archiving - */ - public File getWorkspaceArchiveLocation() { - return this.workspaceArchiveLocation; - } - - /** - * Sets the location where a copy of the workspace will be archived whenever it is retrieved from - * the server. Set this to null if you don't want archiving. - * - * @param workspaceArchiveLocation a File instance representing a directory, or null if - * you don't want archiving - */ - public void setWorkspaceArchiveLocation(File workspaceArchiveLocation) { - this.workspaceArchiveLocation = workspaceArchiveLocation; - } - - /** - * Sets the encryption strategy for use when getting or putting workspaces. - * - * @param encryptionStrategy an EncryptionStrategy implementation - */ - public void setEncryptionStrategy(EncryptionStrategy encryptionStrategy) { - this.encryptionStrategy = encryptionStrategy; - } - - /** - * Specifies whether the layout of diagrams from a remote workspace should be retained when putting - * a new version of the workspace. - * - * @param mergeFromRemote true if layout information should be merged from the remote workspace, false otherwise - */ - public void setMergeFromRemote(boolean mergeFromRemote) { - this.mergeFromRemote = mergeFromRemote; - } - - /** - * Locks the workspace with the given ID. - * - * @param workspaceId the ID of your workspace - * @return true if the workspace could be locked, false otherwise - * @throws StructurizrClientException if there are problems related to the network, authorization, etc - */ - public boolean lockWorkspace(long workspaceId) throws StructurizrClientException { - return manageLockForWorkspace(workspaceId, true); - } - - /** - * Unlocks the workspace with the given ID. - * - * @param workspaceId the ID of your workspace - * @return true if the workspace could be unlocked, false otherwise - * @throws StructurizrClientException if there are problems related to the network, authorization, etc - */ - public boolean unlockWorkspace(long workspaceId) throws StructurizrClientException { - return manageLockForWorkspace(workspaceId, false); - } - - private boolean manageLockForWorkspace(long workspaceId, boolean lock) throws StructurizrClientException { - if (workspaceId <= 0) { - throw new IllegalArgumentException("The workspace ID must be a positive integer."); - } - - try (CloseableHttpClient httpClient = HttpClients.createSystem()) { - HttpUriRequestBase httpRequest; - - if (lock) { - log.info("Locking workspace with ID " + workspaceId); - httpRequest = new HttpPut(url + WORKSPACE_PATH + workspaceId + "/lock?user=" + getUser() + "&agent=" + agent); - } else { - log.info("Unlocking workspace with ID " + workspaceId); - httpRequest = new HttpDelete(url + WORKSPACE_PATH + workspaceId + "/lock?user=" + getUser() + "&agent=" + agent); - } - - addHeaders(httpRequest, "", ""); - debugRequest(httpRequest, null); - - try (CloseableHttpResponse response = httpClient.execute(httpRequest)) { - debugResponse(response); - - String responseText = EntityUtils.toString(response.getEntity()); - ApiResponse apiResponse = ApiResponse.parse(responseText); - log.info(responseText); - - if (response.getCode() == HttpStatus.SC_OK) { - return apiResponse.isSuccess(); - } else { - throw new StructurizrClientException(apiResponse.getMessage()); - } - } - } catch (Exception e) { - log.error(e); - throw new StructurizrClientException(e); - } - } - - /** - * Gets the workspace with the given ID. - * - * @param workspaceId the workspace ID - * @return a Workspace instance - * @throws StructurizrClientException if there are problems related to the network, authorization, JSON deserialization, etc - */ - public Workspace getWorkspace(long workspaceId) throws StructurizrClientException { - if (workspaceId <= 0) { - throw new IllegalArgumentException("The workspace ID must be a positive integer."); - } - - try (CloseableHttpClient httpClient = HttpClients.createSystem()) { - log.info("Getting workspace with ID " + workspaceId); - HttpGet httpGet = new HttpGet(url + WORKSPACE_PATH + workspaceId); - addHeaders(httpGet, "", ""); - debugRequest(httpGet, null); - - try (CloseableHttpResponse response = httpClient.execute(httpGet)) { - debugResponse(response); - - String json = EntityUtils.toString(response.getEntity()); - if (response.getCode() == HttpStatus.SC_OK) { - archiveWorkspace(workspaceId, json); - - if (encryptionStrategy == null) { - if (json.contains("\"encryptionStrategy\"") && json.contains("\"ciphertext\"")) { - log.warn("The JSON may contain a client-side encrypted workspace, but no passphrase has been specified."); - } - - JsonReader jsonReader = new JsonReader(); - jsonReader.setIdGenerator(idGenerator); - return jsonReader.read(new StringReader(json)); - } else { - EncryptedWorkspace encryptedWorkspace = new EncryptedJsonReader().read(new StringReader(json)); - - if (encryptedWorkspace.getEncryptionStrategy() != null) { - encryptedWorkspace.getEncryptionStrategy().setPassphrase(encryptionStrategy.getPassphrase()); - return encryptedWorkspace.getWorkspace(); - } else { - // this workspace isn't encrypted, even though the client has an encryption strategy set - JsonReader jsonReader = new JsonReader(); - jsonReader.setIdGenerator(idGenerator); - return jsonReader.read(new StringReader(json)); - } - } - } else { - ApiResponse apiResponse = ApiResponse.parse(json); - throw new StructurizrClientException(apiResponse.getMessage()); - } - } - } catch (Exception e) { - log.error(e); - throw new StructurizrClientException(e); - } - } - - /** - * Updates the given workspace. - * - * @param workspaceId the workspace ID - * @param workspace the workspace instance to update - * @throws StructurizrClientException if there are problems related to the network, authorization, JSON serialization, etc - */ - public void putWorkspace(long workspaceId, Workspace workspace) throws StructurizrClientException { - if (workspace == null) { - throw new IllegalArgumentException("The workspace must not be null."); - } else if (workspaceId <= 0) { - throw new IllegalArgumentException("The workspace ID must be a positive integer."); - } - - try (CloseableHttpClient httpClient = HttpClients.createSystem()) { - if (mergeFromRemote) { - Workspace remoteWorkspace = getWorkspace(workspaceId); - if (remoteWorkspace != null) { - workspace.getViews().copyLayoutInformationFrom(remoteWorkspace.getViews()); - workspace.getViews().getConfiguration().copyConfigurationFrom(remoteWorkspace.getViews().getConfiguration()); - } - } - - workspace.setId(workspaceId); - workspace.setThumbnail(null); - workspace.setLastModifiedDate(new Date()); - workspace.setLastModifiedAgent(agent); - workspace.setLastModifiedUser(getUser()); - - workspace.countAndLogWarnings(); - - HttpPut httpPut = new HttpPut(url + WORKSPACE_PATH + workspaceId); - - StringWriter stringWriter = new StringWriter(); - if (encryptionStrategy == null) { - JsonWriter jsonWriter = new JsonWriter(false); - jsonWriter.write(workspace, stringWriter); - } else { - EncryptedWorkspace encryptedWorkspace = new EncryptedWorkspace(workspace, encryptionStrategy); - encryptionStrategy.setLocation(EncryptionLocation.Client); - EncryptedJsonWriter jsonWriter = new EncryptedJsonWriter(false); - jsonWriter.write(encryptedWorkspace, stringWriter); - } - - StringEntity stringEntity = new StringEntity(stringWriter.toString(), ContentType.APPLICATION_JSON); - httpPut.setEntity(stringEntity); - addHeaders(httpPut, EntityUtils.toString(stringEntity), ContentType.APPLICATION_JSON.toString()); - - debugRequest(httpPut, EntityUtils.toString(stringEntity)); - - log.info("Putting workspace with ID " + workspaceId); - try (CloseableHttpResponse response = httpClient.execute(httpPut)) { - String json = EntityUtils.toString(response.getEntity()); - if (response.getCode() == HttpStatus.SC_OK) { - debugResponse(response); - log.info(json); - } else { - ApiResponse apiResponse = ApiResponse.parse(json); - throw new StructurizrClientException(apiResponse.getMessage()); - } - } - } catch (Exception e) { - log.error(e); - throw new StructurizrClientException(e); - } - } - - private void debugRequest(HttpUriRequestBase httpRequest, String content) { - if (log.isDebugEnabled()) { - log.debug(httpRequest.getMethod() + " " + httpRequest.getPath()); - Header[] headers = httpRequest.getHeaders(); - for (Header header : headers) { - log.debug(header.getName() + ": " + header.getValue()); - } - if (content != null) { - log.debug(content); - } - } - } - - private void debugResponse(CloseableHttpResponse response) { - log.debug(response.getCode()); - } - - private void addHeaders(HttpUriRequestBase httpRequest, String content, String contentType) throws Exception { - String httpMethod = httpRequest.getMethod(); - String path = httpRequest.getPath(); - String contentMd5 = new Md5Digest().generate(content); - String nonce = "" + System.currentTimeMillis(); - - HashBasedMessageAuthenticationCode hmac = new HashBasedMessageAuthenticationCode(apiSecret); - HmacContent hmacContent = new HmacContent(httpMethod, path, contentMd5, contentType, nonce); - httpRequest.addHeader(HttpHeaders.USER_AGENT, agent); - httpRequest.addHeader(HttpHeaders.AUTHORIZATION, new HmacAuthorizationHeader(apiKey, hmac.generate(hmacContent.toString())).format()); - httpRequest.addHeader(HttpHeaders.NONCE, nonce); - - if (httpMethod.equals("PUT")) { - httpRequest.addHeader(HttpHeaders.CONTENT_MD5, Base64.getEncoder().encodeToString(contentMd5.getBytes("UTF-8"))); - httpRequest.addHeader(HttpHeaders.CONTENT_TYPE, contentType); - } - } - - private void archiveWorkspace(long workspaceId, String json) { - if (this.workspaceArchiveLocation == null) { - return; - } - - File archiveFile = new File(workspaceArchiveLocation, createArchiveFileName(workspaceId)); - try (FileWriter fileWriter = new FileWriter(archiveFile)) { - fileWriter.write(json); - fileWriter.flush(); - - debugArchivedWorkspaceLocation(archiveFile); - } catch (Exception e) { - log.warn("Could not archive JSON to " + archiveFile.getAbsolutePath()); - } - } - - private void debugArchivedWorkspaceLocation(File archiveFile) { - if (log.isDebugEnabled()) { - try { - log.debug("Workspace from server archived to " + archiveFile.getCanonicalPath()); - } catch (IOException ioe) { - log.debug("Workspace from server archived to " + archiveFile.getAbsolutePath()); - } - } - } - - private String createArchiveFileName(long workspaceId) { - SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss"); - return "structurizr-" + workspaceId + "-" + sdf.format(new Date()) + ".json"; - } - - public void setUser(String user) { - this.user = user; - } - - private String getUser() { - if (!StringUtils.isNullOrEmpty(user)) { - return user; - } else { - String username = System.getProperty("user.name"); - - if (username.contains("@")) { - return username; - } else { - String hostname = null; - try { - hostname = InetAddress.getLocalHost().getHostName(); - } catch (UnknownHostException uhe) { - // ignore - } - - return username + (!StringUtils.isNullOrEmpty(hostname) ? "@" + hostname : ""); - } - } + super(url, apiKey, apiSecret); } } \ No newline at end of file diff --git a/structurizr-client/src/com/structurizr/api/WorkspaceApiClient.java b/structurizr-client/src/com/structurizr/api/WorkspaceApiClient.java new file mode 100644 index 000000000..668a62fd5 --- /dev/null +++ b/structurizr-client/src/com/structurizr/api/WorkspaceApiClient.java @@ -0,0 +1,457 @@ +package com.structurizr.api; + +import com.structurizr.Workspace; +import com.structurizr.encryption.EncryptedWorkspace; +import com.structurizr.encryption.EncryptionLocation; +import com.structurizr.encryption.EncryptionStrategy; +import com.structurizr.io.json.EncryptedJsonReader; +import com.structurizr.io.json.EncryptedJsonWriter; +import com.structurizr.io.json.JsonReader; +import com.structurizr.io.json.JsonWriter; +import com.structurizr.model.IdGenerator; +import com.structurizr.util.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hc.client5.http.classic.methods.HttpDelete; +import org.apache.hc.client5.http.classic.methods.HttpGet; +import org.apache.hc.client5.http.classic.methods.HttpPut; +import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.core5.http.ContentType; +import org.apache.hc.core5.http.Header; +import org.apache.hc.core5.http.HttpStatus; +import org.apache.hc.core5.http.io.entity.EntityUtils; +import org.apache.hc.core5.http.io.entity.StringEntity; + +import java.io.*; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.nio.charset.StandardCharsets; +import java.text.SimpleDateFormat; +import java.util.Base64; +import java.util.Date; +import java.util.Properties; + +/** + * A client for the Structurizr workspace API that allows you to get and put Structurizr workspaces in a JSON format. + */ +public class WorkspaceApiClient extends AbstractApiClient { + + private static final Log log = LogFactory.getLog(WorkspaceApiClient.class); + + private static final String STRUCTURIZR_API_URL = "structurizr.api.url"; + private static final String STRUCTURIZR_API_KEY = "structurizr.api.key"; + private static final String STRUCTURIZR_API_SECRET = "structurizr.api.secret"; + + private String user; + + private String apiKey; + private String apiSecret; + + private EncryptionStrategy encryptionStrategy; + + private IdGenerator idGenerator = null; + private boolean mergeFromRemote = true; + private File workspaceArchiveLocation = new File("."); + + /** + * Creates a new Structurizr client based upon configuration in a structurizr.properties file + * on the classpath with the following name-value pairs: + * - structurizr.api.url + * - structurizr.api.key + * - structurizr.api.secret + * + * @throws StructurizrClientException if something goes wrong + */ + public WorkspaceApiClient() throws StructurizrClientException { + try (InputStream in = + WorkspaceApiClient.class.getClassLoader().getResourceAsStream("structurizr.properties")) { + Properties properties = new Properties(); + if (in != null) { + properties.load(in); + + setUrl(properties.getProperty(STRUCTURIZR_API_URL)); + setApiKey(properties.getProperty(STRUCTURIZR_API_KEY)); + setApiSecret(properties.getProperty(STRUCTURIZR_API_SECRET)); + } else { + throw new StructurizrClientException("Could not find a structurizr.properties file on the classpath."); + } + } catch (IOException e) { + log.error(e); + throw new StructurizrClientException(e); + } + } + + /** + * Creates a new Structurizr API client with the specified API key and secret, for the Structurizr cloud service. + * + * @param apiKey the API key of your workspace + * @param apiSecret the API secret of your workspace + */ + public WorkspaceApiClient(String apiKey, String apiSecret) { + this(STRUCTURIZR_CLOUD_SERVICE_API_URL, apiKey, apiSecret); + } + + /** + * Creates a new Structurizr client with the specified API URL, key and secret. + * + * @param url the URL of your Structurizr instance + * @param apiKey the API key of your workspace + * @param apiSecret the API secret of your workspace + */ + public WorkspaceApiClient(String url, String apiKey, String apiSecret) { + setUrl(url); + setApiKey(apiKey); + setApiSecret(apiSecret); + } + + /** + * Sets the ID generator to use when parsing a JSON workspace definition. + * + * @param idGenerator an IdGenerator implementation + */ + public void setIdGenerator(IdGenerator idGenerator) { + this.idGenerator = idGenerator; + } + + String getApiKey() { + return apiKey; + } + + private void setApiKey(String apiKey) { + if (apiKey == null || apiKey.trim().length() == 0) { + throw new IllegalArgumentException("The API key must not be null or empty."); + } + + this.apiKey = apiKey; + } + + String getApiSecret() { + return apiSecret; + } + + private void setApiSecret(String apiSecret) { + if (apiSecret == null || apiSecret.trim().length() == 0) { + throw new IllegalArgumentException("The API secret must not be null or empty."); + } + + this.apiSecret = apiSecret; + } + + /** + * Gets the location where a copy of the workspace is archived when it is retrieved from the server. + * + * @return a File instance representing a directory, or null if this client instance is not archiving + */ + public File getWorkspaceArchiveLocation() { + return this.workspaceArchiveLocation; + } + + /** + * Sets the location where a copy of the workspace will be archived whenever it is retrieved from + * the server. Set this to null if you don't want archiving. + * + * @param workspaceArchiveLocation a File instance representing a directory, or null if + * you don't want archiving + */ + public void setWorkspaceArchiveLocation(File workspaceArchiveLocation) { + this.workspaceArchiveLocation = workspaceArchiveLocation; + } + + /** + * Sets the encryption strategy for use when getting or putting workspaces. + * + * @param encryptionStrategy an EncryptionStrategy implementation + */ + public void setEncryptionStrategy(EncryptionStrategy encryptionStrategy) { + this.encryptionStrategy = encryptionStrategy; + } + + /** + * Specifies whether the layout of diagrams from a remote workspace should be retained when putting + * a new version of the workspace. + * + * @param mergeFromRemote true if layout information should be merged from the remote workspace, false otherwise + */ + public void setMergeFromRemote(boolean mergeFromRemote) { + this.mergeFromRemote = mergeFromRemote; + } + + /** + * Locks the workspace with the given ID. + * + * @param workspaceId the ID of your workspace + * @return true if the workspace could be locked, false otherwise + * @throws StructurizrClientException if there are problems related to the network, authorization, etc + */ + public boolean lockWorkspace(long workspaceId) throws StructurizrClientException { + return manageLockForWorkspace(workspaceId, true); + } + + /** + * Unlocks the workspace with the given ID. + * + * @param workspaceId the ID of your workspace + * @return true if the workspace could be unlocked, false otherwise + * @throws StructurizrClientException if there are problems related to the network, authorization, etc + */ + public boolean unlockWorkspace(long workspaceId) throws StructurizrClientException { + return manageLockForWorkspace(workspaceId, false); + } + + private boolean manageLockForWorkspace(long workspaceId, boolean lock) throws StructurizrClientException { + if (workspaceId <= 0) { + throw new IllegalArgumentException("The workspace ID must be a positive integer."); + } + + try (CloseableHttpClient httpClient = HttpClients.createSystem()) { + HttpUriRequestBase httpRequest; + + if (lock) { + log.info("Locking workspace with ID " + workspaceId); + httpRequest = new HttpPut(url + WORKSPACE_PATH + "/" + workspaceId + "/lock?user=" + getUser() + "&agent=" + agent); + } else { + log.info("Unlocking workspace with ID " + workspaceId); + httpRequest = new HttpDelete(url + WORKSPACE_PATH + "/" + workspaceId + "/lock?user=" + getUser() + "&agent=" + agent); + } + + addHeaders(httpRequest, "", ""); + debugRequest(httpRequest, null); + + try (CloseableHttpResponse response = httpClient.execute(httpRequest)) { + debugResponse(response); + + String responseText = EntityUtils.toString(response.getEntity()); + ApiResponse apiResponse = ApiResponse.parse(responseText); + log.info(responseText); + + if (response.getCode() == HttpStatus.SC_OK) { + return apiResponse.isSuccess(); + } else { + throw new StructurizrClientException(apiResponse.getMessage()); + } + } + } catch (Exception e) { + log.error(e); + throw new StructurizrClientException(e); + } + } + + /** + * Gets the workspace with the given ID. + * + * @param workspaceId the workspace ID + * @return a Workspace instance + * @throws StructurizrClientException if there are problems related to the network, authorization, JSON deserialization, etc + */ + public Workspace getWorkspace(long workspaceId) throws StructurizrClientException { + if (workspaceId <= 0) { + throw new IllegalArgumentException("The workspace ID must be a positive integer."); + } + + try (CloseableHttpClient httpClient = HttpClients.createSystem()) { + log.info("Getting workspace with ID " + workspaceId); + HttpGet httpGet = new HttpGet(url + WORKSPACE_PATH + "/" + workspaceId); + addHeaders(httpGet, "", ""); + debugRequest(httpGet, null); + + try (CloseableHttpResponse response = httpClient.execute(httpGet)) { + debugResponse(response); + + String json = EntityUtils.toString(response.getEntity()); + if (response.getCode() == HttpStatus.SC_OK) { + archiveWorkspace(workspaceId, json); + + if (encryptionStrategy == null) { + if (json.contains("\"encryptionStrategy\"") && json.contains("\"ciphertext\"")) { + log.warn("The JSON may contain a client-side encrypted workspace, but no passphrase has been specified."); + } + + JsonReader jsonReader = new JsonReader(); + jsonReader.setIdGenerator(idGenerator); + return jsonReader.read(new StringReader(json)); + } else { + EncryptedWorkspace encryptedWorkspace = new EncryptedJsonReader().read(new StringReader(json)); + + if (encryptedWorkspace.getEncryptionStrategy() != null) { + encryptedWorkspace.getEncryptionStrategy().setPassphrase(encryptionStrategy.getPassphrase()); + return encryptedWorkspace.getWorkspace(); + } else { + // this workspace isn't encrypted, even though the client has an encryption strategy set + JsonReader jsonReader = new JsonReader(); + jsonReader.setIdGenerator(idGenerator); + return jsonReader.read(new StringReader(json)); + } + } + } else { + ApiResponse apiResponse = ApiResponse.parse(json); + throw new StructurizrClientException(apiResponse.getMessage()); + } + } + } catch (Exception e) { + log.error(e); + throw new StructurizrClientException(e); + } + } + + /** + * Updates the given workspace. + * + * @param workspaceId the workspace ID + * @param workspace the workspace instance to update + * @throws StructurizrClientException if there are problems related to the network, authorization, JSON serialization, etc + */ + public void putWorkspace(long workspaceId, Workspace workspace) throws StructurizrClientException { + if (workspace == null) { + throw new IllegalArgumentException("The workspace must not be null."); + } else if (workspaceId <= 0) { + throw new IllegalArgumentException("The workspace ID must be a positive integer."); + } + + try (CloseableHttpClient httpClient = HttpClients.createSystem()) { + if (mergeFromRemote) { + Workspace remoteWorkspace = getWorkspace(workspaceId); + if (remoteWorkspace != null) { + workspace.getViews().copyLayoutInformationFrom(remoteWorkspace.getViews()); + workspace.getViews().getConfiguration().copyConfigurationFrom(remoteWorkspace.getViews().getConfiguration()); + } + } + + workspace.setId(workspaceId); + workspace.setThumbnail(null); + workspace.setLastModifiedDate(new Date()); + workspace.setLastModifiedAgent(agent); + workspace.setLastModifiedUser(getUser()); + + workspace.countAndLogWarnings(); + + HttpPut httpPut = new HttpPut(url + WORKSPACE_PATH + "/" + workspaceId); + + StringWriter stringWriter = new StringWriter(); + if (encryptionStrategy == null) { + JsonWriter jsonWriter = new JsonWriter(false); + jsonWriter.write(workspace, stringWriter); + } else { + EncryptedWorkspace encryptedWorkspace = new EncryptedWorkspace(workspace, encryptionStrategy); + encryptionStrategy.setLocation(EncryptionLocation.Client); + EncryptedJsonWriter jsonWriter = new EncryptedJsonWriter(false); + jsonWriter.write(encryptedWorkspace, stringWriter); + } + + StringEntity stringEntity = new StringEntity(stringWriter.toString(), ContentType.APPLICATION_JSON); + httpPut.setEntity(stringEntity); + addHeaders(httpPut, EntityUtils.toString(stringEntity), ContentType.APPLICATION_JSON.toString()); + + debugRequest(httpPut, EntityUtils.toString(stringEntity)); + + log.info("Putting workspace with ID " + workspaceId); + try (CloseableHttpResponse response = httpClient.execute(httpPut)) { + String json = EntityUtils.toString(response.getEntity()); + if (response.getCode() == HttpStatus.SC_OK) { + debugResponse(response); + log.info(json); + } else { + ApiResponse apiResponse = ApiResponse.parse(json); + throw new StructurizrClientException(apiResponse.getMessage()); + } + } + } catch (Exception e) { + log.error(e); + throw new StructurizrClientException(e); + } + } + + private void debugRequest(HttpUriRequestBase httpRequest, String content) { + if (log.isDebugEnabled()) { + log.debug(httpRequest.getMethod() + " " + httpRequest.getPath()); + Header[] headers = httpRequest.getHeaders(); + for (Header header : headers) { + log.debug(header.getName() + ": " + header.getValue()); + } + if (content != null) { + log.debug(content); + } + } + } + + private void debugResponse(CloseableHttpResponse response) { + log.debug(response.getCode()); + } + + private void addHeaders(HttpUriRequestBase httpRequest, String content, String contentType) throws Exception { + String httpMethod = httpRequest.getMethod(); + String path = httpRequest.getPath(); + String contentMd5 = new Md5Digest().generate(content); + String nonce = "" + System.currentTimeMillis(); + + HashBasedMessageAuthenticationCode hmac = new HashBasedMessageAuthenticationCode(apiSecret); + HmacContent hmacContent = new HmacContent(httpMethod, path, contentMd5, contentType, nonce); + httpRequest.addHeader(HttpHeaders.USER_AGENT, agent); + httpRequest.addHeader(HttpHeaders.AUTHORIZATION, new HmacAuthorizationHeader(apiKey, hmac.generate(hmacContent.toString())).format()); + httpRequest.addHeader(HttpHeaders.NONCE, nonce); + + if (httpMethod.equals("PUT")) { + httpRequest.addHeader(HttpHeaders.CONTENT_MD5, Base64.getEncoder().encodeToString(contentMd5.getBytes(StandardCharsets.UTF_8))); + httpRequest.addHeader(HttpHeaders.CONTENT_TYPE, contentType); + } + } + + private void archiveWorkspace(long workspaceId, String json) { + if (this.workspaceArchiveLocation == null) { + return; + } + + File archiveFile = new File(workspaceArchiveLocation, createArchiveFileName(workspaceId)); + try (FileWriter fileWriter = new FileWriter(archiveFile)) { + fileWriter.write(json); + fileWriter.flush(); + + debugArchivedWorkspaceLocation(archiveFile); + } catch (Exception e) { + log.warn("Could not archive JSON to " + archiveFile.getAbsolutePath()); + } + } + + private void debugArchivedWorkspaceLocation(File archiveFile) { + if (log.isDebugEnabled()) { + try { + log.debug("Workspace from server archived to " + archiveFile.getCanonicalPath()); + } catch (IOException ioe) { + log.debug("Workspace from server archived to " + archiveFile.getAbsolutePath()); + } + } + } + + private String createArchiveFileName(long workspaceId) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss"); + return "structurizr-" + workspaceId + "-" + sdf.format(new Date()) + ".json"; + } + + public void setUser(String user) { + this.user = user; + } + + private String getUser() { + if (!StringUtils.isNullOrEmpty(user)) { + return user; + } else { + String username = System.getProperty("user.name"); + + if (username.contains("@")) { + return username; + } else { + String hostname = null; + try { + hostname = InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException uhe) { + // ignore + } + + return username + (!StringUtils.isNullOrEmpty(hostname) ? "@" + hostname : ""); + } + } + } + +} \ No newline at end of file diff --git a/structurizr-client/src/com/structurizr/api/WorkspaceMetadata.java b/structurizr-client/src/com/structurizr/api/WorkspaceMetadata.java new file mode 100644 index 000000000..81df2367d --- /dev/null +++ b/structurizr-client/src/com/structurizr/api/WorkspaceMetadata.java @@ -0,0 +1,82 @@ +package com.structurizr.api; + +public class WorkspaceMetadata { + + private int id; + private String name; + private String description; + private String apiKey; + private String apiSecret; + + private String privateUrl; + private String publicUrl; + private String shareableUrl; + + WorkspaceMetadata() { + } + + public int getId() { + return id; + } + + void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + void setDescription(String description) { + this.description = description; + } + + public String getApiKey() { + return apiKey; + } + + void setApiKey(String apiKey) { + this.apiKey = apiKey; + } + + public String getApiSecret() { + return apiSecret; + } + + void setApiSecret(String apiSecret) { + this.apiSecret = apiSecret; + } + + public String getPrivateUrl() { + return privateUrl; + } + + void setPrivateUrl(String privateUrl) { + this.privateUrl = privateUrl; + } + + public String getPublicUrl() { + return publicUrl; + } + + void setPublicUrl(String publicUrl) { + this.publicUrl = publicUrl; + } + + public String getShareableUrl() { + return shareableUrl; + } + + void setShareableUrl(String shareableUrl) { + this.shareableUrl = shareableUrl; + } + +} \ No newline at end of file diff --git a/structurizr-client/src/com/structurizr/api/Workspaces.java b/structurizr-client/src/com/structurizr/api/Workspaces.java new file mode 100644 index 000000000..bf578ec89 --- /dev/null +++ b/structurizr-client/src/com/structurizr/api/Workspaces.java @@ -0,0 +1,28 @@ +package com.structurizr.api; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +class Workspaces { + + private List workspaces; + + Workspaces() { + } + + List getWorkspaces() { + return new ArrayList<>(workspaces); + } + + void setWorkspaces(List workspaces) { + if (workspaces == null) { + this.workspaces = new ArrayList<>(); + } else { + this.workspaces = workspaces; + } + + this.workspaces.sort(Comparator.comparingInt(WorkspaceMetadata::getId)); + } + +} \ No newline at end of file From b3eeea86c7a75204524e141970b2cd1c3274afc6 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 22 Dec 2023 13:35:13 +0000 Subject: [PATCH 427/717] Typo. --- structurizr-core/src/com/structurizr/view/ImageView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structurizr-core/src/com/structurizr/view/ImageView.java b/structurizr-core/src/com/structurizr/view/ImageView.java index 861ce76c9..b67845a32 100644 --- a/structurizr-core/src/com/structurizr/view/ImageView.java +++ b/structurizr-core/src/com/structurizr/view/ImageView.java @@ -77,7 +77,7 @@ public void setContent(String content) { } /** - * Gets the the content type of this view (e.g. "image/png"). + * Gets the content type of this view (e.g. "image/png"). * * @return the content type, as a String */ From 33797b6978c7a335b5461698f2c840f330fcbd63 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 22 Dec 2023 13:45:36 +0000 Subject: [PATCH 428/717] . --- structurizr-client/src/com/structurizr/api/AdminApiClient.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structurizr-client/src/com/structurizr/api/AdminApiClient.java b/structurizr-client/src/com/structurizr/api/AdminApiClient.java index cb84ff595..55a9c79db 100644 --- a/structurizr-client/src/com/structurizr/api/AdminApiClient.java +++ b/structurizr-client/src/com/structurizr/api/AdminApiClient.java @@ -24,7 +24,7 @@ public class AdminApiClient extends AbstractApiClient { private final String apiKey; /** - * Creates a new API client with the specified on-premises API URL and key. + * Creates a new admin API client. * * @param url the URL of your Structurizr instance * @param username the username (only required for the Structurizr cloud service) From 0ccffba53b87917a5697836241a38f22a7cb3fec Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 28 Dec 2023 12:23:04 +0000 Subject: [PATCH 429/717] Adds support for inter-workspace URLs of the form `{workspace:123456}/diagrams`. --- docs/changelog.md | 1 + .../src/com/structurizr/model/ModelItem.java | 4 +++- .../src/com/structurizr/util/Url.java | 3 ++- .../com/structurizr/model/ModelItemTests.java | 16 +++++++++++++++- 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 7e2e1a926..e02d80837 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -3,6 +3,7 @@ ## 1.29.0 (unreleased) - Adds `com.structurizr.api.AdminApiClient` as a client for the cloud service/on-premises admin APIs. +- Adds support for inter-workspace URLs of the form `{workspace:123456}/diagrams`. ## 1.28.1 (11th December 2023) diff --git a/structurizr-core/src/com/structurizr/model/ModelItem.java b/structurizr-core/src/com/structurizr/model/ModelItem.java index ac81b3918..2be654cd6 100644 --- a/structurizr-core/src/com/structurizr/model/ModelItem.java +++ b/structurizr-core/src/com/structurizr/model/ModelItem.java @@ -123,7 +123,9 @@ public void setUrl(String url) { if (StringUtils.isNullOrEmpty(url)) { this.url = null; } else { - if (url.startsWith(Url.WORKSPACE_URL_PREFIX)) { + if (url.startsWith(Url.INTRA_WORKSPACE_URL_PREFIX)) { + this.url = url; + } else if (url.matches(Url.INTER_WORKSPACE_URL_REGEX)) { this.url = url; } else if (Url.isUrl(url)) { this.url = url; diff --git a/structurizr-core/src/com/structurizr/util/Url.java b/structurizr-core/src/com/structurizr/util/Url.java index 49e69ab87..adf0989a4 100644 --- a/structurizr-core/src/com/structurizr/util/Url.java +++ b/structurizr-core/src/com/structurizr/util/Url.java @@ -8,7 +8,8 @@ */ public class Url { - public static final String WORKSPACE_URL_PREFIX = "{workspace}"; + public static final String INTRA_WORKSPACE_URL_PREFIX = "{workspace}"; + public static final String INTER_WORKSPACE_URL_REGEX = "\\{workspace:\\d+\\}.*"; /** * Determines whether the supplied string is a valid URL. diff --git a/structurizr-core/test/unit/com/structurizr/model/ModelItemTests.java b/structurizr-core/test/unit/com/structurizr/model/ModelItemTests.java index 9cb5565f7..783a1c548 100644 --- a/structurizr-core/test/unit/com/structurizr/model/ModelItemTests.java +++ b/structurizr-core/test/unit/com/structurizr/model/ModelItemTests.java @@ -234,10 +234,24 @@ void setUrl_AcceptsAUrl() { } @Test - void setUrl_AcceptsAWorkspaceUrl() { + void setUrl_AcceptsAnIntraWorkspaceUrl() { Element element = model.addSoftwareSystem("Name"); element.setUrl("{workspace}/diagrams#key"); assertEquals("{workspace}/diagrams#key", element.getUrl()); } + @Test + void setUrl_AcceptsAnInterWorkspaceUrl() { + Element element = model.addSoftwareSystem("Name"); + + element.setUrl("{workspace:123456}"); + assertEquals("{workspace:123456}", element.getUrl()); + + element.setUrl("{workspace:123456}/diagrams#key"); + assertEquals("{workspace:123456}/diagrams#key", element.getUrl()); + + element.setUrl("{workspace:123456}/documentation"); + assertEquals("{workspace:123456}/documentation", element.getUrl()); + } + } \ No newline at end of file From 5e7793ad571b271df1536264ef742e11f50c18ac Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 28 Dec 2023 12:27:07 +0000 Subject: [PATCH 430/717] . --- .../src/com/structurizr/view/RelationshipView.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/structurizr-core/src/com/structurizr/view/RelationshipView.java b/structurizr-core/src/com/structurizr/view/RelationshipView.java index 56f5c87a3..e9d01b480 100644 --- a/structurizr-core/src/com/structurizr/view/RelationshipView.java +++ b/structurizr-core/src/com/structurizr/view/RelationshipView.java @@ -109,7 +109,9 @@ public void setUrl(String url) { if (StringUtils.isNullOrEmpty(url)) { this.url = null; } else { - if (url.startsWith(Url.WORKSPACE_URL_PREFIX)) { + if (url.startsWith(Url.INTRA_WORKSPACE_URL_PREFIX)) { + this.url = url; + } else if (url.matches(Url.INTER_WORKSPACE_URL_REGEX)) { this.url = url; } else if (Url.isUrl(url)) { this.url = url; From 0d352c18430887214e5036be40d0bc5f805c2076 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 28 Dec 2023 12:27:34 +0000 Subject: [PATCH 431/717] . --- structurizr-core/src/com/structurizr/view/ModelView.java | 1 + 1 file changed, 1 insertion(+) diff --git a/structurizr-core/src/com/structurizr/view/ModelView.java b/structurizr-core/src/com/structurizr/view/ModelView.java index b10a556f7..c80ddb72a 100644 --- a/structurizr-core/src/com/structurizr/view/ModelView.java +++ b/structurizr-core/src/com/structurizr/view/ModelView.java @@ -15,6 +15,7 @@ /** * The superclass for all views that show elements/relationships from the model, namely: * + * - Custom views * - System landscape views * - System context views * - Container views From aa74fb0d38745d86513ac5a1edb40b3a13882470 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 28 Dec 2023 14:55:02 +0000 Subject: [PATCH 432/717] Deprecates the original StructurizrClient class and properties file configuration. --- .../structurizr/api/StructurizrClient.java | 36 ++++++++++++++++++- .../structurizr/api/WorkspaceApiClient.java | 35 ++---------------- 2 files changed, 38 insertions(+), 33 deletions(-) diff --git a/structurizr-client/src/com/structurizr/api/StructurizrClient.java b/structurizr-client/src/com/structurizr/api/StructurizrClient.java index b7ce1fcd9..422bad04c 100644 --- a/structurizr-client/src/com/structurizr/api/StructurizrClient.java +++ b/structurizr-client/src/com/structurizr/api/StructurizrClient.java @@ -1,12 +1,46 @@ package com.structurizr.api; +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + /** * A client for the Structurizr workspace API that allows you to get and put Structurizr workspaces in a JSON format. + * + * @deprecated Use WorkspaceApiClient instead */ +@Deprecated public class StructurizrClient extends WorkspaceApiClient { + private static final String STRUCTURIZR_API_URL = "structurizr.api.url"; + private static final String STRUCTURIZR_API_KEY = "structurizr.api.key"; + private static final String STRUCTURIZR_API_SECRET = "structurizr.api.secret"; + + /** + * Creates a new Structurizr client based upon configuration in a structurizr.properties file + * on the classpath with the following name-value pairs: + * - structurizr.api.url + * - structurizr.api.key + * - structurizr.api.secret + * + * @throws StructurizrClientException if something goes wrong + */ public StructurizrClient() throws StructurizrClientException { - super(); + try (InputStream in = + WorkspaceApiClient.class.getClassLoader().getResourceAsStream("structurizr.properties")) { + Properties properties = new Properties(); + if (in != null) { + properties.load(in); + + setUrl(properties.getProperty(STRUCTURIZR_API_URL)); + setApiKey(properties.getProperty(STRUCTURIZR_API_KEY)); + setApiSecret(properties.getProperty(STRUCTURIZR_API_SECRET)); + } else { + throw new StructurizrClientException("Could not find a structurizr.properties file on the classpath."); + } + } catch (IOException e) { + throw new StructurizrClientException(e); + } } public StructurizrClient(String apiKey, String apiSecret) { diff --git a/structurizr-client/src/com/structurizr/api/WorkspaceApiClient.java b/structurizr-client/src/com/structurizr/api/WorkspaceApiClient.java index 668a62fd5..29bbb8332 100644 --- a/structurizr-client/src/com/structurizr/api/WorkspaceApiClient.java +++ b/structurizr-client/src/com/structurizr/api/WorkspaceApiClient.java @@ -41,10 +41,6 @@ public class WorkspaceApiClient extends AbstractApiClient { private static final Log log = LogFactory.getLog(WorkspaceApiClient.class); - private static final String STRUCTURIZR_API_URL = "structurizr.api.url"; - private static final String STRUCTURIZR_API_KEY = "structurizr.api.key"; - private static final String STRUCTURIZR_API_SECRET = "structurizr.api.secret"; - private String user; private String apiKey; @@ -56,32 +52,7 @@ public class WorkspaceApiClient extends AbstractApiClient { private boolean mergeFromRemote = true; private File workspaceArchiveLocation = new File("."); - /** - * Creates a new Structurizr client based upon configuration in a structurizr.properties file - * on the classpath with the following name-value pairs: - * - structurizr.api.url - * - structurizr.api.key - * - structurizr.api.secret - * - * @throws StructurizrClientException if something goes wrong - */ - public WorkspaceApiClient() throws StructurizrClientException { - try (InputStream in = - WorkspaceApiClient.class.getClassLoader().getResourceAsStream("structurizr.properties")) { - Properties properties = new Properties(); - if (in != null) { - properties.load(in); - - setUrl(properties.getProperty(STRUCTURIZR_API_URL)); - setApiKey(properties.getProperty(STRUCTURIZR_API_KEY)); - setApiSecret(properties.getProperty(STRUCTURIZR_API_SECRET)); - } else { - throw new StructurizrClientException("Could not find a structurizr.properties file on the classpath."); - } - } catch (IOException e) { - log.error(e); - throw new StructurizrClientException(e); - } + protected WorkspaceApiClient() { } /** @@ -120,7 +91,7 @@ String getApiKey() { return apiKey; } - private void setApiKey(String apiKey) { + protected void setApiKey(String apiKey) { if (apiKey == null || apiKey.trim().length() == 0) { throw new IllegalArgumentException("The API key must not be null or empty."); } @@ -132,7 +103,7 @@ String getApiSecret() { return apiSecret; } - private void setApiSecret(String apiSecret) { + protected void setApiSecret(String apiSecret) { if (apiSecret == null || apiSecret.trim().length() == 0) { throw new IllegalArgumentException("The API secret must not be null or empty."); } From c7ba5ab2eb127bb2ae812693f35acc02e7b550f2 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 28 Dec 2023 15:22:41 +0000 Subject: [PATCH 433/717] Move docs. --- README.md | 62 +------------ docs/api-client.md | 95 -------------------- docs/binaries.md | 7 -- docs/building.md | 9 -- docs/client-side-encryption.md | 23 ----- docs/component-diagram.md | 17 ---- docs/container-diagram.md | 15 ---- docs/deployment-diagram.md | 11 --- docs/dynamic-diagram.md | 19 ---- docs/faq.md | 19 ---- docs/filtered-views.md | 39 -------- docs/getting-started.md | 94 ------------------- docs/images/filtered-views-1.png | Bin 165819 -> 0 bytes docs/images/filtered-views-2.png | Bin 187584 -> 0 bytes docs/images/getting-started-1.png | Bin 130986 -> 0 bytes docs/images/getting-started-2.png | Bin 133350 -> 0 bytes docs/images/getting-started-diagram-key.png | Bin 42766 -> 0 bytes docs/images/implied-relationships-1.png | Bin 54035 -> 0 bytes docs/images/structurizr-banner.png | Bin 53460 -> 0 bytes docs/images/structurizr-logo.png | Bin 10206 -> 0 bytes docs/images/structurizr-overview.png | Bin 330672 -> 0 bytes docs/images/styling-elements-1.png | Bin 171437 -> 0 bytes docs/images/styling-elements-2.png | Bin 175074 -> 0 bytes docs/images/styling-elements-3.png | Bin 175296 -> 0 bytes docs/images/styling-elements-4.png | Bin 193247 -> 0 bytes docs/images/styling-elements-5.png | Bin 382973 -> 0 bytes docs/images/styling-elements-6.png | Bin 75001 -> 0 bytes docs/images/styling-relationships-1.png | Bin 171403 -> 0 bytes docs/images/styling-relationships-2.png | Bin 170530 -> 0 bytes docs/images/styling-relationships-3.png | Bin 170369 -> 0 bytes docs/images/styling-relationships-4.png | Bin 90321 -> 0 bytes docs/implied-relationships.md | 93 ------------------- docs/model.md | 20 ----- docs/styling-elements.md | 78 ---------------- docs/styling-relationships.md | 48 ---------- docs/system-context-diagram.md | 13 --- docs/system-landscape-diagram.md | 13 --- docs/views.md | 29 ------ 38 files changed, 4 insertions(+), 700 deletions(-) delete mode 100644 docs/api-client.md delete mode 100644 docs/binaries.md delete mode 100644 docs/building.md delete mode 100644 docs/client-side-encryption.md delete mode 100644 docs/component-diagram.md delete mode 100644 docs/container-diagram.md delete mode 100644 docs/deployment-diagram.md delete mode 100644 docs/dynamic-diagram.md delete mode 100644 docs/faq.md delete mode 100644 docs/filtered-views.md delete mode 100644 docs/getting-started.md delete mode 100644 docs/images/filtered-views-1.png delete mode 100644 docs/images/filtered-views-2.png delete mode 100644 docs/images/getting-started-1.png delete mode 100644 docs/images/getting-started-2.png delete mode 100644 docs/images/getting-started-diagram-key.png delete mode 100644 docs/images/implied-relationships-1.png delete mode 100644 docs/images/structurizr-banner.png delete mode 100644 docs/images/structurizr-logo.png delete mode 100644 docs/images/structurizr-overview.png delete mode 100644 docs/images/styling-elements-1.png delete mode 100644 docs/images/styling-elements-2.png delete mode 100644 docs/images/styling-elements-3.png delete mode 100644 docs/images/styling-elements-4.png delete mode 100644 docs/images/styling-elements-5.png delete mode 100644 docs/images/styling-elements-6.png delete mode 100644 docs/images/styling-relationships-1.png delete mode 100644 docs/images/styling-relationships-2.png delete mode 100644 docs/images/styling-relationships-3.png delete mode 100644 docs/images/styling-relationships-4.png delete mode 100644 docs/implied-relationships.md delete mode 100644 docs/model.md delete mode 100644 docs/styling-elements.md delete mode 100644 docs/styling-relationships.md delete mode 100644 docs/system-context-diagram.md delete mode 100644 docs/system-landscape-diagram.md delete mode 100644 docs/views.md diff --git a/README.md b/README.md index b6069bca3..9b45a95ef 100644 --- a/README.md +++ b/README.md @@ -1,63 +1,9 @@ -![Structurizr](docs/images/structurizr-banner.png) - # Structurizr for Java This GitHub repository is (1) a client library for the [Structurizr](https://structurizr.com) cloud service and on-premises installation -and (2) a way to create a Structurizr workspace using Java code. Looking for the [Structurizr DSL](https://github.com/structurizr/dsl) instead? - -## A quick example - -As an example, the following Java code can be used to create a software architecture __model__ and an associated __view__ that describes a user using a software system, based upon the [C4 model](https://c4model.com). - -```java -public static void main(String[] args) throws Exception { - Workspace workspace = new Workspace("Getting Started", "This is a model of my software system."); - Model model = workspace.getModel(); - - Person user = model.addPerson("User", "A user of my software system."); - SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "My software system."); - user.uses(softwareSystem, "Uses"); - - ViewSet views = workspace.getViews(); - SystemContextView contextView = views.createSystemContextView(softwareSystem, "SystemContext", "An example of a System Context diagram."); - contextView.addAllSoftwareSystems(); - contextView.addAllPeople(); -} -``` - -The view can then be exported to be visualised using the [Structurizr cloud service/on-premises installation/Lite](https://structurizr.com), -or other formats including PlantUML, Mermaid, DOT, and WebSequenceDiagrams via the [structurizr-export library](https://github.com/structurizr/export). - -## Table of contents +and (2) a way to create a Structurizr workspace using Java code. -* [Changelog](docs/changelog.md) -* Introduction - * [Getting started](docs/getting-started.md) - * [Basic concepts](https://structurizr.com/help/concepts) (workspaces, models, views and documentation) - * [C4 model](https://c4model.com) - * [Examples](https://github.com/structurizr/examples) - * [Binaries](docs/binaries.md) - * [Building from source](docs/building.md) - * [FAQ](docs/faq.md) -* Model - * [Creating your model](docs/model.md) - * [Implied relationships](docs/implied-relationships.md) -* Views - * [Creating views](docs/views.md) - * [System Context diagram](docs/system-context-diagram.md) - * [Container diagram](docs/container-diagram.md) - * [Component diagram](docs/component-diagram.md) - * [Dynamic diagram](docs/dynamic-diagram.md) - * [Deployment diagram](docs/deployment-diagram.md) - * [System Landscape diagram](docs/system-landscape-diagram.md) - * [Styling elements](docs/styling-elements.md) - * [Styling relationships](docs/styling-relationships.md) - * [Filtered views](docs/filtered-views.md) -* Cloud service/on-premises installation - * [API client](docs/api-client.md) - * [Client-side encryption](docs/client-side-encryption.md) -* Related projects - * [structurizr-dsl](https://github.com/structurizr/dsl): A text-based DSL for authoring Structurizr workspaces. - * [structurizr-export](https://github.com/structurizr/export): Export model and views to external formats (e.g. PlantUML, Mermaid, etc). - * [structurizr-import](https://github.com/structurizr/import): Import Markdown/AsciiDoc documentation/ADRs into a Structurizr workspace. +Looking for the [Structurizr DSL](https://github.com/structurizr/dsl) instead? +- [Documentation](https://docs.structurizr.com/java) +- [Changelog](docs/changelog.md) \ No newline at end of file diff --git a/docs/api-client.md b/docs/api-client.md deleted file mode 100644 index 7a45400ac..000000000 --- a/docs/api-client.md +++ /dev/null @@ -1,95 +0,0 @@ -# API client - -The Structurizr for Java library includes a client for the [Structurizr web API](https://api.structurizr.com), which allows you to get and put workspaces using JSON over HTTPS. This page provides a quick overview of how to use the API client. - -## Configuration - -The are two ways to configure the API client. - -### 1. Programmatically - -The easiest way to configure the API client is to provide values for the API key and API secret programmatically. Each workspace has its own API key and secret, the values for which can be found on [your Structurizr dashboard](https://structurizr.com/dashboard). - -```java -StructurizrClient structurizrClient = new StructurizrClient("key", "secret"); -``` - -If you're using the [on-premises installation](https://structurizr.com/help/on-premises), there is a three argument version of the constructor where you can also specify the API URL. - -```java -StructurizrClient structurizrClient = new StructurizrClient("url", "key", "secret"); -``` - -### 2. Properties file - -If you would like to separate your API credentials from the code, you can configure the values in a Java properties file. This should be named ```structurizr.properties``` and located on the classpath. - -``` -structurizr.api.url=https://api.structurizr.com -structurizr.api.key=key -structurizr.api.secret=secret -``` - -The API client can then be constructed using the default, no args, constructor. - -```java -StructurizrClient structurizrClient = new StructurizrClient(); -``` - -## Usage - -The following operations are available on the API client. - -### 1. getWorkspace - -This allows you to get the content of a remote workspace. - -```java -Workspace workspace = structurizrClient.getWorkspace(1234); -``` - -By default, a copy of the workspace (as a JSON document) is archived to the current working directory. You can modify this behaviour by calling ```setWorkspaceArchiveLocation```. A ```null``` value will disable archiving. - -### 2. putWorkspace - -This allows you to overwrite an existing remote workspace. If the ```mergeFromRemote``` property (on the ```StructurizrClient``` instance) is set to ```true``` (this is the default), any layout information (i.e. the location of boxes on diagrams) is preserved where possible (i.e. where diagram elements haven't been renamed). - -```java -structurizrClient.putWorkspace(1234, workspace); -``` - -### 3. lockWorkspace - -If your workspace supports sharing (not available with the Free Plan), you can optionally attempt to lock your workspace before writing to it, to prevent concurrent updates. - -```java -structurizrClient.lockWorkspace(1234); -``` - -This method returns a boolean; ```true``` if the workspace could be locked, ```false``` otherwise. - -### 4. unlockWorkspace - -Similarly, you can unlock a workspace. - -```java -structurizrClient.unlockWorkspace(1234); -``` - -This method also returns a boolean; ```true``` if the workspace could be unlocked, ```false``` otherwise. - -## SSL handshake errors - -SSL handshake errors are likely if using a self-signed certificate with the on-premises installation, because the Structurizr client program runtime won't trust a self-signed certificate by default. - -If this happens, you can use the ```javax.net.ssl.trustStore``` JVM option to point to your keystore. For example: - -``` -java -Djavax.net.ssl.trustStore=/some/path/to/keystore.jks YourJavaProgram -``` - -Alternatively, you can specify this property in your Java program: - -``` -System.setProperty("javax.net.ssl.trustStore", "/some/path/to/keystore.jks"); -``` diff --git a/docs/binaries.md b/docs/binaries.md deleted file mode 100644 index 74ef946e0..000000000 --- a/docs/binaries.md +++ /dev/null @@ -1,7 +0,0 @@ -# Binaries -The "Structurizr for Java" binaries are hosted on [Maven Central](https://repo1.maven.org/maven2/com/structurizr/) and the dependencies for use with Maven, Ivy, Gradle, etc are as follows. - -Name | Description ------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-core | The core library that can used to create software architecture models. -com.structurizr:structurizr-client | The API client for publishing models on the Structurizr cloud service and on-premises installation. \ No newline at end of file diff --git a/docs/building.md b/docs/building.md deleted file mode 100644 index a68e36640..000000000 --- a/docs/building.md +++ /dev/null @@ -1,9 +0,0 @@ -# Building - -To build this repo from the sources (you'll need `git` and Java 11+ installed)... - -``` -git clone https://github.com/structurizr/java.git structurizr-java -cd structurizr-java -./gradlew -``` \ No newline at end of file diff --git a/docs/client-side-encryption.md b/docs/client-side-encryption.md deleted file mode 100644 index 5115c7db1..000000000 --- a/docs/client-side-encryption.md +++ /dev/null @@ -1,23 +0,0 @@ -# Client-side encryption - -> This feature is not available with a free Structurizr cloud service account. - -The JSON representation of your workspace is stored on the Structurizr cloud service using AES encryption with a 128-bit key, a random salt and a server-side passphrase. For additional peace of mind, you can choose to encrypt your workspace with your own passphrase on the client before uploading it to Structurizr. In order to view a client-side encrypted workspace, you will be asked to enter your passphrase when you open the workspace in your web browser. The passphrase is then used to decrypt the workspace in your web browser - at no point does the passphrase leave your computer. - -To use client-side encryption, simply create an instance of ```AesEncryptionStrategy``` and associate it with your ```StructurizrClient``` instance. For example: - -```java -StructurizrClient structurizrClient = new StructurizrClient("key", "secret"); -structurizrClient.setEncryptionStrategy(new AesEncryptionStrategy("password")); -structurizrClient.putWorkspace(1234, workspace); -``` - -The default key size is 128 bits and the default iteration count is 1000. An alternative constructor for AesEncryptionStrategy takes the following parameters: - -- The key size (number of bits; e.g. 128, 192 or 256). -- The iteration count (used when generating keys). -- The passphrase. - -In addition, a random salt and initialization vector are generated automatically for you, using Java's ```SecureRandom``` class. - -See [ClientSideEncryption.java](https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/ClientSideEncryption.java) for a full example, and [https://structurizr.com/share/41](https://structurizr.com/share/41) to access the workspace. \ No newline at end of file diff --git a/docs/component-diagram.md b/docs/component-diagram.md deleted file mode 100644 index 2edbe47b7..000000000 --- a/docs/component-diagram.md +++ /dev/null @@ -1,17 +0,0 @@ -# Component diagram - -Following on from a Container Diagram, next you can zoom in and decompose each container further to identify the major structural building blocks and their interactions. - -The Component diagram shows how a container is made up of a number of "components", what each of those components are, their responsibilities and the technology/implementation details. - -## Example - -This is an example Component diagram for a fictional Internet Banking System, showing some (rather than all) of the components within the API Application. Here, there are two Spring MVC Rest Controllers providing access points for the JSON/HTTPS API, with each controller subsequently using other components to access data from the Database and Mainframe Banking System. - -![An example Component diagram](https://static.structurizr.com/workspace/36141/diagrams/Components.png) - -See [InternetBankingSystem.java](https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/bigbankplc/InternetBankingSystem.java) for the code, and [https://structurizr.com/share/36141/diagrams#Components](https://structurizr.com/share/36141/diagrams#Components) for the diagram. - -### Extracting components automatically - -Please note that, in a real-world scenario, you would probably want to extract components automatically from a codebase with the [component finder](https://github.com/structurizr/java-extensions/blob/master/docs/component-finder.md), using static analysis and reflection techniques. \ No newline at end of file diff --git a/docs/container-diagram.md b/docs/container-diagram.md deleted file mode 100644 index ebc56d8d2..000000000 --- a/docs/container-diagram.md +++ /dev/null @@ -1,15 +0,0 @@ -# Container diagram - -Once you understand how your system fits in to the overall IT environment, a really useful next step is to zoom-in to the system boundary with a Container diagram. A "container" is something like a web application, desktop application, mobile app, database, file system, etc. Essentially, a container is a separately runnable/deployable unit that executes code or stores data. - -The Container diagram shows the high-level shape of the software architecture and how responsibilities are distributed across it. It also shows the major technology choices and how the containers communicate with one another. It's a simple, high-level technology focussed diagram that is useful for software developers and support/operations staff alike. - -## Example - -This is an example Container diagram for a fictional Internet Banking System. It shows that the Internet Banking System is made up of five containers: a server-side Web Application, a Single-Page Application, a Mobile App, a server-side API Application, and a Database. The Web Application is a Java/Spring MVC web application that simply serves static content (HTML, CSS and JavaScript), including the content that makes up the Single-Page Application. The Single-Page Application is an Angular application that runs in the customer's web browser, providing all of the Internet banking features. Alternatively, customers can use the cross-platform Xamarin Mobile App, to access a subset of the Internet banking functionality. - -Both the Single-Page Application and Mobile App use a JSON/HTTPS API, which is provided by another Java/Spring MVC application running on the server. The API Application gets user information from the Database (a relational database schema). The API Application also communicates with the existing Mainframe Banking System, using a propreitary XML/HTTPS interface, to get information about bank accounts or make transactions. The API Application also uses the existing E-mail System if it needs to send e-mails to customers. - -![An example Container diagram](https://static.structurizr.com/workspace/36141/diagrams/Containers.png) - -See [InternetBankingSystem.java](https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/bigbankplc/InternetBankingSystem.java) for the code, and [https://structurizr.com/share/36141/diagrams#Containers](https://structurizr.com/share/36141/diagrams#Containers) for the diagram. \ No newline at end of file diff --git a/docs/deployment-diagram.md b/docs/deployment-diagram.md deleted file mode 100644 index 39b80fc92..000000000 --- a/docs/deployment-diagram.md +++ /dev/null @@ -1,11 +0,0 @@ -# Deployment diagram - -A deployment diagram allows you to illustrate how containers in the static model are mapped to infrastructure. This deployment diagram is based upon a [UML deployment diagram](https://en.wikipedia.org/wiki/Deployment_diagram), although simplified slightly to show the mapping between containers and deployment nodes. A deployment node is something like physical infrastructure (e.g. a physical server or device), virtualised infrastructure (e.g. IaaS, PaaS, a virtual machine), containerised infrastructure (e.g. a Docker container), an execution environment (e.g. a database server, Java EE web/application server, Microsoft IIS), etc. Deployment nodes can be nested. - -## Example - -As an example, a Deployment diagram for the live environment of a simplified, fictional Internet Banking System might look something like this. In summary, it shows the deployment of the Web Application and the Database, with a secondary Database being used for failover purposes. - -![An example Deployment diagram](https://static.structurizr.com/workspace/36141/diagrams/LiveDeployment.png) - -See [InternetBankingSystem.java](https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/bigbankplc/InternetBankingSystem.java) for the code, and [https://structurizr.com/share/36141/diagrams#LiveDeployment](https://structurizr.com/share/36141/diagrams#LiveDeployment) for the diagram. \ No newline at end of file diff --git a/docs/dynamic-diagram.md b/docs/dynamic-diagram.md deleted file mode 100644 index 8443a8234..000000000 --- a/docs/dynamic-diagram.md +++ /dev/null @@ -1,19 +0,0 @@ -# Dynamic diagram - -A simple dynamic diagram can be useful when you want to show how elements in a static model collaborate at runtime to implement a user story, use case, feature, etc. This dynamic diagram is based upon a [UML communication diagram](https://en.wikipedia.org/wiki/Communication_diagram) (previously known as a "UML collaboration diagram"). It is similar to a [UML sequence diagram](https://en.wikipedia.org/wiki/Sequence_diagram) although it allows a free-form arrangement of diagram elements with numbered interactions to indicate ordering. - -## Example - -As an example, a Dynamic diagram describing the customer sign in process for a simplified, fictional Internet Banking System might look something like this. In summary, it shows the components involved in the sign in process, and the interactions between them. - -![An example Dynamic diagram](https://static.structurizr.com/workspace/36141/diagrams/SignIn.png) - -See [InternetBankingSystem.java](https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/bigbankplc/InternetBankingSystem.java) for the code, and [https://structurizr.com/share/36141/diagrams#SignIn](https://structurizr.com/share/36141/diagrams#SignIn) for the diagram. - -### Adding relationships - -In order to add a relationship between two elements to a dynamic view, that relationship must already exist between the two elements in the static view. - -### Parallel behaviour - -Showing parallel behaviour is also possible using the ```startParallelSequence()``` and ```endParallelSequence()``` methods on the ```DynamicView``` class. See [MicroservicesExample.java](https://github.com/structurizr/java/blob/master/structurizr-examples/src/com/structurizr/example/MicroservicesExample.java) and [https://structurizr.com/share/4241#CustomerUpdateEvent](https://structurizr.com/share/4241#CustomerUpdateEvent) for an example. \ No newline at end of file diff --git a/docs/faq.md b/docs/faq.md deleted file mode 100644 index 29f78a937..000000000 --- a/docs/faq.md +++ /dev/null @@ -1,19 +0,0 @@ -# Frequently asked questions - -## Why are many classes final with package-protected members, and not open to extension? - -First and foremost, this repo is a client library for the [Structurizr cloud service and on-premises installation](https://structurizr.com). -It allows you to write Java code to create an in-memory object graph representing a software architecture model and views (a "workspace"), serialize that to JSON, and upload it via a web API. -The workspace has an [OpenAPI definition](https://github.com/structurizr/json/blob/master/structurizr.yaml), but this library also implements a number of rules (think of them as the "business logic") to ensure that the workspace is valid. -These rules include, for example, ensuring that all containers with a software system have unique names, and that you can't add components to a system context view. - -Removing the `final` modifier from the classes and leaving them open for extension allows you to bypass/break these rules, which will likely lead to the serialized workspace definitions being incompatible with the various diagram rendering tools -(i.e. the Structurizr cloud service/on-premises installation/Lite, plus the PlantUML/Mermaid exporters). -The output of this library also needs to be compatible with client libraries written in other languages. - -You are welcome to fork this library for your own purposes. -Alternatively, you can build a thin wrapper around the library, to provide your own custom functionality, or perhaps a more fluent API ... many teams have done this. - -## Can I submit a pull request? - -It depends on the nature of the change. Please open an issue first to discuss it. diff --git a/docs/filtered-views.md b/docs/filtered-views.md deleted file mode 100644 index c0e700872..000000000 --- a/docs/filtered-views.md +++ /dev/null @@ -1,39 +0,0 @@ -# Filtered views - -A filtered view represents a view on top of another view, which can be used to include or exclude specific elements and/or relationships, based upon their tag. The benefit of using filtered views is that element and relationship positions are shared between the views. - -Filtered views can be created on top of static views only; i.e. System Landscape, System Context, Container and Component views. - -## Example - -As an example, let's imagine an organisation where a User uses Software System A for tasks 1 and 2. - -![A diagram showing the current state](images/filtered-views-1.png) - -And, in the future, Software System B will be introduced to fulfil task 2. - -![A diagram showing the future state](images/filtered-views-2.png) - -With Structurizr for Java, you can illustrate this by defining a single context diagram with two filtered views on top; one showing the current state and the other showing future state. - -```java -Person user = model.addPerson("User", "A description of the user."); -SoftwareSystem softwareSystemA = model.addSoftwareSystem("Software System A", "A description of software system A."); -SoftwareSystem softwareSystemB = model.addSoftwareSystem("Software System B", "A description of software system B."); -softwareSystemB.addTags(FUTURE_STATE); - -user.uses(softwareSystemA, "Uses for tasks 1 and 2").addTags(CURRENT_STATE); -user.uses(softwareSystemA, "Uses for task 1").addTags(FUTURE_STATE); -user.uses(softwareSystemB, "Uses for task 2").addTags(FUTURE_STATE); - -ViewSet views = workspace.getViews(); -SystemLandscapeView systemLandscapeView = views.createSystemLandscapeView("SystemLandscape", "An example System Landscape diagram."); -systemLandscapeView.addAllElements(); - -views.createFilteredView(systemLandscapeView, "CurrentState", "The current system landscape.", FilterMode.Exclude, FUTURE_STATE); -views.createFilteredView(systemLandscapeView, "FutureState", "The future state system landscape after Software System B is live.", FilterMode.Exclude, CURRENT_STATE); -``` - -In summary, you create a view with all of the elements and relationships that you want to show, and then create one or more filtered views on top, specifying the tags that you'd like to include or exclude. - -See [FilteredViews.java](https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/FilteredViews.java) for the full code, and [https://structurizr.com/share/19911/diagrams](https://structurizr.com/share/19911/diagrams) for the diagram. \ No newline at end of file diff --git a/docs/getting-started.md b/docs/getting-started.md deleted file mode 100644 index 5a5c3425f..000000000 --- a/docs/getting-started.md +++ /dev/null @@ -1,94 +0,0 @@ -# Getting started - -Here is a quick overview of how to get started with Structurizr for Java so that you can create a software architecture model as code. -You can find the code at [GettingStarted.java](https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/GettingStarted.java) -and the live example workspace at [https://structurizr.com/share/25441](https://structurizr.com/share/25441). - -For more examples, please see [structurizr-examples](https://github.com/structurizr/examples/tree/main/java/src/main/java/com/structurizr/example). - -## 1. Dependencies - -The Structurizr for Java binaries are hosted on [Maven Central](https://repo1.maven.org/maven2/com/structurizr/structurizr-client/) and the dependencies for use with Maven, Ivy, Gradle, etc are as follows. - -Name | Description ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------- -com.structurizr:structurizr-client | The Structurizr API client library. - -## 2. Create a Java program - -The software architecture model is going to be created by a short Java program, so we'll need to start by creating a new Java class, with a ```main``` method as follows: - -```java -public class GettingStarted { - - public static void main(String[] args) throws Exception { - // all of the Structurizr code will go here - } - -} -``` - -## 3. Create a model - -The first step is to create a workspace in which the software architecture model will reside. - -```java -Workspace workspace = new Workspace("Getting Started", "This is a model of my software system."); -Model model = workspace.getModel(); -``` - -Now let's add some elements to the model to describe a user using a software system. - -```java -Person user = model.addPerson("User", "A user of my software system."); -SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "My software system."); -user.uses(softwareSystem, "Uses"); -``` - -## 4. Create some views - -With the model created, we need to create some views with which to visualise it. - -```java -ViewSet views = workspace.getViews(); -SystemContextView contextView = views.createSystemContextView(softwareSystem, "SystemContext", "An example of a System Context diagram."); -contextView.addAllSoftwareSystems(); -contextView.addAllPeople(); -``` - -## 5. Add some colour and shapes - -Optionally, elements and relationships can be styled by specifying colours, sizes and shapes. - -```java -Styles styles = views.getConfiguration().getStyles(); -styles.addElementStyle(Tags.SOFTWARE_SYSTEM).background("#1168bd").color("#ffffff"); -styles.addElementStyle(Tags.PERSON).background("#08427b").color("#ffffff").shape(Shape.Person); -``` - -## 6. Upload to Structurizr - -Structurizr provides a web API to get and put workspaces, and an API client is provided by the ```StructurizrClient``` class. - -```java -StructurizrClient structurizrClient = new StructurizrClient("key", "secret"); -structurizrClient.putWorkspace(25441, workspace); -``` - -> In order to upload your model to Structurizr using the web API, you'll need to [sign up for free](https://structurizr.com/signup) to get your own API key and secret. See [Structurizr - Workspaces](https://structurizr.com/help/workspaces) for information about finding your workspace ID, API key and secret. - -## 7. Open the workspace in Structurizr - -Once you've run your program to create and upload the workspace, you can now sign in to your Structurizr account, and open the workspace from [your dashboard](https://structurizr.com/dashboard). The result should be a diagram like this: - -![Getting Started with Structurizr for Java](images/getting-started-1.png) - -By default, Structurizr does not auto-layout your diagram elements. The diagram layout can be modified by dragging the elements around the diagram canvas in the diagram editor, and the layout saved using the "Save workspace" button. See [Structurizr - Help - Diagram editor](https://structurizr.com/help/diagram-editor) for more information. - -![Getting Started with Structurizr for Java](images/getting-started-2.png) - -A diagram key is automatically generated based upon the styles in the model. Click the "i" button on the toolbar (or press the 'i' key) to display the diagram key. - -![A diagram key](images/getting-started-diagram-key.png) - -When you upload a new version of the same workspace, the Structurizr client will try to retain the diagram layout information. See [API client](api-client.md) for more details. diff --git a/docs/images/filtered-views-1.png b/docs/images/filtered-views-1.png deleted file mode 100644 index 8212d29e943ff3fba1bf01d35f87988989860a15..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 165819 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Ɩ=;|_bn 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:VsglslG0z(8uVbc+WrkgoyYQ7}7P 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 diff --git a/docs/images/filtered-views-2.png b/docs/images/filtered-views-2.png deleted file mode 100644 index 8d833165813342d5324a27593ec9e18c746b43d4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 187584 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Ɩ=;|_bn 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⁢BDgf;vb6hi?XSD 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�UI%ZKf8G0{UfZA5AmX|fd2BuB#58_UX&-F1 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∑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

    aL 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 MeNE*eB}Bmg06*xwf&c&j diff --git a/docs/images/getting-started-2.png b/docs/images/getting-started-2.png deleted file mode 100644 index 43cb96307851fd2621684c21e02ff7bcd39e497d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 133350 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

    S^5r!V_<&1(uR#SCvkL&1@qNb{oxx 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 C@Br2T diff --git a/docs/images/styling-elements-2.png b/docs/images/styling-elements-2.png deleted file mode 100644 index 27abd955dab8b7893a5c41c5a8d43e77e0b6866b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 175074 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

    ej-rlX_eZ3C_wz4gXx0tHA$lQ>{d-gIKZuF{ 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 z z4V5RR4C-b;U6{l9Zv8f|-r#x!;~(#8Wla3uw$x2|Go#TVUmbwCd6M)qz;Js^c+|FC zi9|wgnj#H&$TwVx5X&QbOGZ2(N9umKx$);RXU$p;AWClW0BMhfds@`T=`Ta_Yh+=J zzJ=xiHF%RqY$5O)PRp2Cv_evx9YBdMWOW)_$-?^UO#vGOYGf;(abEJ5Plf zIeR1q<xdy5pxKK%!TD&W+d*h^K~BGgc;b$%o;ViO$!4k7SbxgFHs# z(iL(-FcElM`8TSyiYd=OmQ5ESBTxCnd%=ap-SQd(eIPVFFnoLo7r8!p2)q#ld%95H zmReeT-4wwcBwe6M?Y1&6U|Hd}nc$Z)gi$K&``DMe+V28Ao&=K&L0$lA1bm+f6K0eZ z55gtxGa_ckJRJGZao0uMUMzXE1?}F%-FNenFX#X>q|F#rTdRf{RYj|A1!L$*SZ`aBqTuWIRrfftkyCTjj&h*4eA9c|r4IjBzg#q1VD3IN6KPSDIVRz4nK2 z&;aXa_@)iIXQOTpWr)2Qs!cKuIf^;Lp7KDJKC|$A+0F?2jpaCkS+Arjc`tFE?`)US zkoieu_@vTuI8Z$13;hvJcOK8%=NPuKQ;<$%X3@nZISiZ%X-qHl0L+rF<-Llyktcym zs%SwrwP(EmslbQ~5`E56MIH5x-pZ2f@OgSHP7H2v`68)AzO-cpkJ!>39|J~l@r5Gd zIno6Q3F%8$NI%I`9lJy-SbglQ;HCVob}^4puR^)rfdLd!yJ;uhoYcv*ou3#P$+o)c z9z-LW^V;IElrH+4v*jlL|Mv}=|K{a?0gt}NpULALLbV$z>K1tOt)4z2ZGBSfAxt*< zBZf4kQ-w#pM#N*EZ|B)7I$K=U^juwFQLmOME}W{u;6Y@b!mCF+tmeiqpT;x~`V+{8>WC?d$;r??pP-CS)=jnW;eNfyu?3`fSd>9G(o zqY@nYHA2sGiLOp?tB41xo3>BBmhew^#$n>nYq7dDLw!YygY?G+dH_F)yQj7kEhGhQ z;Ns+HahS>cY|ho5a>=m``P2P&M8;z-T0>QrTU@3WrG_eB^z!eF$Y#w}Wu*mGXWr9DCuVy((F_~Dp5Y2{&=vr2CQiI@)Lf7z-~-Po$TimkxnFT zDLqDg#SC}CK8nR-c3V@Hon+?ANU31;zD|8N{hZqUw+IA6YeK=Yw&q}#qYq-Y z-6426MMe{WPBGM6YR)d4f1*8Avp({gRAJRB6q#ObqwhCD3)Nq>?ubbZ;jFpSh@2QWwslp9ywM@0U>^ zoC}h9-V&$S_hPKpGxw2m#^Cn;&YD0`q4Z47@*s$UQ%y1auFTTCajR+T-)!lN|MI#1 zXU^oYB~I&+kcP2;AQ%GPJClXgQJqV7w_fK|tKKsBkdM}enJpmfGq>W!%DAsQCmS+e z$XO+*vZm%q3UBJ`|GXMV!8P0yKYqM~cFB+b(w*lI?x?ri7J8vnx-8d1+>bVLb*-$h zn(!SIS*gf`I5_Na>-H_;_-M33}#KdN%+10(e6gpc`SXa<5X+p zM49nCuZa7u+T!xEqKF{y9yow#qq)_|7Di<1*~|a(hyVLWkzS>|XrQb1ZHM#>tHk%F zAetT^>c|53P800yTmWqrhuR+D9;)Ejaec5&*%yn$9_LB^&7b<`?@{;RBx(Pf zD#0nqzu0zv{w1$VXV2sXzQC{r{`GVDb-Ny2y=ZV_bt&S;f85mnxK2sdJ9WtMwql;Y zzV=^NK%xEMrS;cJqi6s46aV}T|K&Qb<-a^O^zMFN=YRaEe>{)@Y1*r1+-#ENcIPnPaK{nS5h_WwPAf4+bJxHEq~xc_?qf3b7^_dB4@^LSKV!m|#l RGsnPxCZzG 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 z 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≪OM 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 z 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ª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�(NqP}W}cwIW{2vsNc;}S~KAQ35| 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

    PE8RJTqr*@wP7l+B1-+(~+o?y`TWG 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

    ZkR>{l`ij{2 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 z%0D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%0DD4{0 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 diff --git a/docs/images/styling-relationships-4.png b/docs/images/styling-relationships-4.png deleted file mode 100644 index 1bd7f7a6ba4938e553a5a881a2cf08b5fb2098b8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 90321 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 G;Qs-$@c4HC diff --git a/docs/implied-relationships.md b/docs/implied-relationships.md deleted file mode 100644 index baff56730..000000000 --- a/docs/implied-relationships.md +++ /dev/null @@ -1,93 +0,0 @@ -# Implied relationships - -By default, the Structurizr for Java client library will not create implied relationships. For example, let's say that you have two components in different containers, and you create a relationship between them. - -``` -SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); -Container container1 = softwareSystem.addContainer("Container 1", "", ""); -Component component1 = container1.addComponent("Component 1", "", ""); - -Container container2 = softwareSystem.addContainer("Container 2", "", ""); -Component component2 = container2.addComponent("Component 2", "", ""); - -component1.uses(component2, "Sends data X to"); -``` - -At this point, the model contains a single relationship between the two components, but there are three other implied relationships that could be added: - -![Implied relationships](images/implied-relationships-1.png) - -- Container 1 Sends data X to Component 2 -- Component 1 Sends data X to Container 2 -- Container 1 Sends data X to Container 2 - -To have the client library create these for you, set an ```ImpliedRelationshipsStrategy``` implementation on your model. Possible implementations are as follows. - -## DefaultImpliedRelationshipsStrategy - -This strategy does not create any implied relationships. - -``` -SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); -Container container1 = softwareSystem.addContainer("Container 1", "", ""); -Component component1 = container1.addComponent("Component 1", "", ""); - -Container container2 = softwareSystem.addContainer("Container 2", "", ""); -Component component2 = container2.addComponent("Component 2", "", ""); - -model.setImpliedRelationshipsStrategy(new DefaultImpliedRelationshipsStrategy()); // default -component1.uses(component2, "Sends data X to"); -``` - -Relationships that exist in the model: - -- Component 1 Sends data X to Component 2 - -## CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategy - -This strategy creates implied relationships between all valid combinations of the parent elements, unless the same relationship already exists between them. - -``` -SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); -Container container1 = softwareSystem.addContainer("Container 1", "", ""); -Component component1 = container1.addComponent("Component 1", "", ""); - -Container container2 = softwareSystem.addContainer("Container 2", "", ""); -Component component2 = container2.addComponent("Component 2", "", ""); - -model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); -component1.uses(component2, "Sends data X to"); -``` - -Relationships that exist in the model: - -- Component 1 Sends data X to Component 2 -- Component 1 Sends data X to Container 2 -- Container 1 Sends data X to Component 2 -- Container 1 Sends data X to Container 2 - -## CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy - -This strategy creates implied relationships between all valid combinations of the parent elements, unless *any* relationship already exists between them. - -``` -SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); -Container container1 = softwareSystem.addContainer("Container 1", "", ""); -Component component1 = container1.addComponent("Component 1", "", ""); - -Container container2 = softwareSystem.addContainer("Container 2", "", ""); -Component component2 = container2.addComponent("Component 2", "", ""); - -model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); -container1.uses(container2, "Sends data to"); -component1.uses(component2, "Sends data X to"); -``` - -Relationships that exist in the model: - -- Component 1 Sends data X to Component 2 -- Component 1 Sends data X to Container 2 -- Container 1 Sends data X to Component 2 -- Container 1 Sends data to Container 2 - -This strategy is useful when you want to show a summary relationship at higher levels in the model, especially when multiple implied relationships could be created between elements. \ No newline at end of file diff --git a/docs/model.md b/docs/model.md deleted file mode 100644 index 969755d6d..000000000 --- a/docs/model.md +++ /dev/null @@ -1,20 +0,0 @@ -# Model - -This is the definition of the software architecture model, consisting of people, software systems, containers, components, deployment nodes, etc plus the relationships between them. - -All of the Java classes representing people, software systems, containers, components, etc, and the functionality related to creating a software architecture model can be found in the [com.structurizr.model](https://github.com/structurizr/java/tree/master/structurizr-core/src/com/structurizr/model) package. - -An empty model is created for you when you create a workspace. - -```java -Workspace workspace = new Workspace("Name", "Description"); -Model model = workspace.getModel(); -``` - -Once you have a reference to a ```Model``` instance, you can add elements to it via the various public `add*` methods that you'll find on ```Model```, ```SoftwareSystem```, ```Container```, etc. - -## Automatic model generation - -Although there is nothing included in the Structurizr for Java library to support automatic model generation, -you could choose to parse an external definition of your software architecture (e.g. an AWS infrastructure topology, Terraform definition, another Architecture Description Language, your source code, etc) -and create model elements accordingly. \ No newline at end of file diff --git a/docs/styling-elements.md b/docs/styling-elements.md deleted file mode 100644 index c0ae0967f..000000000 --- a/docs/styling-elements.md +++ /dev/null @@ -1,78 +0,0 @@ -# Styling elements - -By default, all model elements are rendered as grey boxes, as illustrated by the example diagram below. - -![Default styling](images/styling-elements-1.png) - -However, the following characteristics of the elements can be customized: - -- Width (pixels) -- Height (pixels) -- Background colour (HTML hex value) -- Text colour (HTML hex value) -- Font size (pixels) -- Shape (see the [Shape](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/view/Shape.java) enum) -- Border (Solid or Dashed; see the [Border](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/view/Border.java) enum) -- Opacity (an integer between 0 and 100) - -## Tagging elements - -All elements within a software architecture model can have one or more tags associated with them. A tag is simply a free-format string. By default, the Java client library adds the following tags to elements. - -Element | Tags -------- | ---- -Software System | "Element", "Software System" -Person | "Element", "Person" -Container | "Element", "Container" -Component | "Element", "Component" - -All of these tags are defined as constants in the [Tags](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/model/Tags.java) class. As we'll see shortly, you can also add your own custom tags to elements using the ```addTags()``` method on the element. - -## Colour - -To style an element, simply create an [ElementStyle](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/view/ElementStyle.java) for a particular tag and specify the characteristics that you would like to change. For example, you can change the colour of all elements as follows. - -```java -Styles styles = workspace.getViews().getConfiguration().getStyles(); -styles.addElementStyle(Tags.ELEMENT).background("#438dd5").color("#ffffff"); -``` - - ![Colouring all elements](images/styling-elements-2.png) - -You can also change the colour of specific elements, for example based upon their type, as follows. - -```java -styles.addElementStyle(Tags.ELEMENT).color("#ffffff"); -styles.addElementStyle(Tags.PERSON).background("#08427b"); -styles.addElementStyle(Tags.CONTAINER).background("#438dd5"); -``` - -![Colouring elements based upon type](images/styling-elements-3.png) - -> If you're looking for a colour scheme for your diagrams, try the [Adobe Color Wheel](https://color.adobe.com/create/color-wheel/) or [Paletton](http://paletton.com). - -## Shapes - -You can also style elements using different shapes as follows. - -```java -styles.addElementStyle(Tags.ELEMENT).color("#ffffff"); -styles.addElementStyle(Tags.PERSON).background("#08427b").shape(Shape.Person); -styles.addElementStyle(Tags.CONTAINER).background("#438dd5"); -database.addTags("Database"); -styles.addElementStyle("Database").shape(Shape.Cylinder); -``` - -![Adding some shapes](images/styling-elements-4.png) - -As with CSS, styles cascade according to the order in which they are added. In the example above, the database element is coloured using the "Container" style, the shape of which is overriden by the "Database" style. - -The set of available shapes is as follows: - -![The shapes available in Structurizr](images/styling-elements-5.png) - -## Diagram key - -Structurizr will automatically add all element styles to a diagram key, showing you which styles are associated with which tags. - -![The diagram key](images/styling-elements-6.png) \ No newline at end of file diff --git a/docs/styling-relationships.md b/docs/styling-relationships.md deleted file mode 100644 index ae696bd34..000000000 --- a/docs/styling-relationships.md +++ /dev/null @@ -1,48 +0,0 @@ -# Styling relationships - -By default, all relationships are rendered as dashed grey lines as shown in the example diagram below. - -![Default styling](images/styling-relationships-1.png) - -However, the following characteristics of the relationships can be customized: - -- Line thickness (pixels) -- Colour (HTML hex value) -- Dashed (true or false) -- Routing (Direct or Orthogonal; see the [Routing](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/view/Routing.java) enum) -- Font size (pixels) -- Width (of the description, in pixels) -- Position (of the description along the line, as a percentage from start to end) -- Opacity (an integer between 0 and 100) - -## Tagging relationships - -All relationships within a software architecture model can have one or more tags associated with them. A tag is simply a free-format string. By default, the Java client library adds the ```"Relationship"``` tag to relationships. As we'll see shortly, you can add your own custom tags to relationships using the ```addTags()``` method on the relationship. - -## Colour - -To style a relationship, simply create a [RelationshipStyle](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/view/RelationshipStyle.java) for a particular tag and specify the characteristics that you would like to change. For example, you can change the colour of all relationships as follows. - -```java -Styles styles = workspace.getViews().getConfiguration().getStyles(); -styles.addRelationshipStyle(Tags.RELATIONSHIP).color("#ff0000"); -``` - -![Colouring all relationships](images/styling-relationships-2.png) - -You can also change the colour of specific relationships, based upon their tag, as follows. - -```java -model.getRelationships().stream().filter(r -> "HTTPS".equals(r.getTechnology())).forEach(r -> r.addTags("HTTPS")); -model.getRelationships().stream().filter(r -> "JDBC".equals(r.getTechnology())).forEach(r -> r.addTags("JDBC")); -styles.addRelationshipStyle("HTTPS").color("#ff0000"); -styles.addRelationshipStyle("JDBC").color("#0000ff"); -``` - -![Colouring relationships based upon tag](images/styling-relationships-3.png) - -## Diagram key - -Structurizr will automatically add all relationship styles to a diagram key. - -![The diagram key](images/styling-relationships-4.png) \ No newline at end of file diff --git a/docs/system-context-diagram.md b/docs/system-context-diagram.md deleted file mode 100644 index cc4bcfd58..000000000 --- a/docs/system-context-diagram.md +++ /dev/null @@ -1,13 +0,0 @@ -# System Context diagram - -A System Context diagram is a good starting point for diagramming and documenting a software system, allowing you to step back and see the big picture. Draw a diagram showing your system as a box in the centre, surrounded by its users and the other systems that it interacts with. - -Detail isn't important here as this is your zoomed out view showing a big picture of the system landscape. The focus should be on people (actors, roles, personas, etc) and software systems rather than technologies, protocols and other low-level details. It's the sort of diagram that you could show to non-technical people. - -## Example - -This is an example System Context diagram for a fictional Internet Banking System. It shows the people who use it, and the other software systems that the Internet Banking System has a relationship with. Personal Customers of the bank use the Internet Banking System to view information about their bank accounts, and to make payments. The Internet Banking System itself uses the bank's existing Mainframe Banking System to do this, and uses the bank's existing E-mail System to send e-mails to customers. - -![An example System Context diagram](https://static.structurizr.com/workspace/36141/diagrams/SystemContext.png) - -See [InternetBankingSystem.java](https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/bigbankplc/InternetBankingSystem.java) for the code, and [https://structurizr.com/share/36141/diagrams#SystemContext](https://structurizr.com/share/36141/diagrams#SystemContext) for the diagram. \ No newline at end of file diff --git a/docs/system-landscape-diagram.md b/docs/system-landscape-diagram.md deleted file mode 100644 index 457998bf7..000000000 --- a/docs/system-landscape-diagram.md +++ /dev/null @@ -1,13 +0,0 @@ -# System Landscape diagram - -The C4 model provides a static view of a single software system but, in the real-world, software systems never live in isolation. For this reason, and particularly if you are responsible for a collection of software systems, it's often useful to understand how all of these software systems fit together within the bounds of an enterprise. To do this, simply add another diagram that sits "on top" of the C4 diagrams, to show the system landscape from an IT perspective. Like the System Context diagram, this diagram can show the organisational boundary, internal/external users and internal/external systems. - -Essentially this is a high-level map of the software systems at the enterprise level, with a C4 drill-down for each software system of interest. From a practical perspective, a system landscape diagram is really just a system context diagram without a specific focus on a particular software system. - -## Example - -As an example, a System Landscape diagram for a simplified, fictional bank might look something like this. - -![An example System Landscape diagram](https://static.structurizr.com/workspace/28201/diagrams/SystemLandscape.png) - -See [SystemLandscape.java](https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/bigbankplc/SystemLandscape.java) for the code, and [https://structurizr.com/share/28201/diagrams#SystemLandscape](https://structurizr.com/share/28201/diagrams#SystemLandscape) for the diagram. \ No newline at end of file diff --git a/docs/views.md b/docs/views.md deleted file mode 100644 index b53c9e22f..000000000 --- a/docs/views.md +++ /dev/null @@ -1,29 +0,0 @@ -# Views - -Once you've [added elements to a model](model.md), you can create one or more views to visualise parts of the model, which can subsequently be rendered as diagrams by a number of different tools. - -Structurizr for Java supports the following view types described in the [C4 model](https://c4model.com), and the Java classes implementing these views can be found in the [com.structurizr.view](https://github.com/structurizr/java/tree/master/structurizr-core/src/com/structurizr/view) package as follows: - -* Core diagrams - * [SystemContextView](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/view/SystemContextView.java) - * [ContainerView](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/view/ContainerView.java) - * [ComponentView](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/view/ComponentView.java) - -* Supplementary diagrams - * [SystemLandscapeView](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/view/SystemLandscapeView.java) - * [DynamicView](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/view/DynamicView.java) - * [DeploymentView](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/view/DeploymentView.java) - -Please note that code diagrams (level 4 of the C4 model) are not supported. - -## Creating views - -All views are associated with a [ViewSet](https://github.com/structurizr/java/blob/master/structurizr-core/src/com/structurizr/view/ViewSet.java), which is created for you when you create a workspace. - -```java -Workspace workspace = new Workspace("Getting Started", "This is a model of my software system."); -ViewSet views = workspace.getViews(); -``` - -Use the various ```create*View``` methods on the ```ViewSet``` class to create views. - From 245e573e24a8b4ea1ab32925ec6ae8ecbb17ef3f Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 28 Dec 2023 15:30:54 +0000 Subject: [PATCH 434/717] . --- docs/changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.md b/docs/changelog.md index e02d80837..fa0f33d35 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -4,6 +4,7 @@ - Adds `com.structurizr.api.AdminApiClient` as a client for the cloud service/on-premises admin APIs. - Adds support for inter-workspace URLs of the form `{workspace:123456}/diagrams`. +- Deprecates `StructurizrClient` in favour of `WorkspaceApiClient`. ## 1.28.1 (11th December 2023) From b703ddaad8d1bc97ff9dc1e51359ace485cb8b2b Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 28 Dec 2023 15:32:20 +0000 Subject: [PATCH 435/717] Updated to reflect release. --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index fa0f33d35..301b56efe 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 1.29.0 (unreleased) +## 1.29.0 (28th December 2023) - Adds `com.structurizr.api.AdminApiClient` as a client for the cloud service/on-premises admin APIs. - Adds support for inter-workspace URLs of the form `{workspace:123456}/diagrams`. From d830b157ace65d9b54bef97facf06507a4ce6c50 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 5 Jan 2024 11:25:11 +0000 Subject: [PATCH 436/717] Adds `Workspace.trim()` to trim a workspace of unused elements (i.e. those not associated with any views). --- docs/changelog.md | 4 + .../src/com/structurizr/Workspace.java | 194 +++++++++++++- .../src/com/structurizr/model/Container.java | 14 ++ .../com/structurizr/model/DeploymentNode.java | 37 +++ .../src/com/structurizr/model/Element.java | 6 +- .../src/com/structurizr/model/Model.java | 134 +++++++++- .../com/structurizr/model/SoftwareSystem.java | 14 ++ .../unit/com/structurizr/WorkspaceTests.java | 237 +++++++++++++++++- 8 files changed, 623 insertions(+), 17 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 301b56efe..90451c105 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,9 @@ # Changelog +## unreleased + +- Adds `Workspace.trim()` to trim a workspace of unused elements (i.e. those not associated with any views). + ## 1.29.0 (28th December 2023) - Adds `com.structurizr.api.AdminApiClient` as a client for the cloud service/on-premises admin APIs. diff --git a/structurizr-core/src/com/structurizr/Workspace.java b/structurizr-core/src/com/structurizr/Workspace.java index 3c2dc0334..8dd320eb7 100644 --- a/structurizr-core/src/com/structurizr/Workspace.java +++ b/structurizr-core/src/com/structurizr/Workspace.java @@ -4,7 +4,7 @@ import com.structurizr.documentation.Documentable; import com.structurizr.documentation.Documentation; import com.structurizr.model.*; -import com.structurizr.view.ViewSet; +import com.structurizr.view.*; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -12,8 +12,8 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.LinkedList; -import java.util.List; +import java.util.*; +import java.util.stream.Collectors; /** * Represents a Structurizr workspace, which is a wrapper for a @@ -231,12 +231,190 @@ public int countAndLogWarnings() { return warnings.size(); } - private String typeof(Element element) { - if (element instanceof SoftwareSystem) { - return "software system"; - } else { - return element.getClass().getSimpleName().toLowerCase(); + /** + * Trims the workspace by removing all unused elements. + */ + public void trim() { + for (CustomElement element : model.getCustomElements()) { + remove(element); + } + + for (Person person : model.getPeople()) { + remove(person); + } + + for (SoftwareSystem softwareSystem : model.getSoftwareSystems()) { + remove(softwareSystem); + } + + for (DeploymentNode deploymentNode : model.getDeploymentNodes()) { + remove(deploymentNode); + } + } + + void remove(CustomElement element) { + if (!isElementAssociatedWithAnyViews(element)) { + try { + Method method = Model.class.getDeclaredMethod("remove", CustomElement.class); + method.setAccessible(true); + method.invoke(model, element); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + void remove(Person person) { + if (!isElementAssociatedWithAnyViews(person)) { + try { + Method method = Model.class.getDeclaredMethod("remove", Person.class); + method.setAccessible(true); + method.invoke(model, person); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + void remove(SoftwareSystem softwareSystem) { + Set softwareSystemInstances = model.getElements().stream().filter(e -> e instanceof SoftwareSystemInstance && ((SoftwareSystemInstance)e).getSoftwareSystem() == softwareSystem).map(e -> (SoftwareSystemInstance)e).collect(Collectors.toSet()); + for (SoftwareSystemInstance softwareSystemInstance : softwareSystemInstances) { + remove(softwareSystemInstance); + } + + for (Container container : softwareSystem.getContainers()) { + remove(container); + } + + boolean hasContainers = softwareSystem.hasContainers(); + boolean hasSoftwareSystemInstances = model.getElements().stream().anyMatch(e -> e instanceof SoftwareSystemInstance && ((SoftwareSystemInstance)e).getSoftwareSystem() == softwareSystem); + if (!hasContainers && !hasSoftwareSystemInstances && !isElementAssociatedWithAnyViews(softwareSystem)) { + try { + Method method = Model.class.getDeclaredMethod("remove", SoftwareSystem.class); + method.setAccessible(true); + method.invoke(model, softwareSystem); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + void remove(Container container) { + for (Component component : container.getComponents()) { + remove(component); + } + + if (!isElementAssociatedWithAnyViews(container)) { + Set containerInstances = model.getElements().stream().filter(e -> e instanceof ContainerInstance && ((ContainerInstance)e).getContainer() == container).map(e -> (ContainerInstance)e).collect(Collectors.toSet()); + for (ContainerInstance containerInstance : containerInstances) { + remove(containerInstance); + } + + boolean hasComponents = container.hasComponents(); + boolean hasContainerInstances = model.getElements().stream().anyMatch(e -> e instanceof ContainerInstance && ((ContainerInstance)e).getContainer() == container); + if (!hasComponents && !hasContainerInstances && !isElementAssociatedWithAnyViews(container)) { + try { + Method method = Model.class.getDeclaredMethod("remove", Container.class); + method.setAccessible(true); + method.invoke(model, container); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + } + + void remove(Component component) { + if (!isElementAssociatedWithAnyViews(component)) { + try { + Method method = Model.class.getDeclaredMethod("remove", Component.class); + method.setAccessible(true); + method.invoke(model, component); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + void remove(SoftwareSystemInstance softwareSystemInstance) { + if (!isElementAssociatedWithAnyViews(softwareSystemInstance)) { + try { + Method method = Model.class.getDeclaredMethod("remove", SoftwareSystemInstance.class); + method.setAccessible(true); + method.invoke(model, softwareSystemInstance); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + void remove(ContainerInstance containerInstance) { + if (!isElementAssociatedWithAnyViews(containerInstance)) { + try { + Method method = Model.class.getDeclaredMethod("remove", ContainerInstance.class); + method.setAccessible(true); + method.invoke(model, containerInstance); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + void remove(DeploymentNode deploymentNode) { + if (deploymentNode.hasChildren()) { + for (DeploymentNode child : deploymentNode.getChildren()) { + remove(child); + } } + + if (!deploymentNode.hasChildren() && !deploymentNode.hasSoftwareSystemInstances() && !deploymentNode.hasContainerInstances()) { + try { + Method method = Model.class.getDeclaredMethod("remove", DeploymentNode.class); + method.setAccessible(true); + method.invoke(model, deploymentNode); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + private boolean isElementAssociatedWithAnyViews(Element element) { + boolean result = false; + + // is the element used in any views + for (View view : viewSet.getViews()) { + if (view instanceof ModelView) { + ModelView modelView = (ModelView)view; + result = result | modelView.isElementInView(element); + } + } + + // is the element the scope of any views? + for (SystemContextView view : viewSet.getSystemContextViews()) { + result = result | view.getSoftwareSystem() == element; + } + + for (ContainerView view : viewSet.getContainerViews()) { + result = result | view.getSoftwareSystem() == element; + } + + for (ComponentView view : viewSet.getComponentViews()) { + result = result | view.getContainer() == element; + } + + for (DynamicView view : viewSet.getDynamicViews()) { + result = result | view.getElement() == element; + } + + for (DeploymentView view : viewSet.getDeploymentViews()) { + result = result | view.getSoftwareSystem() == element; + } + + for (ImageView view : viewSet.getImageViews()) { + result = result | view.getElement() == element; + } + + return result; } } \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/model/Container.java b/structurizr-core/src/com/structurizr/model/Container.java index a4c9fe29a..d6aff51b8 100644 --- a/structurizr-core/src/com/structurizr/model/Container.java +++ b/structurizr-core/src/com/structurizr/model/Container.java @@ -108,6 +108,10 @@ void add(Component component) { } } + void remove(Component component) { + components.remove(component); + } + /** * Gets the set of components within this software system. * @@ -123,6 +127,16 @@ void setComponents(Set components) { } } + /** + * Determines whether this container has any components. + * + * @return true if it has components, false otherwise + */ + @JsonIgnore + public boolean hasComponents() { + return !components.isEmpty(); + } + /** * Gets the component with the specified name. * diff --git a/structurizr-core/src/com/structurizr/model/DeploymentNode.java b/structurizr-core/src/com/structurizr/model/DeploymentNode.java index faa9c613a..abdd8308f 100644 --- a/structurizr-core/src/com/structurizr/model/DeploymentNode.java +++ b/structurizr-core/src/com/structurizr/model/DeploymentNode.java @@ -54,6 +54,14 @@ public SoftwareSystemInstance add(SoftwareSystem softwareSystem, String... deplo return softwareSystemInstance; } + void remove(SoftwareSystemInstance softwareSystemInstance) { + this.softwareSystemInstances.remove(softwareSystemInstance); + } + + void remove(ContainerInstance containerInstance) { + this.containerInstances.remove(containerInstance); + } + /** * Adds a container instance to this deployment node, replicating relationships. * @@ -131,6 +139,10 @@ public DeploymentNode addDeploymentNode(String name, String description, String return deploymentNode; } + void remove(DeploymentNode deploymentNode) { + children.remove(deploymentNode); + } + /** * Gets the DeploymentNode with the specified name. * @@ -318,11 +330,36 @@ void setInfrastructureNodes(Set infrastructureNodes) { } } + /** + * Determines whether this deployment node has any child deployment nodes. + * + * @return true if it has child deployment nodes, false otherwise + */ @JsonIgnore public boolean hasChildren() { return !children.isEmpty(); } + /** + * Determines whether this deployment node has any software system instances. + * + * @return true if it has software system instances, false otherwise + */ + @JsonIgnore + public boolean hasSoftwareSystemInstances() { + return !softwareSystemInstances.isEmpty(); + } + + /** + * Determines whether this deployment node has any container instances. + * + * @return true if it has container instances, false otherwise + */ + @JsonIgnore + public boolean hasContainerInstances() { + return !containerInstances.isEmpty(); + } + /** * Gets the set of software system instances associated with this deployment node. * diff --git a/structurizr-core/src/com/structurizr/model/Element.java b/structurizr-core/src/com/structurizr/model/Element.java index a22b8f895..a80ad2a75 100644 --- a/structurizr-core/src/com/structurizr/model/Element.java +++ b/structurizr-core/src/com/structurizr/model/Element.java @@ -195,10 +195,14 @@ boolean has(Relationship relationship) { return relationships.stream().anyMatch(r -> r.getDestination().equals(relationship.getDestination()) && r.getDescription().equals(relationship.getDescription())); } - void addRelationship(Relationship relationship) { + void add(Relationship relationship) { relationships.add(relationship); } + void remove(Relationship relationship) { + relationships.remove(relationship); + } + /** * Adds a unidirectional "uses" style relationship between this element and the specified custom element. * diff --git a/structurizr-core/src/com/structurizr/model/Model.java b/structurizr-core/src/com/structurizr/model/Model.java index 04479e9a9..73ac3a65d 100644 --- a/structurizr-core/src/com/structurizr/model/Model.java +++ b/structurizr-core/src/com/structurizr/model/Model.java @@ -227,8 +227,6 @@ public CustomElement addCustomElement(@Nonnull String name, @Nullable String met } } - - @Nonnull Container addContainer(SoftwareSystem parent, @Nonnull String name, @Nullable String description, @Nullable String technology) { if (parent.getContainerWithName(name) == null) { @@ -333,7 +331,7 @@ private boolean isChildOf(Element e1, Element e2) { private boolean addRelationship(Relationship relationship) { if (!relationship.getSource().has(relationship)) { relationship.setId(idGenerator.generateId(relationship)); - relationship.getSource().addRelationship(relationship); + relationship.getSource().add(relationship); addRelationshipToInternalStructures(relationship); return true; @@ -364,6 +362,10 @@ private void addRelationshipToInternalStructures(Relationship relationship) { idGenerator.found(relationship.getId()); } + private void removeRelationshipFromInternalStructures(Relationship relationship) { + relationshipsById.remove(relationship.getId()); + } + /** * Gets the set of all elements in this model. * @@ -618,11 +620,21 @@ private void hydrateRelationships(Element element) { /** * Determines whether this model contains the specified element. * - * @param element any element + * @param element an element * @return true, if the element is contained in this model */ public boolean contains(Element element) { - return elementsById.values().contains(element); + return elementsById.containsValue(element); + } + + /** + * Determines whether this model contains the specified relationship. + * + * @param relationship a relationship + * @return true, if the relationship is contained in this model + */ + public boolean contains(Relationship relationship) { + return relationshipsById.containsValue(relationship); } /** @@ -1101,4 +1113,116 @@ void setProperties(Map properties) { } } + /** + * Removes a custom element from the model. + * + * @param element the CustomElement object to remove + */ + void remove(CustomElement element) { + removeElement(element); + customElements.remove(element); + } + + /** + * Removes a person from the model. + * + * @param person the Person object to remove + */ + void remove(Person person) { + removeElement(person); + people.remove(person); + } + + /** + * Removes a software system from the model. + * + * @param softwareSystem the SoftwareSystem object to remove + */ + void remove(SoftwareSystem softwareSystem) { + removeElement(softwareSystem); + softwareSystems.remove(softwareSystem); + } + + /** + * Removes a container from the model. + * + * @param container the Container object to remove + */ + void remove(Container container) { + removeElement(container); + container.getSoftwareSystem().remove(container); + } + + /** + * Removes a component from the model. + * + * @param component the Component object to remove + */ + void remove(Component component) { + removeElement(component); + component.getContainer().remove(component); + } + + /** + * Removes a software system instance from the model. + * + * @param softwareSystemInstance the SoftwareSystemInstance object to remove + */ + void remove(SoftwareSystemInstance softwareSystemInstance) { + removeElement(softwareSystemInstance); + + Set deploymentNodes = getElements().stream().filter(e -> e instanceof DeploymentNode).map(e -> (DeploymentNode)e).collect(Collectors.toSet()); + for (DeploymentNode deploymentNode : deploymentNodes) { + deploymentNode.remove(softwareSystemInstance); + } + } + + /** + * Removes a container instance from the model. + * + * @param containerInstance the ContainerInstance object to remove + */ + void remove(ContainerInstance containerInstance) { + removeElement(containerInstance); + + Set deploymentNodes = getElements().stream().filter(e -> e instanceof DeploymentNode).map(e -> (DeploymentNode)e).collect(Collectors.toSet()); + for (DeploymentNode deploymentNode : deploymentNodes) { + deploymentNode.remove(containerInstance); + } + } + + /** + * Removes a deployment node from the model. + * + * @param deploymentNode the DeploymentNode object to remove + */ + void remove(DeploymentNode deploymentNode) { + removeElement(deploymentNode); + + if (deploymentNode.getParent() == null) { + deploymentNodes.remove(deploymentNode); + } else { + ((DeploymentNode)deploymentNode.getParent()).remove(deploymentNode); + } + } + + private void removeElement(Element element) { + if (element == null) { + throw new IllegalArgumentException("An element must be specified."); + } + + // remove any relationships to/from the element + for (Relationship relationship : getRelationships()) { + if (relationship.getSource() == element) { + removeRelationshipFromInternalStructures(relationship); + relationship.getSource().remove(relationship); + } else if (relationship.getDestination() == element) { + removeRelationshipFromInternalStructures(relationship); + relationship.getDestination().remove(relationship); + } + } + + elementsById.remove(element.getId()); + } + } \ No newline at end of file diff --git a/structurizr-core/src/com/structurizr/model/SoftwareSystem.java b/structurizr-core/src/com/structurizr/model/SoftwareSystem.java index b700c5b55..6b0135397 100644 --- a/structurizr-core/src/com/structurizr/model/SoftwareSystem.java +++ b/structurizr-core/src/com/structurizr/model/SoftwareSystem.java @@ -79,6 +79,16 @@ void setContainers(Set containers) { } } + /** + * Determines whether this software system has any containers. + * + * @return true if it has containers, false otherwise + */ + @JsonIgnore + public boolean hasContainers() { + return !containers.isEmpty(); + } + /** * Adds a container with the specified name. * @@ -118,6 +128,10 @@ public Container addContainer(@Nonnull String name, String description, String t return getModel().addContainer(this, name, description, technology); } + void remove(Container container) { + containers.remove(container); + } + /** * Gets the container with the specified name. * diff --git a/structurizr-core/test/unit/com/structurizr/WorkspaceTests.java b/structurizr-core/test/unit/com/structurizr/WorkspaceTests.java index 651057bbe..4a5230338 100644 --- a/structurizr-core/test/unit/com/structurizr/WorkspaceTests.java +++ b/structurizr-core/test/unit/com/structurizr/WorkspaceTests.java @@ -1,10 +1,11 @@ package com.structurizr; +import com.structurizr.configuration.WorkspaceScope; import com.structurizr.documentation.Decision; import com.structurizr.documentation.Format; -import com.structurizr.model.Component; -import com.structurizr.model.Container; -import com.structurizr.model.SoftwareSystem; +import com.structurizr.model.*; +import com.structurizr.view.SystemContextView; +import com.structurizr.view.SystemLandscapeView; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; @@ -73,4 +74,234 @@ void hydrate_DoesNotCrash() { workspace.hydrate(); } + @Test + void remove_WhenACustomElementIsUsedInAView() { + Workspace workspace = new Workspace("Name", "Description"); + + CustomElement element = workspace.getModel().addCustomElement("Name"); + workspace.getViews().createCustomView("key", "Title", "Description").addDefaultElements(); + assertEquals(1, workspace.getModel().getCustomElements().size()); + + workspace.remove(element); + assertEquals(1, workspace.getModel().getCustomElements().size()); + } + + @Test + void remove_WhenAPersonIsUsedInAView() { + Workspace workspace = new Workspace("Name", "Description"); + + Person person = workspace.getModel().addPerson("User"); + workspace.getViews().createSystemLandscapeView("key", "Description").addDefaultElements(); + assertEquals(1, workspace.getModel().getPeople().size()); + + workspace.remove(person); + assertEquals(1, workspace.getModel().getPeople().size()); + } + + @Test + void remove_WhenASoftwareSystemIsUsedInAView() { + Workspace workspace = new Workspace("Name", "Description"); + + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); + workspace.getViews().createSystemContextView(softwareSystem, "key", "Description").addDefaultElements(); + assertEquals(1, workspace.getModel().getElements().size()); + + workspace.remove(softwareSystem); + assertEquals(1, workspace.getModel().getElements().size()); + } + + @Test + void remove_WhenAContainerIsUsedInAView() { + Workspace workspace = new Workspace("Name", "Description"); + + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + workspace.getViews().createContainerView(softwareSystem, "key", "Description").addDefaultElements(); + assertEquals(2, workspace.getModel().getElements().size()); + + workspace.remove(softwareSystem); + assertEquals(2, workspace.getModel().getElements().size()); + + workspace.remove(container); + assertEquals(2, workspace.getModel().getElements().size()); + } + + @Test + void remove_WhenAComponentIsUsedInAView() { + Workspace workspace = new Workspace("Name", "Description"); + + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + Component component = container.addComponent("Component"); + workspace.getViews().createComponentView(container, "key", "Description").addDefaultElements(); + assertEquals(3, workspace.getModel().getElements().size()); + + workspace.remove(softwareSystem); + assertEquals(3, workspace.getModel().getElements().size()); + + workspace.remove(container); + assertEquals(3, workspace.getModel().getElements().size()); + + workspace.remove(component); + assertEquals(3, workspace.getModel().getElements().size()); + } + + @Test + void remove_WhenASoftwareSystemInstanceIsUsedInAView() { + Workspace workspace = new Workspace("Name", "Description"); + + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); + DeploymentNode deploymentNode = workspace.getModel().addDeploymentNode("Deployment Node"); + SoftwareSystemInstance softwareSystemInstance = deploymentNode.add(softwareSystem); + workspace.getViews().createDeploymentView("key", "Description").addDefaultElements(); + assertEquals(3, workspace.getModel().getElements().size()); + + workspace.remove(softwareSystem); + assertEquals(3, workspace.getModel().getElements().size()); + + workspace.remove(softwareSystemInstance); + assertEquals(3, workspace.getModel().getElements().size()); + } + + @Test + void remove_WhenAContainerInstanceIsUsedInAView() { + Workspace workspace = new Workspace("Name", "Description"); + + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + DeploymentNode deploymentNode = workspace.getModel().addDeploymentNode("Deployment Node"); + ContainerInstance containerInstance = deploymentNode.add(container); + workspace.getViews().createDeploymentView("key", "Description").addDefaultElements(); + assertEquals(4, workspace.getModel().getElements().size()); + + workspace.remove(softwareSystem); + assertEquals(4, workspace.getModel().getElements().size()); + + workspace.remove(container); + assertEquals(4, workspace.getModel().getElements().size()); + + workspace.remove(containerInstance); + assertEquals(4, workspace.getModel().getElements().size()); + } + + @Test + void remove_WhenAnElementIsTheScopeOfAContainerView() { + Workspace workspace = new Workspace("Name", "Description"); + + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); + workspace.getViews().createContainerView(softwareSystem, "key", "Description"); + assertEquals(1, workspace.getModel().getElements().size()); + + workspace.remove(softwareSystem); + assertEquals(1, workspace.getModel().getElements().size()); + } + + @Test + void remove_WhenAnElementIsTheScopeOfAComponentView() { + Workspace workspace = new Workspace("Name", "Description"); + + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + workspace.getViews().createComponentView(container, "key", "Description"); + assertEquals(2, workspace.getModel().getElements().size()); + + workspace.remove(softwareSystem); + assertEquals(2, workspace.getModel().getElements().size()); + } + + @Test + void remove_WhenAnElementIsTheScopeOfADynamicView() { + Workspace workspace = new Workspace("Name", "Description"); + + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); + workspace.getViews().createDynamicView(softwareSystem, "key", "Description"); + assertEquals(1, workspace.getModel().getElements().size()); + + workspace.remove(softwareSystem); + assertEquals(1, workspace.getModel().getElements().size()); + } + + @Test + void remove_WhenAnElementIsTheScopeOfADeploymentView() { + Workspace workspace = new Workspace("Name", "Description"); + + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); + workspace.getViews().createDeploymentView(softwareSystem, "key", "Description"); + assertEquals(1, workspace.getModel().getElements().size()); + + workspace.remove(softwareSystem); + assertEquals(1, workspace.getModel().getElements().size()); + } + + @Test + void remove_WhenAnElementIsTheScopeOfAnImageView() { + Workspace workspace = new Workspace("Name", "Description"); + + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); + workspace.getViews().createImageView(softwareSystem, "key"); + assertEquals(1, workspace.getModel().getElements().size()); + + workspace.remove(softwareSystem); + assertEquals(1, workspace.getModel().getElements().size()); + } + + @Test + void trim_WhenAllElementsAreUnused() { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getModel().setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategy()); + + CustomElement element = workspace.getModel().addCustomElement("Custom Element"); + Person user = workspace.getModel().addPerson("User"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); + Container webapp = softwareSystem.addContainer("Web Application"); + Container database = softwareSystem.addContainer("Database"); + Component component = webapp.addComponent("Component"); + user.uses(component, "uses"); + webapp.uses(database, "uses"); + + DeploymentNode live = workspace.getModel().addDeploymentNode("Live"); + DeploymentNode server1 = live.addDeploymentNode("Server 1"); + DeploymentNode server2 = live.addDeploymentNode("Server 2"); + ContainerInstance webappInstance = server1.add(webapp); + ContainerInstance databaseInstance = server2.add(database); + + DeploymentNode dev = workspace.getModel().addDeploymentNode("Dev"); + SoftwareSystemInstance softwareSystemInstance = dev.add(softwareSystem); + + assertEquals(13, workspace.getModel().getElements().size()); + assertEquals(5, workspace.getModel().getRelationships().size()); + + workspace.trim(); + assertEquals(0, workspace.getModel().getElements().size()); + } + + @Test + void trim_WhenSomeElementsAreUnused() { + Workspace workspace = new Workspace("Name", "Description"); + + SoftwareSystem a = workspace.getModel().addSoftwareSystem("A"); + SoftwareSystem b = workspace.getModel().addSoftwareSystem("B"); + SoftwareSystem c = workspace.getModel().addSoftwareSystem("C"); + SoftwareSystem d = workspace.getModel().addSoftwareSystem("D"); + + // a -> b -> c -> d + Relationship ab = a.uses(b, "uses"); + Relationship bc = b.uses(c, "uses"); + Relationship cd = c.uses(d, "uses"); + + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "Description"); + view.add(b); + view.add(c); + + assertEquals(4, workspace.getModel().getElements().size()); + assertEquals(3, workspace.getModel().getRelationships().size()); + + workspace.trim(); + assertEquals(2, workspace.getModel().getElements().size()); + assertEquals(1, workspace.getModel().getRelationships().size()); + assertTrue(workspace.getModel().contains(b)); + assertTrue(workspace.getModel().contains(c)); + assertTrue(workspace.getModel().contains(bc)); + } + } \ No newline at end of file From 3e63fb8c5704bc1060ced934f83952a4e3100f81 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 9 Jan 2024 14:53:20 +0000 Subject: [PATCH 437/717] Combines source from dsl/export/import/graphviz repos. --- README.md | 14 +- build.gradle | 15 +- changelog.md | 6 + docs/changelog.md | 402 ---- settings.gradle | 6 +- structurizr-client/README.md | 5 + structurizr-client/build.gradle | 4 +- .../api/BackwardsCompatibilityTests.java | 2 +- .../StructurizrClientIntegrationTests.java | 0 .../api/WorkspaceRulesValidationTests.java | 2 +- .../structurizr-31-workspace.json | 0 .../structurizr-36141-workspace.json | 0 .../structurizr-39459-workspace.json | 0 .../ChildDeploymentNodeNamesAreNotUnique.json | 0 .../ComponentNamesAreNotUnique.json | 0 ...ithComponentViewIsMissingFromTheModel.json | 0 .../ContainerNamesAreNotUnique.json | 0 ...dWithDynamicViewIsMissingFromTheModel.json | 0 .../ElementIdsAreNotUnique.json | 0 ...ReferencedByViewIsMissingFromTheModel.json | 0 ...pleAndSoftwareSystemNamesAreNotUnique.json | 0 .../RelationshipDescriptionsAreNotUnique.json | 0 .../RelationshipIdsAreNotUnique.json | 0 ...ReferencedByViewIsMissingFromTheModel.json | 0 ...ithContainerViewIsMissingFromTheModel.json | 0 ...thDeploymentViewIsMissingFromTheModel.json | 0 ...ystemContextViewIsMissingFromTheModel.json | 0 ...pLevelDeploymentNodeNamesAreNotUnique.json | 0 ...ueButTheyExistInDifferentEnvironments.json | 0 ...FilteredViewIsMissingFromTheWorkspace.json | 0 .../ViewKeysAreNotUnique.json | 0 .../structurizr/api/AbstractApiClient.java | 0 .../com/structurizr/api/AdminApiClient.java | 0 .../com/structurizr/api/ApiResponse.java | 0 .../HashBasedMessageAuthenticationCode.java | 0 .../api/HmacAuthorizationHeader.java | 0 .../com/structurizr/api/HmacContent.java | 0 .../com/structurizr/api/HttpHeaders.java | 0 .../java}/com/structurizr/api/Md5Digest.java | 0 .../structurizr/api/StructurizrClient.java | 0 .../api/StructurizrClientException.java | 0 .../structurizr/api/WorkspaceApiClient.java | 0 .../structurizr/api/WorkspaceMetadata.java | 0 .../java}/com/structurizr/api/Workspaces.java | 0 .../encryption/AesEncryptionStrategy.java | 0 .../encryption/EncryptedWorkspace.java | 0 .../encryption/EncryptionLocation.java | 0 .../encryption/EncryptionStrategy.java | 0 .../com/structurizr/io/WorkspaceReader.java | 0 .../io/WorkspaceReaderException.java | 0 .../com/structurizr/io/WorkspaceWriter.java | 0 .../io/WorkspaceWriterException.java | 0 .../io/json/AbstractJsonReader.java | 0 .../io/json/AbstractJsonWriter.java | 0 .../io/json/EncryptedJsonReader.java | 0 .../io/json/EncryptedJsonWriter.java | 0 .../com/structurizr/io/json/JsonReader.java | 0 .../com/structurizr/io/json/JsonWriter.java | 0 .../com/structurizr/util/WorkspaceUtils.java | 0 .../com/structurizr/view/ThemeUtils.java | 0 .../com/structurizr/api/ApiResponseTests.java | 0 ...shBasedMessageAuthenticationCodeTests.java | 0 .../api/HmacAuthorizationHeaderTests.java | 0 .../com/structurizr/api/HmacContentTests.java | 0 .../com/structurizr/api/Md5DigestTests.java | 0 .../api/StructurizrClientTests.java | 0 .../AesEncryptionStrategyTests.java | 0 .../encryption/EncryptedWorkspaceTests.java | 0 .../encryption/MockEncryptionStrategy.java | 0 .../io/json/EncryptedJsonTests.java | 0 .../io/json/EncryptedJsonWriterTests.java | 0 .../com/structurizr/io/json/JsonTests.java | 0 .../structurizr/io/json/JsonWriterTests.java | 0 .../structurizr/util/WorkspaceUtilsTests.java | 0 .../com/structurizr/view/ThemeUtilsTests.java | 0 structurizr-core/README.md | 6 + .../com/structurizr/AbstractWorkspace.java | 0 .../java}/com/structurizr/PropertyHolder.java | 0 .../java}/com/structurizr/Workspace.java | 0 .../WorkspaceValidationException.java | 0 .../com/structurizr/configuration/Role.java | 0 .../com/structurizr/configuration/User.java | 0 .../structurizr/configuration/Visibility.java | 0 .../configuration/WorkspaceConfiguration.java | 0 .../configuration/WorkspaceScope.java | 0 .../structurizr/documentation/Decision.java | 0 .../documentation/Documentable.java | 0 .../documentation/Documentation.java | 0 .../documentation/DocumentationContent.java | 0 .../com/structurizr/documentation/Format.java | 0 .../com/structurizr/documentation/Image.java | 0 .../structurizr/documentation/Section.java | 0 .../AbstractImpliedRelationshipsStrategy.java | 0 .../model/CanonicalNameGenerator.java | 0 .../com/structurizr/model/Component.java | 0 .../com/structurizr/model/Constants.java | 0 .../com/structurizr/model/Container.java | 0 .../structurizr/model/ContainerInstance.java | 0 ...psUnlessAnyRelationshipExistsStrategy.java | 0 ...sUnlessSameRelationshipExistsStrategy.java | 0 .../com/structurizr/model/CustomElement.java | 0 .../DefaultImpliedRelationshipsStrategy.java | 0 .../structurizr/model/DeploymentElement.java | 0 .../com/structurizr/model/DeploymentNode.java | 10 + .../java}/com/structurizr/model/Element.java | 0 .../com/structurizr/model/Enterprise.java | 0 .../structurizr/model/GroupableElement.java | 0 .../structurizr/model/HttpHealthCheck.java | 0 .../com/structurizr/model/IdGenerator.java | 0 .../model/ImpliedRelationshipsStrategy.java | 0 .../structurizr/model/InfrastructureNode.java | 0 .../structurizr/model/InteractionStyle.java | 0 .../java}/com/structurizr/model/Location.java | 0 .../java}/com/structurizr/model/Model.java | 0 .../com/structurizr/model/ModelItem.java | 0 .../java}/com/structurizr/model/Person.java | 0 .../com/structurizr/model/Perspective.java | 0 .../com/structurizr/model/Relationship.java | 0 .../SequentialIntegerIdGeneratorStrategy.java | 0 .../com/structurizr/model/SoftwareSystem.java | 0 .../model/SoftwareSystemInstance.java | 0 .../model/StaticStructureElement.java | 0 .../model/StaticStructureElementInstance.java | 0 .../java}/com/structurizr/model/Tags.java | 0 .../com/structurizr/util/ImageUtils.java | 0 .../java}/com/structurizr/util/MapUtils.java | 0 .../com/structurizr/util/StringUtils.java | 0 .../java}/com/structurizr/util/TagUtils.java | 0 .../java}/com/structurizr/util/Url.java | 0 .../LandscapeWorkspaceScopeValidator.java | 0 ...SoftwareSystemWorkspaceScopeValidator.java | 0 .../UndefinedWorkspaceScopeValidator.java | 0 .../WorkspaceScopeValidationException.java | 0 .../validation/WorkspaceScopeValidator.java | 0 .../WorkspaceScopeValidatorFactory.java | 0 .../com/structurizr/view/AbstractStyle.java | 0 .../com/structurizr/view/AnimatedView.java | 0 .../java}/com/structurizr/view/Animation.java | 0 .../com/structurizr/view/AutomaticLayout.java | 0 .../java}/com/structurizr/view/Border.java | 0 .../java}/com/structurizr/view/Branding.java | 0 .../java}/com/structurizr/view/Color.java | 0 .../com/structurizr/view/ComponentView.java | 0 .../com/structurizr/view/Configuration.java | 0 .../com/structurizr/view/ContainerView.java | 0 .../com/structurizr/view/CustomView.java | 0 .../view/DefaultLayoutMergeStrategy.java | 0 .../com/structurizr/view/DeploymentView.java | 0 .../com/structurizr/view/Dimensions.java | 0 .../com/structurizr/view/DynamicView.java | 0 .../ElementNotPermittedInViewException.java | 0 .../com/structurizr/view/ElementStyle.java | 0 .../com/structurizr/view/ElementView.java | 0 .../com/structurizr/view/FilterMode.java | 0 .../com/structurizr/view/FilteredView.java | 0 .../java}/com/structurizr/view/Font.java | 0 .../java}/com/structurizr/view/ImageView.java | 0 .../structurizr/view/LayoutMergeStrategy.java | 0 .../java}/com/structurizr/view/LineStyle.java | 0 .../com/structurizr/view/MetadataSymbols.java | 0 .../java}/com/structurizr/view/ModelView.java | 0 .../java}/com/structurizr/view/PaperSize.java | 0 .../view/ParallelSequenceCounter.java | 0 .../structurizr/view/RelationshipStyle.java | 0 .../structurizr/view/RelationshipView.java | 0 .../java}/com/structurizr/view/Routing.java | 0 .../com/structurizr/view/SequenceCounter.java | 0 .../com/structurizr/view/SequenceNumber.java | 0 .../java}/com/structurizr/view/Shape.java | 0 .../com/structurizr/view/StaticView.java | 0 .../java}/com/structurizr/view/Styles.java | 0 .../structurizr/view/SystemContextView.java | 0 .../structurizr/view/SystemLandscapeView.java | 0 .../com/structurizr/view/Terminology.java | 0 .../java}/com/structurizr/view/Theme.java | 0 .../java}/com/structurizr/view/Vertex.java | 0 .../java}/com/structurizr/view/View.java | 0 .../java}/com/structurizr/view/ViewSet.java | 0 .../com/structurizr/view/ViewSortOrder.java | 0 .../AbstractWorkspaceTestBase.java | 0 .../java}/com/structurizr/WorkspaceTests.java | 0 .../structurizr/configuration/UserTests.java | 0 .../WorkspaceConfigurationTests.java | 0 .../documentation/DecisionTests.java | 0 .../documentation/DocumentationTests.java | 0 .../documentation/SectionTests.java | 0 .../com/structurizr/model/ComponentTests.java | 0 .../model/ContainerInstanceTests.java | 0 .../com/structurizr/model/ContainerTests.java | 0 ...essAnyRelationshipExistsStrategyTests.java | 0 ...ssSameRelationshipExistsStrategyTests.java | 0 .../structurizr/model/CustomElementTests.java | 0 ...aultImpliedRelationshipsStrategyTests.java | 0 .../model/DeploymentNodeTests.java | 0 .../com/structurizr/model/ElementTests.java | 0 .../model/GroupableElementTests.java | 0 .../model/HttpHealthCheckTests.java | 0 .../model/InfrastructureNodeTests.java | 0 .../com/structurizr/model/ModelItemTests.java | 0 .../com/structurizr/model/ModelTests.java | 0 .../com/structurizr/model/PersonTests.java | 0 .../structurizr/model/RelationshipTests.java | 0 .../model/SoftwareSystemInstanceTests.java | 0 .../model/SoftwareSystemTests.java | 0 .../com/structurizr/util/ImageUtilsTests.java | 8 +- .../structurizr/util/StringUtilsTests.java | 0 .../java}/com/structurizr/util/UrlTests.java | 0 ...LandscapeWorkspaceScopeValidatorTests.java | 0 ...areSystemWorkspaceScopeValidatorTests.java | 0 .../WorkspaceScopeValidatorFactoryTests.java | 0 .../view/AutomaticLayoutTests.java | 0 .../com/structurizr/view/BrandingTests.java | 0 .../com/structurizr/view/ColorTests.java | 0 .../structurizr/view/ComponentViewTests.java | 0 .../structurizr/view/ConfigurationTests.java | 0 .../structurizr/view/ContainerViewTests.java | 0 .../view/DefaultLayoutMergeStrategyTests.java | 0 .../structurizr/view/DeploymentViewTests.java | 0 .../com/structurizr/view/DimensionsTests.java | 0 .../structurizr/view/DynamicViewTests.java | 0 .../structurizr/view/ElementStyleTests.java | 0 .../structurizr/view/ElementViewTests.java | 0 .../structurizr/view/FilteredViewTests.java | 0 .../java}/com/structurizr/view/FontTests.java | 0 .../com/structurizr/view/ImageViewTests.java | 0 .../com/structurizr/view/PaperSizeTests.java | 0 .../view/RelationshipStyleTests.java | 0 .../view/SequenceCounterTests.java | 0 .../structurizr/view/SequenceNumberTests.java | 0 .../com/structurizr/view/StaticViewTests.java | 0 .../com/structurizr/view/StylesTests.java | 0 .../view/SystemContextViewTests.java | 0 .../view/SystemLandscapeViewTests.java | 0 .../structurizr/view/TerminologyTests.java | 0 .../com/structurizr/view/VertexTests.java | 0 .../com/structurizr/view/ViewSetTests.java | 0 .../java}/com/structurizr/view/ViewTests.java | 0 .../test/resources}/structurizr-logo.png | Bin structurizr-dsl/README.md | 11 + structurizr-dsl/build.gradle | 14 + .../dsl/AbstractExpressionParser.java | 344 +++ .../com/structurizr/dsl/AbstractParser.java | 42 + .../dsl/AbstractRelationshipParser.java | 37 + .../structurizr/dsl/AbstractViewParser.java | 4 + .../java/com/structurizr/dsl/AdrsParser.java | 80 + .../com/structurizr/dsl/AutoLayoutParser.java | 70 + .../structurizr/dsl/BrandingDslContext.java | 25 + .../com/structurizr/dsl/BrandingParser.java | 71 + .../structurizr/dsl/CommentDslContext.java | 10 + .../structurizr/dsl/ComponentDslContext.java | 42 + .../com/structurizr/dsl/ComponentParser.java | 77 + .../dsl/ComponentViewDslContext.java | 11 + .../structurizr/dsl/ComponentViewParser.java | 62 + .../dsl/ConfigurationDslContext.java | 15 + .../structurizr/dsl/ConfigurationParser.java | 61 + .../java/com/structurizr/dsl/Constant.java | 21 + .../com/structurizr/dsl/ConstantParser.java | 33 + .../structurizr/dsl/ContainerDslContext.java | 52 + .../dsl/ContainerInstanceDslContext.java | 42 + .../dsl/ContainerInstanceParser.java | 64 + .../com/structurizr/dsl/ContainerParser.java | 77 + .../dsl/ContainerViewDslContext.java | 11 + .../structurizr/dsl/ContainerViewParser.java | 62 + .../dsl/CustomElementDslContext.java | 41 + .../structurizr/dsl/CustomElementParser.java | 53 + .../dsl/CustomViewAnimationDslContext.java | 26 + .../dsl/CustomViewAnimationStepParser.java | 51 + .../dsl/CustomViewContentParser.java | 97 + .../structurizr/dsl/CustomViewDslContext.java | 29 + .../dsl/CustomViewExpressionParser.java | 45 + .../com/structurizr/dsl/CustomViewParser.java | 48 + .../structurizr/dsl/DefaultViewParser.java | 12 + .../dsl/DeploymentEnvironment.java | 49 + .../dsl/DeploymentEnvironmentDslContext.java | 31 + .../dsl/DeploymentEnvironmentParser.java | 21 + .../com/structurizr/dsl/DeploymentGroup.java | 35 + .../dsl/DeploymentGroupParser.java | 21 + .../dsl/DeploymentNodeDslContext.java | 54 + .../structurizr/dsl/DeploymentNodeParser.java | 140 ++ .../DeploymentViewAnimationDslContext.java | 26 + .../DeploymentViewAnimationStepParser.java | 60 + .../dsl/DeploymentViewContentParser.java | 154 ++ .../dsl/DeploymentViewDslContext.java | 29 + .../dsl/DeploymentViewExpressionParser.java | 70 + .../structurizr/dsl/DeploymentViewParser.java | 85 + .../java/com/structurizr/dsl/DocsParser.java | 80 + .../java/com/structurizr/dsl/DslContext.java | 126 ++ .../java/com/structurizr/dsl/DslLine.java | 24 + .../com/structurizr/dsl/DslParserContext.java | 38 + .../java/com/structurizr/dsl/DslUtils.java | 48 + .../dsl/DynamicViewContentParser.java | 100 + .../dsl/DynamicViewDslContext.java | 27 + ...DynamicViewParallelSequenceDslContext.java | 21 + .../structurizr/dsl/DynamicViewParser.java | 89 + .../dsl/DynamicViewRelationshipContext.java | 26 + .../dsl/DynamicViewRelationshipParser.java | 21 + .../com/structurizr/dsl/ElementGroup.java | 74 + .../dsl/ElementStyleDslContext.java | 46 + .../structurizr/dsl/ElementStyleParser.java | 324 +++ .../structurizr/dsl/EnterpriseDslContext.java | 23 + .../com/structurizr/dsl/EnterpriseParser.java | 30 + .../dsl/ExplicitRelationshipParser.java | 72 + .../dsl/ExternalScriptDslContext.java | 41 + .../java/com/structurizr/dsl/FileUtils.java | 46 + .../dsl/FilteredViewDslContext.java | 25 + .../structurizr/dsl/FilteredViewParser.java | 88 + .../java/com/structurizr/dsl/GroupParser.java | 35 + .../structurizr/dsl/GroupableDslContext.java | 23 + .../dsl/GroupableElementDslContext.java | 17 + .../structurizr/dsl/HealthCheckParser.java | 61 + .../java/com/structurizr/dsl/IconUtils.java | 38 + .../com/structurizr/dsl/IdentifierScope.java | 8 + .../dsl/IdentifierScopeParser.java | 30 + .../structurizr/dsl/IdentifiersRegister.java | 222 ++ .../dsl/ImageViewContentParser.java | 163 ++ .../structurizr/dsl/ImageViewDslContext.java | 50 + .../com/structurizr/dsl/ImageViewParser.java | 56 + .../dsl/ImplicitRelationshipParser.java | 52 + .../dsl/ImpliedRelationshipsParser.java | 34 + .../com/structurizr/dsl/IncludeParser.java | 73 + .../structurizr/dsl/IncludedDslContext.java | 33 + .../com/structurizr/dsl/IncludedFile.java | 24 + .../dsl/InfrastructureNodeDslContext.java | 36 + .../dsl/InfrastructureNodeParser.java | 71 + .../dsl/InlineScriptDslContext.java | 55 + .../com/structurizr/dsl/ModelDslContext.java | 37 + .../structurizr/dsl/ModelItemDslContext.java | 17 + .../com/structurizr/dsl/ModelItemParser.java | 79 + .../dsl/ModelItemPerspectivesDslContext.java | 22 + .../dsl/ModelViewContentParser.java | 19 + .../structurizr/dsl/ModelViewDslContext.java | 15 + .../com/structurizr/dsl/PersonDslContext.java | 41 + .../com/structurizr/dsl/PersonParser.java | 59 + .../com/structurizr/dsl/PluginDslContext.java | 42 + .../com/structurizr/dsl/PluginParser.java | 43 + .../structurizr/dsl/PropertiesDslContext.java | 22 + .../com/structurizr/dsl/PropertyParser.java | 25 + .../java/com/structurizr/dsl/RefParser.java | 52 + .../dsl/RelationshipDslContext.java | 33 + .../dsl/RelationshipStyleDslContext.java | 33 + .../dsl/RelationshipStyleParser.java | 239 ++ .../com/structurizr/dsl/ScriptDslContext.java | 77 + .../com/structurizr/dsl/ScriptParser.java | 66 + .../dsl/SoftwareSystemDslContext.java | 51 + .../dsl/SoftwareSystemInstanceDslContext.java | 42 + .../dsl/SoftwareSystemInstanceParser.java | 65 + .../structurizr/dsl/SoftwareSystemParser.java | 59 + ...ticStructureElementInstanceDslContext.java | 9 + .../dsl/StaticViewAnimationDslContext.java | 26 + .../dsl/StaticViewAnimationStepParser.java | 50 + .../dsl/StaticViewContentParser.java | 117 + .../structurizr/dsl/StaticViewDslContext.java | 29 + .../dsl/StaticViewExpressionParser.java | 64 + .../dsl/StructurizrDslExpressions.java | 22 + .../structurizr/dsl/StructurizrDslParser.java | 1033 +++++++++ .../dsl/StructurizrDslParserException.java | 49 + .../structurizr/dsl/StructurizrDslPlugin.java | 15 + .../dsl/StructurizrDslPluginContext.java | 88 + .../dsl/StructurizrDslScriptContext.java | 76 + .../structurizr/dsl/StructurizrDslTokens.java | 109 + .../com/structurizr/dsl/StylesDslContext.java | 13 + .../dsl/SystemContextViewDslContext.java | 11 + .../dsl/SystemContextViewParser.java | 62 + .../dsl/SystemLandscapeViewDslContext.java | 11 + .../dsl/SystemLandscapeViewParser.java | 43 + .../dsl/TerminologyDslContext.java | 19 + .../structurizr/dsl/TerminologyParser.java | 79 + .../java/com/structurizr/dsl/ThemeParser.java | 41 + .../java/com/structurizr/dsl/Tokenizer.java | 58 + .../main/java/com/structurizr/dsl/Tokens.java | 41 + .../com/structurizr/dsl/UserRoleParser.java | 39 + .../com/structurizr/dsl/UsersDslContext.java | 10 + .../com/structurizr/dsl/ViewDslContext.java | 17 + .../java/com/structurizr/dsl/ViewParser.java | 51 + .../com/structurizr/dsl/ViewsDslContext.java | 25 + .../structurizr/dsl/WorkspaceDslContext.java | 21 + .../com/structurizr/dsl/WorkspaceParser.java | 138 ++ .../dsl/AbstractExpressionParserTests.java | 506 +++++ .../com/structurizr/dsl/AbstractTests.java | 30 + .../com/structurizr/dsl/AdrsParserTests.java | 22 + .../dsl/AutoLayoutParserTests.java | 111 + .../structurizr/dsl/BrandingParserTests.java | 140 ++ .../structurizr/dsl/ComponentParserTests.java | 127 ++ .../dsl/ComponentViewParserTests.java | 111 + .../dsl/ConfigurationParserTests.java | 86 + .../structurizr/dsl/ConstantParserTests.java | 59 + .../dsl/ContainerInstanceParserTests.java | 147 ++ .../structurizr/dsl/ContainerParserTests.java | 122 + .../dsl/ContainerViewParserTests.java | 111 + .../com/structurizr/dsl/CookbookTests.java | 33 + .../dsl/CustomElementParserTests.java | 79 + .../CustomViewAnimationStepParserTests.java | 32 + .../dsl/CustomViewContentParserTests.java | 357 +++ .../dsl/CustomViewParserTests.java | 75 + .../dsl/DefaultViewParserTests.java | 24 + .../dsl/DeploymentEnvironmentParserTests.java | 38 + .../dsl/DeploymentGroupParserTests.java | 38 + .../dsl/DeploymentNodeParserTests.java | 222 ++ ...eploymentViewAnimationStepParserTests.java | 32 + .../dsl/DeploymentViewContentParserTests.java | 576 +++++ .../DeploymentViewExpressionParserTests.java | 220 ++ .../dsl/DeploymentViewParserTests.java | 209 ++ .../com/structurizr/dsl/DocsParserTests.java | 22 + .../java/com/structurizr/dsl/DslTests.java | 1049 +++++++++ .../dsl/DynamicViewContentParserTests.java | 187 ++ .../dsl/DynamicViewParserTests.java | 197 ++ .../DynamicViewRelationshipParserTests.java | 52 + .../dsl/ElementStyleParserTests.java | 546 +++++ .../dsl/EnterpriseParserTests.java | 63 + .../dsl/ExplicitRelationshipParserTests.java | 232 ++ .../dsl/ExternalScriptDslContextTests.java | 21 + .../dsl/FilteredViewParserTests.java | 139 ++ .../com/structurizr/dsl/GroupParserTests.java | 72 + .../dsl/HealthCheckParserTests.java | 127 ++ .../dsl/IdentifierRegisterTests.java | 63 + .../dsl/IdentifierScopeParserTests.java | 46 + .../structurizr/dsl/ImageViewParserTests.java | 62 + .../dsl/ImplicitRelationshipParserTests.java | 179 ++ .../dsl/ImpliedRelationshipsParserTests.java | 46 + .../structurizr/dsl/IncludeParserTests.java | 32 + .../dsl/InfrastructureNodeParserTests.java | 134 ++ .../dsl/InlineScriptDslContextTests.java | 23 + .../structurizr/dsl/ModelDslContextTests.java | 77 + .../structurizr/dsl/ModelItemParserTests.java | 165 ++ .../structurizr/dsl/PersonParserTests.java | 83 + .../dsl/PluginDslContextTests.java | 23 + .../structurizr/dsl/PluginParserTests.java | 38 + .../structurizr/dsl/PropertyParserTests.java | 31 + .../com/structurizr/dsl/RefParserTests.java | 79 + .../dsl/RelationshipStyleParserTests.java | 392 ++++ .../structurizr/dsl/ScriptParserTests.java | 34 + .../SoftwareSystemInstanceParserTests.java | 143 ++ .../dsl/SoftwareSystemParserTests.java | 83 + .../StaticViewAnimationStepParserTests.java | 32 + .../dsl/StaticViewContentParserTests.java | 790 +++++++ .../dsl/StaticViewExpressionParserTests.java | 199 ++ .../dsl/SystemContextViewParserTests.java | 109 + .../dsl/SystemLandscapeViewParserTests.java | 60 + .../dsl/TerminologyParserTests.java | 148 ++ .../com/structurizr/dsl/ThemeParserTests.java | 84 + .../com/structurizr/dsl/TokenizerTests.java | 71 + .../java/com/structurizr/dsl/TokensTests.java | 23 + .../structurizr/dsl/UserRoleParserTests.java | 80 + .../com/structurizr/dsl/ViewParserTests.java | 156 ++ .../structurizr/dsl/WorkspaceParserTests.java | 105 + .../example/ExampleDecisionImporter.java | 6 + .../example/ExampleDocumentationImporter.java | 6 + .../0001-record-architecture-decisions.md | 19 + .../adrs/0002-implement-as-shell-scripts.md | 28 + .../0003-single-command-with-subcommands.md | 45 + .../dsl/adrs/adrs/0004-markdown-format.md | 40 + .../dsl/adrs/adrs/0005-help-comments.md | 42 + ...n-in-other-version-control-repositories.md | 41 + ...-config-executable-to-get-configuration.md | 31 + .../0008-use-iso-8601-format-for-dates.md | 43 + .../dsl/adrs/adrs/0009-help-scripts.md | 28 + .../dsl/adrs/adrs/0010-asciidoc-format.md | 21 + .../src/test/resources/dsl/adrs/workspace.dsl | 19 + .../dsl/amazon-web-services-local.dsl | 65 + .../resources/dsl/amazon-web-services.dsl | 84 + .../src/test/resources/dsl/big-bank-plc.dsl | 249 ++ .../big-bank-plc/internet-banking-system.dsl | 189 ++ .../0001-record-architecture-decisions.md | 19 + .../model/internet-banking-system/details.dsl | 15 + .../docs/01-context.md | 11 + .../docs/02-containers.md | 21 + .../docs/03-development-environment.adoc | 5 + .../docs/04-deployment.adoc | 5 + .../model/internet-banking-system/summary.dsl | 1 + .../model/people-and-software-systems.dsl | 25 + .../dsl/big-bank-plc/system-landscape.dsl | 23 + .../dsl/big-bank-plc/views/styles-people.dsl | 11 + .../dsl/deployment-environment-empty.dsl | 9 + .../test/resources/dsl/deployment-groups.dsl | 48 + .../resources/dsl/docs/docs/01-context.md | 5 + .../src/test/resources/dsl/docs/workspace.dsl | 31 + .../dsl/dynamic-view-with-custom-elements.dsl | 19 + ...namic-view-with-explicit-relationships.dsl | 37 + .../src/test/resources/dsl/dynamic.dsl | 24 + .../dsl/exclude-implied-relationship.dsl | 22 + .../resources/dsl/exclude-relationships.dsl | 20 + .../src/test/resources/dsl/extend/1.dsl | 7 + .../src/test/resources/dsl/extend/2.dsl | 7 + .../src/test/resources/dsl/extend/3.dsl | 7 + .../src/test/resources/dsl/extend/4.dsl | 9 + .../extend/extend-workspace-from-dsl-file.dsl | 12 + .../extend/extend-workspace-from-dsl-url.dsl | 12 + .../extend-workspace-from-json-file.dsl | 18 + .../extend/extend-workspace-from-json-url.dsl | 18 + .../test/resources/dsl/extend/workspace.dsl | 18 + .../test/resources/dsl/extend/workspace.json | 73 + .../src/test/resources/dsl/filteredviews.dsl | 41 + .../resources/dsl/financial-risk-system.dsl | 67 + .../resources/dsl/getting-started-short.dsl | 17 + .../test/resources/dsl/getting-started.dsl | 19 + .../src/test/resources/dsl/group-url.dsl | 11 + .../resources/dsl/group-without-brace.dsl | 7 + .../src/test/resources/dsl/groups-nested.dsl | 47 + .../src/test/resources/dsl/groups.dsl | 51 + ...cal-identifiers-and-deployment-nodes-1.dsl | 40 + ...cal-identifiers-and-deployment-nodes-2.dsl | 18 + ...cal-identifiers-and-deployment-nodes-3.dsl | 22 + ...erarchical-identifiers-when-unassigned.dsl | 19 + .../dsl/hierarchical-identifiers.dsl | 16 + .../src/test/resources/dsl/identifiers.dsl | 14 + .../resources/dsl/image-views/diagram.dot | 1 + .../resources/dsl/image-views/diagram.mmd | 2 + .../resources/dsl/image-views/diagram.puml | 3 + .../test/resources/dsl/image-views/logo.png | Bin 0 -> 9262 bytes .../dsl/image-views/workspace-via-file.dsl | 27 + .../dsl/image-views/workspace-via-url.dsl | 30 + .../test/resources/dsl/include-directory.dsl | 14 + .../src/test/resources/dsl/include-file.dsl | 7 + .../dsl/include-implied-relationship.dsl | 23 + .../src/test/resources/dsl/include-url.dsl | 7 + .../resources/dsl/include/docs/section.md | 3 + .../src/test/resources/dsl/include/model.dsl | 3 + .../include/model/software-system/model.dsl | 3 + .../src/test/resources/dsl/iso-8859.dsl | 5 + .../src/test/resources/dsl/logo.png | Bin 0 -> 9262 bytes .../resources/dsl/multi-line-with-error.dsl | 12 + .../src/test/resources/dsl/multi-line.dsl | 10 + .../resources/dsl/multiple-model-tokens.dsl | 11 + .../resources/dsl/multiple-view-tokens.dsl | 19 + .../dsl/multiple-workspace-tokens.dsl | 15 + .../src/test/resources/dsl/parallel1.dsl | 33 + .../src/test/resources/dsl/parallel2.dsl | 34 + .../resources/dsl/plugin-with-parameters.dsl | 7 + .../dsl/plugin-without-parameters.dsl | 5 + .../plugins/structurizr-dsl-plugin-1.0.0.jar | Bin 0 -> 1214 bytes .../src/test/resources/dsl/ref.dsl | 45 + .../dsl/relationship-already-exists.dsl | 13 + .../dsl/script-external-with-parameters.dsl | 7 + .../test/resources/dsl/script-external.dsl | 7 + .../resources/dsl/script-in-dynamic-view.dsl | 16 + .../src/test/resources/dsl/script-inline.dsl | 42 + .../src/test/resources/dsl/shapes.dsl | 89 + .../resources/dsl/test-with-parameters.groovy | 4 + .../src/test/resources/dsl/test.dsl | 345 +++ .../src/test/resources/dsl/test.groovy | 4 + .../src/test/resources/dsl/test.js | 2 + .../src/test/resources/dsl/test.kts | 2 + .../src/test/resources/dsl/test.rb | 2 + .../src/test/resources/dsl/this.dsl | 71 + .../dsl/unexpected-tokens-after-workspace.dsl | 4 + .../unexpected-tokens-before-workspace.dsl | 4 + .../dsl/unexpected-tokens-in-workspace.dsl | 5 + .../src/test/resources/dsl/utf8.dsl | 7 + .../test/resources/dsl/views-without-keys.dsl | 18 + .../resources/dsl/workspace-properties.dsl | 7 + .../test/resources/dsl/workspace-with-bom.dsl | 29 + structurizr-export/README.md | 9 + structurizr-export/build.gradle | 10 + .../export/AbstractDiagramExporter.java | 748 ++++++ .../structurizr/export/AbstractExporter.java | 119 + .../export/AbstractWorkspaceExporter.java | 4 + .../java/com/structurizr/export/Diagram.java | 51 + .../structurizr/export/DiagramExporter.java | 17 + .../java/com/structurizr/export/Exporter.java | 4 + .../com/structurizr/export/IndentType.java | 8 + .../structurizr/export/IndentingWriter.java | 62 + .../java/com/structurizr/export/Legend.java | 15 + .../structurizr/export/WorkspaceExport.java | 17 + .../structurizr/export/WorkspaceExporter.java | 15 + .../structurizr/export/dot/DOTDiagram.java | 17 + .../structurizr/export/dot/DOTExporter.java | 434 ++++ .../java/com/structurizr/export/dot/README.md | 6 + .../structurizr/export/dot/RankDirection.java | 23 + .../export/ilograph/IlographExporter.java | 391 ++++ .../ilograph/IlographWorkspaceExport.java | 16 + .../com/structurizr/export/ilograph/README.md | 7 + .../export/mermaid/MermaidDiagram.java | 17 + .../mermaid/MermaidDiagramExporter.java | 412 ++++ .../export/mermaid/MermaidEncoder.java | 18 + .../com/structurizr/export/mermaid/README.md | 6 + .../plantuml/AbstractPlantUMLExporter.java | 251 +++ .../export/plantuml/C4PlantUMLExporter.java | 682 ++++++ .../export/plantuml/PlantUMLDiagram.java | 17 + .../export/plantuml/PlantUMLEncoder.java | 73 + .../com/structurizr/export/plantuml/README.md | 7 + .../plantuml/StructurizrPlantUMLExporter.java | 626 ++++++ .../export/websequencediagrams/README.md | 23 + .../WebSequenceDiagramsDiagram.java | 17 + .../WebSequenceDiagramsExporter.java | 176 ++ .../export/AbstractExporterTests.java | 29 + .../export/IndentingWriterTests.java | 76 + .../export/dot/36141-Components.dot | 43 + .../export/dot/36141-Containers.dot | 37 + .../dot/36141-DevelopmentDeployment.dot | 97 + .../export/dot/36141-LiveDeployment.dot | 152 ++ .../structurizr/export/dot/36141-SignIn.dot | 29 + .../export/dot/36141-SystemContext.dot | 17 + .../export/dot/36141-SystemLandscape.dot | 35 + .../dot/54915-AmazonWebServicesDeployment.dot | 75 + .../export/dot/DOTDiagramExporterTests.java | 425 ++++ .../export/dot/groups-Components.dot | 35 + .../export/dot/groups-Containers.dot | 35 + .../export/dot/groups-SystemLandscape.dot | 48 + .../structurizr/export/dot/nested-groups.dot | 69 + .../export/ilograph/36141.ilograph | 631 ++++++ .../export/ilograph/54915.ilograph | 120 + .../export/ilograph/IlographWriterTests.java | 76 + .../export/mermaid/36141-Components.mmd | 48 + .../export/mermaid/36141-Containers.mmd | 39 + .../mermaid/36141-DevelopmentDeployment.mmd | 61 + .../export/mermaid/36141-LiveDeployment.mmd | 92 + .../export/mermaid/36141-SignIn-sequence.mmd | 13 + .../export/mermaid/36141-SignIn.mmd | 27 + .../export/mermaid/36141-SystemContext.mmd | 20 + .../export/mermaid/36141-SystemLandscape.mmd | 36 + .../54915-AmazonWebServicesDeployment.mmd | 48 + .../mermaid/MermaidDiagramExporterTests.java | 317 +++ .../export/mermaid/groups-Components.mmd | 26 + .../export/mermaid/groups-Containers.mmd | 26 + .../export/mermaid/groups-SystemLandscape.mmd | 34 + .../export/mermaid/nested-groups.mmd | 43 + .../C4PlantUMLDiagramExporterTests.java | 755 +++++++ ...ructurizrPlantUMLDiagramExporterTests.java | 1031 +++++++++ .../plantuml/c4plantuml/36141-Components.puml | 52 + .../plantuml/c4plantuml/36141-Containers.puml | 46 + .../36141-DevelopmentDeployment.puml | 55 + .../c4plantuml/36141-LiveDeployment.puml | 79 + .../c4plantuml/36141-SignIn-sequence.puml | 26 + .../plantuml/c4plantuml/36141-SignIn.puml | 36 + .../c4plantuml/36141-SystemContext.puml | 27 + .../c4plantuml/36141-SystemLandscape.puml | 39 + ...-AmazonWebServicesDeployment-WithTags.puml | 52 + ...azonWebServicesDeployment-WithoutTags.puml | 39 + .../plantuml/c4plantuml/group-styles-1.puml | 28 + .../plantuml/c4plantuml/group-styles-2.puml | 28 + .../c4plantuml/groups-Components.puml | 26 + .../c4plantuml/groups-Containers.puml | 26 + .../c4plantuml/groups-SystemLandscape.puml | 31 + .../nested-groups-with-dot-separator.puml | 26 + .../plantuml/c4plantuml/nested-groups.puml | 38 + .../printProperties-containerView.puml | 28 + .../printProperties-deploymentView.puml | 21 + .../structurizr/36141-Components.puml | 116 + .../structurizr/36141-Containers.puml | 92 + .../36141-DevelopmentDeployment.puml | 128 ++ .../structurizr/36141-LiveDeployment.puml | 190 ++ .../structurizr/36141-SignIn-sequence.puml | 49 + .../plantuml/structurizr/36141-SignIn.puml | 60 + .../structurizr/36141-SystemContext.puml | 50 + .../structurizr/36141-SystemLandscape.puml | 82 + ...15-AmazonWebServicesDeployment-Legend.puml | 102 + .../54915-AmazonWebServicesDeployment.puml | 111 + ...onent-view-with-external-components-1.puml | 56 + ...onent-view-with-external-components-2.puml | 62 + ...mic-view-container-scoped-with-groups.puml | 62 + ...ew-software-system-scoped-with-groups.puml | 62 + .../dynamic-view-unscoped-with-groups.puml | 46 + ...namic-view-with-external-components-1.puml | 56 + ...namic-view-with-external-components-2.puml | 62 + .../plantuml/structurizr/group-styles-1.puml | 60 + .../plantuml/structurizr/group-styles-2.puml | 60 + .../structurizr/groups-Components.puml | 56 + .../structurizr/groups-Containers.puml | 56 + .../structurizr/groups-SystemLandscape.puml | 69 + .../plantuml/structurizr/nested-groups.puml | 88 + .../websequencediagrams/36141-SignIn.wsd | 13 + .../WebSequenceDiagramsExporterTests.java | 53 + .../src/test/resources/groups.json | 203 ++ .../structurizr-36141-workspace.json | 1999 +++++++++++++++++ .../structurizr-54915-workspace.json | 353 +++ structurizr-graphviz/README.md | 26 + structurizr-graphviz/build.gradle | 10 + .../com/structurizr/graphviz/Constants.java | 17 + .../com/structurizr/graphviz/DOTDiagram.java | 17 + .../com/structurizr/graphviz/DOTExporter.java | 620 +++++ .../graphviz/GraphvizAutomaticLayout.java | 217 ++ .../structurizr/graphviz/RankDirection.java | 23 + .../com/structurizr/graphviz/SVGReader.java | 203 ++ .../graphviz/DOTExporterTests.java | 616 +++++ .../GraphvizAutomaticLayoutTests.java | 49 + .../structurizr/graphviz/SVGReaderTests.java | 66 + .../test/resources/graphviz/SystemContext.dot | 14 + .../resources/graphviz/SystemContext.dot.svg | 35 + .../structurizr-54915-workspace.json | 353 +++ structurizr-import/README.md | 11 + structurizr-import/build.gradle | 9 + .../diagrams/AbstractDiagramImporter.java | 33 + .../importer/diagrams/kroki/KrokiEncoder.java | 30 + .../diagrams/kroki/KrokiImporter.java | 45 + .../diagrams/mermaid/MermaidEncoder.java | 15 + .../diagrams/mermaid/MermaidImporter.java | 50 + .../diagrams/plantuml/PlantUMLEncoder.java | 73 + .../diagrams/plantuml/PlantUMLImporter.java | 54 + .../AdrToolsDecisionImporter.java | 237 ++ .../DefaultDocumentationImporter.java | 81 + .../documentation/DefaultImageImporter.java | 91 + .../DocumentationImportException.java | 13 + .../documentation/DocumentationImporter.java | 20 + .../importer/documentation/FormatFinder.java | 38 + ...RecursiveDefaultDocumentationImporter.java | 70 + .../diagrams/kroki/KrokiEncoderTests.java | 68 + .../diagrams/kroki/KrokiImporterTests.java | 88 + .../diagrams/mermaid/MermaidEncoderTests.java | 28 + .../mermaid/MermaidImporterTests.java | 88 + .../plantuml/PlantUMLEncoderTests.java | 20 + .../plantuml/PlantUMLImporterTests.java | 95 + .../AdrToolsDecisionImporterTests.java | 128 ++ .../DefaultDocumentImporterTests.java | 81 + .../DefaultImageImporterTests.java | 181 ++ .../documentation/FormatFinderTests.java | 38 + ...RecursiveDefaultDocumentImporterTests.java | 51 + .../0001-record-architecture-decisions.md | 19 + .../adrs/0002-implement-as-shell-scripts.md | 28 + .../0003-single-command-with-subcommands.md | 45 + .../resources/adrs/0004-markdown-format.md | 40 + .../test/resources/adrs/0005-help-comments.md | 42 + ...n-in-other-version-control-repositories.md | 41 + ...-config-executable-to-get-configuration.md | 31 + .../0008-use-iso-8601-format-for-dates.md | 43 + .../test/resources/adrs/0009-help-scripts.md | 28 + .../test/resources/diagrams/kroki/diagram.dot | 1 + .../test/resources/diagrams/mermaid/class.mmd | 21 + .../resources/diagrams/mermaid/flowchart.mmd | 6 + .../diagrams/plantuml/with-title.puml | 4 + .../diagrams/plantuml/without-title.puml | 3 + .../test/resources/docs/docs/01-section-1.md | 1 + .../resources/docs/docs/02-section-2.markdown | 1 + .../resources/docs/docs/03-section-3.text | 1 + .../resources/docs/docs/04-section-4.adoc | 1 + .../resources/docs/docs/05-section-5.asciidoc | 1 + .../test/resources/docs/docs/06-section-6.asc | 1 + .../docs/docs/07-subdirectory/01-section-1.md | 1 + .../test/resources/docs/docs/images/image.gif | Bin 0 -> 4682 bytes .../resources/docs/docs/images/image.jpeg | Bin 0 -> 1467 bytes .../test/resources/docs/docs/images/image.jpg | Bin 0 -> 1467 bytes .../test/resources/docs/docs/images/image.png | Bin 0 -> 1563 bytes .../src/test/resources/docs/images/image.gif | Bin 0 -> 4682 bytes .../src/test/resources/docs/images/image.jpeg | Bin 0 -> 1467 bytes .../src/test/resources/docs/images/image.jpg | Bin 0 -> 1467 bytes .../src/test/resources/docs/images/image.png | Bin 0 -> 1563 bytes .../src/test/resources/docs/images/image.svg | 3 + .../resources/docs/images/images/image.gif | Bin 0 -> 4682 bytes .../resources/docs/images/images/image.jpeg | Bin 0 -> 1467 bytes .../resources/docs/images/images/image.jpg | Bin 0 -> 1467 bytes .../resources/docs/images/images/image.png | Bin 0 -> 1563 bytes .../resources/docs/images/noimages/readme.md | 1 + 741 files changed, 39604 insertions(+), 430 deletions(-) create mode 100644 changelog.md delete mode 100644 docs/changelog.md create mode 100644 structurizr-client/README.md rename structurizr-client/{test/integration => src/integrationTest/java}/com/structurizr/api/BackwardsCompatibilityTests.java (97%) rename structurizr-client/{test/integration => src/integrationTest/java}/com/structurizr/api/StructurizrClientIntegrationTests.java (100%) rename structurizr-client/{test/integration => src/integrationTest/java}/com/structurizr/api/WorkspaceRulesValidationTests.java (99%) rename structurizr-client/{test/integration => src/integrationTest/resources}/backwardsCompatibility/structurizr-31-workspace.json (100%) rename structurizr-client/{test/integration => src/integrationTest/resources}/backwardsCompatibility/structurizr-36141-workspace.json (100%) rename structurizr-client/{test/integration => src/integrationTest/resources}/backwardsCompatibility/structurizr-39459-workspace.json (100%) rename structurizr-client/{test/integration => src/integrationTest/resources}/workspaceValidation/ChildDeploymentNodeNamesAreNotUnique.json (100%) rename structurizr-client/{test/integration => src/integrationTest/resources}/workspaceValidation/ComponentNamesAreNotUnique.json (100%) rename structurizr-client/{test/integration => src/integrationTest/resources}/workspaceValidation/ContainerAssociatedWithComponentViewIsMissingFromTheModel.json (100%) rename structurizr-client/{test/integration => src/integrationTest/resources}/workspaceValidation/ContainerNamesAreNotUnique.json (100%) rename structurizr-client/{test/integration => src/integrationTest/resources}/workspaceValidation/ElementAssociatedWithDynamicViewIsMissingFromTheModel.json (100%) rename structurizr-client/{test/integration => src/integrationTest/resources}/workspaceValidation/ElementIdsAreNotUnique.json (100%) rename structurizr-client/{test/integration => src/integrationTest/resources}/workspaceValidation/ElementReferencedByViewIsMissingFromTheModel.json (100%) rename structurizr-client/{test/integration => src/integrationTest/resources}/workspaceValidation/PeopleAndSoftwareSystemNamesAreNotUnique.json (100%) rename structurizr-client/{test/integration => src/integrationTest/resources}/workspaceValidation/RelationshipDescriptionsAreNotUnique.json (100%) rename structurizr-client/{test/integration => src/integrationTest/resources}/workspaceValidation/RelationshipIdsAreNotUnique.json (100%) rename structurizr-client/{test/integration => src/integrationTest/resources}/workspaceValidation/RelationshipReferencedByViewIsMissingFromTheModel.json (100%) rename structurizr-client/{test/integration => src/integrationTest/resources}/workspaceValidation/SoftwareSystemAssociatedWithContainerViewIsMissingFromTheModel.json (100%) rename structurizr-client/{test/integration => src/integrationTest/resources}/workspaceValidation/SoftwareSystemAssociatedWithDeploymentViewIsMissingFromTheModel.json (100%) rename structurizr-client/{test/integration => src/integrationTest/resources}/workspaceValidation/SoftwareSystemAssociatedWithSystemContextViewIsMissingFromTheModel.json (100%) rename structurizr-client/{test/integration => src/integrationTest/resources}/workspaceValidation/TopLevelDeploymentNodeNamesAreNotUnique.json (100%) rename structurizr-client/{test/integration => src/integrationTest/resources}/workspaceValidation/TopLevelDeploymentNodeNamesAreNotUniqueButTheyExistInDifferentEnvironments.json (100%) rename structurizr-client/{test/integration => src/integrationTest/resources}/workspaceValidation/ViewAssociatedWithFilteredViewIsMissingFromTheWorkspace.json (100%) rename structurizr-client/{test/integration => src/integrationTest/resources}/workspaceValidation/ViewKeysAreNotUnique.json (100%) rename structurizr-client/src/{ => main/java}/com/structurizr/api/AbstractApiClient.java (100%) rename structurizr-client/src/{ => main/java}/com/structurizr/api/AdminApiClient.java (100%) rename structurizr-client/src/{ => main/java}/com/structurizr/api/ApiResponse.java (100%) rename structurizr-client/src/{ => main/java}/com/structurizr/api/HashBasedMessageAuthenticationCode.java (100%) rename structurizr-client/src/{ => main/java}/com/structurizr/api/HmacAuthorizationHeader.java (100%) rename structurizr-client/src/{ => main/java}/com/structurizr/api/HmacContent.java (100%) rename structurizr-client/src/{ => main/java}/com/structurizr/api/HttpHeaders.java (100%) rename structurizr-client/src/{ => main/java}/com/structurizr/api/Md5Digest.java (100%) rename structurizr-client/src/{ => main/java}/com/structurizr/api/StructurizrClient.java (100%) rename structurizr-client/src/{ => main/java}/com/structurizr/api/StructurizrClientException.java (100%) rename structurizr-client/src/{ => main/java}/com/structurizr/api/WorkspaceApiClient.java (100%) rename structurizr-client/src/{ => main/java}/com/structurizr/api/WorkspaceMetadata.java (100%) rename structurizr-client/src/{ => main/java}/com/structurizr/api/Workspaces.java (100%) rename structurizr-client/src/{ => main/java}/com/structurizr/encryption/AesEncryptionStrategy.java (100%) rename structurizr-client/src/{ => main/java}/com/structurizr/encryption/EncryptedWorkspace.java (100%) rename structurizr-client/src/{ => main/java}/com/structurizr/encryption/EncryptionLocation.java (100%) rename structurizr-client/src/{ => main/java}/com/structurizr/encryption/EncryptionStrategy.java (100%) rename structurizr-client/src/{ => main/java}/com/structurizr/io/WorkspaceReader.java (100%) rename structurizr-client/src/{ => main/java}/com/structurizr/io/WorkspaceReaderException.java (100%) rename structurizr-client/src/{ => main/java}/com/structurizr/io/WorkspaceWriter.java (100%) rename structurizr-client/src/{ => main/java}/com/structurizr/io/WorkspaceWriterException.java (100%) rename structurizr-client/src/{ => main/java}/com/structurizr/io/json/AbstractJsonReader.java (100%) rename structurizr-client/src/{ => main/java}/com/structurizr/io/json/AbstractJsonWriter.java (100%) rename structurizr-client/src/{ => main/java}/com/structurizr/io/json/EncryptedJsonReader.java (100%) rename structurizr-client/src/{ => main/java}/com/structurizr/io/json/EncryptedJsonWriter.java (100%) rename structurizr-client/src/{ => main/java}/com/structurizr/io/json/JsonReader.java (100%) rename structurizr-client/src/{ => main/java}/com/structurizr/io/json/JsonWriter.java (100%) rename structurizr-client/src/{ => main/java}/com/structurizr/util/WorkspaceUtils.java (100%) rename structurizr-client/src/{ => main/java}/com/structurizr/view/ThemeUtils.java (100%) rename structurizr-client/{test/unit => src/test/java}/com/structurizr/api/ApiResponseTests.java (100%) rename structurizr-client/{test/unit => src/test/java}/com/structurizr/api/HashBasedMessageAuthenticationCodeTests.java (100%) rename structurizr-client/{test/unit => src/test/java}/com/structurizr/api/HmacAuthorizationHeaderTests.java (100%) rename structurizr-client/{test/unit => src/test/java}/com/structurizr/api/HmacContentTests.java (100%) rename structurizr-client/{test/unit => src/test/java}/com/structurizr/api/Md5DigestTests.java (100%) rename structurizr-client/{test/unit => src/test/java}/com/structurizr/api/StructurizrClientTests.java (100%) rename structurizr-client/{test/unit => src/test/java}/com/structurizr/encryption/AesEncryptionStrategyTests.java (100%) rename structurizr-client/{test/unit => src/test/java}/com/structurizr/encryption/EncryptedWorkspaceTests.java (100%) rename structurizr-client/{test/unit => src/test/java}/com/structurizr/encryption/MockEncryptionStrategy.java (100%) rename structurizr-client/{test/unit => src/test/java}/com/structurizr/io/json/EncryptedJsonTests.java (100%) rename structurizr-client/{test/unit => src/test/java}/com/structurizr/io/json/EncryptedJsonWriterTests.java (100%) rename structurizr-client/{test/unit => src/test/java}/com/structurizr/io/json/JsonTests.java (100%) rename structurizr-client/{test/unit => src/test/java}/com/structurizr/io/json/JsonWriterTests.java (100%) rename structurizr-client/{test/unit => src/test/java}/com/structurizr/util/WorkspaceUtilsTests.java (100%) rename structurizr-client/{test/unit => src/test/java}/com/structurizr/view/ThemeUtilsTests.java (100%) create mode 100644 structurizr-core/README.md rename structurizr-core/src/{ => main/java}/com/structurizr/AbstractWorkspace.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/PropertyHolder.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/Workspace.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/WorkspaceValidationException.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/configuration/Role.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/configuration/User.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/configuration/Visibility.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/configuration/WorkspaceConfiguration.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/configuration/WorkspaceScope.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/documentation/Decision.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/documentation/Documentable.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/documentation/Documentation.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/documentation/DocumentationContent.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/documentation/Format.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/documentation/Image.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/documentation/Section.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/model/AbstractImpliedRelationshipsStrategy.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/model/CanonicalNameGenerator.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/model/Component.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/model/Constants.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/model/Container.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/model/ContainerInstance.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategy.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/model/CustomElement.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/model/DefaultImpliedRelationshipsStrategy.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/model/DeploymentElement.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/model/DeploymentNode.java (98%) rename structurizr-core/src/{ => main/java}/com/structurizr/model/Element.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/model/Enterprise.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/model/GroupableElement.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/model/HttpHealthCheck.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/model/IdGenerator.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/model/ImpliedRelationshipsStrategy.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/model/InfrastructureNode.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/model/InteractionStyle.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/model/Location.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/model/Model.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/model/ModelItem.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/model/Person.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/model/Perspective.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/model/Relationship.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/model/SequentialIntegerIdGeneratorStrategy.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/model/SoftwareSystem.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/model/SoftwareSystemInstance.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/model/StaticStructureElement.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/model/StaticStructureElementInstance.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/model/Tags.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/util/ImageUtils.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/util/MapUtils.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/util/StringUtils.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/util/TagUtils.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/util/Url.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/validation/LandscapeWorkspaceScopeValidator.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/validation/SoftwareSystemWorkspaceScopeValidator.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/validation/UndefinedWorkspaceScopeValidator.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/validation/WorkspaceScopeValidationException.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/validation/WorkspaceScopeValidator.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/validation/WorkspaceScopeValidatorFactory.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/AbstractStyle.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/AnimatedView.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/Animation.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/AutomaticLayout.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/Border.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/Branding.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/Color.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/ComponentView.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/Configuration.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/ContainerView.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/CustomView.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/DefaultLayoutMergeStrategy.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/DeploymentView.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/Dimensions.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/DynamicView.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/ElementNotPermittedInViewException.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/ElementStyle.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/ElementView.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/FilterMode.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/FilteredView.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/Font.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/ImageView.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/LayoutMergeStrategy.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/LineStyle.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/MetadataSymbols.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/ModelView.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/PaperSize.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/ParallelSequenceCounter.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/RelationshipStyle.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/RelationshipView.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/Routing.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/SequenceCounter.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/SequenceNumber.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/Shape.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/StaticView.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/Styles.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/SystemContextView.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/SystemLandscapeView.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/Terminology.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/Theme.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/Vertex.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/View.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/ViewSet.java (100%) rename structurizr-core/src/{ => main/java}/com/structurizr/view/ViewSortOrder.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/AbstractWorkspaceTestBase.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/WorkspaceTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/configuration/UserTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/configuration/WorkspaceConfigurationTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/documentation/DecisionTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/documentation/DocumentationTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/documentation/SectionTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/model/ComponentTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/model/ContainerInstanceTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/model/ContainerTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategyTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategyTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/model/CustomElementTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/model/DefaultImpliedRelationshipsStrategyTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/model/DeploymentNodeTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/model/ElementTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/model/GroupableElementTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/model/HttpHealthCheckTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/model/InfrastructureNodeTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/model/ModelItemTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/model/ModelTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/model/PersonTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/model/RelationshipTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/model/SoftwareSystemInstanceTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/model/SoftwareSystemTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/util/ImageUtilsTests.java (96%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/util/StringUtilsTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/util/UrlTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/validation/LandscapeWorkspaceScopeValidatorTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/validation/SoftwareSystemWorkspaceScopeValidatorTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/validation/WorkspaceScopeValidatorFactoryTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/view/AutomaticLayoutTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/view/BrandingTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/view/ColorTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/view/ComponentViewTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/view/ConfigurationTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/view/ContainerViewTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/view/DefaultLayoutMergeStrategyTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/view/DeploymentViewTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/view/DimensionsTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/view/DynamicViewTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/view/ElementStyleTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/view/ElementViewTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/view/FilteredViewTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/view/FontTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/view/ImageViewTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/view/PaperSizeTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/view/RelationshipStyleTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/view/SequenceCounterTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/view/SequenceNumberTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/view/StaticViewTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/view/StylesTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/view/SystemContextViewTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/view/SystemLandscapeViewTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/view/TerminologyTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/view/VertexTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/view/ViewSetTests.java (100%) rename structurizr-core/{test/unit => src/test/java}/com/structurizr/view/ViewTests.java (100%) rename structurizr-core/{test/unit/com/structurizr/util => src/test/resources}/structurizr-logo.png (100%) create mode 100644 structurizr-dsl/README.md create mode 100644 structurizr-dsl/build.gradle create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractExpressionParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractRelationshipParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractViewParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/AdrsParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/AutoLayoutParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/BrandingDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/BrandingParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/CommentDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentViewDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentViewParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ConfigurationDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ConfigurationParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/Constant.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ConstantParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerInstanceDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerInstanceParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerViewDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerViewParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/CustomElementDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/CustomElementParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/CustomViewAnimationDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/CustomViewAnimationStepParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/CustomViewContentParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/CustomViewDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/CustomViewExpressionParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/CustomViewParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/DefaultViewParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentEnvironment.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentEnvironmentDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentEnvironmentParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentGroup.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentGroupParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentViewAnimationDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentViewAnimationStepParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentViewContentParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentViewDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentViewExpressionParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentViewParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/DocsParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/DslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/DslLine.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/DslParserContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/DslUtils.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/DynamicViewContentParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/DynamicViewDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/DynamicViewParallelSequenceDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/DynamicViewParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/DynamicViewRelationshipContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/DynamicViewRelationshipParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ElementGroup.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ElementStyleDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ElementStyleParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/EnterpriseDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/EnterpriseParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ExplicitRelationshipParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ExternalScriptDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/FileUtils.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/FilteredViewDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/FilteredViewParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/GroupParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/GroupableDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/GroupableElementDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/HealthCheckParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/IconUtils.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/IdentifierScope.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/IdentifierScopeParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/IdentifiersRegister.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewContentParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ImplicitRelationshipParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ImpliedRelationshipsParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/IncludeParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/IncludedDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/IncludedFile.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/InlineScriptDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ModelDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemPerspectivesDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ModelViewContentParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ModelViewDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/PersonDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/PersonParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/PluginDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/PluginParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/PropertiesDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/PropertyParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/RefParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipStyleDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipStyleParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ScriptDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ScriptParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemInstanceDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemInstanceParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/StaticStructureElementInstanceDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/StaticViewAnimationDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/StaticViewAnimationStepParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/StaticViewContentParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/StaticViewDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/StaticViewExpressionParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslExpressions.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParserException.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslPlugin.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslPluginContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslScriptContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/StylesDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/SystemContextViewDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/SystemContextViewParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/SystemLandscapeViewDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/SystemLandscapeViewParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/TerminologyDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/TerminologyParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ThemeParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/Tokenizer.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/Tokens.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/UserRoleParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/UsersDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ViewDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ViewParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ViewsDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/WorkspaceDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/WorkspaceParser.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/AbstractExpressionParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/AbstractTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/AdrsParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/AutoLayoutParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/BrandingParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentViewParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/ConfigurationParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/ConstantParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/ContainerInstanceParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/ContainerParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/ContainerViewParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/CookbookTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/CustomElementParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/CustomViewAnimationStepParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/CustomViewContentParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/CustomViewParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/DefaultViewParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentEnvironmentParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentGroupParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentNodeParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentViewAnimationStepParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentViewContentParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentViewExpressionParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentViewParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/DocsParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/DynamicViewContentParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/DynamicViewParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/DynamicViewRelationshipParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/ElementStyleParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/EnterpriseParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/ExplicitRelationshipParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/ExternalScriptDslContextTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/FilteredViewParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/GroupParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/HealthCheckParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/IdentifierRegisterTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/IdentifierScopeParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/ImplicitRelationshipParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/ImpliedRelationshipsParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/IncludeParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/InfrastructureNodeParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/InlineScriptDslContextTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/ModelDslContextTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/ModelItemParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/PersonParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/PluginDslContextTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/PluginParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/PropertyParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/RefParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/RelationshipStyleParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/ScriptParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/SoftwareSystemInstanceParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/SoftwareSystemParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/StaticViewAnimationStepParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/StaticViewContentParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/StaticViewExpressionParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/SystemContextViewParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/SystemLandscapeViewParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/TerminologyParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/ThemeParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/TokenizerTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/TokensTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/UserRoleParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/ViewParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/WorkspaceParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/example/ExampleDecisionImporter.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/example/ExampleDocumentationImporter.java create mode 100644 structurizr-dsl/src/test/resources/dsl/adrs/adrs/0001-record-architecture-decisions.md create mode 100644 structurizr-dsl/src/test/resources/dsl/adrs/adrs/0002-implement-as-shell-scripts.md create mode 100644 structurizr-dsl/src/test/resources/dsl/adrs/adrs/0003-single-command-with-subcommands.md create mode 100644 structurizr-dsl/src/test/resources/dsl/adrs/adrs/0004-markdown-format.md create mode 100644 structurizr-dsl/src/test/resources/dsl/adrs/adrs/0005-help-comments.md create mode 100644 structurizr-dsl/src/test/resources/dsl/adrs/adrs/0006-packaging-and-distribution-in-other-version-control-repositories.md create mode 100644 structurizr-dsl/src/test/resources/dsl/adrs/adrs/0007-invoke-adr-config-executable-to-get-configuration.md create mode 100644 structurizr-dsl/src/test/resources/dsl/adrs/adrs/0008-use-iso-8601-format-for-dates.md create mode 100644 structurizr-dsl/src/test/resources/dsl/adrs/adrs/0009-help-scripts.md create mode 100644 structurizr-dsl/src/test/resources/dsl/adrs/adrs/0010-asciidoc-format.md create mode 100644 structurizr-dsl/src/test/resources/dsl/adrs/workspace.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/amazon-web-services-local.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/amazon-web-services.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/big-bank-plc.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/big-bank-plc/internet-banking-system.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/big-bank-plc/model/internet-banking-system/adrs/0001-record-architecture-decisions.md create mode 100644 structurizr-dsl/src/test/resources/dsl/big-bank-plc/model/internet-banking-system/details.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/big-bank-plc/model/internet-banking-system/docs/01-context.md create mode 100644 structurizr-dsl/src/test/resources/dsl/big-bank-plc/model/internet-banking-system/docs/02-containers.md create mode 100644 structurizr-dsl/src/test/resources/dsl/big-bank-plc/model/internet-banking-system/docs/03-development-environment.adoc create mode 100644 structurizr-dsl/src/test/resources/dsl/big-bank-plc/model/internet-banking-system/docs/04-deployment.adoc create mode 100644 structurizr-dsl/src/test/resources/dsl/big-bank-plc/model/internet-banking-system/summary.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/big-bank-plc/model/people-and-software-systems.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/big-bank-plc/system-landscape.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/big-bank-plc/views/styles-people.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/deployment-environment-empty.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/deployment-groups.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/docs/docs/01-context.md create mode 100644 structurizr-dsl/src/test/resources/dsl/docs/workspace.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/dynamic-view-with-custom-elements.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/dynamic-view-with-explicit-relationships.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/dynamic.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/exclude-implied-relationship.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/exclude-relationships.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/extend/1.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/extend/2.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/extend/3.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/extend/4.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/extend/extend-workspace-from-dsl-file.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/extend/extend-workspace-from-dsl-url.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/extend/extend-workspace-from-json-file.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/extend/extend-workspace-from-json-url.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/extend/workspace.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/extend/workspace.json create mode 100644 structurizr-dsl/src/test/resources/dsl/filteredviews.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/financial-risk-system.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/getting-started-short.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/getting-started.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/group-url.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/group-without-brace.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/groups-nested.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/groups.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/hierarchical-identifiers-and-deployment-nodes-1.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/hierarchical-identifiers-and-deployment-nodes-2.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/hierarchical-identifiers-and-deployment-nodes-3.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/hierarchical-identifiers-when-unassigned.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/hierarchical-identifiers.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/identifiers.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/image-views/diagram.dot create mode 100644 structurizr-dsl/src/test/resources/dsl/image-views/diagram.mmd create mode 100644 structurizr-dsl/src/test/resources/dsl/image-views/diagram.puml create mode 100644 structurizr-dsl/src/test/resources/dsl/image-views/logo.png create mode 100644 structurizr-dsl/src/test/resources/dsl/image-views/workspace-via-file.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/image-views/workspace-via-url.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/include-directory.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/include-file.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/include-implied-relationship.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/include-url.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/include/docs/section.md create mode 100644 structurizr-dsl/src/test/resources/dsl/include/model.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/include/model/software-system/model.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/iso-8859.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/logo.png create mode 100644 structurizr-dsl/src/test/resources/dsl/multi-line-with-error.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/multi-line.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/multiple-model-tokens.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/multiple-view-tokens.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/multiple-workspace-tokens.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/parallel1.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/parallel2.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/plugin-with-parameters.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/plugin-without-parameters.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/plugins/structurizr-dsl-plugin-1.0.0.jar create mode 100644 structurizr-dsl/src/test/resources/dsl/ref.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/relationship-already-exists.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/script-external-with-parameters.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/script-external.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/script-in-dynamic-view.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/script-inline.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/shapes.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/test-with-parameters.groovy create mode 100644 structurizr-dsl/src/test/resources/dsl/test.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/test.groovy create mode 100644 structurizr-dsl/src/test/resources/dsl/test.js create mode 100644 structurizr-dsl/src/test/resources/dsl/test.kts create mode 100644 structurizr-dsl/src/test/resources/dsl/test.rb create mode 100644 structurizr-dsl/src/test/resources/dsl/this.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/unexpected-tokens-after-workspace.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/unexpected-tokens-before-workspace.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/unexpected-tokens-in-workspace.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/utf8.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/views-without-keys.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/workspace-properties.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/workspace-with-bom.dsl create mode 100644 structurizr-export/README.md create mode 100644 structurizr-export/build.gradle create mode 100644 structurizr-export/src/main/java/com/structurizr/export/AbstractDiagramExporter.java create mode 100644 structurizr-export/src/main/java/com/structurizr/export/AbstractExporter.java create mode 100644 structurizr-export/src/main/java/com/structurizr/export/AbstractWorkspaceExporter.java create mode 100644 structurizr-export/src/main/java/com/structurizr/export/Diagram.java create mode 100644 structurizr-export/src/main/java/com/structurizr/export/DiagramExporter.java create mode 100644 structurizr-export/src/main/java/com/structurizr/export/Exporter.java create mode 100644 structurizr-export/src/main/java/com/structurizr/export/IndentType.java create mode 100644 structurizr-export/src/main/java/com/structurizr/export/IndentingWriter.java create mode 100644 structurizr-export/src/main/java/com/structurizr/export/Legend.java create mode 100644 structurizr-export/src/main/java/com/structurizr/export/WorkspaceExport.java create mode 100644 structurizr-export/src/main/java/com/structurizr/export/WorkspaceExporter.java create mode 100644 structurizr-export/src/main/java/com/structurizr/export/dot/DOTDiagram.java create mode 100644 structurizr-export/src/main/java/com/structurizr/export/dot/DOTExporter.java create mode 100644 structurizr-export/src/main/java/com/structurizr/export/dot/README.md create mode 100644 structurizr-export/src/main/java/com/structurizr/export/dot/RankDirection.java create mode 100644 structurizr-export/src/main/java/com/structurizr/export/ilograph/IlographExporter.java create mode 100644 structurizr-export/src/main/java/com/structurizr/export/ilograph/IlographWorkspaceExport.java create mode 100644 structurizr-export/src/main/java/com/structurizr/export/ilograph/README.md create mode 100644 structurizr-export/src/main/java/com/structurizr/export/mermaid/MermaidDiagram.java create mode 100644 structurizr-export/src/main/java/com/structurizr/export/mermaid/MermaidDiagramExporter.java create mode 100644 structurizr-export/src/main/java/com/structurizr/export/mermaid/MermaidEncoder.java create mode 100644 structurizr-export/src/main/java/com/structurizr/export/mermaid/README.md create mode 100644 structurizr-export/src/main/java/com/structurizr/export/plantuml/AbstractPlantUMLExporter.java create mode 100644 structurizr-export/src/main/java/com/structurizr/export/plantuml/C4PlantUMLExporter.java create mode 100644 structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLDiagram.java create mode 100644 structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLEncoder.java create mode 100644 structurizr-export/src/main/java/com/structurizr/export/plantuml/README.md create mode 100644 structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java create mode 100644 structurizr-export/src/main/java/com/structurizr/export/websequencediagrams/README.md create mode 100644 structurizr-export/src/main/java/com/structurizr/export/websequencediagrams/WebSequenceDiagramsDiagram.java create mode 100644 structurizr-export/src/main/java/com/structurizr/export/websequencediagrams/WebSequenceDiagramsExporter.java create mode 100644 structurizr-export/src/test/java/com/structurizr/export/AbstractExporterTests.java create mode 100644 structurizr-export/src/test/java/com/structurizr/export/IndentingWriterTests.java create mode 100644 structurizr-export/src/test/java/com/structurizr/export/dot/36141-Components.dot create mode 100644 structurizr-export/src/test/java/com/structurizr/export/dot/36141-Containers.dot create mode 100644 structurizr-export/src/test/java/com/structurizr/export/dot/36141-DevelopmentDeployment.dot create mode 100644 structurizr-export/src/test/java/com/structurizr/export/dot/36141-LiveDeployment.dot create mode 100644 structurizr-export/src/test/java/com/structurizr/export/dot/36141-SignIn.dot create mode 100644 structurizr-export/src/test/java/com/structurizr/export/dot/36141-SystemContext.dot create mode 100644 structurizr-export/src/test/java/com/structurizr/export/dot/36141-SystemLandscape.dot create mode 100644 structurizr-export/src/test/java/com/structurizr/export/dot/54915-AmazonWebServicesDeployment.dot create mode 100644 structurizr-export/src/test/java/com/structurizr/export/dot/DOTDiagramExporterTests.java create mode 100644 structurizr-export/src/test/java/com/structurizr/export/dot/groups-Components.dot create mode 100644 structurizr-export/src/test/java/com/structurizr/export/dot/groups-Containers.dot create mode 100644 structurizr-export/src/test/java/com/structurizr/export/dot/groups-SystemLandscape.dot create mode 100644 structurizr-export/src/test/java/com/structurizr/export/dot/nested-groups.dot create mode 100644 structurizr-export/src/test/java/com/structurizr/export/ilograph/36141.ilograph create mode 100644 structurizr-export/src/test/java/com/structurizr/export/ilograph/54915.ilograph create mode 100644 structurizr-export/src/test/java/com/structurizr/export/ilograph/IlographWriterTests.java create mode 100644 structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-Components.mmd create mode 100644 structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-Containers.mmd create mode 100644 structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-DevelopmentDeployment.mmd create mode 100644 structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-LiveDeployment.mmd create mode 100644 structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SignIn-sequence.mmd create mode 100644 structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SignIn.mmd create mode 100644 structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SystemContext.mmd create mode 100644 structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SystemLandscape.mmd create mode 100644 structurizr-export/src/test/java/com/structurizr/export/mermaid/54915-AmazonWebServicesDeployment.mmd create mode 100644 structurizr-export/src/test/java/com/structurizr/export/mermaid/MermaidDiagramExporterTests.java create mode 100644 structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-Components.mmd create mode 100644 structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-Containers.mmd create mode 100644 structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-SystemLandscape.mmd create mode 100644 structurizr-export/src/test/java/com/structurizr/export/mermaid/nested-groups.mmd create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Components.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Containers.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-DevelopmentDeployment.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-LiveDeployment.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn-sequence.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemContext.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemLandscape.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/54915-AmazonWebServicesDeployment-WithTags.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/54915-AmazonWebServicesDeployment-WithoutTags.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/group-styles-1.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/group-styles-2.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Components.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Containers.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-SystemLandscape.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/nested-groups-with-dot-separator.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/nested-groups.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/printProperties-containerView.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/printProperties-deploymentView.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-Components.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-Containers.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-DevelopmentDeployment.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-LiveDeployment.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SignIn-sequence.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SignIn.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemContext.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemLandscape.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/54915-AmazonWebServicesDeployment-Legend.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/54915-AmazonWebServicesDeployment.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/component-view-with-external-components-1.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/component-view-with-external-components-2.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-container-scoped-with-groups.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-software-system-scoped-with-groups.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-unscoped-with-groups.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-with-external-components-1.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-with-external-components-2.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/group-styles-1.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/group-styles-2.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-Components.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-Containers.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-SystemLandscape.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/nested-groups.puml create mode 100644 structurizr-export/src/test/java/com/structurizr/export/websequencediagrams/36141-SignIn.wsd create mode 100644 structurizr-export/src/test/java/com/structurizr/export/websequencediagrams/WebSequenceDiagramsExporterTests.java create mode 100644 structurizr-export/src/test/resources/groups.json create mode 100644 structurizr-export/src/test/resources/structurizr-36141-workspace.json create mode 100644 structurizr-export/src/test/resources/structurizr-54915-workspace.json create mode 100644 structurizr-graphviz/README.md create mode 100644 structurizr-graphviz/build.gradle create mode 100644 structurizr-graphviz/src/main/java/com/structurizr/graphviz/Constants.java create mode 100644 structurizr-graphviz/src/main/java/com/structurizr/graphviz/DOTDiagram.java create mode 100644 structurizr-graphviz/src/main/java/com/structurizr/graphviz/DOTExporter.java create mode 100644 structurizr-graphviz/src/main/java/com/structurizr/graphviz/GraphvizAutomaticLayout.java create mode 100644 structurizr-graphviz/src/main/java/com/structurizr/graphviz/RankDirection.java create mode 100644 structurizr-graphviz/src/main/java/com/structurizr/graphviz/SVGReader.java create mode 100644 structurizr-graphviz/src/test/java/com/structurizr/graphviz/DOTExporterTests.java create mode 100644 structurizr-graphviz/src/test/java/com/structurizr/graphviz/GraphvizAutomaticLayoutTests.java create mode 100644 structurizr-graphviz/src/test/java/com/structurizr/graphviz/SVGReaderTests.java create mode 100644 structurizr-graphviz/src/test/resources/graphviz/SystemContext.dot create mode 100644 structurizr-graphviz/src/test/resources/graphviz/SystemContext.dot.svg create mode 100644 structurizr-graphviz/src/test/resources/structurizr-54915-workspace.json create mode 100644 structurizr-import/README.md create mode 100644 structurizr-import/build.gradle create mode 100644 structurizr-import/src/main/java/com/structurizr/importer/diagrams/AbstractDiagramImporter.java create mode 100644 structurizr-import/src/main/java/com/structurizr/importer/diagrams/kroki/KrokiEncoder.java create mode 100644 structurizr-import/src/main/java/com/structurizr/importer/diagrams/kroki/KrokiImporter.java create mode 100644 structurizr-import/src/main/java/com/structurizr/importer/diagrams/mermaid/MermaidEncoder.java create mode 100644 structurizr-import/src/main/java/com/structurizr/importer/diagrams/mermaid/MermaidImporter.java create mode 100644 structurizr-import/src/main/java/com/structurizr/importer/diagrams/plantuml/PlantUMLEncoder.java create mode 100644 structurizr-import/src/main/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporter.java create mode 100644 structurizr-import/src/main/java/com/structurizr/importer/documentation/AdrToolsDecisionImporter.java create mode 100644 structurizr-import/src/main/java/com/structurizr/importer/documentation/DefaultDocumentationImporter.java create mode 100644 structurizr-import/src/main/java/com/structurizr/importer/documentation/DefaultImageImporter.java create mode 100644 structurizr-import/src/main/java/com/structurizr/importer/documentation/DocumentationImportException.java create mode 100644 structurizr-import/src/main/java/com/structurizr/importer/documentation/DocumentationImporter.java create mode 100644 structurizr-import/src/main/java/com/structurizr/importer/documentation/FormatFinder.java create mode 100644 structurizr-import/src/main/java/com/structurizr/importer/documentation/RecursiveDefaultDocumentationImporter.java create mode 100644 structurizr-import/src/test/java/com/structurizr/importer/diagrams/kroki/KrokiEncoderTests.java create mode 100644 structurizr-import/src/test/java/com/structurizr/importer/diagrams/kroki/KrokiImporterTests.java create mode 100644 structurizr-import/src/test/java/com/structurizr/importer/diagrams/mermaid/MermaidEncoderTests.java create mode 100644 structurizr-import/src/test/java/com/structurizr/importer/diagrams/mermaid/MermaidImporterTests.java create mode 100644 structurizr-import/src/test/java/com/structurizr/importer/diagrams/plantuml/PlantUMLEncoderTests.java create mode 100644 structurizr-import/src/test/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporterTests.java create mode 100644 structurizr-import/src/test/java/com/structurizr/importer/documentation/AdrToolsDecisionImporterTests.java create mode 100644 structurizr-import/src/test/java/com/structurizr/importer/documentation/DefaultDocumentImporterTests.java create mode 100644 structurizr-import/src/test/java/com/structurizr/importer/documentation/DefaultImageImporterTests.java create mode 100644 structurizr-import/src/test/java/com/structurizr/importer/documentation/FormatFinderTests.java create mode 100644 structurizr-import/src/test/java/com/structurizr/importer/documentation/RecursiveDefaultDocumentImporterTests.java create mode 100644 structurizr-import/src/test/resources/adrs/0001-record-architecture-decisions.md create mode 100644 structurizr-import/src/test/resources/adrs/0002-implement-as-shell-scripts.md create mode 100644 structurizr-import/src/test/resources/adrs/0003-single-command-with-subcommands.md create mode 100644 structurizr-import/src/test/resources/adrs/0004-markdown-format.md create mode 100644 structurizr-import/src/test/resources/adrs/0005-help-comments.md create mode 100644 structurizr-import/src/test/resources/adrs/0006-packaging-and-distribution-in-other-version-control-repositories.md create mode 100644 structurizr-import/src/test/resources/adrs/0007-invoke-adr-config-executable-to-get-configuration.md create mode 100644 structurizr-import/src/test/resources/adrs/0008-use-iso-8601-format-for-dates.md create mode 100644 structurizr-import/src/test/resources/adrs/0009-help-scripts.md create mode 100644 structurizr-import/src/test/resources/diagrams/kroki/diagram.dot create mode 100644 structurizr-import/src/test/resources/diagrams/mermaid/class.mmd create mode 100644 structurizr-import/src/test/resources/diagrams/mermaid/flowchart.mmd create mode 100644 structurizr-import/src/test/resources/diagrams/plantuml/with-title.puml create mode 100644 structurizr-import/src/test/resources/diagrams/plantuml/without-title.puml create mode 100644 structurizr-import/src/test/resources/docs/docs/01-section-1.md create mode 100644 structurizr-import/src/test/resources/docs/docs/02-section-2.markdown create mode 100644 structurizr-import/src/test/resources/docs/docs/03-section-3.text create mode 100644 structurizr-import/src/test/resources/docs/docs/04-section-4.adoc create mode 100644 structurizr-import/src/test/resources/docs/docs/05-section-5.asciidoc create mode 100644 structurizr-import/src/test/resources/docs/docs/06-section-6.asc create mode 100644 structurizr-import/src/test/resources/docs/docs/07-subdirectory/01-section-1.md create mode 100644 structurizr-import/src/test/resources/docs/docs/images/image.gif create mode 100644 structurizr-import/src/test/resources/docs/docs/images/image.jpeg create mode 100644 structurizr-import/src/test/resources/docs/docs/images/image.jpg create mode 100644 structurizr-import/src/test/resources/docs/docs/images/image.png create mode 100644 structurizr-import/src/test/resources/docs/images/image.gif create mode 100644 structurizr-import/src/test/resources/docs/images/image.jpeg create mode 100644 structurizr-import/src/test/resources/docs/images/image.jpg create mode 100644 structurizr-import/src/test/resources/docs/images/image.png create mode 100644 structurizr-import/src/test/resources/docs/images/image.svg create mode 100644 structurizr-import/src/test/resources/docs/images/images/image.gif create mode 100644 structurizr-import/src/test/resources/docs/images/images/image.jpeg create mode 100644 structurizr-import/src/test/resources/docs/images/images/image.jpg create mode 100644 structurizr-import/src/test/resources/docs/images/images/image.png create mode 100644 structurizr-import/src/test/resources/docs/images/noimages/readme.md diff --git a/README.md b/README.md index 9b45a95ef..41abcb2b6 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,13 @@ # Structurizr for Java -This GitHub repository is (1) a client library for the [Structurizr](https://structurizr.com) cloud service and on-premises installation -and (2) a way to create a Structurizr workspace using Java code. +This repository contains the source code for the following libraries: -Looking for the [Structurizr DSL](https://github.com/structurizr/dsl) instead? +- [structurizr-client](structurizr-client) +- [structurizr-core](structurizr-core) +- [structurizr-dsl](structurizr-dsl) +- [structurizr-export](structurizr-export) +- [structurizr-graphviz](structurizr-graphviz) +- [structurizr-import](structurizr-import) -- [Documentation](https://docs.structurizr.com/java) -- [Changelog](docs/changelog.md) \ No newline at end of file +- [Documentation](https://docs.structurizr.com) +- [Changelog](changelog.md) \ No newline at end of file diff --git a/build.gradle b/build.gradle index b1052523f..e939207e8 100644 --- a/build.gradle +++ b/build.gradle @@ -8,25 +8,12 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '1.29.0' + version = '2.0.0' repositories { mavenCentral() } - sourceSets { - main { - java { - srcDir 'src' - } - } - test { - java { - srcDir 'test/unit' - } - } - } - test { useJUnitPlatform() } diff --git a/changelog.md b/changelog.md new file mode 100644 index 000000000..cc90bf20c --- /dev/null +++ b/changelog.md @@ -0,0 +1,6 @@ +# Changelog + +## 2.0.0 (unreleased) + +- structurizr-core: Adds `Workspace.trim()` to trim a workspace of unused elements (i.e. those not associated with any views). + diff --git a/docs/changelog.md b/docs/changelog.md deleted file mode 100644 index 90451c105..000000000 --- a/docs/changelog.md +++ /dev/null @@ -1,402 +0,0 @@ -# Changelog - -## unreleased - -- Adds `Workspace.trim()` to trim a workspace of unused elements (i.e. those not associated with any views). - -## 1.29.0 (28th December 2023) - -- Adds `com.structurizr.api.AdminApiClient` as a client for the cloud service/on-premises admin APIs. -- Adds support for inter-workspace URLs of the form `{workspace:123456}/diagrams`. -- Deprecates `StructurizrClient` in favour of `WorkspaceApiClient`. - -## 1.28.1 (11th December 2023) - -- `AbstractWorkspace.clearConfiguration()` creates a new instance rather than nulling it. - -## 1.28.0 (19th November 2023) - -- Adds a flag to determine whether automatic layout has been applied or not. -- Adds support for perspective values. -- Adds the ability to scope and validate a workspace. - -## 1.27.0 (23rd October 2023) - -- Upgrades dependencies, targets Java 17. -- Adds a 'url' property to 'RelationshipView' (see https://github.com/structurizr/java/issues/214). - -## 1.26.1 (28th July 2023) - -- Adds the ability to specify the workspace visibility (private/public) via the workspace configuration. - -## 1.25.1 (26th July 2023) - -- Adds a `clearUsers()` method to clear configured users on the workspace configuration. - -## 1.25.0 (22nd July 2023) - -- Fixes https://github.com/structurizr/java/issues/213 (Views are not created automatically if non-English characters are used in software systems' names) -- Adds a way to load themes with a timeout. - -## 1.24.1 (5th April 2023) - -- Reduces visibility of `setOrder()` and `setDescription()` on `RelationshipView`, as they should not be public. - -## 1.24.0 (30th March 2023) - -- Adds a `group` property to deployment nodes, infrastructure nodes, software system instances, and container instances. - -## 1.23.2 (24th March 2023) - -- `DynamicView.endParallelSequences(true)` will now increment the counter when no relationships have been defined in the parallel sequence. - -## 1.23.1 (17th March 2023) - -- Deprecates the `setExternalBoundariesVisible` methods on `ContainerView`, `ComponentView`, and `DynamicView`. -- Removes the check for empty content when adding a documentation section. - -## 1.23.0 (11th March 2023) - -- Deprecates `Enterprise` and `Location` concepts. -- Adds properties to the model. - -## 1.22.3 (11th March 2023) - -- Adds better backwards compatibility for removal of documentation section titles. - -## 1.22.2 (10th March 2023) - -- Updates Jackson library dependency. - -## 1.22.1 (5th March 2023) - -- Removes unused documentation section title property. - -## 1.22.0 (5th March 2023) - -- Adds documentation to components. - -## 1.21.0 (26th February 2023) - -- __Breaking change__: Removes the concept of "code elements" from `Component`. -- Adds support for element/relationship URLs of the form `{workspace}/...` for linking to diagrams/documentation/decisions in the same workspace. - -## 1.20.1 (16th February 2023) - -- `ViewSet.getViews()` now includes all views. -- `ViewSet.getViewWithKey()` is now public. - -## 1.20.0 (16th February 2023) - -- __Breaking change__: Renamed `com.structurizr.view.View` to `com.structurizr.view.ModelView`. -- Added support for "image views". -- Added a `Window` shape. -- `ThemeUtils.toJson()` now includes the workspace branding logo and font in the resulting theme. - -## 1.19.0 (26th January 2023) - -- Fixes #196 (Named colours are case-sensitive). - -## 1.18.0 (15th January 2023) - -- Fixes #191 (Layout of relationships is reset when changing the description). -- Adds support for using (CSS/HTML) named colors instead of hex color codes (#192). - -## 1.17.0 (5th January 2023) - -- Fixes case-sensitivity inconsistencies related to element names and relationship descriptions (#183). -- Adds support for setting deployment node instances to positive integers or a range (e.g. 0..1, 0..N, 0..*, 1..N, 1..*, 5..10, etc). - -## 1.16.2 (22nd December 2022) - -- Upgraded dependencies. - -## 1.16.1 (27th October 2022) - -- Adds name-value properties to views. - -## 1.16.0 (24th October 2022) - -- Adds support for element style stroke widths. - -## 1.15.3 (11th October 2022) - -- Updates some transitive dependencies. - -## 1.15.2 (3rd October 2022) - -- Adds support for element icons being specified as filenames (rather than full URLs) in themes. - -## 1.15.1 (23rd September 2022) - -- Adds some additional functionality for getting and finding element/relationship styles. - -## 1.15.0 (13th September 2022) - -- Adds documentation section filenames into the model. -- Adds support for custom elements on dynamic views. - -## 1.14.1 (15th August 2022) - -- Enables `structurizr-core` to be used as a transitive dependency by consumers of `structurizr-client`. - -## 1.14.0 (14th August 2022) - -- Adds a helper method (`AbstractImpliedRelationshipsStrategy.createImpliedRelationship`) to create implied relationships, which can then be used by custom implementations. -- Provides a way to add specific relationships to dynamic views. - -## 1.13.0 (25th June 2022) - -- Adds support for name/value properties on element and relationship styles. - -## 1.12.2 (30th March 2022) - -- Adds support for sorting views by the order in which they are created. - -## 1.12.1 (2nd March 2022) - -- Renamed `Decision.Link.type` to `Decision.Link.description`. - -## 1.12.0 (1st March 2022) - -- Breaking API changes to how documentation and decisions are managed. -- Moved documentation importers/templates to [structurizr-documentation](https://github.com/structurizr/documentation). -- Moved examples to [structurizr-examples](https://github.com/structurizr/examples) -- Removal of deprecated `Model.addImplicitRelationships()` method. - -## 1.11.0 (18th February 2022) - -- Fixes #167 (ImpliedRelationship Strategy replication of URL and perspectives). -- Makes the `Decision.setContent()` method public, to allow pre-processing of content before workspace upload/rendering. - -## 1.10.1 (1st February 2022) - -- Makes the `Section.setContent()` method public, to allow pre-processing of content before workspace upload/rendering. - -## 1.10.0 (29th December 2021) - -- Adds support for different relationship line styles (solid, dashed, dotted). -- Adds the ability to indicate that individual views should not merge layout from remotes. -- Adds name/value properties to the view set configuration. - -## 1.9.10 (26th November 2021) - -- Promotes a couple of methods to be public; no functional changes. - -## 1.9.9 (16th October 2021) - -- Adds the implied relationships functionality for custom elements. -- "addDefaultElements" will now also add any connected custom elements. - -## 1.9.8 (1st October 2021) - -- Adds support for relationships from deployment nodes to infrastructure nodes. - -## 1.9.7 (9th September 2021) - -- Adds support for software system/container instances in multiple deployment groups. - -## 1.9.6 (31st August 2021) - -- Added validation logic to reject unsupported image data URIs. -- Fixes #166 (ContainerInstance/SoftwareSystemInstance and auto-generation of deployment diagram). - -## 1.9.5 (7th June 2021) - -- Provides a way to store view dimensions. - -## 1.9.4 (22nd May 2021) - -- Bug fixes to prevent parents and children to both be added to container/component views. - -## 1.9.3 (11th May 2021) - -- Added an `addTheme` method on `Configuration`. -- Removed the `addDefaultStyles` method on `Styles`. - -## 1.9.2 (27th April 2021) - -- Adds a `Diamond` shape. - -## 1.9.1 (28th March 2021) - -- Adds a `findTerminology` method on the `Terminology` class. -- `Styles.findElementStyle` better mirrors how the Structurizr web renderer deals with element styling. - -## 1.9.0 (20th March 2021) - -- Adds support for adding individual infrastructure nodes, software system instances, and container instances to a deployment view. -- Adds support for removing software system instances from deployment views. -- Improved support for themes (e.g. when exporting to PlantUML), which now works the same as described at [Structurizr - Themes](https://structurizr.com/help/themes). -- Adds support for "deployment groups", providing a way to scope how software system/container instance relationships are replicated when added to deployment nodes. __breaking change__ - -## 1.8.0 (20th February 2021) - -- Adds support for custom elements and custom views (experimental). -- Bug fixes and improved workspace validation. - -## 1.7.2 (2nd February 2021) - -- Bug fixes. - -## 1.7.1 (28th January 2021) - -- Bug fixes. - -## 1.7.0 (6th January 2021) - -- Removes the dynamic view restrictions related to adding containers/components outside the scoped software system/container. -- Adds an "externalBoundariesVisible" property to DynamicView, so that external software system/container boundaries can be shown/hidden. -- Enhanced the rules relating to whether elements can be added to a view or not. -- Enhanced the logic to merge layout information of elements on views. - -## 1.6.3 (30th November 2020) - -- When adding a relationship to a dynamic view, the first relationship between the source and destination would be chosen, even if there are multiple relationships with different technologies. This release adds a way to indicate which relationship (based upon technology) should be chosen. -- Suppress description warnings for software system instances. - -## 1.6.2 (10th October 2020) - -- Resolves an issue with the AutomaticDocumentationTemplate, where images were being included as documentation content. - -## 1.6.1 (27th September 2020) - -- Added a "recursive" option to the AutomaticDocumentationTemplate, so that sub-directories can optionally be scanned too. - -## 1.6.0 (18th September 2020) - -- Changed the way that internal canonical element names are generated, to improve layout merging for deployment views. -- getParent() of SoftwareSystemInstance and ContainerInstance now returns the parent deployment node. -- Refactoring and bug fixes. - -## 1.5.0 (4th August 2020) - -- Fixes #151: linked relationship tags were not being taken into account when finding relationship styling. -- Fixes #153: Allow relationships in DynamicView to go both ways without two relationships between Elements in Model. -- Adds support for software system instances on deployment views (#150: how do I provide tech details for an external system to show in Deployment View?) -- The interaction style on relationships no longer defaults to Synchronous. -- Adds support for software system instances on deployment views. - -## 1.4.8 (15th July 2020) - -- Implied relationships now also copy the interaction style and tags. -- Fixes a serialisation problem with themes and styles. - -## 1.4.7 (6th July 2020) - -- Remove default stroke styling. - -## 1.4.6 (6th July 2020) - -- Adds a way to load styles from external themes. - -## 1.4.5 (21st June 2020) - -- Bug fixes. - -## 1.4.4 (21st June 2020) - -- Adds an "addDefaultElements()" method to the static/deployment views. -- Adds an "addDefaultStyles()" method to Styles. -- Adds a "createDefaultViews()" method to Views. - -## 1.4.3 (19th June 2020) - -- Fixes a bug where all deployment nodes would be added to a deployment view, even if that view had an environment set. -- Adds support for removing deployment nodes, infrastructure nodes, and container instances from deployment views. -- Fixes a bug where deployment node instances could set to a non-positive integer. - -## 1.4.2 (18th June 2020) - -- Adds the ability to add container instances and infrastructure nodes to the same animation step on a deployment view. -- Adds the ability to override the Structurizr client agent string. - -## 1.4.1 (14th June 2020) - -- Fixes a bug that defaults the relationship interaction style to Synchronous, when it's specifically set to null. - -## 1.4.0 (5th June 2020) - -- Added a "Component" element shape. -- Added a "Dotted" element border style. -- Components from any container can now be added to a component view. -- Added an externalContainersBoundariesVisible property to ComponentView, to set whether container boundaries should be visible for "external" components (those outside the container in scope). -- Improved the support for creating [implied relationships](docs/implied-relationships.md). -- Added the ability to customize the symbols used when rendering metadata. -- Adds support for infrastructure nodes. -- Adds support for multiple themes. -- Adds support for curved relationship routing. - -## 1.3.5 (26th March 2020) - -- Added an externalSoftwareSystemBoundariesVisible property to ContainerView, to set whether software system boundaries should be visible for "external" containers (those outside the software system in scope). -- Added a 16:10 ratio paper size. - -## 1.3.4 (29th February 2020) - -- Split View.setAutomaticLayout(boolean) to enableAutomaticLayout() and disableAutomaticLayout() (__breaking change__). -- Added A1 and A0 paper sizes. -- Adds support for themes. -- Adds support for tags on deployment nodes. -- Adds support for animations on deployment views. -- Adds support for URLs on relationships. - -## 1.3.3 (24th December 2019) - -- Fixes a deserialization issue with component views. - -## 1.3.2 (22nd November 2019) - -- Added support for element stroke colours. - -## 1.3.1 (29th October 2019) - -- The automatic layout algorithm can now be configured on individual views. -- The structurizr-annotations library can now be more easily used with OSGi applications. -- Fixes a bug with the PlantUML and WebSequenceDiagram writers, where relationships were sorted incorrectly (alphabetically, rather than numerically). -- Fixes a bug that allows relationships to be created between parents and children. -- The way layout information is copied between different versions of a view is now configurable by setting a custom LayoutMergeStrategy on a per view basis. - -## 1.3.0 (3rd March 2019) - -- Added the ability to lock and unlock workspaces, to prevent concurrent updates. - -## 1.2.0 (3rd January 2019) - -- Fixes an issue with Java 11 and SSL handshaking. -- The terminology for relationships can now be customised. -- Added support for icons on element styles. -- Top-level deployment nodes can now be given an environment property, to represent which deployment environment they belong to (e.g. "Development", "Live", etc). -- Relationships can no longer be created between container instances (__breaking change__). -- When adding elements to views, you can now optionally specify whether relationships to/from that element are added. -- Provided a way to customize the sort order when displaying the list of views. - -## 1.1.0 (8th November 2018) - -- Added the ability to specify users who should have read-write or read-only workspace access, via the ```workspace.getConfiguration().addUser(username, role)``` method. - -## 1.0.0 (17th Oct 2018) - -- Added name-value properties to relationships. -- Added the ability to define animations on the static structure diagrams. -- Removed support for colours in the corporate branding feature (__breaking change__). -- The PlantUML writer can now export sequence diagrams. - -## 1.0.0-RC7 - -- HTTP-based health check interval and timeout can be specified via the factory method now (__breaking change__). Also added some documentation and an example. -- Added an ```endParallelSequence(boolean)``` method to the ```DynamicView``` class, which allows sequence numbering to continue. -- Fixed a bug where the software system associated with a SystemContextView could be removed from the view. -- Added support for architecture decision records. - -## 1.0.0-RC6 - -- Component finders are no longer idempotent, and an exception will be thrown if the same component is discovered more than once (__breaking change__). -- Removed the "groups" property of documentation sections (__breaking change__). -- Added some new shapes: web browser, mobile device (portrait and landscape), and robot. -- Addition of @NonNull annotations (JSR 305: Annotations for Software Defect Detection). -- Added the ability to enable/disable the enterprise boundary on system landscape and system context views. -- Added the ability to customise the terminology used when rendering views. -- Added the ability to hide element metadata and/or descriptions. -- The Spring component finder now supports the @Endpoint annotation. -- Bug fixes and performance enhancements. \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 62816092a..3b97826de 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,4 +1,8 @@ rootProject.name = 'structurizr-java' include 'structurizr-client' -include 'structurizr-core' \ No newline at end of file +include 'structurizr-core' +include 'structurizr-dsl' +include 'structurizr-export' +include 'structurizr-graphviz' +include 'structurizr-import' diff --git a/structurizr-client/README.md b/structurizr-client/README.md new file mode 100644 index 000000000..60a155cf1 --- /dev/null +++ b/structurizr-client/README.md @@ -0,0 +1,5 @@ +# structurizr-client + +[![Maven Central](https://img.shields.io/maven-central/v/com.structurizr/structurizr-client.svg?label=Maven%20Central)](https://search.maven.org/artifact/com.structurizr/structurizr-client) + +- [Documentation](https://docs.structurizr.com/java/workspace-api) diff --git a/structurizr-client/build.gradle b/structurizr-client/build.gradle index def7a58e6..f6a2c657e 100644 --- a/structurizr-client/build.gradle +++ b/structurizr-client/build.gradle @@ -13,8 +13,8 @@ dependencies { sourceSets { test { java { - srcDir 'test/unit' - srcDir 'test/integration' + srcDir 'src/test/java' + srcDir 'src/integrationTest/java' } } } \ No newline at end of file diff --git a/structurizr-client/test/integration/com/structurizr/api/BackwardsCompatibilityTests.java b/structurizr-client/src/integrationTest/java/com/structurizr/api/BackwardsCompatibilityTests.java similarity index 97% rename from structurizr-client/test/integration/com/structurizr/api/BackwardsCompatibilityTests.java rename to structurizr-client/src/integrationTest/java/com/structurizr/api/BackwardsCompatibilityTests.java index 7f30cc635..ca11f7cf7 100644 --- a/structurizr-client/test/integration/com/structurizr/api/BackwardsCompatibilityTests.java +++ b/structurizr-client/src/integrationTest/java/com/structurizr/api/BackwardsCompatibilityTests.java @@ -13,7 +13,7 @@ class BackwardsCompatibilityTests { - private static final File PATH_TO_WORKSPACE_FILES = new File("test/integration/backwardsCompatibility"); + private static final File PATH_TO_WORKSPACE_FILES = new File("./src/integrationTest/resources/backwardsCompatibility"); @Test void test() throws Exception { diff --git a/structurizr-client/test/integration/com/structurizr/api/StructurizrClientIntegrationTests.java b/structurizr-client/src/integrationTest/java/com/structurizr/api/StructurizrClientIntegrationTests.java similarity index 100% rename from structurizr-client/test/integration/com/structurizr/api/StructurizrClientIntegrationTests.java rename to structurizr-client/src/integrationTest/java/com/structurizr/api/StructurizrClientIntegrationTests.java diff --git a/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java b/structurizr-client/src/integrationTest/java/com/structurizr/api/WorkspaceRulesValidationTests.java similarity index 99% rename from structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java rename to structurizr-client/src/integrationTest/java/com/structurizr/api/WorkspaceRulesValidationTests.java index efd07a640..5e74670ed 100644 --- a/structurizr-client/test/integration/com/structurizr/api/WorkspaceRulesValidationTests.java +++ b/structurizr-client/src/integrationTest/java/com/structurizr/api/WorkspaceRulesValidationTests.java @@ -10,7 +10,7 @@ public class WorkspaceRulesValidationTests { - private static final File PATH_TO_WORKSPACE_FILES = new File("test/integration/workspaceValidation"); + private static final File PATH_TO_WORKSPACE_FILES = new File("./src/integrationTest/resources/workspaceValidation"); @Test void exceptionThrown_WhenElementIdsAreNotUnique() throws Exception { diff --git a/structurizr-client/test/integration/backwardsCompatibility/structurizr-31-workspace.json b/structurizr-client/src/integrationTest/resources/backwardsCompatibility/structurizr-31-workspace.json similarity index 100% rename from structurizr-client/test/integration/backwardsCompatibility/structurizr-31-workspace.json rename to structurizr-client/src/integrationTest/resources/backwardsCompatibility/structurizr-31-workspace.json diff --git a/structurizr-client/test/integration/backwardsCompatibility/structurizr-36141-workspace.json b/structurizr-client/src/integrationTest/resources/backwardsCompatibility/structurizr-36141-workspace.json similarity index 100% rename from structurizr-client/test/integration/backwardsCompatibility/structurizr-36141-workspace.json rename to structurizr-client/src/integrationTest/resources/backwardsCompatibility/structurizr-36141-workspace.json diff --git a/structurizr-client/test/integration/backwardsCompatibility/structurizr-39459-workspace.json b/structurizr-client/src/integrationTest/resources/backwardsCompatibility/structurizr-39459-workspace.json similarity index 100% rename from structurizr-client/test/integration/backwardsCompatibility/structurizr-39459-workspace.json rename to structurizr-client/src/integrationTest/resources/backwardsCompatibility/structurizr-39459-workspace.json diff --git a/structurizr-client/test/integration/workspaceValidation/ChildDeploymentNodeNamesAreNotUnique.json b/structurizr-client/src/integrationTest/resources/workspaceValidation/ChildDeploymentNodeNamesAreNotUnique.json similarity index 100% rename from structurizr-client/test/integration/workspaceValidation/ChildDeploymentNodeNamesAreNotUnique.json rename to structurizr-client/src/integrationTest/resources/workspaceValidation/ChildDeploymentNodeNamesAreNotUnique.json diff --git a/structurizr-client/test/integration/workspaceValidation/ComponentNamesAreNotUnique.json b/structurizr-client/src/integrationTest/resources/workspaceValidation/ComponentNamesAreNotUnique.json similarity index 100% rename from structurizr-client/test/integration/workspaceValidation/ComponentNamesAreNotUnique.json rename to structurizr-client/src/integrationTest/resources/workspaceValidation/ComponentNamesAreNotUnique.json diff --git a/structurizr-client/test/integration/workspaceValidation/ContainerAssociatedWithComponentViewIsMissingFromTheModel.json b/structurizr-client/src/integrationTest/resources/workspaceValidation/ContainerAssociatedWithComponentViewIsMissingFromTheModel.json similarity index 100% rename from structurizr-client/test/integration/workspaceValidation/ContainerAssociatedWithComponentViewIsMissingFromTheModel.json rename to structurizr-client/src/integrationTest/resources/workspaceValidation/ContainerAssociatedWithComponentViewIsMissingFromTheModel.json diff --git a/structurizr-client/test/integration/workspaceValidation/ContainerNamesAreNotUnique.json b/structurizr-client/src/integrationTest/resources/workspaceValidation/ContainerNamesAreNotUnique.json similarity index 100% rename from structurizr-client/test/integration/workspaceValidation/ContainerNamesAreNotUnique.json rename to structurizr-client/src/integrationTest/resources/workspaceValidation/ContainerNamesAreNotUnique.json diff --git a/structurizr-client/test/integration/workspaceValidation/ElementAssociatedWithDynamicViewIsMissingFromTheModel.json b/structurizr-client/src/integrationTest/resources/workspaceValidation/ElementAssociatedWithDynamicViewIsMissingFromTheModel.json similarity index 100% rename from structurizr-client/test/integration/workspaceValidation/ElementAssociatedWithDynamicViewIsMissingFromTheModel.json rename to structurizr-client/src/integrationTest/resources/workspaceValidation/ElementAssociatedWithDynamicViewIsMissingFromTheModel.json diff --git a/structurizr-client/test/integration/workspaceValidation/ElementIdsAreNotUnique.json b/structurizr-client/src/integrationTest/resources/workspaceValidation/ElementIdsAreNotUnique.json similarity index 100% rename from structurizr-client/test/integration/workspaceValidation/ElementIdsAreNotUnique.json rename to structurizr-client/src/integrationTest/resources/workspaceValidation/ElementIdsAreNotUnique.json diff --git a/structurizr-client/test/integration/workspaceValidation/ElementReferencedByViewIsMissingFromTheModel.json b/structurizr-client/src/integrationTest/resources/workspaceValidation/ElementReferencedByViewIsMissingFromTheModel.json similarity index 100% rename from structurizr-client/test/integration/workspaceValidation/ElementReferencedByViewIsMissingFromTheModel.json rename to structurizr-client/src/integrationTest/resources/workspaceValidation/ElementReferencedByViewIsMissingFromTheModel.json diff --git a/structurizr-client/test/integration/workspaceValidation/PeopleAndSoftwareSystemNamesAreNotUnique.json b/structurizr-client/src/integrationTest/resources/workspaceValidation/PeopleAndSoftwareSystemNamesAreNotUnique.json similarity index 100% rename from structurizr-client/test/integration/workspaceValidation/PeopleAndSoftwareSystemNamesAreNotUnique.json rename to structurizr-client/src/integrationTest/resources/workspaceValidation/PeopleAndSoftwareSystemNamesAreNotUnique.json diff --git a/structurizr-client/test/integration/workspaceValidation/RelationshipDescriptionsAreNotUnique.json b/structurizr-client/src/integrationTest/resources/workspaceValidation/RelationshipDescriptionsAreNotUnique.json similarity index 100% rename from structurizr-client/test/integration/workspaceValidation/RelationshipDescriptionsAreNotUnique.json rename to structurizr-client/src/integrationTest/resources/workspaceValidation/RelationshipDescriptionsAreNotUnique.json diff --git a/structurizr-client/test/integration/workspaceValidation/RelationshipIdsAreNotUnique.json b/structurizr-client/src/integrationTest/resources/workspaceValidation/RelationshipIdsAreNotUnique.json similarity index 100% rename from structurizr-client/test/integration/workspaceValidation/RelationshipIdsAreNotUnique.json rename to structurizr-client/src/integrationTest/resources/workspaceValidation/RelationshipIdsAreNotUnique.json diff --git a/structurizr-client/test/integration/workspaceValidation/RelationshipReferencedByViewIsMissingFromTheModel.json b/structurizr-client/src/integrationTest/resources/workspaceValidation/RelationshipReferencedByViewIsMissingFromTheModel.json similarity index 100% rename from structurizr-client/test/integration/workspaceValidation/RelationshipReferencedByViewIsMissingFromTheModel.json rename to structurizr-client/src/integrationTest/resources/workspaceValidation/RelationshipReferencedByViewIsMissingFromTheModel.json diff --git a/structurizr-client/test/integration/workspaceValidation/SoftwareSystemAssociatedWithContainerViewIsMissingFromTheModel.json b/structurizr-client/src/integrationTest/resources/workspaceValidation/SoftwareSystemAssociatedWithContainerViewIsMissingFromTheModel.json similarity index 100% rename from structurizr-client/test/integration/workspaceValidation/SoftwareSystemAssociatedWithContainerViewIsMissingFromTheModel.json rename to structurizr-client/src/integrationTest/resources/workspaceValidation/SoftwareSystemAssociatedWithContainerViewIsMissingFromTheModel.json diff --git a/structurizr-client/test/integration/workspaceValidation/SoftwareSystemAssociatedWithDeploymentViewIsMissingFromTheModel.json b/structurizr-client/src/integrationTest/resources/workspaceValidation/SoftwareSystemAssociatedWithDeploymentViewIsMissingFromTheModel.json similarity index 100% rename from structurizr-client/test/integration/workspaceValidation/SoftwareSystemAssociatedWithDeploymentViewIsMissingFromTheModel.json rename to structurizr-client/src/integrationTest/resources/workspaceValidation/SoftwareSystemAssociatedWithDeploymentViewIsMissingFromTheModel.json diff --git a/structurizr-client/test/integration/workspaceValidation/SoftwareSystemAssociatedWithSystemContextViewIsMissingFromTheModel.json b/structurizr-client/src/integrationTest/resources/workspaceValidation/SoftwareSystemAssociatedWithSystemContextViewIsMissingFromTheModel.json similarity index 100% rename from structurizr-client/test/integration/workspaceValidation/SoftwareSystemAssociatedWithSystemContextViewIsMissingFromTheModel.json rename to structurizr-client/src/integrationTest/resources/workspaceValidation/SoftwareSystemAssociatedWithSystemContextViewIsMissingFromTheModel.json diff --git a/structurizr-client/test/integration/workspaceValidation/TopLevelDeploymentNodeNamesAreNotUnique.json b/structurizr-client/src/integrationTest/resources/workspaceValidation/TopLevelDeploymentNodeNamesAreNotUnique.json similarity index 100% rename from structurizr-client/test/integration/workspaceValidation/TopLevelDeploymentNodeNamesAreNotUnique.json rename to structurizr-client/src/integrationTest/resources/workspaceValidation/TopLevelDeploymentNodeNamesAreNotUnique.json diff --git a/structurizr-client/test/integration/workspaceValidation/TopLevelDeploymentNodeNamesAreNotUniqueButTheyExistInDifferentEnvironments.json b/structurizr-client/src/integrationTest/resources/workspaceValidation/TopLevelDeploymentNodeNamesAreNotUniqueButTheyExistInDifferentEnvironments.json similarity index 100% rename from structurizr-client/test/integration/workspaceValidation/TopLevelDeploymentNodeNamesAreNotUniqueButTheyExistInDifferentEnvironments.json rename to structurizr-client/src/integrationTest/resources/workspaceValidation/TopLevelDeploymentNodeNamesAreNotUniqueButTheyExistInDifferentEnvironments.json diff --git a/structurizr-client/test/integration/workspaceValidation/ViewAssociatedWithFilteredViewIsMissingFromTheWorkspace.json b/structurizr-client/src/integrationTest/resources/workspaceValidation/ViewAssociatedWithFilteredViewIsMissingFromTheWorkspace.json similarity index 100% rename from structurizr-client/test/integration/workspaceValidation/ViewAssociatedWithFilteredViewIsMissingFromTheWorkspace.json rename to structurizr-client/src/integrationTest/resources/workspaceValidation/ViewAssociatedWithFilteredViewIsMissingFromTheWorkspace.json diff --git a/structurizr-client/test/integration/workspaceValidation/ViewKeysAreNotUnique.json b/structurizr-client/src/integrationTest/resources/workspaceValidation/ViewKeysAreNotUnique.json similarity index 100% rename from structurizr-client/test/integration/workspaceValidation/ViewKeysAreNotUnique.json rename to structurizr-client/src/integrationTest/resources/workspaceValidation/ViewKeysAreNotUnique.json diff --git a/structurizr-client/src/com/structurizr/api/AbstractApiClient.java b/structurizr-client/src/main/java/com/structurizr/api/AbstractApiClient.java similarity index 100% rename from structurizr-client/src/com/structurizr/api/AbstractApiClient.java rename to structurizr-client/src/main/java/com/structurizr/api/AbstractApiClient.java diff --git a/structurizr-client/src/com/structurizr/api/AdminApiClient.java b/structurizr-client/src/main/java/com/structurizr/api/AdminApiClient.java similarity index 100% rename from structurizr-client/src/com/structurizr/api/AdminApiClient.java rename to structurizr-client/src/main/java/com/structurizr/api/AdminApiClient.java diff --git a/structurizr-client/src/com/structurizr/api/ApiResponse.java b/structurizr-client/src/main/java/com/structurizr/api/ApiResponse.java similarity index 100% rename from structurizr-client/src/com/structurizr/api/ApiResponse.java rename to structurizr-client/src/main/java/com/structurizr/api/ApiResponse.java diff --git a/structurizr-client/src/com/structurizr/api/HashBasedMessageAuthenticationCode.java b/structurizr-client/src/main/java/com/structurizr/api/HashBasedMessageAuthenticationCode.java similarity index 100% rename from structurizr-client/src/com/structurizr/api/HashBasedMessageAuthenticationCode.java rename to structurizr-client/src/main/java/com/structurizr/api/HashBasedMessageAuthenticationCode.java diff --git a/structurizr-client/src/com/structurizr/api/HmacAuthorizationHeader.java b/structurizr-client/src/main/java/com/structurizr/api/HmacAuthorizationHeader.java similarity index 100% rename from structurizr-client/src/com/structurizr/api/HmacAuthorizationHeader.java rename to structurizr-client/src/main/java/com/structurizr/api/HmacAuthorizationHeader.java diff --git a/structurizr-client/src/com/structurizr/api/HmacContent.java b/structurizr-client/src/main/java/com/structurizr/api/HmacContent.java similarity index 100% rename from structurizr-client/src/com/structurizr/api/HmacContent.java rename to structurizr-client/src/main/java/com/structurizr/api/HmacContent.java diff --git a/structurizr-client/src/com/structurizr/api/HttpHeaders.java b/structurizr-client/src/main/java/com/structurizr/api/HttpHeaders.java similarity index 100% rename from structurizr-client/src/com/structurizr/api/HttpHeaders.java rename to structurizr-client/src/main/java/com/structurizr/api/HttpHeaders.java diff --git a/structurizr-client/src/com/structurizr/api/Md5Digest.java b/structurizr-client/src/main/java/com/structurizr/api/Md5Digest.java similarity index 100% rename from structurizr-client/src/com/structurizr/api/Md5Digest.java rename to structurizr-client/src/main/java/com/structurizr/api/Md5Digest.java diff --git a/structurizr-client/src/com/structurizr/api/StructurizrClient.java b/structurizr-client/src/main/java/com/structurizr/api/StructurizrClient.java similarity index 100% rename from structurizr-client/src/com/structurizr/api/StructurizrClient.java rename to structurizr-client/src/main/java/com/structurizr/api/StructurizrClient.java diff --git a/structurizr-client/src/com/structurizr/api/StructurizrClientException.java b/structurizr-client/src/main/java/com/structurizr/api/StructurizrClientException.java similarity index 100% rename from structurizr-client/src/com/structurizr/api/StructurizrClientException.java rename to structurizr-client/src/main/java/com/structurizr/api/StructurizrClientException.java diff --git a/structurizr-client/src/com/structurizr/api/WorkspaceApiClient.java b/structurizr-client/src/main/java/com/structurizr/api/WorkspaceApiClient.java similarity index 100% rename from structurizr-client/src/com/structurizr/api/WorkspaceApiClient.java rename to structurizr-client/src/main/java/com/structurizr/api/WorkspaceApiClient.java diff --git a/structurizr-client/src/com/structurizr/api/WorkspaceMetadata.java b/structurizr-client/src/main/java/com/structurizr/api/WorkspaceMetadata.java similarity index 100% rename from structurizr-client/src/com/structurizr/api/WorkspaceMetadata.java rename to structurizr-client/src/main/java/com/structurizr/api/WorkspaceMetadata.java diff --git a/structurizr-client/src/com/structurizr/api/Workspaces.java b/structurizr-client/src/main/java/com/structurizr/api/Workspaces.java similarity index 100% rename from structurizr-client/src/com/structurizr/api/Workspaces.java rename to structurizr-client/src/main/java/com/structurizr/api/Workspaces.java diff --git a/structurizr-client/src/com/structurizr/encryption/AesEncryptionStrategy.java b/structurizr-client/src/main/java/com/structurizr/encryption/AesEncryptionStrategy.java similarity index 100% rename from structurizr-client/src/com/structurizr/encryption/AesEncryptionStrategy.java rename to structurizr-client/src/main/java/com/structurizr/encryption/AesEncryptionStrategy.java diff --git a/structurizr-client/src/com/structurizr/encryption/EncryptedWorkspace.java b/structurizr-client/src/main/java/com/structurizr/encryption/EncryptedWorkspace.java similarity index 100% rename from structurizr-client/src/com/structurizr/encryption/EncryptedWorkspace.java rename to structurizr-client/src/main/java/com/structurizr/encryption/EncryptedWorkspace.java diff --git a/structurizr-client/src/com/structurizr/encryption/EncryptionLocation.java b/structurizr-client/src/main/java/com/structurizr/encryption/EncryptionLocation.java similarity index 100% rename from structurizr-client/src/com/structurizr/encryption/EncryptionLocation.java rename to structurizr-client/src/main/java/com/structurizr/encryption/EncryptionLocation.java diff --git a/structurizr-client/src/com/structurizr/encryption/EncryptionStrategy.java b/structurizr-client/src/main/java/com/structurizr/encryption/EncryptionStrategy.java similarity index 100% rename from structurizr-client/src/com/structurizr/encryption/EncryptionStrategy.java rename to structurizr-client/src/main/java/com/structurizr/encryption/EncryptionStrategy.java diff --git a/structurizr-client/src/com/structurizr/io/WorkspaceReader.java b/structurizr-client/src/main/java/com/structurizr/io/WorkspaceReader.java similarity index 100% rename from structurizr-client/src/com/structurizr/io/WorkspaceReader.java rename to structurizr-client/src/main/java/com/structurizr/io/WorkspaceReader.java diff --git a/structurizr-client/src/com/structurizr/io/WorkspaceReaderException.java b/structurizr-client/src/main/java/com/structurizr/io/WorkspaceReaderException.java similarity index 100% rename from structurizr-client/src/com/structurizr/io/WorkspaceReaderException.java rename to structurizr-client/src/main/java/com/structurizr/io/WorkspaceReaderException.java diff --git a/structurizr-client/src/com/structurizr/io/WorkspaceWriter.java b/structurizr-client/src/main/java/com/structurizr/io/WorkspaceWriter.java similarity index 100% rename from structurizr-client/src/com/structurizr/io/WorkspaceWriter.java rename to structurizr-client/src/main/java/com/structurizr/io/WorkspaceWriter.java diff --git a/structurizr-client/src/com/structurizr/io/WorkspaceWriterException.java b/structurizr-client/src/main/java/com/structurizr/io/WorkspaceWriterException.java similarity index 100% rename from structurizr-client/src/com/structurizr/io/WorkspaceWriterException.java rename to structurizr-client/src/main/java/com/structurizr/io/WorkspaceWriterException.java diff --git a/structurizr-client/src/com/structurizr/io/json/AbstractJsonReader.java b/structurizr-client/src/main/java/com/structurizr/io/json/AbstractJsonReader.java similarity index 100% rename from structurizr-client/src/com/structurizr/io/json/AbstractJsonReader.java rename to structurizr-client/src/main/java/com/structurizr/io/json/AbstractJsonReader.java diff --git a/structurizr-client/src/com/structurizr/io/json/AbstractJsonWriter.java b/structurizr-client/src/main/java/com/structurizr/io/json/AbstractJsonWriter.java similarity index 100% rename from structurizr-client/src/com/structurizr/io/json/AbstractJsonWriter.java rename to structurizr-client/src/main/java/com/structurizr/io/json/AbstractJsonWriter.java diff --git a/structurizr-client/src/com/structurizr/io/json/EncryptedJsonReader.java b/structurizr-client/src/main/java/com/structurizr/io/json/EncryptedJsonReader.java similarity index 100% rename from structurizr-client/src/com/structurizr/io/json/EncryptedJsonReader.java rename to structurizr-client/src/main/java/com/structurizr/io/json/EncryptedJsonReader.java diff --git a/structurizr-client/src/com/structurizr/io/json/EncryptedJsonWriter.java b/structurizr-client/src/main/java/com/structurizr/io/json/EncryptedJsonWriter.java similarity index 100% rename from structurizr-client/src/com/structurizr/io/json/EncryptedJsonWriter.java rename to structurizr-client/src/main/java/com/structurizr/io/json/EncryptedJsonWriter.java diff --git a/structurizr-client/src/com/structurizr/io/json/JsonReader.java b/structurizr-client/src/main/java/com/structurizr/io/json/JsonReader.java similarity index 100% rename from structurizr-client/src/com/structurizr/io/json/JsonReader.java rename to structurizr-client/src/main/java/com/structurizr/io/json/JsonReader.java diff --git a/structurizr-client/src/com/structurizr/io/json/JsonWriter.java b/structurizr-client/src/main/java/com/structurizr/io/json/JsonWriter.java similarity index 100% rename from structurizr-client/src/com/structurizr/io/json/JsonWriter.java rename to structurizr-client/src/main/java/com/structurizr/io/json/JsonWriter.java diff --git a/structurizr-client/src/com/structurizr/util/WorkspaceUtils.java b/structurizr-client/src/main/java/com/structurizr/util/WorkspaceUtils.java similarity index 100% rename from structurizr-client/src/com/structurizr/util/WorkspaceUtils.java rename to structurizr-client/src/main/java/com/structurizr/util/WorkspaceUtils.java diff --git a/structurizr-client/src/com/structurizr/view/ThemeUtils.java b/structurizr-client/src/main/java/com/structurizr/view/ThemeUtils.java similarity index 100% rename from structurizr-client/src/com/structurizr/view/ThemeUtils.java rename to structurizr-client/src/main/java/com/structurizr/view/ThemeUtils.java diff --git a/structurizr-client/test/unit/com/structurizr/api/ApiResponseTests.java b/structurizr-client/src/test/java/com/structurizr/api/ApiResponseTests.java similarity index 100% rename from structurizr-client/test/unit/com/structurizr/api/ApiResponseTests.java rename to structurizr-client/src/test/java/com/structurizr/api/ApiResponseTests.java diff --git a/structurizr-client/test/unit/com/structurizr/api/HashBasedMessageAuthenticationCodeTests.java b/structurizr-client/src/test/java/com/structurizr/api/HashBasedMessageAuthenticationCodeTests.java similarity index 100% rename from structurizr-client/test/unit/com/structurizr/api/HashBasedMessageAuthenticationCodeTests.java rename to structurizr-client/src/test/java/com/structurizr/api/HashBasedMessageAuthenticationCodeTests.java diff --git a/structurizr-client/test/unit/com/structurizr/api/HmacAuthorizationHeaderTests.java b/structurizr-client/src/test/java/com/structurizr/api/HmacAuthorizationHeaderTests.java similarity index 100% rename from structurizr-client/test/unit/com/structurizr/api/HmacAuthorizationHeaderTests.java rename to structurizr-client/src/test/java/com/structurizr/api/HmacAuthorizationHeaderTests.java diff --git a/structurizr-client/test/unit/com/structurizr/api/HmacContentTests.java b/structurizr-client/src/test/java/com/structurizr/api/HmacContentTests.java similarity index 100% rename from structurizr-client/test/unit/com/structurizr/api/HmacContentTests.java rename to structurizr-client/src/test/java/com/structurizr/api/HmacContentTests.java diff --git a/structurizr-client/test/unit/com/structurizr/api/Md5DigestTests.java b/structurizr-client/src/test/java/com/structurizr/api/Md5DigestTests.java similarity index 100% rename from structurizr-client/test/unit/com/structurizr/api/Md5DigestTests.java rename to structurizr-client/src/test/java/com/structurizr/api/Md5DigestTests.java diff --git a/structurizr-client/test/unit/com/structurizr/api/StructurizrClientTests.java b/structurizr-client/src/test/java/com/structurizr/api/StructurizrClientTests.java similarity index 100% rename from structurizr-client/test/unit/com/structurizr/api/StructurizrClientTests.java rename to structurizr-client/src/test/java/com/structurizr/api/StructurizrClientTests.java diff --git a/structurizr-client/test/unit/com/structurizr/encryption/AesEncryptionStrategyTests.java b/structurizr-client/src/test/java/com/structurizr/encryption/AesEncryptionStrategyTests.java similarity index 100% rename from structurizr-client/test/unit/com/structurizr/encryption/AesEncryptionStrategyTests.java rename to structurizr-client/src/test/java/com/structurizr/encryption/AesEncryptionStrategyTests.java diff --git a/structurizr-client/test/unit/com/structurizr/encryption/EncryptedWorkspaceTests.java b/structurizr-client/src/test/java/com/structurizr/encryption/EncryptedWorkspaceTests.java similarity index 100% rename from structurizr-client/test/unit/com/structurizr/encryption/EncryptedWorkspaceTests.java rename to structurizr-client/src/test/java/com/structurizr/encryption/EncryptedWorkspaceTests.java diff --git a/structurizr-client/test/unit/com/structurizr/encryption/MockEncryptionStrategy.java b/structurizr-client/src/test/java/com/structurizr/encryption/MockEncryptionStrategy.java similarity index 100% rename from structurizr-client/test/unit/com/structurizr/encryption/MockEncryptionStrategy.java rename to structurizr-client/src/test/java/com/structurizr/encryption/MockEncryptionStrategy.java diff --git a/structurizr-client/test/unit/com/structurizr/io/json/EncryptedJsonTests.java b/structurizr-client/src/test/java/com/structurizr/io/json/EncryptedJsonTests.java similarity index 100% rename from structurizr-client/test/unit/com/structurizr/io/json/EncryptedJsonTests.java rename to structurizr-client/src/test/java/com/structurizr/io/json/EncryptedJsonTests.java diff --git a/structurizr-client/test/unit/com/structurizr/io/json/EncryptedJsonWriterTests.java b/structurizr-client/src/test/java/com/structurizr/io/json/EncryptedJsonWriterTests.java similarity index 100% rename from structurizr-client/test/unit/com/structurizr/io/json/EncryptedJsonWriterTests.java rename to structurizr-client/src/test/java/com/structurizr/io/json/EncryptedJsonWriterTests.java diff --git a/structurizr-client/test/unit/com/structurizr/io/json/JsonTests.java b/structurizr-client/src/test/java/com/structurizr/io/json/JsonTests.java similarity index 100% rename from structurizr-client/test/unit/com/structurizr/io/json/JsonTests.java rename to structurizr-client/src/test/java/com/structurizr/io/json/JsonTests.java diff --git a/structurizr-client/test/unit/com/structurizr/io/json/JsonWriterTests.java b/structurizr-client/src/test/java/com/structurizr/io/json/JsonWriterTests.java similarity index 100% rename from structurizr-client/test/unit/com/structurizr/io/json/JsonWriterTests.java rename to structurizr-client/src/test/java/com/structurizr/io/json/JsonWriterTests.java diff --git a/structurizr-client/test/unit/com/structurizr/util/WorkspaceUtilsTests.java b/structurizr-client/src/test/java/com/structurizr/util/WorkspaceUtilsTests.java similarity index 100% rename from structurizr-client/test/unit/com/structurizr/util/WorkspaceUtilsTests.java rename to structurizr-client/src/test/java/com/structurizr/util/WorkspaceUtilsTests.java diff --git a/structurizr-client/test/unit/com/structurizr/view/ThemeUtilsTests.java b/structurizr-client/src/test/java/com/structurizr/view/ThemeUtilsTests.java similarity index 100% rename from structurizr-client/test/unit/com/structurizr/view/ThemeUtilsTests.java rename to structurizr-client/src/test/java/com/structurizr/view/ThemeUtilsTests.java diff --git a/structurizr-core/README.md b/structurizr-core/README.md new file mode 100644 index 000000000..23e5b133d --- /dev/null +++ b/structurizr-core/README.md @@ -0,0 +1,6 @@ +# structurizr-core + +[![Maven Central](https://img.shields.io/maven-central/v/com.structurizr/structurizr-core.svg?label=Maven%20Central)](https://search.maven.org/artifact/com.structurizr/structurizr-core) + +- [Documentation](https://docs.structurizr.com/java) + diff --git a/structurizr-core/src/com/structurizr/AbstractWorkspace.java b/structurizr-core/src/main/java/com/structurizr/AbstractWorkspace.java similarity index 100% rename from structurizr-core/src/com/structurizr/AbstractWorkspace.java rename to structurizr-core/src/main/java/com/structurizr/AbstractWorkspace.java diff --git a/structurizr-core/src/com/structurizr/PropertyHolder.java b/structurizr-core/src/main/java/com/structurizr/PropertyHolder.java similarity index 100% rename from structurizr-core/src/com/structurizr/PropertyHolder.java rename to structurizr-core/src/main/java/com/structurizr/PropertyHolder.java diff --git a/structurizr-core/src/com/structurizr/Workspace.java b/structurizr-core/src/main/java/com/structurizr/Workspace.java similarity index 100% rename from structurizr-core/src/com/structurizr/Workspace.java rename to structurizr-core/src/main/java/com/structurizr/Workspace.java diff --git a/structurizr-core/src/com/structurizr/WorkspaceValidationException.java b/structurizr-core/src/main/java/com/structurizr/WorkspaceValidationException.java similarity index 100% rename from structurizr-core/src/com/structurizr/WorkspaceValidationException.java rename to structurizr-core/src/main/java/com/structurizr/WorkspaceValidationException.java diff --git a/structurizr-core/src/com/structurizr/configuration/Role.java b/structurizr-core/src/main/java/com/structurizr/configuration/Role.java similarity index 100% rename from structurizr-core/src/com/structurizr/configuration/Role.java rename to structurizr-core/src/main/java/com/structurizr/configuration/Role.java diff --git a/structurizr-core/src/com/structurizr/configuration/User.java b/structurizr-core/src/main/java/com/structurizr/configuration/User.java similarity index 100% rename from structurizr-core/src/com/structurizr/configuration/User.java rename to structurizr-core/src/main/java/com/structurizr/configuration/User.java diff --git a/structurizr-core/src/com/structurizr/configuration/Visibility.java b/structurizr-core/src/main/java/com/structurizr/configuration/Visibility.java similarity index 100% rename from structurizr-core/src/com/structurizr/configuration/Visibility.java rename to structurizr-core/src/main/java/com/structurizr/configuration/Visibility.java diff --git a/structurizr-core/src/com/structurizr/configuration/WorkspaceConfiguration.java b/structurizr-core/src/main/java/com/structurizr/configuration/WorkspaceConfiguration.java similarity index 100% rename from structurizr-core/src/com/structurizr/configuration/WorkspaceConfiguration.java rename to structurizr-core/src/main/java/com/structurizr/configuration/WorkspaceConfiguration.java diff --git a/structurizr-core/src/com/structurizr/configuration/WorkspaceScope.java b/structurizr-core/src/main/java/com/structurizr/configuration/WorkspaceScope.java similarity index 100% rename from structurizr-core/src/com/structurizr/configuration/WorkspaceScope.java rename to structurizr-core/src/main/java/com/structurizr/configuration/WorkspaceScope.java diff --git a/structurizr-core/src/com/structurizr/documentation/Decision.java b/structurizr-core/src/main/java/com/structurizr/documentation/Decision.java similarity index 100% rename from structurizr-core/src/com/structurizr/documentation/Decision.java rename to structurizr-core/src/main/java/com/structurizr/documentation/Decision.java diff --git a/structurizr-core/src/com/structurizr/documentation/Documentable.java b/structurizr-core/src/main/java/com/structurizr/documentation/Documentable.java similarity index 100% rename from structurizr-core/src/com/structurizr/documentation/Documentable.java rename to structurizr-core/src/main/java/com/structurizr/documentation/Documentable.java diff --git a/structurizr-core/src/com/structurizr/documentation/Documentation.java b/structurizr-core/src/main/java/com/structurizr/documentation/Documentation.java similarity index 100% rename from structurizr-core/src/com/structurizr/documentation/Documentation.java rename to structurizr-core/src/main/java/com/structurizr/documentation/Documentation.java diff --git a/structurizr-core/src/com/structurizr/documentation/DocumentationContent.java b/structurizr-core/src/main/java/com/structurizr/documentation/DocumentationContent.java similarity index 100% rename from structurizr-core/src/com/structurizr/documentation/DocumentationContent.java rename to structurizr-core/src/main/java/com/structurizr/documentation/DocumentationContent.java diff --git a/structurizr-core/src/com/structurizr/documentation/Format.java b/structurizr-core/src/main/java/com/structurizr/documentation/Format.java similarity index 100% rename from structurizr-core/src/com/structurizr/documentation/Format.java rename to structurizr-core/src/main/java/com/structurizr/documentation/Format.java diff --git a/structurizr-core/src/com/structurizr/documentation/Image.java b/structurizr-core/src/main/java/com/structurizr/documentation/Image.java similarity index 100% rename from structurizr-core/src/com/structurizr/documentation/Image.java rename to structurizr-core/src/main/java/com/structurizr/documentation/Image.java diff --git a/structurizr-core/src/com/structurizr/documentation/Section.java b/structurizr-core/src/main/java/com/structurizr/documentation/Section.java similarity index 100% rename from structurizr-core/src/com/structurizr/documentation/Section.java rename to structurizr-core/src/main/java/com/structurizr/documentation/Section.java diff --git a/structurizr-core/src/com/structurizr/model/AbstractImpliedRelationshipsStrategy.java b/structurizr-core/src/main/java/com/structurizr/model/AbstractImpliedRelationshipsStrategy.java similarity index 100% rename from structurizr-core/src/com/structurizr/model/AbstractImpliedRelationshipsStrategy.java rename to structurizr-core/src/main/java/com/structurizr/model/AbstractImpliedRelationshipsStrategy.java diff --git a/structurizr-core/src/com/structurizr/model/CanonicalNameGenerator.java b/structurizr-core/src/main/java/com/structurizr/model/CanonicalNameGenerator.java similarity index 100% rename from structurizr-core/src/com/structurizr/model/CanonicalNameGenerator.java rename to structurizr-core/src/main/java/com/structurizr/model/CanonicalNameGenerator.java diff --git a/structurizr-core/src/com/structurizr/model/Component.java b/structurizr-core/src/main/java/com/structurizr/model/Component.java similarity index 100% rename from structurizr-core/src/com/structurizr/model/Component.java rename to structurizr-core/src/main/java/com/structurizr/model/Component.java diff --git a/structurizr-core/src/com/structurizr/model/Constants.java b/structurizr-core/src/main/java/com/structurizr/model/Constants.java similarity index 100% rename from structurizr-core/src/com/structurizr/model/Constants.java rename to structurizr-core/src/main/java/com/structurizr/model/Constants.java diff --git a/structurizr-core/src/com/structurizr/model/Container.java b/structurizr-core/src/main/java/com/structurizr/model/Container.java similarity index 100% rename from structurizr-core/src/com/structurizr/model/Container.java rename to structurizr-core/src/main/java/com/structurizr/model/Container.java diff --git a/structurizr-core/src/com/structurizr/model/ContainerInstance.java b/structurizr-core/src/main/java/com/structurizr/model/ContainerInstance.java similarity index 100% rename from structurizr-core/src/com/structurizr/model/ContainerInstance.java rename to structurizr-core/src/main/java/com/structurizr/model/ContainerInstance.java diff --git a/structurizr-core/src/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy.java b/structurizr-core/src/main/java/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy.java similarity index 100% rename from structurizr-core/src/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy.java rename to structurizr-core/src/main/java/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy.java diff --git a/structurizr-core/src/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategy.java b/structurizr-core/src/main/java/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategy.java similarity index 100% rename from structurizr-core/src/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategy.java rename to structurizr-core/src/main/java/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategy.java diff --git a/structurizr-core/src/com/structurizr/model/CustomElement.java b/structurizr-core/src/main/java/com/structurizr/model/CustomElement.java similarity index 100% rename from structurizr-core/src/com/structurizr/model/CustomElement.java rename to structurizr-core/src/main/java/com/structurizr/model/CustomElement.java diff --git a/structurizr-core/src/com/structurizr/model/DefaultImpliedRelationshipsStrategy.java b/structurizr-core/src/main/java/com/structurizr/model/DefaultImpliedRelationshipsStrategy.java similarity index 100% rename from structurizr-core/src/com/structurizr/model/DefaultImpliedRelationshipsStrategy.java rename to structurizr-core/src/main/java/com/structurizr/model/DefaultImpliedRelationshipsStrategy.java diff --git a/structurizr-core/src/com/structurizr/model/DeploymentElement.java b/structurizr-core/src/main/java/com/structurizr/model/DeploymentElement.java similarity index 100% rename from structurizr-core/src/com/structurizr/model/DeploymentElement.java rename to structurizr-core/src/main/java/com/structurizr/model/DeploymentElement.java diff --git a/structurizr-core/src/com/structurizr/model/DeploymentNode.java b/structurizr-core/src/main/java/com/structurizr/model/DeploymentNode.java similarity index 98% rename from structurizr-core/src/com/structurizr/model/DeploymentNode.java rename to structurizr-core/src/main/java/com/structurizr/model/DeploymentNode.java index abdd8308f..e40aaeac5 100644 --- a/structurizr-core/src/com/structurizr/model/DeploymentNode.java +++ b/structurizr-core/src/main/java/com/structurizr/model/DeploymentNode.java @@ -330,6 +330,16 @@ void setInfrastructureNodes(Set infrastructureNodes) { } } + /** + * Determines whether this deployment node has any infrastructure nodes. + * + * @return true if it has infrastructure nodes, false otherwise + */ + @JsonIgnore + public boolean hasInfrastructureNodes() { + return !infrastructureNodes.isEmpty(); + } + /** * Determines whether this deployment node has any child deployment nodes. * diff --git a/structurizr-core/src/com/structurizr/model/Element.java b/structurizr-core/src/main/java/com/structurizr/model/Element.java similarity index 100% rename from structurizr-core/src/com/structurizr/model/Element.java rename to structurizr-core/src/main/java/com/structurizr/model/Element.java diff --git a/structurizr-core/src/com/structurizr/model/Enterprise.java b/structurizr-core/src/main/java/com/structurizr/model/Enterprise.java similarity index 100% rename from structurizr-core/src/com/structurizr/model/Enterprise.java rename to structurizr-core/src/main/java/com/structurizr/model/Enterprise.java diff --git a/structurizr-core/src/com/structurizr/model/GroupableElement.java b/structurizr-core/src/main/java/com/structurizr/model/GroupableElement.java similarity index 100% rename from structurizr-core/src/com/structurizr/model/GroupableElement.java rename to structurizr-core/src/main/java/com/structurizr/model/GroupableElement.java diff --git a/structurizr-core/src/com/structurizr/model/HttpHealthCheck.java b/structurizr-core/src/main/java/com/structurizr/model/HttpHealthCheck.java similarity index 100% rename from structurizr-core/src/com/structurizr/model/HttpHealthCheck.java rename to structurizr-core/src/main/java/com/structurizr/model/HttpHealthCheck.java diff --git a/structurizr-core/src/com/structurizr/model/IdGenerator.java b/structurizr-core/src/main/java/com/structurizr/model/IdGenerator.java similarity index 100% rename from structurizr-core/src/com/structurizr/model/IdGenerator.java rename to structurizr-core/src/main/java/com/structurizr/model/IdGenerator.java diff --git a/structurizr-core/src/com/structurizr/model/ImpliedRelationshipsStrategy.java b/structurizr-core/src/main/java/com/structurizr/model/ImpliedRelationshipsStrategy.java similarity index 100% rename from structurizr-core/src/com/structurizr/model/ImpliedRelationshipsStrategy.java rename to structurizr-core/src/main/java/com/structurizr/model/ImpliedRelationshipsStrategy.java diff --git a/structurizr-core/src/com/structurizr/model/InfrastructureNode.java b/structurizr-core/src/main/java/com/structurizr/model/InfrastructureNode.java similarity index 100% rename from structurizr-core/src/com/structurizr/model/InfrastructureNode.java rename to structurizr-core/src/main/java/com/structurizr/model/InfrastructureNode.java diff --git a/structurizr-core/src/com/structurizr/model/InteractionStyle.java b/structurizr-core/src/main/java/com/structurizr/model/InteractionStyle.java similarity index 100% rename from structurizr-core/src/com/structurizr/model/InteractionStyle.java rename to structurizr-core/src/main/java/com/structurizr/model/InteractionStyle.java diff --git a/structurizr-core/src/com/structurizr/model/Location.java b/structurizr-core/src/main/java/com/structurizr/model/Location.java similarity index 100% rename from structurizr-core/src/com/structurizr/model/Location.java rename to structurizr-core/src/main/java/com/structurizr/model/Location.java diff --git a/structurizr-core/src/com/structurizr/model/Model.java b/structurizr-core/src/main/java/com/structurizr/model/Model.java similarity index 100% rename from structurizr-core/src/com/structurizr/model/Model.java rename to structurizr-core/src/main/java/com/structurizr/model/Model.java diff --git a/structurizr-core/src/com/structurizr/model/ModelItem.java b/structurizr-core/src/main/java/com/structurizr/model/ModelItem.java similarity index 100% rename from structurizr-core/src/com/structurizr/model/ModelItem.java rename to structurizr-core/src/main/java/com/structurizr/model/ModelItem.java diff --git a/structurizr-core/src/com/structurizr/model/Person.java b/structurizr-core/src/main/java/com/structurizr/model/Person.java similarity index 100% rename from structurizr-core/src/com/structurizr/model/Person.java rename to structurizr-core/src/main/java/com/structurizr/model/Person.java diff --git a/structurizr-core/src/com/structurizr/model/Perspective.java b/structurizr-core/src/main/java/com/structurizr/model/Perspective.java similarity index 100% rename from structurizr-core/src/com/structurizr/model/Perspective.java rename to structurizr-core/src/main/java/com/structurizr/model/Perspective.java diff --git a/structurizr-core/src/com/structurizr/model/Relationship.java b/structurizr-core/src/main/java/com/structurizr/model/Relationship.java similarity index 100% rename from structurizr-core/src/com/structurizr/model/Relationship.java rename to structurizr-core/src/main/java/com/structurizr/model/Relationship.java diff --git a/structurizr-core/src/com/structurizr/model/SequentialIntegerIdGeneratorStrategy.java b/structurizr-core/src/main/java/com/structurizr/model/SequentialIntegerIdGeneratorStrategy.java similarity index 100% rename from structurizr-core/src/com/structurizr/model/SequentialIntegerIdGeneratorStrategy.java rename to structurizr-core/src/main/java/com/structurizr/model/SequentialIntegerIdGeneratorStrategy.java diff --git a/structurizr-core/src/com/structurizr/model/SoftwareSystem.java b/structurizr-core/src/main/java/com/structurizr/model/SoftwareSystem.java similarity index 100% rename from structurizr-core/src/com/structurizr/model/SoftwareSystem.java rename to structurizr-core/src/main/java/com/structurizr/model/SoftwareSystem.java diff --git a/structurizr-core/src/com/structurizr/model/SoftwareSystemInstance.java b/structurizr-core/src/main/java/com/structurizr/model/SoftwareSystemInstance.java similarity index 100% rename from structurizr-core/src/com/structurizr/model/SoftwareSystemInstance.java rename to structurizr-core/src/main/java/com/structurizr/model/SoftwareSystemInstance.java diff --git a/structurizr-core/src/com/structurizr/model/StaticStructureElement.java b/structurizr-core/src/main/java/com/structurizr/model/StaticStructureElement.java similarity index 100% rename from structurizr-core/src/com/structurizr/model/StaticStructureElement.java rename to structurizr-core/src/main/java/com/structurizr/model/StaticStructureElement.java diff --git a/structurizr-core/src/com/structurizr/model/StaticStructureElementInstance.java b/structurizr-core/src/main/java/com/structurizr/model/StaticStructureElementInstance.java similarity index 100% rename from structurizr-core/src/com/structurizr/model/StaticStructureElementInstance.java rename to structurizr-core/src/main/java/com/structurizr/model/StaticStructureElementInstance.java diff --git a/structurizr-core/src/com/structurizr/model/Tags.java b/structurizr-core/src/main/java/com/structurizr/model/Tags.java similarity index 100% rename from structurizr-core/src/com/structurizr/model/Tags.java rename to structurizr-core/src/main/java/com/structurizr/model/Tags.java diff --git a/structurizr-core/src/com/structurizr/util/ImageUtils.java b/structurizr-core/src/main/java/com/structurizr/util/ImageUtils.java similarity index 100% rename from structurizr-core/src/com/structurizr/util/ImageUtils.java rename to structurizr-core/src/main/java/com/structurizr/util/ImageUtils.java diff --git a/structurizr-core/src/com/structurizr/util/MapUtils.java b/structurizr-core/src/main/java/com/structurizr/util/MapUtils.java similarity index 100% rename from structurizr-core/src/com/structurizr/util/MapUtils.java rename to structurizr-core/src/main/java/com/structurizr/util/MapUtils.java diff --git a/structurizr-core/src/com/structurizr/util/StringUtils.java b/structurizr-core/src/main/java/com/structurizr/util/StringUtils.java similarity index 100% rename from structurizr-core/src/com/structurizr/util/StringUtils.java rename to structurizr-core/src/main/java/com/structurizr/util/StringUtils.java diff --git a/structurizr-core/src/com/structurizr/util/TagUtils.java b/structurizr-core/src/main/java/com/structurizr/util/TagUtils.java similarity index 100% rename from structurizr-core/src/com/structurizr/util/TagUtils.java rename to structurizr-core/src/main/java/com/structurizr/util/TagUtils.java diff --git a/structurizr-core/src/com/structurizr/util/Url.java b/structurizr-core/src/main/java/com/structurizr/util/Url.java similarity index 100% rename from structurizr-core/src/com/structurizr/util/Url.java rename to structurizr-core/src/main/java/com/structurizr/util/Url.java diff --git a/structurizr-core/src/com/structurizr/validation/LandscapeWorkspaceScopeValidator.java b/structurizr-core/src/main/java/com/structurizr/validation/LandscapeWorkspaceScopeValidator.java similarity index 100% rename from structurizr-core/src/com/structurizr/validation/LandscapeWorkspaceScopeValidator.java rename to structurizr-core/src/main/java/com/structurizr/validation/LandscapeWorkspaceScopeValidator.java diff --git a/structurizr-core/src/com/structurizr/validation/SoftwareSystemWorkspaceScopeValidator.java b/structurizr-core/src/main/java/com/structurizr/validation/SoftwareSystemWorkspaceScopeValidator.java similarity index 100% rename from structurizr-core/src/com/structurizr/validation/SoftwareSystemWorkspaceScopeValidator.java rename to structurizr-core/src/main/java/com/structurizr/validation/SoftwareSystemWorkspaceScopeValidator.java diff --git a/structurizr-core/src/com/structurizr/validation/UndefinedWorkspaceScopeValidator.java b/structurizr-core/src/main/java/com/structurizr/validation/UndefinedWorkspaceScopeValidator.java similarity index 100% rename from structurizr-core/src/com/structurizr/validation/UndefinedWorkspaceScopeValidator.java rename to structurizr-core/src/main/java/com/structurizr/validation/UndefinedWorkspaceScopeValidator.java diff --git a/structurizr-core/src/com/structurizr/validation/WorkspaceScopeValidationException.java b/structurizr-core/src/main/java/com/structurizr/validation/WorkspaceScopeValidationException.java similarity index 100% rename from structurizr-core/src/com/structurizr/validation/WorkspaceScopeValidationException.java rename to structurizr-core/src/main/java/com/structurizr/validation/WorkspaceScopeValidationException.java diff --git a/structurizr-core/src/com/structurizr/validation/WorkspaceScopeValidator.java b/structurizr-core/src/main/java/com/structurizr/validation/WorkspaceScopeValidator.java similarity index 100% rename from structurizr-core/src/com/structurizr/validation/WorkspaceScopeValidator.java rename to structurizr-core/src/main/java/com/structurizr/validation/WorkspaceScopeValidator.java diff --git a/structurizr-core/src/com/structurizr/validation/WorkspaceScopeValidatorFactory.java b/structurizr-core/src/main/java/com/structurizr/validation/WorkspaceScopeValidatorFactory.java similarity index 100% rename from structurizr-core/src/com/structurizr/validation/WorkspaceScopeValidatorFactory.java rename to structurizr-core/src/main/java/com/structurizr/validation/WorkspaceScopeValidatorFactory.java diff --git a/structurizr-core/src/com/structurizr/view/AbstractStyle.java b/structurizr-core/src/main/java/com/structurizr/view/AbstractStyle.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/AbstractStyle.java rename to structurizr-core/src/main/java/com/structurizr/view/AbstractStyle.java diff --git a/structurizr-core/src/com/structurizr/view/AnimatedView.java b/structurizr-core/src/main/java/com/structurizr/view/AnimatedView.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/AnimatedView.java rename to structurizr-core/src/main/java/com/structurizr/view/AnimatedView.java diff --git a/structurizr-core/src/com/structurizr/view/Animation.java b/structurizr-core/src/main/java/com/structurizr/view/Animation.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/Animation.java rename to structurizr-core/src/main/java/com/structurizr/view/Animation.java diff --git a/structurizr-core/src/com/structurizr/view/AutomaticLayout.java b/structurizr-core/src/main/java/com/structurizr/view/AutomaticLayout.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/AutomaticLayout.java rename to structurizr-core/src/main/java/com/structurizr/view/AutomaticLayout.java diff --git a/structurizr-core/src/com/structurizr/view/Border.java b/structurizr-core/src/main/java/com/structurizr/view/Border.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/Border.java rename to structurizr-core/src/main/java/com/structurizr/view/Border.java diff --git a/structurizr-core/src/com/structurizr/view/Branding.java b/structurizr-core/src/main/java/com/structurizr/view/Branding.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/Branding.java rename to structurizr-core/src/main/java/com/structurizr/view/Branding.java diff --git a/structurizr-core/src/com/structurizr/view/Color.java b/structurizr-core/src/main/java/com/structurizr/view/Color.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/Color.java rename to structurizr-core/src/main/java/com/structurizr/view/Color.java diff --git a/structurizr-core/src/com/structurizr/view/ComponentView.java b/structurizr-core/src/main/java/com/structurizr/view/ComponentView.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/ComponentView.java rename to structurizr-core/src/main/java/com/structurizr/view/ComponentView.java diff --git a/structurizr-core/src/com/structurizr/view/Configuration.java b/structurizr-core/src/main/java/com/structurizr/view/Configuration.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/Configuration.java rename to structurizr-core/src/main/java/com/structurizr/view/Configuration.java diff --git a/structurizr-core/src/com/structurizr/view/ContainerView.java b/structurizr-core/src/main/java/com/structurizr/view/ContainerView.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/ContainerView.java rename to structurizr-core/src/main/java/com/structurizr/view/ContainerView.java diff --git a/structurizr-core/src/com/structurizr/view/CustomView.java b/structurizr-core/src/main/java/com/structurizr/view/CustomView.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/CustomView.java rename to structurizr-core/src/main/java/com/structurizr/view/CustomView.java diff --git a/structurizr-core/src/com/structurizr/view/DefaultLayoutMergeStrategy.java b/structurizr-core/src/main/java/com/structurizr/view/DefaultLayoutMergeStrategy.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/DefaultLayoutMergeStrategy.java rename to structurizr-core/src/main/java/com/structurizr/view/DefaultLayoutMergeStrategy.java diff --git a/structurizr-core/src/com/structurizr/view/DeploymentView.java b/structurizr-core/src/main/java/com/structurizr/view/DeploymentView.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/DeploymentView.java rename to structurizr-core/src/main/java/com/structurizr/view/DeploymentView.java diff --git a/structurizr-core/src/com/structurizr/view/Dimensions.java b/structurizr-core/src/main/java/com/structurizr/view/Dimensions.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/Dimensions.java rename to structurizr-core/src/main/java/com/structurizr/view/Dimensions.java diff --git a/structurizr-core/src/com/structurizr/view/DynamicView.java b/structurizr-core/src/main/java/com/structurizr/view/DynamicView.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/DynamicView.java rename to structurizr-core/src/main/java/com/structurizr/view/DynamicView.java diff --git a/structurizr-core/src/com/structurizr/view/ElementNotPermittedInViewException.java b/structurizr-core/src/main/java/com/structurizr/view/ElementNotPermittedInViewException.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/ElementNotPermittedInViewException.java rename to structurizr-core/src/main/java/com/structurizr/view/ElementNotPermittedInViewException.java diff --git a/structurizr-core/src/com/structurizr/view/ElementStyle.java b/structurizr-core/src/main/java/com/structurizr/view/ElementStyle.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/ElementStyle.java rename to structurizr-core/src/main/java/com/structurizr/view/ElementStyle.java diff --git a/structurizr-core/src/com/structurizr/view/ElementView.java b/structurizr-core/src/main/java/com/structurizr/view/ElementView.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/ElementView.java rename to structurizr-core/src/main/java/com/structurizr/view/ElementView.java diff --git a/structurizr-core/src/com/structurizr/view/FilterMode.java b/structurizr-core/src/main/java/com/structurizr/view/FilterMode.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/FilterMode.java rename to structurizr-core/src/main/java/com/structurizr/view/FilterMode.java diff --git a/structurizr-core/src/com/structurizr/view/FilteredView.java b/structurizr-core/src/main/java/com/structurizr/view/FilteredView.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/FilteredView.java rename to structurizr-core/src/main/java/com/structurizr/view/FilteredView.java diff --git a/structurizr-core/src/com/structurizr/view/Font.java b/structurizr-core/src/main/java/com/structurizr/view/Font.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/Font.java rename to structurizr-core/src/main/java/com/structurizr/view/Font.java diff --git a/structurizr-core/src/com/structurizr/view/ImageView.java b/structurizr-core/src/main/java/com/structurizr/view/ImageView.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/ImageView.java rename to structurizr-core/src/main/java/com/structurizr/view/ImageView.java diff --git a/structurizr-core/src/com/structurizr/view/LayoutMergeStrategy.java b/structurizr-core/src/main/java/com/structurizr/view/LayoutMergeStrategy.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/LayoutMergeStrategy.java rename to structurizr-core/src/main/java/com/structurizr/view/LayoutMergeStrategy.java diff --git a/structurizr-core/src/com/structurizr/view/LineStyle.java b/structurizr-core/src/main/java/com/structurizr/view/LineStyle.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/LineStyle.java rename to structurizr-core/src/main/java/com/structurizr/view/LineStyle.java diff --git a/structurizr-core/src/com/structurizr/view/MetadataSymbols.java b/structurizr-core/src/main/java/com/structurizr/view/MetadataSymbols.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/MetadataSymbols.java rename to structurizr-core/src/main/java/com/structurizr/view/MetadataSymbols.java diff --git a/structurizr-core/src/com/structurizr/view/ModelView.java b/structurizr-core/src/main/java/com/structurizr/view/ModelView.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/ModelView.java rename to structurizr-core/src/main/java/com/structurizr/view/ModelView.java diff --git a/structurizr-core/src/com/structurizr/view/PaperSize.java b/structurizr-core/src/main/java/com/structurizr/view/PaperSize.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/PaperSize.java rename to structurizr-core/src/main/java/com/structurizr/view/PaperSize.java diff --git a/structurizr-core/src/com/structurizr/view/ParallelSequenceCounter.java b/structurizr-core/src/main/java/com/structurizr/view/ParallelSequenceCounter.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/ParallelSequenceCounter.java rename to structurizr-core/src/main/java/com/structurizr/view/ParallelSequenceCounter.java diff --git a/structurizr-core/src/com/structurizr/view/RelationshipStyle.java b/structurizr-core/src/main/java/com/structurizr/view/RelationshipStyle.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/RelationshipStyle.java rename to structurizr-core/src/main/java/com/structurizr/view/RelationshipStyle.java diff --git a/structurizr-core/src/com/structurizr/view/RelationshipView.java b/structurizr-core/src/main/java/com/structurizr/view/RelationshipView.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/RelationshipView.java rename to structurizr-core/src/main/java/com/structurizr/view/RelationshipView.java diff --git a/structurizr-core/src/com/structurizr/view/Routing.java b/structurizr-core/src/main/java/com/structurizr/view/Routing.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/Routing.java rename to structurizr-core/src/main/java/com/structurizr/view/Routing.java diff --git a/structurizr-core/src/com/structurizr/view/SequenceCounter.java b/structurizr-core/src/main/java/com/structurizr/view/SequenceCounter.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/SequenceCounter.java rename to structurizr-core/src/main/java/com/structurizr/view/SequenceCounter.java diff --git a/structurizr-core/src/com/structurizr/view/SequenceNumber.java b/structurizr-core/src/main/java/com/structurizr/view/SequenceNumber.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/SequenceNumber.java rename to structurizr-core/src/main/java/com/structurizr/view/SequenceNumber.java diff --git a/structurizr-core/src/com/structurizr/view/Shape.java b/structurizr-core/src/main/java/com/structurizr/view/Shape.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/Shape.java rename to structurizr-core/src/main/java/com/structurizr/view/Shape.java diff --git a/structurizr-core/src/com/structurizr/view/StaticView.java b/structurizr-core/src/main/java/com/structurizr/view/StaticView.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/StaticView.java rename to structurizr-core/src/main/java/com/structurizr/view/StaticView.java diff --git a/structurizr-core/src/com/structurizr/view/Styles.java b/structurizr-core/src/main/java/com/structurizr/view/Styles.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/Styles.java rename to structurizr-core/src/main/java/com/structurizr/view/Styles.java diff --git a/structurizr-core/src/com/structurizr/view/SystemContextView.java b/structurizr-core/src/main/java/com/structurizr/view/SystemContextView.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/SystemContextView.java rename to structurizr-core/src/main/java/com/structurizr/view/SystemContextView.java diff --git a/structurizr-core/src/com/structurizr/view/SystemLandscapeView.java b/structurizr-core/src/main/java/com/structurizr/view/SystemLandscapeView.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/SystemLandscapeView.java rename to structurizr-core/src/main/java/com/structurizr/view/SystemLandscapeView.java diff --git a/structurizr-core/src/com/structurizr/view/Terminology.java b/structurizr-core/src/main/java/com/structurizr/view/Terminology.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/Terminology.java rename to structurizr-core/src/main/java/com/structurizr/view/Terminology.java diff --git a/structurizr-core/src/com/structurizr/view/Theme.java b/structurizr-core/src/main/java/com/structurizr/view/Theme.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/Theme.java rename to structurizr-core/src/main/java/com/structurizr/view/Theme.java diff --git a/structurizr-core/src/com/structurizr/view/Vertex.java b/structurizr-core/src/main/java/com/structurizr/view/Vertex.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/Vertex.java rename to structurizr-core/src/main/java/com/structurizr/view/Vertex.java diff --git a/structurizr-core/src/com/structurizr/view/View.java b/structurizr-core/src/main/java/com/structurizr/view/View.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/View.java rename to structurizr-core/src/main/java/com/structurizr/view/View.java diff --git a/structurizr-core/src/com/structurizr/view/ViewSet.java b/structurizr-core/src/main/java/com/structurizr/view/ViewSet.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/ViewSet.java rename to structurizr-core/src/main/java/com/structurizr/view/ViewSet.java diff --git a/structurizr-core/src/com/structurizr/view/ViewSortOrder.java b/structurizr-core/src/main/java/com/structurizr/view/ViewSortOrder.java similarity index 100% rename from structurizr-core/src/com/structurizr/view/ViewSortOrder.java rename to structurizr-core/src/main/java/com/structurizr/view/ViewSortOrder.java diff --git a/structurizr-core/test/unit/com/structurizr/AbstractWorkspaceTestBase.java b/structurizr-core/src/test/java/com/structurizr/AbstractWorkspaceTestBase.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/AbstractWorkspaceTestBase.java rename to structurizr-core/src/test/java/com/structurizr/AbstractWorkspaceTestBase.java diff --git a/structurizr-core/test/unit/com/structurizr/WorkspaceTests.java b/structurizr-core/src/test/java/com/structurizr/WorkspaceTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/WorkspaceTests.java rename to structurizr-core/src/test/java/com/structurizr/WorkspaceTests.java diff --git a/structurizr-core/test/unit/com/structurizr/configuration/UserTests.java b/structurizr-core/src/test/java/com/structurizr/configuration/UserTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/configuration/UserTests.java rename to structurizr-core/src/test/java/com/structurizr/configuration/UserTests.java diff --git a/structurizr-core/test/unit/com/structurizr/configuration/WorkspaceConfigurationTests.java b/structurizr-core/src/test/java/com/structurizr/configuration/WorkspaceConfigurationTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/configuration/WorkspaceConfigurationTests.java rename to structurizr-core/src/test/java/com/structurizr/configuration/WorkspaceConfigurationTests.java diff --git a/structurizr-core/test/unit/com/structurizr/documentation/DecisionTests.java b/structurizr-core/src/test/java/com/structurizr/documentation/DecisionTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/documentation/DecisionTests.java rename to structurizr-core/src/test/java/com/structurizr/documentation/DecisionTests.java diff --git a/structurizr-core/test/unit/com/structurizr/documentation/DocumentationTests.java b/structurizr-core/src/test/java/com/structurizr/documentation/DocumentationTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/documentation/DocumentationTests.java rename to structurizr-core/src/test/java/com/structurizr/documentation/DocumentationTests.java diff --git a/structurizr-core/test/unit/com/structurizr/documentation/SectionTests.java b/structurizr-core/src/test/java/com/structurizr/documentation/SectionTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/documentation/SectionTests.java rename to structurizr-core/src/test/java/com/structurizr/documentation/SectionTests.java diff --git a/structurizr-core/test/unit/com/structurizr/model/ComponentTests.java b/structurizr-core/src/test/java/com/structurizr/model/ComponentTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/model/ComponentTests.java rename to structurizr-core/src/test/java/com/structurizr/model/ComponentTests.java diff --git a/structurizr-core/test/unit/com/structurizr/model/ContainerInstanceTests.java b/structurizr-core/src/test/java/com/structurizr/model/ContainerInstanceTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/model/ContainerInstanceTests.java rename to structurizr-core/src/test/java/com/structurizr/model/ContainerInstanceTests.java diff --git a/structurizr-core/test/unit/com/structurizr/model/ContainerTests.java b/structurizr-core/src/test/java/com/structurizr/model/ContainerTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/model/ContainerTests.java rename to structurizr-core/src/test/java/com/structurizr/model/ContainerTests.java diff --git a/structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategyTests.java b/structurizr-core/src/test/java/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategyTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategyTests.java rename to structurizr-core/src/test/java/com/structurizr/model/CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategyTests.java diff --git a/structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategyTests.java b/structurizr-core/src/test/java/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategyTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategyTests.java rename to structurizr-core/src/test/java/com/structurizr/model/CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategyTests.java diff --git a/structurizr-core/test/unit/com/structurizr/model/CustomElementTests.java b/structurizr-core/src/test/java/com/structurizr/model/CustomElementTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/model/CustomElementTests.java rename to structurizr-core/src/test/java/com/structurizr/model/CustomElementTests.java diff --git a/structurizr-core/test/unit/com/structurizr/model/DefaultImpliedRelationshipsStrategyTests.java b/structurizr-core/src/test/java/com/structurizr/model/DefaultImpliedRelationshipsStrategyTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/model/DefaultImpliedRelationshipsStrategyTests.java rename to structurizr-core/src/test/java/com/structurizr/model/DefaultImpliedRelationshipsStrategyTests.java diff --git a/structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java b/structurizr-core/src/test/java/com/structurizr/model/DeploymentNodeTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/model/DeploymentNodeTests.java rename to structurizr-core/src/test/java/com/structurizr/model/DeploymentNodeTests.java diff --git a/structurizr-core/test/unit/com/structurizr/model/ElementTests.java b/structurizr-core/src/test/java/com/structurizr/model/ElementTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/model/ElementTests.java rename to structurizr-core/src/test/java/com/structurizr/model/ElementTests.java diff --git a/structurizr-core/test/unit/com/structurizr/model/GroupableElementTests.java b/structurizr-core/src/test/java/com/structurizr/model/GroupableElementTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/model/GroupableElementTests.java rename to structurizr-core/src/test/java/com/structurizr/model/GroupableElementTests.java diff --git a/structurizr-core/test/unit/com/structurizr/model/HttpHealthCheckTests.java b/structurizr-core/src/test/java/com/structurizr/model/HttpHealthCheckTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/model/HttpHealthCheckTests.java rename to structurizr-core/src/test/java/com/structurizr/model/HttpHealthCheckTests.java diff --git a/structurizr-core/test/unit/com/structurizr/model/InfrastructureNodeTests.java b/structurizr-core/src/test/java/com/structurizr/model/InfrastructureNodeTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/model/InfrastructureNodeTests.java rename to structurizr-core/src/test/java/com/structurizr/model/InfrastructureNodeTests.java diff --git a/structurizr-core/test/unit/com/structurizr/model/ModelItemTests.java b/structurizr-core/src/test/java/com/structurizr/model/ModelItemTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/model/ModelItemTests.java rename to structurizr-core/src/test/java/com/structurizr/model/ModelItemTests.java diff --git a/structurizr-core/test/unit/com/structurizr/model/ModelTests.java b/structurizr-core/src/test/java/com/structurizr/model/ModelTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/model/ModelTests.java rename to structurizr-core/src/test/java/com/structurizr/model/ModelTests.java diff --git a/structurizr-core/test/unit/com/structurizr/model/PersonTests.java b/structurizr-core/src/test/java/com/structurizr/model/PersonTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/model/PersonTests.java rename to structurizr-core/src/test/java/com/structurizr/model/PersonTests.java diff --git a/structurizr-core/test/unit/com/structurizr/model/RelationshipTests.java b/structurizr-core/src/test/java/com/structurizr/model/RelationshipTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/model/RelationshipTests.java rename to structurizr-core/src/test/java/com/structurizr/model/RelationshipTests.java diff --git a/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemInstanceTests.java b/structurizr-core/src/test/java/com/structurizr/model/SoftwareSystemInstanceTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/model/SoftwareSystemInstanceTests.java rename to structurizr-core/src/test/java/com/structurizr/model/SoftwareSystemInstanceTests.java diff --git a/structurizr-core/test/unit/com/structurizr/model/SoftwareSystemTests.java b/structurizr-core/src/test/java/com/structurizr/model/SoftwareSystemTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/model/SoftwareSystemTests.java rename to structurizr-core/src/test/java/com/structurizr/model/SoftwareSystemTests.java diff --git a/structurizr-core/test/unit/com/structurizr/util/ImageUtilsTests.java b/structurizr-core/src/test/java/com/structurizr/util/ImageUtilsTests.java similarity index 96% rename from structurizr-core/test/unit/com/structurizr/util/ImageUtilsTests.java rename to structurizr-core/src/test/java/com/structurizr/util/ImageUtilsTests.java index 81892cd78..03f9c23dd 100644 --- a/structurizr-core/test/unit/com/structurizr/util/ImageUtilsTests.java +++ b/structurizr-core/src/test/java/com/structurizr/util/ImageUtilsTests.java @@ -52,7 +52,7 @@ void getContentType_ThrowsAnException_WhenAFileIsSpecifiedButItDoesNotExist() th @Test void getContentType_ReturnsTheContentType_WhenAFileIsSpecified() throws Exception { - String contentType = ImageUtils.getContentType(new File("../structurizr-core/test/unit/com/structurizr/util/structurizr-logo.png")); + String contentType = ImageUtils.getContentType(new File("./src/test/resources/structurizr-logo.png")); assertEquals("image/png", contentType); } @@ -134,8 +134,8 @@ void getImageAsBase64_ThrowsAnException_WhenAFileIsSpecifiedButItDoesNotExist() @Test void getImageAsBase64_ReturnsTheImageAsABase64EncodedString_WhenAFileIsSpecified() throws Exception { - String imageAsBase64 = ImageUtils.getImageAsBase64(new File("../structurizr-core/test/unit/com/structurizr/util/structurizr-logo.png")); - assertTrue(imageAsBase64.startsWith("iVBORw0KGgoAAAANSUhEUgAAAMQAAADECAYAAADApo5rAAA")); // the actual base64 encoded string varies between Java 8 and 9 + String imageAsBase64 = ImageUtils.getImageAsBase64(new File("./src/test/resources/structurizr-logo.png")); + assertTrue(imageAsBase64.startsWith("iVBORw0KGgoAAAANSUhEUgAAAMQAAADECAYAAADApo5rAAA")); } @Test @@ -182,7 +182,7 @@ void getImageAsDataUri_ThrowsAnException_WhenAFileIsSpecifiedButItDoesNotExist() @Test void getImageAsDataUri_ReturnsTheImageAsADataUri_WhenAFileIsSpecified() throws Exception { - String imageAsDataUri = ImageUtils.getImageAsDataUri(new File("../structurizr-core/test/unit/com/structurizr/util/structurizr-logo.png")); + String imageAsDataUri = ImageUtils.getImageAsDataUri(new File("./src/test/resources/structurizr-logo.png")); System.out.println(imageAsDataUri); assertTrue(imageAsDataUri.startsWith("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMQAAADECAYAAADA")); // the actual base64 encoded string varies between Java 8 and 9 } diff --git a/structurizr-core/test/unit/com/structurizr/util/StringUtilsTests.java b/structurizr-core/src/test/java/com/structurizr/util/StringUtilsTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/util/StringUtilsTests.java rename to structurizr-core/src/test/java/com/structurizr/util/StringUtilsTests.java diff --git a/structurizr-core/test/unit/com/structurizr/util/UrlTests.java b/structurizr-core/src/test/java/com/structurizr/util/UrlTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/util/UrlTests.java rename to structurizr-core/src/test/java/com/structurizr/util/UrlTests.java diff --git a/structurizr-core/test/unit/com/structurizr/validation/LandscapeWorkspaceScopeValidatorTests.java b/structurizr-core/src/test/java/com/structurizr/validation/LandscapeWorkspaceScopeValidatorTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/validation/LandscapeWorkspaceScopeValidatorTests.java rename to structurizr-core/src/test/java/com/structurizr/validation/LandscapeWorkspaceScopeValidatorTests.java diff --git a/structurizr-core/test/unit/com/structurizr/validation/SoftwareSystemWorkspaceScopeValidatorTests.java b/structurizr-core/src/test/java/com/structurizr/validation/SoftwareSystemWorkspaceScopeValidatorTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/validation/SoftwareSystemWorkspaceScopeValidatorTests.java rename to structurizr-core/src/test/java/com/structurizr/validation/SoftwareSystemWorkspaceScopeValidatorTests.java diff --git a/structurizr-core/test/unit/com/structurizr/validation/WorkspaceScopeValidatorFactoryTests.java b/structurizr-core/src/test/java/com/structurizr/validation/WorkspaceScopeValidatorFactoryTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/validation/WorkspaceScopeValidatorFactoryTests.java rename to structurizr-core/src/test/java/com/structurizr/validation/WorkspaceScopeValidatorFactoryTests.java diff --git a/structurizr-core/test/unit/com/structurizr/view/AutomaticLayoutTests.java b/structurizr-core/src/test/java/com/structurizr/view/AutomaticLayoutTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/view/AutomaticLayoutTests.java rename to structurizr-core/src/test/java/com/structurizr/view/AutomaticLayoutTests.java diff --git a/structurizr-core/test/unit/com/structurizr/view/BrandingTests.java b/structurizr-core/src/test/java/com/structurizr/view/BrandingTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/view/BrandingTests.java rename to structurizr-core/src/test/java/com/structurizr/view/BrandingTests.java diff --git a/structurizr-core/test/unit/com/structurizr/view/ColorTests.java b/structurizr-core/src/test/java/com/structurizr/view/ColorTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/view/ColorTests.java rename to structurizr-core/src/test/java/com/structurizr/view/ColorTests.java diff --git a/structurizr-core/test/unit/com/structurizr/view/ComponentViewTests.java b/structurizr-core/src/test/java/com/structurizr/view/ComponentViewTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/view/ComponentViewTests.java rename to structurizr-core/src/test/java/com/structurizr/view/ComponentViewTests.java diff --git a/structurizr-core/test/unit/com/structurizr/view/ConfigurationTests.java b/structurizr-core/src/test/java/com/structurizr/view/ConfigurationTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/view/ConfigurationTests.java rename to structurizr-core/src/test/java/com/structurizr/view/ConfigurationTests.java diff --git a/structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java b/structurizr-core/src/test/java/com/structurizr/view/ContainerViewTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/view/ContainerViewTests.java rename to structurizr-core/src/test/java/com/structurizr/view/ContainerViewTests.java diff --git a/structurizr-core/test/unit/com/structurizr/view/DefaultLayoutMergeStrategyTests.java b/structurizr-core/src/test/java/com/structurizr/view/DefaultLayoutMergeStrategyTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/view/DefaultLayoutMergeStrategyTests.java rename to structurizr-core/src/test/java/com/structurizr/view/DefaultLayoutMergeStrategyTests.java diff --git a/structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java b/structurizr-core/src/test/java/com/structurizr/view/DeploymentViewTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/view/DeploymentViewTests.java rename to structurizr-core/src/test/java/com/structurizr/view/DeploymentViewTests.java diff --git a/structurizr-core/test/unit/com/structurizr/view/DimensionsTests.java b/structurizr-core/src/test/java/com/structurizr/view/DimensionsTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/view/DimensionsTests.java rename to structurizr-core/src/test/java/com/structurizr/view/DimensionsTests.java diff --git a/structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java b/structurizr-core/src/test/java/com/structurizr/view/DynamicViewTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/view/DynamicViewTests.java rename to structurizr-core/src/test/java/com/structurizr/view/DynamicViewTests.java diff --git a/structurizr-core/test/unit/com/structurizr/view/ElementStyleTests.java b/structurizr-core/src/test/java/com/structurizr/view/ElementStyleTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/view/ElementStyleTests.java rename to structurizr-core/src/test/java/com/structurizr/view/ElementStyleTests.java diff --git a/structurizr-core/test/unit/com/structurizr/view/ElementViewTests.java b/structurizr-core/src/test/java/com/structurizr/view/ElementViewTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/view/ElementViewTests.java rename to structurizr-core/src/test/java/com/structurizr/view/ElementViewTests.java diff --git a/structurizr-core/test/unit/com/structurizr/view/FilteredViewTests.java b/structurizr-core/src/test/java/com/structurizr/view/FilteredViewTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/view/FilteredViewTests.java rename to structurizr-core/src/test/java/com/structurizr/view/FilteredViewTests.java diff --git a/structurizr-core/test/unit/com/structurizr/view/FontTests.java b/structurizr-core/src/test/java/com/structurizr/view/FontTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/view/FontTests.java rename to structurizr-core/src/test/java/com/structurizr/view/FontTests.java diff --git a/structurizr-core/test/unit/com/structurizr/view/ImageViewTests.java b/structurizr-core/src/test/java/com/structurizr/view/ImageViewTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/view/ImageViewTests.java rename to structurizr-core/src/test/java/com/structurizr/view/ImageViewTests.java diff --git a/structurizr-core/test/unit/com/structurizr/view/PaperSizeTests.java b/structurizr-core/src/test/java/com/structurizr/view/PaperSizeTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/view/PaperSizeTests.java rename to structurizr-core/src/test/java/com/structurizr/view/PaperSizeTests.java diff --git a/structurizr-core/test/unit/com/structurizr/view/RelationshipStyleTests.java b/structurizr-core/src/test/java/com/structurizr/view/RelationshipStyleTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/view/RelationshipStyleTests.java rename to structurizr-core/src/test/java/com/structurizr/view/RelationshipStyleTests.java diff --git a/structurizr-core/test/unit/com/structurizr/view/SequenceCounterTests.java b/structurizr-core/src/test/java/com/structurizr/view/SequenceCounterTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/view/SequenceCounterTests.java rename to structurizr-core/src/test/java/com/structurizr/view/SequenceCounterTests.java diff --git a/structurizr-core/test/unit/com/structurizr/view/SequenceNumberTests.java b/structurizr-core/src/test/java/com/structurizr/view/SequenceNumberTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/view/SequenceNumberTests.java rename to structurizr-core/src/test/java/com/structurizr/view/SequenceNumberTests.java diff --git a/structurizr-core/test/unit/com/structurizr/view/StaticViewTests.java b/structurizr-core/src/test/java/com/structurizr/view/StaticViewTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/view/StaticViewTests.java rename to structurizr-core/src/test/java/com/structurizr/view/StaticViewTests.java diff --git a/structurizr-core/test/unit/com/structurizr/view/StylesTests.java b/structurizr-core/src/test/java/com/structurizr/view/StylesTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/view/StylesTests.java rename to structurizr-core/src/test/java/com/structurizr/view/StylesTests.java diff --git a/structurizr-core/test/unit/com/structurizr/view/SystemContextViewTests.java b/structurizr-core/src/test/java/com/structurizr/view/SystemContextViewTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/view/SystemContextViewTests.java rename to structurizr-core/src/test/java/com/structurizr/view/SystemContextViewTests.java diff --git a/structurizr-core/test/unit/com/structurizr/view/SystemLandscapeViewTests.java b/structurizr-core/src/test/java/com/structurizr/view/SystemLandscapeViewTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/view/SystemLandscapeViewTests.java rename to structurizr-core/src/test/java/com/structurizr/view/SystemLandscapeViewTests.java diff --git a/structurizr-core/test/unit/com/structurizr/view/TerminologyTests.java b/structurizr-core/src/test/java/com/structurizr/view/TerminologyTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/view/TerminologyTests.java rename to structurizr-core/src/test/java/com/structurizr/view/TerminologyTests.java diff --git a/structurizr-core/test/unit/com/structurizr/view/VertexTests.java b/structurizr-core/src/test/java/com/structurizr/view/VertexTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/view/VertexTests.java rename to structurizr-core/src/test/java/com/structurizr/view/VertexTests.java diff --git a/structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java b/structurizr-core/src/test/java/com/structurizr/view/ViewSetTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/view/ViewSetTests.java rename to structurizr-core/src/test/java/com/structurizr/view/ViewSetTests.java diff --git a/structurizr-core/test/unit/com/structurizr/view/ViewTests.java b/structurizr-core/src/test/java/com/structurizr/view/ViewTests.java similarity index 100% rename from structurizr-core/test/unit/com/structurizr/view/ViewTests.java rename to structurizr-core/src/test/java/com/structurizr/view/ViewTests.java diff --git a/structurizr-core/test/unit/com/structurizr/util/structurizr-logo.png b/structurizr-core/src/test/resources/structurizr-logo.png similarity index 100% rename from structurizr-core/test/unit/com/structurizr/util/structurizr-logo.png rename to structurizr-core/src/test/resources/structurizr-logo.png diff --git a/structurizr-dsl/README.md b/structurizr-dsl/README.md new file mode 100644 index 000000000..ae9e61d84 --- /dev/null +++ b/structurizr-dsl/README.md @@ -0,0 +1,11 @@ +# Structurizr DSL + +[![Maven Central](https://img.shields.io/maven-central/v/com.structurizr/structurizr-dsl.svg?label=Maven%20Central)](https://search.maven.org/artifact/com.structurizr/structurizr-dsl) + +This GitHub repository contains an implementation of the Structurizr DSL - a way to create Structurizr software +architecture models based upon the [C4 model](https://c4model.com) using a textual domain specific language (DSL). +The Structurizr DSL has appeared on the +[ThoughtWorks Tech Radar - Techniques - Diagrams as code](https://www.thoughtworks.com/radar/techniques/diagrams-as-code) +and is text-based wrapper around the [Structurizr for Java library](https://github.com/structurizr/java). + +- [Documentation](https://docs.structurizr.com/dsl) diff --git a/structurizr-dsl/build.gradle b/structurizr-dsl/build.gradle new file mode 100644 index 000000000..3416670df --- /dev/null +++ b/structurizr-dsl/build.gradle @@ -0,0 +1,14 @@ +dependencies { + + api project(':structurizr-client') + api project(':structurizr-import') + + testImplementation 'org.codehaus.groovy:groovy-jsr223:3.0.19' + testImplementation 'org.jetbrains.kotlin:kotlin-scripting-jsr223:1.8.10' + testImplementation 'org.jruby:jruby-core:9.4.4.0' + + testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.9.2' + testImplementation 'org.junit.jupiter:junit-jupiter-params:5.9.2' +} + +description = 'Structurizr DSL' \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractExpressionParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractExpressionParser.java new file mode 100644 index 000000000..07f7a36c1 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractExpressionParser.java @@ -0,0 +1,344 @@ +package com.structurizr.dsl; + +import com.structurizr.model.Element; +import com.structurizr.model.ModelItem; +import com.structurizr.model.Relationship; +import com.structurizr.model.StaticStructureElementInstance; +import com.structurizr.util.StringUtils; + +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.stream.Collectors; + +import static com.structurizr.dsl.StructurizrDslExpressions.*; + +abstract class AbstractExpressionParser { + + private static final String WILDCARD = "*"; + + static boolean isExpression(String token) { + token = token.toLowerCase(); + + return + token.startsWith(ELEMENT_TYPE_EQUALS_EXPRESSION.toLowerCase()) || + token.startsWith(ELEMENT_TAG_EQUALS_EXPRESSION.toLowerCase()) || + token.startsWith(ELEMENT_TAG_NOT_EQUALS_EXPRESSION.toLowerCase()) || + token.startsWith(ELEMENT_PARENT_EQUALS_EXPRESSION.toLowerCase()) || + token.startsWith(RELATIONSHIP) || token.endsWith(RELATIONSHIP) || token.contains(RELATIONSHIP) || + token.startsWith(ELEMENT_EQUALS_EXPRESSION) || + token.startsWith(RELATIONSHIP_TAG_EQUALS_EXPRESSION.toLowerCase()) || + token.startsWith(RELATIONSHIP_TAG_NOT_EQUALS_EXPRESSION.toLowerCase()) || + token.startsWith(RELATIONSHIP_SOURCE_EQUALS_EXPRESSION.toLowerCase()) || + token.startsWith(RELATIONSHIP_DESTINATION_EQUALS_EXPRESSION.toLowerCase()) || + token.startsWith(RELATIONSHIP_EQUALS_EXPRESSION); + } + + + final Set parseExpression(String expr, DslContext context) { + if (expr.contains(" && ")) { + String[] expressions = expr.split(" && "); + Set modelItems1 = evaluateExpression(expressions[0], context); + Set modelItems2 = evaluateExpression(expressions[1], context); + + Set modelItems = new HashSet<>(modelItems1); + modelItems.retainAll(modelItems2); + + return modelItems; + } else if (expr.contains(" || ")) { + String[] expressions = expr.split(" \\|\\| "); + Set modelItems1 = evaluateExpression(expressions[0], context); + Set modelItems2 = evaluateExpression(expressions[1], context); + + Set elements = new HashSet<>(modelItems1); + elements.addAll(modelItems2); + + return elements; + } else { + return evaluateExpression(expr, context); + } + } + + private Set evaluateExpression(String expr, DslContext context) { + Set modelItems = new LinkedHashSet<>(); + + if (expr.startsWith(ELEMENT_EQUALS_EXPRESSION)) { + expr = expr.substring(ELEMENT_EQUALS_EXPRESSION.length()); + + if (isExpression(expr)) { + modelItems.addAll(evaluateExpression(expr, context)); + } else { + modelItems.addAll(parseIdentifier(expr, context)); + } + } else if (expr.startsWith(RELATIONSHIP_EQUALS_EXPRESSION)) { + expr = expr.substring(RELATIONSHIP_EQUALS_EXPRESSION.length()); + + if (WILDCARD.equals(expr)) { + expr = WILDCARD + RELATIONSHIP + WILDCARD; + } + + if (isExpression(expr)) { + modelItems.addAll(evaluateExpression(expr, context)); + } else { + modelItems.addAll(parseIdentifier(expr, context)); + } + } else if (RELATIONSHIP.equals(expr)) { + throw new RuntimeException("Unexpected identifier \"->\""); + } else if (expr.startsWith(RELATIONSHIP) || expr.endsWith(RELATIONSHIP)) { + // this is an element expression: ->identifier identifier-> ->identifier-> + boolean includeAfferentCouplings = false; + boolean includeEfferentCouplings = false; + + String identifier = expr; + + if (identifier.startsWith(RELATIONSHIP)) { + includeAfferentCouplings = true; + identifier = identifier.substring(RELATIONSHIP.length()); + } + if (identifier.endsWith(RELATIONSHIP)) { + includeEfferentCouplings = true; + identifier = identifier.substring(0, identifier.length() - RELATIONSHIP.length()); + } + + identifier = identifier.trim(); + Set elements; + + if (isExpression(identifier)) { + elements = parseExpression(identifier, context).stream().filter(mi -> mi instanceof Element).map(mi -> (Element)mi).collect(Collectors.toSet()); + } else { + elements = getElements(identifier, context); + } + + if (elements.isEmpty()) { + throw new RuntimeException("The element \"" + identifier + "\" does not exist"); + } + + for (Element element : elements) { + modelItems.add(element); + + if (includeAfferentCouplings) { + modelItems.addAll(findAfferentCouplings(element)); + } + + if (includeEfferentCouplings) { + modelItems.addAll(findEfferentCouplings(element)); + } + } + } else if (expr.contains(RELATIONSHIP)) { + String[] identifiers = expr.split(RELATIONSHIP); + String sourceIdentifier = identifiers[0].trim(); + String destinationIdentifier = identifiers[1].trim(); + + String sourceExpression = RELATIONSHIP_SOURCE_EQUALS_EXPRESSION + sourceIdentifier; + String destinationExpression = RELATIONSHIP_DESTINATION_EQUALS_EXPRESSION + destinationIdentifier; + + if (WILDCARD.equals(sourceIdentifier) && WILDCARD.equals(destinationIdentifier)) { + modelItems.addAll(context.getWorkspace().getModel().getRelationships()); + } else if (WILDCARD.equals(destinationIdentifier)) { + modelItems.addAll(parseExpression(sourceExpression, context)); + } else if (WILDCARD.equals(sourceIdentifier)) { + modelItems.addAll(parseExpression(destinationExpression, context)); + } else { + modelItems.addAll(parseExpression(sourceExpression + " && " + destinationExpression, context)); + } + } else if (expr.toLowerCase().startsWith(ELEMENT_PARENT_EQUALS_EXPRESSION)) { + String parentIdentifier = expr.substring(ELEMENT_PARENT_EQUALS_EXPRESSION.length()); + Element parentElement = context.getElement(parentIdentifier); + if (parentElement == null) { + throw new RuntimeException("The parent element \"" + parentIdentifier + "\" does not exist"); + } else { + context.getWorkspace().getModel().getElements().forEach(element -> { + if (element.getParent() == parentElement) { + modelItems.add(element); + } + }); + } + } else if (expr.toLowerCase().startsWith(ELEMENT_TYPE_EQUALS_EXPRESSION)) { + modelItems.addAll(evaluateElementTypeExpression(expr, context)); + } else if (expr.toLowerCase().startsWith(ELEMENT_TAG_EQUALS_EXPRESSION.toLowerCase())) { + String[] tags = expr.substring(ELEMENT_TAG_EQUALS_EXPRESSION.length()).split(","); + context.getWorkspace().getModel().getElements().forEach(element -> { + if (hasAllTags(element, tags)) { + modelItems.add(element); + } + }); + } else if (expr.toLowerCase().startsWith(ELEMENT_TAG_NOT_EQUALS_EXPRESSION)) { + String[] tags = expr.substring(ELEMENT_TAG_NOT_EQUALS_EXPRESSION.length()).split(","); + context.getWorkspace().getModel().getElements().forEach(element -> { + if (!hasAllTags(element, tags)) { + modelItems.add(element); + } + }); + } else if (expr.startsWith(RELATIONSHIP_TAG_EQUALS_EXPRESSION)) { + String[] tags = expr.substring(RELATIONSHIP_TAG_EQUALS_EXPRESSION.length()).split(","); + context.getWorkspace().getModel().getRelationships().forEach(relationship -> { + if (hasAllTags(relationship, tags)) { + modelItems.add(relationship); + } + }); + } else if (expr.startsWith(RELATIONSHIP_TAG_NOT_EQUALS_EXPRESSION)) { + String[] tags = expr.substring(RELATIONSHIP_TAG_NOT_EQUALS_EXPRESSION.length()).split(","); + context.getWorkspace().getModel().getRelationships().forEach(relationship -> { + if (!hasAllTags(relationship, tags)) { + modelItems.add(relationship); + } + }); + } else if (expr.startsWith(RELATIONSHIP_SOURCE_EQUALS_EXPRESSION)) { + String identifier = expr.substring(RELATIONSHIP_SOURCE_EQUALS_EXPRESSION.length()); + Set sourceElements = new HashSet<>(); + + if (isExpression(identifier)) { + Set set = parseExpression(identifier, context); + for (ModelItem modelItem : set) { + if (modelItem instanceof Element) { + sourceElements.add((Element)modelItem); + } + } + } else { + Element source = context.getElement(identifier); + if (source == null) { + throw new RuntimeException("The element \"" + identifier + "\" does not exist"); + } + + if (source instanceof ElementGroup) { + sourceElements.addAll(((ElementGroup) source).getElements()); + } else { + sourceElements.add(source); + } + } + + context.getWorkspace().getModel().getRelationships().forEach(relationship -> { + if (sourceElements.contains(relationship.getSource())) { + modelItems.add(relationship); + } + }); + } else if (expr.startsWith(RELATIONSHIP_DESTINATION_EQUALS_EXPRESSION)) { + String identifier = expr.substring(RELATIONSHIP_DESTINATION_EQUALS_EXPRESSION.length()); + Set destinationElements = new HashSet<>(); + + if (isExpression(identifier)) { + Set set = parseExpression(identifier, context); + for (ModelItem modelItem : set) { + if (modelItem instanceof Element) { + destinationElements.add((Element)modelItem); + } + } + } else { + Element destination = context.getElement(identifier); + if (destination == null) { + throw new RuntimeException("The element \"" + identifier + "\" does not exist"); + } + + if (destination instanceof ElementGroup) { + destinationElements.addAll(((ElementGroup) destination).getElements()); + } else { + destinationElements.add(destination); + } + } + + context.getWorkspace().getModel().getRelationships().forEach(relationship -> { + if (destinationElements.contains(relationship.getDestination())) { + modelItems.add(relationship); + } + }); + } + + return modelItems; + } + + protected abstract Set evaluateElementTypeExpression(String expr, DslContext context); + + private boolean hasAllTags(ModelItem modelItem, String[] tags) { + boolean result = true; + + for (String tag : tags) { + boolean hasTag = modelItem.hasTag(tag.trim()); + + if (!hasTag) { + // perhaps the tag is instead on a related model item? + if (modelItem instanceof StaticStructureElementInstance) { + StaticStructureElementInstance elementInstance = (StaticStructureElementInstance)modelItem; + hasTag = elementInstance.getElement().hasTag(tag.trim()); + } else if (modelItem instanceof Relationship) { + Relationship relationship = (Relationship)modelItem; + if (!StringUtils.isNullOrEmpty(relationship.getLinkedRelationshipId())) { + Relationship linkedRelationship = relationship.getModel().getRelationship(relationship.getLinkedRelationshipId()); + if (linkedRelationship != null) { + hasTag = linkedRelationship.hasTag(tag.trim()); + } + } + } + } + + result = result && hasTag; + } + + return result; + } + + protected abstract Set findAfferentCouplings(Element element); + + protected Set findAfferentCouplings(Element element, Class typeOfElement) { + Set elements = new LinkedHashSet<>(); + + Set relationships = element.getModel().getRelationships(); + relationships.stream().filter(r -> r.getDestination().equals(element) && typeOfElement.isInstance(r.getSource())) + .map(Relationship::getSource) + .forEach(elements::add); + + return elements; + } + + protected abstract Set findEfferentCouplings(Element element); + + protected Set findEfferentCouplings(Element element, Class typeOfElement) { + Set elements = new LinkedHashSet<>(); + + Set relationships = element.getModel().getRelationships(); + relationships.stream().filter(r -> r.getSource().equals(element) && typeOfElement.isInstance(r.getDestination())) + .map(Relationship::getDestination) + .forEach(elements::add); + + return elements; + } + + protected Set parseIdentifier(String identifier, DslContext context) { + Set modelItems = new LinkedHashSet<>(); + + Element element = context.getElement(identifier); + if (element != null) { + modelItems.addAll(getElements(identifier, context)); + } + + Relationship relationship = context.getRelationship(identifier); + if (relationship != null) { + modelItems.add(relationship); + + // and also find all relationships linked to it (i.e. implied and replicated relationships) + relationship.getModel().getRelationships().stream().filter(r -> relationship.getId().equals(r.getLinkedRelationshipId())).forEach(modelItems::add); + } + + if (modelItems.isEmpty()) { + throw new RuntimeException("The element/relationship \"" + identifier + "\" does not exist"); + } else { + return modelItems; + } + } + + private Set getElements(String identifier, DslContext context) { + Set elements = new HashSet<>(); + + Element element = context.getElement(identifier); + if (element != null) { + if (element instanceof ElementGroup) { + ElementGroup group = (ElementGroup) element; + elements.addAll(group.getElements()); + } else { + elements.add(element); + } + } + + return elements; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractParser.java new file mode 100644 index 000000000..01bfcf4c0 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractParser.java @@ -0,0 +1,42 @@ +package com.structurizr.dsl; + +import org.apache.hc.client5.http.classic.methods.HttpGet; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.core5.http.io.entity.EntityUtils; + +import java.util.regex.Pattern; + +abstract class AbstractParser { + + private static final int HTTP_OK_STATUS = 200; + + private static final Pattern VIEW_KEY_PATTERN = Pattern.compile("[\\w-]+"); + + void validateViewKey(String key) { + if (!VIEW_KEY_PATTERN.matcher(key).matches()) { + throw new RuntimeException("View keys can only contain the following characters: a-zA-0-9_-"); + } + } + + String removeNonWordCharacters(String name) { + return name.replaceAll("\\W", ""); + } + + protected String readFromUrl(String url) { + try (CloseableHttpClient httpClient = HttpClients.createSystem()) { + HttpGet httpGet = new HttpGet(url); + CloseableHttpResponse response = httpClient.execute(httpGet); + + if (response.getCode() == HTTP_OK_STATUS) { + return EntityUtils.toString(response.getEntity()); + } + } catch (Exception ioe) { + ioe.printStackTrace(); + } + + return ""; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractRelationshipParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractRelationshipParser.java new file mode 100644 index 000000000..d8ffb28c6 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractRelationshipParser.java @@ -0,0 +1,37 @@ +package com.structurizr.dsl; + +import com.structurizr.model.*; + +abstract class AbstractRelationshipParser extends AbstractParser { + + protected Relationship createRelationship(Element sourceElement, String description, String technology, String[] tags, Element destinationElement) { + Relationship relationship = null; + + if (sourceElement instanceof CustomElement) { + relationship = ((CustomElement)sourceElement).uses(destinationElement, description, technology, null, tags); + } else if (destinationElement instanceof CustomElement) { + relationship = sourceElement.uses((CustomElement)destinationElement, description, technology, null, tags); + } else if (sourceElement instanceof StaticStructureElement && destinationElement instanceof StaticStructureElement) { + relationship = ((StaticStructureElement)sourceElement).uses((StaticStructureElement)destinationElement, description, technology, null, tags); + } else if (sourceElement instanceof DeploymentNode && destinationElement instanceof DeploymentNode) { + relationship = ((DeploymentNode)sourceElement).uses((DeploymentNode)destinationElement, description, technology, null, tags); + } else if (sourceElement instanceof DeploymentNode && destinationElement instanceof InfrastructureNode) { + relationship = ((DeploymentNode)sourceElement).uses((InfrastructureNode) destinationElement, description, technology, null, tags); + } else if (sourceElement instanceof InfrastructureNode && destinationElement instanceof DeploymentElement) { + relationship = ((InfrastructureNode)sourceElement).uses((DeploymentElement)destinationElement, description, technology, null, tags); + } else if (sourceElement instanceof StaticStructureElementInstance && destinationElement instanceof InfrastructureNode) { + relationship = ((StaticStructureElementInstance)sourceElement).uses((InfrastructureNode)destinationElement, description, technology, null, tags); + } else { + throw new RuntimeException("A relationship between \"" + sourceElement.getCanonicalName() + "\" and \"" + destinationElement.getCanonicalName() + "\" is not permitted"); + } + + if (relationship == null) { + if (sourceElement.hasEfferentRelationshipWith(destinationElement, description) || sourceElement.hasEfferentRelationshipWith(destinationElement)) { + throw new RuntimeException("A relationship between \"" + sourceElement.getCanonicalName() + "\" and \"" + destinationElement.getCanonicalName() + "\" already exists"); + } + } + + return relationship; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractViewParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractViewParser.java new file mode 100644 index 000000000..8f66d3e9b --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractViewParser.java @@ -0,0 +1,4 @@ +package com.structurizr.dsl; + +abstract class AbstractViewParser extends AbstractParser { +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/AdrsParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/AdrsParser.java new file mode 100644 index 000000000..110e23462 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/AdrsParser.java @@ -0,0 +1,80 @@ +package com.structurizr.dsl; + +import com.structurizr.documentation.Documentable; +import com.structurizr.importer.documentation.DefaultImageImporter; +import com.structurizr.importer.documentation.DocumentationImporter; + +import java.io.File; +import java.lang.reflect.Constructor; + +final class AdrsParser extends AbstractParser { + + private static final String DEFAULT_DECISION_IMPORTER = "com.structurizr.importer.documentation.AdrToolsDecisionImporter"; + + private static final String GRAMMAR = "!adrs "; + + private static final int PATH_INDEX = 1; + private static final int FQN_INDEX = 2; + + void parse(WorkspaceDslContext context, File dslFile, Tokens tokens) { + parse(context, context.getWorkspace(), dslFile, tokens); + } + + void parse(SoftwareSystemDslContext context, File dslFile, Tokens tokens) { + parse(context, context.getSoftwareSystem(), dslFile, tokens); + } + + void parse(ContainerDslContext context, File dslFile, Tokens tokens) { + parse(context, context.getContainer(), dslFile, tokens); + } + + void parse(ComponentDslContext context, File dslFile, Tokens tokens) { + parse(context, context.getComponent(), dslFile, tokens); + } + + private void parse(DslContext context, Documentable documentable, File dslFile, Tokens tokens) { + // !adrs + + if (tokens.hasMoreThan(FQN_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } + + if (!tokens.includes(PATH_INDEX)) { + throw new RuntimeException("Expected: " + GRAMMAR); + } + + String fullyQualifiedClassName = DEFAULT_DECISION_IMPORTER; + if (tokens.includes(FQN_INDEX)) { + fullyQualifiedClassName = tokens.get(FQN_INDEX); + } + + if (dslFile != null) { + File path = new File(dslFile.getParentFile(), tokens.get(PATH_INDEX)); + + if (!path.exists()) { + throw new RuntimeException("Documentation path " + path + " does not exist"); + } + + if (!path.isDirectory()) { + throw new RuntimeException("Documentation path " + path + " is not a directory"); + } + + try { + Class decisionImporterClass = context.loadClass(fullyQualifiedClassName, dslFile); + Constructor constructor = decisionImporterClass.getDeclaredConstructor(); + DocumentationImporter decisionImporter = (DocumentationImporter)constructor.newInstance(); + decisionImporter.importDocumentation(documentable, path); + + if (!tokens.includes(FQN_INDEX)) { + DefaultImageImporter imageImporter = new DefaultImageImporter(); + imageImporter.importDocumentation(documentable, path); + } + } catch (ClassNotFoundException cnfe) { + throw new RuntimeException("Error importing ADRs from " + path.getAbsolutePath() + ": " + fullyQualifiedClassName + " was not found"); + } catch (Exception e) { + throw new RuntimeException("Error importing ADRs from " + path.getAbsolutePath() + ": " + e.getMessage()); + } + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/AutoLayoutParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/AutoLayoutParser.java new file mode 100644 index 000000000..7a8be9e4d --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/AutoLayoutParser.java @@ -0,0 +1,70 @@ +package com.structurizr.dsl; + +import com.structurizr.view.AutomaticLayout; +import com.structurizr.view.ModelView; +import com.structurizr.view.View; + +import java.util.HashMap; +import java.util.Map; + +final class AutoLayoutParser extends AbstractParser { + + private static final int DEFAULT_RANK_SEPARATION = 300; + private static final int DEFAULT_NODE_SEPARATION = 300; + + private static final int RANK_DIRECTION_INDEX = 1; + private static final int RANK_SEPARATION_INDEX = 2; + private static final int NODE_SEPARATION_INDEX = 3; + + private static Map RANK_DIRECTIONS = new HashMap<>(); + + static { + RANK_DIRECTIONS.put("tb", AutomaticLayout.RankDirection.TopBottom); + RANK_DIRECTIONS.put("bt", AutomaticLayout.RankDirection.BottomTop); + RANK_DIRECTIONS.put("lr", AutomaticLayout.RankDirection.LeftRight); + RANK_DIRECTIONS.put("rl", AutomaticLayout.RankDirection.RightLeft); + } + + void parse(ModelViewDslContext context, Tokens tokens) { + // autoLayout [rankDirection] [rankSeparation] [nodeSeparation] + ModelView view = context.getView(); + if (view != null) { + AutomaticLayout.RankDirection rankDirection = AutomaticLayout.RankDirection.TopBottom; + int rankSeparation = DEFAULT_RANK_SEPARATION; + int nodeSeparation = DEFAULT_NODE_SEPARATION; + + if (tokens.includes(RANK_DIRECTION_INDEX)) { + String rankDirectionAsString = tokens.get(RANK_DIRECTION_INDEX); + + if (RANK_DIRECTIONS.containsKey(rankDirectionAsString)) { + rankDirection = RANK_DIRECTIONS.get(rankDirectionAsString); + } else { + throw new RuntimeException("Valid rank directions are: tb|bt|lr|rl"); + } + } + + if (tokens.includes(RANK_SEPARATION_INDEX)) { + String rankSeparationAsString = tokens.get(RANK_SEPARATION_INDEX); + + try { + rankSeparation = Integer.parseInt(rankSeparationAsString); + } catch (NumberFormatException e) { + throw new RuntimeException("Rank separation must be positive integer in pixels"); + } + } + + if (tokens.includes(NODE_SEPARATION_INDEX)) { + String nodeSeparationAsString = tokens.get(NODE_SEPARATION_INDEX); + + try { + nodeSeparation = Integer.parseInt(nodeSeparationAsString); + } catch (NumberFormatException e) { + throw new RuntimeException("Node separation must be positive integer in pixels"); + } + } + + view.enableAutomaticLayout(rankDirection, rankSeparation, nodeSeparation); + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/BrandingDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/BrandingDslContext.java new file mode 100644 index 000000000..e2b730cd4 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/BrandingDslContext.java @@ -0,0 +1,25 @@ +package com.structurizr.dsl; + +import java.io.File; + +final class BrandingDslContext extends DslContext { + + private File file; + + BrandingDslContext(File file) { + this.file = file; + } + + File getFile() { + return file; + } + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.BRANDING_LOGO_TOKEN, + StructurizrDslTokens.BRANDING_FONT_TOKEN + }; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/BrandingParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/BrandingParser.java new file mode 100644 index 000000000..62c08fd4b --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/BrandingParser.java @@ -0,0 +1,71 @@ +package com.structurizr.dsl; + +import com.structurizr.util.ImageUtils; +import com.structurizr.view.Font; + +import java.io.File; + +final class BrandingParser extends AbstractParser { + + private static final String LOGO_GRAMMAR = "logo "; + private static final String FONT_GRAMMAR = "font [url]"; + + private static final int LOGO_FILE_INDEX = 1; + + private static final int FONT_NAME_INDEX = 1; + private static final int FONT_URL_INDEX = 2; + + void parseLogo(BrandingDslContext context, Tokens tokens, boolean restricted) { + // logo + + if (tokens.hasMoreThan(LOGO_FILE_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + LOGO_GRAMMAR); + } else if (tokens.includes(LOGO_FILE_INDEX)) { + String path = tokens.get(1); + + if (path.startsWith("data:image/") || path.startsWith("https://") || path.startsWith("http://")) { + if (IconUtils.isSupported(path)) { + context.getWorkspace().getViews().getConfiguration().getBranding().setLogo(path); + } else { + throw new IllegalArgumentException("Only PNG and JPG URLs and data URIs are supported: " + path); + } + } else { + if (!restricted) { + File file = new File(context.getFile().getParent(), path); + if (file.exists() && !file.isDirectory()) { + try { + String dataUri = ImageUtils.getImageAsDataUri(file); + context.getWorkspace().getViews().getConfiguration().getBranding().setLogo(dataUri); + } catch (Exception e) { + throw new RuntimeException(e); + } + } else { + throw new RuntimeException(path + " does not exist"); + } + } + } + } else { + throw new RuntimeException("Expected: " + LOGO_GRAMMAR); + } + } + + void parseFont(BrandingDslContext context, Tokens tokens) { + // font [url] + + if (tokens.hasMoreThan(FONT_URL_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + FONT_GRAMMAR); + } else if (tokens.includes(FONT_URL_INDEX)) { + String name = tokens.get(FONT_NAME_INDEX); + String url = tokens.get(FONT_URL_INDEX); + + context.getWorkspace().getViews().getConfiguration().getBranding().setFont(new Font(name, url)); + } else if (tokens.includes(FONT_NAME_INDEX)) { + String name = tokens.get(FONT_NAME_INDEX); + + context.getWorkspace().getViews().getConfiguration().getBranding().setFont(new Font(name)); + } else { + throw new RuntimeException("Expected: " + FONT_GRAMMAR); + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/CommentDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/CommentDslContext.java new file mode 100644 index 000000000..24f802627 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/CommentDslContext.java @@ -0,0 +1,10 @@ +package com.structurizr.dsl; + +final class CommentDslContext extends DslContext { + + @Override + protected String[] getPermittedTokens() { + return new String[0]; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentDslContext.java new file mode 100644 index 000000000..39e079084 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentDslContext.java @@ -0,0 +1,42 @@ +package com.structurizr.dsl; + +import com.structurizr.model.Component; +import com.structurizr.model.GroupableElement; +import com.structurizr.model.ModelItem; + +final class ComponentDslContext extends GroupableElementDslContext { + + private Component component; + + ComponentDslContext(Component component) { + this.component = component; + } + + Component getComponent() { + return component; + } + + @Override + ModelItem getModelItem() { + return getComponent(); + } + + @Override + GroupableElement getElement() { + return component; + } + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.DESCRIPTION_TOKEN, + StructurizrDslTokens.TECHNOLOGY_TOKEN, + StructurizrDslTokens.TAGS_TOKEN, + StructurizrDslTokens.URL_TOKEN, + StructurizrDslTokens.PROPERTIES_TOKEN, + StructurizrDslTokens.PERSPECTIVES_TOKEN, + StructurizrDslTokens.RELATIONSHIP_TOKEN + }; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentParser.java new file mode 100644 index 000000000..9940d837a --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentParser.java @@ -0,0 +1,77 @@ +package com.structurizr.dsl; + +import com.structurizr.model.Component; +import com.structurizr.model.Container; + +final class ComponentParser extends AbstractParser { + + private static final String GRAMMAR = "component [description] [technology] [tags]"; + + private final static int NAME_INDEX = 1; + private final static int DESCRIPTION_INDEX = 2; + private final static int TECHNOLOGY_INDEX = 3; + private final static int TAGS_INDEX = 4; + + Component parse(ContainerDslContext context, Tokens tokens) { + // component [description] [technology] [tags] + + if (tokens.hasMoreThan(TAGS_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } + + if (!tokens.includes(NAME_INDEX)) { + throw new RuntimeException("Expected: " + GRAMMAR); + } + + Container container = context.getContainer(); + Component component = null; + String name = tokens.get(NAME_INDEX); + + if (context.isExtendingWorkspace()) { + component = container.getComponentWithName(name); + } + + if (component == null) { + component = container.addComponent(name); + } + + if (tokens.includes(DESCRIPTION_INDEX)) { + String description = tokens.get(DESCRIPTION_INDEX); + component.setDescription(description); + } + + if (tokens.includes(TECHNOLOGY_INDEX)) { + String technology = tokens.get(TECHNOLOGY_INDEX); + component.setTechnology(technology); + } + + if (tokens.includes(TAGS_INDEX)) { + String tags = tokens.get(TAGS_INDEX); + component.addTags(tags.split(",")); + } + + if (context.hasGroup()) { + component.setGroup(context.getGroup().getName()); + context.getGroup().addElement(component); + } + + return component; + } + + void parseTechnology(ComponentDslContext context, Tokens tokens) { + int index = 1; + + // technology + if (tokens.hasMoreThan(index)) { + throw new RuntimeException("Too many tokens, expected: technology "); + } + + if (!tokens.includes(index)) { + throw new RuntimeException("Expected: technology "); + } + + String technology = tokens.get(index); + context.getComponent().setTechnology(technology); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentViewDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentViewDslContext.java new file mode 100644 index 000000000..31b482510 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentViewDslContext.java @@ -0,0 +1,11 @@ +package com.structurizr.dsl; + +import com.structurizr.view.ComponentView; + +final class ComponentViewDslContext extends StaticViewDslContext { + + ComponentViewDslContext(ComponentView view) { + super(view); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentViewParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentViewParser.java new file mode 100644 index 000000000..3acd88669 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentViewParser.java @@ -0,0 +1,62 @@ +package com.structurizr.dsl; + +import com.structurizr.Workspace; +import com.structurizr.model.Container; +import com.structurizr.model.Element; +import com.structurizr.view.ComponentView; + +final class ComponentViewParser extends AbstractViewParser { + + private static final String GRAMMAR = "component [key] [description] {"; + + private static final String VIEW_TYPE = "Component"; + + private static final int CONTAINER_IDENTIFIER_INDEX = 1; + private static final int KEY_INDEX = 2; + private static final int DESCRIPTION_INDEX = 3; + + ComponentView parse(DslContext context, Tokens tokens) { + // component [key] [description] { + + if (tokens.hasMoreThan(DESCRIPTION_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } + + if (!tokens.includes(CONTAINER_IDENTIFIER_INDEX)) { + throw new RuntimeException("Expected: " + GRAMMAR); + } + + Workspace workspace = context.getWorkspace(); + Container container; + String key = ""; + String description = ""; + + String containerIdentifier = tokens.get(CONTAINER_IDENTIFIER_INDEX); + Element element = context.getElement(containerIdentifier); + if (element == null) { + throw new RuntimeException("The container \"" + containerIdentifier + "\" does not exist"); + } + if (element instanceof Container) { + container = (Container)element; + } else { + throw new RuntimeException("The element \"" + containerIdentifier + "\" is not a container"); + } + + if (tokens.includes(KEY_INDEX)) { + key = tokens.get(KEY_INDEX); + } else { + key = workspace.getViews().generateViewKey(VIEW_TYPE); + } + validateViewKey(key); + + if (tokens.includes(DESCRIPTION_INDEX)) { + description = tokens.get(DESCRIPTION_INDEX); + } + + ComponentView view = workspace.getViews().createComponentView(container, key, description); + view.setExternalSoftwareSystemBoundariesVisible(true); + + return view; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ConfigurationDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ConfigurationDslContext.java new file mode 100644 index 000000000..18b211f57 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ConfigurationDslContext.java @@ -0,0 +1,15 @@ +package com.structurizr.dsl; + +final class ConfigurationDslContext extends DslContext { + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.SCOPE_TOKEN, + StructurizrDslTokens.VISIBILITY_TOKEN, + StructurizrDslTokens.USERS_TOKEN, + StructurizrDslTokens.PROPERTIES_TOKEN + }; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ConfigurationParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ConfigurationParser.java new file mode 100644 index 000000000..ddd24799e --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ConfigurationParser.java @@ -0,0 +1,61 @@ +package com.structurizr.dsl; + +import com.structurizr.configuration.Visibility; +import com.structurizr.configuration.WorkspaceScope; + +final class ConfigurationParser extends AbstractParser { + + private static final String SCOPE_GRAMMAR = "scope "; + private static final String SCOPE_LANDSCAPE = "landscape"; + private static final String SCOPE_SOFTWARE_SYSTEM = "softwaresystem"; + private static final String SCOPE_NONE = "none"; + + private static final String VISIBILITY_GRAMMAR = "visibility "; + private static final String VISIBILITY_PRIVATE = "private"; + private static final String VISIBILITY_PUBLIC = "public"; + + private static final int FIRST_PROPERTY_INDEX = 1; + + void parseScope(DslContext context, Tokens tokens) { + if (tokens.hasMoreThan(FIRST_PROPERTY_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + SCOPE_GRAMMAR); + } + + if (tokens.includes(FIRST_PROPERTY_INDEX)) { + String scope = tokens.get(1).toLowerCase(); + + if (scope.equalsIgnoreCase(SCOPE_LANDSCAPE)) { + context.getWorkspace().getConfiguration().setScope(WorkspaceScope.Landscape); + } else if (scope.equalsIgnoreCase(SCOPE_SOFTWARE_SYSTEM)) { + context.getWorkspace().getConfiguration().setScope(WorkspaceScope.SoftwareSystem); + } else if (scope.equalsIgnoreCase(SCOPE_NONE)) { + context.getWorkspace().getConfiguration().setScope(null); + } else { + throw new RuntimeException("The scope \"" + scope + "\" is not valid"); + } + } else { + throw new RuntimeException("Expected: " + SCOPE_GRAMMAR); + } + } + + void parseVisibility(DslContext context, Tokens tokens) { + if (tokens.hasMoreThan(FIRST_PROPERTY_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + VISIBILITY_GRAMMAR); + } + + if (tokens.includes(FIRST_PROPERTY_INDEX)) { + String visibility = tokens.get(1).toLowerCase(); + + if (visibility.equalsIgnoreCase(VISIBILITY_PRIVATE)) { + context.getWorkspace().getConfiguration().setVisibility(Visibility.Private); + } else if (visibility.equalsIgnoreCase(VISIBILITY_PUBLIC)) { + context.getWorkspace().getConfiguration().setVisibility(Visibility.Public); + } else { + throw new RuntimeException("The visibility \"" + visibility + "\" is not valid"); + } + } else { + throw new RuntimeException("Expected: " + VISIBILITY_GRAMMAR); + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/Constant.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/Constant.java new file mode 100644 index 000000000..e290a35a6 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/Constant.java @@ -0,0 +1,21 @@ +package com.structurizr.dsl; + +class Constant { + + private String name; + private String value; + + Constant(String name, String value) { + this.name = name; + this.value = value; + } + + String getName() { + return name; + } + + String getValue() { + return value; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ConstantParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ConstantParser.java new file mode 100644 index 000000000..4b8c28204 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ConstantParser.java @@ -0,0 +1,33 @@ +package com.structurizr.dsl; + +final class ConstantParser extends AbstractParser { + + private static final String GRAMMAR = "!constant "; + + private static final int NAME_INDEX = 1; + private static final int VALUE_INDEX = 2; + + private static final String NAME_REGEX = "[a-zA-Z0-9-_.]+"; + + Constant parse(DslContext context, Tokens tokens) { + // !constant name value + + if (tokens.hasMoreThan(VALUE_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } + + if (!tokens.includes(VALUE_INDEX)) { + throw new RuntimeException("Expected: " + GRAMMAR); + } + + String name = tokens.get(NAME_INDEX); + String value = tokens.get(VALUE_INDEX); + + if (!name.matches(NAME_REGEX)) { + throw new RuntimeException("Constant names must only contain the following characters: a-zA-Z0-9-_."); + } + + return new Constant(name, value); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerDslContext.java new file mode 100644 index 000000000..2099a3b15 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerDslContext.java @@ -0,0 +1,52 @@ +package com.structurizr.dsl; + +import com.structurizr.model.Container; +import com.structurizr.model.GroupableElement; +import com.structurizr.model.ModelItem; + +final class ContainerDslContext extends GroupableElementDslContext { + + private Container container; + + ContainerDslContext(Container container) { + this(container, null); + } + + ContainerDslContext(Container container, ElementGroup group) { + super(group); + + this.container = container; + } + + Container getContainer() { + return container; + } + + @Override + ModelItem getModelItem() { + return getContainer(); + } + + @Override + GroupableElement getElement() { + return container; + } + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.DOCS_TOKEN, + StructurizrDslTokens.ADRS_TOKEN, + StructurizrDslTokens.GROUP_TOKEN, + StructurizrDslTokens.COMPONENT_TOKEN, + StructurizrDslTokens.DESCRIPTION_TOKEN, + StructurizrDslTokens.TECHNOLOGY_TOKEN, + StructurizrDslTokens.TAGS_TOKEN, + StructurizrDslTokens.URL_TOKEN, + StructurizrDslTokens.PROPERTIES_TOKEN, + StructurizrDslTokens.PERSPECTIVES_TOKEN, + StructurizrDslTokens.RELATIONSHIP_TOKEN + }; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerInstanceDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerInstanceDslContext.java new file mode 100644 index 000000000..4268f656b --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerInstanceDslContext.java @@ -0,0 +1,42 @@ +package com.structurizr.dsl; + +import com.structurizr.model.ContainerInstance; +import com.structurizr.model.ModelItem; +import com.structurizr.model.StaticStructureElementInstance; + +final class ContainerInstanceDslContext extends StaticStructureElementInstanceDslContext { + + private ContainerInstance containerInstance; + + ContainerInstanceDslContext(ContainerInstance containerInstance) { + this.containerInstance = containerInstance; + } + + ContainerInstance getContainerInstance() { + return containerInstance; + } + + @Override + ModelItem getModelItem() { + return getContainerInstance(); + } + + @Override + StaticStructureElementInstance getElementInstance() { + return getContainerInstance(); + } + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.RELATIONSHIP_TOKEN, + StructurizrDslTokens.DESCRIPTION_TOKEN, + StructurizrDslTokens.TAGS_TOKEN, + StructurizrDslTokens.URL_TOKEN, + StructurizrDslTokens.PROPERTIES_TOKEN, + StructurizrDslTokens.PERSPECTIVES_TOKEN, + StructurizrDslTokens.HEALTH_CHECK_TOKEN + }; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerInstanceParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerInstanceParser.java new file mode 100644 index 000000000..c58287956 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerInstanceParser.java @@ -0,0 +1,64 @@ +package com.structurizr.dsl; + +import com.structurizr.model.*; + +import java.util.HashSet; +import java.util.Set; + +final class ContainerInstanceParser extends AbstractParser { + + private static final String GRAMMAR = "containerInstance [deploymentGroups] [tags]"; + + private static final int IDENTIFIER_INDEX = 1; + private static final int DEPLOYMENT_GROUPS_TOKEN = 2; + private static final int TAGS_INDEX = 3; + + ContainerInstance parse(DeploymentNodeDslContext context, Tokens tokens) { + // containerInstance [deploymentGroups] [tags] + + if (tokens.hasMoreThan(TAGS_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } + + if (!tokens.includes(IDENTIFIER_INDEX)) { + throw new RuntimeException("Expected: " + GRAMMAR); + } + + String containerIdentifier = tokens.get(IDENTIFIER_INDEX); + + Element element = context.getElement(containerIdentifier, Container.class); + if (element == null) { + throw new RuntimeException("The container \"" + containerIdentifier + "\" does not exist"); + } + + DeploymentNode deploymentNode = context.getDeploymentNode(); + + Set deploymentGroups = new HashSet<>(); + if (tokens.includes(DEPLOYMENT_GROUPS_TOKEN)) { + String token = tokens.get(DEPLOYMENT_GROUPS_TOKEN); + + String[] deploymentGroupReferences = token.split(","); + for (String deploymentGroupReference : deploymentGroupReferences) { + Element e = context.getElement(deploymentGroupReference); + if (e instanceof DeploymentGroup) { + deploymentGroups.add(e.getName()); + } + } + } + + ContainerInstance containerInstance = deploymentNode.add((Container)element, deploymentGroups.toArray(new String[]{})); + + if (tokens.includes(TAGS_INDEX)) { + String tags = tokens.get(TAGS_INDEX); + containerInstance.addTags(tags.split(",")); + } + + if (context.hasGroup()) { + containerInstance.setGroup(context.getGroup().getName()); + context.getGroup().addElement(containerInstance); + } + + return containerInstance; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerParser.java new file mode 100644 index 000000000..eb7f38372 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerParser.java @@ -0,0 +1,77 @@ +package com.structurizr.dsl; + +import com.structurizr.model.Container; +import com.structurizr.model.SoftwareSystem; + +final class ContainerParser extends AbstractParser { + + private static final String GRAMMAR = "container [description] [technology] [tags]"; + + private final static int NAME_INDEX = 1; + private final static int DESCRIPTION_INDEX = 2; + private final static int TECHNOLOGY_INDEX = 3; + private final static int TAGS_INDEX = 4; + + Container parse(SoftwareSystemDslContext context, Tokens tokens) { + // container [description] [technology] [tags] + + if (tokens.hasMoreThan(TAGS_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } + + if (!tokens.includes(NAME_INDEX)) { + throw new RuntimeException("Expected: " + GRAMMAR); + } + + SoftwareSystem softwareSystem = context.getSoftwareSystem(); + Container container = null; + String name = tokens.get(NAME_INDEX); + + if (context.isExtendingWorkspace()) { + container = softwareSystem.getContainerWithName(name); + } + + if (container == null) { + container = softwareSystem.addContainer(name); + } + + if (tokens.includes(DESCRIPTION_INDEX)) { + String description = tokens.get(DESCRIPTION_INDEX); + container.setDescription(description); + } + + if (tokens.includes(TECHNOLOGY_INDEX)) { + String technology = tokens.get(TECHNOLOGY_INDEX); + container.setTechnology(technology); + } + + if (tokens.includes(TAGS_INDEX)) { + String tags = tokens.get(TAGS_INDEX); + container.addTags(tags.split(",")); + } + + if (context.hasGroup()) { + container.setGroup(context.getGroup().getName()); + context.getGroup().addElement(container); + } + + return container; + } + + void parseTechnology(ContainerDslContext context, Tokens tokens) { + int index = 1; + + // technology + if (tokens.hasMoreThan(index)) { + throw new RuntimeException("Too many tokens, expected: technology "); + } + + if (!tokens.includes(index)) { + throw new RuntimeException("Expected: technology "); + } + + String technology = tokens.get(index); + context.getContainer().setTechnology(technology); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerViewDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerViewDslContext.java new file mode 100644 index 000000000..c304fd247 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerViewDslContext.java @@ -0,0 +1,11 @@ +package com.structurizr.dsl; + +import com.structurizr.view.ContainerView; + +final class ContainerViewDslContext extends StaticViewDslContext { + + ContainerViewDslContext(ContainerView view) { + super(view); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerViewParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerViewParser.java new file mode 100644 index 000000000..b1033eb43 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerViewParser.java @@ -0,0 +1,62 @@ +package com.structurizr.dsl; + +import com.structurizr.Workspace; +import com.structurizr.model.Element; +import com.structurizr.model.SoftwareSystem; +import com.structurizr.view.ContainerView; + +final class ContainerViewParser extends AbstractViewParser { + + private static final String GRAMMAR = "container [key] [description] {"; + + private static final String VIEW_TYPE = "Container"; + + private static final int SOFTWARE_SYSTEM_IDENTIFIER_INDEX = 1; + private static final int KEY_INDEX = 2; + private static final int DESCRIPTION_INDEX = 3; + + ContainerView parse(DslContext context, Tokens tokens) { + // container [key] [description] { + + if (tokens.hasMoreThan(DESCRIPTION_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } + + if (!tokens.includes(SOFTWARE_SYSTEM_IDENTIFIER_INDEX)) { + throw new RuntimeException("Expected: " + GRAMMAR); + } + + Workspace workspace = context.getWorkspace(); + SoftwareSystem softwareSystem; + String key = ""; + String description = ""; + + String softwareSystemIdentifier = tokens.get(SOFTWARE_SYSTEM_IDENTIFIER_INDEX); + Element element = context.getElement(softwareSystemIdentifier); + if (element == null) { + throw new RuntimeException("The software system \"" + softwareSystemIdentifier + "\" does not exist"); + } + if (element instanceof SoftwareSystem) { + softwareSystem = (SoftwareSystem)element; + } else { + throw new RuntimeException("The element \"" + softwareSystemIdentifier + "\" is not a software system"); + } + + if (tokens.includes(KEY_INDEX)) { + key = tokens.get(KEY_INDEX); + } else { + key = workspace.getViews().generateViewKey(VIEW_TYPE); + } + validateViewKey(key); + + if (tokens.includes(DESCRIPTION_INDEX)) { + description = tokens.get(DESCRIPTION_INDEX); + } + + ContainerView view = workspace.getViews().createContainerView(softwareSystem, key, description); + view.setExternalSoftwareSystemBoundariesVisible(true); + + return view; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomElementDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomElementDslContext.java new file mode 100644 index 000000000..78daacea1 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomElementDslContext.java @@ -0,0 +1,41 @@ +package com.structurizr.dsl; + +import com.structurizr.model.CustomElement; +import com.structurizr.model.GroupableElement; +import com.structurizr.model.ModelItem; + +final class CustomElementDslContext extends GroupableElementDslContext { + + private CustomElement customElement; + + CustomElementDslContext(CustomElement customElement) { + this.customElement = customElement; + } + + CustomElement getCustomElement() { + return customElement; + } + + @Override + ModelItem getModelItem() { + return getCustomElement(); + } + + @Override + GroupableElement getElement() { + return customElement; + } + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.DESCRIPTION_TOKEN, + StructurizrDslTokens.TAGS_TOKEN, + StructurizrDslTokens.URL_TOKEN, + StructurizrDslTokens.PROPERTIES_TOKEN, + StructurizrDslTokens.PERSPECTIVES_TOKEN, + StructurizrDslTokens.RELATIONSHIP_TOKEN + }; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomElementParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomElementParser.java new file mode 100644 index 000000000..bef0a7707 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomElementParser.java @@ -0,0 +1,53 @@ +package com.structurizr.dsl; + +import com.structurizr.model.CustomElement; +import com.structurizr.model.Location; +import com.structurizr.model.Person; + +final class CustomElementParser extends AbstractParser { + + private static final String GRAMMAR = "element [metadata] [description] [tags]"; + + private final static int NAME_INDEX = 1; + private final static int METADATA_INDEX = 2; + private final static int DESCRIPTION_INDEX = 3; + private final static int TAGS_INDEX = 4; + + CustomElement parse(GroupableDslContext context, Tokens tokens) { + // element [metadata] [description] [tags] + + if (tokens.hasMoreThan(TAGS_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } + + if (!tokens.includes(NAME_INDEX)) { + throw new RuntimeException("Expected: " + GRAMMAR); + } + + String name = tokens.get(NAME_INDEX); + + String metadata = ""; + if (tokens.includes(METADATA_INDEX)) { + metadata = tokens.get(METADATA_INDEX); + } + + String description = ""; + if (tokens.includes(DESCRIPTION_INDEX)) { + description = tokens.get(DESCRIPTION_INDEX); + } + + CustomElement customElement = context.getWorkspace().getModel().addCustomElement(name, metadata, description); + + if (tokens.includes(TAGS_INDEX)) { + String tags = tokens.get(TAGS_INDEX); + customElement.addTags(tags.split(",")); + } + + if (context.hasGroup()) { + customElement.setGroup(context.getGroup().getName()); + } + + return customElement; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomViewAnimationDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomViewAnimationDslContext.java new file mode 100644 index 000000000..df19917a8 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomViewAnimationDslContext.java @@ -0,0 +1,26 @@ +package com.structurizr.dsl; + +import com.structurizr.view.CustomView; + +class CustomViewAnimationDslContext extends DslContext { + + private CustomView view; + + CustomViewAnimationDslContext(CustomView view) { + super(); + + this.view = view; + } + + CustomView getView() { + return view; + } + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.ANIMATION_STEP_IN_VIEW_TOKEN + }; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomViewAnimationStepParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomViewAnimationStepParser.java new file mode 100644 index 000000000..c455c18d4 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomViewAnimationStepParser.java @@ -0,0 +1,51 @@ +package com.structurizr.dsl; + +import com.structurizr.model.CustomElement; +import com.structurizr.model.Element; +import com.structurizr.view.CustomView; + +import java.util.ArrayList; +import java.util.List; + +final class CustomViewAnimationStepParser extends AbstractParser { + + void parse(CustomViewDslContext context, Tokens tokens) { + // animationStep [identifier...] + + if (!tokens.includes(1)) { + throw new RuntimeException("Expected: animationStep [identifier...]"); + } + + parse(context, context.getCustomView(), tokens, 1); + } + + void parse(CustomViewAnimationDslContext context, Tokens tokens) { + // [identifier...] + + if (!tokens.includes(0)) { + throw new RuntimeException("Expected: [identifier...]"); + } + + parse(context, context.getView(), tokens, 0); + } + + void parse(DslContext context, CustomView view, Tokens tokens, int startIndex) { + List elements = new ArrayList<>(); + + for (int i = startIndex; i < tokens.size(); i++) { + String elementIdentifier = tokens.get(i); + + Element element = context.getElement(elementIdentifier); + if (element == null) { + throw new RuntimeException("The element \"" + elementIdentifier + "\" does not exist"); + } + + if (element instanceof CustomElement) { + elements.add((CustomElement)element); + } + } + + view.addAnimation(elements.toArray(new CustomElement[0])); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomViewContentParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomViewContentParser.java new file mode 100644 index 000000000..47e97a6cc --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomViewContentParser.java @@ -0,0 +1,97 @@ +package com.structurizr.dsl; + +import com.structurizr.model.CustomElement; +import com.structurizr.model.Element; +import com.structurizr.model.ModelItem; +import com.structurizr.model.Relationship; +import com.structurizr.util.StringUtils; +import com.structurizr.view.CustomView; +import com.structurizr.view.ElementNotPermittedInViewException; + +final class CustomViewContentParser extends ModelViewContentParser { + + private static final int FIRST_IDENTIFIER_INDEX = 1; + + void parseInclude(CustomViewDslContext context, Tokens tokens) { + if (!tokens.includes(FIRST_IDENTIFIER_INDEX)) { + throw new RuntimeException("Expected: include <*|identifier> [*|identifier...]"); + } + + CustomView view = context.getCustomView(); + + // include [identifier...] + for (int i = FIRST_IDENTIFIER_INDEX; i < tokens.size(); i++) { + String token = tokens.get(i); + + if (token.equals(WILDCARD) || token.equals(ELEMENT_WILDCARD)) { + // include * or include element==* + view.addDefaultElements(); + } else if (isExpression(token)) { + new CustomViewExpressionParser().parseExpression(token, context).forEach(mi -> addModelItemToView(mi, view, null)); + } else { + new CustomViewExpressionParser().parseIdentifier(token, context).forEach(mi -> addModelItemToView(mi, view, token)); + } + } + } + + void parseExclude(CustomViewDslContext context, Tokens tokens) { + if (!tokens.includes(FIRST_IDENTIFIER_INDEX)) { + throw new RuntimeException("Expected: exclude [identifier...]"); + } + + CustomView view = context.getCustomView(); + + // exclude [identifier...] + for (int i = FIRST_IDENTIFIER_INDEX; i < tokens.size(); i++) { + String token = tokens.get(i); + if (isExpression(token)) { + new CustomViewExpressionParser().parseExpression(token, context).forEach(mi -> removeModelItemFromView(mi, view)); + } else { + new CustomViewExpressionParser().parseIdentifier(token, context).forEach(mi -> removeModelItemFromView(mi, view)); + } + } + } + + private void addModelItemToView(ModelItem modelItem, CustomView view, String identifier) { + if (modelItem instanceof Element) { + addElementToView((Element)modelItem, view, identifier); + } else { + addRelationshipToView((Relationship)modelItem, view); + } + } + + private void addElementToView(Element element, CustomView view, String identifier) { + try { + if (element instanceof CustomElement) { + view.add((CustomElement) element); + } else { + if (!StringUtils.isNullOrEmpty(identifier)) { + throw new RuntimeException("The element \"" + identifier + "\" can not be added to this type of view"); + } + } + } catch (ElementNotPermittedInViewException e) { + // ignore + } + } + + private void removeModelItemFromView(ModelItem modelItem, CustomView view) { + if (modelItem instanceof Element) { + removeElementFromView((Element)modelItem, view); + } else { + removeRelationshipFromView((Relationship)modelItem, view); + } + } + + private void removeElementFromView(Element element, CustomView view) { + if (element instanceof CustomElement) { + view.remove((CustomElement) element); + } + } + + private void addRelationshipToView(Relationship relationship, CustomView view) { + if (view.isElementInView(relationship.getSource()) && view.isElementInView(relationship.getDestination())) { + view.add(relationship); + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomViewDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomViewDslContext.java new file mode 100644 index 000000000..e434f9bed --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomViewDslContext.java @@ -0,0 +1,29 @@ +package com.structurizr.dsl; + +import com.structurizr.view.CustomView; + +final class CustomViewDslContext extends ModelViewDslContext { + + CustomViewDslContext(CustomView view) { + super(view); + } + + public CustomView getCustomView() { + return (CustomView)super.getView(); + } + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.INCLUDE_IN_VIEW_TOKEN, + StructurizrDslTokens.EXCLUDE_IN_VIEW_TOKEN, + StructurizrDslTokens.AUTOLAYOUT_VIEW_TOKEN, + StructurizrDslTokens.DEFAULT_VIEW_TOKEN, + StructurizrDslTokens.ANIMATION_IN_VIEW_TOKEN, + StructurizrDslTokens.VIEW_TITLE_TOKEN, + StructurizrDslTokens.VIEW_DESCRIPTION_TOKEN, + StructurizrDslTokens.PROPERTIES_TOKEN + }; + } + +} diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomViewExpressionParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomViewExpressionParser.java new file mode 100644 index 000000000..585a23899 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomViewExpressionParser.java @@ -0,0 +1,45 @@ +package com.structurizr.dsl; + +import com.structurizr.model.CustomElement; +import com.structurizr.model.Element; + +import java.util.LinkedHashSet; +import java.util.Set; + +import static com.structurizr.dsl.StructurizrDslExpressions.ELEMENT_TYPE_EQUALS_EXPRESSION; + +final class CustomViewExpressionParser extends AbstractExpressionParser { + + @Override + protected Set evaluateElementTypeExpression(String expr, DslContext context) { + Set elements = new LinkedHashSet<>(); + + String type = expr.substring(ELEMENT_TYPE_EQUALS_EXPRESSION.length()); + switch (type.toLowerCase()) { + case "custom": + context.getWorkspace().getModel().getElements().stream().filter(e -> e instanceof CustomElement).forEach(elements::add); + break; + default: + throw new RuntimeException("The element type of \"" + type + "\" is not valid for this view"); + } + + return elements; + } + + protected Set findAfferentCouplings(Element element) { + Set elements = new LinkedHashSet<>(); + + elements.addAll(findAfferentCouplings(element, CustomElement.class)); + + return elements; + } + + protected Set findEfferentCouplings(Element element) { + Set elements = new LinkedHashSet<>(); + + elements.addAll(findEfferentCouplings(element, CustomElement.class)); + + return elements; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomViewParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomViewParser.java new file mode 100644 index 000000000..5aedf5dc9 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomViewParser.java @@ -0,0 +1,48 @@ +package com.structurizr.dsl; + +import com.structurizr.Workspace; +import com.structurizr.view.CustomView; + +final class CustomViewParser extends AbstractViewParser { + + private static final String GRAMMAR = "custom [key] [title] [description] {"; + + private static final String VIEW_TYPE = "Custom"; + + private static final int KEY_INDEX = 1; + private static final int TITLE_INDEX = 2; + private static final int DESCRIPTION_INDEX = 3; + + CustomView parse(DslContext context, Tokens tokens) { + // custom [key] [title] [description] + + if (tokens.hasMoreThan(DESCRIPTION_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } + + Workspace workspace = context.getWorkspace(); + String key = ""; + String title = ""; + String description = ""; + + if (tokens.includes(KEY_INDEX)) { + key = tokens.get(KEY_INDEX); + } else { + key = workspace.getViews().generateViewKey(VIEW_TYPE); + } + validateViewKey(key); + + if (tokens.includes(TITLE_INDEX)) { + title = tokens.get(TITLE_INDEX); + } + + if (tokens.includes(DESCRIPTION_INDEX)) { + description = tokens.get(DESCRIPTION_INDEX); + } + + CustomView view = workspace.getViews().createCustomView(key, title, description); + + return view; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DefaultViewParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DefaultViewParser.java new file mode 100644 index 000000000..919e2ec57 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DefaultViewParser.java @@ -0,0 +1,12 @@ +package com.structurizr.dsl; + +import com.structurizr.view.View; + +final class DefaultViewParser extends AbstractParser { + + void parse(ViewDslContext context) { + View view = context.getView(); + context.getWorkspace().getViews().getConfiguration().setDefaultView(view); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentEnvironment.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentEnvironment.java new file mode 100644 index 000000000..2b7f60b6c --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentEnvironment.java @@ -0,0 +1,49 @@ +package com.structurizr.dsl; + +import com.structurizr.model.Element; + +import java.util.Objects; +import java.util.Set; + +class DeploymentEnvironment extends Element { + + private String name; + + DeploymentEnvironment(String name) { + this.name = name; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getCanonicalName() { + return name; + } + + @Override + public Element getParent() { + return null; + } + + @Override + public Set getDefaultTags() { + return null; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DeploymentEnvironment that = (DeploymentEnvironment) o; + return name.equals(that.name); + } + + @Override + public int hashCode() { + return Objects.hash(name); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentEnvironmentDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentEnvironmentDslContext.java new file mode 100644 index 000000000..c35027242 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentEnvironmentDslContext.java @@ -0,0 +1,31 @@ +package com.structurizr.dsl; + +final class DeploymentEnvironmentDslContext extends GroupableDslContext { + + private final String environment; + + DeploymentEnvironmentDslContext(String environment) { + super(null); + this.environment = environment; + } + + DeploymentEnvironmentDslContext(String environment, ElementGroup group) { + super(group); + this.environment = environment; + } + + String getEnvironment() { + return environment; + } + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.GROUP_TOKEN, + StructurizrDslTokens.DEPLOYMENT_GROUP_TOKEN, + StructurizrDslTokens.DEPLOYMENT_NODE_TOKEN, + StructurizrDslTokens.RELATIONSHIP_TOKEN + }; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentEnvironmentParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentEnvironmentParser.java new file mode 100644 index 000000000..d206f5f60 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentEnvironmentParser.java @@ -0,0 +1,21 @@ +package com.structurizr.dsl; + +final class DeploymentEnvironmentParser extends AbstractParser { + + private static final String GRAMMAR = "deploymentEnvironment {"; + + private static final int DEPLOYMENT_ENVIRONMENT_NAME_INDEX = 1; + + String parse(Tokens tokens) { + // deploymentEnvironment + + if (tokens.hasMoreThan(DEPLOYMENT_ENVIRONMENT_NAME_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } else if (tokens.size() != DEPLOYMENT_ENVIRONMENT_NAME_INDEX + 1) { + throw new RuntimeException("Expected: " + GRAMMAR); + } else { + return tokens.get(DEPLOYMENT_ENVIRONMENT_NAME_INDEX); + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentGroup.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentGroup.java new file mode 100644 index 000000000..0027da22f --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentGroup.java @@ -0,0 +1,35 @@ +package com.structurizr.dsl; + +import com.structurizr.model.Element; + +import java.util.Set; + +class DeploymentGroup extends Element { + + private String name; + + DeploymentGroup(String name) { + this.name = name; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getCanonicalName() { + return name; + } + + @Override + public Element getParent() { + return null; + } + + @Override + public Set getDefaultTags() { + return null; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentGroupParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentGroupParser.java new file mode 100644 index 000000000..ff0b79fc4 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentGroupParser.java @@ -0,0 +1,21 @@ +package com.structurizr.dsl; + +final class DeploymentGroupParser extends AbstractParser { + + private static final String GRAMMAR = "deploymentGroup "; + + private static final int DEPLOYMENT_GROUP_NAME_INDEX = 1; + + String parse(Tokens tokens) { + // deploymentGroup + + if (tokens.hasMoreThan(DEPLOYMENT_GROUP_NAME_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } else if (tokens.size() != DEPLOYMENT_GROUP_NAME_INDEX + 1) { + throw new RuntimeException("Expected: " + GRAMMAR); + } else { + return tokens.get(DEPLOYMENT_GROUP_NAME_INDEX); + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeDslContext.java new file mode 100644 index 000000000..0cfecda3d --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeDslContext.java @@ -0,0 +1,54 @@ +package com.structurizr.dsl; + +import com.structurizr.model.DeploymentNode; +import com.structurizr.model.GroupableElement; +import com.structurizr.model.ModelItem; + +final class DeploymentNodeDslContext extends GroupableElementDslContext { + + private final DeploymentNode deploymentNode; + + DeploymentNodeDslContext(DeploymentNode deploymentNode) { + this(deploymentNode, null); + } + + public DeploymentNodeDslContext(DeploymentNode deploymentNode, ElementGroup group) { + super(group); + + this.deploymentNode = deploymentNode; + } + + DeploymentNode getDeploymentNode() { + return deploymentNode; + } + + @Override + ModelItem getModelItem() { + return getDeploymentNode(); + } + + @Override + GroupableElement getElement() { + return deploymentNode; + } + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.GROUP_TOKEN, + StructurizrDslTokens.DEPLOYMENT_NODE_TOKEN, + StructurizrDslTokens.INFRASTRUCTURE_NODE_TOKEN, + StructurizrDslTokens.SOFTWARE_SYSTEM_INSTANCE_TOKEN, + StructurizrDslTokens.CONTAINER_INSTANCE_TOKEN, + StructurizrDslTokens.RELATIONSHIP_TOKEN, + StructurizrDslTokens.DESCRIPTION_TOKEN, + StructurizrDslTokens.TECHNOLOGY_TOKEN, + StructurizrDslTokens.INSTANCES_TOKEN, + StructurizrDslTokens.TAGS_TOKEN, + StructurizrDslTokens.URL_TOKEN, + StructurizrDslTokens.PROPERTIES_TOKEN, + StructurizrDslTokens.PERSPECTIVES_TOKEN, + }; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeParser.java new file mode 100644 index 000000000..ba88d73af --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeParser.java @@ -0,0 +1,140 @@ +package com.structurizr.dsl; + +import com.structurizr.model.DeploymentNode; + +final class DeploymentNodeParser extends AbstractParser { + + private static final String GRAMMAR = "deploymentNode [description] [technology] [tags] [instances] {"; + + private static final int NAME_INDEX = 1; + private static final int DESCRIPTION_INDEX = 2; + private static final int TECHNOLOGY_INDEX = 3; + private static final int TAGS_INDEX = 4; + private static final int INSTANCES_INDEX = 5; + + DeploymentNode parse(DeploymentEnvironmentDslContext context, Tokens tokens) { + // deploymentNode [description] [technology] [tags] [instances] + + if (tokens.hasMoreThan(INSTANCES_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } + + if (!tokens.includes(NAME_INDEX)) { + throw new RuntimeException("Expected: " + GRAMMAR); + } + + DeploymentNode deploymentNode = null; + String name = tokens.get(NAME_INDEX); + + String description = ""; + if (tokens.includes(DESCRIPTION_INDEX)) { + description = tokens.get(DESCRIPTION_INDEX); + } + + String technology = ""; + if (tokens.includes(TECHNOLOGY_INDEX)) { + technology = tokens.get(TECHNOLOGY_INDEX); + } + + deploymentNode = context.getWorkspace().getModel().addDeploymentNode(context.getEnvironment(), name, description, technology); + + String tags = ""; + if (tokens.includes(TAGS_INDEX)) { + tags = tokens.get(TAGS_INDEX); + deploymentNode.addTags(tags.split(",")); + } + + String instances = "1"; + if (tokens.includes(INSTANCES_INDEX)) { + instances = tokens.get(INSTANCES_INDEX); + deploymentNode.setInstances(instances); + } + + if (context.hasGroup()) { + deploymentNode.setGroup(context.getGroup().getName()); + context.getGroup().addElement(deploymentNode); + } + + return deploymentNode; + } + + DeploymentNode parse(DeploymentNodeDslContext context, Tokens tokens) { + // deploymentNode [description] [technology] [tags] [instances] + + if (tokens.hasMoreThan(INSTANCES_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } + + if (!tokens.includes(NAME_INDEX)) { + throw new RuntimeException("Expected: " + GRAMMAR); + } + + DeploymentNode deploymentNode = null; + String name = tokens.get(NAME_INDEX); + + String description = ""; + if (tokens.includes(DESCRIPTION_INDEX)) { + description = tokens.get(DESCRIPTION_INDEX); + } + + String technology = ""; + if (tokens.includes(TECHNOLOGY_INDEX)) { + technology = tokens.get(TECHNOLOGY_INDEX); + } + + DeploymentNode parent = context.getDeploymentNode(); + deploymentNode = parent.addDeploymentNode(name, description, technology); + + String tags = ""; + if (tokens.includes(TAGS_INDEX)) { + tags = tokens.get(TAGS_INDEX); + deploymentNode.addTags(tags.split(",")); + } + + String instances = "1"; + if (tokens.includes(INSTANCES_INDEX)) { + instances = tokens.get(INSTANCES_INDEX); + deploymentNode.setInstances(instances); + } + + if (context.hasGroup()) { + deploymentNode.setGroup(context.getGroup().getName()); + context.getGroup().addElement(deploymentNode); + } + + return deploymentNode; + } + + void parseTechnology(DeploymentNodeDslContext context, Tokens tokens) { + int index = 1; + + // technology + if (tokens.hasMoreThan(index)) { + throw new RuntimeException("Too many tokens, expected: technology "); + } + + if (!tokens.includes(index)) { + throw new RuntimeException("Expected: technology "); + } + + String technology = tokens.get(index); + context.getDeploymentNode().setTechnology(technology); + } + + void parseInstances(DeploymentNodeDslContext context, Tokens tokens) { + int index = 1; + + // instances + if (tokens.hasMoreThan(index)) { + throw new RuntimeException("Too many tokens, expected: instances "); + } + + if (!tokens.includes(index)) { + throw new RuntimeException("Expected: instances "); + } + + String instances = tokens.get(index); + context.getDeploymentNode().setInstances(instances); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentViewAnimationDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentViewAnimationDslContext.java new file mode 100644 index 000000000..b08ad1465 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentViewAnimationDslContext.java @@ -0,0 +1,26 @@ +package com.structurizr.dsl; + +import com.structurizr.view.DeploymentView; + +class DeploymentViewAnimationDslContext extends DslContext { + + private DeploymentView view; + + DeploymentViewAnimationDslContext(DeploymentView view) { + super(); + + this.view = view; + } + + DeploymentView getView() { + return view; + } + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.ANIMATION_STEP_IN_VIEW_TOKEN + }; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentViewAnimationStepParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentViewAnimationStepParser.java new file mode 100644 index 000000000..93598de55 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentViewAnimationStepParser.java @@ -0,0 +1,60 @@ +package com.structurizr.dsl; + +import com.structurizr.model.ContainerInstance; +import com.structurizr.model.Element; +import com.structurizr.model.InfrastructureNode; +import com.structurizr.model.StaticStructureElementInstance; +import com.structurizr.view.DeploymentView; + +import java.util.ArrayList; +import java.util.List; + +final class DeploymentViewAnimationStepParser extends AbstractParser { + + void parse(DeploymentViewDslContext context, Tokens tokens) { + // animationStep [identifier...] + + if (!tokens.includes(1)) { + throw new RuntimeException("Expected: animationStep [identifier...]"); + } + + parse(context, context.getView(), tokens, 1); + } + + void parse(DeploymentViewAnimationDslContext context, Tokens tokens) { + // animationStep [identifier...] + + if (!tokens.includes(0)) { + throw new RuntimeException("Expected: [identifier...]"); + } + + parse(context, context.getView(), tokens, 0); + } + + void parse(DslContext context, DeploymentView view, Tokens tokens, int startIndex) { + List staticStructureElementInstances = new ArrayList<>(); + List infrastructureNodes = new ArrayList<>(); + + for (int i = startIndex; i < tokens.size(); i++) { + String identifier = tokens.get(i); + + Element element = context.getElement(identifier); + if (element == null) { + throw new RuntimeException("The element \"" + identifier + "\" does not exist"); + } + + if (element instanceof StaticStructureElementInstance) { + staticStructureElementInstances.add((StaticStructureElementInstance)element); + } + + if (element instanceof InfrastructureNode) { + infrastructureNodes.add((InfrastructureNode)element); + } + } + + if (!(staticStructureElementInstances.isEmpty() && infrastructureNodes.isEmpty())) { + view.addAnimation(staticStructureElementInstances.toArray(new StaticStructureElementInstance[0]), infrastructureNodes.toArray(new InfrastructureNode[0])); + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentViewContentParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentViewContentParser.java new file mode 100644 index 000000000..00a955543 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentViewContentParser.java @@ -0,0 +1,154 @@ +package com.structurizr.dsl; + +import com.structurizr.model.*; +import com.structurizr.util.StringUtils; +import com.structurizr.view.*; + +import java.util.Collection; +import java.util.Set; +import java.util.stream.Collectors; + +final class DeploymentViewContentParser extends ModelViewContentParser { + + private static final int FIRST_IDENTIFIER_INDEX = 1; + + void parseInclude(DeploymentViewDslContext context, Tokens tokens) { + if (!tokens.includes(FIRST_IDENTIFIER_INDEX)) { + throw new RuntimeException("Expected: include <*|identifier> [*|identifier...]"); + } + + DeploymentView view = context.getView(); + + // include [identifier...] + for (int i = FIRST_IDENTIFIER_INDEX; i < tokens.size(); i++) { + String token = tokens.get(i); + + if (token.equals(WILDCARD) || token.equals(ELEMENT_WILDCARD)) { + // include * or include element==* + view.addDefaultElements(); + } else if (isExpression(token)) { + new DeploymentViewExpressionParser().parseExpression(token, context).forEach(mi -> addModelItemToView(mi, view, null)); + } else { + new DeploymentViewExpressionParser().parseIdentifier(token, context).forEach(mi -> addModelItemToView(mi, view, token)); + } + } + } + + void parseExclude(DeploymentViewDslContext context, Tokens tokens) { + if (!tokens.includes(FIRST_IDENTIFIER_INDEX)) { + throw new RuntimeException("Expected: exclude [identifier...]"); + } + + DeploymentView view = context.getView(); + + // exclude [identifier...] + for (int i = FIRST_IDENTIFIER_INDEX; i < tokens.size(); i++) { + String token = tokens.get(i); + + if (isExpression(token)) { + new DeploymentViewExpressionParser().parseExpression(token, context).forEach(e -> removeModelItemFromView(e, view)); + } else { + new DeploymentViewExpressionParser().parseIdentifier(token, context).forEach(mi -> removeModelItemFromView(mi, view)); + } + } + } + + private void addModelItemToView(ModelItem modelItem, DeploymentView view, String identifier) { + if (modelItem instanceof Element) { + addElementToView((Element)modelItem, view, identifier); + } else { + addRelationshipToView((Relationship)modelItem, view); + } + } + + private void addElementToView(Element element, DeploymentView view, String identifier) { + try { + if (element instanceof CustomElement) { + view.add((CustomElement) element); + } else if (element instanceof DeploymentNode) { + view.add((DeploymentNode) element); + } else if (element instanceof InfrastructureNode) { + view.add((InfrastructureNode) element); + } else if (element instanceof SoftwareSystem) { + // find instances of this software system + view.getModel().getElements().stream().filter(e -> e instanceof SoftwareSystemInstance).map(e -> (SoftwareSystemInstance)e).filter(ssi -> ssi.getSoftwareSystem().equals(element) && ssi.getEnvironment().equals(view.getEnvironment())).forEach(view::add); + } else if (element instanceof SoftwareSystemInstance) { + view.add((SoftwareSystemInstance) element); + } else if (element instanceof Container) { + // find instances of this container + view.getModel().getElements().stream().filter(e -> e instanceof ContainerInstance).map(e -> (ContainerInstance)e).filter(ci -> ci.getContainer().equals(element) && ci.getEnvironment().equals(view.getEnvironment())).forEach(view::add); + } else if (element instanceof ContainerInstance) { + view.add((ContainerInstance) element); + } else { + if (!StringUtils.isNullOrEmpty(identifier)) { + throw new RuntimeException("The element \"" + identifier + "\" can not be added to this type of view"); + } + } + } catch (ElementNotPermittedInViewException e) { + // ignore + } + } + + private void removeModelItemFromView(ModelItem modelItem, DeploymentView view) { + if (modelItem instanceof Element) { + removeElementFromView((Element)modelItem, view); + } else { + removeRelationshipFromView((Relationship)modelItem, view); + } + } + + private void removeElementFromView(Element element, DeploymentView view) { + if (element instanceof CustomElement) { + view.remove((CustomElement)element); + } else if (element instanceof DeploymentNode) { + view.remove((DeploymentNode)element); + } else if (element instanceof InfrastructureNode) { + view.remove((InfrastructureNode)element); + } else if (element instanceof SoftwareSystem) { + // find instances of this software system + view.getModel().getElements().stream().filter(e -> e instanceof SoftwareSystemInstance).map(e -> (SoftwareSystemInstance)e).filter(ssi -> ssi.getSoftwareSystem().equals(element) && ssi.getEnvironment().equals(view.getEnvironment())).forEach(view::remove); + } else if (element instanceof SoftwareSystemInstance) { + view.remove((SoftwareSystemInstance)element); + } else if (element instanceof Container) { + // find instances of this container + view.getModel().getElements().stream().filter(e -> e instanceof ContainerInstance).map(e -> (ContainerInstance)e).filter(ci -> ci.getContainer().equals(element) && ci.getEnvironment().equals(view.getEnvironment())).forEach(view::remove); + } else if (element instanceof ContainerInstance) { + view.remove((ContainerInstance)element); + } + } + + private void addRelationshipToView(Relationship relationship, DeploymentView view) { + if (view.isElementInView(relationship.getSource()) && view.isElementInView(relationship.getDestination())) { + view.add(relationship); + } else { + // we have a relationship, but the source and/or destination elements are not present in the view + // if both sides are static structure elements, then perhaps there's a replicated version of the relationship that should be added instead + Element sourceElement = relationship.getSource(); + Element destinationElement = relationship.getDestination(); + + if ((sourceElement instanceof SoftwareSystem || sourceElement instanceof Container) && (destinationElement instanceof SoftwareSystem || destinationElement instanceof Container)) { + String relationshipId = relationship.getId(); + + Set replicatedRelationships = view.getModel().getRelationships().stream().filter(r -> relationshipId.equals(r.getLinkedRelationshipId())).collect(Collectors.toSet()); + for (Relationship replicatedRelationship : replicatedRelationships) { + if (view.isElementInView(replicatedRelationship.getSource()) && view.isElementInView(replicatedRelationship.getDestination())) { + view.add(replicatedRelationship); + } + } + } + } + } + + @Override + protected void removeRelationshipFromView(Relationship relationship, ModelView view) { + // remove the specified relationship + view.remove(relationship); + + // and also remove any replicated versions of the specified relationship + Collection replicatedRelationshipsInView = view.getRelationships().stream().map(RelationshipView::getRelationship).filter(r -> r.getLinkedRelationshipId() != null && r.getLinkedRelationshipId().equals(relationship.getId())).collect(Collectors.toSet()); + for (Relationship r : replicatedRelationshipsInView) { + view.remove(r); + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentViewDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentViewDslContext.java new file mode 100644 index 000000000..7aca919f2 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentViewDslContext.java @@ -0,0 +1,29 @@ +package com.structurizr.dsl; + +import com.structurizr.view.DeploymentView; + +final class DeploymentViewDslContext extends ModelViewDslContext { + + DeploymentViewDslContext(DeploymentView view) { + super(view); + } + + DeploymentView getView() { + return (DeploymentView)super.getView(); + } + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.INCLUDE_IN_VIEW_TOKEN, + StructurizrDslTokens.EXCLUDE_IN_VIEW_TOKEN, + StructurizrDslTokens.AUTOLAYOUT_VIEW_TOKEN, + StructurizrDslTokens.DEFAULT_VIEW_TOKEN, + StructurizrDslTokens.ANIMATION_IN_VIEW_TOKEN, + StructurizrDslTokens.VIEW_TITLE_TOKEN, + StructurizrDslTokens.VIEW_DESCRIPTION_TOKEN, + StructurizrDslTokens.PROPERTIES_TOKEN + }; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentViewExpressionParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentViewExpressionParser.java new file mode 100644 index 000000000..1919888d4 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentViewExpressionParser.java @@ -0,0 +1,70 @@ +package com.structurizr.dsl; + +import com.structurizr.model.*; + +import java.util.LinkedHashSet; +import java.util.Set; + +import static com.structurizr.dsl.StructurizrDslExpressions.ELEMENT_TYPE_EQUALS_EXPRESSION; + +final class DeploymentViewExpressionParser extends AbstractExpressionParser { + + @Override + protected Set evaluateElementTypeExpression(String expr, DslContext context) { + Set elements = new LinkedHashSet<>(); + + String type = expr.substring(ELEMENT_TYPE_EQUALS_EXPRESSION.length()); + switch (type.toLowerCase()) { + case "custom": + context.getWorkspace().getModel().getElements().stream().filter(e -> e instanceof CustomElement).forEach(elements::add); + break; + case "deploymentnode": + context.getWorkspace().getModel().getElements().stream().filter(e -> e instanceof DeploymentNode).forEach(elements::add); + break; + case "infrastructurenode": + context.getWorkspace().getModel().getElements().stream().filter(e -> e instanceof InfrastructureNode).forEach(elements::add); + break; + case "softwaresystem": + context.getWorkspace().getModel().getElements().stream().filter(e -> e instanceof SoftwareSystem).forEach(elements::add); + break; + case "softwaresysteminstance": + context.getWorkspace().getModel().getElements().stream().filter(e -> e instanceof SoftwareSystemInstance).forEach(elements::add); + break; + case "container": + context.getWorkspace().getModel().getElements().stream().filter(e -> e instanceof Container).forEach(elements::add); + break; + case "containerinstance": + context.getWorkspace().getModel().getElements().stream().filter(e -> e instanceof ContainerInstance).forEach(elements::add); + break; + default: + throw new RuntimeException("The element type of \"" + type + "\" is not valid for this view"); + } + + return elements; + } + + protected Set findAfferentCouplings(Element element) { + Set elements = new LinkedHashSet<>(); + + elements.addAll(findAfferentCouplings(element, CustomElement.class)); + elements.addAll(findAfferentCouplings(element, DeploymentNode.class)); + elements.addAll(findAfferentCouplings(element, InfrastructureNode.class)); + elements.addAll(findAfferentCouplings(element, SoftwareSystemInstance.class)); + elements.addAll(findAfferentCouplings(element, ContainerInstance.class)); + + return elements; + } + + protected Set findEfferentCouplings(Element element) { + Set elements = new LinkedHashSet<>(); + + elements.addAll(findEfferentCouplings(element, CustomElement.class)); + elements.addAll(findEfferentCouplings(element, DeploymentNode.class)); + elements.addAll(findEfferentCouplings(element, InfrastructureNode.class)); + elements.addAll(findEfferentCouplings(element, SoftwareSystemInstance.class)); + elements.addAll(findEfferentCouplings(element, ContainerInstance.class)); + + return elements; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentViewParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentViewParser.java new file mode 100644 index 000000000..8794c680e --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentViewParser.java @@ -0,0 +1,85 @@ +package com.structurizr.dsl; + +import com.structurizr.Workspace; +import com.structurizr.model.Element; +import com.structurizr.model.SoftwareSystem; +import com.structurizr.view.DeploymentView; + +final class DeploymentViewParser extends AbstractViewParser { + + private static final String GRAMMAR = "deployment <*|software system identifier> [key] [description] {"; + + private static final String VIEW_TYPE = "Deployment"; + + private static final int SCOPE_IDENTIFIER_INDEX = 1; + private static final int ENVIRONMENT_INDEX = 2; + private static final int KEY_INDEX = 3; + private static final int DESCRIPTION_INDEX = 4; + + DeploymentView parse(DslContext context, Tokens tokens) { + // deployment <*|software system identifier> [key] [description] { + + if (tokens.hasMoreThan(DESCRIPTION_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } + + if (!tokens.includes(ENVIRONMENT_INDEX)) { + throw new RuntimeException("Expected: " + GRAMMAR); + } + + Workspace workspace = context.getWorkspace(); + String key = ""; + String scopeIdentifier = tokens.get(SCOPE_IDENTIFIER_INDEX); + String environment = tokens.get(ENVIRONMENT_INDEX); + if (context.getElement(environment) != null && context.getElement(environment) instanceof DeploymentEnvironment) { + environment = context.getElement(environment).getName(); + } + + // check that the deployment environment exists in the model + final String env = environment; + if (context.getWorkspace().getModel().getDeploymentNodes().stream().noneMatch(dn -> dn.getEnvironment().equals(env))) { + throw new RuntimeException("The environment \"" + environment + "\" does not exist"); + } + + String description = ""; + if (tokens.includes(DESCRIPTION_INDEX)) { + description = tokens.get(DESCRIPTION_INDEX); + } + + DeploymentView view; + + if ("*".equals(scopeIdentifier)) { + if (tokens.includes(KEY_INDEX)) { + key = tokens.get(KEY_INDEX); + } else { + key = workspace.getViews().generateViewKey(VIEW_TYPE); + } + validateViewKey(key); + + view = workspace.getViews().createDeploymentView(key, description); + } else { + Element element = context.getElement(scopeIdentifier); + if (element == null) { + throw new RuntimeException("The software system \"" + scopeIdentifier + "\" does not exist"); + } + + if (element instanceof SoftwareSystem) { + if (tokens.includes(KEY_INDEX)) { + key = tokens.get(KEY_INDEX); + } else { + key = workspace.getViews().generateViewKey(VIEW_TYPE); + } + validateViewKey(key); + + view = workspace.getViews().createDeploymentView((SoftwareSystem)element, key, description); + } else { + throw new RuntimeException("The element \"" + scopeIdentifier + "\" is not a software system"); + } + } + + view.setEnvironment(environment); + + return view; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DocsParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DocsParser.java new file mode 100644 index 000000000..4eaba7a23 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DocsParser.java @@ -0,0 +1,80 @@ +package com.structurizr.dsl; + +import com.structurizr.documentation.Documentable; +import com.structurizr.importer.documentation.DefaultImageImporter; +import com.structurizr.importer.documentation.DocumentationImporter; + +import java.io.File; +import java.lang.reflect.Constructor; + +final class DocsParser extends AbstractParser { + + private static final String DEFAULT_DOCUMENT_IMPORTER = "com.structurizr.importer.documentation.DefaultDocumentationImporter"; + + private static final String GRAMMAR = "!docs "; + + private static final int PATH_INDEX = 1; + private static final int FQN_INDEX = 2; + + void parse(WorkspaceDslContext context, File dslFile, Tokens tokens) { + parse(context, context.getWorkspace(), dslFile, tokens); + } + + void parse(SoftwareSystemDslContext context, File dslFile, Tokens tokens) { + parse(context, context.getSoftwareSystem(), dslFile, tokens); + } + + void parse(ContainerDslContext context, File dslFile, Tokens tokens) { + parse(context, context.getContainer(), dslFile, tokens); + } + + void parse(ComponentDslContext context, File dslFile, Tokens tokens) { + parse(context, context.getComponent(), dslFile, tokens); + } + + private void parse(DslContext context, Documentable documentable, File dslFile, Tokens tokens) { + // !docs + + if (tokens.hasMoreThan(FQN_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } + + if (!tokens.includes(PATH_INDEX)) { + throw new RuntimeException("Expected: " + GRAMMAR); + } + + String fullyQualifiedClassName = DEFAULT_DOCUMENT_IMPORTER; + if (tokens.includes(FQN_INDEX)) { + fullyQualifiedClassName = tokens.get(FQN_INDEX); + } + + if (dslFile != null) { + File path = new File(dslFile.getParentFile(), tokens.get(PATH_INDEX)); + + if (!path.exists()) { + throw new RuntimeException("Documentation path " + path + " does not exist"); + } + + if (!path.isDirectory()) { + throw new RuntimeException("Documentation path " + path + " is not a directory"); + } + + try { + Class documentationImporterClass = context.loadClass(fullyQualifiedClassName, dslFile); + Constructor constructor = documentationImporterClass.getDeclaredConstructor(); + DocumentationImporter documentationImporter = (DocumentationImporter)constructor.newInstance(); + documentationImporter.importDocumentation(documentable, path); + + if (!tokens.includes(FQN_INDEX)) { + DefaultImageImporter imageImporter = new DefaultImageImporter(); + imageImporter.importDocumentation(documentable, path); + } + } catch (ClassNotFoundException cnfe) { + throw new RuntimeException("Error importing documentation from " + path.getAbsolutePath() + ": " + fullyQualifiedClassName + " was not found"); + } catch (Exception e) { + throw new RuntimeException("Error importing documentation from " + path.getAbsolutePath() + ": " + e.getMessage()); + } + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DslContext.java new file mode 100644 index 000000000..ef529853c --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DslContext.java @@ -0,0 +1,126 @@ +package com.structurizr.dsl; + +import com.structurizr.Workspace; +import com.structurizr.model.Element; +import com.structurizr.model.Relationship; + +import java.io.File; +import java.net.URL; +import java.net.URLClassLoader; + +abstract class DslContext { + + private static final String PLUGINS_DIRECTORY_NAME = "plugins"; + + static final String CONTEXT_START_TOKEN = "{"; + static final String CONTEXT_END_TOKEN = "}"; + + private Workspace workspace; + private boolean extendingWorkspace; + + protected IdentifiersRegister identifiersRegister = new IdentifiersRegister(); + + Workspace getWorkspace() { + return workspace; + } + + void setWorkspace(Workspace workspace) { + this.workspace = workspace; + } + + boolean isExtendingWorkspace() { + return extendingWorkspace; + } + + void setExtendingWorkspace(boolean extendingWorkspace) { + this.extendingWorkspace = extendingWorkspace; + } + + void setIdentifierRegister(IdentifiersRegister identifersRegister) { + this.identifiersRegister = identifersRegister; + } + + Element getElement(String identifier) { + return getElement(identifier, null); + } + + Element getElement(String identifier, Class type) { + Element element = null; + identifier = identifier.toLowerCase(); + + if (identifiersRegister.getIdentifierScope() == IdentifierScope.Hierarchical) { + if (this instanceof ModelItemDslContext) { + ModelItemDslContext modelItemDslContext = (ModelItemDslContext)this; + if (modelItemDslContext.getModelItem() instanceof Element) { + Element parent = (Element)modelItemDslContext.getModelItem(); + while (parent != null && element == null) { + String parentIdentifier = identifiersRegister.findIdentifier(parent); + + element = identifiersRegister.getElement(parentIdentifier + "." + identifier); + parent = parent.getParent(); + + element = checkElementType(element, type); + } + } + } else if (this instanceof DeploymentEnvironmentDslContext) { + DeploymentEnvironmentDslContext deploymentEnvironmentDslContext = (DeploymentEnvironmentDslContext)this; + DeploymentEnvironment deploymentEnvironment = new DeploymentEnvironment(deploymentEnvironmentDslContext.getEnvironment()); + String parentIdentifier = identifiersRegister.findIdentifier(deploymentEnvironment); + + element = identifiersRegister.getElement(parentIdentifier + "." + identifier); + } + + if (element == null) { + // default to finding a top-level element + element = identifiersRegister.getElement(identifier); + } + } else { + element = identifiersRegister.getElement(identifier); + } + + element = checkElementType(element, type); + return element; + } + + Element checkElementType(Element element, Class type) { + if (element != null && type != null) { + if (!element.getClass().isAssignableFrom(type)) { + element = null; + } + } + + return element; + } + + Relationship getRelationship(String identifier) { + return identifiersRegister.getRelationship(identifier.toLowerCase()); + } + + protected Class loadClass(String fqn, File dslFile) throws Exception { + File pluginsDirectory = new File(dslFile.getParent(), PLUGINS_DIRECTORY_NAME); + URL[] urls = new URL[0]; + + if (pluginsDirectory.exists()) { + File[] jarFiles = pluginsDirectory.listFiles((dir, name) -> name.endsWith(".jar")); + if (jarFiles != null) { + urls = new URL[jarFiles.length]; + for (int i = 0; i < jarFiles.length; i++) { + try { + urls[i] = jarFiles[i].toURI().toURL(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + + URLClassLoader childClassLoader = new URLClassLoader(urls, getClass().getClassLoader()); + return childClassLoader.loadClass(fqn); + } + + void end() { + } + + protected abstract String[] getPermittedTokens(); + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DslLine.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DslLine.java new file mode 100644 index 000000000..6c8c99944 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DslLine.java @@ -0,0 +1,24 @@ +package com.structurizr.dsl; + +/** + * Represents a line of DSL, and its line number from the source file. + */ +class DslLine { + + private final String source; + private final int lineNumber; + + DslLine(String source, int lineNumber) { + this.source = source; + this.lineNumber = lineNumber; + } + + String getSource() { + return source; + } + + int getLineNumber() { + return lineNumber; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DslParserContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DslParserContext.java new file mode 100644 index 000000000..f7f7491d8 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DslParserContext.java @@ -0,0 +1,38 @@ +package com.structurizr.dsl; + +import java.io.File; + +final class DslParserContext extends DslContext { + + private boolean restricted; + private File file; + + DslParserContext(File file, boolean restricted) { + this.file = file; + this.restricted = restricted; + } + + File getFile() { + return file; + } + + boolean isRestricted() { + return restricted; + } + + void copyFrom(IdentifiersRegister identifersRegister) { + for (String identifier : identifersRegister.getElementIdentifiers()) { + this.identifiersRegister.register(identifier, identifersRegister.getElement(identifier)); + } + + for (String identifier : identifersRegister.getRelationshipIdentifiers()) { + this.identifiersRegister.register(identifier, identifersRegister.getRelationship(identifier)); + } + } + + @Override + protected String[] getPermittedTokens() { + return new String[0]; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DslUtils.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DslUtils.java new file mode 100644 index 000000000..16e73789e --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DslUtils.java @@ -0,0 +1,48 @@ +package com.structurizr.dsl; + +import com.structurizr.Workspace; +import com.structurizr.util.StringUtils; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +/** + * Utility methods to get/set DSL on a workspace. + */ +public class DslUtils { + + private static final String STRUCTURIZR_DSL_PROPERTY_NAME = "structurizr.dsl"; + + /** + * Gets the DSL associated with a workspace. + * + * @param workspace a Workspace object + * @return a DSL string + */ + public static String getDsl(Workspace workspace) { + String base64 = workspace.getProperties().get(STRUCTURIZR_DSL_PROPERTY_NAME); + String dsl = ""; + + if (!StringUtils.isNullOrEmpty(base64)) { + dsl = new String(Base64.getDecoder().decode(base64)); + } + + return dsl; + } + + /** + * Sets the DSL associated with a workspace. + * + * @param workspace a Workspace object + * @param dsl the DSL string + */ + public static void setDsl(Workspace workspace, String dsl) { + String base64 = ""; + if (!StringUtils.isNullOrEmpty(dsl)) { + base64 = Base64.getEncoder().encodeToString(dsl.getBytes(StandardCharsets.UTF_8)); + } + + workspace.addProperty(STRUCTURIZR_DSL_PROPERTY_NAME, base64); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DynamicViewContentParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DynamicViewContentParser.java new file mode 100644 index 000000000..e96f6babf --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DynamicViewContentParser.java @@ -0,0 +1,100 @@ +package com.structurizr.dsl; + +import com.structurizr.model.CustomElement; +import com.structurizr.model.Element; +import com.structurizr.model.Relationship; +import com.structurizr.model.StaticStructureElement; +import com.structurizr.view.DynamicView; +import com.structurizr.view.RelationshipView; + +final class DynamicViewContentParser extends AbstractParser { + + private static final String GRAMMAR_1 = " -> [description] [technology]"; + private static final String GRAMMAR_2 = " [description]"; + + private static final int SOURCE_IDENTIFIER_INDEX = 0; + private static final int RELATIONSHIP_TOKEN_INDEX = 1; + private static final int DESTINATION_IDENTIFIER_INDEX = 2; + private static final int DESCRIPTION_INDEX = 3; + private static final int TECHNOLOGY_INDEX = 4; + + private static final int RELATIONSHIP_IDENTIFIER_INDEX = 0; + + RelationshipView parseRelationship(DynamicViewDslContext context, Tokens tokens) { + DynamicView view = context.getView(); + + if (tokens.size() > 1 && StructurizrDslTokens.RELATIONSHIP_TOKEN.equals(tokens.get(RELATIONSHIP_TOKEN_INDEX))) { + // -> [description] [technology] + if (tokens.hasMoreThan(TECHNOLOGY_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR_1); + } + + if (!tokens.includes(DESTINATION_IDENTIFIER_INDEX)) { + throw new RuntimeException("Expected: " + GRAMMAR_1); + } + + String sourceId = tokens.get(SOURCE_IDENTIFIER_INDEX); + String destinationId = tokens.get(DESTINATION_IDENTIFIER_INDEX); + + Element sourceElement = context.getElement(sourceId); + if (sourceElement == null) { + throw new RuntimeException("The source element \"" + sourceId + "\" does not exist"); + } + + if (!(sourceElement instanceof StaticStructureElement || sourceElement instanceof CustomElement)) { + throw new RuntimeException("The source element \"" + sourceId + "\" should be a static structure or custom element"); + } + + Element destinationElement = context.getElement(destinationId); + if (destinationElement == null) { + throw new RuntimeException("The destination element \"" + destinationId + "\" does not exist"); + } + + if (!(destinationElement instanceof StaticStructureElement || destinationElement instanceof CustomElement)) { + throw new RuntimeException("The destination element \"" + destinationId + "\" should be a static structure or custom element"); + } + + String description = ""; + if (tokens.includes(DESCRIPTION_INDEX)) { + description = tokens.get(DESCRIPTION_INDEX); + } + + String technology = ""; + if (tokens.includes(TECHNOLOGY_INDEX)) { + technology = tokens.get(TECHNOLOGY_INDEX); + } + + if (sourceElement instanceof StaticStructureElement && destinationElement instanceof StaticStructureElement) { + return view.add((StaticStructureElement) sourceElement, description, technology, (StaticStructureElement) destinationElement); + } else if (sourceElement instanceof StaticStructureElement && destinationElement instanceof CustomElement) { + return view.add((StaticStructureElement) sourceElement, description, technology, (CustomElement) destinationElement); + } else if (sourceElement instanceof CustomElement && destinationElement instanceof StaticStructureElement) { + return view.add((CustomElement) sourceElement, description, technology, (StaticStructureElement) destinationElement); + } else if (sourceElement instanceof CustomElement && destinationElement instanceof CustomElement) { + return view.add((CustomElement) sourceElement, description, technology, (CustomElement) destinationElement); + } + } else { + // [description] [technology] + String relationshipId = tokens.get(RELATIONSHIP_IDENTIFIER_INDEX); + Relationship relationship = context.getRelationship(relationshipId); + + if (tokens.hasMoreThan(RELATIONSHIP_IDENTIFIER_INDEX+1)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR_2); + } + + if (relationship == null) { + throw new RuntimeException("The relationship \"" + relationshipId + "\" does not exist"); + } + + String description = ""; + if (tokens.includes(RELATIONSHIP_IDENTIFIER_INDEX+1)) { + description = tokens.get(RELATIONSHIP_IDENTIFIER_INDEX+1); + } + + return view.add(relationship, description); + } + + throw new RuntimeException("The specified relationship could not be added"); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DynamicViewDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DynamicViewDslContext.java new file mode 100644 index 000000000..6ec05d24e --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DynamicViewDslContext.java @@ -0,0 +1,27 @@ +package com.structurizr.dsl; + +import com.structurizr.view.DynamicView; + +class DynamicViewDslContext extends ModelViewDslContext { + + DynamicViewDslContext(DynamicView view) { + super(view); + } + + DynamicView getView() { + return (DynamicView)super.getView(); + } + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.AUTOLAYOUT_VIEW_TOKEN, + StructurizrDslTokens.DEFAULT_VIEW_TOKEN, + StructurizrDslTokens.VIEW_TITLE_TOKEN, + StructurizrDslTokens.VIEW_DESCRIPTION_TOKEN, + StructurizrDslTokens.PROPERTIES_TOKEN, + StructurizrDslTokens.RELATIONSHIP_TOKEN + }; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DynamicViewParallelSequenceDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DynamicViewParallelSequenceDslContext.java new file mode 100644 index 000000000..316bf07a4 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DynamicViewParallelSequenceDslContext.java @@ -0,0 +1,21 @@ +package com.structurizr.dsl; + +final class DynamicViewParallelSequenceDslContext extends DynamicViewDslContext { + + private boolean relationships = false; + + DynamicViewParallelSequenceDslContext(DynamicViewDslContext context) { + super(context.getView()); + getView().startParallelSequence(); + } + + void hasRelationships(boolean definesRelationships) { + this.relationships = definesRelationships; + } + + @Override + void end() { + getView().endParallelSequence(!relationships); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DynamicViewParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DynamicViewParser.java new file mode 100644 index 000000000..78b23103e --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DynamicViewParser.java @@ -0,0 +1,89 @@ +package com.structurizr.dsl; + +import com.structurizr.Workspace; +import com.structurizr.model.Container; +import com.structurizr.model.Element; +import com.structurizr.model.SoftwareSystem; +import com.structurizr.view.DynamicView; + +import java.text.DecimalFormat; + +class DynamicViewParser extends AbstractViewParser { + + private static final String GRAMMAR = "dynamic <*|software system identifier|container identifier> [key] [description] {"; + + private static final String VIEW_TYPE = "Dynamic"; + + private static final int SCOPE_IDENTIFIER_INDEX = 1; + private static final int KEY_INDEX = 2; + private static final int DESCRIPTION_INDEX = 3; + + private static final String WILDCARD = "*"; + + DynamicView parse(DslContext context, Tokens tokens) { + // dynamic <*|software system identifier|container identifier> [key] [description] { + + Workspace workspace = context.getWorkspace(); + String key = ""; + String description = ""; + + if (tokens.hasMoreThan(DESCRIPTION_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } + + if (!tokens.includes(SCOPE_IDENTIFIER_INDEX)) { + throw new RuntimeException("Expected: " + GRAMMAR); + } + + if (tokens.includes(DESCRIPTION_INDEX)) { + description = tokens.get(DESCRIPTION_INDEX); + } + + DynamicView view; + + String scopeIdentifier = tokens.get(SCOPE_IDENTIFIER_INDEX); + if (WILDCARD.equals(scopeIdentifier)) { + if (tokens.includes(KEY_INDEX)) { + key = tokens.get(KEY_INDEX); + } else { + key = workspace.getViews().generateViewKey(VIEW_TYPE); + } + validateViewKey(key); + + view = workspace.getViews().createDynamicView(key, description); + } else { + Element element = context.getElement(scopeIdentifier); + if (element == null) { + throw new RuntimeException("The software system or container \"" + scopeIdentifier + "\" does not exist"); + } + + if (element instanceof SoftwareSystem) { + if (tokens.includes(KEY_INDEX)) { + key = tokens.get(KEY_INDEX); + } else { + key = workspace.getViews().generateViewKey(VIEW_TYPE); + } + validateViewKey(key); + + view = workspace.getViews().createDynamicView((SoftwareSystem)element, key, description); + } else if (element instanceof Container) { + Container container = (Container)element; + if (tokens.includes(KEY_INDEX)) { + key = tokens.get(KEY_INDEX); + } else { + key = workspace.getViews().generateViewKey(VIEW_TYPE); + } + validateViewKey(key); + + view = workspace.getViews().createDynamicView((Container)element, key, description); + } else { + throw new RuntimeException("The element \"" + scopeIdentifier + "\" is not a software system or container"); + } + } + + view.setExternalBoundariesVisible(true); + + return view; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DynamicViewRelationshipContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DynamicViewRelationshipContext.java new file mode 100644 index 000000000..910ef6199 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DynamicViewRelationshipContext.java @@ -0,0 +1,26 @@ +package com.structurizr.dsl; + +import com.structurizr.view.RelationshipView; + +class DynamicViewRelationshipContext extends DslContext { + + private final RelationshipView relationshipView; + + DynamicViewRelationshipContext(RelationshipView relationshipView) { + super(); + + this.relationshipView = relationshipView; + } + + RelationshipView getRelationshipView() { + return relationshipView; + } + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.URL_TOKEN + }; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DynamicViewRelationshipParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DynamicViewRelationshipParser.java new file mode 100644 index 000000000..5562e4d39 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DynamicViewRelationshipParser.java @@ -0,0 +1,21 @@ +package com.structurizr.dsl; + +final class DynamicViewRelationshipParser extends AbstractParser { + + private final static int URL_INDEX = 1; + + void parseUrl(DynamicViewRelationshipContext context, Tokens tokens) { + // url + if (tokens.hasMoreThan(URL_INDEX)) { + throw new RuntimeException("Too many tokens, expected: url "); + } + + if (!tokens.includes(URL_INDEX)) { + throw new RuntimeException("Expected: url "); + } + + String url = tokens.get(URL_INDEX); + context.getRelationshipView().setUrl(url); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementGroup.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementGroup.java new file mode 100644 index 000000000..751dc1b0f --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementGroup.java @@ -0,0 +1,74 @@ +package com.structurizr.dsl; + +import com.structurizr.model.Element; +import com.structurizr.model.Model; +import com.structurizr.util.StringUtils; + +import java.util.HashSet; +import java.util.Set; + +class ElementGroup extends Element { + + private static final String STRUCTURIZR_GROUP_SEPARATOR_PROPERTY_NAME = "structurizr.groupSeparator"; + + private Element parent; + private final ElementGroup parentGroup; + private final String name; + + private final Set elements = new HashSet<>(); + + ElementGroup(Model model, String name) { + setModel(model); + this.name = name; + this.parentGroup = null; + } + + ElementGroup(Model model, String name, ElementGroup parentGroup) { + setModel(model); + String groupSeparator = getModel().getProperties().getOrDefault(STRUCTURIZR_GROUP_SEPARATOR_PROPERTY_NAME, ""); + + if (StringUtils.isNullOrEmpty(groupSeparator)) { + throw new RuntimeException("To use nested groups, please define a model property named " + STRUCTURIZR_GROUP_SEPARATOR_PROPERTY_NAME); + } + + this.name = parentGroup.getName() + groupSeparator + name; + this.parentGroup = parentGroup; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getCanonicalName() { + return name; + } + + void setParent(Element parent) { + this.parent = parent; + } + + @Override + public Element getParent() { + return parent; + } + + @Override + public Set getDefaultTags() { + return null; + } + + void addElement(Element element) { + elements.add(element); + + if (parentGroup != null) { + parentGroup.addElement(element); + } + } + + Set getElements() { + return new HashSet<>(elements); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementStyleDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementStyleDslContext.java new file mode 100644 index 000000000..1ab73cbe3 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementStyleDslContext.java @@ -0,0 +1,46 @@ +package com.structurizr.dsl; + +import com.structurizr.view.ElementStyle; + +import java.io.File; + +final class ElementStyleDslContext extends DslContext { + + private File file; + private ElementStyle style; + + ElementStyleDslContext(ElementStyle style, File file) { + this.style = style; + this.file = file; + } + + File getFile() { + return file; + } + + ElementStyle getStyle() { + return style; + } + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.ELEMENT_STYLE_SHAPE_TOKEN, + StructurizrDslTokens.ELEMENT_STYLE_ICON_TOKEN, + StructurizrDslTokens.ELEMENT_STYLE_WIDTH_TOKEN, + StructurizrDslTokens.ELEMENT_STYLE_HEIGHT_TOKEN, + StructurizrDslTokens.ELEMENT_STYLE_BACKGROUND_TOKEN, + StructurizrDslTokens.ELEMENT_STYLE_COLOR_TOKEN, + StructurizrDslTokens.ELEMENT_STYLE_COLOUR_TOKEN, + StructurizrDslTokens.ELEMENT_STYLE_STROKE_TOKEN, + StructurizrDslTokens.ELEMENT_STYLE_STROKE_WIDTH_TOKEN, + StructurizrDslTokens.ELEMENT_STYLE_FONT_SIZE_TOKEN, + StructurizrDslTokens.ELEMENT_STYLE_BORDER_TOKEN, + StructurizrDslTokens.ELEMENT_STYLE_OPACITY_TOKEN, + StructurizrDslTokens.ELEMENT_STYLE_METADATA_TOKEN, + StructurizrDslTokens.ELEMENT_STYLE_DESCRIPTION_TOKEN, + StructurizrDslTokens.PROPERTIES_TOKEN + }; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementStyleParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementStyleParser.java new file mode 100644 index 000000000..1769ec5c9 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementStyleParser.java @@ -0,0 +1,324 @@ +package com.structurizr.dsl; + +import com.structurizr.Workspace; +import com.structurizr.util.ImageUtils; +import com.structurizr.util.StringUtils; +import com.structurizr.view.Border; +import com.structurizr.view.ElementStyle; +import com.structurizr.view.Shape; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +final class ElementStyleParser extends AbstractParser { + + private static final int FIRST_PROPERTY_INDEX = 1; + + ElementStyle parseElementStyle(DslContext context, Tokens tokens) { + if (tokens.hasMoreThan(FIRST_PROPERTY_INDEX)) { + throw new RuntimeException("Too many tokens, expected: element {"); + } else if (tokens.includes(FIRST_PROPERTY_INDEX)) { + String tag = tokens.get(1); + + if (StringUtils.isNullOrEmpty(tag)) { + throw new RuntimeException("A tag must be specified"); + } + + Workspace workspace = context.getWorkspace(); + ElementStyle elementStyle = workspace.getViews().getConfiguration().getStyles().getElementStyle(tag); + if (elementStyle == null) { + elementStyle = workspace.getViews().getConfiguration().getStyles().addElementStyle(tag); + } + + return elementStyle; + } else { + throw new RuntimeException("Expected: element {"); + } + } + + void parseShape(ElementStyleDslContext context, Tokens tokens) { + Map shapes = new HashMap<>(); + String shapesAsString = ""; + for (Shape shape : Shape.values()) { + shapes.put(shape.toString().toLowerCase(), shape); + shapesAsString += shape; + shapesAsString += "|"; + } + shapesAsString = shapesAsString.substring(0, shapesAsString.length()-1); + + ElementStyle style = context.getStyle(); + + if (tokens.hasMoreThan(FIRST_PROPERTY_INDEX)) { + throw new RuntimeException("Too many tokens, expected: shape <" + shapesAsString + ">"); + } + + if (tokens.includes(FIRST_PROPERTY_INDEX)) { + String shape = tokens.get(1).toLowerCase(); + + if (shapes.containsKey(shape)) { + style.setShape(shapes.get(shape)); + } else { + throw new RuntimeException("The shape \"" + shape + "\" is not valid"); + } + } else { + throw new RuntimeException("Expected: shape <" + shapesAsString + ">"); + } + } + + void parseBackground(ElementStyleDslContext context, Tokens tokens) { + ElementStyle style = context.getStyle(); + + if (tokens.hasMoreThan(FIRST_PROPERTY_INDEX)) { + throw new RuntimeException("Too many tokens, expected: background <#rrggbb|color name>"); + } + + if (tokens.includes(FIRST_PROPERTY_INDEX)) { + String colour = tokens.get(1); + style.setBackground(colour); + } else { + throw new RuntimeException("Expected: background <#rrggbb|color name>"); + } + } + + void parseStroke(ElementStyleDslContext context, Tokens tokens) { + ElementStyle style = context.getStyle(); + + if (tokens.hasMoreThan(FIRST_PROPERTY_INDEX)) { + throw new RuntimeException("Too many tokens, expected: stroke <#rrggbb|color name>"); + } + + if (tokens.includes(FIRST_PROPERTY_INDEX)) { + String colour = tokens.get(1); + style.setStroke(colour); + } else { + throw new RuntimeException("Expected: stroke <#rrggbb|color name>"); + } + } + + void parseStrokeWidth(ElementStyleDslContext context, Tokens tokens) { + ElementStyle style = context.getStyle(); + + if (tokens.hasMoreThan(FIRST_PROPERTY_INDEX)) { + throw new RuntimeException("Too many tokens, expected: strokeWidth <1-10>"); + } + + if (tokens.includes(FIRST_PROPERTY_INDEX)) { + String strokeWidthAsString = tokens.get(1); + + try { + int strokeWidth = Integer.parseInt(strokeWidthAsString); + style.setStrokeWidth(strokeWidth); + } catch (NumberFormatException e) { + throw new RuntimeException("Stroke width must be an integer between 1 and 10"); + } + } else { + throw new RuntimeException("Expected: strokeWidth <1-10>"); + } + } + + void parseColour(ElementStyleDslContext context, Tokens tokens) { + ElementStyle style = context.getStyle(); + + if (tokens.hasMoreThan(FIRST_PROPERTY_INDEX)) { + throw new RuntimeException("Too many tokens, expected: colour <#rrggbb|color name>"); + } + + if (tokens.includes(FIRST_PROPERTY_INDEX)) { + String colour = tokens.get(1); + style.setColor(colour); + } else { + throw new RuntimeException("Expected: colour <#rrggbb|color name>"); + } + } + + void parseBorder(ElementStyleDslContext context, Tokens tokens) { + Map borders = new HashMap<>(); + for (Border border : Border.values()) { + borders.put(border.toString().toLowerCase(), border); + } + + ElementStyle style = context.getStyle(); + + if (tokens.hasMoreThan(FIRST_PROPERTY_INDEX)) { + throw new RuntimeException("Too many tokens, expected: border "); + } + + if (tokens.includes(FIRST_PROPERTY_INDEX)) { + String border = tokens.get(1).toLowerCase(); + + if (borders.containsKey(border)) { + style.setBorder(borders.get(border)); + } else { + throw new RuntimeException("The border \"" + border + "\" is not valid"); + } + } else { + throw new RuntimeException("Expected: border "); + } + } + + void parseOpacity(ElementStyleDslContext context, Tokens tokens) { + ElementStyle style = context.getStyle(); + + if (tokens.hasMoreThan(FIRST_PROPERTY_INDEX)) { + throw new RuntimeException("Too many tokens, expected: opacity <0-100>"); + } + + if (tokens.includes(FIRST_PROPERTY_INDEX)) { + String opacityAsString = tokens.get(1); + + try { + int opacity = Integer.parseInt(opacityAsString); + style.setOpacity(opacity); + } catch (NumberFormatException e) { + throw new RuntimeException("Opacity must be an integer between 0 and 100"); + } + } else { + throw new RuntimeException("Expected: opacity <0-100>"); + } + } + + void parseWidth(ElementStyleDslContext context, Tokens tokens) { + ElementStyle style = context.getStyle(); + + if (tokens.hasMoreThan(FIRST_PROPERTY_INDEX)) { + throw new RuntimeException("Too many tokens, expected: width "); + } + + if (tokens.includes(FIRST_PROPERTY_INDEX)) { + String widthAsString = tokens.get(1); + + try { + int width = Integer.parseInt(widthAsString); + style.setWidth(width); + } catch (RuntimeException e) { + throw new IllegalArgumentException("Width must be a positive integer"); + } + } else { + throw new RuntimeException("Expected: width "); + } + } + + void parseHeight(ElementStyleDslContext context, Tokens tokens) { + ElementStyle style = context.getStyle(); + + if (tokens.hasMoreThan(FIRST_PROPERTY_INDEX)) { + throw new RuntimeException("Too many tokens, expected: height "); + } + + if (tokens.includes(FIRST_PROPERTY_INDEX)) { + String heightAsString = tokens.get(1); + + try { + int height = Integer.parseInt(heightAsString); + style.setHeight(height); + } catch (NumberFormatException e) { + throw new RuntimeException("Height must be a positive integer"); + } + } else { + throw new RuntimeException("Expected: height "); + } + } + + void parseFontSize(ElementStyleDslContext context, Tokens tokens) { + ElementStyle style = context.getStyle(); + + if (tokens.hasMoreThan(FIRST_PROPERTY_INDEX)) { + throw new RuntimeException("Too many tokens, expected: fontSize "); + } + + if (tokens.includes(FIRST_PROPERTY_INDEX)) { + String fontSizeAsString = tokens.get(1); + + try { + int fontSize = Integer.parseInt(fontSizeAsString); + style.setFontSize(fontSize); + } catch (NumberFormatException e) { + throw new RuntimeException("Font size must be a positive integer"); + } + } else { + throw new RuntimeException("Expected: fontSize "); + } + } + + void parseMetadata(ElementStyleDslContext context, Tokens tokens) { + ElementStyle style = context.getStyle(); + + if (tokens.hasMoreThan(FIRST_PROPERTY_INDEX)) { + throw new RuntimeException("Too many tokens, expected: metadata "); + } + + if (tokens.includes(FIRST_PROPERTY_INDEX)) { + String metadata = tokens.get(1); + + if ("true".equalsIgnoreCase(metadata)) { + style.setMetadata(true); + } else if ("false".equalsIgnoreCase(metadata)) { + style.setMetadata(false); + } else { + throw new RuntimeException("Metadata must be true or false"); + } + } else { + throw new RuntimeException("Expected: metadata "); + } + } + + void parseDescription(ElementStyleDslContext context, Tokens tokens) { + ElementStyle style = context.getStyle(); + + if (tokens.hasMoreThan(FIRST_PROPERTY_INDEX)) { + throw new RuntimeException("Too many tokens, expected: description "); + } + + if (tokens.includes(FIRST_PROPERTY_INDEX)) { + String description = tokens.get(1); + + if ("true".equalsIgnoreCase(description)) { + style.setDescription(true); + } else if ("false".equalsIgnoreCase(description)) { + style.setDescription(false); + } else { + throw new RuntimeException("Description must be true or false"); + } + } else { + throw new RuntimeException("Expected: description "); + } + } + + void parseIcon(ElementStyleDslContext context, Tokens tokens, boolean restricted) { + ElementStyle style = context.getStyle(); + + if (tokens.hasMoreThan(FIRST_PROPERTY_INDEX)) { + throw new RuntimeException("Too many tokens, expected: icon "); + } + + if (tokens.includes(FIRST_PROPERTY_INDEX)) { + String path = tokens.get(1); + + if (path.startsWith("data:image/") || path.startsWith("https://") || path.startsWith("http://")) { + if (IconUtils.isSupported(path)) { + style.setIcon(path); + } else { + throw new IllegalArgumentException("Only PNG and JPG URLs/data URIs are supported: " + path); + } + } else { + if (!restricted) { + File file = new File(context.getFile().getParent(), path); + if (file.exists() && !file.isDirectory()) { + try { + style.setIcon(ImageUtils.getImageAsDataUri(file)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } else { + throw new RuntimeException(path + " does not exist"); + } + } + } + } else { + throw new RuntimeException("Expected: icon "); + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/EnterpriseDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/EnterpriseDslContext.java new file mode 100644 index 000000000..3fe12598e --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/EnterpriseDslContext.java @@ -0,0 +1,23 @@ +package com.structurizr.dsl; + +final class EnterpriseDslContext extends GroupableDslContext { + + EnterpriseDslContext() { + super(); + } + + EnterpriseDslContext(ElementGroup group) { + super(group); + } + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.GROUP_TOKEN, + StructurizrDslTokens.PERSON_TOKEN, + StructurizrDslTokens.SOFTWARE_SYSTEM_TOKEN, + StructurizrDslTokens.RELATIONSHIP_TOKEN + }; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/EnterpriseParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/EnterpriseParser.java new file mode 100644 index 000000000..54cbd3bfb --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/EnterpriseParser.java @@ -0,0 +1,30 @@ +package com.structurizr.dsl; + +import com.structurizr.Workspace; +import com.structurizr.model.Enterprise; + +final class EnterpriseParser extends AbstractParser { + + private static final String GRAMMAR = "enterprise [name]"; + + private static final int NAME_INDEX = 1; + + void parse(DslContext context, Tokens tokens) { + Workspace workspace = context.getWorkspace(); + + if (tokens.hasMoreThan(NAME_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } else if (tokens.includes(NAME_INDEX)) { + String name = tokens.get(1); + + if (workspace.getModel().getEnterprise() == null) { + workspace.getModel().setEnterprise(new Enterprise(name)); + } else if (!name.equals(workspace.getModel().getEnterprise().getName())) { + throw new RuntimeException("The name of the enterprise has already been set"); + } + } else { + // do nothing ... this will just create an EnterpriseDslContext, so that people and software systems can be marked as "internal" + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ExplicitRelationshipParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ExplicitRelationshipParser.java new file mode 100644 index 000000000..055167d93 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ExplicitRelationshipParser.java @@ -0,0 +1,72 @@ +package com.structurizr.dsl; + +import com.structurizr.model.*; + +final class ExplicitRelationshipParser extends AbstractRelationshipParser { + + private static final String GRAMMAR = " -> [description] [technology] [tags]"; + + private static final int SOURCE_IDENTIFIER_INDEX = 0; + private static final int DESTINATION_IDENTIFIER_INDEX = 2; + private final static int DESCRIPTION_INDEX = 3; + private final static int TECHNOLOGY_INDEX = 4; + private final static int TAGS_INDEX = 5; + + Relationship parse(DslContext context, Tokens tokens) { + // -> [description] [technology] [tags] + + if (tokens.hasMoreThan(TAGS_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } + + if (!tokens.includes(DESTINATION_IDENTIFIER_INDEX)) { + throw new RuntimeException("Expected: " + GRAMMAR); + } + + String sourceId = tokens.get(SOURCE_IDENTIFIER_INDEX); + String destinationId = tokens.get(DESTINATION_IDENTIFIER_INDEX); + + Element sourceElement = context.getElement(sourceId); + Element destinationElement = context.getElement(destinationId); + + if (sourceElement == null) { + if (StructurizrDslTokens.THIS_TOKEN.equalsIgnoreCase(sourceId) && context instanceof GroupableElementDslContext) { + GroupableElementDslContext groupableElementDslContext = (GroupableElementDslContext)context; + sourceElement = groupableElementDslContext.getElement(); + } else { + throw new RuntimeException("The source element \"" + sourceId + "\" does not exist"); + } + } + + if (destinationElement == null) { + if (StructurizrDslTokens.THIS_TOKEN.equalsIgnoreCase(destinationId) && context instanceof ModelItemDslContext) { + ModelItemDslContext modelItemDslContext = (ModelItemDslContext) context; + if (modelItemDslContext.getModelItem() instanceof Element) { + destinationElement = (Element)modelItemDslContext.getModelItem(); + } + } + } + + if (destinationElement == null) { + throw new RuntimeException("The destination element \"" + destinationId + "\" does not exist"); + } + + String description = ""; + if (tokens.includes(DESCRIPTION_INDEX)) { + description = tokens.get(DESCRIPTION_INDEX); + } + + String technology = ""; + if (tokens.includes(TECHNOLOGY_INDEX)) { + technology = tokens.get(TECHNOLOGY_INDEX); + } + + String[] tags = new String[0]; + if (tokens.includes(TAGS_INDEX)) { + tags = tokens.get(TAGS_INDEX).split(","); + } + + return createRelationship(sourceElement, description, technology, tags, destinationElement); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ExternalScriptDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ExternalScriptDslContext.java new file mode 100644 index 000000000..53305c12f --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ExternalScriptDslContext.java @@ -0,0 +1,41 @@ +package com.structurizr.dsl; + +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.List; + +class ExternalScriptDslContext extends ScriptDslContext { + + private final String filename; + + ExternalScriptDslContext(DslContext parentContext, File dslFile, String filename) { + super(parentContext, dslFile); + + this.filename = filename; + } + + @Override + void end() { + try { + File scriptFile = new File(dslFile.getParent(), filename); + if (!scriptFile.exists()) { + throw new RuntimeException("Script file " + scriptFile.getCanonicalPath() + " does not exist"); + } + + String fileExtension = filename.substring(filename.lastIndexOf('.') + 1); + List lines = Files.readAllLines(scriptFile.toPath(), StandardCharsets.UTF_8); + + run(this, fileExtension, lines); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Error running script at " + filename + ", caused by " + e.getClass().getName() + ": " + e.getMessage()); + } + } + + @Override + protected String[] getPermittedTokens() { + return new String[0]; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/FileUtils.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/FileUtils.java new file mode 100644 index 000000000..4f1279c62 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/FileUtils.java @@ -0,0 +1,46 @@ +package com.structurizr.dsl; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +final class FileUtils { + + private static final String STRUCTURIZR_DSL_FILE_EXTENSION = ".dsl"; + + static List findFiles(File path) { + List files = new ArrayList<>(); + if (path.isDirectory()) { + files = findFilesInDirectory(path); + } else { + files.add(path); + } + + return files; + } + + private static List findFilesInDirectory(File directory) { + List files = new ArrayList<>(); + + File[] filesInDirectory = directory.listFiles(); + if (filesInDirectory == null || filesInDirectory.length == 0) { + return files; + } + + Arrays.sort(filesInDirectory); + + for (File file : filesInDirectory) { + if (!file.isDirectory() && file.getName().endsWith(STRUCTURIZR_DSL_FILE_EXTENSION)) { + files.add(file); + } + + if (file.isDirectory()) { + files.addAll(findFilesInDirectory(file)); + } + } + + return files; + } + +} diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/FilteredViewDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/FilteredViewDslContext.java new file mode 100644 index 000000000..d385e6bf0 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/FilteredViewDslContext.java @@ -0,0 +1,25 @@ +package com.structurizr.dsl; + +import com.structurizr.view.FilteredView; + +class FilteredViewDslContext extends ViewDslContext { + + FilteredViewDslContext(FilteredView view) { + super(view); + } + + FilteredView getView() { + return (FilteredView)super.getView(); + } + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.DEFAULT_VIEW_TOKEN, + StructurizrDslTokens.VIEW_TITLE_TOKEN, + StructurizrDslTokens.VIEW_DESCRIPTION_TOKEN, + StructurizrDslTokens.PROPERTIES_TOKEN + }; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/FilteredViewParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/FilteredViewParser.java new file mode 100644 index 000000000..f90dd9de0 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/FilteredViewParser.java @@ -0,0 +1,88 @@ +package com.structurizr.dsl; + +import com.structurizr.Workspace; +import com.structurizr.util.StringUtils; +import com.structurizr.view.FilterMode; +import com.structurizr.view.FilteredView; +import com.structurizr.view.StaticView; + +import java.text.DecimalFormat; +import java.util.HashSet; +import java.util.Set; + +final class FilteredViewParser extends AbstractViewParser { + + private static final String GRAMMAR = "filtered [key] [description]"; + + private static final String VIEW_TYPE = "Filtered"; + + private static final int BASE_KEY_INDEX = 1; + private static final int MODE_INDEX = 2; + private static final int TAGS_INDEX = 3; + private static final int KEY_INDEX = 4; + private static final int DESCRIPTION_INDEX = 5; + + private static final String FILTER_MODE_INCLUDE = "include"; + private static final String FILTER_MODE_EXCLUDE = "exclude"; + + FilteredView parse(DslContext context, Tokens tokens) { + // filtered [key} [description] + + if (tokens.hasMoreThan(DESCRIPTION_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } + + if (!tokens.includes(TAGS_INDEX)) { + throw new RuntimeException("Expected: " + GRAMMAR); + } + + Workspace workspace = context.getWorkspace(); + String key; + + StaticView baseView; + String baseKey = tokens.get(BASE_KEY_INDEX); + String mode = tokens.get(MODE_INDEX); + String tagsAsString = tokens.get(TAGS_INDEX); + Set tags = new HashSet<>(); + + for (String tag : tagsAsString.split(",")) { + if (!StringUtils.isNullOrEmpty(tag)) { + tags.add(tag.trim()); + } + } + + String description = ""; + + if (tokens.includes(DESCRIPTION_INDEX)) { + description = tokens.get(DESCRIPTION_INDEX); + } + + FilterMode filterMode; + if (FILTER_MODE_INCLUDE.equalsIgnoreCase(mode)) { + filterMode = FilterMode.Include; + } else if (FILTER_MODE_EXCLUDE.equalsIgnoreCase(mode)) { + filterMode = FilterMode.Exclude; + } else { + throw new RuntimeException("Filter mode should be include or exclude"); + } + + if (workspace.getViews().getViews().stream().noneMatch(v -> v.getKey().equals(baseKey))) { + throw new RuntimeException("The view \"" + baseKey + "\" does not exist"); + } + + baseView = (StaticView)workspace.getViews().getViews().stream().filter(v -> v instanceof StaticView && v.getKey().equals(baseKey)).findFirst().orElse(null); + if (baseView == null) { + throw new RuntimeException("The view \"" + baseKey + "\" must be a System Landscape, System Context, Container, or Component view"); + } + + if (tokens.includes(KEY_INDEX)) { + key = tokens.get(KEY_INDEX); + } else { + key = workspace.getViews().generateViewKey(VIEW_TYPE); + } + validateViewKey(key); + + return workspace.getViews().createFilteredView(baseView, key, description, filterMode, tags.toArray(new String[0])); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/GroupParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/GroupParser.java new file mode 100644 index 000000000..8ff6e0fa9 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/GroupParser.java @@ -0,0 +1,35 @@ +package com.structurizr.dsl; + +class GroupParser { + + private static final String GRAMMAR = "group {"; + + private final static int NAME_INDEX = 1; + private final static int BRACE_INDEX = 2; + + ElementGroup parse(GroupableDslContext dslContext, Tokens tokens) { + // group { + + if (tokens.hasMoreThan(BRACE_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } + + if (!tokens.includes(BRACE_INDEX)) { + throw new RuntimeException("Expected: " + GRAMMAR); + } + + if (!DslContext.CONTEXT_START_TOKEN.equalsIgnoreCase(tokens.get(BRACE_INDEX))) { + throw new RuntimeException("Expected: " + GRAMMAR); + } + + ElementGroup group; + if (dslContext.hasGroup()) { + group = new ElementGroup(dslContext.getWorkspace().getModel(), tokens.get(NAME_INDEX), dslContext.getGroup()); + } else { + group = new ElementGroup(dslContext.getWorkspace().getModel(), tokens.get(NAME_INDEX)); + } + + return group; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/GroupableDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/GroupableDslContext.java new file mode 100644 index 000000000..5ac594434 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/GroupableDslContext.java @@ -0,0 +1,23 @@ +package com.structurizr.dsl; + +abstract class GroupableDslContext extends DslContext { + + private ElementGroup group; + + GroupableDslContext() { + this(null); + } + + GroupableDslContext(ElementGroup group) { + this.group = group; + } + + boolean hasGroup() { + return group != null; + } + + ElementGroup getGroup() { + return group; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/GroupableElementDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/GroupableElementDslContext.java new file mode 100644 index 000000000..a26b58cd6 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/GroupableElementDslContext.java @@ -0,0 +1,17 @@ +package com.structurizr.dsl; + +import com.structurizr.model.GroupableElement; + +abstract class GroupableElementDslContext extends ModelItemDslContext { + + GroupableElementDslContext() { + super(); + } + + GroupableElementDslContext(ElementGroup group) { + super(group); + } + + abstract GroupableElement getElement(); + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/HealthCheckParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/HealthCheckParser.java new file mode 100644 index 000000000..6cc6ab9a1 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/HealthCheckParser.java @@ -0,0 +1,61 @@ +package com.structurizr.dsl; + +import com.structurizr.model.StaticStructureElementInstance; + +class HealthCheckParser extends AbstractParser { + + private static final String GRAMMAR = "healthCheck [interval] [timeout]"; + + private final static int NAME_INDEX = 1; + private final static int URL_INDEX = 2; + private final static int INTERVAL_INDEX = 3; + private final static int TIMEOUT_INDEX = 4; + + private final static int DEFAULT_INTERVAL = 60; + private final static long DEFAULT_TIMEOUT = 0; + + void parse(StaticStructureElementInstanceDslContext context, Tokens tokens) { + // healthCheck [interval] [timeout] + + if (tokens.hasMoreThan(TIMEOUT_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } + + if (!tokens.includes(URL_INDEX)) { + throw new RuntimeException("Expected: " + GRAMMAR); + } + + String name = tokens.get(NAME_INDEX); + String url = tokens.get(URL_INDEX); + int interval = DEFAULT_INTERVAL; + long timeout = DEFAULT_TIMEOUT; + + if (tokens.includes(INTERVAL_INDEX)) { + try { + interval = Integer.parseInt(tokens.get(INTERVAL_INDEX)); + + if (interval < 1) { + throw new RuntimeException("The interval must be a positive integer (number of seconds)"); + } + } catch (NumberFormatException e) { + throw new RuntimeException("The interval of \"" + tokens.get(INTERVAL_INDEX) + "\" is not valid - it must be a positive integer (number of seconds)"); + } + } + + if (tokens.includes(TIMEOUT_INDEX)) { + try { + timeout = Integer.parseInt(tokens.get(TIMEOUT_INDEX)); + + if (timeout < 0) { + throw new RuntimeException("The timeout must be zero or a positive integer (number of milliseconds)"); + } + } catch (NumberFormatException e) { + throw new RuntimeException("The timeout of \"" + tokens.get(TIMEOUT_INDEX) + "\" is not valid - it must be zero or a positive integer (number of milliseconds)"); + } + } + + StaticStructureElementInstance elementInstance = context.getElementInstance(); + elementInstance.addHealthCheck(name, url, interval, timeout); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/IconUtils.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/IconUtils.java new file mode 100644 index 000000000..b503c0d5f --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/IconUtils.java @@ -0,0 +1,38 @@ +package com.structurizr.dsl; + +import com.structurizr.util.Url; + +class IconUtils { + + public static boolean isSupported(String url) { + url = url.trim(); + + if (Url.isUrl(url) && isSupportedUrl(url)) { + // all good + return true; + } + + if (url.startsWith("data:image")) { + if (isSupportedDataUri(url)) { + // all good + return true; + } else { + // it's a data URI, but not supported + return false; + } + } + + return false; + } + + private static boolean isSupportedDataUri(String uri) { + return uri.startsWith("data:image/png;base64,") || uri.startsWith("data:image/jpeg;base64,"); + } + + private static boolean isSupportedUrl(String url) { + url = url.toLowerCase(); + + return url.endsWith(".png") || url.endsWith(".jpg") || url.endsWith(".jpeg"); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/IdentifierScope.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/IdentifierScope.java new file mode 100644 index 000000000..1596bcc4b --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/IdentifierScope.java @@ -0,0 +1,8 @@ +package com.structurizr.dsl; + +enum IdentifierScope { + + Flat, + Hierarchical, + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/IdentifierScopeParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/IdentifierScopeParser.java new file mode 100644 index 000000000..93599f71e --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/IdentifierScopeParser.java @@ -0,0 +1,30 @@ +package com.structurizr.dsl; + +final class IdentifierScopeParser extends AbstractParser { + + private static final String GRAMMAR = "!identifiers "; + + private static final int MODE_INDEX = 1; + + IdentifierScope parse(DslContext context, Tokens tokens) { + // !identifiers + + if (tokens.hasMoreThan(MODE_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } + + if (!tokens.includes(MODE_INDEX)) { + throw new RuntimeException("Expected: " + GRAMMAR); + } + + String name = tokens.get(MODE_INDEX); + if ("flat".equalsIgnoreCase(name)) { + return IdentifierScope.Flat; + } else if ("hierarchical".equalsIgnoreCase(name)) { + return IdentifierScope.Hierarchical; + } else { + throw new RuntimeException("Expected: " + GRAMMAR); + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/IdentifiersRegister.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/IdentifiersRegister.java new file mode 100644 index 000000000..1d5524552 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/IdentifiersRegister.java @@ -0,0 +1,222 @@ +package com.structurizr.dsl; + +import com.structurizr.model.DeploymentNode; +import com.structurizr.model.Element; +import com.structurizr.model.Relationship; +import com.structurizr.util.StringUtils; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.regex.Pattern; + +/** + * A register of elements and relationships that were created with an identifier in the DSL. + */ +public class IdentifiersRegister { + + private static final Pattern IDENTIFIER_PATTERN = Pattern.compile("\\w[a-zA-Z0-9_-]*"); + + private IdentifierScope identifierScope = IdentifierScope.Flat; + + private final Map elementsByIdentifier = new HashMap<>(); + + private final Map relationshipsByIdentifier = new HashMap<>(); + + IdentifiersRegister() { + } + + /** + * Gets the identifier scope in use (i.e. Flat or Hierarchical ... applies to elements only). + * + * @return an IdentifierScope enum + */ + public IdentifierScope getIdentifierScope() { + return identifierScope; + } + + /** + * Sets the identifier scope (i.e. Flat or Hierarchical ... applies to elements only). + * + * @param identifierScope an IdentifierScope enum + */ + public void setIdentifierScope(IdentifierScope identifierScope) { + this.identifierScope = identifierScope; + } + + /** + * Gets the set of element identifiers. + * + * @return a Set of String identifiers + */ + public Set getElementIdentifiers() { + return elementsByIdentifier.keySet(); + } + + /** + * Gets the set of relationship identifiers. + * + * @return a Set of String identifiers + */ + public Set getRelationshipIdentifiers() { + return relationshipsByIdentifier.keySet(); + } + + /** + * Gets the element identified by the specified identifier. + * + * @param identifier a String identifier + * @return an Element, or null if one doesn't exist + */ + public Element getElement(String identifier) { + identifier = identifier.toLowerCase(); + return elementsByIdentifier.get(identifier); + } + + /** + * Registers an element with the given identifier. + * + * @param identifier an identifier + * @param element an Element instance + */ + public void register(String identifier, Element element) { + if (element == null) { + throw new IllegalArgumentException("An element must be specified"); + } + + if (StringUtils.isNullOrEmpty(identifier)) { + identifier = UUID.randomUUID().toString(); + } + + identifier = identifier.toLowerCase(); + + if (identifierScope == IdentifierScope.Hierarchical) { + identifier = calculateHierarchicalIdentifier(identifier, element); + } + + // check whether this element has already been registered with another identifier + for (String id : elementsByIdentifier.keySet()) { + Element e = elementsByIdentifier.get(id); + + if (e.equals(element) && !id.equals(identifier)) { + if (id.matches("[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}")) { + throw new RuntimeException("Please assign an identifier to \"" + element.getCanonicalName() + "\" before using it with !ref"); + } else { + throw new RuntimeException("The element is already registered with an identifier of \"" + id + "\""); + } + } + } + + Element e = elementsByIdentifier.get(identifier); + Relationship r = relationshipsByIdentifier.get(identifier); + + if ((e == null && r == null) || (e == element)) { + elementsByIdentifier.put(identifier, element); + } else { + throw new RuntimeException("The identifier \"" + identifier + "\" is already in use"); + } + } + + /** + * Gets the relationship identified by the specified identifier. + * + * @param identifier a String identifier + * @return a Relationship, or null if one doesn't exist + */ + public Relationship getRelationship(String identifier) { + identifier = identifier.toLowerCase(); + return relationshipsByIdentifier.get(identifier); + } + + /** + * Registers a relationship with the given identifier. + * + * @param identifier an identifier + * @param relationship a Relationship instance + */ + public void register(String identifier, Relationship relationship) { + if (relationship == null) { + throw new IllegalArgumentException("A relationship must be specified"); + } + + if (StringUtils.isNullOrEmpty(identifier)) { + identifier = UUID.randomUUID().toString(); + } + + identifier = identifier.toLowerCase(); + + Element e = elementsByIdentifier.get(identifier); + Relationship r = relationshipsByIdentifier.get(identifier); + + if ((e == null && r == null) || (r == relationship)) { + relationshipsByIdentifier.put(identifier, relationship); + } else { + throw new RuntimeException("The identifier \"" + identifier + "\" is already in use"); + } + } + + private String calculateHierarchicalIdentifier(String identifier, Element element) { + if (element.getParent() == null) { + if (element instanceof DeploymentNode) { + DeploymentNode dn = (DeploymentNode)element; + return findIdentifier(new DeploymentEnvironment(dn.getEnvironment())) + "." + identifier; + } else { + return identifier; + } + } else { + return findIdentifier(element.getParent()) + "." + identifier; + } + } + + /** + * Finds the identifier used when defining an element. + * + * @param element an Element instance + * @return a String identifier (could be null if no identifier was explicitly specified) + */ + public String findIdentifier(Element element) { + if (elementsByIdentifier.containsValue(element)) { + for (String identifier : elementsByIdentifier.keySet()) { + Element e = elementsByIdentifier.get(identifier); + + if (e.equals(element)) { + return identifier; + } + } + } + + return null; + } + + /** + * Finds the identifier used when defining a relationship. + * + * @param relationship a Relationship instance + * @return a String identifier (could be null if no identifier was explicitly specified, or for implied relationships) + */ + public String findIdentifier(Relationship relationship) { + if (relationshipsByIdentifier.containsValue(relationship)) { + for (String identifier : relationshipsByIdentifier.keySet()) { + Relationship r = relationshipsByIdentifier.get(identifier); + + if (r.equals(relationship)) { + return identifier; + } + } + } + + return null; + } + + void validateIdentifierName(String identifier) { + if (identifier.startsWith("-")) { + throw new RuntimeException("Identifiers cannot start with a - character"); + } + + if (!IDENTIFIER_PATTERN.matcher(identifier).matches()) { + throw new RuntimeException("Identifiers can only contain the following characters: a-zA-Z0-9_-"); + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewContentParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewContentParser.java new file mode 100644 index 000000000..5f9192af2 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewContentParser.java @@ -0,0 +1,163 @@ +package com.structurizr.dsl; + +import com.structurizr.importer.diagrams.kroki.KrokiImporter; +import com.structurizr.importer.diagrams.mermaid.MermaidImporter; +import com.structurizr.importer.diagrams.plantuml.PlantUMLImporter; +import com.structurizr.util.ImageUtils; +import com.structurizr.util.Url; +import com.structurizr.view.ImageView; + +import java.io.File; + +final class ImageViewContentParser extends AbstractParser { + + private static final String PLANTUML_GRAMMAR = "plantuml "; + private static final String MERMAID_GRAMMAR = "mermaid "; + private static final String KROKI_GRAMMAR = "kroki "; + private static final String IMAGE_GRAMMAR = "image "; + + private static final int TITLE_INDEX = 1; + private static final int DESCRIPTION_INDEX = 1; + + private static final int PLANTUML_SOURCE_INDEX = 1; + private static final int MERMAID_SOURCE_INDEX = 1; + private static final int KROKI_FORMAT_INDEX = 1; + private static final int KROKI_SOURCE_INDEX = 2; + private static final int IMAGE_SOURCE_INDEX = 1; + + private boolean restricted = false; + + ImageViewContentParser(boolean restricted) { + this.restricted = restricted; + } + + void parsePlantUML(ImageViewDslContext context, File dslFile, Tokens tokens) { + // plantuml + + if (tokens.hasMoreThan(PLANTUML_SOURCE_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + PLANTUML_GRAMMAR); + } + + ImageView view = context.getView(); + if (view != null) { + if (tokens.size() == 2) { + String source = tokens.get(PLANTUML_SOURCE_INDEX); + + try { + if (Url.isUrl(source)) { + String content = readFromUrl(source); + new PlantUMLImporter().importDiagram(context.getView(), content); + context.getView().setTitle(source.substring(source.lastIndexOf("/")+1)); + } else { + if (!restricted) { + File file = new File(dslFile.getParentFile(), source); + new PlantUMLImporter().importDiagram(context.getView(), file); + } + } + } catch (Exception e) { + throw new RuntimeException(e.getMessage()); + } + } else { + throw new RuntimeException("Expected: " + PLANTUML_GRAMMAR); + } + } + } + + void parseMermaid(ImageViewDslContext context, File dslFile, Tokens tokens) { + // mermaid + + if (tokens.hasMoreThan(MERMAID_SOURCE_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + MERMAID_GRAMMAR); + } + + ImageView view = context.getView(); + if (view != null) { + if (tokens.size() == 2) { + String source = tokens.get(MERMAID_SOURCE_INDEX); + + try { + if (Url.isUrl(source)) { + String content = readFromUrl(source); + new MermaidImporter().importDiagram(context.getView(), content); + context.getView().setTitle(source.substring(source.lastIndexOf("/")+1)); + } else { + if (!restricted) { + File file = new File(dslFile.getParentFile(), source); + new MermaidImporter().importDiagram(context.getView(), file); + } + } + } catch (Exception e) { + throw new RuntimeException(e.getMessage()); + } + } else { + throw new RuntimeException("Expected: " + MERMAID_GRAMMAR); + } + } + } + + void parseKroki(ImageViewDslContext context, File dslFile, Tokens tokens) { + // kroki + + if (tokens.hasMoreThan(KROKI_SOURCE_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + KROKI_GRAMMAR); + } + + ImageView view = context.getView(); + if (view != null) { + if (tokens.size() == 3) { + String format = tokens.get(KROKI_FORMAT_INDEX); + String source = tokens.get(KROKI_SOURCE_INDEX); + + try { + if (Url.isUrl(source)) { + String content = readFromUrl(source); + new KrokiImporter().importDiagram(context.getView(), format, content); + context.getView().setTitle(source.substring(source.lastIndexOf("/")+1)); + } else { + if (!restricted) { + File file = new File(dslFile.getParentFile(), source); + new KrokiImporter().importDiagram(context.getView(), format, file); + } + } + } catch (Exception e) { + throw new RuntimeException(e.getMessage()); + } + } else { + throw new RuntimeException("Expected: " + KROKI_GRAMMAR); + } + } + } + + void parseImage(ImageViewDslContext context, File dslFile, Tokens tokens) { + // image + + if (tokens.hasMoreThan(IMAGE_SOURCE_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + IMAGE_GRAMMAR); + } + + ImageView view = context.getView(); + if (view != null) { + if (tokens.size() == 2) { + String source = tokens.get(IMAGE_SOURCE_INDEX); + + try { + if (Url.isUrl(source)) { + context.getView().setContent(source); + context.getView().setTitle(source.substring(source.lastIndexOf("/")+1)); + } else { + if (!restricted) { + File file = new File(dslFile.getParentFile(), source); + context.getView().setContent(ImageUtils.getImageAsDataUri(file)); + context.getView().setTitle(file.getName()); + } + } + } catch (Exception e) { + throw new RuntimeException(e.getMessage()); + } + } else { + throw new RuntimeException("Expected: " + IMAGE_GRAMMAR); + } + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewDslContext.java new file mode 100644 index 000000000..1a7a996a5 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewDslContext.java @@ -0,0 +1,50 @@ +package com.structurizr.dsl; + +import com.structurizr.util.ImageUtils; +import com.structurizr.util.StringUtils; +import com.structurizr.view.ImageView; + +import java.io.IOException; + +class ImageViewDslContext extends ViewDslContext { + + ImageViewDslContext(ImageView view) { + super(view); + } + + ImageView getView() { + return (ImageView)super.getView(); + } + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.VIEW_TITLE_TOKEN, + StructurizrDslTokens.VIEW_DESCRIPTION_TOKEN, + StructurizrDslTokens.PLANTUML_TOKEN, + StructurizrDslTokens.MERMAID_TOKEN, + StructurizrDslTokens.KROKI_TOKEN, + StructurizrDslTokens.IMAGE_TOKEN + }; + } + + @Override + void end() { + super.end(); + + // try to set the content type if it hasn't been set ... this helps the diagram render with image sizing/scaling + ImageView imageView = getView(); + if (StringUtils.isNullOrEmpty(imageView.getContentType())) { + if (ImageUtils.isSupportedDataUri(imageView.getContent())) { + imageView.setContentType(ImageUtils.getContentTypeFromDataUri(imageView.getContent())); + } else { + try { + imageView.setContentType(ImageUtils.getContentType(imageView.getContent())); + } catch (IOException e) { + e.printStackTrace(); + // ignore + } + } + } + } +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewParser.java new file mode 100644 index 000000000..548e8234c --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewParser.java @@ -0,0 +1,56 @@ +package com.structurizr.dsl; + +import com.structurizr.Workspace; +import com.structurizr.model.Element; +import com.structurizr.view.ImageView; + +class ImageViewParser extends AbstractViewParser { + + private static final String GRAMMAR = "image <*|element identifier> [key] {"; + + private static final String VIEW_TYPE = "Image"; + + private static final int SCOPE_IDENTIFIER_INDEX = 1; + private static final int KEY_INDEX = 2; + + private static final String WILDCARD = "*"; + + ImageView parse(DslContext context, Tokens tokens) { + // image <*|element identifier> [key] { + + Workspace workspace = context.getWorkspace(); + String key = ""; + + if (tokens.hasMoreThan(KEY_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } + + if (!tokens.includes(SCOPE_IDENTIFIER_INDEX)) { + throw new RuntimeException("Expected: " + GRAMMAR); + } + + ImageView view; + if (tokens.includes(KEY_INDEX)) { + key = tokens.get(KEY_INDEX); + } else { + key = workspace.getViews().generateViewKey(VIEW_TYPE); + } + validateViewKey(key); + + String scopeIdentifier = tokens.get(SCOPE_IDENTIFIER_INDEX); + if (WILDCARD.equals(scopeIdentifier)) { + + view = workspace.getViews().createImageView(key); + } else { + Element element = context.getElement(scopeIdentifier); + if (element == null) { + throw new RuntimeException("The element \"" + scopeIdentifier + "\" does not exist"); + } + + view = workspace.getViews().createImageView(element, key); + } + + return view; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImplicitRelationshipParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImplicitRelationshipParser.java new file mode 100644 index 000000000..d076dffa2 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImplicitRelationshipParser.java @@ -0,0 +1,52 @@ +package com.structurizr.dsl; + +import com.structurizr.model.*; + +final class ImplicitRelationshipParser extends AbstractRelationshipParser { + + private static final String GRAMMAR = "-> [description] [technology] [tags]"; + + private static final int DESTINATION_IDENTIFIER_INDEX = 1; + private final static int DESCRIPTION_INDEX = 2; + private final static int TECHNOLOGY_INDEX = 3; + private final static int TAGS_INDEX = 4; + + Relationship parse(ModelItemDslContext context, Tokens tokens) { + // -> [description] [technology] [tags] + + if (tokens.hasMoreThan(TAGS_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } + + if (!tokens.includes(DESTINATION_IDENTIFIER_INDEX)) { + throw new RuntimeException("Expected: " + GRAMMAR); + } + + String destinationId = tokens.get(DESTINATION_IDENTIFIER_INDEX); + + Element sourceElement = (Element)context.getModelItem(); + Element destinationElement = context.getElement(destinationId); + + if (destinationElement == null) { + throw new RuntimeException("The destination element \"" + destinationId + "\" does not exist"); + } + + String description = ""; + if (tokens.includes(DESCRIPTION_INDEX)) { + description = tokens.get(DESCRIPTION_INDEX); + } + + String technology = ""; + if (tokens.includes(TECHNOLOGY_INDEX)) { + technology = tokens.get(TECHNOLOGY_INDEX); + } + + String[] tags = new String[0]; + if (tokens.includes(TAGS_INDEX)) { + tags = tokens.get(TAGS_INDEX).split(","); + } + + return createRelationship(sourceElement, description, technology, tags, destinationElement); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImpliedRelationshipsParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImpliedRelationshipsParser.java new file mode 100644 index 000000000..f66785ffe --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImpliedRelationshipsParser.java @@ -0,0 +1,34 @@ +package com.structurizr.dsl; + +import com.structurizr.model.CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy; +import com.structurizr.model.DefaultImpliedRelationshipsStrategy; + +import java.util.ArrayList; +import java.util.List; + +final class ImpliedRelationshipsParser extends AbstractParser { + + private static final String GRAMMAR = "!impliedRelationships "; + + private static final int FLAG_INDEX = 1; + private static final String FALSE = "false"; + + void parse(DslContext context, Tokens tokens) { + // impliedRelationships + + if (tokens.hasMoreThan(FLAG_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } + + if (!tokens.includes(FLAG_INDEX)) { + throw new RuntimeException("Expected: " + GRAMMAR); + } + + if (tokens.get(FLAG_INDEX).equalsIgnoreCase(FALSE)) { + context.getWorkspace().getModel().setImpliedRelationshipsStrategy(new DefaultImpliedRelationshipsStrategy()); + } else { + context.getWorkspace().getModel().setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/IncludeParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/IncludeParser.java new file mode 100644 index 000000000..e3fcab817 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/IncludeParser.java @@ -0,0 +1,73 @@ +package com.structurizr.dsl; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.Arrays; +import java.util.List; + +final class IncludeParser extends AbstractParser { + + private static final String GRAMMAR = "!include "; + + private static final int SOURCE_INDEX = 1; + + void parse(IncludedDslContext context, Tokens tokens) { + // !include + + if (tokens.hasMoreThan(SOURCE_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } + + if (!tokens.includes(SOURCE_INDEX)) { + throw new RuntimeException("Expected: " + GRAMMAR); + } + + String source = tokens.get(SOURCE_INDEX); + if (source.startsWith("https://")) { + String dsl = readFromUrl(source); + List lines = Arrays.asList(dsl.split("\n")); + context.addFile(context.getParentFile(), lines); + } else { + if (context.getParentFile() != null) { + File path = new File(context.getParentFile().getParent(), source); + + try { + if (!path.exists()) { + throw new RuntimeException(path.getCanonicalPath() + " could not be found"); + } + + readFiles(context, path); + } catch (IOException e) { + throw new RuntimeException("Error including " + path.getAbsolutePath() + ": " + e.getMessage()); + } + } + } + } + + private void readFiles(IncludedDslContext context, File path) throws IOException { + if (path.isHidden() || path.getName().startsWith(".")) { + // ignore + return; + } + + if (path.isDirectory()) { + File[] files = path.listFiles(); + if (files != null) { + Arrays.sort(files); + + for (File file : files) { + readFiles(context, file); + } + } + } else { + try { + context.addFile(path, Files.readAllLines(path.toPath(), StandardCharsets.UTF_8)); + } catch (IOException e) { + throw new RuntimeException("Error reading file at " + path.getAbsolutePath() + ": " + e.getMessage()); + } + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/IncludedDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/IncludedDslContext.java new file mode 100644 index 000000000..42c86d00a --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/IncludedDslContext.java @@ -0,0 +1,33 @@ +package com.structurizr.dsl; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +final class IncludedDslContext extends DslContext { + + private final File parentFile; + private final List files = new ArrayList<>(); + + IncludedDslContext(File parentFile) { + this.parentFile = parentFile; + } + + File getParentFile() { + return parentFile; + } + + void addFile(File file, List lines) { + this.files.add(new IncludedFile(file, lines)); + } + + List getFiles() { + return new ArrayList<>(files); + } + + @Override + protected String[] getPermittedTokens() { + return new String[0]; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/IncludedFile.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/IncludedFile.java new file mode 100644 index 000000000..f2b5a63ea --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/IncludedFile.java @@ -0,0 +1,24 @@ +package com.structurizr.dsl; + +import java.io.File; +import java.util.List; + +final class IncludedFile { + + private final File file; + private final List lines; + + IncludedFile(File file, List lines) { + this.file = file; + this.lines = lines; + } + + List getLines() { + return lines; + } + + File getFile() { + return file; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeDslContext.java new file mode 100644 index 000000000..97487a2a5 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeDslContext.java @@ -0,0 +1,36 @@ +package com.structurizr.dsl; + +import com.structurizr.model.InfrastructureNode; +import com.structurizr.model.ModelItem; + +final class InfrastructureNodeDslContext extends ModelItemDslContext { + + private InfrastructureNode infrastructureNode; + + InfrastructureNodeDslContext(InfrastructureNode infrastructureNode) { + this.infrastructureNode = infrastructureNode; + } + + InfrastructureNode getInfrastructureNode() { + return infrastructureNode; + } + + @Override + ModelItem getModelItem() { + return getInfrastructureNode(); + } + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.RELATIONSHIP_TOKEN, + StructurizrDslTokens.DESCRIPTION_TOKEN, + StructurizrDslTokens.TECHNOLOGY_TOKEN, + StructurizrDslTokens.TAGS_TOKEN, + StructurizrDslTokens.URL_TOKEN, + StructurizrDslTokens.PROPERTIES_TOKEN, + StructurizrDslTokens.PERSPECTIVES_TOKEN + }; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeParser.java new file mode 100644 index 000000000..9f20ba9a5 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeParser.java @@ -0,0 +1,71 @@ +package com.structurizr.dsl; + +import com.structurizr.model.DeploymentNode; +import com.structurizr.model.InfrastructureNode; + +final class InfrastructureNodeParser extends AbstractParser { + + private static final String GRAMMAR = "infrastructureNode [description] [technology] [tags]"; + + private static final int NAME_INDEX = 1; + private static final int DESCRIPTION_INDEX = 2; + private static final int TECHNOLOGY_INDEX = 3; + private static final int TAGS_INDEX = 4; + + InfrastructureNode parse(DeploymentNodeDslContext context, Tokens tokens) { + // infrastructureNode [description] [technology] [tags] + + if (tokens.hasMoreThan(TAGS_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } + + if (!tokens.includes(NAME_INDEX)) { + throw new RuntimeException("Expected: " + GRAMMAR); + } + + DeploymentNode deploymentNode = context.getDeploymentNode(); + InfrastructureNode infrastructureNode; + String name = tokens.get(NAME_INDEX); + + String description = ""; + if (tokens.includes(DESCRIPTION_INDEX)) { + description = tokens.get(DESCRIPTION_INDEX); + } + + String technology = ""; + if (tokens.includes(TECHNOLOGY_INDEX)) { + technology = tokens.get(TECHNOLOGY_INDEX); + } + + infrastructureNode = deploymentNode.addInfrastructureNode(name, description, technology); + + if (tokens.includes(TAGS_INDEX)) { + String tags = tokens.get(TAGS_INDEX); + infrastructureNode.addTags(tags.split(",")); + } + + if (context.hasGroup()) { + infrastructureNode.setGroup(context.getGroup().getName()); + context.getGroup().addElement(infrastructureNode); + } + + return infrastructureNode; + } + + void parseTechnology(InfrastructureNodeDslContext context, Tokens tokens) { + int index = 1; + + // technology + if (tokens.hasMoreThan(index)) { + throw new RuntimeException("Too many tokens, expected: technology "); + } + + if (!tokens.includes(index)) { + throw new RuntimeException("Expected: technology "); + } + + String technology = tokens.get(index); + context.getInfrastructureNode().setTechnology(technology); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/InlineScriptDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/InlineScriptDslContext.java new file mode 100644 index 000000000..72803e629 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/InlineScriptDslContext.java @@ -0,0 +1,55 @@ +package com.structurizr.dsl; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +class InlineScriptDslContext extends ScriptDslContext { + + static final Map SUPPORTED_LANGUAGES = new HashMap<>(); + + private final String language; + private final List lines = new ArrayList<>(); + + static { + SUPPORTED_LANGUAGES.put("javascript", "js"); + SUPPORTED_LANGUAGES.put("groovy", "groovy"); + SUPPORTED_LANGUAGES.put("kotlin", "kts"); + SUPPORTED_LANGUAGES.put("ruby", "rb"); + } + + InlineScriptDslContext(DslContext parentContext, File dslFile, String language) { + super(parentContext, dslFile); + + this.language = language.toLowerCase(); + } + + void addLine(String line) { + lines.add(line); + } + + @Override + void end() { + try { + String fileExtension; + + if (SUPPORTED_LANGUAGES.containsKey(language)) { + fileExtension = SUPPORTED_LANGUAGES.get(language); + } else { + throw new RuntimeException("Unsupported scripting language \"" + language + "\""); + } + + run(this, fileExtension, lines); + } catch (Exception e) { + throw new RuntimeException("Error running inline script, caused by " + e.getClass().getName() + ": " + e.getMessage(), e); + } + } + + @Override + protected String[] getPermittedTokens() { + return new String[0]; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelDslContext.java new file mode 100644 index 000000000..e803d4fea --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelDslContext.java @@ -0,0 +1,37 @@ +package com.structurizr.dsl; + +import com.structurizr.model.Location; + +final class ModelDslContext extends GroupableDslContext { + + ModelDslContext() { + super(null); + } + + ModelDslContext(ElementGroup group) { + super(group); + } + + @Override + void end() { + // the location is only set to internal when created inside an "enterprise" block, let's assume the rest are external if an enterprise has been specified + if (getWorkspace().getModel().getEnterprise() != null) { + getWorkspace().getModel().getPeople().stream().filter(p -> p.getLocation() != Location.Internal).forEach(p -> p.setLocation(Location.External)); + getWorkspace().getModel().getSoftwareSystems().stream().filter(ss -> ss.getLocation() != Location.Internal).forEach(ss -> ss.setLocation(Location.External)); + } + } + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.ENTERPRISE_TOKEN, + StructurizrDslTokens.GROUP_TOKEN, + StructurizrDslTokens.PERSON_TOKEN, + StructurizrDslTokens.SOFTWARE_SYSTEM_TOKEN, + StructurizrDslTokens.DEPLOYMENT_ENVIRONMENT_TOKEN, + StructurizrDslTokens.CUSTOM_ELEMENT_TOKEN, + StructurizrDslTokens.RELATIONSHIP_TOKEN + }; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemDslContext.java new file mode 100644 index 000000000..72150de5a --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemDslContext.java @@ -0,0 +1,17 @@ +package com.structurizr.dsl; + +import com.structurizr.model.ModelItem; + +abstract class ModelItemDslContext extends GroupableDslContext { + + ModelItemDslContext() { + super(); + } + + ModelItemDslContext(ElementGroup group) { + super(group); + } + + abstract ModelItem getModelItem(); + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemParser.java new file mode 100644 index 000000000..823728bf0 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemParser.java @@ -0,0 +1,79 @@ +package com.structurizr.dsl; + +import com.structurizr.model.Element; + +final class ModelItemParser extends AbstractParser { + + private final static int DESCRIPTION_INDEX = 1; + + private final static int TAGS_INDEX = 1; + + private final static int URL_INDEX = 1; + + private final static int PERSPECTIVE_NAME_INDEX = 0; + private final static int PERSPECTIVE_DESCRIPTION_INDEX = 1; + private final static int PERSPECTIVE_VALUE_INDEX = 2; + + void parseTags(ModelItemDslContext context, Tokens tokens) { + // tags [tags] + if (!tokens.includes(TAGS_INDEX)) { + throw new RuntimeException("Expected: tags [tags]"); + } + + for (int i = TAGS_INDEX; i < tokens.size(); i++) { + String tags = tokens.get(i); + context.getModelItem().addTags(tags.split(",")); + } + } + + void parseDescription(ModelItemDslContext context, Tokens tokens) { + // description + if (tokens.hasMoreThan(DESCRIPTION_INDEX)) { + throw new RuntimeException("Too many tokens, expected: description "); + } + + if (!tokens.includes(DESCRIPTION_INDEX)) { + throw new RuntimeException("Expected: description "); + } + + String description = tokens.get(DESCRIPTION_INDEX); + ((Element)context.getModelItem()).setDescription(description); + } + + void parseUrl(ModelItemDslContext context, Tokens tokens) { + // url + if (tokens.hasMoreThan(URL_INDEX)) { + throw new RuntimeException("Too many tokens, expected: url "); + } + + if (!tokens.includes(URL_INDEX)) { + throw new RuntimeException("Expected: url "); + } + + String url = tokens.get(URL_INDEX); + context.getModelItem().setUrl(url); + } + + void parsePerspective(ModelItemPerspectivesDslContext context, Tokens tokens) { + // [value] + + if (tokens.hasMoreThan(PERSPECTIVE_VALUE_INDEX)) { + throw new RuntimeException("Too many tokens, expected: [value]"); + } + + if (!tokens.includes(PERSPECTIVE_DESCRIPTION_INDEX)) { + throw new RuntimeException("Expected: [value]"); + } + + String name = tokens.get(PERSPECTIVE_NAME_INDEX); + String description = tokens.get(PERSPECTIVE_DESCRIPTION_INDEX); + String value = ""; + + if (tokens.includes(PERSPECTIVE_VALUE_INDEX)) { + value = tokens.get(PERSPECTIVE_VALUE_INDEX); + } + + context.getModelItem().addPerspective(name, description, value); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemPerspectivesDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemPerspectivesDslContext.java new file mode 100644 index 000000000..37647a492 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemPerspectivesDslContext.java @@ -0,0 +1,22 @@ +package com.structurizr.dsl; + +import com.structurizr.model.ModelItem; + +final class ModelItemPerspectivesDslContext extends DslContext { + + private ModelItem modelItem; + + public ModelItemPerspectivesDslContext(ModelItem modelItem) { + this.modelItem = modelItem; + } + + ModelItem getModelItem() { + return this.modelItem; + } + + @Override + protected String[] getPermittedTokens() { + return new String[0]; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelViewContentParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelViewContentParser.java new file mode 100644 index 000000000..44212f52b --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelViewContentParser.java @@ -0,0 +1,19 @@ +package com.structurizr.dsl; + +import com.structurizr.model.Relationship; +import com.structurizr.view.ModelView; + +abstract class ModelViewContentParser extends AbstractParser { + + protected static final String WILDCARD = "*"; + protected static final String ELEMENT_WILDCARD = "element==*"; + + protected boolean isExpression(String token) { + return AbstractExpressionParser.isExpression(token.toLowerCase()); + } + + protected void removeRelationshipFromView(Relationship relationship, ModelView view) { + view.remove(relationship); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelViewDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelViewDslContext.java new file mode 100644 index 000000000..a7e966451 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelViewDslContext.java @@ -0,0 +1,15 @@ +package com.structurizr.dsl; + +import com.structurizr.view.ModelView; + +abstract class ModelViewDslContext extends ViewDslContext { + + ModelViewDslContext(ModelView view) { + super(view); + } + + ModelView getView() { + return (ModelView)super.getView(); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonDslContext.java new file mode 100644 index 000000000..46d01acbb --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonDslContext.java @@ -0,0 +1,41 @@ +package com.structurizr.dsl; + +import com.structurizr.model.GroupableElement; +import com.structurizr.model.ModelItem; +import com.structurizr.model.Person; + +final class PersonDslContext extends GroupableElementDslContext { + + private Person person; + + PersonDslContext(Person person) { + this.person = person; + } + + Person getPerson() { + return person; + } + + @Override + ModelItem getModelItem() { + return getPerson(); + } + + @Override + GroupableElement getElement() { + return person; + } + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.DESCRIPTION_TOKEN, + StructurizrDslTokens.TAGS_TOKEN, + StructurizrDslTokens.URL_TOKEN, + StructurizrDslTokens.PROPERTIES_TOKEN, + StructurizrDslTokens.PERSPECTIVES_TOKEN, + StructurizrDslTokens.RELATIONSHIP_TOKEN + }; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonParser.java new file mode 100644 index 000000000..9ec33bfa6 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonParser.java @@ -0,0 +1,59 @@ +package com.structurizr.dsl; + +import com.structurizr.model.Location; +import com.structurizr.model.Person; + +final class PersonParser extends AbstractParser { + + private static final String GRAMMAR = "person [description] [tags]"; + + private final static int NAME_INDEX = 1; + private final static int DESCRIPTION_INDEX = 2; + private final static int TAGS_INDEX = 3; + + Person parse(GroupableDslContext context, Tokens tokens) { + // person [description] [tags] + + if (tokens.hasMoreThan(TAGS_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } + + if (!tokens.includes(NAME_INDEX)) { + throw new RuntimeException("Expected: " + GRAMMAR); + } + + Person person = null; + String name = tokens.get(NAME_INDEX); + + if (context.isExtendingWorkspace()) { + person = context.getWorkspace().getModel().getPersonWithName(name); + } + + if (person == null) { + person = context.getWorkspace().getModel().addPerson(name); + } + + String description = ""; + if (tokens.includes(DESCRIPTION_INDEX)) { + description = tokens.get(DESCRIPTION_INDEX); + person.setDescription(description); + } + + if (tokens.includes(TAGS_INDEX)) { + String tags = tokens.get(TAGS_INDEX); + person.addTags(tags.split(",")); + } + + if (context instanceof EnterpriseDslContext) { + person.setLocation(Location.Internal); + } + + if (context.hasGroup()) { + person.setGroup(context.getGroup().getName()); + context.getGroup().addElement(person); + } + + return person; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/PluginDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/PluginDslContext.java new file mode 100644 index 000000000..a648c2e80 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/PluginDslContext.java @@ -0,0 +1,42 @@ +package com.structurizr.dsl; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +class PluginDslContext extends DslContext { + + private final String fullyQualifiedClassName; + private final File dslFile; + private final StructurizrDslParser dslParser; + private final Map parameters = new HashMap<>(); + + PluginDslContext(String fullyQualifiedClassName, File dslFile, StructurizrDslParser dslParser) { + this.fullyQualifiedClassName = fullyQualifiedClassName; + this.dslFile = dslFile; + this.dslParser = dslParser; + } + + void addParameter(String name, String value) { + parameters.put(name, value); + } + + @Override + void end() { + try { + Class pluginClass = loadClass(fullyQualifiedClassName, dslFile); + StructurizrDslPlugin plugin = (StructurizrDslPlugin)pluginClass.getDeclaredConstructor().newInstance(); + StructurizrDslPluginContext pluginContext = new StructurizrDslPluginContext(dslParser, dslFile, getWorkspace(), parameters); + plugin.run(pluginContext); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Error running plugin " + fullyQualifiedClassName + ", caused by " + e.getClass().getName() + ": " + e.getMessage()); + } + } + + @Override + protected String[] getPermittedTokens() { + return new String[0]; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/PluginParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/PluginParser.java new file mode 100644 index 000000000..b6471a87c --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/PluginParser.java @@ -0,0 +1,43 @@ +package com.structurizr.dsl; + +final class PluginParser extends AbstractParser { + + private static final String GRAMMAR = "!plugin "; + + private static final int FQN_INDEX = 1; + + private final static int PARAMETER_NAME_INDEX = 0; + private final static int PARAMETER_VALUE_INDEX = 1; + + String parse(DslContext context, Tokens tokens) { + // !plugin + + if (tokens.hasMoreThan(FQN_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } + + if (!tokens.includes(FQN_INDEX)) { + throw new RuntimeException("Expected: " + GRAMMAR); + } + + return tokens.get(FQN_INDEX); + } + + void parseParameter(PluginDslContext context, Tokens tokens) { + // + + if (tokens.hasMoreThan(PARAMETER_VALUE_INDEX)) { + throw new RuntimeException("Too many tokens, expected: "); + } + + if (tokens.size() != 2) { + throw new RuntimeException("Expected: "); + } + + String name = tokens.get(PARAMETER_NAME_INDEX); + String value = tokens.get(PARAMETER_VALUE_INDEX); + + context.addParameter(name, value); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/PropertiesDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/PropertiesDslContext.java new file mode 100644 index 000000000..e1f8184fd --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/PropertiesDslContext.java @@ -0,0 +1,22 @@ +package com.structurizr.dsl; + +import com.structurizr.PropertyHolder; + +final class PropertiesDslContext extends DslContext { + + private PropertyHolder propertyHolder; + + public PropertiesDslContext(PropertyHolder propertyHolder) { + this.propertyHolder = propertyHolder; + } + + PropertyHolder getPropertyHolder() { + return this.propertyHolder; + } + + @Override + protected String[] getPermittedTokens() { + return new String[0]; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/PropertyParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/PropertyParser.java new file mode 100644 index 000000000..2129a2ddd --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/PropertyParser.java @@ -0,0 +1,25 @@ +package com.structurizr.dsl; + +final class PropertyParser extends AbstractParser { + + private final static int PROPERTY_NAME_INDEX = 0; + private final static int PROPERTY_VALUE_INDEX = 1; + + void parse(PropertiesDslContext context, Tokens tokens) { + // + + if (tokens.hasMoreThan(PROPERTY_VALUE_INDEX)) { + throw new RuntimeException("Too many tokens, expected: "); + } + + if (tokens.size() != 2) { + throw new RuntimeException("Expected: "); + } + + String name = tokens.get(PROPERTY_NAME_INDEX); + String value = tokens.get(PROPERTY_VALUE_INDEX); + + context.getPropertyHolder().addProperty(name, value); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/RefParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/RefParser.java new file mode 100644 index 000000000..d9df9b1f8 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/RefParser.java @@ -0,0 +1,52 @@ +package com.structurizr.dsl; + +import com.structurizr.model.ModelItem; +import com.structurizr.model.StaticStructureElement; + +final class RefParser extends AbstractParser { + + private static final String GRAMMAR = "%s "; + + private final static int IDENTIFIER_INDEX = 1; + + ModelItem parse(DslContext context, Tokens tokens) { + // !ref + + if (tokens.hasMoreThan(IDENTIFIER_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + String.format(GRAMMAR, tokens.get(0))); + } + + if (!tokens.includes(IDENTIFIER_INDEX)) { + throw new RuntimeException("Expected: " + String.format(GRAMMAR, tokens.get(0))); + } + + String s = tokens.get(IDENTIFIER_INDEX); + + ModelItem modelItem; + + if (s.contains("://")) { + modelItem = context.getWorkspace().getModel().getElementWithCanonicalName(s); + } else { + modelItem = context.getElement(s); + + if (modelItem == null) { + modelItem = context.getRelationship(s); + } + } + + if (modelItem == null) { + throw new RuntimeException("An element/relationship identified by \"" + s + "\" could not be found"); + } + + if (context instanceof GroupableDslContext && modelItem instanceof StaticStructureElement) { + GroupableDslContext groupableDslContext = (GroupableDslContext)context; + StaticStructureElement staticStructureElement = (StaticStructureElement)modelItem; + if (groupableDslContext.hasGroup()) { + staticStructureElement.setGroup(groupableDslContext.getGroup().getName()); + } + } + + return modelItem; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipDslContext.java new file mode 100644 index 000000000..b39e3043f --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipDslContext.java @@ -0,0 +1,33 @@ +package com.structurizr.dsl; + +import com.structurizr.model.ModelItem; +import com.structurizr.model.Relationship; + +final class RelationshipDslContext extends ModelItemDslContext { + + private Relationship relationship; + + RelationshipDslContext(Relationship relationship) { + this.relationship = relationship; + } + + Relationship getRelationship() { + return relationship; + } + + @Override + ModelItem getModelItem() { + return getRelationship(); + } + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.TAGS_TOKEN, + StructurizrDslTokens.URL_TOKEN, + StructurizrDslTokens.PROPERTIES_TOKEN, + StructurizrDslTokens.PERSPECTIVES_TOKEN + }; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipStyleDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipStyleDslContext.java new file mode 100644 index 000000000..58239b18f --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipStyleDslContext.java @@ -0,0 +1,33 @@ +package com.structurizr.dsl; + +import com.structurizr.view.RelationshipStyle; + +final class RelationshipStyleDslContext extends DslContext { + + private RelationshipStyle style; + + RelationshipStyleDslContext(RelationshipStyle style) { + this.style = style; + } + + RelationshipStyle getStyle() { + return style; + } + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.RELATIONSHIP_STYLE_THICKNESS_TOKEN, + StructurizrDslTokens.RELATIONSHIP_STYLE_COLOR_TOKEN, + StructurizrDslTokens.RELATIONSHIP_STYLE_COLOUR_TOKEN, + StructurizrDslTokens.RELATIONSHIP_STYLE_LINE_STYLE_TOKEN, + StructurizrDslTokens.RELATIONSHIP_STYLE_ROUTING_TOKEN, + StructurizrDslTokens.RELATIONSHIP_STYLE_FONT_SIZE_TOKEN, + StructurizrDslTokens.RELATIONSHIP_STYLE_WIDTH_TOKEN, + StructurizrDslTokens.RELATIONSHIP_STYLE_POSITION_TOKEN, + StructurizrDslTokens.RELATIONSHIP_STYLE_OPACITY_TOKEN, + StructurizrDslTokens.PROPERTIES_TOKEN + }; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipStyleParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipStyleParser.java new file mode 100644 index 000000000..6c260c3bf --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipStyleParser.java @@ -0,0 +1,239 @@ +package com.structurizr.dsl; + +import com.structurizr.Workspace; +import com.structurizr.util.StringUtils; +import com.structurizr.view.LineStyle; +import com.structurizr.view.RelationshipStyle; +import com.structurizr.view.Routing; + +import java.util.HashMap; +import java.util.Map; + +final class RelationshipStyleParser extends AbstractParser { + + private static final int FIRST_PROPERTY_INDEX = 1; + + RelationshipStyle parseRelationshipStyle(DslContext context, Tokens tokens) { + if (tokens.hasMoreThan(FIRST_PROPERTY_INDEX)) { + throw new RuntimeException("Too many tokens, expected: relationship {"); + } + + if (tokens.includes(FIRST_PROPERTY_INDEX)) { + String tag = tokens.get(1); + + if (StringUtils.isNullOrEmpty(tag)) { + throw new RuntimeException("A tag must be specified"); + } + + Workspace workspace = context.getWorkspace(); + RelationshipStyle relationshipStyle = workspace.getViews().getConfiguration().getStyles().getRelationshipStyle(tag); + if (relationshipStyle == null) { + relationshipStyle = workspace.getViews().getConfiguration().getStyles().addRelationshipStyle(tag); + } + + return relationshipStyle; + } else { + throw new RuntimeException("Expected: relationship {"); + } + } + + void parseThickness(RelationshipStyleDslContext context, Tokens tokens) { + // thickness + RelationshipStyle style = context.getStyle(); + + if (tokens.hasMoreThan(FIRST_PROPERTY_INDEX)) { + throw new RuntimeException("Too many tokens, expected: thickness "); + } + + if (tokens.includes(FIRST_PROPERTY_INDEX)) { + String thicknessAsString = tokens.get(1); + + try { + int thickness = Integer.parseInt(thicknessAsString); + style.setThickness(thickness); + } catch (NumberFormatException e) { + throw new RuntimeException("Thickness must be a positive integer"); + } + } else { + throw new RuntimeException("Expected: thickness "); + } + } + + void parseColour(RelationshipStyleDslContext context, Tokens tokens) { + // colour #rrggbb|color name + RelationshipStyle style = context.getStyle(); + + if (tokens.hasMoreThan(FIRST_PROPERTY_INDEX)) { + throw new RuntimeException("Too many tokens, expected: colour <#rrggbb|color name>"); + } + + if (tokens.includes(FIRST_PROPERTY_INDEX)) { + String colour = tokens.get(1); + style.setColor(colour); + } else { + throw new RuntimeException("Expected: colour <#rrggbb|color name>"); + } + } + + void parseDashed(RelationshipStyleDslContext context, Tokens tokens) { + // dashed true|false + RelationshipStyle style = context.getStyle(); + + if (tokens.hasMoreThan(FIRST_PROPERTY_INDEX)) { + throw new RuntimeException("Too many tokens, expected: dashed "); + } + + if (tokens.includes(FIRST_PROPERTY_INDEX)) { + String dashed = tokens.get(1); + + if ("true".equalsIgnoreCase(dashed)) { + style.setDashed(true); + } else if ("false".equalsIgnoreCase(dashed)) { + style.setDashed(false); + } else { + throw new RuntimeException("Dashed must be true or false"); + } + } else { + throw new RuntimeException("Expected: dashed "); + } + } + + void parseOpacity(RelationshipStyleDslContext context, Tokens tokens) { + // opacity 0-100 + RelationshipStyle style = context.getStyle(); + + if (tokens.hasMoreThan(FIRST_PROPERTY_INDEX)) { + throw new RuntimeException("Too many tokens, expected: opacity <0-100>"); + } + + if (tokens.includes(FIRST_PROPERTY_INDEX)) { + String opacityAsString = tokens.get(1); + + try { + int opacity = Integer.parseInt(opacityAsString); + style.setOpacity(opacity); + } catch (NumberFormatException e) { + throw new RuntimeException("Opacity must be an integer between 0 and 100"); + } + } else { + throw new RuntimeException("Expected: opacity <0-100>"); + } + } + + void parseWidth(RelationshipStyleDslContext context, Tokens tokens) { + RelationshipStyle style = context.getStyle(); + + if (tokens.hasMoreThan(FIRST_PROPERTY_INDEX)) { + throw new RuntimeException("Too many tokens, expected: width "); + } + + if (tokens.includes(FIRST_PROPERTY_INDEX)) { + String widthAsString = tokens.get(1); + + try { + int width = Integer.parseInt(widthAsString); + style.setWidth(width); + } catch (NumberFormatException e) { + throw new RuntimeException("Width must be a positive integer"); + } + } else { + throw new RuntimeException("Expected: width "); + } + } + + void parseFontSize(RelationshipStyleDslContext context, Tokens tokens) { + RelationshipStyle style = context.getStyle(); + + if (tokens.hasMoreThan(FIRST_PROPERTY_INDEX)) { + throw new RuntimeException("Too many tokens, expected: fontSize "); + } + + if (tokens.includes(FIRST_PROPERTY_INDEX)) { + String fontSizeAsString = tokens.get(1); + + try { + int fontSize = Integer.parseInt(fontSizeAsString); + style.setFontSize(fontSize); + } catch (NumberFormatException e) { + throw new RuntimeException("Font size must be a positive integer"); + } + } else { + throw new RuntimeException("Expected: fontSize "); + } + } + + void parsePosition(RelationshipStyleDslContext context, Tokens tokens) { + // position 0-100 + RelationshipStyle style = context.getStyle(); + + if (tokens.hasMoreThan(FIRST_PROPERTY_INDEX)) { + throw new RuntimeException("Too many tokens, expected: position <0-100>"); + } + + if (tokens.includes(FIRST_PROPERTY_INDEX)) { + String positionAsString = tokens.get(1); + + try { + int opacity = Integer.parseInt(positionAsString); + style.setPosition(opacity); + } catch (NumberFormatException e) { + throw new RuntimeException("Position must be an integer between 0 and 100"); + } + } else { + throw new RuntimeException("Expected: position <0-100>"); + } + } + + void parseLineStyle(RelationshipStyleDslContext context, Tokens tokens) { + // style solid|dashed|dotted + Map lineStyles = new HashMap<>(); + for (LineStyle lineStyle : LineStyle.values()) { + lineStyles.put(lineStyle.toString().toLowerCase(), lineStyle); + } + + RelationshipStyle style = context.getStyle(); + + if (tokens.hasMoreThan(FIRST_PROPERTY_INDEX)) { + throw new RuntimeException("Too many tokens, expected: style "); + } + + if (tokens.includes(FIRST_PROPERTY_INDEX)) { + String lineStyle = tokens.get(1).toLowerCase(); + + if (lineStyles.containsKey(lineStyle)) { + style.setStyle(lineStyles.get(lineStyle)); + } else { + throw new RuntimeException("The line style \"" + lineStyle + "\" is not valid"); + } + } else { + throw new RuntimeException("Expected: style "); + } + } + + void parseRouting(RelationshipStyleDslContext context, Tokens tokens) { + // routing direct|orthogonal|curved + Map routings = new HashMap<>(); + for (Routing routing : Routing.values()) { + routings.put(routing.toString().toLowerCase(), routing); + } + + RelationshipStyle style = context.getStyle(); + + if (tokens.hasMoreThan(FIRST_PROPERTY_INDEX)) { + throw new RuntimeException("Too many tokens, expected: routing "); + } + + if (tokens.includes(FIRST_PROPERTY_INDEX)) { + String routing = tokens.get(1).toLowerCase(); + + if (routings.containsKey(routing)) { + style.setRouting(routings.get(routing)); + } else { + throw new RuntimeException("The routing \"" + routing + "\" is not valid"); + } + } else { + throw new RuntimeException("Expected: routing "); + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ScriptDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ScriptDslContext.java new file mode 100644 index 000000000..d066bbc44 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ScriptDslContext.java @@ -0,0 +1,77 @@ +package com.structurizr.dsl; + +import com.structurizr.model.Element; +import com.structurizr.model.Relationship; + +import javax.script.Bindings; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import java.io.File; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +abstract class ScriptDslContext extends DslContext { + + private static final String CONTEXT_VARIABLE_NAME = "context"; + private static final String WORKSPACE_VARIABLE_NAME = "workspace"; + private static final String VIEW_VARIABLE_NAME = "view"; + private static final String ELEMENT_VARIABLE_NAME = "element"; + private static final String RELATIONSHIP_VARIABLE_NAME = "relationship"; + + private final DslContext parentContext; + + protected final File dslFile; + + private final Map parameters = new HashMap<>(); + + ScriptDslContext(DslContext parentContext, File dslFile) { + this.parentContext = parentContext; + this.dslFile = dslFile; + } + + void addParameter(String name, String value) { + parameters.put(name, value); + } + + void run(DslContext context, String extension, List lines) throws Exception { + StringBuilder script = new StringBuilder(); + for (String line : lines) { + script.append(line); + script.append('\n'); + } + + ScriptEngineManager manager = new ScriptEngineManager(); + ScriptEngine engine = manager.getEngineByExtension(extension); + + if (engine != null) { + Bindings bindings = engine.createBindings(); + bindings.put(WORKSPACE_VARIABLE_NAME, context.getWorkspace()); + + if (parentContext instanceof ViewDslContext) { + bindings.put(VIEW_VARIABLE_NAME, ((ViewDslContext)parentContext).getView()); + } else if (parentContext instanceof ModelItemDslContext) { + ModelItemDslContext modelItemDslContext = (ModelItemDslContext)parentContext; + if (modelItemDslContext.getModelItem() instanceof Element) { + bindings.put(ELEMENT_VARIABLE_NAME, modelItemDslContext.getModelItem()); + } else if (modelItemDslContext.getModelItem() instanceof Relationship) { + bindings.put(RELATIONSHIP_VARIABLE_NAME, modelItemDslContext.getModelItem()); + } + } + + // bind a context object + StructurizrDslScriptContext scriptContext = new StructurizrDslScriptContext(dslFile, getWorkspace(), parameters); + bindings.put(CONTEXT_VARIABLE_NAME, scriptContext); + + // and any custom parameters + for (String name : parameters.keySet()) { + bindings.put(name, parameters.get(name)); + } + + engine.eval(script.toString(), bindings); + } else { + throw new RuntimeException("Could not load a scripting engine for extension \"" + extension + "\""); + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ScriptParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ScriptParser.java new file mode 100644 index 000000000..6e11a0764 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ScriptParser.java @@ -0,0 +1,66 @@ +package com.structurizr.dsl; + +final class ScriptParser extends AbstractParser { + + private static final String EXTERNAL_GRAMMAR = "!script "; + private static final String INLINE_GRAMMAR = "!script "; + + private static final int FILENAME_INDEX = 1; + private static final int LANGUAGE_INDEX = 1; + + private final static int PARAMETER_NAME_INDEX = 0; + private final static int PARAMETER_VALUE_INDEX = 1; + + boolean isInlineScript(Tokens tokens) { + return + DslContext.CONTEXT_START_TOKEN.equalsIgnoreCase(tokens.get(tokens.size()-1)) && + tokens.includes(LANGUAGE_INDEX) && + InlineScriptDslContext.SUPPORTED_LANGUAGES.containsKey(tokens.get(LANGUAGE_INDEX).toLowerCase()); + } + + String parseExternal(Tokens tokens) { + // !script + + if (tokens.hasMoreThan(FILENAME_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + EXTERNAL_GRAMMAR); + } + + if (!tokens.includes(FILENAME_INDEX)) { + throw new RuntimeException("Expected: " + EXTERNAL_GRAMMAR); + } + + return tokens.get(FILENAME_INDEX); + } + + void parseParameter(ExternalScriptDslContext context, Tokens tokens) { + // + + if (tokens.hasMoreThan(PARAMETER_VALUE_INDEX)) { + throw new RuntimeException("Too many tokens, expected: "); + } + + if (tokens.size() != 2) { + throw new RuntimeException("Expected: "); + } + + String name = tokens.get(PARAMETER_NAME_INDEX); + String value = tokens.get(PARAMETER_VALUE_INDEX); + + context.addParameter(name, value); + } + + String parseInline(Tokens tokens) { + // !script + + if (tokens.hasMoreThan(LANGUAGE_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + INLINE_GRAMMAR); + } + + if (!tokens.includes(LANGUAGE_INDEX)) { + throw new RuntimeException("Expected: " + INLINE_GRAMMAR); + } + + return tokens.get(LANGUAGE_INDEX); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemDslContext.java new file mode 100644 index 000000000..e85280d0c --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemDslContext.java @@ -0,0 +1,51 @@ +package com.structurizr.dsl; + +import com.structurizr.model.GroupableElement; +import com.structurizr.model.ModelItem; +import com.structurizr.model.SoftwareSystem; + +final class SoftwareSystemDslContext extends GroupableElementDslContext { + + private SoftwareSystem softwareSystem; + + SoftwareSystemDslContext(SoftwareSystem softwareSystem) { + this(softwareSystem, null); + } + + SoftwareSystemDslContext(SoftwareSystem softwareSystem, ElementGroup group) { + super(group); + + this.softwareSystem = softwareSystem; + } + + SoftwareSystem getSoftwareSystem() { + return softwareSystem; + } + + @Override + ModelItem getModelItem() { + return getSoftwareSystem(); + } + + @Override + GroupableElement getElement() { + return softwareSystem; + } + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.DOCS_TOKEN, + StructurizrDslTokens.ADRS_TOKEN, + StructurizrDslTokens.GROUP_TOKEN, + StructurizrDslTokens.CONTAINER_TOKEN, + StructurizrDslTokens.DESCRIPTION_TOKEN, + StructurizrDslTokens.TAGS_TOKEN, + StructurizrDslTokens.URL_TOKEN, + StructurizrDslTokens.PROPERTIES_TOKEN, + StructurizrDslTokens.PERSPECTIVES_TOKEN, + StructurizrDslTokens.RELATIONSHIP_TOKEN + }; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemInstanceDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemInstanceDslContext.java new file mode 100644 index 000000000..9da1e6760 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemInstanceDslContext.java @@ -0,0 +1,42 @@ +package com.structurizr.dsl; + +import com.structurizr.model.SoftwareSystemInstance; +import com.structurizr.model.ModelItem; +import com.structurizr.model.StaticStructureElementInstance; + +final class SoftwareSystemInstanceDslContext extends StaticStructureElementInstanceDslContext { + + private SoftwareSystemInstance softwareSystemInstance; + + SoftwareSystemInstanceDslContext(SoftwareSystemInstance softwareSystemInstance) { + this.softwareSystemInstance = softwareSystemInstance; + } + + SoftwareSystemInstance getSoftwareSystemInstance() { + return softwareSystemInstance; + } + + @Override + ModelItem getModelItem() { + return getSoftwareSystemInstance(); + } + + @Override + StaticStructureElementInstance getElementInstance() { + return getSoftwareSystemInstance(); + } + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.RELATIONSHIP_TOKEN, + StructurizrDslTokens.DESCRIPTION_TOKEN, + StructurizrDslTokens.TAGS_TOKEN, + StructurizrDslTokens.URL_TOKEN, + StructurizrDslTokens.PROPERTIES_TOKEN, + StructurizrDslTokens.PERSPECTIVES_TOKEN, + StructurizrDslTokens.HEALTH_CHECK_TOKEN + }; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemInstanceParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemInstanceParser.java new file mode 100644 index 000000000..dee362c64 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemInstanceParser.java @@ -0,0 +1,65 @@ +package com.structurizr.dsl; + +import com.structurizr.model.*; + +import java.util.HashSet; +import java.util.Set; + +final class SoftwareSystemInstanceParser extends AbstractParser { + + private static final String GRAMMAR = "softwareSystemInstance [deploymentGroups] [tags]"; + + private static final int IDENTIFIER_INDEX = 1; + private static final int DEPLOYMENT_GROUPS_TOKEN = 2; + private static final int TAGS_INDEX = 3; + + SoftwareSystemInstance parse(DeploymentNodeDslContext context, Tokens tokens) { + // softwareSystemInstance [tags] + // softwareSystemInstance [deploymentGroup] [tags] + + if (tokens.hasMoreThan(TAGS_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } + + if (!tokens.includes(IDENTIFIER_INDEX)) { + throw new RuntimeException("Expected: " + GRAMMAR); + } + + String softwareSystemIdentifier = tokens.get(IDENTIFIER_INDEX); + + Element element = context.getElement(softwareSystemIdentifier, SoftwareSystem.class); + if (element == null) { + throw new RuntimeException("The software system \"" + softwareSystemIdentifier + "\" does not exist"); + } + + DeploymentNode deploymentNode = context.getDeploymentNode(); + + Set deploymentGroups = new HashSet<>(); + if (tokens.includes(DEPLOYMENT_GROUPS_TOKEN)) { + String token = tokens.get(DEPLOYMENT_GROUPS_TOKEN); + + String[] deploymentGroupReferences = token.split(","); + for (String deploymentGroupReference : deploymentGroupReferences) { + Element e = context.getElement(deploymentGroupReference); + if (e instanceof DeploymentGroup) { + deploymentGroups.add(e.getName()); + } + } + } + + SoftwareSystemInstance softwareSystemInstance = deploymentNode.add((SoftwareSystem)element, deploymentGroups.toArray(new String[]{})); + + if (tokens.includes(TAGS_INDEX)) { + String tags = tokens.get(TAGS_INDEX); + softwareSystemInstance.addTags(tags.split(",")); + } + + if (context.hasGroup()) { + softwareSystemInstance.setGroup(context.getGroup().getName()); + context.getGroup().addElement(softwareSystemInstance); + } + + return softwareSystemInstance; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemParser.java new file mode 100644 index 000000000..9e3214bbd --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemParser.java @@ -0,0 +1,59 @@ +package com.structurizr.dsl; + +import com.structurizr.model.Location; +import com.structurizr.model.SoftwareSystem; + +final class SoftwareSystemParser extends AbstractParser { + + private static final String GRAMMAR = "softwareSystem [description] [tags]"; + + private final static int NAME_INDEX = 1; + private final static int DESCRIPTION_INDEX = 2; + private final static int TAGS_INDEX = 3; + + SoftwareSystem parse(GroupableDslContext context, Tokens tokens) { + // softwareSystem [description] [tags] + + if (tokens.hasMoreThan(TAGS_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } + + if (!tokens.includes(NAME_INDEX)) { + throw new RuntimeException("Expected: " + GRAMMAR); + } + + SoftwareSystem softwareSystem = null; + String name = tokens.get(NAME_INDEX); + + if (context.isExtendingWorkspace()) { + softwareSystem = context.getWorkspace().getModel().getSoftwareSystemWithName(name); + } + + if (softwareSystem == null) { + softwareSystem = context.getWorkspace().getModel().addSoftwareSystem(name); + } + + String description = ""; + if (tokens.includes(DESCRIPTION_INDEX)) { + description = tokens.get(DESCRIPTION_INDEX); + softwareSystem.setDescription(description); + } + + if (tokens.includes(TAGS_INDEX)) { + String tags = tokens.get(TAGS_INDEX); + softwareSystem.addTags(tags.split(",")); + } + + if (context instanceof EnterpriseDslContext) { + softwareSystem.setLocation(Location.Internal); + } + + if (context.hasGroup()) { + softwareSystem.setGroup(context.getGroup().getName()); + context.getGroup().addElement(softwareSystem); + } + + return softwareSystem; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StaticStructureElementInstanceDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StaticStructureElementInstanceDslContext.java new file mode 100644 index 000000000..1a01b973d --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StaticStructureElementInstanceDslContext.java @@ -0,0 +1,9 @@ +package com.structurizr.dsl; + +import com.structurizr.model.StaticStructureElementInstance; + +abstract class StaticStructureElementInstanceDslContext extends ModelItemDslContext { + + abstract StaticStructureElementInstance getElementInstance(); + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StaticViewAnimationDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StaticViewAnimationDslContext.java new file mode 100644 index 000000000..641575692 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StaticViewAnimationDslContext.java @@ -0,0 +1,26 @@ +package com.structurizr.dsl; + +import com.structurizr.view.StaticView; + +class StaticViewAnimationDslContext extends DslContext { + + private StaticView view; + + StaticViewAnimationDslContext(StaticView view) { + super(); + + this.view = view; + } + + StaticView getView() { + return view; + } + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.ANIMATION_STEP_IN_VIEW_TOKEN + }; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StaticViewAnimationStepParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StaticViewAnimationStepParser.java new file mode 100644 index 000000000..04f76a118 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StaticViewAnimationStepParser.java @@ -0,0 +1,50 @@ +package com.structurizr.dsl; + +import com.structurizr.model.Element; +import com.structurizr.view.StaticView; + +import java.util.ArrayList; +import java.util.List; + +final class StaticViewAnimationStepParser extends AbstractParser { + + void parse(StaticViewDslContext context, Tokens tokens) { + // animationStep [identifier...] + + if (!tokens.includes(1)) { + throw new RuntimeException("Expected: animationStep [identifier...]"); + } + + parse(context, context.getView(), tokens, 1); + } + + void parse(StaticViewAnimationDslContext context, Tokens tokens) { + // [identifier...] + + if (!tokens.includes(0)) { + throw new RuntimeException("Expected: [identifier...]"); + } + + parse(context, context.getView(), tokens, 0); + } + + void parse(DslContext context, StaticView view, Tokens tokens, int startIndex) { + // [identifier...] + + List elements = new ArrayList<>(); + + for (int i = startIndex; i < tokens.size(); i++) { + String elementIdentifier = tokens.get(i); + + Element element = context.getElement(elementIdentifier); + if (element == null) { + throw new RuntimeException("The element \"" + elementIdentifier + "\" does not exist"); + } + + elements.add(element); + } + + view.addAnimation(elements.toArray(new Element[0])); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StaticViewContentParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StaticViewContentParser.java new file mode 100644 index 000000000..85d7dec9d --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StaticViewContentParser.java @@ -0,0 +1,117 @@ +package com.structurizr.dsl; + +import com.structurizr.model.*; +import com.structurizr.util.StringUtils; +import com.structurizr.view.ComponentView; +import com.structurizr.view.ContainerView; +import com.structurizr.view.ElementNotPermittedInViewException; +import com.structurizr.view.StaticView; + +final class StaticViewContentParser extends ModelViewContentParser { + + private static final int FIRST_IDENTIFIER_INDEX = 1; + + void parseInclude(StaticViewDslContext context, Tokens tokens) { + if (!tokens.includes(FIRST_IDENTIFIER_INDEX)) { + throw new RuntimeException("Expected: include <*|identifier|expression> [*|identifier|expression...]"); + } + + StaticView view = context.getView(); + + // include [identifier|expression...] + for (int i = FIRST_IDENTIFIER_INDEX; i < tokens.size(); i++) { + String token = tokens.get(i); + + if (token.equals(WILDCARD) || token.equals(ELEMENT_WILDCARD)) { + // include * or include element==* + view.addDefaultElements(); + } else if (isExpression(token)) { + new StaticViewExpressionParser().parseExpression(token, context).forEach(mi -> addModelItemToView(mi, view, null)); + } else { + new StaticViewExpressionParser().parseIdentifier(token, context).forEach(mi -> addModelItemToView(mi, view, token)); + } + } + } + + void parseExclude(StaticViewDslContext context, Tokens tokens) { + if (!tokens.includes(FIRST_IDENTIFIER_INDEX)) { + throw new RuntimeException("Expected: exclude [identifier|expression...]"); + } + + StaticView view = context.getView(); + + // exclude [identifier|expression...] + for (int i = FIRST_IDENTIFIER_INDEX; i < tokens.size(); i++) { + String token = tokens.get(i); + + if (isExpression(token)) { + new StaticViewExpressionParser().parseExpression(token, context).forEach(mi -> removeModelItemFromView(mi, view)); + } else { + new StaticViewExpressionParser().parseIdentifier(token, context).forEach(mi -> removeModelItemFromView(mi, view)); + } + } + } + + private void addModelItemToView(ModelItem modelItem, StaticView view, String identifier) { + if (modelItem instanceof Element) { + addElementToView((Element)modelItem, view, identifier); + } else { + addRelationshipToView((Relationship)modelItem, view); + } + } + + private void addElementToView(Element element, StaticView view, String identifier) { + try { + if (element instanceof CustomElement) { + view.add((CustomElement) element); + } else if (element instanceof Person) { + view.add((Person) element); + } else if (element instanceof SoftwareSystem) { + view.add((SoftwareSystem) element); + } else if (element instanceof Container && (view instanceof ContainerView)) { + ((ContainerView) view).add((Container) element); + } else if (element instanceof Container && (view instanceof ComponentView)) { + ((ComponentView) view).add((Container) element); + } else if (element instanceof Component && (view instanceof ComponentView)) { + ((ComponentView) view).add((Component) element); + } else { + if (!StringUtils.isNullOrEmpty(identifier)) { + throw new RuntimeException("The element \"" + identifier + "\" can not be added to this type of view"); + } + } + } catch (ElementNotPermittedInViewException e) { + // ignore + } + } + + private void removeModelItemFromView(ModelItem modelItem, StaticView view) { + if (modelItem instanceof Element) { + removeElementFromView((Element)modelItem, view); + } else { + removeRelationshipFromView((Relationship)modelItem, view); + } + } + + private void removeElementFromView(Element element, StaticView view) { + if (element instanceof CustomElement) { + view.remove((CustomElement) element); + } else if (element instanceof Person) { + view.remove((Person) element); + } else if (element instanceof SoftwareSystem) { + view.remove((SoftwareSystem) element); + } else if (element instanceof Container && (view instanceof ContainerView)) { + ((ContainerView) view).remove((Container) element); + } else if (element instanceof Container && (view instanceof ComponentView)) { + ((ComponentView) view).remove((Container) element); + } else if (element instanceof Component && (view instanceof ComponentView)) { + ((ComponentView) view).remove((Component) element); + } + } + + private void addRelationshipToView(Relationship relationship, StaticView view) { + if (view.isElementInView(relationship.getSource()) && view.isElementInView(relationship.getDestination())) { + view.add(relationship); + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StaticViewDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StaticViewDslContext.java new file mode 100644 index 000000000..01c0c5307 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StaticViewDslContext.java @@ -0,0 +1,29 @@ +package com.structurizr.dsl; + +import com.structurizr.view.StaticView; + +class StaticViewDslContext extends ModelViewDslContext { + + StaticViewDslContext(StaticView view) { + super(view); + } + + StaticView getView() { + return (StaticView)super.getView(); + } + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.INCLUDE_IN_VIEW_TOKEN, + StructurizrDslTokens.EXCLUDE_IN_VIEW_TOKEN, + StructurizrDslTokens.AUTOLAYOUT_VIEW_TOKEN, + StructurizrDslTokens.DEFAULT_VIEW_TOKEN, + StructurizrDslTokens.ANIMATION_IN_VIEW_TOKEN, + StructurizrDslTokens.VIEW_TITLE_TOKEN, + StructurizrDslTokens.VIEW_DESCRIPTION_TOKEN, + StructurizrDslTokens.PROPERTIES_TOKEN + }; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StaticViewExpressionParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StaticViewExpressionParser.java new file mode 100644 index 000000000..756dd3799 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StaticViewExpressionParser.java @@ -0,0 +1,64 @@ +package com.structurizr.dsl; + +import com.structurizr.model.*; + +import java.util.LinkedHashSet; +import java.util.Set; + +import static com.structurizr.dsl.StructurizrDslExpressions.ELEMENT_TYPE_EQUALS_EXPRESSION; + +final class StaticViewExpressionParser extends AbstractExpressionParser { + + @Override + protected Set evaluateElementTypeExpression(String expr, DslContext context) { + Set elements = new LinkedHashSet<>(); + + String type = expr.substring(ELEMENT_TYPE_EQUALS_EXPRESSION.length()); + switch (type.toLowerCase()) { + case "custom": + context.getWorkspace().getModel().getElements().stream().filter(e -> e instanceof CustomElement).forEach(elements::add); + break; + case "person": + context.getWorkspace().getModel().getElements().stream().filter(e -> e instanceof Person).forEach(elements::add); + break; + case "softwaresystem": + context.getWorkspace().getModel().getElements().stream().filter(e -> e instanceof SoftwareSystem).forEach(elements::add); + break; + case "container": + context.getWorkspace().getModel().getElements().stream().filter(e -> e instanceof Container).forEach(elements::add); + break; + case "component": + context.getWorkspace().getModel().getElements().stream().filter(e -> e instanceof Component).forEach(elements::add); + break; + default: + throw new RuntimeException("The element type of \"" + type + "\" is not valid for this view"); + } + + return elements; + } + + protected Set findAfferentCouplings(Element element) { + Set elements = new LinkedHashSet<>(); + + elements.addAll(findAfferentCouplings(element, CustomElement.class)); + elements.addAll(findAfferentCouplings(element, Person.class)); + elements.addAll(findAfferentCouplings(element, SoftwareSystem.class)); + elements.addAll(findAfferentCouplings(element, Container.class)); + elements.addAll(findAfferentCouplings(element, Component.class)); + + return elements; + } + + protected Set findEfferentCouplings(Element element) { + Set elements = new LinkedHashSet<>(); + + elements.addAll(findEfferentCouplings(element, CustomElement.class)); + elements.addAll(findEfferentCouplings(element, Person.class)); + elements.addAll(findEfferentCouplings(element, SoftwareSystem.class)); + elements.addAll(findEfferentCouplings(element, Container.class)); + elements.addAll(findEfferentCouplings(element, Component.class)); + + return elements; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslExpressions.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslExpressions.java new file mode 100644 index 000000000..8b205bf28 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslExpressions.java @@ -0,0 +1,22 @@ +package com.structurizr.dsl; + +class StructurizrDslExpressions { + + static final String ELEMENT_TYPE_EQUALS_EXPRESSION = "element.type=="; + static final String ELEMENT_TAG_EQUALS_EXPRESSION = "element.tag=="; + static final String ELEMENT_TAG_NOT_EQUALS_EXPRESSION = "element.tag!="; + + static final String ELEMENT_EQUALS_EXPRESSION = "element=="; + static final String ELEMENT_PARENT_EQUALS_EXPRESSION = "element.parent=="; + + static final String RELATIONSHIP_TAG_EQUALS_EXPRESSION = "relationship.tag=="; + static final String RELATIONSHIP_TAG_NOT_EQUALS_EXPRESSION = "relationship.tag!="; + + static final String RELATIONSHIP_SOURCE_EQUALS_EXPRESSION = "relationship.source=="; + static final String RELATIONSHIP_DESTINATION_EQUALS_EXPRESSION = "relationship.destination=="; + + static final String RELATIONSHIP_EQUALS_EXPRESSION = "relationship=="; + + static final String RELATIONSHIP = "->"; + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java new file mode 100644 index 000000000..17fbedb4d --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -0,0 +1,1033 @@ +package com.structurizr.dsl; + +import com.structurizr.Workspace; +import com.structurizr.model.*; +import com.structurizr.util.StringUtils; +import com.structurizr.view.*; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + * Main DSL parser class - forms the API for using the parser. + */ +public final class StructurizrDslParser extends StructurizrDslTokens { + + private static final String BOM = "\uFEFF"; + + private static final Pattern EMPTY_LINE_PATTERN = Pattern.compile("^\\s*"); + + private static final Pattern COMMENT_PATTERN = Pattern.compile("^\\s*?(//|#).*$"); + private static final String MULTI_LINE_COMMENT_START_TOKEN = "/*"; + private static final String MULTI_LINE_COMMENT_END_TOKEN = "*/"; + private static final String MULTI_LINE_SEPARATOR = "\\"; + + private static final Pattern STRING_SUBSTITUTION_PATTERN = Pattern.compile("(\\$\\{[a-zA-Z0-9-_.]+?})"); + + private static final String STRUCTURIZR_DSL_IDENTIFIER_PROPERTY_NAME = "structurizr.dsl.identifier"; + + private Charset characterEncoding = StandardCharsets.UTF_8; + private IdentifierScope identifierScope = IdentifierScope.Flat; + private Stack contextStack; + private Set parsedTokens = new HashSet<>(); + private IdentifiersRegister identifiersRegister; + private Map constants; + + private List dslSourceLines = new ArrayList<>(); + private Workspace workspace; + private boolean extendingWorkspace = false; + + private boolean restricted = false; + + /** + * Creates a new instance of the parser. + */ + public StructurizrDslParser() { + contextStack = new Stack<>(); + identifiersRegister = new IdentifiersRegister(); + constants = new HashMap<>(); + } + + /** + * Provides a way to change the character encoding used by the DSL parser. + * + * @param characterEncoding a Charset instance + */ + public void setCharacterEncoding(Charset characterEncoding) { + if (characterEncoding == null) { + throw new IllegalArgumentException("A character encoding must be specified"); + } + + this.characterEncoding = characterEncoding; + } + + IdentifierScope getIdentifierScope() { + return identifierScope; + } + + void setIdentifierScope(IdentifierScope identifierScope) { + if (identifierScope == null) { + identifierScope = IdentifierScope.Flat; + } + + this.identifierScope = identifierScope; + this.identifiersRegister.setIdentifierScope(identifierScope); + } + + /** + * Sets whether to run this parser in restricted mode (this stops !include, !docs, !adrs from working). + * + * @param restricted true for restricted mode, false otherwise + */ + public void setRestricted(boolean restricted) { + this.restricted = restricted; + } + + /** + * Gets the workspace that has been created by parsing the Structurizr DSL. + * + * @return a Workspace instance + */ + public Workspace getWorkspace() { + if (workspace != null) { + DslUtils.setDsl(workspace, getParsedDsl()); + } + + return workspace; + } + + private String getParsedDsl() { + StringBuilder buf = new StringBuilder(); + + for (String line : dslSourceLines) { + buf.append(line); + buf.append(System.lineSeparator()); + } + + return buf.toString(); + } + + void parse(DslParserContext context, File path) throws StructurizrDslParserException { + parse(path); + + context.copyFrom(identifiersRegister); + } + + /** + * Parses the specified Structurizr DSL file(s), adding the parsed content to the workspace. + * If "path" represents a single file, that single file will be parsed. + * If "path" represents a directory, all files in that directory (recursively) will be parsed. + * + * @param path a File object representing a file or directory + * @throws StructurizrDslParserException when something goes wrong + */ + public void parse(File path) throws StructurizrDslParserException { + if (path == null) { + throw new StructurizrDslParserException("A file must be specified"); + } + + if (!path.exists()) { + throw new StructurizrDslParserException("The file at " + path.getAbsolutePath() + " does not exist"); + } + + List files = FileUtils.findFiles(path); + try { + for (File file : files) { + parse(Files.readAllLines(file.toPath(), characterEncoding), file); + } + } catch (IOException e) { + throw new StructurizrDslParserException(e.getMessage()); + } + } + + void parse(DslParserContext context, String dsl) throws StructurizrDslParserException { + parse(dsl); + + context.copyFrom(identifiersRegister); + } + + /** + * Parses the specified Structurizr DSL fragment, adding the parsed content to the workspace. + * + * @param dsl a DSL fragment + * @throws StructurizrDslParserException when something goes wrong + */ + public void parse(String dsl) throws StructurizrDslParserException { + if (StringUtils.isNullOrEmpty(dsl)) { + throw new StructurizrDslParserException("A DSL fragment must be specified"); + } + + List lines = Arrays.asList(dsl.split("\\r?\\n")); + parse(lines, new File(".")); + } + + private List preProcessLines(List lines) { + List dslLines = new ArrayList<>(); + + int lineNumber = 1; + StringBuilder buf = new StringBuilder(); + boolean lineComplete = true; + + for (String line : lines) { + if (line.endsWith(MULTI_LINE_SEPARATOR)) { + buf.append(line, 0, line.length()-1); + lineComplete = false; + } else { + if (lineComplete) { + buf.append(line); + } else { + buf.append(line.stripLeading()); + lineComplete = true; + } + } + + if (lineComplete) { + dslLines.add(new DslLine(buf.toString(), lineNumber)); + buf = new StringBuilder(); + } + + lineNumber++; + } + + return dslLines; + } + + void parse(List lines, File dslFile) throws StructurizrDslParserException { + List dslLines = preProcessLines(lines); + + for (DslLine dslLine : dslLines) { + boolean includeInDslSourceLines = true; + + String line = dslLine.getSource(); + + if (line.startsWith(BOM)) { + // this caters for files encoded as "UTF-8 with BOM" + line = line.substring(1); + } + + try { + if (EMPTY_LINE_PATTERN.matcher(line).matches()) { + // do nothing + } else if (COMMENT_PATTERN.matcher(line).matches()) { + // do nothing + } else if (inContext(InlineScriptDslContext.class)) { + if (DslContext.CONTEXT_END_TOKEN.equals(line.trim())) { + endContext(); + } else { + getContext(InlineScriptDslContext.class).addLine(line); + } + } else { + List listOfTokens = new Tokenizer().tokenize(line); + listOfTokens = listOfTokens.stream().map(this::substituteStrings).collect(Collectors.toList()); + + Tokens tokens = new Tokens(listOfTokens); + + String identifier = null; + if (tokens.size() > 3 && ASSIGNMENT_OPERATOR_TOKEN.equals(tokens.get(1))) { + identifier = tokens.get(0); + identifiersRegister.validateIdentifierName(identifier); + + tokens = new Tokens(listOfTokens.subList(2, listOfTokens.size())); + } + + String firstToken = tokens.get(0); + + if (line.trim().startsWith(MULTI_LINE_COMMENT_START_TOKEN) && line.trim().endsWith(MULTI_LINE_COMMENT_END_TOKEN)) { + // do nothing + } else if (firstToken.startsWith(MULTI_LINE_COMMENT_START_TOKEN)) { + startContext(new CommentDslContext()); + + } else if (inContext(CommentDslContext.class) && line.trim().endsWith(MULTI_LINE_COMMENT_END_TOKEN)) { + endContext(); + + } else if (inContext(CommentDslContext.class)) { + // do nothing + + } else if (DslContext.CONTEXT_END_TOKEN.equals(tokens.get(0))) { + endContext(); + + } else if (INCLUDE_FILE_TOKEN.equalsIgnoreCase(firstToken)) { + if (!restricted || tokens.get(1).startsWith("https://")) { + String leadingSpace = line.substring(0, line.indexOf(INCLUDE_FILE_TOKEN)); + + IncludedDslContext context = new IncludedDslContext(dslFile); + new IncludeParser().parse(context, tokens); + for (IncludedFile includedFile : context.getFiles()) { + List paddedLines = new ArrayList<>(); + for (String unpaddedLine : includedFile.getLines()) { + paddedLines.add(leadingSpace + unpaddedLine); + } + + parse(paddedLines, includedFile.getFile()); + } + + includeInDslSourceLines = false; + } + + } else if (PLUGIN_TOKEN.equalsIgnoreCase(firstToken)) { + if (!restricted) { + String fullyQualifiedClassName = new PluginParser().parse(getContext(), tokens.withoutContextStartToken()); + startContext(new PluginDslContext(fullyQualifiedClassName, dslFile, this)); + if (!shouldStartContext(tokens)) { + // run the plugin immediately, without looking for parameters + endContext(); + } + } + + } else if (inContext(PluginDslContext.class)) { + new PluginParser().parseParameter(getContext(PluginDslContext.class), tokens); + + } else if (SCRIPT_TOKEN.equalsIgnoreCase(firstToken)) { + if (!restricted) { + ScriptParser scriptParser = new ScriptParser(); + if (scriptParser.isInlineScript(tokens)) { + String language = scriptParser.parseInline(tokens.withoutContextStartToken()); + startContext(new InlineScriptDslContext(getContext(), dslFile, language)); + } else { + String filename = scriptParser.parseExternal(tokens.withoutContextStartToken()); + startContext(new ExternalScriptDslContext(getContext(), dslFile, filename)); + + if (shouldStartContext(tokens)) { + // we'll wait for parameters before executing the script + } else { + endContext(); + } + } + } + + } else if (inContext(ExternalScriptDslContext.class)) { + new ScriptParser().parseParameter(getContext(ExternalScriptDslContext.class), tokens); + + } else if (tokens.size() > 2 && RELATIONSHIP_TOKEN.equals(tokens.get(1)) && (inContext(ModelDslContext.class) || inContext(EnterpriseDslContext.class) || inContext(CustomElementDslContext.class) || inContext(PersonDslContext.class) || inContext(SoftwareSystemDslContext.class) || inContext(ContainerDslContext.class) || inContext(ComponentDslContext.class) || inContext(DeploymentEnvironmentDslContext.class) || inContext(DeploymentNodeDslContext.class) || inContext(InfrastructureNodeDslContext.class) || inContext(SoftwareSystemInstanceDslContext.class) || inContext(ContainerInstanceDslContext.class))) { + Relationship relationship = new ExplicitRelationshipParser().parse(getContext(), tokens.withoutContextStartToken()); + + if (shouldStartContext(tokens)) { + startContext(new RelationshipDslContext(relationship)); + } + + registerIdentifier(identifier, relationship); + + } else if (tokens.size() >= 2 && RELATIONSHIP_TOKEN.equals(tokens.get(0)) && (inContext(CustomElementDslContext.class) || inContext(PersonDslContext.class) || inContext(SoftwareSystemDslContext.class) || inContext(ContainerDslContext.class) || inContext(ComponentDslContext.class) || inContext(DeploymentNodeDslContext.class) || inContext(InfrastructureNodeDslContext.class) || inContext(SoftwareSystemInstanceDslContext.class) || inContext(ContainerInstanceDslContext.class))) { + Relationship relationship = new ImplicitRelationshipParser().parse(getContext(ModelItemDslContext.class), tokens.withoutContextStartToken()); + + if (shouldStartContext(tokens)) { + startContext(new RelationshipDslContext(relationship)); + } + + registerIdentifier(identifier, relationship); + + } else if ((REF_TOKEN.equalsIgnoreCase(firstToken) || EXTEND_TOKEN.equalsIgnoreCase(firstToken)) && (inContext(ModelDslContext.class))) { + ModelItem modelItem = new RefParser().parse(getContext(), tokens.withoutContextStartToken()); + + if (shouldStartContext(tokens)) { + if (modelItem instanceof Person) { + startContext(new PersonDslContext((Person)modelItem)); + } else if (modelItem instanceof SoftwareSystem) { + startContext(new SoftwareSystemDslContext((SoftwareSystem)modelItem)); + } else if (modelItem instanceof Container) { + startContext(new ContainerDslContext((Container) modelItem)); + } else if (modelItem instanceof Component) { + startContext(new ComponentDslContext((Component)modelItem)); + } else if (modelItem instanceof DeploymentEnvironment) { + startContext(new DeploymentEnvironmentDslContext(((DeploymentEnvironment)modelItem).getName())); + } else if (modelItem instanceof DeploymentNode) { + startContext(new DeploymentNodeDslContext((DeploymentNode)modelItem)); + } else if (modelItem instanceof InfrastructureNode) { + startContext(new InfrastructureNodeDslContext((InfrastructureNode)modelItem)); + } else if (modelItem instanceof SoftwareSystemInstance) { + startContext(new SoftwareSystemInstanceDslContext((SoftwareSystemInstance)modelItem)); + } else if (modelItem instanceof ContainerInstance) { + startContext(new ContainerInstanceDslContext((ContainerInstance)modelItem)); + } else if (modelItem instanceof Relationship) { + startContext(new RelationshipDslContext((Relationship)modelItem)); + } + } + + if (!StringUtils.isNullOrEmpty(identifier)) { + if (modelItem instanceof Element) { + registerIdentifier(identifier, (Element)modelItem); + } else if (modelItem instanceof Relationship) { + registerIdentifier(identifier, (Relationship)modelItem); + } + } + + } else if (CUSTOM_ELEMENT_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ModelDslContext.class))) { + CustomElement customElement = new CustomElementParser().parse(getContext(GroupableDslContext.class), tokens.withoutContextStartToken()); + + if (shouldStartContext(tokens)) { + startContext(new CustomElementDslContext(customElement)); + } + + registerIdentifier(identifier, customElement); + + } else if (PERSON_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ModelDslContext.class) || inContext(EnterpriseDslContext.class))) { + Person person = new PersonParser().parse(getContext(GroupableDslContext.class), tokens.withoutContextStartToken()); + + if (shouldStartContext(tokens)) { + startContext(new PersonDslContext(person)); + } + + registerIdentifier(identifier, person); + + } else if (SOFTWARE_SYSTEM_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ModelDslContext.class) || inContext(EnterpriseDslContext.class))) { + SoftwareSystem softwareSystem = new SoftwareSystemParser().parse(getContext(GroupableDslContext.class), tokens.withoutContextStartToken()); + + if (shouldStartContext(tokens)) { + startContext(new SoftwareSystemDslContext(softwareSystem)); + } + + registerIdentifier(identifier, softwareSystem); + + } else if (CONTAINER_TOKEN.equalsIgnoreCase(firstToken) && inContext(SoftwareSystemDslContext.class)) { + Container container = new ContainerParser().parse(getContext(SoftwareSystemDslContext.class), tokens.withoutContextStartToken()); + + if (shouldStartContext(tokens)) { + startContext(new ContainerDslContext(container)); + } + + registerIdentifier(identifier, container); + + } else if (COMPONENT_TOKEN.equalsIgnoreCase(firstToken) && inContext(ContainerDslContext.class)) { + Component component = new ComponentParser().parse(getContext(ContainerDslContext.class), tokens.withoutContextStartToken()); + + if (shouldStartContext(tokens)) { + startContext(new ComponentDslContext(component)); + } + + registerIdentifier(identifier, component); + + } else if (GROUP_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelDslContext.class)) { + ElementGroup group = new GroupParser().parse(getContext(ModelDslContext.class), tokens); + + startContext(new ModelDslContext(group)); + registerIdentifier(identifier, group); + } else if (GROUP_TOKEN.equalsIgnoreCase(firstToken) && inContext(EnterpriseDslContext.class)) { + ElementGroup group = new GroupParser().parse(getContext(EnterpriseDslContext.class), tokens); + + startContext(new EnterpriseDslContext(group)); + registerIdentifier(identifier, group); + } else if (GROUP_TOKEN.equalsIgnoreCase(firstToken) && inContext(SoftwareSystemDslContext.class)) { + ElementGroup group = new GroupParser().parse(getContext(SoftwareSystemDslContext.class), tokens); + + SoftwareSystem softwareSystem = getContext(SoftwareSystemDslContext.class).getSoftwareSystem(); + group.setParent(softwareSystem); + startContext(new SoftwareSystemDslContext(softwareSystem, group)); + registerIdentifier(identifier, group); + } else if (GROUP_TOKEN.equalsIgnoreCase(firstToken) && inContext(ContainerDslContext.class)) { + ElementGroup group = new GroupParser().parse(getContext(ContainerDslContext.class), tokens); + + Container container = getContext(ContainerDslContext.class).getContainer(); + group.setParent(container); + startContext(new ContainerDslContext(container, group)); + registerIdentifier(identifier, group); + } else if (GROUP_TOKEN.equalsIgnoreCase(firstToken) && inContext(DeploymentEnvironmentDslContext.class)) { + ElementGroup group = new GroupParser().parse(getContext(DeploymentEnvironmentDslContext.class), tokens); + + String environment = getContext(DeploymentEnvironmentDslContext.class).getEnvironment(); + startContext(new DeploymentEnvironmentDslContext(environment, group)); + registerIdentifier(identifier, group); + } else if (GROUP_TOKEN.equalsIgnoreCase(firstToken) && inContext(DeploymentNodeDslContext.class)) { + ElementGroup group = new GroupParser().parse(getContext(DeploymentNodeDslContext.class), tokens); + + DeploymentNode deploymentNode = getContext(DeploymentNodeDslContext.class).getDeploymentNode(); + startContext(new DeploymentNodeDslContext(deploymentNode, group)); + registerIdentifier(identifier, group); + } else if (TAGS_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelItemDslContext.class) && !getContext(ModelItemDslContext.class).hasGroup()) { + new ModelItemParser().parseTags(getContext(ModelItemDslContext.class), tokens); + + } else if (DESCRIPTION_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelItemDslContext.class) && getContext(ModelItemDslContext.class).getModelItem() instanceof Element && !getContext(ModelItemDslContext.class).hasGroup()) { + new ModelItemParser().parseDescription(getContext(ModelItemDslContext.class), tokens); + + } else if (TECHNOLOGY_TOKEN.equalsIgnoreCase(firstToken) && inContext(ContainerDslContext.class) && !getContext(ContainerDslContext.class).hasGroup()) { + new ContainerParser().parseTechnology(getContext(ContainerDslContext.class), tokens); + + } else if (TECHNOLOGY_TOKEN.equalsIgnoreCase(firstToken) && inContext(ComponentDslContext.class) && !getContext(ComponentDslContext.class).hasGroup()) { + new ComponentParser().parseTechnology(getContext(ComponentDslContext.class), tokens); + + } else if (TECHNOLOGY_TOKEN.equalsIgnoreCase(firstToken) && inContext(DeploymentNodeDslContext.class)) { + new DeploymentNodeParser().parseTechnology(getContext(DeploymentNodeDslContext.class), tokens); + + } else if (TECHNOLOGY_TOKEN.equalsIgnoreCase(firstToken) && inContext(InfrastructureNodeDslContext.class)) { + new InfrastructureNodeParser().parseTechnology(getContext(InfrastructureNodeDslContext.class), tokens); + + } else if (INSTANCES_TOKEN.equalsIgnoreCase(firstToken) && inContext(DeploymentNodeDslContext.class)) { + new DeploymentNodeParser().parseInstances(getContext(DeploymentNodeDslContext.class), tokens); + + } else if (URL_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelItemDslContext.class) && !getContext(ModelItemDslContext.class).hasGroup()) { + new ModelItemParser().parseUrl(getContext(ModelItemDslContext.class), tokens); + + } else if (PROPERTIES_TOKEN.equalsIgnoreCase(firstToken) && inContext(WorkspaceDslContext.class)) { + startContext(new PropertiesDslContext(workspace)); + + } else if (PROPERTIES_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelDslContext.class)) { + startContext(new PropertiesDslContext(workspace.getModel())); + + } else if (PROPERTIES_TOKEN.equalsIgnoreCase(firstToken) && inContext(ConfigurationDslContext.class) && !getContext(ModelItemDslContext.class).hasGroup()) { + startContext(new PropertiesDslContext(getContext(ConfigurationDslContext.class).getWorkspace())); + + } else if (PROPERTIES_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelItemDslContext.class) && !getContext(ModelItemDslContext.class).hasGroup()) { + startContext(new PropertiesDslContext(getContext(ModelItemDslContext.class).getModelItem())); + + } else if (PROPERTIES_TOKEN.equalsIgnoreCase(firstToken) && inContext(ViewsDslContext.class)) { + startContext(new PropertiesDslContext(workspace.getViews().getConfiguration())); + + } else if (PROPERTIES_TOKEN.equalsIgnoreCase(firstToken) && inContext(ViewDslContext.class)) { + startContext(new PropertiesDslContext(getContext(ViewDslContext.class).getView())); + + } else if (PROPERTIES_TOKEN.equalsIgnoreCase(firstToken) && inContext(ElementStyleDslContext.class)) { + startContext(new PropertiesDslContext(getContext((ElementStyleDslContext.class)).getStyle())); + + } else if (PROPERTIES_TOKEN.equalsIgnoreCase(firstToken) && inContext(RelationshipStyleDslContext.class)) { + startContext(new PropertiesDslContext(getContext((RelationshipStyleDslContext.class)).getStyle())); + + } else if (inContext(PropertiesDslContext.class)) { + new PropertyParser().parse(getContext(PropertiesDslContext.class), tokens); + + } else if (PERSPECTIVES_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelItemDslContext.class) && !getContext(ModelItemDslContext.class).hasGroup()) { + startContext(new ModelItemPerspectivesDslContext(getContext(ModelItemDslContext.class).getModelItem())); + + } else if (inContext(ModelItemPerspectivesDslContext.class)) { + new ModelItemParser().parsePerspective(getContext(ModelItemPerspectivesDslContext.class), tokens); + + } else if (WORKSPACE_TOKEN.equalsIgnoreCase(firstToken) && contextStack.empty()) { + if (parsedTokens.contains(WORKSPACE_TOKEN)) { + throw new RuntimeException("Multiple workspaces are not permitted in a DSL definition"); + } + DslParserContext dslParserContext = new DslParserContext(dslFile, restricted); + dslParserContext.setIdentifierRegister(identifiersRegister); + + workspace = new WorkspaceParser().parse(dslParserContext, tokens.withoutContextStartToken()); + extendingWorkspace = !workspace.getModel().isEmpty(); + startContext(new WorkspaceDslContext()); + parsedTokens.add(WORKSPACE_TOKEN); + } else if (IMPLIED_RELATIONSHIPS_TOKEN.equalsIgnoreCase(firstToken) || IMPLIED_RELATIONSHIPS_TOKEN.substring(1).equalsIgnoreCase(firstToken)) { + new ImpliedRelationshipsParser().parse(getContext(), tokens); + + } else if (NAME_TOKEN.equalsIgnoreCase(firstToken) && inContext(WorkspaceDslContext.class)) { + new WorkspaceParser().parseName(getContext(), tokens); + + } else if (DESCRIPTION_TOKEN.equalsIgnoreCase(firstToken) && inContext(WorkspaceDslContext.class)) { + new WorkspaceParser().parseDescription(getContext(), tokens); + + } else if (MODEL_TOKEN.equalsIgnoreCase(firstToken) && inContext(WorkspaceDslContext.class)) { + if (parsedTokens.contains(MODEL_TOKEN)) { + throw new RuntimeException("Multiple models are not permitted in a DSL definition"); + } + + startContext(new ModelDslContext()); + parsedTokens.add(MODEL_TOKEN); + + } else if (VIEWS_TOKEN.equalsIgnoreCase(firstToken) && inContext(WorkspaceDslContext.class)) { + if (parsedTokens.contains(VIEWS_TOKEN)) { + throw new RuntimeException("Multiple view sets are not permitted in a DSL definition"); + } + + startContext(new ViewsDslContext()); + parsedTokens.add(VIEWS_TOKEN); + + } else if (BRANDING_TOKEN.equalsIgnoreCase(firstToken) && inContext(ViewsDslContext.class)) { + startContext(new BrandingDslContext(dslFile)); + + } else if (BRANDING_LOGO_TOKEN.equalsIgnoreCase(firstToken) && inContext(BrandingDslContext.class)) { + new BrandingParser().parseLogo(getContext(BrandingDslContext.class), tokens, restricted); + + } else if (BRANDING_FONT_TOKEN.equalsIgnoreCase(firstToken) && inContext(BrandingDslContext.class)) { + new BrandingParser().parseFont(getContext(BrandingDslContext.class), tokens); + + } else if (STYLES_TOKEN.equalsIgnoreCase(firstToken) && inContext(ViewsDslContext.class)) { + startContext(new StylesDslContext()); + + } else if (ELEMENT_STYLE_TOKEN.equalsIgnoreCase(firstToken) && inContext(StylesDslContext.class)) { + ElementStyle elementStyle = new ElementStyleParser().parseElementStyle(getContext(), tokens.withoutContextStartToken()); + startContext(new ElementStyleDslContext(elementStyle, dslFile)); + + } else if (ELEMENT_STYLE_BACKGROUND_TOKEN.equalsIgnoreCase(firstToken) && inContext(ElementStyleDslContext.class)) { + new ElementStyleParser().parseBackground(getContext(ElementStyleDslContext.class), tokens); + + } else if ((ELEMENT_STYLE_COLOUR_TOKEN.equalsIgnoreCase(firstToken) || ELEMENT_STYLE_COLOR_TOKEN.equalsIgnoreCase(firstToken)) && inContext(ElementStyleDslContext.class)) { + new ElementStyleParser().parseColour(getContext(ElementStyleDslContext.class), tokens); + + } else if (ELEMENT_STYLE_STROKE_TOKEN.equalsIgnoreCase(firstToken) && inContext(ElementStyleDslContext.class)) { + new ElementStyleParser().parseStroke(getContext(ElementStyleDslContext.class), tokens); + + } else if (ELEMENT_STYLE_STROKE_WIDTH_TOKEN.equalsIgnoreCase(firstToken) && inContext(ElementStyleDslContext.class)) { + new ElementStyleParser().parseStrokeWidth(getContext(ElementStyleDslContext.class), tokens); + + } else if (ELEMENT_STYLE_SHAPE_TOKEN.equalsIgnoreCase(firstToken) && inContext(ElementStyleDslContext.class)) { + new ElementStyleParser().parseShape(getContext(ElementStyleDslContext.class), tokens); + + } else if (ELEMENT_STYLE_BORDER_TOKEN.equalsIgnoreCase(firstToken) && inContext(ElementStyleDslContext.class)) { + new ElementStyleParser().parseBorder(getContext(ElementStyleDslContext.class), tokens); + + } else if (ELEMENT_STYLE_OPACITY_TOKEN.equalsIgnoreCase(firstToken) && inContext(ElementStyleDslContext.class)) { + new ElementStyleParser().parseOpacity(getContext(ElementStyleDslContext.class), tokens); + + } else if (ELEMENT_STYLE_WIDTH_TOKEN.equalsIgnoreCase(firstToken) && inContext(ElementStyleDslContext.class)) { + new ElementStyleParser().parseWidth(getContext(ElementStyleDslContext.class), tokens); + + } else if (ELEMENT_STYLE_HEIGHT_TOKEN.equalsIgnoreCase(firstToken) && inContext(ElementStyleDslContext.class)) { + new ElementStyleParser().parseHeight(getContext(ElementStyleDslContext.class), tokens); + + } else if (ELEMENT_STYLE_FONT_SIZE_TOKEN.equalsIgnoreCase(firstToken) && inContext(ElementStyleDslContext.class)) { + new ElementStyleParser().parseFontSize(getContext(ElementStyleDslContext.class), tokens); + + } else if (ELEMENT_STYLE_METADATA_TOKEN.equalsIgnoreCase(firstToken) && inContext(ElementStyleDslContext.class)) { + new ElementStyleParser().parseMetadata(getContext(ElementStyleDslContext.class), tokens); + + } else if (ELEMENT_STYLE_DESCRIPTION_TOKEN.equalsIgnoreCase(firstToken) && inContext(ElementStyleDslContext.class)) { + new ElementStyleParser().parseDescription(getContext(ElementStyleDslContext.class), tokens); + + } else if (ELEMENT_STYLE_ICON_TOKEN.equalsIgnoreCase(firstToken) && inContext(ElementStyleDslContext.class)) { + new ElementStyleParser().parseIcon(getContext(ElementStyleDslContext.class), tokens, restricted); + + } else if (RELATIONSHIP_STYLE_TOKEN.equalsIgnoreCase(firstToken) && inContext(StylesDslContext.class)) { + RelationshipStyle relationshipStyle = new RelationshipStyleParser().parseRelationshipStyle(getContext(), tokens.withoutContextStartToken()); + startContext(new RelationshipStyleDslContext(relationshipStyle)); + + } else if (RELATIONSHIP_STYLE_THICKNESS_TOKEN.equalsIgnoreCase(firstToken) && inContext(RelationshipStyleDslContext.class)) { + new RelationshipStyleParser().parseThickness(getContext(RelationshipStyleDslContext.class), tokens); + + } else if ((RELATIONSHIP_STYLE_COLOUR_TOKEN.equalsIgnoreCase(firstToken) || RELATIONSHIP_STYLE_COLOR_TOKEN.equalsIgnoreCase(firstToken)) && inContext(RelationshipStyleDslContext.class)) { + new RelationshipStyleParser().parseColour(getContext(RelationshipStyleDslContext.class), tokens); + + } else if (RELATIONSHIP_STYLE_DASHED_TOKEN.equalsIgnoreCase(firstToken) && inContext(RelationshipStyleDslContext.class)) { + new RelationshipStyleParser().parseDashed(getContext(RelationshipStyleDslContext.class), tokens); + + } else if (RELATIONSHIP_STYLE_OPACITY_TOKEN.equalsIgnoreCase(firstToken) && inContext(RelationshipStyleDslContext.class)) { + new RelationshipStyleParser().parseOpacity(getContext(RelationshipStyleDslContext.class), tokens); + + } else if (RELATIONSHIP_STYLE_WIDTH_TOKEN.equalsIgnoreCase(firstToken) && inContext(RelationshipStyleDslContext.class)) { + new RelationshipStyleParser().parseWidth(getContext(RelationshipStyleDslContext.class), tokens); + + } else if (RELATIONSHIP_STYLE_FONT_SIZE_TOKEN.equalsIgnoreCase(firstToken) && inContext(RelationshipStyleDslContext.class)) { + new RelationshipStyleParser().parseFontSize(getContext(RelationshipStyleDslContext.class), tokens); + + } else if (RELATIONSHIP_STYLE_POSITION_TOKEN.equalsIgnoreCase(firstToken) && inContext(RelationshipStyleDslContext.class)) { + new RelationshipStyleParser().parsePosition(getContext(RelationshipStyleDslContext.class), tokens); + + } else if (RELATIONSHIP_STYLE_LINE_STYLE_TOKEN.equalsIgnoreCase(firstToken) && inContext(RelationshipStyleDslContext.class)) { + new RelationshipStyleParser().parseLineStyle(getContext(RelationshipStyleDslContext.class), tokens); + + } else if (RELATIONSHIP_STYLE_ROUTING_TOKEN.equalsIgnoreCase(firstToken) && inContext(RelationshipStyleDslContext.class)) { + new RelationshipStyleParser().parseRouting(getContext(RelationshipStyleDslContext.class), tokens); + + } else if (ENTERPRISE_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelDslContext.class)) { + new EnterpriseParser().parse(getContext(), tokens.withoutContextStartToken()); + startContext(new EnterpriseDslContext()); + + } else if (DEPLOYMENT_ENVIRONMENT_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelDslContext.class)) { + String environment = new DeploymentEnvironmentParser().parse(tokens.withoutContextStartToken()); + + if (shouldStartContext(tokens)) { + startContext(new DeploymentEnvironmentDslContext(environment)); + } + + registerIdentifier(identifier, new DeploymentEnvironment(environment)); + + } else if (DEPLOYMENT_GROUP_TOKEN.equalsIgnoreCase(firstToken) && inContext(DeploymentEnvironmentDslContext.class)) { + String group = new DeploymentGroupParser().parse(tokens.withoutContextStartToken()); + + registerIdentifier(identifier, new DeploymentGroup(group)); + + } else if (DEPLOYMENT_NODE_TOKEN.equalsIgnoreCase(firstToken) && inContext(DeploymentEnvironmentDslContext.class)) { + DeploymentNode deploymentNode = new DeploymentNodeParser().parse(getContext(DeploymentEnvironmentDslContext.class), tokens.withoutContextStartToken()); + + if (shouldStartContext(tokens)) { + startContext(new DeploymentNodeDslContext(deploymentNode)); + } + + registerIdentifier(identifier, deploymentNode); + } else if (DEPLOYMENT_NODE_TOKEN.equalsIgnoreCase(firstToken) && inContext(DeploymentNodeDslContext.class)) { + DeploymentNode deploymentNode = new DeploymentNodeParser().parse(getContext(DeploymentNodeDslContext.class), tokens.withoutContextStartToken()); + + if (shouldStartContext(tokens)) { + startContext(new DeploymentNodeDslContext(deploymentNode)); + } + + registerIdentifier(identifier, deploymentNode); + } else if (INFRASTRUCTURE_NODE_TOKEN.equalsIgnoreCase(firstToken) && inContext(DeploymentNodeDslContext.class)) { + InfrastructureNode infrastructureNode = new InfrastructureNodeParser().parse(getContext(DeploymentNodeDslContext.class), tokens.withoutContextStartToken()); + + if (shouldStartContext(tokens)) { + startContext(new InfrastructureNodeDslContext(infrastructureNode)); + } + + registerIdentifier(identifier, infrastructureNode); + + } else if (SOFTWARE_SYSTEM_INSTANCE_TOKEN.equalsIgnoreCase(firstToken) && inContext(DeploymentNodeDslContext.class)) { + SoftwareSystemInstance softwareSystemInstance = new SoftwareSystemInstanceParser().parse(getContext(DeploymentNodeDslContext.class), tokens.withoutContextStartToken()); + + if (shouldStartContext(tokens)) { + startContext(new SoftwareSystemInstanceDslContext(softwareSystemInstance)); + } + + registerIdentifier(identifier, softwareSystemInstance); + + } else if (CONTAINER_INSTANCE_TOKEN.equalsIgnoreCase(firstToken) && inContext(DeploymentNodeDslContext.class)) { + ContainerInstance containerInstance = new ContainerInstanceParser().parse(getContext(DeploymentNodeDslContext.class), tokens.withoutContextStartToken()); + + if (shouldStartContext(tokens)) { + startContext(new ContainerInstanceDslContext(containerInstance)); + } + + registerIdentifier(identifier, containerInstance); + + } else if (HEALTH_CHECK_TOKEN.equalsIgnoreCase(firstToken) && inContext(StaticStructureElementInstanceDslContext.class)) { + new HealthCheckParser().parse(getContext(StaticStructureElementInstanceDslContext.class), tokens.withoutContextStartToken()); + } else if (CUSTOM_VIEW_TOKEN.equalsIgnoreCase(firstToken) && inContext(ViewsDslContext.class)) { + CustomView view = new CustomViewParser().parse(getContext(), tokens.withoutContextStartToken()); + startContext(new CustomViewDslContext(view)); + + } else if (SYSTEM_LANDSCAPE_VIEW_TOKEN.equalsIgnoreCase(firstToken) && inContext(ViewsDslContext.class)) { + SystemLandscapeView view = new SystemLandscapeViewParser().parse(getContext(), tokens.withoutContextStartToken()); + startContext(new SystemLandscapeViewDslContext(view)); + + } else if (SYSTEM_CONTEXT_VIEW_TOKEN.equalsIgnoreCase(firstToken) && inContext(ViewsDslContext.class)) { + SystemContextView view = new SystemContextViewParser().parse(getContext(), tokens.withoutContextStartToken()); + startContext(new SystemContextViewDslContext(view)); + + } else if (CONTAINER_VIEW_TOKEN.equalsIgnoreCase(firstToken) && inContext(ViewsDslContext.class)) { + ContainerView view = new ContainerViewParser().parse(getContext(), tokens.withoutContextStartToken()); + startContext(new ContainerViewDslContext(view)); + + } else if (COMPONENT_VIEW_TOKEN.equalsIgnoreCase(firstToken) && inContext(ViewsDslContext.class)) { + ComponentView view = new ComponentViewParser().parse(getContext(), tokens.withoutContextStartToken()); + startContext(new ComponentViewDslContext(view)); + + } else if (DYNAMIC_VIEW_TOKEN.equalsIgnoreCase(firstToken) && inContext(ViewsDslContext.class)) { + DynamicView view = new DynamicViewParser().parse(getContext(), tokens.withoutContextStartToken()); + startContext(new DynamicViewDslContext(view)); + + } else if (DEPLOYMENT_VIEW_TOKEN.equalsIgnoreCase(firstToken) && inContext(ViewsDslContext.class)) { + DeploymentView view = new DeploymentViewParser().parse(getContext(), tokens.withoutContextStartToken()); + startContext(new DeploymentViewDslContext(view)); + + } else if (FILTERED_VIEW_TOKEN.equalsIgnoreCase(firstToken) && inContext(ViewsDslContext.class)) { + FilteredView view = new FilteredViewParser().parse(getContext(), tokens.withoutContextStartToken()); + + if (shouldStartContext(tokens)) { + startContext(new FilteredViewDslContext(view)); + } + + } else if (IMAGE_VIEW_TOKEN.equalsIgnoreCase(firstToken) && inContext(ViewsDslContext.class)) { + ImageView view = new ImageViewParser().parse(getContext(), tokens.withoutContextStartToken()); + startContext(new ImageViewDslContext(view)); + + } else if (DslContext.CONTEXT_START_TOKEN.equalsIgnoreCase(firstToken) && inContext(DynamicViewDslContext.class)) { + startContext(new DynamicViewParallelSequenceDslContext(getContext(DynamicViewDslContext.class))); + + } else if (INCLUDE_IN_VIEW_TOKEN.equalsIgnoreCase(firstToken) && inContext(CustomViewDslContext.class)) { + new CustomViewContentParser().parseInclude(getContext(CustomViewDslContext.class), tokens); + + } else if (EXCLUDE_IN_VIEW_TOKEN.equalsIgnoreCase(firstToken) && inContext(CustomViewDslContext.class)) { + new CustomViewContentParser().parseExclude(getContext(CustomViewDslContext.class), tokens); + + } else if (ANIMATION_STEP_IN_VIEW_TOKEN.equalsIgnoreCase(firstToken) && inContext(CustomViewDslContext.class)) { + new CustomViewAnimationStepParser().parse(getContext(CustomViewDslContext.class), tokens); + + } else if (ANIMATION_IN_VIEW_TOKEN.equalsIgnoreCase(firstToken) && inContext(CustomViewDslContext.class)) { + startContext(new CustomViewAnimationDslContext(getContext(CustomViewDslContext.class).getCustomView())); + + } else if (inContext(CustomViewAnimationDslContext.class)) { + new CustomViewAnimationStepParser().parse(getContext(CustomViewAnimationDslContext.class), tokens); + + } else if (INCLUDE_IN_VIEW_TOKEN.equalsIgnoreCase(firstToken) && inContext(StaticViewDslContext.class)) { + new StaticViewContentParser().parseInclude(getContext(StaticViewDslContext.class), tokens); + + } else if (EXCLUDE_IN_VIEW_TOKEN.equalsIgnoreCase(firstToken) && inContext(StaticViewDslContext.class)) { + new StaticViewContentParser().parseExclude(getContext(StaticViewDslContext.class), tokens); + + } else if (ANIMATION_STEP_IN_VIEW_TOKEN.equalsIgnoreCase(firstToken) && inContext(StaticViewDslContext.class)) { + new StaticViewAnimationStepParser().parse(getContext(StaticViewDslContext.class), tokens); + + } else if (ANIMATION_IN_VIEW_TOKEN.equalsIgnoreCase(firstToken) && inContext(StaticViewDslContext.class)) { + startContext(new StaticViewAnimationDslContext(getContext(StaticViewDslContext.class).getView())); + + } else if (inContext(StaticViewAnimationDslContext.class)) { + new StaticViewAnimationStepParser().parse(getContext(StaticViewAnimationDslContext.class), tokens); + + } else if (INCLUDE_IN_VIEW_TOKEN.equalsIgnoreCase(firstToken) && inContext(DeploymentViewDslContext.class)) { + new DeploymentViewContentParser().parseInclude(getContext(DeploymentViewDslContext.class), tokens); + + } else if (EXCLUDE_IN_VIEW_TOKEN.equalsIgnoreCase(firstToken) && inContext(DeploymentViewDslContext.class)) { + new DeploymentViewContentParser().parseExclude(getContext(DeploymentViewDslContext.class), tokens); + + } else if (ANIMATION_STEP_IN_VIEW_TOKEN.equalsIgnoreCase(firstToken) && inContext(DeploymentViewDslContext.class)) { + new DeploymentViewAnimationStepParser().parse(getContext(DeploymentViewDslContext.class), tokens); + + } else if (ANIMATION_IN_VIEW_TOKEN.equalsIgnoreCase(firstToken) && inContext(DeploymentViewDslContext.class)) { + startContext(new DeploymentViewAnimationDslContext(getContext(DeploymentViewDslContext.class).getView())); + + } else if (inContext(DeploymentViewAnimationDslContext.class)) { + new DeploymentViewAnimationStepParser().parse(getContext(DeploymentViewAnimationDslContext.class), tokens); + + } else if (AUTOLAYOUT_VIEW_TOKEN.equalsIgnoreCase(firstToken) && inContext(ViewDslContext.class)) { + new AutoLayoutParser().parse(getContext(ModelViewDslContext.class), tokens); + + } else if (DEFAULT_VIEW_TOKEN.equalsIgnoreCase(firstToken) && inContext(ViewDslContext.class)) { + new DefaultViewParser().parse(getContext(ViewDslContext.class)); + + } else if (VIEW_TITLE_TOKEN.equalsIgnoreCase(firstToken) && inContext(ViewDslContext.class)) { + new ViewParser().parseTitle(getContext(ViewDslContext.class), tokens); + + } else if (VIEW_DESCRIPTION_TOKEN.equalsIgnoreCase(firstToken) && inContext(ViewDslContext.class)) { + new ViewParser().parseDescription(getContext(ViewDslContext.class), tokens); + + } else if (PLANTUML_TOKEN.equalsIgnoreCase(firstToken) && inContext(ImageViewDslContext.class)) { + new ImageViewContentParser(restricted).parsePlantUML(getContext(ImageViewDslContext.class), dslFile, tokens); + + } else if (MERMAID_TOKEN.equalsIgnoreCase(firstToken) && inContext(ImageViewDslContext.class)) { + new ImageViewContentParser(restricted).parseMermaid(getContext(ImageViewDslContext.class), dslFile, tokens); + + } else if (KROKI_TOKEN.equalsIgnoreCase(firstToken) && inContext(ImageViewDslContext.class)) { + new ImageViewContentParser(restricted).parseKroki(getContext(ImageViewDslContext.class), dslFile, tokens); + + } else if (IMAGE_VIEW_TOKEN.equalsIgnoreCase(firstToken) && inContext(ImageViewDslContext.class)) { + new ImageViewContentParser(restricted).parseImage(getContext(ImageViewDslContext.class), dslFile, tokens); + + } else if (inContext(DynamicViewDslContext.class)) { + RelationshipView relationshipView = new DynamicViewContentParser().parseRelationship(getContext(DynamicViewDslContext.class), tokens); + + if (inContext(DynamicViewParallelSequenceDslContext.class)) { + getContext(DynamicViewParallelSequenceDslContext.class).hasRelationships(true); + } + + if (shouldStartContext(tokens)) { + startContext(new DynamicViewRelationshipContext(relationshipView)); + } + + } else if (URL_TOKEN.equalsIgnoreCase(firstToken) && inContext(DynamicViewRelationshipContext.class)) { + new DynamicViewRelationshipParser().parseUrl(getContext(DynamicViewRelationshipContext.class), tokens.withoutContextStartToken()); + + } else if (THEME_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ViewsDslContext.class) || inContext(StylesDslContext.class))) { + new ThemeParser().parseTheme(getContext(), tokens); + + } else if (THEMES_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ViewsDslContext.class) || inContext(StylesDslContext.class))) { + new ThemeParser().parseThemes(getContext(), tokens); + + } else if (TERMINOLOGY_TOKEN.equalsIgnoreCase(firstToken) && inContext(ViewsDslContext.class)) { + startContext(new TerminologyDslContext()); + + } else if (ENTERPRISE_TOKEN.equalsIgnoreCase(firstToken) && inContext(TerminologyDslContext.class)) { + new TerminologyParser().parseEnterprise(getContext(), tokens); + + } else if (PERSON_TOKEN.equalsIgnoreCase(firstToken) && inContext(TerminologyDslContext.class)) { + new TerminologyParser().parsePerson(getContext(), tokens); + + } else if (SOFTWARE_SYSTEM_TOKEN.equalsIgnoreCase(firstToken) && inContext(TerminologyDslContext.class)) { + new TerminologyParser().parseSoftwareSystem(getContext(), tokens); + + } else if (CONTAINER_TOKEN.equalsIgnoreCase(firstToken) && inContext(TerminologyDslContext.class)) { + new TerminologyParser().parseContainer(getContext(), tokens); + + } else if (COMPONENT_TOKEN.equalsIgnoreCase(firstToken) && inContext(TerminologyDslContext.class)) { + new TerminologyParser().parseComponent(getContext(), tokens); + + } else if (DEPLOYMENT_NODE_TOKEN.equalsIgnoreCase(firstToken) && inContext(TerminologyDslContext.class)) { + new TerminologyParser().parseDeploymentNode(getContext(), tokens); + + } else if (INFRASTRUCTURE_NODE_TOKEN.equalsIgnoreCase(firstToken) && inContext(TerminologyDslContext.class)) { + new TerminologyParser().parseInfrastructureNode(getContext(), tokens); + + } else if (TERMINOLOGY_RELATIONSHIP_TOKEN.equalsIgnoreCase(firstToken) && inContext(TerminologyDslContext.class)) { + new TerminologyParser().parseRelationship(getContext(), tokens); + + } else if (CONFIGURATION_TOKEN.equalsIgnoreCase(firstToken) && inContext(WorkspaceDslContext.class)) { + startContext(new ConfigurationDslContext()); + + } else if (SCOPE_TOKEN.equalsIgnoreCase(firstToken) && inContext(ConfigurationDslContext.class)) { + new ConfigurationParser().parseScope(getContext(), tokens); + + } else if (VISIBILITY_TOKEN.equalsIgnoreCase(firstToken) && inContext(ConfigurationDslContext.class)) { + new ConfigurationParser().parseVisibility(getContext(), tokens); + + } else if (USERS_TOKEN.equalsIgnoreCase(firstToken) && inContext(ConfigurationDslContext.class)) { + startContext(new UsersDslContext()); + + } else if (inContext(UsersDslContext.class)) { + new UserRoleParser().parse(getContext(), tokens); + + } else if (DOCS_TOKEN.equalsIgnoreCase(firstToken) && inContext(WorkspaceDslContext.class)) { + if (!restricted) { + new DocsParser().parse(getContext(WorkspaceDslContext.class), dslFile, tokens); + } + + } else if (DOCS_TOKEN.equalsIgnoreCase(firstToken) && inContext(SoftwareSystemDslContext.class)) { + if (!restricted) { + new DocsParser().parse(getContext(SoftwareSystemDslContext.class), dslFile, tokens); + } + + } else if (DOCS_TOKEN.equalsIgnoreCase(firstToken) && inContext(ContainerDslContext.class)) { + if (!restricted) { + new DocsParser().parse(getContext(ContainerDslContext.class), dslFile, tokens); + } + + } else if (DOCS_TOKEN.equalsIgnoreCase(firstToken) && inContext(ComponentDslContext.class)) { + if (!restricted) { + new DocsParser().parse(getContext(ComponentDslContext.class), dslFile, tokens); + } + + } else if (ADRS_TOKEN.equalsIgnoreCase(firstToken) && inContext(WorkspaceDslContext.class)) { + if (!restricted) { + new AdrsParser().parse(getContext(WorkspaceDslContext.class), dslFile, tokens); + } + + } else if (ADRS_TOKEN.equalsIgnoreCase(firstToken) && inContext(SoftwareSystemDslContext.class)) { + if (!restricted) { + new AdrsParser().parse(getContext(SoftwareSystemDslContext.class), dslFile, tokens); + } + + } else if (ADRS_TOKEN.equalsIgnoreCase(firstToken) && inContext(ContainerDslContext.class)) { + if (!restricted) { + new AdrsParser().parse(getContext(ContainerDslContext.class), dslFile, tokens); + } + + } else if (ADRS_TOKEN.equalsIgnoreCase(firstToken) && inContext(ComponentDslContext.class)) { + if (!restricted) { + new AdrsParser().parse(getContext(ComponentDslContext.class), dslFile, tokens); + } + + } else if (CONSTANT_TOKEN.equalsIgnoreCase(firstToken)) { + Constant constant = new ConstantParser().parse(getContext(), tokens); + constants.put(constant.getName(), constant); + + } else if (IDENTIFIERS_TOKEN.equalsIgnoreCase(firstToken) && inContext(WorkspaceDslContext.class)) { + setIdentifierScope(new IdentifierScopeParser().parse(getContext(), tokens)); + + } else { + String[] expectedTokens; + if (getContext() == null) { + if (getWorkspace() == null) { + // the workspace hasn't yet been created + expectedTokens = new String[]{ + StructurizrDslTokens.WORKSPACE_TOKEN + }; + } else { + expectedTokens = new String[0]; + } + } else { + expectedTokens = getContext().getPermittedTokens(); + } + + if (expectedTokens.length > 0) { + StringBuilder buf = new StringBuilder(); + for (String expectedToken : expectedTokens) { + buf.append(expectedToken); + buf.append(", "); + } + throw new StructurizrDslParserException("Unexpected tokens (expected: " + buf.substring(0, buf.length() - 2) + ")"); + } else { + throw new StructurizrDslParserException("Unexpected tokens"); + } + } + } + + if (includeInDslSourceLines) { + dslSourceLines.add(line); + } + } catch (Exception e) { + if (e.getMessage() != null) { + throw new StructurizrDslParserException(e.getMessage(), dslFile, dslLine.getLineNumber(), line); + } else { + throw new StructurizrDslParserException(e.getClass().getSimpleName(), dslFile, dslLine.getLineNumber(), line); + } + } + } + } + + private String substituteStrings(String token) { + Matcher m = STRING_SUBSTITUTION_PATTERN.matcher(token); + while (m.find()) { + String before = m.group(0); + String after = null; + String name = before.substring(2, before.length()-1); + if (constants.containsKey(name)) { + after = constants.get(name).getValue(); + } else { + if (!restricted) { + String environmentVariable = System.getenv().get(name); + if (environmentVariable != null) { + after = environmentVariable; + } + } + } + + if (after != null) { + token = token.replace(before, after); + } + } + + return token; + } + + private boolean shouldStartContext(Tokens tokens) { + return DslContext.CONTEXT_START_TOKEN.equalsIgnoreCase(tokens.get(tokens.size()-1)); + } + + private void startContext(DslContext context) { + context.setWorkspace(workspace); + context.setIdentifierRegister(identifiersRegister); + context.setExtendingWorkspace(extendingWorkspace); + contextStack.push(context); + } + + private DslContext getContext() { + if (!contextStack.empty()) { + return contextStack.peek(); + } else { + return null; + } + } + + private T getContext(Class clazz) throws StructurizrDslParserException { + if (inContext(clazz)) { + return (T)contextStack.peek(); + } else { + throw new StructurizrDslParserException("Expected " + clazz.getName() + " but got " + contextStack.peek().getClass().getName()); + } + } + + private void endContext() throws StructurizrDslParserException { + if (!contextStack.empty()) { + DslContext context = contextStack.pop(); + context.end(); + } else { + throw new StructurizrDslParserException("Unexpected end of context"); + } + } + + /** + * Gets the identifier register in use (this is the mapping of DSL identifiers to elements/relationships). + * + * @return an IdentifiersRegister object + */ + public IdentifiersRegister getIdentifiersRegister() { + return identifiersRegister; + } + + private void registerIdentifier(String identifier, Element element) { + identifiersRegister.register(identifier, element); + element.addProperty(STRUCTURIZR_DSL_IDENTIFIER_PROPERTY_NAME, identifiersRegister.findIdentifier(element)); + } + + private void registerIdentifier(String identifier, Relationship relationship) { + identifiersRegister.register(identifier, relationship); + relationship.addProperty(STRUCTURIZR_DSL_IDENTIFIER_PROPERTY_NAME, identifiersRegister.findIdentifier(relationship)); + } + + private boolean inContext(Class clazz) { + if (contextStack.empty()) { + return false; + } + + return clazz.isAssignableFrom(contextStack.peek().getClass()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParserException.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParserException.java new file mode 100644 index 000000000..9868ba0a0 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParserException.java @@ -0,0 +1,49 @@ +package com.structurizr.dsl; + +import java.io.File; + +/** + * Throw when there are parsing errors. + */ +public final class StructurizrDslParserException extends Exception { + + /** line number */ + private int lineNumber; + + /** line */ + private String line; + + /** + * Creates a new instance with the specified message. + * + * @param message the message + */ + StructurizrDslParserException(String message) { + super(message); + } + + StructurizrDslParserException(String message, File dslFile, int lineNumber, String line) { + super((message.endsWith(".") ? message.substring(0, message.length()-1) : message) + " at line " + lineNumber + (dslFile != null ? " of " + dslFile.getAbsolutePath() : "") + ": " + line.trim()); + this.lineNumber = lineNumber; + this.line = line; + } + + /** + * Gets the line number associated with the parsing exception. + * + * @return the line number, an integer + */ + public int getLineNumber() { + return lineNumber; + } + + /** + * Gets the line associated with the parsing exception. + * + * @return the line, as a String + */ + public String getLine() { + return line; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslPlugin.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslPlugin.java new file mode 100644 index 000000000..cd73e109c --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslPlugin.java @@ -0,0 +1,15 @@ +package com.structurizr.dsl; + +/** + * An interface implemented by DSL plugins. + */ +public interface StructurizrDslPlugin { + + /** + * Called to execute the plugin. + * + * @param context a StructurizrDslPluginContext instance + */ + void run(StructurizrDslPluginContext context); + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslPluginContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslPluginContext.java new file mode 100644 index 000000000..10f104907 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslPluginContext.java @@ -0,0 +1,88 @@ +package com.structurizr.dsl; + +import com.structurizr.Workspace; +import com.structurizr.util.StringUtils; + +import java.io.File; +import java.util.Map; + +/** + * Used to pass contextual information to DSL plugins when they are executed. + */ +public class StructurizrDslPluginContext { + + private final StructurizrDslParser dslParser; + private final File dslFile; + private final Workspace workspace; + private final Map parameters; + + /** + * Creates a new instance. + * + * @param dslParser a reference to the DSL parser that loaded the plugin + * @param dslFile a reference to the DSL file that loaded the plugin + * @param workspace the workspace + * @param parameters a map of name/value pairs representing parameters + */ + public StructurizrDslPluginContext(StructurizrDslParser dslParser, File dslFile, Workspace workspace, Map parameters) { + this.dslParser = dslParser; + this.dslFile = dslFile; + this.workspace = workspace; + this.parameters = parameters; + } + + /** + * Gets a reference to the DSL parser that initiated this plugin context. + * + * @return a StructurizrDslParser instance + */ + public StructurizrDslParser getDslParser() { + return dslParser; + } + + /** + * Gets a reference to the DSL file that initiated this plugin context. + * + * @return a File instance + */ + public File getDslFile() { + return dslFile; + } + + /** + * Gets the current workspace. + * + * @return a Workspace instance + */ + public Workspace getWorkspace() { + return workspace; + } + + /** + * Gets the named parameter. + * + * @param name the parameter name + * @return the parameter value (null if unset) + */ + public String getParameter(String name) { + return parameters.get(name); + } + + /** + * Gets the named parameter, with a default value if unset. + * + * @param name the parameter name + * @param defaultValue the default value + * @return the parameter value, or defaultValue if unset + */ + public String getParameter(String name, String defaultValue) { + String value = parameters.get(name); + + if (StringUtils.isNullOrEmpty(value)) { + value = defaultValue; + } + + return value; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslScriptContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslScriptContext.java new file mode 100644 index 000000000..737c6ed6b --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslScriptContext.java @@ -0,0 +1,76 @@ +package com.structurizr.dsl; + +import com.structurizr.Workspace; +import com.structurizr.util.StringUtils; + +import java.io.File; +import java.util.Map; + +/** + * Used to pass contextual information to DSL scripts when they are executed. + */ +public class StructurizrDslScriptContext { + + private final File dslFile; + private final Workspace workspace; + private final Map parameters; + + /** + * Creates a new instance. + * + * @param dslFile a reference to the DSL file that loaded the script + * @param workspace the workspace + * @param parameters a map of name/value pairs representing parameters + */ + public StructurizrDslScriptContext(File dslFile, Workspace workspace, Map parameters) { + this.dslFile = dslFile; + this.workspace = workspace; + this.parameters = parameters; + } + + /** + * Gets a reference to the DSL file that initiated this script context. + * + * @return a File instance + */ + public File getDslFile() { + return dslFile; + } + + /** + * Gets the current workspace. + * + * @return a Workspace instance + */ + public Workspace getWorkspace() { + return workspace; + } + + /** + * Gets the named parameter. + * + * @param name the parameter name + * @return the parameter value (null if unset) + */ + public String getParameter(String name) { + return parameters.get(name); + } + + /** + * Gets the named parameter, with a default value if unset. + * + * @param name the parameter name + * @param defaultValue the default value + * @return the parameter value, or defaultValue if unset + */ + public String getParameter(String name, String defaultValue) { + String value = parameters.get(name); + + if (StringUtils.isNullOrEmpty(value)) { + value = defaultValue; + } + + return value; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java new file mode 100644 index 000000000..946185e21 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java @@ -0,0 +1,109 @@ +package com.structurizr.dsl; + +/** + * Main DSL parser class - forms the API for using the parser. + */ +class StructurizrDslTokens { + + static final String ASSIGNMENT_OPERATOR_TOKEN = "="; + + static final String CUSTOM_ELEMENT_TOKEN = "element"; + static final String PERSON_TOKEN = "person"; + static final String SOFTWARE_SYSTEM_TOKEN = "softwareSystem"; + static final String RELATIONSHIP_TOKEN = "->"; + static final String CONTAINER_TOKEN = "container"; + static final String COMPONENT_TOKEN = "component"; + static final String GROUP_TOKEN = "group"; + static final String NAME_TOKEN = "name"; + static final String DESCRIPTION_TOKEN = "description"; + static final String TECHNOLOGY_TOKEN = "technology"; + static final String INSTANCES_TOKEN = "instances"; + static final String TAGS_TOKEN = "tags"; + static final String URL_TOKEN = "url"; + static final String PROPERTIES_TOKEN = "properties"; + static final String PERSPECTIVES_TOKEN = "perspectives"; + static final String WORKSPACE_TOKEN = "workspace"; + static final String EXTENDS_TOKEN = "extends"; + static final String SCOPE_TOKEN = "scope"; + static final String MODEL_TOKEN = "model"; + static final String VIEWS_TOKEN = "views"; + static final String ENTERPRISE_TOKEN = "enterprise"; + static final String DEPLOYMENT_ENVIRONMENT_TOKEN = "deploymentEnvironment"; + static final String DEPLOYMENT_GROUP_TOKEN = "deploymentGroup"; + static final String DEPLOYMENT_NODE_TOKEN = "deploymentNode"; + static final String INFRASTRUCTURE_NODE_TOKEN = "infrastructureNode"; + static final String SOFTWARE_SYSTEM_INSTANCE_TOKEN = "softwareSystemInstance"; + static final String CONTAINER_INSTANCE_TOKEN = "containerInstance"; + static final String HEALTH_CHECK_TOKEN = "healthCheck"; + static final String CUSTOM_VIEW_TOKEN = "custom"; + static final String SYSTEM_LANDSCAPE_VIEW_TOKEN = "systemLandscape"; + static final String SYSTEM_CONTEXT_VIEW_TOKEN = "systemContext"; + static final String CONTAINER_VIEW_TOKEN = "container"; + static final String COMPONENT_VIEW_TOKEN = "component"; + static final String DYNAMIC_VIEW_TOKEN = "dynamic"; + static final String DEPLOYMENT_VIEW_TOKEN = "deployment"; + static final String FILTERED_VIEW_TOKEN = "filtered"; + static final String IMAGE_VIEW_TOKEN = "image"; + static final String INCLUDE_IN_VIEW_TOKEN = "include"; + static final String EXCLUDE_IN_VIEW_TOKEN = "exclude"; + static final String ANIMATION_IN_VIEW_TOKEN = "animation"; + static final String ANIMATION_STEP_IN_VIEW_TOKEN = "animationStep"; + static final String AUTOLAYOUT_VIEW_TOKEN = "autolayout"; + static final String DEFAULT_VIEW_TOKEN = "default"; + static final String VIEW_TITLE_TOKEN = "title"; + static final String VIEW_DESCRIPTION_TOKEN = "description"; + static final String PLANTUML_TOKEN = "plantuml"; + static final String MERMAID_TOKEN = "mermaid"; + static final String KROKI_TOKEN = "kroki"; + static final String IMAGE_TOKEN = "image"; + static final String STYLES_TOKEN = "styles"; + static final String BRANDING_TOKEN = "branding"; + static final String BRANDING_LOGO_TOKEN = "logo"; + static final String BRANDING_FONT_TOKEN = "font"; + static final String ELEMENT_STYLE_TOKEN = "element"; + static final String ELEMENT_STYLE_SHAPE_TOKEN = "shape"; + static final String ELEMENT_STYLE_BACKGROUND_TOKEN = "background"; + static final String ELEMENT_STYLE_STROKE_TOKEN = "stroke"; + static final String ELEMENT_STYLE_STROKE_WIDTH_TOKEN = "strokeWidth"; + static final String ELEMENT_STYLE_COLOUR_TOKEN = "colour"; + static final String ELEMENT_STYLE_COLOR_TOKEN = "color"; + static final String ELEMENT_STYLE_ICON_TOKEN = "icon"; + static final String ELEMENT_STYLE_OPACITY_TOKEN = "opacity"; + static final String ELEMENT_STYLE_BORDER_TOKEN = "border"; + static final String ELEMENT_STYLE_FONT_SIZE_TOKEN = "fontSize"; + static final String ELEMENT_STYLE_WIDTH_TOKEN = "width"; + static final String ELEMENT_STYLE_HEIGHT_TOKEN = "height"; + static final String ELEMENT_STYLE_METADATA_TOKEN = "metadata"; + static final String ELEMENT_STYLE_DESCRIPTION_TOKEN = "description"; + static final String RELATIONSHIP_STYLE_TOKEN = "relationship"; + static final String RELATIONSHIP_STYLE_THICKNESS_TOKEN = "thickness"; + static final String RELATIONSHIP_STYLE_COLOUR_TOKEN = "colour"; + static final String RELATIONSHIP_STYLE_COLOR_TOKEN = "color"; + static final String RELATIONSHIP_STYLE_DASHED_TOKEN = "dashed"; + static final String RELATIONSHIP_STYLE_OPACITY_TOKEN = "opacity"; + static final String RELATIONSHIP_STYLE_ROUTING_TOKEN = "routing"; + static final String RELATIONSHIP_STYLE_LINE_STYLE_TOKEN = "style"; + static final String RELATIONSHIP_STYLE_FONT_SIZE_TOKEN = "fontSize"; + static final String RELATIONSHIP_STYLE_WIDTH_TOKEN = "width"; + static final String RELATIONSHIP_STYLE_POSITION_TOKEN = "position"; + static final String THEME_TOKEN = "theme"; + static final String THEMES_TOKEN = "themes"; + static final String CONFIGURATION_TOKEN = "configuration"; + static final String VISIBILITY_TOKEN = "visibility"; + static final String TERMINOLOGY_TOKEN = "terminology"; + static final String TERMINOLOGY_RELATIONSHIP_TOKEN = "relationship"; + static final String USERS_TOKEN = "users"; + static final String THIS_TOKEN = "this"; + + static final String INCLUDE_FILE_TOKEN = "!include"; + static final String DOCS_TOKEN = "!docs"; + static final String ADRS_TOKEN = "!adrs"; + static final String CONSTANT_TOKEN = "!constant"; + static final String IDENTIFIERS_TOKEN = "!identifiers"; + static final String IMPLIED_RELATIONSHIPS_TOKEN = "!impliedRelationships"; + static final String REF_TOKEN = "!ref"; + static final String EXTEND_TOKEN = "!extend"; + static final String PLUGIN_TOKEN = "!plugin"; + static final String SCRIPT_TOKEN = "!script"; + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StylesDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StylesDslContext.java new file mode 100644 index 000000000..a27234faf --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StylesDslContext.java @@ -0,0 +1,13 @@ +package com.structurizr.dsl; + +final class StylesDslContext extends DslContext { + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.ELEMENT_STYLE_TOKEN, + StructurizrDslTokens.RELATIONSHIP_STYLE_TOKEN + }; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/SystemContextViewDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/SystemContextViewDslContext.java new file mode 100644 index 000000000..8fd9e05ae --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/SystemContextViewDslContext.java @@ -0,0 +1,11 @@ +package com.structurizr.dsl; + +import com.structurizr.view.SystemContextView; + +final class SystemContextViewDslContext extends StaticViewDslContext { + + SystemContextViewDslContext(SystemContextView view) { + super(view); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/SystemContextViewParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/SystemContextViewParser.java new file mode 100644 index 000000000..4dc004a95 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/SystemContextViewParser.java @@ -0,0 +1,62 @@ +package com.structurizr.dsl; + +import com.structurizr.Workspace; +import com.structurizr.model.Element; +import com.structurizr.model.SoftwareSystem; +import com.structurizr.view.SystemContextView; + +final class SystemContextViewParser extends AbstractViewParser { + + private static final String GRAMMAR = "systemContext [key] [description] {"; + + private static final String VIEW_TYPE = "SystemContext"; + + private static final int SOFTWARE_SYSTEM_IDENTIFIER_INDEX = 1; + private static final int KEY_INDEX = 2; + private static final int DESCRIPTION_INDEX = 3; + + SystemContextView parse(DslContext context, Tokens tokens) { + // systemContext [key] [description] { + + Workspace workspace = context.getWorkspace(); + SoftwareSystem softwareSystem; + String key = ""; + String description = ""; + + if (tokens.hasMoreThan(DESCRIPTION_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } + + if (!tokens.includes(SOFTWARE_SYSTEM_IDENTIFIER_INDEX)) { + throw new RuntimeException("Expected: " + GRAMMAR); + } + + String softwareSystemIdentifier = tokens.get(SOFTWARE_SYSTEM_IDENTIFIER_INDEX); + Element element = context.getElement(softwareSystemIdentifier); + if (element == null) { + throw new RuntimeException("The software system \"" + softwareSystemIdentifier + "\" does not exist"); + } + if (element instanceof SoftwareSystem) { + softwareSystem = (SoftwareSystem)element; + } else { + throw new RuntimeException("The element \"" + softwareSystemIdentifier + "\" is not a software system"); + } + + if (tokens.includes(KEY_INDEX)) { + key = tokens.get(KEY_INDEX); + } else { + key = workspace.getViews().generateViewKey(VIEW_TYPE); + } + validateViewKey(key); + + if (tokens.includes(DESCRIPTION_INDEX)) { + description = tokens.get(DESCRIPTION_INDEX); + } + + SystemContextView view = workspace.getViews().createSystemContextView(softwareSystem, key, description); + view.setEnterpriseBoundaryVisible(true); + + return view; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/SystemLandscapeViewDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/SystemLandscapeViewDslContext.java new file mode 100644 index 000000000..1ca6c6cff --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/SystemLandscapeViewDslContext.java @@ -0,0 +1,11 @@ +package com.structurizr.dsl; + +import com.structurizr.view.SystemLandscapeView; + +final class SystemLandscapeViewDslContext extends StaticViewDslContext { + + SystemLandscapeViewDslContext(SystemLandscapeView view) { + super(view); + } + +} diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/SystemLandscapeViewParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/SystemLandscapeViewParser.java new file mode 100644 index 000000000..e03919f72 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/SystemLandscapeViewParser.java @@ -0,0 +1,43 @@ +package com.structurizr.dsl; + +import com.structurizr.Workspace; +import com.structurizr.view.SystemLandscapeView; + +final class SystemLandscapeViewParser extends AbstractViewParser { + + private static final String GRAMMAR = "systemLandscape [key] [description] {"; + + private static final String VIEW_TYPE = "SystemLandscape"; + + private static final int KEY_INDEX = 1; + private static final int DESCRIPTION_INDEX = 2; + + SystemLandscapeView parse(DslContext context, Tokens tokens) { + // systemLandscape [key] [description] + + if (tokens.hasMoreThan(DESCRIPTION_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } + + Workspace workspace = context.getWorkspace(); + String key = ""; + String description = ""; + + if (tokens.includes(KEY_INDEX)) { + key = tokens.get(KEY_INDEX); + } else { + key = workspace.getViews().generateViewKey(VIEW_TYPE); + } + validateViewKey(key); + + if (tokens.includes(DESCRIPTION_INDEX)) { + description = tokens.get(DESCRIPTION_INDEX); + } + + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView(key, description); + view.setEnterpriseBoundaryVisible(true); + + return view; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/TerminologyDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/TerminologyDslContext.java new file mode 100644 index 000000000..27e69e8fb --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/TerminologyDslContext.java @@ -0,0 +1,19 @@ +package com.structurizr.dsl; + +final class TerminologyDslContext extends DslContext { + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.ENTERPRISE_TOKEN, + StructurizrDslTokens.PERSON_TOKEN, + StructurizrDslTokens.SOFTWARE_SYSTEM_TOKEN, + StructurizrDslTokens.CONTAINER_TOKEN, + StructurizrDslTokens.COMPONENT_TOKEN, + StructurizrDslTokens.DEPLOYMENT_NODE_TOKEN, + StructurizrDslTokens.INFRASTRUCTURE_NODE_TOKEN, + StructurizrDslTokens.TERMINOLOGY_RELATIONSHIP_TOKEN + }; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/TerminologyParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/TerminologyParser.java new file mode 100644 index 000000000..b189f5d86 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/TerminologyParser.java @@ -0,0 +1,79 @@ +package com.structurizr.dsl; + +final class TerminologyParser extends AbstractParser { + + private final static int TERM_INDEX = 1; + + void parseEnterprise(DslContext context, Tokens tokens) { + // enterprise + if (!tokens.includes(TERM_INDEX)) { + throw new RuntimeException("Expected: enterprise "); + } + + context.getWorkspace().getViews().getConfiguration().getTerminology().setEnterprise(tokens.get(TERM_INDEX)); + } + + void parsePerson(DslContext context, Tokens tokens) { + // person + if (!tokens.includes(TERM_INDEX)) { + throw new RuntimeException("Expected: person "); + } + + context.getWorkspace().getViews().getConfiguration().getTerminology().setPerson(tokens.get(TERM_INDEX)); + } + + void parseSoftwareSystem(DslContext context, Tokens tokens) { + // softwareSystem + if (!tokens.includes(TERM_INDEX)) { + throw new RuntimeException("Expected: softwareSystem "); + } + + context.getWorkspace().getViews().getConfiguration().getTerminology().setSoftwareSystem(tokens.get(TERM_INDEX)); + } + + void parseContainer(DslContext context, Tokens tokens) { + // container + if (!tokens.includes(TERM_INDEX)) { + throw new RuntimeException("Expected: container "); + } + + context.getWorkspace().getViews().getConfiguration().getTerminology().setContainer(tokens.get(TERM_INDEX)); + } + + void parseComponent(DslContext context, Tokens tokens) { + // component + if (!tokens.includes(TERM_INDEX)) { + throw new RuntimeException("Expected: component "); + } + + context.getWorkspace().getViews().getConfiguration().getTerminology().setComponent(tokens.get(TERM_INDEX)); + } + + void parseDeploymentNode(DslContext context, Tokens tokens) { + // deploymentNode + if (!tokens.includes(TERM_INDEX)) { + throw new RuntimeException("Expected: deploymentNode "); + } + + context.getWorkspace().getViews().getConfiguration().getTerminology().setDeploymentNode(tokens.get(TERM_INDEX)); + } + + void parseInfrastructureNode(DslContext context, Tokens tokens) { + // infrastructureNode + if (!tokens.includes(TERM_INDEX)) { + throw new RuntimeException("Expected: infrastructureNode "); + } + + context.getWorkspace().getViews().getConfiguration().getTerminology().setInfrastructureNode(tokens.get(TERM_INDEX)); + } + + void parseRelationship(DslContext context, Tokens tokens) { + // relationship + if (!tokens.includes(TERM_INDEX)) { + throw new RuntimeException("Expected: relationship "); + } + + context.getWorkspace().getViews().getConfiguration().getTerminology().setRelationship(tokens.get(TERM_INDEX)); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ThemeParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ThemeParser.java new file mode 100644 index 000000000..9cc5286af --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ThemeParser.java @@ -0,0 +1,41 @@ +package com.structurizr.dsl; + +final class ThemeParser extends AbstractParser { + + private static final String DEFAULT_THEME_URL = "https://static.structurizr.com/themes/default/theme.json"; + + private final static int FIRST_THEME_INDEX = 1; + + void parseTheme(DslContext context, Tokens tokens) { + // theme + if (tokens.hasMoreThan(FIRST_THEME_INDEX)) { + throw new RuntimeException("Too many tokens, expected: theme "); + } + + if (!tokens.includes(FIRST_THEME_INDEX)) { + throw new RuntimeException("Expected: theme "); + } + + addTheme(context, tokens.get(FIRST_THEME_INDEX)); + } + + void parseThemes(DslContext context, Tokens tokens) { + // themes [url] ... [url] + if (!tokens.includes(FIRST_THEME_INDEX)) { + throw new RuntimeException("Expected: themes [url] ... [url]"); + } + + for (int i = FIRST_THEME_INDEX; i < tokens.size(); i++) { + addTheme(context, tokens.get(i)); + } + } + + private void addTheme(DslContext context, String url) { + if ("default".equalsIgnoreCase(url)) { + url = DEFAULT_THEME_URL; + } + + context.getWorkspace().getViews().getConfiguration().addTheme(url); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/Tokenizer.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/Tokenizer.java new file mode 100644 index 000000000..cd371def3 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/Tokenizer.java @@ -0,0 +1,58 @@ +package com.structurizr.dsl; + +import java.util.ArrayList; +import java.util.List; + +class Tokenizer { + + List tokenize(String line) { + List tokens = new ArrayList<>(); + line = line.trim(); + + boolean tokenStarted = false; + boolean quoted = false; + StringBuilder token = new StringBuilder(); + + for (int i = 0; i < line.length(); i++) { + char c = line.charAt(i); + + if (!tokenStarted) { + if (c == '"') { + quoted = true; + tokenStarted = true; + token = new StringBuilder(); + } else if (Character.isWhitespace(c)) { + // skip + } else { + quoted = false; + tokenStarted = true; + token = new StringBuilder(); + token.append(c); + } + } else { + if (c == '"' && line.charAt(i-1) == '\\') { + // escaped quote + token.append(c); + } else if (quoted && c == '"') { + // this is the end of the token + tokens.add(token.toString()); + tokenStarted = false; + quoted = false; + } else if (!quoted && Character.isWhitespace(c)) { + tokens.add(token.toString()); + tokenStarted = false; + quoted = false; + } else { + token.append(c); + } + } + } + + if (tokenStarted) { + tokens.add(token.toString()); + } + + return tokens; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/Tokens.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/Tokens.java new file mode 100644 index 000000000..f00c1cfc7 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/Tokens.java @@ -0,0 +1,41 @@ +package com.structurizr.dsl; + +import java.util.List; + +final class Tokens { + + private List tokens; + + Tokens(List tokens) { + this.tokens = tokens; + } + + String get(int index) { + return tokens.get(index).trim().replaceAll("\\\\\"", "\"").trim().replaceAll("\\\\n", "\n"); + } + + int size() { + return tokens.size(); + } + + boolean contains(String token) { + return tokens.contains(token.trim()); + } + + Tokens withoutContextStartToken() { + if (tokens.get(tokens.size()-1).equals(DslContext.CONTEXT_START_TOKEN)) { + return new Tokens(tokens.subList(0, tokens.size()-1)); + } else { + return this; + } + } + + boolean includes(int index) { + return tokens.size() - 1 >= index; + } + + boolean hasMoreThan(int index) { + return includes(index + 1); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/UserRoleParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/UserRoleParser.java new file mode 100644 index 000000000..e0db37ef8 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/UserRoleParser.java @@ -0,0 +1,39 @@ +package com.structurizr.dsl; + +import com.structurizr.configuration.Role; + +final class UserRoleParser extends AbstractParser { + + private static final String GRAMMAR = " "; + + private final static int USERNAME_INDEX = 0; + private final static int ROLE_INDEX = 1; + + void parse(DslContext context, Tokens tokens) { + // + + if (tokens.hasMoreThan(ROLE_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } + + if (tokens.size() != 2) { + throw new RuntimeException("Expected: " + GRAMMAR); + } + + String username = tokens.get(USERNAME_INDEX); + String roleAsString = tokens.get(ROLE_INDEX); + + Role role; + + if (roleAsString.equalsIgnoreCase("write")) { + role = Role.ReadWrite; + } else if (roleAsString.equalsIgnoreCase("read")) { + role = Role.ReadOnly; + } else { + throw new RuntimeException("The role should be \"read\" or \"write\""); + } + + context.getWorkspace().getConfiguration().addUser(username, role); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/UsersDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/UsersDslContext.java new file mode 100644 index 000000000..e8b6a0170 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/UsersDslContext.java @@ -0,0 +1,10 @@ +package com.structurizr.dsl; + +final class UsersDslContext extends DslContext { + + @Override + protected String[] getPermittedTokens() { + return new String[0]; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ViewDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ViewDslContext.java new file mode 100644 index 000000000..37a5660b8 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ViewDslContext.java @@ -0,0 +1,17 @@ +package com.structurizr.dsl; + +import com.structurizr.view.View; + +abstract class ViewDslContext extends DslContext { + + private final View view; + + ViewDslContext(View view) { + this.view = view; + } + + View getView() { + return view; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ViewParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ViewParser.java new file mode 100644 index 000000000..b632cbc87 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ViewParser.java @@ -0,0 +1,51 @@ +package com.structurizr.dsl; + +import com.structurizr.view.View; + +final class ViewParser extends AbstractParser { + + private static final String TITLE_GRAMMAR = "title "; + private static final String DESCRIPTION_GRAMMAR = "description <description>"; + + private static final int TITLE_INDEX = 1; + private static final int DESCRIPTION_INDEX = 1; + + void parseTitle(ViewDslContext context, Tokens tokens) { + // title <title> + + if (tokens.hasMoreThan(TITLE_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + TITLE_GRAMMAR); + } + + View view = context.getView(); + if (view != null) { + if (tokens.size() == 2) { + String title = tokens.get(TITLE_INDEX); + + view.setTitle(title); + } else { + throw new RuntimeException("Expected: " + TITLE_GRAMMAR); + } + } + } + + void parseDescription(ViewDslContext context, Tokens tokens) { + // description <description> + + if (tokens.hasMoreThan(DESCRIPTION_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + DESCRIPTION_GRAMMAR); + } + + View view = context.getView(); + if (view != null) { + if (tokens.size() == 2) { + String description = tokens.get(DESCRIPTION_INDEX); + + view.setDescription(description); + } else { + throw new RuntimeException("Expected: " + DESCRIPTION_GRAMMAR); + } + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ViewsDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ViewsDslContext.java new file mode 100644 index 000000000..bfa78b285 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ViewsDslContext.java @@ -0,0 +1,25 @@ +package com.structurizr.dsl; + +final class ViewsDslContext extends DslContext { + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.SYSTEM_LANDSCAPE_VIEW_TOKEN, + StructurizrDslTokens.SYSTEM_CONTEXT_VIEW_TOKEN, + StructurizrDslTokens.CONTAINER_VIEW_TOKEN, + StructurizrDslTokens.COMPONENT_VIEW_TOKEN, + StructurizrDslTokens.FILTERED_VIEW_TOKEN, + StructurizrDslTokens.DYNAMIC_VIEW_TOKEN, + StructurizrDslTokens.DEPLOYMENT_VIEW_TOKEN, + StructurizrDslTokens.CUSTOM_VIEW_TOKEN, + StructurizrDslTokens.STYLES_TOKEN, + StructurizrDslTokens.THEME_TOKEN, + StructurizrDslTokens.THEMES_TOKEN, + StructurizrDslTokens.BRANDING_TOKEN, + StructurizrDslTokens.TERMINOLOGY_TOKEN, + StructurizrDslTokens.PROPERTIES_TOKEN + }; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/WorkspaceDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/WorkspaceDslContext.java new file mode 100644 index 000000000..5a5ab7c4e --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/WorkspaceDslContext.java @@ -0,0 +1,21 @@ +package com.structurizr.dsl; + +final class WorkspaceDslContext extends DslContext { + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.NAME_TOKEN, + StructurizrDslTokens.DESCRIPTION_TOKEN, + StructurizrDslTokens.PROPERTIES_TOKEN, + StructurizrDslTokens.DOCS_TOKEN, + StructurizrDslTokens.ADRS_TOKEN, + StructurizrDslTokens.IDENTIFIERS_TOKEN, + StructurizrDslTokens.IMPLIED_RELATIONSHIPS_TOKEN, + StructurizrDslTokens.MODEL_TOKEN, + StructurizrDslTokens.VIEWS_TOKEN, + StructurizrDslTokens.CONFIGURATION_TOKEN + }; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/WorkspaceParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/WorkspaceParser.java new file mode 100644 index 000000000..6218a4a86 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/WorkspaceParser.java @@ -0,0 +1,138 @@ +package com.structurizr.dsl; + +import com.structurizr.Workspace; +import com.structurizr.model.CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy; +import com.structurizr.model.Element; +import com.structurizr.model.Relationship; +import com.structurizr.util.WorkspaceUtils; + +import java.io.File; + +final class WorkspaceParser extends AbstractParser { + + private static final String GRAMMAR_STANDALONE = "workspace [name] [description]"; + private static final String GRAMMAR_EXTENDS = "workspace extends <file|url>"; + + private static final String STRUCTURIZR_DSL_IDENTIFIER_PROPERTY_NAME = "structurizr.dsl.identifier"; + + private static final int FIRST_INDEX = 1; + private static final int SECOND_INDEX = 2; + + Workspace parse(DslParserContext context, Tokens tokens) { + // workspace [name] [description] + // workspace extends <file|url> + + Workspace workspace = new Workspace("Name", "Description"); + workspace.getModel().setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); + + if (tokens.hasMoreThan(SECOND_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR_STANDALONE + " or " + GRAMMAR_EXTENDS); + } + + if (tokens.includes(FIRST_INDEX)) { + String firstToken = tokens.get(FIRST_INDEX); + + if (StructurizrDslTokens.EXTENDS_TOKEN.equals(firstToken)) { + if (tokens.includes(SECOND_INDEX)) { + String source = tokens.get(SECOND_INDEX); + + try { + if (source.startsWith("https://")) { + if (source.endsWith(".json")) { + String json = readFromUrl(source); + workspace = WorkspaceUtils.fromJson(json); + registerIdentifiers(workspace, context); + } else { + String dsl = readFromUrl(source); + StructurizrDslParser structurizrDslParser = new StructurizrDslParser(); + structurizrDslParser.parse(context, dsl); + workspace = structurizrDslParser.getWorkspace(); + } + } else { + if (context.isRestricted()) { + throw new RuntimeException("Cannot import workspace from a file when running in restricted mode"); + } + + if (context.getFile() != null) { + File file = new File(context.getFile().getParent(), source); + if (!file.exists()) { + throw new RuntimeException(file.getCanonicalPath() + " could not be found"); + } + + if (file.isDirectory()) { + throw new RuntimeException(file.getCanonicalPath() + " should be a single file"); + } + + if (source.endsWith(".json")) { + workspace = WorkspaceUtils.loadWorkspaceFromJson(file); + registerIdentifiers(workspace, context); + } else { + StructurizrDslParser structurizrDslParser = new StructurizrDslParser(); + structurizrDslParser.parse(context, file); + workspace = structurizrDslParser.getWorkspace(); + } + } + } + } catch (Exception e) { + throw new RuntimeException(e.getMessage()); + } + } else { + throw new RuntimeException("Expected: " + GRAMMAR_EXTENDS); + } + } else { + workspace.setName(firstToken); + + if (tokens.includes(SECOND_INDEX)) { + workspace.setDescription(tokens.get(SECOND_INDEX)); + } + } + } + + return workspace; + } + + private void registerIdentifiers(Workspace workspace, DslParserContext context) { + for (Element element : workspace.getModel().getElements()) { + if (element.getProperties().containsKey(STRUCTURIZR_DSL_IDENTIFIER_PROPERTY_NAME)) { + String identifier = element.getProperties().get(STRUCTURIZR_DSL_IDENTIFIER_PROPERTY_NAME); + context.identifiersRegister.register(identifier, element); + } + } + + for (Relationship relationship : workspace.getModel().getRelationships()) { + if (relationship.getProperties().containsKey(STRUCTURIZR_DSL_IDENTIFIER_PROPERTY_NAME)) { + String identifier = relationship.getProperties().get(STRUCTURIZR_DSL_IDENTIFIER_PROPERTY_NAME); + context.identifiersRegister.register(identifier, relationship); + } + } + } + + void parseName(DslContext context, Tokens tokens) { + // name <name> + if (tokens.hasMoreThan(FIRST_INDEX)) { + throw new RuntimeException("Too many tokens, expected: name <name>"); + } + + if (!tokens.includes(FIRST_INDEX)) { + throw new RuntimeException("Expected: name <name>"); + } + + String name = tokens.get(FIRST_INDEX); + context.getWorkspace().setName(name); + } + + void parseDescription(DslContext context, Tokens tokens) { + // description <description> + if (tokens.hasMoreThan(FIRST_INDEX)) { + throw new RuntimeException("Too many tokens, expected: description <description>"); + } + + if (!tokens.includes(FIRST_INDEX)) { + throw new RuntimeException("Expected: description <description>"); + } + + String description = tokens.get(FIRST_INDEX); + context.getWorkspace().setDescription(description); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/AbstractExpressionParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/AbstractExpressionParserTests.java new file mode 100644 index 000000000..7a81051ce --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/AbstractExpressionParserTests.java @@ -0,0 +1,506 @@ +package com.structurizr.dsl; + +import com.structurizr.model.*; +import com.structurizr.view.ContainerView; +import com.structurizr.view.DeploymentView; +import com.structurizr.view.SystemLandscapeView; +import org.junit.jupiter.api.Test; + +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.*; + +class AbstractExpressionParserTests extends AbstractTests { + + private StaticViewExpressionParser parser = new StaticViewExpressionParser(); + + @Test + void test_parseExpression_ThrowsAnException_WhenTheRelationshipSourceIsSpecifiedUsingLongSyntaxButDoesNotExist() { + try { + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(null); + context.setWorkspace(workspace); + + parser.parseExpression("relationship.source==a", context); + fail(); + } catch (Exception e) { + assertEquals("The element \"a\" does not exist", e.getMessage()); + } + } + + @Test + void test_parseExpression_ThrowsAnException_WhenTheRelationshipSourceIsSpecifiedUsingShortSyntaxButDoesNotExist() { + try { + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(null); + context.setWorkspace(workspace); + + parser.parseExpression("relationship==a->*", context); + fail(); + } catch (Exception e) { + assertEquals("The element \"a\" does not exist", e.getMessage()); + } + } + + @Test + void test_parseExpression_ThrowsAnException_WhenTheRelationshipDestinationIsSpecifiedUsingLongSyntaxButDoesNotExist() { + try { + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(null); + context.setWorkspace(workspace); + + parser.parseExpression("relationship.destination==a", context); + fail(); + } catch (Exception e) { + assertEquals("The element \"a\" does not exist", e.getMessage()); + } + } + + @Test + void test_parseExpression_ThrowsAnException_WhenTheRelationshipDestinationIsSpecifiedUsingShortSyntaxButDoesNotExist() { + try { + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(null); + context.setWorkspace(workspace); + + parser.parseExpression("relationship==*->a", context); + fail(); + } catch (Exception e) { + assertEquals("The element \"a\" does not exist", e.getMessage()); + } + } + + @Test + void test_parseExpression_ReturnsARelationship_WhenTheRelationshipSourceIsSpecifiedUsingLongSyntax() { + SoftwareSystem a = model.addSoftwareSystem("A"); + SoftwareSystem b = model.addSoftwareSystem("B"); + SoftwareSystem c = model.addSoftwareSystem("C"); + Relationship aToB = a.uses(b, "Uses"); + Relationship bToC = b.uses(c, "Uses"); + + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(null); + context.setWorkspace(workspace); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("a", a); + elements.register("b", b); + elements.register("c", c); + context.setIdentifierRegister(elements); + + Set<ModelItem> relationships = parser.parseExpression("relationship.source==a", context); + assertEquals(1, relationships.size()); + assertTrue(relationships.contains(aToB)); + } + + @Test + void test_parseExpression_ReturnsARelationship_WhenTheRelationshipSourceIsSpecifiedUsingAnExpression() { + SoftwareSystem a = model.addSoftwareSystem("A"); + a.addTags("A"); + SoftwareSystem b = model.addSoftwareSystem("B"); + b.addTags("B"); + SoftwareSystem c = model.addSoftwareSystem("C"); + c.addTags("C"); + Relationship aToB = a.uses(b, "Uses"); + Relationship bToC = b.uses(c, "Uses"); + + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(null); + context.setWorkspace(workspace); + context.setIdentifierRegister(new IdentifiersRegister()); + + Set<ModelItem> relationships = parser.parseExpression("* -> element.tag==B", context); + assertEquals(1, relationships.size()); + assertTrue(relationships.contains(aToB)); + + relationships = parser.parseExpression("element.tag==A -> *", context); + assertEquals(1, relationships.size()); + assertTrue(relationships.contains(aToB)); + + relationships = parser.parseExpression("element.tag==A -> element.tag==B", context); + assertEquals(1, relationships.size()); + assertTrue(relationships.contains(aToB)); + } + + @Test + void test_parseExpression_ReturnsARelationship_WhenTheRelationshipSourceIsSpecifiedUsingShortSyntax() { + SoftwareSystem a = model.addSoftwareSystem("A"); + SoftwareSystem b = model.addSoftwareSystem("B"); + SoftwareSystem c = model.addSoftwareSystem("C"); + Relationship aToB = a.uses(b, "Uses"); + Relationship bToC = b.uses(c, "Uses"); + + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(null); + context.setWorkspace(workspace); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("a", a); + elements.register("b", b); + elements.register("c", c); + context.setIdentifierRegister(elements); + + Set<ModelItem> relationships = parser.parseExpression("relationship==a->*", context); + assertEquals(1, relationships.size()); + assertTrue(relationships.contains(aToB)); + + relationships = parser.parseExpression("a -> *", context); + assertEquals(1, relationships.size()); + assertTrue(relationships.contains(aToB)); + } + + @Test + void test_parseExpression_ReturnsARelationship_WhenTheRelationshipDestinationIsSpecifiedUsingLongSyntax() { + SoftwareSystem a = model.addSoftwareSystem("A"); + SoftwareSystem b = model.addSoftwareSystem("B"); + SoftwareSystem c = model.addSoftwareSystem("C"); + Relationship aToB = a.uses(b, "Uses"); + Relationship bToC = b.uses(c, "Uses"); + + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(null); + context.setWorkspace(workspace); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("a", a); + elements.register("b", b); + elements.register("c", c); + context.setIdentifierRegister(elements); + + Set<ModelItem> relationships = parser.parseExpression("relationship.destination==b", context); + assertEquals(1, relationships.size()); + assertTrue(relationships.contains(aToB)); + } + + @Test + void test_parseExpression_ReturnsARelationship_WhenTheRelationshipDestinationIsSpecifiedUsingShortSyntax() { + SoftwareSystem a = model.addSoftwareSystem("A"); + SoftwareSystem b = model.addSoftwareSystem("B"); + SoftwareSystem c = model.addSoftwareSystem("C"); + Relationship aToB = a.uses(b, "Uses"); + Relationship bToC = b.uses(c, "Uses"); + + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(null); + context.setWorkspace(workspace); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("a", a); + elements.register("b", b); + elements.register("c", c); + context.setIdentifierRegister(elements); + + Set<ModelItem> relationships = parser.parseExpression("relationship==*->b", context); + assertEquals(1, relationships.size()); + assertTrue(relationships.contains(aToB)); + + relationships = parser.parseExpression("* -> b", context); + assertEquals(1, relationships.size()); + assertTrue(relationships.contains(aToB)); + } + + @Test + void test_parseExpression_ReturnsARelationship_WhenTheRelationshipSourceAndDestinationAreSpecifiedUsingLongSyntax() { + SoftwareSystem a = model.addSoftwareSystem("A"); + SoftwareSystem b = model.addSoftwareSystem("B"); + SoftwareSystem c = model.addSoftwareSystem("C"); + Relationship aToB = a.uses(b, "Uses"); + Relationship bToC = b.uses(c, "Uses"); + + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(null); + context.setWorkspace(workspace); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("a", a); + elements.register("b", b); + elements.register("c", c); + context.setIdentifierRegister(elements); + + Set<ModelItem> relationships = parser.parseExpression("relationship.source==a && relationship.destination==b", context); + assertEquals(1, relationships.size()); + assertTrue(relationships.contains(aToB)); + } + + @Test + void test_parseExpression_ReturnsARelationship_WhenTheRelationshipSourceAndDestinationAreSpecifiedUsingShortSyntax() { + SoftwareSystem a = model.addSoftwareSystem("A"); + SoftwareSystem b = model.addSoftwareSystem("B"); + SoftwareSystem c = model.addSoftwareSystem("C"); + Relationship aToB = a.uses(b, "Uses"); + Relationship bToC = b.uses(c, "Uses"); + + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(null); + context.setWorkspace(workspace); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("a", a); + elements.register("b", b); + elements.register("c", c); + context.setIdentifierRegister(elements); + + Set<ModelItem> relationships = parser.parseExpression("relationship==a->b", context); + assertEquals(1, relationships.size()); + assertTrue(relationships.contains(aToB)); + } + + @Test + void test_parseExpression_ReturnsElements_WhenUsingAnAfferentCouplingExpressionWithAnElementIdentifier() { + SoftwareSystem a = model.addSoftwareSystem("A"); + SoftwareSystem b = model.addSoftwareSystem("B"); + SoftwareSystem c = model.addSoftwareSystem("C"); + Relationship aToB = a.uses(b, "Uses"); + Relationship bToC = b.uses(c, "Uses"); + + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(null); + context.setWorkspace(workspace); + + IdentifiersRegister map = new IdentifiersRegister(); + map.register("a", a); + map.register("b", b); + map.register("c", c); + context.setIdentifierRegister(map); + + Set<ModelItem> elements = parser.parseExpression("->b", context); + assertEquals(2, elements.size()); + assertTrue(elements.contains(a)); + assertTrue(elements.contains(b)); + + elements = parser.parseExpression("element==->b", context); + assertEquals(2, elements.size()); + assertTrue(elements.contains(a)); + assertTrue(elements.contains(b)); + } + + @Test + void test_parseExpression_ReturnsElements_WhenUsingAnEfferentCouplingExpressionWithAnElementIdentifier() { + SoftwareSystem a = model.addSoftwareSystem("A"); + SoftwareSystem b = model.addSoftwareSystem("B"); + SoftwareSystem c = model.addSoftwareSystem("C"); + Relationship aToB = a.uses(b, "Uses"); + Relationship bToC = b.uses(c, "Uses"); + + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(null); + context.setWorkspace(workspace); + + IdentifiersRegister map = new IdentifiersRegister(); + map.register("a", a); + map.register("b", b); + map.register("c", c); + context.setIdentifierRegister(map); + + Set<ModelItem> elements = parser.parseExpression("b->", context); + assertEquals(2, elements.size()); + assertTrue(elements.contains(b)); + assertTrue(elements.contains(c)); + + elements = parser.parseExpression("element==b->", context); + assertEquals(2, elements.size()); + assertTrue(elements.contains(b)); + assertTrue(elements.contains(c)); + } + + @Test + void test_parseExpression_ReturnsElements_WhenUsingAnAfferentAndEfferentCouplingExpressionWithAnElementIdentifier() { + SoftwareSystem a = model.addSoftwareSystem("A"); + SoftwareSystem b = model.addSoftwareSystem("B"); + SoftwareSystem c = model.addSoftwareSystem("C"); + Relationship aToB = a.uses(b, "Uses"); + Relationship bToC = b.uses(c, "Uses"); + + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(null); + context.setWorkspace(workspace); + + IdentifiersRegister map = new IdentifiersRegister(); + map.register("a", a); + map.register("b", b); + map.register("c", c); + context.setIdentifierRegister(map); + + Set<ModelItem> elements = parser.parseExpression("->b->", context); + assertEquals(3, elements.size()); + assertTrue(elements.contains(a)); + assertTrue(elements.contains(b)); + assertTrue(elements.contains(c)); + + elements = parser.parseExpression("element==->b->", context); + assertEquals(3, elements.size()); + assertTrue(elements.contains(a)); + assertTrue(elements.contains(b)); + assertTrue(elements.contains(c)); + } + + @Test + void test_parseExpression_ReturnsElements_WhenUsingAnAfferentAndEfferentCouplingExpressionWithAnElementExpression() { + SoftwareSystem a = model.addSoftwareSystem("A"); + SoftwareSystem b = model.addSoftwareSystem("B"); + b.addTags("Tag 1"); + SoftwareSystem c = model.addSoftwareSystem("C"); + Relationship aToB = a.uses(b, "Uses"); + Relationship bToC = b.uses(c, "Uses"); + + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(null); + context.setWorkspace(workspace); + + IdentifiersRegister map = new IdentifiersRegister(); + map.register("a", a); + map.register("b", b); + map.register("c", c); + context.setIdentifierRegister(map); + + Set<ModelItem> elements = parser.parseExpression("->element.tag==Tag 1->", context); + assertEquals(3, elements.size()); + assertTrue(elements.contains(a)); + assertTrue(elements.contains(b)); + assertTrue(elements.contains(c)); + + elements = parser.parseExpression("element==->element.tag==Tag 1->", context); + assertEquals(3, elements.size()); + assertTrue(elements.contains(a)); + assertTrue(elements.contains(b)); + assertTrue(elements.contains(c)); + } + + @Test + void test_parseExpression_ReturnsAllRelationships_WhenUsingTheWildcardRelationshipExpression() { + SoftwareSystem a = model.addSoftwareSystem("A"); + SoftwareSystem b = model.addSoftwareSystem("B"); + SoftwareSystem c = model.addSoftwareSystem("C"); + Relationship aToB = a.uses(b, "Uses"); + Relationship bToC = b.uses(c, "Uses"); + + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(null); + context.setWorkspace(workspace); + + IdentifiersRegister map = new IdentifiersRegister(); + map.register("a", a); + map.register("b", b); + map.register("c", c); + context.setIdentifierRegister(map); + + Set<ModelItem> relationships = parser.parseExpression("relationship==*->*", context); + assertEquals(2, relationships.size()); + assertTrue(relationships.contains(aToB)); + assertTrue(relationships.contains(bToC)); + + relationships = parser.parseExpression("* -> *", context); + assertEquals(2, relationships.size()); + assertTrue(relationships.contains(aToB)); + assertTrue(relationships.contains(bToC)); + + relationships = parser.parseExpression("relationship==*", context); + assertEquals(2, relationships.size()); + assertTrue(relationships.contains(aToB)); + assertTrue(relationships.contains(bToC)); + } + + @Test + void test_parseExpression_ReturnsElements_WhenUsingAnElementTagExpression() { + model.addPerson("User"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + + SystemLandscapeView view = views.createSystemLandscapeView("key", "Description"); + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + context.setIdentifierRegister(new IdentifiersRegister()); + + Set<ModelItem> elements = parser.parseExpression("element.tag==Software System", context); + assertEquals(1, elements.size()); + assertTrue(elements.contains(softwareSystem)); + } + + @Test + void test_parseExpression_ReturnsElementInstances_WhenUsingAnElementTagExpression() { + model.addPerson("User"); + SoftwareSystem ss = model.addSoftwareSystem("Software System"); + SoftwareSystemInstance ssi = model.addDeploymentNode("DN").add(ss); + + DeploymentView view = views.createDeploymentView("key", "Description"); + DeploymentViewDslContext context = new DeploymentViewDslContext(view); + context.setWorkspace(workspace); + context.setIdentifierRegister(new IdentifiersRegister()); + + Set<ModelItem> elements = parser.parseExpression("element.tag==Software System", context); + assertEquals(2, elements.size()); + assertTrue(elements.contains(ss)); // this is tagged "Software System" + assertTrue(elements.contains(ssi)); // this is not tagged "Software System", but the element it's based upon is + } + + @Test + void test_parseExpression_ReturnsElements_WhenUsingAnElementTypeExpression() { + model.addPerson("User"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + + SystemLandscapeView view = views.createSystemLandscapeView("key", "Description"); + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + context.setIdentifierRegister(new IdentifiersRegister()); + + Set<ModelItem> elements = parser.parseExpression("element.type==SoftwareSystem", context); + assertEquals(1, elements.size()); + assertTrue(elements.contains(softwareSystem)); + } + + @Test + void test_parseExpression_ThrowsAnException_WhenUsingAnElementParentExpressionAndTheElementDoesNotExist() { + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(null); + context.setWorkspace(workspace); + context.setIdentifierRegister(new IdentifiersRegister()); + + try { + parser.parseExpression("element.parent==a", context); + fail(); + } catch (Exception e) { + assertEquals("The parent element \"a\" does not exist", e.getMessage()); + } + } + + @Test + void test_parseExpression_ReturnsElements_WhenUsingAnElementParentExpression() { + SoftwareSystem softwareSystemA = model.addSoftwareSystem("Software System A"); + Container container1 = softwareSystemA.addContainer("Container 1"); + SoftwareSystem softwareSystemB = model.addSoftwareSystem("Software System B"); + Container container2 = softwareSystemB.addContainer("Container 2"); + + ContainerView view = views.createContainerView(softwareSystemA, "key", "Description"); + ContainerViewDslContext context = new ContainerViewDslContext(view); + context.setWorkspace(workspace); + IdentifiersRegister identifiersRegister = new IdentifiersRegister(); + identifiersRegister.register("b", softwareSystemB); + context.setIdentifierRegister(identifiersRegister); + + Set<ModelItem> elements = parser.parseExpression("element.parent==b", context); + assertEquals(1, elements.size()); + assertTrue(elements.contains(container2)); + } + + @Test + void test_parseExpression_ReturnsRelationships_WhenUsingARelationshipTagExpression() { + SoftwareSystem a = model.addSoftwareSystem("A"); + SoftwareSystem b = model.addSoftwareSystem("B"); + Relationship r = a.uses(b, "Uses"); + r.addTags("Tag 1"); + + SystemLandscapeView view = views.createSystemLandscapeView("key", "Description"); + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + context.setIdentifierRegister(new IdentifiersRegister()); + + Set<ModelItem> relationships = parser.parseExpression("relationship.tag==Tag 1", context); + assertEquals(1, relationships.size()); + assertTrue(relationships.contains(r)); + } + + @Test + void test_parseExpression_ReturnsRelationships_WhenUsingARelationshipTagExpressionAndTheTagIsSetOnTheLinkedRelationship() { + SoftwareSystem a = model.addSoftwareSystem("A"); + SoftwareSystem b = model.addSoftwareSystem("B"); + Relationship r = a.uses(b, "Uses"); + r.addTags("Tag 1"); + + DeploymentNode dn = model.addDeploymentNode("DN"); + SoftwareSystemInstance ai = dn.add(a); + SoftwareSystemInstance bi = dn.add(b); + + DeploymentView view = views.createDeploymentView("key", "Description"); + DeploymentViewDslContext context = new DeploymentViewDslContext(view); + context.setWorkspace(workspace); + context.setIdentifierRegister(new IdentifiersRegister()); + + Set<ModelItem> relationships = parser.parseExpression("relationship.tag==Tag 1", context); + assertEquals(2, relationships.size()); + assertTrue(relationships.contains(r)); + assertTrue(relationships.contains(ai.getRelationships().iterator().next())); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/AbstractTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/AbstractTests.java new file mode 100644 index 000000000..3408890d4 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/AbstractTests.java @@ -0,0 +1,30 @@ +package com.structurizr.dsl; + +import com.structurizr.Workspace; +import com.structurizr.model.CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy; +import com.structurizr.model.Model; +import com.structurizr.view.ViewSet; + +import java.util.ArrayList; +import java.util.Arrays; + +abstract class AbstractTests { + + protected Workspace workspace = new Workspace("Name", "Description"); + protected Model model = workspace.getModel(); + protected ViewSet views = workspace.getViews(); + + protected ModelDslContext context() { + model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); + + ModelDslContext context = new ModelDslContext(); + context.setWorkspace(workspace); + + return context; + } + + protected Tokens tokens(String... tokens) { + return new Tokens(new ArrayList<>(Arrays.asList(tokens))); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/AdrsParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/AdrsParserTests.java new file mode 100644 index 000000000..240a3a621 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/AdrsParserTests.java @@ -0,0 +1,22 @@ +package com.structurizr.dsl; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +class AdrsParserTests extends AbstractTests { + + private AdrsParser parser = new AdrsParser(); + + @Test + void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parse(new WorkspaceDslContext(), null, tokens("adrs", "path", "fqn", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: !adrs <path> <fqn>", e.getMessage()); + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/AutoLayoutParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/AutoLayoutParserTests.java new file mode 100644 index 000000000..ea6e1e68f --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/AutoLayoutParserTests.java @@ -0,0 +1,111 @@ +package com.structurizr.dsl; + +import com.structurizr.view.AutomaticLayout; +import com.structurizr.view.SystemLandscapeView; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class AutoLayoutParserTests extends AbstractTests { + + private AutoLayoutParser parser = new AutoLayoutParser(); + + @Test + void test_parse_EnablesAutoLayoutWithSomeDefaults() { + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "description"); + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + assertNull(view.getAutomaticLayout()); + parser.parse(context, tokens("autoLayout")); + assertEquals(AutomaticLayout.Implementation.Graphviz, view.getAutomaticLayout().getImplementation()); + assertEquals(AutomaticLayout.RankDirection.TopBottom, view.getAutomaticLayout().getRankDirection()); + assertEquals(300, view.getAutomaticLayout().getRankSeparation()); + assertEquals(300, view.getAutomaticLayout().getNodeSeparation()); + } + + @Test + void test_parse_EnablesAutoLayoutWithAValidRankDirection() { + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "description"); + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + assertNull(view.getAutomaticLayout()); + parser.parse(context, tokens("autoLayout", "lr")); + assertEquals(AutomaticLayout.Implementation.Graphviz, view.getAutomaticLayout().getImplementation()); + assertEquals(AutomaticLayout.RankDirection.LeftRight, view.getAutomaticLayout().getRankDirection()); + assertEquals(300, view.getAutomaticLayout().getRankSeparation()); + assertEquals(300, view.getAutomaticLayout().getNodeSeparation()); + } + + @Test + void test_parse_ThrowsAnException_WhenAnInvalidRankDirectionIsSpecified() { + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "description"); + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + try { + parser.parse(context, tokens("autoLayout", "hello")); + fail(); + } catch (Exception e) { + assertEquals("Valid rank directions are: tb|bt|lr|rl", e.getMessage()); + } + } + + @Test + void test_parse_EnablesAutoLayoutWithAValidRankSeparation() { + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "description"); + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + assertNull(view.getAutomaticLayout()); + parser.parse(context, tokens("autoLayout", "tb", "123")); + assertEquals(AutomaticLayout.Implementation.Graphviz, view.getAutomaticLayout().getImplementation()); + assertEquals(AutomaticLayout.RankDirection.TopBottom, view.getAutomaticLayout().getRankDirection()); + assertEquals(123, view.getAutomaticLayout().getRankSeparation()); + assertEquals(300, view.getAutomaticLayout().getNodeSeparation()); + } + + @Test + void test_parse_ThrowsAnException_WhenAnInvalidRankSeparationIsSpecified() { + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "description"); + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + try { + parser.parse(context, tokens("autoLayout", "tb", "hello")); + fail(); + } catch (Exception e) { + assertEquals("Rank separation must be positive integer in pixels", e.getMessage()); + } + } + + @Test + void test_parse_EnablesAutoLayoutWithAValidNodeSeparation() { + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "description"); + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + assertNull(view.getAutomaticLayout()); + parser.parse(context, tokens("autoLayout", "tb", "123", "456")); + assertEquals(AutomaticLayout.Implementation.Graphviz, view.getAutomaticLayout().getImplementation()); + assertEquals(AutomaticLayout.RankDirection.TopBottom, view.getAutomaticLayout().getRankDirection()); + assertEquals(123, view.getAutomaticLayout().getRankSeparation()); + assertEquals(456, view.getAutomaticLayout().getNodeSeparation()); + } + + @Test + void test_parse_ThrowsAnException_WhenAnInvalidNodeSeparationIsSpecified() { + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "description"); + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + try { + parser.parse(context, tokens("autoLayout", "tb", "300", "hello")); + fail(); + } catch (Exception e) { + assertEquals("Node separation must be positive integer in pixels", e.getMessage()); + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/BrandingParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/BrandingParserTests.java new file mode 100644 index 000000000..dec579440 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/BrandingParserTests.java @@ -0,0 +1,140 @@ +package com.structurizr.dsl; + +import org.junit.jupiter.api.Test; + +import java.io.File; + +import static org.junit.jupiter.api.Assertions.*; + +class BrandingParserTests extends AbstractTests { + + private BrandingParser parser = new BrandingParser(); + + @Test + void test_parseLogo_ThrowsAnException_WhenThereAreTooManyTokens() { + BrandingDslContext context = new BrandingDslContext(null); + + try { + parser.parseLogo(context, tokens("logo", "path", "extra"), false); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: logo <path|url>", e.getMessage()); + } + } + + @Test + void test_parseLogo_ThrowsAnException_WhenNoPathIsSpecified() { + BrandingDslContext context = new BrandingDslContext(null); + + try { + parser.parseLogo(context, tokens("logo"), false); + fail(); + } catch (Exception e) { + assertEquals("Expected: logo <path|url>", e.getMessage()); + } + } + + @Test + void test_parseLogo_ThrowsAnException_WhenTheLogoDoesNotExist() { + BrandingDslContext context = new BrandingDslContext(new File(".")); + + try { + parser.parseLogo(context, tokens("logo", "hello.png"), false); + fail(); + } catch (Exception e) { + assertEquals("hello.png does not exist", e.getMessage()); + } + } + + @Test + void test_parseLogo_ThrowsAnException_WhenTheFileIsNotSupported() { + BrandingDslContext context = new BrandingDslContext(new File(".")); + + try { + parser.parseLogo(context, tokens("logo", "src/test/resources/dsl/getting-started.dsl"), false); + fail(); + } catch (Exception e) { + e.printStackTrace(); + assertTrue(e.getMessage().endsWith("is not a supported image file.")); + } + } + + @Test + void test_parseLogo_SetsTheLogo_WhenTheLogoDoesExist() { + BrandingDslContext context = new BrandingDslContext(new File(".")); + context.setWorkspace(workspace); + + parser.parseLogo(context, tokens("logo", "src/test/resources/dsl/logo.png"), false); + assertTrue(workspace.getViews().getConfiguration().getBranding().getLogo().startsWith("data:image/png;base64,")); + } + + @Test + void test_parseLogo_SetsTheLogoFromADataUri() { + BrandingDslContext context = new BrandingDslContext(new File(".")); + context.setWorkspace(workspace); + + parser.parseLogo(context, tokens("logo", "data:image/png;base64,123456789012345678901234567890"), true); + assertTrue(workspace.getViews().getConfiguration().getBranding().getLogo().startsWith("data:image/png;base64,123456789012345678901234567890")); + } + + @Test + void test_parseLogo_SetsTheLogoFromAHttpUrl() { + BrandingDslContext context = new BrandingDslContext(new File(".")); + context.setWorkspace(workspace); + + parser.parseLogo(context, tokens("logo", "http://structurizr.com/logo.png"), true); + assertEquals("http://structurizr.com/logo.png", workspace.getViews().getConfiguration().getBranding().getLogo()); + } + + @Test + void test_parseLogo_SetsTheLogoFromAHttpsUrl() { + BrandingDslContext context = new BrandingDslContext(new File(".")); + context.setWorkspace(workspace); + + parser.parseLogo(context, tokens("logo", "https://structurizr.com/logo.png"), true); + assertEquals("https://structurizr.com/logo.png", workspace.getViews().getConfiguration().getBranding().getLogo()); + } + + @Test + void test_parseFont_ThrowsAnException_WhenThereAreTooManyTokens() { + BrandingDslContext context = new BrandingDslContext(null); + + try { + parser.parseFont(context, tokens("font", "name", "url", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: font <name> [url]", e.getMessage()); + } + } + + @Test + void test_parseFont_ThrowsAnException_WhenNoNameIsSpecified() { + BrandingDslContext context = new BrandingDslContext(null); + + try { + parser.parseFont(context, tokens("font")); + fail(); + } catch (Exception e) { + assertEquals("Expected: font <name> [url]", e.getMessage()); + } + } + + @Test + void test_parseFont_SetsTheFontName() { + BrandingDslContext context = new BrandingDslContext(null); + context.setWorkspace(workspace); + + parser.parseFont(context, tokens("font", "Times New Roman")); + assertEquals("Times New Roman", workspace.getViews().getConfiguration().getBranding().getFont().getName()); + } + + @Test + void test_parseFont_SetsTheFontUrl() { + BrandingDslContext context = new BrandingDslContext(null); + context.setWorkspace(workspace); + + parser.parseFont(context, tokens("font", "Open Sans", "https://fonts.googleapis.com/css2?family=Open+Sans")); + assertEquals("https://fonts.googleapis.com/css2?family=Open+Sans", workspace.getViews().getConfiguration().getBranding().getFont().getUrl()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentParserTests.java new file mode 100644 index 000000000..77e4993f2 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentParserTests.java @@ -0,0 +1,127 @@ +package com.structurizr.dsl; + +import com.structurizr.model.Component; +import com.structurizr.model.Container; +import com.structurizr.model.SoftwareSystem; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class ComponentParserTests extends AbstractTests { + + private ComponentParser parser = new ComponentParser(); + + @Test + void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parse(new ContainerDslContext(null), tokens("container", "name", "description", "technology", "tags", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: component <name> [description] [technology] [tags]", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheNameIsNotSpecified() { + try { + parser.parse(new ContainerDslContext(null), tokens("container")); + fail(); + } catch (Exception e) { + assertEquals("Expected: component <name> [description] [technology] [tags]", e.getMessage()); + } + } + + @Test + void test_parse_CreatesAComponent() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + Container container = softwareSystem.addContainer("Container", "Description", "Technology"); + ContainerDslContext context = new ContainerDslContext(container); + parser.parse(context, tokens("component", "Name")); + + assertEquals(3, model.getElements().size()); + Component component = container.getComponentWithName("Name"); + assertNotNull(component); + assertEquals("", component.getDescription()); + assertEquals(null, component.getTechnology()); + assertEquals("Element,Component", component.getTags()); + } + + @Test + void test_parse_CreatesAComponentWithADescription() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + Container container = softwareSystem.addContainer("Container", "Description", "Technology"); + ContainerDslContext context = new ContainerDslContext(container); + parser.parse(context, tokens("component", "Name", "Description")); + + assertEquals(3, model.getElements().size()); + Component component = container.getComponentWithName("Name"); + assertNotNull(component); + assertEquals("Description", component.getDescription()); + assertEquals(null, component.getTechnology()); + assertEquals("Element,Component", component.getTags()); + } + + @Test + void test_parse_CreatesAComponentWithADescriptionAndTechnology() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + Container container = softwareSystem.addContainer("Container", "Description", "Technology"); + ContainerDslContext context = new ContainerDslContext(container); + parser.parse(context, tokens("component", "Name", "Description", "Technology")); + + assertEquals(3, model.getElements().size()); + Component component = container.getComponentWithName("Name"); + assertNotNull(component); + assertEquals("Description", component.getDescription()); + assertEquals("Technology", component.getTechnology()); + assertEquals("Element,Component", component.getTags()); + } + + @Test + void test_parse_CreatesAComponentWithADescriptionAndTechnologyAndTags() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + Container container = softwareSystem.addContainer("Container", "Description", "Technology"); + ContainerDslContext context = new ContainerDslContext(container); + parser.parse(context, tokens("component", "Name", "Description", "Technology", "Tag 1, Tag 2")); + + assertEquals(3, model.getElements().size()); + Component component = container.getComponentWithName("Name"); + assertNotNull(component); + assertEquals("Description", component.getDescription()); + assertEquals("Technology", component.getTechnology()); + assertEquals("Element,Component,Tag 1,Tag 2", component.getTags()); + } + + @Test + void test_parseTechnology_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + Component component = model.addSoftwareSystem("Software System").addContainer("Container").addComponent("Component"); + ComponentDslContext context = new ComponentDslContext(component); + parser.parseTechnology(context, tokens("technology", "technology", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: technology <technology>", e.getMessage()); + } + } + + @Test + void test_parseTechnology_ThrowsAnException_WhenNoDescriptionIsSpecified() { + try { + Component component = model.addSoftwareSystem("Software System").addContainer("Container").addComponent("Component"); + ComponentDslContext context = new ComponentDslContext(component); + parser.parseTechnology(context, tokens("technology")); + fail(); + } catch (Exception e) { + assertEquals("Expected: technology <technology>", e.getMessage()); + } + } + + @Test + void test_parseTechnology_SetsTheDescription_WhenADescriptionIsSpecified() { + Component component = model.addSoftwareSystem("Software System").addContainer("Container").addComponent("Component"); + ComponentDslContext context = new ComponentDslContext(component); + parser.parseTechnology(context, tokens("technology", "Technology")); + + assertEquals("Technology", component.getTechnology()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentViewParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentViewParserTests.java new file mode 100644 index 000000000..a67d1d6d9 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentViewParserTests.java @@ -0,0 +1,111 @@ +package com.structurizr.dsl; + +import com.structurizr.view.ComponentView; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class ComponentViewParserTests extends AbstractTests { + + private ComponentViewParser parser = new ComponentViewParser(); + + @Test + void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { + DslContext context = context(); + try { + parser.parse(context, tokens("component", "container", "key", "description", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: component <container identifier> [key] [description] {", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheContainerIdentifierIsMissing() { + DslContext context = context(); + try { + parser.parse(context, tokens("component")); + fail(); + } catch (Exception e) { + assertEquals("Expected: component <container identifier> [key] [description] {", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheContainerIsNotDefined() { + DslContext context = context(); + try { + parser.parse(context, tokens("component", "container", "key")); + fail(); + } catch (Exception e) { + assertEquals("The container \"container\" does not exist", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheElementIsNotAContainer() { + DslContext context = context(); + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("container", model.addPerson("Name", "Description")); + context.setIdentifierRegister(elements); + + try { + parser.parse(context, tokens("component", "container", "key")); + fail(); + } catch (Exception e) { + assertEquals("The element \"container\" is not a container", e.getMessage()); + } + } + + @Test + void test_parse_CreatesAComponentView() { + DslContext context = context(); + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("container", model.addSoftwareSystem("Name", "Description").addContainer("Container", "Name", "Description")); + context.setIdentifierRegister(elements); + + parser.parse(context, tokens("component", "container")); + List<ComponentView> views = new ArrayList<>(context.getWorkspace().getViews().getComponentViews()); + + assertEquals(1, views.size()); + assertEquals("Component-001", views.get(0).getKey()); + assertEquals("", views.get(0).getDescription()); + assertTrue(views.get(0).getExternalContainerBoundariesVisible()); + } + + @Test + void test_parse_CreatesAComponentViewWithAKey() { + DslContext context = context(); + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("container", model.addSoftwareSystem("Name", "Description").addContainer("container", "Name", "Description")); + context.setIdentifierRegister(elements); + + parser.parse(context, tokens("component", "container", "key")); + List<ComponentView> views = new ArrayList<>(context.getWorkspace().getViews().getComponentViews()); + + assertEquals(1, views.size()); + assertEquals("key", views.get(0).getKey()); + assertEquals("", views.get(0).getDescription()); + assertTrue(views.get(0).getExternalContainerBoundariesVisible()); + } + + @Test + void test_parse_CreatesAComponentViewWithAKeyAndDescription() { + DslContext context = context(); + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("container", model.addSoftwareSystem("Name", "Description").addContainer("container", "Name", "Description")); + context.setIdentifierRegister(elements); + + parser.parse(context, tokens("component", "container", "key", "Description")); + List<ComponentView> views = new ArrayList<>(context.getWorkspace().getViews().getComponentViews()); + + assertEquals(1, views.size()); + assertEquals("key", views.get(0).getKey()); + assertEquals("Description", views.get(0).getDescription()); + assertTrue(views.get(0).getExternalContainerBoundariesVisible()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ConfigurationParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ConfigurationParserTests.java new file mode 100644 index 000000000..157c9bc61 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ConfigurationParserTests.java @@ -0,0 +1,86 @@ +package com.structurizr.dsl; + +import com.structurizr.configuration.Visibility; +import com.structurizr.configuration.WorkspaceScope; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +class ConfigurationParserTests extends AbstractTests { + + private final ConfigurationParser parser = new ConfigurationParser(); + + @Test + void test_parseScope_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parseScope(context(), tokens("scope", "landscape", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: scope <landscape|softwaresystem|none>", e.getMessage()); + } + } + + @Test + void test_parseScope_ThrowsAnException_WhenTheScopeIsMissing() { + try { + parser.parseScope(context(), tokens("scope")); + fail(); + } catch (Exception e) { + assertEquals("Expected: scope <landscape|softwaresystem|none>", e.getMessage()); + } + } + + @Test + void test_parseScope_ThrowsAnException_WhenTheScopeIsNotValid() { + try { + parser.parseScope(context(), tokens("scope", "container")); + fail(); + } catch (Exception e) { + assertEquals("The scope \"container\" is not valid", e.getMessage()); + } + } + + @Test + void test_parseScope_SetsTheScope() { + parser.parseScope(context(), tokens("scope", "softwaresystem")); + assertEquals(WorkspaceScope.SoftwareSystem, workspace.getConfiguration().getScope()); + } + + @Test + void test_parseVisibility_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parseVisibility(context(), tokens("visibility", "public", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: visibility <private|public>", e.getMessage()); + } + } + + @Test + void test_parseVisibility_ThrowsAnException_WhenTheVisibilityIsMissing() { + try { + parser.parseVisibility(context(), tokens("visibility")); + fail(); + } catch (Exception e) { + assertEquals("Expected: visibility <private|public>", e.getMessage()); + } + } + + @Test + void test_parseVisibility_ThrowsAnException_WhenTheVisibilityIsNotValid() { + try { + parser.parseVisibility(context(), tokens("visibility", "shared")); + fail(); + } catch (Exception e) { + assertEquals("The visibility \"shared\" is not valid", e.getMessage()); + } + } + + @Test + void test_parseVisibility_SetsTheVisibility() { + parser.parseVisibility(context(), tokens("visibility", "public")); + assertEquals(Visibility.Public, workspace.getConfiguration().getVisibility()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ConstantParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ConstantParserTests.java new file mode 100644 index 000000000..46d645e4f --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ConstantParserTests.java @@ -0,0 +1,59 @@ +package com.structurizr.dsl; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +class ConstantParserTests extends AbstractTests { + + private ConstantParser parser = new ConstantParser(); + + @Test + void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parse(context(), tokens("!constant", "name", "value", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: !constant <name> <value>", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenNoNameOrValueIsSpecified() { + try { + parser.parse(context(), tokens("!constant")); + fail(); + } catch (Exception e) { + assertEquals("Expected: !constant <name> <value>", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenNoValueIsSpecified() { + try { + parser.parse(context(), tokens("!constant", "name")); + fail(); + } catch (Exception e) { + assertEquals("Expected: !constant <name> <value>", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenNameContainsDisallowedCharacters() { + try { + parser.parse(context(), tokens("!constant", "${NAME}", "value")); + fail(); + } catch (Exception e) { + assertEquals("Constant names must only contain the following characters: a-zA-Z0-9-_.", e.getMessage()); + } + } + + @Test + void test_parse_CreatesAConstant() { + Constant constant = parser.parse(context(), tokens("!constant", "name", "value")); + assertEquals("name", constant.getName()); + assertEquals("value", constant.getValue()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ContainerInstanceParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ContainerInstanceParserTests.java new file mode 100644 index 000000000..073602d61 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ContainerInstanceParserTests.java @@ -0,0 +1,147 @@ +package com.structurizr.dsl; + +import com.structurizr.model.*; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class ContainerInstanceParserTests extends AbstractTests { + + private ContainerInstanceParser parser = new ContainerInstanceParser(); + + @Test + void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parse(new DeploymentNodeDslContext(null), tokens("containerInstance", "identifier", "deploymentGroups", "tags", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: containerInstance <identifier> [deploymentGroups] [tags]", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheIdentifierIsNotSpecified() { + try { + parser.parse(new DeploymentNodeDslContext(null), tokens("containerInstance")); + fail(); + } catch (Exception e) { + assertEquals("Expected: containerInstance <identifier> [deploymentGroups] [tags]", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheElementDoesNotExist() { + try { + parser.parse(new DeploymentNodeDslContext(null), tokens("containerInstance", "container")); + fail(); + } catch (Exception e) { + assertEquals("The container \"container\" does not exist", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheElementIsNotAContainer() { + DeploymentNodeDslContext context = new DeploymentNodeDslContext(null); + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("container", model.addPerson("Name", "Description")); + context.setIdentifierRegister(elements); + + try { + parser.parse(context, tokens("containerInstance", "container")); + fail(); + } catch (Exception e) { + assertEquals("The container \"container\" does not exist", e.getMessage()); + } + } + + @Test + void test_parse_CreatesAContainerInstanceInTheDefaultDeploymentGroup() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + Container container = softwareSystem.addContainer("Container", "Description", "Technology"); + DeploymentNode deploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + DeploymentNodeDslContext context = new DeploymentNodeDslContext(deploymentNode); + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("container", container); + context.setIdentifierRegister(elements); + + parser.parse(context, tokens("containerInstance", "container")); + + assertEquals(4, model.getElements().size()); + assertEquals(1, deploymentNode.getContainerInstances().size()); + ContainerInstance containerInstance = deploymentNode.getContainerInstances().iterator().next(); + assertSame(container, containerInstance.getContainer()); + assertEquals("Container Instance", containerInstance.getTags()); + assertEquals("Live", containerInstance.getEnvironment()); + assertEquals(1, containerInstance.getDeploymentGroups().size()); + assertEquals("Default", containerInstance.getDeploymentGroups().iterator().next()); + } + + @Test + void test_parse_CreatesAContainerInstanceInTheDefaultDeploymentGroupWithTags() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + Container container = softwareSystem.addContainer("Container", "Description", "Technology"); + DeploymentNode deploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + DeploymentNodeDslContext context = new DeploymentNodeDslContext(deploymentNode); + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("container", container); + context.setIdentifierRegister(elements); + + parser.parse(context, tokens("containerInstance", "container", "", "Tag 1, Tag 2")); + + assertEquals(4, model.getElements().size()); + assertEquals(1, deploymentNode.getContainerInstances().size()); + ContainerInstance containerInstance = deploymentNode.getContainerInstances().iterator().next(); + assertSame(container, containerInstance.getContainer()); + assertEquals("Container Instance,Tag 1,Tag 2", containerInstance.getTags()); + assertEquals("Live", containerInstance.getEnvironment()); + assertEquals(1, containerInstance.getDeploymentGroups().size()); + assertEquals("Default", containerInstance.getDeploymentGroups().iterator().next()); + } + + @Test + void test_parse_CreatesAContainerInstanceInTheSpecifiedDeploymentGroup() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + Container container = softwareSystem.addContainer("Container", "Description", "Technology"); + DeploymentNode deploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + DeploymentNodeDslContext context = new DeploymentNodeDslContext(deploymentNode); + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("container", container); + elements.register("group", new DeploymentGroup("Group")); + context.setIdentifierRegister(elements); + + parser.parse(context, tokens("containerInstance", "container", "group")); + + assertEquals(4, model.getElements().size()); + assertEquals(1, deploymentNode.getContainerInstances().size()); + ContainerInstance containerInstance = deploymentNode.getContainerInstances().iterator().next(); + assertSame(container, containerInstance.getContainer()); + assertEquals("Container Instance", containerInstance.getTags()); + assertEquals("Live", containerInstance.getEnvironment()); + assertEquals(1, containerInstance.getDeploymentGroups().size()); + assertEquals("Group", containerInstance.getDeploymentGroups().iterator().next()); + } + + @Test + void test_parse_CreatesAContainerInstanceInTheSpecifiedDeploymentGroupWithTags() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + Container container = softwareSystem.addContainer("Container", "Description", "Technology"); + DeploymentNode deploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + DeploymentNodeDslContext context = new DeploymentNodeDslContext(deploymentNode); + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("container", container); + elements.register("group", new DeploymentGroup("Group")); + context.setIdentifierRegister(elements); + + parser.parse(context, tokens("containerInstance", "container", "group", "Tag 1, Tag 2")); + + assertEquals(4, model.getElements().size()); + assertEquals(1, deploymentNode.getContainerInstances().size()); + ContainerInstance containerInstance = deploymentNode.getContainerInstances().iterator().next(); + assertSame(container, containerInstance.getContainer()); + assertEquals("Container Instance,Tag 1,Tag 2", containerInstance.getTags()); + assertEquals("Live", containerInstance.getEnvironment()); + assertEquals(1, containerInstance.getDeploymentGroups().size()); + assertEquals("Group", containerInstance.getDeploymentGroups().iterator().next()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ContainerParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ContainerParserTests.java new file mode 100644 index 000000000..79fc43a77 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ContainerParserTests.java @@ -0,0 +1,122 @@ +package com.structurizr.dsl; + +import com.structurizr.model.Container; +import com.structurizr.model.SoftwareSystem; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class ContainerParserTests extends AbstractTests { + + private ContainerParser parser = new ContainerParser(); + + @Test + void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parse(new SoftwareSystemDslContext(null), tokens("container", "name", "description", "technology", "tags", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: container <name> [description] [technology] [tags]", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheNameIsNotSpecified() { + try { + parser.parse(new SoftwareSystemDslContext(null), tokens("container")); + fail(); + } catch (Exception e) { + assertEquals("Expected: container <name> [description] [technology] [tags]", e.getMessage()); + } + } + + @Test + void test_parse_CreatesAContainer() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + SoftwareSystemDslContext context = new SoftwareSystemDslContext(softwareSystem); + parser.parse(context, tokens("container", "Name")); + + assertEquals(2, model.getElements().size()); + Container container = softwareSystem.getContainerWithName("Name"); + assertNotNull(container); + assertEquals("", container.getDescription()); + assertEquals("", container.getTechnology()); + assertEquals("Element,Container", container.getTags()); + } + + @Test + void test_parse_CreatesAContainerWithADescription() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + SoftwareSystemDslContext context = new SoftwareSystemDslContext(softwareSystem); + parser.parse(context, tokens("container", "Name", "Description")); + + assertEquals(2, model.getElements().size()); + Container container = softwareSystem.getContainerWithName("Name"); + assertNotNull(container); + assertEquals("Description", container.getDescription()); + assertEquals("", container.getTechnology()); + assertEquals("Element,Container", container.getTags()); + } + + @Test + void test_parse_CreatesAContainerWithADescriptionAndTechnology() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + SoftwareSystemDslContext context = new SoftwareSystemDslContext(softwareSystem); + parser.parse(context, tokens("container", "Name", "Description", "Technology")); + + assertEquals(2, model.getElements().size()); + Container container = softwareSystem.getContainerWithName("Name"); + assertNotNull(container); + assertEquals("Description", container.getDescription()); + assertEquals("Technology", container.getTechnology()); + assertEquals("Element,Container", container.getTags()); + } + + @Test + void test_parse_CreatesAContainerWithADescriptionAndTechnologyAndTags() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + SoftwareSystemDslContext context = new SoftwareSystemDslContext(softwareSystem); + parser.parse(context, tokens("container", "Name", "Description", "Technology", "Tag 1, Tag 2")); + + assertEquals(2, model.getElements().size()); + Container container = softwareSystem.getContainerWithName("Name"); + assertNotNull(container); + assertEquals("Description", container.getDescription()); + assertEquals("Technology", container.getTechnology()); + assertEquals("Element,Container,Tag 1,Tag 2", container.getTags()); + } + + @Test + void test_parseTechnology_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + Container container = model.addSoftwareSystem("Software System").addContainer("Container"); + ContainerDslContext context = new ContainerDslContext(container); + parser.parseTechnology(context, tokens("technology", "technology", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: technology <technology>", e.getMessage()); + } + } + + @Test + void test_parseTechnology_ThrowsAnException_WhenNoDescriptionIsSpecified() { + try { + Container container = model.addSoftwareSystem("Software System").addContainer("Container"); + ContainerDslContext context = new ContainerDslContext(container); + parser.parseTechnology(context, tokens("technology")); + fail(); + } catch (Exception e) { + assertEquals("Expected: technology <technology>", e.getMessage()); + } + } + + @Test + void test_parseTechnology_SetsTheDescription_WhenADescriptionIsSpecified() { + Container container = model.addSoftwareSystem("Software System").addContainer("Container"); + ContainerDslContext context = new ContainerDslContext(container); + parser.parseTechnology(context, tokens("technology", "Technology")); + + assertEquals("Technology", container.getTechnology()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ContainerViewParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ContainerViewParserTests.java new file mode 100644 index 000000000..3044d5900 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ContainerViewParserTests.java @@ -0,0 +1,111 @@ +package com.structurizr.dsl; + +import com.structurizr.view.ContainerView; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class ContainerViewParserTests extends AbstractTests { + + private ContainerViewParser parser = new ContainerViewParser(); + + @Test + void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { + DslContext context = context(); + try { + parser.parse(context, tokens("container", "identifier", "key", "description", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: container <software system identifier> [key] [description] {", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheSoftwareSystemIdentifierIsMissing() { + DslContext context = context(); + try { + parser.parse(context, tokens("container")); + fail(); + } catch (Exception e) { + assertEquals("Expected: container <software system identifier> [key] [description] {", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheSoftwareSystemIsNotDefined() { + DslContext context = context(); + try { + parser.parse(context, tokens("container", "softwareSystem", "key")); + fail(); + } catch (Exception e) { + assertEquals("The software system \"softwareSystem\" does not exist", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheElementIsNotASoftwareSystem() { + DslContext context = context(); + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("softwaresystem", model.addPerson("Name", "Description")); + context.setIdentifierRegister(elements); + + try { + parser.parse(context, tokens("container", "softwareSystem", "key")); + fail(); + } catch (Exception e) { + assertEquals("The element \"softwareSystem\" is not a software system", e.getMessage()); + } + } + + @Test + void test_parse_CreatesAContainerView() { + DslContext context = context(); + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("softwaresystem", model.addSoftwareSystem("Name", "Description")); + context.setIdentifierRegister(elements); + + parser.parse(context, tokens("container", "softwareSystem")); + List<ContainerView> views = new ArrayList<>(context.getWorkspace().getViews().getContainerViews()); + + assertEquals(1, views.size()); + assertEquals("Container-001", views.get(0).getKey()); + assertEquals("", views.get(0).getDescription()); + assertTrue(views.get(0).getExternalSoftwareSystemBoundariesVisible()); + } + + @Test + void test_parse_CreatesAContainerViewWithAKey() { + DslContext context = context(); + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("softwaresystem", model.addSoftwareSystem("Name", "Description")); + context.setIdentifierRegister(elements); + + parser.parse(context, tokens("container", "softwareSystem", "key")); + List<ContainerView> views = new ArrayList<>(context.getWorkspace().getViews().getContainerViews()); + + assertEquals(1, views.size()); + assertEquals("key", views.get(0).getKey()); + assertEquals("", views.get(0).getDescription()); + assertTrue(views.get(0).getExternalSoftwareSystemBoundariesVisible()); + } + + @Test + void test_parse_CreatesAContainerViewWithAKeyAndDescription() { + DslContext context = context(); + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("softwaresystem", model.addSoftwareSystem("Name", "Description")); + context.setIdentifierRegister(elements); + + parser.parse(context, tokens("container", "softwareSystem", "key", "Description")); + List<ContainerView> views = new ArrayList<>(context.getWorkspace().getViews().getContainerViews()); + + assertEquals(1, views.size()); + assertEquals("key", views.get(0).getKey()); + assertEquals("Description", views.get(0).getDescription()); + assertTrue(views.get(0).getExternalSoftwareSystemBoundariesVisible()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/CookbookTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/CookbookTests.java new file mode 100644 index 000000000..f5c666ecf --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/CookbookTests.java @@ -0,0 +1,33 @@ +package com.structurizr.dsl; + +import org.junit.jupiter.api.Test; + +import java.io.File; + +class CookbookTests extends AbstractTests { + + @Test + void test() throws Exception { + File cookbookDirectory = new File("docs/cookbook"); + parseDslFiles(cookbookDirectory); + } + + private void parseDslFiles(File directory) throws Exception { + File[] files = directory.listFiles(); + if (files != null) { + for (File file : files) { + if (file.isDirectory()) { + parseDslFiles(file); + } else if (file.getName().startsWith("example-") && file.getName().endsWith(".dsl")) { + parseDslFile(file); + } + } + } + } + + private void parseDslFile(File file) throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(file); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/CustomElementParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/CustomElementParserTests.java new file mode 100644 index 000000000..37c108b19 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/CustomElementParserTests.java @@ -0,0 +1,79 @@ +package com.structurizr.dsl; + +import com.structurizr.model.CustomElement; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class CustomElementParserTests extends AbstractTests { + + private CustomElementParser parser = new CustomElementParser(); + + @Test + void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parse(context(), tokens("element", "name", "metadata", "description", "tags", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: element <name> [metadata] [description] [tags]", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheNameIsNotSpecified() { + try { + parser.parse(context(), tokens("element")); + fail(); + } catch (Exception e) { + assertEquals("Expected: element <name> [metadata] [description] [tags]", e.getMessage()); + } + } + + @Test + void test_parse_CreatesACustomElement() { + parser.parse(context(), tokens("element", "Name")); + + assertEquals(1, model.getElements().size()); + CustomElement element = model.getCustomElementWithName("Name"); + assertNotNull(element); + assertEquals("", element.getDescription()); + assertEquals("Element", element.getTags()); + } + + @Test + void test_parse_CreatesACustomElementWithMetadata() { + parser.parse(context(), tokens("element", "Name", "Box")); + + assertEquals(1, model.getElements().size()); + CustomElement element = model.getCustomElementWithName("Name"); + assertNotNull(element); + assertEquals("Box", element.getMetadata()); + assertEquals("", element.getDescription()); + assertEquals("Element", element.getTags()); + } + + @Test + void test_parse_CreatesACustomElementWithMetadataAndDescription() { + parser.parse(context(), tokens("element", "Name", "Box", "Description")); + + assertEquals(1, model.getElements().size()); + CustomElement element = model.getCustomElementWithName("Name"); + assertNotNull(element); + assertEquals("Box", element.getMetadata()); + assertEquals("Description", element.getDescription()); + assertEquals("Element", element.getTags()); + } + + @Test + void test_parse_CreatesACustomElementWithMetadataAndDescriptionAndTags() { + parser.parse(context(), tokens("element", "Name", "Box", "Description", "Tag 1, Tag 2")); + + assertEquals(1, model.getElements().size()); + CustomElement element = model.getCustomElementWithName("Name"); + assertNotNull(element); + assertEquals("Box", element.getMetadata()); + assertEquals("Description", element.getDescription()); + assertEquals("Element,Tag 1,Tag 2", element.getTags()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/CustomViewAnimationStepParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/CustomViewAnimationStepParserTests.java new file mode 100644 index 000000000..b820ae888 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/CustomViewAnimationStepParserTests.java @@ -0,0 +1,32 @@ +package com.structurizr.dsl; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +class CustomViewAnimationStepParserTests extends AbstractTests { + + private CustomViewAnimationStepParser parser = new CustomViewAnimationStepParser(); + + @Test + void test_parseExplicit_ThrowsAnException_WhenElementsAreMissing() { + try { + parser.parse((CustomViewDslContext)null, tokens("animationStep")); + fail(); + } catch (Exception e) { + assertEquals("Expected: animationStep <identifier> [identifier...]", e.getMessage()); + } + } + + @Test + void test_parseImplicit_ThrowsAnException_WhenElementsAreMissing() { + try { + parser.parse((CustomViewAnimationDslContext) null, tokens()); + fail(); + } catch (Exception e) { + assertEquals("Expected: <identifier> [identifier...]", e.getMessage()); + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/CustomViewContentParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/CustomViewContentParserTests.java new file mode 100644 index 000000000..25e616b5d --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/CustomViewContentParserTests.java @@ -0,0 +1,357 @@ +package com.structurizr.dsl; + +import com.structurizr.model.*; +import com.structurizr.view.CustomView; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class CustomViewContentParserTests extends AbstractTests { + + private CustomViewContentParser parser = new CustomViewContentParser(); + + @Test + void test_parseInclude_ThrowsAnException_WhenTheNoElementsAreSpecified() { + try { + parser.parseInclude(new CustomViewDslContext(null), tokens("include")); + fail(); + } catch (RuntimeException iae) { + assertEquals("Expected: include <*|identifier> [*|identifier...]", iae.getMessage()); + } + } + + @Test + void test_parseInclude_ThrowsAnException_WhenTheSpecifiedElementDoesNotExist() { + try { + parser.parseInclude(new CustomViewDslContext(null), tokens("include", "box")); + fail(); + } catch (RuntimeException iae) { + assertEquals("The element/relationship \"box\" does not exist", iae.getMessage()); + } + } + + @Test + void test_parseInclude_ThrowsAnException_WhenTryingToAddAStaticStructureElementToACustomView() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + + CustomView view = views.createCustomView("key", "Title", "Description"); + CustomViewDslContext context = new CustomViewDslContext(view); + context.setWorkspace(workspace); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("element", softwareSystem); + context.setIdentifierRegister(elements); + + try { + parser.parseInclude(context, tokens("include", "element")); + fail(); + } catch (RuntimeException iae) { + assertEquals("The element \"element\" can not be added to this type of view", iae.getMessage()); + } + } + + @Test + void test_parseInclude_ThrowsAnException_WhenTryingToAddADeploymentElementToACustomView() { + DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node"); + + CustomView view = views.createCustomView("key", "Title", "Description"); + CustomViewDslContext context = new CustomViewDslContext(view); + context.setWorkspace(workspace); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("element", deploymentNode); + context.setIdentifierRegister(elements); + + try { + parser.parseInclude(context, tokens("include", "element")); + fail(); + } catch (RuntimeException iae) { + assertEquals("The element \"element\" can not be added to this type of view", iae.getMessage()); + } + } + + @Test + void test_parseInclude_AddsAllCustomElementsToA_WhenTheWildcardIsSpecified() { + CustomElement box1 = model.addCustomElement("Box 1"); + CustomElement box2 = model.addCustomElement("Box 2"); + box1.uses(box2, ""); + + CustomView view = views.createCustomView("key", "Title", "Description"); + CustomViewDslContext context = new CustomViewDslContext(view); + context.setWorkspace(workspace); + + parser.parseInclude(context, tokens("include", "*")); + + assertEquals(2, view.getElements().size()); + assertEquals(1, view.getRelationships().size()); + } + + @Test + void test_parseInclude_AddsTheSpecifiedPeopleAndSoftwareSystemsToASystemLandscapeView() { + CustomElement box1 = model.addCustomElement("Box 1"); + CustomElement box2 = model.addCustomElement("Box 2"); + CustomElement box3 = model.addCustomElement("Box 3"); + box1.uses(box2, ""); + box2.uses(box3, ""); + + CustomView view = views.createCustomView("key", "Title", "Description"); + CustomViewDslContext context = new CustomViewDslContext(view); + context.setWorkspace(workspace); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("box1", box1); + elements.register("box2", box2); + elements.register("box3", box3); + context.setIdentifierRegister(elements); + + parser.parseInclude(context, tokens("include", "box1", "box2")); + + assertEquals(2, view.getElements().size()); + assertTrue(view.getElements().stream().anyMatch(ev -> ev.getElement().equals(box1))); + assertTrue(view.getElements().stream().anyMatch(ev -> ev.getElement().equals(box2))); + assertEquals(1, view.getRelationships().size()); + } + + @Test + void test_parseInclude_IncludesTheSpecifiedRelationship_WhenARelationshipExpressionIsSpecified() { + CustomElement box1 = model.addCustomElement("Box 1"); + CustomElement box2 = model.addCustomElement("Box 2"); + Relationship relationship = box1.uses(box2, ""); + + CustomView view = views.createCustomView("key", "Title", "Description"); + CustomViewDslContext context = new CustomViewDslContext(view); + context.setWorkspace(workspace); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("box1", box1); + elements.register("box2", box2); + context.setIdentifierRegister(elements); + + view.add(box1); + view.add(box2); + view.remove(relationship); + assertEquals(2, view.getElements().size()); + assertEquals(0, view.getRelationships().size()); + + parser.parseInclude(context, tokens("include", "relationship.source==box1 && relationship.destination==box2")); + assertEquals(1, view.getRelationships().size()); + } + + @Test + void test_parseExclude_ThrowsAnException_WhenTheNoElementsAreSpecified() { + CustomView view = views.createCustomView("key", "Title", "Description"); + CustomViewDslContext context = new CustomViewDslContext(view); + context.setWorkspace(workspace); + + try { + parser.parseExclude(context, tokens("include")); + fail(); + } catch (RuntimeException iae) { + assertEquals("Expected: exclude <identifier> [identifier...]", iae.getMessage()); + } + } + + @Test + void test_parseExclude_ThrowsAnException_WhenTheSpecifiedElementDoesNotExist() { + CustomView view = views.createCustomView("key", "Title", "Description"); + CustomViewDslContext context = new CustomViewDslContext(view); + context.setWorkspace(workspace); + + try { + parser.parseExclude(context, tokens("exclude", "box")); + fail(); + } catch (RuntimeException iae) { + assertEquals("The element/relationship \"box\" does not exist", iae.getMessage()); + } + } + + @Test + void test_parseExclude_RemovesTheSpecifiedElementsFromAView() { + CustomElement box1 = model.addCustomElement("Box 1"); + CustomElement box2 = model.addCustomElement("Box 2"); + box1.uses(box2, ""); + + CustomView view = views.createCustomView("key", "Title", "Description"); + view.add(box1); + view.add(box2); + CustomViewDslContext context = new CustomViewDslContext(view); + context.setWorkspace(workspace); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("box1", box1); + elements.register("box2", box2); + context.setIdentifierRegister(elements); + + assertEquals(2, view.getElements().size()); + assertEquals(1, view.getRelationships().size()); + + parser.parseExclude(context, tokens("exclude", "box2")); + + assertEquals(1, view.getElements().size()); + assertTrue(view.getElements().stream().noneMatch(ev -> ev.getElement().equals(box2))); + assertEquals(0, view.getRelationships().size()); + } + + @Test + void test_parseExclude_RemovesTheRelationshipFromAView_WhenAnExplicitIdentifierIsSpecified() { + CustomElement box1 = model.addCustomElement("Box 1"); + CustomElement box2 = model.addCustomElement("Box 2"); + Relationship relationship = box1.uses(box2, ""); + + CustomView view = views.createCustomView("key", "Title", "Description"); + view.add(box1); + view.add(box2); + CustomViewDslContext context = new CustomViewDslContext(view); + context.setWorkspace(workspace); + + IdentifiersRegister identifersRegister = new IdentifiersRegister(); + identifersRegister.register("rel", relationship); + context.setIdentifierRegister(identifersRegister); + + assertEquals(2, view.getElements().size()); + assertEquals(1, view.getRelationships().size()); + + parser.parseExclude(context, tokens("exclude", "rel")); + + assertEquals(2, view.getElements().size()); + assertEquals(0, view.getRelationships().size()); + } + + @Test + void test_parseExclude_ThrowsAnException_WhenTheRelationshipSourceElementDoesNotExistInTheModel() { + try { + CustomView view = views.createCustomView("key", "Title", "Description"); + CustomViewDslContext context = new CustomViewDslContext(view); + context.setWorkspace(workspace); + + parser.parseExclude(context, tokens("exclude", "relationship.source==box1 && relationship.destination==box2")); + + fail(); + } catch (RuntimeException re) { + assertEquals("The element \"box1\" does not exist", re.getMessage()); + } + } + + @Test + void test_parseExclude_ThrowsAnException_WhenTheRelationshipDestinationElementDoesNotExistInTheModel() { + try { + CustomElement box1 = model.addCustomElement("Box 1"); + + CustomView view = views.createCustomView("key", "Title", "Description"); + view.add(box1); + CustomViewDslContext context = new CustomViewDslContext(view); + context.setWorkspace(workspace); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("box1", box1); + context.setIdentifierRegister(elements); + + parser.parseExclude(context, tokens("exclude", "relationship.source==box1 && relationship.source==box2")); + + fail(); + } catch (RuntimeException re) { + assertEquals("The element \"box2\" does not exist", re.getMessage()); + } + } + + @Test + void test_parseExclude_RemovesTheRelationshipFromAView_WhenAnExpressionIsSpecifiedWithSourceAndDestination() { + CustomElement box1 = model.addCustomElement("Box 1"); + CustomElement box2 = model.addCustomElement("Box 2"); + box1.uses(box2, ""); + + CustomView view = views.createCustomView("key", "Title", "Description"); + view.add(box1); + view.add(box2); + CustomViewDslContext context = new CustomViewDslContext(view); + context.setWorkspace(workspace); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("box1", box1); + elements.register("box2", box2); + context.setIdentifierRegister(elements); + + assertEquals(2, view.getElements().size()); + assertEquals(1, view.getRelationships().size()); + + parser.parseExclude(context, tokens("exclude", "relationship.source==box1 && relationship.destination==box2")); + + assertEquals(0, view.getRelationships().size()); + } + + @Test + void test_parseExclude_RemovesTheRelationshipFromAView_WhenAnExpressionIsSpecifiedWithSource() { + CustomElement box1 = model.addCustomElement("Box 1"); + CustomElement box2 = model.addCustomElement("Box 2"); + box1.uses(box2, ""); + + CustomView view = views.createCustomView("key", "Title", "Description"); + view.add(box1); + view.add(box2); + CustomViewDslContext context = new CustomViewDslContext(view); + context.setWorkspace(workspace); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("box1", box1); + elements.register("box2", box2); + context.setIdentifierRegister(elements); + + assertEquals(2, view.getElements().size()); + assertEquals(1, view.getRelationships().size()); + + parser.parseExclude(context, tokens("exclude", "relationship.source==box1")); + + assertEquals(0, view.getRelationships().size()); + } + + @Test + void test_parseExclude_RemovesTheRelationshipFromAView_WhenAnExpressionIsSpecifiedWithADestination() { + CustomElement box1 = model.addCustomElement("Box 1"); + CustomElement box2 = model.addCustomElement("Box 2"); + box1.uses(box2, ""); + + CustomView view = views.createCustomView("key", "Title", "Description"); + view.add(box1); + view.add(box2); + CustomViewDslContext context = new CustomViewDslContext(view); + context.setWorkspace(workspace); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("box1", box1); + elements.register("box2", box2); + context.setIdentifierRegister(elements); + + assertEquals(2, view.getElements().size()); + assertEquals(1, view.getRelationships().size()); + + parser.parseExclude(context, tokens("exclude", "relationship.destination==box2")); + + assertEquals(0, view.getRelationships().size()); + } + + @Test + void test_parseExclude_RemovesAllRelationshipsFromAView_WhenAnExpressionIsSpecifiedWithAWildcard() { + CustomElement box1 = model.addCustomElement("Box 1"); + CustomElement box2 = model.addCustomElement("Box 2"); + box1.uses(box2, ""); + + CustomView view = views.createCustomView("key", "Title", "Description"); + view.add(box1); + view.add(box2); + CustomViewDslContext context = new CustomViewDslContext(view); + context.setWorkspace(workspace); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("box1", box1); + elements.register("box2", box2); + context.setIdentifierRegister(elements); + + assertEquals(2, view.getElements().size()); + assertEquals(1, view.getRelationships().size()); + + parser.parseExclude(context, tokens("exclude", "relationship==*")); + + assertEquals(0, view.getRelationships().size()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/CustomViewParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/CustomViewParserTests.java new file mode 100644 index 000000000..1b6c644ce --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/CustomViewParserTests.java @@ -0,0 +1,75 @@ +package com.structurizr.dsl; + +import com.structurizr.view.CustomView; +import com.structurizr.view.SystemLandscapeView; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +class CustomViewParserTests extends AbstractTests { + + private CustomViewParser parser = new CustomViewParser(); + + @Test + void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parse(context(), tokens("custom", "key", "title", "description", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: custom [key] [title] [description] {", e.getMessage()); + } + } + + @Test + void test_parse_CreatesACustomView() { + DslContext context = context(); + parser.parse(context, tokens("custom")); + List<CustomView> views = new ArrayList<>(context.getWorkspace().getViews().getCustomViews()); + + assertEquals(1, views.size()); + assertEquals("Custom-001", views.get(0).getKey()); + assertEquals("", views.get(0).getTitle()); + assertEquals("", views.get(0).getDescription()); + } + + @Test + void test_parse_CreatesACustomViewWithAKey() { + DslContext context = context(); + parser.parse(context, tokens("custom", "key")); + List<CustomView> views = new ArrayList<>(context.getWorkspace().getViews().getCustomViews()); + + assertEquals(1, views.size()); + assertEquals("key", views.get(0).getKey()); + assertEquals("", views.get(0).getTitle()); + assertEquals("", views.get(0).getDescription()); + } + + @Test + void test_parse_CreatesACustomViewWithAKeyAndTitle() { + DslContext context = context(); + parser.parse(context, tokens("custom", "key", "Title")); + List<CustomView> views = new ArrayList<>(context.getWorkspace().getViews().getCustomViews()); + + assertEquals(1, views.size()); + assertEquals("key", views.get(0).getKey()); + assertEquals("Title", views.get(0).getTitle()); + assertEquals("", views.get(0).getDescription()); + } + + @Test + void test_parse_CreatesACustomViewWithAKeyAndTitleAndDescription() { + DslContext context = context(); + parser.parse(context, tokens("custom", "key", "Title", "Description")); + List<CustomView> views = new ArrayList<>(context.getWorkspace().getViews().getCustomViews()); + + assertEquals(1, views.size()); + assertEquals("key", views.get(0).getKey()); + assertEquals("Title", views.get(0).getTitle()); + assertEquals("Description", views.get(0).getDescription()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DefaultViewParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DefaultViewParserTests.java new file mode 100644 index 000000000..df7eeeffa --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DefaultViewParserTests.java @@ -0,0 +1,24 @@ +package com.structurizr.dsl; + +import com.structurizr.view.SystemLandscapeView; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +class DefaultViewParserTests extends AbstractTests { + + private final DefaultViewParser parser = new DefaultViewParser(); + + @Test + void test_parse_SetsTheDefaultView() { + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "description"); + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + assertNull(context.getWorkspace().getViews().getConfiguration().getDefaultView()); + parser.parse(context); + assertEquals("key", context.getWorkspace().getViews().getConfiguration().getDefaultView()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentEnvironmentParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentEnvironmentParserTests.java new file mode 100644 index 000000000..ebb1cb16c --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentEnvironmentParserTests.java @@ -0,0 +1,38 @@ +package com.structurizr.dsl; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +class DeploymentEnvironmentParserTests extends AbstractTests { + + private DeploymentEnvironmentParser parser = new DeploymentEnvironmentParser(); + + @Test + void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parse(tokens("deploymentEnvironment", "name", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: deploymentEnvironment <name> {", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheNameIsMissing() { + try { + parser.parse(tokens("deploymentEnvironment")); + fail(); + } catch (Exception e) { + assertEquals("Expected: deploymentEnvironment <name> {", e.getMessage()); + } + } + + @Test + void test_parse() { + String environment = parser.parse(tokens("deploymentEnvironment", "Live")); + assertEquals("Live", environment); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentGroupParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentGroupParserTests.java new file mode 100644 index 000000000..71ff1e2ed --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentGroupParserTests.java @@ -0,0 +1,38 @@ +package com.structurizr.dsl; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +class DeploymentGroupParserTests extends AbstractTests { + + private DeploymentGroupParser parser = new DeploymentGroupParser(); + + @Test + void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parse(tokens("deploymentGroup", "name", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: deploymentGroup <name>", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheNameIsMissing() { + try { + parser.parse(tokens("deploymentGroup")); + fail(); + } catch (Exception e) { + assertEquals("Expected: deploymentGroup <name>", e.getMessage()); + } + } + + @Test + void test_parse() { + String service1 = parser.parse(tokens("deploymentGroup", "Service 1")); + assertEquals("Service 1", service1); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentNodeParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentNodeParserTests.java new file mode 100644 index 000000000..d9ec9be15 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentNodeParserTests.java @@ -0,0 +1,222 @@ +package com.structurizr.dsl; + +import com.structurizr.model.Component; +import com.structurizr.model.DeploymentNode; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class DeploymentNodeParserTests extends AbstractTests { + + private DeploymentNodeParser parser = new DeploymentNodeParser(); + + @Test + void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parse(new DeploymentEnvironmentDslContext("env"), tokens("deploymentNode", "name", "description", "technology", "tags", "instances", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: deploymentNode <name> [description] [technology] [tags] [instances] {", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheNameIsNotSpecified() { + try { + parser.parse(new DeploymentEnvironmentDslContext("env"), tokens("deploymentNode")); + fail(); + } catch (Exception e) { + assertEquals("Expected: deploymentNode <name> [description] [technology] [tags] [instances] {", e.getMessage()); + } + } + + @Test + void test_parse_CreatesADeploymentNode() { + DeploymentEnvironmentDslContext context = new DeploymentEnvironmentDslContext("Live"); + context.setWorkspace(workspace); + parser.parse(context, tokens("deploymentNode", "Name")); + + assertEquals(1, model.getElements().size()); + DeploymentNode deploymentNode = model.getDeploymentNodeWithName("Name", "Live"); + assertNotNull(deploymentNode); + assertEquals("", deploymentNode.getDescription()); + assertEquals("", deploymentNode.getTechnology()); + assertEquals("Element,Deployment Node", deploymentNode.getTags()); + assertEquals("1", deploymentNode.getInstances()); + assertEquals("Live", deploymentNode.getEnvironment()); + } + + @Test + void test_parse_CreatesADeploymentNodeWithADescription() { + DeploymentEnvironmentDslContext context = new DeploymentEnvironmentDslContext("Live"); + context.setWorkspace(workspace); + parser.parse(context, tokens("deploymentNode", "Name", "Description")); + + assertEquals(1, model.getElements().size()); + DeploymentNode deploymentNode = model.getDeploymentNodeWithName("Name", "Live"); + assertNotNull(deploymentNode); + assertEquals("Description", deploymentNode.getDescription()); + assertEquals("", deploymentNode.getTechnology()); + assertEquals("Element,Deployment Node", deploymentNode.getTags()); + assertEquals("1", deploymentNode.getInstances()); + assertEquals("Live", deploymentNode.getEnvironment()); + } + + @Test + void test_parse_CreatesADeploymentNodeWithADescriptionAndTechnology() { + DeploymentEnvironmentDslContext context = new DeploymentEnvironmentDslContext("Live"); + context.setWorkspace(workspace); + parser.parse(context, tokens("deploymentNode", "Name", "Description", "Technology")); + + assertEquals(1, model.getElements().size()); + DeploymentNode deploymentNode = model.getDeploymentNodeWithName("Name", "Live"); + assertNotNull(deploymentNode); + assertEquals("Description", deploymentNode.getDescription()); + assertEquals("Technology", deploymentNode.getTechnology()); + assertEquals("Element,Deployment Node", deploymentNode.getTags()); + assertEquals("1", deploymentNode.getInstances()); + assertEquals("Live", deploymentNode.getEnvironment()); + } + + @Test + void test_parse_CreatesADeploymentNodeWithADescriptionAndTechnologyAndTags() { + DeploymentEnvironmentDslContext context = new DeploymentEnvironmentDslContext("Live"); + context.setWorkspace(workspace); + parser.parse(context, tokens("deploymentNode", "Name", "Description", "Technology", "Tag 1, Tag 2")); + + assertEquals(1, model.getElements().size()); + DeploymentNode deploymentNode = model.getDeploymentNodeWithName("Name", "Live"); + assertNotNull(deploymentNode); + assertEquals("Description", deploymentNode.getDescription()); + assertEquals("Technology", deploymentNode.getTechnology()); + assertEquals("Element,Deployment Node,Tag 1,Tag 2", deploymentNode.getTags()); + assertEquals("1", deploymentNode.getInstances()); + assertEquals("Live", deploymentNode.getEnvironment()); + } + + @Test + void test_parse_CreatesADeploymentNodeWithADescriptionAndTechnologyAndTagsAndInstances() { + DeploymentEnvironmentDslContext context = new DeploymentEnvironmentDslContext("Live"); + context.setWorkspace(workspace); + parser.parse(context, tokens("deploymentNode", "Name", "Description", "Technology", "Tag 1, Tag 2", "8")); + + assertEquals(1, model.getElements().size()); + DeploymentNode deploymentNode = model.getDeploymentNodeWithName("Name", "Live"); + assertNotNull(deploymentNode); + assertEquals("Description", deploymentNode.getDescription()); + assertEquals("Technology", deploymentNode.getTechnology()); + assertEquals("Element,Deployment Node,Tag 1,Tag 2", deploymentNode.getTags()); + assertEquals("8", deploymentNode.getInstances()); + assertEquals("Live", deploymentNode.getEnvironment()); + } + + @Test + void test_parse_ThrowsAnException_WhenTheNumberOfInstancesIsNotValid() { + DeploymentEnvironmentDslContext context = new DeploymentEnvironmentDslContext("Live"); + context.setWorkspace(workspace); + + try { + parser.parse(context, tokens("deploymentNode", "Name", "Description", "Technology", "Tag 1, Tag 2", "abc")); + System.out.println(model.getDeploymentNodes().iterator().next().getInstances()); + fail(); + } catch (Exception e) { + assertEquals("Number of instances must be a positive integer or a range.", e.getMessage()); + } + } + + @Test + void test_parse_CreatesAChildDeploymentNode() { + DeploymentNode parent = model.addDeploymentNode("Live", "Parent", "Description", "Technology"); + DeploymentNodeDslContext context = new DeploymentNodeDslContext(parent); + context.setWorkspace(workspace); + parser.parse(context, tokens("deploymentNode", "Name")); + + assertEquals(2, model.getElements().size()); + DeploymentNode deploymentNode = parent.getDeploymentNodeWithName("Name"); + assertNotNull(deploymentNode); + assertEquals("", deploymentNode.getDescription()); + assertEquals("", deploymentNode.getTechnology()); + assertEquals("Element,Deployment Node", deploymentNode.getTags()); + assertEquals("1", deploymentNode.getInstances()); + assertEquals("Live", deploymentNode.getEnvironment()); + } + + @Test + void test_parseTechnology_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node"); + DeploymentNodeDslContext context = new DeploymentNodeDslContext(deploymentNode); + parser.parseTechnology(context, tokens("technology", "technology", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: technology <technology>", e.getMessage()); + } + } + + @Test + void test_parseTechnology_ThrowsAnException_WhenNoDescriptionIsSpecified() { + try { + DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node"); + DeploymentNodeDslContext context = new DeploymentNodeDslContext(deploymentNode); + parser.parseTechnology(context, tokens("technology")); + fail(); + } catch (Exception e) { + assertEquals("Expected: technology <technology>", e.getMessage()); + } + } + + @Test + void test_parseTechnology_SetsTheTechnology_WhenADescriptionIsSpecified() { + DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node"); + DeploymentNodeDslContext context = new DeploymentNodeDslContext(deploymentNode); + parser.parseTechnology(context, tokens("technology", "Technology")); + + assertEquals("Technology", deploymentNode.getTechnology()); + } + + @Test + void test_parseInstances_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node"); + DeploymentNodeDslContext context = new DeploymentNodeDslContext(deploymentNode); + parser.parseInstances(context, tokens("instances", "number", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: instances <number|range>", e.getMessage()); + } + } + + @Test + void test_parseInstances_ThrowsAnException_WhenNoNumberIsSpecified() { + try { + DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node"); + DeploymentNodeDslContext context = new DeploymentNodeDslContext(deploymentNode); + parser.parseInstances(context, tokens("instances")); + fail(); + } catch (Exception e) { + assertEquals("Expected: instances <number|range>", e.getMessage()); + } + } + + @Test + void test_parseInstances_ThrowsAnException_WhenAnInvalidNumberIsSpecified() { + try { + DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node"); + DeploymentNodeDslContext context = new DeploymentNodeDslContext(deploymentNode); + parser.parseInstances(context, tokens("instances", "abc")); + fail(); + } catch (Exception e) { + assertEquals("Number of instances must be a positive integer or a range.", e.getMessage()); + } + } + + @Test + void test_parseInstances_SetsTheInstances_WhenANumberIsSpecified() { + DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node"); + DeploymentNodeDslContext context = new DeploymentNodeDslContext(deploymentNode); + parser.parseInstances(context, tokens("instances", "123")); + + assertEquals("123", deploymentNode.getInstances()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentViewAnimationStepParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentViewAnimationStepParserTests.java new file mode 100644 index 000000000..359931273 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentViewAnimationStepParserTests.java @@ -0,0 +1,32 @@ +package com.structurizr.dsl; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +class DeploymentViewAnimationStepParserTests extends AbstractTests { + + private DeploymentViewAnimationStepParser parser = new DeploymentViewAnimationStepParser(); + + @Test + void test_parseExplicit_ThrowsAnException_WhenElementsAreMissing() { + try { + parser.parse((DeploymentViewDslContext)null, tokens("animationStep")); + fail(); + } catch (Exception e) { + assertEquals("Expected: animationStep <identifier> [identifier...]", e.getMessage()); + } + } + + @Test + void test_parseImplicit_ThrowsAnException_WhenElementsAreMissing() { + try { + parser.parse((DeploymentViewAnimationDslContext)null, tokens()); + fail(); + } catch (Exception e) { + assertEquals("Expected: <identifier> [identifier...]", e.getMessage()); + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentViewContentParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentViewContentParserTests.java new file mode 100644 index 000000000..729ce189e --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentViewContentParserTests.java @@ -0,0 +1,576 @@ +package com.structurizr.dsl; + +import com.structurizr.model.*; +import com.structurizr.view.DeploymentView; +import com.structurizr.view.RelationshipView; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class DeploymentViewContentParserTests extends AbstractTests { + + private DeploymentViewContentParser parser = new DeploymentViewContentParser(); + + @Test + void test_parseInclude_ThrowsAnException_WhenTheNoElementsAreSpecified() { + try { + parser.parseInclude(new DeploymentViewDslContext(null), tokens("include")); + fail(); + } catch (RuntimeException iae) { + assertEquals("Expected: include <*|identifier> [*|identifier...]", iae.getMessage()); + } + } + + @Test + void test_parseInclude_ThrowsAnException_WhenTheSpecifiedElementDoesNotExist() { + try { + parser.parseInclude(new DeploymentViewDslContext(null), tokens("include", "user")); + fail(); + } catch (RuntimeException iae) { + assertEquals("The element/relationship \"user\" does not exist", iae.getMessage()); + } + } + + @Test + void test_parseInclude_AddsAllDeploymentNodesAndChildrenInTheDeploymentEnvironment_WhenTheWildcardIsSpecifiedAndTheViewHasNoSoftwareSystemScope() { + SoftwareSystem ss1 = model.addSoftwareSystem("SS1", "Description"); + Container c1 = ss1.addContainer("C1", "Description", "Technology"); + SoftwareSystem ss2 = model.addSoftwareSystem("SS2", "Description"); + Container c2 = ss2.addContainer("C2", "Description", "Technology"); + + DeploymentNode dev1 = model.addDeploymentNode("Dev", "Dev 1", "Description", "Technology"); + DeploymentNode dev2 = dev1.addDeploymentNode("Dev 2", "Description", "Technology"); + InfrastructureNode dev3 = dev2.addInfrastructureNode("Dev 3", "Description", "Technology"); + ContainerInstance dev4 = dev2.add(c1); + ContainerInstance dev5 = dev2.add(c2); + + DeploymentNode live1 = model.addDeploymentNode("Live", "Live 1", "Description", "Technology"); + DeploymentNode live2 = live1.addDeploymentNode("Live 2", "Description", "Technology"); + InfrastructureNode live3 = live2.addInfrastructureNode("Live 3", "Description", "Technology"); + ContainerInstance live4 = live2.add(c1); + ContainerInstance live5 = live2.add(c2); + + DeploymentView view = views.createDeploymentView("key", "Description"); + view.setEnvironment("Live"); + DeploymentViewDslContext context = new DeploymentViewDslContext(view); + context.setWorkspace(workspace); + + parser.parseInclude(context, tokens("include", "*")); + + assertEquals(5, view.getElements().size()); + assertTrue(view.getElements().stream().anyMatch(ev -> ev.getElement().equals(live1))); + assertTrue(view.getElements().stream().anyMatch(ev -> ev.getElement().equals(live2))); + assertTrue(view.getElements().stream().anyMatch(ev -> ev.getElement().equals(live3))); + assertTrue(view.getElements().stream().anyMatch(ev -> ev.getElement().equals(live4))); + assertTrue(view.getElements().stream().anyMatch(ev -> ev.getElement().equals(live5))); + } + + @Test + void test_parseInclude_AddsAllDeploymentNodesAndChildrenInTheDeploymentEnvironment_WhenTheWildcardIsSpecifiedAndTheViewHasASoftwareSystemScope() { + SoftwareSystem ss1 = model.addSoftwareSystem("SS1", "Description"); + Container c1 = ss1.addContainer("C1", "Description", "Technology"); + SoftwareSystem ss2 = model.addSoftwareSystem("SS2", "Description"); + Container c2 = ss2.addContainer("C2", "Description", "Technology"); + + DeploymentNode dev1 = model.addDeploymentNode("Dev", "Dev 1", "Description", "Technology"); + DeploymentNode dev2 = dev1.addDeploymentNode("Dev 2", "Description", "Technology"); + InfrastructureNode dev3 = dev2.addInfrastructureNode("Dev 3", "Description", "Technology"); + ContainerInstance dev4 = dev2.add(c1); + ContainerInstance dev5 = dev2.add(c2); + + DeploymentNode live1 = model.addDeploymentNode("Live", "Live 1", "Description", "Technology"); + DeploymentNode live2 = live1.addDeploymentNode("Live 2", "Description", "Technology"); + InfrastructureNode live3 = live2.addInfrastructureNode("Live 3", "Description", "Technology"); + ContainerInstance live4 = live2.add(c1); + ContainerInstance live5 = live2.add(c2); + + DeploymentView view = views.createDeploymentView(ss1, "key", "Description"); + view.setEnvironment("Live"); + DeploymentViewDslContext context = new DeploymentViewDslContext(view); + context.setWorkspace(workspace); + + parser.parseInclude(context, tokens("include", "*")); + + assertEquals(4, view.getElements().size()); + assertTrue(view.getElements().stream().anyMatch(ev -> ev.getElement().equals(live1))); + assertTrue(view.getElements().stream().anyMatch(ev -> ev.getElement().equals(live2))); + assertTrue(view.getElements().stream().anyMatch(ev -> ev.getElement().equals(live3))); + assertTrue(view.getElements().stream().anyMatch(ev -> ev.getElement().equals(live4))); + } + + @Test + void test_parseInclude_AddsTheSpecifiedElements() { + SoftwareSystem ss1 = model.addSoftwareSystem("SS1", "Description"); + Container c1 = ss1.addContainer("C1", "Description", "Technology"); + SoftwareSystem ss2 = model.addSoftwareSystem("SS2", "Description"); + Container c2 = ss2.addContainer("C2", "Description", "Technology"); + CustomElement box1 = model.addCustomElement("Box 1"); + + DeploymentNode dev1 = model.addDeploymentNode("Dev", "Dev 1", "Description", "Technology"); + DeploymentNode dev2 = dev1.addDeploymentNode("Dev 2", "Description", "Technology"); + InfrastructureNode dev3 = dev2.addInfrastructureNode("Dev 3", "Description", "Technology"); + ContainerInstance dev4 = dev2.add(c1); + ContainerInstance dev5 = dev2.add(c2); + + DeploymentNode live1 = model.addDeploymentNode("Live", "Live 1", "Description", "Technology"); + DeploymentNode live2 = live1.addDeploymentNode("Live 2", "Description", "Technology"); + InfrastructureNode live3 = live2.addInfrastructureNode("Live 3", "Description", "Technology"); + ContainerInstance live4 = live2.add(c1); + ContainerInstance live5 = live2.add(c2); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("element", live1); + elements.register("box1", box1); + + DeploymentView view = views.createDeploymentView(ss1, "key", "Description"); + view.setEnvironment("Live"); + DeploymentViewDslContext context = new DeploymentViewDslContext(view); + context.setWorkspace(workspace); + context.setIdentifierRegister(elements); + + parser.parseInclude(context, tokens("include", "element", "box1")); + + assertEquals(5, view.getElements().size()); + assertTrue(view.getElements().stream().anyMatch(ev -> ev.getElement().equals(live1))); + assertTrue(view.getElements().stream().anyMatch(ev -> ev.getElement().equals(live2))); + assertTrue(view.getElements().stream().anyMatch(ev -> ev.getElement().equals(live3))); + assertTrue(view.getElements().stream().anyMatch(ev -> ev.getElement().equals(live4))); + assertTrue(view.getElements().stream().anyMatch(ev -> ev.getElement().equals(box1))); + } + + @Test + void test_parseInclude_AddsTheElement_WhenTheElementIsAnInfrastructureNode() { + DeploymentNode dn = model.addDeploymentNode("Live", "DN", "Description", "Technology"); + InfrastructureNode in = dn.addInfrastructureNode("IN", "Description", "Technology"); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("element", in); + + DeploymentView view = views.createDeploymentView("key", "Description"); + view.setEnvironment("Live"); + DeploymentViewDslContext context = new DeploymentViewDslContext(view); + context.setWorkspace(workspace); + context.setIdentifierRegister(elements); + + parser.parseInclude(context, tokens("include", "element")); + + assertEquals(2, view.getElements().size()); + assertNotNull(view.getElementView(dn)); + assertNotNull(view.getElementView(in)); + } + + @Test + void test_parseInclude_AddsTheElement_WhenTheElementIsASoftwareSystemInstance() { + DeploymentNode dn = model.addDeploymentNode("Live", "DN", "Description", "Technology"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + SoftwareSystemInstance softwareSystemInstance = dn.add(softwareSystem); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("element", softwareSystemInstance); + + DeploymentView view = views.createDeploymentView("key", "Description"); + view.setEnvironment("Live"); + DeploymentViewDslContext context = new DeploymentViewDslContext(view); + context.setWorkspace(workspace); + context.setIdentifierRegister(elements); + + parser.parseInclude(context, tokens("include", "element")); + + assertEquals(2, view.getElements().size()); + assertNotNull(view.getElementView(dn)); + assertNotNull(view.getElementView(softwareSystemInstance)); + } + + @Test + void test_parseInclude_AddsTheElement_WhenTheElementIsAContainerInstance() { + DeploymentNode dn = model.addDeploymentNode("Live", "DN", "Description", "Technology"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + ContainerInstance containerInstance = dn.add(container); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("element", containerInstance); + + DeploymentView view = views.createDeploymentView("key", "Description"); + view.setEnvironment("Live"); + DeploymentViewDslContext context = new DeploymentViewDslContext(view); + context.setWorkspace(workspace); + context.setIdentifierRegister(elements); + + parser.parseInclude(context, tokens("include", "element")); + + assertEquals(2, view.getElements().size()); + assertNotNull(view.getElementView(dn)); + assertNotNull(view.getElementView(containerInstance)); + } + + @Test + void test_parseExclude_ThrowsAnException_WhenTheNoElementsAreSpecified() { + try { + parser.parseExclude(new DeploymentViewDslContext(null), tokens("exclude")); + fail(); + } catch (RuntimeException iae) { + assertEquals("Expected: exclude <identifier> [identifier...]", iae.getMessage()); + } + } + + @Test + void test_parseExclude_ThrowsAnException_WhenTheSpecifiedElementDoesNotExist() { + try { + parser.parseExclude(new DeploymentViewDslContext(null), tokens("exclude", "user")); + fail(); + } catch (RuntimeException iae) { + assertEquals("The element/relationship \"user\" does not exist", iae.getMessage()); + } + } + + @Test + void test_parseExclude_RemovesTheSpecifiedElement() { + DeploymentNode dn = model.addDeploymentNode("Live", "DN", "Description", "Technology"); + InfrastructureNode in = dn.addInfrastructureNode("IN", "Description", "Technology"); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("element", in); + + DeploymentView view = views.createDeploymentView("key", "Description"); + view.setEnvironment("Live"); + view.addAllDeploymentNodes(); + DeploymentViewDslContext context = new DeploymentViewDslContext(view); + context.setWorkspace(workspace); + context.setIdentifierRegister(elements); + + assertEquals(2, view.getElements().size()); + assertTrue(view.getElements().stream().anyMatch(ev -> ev.getElement().equals(dn))); + assertTrue(view.getElements().stream().anyMatch(ev -> ev.getElement().equals(in))); + + parser.parseExclude(context, tokens("exclude", "element")); + + assertEquals(1, view.getElements().size()); + assertTrue(view.getElements().stream().anyMatch(ev -> ev.getElement().equals(dn))); + } + + @Test + void test_parseExclude_ExcludesReplicatedVersionsOfTheSpecifiedRelationship() { + SoftwareSystem ss1 = model.addSoftwareSystem("SS1", "Description"); + SoftwareSystem ss2 = model.addSoftwareSystem("SS2", "Description"); + Relationship rel = ss1.uses(ss2, "Uses"); + + DeploymentNode dn = model.addDeploymentNode("Live", "Live", "Description", "Technology"); + dn.add(ss1); + dn.add(ss2); + + DeploymentView view = views.createDeploymentView("key", "Description"); + view.setEnvironment("Live"); + DeploymentViewDslContext context = new DeploymentViewDslContext(view); + context.setWorkspace(workspace); + + IdentifiersRegister identifersRegister = new IdentifiersRegister(); + identifersRegister.register("rel", rel); + context.setIdentifierRegister(identifersRegister); + + view.addDefaultElements(); + assertEquals(1, view.getRelationships().stream().map(RelationshipView::getRelationship).filter(r -> r.getLinkedRelationshipId().equals(rel.getId())).count()); + + parser.parseExclude(context, tokens("exclude", "rel")); + assertEquals(0, view.getRelationships().size()); + } + + @Test + void test_parseExclude_ThrowsAnException_WhenTheRelationshipSourceElementDoesNotExistInTheModel() { + try { + SoftwareSystem ss1 = model.addSoftwareSystem("SS1", "Description"); + SoftwareSystem ss2 = model.addSoftwareSystem("SS2", "Description"); + Relationship rel = ss1.uses(ss2, "Uses"); + + DeploymentNode dn = model.addDeploymentNode("Live", "Live", "Description", "Technology"); + dn.add(ss1); + dn.add(ss2); + + DeploymentView view = views.createDeploymentView("key", "Description"); + view.setEnvironment("Live"); + DeploymentViewDslContext context = new DeploymentViewDslContext(view); + context.setWorkspace(workspace); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("ss2", ss2); + context.setIdentifierRegister(elements); + + parser.parseExclude(context, tokens("exclude", "relationship.source==ss1 && relationship.destination==ss2")); + + fail(); + } catch (RuntimeException re) { + assertEquals("The element \"ss1\" does not exist", re.getMessage()); + } + } + + @Test + void test_parseExclude_ThrowsAnException_WhenTheRelationshipDestinationElementDoesNotExistInTheModel() { + try { + SoftwareSystem ss1 = model.addSoftwareSystem("SS1", "Description"); + SoftwareSystem ss2 = model.addSoftwareSystem("SS2", "Description"); + Relationship rel = ss1.uses(ss2, "Uses"); + + DeploymentNode dn = model.addDeploymentNode("Live", "Live", "Description", "Technology"); + dn.add(ss1); + + DeploymentView view = views.createDeploymentView("key", "Description"); + view.setEnvironment("Live"); + view.add(dn); + DeploymentViewDslContext context = new DeploymentViewDslContext(view); + context.setWorkspace(workspace); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("ss1", ss1); + context.setIdentifierRegister(elements); + + parser.parseExclude(context, tokens("exclude", "relationship.source==ss1 && relationship.destination==ss2")); + + fail(); + } catch (RuntimeException re) { + assertEquals("The element \"ss2\" does not exist", re.getMessage()); + } + } + + @Test + void test_parseExclude_RemovesTheRelationshipFromAView_WhenAnExpressionIsSpecifiedWithSourceAndDestination() { + SoftwareSystem ss1 = model.addSoftwareSystem("SS1", "Description"); + SoftwareSystem ss2 = model.addSoftwareSystem("SS2", "Description"); + Relationship rel = ss1.uses(ss2, "Uses"); + + DeploymentNode dn = model.addDeploymentNode("Live", "Live", "Description", "Technology"); + dn.add(ss1); + dn.add(ss2); + + DeploymentView view = views.createDeploymentView("key", "Description"); + view.setEnvironment("Live"); + view.add(dn); + DeploymentViewDslContext context = new DeploymentViewDslContext(view); + context.setWorkspace(workspace); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("ss1", ss1); + elements.register("ss2", ss2); + context.setIdentifierRegister(elements); + + parser.parseExclude(context, tokens("exclude", "relationship.source==ss1 && relationship.destination==ss2")); + assertEquals(0, view.getRelationships().size()); + } + + @Test + void test_parseExclude_RemovesTheRelationshipFromAView_WhenAnExpressionIsSpecifiedWithSource() { + SoftwareSystem ss1 = model.addSoftwareSystem("SS1", "Description"); + SoftwareSystem ss2 = model.addSoftwareSystem("SS2", "Description"); + Relationship rel = ss1.uses(ss2, "Uses"); + + DeploymentNode dn = model.addDeploymentNode("Live", "Live", "Description", "Technology"); + dn.add(ss1); + dn.add(ss2); + + DeploymentView view = views.createDeploymentView("key", "Description"); + view.setEnvironment("Live"); + view.add(dn); + DeploymentViewDslContext context = new DeploymentViewDslContext(view); + context.setWorkspace(workspace); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("ss1", ss1); + elements.register("ss2", ss2); + context.setIdentifierRegister(elements); + + parser.parseExclude(context, tokens("exclude", "relationship.source==ss1")); + assertEquals(0, view.getRelationships().size()); + } + + @Test + void test_parseExclude_RemovesTheRelationshipFromAView_WhenAnExpressionIsSpecifiedWithADestination() { + SoftwareSystem ss1 = model.addSoftwareSystem("SS1", "Description"); + SoftwareSystem ss2 = model.addSoftwareSystem("SS2", "Description"); + Relationship rel = ss1.uses(ss2, "Uses"); + + DeploymentNode dn = model.addDeploymentNode("Live", "Live", "Description", "Technology"); + dn.add(ss1); + dn.add(ss2); + + DeploymentView view = views.createDeploymentView("key", "Description"); + view.setEnvironment("Live"); + view.add(dn); + DeploymentViewDslContext context = new DeploymentViewDslContext(view); + context.setWorkspace(workspace); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("ss1", ss1); + elements.register("ss2", ss2); + context.setIdentifierRegister(elements); + + parser.parseExclude(context, tokens("exclude", "relationship.destination==ss2")); + assertEquals(0, view.getRelationships().size()); + } + + @Test + void test_parseExclude_RemovesAllRelationshipsFromAView_WhenAnExpressionIsSpecifiedWithAWildcard() { + SoftwareSystem ss1 = model.addSoftwareSystem("SS1", "Description"); + SoftwareSystem ss2 = model.addSoftwareSystem("SS2", "Description"); + Relationship rel = ss1.uses(ss2, "Uses"); + + DeploymentNode dn = model.addDeploymentNode("Live", "Live", "Description", "Technology"); + dn.add(ss1); + dn.add(ss2); + + DeploymentView view = views.createDeploymentView("key", "Description"); + view.setEnvironment("Live"); + view.add(dn); + DeploymentViewDslContext context = new DeploymentViewDslContext(view); + context.setWorkspace(workspace); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("ss1", ss1); + elements.register("ss2", ss2); + context.setIdentifierRegister(elements); + + parser.parseExclude(context, tokens("exclude", "relationship==*")); + assertEquals(0, view.getRelationships().size()); + } + + @Test + void test_parseInclude_AddsAllDeploymentNodesWithTheSpecifiedTag() { + SoftwareSystem ss1 = model.addSoftwareSystem("SS1", "Description"); + SoftwareSystem ss2 = model.addSoftwareSystem("SS2", "Description"); + ss1.uses(ss2, "Uses"); + + DeploymentNode dn = model.addDeploymentNode("Live", "Live", "Description", "Technology"); + DeploymentNode dn1 = dn.addDeploymentNode("DN 1"); + dn1.addTags("Tag 1"); + SoftwareSystemInstance ss1Instance = dn1.add(ss1); + DeploymentNode dn2 = dn.addDeploymentNode("DN 2"); + SoftwareSystemInstance ss2Instance = dn2.add(ss2); + + DeploymentView view = views.createDeploymentView("key", "Description"); + view.setEnvironment("Live"); + DeploymentViewDslContext context = new DeploymentViewDslContext(view); + context.setWorkspace(workspace); + + parser.parseInclude(context, tokens("include", "element.tag==Tag 1")); + + assertEquals(3, view.getElements().size()); + assertNotNull(view.getElementView(dn)); + assertNotNull(view.getElementView(dn1)); + assertNotNull(view.getElementView(ss1Instance)); + } + + @Test + void test_parseInclude_AddsAllInfrastructureNodesWithTheSpecifiedTag() { + DeploymentNode dn = model.addDeploymentNode("Live", "Live", "Description", "Technology"); + InfrastructureNode in1 = dn.addInfrastructureNode("Infrastructure Node 1"); + in1.addTags("Tag 1"); + InfrastructureNode in2 = dn.addInfrastructureNode("Infrastructure Node 2"); + in2.addTags("Tag 2"); + + DeploymentView view = views.createDeploymentView("key", "Description"); + view.setEnvironment("Live"); + DeploymentViewDslContext context = new DeploymentViewDslContext(view); + context.setWorkspace(workspace); + + parser.parseInclude(context, tokens("include", "element.tag==Tag 1")); + + assertEquals(2, view.getElements().size()); + assertNotNull(view.getElementView(dn)); + assertNotNull(view.getElementView(in1)); + } + + @Test + void test_parseInclude_AddsAllInstancesOfSoftwareSystemsWithTheSpecifiedTag() { + SoftwareSystem ss1 = model.addSoftwareSystem("SS1", "Description"); + ss1.addTags("Tag 1"); + SoftwareSystem ss2 = model.addSoftwareSystem("SS2", "Description"); + ss2.addTags("Tag 2"); + ss1.uses(ss2, "Uses"); + + DeploymentNode dn = model.addDeploymentNode("Live", "Live", "Description", "Technology"); + SoftwareSystemInstance ss1Instance = dn.add(ss1); + SoftwareSystemInstance ss2Instance = dn.add(ss2); + + DeploymentView view = views.createDeploymentView("key", "Description"); + view.setEnvironment("Live"); + DeploymentViewDslContext context = new DeploymentViewDslContext(view); + context.setWorkspace(workspace); + + parser.parseInclude(context, tokens("include", "element.tag==Tag 1")); + + assertEquals(2, view.getElements().size()); + assertNotNull(view.getElementView(dn)); + assertNotNull(view.getElementView(ss1Instance)); + } + + @Test + void test_parseInclude_AddsAllSoftwareSystemInstancesWithTheSpecifiedTag() { + SoftwareSystem ss1 = model.addSoftwareSystem("SS1", "Description"); + SoftwareSystem ss2 = model.addSoftwareSystem("SS2", "Description"); + ss1.uses(ss2, "Uses"); + + DeploymentNode dn = model.addDeploymentNode("Live", "Live", "Description", "Technology"); + SoftwareSystemInstance ss1Instance = dn.add(ss1); + ss1Instance.addTags("Tag 1"); + SoftwareSystemInstance ss2Instance = dn.add(ss2); + ss2Instance.addTags("Tag 2"); + + DeploymentView view = views.createDeploymentView("key", "Description"); + view.setEnvironment("Live"); + DeploymentViewDslContext context = new DeploymentViewDslContext(view); + context.setWorkspace(workspace); + + parser.parseInclude(context, tokens("include", "element.tag==Tag 1")); + + assertEquals(2, view.getElements().size()); + assertNotNull(view.getElementView(dn)); + assertNotNull(view.getElementView(ss1Instance)); + } + + @Test + void test_parseInclude_AddsAllInstancesOfContainersWithTheSpecifiedTag() { + SoftwareSystem ss = model.addSoftwareSystem("SS", "Description"); + Container c1 = ss.addContainer("Container 1"); + c1.addTags("Tag 1"); + Container c2 = ss.addContainer("Container 2"); + c2.addTags("Tag 2"); + + DeploymentNode dn = model.addDeploymentNode("Live", "Live", "Description", "Technology"); + ContainerInstance c1Instance = dn.add(c1); + ContainerInstance c2Instance = dn.add(c2); + + DeploymentView view = views.createDeploymentView("key", "Description"); + view.setEnvironment("Live"); + DeploymentViewDslContext context = new DeploymentViewDslContext(view); + context.setWorkspace(workspace); + + parser.parseInclude(context, tokens("include", "element.tag==Tag 1")); + + assertEquals(2, view.getElements().size()); + assertNotNull(view.getElementView(dn)); + assertNotNull(view.getElementView(c1Instance)); + } + + @Test + void test_parseInclude_AddsAllContainerInstancesWithTheSpecifiedTag() { + SoftwareSystem ss = model.addSoftwareSystem("SS", "Description"); + Container c1 = ss.addContainer("Container 1"); + Container c2 = ss.addContainer("Container 2"); + + DeploymentNode dn = model.addDeploymentNode("Live", "Live", "Description", "Technology"); + ContainerInstance c1Instance = dn.add(c1); + c1Instance.addTags("Tag 1"); + ContainerInstance c2Instance = dn.add(c2); + c2Instance.addTags("Tag 2"); + + DeploymentView view = views.createDeploymentView("key", "Description"); + view.setEnvironment("Live"); + DeploymentViewDslContext context = new DeploymentViewDslContext(view); + context.setWorkspace(workspace); + + parser.parseInclude(context, tokens("include", "element.tag==Tag 1")); + + assertEquals(2, view.getElements().size()); + assertNotNull(view.getElementView(dn)); + assertNotNull(view.getElementView(c1Instance)); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentViewExpressionParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentViewExpressionParserTests.java new file mode 100644 index 000000000..bf4a417a0 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentViewExpressionParserTests.java @@ -0,0 +1,220 @@ +package com.structurizr.dsl; + +import com.structurizr.model.*; +import org.junit.jupiter.api.Test; + +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.*; + +class DeploymentViewExpressionParserTests extends AbstractTests { + + private DeploymentViewExpressionParser parser = new DeploymentViewExpressionParser(); + + @Test + void test_parseExpression_ThrowsAnException_WhenElementTypeIsNotSupported() { + try { + parser.parseExpression("element.type==Component", null); + fail(); + } catch (RuntimeException iae) { + assertEquals("The element type of \"Component\" is not valid for this view", iae.getMessage()); + } + } + + @Test + void test_parseExpression_ReturnsElements_WhenElementTypeEqualsDeploymentNode() { + Person user = model.addPerson("User"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + Component component = container.addComponent("Component"); + + DeploymentNode deploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + InfrastructureNode infrastructureNode = deploymentNode.addInfrastructureNode("Infrastructure Node"); + SoftwareSystemInstance softwareSystemInstance = deploymentNode.add(softwareSystem); + ContainerInstance containerInstance = deploymentNode.add(container); + + DeploymentViewDslContext context = new DeploymentViewDslContext(null); + context.setWorkspace(workspace); + + Set<ModelItem> elements = parser.parseExpression("element.type==DeploymentNode", context); + assertEquals(1, elements.size()); + assertTrue(elements.contains(deploymentNode)); + } + + @Test + void test_parseExpression_ReturnsElements_WhenElementTypeEqualsInfrastructureNode() { + Person user = model.addPerson("User"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + Component component = container.addComponent("Component"); + + DeploymentNode deploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + InfrastructureNode infrastructureNode = deploymentNode.addInfrastructureNode("Infrastructure Node"); + SoftwareSystemInstance softwareSystemInstance = deploymentNode.add(softwareSystem); + ContainerInstance containerInstance = deploymentNode.add(container); + + DeploymentViewDslContext context = new DeploymentViewDslContext(null); + context.setWorkspace(workspace); + + Set<ModelItem> elements = parser.parseExpression("element.type==InfrastructureNode", context); + assertEquals(1, elements.size()); + assertTrue(elements.contains(infrastructureNode)); + } + + @Test + void test_parseExpression_ReturnsElements_WhenElementTypeEqualsSoftwareSystem() { + Person user = model.addPerson("User"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + Component component = container.addComponent("Component"); + + DeploymentNode deploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + InfrastructureNode infrastructureNode = deploymentNode.addInfrastructureNode("Infrastructure Node"); + SoftwareSystemInstance softwareSystemInstance = deploymentNode.add(softwareSystem); + ContainerInstance containerInstance = deploymentNode.add(container); + + DeploymentViewDslContext context = new DeploymentViewDslContext(null); + context.setWorkspace(workspace); + + Set<ModelItem> elements = parser.parseExpression("element.type==SoftwareSystem", context); + assertEquals(1, elements.size()); + assertTrue(elements.contains(softwareSystem)); + } + + @Test + void test_parseExpression_ReturnsElements_WhenElementTypeEqualsSoftwareSystemInstance() { + Person user = model.addPerson("User"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + Component component = container.addComponent("Component"); + + DeploymentNode deploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + InfrastructureNode infrastructureNode = deploymentNode.addInfrastructureNode("Infrastructure Node"); + SoftwareSystemInstance softwareSystemInstance = deploymentNode.add(softwareSystem); + ContainerInstance containerInstance = deploymentNode.add(container); + + DeploymentViewDslContext context = new DeploymentViewDslContext(null); + context.setWorkspace(workspace); + + Set<ModelItem> elements = parser.parseExpression("element.type==SoftwareSystemInstance", context); + assertEquals(1, elements.size()); + assertTrue(elements.contains(softwareSystemInstance)); + } + + @Test + void test_parseExpression_ReturnsElements_WhenElementTypeEqualsContainer() { + Person user = model.addPerson("User"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + Component component = container.addComponent("Component"); + + DeploymentNode deploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + InfrastructureNode infrastructureNode = deploymentNode.addInfrastructureNode("Infrastructure Node"); + SoftwareSystemInstance softwareSystemInstance = deploymentNode.add(softwareSystem); + ContainerInstance containerInstance = deploymentNode.add(container); + + DeploymentViewDslContext context = new DeploymentViewDslContext(null); + context.setWorkspace(workspace); + + Set<ModelItem> elements = parser.parseExpression("element.type==Container", context); + assertEquals(1, elements.size()); + assertTrue(elements.contains(container)); + } + + @Test + void test_parseExpression_ReturnsElements_WhenElementTypeEqualsContainerInstance() { + Person user = model.addPerson("User"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + Component component = container.addComponent("Component"); + + DeploymentNode deploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + InfrastructureNode infrastructureNode = deploymentNode.addInfrastructureNode("Infrastructure Node"); + SoftwareSystemInstance softwareSystemInstance = deploymentNode.add(softwareSystem); + ContainerInstance containerInstance = deploymentNode.add(container); + + DeploymentViewDslContext context = new DeploymentViewDslContext(null); + context.setWorkspace(workspace); + + Set<ModelItem> elements = parser.parseExpression("element.type==ContainerInstance", context); + assertEquals(1, elements.size()); + assertTrue(elements.contains(containerInstance)); + } + + @Test + void test_parseExpression_ReturnsElements_WhenElementHasTag() { + Person user = model.addPerson("User"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + Component component = container.addComponent("Component"); + + DeploymentNode deploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + InfrastructureNode infrastructureNode = deploymentNode.addInfrastructureNode("Infrastructure Node"); + SoftwareSystemInstance softwareSystemInstance = deploymentNode.add(softwareSystem); + ContainerInstance containerInstance = deploymentNode.add(container); + + DeploymentViewDslContext context = new DeploymentViewDslContext(null); + context.setWorkspace(workspace); + + Set<ModelItem> elements = parser.parseExpression("element.tag==Infrastructure Node", context); + assertEquals(1, elements.size()); + assertTrue(elements.contains(infrastructureNode)); + } + + @Test + void test_parseExpression_ReturnsElements_WhenElementDoesNotHaveTag() { + Person user = model.addPerson("User"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + Component component = container.addComponent("Component"); + + DeploymentNode deploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + InfrastructureNode infrastructureNode = deploymentNode.addInfrastructureNode("Infrastructure Node"); + SoftwareSystemInstance softwareSystemInstance = deploymentNode.add(softwareSystem); + ContainerInstance containerInstance = deploymentNode.add(container); + + DeploymentViewDslContext context = new DeploymentViewDslContext(null); + context.setWorkspace(workspace); + + Set<ModelItem> elements = parser.parseExpression("element.tag!=Infrastructure Node", context); + assertEquals(7, elements.size()); + assertFalse(elements.contains(infrastructureNode)); + } + + @Test + void test_parseExpression_ReturnsElements_WhenBooleanAndUsed() { + Person user = model.addPerson("User"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + Component component = container.addComponent("Component"); + + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(null); + context.setWorkspace(workspace); + + Set<ModelItem> elements = parser.parseExpression("element.tag==Element && element.type==Container", context); + assertEquals(1, elements.size()); + assertTrue(elements.contains(container)); + } + + @Test + void test_parseExpression_ReturnsElements_WhenBooleanOrUsed() { + Person user = model.addPerson("User"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + Component component = container.addComponent("Component"); + + DeploymentNode deploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + InfrastructureNode infrastructureNode = deploymentNode.addInfrastructureNode("Infrastructure Node"); + SoftwareSystemInstance softwareSystemInstance = deploymentNode.add(softwareSystem); + ContainerInstance containerInstance = deploymentNode.add(container); + + DeploymentViewDslContext context = new DeploymentViewDslContext(null); + context.setWorkspace(workspace); + + Set<ModelItem> elements = parser.parseExpression("element.tag==Software System Instance || element.type==ContainerInstance", context); + assertEquals(2, elements.size()); + assertTrue(elements.contains(softwareSystemInstance)); + assertTrue(elements.contains(containerInstance)); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentViewParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentViewParserTests.java new file mode 100644 index 000000000..4d3e1ccf8 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentViewParserTests.java @@ -0,0 +1,209 @@ +package com.structurizr.dsl; + +import com.structurizr.model.SoftwareSystem; +import com.structurizr.view.DeploymentView; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class DeploymentViewParserTests extends AbstractTests { + + private DeploymentViewParser parser = new DeploymentViewParser(); + + @Test + void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { + DslContext context = context(); + try { + parser.parse(context, tokens("deployment", "identifier", "environment", "key", "description", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: deployment <*|software system identifier> <environment> [key] [description] {", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheElementIdentifierIsMissing() { + DslContext context = context(); + try { + parser.parse(context, tokens("deployment")); + fail(); + } catch (Exception e) { + assertEquals("Expected: deployment <*|software system identifier> <environment> [key] [description] {", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheDeploymentEnvironmentIsMissing() { + DslContext context = context(); + try { + parser.parse(context, tokens("deployment", "*")); + fail(); + } catch (Exception e) { + assertEquals("Expected: deployment <*|software system identifier> <environment> [key] [description] {", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheEnvironmentDoesNotExist() { + DslContext context = context(); + + try { + parser.parse(context, tokens("deployment", "softwareSystem", "Live", "key")); + fail(); + } catch (Exception e) { + assertEquals("The environment \"Live\" does not exist", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheElementIsNotDefined() { + DslContext context = context(); + context.getWorkspace().getModel().addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + + try { + parser.parse(context, tokens("deployment", "softwareSystem", "Live", "key")); + fail(); + } catch (Exception e) { + assertEquals("The software system \"softwareSystem\" does not exist", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheElementIsNotASoftwareSystem() { + DslContext context = context(); + context.getWorkspace().getModel().addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("softwaresystem", model.addPerson("Name", "Description")); + context.setIdentifierRegister(elements); + + try { + parser.parse(context, tokens("deployment", "softwareSystem", "Live", "key")); + fail(); + } catch (Exception e) { + assertEquals("The element \"softwareSystem\" is not a software system", e.getMessage()); + } + } + + @Test + void test_parse_CreatesADeploymentViewWithNoScope() { + context().getWorkspace().getModel().addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + + parser.parse(context(), tokens("deployment", "*", "Live")); + List<DeploymentView> views = new ArrayList<>(this.views.getDeploymentViews()); + + assertEquals(1, views.size()); + assertEquals("Deployment-001", views.get(0).getKey()); + assertEquals("", views.get(0).getDescription()); + assertNull(views.get(0).getSoftwareSystem()); + } + + @Test + void test_parse_CreatesADeploymentViewWithNoScopeAndKey_ViaEnvironmentName() { + DslContext context = context(); + context.getWorkspace().getModel().addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + + parser.parse(context, tokens("deployment", "*", "Live", "key")); + List<DeploymentView> views = new ArrayList<>(this.views.getDeploymentViews()); + + assertEquals(1, views.size()); + assertEquals("key", views.get(0).getKey()); + assertEquals("", views.get(0).getDescription()); + assertNull(views.get(0).getSoftwareSystem()); + assertEquals("Live", views.get(0).getEnvironment()); + } + + @Test + void test_parse_CreatesADeploymentViewWithNoScopeAndKey_ViaEnvironmentReference() { + DslContext context = context(); + context.getWorkspace().getModel().addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("env", new DeploymentEnvironment("Live")); + context.setIdentifierRegister(elements); + + parser.parse(context, tokens("deployment", "*", "env", "key")); + List<DeploymentView> views = new ArrayList<>(this.views.getDeploymentViews()); + + assertEquals(1, views.size()); + assertEquals("key", views.get(0).getKey()); + assertEquals("", views.get(0).getDescription()); + assertNull(views.get(0).getSoftwareSystem()); + assertEquals("Live", views.get(0).getEnvironment()); + } + + @Test + void test_parse_CreatesADeploymentViewWithNoScopeAndKeyAndDescription() { + DslContext context = context(); + context.getWorkspace().getModel().addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + + parser.parse(context, tokens("deployment", "*", "Live", "key", "Description")); + List<DeploymentView> views = new ArrayList<>(this.views.getDeploymentViews()); + + assertEquals(1, views.size()); + assertEquals("key", views.get(0).getKey()); + assertEquals("Description", views.get(0).getDescription()); + assertNull(views.get(0).getSoftwareSystem()); + } + + @Test + void test_parse_CreatesADeploymentViewWithSoftwareSystemScope() { + DslContext context = context(); + context.getWorkspace().getModel().addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + + IdentifiersRegister elements = new IdentifiersRegister(); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); + elements.register("softwaresystem", softwareSystem); + context.setIdentifierRegister(elements); + + parser.parse(context, tokens("deployment", "softwareSystem", "Live")); + List<DeploymentView> views = new ArrayList<>(this.views.getDeploymentViews()); + + assertEquals(1, views.size()); + assertEquals("Deployment-001", views.get(0).getKey()); + assertEquals("", views.get(0).getDescription()); + assertSame(softwareSystem, views.get(0).getSoftwareSystem()); + } + + @Test + void test_parse_CreatesADeploymentViewWithSoftwareSystemScopeAndKey() { + DslContext context = context(); + context.getWorkspace().getModel().addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + + IdentifiersRegister elements = new IdentifiersRegister(); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); + elements.register("softwaresystem", softwareSystem); + context.setIdentifierRegister(elements); + + parser.parse(context, tokens("deployment", "softwareSystem", "Live", "key")); + List<DeploymentView> views = new ArrayList<>(this.views.getDeploymentViews()); + + assertEquals(1, views.size()); + assertEquals("key", views.get(0).getKey()); + assertEquals("", views.get(0).getDescription()); + assertSame(softwareSystem, views.get(0).getSoftwareSystem()); + } + + @Test + void test_parse_CreatesADeploymentViewWithSoftwareSystemScopeAndKeyAndDescription() { + DslContext context = context(); + context.getWorkspace().getModel().addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + + IdentifiersRegister elements = new IdentifiersRegister(); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); + elements.register("softwaresystem", softwareSystem); + context.setIdentifierRegister(elements); + + parser.parse(context, tokens("deployment", "softwareSystem", "Live", "key", "Description")); + List<DeploymentView> views = new ArrayList<>(this.views.getDeploymentViews()); + + assertEquals(1, views.size()); + assertEquals("key", views.get(0).getKey()); + assertEquals("Description", views.get(0).getDescription()); + assertSame(softwareSystem, views.get(0).getSoftwareSystem()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DocsParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DocsParserTests.java new file mode 100644 index 000000000..50660421e --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DocsParserTests.java @@ -0,0 +1,22 @@ +package com.structurizr.dsl; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +class DocsParserTests extends AbstractTests { + + private DocsParser parser = new DocsParser(); + + @Test + void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parse(new WorkspaceDslContext(), null, tokens("docs", "path", "fqn", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: !docs <path> <fqn>", e.getMessage()); + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java new file mode 100644 index 000000000..b8f2002b8 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -0,0 +1,1049 @@ +package com.structurizr.dsl; + +import com.structurizr.Workspace; +import com.structurizr.model.*; +import com.structurizr.view.*; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Base64; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class DslTests extends AbstractTests { + + @Test + void test_test() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/test.dsl")); + + assertFalse(parser.getWorkspace().isEmpty()); + assertEquals("Organisation - Group", parser.getWorkspace().getModel().getEnterprise().getName()); + } + + @Test + void test_utf8() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/utf8.dsl")); + + Workspace workspace = parser.getWorkspace(); + Model model = workspace.getModel(); + + assertEquals(1, model.getPeople().size()); + Person user = model.getPersonWithName("你好 Usér \uD83D\uDE42"); + assertNotNull(user); + } + + @Test + void test_gettingstarted() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/getting-started.dsl")); + + Workspace workspace = parser.getWorkspace(); + Model model = workspace.getModel(); + ViewSet views = workspace.getViews(); + + assertEquals(1, model.getPeople().size()); + Person user = model.getPersonWithName("User"); + + assertEquals(1, workspace.getModel().getSoftwareSystems().size()); + SoftwareSystem softwareSystem = model.getSoftwareSystemWithName("Software System"); + + assertEquals(1, workspace.getModel().getRelationships().size()); + Relationship relationship = user.getRelationships().iterator().next(); + assertEquals("Uses", relationship.getDescription()); + assertSame(softwareSystem, relationship.getDestination()); + + assertEquals(1, views.getViews().size()); + assertEquals(1, views.getSystemContextViews().size()); + SystemContextView view = views.getSystemContextViews().iterator().next(); + assertEquals("SystemContext-001", view.getKey()); + assertEquals(2, view.getElements().size()); + assertEquals(1, view.getRelationships().size()); + } + + @Test + void test_aws() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/amazon-web-services.dsl")); + + Workspace workspace = parser.getWorkspace(); + + assertEquals(13, workspace.getModel().getElements().size()); + assertEquals(0, workspace.getModel().getPeople().size()); + assertEquals(1, workspace.getModel().getSoftwareSystems().size()); + assertEquals(2, workspace.getModel().getSoftwareSystemWithName("Spring PetClinic").getContainers().size()); + assertEquals(1, workspace.getModel().getDeploymentNodes().size()); + assertEquals(6, workspace.getModel().getElements().stream().filter(e -> e instanceof DeploymentNode).count()); + assertEquals(2, workspace.getModel().getElements().stream().filter(e -> e instanceof ContainerInstance).count()); + assertEquals(2, workspace.getModel().getElements().stream().filter(e -> e instanceof InfrastructureNode).count()); + + assertEquals(4, workspace.getModel().getRelationships().size()); + + assertEquals(0, workspace.getViews().getSystemLandscapeViews().size()); + assertEquals(0, workspace.getViews().getSystemContextViews().size()); + assertEquals(0, workspace.getViews().getContainerViews().size()); + assertEquals(0, workspace.getViews().getComponentViews().size()); + assertEquals(0, workspace.getViews().getDynamicViews().size()); + assertEquals(1, workspace.getViews().getDeploymentViews().size()); + + DeploymentView deploymentView = workspace.getViews().getDeploymentViews().iterator().next(); + assertEquals(10, deploymentView.getElements().size()); + assertEquals(3, deploymentView.getRelationships().size()); + assertEquals(4, deploymentView.getAnimations().size()); + + assertEquals(3, workspace.getViews().getConfiguration().getStyles().getElements().size()); + assertEquals(0, workspace.getViews().getConfiguration().getStyles().getRelationships().size()); + + assertEquals(1, workspace.getViews().getConfiguration().getThemes().length); + } + + @Test + void test_awsLocal() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/amazon-web-services-local.dsl")); + + Workspace workspace = parser.getWorkspace(); + + assertEquals(13, workspace.getModel().getElements().size()); + assertEquals(0, workspace.getModel().getPeople().size()); + assertEquals(1, workspace.getModel().getSoftwareSystems().size()); + assertEquals(2, workspace.getModel().getSoftwareSystemWithName("Spring PetClinic").getContainers().size()); + assertEquals(1, workspace.getModel().getDeploymentNodes().size()); + assertEquals(6, workspace.getModel().getElements().stream().filter(e -> e instanceof DeploymentNode).count()); + assertEquals(2, workspace.getModel().getElements().stream().filter(e -> e instanceof ContainerInstance).count()); + assertEquals(2, workspace.getModel().getElements().stream().filter(e -> e instanceof InfrastructureNode).count()); + + assertEquals(4, workspace.getModel().getRelationships().size()); + + assertEquals(0, workspace.getViews().getSystemLandscapeViews().size()); + assertEquals(0, workspace.getViews().getSystemContextViews().size()); + assertEquals(0, workspace.getViews().getContainerViews().size()); + assertEquals(0, workspace.getViews().getComponentViews().size()); + assertEquals(0, workspace.getViews().getDynamicViews().size()); + assertEquals(1, workspace.getViews().getDeploymentViews().size()); + + DeploymentView deploymentView = workspace.getViews().getDeploymentViews().iterator().next(); + assertEquals(10, deploymentView.getElements().size()); + assertEquals(3, deploymentView.getRelationships().size()); + assertEquals(4, deploymentView.getAnimations().size()); + + assertEquals(3, workspace.getViews().getConfiguration().getStyles().getElements().size()); + assertEquals(0, workspace.getViews().getConfiguration().getStyles().getRelationships().size()); + + assertEquals(1, workspace.getViews().getConfiguration().getThemes().length); + } + + @Test + void test_bigbankplc() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/big-bank-plc.dsl")); + + Workspace workspace = parser.getWorkspace(); + + assertEquals(Location.External, workspace.getModel().getPersonWithName("Personal Banking Customer").getLocation()); + assertEquals(Location.Internal, workspace.getModel().getPersonWithName("Customer Service Staff").getLocation()); + assertEquals(Location.Internal, workspace.getModel().getPersonWithName("Back Office Staff").getLocation()); + + assertEquals(51, workspace.getModel().getElements().size()); + assertEquals(3, workspace.getModel().getPeople().size()); + assertEquals(4, workspace.getModel().getSoftwareSystems().size()); + assertEquals(5, workspace.getModel().getSoftwareSystemWithName("Internet Banking System").getContainers().size()); + assertEquals(6, workspace.getModel().getSoftwareSystemWithName("Internet Banking System").getContainerWithName("API Application").getComponents().size()); + assertEquals(5, workspace.getModel().getDeploymentNodes().size()); + assertEquals(21, workspace.getModel().getElements().stream().filter(e -> e instanceof DeploymentNode).count()); + assertEquals(2, workspace.getModel().getElements().stream().filter(e -> e instanceof SoftwareSystemInstance).count()); + assertEquals(10, workspace.getModel().getElements().stream().filter(e -> e instanceof ContainerInstance).count()); + assertEquals(0, workspace.getModel().getElements().stream().filter(e -> e instanceof InfrastructureNode).count()); + + assertEquals(42, workspace.getModel().getRelationships().size()); + + assertEquals(1, workspace.getViews().getSystemLandscapeViews().size()); + assertEquals(1, workspace.getViews().getSystemContextViews().size()); + assertEquals(1, workspace.getViews().getContainerViews().size()); + assertEquals(1, workspace.getViews().getComponentViews().size()); + assertEquals(1, workspace.getViews().getDynamicViews().size()); + assertEquals(2, workspace.getViews().getDeploymentViews().size()); + + assertEquals(7, workspace.getViews().getSystemLandscapeViews().iterator().next().getElements().size()); + assertEquals(9, workspace.getViews().getSystemLandscapeViews().iterator().next().getRelationships().size()); + + assertEquals(4, workspace.getViews().getSystemContextViews().iterator().next().getElements().size()); + assertEquals(4, workspace.getViews().getSystemContextViews().iterator().next().getRelationships().size()); + + assertEquals(8, workspace.getViews().getContainerViews().iterator().next().getElements().size()); + assertEquals(10, workspace.getViews().getContainerViews().iterator().next().getRelationships().size()); + + assertEquals(11, workspace.getViews().getComponentViews().iterator().next().getElements().size()); + assertEquals(13, workspace.getViews().getComponentViews().iterator().next().getRelationships().size()); + + assertEquals(4, workspace.getViews().getDynamicViews().iterator().next().getElements().size()); + assertEquals(6, workspace.getViews().getDynamicViews().iterator().next().getRelationships().size()); + + assertEquals(13, workspace.getViews().getDeploymentViews().stream().filter(v -> v.getKey().equals("DevelopmentDeployment")).findFirst().get().getElements().size()); + assertEquals(4, workspace.getViews().getDeploymentViews().stream().filter(v -> v.getKey().equals("DevelopmentDeployment")).findFirst().get().getRelationships().size()); + + assertEquals(20, workspace.getViews().getDeploymentViews().stream().filter(v -> v.getKey().equals("LiveDeployment")).findFirst().get().getElements().size()); + assertEquals(7, workspace.getViews().getDeploymentViews().stream().filter(v -> v.getKey().equals("LiveDeployment")).findFirst().get().getRelationships().size()); + + assertEquals(11, workspace.getViews().getConfiguration().getStyles().getElements().size()); + assertEquals(0, workspace.getViews().getConfiguration().getStyles().getRelationships().size()); + + assertEquals(0, workspace.getViews().getConfiguration().getThemes().length); + + assertEquals(0, workspace.getDocumentation().getSections().size()); + assertEquals(0, workspace.getDocumentation().getDecisions().size()); + } + + @Test + void test_bigbankplc_systemlandscape() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/big-bank-plc/system-landscape.dsl")); + + Workspace workspace = parser.getWorkspace(); + + assertEquals(Location.External, workspace.getModel().getPersonWithName("Personal Banking Customer").getLocation()); + assertEquals(Location.Internal, workspace.getModel().getPersonWithName("Customer Service Staff").getLocation()); + assertEquals(Location.Internal, workspace.getModel().getPersonWithName("Back Office Staff").getLocation()); + + assertEquals(7, workspace.getModel().getElements().size()); + assertEquals(3, workspace.getModel().getPeople().size()); + assertEquals(4, workspace.getModel().getSoftwareSystems().size()); + + assertEquals(9, workspace.getModel().getRelationships().size()); + + assertEquals(1, workspace.getViews().getSystemLandscapeViews().size()); + assertEquals(0, workspace.getViews().getSystemContextViews().size()); + assertEquals(0, workspace.getViews().getContainerViews().size()); + assertEquals(0, workspace.getViews().getComponentViews().size()); + assertEquals(0, workspace.getViews().getDynamicViews().size()); + assertEquals(0, workspace.getViews().getDeploymentViews().size()); + + assertEquals(7, workspace.getViews().getSystemLandscapeViews().iterator().next().getElements().size()); + assertEquals(9, workspace.getViews().getSystemLandscapeViews().iterator().next().getRelationships().size()); + + assertEquals(4, workspace.getViews().getConfiguration().getStyles().getElements().size()); + assertEquals(0, workspace.getViews().getConfiguration().getStyles().getRelationships().size()); + + assertEquals(0, workspace.getViews().getConfiguration().getThemes().length); + } + + @Test + void test_bigbankplc_internetbankingsystem() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/big-bank-plc/internet-banking-system.dsl")); + + Workspace workspace = parser.getWorkspace(); + + assertEquals(Location.External, workspace.getModel().getPersonWithName("Personal Banking Customer").getLocation()); + assertEquals(Location.Internal, workspace.getModel().getPersonWithName("Customer Service Staff").getLocation()); + assertEquals(Location.Internal, workspace.getModel().getPersonWithName("Back Office Staff").getLocation()); + + assertEquals(51, workspace.getModel().getElements().size()); + assertEquals(3, workspace.getModel().getPeople().size()); + assertEquals(4, workspace.getModel().getSoftwareSystems().size()); + assertEquals(5, workspace.getModel().getSoftwareSystemWithName("Internet Banking System").getContainers().size()); + assertEquals(6, workspace.getModel().getSoftwareSystemWithName("Internet Banking System").getContainerWithName("API Application").getComponents().size()); + assertEquals(5, workspace.getModel().getDeploymentNodes().size()); + assertEquals(21, workspace.getModel().getElements().stream().filter(e -> e instanceof DeploymentNode).count()); + assertEquals(2, workspace.getModel().getElements().stream().filter(e -> e instanceof SoftwareSystemInstance).count()); + assertEquals(10, workspace.getModel().getElements().stream().filter(e -> e instanceof ContainerInstance).count()); + assertEquals(0, workspace.getModel().getElements().stream().filter(e -> e instanceof InfrastructureNode).count()); + + assertEquals(42, workspace.getModel().getRelationships().size()); + + assertEquals(0, workspace.getViews().getSystemLandscapeViews().size()); + assertEquals(1, workspace.getViews().getSystemContextViews().size()); + assertEquals(1, workspace.getViews().getContainerViews().size()); + assertEquals(1, workspace.getViews().getComponentViews().size()); + assertEquals(1, workspace.getViews().getDynamicViews().size()); + assertEquals(2, workspace.getViews().getDeploymentViews().size()); + + assertEquals(4, workspace.getViews().getSystemContextViews().iterator().next().getElements().size()); + assertEquals(4, workspace.getViews().getSystemContextViews().iterator().next().getRelationships().size()); + + assertEquals(8, workspace.getViews().getContainerViews().iterator().next().getElements().size()); + assertEquals(10, workspace.getViews().getContainerViews().iterator().next().getRelationships().size()); + + assertEquals(11, workspace.getViews().getComponentViews().iterator().next().getElements().size()); + assertEquals(13, workspace.getViews().getComponentViews().iterator().next().getRelationships().size()); + + assertEquals(4, workspace.getViews().getDynamicViews().iterator().next().getElements().size()); + assertEquals(6, workspace.getViews().getDynamicViews().iterator().next().getRelationships().size()); + + assertEquals(13, workspace.getViews().getDeploymentViews().stream().filter(v -> v.getKey().equals("DevelopmentDeployment")).findFirst().get().getElements().size()); + assertEquals(4, workspace.getViews().getDeploymentViews().stream().filter(v -> v.getKey().equals("DevelopmentDeployment")).findFirst().get().getRelationships().size()); + + assertEquals(20, workspace.getViews().getDeploymentViews().stream().filter(v -> v.getKey().equals("LiveDeployment")).findFirst().get().getElements().size()); + assertEquals(7, workspace.getViews().getDeploymentViews().stream().filter(v -> v.getKey().equals("LiveDeployment")).findFirst().get().getRelationships().size()); + + assertEquals(11, workspace.getViews().getConfiguration().getStyles().getElements().size()); + assertEquals(0, workspace.getViews().getConfiguration().getStyles().getRelationships().size()); + + assertEquals(0, workspace.getViews().getConfiguration().getThemes().length); + + assertEquals(4, workspace.getModel().getSoftwareSystemWithName("Internet Banking System").getDocumentation().getSections().size()); + assertEquals(1, workspace.getModel().getSoftwareSystemWithName("Internet Banking System").getDocumentation().getDecisions().size()); + } + + @Test + void test_frs() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/financial-risk-system.dsl")); + + + Workspace workspace = parser.getWorkspace(); + + assertEquals(9, workspace.getModel().getElements().size()); + assertEquals(2, workspace.getModel().getPeople().size()); + assertEquals(7, workspace.getModel().getSoftwareSystems().size()); + assertEquals(0, workspace.getModel().getDeploymentNodes().size()); + assertEquals(0, workspace.getModel().getElements().stream().filter(e -> e instanceof DeploymentNode).count()); + assertEquals(0, workspace.getModel().getElements().stream().filter(e -> e instanceof ContainerInstance).count()); + assertEquals(0, workspace.getModel().getElements().stream().filter(e -> e instanceof InfrastructureNode).count()); + + assertEquals(9, workspace.getModel().getRelationships().size()); + + assertEquals(0, workspace.getViews().getSystemLandscapeViews().size()); + assertEquals(1, workspace.getViews().getSystemContextViews().size()); + assertEquals(0, workspace.getViews().getContainerViews().size()); + assertEquals(0, workspace.getViews().getComponentViews().size()); + assertEquals(0, workspace.getViews().getDynamicViews().size()); + assertEquals(0, workspace.getViews().getDeploymentViews().size()); + + assertEquals(9, workspace.getViews().getSystemContextViews().iterator().next().getElements().size()); + assertEquals(9, workspace.getViews().getSystemContextViews().iterator().next().getRelationships().size()); + + assertEquals(5, workspace.getViews().getConfiguration().getStyles().getElements().size()); + assertEquals(4, workspace.getViews().getConfiguration().getStyles().getRelationships().size()); + + assertEquals(0, workspace.getViews().getConfiguration().getThemes().length); + } + + @Test + void test_includeLocalFile() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/include-file.dsl")); + + Workspace workspace = parser.getWorkspace(); + Model model = workspace.getModel(); + + assertEquals(1, model.getSoftwareSystems().size()); + assertNotNull(model.getSoftwareSystemWithName("Software System")); + + assertEquals("workspace {\n" + + "\n" + + " model {\n" + + " softwareSystem = softwareSystem \"Software System\" {\n" + + " !docs docs\n" + + " }\n" + + " }\n" + + "\n" + + "}\n", new String(Base64.getDecoder().decode(workspace.getProperties().get("structurizr.dsl")))); + } + + @Test + void test_includeLocalDirectory() throws Exception { + File hiddenFile = new File("src/test/resources/dsl/include/model/software-system/.DS_Store"); + if (hiddenFile.exists()) { + hiddenFile.delete(); + } + + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/include-directory.dsl")); + + Workspace workspace = parser.getWorkspace(); + Model model = workspace.getModel(); + + assertEquals(3, workspace.getModel().getSoftwareSystems().size()); + SoftwareSystem softwareSystem1 = model.getSoftwareSystemWithName("Software System 1"); + assertNotNull(softwareSystem1); + assertEquals(1, softwareSystem1.getDocumentation().getSections().size()); + + SoftwareSystem softwareSystem2 = model.getSoftwareSystemWithName("Software System 2"); + assertNotNull(softwareSystem2); + assertEquals(1, softwareSystem2.getDocumentation().getSections().size()); + + SoftwareSystem softwareSystem3 = model.getSoftwareSystemWithName("Software System 3"); + assertNotNull(softwareSystem3); + assertEquals(1, softwareSystem3.getDocumentation().getSections().size()); + + assertEquals("workspace {\n" + + "\n" + + " model {\n" + + " !constant SOFTWARE_SYSTEM_NAME \"Software System 1\"\n" + + " softwareSystem \"${SOFTWARE_SYSTEM_NAME}\" {\n" + + " !docs ../../docs\n" + + " }\n" + + "\n" + + " !constant SOFTWARE_SYSTEM_NAME \"Software System 2\"\n" + + " softwareSystem \"${SOFTWARE_SYSTEM_NAME}\" {\n" + + " !docs ../../docs\n" + + " }\n" + + "\n" + + " !constant SOFTWARE_SYSTEM_NAME \"Software System 3\"\n" + + " softwareSystem \"${SOFTWARE_SYSTEM_NAME}\" {\n" + + " !docs ../../docs\n" + + " }\n" + + " }\n" + + "\n" + + "}\n", new String(Base64.getDecoder().decode(workspace.getProperties().get("structurizr.dsl")))); + } + + @Test + void test_includeLocalDirectory_WhenThereAreHiddenFiles() throws Exception { + File hiddenFile = new File("src/test/resources/dsl/include/model/software-system/.DS_Store"); + if (hiddenFile.exists()) { + hiddenFile.delete(); + } + Files.writeString(hiddenFile.toPath(), "hello world"); + + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/include-directory.dsl")); + } + + @Test + void test_includeUrl() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/include-url.dsl")); + + Workspace workspace = parser.getWorkspace(); + Model model = workspace.getModel(); + ViewSet views = workspace.getViews(); + + assertEquals(1, workspace.getModel().getSoftwareSystems().size()); + SoftwareSystem softwareSystem = model.getSoftwareSystemWithName("Software System"); + + assertEquals("workspace {\n" + + "\n" + + " model {\n" + + " softwareSystem = softwareSystem \"Software System\" {\n" + + " !docs docs\n" + + " }\n" + + " }\n" + + "\n" + + "}\n", new String(Base64.getDecoder().decode(workspace.getProperties().get("structurizr.dsl")))); + } + + @Test + void test_include_WhenRunningInRestrictedMode() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.setRestricted(true); + + // the model include will be ignored, so no software systems + parser.parse(new File("src/test/resources/dsl/include-file.dsl")); + assertEquals(0, model.getSoftwareSystems().size()); + } + + @ParameterizedTest + @ValueSource(strings = { "src/test/resources/dsl/extend/extend-workspace-from-json-file.dsl", "src/test/resources/dsl/extend/extend-workspace-from-json-url.dsl" }) + void test_extendWorkspaceFromJson(String dslFile) throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File(dslFile)); + + Workspace workspace = parser.getWorkspace(); + Model model = workspace.getModel(); + + assertEquals(1, model.getPeople().size()); + Person user = model.getPersonWithName("User"); + + assertEquals(3, workspace.getModel().getSoftwareSystems().size()); + SoftwareSystem softwareSystem = model.getSoftwareSystemWithName("Software System 1"); + assertTrue(user.hasEfferentRelationshipWith(softwareSystem, "Uses")); + + assertEquals(2, softwareSystem.getContainers().size()); + assertNotNull(softwareSystem.getContainers().stream().filter(c -> c.getName().equals("Web Application 1")).findFirst()); + assertNotNull(softwareSystem.getContainers().stream().filter(c -> c.getName().equals("Web Application 2")).findFirst()); + } + + @Test + void test_extendWorkspaceFromJsonFile_WhenRunningInRestrictedMode() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.setRestricted(true); + + File dslFile = new File("src/test/resources/dsl/extend/extend-workspace-from-json-file.dsl"); + + try { + // this will fail, because the model import will be ignored + parser.parse(dslFile); + fail(); + } catch (StructurizrDslParserException e) { + assertEquals("Cannot import workspace from a file when running in restricted mode at line 1 of " + dslFile.getAbsolutePath() + ": workspace extends workspace.json {", e.getMessage()); + } + } + + @ParameterizedTest + @ValueSource(strings = { "src/test/resources/dsl/extend/extend-workspace-from-dsl-file.dsl", "src/test/resources/dsl/extend/extend-workspace-from-dsl-url.dsl" }) + void test_extendWorkspaceFromDsl(String dslFile) throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File(dslFile)); + + Workspace workspace = parser.getWorkspace(); + Model model = workspace.getModel(); + + assertEquals(1, model.getPeople().size()); + Person user = model.getPersonWithName("User"); + + assertEquals(3, workspace.getModel().getSoftwareSystems().size()); + SoftwareSystem softwareSystem = model.getSoftwareSystemWithName("Software System 1"); + assertTrue(user.hasEfferentRelationshipWith(softwareSystem, "Uses")); + + assertEquals(1, softwareSystem.getContainers().size()); + assertEquals("Web Application", softwareSystem.getContainers().iterator().next().getName()); + } + + @Test + void test_extendWorkspaceFromDslFile_WhenRunningInRestrictedMode() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.setRestricted(true); + + File dslFile = new File("src/test/resources/dsl/extend/extend-workspace-from-dsl-file.dsl"); + try { + // this will fail, because the model import will be ignored + parser.parse(dslFile); + fail(); + } catch (StructurizrDslParserException e) { + assertEquals("Cannot import workspace from a file when running in restricted mode at line 1 of " + dslFile.getAbsolutePath() +": workspace extends workspace.dsl {", e.getMessage()); + } + } + + @Test + void test_extendWorkspaceFromDslFiles() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/extend/4.dsl")); + + Workspace workspace = parser.getWorkspace(); + Model model = workspace.getModel(); + ViewSet views = workspace.getViews(); + + assertEquals(3, model.getPeople().size()); + assertEquals(1, views.getViews().size()); + } + + @Test + void test_ref() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/ref.dsl")); + + assertNotNull(parser.getWorkspace().getModel().getElementWithCanonicalName("InfrastructureNode://Live/Amazon Web Services/New deployment node/New infrastructure node")); + assertNotNull(parser.getWorkspace().getModel().getElementWithCanonicalName("InfrastructureNode://Live/Amazon Web Services/US-East-1/New deployment node 1/New infrastructure node 1")); + assertNotNull(parser.getWorkspace().getModel().getElementWithCanonicalName("InfrastructureNode://Live/Amazon Web Services/US-East-1/New deployment node 2/New infrastructure node 2")); + } + + @Test + void test_parallel1() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/parallel1.dsl")); + + assertFalse(parser.getWorkspace().isEmpty()); + DynamicView view = parser.getWorkspace().getViews().getDynamicViews().iterator().next(); + List<RelationshipView> relationships = new ArrayList<>(view.getRelationships()); + assertEquals(4, relationships.size()); + assertEquals("1", relationships.get(0).getOrder()); + assertEquals("2", relationships.get(1).getOrder()); + assertEquals("3", relationships.get(2).getOrder()); + assertEquals("3", relationships.get(3).getOrder()); + } + + @Test + void test_parallel2() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/parallel2.dsl")); + + assertFalse(parser.getWorkspace().isEmpty()); + DynamicView view = parser.getWorkspace().getViews().getDynamicViews().iterator().next(); + List<RelationshipView> relationships = new ArrayList<>(view.getRelationships()); + assertEquals(4, relationships.size()); + assertEquals("1", relationships.get(0).getOrder()); + assertEquals("2", relationships.get(1).getOrder()); + assertEquals("2", relationships.get(2).getOrder()); + assertEquals("3", relationships.get(3).getOrder()); + } + + @Test + void test_groups() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/groups.dsl")); + + ContainerView containerView = parser.getWorkspace().getViews().getContainerViews().iterator().next(); + assertEquals(4, containerView.getElements().size()); + + DeploymentView deploymentView = parser.getWorkspace().getViews().getDeploymentViews().iterator().next(); + assertEquals(6, deploymentView.getElements().size()); + } + + @Test + void test_nested_groups() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/groups-nested.dsl")); + + SoftwareSystem a = parser.getWorkspace().getModel().getSoftwareSystemWithName("A"); + assertEquals("Organisation/Department A", a.getGroup()); + + Container aApi = a.getContainerWithName("A API"); + assertEquals("Capability 1/Service A", aApi.getGroup()); + + Container aDatabase = a.getContainerWithName("A Database"); + assertEquals("Capability 1/Service A", aDatabase.getGroup()); + + Container bApi = a.getContainerWithName("B API"); + assertEquals("Capability 1/Service B", bApi.getGroup()); + + Container bDatabase = a.getContainerWithName("B Database"); + assertEquals("Capability 1/Service B", bDatabase.getGroup()); + + SoftwareSystem b = parser.getWorkspace().getModel().getSoftwareSystemWithName("B"); + assertEquals("Organisation/Department B", b.getGroup()); + + SoftwareSystem c = parser.getWorkspace().getModel().getSoftwareSystemWithName("C"); + assertEquals("Organisation", c.getGroup()); + + SoftwareSystem d = parser.getWorkspace().getModel().getSoftwareSystemWithName("D"); + assertEquals("Department A/Team 1", d.getGroup()); + } + + @Test + void test_hierarchicalIdentifiers() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/hierarchical-identifiers.dsl")); + + Workspace workspace = parser.getWorkspace(); + assertEquals(0, workspace.getModel().getSoftwareSystemWithName("B").getRelationships().size()); + } + + @Test + void test_hierarchicalIdentifiersWhenUnassigned() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/hierarchical-identifiers-when-unassigned.dsl")); + + Workspace workspace = parser.getWorkspace(); + IdentifiersRegister identifiersRegister = parser.getIdentifiersRegister(); + + assertEquals(6, identifiersRegister.getElementIdentifiers().size()); + for (String identifier : identifiersRegister.getElementIdentifiers()) { + assertFalse(identifier.startsWith("null")); + } + } + + @Test + void test_hierarchicalIdentifiersAndDeploymentNodes() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/hierarchical-identifiers-and-deployment-nodes-1.dsl")); + } + + @Test + void test_hierarchicalIdentifiersAndDeploymentNodes_WhenSoftwareSystemNameClashes() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/hierarchical-identifiers-and-deployment-nodes-2.dsl")); + } + + @Test + void test_hierarchicalIdentifiersAndDeploymentNodes_WhenSoftwareContainerClashes() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/hierarchical-identifiers-and-deployment-nodes-3.dsl")); + } + + @Test + void test_pluginWithoutParameters() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/plugin-without-parameters.dsl")); + + assertNotNull(parser.getWorkspace().getModel().getPersonWithName("Java")); + } + + @Test + void test_pluginWithParameters() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/plugin-with-parameters.dsl")); + + assertNotNull(parser.getWorkspace().getModel().getPersonWithName("Java")); + } + + @Test + void test_script() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/script-external.dsl")); + + assertNotNull(parser.getWorkspace().getModel().getPersonWithName("Groovy")); + assertNotNull(parser.getWorkspace().getModel().getPersonWithName("Kotlin")); + assertNotNull(parser.getWorkspace().getModel().getPersonWithName("Ruby")); + } + + @Test + void test_scriptWithParameters() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/script-external-with-parameters.dsl")); + + assertNotNull(parser.getWorkspace().getModel().getPersonWithName("Groovy")); + } + + @Test + void test_inlineScript() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/script-inline.dsl")); + + assertNotNull(parser.getWorkspace().getModel().getPersonWithName("Groovy")); + assertNotNull(parser.getWorkspace().getModel().getPersonWithName("Kotlin")); + assertNotNull(parser.getWorkspace().getModel().getPersonWithName("Ruby")); + + assertTrue(parser.getWorkspace().getModel().getPersonWithName("User").hasTag("Groovy")); + assertTrue(parser.getWorkspace().getModel().getPersonWithName("User").getRelationships().iterator().next().hasTag("Groovy")); + assertEquals("Groovy", parser.getWorkspace().getViews().getSystemLandscapeViews().iterator().next().getDescription()); + } + + @Test + void test_docs() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/docs/workspace.dsl")); + + SoftwareSystem softwareSystem = parser.getWorkspace().getModel().getSoftwareSystemWithName("Software System"); + Container container = softwareSystem.getContainerWithName("Container"); + Component component = container.getComponentWithName("Component"); + + assertEquals(1, parser.getWorkspace().getDocumentation().getSections().size()); + assertEquals(1, softwareSystem.getDocumentation().getSections().size()); + assertEquals(1, container.getDocumentation().getSections().size()); + assertEquals(1, component.getDocumentation().getSections().size()); + } + + @Test + void test_adrs() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/adrs/workspace.dsl")); + + SoftwareSystem softwareSystem = parser.getWorkspace().getModel().getSoftwareSystemWithName("Software System"); + Container container = softwareSystem.getContainerWithName("Container"); + Component component = container.getComponentWithName("Component"); + + assertEquals(10, parser.getWorkspace().getDocumentation().getDecisions().size()); + assertEquals(10, softwareSystem.getDocumentation().getDecisions().size()); + assertEquals(10, container.getDocumentation().getDecisions().size()); + assertEquals(10, component.getDocumentation().getDecisions().size()); + } + + @Test + void test_this() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/this.dsl")); + } + + @Test + void test_workspaceWithControlCharacters() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/workspace-with-bom.dsl")); + } + + @Test + void test_excludeRelationships() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/exclude-relationships.dsl")); + } + + @Test + void test_unexpectedTokensBeforeWorkspace() { + File dslFile = new File("src/test/resources/dsl/unexpected-tokens-before-workspace.dsl"); + + try { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(dslFile); + fail(); + } catch (StructurizrDslParserException e) { + assertEquals("Unexpected tokens (expected: workspace) at line 1 of " + dslFile.getAbsolutePath() + ": hello world", e.getMessage()); + } + } + + @Test + void test_unexpectedTokensAfterWorkspace() { + File dslFile = new File("src/test/resources/dsl/unexpected-tokens-after-workspace.dsl"); + + try { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(dslFile); + fail(); + } catch (StructurizrDslParserException e) { + assertEquals("Unexpected tokens at line 4 of " + dslFile.getAbsolutePath() + ": hello world", e.getMessage()); + } + } + + @Test + void test_unexpectedTokensInWorkspace() { + File dslFile = new File("src/test/resources/dsl/unexpected-tokens-in-workspace.dsl"); + + try { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(dslFile); + fail(); + } catch (StructurizrDslParserException e) { + assertEquals("Unexpected tokens (expected: name, description, properties, !docs, !adrs, !identifiers, !impliedRelationships, model, views, configuration) at line 3 of " + dslFile.getAbsolutePath() + ": softwareSystem \"Name\"", e.getMessage()); + } + } + + @Test + void test_urlNotPermittedInGroup() { + File dslFile = new File("src/test/resources/dsl/group-url.dsl"); + + try { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(dslFile); + fail(); + } catch (StructurizrDslParserException e) { + assertEquals("Unexpected tokens (expected: !docs, !adrs, group, container, description, tags, url, properties, perspectives, ->) at line 6 of " + dslFile.getAbsolutePath() + ": url \"https://example.com\"", e.getMessage()); + } + } + + @Test + void test_multipleWorkspaceTokens_ThrowsAnException() { + File dslFile = new File("src/test/resources/dsl/multiple-workspace-tokens.dsl"); + + try { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(dslFile); + fail(); + } catch (StructurizrDslParserException e) { + assertEquals("Multiple workspaces are not permitted in a DSL definition at line 9 of " + dslFile.getAbsolutePath() + ": workspace {", e.getMessage()); + } + } + + @Test + void test_multipleModelTokens_ThrowsAnException() { + File dslFile = new File("src/test/resources/dsl/multiple-model-tokens.dsl"); + + try { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(dslFile); + fail(); + } catch (StructurizrDslParserException e) { + assertEquals("Multiple models are not permitted in a DSL definition at line 7 of " + dslFile.getAbsolutePath() + ": model {", e.getMessage()); + } + } + + @Test + void test_multipleViewTokens_ThrowsAnException() { + File dslFile = new File("src/test/resources/dsl/multiple-view-tokens.dsl"); + + try { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(dslFile); + fail(); + } catch (StructurizrDslParserException e) { + assertEquals("Multiple view sets are not permitted in a DSL definition at line 13 of " + dslFile.getAbsolutePath() + ": views {", e.getMessage()); + } + } + + @Test + void test_dynamicViewWithExplicitRelationships() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/dynamic-view-with-explicit-relationships.dsl")); + } + + @Test + void test_dynamicViewWithCustomElements() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/dynamic-view-with-custom-elements.dsl")); + } + + @Test + void test_workspaceProperties() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/workspace-properties.dsl")); + + assertEquals("false", parser.getWorkspace().getProperties().get("structurizr.dslEditor")); + } + + @Test + void test_viewsWithoutKeys() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/views-without-keys.dsl")); + + assertTrue(parser.getWorkspace().getViews().getSystemLandscapeViews().stream().anyMatch(view -> view.getKey().equals("SystemLandscape-001"))); + assertTrue(parser.getWorkspace().getViews().getSystemLandscapeViews().stream().anyMatch(view -> view.getKey().equals("SystemLandscape-002"))); + assertTrue(parser.getWorkspace().getViews().getSystemLandscapeViews().stream().anyMatch(view -> view.getKey().equals("SystemLandscape-003"))); + } + + @Test + void test_identifiers() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/identifiers.dsl")); + + Workspace workspace = parser.getWorkspace(); + Model model = workspace.getModel(); + + Person user = model.getPersonWithName("User"); + SoftwareSystem softwareSystem = model.getSoftwareSystemWithName("Software System"); + Container container = softwareSystem.getContainerWithName("Container"); + Relationship relationship = user.getEfferentRelationshipWith(container); + Relationship impliedRelationship = user.getEfferentRelationshipWith(softwareSystem); + + IdentifiersRegister register = parser.getIdentifiersRegister(); + assertEquals("user", register.findIdentifier(user)); + assertEquals("softwaresystem", register.findIdentifier(softwareSystem)); + assertEquals("softwaresystem.container", register.findIdentifier(container)); + assertEquals("rel", register.findIdentifier(relationship)); + + assertSame(user, register.getElement("user")); + assertSame(softwareSystem, register.getElement("softwareSystem")); + assertSame(container, register.getElement("softwareSystem.container")); + assertSame(relationship, register.getRelationship("rel")); + + assertEquals("user", user.getProperties().get("structurizr.dsl.identifier")); + assertEquals("softwaresystem", softwareSystem.getProperties().get("structurizr.dsl.identifier")); + assertEquals("softwaresystem.container", container.getProperties().get("structurizr.dsl.identifier")); + assertEquals("rel", relationship.getProperties().get("structurizr.dsl.identifier")); + assertNull(impliedRelationship.getProperties().get("structurizr.dsl.identifier")); + } + + @Test + void test_imageViews_ViaFiles() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/image-views/workspace-via-file.dsl")); + + Workspace workspace = parser.getWorkspace(); + assertEquals(4, workspace.getViews().getImageViews().size()); + + ImageView plantumlView = (ImageView)workspace.getViews().getViewWithKey("plantuml"); + assertEquals("diagram.puml", plantumlView.getTitle()); + assertEquals("http://localhost:7777/png/SoWkIImgAStDuNBAJrBGjLDmpCbCJbMmKiX8pSd9vt98pKi1IW80", plantumlView.getContent()); + assertEquals("image/png", plantumlView.getContentType()); + + ImageView mermaidView = (ImageView)workspace.getViews().getViewWithKey("mermaid"); + assertEquals("diagram.mmd", mermaidView.getTitle()); + assertEquals("http://localhost:8888/img/Zmxvd2NoYXJ0IFRECiAgICBTdGFydCAtLT4gU3RvcA==?type=png", mermaidView.getContent()); + assertEquals("image/png", mermaidView.getContentType()); + + ImageView krokiView = (ImageView)workspace.getViews().getViewWithKey("kroki"); + assertEquals("diagram.dot", krokiView.getTitle()); + assertEquals("http://localhost:9999/graphviz/png/eNpLyUwvSizIUHBXqPZIzcnJ17ULzy_KSanlAgB1EAjQ", krokiView.getContent()); + assertEquals("image/png", krokiView.getContentType()); + + ImageView imageView = (ImageView)workspace.getViews().getViewWithKey("image"); + assertEquals("logo.png", imageView.getTitle()); + assertEquals("data:image/png;base64,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", imageView.getContent()); + assertEquals("image/png", imageView.getContentType()); + } + + @Test + void test_imageViews_ViaUrls() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/image-views/workspace-via-url.dsl")); + + Workspace workspace = parser.getWorkspace(); + assertEquals(4, workspace.getViews().getImageViews().size()); + + ImageView plantumlView = (ImageView)workspace.getViews().getViewWithKey("plantuml"); + assertEquals("diagram.puml", plantumlView.getTitle()); + assertEquals("http://localhost:7777/svg/SoWkIImgAStDuNBAJrBGjLDmpCbCJbMmKiX8pSd9vt98pKi1IW80", plantumlView.getContent()); + assertEquals("image/svg+xml", plantumlView.getContentType()); + + ImageView mermaidView = (ImageView)workspace.getViews().getViewWithKey("mermaid"); + assertEquals("diagram.mmd", mermaidView.getTitle()); + assertEquals("http://localhost:8888/svg/Zmxvd2NoYXJ0IFRECiAgICBTdGFydCAtLT4gU3RvcA==", mermaidView.getContent()); + assertEquals("image/svg+xml", mermaidView.getContentType()); + + ImageView krokiView = (ImageView)workspace.getViews().getViewWithKey("kroki"); + assertEquals("diagram.dot", krokiView.getTitle()); + assertEquals("http://localhost:9999/graphviz/svg/eNpLyUwvSizIUHBXqPZIzcnJ17ULzy_KSanlAgB1EAjQ", krokiView.getContent()); + assertEquals("image/svg+xml", krokiView.getContentType()); + + ImageView imageView = (ImageView)workspace.getViews().getViewWithKey("image"); + assertEquals("logo.png", imageView.getTitle()); + assertEquals("https://raw.githubusercontent.com/structurizr/dsl/master/src/test/dsl/image-views/logo.png", imageView.getContent()); + assertEquals("image/png", imageView.getContentType()); + } + + @Test + void test_EmptyDeploymentEnvironment() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/deployment-environment-empty.dsl")); + + assertEquals(1, parser.getWorkspace().getModel().getDeploymentNodes().size()); + } + + @Test + void test_MultiLineSupport() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/multi-line.dsl")); + + assertNotNull(parser.getWorkspace().getModel().getSoftwareSystemWithName("Software System")); + } + + @Test + void test_MultiLineWithError() { + File dslFile = new File("src/test/resources/dsl/multi-line-with-error.dsl"); + + try { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(dslFile); + fail(); + } catch (StructurizrDslParserException e) { + // check that the error message includes the original line number + assertEquals("Unexpected tokens (expected: !docs, !adrs, group, container, description, tags, url, properties, perspectives, ->) at line 8 of " + dslFile.getAbsolutePath() + ": component \"Component\" // components not permitted inside software systems", e.getMessage()); + } + } + + @Test + void test_RelationshipAlreadyExists() { + File dslFile = new File("src/test/resources/dsl/relationship-already-exists.dsl"); + + try { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(dslFile); + fail(); + } catch (StructurizrDslParserException e) { + assertEquals("A relationship between \"SoftwareSystem://B\" and \"SoftwareSystem://A\" already exists at line 10 of " + dslFile.getAbsolutePath() + ": b -> a", e.getMessage()); + } + } + + @Test + void test_ExcludeImpliedRelationship() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/exclude-implied-relationship.dsl")); + + // check the system landscape view doesn't include any relationships + assertEquals(0, parser.getWorkspace().getViews().getSystemLandscapeViews().iterator().next().getRelationships().size()); + } + + @Test + void test_IncludeImpliedRelationship() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/include-implied-relationship.dsl")); + + // check the system landscape view includes a relationship + assertEquals(1, parser.getWorkspace().getViews().getSystemLandscapeViews().iterator().next().getRelationships().size()); + } + + @Test + void test_GroupWithoutBrace() throws Exception { + File dslFile = new File("src/test/resources/dsl/group-without-brace.dsl"); + + try { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(dslFile); + fail(); + } catch (StructurizrDslParserException e) { + assertEquals("Expected: group <name> { at line 4 of " + dslFile.getAbsolutePath() + ": group \"Name\"", e.getMessage()); + } + } + + @Test + void test_ISO8859Encoding() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.setCharacterEncoding(StandardCharsets.ISO_8859_1); + parser.parse(new File("src/test/resources/dsl/iso-8859.dsl")); + assertNotNull(parser.getWorkspace().getModel().getSoftwareSystemWithName("Namé")); + } + + @Test + void test_ScriptInDynamicView() throws Exception { + File dslFile = new File("src/test/resources/dsl/script-in-dynamic-view.dsl"); + + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(dslFile); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DynamicViewContentParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DynamicViewContentParserTests.java new file mode 100644 index 000000000..70bba7b50 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DynamicViewContentParserTests.java @@ -0,0 +1,187 @@ +package com.structurizr.dsl; + +import com.structurizr.model.Person; +import com.structurizr.model.Relationship; +import com.structurizr.model.SoftwareSystem; +import com.structurizr.view.DynamicView; +import com.structurizr.view.RelationshipView; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class DynamicViewContentParserTests extends AbstractTests { + + private DynamicViewContentParser parser = new DynamicViewContentParser(); + + @Test + void test_parseRelationship_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parseRelationship(new DynamicViewDslContext(null), tokens("source", "->", "destination", "description", "technology", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: <identifier> -> <identifier> [description] [technology]", e.getMessage()); + } + } + + @Test + void test_parseRelationship_ThrowsAnException_WhenTheDestinationIdentifierIsMissing() { + try { + parser.parseRelationship(new DynamicViewDslContext(null), tokens("source", "->")); + fail(); + } catch (Exception e) { + assertEquals("Expected: <identifier> -> <identifier> [description] [technology]", e.getMessage()); + } + } + + @Test + void test_parseRelationship_ThrowsAnException_WhenTheSourceElementIsNotDefined() { + DynamicViewDslContext context = new DynamicViewDslContext(null); + + try { + parser.parseRelationship(context, tokens("source", "->", "destination")); + fail(); + } catch (Exception e) { + assertEquals("The source element \"source\" does not exist", e.getMessage()); + } + } + + @Test + void test_parseRelationship_ThrowsAnException_WhenTheSourceElementIsNotAStaticStructureElement() { + DynamicViewDslContext context = new DynamicViewDslContext(null); + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("source", model.addDeploymentNode("Deployment Node")); + context.setIdentifierRegister(elements); + + try { + parser.parseRelationship(context, tokens("source", "->", "destination")); + fail(); + } catch (Exception e) { + assertEquals("The source element \"source\" should be a static structure or custom element", e.getMessage()); + } + } + + @Test + void test_parseRelationship_ThrowsAnException_WhenTheDestinationElementIsNotDefined() { + DynamicViewDslContext context = new DynamicViewDslContext(null); + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("source", model.addPerson("User", "Description")); + context.setIdentifierRegister(elements); + + try { + parser.parseRelationship(context, tokens("source", "->", "destination")); + fail(); + } catch (Exception e) { + assertEquals("The destination element \"destination\" does not exist", e.getMessage()); + } + } + + @Test + void test_parseRelationship_ThrowsAnException_WhenTheDestinationElementIsNotAStaticStructureElement() { + DynamicViewDslContext context = new DynamicViewDslContext(null); + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("source", model.addPerson("User", "Description")); + elements.register("destination", model.addDeploymentNode("Deployment Node")); + context.setIdentifierRegister(elements); + + try { + parser.parseRelationship(context, tokens("source", "->", "destination")); + fail(); + } catch (Exception e) { + assertEquals("The destination element \"destination\" should be a static structure or custom element", e.getMessage()); + } + } + + @Test + void test_parseRelationship_AddsTheRelationshipToTheView_WhenItAlreadyExistsInTheModel() { + Person user = model.addPerson("User", "Description"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + user.uses(softwareSystem, "Uses"); + DynamicView view = views.createDynamicView("key", "Description"); + DynamicViewDslContext context = new DynamicViewDslContext(view); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("source", user); + elements.register("destination", softwareSystem); + context.setIdentifierRegister(elements); + + parser.parseRelationship(context, tokens("source", "->", "destination")); + + assertEquals(1, view.getRelationships().size()); + RelationshipView rv = view.getRelationships().iterator().next(); + assertSame(user, rv.getRelationship().getSource()); + assertSame(softwareSystem, rv.getRelationship().getDestination()); + assertEquals("", rv.getDescription()); + assertEquals("1", rv.getOrder()); + } + + @Test + void test_parseRelationship_AddsTheRelationshipToTheViewWithAnOverriddenDescription_WhenItAlreadyExistsInTheModel() { + Person user = model.addPerson("User", "Description"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + user.uses(softwareSystem, "Uses"); + DynamicView view = views.createDynamicView("key", "Description"); + DynamicViewDslContext context = new DynamicViewDslContext(view); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("source", user); + elements.register("destination", softwareSystem); + context.setIdentifierRegister(elements); + + parser.parseRelationship(context, tokens("source", "->", "destination", "Does something with")); + + assertEquals(1, view.getRelationships().size()); + RelationshipView rv = view.getRelationships().iterator().next(); + assertSame(user, rv.getRelationship().getSource()); + assertSame(softwareSystem, rv.getRelationship().getDestination()); + assertEquals("Does something with", rv.getDescription()); + assertEquals("1", rv.getOrder()); + } + + @Test + void test_parseRelationship_AddsTheRelationshipWithTheSpecifiedTechnologyToTheView_WhenItAlreadyExistsInTheModel() { + Person user = model.addPerson("User", "Description"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + Relationship r1 = user.uses(softwareSystem, "Uses 1", "Tech 1"); + Relationship r2 = user.uses(softwareSystem, "Uses 2", "Tech 2"); + DynamicView view = views.createDynamicView("key", "Description"); + DynamicViewDslContext context = new DynamicViewDslContext(view); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("source", user); + elements.register("destination", softwareSystem); + context.setIdentifierRegister(elements); + + parser.parseRelationship(context, tokens("source", "->", "destination", "Description", "Tech 2")); + + assertEquals(1, view.getRelationships().size()); + RelationshipView rv = view.getRelationships().iterator().next(); + assertSame(r2, rv.getRelationship()); + assertSame(user, rv.getRelationship().getSource()); + assertSame(softwareSystem, rv.getRelationship().getDestination()); + assertEquals("Description", rv.getDescription()); + assertEquals("1", rv.getOrder()); + } + + @Test + void test_parseRelationship_ThrowsAnException_WhenItDoesNotAlreadyExistInTheModel() { + Person user = model.addPerson("User", "Description"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + DynamicView view = views.createDynamicView("key", "Description"); + DynamicViewDslContext context = new DynamicViewDslContext(view); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("source", user); + elements.register("destination", softwareSystem); + context.setIdentifierRegister(elements); + + assertEquals(0, model.getRelationships().size()); + + try { + parser.parseRelationship(context, tokens("source", "->", "destination", "Uses")); + fail(); + } catch (Exception e) { + assertEquals("A relationship between User and Software System does not exist in model.", e.getMessage()); + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DynamicViewParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DynamicViewParserTests.java new file mode 100644 index 000000000..f367ff064 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DynamicViewParserTests.java @@ -0,0 +1,197 @@ +package com.structurizr.dsl; + +import com.structurizr.model.Container; +import com.structurizr.model.SoftwareSystem; +import com.structurizr.view.DynamicView; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class DynamicViewParserTests extends AbstractTests { + + private DynamicViewParser parser = new DynamicViewParser(); + + @Test + void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { + DslContext context = context(); + try { + parser.parse(context, tokens("dynamic", "identifier", "key", "description", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: dynamic <*|software system identifier|container identifier> [key] [description] {", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheElementIdentifierIsMissing() { + DslContext context = context(); + try { + parser.parse(context, tokens("dynamic")); + fail(); + } catch (Exception e) { + assertEquals("Expected: dynamic <*|software system identifier|container identifier> [key] [description] {", e.getMessage()); + } + } + + @Test + void test_parse_GeneratesAKey_WhenTheKeyIsMissing() { + DslContext context = context(); + DynamicView view = parser.parse(context, tokens("dynamic", "*")); + + assertEquals("Dynamic-001", view.getKey()); + } + + @Test + void test_parse_ThrowsAnException_WhenTheElementIsNotDefined() { + DslContext context = context(); + try { + parser.parse(context, tokens("dynamic", "softwareSystem", "key")); + fail(); + } catch (Exception e) { + assertEquals("The software system or container \"softwareSystem\" does not exist", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheElementIsNotASoftwareSystemOrContainer() { + DslContext context = context(); + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("person", model.addPerson("Name", "Description")); + context.setIdentifierRegister(elements); + + try { + parser.parse(context, tokens("dynamic", "person", "key")); + fail(); + } catch (Exception e) { + assertEquals("The element \"person\" is not a software system or container", e.getMessage()); + } + } + + @Test + void test_parse_CreatesADynamicViewWithNoScope() { + parser.parse(context(), tokens("dynamic", "*", "key")); + List<DynamicView> views = new ArrayList<>(this.views.getDynamicViews()); + + assertEquals(1, views.size()); + assertEquals("key", views.get(0).getKey()); + assertEquals("", views.get(0).getDescription()); + assertNull(views.get(0).getElement()); + } + + @Test + void test_parse_CreatesADynamicViewWithNoScopeAndADescription() { + parser.parse(context(), tokens("dynamic", "*", "key", "Description")); + List<DynamicView> views = new ArrayList<>(this.views.getDynamicViews()); + + assertEquals(1, views.size()); + assertEquals("key", views.get(0).getKey()); + assertEquals("Description", views.get(0).getDescription()); + assertNull(views.get(0).getElement()); + } + + @Test + void test_parse_CreatesADynamicViewWithSoftwareSystemScope() { + DslContext context = context(); + IdentifiersRegister elements = new IdentifiersRegister(); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); + elements.register("softwaresystem", softwareSystem); + context.setIdentifierRegister(elements); + + parser.parse(context, tokens("dynamic", "softwareSystem")); + List<DynamicView> views = new ArrayList<>(this.views.getDynamicViews()); + + assertEquals(1, views.size()); + assertEquals("Dynamic-001", views.get(0).getKey()); + assertEquals("", views.get(0).getDescription()); + assertSame(softwareSystem, views.get(0).getElement()); + } + + @Test + void test_parse_CreatesADynamicViewWithSoftwareSystemScopeAndKey() { + DslContext context = context(); + IdentifiersRegister elements = new IdentifiersRegister(); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); + elements.register("softwaresystem", softwareSystem); + context.setIdentifierRegister(elements); + + parser.parse(context, tokens("dynamic", "softwareSystem", "key")); + List<DynamicView> views = new ArrayList<>(this.views.getDynamicViews()); + + assertEquals(1, views.size()); + assertEquals("key", views.get(0).getKey()); + assertEquals("", views.get(0).getDescription()); + assertSame(softwareSystem, views.get(0).getElement()); + } + + @Test + void test_parse_CreatesADynamicViewWithSoftwareSystemScopeAndKeyAndDescription() { + DslContext context = context(); + IdentifiersRegister elements = new IdentifiersRegister(); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); + elements.register("softwaresystem", softwareSystem); + context.setIdentifierRegister(elements); + + parser.parse(context, tokens("dynamic", "softwareSystem", "key", "Description")); + List<DynamicView> views = new ArrayList<>(this.views.getDynamicViews()); + + assertEquals(1, views.size()); + assertEquals("key", views.get(0).getKey()); + assertEquals("Description", views.get(0).getDescription()); + assertSame(softwareSystem, views.get(0).getElement()); + } + + @Test + void test_parse_CreatesADynamicViewWithContainerScope() { + DslContext context = context(); + IdentifiersRegister elements = new IdentifiersRegister(); + Container container = model.addSoftwareSystem("Name", "Description").addContainer("Container", "Description", "Technology"); + elements.register("container", container); + context.setIdentifierRegister(elements); + + parser.parse(context, tokens("dynamic", "container")); + List<DynamicView> views = new ArrayList<>(this.views.getDynamicViews()); + + assertEquals(1, views.size()); + assertEquals("Dynamic-001", views.get(0).getKey()); + assertEquals("", views.get(0).getDescription()); + assertSame(container, views.get(0).getElement()); + } + + @Test + void test_parse_CreatesADynamicViewWithContainerScopeAndKey() { + DslContext context = context(); + IdentifiersRegister elements = new IdentifiersRegister(); + Container container = model.addSoftwareSystem("Name", "Description").addContainer("Container", "Description", "Technology"); + elements.register("container", container); + context.setIdentifierRegister(elements); + + parser.parse(context, tokens("dynamic", "container", "key")); + List<DynamicView> views = new ArrayList<>(this.views.getDynamicViews()); + + assertEquals(1, views.size()); + assertEquals("key", views.get(0).getKey()); + assertEquals("", views.get(0).getDescription()); + assertSame(container, views.get(0).getElement()); + } + + @Test + void test_parse_CreatesADynamicViewWithContainerScopeAndKeyAndDescription() { + DslContext context = context(); + IdentifiersRegister elements = new IdentifiersRegister(); + Container container = model.addSoftwareSystem("Name", "Description").addContainer("Container", "Description", "Technology"); + elements.register("container", container); + context.setIdentifierRegister(elements); + + parser.parse(context, tokens("dynamic", "container", "key", "Description")); + List<DynamicView> views = new ArrayList<>(this.views.getDynamicViews()); + + assertEquals(1, views.size()); + assertEquals("key", views.get(0).getKey()); + assertEquals("Description", views.get(0).getDescription()); + assertSame(container, views.get(0).getElement()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DynamicViewRelationshipParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DynamicViewRelationshipParserTests.java new file mode 100644 index 000000000..2bd6cf066 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DynamicViewRelationshipParserTests.java @@ -0,0 +1,52 @@ +package com.structurizr.dsl; + +import com.structurizr.model.Relationship; +import com.structurizr.model.SoftwareSystem; +import com.structurizr.view.DynamicView; +import com.structurizr.view.RelationshipView; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +class DynamicViewRelationshipParserTests extends AbstractTests { + + private final DynamicViewRelationshipParser parser = new DynamicViewRelationshipParser(); + + @Test + void test_parseUrl_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + DynamicViewRelationshipContext context = new DynamicViewRelationshipContext(null); + parser.parseUrl(context, tokens("url", "url", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: url <url>", e.getMessage()); + } + } + + @Test + void test_parseUrl_ThrowsAnException_WhenNoUrlIsSpecified() { + try { + DynamicViewRelationshipContext context = new DynamicViewRelationshipContext(null); + parser.parseUrl(context, tokens("url")); + fail(); + } catch (Exception e) { + assertEquals("Expected: url <url>", e.getMessage()); + } + } + + @Test + void test_parseUrl_SetsTheUrl_WhenAUrlIsSpecified() { + SoftwareSystem a = model.addSoftwareSystem("A"); + SoftwareSystem b = model.addSoftwareSystem("B"); + Relationship r = a.uses(b, "Uses"); + + DynamicView dynamicView = workspace.getViews().createDynamicView("key", "Description"); + RelationshipView rv = dynamicView.add(r); + DynamicViewRelationshipContext context = new DynamicViewRelationshipContext(rv); + parser.parseUrl(context, tokens("url", "http://example.com")); + + assertEquals("http://example.com", rv.getUrl()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementStyleParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementStyleParserTests.java new file mode 100644 index 000000000..50086e8dc --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementStyleParserTests.java @@ -0,0 +1,546 @@ +package com.structurizr.dsl; + +import com.structurizr.view.Border; +import com.structurizr.view.ElementStyle; +import com.structurizr.view.Shape; +import org.junit.jupiter.api.Test; + +import java.io.File; + +import static org.junit.jupiter.api.Assertions.*; + +class ElementStyleParserTests extends AbstractTests { + + private ElementStyleParser parser = new ElementStyleParser(); + private ElementStyle elementStyle; + + private ElementStyleDslContext elementStyleDslContext() { + elementStyle = workspace.getViews().getConfiguration().getStyles().addElementStyle("Tag"); + ElementStyleDslContext context = new ElementStyleDslContext(elementStyle, new File(".")); + context.setWorkspace(workspace); + + return context; + } + + @Test + void test_parseElementStyle_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parseElementStyle(context(), tokens("element", "tag", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: element <tag> {", e.getMessage()); + } + } + + @Test + void test_parseElementStyle_ThrowsAnException_WhenTheTagIsMissing() { + try { + parser.parseElementStyle(context(), tokens("element")); + fail(); + } catch (Exception e) { + assertEquals("Expected: element <tag> {", e.getMessage()); + } + } + + @Test + void test_parseElementStyle_ThrowsAnException_WhenTheTagIsEmpty() { + try { + parser.parseElementStyle(context(), tokens("element", "")); + fail(); + } catch (Exception e) { + assertEquals("A tag must be specified", e.getMessage()); + } + } + + @Test + void test_parseElementStyle_CreatesAnElementStyle() { + parser.parseElementStyle(context(), tokens("element", "Element")); + + ElementStyle style = workspace.getViews().getConfiguration().getStyles().getElements().stream().filter(es -> "Element".equals(es.getTag())).findFirst().get(); + assertNotNull(style); + } + + @Test + void test_parseElementStyle_FindsAnExistingElementStyle() { + ElementStyle style = workspace.getViews().getConfiguration().getStyles().addElementStyle("Tag"); + assertSame(style, parser.parseElementStyle(context(), tokens("element", "Tag"))); + } + + @Test + void test_parseShape_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parseShape(elementStyleDslContext(), tokens("shape", "shape", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: shape <Box|RoundedBox|Circle|Ellipse|Hexagon|Diamond|Cylinder|Pipe|Person|Robot|Folder|WebBrowser|Window|MobileDevicePortrait|MobileDeviceLandscape|Component>", e.getMessage()); + } + } + + @Test + void test_parseShape_ThrowsAnException_WhenTheShapeIsMissing() { + try { + parser.parseShape(elementStyleDslContext(), tokens("shape")); + fail(); + } catch (Exception e) { + assertEquals("Expected: shape <Box|RoundedBox|Circle|Ellipse|Hexagon|Diamond|Cylinder|Pipe|Person|Robot|Folder|WebBrowser|Window|MobileDevicePortrait|MobileDeviceLandscape|Component>", e.getMessage()); + } + } + + @Test + void test_parseShape_ThrowsAnException_WhenTheShapeIsNotValid() { + try { + parser.parseShape(elementStyleDslContext(), tokens("shape", "square")); + fail(); + } catch (Exception e) { + assertEquals("The shape \"square\" is not valid", e.getMessage()); + } + } + + @Test + void test_parseShape_SetsTheShape() { + parser.parseShape(elementStyleDslContext(), tokens("shape", "roundedbox")); + assertEquals(Shape.RoundedBox, elementStyle.getShape()); + } + + @Test + void test_parseBackground_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parseBackground(elementStyleDslContext(), tokens("background", "hex", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: background <#rrggbb|color name>", e.getMessage()); + } + } + + @Test + void test_parseBackground_ThrowsAnException_WhenTheBackgroundIsMissing() { + try { + parser.parseBackground(elementStyleDslContext(), tokens("background")); + fail(); + } catch (Exception e) { + assertEquals("Expected: background <#rrggbb|color name>", e.getMessage()); + } + } + + @Test + void test_parseBackground_SetsTheBackgroundWhenUsingAHexColourCode() { + parser.parseBackground(elementStyleDslContext(), tokens("background", "#ff0000")); + assertEquals("#ff0000", elementStyle.getBackground()); + } + + @Test + void test_parseBackground_SetsTheBackgroundWhenUsingAColourName() { + parser.parseBackground(elementStyleDslContext(), tokens("background", "yellow")); + assertEquals("#ffff00", elementStyle.getBackground()); + } + + @Test + void test_parseStroke_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parseStroke(elementStyleDslContext(), tokens("stroke", "hex", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: stroke <#rrggbb|color name>", e.getMessage()); + } + } + + @Test + void test_parseStroke_ThrowsAnException_WhenTheStrokeIsMissing() { + try { + parser.parseStroke(elementStyleDslContext(), tokens("stroke")); + fail(); + } catch (Exception e) { + assertEquals("Expected: stroke <#rrggbb|color name>", e.getMessage()); + } + } + + @Test + void test_parseStroke_SetsTheStrokeWhenUsingAHexColourCode() { + parser.parseStroke(elementStyleDslContext(), tokens("stroke", "yellow")); + assertEquals("#ffff00", elementStyle.getStroke()); + } + + @Test + void test_parseStrokeWidth_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parseStrokeWidth(elementStyleDslContext(), tokens("strokeWidth", "4", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: strokeWidth <1-10>", e.getMessage()); + } + } + + @Test + void test_parseStrokeWidth_ThrowsAnException_WhenTheStrokeWidthIsMissing() { + try { + parser.parseStrokeWidth(elementStyleDslContext(), tokens("strokeWidth")); + fail(); + } catch (Exception e) { + assertEquals("Expected: strokeWidth <1-10>", e.getMessage()); + } + } + + @Test + void test_parseStrokeWidth_ThrowsAnException_WhenTheStrokeWidthIsNotANumber() { + try { + parser.parseStrokeWidth(elementStyleDslContext(), tokens("strokeWidth", "abc")); + fail(); + } catch (Exception e) { + assertEquals("Stroke width must be an integer between 1 and 10", e.getMessage()); + } + } + + @Test + void test_parseStrokeWidth_SetsTheStrokeWidth() { + parser.parseStrokeWidth(elementStyleDslContext(), tokens("strokeWidth", "4")); + assertEquals(4, elementStyle.getStrokeWidth()); + } + + @Test + void test_parseColour_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parseColour(elementStyleDslContext(), tokens("colour", "hex", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: colour <#rrggbb|color name>", e.getMessage()); + } + } + + @Test + void test_parseColour_ThrowsAnException_WhenTheColourIsMissing() { + try { + parser.parseColour(elementStyleDslContext(), tokens("colour")); + fail(); + } catch (Exception e) { + assertEquals("Expected: colour <#rrggbb|color name>", e.getMessage()); + } + } + + @Test + void test_parseColour_SetsTheColourWhenUsingAHexColourCode() { + parser.parseColour(elementStyleDslContext(), tokens("colour", "#ff0000")); + assertEquals("#ff0000", elementStyle.getColor()); + } + + @Test + void test_parseColour_SetsTheColourWhenUsingColourName() { + parser.parseColour(elementStyleDslContext(), tokens("colour", "yellow")); + assertEquals("#ffff00", elementStyle.getColor()); + } + + @Test + void test_parseBorder_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parseBorder(elementStyleDslContext(), tokens("border", "style", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: border <solid|dashed|dotted>", e.getMessage()); + } + } + + @Test + void test_parseBorder_ThrowsAnException_WhenTheBorderIsMissing() { + try { + parser.parseBorder(elementStyleDslContext(), tokens("border")); + fail(); + } catch (Exception e) { + assertEquals("Expected: border <solid|dashed|dotted>", e.getMessage()); + } + } + + @Test + void test_parseBorder_ThrowsAnException_WhenTheBorderIsNotValid() { + try { + parser.parseBorder(elementStyleDslContext(), tokens("border", "rounded")); + fail(); + } catch (Exception e) { + assertEquals("The border \"rounded\" is not valid", e.getMessage()); + } + } + + @Test + void test_parseBorder_SetsTheBorder() { + parser.parseBorder(elementStyleDslContext(), tokens("border", "dotted")); + assertEquals(Border.Dotted, elementStyle.getBorder()); + } + + @Test + void test_parseOpacity_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parseOpacity(elementStyleDslContext(), tokens("opacity", "percentage", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: opacity <0-100>", e.getMessage()); + } + } + + @Test + void test_parseOpacity_ThrowsAnException_WhenTheOpacityIsMissing() { + try { + parser.parseOpacity(elementStyleDslContext(), tokens("opacity")); + fail(); + } catch (Exception e) { + assertEquals("Expected: opacity <0-100>", e.getMessage()); + } + } + + @Test + void test_parseOpacity_ThrowsAnException_WhenTheOpacityIsNotValid() { + try { + parser.parseOpacity(elementStyleDslContext(), tokens("opacity", "abc")); + fail(); + } catch (Exception e) { + assertEquals("Opacity must be an integer between 0 and 100", e.getMessage()); + } + } + + @Test + void test_parseOpacity_SetsTheOpacity() { + parser.parseOpacity(elementStyleDslContext(), tokens("opacity", "75")); + assertEquals(75, elementStyle.getOpacity()); + } + + @Test + void test_parseWidth_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parseWidth(elementStyleDslContext(), tokens("width", "number", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: width <number>", e.getMessage()); + } + } + + @Test + void test_parseWidth_ThrowsAnException_WhenTheWidthIsMissing() { + try { + parser.parseWidth(elementStyleDslContext(), tokens("width")); + fail(); + } catch (Exception e) { + assertEquals("Expected: width <number>", e.getMessage()); + } + } + + @Test + void test_parseWidth_ThrowsAnException_WhenTheWidthIsNotValid() { + try { + parser.parseWidth(elementStyleDslContext(), tokens("width", "abc")); + fail(); + } catch (Exception e) { + assertEquals("Width must be a positive integer", e.getMessage()); + } + } + + @Test + void test_parseWidth_SetsTheWidth() { + parser.parseWidth(elementStyleDslContext(), tokens("width", "75")); + assertEquals(75, elementStyle.getWidth()); + } + + @Test + void test_parseHeight_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parseHeight(elementStyleDslContext(), tokens("height", "number", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: height <number>", e.getMessage()); + } + } + + @Test + void test_parseHeight_ThrowsAnException_WhenTheHeightIsMissing() { + try { + parser.parseHeight(elementStyleDslContext(), tokens("height")); + fail(); + } catch (Exception e) { + assertEquals("Expected: height <number>", e.getMessage()); + } + } + + @Test + void test_parseHeight_ThrowsAnException_WhenTheHeightIsNotValid() { + try { + parser.parseHeight(elementStyleDslContext(), tokens("height", "abc")); + fail(); + } catch (Exception e) { + assertEquals("Height must be a positive integer", e.getMessage()); + } + } + + @Test + void test_parseHeight_SetsTheHeight() { + parser.parseHeight(elementStyleDslContext(), tokens("height", "75")); + assertEquals(75, elementStyle.getHeight()); + } + + @Test + void test_parseFontSize_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parseFontSize(elementStyleDslContext(), tokens("fontSize", "number", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: fontSize <number>", e.getMessage()); + } + } + + @Test + void test_parseFontSize_ThrowsAnException_WhenTheFontSizeIsMissing() { + try { + parser.parseFontSize(elementStyleDslContext(), tokens("fontSize")); + fail(); + } catch (Exception e) { + assertEquals("Expected: fontSize <number>", e.getMessage()); + } + } + + @Test + void test_parseFontSize_ThrowsAnException_WhenTheFontSizeIsNotValid() { + try { + parser.parseFontSize(elementStyleDslContext(), tokens("fontSize", "abc")); + fail(); + } catch (Exception e) { + assertEquals("Font size must be a positive integer", e.getMessage()); + } + } + + @Test + void test_parseFontSize_SetsTheFontSize() { + parser.parseFontSize(elementStyleDslContext(), tokens("fontSize", "75")); + assertEquals(75, elementStyle.getFontSize()); + } + + @Test + void test_parseMetadata_ThrowsAnException_WhenTheMetadataIsMissing() { + try { + parser.parseMetadata(elementStyleDslContext(), tokens("metadata")); + fail(); + } catch (Exception e) { + assertEquals("Expected: metadata <true|false>", e.getMessage()); + } + } + + @Test + void test_parseMetadata_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parseMetadata(elementStyleDslContext(), tokens("metadata", "boolean", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: metadata <true|false>", e.getMessage()); + } + } + + @Test + void test_parseMetadata_ThrowsAnException_WhenTheMetadataIsNotValid() { + try { + parser.parseMetadata(elementStyleDslContext(), tokens("metadata", "abc")); + fail(); + } catch (Exception e) { + assertEquals("Metadata must be true or false", e.getMessage()); + } + } + + @Test + void test_parseMetadata_SetsTheMetadata() { + ElementStyleDslContext context = elementStyleDslContext(); + parser.parseMetadata(context, tokens("metadata", "false")); + assertEquals(false, elementStyle.getMetadata()); + + parser.parseMetadata(context, tokens("metadata", "true")); + assertEquals(true, elementStyle.getMetadata()); + } + + @Test + void test_parseDescription_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parseDescription(elementStyleDslContext(), tokens("description", "boolean", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: description <true|false>", e.getMessage()); + } + } + + @Test + void test_parseDescription_ThrowsAnException_WhenTheDescriptionIsMissing() { + try { + parser.parseDescription(elementStyleDslContext(), tokens("description")); + fail(); + } catch (Exception e) { + assertEquals("Expected: description <true|false>", e.getMessage()); + } + } + + @Test + void test_parseDescription_ThrowsAnException_WhenTheDescriptionIsNotValid() { + try { + parser.parseDescription(elementStyleDslContext(), tokens("description", "abc")); + fail(); + } catch (Exception e) { + assertEquals("Description must be true or false", e.getMessage()); + } + } + + @Test + void test_parseDescription_SetsTheDescription() { + ElementStyleDslContext context = elementStyleDslContext(); + parser.parseDescription(context, tokens("description", "false")); + assertEquals(false, elementStyle.getDescription()); + + parser.parseDescription(context, tokens("description", "true")); + assertEquals(true, elementStyle.getDescription()); + } + + @Test + void test_parseIcon_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parseIcon(elementStyleDslContext(), tokens("icon", "file", "extra"), false); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: icon <file|url>", e.getMessage()); + } + } + + @Test + void test_parseIcon_ThrowsAnException_WhenTheIconIsMissing() { + try { + parser.parseIcon(elementStyleDslContext(), tokens("icon"), false); + fail(); + } catch (Exception e) { + assertEquals("Expected: icon <file|url>", e.getMessage()); + } + } + + @Test + void test_parseIcon_ThrowsAnException_WhenTheIconDoesNotExist() { + try { + parser.parseIcon(elementStyleDslContext(), tokens("icon", "hello.png"), false); + fail(); + } catch (Exception e) { + assertEquals("hello.png does not exist", e.getMessage()); + } + } + + @Test + void test_parseIcon_SetsTheIconFromADataUri() { + parser.parseIcon(elementStyleDslContext(), tokens("icon", "data:image/png;base64,123456789012345678901234567890"), true); + assertTrue(elementStyle.getIcon().startsWith("data:image/png;base64,123456789012345678901234567890")); + } + + @Test + void test_parseIcon_SetsTheIconFromAHttpUrl() { + parser.parseIcon(elementStyleDslContext(), tokens("icon", "http://structurizr.com/logo.png"), true); + assertEquals("http://structurizr.com/logo.png", elementStyle.getIcon()); + } + + @Test + void test_parseIcon_SetsTheIconFromAHttpsUrl() { + parser.parseIcon(elementStyleDslContext(), tokens("icon", "https://structurizr.com/logo.png"), true); + assertEquals("https://structurizr.com/logo.png", elementStyle.getIcon()); + } + + @Test + void test_parseIcon_SetsTheIconFromAFile() { + parser.parseIcon(elementStyleDslContext(), tokens("icon", "src/test/resources/dsl/logo.png"), false); + System.out.println(elementStyle.getIcon()); + assertTrue(elementStyle.getIcon().startsWith("data:image/png;base64,")); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/EnterpriseParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/EnterpriseParserTests.java new file mode 100644 index 000000000..fa28b7cec --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/EnterpriseParserTests.java @@ -0,0 +1,63 @@ +package com.structurizr.dsl; + +import com.structurizr.model.Enterprise; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class EnterpriseParserTests extends AbstractTests { + + private EnterpriseParser parser = new EnterpriseParser(); + + @Test + void test_parse_SetsTheEnterpriseName_WhenItHasNotBeenSet() { + assertNull(workspace.getModel().getEnterprise()); + parser.parse(context(), tokens("enterprise", "New Name")); + assertEquals("New Name", workspace.getModel().getEnterprise().getName()); + } + + @Test + void test_parse_ThrowsAnException_WhenTheEnterpriseNameHasAlreadyBeenSet() { + workspace.getModel().setEnterprise(new Enterprise("My Enterprise")); + try { + parser.parse(context(), tokens("enterprise", "name")); + fail(); + } catch (Exception e) { + assertEquals("The name of the enterprise has already been set", e.getMessage()); + } + } + + @Test + void test_parse_DoesNothing_WhenANameIsSpecifiedButIsTheSameAsTheExistingEnterpriseName() { + workspace.getModel().setEnterprise(new Enterprise("My Enterprise")); + parser.parse(context(), tokens("My Enterprise")); + + assertEquals("My Enterprise", workspace.getModel().getEnterprise().getName()); + } + + @Test + void test_parse_DoesNothing_WhenNoNameIsSpecified() { + parser.parse(context(), tokens("enterprise")); + + assertNull(workspace.getModel().getEnterprise()); + } + + @Test + void test_parse_DoesNothing_WhenNoNameIsSpecifiedAndTheEnterpriseNameHasAlreadyBeenSet() { + workspace.getModel().setEnterprise(new Enterprise("My Enterprise")); + parser.parse(context(), tokens("enterprise")); + + assertEquals("My Enterprise", workspace.getModel().getEnterprise().getName()); + } + + @Test + void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parse(context(), tokens("enterprise", "name", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: enterprise [name]", e.getMessage()); + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ExplicitRelationshipParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ExplicitRelationshipParserTests.java new file mode 100644 index 000000000..1c9b5c401 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ExplicitRelationshipParserTests.java @@ -0,0 +1,232 @@ +package com.structurizr.dsl; + +import com.structurizr.model.*; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class ExplicitRelationshipParserTests extends AbstractTests { + + private ExplicitRelationshipParser parser = new ExplicitRelationshipParser(); + + @Test + void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parse(context(), tokens("source", "->", "destination", "description", "technology", "tags", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: <identifier> -> <identifier> [description] [technology] [tags]", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheDestinationIdentifierIsMissing() { + try { + parser.parse(context(), tokens("source", "->")); + fail(); + } catch (Exception e) { + assertEquals("Expected: <identifier> -> <identifier> [description] [technology] [tags]", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheSourceElementIsNotDefined() { + try { + parser.parse(context(), tokens("source", "->", "destination")); + fail(); + } catch (Exception e) { + assertEquals("The source element \"source\" does not exist", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheDestinationElementIsNotDefined() { + DslContext context = context(); + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("source", model.addPerson("User", "Description")); + context.setIdentifierRegister(elements); + + try { + parser.parse(context, tokens("source", "->", "destination")); + fail(); + } catch (Exception e) { + assertEquals("The destination element \"destination\" does not exist", e.getMessage()); + } + } + + @Test + void test_parse_AddsTheRelationship() { + Person user = model.addPerson("User", "Description"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + DslContext context = context(); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("source", user); + elements.register("destination", softwareSystem); + context.setIdentifierRegister(elements); + + assertEquals(0, model.getRelationships().size()); + + parser.parse(context, tokens("source", "->", "destination")); + + assertEquals(1, model.getRelationships().size()); + Relationship r = model.getRelationships().iterator().next(); + assertSame(user, r.getSource()); + assertSame(softwareSystem, r.getDestination()); + assertEquals("", r.getDescription()); + assertEquals("", r.getTechnology()); + assertEquals("Relationship", r.getTags()); + } + + @Test + void test_parse_AddsTheRelationshipWithADescription() { + Person user = model.addPerson("User", "Description"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + DslContext context = context(); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("source", user); + elements.register("destination", softwareSystem); + context.setIdentifierRegister(elements); + + assertEquals(0, model.getRelationships().size()); + + parser.parse(context, tokens("source", "->", "destination", "Uses")); + + assertEquals(1, model.getRelationships().size()); + Relationship r = model.getRelationships().iterator().next(); + assertSame(user, r.getSource()); + assertSame(softwareSystem, r.getDestination()); + assertEquals("Uses", r.getDescription()); + assertEquals("", r.getTechnology()); + assertEquals("Relationship", r.getTags()); + } + + @Test + void test_parse_AddsTheRelationshipWithADescriptionAndTechnology() { + Person user = model.addPerson("User", "Description"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + DslContext context = context(); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("source", user); + elements.register("destination", softwareSystem); + context.setIdentifierRegister(elements); + + assertEquals(0, model.getRelationships().size()); + + parser.parse(context, tokens("source", "->", "destination", "Uses", "HTTP")); + + assertEquals(1, model.getRelationships().size()); + Relationship r = model.getRelationships().iterator().next(); + assertSame(user, r.getSource()); + assertSame(softwareSystem, r.getDestination()); + assertEquals("Uses", r.getDescription()); + assertEquals("HTTP", r.getTechnology()); + } + + @Test + void test_parse_AddsTheRelationshipWithADescriptionAndTechnologyAndTags() { + Person user = model.addPerson("User", "Description"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + DslContext context = context(); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("source", user); + elements.register("destination", softwareSystem); + context.setIdentifierRegister(elements); + + assertEquals(0, model.getRelationships().size()); + + parser.parse(context, tokens("source", "->", "destination", "Uses", "HTTP", "Tag 1,Tag 2")); + + assertEquals(1, model.getRelationships().size()); + Relationship r = model.getRelationships().iterator().next(); + assertSame(user, r.getSource()); + assertSame(softwareSystem, r.getDestination()); + assertEquals("Uses", r.getDescription()); + assertEquals("HTTP", r.getTechnology()); + assertEquals("Relationship,Tag 1,Tag 2", r.getTags()); + } + + @Test + void test_parse_AddsTheRelationshipAndImplicitRelationshipsWithADescriptionAndTechnologyAndTags() { + Person user = model.addPerson("User", "Description"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + Container container = softwareSystem.addContainer("Container", "Description", "Technology"); + DslContext context = context(); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("source", user); + elements.register("destination", container); + context.setIdentifierRegister(elements); + + assertEquals(0, model.getRelationships().size()); + + parser.parse(context, tokens("source", "->", "destination", "Uses", "HTTP", "Tag 1,Tag 2")); + assertEquals(2, model.getRelationships().size()); + + // this is the relationship that was created + Relationship r = user.getEfferentRelationshipWith(container); + assertSame(user, r.getSource()); + assertSame(container, r.getDestination()); + assertEquals("Uses", r.getDescription()); + assertEquals("HTTP", r.getTechnology()); + assertEquals("Relationship,Tag 1,Tag 2", r.getTags()); + + // and this is an implied relationship + r = user.getEfferentRelationshipWith(softwareSystem); + assertSame(user, r.getSource()); + assertSame(softwareSystem, r.getDestination()); + assertEquals("Uses", r.getDescription()); + assertEquals("HTTP", r.getTechnology()); + assertEquals("", r.getTags()); + } + + @Test + void test_parse_AddsTheRelationship_WithASourceOfThis() { + Person user = model.addPerson("User"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + + GroupableElementDslContext context = new PersonDslContext(user); + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("destination", softwareSystem); + context.setIdentifierRegister(elements); + + assertEquals(0, model.getRelationships().size()); + + parser.parse(context, tokens("this", "->", "destination")); + + assertEquals(1, model.getRelationships().size()); + Relationship r = model.getRelationships().iterator().next(); + assertSame(user, r.getSource()); + assertSame(softwareSystem, r.getDestination()); + assertEquals("", r.getDescription()); + assertEquals("", r.getTechnology()); + assertEquals("Relationship", r.getTags()); + } + + @Test + void test_parse_AddsTheRelationship_WithADestinationOfThis() { + Person user = model.addPerson("User"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + + GroupableElementDslContext context = new SoftwareSystemDslContext(softwareSystem); + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("source", user); + context.setIdentifierRegister(elements); + + assertEquals(0, model.getRelationships().size()); + + parser.parse(context, tokens("source", "->", "this")); + + assertEquals(1, model.getRelationships().size()); + Relationship r = model.getRelationships().iterator().next(); + assertSame(user, r.getSource()); + assertSame(softwareSystem, r.getDestination()); + assertEquals("", r.getDescription()); + assertEquals("", r.getTechnology()); + assertEquals("Relationship", r.getTags()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ExternalScriptDslContextTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ExternalScriptDslContextTests.java new file mode 100644 index 000000000..706c9cdc6 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ExternalScriptDslContextTests.java @@ -0,0 +1,21 @@ +package com.structurizr.dsl; + +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.util.Collections; + +import static org.junit.jupiter.api.Assertions.*; + +class ExternalScriptDslContextTests extends AbstractTests { + + @Test + void test_parseExternal_RunsTheScript_WhenAValidScriptFilenameIsSpecified() { + ExternalScriptDslContext context = new ExternalScriptDslContext(new WorkspaceDslContext(), new File("src/test/resources/dsl/workspace.dsl"), "test.kts"); + context.setWorkspace(workspace); + context.end(); + + assertNotNull(workspace.getModel().getPersonWithName("Kotlin")); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/FilteredViewParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/FilteredViewParserTests.java new file mode 100644 index 000000000..5e5c5699c --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/FilteredViewParserTests.java @@ -0,0 +1,139 @@ +package com.structurizr.dsl; + +import com.structurizr.view.FilteredView; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class FilteredViewParserTests extends AbstractTests { + + private FilteredViewParser parser = new FilteredViewParser(); + + @Test + void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { + DslContext context = context(); + try { + parser.parse(context, tokens("filtered", "baseKey", "key", "mode", "tags", "description", "extra")); + fail(); + } catch (RuntimeException iae) { + assertEquals("Too many tokens, expected: filtered <baseKey> <include|exclude> <tags> [key] [description]", iae.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheBaseKeyIsMissing() { + DslContext context = context(); + try { + parser.parse(context, tokens("filtered")); + fail(); + } catch (RuntimeException iae) { + assertEquals("Expected: filtered <baseKey> <include|exclude> <tags> [key] [description]", iae.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheModeIsMissing() { + DslContext context = context(); + try { + parser.parse(context, tokens("filtered", "baseKey")); + fail(); + } catch (RuntimeException iae) { + assertEquals("Expected: filtered <baseKey> <include|exclude> <tags> [key] [description]", iae.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheTagsAreMissing() { + DslContext context = context(); + try { + parser.parse(context, tokens("filtered", "baseKey", "include")); + fail(); + } catch (RuntimeException iae) { + assertEquals("Expected: filtered <baseKey> <include|exclude> <tags> [key] [description]", iae.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheModeIsInvalid() { + DslContext context = context(); + views.createDeploymentView("deployment", "Description"); + try { + parser.parse(context, tokens("filtered", "baseKey", "mode", "Tag 1, Tag 2", "key")); + fail(); + } catch (RuntimeException iae) { + assertEquals("Filter mode should be include or exclude", iae.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheBaseViewDoesNotExist() { + DslContext context = context(); + try { + parser.parse(context, tokens("filtered", "baseKey", "include", "Tag 1, Tag 2", "key")); + fail(); + } catch (RuntimeException iae) { + assertEquals("The view \"baseKey\" does not exist", iae.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheBaseViewIsNotAStaticView() { + DslContext context = context(); + views.createDeploymentView("baseKey", "Description"); + try { + parser.parse(context, tokens("filtered", "baseKey", "include", "Tag 1, Tag 2", "key")); + fail(); + } catch (RuntimeException iae) { + assertEquals("The view \"baseKey\" must be a System Landscape, System Context, Container, or Component view", iae.getMessage()); + } + } + + @Test + void test_parse_CreatesAFilteredView() { + DslContext context = context(); + views.createSystemLandscapeView("SystemLandscape", "Description"); + parser.parse(context, tokens("filtered", "SystemLandscape", "include", "Tag 1, Tag 2")); + List<FilteredView> views = new ArrayList<>(context.getWorkspace().getViews().getFilteredViews()); + + assertEquals(1, views.size()); + assertEquals("Filtered-001", views.get(0).getKey()); + assertEquals(2, views.get(0).getTags().size()); + assertTrue(views.get(0).getTags().contains("Tag 1")); + assertTrue(views.get(0).getTags().contains("Tag 2")); + assertEquals("", views.get(0).getDescription()); + } + + @Test + void test_parse_CreatesAFilteredViewWithAKey() { + DslContext context = context(); + views.createSystemLandscapeView("SystemLandscape", "Description"); + parser.parse(context, tokens("filtered", "SystemLandscape", "include", "Tag 1, Tag 2", "key")); + List<FilteredView> views = new ArrayList<>(context.getWorkspace().getViews().getFilteredViews()); + + assertEquals(1, views.size()); + assertEquals("key", views.get(0).getKey()); + assertEquals(2, views.get(0).getTags().size()); + assertTrue(views.get(0).getTags().contains("Tag 1")); + assertTrue(views.get(0).getTags().contains("Tag 2")); + assertEquals("", views.get(0).getDescription()); + } + + @Test + void test_parse_CreatesAFilteredViewWithAKeyAndDescription() { + DslContext context = context(); + views.createSystemLandscapeView("SystemLandscape", "Description"); + parser.parse(context, tokens("filtered", "SystemLandscape", "include", "Tag 1, Tag 2", "key", "Description")); + List<FilteredView> views = new ArrayList<>(context.getWorkspace().getViews().getFilteredViews()); + + assertEquals(1, views.size()); + assertEquals("key", views.get(0).getKey()); + assertEquals(2, views.get(0).getTags().size()); + assertTrue(views.get(0).getTags().contains("Tag 1")); + assertTrue(views.get(0).getTags().contains("Tag 2")); + assertEquals("Description", views.get(0).getDescription()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/GroupParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/GroupParserTests.java new file mode 100644 index 000000000..0186144de --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/GroupParserTests.java @@ -0,0 +1,72 @@ +package com.structurizr.dsl; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class GroupParserTests extends AbstractTests { + + private GroupParser parser = new GroupParser(); + + @Test + void parse_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parse(null, tokens("group", "name", "{", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: group <name> {", e.getMessage()); + } + } + + @Test + void parse_ThrowsAnException_WhenTheNameIsMissing() { + try { + parser.parse(null, tokens("group")); + fail(); + } catch (Exception e) { + assertEquals("Expected: group <name> {", e.getMessage()); + } + } + + @Test + void parse_ThrowsAnException_WhenTheBraceIsMissing() { + try { + parser.parse(null, tokens("group", "Name", "foo")); + fail(); + } catch (Exception e) { + assertEquals("Expected: group <name> {", e.getMessage()); + } + } + + @Test + void parse() { + ElementGroup group = parser.parse(context(), tokens("group", "Group 1", "{")); + assertEquals("Group 1", group.getName()); + assertTrue(group.getElements().isEmpty()); + } + + @Test + void parse_NestedGroup_ThrowsAnExceptionWhenNestedGroupsAreNotConfigured() { + ModelDslContext context = new ModelDslContext(new ElementGroup(workspace.getModel(), "Group 1")); + context.setWorkspace(workspace); + + try { + parser.parse(context, tokens("group", "Group 2", "{")); + fail(); + } catch (Exception e) { + assertEquals("To use nested groups, please define a model property named structurizr.groupSeparator", e.getMessage()); + } + } + + @Test + void parse_NestedGroup() { + workspace.getModel().addProperty("structurizr.groupSeparator", "/"); + ModelDslContext context = new ModelDslContext(new ElementGroup(workspace.getModel(), "Group 1")); + context.setWorkspace(workspace); + + ElementGroup group = parser.parse(context, tokens("group", "Group 2", "{")); + assertEquals("Group 1/Group 2", group.getName()); + assertTrue(group.getElements().isEmpty()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/HealthCheckParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/HealthCheckParserTests.java new file mode 100644 index 000000000..9ab9c60b4 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/HealthCheckParserTests.java @@ -0,0 +1,127 @@ +package com.structurizr.dsl; + +import com.structurizr.model.DeploymentNode; +import com.structurizr.model.HttpHealthCheck; +import com.structurizr.model.SoftwareSystem; +import com.structurizr.model.SoftwareSystemInstance; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +class HealthCheckParserTests extends AbstractTests { + + private HealthCheckParser parser = new HealthCheckParser(); + + private SoftwareSystemInstance softwareSystemInstance; + + @BeforeEach + void setUp() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Name"); + DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node"); + softwareSystemInstance = deploymentNode.add(softwareSystem); + } + + @Test + void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + SoftwareSystemInstanceDslContext context = new SoftwareSystemInstanceDslContext(softwareSystemInstance); + parser.parse(context, tokens("healthCheck", "name", "url", "interval", "timeout", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: healthCheck <name> <url> [interval] [timeout]", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenNoNameIsSpecified() { + try { + SoftwareSystemInstanceDslContext context = new SoftwareSystemInstanceDslContext(softwareSystemInstance); + parser.parse(context, tokens("healthCheck")); + fail(); + } catch (Exception e) { + assertEquals("Expected: healthCheck <name> <url> [interval] [timeout]", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenNoUrlIsSpecified() { + try { + SoftwareSystemInstanceDslContext context = new SoftwareSystemInstanceDslContext(softwareSystemInstance); + parser.parse(context, tokens("healthCheck", "Name")); + fail(); + } catch (Exception e) { + assertEquals("Expected: healthCheck <name> <url> [interval] [timeout]", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenAnInvalidIntervalIsSpecified() { + try { + SoftwareSystemInstanceDslContext context = new SoftwareSystemInstanceDslContext(softwareSystemInstance); + parser.parse(context, tokens("healthCheck", "Name", "https://example.com/health", "hello")); + fail(); + } catch (Exception e) { + assertEquals("The interval of \"hello\" is not valid - it must be a positive integer (number of seconds)", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenANegativeIntervalIsSpecified() { + try { + SoftwareSystemInstanceDslContext context = new SoftwareSystemInstanceDslContext(softwareSystemInstance); + parser.parse(context, tokens("healthCheck", "Name", "https://example.com/health", "-1")); + fail(); + } catch (Exception e) { + assertEquals("The interval must be a positive integer (number of seconds)", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenAnInvalidTimeoutIsSpecified() { + try { + SoftwareSystemInstanceDslContext context = new SoftwareSystemInstanceDslContext(softwareSystemInstance); + parser.parse(context, tokens("healthCheck", "Name", "https://example.com/health", "60", "hello")); + fail(); + } catch (Exception e) { + assertEquals("The timeout of \"hello\" is not valid - it must be zero or a positive integer (number of milliseconds)", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenANegativeTimeoutIsSpecified() { + try { + SoftwareSystemInstanceDslContext context = new SoftwareSystemInstanceDslContext(softwareSystemInstance); + parser.parse(context, tokens("healthCheck", "Name", "https://example.com/health", "60", "-1")); + fail(); + } catch (Exception e) { + assertEquals("The timeout must be zero or a positive integer (number of milliseconds)", e.getMessage()); + } + } + + @Test + void test_parse_AddsAHealthCheck_WhenTheNameAndUrlAreSpecified() { + SoftwareSystemInstanceDslContext context = new SoftwareSystemInstanceDslContext(softwareSystemInstance); + parser.parse(context, tokens("healthCheck", "Name", "https://example.com/health")); + + HttpHealthCheck healthCheck = softwareSystemInstance.getHealthChecks().iterator().next(); + assertEquals("Name", healthCheck.getName()); + assertEquals("https://example.com/health", healthCheck.getUrl()); + assertEquals(60, healthCheck.getInterval()); + assertEquals(0, healthCheck.getTimeout()); + } + + @Test + void test_parse_AddsAHealthCheck_WhenAllPropertiesAreSpecified() { + SoftwareSystemInstanceDslContext context = new SoftwareSystemInstanceDslContext(softwareSystemInstance); + parser.parse(context, tokens("healthCheck", "Name", "https://example.com/health", "120", "2000")); + + HttpHealthCheck healthCheck = softwareSystemInstance.getHealthChecks().iterator().next(); + assertEquals("Name", healthCheck.getName()); + assertEquals("https://example.com/health", healthCheck.getUrl()); + assertEquals(120, healthCheck.getInterval()); + assertEquals(2000, healthCheck.getTimeout()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/IdentifierRegisterTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/IdentifierRegisterTests.java new file mode 100644 index 000000000..15936e9ee --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/IdentifierRegisterTests.java @@ -0,0 +1,63 @@ +package com.structurizr.dsl; + +import com.structurizr.model.SoftwareSystem; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +class IdentifierRegisterTests extends AbstractTests { + + private final IdentifiersRegister register = new IdentifiersRegister(); + + @Test + void test_validateIdentifierName() { + new IdentifiersRegister().validateIdentifierName("a"); + new IdentifiersRegister().validateIdentifierName("abc"); + new IdentifiersRegister().validateIdentifierName("ABC"); + new IdentifiersRegister().validateIdentifierName("softwaresystem"); + new IdentifiersRegister().validateIdentifierName("SoftwareSystem"); + new IdentifiersRegister().validateIdentifierName("123456"); + new IdentifiersRegister().validateIdentifierName("_softwareSystem"); + new IdentifiersRegister().validateIdentifierName("SoftwareSystem-1"); + + try { + new IdentifiersRegister().validateIdentifierName("-softwareSystem"); + fail(); + } catch (Exception e) { + assertEquals("Identifiers cannot start with a - character", e.getMessage()); + } + + try { + new IdentifiersRegister().validateIdentifierName("SoftwareSystém"); + fail(); + } catch (Exception e) { + assertEquals("Identifiers can only contain the following characters: a-zA-Z0-9_-", e.getMessage()); + } + } + + @Test + void test_register_ThrowsAnException_WhenTheElementHasAlreadyBeenRegisteredWithADifferentIdentifier() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + try { + register.register("a", softwareSystem); + register.register("x", softwareSystem); + fail(); + } catch (Exception e) { + assertEquals("The element is already registered with an identifier of \"a\"", e.getMessage()); + } + } + + @Test + void test_register_ThrowsAnException_WhenTheElementHasAlreadyBeenRegisteredWithAnInternalIdentifier() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + try { + register.register("", softwareSystem); + register.register("x", softwareSystem); + fail(); + } catch (Exception e) { + assertEquals("Please assign an identifier to \"SoftwareSystem://Software System\" before using it with !ref", e.getMessage()); + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/IdentifierScopeParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/IdentifierScopeParserTests.java new file mode 100644 index 000000000..7ce861a1a --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/IdentifierScopeParserTests.java @@ -0,0 +1,46 @@ +package com.structurizr.dsl; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +class IdentifierScopeParserTests extends AbstractTests { + + private IdentifierScopeParser parser = new IdentifierScopeParser(); + + @Test + void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parse(context(), tokens("!identifiers", "hierarchical", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: !identifiers <flat|hierarchical>", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenNoScopeIsSpecified() { + try { + parser.parse(context(), tokens("!identifiers")); + fail(); + } catch (Exception e) { + assertEquals("Expected: !identifiers <flat|hierarchical>", e.getMessage()); + } + } + + @Test + void test_parse_SetsTheScope_WhenLocalIsSpecified() { + IdentifierScope scope = parser.parse(context(), tokens("!identifiers", "hierarchical")); + + assertEquals(IdentifierScope.Hierarchical, scope); + } + + @Test + void test_parse_SetsTheScope_WhenGlobalIsSpecified() { + IdentifierScope scope = parser.parse(context(), tokens("!identifiers", "flat")); + + assertEquals(IdentifierScope.Flat, scope); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewParserTests.java new file mode 100644 index 000000000..fb2260f0a --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewParserTests.java @@ -0,0 +1,62 @@ +package com.structurizr.dsl; + +import com.structurizr.model.SoftwareSystem; +import com.structurizr.view.ImageView; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class ImageViewParserTests extends AbstractTests { + + private final ImageViewParser parser = new ImageViewParser(); + + @Test + void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { + DslContext context = context(); + try { + parser.parse(context, tokens("image", "*", "key", "extra")); + fail(); + } catch (RuntimeException iae) { + assertEquals("Too many tokens, expected: image <*|element identifier> [key] {", iae.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheElementDoesNotExist() { + DslContext context = context(); + try { + parser.parse(context, tokens("image", "element", "key")); + fail(); + } catch (RuntimeException iae) { + assertEquals("The element \"element\" does not exist", iae.getMessage()); + } + } + + @Test + void test_parse_CreatesAnImageView() { + DslContext context = context(); + parser.parse(context, tokens("image", "*", "key")); + + ImageView imageView = (ImageView)context.getWorkspace().getViews().getViewWithKey("key"); + assertEquals("key", imageView.getKey()); + assertNull(imageView.getElement()); + assertNull(imageView.getElementId()); + } + + + @Test + void test_parse_CreatesAnImageViewForAnElement() { + DslContext context = context(); + IdentifiersRegister elements = new IdentifiersRegister(); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); + elements.register("softwaresystem", softwareSystem); + context.setIdentifierRegister(elements); + parser.parse(context, tokens("image", "softwaresystem", "key")); + + ImageView imageView = (ImageView)context.getWorkspace().getViews().getViewWithKey("key"); + assertEquals("key", imageView.getKey()); + assertSame(softwareSystem, imageView.getElement()); + assertEquals(softwareSystem.getId(), imageView.getElementId()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ImplicitRelationshipParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ImplicitRelationshipParserTests.java new file mode 100644 index 000000000..25f7d194b --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ImplicitRelationshipParserTests.java @@ -0,0 +1,179 @@ +package com.structurizr.dsl; + +import com.structurizr.model.*; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class ImplicitRelationshipParserTests extends AbstractTests { + + private ImplicitRelationshipParser parser = new ImplicitRelationshipParser(); + + private ModelItemDslContext context(Person person) { + ModelItemDslContext context = new PersonDslContext(person); + context.setWorkspace(workspace); + model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); + + return context; + } + + @Test + void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parse(context(null), tokens("->", "destination", "description", "technology", "tags", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: -> <identifier> [description] [technology] [tags]", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheDestinationIdentifierIsMissing() { + try { + parser.parse(context(null), tokens("->")); + fail(); + } catch (Exception e) { + assertEquals("Expected: -> <identifier> [description] [technology] [tags]", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheDestinationElementIsNotDefined() { + Person user = model.addPerson("User", "Description"); + ModelItemDslContext context = context(user); + IdentifiersRegister elements = new IdentifiersRegister(); + context.setIdentifierRegister(elements); + + try { + parser.parse(context, tokens("->", "destination")); + fail(); + } catch (Exception e) { + assertEquals("The destination element \"destination\" does not exist", e.getMessage()); + } + } + + @Test + void test_parse_AddsTheRelationship() { + Person user = model.addPerson("User", "Description"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + ModelItemDslContext context = context(user); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("destination", softwareSystem); + context.setIdentifierRegister(elements); + + assertEquals(0, model.getRelationships().size()); + + parser.parse(context, tokens("->", "destination")); + + assertEquals(1, model.getRelationships().size()); + Relationship r = model.getRelationships().iterator().next(); + assertSame(user, r.getSource()); + assertSame(softwareSystem, r.getDestination()); + assertEquals("", r.getDescription()); + assertEquals("", r.getTechnology()); + assertEquals("Relationship", r.getTags()); + } + + @Test + void test_parse_AddsTheRelationshipWithADescription() { + Person user = model.addPerson("User", "Description"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + ModelItemDslContext context = context(user); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("destination", softwareSystem); + context.setIdentifierRegister(elements); + + assertEquals(0, model.getRelationships().size()); + + parser.parse(context, tokens("->", "destination", "Uses")); + + assertEquals(1, model.getRelationships().size()); + Relationship r = model.getRelationships().iterator().next(); + assertSame(user, r.getSource()); + assertSame(softwareSystem, r.getDestination()); + assertEquals("Uses", r.getDescription()); + assertEquals("", r.getTechnology()); + assertEquals("Relationship", r.getTags()); + } + + @Test + void test_parse_AddsTheRelationshipWithADescriptionAndTechnology() { + Person user = model.addPerson("User", "Description"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + ModelItemDslContext context = context(user); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("destination", softwareSystem); + context.setIdentifierRegister(elements); + + assertEquals(0, model.getRelationships().size()); + + parser.parse(context, tokens("->", "destination", "Uses", "HTTP")); + + assertEquals(1, model.getRelationships().size()); + Relationship r = model.getRelationships().iterator().next(); + assertSame(user, r.getSource()); + assertSame(softwareSystem, r.getDestination()); + assertEquals("Uses", r.getDescription()); + assertEquals("HTTP", r.getTechnology()); + } + + @Test + void test_parse_AddsTheRelationshipWithADescriptionAndTechnologyAndTags() { + Person user = model.addPerson("User", "Description"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + ModelItemDslContext context = context(user); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("destination", softwareSystem); + context.setIdentifierRegister(elements); + + assertEquals(0, model.getRelationships().size()); + + parser.parse(context, tokens("->", "destination", "Uses", "HTTP", "Tag 1,Tag 2")); + + assertEquals(1, model.getRelationships().size()); + Relationship r = model.getRelationships().iterator().next(); + assertSame(user, r.getSource()); + assertSame(softwareSystem, r.getDestination()); + assertEquals("Uses", r.getDescription()); + assertEquals("HTTP", r.getTechnology()); + assertEquals("Relationship,Tag 1,Tag 2", r.getTags()); + } + + @Test + void test_parse_AddsTheRelationshipAndImplicitRelationshipsWithADescriptionAndTechnologyAndTags() { + Person user = model.addPerson("User", "Description"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + Container container = softwareSystem.addContainer("Container", "Description", "Technology"); + ModelItemDslContext context = context(user); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("destination", container); + context.setIdentifierRegister(elements); + + assertEquals(0, model.getRelationships().size()); + + parser.parse(context, tokens("->", "destination", "Uses", "HTTP", "Tag 1,Tag 2")); + assertEquals(2, model.getRelationships().size()); + + // this is the relationship that was created + Relationship r = user.getEfferentRelationshipWith(container); + assertSame(user, r.getSource()); + assertSame(container, r.getDestination()); + assertEquals("Uses", r.getDescription()); + assertEquals("HTTP", r.getTechnology()); + assertEquals("Relationship,Tag 1,Tag 2", r.getTags()); + + // and this is an implied relationship + r = user.getEfferentRelationshipWith(softwareSystem); + assertSame(user, r.getSource()); + assertSame(softwareSystem, r.getDestination()); + assertEquals("Uses", r.getDescription()); + assertEquals("HTTP", r.getTechnology()); + assertEquals("", r.getTags()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ImpliedRelationshipsParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ImpliedRelationshipsParserTests.java new file mode 100644 index 000000000..a8e4e8585 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ImpliedRelationshipsParserTests.java @@ -0,0 +1,46 @@ +package com.structurizr.dsl; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +class ImpliedRelationshipsParserTests extends AbstractTests { + + private ImpliedRelationshipsParser parser = new ImpliedRelationshipsParser(); + + @Test + void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parse(context(), tokens("!impliedRelationships", "boolean", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: !impliedRelationships <true|false>", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenNoFlagIsSpecified() { + try { + parser.parse(context(), tokens("!impliedRelationships")); + fail(); + } catch (Exception e) { + assertEquals("Expected: !impliedRelationships <true|false>", e.getMessage()); + } + } + + @Test + void test_parse_SetsTheStrategy_WhenFalseIsSpecified() { + parser.parse(context(), tokens("!impliedRelationships", "false")); + + assertEquals("com.structurizr.model.DefaultImpliedRelationshipsStrategy", workspace.getModel().getImpliedRelationshipsStrategy().getClass().getCanonicalName()); + } + + @Test + void test_parse_SetsTheStrategy_WhenTrueIsSpecified() { + parser.parse(context(), tokens("!impliedRelationships", "true")); + + assertEquals("com.structurizr.model.CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy", workspace.getModel().getImpliedRelationshipsStrategy().getClass().getCanonicalName()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/IncludeParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/IncludeParserTests.java new file mode 100644 index 000000000..cc5cad518 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/IncludeParserTests.java @@ -0,0 +1,32 @@ +package com.structurizr.dsl; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +class IncludeParserTests extends AbstractTests { + + private IncludeParser parser = new IncludeParser(); + + @Test + void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parse(new IncludedDslContext(null), tokens("!include", "file", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: !include <file|directory|url>", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenAFileIsNotSpecified() { + try { + parser.parse(new IncludedDslContext(null), tokens("!include")); + fail(); + } catch (Exception e) { + assertEquals("Expected: !include <file|directory|url>", e.getMessage()); + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/InfrastructureNodeParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/InfrastructureNodeParserTests.java new file mode 100644 index 000000000..9c0aa769c --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/InfrastructureNodeParserTests.java @@ -0,0 +1,134 @@ +package com.structurizr.dsl; + +import com.structurizr.model.DeploymentNode; +import com.structurizr.model.InfrastructureNode; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class InfrastructureNodeParserTests extends AbstractTests { + + private InfrastructureNodeParser parser = new InfrastructureNodeParser(); + + @Test + void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parse(new DeploymentNodeDslContext(null), tokens("infrastructureNode", "name", "description", "technology", "tags", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: infrastructureNode <name> [description] [technology] [tags]", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheNameIsNotSpecified() { + try { + parser.parse(new DeploymentNodeDslContext(null), tokens("infrastructureNode")); + fail(); + } catch (Exception e) { + assertEquals("Expected: infrastructureNode <name> [description] [technology] [tags]", e.getMessage()); + } + } + + @Test + void test_parse_CreatesAnInfrastructureNode() { + DeploymentNode deploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + DeploymentNodeDslContext context = new DeploymentNodeDslContext(deploymentNode); + + parser.parse(context, tokens("infrastructureNode", "Name")); + + assertEquals(2, model.getElements().size()); + assertEquals(1, deploymentNode.getInfrastructureNodes().size()); + InfrastructureNode infrastructureNode = deploymentNode.getInfrastructureNodeWithName("Name"); + assertNotNull(infrastructureNode); + assertEquals("", infrastructureNode.getDescription()); + assertEquals("", infrastructureNode.getTechnology()); + assertEquals("Element,Infrastructure Node", infrastructureNode.getTags()); + assertEquals("Live", infrastructureNode.getEnvironment()); + } + + @Test + void test_parse_CreatesAnInfrastructureNodeWithADescription() { + DeploymentNode deploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + DeploymentNodeDslContext context = new DeploymentNodeDslContext(deploymentNode); + + parser.parse(context, tokens("infrastructureNode", "Name", "Description")); + + assertEquals(2, model.getElements().size()); + assertEquals(1, deploymentNode.getInfrastructureNodes().size()); + InfrastructureNode infrastructureNode = deploymentNode.getInfrastructureNodeWithName("Name"); + assertNotNull(infrastructureNode); + assertEquals("Description", infrastructureNode.getDescription()); + assertEquals("", infrastructureNode.getTechnology()); + assertEquals("Element,Infrastructure Node", infrastructureNode.getTags()); + assertEquals("Live", infrastructureNode.getEnvironment()); + } + + @Test + void test_parse_CreatesAnInfrastructureNodeWithADescriptionAndTechnology() { + DeploymentNode deploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + DeploymentNodeDslContext context = new DeploymentNodeDslContext(deploymentNode); + + parser.parse(context, tokens("infrastructureNode", "Name", "Description", "Technology")); + + assertEquals(2, model.getElements().size()); + assertEquals(1, deploymentNode.getInfrastructureNodes().size()); + InfrastructureNode infrastructureNode = deploymentNode.getInfrastructureNodeWithName("Name"); + assertNotNull(infrastructureNode); + assertEquals("Description", infrastructureNode.getDescription()); + assertEquals("Technology", infrastructureNode.getTechnology()); + assertEquals("Element,Infrastructure Node", infrastructureNode.getTags()); + assertEquals("Live", infrastructureNode.getEnvironment()); + } + + @Test + void test_parse_CreatesAnInfrastructureNodeWithADescriptionAndTechnologyAndTags() { + DeploymentNode deploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + DeploymentNodeDslContext context = new DeploymentNodeDslContext(deploymentNode); + + parser.parse(context, tokens("infrastructureNode", "Name", "Description", "Technology", "Tag 1, Tag 2")); + + assertEquals(2, model.getElements().size()); + assertEquals(1, deploymentNode.getInfrastructureNodes().size()); + InfrastructureNode infrastructureNode = deploymentNode.getInfrastructureNodeWithName("Name"); + assertNotNull(infrastructureNode); + assertEquals("Description", infrastructureNode.getDescription()); + assertEquals("Technology", infrastructureNode.getTechnology()); + assertEquals("Element,Infrastructure Node,Tag 1,Tag 2", infrastructureNode.getTags()); + assertEquals("Live", infrastructureNode.getEnvironment()); + } + + @Test + void test_parseTechnology_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + InfrastructureNode infrastructureNode = model.addDeploymentNode("Deployment Node").addInfrastructureNode("Infrastructure Node"); + InfrastructureNodeDslContext context = new InfrastructureNodeDslContext(infrastructureNode); + parser.parseTechnology(context, tokens("technology", "technology", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: technology <technology>", e.getMessage()); + } + } + + @Test + void test_parseTechnology_ThrowsAnException_WhenNoDescriptionIsSpecified() { + try { + InfrastructureNode infrastructureNode = model.addDeploymentNode("Deployment Node").addInfrastructureNode("Infrastructure Node"); + InfrastructureNodeDslContext context = new InfrastructureNodeDslContext(infrastructureNode); + parser.parseTechnology(context, tokens("technology")); + fail(); + } catch (Exception e) { + assertEquals("Expected: technology <technology>", e.getMessage()); + } + } + + @Test + void test_parseTechnology_SetsTheDescription_WhenADescriptionIsSpecified() { + InfrastructureNode infrastructureNode = model.addDeploymentNode("Deployment Node").addInfrastructureNode("Infrastructure Node"); + InfrastructureNodeDslContext context = new InfrastructureNodeDslContext(infrastructureNode); + parser.parseTechnology(context, tokens("technology", "Technology")); + + assertEquals("Technology", infrastructureNode.getTechnology()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/InlineScriptDslContextTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/InlineScriptDslContextTests.java new file mode 100644 index 000000000..6b2775318 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/InlineScriptDslContextTests.java @@ -0,0 +1,23 @@ +package com.structurizr.dsl; + +import org.junit.jupiter.api.Test; + +import java.io.File; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +class InlineScriptDslContextTests extends AbstractTests { + + @Test + void test_end_ThrowsAnException_WhenAnUnsupportedLanguageIsSpecified() { + try { + InlineScriptDslContext context = new InlineScriptDslContext(new WorkspaceDslContext(), new File("workspace.dsl"), "java"); + context.end(); + fail(); + } catch (Exception e) { + assertEquals("Error running inline script, caused by java.lang.RuntimeException: Unsupported scripting language \"java\"", e.getMessage()); + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ModelDslContextTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ModelDslContextTests.java new file mode 100644 index 000000000..08e976738 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ModelDslContextTests.java @@ -0,0 +1,77 @@ +package com.structurizr.dsl; + +import com.structurizr.model.Enterprise; +import com.structurizr.model.Location; +import com.structurizr.model.Person; +import com.structurizr.model.SoftwareSystem; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class ModelDslContextTests extends AbstractTests { + + @Test + void end_DoesNothing_WhenNoPeopleAreMarkedAsInternal() { + ModelDslContext context = new ModelDslContext(); + context.setWorkspace(workspace); + + Person user1 = workspace.getModel().addPerson("Name 1"); + Person user2 = workspace.getModel().addPerson("Name 2"); + assertEquals(Location.Unspecified, user1.getLocation()); + assertEquals(Location.Unspecified, user2.getLocation()); + + context.end(); + assertEquals(Location.Unspecified, user1.getLocation()); + assertEquals(Location.Unspecified, user2.getLocation()); + } + + @Test + void end_MarksAllOtherPeopleAsExternal_WhenSomePeopleAreMarkedAsInternal() { + ModelDslContext context = new ModelDslContext(); + context.setWorkspace(workspace); + workspace.getModel().setEnterprise(new Enterprise("Name")); + + Person user1 = workspace.getModel().addPerson("Name 1"); + Person user2 = workspace.getModel().addPerson("Name 2"); + user2.setLocation(Location.Internal); + assertEquals(Location.Unspecified, user1.getLocation()); + assertEquals(Location.Internal, user2.getLocation()); + + context.end(); + assertEquals(Location.External, user1.getLocation()); + assertEquals(Location.Internal, user2.getLocation()); + } + + @Test + void end_DoesNothing_WhenNoSoftwareSystemsAreMarkedAsInternal() { + ModelDslContext context = new ModelDslContext(); + context.setWorkspace(workspace); + + SoftwareSystem softwareSystem1 = workspace.getModel().addSoftwareSystem("Name 1"); + SoftwareSystem softwareSystem2 = workspace.getModel().addSoftwareSystem("Name 2"); + assertEquals(Location.Unspecified, softwareSystem1.getLocation()); + assertEquals(Location.Unspecified, softwareSystem2.getLocation()); + + context.end(); + assertEquals(Location.Unspecified, softwareSystem1.getLocation()); + assertEquals(Location.Unspecified, softwareSystem2.getLocation()); + } + + @Test + void end_MarksAllOtherSoftwareSystemsAsExternal_WhenSomeSoftwareSystemsAreMarkedAsInternal() { + ModelDslContext context = new ModelDslContext(); + context.setWorkspace(workspace); + workspace.getModel().setEnterprise(new Enterprise("Name")); + + SoftwareSystem softwareSystem1 = workspace.getModel().addSoftwareSystem("Name 1"); + SoftwareSystem softwareSystem2 = workspace.getModel().addSoftwareSystem("Name 2"); + softwareSystem1.setLocation(Location.Internal); + assertEquals(Location.Internal, softwareSystem1.getLocation()); + assertEquals(Location.Unspecified, softwareSystem2.getLocation()); + + context.end(); + assertEquals(Location.Internal, softwareSystem1.getLocation()); + assertEquals(Location.External, softwareSystem2.getLocation()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ModelItemParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ModelItemParserTests.java new file mode 100644 index 000000000..379ce8e29 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ModelItemParserTests.java @@ -0,0 +1,165 @@ +package com.structurizr.dsl; + +import com.structurizr.model.Perspective; +import com.structurizr.model.SoftwareSystem; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class ModelItemParserTests extends AbstractTests { + + private ModelItemParser parser = new ModelItemParser(); + + @Test + void test_parseTags_ThrowsAnException_WhenNoTagsAreSpecified() { + try { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); + ModelItemDslContext context = new SoftwareSystemDslContext(softwareSystem); + parser.parseTags(context, tokens("tags")); + fail(); + } catch (Exception e) { + assertEquals("Expected: tags <tags> [tags]", e.getMessage()); + } + } + + @Test + void test_parseTags_AddsTheTags_WhenTagsAreSpecified() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); + ModelItemDslContext context = new SoftwareSystemDslContext(softwareSystem); + parser.parseTags(context, tokens("tags", "Tag 1")); + assertEquals(3, softwareSystem.getTagsAsSet().size()); + assertTrue(softwareSystem.getTagsAsSet().contains("Tag 1")); + + parser.parseTags(context, tokens("tags", "Tag 1, Tag 2, Tag 3")); + assertEquals(5, softwareSystem.getTagsAsSet().size()); + assertTrue(softwareSystem.getTagsAsSet().contains("Tag 2")); + assertTrue(softwareSystem.getTagsAsSet().contains("Tag 3")); + + parser.parseTags(context, tokens("tags", "Tag 3", "Tag 4", "Tag 5")); + assertEquals(7, softwareSystem.getTagsAsSet().size()); + assertTrue(softwareSystem.getTagsAsSet().contains("Tag 4")); + assertTrue(softwareSystem.getTagsAsSet().contains("Tag 5")); + } + + @Test + void test_parseDescription_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + ModelItemDslContext context = new SoftwareSystemDslContext(null); + parser.parseDescription(context, tokens("description", "description", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: description <description>", e.getMessage()); + } + } + + @Test + void test_parseDescription_ThrowsAnException_WhenNoDescriptionIsSpecified() { + try { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); + ModelItemDslContext context = new SoftwareSystemDslContext(softwareSystem); + parser.parseDescription(context, tokens("description")); + fail(); + } catch (Exception e) { + assertEquals("Expected: description <description>", e.getMessage()); + } + } + + @Test + void test_parseDescription_SetsTheDescription_WhenADescriptionIsSpecified() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", ""); + ModelItemDslContext context = new SoftwareSystemDslContext(softwareSystem); + parser.parseDescription(context, tokens("description", "Description")); + + assertEquals("Description", softwareSystem.getDescription()); + } + + @Test + void test_parseUrl_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + ModelItemDslContext context = new SoftwareSystemDslContext(null); + parser.parseUrl(context, tokens("url", "url", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: url <url>", e.getMessage()); + } + } + + @Test + void test_parseUrl_ThrowsAnException_WhenNoUrlIsSpecified() { + try { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); + ModelItemDslContext context = new SoftwareSystemDslContext(softwareSystem); + parser.parseUrl(context, tokens("url")); + fail(); + } catch (Exception e) { + assertEquals("Expected: url <url>", e.getMessage()); + } + } + + @Test + void test_parseUrl_SetsTheUrl_WhenAUrlIsSpecified() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); + ModelItemDslContext context = new SoftwareSystemDslContext(softwareSystem); + parser.parseUrl(context, tokens("url", "http://example.com")); + + assertEquals("http://example.com", softwareSystem.getUrl()); + } + + @Test + void test_parsePerspective_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + ModelItemPerspectivesDslContext context = new ModelItemPerspectivesDslContext(null); + parser.parsePerspective(context, tokens("name", "description", "value", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: <name> <description> [value]", e.getMessage()); + } + } + + @Test + void test_parsePerspective_ThrowsAnException_WhenNoNameIsSpecified() { + try { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); + ModelItemPerspectivesDslContext context = new ModelItemPerspectivesDslContext(softwareSystem); + parser.parsePerspective(context, tokens()); + fail(); + } catch (Exception e) { + assertEquals("Expected: <name> <description> [value]", e.getMessage()); + } + } + + @Test + void test_parsePerspective_ThrowsAnException_WhenNoDescriptionIsSpecified() { + try { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); + ModelItemPerspectivesDslContext context = new ModelItemPerspectivesDslContext(softwareSystem); + parser.parsePerspective(context, tokens("name")); + fail(); + } catch (Exception e) { + assertEquals("Expected: <name> <description> [value]", e.getMessage()); + } + } + + @Test + void test_parsePerspective_AddsThePerspective_WhenADescriptionIsSpecified() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); + ModelItemPerspectivesDslContext context = new ModelItemPerspectivesDslContext(softwareSystem); + parser.parsePerspective(context, tokens("Security", "Description")); + + Perspective perspective = softwareSystem.getPerspectives().stream().filter(p -> p.getName().equals("Security")).findFirst().get(); + assertEquals("Description", perspective.getDescription()); + assertEquals("", perspective.getValue()); + } + + @Test + void test_parsePerspective_AddsThePerspective_WhenADescriptionAndValueIsSpecified() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); + ModelItemPerspectivesDslContext context = new ModelItemPerspectivesDslContext(softwareSystem); + parser.parsePerspective(context, tokens("Security", "Description", "Value")); + + Perspective perspective = softwareSystem.getPerspectives().stream().filter(p -> p.getName().equals("Security")).findFirst().get(); + assertEquals("Description", perspective.getDescription()); + assertEquals("Value", perspective.getValue()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/PersonParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/PersonParserTests.java new file mode 100644 index 000000000..f24c1c20d --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/PersonParserTests.java @@ -0,0 +1,83 @@ +package com.structurizr.dsl; + +import com.structurizr.model.Location; +import com.structurizr.model.Person; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class PersonParserTests extends AbstractTests { + + private PersonParser parser = new PersonParser(); + + @Test + void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parse(context(), tokens("person", "name", "description", "tags", "tokens")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: person <name> [description] [tags]", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheNameIsNotSpecified() { + try { + parser.parse(context(), tokens("person")); + fail(); + } catch (Exception e) { + assertEquals("Expected: person <name> [description] [tags]", e.getMessage()); + } + } + + @Test + void test_parse_CreatesAPerson() { + parser.parse(context(), tokens("person", "User")); + + assertEquals(1, model.getElements().size()); + Person user = model.getPersonWithName("User"); + assertNotNull(user); + assertEquals("", user.getDescription()); + assertEquals(Location.Unspecified, user.getLocation()); + assertEquals("Element,Person", user.getTags()); + } + + @Test + void test_parse_CreatesAPersonWithADescription() { + parser.parse(context(), tokens("person", "User", "Description")); + + assertEquals(1, model.getElements().size()); + Person user = model.getPersonWithName("User"); + assertNotNull(user); + assertEquals("Description", user.getDescription()); + assertEquals(Location.Unspecified, user.getLocation()); + assertEquals("Element,Person", user.getTags()); + } + + @Test + void test_parse_CreatesAPersonWithADescriptionAndTags() { + parser.parse(context(), tokens("person", "User", "Description", "Tag 1, Tag 2")); + + assertEquals(1, model.getElements().size()); + Person user = model.getPersonWithName("User"); + assertNotNull(user); + assertEquals("Description", user.getDescription()); + assertEquals(Location.Unspecified, user.getLocation()); + assertEquals("Element,Person,Tag 1,Tag 2", user.getTags()); + } + + @Test + void test_parse_CreatesAnInternalPerson() { + EnterpriseDslContext context = new EnterpriseDslContext(); + context.setWorkspace(workspace); + parser.parse(context, tokens("person", "User")); + + assertEquals(1, model.getElements().size()); + Person user = model.getPersonWithName("User"); + assertNotNull(user); + assertEquals("", user.getDescription()); + assertEquals(Location.Internal, user.getLocation()); + assertEquals("Element,Person", user.getTags()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/PluginDslContextTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/PluginDslContextTests.java new file mode 100644 index 000000000..90900c706 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/PluginDslContextTests.java @@ -0,0 +1,23 @@ +package com.structurizr.dsl; + +import org.junit.jupiter.api.Test; + +import java.io.File; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +class PluginDslContextTests extends AbstractTests { + + @Test + void test_end_ThrowsAnException_WhenThePluginClassDoesNotExist() { + try { + PluginDslContext context = new PluginDslContext("com.structurizr.TestPlugin", new File("src/test/dsl"), null); + context.end(); + fail(); + } catch (Exception e) { + assertEquals("Error running plugin com.structurizr.TestPlugin, caused by java.lang.ClassNotFoundException: com.structurizr.TestPlugin", e.getMessage()); + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/PluginParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/PluginParserTests.java new file mode 100644 index 000000000..c55de300a --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/PluginParserTests.java @@ -0,0 +1,38 @@ +package com.structurizr.dsl; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +class PluginParserTests extends AbstractTests { + + private PluginParser parser = new PluginParser(); + + @Test + void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parse(context(), tokens("!plugin", "com.example.ClassName", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: !plugin <fqn>", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenNoFullyQualifiedNameIsSpecified() { + try { + parser.parse(context(), tokens("!plugin")); + fail(); + } catch (Exception e) { + assertEquals("Expected: !plugin <fqn>", e.getMessage()); + } + } + + @Test + void test_parse_ReturnsTheFullyQualifiedClassName_WhenAValidPluginIsSpecified() { + String fqcn = parser.parse(context(), tokens("!plugin", "com.example.ExampleStructurizrDslPlugin")); + assertEquals("com.example.ExampleStructurizrDslPlugin", fqcn); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/PropertyParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/PropertyParserTests.java new file mode 100644 index 000000000..33101711c --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/PropertyParserTests.java @@ -0,0 +1,31 @@ +package com.structurizr.dsl; + +import com.structurizr.model.SoftwareSystem; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class PropertyParserTests extends AbstractTests { + + @Test + void test_parseProperty_ThrowsAnException_WhenNoValueIsSpecified() { + try { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); + PropertiesDslContext context = new PropertiesDslContext(softwareSystem); + new PropertyParser().parse(context, tokens("name")); + fail(); + } catch (Exception e) { + assertEquals("Expected: <name> <value>", e.getMessage()); + } + } + + @Test + void test_parseProperty_AddsTheProperty_WhenAValueIsSpecified() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); + PropertiesDslContext context = new PropertiesDslContext(softwareSystem); + new PropertyParser().parse(context, tokens("name", "value")); + + assertEquals("value", softwareSystem.getProperties().get("name")); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/RefParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/RefParserTests.java new file mode 100644 index 000000000..10e0cf947 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/RefParserTests.java @@ -0,0 +1,79 @@ +package com.structurizr.dsl; + +import com.structurizr.model.ModelItem; +import com.structurizr.model.Person; +import com.structurizr.model.Relationship; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class RefParserTests extends AbstractTests { + + private RefParser parser = new RefParser(); + + @Test + void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parse(context(), tokens("!ref", "name", "tokens")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: !ref <identifier|canonical name>", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheIdentifierOrCanonicalNameIsNotSpecified() { + try { + parser.parse(context(), tokens("!extend")); + fail(); + } catch (Exception e) { + assertEquals("Expected: !extend <identifier|canonical name>", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheReferencedElementCannotBeFound() { + try { + parser.parse(context(), tokens("!ref", "Person://User")); + fail(); + } catch (Exception e) { + assertEquals("An element/relationship identified by \"Person://User\" could not be found", e.getMessage()); + } + } + + @Test + void test_parse_FindsAnElementByCanonicalName() { + Person user = workspace.getModel().addPerson("User"); + ModelItem element = parser.parse(context(), tokens("!ref", "Person://User")); + + assertSame(user, element); + } + + @Test + void test_parse_FindsAnElementByIdentifier() { + Person user = workspace.getModel().addPerson("User"); + + ModelDslContext context = context(); + IdentifiersRegister register = new IdentifiersRegister(); + register.register("user", user); + context.setIdentifierRegister(register); + + ModelItem modelItem = parser.parse(context, tokens("!ref", "user")); + assertSame(modelItem, user); + } + + @Test + void test_parse_FindsARelationshipByIdentifier() { + Person user = workspace.getModel().addPerson("User"); + Relationship relationship = user.interactsWith(user, "Description"); + + ModelDslContext context = context(); + IdentifiersRegister register = new IdentifiersRegister(); + register.register("rel", relationship); + context.setIdentifierRegister(register); + + ModelItem modelItem = parser.parse(context, tokens("!ref", "rel")); + assertSame(modelItem, relationship); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/RelationshipStyleParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/RelationshipStyleParserTests.java new file mode 100644 index 000000000..8736c5acb --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/RelationshipStyleParserTests.java @@ -0,0 +1,392 @@ +package com.structurizr.dsl; + +import com.structurizr.view.Border; +import com.structurizr.view.LineStyle; +import com.structurizr.view.RelationshipStyle; +import com.structurizr.view.Routing; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class RelationshipStyleParserTests extends AbstractTests { + + private RelationshipStyleParser parser = new RelationshipStyleParser(); + private RelationshipStyle relationshipStyle; + + private RelationshipStyleDslContext relationshipStyleDslContext() { + relationshipStyle = workspace.getViews().getConfiguration().getStyles().addRelationshipStyle("Tag"); + RelationshipStyleDslContext context = new RelationshipStyleDslContext(relationshipStyle); + context.setWorkspace(workspace); + + return context; + } + + @Test + void test_parseRelationshipStyle_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parseRelationshipStyle(context(), tokens("relationship", "tag", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: relationship <tag> {", e.getMessage()); + } + } + + @Test + void test_parseRelationshipStyle_ThrowsAnException_WhenTheTagIsMissing() { + try { + parser.parseRelationshipStyle(context(), tokens("relationship")); + fail(); + } catch (Exception e) { + assertEquals("Expected: relationship <tag> {", e.getMessage()); + } + } + + @Test + void test_parseRelationshipStyle_ThrowsAnException_WhenTheTagIsEmpty() { + try { + parser.parseRelationshipStyle(context(), tokens("relationship", "")); + fail(); + } catch (Exception e) { + assertEquals("A tag must be specified", e.getMessage()); + } + } + + @Test + void test_parseRelationshipStyle_CreatesAnRelationshipStyle() { + parser.parseRelationshipStyle(context(), tokens("relationship", "Relationship")); + + RelationshipStyle style = workspace.getViews().getConfiguration().getStyles().getRelationships().stream().filter(es -> "Relationship".equals(es.getTag())).findFirst().get(); + assertNotNull(style); + } + + @Test + void test_parseRelationshipStyle_FindsAnExistingRelationshipStyle() { + RelationshipStyle style = workspace.getViews().getConfiguration().getStyles().addRelationshipStyle("Tag"); + assertSame(style, parser.parseRelationshipStyle(context(), tokens("relationship", "Tag"))); + } + + @Test + void test_parseThickness_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parseThickness(relationshipStyleDslContext(), tokens("thickness", "number", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: thickness <number>", e.getMessage()); + } + } + + @Test + void test_parseThickness_ThrowsAnException_WhenTheThicknessIsMissing() { + try { + parser.parseThickness(relationshipStyleDslContext(), tokens("thickness")); + fail(); + } catch (Exception e) { + assertEquals("Expected: thickness <number>", e.getMessage()); + } + } + + @Test + void test_parseThickness_ThrowsAnException_WhenTheThicknessIsNotValid() { + try { + parser.parseThickness(relationshipStyleDslContext(), tokens("thickness", "abc")); + fail(); + } catch (Exception e) { + assertEquals("Thickness must be a positive integer", e.getMessage()); + } + } + + @Test + void test_parseThickness_SetsTheThickness() { + parser.parseThickness(relationshipStyleDslContext(), tokens("thickness", "75")); + assertEquals(75, relationshipStyle.getThickness()); + } + + @Test + void test_parseColour_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parseColour(relationshipStyleDslContext(), tokens("colour", "hex", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: colour <#rrggbb|color name>", e.getMessage()); + } + } + + @Test + void test_parseColour_ThrowsAnException_WhenTheColourIsMissing() { + try { + parser.parseColour(relationshipStyleDslContext(), tokens("colour")); + fail(); + } catch (Exception e) { + assertEquals("Expected: colour <#rrggbb|color name>", e.getMessage()); + } + } + + @Test + void test_parseColour_SetsTheColourWhenUsingAHexColourCode() { + parser.parseColour(relationshipStyleDslContext(), tokens("colour", "#ff0000")); + assertEquals("#ff0000", relationshipStyle.getColor()); + } + + @Test + void test_parseColour_SetsTheColourWhenUsingAColourName() { + parser.parseColour(relationshipStyleDslContext(), tokens("colour", "yellow")); + assertEquals("#ffff00", relationshipStyle.getColor()); + } + + @Test + void test_parseOpacity_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parseOpacity(relationshipStyleDslContext(), tokens("opacity", "number", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: opacity <0-100>", e.getMessage()); + } + } + + @Test + void test_parseOpacity_ThrowsAnException_WhenTheOpacityIsMissing() { + try { + parser.parseOpacity(relationshipStyleDslContext(), tokens("opacity")); + fail(); + } catch (Exception e) { + assertEquals("Expected: opacity <0-100>", e.getMessage()); + } + } + + @Test + void test_parseOpacity_ThrowsAnException_WhenTheOpacityIsNotValid() { + try { + parser.parseOpacity(relationshipStyleDslContext(), tokens("opacity", "abc")); + fail(); + } catch (Exception e) { + assertEquals("Opacity must be an integer between 0 and 100", e.getMessage()); + } + } + + @Test + void test_parseOpacity_SetsTheOpacity() { + parser.parseOpacity(relationshipStyleDslContext(), tokens("opacity", "75")); + assertEquals(75, relationshipStyle.getOpacity()); + } + + @Test + void test_parseWidth_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parseWidth(relationshipStyleDslContext(), tokens("width", "number", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: width <number>", e.getMessage()); + } + } + + @Test + void test_parseWidth_ThrowsAnException_WhenTheWidthIsMissing() { + try { + parser.parseWidth(relationshipStyleDslContext(), tokens("width")); + fail(); + } catch (Exception e) { + assertEquals("Expected: width <number>", e.getMessage()); + } + } + + @Test + void test_parseWidth_ThrowsAnException_WhenTheWidthIsNotValid() { + try { + parser.parseWidth(relationshipStyleDslContext(), tokens("width", "abc")); + fail(); + } catch (Exception e) { + assertEquals("Width must be a positive integer", e.getMessage()); + } + } + + @Test + void test_parseWidth_SetsTheWidth() { + parser.parseWidth(relationshipStyleDslContext(), tokens("width", "75")); + assertEquals(75, relationshipStyle.getWidth()); + } + + @Test + void test_parseFontSize_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parseFontSize(relationshipStyleDslContext(), tokens("fontSize", "number", "extrta")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: fontSize <number>", e.getMessage()); + } + } + + @Test + void test_parseFontSize_ThrowsAnException_WhenTheFontSizeIsMissing() { + try { + parser.parseFontSize(relationshipStyleDslContext(), tokens("fontSize")); + fail(); + } catch (Exception e) { + assertEquals("Expected: fontSize <number>", e.getMessage()); + } + } + + @Test + void test_parseFontSize_ThrowsAnException_WhenTheFontSizeIsNotValid() { + try { + parser.parseFontSize(relationshipStyleDslContext(), tokens("fontSize", "abc")); + fail(); + } catch (Exception e) { + assertEquals("Font size must be a positive integer", e.getMessage()); + } + } + + @Test + void test_parseFontSize_SetsTheFontSize() { + parser.parseFontSize(relationshipStyleDslContext(), tokens("fontSize", "75")); + assertEquals(75, relationshipStyle.getFontSize()); + } + + @Test + void test_parseDashed_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parseDashed(relationshipStyleDslContext(), tokens("dashed", "boolean", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: dashed <true|false>", e.getMessage()); + } + } + + @Test + void test_parseDashed_ThrowsAnException_WhenTheDashedIsMissing() { + try { + parser.parseDashed(relationshipStyleDslContext(), tokens("dashed")); + fail(); + } catch (Exception e) { + assertEquals("Expected: dashed <true|false>", e.getMessage()); + } + } + + @Test + void test_parseDashed_ThrowsAnException_WhenTheDashedIsNotValid() { + try { + parser.parseDashed(relationshipStyleDslContext(), tokens("dashed", "abc")); + fail(); + } catch (Exception e) { + assertEquals("Dashed must be true or false", e.getMessage()); + } + } + + @Test + void test_parseDashed_SetsTheDashed() { + RelationshipStyleDslContext context = relationshipStyleDslContext(); + parser.parseDashed(context, tokens("dashed", "false")); + assertEquals(false, relationshipStyle.getDashed()); + + parser.parseDashed(context, tokens("dashed", "true")); + assertEquals(true, relationshipStyle.getDashed()); + } + + @Test + void test_parseLineStyle_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parseLineStyle(relationshipStyleDslContext(), tokens("style", "solid", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: style <solid|dashed|dotted>", e.getMessage()); + } + } + + @Test + void test_parseLineStyle_ThrowsAnException_WhenTheStyleIsMissing() { + try { + parser.parseLineStyle(relationshipStyleDslContext(), tokens("style")); + fail(); + } catch (Exception e) { + assertEquals("Expected: style <solid|dashed|dotted>", e.getMessage()); + } + } + + @Test + void test_parseLineStyle_ThrowsAnException_WhenTheStyleIsNotValid() { + try { + parser.parseLineStyle(relationshipStyleDslContext(), tokens("style", "none")); + fail(); + } catch (Exception e) { + assertEquals("The line style \"none\" is not valid", e.getMessage()); + } + } + + @Test + void test_parseLineStyle_SetsTheStyle() { + parser.parseLineStyle(relationshipStyleDslContext(), tokens("style", "dotted")); + assertEquals(LineStyle.Dotted, relationshipStyle.getStyle()); + } + + @Test + void test_parsePosition_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parsePosition(relationshipStyleDslContext(), tokens("position", "number", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: position <0-100>", e.getMessage()); + } + } + + @Test + void test_parsePosition_ThrowsAnException_WhenThePositionIsMissing() { + try { + parser.parsePosition(relationshipStyleDslContext(), tokens("position")); + fail(); + } catch (Exception e) { + assertEquals("Expected: position <0-100>", e.getMessage()); + } + } + + @Test + void test_parsePosition_ThrowsAnException_WhenThePositionIsNotValid() { + try { + parser.parsePosition(relationshipStyleDslContext(), tokens("position", "abc")); + fail(); + } catch (Exception e) { + assertEquals("Position must be an integer between 0 and 100", e.getMessage()); + } + } + + @Test + void test_parsePosition_SetsThePosition() { + parser.parsePosition(relationshipStyleDslContext(), tokens("position", "75")); + assertEquals(75, relationshipStyle.getPosition()); + } + + @Test + void test_parseRouting_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parseRouting(relationshipStyleDslContext(), tokens("routing", "enum", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: routing <direct|orthogonal|curved>", e.getMessage()); + } + } + + @Test + void test_parseRouting_ThrowsAnException_WhenTheRoutingIsMissing() { + try { + parser.parseRouting(relationshipStyleDslContext(), tokens("routing")); + fail(); + } catch (Exception e) { + assertEquals("Expected: routing <direct|orthogonal|curved>", e.getMessage()); + } + } + + @Test + void test_parseRouting_ThrowsAnException_WhenTheRoutingIsNotValid() { + try { + parser.parseRouting(relationshipStyleDslContext(), tokens("routing", "rounded")); + fail(); + } catch (Exception e) { + assertEquals("The routing \"rounded\" is not valid", e.getMessage()); + } + } + + @Test + void test_parseRouting_SetsTheRouting() { + parser.parseRouting(relationshipStyleDslContext(), tokens("routing", "curved")); + assertEquals(Routing.Curved, relationshipStyle.getRouting()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ScriptParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ScriptParserTests.java new file mode 100644 index 000000000..a7627f7af --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ScriptParserTests.java @@ -0,0 +1,34 @@ +package com.structurizr.dsl; + +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.util.Collections; + +import static org.junit.jupiter.api.Assertions.*; + +class ScriptParserTests extends AbstractTests { + + private ScriptParser parser = new ScriptParser(); + + @Test + void test_parseExternal_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parseExternal(tokens("!script", "test.kts", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: !script <filename>", e.getMessage()); + } + } + + @Test + void test_parseExternal_ThrowsAnException_WhenNoFilenameIsSpecified() { + try { + parser.parseExternal(tokens("!script")); + fail(); + } catch (Exception e) { + assertEquals("Expected: !script <filename>", e.getMessage()); + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/SoftwareSystemInstanceParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/SoftwareSystemInstanceParserTests.java new file mode 100644 index 000000000..1d0b7e278 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/SoftwareSystemInstanceParserTests.java @@ -0,0 +1,143 @@ +package com.structurizr.dsl; + +import com.structurizr.model.*; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class SoftwareSystemInstanceParserTests extends AbstractTests { + + private SoftwareSystemInstanceParser parser = new SoftwareSystemInstanceParser(); + + @Test + void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parse(new DeploymentNodeDslContext(null), tokens("softwareSystemInstance", "identifier", "deploymentGroups", "tags", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: softwareSystemInstance <identifier> [deploymentGroups] [tags]", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheIdentifierIsNotSpecified() { + try { + parser.parse(new DeploymentNodeDslContext(null), tokens("softwareSystemInstance")); + fail(); + } catch (Exception e) { + assertEquals("Expected: softwareSystemInstance <identifier> [deploymentGroups] [tags]", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheElementDoesNotExist() { + try { + parser.parse(new DeploymentNodeDslContext(null), tokens("softwareSystemInstance", "softwareSystem")); + fail(); + } catch (Exception e) { + assertEquals("The software system \"softwareSystem\" does not exist", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheElementIsNotASoftwareSystem() { + DeploymentNodeDslContext context = new DeploymentNodeDslContext(null); + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("softwaresystem", model.addPerson("Name", "Description")); + context.setIdentifierRegister(elements); + + try { + parser.parse(context, tokens("softwareSystemInstance", "softwareSystem")); + fail(); + } catch (Exception e) { + assertEquals("The software system \"softwareSystem\" does not exist", e.getMessage()); + } + } + + @Test + void test_parse_CreatesASoftwareSystemInstanceInTheDefaultDeploymentGroup() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + DeploymentNode deploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + DeploymentNodeDslContext context = new DeploymentNodeDslContext(deploymentNode); + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("softwaresystem", softwareSystem); + context.setIdentifierRegister(elements); + + parser.parse(context, tokens("softwareSystemInstance", "softwareSystem")); + + assertEquals(3, model.getElements().size()); + assertEquals(1, deploymentNode.getSoftwareSystemInstances().size()); + SoftwareSystemInstance softwareSystemInstance = deploymentNode.getSoftwareSystemInstances().iterator().next(); + assertSame(softwareSystem, softwareSystemInstance.getSoftwareSystem()); + assertEquals("Software System Instance", softwareSystemInstance.getTags()); + assertEquals("Live", softwareSystemInstance.getEnvironment()); + assertEquals(1, softwareSystemInstance.getDeploymentGroups().size()); + assertEquals("Default", softwareSystemInstance.getDeploymentGroups().iterator().next()); + } + + @Test + void test_parse_CreatesASoftwareSystemInstanceInTheDefaultDeploymentGroupWithTags() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + DeploymentNode deploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + DeploymentNodeDslContext context = new DeploymentNodeDslContext(deploymentNode); + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("softwaresystem", softwareSystem); + context.setIdentifierRegister(elements); + + parser.parse(context, tokens("softwareSystemInstance", "softwareSystem", "", "Tag 1, Tag 2")); + + assertEquals(3, model.getElements().size()); + assertEquals(1, deploymentNode.getSoftwareSystemInstances().size()); + SoftwareSystemInstance softwareSystemInstance = deploymentNode.getSoftwareSystemInstances().iterator().next(); + assertSame(softwareSystem, softwareSystemInstance.getSoftwareSystem()); + assertEquals("Software System Instance,Tag 1,Tag 2", softwareSystemInstance.getTags()); + assertEquals("Live", softwareSystemInstance.getEnvironment()); + assertEquals(1, softwareSystemInstance.getDeploymentGroups().size()); + assertEquals("Default", softwareSystemInstance.getDeploymentGroups().iterator().next()); + } + + @Test + void test_parse_CreatesASoftwareSystemInstanceInTheSpecifiedDeploymentGroup() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + DeploymentNode deploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + DeploymentNodeDslContext context = new DeploymentNodeDslContext(deploymentNode); + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("softwaresystem", softwareSystem); + elements.register("group", new DeploymentGroup("Group")); + context.setIdentifierRegister(elements); + + parser.parse(context, tokens("softwareSystemInstance", "softwareSystem", "group")); + + assertEquals(3, model.getElements().size()); + assertEquals(1, deploymentNode.getSoftwareSystemInstances().size()); + SoftwareSystemInstance softwareSystemInstance = deploymentNode.getSoftwareSystemInstances().iterator().next(); + assertSame(softwareSystem, softwareSystemInstance.getSoftwareSystem()); + assertEquals("Software System Instance", softwareSystemInstance.getTags()); + assertEquals("Live", softwareSystemInstance.getEnvironment()); + assertEquals(1, softwareSystemInstance.getDeploymentGroups().size()); + assertEquals("Group", softwareSystemInstance.getDeploymentGroups().iterator().next()); + } + + @Test + void test_parse_CreatesASoftwareSystemInstanceInTheSpecifiedDeploymentGroupWithTags() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + DeploymentNode deploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + DeploymentNodeDslContext context = new DeploymentNodeDslContext(deploymentNode); + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("softwaresystem", softwareSystem); + elements.register("group", new DeploymentGroup("Group")); + context.setIdentifierRegister(elements); + + parser.parse(context, tokens("softwareSystemInstance", "softwareSystem", "group", "Tag 1, Tag 2")); + + assertEquals(3, model.getElements().size()); + assertEquals(1, deploymentNode.getSoftwareSystemInstances().size()); + SoftwareSystemInstance softwareSystemInstance = deploymentNode.getSoftwareSystemInstances().iterator().next(); + assertSame(softwareSystem, softwareSystemInstance.getSoftwareSystem()); + assertEquals("Software System Instance,Tag 1,Tag 2", softwareSystemInstance.getTags()); + assertEquals("Live", softwareSystemInstance.getEnvironment()); + assertEquals(1, softwareSystemInstance.getDeploymentGroups().size()); + assertEquals("Group", softwareSystemInstance.getDeploymentGroups().iterator().next()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/SoftwareSystemParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/SoftwareSystemParserTests.java new file mode 100644 index 000000000..adbc2dfa8 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/SoftwareSystemParserTests.java @@ -0,0 +1,83 @@ +package com.structurizr.dsl; + +import com.structurizr.model.Location; +import com.structurizr.model.SoftwareSystem; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class SoftwareSystemParserTests extends AbstractTests { + + private SoftwareSystemParser parser = new SoftwareSystemParser(); + + @Test + void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parse(context(), tokens("softwareSystem", "name", "description", "tags", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: softwareSystem <name> [description] [tags]", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheNameIsNotSpecified() { + try { + parser.parse(context(), tokens("softwareSystem")); + fail(); + } catch (Exception e) { + assertEquals("Expected: softwareSystem <name> [description] [tags]", e.getMessage()); + } + } + + @Test + void test_parse_CreatesASoftwareSystem() { + parser.parse(context(), tokens("softwareSystem", "Name")); + + assertEquals(1, model.getElements().size()); + SoftwareSystem softwareSystem = model.getSoftwareSystemWithName("Name"); + assertNotNull(softwareSystem); + assertEquals("", softwareSystem.getDescription()); + assertEquals(Location.Unspecified, softwareSystem.getLocation()); + assertEquals("Element,Software System", softwareSystem.getTags()); + } + + @Test + void test_parse_CreatesASoftwareSystemWithADescription() { + parser.parse(context(), tokens("softwareSystem", "Name", "Description")); + + assertEquals(1, model.getElements().size()); + SoftwareSystem softwareSystem = model.getSoftwareSystemWithName("Name"); + assertNotNull(softwareSystem); + assertEquals("Description", softwareSystem.getDescription()); + assertEquals(Location.Unspecified, softwareSystem.getLocation()); + assertEquals("Element,Software System", softwareSystem.getTags()); + } + + @Test + void test_parse_CreatesASoftwareSystemWithADescriptionAndTags() { + parser.parse(context(), tokens("softwareSystem", "Name", "Description", "Tag 1, Tag 2")); + + assertEquals(1, model.getElements().size()); + SoftwareSystem softwareSystem = model.getSoftwareSystemWithName("Name"); + assertNotNull(softwareSystem); + assertEquals("Description", softwareSystem.getDescription()); + assertEquals(Location.Unspecified, softwareSystem.getLocation()); + assertEquals("Element,Software System,Tag 1,Tag 2", softwareSystem.getTags()); + } + + @Test + void test_parse_CreatesAnInternalSoftwareSystem() { + EnterpriseDslContext context = new EnterpriseDslContext(); + context.setWorkspace(workspace); + parser.parse(context, tokens("softwareSystem", "Name")); + + assertEquals(1, model.getElements().size()); + SoftwareSystem softwareSystem = model.getSoftwareSystemWithName("Name"); + assertNotNull(softwareSystem); + assertEquals("", softwareSystem.getDescription()); + assertEquals(Location.Internal, softwareSystem.getLocation()); + assertEquals("Element,Software System", softwareSystem.getTags()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/StaticViewAnimationStepParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/StaticViewAnimationStepParserTests.java new file mode 100644 index 000000000..557f1e378 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/StaticViewAnimationStepParserTests.java @@ -0,0 +1,32 @@ +package com.structurizr.dsl; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +class StaticViewAnimationStepParserTests extends AbstractTests { + + private StaticViewAnimationStepParser parser = new StaticViewAnimationStepParser(); + + @Test + void test_parseExplicit_ThrowsAnException_WhenElementsAreMissing() { + try { + parser.parse((StaticViewDslContext)null, tokens("animationStep")); + fail(); + } catch (Exception e) { + assertEquals("Expected: animationStep <identifier> [identifier...]", e.getMessage()); + } + } + + @Test + void test_parseImplicit_ThrowsAnException_WhenElementsAreMissing() { + try { + parser.parse((StaticViewAnimationDslContext) null, tokens()); + fail(); + } catch (Exception e) { + assertEquals("Expected: <identifier> [identifier...]", e.getMessage()); + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/StaticViewContentParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/StaticViewContentParserTests.java new file mode 100644 index 000000000..82d572de7 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/StaticViewContentParserTests.java @@ -0,0 +1,790 @@ +package com.structurizr.dsl; + +import com.structurizr.model.*; +import com.structurizr.view.ComponentView; +import com.structurizr.view.SystemContextView; +import com.structurizr.view.SystemLandscapeView; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class StaticViewContentParserTests extends AbstractTests { + + private StaticViewContentParser parser = new StaticViewContentParser(); + + @Test + void test_parseInclude_ThrowsAnException_WhenTheNoElementsAreSpecified() { + try { + parser.parseInclude(new SystemLandscapeViewDslContext(null), tokens("include")); + fail(); + } catch (RuntimeException iae) { + assertEquals("Expected: include <*|identifier|expression> [*|identifier|expression...]", iae.getMessage()); + } + } + + @Test + void test_parseInclude_ThrowsAnException_WhenTheSpecifiedElementDoesNotExist() { + try { + parser.parseInclude(new SystemLandscapeViewDslContext(null), tokens("include", "user")); + fail(); + } catch (RuntimeException iae) { + assertEquals("The element/relationship \"user\" does not exist", iae.getMessage()); + } + } + + @Test + void test_parseInclude_ThrowsAnException_WhenTryingToAddAContainerToASystemLandscapeView() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + Container container = softwareSystem.addContainer("Container", "Description", "Technology"); + + SystemLandscapeView view = views.createSystemLandscapeView("key", "Description"); + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("container", container); + context.setIdentifierRegister(elements); + + try { + parser.parseInclude(context, tokens("include", "container")); + fail(); + } catch (RuntimeException iae) { + assertEquals("The element \"container\" can not be added to this type of view", iae.getMessage()); + } + } + + @Test + void test_parseInclude_ThrowsAnException_WhenTryingToAddAComponentToASystemLandscapeView() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + Container container = softwareSystem.addContainer("Container", "Description", "Technology"); + Component component = container.addComponent("Component", "Description", "Technology"); + + SystemLandscapeView view = views.createSystemLandscapeView("key", "Description"); + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("component", component); + context.setIdentifierRegister(elements); + + try { + parser.parseInclude(context, tokens("include", "component")); + fail(); + } catch (RuntimeException iae) { + assertEquals("The element \"component\" can not be added to this type of view", iae.getMessage()); + } + } + + @Test + void test_parseInclude_AddsAllPeopleAndSoftwareSystemsToASystemLandscapeView_WhenTheWildcardIsSpecified() { + Person user = model.addPerson("User", "Description"); + SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1", "Description"); + softwareSystem1.addContainer("Container 1", "Description", "Technology"); + SoftwareSystem softwareSystem2 = model.addSoftwareSystem("Software System 2", "Description"); + user.uses(softwareSystem1, "Uses"); + softwareSystem1.uses(softwareSystem2, "Uses"); + + SystemLandscapeView view = views.createSystemLandscapeView("key", "Description"); + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + parser.parseInclude(context, tokens("include", "*")); + + assertEquals(3, view.getElements().size()); + assertEquals(2, view.getRelationships().size()); + } + + @Test + void test_parseInclude_AddsTheSpecifiedElementsToASystemLandscapeView() { + Person user = model.addPerson("User", "Description"); + SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1", "Description"); + SoftwareSystem softwareSystem2 = model.addSoftwareSystem("Software System 2", "Description"); + user.uses(softwareSystem1, "Uses"); + softwareSystem1.uses(softwareSystem2, "Uses"); + CustomElement box1 = model.addCustomElement("Box 1"); + box1.uses(softwareSystem1, ""); + + SystemLandscapeView view = views.createSystemLandscapeView("key", "Description"); + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("user", user); + elements.register("softwaresystem1", softwareSystem1); + elements.register("softwaresystem2", softwareSystem2); + elements.register("box1", box1); + context.setIdentifierRegister(elements); + + parser.parseInclude(context, tokens("include", "user", "softwareSystem1", "box1")); + + assertEquals(3, view.getElements().size()); + assertTrue(view.getElements().stream().anyMatch(ev -> ev.getElement().equals(user))); + assertTrue(view.getElements().stream().anyMatch(ev -> ev.getElement().equals(softwareSystem1))); + assertTrue(view.getElements().stream().anyMatch(ev -> ev.getElement().equals(box1))); + assertTrue(view.getElements().stream().noneMatch(ev -> ev.getElement().equals(softwareSystem2))); + assertEquals(2, view.getRelationships().size()); + } + + @Test + void test_parseInclude_AddsNearestNeighboursToASystemContextView_WhenTheWildcardIsSpecified() { + Person user = model.addPerson("User", "Description"); + SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1", "Description"); + softwareSystem1.addContainer("Container 1", "Description", "Technology"); + SoftwareSystem softwareSystem2 = model.addSoftwareSystem("Software System 2", "Description"); + user.uses(softwareSystem1, "Uses"); + softwareSystem1.uses(softwareSystem2, "Uses"); + + SystemContextView view = views.createSystemContextView(softwareSystem1, "key", "Description"); + SystemContextViewDslContext context = new SystemContextViewDslContext(view); + context.setWorkspace(workspace); + + parser.parseInclude(context, tokens("include", "*")); + + assertEquals(3, view.getElements().size()); + assertEquals(2, view.getRelationships().size()); + } + + @Test + void test_parseInclude_AddsTheSpecifiedPeopleAndSoftwareSystemsToASystemContextView() { + Person user = model.addPerson("User", "Description"); + SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1", "Description"); + SoftwareSystem softwareSystem2 = model.addSoftwareSystem("Software System 2", "Description"); + user.uses(softwareSystem1, "Uses"); + softwareSystem1.uses(softwareSystem2, "Uses"); + + SystemContextView view = views.createSystemContextView(softwareSystem1, "key", "Description"); + SystemContextViewDslContext context = new SystemContextViewDslContext(view); + context.setWorkspace(workspace); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("user", user); + elements.register("softwaresystem1", softwareSystem1); + elements.register("softwaresystem2", softwareSystem2); + context.setIdentifierRegister(elements); + + parser.parseInclude(context, tokens("include", "user")); + + assertEquals(2, view.getElements().size()); + assertTrue(view.getElements().stream().anyMatch(ev -> ev.getElement().equals(user))); + assertTrue(view.getElements().stream().anyMatch(ev -> ev.getElement().equals(softwareSystem1))); + assertTrue(view.getElements().stream().noneMatch(ev -> ev.getElement().equals(softwareSystem2))); + assertEquals(1, view.getRelationships().size()); + } + + @Test + void test_parseInclude_ThrowsAnException_WhenTryingToAddAContainerToASystemContextView() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + Container container = softwareSystem.addContainer("Container", "Description", "Technology"); + + SystemContextView view = views.createSystemContextView(softwareSystem, "key", "Description"); + SystemContextViewDslContext context = new SystemContextViewDslContext(view); + context.setWorkspace(workspace); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("container", container); + context.setIdentifierRegister(elements); + + try { + parser.parseInclude(context, tokens("include", "container")); + fail(); + } catch (RuntimeException iae) { + assertEquals("The element \"container\" can not be added to this type of view", iae.getMessage()); + } + } + + @Test + void test_parseInclude_ThrowsAnException_WhenTryingToAddAComponentToASystemContextView() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + Container container = softwareSystem.addContainer("Container", "Description", "Technology"); + Component component = container.addComponent("Component", "Description", "Technology"); + + SystemContextView view = views.createSystemContextView(softwareSystem, "key", "Description"); + SystemContextViewDslContext context = new SystemContextViewDslContext(view); + context.setWorkspace(workspace); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("component", component); + context.setIdentifierRegister(elements); + + try { + parser.parseInclude(context, tokens("include", "component")); + fail(); + } catch (RuntimeException iae) { + assertEquals("The element \"component\" can not be added to this type of view", iae.getMessage()); + } + } + + @Test + void test_parseExclude_ThrowsAnException_WhenTheNoElementsAreSpecified() { + SystemLandscapeView view = views.createSystemLandscapeView("key", "Description"); + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + try { + parser.parseExclude(context, tokens("include")); + fail(); + } catch (RuntimeException iae) { + assertEquals("Expected: exclude <identifier|expression> [identifier|expression...]", iae.getMessage()); + } + } + + @Test + void test_parseExclude_ThrowsAnException_WhenTheSpecifiedElementDoesNotExist() { + SystemLandscapeView view = views.createSystemLandscapeView("key", "Description"); + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + try { + parser.parseExclude(context, tokens("exclude", "user")); + fail(); + } catch (RuntimeException iae) { + assertEquals("The element/relationship \"user\" does not exist", iae.getMessage()); + } + } + + @Test + void test_parseExclude_RemovesTheSpecifiedPeopleAndSoftwareSystemsFromAView() { + Person user = model.addPerson("User", "Description"); + SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1", "Description"); + SoftwareSystem softwareSystem2 = model.addSoftwareSystem("Software System 2", "Description"); + user.uses(softwareSystem1, "Uses"); + softwareSystem1.uses(softwareSystem2, "Uses"); + + SystemLandscapeView view = views.createSystemLandscapeView("key", "Description"); + view.addAllElements(); + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("user", user); + elements.register("softwaresystem1", softwareSystem1); + elements.register("softwaresystem2", softwareSystem2); + context.setIdentifierRegister(elements); + + assertEquals(3, view.getElements().size()); + assertEquals(2, view.getRelationships().size()); + + parser.parseExclude(context, tokens("exclude", "user", "softwaresystem1")); + + assertEquals(1, view.getElements().size()); + assertTrue(view.getElements().stream().noneMatch(ev -> ev.getElement().equals(user))); + assertTrue(view.getElements().stream().noneMatch(ev -> ev.getElement().equals(softwareSystem1))); + assertTrue(view.getElements().stream().anyMatch(ev -> ev.getElement().equals(softwareSystem2))); + assertEquals(0, view.getRelationships().size()); + } + + @Test + void test_parseExclude_RemovesTheRelationshipFromAView_WhenAnExplicitIdentifierIsSpecified() { + Person user = model.addPerson("User", "Description"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + Relationship rel = user.uses(softwareSystem, "Uses"); + + SystemLandscapeView view = views.createSystemLandscapeView("key", "Description"); + view.addAllElements(); + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + IdentifiersRegister identifersRegister = new IdentifiersRegister(); + identifersRegister.register("rel", rel); + context.setIdentifierRegister(identifersRegister); + + assertEquals(2, view.getElements().size()); + assertEquals(1, view.getRelationships().size()); + + parser.parseExclude(context, tokens("exclude", "rel")); + + assertEquals(2, view.getElements().size()); + assertTrue(view.getElements().stream().anyMatch(ev -> ev.getElement().equals(user))); + assertTrue(view.getElements().stream().anyMatch(ev -> ev.getElement().equals(softwareSystem))); + assertEquals(0, view.getRelationships().size()); + } + + @Test + void test_parseExclude_ThrowsAnException_WhenTheRelationshipSourceElementDoesNotExistInTheModel() { + try { + SystemLandscapeView view = views.createSystemLandscapeView("key", "Description"); + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + parser.parseExclude(context, tokens("exclude", "relationship.source==user && relationship.destination==softwareSystem")); + + fail(); + } catch (RuntimeException re) { + assertEquals("The element \"user\" does not exist", re.getMessage()); + } + } + + @Test + void test_parseExclude_ThrowsAnException_WhenTheRelationshipDestinationElementDoesNotExistInTheModel() { + try { + Person user = model.addPerson("User", "Description"); + + SystemLandscapeView view = views.createSystemLandscapeView("key", "Description"); + view.add(user); + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("user", user); + context.setIdentifierRegister(elements); + + parser.parseExclude(context, tokens("exclude", "relationship.source==user && relationship.destination==softwareSystem")); + + fail(); + } catch (RuntimeException re) { + assertEquals("The element \"softwareSystem\" does not exist", re.getMessage()); + } + } + + @Test + void test_parseExclude_RemovesTheRelationshipFromAView_WhenAnExpressionIsSpecifiedWithSourceAndDestination() { + Person user = model.addPerson("User", "Description"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + user.uses(softwareSystem, "Uses"); + + SystemLandscapeView view = views.createSystemLandscapeView("key", "Description"); + view.addAllElements(); + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("user", user); + elements.register("softwaresystem", softwareSystem); + context.setIdentifierRegister(elements); + + assertEquals(2, view.getElements().size()); + assertEquals(1, view.getRelationships().size()); + + parser.parseExclude(context, tokens("exclude", "relationship.source==user && relationship.destination==softwareSystem")); + assertEquals(0, view.getRelationships().size()); + } + + @Test + void test_parseExclude_RemovesTheRelationshipFromAView_WhenAnExpressionIsSpecifiedWithSourceAndWildcard() { + Person user = model.addPerson("User", "Description"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + user.uses(softwareSystem, "Uses"); + + SystemLandscapeView view = views.createSystemLandscapeView("key", "Description"); + view.addAllElements(); + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("user", user); + elements.register("softwaresystem", softwareSystem); + context.setIdentifierRegister(elements); + + assertEquals(2, view.getElements().size()); + assertEquals(1, view.getRelationships().size()); + + parser.parseExclude(context, tokens("exclude", "relationship.source==user")); + assertEquals(0, view.getRelationships().size()); + } + + @Test + void test_parseExclude_RemovesTheRelationshipFromAView_WhenAnExpressionIsSpecifiedWithWildcardAndDestination() { + Person user = model.addPerson("User", "Description"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + user.uses(softwareSystem, "Uses"); + + SystemLandscapeView view = views.createSystemLandscapeView("key", "Description"); + view.addAllElements(); + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("user", user); + elements.register("softwaresystem", softwareSystem); + context.setIdentifierRegister(elements); + + assertEquals(2, view.getElements().size()); + assertEquals(1, view.getRelationships().size()); + + parser.parseExclude(context, tokens("exclude", "relationship.destination==softwareSystem")); + assertEquals(0, view.getRelationships().size()); + } + + @Test + void test_parseExclude_RemovesTheRelationshipFromAView_WhenAnExpressionIsSpecifiedWithWildcardAndWildcard() { + Person user = model.addPerson("User", "Description"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + user.uses(softwareSystem, "Uses"); + + SystemLandscapeView view = views.createSystemLandscapeView("key", "Description"); + view.addAllElements(); + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("user", user); + elements.register("softwaresystem", softwareSystem); + context.setIdentifierRegister(elements); + + assertEquals(2, view.getElements().size()); + assertEquals(1, view.getRelationships().size()); + + parser.parseExclude(context, tokens("exclude", "relationship==*")); + assertEquals(0, view.getRelationships().size()); + } + + @Test + void test_parseInclude_AddsAllElementsWithTheSpecifiedTag() { + Person user = model.addPerson("User"); + user.addTags("Tag 1"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container 1"); + Component component = container.addComponent("Component"); + + SystemLandscapeView view = views.createSystemLandscapeView("key", "Description"); + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + parser.parseInclude(context, tokens("include", "element.tag==Tag 1")); + + assertEquals(1, view.getElements().size()); + assertNotNull(view.getElementView(user)); + } + + @Test + void test_parseInclude_AddsAllElementsWithTheSpecifiedTags() { + Person user = model.addPerson("User"); + user.addTags("Tag 1", "Tag 2"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + softwareSystem.addTags("Tag 1"); + Container container = softwareSystem.addContainer("Container 1"); + Component component = container.addComponent("Component"); + + SystemLandscapeView view = views.createSystemLandscapeView("key", "Description"); + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + parser.parseInclude(context, tokens("include", "element.tag==Tag 1,Tag 2")); + + assertEquals(1, view.getElements().size()); + assertNotNull(view.getElementView(user)); + } + + @Test + void test_parseInclude_AddsAllElementsWithTheSpecifiedTagIgnoringElementsThatAreNotPermitted() { + Person user = model.addPerson("User"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container 1"); + Component component = container.addComponent("Component"); + + model.getElements().forEach(e -> e.addTags("Tag 1")); + + SystemLandscapeView view = views.createSystemLandscapeView("key", "Description"); + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + parser.parseInclude(context, tokens("include", "element.tag==Tag 1")); + + assertEquals(2, view.getElements().size()); + assertNotNull(view.getElementView(user)); + assertNotNull(view.getElementView(softwareSystem)); + assertNull(view.getElementView(container)); // containers are not permitted on system landscape views + assertNull(view.getElementView(component)); // components are not permitted on system landscape views + } + + @Test + void test_parseInclude_AddsAllElementsWithTheSpecifiedTagsIgnoringElementsThatAreNotPermitted() { + Person user = model.addPerson("User"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container 1"); + Component component = container.addComponent("Component"); + + model.getElements().forEach(e -> e.addTags("Tag 1", "Tag 2")); + + SystemLandscapeView view = views.createSystemLandscapeView("key", "Description"); + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + parser.parseInclude(context, tokens("include", "element.tag==Tag 1,Tag 2")); + + assertEquals(2, view.getElements().size()); + assertNotNull(view.getElementView(user)); + assertNotNull(view.getElementView(softwareSystem)); + assertNull(view.getElementView(container)); // containers are not permitted on system landscape views + assertNull(view.getElementView(component)); // components are not permitted on system landscape views + } + + @Test + void test_parseInclude_AddsAllElementsWithoutTheSpecifiedTag() { + Person user = model.addPerson("User"); + user.addTags("Tag 1"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container 1"); + Component component = container.addComponent("Component"); + + SystemLandscapeView view = views.createSystemLandscapeView("key", "Description"); + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + parser.parseInclude(context, tokens("include", "element.tag!=Tag 1")); + + assertEquals(1, view.getElements().size()); + assertNotNull(view.getElementView(softwareSystem)); + } + + @Test + void test_parseInclude_AddsAllElementsWithoutTheSpecifiedTags() { + Person user = model.addPerson("User"); + user.addTags("Tag 1", "Tag 2"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + softwareSystem.addTags("Tag 1"); + Container container = softwareSystem.addContainer("Container 1"); + Component component = container.addComponent("Component"); + + SystemLandscapeView view = views.createSystemLandscapeView("key", "Description"); + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + parser.parseInclude(context, tokens("include", "element.tag!=Tag 1,Tag 2")); + + assertEquals(1, view.getElements().size()); + assertNotNull(view.getElementView(softwareSystem)); + } + + @Test + void test_parseInclude_AddsAllElementsWithoutTheSpecifiedTagIgnoringElementsThatAreNotPermitted() { + Person user = model.addPerson("User"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container 1"); + Component component = container.addComponent("Component"); + + model.getElements().forEach(e -> e.addTags("Tag 1")); + + SystemLandscapeView view = views.createSystemLandscapeView("key", "Description"); + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + parser.parseInclude(context, tokens("include", "element.tag!=Tag 2")); + + assertEquals(2, view.getElements().size()); + assertNotNull(view.getElementView(user)); + assertNotNull(view.getElementView(softwareSystem)); + assertNull(view.getElementView(container)); // containers are not permitted on system landscape views + assertNull(view.getElementView(component)); // components are not permitted on system landscape views + } + + @Test + void test_parseExclude_RemovesAllElementsWithTheSpecifiedTag() { + Person user = model.addPerson("User"); + user.addTags("Tag 1"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + + SystemLandscapeView view = views.createSystemLandscapeView("key", "Description"); + view.addDefaultElements(); + assertEquals(2, view.getElements().size()); + assertNotNull(view.getElementView(user)); + assertNotNull(view.getElementView(softwareSystem)); + + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + parser.parseExclude(context, tokens("exclude", "element.tag==Tag 1")); + + assertEquals(1, view.getElements().size()); + assertNull(view.getElementView(user)); + assertNotNull(view.getElementView(softwareSystem)); + } + + @Test + void test_parseExclude_RemovesAllElementsWithTheSpecifiedTags() { + Person user = model.addPerson("User"); + user.addTags("Tag 1", "Tag 2"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + softwareSystem.addTags("Tag 1"); + + SystemLandscapeView view = views.createSystemLandscapeView("key", "Description"); + view.addDefaultElements(); + assertEquals(2, view.getElements().size()); + assertNotNull(view.getElementView(user)); + assertNotNull(view.getElementView(softwareSystem)); + + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + parser.parseExclude(context, tokens("exclude", "element.tag==Tag 1,Tag 2")); + + assertEquals(1, view.getElements().size()); + assertNull(view.getElementView(user)); + assertNotNull(view.getElementView(softwareSystem)); + } + + @Test + void test_parseExclude_RemovesAllElementsWithoutTheSpecifiedTag() { + Person user = model.addPerson("User"); + user.addTags("Tag 1"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + + SystemLandscapeView view = views.createSystemLandscapeView("key", "Description"); + view.addDefaultElements(); + assertEquals(2, view.getElements().size()); + assertNotNull(view.getElementView(user)); + assertNotNull(view.getElementView(softwareSystem)); + + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + parser.parseExclude(context, tokens("exclude", "element.tag!=Tag 1")); + + assertEquals(1, view.getElements().size()); + assertNotNull(view.getElementView(user)); + assertNull(view.getElementView(softwareSystem)); + } + + @Test + void test_parseExclude_RemovesAllElementsWithoutTheSpecifiedTags() { + Person user = model.addPerson("User"); + user.addTags("Tag 1", "Tag 2"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + softwareSystem.addTags("Tag 1"); + + SystemLandscapeView view = views.createSystemLandscapeView("key", "Description"); + view.addDefaultElements(); + assertEquals(2, view.getElements().size()); + assertNotNull(view.getElementView(user)); + assertNotNull(view.getElementView(softwareSystem)); + + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + parser.parseExclude(context, tokens("exclude", "element.tag!=Tag 1,Tag 2")); + + assertEquals(1, view.getElements().size()); + assertNotNull(view.getElementView(user)); + assertNull(view.getElementView(softwareSystem)); + } + + @Test + void test_parseInclude_AddsAllRelationshipsWithTheSpecifiedTag() { + Person user = model.addPerson("User"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + + Relationship relationship1 = user.uses(softwareSystem, "1"); + relationship1.addTags("Tag 1"); + Relationship relationship2 = user.uses(softwareSystem, "2"); + relationship2.addTags("Tag 2"); + + SystemLandscapeView view = views.createSystemLandscapeView("key", "Description"); + view.addAllElements(); + view.remove(relationship1); + view.remove(relationship2); + assertEquals(0, view.getRelationships().size()); + + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + parser.parseInclude(context, tokens("include", "relationship.tag==Tag 1")); + + assertEquals(1, view.getRelationships().size()); + assertNotNull(view.getRelationshipView(relationship1)); + } + + @Test + void test_parseInclude_AddsAllRelationshipsWithTheSpecifiedTags() { + Person user = model.addPerson("User"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + + Relationship relationship1 = user.uses(softwareSystem, "1"); + relationship1.addTags("Tag 1", "Tag 2"); + Relationship relationship2 = user.uses(softwareSystem, "2"); + relationship2.addTags("Tag 2"); + + SystemLandscapeView view = views.createSystemLandscapeView("key", "Description"); + view.addAllElements(); + view.remove(relationship1); + view.remove(relationship2); + assertEquals(0, view.getRelationships().size()); + + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + parser.parseInclude(context, tokens("include", "relationship.tag==Tag 1,Tag 2")); + + assertEquals(1, view.getRelationships().size()); + assertNotNull(view.getRelationshipView(relationship1)); + } + + @Test + void test_parseExclude_RemovesAllRelationshipsWithTheSpecifiedTag() { + Person user = model.addPerson("User"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + + Relationship relationship1 = user.uses(softwareSystem, "1"); + relationship1.addTags("Tag 1"); + Relationship relationship2 = user.uses(softwareSystem, "2"); + relationship2.addTags("Tag 2"); + + SystemLandscapeView view = views.createSystemLandscapeView("key", "Description"); + view.addAllElements(); + assertEquals(2, view.getRelationships().size()); + + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + parser.parseExclude(context, tokens("exclude", "relationship.tag==Tag 1")); + + assertEquals(1, view.getRelationships().size()); + assertNull(view.getRelationshipView(relationship1)); + assertNotNull(view.getRelationshipView(relationship2)); + } + + @Test + void test_parseExclude_RemovesAllRelationshipsWithTheSpecifiedTags() { + Person user = model.addPerson("User"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + + Relationship relationship1 = user.uses(softwareSystem, "1"); + relationship1.addTags("Tag 1", "Tag 2"); + Relationship relationship2 = user.uses(softwareSystem, "2"); + relationship2.addTags("Tag 2"); + + SystemLandscapeView view = views.createSystemLandscapeView("key", "Description"); + view.addAllElements(); + assertEquals(2, view.getRelationships().size()); + + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + parser.parseExclude(context, tokens("exclude", "relationship.tag==Tag 1,Tag 2")); + + assertEquals(1, view.getRelationships().size()); + assertNull(view.getRelationshipView(relationship1)); + assertNotNull(view.getRelationshipView(relationship2)); + } + + @Test + void test_parseInclude_IncludesTheMostAbstractElementWhenEfferentCouplingExpressionUsed() { + SoftwareSystem ss1 = model.addSoftwareSystem("Software System 1"); + Container c1 = ss1.addContainer("Container 1"); + Component cc1 = c1.addComponent("Component 1"); + + SoftwareSystem ss2 = model.addSoftwareSystem("Software System 2"); + Container c2 = ss2.addContainer("Container 2"); + Component cc2 = c2.addComponent("Component 2"); + + model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); + cc1.uses(cc2, "Uses"); + + ComponentView view = views.createComponentView(c1, "key", "Description"); + ComponentViewDslContext context = new ComponentViewDslContext(view); + context.setWorkspace(workspace); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("cc1", cc1); + context.setIdentifierRegister(elements); + + parser.parseInclude(context, tokens("include", "cc1->")); + + assertEquals(2, view.getElements().size()); + assertTrue(view.isElementInView(cc1)); + assertTrue(view.isElementInView(ss2)); // this is the software system, not the component + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/StaticViewExpressionParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/StaticViewExpressionParserTests.java new file mode 100644 index 000000000..5c4a45e38 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/StaticViewExpressionParserTests.java @@ -0,0 +1,199 @@ +package com.structurizr.dsl; + +import com.structurizr.model.*; +import org.junit.jupiter.api.Test; + +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.*; + +class StaticViewExpressionParserTests extends AbstractTests { + + private StaticViewExpressionParser parser = new StaticViewExpressionParser(); + + @Test + void test_parseExpression_ThrowsAnException_WhenElementTypeIsNotSupported() { + try { + parser.parseExpression("element.type==DeploymentNode", null); + fail(); + } catch (RuntimeException iae) { + assertEquals("The element type of \"DeploymentNode\" is not valid for this view", iae.getMessage()); + } + } + + @Test + void test_parseExpression_ReturnsElements_WhenElementTypeEqualsPerson() { + Person user = model.addPerson("User"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + Component component = container.addComponent("Component"); + + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(null); + context.setWorkspace(workspace); + + Set<ModelItem> elements = parser.parseExpression("element.type==Person", context); + assertEquals(1, elements.size()); + assertTrue(elements.contains(user)); + } + + @Test + void test_parseExpression_ReturnsElements_WhenElementTypeEqualsSoftwareSystem() { + Person user = model.addPerson("User"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + Component component = container.addComponent("Component"); + + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(null); + context.setWorkspace(workspace); + + Set<ModelItem> elements = parser.parseExpression("element.type==SoftwareSystem", context); + assertEquals(1, elements.size()); + assertTrue(elements.contains(softwareSystem)); + } + + @Test + void test_parseExpression_ReturnsElements_WhenElementTypeEqualsContainer() { + Person user = model.addPerson("User"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + Component component = container.addComponent("Component"); + + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(null); + context.setWorkspace(workspace); + + Set<ModelItem> elements = parser.parseExpression("element.type==Container", context); + assertEquals(1, elements.size()); + assertTrue(elements.contains(container)); + } + + @Test + void test_parseExpression_ReturnsElements_WhenElementTypeEqualsComponent() { + Person user = model.addPerson("User"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + Component component = container.addComponent("Component"); + + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(null); + context.setWorkspace(workspace); + + Set<ModelItem> elements = parser.parseExpression("element.type==Component", context); + assertEquals(1, elements.size()); + assertTrue(elements.contains(component)); + } + + @Test + void test_parseExpression_ReturnsElements_WhenElementHasTag() { + Person user = model.addPerson("User"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + Component component = container.addComponent("Component"); + + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(null); + context.setWorkspace(workspace); + + Set<ModelItem> elements = parser.parseExpression("element.tag==Container", context); + assertEquals(1, elements.size()); + assertTrue(elements.contains(container)); + } + + @Test + void test_parseExpression_ReturnsElements_WhenElementDoesNotHaveTag() { + Person user = model.addPerson("User"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + Component component = container.addComponent("Component"); + + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(null); + context.setWorkspace(workspace); + + Set<ModelItem> elements = parser.parseExpression("element.tag!=Container", context); + assertEquals(3, elements.size()); + assertFalse(elements.contains(container)); + } + + @Test + void test_parseExpression_ReturnsElements_WhenBooleanAndUsed() { + Person user = model.addPerson("User"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + Component component = container.addComponent("Component"); + + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(null); + context.setWorkspace(workspace); + + Set<ModelItem> elements = parser.parseExpression("element.tag==Element && element.type==Container", context); + assertEquals(1, elements.size()); + assertTrue(elements.contains(container)); + } + + @Test + void test_parseExpression_ReturnsElements_WhenBooleanOrUsed() { + Person user = model.addPerson("User"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + Component component = container.addComponent("Component"); + + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(null); + context.setWorkspace(workspace); + + Set<ModelItem> elements = parser.parseExpression("element.tag==Container || element.type==Component", context); + assertEquals(2, elements.size()); + assertTrue(elements.contains(container)); + assertTrue(elements.contains(component)); + } + + @Test + void test_parseExpression_ReturnsElements_ForAfferentCouplings() { + SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1"); + Container container1 = softwareSystem1.addContainer("Container 1"); + Component component1 = container1.addComponent("Component 1"); + + SoftwareSystem softwareSystem2 = model.addSoftwareSystem("Software System 2"); + Container container2 = softwareSystem2.addContainer("Container 2"); + Component component2 = container2.addComponent("Component 2"); + + model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); + component1.uses(component2, "Uses"); + + ComponentViewDslContext context = new ComponentViewDslContext(null); + context.setWorkspace(workspace); + + IdentifiersRegister map = new IdentifiersRegister(); + map.register("container1", container1); + context.setIdentifierRegister(map); + + Set<ModelItem> elements = parser.parseExpression("container1->", context); + assertEquals(4, elements.size()); + assertTrue(elements.contains(container1)); + assertTrue(elements.contains(softwareSystem2)); + assertTrue(elements.contains(container2)); + assertTrue(elements.contains(component2)); + } + + @Test + void test_parseExpression_ReturnsElements_ForAfferentCouplingsOfType() { + SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1"); + Container container1 = softwareSystem1.addContainer("Container 1"); + Component component1 = container1.addComponent("Component 1"); + + SoftwareSystem softwareSystem2 = model.addSoftwareSystem("Software System 2"); + Container container2 = softwareSystem2.addContainer("Container 2"); + Component component2 = container2.addComponent("Component 2"); + + model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); + component1.uses(component2, "Uses"); + + ComponentViewDslContext context = new ComponentViewDslContext(null); + context.setWorkspace(workspace); + + IdentifiersRegister map = new IdentifiersRegister(); + map.register("container1", container1); + context.setIdentifierRegister(map); + + Set<ModelItem> elements = parser.parseExpression("container1-> && element.type==Container", context); + assertEquals(2, elements.size()); + assertTrue(elements.contains(container1)); + assertTrue(elements.contains(container2)); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/SystemContextViewParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/SystemContextViewParserTests.java new file mode 100644 index 000000000..0b04ce40f --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/SystemContextViewParserTests.java @@ -0,0 +1,109 @@ +package com.structurizr.dsl; + +import com.structurizr.view.SystemContextView; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +class SystemContextViewParserTests extends AbstractTests { + + private SystemContextViewParser parser = new SystemContextViewParser(); + + @Test + void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { + DslContext context = context(); + try { + parser.parse(context, tokens("systemContext", "identifier", "key", "description", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: systemContext <software system identifier> [key] [description] {", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheSoftwareSystemIdentifierIsMissing() { + DslContext context = context(); + try { + parser.parse(context, tokens("systemContext")); + fail(); + } catch (Exception e) { + assertEquals("Expected: systemContext <software system identifier> [key] [description] {", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheSoftwareSystemIsNotDefined() { + DslContext context = context(); + try { + parser.parse(context, tokens("systemContext", "softwareSystem", "key")); + fail(); + } catch (Exception e) { + assertEquals("The software system \"softwareSystem\" does not exist", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheElementIsNotASoftwareSystem() { + DslContext context = context(); + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("softwaresystem", model.addPerson("Name", "Description")); + context.setIdentifierRegister(elements); + + try { + parser.parse(context, tokens("systemContext", "softwareSystem", "key")); + fail(); + } catch (Exception e) { + assertEquals("The element \"softwareSystem\" is not a software system", e.getMessage()); + } + } + + @Test + void test_parse_CreatesASystemContextView() { + DslContext context = context(); + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("softwaresystem", model.addSoftwareSystem("Name", "Description")); + context.setIdentifierRegister(elements); + + parser.parse(context, tokens("systemContext", "softwareSystem")); + List<SystemContextView> views = new ArrayList<>(context.getWorkspace().getViews().getSystemContextViews()); + + assertEquals(1, views.size()); + assertEquals("SystemContext-001", views.get(0).getKey()); + assertEquals("", views.get(0).getDescription()); + } + + @Test + void test_parse_CreatesASystemContextViewWithAKey() { + DslContext context = context(); + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("softwaresystem", model.addSoftwareSystem("Name", "Description")); + context.setIdentifierRegister(elements); + + parser.parse(context, tokens("systemContext", "softwareSystem", "key")); + List<SystemContextView> views = new ArrayList<>(context.getWorkspace().getViews().getSystemContextViews()); + + assertEquals(1, views.size()); + assertEquals("key", views.get(0).getKey()); + assertEquals("", views.get(0).getDescription()); + } + + @Test + void test_parse_CreatesASystemContextViewWithAKeyAndDescription() { + DslContext context = context(); + IdentifiersRegister register = new IdentifiersRegister(); + register.register("softwaresystem", model.addSoftwareSystem("Name", "Description")); + context.setIdentifierRegister(register); + + parser.parse(context, tokens("systemContext", "softwareSystem", "key", "Description")); + List<SystemContextView> views = new ArrayList<>(context.getWorkspace().getViews().getSystemContextViews()); + + assertEquals(1, views.size()); + assertEquals("key", views.get(0).getKey()); + assertEquals("Description", views.get(0).getDescription()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/SystemLandscapeViewParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/SystemLandscapeViewParserTests.java new file mode 100644 index 000000000..5b00923cf --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/SystemLandscapeViewParserTests.java @@ -0,0 +1,60 @@ +package com.structurizr.dsl; + +import com.structurizr.view.SystemLandscapeView; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +class SystemLandscapeViewParserTests extends AbstractTests { + + private SystemLandscapeViewParser parser = new SystemLandscapeViewParser(); + + @Test + void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { + DslContext context = context(); + try { + parser.parse(context, tokens("systemLandscape", "key", "description", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: systemLandscape [key] [description] {", e.getMessage()); + } + } + + @Test + void test_parse_CreatesASystemLandscapeView() { + DslContext context = context(); + parser.parse(context, tokens("systemLandscape")); + List<SystemLandscapeView> views = new ArrayList<>(context.getWorkspace().getViews().getSystemLandscapeViews()); + + assertEquals(1, views.size()); + assertEquals("SystemLandscape-001", views.get(0).getKey()); + assertEquals("", views.get(0).getDescription()); + } + + @Test + void test_parse_CreatesASystemLandscapeViewWithAKey() { + DslContext context = context(); + parser.parse(context, tokens("systemLandscape", "key")); + List<SystemLandscapeView> views = new ArrayList<>(context.getWorkspace().getViews().getSystemLandscapeViews()); + + assertEquals(1, views.size()); + assertEquals("key", views.get(0).getKey()); + assertEquals("", views.get(0).getDescription()); + } + + @Test + void test_parse_CreatesASystemLandscapeViewWithAKeyAndDescription() { + DslContext context = context(); + parser.parse(context, tokens("systemLandscape", "key", "description")); + List<SystemLandscapeView> views = new ArrayList<>(context.getWorkspace().getViews().getSystemLandscapeViews()); + + assertEquals(1, views.size()); + assertEquals("key", views.get(0).getKey()); + assertEquals("description", views.get(0).getDescription()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/TerminologyParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/TerminologyParserTests.java new file mode 100644 index 000000000..dbff12115 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/TerminologyParserTests.java @@ -0,0 +1,148 @@ +package com.structurizr.dsl; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +class TerminologyParserTests extends AbstractTests { + + private TerminologyParser parser = new TerminologyParser(); + + @Test + void test_parseEnterprise_ThrowsAnException_WhenNoTermIsSpecified() { + try { + parser.parseEnterprise(context(), tokens("enterprise")); + fail(); + } catch (Exception e) { + assertEquals("Expected: enterprise <term>", e.getMessage()); + } + } + + @Test + void test_parseEnterprise_SetsTheTerm_WhenOneIsSpecified() { + parser.parseEnterprise(context(), tokens("enterprise", "TERM")); + + assertEquals("TERM", workspace.getViews().getConfiguration().getTerminology().getEnterprise()); + } + + @Test + void test_parsePerson_ThrowsAnException_WhenNoTermIsSpecified() { + try { + parser.parsePerson(context(), tokens("person")); + fail(); + } catch (Exception e) { + assertEquals("Expected: person <term>", e.getMessage()); + } + } + + @Test + void test_parsePerson_SetsTheTerm_WhenOneIsSpecified() { + parser.parsePerson(context(), tokens("person", "TERM")); + + assertEquals("TERM", workspace.getViews().getConfiguration().getTerminology().getPerson()); + } + + @Test + void test_parseSoftwareSystem_ThrowsAnException_WhenNoTermIsSpecified() { + try { + parser.parseSoftwareSystem(context(), tokens("softwareSystem")); + fail(); + } catch (Exception e) { + assertEquals("Expected: softwareSystem <term>", e.getMessage()); + } + } + + @Test + void test_parseSoftwareSystem_SetsTheTerm_WhenOneIsSpecified() { + parser.parseSoftwareSystem(context(), tokens("softwareSystem", "TERM")); + + assertEquals("TERM", workspace.getViews().getConfiguration().getTerminology().getSoftwareSystem()); + } + + @Test + void test_parseContainer_ThrowsAnException_WhenNoTermIsSpecified() { + try { + parser.parseContainer(context(), tokens("container")); + fail(); + } catch (Exception e) { + assertEquals("Expected: container <term>", e.getMessage()); + } + } + + @Test + void test_parseContainer_SetsTheTerm_WhenOneIsSpecified() { + parser.parseContainer(context(), tokens("container", "TERM")); + + assertEquals("TERM", workspace.getViews().getConfiguration().getTerminology().getContainer()); + } + + @Test + void test_parseComponent_ThrowsAnException_WhenNoTermIsSpecified() { + try { + parser.parseComponent(context(), tokens("component")); + fail(); + } catch (Exception e) { + assertEquals("Expected: component <term>", e.getMessage()); + } + } + + @Test + void test_parseComponent_SetsTheTerm_WhenOneIsSpecified() { + parser.parseComponent(context(), tokens("component", "TERM")); + + assertEquals("TERM", workspace.getViews().getConfiguration().getTerminology().getComponent()); + } + + @Test + void test_parsedeploymentNode_ThrowsAnException_WhenNoTermIsSpecified() { + try { + parser.parseDeploymentNode(context(), tokens("deploymentNode")); + fail(); + } catch (Exception e) { + assertEquals("Expected: deploymentNode <term>", e.getMessage()); + } + } + + @Test + void test_parsedeploymentNode_SetsTheTerm_WhenOneIsSpecified() { + parser.parseDeploymentNode(context(), tokens("deploymentNode", "TERM")); + + assertEquals("TERM", workspace.getViews().getConfiguration().getTerminology().getDeploymentNode()); + } + + @Test + void test_parseInfrastructureNode_ThrowsAnException_WhenNoTermIsSpecified() { + try { + parser.parseInfrastructureNode(context(), tokens("infrastructureNode")); + fail(); + } catch (Exception e) { + assertEquals("Expected: infrastructureNode <term>", e.getMessage()); + } + } + + @Test + void test_parseInfrastructureNode_SetsTheTerm_WhenOneIsSpecified() { + parser.parseInfrastructureNode(context(), tokens("infrastructureNode", "TERM")); + + assertEquals("TERM", workspace.getViews().getConfiguration().getTerminology().getInfrastructureNode()); + } + + @Test + void test_parseRelationship_ThrowsAnException_WhenNoTermIsSpecified() { + try { + parser.parseRelationship(context(), tokens("relationship")); + fail(); + } catch (Exception e) { + assertEquals("Expected: relationship <term>", e.getMessage()); + } + } + + @Test + void test_parseRelationship_SetsTheTerm_WhenOneIsSpecified() { + parser.parseRelationship(context(), tokens("relationship", "TERM")); + + assertEquals("TERM", workspace.getViews().getConfiguration().getTerminology().getRelationship()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ThemeParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ThemeParserTests.java new file mode 100644 index 000000000..46463d603 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ThemeParserTests.java @@ -0,0 +1,84 @@ +package com.structurizr.dsl; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +class ThemeParserTests extends AbstractTests { + + private ThemeParser parser = new ThemeParser(); + + @Test + void test_parseTheme_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parseTheme(context(), tokens("theme", "url", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: theme <url>", e.getMessage()); + } + } + + @Test + void test_parseTheme_ThrowsAnException_WhenNoThemeIsSpecified() { + try { + parser.parseTheme(context(), tokens("theme")); + fail(); + } catch (Exception e) { + assertEquals("Expected: theme <url>", e.getMessage()); + } + } + + @Test + void test_parseTheme_AddsTheTheme_WhenAThemeIsSpecified() { + parser.parseTheme(context(), tokens("theme", "http://example.com/1")); + + assertEquals(1, workspace.getViews().getConfiguration().getThemes().length); + assertEquals("http://example.com/1", workspace.getViews().getConfiguration().getThemes()[0]); + } + + @Test + void test_parseTheme_AddsTheTheme_WhenTheDefaultThemeIsSpecified() { + parser.parseTheme(context(), tokens("theme", "default")); + + assertEquals(1, workspace.getViews().getConfiguration().getThemes().length); + assertEquals("https://static.structurizr.com/themes/default/theme.json", workspace.getViews().getConfiguration().getThemes()[0]); + } + + @Test + void test_parseThemes_ThrowsAnException_WhenNoThemesAreSpecified() { + try { + parser.parseThemes(context(), tokens("themes")); + fail(); + } catch (Exception e) { + assertEquals("Expected: themes <url> [url] ... [url]", e.getMessage()); + } + } + + @Test + void test_parseThemes_AddsTheTheme_WhenOneThemeIsSpecified() { + parser.parseThemes(context(), tokens("themes", "http://example.com/1")); + + assertEquals(1, workspace.getViews().getConfiguration().getThemes().length); + assertEquals("http://example.com/1", workspace.getViews().getConfiguration().getThemes()[0]); + } + + @Test + void test_parseThemes_AddsTheThemes_WhenMultipleThemesAreSpecified() { + parser.parseThemes(context(), tokens("themes", "http://example.com/1", "http://example.com/2", "http://example.com/3")); + + assertEquals(3, workspace.getViews().getConfiguration().getThemes().length); + assertEquals("http://example.com/1", workspace.getViews().getConfiguration().getThemes()[0]); + assertEquals("http://example.com/2", workspace.getViews().getConfiguration().getThemes()[1]); + assertEquals("http://example.com/3", workspace.getViews().getConfiguration().getThemes()[2]); + } + + @Test + void test_parseThemes_AddsTheTheme_WhenTheDefaultThemeIsSpecified() { + parser.parseThemes(context(), tokens("themes", "default")); + + assertEquals(1, workspace.getViews().getConfiguration().getThemes().length); + assertEquals("https://static.structurizr.com/themes/default/theme.json", workspace.getViews().getConfiguration().getThemes()[0]); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/TokenizerTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/TokenizerTests.java new file mode 100644 index 000000000..71b316836 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/TokenizerTests.java @@ -0,0 +1,71 @@ +package com.structurizr.dsl; + +import com.structurizr.model.Location; +import com.structurizr.model.Person; +import com.structurizr.model.SoftwareSystem; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class TokenizerTests extends AbstractTests { + + @Test + void tokenize_ReturnsASingleToken_WhenTheLineIsOneUnquotedToken() { + List<String> tokens = new Tokenizer().tokenize("Hello"); + assertEquals(1, tokens.size()); + assertEquals("Hello", tokens.get(0)); + } + + @Test + void tokenize_ReturnsASingleToken_WhenTheLineIsOneUnquotedTokenWithEscapedQuoteCharacters() { + List<String> tokens = new Tokenizer().tokenize("\"Hello \\\"World\\\"\""); + assertEquals(1, tokens.size()); + assertEquals("Hello \\\"World\\\"", tokens.get(0)); + } + + @Test + void tokenize_ReturnsASingleToken_WhenTheLineIsTwoUnquotedTokens() { + List<String> tokens = new Tokenizer().tokenize("Hello World"); + assertEquals(2, tokens.size()); + assertEquals("Hello", tokens.get(0)); + assertEquals("World", tokens.get(1)); + } + + @Test + void tokenize_ReturnsTwoTokens_WhenTheLineIsOneQuotedToken() { + List<String> tokens = new Tokenizer().tokenize("\"Hello World\""); + assertEquals(1, tokens.size()); + assertEquals("Hello World", tokens.get(0)); + } + + @Test + void tokenize_ReturnsTwoTokens_WhenTheLineIsTwoQuotedTokens() { + List<String> tokens = new Tokenizer().tokenize("\"Hello\" \"World\""); + assertEquals(2, tokens.size()); + assertEquals("Hello", tokens.get(0)); + assertEquals("World", tokens.get(1)); + } + + @Test + void tokenize_ReturnsTokens_WhenTheLineIsSeveralQuotedTokens() { + List<String> tokens = new Tokenizer().tokenize("user = person \"User\""); + assertEquals(4, tokens.size()); + assertEquals("user", tokens.get(0)); + assertEquals("=", tokens.get(1)); + assertEquals("person", tokens.get(2)); + assertEquals("User", tokens.get(3)); + } + + @Test + void tokenize_ReturnsASingleToken_WhenTheLineIncludesTabCharacters() { + List<String> tokens = new Tokenizer().tokenize("\t\tuser\t=\tperson\t\"User\""); + assertEquals(4, tokens.size()); + assertEquals("user", tokens.get(0)); + assertEquals("=", tokens.get(1)); + assertEquals("person", tokens.get(2)); + assertEquals("User", tokens.get(3)); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/TokensTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/TokensTests.java new file mode 100644 index 000000000..0b03ba3c1 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/TokensTests.java @@ -0,0 +1,23 @@ +package com.structurizr.dsl; + +import org.junit.jupiter.api.Test; + +import java.util.Collections; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class TokensTests { + + @Test + void test_get_HandlesEscapedDoubleQuotes() { + Tokens tokens = new Tokens(Collections.singletonList("Hello \\\"World\\\"")); + assertEquals("Hello \"World\"", tokens.get(0)); + } + + @Test + void test_get_HandlesEscapedNewlines() { + Tokens tokens = new Tokens(Collections.singletonList("Hello\\nWorld")); + assertEquals("Hello\nWorld", tokens.get(0)); + } + +} diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/UserRoleParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/UserRoleParserTests.java new file mode 100644 index 000000000..df80a4b25 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/UserRoleParserTests.java @@ -0,0 +1,80 @@ +package com.structurizr.dsl; + +import com.structurizr.configuration.Role; +import com.structurizr.configuration.User; +import org.junit.jupiter.api.Test; + +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +class UserRoleParserTests extends AbstractTests { + + private UserRoleParser parser = new UserRoleParser(); + + @Test + void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parse(context(), tokens("username", "role", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: <username> <read|write>", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenNoUsernameIsSpecified() { + try { + parser.parse(context(), tokens("")); + fail(); + } catch (Exception e) { + assertEquals("Expected: <username> <read|write>", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenNoRoleIsSpecified() { + try { + parser.parse(context(), tokens("user@example.com")); + fail(); + } catch (Exception e) { + assertEquals("Expected: <username> <read|write>", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenAnInvalidRoleIsSpecified() { + try { + parser.parse(context(), tokens("user@example.com", "role")); + fail(); + } catch (Exception e) { + assertEquals("The role should be \"read\" or \"write\"", e.getMessage()); + } + } + + @Test + void test_parse_AddsAReadOnlyUser() { + parser.parse(context(), tokens("user@example.com", "read")); + + Set<User> users = context().getWorkspace().getConfiguration().getUsers(); + assertEquals(1, users.size()); + + User user = users.stream().findFirst().get(); + assertEquals("user@example.com", user.getUsername()); + assertEquals(Role.ReadOnly, user.getRole()); + } + + @Test + void test_parse_AddsAReadWriteUser() { + parser.parse(context(), tokens("user@example.com", "write")); + + Set<User> users = context().getWorkspace().getConfiguration().getUsers(); + assertEquals(1, users.size()); + + User user = users.stream().findFirst().get(); + assertEquals("user@example.com", user.getUsername()); + assertEquals(Role.ReadWrite, user.getRole()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ViewParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ViewParserTests.java new file mode 100644 index 000000000..bad186b03 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ViewParserTests.java @@ -0,0 +1,156 @@ +package com.structurizr.dsl; + +import com.structurizr.view.CustomView; +import com.structurizr.view.DeploymentView; +import com.structurizr.view.DynamicView; +import com.structurizr.view.SystemLandscapeView; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class ViewParserTests extends AbstractTests { + + private ViewParser parser = new ViewParser(); + + @Test + void test_parseTitle_ThrowsAnException_WhenThereAreTooManyTokens() { + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(null); + context.setWorkspace(workspace); + + try { + parser.parseTitle(context, tokens("title", "title", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: title <title>", e.getMessage()); + } + } + + @Test + void test_parseTitle_ThrowsAnException_WhenNoTitleIsSpecified() { + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "description"); + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + try { + parser.parseTitle(context, tokens("title")); + fail(); + } catch (Exception e) { + assertEquals("Expected: title <title>", e.getMessage()); + } + } + + @Test + void test_parseTitle_SetsTheTitleOfACustomView() { + CustomView view = workspace.getViews().createCustomView("key", "title", "description"); + CustomViewDslContext context = new CustomViewDslContext(view); + context.setWorkspace(workspace); + + assertEquals("title", view.getTitle()); + parser.parseTitle(context, tokens("title", "A new title")); + assertEquals("A new title", view.getTitle()); + } + + @Test + void test_parseTitle_SetsTheTitleOfAStaticView() { + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "description"); + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + assertNull(view.getTitle()); + parser.parseTitle(context, tokens("title", "A new title")); + assertEquals("A new title", view.getTitle()); + } + + @Test + void test_parseTitle_SetsTheTitleOfADynamicView() { + DynamicView view = workspace.getViews().createDynamicView("key", "description"); + DynamicViewDslContext context = new DynamicViewDslContext(view); + context.setWorkspace(workspace); + + assertNull(view.getTitle()); + parser.parseTitle(context, tokens("title", "A new title")); + assertEquals("A new title", view.getTitle()); + } + + @Test + void test_parseTitle_SetsTheTitleOfADeploymentView() { + DeploymentView view = workspace.getViews().createDeploymentView("key", "description"); + DeploymentViewDslContext context = new DeploymentViewDslContext(view); + context.setWorkspace(workspace); + + assertNull(view.getTitle()); + parser.parseTitle(context, tokens("title", "A new title")); + assertEquals("A new title", view.getTitle()); + } + + @Test + void test_parseDescription_ThrowsAnException_WhenThereAreTooManyTokens() { + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(null); + context.setWorkspace(workspace); + + try { + parser.parseDescription(context, tokens("description", "description", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: description <description>", e.getMessage()); + } + } + + @Test + void test_parseDescription_ThrowsAnException_WhenNoTitleIsSpecified() { + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "description"); + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + try { + parser.parseDescription(context, tokens("description")); + fail(); + } catch (Exception e) { + assertEquals("Expected: description <description>", e.getMessage()); + } + } + + @Test + void test_parseDescription_SetsTheTitleOfACustomView() { + CustomView view = workspace.getViews().createCustomView("key", "title", "description"); + CustomViewDslContext context = new CustomViewDslContext(view); + context.setWorkspace(workspace); + + assertEquals("description", view.getDescription()); + parser.parseDescription(context, tokens("description", "A new description")); + assertEquals("A new description", view.getDescription()); + } + + @Test + void test_parseDescription_SetsTheTitleOfAStaticView() { + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "description"); + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + assertEquals("description", view.getDescription()); + parser.parseDescription(context, tokens("description", "A new description")); + assertEquals("A new description", view.getDescription()); + } + + @Test + void test_parseDescription_SetsTheTitleOfADynamicView() { + DynamicView view = workspace.getViews().createDynamicView("key", "description"); + DynamicViewDslContext context = new DynamicViewDslContext(view); + context.setWorkspace(workspace); + + assertEquals("description", view.getDescription()); + parser.parseDescription(context, tokens("description", "A new description")); + assertEquals("A new description", view.getDescription()); + } + + @Test + void test_parseDescription_SetsTheTitleOfADeploymentView() { + DeploymentView view = workspace.getViews().createDeploymentView("key", "description"); + DeploymentViewDslContext context = new DeploymentViewDslContext(view); + context.setWorkspace(workspace); + + assertEquals("description", view.getDescription()); + parser.parseDescription(context, tokens("description", "A new description")); + assertEquals("A new description", view.getDescription()); + } +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/WorkspaceParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/WorkspaceParserTests.java new file mode 100644 index 000000000..009498f92 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/WorkspaceParserTests.java @@ -0,0 +1,105 @@ +package com.structurizr.dsl; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +class WorkspaceParserTests extends AbstractTests { + + private WorkspaceParser parser = new WorkspaceParser(); + + @Test + void test_parseTitle_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parse(null, tokens("workspace", "name", "description", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: workspace [name] [description] or workspace extends <file|url>", e.getMessage()); + } + } + + @Test + void test_parse_DoesNotThrowAnException_WhenNoPropertiesAreSpecified() { + assertEquals("Name", workspace.getName()); + assertEquals("Description", workspace.getDescription()); + workspace = parser.parse(null, tokens("workspace")); + assertEquals("Name", workspace.getName()); + assertEquals("Description", workspace.getDescription()); + } + + @Test + void test_parse_SetsTheWorkspaceName_WhenANameIsSpecified() { + assertEquals("Name", workspace.getName()); + assertEquals("Description", workspace.getDescription()); + workspace = parser.parse(null, tokens("workspace", "New Name")); + assertEquals("New Name", workspace.getName()); + assertEquals("Description", workspace.getDescription()); + } + + @Test + void test_parse_SetsTheWorkspaceNameAndDescription_WhenANameAndDescriptionAreSpecified() { + assertEquals("Name", workspace.getName()); + assertEquals("Description", workspace.getDescription()); + workspace = parser.parse(null, tokens("workspace", "New Name", "New Description")); + assertEquals("New Name", workspace.getName()); + assertEquals("New Description", workspace.getDescription()); + } + + @Test + void test_parseName_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + ModelItemDslContext context = new SoftwareSystemDslContext(null); + parser.parseName(context, tokens("name", "name", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: name <name>", e.getMessage()); + } + } + + @Test + void test_parseName_ThrowsAnException_WhenNoUrlIsSpecified() { + try { + parser.parseName(context(), tokens("name")); + fail(); + } catch (Exception e) { + assertEquals("Expected: name <name>", e.getMessage()); + } + } + + @Test + void test_parseName_SetsTheName_WhenANameIsSpecified() { + parser.parseName(context(), tokens("name", "A new name")); + + assertEquals("A new name", context().getWorkspace().getName()); + } + + @Test + void test_parseDescription_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + ModelItemDslContext context = new SoftwareSystemDslContext(null); + parser.parseDescription(context, tokens("description", "description", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: description <description>", e.getMessage()); + } + } + + @Test + void test_parseDescription_ThrowsAnException_WhenNoUrlIsSpecified() { + try { + parser.parseDescription(context(), tokens("description")); + fail(); + } catch (Exception e) { + assertEquals("Expected: description <description>", e.getMessage()); + } + } + + @Test + void test_parseDescription_SetsTheDescription_WhenADescriptionIsSpecified() { + parser.parseDescription(context(), tokens("description", "A new description")); + + assertEquals("A new description", context().getWorkspace().getDescription()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/example/ExampleDecisionImporter.java b/structurizr-dsl/src/test/java/com/structurizr/example/ExampleDecisionImporter.java new file mode 100644 index 000000000..e17d2370e --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/example/ExampleDecisionImporter.java @@ -0,0 +1,6 @@ +package com.structurizr.example; + +import com.structurizr.importer.documentation.AdrToolsDecisionImporter; + +public class ExampleDecisionImporter extends AdrToolsDecisionImporter { +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/example/ExampleDocumentationImporter.java b/structurizr-dsl/src/test/java/com/structurizr/example/ExampleDocumentationImporter.java new file mode 100644 index 000000000..59e58a1c4 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/example/ExampleDocumentationImporter.java @@ -0,0 +1,6 @@ +package com.structurizr.example; + +import com.structurizr.importer.documentation.DefaultDocumentationImporter; + +public class ExampleDocumentationImporter extends DefaultDocumentationImporter { +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/adrs/adrs/0001-record-architecture-decisions.md b/structurizr-dsl/src/test/resources/dsl/adrs/adrs/0001-record-architecture-decisions.md new file mode 100644 index 000000000..f30860000 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/adrs/adrs/0001-record-architecture-decisions.md @@ -0,0 +1,19 @@ +# 1. Record architecture decisions + +Date: 2016-02-12 + +## Status + +Accepted + +## Context + +We need to record the architectural decisions made on this project. + +## Decision + +We will use Architecture Decision Records, as described by Michael Nygard in this article: http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions + +## Consequences + +See Michael Nygard's article, linked above. diff --git a/structurizr-dsl/src/test/resources/dsl/adrs/adrs/0002-implement-as-shell-scripts.md b/structurizr-dsl/src/test/resources/dsl/adrs/adrs/0002-implement-as-shell-scripts.md new file mode 100644 index 000000000..8e6ea15e6 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/adrs/adrs/0002-implement-as-shell-scripts.md @@ -0,0 +1,28 @@ +# 2. Implement as shell scripts + +Date: 2016-02-12 + +## Status + +Accepted + +## Context + +ADRs are plain text files stored in a subdirectory of the project. + +The tool needs to create new files and apply small edits to +the Status section of existing files. + +## Decision + +The tool is implemented as shell scripts that use standard Unix +tools -- grep, sed, awk, etc. + +## Consequences + +The tool won't support Windows. Being plain text files, ADRs can +be created by hand and edited in any text editor. This tool just +makes the process more convenient. + +Development will have to cope with differences between Unix +variants, particularly Linux and MacOS X. diff --git a/structurizr-dsl/src/test/resources/dsl/adrs/adrs/0003-single-command-with-subcommands.md b/structurizr-dsl/src/test/resources/dsl/adrs/adrs/0003-single-command-with-subcommands.md new file mode 100644 index 000000000..f64db8da1 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/adrs/adrs/0003-single-command-with-subcommands.md @@ -0,0 +1,45 @@ +# 3. Single command with subcommands + +Date: 2016-02-12 + +## Status + +Accepted + +## Context + +The tool provides a number of related commands to create +and manipulate architecture decision records. + +How can the user find out about the commands that are available? + +## Decision + +The tool defines a single command, called `adr`. + +The first argument to `adr` (the subcommand) specifies the +action to perform. Further arguments are interpreted by the +subcommand. + +Running `adr` without any arguments lists the available +subcommands. + +Subcommands are implemented as scripts in the same +directory as the `adr` script. E.g. the subcommand `new` is +implemented as the script `adr-new`, the subcommand `help` +as the script `adr-help` and so on. + +Helper scripts that are part of the implementation but not +subcommands follow a different naming convention, so that +subcommands can be listed by filtering and transforming script +file names. + +## Consequences + +Users can more easily explore the capabilities of the tool. + +Users are already used to this style of command-line tool. For +example, Git works this way. + +Each subcommand can be implemented in the most appropriate +language. diff --git a/structurizr-dsl/src/test/resources/dsl/adrs/adrs/0004-markdown-format.md b/structurizr-dsl/src/test/resources/dsl/adrs/adrs/0004-markdown-format.md new file mode 100644 index 000000000..86a21bf7e --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/adrs/adrs/0004-markdown-format.md @@ -0,0 +1,40 @@ +# 4. Markdown format + +Date: 2016-02-12 + +## Status + +Superceded by [10. AsciiDoc format](0010-asciidoc-format.md) + +## Context + +The decision records must be stored in a plain text format: + +* This works well with version control systems. + +* It allows the tool to modify the status of records and insert + hyperlinks when one decision supercedes another. + +* Decisions can be read in the terminal, IDE, version control + browser, etc. + +People will want to use some formatting: lists, code examples, +and so on. + +People will want to view the decision records in a more readable +format than plain text, and maybe print them out. + + +## Decision + +Record architecture decisions in [Markdown format](https://daringfireball.net/projects/markdown/). + +## Consequences + +Decisions can be read in the terminal. + +Decisions will be formatted nicely and hyperlinked by the +browsers of project hosting sites like GitHub and Bitbucket. + +Tools like [Pandoc](http://pandoc.org/) can be used to convert +the decision records into HTML or PDF. diff --git a/structurizr-dsl/src/test/resources/dsl/adrs/adrs/0005-help-comments.md b/structurizr-dsl/src/test/resources/dsl/adrs/adrs/0005-help-comments.md new file mode 100644 index 000000000..b19bf0fb1 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/adrs/adrs/0005-help-comments.md @@ -0,0 +1,42 @@ +# 5. Help comments + +Date: 2016-02-13 + +## Status + +Accepted + +Amended by [9. Help scripts](0009-help-scripts.md) + +## Context + +The tool will have a `help` subcommand to provide documentation +for users. + +It's nice to have usage documentation in the script files +themselves, in comments. When reading the code, that's the first +place to look for information about how to run a script. + +## Decision + +Write usage documentation in comments in the source file. + +Distinguish between documentation comments and normal comments. +Documentation comments have two hash characters at the start of +the line. + +The `adr help` command can parse comments out from the script +using the standard Unix tools `grep` and `cut`. + +## Consequences + +No need to maintain help text in a separate file. + +Help text can easily be kept up to date as the script is edited. + +There's no automated check that the help text is up to date. The +tests do not work well as documentation for users, and the help +text is not easily cross-checked against the code. + +This won't work if any subcommands are not implemented as scripts +that use '#' as a comment character. diff --git a/structurizr-dsl/src/test/resources/dsl/adrs/adrs/0006-packaging-and-distribution-in-other-version-control-repositories.md b/structurizr-dsl/src/test/resources/dsl/adrs/adrs/0006-packaging-and-distribution-in-other-version-control-repositories.md new file mode 100644 index 000000000..4a3485f79 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/adrs/adrs/0006-packaging-and-distribution-in-other-version-control-repositories.md @@ -0,0 +1,41 @@ +# 6. Packaging and distribution in other version control repositories + +Date: 2016-02-16 + +## Status + +Accepted + +## Context + +Users want to install adr-tools with their preferred package +manager. For example, Ubuntu users use `apt`, RedHat users use +`yum` and Mac OS X users use [Homebrew](http://brew.sh). + +The developers of `adr-tools` don't know how, nor have permissions, +to use all these packaging and distribution systems. Therefore packaging +and distribution must be done by "downstream" parties. + +The developers of the tool should not favour any one particular +packaging and distribution solution. + +## Decision + +The `adr-tools` project will not contain any packaging or +distribution scripts and config. + +Packaging and distribution will be managed by other projects in +separate version control repositories. + +## Consequences + +The git repo of this project will be simpler. + +Eventually, users will not have to use Git to get the software. + +We will have to tag releases in the `adr-tools` repository so that +packaging projects know what can be published and how it should be +identified. + +We will document how users can install the software in this +project's README file. diff --git a/structurizr-dsl/src/test/resources/dsl/adrs/adrs/0007-invoke-adr-config-executable-to-get-configuration.md b/structurizr-dsl/src/test/resources/dsl/adrs/adrs/0007-invoke-adr-config-executable-to-get-configuration.md new file mode 100644 index 000000000..a649b2356 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/adrs/adrs/0007-invoke-adr-config-executable-to-get-configuration.md @@ -0,0 +1,31 @@ +# 7. Invoke adr-config executable to get configuration + +Date: 2016-12-17 + +## Status + +Accepted + +## Context + +Packagers (e.g. Homebrew developers) want to configure adr-tools to match the conventions of their installation. + +Currently, this is done by sourcing a file `config.sh`, which should sit beside the `adr` executable. + +This name is too common. + +The `config.sh` file is not executable, and so doesn't belong in a bin directory. + +## Decision + +Replace `config.sh` with an executable, named `adr-config` that outputs configuration. + +Each script in ADR Tools will eval the output of `adr-config` to configure itself. + +## Consequences + +Configuration within ADR Tools is a little more complicated. + +Packagers can write their own implementation of `adr-config` that outputs configuration that matches the platform's installation conventions, and deploy it next to the `adr` script. + +To make development easier, the implementation of `adr-config` in the project's src/ directory will output configuration that lets the tool to run from the src/ directory without any installation step. (Packagers should not include this script in a deployable package.) diff --git a/structurizr-dsl/src/test/resources/dsl/adrs/adrs/0008-use-iso-8601-format-for-dates.md b/structurizr-dsl/src/test/resources/dsl/adrs/adrs/0008-use-iso-8601-format-for-dates.md new file mode 100644 index 000000000..4146f11df --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/adrs/adrs/0008-use-iso-8601-format-for-dates.md @@ -0,0 +1,43 @@ +# 8. Use ISO 8601 Format for Dates + +Date: 2017-02-21 + +## Status + +Accepted + +## Context + +`adr-tools` seeks to communicate the history of architectural decisions of a +project. An important component of the history is the time at which a decision +was made. + +To communicate effectively, `adr-tools` should present information as +unambiguously as possible. That means that culture-neutral data formats should +be preferred over culture-specific formats. + +Existing `adr-tools` deployments format dates as `dd/mm/yyyy` by default. That +formatting is common formatting in the United Kingdom (where the `adr-tools` +project was originally written), but is easily confused with the `mm/dd/yyyy` +format preferred in the United States. + +The default date format may be overridden by setting `ADR_DATE` in `config.sh`. + +## Decision + +`adr-tools` will use the ISO 8601 format for dates: `yyyy-mm-dd` + +## Consequences + +Dates are displayed in a standard, culture-neutral format. + +The UK-style and ISO 8601 formats can be distinguished by their separator +character. The UK-style dates used a slash (`/`), while the ISO dates use a +hyphen (`-`). + +Prior to this decision, `adr-tools` was deployed using the UK format for dates. +After adopting the ISO 8601 format, existing deployments of `adr-tools` must do +one of the following: + + * Accept mixed formatting of dates within their documentation library. + * Update existing documents to use ISO 8601 dates by running `adr upgrade-repository` diff --git a/structurizr-dsl/src/test/resources/dsl/adrs/adrs/0009-help-scripts.md b/structurizr-dsl/src/test/resources/dsl/adrs/adrs/0009-help-scripts.md new file mode 100644 index 000000000..0146d127c --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/adrs/adrs/0009-help-scripts.md @@ -0,0 +1,28 @@ +# 9. Help scripts + +Date: 2018-06-26 + +## Status + +Accepted + +Amends [5. Help comments](0005-help-comments.md) + +## Context + +Currently help text is generated by extracting specially formatted comments from the top of the command script. + +This makes it easy for developers of the tool: documentation and code is all in one place. + +But, it means that help text cannot include calculated values, such as the location of files. + +## Decision + +Where necessary, help text can be generated by a script. + +The script will be called _adr_help_<command>_<subcommand> + +## Consequences + +Help scripts can include helper scripts to locate files, giving more accurate instructions to the user that reflect how the tool is deployed in their environment. + diff --git a/structurizr-dsl/src/test/resources/dsl/adrs/adrs/0010-asciidoc-format.md b/structurizr-dsl/src/test/resources/dsl/adrs/adrs/0010-asciidoc-format.md new file mode 100644 index 000000000..edb613d1a --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/adrs/adrs/0010-asciidoc-format.md @@ -0,0 +1,21 @@ +# 10. AsciiDoc format + +Date: 2018-08-11 + +## Status + +Accepted + +Supercedes [4. Markdown format](0004-markdown-format.md) + +## Context + +The issue motivating this decision, and any context that influences or constrains the decision. + +## Decision + +The change that we're proposing or have agreed to implement. + +## Consequences + +What becomes easier or more difficult to do and any risks introduced by the change that will need to be mitigated. diff --git a/structurizr-dsl/src/test/resources/dsl/adrs/workspace.dsl b/structurizr-dsl/src/test/resources/dsl/adrs/workspace.dsl new file mode 100644 index 000000000..a27b08488 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/adrs/workspace.dsl @@ -0,0 +1,19 @@ +workspace { + + !adrs adrs com.structurizr.example.ExampleDecisionImporter + + model { + softwareSystem = softwareSystem "Software System" { + !adrs adrs + + container "Container" { + !adrs adrs + + component "Component" { + !adrs adrs + } + } + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/amazon-web-services-local.dsl b/structurizr-dsl/src/test/resources/dsl/amazon-web-services-local.dsl new file mode 100644 index 000000000..6cb363198 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/amazon-web-services-local.dsl @@ -0,0 +1,65 @@ +workspace "Amazon Web Services Example" "An example AWS deployment architecture." { + + !identifiers hierarchical + + model { + springPetClinic = softwaresystem "Spring PetClinic" "Allows employees to view and manage information regarding the veterinarians, the clients, and their pets." "Spring Boot Application" { + webApplication = container "Web Application" "Allows employees to view and manage information regarding the veterinarians, the clients, and their pets." "Java and Spring Boot" + database = container "Database" "Stores information regarding the veterinarians, the clients, and their pets." "Relational database schema" "Database" + webApplication -> database "Reads from and writes to" "JDBC/SSL" + } + + live = deploymentEnvironment "Live" { + aws = deploymentNode "Amazon Web Services" "" "" "Amazon Web Services - Cloud" { + region = deploymentNode "US-East-1" "" "" "Amazon Web Services - Region" { + route53 = infrastructureNode "Route 53" "" "" "Amazon Web Services - Route 53" + elb = infrastructureNode "Elastic Load Balancer" "" "" "Amazon Web Services - Elastic Load Balancing" + + autoscalingGroup = deploymentNode "Autoscaling group" "" "" "Amazon Web Services - Auto Scaling" { + ec2 = deploymentNode "Amazon EC2" "" "" "Amazon Web Services - EC2" { + webApplicationInstance = containerInstance springPetClinic.webApplication + elb -> webApplicationInstance "Forwards requests to" "HTTPS" + } + } + + rds = deploymentNode "Amazon RDS" "" "" "Amazon Web Services - RDS" { + mysql = deploymentNode "MySQL" "" "" "Amazon Web Services - RDS MySQL instance" { + databaseInstance = containerInstance springPetClinic.database + } + } + + route53 -> elb "Forwards requests to" "HTTPS" + } + } + } + } + + views { + deployment springPetClinic "Live" "AmazonWebServicesDeployment" { + include * + autolayout lr + + animation { + live.aws.region.route53 + live.aws.region.elb + live.aws.region.autoscalingGroup.ec2.webApplicationInstance + live.aws.region.rds.mysql.databaseInstance + } + } + + styles { + element "Element" { + shape roundedbox + background #ffffff + } + element "Database" { + shape cylinder + } + element "Infrastructure Node" { + shape roundedbox + } + } + + themes https://static.structurizr.com/themes/amazon-web-services-2020.04.30/theme.json + } +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/amazon-web-services.dsl b/structurizr-dsl/src/test/resources/dsl/amazon-web-services.dsl new file mode 100644 index 000000000..78948465d --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/amazon-web-services.dsl @@ -0,0 +1,84 @@ +workspace "Amazon Web Services Example" "An example AWS deployment architecture." { + + model { + springPetClinic = softwaresystem "Spring PetClinic" "Allows employees to view and manage information regarding the veterinarians, the clients, and their pets." "Spring Boot Application" { + webApplication = container "Web Application" "Allows employees to view and manage information regarding the veterinarians, the clients, and their pets." "Java and Spring Boot" + database = container "Database" "Stores information regarding the veterinarians, the clients, and their pets." "Relational database schema" "Database" + } + + webApplication -> database "Reads from and writes to" "MySQL Protocol/SSL" + + live = deploymentEnvironment "Live" { + + deploymentNode "Amazon Web Services" { + tags "Amazon Web Services - Cloud" + + region = deploymentNode "US-East-1" { + tags "Amazon Web Services - Region" + + route53 = infrastructureNode "Route 53" { + tags "Amazon Web Services - Route 53" + } + + elb = infrastructureNode "Elastic Load Balancer" { + tags "Amazon Web Services - Elastic Load Balancing" + } + + deploymentNode "Autoscaling group" { + tags "Amazon Web Services - Auto Scaling" + + deploymentNode "Amazon EC2" { + tags "Amazon Web Services - EC2" + + webApplicationInstance = containerInstance webApplication + } + } + + deploymentNode "Amazon RDS" { + tags "Amazon Web Services - RDS" + + deploymentNode "MySQL" { + tags "Amazon Web Services - RDS MySQL instance" + + databaseInstance = containerInstance database + } + } + + } + } + + route53 -> elb "Forwards requests to" "HTTPS" + elb -> webApplicationInstance "Forwards requests to" "HTTPS" + } + } + + views { + deployment springPetClinic "Live" "AmazonWebServicesDeployment" { + include * + autolayout lr + + animation { + route53 + elb + webApplicationInstance + databaseInstance + } + } + + styles { + element "Element" { + shape roundedbox + background #ffffff + } + element "Database" { + shape cylinder + } + element "Infrastructure Node" { + shape roundedbox + } + } + + themes https://static.structurizr.com/themes/amazon-web-services-2020.04.30/theme.json + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/big-bank-plc.dsl b/structurizr-dsl/src/test/resources/dsl/big-bank-plc.dsl new file mode 100644 index 000000000..2e6783160 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/big-bank-plc.dsl @@ -0,0 +1,249 @@ +/* + * This is a combined version of the following workspaces: + * + * - "Big Bank plc - System Landscape" (https://structurizr.com/share/28201/) + * - "Big Bank plc - Internet Banking System" (https://structurizr.com/share/36141/) +*/ +workspace "Big Bank plc" "This is an example workspace to illustrate the key features of Structurizr, via the DSL, based around a fictional online banking system." { + + model { + customer = person "Personal Banking Customer" "A customer of the bank, with personal bank accounts." "Customer" + + enterprise "Big Bank plc" { + supportStaff = person "Customer Service Staff" "Customer service staff within the bank." "Bank Staff" + backoffice = person "Back Office Staff" "Administration and support staff within the bank." "Bank Staff" + + mainframe = softwaresystem "Mainframe Banking System" "Stores all of the core banking information about customers, accounts, transactions, etc." "Existing System" + email = softwaresystem "E-mail System" "The internal Microsoft Exchange e-mail system." "Existing System" + atm = softwaresystem "ATM" "Allows customers to withdraw cash." "Existing System" + + internetBankingSystem = softwaresystem "Internet Banking System" "Allows customers to view information about their bank accounts, and make payments." { + singlePageApplication = container "Single-Page Application" "Provides all of the Internet banking functionality to customers via their web browser." "JavaScript and Angular" "Web Browser" + mobileApp = container "Mobile App" "Provides a limited subset of the Internet banking functionality to customers via their mobile device." "Xamarin" "Mobile App" + webApplication = container "Web Application" "Delivers the static content and the Internet banking single page application." "Java and Spring MVC" + apiApplication = container "API Application" "Provides Internet banking functionality via a JSON/HTTPS API." "Java and Spring MVC" { + signinController = component "Sign In Controller" "Allows users to sign in to the Internet Banking System." "Spring MVC Rest Controller" + accountsSummaryController = component "Accounts Summary Controller" "Provides customers with a summary of their bank accounts." "Spring MVC Rest Controller" + resetPasswordController = component "Reset Password Controller" "Allows users to reset their passwords with a single use URL." "Spring MVC Rest Controller" + securityComponent = component "Security Component" "Provides functionality related to signing in, changing passwords, etc." "Spring Bean" + mainframeBankingSystemFacade = component "Mainframe Banking System Facade" "A facade onto the mainframe banking system." "Spring Bean" + emailComponent = component "E-mail Component" "Sends e-mails to users." "Spring Bean" + } + database = container "Database" "Stores user registration information, hashed authentication credentials, access logs, etc." "Oracle Database Schema" "Database" + } + } + + # relationships between people and software systems + customer -> internetBankingSystem "Views account balances, and makes payments using" + internetBankingSystem -> mainframe "Gets account information from, and makes payments using" + internetBankingSystem -> email "Sends e-mail using" + email -> customer "Sends e-mails to" + customer -> supportStaff "Asks questions to" "Telephone" + supportStaff -> mainframe "Uses" + customer -> atm "Withdraws cash using" + atm -> mainframe "Uses" + backoffice -> mainframe "Uses" + + # relationships to/from containers + customer -> webApplication "Visits bigbank.com/ib using" "HTTPS" + customer -> singlePageApplication "Views account balances, and makes payments using" + customer -> mobileApp "Views account balances, and makes payments using" + webApplication -> singlePageApplication "Delivers to the customer's web browser" + + # relationships to/from components + singlePageApplication -> signinController "Makes API calls to" "JSON/HTTPS" + singlePageApplication -> accountsSummaryController "Makes API calls to" "JSON/HTTPS" + singlePageApplication -> resetPasswordController "Makes API calls to" "JSON/HTTPS" + mobileApp -> signinController "Makes API calls to" "JSON/HTTPS" + mobileApp -> accountsSummaryController "Makes API calls to" "JSON/HTTPS" + mobileApp -> resetPasswordController "Makes API calls to" "JSON/HTTPS" + signinController -> securityComponent "Uses" + accountsSummaryController -> mainframeBankingSystemFacade "Uses" + resetPasswordController -> securityComponent "Uses" + resetPasswordController -> emailComponent "Uses" + securityComponent -> database "Reads from and writes to" "JDBC" + mainframeBankingSystemFacade -> mainframe "Makes API calls to" "XML/HTTPS" + emailComponent -> email "Sends e-mail using" + + deploymentEnvironment "Development" { + deploymentNode "Developer Laptop" "" "Microsoft Windows 10 or Apple macOS" { + deploymentNode "Web Browser" "" "Chrome, Firefox, Safari, or Edge" { + developerSinglePageApplicationInstance = containerInstance singlePageApplication + } + deploymentNode "Docker Container - Web Server" "" "Docker" { + deploymentNode "Apache Tomcat" "" "Apache Tomcat 8.x" { + developerWebApplicationInstance = containerInstance webApplication + developerApiApplicationInstance = containerInstance apiApplication + } + } + deploymentNode "Docker Container - Database Server" "" "Docker" { + deploymentNode "Database Server" "" "Oracle 12c" { + developerDatabaseInstance = containerInstance database + } + } + } + deploymentNode "Big Bank plc" "" "Big Bank plc data center" "" { + deploymentNode "bigbank-dev001" "" "" "" { + softwareSystemInstance mainframe + } + } + + } + + deploymentEnvironment "Live" { + deploymentNode "Customer's mobile device" "" "Apple iOS or Android" { + liveMobileAppInstance = containerInstance mobileApp + } + deploymentNode "Customer's computer" "" "Microsoft Windows or Apple macOS" { + deploymentNode "Web Browser" "" "Chrome, Firefox, Safari, or Edge" { + liveSinglePageApplicationInstance = containerInstance singlePageApplication + } + } + + deploymentNode "Big Bank plc" "" "Big Bank plc data center" { + deploymentNode "bigbank-web***" "" "Ubuntu 16.04 LTS" "" 4 { + deploymentNode "Apache Tomcat" "" "Apache Tomcat 8.x" { + liveWebApplicationInstance = containerInstance webApplication + } + } + deploymentNode "bigbank-api***" "" "Ubuntu 16.04 LTS" "" 8 { + deploymentNode "Apache Tomcat" "" "Apache Tomcat 8.x" { + liveApiApplicationInstance = containerInstance apiApplication + } + } + + deploymentNode "bigbank-db01" "" "Ubuntu 16.04 LTS" { + primaryDatabaseServer = deploymentNode "Oracle - Primary" "" "Oracle 12c" { + livePrimaryDatabaseInstance = containerInstance database + } + } + deploymentNode "bigbank-db02" "" "Ubuntu 16.04 LTS" "Failover" { + secondaryDatabaseServer = deploymentNode "Oracle - Secondary" "" "Oracle 12c" "Failover" { + liveSecondaryDatabaseInstance = containerInstance database "Failover" + } + } + deploymentNode "bigbank-prod001" "" "" "" { + softwareSystemInstance mainframe + } + } + + primaryDatabaseServer -> secondaryDatabaseServer "Replicates data to" + } + } + + views { + systemlandscape "SystemLandscape" { + include * + autoLayout + } + + systemcontext internetBankingSystem "SystemContext" { + include * + animation { + internetBankingSystem + customer + mainframe + email + } + autoLayout + } + + container internetBankingSystem "Containers" { + include * + animation { + customer mainframe email + webApplication + singlePageApplication + mobileApp + apiApplication + database + } + autoLayout + } + + component apiApplication "Components" { + include * + animation { + singlePageApplication mobileApp database email mainframe + signinController securityComponent + accountsSummaryController mainframeBankingSystemFacade + resetPasswordController emailComponent + } + autoLayout + } + + dynamic apiApplication "SignIn" "Summarises how the sign in feature works in the single-page application." { + singlePageApplication -> signinController "Submits credentials to" + signinController -> securityComponent "Validates credentials using" + securityComponent -> database "select * from users where username = ?" + database -> securityComponent "Returns user data to" + securityComponent -> signinController "Returns true if the hashed password matches" + signinController -> singlePageApplication "Sends back an authentication token to" + autoLayout + } + + deployment internetBankingSystem "Development" "DevelopmentDeployment" { + include * + animation { + developerSinglePageApplicationInstance + developerWebApplicationInstance developerApiApplicationInstance + developerDatabaseInstance + } + autoLayout + } + + deployment internetBankingSystem "Live" "LiveDeployment" { + include * + animation { + liveSinglePageApplicationInstance + liveMobileAppInstance + liveWebApplicationInstance liveApiApplicationInstance + livePrimaryDatabaseInstance + liveSecondaryDatabaseInstance + } + autoLayout + } + + styles { + element "Person" { + color #ffffff + fontSize 22 + shape Person + } + element "Customer" { + background #08427b + } + element "Bank Staff" { + background #999999 + } + element "Software System" { + background #1168bd + color #ffffff + } + element "Existing System" { + background #999999 + color #ffffff + } + element "Container" { + background #438dd5 + color #ffffff + } + element "Web Browser" { + shape WebBrowser + } + element "Mobile App" { + shape MobileDeviceLandscape + } + element "Database" { + shape Cylinder + } + element "Component" { + background #85bbf0 + color #000000 + } + element "Failover" { + opacity 25 + } + } + } +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/big-bank-plc/internet-banking-system.dsl b/structurizr-dsl/src/test/resources/dsl/big-bank-plc/internet-banking-system.dsl new file mode 100644 index 000000000..140b822ae --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/big-bank-plc/internet-banking-system.dsl @@ -0,0 +1,189 @@ +!constant INTERNET_BANKING_SYSTEM_INCLUDE "details.dsl" + +workspace "Big Bank plc - Internet Banking System" "The software architecture of the Big Bank plc Internet Banking System." { + + model { + !include model/people-and-software-systems.dsl + + # relationships to/from containers + customer -> webApplication "Visits bigbank.com/ib using" "HTTPS" + customer -> singlePageApplication "Views account balances, and makes payments using" + customer -> mobileApp "Views account balances, and makes payments using" + webApplication -> singlePageApplication "Delivers to the customer's web browser" + + # relationships to/from components + singlePageApplication -> signinController "Makes API calls to" "JSON/HTTPS" + singlePageApplication -> accountsSummaryController "Makes API calls to" "JSON/HTTPS" + singlePageApplication -> resetPasswordController "Makes API calls to" "JSON/HTTPS" + mobileApp -> signinController "Makes API calls to" "JSON/HTTPS" + mobileApp -> accountsSummaryController "Makes API calls to" "JSON/HTTPS" + mobileApp -> resetPasswordController "Makes API calls to" "JSON/HTTPS" + signinController -> securityComponent "Uses" + accountsSummaryController -> mainframeBankingSystemFacade "Uses" + resetPasswordController -> securityComponent "Uses" + resetPasswordController -> emailComponent "Uses" + securityComponent -> database "Reads from and writes to" "JDBC" + mainframeBankingSystemFacade -> mainframe "Makes API calls to" "XML/HTTPS" + emailComponent -> email "Sends e-mail using" + + deploymentEnvironment "Development" { + deploymentNode "Developer Laptop" "" "Microsoft Windows 10 or Apple macOS" { + deploymentNode "Web Browser" "" "Chrome, Firefox, Safari, or Edge" { + developerSinglePageApplicationInstance = containerInstance singlePageApplication + } + deploymentNode "Docker Container - Web Server" "" "Docker" { + deploymentNode "Apache Tomcat" "" "Apache Tomcat 8.x" { + developerWebApplicationInstance = containerInstance webApplication + developerApiApplicationInstance = containerInstance apiApplication + } + } + deploymentNode "Docker Container - Database Server" "" "Docker" { + deploymentNode "Database Server" "" "Oracle 12c" { + developerDatabaseInstance = containerInstance database + } + } + } + deploymentNode "Big Bank plc" "" "Big Bank plc data center" "" { + deploymentNode "bigbank-dev001" "" "" "" { + softwareSystemInstance mainframe + } + } + } + + deploymentEnvironment "Live" { + deploymentNode "Customer's mobile device" "" "Apple iOS or Android" { + liveMobileAppInstance = containerInstance mobileApp + } + deploymentNode "Customer's computer" "" "Microsoft Windows or Apple macOS" { + deploymentNode "Web Browser" "" "Chrome, Firefox, Safari, or Edge" { + liveSinglePageApplicationInstance = containerInstance singlePageApplication + } + } + + deploymentNode "Big Bank plc" "" "Big Bank plc data center" { + deploymentNode "bigbank-web***" "" "Ubuntu 16.04 LTS" "" 4 { + deploymentNode "Apache Tomcat" "" "Apache Tomcat 8.x" { + liveWebApplicationInstance = containerInstance webApplication + } + } + deploymentNode "bigbank-api***" "" "Ubuntu 16.04 LTS" "" 8 { + deploymentNode "Apache Tomcat" "" "Apache Tomcat 8.x" { + liveApiApplicationInstance = containerInstance apiApplication + } + } + + deploymentNode "bigbank-db01" "" "Ubuntu 16.04 LTS" { + primaryDatabaseServer = deploymentNode "Oracle - Primary" "" "Oracle 12c" { + livePrimaryDatabaseInstance = containerInstance database + } + } + deploymentNode "bigbank-db02" "" "Ubuntu 16.04 LTS" "Failover" { + secondaryDatabaseServer = deploymentNode "Oracle - Secondary" "" "Oracle 12c" "Failover" { + liveSecondaryDatabaseInstance = containerInstance database "Failover" + } + } + deploymentNode "bigbank-prod001" "" "" "" { + softwareSystemInstance mainframe + } + } + + primaryDatabaseServer -> secondaryDatabaseServer "Replicates data to" + } + } + + views { + systemcontext internetBankingSystem "SystemContext" { + include * + animation { + internetBankingSystem + customer + mainframe + email + } + } + + container internetBankingSystem "Containers" { + include * + animation { + customer mainframe email + webApplication + singlePageApplication + mobileApp + apiApplication + database + } + } + + component apiApplication "Components" { + include * + animation { + singlePageApplication mobileApp database email mainframe + signinController securityComponent + accountsSummaryController mainframeBankingSystemFacade + resetPasswordController emailComponent + } + } + + dynamic apiApplication "SignIn" "Summarises how the sign in feature works in the single-page application." { + singlePageApplication -> signinController "Submits credentials to" + signinController -> securityComponent "Validates credentials using" + securityComponent -> database "select * from users where username = ?" + database -> securityComponent "Returns user data to" + securityComponent -> signinController "Returns true if the hashed password matches" + signinController -> singlePageApplication "Sends back an authentication token to" + } + + deployment internetBankingSystem "Development" "DevelopmentDeployment" { + include * + animation { + developerSinglePageApplicationInstance + developerWebApplicationInstance developerApiApplicationInstance + developerDatabaseInstance + } + } + + deployment internetBankingSystem "Live" "LiveDeployment" { + include * + animation { + liveSinglePageApplicationInstance + liveMobileAppInstance + liveWebApplicationInstance liveApiApplicationInstance + livePrimaryDatabaseInstance + liveSecondaryDatabaseInstance + } + } + + styles { + !include views/styles-people.dsl + + element "Software System" { + background #1168bd + color #ffffff + } + element "Existing System" { + background #999999 + color #ffffff + } + element "Container" { + background #438dd5 + color #ffffff + } + element "Web Browser" { + shape WebBrowser + } + element "Mobile App" { + shape MobileDeviceLandscape + } + element "Database" { + shape Cylinder + } + element "Component" { + background #85bbf0 + color #000000 + } + element "Failover" { + opacity 25 + } + } + } +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/big-bank-plc/model/internet-banking-system/adrs/0001-record-architecture-decisions.md b/structurizr-dsl/src/test/resources/dsl/big-bank-plc/model/internet-banking-system/adrs/0001-record-architecture-decisions.md new file mode 100644 index 000000000..7461d99d3 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/big-bank-plc/model/internet-banking-system/adrs/0001-record-architecture-decisions.md @@ -0,0 +1,19 @@ +# 1. Record architecture decisions + +Date: 2020-06-05 + +## Status + +Accepted + +## Context + +We need to record the architectural decisions made on this project. + +## Decision + +We will use Architecture Decision Records, as described by Michael Nygard in this article: [http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions) + +## Consequences + +See Michael Nygard's article, linked above. \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/big-bank-plc/model/internet-banking-system/details.dsl b/structurizr-dsl/src/test/resources/dsl/big-bank-plc/model/internet-banking-system/details.dsl new file mode 100644 index 000000000..c3b491544 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/big-bank-plc/model/internet-banking-system/details.dsl @@ -0,0 +1,15 @@ +singlePageApplication = container "Single-Page Application" "Provides all of the Internet banking functionality to customers via their web browser." "JavaScript and Angular" "Web Browser" +mobileApp = container "Mobile App" "Provides a limited subset of the Internet banking functionality to customers via their mobile device." "Xamarin" "Mobile App" +webApplication = container "Web Application" "Delivers the static content and the Internet banking single page application." "Java and Spring MVC" +apiApplication = container "API Application" "Provides Internet banking functionality via a JSON/HTTPS API." "Java and Spring MVC" { + signinController = component "Sign In Controller" "Allows users to sign in to the Internet Banking System." "Spring MVC Rest Controller" + accountsSummaryController = component "Accounts Summary Controller" "Provides customers with a summary of their bank accounts." "Spring MVC Rest Controller" + resetPasswordController = component "Reset Password Controller" "Allows users to reset their passwords with a single use URL." "Spring MVC Rest Controller" + securityComponent = component "Security Component" "Provides functionality related to signing in, changing passwords, etc." "Spring Bean" + mainframeBankingSystemFacade = component "Mainframe Banking System Facade" "A facade onto the mainframe banking system." "Spring Bean" + emailComponent = component "E-mail Component" "Sends e-mails to users." "Spring Bean" +} +database = container "Database" "Stores user registration information, hashed authentication credentials, access logs, etc." "Oracle Database Schema" "Database" + +!docs docs +!adrs adrs \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/big-bank-plc/model/internet-banking-system/docs/01-context.md b/structurizr-dsl/src/test/resources/dsl/big-bank-plc/model/internet-banking-system/docs/01-context.md new file mode 100644 index 000000000..5440e9435 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/big-bank-plc/model/internet-banking-system/docs/01-context.md @@ -0,0 +1,11 @@ +## Context + +Here is some context about the Internet Banking System... + +![](embed:SystemContext) + +### Internet Banking System +... + +### Mainframe Banking System +... diff --git a/structurizr-dsl/src/test/resources/dsl/big-bank-plc/model/internet-banking-system/docs/02-containers.md b/structurizr-dsl/src/test/resources/dsl/big-bank-plc/model/internet-banking-system/docs/02-containers.md new file mode 100644 index 000000000..d4d8d9aab --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/big-bank-plc/model/internet-banking-system/docs/02-containers.md @@ -0,0 +1,21 @@ +## Software Architecture + +Here is some information about the software architecture of the Internet Banking System... + +![](embed:Containers) + +### Web Application +... + +### Database +... + +Here is some information about the API Application... + +![](embed:Components) + +### Sign in process + +Here is some information about the Sign In Controller, including how the sign in process works... + +![](embed:SignIn) \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/big-bank-plc/model/internet-banking-system/docs/03-development-environment.adoc b/structurizr-dsl/src/test/resources/dsl/big-bank-plc/model/internet-banking-system/docs/03-development-environment.adoc new file mode 100644 index 000000000..9f9b6d664 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/big-bank-plc/model/internet-banking-system/docs/03-development-environment.adoc @@ -0,0 +1,5 @@ +== Development Environment + +Here is some information about how to set up a development environment for the Internet Banking System... + +image::embed:DevelopmentDeployment[] \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/big-bank-plc/model/internet-banking-system/docs/04-deployment.adoc b/structurizr-dsl/src/test/resources/dsl/big-bank-plc/model/internet-banking-system/docs/04-deployment.adoc new file mode 100644 index 000000000..be82ea565 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/big-bank-plc/model/internet-banking-system/docs/04-deployment.adoc @@ -0,0 +1,5 @@ +== Deployment + +Here is some information about the live deployment environment for the Internet Banking System... + +image::embed:LiveDeployment[] \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/big-bank-plc/model/internet-banking-system/summary.dsl b/structurizr-dsl/src/test/resources/dsl/big-bank-plc/model/internet-banking-system/summary.dsl new file mode 100644 index 000000000..71adf255f --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/big-bank-plc/model/internet-banking-system/summary.dsl @@ -0,0 +1 @@ +url https://structurizr.com/share/36141/diagrams#SystemContext \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/big-bank-plc/model/people-and-software-systems.dsl b/structurizr-dsl/src/test/resources/dsl/big-bank-plc/model/people-and-software-systems.dsl new file mode 100644 index 000000000..b4ff925b0 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/big-bank-plc/model/people-and-software-systems.dsl @@ -0,0 +1,25 @@ +customer = person "Personal Banking Customer" "A customer of the bank, with personal bank accounts." "Customer" + +enterprise "Big Bank plc" { + supportStaff = person "Customer Service Staff" "Customer service staff within the bank." "Bank Staff" + backoffice = person "Back Office Staff" "Administration and support staff within the bank." "Bank Staff" + + mainframe = softwaresystem "Mainframe Banking System" "Stores all of the core banking information about customers, accounts, transactions, etc." "Existing System" + email = softwaresystem "E-mail System" "The internal Microsoft Exchange e-mail system." "Existing System" + atm = softwaresystem "ATM" "Allows customers to withdraw cash." "Existing System" + + internetBankingSystem = softwaresystem "Internet Banking System" "Allows customers to view information about their bank accounts, and make payments." { + !include "internet-banking-system/${INTERNET_BANKING_SYSTEM_INCLUDE}" + } +} + +# relationships between people and software systems +customer -> internetBankingSystem "Views account balances, and makes payments using" +internetBankingSystem -> mainframe "Gets account information from, and makes payments using" +internetBankingSystem -> email "Sends e-mail using" +email -> customer "Sends e-mails to" +customer -> supportStaff "Asks questions to" "Telephone" +supportStaff -> mainframe "Uses" +customer -> atm "Withdraws cash using" +atm -> mainframe "Uses" +backoffice -> mainframe "Uses" \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/big-bank-plc/system-landscape.dsl b/structurizr-dsl/src/test/resources/dsl/big-bank-plc/system-landscape.dsl new file mode 100644 index 000000000..f60329605 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/big-bank-plc/system-landscape.dsl @@ -0,0 +1,23 @@ +!constant INTERNET_BANKING_SYSTEM_INCLUDE "summary.dsl" + +workspace "Big Bank plc - System Landscape" "The system landscape for Big Bank plc." { + + model { + !include model/people-and-software-systems.dsl + } + + views { + systemlandscape "SystemLandscape" { + include * + } + + styles { + !include views/styles-people.dsl + + element "Software System" { + background #999999 + color #ffffff + } + } + } +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/big-bank-plc/views/styles-people.dsl b/structurizr-dsl/src/test/resources/dsl/big-bank-plc/views/styles-people.dsl new file mode 100644 index 000000000..f8052dca0 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/big-bank-plc/views/styles-people.dsl @@ -0,0 +1,11 @@ +element "Person" { + color #ffffff + fontSize 22 + shape Person +} +element "Customer" { + background #08427b +} +element "Bank Staff" { + background #999999 +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/deployment-environment-empty.dsl b/structurizr-dsl/src/test/resources/dsl/deployment-environment-empty.dsl new file mode 100644 index 000000000..484323ede --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/deployment-environment-empty.dsl @@ -0,0 +1,9 @@ +workspace { + model { + de = deploymentEnvironment "DeploymentEnvironment" + + !ref de { + dn = deploymentNode "DeploymentNode" + } + } +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/deployment-groups.dsl b/structurizr-dsl/src/test/resources/dsl/deployment-groups.dsl new file mode 100644 index 000000000..e58f506ee --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/deployment-groups.dsl @@ -0,0 +1,48 @@ +workspace { + + model { + softwareSystem = softwareSystem "Software System" { + database = container "Database" + api = container "Service API" { + -> database "Uses" + } + } + + deploymentEnvironment "Example 1" { + deploymentNode "Server 1" { + containerInstance api + containerInstance database + } + deploymentNode "Server 2" { + containerInstance api + containerInstance database + } + } + + deploymentEnvironment "Example 2" { + serviceInstance1 = deploymentGroup "Service Instance 1" + serviceInstance2 = deploymentGroup "Service Instance 2" + deploymentNode "Server 1" { + containerInstance api serviceInstance1 + containerInstance database serviceInstance1 + } + deploymentNode "Server 2" { + containerInstance api serviceInstance2 + containerInstance database serviceInstance2 + } + } + } + + views { + deployment * "Example 1" { + include * + autolayout + } + + deployment * "Example 2" { + include * + autolayout + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/docs/docs/01-context.md b/structurizr-dsl/src/test/resources/dsl/docs/docs/01-context.md new file mode 100644 index 000000000..ddb90b8f7 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/docs/docs/01-context.md @@ -0,0 +1,5 @@ +## Context + +Here is a description of my software system... + +![](embed:Diagram1) \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/docs/workspace.dsl b/structurizr-dsl/src/test/resources/dsl/docs/workspace.dsl new file mode 100644 index 000000000..937977cc4 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/docs/workspace.dsl @@ -0,0 +1,31 @@ +workspace { + + !docs docs com.structurizr.example.ExampleDocumentationImporter + + model { + user = person "User" + softwareSystem = softwareSystem "Software System" { + !docs docs + + container "Container" { + !docs docs + + component "Component" { + !docs docs + } + } + } + + user -> softwareSystem "Uses" + } + + views { + systemContext softwareSystem "Diagram1" { + include * + autoLayout + } + + theme default + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/dynamic-view-with-custom-elements.dsl b/structurizr-dsl/src/test/resources/dsl/dynamic-view-with-custom-elements.dsl new file mode 100644 index 000000000..4dbbb263b --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/dynamic-view-with-custom-elements.dsl @@ -0,0 +1,19 @@ +workspace { + + model { + a = element "A" + b = softwareSystem "B" + c = element "C" + + a -> b + b -> c + } + + views { + dynamic * { + a -> b + b -> c + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/dynamic-view-with-explicit-relationships.dsl b/structurizr-dsl/src/test/resources/dsl/dynamic-view-with-explicit-relationships.dsl new file mode 100644 index 000000000..33103f4b4 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/dynamic-view-with-explicit-relationships.dsl @@ -0,0 +1,37 @@ +workspace { + + model { + a = softwareSystem "A" + b = softwareSystem "B" + + r1 = a -> b "Sync" { + tags "Sync" + } + + r2 = a -> b "Async" { + tags "Async" + } + } + + views { + systemLandscape { + include * + autoLayout + } + + dynamic * { + r2 "Async" + autoLayout + } + + styles { + relationship "Sync" { + style solid + } + relationship "Async" { + style dashed + } + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/dynamic.dsl b/structurizr-dsl/src/test/resources/dsl/dynamic.dsl new file mode 100644 index 000000000..774afab65 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/dynamic.dsl @@ -0,0 +1,24 @@ +workspace { + + model { + a = softwareSystem "A" + b = softwareSystem "B" + + a -> b "Sends data to" + } + + views { + dynamic * { + // with this example, the relationship uses the same description as defined in the static model + a -> b + autoLayout + } + + dynamic * { + // with this example, the relationship description is overriden to describe a particular feature/use case/etc + a -> b "Sends customer data to" + autoLayout + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/exclude-implied-relationship.dsl b/structurizr-dsl/src/test/resources/dsl/exclude-implied-relationship.dsl new file mode 100644 index 000000000..ec8370df9 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/exclude-implied-relationship.dsl @@ -0,0 +1,22 @@ +workspace { + + model { + softwareSystem "A" { + a = container "A" + } + + softwareSystem "B" { + b = container "B" + } + + r = a -> b + } + + views { + systemLandscape { + include * + exclude r + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/exclude-relationships.dsl b/structurizr-dsl/src/test/resources/dsl/exclude-relationships.dsl new file mode 100644 index 000000000..463d8d45c --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/exclude-relationships.dsl @@ -0,0 +1,20 @@ +workspace { + + model { + user = person "User" + softwareSystem = softwareSystem "Software System" + + user -> softwareSystem "Uses" + } + + views { + systemContext softwareSystem { + include * + exclude "* -> element.tag==Software System" + autolayout + } + + theme default + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/extend/1.dsl b/structurizr-dsl/src/test/resources/dsl/extend/1.dsl new file mode 100644 index 000000000..b3c1cbc0b --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/extend/1.dsl @@ -0,0 +1,7 @@ +workspace { + + model { + user1 = person "User 1" + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/extend/2.dsl b/structurizr-dsl/src/test/resources/dsl/extend/2.dsl new file mode 100644 index 000000000..f873c6d4e --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/extend/2.dsl @@ -0,0 +1,7 @@ +workspace extends 1.dsl { + + model { + user2 = person "User 2" + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/extend/3.dsl b/structurizr-dsl/src/test/resources/dsl/extend/3.dsl new file mode 100644 index 000000000..d23c96655 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/extend/3.dsl @@ -0,0 +1,7 @@ +workspace extends 2.dsl { + + model { + user3 = person "User 3" + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/extend/4.dsl b/structurizr-dsl/src/test/resources/dsl/extend/4.dsl new file mode 100644 index 000000000..a978e2db0 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/extend/4.dsl @@ -0,0 +1,9 @@ +workspace extends 3.dsl { + + views { + systemLandscape { + include user1 user2 user3 + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/extend/extend-workspace-from-dsl-file.dsl b/structurizr-dsl/src/test/resources/dsl/extend/extend-workspace-from-dsl-file.dsl new file mode 100644 index 000000000..4260ec1e2 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/extend/extend-workspace-from-dsl-file.dsl @@ -0,0 +1,12 @@ +workspace extends workspace.dsl { + + model { + !ref softwareSystem1 { + webapp = container "Web Application" + } + + user -> softwareSystem1 "Uses" + softwareSystem3.webapp -> softwareSystem3.db + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/extend/extend-workspace-from-dsl-url.dsl b/structurizr-dsl/src/test/resources/dsl/extend/extend-workspace-from-dsl-url.dsl new file mode 100644 index 000000000..d5a24910a --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/extend/extend-workspace-from-dsl-url.dsl @@ -0,0 +1,12 @@ +workspace extends https://raw.githubusercontent.com/structurizr/dsl/master/src/test/dsl/extend/workspace.dsl { + + model { + !ref softwareSystem1 { + webapp = container "Web Application" + } + + user -> softwareSystem1 "Uses" + softwareSystem3.webapp -> softwareSystem3.db + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/extend/extend-workspace-from-json-file.dsl b/structurizr-dsl/src/test/resources/dsl/extend/extend-workspace-from-json-file.dsl new file mode 100644 index 000000000..06e3ffc8b --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/extend/extend-workspace-from-json-file.dsl @@ -0,0 +1,18 @@ +workspace extends workspace.json { + + model { + // !extend with DSL identifier + !extend softwareSystem1 { + webapp1 = container "Web Application 1" + } + + // !extend with canonical name + !extend "SoftwareSystem://Software System 1" { + webapp2 = container "Web Application 2" + } + + user -> softwareSystem1 "Uses" + softwareSystem3.webapp -> softwareSystem3.db + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/extend/extend-workspace-from-json-url.dsl b/structurizr-dsl/src/test/resources/dsl/extend/extend-workspace-from-json-url.dsl new file mode 100644 index 000000000..e84912308 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/extend/extend-workspace-from-json-url.dsl @@ -0,0 +1,18 @@ +workspace extends https://raw.githubusercontent.com/structurizr/dsl/master/src/test/dsl/extend/workspace.json { + + model { + // !extend with DSL identifier + !extend softwareSystem1 { + webapp1 = container "Web Application 1" + } + + // !extend with canonical name + !extend "SoftwareSystem://Software System 1" { + webapp2 = container "Web Application 2" + } + + user -> softwareSystem1 "Uses" + softwareSystem3.webapp -> softwareSystem3.db + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/extend/workspace.dsl b/structurizr-dsl/src/test/resources/dsl/extend/workspace.dsl new file mode 100644 index 000000000..29861779d --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/extend/workspace.dsl @@ -0,0 +1,18 @@ +workspace { + + !identifiers hierarchical + + model { + user = person "User" + + softwareSystem1 = softwareSystem "Software System 1" + + softwareSystem "Software System 2" + + softwareSystem3 = softwareSystem "Software System 3" { + webapp = container "Web Application" + db = container "Database" + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/extend/workspace.json b/structurizr-dsl/src/test/resources/dsl/extend/workspace.json new file mode 100644 index 000000000..09a9f179a --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/extend/workspace.json @@ -0,0 +1,73 @@ +{ + "id" : 0, + "name" : "Name", + "description" : "Description", + "properties" : { + "structurizr.dsl" : "d29ya3NwYWNlIHsKCiAgICAhaWRlbnRpZmllcnMgaGllcmFyY2hpY2FsCgogICAgbW9kZWwgewogICAgICAgIHVzZXIgPSBwZXJzb24gIlVzZXIiCgogICAgICAgIHNvZnR3YXJlU3lzdGVtMSA9IHNvZnR3YXJlU3lzdGVtICJTb2Z0d2FyZSBTeXN0ZW0gMSIKCiAgICAgICAgc29mdHdhcmVTeXN0ZW0gIlNvZnR3YXJlIFN5c3RlbSAyIgoKICAgICAgICBzb2Z0d2FyZVN5c3RlbTMgPSBzb2Z0d2FyZVN5c3RlbSAiU29mdHdhcmUgU3lzdGVtIDMiIHsKICAgICAgICAgICAgd2ViYXBwID0gY29udGFpbmVyICJXZWIgQXBwbGljYXRpb24iCiAgICAgICAgICAgIGRiID0gY29udGFpbmVyICJEYXRhYmFzZSIKICAgICAgICB9CiAgICB9Cgp9Cg==" + }, + "configuration" : { }, + "model" : { + "people" : [ { + "id" : "1", + "tags" : "Element,Person", + "properties" : { + "structurizr.dsl.identifier" : "user" + }, + "name" : "User", + "location" : "Unspecified" + } ], + "softwareSystems" : [ { + "id" : "2", + "tags" : "Element,Software System", + "properties" : { + "structurizr.dsl.identifier" : "softwaresystem1" + }, + "name" : "Software System 1", + "location" : "Unspecified", + "documentation" : { } + }, { + "id" : "3", + "tags" : "Element,Software System", + "properties" : { + "structurizr.dsl.identifier" : "93ea2110-adec-4936-97aa-b55397325115" + }, + "name" : "Software System 2", + "location" : "Unspecified", + "documentation" : { } + }, { + "id" : "4", + "tags" : "Element,Software System", + "properties" : { + "structurizr.dsl.identifier" : "softwaresystem3" + }, + "name" : "Software System 3", + "location" : "Unspecified", + "containers" : [ { + "id" : "5", + "tags" : "Element,Container", + "properties" : { + "structurizr.dsl.identifier" : "softwaresystem3.webapp" + }, + "name" : "Web Application", + "documentation" : { } + }, { + "id" : "6", + "tags" : "Element,Container", + "properties" : { + "structurizr.dsl.identifier" : "softwaresystem3.db" + }, + "name" : "Database", + "documentation" : { } + } ], + "documentation" : { } + } ] + }, + "documentation" : { }, + "views" : { + "configuration" : { + "branding" : { }, + "styles" : { }, + "terminology" : { } + } + } +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/filteredviews.dsl b/structurizr-dsl/src/test/resources/dsl/filteredviews.dsl new file mode 100644 index 000000000..236be9509 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/filteredviews.dsl @@ -0,0 +1,41 @@ +workspace "FilteredDemo" "This is an example of Filtered views" { + + # This will render the two diagrams at https://structurizr.com/help/filtered-views + + model { + + user = person "Customer" "A description of the user." + sysa = softwareSystem "Software System A" "A description of software system A." + + user -> sysa "Uses for tasks 1 and 2" "" Current + + sysb = softwareSystem "Software System B" "A description of software system B." Future + + user -> sysa "Uses for task 1" "" Future + user -> sysb "Uses for task 2" "" Future + + } + + views { + + systemLandscape FullLandscape "System Landscape, current and future" { + include * + } + + filtered FullLandscape exclude Future CurrentLandscape "The current system landscape." + filtered FullLandscape exclude Current FutureLandscape "The future state system landscape after Software System B is live." + + styles { + element "Software System" { + background #91a437 + shape RoundedBox + } + + element "Person" { + background #6a7b15 + shape Person + } + } + + } +} diff --git a/structurizr-dsl/src/test/resources/dsl/financial-risk-system.dsl b/structurizr-dsl/src/test/resources/dsl/financial-risk-system.dsl new file mode 100644 index 000000000..a5539386f --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/financial-risk-system.dsl @@ -0,0 +1,67 @@ +workspace "Financial Risk System" "This is a simple (incomplete) example C4 model based upon the financial risk system architecture kata, which can be found at http://bit.ly/sa4d-risksystem" { + + model { + businessUser = person "Business User" "A regular business user." + configurationUser = person "Configuration User" "A regular business user who can also configure the parameters used in the risk calculations." + + financialRiskSystem = softwareSystem "Financial Risk System" "Calculates the bank's exposure to risk for product X." "Financial Risk System" + tradeDataSystem = softwareSystem "Trade Data System" "The system of record for trades of type X." + referenceDataSystem = softwareSystem "Reference Data System" "Manages reference data for all counterparties the bank interacts with." + referenceDataSystemV2 = softwareSystem "Reference Data System v2.0" "Manages reference data for all counterparties the bank interacts with." "Future State" + emailSystem = softwareSystem "E-mail system" "The bank's Microsoft Exchange system." + centralMonitoringService = softwareSystem "Central Monitoring Service" "The bank's central monitoring and alerting dashboard." + activeDirectory = softwareSystem "Active Directory" "The bank's authentication and authorisation system." + + businessUser -> financialRiskSystem "Views reports using" + financialRiskSystem -> tradeDataSystem "Gets trade data from" + financialRiskSystem -> referenceDataSystem "Gets counterparty data from" + financialRiskSystem -> referenceDataSystemV2 "Gets counterparty data from" "" "Future State" + configurationUser -> financialRiskSystem "Configures parameters using" + financialRiskSystem -> emailSystem "Sends a notification that a report is ready to" + emailSystem -> businessUser "Sends a notification that a report is ready to" "E-mail message" "Asynchronous" + financialRiskSystem -> centralMonitoringService "Sends critical failure alerts to" "SNMP" "Asynchronous, Alert" + financialRiskSystem -> activeDirectory "Uses for user authentication and authorisation" + } + + views { + + systemContext financialRiskSystem "Context" "An example System Context diagram for the Financial Risk System architecture kata." { + include * + autoLayout + } + + styles { + element "Element" { + color #ffffff + } + element "Software System" { + background #801515 + shape RoundedBox + } + element "Financial Risk System" { + background #550000 + color #ffffff + } + element "Future State" { + opacity 30 + } + element "Person" { + background #d46a6a + shape Person + } + relationship "Relationship" { + dashed false + } + relationship "Asynchronous" { + dashed true + } + relationship "Alert" { + color #ff0000 + } + relationship "Future State" { + opacity 30 + } + } + + } +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/getting-started-short.dsl b/structurizr-dsl/src/test/resources/dsl/getting-started-short.dsl new file mode 100644 index 000000000..ddfcf56e1 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/getting-started-short.dsl @@ -0,0 +1,17 @@ +workspace { + + model { + user = person "User" "A user of my software system." + softwareSystem = softwareSystem "Software System" "My software system." + + user -> softwareSystem "Uses" + } + + views { + systemContext softwareSystem { + include * + autoLayout + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/getting-started.dsl b/structurizr-dsl/src/test/resources/dsl/getting-started.dsl new file mode 100644 index 000000000..3706c0796 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/getting-started.dsl @@ -0,0 +1,19 @@ +workspace { + + model { + user = person "User" + softwareSystem = softwareSystem "Software System" + + user -> softwareSystem "Uses" + } + + views { + systemContext softwareSystem { + include * + autolayout + } + + theme default + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/group-url.dsl b/structurizr-dsl/src/test/resources/dsl/group-url.dsl new file mode 100644 index 000000000..64d36d9c7 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/group-url.dsl @@ -0,0 +1,11 @@ +workspace { + + model { + softwareSystem "Software System" { + group "Name" { + url "https://example.com" + } + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/group-without-brace.dsl b/structurizr-dsl/src/test/resources/dsl/group-without-brace.dsl new file mode 100644 index 000000000..71647fe6a --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/group-without-brace.dsl @@ -0,0 +1,7 @@ +workspace { + + model { + group "Name" + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/groups-nested.dsl b/structurizr-dsl/src/test/resources/dsl/groups-nested.dsl new file mode 100644 index 000000000..4e98e49a3 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/groups-nested.dsl @@ -0,0 +1,47 @@ +workspace { + + model { + properties { + structurizr.groupSeparator / + } + + group "Organisation" { + group "Department A" { + a = softwareSystem "A" { + group "Capability 1" { + group "Service A" { + container "A API" + container "A Database" + } + group "Service B" { + container "B API" + container "B Database" + } + } + } + } + + group "Department B" { + b = softwareSystem "B" + } + + c = softwareSystem "C" + } + + enterprise "Enterprise" { + group "Department A" { + group "Team 1" { + d = softwareSystem "D" + } + } + } + } + + views { + systemLandscape { + include * + autolayout + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/groups.dsl b/structurizr-dsl/src/test/resources/dsl/groups.dsl new file mode 100644 index 000000000..d38ce2d46 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/groups.dsl @@ -0,0 +1,51 @@ +workspace { + + model { + softwareSystem = softwareSystem "Software System" { + service1 = group "Service 1" { + service1Api = container "Service 1 API" + service1Database = container "Service 1 Database" + + service1Api -> service1Database "Reads from and writes to" + } + service2 = group "Service 2" { + service2Api = container "Service 2 API" + service2Database = container "Service 2 Database" + + service2Api -> service2Database "Reads from and writes to" + } + } + + live = deploymentEnvironment "Live" { + group "Servers" { + deploymentNode "Server 1" { + group "Service 1" { + containerInstance service1Api + containerInstance service1Database + } + } + deploymentNode "Server 2" { + group "Service 2" { + containerInstance service2Api + containerInstance service2Database + } + } + } + } + + service1Api -> service2Api "Uses" + } + + views { + container softwareSystem { + include service1 service2 + autolayout + } + + deployment softwareSystem live { + include service1 service2 + autolayout + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/hierarchical-identifiers-and-deployment-nodes-1.dsl b/structurizr-dsl/src/test/resources/dsl/hierarchical-identifiers-and-deployment-nodes-1.dsl new file mode 100644 index 000000000..54af32462 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/hierarchical-identifiers-and-deployment-nodes-1.dsl @@ -0,0 +1,40 @@ + workspace { + + !identifiers hierarchical + + model { + ss = softwareSystem "Software System" + + deploymentEnvironment "Live" { + + dn = deploymentNode "DN" { + dn1 = deploymentNode "DN1" { + softwareSystemInstance ss + } + + dn2 = deploymentNode "DN2" { + softwareSystemInstance ss + } + + dn1 -> dn2 + } + + dn1 = deploymentNode "DN1" { + softwareSystemInstance ss + } + + dn2 = deploymentNode "DN2" { + softwareSystemInstance ss + } + + dn1 -> dn2 + } + } + + views { + deployment * "Live" { + include * + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/hierarchical-identifiers-and-deployment-nodes-2.dsl b/structurizr-dsl/src/test/resources/dsl/hierarchical-identifiers-and-deployment-nodes-2.dsl new file mode 100644 index 000000000..ae26c3d9c --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/hierarchical-identifiers-and-deployment-nodes-2.dsl @@ -0,0 +1,18 @@ +workspace { + + !identifiers hierarchical + + model { + ss = softwareSystem "SS" + + live = deploymentEnvironment "Environment" { + dn = deploymentNode "DN1" { + ss = deploymentNode "DN2" { + softwareSystemInstance ss + } + } + } + + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/hierarchical-identifiers-and-deployment-nodes-3.dsl b/structurizr-dsl/src/test/resources/dsl/hierarchical-identifiers-and-deployment-nodes-3.dsl new file mode 100644 index 000000000..97d3b7d37 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/hierarchical-identifiers-and-deployment-nodes-3.dsl @@ -0,0 +1,22 @@ +workspace { + + !identifiers hierarchical + + model { + ss = softwareSystem "SS" { + c = container "C" + } + + live = deploymentEnvironment "Environment" { + dn = deploymentNode "DN1" { + ss = deploymentNode "DN2" { + c = deploymentNode "DN3" { + containerInstance ss.c + } + } + } + } + + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/hierarchical-identifiers-when-unassigned.dsl b/structurizr-dsl/src/test/resources/dsl/hierarchical-identifiers-when-unassigned.dsl new file mode 100644 index 000000000..067a22cd8 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/hierarchical-identifiers-when-unassigned.dsl @@ -0,0 +1,19 @@ +workspace { + + !identifiers hierarchical + + model { + softwareSystem "A" { + container "B" { + component "C" + } + } + + deploymentEnvironment "Environment" { + deploymentNode "Deployment Node" { + infrastructureNode "Infrastructure Node" + } + } + + } +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/hierarchical-identifiers.dsl b/structurizr-dsl/src/test/resources/dsl/hierarchical-identifiers.dsl new file mode 100644 index 000000000..503f6b91d --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/hierarchical-identifiers.dsl @@ -0,0 +1,16 @@ +workspace { + + !identifiers hierarchical + + model { + a = person "A" + b = softwareSystem "B"{ + c = container "C" { + d = component "D" + a = component "A" + + d -> a + } + } + } +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/identifiers.dsl b/structurizr-dsl/src/test/resources/dsl/identifiers.dsl new file mode 100644 index 000000000..d4a32f218 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/identifiers.dsl @@ -0,0 +1,14 @@ +workspace { + + !identifiers hierarchical + + model { + user = person "User" + softwareSystem = softwareSystem "Software System" { + container = container "Container" { + rel = user -> this "Uses" + } + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/image-views/diagram.dot b/structurizr-dsl/src/test/resources/dsl/image-views/diagram.dot new file mode 100644 index 000000000..3f2b18926 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/image-views/diagram.dot @@ -0,0 +1 @@ +digraph G {Hello->World} diff --git a/structurizr-dsl/src/test/resources/dsl/image-views/diagram.mmd b/structurizr-dsl/src/test/resources/dsl/image-views/diagram.mmd new file mode 100644 index 000000000..498140e3e --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/image-views/diagram.mmd @@ -0,0 +1,2 @@ +flowchart TD + Start --> Stop \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/image-views/diagram.puml b/structurizr-dsl/src/test/resources/dsl/image-views/diagram.puml new file mode 100644 index 000000000..1da6ac585 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/image-views/diagram.puml @@ -0,0 +1,3 @@ +@startuml +Bob -> Alice : hello +@enduml \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/image-views/logo.png b/structurizr-dsl/src/test/resources/dsl/image-views/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..763d19bf5c7d827bec4f6c31ff8e8660f09aab54 GIT binary patch literal 9262 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 literal 0 HcmV?d00001 diff --git a/structurizr-dsl/src/test/resources/dsl/image-views/workspace-via-file.dsl b/structurizr-dsl/src/test/resources/dsl/image-views/workspace-via-file.dsl new file mode 100644 index 000000000..22d141f68 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/image-views/workspace-via-file.dsl @@ -0,0 +1,27 @@ +workspace { + + views { + properties { + "plantuml.url" "http://localhost:7777" + "mermaid.url" "http://localhost:8888" + "kroki.url" "http://localhost:9999" + } + + image * "plantuml" { + plantuml diagram.puml + } + + image * "mermaid" { + mermaid diagram.mmd + } + + image * "kroki" { + kroki graphviz diagram.dot + } + + image * "image" { + image logo.png + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/image-views/workspace-via-url.dsl b/structurizr-dsl/src/test/resources/dsl/image-views/workspace-via-url.dsl new file mode 100644 index 000000000..91ff21755 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/image-views/workspace-via-url.dsl @@ -0,0 +1,30 @@ +workspace { + + views { + properties { + "plantuml.url" "http://localhost:7777" + "plantuml.format" "svg" + "mermaid.url" "http://localhost:8888" + "mermaid.format" "svg" + "kroki.url" "http://localhost:9999" + "kroki.format" "svg" + } + + image * "plantuml" { + plantuml https://raw.githubusercontent.com/structurizr/dsl/master/src/test/dsl/image-views/diagram.puml + } + + image * "mermaid" { + mermaid https://raw.githubusercontent.com/structurizr/dsl/master/src/test/dsl/image-views/diagram.mmd + } + + image * "kroki" { + kroki graphviz https://raw.githubusercontent.com/structurizr/dsl/master/src/test/dsl/image-views/diagram.dot + } + + image * "image" { + image https://raw.githubusercontent.com/structurizr/dsl/master/src/test/dsl/image-views/logo.png + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/include-directory.dsl b/structurizr-dsl/src/test/resources/dsl/include-directory.dsl new file mode 100644 index 000000000..88e4a04bd --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/include-directory.dsl @@ -0,0 +1,14 @@ +workspace { + + model { + !constant SOFTWARE_SYSTEM_NAME "Software System 1" + !include include/model/software-system/model.dsl + + !constant SOFTWARE_SYSTEM_NAME "Software System 2" + !include include/model/software-system + + !constant SOFTWARE_SYSTEM_NAME "Software System 3" + !include include/model + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/include-file.dsl b/structurizr-dsl/src/test/resources/dsl/include-file.dsl new file mode 100644 index 000000000..83d8d93db --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/include-file.dsl @@ -0,0 +1,7 @@ +workspace { + + model { + !include include/model.dsl + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/include-implied-relationship.dsl b/structurizr-dsl/src/test/resources/dsl/include-implied-relationship.dsl new file mode 100644 index 000000000..86757f95c --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/include-implied-relationship.dsl @@ -0,0 +1,23 @@ +workspace { + + model { + softwareSystem "A" { + a = container "A" + } + + softwareSystem "B" { + b = container "B" + } + + r = a -> b + } + + views { + systemLandscape { + include * + exclude *->* + include r + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/include-url.dsl b/structurizr-dsl/src/test/resources/dsl/include-url.dsl new file mode 100644 index 000000000..9739a90f7 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/include-url.dsl @@ -0,0 +1,7 @@ +workspace { + + model { + !include https://raw.githubusercontent.com/structurizr/dsl/master/src/test/dsl/include/model.dsl + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/include/docs/section.md b/structurizr-dsl/src/test/resources/dsl/include/docs/section.md new file mode 100644 index 000000000..fa8d5e722 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/include/docs/section.md @@ -0,0 +1,3 @@ +## Heading + +Text... \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/include/model.dsl b/structurizr-dsl/src/test/resources/dsl/include/model.dsl new file mode 100644 index 000000000..6bdd6ec8b --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/include/model.dsl @@ -0,0 +1,3 @@ +softwareSystem = softwareSystem "Software System" { + !docs docs +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/include/model/software-system/model.dsl b/structurizr-dsl/src/test/resources/dsl/include/model/software-system/model.dsl new file mode 100644 index 000000000..a040bae35 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/include/model/software-system/model.dsl @@ -0,0 +1,3 @@ +softwareSystem "${SOFTWARE_SYSTEM_NAME}" { + !docs ../../docs +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/iso-8859.dsl b/structurizr-dsl/src/test/resources/dsl/iso-8859.dsl new file mode 100644 index 000000000..caad37c01 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/iso-8859.dsl @@ -0,0 +1,5 @@ +workspace { + model { + softwareSystem "Namé" + } +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/logo.png b/structurizr-dsl/src/test/resources/dsl/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..763d19bf5c7d827bec4f6c31ff8e8660f09aab54 GIT binary patch literal 9262 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 literal 0 HcmV?d00001 diff --git a/structurizr-dsl/src/test/resources/dsl/multi-line-with-error.dsl b/structurizr-dsl/src/test/resources/dsl/multi-line-with-error.dsl new file mode 100644 index 000000000..8c3290044 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/multi-line-with-error.dsl @@ -0,0 +1,12 @@ +workspace { + + model { + softwareSystem = \ + softwareSystem \ + "Software \ + System" { + component "Component" // components not permitted inside software systems + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/multi-line.dsl b/structurizr-dsl/src/test/resources/dsl/multi-line.dsl new file mode 100644 index 000000000..2fc5b87c6 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/multi-line.dsl @@ -0,0 +1,10 @@ +workspace { + + model { + softwareSystem = \ + softwareSystem \ + "Software \ + System" + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/multiple-model-tokens.dsl b/structurizr-dsl/src/test/resources/dsl/multiple-model-tokens.dsl new file mode 100644 index 000000000..d75ee05f3 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/multiple-model-tokens.dsl @@ -0,0 +1,11 @@ +workspace { + + model { + person "User 1" + } + + model { + person "User 2" + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/multiple-view-tokens.dsl b/structurizr-dsl/src/test/resources/dsl/multiple-view-tokens.dsl new file mode 100644 index 000000000..23c1a878f --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/multiple-view-tokens.dsl @@ -0,0 +1,19 @@ +workspace { + + model { + person "User 1" + } + + views { + systemLandscape { + include * + } + } + + views { + systemLandscape { + include * + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/multiple-workspace-tokens.dsl b/structurizr-dsl/src/test/resources/dsl/multiple-workspace-tokens.dsl new file mode 100644 index 000000000..391d88d81 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/multiple-workspace-tokens.dsl @@ -0,0 +1,15 @@ +workspace { + + model { + person "User 1" + } + +} + +workspace { + + model { + person "User 2" + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/parallel1.dsl b/structurizr-dsl/src/test/resources/dsl/parallel1.dsl new file mode 100644 index 000000000..70fc06138 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/parallel1.dsl @@ -0,0 +1,33 @@ +workspace { + + model { + user = person "User" + softwareSystem = softwareSystem "Software System" { + webapp = container "Web Application" + bus = container "Message Bus" + app1 = container "App 1" + app2 = container "App 2" + } + + user -> webapp "Updates details" + webapp -> bus "Sends update event" + bus -> app1 "Broadcasts update event" + bus -> app2 "Broadcasts update event" + } + + views { + dynamic softwareSystem { + user -> webapp + webapp -> bus + { + bus -> app1 + } + { + bus -> app2 + } + + autoLayout + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/parallel2.dsl b/structurizr-dsl/src/test/resources/dsl/parallel2.dsl new file mode 100644 index 000000000..8d7d39e94 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/parallel2.dsl @@ -0,0 +1,34 @@ +workspace { + + model { + a = softwareSystem "A" + b = softwareSystem "B" + c = softwareSystem "C" + d = softwareSystem "D" + e = softwareSystem "E" + + a -> b + b -> c + b -> d + b -> e + } + + views { + + dynamic * { + a -> b "Makes a request to" + { + { + b -> c "Gets data from" + } + { + b -> d "Gets data from" + } + } + b -> e "Sends data to" + + autoLayout + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/plugin-with-parameters.dsl b/structurizr-dsl/src/test/resources/dsl/plugin-with-parameters.dsl new file mode 100644 index 000000000..4314c7539 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/plugin-with-parameters.dsl @@ -0,0 +1,7 @@ +workspace { + + !plugin com.example.ExampleStructurizrDslPlugin { + name Java + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/plugin-without-parameters.dsl b/structurizr-dsl/src/test/resources/dsl/plugin-without-parameters.dsl new file mode 100644 index 000000000..2831b60dc --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/plugin-without-parameters.dsl @@ -0,0 +1,5 @@ +workspace { + + !plugin com.example.ExampleStructurizrDslPlugin + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/plugins/structurizr-dsl-plugin-1.0.0.jar b/structurizr-dsl/src/test/resources/dsl/plugins/structurizr-dsl-plugin-1.0.0.jar new file mode 100644 index 0000000000000000000000000000000000000000..42234ce4b597ea68492ff64a8d4095fecdd73902 GIT binary patch literal 1214 zcmWIWW@h1HVBp|j5VBMXW&i>v5CH_7K<w)p;;8HC=cdoh5P+g^Q$5o=X`lj0AQnMZ z=<Dd`>E;?7qUY=O+4sz8A8%c~i@e^tTIbH3-yCFc#rVO~M^Bm13<K(i+sXoDC+Fv4 zSct5K2dXBuA~ClhCly2OG0A=G;XrR(1{$V|rq&grC%B}jG`Xa-D6^`_r8p-br!+k? zPcJzqvAB3@$ju_5KmptEX^JTvf;_V(NIaUfp{1v@Q1z*|ki3pU$igM3wx2HGR+mg$ z5oz*^zpi2aL+&WMAB_JdPqQ`FNOkDZ-~Rktkze`!cR#;9J{``m`$qR;At#|jMoJw< zXXbyn9&>T$#n8T6>Q1E+i>hZWwDgy7`st+ICy}OdB=w9_T0qF=<v04CuIkIqe6O=K z`L$PZiT#GJZSTrVlkc0z6uM;;g<Bk7R+{yFr<O$6r`A`MJ@=n^@f7nUHh*t9Zms{= zD2UOeIeJNH%h3(<*7r?P%RBobjO+0#i**T(PHx9SW0FtIO)r)8mH)MGf&Xm9g*QvX zulKJx9CyF+^ag{@7pXknQyv=|9og6;%6UC?jneTAKd$eRND6T}`-Wp~VyP2*)jZFS zvKsHDcW6GG)^j@|{e<7g)EN)w`Z-5z%QSf7^XBxE4+_ha6whyXxU<9f-s{&+mOM|E zs80<@IT2sNu)_Vf%h3sIvf5Vc-n(tXywBVGW`$f0*?I5Lv}bqQPi^?@wb-0PXrF_b z=q&%!s<ZC?iRk?=ams7qQH7QJ)k=Qvf4uU2zFYq6xkeY_HYWEwO}<vLqbllg?fweR z{(lii4)iMCGl}&U^*3E_zf*JXjZaA%+jwu~W-03KQqSD~Qs<(t;z<v;uK5S-em(QG zTD;Gw&1i1Xg9hCqw%Yp#zZv|nt!(#v=Edx>HAu_QVN=|{o-gb!!QEfaGH!axc&*R& z&V>HmE+^!YSU9boPjC~GT$15^$u8XRfOJu1y!DRP>T{ObU$K2OIkWtE!Tb+$6KciY zSQ`mVQ?*gqe!Pxx>iht2MkWzv+&K-Dz8M$<7~VR9Xn4Lu*M^?&K#D<NNuwr^glj{} zh3F=LRKv13D6>HT$l@{}6O{Gv=>TOt2mtAr1!RJBz%nAbPV|fj)4Qbc7@AIk*)qVJ Sl?|kw6$l*|85lB{K|BDw)SE;A literal 0 HcmV?d00001 diff --git a/structurizr-dsl/src/test/resources/dsl/ref.dsl b/structurizr-dsl/src/test/resources/dsl/ref.dsl new file mode 100644 index 000000000..cf6ce5a1f --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/ref.dsl @@ -0,0 +1,45 @@ +workspace extends amazon-web-services.dsl { + + model { + + !ref "DeploymentNode://Live/Amazon Web Services" { + deploymentNode "New deployment node" { + infrastructureNode "New infrastructure node" { + -> route53 + } + } + } + + !ref "DeploymentNode://Live/Amazon Web Services/US-East-1" { + deploymentNode "New deployment node 1" { + infrastructureNode "New infrastructure node 1" { + -> route53 + } + } + } + + !ref region { + deploymentNode "New deployment node 2" { + infrastructureNode "New infrastructure node 2" { + -> route53 + } + } + } + + !ref live { + deploymentNode "New deployment node 3" { + infrastructureNode "New infrastructure node 3" { + -> route53 + } + } + } + } + + views { + deployment * "Live" { + include * + autolayout lr + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/relationship-already-exists.dsl b/structurizr-dsl/src/test/resources/dsl/relationship-already-exists.dsl new file mode 100644 index 000000000..59030d423 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/relationship-already-exists.dsl @@ -0,0 +1,13 @@ +workspace { + + model { + a = softwareSystem "A" + b = softwareSystem "B" { + c = container "C" + } + + c -> a + b -> a + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/script-external-with-parameters.dsl b/structurizr-dsl/src/test/resources/dsl/script-external-with-parameters.dsl new file mode 100644 index 000000000..47002cedf --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/script-external-with-parameters.dsl @@ -0,0 +1,7 @@ +workspace { + + !script test.groovy { + "name" "Groovy" + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/script-external.dsl b/structurizr-dsl/src/test/resources/dsl/script-external.dsl new file mode 100644 index 000000000..4e2d8988a --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/script-external.dsl @@ -0,0 +1,7 @@ +workspace { + + !script test.groovy + !script test.kts + !script test.rb + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/script-in-dynamic-view.dsl b/structurizr-dsl/src/test/resources/dsl/script-in-dynamic-view.dsl new file mode 100644 index 000000000..f29f4e950 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/script-in-dynamic-view.dsl @@ -0,0 +1,16 @@ +workspace { + + model { + user = person "User" + softwareSystem = softwareSystem "Software System" + } + + views { + dynamic * "key" { + !script groovy { + view.description = "Groovy" + } + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/script-inline.dsl b/structurizr-dsl/src/test/resources/dsl/script-inline.dsl new file mode 100644 index 000000000..3325479af --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/script-inline.dsl @@ -0,0 +1,42 @@ +workspace { + + !script groovy { + println("Hello from Groovy"); + workspace.model.addPerson("Groovy"); + } + + !script kotlin { + println("Hello from Kotlin"); + workspace.model.addPerson("Kotlin"); + } + + !script ruby { + puts "Hello from Ruby" + workspace.model.addPerson("Ruby"); + } + + model { + user = person "User" { + !script groovy { + element.addTags("Groovy") + } + } + + softwareSystem "Software System" { + user -> this { + !script groovy { + relationship.addTags("Groovy") + } + } + } + } + + views { + systemLandscape { + !script groovy { + view.description = "Groovy" + } + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/shapes.dsl b/structurizr-dsl/src/test/resources/dsl/shapes.dsl new file mode 100644 index 000000000..4f408498d --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/shapes.dsl @@ -0,0 +1,89 @@ +workspace "Shapes" "An example of all shapes available in Structurizr." { + + model { + softwareSystem "Box" "" "Box" + softwareSystem "RoundedBox" "" "RoundedBox" + softwareSystem "Diamond" "" "Diamond" + softwareSystem "Circle" "" "Circle" + softwareSystem "Ellipse" "" "Ellipse" + softwareSystem "Hexagon" "" "Hexagon" + softwareSystem "Folder" "" "Folder" + softwareSystem "Cylinder" "" "Cylinder" + softwareSystem "Pipe" "" "Pipe" + softwareSystem "WebBrowser" "" "Web Browser" + softwareSystem "Mobile Device Portrait" "" "Mobile Device Portrait" + softwareSystem "Mobile Device Landscape" "" "Mobile Device Landscape" + softwareSystem "Component" "" "Component" + person "Person" + softwareSystem "Robot" "" "Robot" + } + + views { + systemLandscape "shapes" "An example of all shapes available in Structurizr." { + include * + } + + styles { + element "Element" { + width "650" + height "400" + background "#438dd5" + color "#ffffff" + fontSize "34" + metadata "false" + description "false" + } + element "Box" { + shape "Box" + } + element "RoundedBox" { + shape "RoundedBox" + } + element "Diamond" { + shape "Diamond" + } + element "Circle" { + shape "Circle" + } + element "Ellipse" { + shape "Ellipse" + } + element "Hexagon" { + shape "Hexagon" + } + element "Folder" { + shape "Folder" + } + element "Cylinder" { + shape "Cylinder" + } + element "Pipe" { + shape "Pipe" + } + element "Web Browser" { + shape "WebBrowser" + } + element "Mobile Device Portrait" { + shape "MobileDevicePortrait" + width "400" + height "650" + } + element "Mobile Device Landscape" { + shape "MobileDeviceLandscape" + } + element "Component" { + shape "Component" + } + element "Person" { + shape "Person" + width "550" + } + element "Robot" { + shape "Robot" + width "550" + } + } + + } + +} diff --git a/structurizr-dsl/src/test/resources/dsl/test-with-parameters.groovy b/structurizr-dsl/src/test/resources/dsl/test-with-parameters.groovy new file mode 100644 index 000000000..fa7d24643 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/test-with-parameters.groovy @@ -0,0 +1,4 @@ +package dsl + +println("Hello from " + name); +workspace.model.addPerson(name); \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/test.dsl b/structurizr-dsl/src/test/resources/dsl/test.dsl new file mode 100644 index 000000000..cab02b522 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/test.dsl @@ -0,0 +1,345 @@ +!constant ORGANISATION_NAME "Organisation" +!constant GROUP_NAME "Group" + +workspace "Name" "Description" { + + /* + multi-line comment + */ + + /** + multi-line comment + */ + + /* multi-line comment on single line */ + + /* multi-line comment + on two lines */ + + # single line comment + // single line comment + + model { + properties { + "Name" "Value" + } + + box1 = element "Box 1" "Metadata" "Description" "Tag" + box2 = element "Box 2" "Metadata" "Description" "Tag" + box1 -> box2 + + user = person "User" "Description" "Tag" { + url "https://structurizr.com" + properties { + "Name" "Value" + } + perspectives { + "Security" "A description..." + "Technical Debt" "Tech debt is high due to delivering feature X rapidly." "High" + } + } + + enterprise "${ORGANISATION_NAME} - ${GROUP_NAME}" { + softwareSystem = softwareSystem "Software System" "Description" "Tag" { + webApplication = container "Web Application" "Description" "Technology" "Tag" { + homePageController = component "HomePageController" "Description" "Spring MVC Controller" "Tag" { + url "https://structurizr.com" + properties { + "Name" "Value" + } + perspectives { + "Security" "A description..." + } + } + + url "https://structurizr.com" + properties { + "Name" "Value" + } + perspectives { + "Security" "A description..." + } + } + + url "https://structurizr.com" + properties { + "Name" "Value" + } + perspectives { + "Security" "A description..." + } + } + + softwareSystem "E-mail System" "Description" "Tag" + } + + user -> HomePageController "Visits" "HTTPS" "Tag" + + developmentEnvironment = deploymentEnvironment "Development" { + deploymentNode "Amazon Web Services" "Description" "Technology" "Tag" { + softwareSystemInstance softwareSystem { + url "https://structurizr.com" + properties { + "Name" "Value" + } + perspectives { + "Security" "A description..." + } + healthCheck "Check 1" "https://example.com/health" + healthCheck "Check 2" "https://example.com/health" 60 + healthCheck "Check 2" "https://example.com/health" 120 1000 + } + } + } + + deploymentEnvironment "Live" { + deploymentNode "Amazon Web Services" "Description" "Technology" "Tag" { + + infrastructureNode "Elastic Load Balancer" "Description" "Technology" "Tag" { + url "https://structurizr.com" + properties { + "Name" "Value" + } + perspectives { + "Security" "A description..." + } + } + + deploymentNode "Amazon Web Services - EC2" "Description" "Technology" "Tag" { + containerInstance webApplication { + url "https://structurizr.com" + properties { + "Name" "Value" + } + perspectives { + "Security" "A description..." + } + healthCheck "Check 1" "https://example.com/health" + healthCheck "Check 2" "https://example.com/health" 60 + healthCheck "Check 2" "https://example.com/health" 120 1000 + } + } + + url "https://structurizr.com" + properties { + "Name" "Value" + } + perspectives { + "Security" "A description..." + } + + } + } + } + + views { + + custom "CustomDiagram" "Title" "Description" { + title "Title" + description "Description" + + include box1 box2 + + animation { + box1 + box2 + } + + autolayout + + properties { + "Name" "Value" + } + + default + } + + systemLandscape "SystemLandscape" "Description" { + title "Title" + description "Description" + + include * + autoLayout + + properties { + "Name" "Value" + } + + default + } + + systemContext softwareSystem "SystemContext" "Description" { + title "Title" + description "Description" + + include * + autoLayout + + properties { + "Name" "Value" + } + + default + } + + container softwareSystem "Containers" "Description" { + title "Title" + description "Description" + + include * + autoLayout + + properties { + "Name" "Value" + } + + default + } + + component webApplication "Components" "Description" { + title "Title" + description "Description" + + include * + autoLayout + + properties { + "Name" "Value" + } + + default + } + + filtered "SystemLandscape" include "Element,Relationship" "Filtered1" + + filtered "SystemLandscape" include "Element,Relationship" "Filtered2" { + title "Filtered view" + description "Description" + + properties { + "Name" "Value" + } + + default + } + + dynamic webApplication "Dynamic" "Description" { + title "Title" + description "Description" + + user -> homePageController "Requests via web browser" + homePageController -> user { + url "https://structurizr.com" + } + + autoLayout + + properties { + "Name" "Value" + } + + default + } + + deployment * developmentEnvironment "Deployment-Development" "Description" { + title "Title" + description "Description" + + include * + autoLayout + + properties { + "Name" "Value" + } + + default + } + + deployment * "Live" "Deployment-Live" "Description" { + title "Title" + description "Description" + + include * + autoLayout + + properties { + "Name" "Value" + } + + default + } + + styles { + element "Element" { + shape roundedbox + icon logo.png + width 450 + height 300 + background #ffffff + color #000000 + colour #000000 + stroke #777777 + fontSize 24 + border solid + opacity 50 + metadata false + description false + properties { + "Name" "Value" + } + } + + relationship "Relationship" { + thickness 2 + color #777777 + colour #777777 + dashed true + routing curved + fontSize 24 + width 400 + position 50 + opacity 50 + properties { + "Name" "Value" + } + } + + theme https://example.com/theme1 + themes https://example.com/theme2 https://example.com/theme3 + } + + theme https://example.com/theme1 + themes https://example.com/theme2 https://example.com/theme3 + + branding { + logo logo.png + font "Example" https://example/com/font + } + + terminology { + enterprise "Enterprise" + person "Person" + softwareSystem "Software System" + container "Container" + component "Component" + deploymentNode "Deployment Node" + infrastructureNode "Infrastructure Node" + relationship "Relationship" + } + + properties { + "Name" "Value" + } + } + + configuration { + users { + user1@example.com read + user2@example.com write + } + + visibility public + scope softwaresystem + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/test.groovy b/structurizr-dsl/src/test/resources/dsl/test.groovy new file mode 100644 index 000000000..eefdea320 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/test.groovy @@ -0,0 +1,4 @@ +package dsl + +println("Hello from Groovy"); +workspace.model.addPerson("Groovy"); \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/test.js b/structurizr-dsl/src/test/resources/dsl/test.js new file mode 100644 index 000000000..7b6a7297d --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/test.js @@ -0,0 +1,2 @@ +print("Hello from JavaScript"); +workspace.model.addPerson("JavaScript"); \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/test.kts b/structurizr-dsl/src/test/resources/dsl/test.kts new file mode 100644 index 000000000..ba1ad34fe --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/test.kts @@ -0,0 +1,2 @@ +println("Hello from Kotlin"); +workspace.model.addPerson("Kotlin"); \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/test.rb b/structurizr-dsl/src/test/resources/dsl/test.rb new file mode 100644 index 000000000..8b17cc3d4 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/test.rb @@ -0,0 +1,2 @@ +puts "Hello from JRuby" +workspace.model.addPerson("Ruby") \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/this.dsl b/structurizr-dsl/src/test/resources/dsl/this.dsl new file mode 100644 index 000000000..acc5f9af5 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/this.dsl @@ -0,0 +1,71 @@ +workspace { + + model { + custom = element "Element" + + s = softwareSystem "Software System" { + custom -> this + + c = container "Container" { + custom -> this + + component "Component" { + custom -> this + } + } + } + + live = deploymentEnvironment "live" { + deploymentNode "Live" { + in = infrastructureNode "Infrastructure Node" { + custom -> this + } + + dn = deploymentNode "Deployment Node" { + in -> this + + softwareSystemInstance s { + in -> this + } + + containerInstance c { + in -> this + } + } + } + } + } + + views { + systemLandscape { + include * + include custom + autolayout + } + + systemContext s { + include * + include custom + autolayout + } + + container s { + include * + include custom + autolayout + } + + component c { + include * + include custom + autolayout + } + + deployment * live { + include * + include custom + autolayout + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/unexpected-tokens-after-workspace.dsl b/structurizr-dsl/src/test/resources/dsl/unexpected-tokens-after-workspace.dsl new file mode 100644 index 000000000..b63613317 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/unexpected-tokens-after-workspace.dsl @@ -0,0 +1,4 @@ +workspace { +} + +hello world \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/unexpected-tokens-before-workspace.dsl b/structurizr-dsl/src/test/resources/dsl/unexpected-tokens-before-workspace.dsl new file mode 100644 index 000000000..1144a9886 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/unexpected-tokens-before-workspace.dsl @@ -0,0 +1,4 @@ +hello world + +workspace { +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/unexpected-tokens-in-workspace.dsl b/structurizr-dsl/src/test/resources/dsl/unexpected-tokens-in-workspace.dsl new file mode 100644 index 000000000..2ab0050d4 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/unexpected-tokens-in-workspace.dsl @@ -0,0 +1,5 @@ +workspace { + + softwareSystem "Name" + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/utf8.dsl b/structurizr-dsl/src/test/resources/dsl/utf8.dsl new file mode 100644 index 000000000..9a00ab44d --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/utf8.dsl @@ -0,0 +1,7 @@ +workspace { + + model { + user = person "你好 Usér 🙂" + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/views-without-keys.dsl b/structurizr-dsl/src/test/resources/dsl/views-without-keys.dsl new file mode 100644 index 000000000..ec9074d36 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/views-without-keys.dsl @@ -0,0 +1,18 @@ +workspace { + + model { + person "User" + } + + views { + systemLandscape { + } + + systemLandscape { + } + + systemLandscape { + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/workspace-properties.dsl b/structurizr-dsl/src/test/resources/dsl/workspace-properties.dsl new file mode 100644 index 000000000..15b7ff9c7 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/workspace-properties.dsl @@ -0,0 +1,7 @@ +workspace { + + properties { + "structurizr.dslEditor" "false" + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/workspace-with-bom.dsl b/structurizr-dsl/src/test/resources/dsl/workspace-with-bom.dsl new file mode 100644 index 000000000..c5556fd63 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/workspace-with-bom.dsl @@ -0,0 +1,29 @@ +workspace "Getting Started" "This is a model of my software system." { + + model { + user = person "User" "A user of my software system." + softwareSystem = softwareSystem "Software System" "My software system, code-named \"X\"." + + user -> softwareSystem "Uses" + } + + views { + systemContext softwareSystem "SystemContext" "An example of a System Context diagram." { + include * + autoLayout + } + + styles { + element "Software System" { + background #1168bd + color #ffffff + } + element "Person" { + shape person + background #08427b + color #ffffff + } + } + } + +} \ No newline at end of file diff --git a/structurizr-export/README.md b/structurizr-export/README.md new file mode 100644 index 000000000..7ad1a2da5 --- /dev/null +++ b/structurizr-export/README.md @@ -0,0 +1,9 @@ +# structurizr-export + +[![Maven Central](https://img.shields.io/maven-central/v/com.structurizr/structurizr-export.svg?label=Maven%20Central)](https://search.maven.org/artifact/com.structurizr/structurizr-export) + +This library provides the ability to export the model and views defined in a Structurizr workspace to a number of formats, +including PlantUML and C4-PlantUML, Mermaid, DOT, WebSequenceDiagrams, and Ilograph. + +- [Structurizr DSL demo page](https://structurizr.com/dsl) (demo of export formats) +- [Documentation](https://docs.structurizr.com/export) diff --git a/structurizr-export/build.gradle b/structurizr-export/build.gradle new file mode 100644 index 000000000..0729fcb26 --- /dev/null +++ b/structurizr-export/build.gradle @@ -0,0 +1,10 @@ +dependencies { + + api project(':structurizr-core') + + testImplementation project(':structurizr-client') + testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2' + +} + +description = 'Export Structurizr models and views to external formats' diff --git a/structurizr-export/src/main/java/com/structurizr/export/AbstractDiagramExporter.java b/structurizr-export/src/main/java/com/structurizr/export/AbstractDiagramExporter.java new file mode 100644 index 000000000..9c2202bd8 --- /dev/null +++ b/structurizr-export/src/main/java/com/structurizr/export/AbstractDiagramExporter.java @@ -0,0 +1,748 @@ +package com.structurizr.export; + +import com.structurizr.Workspace; +import com.structurizr.model.*; +import com.structurizr.util.StringUtils; +import com.structurizr.view.*; + +import java.util.*; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +public abstract class AbstractDiagramExporter extends AbstractExporter implements DiagramExporter { + + protected static final String GROUP_SEPARATOR_PROPERTY_NAME = "structurizr.groupSeparator"; + + private Object frame = null; + + /** + * Exports all views in the workspace. + * + * @param workspace the workspace containing the views to be written + * @return a collection of diagram definitions, one per view + */ + public final Collection<Diagram> export(Workspace workspace) { + if (workspace == null) { + throw new IllegalArgumentException("A workspace must be provided."); + } + + Collection<Diagram> diagrams = new ArrayList<>(); + + for (CustomView view : workspace.getViews().getCustomViews()) { + Diagram diagram = export(view); + if (diagram != null) { + diagrams.add(diagram); + } + } + + for (SystemLandscapeView view : workspace.getViews().getSystemLandscapeViews()) { + Diagram diagram = export(view); + if (diagram != null) { + diagrams.add(diagram); + } + } + + for (SystemContextView view : workspace.getViews().getSystemContextViews()) { + Diagram diagram = export(view); + if (diagram != null) { + diagrams.add(diagram); + } + } + + for (ContainerView view : workspace.getViews().getContainerViews()) { + Diagram diagram = export(view); + if (diagram != null) { + diagrams.add(diagram); + } + } + + for (ComponentView view : workspace.getViews().getComponentViews()) { + Diagram diagram = export(view); + if (diagram != null) { + diagrams.add(diagram); + } + } + + for (DynamicView view : workspace.getViews().getDynamicViews()) { + Diagram diagram = export(view); + if (diagram != null) { + diagrams.add(diagram); + } + } + + for (DeploymentView view : workspace.getViews().getDeploymentViews()) { + Diagram diagram = export(view); + if (diagram != null) { + diagrams.add(diagram); + } + } + + return diagrams; + } + + public Diagram export(CustomView view) { + Diagram diagram = export(view, null); + + if (isAnimationSupported(view) && !view.getAnimations().isEmpty()) { + for (Animation animation : view.getAnimations()) { + Diagram frame = export(view, animation.getOrder()); + diagram.addFrame(frame); + } + } + + diagram.setLegend(createLegend(view)); + return diagram; + } + + private Diagram export(CustomView view, Integer animationStep) { + this.frame = animationStep; + + IndentingWriter writer = new IndentingWriter(); + writeHeader(view, writer); + + List<GroupableElement> elements = new ArrayList<>(); + for (ElementView elementView : view.getElements()) { + elements.add((CustomElement)elementView.getElement()); + } + + writeElements(view, elements, writer); + + writer.writeLine(); + writeRelationships(view, writer); + writeFooter(view, writer); + + return createDiagram(view, writer.toString()); + } + + public Diagram export(SystemLandscapeView view) { + Diagram diagram = export(view, null); + + if (isAnimationSupported(view) && !view.getAnimations().isEmpty()) { + for (Animation animation : view.getAnimations()) { + Diagram frame = export(view, animation.getOrder()); + diagram.addFrame(frame); + } + } + + diagram.setLegend(createLegend(view)); + return diagram; + } + + private Diagram export(SystemLandscapeView view, Integer animationStep) { + this.frame = animationStep; + return export(view, view.isEnterpriseBoundaryVisible()); + } + + public Diagram export(SystemContextView view) { + Diagram diagram = export(view, null); + + if (isAnimationSupported(view) && !view.getAnimations().isEmpty()) { + for (Animation animation : view.getAnimations()) { + Diagram frame = export(view, animation.getOrder()); + diagram.addFrame(frame); + } + } + + diagram.setLegend(createLegend(view)); + return diagram; + } + + private Diagram export(SystemContextView view, Integer animationStep) { + this.frame = animationStep; + return export(view, view.isEnterpriseBoundaryVisible()); + } + + private Diagram export(ModelView view, boolean enterpriseBoundaryIsVisible) { + IndentingWriter writer = new IndentingWriter(); + writeHeader(view, writer); + + boolean showEnterpriseBoundary = + enterpriseBoundaryIsVisible && + (view.getElements().stream().map(ElementView::getElement).anyMatch(e -> e instanceof Person && ((Person)e).getLocation() == Location.Internal) || + view.getElements().stream().map(ElementView::getElement).anyMatch(e -> e instanceof SoftwareSystem && ((SoftwareSystem)e).getLocation() == Location.Internal)); + + if (showEnterpriseBoundary) { + String enterpriseName = "Enterprise"; + if (view.getModel().getEnterprise() != null) { + enterpriseName = view.getModel().getEnterprise().getName(); + } + + startEnterpriseBoundary(view, enterpriseName, writer); + + List<GroupableElement> elementsInsideEnterpriseBoundary = new ArrayList<>(); + for (ElementView elementView : view.getElements()) { + if (elementView.getElement() instanceof Person && ((Person)elementView.getElement()).getLocation() == Location.Internal) { + elementsInsideEnterpriseBoundary.add((StaticStructureElement)elementView.getElement()); + } + if (elementView.getElement() instanceof SoftwareSystem && ((SoftwareSystem)elementView.getElement()).getLocation() == Location.Internal) { + elementsInsideEnterpriseBoundary.add((StaticStructureElement)elementView.getElement()); + } + } + writeElements(view, elementsInsideEnterpriseBoundary, writer); + + endEnterpriseBoundary(view, writer); + + List<GroupableElement> elementsOutsideEnterpriseBoundary = new ArrayList<>(); + for (ElementView elementView : view.getElements()) { + if (elementView.getElement() instanceof Person && ((Person)elementView.getElement()).getLocation() != Location.Internal) { + elementsOutsideEnterpriseBoundary.add((StaticStructureElement)elementView.getElement()); + } + if (elementView.getElement() instanceof SoftwareSystem && ((SoftwareSystem)elementView.getElement()).getLocation() != Location.Internal) { + elementsOutsideEnterpriseBoundary.add((StaticStructureElement)elementView.getElement()); + } + if (elementView.getElement() instanceof CustomElement) { + elementsOutsideEnterpriseBoundary.add((CustomElement)elementView.getElement()); + } + } + writeElements(view, elementsOutsideEnterpriseBoundary, writer); + } else { + List<GroupableElement> elements = new ArrayList<>(); + for (ElementView elementView : view.getElements()) { + elements.add((GroupableElement)elementView.getElement()); + } + writeElements(view, elements, writer); + } + + writer.writeLine(); + writeRelationships(view, writer); + writeFooter(view, writer); + + return createDiagram(view, writer.toString()); + } + + public Diagram export(ContainerView view) { + Diagram diagram = export(view, null); + + if (isAnimationSupported(view) && !view.getAnimations().isEmpty()) { + for (Animation animation : view.getAnimations()) { + Diagram frame = export(view, animation.getOrder()); + diagram.addFrame(frame); + } + } + + diagram.setLegend(createLegend(view)); + return diagram; + } + + public Diagram export(ContainerView view, Integer animationStep) { + this.frame = animationStep; + IndentingWriter writer = new IndentingWriter(); + writeHeader(view, writer); + + boolean elementsWritten = false; + for (ElementView elementView : view.getElements()) { + if (!(elementView.getElement() instanceof Container)) { + writeElement(view, elementView.getElement(), writer); + elementsWritten = true; + } + } + + if (elementsWritten) { + writer.writeLine(); + } + + List<SoftwareSystem> softwareSystems = getBoundarySoftwareSystems(view); + for (SoftwareSystem softwareSystem : softwareSystems) { + startSoftwareSystemBoundary(view, softwareSystem, writer); + + List<GroupableElement> scopedElements = new ArrayList<>(); + for (ElementView elementView : view.getElements()) { + if (elementView.getElement().getParent() == softwareSystem) { + scopedElements.add((StaticStructureElement) elementView.getElement()); + } + } + + writeElements(view, scopedElements, writer); + + endSoftwareSystemBoundary(view, writer); + } + + writeRelationships(view, writer); + + writeFooter(view, writer); + + return createDiagram(view, writer.toString()); + } + + protected List<SoftwareSystem> getBoundarySoftwareSystems(ModelView view) { + List<SoftwareSystem> softwareSystems = new ArrayList<>(view.getElements().stream().map(ElementView::getElement).filter(e -> e instanceof Container).map(c -> ((Container)c).getSoftwareSystem()).collect(Collectors.toSet())); + softwareSystems.sort(Comparator.comparing(Element::getId)); + + return softwareSystems; + } + + public Diagram export(ComponentView view) { + Diagram diagram = export(view, null); + + if (isAnimationSupported(view) && !view.getAnimations().isEmpty()) { + for (Animation animation : view.getAnimations()) { + Diagram frame = export(view, animation.getOrder()); + diagram.addFrame(frame); + } + } + + diagram.setLegend(createLegend(view)); + return diagram; + } + + public Diagram export(ComponentView view, Integer animationStep) { + this.frame = animationStep; + IndentingWriter writer = new IndentingWriter(); + writeHeader(view, writer); + + boolean elementsWritten = false; + for (ElementView elementView : view.getElements()) { + if (!(elementView.getElement() instanceof Component)) { + writeElement(view, elementView.getElement(), writer); + elementsWritten = true; + } + } + + if (elementsWritten) { + writer.writeLine(); + } + + boolean includeSoftwareSystemBoundaries = "true".equals(view.getProperties().getOrDefault("structurizr.softwareSystemBoundaries", "false")); + + List<Container> containers = getBoundaryContainers(view); + Set<SoftwareSystem> softwareSystems = containers.stream().map(Container::getSoftwareSystem).collect(Collectors.toCollection(LinkedHashSet::new)); + for (SoftwareSystem softwareSystem : softwareSystems) { + + if (includeSoftwareSystemBoundaries) { + startSoftwareSystemBoundary(view, softwareSystem, writer); + writer.indent(); + } + + for (Container container : containers) { + if (container.getSoftwareSystem() == softwareSystem) { + startContainerBoundary(view, container, writer); + + List<GroupableElement> scopedElements = new ArrayList<>(); + for (ElementView elementView : view.getElements()) { + if (elementView.getElement().getParent() == container) { + scopedElements.add((StaticStructureElement) elementView.getElement()); + } + } + writeElements(view, scopedElements, writer); + + endContainerBoundary(view, writer); + } + } + + if (includeSoftwareSystemBoundaries) { + endSoftwareSystemBoundary(view, writer); + writer.outdent(); + } + } + + writeRelationships(view, writer); + + writeFooter(view, writer); + + return createDiagram(view, writer.toString()); + } + + protected List<Container> getBoundaryContainers(ModelView view) { + List<Container> containers = new ArrayList<>(view.getElements().stream().map(ElementView::getElement).filter(e -> e instanceof Component).map(c -> ((Component)c).getContainer()).collect(Collectors.toSet())); + containers.sort(Comparator.comparing(Element::getId)); + + return containers; + } + + public Diagram export(DynamicView view) { + Diagram diagram = export(view, null); + + if (isAnimationSupported(view)) { + LinkedHashSet<String> orders = new LinkedHashSet<>(); + for (RelationshipView relationshipView : view.getRelationships()) { + orders.add(relationshipView.getOrder()); + } + + for (String order : orders) { + Diagram frame = export(view, order); + diagram.addFrame(frame); + } + } + + diagram.setLegend(createLegend(view)); + return diagram; + } + + public Diagram export(DynamicView view, String order) { + this.frame = order; + IndentingWriter writer = new IndentingWriter(); + writeHeader(view, writer); + + boolean elementsWritten = false; + + Element element = view.getElement(); + + if (element == null) { + // dynamic view with no scope + List<GroupableElement> elements = new ArrayList<>(); + for (ElementView elementView : view.getElements()) { + elements.add((StaticStructureElement) elementView.getElement()); + } + writeElements(view, elements, writer); + } else { + if (element instanceof SoftwareSystem) { + // dynamic view with software system scope + List<SoftwareSystem> softwareSystems = getBoundarySoftwareSystems(view); + for (SoftwareSystem softwareSystem : softwareSystems) { + startSoftwareSystemBoundary(view, softwareSystem, writer); + + List<GroupableElement> scopedElements = new ArrayList<>(); + for (ElementView elementView : view.getElements()) { + if (elementView.getElement().getParent() == softwareSystem) { + scopedElements.add((StaticStructureElement) elementView.getElement()); + } + } + + writeElements(view, scopedElements, writer); + + endSoftwareSystemBoundary(view, writer); + } + + for (ElementView elementView : view.getElements()) { + if (elementView.getElement().getParent() == null) { + writeElement(view, elementView.getElement(), writer); + elementsWritten = true; + } + } + } else if (element instanceof Container) { + // dynamic view with container scope + boolean includeSoftwareSystemBoundaries = "true".equals(view.getProperties().getOrDefault("structurizr.softwareSystemBoundaries", "false")); + + List<Container> containers = getBoundaryContainers(view); + Set<SoftwareSystem> softwareSystems = containers.stream().map(Container::getSoftwareSystem).collect(Collectors.toCollection(LinkedHashSet::new)); + for (SoftwareSystem softwareSystem : softwareSystems) { + + if (includeSoftwareSystemBoundaries) { + startSoftwareSystemBoundary(view, softwareSystem, writer); + writer.indent(); + } + + for (Container container : containers) { + if (container.getSoftwareSystem() == softwareSystem) { + startContainerBoundary(view, container, writer); + + List<GroupableElement> scopedElements = new ArrayList<>(); + for (ElementView elementView : view.getElements()) { + if (elementView.getElement().getParent() == container) { + scopedElements.add((StaticStructureElement) elementView.getElement()); + } + } + writeElements(view, scopedElements, writer); + + endContainerBoundary(view, writer); + } + } + + if (includeSoftwareSystemBoundaries) { + endSoftwareSystemBoundary(view, writer); + writer.outdent(); + } + } + + for (ElementView elementView : view.getElements()) { + if (!(elementView.getElement().getParent() instanceof Container)) { + writeElement(view, elementView.getElement(), writer); + elementsWritten = true; + } + } + } + } + + if (elementsWritten) { + writer.writeLine(); + } + + writeRelationships(view, writer); + writeFooter(view, writer); + + return createDiagram(view, writer.toString()); + } + + public Diagram export(DeploymentView view) { + Diagram diagram = export(view, null); + + if (isAnimationSupported(view) && !view.getAnimations().isEmpty()) { + for (Animation animation : view.getAnimations()) { + Diagram frame = export(view, animation.getOrder()); + diagram.addFrame(frame); + } + } + + diagram.setLegend(createLegend(view)); + return diagram; + } + + public Diagram export(DeploymentView view, Integer animationStep) { + this.frame = animationStep; + IndentingWriter writer = new IndentingWriter(); + writeHeader(view, writer); + + List<GroupableElement> elements = new ArrayList<>(); + + for (ElementView elementView : view.getElements()) { + if (elementView.getElement() instanceof DeploymentNode && elementView.getElement().getParent() == null) { + elements.add((DeploymentNode)elementView.getElement()); + } + } + + writeElements(view, elements, writer); + + writeRelationships(view, writer); + writeFooter(view, writer); + + return createDiagram(view, writer.toString()); + } + + protected void writeElements(ModelView view, List<GroupableElement> elements, IndentingWriter writer) { + String groupSeparator = view.getModel().getProperties().get(GROUP_SEPARATOR_PROPERTY_NAME); + boolean nested = !StringUtils.isNullOrEmpty(groupSeparator); + + elements.sort(Comparator.comparing(Element::getId)); + + Set<String> groupsAsSet = new HashSet<>(); + for (GroupableElement element : elements) { + String group = element.getGroup(); + + if (!StringUtils.isNullOrEmpty(group)) { + groupsAsSet.add(group); + + if (nested) { + while (group.contains(groupSeparator)) { + group = group.substring(0, group.lastIndexOf(groupSeparator)); + groupsAsSet.add(group); + } + } + } + } + + List<String> groupsAsList = new ArrayList<>(groupsAsSet); + Collections.sort(groupsAsList); + + // first render grouped elements + if (groupsAsList.size() > 0) { + if (nested) { + String context = ""; + + for (String group : groupsAsList) { + int groupCount = group.split(Pattern.quote(groupSeparator)).length; + int contextCount = context.split(Pattern.quote(groupSeparator)).length; + + if (groupCount > contextCount) { + // moved from a to a/b + // - increase padding + writer.indent(); + } else if (groupCount == contextCount) { + // moved from a/b to a/c + // - close off previous subgraph + if (context.length() > 1) { + endGroupBoundary(view, writer); + } + } else { + // moved from a/b/c to a/b or a + // - close off previous subgraphs + // - close off current subgraph + for (int i = 0; i < (contextCount - groupCount); i++) { + endGroupBoundary(view, writer); + writer.outdent(); + } + endGroupBoundary(view, writer); + } + + startGroupBoundary(view, group, writer); + + for (GroupableElement element : elements) { + if (group.equals(element.getGroup())) { + write(view, element, writer); + } + } + + context = group; + } + + int contextCount = context.split(Pattern.quote(groupSeparator)).length; + for (int i = 0; i < contextCount; i++) { + endGroupBoundary(view, writer); + + if (i < contextCount-1) { + writer.outdent(); + } + } + } else { + for (String group : groupsAsList) { + startGroupBoundary(view, group, writer); + + for (GroupableElement element : elements) { + if (group.equals(element.getGroup())) { + write(view, element, writer); + } + } + + endGroupBoundary(view, writer); + } + } + } + + // then render ungrouped elements + for (GroupableElement element : elements) { + if (StringUtils.isNullOrEmpty(element.getGroup())) { + write(view, element, writer); + } + } + } + + protected void writeRelationships(ModelView view, IndentingWriter writer) { + Collection<RelationshipView> relationshipList; + + if (view instanceof DynamicView) { + relationshipList = view.getRelationships(); + } else { + relationshipList = view.getRelationships().stream().sorted(Comparator.comparing(rv -> rv.getRelationship().getId())).collect(Collectors.toList()); + } + + for (RelationshipView relationshipView : relationshipList) { + writeRelationship(view, relationshipView, writer); + } + } + + protected abstract void writeHeader(ModelView view, IndentingWriter writer); + protected abstract void writeFooter(ModelView view, IndentingWriter writer); + + protected abstract void startEnterpriseBoundary(ModelView view, String enterpriseName, IndentingWriter writer); + protected abstract void endEnterpriseBoundary(ModelView view, IndentingWriter writer); + + protected abstract void startGroupBoundary(ModelView view, String group, IndentingWriter writer); + protected abstract void endGroupBoundary(ModelView view, IndentingWriter writer); + + protected abstract void startSoftwareSystemBoundary(ModelView view, SoftwareSystem softwareSystem, IndentingWriter writer); + protected abstract void endSoftwareSystemBoundary(ModelView view, IndentingWriter writer); + + protected abstract void startContainerBoundary(ModelView view, Container container, IndentingWriter writer); + protected abstract void endContainerBoundary(ModelView view, IndentingWriter writer); + + protected abstract void startDeploymentNodeBoundary(DeploymentView view, DeploymentNode deploymentNode, IndentingWriter writer); + protected abstract void endDeploymentNodeBoundary(ModelView view, IndentingWriter writer); + + private void write(ModelView view, Element element, IndentingWriter writer) { + if (view instanceof DeploymentView && element instanceof DeploymentNode) { + writeDeploymentNode((DeploymentView)view, (DeploymentNode)element, writer); + } else { + writeElement(view, element, writer); + } + } + + private void writeDeploymentNode(DeploymentView view, DeploymentNode deploymentNode, IndentingWriter writer) { + startDeploymentNodeBoundary(view, deploymentNode, writer); + + List<GroupableElement> elements = new ArrayList<>(); + + List<DeploymentNode> children = new ArrayList<>(deploymentNode.getChildren()); + children.sort(Comparator.comparing(DeploymentNode::getName)); + for (DeploymentNode child : children) { + if (view.isElementInView(child)) { + elements.add(child); + } + } + + List<InfrastructureNode> infrastructureNodes = new ArrayList<>(deploymentNode.getInfrastructureNodes()); + infrastructureNodes.sort(Comparator.comparing(InfrastructureNode::getName)); + for (InfrastructureNode infrastructureNode : infrastructureNodes) { + if (view.isElementInView(infrastructureNode)) { + elements.add(infrastructureNode); + } + } + + List<SoftwareSystemInstance> softwareSystemInstances = new ArrayList<>(deploymentNode.getSoftwareSystemInstances()); + softwareSystemInstances.sort(Comparator.comparing(SoftwareSystemInstance::getName)); + for (SoftwareSystemInstance softwareSystemInstance : softwareSystemInstances) { + if (view.isElementInView(softwareSystemInstance)) { + elements.add(softwareSystemInstance); + } + } + + List<ContainerInstance> containerInstances = new ArrayList<>(deploymentNode.getContainerInstances()); + containerInstances.sort(Comparator.comparing(ContainerInstance::getName)); + for (ContainerInstance containerInstance : containerInstances) { + if (view.isElementInView(containerInstance)) { + elements.add(containerInstance); + } + } + + writeElements(view, elements, writer); + + endDeploymentNodeBoundary(view, writer); + } + + protected abstract void writeElement(ModelView view, Element element, IndentingWriter writer); + protected abstract void writeRelationship(ModelView view, RelationshipView relationshipView, IndentingWriter writer); + + protected boolean isAnimationSupported(ModelView view) { + return false; + } + + protected boolean isVisible(ModelView view, Element element) { + if (frame != null) { + Set<String> elementIds = new HashSet<>(); + + if (view instanceof StaticView) { + int step = (int)frame; + if (step > 0) { + StaticView staticView = (StaticView) view; + staticView.getAnimations().stream().filter(a -> a.getOrder() <= step).forEach(a -> { + elementIds.addAll(a.getElements()); + }); + + return elementIds.contains(element.getId()); + } + } else if (view instanceof DeploymentView) { + int step = (int)frame; + if (step > 0) { + DeploymentView deploymentView = (DeploymentView) view; + deploymentView.getAnimations().stream().filter(a -> a.getOrder() <= step).forEach(a -> { + elementIds.addAll(a.getElements()); + }); + + return elementIds.contains(element.getId()); + } + } else if (view instanceof DynamicView) { + String order = (String)frame; + view.getRelationships().stream().filter(rv -> order.equals(rv.getOrder())).forEach(rv -> { + elementIds.add(rv.getRelationship().getSourceId()); + elementIds.add(rv.getRelationship().getDestinationId()); + }); + + return elementIds.contains(element.getId()); + } + } + + return true; + } + + protected boolean isVisible(ModelView view, RelationshipView relationshipView) { + if (view instanceof DynamicView && frame != null) { + return frame.equals(relationshipView.getOrder()); + } + + return true; + } + + protected abstract Diagram createDiagram(ModelView view, String definition); + + protected Legend createLegend(ModelView view) { + return null; + } + + protected String getViewOrViewSetProperty(ModelView view, String name, String defaultValue) { + ViewSet views = view.getViewSet(); + + return + view.getProperties().getOrDefault(name, + views.getConfiguration().getProperties().getOrDefault(name, defaultValue) + ); + } + +} \ No newline at end of file diff --git a/structurizr-export/src/main/java/com/structurizr/export/AbstractExporter.java b/structurizr-export/src/main/java/com/structurizr/export/AbstractExporter.java new file mode 100644 index 000000000..d58d573d6 --- /dev/null +++ b/structurizr-export/src/main/java/com/structurizr/export/AbstractExporter.java @@ -0,0 +1,119 @@ +package com.structurizr.export; + +import com.structurizr.Workspace; +import com.structurizr.model.*; +import com.structurizr.util.StringUtils; +import com.structurizr.view.*; + +public abstract class AbstractExporter { + + protected String breakText(int maxWidth, int fontSize, String s) { + if (StringUtils.isNullOrEmpty(s)) { + return ""; + } + + StringBuilder buf = new StringBuilder(); + + double characterWidth = fontSize * 0.6; + int maxCharacters = (int)(maxWidth / characterWidth); + + if (s.length() < maxCharacters) { + return s; + } + + String[] words = s.split(" "); + String line = null; + for (String word : words) { + if (line == null) { + line = word; + } else { + if ((line.length() + word.length() + 1) < maxCharacters) { + line += " "; + line += word; + } else { + buf.append(line); + buf.append("<br />"); + line = word; + } + } + } + + if (line != null) { + buf.append(line); + } + + return buf.toString(); + } + + protected String typeOf(Workspace workspace, Element e, boolean includeMetadataSymbols) { + return typeOf(workspace.getViews().getConfiguration(), e, includeMetadataSymbols); + } + + protected String typeOf(ModelView view, Element e, boolean includeMetadataSymbols) { + return typeOf(view.getViewSet().getConfiguration(), e, includeMetadataSymbols); + } + + private String typeOf(Configuration configuration, Element e, boolean includeMetadataSymbols) { + String type = ""; + + if (e instanceof Person) { + type = configuration.getTerminology().findTerminology(e); + } else if (e instanceof SoftwareSystem) { + type = configuration.getTerminology().findTerminology(e); + } else if (e instanceof Container) { + Container container = (Container)e; + type = configuration.getTerminology().findTerminology(e) + (hasValue(container.getTechnology()) ? ": " + container.getTechnology() : ""); + } else if (e instanceof Component) { + Component component = (Component)e; + type = configuration.getTerminology().findTerminology(e) + (hasValue(component.getTechnology()) ? ": " + component.getTechnology() : ""); + } else if (e instanceof DeploymentNode) { + DeploymentNode deploymentNode = (DeploymentNode)e; + type = configuration.getTerminology().findTerminology(e) + (hasValue(deploymentNode.getTechnology()) ? ": " + deploymentNode.getTechnology() : ""); + } else if (e instanceof InfrastructureNode) { + InfrastructureNode infrastructureNode = (InfrastructureNode)e; + type = configuration.getTerminology().findTerminology(e) + (hasValue(infrastructureNode.getTechnology()) ? ": " + infrastructureNode.getTechnology() : ""); + } else if (e instanceof CustomElement) { + type = ((CustomElement)e).getMetadata(); + } + + if (StringUtils.isNullOrEmpty(type)) { + return type; + } + + if (includeMetadataSymbols) { + if (configuration.getMetadataSymbols() == null) { + configuration.setMetadataSymbols(MetadataSymbols.SquareBrackets); + } + + switch (configuration.getMetadataSymbols()) { + case RoundBrackets: + return "(" + type + ")"; + case CurlyBrackets: + return "{" + type + "}"; + case AngleBrackets: + return "<" + type + ">"; + case DoubleAngleBrackets: + return "<<" + type + ">>"; + case None: + return type; + default: + return "[" + type + "]"; + } + } else { + return type; + } + } + + protected boolean hasValue(String s) { + return !StringUtils.isNullOrEmpty(s); + } + + protected ElementStyle findElementStyle(ModelView view, Element element) { + return view.getViewSet().getConfiguration().getStyles().findElementStyle(element); + } + + protected RelationshipStyle findRelationshipStyle(ModelView view, Relationship relationship) { + return view.getViewSet().getConfiguration().getStyles().findRelationshipStyle(relationship); + } + +} \ No newline at end of file diff --git a/structurizr-export/src/main/java/com/structurizr/export/AbstractWorkspaceExporter.java b/structurizr-export/src/main/java/com/structurizr/export/AbstractWorkspaceExporter.java new file mode 100644 index 000000000..6190149ea --- /dev/null +++ b/structurizr-export/src/main/java/com/structurizr/export/AbstractWorkspaceExporter.java @@ -0,0 +1,4 @@ +package com.structurizr.export; + +public abstract class AbstractWorkspaceExporter extends AbstractExporter implements WorkspaceExporter { +} \ No newline at end of file diff --git a/structurizr-export/src/main/java/com/structurizr/export/Diagram.java b/structurizr-export/src/main/java/com/structurizr/export/Diagram.java new file mode 100644 index 000000000..271b7b925 --- /dev/null +++ b/structurizr-export/src/main/java/com/structurizr/export/Diagram.java @@ -0,0 +1,51 @@ +package com.structurizr.export; + +import com.structurizr.view.View; + +import java.util.ArrayList; +import java.util.List; + +public abstract class Diagram { + + private View view; + private String definition; + + private List<Diagram> frames = new ArrayList<>(); + private Legend legend; + + public Diagram(View view, String definition) { + this.view = view; + this.definition = definition; + } + + public String getKey() { + return view.getKey(); + } + + public View getView() { + return view; + } + + public String getDefinition() { + return definition; + } + + public void addFrame(Diagram frame) { + frames.add(frame); + } + + public List<Diagram> getFrames() { + return new ArrayList<>(frames); + } + + public Legend getLegend() { + return legend; + } + + public void setLegend(Legend legend) { + this.legend = legend; + } + + public abstract String getFileExtension(); + +} \ No newline at end of file diff --git a/structurizr-export/src/main/java/com/structurizr/export/DiagramExporter.java b/structurizr-export/src/main/java/com/structurizr/export/DiagramExporter.java new file mode 100644 index 000000000..73a43ad5b --- /dev/null +++ b/structurizr-export/src/main/java/com/structurizr/export/DiagramExporter.java @@ -0,0 +1,17 @@ +package com.structurizr.export; + +import com.structurizr.Workspace; + +import java.util.Collection; + +public interface DiagramExporter extends Exporter { + + /** + * Exports all views in the workspace. + * + * @param workspace the workspace containing the views to be written + * @return a collection of diagram definitions, one per view + */ + Collection<Diagram> export(Workspace workspace); + +} \ No newline at end of file diff --git a/structurizr-export/src/main/java/com/structurizr/export/Exporter.java b/structurizr-export/src/main/java/com/structurizr/export/Exporter.java new file mode 100644 index 000000000..45bf1b263 --- /dev/null +++ b/structurizr-export/src/main/java/com/structurizr/export/Exporter.java @@ -0,0 +1,4 @@ +package com.structurizr.export; + +public interface Exporter { +} \ No newline at end of file diff --git a/structurizr-export/src/main/java/com/structurizr/export/IndentType.java b/structurizr-export/src/main/java/com/structurizr/export/IndentType.java new file mode 100644 index 000000000..16534cb5c --- /dev/null +++ b/structurizr-export/src/main/java/com/structurizr/export/IndentType.java @@ -0,0 +1,8 @@ +package com.structurizr.export; + +public enum IndentType { + + Spaces, + Tabs + +} \ No newline at end of file diff --git a/structurizr-export/src/main/java/com/structurizr/export/IndentingWriter.java b/structurizr-export/src/main/java/com/structurizr/export/IndentingWriter.java new file mode 100644 index 000000000..f7bf0a0bc --- /dev/null +++ b/structurizr-export/src/main/java/com/structurizr/export/IndentingWriter.java @@ -0,0 +1,62 @@ +package com.structurizr.export; + +public final class IndentingWriter { + + private int indent = 0; + private IndentType indentType = IndentType.Spaces; + private int indentQuantity = 2; + + private StringBuilder buf = new StringBuilder(); + + public IndentingWriter() { + } + + public void setIndentType(IndentType indentType) { + this.indentType = indentType; + } + + public void setIndentQuantity(int indentQuantity) { + this.indentQuantity = indentQuantity; + } + + public void indent() { + indent++; + } + + public void outdent() { + indent--; + } + + private String padding() { + StringBuilder buf = new StringBuilder(); + + for (int i = 0; i < indent * indentQuantity; i++) { + if (indentType == IndentType.Spaces) { + buf.append(" "); + } else { + buf.append("\t"); + } + } + + return buf.toString(); + } + + public void writeLine() { + buf.append("\n"); + } + + public void writeLine(String content) { + buf.append(String.format("%s%s\n", padding(), content.replace("\n", "\\n"))); + } + + @Override + public String toString() { + String s = buf.toString(); + if (s.endsWith("\n")) { + s = s.substring(0, s.length()-1); + } + + return s; + } + +} \ No newline at end of file diff --git a/structurizr-export/src/main/java/com/structurizr/export/Legend.java b/structurizr-export/src/main/java/com/structurizr/export/Legend.java new file mode 100644 index 000000000..49e552381 --- /dev/null +++ b/structurizr-export/src/main/java/com/structurizr/export/Legend.java @@ -0,0 +1,15 @@ +package com.structurizr.export; + +public final class Legend { + + private final String definition; + + public Legend(String definition) { + this.definition = definition; + } + + public String getDefinition() { + return definition; + } + +} \ No newline at end of file diff --git a/structurizr-export/src/main/java/com/structurizr/export/WorkspaceExport.java b/structurizr-export/src/main/java/com/structurizr/export/WorkspaceExport.java new file mode 100644 index 000000000..1f25745fd --- /dev/null +++ b/structurizr-export/src/main/java/com/structurizr/export/WorkspaceExport.java @@ -0,0 +1,17 @@ +package com.structurizr.export; + +public abstract class WorkspaceExport { + + private String definition; + + public WorkspaceExport(String definition) { + this.definition = definition; + } + + public String getDefinition() { + return definition; + } + + public abstract String getFileExtension(); + +} \ No newline at end of file diff --git a/structurizr-export/src/main/java/com/structurizr/export/WorkspaceExporter.java b/structurizr-export/src/main/java/com/structurizr/export/WorkspaceExporter.java new file mode 100644 index 000000000..bd59cb3a5 --- /dev/null +++ b/structurizr-export/src/main/java/com/structurizr/export/WorkspaceExporter.java @@ -0,0 +1,15 @@ +package com.structurizr.export; + +import com.structurizr.Workspace; + +public interface WorkspaceExporter extends Exporter { + + /** + * Exports the entire workspace to a single String. + * + * @param workspace the workspace to be exported + * @return a String export of the workspace + */ + WorkspaceExport export(Workspace workspace); + +} \ No newline at end of file diff --git a/structurizr-export/src/main/java/com/structurizr/export/dot/DOTDiagram.java b/structurizr-export/src/main/java/com/structurizr/export/dot/DOTDiagram.java new file mode 100644 index 000000000..a66af0ea9 --- /dev/null +++ b/structurizr-export/src/main/java/com/structurizr/export/dot/DOTDiagram.java @@ -0,0 +1,17 @@ +package com.structurizr.export.dot; + +import com.structurizr.export.Diagram; +import com.structurizr.view.ModelView; + +public class DOTDiagram extends Diagram { + + public DOTDiagram(ModelView view, String definition) { + super(view, definition); + } + + @Override + public String getFileExtension() { + return "dot"; + } + +} \ No newline at end of file diff --git a/structurizr-export/src/main/java/com/structurizr/export/dot/DOTExporter.java b/structurizr-export/src/main/java/com/structurizr/export/dot/DOTExporter.java new file mode 100644 index 000000000..b4671c4ca --- /dev/null +++ b/structurizr-export/src/main/java/com/structurizr/export/dot/DOTExporter.java @@ -0,0 +1,434 @@ +package com.structurizr.export.dot; + +import com.structurizr.export.AbstractDiagramExporter; +import com.structurizr.export.Diagram; +import com.structurizr.export.IndentingWriter; +import com.structurizr.model.*; +import com.structurizr.util.StringUtils; +import com.structurizr.view.*; + +/** + * Exports Structurizr views to Graphviz DOT definitions. + */ +public class DOTExporter extends AbstractDiagramExporter { + + private static final String DEFAULT_FONT = "Arial"; + + private int clusterInternalMargin = 25; + + public DOTExporter() { + } + + public void setClusterInternalMargin(int clusterInternalMargin) { + this.clusterInternalMargin = clusterInternalMargin; + } + + @Override + protected void writeHeader(ModelView view, IndentingWriter writer) { + String title = view.getTitle(); + if (StringUtils.isNullOrEmpty(title)) { + title = view.getName(); + } + + String description = view.getDescription(); + if (StringUtils.isNullOrEmpty(description)) { + description = ""; + } else { + description = String.format("<br /><font point-size=\"24\">%s</font>", description); + } + + String fontName = DEFAULT_FONT; + Font font = view.getViewSet().getConfiguration().getBranding().getFont(); + if (font != null) { + fontName = font.getName(); + } + + RankDirection rankDirection = RankDirection.TopBottom; + + if (view.getAutomaticLayout() != null) { + switch (view.getAutomaticLayout().getRankDirection()) { + case TopBottom: + rankDirection = RankDirection.TopBottom; + break; + case BottomTop: + rankDirection = RankDirection.BottomTop; + break; + case LeftRight: + rankDirection = RankDirection.LeftRight; + break; + case RightLeft: + rankDirection = RankDirection.RightLeft; + break; + } + } + + writer.writeLine("digraph {"); + writer.indent(); + writer.writeLine("compound=true"); + writer.writeLine(String.format("graph [fontname=\"%s\", rankdir=%s, ranksep=1.0, nodesep=1.0]", fontName, rankDirection.getCode())); + writer.writeLine(String.format("node [fontname=\"%s\", shape=box, margin=\"0.4,0.3\"]", fontName)); + writer.writeLine(String.format("edge [fontname=\"%s\"]", fontName)); + writer.writeLine(String.format("label=<<br /><font point-size=\"34\">%s</font>%s>", title, description)); + writer.writeLine(); + } + + @Override + protected void writeFooter(ModelView view, IndentingWriter writer) { + writer.outdent(); + writer.writeLine("}"); + } + + @Override + protected void startEnterpriseBoundary(ModelView view, String enterpriseName, IndentingWriter writer) { + writer.writeLine("subgraph cluster_enterprise {"); + + writer.indent(); + writer.writeLine("margin=" + clusterInternalMargin); + writer.writeLine(String.format("label=<<font point-size=\"24\"><br />%s</font><br /><font point-size=\"19\">[Enterprise]</font>>", enterpriseName)); + writer.writeLine("labelloc=b"); + writer.writeLine("color=\"#444444\""); + writer.writeLine("fontcolor=\"#444444\""); + writer.writeLine("fillcolor=\"#ffffff\""); + writer.writeLine(); + } + + @Override + protected void endEnterpriseBoundary(ModelView view, IndentingWriter writer) { + writer.outdent(); + writer.writeLine("}"); + writer.writeLine(); + } + + @Override + protected void startGroupBoundary(ModelView view, String group, IndentingWriter writer) { + String color = "#cccccc"; + + String groupName = group; + + String groupSeparator = view.getModel().getProperties().get(GROUP_SEPARATOR_PROPERTY_NAME); + if (!StringUtils.isNullOrEmpty(groupSeparator)) { + groupName = group.substring(group.lastIndexOf(groupSeparator) + groupSeparator.length()); + } + + // is there a style for the group? + ElementStyle elementStyle = view.getViewSet().getConfiguration().getStyles().findElementStyle("Group:" + group); + + if (elementStyle == null || StringUtils.isNullOrEmpty(elementStyle.getColor())) { + // no, so is there a default group style? + elementStyle = view.getViewSet().getConfiguration().getStyles().findElementStyle("Group"); + } + + if (elementStyle != null && !StringUtils.isNullOrEmpty(elementStyle.getColor())) { + color = elementStyle.getColor(); + } + + writer.writeLine("subgraph \"cluster_group_" + groupName + "\" {"); + + writer.indent(); + writer.writeLine("margin=" + clusterInternalMargin); + writer.writeLine(String.format("label=<<font point-size=\"24\"><br />%s</font>>", groupName)); + writer.writeLine("labelloc=b"); + writer.writeLine(String.format("color=\"%s\"", color)); + writer.writeLine(String.format("fontcolor=\"%s\"", color)); + writer.writeLine("fillcolor=\"#ffffff\""); + writer.writeLine("style=\"dashed\""); + writer.writeLine(); + } + + @Override + protected void endGroupBoundary(ModelView view, IndentingWriter writer) { + writer.outdent(); + writer.writeLine("}"); + writer.writeLine(); + } + + @Override + protected void startSoftwareSystemBoundary(ModelView view, SoftwareSystem softwareSystem, IndentingWriter writer) { + String color; + if (softwareSystem.equals(view.getSoftwareSystem())) { + color = "#444444"; + } else { + color = "#cccccc"; + } + + writer.writeLine(String.format("subgraph cluster_%s {", softwareSystem.getId())); + writer.indent(); + writer.writeLine("margin=" + clusterInternalMargin); + writer.writeLine(String.format("label=<<font point-size=\"24\"><br />%s</font><br /><font point-size=\"19\">%s</font>>", softwareSystem.getName(), typeOf(view, softwareSystem, true))); + writer.writeLine("labelloc=b"); + writer.writeLine(String.format("color=\"%s\"", color)); + writer.writeLine(String.format("fontcolor=\"%s\"", color)); + writer.writeLine(String.format("fillcolor=\"%s\"", color)); + writer.writeLine(); + } + + @Override + protected void endSoftwareSystemBoundary(ModelView view, IndentingWriter writer) { + writer.outdent(); + writer.writeLine("}"); + writer.writeLine(); + } + + @Override + protected void startContainerBoundary(ModelView view, Container container, IndentingWriter writer) { + String color = "#444444"; + if (view instanceof ComponentView) { + if (container.equals(((ComponentView)view).getContainer())) { + color = "#444444"; + } else { + color = "#cccccc"; + } + } else if (view instanceof DynamicView) { + if (container.equals(((DynamicView)view).getElement())) { + color = "#444444"; + } else { + color = "#cccccc"; + } + } + + writer.writeLine(String.format("subgraph cluster_%s {", container.getId())); + writer.indent(); + writer.writeLine("margin=" + clusterInternalMargin); + writer.writeLine(String.format("label=<<font point-size=\"24\"><br />%s</font><br /><font point-size=\"19\">%s</font>>", container.getName(), typeOf(view, container, true))); + writer.writeLine("labelloc=b"); + writer.writeLine(String.format("color=\"%s\"", color)); + writer.writeLine(String.format("fontcolor=\"%s\"", color)); + writer.writeLine(String.format("fillcolor=\"%s\"", color)); + writer.writeLine(); + } + + @Override + protected void endContainerBoundary(ModelView view, IndentingWriter writer) { + writer.outdent(); + writer.writeLine("}"); + writer.writeLine(); + } + + @Override + protected void startDeploymentNodeBoundary(DeploymentView view, DeploymentNode deploymentNode, IndentingWriter writer) { + ElementStyle elementStyle = view.getViewSet().getConfiguration().getStyles().findElementStyle(deploymentNode); + + writer.writeLine(String.format("subgraph cluster_%s {", deploymentNode.getId())); + writer.indent(); + writer.writeLine("margin=" + clusterInternalMargin); + writer.writeLine(String.format("label=<<font point-size=\"24\">%s</font><br /><font point-size=\"19\">%s</font>>", deploymentNode.getName(), typeOf(view, deploymentNode, true))); + writer.writeLine("labelloc=b"); + writer.writeLine(String.format("color=\"%s\"", elementStyle.getStroke())); + writer.writeLine(String.format("fontcolor=\"%s\"", elementStyle.getColor())); + writer.writeLine("fillcolor=\"#ffffff\""); + writer.writeLine(); + } + + @Override + protected void endDeploymentNodeBoundary(ModelView view, IndentingWriter writer) { + writer.outdent(); + writer.writeLine("}"); + writer.writeLine(); + } + + @Override + protected void writeElement(ModelView view, Element element, IndentingWriter writer) { + ElementStyle elementStyle = view.getViewSet().getConfiguration().getStyles().findElementStyle(element); + + int nameFontSize = elementStyle.getFontSize() + 10; + int metadataFontSize = elementStyle.getFontSize() - 5; + int descriptionFontSize = elementStyle.getFontSize(); + + + String shape = shapeOf(view, element); + String name = element.getName(); + String description = element.getDescription(); + String type = typeOf(view, element, true); + + if (element instanceof StaticStructureElementInstance) { + StaticStructureElementInstance elementInstance = (StaticStructureElementInstance)element; + name = elementInstance.getElement().getName(); + description = elementInstance.getElement().getDescription(); + type = typeOf(view, elementInstance.getElement(), true); + shape = shapeOf(view, elementInstance.getElement()); + } + + if (StringUtils.isNullOrEmpty(name)) { + name = ""; + } else { + name = String.format("<font point-size=\"%s\">%s</font>", nameFontSize, breakText(elementStyle.getWidth(), nameFontSize, escape(name))); + } + + if (StringUtils.isNullOrEmpty(description) || false == elementStyle.getDescription()) { + description = ""; + } else { + description = String.format("<br /><br /><font point-size=\"%s\">%s</font>", descriptionFontSize, breakText(elementStyle.getWidth(), descriptionFontSize, escape(description))); + } + + if (StringUtils.isNullOrEmpty(type) || false == elementStyle.getMetadata()) { + type = ""; + } else { + type = String.format("<br /><font point-size=\"%s\">%s</font>", metadataFontSize, type); + } + + writer.writeLine(String.format("%s [id=%s,shape=%s, label=<%s%s%s>, style=filled, color=\"%s\", fillcolor=\"%s\", fontcolor=\"%s\"]", + element.getId(), + element.getId(), + shape, + name, + type, + description, + elementStyle.getStroke(), + elementStyle.getBackground(), + elementStyle.getColor() + )); + } + + @Override + protected void writeRelationship(ModelView view, RelationshipView relationshipView, IndentingWriter writer) { + Element source; + Element destination; + + RelationshipStyle relationshipStyle = view.getViewSet().getConfiguration().getStyles().findRelationshipStyle(relationshipView.getRelationship()); + relationshipStyle.setWidth(400); + int descriptionFontSize = relationshipStyle.getFontSize(); + int metadataFontSize = relationshipStyle.getFontSize() - 5; + + String description = relationshipView.getDescription(); + if (StringUtils.isNullOrEmpty(description)) { + description = relationshipView.getRelationship().getDescription(); + } + + if (!StringUtils.isNullOrEmpty(relationshipView.getOrder())) { + description = relationshipView.getOrder() + ". " + description; + } + + if (StringUtils.isNullOrEmpty(description)) { + description = ""; + } else { + description = breakText(relationshipStyle.getWidth(), descriptionFontSize, description); + description = String.format("<font point-size=\"%s\">%s</font>", descriptionFontSize, description); + } + + String technology = relationshipView.getRelationship().getTechnology(); + if (StringUtils.isNullOrEmpty(technology)) { + technology = ""; + } else { + technology = String.format("<br /><font point-size=\"%s\">[%s]</font>", metadataFontSize, technology); + } + + String clusterConfig = ""; + + if (relationshipView.getRelationship().getSource() instanceof DeploymentNode || relationshipView.getRelationship().getDestination() instanceof DeploymentNode) { + source = relationshipView.getRelationship().getSource(); + if (source instanceof DeploymentNode) { + source = findElementInside((DeploymentNode)source, view); + } + + destination = relationshipView.getRelationship().getDestination(); + if (destination instanceof DeploymentNode) { + destination = findElementInside((DeploymentNode)destination, view); + } + + if (source != null && destination != null) { + + if (relationshipView.getRelationship().getSource() instanceof DeploymentNode) { + clusterConfig += ",ltail=cluster_" + relationshipView.getRelationship().getSource().getId(); + } + + if (relationshipView.getRelationship().getDestination() instanceof DeploymentNode) { + clusterConfig += ",lhead=cluster_" + relationshipView.getRelationship().getDestination().getId(); + } + } + } else { + source = relationshipView.getRelationship().getSource(); + destination = relationshipView.getRelationship().getDestination(); + + if (relationshipView.isResponse() != null && relationshipView.isResponse()) { + source = relationshipView.getRelationship().getDestination(); + destination = relationshipView.getRelationship().getSource(); + } + } + + boolean solid = relationshipStyle.getStyle() == LineStyle.Solid || false == relationshipStyle.getDashed(); + + writer.writeLine(String.format("%s -> %s [id=%s, label=<%s%s>, style=\"%s\", color=\"%s\", fontcolor=\"%s\"%s]", + source.getId(), + destination.getId(), + relationshipView.getId(), + description, + technology, + solid ? "solid" : "dashed", + relationshipStyle.getColor(), + relationshipStyle.getColor(), + clusterConfig + )); + } + + private String escape(String s) { + if (StringUtils.isNullOrEmpty(s)) { + return s; + } else { + return s.replaceAll("\"", "\\\\\""); + } + } + + private String shapeOf(ModelView view, Element element) { + if (element instanceof DeploymentNode) { + return "node"; + } + + Shape shape = view.getViewSet().getConfiguration().getStyles().findElementStyle(element).getShape(); + switch(shape) { + case Circle: + return "circle"; + case Component: + return "component"; + case Cylinder: + return "cylinder"; + case Ellipse: + return "ellipse"; + case Folder: + return "folder"; + case Hexagon: + return "hexagon"; + case Diamond: + return "diamond"; + default: + return "rect"; + } + } + + private Element findElementInside(DeploymentNode deploymentNode, ModelView view) { + for (SoftwareSystemInstance softwareSystemInstance : deploymentNode.getSoftwareSystemInstances()) { + if (view.isElementInView(softwareSystemInstance)) { + return softwareSystemInstance; + } + } + + for (ContainerInstance containerInstance : deploymentNode.getContainerInstances()) { + if (view.isElementInView(containerInstance)) { + return containerInstance; + } + } + + for (InfrastructureNode infrastructureNode : deploymentNode.getInfrastructureNodes()) { + if (view.isElementInView(infrastructureNode)) { + return infrastructureNode; + } + } + + if (deploymentNode.hasChildren()) { + for (DeploymentNode child : deploymentNode.getChildren()) { + Element element = findElementInside(child, view); + + if (element != null) { + return element; + } + } + } + + return null; + } + + @Override + protected Diagram createDiagram(ModelView view, String definition) { + return new DOTDiagram(view, definition); + } + +} \ No newline at end of file diff --git a/structurizr-export/src/main/java/com/structurizr/export/dot/README.md b/structurizr-export/src/main/java/com/structurizr/export/dot/README.md new file mode 100644 index 000000000..f528413dc --- /dev/null +++ b/structurizr-export/src/main/java/com/structurizr/export/dot/README.md @@ -0,0 +1,6 @@ +# DOT (Graphviz) + +The [DOTExporter](DOTExporter.java) class provides a way to export views to +diagram definitions that are compatible with [Graphviz](https://graphviz.org). + +See https://docs.structurizr.com/export for more. \ No newline at end of file diff --git a/structurizr-export/src/main/java/com/structurizr/export/dot/RankDirection.java b/structurizr-export/src/main/java/com/structurizr/export/dot/RankDirection.java new file mode 100644 index 000000000..e79fef327 --- /dev/null +++ b/structurizr-export/src/main/java/com/structurizr/export/dot/RankDirection.java @@ -0,0 +1,23 @@ +package com.structurizr.export.dot; + +/** + * The various rank directions used by Graphviz. + */ +enum RankDirection { + + TopBottom("TB"), + BottomTop("BT"), + LeftRight("LR"), + RightLeft("RL"); + + private String code; + + RankDirection(String code) { + this.code = code; + } + + String getCode() { + return code; + } + +} \ No newline at end of file diff --git a/structurizr-export/src/main/java/com/structurizr/export/ilograph/IlographExporter.java b/structurizr-export/src/main/java/com/structurizr/export/ilograph/IlographExporter.java new file mode 100644 index 000000000..b091d40dc --- /dev/null +++ b/structurizr-export/src/main/java/com/structurizr/export/ilograph/IlographExporter.java @@ -0,0 +1,391 @@ +package com.structurizr.export.ilograph; + +import com.structurizr.Workspace; +import com.structurizr.export.AbstractWorkspaceExporter; +import com.structurizr.export.IndentingWriter; +import com.structurizr.export.WorkspaceExport; +import com.structurizr.model.*; +import com.structurizr.util.StringUtils; +import com.structurizr.view.*; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * Exports a Structurizr workspace to the Ilograph definition language, for use with https://app.ilograph.com/ + */ +public class IlographExporter extends AbstractWorkspaceExporter { + + public WorkspaceExport export(Workspace workspace) { + IndentingWriter writer = new IndentingWriter(); + writer.writeLine("resources:"); + writer.writeLine(); + writer.indent(); + + Model model = workspace.getModel(); + List<GroupableElement> elements = new ArrayList<>(); + + List<CustomElement> customElements = new ArrayList<>(model.getCustomElements()); + customElements.sort(Comparator.comparing(CustomElement::getId)); + for (CustomElement customElement : customElements) { + writeElement(writer, workspace, customElement); + elements.add(customElement); + } + + List<Person> people = new ArrayList<>(model.getPeople()); + people.sort(Comparator.comparing(Person::getId)); + for (Person person : people) { + writeElement(writer, workspace, person); + elements.add(person); + } + + List<SoftwareSystem> softwareSystems = new ArrayList<>(model.getSoftwareSystems()); + softwareSystems.sort(Comparator.comparing(SoftwareSystem::getId)); + for (SoftwareSystem softwareSystem : softwareSystems) { + writeElement(writer, workspace, softwareSystem); + elements.add(softwareSystem); + + if (!softwareSystem.getContainers().isEmpty()) { + writer.indent(); + writer.writeLine("children:"); + writer.indent(); + + List<Container> containers = new ArrayList<>(softwareSystem.getContainers()); + containers.sort(Comparator.comparing(Container::getId)); + for (Container container : containers) { + writeElement(writer, workspace, container); + elements.add(container); + + if (!container.getComponents().isEmpty()) { + writer.indent(); + writer.writeLine("children:"); + writer.indent(); + + List<Component> components = new ArrayList<>(container.getComponents()); + components.sort(Comparator.comparing(Component::getId)); + for (Component component : components) { + writeElement(writer, workspace, component); + elements.add(component); + } + + writer.outdent(); + writer.outdent(); + } + + } + + writer.outdent(); + writer.outdent(); + } + } + + List<DeploymentNode> deploymentNodes = new ArrayList<>(model.getDeploymentNodes()); + deploymentNodes.sort(Comparator.comparing(DeploymentNode::getId)); + for (DeploymentNode deploymentNode : deploymentNodes) { + writeDeploymentNode(workspace, deploymentNode, writer); + } + + Set<Relationship> relationships = new LinkedHashSet<>(); + Set<Class> elementTypes = new HashSet<>(); + + elementTypes.add(CustomElement.class); + elementTypes.add(Person.class); + elementTypes.add(SoftwareSystem.class); + for (GroupableElement element : elements) { + List<Relationship> sortedRelationships = new ArrayList<>(element.getRelationships()); + sortedRelationships.sort(Comparator.comparing(Relationship::getId)); + for (Relationship relationship : sortedRelationships) { + if (include(relationship, elementTypes)) { + relationships.add(relationship); + } + } + } + + elementTypes.add(Container.class); + for (GroupableElement element : elements) { + List<Relationship> sortedRelationships = new ArrayList<>(element.getRelationships()); + sortedRelationships.sort(Comparator.comparing(Relationship::getId)); + for (Relationship relationship : sortedRelationships) { + if (include(relationship, elementTypes)) { + relationships.add(relationship); + } + } + } + + elementTypes.add(Component.class); + for (GroupableElement element : elements) { + List<Relationship> sortedRelationships = new ArrayList<>(element.getRelationships()); + sortedRelationships.sort(Comparator.comparing(Relationship::getId)); + for (Relationship relationship : sortedRelationships) { + if (include(relationship, elementTypes)) { + relationships.add(relationship); + } + } + } + + writer.outdent(); + + writeRelationshipsForStaticStructurePerspective(workspace.getViews().getConfiguration(), relationships, writer); + + for (DynamicView dynamicView : workspace.getViews().getDynamicViews()) { + writeDynamicView(dynamicView, writer); + } + + Set<String> deploymentEnvironments = new HashSet<>(); + for (DeploymentNode deploymentNode : model.getDeploymentNodes()) { + deploymentEnvironments.add(deploymentNode.getEnvironment()); + } + List<String> sortedDeploymentEnvironments = new ArrayList<>(deploymentEnvironments); + sortedDeploymentEnvironments.sort(Comparator.comparing(String::toString)); + for (String deploymentEnvironment : sortedDeploymentEnvironments) { + writeDeploymentEnvironment(workspace, deploymentEnvironment, writer); + } + + return new IlographWorkspaceExport(writer.toString()); + } + + private void writeDeploymentNode(Workspace workspace, DeploymentNode deploymentNode, IndentingWriter writer) { + writeElement(writer, workspace, deploymentNode); + + boolean hasChildren = !deploymentNode.getChildren().isEmpty() || !deploymentNode.getInfrastructureNodes().isEmpty() || !deploymentNode.getSoftwareSystemInstances().isEmpty() || !deploymentNode.getContainerInstances().isEmpty(); + + if (hasChildren) { + writer.indent(); + writer.writeLine("children:"); + writer.indent(); + } + + List<DeploymentNode> deploymentNodes = new ArrayList<>(deploymentNode.getChildren()); + deploymentNodes.sort(Comparator.comparing(DeploymentNode::getId)); + for (DeploymentNode child : deploymentNodes) { + writeDeploymentNode(workspace, child, writer); + } + + List<InfrastructureNode> infrastructureNodes = new ArrayList<>(deploymentNode.getInfrastructureNodes()); + infrastructureNodes.sort(Comparator.comparing(InfrastructureNode::getId)); + for (InfrastructureNode infrastructureNode : infrastructureNodes) { + writeElement(writer, workspace, infrastructureNode); + } + + List<SoftwareSystemInstance> softwareSystemInstances = new ArrayList<>(deploymentNode.getSoftwareSystemInstances()); + softwareSystemInstances.sort(Comparator.comparing(SoftwareSystemInstance::getId)); + for (SoftwareSystemInstance softwareSystemInstance : softwareSystemInstances) { + writeElement(writer, workspace, softwareSystemInstance); + } + + List<ContainerInstance> containerInstances = new ArrayList<>(deploymentNode.getContainerInstances()); + containerInstances.sort(Comparator.comparing(ContainerInstance::getId)); + for (ContainerInstance containerInstance : containerInstances) { + writeElement(writer, workspace, containerInstance); + } + + writer.outdent(); + writer.outdent(); + } + + private void writeElement(IndentingWriter writer, Workspace workspace, Element element) { + writer.writeLine(String.format("- id: \"%s\"", element.getId())); + + String name; + String type; + String description; + ElementStyle elementStyle = workspace.getViews().getConfiguration().getStyles().findElementStyle(element); + + if (element instanceof StaticStructureElementInstance) { + StaticStructureElementInstance elementInstance = (StaticStructureElementInstance)element; + name = elementInstance.getElement().getName(); + type = typeOf(workspace, elementInstance.getElement(), true); + description = elementInstance.getElement().getDescription(); + } else { + name = element.getName(); + type = typeOf(workspace, element, true); + description = element.getDescription(); + } + + writer.indent(); + writer.writeLine(String.format("name: \"%s\"", name)); + writer.writeLine(String.format("subtitle: \"%s\"", type)); + + if (!StringUtils.isNullOrEmpty(description)) { + writer.writeLine(String.format("description: \"%s\"", description)); + } + + if (element instanceof DeploymentNode) { + writer.writeLine(String.format("backgroundColor: \"%s\"", "#ffffff")); + } else { + writer.writeLine(String.format("backgroundColor: \"%s\"", elementStyle.getBackground())); + } + writer.writeLine(String.format("color: \"%s\"", elementStyle.getColor())); + writer.writeLine(); + writer.outdent(); + } + + private void writeRelationshipsForStaticStructurePerspective(Configuration configuration, Collection<Relationship> relationships, IndentingWriter writer) { + writer.writeLine("perspectives:"); + writer.indent(); + writer.writeLine("- name: Static Structure"); + writer.indent(); + writer.writeLine("relations:"); + writer.indent(); + + for (Relationship relationship : relationships) { + RelationshipStyle relationshipStyle = configuration.getStyles().findRelationshipStyle(relationship); + + writer.writeLine(String.format("- from: \"%s\"", relationship.getSourceId())); + writer.indent(); + writer.writeLine(String.format("to: \"%s\"", relationship.getDestinationId())); + + if (!StringUtils.isNullOrEmpty(relationship.getDescription())) { + writer.writeLine(String.format("label: \"%s\"", relationship.getDescription())); + } + + if (!StringUtils.isNullOrEmpty(relationship.getTechnology())) { + writer.writeLine(String.format("description: \"%s\"", relationship.getTechnology())); + } + + if (!StringUtils.isNullOrEmpty(relationshipStyle.getColor())) { + writer.writeLine(String.format("color: \"%s\"", relationshipStyle.getColor())); + } + + writer.writeLine(); + writer.outdent(); + } + + writer.outdent(); + writer.outdent(); + writer.outdent(); + } + + private void writeDynamicView(DynamicView dynamicView, IndentingWriter writer) { + writer.indent(); + writer.writeLine("- name: Dynamic - " + dynamicView.getName()); + writer.indent(); + writer.writeLine("sequence:"); + + int count = 0; + for (RelationshipView relationshipView : dynamicView.getRelationships()) { + Relationship relationship = relationshipView.getRelationship(); + RelationshipStyle relationshipStyle = dynamicView.getViewSet().getConfiguration().getStyles().findRelationshipStyle(relationship); + + if (count == 0) { + writer.indent(); + writer.writeLine(String.format("start: \"%s\"", relationship.getSourceId())); + writer.writeLine("steps:"); + writer.writeLine(String.format("- to: \"%s\"", relationship.getDestinationId())); + } else { + if (relationshipView.isResponse() != null && relationshipView.isResponse()) { + writer.writeLine(String.format("- to: \"%s\"", relationship.getSourceId())); + } else { + writer.writeLine(String.format("- to: \"%s\"", relationship.getDestinationId())); + } + } + + writer.indent(); + if (!StringUtils.isNullOrEmpty(relationshipView.getDescription())) { + writer.writeLine(String.format("label: \"%s. %s\"", relationshipView.getOrder(), relationshipView.getDescription())); + } else if (!StringUtils.isNullOrEmpty(relationship.getDescription())) { + writer.writeLine(String.format("label: \"%s. %s\"", relationshipView.getOrder(), relationship.getDescription())); + } + + if (!StringUtils.isNullOrEmpty(relationship.getTechnology())) { + writer.writeLine(String.format("description: \"%s\"", relationship.getTechnology())); + } + + if (!StringUtils.isNullOrEmpty(relationshipStyle.getColor())) { + writer.writeLine(String.format("color: \"%s\"", relationshipStyle.getColor())); + } + + writer.outdent(); + + writer.writeLine(); + + count++; + } + + writer.outdent(); + writer.outdent(); + writer.outdent(); + } + + private void writeDeploymentEnvironment(Workspace workspace, String deploymentEnvironment, IndentingWriter writer) { + writer.indent(); + writer.writeLine("- name: Deployment - " + deploymentEnvironment); + writer.indent(); + writer.writeLine("relations:"); + + List<DeploymentNode> topLevelDeploymentNodes = workspace.getModel().getDeploymentNodes().stream().filter(dn -> dn.getEnvironment().equals(deploymentEnvironment)).sorted(Comparator.comparing(DeploymentNode::getId)).collect(Collectors.toList()); + List<Element> deploymentElementsInEnvironment = new ArrayList<>(topLevelDeploymentNodes); + for (DeploymentNode deploymentNode : topLevelDeploymentNodes) { + deploymentElementsInEnvironment.addAll(findAllChildren(deploymentNode)); + } + + Collection<Relationship> relationships = findRelationships(deploymentElementsInEnvironment); + writer.indent(); + + for (Relationship relationship : relationships) { + RelationshipStyle relationshipStyle = workspace.getViews().getConfiguration().getStyles().findRelationshipStyle(relationship); + + writer.writeLine(String.format("- from: \"%s\"", relationship.getSourceId())); + writer.indent(); + writer.writeLine(String.format("to: \"%s\"", relationship.getDestinationId())); + + if (!StringUtils.isNullOrEmpty(relationship.getDescription())) { + writer.writeLine(String.format("label: \"%s\"", relationship.getDescription())); + } + + if (!StringUtils.isNullOrEmpty(relationship.getTechnology())) { + writer.writeLine(String.format("description: \"%s\"", relationship.getTechnology())); + } + + if (!StringUtils.isNullOrEmpty(relationshipStyle.getColor())) { + writer.writeLine(String.format("color: \"%s\"", relationshipStyle.getColor())); + } + + writer.outdent(); + } + + writer.outdent(); + writer.outdent(); + writer.outdent(); + } + + private Collection<Element> findAllChildren(DeploymentNode deploymentNode) { + List<Element> deploymentElements = new ArrayList<>(); + + List<DeploymentNode> deploymentNodes = new ArrayList<>(deploymentNode.getChildren()); + deploymentNodes.sort(Comparator.comparing(DeploymentNode::getId)); + for (DeploymentNode child : deploymentNodes) { + deploymentElements.addAll(findAllChildren(child)); + } + + deploymentElements.addAll(deploymentNode.getSoftwareSystemInstances().stream().sorted(Comparator.comparing(SoftwareSystemInstance::getId)).collect(Collectors.toList())); + deploymentElements.addAll(deploymentNode.getContainerInstances().stream().sorted(Comparator.comparing(ContainerInstance::getId)).collect(Collectors.toList())); + deploymentElements.addAll(deploymentNode.getInfrastructureNodes().stream().sorted(Comparator.comparing(InfrastructureNode::getId)).collect(Collectors.toList())); + + return deploymentElements; + } + + private Collection<Relationship> findRelationships(Collection<Element> elements) { + List<Relationship> relationships = new ArrayList<>(); + + for (Element element : elements) { + List<Relationship> sortedRelationships = new ArrayList<>(element.getRelationships()); + sortedRelationships.sort(Comparator.comparing(Relationship::getId)); + for (Relationship relationship : sortedRelationships) { + if (elements.contains(relationship.getSource()) && elements.contains(relationship.getDestination())) { + relationships.add(relationship); + } + } + } + + return relationships; + } + + private boolean include(Relationship relationship, Set<Class> elementTypes) { + Element source = relationship.getSource(); + Element destination = relationship.getDestination(); + + return elementTypes.contains(source.getClass()) && elementTypes.contains(destination.getClass()); + } + +} \ No newline at end of file diff --git a/structurizr-export/src/main/java/com/structurizr/export/ilograph/IlographWorkspaceExport.java b/structurizr-export/src/main/java/com/structurizr/export/ilograph/IlographWorkspaceExport.java new file mode 100644 index 000000000..a1ae4942c --- /dev/null +++ b/structurizr-export/src/main/java/com/structurizr/export/ilograph/IlographWorkspaceExport.java @@ -0,0 +1,16 @@ +package com.structurizr.export.ilograph; + +import com.structurizr.export.WorkspaceExport; + +public class IlographWorkspaceExport extends WorkspaceExport { + + public IlographWorkspaceExport(String definition) { + super(definition); + } + + @Override + public String getFileExtension() { + return "idl"; + } + +} diff --git a/structurizr-export/src/main/java/com/structurizr/export/ilograph/README.md b/structurizr-export/src/main/java/com/structurizr/export/ilograph/README.md new file mode 100644 index 000000000..22ddfa09b --- /dev/null +++ b/structurizr-export/src/main/java/com/structurizr/export/ilograph/README.md @@ -0,0 +1,7 @@ +# Ilograph + +The [IlographExporter](IlographExporter.java) class provides a way to export the software architecture model +to the YAML format used by [Ilograph](https://www.ilograph.com), which provides an interactive way to explore +a hierarchical dataset (which the C4 model is). + +See https://docs.structurizr.com/export for more. \ No newline at end of file diff --git a/structurizr-export/src/main/java/com/structurizr/export/mermaid/MermaidDiagram.java b/structurizr-export/src/main/java/com/structurizr/export/mermaid/MermaidDiagram.java new file mode 100644 index 000000000..9863eef19 --- /dev/null +++ b/structurizr-export/src/main/java/com/structurizr/export/mermaid/MermaidDiagram.java @@ -0,0 +1,17 @@ +package com.structurizr.export.mermaid; + +import com.structurizr.export.Diagram; +import com.structurizr.view.ModelView; + +public class MermaidDiagram extends Diagram { + + public MermaidDiagram(ModelView view, String definition) { + super(view, definition); + } + + @Override + public String getFileExtension() { + return "mmd"; + } + +} diff --git a/structurizr-export/src/main/java/com/structurizr/export/mermaid/MermaidDiagramExporter.java b/structurizr-export/src/main/java/com/structurizr/export/mermaid/MermaidDiagramExporter.java new file mode 100644 index 000000000..54b6b2652 --- /dev/null +++ b/structurizr-export/src/main/java/com/structurizr/export/mermaid/MermaidDiagramExporter.java @@ -0,0 +1,412 @@ +package com.structurizr.export.mermaid; + +import com.structurizr.export.AbstractDiagramExporter; +import com.structurizr.export.Diagram; +import com.structurizr.export.IndentingWriter; +import com.structurizr.model.*; +import com.structurizr.util.StringUtils; +import com.structurizr.view.*; + +import java.util.LinkedHashSet; +import java.util.Set; + +import static java.lang.String.format; + +/** + * Exports diagram definitions that can be used to create diagrams + * using mermaid (https://mermaidjs.github.io). + * + * System landscape, system context, container, component, dynamic and deployment diagrams are supported. + * Deployment node -> deployment node relationships are not rendered. + */ +public class MermaidDiagramExporter extends AbstractDiagramExporter { + + public static final String MERMAID_TITLE_PROPERTY = "mermaid.title"; + public static final String MERMAID_SEQUENCE_DIAGRAM_PROPERTY = "mermaid.sequenceDiagram"; + public static final String MERMAID_ICONS_PROPERTY = "mermaid.icons"; + + private int groupId = 0; + + public MermaidDiagramExporter() { + } + + @Override + protected void writeHeader(ModelView view, IndentingWriter writer) { + groupId = 0; + String direction = "TB"; + + if (view.getAutomaticLayout() != null) { + switch (view.getAutomaticLayout().getRankDirection()) { + case TopBottom: + direction = "TB"; + break; + case BottomTop: + direction = "BT"; + break; + case LeftRight: + direction = "LR"; + break; + case RightLeft: + direction = "RL"; + break; + } + } + + writer.writeLine("graph " + direction); + writer.indent(); + writer.writeLine("linkStyle default fill:#ffffff"); + writer.writeLine(); + + String viewTitle = " "; + if (includeTitle(view)) { + viewTitle = view.getTitle(); + if (StringUtils.isNullOrEmpty(viewTitle)) { + viewTitle = view.getName(); + } + } + + writer.writeLine("subgraph diagram [\"" + viewTitle + "\"]"); + writer.indent(); + writer.writeLine("style diagram fill:#ffffff,stroke:#ffffff"); + writer.writeLine(); + } + + @Override + protected void writeFooter(ModelView view, IndentingWriter writer) { + writer.outdent(); + writer.writeLine("end"); + writer.outdent(); + } + + @Override + protected void startEnterpriseBoundary(ModelView view, String enterpriseName, IndentingWriter writer) { + writer.writeLine("subgraph enterprise [" + enterpriseName + "]"); + writer.indent(); + writer.writeLine("style enterprise fill:#ffffff,stroke:#444444,color:#444444"); + writer.writeLine(); + } + + @Override + protected void endEnterpriseBoundary(ModelView view, IndentingWriter writer) { + writer.outdent(); + writer.writeLine("end"); + writer.writeLine(); + } + + @Override + protected void startGroupBoundary(ModelView view, String group, IndentingWriter writer) { + groupId++; + + String groupName = group; + + String groupSeparator = view.getModel().getProperties().get(GROUP_SEPARATOR_PROPERTY_NAME); + if (!StringUtils.isNullOrEmpty(groupSeparator)) { + groupName = group.substring(group.lastIndexOf(groupSeparator) + groupSeparator.length()); + } + + String color = "#cccccc"; + + // is there a style for the group? + ElementStyle elementStyle = view.getViewSet().getConfiguration().getStyles().findElementStyle("Group:" + group); + + if (elementStyle == null || StringUtils.isNullOrEmpty(elementStyle.getColor())) { + // no, so is there a default group style? + elementStyle = view.getViewSet().getConfiguration().getStyles().findElementStyle("Group"); + } + + if (elementStyle != null && !StringUtils.isNullOrEmpty(elementStyle.getColor())) { + color = elementStyle.getColor(); + } + + writer.writeLine(String.format("subgraph group%s [" + groupName + "]", groupId)); + writer.indent(); + writer.writeLine(String.format("style group%s fill:#ffffff,stroke:%s,color:%s,stroke-dasharray:5", groupId, color, color)); + writer.writeLine(); + } + + @Override + protected void endGroupBoundary(ModelView view, IndentingWriter writer) { + writer.outdent(); + writer.writeLine("end"); + writer.writeLine(); + } + + @Override + protected void startSoftwareSystemBoundary(ModelView view, SoftwareSystem softwareSystem, IndentingWriter writer) { + ElementStyle elementStyle = view.getViewSet().getConfiguration().getStyles().findElementStyle(softwareSystem); + String color = elementStyle.getStroke(); + + writer.writeLine(String.format("subgraph %s [%s]", softwareSystem.getId(), softwareSystem.getName())); + writer.indent(); + writer.writeLine(String.format("style %s fill:#ffffff,stroke:%s,color:%s", softwareSystem.getId(), color, color)); + writer.writeLine(); + } + + @Override + protected void endSoftwareSystemBoundary(ModelView view, IndentingWriter writer) { + writer.outdent(); + writer.writeLine("end"); + writer.writeLine(); + } + + @Override + protected void startContainerBoundary(ModelView view, Container container, IndentingWriter writer) { + ElementStyle elementStyle = view.getViewSet().getConfiguration().getStyles().findElementStyle(container); + String color = elementStyle.getStroke(); + + writer.writeLine(String.format("subgraph %s [%s]", container.getId(), container.getName())); + writer.indent(); + writer.writeLine(String.format("style %s fill:#ffffff,stroke:%s,color:%s", container.getId(), color, color)); + writer.writeLine(); + } + + @Override + protected void endContainerBoundary(ModelView view, IndentingWriter writer) { + writer.outdent(); + writer.writeLine("end"); + writer.writeLine(); + } + + @Override + protected void startDeploymentNodeBoundary(DeploymentView view, DeploymentNode deploymentNode, IndentingWriter writer) { + ElementStyle elementStyle = view.getViewSet().getConfiguration().getStyles().findElementStyle(deploymentNode); + + writer.writeLine(String.format("subgraph %s [%s]", deploymentNode.getId(), deploymentNode.getName())); + writer.indent(); + writer.writeLine(String.format("style %s fill:#ffffff,stroke:%s,color:%s", deploymentNode.getId(), elementStyle.getStroke(), elementStyle.getColor())); + writer.writeLine(); + } + + @Override + protected void endDeploymentNodeBoundary(ModelView view, IndentingWriter writer) { + writer.outdent(); + writer.writeLine("end"); + writer.writeLine(); + } + + @Override + public Diagram export(DynamicView view) { + if (renderAsSequenceDiagram(view)) { + IndentingWriter writer = new IndentingWriter(); + writer.writeLine("sequenceDiagram"); + writer.writeLine(); + writer.indent(); + + Set<Element> elements = new LinkedHashSet<>(); + for (RelationshipView relationshipView : view.getRelationships()) { + elements.add(relationshipView.getRelationship().getSource()); + elements.add(relationshipView.getRelationship().getDestination()); + } + + for (Element element : elements) { + ElementStyle elementStyle = view.getViewSet().getConfiguration().getStyles().findElementStyle(element); + String shape = "participant"; + if (elementStyle.getShape() == Shape.Person) { + shape = "actor"; + } + + String type = typeOf(view, element, true); + + if (StringUtils.isNullOrEmpty(type) || false == elementStyle.getMetadata()) { + type = ""; + } else { + type = "<br />" + type; + } + + writer.writeLine(String.format("%s %s as %s%s", shape, element.getId(), element.getName(), type)); + } + + writer.writeLine(); + + for (RelationshipView relationshipView : view.getRelationships()) { + Relationship relationship = relationshipView.getRelationship(); + RelationshipStyle style = view.getViewSet().getConfiguration().getStyles().findRelationshipStyle(relationship); + + String description = relationshipView.getDescription(); + if (StringUtils.isNullOrEmpty(description)) { + description = relationship.getDescription(); + } + + String sourceId = relationship.getSourceId(); + String destinationId = relationship.getDestinationId(); + + if (relationshipView.isResponse()) { + sourceId = relationship.getDestinationId(); + destinationId = relationship.getSourceId(); + } + + String technology = !StringUtils.isNullOrEmpty(relationship.getTechnology()) ? "<br />[" + relationship.getTechnology() + "]" : ""; + + String arrow; + + if (!relationshipView.isResponse()) { + arrow = "->>"; + } else { + arrow = "-->>"; + } + + writer.writeLine(String.format("%s%s%s: %s%s", + sourceId, + arrow, + destinationId, + description, + technology)); + } + + return createDiagram(view, writer.toString()); + } else { + return super.export(view); + } + } + + @Override + protected void writeElement(ModelView view, Element element, IndentingWriter writer) { + ElementStyle elementStyle = view.getViewSet().getConfiguration().getStyles().findElementStyle(element); + + String name = element.getName(); + String description = element.getDescription(); + String type = typeOf(view, element, true); + String icon = ""; + + if (element instanceof StaticStructureElementInstance) { + StaticStructureElementInstance elementInstance = (StaticStructureElementInstance)element; + name = elementInstance.getElement().getName(); + description = elementInstance.getElement().getDescription(); + type = typeOf(view, elementInstance.getElement(), true); + } + + String nodeOpeningSymbol = "["; + String nodeClosingSymbol = "]"; + + if (elementStyle.getShape() == Shape.RoundedBox) { + nodeOpeningSymbol = "("; + nodeClosingSymbol = ")"; + } else if (elementStyle.getShape() == Shape.Cylinder) { + nodeOpeningSymbol = "[("; + nodeClosingSymbol = ")]"; + } + + if (StringUtils.isNullOrEmpty(description) || false == elementStyle.getDescription()) { + description = ""; + } else { + description = String.format("<div style='font-size: 80%%; margin-top:10px'>%s</div>", lines(description)); + } + + if (false == elementStyle.getMetadata()) { + type = ""; + } else { + type = String.format("<div style='font-size: 70%%; margin-top: 0px'>%s</div>", type); + } + + if ("true".equals(getViewOrViewSetProperty(view, MERMAID_ICONS_PROPERTY, "false")) && elementStyleHasSupportedIcon(elementStyle)) { + icon = "<div><img src='" + elementStyle.getIcon() + "' style='max-height: 50px; margin: auto; margin-top:10px'/></div>"; + } + + writer.writeLine(format("%s%s\"<div style='font-weight: bold'>%s</div>%s%s%s\"%s", + element.getId(), + nodeOpeningSymbol, + name, + type, + description, + icon, + nodeClosingSymbol + )); + + if (!StringUtils.isNullOrEmpty(element.getUrl())) { + writer.writeLine(format("click %s %s \"%s\"", element.getId(), element.getUrl(), element.getUrl())); + } + + if (element instanceof StaticStructureElementInstance) { + Element e = ((StaticStructureElementInstance)element).getElement(); + writer.writeLine(format("style %s fill:%s,stroke:%s,color:%s", element.getId(), elementStyle.getBackground(), elementStyle.getStroke(), elementStyle.getColor())); + } else { + writer.writeLine(format("style %s fill:%s,stroke:%s,color:%s", element.getId(), elementStyle.getBackground(), elementStyle.getStroke(), elementStyle.getColor())); + } + } + + @Override + protected void writeRelationship(ModelView view, RelationshipView relationshipView, IndentingWriter writer) { + Relationship relationship = relationshipView.getRelationship(); + RelationshipStyle style = view.getViewSet().getConfiguration().getStyles().findRelationshipStyle(relationship); + + Element source = relationship.getSource(); + Element destination = relationship.getDestination(); + + if (source instanceof DeploymentNode || destination instanceof DeploymentNode) { + return; + } + + if (relationshipView.isResponse() != null && relationshipView.isResponse()) { + source = relationship.getDestination(); + destination = relationship.getSource(); + } + + boolean solid = style.getStyle() == LineStyle.Solid || false == style.getDashed(); + // solid: A-- text -->B + // dotted: A-. text .->B + + String description = relationshipView.getDescription(); + if (StringUtils.isNullOrEmpty(description)) { + description = relationshipView.getRelationship().getDescription(); + } + + if (!StringUtils.isNullOrEmpty(relationshipView.getOrder())) { + description = relationshipView.getOrder() + ". " + description; + } + + writer.writeLine( + format("%s-%s \"<div>%s</div><div style='font-size: 70%%'>%s</div>\" %s->%s", + source.getId(), + solid ? "-" : ".", + lines(description), + !StringUtils.isNullOrEmpty(relationship.getTechnology()) ? "[" + relationship.getTechnology() + "]" : "", + solid ? "-" : ".", + destination.getId() + ) + ); + } + + private String lines(final String text) { + StringBuilder buf = new StringBuilder(); + if (text != null) { + final String[] words = text.trim().split("\\s+"); + + final StringBuilder line = new StringBuilder(); + for (final String word : words) { + if (line.length() == 0) { + line.append(word); + } else if (line.length() + word.length() + 1 < 30) { + line.append(' ').append(word); + } else { + buf.append(line.toString()); + buf.append("<br />"); + line.setLength(0); + line.append(word); + } + } + if (line.length() > 0) { + buf.append(line.toString()); + } + } + + return buf.toString(); + } + + @Override + protected Diagram createDiagram(ModelView view, String definition) { + return new MermaidDiagram(view, definition); + } + + protected boolean includeTitle(ModelView view) { + return "true".equals(getViewOrViewSetProperty(view, MERMAID_TITLE_PROPERTY, "true")); + } + + protected boolean renderAsSequenceDiagram(ModelView view) { + return "true".equalsIgnoreCase(getViewOrViewSetProperty(view, MERMAID_SEQUENCE_DIAGRAM_PROPERTY, "false")); + } + + private boolean elementStyleHasSupportedIcon(ElementStyle elementStyle) { + return !StringUtils.isNullOrEmpty(elementStyle.getIcon()) && elementStyle.getIcon().startsWith("http"); + } + +} \ No newline at end of file diff --git a/structurizr-export/src/main/java/com/structurizr/export/mermaid/MermaidEncoder.java b/structurizr-export/src/main/java/com/structurizr/export/mermaid/MermaidEncoder.java new file mode 100644 index 000000000..3f1e9fe9d --- /dev/null +++ b/structurizr-export/src/main/java/com/structurizr/export/mermaid/MermaidEncoder.java @@ -0,0 +1,18 @@ +package com.structurizr.export.mermaid; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +/** + * Encodes a Mermaid diagram definition to base64 format, for use with image URLs, etc. + */ +public class MermaidEncoder { + + private static final String TEMPLATE = "{ \"code\":\"%s\", \"mermaid\":{\"theme\":\"default\", \"securityLevel\": \"loose\"}}"; + + public String encode(String mermaidDefinition) { + String s = String.format(TEMPLATE, mermaidDefinition.replaceAll("\n", "\\\\n").replaceAll("\"", "\\\\\"")); + return Base64.getEncoder().encodeToString(s.getBytes(StandardCharsets.UTF_8)); + } + +} \ No newline at end of file diff --git a/structurizr-export/src/main/java/com/structurizr/export/mermaid/README.md b/structurizr-export/src/main/java/com/structurizr/export/mermaid/README.md new file mode 100644 index 000000000..73b43c1c4 --- /dev/null +++ b/structurizr-export/src/main/java/com/structurizr/export/mermaid/README.md @@ -0,0 +1,6 @@ +# Mermaid + +The [MermaidDiagramExporter](MermaidDiagramExporter.java) provides a way to export views that are compatible with the +[Mermaid](https://mermaid-js.github.io/) diagramming tool. + +See https://docs.structurizr.com/export for more. \ No newline at end of file diff --git a/structurizr-export/src/main/java/com/structurizr/export/plantuml/AbstractPlantUMLExporter.java b/structurizr-export/src/main/java/com/structurizr/export/plantuml/AbstractPlantUMLExporter.java new file mode 100644 index 000000000..7424cbd2e --- /dev/null +++ b/structurizr-export/src/main/java/com/structurizr/export/plantuml/AbstractPlantUMLExporter.java @@ -0,0 +1,251 @@ +package com.structurizr.export.plantuml; + +import com.structurizr.export.AbstractDiagramExporter; +import com.structurizr.export.Diagram; +import com.structurizr.export.IndentingWriter; +import com.structurizr.model.*; +import com.structurizr.util.StringUtils; +import com.structurizr.view.ElementStyle; +import com.structurizr.view.ModelView; +import com.structurizr.view.Shape; + +import javax.imageio.IIOException; +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.net.URL; +import java.util.LinkedHashMap; +import java.util.Map; + +import static java.lang.String.format; + +public abstract class AbstractPlantUMLExporter extends AbstractDiagramExporter { + + public static final String PLANTUML_TITLE_PROPERTY = "plantuml.title"; + public static final String PLANTUML_INCLUDES_PROPERTY = "plantuml.includes"; + public static final String PLANTUML_ANIMATION_PROPERTY = "plantuml.animation"; + public static final String PLANTUML_SEQUENCE_DIAGRAM_PROPERTY = "plantuml.sequenceDiagram"; + + private static final double MAX_ICON_SIZE = 30.0; + + private final Map<String, String> skinParams = new LinkedHashMap<>(); + + protected Map<String, String> getSkinParams() { + return skinParams; + } + + public void addSkinParam(String name, String value) { + skinParams.put(name, value); + } + + public void clearSkinParams() { + skinParams.clear(); + } + + String plantUMLShapeOf(ModelView view, Element element) { + Shape shape = findElementStyle(view, element).getShape(); + + return plantUMLShapeOf(shape); + } + + String plantUMLShapeOf(Shape shape) { + switch(shape) { + case Person: + case Robot: + return "person"; + case Component: + return "component"; + case Cylinder: + return "database"; + case Folder: + return "folder"; + case Ellipse: + case Circle: + return "storage"; + case Hexagon: + return "hexagon"; + case Pipe: + return "queue"; + default: + return "rectangle"; + } + } + + String plantumlSequenceType(ModelView view, Element element) { + Shape shape = findElementStyle(view, element).getShape(); + + switch(shape) { + case Box: + return "participant"; + case Person: + return "actor"; + case Cylinder: + return "database"; + case Folder: + return "collections"; + case Ellipse: + case Circle: + return "entity"; + default: + return "participant"; + } + } + + String idOf(ModelItem modelItem) { + if (modelItem instanceof Element) { + Element element = (Element)modelItem; + if (element.getParent() == null) { + if (element instanceof DeploymentNode) { + DeploymentNode dn = (DeploymentNode)element; + return filter(dn.getEnvironment()) + "." + id(dn); + } else { + return id(element); + } + } else { + return idOf(element.getParent()) + "." + id(modelItem); + } + } + + return id(modelItem); + } + + private String id(ModelItem modelItem) { + if (modelItem instanceof Person) { + return id((Person)modelItem); + } else if (modelItem instanceof SoftwareSystem) { + return id((SoftwareSystem)modelItem); + } else if (modelItem instanceof Container) { + return id((Container)modelItem); + } else if (modelItem instanceof Component) { + return id((Component)modelItem); + } else if (modelItem instanceof DeploymentNode) { + return id((DeploymentNode)modelItem); + } else if (modelItem instanceof InfrastructureNode) { + return id((InfrastructureNode)modelItem); + } else if (modelItem instanceof SoftwareSystemInstance) { + return id((SoftwareSystemInstance)modelItem); + } else if (modelItem instanceof ContainerInstance) { + return id((ContainerInstance)modelItem); + } + + return modelItem.getId(); + } + + private String id(Person person) { + return filter(person.getName()); + } + + private String id(SoftwareSystem softwareSystem) { + return filter(softwareSystem.getName()); + } + + private String id(Container container) { + return filter(container.getName()); + } + + private String id(Component component) { + return filter(component.getName()); + } + + private String id(DeploymentNode deploymentNode) { + return filter(deploymentNode.getName()); + } + + private String id(InfrastructureNode infrastructureNode) { + return filter(infrastructureNode.getName()); + } + + private String id(SoftwareSystemInstance softwareSystemInstance) { + return filter(softwareSystemInstance.getName()) + "_" + softwareSystemInstance.getInstanceId(); + } + + private String id(ContainerInstance containerInstance) { + return filter(containerInstance.getName()) + "_" + containerInstance.getInstanceId(); + } + + private String filter(String s) { + return s.replaceAll("(?U)\\W", ""); + } + + protected boolean includeTitle(ModelView view) { + return "true".equals(getViewOrViewSetProperty(view, PLANTUML_TITLE_PROPERTY, "true")); + } + + @Override + protected boolean isAnimationSupported(ModelView view) { + return "true".equalsIgnoreCase(getViewOrViewSetProperty(view, PLANTUML_ANIMATION_PROPERTY, "false")); + } + + @Override + protected void writeHeader(ModelView view, IndentingWriter writer) { + writer.writeLine("@startuml"); + writer.writeLine("set separator none"); + + if (includeTitle(view)) { + String viewTitle = view.getTitle(); + if (StringUtils.isNullOrEmpty(viewTitle)) { + viewTitle = view.getName(); + } + writer.writeLine("title " + viewTitle); + } + + writer.writeLine(); + } + + protected void writeSkinParams(IndentingWriter writer) { + if (!skinParams.isEmpty()) { + writer.writeLine("skinparam {"); + writer.indent(); + for (final String name : skinParams.keySet()) { + writer.writeLine(format("%s %s", name, skinParams.get(name))); + } + writer.outdent(); + writer.writeLine("}"); + } + } + + protected void writeIncludes(ModelView view, IndentingWriter writer) { + String[] includes = getViewOrViewSetProperty(view, PLANTUML_INCLUDES_PROPERTY, "").split(","); + for (String include : includes) { + if (!StringUtils.isNullOrEmpty(include)) { + include = include.trim(); + writer.writeLine("!include " + include); + } + } + } + + @Override + protected void writeFooter(ModelView view, IndentingWriter writer) { + writer.writeLine("@enduml"); + } + + @Override + protected Diagram createDiagram(ModelView view, String definition) { + return new PlantUMLDiagram(view, definition); + } + + protected boolean elementStyleHasSupportedIcon(ElementStyle elementStyle) { + return !StringUtils.isNullOrEmpty(elementStyle.getIcon()) && elementStyle.getIcon().startsWith("http"); + } + + protected double calculateIconScale(String iconUrl) { + double scale = 0.5; + + try { + URL url = new URL(iconUrl); + BufferedImage bi = ImageIO.read(url); + + int width = bi.getWidth(); + int height = bi.getHeight(); + + scale = MAX_ICON_SIZE / Math.max(width, height); + } catch (UnsupportedOperationException | UnsatisfiedLinkError | IIOException e) { + // This is a known issue on native builds since AWT packages aren't available. + // So we just swallow the error and use the default scale + } catch (Exception e) { + e.printStackTrace(); + } + + return scale; + } + +} diff --git a/structurizr-export/src/main/java/com/structurizr/export/plantuml/C4PlantUMLExporter.java b/structurizr-export/src/main/java/com/structurizr/export/plantuml/C4PlantUMLExporter.java new file mode 100644 index 000000000..bf67727da --- /dev/null +++ b/structurizr-export/src/main/java/com/structurizr/export/plantuml/C4PlantUMLExporter.java @@ -0,0 +1,682 @@ +package com.structurizr.export.plantuml; + +import com.structurizr.export.Diagram; +import com.structurizr.export.IndentingWriter; +import com.structurizr.model.*; +import com.structurizr.util.StringUtils; +import com.structurizr.view.*; + +import java.util.*; +import java.util.stream.Collectors; + +import static java.lang.String.format; + +public class C4PlantUMLExporter extends AbstractPlantUMLExporter { + + private static final String STRUCTURIZR_PROPERTY_NAME = "structurizr."; + + public static final String C4PLANTUML_LEGEND_PROPERTY = "c4plantuml.legend"; + public static final String C4PLANTUML_STEREOTYPES_PROPERTY = "c4plantuml.stereotypes"; + public static final String C4PLANTUML_TAGS_PROPERTY = "c4plantuml.tags"; + public static final String C4PLANTUML_STANDARD_LIBRARY_PROPERTY = "c4plantuml.stdlib"; + public static final String C4PLANTUML_SPRITE = "c4plantuml.sprite"; + public static final String C4PLANTUML_SHADOW = "c4plantuml.shadow"; + + /** + * <p>Set this property to <code>true</code> by calling {@link Configuration#addProperty(String, String)} in your + * {@link ViewSet} in order to have all {@link ModelItem#getProperties()} for {@link Component}s + * being printed in the PlantUML diagrams.</p> + * + * <p>The default value is <code>false</code>.</p> + * + * @see ViewSet#getConfiguration() + * @see Configuration#getProperties() + */ + public static final String C4PLANTUML_ELEMENT_PROPERTIES_PROPERTY = "c4plantuml.elementProperties"; + + /** + * <p>Set this property to <code>true</code> by calling {@link Configuration#addProperty(String, String)} in your + * {@link ViewSet} in order to have all {@link ModelItem#getProperties()} for {@link Relationship}s being + * printed in the PlantUML diagrams.</p> + * + * <p>The default value is <code>false</code>.</p> + * + * @see ViewSet#getConfiguration() + * @see Configuration#getProperties() + */ + public static final String C4PLANTUML_RELATIONSHIP_PROPERTIES_PROPERTY = "c4plantuml.relationshipProperties"; + + private int groupId = 0; + + public C4PlantUMLExporter() { + } + + @Override + protected void writeHeader(ModelView view, IndentingWriter writer) { + super.writeHeader(view, writer); + groupId = 0; + + Font font = view.getViewSet().getConfiguration().getBranding().getFont(); + if (font != null) { + String fontName = font.getName(); + if (!StringUtils.isNullOrEmpty(fontName)) { + addSkinParam("defaultFontName", "\"" + fontName + "\""); + } + } + + writeSkinParams(writer); + + if (renderAsSequenceDiagram(view)) { + if (usePlantUMLStandardLibrary(view)) { + writer.writeLine("!include <C4/C4_Sequence>"); + } else { + writer.writeLine("!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Sequence.puml"); + } + } else { + if (view.getAutomaticLayout() != null) { + switch (view.getAutomaticLayout().getRankDirection()) { + case LeftRight: + writer.writeLine("left to right direction"); + break; + default: + writer.writeLine("top to bottom direction"); + break; + } + } else { + writer.writeLine("top to bottom direction"); + } + + writer.writeLine(); + + if (usePlantUMLStandardLibrary(view)) { + writer.writeLine("!include <C4/C4>"); + writer.writeLine("!include <C4/C4_Context>"); + } else { + writer.writeLine("!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4.puml"); + writer.writeLine("!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Context.puml"); + } + + if (view.getElements().stream().map(ElementView::getElement).anyMatch(e -> e instanceof Container || e instanceof ContainerInstance)) { + if (usePlantUMLStandardLibrary(view)) { + writer.writeLine("!include <C4/C4_Container>"); + } else { + writer.writeLine("!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml"); + } + } + + if (view.getElements().stream().map(ElementView::getElement).anyMatch(e -> e instanceof Component)) { + if (usePlantUMLStandardLibrary(view)) { + writer.writeLine("!include <C4/C4_Component>"); + } else { + writer.writeLine("!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Component.puml"); + } + } + + if (view instanceof DeploymentView) { + if (usePlantUMLStandardLibrary(view)) { + writer.writeLine("!include <C4/C4_Deployment>"); + } else { + writer.writeLine("!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Deployment.puml"); + } + } + } + + writeIncludes(view, writer); + + if (includeTags(view)) { + Map<String,ElementStyle> elementStyles = new HashMap<>(); + Map<String,RelationshipStyle> relationshipStyles = new HashMap<>(); + Map<String,ElementStyle> boundaryStyles = new HashMap<>(); + + // elements + for (ElementView elementView : view.getElements()) { + Element element = elementView.getElement(); + ElementStyle elementStyle = view.getViewSet().getConfiguration().getStyles().findElementStyle(element); + + elementStyles.put(elementStyle.getTag(), elementStyle); + } + + // relationships + for (RelationshipView relationshipView : view.getRelationships()) { + Relationship relationship = relationshipView.getRelationship(); + RelationshipStyle relationshipStyle = view.getViewSet().getConfiguration().getStyles().findRelationshipStyle(relationship); + + relationshipStyles.put(relationshipStyle.getTag(), relationshipStyle); + } + + if (renderAsSequenceDiagram(view)) { + // no boundaries, do nothing + } else { + // boundaries + List<Element> boundaryElements = new ArrayList<>(); + if (view instanceof ContainerView) { + boundaryElements.addAll(getBoundarySoftwareSystems(view)); + } else if (view instanceof ComponentView) { + boundaryElements.addAll(getBoundaryContainers(view)); + } else if (view instanceof DynamicView) { + DynamicView dynamicView = (DynamicView) view; + if (dynamicView.getElement() instanceof SoftwareSystem) { + boundaryElements.addAll(getBoundarySoftwareSystems(view)); + } else if (dynamicView.getElement() instanceof Container) { + boundaryElements.addAll(getBoundaryContainers(view)); + } + } + + for (Element boundaryElement : boundaryElements) { + ElementStyle elementStyle = view.getViewSet().getConfiguration().getStyles().findElementStyle(boundaryElement); + boundaryStyles.put(elementStyle.getTag(), elementStyle); + } + } + + if (!elementStyles.isEmpty()) { + writer.writeLine(); + + for (String tagList : elementStyles.keySet()) { + ElementStyle elementStyle = elementStyles.get(tagList); + tagList = tagList.replaceFirst("Element,", ""); + + String sprite = ""; + if (elementStyleHasSupportedIcon(elementStyle)) { + double scale = calculateIconScale(elementStyle.getIcon()); + sprite = "img:" + elementStyle.getIcon() + "{scale=" + scale + "}"; + } + sprite = elementStyle.getProperties().getOrDefault(C4PLANTUML_SPRITE, sprite); + + int borderThickness = 1; + if (elementStyle.getStrokeWidth() != null) { + borderThickness = elementStyle.getStrokeWidth(); + } + + writer.writeLine(String.format("AddElementTag(\"%s\", $bgColor=\"%s\", $borderColor=\"%s\", $fontColor=\"%s\", $sprite=\"%s\", $shadowing=\"%s\", $borderStyle=\"%s\", $borderThickness=\"%s\")", + tagList, + elementStyle.getBackground(), + elementStyle.getStroke(), + elementStyle.getColor(), + sprite, + elementStyle.getProperties().getOrDefault(C4PLANTUML_SHADOW, ""), + elementStyle.getBorder(), + borderThickness + )); + } + } + + if (!relationshipStyles.isEmpty()) { + writer.writeLine(); + + for (String tagList : relationshipStyles.keySet()) { + RelationshipStyle relationshipStyle = relationshipStyles.get(tagList); + tagList = tagList.replaceFirst("Relationship,", ""); + + String lineStyle = "\"\""; + if (relationshipStyle.getStyle() == LineStyle.Dashed) { + lineStyle = "DashedLine()"; + } else if (relationshipStyle.getStyle() == LineStyle.Dotted) { + lineStyle = "DottedLine()"; + } + + writer.writeLine(String.format("AddRelTag(\"%s\", $textColor=\"%s\", $lineColor=\"%s\", $lineStyle = %s)", + tagList, + relationshipStyle.getColor(), + relationshipStyle.getColor(), + lineStyle + )); + } + } + + if (!boundaryStyles.isEmpty()) { + writer.writeLine(); + + for (String tagList : boundaryStyles.keySet()) { + ElementStyle elementStyle = boundaryStyles.get(tagList); + tagList = tagList.replaceFirst("Element,", ""); + + int borderThickness = 1; + if (elementStyle.getStrokeWidth() != null) { + borderThickness = elementStyle.getStrokeWidth(); + } + + writer.writeLine(String.format("AddBoundaryTag(\"%s\", $bgColor=\"%s\", $borderColor=\"%s\", $fontColor=\"%s\", $shadowing=\"%s\", $borderStyle=\"%s\", $borderThickness=\"%s\")", + tagList, + "#ffffff", + elementStyle.getStroke(), + elementStyle.getStroke(), + elementStyle.getProperties().getOrDefault(C4PLANTUML_SHADOW, ""), + elementStyle.getBorder(), + borderThickness + )); + } + } + } + + writer.writeLine(); + } + + @Override + protected void writeFooter(ModelView view, IndentingWriter writer) { + if (includeLegend(view)) { + writer.writeLine(); + writer.writeLine("SHOW_LEGEND(" + !(includeStereotypes(view)) + ")"); + } else { + writer.writeLine(); + writer.writeLine((includeStereotypes(view) ? "show" : "hide") + " stereotypes"); + } + + super.writeFooter(view, writer); + } + + @Override + protected void startEnterpriseBoundary(ModelView view, String enterpriseName, IndentingWriter writer) { + writer.writeLine(String.format("Enterprise_Boundary(enterprise, \"%s\") {", enterpriseName)); + writer.indent(); + } + + @Override + protected void endEnterpriseBoundary(ModelView view, IndentingWriter writer) { + writer.outdent(); + writer.writeLine("}"); + writer.writeLine(); + } + + @Override + protected void startGroupBoundary(ModelView view, String group, IndentingWriter writer) { + groupId++; + String groupName = group; + + String groupSeparator = view.getModel().getProperties().get(GROUP_SEPARATOR_PROPERTY_NAME); + if (!StringUtils.isNullOrEmpty(groupSeparator)) { + groupName = group.substring(group.lastIndexOf(groupSeparator) + groupSeparator.length()); + } + + String color = "#cccccc"; + String borderStyle = "Dashed"; + int borderThickness = 1; +// String icon = ""; + + ElementStyle elementStyleForGroup = view.getViewSet().getConfiguration().getStyles().findElementStyle("Group:" + group); + ElementStyle elementStyleForAllGroups = view.getViewSet().getConfiguration().getStyles().findElementStyle("Group"); + + if (elementStyleForGroup != null && !StringUtils.isNullOrEmpty(elementStyleForGroup.getColor())) { + color = elementStyleForGroup.getColor(); + } else if (elementStyleForAllGroups != null && !StringUtils.isNullOrEmpty(elementStyleForAllGroups.getColor())) { + color = elementStyleForAllGroups.getColor(); + } + + if (elementStyleForGroup != null && !StringUtils.isNullOrEmpty(elementStyleForGroup.getStroke())) { + borderStyle = elementStyleForGroup.getStroke(); + } else if (elementStyleForAllGroups != null && !StringUtils.isNullOrEmpty(elementStyleForAllGroups.getStroke())) { + borderStyle = elementStyleForAllGroups.getStroke(); + } + + if (elementStyleForGroup != null && elementStyleForGroup.getStrokeWidth() != null) { + borderThickness = elementStyleForGroup.getStrokeWidth(); + } else if (elementStyleForAllGroups != null && elementStyleForAllGroups.getStrokeWidth() != null) { + borderThickness = elementStyleForAllGroups.getStrokeWidth(); + } + + +// todo: $sprite doesn't seem to be supported for boundary styles +// if (elementStyleForGroup != null && elementStyleHasSupportedIcon(elementStyleForGroup)) { +// icon = elementStyleForGroup.getIcon(); +// } else if (elementStyleForAllGroups != null && elementStyleHasSupportedIcon(elementStyleForAllGroups)) { +// icon = elementStyleForAllGroups.getColor(); +// } +// +// if (!StringUtils.isNullOrEmpty(icon)) { +// double scale = calculateIconScale(icon); +// icon = "\\n\\n<img:" + icon + "{scale=" + scale + "}>"; +// } + + writer.writeLine(String.format("AddBoundaryTag(\"%s\", $borderColor=\"%s\", $fontColor=\"%s\", $borderStyle=\"%s\", $borderThickness=\"%s\")", + group, + color, + color, + borderStyle, + borderThickness) + ); + + writer.writeLine(String.format("Boundary(group_%s, \"%s\", $tags=\"%s\") {", groupId, groupName, group)); + writer.indent(); + } + + @Override + protected void endGroupBoundary(ModelView view, IndentingWriter writer) { + writer.outdent(); + writer.writeLine("}"); + writer.writeLine(); + } + + @Override + protected void startSoftwareSystemBoundary(ModelView view, SoftwareSystem softwareSystem, IndentingWriter writer) { + writer.writeLine(String.format("System_Boundary(\"%s_boundary\", \"%s\", $tags=\"%s\") {", idOf(softwareSystem), softwareSystem.getName(), tagsOf(view, softwareSystem))); + writer.indent(); + } + + @Override + protected void endSoftwareSystemBoundary(ModelView view, IndentingWriter writer) { + writer.outdent(); + writer.writeLine("}"); + writer.writeLine(); + } + + @Override + protected void startContainerBoundary(ModelView view, Container container, IndentingWriter writer) { + writer.writeLine(String.format("Container_Boundary(\"%s_boundary\", \"%s\", $tags=\"%s\") {", idOf(container), container.getName(), tagsOf(view,container))); + writer.indent(); + } + + @Override + protected void endContainerBoundary(ModelView view, IndentingWriter writer) { + writer.outdent(); + writer.writeLine("}"); + writer.writeLine(); + } + + @Override + protected void startDeploymentNodeBoundary(DeploymentView view, DeploymentNode deploymentNode, IndentingWriter writer) { + String url = deploymentNode.getUrl(); + if (StringUtils.isNullOrEmpty(url)) { + url = ""; + } + + if (Boolean.TRUE.toString().equalsIgnoreCase(getViewOrViewSetProperty(view, C4PLANTUML_ELEMENT_PROPERTIES_PROPERTY, Boolean.FALSE.toString()))) { + addProperties(view, writer, deploymentNode); + } + + String technology = deploymentNode.getTechnology(); + if (StringUtils.isNullOrEmpty(technology)) { + technology = ""; + } + String description = deploymentNode.getDescription(); + if (StringUtils.isNullOrEmpty(description)) { + description = ""; + } + + // Deployment_Node(alias, label, ?type, ?descr, ?sprite, ?tags, ?link) + writer.writeLine( + format("Deployment_Node(%s, \"%s\", $type=\"%s\", $descr=\"%s\", $tags=\"%s\", $link=\"%s\") {", + idOf(deploymentNode), + deploymentNode.getName() + (!"1".equals(deploymentNode.getInstances()) ? " (x" + deploymentNode.getInstances() + ")" : ""), + technology, + description, + tagsOf(view, deploymentNode), + url + ) + ); + writer.indent(); + + if (!isVisible(view, deploymentNode)) { + writer.writeLine("hide " + idOf(deploymentNode)); + } + } + + @Override + protected void endDeploymentNodeBoundary(ModelView view, IndentingWriter writer) { + writer.outdent(); + writer.writeLine("}"); + writer.writeLine(); + } + + @Override + public Diagram export(CustomView view) { + return null; + } + + @Override + public Diagram export(DynamicView view, String order) { + if (renderAsSequenceDiagram(view)) { + IndentingWriter writer = new IndentingWriter(); + writeHeader(view, writer); + + boolean elementsWritten = false; + + Set<Element> elements = new LinkedHashSet<>(); + for (RelationshipView relationshipView : view.getRelationships()) { + elements.add(relationshipView.getRelationship().getSource()); + elements.add(relationshipView.getRelationship().getDestination()); + } + + for (Element element : elements) { + writeElement(view, element, writer); + elementsWritten = true; + } + + if (elementsWritten) { + writer.writeLine(); + } + + writeRelationships(view, writer); + writeFooter(view, writer); + + return createDiagram(view, writer.toString()); + } else { + return super.export(view, order); + } + } + + @Override + protected void writeElement(ModelView view, Element element, IndentingWriter writer) { + if (element instanceof CustomElement) { + return; + } + + Element elementToWrite = element; + ElementStyle elementStyle = view.getViewSet().getConfiguration().getStyles().findElementStyle(element); + String id = idOf(element); + + String url = element.getUrl(); + if (StringUtils.isNullOrEmpty(url)) { + url = ""; + } + + if (Boolean.TRUE.toString().equalsIgnoreCase(getViewOrViewSetProperty(view, C4PLANTUML_ELEMENT_PROPERTIES_PROPERTY, Boolean.FALSE.toString()))) { + addProperties(view, writer, element); + } + + if (element instanceof StaticStructureElementInstance) { + StaticStructureElementInstance elementInstance = (StaticStructureElementInstance)element; + element = elementInstance.getElement(); + + if (StringUtils.isNullOrEmpty(url)) { + url = element.getUrl(); + if (StringUtils.isNullOrEmpty(url)) { + url = ""; + } + } + } + + String name = element.getName(); + String description = element.getDescription(); + + if (StringUtils.isNullOrEmpty(description)) { + description = ""; + } + + if (element instanceof Person) { + Person person = (Person)element; + String location = ""; + if (person.getLocation() == Location.External) { + location = "_Ext"; + } + + // Person(alias, label, ?descr, ?sprite, ?tags, ?link, ?type) + writer.writeLine( + String.format("Person%s(%s, \"%s\", $descr=\"%s\", $tags=\"%s\", $link=\"%s\")", + location, id, name, description, tagsOf(view, elementToWrite), url) + ); + } else if (element instanceof SoftwareSystem) { + SoftwareSystem softwareSystem = (SoftwareSystem)element; + String location = ""; + if (softwareSystem.getLocation() == Location.External) { + location = "_Ext"; + } + + // System(alias, label, ?descr, ?sprite, ?tags, ?link, ?type) + writer.writeLine( + String.format("System%s(%s, \"%s\", $descr=\"%s\", $tags=\"%s\", $link=\"%s\")", + location, id, name, description, tagsOf(view, elementToWrite), url) + ); + } else if (element instanceof Container) { + Container container = (Container)element; + String shape = ""; + if (elementStyle.getShape() == Shape.Cylinder) { + shape = "Db"; + } else if (elementStyle.getShape() == Shape.Pipe) { + shape = "Queue"; + } + + String technology = container.getTechnology(); + if (StringUtils.isNullOrEmpty(technology)) { + technology = ""; + } + + // Container(alias, label, ?techn, ?descr, ?sprite, ?tags, ?link) + writer.writeLine( + String.format("Container%s(%s, \"%s\", $techn=\"%s\", $descr=\"%s\", $tags=\"%s\", $link=\"%s\")", + shape, id, name, technology, description, tagsOf(view, elementToWrite), url) + ); + } else if (element instanceof Component) { + Component component = (Component)element; + String shape = ""; + + if (elementStyle.getShape() == Shape.Cylinder) { + shape = "Db"; + } else if (elementStyle.getShape() == Shape.Pipe) { + shape = "Queue"; + } + + String technology = component.getTechnology(); + if (StringUtils.isNullOrEmpty(technology)) { + technology = ""; + } + + // Component(alias, label, ?techn, ?descr, ?sprite, ?tags, ?link) + writer.writeLine( + String.format("Component%s(%s, \"%s\", $techn=\"%s\", $descr=\"%s\", $tags=\"%s\", $link=\"%s\")", + shape, id, name, technology, description, tagsOf(view, elementToWrite), url) + ); + } else if (element instanceof InfrastructureNode) { + InfrastructureNode infrastructureNode = (InfrastructureNode)element; + String technology = infrastructureNode.getTechnology(); + if (StringUtils.isNullOrEmpty(technology)) { + technology = ""; + } + + // Deployment_Node(alias, label, ?type, ?descr, ?sprite, ?tags, ?link) + writer.writeLine( + String.format("Deployment_Node(%s, \"%s\", $type=\"%s\", $descr=\"%s\", $tags=\"%s\", $link=\"%s\")", + idOf(infrastructureNode), name, technology, description, tagsOf(view, elementToWrite), url) + ); + } + + if (!isVisible(view, elementToWrite)) { + writer.writeLine("hide " + id); + } + } + + private String tagsOf(ModelView view, Element element) { + if (includeTags(view)) { + return view.getViewSet().getConfiguration().getStyles().findElementStyle(element).getTag().replaceFirst("Element,", ""); + } else { + return ""; + } + } + + private String tagsOf(ModelView view, Relationship relationship) { + if (includeTags(view)) { + return view.getViewSet().getConfiguration().getStyles().findRelationshipStyle(relationship).getTag().replaceFirst("Relationship,", ""); + } else { + return ""; + } + } + + @Override + protected void writeRelationship(ModelView view, RelationshipView relationshipView, IndentingWriter writer) { + Relationship relationship = relationshipView.getRelationship(); + Element source = relationship.getSource(); + Element destination = relationship.getDestination(); + + if (source instanceof CustomElement || destination instanceof CustomElement) { + return; + } + + if (Boolean.TRUE.toString().equalsIgnoreCase(getViewOrViewSetProperty(view, C4PLANTUML_RELATIONSHIP_PROPERTIES_PROPERTY, Boolean.FALSE.toString()))) { + addProperties(view, writer, relationship); + } + + if (relationshipView.isResponse() != null && relationshipView.isResponse()) { + source = relationship.getDestination(); + destination = relationship.getSource(); + } + + String description = ""; + + if (renderAsSequenceDiagram(view)) { + // do nothing - sequence diagrams don't need the order + } else { + if (!StringUtils.isNullOrEmpty(relationshipView.getOrder())) { + description = relationshipView.getOrder() + ". "; + } + } + + description += (hasValue(relationshipView.getDescription()) ? relationshipView.getDescription() : hasValue(relationshipView.getRelationship().getDescription()) ? relationshipView.getRelationship().getDescription() : ""); + + String technology = relationship.getTechnology(); + if (StringUtils.isNullOrEmpty(technology)) { + technology = ""; + } + + String url = relationship.getUrl(); + if (StringUtils.isNullOrEmpty(url)) { + url = ""; + } + + // Rel(from, to, label, ?techn, ?descr, ?sprite, ?tags, ?link) + writer.writeLine( + format("Rel(%s, %s, \"%s\", $techn=\"%s\", $tags=\"%s\", $link=\"%s\")", + idOf(source), idOf(destination), description, technology, tagsOf(view, relationship), url) + ); + } + + private void addProperties(ModelView view, IndentingWriter writer, ModelItem element) { + Map<String, String> properties = new HashMap<>(); + for (String key : element.getProperties().keySet()) { + // don't include any internal Structurizr properties (e.g. structurizr.dsl.identifier) + if (!key.startsWith(STRUCTURIZR_PROPERTY_NAME)) { + properties.put(key, element.getProperties().get(key)); + } + } + + if (!properties.isEmpty()) { + writer.writeLine("WithoutPropertyHeader()"); + properties.keySet().stream().sorted().forEach(key -> + writer.writeLine(String.format("AddProperty(\"%s\",\"%s\")", key, properties.get(key))) + ); + } + } + + @Override + protected boolean isAnimationSupported(ModelView view) { + return !(view instanceof DynamicView) && super.isAnimationSupported(view); + } + + protected boolean includeLegend(ModelView view) { + return "true".equalsIgnoreCase(getViewOrViewSetProperty(view, C4PLANTUML_LEGEND_PROPERTY, "true")); + } + + protected boolean includeStereotypes(ModelView view) { + return "true".equalsIgnoreCase(getViewOrViewSetProperty(view, C4PLANTUML_STEREOTYPES_PROPERTY, "false")); + } + + protected boolean includeTags(ModelView view) { + return "true".equalsIgnoreCase(getViewOrViewSetProperty(view, C4PLANTUML_TAGS_PROPERTY, "false")); + } + + protected boolean usePlantUMLStandardLibrary(ModelView view) { + return "true".equalsIgnoreCase(getViewOrViewSetProperty(view, C4PLANTUML_STANDARD_LIBRARY_PROPERTY, "true")); + } + + protected boolean renderAsSequenceDiagram(ModelView view) { + return view instanceof DynamicView && "true".equalsIgnoreCase(getViewOrViewSetProperty(view, PLANTUML_SEQUENCE_DIAGRAM_PROPERTY, "false")); + } + +} \ No newline at end of file diff --git a/structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLDiagram.java b/structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLDiagram.java new file mode 100644 index 000000000..b35e854e1 --- /dev/null +++ b/structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLDiagram.java @@ -0,0 +1,17 @@ +package com.structurizr.export.plantuml; + +import com.structurizr.export.Diagram; +import com.structurizr.view.ModelView; + +public class PlantUMLDiagram extends Diagram { + + public PlantUMLDiagram(ModelView view, String definition) { + super(view, definition); + } + + @Override + public String getFileExtension() { + return "puml"; + } + +} \ No newline at end of file diff --git a/structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLEncoder.java b/structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLEncoder.java new file mode 100644 index 000000000..1b5d24da7 --- /dev/null +++ b/structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLEncoder.java @@ -0,0 +1,73 @@ +package com.structurizr.export.plantuml; + +import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; +import java.util.zip.Deflater; +import java.util.zip.DeflaterOutputStream; + +/** + * A Java implementation of http://plantuml.com/code-javascript-synchronous + * that uses Java's built-in Deflate algorithm. + */ +public class PlantUMLEncoder { + + public String encode(String plantUMLDefinition) throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION, true); + + DeflaterOutputStream dos = new DeflaterOutputStream(baos, deflater, true); + dos.write(plantUMLDefinition.getBytes(StandardCharsets.UTF_8)); + dos.finish(); + + return encode(baos.toByteArray()); + } + + private String encode(byte[] bytes) { + StringBuilder buf = new StringBuilder(); + for (int i = 0; i < bytes.length; i += 3) { + int b1 = (bytes[i]) & 0xFF; + int b2 = (i + 1 < bytes.length ? bytes[i + 1] : (byte)0) & 0xFF; + int b3 = (i + 2 < bytes.length ? bytes[i + 2] : (byte)0) & 0xFF; + + append3bytes(buf, b1, b2, b3); + } + + return buf.toString(); + } + + private char encode6bit(byte b) { + if (b < 10) { + return (char) ('0' + b); + } + b -= 10; + if (b < 26) { + return (char) ('A' + b); + } + b -= 26; + if (b < 26) { + return (char) ('a' + b); + } + b -= 26; + if (b == 0) { + return '-'; + } + if (b == 1) { + return '_'; + } + + return '?'; + } + + private void append3bytes(StringBuilder buf, int b1, int b2, int b3) { + int c1 = b1 >> 2; + int c2 = (b1 & 0x3) << 4 | b2 >> 4; + int c3 = (b2 & 0xF) << 2 | b3 >> 6; + int c4 = b3 & 0x3F; + + buf.append(encode6bit((byte)(c1 & 0x3F))); + buf.append(encode6bit((byte)(c2 & 0x3F))); + buf.append(encode6bit((byte)(c3 & 0x3F))); + buf.append(encode6bit((byte)(c4 & 0x3F))); + } + +} \ No newline at end of file diff --git a/structurizr-export/src/main/java/com/structurizr/export/plantuml/README.md b/structurizr-export/src/main/java/com/structurizr/export/plantuml/README.md new file mode 100644 index 000000000..616e11ca9 --- /dev/null +++ b/structurizr-export/src/main/java/com/structurizr/export/plantuml/README.md @@ -0,0 +1,7 @@ +# PlantUML + +There are two PlantUML exporters in this package - [StructurizrPlantUMLExporter](StructurizrPlantUMLExporter.java) and [C4PlantUMLExporter](C4PlantUMLExporter.java). + +If neither of these provide the features you are looking for, an alternative PlantUML exporter can be found at [https://github.com/cloudflightio/structurizr-export-c4plantuml](https://github.com/cloudflightio/structurizr-export-c4plantuml). + +See https://docs.structurizr.com/export for more. \ No newline at end of file diff --git a/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java b/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java new file mode 100644 index 000000000..97d92a818 --- /dev/null +++ b/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java @@ -0,0 +1,626 @@ +package com.structurizr.export.plantuml; + +import com.structurizr.export.Diagram; +import com.structurizr.export.IndentingWriter; +import com.structurizr.export.Legend; +import com.structurizr.model.*; +import com.structurizr.util.StringUtils; +import com.structurizr.view.*; + +import java.util.*; +import java.util.stream.Collectors; + +import static java.lang.String.format; + +public class StructurizrPlantUMLExporter extends AbstractPlantUMLExporter { + + public static final String PLANTUML_SHADOW = "plantuml.shadow"; + + private int groupId = 0; + + public StructurizrPlantUMLExporter() { + addSkinParam("arrowFontSize", "10"); + addSkinParam("defaultTextAlignment", "center"); + addSkinParam("wrapWidth", "200"); + addSkinParam("maxMessageSize", "100"); + } + + @Override + protected void writeHeader(ModelView view, IndentingWriter writer) { + super.writeHeader(view, writer); + groupId = 0; + + if (view instanceof DynamicView && renderAsSequenceDiagram(view)) { + // do nothing + } else { + if (view.getAutomaticLayout() != null) { + switch (view.getAutomaticLayout().getRankDirection()) { + case LeftRight: + writer.writeLine("left to right direction"); + break; + default: + writer.writeLine("top to bottom direction"); + break; + } + } else { + writer.writeLine("top to bottom direction"); + } + + writer.writeLine(); + } + + Font font = view.getViewSet().getConfiguration().getBranding().getFont(); + if (font != null) { + String fontName = font.getName(); + if (!StringUtils.isNullOrEmpty(fontName)) { + addSkinParam("defaultFontName", "\"" + fontName + "\""); + } + } + + writeSkinParams(writer); + writeIncludes(view, writer); + + writer.writeLine(); + writer.writeLine("hide stereotype"); + writer.writeLine(); + + List<Element> elements = view.getElements().stream().map(ElementView::getElement).sorted(Comparator.comparing(Element::getName)).collect(Collectors.toList()); + for (Element element : elements) { + String id = idOf(element); + + String type = plantUMLShapeOf(view, element); + if ("actor".equals(type)) { + type = "rectangle"; // the actor shape is not supported in this implementation + } + + ElementStyle elementStyle = findElementStyle(view, element); + + String background = elementStyle.getBackground(); + String stroke = elementStyle.getStroke(); + String color = elementStyle.getColor(); + Shape shape = elementStyle.getShape(); + + if (view instanceof DynamicView && renderAsSequenceDiagram(view)) { + type = "sequenceParticipant"; + } + + writer.writeLine(format("skinparam %s<<%s>> {", type, id)); + writer.indent(); + if (element instanceof DeploymentNode) { + writer.writeLine("BackgroundColor #ffffff"); + } else { + writer.writeLine(String.format("BackgroundColor %s", background)); + } + writer.writeLine(String.format("FontColor %s", color)); + writer.writeLine(String.format("BorderColor %s", stroke)); + + if (shape == Shape.RoundedBox) { + writer.writeLine("roundCorner 20"); + } + + boolean shadow = "true".equalsIgnoreCase(elementStyle.getProperties().getOrDefault(PLANTUML_SHADOW, "false")); + writer.writeLine(String.format("shadowing %s", shadow)); + + writer.outdent(); + writer.writeLine("}"); + } + + if (!renderAsSequenceDiagram(view)) { + // boundaries + List<Element> boundaryElements = new ArrayList<>(); + if (view instanceof ContainerView) { + boundaryElements.addAll(getBoundarySoftwareSystems(view)); + } else if (view instanceof ComponentView) { + boundaryElements.addAll(getBoundaryContainers(view)); + } else if (view instanceof DynamicView) { + DynamicView dynamicView = (DynamicView) view; + if (dynamicView.getElement() instanceof SoftwareSystem) { + boundaryElements.addAll(getBoundarySoftwareSystems(view)); + } else if (dynamicView.getElement() instanceof Container) { + boundaryElements.addAll(getBoundaryContainers(view)); + } + } + + for (Element boundaryElement : boundaryElements) { + ElementStyle elementStyle = view.getViewSet().getConfiguration().getStyles().findElementStyle(boundaryElement); + String id = idOf(boundaryElement); + String color = elementStyle.getStroke(); + boolean shadow = "true".equalsIgnoreCase(elementStyle.getProperties().getOrDefault(PLANTUML_SHADOW, "false")); + + writer.writeLine(format("skinparam rectangle<<%s>> {", id)); + writer.indent(); + writer.writeLine(String.format("BorderColor %s", color)); + writer.writeLine(String.format("FontColor %s", color)); + writer.writeLine(String.format("shadowing %s", shadow)); + writer.outdent(); + writer.writeLine("}"); + } + } + + writer.writeLine(); + } + + @Override + protected void startEnterpriseBoundary(ModelView view, String enterpriseName, IndentingWriter writer) { + if (!renderAsSequenceDiagram(view)) { + writer.writeLine(String.format("rectangle \"%s\" <<enterprise>> {", enterpriseName)); + writer.indent(); + writer.writeLine("skinparam RectangleBorderColor<<enterprise>> #444444"); + writer.writeLine("skinparam RectangleFontColor<<enterprise>> #444444"); + writer.writeLine(); + } + } + + @Override + protected void endEnterpriseBoundary(ModelView view, IndentingWriter writer) { + if (!renderAsSequenceDiagram(view)) { + writer.outdent(); + writer.writeLine("}"); + writer.writeLine(); + } + } + + @Override + protected void startGroupBoundary(ModelView view, String group, IndentingWriter writer) { + groupId++; + String groupName = group; + + String groupSeparator = view.getModel().getProperties().get(GROUP_SEPARATOR_PROPERTY_NAME); + if (!StringUtils.isNullOrEmpty(groupSeparator)) { + groupName = group.substring(group.lastIndexOf(groupSeparator) + groupSeparator.length()); + } + + if (!renderAsSequenceDiagram(view)) { + String color = "#cccccc"; + String icon = ""; + + ElementStyle elementStyleForGroup = view.getViewSet().getConfiguration().getStyles().findElementStyle("Group:" + group); + ElementStyle elementStyleForAllGroups = view.getViewSet().getConfiguration().getStyles().findElementStyle("Group"); + + if (elementStyleForGroup != null && !StringUtils.isNullOrEmpty(elementStyleForGroup.getColor())) { + color = elementStyleForGroup.getColor(); + } else if (elementStyleForAllGroups != null && !StringUtils.isNullOrEmpty(elementStyleForAllGroups.getColor())) { + color = elementStyleForAllGroups.getColor(); + } + + if (elementStyleForGroup != null && elementStyleHasSupportedIcon(elementStyleForGroup)) { + icon = elementStyleForGroup.getIcon(); + } else if (elementStyleForAllGroups != null && elementStyleHasSupportedIcon(elementStyleForAllGroups)) { + icon = elementStyleForAllGroups.getColor(); + } + + if (!StringUtils.isNullOrEmpty(icon)) { + double scale = calculateIconScale(icon); + icon = "\\n\\n<img:" + icon + "{scale=" + scale + "}>"; + } + + writer.writeLine(String.format("rectangle \"%s%s\" <<group%s>> {", groupName, icon, groupId)); + writer.indent(); + writer.writeLine(String.format("skinparam RectangleBorderColor<<group%s>> %s", groupId, color)); + writer.writeLine(String.format("skinparam RectangleFontColor<<group%s>> %s", groupId, color)); + writer.writeLine(String.format("skinparam RectangleBorderStyle<<group%s>> dashed", groupId)); + + writer.writeLine(); + } + } + + @Override + protected void endGroupBoundary(ModelView view, IndentingWriter writer) { + if (!renderAsSequenceDiagram(view)) { + writer.outdent(); + writer.writeLine("}"); + writer.writeLine(); + } + } + + @Override + protected void startSoftwareSystemBoundary(ModelView view, SoftwareSystem softwareSystem, IndentingWriter writer) { + if (!renderAsSequenceDiagram(view)) { + writer.writeLine(String.format("rectangle \"%s\\n<size:10>%s</size>\" <<%s>> {", softwareSystem.getName(), typeOf(view, softwareSystem, true), idOf(softwareSystem))); + writer.indent(); + } + } + + @Override + protected void endSoftwareSystemBoundary(ModelView view, IndentingWriter writer) { + if (!renderAsSequenceDiagram(view)) { + writer.outdent(); + writer.writeLine("}"); + writer.writeLine(); + } + } + + @Override + protected void startContainerBoundary(ModelView view, Container container, IndentingWriter writer) { + if (!renderAsSequenceDiagram(view)) { + writer.writeLine(String.format("rectangle \"%s\\n<size:10>%s</size>\" <<%s>> {", container.getName(), typeOf(view, container, true), idOf(container))); + writer.indent(); + } + } + + @Override + protected void endContainerBoundary(ModelView view, IndentingWriter writer) { + if (!renderAsSequenceDiagram(view)) { + writer.outdent(); + writer.writeLine("}"); + writer.writeLine(); + } + } + + @Override + protected void startDeploymentNodeBoundary(DeploymentView view, DeploymentNode deploymentNode, IndentingWriter writer) { + ElementStyle elementStyle = findElementStyle(view, deploymentNode); + + String icon = ""; + if (elementStyleHasSupportedIcon(elementStyle)) { + double scale = calculateIconScale(elementStyle.getIcon()); + icon = "\\n\\n<img:" + elementStyle.getIcon() + "{scale=" + scale + "}>"; + } + + String url = deploymentNode.getUrl(); + if (!StringUtils.isNullOrEmpty(url)) { + url = " [[" + url + "]]"; + } else { + url = ""; + } + + writer.writeLine( + format("rectangle \"%s\\n<size:10>%s</size>%s\" <<%s>> as %s%s {", + deploymentNode.getName() + (!"1".equals(deploymentNode.getInstances()) ? " (x" + deploymentNode.getInstances() + ")" : ""), + typeOf(view, deploymentNode, true), + icon, + idOf(deploymentNode), + idOf(deploymentNode), + url + ) + ); + writer.indent(); + + if (!isVisible(view, deploymentNode)) { + writer.writeLine("hide " + idOf(deploymentNode)); + } + } + + @Override + protected void endDeploymentNodeBoundary(ModelView view, IndentingWriter writer) { + writer.outdent(); + writer.writeLine("}"); + writer.writeLine(); + } + + @Override + public Diagram export(DynamicView view) { + if (renderAsSequenceDiagram(view)) { + IndentingWriter writer = new IndentingWriter(); + writeHeader(view, writer); + + Set<Element> elements = new LinkedHashSet<>(); + for (RelationshipView relationshipView : view.getRelationships()) { + elements.add(relationshipView.getRelationship().getSource()); + elements.add(relationshipView.getRelationship().getDestination()); + } + + for (Element element : elements) { + writeElement(view, element, writer); + } + + writeRelationships(view, writer); + writeFooter(view, writer); + + return createDiagram(view, writer.toString()); + } else { + return super.export(view); + } + } + + @Override + protected void writeElement(ModelView view, Element element, IndentingWriter writer) { + ElementStyle elementStyle = findElementStyle(view, element); + + if (view instanceof DynamicView && renderAsSequenceDiagram(view)) { + writer.writeLine(String.format("%s \"%s\\n<size:10>%s</size>\" as %s <<%s>> %s", + plantumlSequenceType(view, element), + element.getName(), + typeOf(view, element, true), + idOf(element), + idOf(element), + elementStyle.getBackground())); + } else { + String shape = plantUMLShapeOf(view, element); + if ("actor".equals(shape)) { + shape = "rectangle"; + } + String name = element.getName(); + String description = element.getDescription(); + String type = typeOf(view, element, true); + String icon = ""; + String url = element.getUrl(); + + if (element instanceof StaticStructureElementInstance) { + StaticStructureElementInstance elementInstance = (StaticStructureElementInstance) element; + name = elementInstance.getElement().getName(); + description = elementInstance.getElement().getDescription(); + type = typeOf(view, elementInstance.getElement(), true); + shape = plantUMLShapeOf(view, elementInstance.getElement()); + url = elementInstance.getUrl(); + + if (StringUtils.isNullOrEmpty(url)) { + url = elementInstance.getElement().getUrl(); + } + } + + if (!StringUtils.isNullOrEmpty(url)) { + url = " [[" + url + "]]"; + } else { + url = ""; + } + + if (StringUtils.isNullOrEmpty(description) || false == elementStyle.getDescription()) { + description = ""; + } else { + description = "\\n\\n" + description; + } + + if (StringUtils.isNullOrEmpty(type) || false == elementStyle.getMetadata()) { + type = ""; + } else { + type = String.format("\\n<size:10>%s</size>", type); + } + + if (elementStyleHasSupportedIcon(elementStyle)) { + double scale = calculateIconScale(elementStyle.getIcon()); + icon = "\\n\\n<img:" + elementStyle.getIcon() + "{scale=" + scale + "}>"; + } + + String id = idOf(element); + + writer.writeLine(format("%s \"==%s%s%s%s\" <<%s>> as %s%s", + shape, + name, + type, + description, + icon, + id, + id, + url) + ); + + if (!isVisible(view, element)) { + writer.writeLine("hide " + id); + } + } + } + + @Override + protected void writeRelationship(ModelView view, RelationshipView relationshipView, IndentingWriter writer) { + Relationship relationship = relationshipView.getRelationship(); + RelationshipStyle style = findRelationshipStyle(view, relationship); + + String description = ""; + String technology = relationship.getTechnology(); + + if (view instanceof DynamicView && renderAsSequenceDiagram(view)) { + // do nothing - sequence diagrams don't need the order + } else { + if (!StringUtils.isNullOrEmpty(relationshipView.getOrder())) { + description = relationshipView.getOrder() + ". "; + } + } + + description += (hasValue(relationshipView.getDescription()) ? relationshipView.getDescription() : hasValue(relationshipView.getRelationship().getDescription()) ? relationshipView.getRelationship().getDescription() : ""); + + if (view instanceof DynamicView && renderAsSequenceDiagram(view)) { + String arrowStart = "-"; + String arrowEnd = ">"; + + if (relationshipView.isResponse() != null && relationshipView.isResponse() == true) { + arrowStart = "<-"; + arrowEnd = "-"; + } + + writer.writeLine( + String.format("%s %s[%s]%s %s : %s", + idOf(relationship.getSource()), + arrowStart, + style.getColor(), + arrowEnd, + idOf(relationship.getDestination()), + description)); + } else { + boolean solid = style.getStyle() == LineStyle.Solid || false == style.getDashed(); + + String arrowStart; + String arrowEnd; + String relationshipStyle = style.getColor(); + + if (style.getThickness() != null) { + relationshipStyle += ",thickness=" + style.getThickness(); + } + + if (relationshipView.isResponse() != null && relationshipView.isResponse()) { + arrowStart = solid ? "<-" : "<."; + arrowEnd = solid ? "-" : "."; + } else { + arrowStart = solid ? "-" : "."; + arrowEnd = solid ? "->" : ".>"; + } + + if (!isVisible(view, relationshipView)) { + relationshipStyle = "hidden"; + } + + // 1 .[#rrggbb,thickness=n].> 2 : "...\n<size:8>...</size> + writer.writeLine(format("%s %s[%s]%s %s : \"<color:%s>%s%s\"", + idOf(relationship.getSource()), + arrowStart, + relationshipStyle, + arrowEnd, + idOf(relationship.getDestination()), + style.getColor(), + description, + (StringUtils.isNullOrEmpty(technology) ? "" : "\\n<color:" + style.getColor() + "><size:8>[" + technology + "]</size>") + )); + } + } + + @Override + protected Legend createLegend(ModelView view) { + IndentingWriter writer = new IndentingWriter(); + int id = 0; + + writer.writeLine("@startuml"); + writer.writeLine("set separator none"); + writer.writeLine(); + + writer.writeLine("skinparam {"); + writer.indent(); + writer.writeLine("shadowing false"); + writer.writeLine("arrowFontSize 15"); + writer.writeLine("defaultTextAlignment center"); + writer.writeLine("wrapWidth 100"); + writer.writeLine("maxMessageSize 100"); + Font font = view.getViewSet().getConfiguration().getBranding().getFont(); + if (font != null) { + String fontName = font.getName(); + if (!StringUtils.isNullOrEmpty(fontName)) { + writer.writeLine("defaultFontName \"" + fontName + "\""); + } + } + writer.outdent(); + writer.writeLine("}"); + + writer.writeLine("hide stereotype"); + writer.writeLine(); + + writer.writeLine("skinparam rectangle<<_transparent>> {"); + writer.indent(); + writer.writeLine("BorderColor transparent"); + writer.writeLine("BackgroundColor transparent"); + writer.writeLine("FontColor transparent"); + writer.outdent(); + writer.writeLine("}"); + writer.writeLine(); + + Map<String,ElementStyle> elementStyles = new HashMap<>(); + List<Element> elements = view.getElements().stream().map(ElementView::getElement).collect(Collectors.toList()); + for (Element element : elements) { + ElementStyle elementStyle = findElementStyle(view, element); + + if (element instanceof DeploymentNode) { + // deployment node backgrounds are always white + elementStyle.setBackground("#ffffff"); + } + + if (!StringUtils.isNullOrEmpty(elementStyle.getTag()) ) { + elementStyles.put(elementStyle.getTag(), elementStyle); + }; + } + + List<ElementStyle> sortedElementStyles = elementStyles.values().stream().sorted(Comparator.comparing(ElementStyle::getTag)).collect(Collectors.toList());; + for (ElementStyle elementStyle : sortedElementStyles) { + id++; + Shape shape = elementStyle.getShape(); + String type = plantUMLShapeOf(elementStyle.getShape()); + if ("actor".equals(type)) { + type = "rectangle"; // the actor shape is not supported in this implementation + } + + String background = elementStyle.getBackground(); + String stroke = elementStyle.getStroke(); + String color = elementStyle.getColor(); + + if (view instanceof DynamicView && renderAsSequenceDiagram(view)) { + type = "sequenceParticipant"; + } + + writer.writeLine(format("skinparam %s<<%s>> {", type, id)); + writer.indent(); + writer.writeLine(String.format("BackgroundColor %s", background)); + writer.writeLine(String.format("FontColor %s", color)); + writer.writeLine(String.format("BorderColor %s", stroke)); + + if (shape == Shape.RoundedBox) { + writer.writeLine("roundCorner 20"); + } + writer.outdent(); + writer.writeLine("}"); + + String description = elementStyle.getTag(); + if (description.startsWith("Element,")) { + description = description.substring("Element,".length()); + } + description = description.replaceAll(",", ", "); + + String icon = ""; + if (elementStyleHasSupportedIcon(elementStyle)) { + double scale = calculateIconScale(elementStyle.getIcon()); + icon = "\\n\\n<img:" + elementStyle.getIcon() + "{scale=" + scale + "}>"; + } + + writer.writeLine(format("%s \"==%s%s\" <<%s>>", + type, + description, + icon, + id) + ); + writer.writeLine(); + } + + Map<String,RelationshipStyle> relationshipStyles = new HashMap<>(); + List<Relationship> relationships = view.getRelationships().stream().map(RelationshipView::getRelationship).collect(Collectors.toList()); + for (Relationship relationship : relationships) { + RelationshipStyle relationshipStyle = findRelationshipStyle(view, relationship); + + if (!StringUtils.isNullOrEmpty(relationshipStyle.getTag())) { + relationshipStyles.put(relationshipStyle.getTag(), relationshipStyle); + } + } + + List<RelationshipStyle> sortedRelationshipStyles = relationshipStyles.values().stream().sorted(Comparator.comparing(RelationshipStyle::getTag)).collect(Collectors.toList());; + for (RelationshipStyle relationshipStyle : sortedRelationshipStyles) { + id++; + + String description = relationshipStyle.getTag(); + if (description.startsWith("Relationship,")) { + description = description.substring("Relationship,".length()); + } + description = description.replaceAll(",", ", "); + + writer.writeLine(format("rectangle \".\" <<_transparent>> as %s", id)); + + boolean solid = relationshipStyle.getStyle() == LineStyle.Solid || false == relationshipStyle.getDashed(); + + String arrowStart = solid ? "-" : "."; + String arrowEnd = solid ? "->" : ".>"; + String buf = relationshipStyle.getColor(); + + if (relationshipStyle.getThickness() != null) { + buf += ",thickness=" + relationshipStyle.getThickness(); + } + + // 1 .[#rrggbb,thickness=n].> 2 : "..." + writer.writeLine(format("%s %s[%s]%s %s : \"<color:%s>%s\"", + id, + arrowStart, + buf, + arrowEnd, + id, + relationshipStyle.getColor(), + description) + ); + + writer.writeLine(); + } + + writer.writeLine(); + + writer.writeLine("@enduml"); + + return new Legend(writer.toString()); + } + + protected boolean renderAsSequenceDiagram(ModelView view) { + return view instanceof DynamicView && "true".equalsIgnoreCase(getViewOrViewSetProperty(view, PLANTUML_SEQUENCE_DIAGRAM_PROPERTY, "false")); + } + +} diff --git a/structurizr-export/src/main/java/com/structurizr/export/websequencediagrams/README.md b/structurizr-export/src/main/java/com/structurizr/export/websequencediagrams/README.md new file mode 100644 index 000000000..f75f782df --- /dev/null +++ b/structurizr-export/src/main/java/com/structurizr/export/websequencediagrams/README.md @@ -0,0 +1,23 @@ +# WebSequenceDiagrams + +The [WebSequenceDiagramExporter](WebSequenceDiagramsDiagram.java) class provides a way to export dynamic views to +diagram definitions that are compatible with [websequencediagrams.com](https://www.websequencediagrams.com). + +## Example usage + +You can either export all dynamic views in a workspace: + +``` +Workspace workspace = ... +WebSequenceDiagramsExporter exporter = new WebSequenceDiagramsExporter(); +Collection<Diagram> diagrams = exporter.export(workspace); +``` + +Or just a single dynamic view: + +``` +Workspace workspace = ... +DynamicView view = ... +WebSequenceDiagramsExporter exporter = new WebSequenceDiagramsExporter(); +Diagram diagram = exporter.export(view); +``` \ No newline at end of file diff --git a/structurizr-export/src/main/java/com/structurizr/export/websequencediagrams/WebSequenceDiagramsDiagram.java b/structurizr-export/src/main/java/com/structurizr/export/websequencediagrams/WebSequenceDiagramsDiagram.java new file mode 100644 index 000000000..73445af84 --- /dev/null +++ b/structurizr-export/src/main/java/com/structurizr/export/websequencediagrams/WebSequenceDiagramsDiagram.java @@ -0,0 +1,17 @@ +package com.structurizr.export.websequencediagrams; + +import com.structurizr.export.Diagram; +import com.structurizr.view.ModelView; + +public class WebSequenceDiagramsDiagram extends Diagram { + + public WebSequenceDiagramsDiagram(ModelView view, String definition) { + super(view, definition); + } + + @Override + public String getFileExtension() { + return "wsd"; + } + +} \ No newline at end of file diff --git a/structurizr-export/src/main/java/com/structurizr/export/websequencediagrams/WebSequenceDiagramsExporter.java b/structurizr-export/src/main/java/com/structurizr/export/websequencediagrams/WebSequenceDiagramsExporter.java new file mode 100644 index 000000000..b731844a5 --- /dev/null +++ b/structurizr-export/src/main/java/com/structurizr/export/websequencediagrams/WebSequenceDiagramsExporter.java @@ -0,0 +1,176 @@ +package com.structurizr.export.websequencediagrams; + +import com.structurizr.export.AbstractDiagramExporter; +import com.structurizr.export.Diagram; +import com.structurizr.export.IndentingWriter; +import com.structurizr.model.*; +import com.structurizr.util.StringUtils; +import com.structurizr.view.*; + +import java.util.LinkedHashSet; +import java.util.Set; + +/** + * Exports dynamic diagram definitions that can be copy-pasted + * into https://www.websequencediagrams.com + * + * This implementation only supports a basic sequence of interactions, + * both synchronous and asynchronous. It doesn't support return messages, + * parallel behaviour, etc. + */ +public class WebSequenceDiagramsExporter extends AbstractDiagramExporter { + + private static final String SYNCHRONOUS_INTERACTION = "->"; + private static final String ASYNCHRONOUS_INTERACTION = "->>"; + private static final String SYNCHRONOUS_INTERACTION_RETURN = "-->"; + private static final String ASYNCHRONOUS_INTERACTION_RETURN = "-->>"; + + @Override + public Diagram export(SystemLandscapeView view) { + return null; + } + + @Override + public Diagram export(SystemContextView view) { + return null; + } + + @Override + public Diagram export(ContainerView view) { + return null; + } + + @Override + public Diagram export(ComponentView view) { + return null; + } + + @Override + public Diagram export(DynamicView view) { + IndentingWriter writer = new IndentingWriter(); + writeHeader(view, writer); + + Set<Element> elements = new LinkedHashSet<>(); + for (RelationshipView relationshipView : view.getRelationships()) { + elements.add(relationshipView.getRelationship().getSource()); + elements.add(relationshipView.getRelationship().getDestination()); + } + + for (Element element : elements) { + writeElement(view, element, writer); + } + + writer.writeLine(); + + writeRelationships(view, writer); + writeFooter(view, writer); + + return createDiagram(view, writer.toString()); + } + + @Override + public Diagram export(DeploymentView view) { + return null; + } + + @Override + protected void writeHeader(ModelView view, IndentingWriter writer) { + writer.writeLine("title " + view.getName() + " - " + view.getKey()); + writer.writeLine(); + } + + @Override + protected void writeFooter(ModelView view, IndentingWriter writer) { + } + + @Override + protected void startEnterpriseBoundary(ModelView view, String enterpriseName, IndentingWriter writer) { + } + + @Override + protected void endEnterpriseBoundary(ModelView view, IndentingWriter writer) { + } + + @Override + protected void startGroupBoundary(ModelView view, String group, IndentingWriter writer) { + } + + @Override + protected void endGroupBoundary(ModelView view, IndentingWriter writer) { + } + + @Override + protected void startSoftwareSystemBoundary(ModelView view, SoftwareSystem softwareSystem, IndentingWriter writer) { + } + + @Override + protected void endSoftwareSystemBoundary(ModelView view, IndentingWriter writer) { + } + + @Override + protected void startContainerBoundary(ModelView view, Container container, IndentingWriter writer) { + } + + @Override + protected void endContainerBoundary(ModelView view, IndentingWriter writer) { + } + + @Override + protected void startDeploymentNodeBoundary(DeploymentView view, DeploymentNode deploymentNode, IndentingWriter writer) { + } + + @Override + protected void endDeploymentNodeBoundary(ModelView view, IndentingWriter writer) { + } + + @Override + protected void writeElement(ModelView view, Element element, IndentingWriter writer) { + if (element instanceof Person) { + writer.writeLine(String.format("actor <<%s>>\\n%s as %s", + view.getViewSet().getConfiguration().getTerminology().findTerminology(element), + element.getName(), + element.getName()) + ); + } else { + writer.writeLine(String.format("participant <<%s>>\\n%s as %s", + view.getViewSet().getConfiguration().getTerminology().findTerminology(element), + element.getName(), + element.getName()) + ); + } + } + + @Override + protected void writeRelationship(ModelView view, RelationshipView relationshipView, IndentingWriter writer) { + Relationship r = relationshipView.getRelationship(); + + Element source = r.getSource(); + Element destination = r.getDestination(); + String description = relationshipView.getDescription(); + String arrow = r.getInteractionStyle() == InteractionStyle.Asynchronous ? ASYNCHRONOUS_INTERACTION : SYNCHRONOUS_INTERACTION; + + if (relationshipView.isResponse() != null && relationshipView.isResponse()) { + source = r.getDestination(); + destination = r.getSource(); + arrow = r.getInteractionStyle() == InteractionStyle.Asynchronous ? ASYNCHRONOUS_INTERACTION_RETURN : SYNCHRONOUS_INTERACTION_RETURN; + } + + if (StringUtils.isNullOrEmpty(description)) { + description = relationshipView.getRelationship().getDescription(); + } + + // Thing A->Thing B: Description + writer.writeLine(String.format("%s%s%s: %s", + source.getName(), + arrow, + destination.getName(), + description + )); + } + + @Override + protected Diagram createDiagram(ModelView view, String definition) { + return new WebSequenceDiagramsDiagram(view, definition); + } + +} \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/AbstractExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/AbstractExporterTests.java new file mode 100644 index 000000000..7782650d9 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/AbstractExporterTests.java @@ -0,0 +1,29 @@ +package com.structurizr.export; + +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.List; + +public abstract class AbstractExporterTests { + + protected String readFile(File file) throws Exception { + StringBuilder buf = new StringBuilder(); + + Files.readAllLines(file.toPath(), StandardCharsets.UTF_8); + + List<String> lines = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8); + + for (String line : lines) { + buf.append(line); + buf.append("\n"); + } + + if (buf.length() > 1) { + return buf.toString().substring(0, buf.length() - 1); + } else { + return buf.toString(); + } + } + +} diff --git a/structurizr-export/src/test/java/com/structurizr/export/IndentingWriterTests.java b/structurizr-export/src/test/java/com/structurizr/export/IndentingWriterTests.java new file mode 100644 index 000000000..7c050b98a --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/IndentingWriterTests.java @@ -0,0 +1,76 @@ +package com.structurizr.export; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class IndentingWriterTests { + + @Test + public void test_WithDefaultSettings() { + IndentingWriter writer = new IndentingWriter(); + + writer.writeLine("Line 1"); + writer.indent(); + writer.writeLine("Line 2"); + writer.indent(); + writer.writeLine("Line 3"); + writer.outdent(); + writer.writeLine("Line 4"); + writer.outdent(); + writer.writeLine("Line 4"); + + assertEquals("Line 1\n" + + " Line 2\n" + + " Line 3\n" + + " Line 4\n" + + "Line 4", writer.toString()); + } + + @Test + public void test_WithSpaces() { + IndentingWriter writer = new IndentingWriter(); + writer.setIndentType(IndentType.Spaces); + writer.setIndentQuantity(4); + + writer.writeLine("Line 1"); + writer.indent(); + writer.writeLine("Line 2"); + writer.indent(); + writer.writeLine("Line 3"); + writer.outdent(); + writer.writeLine("Line 4"); + writer.outdent(); + writer.writeLine("Line 4"); + + assertEquals("Line 1\n" + + " Line 2\n" + + " Line 3\n" + + " Line 4\n" + + "Line 4", writer.toString()); + } + + @Test + public void test_WithTabs() { + IndentingWriter writer = new IndentingWriter(); + writer.setIndentType(IndentType.Tabs); + writer.setIndentQuantity(1); + + writer.writeLine("Line 1"); + writer.indent(); + writer.writeLine("Line 2"); + writer.indent(); + writer.writeLine("Line 3"); + writer.outdent(); + writer.writeLine("Line 4"); + writer.outdent(); + writer.writeLine("Line 4"); + + assertEquals("Line 1\n" + + "\tLine 2\n" + + "\t\tLine 3\n" + + "\tLine 4\n" + + "Line 4", writer.toString()); + } + +} \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/36141-Components.dot b/structurizr-export/src/test/java/com/structurizr/export/dot/36141-Components.dot new file mode 100644 index 000000000..76c5ff5e9 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/dot/36141-Components.dot @@ -0,0 +1,43 @@ +digraph { + compound=true + graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] + node [fontname="Arial", shape=box, margin="0.4,0.3"] + edge [fontname="Arial"] + label=<<br /><font point-size="34">Internet Banking System - API Application - Components</font><br /><font point-size="24">The component diagram for the API Application.</font>> + + 4 [id=4,shape=rect, label=<<font point-size="34">Mainframe Banking<br />System</font><br /><font point-size="19">[Software System]</font><br /><br /><font point-size="24">Stores all of the core banking<br />information about customers,<br />accounts, transactions, etc.</font>>, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] + 17 [id=17,shape=rect, label=<<font point-size="34">Single-Page<br />Application</font><br /><font point-size="19">[Container: JavaScript and Angular]</font><br /><br /><font point-size="24">Provides all of the Internet<br />banking functionality to<br />customers via their web<br />browser.</font>>, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + 6 [id=6,shape=rect, label=<<font point-size="34">E-mail System</font><br /><font point-size="19">[Software System]</font><br /><br /><font point-size="24">The internal Microsoft<br />Exchange e-mail system.</font>>, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] + 18 [id=18,shape=rect, label=<<font point-size="34">Mobile App</font><br /><font point-size="19">[Container: Xamarin]</font><br /><br /><font point-size="24">Provides a limited subset of<br />the Internet banking<br />functionality to customers via<br />their mobile device.</font>>, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + 21 [id=21,shape=cylinder, label=<<font point-size="34">Database</font><br /><font point-size="19">[Container: Oracle Database Schema]</font><br /><br /><font point-size="24">Stores user registration<br />information, hashed<br />authentication credentials,<br />access logs, etc.</font>>, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + + subgraph cluster_20 { + margin=25 + label=<<font point-size="24"><br />API Application</font><br /><font point-size="19">[Container: Java and Spring MVC]</font>> + labelloc=b + color="#444444" + fontcolor="#444444" + fillcolor="#444444" + + 29 [id=29,shape=rect, label=<<font point-size="34">Sign In Controller</font><br /><font point-size="19">[Component: Spring MVC Rest Controller]</font><br /><br /><font point-size="24">Allows users to sign in to the<br />Internet Banking System.</font>>, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] + 30 [id=30,shape=rect, label=<<font point-size="34">Accounts Summary<br />Controller</font><br /><font point-size="19">[Component: Spring MVC Rest Controller]</font><br /><br /><font point-size="24">Provides customers with a<br />summary of their bank<br />accounts.</font>>, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] + 31 [id=31,shape=rect, label=<<font point-size="34">Reset Password<br />Controller</font><br /><font point-size="19">[Component: Spring MVC Rest Controller]</font><br /><br /><font point-size="24">Allows users to reset their<br />passwords with a single use<br />URL.</font>>, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] + 32 [id=32,shape=rect, label=<<font point-size="34">Security Component</font><br /><font point-size="19">[Component: Spring Bean]</font><br /><br /><font point-size="24">Provides functionality related<br />to signing in, changing<br />passwords, etc.</font>>, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] + 33 [id=33,shape=rect, label=<<font point-size="34">Mainframe Banking<br />System Facade</font><br /><font point-size="19">[Component: Spring Bean]</font><br /><br /><font point-size="24">A facade onto the mainframe<br />banking system.</font>>, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] + 34 [id=34,shape=rect, label=<<font point-size="34">E-mail Component</font><br /><font point-size="19">[Component: Spring Bean]</font><br /><br /><font point-size="24">Sends e-mails to users.</font>>, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] + } + + 17 -> 29 [id=35, label=<<font point-size="24">Makes API calls to</font><br /><font point-size="19">[JSON/HTTPS]</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 17 -> 31 [id=37, label=<<font point-size="24">Makes API calls to</font><br /><font point-size="19">[JSON/HTTPS]</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 17 -> 30 [id=38, label=<<font point-size="24">Makes API calls to</font><br /><font point-size="19">[JSON/HTTPS]</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 18 -> 29 [id=39, label=<<font point-size="24">Makes API calls to</font><br /><font point-size="19">[JSON/HTTPS]</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 18 -> 31 [id=41, label=<<font point-size="24">Makes API calls to</font><br /><font point-size="19">[JSON/HTTPS]</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 18 -> 30 [id=42, label=<<font point-size="24">Makes API calls to</font><br /><font point-size="19">[JSON/HTTPS]</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 29 -> 32 [id=43, label=<<font point-size="24">Uses</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 30 -> 33 [id=44, label=<<font point-size="24">Uses</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 31 -> 32 [id=45, label=<<font point-size="24">Uses</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 31 -> 34 [id=46, label=<<font point-size="24">Uses</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 32 -> 21 [id=47, label=<<font point-size="24">Reads from and writes to</font><br /><font point-size="19">[JDBC]</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 33 -> 4 [id=48, label=<<font point-size="24">Uses</font><br /><font point-size="19">[XML/HTTPS]</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 34 -> 6 [id=49, label=<<font point-size="24">Sends e-mail using</font>>, style="dashed", color="#707070", fontcolor="#707070"] +} \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/36141-Containers.dot b/structurizr-export/src/test/java/com/structurizr/export/dot/36141-Containers.dot new file mode 100644 index 000000000..c4548fdc9 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/dot/36141-Containers.dot @@ -0,0 +1,37 @@ +digraph { + compound=true + graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] + node [fontname="Arial", shape=box, margin="0.4,0.3"] + edge [fontname="Arial"] + label=<<br /><font point-size="34">Internet Banking System - Containers</font><br /><font point-size="24">The container diagram for the Internet Banking System.</font>> + + 1 [id=1,shape=rect, label=<<font point-size="32">Personal Banking<br />Customer</font><br /><font point-size="17">[Person]</font><br /><br /><font point-size="22">A customer of the bank, with<br />personal bank accounts.</font>>, style=filled, color="#052e56", fillcolor="#08427b", fontcolor="#ffffff"] + 4 [id=4,shape=rect, label=<<font point-size="34">Mainframe Banking<br />System</font><br /><font point-size="19">[Software System]</font><br /><br /><font point-size="24">Stores all of the core banking<br />information about customers,<br />accounts, transactions, etc.</font>>, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] + 6 [id=6,shape=rect, label=<<font point-size="34">E-mail System</font><br /><font point-size="19">[Software System]</font><br /><br /><font point-size="24">The internal Microsoft<br />Exchange e-mail system.</font>>, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] + + subgraph cluster_2 { + margin=25 + label=<<font point-size="24"><br />Internet Banking System</font><br /><font point-size="19">[Software System]</font>> + labelloc=b + color="#444444" + fontcolor="#444444" + fillcolor="#444444" + + 17 [id=17,shape=rect, label=<<font point-size="34">Single-Page<br />Application</font><br /><font point-size="19">[Container: JavaScript and Angular]</font><br /><br /><font point-size="24">Provides all of the Internet<br />banking functionality to<br />customers via their web<br />browser.</font>>, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + 18 [id=18,shape=rect, label=<<font point-size="34">Mobile App</font><br /><font point-size="19">[Container: Xamarin]</font><br /><br /><font point-size="24">Provides a limited subset of<br />the Internet banking<br />functionality to customers via<br />their mobile device.</font>>, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + 19 [id=19,shape=rect, label=<<font point-size="34">Web Application</font><br /><font point-size="19">[Container: Java and Spring MVC]</font><br /><br /><font point-size="24">Delivers the static content<br />and the Internet banking<br />single page application.</font>>, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + 20 [id=20,shape=rect, label=<<font point-size="34">API Application</font><br /><font point-size="19">[Container: Java and Spring MVC]</font><br /><br /><font point-size="24">Provides Internet banking<br />functionality via a JSON/HTTPS<br />API.</font>>, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + 21 [id=21,shape=cylinder, label=<<font point-size="34">Database</font><br /><font point-size="19">[Container: Oracle Database Schema]</font><br /><br /><font point-size="24">Stores user registration<br />information, hashed<br />authentication credentials,<br />access logs, etc.</font>>, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + } + + 1 -> 19 [id=22, label=<<font point-size="24">Visits bigbank.com/ib<br />using</font><br /><font point-size="19">[HTTPS]</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 1 -> 17 [id=23, label=<<font point-size="24">Views account balances,<br />and makes payments using</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 1 -> 18 [id=24, label=<<font point-size="24">Views account balances,<br />and makes payments using</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 19 -> 17 [id=25, label=<<font point-size="24">Delivers to the customer's<br />web browser</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 20 -> 21 [id=26, label=<<font point-size="24">Reads from and writes to</font><br /><font point-size="19">[JDBC]</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 20 -> 4 [id=27, label=<<font point-size="24">Makes API calls to</font><br /><font point-size="19">[XML/HTTPS]</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 20 -> 6 [id=28, label=<<font point-size="24">Sends e-mail using</font><br /><font point-size="19">[SMTP]</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 17 -> 20 [id=36, label=<<font point-size="24">Makes API calls to</font><br /><font point-size="19">[JSON/HTTPS]</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 18 -> 20 [id=40, label=<<font point-size="24">Makes API calls to</font><br /><font point-size="19">[JSON/HTTPS]</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 6 -> 1 [id=8, label=<<font point-size="24">Sends e-mails to</font>>, style="dashed", color="#707070", fontcolor="#707070"] +} \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/36141-DevelopmentDeployment.dot b/structurizr-export/src/test/java/com/structurizr/export/dot/36141-DevelopmentDeployment.dot new file mode 100644 index 000000000..0b86e2c80 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/dot/36141-DevelopmentDeployment.dot @@ -0,0 +1,97 @@ +digraph { + compound=true + graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] + node [fontname="Arial", shape=box, margin="0.4,0.3"] + edge [fontname="Arial"] + label=<<br /><font point-size="34">Internet Banking System - Deployment - Development</font><br /><font point-size="24">An example development deployment scenario for the Internet Banking System.</font>> + + subgraph cluster_50 { + margin=25 + label=<<font point-size="24">Developer Laptop</font><br /><font point-size="19">[Deployment Node: Microsoft Windows 10 or Apple macOS]</font>> + labelloc=b + color="#888888" + fontcolor="#000000" + fillcolor="#ffffff" + + subgraph cluster_51 { + margin=25 + label=<<font point-size="24">Docker Container - Web Server</font><br /><font point-size="19">[Deployment Node: Docker]</font>> + labelloc=b + color="#888888" + fontcolor="#000000" + fillcolor="#ffffff" + + subgraph cluster_52 { + margin=25 + label=<<font point-size="24">Apache Tomcat</font><br /><font point-size="19">[Deployment Node: Apache Tomcat 8.x]</font>> + labelloc=b + color="#888888" + fontcolor="#000000" + fillcolor="#ffffff" + + 53 [id=53,shape=rect, label=<<font point-size="34">Web Application</font><br /><font point-size="19">[Container: Java and Spring MVC]</font><br /><br /><font point-size="24">Delivers the static content<br />and the Internet banking<br />single page application.</font>>, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + 54 [id=54,shape=rect, label=<<font point-size="34">API Application</font><br /><font point-size="19">[Container: Java and Spring MVC]</font><br /><br /><font point-size="24">Provides Internet banking<br />functionality via a JSON/HTTPS<br />API.</font>>, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + } + + } + + subgraph cluster_59 { + margin=25 + label=<<font point-size="24">Docker Container - Database Server</font><br /><font point-size="19">[Deployment Node: Docker]</font>> + labelloc=b + color="#888888" + fontcolor="#000000" + fillcolor="#ffffff" + + subgraph cluster_60 { + margin=25 + label=<<font point-size="24">Database Server</font><br /><font point-size="19">[Deployment Node: Oracle 12c]</font>> + labelloc=b + color="#888888" + fontcolor="#000000" + fillcolor="#ffffff" + + 61 [id=61,shape=cylinder, label=<<font point-size="34">Database</font><br /><font point-size="19">[Container: Oracle Database Schema]</font><br /><br /><font point-size="24">Stores user registration<br />information, hashed<br />authentication credentials,<br />access logs, etc.</font>>, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + } + + } + + subgraph cluster_63 { + margin=25 + label=<<font point-size="24">Web Browser</font><br /><font point-size="19">[Deployment Node: Chrome, Firefox, Safari, or Edge]</font>> + labelloc=b + color="#888888" + fontcolor="#000000" + fillcolor="#ffffff" + + 64 [id=64,shape=rect, label=<<font point-size="34">Single-Page<br />Application</font><br /><font point-size="19">[Container: JavaScript and Angular]</font><br /><br /><font point-size="24">Provides all of the Internet<br />banking functionality to<br />customers via their web<br />browser.</font>>, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + } + + } + + subgraph cluster_55 { + margin=25 + label=<<font point-size="24">Big Bank plc</font><br /><font point-size="19">[Deployment Node: Big Bank plc data center]</font>> + labelloc=b + color="#888888" + fontcolor="#000000" + fillcolor="#ffffff" + + subgraph cluster_56 { + margin=25 + label=<<font point-size="24">bigbank-dev001</font><br /><font point-size="19">[Deployment Node]</font>> + labelloc=b + color="#888888" + fontcolor="#000000" + fillcolor="#ffffff" + + 57 [id=57,shape=rect, label=<<font point-size="34">Mainframe Banking<br />System</font><br /><font point-size="19">[Software System]</font><br /><br /><font point-size="24">Stores all of the core banking<br />information about customers,<br />accounts, transactions, etc.</font>>, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] + } + + } + + 54 -> 57 [id=58, label=<<font point-size="24">Makes API calls to</font><br /><font point-size="19">[XML/HTTPS]</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 54 -> 61 [id=62, label=<<font point-size="24">Reads from and writes to</font><br /><font point-size="19">[JDBC]</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 64 -> 54 [id=65, label=<<font point-size="24">Makes API calls to</font><br /><font point-size="19">[JSON/HTTPS]</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 53 -> 64 [id=66, label=<<font point-size="24">Delivers to the customer's<br />web browser</font>>, style="dashed", color="#707070", fontcolor="#707070"] +} \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/36141-LiveDeployment.dot b/structurizr-export/src/test/java/com/structurizr/export/dot/36141-LiveDeployment.dot new file mode 100644 index 000000000..841b0dc42 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/dot/36141-LiveDeployment.dot @@ -0,0 +1,152 @@ +digraph { + compound=true + graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] + node [fontname="Arial", shape=box, margin="0.4,0.3"] + edge [fontname="Arial"] + label=<<br /><font point-size="34">Internet Banking System - Deployment - Live</font><br /><font point-size="24">An example live deployment scenario for the Internet Banking System.</font>> + + subgraph cluster_67 { + margin=25 + label=<<font point-size="24">Customer's mobile device</font><br /><font point-size="19">[Deployment Node: Apple iOS or Android]</font>> + labelloc=b + color="#888888" + fontcolor="#000000" + fillcolor="#ffffff" + + 68 [id=68,shape=rect, label=<<font point-size="34">Mobile App</font><br /><font point-size="19">[Container: Xamarin]</font><br /><br /><font point-size="24">Provides a limited subset of<br />the Internet banking<br />functionality to customers via<br />their mobile device.</font>>, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + } + + subgraph cluster_69 { + margin=25 + label=<<font point-size="24">Customer's computer</font><br /><font point-size="19">[Deployment Node: Microsoft Windows or Apple macOS]</font>> + labelloc=b + color="#888888" + fontcolor="#000000" + fillcolor="#ffffff" + + subgraph cluster_70 { + margin=25 + label=<<font point-size="24">Web Browser</font><br /><font point-size="19">[Deployment Node: Chrome, Firefox, Safari, or Edge]</font>> + labelloc=b + color="#888888" + fontcolor="#000000" + fillcolor="#ffffff" + + 71 [id=71,shape=rect, label=<<font point-size="34">Single-Page<br />Application</font><br /><font point-size="19">[Container: JavaScript and Angular]</font><br /><br /><font point-size="24">Provides all of the Internet<br />banking functionality to<br />customers via their web<br />browser.</font>>, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + } + + } + + subgraph cluster_72 { + margin=25 + label=<<font point-size="24">Big Bank plc</font><br /><font point-size="19">[Deployment Node: Big Bank plc data center]</font>> + labelloc=b + color="#888888" + fontcolor="#000000" + fillcolor="#ffffff" + + subgraph cluster_73 { + margin=25 + label=<<font point-size="24">bigbank-prod001</font><br /><font point-size="19">[Deployment Node]</font>> + labelloc=b + color="#888888" + fontcolor="#000000" + fillcolor="#ffffff" + + 74 [id=74,shape=rect, label=<<font point-size="34">Mainframe Banking<br />System</font><br /><font point-size="19">[Software System]</font><br /><br /><font point-size="24">Stores all of the core banking<br />information about customers,<br />accounts, transactions, etc.</font>>, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] + } + + subgraph cluster_75 { + margin=25 + label=<<font point-size="24">bigbank-web***</font><br /><font point-size="19">[Deployment Node: Ubuntu 16.04 LTS]</font>> + labelloc=b + color="#888888" + fontcolor="#000000" + fillcolor="#ffffff" + + subgraph cluster_76 { + margin=25 + label=<<font point-size="24">Apache Tomcat</font><br /><font point-size="19">[Deployment Node: Apache Tomcat 8.x]</font>> + labelloc=b + color="#888888" + fontcolor="#000000" + fillcolor="#ffffff" + + 77 [id=77,shape=rect, label=<<font point-size="34">Web Application</font><br /><font point-size="19">[Container: Java and Spring MVC]</font><br /><br /><font point-size="24">Delivers the static content<br />and the Internet banking<br />single page application.</font>>, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + } + + } + + subgraph cluster_79 { + margin=25 + label=<<font point-size="24">bigbank-api***</font><br /><font point-size="19">[Deployment Node: Ubuntu 16.04 LTS]</font>> + labelloc=b + color="#888888" + fontcolor="#000000" + fillcolor="#ffffff" + + subgraph cluster_80 { + margin=25 + label=<<font point-size="24">Apache Tomcat</font><br /><font point-size="19">[Deployment Node: Apache Tomcat 8.x]</font>> + labelloc=b + color="#888888" + fontcolor="#000000" + fillcolor="#ffffff" + + 81 [id=81,shape=rect, label=<<font point-size="34">API Application</font><br /><font point-size="19">[Container: Java and Spring MVC]</font><br /><br /><font point-size="24">Provides Internet banking<br />functionality via a JSON/HTTPS<br />API.</font>>, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + } + + } + + subgraph cluster_85 { + margin=25 + label=<<font point-size="24">bigbank-db01</font><br /><font point-size="19">[Deployment Node: Ubuntu 16.04 LTS]</font>> + labelloc=b + color="#888888" + fontcolor="#000000" + fillcolor="#ffffff" + + subgraph cluster_86 { + margin=25 + label=<<font point-size="24">Oracle - Primary</font><br /><font point-size="19">[Deployment Node: Oracle 12c]</font>> + labelloc=b + color="#888888" + fontcolor="#000000" + fillcolor="#ffffff" + + 87 [id=87,shape=cylinder, label=<<font point-size="34">Database</font><br /><font point-size="19">[Container: Oracle Database Schema]</font><br /><br /><font point-size="24">Stores user registration<br />information, hashed<br />authentication credentials,<br />access logs, etc.</font>>, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + } + + } + + subgraph cluster_89 { + margin=25 + label=<<font point-size="24">bigbank-db02</font><br /><font point-size="19">[Deployment Node: Ubuntu 16.04 LTS]</font>> + labelloc=b + color="#888888" + fontcolor="#000000" + fillcolor="#ffffff" + + subgraph cluster_90 { + margin=25 + label=<<font point-size="24">Oracle - Secondary</font><br /><font point-size="19">[Deployment Node: Oracle 12c]</font>> + labelloc=b + color="#888888" + fontcolor="#000000" + fillcolor="#ffffff" + + 91 [id=91,shape=cylinder, label=<<font point-size="34">Database</font><br /><font point-size="19">[Container: Oracle Database Schema]</font><br /><br /><font point-size="24">Stores user registration<br />information, hashed<br />authentication credentials,<br />access logs, etc.</font>>, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + } + + } + + } + + 77 -> 71 [id=78, label=<<font point-size="24">Delivers to the customer's<br />web browser</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 68 -> 81 [id=82, label=<<font point-size="24">Makes API calls to</font><br /><font point-size="19">[JSON/HTTPS]</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 71 -> 81 [id=83, label=<<font point-size="24">Makes API calls to</font><br /><font point-size="19">[JSON/HTTPS]</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 81 -> 74 [id=84, label=<<font point-size="24">Makes API calls to</font><br /><font point-size="19">[XML/HTTPS]</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 81 -> 87 [id=88, label=<<font point-size="24">Reads from and writes to</font><br /><font point-size="19">[JDBC]</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 81 -> 91 [id=92, label=<<font point-size="24">Reads from and writes to</font><br /><font point-size="19">[JDBC]</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 87 -> 91 [id=93, label=<<font point-size="24">Replicates data to</font>>, style="dashed", color="#707070", fontcolor="#707070",ltail=cluster_86,lhead=cluster_90] +} \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/36141-SignIn.dot b/structurizr-export/src/test/java/com/structurizr/export/dot/36141-SignIn.dot new file mode 100644 index 000000000..bcf42801d --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/dot/36141-SignIn.dot @@ -0,0 +1,29 @@ +digraph { + compound=true + graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] + node [fontname="Arial", shape=box, margin="0.4,0.3"] + edge [fontname="Arial"] + label=<<br /><font point-size="34">API Application - Dynamic</font><br /><font point-size="24">Summarises how the sign in feature works in the single-page application.</font>> + + subgraph cluster_20 { + margin=25 + label=<<font point-size="24"><br />API Application</font><br /><font point-size="19">[Container: Java and Spring MVC]</font>> + labelloc=b + color="#444444" + fontcolor="#444444" + fillcolor="#444444" + + 29 [id=29,shape=rect, label=<<font point-size="34">Sign In Controller</font><br /><font point-size="19">[Component: Spring MVC Rest Controller]</font><br /><br /><font point-size="24">Allows users to sign in to the<br />Internet Banking System.</font>>, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] + 32 [id=32,shape=rect, label=<<font point-size="34">Security Component</font><br /><font point-size="19">[Component: Spring Bean]</font><br /><br /><font point-size="24">Provides functionality related<br />to signing in, changing<br />passwords, etc.</font>>, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] + } + + 17 [id=17,shape=rect, label=<<font point-size="34">Single-Page<br />Application</font><br /><font point-size="19">[Container: JavaScript and Angular]</font><br /><br /><font point-size="24">Provides all of the Internet<br />banking functionality to<br />customers via their web<br />browser.</font>>, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + 21 [id=21,shape=cylinder, label=<<font point-size="34">Database</font><br /><font point-size="19">[Container: Oracle Database Schema]</font><br /><br /><font point-size="24">Stores user registration<br />information, hashed<br />authentication credentials,<br />access logs, etc.</font>>, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + + 17 -> 29 [id=35, label=<<font point-size="24">1. Submits credentials to</font><br /><font point-size="19">[JSON/HTTPS]</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 29 -> 32 [id=43, label=<<font point-size="24">2. Validates credentials<br />using</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 32 -> 21 [id=47, label=<<font point-size="24">3. select * from users<br />where username = ?</font><br /><font point-size="19">[JDBC]</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 21 -> 32 [id=47, label=<<font point-size="24">4. Returns user data to</font><br /><font point-size="19">[JDBC]</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 32 -> 29 [id=43, label=<<font point-size="24">5. Returns true if the<br />hashed password matches</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 29 -> 17 [id=35, label=<<font point-size="24">6. Sends back an<br />authentication token to</font><br /><font point-size="19">[JSON/HTTPS]</font>>, style="dashed", color="#707070", fontcolor="#707070"] +} \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/36141-SystemContext.dot b/structurizr-export/src/test/java/com/structurizr/export/dot/36141-SystemContext.dot new file mode 100644 index 000000000..5f1280e06 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/dot/36141-SystemContext.dot @@ -0,0 +1,17 @@ +digraph { + compound=true + graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] + node [fontname="Arial", shape=box, margin="0.4,0.3"] + edge [fontname="Arial"] + label=<<br /><font point-size="34">Internet Banking System - System Context</font><br /><font point-size="24">The system context diagram for the Internet Banking System.</font>> + + 1 [id=1,shape=rect, label=<<font point-size="32">Personal Banking<br />Customer</font><br /><font point-size="17">[Person]</font><br /><br /><font point-size="22">A customer of the bank, with<br />personal bank accounts.</font>>, style=filled, color="#052e56", fillcolor="#08427b", fontcolor="#ffffff"] + 2 [id=2,shape=rect, label=<<font point-size="34">Internet Banking<br />System</font><br /><font point-size="19">[Software System]</font><br /><br /><font point-size="24">Allows customers to view<br />information about their bank<br />accounts, and make payments.</font>>, style=filled, color="#0b4884", fillcolor="#1168bd", fontcolor="#ffffff"] + 4 [id=4,shape=rect, label=<<font point-size="34">Mainframe Banking<br />System</font><br /><font point-size="19">[Software System]</font><br /><br /><font point-size="24">Stores all of the core banking<br />information about customers,<br />accounts, transactions, etc.</font>>, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] + 6 [id=6,shape=rect, label=<<font point-size="34">E-mail System</font><br /><font point-size="19">[Software System]</font><br /><br /><font point-size="24">The internal Microsoft<br />Exchange e-mail system.</font>>, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] + + 1 -> 2 [id=3, label=<<font point-size="24">Views account balances,<br />and makes payments using</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 2 -> 4 [id=5, label=<<font point-size="24">Gets account information<br />from, and makes payments<br />using</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 2 -> 6 [id=7, label=<<font point-size="24">Sends e-mail using</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 6 -> 1 [id=8, label=<<font point-size="24">Sends e-mails to</font>>, style="dashed", color="#707070", fontcolor="#707070"] +} \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/36141-SystemLandscape.dot b/structurizr-export/src/test/java/com/structurizr/export/dot/36141-SystemLandscape.dot new file mode 100644 index 000000000..b82be15da --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/dot/36141-SystemLandscape.dot @@ -0,0 +1,35 @@ +digraph { + compound=true + graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] + node [fontname="Arial", shape=box, margin="0.4,0.3"] + edge [fontname="Arial"] + label=<<br /><font point-size="34">System Landscape</font><br /><font point-size="24">The system landscape diagram for Big Bank plc.</font>> + + subgraph cluster_enterprise { + margin=25 + label=<<font point-size="24"><br />Big Bank plc</font><br /><font point-size="19">[Enterprise]</font>> + labelloc=b + color="#444444" + fontcolor="#444444" + fillcolor="#ffffff" + + 12 [id=12,shape=rect, label=<<font point-size="32">Customer Service<br />Staff</font><br /><font point-size="17">[Person]</font><br /><br /><font point-size="22">Customer service staff within<br />the bank.</font>>, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] + 15 [id=15,shape=rect, label=<<font point-size="32">Back Office Staff</font><br /><font point-size="17">[Person]</font><br /><br /><font point-size="22">Administration and support<br />staff within the bank.</font>>, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] + 2 [id=2,shape=rect, label=<<font point-size="34">Internet Banking<br />System</font><br /><font point-size="19">[Software System]</font><br /><br /><font point-size="24">Allows customers to view<br />information about their bank<br />accounts, and make payments.</font>>, style=filled, color="#0b4884", fillcolor="#1168bd", fontcolor="#ffffff"] + 4 [id=4,shape=rect, label=<<font point-size="34">Mainframe Banking<br />System</font><br /><font point-size="19">[Software System]</font><br /><br /><font point-size="24">Stores all of the core banking<br />information about customers,<br />accounts, transactions, etc.</font>>, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] + 6 [id=6,shape=rect, label=<<font point-size="34">E-mail System</font><br /><font point-size="19">[Software System]</font><br /><br /><font point-size="24">The internal Microsoft<br />Exchange e-mail system.</font>>, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] + 9 [id=9,shape=rect, label=<<font point-size="34">ATM</font><br /><font point-size="19">[Software System]</font><br /><br /><font point-size="24">Allows customers to withdraw<br />cash.</font>>, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] + } + + 1 [id=1,shape=rect, label=<<font point-size="32">Personal Banking<br />Customer</font><br /><font point-size="17">[Person]</font><br /><br /><font point-size="22">A customer of the bank, with<br />personal bank accounts.</font>>, style=filled, color="#052e56", fillcolor="#08427b", fontcolor="#ffffff"] + + 9 -> 4 [id=10, label=<<font point-size="24">Uses</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 1 -> 9 [id=11, label=<<font point-size="24">Withdraws cash using</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 12 -> 4 [id=13, label=<<font point-size="24">Uses</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 1 -> 12 [id=14, label=<<font point-size="24">Asks questions to</font><br /><font point-size="19">[Telephone]</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 15 -> 4 [id=16, label=<<font point-size="24">Uses</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 1 -> 2 [id=3, label=<<font point-size="24">Views account balances,<br />and makes payments using</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 2 -> 4 [id=5, label=<<font point-size="24">Gets account information<br />from, and makes payments<br />using</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 2 -> 6 [id=7, label=<<font point-size="24">Sends e-mail using</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 6 -> 1 [id=8, label=<<font point-size="24">Sends e-mails to</font>>, style="dashed", color="#707070", fontcolor="#707070"] +} \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/54915-AmazonWebServicesDeployment.dot b/structurizr-export/src/test/java/com/structurizr/export/dot/54915-AmazonWebServicesDeployment.dot new file mode 100644 index 000000000..9c493cabe --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/dot/54915-AmazonWebServicesDeployment.dot @@ -0,0 +1,75 @@ +digraph { + compound=true + graph [fontname="Arial", rankdir=LR, ranksep=1.0, nodesep=1.0] + node [fontname="Arial", shape=box, margin="0.4,0.3"] + edge [fontname="Arial"] + label=<<br /><font point-size="34">Spring PetClinic - Deployment - Live</font>> + + subgraph cluster_5 { + margin=25 + label=<<font point-size="24">Amazon Web Services</font><br /><font point-size="19">[Deployment Node]</font>> + labelloc=b + color="#232f3e" + fontcolor="#232f3e" + fillcolor="#ffffff" + + subgraph cluster_6 { + margin=25 + label=<<font point-size="24">US-East-1</font><br /><font point-size="19">[Deployment Node]</font>> + labelloc=b + color="#147eba" + fontcolor="#147eba" + fillcolor="#ffffff" + + subgraph cluster_12 { + margin=25 + label=<<font point-size="24">Amazon RDS</font><br /><font point-size="19">[Deployment Node]</font>> + labelloc=b + color="#3b48cc" + fontcolor="#3b48cc" + fillcolor="#ffffff" + + subgraph cluster_13 { + margin=25 + label=<<font point-size="24">MySQL</font><br /><font point-size="19">[Deployment Node]</font>> + labelloc=b + color="#3b48cc" + fontcolor="#3b48cc" + fillcolor="#ffffff" + + 14 [id=14,shape=cylinder, label=<<font point-size="34">Database</font><br /><font point-size="19">[Container: Relational database schema]</font><br /><br /><font point-size="24">Stores information regarding<br />the veterinarians, the<br />clients, and their pets.</font>>, style=filled, color="#b2b2b2", fillcolor="#ffffff", fontcolor="#000000"] + } + + } + + 7 [id=7,shape=rect, label=<<font point-size="34">Route 53</font><br /><font point-size="19">[Infrastructure Node]</font><br /><br /><font point-size="24">Highly available and scalable<br />cloud DNS service.</font>>, style=filled, color="#693cc5", fillcolor="#ffffff", fontcolor="#693cc5"] + 8 [id=8,shape=rect, label=<<font point-size="34">Elastic Load Balancer</font><br /><font point-size="19">[Infrastructure Node]</font><br /><br /><font point-size="24">Automatically distributes<br />incoming application traffic.</font>>, style=filled, color="#693cc5", fillcolor="#ffffff", fontcolor="#693cc5"] + subgraph cluster_9 { + margin=25 + label=<<font point-size="24">Autoscaling group</font><br /><font point-size="19">[Deployment Node]</font>> + labelloc=b + color="#cc2264" + fontcolor="#cc2264" + fillcolor="#ffffff" + + subgraph cluster_10 { + margin=25 + label=<<font point-size="24">Amazon EC2</font><br /><font point-size="19">[Deployment Node]</font>> + labelloc=b + color="#d86613" + fontcolor="#d86613" + fillcolor="#ffffff" + + 11 [id=11,shape=rect, label=<<font point-size="34">Web Application</font><br /><font point-size="19">[Container: Java and Spring Boot]</font><br /><br /><font point-size="24">Allows employees to view and<br />manage information regarding<br />the veterinarians, the<br />clients, and their pets.</font>>, style=filled, color="#b2b2b2", fillcolor="#ffffff", fontcolor="#000000"] + } + + } + + } + + } + + 11 -> 14 [id=15, label=<<font point-size="24">Reads from and writes to</font><br /><font point-size="19">[MySQL Protocol/SSL]</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 7 -> 8 [id=16, label=<<font point-size="24">Forwards requests to</font><br /><font point-size="19">[HTTPS]</font>>, style="dashed", color="#707070", fontcolor="#707070"] + 8 -> 11 [id=17, label=<<font point-size="24">Forwards requests to</font><br /><font point-size="19">[HTTPS]</font>>, style="dashed", color="#707070", fontcolor="#707070"] +} \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/DOTDiagramExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/dot/DOTDiagramExporterTests.java new file mode 100644 index 000000000..39f175254 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/dot/DOTDiagramExporterTests.java @@ -0,0 +1,425 @@ +package com.structurizr.export.dot; + +import com.structurizr.Workspace; +import com.structurizr.export.AbstractExporterTests; +import com.structurizr.export.Diagram; +import com.structurizr.model.*; +import com.structurizr.util.WorkspaceUtils; +import com.structurizr.view.*; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.util.Collection; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class DOTDiagramExporterTests extends AbstractExporterTests { + + @Test + public void test_BigBankPlcExample() throws Exception { + Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/structurizr-36141-workspace.json")); + DOTExporter dotWriter = new DOTExporter(); + + Collection<Diagram> diagrams = dotWriter.export(workspace); + assertEquals(7, diagrams.size()); + + Diagram diagram = diagrams.stream().filter(md -> md.getKey().equals("SystemLandscape")).findFirst().get(); + String expected = readFile(new File("./src/test/java/com/structurizr/export/dot/36141-SystemLandscape.dot")); + assertEquals(expected, diagram.getDefinition()); + + diagram = diagrams.stream().filter(md -> md.getKey().equals("SystemContext")).findFirst().get(); + expected = readFile(new File("./src/test/java/com/structurizr/export/dot/36141-SystemContext.dot")); + assertEquals(expected, diagram.getDefinition()); + + diagram = diagrams.stream().filter(md -> md.getKey().equals("Containers")).findFirst().get(); + expected = readFile(new File("./src/test/java/com/structurizr/export/dot/36141-Containers.dot")); + assertEquals(expected, diagram.getDefinition()); + + diagram = diagrams.stream().filter(md -> md.getKey().equals("Components")).findFirst().get(); + expected = readFile(new File("./src/test/java/com/structurizr/export/dot/36141-Components.dot")); + assertEquals(expected, diagram.getDefinition()); + + diagram = diagrams.stream().filter(md -> md.getKey().equals("SignIn")).findFirst().get(); + expected = readFile(new File("./src/test/java/com/structurizr/export/dot/36141-SignIn.dot")); + assertEquals(expected, diagram.getDefinition()); + + diagram = diagrams.stream().filter(md -> md.getKey().equals("DevelopmentDeployment")).findFirst().get(); + expected = readFile(new File("./src/test/java/com/structurizr/export/dot/36141-DevelopmentDeployment.dot")); + assertEquals(expected, diagram.getDefinition()); + + diagram = diagrams.stream().filter(md -> md.getKey().equals("LiveDeployment")).findFirst().get(); + expected = readFile(new File("./src/test/java/com/structurizr/export/dot/36141-LiveDeployment.dot")); + assertEquals(expected, diagram.getDefinition()); + } + + @Test + public void test_AmazonWebServicesExample() throws Exception { + Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/structurizr-54915-workspace.json")); + ThemeUtils.loadThemes(workspace); + workspace.getViews().getDeploymentViews().iterator().next().enableAutomaticLayout(AutomaticLayout.RankDirection.LeftRight, 300, 300); + + DOTExporter exporter = new DOTExporter(); + Collection<Diagram> diagrams = exporter.export(workspace); + assertEquals(1, diagrams.size()); + + Diagram diagram = diagrams.stream().findFirst().get(); + String expected = readFile(new File("./src/test/java/com/structurizr/export/dot/54915-AmazonWebServicesDeployment.dot")); + assertEquals(expected, diagram.getDefinition()); + } + + @Test + public void test_GroupsExample() throws Exception { + Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/groups.json")); + ThemeUtils.loadThemes(workspace); + + DOTExporter exporter = new DOTExporter(); + Collection<Diagram> diagrams = exporter.export(workspace); + assertEquals(3, diagrams.size()); + + Diagram diagram = diagrams.stream().filter(md -> md.getKey().equals("SystemLandscape")).findFirst().get(); + String expected = readFile(new File("./src/test/java/com/structurizr/export/dot/groups-SystemLandscape.dot")); + assertEquals(expected, diagram.getDefinition()); + + diagram = diagrams.stream().filter(md -> md.getKey().equals("Containers")).findFirst().get(); + expected = readFile(new File("./src/test/java/com/structurizr/export/dot/groups-Containers.dot")); + assertEquals(expected, diagram.getDefinition()); + + diagram = diagrams.stream().filter(md -> md.getKey().equals("Components")).findFirst().get(); + expected = readFile(new File("./src/test/java/com/structurizr/export/dot/groups-Components.dot")); + assertEquals(expected, diagram.getDefinition()); + } + + @Test + public void test_NestedGroupsExample() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getModel().addProperty("structurizr.groupSeparator", "/"); + + SoftwareSystem a = workspace.getModel().addSoftwareSystem("Team 1"); + a.setGroup("Organisation 1/Department 1/Team 1"); + + SoftwareSystem b = workspace.getModel().addSoftwareSystem("Team 2"); + b.setGroup("Organisation 1/Department 1/Team 2"); + + SoftwareSystem c = workspace.getModel().addSoftwareSystem("Organisation 1"); + c.setGroup("Organisation 1"); + + SoftwareSystem d = workspace.getModel().addSoftwareSystem("Organisation 2"); + d.setGroup("Organisation 2"); + + SoftwareSystem e = workspace.getModel().addSoftwareSystem("Department 1"); + e.setGroup("Organisation 1/Department 1"); + + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("SystemLandscape", "Description"); + view.addAllElements(); + + DOTExporter exporter = new DOTExporter(); + Collection<Diagram> diagrams = exporter.export(workspace); + + Diagram diagram = diagrams.stream().filter(md -> md.getKey().equals("SystemLandscape")).findFirst().get(); + String expected = readFile(new File("./src/test/java/com/structurizr/export/dot/nested-groups.dot")); + assertEquals(expected, diagram.getDefinition()); + } + + @Test + public void test_renderContainerDiagramWithExternalContainers() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystem1 = workspace.getModel().addSoftwareSystem("Software System 1"); + Container container1 = softwareSystem1.addContainer("Container 1"); + SoftwareSystem softwareSystem2 = workspace.getModel().addSoftwareSystem("Software System 2"); + Container container2 = softwareSystem2.addContainer("Container 2"); + + container1.uses(container2, "Uses"); + + ContainerView containerView = workspace.getViews().createContainerView(softwareSystem1, "Containers", ""); + containerView.add(container1); + containerView.add(container2); + + Diagram diagram = new DOTExporter().export(containerView); + assertEquals("digraph {\n" + + " compound=true\n" + + " graph [fontname=\"Arial\", rankdir=TB, ranksep=1.0, nodesep=1.0]\n" + + " node [fontname=\"Arial\", shape=box, margin=\"0.4,0.3\"]\n" + + " edge [fontname=\"Arial\"]\n" + + " label=<<br /><font point-size=\"34\">Software System 1 - Containers</font>>\n" + + "\n" + + " subgraph cluster_1 {\n" + + " margin=25\n" + + " label=<<font point-size=\"24\"><br />Software System 1</font><br /><font point-size=\"19\">[Software System]</font>>\n" + + " labelloc=b\n" + + " color=\"#444444\"\n" + + " fontcolor=\"#444444\"\n" + + " fillcolor=\"#444444\"\n" + + "\n" + + " 2 [id=2,shape=rect, label=<<font point-size=\"34\">Container 1</font><br /><font point-size=\"19\">[Container]</font>>, style=filled, color=\"#9a9a9a\", fillcolor=\"#dddddd\", fontcolor=\"#000000\"]\n" + + " }\n" + + "\n" + + " subgraph cluster_3 {\n" + + " margin=25\n" + + " label=<<font point-size=\"24\"><br />Software System 2</font><br /><font point-size=\"19\">[Software System]</font>>\n" + + " labelloc=b\n" + + " color=\"#cccccc\"\n" + + " fontcolor=\"#cccccc\"\n" + + " fillcolor=\"#cccccc\"\n" + + "\n" + + " 4 [id=4,shape=rect, label=<<font point-size=\"34\">Container 2</font><br /><font point-size=\"19\">[Container]</font>>, style=filled, color=\"#9a9a9a\", fillcolor=\"#dddddd\", fontcolor=\"#000000\"]\n" + + " }\n" + + "\n" + + " 2 -> 4 [id=5, label=<<font point-size=\"24\">Uses</font>>, style=\"dashed\", color=\"#707070\", fontcolor=\"#707070\"]\n" + + "}", diagram.getDefinition()); + } + + @Test + public void test_renderComponentDiagramWithExternalComponents() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystem1 = workspace.getModel().addSoftwareSystem("Software System 1"); + Container container1 = softwareSystem1.addContainer("Container 1"); + Component component1 = container1.addComponent("Component 1"); + SoftwareSystem softwareSystem2 = workspace.getModel().addSoftwareSystem("Software System 2"); + Container container2 = softwareSystem2.addContainer("Container 2"); + Component component2 = container2.addComponent("Component 2"); + + component1.uses(component2, "Uses"); + + ComponentView componentView = workspace.getViews().createComponentView(container1, "Components", ""); + componentView.add(component1); + componentView.add(component2); + + Diagram diagram = new DOTExporter().export(componentView); + assertEquals("digraph {\n" + + " compound=true\n" + + " graph [fontname=\"Arial\", rankdir=TB, ranksep=1.0, nodesep=1.0]\n" + + " node [fontname=\"Arial\", shape=box, margin=\"0.4,0.3\"]\n" + + " edge [fontname=\"Arial\"]\n" + + " label=<<br /><font point-size=\"34\">Software System 1 - Container 1 - Components</font>>\n" + + "\n" + + " subgraph cluster_2 {\n" + + " margin=25\n" + + " label=<<font point-size=\"24\"><br />Container 1</font><br /><font point-size=\"19\">[Container]</font>>\n" + + " labelloc=b\n" + + " color=\"#444444\"\n" + + " fontcolor=\"#444444\"\n" + + " fillcolor=\"#444444\"\n" + + "\n" + + " 3 [id=3,shape=rect, label=<<font point-size=\"34\">Component 1</font><br /><font point-size=\"19\">[Component]</font>>, style=filled, color=\"#9a9a9a\", fillcolor=\"#dddddd\", fontcolor=\"#000000\"]\n" + + " }\n" + + "\n" + + " subgraph cluster_5 {\n" + + " margin=25\n" + + " label=<<font point-size=\"24\"><br />Container 2</font><br /><font point-size=\"19\">[Container]</font>>\n" + + " labelloc=b\n" + + " color=\"#cccccc\"\n" + + " fontcolor=\"#cccccc\"\n" + + " fillcolor=\"#cccccc\"\n" + + "\n" + + " 6 [id=6,shape=rect, label=<<font point-size=\"34\">Component 2</font><br /><font point-size=\"19\">[Component]</font>>, style=filled, color=\"#9a9a9a\", fillcolor=\"#dddddd\", fontcolor=\"#000000\"]\n" + + " }\n" + + "\n" + + " 3 -> 6 [id=7, label=<<font point-size=\"24\">Uses</font>>, style=\"dashed\", color=\"#707070\", fontcolor=\"#707070\"]\n" + + "}", diagram.getDefinition()); + } + + @Test + public void test_renderGroupStyles() { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getModel().addPerson("User 1").setGroup("Group 1"); + workspace.getModel().addPerson("User 2").setGroup("Group 2"); + workspace.getModel().addPerson("User 3").setGroup("Group 3"); + + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", ""); + view.addDefaultElements(); + + workspace.getViews().getConfiguration().getStyles().addElementStyle("Group:Group 1").color("#111111"); + workspace.getViews().getConfiguration().getStyles().addElementStyle("Group:Group 2").color("#222222"); + + DOTExporter exporter = new DOTExporter(); + Diagram diagram = exporter.export(view); + assertEquals("digraph {\n" + + " compound=true\n" + + " graph [fontname=\"Arial\", rankdir=TB, ranksep=1.0, nodesep=1.0]\n" + + " node [fontname=\"Arial\", shape=box, margin=\"0.4,0.3\"]\n" + + " edge [fontname=\"Arial\"]\n" + + " label=<<br /><font point-size=\"34\">System Landscape</font>>\n" + + "\n" + + " subgraph \"cluster_group_Group 1\" {\n" + + " margin=25\n" + + " label=<<font point-size=\"24\"><br />Group 1</font>>\n" + + " labelloc=b\n" + + " color=\"#111111\"\n" + + " fontcolor=\"#111111\"\n" + + " fillcolor=\"#ffffff\"\n" + + " style=\"dashed\"\n" + + "\n" + + " 1 [id=1,shape=rect, label=<<font point-size=\"34\">User 1</font><br /><font point-size=\"19\">[Person]</font>>, style=filled, color=\"#9a9a9a\", fillcolor=\"#dddddd\", fontcolor=\"#000000\"]\n" + + " }\n" + + "\n" + + " subgraph \"cluster_group_Group 2\" {\n" + + " margin=25\n" + + " label=<<font point-size=\"24\"><br />Group 2</font>>\n" + + " labelloc=b\n" + + " color=\"#222222\"\n" + + " fontcolor=\"#222222\"\n" + + " fillcolor=\"#ffffff\"\n" + + " style=\"dashed\"\n" + + "\n" + + " 2 [id=2,shape=rect, label=<<font point-size=\"34\">User 2</font><br /><font point-size=\"19\">[Person]</font>>, style=filled, color=\"#9a9a9a\", fillcolor=\"#dddddd\", fontcolor=\"#000000\"]\n" + + " }\n" + + "\n" + + " subgraph \"cluster_group_Group 3\" {\n" + + " margin=25\n" + + " label=<<font point-size=\"24\"><br />Group 3</font>>\n" + + " labelloc=b\n" + + " color=\"#cccccc\"\n" + + " fontcolor=\"#cccccc\"\n" + + " fillcolor=\"#ffffff\"\n" + + " style=\"dashed\"\n" + + "\n" + + " 3 [id=3,shape=rect, label=<<font point-size=\"34\">User 3</font><br /><font point-size=\"19\">[Person]</font>>, style=filled, color=\"#9a9a9a\", fillcolor=\"#dddddd\", fontcolor=\"#000000\"]\n" + + " }\n" + + "\n" + + "\n" + + "}", diagram.getDefinition()); + + workspace.getViews().getConfiguration().getStyles().addElementStyle("Group").color("#aabbcc"); + + diagram = exporter.export(view); + assertEquals("digraph {\n" + + " compound=true\n" + + " graph [fontname=\"Arial\", rankdir=TB, ranksep=1.0, nodesep=1.0]\n" + + " node [fontname=\"Arial\", shape=box, margin=\"0.4,0.3\"]\n" + + " edge [fontname=\"Arial\"]\n" + + " label=<<br /><font point-size=\"34\">System Landscape</font>>\n" + + "\n" + + " subgraph \"cluster_group_Group 1\" {\n" + + " margin=25\n" + + " label=<<font point-size=\"24\"><br />Group 1</font>>\n" + + " labelloc=b\n" + + " color=\"#111111\"\n" + + " fontcolor=\"#111111\"\n" + + " fillcolor=\"#ffffff\"\n" + + " style=\"dashed\"\n" + + "\n" + + " 1 [id=1,shape=rect, label=<<font point-size=\"34\">User 1</font><br /><font point-size=\"19\">[Person]</font>>, style=filled, color=\"#9a9a9a\", fillcolor=\"#dddddd\", fontcolor=\"#000000\"]\n" + + " }\n" + + "\n" + + " subgraph \"cluster_group_Group 2\" {\n" + + " margin=25\n" + + " label=<<font point-size=\"24\"><br />Group 2</font>>\n" + + " labelloc=b\n" + + " color=\"#222222\"\n" + + " fontcolor=\"#222222\"\n" + + " fillcolor=\"#ffffff\"\n" + + " style=\"dashed\"\n" + + "\n" + + " 2 [id=2,shape=rect, label=<<font point-size=\"34\">User 2</font><br /><font point-size=\"19\">[Person]</font>>, style=filled, color=\"#9a9a9a\", fillcolor=\"#dddddd\", fontcolor=\"#000000\"]\n" + + " }\n" + + "\n" + + " subgraph \"cluster_group_Group 3\" {\n" + + " margin=25\n" + + " label=<<font point-size=\"24\"><br />Group 3</font>>\n" + + " labelloc=b\n" + + " color=\"#aabbcc\"\n" + + " fontcolor=\"#aabbcc\"\n" + + " fillcolor=\"#ffffff\"\n" + + " style=\"dashed\"\n" + + "\n" + + " 3 [id=3,shape=rect, label=<<font point-size=\"34\">User 3</font><br /><font point-size=\"19\">[Person]</font>>, style=filled, color=\"#9a9a9a\", fillcolor=\"#dddddd\", fontcolor=\"#000000\"]\n" + + " }\n" + + "\n" + + "\n" + + "}", diagram.getDefinition()); + } + + @Test + public void test_renderCustomView() { + Workspace workspace = new Workspace("Name", "Description"); + Model model = workspace.getModel(); + + CustomElement a = model.addCustomElement("A"); + CustomElement b = model.addCustomElement("B", "Custom", "Description"); + a.uses(b, "Uses"); + + CustomView view = workspace.getViews().createCustomView("key", "Title", "Description"); + view.addDefaultElements(); + + Diagram diagram = new DOTExporter().export(view); + assertEquals("digraph {\n" + + " compound=true\n" + + " graph [fontname=\"Arial\", rankdir=TB, ranksep=1.0, nodesep=1.0]\n" + + " node [fontname=\"Arial\", shape=box, margin=\"0.4,0.3\"]\n" + + " edge [fontname=\"Arial\"]\n" + + " label=<<br /><font point-size=\"34\">Title</font><br /><font point-size=\"24\">Description</font>>\n" + + "\n" + + " 1 [id=1,shape=rect, label=<<font point-size=\"34\">A</font>>, style=filled, color=\"#9a9a9a\", fillcolor=\"#dddddd\", fontcolor=\"#000000\"]\n" + + " 2 [id=2,shape=rect, label=<<font point-size=\"34\">B</font><br /><font point-size=\"19\">[Custom]</font><br /><br /><font point-size=\"24\">Description</font>>, style=filled, color=\"#9a9a9a\", fillcolor=\"#dddddd\", fontcolor=\"#000000\"]\n" + + "\n" + + " 1 -> 2 [id=3, label=<<font point-size=\"24\">Uses</font>>, style=\"dashed\", color=\"#707070\", fontcolor=\"#707070\"]\n" + + "}", diagram.getDefinition()); + } + + @Test + public void test_writeContainerViewWithGroupedElements_WithAndWithoutAGroupSeparator() { + Workspace workspace = new Workspace("Name", ""); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", ""); + Container container1 = softwareSystem.addContainer("Container 1"); + container1.setGroup("Group 1"); + Container container2 = softwareSystem.addContainer("Container 2"); + container2.setGroup("Group 2"); + + ContainerView view = workspace.getViews().createContainerView(softwareSystem, "Containers", ""); + view.addAllElements(); + + String expectedResult = "digraph {\n" + + " compound=true\n" + + " graph [fontname=\"Arial\", rankdir=TB, ranksep=1.0, nodesep=1.0]\n" + + " node [fontname=\"Arial\", shape=box, margin=\"0.4,0.3\"]\n" + + " edge [fontname=\"Arial\"]\n" + + " label=<<br /><font point-size=\"34\">Software System - Containers</font>>\n" + + "\n" + + " subgraph cluster_1 {\n" + + " margin=25\n" + + " label=<<font point-size=\"24\"><br />Software System</font><br /><font point-size=\"19\">[Software System]</font>>\n" + + " labelloc=b\n" + + " color=\"#444444\"\n" + + " fontcolor=\"#444444\"\n" + + " fillcolor=\"#444444\"\n" + + "\n" + + " subgraph \"cluster_group_Group 1\" {\n" + + " margin=25\n" + + " label=<<font point-size=\"24\"><br />Group 1</font>>\n" + + " labelloc=b\n" + + " color=\"#cccccc\"\n" + + " fontcolor=\"#cccccc\"\n" + + " fillcolor=\"#ffffff\"\n" + + " style=\"dashed\"\n" + + "\n" + + " 2 [id=2,shape=rect, label=<<font point-size=\"34\">Container 1</font><br /><font point-size=\"19\">[Container]</font>>, style=filled, color=\"#9a9a9a\", fillcolor=\"#dddddd\", fontcolor=\"#000000\"]\n" + + " }\n" + + "\n" + + " subgraph \"cluster_group_Group 2\" {\n" + + " margin=25\n" + + " label=<<font point-size=\"24\"><br />Group 2</font>>\n" + + " labelloc=b\n" + + " color=\"#cccccc\"\n" + + " fontcolor=\"#cccccc\"\n" + + " fillcolor=\"#ffffff\"\n" + + " style=\"dashed\"\n" + + "\n" + + " 3 [id=3,shape=rect, label=<<font point-size=\"34\">Container 2</font><br /><font point-size=\"19\">[Container]</font>>, style=filled, color=\"#9a9a9a\", fillcolor=\"#dddddd\", fontcolor=\"#000000\"]\n" + + " }\n" + + "\n" + + " }\n" + + "\n" + + "}"; + + DOTExporter exporter = new DOTExporter(); + Diagram diagram = exporter.export(view); + assertEquals(expectedResult, diagram.getDefinition()); + + // this should be the same + workspace.getModel().addProperty("structurizr.groupSeparator", "/"); + exporter = new DOTExporter(); + diagram = exporter.export(view); + assertEquals(expectedResult, diagram.getDefinition()); + } + +} \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/groups-Components.dot b/structurizr-export/src/test/java/com/structurizr/export/dot/groups-Components.dot new file mode 100644 index 000000000..efa20b99e --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/dot/groups-Components.dot @@ -0,0 +1,35 @@ +digraph { + compound=true + graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] + node [fontname="Arial", shape=box, margin="0.4,0.3"] + edge [fontname="Arial"] + label=<<br /><font point-size="34">D - F - Components</font>> + + 3 [id=3,shape=rect, label=<<font point-size="34">C</font><br /><font point-size="19">[Software System]</font>>, style=filled, color="#9a9a9a", fillcolor="#dddddd", fontcolor="#000000"] + + subgraph cluster_6 { + margin=25 + label=<<font point-size="24"><br />F</font><br /><font point-size="19">[Container]</font>> + labelloc=b + color="#444444" + fontcolor="#444444" + fillcolor="#444444" + + subgraph "cluster_group_Group 4" { + margin=25 + label=<<font point-size="24"><br />Group 4</font>> + labelloc=b + color="#cccccc" + fontcolor="#cccccc" + fillcolor="#ffffff" + style="dashed" + + 8 [id=8,shape=rect, label=<<font point-size="34">H</font><br /><font point-size="19">[Component]</font>>, style=filled, color="#9a9a9a", fillcolor="#dddddd", fontcolor="#000000"] + } + + 7 [id=7,shape=rect, label=<<font point-size="34">G</font><br /><font point-size="19">[Component]</font>>, style=filled, color="#9a9a9a", fillcolor="#dddddd", fontcolor="#000000"] + } + + 3 -> 7 [id=13, label=<>, style="dashed", color="#707070", fontcolor="#707070"] + 3 -> 8 [id=15, label=<>, style="dashed", color="#707070", fontcolor="#707070"] +} \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/groups-Containers.dot b/structurizr-export/src/test/java/com/structurizr/export/dot/groups-Containers.dot new file mode 100644 index 000000000..fcddeeed4 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/dot/groups-Containers.dot @@ -0,0 +1,35 @@ +digraph { + compound=true + graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] + node [fontname="Arial", shape=box, margin="0.4,0.3"] + edge [fontname="Arial"] + label=<<br /><font point-size="34">D - Containers</font>> + + 3 [id=3,shape=rect, label=<<font point-size="34">C</font><br /><font point-size="19">[Software System]</font>>, style=filled, color="#9a9a9a", fillcolor="#dddddd", fontcolor="#000000"] + + subgraph cluster_4 { + margin=25 + label=<<font point-size="24"><br />D</font><br /><font point-size="19">[Software System]</font>> + labelloc=b + color="#444444" + fontcolor="#444444" + fillcolor="#444444" + + subgraph "cluster_group_Group 3" { + margin=25 + label=<<font point-size="24"><br />Group 3</font>> + labelloc=b + color="#cccccc" + fontcolor="#cccccc" + fillcolor="#ffffff" + style="dashed" + + 6 [id=6,shape=rect, label=<<font point-size="34">F</font><br /><font point-size="19">[Container]</font>>, style=filled, color="#9a9a9a", fillcolor="#dddddd", fontcolor="#000000"] + } + + 5 [id=5,shape=rect, label=<<font point-size="34">E</font><br /><font point-size="19">[Container]</font>>, style=filled, color="#9a9a9a", fillcolor="#dddddd", fontcolor="#000000"] + } + + 3 -> 5 [id=11, label=<>, style="dashed", color="#707070", fontcolor="#707070"] + 3 -> 6 [id=14, label=<>, style="dashed", color="#707070", fontcolor="#707070"] +} \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/groups-SystemLandscape.dot b/structurizr-export/src/test/java/com/structurizr/export/dot/groups-SystemLandscape.dot new file mode 100644 index 000000000..5daee1e84 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/dot/groups-SystemLandscape.dot @@ -0,0 +1,48 @@ +digraph { + compound=true + graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] + node [fontname="Arial", shape=box, margin="0.4,0.3"] + edge [fontname="Arial"] + label=<<br /><font point-size="34">System Landscape</font>> + + subgraph cluster_enterprise { + margin=25 + label=<<font point-size="24"><br />Enterprise</font><br /><font point-size="19">[Enterprise]</font>> + labelloc=b + color="#444444" + fontcolor="#444444" + fillcolor="#ffffff" + + subgraph "cluster_group_Group 2" { + margin=25 + label=<<font point-size="24"><br />Group 2</font>> + labelloc=b + color="#cccccc" + fontcolor="#cccccc" + fillcolor="#ffffff" + style="dashed" + + 4 [id=4,shape=rect, label=<<font point-size="34">D</font><br /><font point-size="19">[Software System]</font>>, style=filled, color="#9a9a9a", fillcolor="#dddddd", fontcolor="#000000"] + } + + 3 [id=3,shape=rect, label=<<font point-size="34">C</font><br /><font point-size="19">[Software System]</font>>, style=filled, color="#9a9a9a", fillcolor="#dddddd", fontcolor="#000000"] + } + + subgraph "cluster_group_Group 1" { + margin=25 + label=<<font point-size="24"><br />Group 1</font>> + labelloc=b + color="#cccccc" + fontcolor="#cccccc" + fillcolor="#ffffff" + style="dashed" + + 2 [id=2,shape=rect, label=<<font point-size="34">B</font><br /><font point-size="19">[Software System]</font>>, style=filled, color="#9a9a9a", fillcolor="#dddddd", fontcolor="#000000"] + } + + 1 [id=1,shape=rect, label=<<font point-size="34">A</font><br /><font point-size="19">[Software System]</font>>, style=filled, color="#9a9a9a", fillcolor="#dddddd", fontcolor="#000000"] + + 2 -> 3 [id=10, label=<>, style="dashed", color="#707070", fontcolor="#707070"] + 3 -> 4 [id=12, label=<>, style="dashed", color="#707070", fontcolor="#707070"] + 1 -> 2 [id=9, label=<>, style="dashed", color="#707070", fontcolor="#707070"] +} \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/nested-groups.dot b/structurizr-export/src/test/java/com/structurizr/export/dot/nested-groups.dot new file mode 100644 index 000000000..702f73d43 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/dot/nested-groups.dot @@ -0,0 +1,69 @@ +digraph { + compound=true + graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] + node [fontname="Arial", shape=box, margin="0.4,0.3"] + edge [fontname="Arial"] + label=<<br /><font point-size="34">System Landscape</font><br /><font point-size="24">Description</font>> + + subgraph "cluster_group_Organisation 1" { + margin=25 + label=<<font point-size="24"><br />Organisation 1</font>> + labelloc=b + color="#cccccc" + fontcolor="#cccccc" + fillcolor="#ffffff" + style="dashed" + + 3 [id=3,shape=rect, label=<<font point-size="34">Organisation 1</font><br /><font point-size="19">[Software System]</font>>, style=filled, color="#9a9a9a", fillcolor="#dddddd", fontcolor="#000000"] + subgraph "cluster_group_Department 1" { + margin=25 + label=<<font point-size="24"><br />Department 1</font>> + labelloc=b + color="#cccccc" + fontcolor="#cccccc" + fillcolor="#ffffff" + style="dashed" + + 5 [id=5,shape=rect, label=<<font point-size="34">Department 1</font><br /><font point-size="19">[Software System]</font>>, style=filled, color="#9a9a9a", fillcolor="#dddddd", fontcolor="#000000"] + subgraph "cluster_group_Team 1" { + margin=25 + label=<<font point-size="24"><br />Team 1</font>> + labelloc=b + color="#cccccc" + fontcolor="#cccccc" + fillcolor="#ffffff" + style="dashed" + + 1 [id=1,shape=rect, label=<<font point-size="34">Team 1</font><br /><font point-size="19">[Software System]</font>>, style=filled, color="#9a9a9a", fillcolor="#dddddd", fontcolor="#000000"] + } + + subgraph "cluster_group_Team 2" { + margin=25 + label=<<font point-size="24"><br />Team 2</font>> + labelloc=b + color="#cccccc" + fontcolor="#cccccc" + fillcolor="#ffffff" + style="dashed" + + 2 [id=2,shape=rect, label=<<font point-size="34">Team 2</font><br /><font point-size="19">[Software System]</font>>, style=filled, color="#9a9a9a", fillcolor="#dddddd", fontcolor="#000000"] + } + + } + + } + + subgraph "cluster_group_Organisation 2" { + margin=25 + label=<<font point-size="24"><br />Organisation 2</font>> + labelloc=b + color="#cccccc" + fontcolor="#cccccc" + fillcolor="#ffffff" + style="dashed" + + 4 [id=4,shape=rect, label=<<font point-size="34">Organisation 2</font><br /><font point-size="19">[Software System]</font>>, style=filled, color="#9a9a9a", fillcolor="#dddddd", fontcolor="#000000"] + } + + +} \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/ilograph/36141.ilograph b/structurizr-export/src/test/java/com/structurizr/export/ilograph/36141.ilograph new file mode 100644 index 000000000..2452b8dbc --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/ilograph/36141.ilograph @@ -0,0 +1,631 @@ +resources: + + - id: "1" + name: "Personal Banking Customer" + subtitle: "[Person]" + description: "A customer of the bank, with personal bank accounts." + backgroundColor: "#08427b" + color: "#ffffff" + + - id: "12" + name: "Customer Service Staff" + subtitle: "[Person]" + description: "Customer service staff within the bank." + backgroundColor: "#999999" + color: "#ffffff" + + - id: "15" + name: "Back Office Staff" + subtitle: "[Person]" + description: "Administration and support staff within the bank." + backgroundColor: "#999999" + color: "#ffffff" + + - id: "2" + name: "Internet Banking System" + subtitle: "[Software System]" + description: "Allows customers to view information about their bank accounts, and make payments." + backgroundColor: "#1168bd" + color: "#ffffff" + + children: + - id: "17" + name: "Single-Page Application" + subtitle: "[Container: JavaScript and Angular]" + description: "Provides all of the Internet banking functionality to customers via their web browser." + backgroundColor: "#438dd5" + color: "#ffffff" + + - id: "18" + name: "Mobile App" + subtitle: "[Container: Xamarin]" + description: "Provides a limited subset of the Internet banking functionality to customers via their mobile device." + backgroundColor: "#438dd5" + color: "#ffffff" + + - id: "19" + name: "Web Application" + subtitle: "[Container: Java and Spring MVC]" + description: "Delivers the static content and the Internet banking single page application." + backgroundColor: "#438dd5" + color: "#ffffff" + + - id: "20" + name: "API Application" + subtitle: "[Container: Java and Spring MVC]" + description: "Provides Internet banking functionality via a JSON/HTTPS API." + backgroundColor: "#438dd5" + color: "#ffffff" + + children: + - id: "29" + name: "Sign In Controller" + subtitle: "[Component: Spring MVC Rest Controller]" + description: "Allows users to sign in to the Internet Banking System." + backgroundColor: "#85bbf0" + color: "#000000" + + - id: "30" + name: "Accounts Summary Controller" + subtitle: "[Component: Spring MVC Rest Controller]" + description: "Provides customers with a summary of their bank accounts." + backgroundColor: "#85bbf0" + color: "#000000" + + - id: "31" + name: "Reset Password Controller" + subtitle: "[Component: Spring MVC Rest Controller]" + description: "Allows users to reset their passwords with a single use URL." + backgroundColor: "#85bbf0" + color: "#000000" + + - id: "32" + name: "Security Component" + subtitle: "[Component: Spring Bean]" + description: "Provides functionality related to signing in, changing passwords, etc." + backgroundColor: "#85bbf0" + color: "#000000" + + - id: "33" + name: "Mainframe Banking System Facade" + subtitle: "[Component: Spring Bean]" + description: "A facade onto the mainframe banking system." + backgroundColor: "#85bbf0" + color: "#000000" + + - id: "34" + name: "E-mail Component" + subtitle: "[Component: Spring Bean]" + description: "Sends e-mails to users." + backgroundColor: "#85bbf0" + color: "#000000" + + - id: "21" + name: "Database" + subtitle: "[Container: Oracle Database Schema]" + description: "Stores user registration information, hashed authentication credentials, access logs, etc." + backgroundColor: "#438dd5" + color: "#ffffff" + + - id: "4" + name: "Mainframe Banking System" + subtitle: "[Software System]" + description: "Stores all of the core banking information about customers, accounts, transactions, etc." + backgroundColor: "#999999" + color: "#ffffff" + + - id: "6" + name: "E-mail System" + subtitle: "[Software System]" + description: "The internal Microsoft Exchange e-mail system." + backgroundColor: "#999999" + color: "#ffffff" + + - id: "9" + name: "ATM" + subtitle: "[Software System]" + description: "Allows customers to withdraw cash." + backgroundColor: "#999999" + color: "#ffffff" + + - id: "50" + name: "Developer Laptop" + subtitle: "[Deployment Node: Microsoft Windows 10 or Apple macOS]" + description: "A developer laptop." + backgroundColor: "#ffffff" + color: "#000000" + + children: + - id: "51" + name: "Docker Container - Web Server" + subtitle: "[Deployment Node: Docker]" + description: "A Docker container." + backgroundColor: "#ffffff" + color: "#000000" + + children: + - id: "52" + name: "Apache Tomcat" + subtitle: "[Deployment Node: Apache Tomcat 8.x]" + description: "An open source Java EE web server." + backgroundColor: "#ffffff" + color: "#000000" + + children: + - id: "53" + name: "Web Application" + subtitle: "[Container: Java and Spring MVC]" + description: "Delivers the static content and the Internet banking single page application." + backgroundColor: "#438dd5" + color: "#ffffff" + + - id: "54" + name: "API Application" + subtitle: "[Container: Java and Spring MVC]" + description: "Provides Internet banking functionality via a JSON/HTTPS API." + backgroundColor: "#438dd5" + color: "#ffffff" + + - id: "59" + name: "Docker Container - Database Server" + subtitle: "[Deployment Node: Docker]" + description: "A Docker container." + backgroundColor: "#ffffff" + color: "#000000" + + children: + - id: "60" + name: "Database Server" + subtitle: "[Deployment Node: Oracle 12c]" + description: "A development database." + backgroundColor: "#ffffff" + color: "#000000" + + children: + - id: "61" + name: "Database" + subtitle: "[Container: Oracle Database Schema]" + description: "Stores user registration information, hashed authentication credentials, access logs, etc." + backgroundColor: "#438dd5" + color: "#ffffff" + + - id: "63" + name: "Web Browser" + subtitle: "[Deployment Node: Chrome, Firefox, Safari, or Edge]" + backgroundColor: "#ffffff" + color: "#000000" + + children: + - id: "64" + name: "Single-Page Application" + subtitle: "[Container: JavaScript and Angular]" + description: "Provides all of the Internet banking functionality to customers via their web browser." + backgroundColor: "#438dd5" + color: "#ffffff" + + - id: "55" + name: "Big Bank plc" + subtitle: "[Deployment Node: Big Bank plc data center]" + backgroundColor: "#ffffff" + color: "#000000" + + children: + - id: "56" + name: "bigbank-dev001" + subtitle: "[Deployment Node]" + backgroundColor: "#ffffff" + color: "#000000" + + children: + - id: "57" + name: "Mainframe Banking System" + subtitle: "[Software System]" + description: "Stores all of the core banking information about customers, accounts, transactions, etc." + backgroundColor: "#999999" + color: "#ffffff" + + - id: "67" + name: "Customer's mobile device" + subtitle: "[Deployment Node: Apple iOS or Android]" + backgroundColor: "#ffffff" + color: "#000000" + + children: + - id: "68" + name: "Mobile App" + subtitle: "[Container: Xamarin]" + description: "Provides a limited subset of the Internet banking functionality to customers via their mobile device." + backgroundColor: "#438dd5" + color: "#ffffff" + + - id: "69" + name: "Customer's computer" + subtitle: "[Deployment Node: Microsoft Windows or Apple macOS]" + backgroundColor: "#ffffff" + color: "#000000" + + children: + - id: "70" + name: "Web Browser" + subtitle: "[Deployment Node: Chrome, Firefox, Safari, or Edge]" + backgroundColor: "#ffffff" + color: "#000000" + + children: + - id: "71" + name: "Single-Page Application" + subtitle: "[Container: JavaScript and Angular]" + description: "Provides all of the Internet banking functionality to customers via their web browser." + backgroundColor: "#438dd5" + color: "#ffffff" + + - id: "72" + name: "Big Bank plc" + subtitle: "[Deployment Node: Big Bank plc data center]" + backgroundColor: "#ffffff" + color: "#000000" + + children: + - id: "73" + name: "bigbank-prod001" + subtitle: "[Deployment Node]" + backgroundColor: "#ffffff" + color: "#000000" + + children: + - id: "74" + name: "Mainframe Banking System" + subtitle: "[Software System]" + description: "Stores all of the core banking information about customers, accounts, transactions, etc." + backgroundColor: "#999999" + color: "#ffffff" + + - id: "75" + name: "bigbank-web***" + subtitle: "[Deployment Node: Ubuntu 16.04 LTS]" + description: "A web server residing in the web server farm, accessed via F5 BIG-IP LTMs." + backgroundColor: "#ffffff" + color: "#000000" + + children: + - id: "76" + name: "Apache Tomcat" + subtitle: "[Deployment Node: Apache Tomcat 8.x]" + description: "An open source Java EE web server." + backgroundColor: "#ffffff" + color: "#000000" + + children: + - id: "77" + name: "Web Application" + subtitle: "[Container: Java and Spring MVC]" + description: "Delivers the static content and the Internet banking single page application." + backgroundColor: "#438dd5" + color: "#ffffff" + + - id: "79" + name: "bigbank-api***" + subtitle: "[Deployment Node: Ubuntu 16.04 LTS]" + description: "A web server residing in the web server farm, accessed via F5 BIG-IP LTMs." + backgroundColor: "#ffffff" + color: "#000000" + + children: + - id: "80" + name: "Apache Tomcat" + subtitle: "[Deployment Node: Apache Tomcat 8.x]" + description: "An open source Java EE web server." + backgroundColor: "#ffffff" + color: "#000000" + + children: + - id: "81" + name: "API Application" + subtitle: "[Container: Java and Spring MVC]" + description: "Provides Internet banking functionality via a JSON/HTTPS API." + backgroundColor: "#438dd5" + color: "#ffffff" + + - id: "85" + name: "bigbank-db01" + subtitle: "[Deployment Node: Ubuntu 16.04 LTS]" + description: "The primary database server." + backgroundColor: "#ffffff" + color: "#000000" + + children: + - id: "86" + name: "Oracle - Primary" + subtitle: "[Deployment Node: Oracle 12c]" + description: "The primary, live database server." + backgroundColor: "#ffffff" + color: "#000000" + + children: + - id: "87" + name: "Database" + subtitle: "[Container: Oracle Database Schema]" + description: "Stores user registration information, hashed authentication credentials, access logs, etc." + backgroundColor: "#438dd5" + color: "#ffffff" + + - id: "89" + name: "bigbank-db02" + subtitle: "[Deployment Node: Ubuntu 16.04 LTS]" + description: "The secondary database server." + backgroundColor: "#ffffff" + color: "#000000" + + children: + - id: "90" + name: "Oracle - Secondary" + subtitle: "[Deployment Node: Oracle 12c]" + description: "A secondary, standby database server, used for failover purposes only." + backgroundColor: "#ffffff" + color: "#000000" + + children: + - id: "91" + name: "Database" + subtitle: "[Container: Oracle Database Schema]" + description: "Stores user registration information, hashed authentication credentials, access logs, etc." + backgroundColor: "#438dd5" + color: "#ffffff" + +perspectives: + - name: Static Structure + relations: + - from: "1" + to: "9" + label: "Withdraws cash using" + color: "#707070" + + - from: "1" + to: "12" + label: "Asks questions to" + description: "Telephone" + color: "#707070" + + - from: "1" + to: "2" + label: "Views account balances, and makes payments using" + color: "#707070" + + - from: "12" + to: "4" + label: "Uses" + color: "#707070" + + - from: "15" + to: "4" + label: "Uses" + color: "#707070" + + - from: "2" + to: "4" + label: "Gets account information from, and makes payments using" + color: "#707070" + + - from: "2" + to: "6" + label: "Sends e-mail using" + color: "#707070" + + - from: "6" + to: "1" + label: "Sends e-mails to" + color: "#707070" + + - from: "9" + to: "4" + label: "Uses" + color: "#707070" + + - from: "1" + to: "19" + label: "Visits bigbank.com/ib using" + description: "HTTPS" + color: "#707070" + + - from: "1" + to: "17" + label: "Views account balances, and makes payments using" + color: "#707070" + + - from: "1" + to: "18" + label: "Views account balances, and makes payments using" + color: "#707070" + + - from: "17" + to: "20" + label: "Makes API calls to" + description: "JSON/HTTPS" + color: "#707070" + + - from: "18" + to: "20" + label: "Makes API calls to" + description: "JSON/HTTPS" + color: "#707070" + + - from: "19" + to: "17" + label: "Delivers to the customer's web browser" + color: "#707070" + + - from: "20" + to: "21" + label: "Reads from and writes to" + description: "JDBC" + color: "#707070" + + - from: "20" + to: "4" + label: "Makes API calls to" + description: "XML/HTTPS" + color: "#707070" + + - from: "20" + to: "6" + label: "Sends e-mail using" + description: "SMTP" + color: "#707070" + + - from: "17" + to: "29" + label: "Makes API calls to" + description: "JSON/HTTPS" + color: "#707070" + + - from: "17" + to: "31" + label: "Makes API calls to" + description: "JSON/HTTPS" + color: "#707070" + + - from: "17" + to: "30" + label: "Makes API calls to" + description: "JSON/HTTPS" + color: "#707070" + + - from: "18" + to: "29" + label: "Makes API calls to" + description: "JSON/HTTPS" + color: "#707070" + + - from: "18" + to: "31" + label: "Makes API calls to" + description: "JSON/HTTPS" + color: "#707070" + + - from: "18" + to: "30" + label: "Makes API calls to" + description: "JSON/HTTPS" + color: "#707070" + + - from: "29" + to: "32" + label: "Uses" + color: "#707070" + + - from: "30" + to: "33" + label: "Uses" + color: "#707070" + + - from: "31" + to: "32" + label: "Uses" + color: "#707070" + + - from: "31" + to: "34" + label: "Uses" + color: "#707070" + + - from: "32" + to: "21" + label: "Reads from and writes to" + description: "JDBC" + color: "#707070" + + - from: "33" + to: "4" + label: "Uses" + description: "XML/HTTPS" + color: "#707070" + + - from: "34" + to: "6" + label: "Sends e-mail using" + color: "#707070" + + - name: Dynamic - API Application - Dynamic + sequence: + start: "17" + steps: + - to: "29" + label: "1. Submits credentials to" + description: "JSON/HTTPS" + color: "#707070" + + - to: "32" + label: "2. Validates credentials using" + color: "#707070" + + - to: "21" + label: "3. select * from users where username = ?" + description: "JDBC" + color: "#707070" + + - to: "32" + label: "4. Returns user data to" + description: "JDBC" + color: "#707070" + + - to: "29" + label: "5. Returns true if the hashed password matches" + color: "#707070" + + - to: "17" + label: "6. Sends back an authentication token to" + description: "JSON/HTTPS" + color: "#707070" + + - name: Deployment - Development + relations: + - from: "53" + to: "64" + label: "Delivers to the customer's web browser" + color: "#707070" + - from: "54" + to: "57" + label: "Makes API calls to" + description: "XML/HTTPS" + color: "#707070" + - from: "54" + to: "61" + label: "Reads from and writes to" + description: "JDBC" + color: "#707070" + - from: "64" + to: "54" + label: "Makes API calls to" + description: "JSON/HTTPS" + color: "#707070" + - name: Deployment - Live + relations: + - from: "68" + to: "81" + label: "Makes API calls to" + description: "JSON/HTTPS" + color: "#707070" + - from: "71" + to: "81" + label: "Makes API calls to" + description: "JSON/HTTPS" + color: "#707070" + - from: "77" + to: "71" + label: "Delivers to the customer's web browser" + color: "#707070" + - from: "81" + to: "74" + label: "Makes API calls to" + description: "XML/HTTPS" + color: "#707070" + - from: "81" + to: "87" + label: "Reads from and writes to" + description: "JDBC" + color: "#707070" + - from: "81" + to: "91" + label: "Reads from and writes to" + description: "JDBC" + color: "#707070" diff --git a/structurizr-export/src/test/java/com/structurizr/export/ilograph/54915.ilograph b/structurizr-export/src/test/java/com/structurizr/export/ilograph/54915.ilograph new file mode 100644 index 000000000..9689909c5 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/ilograph/54915.ilograph @@ -0,0 +1,120 @@ +resources: + + - id: "1" + name: "Spring PetClinic" + subtitle: "[Software System]" + description: "Allows employees to view and manage information regarding the veterinarians, the clients, and their pets." + backgroundColor: "#ffffff" + color: "#000000" + + children: + - id: "2" + name: "Web Application" + subtitle: "[Container: Java and Spring Boot]" + description: "Allows employees to view and manage information regarding the veterinarians, the clients, and their pets." + backgroundColor: "#ffffff" + color: "#000000" + + - id: "3" + name: "Database" + subtitle: "[Container: Relational database schema]" + description: "Stores information regarding the veterinarians, the clients, and their pets." + backgroundColor: "#ffffff" + color: "#000000" + + - id: "5" + name: "Amazon Web Services" + subtitle: "[Deployment Node]" + backgroundColor: "#ffffff" + color: "#232f3e" + + children: + - id: "6" + name: "US-East-1" + subtitle: "[Deployment Node]" + backgroundColor: "#ffffff" + color: "#147eba" + + children: + - id: "12" + name: "Amazon RDS" + subtitle: "[Deployment Node]" + backgroundColor: "#ffffff" + color: "#3b48cc" + + children: + - id: "13" + name: "MySQL" + subtitle: "[Deployment Node]" + backgroundColor: "#ffffff" + color: "#3b48cc" + + children: + - id: "14" + name: "Database" + subtitle: "[Container: Relational database schema]" + description: "Stores information regarding the veterinarians, the clients, and their pets." + backgroundColor: "#ffffff" + color: "#000000" + + - id: "9" + name: "Autoscaling group" + subtitle: "[Deployment Node]" + backgroundColor: "#ffffff" + color: "#cc2264" + + children: + - id: "10" + name: "Amazon EC2" + subtitle: "[Deployment Node]" + backgroundColor: "#ffffff" + color: "#d86613" + + children: + - id: "11" + name: "Web Application" + subtitle: "[Container: Java and Spring Boot]" + description: "Allows employees to view and manage information regarding the veterinarians, the clients, and their pets." + backgroundColor: "#ffffff" + color: "#000000" + + - id: "7" + name: "Route 53" + subtitle: "[Infrastructure Node]" + description: "Highly available and scalable cloud DNS service." + backgroundColor: "#ffffff" + color: "#693cc5" + + - id: "8" + name: "Elastic Load Balancer" + subtitle: "[Infrastructure Node]" + description: "Automatically distributes incoming application traffic." + backgroundColor: "#ffffff" + color: "#693cc5" + +perspectives: + - name: Static Structure + relations: + - from: "2" + to: "3" + label: "Reads from and writes to" + description: "MySQL Protocol/SSL" + color: "#707070" + + - name: Deployment - Live + relations: + - from: "11" + to: "14" + label: "Reads from and writes to" + description: "MySQL Protocol/SSL" + color: "#707070" + - from: "7" + to: "8" + label: "Forwards requests to" + description: "HTTPS" + color: "#707070" + - from: "8" + to: "11" + label: "Forwards requests to" + description: "HTTPS" + color: "#707070" \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/ilograph/IlographWriterTests.java b/structurizr-export/src/test/java/com/structurizr/export/ilograph/IlographWriterTests.java new file mode 100644 index 000000000..3cca4626d --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/ilograph/IlographWriterTests.java @@ -0,0 +1,76 @@ +package com.structurizr.export.ilograph; + +import com.structurizr.Workspace; +import com.structurizr.export.AbstractExporterTests; +import com.structurizr.export.Diagram; +import com.structurizr.export.WorkspaceExport; +import com.structurizr.export.dot.DOTExporter; +import com.structurizr.model.CustomElement; +import com.structurizr.model.Model; +import com.structurizr.util.WorkspaceUtils; +import com.structurizr.view.CustomView; +import com.structurizr.view.ThemeUtils; +import org.junit.jupiter.api.Test; + +import java.io.File; + +import static org.junit.jupiter.api.Assertions.*; + +public class IlographWriterTests extends AbstractExporterTests { + + @Test + public void test_BigBankPlcExample() throws Exception { + Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/structurizr-36141-workspace.json")); + IlographExporter ilographExporter = new IlographExporter(); + WorkspaceExport export = ilographExporter.export(workspace); + + String expected = readFile(new File("./src/test/java/com/structurizr/export/ilograph/36141.ilograph")); + assertEquals(expected, export.getDefinition()); + } + + @Test + public void test_AmazonWebServicesExample() throws Exception { + Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/structurizr-54915-workspace.json")); + ThemeUtils.loadThemes(workspace); + IlographExporter ilographExporter = new IlographExporter(); + WorkspaceExport export = ilographExporter.export(workspace); + + String expected = readFile(new File("./src/test/java/com/structurizr/export/ilograph/54915.ilograph")); + assertEquals(expected, export.getDefinition()); + } + + @Test + public void test_renderCustomElements() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + Model model = workspace.getModel(); + + CustomElement a = model.addCustomElement("A"); + CustomElement b = model.addCustomElement("B", "Custom", "Description"); + a.uses(b, "Uses"); + + WorkspaceExport export = new IlographExporter().export(workspace); + assertEquals("resources:\n" + + "\n" + + " - id: \"1\"\n" + + " name: \"A\"\n" + + " subtitle: \"\"\n" + + " backgroundColor: \"#dddddd\"\n" + + " color: \"#000000\"\n" + + "\n" + + " - id: \"2\"\n" + + " name: \"B\"\n" + + " subtitle: \"[Custom]\"\n" + + " description: \"Description\"\n" + + " backgroundColor: \"#dddddd\"\n" + + " color: \"#000000\"\n" + + "\n" + + "perspectives:\n" + + " - name: Static Structure\n" + + " relations:\n" + + " - from: \"1\"\n" + + " to: \"2\"\n" + + " label: \"Uses\"\n" + + " color: \"#707070\"\n", export.getDefinition()); + } + +} \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-Components.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-Components.mmd new file mode 100644 index 000000000..dcae0ac7f --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-Components.mmd @@ -0,0 +1,48 @@ +graph TB + linkStyle default fill:#ffffff + + subgraph diagram ["Internet Banking System - API Application - Components"] + style diagram fill:#ffffff,stroke:#ffffff + + 4["<div style='font-weight: bold'>Mainframe Banking System</div><div style='font-size: 70%; margin-top: 0px'>[Software System]</div><div style='font-size: 80%; margin-top:10px'>Stores all of the core<br />banking information about<br />customers, accounts,<br />transactions, etc.</div>"] + style 4 fill:#999999,stroke:#6b6b6b,color:#ffffff + 17["<div style='font-weight: bold'>Single-Page Application</div><div style='font-size: 70%; margin-top: 0px'>[Container: JavaScript and Angular]</div><div style='font-size: 80%; margin-top:10px'>Provides all of the Internet<br />banking functionality to<br />customers via their web<br />browser.</div>"] + style 17 fill:#438dd5,stroke:#2e6295,color:#ffffff + 6["<div style='font-weight: bold'>E-mail System</div><div style='font-size: 70%; margin-top: 0px'>[Software System]</div><div style='font-size: 80%; margin-top:10px'>The internal Microsoft<br />Exchange e-mail system.</div>"] + style 6 fill:#999999,stroke:#6b6b6b,color:#ffffff + 18["<div style='font-weight: bold'>Mobile App</div><div style='font-size: 70%; margin-top: 0px'>[Container: Xamarin]</div><div style='font-size: 80%; margin-top:10px'>Provides a limited subset of<br />the Internet banking<br />functionality to customers<br />via their mobile device.</div>"] + style 18 fill:#438dd5,stroke:#2e6295,color:#ffffff + 21[("<div style='font-weight: bold'>Database</div><div style='font-size: 70%; margin-top: 0px'>[Container: Oracle Database Schema]</div><div style='font-size: 80%; margin-top:10px'>Stores user registration<br />information, hashed<br />authentication credentials,<br />access logs, etc.</div>")] + style 21 fill:#438dd5,stroke:#2e6295,color:#ffffff + + subgraph 20 [API Application] + style 20 fill:#ffffff,stroke:#2e6295,color:#2e6295 + + 29["<div style='font-weight: bold'>Sign In Controller</div><div style='font-size: 70%; margin-top: 0px'>[Component: Spring MVC Rest Controller]</div><div style='font-size: 80%; margin-top:10px'>Allows users to sign in to<br />the Internet Banking System.</div>"] + style 29 fill:#85bbf0,stroke:#5d82a8,color:#000000 + 30["<div style='font-weight: bold'>Accounts Summary Controller</div><div style='font-size: 70%; margin-top: 0px'>[Component: Spring MVC Rest Controller]</div><div style='font-size: 80%; margin-top:10px'>Provides customers with a<br />summary of their bank<br />accounts.</div>"] + style 30 fill:#85bbf0,stroke:#5d82a8,color:#000000 + 31["<div style='font-weight: bold'>Reset Password Controller</div><div style='font-size: 70%; margin-top: 0px'>[Component: Spring MVC Rest Controller]</div><div style='font-size: 80%; margin-top:10px'>Allows users to reset their<br />passwords with a single use<br />URL.</div>"] + style 31 fill:#85bbf0,stroke:#5d82a8,color:#000000 + 32["<div style='font-weight: bold'>Security Component</div><div style='font-size: 70%; margin-top: 0px'>[Component: Spring Bean]</div><div style='font-size: 80%; margin-top:10px'>Provides functionality<br />related to signing in,<br />changing passwords, etc.</div>"] + style 32 fill:#85bbf0,stroke:#5d82a8,color:#000000 + 33["<div style='font-weight: bold'>Mainframe Banking System Facade</div><div style='font-size: 70%; margin-top: 0px'>[Component: Spring Bean]</div><div style='font-size: 80%; margin-top:10px'>A facade onto the mainframe<br />banking system.</div>"] + style 33 fill:#85bbf0,stroke:#5d82a8,color:#000000 + 34["<div style='font-weight: bold'>E-mail Component</div><div style='font-size: 70%; margin-top: 0px'>[Component: Spring Bean]</div><div style='font-size: 80%; margin-top:10px'>Sends e-mails to users.</div>"] + style 34 fill:#85bbf0,stroke:#5d82a8,color:#000000 + end + + 17-. "<div>Makes API calls to</div><div style='font-size: 70%'>[JSON/HTTPS]</div>" .->29 + 17-. "<div>Makes API calls to</div><div style='font-size: 70%'>[JSON/HTTPS]</div>" .->31 + 17-. "<div>Makes API calls to</div><div style='font-size: 70%'>[JSON/HTTPS]</div>" .->30 + 18-. "<div>Makes API calls to</div><div style='font-size: 70%'>[JSON/HTTPS]</div>" .->29 + 18-. "<div>Makes API calls to</div><div style='font-size: 70%'>[JSON/HTTPS]</div>" .->31 + 18-. "<div>Makes API calls to</div><div style='font-size: 70%'>[JSON/HTTPS]</div>" .->30 + 29-. "<div>Uses</div><div style='font-size: 70%'></div>" .->32 + 30-. "<div>Uses</div><div style='font-size: 70%'></div>" .->33 + 31-. "<div>Uses</div><div style='font-size: 70%'></div>" .->32 + 31-. "<div>Uses</div><div style='font-size: 70%'></div>" .->34 + 32-. "<div>Reads from and writes to</div><div style='font-size: 70%'>[JDBC]</div>" .->21 + 33-. "<div>Uses</div><div style='font-size: 70%'>[XML/HTTPS]</div>" .->4 + 34-. "<div>Sends e-mail using</div><div style='font-size: 70%'></div>" .->6 + end \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-Containers.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-Containers.mmd new file mode 100644 index 000000000..4cda955bd --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-Containers.mmd @@ -0,0 +1,39 @@ +graph TB + linkStyle default fill:#ffffff + + subgraph diagram ["Internet Banking System - Containers"] + style diagram fill:#ffffff,stroke:#ffffff + + 1["<div style='font-weight: bold'>Personal Banking Customer</div><div style='font-size: 70%; margin-top: 0px'>[Person]</div><div style='font-size: 80%; margin-top:10px'>A customer of the bank, with<br />personal bank accounts.</div>"] + style 1 fill:#08427b,stroke:#052e56,color:#ffffff + 4["<div style='font-weight: bold'>Mainframe Banking System</div><div style='font-size: 70%; margin-top: 0px'>[Software System]</div><div style='font-size: 80%; margin-top:10px'>Stores all of the core<br />banking information about<br />customers, accounts,<br />transactions, etc.</div>"] + style 4 fill:#999999,stroke:#6b6b6b,color:#ffffff + 6["<div style='font-weight: bold'>E-mail System</div><div style='font-size: 70%; margin-top: 0px'>[Software System]</div><div style='font-size: 80%; margin-top:10px'>The internal Microsoft<br />Exchange e-mail system.</div>"] + style 6 fill:#999999,stroke:#6b6b6b,color:#ffffff + + subgraph 2 [Internet Banking System] + style 2 fill:#ffffff,stroke:#0b4884,color:#0b4884 + + 17["<div style='font-weight: bold'>Single-Page Application</div><div style='font-size: 70%; margin-top: 0px'>[Container: JavaScript and Angular]</div><div style='font-size: 80%; margin-top:10px'>Provides all of the Internet<br />banking functionality to<br />customers via their web<br />browser.</div>"] + style 17 fill:#438dd5,stroke:#2e6295,color:#ffffff + 18["<div style='font-weight: bold'>Mobile App</div><div style='font-size: 70%; margin-top: 0px'>[Container: Xamarin]</div><div style='font-size: 80%; margin-top:10px'>Provides a limited subset of<br />the Internet banking<br />functionality to customers<br />via their mobile device.</div>"] + style 18 fill:#438dd5,stroke:#2e6295,color:#ffffff + 19["<div style='font-weight: bold'>Web Application</div><div style='font-size: 70%; margin-top: 0px'>[Container: Java and Spring MVC]</div><div style='font-size: 80%; margin-top:10px'>Delivers the static content<br />and the Internet banking<br />single page application.</div>"] + style 19 fill:#438dd5,stroke:#2e6295,color:#ffffff + 20["<div style='font-weight: bold'>API Application</div><div style='font-size: 70%; margin-top: 0px'>[Container: Java and Spring MVC]</div><div style='font-size: 80%; margin-top:10px'>Provides Internet banking<br />functionality via a<br />JSON/HTTPS API.</div>"] + style 20 fill:#438dd5,stroke:#2e6295,color:#ffffff + 21[("<div style='font-weight: bold'>Database</div><div style='font-size: 70%; margin-top: 0px'>[Container: Oracle Database Schema]</div><div style='font-size: 80%; margin-top:10px'>Stores user registration<br />information, hashed<br />authentication credentials,<br />access logs, etc.</div>")] + style 21 fill:#438dd5,stroke:#2e6295,color:#ffffff + end + + 1-. "<div>Visits bigbank.com/ib using</div><div style='font-size: 70%'>[HTTPS]</div>" .->19 + 1-. "<div>Views account balances, and<br />makes payments using</div><div style='font-size: 70%'></div>" .->17 + 1-. "<div>Views account balances, and<br />makes payments using</div><div style='font-size: 70%'></div>" .->18 + 19-. "<div>Delivers to the customer's<br />web browser</div><div style='font-size: 70%'></div>" .->17 + 20-. "<div>Reads from and writes to</div><div style='font-size: 70%'>[JDBC]</div>" .->21 + 20-. "<div>Makes API calls to</div><div style='font-size: 70%'>[XML/HTTPS]</div>" .->4 + 20-. "<div>Sends e-mail using</div><div style='font-size: 70%'>[SMTP]</div>" .->6 + 17-. "<div>Makes API calls to</div><div style='font-size: 70%'>[JSON/HTTPS]</div>" .->20 + 18-. "<div>Makes API calls to</div><div style='font-size: 70%'>[JSON/HTTPS]</div>" .->20 + 6-. "<div>Sends e-mails to</div><div style='font-size: 70%'></div>" .->1 + end \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-DevelopmentDeployment.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-DevelopmentDeployment.mmd new file mode 100644 index 000000000..14b3194e6 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-DevelopmentDeployment.mmd @@ -0,0 +1,61 @@ +graph TB + linkStyle default fill:#ffffff + + subgraph diagram ["Internet Banking System - Deployment - Development"] + style diagram fill:#ffffff,stroke:#ffffff + + subgraph 50 [Developer Laptop] + style 50 fill:#ffffff,stroke:#888888,color:#000000 + + subgraph 51 [Docker Container - Web Server] + style 51 fill:#ffffff,stroke:#888888,color:#000000 + + subgraph 52 [Apache Tomcat] + style 52 fill:#ffffff,stroke:#888888,color:#000000 + + 53["<div style='font-weight: bold'>Web Application</div><div style='font-size: 70%; margin-top: 0px'>[Container: Java and Spring MVC]</div><div style='font-size: 80%; margin-top:10px'>Delivers the static content<br />and the Internet banking<br />single page application.</div>"] + style 53 fill:#438dd5,stroke:#2e6295,color:#ffffff + 54["<div style='font-weight: bold'>API Application</div><div style='font-size: 70%; margin-top: 0px'>[Container: Java and Spring MVC]</div><div style='font-size: 80%; margin-top:10px'>Provides Internet banking<br />functionality via a<br />JSON/HTTPS API.</div>"] + style 54 fill:#438dd5,stroke:#2e6295,color:#ffffff + end + + end + + subgraph 59 [Docker Container - Database Server] + style 59 fill:#ffffff,stroke:#888888,color:#000000 + + subgraph 60 [Database Server] + style 60 fill:#ffffff,stroke:#888888,color:#000000 + + 61[("<div style='font-weight: bold'>Database</div><div style='font-size: 70%; margin-top: 0px'>[Container: Oracle Database Schema]</div><div style='font-size: 80%; margin-top:10px'>Stores user registration<br />information, hashed<br />authentication credentials,<br />access logs, etc.</div>")] + style 61 fill:#438dd5,stroke:#2e6295,color:#ffffff + end + + end + + subgraph 63 [Web Browser] + style 63 fill:#ffffff,stroke:#888888,color:#000000 + + 64["<div style='font-weight: bold'>Single-Page Application</div><div style='font-size: 70%; margin-top: 0px'>[Container: JavaScript and Angular]</div><div style='font-size: 80%; margin-top:10px'>Provides all of the Internet<br />banking functionality to<br />customers via their web<br />browser.</div>"] + style 64 fill:#438dd5,stroke:#2e6295,color:#ffffff + end + + end + + subgraph 55 [Big Bank plc] + style 55 fill:#ffffff,stroke:#888888,color:#000000 + + subgraph 56 [bigbank-dev001] + style 56 fill:#ffffff,stroke:#888888,color:#000000 + + 57["<div style='font-weight: bold'>Mainframe Banking System</div><div style='font-size: 70%; margin-top: 0px'>[Software System]</div><div style='font-size: 80%; margin-top:10px'>Stores all of the core<br />banking information about<br />customers, accounts,<br />transactions, etc.</div>"] + style 57 fill:#999999,stroke:#6b6b6b,color:#ffffff + end + + end + + 54-. "<div>Makes API calls to</div><div style='font-size: 70%'>[XML/HTTPS]</div>" .->57 + 54-. "<div>Reads from and writes to</div><div style='font-size: 70%'>[JDBC]</div>" .->61 + 64-. "<div>Makes API calls to</div><div style='font-size: 70%'>[JSON/HTTPS]</div>" .->54 + 53-. "<div>Delivers to the customer's<br />web browser</div><div style='font-size: 70%'></div>" .->64 + end \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-LiveDeployment.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-LiveDeployment.mmd new file mode 100644 index 000000000..73b4fb23c --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-LiveDeployment.mmd @@ -0,0 +1,92 @@ +graph TB + linkStyle default fill:#ffffff + + subgraph diagram ["Internet Banking System - Deployment - Live"] + style diagram fill:#ffffff,stroke:#ffffff + + subgraph 67 [Customer's mobile device] + style 67 fill:#ffffff,stroke:#888888,color:#000000 + + 68["<div style='font-weight: bold'>Mobile App</div><div style='font-size: 70%; margin-top: 0px'>[Container: Xamarin]</div><div style='font-size: 80%; margin-top:10px'>Provides a limited subset of<br />the Internet banking<br />functionality to customers<br />via their mobile device.</div>"] + style 68 fill:#438dd5,stroke:#2e6295,color:#ffffff + end + + subgraph 69 [Customer's computer] + style 69 fill:#ffffff,stroke:#888888,color:#000000 + + subgraph 70 [Web Browser] + style 70 fill:#ffffff,stroke:#888888,color:#000000 + + 71["<div style='font-weight: bold'>Single-Page Application</div><div style='font-size: 70%; margin-top: 0px'>[Container: JavaScript and Angular]</div><div style='font-size: 80%; margin-top:10px'>Provides all of the Internet<br />banking functionality to<br />customers via their web<br />browser.</div>"] + style 71 fill:#438dd5,stroke:#2e6295,color:#ffffff + end + + end + + subgraph 72 [Big Bank plc] + style 72 fill:#ffffff,stroke:#888888,color:#000000 + + subgraph 73 [bigbank-prod001] + style 73 fill:#ffffff,stroke:#888888,color:#000000 + + 74["<div style='font-weight: bold'>Mainframe Banking System</div><div style='font-size: 70%; margin-top: 0px'>[Software System]</div><div style='font-size: 80%; margin-top:10px'>Stores all of the core<br />banking information about<br />customers, accounts,<br />transactions, etc.</div>"] + style 74 fill:#999999,stroke:#6b6b6b,color:#ffffff + end + + subgraph 75 [bigbank-web***] + style 75 fill:#ffffff,stroke:#888888,color:#000000 + + subgraph 76 [Apache Tomcat] + style 76 fill:#ffffff,stroke:#888888,color:#000000 + + 77["<div style='font-weight: bold'>Web Application</div><div style='font-size: 70%; margin-top: 0px'>[Container: Java and Spring MVC]</div><div style='font-size: 80%; margin-top:10px'>Delivers the static content<br />and the Internet banking<br />single page application.</div>"] + style 77 fill:#438dd5,stroke:#2e6295,color:#ffffff + end + + end + + subgraph 79 [bigbank-api***] + style 79 fill:#ffffff,stroke:#888888,color:#000000 + + subgraph 80 [Apache Tomcat] + style 80 fill:#ffffff,stroke:#888888,color:#000000 + + 81["<div style='font-weight: bold'>API Application</div><div style='font-size: 70%; margin-top: 0px'>[Container: Java and Spring MVC]</div><div style='font-size: 80%; margin-top:10px'>Provides Internet banking<br />functionality via a<br />JSON/HTTPS API.</div>"] + style 81 fill:#438dd5,stroke:#2e6295,color:#ffffff + end + + end + + subgraph 85 [bigbank-db01] + style 85 fill:#ffffff,stroke:#888888,color:#000000 + + subgraph 86 [Oracle - Primary] + style 86 fill:#ffffff,stroke:#888888,color:#000000 + + 87[("<div style='font-weight: bold'>Database</div><div style='font-size: 70%; margin-top: 0px'>[Container: Oracle Database Schema]</div><div style='font-size: 80%; margin-top:10px'>Stores user registration<br />information, hashed<br />authentication credentials,<br />access logs, etc.</div>")] + style 87 fill:#438dd5,stroke:#2e6295,color:#ffffff + end + + end + + subgraph 89 [bigbank-db02] + style 89 fill:#ffffff,stroke:#888888,color:#000000 + + subgraph 90 [Oracle - Secondary] + style 90 fill:#ffffff,stroke:#888888,color:#000000 + + 91[("<div style='font-weight: bold'>Database</div><div style='font-size: 70%; margin-top: 0px'>[Container: Oracle Database Schema]</div><div style='font-size: 80%; margin-top:10px'>Stores user registration<br />information, hashed<br />authentication credentials,<br />access logs, etc.</div>")] + style 91 fill:#438dd5,stroke:#2e6295,color:#ffffff + end + + end + + end + + 77-. "<div>Delivers to the customer's<br />web browser</div><div style='font-size: 70%'></div>" .->71 + 68-. "<div>Makes API calls to</div><div style='font-size: 70%'>[JSON/HTTPS]</div>" .->81 + 71-. "<div>Makes API calls to</div><div style='font-size: 70%'>[JSON/HTTPS]</div>" .->81 + 81-. "<div>Makes API calls to</div><div style='font-size: 70%'>[XML/HTTPS]</div>" .->74 + 81-. "<div>Reads from and writes to</div><div style='font-size: 70%'>[JDBC]</div>" .->87 + 81-. "<div>Reads from and writes to</div><div style='font-size: 70%'>[JDBC]</div>" .->91 + end \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SignIn-sequence.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SignIn-sequence.mmd new file mode 100644 index 000000000..655ff882c --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SignIn-sequence.mmd @@ -0,0 +1,13 @@ +sequenceDiagram + + participant 17 as Single-Page Application<br />[Container: JavaScript and Angular] + participant 29 as Sign In Controller<br />[Component: Spring MVC Rest Controller] + participant 32 as Security Component<br />[Component: Spring Bean] + participant 21 as Database<br />[Container: Oracle Database Schema] + + 17->>29: Submits credentials to<br />[JSON/HTTPS] + 29->>32: Validates credentials using + 32->>21: select * from users where username = ?<br />[JDBC] + 21-->>32: Returns user data to<br />[JDBC] + 32-->>29: Returns true if the hashed password matches + 29-->>17: Sends back an authentication token to<br />[JSON/HTTPS] \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SignIn.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SignIn.mmd new file mode 100644 index 000000000..ad523b076 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SignIn.mmd @@ -0,0 +1,27 @@ +graph TB + linkStyle default fill:#ffffff + + subgraph diagram ["API Application - Dynamic"] + style diagram fill:#ffffff,stroke:#ffffff + + subgraph 20 [API Application] + style 20 fill:#ffffff,stroke:#2e6295,color:#2e6295 + + 29["<div style='font-weight: bold'>Sign In Controller</div><div style='font-size: 70%; margin-top: 0px'>[Component: Spring MVC Rest Controller]</div><div style='font-size: 80%; margin-top:10px'>Allows users to sign in to<br />the Internet Banking System.</div>"] + style 29 fill:#85bbf0,stroke:#5d82a8,color:#000000 + 32["<div style='font-weight: bold'>Security Component</div><div style='font-size: 70%; margin-top: 0px'>[Component: Spring Bean]</div><div style='font-size: 80%; margin-top:10px'>Provides functionality<br />related to signing in,<br />changing passwords, etc.</div>"] + style 32 fill:#85bbf0,stroke:#5d82a8,color:#000000 + end + + 17["<div style='font-weight: bold'>Single-Page Application</div><div style='font-size: 70%; margin-top: 0px'>[Container: JavaScript and Angular]</div><div style='font-size: 80%; margin-top:10px'>Provides all of the Internet<br />banking functionality to<br />customers via their web<br />browser.</div>"] + style 17 fill:#438dd5,stroke:#2e6295,color:#ffffff + 21[("<div style='font-weight: bold'>Database</div><div style='font-size: 70%; margin-top: 0px'>[Container: Oracle Database Schema]</div><div style='font-size: 80%; margin-top:10px'>Stores user registration<br />information, hashed<br />authentication credentials,<br />access logs, etc.</div>")] + style 21 fill:#438dd5,stroke:#2e6295,color:#ffffff + + 17-. "<div>1. Submits credentials to</div><div style='font-size: 70%'>[JSON/HTTPS]</div>" .->29 + 29-. "<div>2. Validates credentials<br />using</div><div style='font-size: 70%'></div>" .->32 + 32-. "<div>3. select * from users where<br />username = ?</div><div style='font-size: 70%'>[JDBC]</div>" .->21 + 21-. "<div>4. Returns user data to</div><div style='font-size: 70%'>[JDBC]</div>" .->32 + 32-. "<div>5. Returns true if the hashed<br />password matches</div><div style='font-size: 70%'></div>" .->29 + 29-. "<div>6. Sends back an<br />authentication token to</div><div style='font-size: 70%'>[JSON/HTTPS]</div>" .->17 + end \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SystemContext.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SystemContext.mmd new file mode 100644 index 000000000..184335671 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SystemContext.mmd @@ -0,0 +1,20 @@ +graph TB + linkStyle default fill:#ffffff + + subgraph diagram ["Internet Banking System - System Context"] + style diagram fill:#ffffff,stroke:#ffffff + + 1["<div style='font-weight: bold'>Personal Banking Customer</div><div style='font-size: 70%; margin-top: 0px'>[Person]</div><div style='font-size: 80%; margin-top:10px'>A customer of the bank, with<br />personal bank accounts.</div>"] + style 1 fill:#08427b,stroke:#052e56,color:#ffffff + 2["<div style='font-weight: bold'>Internet Banking System</div><div style='font-size: 70%; margin-top: 0px'>[Software System]</div><div style='font-size: 80%; margin-top:10px'>Allows customers to view<br />information about their bank<br />accounts, and make payments.</div>"] + style 2 fill:#1168bd,stroke:#0b4884,color:#ffffff + 4["<div style='font-weight: bold'>Mainframe Banking System</div><div style='font-size: 70%; margin-top: 0px'>[Software System]</div><div style='font-size: 80%; margin-top:10px'>Stores all of the core<br />banking information about<br />customers, accounts,<br />transactions, etc.</div>"] + style 4 fill:#999999,stroke:#6b6b6b,color:#ffffff + 6["<div style='font-weight: bold'>E-mail System</div><div style='font-size: 70%; margin-top: 0px'>[Software System]</div><div style='font-size: 80%; margin-top:10px'>The internal Microsoft<br />Exchange e-mail system.</div>"] + style 6 fill:#999999,stroke:#6b6b6b,color:#ffffff + + 1-. "<div>Views account balances, and<br />makes payments using</div><div style='font-size: 70%'></div>" .->2 + 2-. "<div>Gets account information<br />from, and makes payments<br />using</div><div style='font-size: 70%'></div>" .->4 + 2-. "<div>Sends e-mail using</div><div style='font-size: 70%'></div>" .->6 + 6-. "<div>Sends e-mails to</div><div style='font-size: 70%'></div>" .->1 + end \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SystemLandscape.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SystemLandscape.mmd new file mode 100644 index 000000000..9e382989d --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SystemLandscape.mmd @@ -0,0 +1,36 @@ +graph TB + linkStyle default fill:#ffffff + + subgraph diagram ["System Landscape"] + style diagram fill:#ffffff,stroke:#ffffff + + subgraph enterprise [Big Bank plc] + style enterprise fill:#ffffff,stroke:#444444,color:#444444 + + 12["<div style='font-weight: bold'>Customer Service Staff</div><div style='font-size: 70%; margin-top: 0px'>[Person]</div><div style='font-size: 80%; margin-top:10px'>Customer service staff within<br />the bank.</div>"] + style 12 fill:#999999,stroke:#6b6b6b,color:#ffffff + 15["<div style='font-weight: bold'>Back Office Staff</div><div style='font-size: 70%; margin-top: 0px'>[Person]</div><div style='font-size: 80%; margin-top:10px'>Administration and support<br />staff within the bank.</div>"] + style 15 fill:#999999,stroke:#6b6b6b,color:#ffffff + 2["<div style='font-weight: bold'>Internet Banking System</div><div style='font-size: 70%; margin-top: 0px'>[Software System]</div><div style='font-size: 80%; margin-top:10px'>Allows customers to view<br />information about their bank<br />accounts, and make payments.</div>"] + style 2 fill:#1168bd,stroke:#0b4884,color:#ffffff + 4["<div style='font-weight: bold'>Mainframe Banking System</div><div style='font-size: 70%; margin-top: 0px'>[Software System]</div><div style='font-size: 80%; margin-top:10px'>Stores all of the core<br />banking information about<br />customers, accounts,<br />transactions, etc.</div>"] + style 4 fill:#999999,stroke:#6b6b6b,color:#ffffff + 6["<div style='font-weight: bold'>E-mail System</div><div style='font-size: 70%; margin-top: 0px'>[Software System]</div><div style='font-size: 80%; margin-top:10px'>The internal Microsoft<br />Exchange e-mail system.</div>"] + style 6 fill:#999999,stroke:#6b6b6b,color:#ffffff + 9["<div style='font-weight: bold'>ATM</div><div style='font-size: 70%; margin-top: 0px'>[Software System]</div><div style='font-size: 80%; margin-top:10px'>Allows customers to withdraw<br />cash.</div>"] + style 9 fill:#999999,stroke:#6b6b6b,color:#ffffff + end + + 1["<div style='font-weight: bold'>Personal Banking Customer</div><div style='font-size: 70%; margin-top: 0px'>[Person]</div><div style='font-size: 80%; margin-top:10px'>A customer of the bank, with<br />personal bank accounts.</div>"] + style 1 fill:#08427b,stroke:#052e56,color:#ffffff + + 9-. "<div>Uses</div><div style='font-size: 70%'></div>" .->4 + 1-. "<div>Withdraws cash using</div><div style='font-size: 70%'></div>" .->9 + 12-. "<div>Uses</div><div style='font-size: 70%'></div>" .->4 + 1-. "<div>Asks questions to</div><div style='font-size: 70%'>[Telephone]</div>" .->12 + 15-. "<div>Uses</div><div style='font-size: 70%'></div>" .->4 + 1-. "<div>Views account balances, and<br />makes payments using</div><div style='font-size: 70%'></div>" .->2 + 2-. "<div>Gets account information<br />from, and makes payments<br />using</div><div style='font-size: 70%'></div>" .->4 + 2-. "<div>Sends e-mail using</div><div style='font-size: 70%'></div>" .->6 + 6-. "<div>Sends e-mails to</div><div style='font-size: 70%'></div>" .->1 + end \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/54915-AmazonWebServicesDeployment.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/54915-AmazonWebServicesDeployment.mmd new file mode 100644 index 000000000..dac7a2f2e --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/54915-AmazonWebServicesDeployment.mmd @@ -0,0 +1,48 @@ +graph LR + linkStyle default fill:#ffffff + + subgraph diagram ["Spring PetClinic - Deployment - Live"] + style diagram fill:#ffffff,stroke:#ffffff + + subgraph 5 [Amazon Web Services] + style 5 fill:#ffffff,stroke:#232f3e,color:#232f3e + + subgraph 6 [US-East-1] + style 6 fill:#ffffff,stroke:#147eba,color:#147eba + + subgraph 12 [Amazon RDS] + style 12 fill:#ffffff,stroke:#3b48cc,color:#3b48cc + + subgraph 13 [MySQL] + style 13 fill:#ffffff,stroke:#3b48cc,color:#3b48cc + + 14[("<div style='font-weight: bold'>Database</div><div style='font-size: 70%; margin-top: 0px'>[Container: Relational database schema]</div><div style='font-size: 80%; margin-top:10px'>Stores information regarding<br />the veterinarians, the<br />clients, and their pets.</div>")] + style 14 fill:#ffffff,stroke:#b2b2b2,color:#000000 + end + + end + + 7("<div style='font-weight: bold'>Route 53</div><div style='font-size: 70%; margin-top: 0px'>[Infrastructure Node]</div><div style='font-size: 80%; margin-top:10px'>Highly available and scalable<br />cloud DNS service.</div>") + style 7 fill:#ffffff,stroke:#693cc5,color:#693cc5 + 8("<div style='font-weight: bold'>Elastic Load Balancer</div><div style='font-size: 70%; margin-top: 0px'>[Infrastructure Node]</div><div style='font-size: 80%; margin-top:10px'>Automatically distributes<br />incoming application traffic.</div>") + style 8 fill:#ffffff,stroke:#693cc5,color:#693cc5 + subgraph 9 [Autoscaling group] + style 9 fill:#ffffff,stroke:#cc2264,color:#cc2264 + + subgraph 10 [Amazon EC2] + style 10 fill:#ffffff,stroke:#d86613,color:#d86613 + + 11("<div style='font-weight: bold'>Web Application</div><div style='font-size: 70%; margin-top: 0px'>[Container: Java and Spring Boot]</div><div style='font-size: 80%; margin-top:10px'>Allows employees to view and<br />manage information regarding<br />the veterinarians, the<br />clients, and their pets.</div>") + style 11 fill:#ffffff,stroke:#b2b2b2,color:#000000 + end + + end + + end + + end + + 11-. "<div>Reads from and writes to</div><div style='font-size: 70%'>[MySQL Protocol/SSL]</div>" .->14 + 7-. "<div>Forwards requests to</div><div style='font-size: 70%'>[HTTPS]</div>" .->8 + 8-. "<div>Forwards requests to</div><div style='font-size: 70%'>[HTTPS]</div>" .->11 + end \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/MermaidDiagramExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/mermaid/MermaidDiagramExporterTests.java new file mode 100644 index 000000000..e01c6ca53 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/MermaidDiagramExporterTests.java @@ -0,0 +1,317 @@ +package com.structurizr.export.mermaid; + +import com.structurizr.Workspace; +import com.structurizr.export.AbstractExporterTests; +import com.structurizr.export.Diagram; +import com.structurizr.model.*; +import com.structurizr.util.WorkspaceUtils; +import com.structurizr.view.*; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.util.Collection; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class MermaidDiagramExporterTests extends AbstractExporterTests { + + @Test + public void test_BigBankPlcExample() throws Exception { + Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/structurizr-36141-workspace.json")); + MermaidDiagramExporter exporter = new MermaidDiagramExporter(); + + Collection<Diagram> diagrams = exporter.export(workspace); + assertEquals(7, diagrams.size()); + + Diagram diagram = diagrams.stream().filter(d -> d.getKey().equals("SystemLandscape")).findFirst().get(); + String expected = readFile(new File("./src/test/java/com/structurizr/export/mermaid/36141-SystemLandscape.mmd")); + assertEquals(expected, diagram.getDefinition()); + + diagram = diagrams.stream().filter(d -> d.getKey().equals("SystemContext")).findFirst().get(); + expected = readFile(new File("./src/test/java/com/structurizr/export/mermaid/36141-SystemContext.mmd")); + assertEquals(expected, diagram.getDefinition()); + + diagram = diagrams.stream().filter(d -> d.getKey().equals("Containers")).findFirst().get(); + expected = readFile(new File("./src/test/java/com/structurizr/export/mermaid/36141-Containers.mmd")); + assertEquals(expected, diagram.getDefinition()); + + diagram = diagrams.stream().filter(d -> d.getKey().equals("Components")).findFirst().get(); + expected = readFile(new File("./src/test/java/com/structurizr/export/mermaid/36141-Components.mmd")); + assertEquals(expected, diagram.getDefinition()); + + diagram = diagrams.stream().filter(d -> d.getKey().equals("SignIn")).findFirst().get(); + expected = readFile(new File("./src/test/java/com/structurizr/export/mermaid/36141-SignIn.mmd")); + assertEquals(expected, diagram.getDefinition()); + + diagram = diagrams.stream().filter(md -> md.getKey().equals("DevelopmentDeployment")).findFirst().get(); + expected = readFile(new File("./src/test/java/com/structurizr/export/mermaid/36141-DevelopmentDeployment.mmd")); + assertEquals(expected, diagram.getDefinition()); + + diagram = diagrams.stream().filter(md -> md.getKey().equals("LiveDeployment")).findFirst().get(); + expected = readFile(new File("./src/test/java/com/structurizr/export/mermaid/36141-LiveDeployment.mmd")); + assertEquals(expected, diagram.getDefinition()); + + // and the sequence diagram version + workspace.getViews().getConfiguration().addProperty(exporter.MERMAID_SEQUENCE_DIAGRAM_PROPERTY, "true"); + diagrams = exporter.export(workspace); + diagram = diagrams.stream().filter(d -> d.getKey().equals("SignIn")).findFirst().get(); + expected = readFile(new File("./src/test/java/com/structurizr/export/mermaid/36141-SignIn-sequence.mmd")); + assertEquals(expected, diagram.getDefinition()); + } + + @Test + public void test_AmazonWebServicesExample() throws Exception { + Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/structurizr-54915-workspace.json")); + ThemeUtils.loadThemes(workspace); + workspace.getViews().getDeploymentViews().iterator().next().enableAutomaticLayout(AutomaticLayout.RankDirection.LeftRight, 300, 300); + + MermaidDiagramExporter exporter = new MermaidDiagramExporter(); + Collection<Diagram> diagrams = exporter.export(workspace); + assertEquals(1, diagrams.size()); + + Diagram diagram = diagrams.stream().findFirst().get(); + String expected = readFile(new File("./src/test/java/com/structurizr/export/mermaid/54915-AmazonWebServicesDeployment.mmd")); + assertEquals(expected, diagram.getDefinition()); + } + + @Test + public void test_GroupsExample() throws Exception { + Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/groups.json")); + ThemeUtils.loadThemes(workspace); + + MermaidDiagramExporter exporter = new MermaidDiagramExporter(); + Collection<Diagram> diagrams = exporter.export(workspace); + assertEquals(3, diagrams.size()); + + Diagram diagram = diagrams.stream().filter(md -> md.getKey().equals("SystemLandscape")).findFirst().get(); + String expected = readFile(new File("./src/test/java/com/structurizr/export/mermaid/groups-SystemLandscape.mmd")); + assertEquals(expected, diagram.getDefinition()); + + diagram = diagrams.stream().filter(md -> md.getKey().equals("Containers")).findFirst().get(); + expected = readFile(new File("./src/test/java/com/structurizr/export/mermaid/groups-Containers.mmd")); + assertEquals(expected, diagram.getDefinition()); + + diagram = diagrams.stream().filter(md -> md.getKey().equals("Components")).findFirst().get(); + expected = readFile(new File("./src/test/java/com/structurizr/export/mermaid/groups-Components.mmd")); + assertEquals(expected, diagram.getDefinition()); + } + + @Test + public void test_NestedGroupsExample() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getModel().addProperty("structurizr.groupSeparator", "/"); + + SoftwareSystem a = workspace.getModel().addSoftwareSystem("Team 1"); + a.setGroup("Organisation 1/Department 1/Team 1"); + + SoftwareSystem b = workspace.getModel().addSoftwareSystem("Team 2"); + b.setGroup("Organisation 1/Department 1/Team 2"); + + SoftwareSystem c = workspace.getModel().addSoftwareSystem("Organisation 1"); + c.setGroup("Organisation 1"); + + SoftwareSystem d = workspace.getModel().addSoftwareSystem("Organisation 2"); + d.setGroup("Organisation 2"); + + SoftwareSystem e = workspace.getModel().addSoftwareSystem("Department 1"); + e.setGroup("Organisation 1/Department 1"); + + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("SystemLandscape", "Description"); + view.addAllElements(); + + MermaidDiagramExporter exporter = new MermaidDiagramExporter(); + Collection<Diagram> diagrams = exporter.export(workspace); + + Diagram diagram = diagrams.stream().filter(md -> md.getKey().equals("SystemLandscape")).findFirst().get(); + String expected = readFile(new File("./src/test/java/com/structurizr/export/mermaid/nested-groups.mmd")); + assertEquals(expected, diagram.getDefinition()); + } + + @Test + public void test_renderContainerDiagramWithExternalContainers() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystem1 = workspace.getModel().addSoftwareSystem("Software System 1"); + Container container1 = softwareSystem1.addContainer("Container 1"); + SoftwareSystem softwareSystem2 = workspace.getModel().addSoftwareSystem("Software System 2"); + Container container2 = softwareSystem2.addContainer("Container 2"); + + container1.uses(container2, "Uses"); + + ContainerView containerView = workspace.getViews().createContainerView(softwareSystem1, "Containers", ""); + containerView.add(container1); + containerView.add(container2); + + Diagram diagram = new MermaidDiagramExporter().export(containerView); + assertEquals("graph TB\n" + + " linkStyle default fill:#ffffff\n" + + "\n" + + " subgraph diagram [\"Software System 1 - Containers\"]\n" + + " style diagram fill:#ffffff,stroke:#ffffff\n" + + "\n" + + " subgraph 1 [Software System 1]\n" + + " style 1 fill:#ffffff,stroke:#9a9a9a,color:#9a9a9a\n" + + "\n" + + " 2[\"<div style='font-weight: bold'>Container 1</div><div style='font-size: 70%; margin-top: 0px'>[Container]</div>\"]\n" + + " style 2 fill:#dddddd,stroke:#9a9a9a,color:#000000\n" + + " end\n" + + "\n" + + " subgraph 3 [Software System 2]\n" + + " style 3 fill:#ffffff,stroke:#9a9a9a,color:#9a9a9a\n" + + "\n" + + " 4[\"<div style='font-weight: bold'>Container 2</div><div style='font-size: 70%; margin-top: 0px'>[Container]</div>\"]\n" + + " style 4 fill:#dddddd,stroke:#9a9a9a,color:#000000\n" + + " end\n" + + "\n" + + " 2-. \"<div>Uses</div><div style='font-size: 70%'></div>\" .->4\n" + + " end", diagram.getDefinition()); + } + + @Test + public void test_renderComponentDiagramWithExternalComponents() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystem1 = workspace.getModel().addSoftwareSystem("Software System 1"); + Container container1 = softwareSystem1.addContainer("Container 1"); + Component component1 = container1.addComponent("Component 1"); + SoftwareSystem softwareSystem2 = workspace.getModel().addSoftwareSystem("Software System 2"); + Container container2 = softwareSystem2.addContainer("Container 2"); + Component component2 = container2.addComponent("Component 2"); + + component1.uses(component2, "Uses"); + + ComponentView componentView = workspace.getViews().createComponentView(container1, "Components", ""); + componentView.add(component1); + componentView.add(component2); + + Diagram diagram = new MermaidDiagramExporter().export(componentView); + assertEquals("graph TB\n" + + " linkStyle default fill:#ffffff\n" + + "\n" + + " subgraph diagram [\"Software System 1 - Container 1 - Components\"]\n" + + " style diagram fill:#ffffff,stroke:#ffffff\n" + + "\n" + + " subgraph 2 [Container 1]\n" + + " style 2 fill:#ffffff,stroke:#9a9a9a,color:#9a9a9a\n" + + "\n" + + " 3[\"<div style='font-weight: bold'>Component 1</div><div style='font-size: 70%; margin-top: 0px'>[Component]</div>\"]\n" + + " style 3 fill:#dddddd,stroke:#9a9a9a,color:#000000\n" + + " end\n" + + "\n" + + " subgraph 5 [Container 2]\n" + + " style 5 fill:#ffffff,stroke:#9a9a9a,color:#9a9a9a\n" + + "\n" + + " 6[\"<div style='font-weight: bold'>Component 2</div><div style='font-size: 70%; margin-top: 0px'>[Component]</div>\"]\n" + + " style 6 fill:#dddddd,stroke:#9a9a9a,color:#000000\n" + + " end\n" + + "\n" + + " 3-. \"<div>Uses</div><div style='font-size: 70%'></div>\" .->6\n" + + " end", diagram.getDefinition()); + } + + @Test + public void test_renderGroupStyles() { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getModel().addPerson("User 1").setGroup("Group 1"); + workspace.getModel().addPerson("User 2").setGroup("Group 2"); + workspace.getModel().addPerson("User 3").setGroup("Group 3"); + + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", ""); + view.addDefaultElements(); + + workspace.getViews().getConfiguration().getStyles().addElementStyle("Group:Group 1").color("#111111"); + workspace.getViews().getConfiguration().getStyles().addElementStyle("Group:Group 2").color("#222222"); + + MermaidDiagramExporter exporter = new MermaidDiagramExporter(); + Diagram diagram = exporter.export(view); + assertEquals("graph TB\n" + + " linkStyle default fill:#ffffff\n" + + "\n" + + " subgraph diagram [\"System Landscape\"]\n" + + " style diagram fill:#ffffff,stroke:#ffffff\n" + + "\n" + + " subgraph group1 [Group 1]\n" + + " style group1 fill:#ffffff,stroke:#111111,color:#111111,stroke-dasharray:5\n" + + "\n" + + " 1[\"<div style='font-weight: bold'>User 1</div><div style='font-size: 70%; margin-top: 0px'>[Person]</div>\"]\n" + + " style 1 fill:#dddddd,stroke:#9a9a9a,color:#000000\n" + + " end\n" + + "\n" + + " subgraph group2 [Group 2]\n" + + " style group2 fill:#ffffff,stroke:#222222,color:#222222,stroke-dasharray:5\n" + + "\n" + + " 2[\"<div style='font-weight: bold'>User 2</div><div style='font-size: 70%; margin-top: 0px'>[Person]</div>\"]\n" + + " style 2 fill:#dddddd,stroke:#9a9a9a,color:#000000\n" + + " end\n" + + "\n" + + " subgraph group3 [Group 3]\n" + + " style group3 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5\n" + + "\n" + + " 3[\"<div style='font-weight: bold'>User 3</div><div style='font-size: 70%; margin-top: 0px'>[Person]</div>\"]\n" + + " style 3 fill:#dddddd,stroke:#9a9a9a,color:#000000\n" + + " end\n" + + "\n" + + "\n" + + " end", diagram.getDefinition()); + + workspace.getViews().getConfiguration().getStyles().addElementStyle("Group").color("#aabbcc"); + + diagram = exporter.export(view); + assertEquals("graph TB\n" + + " linkStyle default fill:#ffffff\n" + + "\n" + + " subgraph diagram [\"System Landscape\"]\n" + + " style diagram fill:#ffffff,stroke:#ffffff\n" + + "\n" + + " subgraph group1 [Group 1]\n" + + " style group1 fill:#ffffff,stroke:#111111,color:#111111,stroke-dasharray:5\n" + + "\n" + + " 1[\"<div style='font-weight: bold'>User 1</div><div style='font-size: 70%; margin-top: 0px'>[Person]</div>\"]\n" + + " style 1 fill:#dddddd,stroke:#9a9a9a,color:#000000\n" + + " end\n" + + "\n" + + " subgraph group2 [Group 2]\n" + + " style group2 fill:#ffffff,stroke:#222222,color:#222222,stroke-dasharray:5\n" + + "\n" + + " 2[\"<div style='font-weight: bold'>User 2</div><div style='font-size: 70%; margin-top: 0px'>[Person]</div>\"]\n" + + " style 2 fill:#dddddd,stroke:#9a9a9a,color:#000000\n" + + " end\n" + + "\n" + + " subgraph group3 [Group 3]\n" + + " style group3 fill:#ffffff,stroke:#aabbcc,color:#aabbcc,stroke-dasharray:5\n" + + "\n" + + " 3[\"<div style='font-weight: bold'>User 3</div><div style='font-size: 70%; margin-top: 0px'>[Person]</div>\"]\n" + + " style 3 fill:#dddddd,stroke:#9a9a9a,color:#000000\n" + + " end\n" + + "\n" + + "\n" + + " end", diagram.getDefinition()); + } + + @Test + public void test_renderCustomView() { + Workspace workspace = new Workspace("Name", "Description"); + Model model = workspace.getModel(); + + CustomElement a = model.addCustomElement("A"); + CustomElement b = model.addCustomElement("B", "Custom", "Description"); + a.uses(b, "Uses"); + + CustomView view = workspace.getViews().createCustomView("key", "Title", "Description"); + view.addDefaultElements(); + + Diagram diagram = new MermaidDiagramExporter().export(view); + assertEquals("graph TB\n" + + " linkStyle default fill:#ffffff\n" + + "\n" + + " subgraph diagram [\"Title\"]\n" + + " style diagram fill:#ffffff,stroke:#ffffff\n" + + "\n" + + " 1[\"<div style='font-weight: bold'>A</div><div style='font-size: 70%; margin-top: 0px'></div>\"]\n" + + " style 1 fill:#dddddd,stroke:#9a9a9a,color:#000000\n" + + " 2[\"<div style='font-weight: bold'>B</div><div style='font-size: 70%; margin-top: 0px'>[Custom]</div><div style='font-size: 80%; margin-top:10px'>Description</div>\"]\n" + + " style 2 fill:#dddddd,stroke:#9a9a9a,color:#000000\n" + + "\n" + + " 1-. \"<div>Uses</div><div style='font-size: 70%'></div>\" .->2\n" + + " end", diagram.getDefinition()); + } + +} \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-Components.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-Components.mmd new file mode 100644 index 000000000..79c389eca --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-Components.mmd @@ -0,0 +1,26 @@ +graph TB + linkStyle default fill:#ffffff + + subgraph diagram ["D - F - Components"] + style diagram fill:#ffffff,stroke:#ffffff + + 3["<div style='font-weight: bold'>C</div><div style='font-size: 70%; margin-top: 0px'>[Software System]</div>"] + style 3 fill:#dddddd,stroke:#9a9a9a,color:#000000 + + subgraph 6 [F] + style 6 fill:#ffffff,stroke:#9a9a9a,color:#9a9a9a + + subgraph group1 [Group 4] + style group1 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 + + 8["<div style='font-weight: bold'>H</div><div style='font-size: 70%; margin-top: 0px'>[Component]</div>"] + style 8 fill:#dddddd,stroke:#9a9a9a,color:#000000 + end + + 7["<div style='font-weight: bold'>G</div><div style='font-size: 70%; margin-top: 0px'>[Component]</div>"] + style 7 fill:#dddddd,stroke:#9a9a9a,color:#000000 + end + + 3-. "<div></div><div style='font-size: 70%'></div>" .->7 + 3-. "<div></div><div style='font-size: 70%'></div>" .->8 + end \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-Containers.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-Containers.mmd new file mode 100644 index 000000000..3c5abba9d --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-Containers.mmd @@ -0,0 +1,26 @@ +graph TB + linkStyle default fill:#ffffff + + subgraph diagram ["D - Containers"] + style diagram fill:#ffffff,stroke:#ffffff + + 3["<div style='font-weight: bold'>C</div><div style='font-size: 70%; margin-top: 0px'>[Software System]</div>"] + style 3 fill:#dddddd,stroke:#9a9a9a,color:#000000 + + subgraph 4 [D] + style 4 fill:#ffffff,stroke:#9a9a9a,color:#9a9a9a + + subgraph group1 [Group 3] + style group1 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 + + 6["<div style='font-weight: bold'>F</div><div style='font-size: 70%; margin-top: 0px'>[Container]</div>"] + style 6 fill:#dddddd,stroke:#9a9a9a,color:#000000 + end + + 5["<div style='font-weight: bold'>E</div><div style='font-size: 70%; margin-top: 0px'>[Container]</div>"] + style 5 fill:#dddddd,stroke:#9a9a9a,color:#000000 + end + + 3-. "<div></div><div style='font-size: 70%'></div>" .->5 + 3-. "<div></div><div style='font-size: 70%'></div>" .->6 + end \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-SystemLandscape.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-SystemLandscape.mmd new file mode 100644 index 000000000..5ca147d80 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-SystemLandscape.mmd @@ -0,0 +1,34 @@ +graph TB + linkStyle default fill:#ffffff + + subgraph diagram ["System Landscape"] + style diagram fill:#ffffff,stroke:#ffffff + + subgraph enterprise [Enterprise] + style enterprise fill:#ffffff,stroke:#444444,color:#444444 + + subgraph group1 [Group 2] + style group1 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 + + 4["<div style='font-weight: bold'>D</div><div style='font-size: 70%; margin-top: 0px'>[Software System]</div>"] + style 4 fill:#dddddd,stroke:#9a9a9a,color:#000000 + end + + 3["<div style='font-weight: bold'>C</div><div style='font-size: 70%; margin-top: 0px'>[Software System]</div>"] + style 3 fill:#dddddd,stroke:#9a9a9a,color:#000000 + end + + subgraph group2 [Group 1] + style group2 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 + + 2["<div style='font-weight: bold'>B</div><div style='font-size: 70%; margin-top: 0px'>[Software System]</div>"] + style 2 fill:#dddddd,stroke:#9a9a9a,color:#000000 + end + + 1["<div style='font-weight: bold'>A</div><div style='font-size: 70%; margin-top: 0px'>[Software System]</div>"] + style 1 fill:#dddddd,stroke:#9a9a9a,color:#000000 + + 2-. "<div></div><div style='font-size: 70%'></div>" .->3 + 3-. "<div></div><div style='font-size: 70%'></div>" .->4 + 1-. "<div></div><div style='font-size: 70%'></div>" .->2 + end \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/nested-groups.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/nested-groups.mmd new file mode 100644 index 000000000..e48b4e3ec --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/nested-groups.mmd @@ -0,0 +1,43 @@ +graph TB + linkStyle default fill:#ffffff + + subgraph diagram ["System Landscape"] + style diagram fill:#ffffff,stroke:#ffffff + + subgraph group1 [Organisation 1] + style group1 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 + + 3["<div style='font-weight: bold'>Organisation 1</div><div style='font-size: 70%; margin-top: 0px'>[Software System]</div>"] + style 3 fill:#dddddd,stroke:#9a9a9a,color:#000000 + subgraph group2 [Department 1] + style group2 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 + + 5["<div style='font-weight: bold'>Department 1</div><div style='font-size: 70%; margin-top: 0px'>[Software System]</div>"] + style 5 fill:#dddddd,stroke:#9a9a9a,color:#000000 + subgraph group3 [Team 1] + style group3 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 + + 1["<div style='font-weight: bold'>Team 1</div><div style='font-size: 70%; margin-top: 0px'>[Software System]</div>"] + style 1 fill:#dddddd,stroke:#9a9a9a,color:#000000 + end + + subgraph group4 [Team 2] + style group4 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 + + 2["<div style='font-weight: bold'>Team 2</div><div style='font-size: 70%; margin-top: 0px'>[Software System]</div>"] + style 2 fill:#dddddd,stroke:#9a9a9a,color:#000000 + end + + end + + end + + subgraph group5 [Organisation 2] + style group5 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 + + 4["<div style='font-weight: bold'>Organisation 2</div><div style='font-size: 70%; margin-top: 0px'>[Software System]</div>"] + style 4 fill:#dddddd,stroke:#9a9a9a,color:#000000 + end + + + end \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java new file mode 100644 index 000000000..8066396c2 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java @@ -0,0 +1,755 @@ +package com.structurizr.export.plantuml; + +import com.structurizr.Workspace; +import com.structurizr.export.AbstractExporterTests; +import com.structurizr.export.Diagram; +import com.structurizr.model.*; +import com.structurizr.util.WorkspaceUtils; +import com.structurizr.view.*; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.util.Collection; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class C4PlantUMLDiagramExporterTests extends AbstractExporterTests { + + @Test + public void test_BigBankPlcExample() throws Exception { + Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/structurizr-36141-workspace.json")); + workspace.getViews().getConfiguration().addProperty(C4PlantUMLExporter.C4PLANTUML_TAGS_PROPERTY, "true"); + + C4PlantUMLExporter exporter = new C4PlantUMLExporter(); + Collection<Diagram> diagrams = exporter.export(workspace); + assertEquals(7, diagrams.size()); + + Diagram diagram = diagrams.stream().filter(d -> d.getKey().equals("SystemLandscape")).findFirst().get(); + String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemLandscape.puml")); + assertEquals(expected, diagram.getDefinition()); + + diagram = diagrams.stream().filter(d -> d.getKey().equals("SystemContext")).findFirst().get(); + expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemContext.puml")); + assertEquals(expected, diagram.getDefinition()); + + diagram = diagrams.stream().filter(d -> d.getKey().equals("Containers")).findFirst().get(); + expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Containers.puml")); + assertEquals(expected, diagram.getDefinition()); + + diagram = diagrams.stream().filter(d -> d.getKey().equals("Components")).findFirst().get(); + expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Components.puml")); + assertEquals(expected, diagram.getDefinition()); + + diagram = diagrams.stream().filter(d -> d.getKey().equals("SignIn")).findFirst().get(); + expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn.puml")); + assertEquals(expected, diagram.getDefinition()); + + diagram = diagrams.stream().filter(md -> md.getKey().equals("DevelopmentDeployment")).findFirst().get(); + expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-DevelopmentDeployment.puml")); + assertEquals(expected, diagram.getDefinition()); + + diagram = diagrams.stream().filter(md -> md.getKey().equals("LiveDeployment")).findFirst().get(); + expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-LiveDeployment.puml")); + assertEquals(expected, diagram.getDefinition()); + + // and the sequence diagram version + workspace.getViews().getConfiguration().addProperty(exporter.PLANTUML_SEQUENCE_DIAGRAM_PROPERTY, "true"); + diagrams = exporter.export(workspace); + diagram = diagrams.stream().filter(d -> d.getKey().equals("SignIn")).findFirst().get(); + expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn-sequence.puml")); + assertEquals(expected, diagram.getDefinition()); + } + + @Test + public void test_AmazonWebServicesExampleWithoutTags() throws Exception { + Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/structurizr-54915-workspace.json")); + ThemeUtils.loadThemes(workspace); + workspace.getViews().getDeploymentViews().iterator().next().enableAutomaticLayout(AutomaticLayout.RankDirection.LeftRight, 300, 300); + workspace.getViews().getViews().forEach(v -> v.addProperty(C4PlantUMLExporter.C4PLANTUML_TAGS_PROPERTY, "false")); + + C4PlantUMLExporter exporter = new C4PlantUMLExporter(); + Collection<Diagram> diagrams = exporter.export(workspace); + assertEquals(1, diagrams.size()); + + Diagram diagram = diagrams.stream().findFirst().get(); + String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/c4plantuml/54915-AmazonWebServicesDeployment-WithoutTags.puml")); + assertEquals(expected, diagram.getDefinition()); + } + + @Test + public void test_AmazonWebServicesExampleWithTags() throws Exception { + Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/structurizr-54915-workspace.json")); + ThemeUtils.loadThemes(workspace); + workspace.getViews().getDeploymentViews().iterator().next().enableAutomaticLayout(AutomaticLayout.RankDirection.LeftRight, 300, 300); + workspace.getViews().getConfiguration().addProperty(C4PlantUMLExporter.C4PLANTUML_TAGS_PROPERTY, "true"); + + C4PlantUMLExporter exporter = new C4PlantUMLExporter(); + Collection<Diagram> diagrams = exporter.export(workspace); + assertEquals(1, diagrams.size()); + + Diagram diagram = diagrams.stream().findFirst().get(); + String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/c4plantuml/54915-AmazonWebServicesDeployment-WithTags.puml")); + assertEquals(expected, diagram.getDefinition()); + } + + @Test + public void test_GroupsExample() throws Exception { + Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/groups.json")); + ThemeUtils.loadThemes(workspace); + + C4PlantUMLExporter exporter = new C4PlantUMLExporter(); + Collection<Diagram> diagrams = exporter.export(workspace); + assertEquals(3, diagrams.size()); + + Diagram diagram = diagrams.stream().filter(md -> md.getKey().equals("SystemLandscape")).findFirst().get(); + String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-SystemLandscape.puml")); + assertEquals(expected, diagram.getDefinition()); + + diagram = diagrams.stream().filter(md -> md.getKey().equals("Containers")).findFirst().get(); + expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Containers.puml")); + assertEquals(expected, diagram.getDefinition()); + + diagram = diagrams.stream().filter(md -> md.getKey().equals("Components")).findFirst().get(); + expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Components.puml")); + assertEquals(expected, diagram.getDefinition()); + } + + @Test + public void test_NestedGroupsExample() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getModel().addProperty("structurizr.groupSeparator", "/"); + + SoftwareSystem a = workspace.getModel().addSoftwareSystem("Team 1"); + a.setGroup("Organisation 1/Department 1/Team 1"); + + SoftwareSystem b = workspace.getModel().addSoftwareSystem("Team 2"); + b.setGroup("Organisation 1/Department 1/Team 2"); + + SoftwareSystem c = workspace.getModel().addSoftwareSystem("Organisation 1"); + c.setGroup("Organisation 1"); + + SoftwareSystem d = workspace.getModel().addSoftwareSystem("Organisation 2"); + d.setGroup("Organisation 2"); + + SoftwareSystem e = workspace.getModel().addSoftwareSystem("Department 1"); + e.setGroup("Organisation 1/Department 1"); + + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("SystemLandscape", "Description"); + view.addAllElements(); + + C4PlantUMLExporter exporter = new C4PlantUMLExporter(); + Collection<Diagram> diagrams = exporter.export(workspace); + + Diagram diagram = diagrams.stream().filter(md -> md.getKey().equals("SystemLandscape")).findFirst().get(); + String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/c4plantuml/nested-groups.puml")); + assertEquals(expected, diagram.getDefinition()); + } + + @Test + public void test_NestedGroupsExample_WithDotAsGroupSeparator() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getModel().addProperty("structurizr.groupSeparator", "."); + + SoftwareSystem a = workspace.getModel().addSoftwareSystem("Team 1"); + a.setGroup("Organisation 1.Department 1.Team 1"); + + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("SystemLandscape", "Description"); + view.addAllElements(); + + C4PlantUMLExporter exporter = new C4PlantUMLExporter(); + Collection<Diagram> diagrams = exporter.export(workspace); + + Diagram diagram = diagrams.stream().filter(md -> md.getKey().equals("SystemLandscape")).findFirst().get(); + String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/c4plantuml/nested-groups-with-dot-separator.puml")); + assertEquals(expected, diagram.getDefinition()); + } + + @Test + public void test_renderGroupStyles() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getModel().addPerson("User 1").setGroup("Group 1"); + workspace.getModel().addPerson("User 2").setGroup("Group 2"); + workspace.getModel().addPerson("User 3").setGroup("Group 3"); + + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", ""); + view.addDefaultElements(); + + workspace.getViews().getConfiguration().getStyles().addElementStyle("Group:Group 1").color("#111111").icon("https://example.com/icon1.png"); + workspace.getViews().getConfiguration().getStyles().addElementStyle("Group:Group 2").color("#222222").icon("https://example.com/icon2.png"); + + C4PlantUMLExporter exporter = new C4PlantUMLExporter() { + @Override + protected double calculateIconScale(String iconUrl) { + return 1.0; + } + }; + + Diagram diagram = exporter.export(view); + String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/c4plantuml/group-styles-1.puml")); + assertEquals(expected, diagram.getDefinition()); + + workspace.getViews().getConfiguration().getStyles().addElementStyle("Group").color("#aabbcc"); + + diagram = exporter.export(view); + expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/c4plantuml/group-styles-2.puml")); + assertEquals(expected, diagram.getDefinition()); + } + + @Test + public void test_renderContainerDiagramWithExternalContainers() { + Workspace workspace = new Workspace("Name", "Description"); + + SoftwareSystem softwareSystem1 = workspace.getModel().addSoftwareSystem("Software System 1"); + Container container1 = softwareSystem1.addContainer("Container 1"); + SoftwareSystem softwareSystem2 = workspace.getModel().addSoftwareSystem("Software System 2"); + Container container2 = softwareSystem2.addContainer("Container 2"); + + container1.uses(container2, "Uses"); + + ContainerView containerView = workspace.getViews().createContainerView(softwareSystem1, "Containers", ""); + containerView.add(container1); + containerView.add(container2); + + Diagram diagram = new C4PlantUMLExporter().export(containerView); + assertEquals("@startuml\n" + + "set separator none\n" + + "title Software System 1 - Containers\n" + + "\n" + + "top to bottom direction\n" + + "\n" + + "!include <C4/C4>\n" + + "!include <C4/C4_Context>\n" + + "!include <C4/C4_Container>\n" + + "\n" + + "System_Boundary(\"SoftwareSystem1_boundary\", \"Software System 1\", $tags=\"\") {\n" + + " Container(SoftwareSystem1.Container1, \"Container 1\", $techn=\"\", $descr=\"\", $tags=\"\", $link=\"\")\n" + + "}\n" + + "\n" + + "System_Boundary(\"SoftwareSystem2_boundary\", \"Software System 2\", $tags=\"\") {\n" + + " Container(SoftwareSystem2.Container2, \"Container 2\", $techn=\"\", $descr=\"\", $tags=\"\", $link=\"\")\n" + + "}\n" + + "\n" + + "Rel(SoftwareSystem1.Container1, SoftwareSystem2.Container2, \"Uses\", $techn=\"\", $tags=\"\", $link=\"\")\n" + + "\n" + + "SHOW_LEGEND(true)\n" + + "@enduml", diagram.getDefinition()); + } + + @Test + public void test_renderComponentDiagramWithExternalComponents() { + Workspace workspace = new Workspace("Name", "Description"); + + SoftwareSystem softwareSystem1 = workspace.getModel().addSoftwareSystem("Software System 1"); + Container container1 = softwareSystem1.addContainer("Container 1"); + Component component1 = container1.addComponent("Component 1"); + SoftwareSystem softwareSystem2 = workspace.getModel().addSoftwareSystem("Software System 2"); + Container container2 = softwareSystem2.addContainer("Container 2"); + Component component2 = container2.addComponent("Component 2"); + + component1.uses(component2, "Uses"); + + ComponentView componentView = workspace.getViews().createComponentView(container1, "Components", ""); + componentView.add(component1); + componentView.add(component2); + + Diagram diagram = new C4PlantUMLExporter().export(componentView); + assertEquals("@startuml\n" + + "set separator none\n" + + "title Software System 1 - Container 1 - Components\n" + + "\n" + + "top to bottom direction\n" + + "\n" + + "!include <C4/C4>\n" + + "!include <C4/C4_Context>\n" + + "!include <C4/C4_Component>\n" + + "\n" + + "Container_Boundary(\"SoftwareSystem1.Container1_boundary\", \"Container 1\", $tags=\"\") {\n" + + " Component(SoftwareSystem1.Container1.Component1, \"Component 1\", $techn=\"\", $descr=\"\", $tags=\"\", $link=\"\")\n" + + "}\n" + + "\n" + + "Container_Boundary(\"SoftwareSystem2.Container2_boundary\", \"Container 2\", $tags=\"\") {\n" + + " Component(SoftwareSystem2.Container2.Component2, \"Component 2\", $techn=\"\", $descr=\"\", $tags=\"\", $link=\"\")\n" + + "}\n" + + "\n" + + "Rel(SoftwareSystem1.Container1.Component1, SoftwareSystem2.Container2.Component2, \"Uses\", $techn=\"\", $tags=\"\", $link=\"\")\n" + + "\n" + + "SHOW_LEGEND(true)\n" + + "@enduml", diagram.getDefinition()); + } + + @Test + public void test_renderDiagramWithElementUrls() { + Workspace workspace = new Workspace("Name", "Description"); + + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); + softwareSystem.setUrl("https://structurizr.com"); + + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "Description"); + view.addDefaultElements(); + + Diagram diagram = new C4PlantUMLExporter().export(view); + assertEquals("@startuml\n" + + "set separator none\n" + + "title System Landscape\n" + + "\n" + + "top to bottom direction\n" + + "\n" + + "!include <C4/C4>\n" + + "!include <C4/C4_Context>\n" + + "\n" + + "System(SoftwareSystem, \"Software System\", $descr=\"\", $tags=\"\", $link=\"https://structurizr.com\")\n" + + "\n" + + "\n" + + "SHOW_LEGEND(true)\n" + + "@enduml", diagram.getDefinition()); + } + + @Test + public void test_renderDiagramWithIncludes() { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getModel().addSoftwareSystem("Software System"); + + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "Description"); + view.addDefaultElements(); + + view.addProperty(C4PlantUMLExporter.PLANTUML_INCLUDES_PROPERTY, "styles.puml"); + + Diagram diagram = new C4PlantUMLExporter().export(view); + assertEquals("@startuml\n" + + "set separator none\n" + + "title System Landscape\n" + + "\n" + + "top to bottom direction\n" + + "\n" + + "!include <C4/C4>\n" + + "!include <C4/C4_Context>\n" + + "!include styles.puml\n" + + "\n" + + "System(SoftwareSystem, \"Software System\", $descr=\"\", $tags=\"\", $link=\"\")\n" + + "\n" + + "\n" + + "SHOW_LEGEND(true)\n" + + "@enduml", diagram.getDefinition()); + } + + @Test + public void test_renderDiagramWithNewLineCharacterInElementName() { + Workspace workspace = new Workspace("Name", "Description"); + + workspace.getModel().addSoftwareSystem("Software\nSystem"); + + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "Description"); + view.addDefaultElements(); + + Diagram diagram = new C4PlantUMLExporter().export(view); + assertEquals("@startuml\n" + + "set separator none\n" + + "title System Landscape\n" + + "\n" + + "top to bottom direction\n" + + "\n" + + "!include <C4/C4>\n" + + "!include <C4/C4_Context>\n" + + "\n" + + "System(SoftwareSystem, \"Software\\nSystem\", $descr=\"\", $tags=\"\", $link=\"\")\n" + + "\n" + + "\n" + + "SHOW_LEGEND(true)\n" + + "@enduml", diagram.getDefinition()); + } + + @Test + public void test_renderInfrastructureNodeWithTechnology() { + Workspace workspace = new Workspace("Name", "Description"); + DeploymentNode deploymentNode = workspace.getModel().addDeploymentNode("Deployment node"); + deploymentNode.addInfrastructureNode("Infrastructure node", "description", "technology"); + + DeploymentView view = workspace.getViews().createDeploymentView("key", "view description"); + view.addDefaultElements(); + + Diagram diagram = new C4PlantUMLExporter().export(view); + assertEquals("@startuml\n" + + "set separator none\n" + + "title Deployment - Default\n" + + "\n" + + "top to bottom direction\n" + + "\n" + + "!include <C4/C4>\n" + + "!include <C4/C4_Context>\n" + + "!include <C4/C4_Deployment>\n" + + "\n" + + "Deployment_Node(Default.Deploymentnode, \"Deployment node\", $type=\"\", $descr=\"\", $tags=\"\", $link=\"\") {\n" + + " Deployment_Node(Default.Deploymentnode.Infrastructurenode, \"Infrastructure node\", $type=\"technology\", $descr=\"description\", $tags=\"\", $link=\"\")\n" + + "}\n" + + "\n" + + "\n" + + "SHOW_LEGEND(true)\n" + + "@enduml", diagram.getDefinition()); + } + + @Test + public void test_printProperties() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("SoftwareSystem"); + Container container1 = softwareSystem.addContainer("Container 1"); + container1.addProperty("structurizr.dsl.identifier", "container1"); + container1.addProperty("IP", "127.0.0.1"); + container1.addProperty("Region", "East"); + Container container2 = softwareSystem.addContainer("Container 2"); + container1.addProperty("structurizr.dsl.identifier", "container2"); + container2.addProperty("Region", "West"); + container2.addProperty("IP", "127.0.0.2"); + Relationship relationship = container1.uses(container2, ""); + relationship.addProperty("Prop1", "Value1"); + relationship.addProperty("Prop2", "Value2"); + + workspace.getViews().getConfiguration().addProperty(C4PlantUMLExporter.C4PLANTUML_ELEMENT_PROPERTIES_PROPERTY, Boolean.TRUE.toString()); + workspace.getViews().getConfiguration().addProperty(C4PlantUMLExporter.C4PLANTUML_RELATIONSHIP_PROPERTIES_PROPERTY, Boolean.TRUE.toString()); + ContainerView view = workspace.getViews().createContainerView(softwareSystem, "containerView", ""); + view.addDefaultElements(); + + Diagram diagram = new C4PlantUMLExporter().export(view); + + String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/c4plantuml/printProperties-containerView.puml")); + assertEquals(expected, diagram.getDefinition()); + } + + @Test + public void test_deploymentViewPrintProperties() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + + DeploymentNode deploymentNode = workspace.getModel().addDeploymentNode("Deployment node"); + deploymentNode.addProperty("Prop1", "Value1"); + + InfrastructureNode infraNode = deploymentNode.addInfrastructureNode("Infrastructure node", "description", "technology"); + infraNode.addProperty("Prop2", "Value2"); + + workspace.getViews().getConfiguration().addProperty(C4PlantUMLExporter.C4PLANTUML_ELEMENT_PROPERTIES_PROPERTY, Boolean.TRUE.toString()); + DeploymentView deploymentView = workspace.getViews().createDeploymentView("deploymentView", ""); + deploymentView.addDefaultElements(); + + Diagram diagram = new C4PlantUMLExporter().export(deploymentView); + + String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/c4plantuml/printProperties-deploymentView.puml")); + assertEquals(expected, diagram.getDefinition()); + } + + @Test + public void test_legendAndStereotypes() { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getModel().addSoftwareSystem("Name"); + + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "Description"); + view.addDefaultElements(); + + // legend (true) and stereotypes (false) + view.addProperty(C4PlantUMLExporter.C4PLANTUML_LEGEND_PROPERTY, "true"); + view.addProperty(C4PlantUMLExporter.C4PLANTUML_STEREOTYPES_PROPERTY, "false"); + Diagram diagram = new C4PlantUMLExporter().export(view); + assertEquals("@startuml\n" + + "set separator none\n" + + "title System Landscape\n" + + "\n" + + "top to bottom direction\n" + + "\n" + + "!include <C4/C4>\n" + + "!include <C4/C4_Context>\n" + + "\n" + + "System(Name, \"Name\", $descr=\"\", $tags=\"\", $link=\"\")\n" + + "\n" + + "\n" + + "SHOW_LEGEND(true)\n" + + "@enduml", diagram.getDefinition()); + + // legend (true) and stereotypes (true) + view.addProperty(C4PlantUMLExporter.C4PLANTUML_LEGEND_PROPERTY, "true"); + view.addProperty(C4PlantUMLExporter.C4PLANTUML_STEREOTYPES_PROPERTY, "true"); + diagram = new C4PlantUMLExporter().export(view); + assertEquals("@startuml\n" + + "set separator none\n" + + "title System Landscape\n" + + "\n" + + "top to bottom direction\n" + + "\n" + + "!include <C4/C4>\n" + + "!include <C4/C4_Context>\n" + + "\n" + + "System(Name, \"Name\", $descr=\"\", $tags=\"\", $link=\"\")\n" + + "\n" + + "\n" + + "SHOW_LEGEND(false)\n" + + "@enduml", diagram.getDefinition()); + + // legend (false) and stereotypes (false) + view.addProperty(C4PlantUMLExporter.C4PLANTUML_LEGEND_PROPERTY, "false"); + view.addProperty(C4PlantUMLExporter.C4PLANTUML_STEREOTYPES_PROPERTY, "false"); + diagram = new C4PlantUMLExporter().export(view); + assertEquals("@startuml\n" + + "set separator none\n" + + "title System Landscape\n" + + "\n" + + "top to bottom direction\n" + + "\n" + + "!include <C4/C4>\n" + + "!include <C4/C4_Context>\n" + + "\n" + + "System(Name, \"Name\", $descr=\"\", $tags=\"\", $link=\"\")\n" + + "\n" + + "\n" + + "hide stereotypes\n" + + "@enduml", diagram.getDefinition()); + + // legend (false) and stereotypes (true) + view.addProperty(C4PlantUMLExporter.C4PLANTUML_LEGEND_PROPERTY, "false"); + view.addProperty(C4PlantUMLExporter.C4PLANTUML_STEREOTYPES_PROPERTY, "true"); + diagram = new C4PlantUMLExporter().export(view); + assertEquals("@startuml\n" + + "set separator none\n" + + "title System Landscape\n" + + "\n" + + "top to bottom direction\n" + + "\n" + + "!include <C4/C4>\n" + + "!include <C4/C4_Context>\n" + + "\n" + + "System(Name, \"Name\", $descr=\"\", $tags=\"\", $link=\"\")\n" + + "\n" + + "\n" + + "show stereotypes\n" + + "@enduml", diagram.getDefinition()); + } + + @Test + public void test_renderContainerShapes() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); + + Container container1 = softwareSystem.addContainer("Default Container"); + Container container2 = softwareSystem.addContainer("Cylinder Container"); + Container container3 = softwareSystem.addContainer("Pipe Container"); + Container container4 = softwareSystem.addContainer("Robot Container"); + container2.addTags("Cylinder"); + container3.addTags("Pipe"); + container4.addTags("Robot"); // Just an example of a shape that has no C4-PlantUML mapping. + + ContainerView containerView = workspace.getViews().createContainerView(softwareSystem, "Containers", ""); + containerView.add(container1); + containerView.add(container2); + containerView.add(container3); + containerView.add(container4); + + workspace.getViews().getConfiguration().getStyles().addElementStyle("Cylinder").shape(Shape.Cylinder); + workspace.getViews().getConfiguration().getStyles().addElementStyle("Pipe").shape(Shape.Pipe); + workspace.getViews().getConfiguration().getStyles().addElementStyle("Robot").shape(Shape.Robot); + + Diagram diagram = new C4PlantUMLExporter().export(containerView); + assertEquals("@startuml\n" + + "set separator none\n" + + "title Software System - Containers\n" + + "\n" + + "top to bottom direction\n" + + "\n" + + "!include <C4/C4>\n" + + "!include <C4/C4_Context>\n" + + "!include <C4/C4_Container>\n" + + "\n" + + "System_Boundary(\"SoftwareSystem_boundary\", \"Software System\", $tags=\"\") {\n" + + " Container(SoftwareSystem.DefaultContainer, \"Default Container\", $techn=\"\", $descr=\"\", $tags=\"\", $link=\"\")\n" + + " ContainerDb(SoftwareSystem.CylinderContainer, \"Cylinder Container\", $techn=\"\", $descr=\"\", $tags=\"\", $link=\"\")\n" + + " ContainerQueue(SoftwareSystem.PipeContainer, \"Pipe Container\", $techn=\"\", $descr=\"\", $tags=\"\", $link=\"\")\n" + + " Container(SoftwareSystem.RobotContainer, \"Robot Container\", $techn=\"\", $descr=\"\", $tags=\"\", $link=\"\")\n" + + "}\n" + + "\n" + + "\n" + + "SHOW_LEGEND(true)\n" + + "@enduml", diagram.getDefinition()); + } + + @Test + public void test_renderComponentShapes() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + + Component component1 = container.addComponent("Default Component"); + Component component2 = container.addComponent("Cylinder Component"); + Component component3 = container.addComponent("Pipe Component"); + Component component4 = container.addComponent("Robot Component"); + + component2.addTags("Cylinder"); + component3.addTags("Pipe"); + component4.addTags("Robot"); // Just an example of a shape that has no C4-PlantUML mapping. + + ContainerView containerView = workspace.getViews().createContainerView(softwareSystem, "Containers", ""); + ComponentView componentView = workspace.getViews().createComponentView(container, "Components", ""); + componentView.add(component1); + componentView.add(component2); + componentView.add(component3); + componentView.add(component4); + + workspace.getViews().getConfiguration().getStyles().addElementStyle("Cylinder").shape(Shape.Cylinder); + workspace.getViews().getConfiguration().getStyles().addElementStyle("Pipe").shape(Shape.Pipe); + workspace.getViews().getConfiguration().getStyles().addElementStyle("Robot").shape(Shape.Robot); + + Diagram diagram = new C4PlantUMLExporter().export(componentView); + assertEquals("@startuml\n" + + "set separator none\n" + + "title Software System - Container - Components\n" + + "\n" + + "top to bottom direction\n" + + "\n" + + "!include <C4/C4>\n" + + "!include <C4/C4_Context>\n" + + "!include <C4/C4_Component>\n" + + "\n" + + "Container_Boundary(\"SoftwareSystem.Container_boundary\", \"Container\", $tags=\"\") {\n" + + " Component(SoftwareSystem.Container.DefaultComponent, \"Default Component\", $techn=\"\", $descr=\"\", $tags=\"\", $link=\"\")\n" + + " ComponentDb(SoftwareSystem.Container.CylinderComponent, \"Cylinder Component\", $techn=\"\", $descr=\"\", $tags=\"\", $link=\"\")\n" + + " ComponentQueue(SoftwareSystem.Container.PipeComponent, \"Pipe Component\", $techn=\"\", $descr=\"\", $tags=\"\", $link=\"\")\n" + + " Component(SoftwareSystem.Container.RobotComponent, \"Robot Component\", $techn=\"\", $descr=\"\", $tags=\"\", $link=\"\")\n" + + "}\n" + + "\n" + + "\n" + + "SHOW_LEGEND(true)\n" + + "@enduml", diagram.getDefinition()); + } + + @Test + public void testFont() { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getModel().addPerson("User"); + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "Description"); + view.addAllElements(); + workspace.getViews().getConfiguration().getBranding().setFont(new Font("Courier")); + + Diagram diagram = new C4PlantUMLExporter().export(view); + assertEquals("@startuml\n" + + "set separator none\n" + + "title System Landscape\n" + + "\n" + + "skinparam {\n" + + " defaultFontName \"Courier\"\n" + + "}\n" + + "top to bottom direction\n" + + "\n" + + "!include <C4/C4>\n" + + "!include <C4/C4_Context>\n" + + "\n" + + "Person(User, \"User\", $descr=\"\", $tags=\"\", $link=\"\")\n" + + "\n" + + "\n" + + "SHOW_LEGEND(true)\n" + + "@enduml", diagram.getDefinition().toString()); + + } + + @Test + public void stdlib_false() { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getModel().addPerson("User"); + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "Description"); + view.addAllElements(); + view.addProperty(C4PlantUMLExporter.C4PLANTUML_STANDARD_LIBRARY_PROPERTY, "false"); + + Diagram diagram = new C4PlantUMLExporter().export(view); + assertEquals("@startuml\n" + + "set separator none\n" + + "title System Landscape\n" + + "\n" + + "top to bottom direction\n" + + "\n" + + "!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4.puml\n" + + "!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Context.puml\n" + + "\n" + + "Person(User, \"User\", $descr=\"\", $tags=\"\", $link=\"\")\n" + + "\n" + + "\n" + + "SHOW_LEGEND(true)\n" + + "@enduml", diagram.getDefinition().toString()); + + } + + @Test + public void componentWithoutTechnology() { + Workspace workspace = new Workspace("Name", "Description"); + Container container = workspace.getModel().addSoftwareSystem("Name").addContainer("Name"); + container.addComponent("Name").setDescription("Description"); + + ComponentView view = workspace.getViews().createComponentView(container, "key", "Description"); + view.addAllElements(); + + Diagram diagram = new C4PlantUMLExporter().export(view); + assertEquals("@startuml\n" + + "set separator none\n" + + "title Name - Name - Components\n" + + "\n" + + "top to bottom direction\n" + + "\n" + + "!include <C4/C4>\n" + + "!include <C4/C4_Context>\n" + + "!include <C4/C4_Component>\n" + + "\n" + + "Container_Boundary(\"Name.Name_boundary\", \"Name\", $tags=\"\") {\n" + + " Component(Name.Name.Name, \"Name\", $techn=\"\", $descr=\"Description\", $tags=\"\", $link=\"\")\n" + + "}\n" + + "\n" + + "\n" + + "SHOW_LEGEND(true)\n" + + "@enduml", diagram.getDefinition()); + } + + @Test + public void borderStyling() { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getModel().addSoftwareSystem("Name"); + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "Description"); + view.addAllElements(); + workspace.getViews().getConfiguration().addProperty(C4PlantUMLExporter.C4PLANTUML_TAGS_PROPERTY, "true"); + workspace.getViews().getConfiguration().getStyles().addElementStyle(Tags.ELEMENT).stroke("green").border(Border.Dashed).strokeWidth(2); + + Diagram diagram = new C4PlantUMLExporter().export(view); + assertEquals("@startuml\n" + + "set separator none\n" + + "title System Landscape\n" + + "\n" + + "top to bottom direction\n" + + "\n" + + "!include <C4/C4>\n" + + "!include <C4/C4_Context>\n" + + "\n" + + "AddElementTag(\"Element\", $bgColor=\"#dddddd\", $borderColor=\"#008000\", $fontColor=\"#000000\", $sprite=\"\", $shadowing=\"\", $borderStyle=\"Dashed\", $borderThickness=\"2\")\n" + + "\n" + + "System(Name, \"Name\", $descr=\"\", $tags=\"Element\", $link=\"\")\n" + + "\n" + + "\n" + + "SHOW_LEGEND(true)\n" + + "@enduml", diagram.getDefinition()); + } + + @Test + public void elementWithUrl() { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getModel().addSoftwareSystem("Name").setUrl("https://example.com"); + + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "Description"); + view.addAllElements(); + + Diagram diagram = new C4PlantUMLExporter().export(view); + assertEquals(""" + @startuml + set separator none + title System Landscape + + top to bottom direction + + !include <C4/C4> + !include <C4/C4_Context> + + System(Name, "Name", $descr="", $tags="", $link="https://example.com") + + + SHOW_LEGEND(true) + @enduml""", diagram.getDefinition()); + } + +} \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java new file mode 100644 index 000000000..0acf637cb --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java @@ -0,0 +1,1031 @@ +package com.structurizr.export.plantuml; + +import com.structurizr.Workspace; +import com.structurizr.export.AbstractExporterTests; +import com.structurizr.export.Diagram; +import com.structurizr.model.*; +import com.structurizr.util.WorkspaceUtils; +import com.structurizr.view.*; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.util.Collection; + +import static org.junit.jupiter.api.Assertions.*; + +public class StructurizrPlantUMLDiagramExporterTests extends AbstractExporterTests { + + @Test + public void test_BigBankPlcExample() throws Exception { + Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/structurizr-36141-workspace.json")); + workspace.getViews().getConfiguration().addProperty(StructurizrPlantUMLExporter.PLANTUML_ANIMATION_PROPERTY, "true"); + + StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(); + + Collection<Diagram> diagrams = exporter.export(workspace); + assertEquals(7, diagrams.size()); + + Diagram diagram = diagrams.stream().filter(d -> d.getKey().equals("SystemLandscape")).findFirst().get(); + String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemLandscape.puml")); + assertEquals(expected, diagram.getDefinition()); + assertEquals(3, diagram.getFrames().size()); + + //assertEquals("", diagram.getLegend().getDefinition()); + + diagram = diagrams.stream().filter(d -> d.getKey().equals("SystemContext")).findFirst().get(); + expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemContext.puml")); + assertEquals(expected, diagram.getDefinition()); + assertEquals(4, diagram.getFrames().size()); + + diagram = diagrams.stream().filter(d -> d.getKey().equals("Containers")).findFirst().get(); + expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/36141-Containers.puml")); + assertEquals(expected, diagram.getDefinition()); + assertEquals(6, diagram.getFrames().size()); + + diagram = diagrams.stream().filter(d -> d.getKey().equals("Components")).findFirst().get(); + expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/36141-Components.puml")); + assertEquals(expected, diagram.getDefinition()); + assertEquals(4, diagram.getFrames().size()); + + diagram = diagrams.stream().filter(d -> d.getKey().equals("SignIn")).findFirst().get(); + expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/36141-SignIn.puml")); + assertEquals(expected, diagram.getDefinition()); + assertEquals(6, diagram.getFrames().size()); + + diagram = diagrams.stream().filter(md -> md.getKey().equals("DevelopmentDeployment")).findFirst().get(); + expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/36141-DevelopmentDeployment.puml")); + assertEquals(expected, diagram.getDefinition()); + assertEquals(4, diagram.getFrames().size()); + + diagram = diagrams.stream().filter(md -> md.getKey().equals("LiveDeployment")).findFirst().get(); + expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/36141-LiveDeployment.puml")); + assertEquals(expected, diagram.getDefinition()); + assertEquals(6, diagram.getFrames().size()); + + // and the sequence diagram version + workspace.getViews().getConfiguration().addProperty(exporter.PLANTUML_SEQUENCE_DIAGRAM_PROPERTY, "true"); + diagrams = exporter.export(workspace); + diagram = diagrams.stream().filter(d -> d.getKey().equals("SignIn")).findFirst().get(); + expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/36141-SignIn-sequence.puml")); + assertEquals(expected, diagram.getDefinition()); + } + + @Test + public void test_AmazonWebServicesExample() throws Exception { + Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/structurizr-54915-workspace.json")); + ThemeUtils.loadThemes(workspace); + workspace.getViews().getDeploymentViews().iterator().next().enableAutomaticLayout(AutomaticLayout.RankDirection.LeftRight, 300, 300); + + StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(); + Collection<Diagram> diagrams = exporter.export(workspace); + assertEquals(1, diagrams.size()); + + Diagram diagram = diagrams.stream().findFirst().get(); + String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/54915-AmazonWebServicesDeployment.puml")); + assertEquals(expected, diagram.getDefinition()); + + expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/54915-AmazonWebServicesDeployment-Legend.puml")); + assertEquals(expected, diagram.getLegend().getDefinition()); + } + + @Test + public void test_GroupsExample() throws Exception { + Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/groups.json")); + ThemeUtils.loadThemes(workspace); + + StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(); + Collection<Diagram> diagrams = exporter.export(workspace); + assertEquals(3, diagrams.size()); + + Diagram diagram = diagrams.stream().filter(md -> md.getKey().equals("SystemLandscape")).findFirst().get(); + String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/groups-SystemLandscape.puml")); + assertEquals(expected, diagram.getDefinition()); + + diagram = diagrams.stream().filter(md -> md.getKey().equals("Containers")).findFirst().get(); + expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/groups-Containers.puml")); + assertEquals(expected, diagram.getDefinition()); + + diagram = diagrams.stream().filter(md -> md.getKey().equals("Components")).findFirst().get(); + expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/groups-Components.puml")); + assertEquals(expected, diagram.getDefinition()); + } + + @Test + public void test_NestedGroupsExample() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getModel().addProperty("structurizr.groupSeparator", "/"); + + SoftwareSystem a = workspace.getModel().addSoftwareSystem("Team 1"); + a.setGroup("Organisation 1/Department 1/Team 1"); + + SoftwareSystem b = workspace.getModel().addSoftwareSystem("Team 2"); + b.setGroup("Organisation 1/Department 1/Team 2"); + + SoftwareSystem c = workspace.getModel().addSoftwareSystem("Organisation 1"); + c.setGroup("Organisation 1"); + + SoftwareSystem d = workspace.getModel().addSoftwareSystem("Organisation 2"); + d.setGroup("Organisation 2"); + + SoftwareSystem e = workspace.getModel().addSoftwareSystem("Department 1"); + e.setGroup("Organisation 1/Department 1"); + + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("SystemLandscape", "Description"); + view.addAllElements(); + + workspace.getViews().getConfiguration().getStyles().addElementStyle("Group:Organisation 1/Department 1/Team 1").color("#ff0000"); + workspace.getViews().getConfiguration().getStyles().addElementStyle("Group:Organisation 1/Department 1/Team 2").color("#0000ff"); + + StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(); + Collection<Diagram> diagrams = exporter.export(workspace); + + Diagram diagram = diagrams.stream().filter(md -> md.getKey().equals("SystemLandscape")).findFirst().get(); + String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/nested-groups.puml")); + assertEquals(expected, diagram.getDefinition()); + } + + @Test + public void test_renderGroupStyles() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getModel().addPerson("User 1").setGroup("Group 1"); + workspace.getModel().addPerson("User 2").setGroup("Group 2"); + workspace.getModel().addPerson("User 3").setGroup("Group 3"); + + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", ""); + view.addDefaultElements(); + + workspace.getViews().getConfiguration().getStyles().addElementStyle("Group:Group 1").color("#111111").icon("https://example.com/icon1.png"); + workspace.getViews().getConfiguration().getStyles().addElementStyle("Group:Group 2").color("#222222").icon("https://example.com/icon2.png"); + + StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter() { + @Override + protected double calculateIconScale(String iconUrl) { + return 1.0; + } + }; + + Diagram diagram = exporter.export(view); + String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/group-styles-1.puml")); + assertEquals(expected, diagram.getDefinition()); + + workspace.getViews().getConfiguration().getStyles().addElementStyle("Group").color("#aabbcc"); + + diagram = exporter.export(view); + expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/group-styles-2.puml")); + assertEquals(expected, diagram.getDefinition()); + } + + @Test + public void test_renderContainerDiagramWithExternalContainers() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystem1 = workspace.getModel().addSoftwareSystem("Software System 1"); + Container container1 = softwareSystem1.addContainer("Container 1"); + SoftwareSystem softwareSystem2 = workspace.getModel().addSoftwareSystem("Software System 2"); + Container container2 = softwareSystem2.addContainer("Container 2"); + + container1.uses(container2, "Uses"); + + ContainerView containerView = workspace.getViews().createContainerView(softwareSystem1, "Containers", ""); + containerView.add(container1); + containerView.add(container2); + + Diagram diagram = new StructurizrPlantUMLExporter().export(containerView); + assertEquals("@startuml\n" + + "set separator none\n" + + "title Software System 1 - Containers\n" + + "\n" + + "top to bottom direction\n" + + "\n" + + "skinparam {\n" + + " arrowFontSize 10\n" + + " defaultTextAlignment center\n" + + " wrapWidth 200\n" + + " maxMessageSize 100\n" + + "}\n" + + "\n" + + "hide stereotype\n" + + "\n" + + "skinparam rectangle<<SoftwareSystem1.Container1>> {\n" + + " BackgroundColor #dddddd\n" + + " FontColor #000000\n" + + " BorderColor #9a9a9a\n" + + " shadowing false\n" + + "}\n" + + "skinparam rectangle<<SoftwareSystem2.Container2>> {\n" + + " BackgroundColor #dddddd\n" + + " FontColor #000000\n" + + " BorderColor #9a9a9a\n" + + " shadowing false\n" + + "}\n" + + "skinparam rectangle<<SoftwareSystem1>> {\n" + + " BorderColor #9a9a9a\n" + + " FontColor #9a9a9a\n" + + " shadowing false\n" + + "}\n" + + "skinparam rectangle<<SoftwareSystem2>> {\n" + + " BorderColor #9a9a9a\n" + + " FontColor #9a9a9a\n" + + " shadowing false\n" + + "}\n" + + "\n" + + "rectangle \"Software System 1\\n<size:10>[Software System]</size>\" <<SoftwareSystem1>> {\n" + + " rectangle \"==Container 1\\n<size:10>[Container]</size>\" <<SoftwareSystem1.Container1>> as SoftwareSystem1.Container1\n" + + "}\n" + + "\n" + + "rectangle \"Software System 2\\n<size:10>[Software System]</size>\" <<SoftwareSystem2>> {\n" + + " rectangle \"==Container 2\\n<size:10>[Container]</size>\" <<SoftwareSystem2.Container2>> as SoftwareSystem2.Container2\n" + + "}\n" + + "\n" + + "SoftwareSystem1.Container1 .[#707070,thickness=2].> SoftwareSystem2.Container2 : \"<color:#707070>Uses\"\n" + + "@enduml", diagram.getDefinition()); + } + + @Test + public void test_renderComponentDiagramWithExternalComponents() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + + SoftwareSystem softwareSystem1 = workspace.getModel().addSoftwareSystem("Software System 1"); + Container container1 = softwareSystem1.addContainer("Container 1"); + Component component1 = container1.addComponent("Component 1"); + Component component2 = container1.addComponent("Component 2"); + + SoftwareSystem softwareSystem2 = workspace.getModel().addSoftwareSystem("Software System 2"); + Container container2 = softwareSystem2.addContainer("Container 2"); + Component component3 = container2.addComponent("Component 3"); + + component1.uses(component2, "Uses"); + component2.uses(component3, "Uses"); + + ComponentView componentView = workspace.getViews().createComponentView(container1, "Components", ""); + componentView.add(component1); + componentView.add(component2); + componentView.add(component3); + + Diagram diagram = new StructurizrPlantUMLExporter().export(componentView); + String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/component-view-with-external-components-1.puml")); + assertEquals(expected, diagram.getDefinition()); + } + + @Test + public void test_renderComponentDiagramWithExternalComponentsAndSoftwareSystemBoundariesIncluded() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + + SoftwareSystem softwareSystem1 = workspace.getModel().addSoftwareSystem("Software System 1"); + Container container1 = softwareSystem1.addContainer("Container 1"); + Component component1 = container1.addComponent("Component 1"); + Component component2 = container1.addComponent("Component 2"); + + SoftwareSystem softwareSystem2 = workspace.getModel().addSoftwareSystem("Software System 2"); + Container container2 = softwareSystem2.addContainer("Container 2"); + Component component3 = container2.addComponent("Component 3"); + + component1.uses(component2, "Uses"); + component2.uses(component3, "Uses"); + + ComponentView componentView = workspace.getViews().createComponentView(container1, "Components", ""); + componentView.add(component1); + componentView.add(component2); + componentView.add(component3); + componentView.addProperty("structurizr.softwareSystemBoundaries", "true"); + + Diagram diagram = new StructurizrPlantUMLExporter().export(componentView); + String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/component-view-with-external-components-2.puml")); + assertEquals(expected, diagram.getDefinition()); + } + + @Test + public void test_renderDynamicDiagramWithExternalContainers() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystem1 = workspace.getModel().addSoftwareSystem("Software System 1"); + Container container1 = softwareSystem1.addContainer("Container 1"); + SoftwareSystem softwareSystem2 = workspace.getModel().addSoftwareSystem("Software System 2"); + Container container2 = softwareSystem2.addContainer("Container 2"); + + container1.uses(container2, "Uses"); + + DynamicView dynamicView = workspace.getViews().createDynamicView(softwareSystem1, "Dynamic", ""); + dynamicView.add(container1, container2); + + Diagram diagram = new StructurizrPlantUMLExporter().export(dynamicView); + assertEquals("@startuml\n" + + "set separator none\n" + + "title Software System 1 - Dynamic\n" + + "\n" + + "top to bottom direction\n" + + "\n" + + "skinparam {\n" + + " arrowFontSize 10\n" + + " defaultTextAlignment center\n" + + " wrapWidth 200\n" + + " maxMessageSize 100\n" + + "}\n" + + "\n" + + "hide stereotype\n" + + "\n" + + "skinparam rectangle<<SoftwareSystem1.Container1>> {\n" + + " BackgroundColor #dddddd\n" + + " FontColor #000000\n" + + " BorderColor #9a9a9a\n" + + " shadowing false\n" + + "}\n" + + "skinparam rectangle<<SoftwareSystem2.Container2>> {\n" + + " BackgroundColor #dddddd\n" + + " FontColor #000000\n" + + " BorderColor #9a9a9a\n" + + " shadowing false\n" + + "}\n" + + "skinparam rectangle<<SoftwareSystem1>> {\n" + + " BorderColor #9a9a9a\n" + + " FontColor #9a9a9a\n" + + " shadowing false\n" + + "}\n" + + "skinparam rectangle<<SoftwareSystem2>> {\n" + + " BorderColor #9a9a9a\n" + + " FontColor #9a9a9a\n" + + " shadowing false\n" + + "}\n" + + "\n" + + "rectangle \"Software System 1\\n<size:10>[Software System]</size>\" <<SoftwareSystem1>> {\n" + + " rectangle \"==Container 1\\n<size:10>[Container]</size>\" <<SoftwareSystem1.Container1>> as SoftwareSystem1.Container1\n" + + "}\n" + + "\n" + + "rectangle \"Software System 2\\n<size:10>[Software System]</size>\" <<SoftwareSystem2>> {\n" + + " rectangle \"==Container 2\\n<size:10>[Container]</size>\" <<SoftwareSystem2.Container2>> as SoftwareSystem2.Container2\n" + + "}\n" + + "\n" + + "SoftwareSystem1.Container1 .[#707070,thickness=2].> SoftwareSystem2.Container2 : \"<color:#707070>1. Uses\"\n" + + "@enduml", diagram.getDefinition()); + } + + @Test + public void test_renderDynamicDiagramWithExternalComponents() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + + SoftwareSystem softwareSystem1 = workspace.getModel().addSoftwareSystem("Software System 1"); + Container container1 = softwareSystem1.addContainer("Container 1"); + Component component1 = container1.addComponent("Component 1"); + Component component2 = container1.addComponent("Component 2"); + + SoftwareSystem softwareSystem2 = workspace.getModel().addSoftwareSystem("Software System 2"); + Container container2 = softwareSystem2.addContainer("Container 2"); + Component component3 = container2.addComponent("Component 3"); + + component1.uses(component2, "Uses"); + component2.uses(component3, "Uses"); + + DynamicView dynamicView = workspace.getViews().createDynamicView(container1, "Dynamic", ""); + dynamicView.add(component1, component2); + dynamicView.add(component2, component3); + + Diagram diagram = new StructurizrPlantUMLExporter().export(dynamicView); + String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-with-external-components-1.puml")); + assertEquals(expected, diagram.getDefinition()); + } + + @Test + public void test_renderDynamicDiagramWithExternalComponentsAndSoftwareSystemBoundariesIncluded() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + + SoftwareSystem softwareSystem1 = workspace.getModel().addSoftwareSystem("Software System 1"); + Container container1 = softwareSystem1.addContainer("Container 1"); + Component component1 = container1.addComponent("Component 1"); + Component component2 = container1.addComponent("Component 2"); + + SoftwareSystem softwareSystem2 = workspace.getModel().addSoftwareSystem("Software System 2"); + Container container2 = softwareSystem2.addContainer("Container 2"); + Component component3 = container2.addComponent("Component 3"); + + component1.uses(component2, "Uses"); + component2.uses(component3, "Uses"); + + DynamicView dynamicView = workspace.getViews().createDynamicView(container1, "Dynamic", ""); + dynamicView.add(component1, component2); + dynamicView.add(component2, component3); + dynamicView.addProperty("structurizr.softwareSystemBoundaries", "true"); + + Diagram diagram = new StructurizrPlantUMLExporter().export(dynamicView); + String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-with-external-components-2.puml")); + assertEquals(expected, diagram.getDefinition()); + } + + @Test + public void test_renderDiagramWithElementUrls() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); + softwareSystem.setUrl("https://structurizr.com"); + + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "Description"); + view.addDefaultElements(); + + Diagram diagram = new StructurizrPlantUMLExporter().export(view); + assertTrue(diagram.getDefinition().contains("rectangle \"==Software System\\n<size:10>[Software System]</size>\" <<SoftwareSystem>> as SoftwareSystem [[https://structurizr.com]]\n")); + } + + @Test + public void test_renderDiagramWithIncludes() { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getModel().addSoftwareSystem("Software System"); + + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "Description"); + view.addDefaultElements(); + + view.getViewSet().getConfiguration().addProperty(StructurizrPlantUMLExporter.PLANTUML_INCLUDES_PROPERTY, "styles.puml"); + + Diagram diagram = new StructurizrPlantUMLExporter().export(view); + assertTrue(diagram.getDefinition().contains("!include styles.puml\n")); + } + + @Test + public void test_renderDiagramWithNewLineCharacterInElementName() { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getModel().addSoftwareSystem("Software\nSystem"); + + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "Description"); + view.addDefaultElements(); + + Diagram diagram = new StructurizrPlantUMLExporter().export(view); + assertTrue(diagram.getDefinition().contains("rectangle \"==Software\\nSystem\\n<size:10>[Software System]</size>\" <<SoftwareSystem>> as SoftwareSystem")); + } + + @Test + public void test_renderCustomView() { + Workspace workspace = new Workspace("Name", "Description"); + Model model = workspace.getModel(); + + CustomElement a = model.addCustomElement("A"); + CustomElement b = model.addCustomElement("B", "Custom", "Description"); + a.uses(b, "Uses"); + + CustomView view = workspace.getViews().createCustomView("key", "Title", "Description"); + view.addDefaultElements(); + + Diagram diagram = new StructurizrPlantUMLExporter().export(view); + assertEquals("@startuml\nset separator none\n" + + "title Title\n" + + "\n" + + "top to bottom direction\n" + + "\n" + + "skinparam {\n" + + " arrowFontSize 10\n" + + " defaultTextAlignment center\n" + + " wrapWidth 200\n" + + " maxMessageSize 100\n" + + "}\n" + + "\n" + + "hide stereotype\n" + + "\n" + + "skinparam rectangle<<1>> {\n" + + " BackgroundColor #dddddd\n" + + " FontColor #000000\n" + + " BorderColor #9a9a9a\n" + + " shadowing false\n" + + "}\n" + + "skinparam rectangle<<2>> {\n" + + " BackgroundColor #dddddd\n" + + " FontColor #000000\n" + + " BorderColor #9a9a9a\n" + + " shadowing false\n" + + "}\n" + + "\n" + + "rectangle \"==A\" <<1>> as 1\n" + + "rectangle \"==B\\n<size:10>[Custom]</size>\\n\\nDescription\" <<2>> as 2\n" + + "\n" + + "1 .[#707070,thickness=2].> 2 : \"<color:#707070>Uses\"\n" + + "@enduml", diagram.getDefinition()); + } + + @Test + void renderWorkspaceWithUnicodeElementName() { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getModel().addPerson("Пользователь"); + workspace.getViews().createSystemLandscapeView("key", "Description").addDefaultElements(); + + String diagramDefinition = new StructurizrPlantUMLExporter().export(workspace).stream().findFirst().get().getDefinition(); + + assertTrue(diagramDefinition.contains("skinparam rectangle<<Пользователь>> {")); + assertTrue(diagramDefinition.contains("rectangle \"==Пользователь\\n<size:10>[Person]</size>\" <<Пользователь>> as Пользователь")); + } + + @Test + public void testLegend() { + Workspace workspace = new Workspace("Name", "Description"); + Model model = workspace.getModel(); + + CustomElement a = model.addCustomElement("A"); + a.addTags("Tag 1"); + CustomElement b = model.addCustomElement("B"); + b.addTags("Tag 2"); + a.uses(b, "...").addTags("Tag 3"); + b.uses(a, "...").addTags("Tag 4"); + + CustomView view = workspace.getViews().createCustomView("key", "Title", "Description"); + view.addDefaultElements(); + + Diagram diagram = new StructurizrPlantUMLExporter().export(view); + assertEquals("@startuml\nset separator none\n" + + "\n" + + "skinparam {\n" + + " shadowing false\n" + + " arrowFontSize 15\n" + + " defaultTextAlignment center\n" + + " wrapWidth 100\n" + + " maxMessageSize 100\n" + + "}\n" + + "hide stereotype\n" + + "\n" + + "skinparam rectangle<<_transparent>> {\n" + + " BorderColor transparent\n" + + " BackgroundColor transparent\n" + + " FontColor transparent\n" + + "}\n" + + "\n" + + "skinparam rectangle<<1>> {\n" + + " BackgroundColor #dddddd\n" + + " FontColor #000000\n" + + " BorderColor #9a9a9a\n" + + "}\n" + + "rectangle \"==Element\" <<1>>\n" + + "\n" + + "rectangle \".\" <<_transparent>> as 2\n" + + "2 .[#707070,thickness=2].> 2 : \"<color:#707070>Relationship\"\n" + + "\n" + + "\n" + + "@enduml", diagram.getLegend().getDefinition()); + + workspace.getViews().getConfiguration().getStyles().addElementStyle("Tag 1").background("#ff0000").color("#ffffff").shape(Shape.RoundedBox); + workspace.getViews().getConfiguration().getStyles().addElementStyle("Tag 2").background("#00ff00").color("#ffffff").shape(Shape.Hexagon); + workspace.getViews().getConfiguration().getStyles().addRelationshipStyle("Tag 3").color("#0000ff"); + workspace.getViews().getConfiguration().getStyles().addRelationshipStyle("Tag 4").color("#ff00ff").thickness(3).style(LineStyle.Solid); + + diagram = new StructurizrPlantUMLExporter().export(view); + assertEquals("@startuml\nset separator none\n" + + "\n" + + "skinparam {\n" + + " shadowing false\n" + + " arrowFontSize 15\n" + + " defaultTextAlignment center\n" + + " wrapWidth 100\n" + + " maxMessageSize 100\n" + + "}\n" + + "hide stereotype\n" + + "\n" + + "skinparam rectangle<<_transparent>> {\n" + + " BorderColor transparent\n" + + " BackgroundColor transparent\n" + + " FontColor transparent\n" + + "}\n" + + "\n" + + "skinparam rectangle<<1>> {\n" + + " BackgroundColor #ff0000\n" + + " FontColor #ffffff\n" + + " BorderColor #b20000\n" + + " roundCorner 20\n" + + "}\n" + + "rectangle \"==Tag 1\" <<1>>\n" + + "\n" + + "skinparam hexagon<<2>> {\n" + + " BackgroundColor #00ff00\n" + + " FontColor #ffffff\n" + + " BorderColor #00b200\n" + + "}\n" + + "hexagon \"==Tag 2\" <<2>>\n" + + "\n" + + "rectangle \".\" <<_transparent>> as 3\n" + + "3 .[#0000ff,thickness=2].> 3 : \"<color:#0000ff>Tag 3\"\n" + + "\n" + + "rectangle \".\" <<_transparent>> as 4\n" + + "4 -[#ff00ff,thickness=3]-> 4 : \"<color:#ff00ff>Tag 4\"\n" + + "\n" + + "\n" + + "@enduml", diagram.getLegend().getDefinition()); + } + + @Test + public void staticDiagramsAreUnchangedWhenSequenceDiagramsAreEnabled() { + Workspace workspace = new Workspace("Name", "Description"); + Model model = workspace.getModel(); + + model.addSoftwareSystem("Software System").setGroup("Group"); + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "Description"); + view.addAllElements(); + + StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(); + Diagram diagram; + String expected = "@startuml\n" + + "set separator none\n" + + "title System Landscape\n" + + "\n" + + "top to bottom direction\n" + + "\n" + + "skinparam {\n" + + " arrowFontSize 10\n" + + " defaultTextAlignment center\n" + + " wrapWidth 200\n" + + " maxMessageSize 100\n" + + "}\n" + + "\n" + + "hide stereotype\n" + + "\n" + + "skinparam rectangle<<SoftwareSystem>> {\n" + + " BackgroundColor #dddddd\n" + + " FontColor #000000\n" + + " BorderColor #9a9a9a\n" + + " shadowing false\n" + + "}\n" + + "\n" + + "rectangle \"Group\" <<group1>> {\n" + + " skinparam RectangleBorderColor<<group1>> #cccccc\n" + + " skinparam RectangleFontColor<<group1>> #cccccc\n" + + " skinparam RectangleBorderStyle<<group1>> dashed\n" + + "\n" + + " rectangle \"==Software System\\n<size:10>[Software System]</size>\" <<SoftwareSystem>> as SoftwareSystem\n" + + "}\n" + + "\n" + + "\n" + + "@enduml"; + + diagram = exporter.export(view); + assertEquals(expected, diagram.getDefinition()); + + workspace.getViews().getConfiguration().addProperty(StructurizrPlantUMLExporter.PLANTUML_SEQUENCE_DIAGRAM_PROPERTY, "true"); + + diagram = exporter.export(view); + assertEquals(expected, diagram.getDefinition()); + } + + @Test + public void testFont() { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getModel().addPerson("User"); + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "Description"); + view.addAllElements(); + workspace.getViews().getConfiguration().getBranding().setFont(new Font("Courier")); + + Diagram diagram = new StructurizrPlantUMLExporter().export(view); + assertEquals("@startuml\nset separator none\n" + + "title System Landscape\n" + + "\n" + + "top to bottom direction\n" + + "\n" + + "skinparam {\n" + + " arrowFontSize 10\n" + + " defaultTextAlignment center\n" + + " wrapWidth 200\n" + + " maxMessageSize 100\n" + + " defaultFontName \"Courier\"\n" + + "}\n" + + "\n" + + "hide stereotype\n" + + "\n" + + "skinparam rectangle<<User>> {\n" + + " BackgroundColor #dddddd\n" + + " FontColor #000000\n" + + " BorderColor #9a9a9a\n" + + " shadowing false\n" + + "}\n" + + "\n" + + "rectangle \"==User\\n<size:10>[Person]</size>\" <<User>> as User\n" + + "\n" + + "@enduml", diagram.getDefinition().toString()); + + assertEquals("@startuml\nset separator none\n" + + "\n" + + "skinparam {\n" + + " shadowing false\n" + + " arrowFontSize 15\n" + + " defaultTextAlignment center\n" + + " wrapWidth 100\n" + + " maxMessageSize 100\n" + + " defaultFontName \"Courier\"\n" + + "}\n" + + "hide stereotype\n" + + "\n" + + "skinparam rectangle<<_transparent>> {\n" + + " BorderColor transparent\n" + + " BackgroundColor transparent\n" + + " FontColor transparent\n" + + "}\n" + + "\n" + + "skinparam rectangle<<1>> {\n" + + " BackgroundColor #dddddd\n" + + " FontColor #000000\n" + + " BorderColor #9a9a9a\n" + + "}\n" + + "rectangle \"==Element\" <<1>>\n" + + "\n" + + "\n" + + "@enduml", diagram.getLegend().getDefinition()); + } + + @Test + public void dynamicView_UnscopedWithGroups() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystemA = workspace.getModel().addSoftwareSystem("A"); + softwareSystemA.setGroup("Group 1"); + SoftwareSystem softwareSystemB = workspace.getModel().addSoftwareSystem("B"); + softwareSystemB.setGroup("Group 2"); + softwareSystemA.uses(softwareSystemB, "Uses"); + + DynamicView view = workspace.getViews().createDynamicView("key", "Description"); + view.add(softwareSystemA, softwareSystemB); + + StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(); + Diagram diagram = exporter.export(view); + String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-unscoped-with-groups.puml")); + assertEquals(expected, diagram.getDefinition()); + } + + @Test + public void dynamicView_SoftwareSystemScopedWithGroups() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystemA = workspace.getModel().addSoftwareSystem("A"); + Container containerA = softwareSystemA.addContainer("A"); + containerA.setGroup("Group 1"); + SoftwareSystem softwareSystemB = workspace.getModel().addSoftwareSystem("B"); + Container containerB = softwareSystemB.addContainer("B"); + containerB.setGroup("Group 2"); + containerA.uses(containerB, "Uses"); + + DynamicView view = workspace.getViews().createDynamicView(softwareSystemA, "key", "Description"); + view.add(containerA, containerB); + + StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(); + Diagram diagram = exporter.export(view); + String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-software-system-scoped-with-groups.puml")); + assertEquals(expected, diagram.getDefinition()); + } + + @Test + public void dynamicView_ContainerScopedWithGroups() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystemA = workspace.getModel().addSoftwareSystem("A"); + Container containerA = softwareSystemA.addContainer("A"); + Component componentA = containerA.addComponent("A"); + componentA.setGroup("Group 1"); + SoftwareSystem softwareSystemB = workspace.getModel().addSoftwareSystem("B"); + Container containerB = softwareSystemB.addContainer("B"); + Component componentB = containerB.addComponent("B"); + componentB.setGroup("Group 2"); + componentA.uses(componentB, "Uses"); + + DynamicView view = workspace.getViews().createDynamicView(containerA, "key", "Description"); + view.add(componentA, componentB); + + StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(); + Diagram diagram = exporter.export(view); + String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-container-scoped-with-groups.puml")); + assertEquals(expected, diagram.getDefinition()); + } + + @Test + public void test_writeContainerViewWithGroupedElements_WithAndWithoutAGroupSeparator() { + Workspace workspace = new Workspace("Name", ""); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", ""); + Container container1 = softwareSystem.addContainer("Container 1"); + container1.setGroup("Group 1"); + Container container2 = softwareSystem.addContainer("Container 2"); + container2.setGroup("Group 2"); + + ContainerView view = workspace.getViews().createContainerView(softwareSystem, "Containers", ""); + view.addAllElements(); + + String expectedResult = "@startuml\n" + + "set separator none\n" + + "title Software System - Containers\n" + + "\n" + + "top to bottom direction\n" + + "\n" + + "skinparam {\n" + + " arrowFontSize 10\n" + + " defaultTextAlignment center\n" + + " wrapWidth 200\n" + + " maxMessageSize 100\n" + + "}\n" + + "\n" + + "hide stereotype\n" + + "\n" + + "skinparam rectangle<<SoftwareSystem.Container1>> {\n" + + " BackgroundColor #dddddd\n" + + " FontColor #000000\n" + + " BorderColor #9a9a9a\n" + + " shadowing false\n" + + "}\n" + + "skinparam rectangle<<SoftwareSystem.Container2>> {\n" + + " BackgroundColor #dddddd\n" + + " FontColor #000000\n" + + " BorderColor #9a9a9a\n" + + " shadowing false\n" + + "}\n" + + "skinparam rectangle<<SoftwareSystem>> {\n" + + " BorderColor #9a9a9a\n" + + " FontColor #9a9a9a\n" + + " shadowing false\n" + + "}\n" + + "\n" + + "rectangle \"Software System\\n<size:10>[Software System]</size>\" <<SoftwareSystem>> {\n" + + " rectangle \"Group 1\" <<group1>> {\n" + + " skinparam RectangleBorderColor<<group1>> #cccccc\n" + + " skinparam RectangleFontColor<<group1>> #cccccc\n" + + " skinparam RectangleBorderStyle<<group1>> dashed\n" + + "\n" + + " rectangle \"==Container 1\\n<size:10>[Container]</size>\" <<SoftwareSystem.Container1>> as SoftwareSystem.Container1\n" + + " }\n" + + "\n" + + " rectangle \"Group 2\" <<group2>> {\n" + + " skinparam RectangleBorderColor<<group2>> #cccccc\n" + + " skinparam RectangleFontColor<<group2>> #cccccc\n" + + " skinparam RectangleBorderStyle<<group2>> dashed\n" + + "\n" + + " rectangle \"==Container 2\\n<size:10>[Container]</size>\" <<SoftwareSystem.Container2>> as SoftwareSystem.Container2\n" + + " }\n" + + "\n" + + "}\n" + + "\n" + + "@enduml"; + + StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(); + Diagram diagram = exporter.export(view); + assertEquals(expectedResult, diagram.getDefinition()); + + // this should be the same + workspace.getModel().addProperty("structurizr.groupSeparator", "/"); + exporter = new StructurizrPlantUMLExporter(); + diagram = exporter.export(view); + assertEquals(expectedResult, diagram.getDefinition()); + } + + @Test + public void deploymentView_WithGroups() { + Workspace workspace = new Workspace("Name", ""); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); + + DeploymentNode server1 = workspace.getModel().addDeploymentNode("Server 1"); + server1.setGroup("Group 1"); + + InfrastructureNode infrastructureNode1 = server1.addInfrastructureNode("Infrastructure Node 1"); + InfrastructureNode infrastructureNode2 = server1.addInfrastructureNode("Infrastructure Node 2"); + + SoftwareSystemInstance softwareSystemInstance = server1.add(softwareSystem); + softwareSystemInstance.setGroup("Group 2"); + infrastructureNode2.setGroup("Group 2"); + + DeploymentView view = workspace.getViews().createDeploymentView("key", "Description"); + view.add(infrastructureNode1); + view.add(infrastructureNode2); + view.add(softwareSystemInstance); + + String expectedResult = "@startuml\n" + + "set separator none\n" + + "title Deployment - Default\n" + + "\n" + + "top to bottom direction\n" + + "\n" + + "skinparam {\n" + + " arrowFontSize 10\n" + + " defaultTextAlignment center\n" + + " wrapWidth 200\n" + + " maxMessageSize 100\n" + + "}\n" + + "\n" + + "hide stereotype\n" + + "\n" + + "skinparam rectangle<<Default.Server1.InfrastructureNode1>> {\n" + + " BackgroundColor #dddddd\n" + + " FontColor #000000\n" + + " BorderColor #9a9a9a\n" + + " shadowing false\n" + + "}\n" + + "skinparam rectangle<<Default.Server1.InfrastructureNode2>> {\n" + + " BackgroundColor #dddddd\n" + + " FontColor #000000\n" + + " BorderColor #9a9a9a\n" + + " shadowing false\n" + + "}\n" + + "skinparam rectangle<<Default.Server1>> {\n" + + " BackgroundColor #ffffff\n" + + " FontColor #000000\n" + + " BorderColor #888888\n" + + " shadowing false\n" + + "}\n" + + "skinparam rectangle<<Default.Server1.SoftwareSystem_1>> {\n" + + " BackgroundColor #dddddd\n" + + " FontColor #000000\n" + + " BorderColor #9a9a9a\n" + + " shadowing false\n" + + "}\n" + + "\n" + + "rectangle \"Group 1\" <<group1>> {\n" + + " skinparam RectangleBorderColor<<group1>> #cccccc\n" + + " skinparam RectangleFontColor<<group1>> #cccccc\n" + + " skinparam RectangleBorderStyle<<group1>> dashed\n" + + "\n" + + " rectangle \"Server 1\\n<size:10>[Deployment Node]</size>\" <<Default.Server1>> as Default.Server1 {\n" + + " rectangle \"Group 2\" <<group2>> {\n" + + " skinparam RectangleBorderColor<<group2>> #cccccc\n" + + " skinparam RectangleFontColor<<group2>> #cccccc\n" + + " skinparam RectangleBorderStyle<<group2>> dashed\n" + + "\n" + + " rectangle \"==Infrastructure Node 2\\n<size:10>[Infrastructure Node]</size>\" <<Default.Server1.InfrastructureNode2>> as Default.Server1.InfrastructureNode2\n" + + " rectangle \"==Software System\\n<size:10>[Software System]</size>\" <<Default.Server1.SoftwareSystem_1>> as Default.Server1.SoftwareSystem_1\n" + + " }\n" + + "\n" + + " rectangle \"==Infrastructure Node 1\\n<size:10>[Infrastructure Node]</size>\" <<Default.Server1.InfrastructureNode1>> as Default.Server1.InfrastructureNode1\n" + + " }\n" + + "\n" + + "}\n" + + "\n" + + "@enduml"; + + StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(); + Diagram diagram = exporter.export(view); + assertEquals(expectedResult, diagram.getDefinition()); + + // this should be the same + workspace.getModel().addProperty("structurizr.groupSeparator", "/"); + exporter = new StructurizrPlantUMLExporter(); + diagram = exporter.export(view); + assertEquals(expectedResult, diagram.getDefinition()); + } + + @Test + public void test_ElementInstanceUrl() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem a = workspace.getModel().addSoftwareSystem("A"); + a.setUrl("https://example.com/url1"); + SoftwareSystemInstance aInstance = workspace.getModel().addDeploymentNode("Node").add(a); + + DeploymentView view = workspace.getViews().createDeploymentView("deployment", "Default"); + view.add(aInstance); + + assertEquals(""" +@startuml +set separator none +title Deployment - Default + +top to bottom direction + +skinparam { + arrowFontSize 10 + defaultTextAlignment center + wrapWidth 200 + maxMessageSize 100 +} + +hide stereotype + +skinparam rectangle<<Default.Node.A_1>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} +skinparam rectangle<<Default.Node>> { + BackgroundColor #ffffff + FontColor #000000 + BorderColor #888888 + shadowing false +} + +rectangle "Node\\n<size:10>[Deployment Node]</size>" <<Default.Node>> as Default.Node { + rectangle "==A\\n<size:10>[Software System]</size>" <<Default.Node.A_1>> as Default.Node.A_1 [[https://example.com/url1]] +} + +@enduml""", new StructurizrPlantUMLExporter().export(view).getDefinition()); + + aInstance.setUrl("https://example.com/url2"); + assertEquals(""" +@startuml +set separator none +title Deployment - Default + +top to bottom direction + +skinparam { + arrowFontSize 10 + defaultTextAlignment center + wrapWidth 200 + maxMessageSize 100 +} + +hide stereotype + +skinparam rectangle<<Default.Node.A_1>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} +skinparam rectangle<<Default.Node>> { + BackgroundColor #ffffff + FontColor #000000 + BorderColor #888888 + shadowing false +} + +rectangle "Node\\n<size:10>[Deployment Node]</size>" <<Default.Node>> as Default.Node { + rectangle "==A\\n<size:10>[Software System]</size>" <<Default.Node.A_1>> as Default.Node.A_1 [[https://example.com/url2]] +} + +@enduml""", new StructurizrPlantUMLExporter().export(view).getDefinition()); } + +} \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Components.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Components.puml new file mode 100644 index 000000000..47509b6c7 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Components.puml @@ -0,0 +1,52 @@ +@startuml +set separator none +title Internet Banking System - API Application - Components + +top to bottom direction + +!include <C4/C4> +!include <C4/C4_Context> +!include <C4/C4_Container> +!include <C4/C4_Component> + +AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Component", $bgColor="#85bbf0", $borderColor="#5d82a8", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Container,Mobile App", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") + +AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") + +AddBoundaryTag("Container", $bgColor="#ffffff", $borderColor="#2e6295", $fontColor="#2e6295", $shadowing="", $borderStyle="Solid", $borderThickness="1") + +System(MainframeBankingSystem, "Mainframe Banking System", $descr="Stores all of the core banking information about customers, accounts, transactions, etc.", $tags="Software System,Existing System", $link="") +Container(InternetBankingSystem.SinglePageApplication, "Single-Page Application", $techn="JavaScript and Angular", $descr="Provides all of the Internet banking functionality to customers via their web browser.", $tags="Container,Web Browser", $link="") +System(EmailSystem, "E-mail System", $descr="The internal Microsoft Exchange e-mail system.", $tags="Software System,Existing System", $link="") +Container(InternetBankingSystem.MobileApp, "Mobile App", $techn="Xamarin", $descr="Provides a limited subset of the Internet banking functionality to customers via their mobile device.", $tags="Container,Mobile App", $link="") +ContainerDb(InternetBankingSystem.Database, "Database", $techn="Oracle Database Schema", $descr="Stores user registration information, hashed authentication credentials, access logs, etc.", $tags="Container,Database", $link="") + +Container_Boundary("InternetBankingSystem.APIApplication_boundary", "API Application", $tags="Container") { + Component(InternetBankingSystem.APIApplication.SignInController, "Sign In Controller", $techn="Spring MVC Rest Controller", $descr="Allows users to sign in to the Internet Banking System.", $tags="Component", $link="") + Component(InternetBankingSystem.APIApplication.AccountsSummaryController, "Accounts Summary Controller", $techn="Spring MVC Rest Controller", $descr="Provides customers with a summary of their bank accounts.", $tags="Component", $link="") + Component(InternetBankingSystem.APIApplication.ResetPasswordController, "Reset Password Controller", $techn="Spring MVC Rest Controller", $descr="Allows users to reset their passwords with a single use URL.", $tags="Component", $link="") + Component(InternetBankingSystem.APIApplication.SecurityComponent, "Security Component", $techn="Spring Bean", $descr="Provides functionality related to signing in, changing passwords, etc.", $tags="Component", $link="") + Component(InternetBankingSystem.APIApplication.MainframeBankingSystemFacade, "Mainframe Banking System Facade", $techn="Spring Bean", $descr="A facade onto the mainframe banking system.", $tags="Component", $link="") + Component(InternetBankingSystem.APIApplication.EmailComponent, "E-mail Component", $techn="Spring Bean", $descr="Sends e-mails to users.", $tags="Component", $link="") +} + +Rel(InternetBankingSystem.SinglePageApplication, InternetBankingSystem.APIApplication.SignInController, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") +Rel(InternetBankingSystem.SinglePageApplication, InternetBankingSystem.APIApplication.ResetPasswordController, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") +Rel(InternetBankingSystem.SinglePageApplication, InternetBankingSystem.APIApplication.AccountsSummaryController, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") +Rel(InternetBankingSystem.MobileApp, InternetBankingSystem.APIApplication.SignInController, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") +Rel(InternetBankingSystem.MobileApp, InternetBankingSystem.APIApplication.ResetPasswordController, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") +Rel(InternetBankingSystem.MobileApp, InternetBankingSystem.APIApplication.AccountsSummaryController, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") +Rel(InternetBankingSystem.APIApplication.SignInController, InternetBankingSystem.APIApplication.SecurityComponent, "Uses", $techn="", $tags="Relationship", $link="") +Rel(InternetBankingSystem.APIApplication.AccountsSummaryController, InternetBankingSystem.APIApplication.MainframeBankingSystemFacade, "Uses", $techn="", $tags="Relationship", $link="") +Rel(InternetBankingSystem.APIApplication.ResetPasswordController, InternetBankingSystem.APIApplication.SecurityComponent, "Uses", $techn="", $tags="Relationship", $link="") +Rel(InternetBankingSystem.APIApplication.ResetPasswordController, InternetBankingSystem.APIApplication.EmailComponent, "Uses", $techn="", $tags="Relationship", $link="") +Rel(InternetBankingSystem.APIApplication.SecurityComponent, InternetBankingSystem.Database, "Reads from and writes to", $techn="JDBC", $tags="Relationship", $link="") +Rel(InternetBankingSystem.APIApplication.MainframeBankingSystemFacade, MainframeBankingSystem, "Uses", $techn="XML/HTTPS", $tags="Relationship", $link="") +Rel(InternetBankingSystem.APIApplication.EmailComponent, EmailSystem, "Sends e-mail using", $techn="", $tags="Relationship", $link="") + +SHOW_LEGEND(true) +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Containers.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Containers.puml new file mode 100644 index 000000000..9badc190c --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Containers.puml @@ -0,0 +1,46 @@ +@startuml +set separator none +title Internet Banking System - Containers + +top to bottom direction + +!include <C4/C4> +!include <C4/C4_Context> +!include <C4/C4_Container> + +AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Container,Mobile App", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Person", $bgColor="#08427b", $borderColor="#052e56", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Container", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") + +AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") + +AddBoundaryTag("Software System", $bgColor="#ffffff", $borderColor="#0b4884", $fontColor="#0b4884", $shadowing="", $borderStyle="Solid", $borderThickness="1") + +Person_Ext(PersonalBankingCustomer, "Personal Banking Customer", $descr="A customer of the bank, with personal bank accounts.", $tags="Person", $link="") +System(MainframeBankingSystem, "Mainframe Banking System", $descr="Stores all of the core banking information about customers, accounts, transactions, etc.", $tags="Software System,Existing System", $link="") +System(EmailSystem, "E-mail System", $descr="The internal Microsoft Exchange e-mail system.", $tags="Software System,Existing System", $link="") + +System_Boundary("InternetBankingSystem_boundary", "Internet Banking System", $tags="Software System") { + Container(InternetBankingSystem.SinglePageApplication, "Single-Page Application", $techn="JavaScript and Angular", $descr="Provides all of the Internet banking functionality to customers via their web browser.", $tags="Container,Web Browser", $link="") + Container(InternetBankingSystem.MobileApp, "Mobile App", $techn="Xamarin", $descr="Provides a limited subset of the Internet banking functionality to customers via their mobile device.", $tags="Container,Mobile App", $link="") + Container(InternetBankingSystem.WebApplication, "Web Application", $techn="Java and Spring MVC", $descr="Delivers the static content and the Internet banking single page application.", $tags="Container", $link="") + Container(InternetBankingSystem.APIApplication, "API Application", $techn="Java and Spring MVC", $descr="Provides Internet banking functionality via a JSON/HTTPS API.", $tags="Container", $link="") + ContainerDb(InternetBankingSystem.Database, "Database", $techn="Oracle Database Schema", $descr="Stores user registration information, hashed authentication credentials, access logs, etc.", $tags="Container,Database", $link="") +} + +Rel(PersonalBankingCustomer, InternetBankingSystem.WebApplication, "Visits bigbank.com/ib using", $techn="HTTPS", $tags="Relationship", $link="") +Rel(PersonalBankingCustomer, InternetBankingSystem.SinglePageApplication, "Views account balances, and makes payments using", $techn="", $tags="Relationship", $link="") +Rel(PersonalBankingCustomer, InternetBankingSystem.MobileApp, "Views account balances, and makes payments using", $techn="", $tags="Relationship", $link="") +Rel(InternetBankingSystem.WebApplication, InternetBankingSystem.SinglePageApplication, "Delivers to the customer's web browser", $techn="", $tags="Relationship", $link="") +Rel(InternetBankingSystem.APIApplication, InternetBankingSystem.Database, "Reads from and writes to", $techn="JDBC", $tags="Relationship", $link="") +Rel(InternetBankingSystem.APIApplication, MainframeBankingSystem, "Makes API calls to", $techn="XML/HTTPS", $tags="Relationship", $link="") +Rel(InternetBankingSystem.APIApplication, EmailSystem, "Sends e-mail using", $techn="SMTP", $tags="Relationship", $link="") +Rel(InternetBankingSystem.SinglePageApplication, InternetBankingSystem.APIApplication, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") +Rel(InternetBankingSystem.MobileApp, InternetBankingSystem.APIApplication, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") +Rel(EmailSystem, PersonalBankingCustomer, "Sends e-mails to", $techn="", $tags="Relationship", $link="") + +SHOW_LEGEND(true) +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-DevelopmentDeployment.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-DevelopmentDeployment.puml new file mode 100644 index 000000000..d3e9a8d69 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-DevelopmentDeployment.puml @@ -0,0 +1,55 @@ +@startuml +set separator none +title Internet Banking System - Deployment - Development + +top to bottom direction + +!include <C4/C4> +!include <C4/C4_Context> +!include <C4/C4_Container> +!include <C4/C4_Deployment> + +AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Element", $bgColor="#ffffff", $borderColor="#888888", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Container", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") + +AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") + +Deployment_Node(Development.DeveloperLaptop, "Developer Laptop", $type="Microsoft Windows 10 or Apple macOS", $descr="A developer laptop.", $tags="Element", $link="") { + Deployment_Node(Development.DeveloperLaptop.DockerContainerWebServer, "Docker Container - Web Server", $type="Docker", $descr="A Docker container.", $tags="Element", $link="") { + Deployment_Node(Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat, "Apache Tomcat", $type="Apache Tomcat 8.x", $descr="An open source Java EE web server.", $tags="Element", $link="") { + Container(Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.WebApplication_1, "Web Application", $techn="Java and Spring MVC", $descr="Delivers the static content and the Internet banking single page application.", $tags="Container", $link="") + Container(Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.APIApplication_1, "API Application", $techn="Java and Spring MVC", $descr="Provides Internet banking functionality via a JSON/HTTPS API.", $tags="Container", $link="") + } + + } + + Deployment_Node(Development.DeveloperLaptop.DockerContainerDatabaseServer, "Docker Container - Database Server", $type="Docker", $descr="A Docker container.", $tags="Element", $link="") { + Deployment_Node(Development.DeveloperLaptop.DockerContainerDatabaseServer.DatabaseServer, "Database Server", $type="Oracle 12c", $descr="A development database.", $tags="Element", $link="") { + ContainerDb(Development.DeveloperLaptop.DockerContainerDatabaseServer.DatabaseServer.Database_1, "Database", $techn="Oracle Database Schema", $descr="Stores user registration information, hashed authentication credentials, access logs, etc.", $tags="Container,Database", $link="") + } + + } + + Deployment_Node(Development.DeveloperLaptop.WebBrowser, "Web Browser", $type="Chrome, Firefox, Safari, or Edge", $descr="", $tags="Element", $link="") { + Container(Development.DeveloperLaptop.WebBrowser.SinglePageApplication_1, "Single-Page Application", $techn="JavaScript and Angular", $descr="Provides all of the Internet banking functionality to customers via their web browser.", $tags="Container,Web Browser", $link="") + } + +} + +Deployment_Node(Development.BigBankplc, "Big Bank plc", $type="Big Bank plc data center", $descr="", $tags="Element", $link="") { + Deployment_Node(Development.BigBankplc.bigbankdev001, "bigbank-dev001", $type="", $descr="", $tags="Element", $link="") { + System(Development.BigBankplc.bigbankdev001.MainframeBankingSystem_1, "Mainframe Banking System", $descr="Stores all of the core banking information about customers, accounts, transactions, etc.", $tags="Software System,Existing System", $link="") + } + +} + +Rel(Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.APIApplication_1, Development.BigBankplc.bigbankdev001.MainframeBankingSystem_1, "Makes API calls to", $techn="XML/HTTPS", $tags="Relationship", $link="") +Rel(Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.APIApplication_1, Development.DeveloperLaptop.DockerContainerDatabaseServer.DatabaseServer.Database_1, "Reads from and writes to", $techn="JDBC", $tags="Relationship", $link="") +Rel(Development.DeveloperLaptop.WebBrowser.SinglePageApplication_1, Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.APIApplication_1, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") +Rel(Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.WebApplication_1, Development.DeveloperLaptop.WebBrowser.SinglePageApplication_1, "Delivers to the customer's web browser", $techn="", $tags="Relationship", $link="") + +SHOW_LEGEND(true) +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-LiveDeployment.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-LiveDeployment.puml new file mode 100644 index 000000000..6a33ec429 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-LiveDeployment.puml @@ -0,0 +1,79 @@ +@startuml +set separator none +title Internet Banking System - Deployment - Live + +top to bottom direction + +!include <C4/C4> +!include <C4/C4_Context> +!include <C4/C4_Container> +!include <C4/C4_Deployment> + +AddElementTag("Failover", $bgColor="#ffffff", $borderColor="#888888", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Element", $bgColor="#ffffff", $borderColor="#888888", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Container,Mobile App", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Container,Database,Failover", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Container", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") + +AddRelTag("Failover", $textColor="#707070", $lineColor="#707070", $lineStyle = "") +AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") + +Deployment_Node(Live.Customersmobiledevice, "Customer's mobile device", $type="Apple iOS or Android", $descr="", $tags="Element", $link="") { + Container(Live.Customersmobiledevice.MobileApp_1, "Mobile App", $techn="Xamarin", $descr="Provides a limited subset of the Internet banking functionality to customers via their mobile device.", $tags="Container,Mobile App", $link="") +} + +Deployment_Node(Live.Customerscomputer, "Customer's computer", $type="Microsoft Windows or Apple macOS", $descr="", $tags="Element", $link="") { + Deployment_Node(Live.Customerscomputer.WebBrowser, "Web Browser", $type="Chrome, Firefox, Safari, or Edge", $descr="", $tags="Element", $link="") { + Container(Live.Customerscomputer.WebBrowser.SinglePageApplication_1, "Single-Page Application", $techn="JavaScript and Angular", $descr="Provides all of the Internet banking functionality to customers via their web browser.", $tags="Container,Web Browser", $link="") + } + +} + +Deployment_Node(Live.BigBankplc, "Big Bank plc", $type="Big Bank plc data center", $descr="", $tags="Element", $link="") { + Deployment_Node(Live.BigBankplc.bigbankprod001, "bigbank-prod001", $type="", $descr="", $tags="Element", $link="") { + System(Live.BigBankplc.bigbankprod001.MainframeBankingSystem_1, "Mainframe Banking System", $descr="Stores all of the core banking information about customers, accounts, transactions, etc.", $tags="Software System,Existing System", $link="") + } + + Deployment_Node(Live.BigBankplc.bigbankweb, "bigbank-web*** (x4)", $type="Ubuntu 16.04 LTS", $descr="A web server residing in the web server farm, accessed via F5 BIG-IP LTMs.", $tags="Element", $link="") { + Deployment_Node(Live.BigBankplc.bigbankweb.ApacheTomcat, "Apache Tomcat", $type="Apache Tomcat 8.x", $descr="An open source Java EE web server.", $tags="Element", $link="") { + Container(Live.BigBankplc.bigbankweb.ApacheTomcat.WebApplication_1, "Web Application", $techn="Java and Spring MVC", $descr="Delivers the static content and the Internet banking single page application.", $tags="Container", $link="") + } + + } + + Deployment_Node(Live.BigBankplc.bigbankapi, "bigbank-api*** (x8)", $type="Ubuntu 16.04 LTS", $descr="A web server residing in the web server farm, accessed via F5 BIG-IP LTMs.", $tags="Element", $link="") { + Deployment_Node(Live.BigBankplc.bigbankapi.ApacheTomcat, "Apache Tomcat", $type="Apache Tomcat 8.x", $descr="An open source Java EE web server.", $tags="Element", $link="") { + Container(Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1, "API Application", $techn="Java and Spring MVC", $descr="Provides Internet banking functionality via a JSON/HTTPS API.", $tags="Container", $link="") + } + + } + + Deployment_Node(Live.BigBankplc.bigbankdb01, "bigbank-db01", $type="Ubuntu 16.04 LTS", $descr="The primary database server.", $tags="Element", $link="") { + Deployment_Node(Live.BigBankplc.bigbankdb01.OraclePrimary, "Oracle - Primary", $type="Oracle 12c", $descr="The primary, live database server.", $tags="Element", $link="") { + ContainerDb(Live.BigBankplc.bigbankdb01.OraclePrimary.Database_1, "Database", $techn="Oracle Database Schema", $descr="Stores user registration information, hashed authentication credentials, access logs, etc.", $tags="Container,Database", $link="") + } + + } + + Deployment_Node(Live.BigBankplc.bigbankdb02, "bigbank-db02", $type="Ubuntu 16.04 LTS", $descr="The secondary database server.", $tags="Failover", $link="") { + Deployment_Node(Live.BigBankplc.bigbankdb02.OracleSecondary, "Oracle - Secondary", $type="Oracle 12c", $descr="A secondary, standby database server, used for failover purposes only.", $tags="Failover", $link="") { + ContainerDb(Live.BigBankplc.bigbankdb02.OracleSecondary.Database_2, "Database", $techn="Oracle Database Schema", $descr="Stores user registration information, hashed authentication credentials, access logs, etc.", $tags="Container,Database,Failover", $link="") + } + + } + +} + +Rel(Live.BigBankplc.bigbankweb.ApacheTomcat.WebApplication_1, Live.Customerscomputer.WebBrowser.SinglePageApplication_1, "Delivers to the customer's web browser", $techn="", $tags="Relationship", $link="") +Rel(Live.Customersmobiledevice.MobileApp_1, Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") +Rel(Live.Customerscomputer.WebBrowser.SinglePageApplication_1, Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") +Rel(Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1, Live.BigBankplc.bigbankprod001.MainframeBankingSystem_1, "Makes API calls to", $techn="XML/HTTPS", $tags="Relationship", $link="") +Rel(Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1, Live.BigBankplc.bigbankdb01.OraclePrimary.Database_1, "Reads from and writes to", $techn="JDBC", $tags="Relationship", $link="") +Rel(Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1, Live.BigBankplc.bigbankdb02.OracleSecondary.Database_2, "Reads from and writes to", $techn="JDBC", $tags="Failover", $link="") +Rel(Live.BigBankplc.bigbankdb01.OraclePrimary, Live.BigBankplc.bigbankdb02.OracleSecondary, "Replicates data to", $techn="", $tags="Relationship", $link="") + +SHOW_LEGEND(true) +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn-sequence.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn-sequence.puml new file mode 100644 index 000000000..57f8c67a1 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn-sequence.puml @@ -0,0 +1,26 @@ +@startuml +set separator none +title API Application - Dynamic + +!include <C4/C4_Sequence> + +AddElementTag("Component", $bgColor="#85bbf0", $borderColor="#5d82a8", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") + +AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") + +Container(InternetBankingSystem.SinglePageApplication, "Single-Page Application", $techn="JavaScript and Angular", $descr="Provides all of the Internet banking functionality to customers via their web browser.", $tags="Container,Web Browser", $link="") +Component(InternetBankingSystem.APIApplication.SignInController, "Sign In Controller", $techn="Spring MVC Rest Controller", $descr="Allows users to sign in to the Internet Banking System.", $tags="Component", $link="") +Component(InternetBankingSystem.APIApplication.SecurityComponent, "Security Component", $techn="Spring Bean", $descr="Provides functionality related to signing in, changing passwords, etc.", $tags="Component", $link="") +ContainerDb(InternetBankingSystem.Database, "Database", $techn="Oracle Database Schema", $descr="Stores user registration information, hashed authentication credentials, access logs, etc.", $tags="Container,Database", $link="") + +Rel(InternetBankingSystem.SinglePageApplication, InternetBankingSystem.APIApplication.SignInController, "Submits credentials to", $techn="JSON/HTTPS", $tags="Relationship", $link="") +Rel(InternetBankingSystem.APIApplication.SignInController, InternetBankingSystem.APIApplication.SecurityComponent, "Validates credentials using", $techn="", $tags="Relationship", $link="") +Rel(InternetBankingSystem.APIApplication.SecurityComponent, InternetBankingSystem.Database, "select * from users where username = ?", $techn="JDBC", $tags="Relationship", $link="") +Rel(InternetBankingSystem.Database, InternetBankingSystem.APIApplication.SecurityComponent, "Returns user data to", $techn="JDBC", $tags="Relationship", $link="") +Rel(InternetBankingSystem.APIApplication.SecurityComponent, InternetBankingSystem.APIApplication.SignInController, "Returns true if the hashed password matches", $techn="", $tags="Relationship", $link="") +Rel(InternetBankingSystem.APIApplication.SignInController, InternetBankingSystem.SinglePageApplication, "Sends back an authentication token to", $techn="JSON/HTTPS", $tags="Relationship", $link="") + +SHOW_LEGEND(true) +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn.puml new file mode 100644 index 000000000..ee3f343b4 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn.puml @@ -0,0 +1,36 @@ +@startuml +set separator none +title API Application - Dynamic + +top to bottom direction + +!include <C4/C4> +!include <C4/C4_Context> +!include <C4/C4_Container> +!include <C4/C4_Component> + +AddElementTag("Component", $bgColor="#85bbf0", $borderColor="#5d82a8", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") + +AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") + +AddBoundaryTag("Container", $bgColor="#ffffff", $borderColor="#2e6295", $fontColor="#2e6295", $shadowing="", $borderStyle="Solid", $borderThickness="1") + +Container_Boundary("InternetBankingSystem.APIApplication_boundary", "API Application", $tags="Container") { + Component(InternetBankingSystem.APIApplication.SignInController, "Sign In Controller", $techn="Spring MVC Rest Controller", $descr="Allows users to sign in to the Internet Banking System.", $tags="Component", $link="") + Component(InternetBankingSystem.APIApplication.SecurityComponent, "Security Component", $techn="Spring Bean", $descr="Provides functionality related to signing in, changing passwords, etc.", $tags="Component", $link="") +} + +Container(InternetBankingSystem.SinglePageApplication, "Single-Page Application", $techn="JavaScript and Angular", $descr="Provides all of the Internet banking functionality to customers via their web browser.", $tags="Container,Web Browser", $link="") +ContainerDb(InternetBankingSystem.Database, "Database", $techn="Oracle Database Schema", $descr="Stores user registration information, hashed authentication credentials, access logs, etc.", $tags="Container,Database", $link="") + +Rel(InternetBankingSystem.SinglePageApplication, InternetBankingSystem.APIApplication.SignInController, "1. Submits credentials to", $techn="JSON/HTTPS", $tags="Relationship", $link="") +Rel(InternetBankingSystem.APIApplication.SignInController, InternetBankingSystem.APIApplication.SecurityComponent, "2. Validates credentials using", $techn="", $tags="Relationship", $link="") +Rel(InternetBankingSystem.APIApplication.SecurityComponent, InternetBankingSystem.Database, "3. select * from users where username = ?", $techn="JDBC", $tags="Relationship", $link="") +Rel(InternetBankingSystem.Database, InternetBankingSystem.APIApplication.SecurityComponent, "4. Returns user data to", $techn="JDBC", $tags="Relationship", $link="") +Rel(InternetBankingSystem.APIApplication.SecurityComponent, InternetBankingSystem.APIApplication.SignInController, "5. Returns true if the hashed password matches", $techn="", $tags="Relationship", $link="") +Rel(InternetBankingSystem.APIApplication.SignInController, InternetBankingSystem.SinglePageApplication, "6. Sends back an authentication token to", $techn="JSON/HTTPS", $tags="Relationship", $link="") + +SHOW_LEGEND(true) +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemContext.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemContext.puml new file mode 100644 index 000000000..e4b9cce22 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemContext.puml @@ -0,0 +1,27 @@ +@startuml +set separator none +title Internet Banking System - System Context + +top to bottom direction + +!include <C4/C4> +!include <C4/C4_Context> + +AddElementTag("Software System", $bgColor="#1168bd", $borderColor="#0b4884", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Person", $bgColor="#08427b", $borderColor="#052e56", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") + +AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") + +Person_Ext(PersonalBankingCustomer, "Personal Banking Customer", $descr="A customer of the bank, with personal bank accounts.", $tags="Person", $link="") +System(InternetBankingSystem, "Internet Banking System", $descr="Allows customers to view information about their bank accounts, and make payments.", $tags="Software System", $link="") +System(MainframeBankingSystem, "Mainframe Banking System", $descr="Stores all of the core banking information about customers, accounts, transactions, etc.", $tags="Software System,Existing System", $link="") +System(EmailSystem, "E-mail System", $descr="The internal Microsoft Exchange e-mail system.", $tags="Software System,Existing System", $link="") + +Rel(PersonalBankingCustomer, InternetBankingSystem, "Views account balances, and makes payments using", $techn="", $tags="Relationship", $link="") +Rel(InternetBankingSystem, MainframeBankingSystem, "Gets account information from, and makes payments using", $techn="", $tags="Relationship", $link="") +Rel(InternetBankingSystem, EmailSystem, "Sends e-mail using", $techn="", $tags="Relationship", $link="") +Rel(EmailSystem, PersonalBankingCustomer, "Sends e-mails to", $techn="", $tags="Relationship", $link="") + +SHOW_LEGEND(true) +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemLandscape.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemLandscape.puml new file mode 100644 index 000000000..8f4464816 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemLandscape.puml @@ -0,0 +1,39 @@ +@startuml +set separator none +title System Landscape + +top to bottom direction + +!include <C4/C4> +!include <C4/C4_Context> + +AddElementTag("Software System", $bgColor="#1168bd", $borderColor="#0b4884", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Person,Bank Staff", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Person", $bgColor="#08427b", $borderColor="#052e56", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") + +AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") + +Enterprise_Boundary(enterprise, "Big Bank plc") { + Person(CustomerServiceStaff, "Customer Service Staff", $descr="Customer service staff within the bank.", $tags="Person,Bank Staff", $link="") + Person(BackOfficeStaff, "Back Office Staff", $descr="Administration and support staff within the bank.", $tags="Person,Bank Staff", $link="") + System(InternetBankingSystem, "Internet Banking System", $descr="Allows customers to view information about their bank accounts, and make payments.", $tags="Software System", $link="") + System(MainframeBankingSystem, "Mainframe Banking System", $descr="Stores all of the core banking information about customers, accounts, transactions, etc.", $tags="Software System,Existing System", $link="") + System(EmailSystem, "E-mail System", $descr="The internal Microsoft Exchange e-mail system.", $tags="Software System,Existing System", $link="") + System(ATM, "ATM", $descr="Allows customers to withdraw cash.", $tags="Software System,Existing System", $link="") +} + +Person_Ext(PersonalBankingCustomer, "Personal Banking Customer", $descr="A customer of the bank, with personal bank accounts.", $tags="Person", $link="") + +Rel(ATM, MainframeBankingSystem, "Uses", $techn="", $tags="Relationship", $link="") +Rel(PersonalBankingCustomer, ATM, "Withdraws cash using", $techn="", $tags="Relationship", $link="") +Rel(CustomerServiceStaff, MainframeBankingSystem, "Uses", $techn="", $tags="Relationship", $link="") +Rel(PersonalBankingCustomer, CustomerServiceStaff, "Asks questions to", $techn="Telephone", $tags="Relationship", $link="") +Rel(BackOfficeStaff, MainframeBankingSystem, "Uses", $techn="", $tags="Relationship", $link="") +Rel(PersonalBankingCustomer, InternetBankingSystem, "Views account balances, and makes payments using", $techn="", $tags="Relationship", $link="") +Rel(InternetBankingSystem, MainframeBankingSystem, "Gets account information from, and makes payments using", $techn="", $tags="Relationship", $link="") +Rel(InternetBankingSystem, EmailSystem, "Sends e-mail using", $techn="", $tags="Relationship", $link="") +Rel(EmailSystem, PersonalBankingCustomer, "Sends e-mails to", $techn="", $tags="Relationship", $link="") + +SHOW_LEGEND(true) +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/54915-AmazonWebServicesDeployment-WithTags.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/54915-AmazonWebServicesDeployment-WithTags.puml new file mode 100644 index 000000000..db81378bc --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/54915-AmazonWebServicesDeployment-WithTags.puml @@ -0,0 +1,52 @@ +@startuml +set separator none +title Spring PetClinic - Deployment - Live + +left to right direction + +!include <C4/C4> +!include <C4/C4_Context> +!include <C4/C4_Container> +!include <C4/C4_Deployment> + +AddElementTag("Container,Application", $bgColor="#ffffff", $borderColor="#b2b2b2", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Amazon Web Services - RDS", $bgColor="#ffffff", $borderColor="#3b48cc", $fontColor="#3b48cc", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Amazon-RDS_light-bg@4x.png{scale=0.1}", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Amazon Web Services - Auto Scaling", $bgColor="#ffffff", $borderColor="#cc2264", $fontColor="#cc2264", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/AWS-Auto-Scaling_light-bg@4x.png{scale=0.1}", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Amazon Web Services - Route 53", $bgColor="#ffffff", $borderColor="#693cc5", $fontColor="#693cc5", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Amazon-Route-53_light-bg@4x.png{scale=0.1}", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Amazon Web Services - EC2", $bgColor="#ffffff", $borderColor="#d86613", $fontColor="#d86613", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Amazon-EC2_light-bg@4x.png{scale=0.1}", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Amazon Web Services - Region", $bgColor="#ffffff", $borderColor="#147eba", $fontColor="#147eba", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Region_light-bg@4x.png{scale=0.21428571428571427}", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Amazon Web Services - Elastic Load Balancing", $bgColor="#ffffff", $borderColor="#693cc5", $fontColor="#693cc5", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Elastic-Load-Balancing_light-bg@4x.png{scale=0.1}", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Amazon Web Services - RDS MySQL instance", $bgColor="#ffffff", $borderColor="#3b48cc", $fontColor="#3b48cc", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Amazon-RDS_MySQL_instance_light-bg@4x.png{scale=0.15}", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Container,Database", $bgColor="#ffffff", $borderColor="#b2b2b2", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Amazon Web Services - Cloud", $bgColor="#ffffff", $borderColor="#232f3e", $fontColor="#232f3e", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/AWS-Cloud_light-bg@4x.png{scale=0.21428571428571427}", $shadowing="", $borderStyle="Solid", $borderThickness="1") + +AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") + +Deployment_Node(Live.AmazonWebServices, "Amazon Web Services", $type="", $descr="", $tags="Amazon Web Services - Cloud", $link="") { + Deployment_Node(Live.AmazonWebServices.USEast1, "US-East-1", $type="", $descr="", $tags="Amazon Web Services - Region", $link="") { + Deployment_Node(Live.AmazonWebServices.USEast1.AmazonRDS, "Amazon RDS", $type="", $descr="", $tags="Amazon Web Services - RDS", $link="") { + Deployment_Node(Live.AmazonWebServices.USEast1.AmazonRDS.MySQL, "MySQL", $type="", $descr="", $tags="Amazon Web Services - RDS MySQL instance", $link="") { + ContainerDb(Live.AmazonWebServices.USEast1.AmazonRDS.MySQL.Database_1, "Database", $techn="Relational database schema", $descr="Stores information regarding the veterinarians, the clients, and their pets.", $tags="Container,Database", $link="") + } + + } + + Deployment_Node(Live.AmazonWebServices.USEast1.Route53, "Route 53", $type="", $descr="Highly available and scalable cloud DNS service.", $tags="Amazon Web Services - Route 53", $link="") + Deployment_Node(Live.AmazonWebServices.USEast1.ElasticLoadBalancer, "Elastic Load Balancer", $type="", $descr="Automatically distributes incoming application traffic.", $tags="Amazon Web Services - Elastic Load Balancing", $link="") + Deployment_Node(Live.AmazonWebServices.USEast1.Autoscalinggroup, "Autoscaling group", $type="", $descr="", $tags="Amazon Web Services - Auto Scaling", $link="") { + Deployment_Node(Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2, "Amazon EC2", $type="", $descr="", $tags="Amazon Web Services - EC2", $link="") { + Container(Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2.WebApplication_1, "Web Application", $techn="Java and Spring Boot", $descr="Allows employees to view and manage information regarding the veterinarians, the clients, and their pets.", $tags="Container,Application", $link="") + } + + } + + } + +} + +Rel(Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2.WebApplication_1, Live.AmazonWebServices.USEast1.AmazonRDS.MySQL.Database_1, "Reads from and writes to", $techn="MySQL Protocol/SSL", $tags="Relationship", $link="") +Rel(Live.AmazonWebServices.USEast1.Route53, Live.AmazonWebServices.USEast1.ElasticLoadBalancer, "Forwards requests to", $techn="HTTPS", $tags="Relationship", $link="") +Rel(Live.AmazonWebServices.USEast1.ElasticLoadBalancer, Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2.WebApplication_1, "Forwards requests to", $techn="HTTPS", $tags="Relationship", $link="") + +SHOW_LEGEND(true) +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/54915-AmazonWebServicesDeployment-WithoutTags.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/54915-AmazonWebServicesDeployment-WithoutTags.puml new file mode 100644 index 000000000..f6a6505d3 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/54915-AmazonWebServicesDeployment-WithoutTags.puml @@ -0,0 +1,39 @@ +@startuml +set separator none +title Spring PetClinic - Deployment - Live + +left to right direction + +!include <C4/C4> +!include <C4/C4_Context> +!include <C4/C4_Container> +!include <C4/C4_Deployment> + +Deployment_Node(Live.AmazonWebServices, "Amazon Web Services", $type="", $descr="", $tags="", $link="") { + Deployment_Node(Live.AmazonWebServices.USEast1, "US-East-1", $type="", $descr="", $tags="", $link="") { + Deployment_Node(Live.AmazonWebServices.USEast1.AmazonRDS, "Amazon RDS", $type="", $descr="", $tags="", $link="") { + Deployment_Node(Live.AmazonWebServices.USEast1.AmazonRDS.MySQL, "MySQL", $type="", $descr="", $tags="", $link="") { + ContainerDb(Live.AmazonWebServices.USEast1.AmazonRDS.MySQL.Database_1, "Database", $techn="Relational database schema", $descr="Stores information regarding the veterinarians, the clients, and their pets.", $tags="", $link="") + } + + } + + Deployment_Node(Live.AmazonWebServices.USEast1.Route53, "Route 53", $type="", $descr="Highly available and scalable cloud DNS service.", $tags="", $link="") + Deployment_Node(Live.AmazonWebServices.USEast1.ElasticLoadBalancer, "Elastic Load Balancer", $type="", $descr="Automatically distributes incoming application traffic.", $tags="", $link="") + Deployment_Node(Live.AmazonWebServices.USEast1.Autoscalinggroup, "Autoscaling group", $type="", $descr="", $tags="", $link="") { + Deployment_Node(Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2, "Amazon EC2", $type="", $descr="", $tags="", $link="") { + Container(Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2.WebApplication_1, "Web Application", $techn="Java and Spring Boot", $descr="Allows employees to view and manage information regarding the veterinarians, the clients, and their pets.", $tags="", $link="") + } + + } + + } + +} + +Rel(Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2.WebApplication_1, Live.AmazonWebServices.USEast1.AmazonRDS.MySQL.Database_1, "Reads from and writes to", $techn="MySQL Protocol/SSL", $tags="", $link="") +Rel(Live.AmazonWebServices.USEast1.Route53, Live.AmazonWebServices.USEast1.ElasticLoadBalancer, "Forwards requests to", $techn="HTTPS", $tags="", $link="") +Rel(Live.AmazonWebServices.USEast1.ElasticLoadBalancer, Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2.WebApplication_1, "Forwards requests to", $techn="HTTPS", $tags="", $link="") + +SHOW_LEGEND(true) +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/group-styles-1.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/group-styles-1.puml new file mode 100644 index 000000000..94c6a07ac --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/group-styles-1.puml @@ -0,0 +1,28 @@ +@startuml +set separator none +title System Landscape + +top to bottom direction + +!include <C4/C4> +!include <C4/C4_Context> + +AddBoundaryTag("Group 1", $borderColor="#111111", $fontColor="#111111", $borderStyle="Dashed", $borderThickness="1") +Boundary(group_1, "Group 1", $tags="Group 1") { + Person(User1, "User 1", $descr="", $tags="", $link="") +} + +AddBoundaryTag("Group 2", $borderColor="#222222", $fontColor="#222222", $borderStyle="Dashed", $borderThickness="1") +Boundary(group_2, "Group 2", $tags="Group 2") { + Person(User2, "User 2", $descr="", $tags="", $link="") +} + +AddBoundaryTag("Group 3", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="Dashed", $borderThickness="1") +Boundary(group_3, "Group 3", $tags="Group 3") { + Person(User3, "User 3", $descr="", $tags="", $link="") +} + + + +SHOW_LEGEND(true) +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/group-styles-2.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/group-styles-2.puml new file mode 100644 index 000000000..085b61b85 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/group-styles-2.puml @@ -0,0 +1,28 @@ +@startuml +set separator none +title System Landscape + +top to bottom direction + +!include <C4/C4> +!include <C4/C4_Context> + +AddBoundaryTag("Group 1", $borderColor="#111111", $fontColor="#111111", $borderStyle="Dashed", $borderThickness="1") +Boundary(group_1, "Group 1", $tags="Group 1") { + Person(User1, "User 1", $descr="", $tags="", $link="") +} + +AddBoundaryTag("Group 2", $borderColor="#222222", $fontColor="#222222", $borderStyle="Dashed", $borderThickness="1") +Boundary(group_2, "Group 2", $tags="Group 2") { + Person(User2, "User 2", $descr="", $tags="", $link="") +} + +AddBoundaryTag("Group 3", $borderColor="#aabbcc", $fontColor="#aabbcc", $borderStyle="Dashed", $borderThickness="1") +Boundary(group_3, "Group 3", $tags="Group 3") { + Person(User3, "User 3", $descr="", $tags="", $link="") +} + + + +SHOW_LEGEND(true) +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Components.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Components.puml new file mode 100644 index 000000000..5707e4f47 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Components.puml @@ -0,0 +1,26 @@ +@startuml +set separator none +title D - F - Components + +top to bottom direction + +!include <C4/C4> +!include <C4/C4_Context> +!include <C4/C4_Component> + +System(C, "C", $descr="", $tags="", $link="") + +Container_Boundary("D.F_boundary", "F", $tags="") { + AddBoundaryTag("Group 4", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="Dashed", $borderThickness="1") + Boundary(group_1, "Group 4", $tags="Group 4") { + Component(D.F.H, "H", $techn="", $descr="", $tags="", $link="") + } + + Component(D.F.G, "G", $techn="", $descr="", $tags="", $link="") +} + +Rel(C, D.F.G, "", $techn="", $tags="", $link="") +Rel(C, D.F.H, "", $techn="", $tags="", $link="") + +SHOW_LEGEND(true) +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Containers.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Containers.puml new file mode 100644 index 000000000..42af97606 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Containers.puml @@ -0,0 +1,26 @@ +@startuml +set separator none +title D - Containers + +top to bottom direction + +!include <C4/C4> +!include <C4/C4_Context> +!include <C4/C4_Container> + +System(C, "C", $descr="", $tags="", $link="") + +System_Boundary("D_boundary", "D", $tags="") { + AddBoundaryTag("Group 3", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="Dashed", $borderThickness="1") + Boundary(group_1, "Group 3", $tags="Group 3") { + Container(D.F, "F", $techn="", $descr="", $tags="", $link="") + } + + Container(D.E, "E", $techn="", $descr="", $tags="", $link="") +} + +Rel(C, D.E, "", $techn="", $tags="", $link="") +Rel(C, D.F, "", $techn="", $tags="", $link="") + +SHOW_LEGEND(true) +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-SystemLandscape.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-SystemLandscape.puml new file mode 100644 index 000000000..add8abb27 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-SystemLandscape.puml @@ -0,0 +1,31 @@ +@startuml +set separator none +title System Landscape + +top to bottom direction + +!include <C4/C4> +!include <C4/C4_Context> + +Enterprise_Boundary(enterprise, "Enterprise") { + AddBoundaryTag("Group 2", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="Dashed", $borderThickness="1") + Boundary(group_1, "Group 2", $tags="Group 2") { + System(D, "D", $descr="", $tags="", $link="") + } + + System(C, "C", $descr="", $tags="", $link="") +} + +AddBoundaryTag("Group 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="Dashed", $borderThickness="1") +Boundary(group_2, "Group 1", $tags="Group 1") { + System_Ext(B, "B", $descr="", $tags="", $link="") +} + +System_Ext(A, "A", $descr="", $tags="", $link="") + +Rel(B, C, "", $techn="", $tags="", $link="") +Rel(C, D, "", $techn="", $tags="", $link="") +Rel(A, B, "", $techn="", $tags="", $link="") + +SHOW_LEGEND(true) +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/nested-groups-with-dot-separator.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/nested-groups-with-dot-separator.puml new file mode 100644 index 000000000..cda61f1b3 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/nested-groups-with-dot-separator.puml @@ -0,0 +1,26 @@ +@startuml +set separator none +title System Landscape + +top to bottom direction + +!include <C4/C4> +!include <C4/C4_Context> + +AddBoundaryTag("Organisation 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="Dashed", $borderThickness="1") +Boundary(group_1, "Organisation 1", $tags="Organisation 1") { + AddBoundaryTag("Organisation 1.Department 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="Dashed", $borderThickness="1") + Boundary(group_2, "Department 1", $tags="Organisation 1.Department 1") { + AddBoundaryTag("Organisation 1.Department 1.Team 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="Dashed", $borderThickness="1") + Boundary(group_3, "Team 1", $tags="Organisation 1.Department 1.Team 1") { + System(Team1, "Team 1", $descr="", $tags="", $link="") + } + + } + +} + + + +SHOW_LEGEND(true) +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/nested-groups.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/nested-groups.puml new file mode 100644 index 000000000..783dc9fb8 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/nested-groups.puml @@ -0,0 +1,38 @@ +@startuml +set separator none +title System Landscape + +top to bottom direction + +!include <C4/C4> +!include <C4/C4_Context> + +AddBoundaryTag("Organisation 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="Dashed", $borderThickness="1") +Boundary(group_1, "Organisation 1", $tags="Organisation 1") { + System(Organisation1, "Organisation 1", $descr="", $tags="", $link="") + AddBoundaryTag("Organisation 1/Department 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="Dashed", $borderThickness="1") + Boundary(group_2, "Department 1", $tags="Organisation 1/Department 1") { + System(Department1, "Department 1", $descr="", $tags="", $link="") + AddBoundaryTag("Organisation 1/Department 1/Team 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="Dashed", $borderThickness="1") + Boundary(group_3, "Team 1", $tags="Organisation 1/Department 1/Team 1") { + System(Team1, "Team 1", $descr="", $tags="", $link="") + } + + AddBoundaryTag("Organisation 1/Department 1/Team 2", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="Dashed", $borderThickness="1") + Boundary(group_4, "Team 2", $tags="Organisation 1/Department 1/Team 2") { + System(Team2, "Team 2", $descr="", $tags="", $link="") + } + + } + +} + +AddBoundaryTag("Organisation 2", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="Dashed", $borderThickness="1") +Boundary(group_5, "Organisation 2", $tags="Organisation 2") { + System(Organisation2, "Organisation 2", $descr="", $tags="", $link="") +} + + + +SHOW_LEGEND(true) +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/printProperties-containerView.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/printProperties-containerView.puml new file mode 100644 index 000000000..3a505ee33 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/printProperties-containerView.puml @@ -0,0 +1,28 @@ +@startuml +set separator none +title SoftwareSystem - Containers + +top to bottom direction + +!include <C4/C4> +!include <C4/C4_Context> +!include <C4/C4_Container> + +System_Boundary("SoftwareSystem_boundary", "SoftwareSystem", $tags="") { + WithoutPropertyHeader() + AddProperty("IP","127.0.0.1") + AddProperty("Region","East") + Container(SoftwareSystem.Container1, "Container 1", $techn="", $descr="", $tags="", $link="") + WithoutPropertyHeader() + AddProperty("IP","127.0.0.2") + AddProperty("Region","West") + Container(SoftwareSystem.Container2, "Container 2", $techn="", $descr="", $tags="", $link="") +} + +WithoutPropertyHeader() +AddProperty("Prop1","Value1") +AddProperty("Prop2","Value2") +Rel(SoftwareSystem.Container1, SoftwareSystem.Container2, "", $techn="", $tags="", $link="") + +SHOW_LEGEND(true) +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/printProperties-deploymentView.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/printProperties-deploymentView.puml new file mode 100644 index 000000000..f96e7c97a --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/printProperties-deploymentView.puml @@ -0,0 +1,21 @@ +@startuml +set separator none +title Deployment - Default + +top to bottom direction + +!include <C4/C4> +!include <C4/C4_Context> +!include <C4/C4_Deployment> + +WithoutPropertyHeader() +AddProperty("Prop1","Value1") +Deployment_Node(Default.Deploymentnode, "Deployment node", $type="", $descr="", $tags="", $link="") { + WithoutPropertyHeader() + AddProperty("Prop2","Value2") + Deployment_Node(Default.Deploymentnode.Infrastructurenode, "Infrastructure node", $type="technology", $descr="description", $tags="", $link="") +} + + +SHOW_LEGEND(true) +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-Components.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-Components.puml new file mode 100644 index 000000000..edc664115 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-Components.puml @@ -0,0 +1,116 @@ +@startuml +set separator none +title Internet Banking System - API Application - Components + +top to bottom direction + +skinparam { + arrowFontSize 10 + defaultTextAlignment center + wrapWidth 200 + maxMessageSize 100 +} + +hide stereotype + +skinparam rectangle<<InternetBankingSystem.APIApplication.AccountsSummaryController>> { + BackgroundColor #85bbf0 + FontColor #000000 + BorderColor #5d82a8 + shadowing false +} +skinparam database<<InternetBankingSystem.Database>> { + BackgroundColor #438dd5 + FontColor #ffffff + BorderColor #2e6295 + shadowing false +} +skinparam rectangle<<InternetBankingSystem.APIApplication.EmailComponent>> { + BackgroundColor #85bbf0 + FontColor #000000 + BorderColor #5d82a8 + shadowing false +} +skinparam rectangle<<EmailSystem>> { + BackgroundColor #999999 + FontColor #ffffff + BorderColor #6b6b6b + shadowing false +} +skinparam rectangle<<MainframeBankingSystem>> { + BackgroundColor #999999 + FontColor #ffffff + BorderColor #6b6b6b + shadowing false +} +skinparam rectangle<<InternetBankingSystem.APIApplication.MainframeBankingSystemFacade>> { + BackgroundColor #85bbf0 + FontColor #000000 + BorderColor #5d82a8 + shadowing false +} +skinparam rectangle<<InternetBankingSystem.MobileApp>> { + BackgroundColor #438dd5 + FontColor #ffffff + BorderColor #2e6295 + shadowing false +} +skinparam rectangle<<InternetBankingSystem.APIApplication.ResetPasswordController>> { + BackgroundColor #85bbf0 + FontColor #000000 + BorderColor #5d82a8 + shadowing false +} +skinparam rectangle<<InternetBankingSystem.APIApplication.SecurityComponent>> { + BackgroundColor #85bbf0 + FontColor #000000 + BorderColor #5d82a8 + shadowing false +} +skinparam rectangle<<InternetBankingSystem.APIApplication.SignInController>> { + BackgroundColor #85bbf0 + FontColor #000000 + BorderColor #5d82a8 + shadowing false +} +skinparam rectangle<<InternetBankingSystem.SinglePageApplication>> { + BackgroundColor #438dd5 + FontColor #ffffff + BorderColor #2e6295 + shadowing false +} +skinparam rectangle<<InternetBankingSystem.APIApplication>> { + BorderColor #2e6295 + FontColor #2e6295 + shadowing false +} + +rectangle "==Mainframe Banking System\n<size:10>[Software System]</size>\n\nStores all of the core banking information about customers, accounts, transactions, etc." <<MainframeBankingSystem>> as MainframeBankingSystem +rectangle "==Single-Page Application\n<size:10>[Container: JavaScript and Angular]</size>\n\nProvides all of the Internet banking functionality to customers via their web browser." <<InternetBankingSystem.SinglePageApplication>> as InternetBankingSystem.SinglePageApplication +rectangle "==E-mail System\n<size:10>[Software System]</size>\n\nThe internal Microsoft Exchange e-mail system." <<EmailSystem>> as EmailSystem +rectangle "==Mobile App\n<size:10>[Container: Xamarin]</size>\n\nProvides a limited subset of the Internet banking functionality to customers via their mobile device." <<InternetBankingSystem.MobileApp>> as InternetBankingSystem.MobileApp +database "==Database\n<size:10>[Container: Oracle Database Schema]</size>\n\nStores user registration information, hashed authentication credentials, access logs, etc." <<InternetBankingSystem.Database>> as InternetBankingSystem.Database + +rectangle "API Application\n<size:10>[Container: Java and Spring MVC]</size>" <<InternetBankingSystem.APIApplication>> { + rectangle "==Sign In Controller\n<size:10>[Component: Spring MVC Rest Controller]</size>\n\nAllows users to sign in to the Internet Banking System." <<InternetBankingSystem.APIApplication.SignInController>> as InternetBankingSystem.APIApplication.SignInController + rectangle "==Accounts Summary Controller\n<size:10>[Component: Spring MVC Rest Controller]</size>\n\nProvides customers with a summary of their bank accounts." <<InternetBankingSystem.APIApplication.AccountsSummaryController>> as InternetBankingSystem.APIApplication.AccountsSummaryController + rectangle "==Reset Password Controller\n<size:10>[Component: Spring MVC Rest Controller]</size>\n\nAllows users to reset their passwords with a single use URL." <<InternetBankingSystem.APIApplication.ResetPasswordController>> as InternetBankingSystem.APIApplication.ResetPasswordController + rectangle "==Security Component\n<size:10>[Component: Spring Bean]</size>\n\nProvides functionality related to signing in, changing passwords, etc." <<InternetBankingSystem.APIApplication.SecurityComponent>> as InternetBankingSystem.APIApplication.SecurityComponent + rectangle "==Mainframe Banking System Facade\n<size:10>[Component: Spring Bean]</size>\n\nA facade onto the mainframe banking system." <<InternetBankingSystem.APIApplication.MainframeBankingSystemFacade>> as InternetBankingSystem.APIApplication.MainframeBankingSystemFacade + rectangle "==E-mail Component\n<size:10>[Component: Spring Bean]</size>\n\nSends e-mails to users." <<InternetBankingSystem.APIApplication.EmailComponent>> as InternetBankingSystem.APIApplication.EmailComponent +} + +InternetBankingSystem.SinglePageApplication .[#707070,thickness=2].> InternetBankingSystem.APIApplication.SignInController : "<color:#707070>Makes API calls to\n<color:#707070><size:8>[JSON/HTTPS]</size>" +InternetBankingSystem.SinglePageApplication .[#707070,thickness=2].> InternetBankingSystem.APIApplication.ResetPasswordController : "<color:#707070>Makes API calls to\n<color:#707070><size:8>[JSON/HTTPS]</size>" +InternetBankingSystem.SinglePageApplication .[#707070,thickness=2].> InternetBankingSystem.APIApplication.AccountsSummaryController : "<color:#707070>Makes API calls to\n<color:#707070><size:8>[JSON/HTTPS]</size>" +InternetBankingSystem.MobileApp .[#707070,thickness=2].> InternetBankingSystem.APIApplication.SignInController : "<color:#707070>Makes API calls to\n<color:#707070><size:8>[JSON/HTTPS]</size>" +InternetBankingSystem.MobileApp .[#707070,thickness=2].> InternetBankingSystem.APIApplication.ResetPasswordController : "<color:#707070>Makes API calls to\n<color:#707070><size:8>[JSON/HTTPS]</size>" +InternetBankingSystem.MobileApp .[#707070,thickness=2].> InternetBankingSystem.APIApplication.AccountsSummaryController : "<color:#707070>Makes API calls to\n<color:#707070><size:8>[JSON/HTTPS]</size>" +InternetBankingSystem.APIApplication.SignInController .[#707070,thickness=2].> InternetBankingSystem.APIApplication.SecurityComponent : "<color:#707070>Uses" +InternetBankingSystem.APIApplication.AccountsSummaryController .[#707070,thickness=2].> InternetBankingSystem.APIApplication.MainframeBankingSystemFacade : "<color:#707070>Uses" +InternetBankingSystem.APIApplication.ResetPasswordController .[#707070,thickness=2].> InternetBankingSystem.APIApplication.SecurityComponent : "<color:#707070>Uses" +InternetBankingSystem.APIApplication.ResetPasswordController .[#707070,thickness=2].> InternetBankingSystem.APIApplication.EmailComponent : "<color:#707070>Uses" +InternetBankingSystem.APIApplication.SecurityComponent .[#707070,thickness=2].> InternetBankingSystem.Database : "<color:#707070>Reads from and writes to\n<color:#707070><size:8>[JDBC]</size>" +InternetBankingSystem.APIApplication.MainframeBankingSystemFacade .[#707070,thickness=2].> MainframeBankingSystem : "<color:#707070>Uses\n<color:#707070><size:8>[XML/HTTPS]</size>" +InternetBankingSystem.APIApplication.EmailComponent .[#707070,thickness=2].> EmailSystem : "<color:#707070>Sends e-mail using" +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-Containers.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-Containers.puml new file mode 100644 index 000000000..300b0d9e8 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-Containers.puml @@ -0,0 +1,92 @@ +@startuml +set separator none +title Internet Banking System - Containers + +top to bottom direction + +skinparam { + arrowFontSize 10 + defaultTextAlignment center + wrapWidth 200 + maxMessageSize 100 +} + +hide stereotype + +skinparam rectangle<<InternetBankingSystem.APIApplication>> { + BackgroundColor #438dd5 + FontColor #ffffff + BorderColor #2e6295 + shadowing false +} +skinparam database<<InternetBankingSystem.Database>> { + BackgroundColor #438dd5 + FontColor #ffffff + BorderColor #2e6295 + shadowing false +} +skinparam rectangle<<EmailSystem>> { + BackgroundColor #999999 + FontColor #ffffff + BorderColor #6b6b6b + shadowing false +} +skinparam rectangle<<MainframeBankingSystem>> { + BackgroundColor #999999 + FontColor #ffffff + BorderColor #6b6b6b + shadowing false +} +skinparam rectangle<<InternetBankingSystem.MobileApp>> { + BackgroundColor #438dd5 + FontColor #ffffff + BorderColor #2e6295 + shadowing false +} +skinparam person<<PersonalBankingCustomer>> { + BackgroundColor #08427b + FontColor #ffffff + BorderColor #052e56 + shadowing false +} +skinparam rectangle<<InternetBankingSystem.SinglePageApplication>> { + BackgroundColor #438dd5 + FontColor #ffffff + BorderColor #2e6295 + shadowing false +} +skinparam rectangle<<InternetBankingSystem.WebApplication>> { + BackgroundColor #438dd5 + FontColor #ffffff + BorderColor #2e6295 + shadowing false +} +skinparam rectangle<<InternetBankingSystem>> { + BorderColor #0b4884 + FontColor #0b4884 + shadowing false +} + +person "==Personal Banking Customer\n<size:10>[Person]</size>\n\nA customer of the bank, with personal bank accounts." <<PersonalBankingCustomer>> as PersonalBankingCustomer +rectangle "==Mainframe Banking System\n<size:10>[Software System]</size>\n\nStores all of the core banking information about customers, accounts, transactions, etc." <<MainframeBankingSystem>> as MainframeBankingSystem +rectangle "==E-mail System\n<size:10>[Software System]</size>\n\nThe internal Microsoft Exchange e-mail system." <<EmailSystem>> as EmailSystem + +rectangle "Internet Banking System\n<size:10>[Software System]</size>" <<InternetBankingSystem>> { + rectangle "==Single-Page Application\n<size:10>[Container: JavaScript and Angular]</size>\n\nProvides all of the Internet banking functionality to customers via their web browser." <<InternetBankingSystem.SinglePageApplication>> as InternetBankingSystem.SinglePageApplication + rectangle "==Mobile App\n<size:10>[Container: Xamarin]</size>\n\nProvides a limited subset of the Internet banking functionality to customers via their mobile device." <<InternetBankingSystem.MobileApp>> as InternetBankingSystem.MobileApp + rectangle "==Web Application\n<size:10>[Container: Java and Spring MVC]</size>\n\nDelivers the static content and the Internet banking single page application." <<InternetBankingSystem.WebApplication>> as InternetBankingSystem.WebApplication + rectangle "==API Application\n<size:10>[Container: Java and Spring MVC]</size>\n\nProvides Internet banking functionality via a JSON/HTTPS API." <<InternetBankingSystem.APIApplication>> as InternetBankingSystem.APIApplication + database "==Database\n<size:10>[Container: Oracle Database Schema]</size>\n\nStores user registration information, hashed authentication credentials, access logs, etc." <<InternetBankingSystem.Database>> as InternetBankingSystem.Database +} + +PersonalBankingCustomer .[#707070,thickness=2].> InternetBankingSystem.WebApplication : "<color:#707070>Visits bigbank.com/ib using\n<color:#707070><size:8>[HTTPS]</size>" +PersonalBankingCustomer .[#707070,thickness=2].> InternetBankingSystem.SinglePageApplication : "<color:#707070>Views account balances, and makes payments using" +PersonalBankingCustomer .[#707070,thickness=2].> InternetBankingSystem.MobileApp : "<color:#707070>Views account balances, and makes payments using" +InternetBankingSystem.WebApplication .[#707070,thickness=2].> InternetBankingSystem.SinglePageApplication : "<color:#707070>Delivers to the customer's web browser" +InternetBankingSystem.APIApplication .[#707070,thickness=2].> InternetBankingSystem.Database : "<color:#707070>Reads from and writes to\n<color:#707070><size:8>[JDBC]</size>" +InternetBankingSystem.APIApplication .[#707070,thickness=2].> MainframeBankingSystem : "<color:#707070>Makes API calls to\n<color:#707070><size:8>[XML/HTTPS]</size>" +InternetBankingSystem.APIApplication .[#707070,thickness=2].> EmailSystem : "<color:#707070>Sends e-mail using\n<color:#707070><size:8>[SMTP]</size>" +InternetBankingSystem.SinglePageApplication .[#707070,thickness=2].> InternetBankingSystem.APIApplication : "<color:#707070>Makes API calls to\n<color:#707070><size:8>[JSON/HTTPS]</size>" +InternetBankingSystem.MobileApp .[#707070,thickness=2].> InternetBankingSystem.APIApplication : "<color:#707070>Makes API calls to\n<color:#707070><size:8>[JSON/HTTPS]</size>" +EmailSystem .[#707070,thickness=2].> PersonalBankingCustomer : "<color:#707070>Sends e-mails to" +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-DevelopmentDeployment.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-DevelopmentDeployment.puml new file mode 100644 index 000000000..e35d6fa8c --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-DevelopmentDeployment.puml @@ -0,0 +1,128 @@ +@startuml +set separator none +title Internet Banking System - Deployment - Development + +top to bottom direction + +skinparam { + arrowFontSize 10 + defaultTextAlignment center + wrapWidth 200 + maxMessageSize 100 +} + +hide stereotype + +skinparam rectangle<<Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.APIApplication_1>> { + BackgroundColor #438dd5 + FontColor #ffffff + BorderColor #2e6295 + shadowing false +} +skinparam rectangle<<Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat>> { + BackgroundColor #ffffff + FontColor #000000 + BorderColor #888888 + shadowing false +} +skinparam rectangle<<Development.BigBankplc>> { + BackgroundColor #ffffff + FontColor #000000 + BorderColor #888888 + shadowing false +} +skinparam database<<Development.DeveloperLaptop.DockerContainerDatabaseServer.DatabaseServer.Database_1>> { + BackgroundColor #438dd5 + FontColor #ffffff + BorderColor #2e6295 + shadowing false +} +skinparam rectangle<<Development.DeveloperLaptop.DockerContainerDatabaseServer.DatabaseServer>> { + BackgroundColor #ffffff + FontColor #000000 + BorderColor #888888 + shadowing false +} +skinparam rectangle<<Development.DeveloperLaptop>> { + BackgroundColor #ffffff + FontColor #000000 + BorderColor #888888 + shadowing false +} +skinparam rectangle<<Development.DeveloperLaptop.DockerContainerDatabaseServer>> { + BackgroundColor #ffffff + FontColor #000000 + BorderColor #888888 + shadowing false +} +skinparam rectangle<<Development.DeveloperLaptop.DockerContainerWebServer>> { + BackgroundColor #ffffff + FontColor #000000 + BorderColor #888888 + shadowing false +} +skinparam rectangle<<Development.BigBankplc.bigbankdev001.MainframeBankingSystem_1>> { + BackgroundColor #999999 + FontColor #ffffff + BorderColor #6b6b6b + shadowing false +} +skinparam rectangle<<Development.DeveloperLaptop.WebBrowser.SinglePageApplication_1>> { + BackgroundColor #438dd5 + FontColor #ffffff + BorderColor #2e6295 + shadowing false +} +skinparam rectangle<<Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.WebApplication_1>> { + BackgroundColor #438dd5 + FontColor #ffffff + BorderColor #2e6295 + shadowing false +} +skinparam rectangle<<Development.DeveloperLaptop.WebBrowser>> { + BackgroundColor #ffffff + FontColor #000000 + BorderColor #888888 + shadowing false +} +skinparam rectangle<<Development.BigBankplc.bigbankdev001>> { + BackgroundColor #ffffff + FontColor #000000 + BorderColor #888888 + shadowing false +} + +rectangle "Developer Laptop\n<size:10>[Deployment Node: Microsoft Windows 10 or Apple macOS]</size>" <<Development.DeveloperLaptop>> as Development.DeveloperLaptop { + rectangle "Docker Container - Web Server\n<size:10>[Deployment Node: Docker]</size>" <<Development.DeveloperLaptop.DockerContainerWebServer>> as Development.DeveloperLaptop.DockerContainerWebServer { + rectangle "Apache Tomcat\n<size:10>[Deployment Node: Apache Tomcat 8.x]</size>" <<Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat>> as Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat { + rectangle "==Web Application\n<size:10>[Container: Java and Spring MVC]</size>\n\nDelivers the static content and the Internet banking single page application." <<Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.WebApplication_1>> as Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.WebApplication_1 + rectangle "==API Application\n<size:10>[Container: Java and Spring MVC]</size>\n\nProvides Internet banking functionality via a JSON/HTTPS API." <<Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.APIApplication_1>> as Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.APIApplication_1 + } + + } + + rectangle "Docker Container - Database Server\n<size:10>[Deployment Node: Docker]</size>" <<Development.DeveloperLaptop.DockerContainerDatabaseServer>> as Development.DeveloperLaptop.DockerContainerDatabaseServer { + rectangle "Database Server\n<size:10>[Deployment Node: Oracle 12c]</size>" <<Development.DeveloperLaptop.DockerContainerDatabaseServer.DatabaseServer>> as Development.DeveloperLaptop.DockerContainerDatabaseServer.DatabaseServer { + database "==Database\n<size:10>[Container: Oracle Database Schema]</size>\n\nStores user registration information, hashed authentication credentials, access logs, etc." <<Development.DeveloperLaptop.DockerContainerDatabaseServer.DatabaseServer.Database_1>> as Development.DeveloperLaptop.DockerContainerDatabaseServer.DatabaseServer.Database_1 + } + + } + + rectangle "Web Browser\n<size:10>[Deployment Node: Chrome, Firefox, Safari, or Edge]</size>" <<Development.DeveloperLaptop.WebBrowser>> as Development.DeveloperLaptop.WebBrowser { + rectangle "==Single-Page Application\n<size:10>[Container: JavaScript and Angular]</size>\n\nProvides all of the Internet banking functionality to customers via their web browser." <<Development.DeveloperLaptop.WebBrowser.SinglePageApplication_1>> as Development.DeveloperLaptop.WebBrowser.SinglePageApplication_1 + } + +} + +rectangle "Big Bank plc\n<size:10>[Deployment Node: Big Bank plc data center]</size>" <<Development.BigBankplc>> as Development.BigBankplc { + rectangle "bigbank-dev001\n<size:10>[Deployment Node]</size>" <<Development.BigBankplc.bigbankdev001>> as Development.BigBankplc.bigbankdev001 { + rectangle "==Mainframe Banking System\n<size:10>[Software System]</size>\n\nStores all of the core banking information about customers, accounts, transactions, etc." <<Development.BigBankplc.bigbankdev001.MainframeBankingSystem_1>> as Development.BigBankplc.bigbankdev001.MainframeBankingSystem_1 + } + +} + +Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.APIApplication_1 .[#707070,thickness=2].> Development.BigBankplc.bigbankdev001.MainframeBankingSystem_1 : "<color:#707070>Makes API calls to\n<color:#707070><size:8>[XML/HTTPS]</size>" +Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.APIApplication_1 .[#707070,thickness=2].> Development.DeveloperLaptop.DockerContainerDatabaseServer.DatabaseServer.Database_1 : "<color:#707070>Reads from and writes to\n<color:#707070><size:8>[JDBC]</size>" +Development.DeveloperLaptop.WebBrowser.SinglePageApplication_1 .[#707070,thickness=2].> Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.APIApplication_1 : "<color:#707070>Makes API calls to\n<color:#707070><size:8>[JSON/HTTPS]</size>" +Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.WebApplication_1 .[#707070,thickness=2].> Development.DeveloperLaptop.WebBrowser.SinglePageApplication_1 : "<color:#707070>Delivers to the customer's web browser" +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-LiveDeployment.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-LiveDeployment.puml new file mode 100644 index 000000000..72427ad33 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-LiveDeployment.puml @@ -0,0 +1,190 @@ +@startuml +set separator none +title Internet Banking System - Deployment - Live + +top to bottom direction + +skinparam { + arrowFontSize 10 + defaultTextAlignment center + wrapWidth 200 + maxMessageSize 100 +} + +hide stereotype + +skinparam rectangle<<Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1>> { + BackgroundColor #438dd5 + FontColor #ffffff + BorderColor #2e6295 + shadowing false +} +skinparam rectangle<<Live.BigBankplc.bigbankapi.ApacheTomcat>> { + BackgroundColor #ffffff + FontColor #000000 + BorderColor #888888 + shadowing false +} +skinparam rectangle<<Live.BigBankplc.bigbankweb.ApacheTomcat>> { + BackgroundColor #ffffff + FontColor #000000 + BorderColor #888888 + shadowing false +} +skinparam rectangle<<Live.BigBankplc>> { + BackgroundColor #ffffff + FontColor #000000 + BorderColor #888888 + shadowing false +} +skinparam rectangle<<Live.Customerscomputer>> { + BackgroundColor #ffffff + FontColor #000000 + BorderColor #888888 + shadowing false +} +skinparam rectangle<<Live.Customersmobiledevice>> { + BackgroundColor #ffffff + FontColor #000000 + BorderColor #888888 + shadowing false +} +skinparam database<<Live.BigBankplc.bigbankdb02.OracleSecondary.Database_2>> { + BackgroundColor #438dd5 + FontColor #ffffff + BorderColor #2e6295 + shadowing false +} +skinparam database<<Live.BigBankplc.bigbankdb01.OraclePrimary.Database_1>> { + BackgroundColor #438dd5 + FontColor #ffffff + BorderColor #2e6295 + shadowing false +} +skinparam rectangle<<Live.BigBankplc.bigbankprod001.MainframeBankingSystem_1>> { + BackgroundColor #999999 + FontColor #ffffff + BorderColor #6b6b6b + shadowing false +} +skinparam rectangle<<Live.Customersmobiledevice.MobileApp_1>> { + BackgroundColor #438dd5 + FontColor #ffffff + BorderColor #2e6295 + shadowing false +} +skinparam rectangle<<Live.BigBankplc.bigbankdb01.OraclePrimary>> { + BackgroundColor #ffffff + FontColor #000000 + BorderColor #888888 + shadowing false +} +skinparam rectangle<<Live.BigBankplc.bigbankdb02.OracleSecondary>> { + BackgroundColor #ffffff + FontColor #000000 + BorderColor #888888 + shadowing false +} +skinparam rectangle<<Live.Customerscomputer.WebBrowser.SinglePageApplication_1>> { + BackgroundColor #438dd5 + FontColor #ffffff + BorderColor #2e6295 + shadowing false +} +skinparam rectangle<<Live.BigBankplc.bigbankweb.ApacheTomcat.WebApplication_1>> { + BackgroundColor #438dd5 + FontColor #ffffff + BorderColor #2e6295 + shadowing false +} +skinparam rectangle<<Live.Customerscomputer.WebBrowser>> { + BackgroundColor #ffffff + FontColor #000000 + BorderColor #888888 + shadowing false +} +skinparam rectangle<<Live.BigBankplc.bigbankapi>> { + BackgroundColor #ffffff + FontColor #000000 + BorderColor #888888 + shadowing false +} +skinparam rectangle<<Live.BigBankplc.bigbankdb01>> { + BackgroundColor #ffffff + FontColor #000000 + BorderColor #888888 + shadowing false +} +skinparam rectangle<<Live.BigBankplc.bigbankdb02>> { + BackgroundColor #ffffff + FontColor #000000 + BorderColor #888888 + shadowing false +} +skinparam rectangle<<Live.BigBankplc.bigbankprod001>> { + BackgroundColor #ffffff + FontColor #000000 + BorderColor #888888 + shadowing false +} +skinparam rectangle<<Live.BigBankplc.bigbankweb>> { + BackgroundColor #ffffff + FontColor #000000 + BorderColor #888888 + shadowing false +} + +rectangle "Customer's mobile device\n<size:10>[Deployment Node: Apple iOS or Android]</size>" <<Live.Customersmobiledevice>> as Live.Customersmobiledevice { + rectangle "==Mobile App\n<size:10>[Container: Xamarin]</size>\n\nProvides a limited subset of the Internet banking functionality to customers via their mobile device." <<Live.Customersmobiledevice.MobileApp_1>> as Live.Customersmobiledevice.MobileApp_1 +} + +rectangle "Customer's computer\n<size:10>[Deployment Node: Microsoft Windows or Apple macOS]</size>" <<Live.Customerscomputer>> as Live.Customerscomputer { + rectangle "Web Browser\n<size:10>[Deployment Node: Chrome, Firefox, Safari, or Edge]</size>" <<Live.Customerscomputer.WebBrowser>> as Live.Customerscomputer.WebBrowser { + rectangle "==Single-Page Application\n<size:10>[Container: JavaScript and Angular]</size>\n\nProvides all of the Internet banking functionality to customers via their web browser." <<Live.Customerscomputer.WebBrowser.SinglePageApplication_1>> as Live.Customerscomputer.WebBrowser.SinglePageApplication_1 + } + +} + +rectangle "Big Bank plc\n<size:10>[Deployment Node: Big Bank plc data center]</size>" <<Live.BigBankplc>> as Live.BigBankplc { + rectangle "bigbank-prod001\n<size:10>[Deployment Node]</size>" <<Live.BigBankplc.bigbankprod001>> as Live.BigBankplc.bigbankprod001 { + rectangle "==Mainframe Banking System\n<size:10>[Software System]</size>\n\nStores all of the core banking information about customers, accounts, transactions, etc." <<Live.BigBankplc.bigbankprod001.MainframeBankingSystem_1>> as Live.BigBankplc.bigbankprod001.MainframeBankingSystem_1 + } + + rectangle "bigbank-web*** (x4)\n<size:10>[Deployment Node: Ubuntu 16.04 LTS]</size>" <<Live.BigBankplc.bigbankweb>> as Live.BigBankplc.bigbankweb { + rectangle "Apache Tomcat\n<size:10>[Deployment Node: Apache Tomcat 8.x]</size>" <<Live.BigBankplc.bigbankweb.ApacheTomcat>> as Live.BigBankplc.bigbankweb.ApacheTomcat { + rectangle "==Web Application\n<size:10>[Container: Java and Spring MVC]</size>\n\nDelivers the static content and the Internet banking single page application." <<Live.BigBankplc.bigbankweb.ApacheTomcat.WebApplication_1>> as Live.BigBankplc.bigbankweb.ApacheTomcat.WebApplication_1 + } + + } + + rectangle "bigbank-api*** (x8)\n<size:10>[Deployment Node: Ubuntu 16.04 LTS]</size>" <<Live.BigBankplc.bigbankapi>> as Live.BigBankplc.bigbankapi { + rectangle "Apache Tomcat\n<size:10>[Deployment Node: Apache Tomcat 8.x]</size>" <<Live.BigBankplc.bigbankapi.ApacheTomcat>> as Live.BigBankplc.bigbankapi.ApacheTomcat { + rectangle "==API Application\n<size:10>[Container: Java and Spring MVC]</size>\n\nProvides Internet banking functionality via a JSON/HTTPS API." <<Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1>> as Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1 + } + + } + + rectangle "bigbank-db01\n<size:10>[Deployment Node: Ubuntu 16.04 LTS]</size>" <<Live.BigBankplc.bigbankdb01>> as Live.BigBankplc.bigbankdb01 { + rectangle "Oracle - Primary\n<size:10>[Deployment Node: Oracle 12c]</size>" <<Live.BigBankplc.bigbankdb01.OraclePrimary>> as Live.BigBankplc.bigbankdb01.OraclePrimary { + database "==Database\n<size:10>[Container: Oracle Database Schema]</size>\n\nStores user registration information, hashed authentication credentials, access logs, etc." <<Live.BigBankplc.bigbankdb01.OraclePrimary.Database_1>> as Live.BigBankplc.bigbankdb01.OraclePrimary.Database_1 + } + + } + + rectangle "bigbank-db02\n<size:10>[Deployment Node: Ubuntu 16.04 LTS]</size>" <<Live.BigBankplc.bigbankdb02>> as Live.BigBankplc.bigbankdb02 { + rectangle "Oracle - Secondary\n<size:10>[Deployment Node: Oracle 12c]</size>" <<Live.BigBankplc.bigbankdb02.OracleSecondary>> as Live.BigBankplc.bigbankdb02.OracleSecondary { + database "==Database\n<size:10>[Container: Oracle Database Schema]</size>\n\nStores user registration information, hashed authentication credentials, access logs, etc." <<Live.BigBankplc.bigbankdb02.OracleSecondary.Database_2>> as Live.BigBankplc.bigbankdb02.OracleSecondary.Database_2 + } + + } + +} + +Live.BigBankplc.bigbankweb.ApacheTomcat.WebApplication_1 .[#707070,thickness=2].> Live.Customerscomputer.WebBrowser.SinglePageApplication_1 : "<color:#707070>Delivers to the customer's web browser" +Live.Customersmobiledevice.MobileApp_1 .[#707070,thickness=2].> Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1 : "<color:#707070>Makes API calls to\n<color:#707070><size:8>[JSON/HTTPS]</size>" +Live.Customerscomputer.WebBrowser.SinglePageApplication_1 .[#707070,thickness=2].> Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1 : "<color:#707070>Makes API calls to\n<color:#707070><size:8>[JSON/HTTPS]</size>" +Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1 .[#707070,thickness=2].> Live.BigBankplc.bigbankprod001.MainframeBankingSystem_1 : "<color:#707070>Makes API calls to\n<color:#707070><size:8>[XML/HTTPS]</size>" +Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1 .[#707070,thickness=2].> Live.BigBankplc.bigbankdb01.OraclePrimary.Database_1 : "<color:#707070>Reads from and writes to\n<color:#707070><size:8>[JDBC]</size>" +Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1 .[#707070,thickness=2].> Live.BigBankplc.bigbankdb02.OracleSecondary.Database_2 : "<color:#707070>Reads from and writes to\n<color:#707070><size:8>[JDBC]</size>" +Live.BigBankplc.bigbankdb01.OraclePrimary .[#707070,thickness=2].> Live.BigBankplc.bigbankdb02.OracleSecondary : "<color:#707070>Replicates data to" +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SignIn-sequence.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SignIn-sequence.puml new file mode 100644 index 000000000..eb84e8096 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SignIn-sequence.puml @@ -0,0 +1,49 @@ +@startuml +set separator none +title API Application - Dynamic + +skinparam { + arrowFontSize 10 + defaultTextAlignment center + wrapWidth 200 + maxMessageSize 100 +} + +hide stereotype + +skinparam sequenceParticipant<<InternetBankingSystem.Database>> { + BackgroundColor #438dd5 + FontColor #ffffff + BorderColor #2e6295 + shadowing false +} +skinparam sequenceParticipant<<InternetBankingSystem.APIApplication.SecurityComponent>> { + BackgroundColor #85bbf0 + FontColor #000000 + BorderColor #5d82a8 + shadowing false +} +skinparam sequenceParticipant<<InternetBankingSystem.APIApplication.SignInController>> { + BackgroundColor #85bbf0 + FontColor #000000 + BorderColor #5d82a8 + shadowing false +} +skinparam sequenceParticipant<<InternetBankingSystem.SinglePageApplication>> { + BackgroundColor #438dd5 + FontColor #ffffff + BorderColor #2e6295 + shadowing false +} + +participant "Single-Page Application\n<size:10>[Container: JavaScript and Angular]</size>" as InternetBankingSystem.SinglePageApplication <<InternetBankingSystem.SinglePageApplication>> #438dd5 +participant "Sign In Controller\n<size:10>[Component: Spring MVC Rest Controller]</size>" as InternetBankingSystem.APIApplication.SignInController <<InternetBankingSystem.APIApplication.SignInController>> #85bbf0 +participant "Security Component\n<size:10>[Component: Spring Bean]</size>" as InternetBankingSystem.APIApplication.SecurityComponent <<InternetBankingSystem.APIApplication.SecurityComponent>> #85bbf0 +database "Database\n<size:10>[Container: Oracle Database Schema]</size>" as InternetBankingSystem.Database <<InternetBankingSystem.Database>> #438dd5 +InternetBankingSystem.SinglePageApplication -[#707070]> InternetBankingSystem.APIApplication.SignInController : Submits credentials to +InternetBankingSystem.APIApplication.SignInController -[#707070]> InternetBankingSystem.APIApplication.SecurityComponent : Validates credentials using +InternetBankingSystem.APIApplication.SecurityComponent -[#707070]> InternetBankingSystem.Database : select * from users where username = ? +InternetBankingSystem.APIApplication.SecurityComponent <-[#707070]- InternetBankingSystem.Database : Returns user data to +InternetBankingSystem.APIApplication.SignInController <-[#707070]- InternetBankingSystem.APIApplication.SecurityComponent : Returns true if the hashed password matches +InternetBankingSystem.SinglePageApplication <-[#707070]- InternetBankingSystem.APIApplication.SignInController : Sends back an authentication token to +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SignIn.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SignIn.puml new file mode 100644 index 000000000..687ce9d7e --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SignIn.puml @@ -0,0 +1,60 @@ +@startuml +set separator none +title API Application - Dynamic + +top to bottom direction + +skinparam { + arrowFontSize 10 + defaultTextAlignment center + wrapWidth 200 + maxMessageSize 100 +} + +hide stereotype + +skinparam database<<InternetBankingSystem.Database>> { + BackgroundColor #438dd5 + FontColor #ffffff + BorderColor #2e6295 + shadowing false +} +skinparam rectangle<<InternetBankingSystem.APIApplication.SecurityComponent>> { + BackgroundColor #85bbf0 + FontColor #000000 + BorderColor #5d82a8 + shadowing false +} +skinparam rectangle<<InternetBankingSystem.APIApplication.SignInController>> { + BackgroundColor #85bbf0 + FontColor #000000 + BorderColor #5d82a8 + shadowing false +} +skinparam rectangle<<InternetBankingSystem.SinglePageApplication>> { + BackgroundColor #438dd5 + FontColor #ffffff + BorderColor #2e6295 + shadowing false +} +skinparam rectangle<<InternetBankingSystem.APIApplication>> { + BorderColor #2e6295 + FontColor #2e6295 + shadowing false +} + +rectangle "API Application\n<size:10>[Container: Java and Spring MVC]</size>" <<InternetBankingSystem.APIApplication>> { + rectangle "==Sign In Controller\n<size:10>[Component: Spring MVC Rest Controller]</size>\n\nAllows users to sign in to the Internet Banking System." <<InternetBankingSystem.APIApplication.SignInController>> as InternetBankingSystem.APIApplication.SignInController + rectangle "==Security Component\n<size:10>[Component: Spring Bean]</size>\n\nProvides functionality related to signing in, changing passwords, etc." <<InternetBankingSystem.APIApplication.SecurityComponent>> as InternetBankingSystem.APIApplication.SecurityComponent +} + +rectangle "==Single-Page Application\n<size:10>[Container: JavaScript and Angular]</size>\n\nProvides all of the Internet banking functionality to customers via their web browser." <<InternetBankingSystem.SinglePageApplication>> as InternetBankingSystem.SinglePageApplication +database "==Database\n<size:10>[Container: Oracle Database Schema]</size>\n\nStores user registration information, hashed authentication credentials, access logs, etc." <<InternetBankingSystem.Database>> as InternetBankingSystem.Database + +InternetBankingSystem.SinglePageApplication .[#707070,thickness=2].> InternetBankingSystem.APIApplication.SignInController : "<color:#707070>1. Submits credentials to\n<color:#707070><size:8>[JSON/HTTPS]</size>" +InternetBankingSystem.APIApplication.SignInController .[#707070,thickness=2].> InternetBankingSystem.APIApplication.SecurityComponent : "<color:#707070>2. Validates credentials using" +InternetBankingSystem.APIApplication.SecurityComponent .[#707070,thickness=2].> InternetBankingSystem.Database : "<color:#707070>3. select * from users where username = ?\n<color:#707070><size:8>[JDBC]</size>" +InternetBankingSystem.APIApplication.SecurityComponent <.[#707070,thickness=2]. InternetBankingSystem.Database : "<color:#707070>4. Returns user data to\n<color:#707070><size:8>[JDBC]</size>" +InternetBankingSystem.APIApplication.SignInController <.[#707070,thickness=2]. InternetBankingSystem.APIApplication.SecurityComponent : "<color:#707070>5. Returns true if the hashed password matches" +InternetBankingSystem.SinglePageApplication <.[#707070,thickness=2]. InternetBankingSystem.APIApplication.SignInController : "<color:#707070>6. Sends back an authentication token to\n<color:#707070><size:8>[JSON/HTTPS]</size>" +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemContext.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemContext.puml new file mode 100644 index 000000000..36302bdb3 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemContext.puml @@ -0,0 +1,50 @@ +@startuml +set separator none +title Internet Banking System - System Context + +top to bottom direction + +skinparam { + arrowFontSize 10 + defaultTextAlignment center + wrapWidth 200 + maxMessageSize 100 +} + +hide stereotype + +skinparam rectangle<<EmailSystem>> { + BackgroundColor #999999 + FontColor #ffffff + BorderColor #6b6b6b + shadowing false +} +skinparam rectangle<<InternetBankingSystem>> { + BackgroundColor #1168bd + FontColor #ffffff + BorderColor #0b4884 + shadowing false +} +skinparam rectangle<<MainframeBankingSystem>> { + BackgroundColor #999999 + FontColor #ffffff + BorderColor #6b6b6b + shadowing false +} +skinparam person<<PersonalBankingCustomer>> { + BackgroundColor #08427b + FontColor #ffffff + BorderColor #052e56 + shadowing false +} + +person "==Personal Banking Customer\n<size:10>[Person]</size>\n\nA customer of the bank, with personal bank accounts." <<PersonalBankingCustomer>> as PersonalBankingCustomer +rectangle "==Internet Banking System\n<size:10>[Software System]</size>\n\nAllows customers to view information about their bank accounts, and make payments." <<InternetBankingSystem>> as InternetBankingSystem +rectangle "==Mainframe Banking System\n<size:10>[Software System]</size>\n\nStores all of the core banking information about customers, accounts, transactions, etc." <<MainframeBankingSystem>> as MainframeBankingSystem +rectangle "==E-mail System\n<size:10>[Software System]</size>\n\nThe internal Microsoft Exchange e-mail system." <<EmailSystem>> as EmailSystem + +PersonalBankingCustomer .[#707070,thickness=2].> InternetBankingSystem : "<color:#707070>Views account balances, and makes payments using" +InternetBankingSystem .[#707070,thickness=2].> MainframeBankingSystem : "<color:#707070>Gets account information from, and makes payments using" +InternetBankingSystem .[#707070,thickness=2].> EmailSystem : "<color:#707070>Sends e-mail using" +EmailSystem .[#707070,thickness=2].> PersonalBankingCustomer : "<color:#707070>Sends e-mails to" +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemLandscape.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemLandscape.puml new file mode 100644 index 000000000..511b1a16c --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemLandscape.puml @@ -0,0 +1,82 @@ +@startuml +set separator none +title System Landscape + +top to bottom direction + +skinparam { + arrowFontSize 10 + defaultTextAlignment center + wrapWidth 200 + maxMessageSize 100 +} + +hide stereotype + +skinparam rectangle<<ATM>> { + BackgroundColor #999999 + FontColor #ffffff + BorderColor #6b6b6b + shadowing false +} +skinparam person<<BackOfficeStaff>> { + BackgroundColor #999999 + FontColor #ffffff + BorderColor #6b6b6b + shadowing false +} +skinparam person<<CustomerServiceStaff>> { + BackgroundColor #999999 + FontColor #ffffff + BorderColor #6b6b6b + shadowing false +} +skinparam rectangle<<EmailSystem>> { + BackgroundColor #999999 + FontColor #ffffff + BorderColor #6b6b6b + shadowing false +} +skinparam rectangle<<InternetBankingSystem>> { + BackgroundColor #1168bd + FontColor #ffffff + BorderColor #0b4884 + shadowing false +} +skinparam rectangle<<MainframeBankingSystem>> { + BackgroundColor #999999 + FontColor #ffffff + BorderColor #6b6b6b + shadowing false +} +skinparam person<<PersonalBankingCustomer>> { + BackgroundColor #08427b + FontColor #ffffff + BorderColor #052e56 + shadowing false +} + +rectangle "Big Bank plc" <<enterprise>> { + skinparam RectangleBorderColor<<enterprise>> #444444 + skinparam RectangleFontColor<<enterprise>> #444444 + + person "==Customer Service Staff\n<size:10>[Person]</size>\n\nCustomer service staff within the bank." <<CustomerServiceStaff>> as CustomerServiceStaff + person "==Back Office Staff\n<size:10>[Person]</size>\n\nAdministration and support staff within the bank." <<BackOfficeStaff>> as BackOfficeStaff + rectangle "==Internet Banking System\n<size:10>[Software System]</size>\n\nAllows customers to view information about their bank accounts, and make payments." <<InternetBankingSystem>> as InternetBankingSystem + rectangle "==Mainframe Banking System\n<size:10>[Software System]</size>\n\nStores all of the core banking information about customers, accounts, transactions, etc." <<MainframeBankingSystem>> as MainframeBankingSystem + rectangle "==E-mail System\n<size:10>[Software System]</size>\n\nThe internal Microsoft Exchange e-mail system." <<EmailSystem>> as EmailSystem + rectangle "==ATM\n<size:10>[Software System]</size>\n\nAllows customers to withdraw cash." <<ATM>> as ATM +} + +person "==Personal Banking Customer\n<size:10>[Person]</size>\n\nA customer of the bank, with personal bank accounts." <<PersonalBankingCustomer>> as PersonalBankingCustomer + +ATM .[#707070,thickness=2].> MainframeBankingSystem : "<color:#707070>Uses" +PersonalBankingCustomer .[#707070,thickness=2].> ATM : "<color:#707070>Withdraws cash using" +CustomerServiceStaff .[#707070,thickness=2].> MainframeBankingSystem : "<color:#707070>Uses" +PersonalBankingCustomer .[#707070,thickness=2].> CustomerServiceStaff : "<color:#707070>Asks questions to\n<color:#707070><size:8>[Telephone]</size>" +BackOfficeStaff .[#707070,thickness=2].> MainframeBankingSystem : "<color:#707070>Uses" +PersonalBankingCustomer .[#707070,thickness=2].> InternetBankingSystem : "<color:#707070>Views account balances, and makes payments using" +InternetBankingSystem .[#707070,thickness=2].> MainframeBankingSystem : "<color:#707070>Gets account information from, and makes payments using" +InternetBankingSystem .[#707070,thickness=2].> EmailSystem : "<color:#707070>Sends e-mail using" +EmailSystem .[#707070,thickness=2].> PersonalBankingCustomer : "<color:#707070>Sends e-mails to" +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/54915-AmazonWebServicesDeployment-Legend.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/54915-AmazonWebServicesDeployment-Legend.puml new file mode 100644 index 000000000..3532dad2d --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/54915-AmazonWebServicesDeployment-Legend.puml @@ -0,0 +1,102 @@ +@startuml +set separator none + +skinparam { + shadowing false + arrowFontSize 15 + defaultTextAlignment center + wrapWidth 100 + maxMessageSize 100 +} +hide stereotype + +skinparam rectangle<<_transparent>> { + BorderColor transparent + BackgroundColor transparent + FontColor transparent +} + +skinparam rectangle<<1>> { + BackgroundColor #ffffff + FontColor #cc2264 + BorderColor #cc2264 + roundCorner 20 +} +rectangle "==Amazon Web Services - Auto Scaling\n\n<img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/AWS-Auto-Scaling_light-bg@4x.png{scale=0.1}>" <<1>> + +skinparam rectangle<<2>> { + BackgroundColor #ffffff + FontColor #232f3e + BorderColor #232f3e + roundCorner 20 +} +rectangle "==Amazon Web Services - Cloud\n\n<img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/AWS-Cloud_light-bg@4x.png{scale=0.21428571428571427}>" <<2>> + +skinparam rectangle<<3>> { + BackgroundColor #ffffff + FontColor #d86613 + BorderColor #d86613 + roundCorner 20 +} +rectangle "==Amazon Web Services - EC2\n\n<img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Amazon-EC2_light-bg@4x.png{scale=0.1}>" <<3>> + +skinparam rectangle<<4>> { + BackgroundColor #ffffff + FontColor #693cc5 + BorderColor #693cc5 + roundCorner 20 +} +rectangle "==Amazon Web Services - Elastic Load Balancing\n\n<img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Elastic-Load-Balancing_light-bg@4x.png{scale=0.1}>" <<4>> + +skinparam rectangle<<5>> { + BackgroundColor #ffffff + FontColor #3b48cc + BorderColor #3b48cc + roundCorner 20 +} +rectangle "==Amazon Web Services - RDS\n\n<img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Amazon-RDS_light-bg@4x.png{scale=0.1}>" <<5>> + +skinparam rectangle<<6>> { + BackgroundColor #ffffff + FontColor #3b48cc + BorderColor #3b48cc + roundCorner 20 +} +rectangle "==Amazon Web Services - RDS MySQL instance\n\n<img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Amazon-RDS_MySQL_instance_light-bg@4x.png{scale=0.15}>" <<6>> + +skinparam rectangle<<7>> { + BackgroundColor #ffffff + FontColor #147eba + BorderColor #147eba + roundCorner 20 +} +rectangle "==Amazon Web Services - Region\n\n<img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Region_light-bg@4x.png{scale=0.21428571428571427}>" <<7>> + +skinparam rectangle<<8>> { + BackgroundColor #ffffff + FontColor #693cc5 + BorderColor #693cc5 + roundCorner 20 +} +rectangle "==Amazon Web Services - Route 53\n\n<img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Amazon-Route-53_light-bg@4x.png{scale=0.1}>" <<8>> + +skinparam rectangle<<9>> { + BackgroundColor #ffffff + FontColor #000000 + BorderColor #b2b2b2 + roundCorner 20 +} +rectangle "==Container, Application" <<9>> + +skinparam database<<10>> { + BackgroundColor #ffffff + FontColor #000000 + BorderColor #b2b2b2 +} +database "==Container, Database" <<10>> + +rectangle "." <<_transparent>> as 11 +11 .[#707070,thickness=2].> 11 : "<color:#707070>Relationship" + + +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/54915-AmazonWebServicesDeployment.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/54915-AmazonWebServicesDeployment.puml new file mode 100644 index 000000000..b0799c732 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/54915-AmazonWebServicesDeployment.puml @@ -0,0 +1,111 @@ +@startuml +set separator none +title Spring PetClinic - Deployment - Live + +left to right direction + +skinparam { + arrowFontSize 10 + defaultTextAlignment center + wrapWidth 200 + maxMessageSize 100 +} + +hide stereotype + +skinparam rectangle<<Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2>> { + BackgroundColor #ffffff + FontColor #d86613 + BorderColor #d86613 + roundCorner 20 + shadowing false +} +skinparam rectangle<<Live.AmazonWebServices.USEast1.AmazonRDS>> { + BackgroundColor #ffffff + FontColor #3b48cc + BorderColor #3b48cc + roundCorner 20 + shadowing false +} +skinparam rectangle<<Live.AmazonWebServices>> { + BackgroundColor #ffffff + FontColor #232f3e + BorderColor #232f3e + roundCorner 20 + shadowing false +} +skinparam rectangle<<Live.AmazonWebServices.USEast1.Autoscalinggroup>> { + BackgroundColor #ffffff + FontColor #cc2264 + BorderColor #cc2264 + roundCorner 20 + shadowing false +} +skinparam database<<Live.AmazonWebServices.USEast1.AmazonRDS.MySQL.Database_1>> { + BackgroundColor #ffffff + FontColor #000000 + BorderColor #b2b2b2 + shadowing false +} +skinparam rectangle<<Live.AmazonWebServices.USEast1.ElasticLoadBalancer>> { + BackgroundColor #ffffff + FontColor #693cc5 + BorderColor #693cc5 + roundCorner 20 + shadowing false +} +skinparam rectangle<<Live.AmazonWebServices.USEast1.AmazonRDS.MySQL>> { + BackgroundColor #ffffff + FontColor #3b48cc + BorderColor #3b48cc + roundCorner 20 + shadowing false +} +skinparam rectangle<<Live.AmazonWebServices.USEast1.Route53>> { + BackgroundColor #ffffff + FontColor #693cc5 + BorderColor #693cc5 + roundCorner 20 + shadowing false +} +skinparam rectangle<<Live.AmazonWebServices.USEast1>> { + BackgroundColor #ffffff + FontColor #147eba + BorderColor #147eba + roundCorner 20 + shadowing false +} +skinparam rectangle<<Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2.WebApplication_1>> { + BackgroundColor #ffffff + FontColor #000000 + BorderColor #b2b2b2 + roundCorner 20 + shadowing false +} + +rectangle "Amazon Web Services\n<size:10>[Deployment Node]</size>\n\n<img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/AWS-Cloud_light-bg@4x.png{scale=0.21428571428571427}>" <<Live.AmazonWebServices>> as Live.AmazonWebServices { + rectangle "US-East-1\n<size:10>[Deployment Node]</size>\n\n<img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Region_light-bg@4x.png{scale=0.21428571428571427}>" <<Live.AmazonWebServices.USEast1>> as Live.AmazonWebServices.USEast1 { + rectangle "Amazon RDS\n<size:10>[Deployment Node]</size>\n\n<img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Amazon-RDS_light-bg@4x.png{scale=0.1}>" <<Live.AmazonWebServices.USEast1.AmazonRDS>> as Live.AmazonWebServices.USEast1.AmazonRDS { + rectangle "MySQL\n<size:10>[Deployment Node]</size>\n\n<img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Amazon-RDS_MySQL_instance_light-bg@4x.png{scale=0.15}>" <<Live.AmazonWebServices.USEast1.AmazonRDS.MySQL>> as Live.AmazonWebServices.USEast1.AmazonRDS.MySQL { + database "==Database\n<size:10>[Container: Relational database schema]</size>\n\nStores information regarding the veterinarians, the clients, and their pets." <<Live.AmazonWebServices.USEast1.AmazonRDS.MySQL.Database_1>> as Live.AmazonWebServices.USEast1.AmazonRDS.MySQL.Database_1 + } + + } + + rectangle "==Route 53\n<size:10>[Infrastructure Node]</size>\n\nHighly available and scalable cloud DNS service.\n\n<img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Amazon-Route-53_light-bg@4x.png{scale=0.1}>" <<Live.AmazonWebServices.USEast1.Route53>> as Live.AmazonWebServices.USEast1.Route53 + rectangle "==Elastic Load Balancer\n<size:10>[Infrastructure Node]</size>\n\nAutomatically distributes incoming application traffic.\n\n<img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Elastic-Load-Balancing_light-bg@4x.png{scale=0.1}>" <<Live.AmazonWebServices.USEast1.ElasticLoadBalancer>> as Live.AmazonWebServices.USEast1.ElasticLoadBalancer + rectangle "Autoscaling group\n<size:10>[Deployment Node]</size>\n\n<img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/AWS-Auto-Scaling_light-bg@4x.png{scale=0.1}>" <<Live.AmazonWebServices.USEast1.Autoscalinggroup>> as Live.AmazonWebServices.USEast1.Autoscalinggroup { + rectangle "Amazon EC2\n<size:10>[Deployment Node]</size>\n\n<img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Amazon-EC2_light-bg@4x.png{scale=0.1}>" <<Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2>> as Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2 { + rectangle "==Web Application\n<size:10>[Container: Java and Spring Boot]</size>\n\nAllows employees to view and manage information regarding the veterinarians, the clients, and their pets." <<Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2.WebApplication_1>> as Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2.WebApplication_1 + } + + } + + } + +} + +Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2.WebApplication_1 .[#707070,thickness=2].> Live.AmazonWebServices.USEast1.AmazonRDS.MySQL.Database_1 : "<color:#707070>Reads from and writes to\n<color:#707070><size:8>[MySQL Protocol/SSL]</size>" +Live.AmazonWebServices.USEast1.Route53 .[#707070,thickness=2].> Live.AmazonWebServices.USEast1.ElasticLoadBalancer : "<color:#707070>Forwards requests to\n<color:#707070><size:8>[HTTPS]</size>" +Live.AmazonWebServices.USEast1.ElasticLoadBalancer .[#707070,thickness=2].> Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2.WebApplication_1 : "<color:#707070>Forwards requests to\n<color:#707070><size:8>[HTTPS]</size>" +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/component-view-with-external-components-1.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/component-view-with-external-components-1.puml new file mode 100644 index 000000000..524f39cc9 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/component-view-with-external-components-1.puml @@ -0,0 +1,56 @@ +@startuml +set separator none +title Software System 1 - Container 1 - Components + +top to bottom direction + +skinparam { + arrowFontSize 10 + defaultTextAlignment center + wrapWidth 200 + maxMessageSize 100 +} + +hide stereotype + +skinparam rectangle<<SoftwareSystem1.Container1.Component1>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} +skinparam rectangle<<SoftwareSystem1.Container1.Component2>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} +skinparam rectangle<<SoftwareSystem2.Container2.Component3>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} +skinparam rectangle<<SoftwareSystem1.Container1>> { + BorderColor #9a9a9a + FontColor #9a9a9a + shadowing false +} +skinparam rectangle<<SoftwareSystem2.Container2>> { + BorderColor #9a9a9a + FontColor #9a9a9a + shadowing false +} + +rectangle "Container 1\n<size:10>[Container]</size>" <<SoftwareSystem1.Container1>> { + rectangle "==Component 1\n<size:10>[Component]</size>" <<SoftwareSystem1.Container1.Component1>> as SoftwareSystem1.Container1.Component1 + rectangle "==Component 2\n<size:10>[Component]</size>" <<SoftwareSystem1.Container1.Component2>> as SoftwareSystem1.Container1.Component2 +} + +rectangle "Container 2\n<size:10>[Container]</size>" <<SoftwareSystem2.Container2>> { + rectangle "==Component 3\n<size:10>[Component]</size>" <<SoftwareSystem2.Container2.Component3>> as SoftwareSystem2.Container2.Component3 +} + +SoftwareSystem1.Container1.Component1 .[#707070,thickness=2].> SoftwareSystem1.Container1.Component2 : "<color:#707070>Uses" +SoftwareSystem1.Container1.Component2 .[#707070,thickness=2].> SoftwareSystem2.Container2.Component3 : "<color:#707070>Uses" +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/component-view-with-external-components-2.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/component-view-with-external-components-2.puml new file mode 100644 index 000000000..4888c4988 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/component-view-with-external-components-2.puml @@ -0,0 +1,62 @@ +@startuml +set separator none +title Software System 1 - Container 1 - Components + +top to bottom direction + +skinparam { + arrowFontSize 10 + defaultTextAlignment center + wrapWidth 200 + maxMessageSize 100 +} + +hide stereotype + +skinparam rectangle<<SoftwareSystem1.Container1.Component1>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} +skinparam rectangle<<SoftwareSystem1.Container1.Component2>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} +skinparam rectangle<<SoftwareSystem2.Container2.Component3>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} +skinparam rectangle<<SoftwareSystem1.Container1>> { + BorderColor #9a9a9a + FontColor #9a9a9a + shadowing false +} +skinparam rectangle<<SoftwareSystem2.Container2>> { + BorderColor #9a9a9a + FontColor #9a9a9a + shadowing false +} + +rectangle "Software System 1\n<size:10>[Software System]</size>" <<SoftwareSystem1>> { + rectangle "Container 1\n<size:10>[Container]</size>" <<SoftwareSystem1.Container1>> { + rectangle "==Component 1\n<size:10>[Component]</size>" <<SoftwareSystem1.Container1.Component1>> as SoftwareSystem1.Container1.Component1 + rectangle "==Component 2\n<size:10>[Component]</size>" <<SoftwareSystem1.Container1.Component2>> as SoftwareSystem1.Container1.Component2 + } + + } + +rectangle "Software System 2\n<size:10>[Software System]</size>" <<SoftwareSystem2>> { + rectangle "Container 2\n<size:10>[Container]</size>" <<SoftwareSystem2.Container2>> { + rectangle "==Component 3\n<size:10>[Component]</size>" <<SoftwareSystem2.Container2.Component3>> as SoftwareSystem2.Container2.Component3 + } + + } + +SoftwareSystem1.Container1.Component1 .[#707070,thickness=2].> SoftwareSystem1.Container1.Component2 : "<color:#707070>Uses" +SoftwareSystem1.Container1.Component2 .[#707070,thickness=2].> SoftwareSystem2.Container2.Component3 : "<color:#707070>Uses" +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-container-scoped-with-groups.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-container-scoped-with-groups.puml new file mode 100644 index 000000000..bd0fade3d --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-container-scoped-with-groups.puml @@ -0,0 +1,62 @@ +@startuml +set separator none +title A - Dynamic + +top to bottom direction + +skinparam { + arrowFontSize 10 + defaultTextAlignment center + wrapWidth 200 + maxMessageSize 100 +} + +hide stereotype + +skinparam rectangle<<A.A.A>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} +skinparam rectangle<<B.B.B>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} +skinparam rectangle<<A.A>> { + BorderColor #9a9a9a + FontColor #9a9a9a + shadowing false +} +skinparam rectangle<<B.B>> { + BorderColor #9a9a9a + FontColor #9a9a9a + shadowing false +} + +rectangle "A\n<size:10>[Container]</size>" <<A.A>> { + rectangle "Group 1" <<group1>> { + skinparam RectangleBorderColor<<group1>> #cccccc + skinparam RectangleFontColor<<group1>> #cccccc + skinparam RectangleBorderStyle<<group1>> dashed + + rectangle "==A\n<size:10>[Component]</size>" <<A.A.A>> as A.A.A + } + +} + +rectangle "B\n<size:10>[Container]</size>" <<B.B>> { + rectangle "Group 2" <<group2>> { + skinparam RectangleBorderColor<<group2>> #cccccc + skinparam RectangleFontColor<<group2>> #cccccc + skinparam RectangleBorderStyle<<group2>> dashed + + rectangle "==B\n<size:10>[Component]</size>" <<B.B.B>> as B.B.B + } + +} + +A.A.A .[#707070,thickness=2].> B.B.B : "<color:#707070>1. Uses" +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-software-system-scoped-with-groups.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-software-system-scoped-with-groups.puml new file mode 100644 index 000000000..13cc2141b --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-software-system-scoped-with-groups.puml @@ -0,0 +1,62 @@ +@startuml +set separator none +title A - Dynamic + +top to bottom direction + +skinparam { + arrowFontSize 10 + defaultTextAlignment center + wrapWidth 200 + maxMessageSize 100 +} + +hide stereotype + +skinparam rectangle<<A.A>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} +skinparam rectangle<<B.B>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} +skinparam rectangle<<A>> { + BorderColor #9a9a9a + FontColor #9a9a9a + shadowing false +} +skinparam rectangle<<B>> { + BorderColor #9a9a9a + FontColor #9a9a9a + shadowing false +} + +rectangle "A\n<size:10>[Software System]</size>" <<A>> { + rectangle "Group 1" <<group1>> { + skinparam RectangleBorderColor<<group1>> #cccccc + skinparam RectangleFontColor<<group1>> #cccccc + skinparam RectangleBorderStyle<<group1>> dashed + + rectangle "==A\n<size:10>[Container]</size>" <<A.A>> as A.A + } + +} + +rectangle "B\n<size:10>[Software System]</size>" <<B>> { + rectangle "Group 2" <<group2>> { + skinparam RectangleBorderColor<<group2>> #cccccc + skinparam RectangleFontColor<<group2>> #cccccc + skinparam RectangleBorderStyle<<group2>> dashed + + rectangle "==B\n<size:10>[Container]</size>" <<B.B>> as B.B + } + +} + +A.A .[#707070,thickness=2].> B.B : "<color:#707070>1. Uses" +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-unscoped-with-groups.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-unscoped-with-groups.puml new file mode 100644 index 000000000..5b7656db8 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-unscoped-with-groups.puml @@ -0,0 +1,46 @@ +@startuml +set separator none +title Dynamic + +top to bottom direction + +skinparam { + arrowFontSize 10 + defaultTextAlignment center + wrapWidth 200 + maxMessageSize 100 +} + +hide stereotype + +skinparam rectangle<<A>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} +skinparam rectangle<<B>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} + +rectangle "Group 1" <<group1>> { + skinparam RectangleBorderColor<<group1>> #cccccc + skinparam RectangleFontColor<<group1>> #cccccc + skinparam RectangleBorderStyle<<group1>> dashed + + rectangle "==A\n<size:10>[Software System]</size>" <<A>> as A +} + +rectangle "Group 2" <<group2>> { + skinparam RectangleBorderColor<<group2>> #cccccc + skinparam RectangleFontColor<<group2>> #cccccc + skinparam RectangleBorderStyle<<group2>> dashed + + rectangle "==B\n<size:10>[Software System]</size>" <<B>> as B +} + +A .[#707070,thickness=2].> B : "<color:#707070>1. Uses" +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-with-external-components-1.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-with-external-components-1.puml new file mode 100644 index 000000000..ba9a23a8a --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-with-external-components-1.puml @@ -0,0 +1,56 @@ +@startuml +set separator none +title Container 1 - Dynamic + +top to bottom direction + +skinparam { + arrowFontSize 10 + defaultTextAlignment center + wrapWidth 200 + maxMessageSize 100 +} + +hide stereotype + +skinparam rectangle<<SoftwareSystem1.Container1.Component1>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} +skinparam rectangle<<SoftwareSystem1.Container1.Component2>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} +skinparam rectangle<<SoftwareSystem2.Container2.Component3>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} +skinparam rectangle<<SoftwareSystem1.Container1>> { + BorderColor #9a9a9a + FontColor #9a9a9a + shadowing false +} +skinparam rectangle<<SoftwareSystem2.Container2>> { + BorderColor #9a9a9a + FontColor #9a9a9a + shadowing false +} + +rectangle "Container 1\n<size:10>[Container]</size>" <<SoftwareSystem1.Container1>> { + rectangle "==Component 1\n<size:10>[Component]</size>" <<SoftwareSystem1.Container1.Component1>> as SoftwareSystem1.Container1.Component1 + rectangle "==Component 2\n<size:10>[Component]</size>" <<SoftwareSystem1.Container1.Component2>> as SoftwareSystem1.Container1.Component2 +} + +rectangle "Container 2\n<size:10>[Container]</size>" <<SoftwareSystem2.Container2>> { + rectangle "==Component 3\n<size:10>[Component]</size>" <<SoftwareSystem2.Container2.Component3>> as SoftwareSystem2.Container2.Component3 +} + +SoftwareSystem1.Container1.Component1 .[#707070,thickness=2].> SoftwareSystem1.Container1.Component2 : "<color:#707070>1. Uses" +SoftwareSystem1.Container1.Component2 .[#707070,thickness=2].> SoftwareSystem2.Container2.Component3 : "<color:#707070>2. Uses" +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-with-external-components-2.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-with-external-components-2.puml new file mode 100644 index 000000000..588a26fb6 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-with-external-components-2.puml @@ -0,0 +1,62 @@ +@startuml +set separator none +title Container 1 - Dynamic + +top to bottom direction + +skinparam { + arrowFontSize 10 + defaultTextAlignment center + wrapWidth 200 + maxMessageSize 100 +} + +hide stereotype + +skinparam rectangle<<SoftwareSystem1.Container1.Component1>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} +skinparam rectangle<<SoftwareSystem1.Container1.Component2>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} +skinparam rectangle<<SoftwareSystem2.Container2.Component3>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} +skinparam rectangle<<SoftwareSystem1.Container1>> { + BorderColor #9a9a9a + FontColor #9a9a9a + shadowing false +} +skinparam rectangle<<SoftwareSystem2.Container2>> { + BorderColor #9a9a9a + FontColor #9a9a9a + shadowing false +} + +rectangle "Software System 1\n<size:10>[Software System]</size>" <<SoftwareSystem1>> { + rectangle "Container 1\n<size:10>[Container]</size>" <<SoftwareSystem1.Container1>> { + rectangle "==Component 1\n<size:10>[Component]</size>" <<SoftwareSystem1.Container1.Component1>> as SoftwareSystem1.Container1.Component1 + rectangle "==Component 2\n<size:10>[Component]</size>" <<SoftwareSystem1.Container1.Component2>> as SoftwareSystem1.Container1.Component2 + } + + } + +rectangle "Software System 2\n<size:10>[Software System]</size>" <<SoftwareSystem2>> { + rectangle "Container 2\n<size:10>[Container]</size>" <<SoftwareSystem2.Container2>> { + rectangle "==Component 3\n<size:10>[Component]</size>" <<SoftwareSystem2.Container2.Component3>> as SoftwareSystem2.Container2.Component3 + } + + } + +SoftwareSystem1.Container1.Component1 .[#707070,thickness=2].> SoftwareSystem1.Container1.Component2 : "<color:#707070>1. Uses" +SoftwareSystem1.Container1.Component2 .[#707070,thickness=2].> SoftwareSystem2.Container2.Component3 : "<color:#707070>2. Uses" +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/group-styles-1.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/group-styles-1.puml new file mode 100644 index 000000000..8406d0994 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/group-styles-1.puml @@ -0,0 +1,60 @@ +@startuml +set separator none +title System Landscape + +top to bottom direction + +skinparam { + arrowFontSize 10 + defaultTextAlignment center + wrapWidth 200 + maxMessageSize 100 +} + +hide stereotype + +skinparam rectangle<<User1>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} +skinparam rectangle<<User2>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} +skinparam rectangle<<User3>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} + +rectangle "Group 1\n\n<img:https://example.com/icon1.png{scale=1.0}>" <<group1>> { + skinparam RectangleBorderColor<<group1>> #111111 + skinparam RectangleFontColor<<group1>> #111111 + skinparam RectangleBorderStyle<<group1>> dashed + + rectangle "==User 1\n<size:10>[Person]</size>" <<User1>> as User1 +} + +rectangle "Group 2\n\n<img:https://example.com/icon2.png{scale=1.0}>" <<group2>> { + skinparam RectangleBorderColor<<group2>> #222222 + skinparam RectangleFontColor<<group2>> #222222 + skinparam RectangleBorderStyle<<group2>> dashed + + rectangle "==User 2\n<size:10>[Person]</size>" <<User2>> as User2 +} + +rectangle "Group 3" <<group3>> { + skinparam RectangleBorderColor<<group3>> #cccccc + skinparam RectangleFontColor<<group3>> #cccccc + skinparam RectangleBorderStyle<<group3>> dashed + + rectangle "==User 3\n<size:10>[Person]</size>" <<User3>> as User3 +} + + +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/group-styles-2.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/group-styles-2.puml new file mode 100644 index 000000000..5c51fe4d3 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/group-styles-2.puml @@ -0,0 +1,60 @@ +@startuml +set separator none +title System Landscape + +top to bottom direction + +skinparam { + arrowFontSize 10 + defaultTextAlignment center + wrapWidth 200 + maxMessageSize 100 +} + +hide stereotype + +skinparam rectangle<<User1>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} +skinparam rectangle<<User2>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} +skinparam rectangle<<User3>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} + +rectangle "Group 1\n\n<img:https://example.com/icon1.png{scale=1.0}>" <<group1>> { + skinparam RectangleBorderColor<<group1>> #111111 + skinparam RectangleFontColor<<group1>> #111111 + skinparam RectangleBorderStyle<<group1>> dashed + + rectangle "==User 1\n<size:10>[Person]</size>" <<User1>> as User1 +} + +rectangle "Group 2\n\n<img:https://example.com/icon2.png{scale=1.0}>" <<group2>> { + skinparam RectangleBorderColor<<group2>> #222222 + skinparam RectangleFontColor<<group2>> #222222 + skinparam RectangleBorderStyle<<group2>> dashed + + rectangle "==User 2\n<size:10>[Person]</size>" <<User2>> as User2 +} + +rectangle "Group 3" <<group3>> { + skinparam RectangleBorderColor<<group3>> #aabbcc + skinparam RectangleFontColor<<group3>> #aabbcc + skinparam RectangleBorderStyle<<group3>> dashed + + rectangle "==User 3\n<size:10>[Person]</size>" <<User3>> as User3 +} + + +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-Components.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-Components.puml new file mode 100644 index 000000000..0cc6d9bd4 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-Components.puml @@ -0,0 +1,56 @@ +@startuml +set separator none +title D - F - Components + +top to bottom direction + +skinparam { + arrowFontSize 10 + defaultTextAlignment center + wrapWidth 200 + maxMessageSize 100 +} + +hide stereotype + +skinparam rectangle<<C>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} +skinparam rectangle<<D.F.G>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} +skinparam rectangle<<D.F.H>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} +skinparam rectangle<<D.F>> { + BorderColor #9a9a9a + FontColor #9a9a9a + shadowing false +} + +rectangle "==C\n<size:10>[Software System]</size>" <<C>> as C + +rectangle "F\n<size:10>[Container]</size>" <<D.F>> { + rectangle "Group 4" <<group1>> { + skinparam RectangleBorderColor<<group1>> #cccccc + skinparam RectangleFontColor<<group1>> #cccccc + skinparam RectangleBorderStyle<<group1>> dashed + + rectangle "==H\n<size:10>[Component]</size>" <<D.F.H>> as D.F.H + } + + rectangle "==G\n<size:10>[Component]</size>" <<D.F.G>> as D.F.G +} + +C .[#707070,thickness=2].> D.F.G : "<color:#707070>" +C .[#707070,thickness=2].> D.F.H : "<color:#707070>" +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-Containers.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-Containers.puml new file mode 100644 index 000000000..fd5a74bf5 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-Containers.puml @@ -0,0 +1,56 @@ +@startuml +set separator none +title D - Containers + +top to bottom direction + +skinparam { + arrowFontSize 10 + defaultTextAlignment center + wrapWidth 200 + maxMessageSize 100 +} + +hide stereotype + +skinparam rectangle<<C>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} +skinparam rectangle<<D.E>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} +skinparam rectangle<<D.F>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} +skinparam rectangle<<D>> { + BorderColor #9a9a9a + FontColor #9a9a9a + shadowing false +} + +rectangle "==C\n<size:10>[Software System]</size>" <<C>> as C + +rectangle "D\n<size:10>[Software System]</size>" <<D>> { + rectangle "Group 3" <<group1>> { + skinparam RectangleBorderColor<<group1>> #cccccc + skinparam RectangleFontColor<<group1>> #cccccc + skinparam RectangleBorderStyle<<group1>> dashed + + rectangle "==F\n<size:10>[Container]</size>" <<D.F>> as D.F + } + + rectangle "==E\n<size:10>[Container]</size>" <<D.E>> as D.E +} + +C .[#707070,thickness=2].> D.E : "<color:#707070>" +C .[#707070,thickness=2].> D.F : "<color:#707070>" +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-SystemLandscape.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-SystemLandscape.puml new file mode 100644 index 000000000..094f24a31 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-SystemLandscape.puml @@ -0,0 +1,69 @@ +@startuml +set separator none +title System Landscape + +top to bottom direction + +skinparam { + arrowFontSize 10 + defaultTextAlignment center + wrapWidth 200 + maxMessageSize 100 +} + +hide stereotype + +skinparam rectangle<<A>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} +skinparam rectangle<<B>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} +skinparam rectangle<<C>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} +skinparam rectangle<<D>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} + +rectangle "Enterprise" <<enterprise>> { + skinparam RectangleBorderColor<<enterprise>> #444444 + skinparam RectangleFontColor<<enterprise>> #444444 + + rectangle "Group 2" <<group1>> { + skinparam RectangleBorderColor<<group1>> #cccccc + skinparam RectangleFontColor<<group1>> #cccccc + skinparam RectangleBorderStyle<<group1>> dashed + + rectangle "==D\n<size:10>[Software System]</size>" <<D>> as D + } + + rectangle "==C\n<size:10>[Software System]</size>" <<C>> as C +} + +rectangle "Group 1" <<group2>> { + skinparam RectangleBorderColor<<group2>> #cccccc + skinparam RectangleFontColor<<group2>> #cccccc + skinparam RectangleBorderStyle<<group2>> dashed + + rectangle "==B\n<size:10>[Software System]</size>" <<B>> as B +} + +rectangle "==A\n<size:10>[Software System]</size>" <<A>> as A + +B .[#707070,thickness=2].> C : "<color:#707070>" +C .[#707070,thickness=2].> D : "<color:#707070>" +A .[#707070,thickness=2].> B : "<color:#707070>" +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/nested-groups.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/nested-groups.puml new file mode 100644 index 000000000..3c3d12a53 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/nested-groups.puml @@ -0,0 +1,88 @@ +@startuml +set separator none +title System Landscape + +top to bottom direction + +skinparam { + arrowFontSize 10 + defaultTextAlignment center + wrapWidth 200 + maxMessageSize 100 +} + +hide stereotype + +skinparam rectangle<<Department1>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} +skinparam rectangle<<Organisation1>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} +skinparam rectangle<<Organisation2>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} +skinparam rectangle<<Team1>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} +skinparam rectangle<<Team2>> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false +} + +rectangle "Organisation 1" <<group1>> { + skinparam RectangleBorderColor<<group1>> #cccccc + skinparam RectangleFontColor<<group1>> #cccccc + skinparam RectangleBorderStyle<<group1>> dashed + + rectangle "==Organisation 1\n<size:10>[Software System]</size>" <<Organisation1>> as Organisation1 + rectangle "Department 1" <<group2>> { + skinparam RectangleBorderColor<<group2>> #cccccc + skinparam RectangleFontColor<<group2>> #cccccc + skinparam RectangleBorderStyle<<group2>> dashed + + rectangle "==Department 1\n<size:10>[Software System]</size>" <<Department1>> as Department1 + rectangle "Team 1" <<group3>> { + skinparam RectangleBorderColor<<group3>> #ff0000 + skinparam RectangleFontColor<<group3>> #ff0000 + skinparam RectangleBorderStyle<<group3>> dashed + + rectangle "==Team 1\n<size:10>[Software System]</size>" <<Team1>> as Team1 + } + + rectangle "Team 2" <<group4>> { + skinparam RectangleBorderColor<<group4>> #0000ff + skinparam RectangleFontColor<<group4>> #0000ff + skinparam RectangleBorderStyle<<group4>> dashed + + rectangle "==Team 2\n<size:10>[Software System]</size>" <<Team2>> as Team2 + } + + } + +} + +rectangle "Organisation 2" <<group5>> { + skinparam RectangleBorderColor<<group5>> #cccccc + skinparam RectangleFontColor<<group5>> #cccccc + skinparam RectangleBorderStyle<<group5>> dashed + + rectangle "==Organisation 2\n<size:10>[Software System]</size>" <<Organisation2>> as Organisation2 +} + + +@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/websequencediagrams/36141-SignIn.wsd b/structurizr-export/src/test/java/com/structurizr/export/websequencediagrams/36141-SignIn.wsd new file mode 100644 index 000000000..240ee181c --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/websequencediagrams/36141-SignIn.wsd @@ -0,0 +1,13 @@ +title API Application - Dynamic - SignIn + +participant <<Container>>\nSingle-Page Application as Single-Page Application +participant <<Component>>\nSign In Controller as Sign In Controller +participant <<Component>>\nSecurity Component as Security Component +participant <<Container>>\nDatabase as Database + +Single-Page Application->Sign In Controller: Submits credentials to +Sign In Controller->Security Component: Validates credentials using +Security Component->Database: select * from users where username = ? +Database-->Security Component: Returns user data to +Security Component-->Sign In Controller: Returns true if the hashed password matches +Sign In Controller-->Single-Page Application: Sends back an authentication token to \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/websequencediagrams/WebSequenceDiagramsExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/websequencediagrams/WebSequenceDiagramsExporterTests.java new file mode 100644 index 000000000..e0a4c5229 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/websequencediagrams/WebSequenceDiagramsExporterTests.java @@ -0,0 +1,53 @@ +package com.structurizr.export.websequencediagrams; + +import com.structurizr.Workspace; +import com.structurizr.export.AbstractExporterTests; +import com.structurizr.export.Diagram; +import com.structurizr.model.SoftwareSystem; +import com.structurizr.util.WorkspaceUtils; +import com.structurizr.view.DynamicView; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.util.Collection; + +import static org.junit.jupiter.api.Assertions.*; + +public class WebSequenceDiagramsExporterTests extends AbstractExporterTests { + + @Test + public void test_BigBankPlcExample() throws Exception { + Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/structurizr-36141-workspace.json")); + WebSequenceDiagramsExporter exporter = new WebSequenceDiagramsExporter(); + + Collection<Diagram> diagrams = exporter.export(workspace); + assertEquals(1, diagrams.size()); + + Diagram diagram = diagrams.stream().filter(d -> d.getKey().equals("SignIn")).findFirst().get(); + String expected = readFile(new File("./src/test/java/com/structurizr/export/websequencediagrams/36141-SignIn.wsd")); + assertEquals(expected, diagram.getDefinition()); + } + + @Test + public void test_dynamicViewThatDoeNotOverrideRelationshipDescriptions() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem a = workspace.getModel().addSoftwareSystem("A"); + SoftwareSystem b = workspace.getModel().addSoftwareSystem("B"); + a.uses(b, "Uses"); + + DynamicView view = workspace.getViews().createDynamicView("key", "Description"); + view.add(a, b); + + WebSequenceDiagramsExporter exporter = new WebSequenceDiagramsExporter(); + + Collection<Diagram> diagrams = exporter.export(workspace); + Diagram diagram = diagrams.iterator().next(); + assertEquals("title Dynamic - key\n" + + "\n" + + "participant <<Software System>>\\nA as A\n" + + "participant <<Software System>>\\nB as B\n" + + "\n" + + "A->B: Uses", diagram.getDefinition()); + } + +} \ No newline at end of file diff --git a/structurizr-export/src/test/resources/groups.json b/structurizr-export/src/test/resources/groups.json new file mode 100644 index 000000000..62c3303b8 --- /dev/null +++ b/structurizr-export/src/test/resources/groups.json @@ -0,0 +1,203 @@ +{ + "id" : 0, + "name" : "Name", + "description" : "Description", + "properties" : { + "structurizr.dsl" : "d29ya3NwYWNlIHsKCiAgICBtb2RlbCB7CiAgICAgICAgYSA9IHNvZnR3YXJlU3lzdGVtICJBIgogICAgICAgIAogICAgICAgIGdyb3VwICJHcm91cCAxIiB7CiAgICAgICAgICAgIGIgPSBzb2Z0d2FyZVN5c3RlbSAiQiIKICAgICAgICB9CgogICAgICAgIGVudGVycHJpc2UgIkVudGVycHJpc2UiIHsKICAgICAgICAgICAgYyA9IHNvZnR3YXJlU3lzdGVtICJDIgoKICAgICAgICAgICAgZ3JvdXAgIkdyb3VwIDIiIHsKICAgICAgICAgICAgICAgIGQgPSBzb2Z0d2FyZVN5c3RlbSAiRCIgewogICAgICAgICAgICAgICAgICAgIGUgPSBjb250YWluZXIgIkUiCiAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgZ3JvdXAgIkdyb3VwIDMiIHsKICAgICAgICAgICAgICAgICAgICAgICAgZiA9IGNvbnRhaW5lciAiRiIgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgZyA9IGNvbXBvbmVudCAiRyIKICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgIkdyb3VwIDQiIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoID0gY29tcG9uZW50ICJIIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIAogICAgICAgIGEgLT4gYgogICAgICAgIGIgLT4gYwogICAgICAgIGMgLT4gZQogICAgICAgIGMgLT4gZwogICAgICAgIGMgLT4gaAoKICAgIH0KICAgIAogICAgdmlld3MgewogICAgICAgIHN5c3RlbWxhbmRzY2FwZSAiU3lzdGVtTGFuZHNjYXBlIiB7CiAgICAgICAgICAgIGluY2x1ZGUgKgogICAgICAgICAgICBhdXRvbGF5b3V0CiAgICAgICAgfQogICAgICAgIAogICAgICAgIGNvbnRhaW5lciBkICJDb250YWluZXJzIiB7CiAgICAgICAgICAgIGluY2x1ZGUgKgogICAgICAgICAgICBhdXRvbGF5b3V0CiAgICAgICAgfQoKICAgICAgICBjb21wb25lbnQgZiAiQ29tcG9uZW50cyIgewogICAgICAgICAgICBpbmNsdWRlICoKICAgICAgICAgICAgYXV0b2xheW91dAogICAgICAgIH0KICAgIH0KCn0K" + }, + "configuration" : { }, + "model" : { + "enterprise" : { + "name" : "Enterprise" + }, + "softwareSystems" : [ { + "id" : "1", + "tags" : "Element,Software System", + "name" : "A", + "relationships" : [ { + "id" : "9", + "tags" : "Relationship", + "sourceId" : "1", + "destinationId" : "2" + } ], + "location" : "External" + }, { + "id" : "2", + "tags" : "Element,Software System", + "name" : "B", + "relationships" : [ { + "id" : "10", + "tags" : "Relationship", + "sourceId" : "2", + "destinationId" : "3" + } ], + "group" : "Group 1", + "location" : "External" + }, { + "id" : "3", + "tags" : "Element,Software System", + "name" : "C", + "relationships" : [ { + "id" : "11", + "tags" : "Relationship", + "sourceId" : "3", + "destinationId" : "5" + }, { + "id" : "12", + "tags" : "Relationship", + "sourceId" : "3", + "destinationId" : "4" + }, { + "id" : "13", + "tags" : "Relationship", + "sourceId" : "3", + "destinationId" : "7" + }, { + "id" : "14", + "tags" : "Relationship", + "sourceId" : "3", + "destinationId" : "6" + }, { + "id" : "15", + "tags" : "Relationship", + "sourceId" : "3", + "destinationId" : "8" + } ], + "location" : "Internal" + }, { + "id" : "4", + "tags" : "Element,Software System", + "name" : "D", + "group" : "Group 2", + "location" : "Internal", + "containers" : [ { + "id" : "5", + "tags" : "Element,Container", + "name" : "E" + }, { + "id" : "6", + "tags" : "Element,Container", + "name" : "F", + "group" : "Group 3", + "components" : [ { + "id" : "8", + "tags" : "Element,Component", + "name" : "H", + "group" : "Group 4", + "size" : 0 + }, { + "id" : "7", + "tags" : "Element,Component", + "name" : "G", + "size" : 0 + } ] + } ] + } ] + }, + "documentation" : { }, + "views" : { + "systemLandscapeViews" : [ { + "key" : "SystemLandscape", + "automaticLayout" : { + "implementation" : "Graphviz", + "rankDirection" : "TopBottom", + "rankSeparation" : 300, + "nodeSeparation" : 300, + "edgeSeparation" : 0, + "vertices" : false + }, + "enterpriseBoundaryVisible" : true, + "elements" : [ { + "id" : "1", + "x" : 0, + "y" : 0 + }, { + "id" : "2", + "x" : 0, + "y" : 0 + }, { + "id" : "3", + "x" : 0, + "y" : 0 + }, { + "id" : "4", + "x" : 0, + "y" : 0 + } ], + "relationships" : [ { + "id" : "12" + }, { + "id" : "9" + }, { + "id" : "10" + } ] + } ], + "containerViews" : [ { + "softwareSystemId" : "4", + "key" : "Containers", + "automaticLayout" : { + "implementation" : "Graphviz", + "rankDirection" : "TopBottom", + "rankSeparation" : 300, + "nodeSeparation" : 300, + "edgeSeparation" : 0, + "vertices" : false + }, + "externalSoftwareSystemBoundariesVisible" : true, + "elements" : [ { + "id" : "3", + "x" : 0, + "y" : 0 + }, { + "id" : "5", + "x" : 0, + "y" : 0 + }, { + "id" : "6", + "x" : 0, + "y" : 0 + } ], + "relationships" : [ { + "id" : "14" + }, { + "id" : "11" + } ] + } ], + "componentViews" : [ { + "key" : "Components", + "automaticLayout" : { + "implementation" : "Graphviz", + "rankDirection" : "TopBottom", + "rankSeparation" : 300, + "nodeSeparation" : 300, + "edgeSeparation" : 0, + "vertices" : false + }, + "containerId" : "6", + "externalContainerBoundariesVisible" : true, + "elements" : [ { + "id" : "3", + "x" : 0, + "y" : 0 + }, { + "id" : "7", + "x" : 0, + "y" : 0 + }, { + "id" : "8", + "x" : 0, + "y" : 0 + } ], + "relationships" : [ { + "id" : "15" + }, { + "id" : "13" + } ] + } ], + "configuration" : { + "branding" : { }, + "styles" : { }, + "terminology" : { } + } + } +} \ No newline at end of file diff --git a/structurizr-export/src/test/resources/structurizr-36141-workspace.json b/structurizr-export/src/test/resources/structurizr-36141-workspace.json new file mode 100644 index 000000000..0a52ae906 --- /dev/null +++ b/structurizr-export/src/test/resources/structurizr-36141-workspace.json @@ -0,0 +1,1999 @@ +{ + "id": 36141, + "name": "Big Bank plc", + "description": "This is an example workspace to illustrate the key features of Structurizr, based around a fictional online banking system.", + "model": { + "enterprise": { + "name": "Big Bank plc" + }, + "people": [ + { + "id": "15", + "tags": "Element,Person,Bank Staff", + "name": "Back Office Staff", + "description": "Administration and support staff within the bank.", + "relationships": [ + { + "id": "16", + "tags": "Relationship", + "sourceId": "15", + "destinationId": "4", + "description": "Uses" + } + ], + "location": "Internal" + }, + { + "id": "12", + "tags": "Element,Person,Bank Staff", + "name": "Customer Service Staff", + "description": "Customer service staff within the bank.", + "relationships": [ + { + "id": "13", + "tags": "Relationship", + "sourceId": "12", + "destinationId": "4", + "description": "Uses" + } + ], + "location": "Internal" + }, + { + "id": "1", + "tags": "Element,Person", + "name": "Personal Banking Customer", + "description": "A customer of the bank, with personal bank accounts.", + "relationships": [ + { + "id": "3", + "tags": "Relationship", + "sourceId": "1", + "destinationId": "2", + "description": "Views account balances, and makes payments using" + }, + { + "id": "23", + "tags": "Relationship", + "sourceId": "1", + "destinationId": "17", + "description": "Views account balances, and makes payments using" + }, + { + "id": "22", + "tags": "Relationship", + "sourceId": "1", + "destinationId": "19", + "description": "Visits bigbank.com/ib using", + "technology": "HTTPS" + }, + { + "id": "24", + "tags": "Relationship", + "sourceId": "1", + "destinationId": "18", + "description": "Views account balances, and makes payments using" + }, + { + "id": "11", + "tags": "Relationship", + "sourceId": "1", + "destinationId": "9", + "description": "Withdraws cash using" + }, + { + "id": "14", + "tags": "Relationship,Synchronous", + "sourceId": "1", + "destinationId": "12", + "description": "Asks questions to", + "technology": "Telephone", + "interactionStyle": "Synchronous" + } + ], + "location": "External" + } + ], + "softwareSystems": [ + { + "id": "9", + "tags": "Element,Software System,Existing System", + "name": "ATM", + "description": "Allows customers to withdraw cash.", + "relationships": [ + { + "id": "10", + "tags": "Relationship", + "sourceId": "9", + "destinationId": "4", + "description": "Uses" + } + ], + "location": "Internal" + }, + { + "id": "6", + "tags": "Element,Software System,Existing System", + "name": "E-mail System", + "description": "The internal Microsoft Exchange e-mail system.", + "relationships": [ + { + "id": "8", + "tags": "Relationship", + "sourceId": "6", + "destinationId": "1", + "description": "Sends e-mails to" + } + ], + "location": "Internal" + }, + { + "id": "2", + "tags": "Element,Software System", + "name": "Internet Banking System", + "description": "Allows customers to view information about their bank accounts, and make payments.", + "relationships": [ + { + "id": "5", + "tags": "Relationship", + "sourceId": "2", + "destinationId": "4", + "description": "Gets account information from, and makes payments using" + }, + { + "id": "7", + "tags": "Relationship", + "sourceId": "2", + "destinationId": "6", + "description": "Sends e-mail using" + } + ], + "location": "Internal", + "containers": [ + { + "id": "20", + "tags": "Element,Container", + "name": "API Application", + "description": "Provides Internet banking functionality via a JSON/HTTPS API.", + "relationships": [ + { + "id": "26", + "tags": "Relationship", + "sourceId": "20", + "destinationId": "21", + "description": "Reads from and writes to", + "technology": "JDBC" + }, + { + "id": "27", + "tags": "Relationship", + "sourceId": "20", + "destinationId": "4", + "description": "Makes API calls to", + "technology": "XML/HTTPS" + }, + { + "id": "28", + "tags": "Relationship", + "sourceId": "20", + "destinationId": "6", + "description": "Sends e-mail using", + "technology": "SMTP" + } + ], + "technology": "Java and Spring MVC", + "components": [ + { + "id": "30", + "tags": "Element,Component", + "name": "Accounts Summary Controller", + "description": "Provides customers with a summary of their bank accounts.", + "relationships": [ + { + "id": "44", + "tags": "Relationship", + "sourceId": "30", + "destinationId": "33", + "description": "Uses" + } + ], + "technology": "Spring MVC Rest Controller", + "size": 0 + }, + { + "id": "34", + "tags": "Element,Component", + "name": "E-mail Component", + "description": "Sends e-mails to users.", + "relationships": [ + { + "id": "49", + "tags": "Relationship", + "sourceId": "34", + "destinationId": "6", + "description": "Sends e-mail using" + } + ], + "technology": "Spring Bean", + "size": 0 + }, + { + "id": "33", + "tags": "Element,Component", + "name": "Mainframe Banking System Facade", + "description": "A facade onto the mainframe banking system.", + "relationships": [ + { + "id": "48", + "tags": "Relationship", + "sourceId": "33", + "destinationId": "4", + "description": "Uses", + "technology": "XML/HTTPS" + } + ], + "technology": "Spring Bean", + "size": 0 + }, + { + "id": "31", + "tags": "Element,Component", + "name": "Reset Password Controller", + "description": "Allows users to reset their passwords with a single use URL.", + "relationships": [ + { + "id": "45", + "tags": "Relationship", + "sourceId": "31", + "destinationId": "32", + "description": "Uses" + }, + { + "id": "46", + "tags": "Relationship", + "sourceId": "31", + "destinationId": "34", + "description": "Uses" + } + ], + "technology": "Spring MVC Rest Controller", + "size": 0 + }, + { + "id": "32", + "tags": "Element,Component", + "name": "Security Component", + "description": "Provides functionality related to signing in, changing passwords, etc.", + "relationships": [ + { + "id": "47", + "tags": "Relationship", + "sourceId": "32", + "destinationId": "21", + "description": "Reads from and writes to", + "technology": "JDBC" + } + ], + "technology": "Spring Bean", + "size": 0 + }, + { + "id": "29", + "tags": "Element,Component", + "name": "Sign In Controller", + "description": "Allows users to sign in to the Internet Banking System.", + "relationships": [ + { + "id": "43", + "tags": "Relationship", + "sourceId": "29", + "destinationId": "32", + "description": "Uses" + } + ], + "technology": "Spring MVC Rest Controller", + "size": 0 + } + ] + }, + { + "id": "21", + "tags": "Element,Container,Database", + "name": "Database", + "description": "Stores user registration information, hashed authentication credentials, access logs, etc.", + "technology": "Oracle Database Schema" + }, + { + "id": "18", + "tags": "Element,Container,Mobile App", + "name": "Mobile App", + "description": "Provides a limited subset of the Internet banking functionality to customers via their mobile device.", + "relationships": [ + { + "id": "39", + "tags": "Relationship", + "sourceId": "18", + "destinationId": "29", + "description": "Makes API calls to", + "technology": "JSON/HTTPS" + }, + { + "id": "41", + "tags": "Relationship", + "sourceId": "18", + "destinationId": "31", + "description": "Makes API calls to", + "technology": "JSON/HTTPS" + }, + { + "id": "42", + "tags": "Relationship", + "sourceId": "18", + "destinationId": "30", + "description": "Makes API calls to", + "technology": "JSON/HTTPS" + }, + { + "id": "40", + "tags": "Relationship", + "sourceId": "18", + "destinationId": "20", + "description": "Makes API calls to", + "technology": "JSON/HTTPS" + } + ], + "technology": "Xamarin" + }, + { + "id": "17", + "tags": "Element,Container,Web Browser", + "name": "Single-Page Application", + "description": "Provides all of the Internet banking functionality to customers via their web browser.", + "relationships": [ + { + "id": "38", + "tags": "Relationship", + "sourceId": "17", + "destinationId": "30", + "description": "Makes API calls to", + "technology": "JSON/HTTPS" + }, + { + "id": "36", + "tags": "Relationship", + "sourceId": "17", + "destinationId": "20", + "description": "Makes API calls to", + "technology": "JSON/HTTPS" + }, + { + "id": "37", + "tags": "Relationship", + "sourceId": "17", + "destinationId": "31", + "description": "Makes API calls to", + "technology": "JSON/HTTPS" + }, + { + "id": "35", + "tags": "Relationship", + "sourceId": "17", + "destinationId": "29", + "description": "Makes API calls to", + "technology": "JSON/HTTPS" + } + ], + "technology": "JavaScript and Angular" + }, + { + "id": "19", + "tags": "Element,Container", + "name": "Web Application", + "description": "Delivers the static content and the Internet banking single page application.", + "relationships": [ + { + "id": "25", + "tags": "Relationship", + "sourceId": "19", + "destinationId": "17", + "description": "Delivers to the customer's web browser" + } + ], + "technology": "Java and Spring MVC" + } + ] + }, + { + "id": "4", + "tags": "Element,Software System,Existing System", + "name": "Mainframe Banking System", + "description": "Stores all of the core banking information about customers, accounts, transactions, etc.", + "location": "Internal" + } + ], + "deploymentNodes": [ + { + "id": "55", + "tags": "Element,Deployment Node", + "name": "Big Bank plc", + "environment": "Development", + "technology": "Big Bank plc data center", + "instances": 1, + "children": [ + { + "id": "56", + "tags": "Element,Deployment Node", + "name": "bigbank-dev001", + "environment": "Development", + "instances": 1, + "softwareSystemInstances": [ + { + "id": "57", + "tags": "Software System Instance", + "environment": "Development", + "instanceId": 1, + "softwareSystemId": "4", + "properties": {} + } + ], + "children": [], + "containerInstances": [], + "infrastructureNodes": [] + } + ], + "softwareSystemInstances": [], + "containerInstances": [], + "infrastructureNodes": [] + }, + { + "id": "50", + "tags": "Element,Deployment Node", + "name": "Developer Laptop", + "description": "A developer laptop.", + "environment": "Development", + "technology": "Microsoft Windows 10 or Apple macOS", + "instances": 1, + "children": [ + { + "id": "59", + "tags": "Element,Deployment Node", + "name": "Docker Container - Database Server", + "description": "A Docker container.", + "environment": "Development", + "technology": "Docker", + "instances": 1, + "children": [ + { + "id": "60", + "tags": "Element,Deployment Node", + "name": "Database Server", + "description": "A development database.", + "environment": "Development", + "technology": "Oracle 12c", + "instances": 1, + "containerInstances": [ + { + "id": "61", + "tags": "Container Instance", + "environment": "Development", + "instanceId": 1, + "containerId": "21", + "properties": {} + } + ], + "children": [], + "softwareSystemInstances": [], + "infrastructureNodes": [] + } + ], + "softwareSystemInstances": [], + "containerInstances": [], + "infrastructureNodes": [] + }, + { + "id": "51", + "tags": "Element,Deployment Node", + "name": "Docker Container - Web Server", + "description": "A Docker container.", + "environment": "Development", + "technology": "Docker", + "instances": 1, + "children": [ + { + "id": "52", + "tags": "Element,Deployment Node", + "properties": { + "Java Version": "8", + "Xms": "1024M", + "Xmx": "512M" + }, + "name": "Apache Tomcat", + "description": "An open source Java EE web server.", + "environment": "Development", + "technology": "Apache Tomcat 8.x", + "instances": 1, + "containerInstances": [ + { + "id": "54", + "tags": "Container Instance", + "relationships": [ + { + "id": "62", + "sourceId": "54", + "destinationId": "61", + "description": "Reads from and writes to", + "technology": "JDBC", + "linkedRelationshipId": "26" + }, + { + "id": "58", + "sourceId": "54", + "destinationId": "57", + "description": "Makes API calls to", + "technology": "XML/HTTPS", + "linkedRelationshipId": "27" + } + ], + "environment": "Development", + "instanceId": 1, + "containerId": "20", + "properties": {} + }, + { + "id": "53", + "tags": "Container Instance", + "relationships": [ + { + "id": "66", + "sourceId": "53", + "destinationId": "64", + "description": "Delivers to the customer's web browser", + "linkedRelationshipId": "25" + } + ], + "environment": "Development", + "instanceId": 1, + "containerId": "19", + "properties": {} + } + ], + "children": [], + "softwareSystemInstances": [], + "infrastructureNodes": [] + } + ], + "softwareSystemInstances": [], + "containerInstances": [], + "infrastructureNodes": [] + }, + { + "id": "63", + "tags": "Element,Deployment Node", + "name": "Web Browser", + "environment": "Development", + "technology": "Chrome, Firefox, Safari, or Edge", + "instances": 1, + "containerInstances": [ + { + "id": "64", + "tags": "Container Instance", + "relationships": [ + { + "id": "65", + "sourceId": "64", + "destinationId": "54", + "description": "Makes API calls to", + "technology": "JSON/HTTPS", + "linkedRelationshipId": "36" + } + ], + "environment": "Development", + "instanceId": 1, + "containerId": "17", + "properties": {} + } + ], + "children": [], + "softwareSystemInstances": [], + "infrastructureNodes": [] + } + ], + "softwareSystemInstances": [], + "containerInstances": [], + "infrastructureNodes": [] + }, + { + "id": "72", + "tags": "Element,Deployment Node", + "name": "Big Bank plc", + "environment": "Live", + "technology": "Big Bank plc data center", + "instances": 1, + "children": [ + { + "id": "79", + "tags": "Element,Deployment Node", + "properties": { + "Location": "London and Reading" + }, + "name": "bigbank-api***", + "description": "A web server residing in the web server farm, accessed via F5 BIG-IP LTMs.", + "environment": "Live", + "technology": "Ubuntu 16.04 LTS", + "instances": 8, + "children": [ + { + "id": "80", + "tags": "Element,Deployment Node", + "properties": { + "Java Version": "8", + "Xms": "1024M", + "Xmx": "512M" + }, + "name": "Apache Tomcat", + "description": "An open source Java EE web server.", + "environment": "Live", + "technology": "Apache Tomcat 8.x", + "instances": 1, + "containerInstances": [ + { + "id": "81", + "tags": "Container Instance", + "relationships": [ + { + "id": "88", + "sourceId": "81", + "destinationId": "87", + "description": "Reads from and writes to", + "technology": "JDBC", + "linkedRelationshipId": "26" + }, + { + "id": "84", + "sourceId": "81", + "destinationId": "74", + "description": "Makes API calls to", + "technology": "XML/HTTPS", + "linkedRelationshipId": "27" + }, + { + "id": "92", + "tags": "Failover", + "sourceId": "81", + "destinationId": "91", + "description": "Reads from and writes to", + "technology": "JDBC", + "linkedRelationshipId": "26" + } + ], + "environment": "Live", + "instanceId": 1, + "containerId": "20", + "properties": {} + } + ], + "children": [], + "softwareSystemInstances": [], + "infrastructureNodes": [] + } + ], + "softwareSystemInstances": [], + "containerInstances": [], + "infrastructureNodes": [] + }, + { + "id": "85", + "tags": "Element,Deployment Node", + "properties": { + "Location": "London" + }, + "name": "bigbank-db01", + "description": "The primary database server.", + "environment": "Live", + "technology": "Ubuntu 16.04 LTS", + "instances": 1, + "children": [ + { + "id": "86", + "tags": "Element,Deployment Node", + "name": "Oracle - Primary", + "description": "The primary, live database server.", + "relationships": [ + { + "id": "93", + "tags": "Relationship,Synchronous", + "sourceId": "86", + "destinationId": "90", + "description": "Replicates data to", + "interactionStyle": "Synchronous" + } + ], + "environment": "Live", + "technology": "Oracle 12c", + "instances": 1, + "containerInstances": [ + { + "id": "87", + "tags": "Container Instance", + "environment": "Live", + "instanceId": 1, + "containerId": "21", + "properties": {} + } + ], + "children": [], + "softwareSystemInstances": [], + "infrastructureNodes": [] + } + ], + "softwareSystemInstances": [], + "containerInstances": [], + "infrastructureNodes": [] + }, + { + "id": "89", + "tags": "Element,Deployment Node,Failover", + "properties": { + "Location": "Reading" + }, + "name": "bigbank-db02", + "description": "The secondary database server.", + "environment": "Live", + "technology": "Ubuntu 16.04 LTS", + "instances": 1, + "children": [ + { + "id": "90", + "tags": "Element,Deployment Node,Failover", + "name": "Oracle - Secondary", + "description": "A secondary, standby database server, used for failover purposes only.", + "environment": "Live", + "technology": "Oracle 12c", + "instances": 1, + "containerInstances": [ + { + "id": "91", + "tags": "Container Instance,Failover", + "environment": "Live", + "instanceId": 2, + "containerId": "21", + "properties": {} + } + ], + "children": [], + "softwareSystemInstances": [], + "infrastructureNodes": [] + } + ], + "softwareSystemInstances": [], + "containerInstances": [], + "infrastructureNodes": [] + }, + { + "id": "73", + "tags": "Element,Deployment Node", + "name": "bigbank-prod001", + "environment": "Live", + "instances": 1, + "softwareSystemInstances": [ + { + "id": "74", + "tags": "Software System Instance", + "environment": "Live", + "instanceId": 1, + "softwareSystemId": "4", + "properties": {} + } + ], + "children": [], + "containerInstances": [], + "infrastructureNodes": [] + }, + { + "id": "75", + "tags": "Element,Deployment Node", + "properties": { + "Location": "London and Reading" + }, + "name": "bigbank-web***", + "description": "A web server residing in the web server farm, accessed via F5 BIG-IP LTMs.", + "environment": "Live", + "technology": "Ubuntu 16.04 LTS", + "instances": 4, + "children": [ + { + "id": "76", + "tags": "Element,Deployment Node", + "properties": { + "Java Version": "8", + "Xms": "1024M", + "Xmx": "512M" + }, + "name": "Apache Tomcat", + "description": "An open source Java EE web server.", + "environment": "Live", + "technology": "Apache Tomcat 8.x", + "instances": 1, + "containerInstances": [ + { + "id": "77", + "tags": "Container Instance", + "relationships": [ + { + "id": "78", + "sourceId": "77", + "destinationId": "71", + "description": "Delivers to the customer's web browser", + "linkedRelationshipId": "25" + } + ], + "environment": "Live", + "instanceId": 1, + "containerId": "19", + "properties": {} + } + ], + "children": [], + "softwareSystemInstances": [], + "infrastructureNodes": [] + } + ], + "softwareSystemInstances": [], + "containerInstances": [], + "infrastructureNodes": [] + } + ], + "softwareSystemInstances": [], + "containerInstances": [], + "infrastructureNodes": [] + }, + { + "id": "69", + "tags": "Element,Deployment Node", + "name": "Customer's computer", + "environment": "Live", + "technology": "Microsoft Windows or Apple macOS", + "instances": 1, + "children": [ + { + "id": "70", + "tags": "Element,Deployment Node", + "name": "Web Browser", + "environment": "Live", + "technology": "Chrome, Firefox, Safari, or Edge", + "instances": 1, + "containerInstances": [ + { + "id": "71", + "tags": "Container Instance", + "relationships": [ + { + "id": "83", + "sourceId": "71", + "destinationId": "81", + "description": "Makes API calls to", + "technology": "JSON/HTTPS", + "linkedRelationshipId": "36" + } + ], + "environment": "Live", + "instanceId": 1, + "containerId": "17", + "properties": {} + } + ], + "children": [], + "softwareSystemInstances": [], + "infrastructureNodes": [] + } + ], + "softwareSystemInstances": [], + "containerInstances": [], + "infrastructureNodes": [] + }, + { + "id": "67", + "tags": "Element,Deployment Node", + "name": "Customer's mobile device", + "environment": "Live", + "technology": "Apple iOS or Android", + "instances": 1, + "containerInstances": [ + { + "id": "68", + "tags": "Container Instance", + "relationships": [ + { + "id": "82", + "sourceId": "68", + "destinationId": "81", + "description": "Makes API calls to", + "technology": "JSON/HTTPS", + "linkedRelationshipId": "40" + } + ], + "environment": "Live", + "instanceId": 1, + "containerId": "18", + "properties": {} + } + ], + "children": [], + "softwareSystemInstances": [], + "infrastructureNodes": [] + } + ] + }, + "documentation": { + "sections": [ + { + "elementId": "2", + "title": "Context", + "order": 1, + "format": "Markdown", + "content": "Here is some context about the Internet Banking System...\n![](embed:SystemLandscape)\n![](embed:SystemContext)\n### Internet Banking System\n...\n### Mainframe Banking System\n...\n" + }, + { + "elementId": "19", + "title": "Components", + "order": 3, + "format": "Markdown", + "content": "Here is some information about the API Application...\n![](embed:Components)\n### Sign in process\nHere is some information about the Sign In Controller, including how the sign in process works...\n![](embed:SignIn)" + }, + { + "elementId": "2", + "title": "Development Environment", + "order": 4, + "format": "AsciiDoc", + "content": "Here is some information about how to set up a development environment for the Internet Banking System...\nimage::embed:DevelopmentDeployment[]" + }, + { + "elementId": "2", + "title": "Containers", + "order": 2, + "format": "Markdown", + "content": "Here is some information about the containers within the Internet Banking System...\n![](embed:Containers)\n### Web Application\n...\n### Database\n...\n" + }, + { + "elementId": "2", + "title": "Deployment", + "order": 5, + "format": "AsciiDoc", + "content": "Here is some information about the live deployment environment for the Internet Banking System...\nimage::embed:LiveDeployment[]" + } + ], + "template": { + "name": "Software Guidebook", + "author": "Simon Brown", + "url": "https://leanpub.com/visualising-software-architecture" + }, + "decisions": [], + "images": [] + }, + "views": { + "systemLandscapeViews": [ + { + "description": "The system landscape diagram for Big Bank plc.", + "key": "SystemLandscape", + "paperSize": "A5_Landscape", + "animations": [ + { + "order": 1, + "elements": [ + "1", + "2", + "4", + "6" + ], + "relationships": [ + "3", + "5", + "7", + "8" + ] + }, + { + "order": 2, + "elements": [ + "9" + ], + "relationships": [ + "11", + "10" + ] + }, + { + "order": 3, + "elements": [ + "12", + "15" + ], + "relationships": [ + "13", + "14", + "16" + ] + } + ], + "enterpriseBoundaryVisible": true, + "elements": [ + { + "id": "1", + "x": 87, + "y": 643 + }, + { + "id": "12", + "x": 1947, + "y": 36 + }, + { + "id": "2", + "x": 1012, + "y": 813 + }, + { + "id": "4", + "x": 1922, + "y": 693 + }, + { + "id": "15", + "x": 1947, + "y": 1241 + }, + { + "id": "6", + "x": 1012, + "y": 1326 + }, + { + "id": "9", + "x": 1012, + "y": 301 + } + ], + "relationships": [ + { + "id": "16" + }, + { + "id": "3" + }, + { + "id": "14", + "vertices": [ + { + "x": 285, + "y": 240 + } + ] + }, + { + "id": "5" + }, + { + "id": "13" + }, + { + "id": "11" + }, + { + "id": "7" + }, + { + "id": "8" + }, + { + "id": "10" + } + ] + } + ], + "systemContextViews": [ + { + "softwareSystemId": "2", + "description": "The system context diagram for the Internet Banking System.", + "key": "SystemContext", + "paperSize": "A5_Landscape", + "animations": [ + { + "order": 1, + "elements": [ + "2" + ], + "relationships": [] + }, + { + "order": 2, + "elements": [ + "1" + ], + "relationships": [ + "3" + ] + }, + { + "order": 3, + "elements": [ + "4" + ], + "relationships": [ + "5" + ] + }, + { + "order": 4, + "elements": [ + "6" + ], + "relationships": [ + "7", + "8" + ] + } + ], + "enterpriseBoundaryVisible": false, + "elements": [ + { + "id": "1", + "x": 632, + "y": 69 + }, + { + "id": "2", + "x": 607, + "y": 714 + }, + { + "id": "4", + "x": 607, + "y": 1259 + }, + { + "id": "6", + "x": 1422, + "y": 714 + } + ], + "relationships": [ + { + "id": "3" + }, + { + "id": "5" + }, + { + "id": "7" + }, + { + "id": "8" + } + ] + } + ], + "containerViews": [ + { + "softwareSystemId": "2", + "description": "The container diagram for the Internet Banking System.", + "key": "Containers", + "paperSize": "A5_Landscape", + "animations": [ + { + "order": 1, + "elements": [ + "1", + "4", + "6" + ], + "relationships": [ + "8" + ] + }, + { + "order": 2, + "elements": [ + "19" + ], + "relationships": [ + "22" + ] + }, + { + "order": 3, + "elements": [ + "17" + ], + "relationships": [ + "23", + "25" + ] + }, + { + "order": 4, + "elements": [ + "18" + ], + "relationships": [ + "24" + ] + }, + { + "order": 5, + "elements": [ + "20" + ], + "relationships": [ + "36", + "27", + "28", + "40" + ] + }, + { + "order": 6, + "elements": [ + "21" + ], + "relationships": [ + "26" + ] + } + ], + "externalSoftwareSystemBoundariesVisible": false, + "elements": [ + { + "id": "1", + "x": 1056, + "y": 24 + }, + { + "id": "4", + "x": 2012, + "y": 1214 + }, + { + "id": "17", + "x": 780, + "y": 664 + }, + { + "id": "6", + "x": 2012, + "y": 664 + }, + { + "id": "18", + "x": 1283, + "y": 664 + }, + { + "id": "19", + "x": 37, + "y": 664 + }, + { + "id": "20", + "x": 1031, + "y": 1214 + }, + { + "id": "21", + "x": 37, + "y": 1214 + } + ], + "relationships": [ + { + "id": "28" + }, + { + "id": "27" + }, + { + "id": "26" + }, + { + "id": "25" + }, + { + "id": "36" + }, + { + "id": "40" + }, + { + "id": "24" + }, + { + "id": "23" + }, + { + "id": "22" + }, + { + "id": "8" + } + ] + } + ], + "componentViews": [ + { + "description": "The component diagram for the API Application.", + "key": "Components", + "paperSize": "A5_Landscape", + "animations": [ + { + "order": 1, + "elements": [ + "4", + "17", + "6", + "18", + "21" + ], + "relationships": [] + }, + { + "order": 2, + "elements": [ + "29", + "32" + ], + "relationships": [ + "35", + "47", + "39", + "43" + ] + }, + { + "order": 3, + "elements": [ + "33", + "30" + ], + "relationships": [ + "44", + "48", + "38", + "42" + ] + }, + { + "order": 4, + "elements": [ + "34", + "31" + ], + "relationships": [ + "45", + "46", + "37", + "49", + "41" + ] + } + ], + "containerId": "20", + "externalContainerBoundariesVisible": false, + "elements": [ + { + "id": "33", + "x": 1925, + "y": 817 + }, + { + "id": "34", + "x": 1015, + "y": 817 + }, + { + "id": "4", + "x": 1925, + "y": 1307 + }, + { + "id": "17", + "x": 560, + "y": 10 + }, + { + "id": "6", + "x": 1015, + "y": 1307 + }, + { + "id": "18", + "x": 1470, + "y": 11 + }, + { + "id": "29", + "x": 105, + "y": 436 + }, + { + "id": "30", + "x": 1925, + "y": 436 + }, + { + "id": "31", + "x": 1015, + "y": 436 + }, + { + "id": "21", + "x": 105, + "y": 1307 + }, + { + "id": "32", + "x": 105, + "y": 817 + } + ], + "relationships": [ + { + "id": "41", + "position": 40 + }, + { + "id": "42", + "position": 40 + }, + { + "id": "43", + "position": 55 + }, + { + "id": "37", + "position": 45 + }, + { + "id": "35", + "position": 35 + }, + { + "id": "44", + "position": 50 + }, + { + "id": "45" + }, + { + "id": "46" + }, + { + "id": "47", + "position": 60 + }, + { + "id": "48" + }, + { + "id": "38", + "position": 85 + }, + { + "id": "49" + }, + { + "id": "39", + "position": 85 + } + ] + } + ], + "dynamicViews": [ + { + "description": "Summarises how the sign in feature works in the single-page application.", + "key": "SignIn", + "paperSize": "A5_Landscape", + "elementId": "20", + "relationships": [ + { + "id": "35", + "description": "Submits credentials to", + "order": "1", + "response": false, + "vertices": [ + { + "x": 1238, + "y": 236 + } + ], + "routing": "Curved", + "position": 50 + }, + { + "id": "43", + "description": "Validates credentials using", + "order": "2", + "response": false, + "vertices": [ + { + "x": 2065, + "y": 845 + } + ], + "routing": "Curved" + }, + { + "id": "47", + "description": "select * from users where username = ?", + "order": "3", + "response": false, + "vertices": [ + { + "x": 1218, + "y": 1416 + } + ], + "routing": "Curved" + }, + { + "id": "47", + "description": "Returns user data to", + "order": "4", + "response": true, + "vertices": [ + { + "x": 1240, + "y": 1220 + } + ], + "routing": "Curved" + }, + { + "id": "43", + "description": "Returns true if the hashed password matches", + "order": "5", + "response": true, + "vertices": [ + { + "x": 1828, + "y": 841 + } + ], + "routing": "Curved" + }, + { + "id": "35", + "description": "Sends back an authentication token to", + "order": "6", + "response": true, + "vertices": [ + { + "x": 1210, + "y": 450 + } + ], + "routing": "Curved" + } + ], + "elements": [ + { + "id": "17", + "x": 290, + "y": 192 + }, + { + "id": "29", + "x": 1720, + "y": 192 + }, + { + "id": "32", + "x": 1720, + "y": 1182 + }, + { + "id": "21", + "x": 290, + "y": 1182 + } + ] + } + ], + "deploymentViews": [ + { + "softwareSystemId": "2", + "description": "An example live deployment scenario for the Internet Banking System.", + "key": "LiveDeployment", + "paperSize": "A4_Landscape", + "environment": "Live", + "animations": [ + { + "order": 1, + "elements": [ + "69", + "70", + "71" + ] + }, + { + "order": 2, + "elements": [ + "67", + "68" + ] + }, + { + "order": 3, + "elements": [ + "77", + "79", + "80", + "81", + "72", + "75", + "76" + ], + "relationships": [ + "78", + "82", + "83" + ] + }, + { + "order": 4, + "elements": [ + "85", + "86", + "87" + ], + "relationships": [ + "88" + ] + }, + { + "order": 5, + "elements": [ + "89", + "90", + "91" + ], + "relationships": [ + "92", + "93" + ] + }, + { + "order": 6, + "elements": [ + "74" + ], + "relationships": [ + "84" + ] + } + ], + "elements": [ + { + "id": "77", + "x": 1504, + "y": 184 + }, + { + "id": "89", + "x": 0, + "y": 0 + }, + { + "id": "67", + "x": 0, + "y": 0 + }, + { + "id": "79", + "x": 0, + "y": 0 + }, + { + "id": "68", + "x": 424, + "y": 1071 + }, + { + "id": "69", + "x": 0, + "y": 0 + }, + { + "id": "90", + "x": 0, + "y": 0 + }, + { + "id": "91", + "x": 2584, + "y": 184 + }, + { + "id": "80", + "x": 0, + "y": 0 + }, + { + "id": "81", + "x": 1504, + "y": 1071 + }, + { + "id": "70", + "x": 0, + "y": 0 + }, + { + "id": "71", + "x": 424, + "y": 184 + }, + { + "id": "72", + "x": 0, + "y": 0 + }, + { + "id": "73", + "x": 0, + "y": 0 + }, + { + "id": "74", + "x": 2584, + "y": 1959 + }, + { + "id": "85", + "x": 0, + "y": 0 + }, + { + "id": "86", + "x": 0, + "y": 0 + }, + { + "id": "75", + "x": 0, + "y": 0 + }, + { + "id": "87", + "x": 2584, + "y": 1071 + }, + { + "id": "76", + "x": 0, + "y": 0 + } + ], + "relationships": [ + { + "id": "93" + }, + { + "id": "82" + }, + { + "id": "83" + }, + { + "id": "92" + }, + { + "id": "84" + }, + { + "id": "78" + }, + { + "id": "88" + } + ] + }, + { + "softwareSystemId": "2", + "description": "An example development deployment scenario for the Internet Banking System.", + "key": "DevelopmentDeployment", + "paperSize": "A5_Landscape", + "environment": "Development", + "animations": [ + { + "order": 1, + "elements": [ + "50", + "63", + "64" + ] + }, + { + "order": 2, + "elements": [ + "51", + "52", + "53", + "54" + ], + "relationships": [ + "66", + "65" + ] + }, + { + "order": 3, + "elements": [ + "59", + "60", + "61" + ], + "relationships": [ + "62" + ] + }, + { + "order": 4, + "elements": [ + "57" + ], + "relationships": [ + "58" + ] + } + ], + "elements": [ + { + "id": "55", + "x": 0, + "y": 0 + }, + { + "id": "56", + "x": 0, + "y": 0 + }, + { + "id": "57", + "x": 1827, + "y": 1236 + }, + { + "id": "59", + "x": 0, + "y": 0 + }, + { + "id": "60", + "x": 0, + "y": 0 + }, + { + "id": "61", + "x": 1827, + "y": 176 + }, + { + "id": "50", + "x": 0, + "y": 0 + }, + { + "id": "51", + "x": 0, + "y": 0 + }, + { + "id": "63", + "x": 0, + "y": 0 + }, + { + "id": "52", + "x": 0, + "y": 0 + }, + { + "id": "64", + "x": 152, + "y": 346 + }, + { + "id": "53", + "x": 989, + "y": 176 + }, + { + "id": "54", + "x": 989, + "y": 516 + } + ], + "relationships": [ + { + "id": "62", + "position": 50 + }, + { + "id": "65" + }, + { + "id": "66" + }, + { + "id": "58" + } + ] + } + ], + "configuration": { + "branding": {}, + "styles": { + "elements": [ + { + "tag": "Software System", + "background": "#1168bd", + "color": "#ffffff" + }, + { + "tag": "Container", + "background": "#438dd5", + "color": "#ffffff" + }, + { + "tag": "Component", + "background": "#85bbf0", + "color": "#000000" + }, + { + "tag": "Person", + "background": "#08427b", + "color": "#ffffff", + "fontSize": 22, + "shape": "Person" + }, + { + "tag": "Existing System", + "background": "#999999", + "color": "#ffffff" + }, + { + "tag": "Bank Staff", + "background": "#999999", + "color": "#ffffff" + }, + { + "tag": "Web Browser", + "shape": "WebBrowser" + }, + { + "tag": "Mobile App", + "shape": "MobileDeviceLandscape" + }, + { + "tag": "Database", + "shape": "Cylinder" + }, + { + "tag": "Failover", + "opacity": 25 + } + ], + "relationships": [ + { + "tag": "Failover", + "position": 70, + "opacity": 25 + } + ] + }, + "terminology": {}, + "lastSavedView": "SignIn", + "themes": [] + }, + "filteredViews": [] + } +} \ No newline at end of file diff --git a/structurizr-export/src/test/resources/structurizr-54915-workspace.json b/structurizr-export/src/test/resources/structurizr-54915-workspace.json new file mode 100644 index 000000000..28e62ac94 --- /dev/null +++ b/structurizr-export/src/test/resources/structurizr-54915-workspace.json @@ -0,0 +1,353 @@ +{ + "id": 54915, + "name": "Amazon Web Services Example", + "description": "An example AWS deployment architecture.", + "model": { + "softwareSystems": [ + { + "id": "1", + "tags": "Element,Software System", + "name": "Spring PetClinic", + "description": "Allows employees to view and manage information regarding the veterinarians, the clients, and their pets.", + "location": "Unspecified", + "containers": [ + { + "id": "3", + "tags": "Element,Container,Database", + "name": "Database", + "description": "Stores information regarding the veterinarians, the clients, and their pets.", + "technology": "Relational database schema" + }, + { + "id": "2", + "tags": "Element,Container,Application", + "name": "Web Application", + "description": "Allows employees to view and manage information regarding the veterinarians, the clients, and their pets.", + "relationships": [ + { + "id": "4", + "tags": "Relationship", + "sourceId": "2", + "destinationId": "3", + "description": "Reads from and writes to", + "technology": "MySQL Protocol/SSL" + } + ], + "technology": "Java and Spring Boot" + } + ], + "documentation": {} + } + ], + "deploymentNodes": [ + { + "id": "5", + "tags": "Element,Deployment Node,Amazon Web Services - Cloud", + "name": "Amazon Web Services", + "environment": "Live", + "instances": 1, + "children": [ + { + "id": "6", + "tags": "Element,Deployment Node,Amazon Web Services - Region", + "name": "US-East-1", + "environment": "Live", + "instances": 1, + "children": [ + { + "id": "12", + "tags": "Element,Deployment Node,Amazon Web Services - RDS", + "name": "Amazon RDS", + "environment": "Live", + "instances": 1, + "children": [ + { + "id": "13", + "tags": "Element,Deployment Node,Amazon Web Services - RDS MySQL instance", + "name": "MySQL", + "environment": "Live", + "instances": 1, + "containerInstances": [ + { + "id": "14", + "tags": "Container Instance", + "environment": "Live", + "deploymentGroups": [ + "Default" + ], + "instanceId": 1, + "containerId": "3" + } + ], + "children": [], + "softwareSystemInstances": [], + "infrastructureNodes": [] + } + ], + "softwareSystemInstances": [], + "containerInstances": [], + "infrastructureNodes": [] + }, + { + "id": "9", + "tags": "Element,Deployment Node,Amazon Web Services - Auto Scaling", + "name": "Autoscaling group", + "environment": "Live", + "instances": 1, + "children": [ + { + "id": "10", + "tags": "Element,Deployment Node,Amazon Web Services - EC2", + "name": "Amazon EC2", + "environment": "Live", + "instances": 1, + "containerInstances": [ + { + "id": "11", + "tags": "Container Instance", + "relationships": [ + { + "id": "15", + "sourceId": "11", + "destinationId": "14", + "description": "Reads from and writes to", + "technology": "MySQL Protocol/SSL", + "linkedRelationshipId": "4" + } + ], + "environment": "Live", + "deploymentGroups": [ + "Default" + ], + "instanceId": 1, + "containerId": "2" + } + ], + "children": [], + "softwareSystemInstances": [], + "infrastructureNodes": [] + } + ], + "softwareSystemInstances": [], + "containerInstances": [], + "infrastructureNodes": [] + } + ], + "infrastructureNodes": [ + { + "id": "8", + "tags": "Element,Infrastructure Node,Amazon Web Services - Elastic Load Balancing", + "name": "Elastic Load Balancer", + "description": "Automatically distributes incoming application traffic.", + "relationships": [ + { + "id": "17", + "tags": "Relationship", + "sourceId": "8", + "destinationId": "11", + "description": "Forwards requests to", + "technology": "HTTPS" + } + ], + "environment": "Live" + }, + { + "id": "7", + "tags": "Element,Infrastructure Node,Amazon Web Services - Route 53", + "name": "Route 53", + "description": "Highly available and scalable cloud DNS service.", + "relationships": [ + { + "id": "16", + "tags": "Relationship", + "sourceId": "7", + "destinationId": "8", + "description": "Forwards requests to", + "technology": "HTTPS" + } + ], + "environment": "Live" + } + ], + "softwareSystemInstances": [], + "containerInstances": [] + } + ], + "softwareSystemInstances": [], + "containerInstances": [], + "infrastructureNodes": [] + } + ], + "customElements": [], + "people": [] + }, + "documentation": { + "sections": [], + "decisions": [], + "images": [] + }, + "views": { + "deploymentViews": [ + { + "softwareSystemId": "1", + "key": "AmazonWebServicesDeployment", + "order": 1, + "paperSize": "A3_Landscape", + "dimensions": { + "width": 3925, + "height": 1816 + }, + "automaticLayout": { + "implementation": "Graphviz", + "rankDirection": "LeftRight", + "rankSeparation": 300, + "nodeSeparation": 300, + "edgeSeparation": 0, + "vertices": false + }, + "environment": "Live", + "animations": [ + { + "order": 1, + "elements": [ + "5", + "6", + "7" + ] + }, + { + "order": 2, + "elements": [ + "8" + ], + "relationships": [ + "16" + ] + }, + { + "order": 3, + "elements": [ + "11", + "9", + "10" + ], + "relationships": [ + "17" + ] + }, + { + "order": 4, + "elements": [ + "12", + "13", + "14" + ], + "relationships": [ + "15" + ] + } + ], + "elements": [ + { + "id": "11", + "x": 1987, + "y": 672 + }, + { + "id": "12", + "x": 175, + "y": 175 + }, + { + "id": "13", + "x": 175, + "y": 175 + }, + { + "id": "14", + "x": 2887, + "y": 672 + }, + { + "id": "5", + "x": 175, + "y": 175 + }, + { + "id": "6", + "x": 175, + "y": 175 + }, + { + "id": "7", + "x": 487, + "y": 672 + }, + { + "id": "8", + "x": 1237, + "y": 672 + }, + { + "id": "9", + "x": 175, + "y": 175 + }, + { + "id": "10", + "x": 175, + "y": 175 + } + ], + "relationships": [ + { + "id": "17" + }, + { + "id": "16" + }, + { + "id": "15" + } + ] + } + ], + "configuration": { + "branding": {}, + "styles": { + "elements": [ + { + "tag": "Element", + "background": "#ffffff", + "shape": "RoundedBox" + }, + { + "tag": "Container", + "background": "#ffffff" + }, + { + "tag": "Application", + "background": "#ffffff" + }, + { + "tag": "Database", + "shape": "Cylinder" + } + ], + "relationships": [] + }, + "themes": [ + "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/theme.json" + ], + "terminology": {}, + "lastSavedView": "AmazonWebServicesDeployment" + }, + "customViews": [], + "systemLandscapeViews": [], + "systemContextViews": [], + "containerViews": [], + "componentViews": [], + "dynamicViews": [], + "filteredViews": [] + } +} \ No newline at end of file diff --git a/structurizr-graphviz/README.md b/structurizr-graphviz/README.md new file mode 100644 index 000000000..bed0d0a76 --- /dev/null +++ b/structurizr-graphviz/README.md @@ -0,0 +1,26 @@ +# structurizr-graphviz + +[![Maven Central](https://img.shields.io/maven-central/v/com.structurizr/structurizr-graphviz.svg?label=Maven%20Central)](https://search.maven.org/artifact/com.structurizr/structurizr-graphviz) + +This library provides automatic facilities for Structurizr views. +It's a wrapper around the [Graphviz tool](http://www.graphviz.org), +which allows you to apply the Graphviz layout algorithm to the views in a Structurizr workspace. + +> You will need Graphviz installed. + +For example: + +```java +Workspace workspace = ... + +GraphvizAutomaticLayout graphviz = new GraphvizAutomaticLayout(); +graphviz.apply(workspace); +``` + +The ```structurizr-graphviz``` library does the following for every view in the workspace: + +1. Export the view to a DOT file. +2. Run Graphviz (via the ```dot``` command), with the output format set to SVG. +3. Parse the generated SVG to extract layout information, and apply this to the Structurizr view (element x,y positions, relationship vertices, and paper size). + +Once the layout has been applied, you can upload your workspace to the Structurizr cloud service/on-premises installation as usual. diff --git a/structurizr-graphviz/build.gradle b/structurizr-graphviz/build.gradle new file mode 100644 index 000000000..c40b607ba --- /dev/null +++ b/structurizr-graphviz/build.gradle @@ -0,0 +1,10 @@ +dependencies { + + api project(':structurizr-export') + + testImplementation project(':structurizr-client') + testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2' + +} + +description = 'Automatic layout facilities for Structurizr views' \ No newline at end of file diff --git a/structurizr-graphviz/src/main/java/com/structurizr/graphviz/Constants.java b/structurizr-graphviz/src/main/java/com/structurizr/graphviz/Constants.java new file mode 100644 index 000000000..37340f9cb --- /dev/null +++ b/structurizr-graphviz/src/main/java/com/structurizr/graphviz/Constants.java @@ -0,0 +1,17 @@ +package com.structurizr.graphviz; + +/** + * Some constants used when applying graphviz. + */ +class Constants { + + // diagrams created by the Structurizr cloud service/on-premises installation/Lite are sized for 300dpi + static final double STRUCTURIZR_DPI = 300.0; + + // graphviz uses 72dpi by default + private static final double GRAPHVIZ_DPI = 72.0; + + // this is needed to convert coordinates provided by graphviz, to those used by Structurizr + static final double DPI_RATIO = STRUCTURIZR_DPI / GRAPHVIZ_DPI; + +} \ No newline at end of file diff --git a/structurizr-graphviz/src/main/java/com/structurizr/graphviz/DOTDiagram.java b/structurizr-graphviz/src/main/java/com/structurizr/graphviz/DOTDiagram.java new file mode 100644 index 000000000..40b232858 --- /dev/null +++ b/structurizr-graphviz/src/main/java/com/structurizr/graphviz/DOTDiagram.java @@ -0,0 +1,17 @@ +package com.structurizr.graphviz; + +import com.structurizr.export.Diagram; +import com.structurizr.view.ModelView; + +class DOTDiagram extends Diagram { + + DOTDiagram(ModelView view, String definition) { + super(view, definition); + } + + @Override + public String getFileExtension() { + return "dot"; + } + +} \ No newline at end of file diff --git a/structurizr-graphviz/src/main/java/com/structurizr/graphviz/DOTExporter.java b/structurizr-graphviz/src/main/java/com/structurizr/graphviz/DOTExporter.java new file mode 100644 index 000000000..c3c73b6cc --- /dev/null +++ b/structurizr-graphviz/src/main/java/com/structurizr/graphviz/DOTExporter.java @@ -0,0 +1,620 @@ +package com.structurizr.graphviz; + +import com.structurizr.export.AbstractDiagramExporter; +import com.structurizr.export.Diagram; +import com.structurizr.export.IndentingWriter; +import com.structurizr.model.*; +import com.structurizr.util.StringUtils; +import com.structurizr.view.DeploymentView; +import com.structurizr.view.ModelView; +import com.structurizr.view.RelationshipView; + +import java.util.Locale; + +/** + * Writes a Structurizr view to a graphviz dot file. Please note that this is not a full export (colours, shapes, etc); + * it just contains the basics required for layout purposes. + */ +class DOTExporter extends AbstractDiagramExporter { + + private static final int CLUSTER_INTERNAL_MARGIN = 25; + + private Locale locale = Locale.US; + private RankDirection rankDirection; + private double rankSeparation; + private double nodeSeparation; + + private int groupId = 1; + + DOTExporter(RankDirection rankDirection, double rankSeparation, double nodeSeparation) { + this.rankDirection = rankDirection; + this.rankSeparation = rankSeparation; + this.nodeSeparation = nodeSeparation; + } + + void setLocale(Locale locale) { + this.locale = locale; + } + + @Override + protected void writeHeader(ModelView view, IndentingWriter writer) { + if (view.getAutomaticLayout() != null) { + if (view.getAutomaticLayout().getRankDirection() == null) { + rankDirection = RankDirection.TopBottom; + } else { + switch (view.getAutomaticLayout().getRankDirection()) { + case TopBottom: + rankDirection = RankDirection.TopBottom; + break; + case BottomTop: + rankDirection = RankDirection.BottomTop; + break; + case LeftRight: + rankDirection = RankDirection.LeftRight; + break; + case RightLeft: + rankDirection = RankDirection.RightLeft; + break; + } + } + + rankSeparation = view.getAutomaticLayout().getRankSeparation(); + nodeSeparation = view.getAutomaticLayout().getNodeSeparation(); + } + + rankSeparation = rankSeparation / Constants.STRUCTURIZR_DPI; + nodeSeparation = nodeSeparation / Constants.STRUCTURIZR_DPI; + + writer.writeLine("digraph {"); + writer.indent(); + writer.writeLine("compound=true"); + writer.writeLine(String.format(locale, "graph [splines=polyline,rankdir=%s,ranksep=%s,nodesep=%s,fontsize=5]", rankDirection.getCode(), rankSeparation, nodeSeparation)); + writer.writeLine("node [shape=box,fontsize=5]"); + writer.writeLine("edge []"); + writer.writeLine(); + } + + @Override + protected void writeFooter(ModelView view, IndentingWriter writer) { + writer.outdent(); + writer.writeLine("}"); + } + + @Override + protected void startEnterpriseBoundary(ModelView view, String enterpriseName, IndentingWriter writer) { + writer.writeLine("subgraph cluster_enterprise {"); + writer.indent(); + writer.writeLine("margin=" + CLUSTER_INTERNAL_MARGIN); + } + + @Override + protected void endEnterpriseBoundary(ModelView view, IndentingWriter writer) { + writer.outdent(); + writer.writeLine("}"); + writer.writeLine(); + } + + @Override + protected void startGroupBoundary(ModelView view, String group, IndentingWriter writer) { + writer.writeLine("subgraph \"cluster_group_" + (groupId++) + "\" {"); + + writer.indent(); + writer.writeLine("margin=" + CLUSTER_INTERNAL_MARGIN); + } + + @Override + protected void endGroupBoundary(ModelView view, IndentingWriter writer) { + writer.outdent(); + writer.writeLine("}"); + writer.writeLine(); + } + + @Override + protected void startSoftwareSystemBoundary(ModelView view, SoftwareSystem softwareSystem, IndentingWriter writer) { + writer.writeLine(String.format("subgraph cluster_%s {", softwareSystem.getId())); + writer.indent(); + writer.writeLine("margin=" + CLUSTER_INTERNAL_MARGIN); + } + + @Override + protected void endSoftwareSystemBoundary(ModelView view, IndentingWriter writer) { + writer.outdent(); + writer.writeLine("}"); + writer.writeLine(); + } + + @Override + protected void startContainerBoundary(ModelView view, Container container, IndentingWriter writer) { + writer.writeLine(String.format("subgraph cluster_%s {", container.getId())); + writer.indent(); + writer.writeLine("margin=" + CLUSTER_INTERNAL_MARGIN); + } + + @Override + protected void endContainerBoundary(ModelView view, IndentingWriter writer) { + writer.outdent(); + writer.writeLine("}"); + writer.writeLine(); + } + + @Override + protected void startDeploymentNodeBoundary(DeploymentView view, DeploymentNode deploymentNode, IndentingWriter writer) { + writer.writeLine(String.format("subgraph cluster_%s {", deploymentNode.getId())); + writer.indent(); + writer.writeLine("margin=" + CLUSTER_INTERNAL_MARGIN); + } + + @Override + protected void endDeploymentNodeBoundary(ModelView view, IndentingWriter writer) { + writer.outdent(); + writer.writeLine("}"); + writer.writeLine(); + } + + @Override + protected void writeElement(ModelView view, Element element, IndentingWriter writer) { + writer.writeLine(String.format(locale, "%s [width=%f,height=%f,fixedsize=true,id=%s,label=\"%s: %s\"]", + element.getId(), + getElementWidth(view, element.getId()) / Constants.STRUCTURIZR_DPI, // convert Structurizr dimensions to inches + getElementHeight(view, element.getId()) / Constants.STRUCTURIZR_DPI, // convert Structurizr dimensions to inches + element.getId(), + element.getId(), + escape(element.getName()) + )); + } + + @Override + protected void writeRelationship(ModelView view, RelationshipView relationshipView, IndentingWriter writer) { + if (relationshipView.getRelationship().getSource() instanceof DeploymentNode || relationshipView.getRelationship().getDestination() instanceof DeploymentNode) { + Element source = relationshipView.getRelationship().getSource(); + if (source instanceof DeploymentNode) { + source = findElementInside((DeploymentNode)source, view); + } + + Element destination = relationshipView.getRelationship().getDestination(); + if (destination instanceof DeploymentNode) { + destination = findElementInside((DeploymentNode)destination, view); + } + + if (source != null && destination != null) { + String clusterConfig = ""; + + if (relationshipView.getRelationship().getSource() instanceof DeploymentNode) { + clusterConfig += ",ltail=cluster_" + relationshipView.getRelationship().getSource().getId(); + } + + if (relationshipView.getRelationship().getDestination() instanceof DeploymentNode) { + clusterConfig += ",lhead=cluster_" + relationshipView.getRelationship().getDestination().getId(); + } + + writer.writeLine(String.format(locale, "%s -> %s [id=%s%s]", + source.getId(), + destination.getId(), + relationshipView.getId(), + clusterConfig + )); + } + } else { + Element source = relationshipView.getRelationship().getSource(); + Element destination = relationshipView.getRelationship().getDestination(); + + if (relationshipView.isResponse() != null && relationshipView.isResponse()) { + source = relationshipView.getRelationship().getDestination(); + destination = relationshipView.getRelationship().getSource(); + } + + writer.writeLine(String.format(locale, "%s -> %s [id=%s]", + source.getId(), + destination.getId(), + relationshipView.getId() + )); + } + } + + @Override + protected Diagram createDiagram(ModelView view, String definition) { + return new DOTDiagram(view, definition); + } + +// private void write(ModelView view, boolean enterpriseBoundaryIsVisible) throws Exception { +// File file = new File(path, view.getKey() + ".dot"); +// FileWriter fileWriter = new FileWriter(file); +// writeHeader(fileWriter, view); +// +// if (enterpriseBoundaryIsVisible) { +// fileWriter.write(" subgraph cluster_enterprise {\n"); +// fileWriter.write(" margin=" + CLUSTER_INTERNAL_MARGIN + "\n"); +// Set<GroupableElement> elementsInsideEnterpriseBoundary = new LinkedHashSet<>(); +// for (ElementView elementView : view.getElements()) { +// if (elementView.getElement() instanceof Person && ((Person)elementView.getElement()).getLocation() == Location.Internal) { +// elementsInsideEnterpriseBoundary.add((StaticStructureElement)elementView.getElement()); +// } +// if (elementView.getElement() instanceof SoftwareSystem && ((SoftwareSystem)elementView.getElement()).getLocation() == Location.Internal) { +// elementsInsideEnterpriseBoundary.add((StaticStructureElement)elementView.getElement()); +// } +// } +// writeElements(view, " ", elementsInsideEnterpriseBoundary, fileWriter); +// fileWriter.write(" }\n\n"); +// +// Set<GroupableElement> elementsOutsideEnterpriseBoundary = new LinkedHashSet<>(); +// for (ElementView elementView : view.getElements()) { +// if (elementView.getElement() instanceof Person && ((Person)elementView.getElement()).getLocation() != Location.Internal) { +// elementsOutsideEnterpriseBoundary.add((StaticStructureElement)elementView.getElement()); +// } +// if (elementView.getElement() instanceof SoftwareSystem && ((SoftwareSystem)elementView.getElement()).getLocation() != Location.Internal) { +// elementsOutsideEnterpriseBoundary.add((StaticStructureElement)elementView.getElement()); +// } +// if (elementView.getElement() instanceof CustomElement) { +// elementsOutsideEnterpriseBoundary.add((CustomElement)elementView.getElement()); +// } +// } +// +// writeElements(view, " ", elementsOutsideEnterpriseBoundary, fileWriter); +// } else { +// Set<GroupableElement> elements = new LinkedHashSet<>(); +// for (ElementView elementView : view.getElements()) { +// elements.add((GroupableElement)elementView.getElement()); +// } +// writeElements(view, " ", elements, fileWriter); +// } +// +// writeRelationships(view, fileWriter); +// writeFooter(fileWriter); +// fileWriter.close(); +// } +// +// void write(ContainerView view) throws Exception { +// File file = new File(path, view.getKey() + ".dot"); +// FileWriter fileWriter = new FileWriter(file); +// writeHeader(fileWriter, view); +// +// Set<SoftwareSystem> softwareSystems = new HashSet<>(); +// for (ElementView elementView : view.getElements()) { +// if (elementView.getElement().getParent() instanceof SoftwareSystem) { +// softwareSystems.add((SoftwareSystem)elementView.getElement().getParent()); +// } +// } +// List<SoftwareSystem> sortedSoftwareSystems = new ArrayList<>(softwareSystems); +// sortedSoftwareSystems.sort(Comparator.comparing(Element::getId)); +// +// for (SoftwareSystem softwareSystem : sortedSoftwareSystems) { +// fileWriter.write(String.format(locale, " subgraph cluster_%s {\n", softwareSystem.getId())); +// fileWriter.write(" margin=" + CLUSTER_INTERNAL_MARGIN + "\n"); +// +// Set<GroupableElement> scopedElements = new LinkedHashSet<>(); +// for (ElementView elementView : view.getElements()) { +// if (elementView.getElement().getParent() == softwareSystem) { +// scopedElements.add((StaticStructureElement) elementView.getElement()); +// } +// } +// writeElements(view, " ", scopedElements, fileWriter); +// fileWriter.write(" }\n"); +// +// } +// +// for (ElementView elementView : view.getElements()) { +// if (elementView.getElement().getParent() == null) { +// writeElement(view, " ", elementView.getElement(), fileWriter); +// } +// } +// +// writeRelationships(view, fileWriter); +// +// writeFooter(fileWriter); +// fileWriter.close(); +// } +// +// void write(ComponentView view) throws Exception { +// File file = new File(path, view.getKey() + ".dot"); +// FileWriter fileWriter = new FileWriter(file); +// writeHeader(fileWriter, view); +// +// Set<Container> containers = new HashSet<>(); +// for (ElementView elementView : view.getElements()) { +// if (elementView.getElement().getParent() instanceof Container) { +// containers.add((Container)elementView.getElement().getParent()); +// } +// } +// List<Container> sortedContainers = new ArrayList<>(containers); +// sortedContainers.sort(Comparator.comparing(Element::getId)); +// +// for (Container container : sortedContainers) { +// fileWriter.write(String.format(locale, " subgraph cluster_%s {\n", container.getId())); +// fileWriter.write(" margin=" + CLUSTER_INTERNAL_MARGIN + "\n"); +// +// Set<GroupableElement> scopedElements = new LinkedHashSet<>(); +// for (ElementView elementView : view.getElements()) { +// if (elementView.getElement().getParent() == container) { +// scopedElements.add((StaticStructureElement) elementView.getElement()); +// } +// } +// writeElements(view, " ", scopedElements, fileWriter); +// fileWriter.write(" }\n"); +// } +// +// for (ElementView elementView : view.getElements()) { +// if (!(elementView.getElement().getParent() instanceof Container)) { +// writeElement(view, " ", elementView.getElement(), fileWriter); +// } +// } +// +// writeRelationships(view, fileWriter); +// +// writeFooter(fileWriter); +// fileWriter.close(); +// } +// +// void write(DynamicView view) throws Exception { +// File file = new File(path, view.getKey() + ".dot"); +// FileWriter fileWriter = new FileWriter(file); +// writeHeader(fileWriter, view); +// +// Element element = view.getElement(); +// +// if (element == null) { +// for (ElementView elementView : view.getElements()) { +// writeElement(view, " ", elementView.getElement(), fileWriter); +// } +// } else if (element instanceof SoftwareSystem) { +// List<SoftwareSystem> softwareSystems = new ArrayList<>(view.getElements().stream().map(ElementView::getElement).filter(e -> e instanceof Container).map(c -> ((Container)c).getSoftwareSystem()).collect(Collectors.toSet())); +// softwareSystems.sort(Comparator.comparing(Element::getId)); +// +// for (SoftwareSystem softwareSystem : softwareSystems) { +// fileWriter.write(String.format(locale, " subgraph cluster_%s {\n", softwareSystem.getId())); +// fileWriter.write(" margin=" + CLUSTER_INTERNAL_MARGIN + "\n"); +// for (ElementView elementView : view.getElements()) { +// if (elementView.getElement().getParent() == softwareSystem) { +// writeElement(view, " ", elementView.getElement(), fileWriter); +// } +// } +// fileWriter.write(" }\n"); +// } +// +// for (ElementView elementView : view.getElements()) { +// if (elementView.getElement().getParent() == null) { +// writeElement(view, " ", elementView.getElement(), fileWriter); +// } +// } +// } else if (element instanceof Container) { +// List<Container> containers = new ArrayList<>(view.getElements().stream().map(ElementView::getElement).filter(e -> e instanceof Component).map(c -> ((Component)c).getContainer()).collect(Collectors.toSet())); +// containers.sort(Comparator.comparing(Element::getId)); +// +// for (Container container : containers) { +// fileWriter.write(String.format(locale, " subgraph cluster_%s {\n", container.getId())); +// fileWriter.write(" margin=" + CLUSTER_INTERNAL_MARGIN + "\n"); +// for (ElementView elementView : view.getElements()) { +// if (elementView.getElement().getParent() == container) { +// writeElement(view, " ", elementView.getElement(), fileWriter); +// } +// } +// fileWriter.write(" }\n"); +// } +// +// for (ElementView elementView : view.getElements()) { +// if (!(elementView.getElement().getParent() instanceof Container)) { +// writeElement(view, " ", elementView.getElement(), fileWriter); +// } +// } +// } +// +// writeRelationships(view, fileWriter); +// +// writeFooter(fileWriter); +// fileWriter.close(); +// } +// +// void write(DeploymentView view) throws Exception { +// File file = new File(path, view.getKey() + ".dot"); +// FileWriter fileWriter = new FileWriter(file); +// writeHeader(fileWriter, view); +// +// for (ElementView elementView : view.getElements()) { +// if (elementView.getElement() instanceof DeploymentNode && elementView.getElement().getParent() == null) { +// write(view, (DeploymentNode)elementView.getElement(), fileWriter, ""); +// } else if (elementView.getElement() instanceof CustomElement) { +// writeElement(view, " ", elementView.getElement(), fileWriter); +// } +// } +// +// writeRelationships(view, fileWriter); +// +// writeFooter(fileWriter); +// fileWriter.close(); +// } +// +// private void write(DeploymentView view, DeploymentNode deploymentNode, FileWriter fileWriter, String indent) throws Exception { +// fileWriter.write(String.format(locale, indent + "subgraph cluster_%s {\n", deploymentNode.getId())); +// fileWriter.write(indent + " margin=" + CLUSTER_INTERNAL_MARGIN + "\n"); +// fileWriter.write(String.format(locale, indent + " label=\"%s: %s\"\n", deploymentNode.getId(), deploymentNode.getName())); +// +// for (DeploymentNode child : deploymentNode.getChildren()) { +// if (view.isElementInView(child)) { +// write(view, child, fileWriter, indent + " "); +// +// } +// } +// +// for (InfrastructureNode infrastructureNode : deploymentNode.getInfrastructureNodes()) { +// if (view.isElementInView(infrastructureNode)) { +// writeElement(view, indent + " ", infrastructureNode, fileWriter); +// } +// } +// +// for (SoftwareSystemInstance softwareSystemInstance : deploymentNode.getSoftwareSystemInstances()) { +// if (view.isElementInView(softwareSystemInstance)) { +// writeElement(view, indent + " ", softwareSystemInstance, fileWriter); +// } +// } +// +// for (ContainerInstance containerInstance : deploymentNode.getContainerInstances()) { +// if (view.isElementInView(containerInstance)) { +// writeElement(view, indent + " ", containerInstance, fileWriter); +// } +// } +// +// fileWriter.write(indent + "}\n"); +// } +// +// private void writeElements(ModelView view, String padding, Set<GroupableElement> elements, Writer writer) throws Exception { +// String groupSeparator = view.getModel().getProperties().get(GROUP_SEPARATOR_PROPERTY_NAME); +// boolean nested = !StringUtils.isNullOrEmpty(groupSeparator); +// +// Set<String> groups = new HashSet<>(); +// for (GroupableElement element : elements) { +// String group = element.getGroup(); +// +// if (!StringUtils.isNullOrEmpty(group)) { +// groups.add(group); +// +// if (nested) { +// while (group.contains(groupSeparator)) { +// group = group.substring(0, group.lastIndexOf(groupSeparator)); +// groups.add(group); +// } +// } +// } +// } +// +// List<String> sortedGroups = new ArrayList<>(groups); +// sortedGroups.sort(String::compareTo); +// +// // first render grouped elements +// if (nested) { +// if (groups.size() > 0) { +// String context = ""; +// for (String group : sortedGroups) { +// int groupCount = group.split(groupSeparator).length; +// int contextCount = context.split(groupSeparator).length; +// +// if (groupCount > contextCount) { +// // moved from a to a/b +// // - increase padding +// padding = padding + INDENT; +// } else if (groupCount == contextCount) { +// // moved from a/b to a/c +// // - close off previous subgraph +// if (context.length() > 0) { +// writer.write(padding + "}\n"); +// } +// } else { +// // moved from a/b/c to a/b or a +// // - close off previous subgraphs +// // - close off current subgraph +// for (int i = 0; i < (contextCount - groupCount); i++) { +// writer.write(padding + "}\n"); +// padding = padding.substring(0, padding.length() - INDENT.length()); +// } +// writer.write(padding + "}\n"); +// } +// +// writer.write(padding + "subgraph cluster_group_" + groupId + " {\n"); +//// writer.write(padding + " // " + group + "\n"); +// writer.write(padding + " margin=" + CLUSTER_INTERNAL_MARGIN + "\n"); +// for (GroupableElement element : elements) { +// if (group.equals(element.getGroup())) { +// writeElement(view, padding + INDENT, element, writer); +// } +// } +// groupId++; +// context = group; +// } +// +// int contextCount = context.split(groupSeparator).length; +// for (int i = 0; i < contextCount; i++) { +// writer.write(padding + "}\n"); +// padding = padding.substring(0, padding.length() - INDENT.length()); +// } +// } +// } else { +// for (String group : sortedGroups) { +// writer.write(padding + "subgraph cluster_group_" + groupId + " {\n"); +// writer.write(padding + " margin=" + CLUSTER_INTERNAL_MARGIN + "\n"); +// for (GroupableElement element : elements) { +// if (group.equals(element.getGroup())) { +// writeElement(view, padding + INDENT, element, writer); +// } +// } +// writer.write(padding + "}\n"); +// groupId++; +// } +// } +// +// // then render ungrouped elements +// for (GroupableElement element : elements) { +// if (StringUtils.isNullOrEmpty(element.getGroup())) { +// writeElement(view, padding, element, writer); +// } +// } +// } +// +// private void writeElement(ModelView view, String padding, Element element, Writer writer) throws Exception { +// writer.write(String.format(locale, "%s%s [width=%f,height=%f,fixedsize=true,id=%s,label=\"%s: %s\"]", +// padding, +// element.getId(), +// getElementWidth(view, element.getId()) / Constants.STRUCTURIZR_DPI, // convert Structurizr dimensions to inches +// getElementHeight(view, element.getId()) / Constants.STRUCTURIZR_DPI, // convert Structurizr dimensions to inches +// element.getId(), +// element.getId(), +// escape(element.getName()) +// )); +// writer.write("\n"); +// } +// + private String escape(String s) { + if (StringUtils.isNullOrEmpty(s)) { + return s; + } else { + return s.replaceAll("\"", "\\\\\""); + } + } +// +// private void writeRelationships(ModelView view, Writer writer) throws Exception { +// writer.write("\n"); +// +// for (RelationshipView relationshipView : view.getRelationships()) { +// } +// } + + private Element findElementInside(DeploymentNode deploymentNode, ModelView view) { + for (SoftwareSystemInstance softwareSystemInstance : deploymentNode.getSoftwareSystemInstances()) { + if (view.isElementInView(softwareSystemInstance)) { + return softwareSystemInstance; + } + } + + for (ContainerInstance containerInstance : deploymentNode.getContainerInstances()) { + if (view.isElementInView(containerInstance)) { + return containerInstance; + } + } + + for (InfrastructureNode infrastructureNode : deploymentNode.getInfrastructureNodes()) { + if (view.isElementInView(infrastructureNode)) { + return infrastructureNode; + } + } + + if (deploymentNode.hasChildren()) { + for (DeploymentNode child : deploymentNode.getChildren()) { + Element element = findElementInside(child, view); + + if (element != null) { + return element; + } + } + } + + return null; + } + + private int getElementWidth(ModelView view, String elementId) { + Element element = view.getModel().getElement(elementId); + return view.getViewSet().getConfiguration().getStyles().findElementStyle(element).getWidth(); + } + + private int getElementHeight(ModelView view, String elementId) { + Element element = view.getModel().getElement(elementId); + return view.getViewSet().getConfiguration().getStyles().findElementStyle(element).getHeight(); + } + +} \ No newline at end of file diff --git a/structurizr-graphviz/src/main/java/com/structurizr/graphviz/GraphvizAutomaticLayout.java b/structurizr-graphviz/src/main/java/com/structurizr/graphviz/GraphvizAutomaticLayout.java new file mode 100644 index 000000000..defdc86f0 --- /dev/null +++ b/structurizr-graphviz/src/main/java/com/structurizr/graphviz/GraphvizAutomaticLayout.java @@ -0,0 +1,217 @@ +package com.structurizr.graphviz; + +import com.structurizr.Workspace; +import com.structurizr.export.Diagram; +import com.structurizr.view.*; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.BufferedWriter; +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.List; +import java.util.Locale; + +/** + * Applies the graphviz automatic layout to views in a Structurizr workspace. + * + * Note: this class assumes that the "dot" command is available. + */ +public class GraphvizAutomaticLayout { + + private static final Log log = LogFactory.getLog(GraphvizAutomaticLayout.class); + + private static final String DOT_EXECUTABLE = "dot"; + private static final String USE_SVG_OUTPUT_FORMAT_OPTION = "-Tsvg"; + private static final String AUTOMATICALLY_GENERATE_OUTPUT_FILE_OPTION = "-O"; + private static final String DOT_FILE_EXTENSION = ".dot"; + + private final File path; + + private RankDirection rankDirection = RankDirection.TopBottom; + private double rankSeparation = 1.0; + private double nodeSeparation = 1.0; + + private int margin = 400; + private boolean changePaperSize = true; + + private Locale locale = Locale.US; + + public GraphvizAutomaticLayout() { + this(new File(".")); + } + + public GraphvizAutomaticLayout(File path) { + this.path = path; + } + + public void setRankDirection(RankDirection rankDirection) { + this.rankDirection = rankDirection; + } + + public void setRankSeparation(double rankSeparation) { + this.rankSeparation = rankSeparation; + } + + public void setNodeSeparation(double nodeSeparation) { + this.nodeSeparation = nodeSeparation; + } + + public void setMargin(int margin) { + this.margin = margin; + } + + public void setChangePaperSize(boolean changePaperSize) { + this.changePaperSize = changePaperSize; + } + + /** + * Sets the locale used when writing DOT files. + * + * @param locale a Locale instance + */ + public void setLocale(Locale locale) { + this.locale = locale; + } + + private DOTExporter createDOTExporter() { + DOTExporter exporter = new DOTExporter(rankDirection, rankSeparation, nodeSeparation); + exporter.setLocale(locale); + + return exporter; + } + + private void writeFile(Diagram diagram) throws Exception { + File file = new File(path, diagram.getKey() + DOT_FILE_EXTENSION); + log.debug("Writing " + file.getAbsolutePath()); + BufferedWriter writer = Files.newBufferedWriter(file.toPath(), StandardCharsets.UTF_8); + writer.write(diagram.getDefinition()); + writer.flush(); + writer.close(); + + if (!file.exists()) { + log.error(file.getAbsolutePath() + " does not exist"); + } + } + + private SVGReader createSVGReader() { + return new SVGReader(path, margin, changePaperSize); + } + + private void runGraphviz(View view) throws Exception { + ProcessBuilder processBuilder = new ProcessBuilder().inheritIO(); + List<String> command = List.of( + DOT_EXECUTABLE, + new File(path, view.getKey() + DOT_FILE_EXTENSION).getAbsolutePath(), + USE_SVG_OUTPUT_FORMAT_OPTION, + AUTOMATICALLY_GENERATE_OUTPUT_FILE_OPTION + ); + + processBuilder.command(command); + + StringBuilder buf = new StringBuilder(); + for (String s : command) { + buf.append(s); + buf.append(" "); + } + log.debug(buf); + + Process process = processBuilder.start(); + int exitCode = process.waitFor(); + assert exitCode == 0; + + String input = new String(process.getInputStream().readAllBytes()); + String error = new String(process.getErrorStream().readAllBytes()); + + log.debug("stdout: " + input); + log.debug("stderr: " + error); + } + + public void apply(CustomView view) throws Exception { + log.debug("Running Graphviz for view with key " + view.getKey()); + Diagram diagram = createDOTExporter().export(view); + writeFile(diagram); + runGraphviz(view); + createSVGReader().parseAndApplyLayout(view); + } + + public void apply(SystemLandscapeView view) throws Exception { + log.debug("Running Graphviz for view with key " + view.getKey()); + Diagram diagram = createDOTExporter().export(view); + writeFile(diagram); + runGraphviz(view); + createSVGReader().parseAndApplyLayout(view); + } + + public void apply(SystemContextView view) throws Exception { + log.debug("Running Graphviz for view with key " + view.getKey()); + Diagram diagram = createDOTExporter().export(view); + writeFile(diagram); + runGraphviz(view); + createSVGReader().parseAndApplyLayout(view); + } + + public void apply(ContainerView view) throws Exception { + log.debug("Running Graphviz for view with key " + view.getKey()); + Diagram diagram = createDOTExporter().export(view); + writeFile(diagram); + runGraphviz(view); + createSVGReader().parseAndApplyLayout(view); + } + + public void apply(ComponentView view) throws Exception { + log.debug("Running Graphviz for view with key " + view.getKey()); + Diagram diagram = createDOTExporter().export(view); + writeFile(diagram); + runGraphviz(view); + createSVGReader().parseAndApplyLayout(view); + } + + public void apply(DynamicView view) throws Exception { + log.debug("Running Graphviz for view with key " + view.getKey()); + Diagram diagram = createDOTExporter().export(view); + writeFile(diagram); + runGraphviz(view); + createSVGReader().parseAndApplyLayout(view); + } + + public void apply(DeploymentView view) throws Exception { + log.debug("Running Graphviz for view with key " + view.getKey()); + Diagram diagram = createDOTExporter().export(view); + writeFile(diagram); + runGraphviz(view); + createSVGReader().parseAndApplyLayout(view); + } + + public void apply(Workspace workspace) throws Exception { + for (CustomView view : workspace.getViews().getCustomViews()) { + apply(view); + } + + for (SystemLandscapeView view : workspace.getViews().getSystemLandscapeViews()) { + apply(view); + } + + for (SystemContextView view : workspace.getViews().getSystemContextViews()) { + apply(view); + } + + for (ContainerView view : workspace.getViews().getContainerViews()) { + apply(view); + } + + for (ComponentView view : workspace.getViews().getComponentViews()) { + apply(view); + } + + for (DynamicView view : workspace.getViews().getDynamicViews()) { + apply(view); + } + + for (DeploymentView view : workspace.getViews().getDeploymentViews()) { + apply(view); + } + } + +} \ No newline at end of file diff --git a/structurizr-graphviz/src/main/java/com/structurizr/graphviz/RankDirection.java b/structurizr-graphviz/src/main/java/com/structurizr/graphviz/RankDirection.java new file mode 100644 index 000000000..4bbe7486e --- /dev/null +++ b/structurizr-graphviz/src/main/java/com/structurizr/graphviz/RankDirection.java @@ -0,0 +1,23 @@ +package com.structurizr.graphviz; + +/** + * The various rank directions used by graphviz. + */ +public enum RankDirection { + + TopBottom("TB"), + BottomTop("BT"), + LeftRight("LR"), + RightLeft("RL"); + + private String code; + + RankDirection(String code) { + this.code = code; + } + + public String getCode() { + return code; + } + +} \ No newline at end of file diff --git a/structurizr-graphviz/src/main/java/com/structurizr/graphviz/SVGReader.java b/structurizr-graphviz/src/main/java/com/structurizr/graphviz/SVGReader.java new file mode 100644 index 000000000..4fde3f242 --- /dev/null +++ b/structurizr-graphviz/src/main/java/com/structurizr/graphviz/SVGReader.java @@ -0,0 +1,203 @@ +package com.structurizr.graphviz; + +import com.structurizr.model.DeploymentNode; +import com.structurizr.model.Element; +import com.structurizr.view.*; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.w3c.dom.Document; +import org.w3c.dom.NodeList; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathFactory; +import java.io.File; +import java.io.FileInputStream; +import java.util.LinkedHashSet; +import java.util.Set; + +/** + * Parses an SVG file created by graphviz, extracts the layout information, and applies it to a Structurizr view. + */ +class SVGReader { + + private static final Log log = LogFactory.getLog(GraphvizAutomaticLayout.class); + + private final File path; + private final int margin; + private final boolean changePaperSize; + + SVGReader(File path, int margin, boolean changePaperSize) { + this.path = path; + this.margin = margin; + this.changePaperSize = changePaperSize; + } + + void parseAndApplyLayout(ModelView view) throws Exception { + File file = new File(path, view.getKey() + ".dot.svg"); + log.debug("Reading " + file.getAbsolutePath()); + + if (file.exists()) { + FileInputStream fileIS = new FileInputStream(file); + DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); + builderFactory.setNamespaceAware(false); + builderFactory.setValidating(false); + builderFactory.setFeature("http://xml.org/sax/features/namespaces", false); + builderFactory.setFeature("http://xml.org/sax/features/validation", false); + builderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false); + builderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + + DocumentBuilder builder = builderFactory.newDocumentBuilder(); + Document xmlDocument = builder.parse(fileIS); + XPath xPath = XPathFactory.newInstance().newXPath(); + NodeList nodeList = (NodeList) xPath.compile("/svg/g[@class=\"graph\"]").evaluate(xmlDocument, XPathConstants.NODESET); + String transform = nodeList.item(0).getAttributes().getNamedItem("transform").getNodeValue(); + String translate = transform.substring(transform.indexOf("translate")); + String numbers = translate.substring(translate.indexOf("(") + 1, translate.indexOf(")")); + int transformX = (int) Double.parseDouble(numbers.split(" ")[0]); + int transformY = (int) Double.parseDouble(numbers.split(" ")[1]); + + int minimumX = Integer.MAX_VALUE; + int minimumY = Integer.MAX_VALUE; + int maximumX = Integer.MIN_VALUE; + int maximumY = Integer.MIN_VALUE; + + for (ElementView elementView : view.getElements()) { + if (elementView.getElement() instanceof DeploymentNode) { + // deployment nodes are clusters, so positioned automatically + continue; + } + + String expression = String.format("/svg/g/g[@id=\"%s\"]/polygon", elementView.getId()); + nodeList = (NodeList) xPath.compile(expression).evaluate(xmlDocument, XPathConstants.NODESET); + if (nodeList.getLength() == 0) { + continue; + } + + String pointsAsString = nodeList.item(0).getAttributes().getNamedItem("points").getNodeValue(); + String[] points = pointsAsString.split(" "); + String[] coordinates = points[1].split(","); + + double x = Double.parseDouble(coordinates[0]) + transformX; + double y = Double.parseDouble(coordinates[1]) + transformY; + + elementView.setX((int) (x * Constants.DPI_RATIO)); + elementView.setY((int) (y * Constants.DPI_RATIO)); + + minimumX = Math.min(elementView.getX(), minimumX); + minimumY = Math.min(elementView.getY(), minimumY); + + ElementStyle style = view.getViewSet().getConfiguration().getStyles().findElementStyle(view.getModel().getElement(elementView.getId())); + + maximumX = Math.max(elementView.getX() + style.getWidth(), maximumX); + maximumY = Math.max(elementView.getY() + style.getHeight(), maximumY); + } + + for (RelationshipView relationshipView : view.getRelationships()) { + String expression = String.format("/svg/g/g[@id=\"%s\"]/path", relationshipView.getId()); + nodeList = (NodeList) xPath.compile(expression).evaluate(xmlDocument, XPathConstants.NODESET); + if (nodeList.getLength() == 0) { + continue; + } + + String dAsString = nodeList.item(0).getAttributes().getNamedItem("d").getNodeValue(); + String[] d = dAsString.split(" "); + + Set<Vertex> vertices = new LinkedHashSet<>(); + + if (d.length == 3) { + relationshipView.setVertices(vertices); + } else { + for (int i = 1; i < d.length - 2; i++) { + double x = Double.parseDouble(d[i].split(",")[0]) + transformX; + double y = Double.parseDouble(d[i].split(",")[1]) + transformY; + Vertex vertex = new Vertex((int) (x * Constants.DPI_RATIO), (int) (y * Constants.DPI_RATIO)); + vertices.add(vertex); + + minimumX = Math.min(vertex.getX(), minimumX); + minimumY = Math.min(vertex.getY(), minimumY); + maximumX = Math.max(vertex.getX(), maximumX); + maximumY = Math.max(vertex.getY(), maximumY); + } + relationshipView.setVertices(vertices); + } + } + + // also take into account any clusters that might be rendered outside the nodes + String expression = "/svg/g/g[@class=\"cluster\"]/polygon"; + nodeList = (NodeList) xPath.compile(expression).evaluate(xmlDocument, XPathConstants.NODESET); + for (int i = 0; i < nodeList.getLength(); i++) { + String[] points = nodeList.item(i).getAttributes().getNamedItem("points").getNodeValue().split(" "); + for (String point : points) { + int x = (int) ((Double.parseDouble(point.split(",")[0]) + transformX) * Constants.DPI_RATIO); + int y = (int) ((Double.parseDouble(point.split(",")[1]) + transformY) * Constants.DPI_RATIO); + + minimumX = Math.min(x, minimumX); + minimumY = Math.min(y, minimumY); + maximumX = Math.max(x, maximumX); + maximumY = Math.max(y, maximumY); + } + } + + int pageWidth = Math.max(margin, maximumX + margin); + int pageHeight = Math.max(margin, maximumY + margin); + + if (changePaperSize) { + view.setPaperSize(null); + view.setDimensions(new Dimensions(pageWidth, pageHeight)); + + PaperSize.Orientation orientation = (pageWidth > pageHeight) ? PaperSize.Orientation.Landscape : PaperSize.Orientation.Portrait; + for (PaperSize paperSize : PaperSize.getOrderedPaperSizes(orientation)) { + if (paperSize.getWidth() > (pageWidth) && paperSize.getHeight() > (pageHeight)) { + view.setPaperSize(paperSize); + break; + } + } + } + + int deltaX = (pageWidth - maximumX + minimumX) / 2; + int deltaY = (pageHeight - maximumY + minimumY) / 2; + + // move everything relative to 0,0 + for (ElementView elementView : view.getElements()) { + elementView.setX(elementView.getX() - minimumX); + elementView.setY(elementView.getY() - minimumY); + } + for (RelationshipView relationshipView : view.getRelationships()) { + for (Vertex vertex : relationshipView.getVertices()) { + vertex.setX(vertex.getX() - minimumX); + vertex.setY(vertex.getY() - minimumY); + } + } + + // and now centre everything + for (ElementView elementView : view.getElements()) { + elementView.setX(elementView.getX() + deltaX); + elementView.setY(elementView.getY() + deltaY); + } + for (RelationshipView relationshipView : view.getRelationships()) { + for (Vertex vertex : relationshipView.getVertices()) { + vertex.setX(vertex.getX() + deltaX); + vertex.setY(vertex.getY() + deltaY); + } + } + + log.debug("Layout applied to view with key " + view.getKey()); + } else { + log.error(file.getAbsolutePath() + " does not exist; layout not applied to view with key " + view.getKey()); + } + } + + private int getElementWidth(ModelView view, String elementId) { + Element element = view.getModel().getElement(elementId); + return view.getViewSet().getConfiguration().getStyles().findElementStyle(element).getWidth(); + } + + private int getElementHeight(ModelView view, String elementId) { + Element element = view.getModel().getElement(elementId); + return view.getViewSet().getConfiguration().getStyles().findElementStyle(element).getHeight(); + } + +} \ No newline at end of file diff --git a/structurizr-graphviz/src/test/java/com/structurizr/graphviz/DOTExporterTests.java b/structurizr-graphviz/src/test/java/com/structurizr/graphviz/DOTExporterTests.java new file mode 100644 index 000000000..b3ffc35de --- /dev/null +++ b/structurizr-graphviz/src/test/java/com/structurizr/graphviz/DOTExporterTests.java @@ -0,0 +1,616 @@ +package com.structurizr.graphviz; + +import com.structurizr.Workspace; +import com.structurizr.export.Diagram; +import com.structurizr.model.*; +import com.structurizr.util.WorkspaceUtils; +import com.structurizr.view.*; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.util.Locale; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class DOTExporterTests { + + @Test + public void test_writeCustomView() { + Workspace workspace = new Workspace("Name", ""); + CustomElement box1 = workspace.getModel().addCustomElement("Box 1"); + CustomElement box2 = workspace.getModel().addCustomElement("Box 2"); + box1.uses(box2, "Uses"); + + CustomView view = workspace.getViews().createCustomView("CustomView", "Title", "Description"); + view.add(box1); + view.add(box2); + + DOTExporter exporter = new DOTExporter(RankDirection.TopBottom, 300, 300); + Diagram diagram = exporter.export(view); + + String content = diagram.getDefinition(); + assertEquals("digraph {\n" + + " compound=true\n" + + " graph [splines=polyline,rankdir=TB,ranksep=1.0,nodesep=1.0,fontsize=5]\n" + + " node [shape=box,fontsize=5]\n" + + " edge []\n" + + "\n" + + " 1 [width=1.500000,height=1.000000,fixedsize=true,id=1,label=\"1: Box 1\"]\n" + + " 2 [width=1.500000,height=1.000000,fixedsize=true,id=2,label=\"2: Box 2\"]\n" + + "\n" + + " 1 -> 2 [id=3]\n" + + "}", content); + } + + @Test + public void test_writeSystemLandscapeViewWithNoEnterpriseBoundary() { + Workspace workspace = new Workspace("Name", ""); + CustomElement box = workspace.getModel().addCustomElement("Box"); + Person user = workspace.getModel().addPerson("User", ""); + user.setLocation(Location.External); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", ""); + softwareSystem.setLocation(Location.Internal); + user.uses(softwareSystem, "Uses"); + + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("SystemLandscape", ""); + view.addAllElements(); + view.add(box); + view.setEnterpriseBoundaryVisible(false); + + DOTExporter exporter = new DOTExporter(RankDirection.TopBottom, 300, 300); + Diagram diagram = exporter.export(view); + + String content = diagram.getDefinition(); + assertEquals("digraph {\n" + + " compound=true\n" + + " graph [splines=polyline,rankdir=TB,ranksep=1.0,nodesep=1.0,fontsize=5]\n" + + " node [shape=box,fontsize=5]\n" + + " edge []\n" + + "\n" + + " 1 [width=1.500000,height=1.000000,fixedsize=true,id=1,label=\"1: Box\"]\n" + + " 2 [width=1.500000,height=1.000000,fixedsize=true,id=2,label=\"2: User\"]\n" + + " 3 [width=1.500000,height=1.000000,fixedsize=true,id=3,label=\"3: Software System\"]\n" + + "\n" + + " 2 -> 3 [id=4]\n" + + "}", content); + } + + @Test + public void test_writeSystemLandscapeViewWithGroupedElements() throws Exception { + Workspace workspace = new Workspace("Name", ""); + CustomElement box = workspace.getModel().addCustomElement("Box"); + Person user = workspace.getModel().addPerson("User", ""); + user.setGroup("External"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", ""); + softwareSystem.setGroup("Internal"); + user.uses(softwareSystem, "Uses"); + + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("SystemLandscape", ""); + view.addAllElements(); + view.add(box); + view.setEnterpriseBoundaryVisible(false); + + DOTExporter exporter = new DOTExporter(RankDirection.TopBottom, 300, 300); + Diagram diagram = exporter.export(view); + + String content = diagram.getDefinition(); + assertEquals("digraph {\n" + + " compound=true\n" + + " graph [splines=polyline,rankdir=TB,ranksep=1.0,nodesep=1.0,fontsize=5]\n" + + " node [shape=box,fontsize=5]\n" + + " edge []\n" + + "\n" + + " subgraph \"cluster_group_1\" {\n" + + " margin=25\n" + + " 2 [width=1.500000,height=1.000000,fixedsize=true,id=2,label=\"2: User\"]\n" + + " }\n" + + "\n" + + " subgraph \"cluster_group_2\" {\n" + + " margin=25\n" + + " 3 [width=1.500000,height=1.000000,fixedsize=true,id=3,label=\"3: Software System\"]\n" + + " }\n" + + "\n" + + " 1 [width=1.500000,height=1.000000,fixedsize=true,id=1,label=\"1: Box\"]\n" + + "\n" + + " 2 -> 3 [id=4]\n" + + "}", content); + } + + @Test + public void test_writeSystemLandscapeViewWithNestedGroupedElements() throws Exception { + Workspace workspace = new Workspace("Name", ""); + workspace.getModel().addProperty("structurizr.groupSeparator", "/"); + + SoftwareSystem a = workspace.getModel().addSoftwareSystem("A"); + a.setGroup("Enterprise 1/Department 1/Team 1"); + + SoftwareSystem b = workspace.getModel().addSoftwareSystem("B"); + b.setGroup("Enterprise 1/Department 1/Team 2"); + + SoftwareSystem c = workspace.getModel().addSoftwareSystem("C"); + c.setGroup("Enterprise 1/Department 2"); + + SoftwareSystem d = workspace.getModel().addSoftwareSystem("D"); + d.setGroup("Enterprise 2"); + + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("SystemLandscape", ""); + view.addAllElements(); + + DOTExporter exporter = new DOTExporter(RankDirection.TopBottom, 300, 300); + Diagram diagram = exporter.export(view); + + String content = diagram.getDefinition(); + assertEquals("digraph {\n" + + " compound=true\n" + + " graph [splines=polyline,rankdir=TB,ranksep=1.0,nodesep=1.0,fontsize=5]\n" + + " node [shape=box,fontsize=5]\n" + + " edge []\n" + + "\n" + + " subgraph \"cluster_group_1\" {\n" + + " margin=25\n" + + " subgraph \"cluster_group_2\" {\n" + + " margin=25\n" + + " subgraph \"cluster_group_3\" {\n" + + " margin=25\n" + + " 1 [width=1.500000,height=1.000000,fixedsize=true,id=1,label=\"1: A\"]\n" + + " }\n" + + "\n" + + " subgraph \"cluster_group_4\" {\n" + + " margin=25\n" + + " 2 [width=1.500000,height=1.000000,fixedsize=true,id=2,label=\"2: B\"]\n" + + " }\n" + + "\n" + + " }\n" + + "\n" + + " subgraph \"cluster_group_5\" {\n" + + " margin=25\n" + + " 3 [width=1.500000,height=1.000000,fixedsize=true,id=3,label=\"3: C\"]\n" + + " }\n" + + "\n" + + " }\n" + + "\n" + + " subgraph \"cluster_group_6\" {\n" + + " margin=25\n" + + " 4 [width=1.500000,height=1.000000,fixedsize=true,id=4,label=\"4: D\"]\n" + + " }\n" + + "\n" + + "\n" + + "}", content); + } + + @Test + public void test_writeSystemLandscapeViewWithNoEnterpriseBoundaryInGermanLocale() throws Exception { + // ranksep=1.0 was being output as ranksep=1,0 + Locale.setDefault(new Locale("de", "DE")); + Workspace workspace = new Workspace("Name", ""); + CustomElement box = workspace.getModel().addCustomElement("Box"); + Person user = workspace.getModel().addPerson("User", ""); + user.setLocation(Location.External); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", ""); + softwareSystem.setLocation(Location.Internal); + user.uses(softwareSystem, "Uses"); + + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("SystemLandscape", ""); + view.addAllElements(); + view.add(box); + view.setEnterpriseBoundaryVisible(false); + + DOTExporter exporter = new DOTExporter(RankDirection.TopBottom, 300, 300); + Diagram diagram = exporter.export(view); + + String content = diagram.getDefinition(); + assertEquals("digraph {\n" + + " compound=true\n" + + " graph [splines=polyline,rankdir=TB,ranksep=1.0,nodesep=1.0,fontsize=5]\n" + + " node [shape=box,fontsize=5]\n" + + " edge []\n" + + "\n" + + " 1 [width=1.500000,height=1.000000,fixedsize=true,id=1,label=\"1: Box\"]\n" + + " 2 [width=1.500000,height=1.000000,fixedsize=true,id=2,label=\"2: User\"]\n" + + " 3 [width=1.500000,height=1.000000,fixedsize=true,id=3,label=\"3: Software System\"]\n" + + "\n" + + " 2 -> 3 [id=4]\n" + + "}", content); + } + + @Test + public void test_writeSystemLandscapeViewWithAnEnterpriseBoundary() throws Exception { + Workspace workspace = new Workspace("Name", ""); + CustomElement box = workspace.getModel().addCustomElement("Box"); + Person user = workspace.getModel().addPerson("User", ""); + user.setLocation(Location.External); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", ""); + softwareSystem.setLocation(Location.Internal); + user.uses(softwareSystem, "Uses"); + + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("SystemLandscape", ""); + view.addAllElements(); + view.add(box); + view.setEnterpriseBoundaryVisible(true); + + DOTExporter exporter = new DOTExporter(RankDirection.TopBottom, 300, 300); + Diagram diagram = exporter.export(view); + + String content = diagram.getDefinition(); + assertEquals("digraph {\n" + + " compound=true\n" + + " graph [splines=polyline,rankdir=TB,ranksep=1.0,nodesep=1.0,fontsize=5]\n" + + " node [shape=box,fontsize=5]\n" + + " edge []\n" + + "\n" + + " subgraph cluster_enterprise {\n" + + " margin=25\n" + + " 3 [width=1.500000,height=1.000000,fixedsize=true,id=3,label=\"3: Software System\"]\n" + + " }\n" + + "\n" + + " 1 [width=1.500000,height=1.000000,fixedsize=true,id=1,label=\"1: Box\"]\n" + + " 2 [width=1.500000,height=1.000000,fixedsize=true,id=2,label=\"2: User\"]\n" + + "\n" + + " 2 -> 3 [id=4]\n" + + "}", content); + } + + @Test + public void test_writeSystemContextViewWithNoEnterpriseBoundary() throws Exception { + Workspace workspace = new Workspace("Name", ""); + CustomElement box = workspace.getModel().addCustomElement("Box"); + Person user = workspace.getModel().addPerson("User", ""); + user.setLocation(Location.External); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", ""); + softwareSystem.setLocation(Location.Internal); + user.uses(softwareSystem, "Uses"); + + SystemContextView view = workspace.getViews().createSystemContextView(softwareSystem, "SystemContext", ""); + view.addAllElements(); + view.add(box); + view.setEnterpriseBoundaryVisible(false); + + DOTExporter exporter = new DOTExporter(RankDirection.TopBottom, 300, 300); + Diagram diagram = exporter.export(view); + + String content = diagram.getDefinition(); + assertEquals("digraph {\n" + + " compound=true\n" + + " graph [splines=polyline,rankdir=TB,ranksep=1.0,nodesep=1.0,fontsize=5]\n" + + " node [shape=box,fontsize=5]\n" + + " edge []\n" + + "\n" + + " 1 [width=1.500000,height=1.000000,fixedsize=true,id=1,label=\"1: Box\"]\n" + + " 2 [width=1.500000,height=1.000000,fixedsize=true,id=2,label=\"2: User\"]\n" + + " 3 [width=1.500000,height=1.000000,fixedsize=true,id=3,label=\"3: Software System\"]\n" + + "\n" + + " 2 -> 3 [id=4]\n" + + "}", content); + } + + + @Test + public void test_writeSystemContextViewWithAnEnterpriseBoundary() throws Exception { + Workspace workspace = new Workspace("Name", ""); + CustomElement box = workspace.getModel().addCustomElement("Box"); + Person user = workspace.getModel().addPerson("User", ""); + user.setLocation(Location.External); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", ""); + softwareSystem.setLocation(Location.Internal); + user.uses(softwareSystem, "Uses"); + + SystemContextView view = workspace.getViews().createSystemContextView(softwareSystem, "SystemContext", ""); + view.addAllElements(); + view.add(box); + view.setEnterpriseBoundaryVisible(true); + + DOTExporter exporter = new DOTExporter(RankDirection.TopBottom, 300, 300); + Diagram diagram = exporter.export(view); + + String content = diagram.getDefinition(); + assertEquals("digraph {\n" + + " compound=true\n" + + " graph [splines=polyline,rankdir=TB,ranksep=1.0,nodesep=1.0,fontsize=5]\n" + + " node [shape=box,fontsize=5]\n" + + " edge []\n" + + "\n" + + " subgraph cluster_enterprise {\n" + + " margin=25\n" + + " 3 [width=1.500000,height=1.000000,fixedsize=true,id=3,label=\"3: Software System\"]\n" + + " }\n" + + "\n" + + " 1 [width=1.500000,height=1.000000,fixedsize=true,id=1,label=\"1: Box\"]\n" + + " 2 [width=1.500000,height=1.000000,fixedsize=true,id=2,label=\"2: User\"]\n" + + "\n" + + " 2 -> 3 [id=4]\n" + + "}", content); + } + + @Test + public void test_writeSystemContextViewWithGroupedElements() throws Exception { + Workspace workspace = new Workspace("Name", ""); + CustomElement box = workspace.getModel().addCustomElement("Box"); + Person user = workspace.getModel().addPerson("User", ""); + user.setGroup("External"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", ""); + softwareSystem.setGroup("Internal"); + user.uses(softwareSystem, "Uses"); + + SystemContextView view = workspace.getViews().createSystemContextView(softwareSystem, "SystemContext", ""); + view.addAllElements(); + view.add(box); + view.setEnterpriseBoundaryVisible(false); + + DOTExporter exporter = new DOTExporter(RankDirection.TopBottom, 300, 300); + Diagram diagram = exporter.export(view); + + String content = diagram.getDefinition(); + assertEquals("digraph {\n" + + " compound=true\n" + + " graph [splines=polyline,rankdir=TB,ranksep=1.0,nodesep=1.0,fontsize=5]\n" + + " node [shape=box,fontsize=5]\n" + + " edge []\n" + + "\n" + + " subgraph \"cluster_group_1\" {\n" + + " margin=25\n" + + " 2 [width=1.500000,height=1.000000,fixedsize=true,id=2,label=\"2: User\"]\n" + + " }\n" + + "\n" + + " subgraph \"cluster_group_2\" {\n" + + " margin=25\n" + + " 3 [width=1.500000,height=1.000000,fixedsize=true,id=3,label=\"3: Software System\"]\n" + + " }\n" + + "\n" + + " 1 [width=1.500000,height=1.000000,fixedsize=true,id=1,label=\"1: Box\"]\n" + + "\n" + + " 2 -> 3 [id=4]\n" + + "}", content); + } + + @Test + public void test_writeContainerViewWithGroupedElementsInASingleSoftwareSystem() throws Exception { + Workspace workspace = new Workspace("Name", ""); + CustomElement box = workspace.getModel().addCustomElement("Box"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", ""); + Container container1 = softwareSystem.addContainer("Container 1"); + container1.setGroup("Group 1"); + Container container2 = softwareSystem.addContainer("Container 2"); + container2.setGroup("Group 2"); + Container container3 = softwareSystem.addContainer("Container 3"); + + container1.uses(container2, "Uses"); + container2.uses(container3, "Uses"); + + ContainerView view = workspace.getViews().createContainerView(softwareSystem, "Containers", ""); + view.addAllElements(); + view.add(box); + + DOTExporter exporter = new DOTExporter(RankDirection.TopBottom, 300, 300); + Diagram diagram = exporter.export(view); + + String content = diagram.getDefinition(); + assertEquals("digraph {\n" + + " compound=true\n" + + " graph [splines=polyline,rankdir=TB,ranksep=1.0,nodesep=1.0,fontsize=5]\n" + + " node [shape=box,fontsize=5]\n" + + " edge []\n" + + "\n" + + " 1 [width=1.500000,height=1.000000,fixedsize=true,id=1,label=\"1: Box\"]\n" + + "\n" + + " subgraph cluster_2 {\n" + + " margin=25\n" + + " subgraph \"cluster_group_1\" {\n" + + " margin=25\n" + + " 3 [width=1.500000,height=1.000000,fixedsize=true,id=3,label=\"3: Container 1\"]\n" + + " }\n" + + "\n" + + " subgraph \"cluster_group_2\" {\n" + + " margin=25\n" + + " 4 [width=1.500000,height=1.000000,fixedsize=true,id=4,label=\"4: Container 2\"]\n" + + " }\n" + + "\n" + + " 5 [width=1.500000,height=1.000000,fixedsize=true,id=5,label=\"5: Container 3\"]\n" + + " }\n" + + "\n" + + " 3 -> 4 [id=6]\n" + + " 4 -> 5 [id=7]\n" + + "}", content); + } + + @Test + public void test_writeContainerViewWithGroupedElementsInMultipleSoftwareSystems() throws Exception { + Workspace workspace = new Workspace("Name", ""); + + SoftwareSystem softwareSystem1 = workspace.getModel().addSoftwareSystem("Software System 1"); + Container container1 = softwareSystem1.addContainer("Container 1"); + container1.setGroup("Group"); + + SoftwareSystem softwareSystem2 = workspace.getModel().addSoftwareSystem("Software System 2"); + Container container2 = softwareSystem2.addContainer("Container 2"); + container2.setGroup("Group"); + + container1.uses(container2, "Uses"); + + ContainerView view = workspace.getViews().createContainerView(softwareSystem1, "Containers", ""); + view.add(container1); + view.add(container2); + + DOTExporter exporter = new DOTExporter(RankDirection.TopBottom, 300, 300); + Diagram diagram = exporter.export(view); + + String content = diagram.getDefinition(); + assertEquals("digraph {\n" + + " compound=true\n" + + " graph [splines=polyline,rankdir=TB,ranksep=1.0,nodesep=1.0,fontsize=5]\n" + + " node [shape=box,fontsize=5]\n" + + " edge []\n" + + "\n" + + " subgraph cluster_1 {\n" + + " margin=25\n" + + " subgraph \"cluster_group_1\" {\n" + + " margin=25\n" + + " 2 [width=1.500000,height=1.000000,fixedsize=true,id=2,label=\"2: Container 1\"]\n" + + " }\n" + + "\n" + + " }\n" + + "\n" + + " subgraph cluster_3 {\n" + + " margin=25\n" + + " subgraph \"cluster_group_2\" {\n" + + " margin=25\n" + + " 4 [width=1.500000,height=1.000000,fixedsize=true,id=4,label=\"4: Container 2\"]\n" + + " }\n" + + "\n" + + " }\n" + + "\n" + + " 2 -> 4 [id=5]\n" + + "}", content); + } + + @Test + public void test_writeComponentViewWithGroupedElements() throws Exception { + Workspace workspace = new Workspace("Name", ""); + CustomElement box = workspace.getModel().addCustomElement("Box"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", ""); + Container container = softwareSystem.addContainer("Container"); + Component component1 = container.addComponent("Component 1", ""); + Component component2 = container.addComponent("Component 2", ""); + component2.setGroup("Group 2"); + Component component3 = container.addComponent("Component 3", ""); + component3.setGroup("Group 3"); + + component1.uses(component2, "Uses"); + component2.uses(component3, "Uses"); + + ComponentView view = workspace.getViews().createComponentView(container, "Components", ""); + view.addAllElements(); + view.add(box); + + DOTExporter exporter = new DOTExporter(RankDirection.TopBottom, 300, 300); + Diagram diagram = exporter.export(view); + + String content = diagram.getDefinition(); + assertEquals("digraph {\n" + + " compound=true\n" + + " graph [splines=polyline,rankdir=TB,ranksep=1.0,nodesep=1.0,fontsize=5]\n" + + " node [shape=box,fontsize=5]\n" + + " edge []\n" + + "\n" + + " 1 [width=1.500000,height=1.000000,fixedsize=true,id=1,label=\"1: Box\"]\n" + + "\n" + + " subgraph cluster_3 {\n" + + " margin=25\n" + + " subgraph \"cluster_group_1\" {\n" + + " margin=25\n" + + " 5 [width=1.500000,height=1.000000,fixedsize=true,id=5,label=\"5: Component 2\"]\n" + + " }\n" + + "\n" + + " subgraph \"cluster_group_2\" {\n" + + " margin=25\n" + + " 6 [width=1.500000,height=1.000000,fixedsize=true,id=6,label=\"6: Component 3\"]\n" + + " }\n" + + "\n" + + " 4 [width=1.500000,height=1.000000,fixedsize=true,id=4,label=\"4: Component 1\"]\n" + + " }\n" + + "\n" + + " 4 -> 5 [id=7]\n" + + " 5 -> 6 [id=8]\n" + + "}", content); + } + + @Test + public void test_writeContainerViewWithGroupedElements_WithAndWithoutAGroupSeparator() throws Exception { + Workspace workspace = new Workspace("Name", ""); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", ""); + Container container1 = softwareSystem.addContainer("Container 1"); + container1.setGroup("Group 1"); + Container container2 = softwareSystem.addContainer("Container 2"); + container2.setGroup("Group 2"); + + ContainerView view = workspace.getViews().createContainerView(softwareSystem, "Containers", ""); + view.addAllElements(); + + DOTExporter exporter = new DOTExporter(RankDirection.TopBottom, 300, 300); + Diagram diagram = exporter.export(view); + + String content = diagram.getDefinition(); + + String expectedResult = "digraph {\n" + + " compound=true\n" + + " graph [splines=polyline,rankdir=TB,ranksep=1.0,nodesep=1.0,fontsize=5]\n" + + " node [shape=box,fontsize=5]\n" + + " edge []\n" + + "\n" + + " subgraph cluster_1 {\n" + + " margin=25\n" + + " subgraph \"cluster_group_1\" {\n" + + " margin=25\n" + + " 2 [width=1.500000,height=1.000000,fixedsize=true,id=2,label=\"2: Container 1\"]\n" + + " }\n" + + "\n" + + " subgraph \"cluster_group_2\" {\n" + + " margin=25\n" + + " 3 [width=1.500000,height=1.000000,fixedsize=true,id=3,label=\"3: Container 2\"]\n" + + " }\n" + + "\n" + + " }\n" + + "\n" + + "}"; + + assertEquals(expectedResult, content); + + // this should be the same + workspace.getModel().addProperty("structurizr.groupSeparator", "/"); + exporter = new DOTExporter(RankDirection.TopBottom, 300, 300); + diagram = exporter.export(view); + + content = diagram.getDefinition(); + assertEquals(expectedResult, content); + } + + @Test + public void test_AmazonWebServicesExample() throws Exception { + Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("src/test/resources/structurizr-54915-workspace.json")); + DOTExporter exporter = new DOTExporter(RankDirection.TopBottom, 300, 300); + Diagram diagram = exporter.export(workspace.getViews().getDeploymentViews().iterator().next()); + + String content = diagram.getDefinition(); + + String expectedResult = "digraph {\n" + + " compound=true\n" + + " graph [splines=polyline,rankdir=LR,ranksep=1.0,nodesep=1.0,fontsize=5]\n" + + " node [shape=box,fontsize=5]\n" + + " edge []\n" + + "\n" + + " subgraph cluster_5 {\n" + + " margin=25\n" + + " subgraph cluster_6 {\n" + + " margin=25\n" + + " subgraph cluster_12 {\n" + + " margin=25\n" + + " subgraph cluster_13 {\n" + + " margin=25\n" + + " 14 [width=1.500000,height=1.000000,fixedsize=true,id=14,label=\"14: Database\"]\n" + + " }\n" + + "\n" + + " }\n" + + "\n" + + " 7 [width=1.500000,height=1.000000,fixedsize=true,id=7,label=\"7: Route 53\"]\n" + + " 8 [width=1.500000,height=1.000000,fixedsize=true,id=8,label=\"8: Elastic Load Balancer\"]\n" + + " subgraph cluster_9 {\n" + + " margin=25\n" + + " subgraph cluster_10 {\n" + + " margin=25\n" + + " 11 [width=1.500000,height=1.000000,fixedsize=true,id=11,label=\"11: Web Application\"]\n" + + " }\n" + + "\n" + + " }\n" + + "\n" + + " }\n" + + "\n" + + " }\n" + + "\n" + + " 11 -> 14 [id=15]\n" + + " 7 -> 8 [id=16]\n" + + " 8 -> 11 [id=17]\n" + + "}"; + + assertEquals(expectedResult, content); + } + +} \ No newline at end of file diff --git a/structurizr-graphviz/src/test/java/com/structurizr/graphviz/GraphvizAutomaticLayoutTests.java b/structurizr-graphviz/src/test/java/com/structurizr/graphviz/GraphvizAutomaticLayoutTests.java new file mode 100644 index 000000000..c92385bb4 --- /dev/null +++ b/structurizr-graphviz/src/test/java/com/structurizr/graphviz/GraphvizAutomaticLayoutTests.java @@ -0,0 +1,49 @@ +package com.structurizr.graphviz; + +import com.structurizr.Workspace; +import com.structurizr.model.Person; +import com.structurizr.model.SoftwareSystem; +import com.structurizr.model.Tags; +import com.structurizr.view.Shape; +import com.structurizr.view.SystemContextView; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.nio.file.Files; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class GraphvizAutomaticLayoutTests { + + @Test + public void test() throws Exception { + Workspace workspace = new Workspace("Name", ""); + Person user = workspace.getModel().addPerson("User"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); + user.uses(softwareSystem, "Uses"); + + SystemContextView view = workspace.getViews().createSystemContextView(softwareSystem, "SystemContext", ""); + view.addAllElements(); + + workspace.getViews().getConfiguration().getStyles().addElementStyle(Tags.PERSON).shape(Shape.Person); + + assertEquals(0, view.getElementView(user).getX()); + assertEquals(0, view.getElementView(user).getY()); + assertEquals(0, view.getElementView(softwareSystem).getX()); + assertEquals(0, view.getElementView(softwareSystem).getY()); + + File tempDir = Files.createTempDirectory("graphviz").toFile(); + GraphvizAutomaticLayout graphviz = new GraphvizAutomaticLayout(tempDir); + graphviz.setRankSeparation(300); + graphviz.setNodeSeparation(300); + graphviz.setMargin(400); + + graphviz.apply(workspace); + + assertEquals(233, view.getElementView(user).getX()); + assertEquals(208, view.getElementView(user).getY()); + assertEquals(208, view.getElementView(softwareSystem).getX()); + assertEquals(908, view.getElementView(softwareSystem).getY()); + } + +} \ No newline at end of file diff --git a/structurizr-graphviz/src/test/java/com/structurizr/graphviz/SVGReaderTests.java b/structurizr-graphviz/src/test/java/com/structurizr/graphviz/SVGReaderTests.java new file mode 100644 index 000000000..0c5ecc86b --- /dev/null +++ b/structurizr-graphviz/src/test/java/com/structurizr/graphviz/SVGReaderTests.java @@ -0,0 +1,66 @@ +package com.structurizr.graphviz; + +import com.structurizr.Workspace; +import com.structurizr.model.Location; +import com.structurizr.model.Person; +import com.structurizr.model.SoftwareSystem; +import com.structurizr.model.Tags; +import com.structurizr.view.PaperSize; +import com.structurizr.view.Shape; +import com.structurizr.view.SystemContextView; +import org.junit.jupiter.api.Test; + +import java.io.File; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class SVGReaderTests { + + private static final File PATH = new File("./src/test/resources/graphviz"); + + @Test + public void test_readView() throws Exception { + Workspace workspace = createWorkspace(); + SystemContextView view = workspace.getViews().getSystemContextViews().iterator().next(); + Person user = workspace.getModel().getPersonWithName("User"); + SoftwareSystem softwareSystem = workspace.getModel().getSoftwareSystemWithName("Software System"); + + assertEquals(0, view.getElementView(user).getX()); + assertEquals(0, view.getElementView(user).getY()); + + assertEquals(0, view.getElementView(softwareSystem).getX()); + assertEquals(0, view.getElementView(softwareSystem).getY()); + + assertNull(view.getPaperSize()); + + SVGReader svgReader = new SVGReader(PATH, 200, true); + svgReader.parseAndApplyLayout(view); + + assertEquals(PaperSize.A6_Portrait, view.getPaperSize()); + + assertEquals(254, view.getElementView(user).getX()); + assertEquals(108, view.getElementView(user).getY()); + + assertEquals(229, view.getElementView(softwareSystem).getX()); + assertEquals(808, view.getElementView(softwareSystem).getY()); + } + + private static Workspace createWorkspace() { + Workspace workspace = new Workspace("Name", ""); + Person user = workspace.getModel().addPerson("User", ""); + user.setLocation(Location.External); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", ""); + softwareSystem.setLocation(Location.Internal); + user.uses(softwareSystem, "Uses"); + + SystemContextView view = workspace.getViews().createSystemContextView(softwareSystem, "SystemContext", ""); + view.addAllElements(); + view.setEnterpriseBoundaryVisible(true); + + workspace.getViews().getConfiguration().getStyles().addElementStyle(Tags.PERSON).shape(Shape.Person); + + return workspace; + } + +} \ No newline at end of file diff --git a/structurizr-graphviz/src/test/resources/graphviz/SystemContext.dot b/structurizr-graphviz/src/test/resources/graphviz/SystemContext.dot new file mode 100644 index 000000000..3766cb71a --- /dev/null +++ b/structurizr-graphviz/src/test/resources/graphviz/SystemContext.dot @@ -0,0 +1,14 @@ +digraph { + graph [splines=polyline,rankdir=TB,ranksep=1.0,nodesep=1.0,fontsize=5] + node [shape=box,fontsize=5] + edge [] + + subgraph cluster_enterprise { + margin=25 + 2 [width=1.500000,height=1.000000,fixedsize=true,id=2,label="2: Software System"] + } + + 1 [width=1.333333,height=1.333333,fixedsize=true,id=1,label="1: User"] + + 1 -> 2 [id=3] +} \ No newline at end of file diff --git a/structurizr-graphviz/src/test/resources/graphviz/SystemContext.dot.svg b/structurizr-graphviz/src/test/resources/graphviz/SystemContext.dot.svg new file mode 100644 index 000000000..216272c76 --- /dev/null +++ b/structurizr-graphviz/src/test/resources/graphviz/SystemContext.dot.svg @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" + "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<!-- Generated by graphviz version 2.42.3 (20191010.1750) + --> +<!-- Title: %3 Pages: 1 --> +<svg width="182pt" height="281pt" + viewBox="0.00 0.00 182.00 281.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 277)"> +<title>%3 + + +cluster_enterprise + + + + +2 + +2: Software System + + + +1 + +1: User + + + +1->2 + + + + + diff --git a/structurizr-graphviz/src/test/resources/structurizr-54915-workspace.json b/structurizr-graphviz/src/test/resources/structurizr-54915-workspace.json new file mode 100644 index 000000000..28e62ac94 --- /dev/null +++ b/structurizr-graphviz/src/test/resources/structurizr-54915-workspace.json @@ -0,0 +1,353 @@ +{ + "id": 54915, + "name": "Amazon Web Services Example", + "description": "An example AWS deployment architecture.", + "model": { + "softwareSystems": [ + { + "id": "1", + "tags": "Element,Software System", + "name": "Spring PetClinic", + "description": "Allows employees to view and manage information regarding the veterinarians, the clients, and their pets.", + "location": "Unspecified", + "containers": [ + { + "id": "3", + "tags": "Element,Container,Database", + "name": "Database", + "description": "Stores information regarding the veterinarians, the clients, and their pets.", + "technology": "Relational database schema" + }, + { + "id": "2", + "tags": "Element,Container,Application", + "name": "Web Application", + "description": "Allows employees to view and manage information regarding the veterinarians, the clients, and their pets.", + "relationships": [ + { + "id": "4", + "tags": "Relationship", + "sourceId": "2", + "destinationId": "3", + "description": "Reads from and writes to", + "technology": "MySQL Protocol/SSL" + } + ], + "technology": "Java and Spring Boot" + } + ], + "documentation": {} + } + ], + "deploymentNodes": [ + { + "id": "5", + "tags": "Element,Deployment Node,Amazon Web Services - Cloud", + "name": "Amazon Web Services", + "environment": "Live", + "instances": 1, + "children": [ + { + "id": "6", + "tags": "Element,Deployment Node,Amazon Web Services - Region", + "name": "US-East-1", + "environment": "Live", + "instances": 1, + "children": [ + { + "id": "12", + "tags": "Element,Deployment Node,Amazon Web Services - RDS", + "name": "Amazon RDS", + "environment": "Live", + "instances": 1, + "children": [ + { + "id": "13", + "tags": "Element,Deployment Node,Amazon Web Services - RDS MySQL instance", + "name": "MySQL", + "environment": "Live", + "instances": 1, + "containerInstances": [ + { + "id": "14", + "tags": "Container Instance", + "environment": "Live", + "deploymentGroups": [ + "Default" + ], + "instanceId": 1, + "containerId": "3" + } + ], + "children": [], + "softwareSystemInstances": [], + "infrastructureNodes": [] + } + ], + "softwareSystemInstances": [], + "containerInstances": [], + "infrastructureNodes": [] + }, + { + "id": "9", + "tags": "Element,Deployment Node,Amazon Web Services - Auto Scaling", + "name": "Autoscaling group", + "environment": "Live", + "instances": 1, + "children": [ + { + "id": "10", + "tags": "Element,Deployment Node,Amazon Web Services - EC2", + "name": "Amazon EC2", + "environment": "Live", + "instances": 1, + "containerInstances": [ + { + "id": "11", + "tags": "Container Instance", + "relationships": [ + { + "id": "15", + "sourceId": "11", + "destinationId": "14", + "description": "Reads from and writes to", + "technology": "MySQL Protocol/SSL", + "linkedRelationshipId": "4" + } + ], + "environment": "Live", + "deploymentGroups": [ + "Default" + ], + "instanceId": 1, + "containerId": "2" + } + ], + "children": [], + "softwareSystemInstances": [], + "infrastructureNodes": [] + } + ], + "softwareSystemInstances": [], + "containerInstances": [], + "infrastructureNodes": [] + } + ], + "infrastructureNodes": [ + { + "id": "8", + "tags": "Element,Infrastructure Node,Amazon Web Services - Elastic Load Balancing", + "name": "Elastic Load Balancer", + "description": "Automatically distributes incoming application traffic.", + "relationships": [ + { + "id": "17", + "tags": "Relationship", + "sourceId": "8", + "destinationId": "11", + "description": "Forwards requests to", + "technology": "HTTPS" + } + ], + "environment": "Live" + }, + { + "id": "7", + "tags": "Element,Infrastructure Node,Amazon Web Services - Route 53", + "name": "Route 53", + "description": "Highly available and scalable cloud DNS service.", + "relationships": [ + { + "id": "16", + "tags": "Relationship", + "sourceId": "7", + "destinationId": "8", + "description": "Forwards requests to", + "technology": "HTTPS" + } + ], + "environment": "Live" + } + ], + "softwareSystemInstances": [], + "containerInstances": [] + } + ], + "softwareSystemInstances": [], + "containerInstances": [], + "infrastructureNodes": [] + } + ], + "customElements": [], + "people": [] + }, + "documentation": { + "sections": [], + "decisions": [], + "images": [] + }, + "views": { + "deploymentViews": [ + { + "softwareSystemId": "1", + "key": "AmazonWebServicesDeployment", + "order": 1, + "paperSize": "A3_Landscape", + "dimensions": { + "width": 3925, + "height": 1816 + }, + "automaticLayout": { + "implementation": "Graphviz", + "rankDirection": "LeftRight", + "rankSeparation": 300, + "nodeSeparation": 300, + "edgeSeparation": 0, + "vertices": false + }, + "environment": "Live", + "animations": [ + { + "order": 1, + "elements": [ + "5", + "6", + "7" + ] + }, + { + "order": 2, + "elements": [ + "8" + ], + "relationships": [ + "16" + ] + }, + { + "order": 3, + "elements": [ + "11", + "9", + "10" + ], + "relationships": [ + "17" + ] + }, + { + "order": 4, + "elements": [ + "12", + "13", + "14" + ], + "relationships": [ + "15" + ] + } + ], + "elements": [ + { + "id": "11", + "x": 1987, + "y": 672 + }, + { + "id": "12", + "x": 175, + "y": 175 + }, + { + "id": "13", + "x": 175, + "y": 175 + }, + { + "id": "14", + "x": 2887, + "y": 672 + }, + { + "id": "5", + "x": 175, + "y": 175 + }, + { + "id": "6", + "x": 175, + "y": 175 + }, + { + "id": "7", + "x": 487, + "y": 672 + }, + { + "id": "8", + "x": 1237, + "y": 672 + }, + { + "id": "9", + "x": 175, + "y": 175 + }, + { + "id": "10", + "x": 175, + "y": 175 + } + ], + "relationships": [ + { + "id": "17" + }, + { + "id": "16" + }, + { + "id": "15" + } + ] + } + ], + "configuration": { + "branding": {}, + "styles": { + "elements": [ + { + "tag": "Element", + "background": "#ffffff", + "shape": "RoundedBox" + }, + { + "tag": "Container", + "background": "#ffffff" + }, + { + "tag": "Application", + "background": "#ffffff" + }, + { + "tag": "Database", + "shape": "Cylinder" + } + ], + "relationships": [] + }, + "themes": [ + "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/theme.json" + ], + "terminology": {}, + "lastSavedView": "AmazonWebServicesDeployment" + }, + "customViews": [], + "systemLandscapeViews": [], + "systemContextViews": [], + "containerViews": [], + "componentViews": [], + "dynamicViews": [], + "filteredViews": [] + } +} \ No newline at end of file diff --git a/structurizr-import/README.md b/structurizr-import/README.md new file mode 100644 index 000000000..210022b6c --- /dev/null +++ b/structurizr-import/README.md @@ -0,0 +1,11 @@ +# structurizr-import + +[![Maven Central](https://img.shields.io/maven-central/v/com.structurizr/structurizr-import.svg?label=Maven%20Central)](https://search.maven.org/artifact/com.structurizr/structurizr-import) + +This library includes a number of classes for importing the following into a Structurizr workspace: + +- Diagrams from PlantUML, Mermaid, and Kroki. +- Supplementary Markdown/AsciiDoc documentation. +- Architecture decision records (ADRs). +- Images (for use in the above). + diff --git a/structurizr-import/build.gradle b/structurizr-import/build.gradle new file mode 100644 index 000000000..bf562441f --- /dev/null +++ b/structurizr-import/build.gradle @@ -0,0 +1,9 @@ +dependencies { + + api project(':structurizr-core') + + testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2' + +} + +description = 'Utilities to import diagrams and documentation into a Structurizr workspace' \ No newline at end of file diff --git a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/AbstractDiagramImporter.java b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/AbstractDiagramImporter.java new file mode 100644 index 000000000..04682876a --- /dev/null +++ b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/AbstractDiagramImporter.java @@ -0,0 +1,33 @@ +package com.structurizr.importer.diagrams; + +import com.structurizr.view.View; +import com.structurizr.view.ViewSet; + +import java.util.HashMap; +import java.util.Map; + +public abstract class AbstractDiagramImporter { + + protected static final Map CONTENT_TYPES_BY_FORMAT = new HashMap<>(); + + protected static final String CONTENT_TYPE_IMAGE_PNG = "image/png"; + protected static final String CONTENT_TYPE_IMAGE_SVG = "image/svg+xml"; + + protected static final String PNG_FORMAT = "png"; + protected static final String SVG_FORMAT = "svg"; + + static { + CONTENT_TYPES_BY_FORMAT.put(PNG_FORMAT, CONTENT_TYPE_IMAGE_PNG); + CONTENT_TYPES_BY_FORMAT.put(SVG_FORMAT, CONTENT_TYPE_IMAGE_SVG); + } + + protected String getViewOrViewSetProperty(View view, String name) { + ViewSet views = view.getViewSet(); + + return + view.getProperties().getOrDefault(name, + views.getConfiguration().getProperties().get(name) + ); + } + +} diff --git a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/kroki/KrokiEncoder.java b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/kroki/KrokiEncoder.java new file mode 100644 index 000000000..22b2c4f38 --- /dev/null +++ b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/kroki/KrokiEncoder.java @@ -0,0 +1,30 @@ +package com.structurizr.importer.diagrams.kroki; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.zip.Deflater; + +/** + * See https://docs.kroki.io/kroki/setup/encode-diagram/#java + */ +class KrokiEncoder { + + public String encode(String decoded) throws IOException { + byte[] bytes = Base64.getUrlEncoder().encode(compress(decoded.getBytes())); + return new String(bytes, StandardCharsets.UTF_8); + } + + private byte[] compress(byte[] source) throws IOException { + Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION); + deflater.setInput(source); + deflater.finish(); + + byte[] buffer = new byte[2048]; + int compressedLength = deflater.deflate(buffer); + byte[] result = new byte[compressedLength]; + System.arraycopy(buffer, 0, result, 0, compressedLength); + return result; + } + +} \ No newline at end of file diff --git a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/kroki/KrokiImporter.java b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/kroki/KrokiImporter.java new file mode 100644 index 000000000..6c01587e4 --- /dev/null +++ b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/kroki/KrokiImporter.java @@ -0,0 +1,45 @@ +package com.structurizr.importer.diagrams.kroki; + +import com.structurizr.importer.diagrams.AbstractDiagramImporter; +import com.structurizr.util.StringUtils; +import com.structurizr.view.ImageView; + +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; + +public class KrokiImporter extends AbstractDiagramImporter { + + private static final String KROKI_URL_PROPERTY = "kroki.url"; + private static final String KROKI_FORMAT_PROPERTY = "kroki.format"; + + public void importDiagram(ImageView view, String format, File file) throws Exception { + String content = new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8); + view.setTitle(file.getName()); + + importDiagram(view, format, content); + } + + public void importDiagram(ImageView view, String format, String content) throws Exception { + String krokiServer = getViewOrViewSetProperty(view, KROKI_URL_PROPERTY); + if (StringUtils.isNullOrEmpty(krokiServer)) { + throw new IllegalArgumentException("Please define a view/viewset property named " + KROKI_URL_PROPERTY + " to specify your Kroki server"); + } + + String imageFormat = getViewOrViewSetProperty(view, KROKI_FORMAT_PROPERTY); + if (StringUtils.isNullOrEmpty(imageFormat)) { + imageFormat = PNG_FORMAT; + } + + if (!imageFormat.equals(PNG_FORMAT) && !imageFormat.equals(SVG_FORMAT)) { + throw new IllegalArgumentException(String.format("Expected a format of %s or %s", PNG_FORMAT, SVG_FORMAT)); + } + + String encodedDiagram = new KrokiEncoder().encode(content); + String url = String.format("%s/%s/%s/%s", krokiServer, format, imageFormat, encodedDiagram); + + view.setContent(url); + view.setContentType(CONTENT_TYPES_BY_FORMAT.get(imageFormat)); + } + +} \ No newline at end of file diff --git a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/mermaid/MermaidEncoder.java b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/mermaid/MermaidEncoder.java new file mode 100644 index 000000000..8bbc4bc91 --- /dev/null +++ b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/mermaid/MermaidEncoder.java @@ -0,0 +1,15 @@ +package com.structurizr.importer.diagrams.mermaid; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +/** + * Encodes a Mermaid diagram definition to base64 format, for use with image URLs, etc. + */ +public class MermaidEncoder { + + public String encode(String mermaidDefinition) { + return Base64.getUrlEncoder().encodeToString(mermaidDefinition.getBytes(StandardCharsets.UTF_8)); + } + +} \ No newline at end of file diff --git a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/mermaid/MermaidImporter.java b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/mermaid/MermaidImporter.java new file mode 100644 index 000000000..9168fff71 --- /dev/null +++ b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/mermaid/MermaidImporter.java @@ -0,0 +1,50 @@ +package com.structurizr.importer.diagrams.mermaid; + +import com.structurizr.importer.diagrams.AbstractDiagramImporter; +import com.structurizr.util.StringUtils; +import com.structurizr.view.ImageView; + +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; + +public class MermaidImporter extends AbstractDiagramImporter { + + private static final String MERMAID_URL_PROPERTY = "mermaid.url"; + private static final String MERMAID_FORMAT_PROPERTY = "mermaid.format"; + + public void importDiagram(ImageView view, File file) throws Exception { + String content = new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8); + view.setTitle(file.getName()); + + importDiagram(view, content); + } + + public void importDiagram(ImageView view, String content) { + String mermaidServer = getViewOrViewSetProperty(view, MERMAID_URL_PROPERTY); + if (StringUtils.isNullOrEmpty(mermaidServer)) { + throw new IllegalArgumentException("Please define a view/viewset property named " + MERMAID_URL_PROPERTY + " to specify your Mermaid server"); + } + + String format = getViewOrViewSetProperty(view, MERMAID_FORMAT_PROPERTY); + if (StringUtils.isNullOrEmpty(format)) { + format = PNG_FORMAT; + } + + if (!format.equals(PNG_FORMAT) && !format.equals(SVG_FORMAT)) { + throw new IllegalArgumentException(String.format("Expected a format of %s or %s", PNG_FORMAT, SVG_FORMAT)); + } + + String encodedMermaid = new MermaidEncoder().encode(content); + String url; + if (format.equals(PNG_FORMAT)) { + url = String.format("%s/img/%s?type=png", mermaidServer, encodedMermaid); + } else { + url = String.format("%s/svg/%s", mermaidServer, encodedMermaid); + } + + view.setContent(url); + view.setContentType(CONTENT_TYPES_BY_FORMAT.get(format)); + } + +} \ No newline at end of file diff --git a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/plantuml/PlantUMLEncoder.java b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/plantuml/PlantUMLEncoder.java new file mode 100644 index 000000000..20238926b --- /dev/null +++ b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/plantuml/PlantUMLEncoder.java @@ -0,0 +1,73 @@ +package com.structurizr.importer.diagrams.plantuml; + +import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; +import java.util.zip.Deflater; +import java.util.zip.DeflaterOutputStream; + +/** + * A Java implementation of http://plantuml.com/code-javascript-synchronous + * that uses Java's built-in Deflate algorithm. + */ +class PlantUMLEncoder { + + String encode(String plantUMLDefinition) throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION, true); + + DeflaterOutputStream dos = new DeflaterOutputStream(baos, deflater, true); + dos.write(plantUMLDefinition.getBytes(StandardCharsets.UTF_8)); + dos.finish(); + + return encode(baos.toByteArray()); + } + + private String encode(byte[] bytes) { + StringBuilder buf = new StringBuilder(); + for (int i = 0; i < bytes.length; i += 3) { + int b1 = (bytes[i]) & 0xFF; + int b2 = (i + 1 < bytes.length ? bytes[i + 1] : (byte)0) & 0xFF; + int b3 = (i + 2 < bytes.length ? bytes[i + 2] : (byte)0) & 0xFF; + + append3bytes(buf, b1, b2, b3); + } + + return buf.toString(); + } + + private char encode6bit(byte b) { + if (b < 10) { + return (char) ('0' + b); + } + b -= 10; + if (b < 26) { + return (char) ('A' + b); + } + b -= 26; + if (b < 26) { + return (char) ('a' + b); + } + b -= 26; + if (b == 0) { + return '-'; + } + if (b == 1) { + return '_'; + } + + return '?'; + } + + private void append3bytes(StringBuilder buf, int b1, int b2, int b3) { + int c1 = b1 >> 2; + int c2 = (b1 & 0x3) << 4 | b2 >> 4; + int c3 = (b2 & 0xF) << 2 | b3 >> 6; + int c4 = b3 & 0x3F; + + buf.append(encode6bit((byte)(c1 & 0x3F))); + buf.append(encode6bit((byte)(c2 & 0x3F))); + buf.append(encode6bit((byte)(c3 & 0x3F))); + buf.append(encode6bit((byte)(c4 & 0x3F))); + } + +} \ No newline at end of file diff --git a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporter.java b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporter.java new file mode 100644 index 000000000..56ed5a835 --- /dev/null +++ b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporter.java @@ -0,0 +1,54 @@ +package com.structurizr.importer.diagrams.plantuml; + +import com.structurizr.importer.diagrams.AbstractDiagramImporter; +import com.structurizr.util.StringUtils; +import com.structurizr.view.ImageView; + +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; + +public class PlantUMLImporter extends AbstractDiagramImporter { + + private static final String PLANTUML_URL_PROPERTY = "plantuml.url"; + private static final String PLANTUML_FORMAT_PROPERTY = "plantuml.format"; + private static final String TITLE_STRING = "title "; + private static final String NEWLINE = "\n"; + + public void importDiagram(ImageView view, File file) throws Exception { + String content = new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8); + view.setTitle(file.getName()); + + importDiagram(view, content); + } + + public void importDiagram(ImageView view, String content) throws Exception { + String plantUMLServer = getViewOrViewSetProperty(view, PLANTUML_URL_PROPERTY); + if (StringUtils.isNullOrEmpty(plantUMLServer)) { + throw new IllegalArgumentException("Please define a view/viewset property named " + PLANTUML_URL_PROPERTY + " to specify your PlantUML server"); + } + + String format = getViewOrViewSetProperty(view, PLANTUML_FORMAT_PROPERTY); + if (StringUtils.isNullOrEmpty(format)) { + format = PNG_FORMAT; + } + + if (!format.equals(PNG_FORMAT) && !format.equals(SVG_FORMAT)) { + throw new IllegalArgumentException(String.format("Expected a format of %s or %s", PNG_FORMAT, SVG_FORMAT)); + } + + String encodedPlantUML = new PlantUMLEncoder().encode(content); + String url = String.format("%s/%s/%s", plantUMLServer, format, encodedPlantUML); + view.setContent(url); + view.setContentType(CONTENT_TYPES_BY_FORMAT.get(format)); + + String[] lines = content.split(NEWLINE); + for (String line : lines) { + if (line.startsWith(TITLE_STRING)) { + String title = line.substring(TITLE_STRING.length()); + view.setTitle(title); + } + } + } + +} \ No newline at end of file diff --git a/structurizr-import/src/main/java/com/structurizr/importer/documentation/AdrToolsDecisionImporter.java b/structurizr-import/src/main/java/com/structurizr/importer/documentation/AdrToolsDecisionImporter.java new file mode 100644 index 000000000..0a14e90e3 --- /dev/null +++ b/structurizr-import/src/main/java/com/structurizr/importer/documentation/AdrToolsDecisionImporter.java @@ -0,0 +1,237 @@ +package com.structurizr.importer.documentation; + +import com.structurizr.documentation.Decision; +import com.structurizr.documentation.Documentable; +import com.structurizr.documentation.Format; +import com.structurizr.util.StringUtils; + +import java.io.File; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Imports architecture decision records created/managed by adr-tools (https://github.com/npryce/adr-tools). + * The format for ADRs is as follows: + * + * Filename: {DECISION_ID:0000}-*.md + * + * Content: + * # {DECISION_ID}. {DECISION_TITLE} + * + * Date: {DECISION_DATE:YYYY-MM-DD} + * + * ## Status + * + * {DECISION_STATUS and links} + * + * ## Context + * ... + */ +public class AdrToolsDecisionImporter implements DocumentationImporter { + + private static final String STATUS_PROPOSED = "Proposed"; + private static final String STATUS_SUPERSEDED = "Superseded"; + private static final String SUPERCEDED_ALTERNATIVE_SPELLING = "Superceded"; + + private static final String DATE_PREFIX = "Date: "; + private static final String STATUS_HEADING = "## Status"; + private static final String CONTEXT_HEADING = "## Context"; + + private static final Pattern LINK_REGEX = Pattern.compile("(.*) \\[.*]\\((.*)\\)"); + + private String dateFormat = "yyyy-MM-dd"; + private TimeZone timeZone = TimeZone.getDefault(); + + /** + * Sets the date format to use when parsing dates (the default is "yyyy-MM-dd"). + * + * @param dateFormat a date format, as a String + */ + public void setDateFormat(String dateFormat) { + this.dateFormat = dateFormat; + } + + /** + * Sets the time zone to use when parsing dates (the default is UTC) + * + * @param timeZone a time zone as a String (e.g. "Europe/London" or "UTC") + */ + public void setTimeZone(String timeZone) { + this.timeZone = TimeZone.getTimeZone(timeZone); + } + + /** + * Sets the time zone to use when parsing dates. + * + * @param timeZone a TimeZone instance + */ + public void setTimeZone(TimeZone timeZone) { + this.timeZone = timeZone; + } + + /** + * Imports Markdown files from the specified path, one per decision. + * + * @param documentable the item that documentation should be associated with + * @param path the path to import documentation from + */ + @Override + public void importDocumentation(Documentable documentable, File path) { + if (documentable == null) { + throw new IllegalArgumentException("A workspace, software system, container, or component must be specified."); + } + + if (path == null) { + throw new IllegalArgumentException("A path must be specified."); + } else if (!path.exists()) { + throw new IllegalArgumentException(path.getAbsolutePath() + " does not exist."); + } + + if (!path.isDirectory()) { + throw new IllegalArgumentException(path.getAbsolutePath() + " is not a directory."); + } + + try { + Map decisionsById = new LinkedHashMap<>(); + + File[] markdownFiles = path.listFiles((dir, name) -> name.endsWith(".md")); + if (markdownFiles != null) { + Map decisionsByFilename = new HashMap<>(); + + for (File file : markdownFiles) { + Decision decision = importDecision(file); + documentable.getDocumentation().addDecision(decision); + + decisionsById.put(decision.getId(), decision); + decisionsByFilename.put(file.getName(), decision); + } + + for (Decision decision : decisionsById.values()) { + extractLinks(decision, decisionsByFilename); + + // and replace file references, for example "0008-some-decision.md" -> "#8" + String content = decision.getContent(); + for (String filename : decisionsByFilename.keySet()) { + content = content.replace(filename, calculateUrl(decisionsByFilename.get(filename))); + } + decision.setContent(content); + } + } + } catch (Exception e) { + throw new DocumentationImportException(e); + } + } + + protected Decision importDecision(File file) throws Exception { + String id = extractIntegerIDFromFileName(file); + Decision decision = new Decision(id); + + String content = new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8); + content = content.replace("\r", ""); + decision.setContent(content); + + String[] lines = content.split("\\n"); + decision.setTitle(extractTitle(lines)); + decision.setDate(extractDate(lines)); + decision.setStatus(extractStatus(lines)); + decision.setFormat(Format.Markdown); + + return decision; + } + + protected String extractIntegerIDFromFileName(File file) { + return "" + Integer.parseInt(file.getName().substring(0, 4)); + } + + protected String extractTitle(String[] lines) { + // the title is assumed to be the first line of the content, in the format: + // # {DECISION_ID}. {DECISION_TITLE} + String titleLine = lines[0]; + + return titleLine.substring(titleLine.indexOf(".") + 2); + } + + protected Date extractDate(String[] lines) throws Exception { + // the date is on a line of its own, in the format: + // Date: {DECISION_DATE:YYYY-MM-DD} + SimpleDateFormat sdf = new SimpleDateFormat(dateFormat); + sdf.setTimeZone(timeZone); + + for (String line : lines) { + if (line.startsWith(DATE_PREFIX)) { + String dateAsString = line.substring(DATE_PREFIX.length()); + + return sdf.parse(dateAsString); + } + } + + return new Date(); + } + + protected String extractStatus(String[] lines) { + // the status is on a line of its own, after the ## Status header: + boolean inStatusSection = false; + for (String line : lines) { + if (!inStatusSection) { + if (line.startsWith(STATUS_HEADING)) { + inStatusSection = true; + } + } else { + if (!StringUtils.isNullOrEmpty(line)) { + String status = line.split(" ")[0]; + // early versions of adr-tools used the alternative spelling + if (SUPERCEDED_ALTERNATIVE_SPELLING.equals(status)) { + status = STATUS_SUPERSEDED; + } + + return status; + } + } + } + + return STATUS_PROPOSED; + } + + protected void extractLinks(Decision decision, Map decisionsByFilename) { + // adr-tools allows users to create arbitrary links between ADRs, which reside inside the ## Status section + String[] lines = decision.getContent().split("\\n"); + boolean inStatusSection = false; + for (String line : lines) { + if (!inStatusSection) { + if (line.startsWith(STATUS_HEADING)) { + inStatusSection = true; + } + } else { + if (line.startsWith(CONTEXT_HEADING)) { + // we're done + return; + } else if (!StringUtils.isNullOrEmpty(line)) { + Matcher matcher = LINK_REGEX.matcher(line); + if (matcher.find()) { + String linkDescription = matcher.group(1); + String markdownFile = matcher.group(2); + + Decision targetDecision = decisionsByFilename.get(markdownFile); + if (targetDecision != null) { + decision.addLink(targetDecision, linkDescription); + } + } + } + } + } + } + + protected String calculateUrl(Decision decision) throws Exception { + return "#" + urlEncode(decision.getId()); + } + + protected String urlEncode(String value) throws Exception { + return URLEncoder.encode(value, StandardCharsets.UTF_8.toString()).replaceAll("\\+", "%20"); + } + +} \ No newline at end of file diff --git a/structurizr-import/src/main/java/com/structurizr/importer/documentation/DefaultDocumentationImporter.java b/structurizr-import/src/main/java/com/structurizr/importer/documentation/DefaultDocumentationImporter.java new file mode 100644 index 000000000..1572a5ccd --- /dev/null +++ b/structurizr-import/src/main/java/com/structurizr/importer/documentation/DefaultDocumentationImporter.java @@ -0,0 +1,81 @@ +package com.structurizr.importer.documentation; + +import com.structurizr.documentation.Documentable; +import com.structurizr.documentation.Format; +import com.structurizr.documentation.Section; + +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.Arrays; + +/** + * This implementation scans a given directory and automatically imports all Markdown or AsciiDoc + * files in that directory. + * + * See https://structurizr.com/help/documentation/headings for details of how section headings and numbering are handled. + */ +public class DefaultDocumentationImporter implements DocumentationImporter { + + /** + * Imports Markdown/AsciiDoc files from the specified path, each in its own section. + * + * @param documentable the item that documentation should be associated with + * @param path the path to import documentation from + */ + @Override + public void importDocumentation(Documentable documentable, File path) { + if (documentable == null) { + throw new IllegalArgumentException("A workspace, software system, container, or component must be specified."); + } + + if (path == null) { + throw new IllegalArgumentException("A path must be specified."); + } else if (!path.exists()) { + throw new IllegalArgumentException(path.getAbsolutePath() + " does not exist."); + } + + try { + if (path.isDirectory()) { + File[] filesInDirectory = path.listFiles(); + if (filesInDirectory != null) { + Arrays.sort(filesInDirectory); + + for (File file : filesInDirectory) { + if (!file.isDirectory() && !file.getName().startsWith(".")) { + importFile(documentable, file); + } + } + } + } else { + importFile(documentable, path); + } + + // now trim the filenames + for (Section section : documentable.getDocumentation().getSections()) { + String filename = section.getFilename(); + + filename = filename.replace(path.getCanonicalPath(), ""); + if (filename.startsWith("/")) { + filename = filename.substring(1); + } + + section.setFilename(filename); + } + } catch (Exception e) { + throw new DocumentationImportException(e); + } + } + + protected void importFile(Documentable documentable, File file) throws Exception { + if (FormatFinder.isMarkdownOrAsciiDoc(file)) { + Format format = FormatFinder.findFormat(file); + String content = new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8); + + Section section = new Section(format, content); + section.setFilename(file.getCanonicalPath()); + documentable.getDocumentation().addSection(section); + } + } + +} \ No newline at end of file diff --git a/structurizr-import/src/main/java/com/structurizr/importer/documentation/DefaultImageImporter.java b/structurizr-import/src/main/java/com/structurizr/importer/documentation/DefaultImageImporter.java new file mode 100644 index 000000000..f1cc2a3cd --- /dev/null +++ b/structurizr-import/src/main/java/com/structurizr/importer/documentation/DefaultImageImporter.java @@ -0,0 +1,91 @@ +package com.structurizr.importer.documentation; + +import com.structurizr.documentation.Documentable; +import com.structurizr.documentation.Image; +import com.structurizr.util.ImageUtils; +import com.structurizr.util.StringUtils; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.Base64; + +/** + * This implementation scans a given directory and automatically imports all Markdown or AsciiDoc + * files in that directory. + * + * - Each file must represent a separate documentation section. + * - The second level heading ("## Section Title" in Markdown and "== Section Title" in AsciiDoc) will be used as the section title. + */ +public class DefaultImageImporter implements DocumentationImporter { + + /** + * Imports one or more png/jpg/jpeg/gif images from the specified path. + * + * @param documentable the item that documentation should be associated with + * @param path the path to import images from + */ + public void importDocumentation(Documentable documentable, File path) { + if (documentable == null) { + throw new IllegalArgumentException("A workspace or software system must be specified."); + } + + if (path == null) { + throw new IllegalArgumentException("A path must be specified."); + } else if (!path.exists()) { + throw new IllegalArgumentException(path.getAbsolutePath() + " does not exist."); + } + + try { + if (path.isDirectory()) { + importImages(documentable, "", path); + } else { + importImage(documentable, "", path); + } + } catch (Exception e) { + throw new DocumentationImportException(e.getMessage(), e); + } + } + + private void importImages(Documentable documentable, String root, File path) throws IOException { + File[] files = path.listFiles(); + if (files != null) { + for (File file : files) { + String name = file.getName().toLowerCase(); + if (file.isDirectory() && !file.isHidden()) { + if (StringUtils.isNullOrEmpty(root)) { + importImages(documentable, file.getName(), file); + } else { + importImages(documentable, root + "/" + file.getName(), file); + } + } else { + if (name.endsWith(".png") || name.endsWith(".jpg") || name.endsWith(".jpeg") || name.endsWith(".gif") || name.endsWith(".svg")) { + importImage(documentable, root, file); + } + } + } + } + } + + private void importImage(Documentable documentable, String path, File file) throws IOException { + String contentType = ImageUtils.getContentType(file); + String base64Content; + + String name; + if (StringUtils.isNullOrEmpty(path)) { + name = file.getName(); + } else { + name = path + "/" + file.getName(); + } + + if (ImageUtils.CONTENT_TYPE_IMAGE_SVG.equalsIgnoreCase(contentType)) { + base64Content = Base64.getEncoder().encodeToString(Files.readAllBytes(file.toPath())); + } else { + contentType = ImageUtils.getContentType(file); + base64Content = ImageUtils.getImageAsBase64(file); + } + + documentable.getDocumentation().addImage(new Image(name, contentType, base64Content)); + } + +} \ No newline at end of file diff --git a/structurizr-import/src/main/java/com/structurizr/importer/documentation/DocumentationImportException.java b/structurizr-import/src/main/java/com/structurizr/importer/documentation/DocumentationImportException.java new file mode 100644 index 000000000..3d068a298 --- /dev/null +++ b/structurizr-import/src/main/java/com/structurizr/importer/documentation/DocumentationImportException.java @@ -0,0 +1,13 @@ +package com.structurizr.importer.documentation; + +public class DocumentationImportException extends RuntimeException { + + public DocumentationImportException(Throwable cause) { + super(cause); + } + + public DocumentationImportException(String message, Throwable cause) { + super(message, cause); + } + +} \ No newline at end of file diff --git a/structurizr-import/src/main/java/com/structurizr/importer/documentation/DocumentationImporter.java b/structurizr-import/src/main/java/com/structurizr/importer/documentation/DocumentationImporter.java new file mode 100644 index 000000000..ee006b3bc --- /dev/null +++ b/structurizr-import/src/main/java/com/structurizr/importer/documentation/DocumentationImporter.java @@ -0,0 +1,20 @@ +package com.structurizr.importer.documentation; + +import com.structurizr.documentation.Documentable; + +import java.io.File; + +/** + * An interface implemented by documentation importers. + */ +public interface DocumentationImporter { + + /** + * Imports documentation from the specified path. + * + * @param documentable the item that documentation should be associated with + * @param path the path to import documentation from + */ + void importDocumentation(Documentable documentable, File path); + +} \ No newline at end of file diff --git a/structurizr-import/src/main/java/com/structurizr/importer/documentation/FormatFinder.java b/structurizr-import/src/main/java/com/structurizr/importer/documentation/FormatFinder.java new file mode 100644 index 000000000..30e7f376c --- /dev/null +++ b/structurizr-import/src/main/java/com/structurizr/importer/documentation/FormatFinder.java @@ -0,0 +1,38 @@ +package com.structurizr.importer.documentation; + +import com.structurizr.documentation.Format; + +import java.io.File; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +public class FormatFinder { + + private static final Set MARKDOWN_EXTENSIONS = new HashSet<>(Arrays.asList(".md", ".markdown", ".text")); + + private static final Set ASCIIDOC_EXTENSIONS = new HashSet<>(Arrays.asList(".asciidoc", ".adoc", ".asc")); + + public static boolean isMarkdownOrAsciiDoc(File file) { + String extension = file.getName().substring(file.getName().lastIndexOf(".")); + + return MARKDOWN_EXTENSIONS.contains(extension) || ASCIIDOC_EXTENSIONS.contains(extension); + } + + public static Format findFormat(File file) { + if (file == null) { + throw new IllegalArgumentException("A file must be specified."); + } + + String extension = file.getName().substring(file.getName().lastIndexOf(".")); + if (MARKDOWN_EXTENSIONS.contains(extension)) { + return Format.Markdown; + } else if (ASCIIDOC_EXTENSIONS.contains(extension)) { + return Format.AsciiDoc; + } else { + // just assume Markdown + return Format.Markdown; + } + } + +} \ No newline at end of file diff --git a/structurizr-import/src/main/java/com/structurizr/importer/documentation/RecursiveDefaultDocumentationImporter.java b/structurizr-import/src/main/java/com/structurizr/importer/documentation/RecursiveDefaultDocumentationImporter.java new file mode 100644 index 000000000..26bfe6880 --- /dev/null +++ b/structurizr-import/src/main/java/com/structurizr/importer/documentation/RecursiveDefaultDocumentationImporter.java @@ -0,0 +1,70 @@ +package com.structurizr.importer.documentation; + +import com.structurizr.documentation.Documentable; +import com.structurizr.documentation.Section; + +import java.io.File; +import java.util.Arrays; + +/** + * This implementation extends the DefaultDocumentationImporter to recursively import documentation. + */ +public class RecursiveDefaultDocumentationImporter extends DefaultDocumentationImporter { + + /** + * Imports Markdown/AsciiDoc files from the specified path, each in its own section. + * + * @param documentable the item that documentation should be associated with + * @param path the path to import documentation from + */ + @Override + public void importDocumentation(Documentable documentable, File path) { + try { + if (documentable == null) { + throw new IllegalArgumentException("A workspace or software system must be specified"); + } + + if (path == null) { + throw new IllegalArgumentException("A path must be specified"); + } else if (!path.exists()) { + throw new IllegalArgumentException(path.getAbsolutePath() + " does not exist"); + } + + if (path.isDirectory()) { + importDirectory(documentable, path); + } else { + importFile(documentable, path); + } + + // now trim the filenames + for (Section section : documentable.getDocumentation().getSections()) { + String filename = section.getFilename(); + + filename = filename.replace(path.getCanonicalPath(), ""); + if (filename.startsWith("/")) { + filename = filename.substring(1); + } + + section.setFilename(filename); + } + } catch (Exception e) { + throw new DocumentationImportException(e); + } + } + + private void importDirectory(Documentable documentable, File path) throws Exception { + File[] filesInDirectory = path.listFiles(); + if (filesInDirectory != null) { + Arrays.sort(filesInDirectory); + + for (File file : filesInDirectory) { + if (!file.isDirectory() && !file.getName().startsWith(".")) { + importFile(documentable, file); + } else if (file.isDirectory()) { + importDirectory(documentable, file); + } + } + } + } + +} \ No newline at end of file diff --git a/structurizr-import/src/test/java/com/structurizr/importer/diagrams/kroki/KrokiEncoderTests.java b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/kroki/KrokiEncoderTests.java new file mode 100644 index 000000000..5d24e00ab --- /dev/null +++ b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/kroki/KrokiEncoderTests.java @@ -0,0 +1,68 @@ +package com.structurizr.importer.diagrams.kroki; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class KrokiEncoderTests { + + @Test + public void encode_plantuml() throws Exception { + assertEquals("eNpzKC5JLCopzc3hcspPUtC1U3DMyUxOVbBSyEjNycnnckjNSwFKAgD4CQzA", + new KrokiEncoder().encode("@startuml\n" + + "Bob -> Alice : hello\n" + + "@enduml")); + } + + @Test + public void encode_seqdiag() throws Exception { + assertEquals("eNorTi1MyUxMV6jmUlBIKsovL04tUlDQtVMoT00CMsuAvOicxKTUHAVbBSV31xAF_WKIBv3isnT9pMTiVDMTpVhroGaEBpD2gqL85NTi4nxk7c75eUDpEoWS1Aogka-QmZuYnoqu2UZXF6HZGslRIAm4MmuuWgA13z1R", + new KrokiEncoder().encode("seqdiag {\n" + + " browser -> webserver [label = \"GET /seqdiag/svg/base64\"];\n" + + " webserver -> processor [label = \"Convert text to image\"];\n" + + " webserver <-- processor;\n" + + " browser <-- webserver;\n" + + "}")); + } + + @Test + public void encode_erd() throws Exception { + assertEquals( + "eNqLDkgtKs7Pi-XSykvMTeXKSM1MzyjhKodQ2kmZRSUZ8Tn5yYklmfl58ZkpXFzRPlAeUAuQn5xZUslVXJJYksqVnF-aV1JUycUFMVJBS1fXUAGmGgCFAiQX", + new KrokiEncoder().encode("[Person]\n" + + "*name\n" + + "height\n" + + "weight\n" + + "+birth_location_id\n" + + "\n" + + "[Location]\n" + + "*id\n" + + "city\n" + + "state\n" + + "country\n" + + "\n" + + "Person *--1 Location")); + } + + @Test + public void encode_graphviz() throws Exception { + assertEquals( + "eNpLyUwvSizIUHBXqPZIzcnJ17ULzy_KSanlAgB1EAjQ", + new KrokiEncoder().encode("digraph G {Hello->World}\n")); + } + + @Test + public void encode_blockdiag() throws Exception { + assertEquals( + "eNpdzDEKQjEQhOHeU4zpPYFoYesRxGJ9bwghMSsbUYJ4d10UCZbDfPynolOek0Q8FsDeNCestoisNLmy-Qg7R3Blcm5hPcr0ITdaB6X15fv-_YdJixo2CNHI2lmK3sPRA__RwV5SzV80ZAegJjXSyfMFptc71w==", + new KrokiEncoder().encode("blockdiag {\n" + + " Kroki -> generates -> \"Block diagrams\";\n" + + " Kroki -> is -> \"very easy!\";\n" + + "\n" + + " Kroki [color = \"greenyellow\"];\n" + + " \"Block diagrams\" [color = \"pink\"];\n" + + " \"very easy!\" [color = \"orange\"];\n" + + "}")); + } + +} \ No newline at end of file diff --git a/structurizr-import/src/test/java/com/structurizr/importer/diagrams/kroki/KrokiImporterTests.java b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/kroki/KrokiImporterTests.java new file mode 100644 index 000000000..cd3b0c6a8 --- /dev/null +++ b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/kroki/KrokiImporterTests.java @@ -0,0 +1,88 @@ +package com.structurizr.importer.diagrams.kroki; + +import com.structurizr.Workspace; +import com.structurizr.view.ImageView; +import org.junit.jupiter.api.Test; + +import java.io.File; + +import static org.junit.jupiter.api.Assertions.*; + +public class KrokiImporterTests { + + @Test + public void importDiagram() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getViews().getConfiguration().addProperty("kroki.url", "https://kroki.io"); + ImageView view = workspace.getViews().createImageView("key"); + + new KrokiImporter().importDiagram(view, "graphviz", new File("./src/test/resources/diagrams/kroki/diagram.dot")); + assertEquals("key", view.getKey()); + assertNull(view.getElement()); + assertNull(view.getElementId()); + assertEquals("diagram.dot", view.getTitle()); + assertEquals("https://kroki.io/graphviz/png/eNpLyUwvSizIUHBXqPZIzcnJ17ULzy_KSanlAgB1EAjQ", view.getContent()); + assertEquals("image/png", view.getContentType()); + } + + @Test + public void importDiagram_AsPNG() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getViews().getConfiguration().addProperty("kroki.url", "https://kroki.io"); + workspace.getViews().getConfiguration().addProperty("kroki.format", "png"); + ImageView view = workspace.getViews().createImageView("key"); + + new KrokiImporter().importDiagram(view, "graphviz", new File("./src/test/resources/diagrams/kroki/diagram.dot")); + assertEquals("key", view.getKey()); + assertNull(view.getElement()); + assertNull(view.getElementId()); + assertEquals("diagram.dot", view.getTitle()); + assertEquals("https://kroki.io/graphviz/png/eNpLyUwvSizIUHBXqPZIzcnJ17ULzy_KSanlAgB1EAjQ", view.getContent()); + assertEquals("image/png", view.getContentType()); + } + + @Test + public void importDiagram_AsSVG() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getViews().getConfiguration().addProperty("kroki.url", "https://kroki.io"); + workspace.getViews().getConfiguration().addProperty("kroki.format", "svg"); + ImageView view = workspace.getViews().createImageView("key"); + + new KrokiImporter().importDiagram(view, "graphviz", new File("./src/test/resources/diagrams/kroki/diagram.dot")); + assertEquals("key", view.getKey()); + assertNull(view.getElement()); + assertNull(view.getElementId()); + assertEquals("diagram.dot", view.getTitle()); + assertEquals("https://kroki.io/graphviz/svg/eNpLyUwvSizIUHBXqPZIzcnJ17ULzy_KSanlAgB1EAjQ", view.getContent()); + assertEquals("image/svg+xml", view.getContentType()); + } + + @Test + public void importDiagram_WhenTheKrokiUrlIsNotDefined() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + ImageView view = workspace.getViews().createImageView("key"); + + try { + new KrokiImporter().importDiagram(view, "graphviz", new File("./src/test/resources/diagrams/kroki/diagram.dot")); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("Please define a view/viewset property named kroki.url to specify your Kroki server", e.getMessage()); + } + } + + @Test + public void importDiagram_WhenAnInvalidFormatIsSpecified() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getViews().getConfiguration().addProperty("kroki.url", "https://mermaid.ink"); + workspace.getViews().getConfiguration().addProperty("kroki.format", "jpg"); + ImageView view = workspace.getViews().createImageView("key"); + + try { + new KrokiImporter().importDiagram(view, "graphviz", "..."); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("Expected a format of png or svg", e.getMessage()); + } + } + +} \ No newline at end of file diff --git a/structurizr-import/src/test/java/com/structurizr/importer/diagrams/mermaid/MermaidEncoderTests.java b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/mermaid/MermaidEncoderTests.java new file mode 100644 index 000000000..b249e45ff --- /dev/null +++ b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/mermaid/MermaidEncoderTests.java @@ -0,0 +1,28 @@ +package com.structurizr.importer.diagrams.mermaid; + +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class MermaidEncoderTests { + + @Test + public void encode_flowchart() throws Exception { + File file = new File("./src/test/resources/diagrams/mermaid/flowchart.mmd"); + String mermaid = Files.readString(file.toPath()); + assertEquals("Zmxvd2NoYXJ0IFRECiAgICBBW0NocmlzdG1hc10gLS0-fEdldCBtb25leXwgQihHbyBzaG9wcGluZykKICAgIEIgLS0-IEN7TGV0IG1lIHRoaW5rfQogICAgQyAtLT58T25lfCBEW0xhcHRvcF0KICAgIEMgLS0-fFR3b3wgRVtpUGhvbmVdCiAgICBDIC0tPnxUaHJlZXwgRltmYTpmYS1jYXIgQ2FyXQ==", new MermaidEncoder().encode(mermaid)); + } + + @Test + public void encode_class() throws Exception { + File file = new File("./src/test/resources/diagrams/mermaid/class.mmd"); + String mermaid = Files.readString(file.toPath()); + assertEquals("Y2xhc3NEaWFncmFtCiAgICBBbmltYWwgPHwtLSBEdWNrCiAgICBBbmltYWwgPHwtLSBGaXNoCiAgICBBbmltYWwgPHwtLSBaZWJyYQogICAgQW5pbWFsIDogK2ludCBhZ2UKICAgIEFuaW1hbCA6ICtTdHJpbmcgZ2VuZGVyCiAgICBBbmltYWw6ICtpc01hbW1hbCgpCiAgICBBbmltYWw6ICttYXRlKCkKICAgIGNsYXNzIER1Y2t7CiAgICAgICtTdHJpbmcgYmVha0NvbG9yCiAgICAgICtzd2ltKCkKICAgICAgK3F1YWNrKCkKICAgIH0KICAgIGNsYXNzIEZpc2h7CiAgICAgIC1pbnQgc2l6ZUluRmVldAogICAgICAtY2FuRWF0KCkKICAgIH0KICAgIGNsYXNzIFplYnJhewogICAgICArYm9vbCBpc193aWxkCiAgICAgICtydW4oKQogICAgfQo=", new MermaidEncoder().encode(mermaid)); + + } + +} \ No newline at end of file diff --git a/structurizr-import/src/test/java/com/structurizr/importer/diagrams/mermaid/MermaidImporterTests.java b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/mermaid/MermaidImporterTests.java new file mode 100644 index 000000000..3cccf9ac8 --- /dev/null +++ b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/mermaid/MermaidImporterTests.java @@ -0,0 +1,88 @@ +package com.structurizr.importer.diagrams.mermaid; + +import com.structurizr.Workspace; +import com.structurizr.view.ImageView; +import org.junit.jupiter.api.Test; + +import java.io.File; + +import static org.junit.jupiter.api.Assertions.*; + +public class MermaidImporterTests { + + @Test + public void importDiagram() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getViews().getConfiguration().addProperty("mermaid.url", "https://mermaid.ink"); + ImageView view = workspace.getViews().createImageView("key"); + + new MermaidImporter().importDiagram(view, new File("./src/test/resources/diagrams/mermaid/flowchart.mmd")); + assertEquals("key", view.getKey()); + assertNull(view.getElement()); + assertNull(view.getElementId()); + assertEquals("flowchart.mmd", view.getTitle()); + assertEquals("https://mermaid.ink/img/Zmxvd2NoYXJ0IFRECiAgICBBW0NocmlzdG1hc10gLS0-fEdldCBtb25leXwgQihHbyBzaG9wcGluZykKICAgIEIgLS0-IEN7TGV0IG1lIHRoaW5rfQogICAgQyAtLT58T25lfCBEW0xhcHRvcF0KICAgIEMgLS0-fFR3b3wgRVtpUGhvbmVdCiAgICBDIC0tPnxUaHJlZXwgRltmYTpmYS1jYXIgQ2FyXQ==?type=png", view.getContent()); + assertEquals("image/png", view.getContentType()); + } + + @Test + public void importDiagram_AsPNG() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getViews().getConfiguration().addProperty("mermaid.url", "https://mermaid.ink"); + workspace.getViews().getConfiguration().addProperty("mermaid.format", "png"); + ImageView view = workspace.getViews().createImageView("key"); + + new MermaidImporter().importDiagram(view, new File("./src/test/resources/diagrams/mermaid/flowchart.mmd")); + assertEquals("key", view.getKey()); + assertNull(view.getElement()); + assertNull(view.getElementId()); + assertEquals("flowchart.mmd", view.getTitle()); + assertEquals("https://mermaid.ink/img/Zmxvd2NoYXJ0IFRECiAgICBBW0NocmlzdG1hc10gLS0-fEdldCBtb25leXwgQihHbyBzaG9wcGluZykKICAgIEIgLS0-IEN7TGV0IG1lIHRoaW5rfQogICAgQyAtLT58T25lfCBEW0xhcHRvcF0KICAgIEMgLS0-fFR3b3wgRVtpUGhvbmVdCiAgICBDIC0tPnxUaHJlZXwgRltmYTpmYS1jYXIgQ2FyXQ==?type=png", view.getContent()); + assertEquals("image/png", view.getContentType()); + } + + @Test + public void importDiagram_AsSVG() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getViews().getConfiguration().addProperty("mermaid.url", "https://mermaid.ink"); + workspace.getViews().getConfiguration().addProperty("mermaid.format", "svg"); + ImageView view = workspace.getViews().createImageView("key"); + + new MermaidImporter().importDiagram(view, new File("./src/test/resources/diagrams/mermaid/flowchart.mmd")); + assertEquals("key", view.getKey()); + assertNull(view.getElement()); + assertNull(view.getElementId()); + assertEquals("flowchart.mmd", view.getTitle()); + assertEquals("https://mermaid.ink/svg/Zmxvd2NoYXJ0IFRECiAgICBBW0NocmlzdG1hc10gLS0-fEdldCBtb25leXwgQihHbyBzaG9wcGluZykKICAgIEIgLS0-IEN7TGV0IG1lIHRoaW5rfQogICAgQyAtLT58T25lfCBEW0xhcHRvcF0KICAgIEMgLS0-fFR3b3wgRVtpUGhvbmVdCiAgICBDIC0tPnxUaHJlZXwgRltmYTpmYS1jYXIgQ2FyXQ==", view.getContent()); + assertEquals("image/svg+xml", view.getContentType()); + } + + @Test + public void importDiagram_WhenTheMermaidUrlIsNotDefined() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + ImageView view = workspace.getViews().createImageView("key"); + + try { + new MermaidImporter().importDiagram(view, new File("./src/test/resources/diagrams/mermaid/flowchart.mmd")); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("Please define a view/viewset property named mermaid.url to specify your Mermaid server", e.getMessage()); + } + } + + @Test + public void importDiagram_WhenAnInvalidFormatIsSpecified() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getViews().getConfiguration().addProperty("mermaid.url", "https://mermaid.ink"); + workspace.getViews().getConfiguration().addProperty("mermaid.format", "jpg"); + ImageView view = workspace.getViews().createImageView("key"); + + try { + new MermaidImporter().importDiagram(view, "..."); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("Expected a format of png or svg", e.getMessage()); + } + } + +} \ No newline at end of file diff --git a/structurizr-import/src/test/java/com/structurizr/importer/diagrams/plantuml/PlantUMLEncoderTests.java b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/plantuml/PlantUMLEncoderTests.java new file mode 100644 index 000000000..867cf0bb2 --- /dev/null +++ b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/plantuml/PlantUMLEncoderTests.java @@ -0,0 +1,20 @@ +package com.structurizr.importer.diagrams.plantuml; + +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class PlantUMLEncoderTests { + + @Test + public void encode() throws Exception { + File file = new File("./src/test/resources/diagrams/plantuml/with-title.puml"); + String mermaid = new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8); + assertEquals("SoWkIImgAStDuIh9BCb9LGXEBInDpKjELKZ9J4mlIinLIAr8p2t8IULooazIqBLJSCp914fQAMIavkJaSpcavgK0zG80", new PlantUMLEncoder().encode(mermaid)); + } + +} \ No newline at end of file diff --git a/structurizr-import/src/test/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporterTests.java b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporterTests.java new file mode 100644 index 000000000..7e51dcd50 --- /dev/null +++ b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporterTests.java @@ -0,0 +1,95 @@ +package com.structurizr.importer.diagrams.plantuml; + +import com.structurizr.Workspace; +import com.structurizr.view.ImageView; +import org.junit.jupiter.api.Test; + +import java.io.File; + +import static org.junit.jupiter.api.Assertions.*; + +public class PlantUMLImporterTests { + + @Test + public void importDiagram_WhenATitleIsDefined() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getViews().getConfiguration().addProperty("plantuml.url", "https://plantuml.com/plantuml"); + ImageView view = workspace.getViews().createImageView("key"); + + new PlantUMLImporter().importDiagram(view, new File("./src/test/resources/diagrams/plantuml/with-title.puml")); + assertEquals("key", view.getKey()); + assertNull(view.getElement()); + assertNull(view.getElementId()); + assertEquals("Sequence diagram example", view.getTitle()); + assertEquals("https://plantuml.com/plantuml/png/SoWkIImgAStDuIh9BCb9LGXEBInDpKjELKZ9J4mlIinLIAr8p2t8IULooazIqBLJSCp914fQAMIavkJaSpcavgK0zG80", view.getContent()); + assertEquals("image/png", view.getContentType()); + } + + @Test + public void importDiagram_WhenATitleIsNotDefined() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getViews().getConfiguration().addProperty("plantuml.url", "https://plantuml.com/plantuml"); + ImageView view = workspace.getViews().createImageView("key"); + + new PlantUMLImporter().importDiagram(view, new File("./src/test/resources/diagrams/plantuml/without-title.puml")); + assertEquals("key", view.getKey()); + assertNull(view.getElement()); + assertNull(view.getElementId()); + assertEquals("without-title.puml", view.getTitle()); + assertEquals("https://plantuml.com/plantuml/png/SoWkIImgAStDuNBAJrBGjLDmpCbCJbMmKiX8pSd9vt98pKi1IW80", view.getContent()); + assertEquals("image/png", view.getContentType()); + } + + @Test + public void importDiagram_AsPNG() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getViews().getConfiguration().addProperty("plantuml.url", "https://plantuml.com/plantuml"); + workspace.getViews().getConfiguration().addProperty("plantuml.format", "png"); + ImageView view = workspace.getViews().createImageView("key"); + + new PlantUMLImporter().importDiagram(view, new File("./src/test/resources/diagrams/plantuml/with-title.puml")); + assertEquals("https://plantuml.com/plantuml/png/SoWkIImgAStDuIh9BCb9LGXEBInDpKjELKZ9J4mlIinLIAr8p2t8IULooazIqBLJSCp914fQAMIavkJaSpcavgK0zG80", view.getContent()); + assertEquals("image/png", view.getContentType()); + } + + @Test + public void importDiagram_AsSVG() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getViews().getConfiguration().addProperty("plantuml.url", "https://plantuml.com/plantuml"); + workspace.getViews().getConfiguration().addProperty("plantuml.format", "svg"); + ImageView view = workspace.getViews().createImageView("key"); + + new PlantUMLImporter().importDiagram(view, new File("./src/test/resources/diagrams/plantuml/with-title.puml")); + assertEquals("https://plantuml.com/plantuml/svg/SoWkIImgAStDuIh9BCb9LGXEBInDpKjELKZ9J4mlIinLIAr8p2t8IULooazIqBLJSCp914fQAMIavkJaSpcavgK0zG80", view.getContent()); + assertEquals("image/svg+xml", view.getContentType()); + } + + @Test + public void importDiagram_WhenThePlantUMLURLIsNotSpecified() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + ImageView view = workspace.getViews().createImageView("key"); + + try { + new PlantUMLImporter().importDiagram(view, new File("./src/test/resources/diagrams/plantuml/with-title.puml")); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("Please define a view/viewset property named plantuml.url to specify your PlantUML server", e.getMessage()); + } + } + + @Test + public void importDiagram_WhenAnInvalidFormatIsSpecified() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getViews().getConfiguration().addProperty("plantuml.url", "https://plantuml.com/plantuml"); + workspace.getViews().getConfiguration().addProperty("plantuml.format", "jpg"); + ImageView view = workspace.getViews().createImageView("key"); + + try { + new PlantUMLImporter().importDiagram(view, "..."); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("Expected a format of png or svg", e.getMessage()); + } + } + +} diff --git a/structurizr-import/src/test/java/com/structurizr/importer/documentation/AdrToolsDecisionImporterTests.java b/structurizr-import/src/test/java/com/structurizr/importer/documentation/AdrToolsDecisionImporterTests.java new file mode 100644 index 000000000..8b31bbbc0 --- /dev/null +++ b/structurizr-import/src/test/java/com/structurizr/importer/documentation/AdrToolsDecisionImporterTests.java @@ -0,0 +1,128 @@ +package com.structurizr.importer.documentation; + +import com.structurizr.Workspace; +import com.structurizr.documentation.Decision; +import com.structurizr.documentation.Documentation; +import com.structurizr.documentation.Format; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.text.SimpleDateFormat; +import java.util.TimeZone; + +import static org.junit.jupiter.api.Assertions.*; + + +public class AdrToolsDecisionImporterTests { + + private AdrToolsDecisionImporter decisionImporter; + private Workspace workspace; + private Documentation documentation; + + @BeforeEach + public void setUp() { + decisionImporter = new AdrToolsDecisionImporter(); + workspace = new Workspace("Name", "Description"); + documentation = workspace.getDocumentation(); + } + + @Test + public void test_importDocumentation_ThrowsAnException_WhenANullDocumentableIsSpecified() { + try { + decisionImporter.importDocumentation(null, null); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("A workspace, software system, container, or component must be specified.", iae.getMessage()); + } + } + + @Test + public void test_importDocumentation_ThrowsAnException_WhenADirectoryIsNotSpecified() { + try { + decisionImporter.importDocumentation(workspace, null); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("A path must be specified.", iae.getMessage()); + } + } + + @Test + public void test_importDocumentation_ThrowsAnException_WhenADirectoryIsSpecifiedButItDoesNotExist() { + try { + File directory = new File("foo"); + assertFalse(directory.exists()); + decisionImporter.importDocumentation(workspace, directory); + fail(); + } catch (IllegalArgumentException iae) { + assertTrue(iae.getMessage().endsWith("foo does not exist.")); + } + } + + @Test + public void test_importDocumentation_ThrowsAnException_WhenAPathIsSpecifiedButItIsNotADirectory() { + try { + decisionImporter.importDocumentation(workspace, new File("build.gradle")); + fail(); + } catch (IllegalArgumentException iae) { + assertTrue(iae.getMessage().endsWith("/build.gradle is not a directory.")); + } + } + + @Test + public void test_importDecisions() { + decisionImporter.importDocumentation(workspace, new File("./src/test/resources/adrs")); + + assertEquals(9, documentation.getDecisions().size()); + + Decision decision1 = documentation.getDecisions().stream().filter(d -> d.getId().equals("1")).findFirst().get(); + assertEquals("1", decision1.getId()); + assertEquals("Record architecture decisions", decision1.getTitle()); + SimpleDateFormat sdf = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss ZZZ"); + sdf.setTimeZone(TimeZone.getTimeZone("UTC")); + assertEquals("12-Feb-2016 00:00:00 +0000", sdf.format(decision1.getDate())); + assertEquals("Accepted", decision1.getStatus()); + Assertions.assertEquals(Format.Markdown, decision1.getFormat()); + assertEquals("# 1. Record architecture decisions\n" + + "\n" + + "Date: 2016-02-12\n" + + "\n" + + "## Status\n" + + "\n" + + "Accepted\n" + + "\n" + + "## Context\n" + + "\n" + + "We need to record the architectural decisions made on this project.\n" + + "\n" + + "## Decision\n" + + "\n" + + "We will use Architecture Decision Records, as described by Michael Nygard in this article: http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions\n" + + "\n" + + "## Consequences\n" + + "\n" + + "See Michael Nygard's article, linked above.\n", + decision1.getContent()); + } + + @Test + public void test_importDocumentation_CapturesLinksBetweenDecisions() { + decisionImporter.importDocumentation(workspace, new File("./src/test/resources/adrs")); + + Decision decision5 = documentation.getDecisions().stream().filter(d -> d.getId().equals("5")).findFirst().get(); + assertEquals(1, decision5.getLinks().size()); + Decision.Link link = decision5.getLinks().iterator().next(); + assertEquals("9", link.getId()); + assertEquals("Amended by", link.getDescription()); + } + + @Test + public void test_importDocumentation_RewritesLinksBetweenDecisions() { + decisionImporter.importDocumentation(workspace, new File("./src/test/resources/adrs")); + + Decision decision5 = documentation.getDecisions().stream().filter(d -> d.getId().equals("5")).findFirst().get(); + assertTrue(decision5.getContent().contains("Amended by [9. Help scripts](#9)")); + } + +} \ No newline at end of file diff --git a/structurizr-import/src/test/java/com/structurizr/importer/documentation/DefaultDocumentImporterTests.java b/structurizr-import/src/test/java/com/structurizr/importer/documentation/DefaultDocumentImporterTests.java new file mode 100644 index 000000000..5b3835ff1 --- /dev/null +++ b/structurizr-import/src/test/java/com/structurizr/importer/documentation/DefaultDocumentImporterTests.java @@ -0,0 +1,81 @@ +package com.structurizr.importer.documentation; + +import com.structurizr.Workspace; +import com.structurizr.documentation.Format; +import com.structurizr.documentation.Section; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.util.Collection; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.*; + +public class DefaultDocumentImporterTests { + + private Workspace workspace; + private DefaultDocumentationImporter documentationImporter; + + @BeforeEach + public void setUp() { + documentationImporter = new DefaultDocumentationImporter(); + workspace = new Workspace("Name", "Description"); + } + + @Test + public void test_importDocumentation_ThrowsAnException_WhenANullDocumentableIsSpecified() { + try { + documentationImporter.importDocumentation(null, null); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("A workspace, software system, container, or component must be specified.", iae.getMessage()); + } + } + + @Test + public void test_importDocumentation_ThrowsAnException_WhenANullPathIsSpecified() { + try { + documentationImporter.importDocumentation(workspace, null); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("A path must be specified.", iae.getMessage()); + } + } + + @Test + public void test_importDocumentation_ThrowsAnException_WhenThePathDoesNotExist() { + try { + File directory = new File("foo"); + assertFalse(directory.exists()); + documentationImporter.importDocumentation(workspace, directory); + fail(); + } catch (IllegalArgumentException iae) { + assertTrue(iae.getMessage().endsWith("foo does not exist.")); + } + } + + @Test + public void test_importDocumentation() { + File directory = new File("./src/test/resources/docs/docs"); + documentationImporter.importDocumentation(workspace, directory); + Collection

    sections = workspace.getDocumentation().getSections(); + assertEquals(6, sections.size()); + + assertSection(Format.Markdown, "## Section 1", 1, "01-section-1.md", sections.stream().filter(s -> s.getOrder() == 1).findFirst().get()); + assertSection(Format.Markdown, "## Section 2", 2, "02-section-2.markdown", sections.stream().filter(s -> s.getOrder() == 2).findFirst().get()); + assertSection(Format.Markdown, "## Section 3", 3, "03-section-3.text", sections.stream().filter(s -> s.getOrder() == 3).findFirst().get()); + assertSection(Format.AsciiDoc, "== Section 4", 4, "04-section-4.adoc", sections.stream().filter(s -> s.getOrder() == 4).findFirst().get()); + assertSection(Format.AsciiDoc, "== Section 5", 5, "05-section-5.asciidoc", sections.stream().filter(s -> s.getOrder() == 5).findFirst().get()); + assertSection(Format.AsciiDoc, "== Section 6", 6, "06-section-6.asc", sections.stream().filter(s -> s.getOrder() == 6).findFirst().get()); + } + + private void assertSection(Format format, String content, int order, String filename, Section section) { + assertTrue(workspace.getDocumentation().getSections().contains(section)); + assertEquals(format, section.getFormat()); + assertEquals(content, section.getContent()); + assertEquals(order, section.getOrder()); + assertEquals(filename, section.getFilename()); + } + +} \ No newline at end of file diff --git a/structurizr-import/src/test/java/com/structurizr/importer/documentation/DefaultImageImporterTests.java b/structurizr-import/src/test/java/com/structurizr/importer/documentation/DefaultImageImporterTests.java new file mode 100644 index 000000000..58324c1b0 --- /dev/null +++ b/structurizr-import/src/test/java/com/structurizr/importer/documentation/DefaultImageImporterTests.java @@ -0,0 +1,181 @@ +package com.structurizr.importer.documentation; + +import com.structurizr.Workspace; +import com.structurizr.documentation.Documentation; +import com.structurizr.documentation.Image; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.*; + +public class DefaultImageImporterTests { + + private Workspace workspace; + private DefaultImageImporter imageImporter; + + @BeforeEach + public void setUp() { + workspace = new Workspace("Name", "Description"); + imageImporter = new DefaultImageImporter(); + } + + @Test + public void test_importDocumentation_ThrowsAnException_WhenTheDocumentableIsNull() { + try { + imageImporter.importDocumentation(null, null); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("A workspace or software system must be specified.", e.getMessage()); + } + } + + @Test + public void test_importDocumentation_ThrowsAnException_WhenThePathIsNull() { + try { + imageImporter.importDocumentation(workspace, null); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("A path must be specified.", iae.getMessage()); + } + } + + @Test + public void test_importDocumentation_ThrowsAnException_WhenThePathDoesNotExist() { + try { + imageImporter.importDocumentation(workspace, new File("./src/test/resources/java/com/structurizr/documentation/foo")); + fail(); + } catch (IllegalArgumentException iae) { + assertTrue(iae.getMessage().endsWith("foo does not exist.")); + } + } + + @Test + public void test_importDocumentation_DoesNothing_WhenThereAreNoImageFilesInThePath() { + File directory = new File("./src/test/resources/docs/images/noimages"); + assertTrue(directory.exists()); + imageImporter.importDocumentation(workspace, directory); + assertTrue(workspace.getDocumentation().getImages().isEmpty()); + } + + @Test + public void test_importDocumentation_ThrowsAnException_WhenTheSpecifiedDirectoryIsNotAnImage() throws IOException { + try { + imageImporter.importDocumentation(workspace, new File("README.md")); + fail(); + } catch (Exception e) { + assertTrue(e.getMessage().endsWith("README.md is not a supported image file.")); + } + } + + @Test + public void test_importDocumentation_AddsAllImages_NonRecursively() { + Documentation documentation = workspace.getDocumentation(); + assertTrue(documentation.getImages().isEmpty()); + + imageImporter.importDocumentation(workspace, new File("./src/test/resources/docs/images/images")); + + Set images = documentation.getImages(); + assertEquals(4, documentation.getImages().size()); + + Image png = documentation.getImages().stream().filter(i -> i.getName().equals("image.png")).findFirst().get(); + assertEquals("image/png", png.getType()); + assertTrue(png.getContent().startsWith("iVBORw0KGgoAAAANSUhEUgAAACAAAAAaCAYAAADWm14/AAAD")); + assertTrue(images.contains(png)); + + Image jpg = documentation.getImages().stream().filter(i -> i.getName().equals("image.jpg")).findFirst().get(); + assertEquals("image/jpeg", jpg.getType()); + assertEquals("/9j/4AAQSkZJRgABAgAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAAaACADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDxzUdRu9Vv5r69nea4mcu7uxJyTnv2GeBT49I1CWNXS1cqwyDkDP5mqLfdP0NeueF9Pt9Q1AR3Kb40h3begJ4HNerhqEal+boebiK0qdra3PNP7D1L/nzf/vpf8afbnVvD17BqEPnWs0UgZJVbHIOccHvjoete8/8ACP6R/wA+EP5V598R9Ot9PttttHsSRUbaOgIcDitZ4anytpszhXqcyTS1PNHVl3KykMMgg8c16n4b1q20+6W5dt8MkWwlCCR05x+Fc78UraC1+IGqx28EcKeYW2xoFGSTk4Hc1x5RSCSoz9KwoV/Zpu17m9ah7RpXtY99/wCEy0j+9N/3x/8AXrhfH+swavEotwf4I0U43Md2ScCvPPLT+4v5V2XwttLa5+IOlRz28UqeaG2yIGGQRg4NVPFrlaUfxFHC2km5bH//2Q==", jpg.getContent()); + assertTrue(images.contains(jpg)); + + Image jpeg = documentation.getImages().stream().filter(i -> i.getName().equals("image.jpeg")).findFirst().get(); + assertEquals("image/jpeg", jpeg.getType()); + assertEquals("/9j/4AAQSkZJRgABAgAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAAaACADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDxzUdRu9Vv5r69nea4mcu7uxJyTnv2GeBT49I1CWNXS1cqwyDkDP5mqLfdP0NeueF9Pt9Q1AR3Kb40h3begJ4HNerhqEal+boebiK0qdra3PNP7D1L/nzf/vpf8afbnVvD17BqEPnWs0UgZJVbHIOccHvjoete8/8ACP6R/wA+EP5V598R9Ot9PttttHsSRUbaOgIcDitZ4anytpszhXqcyTS1PNHVl3KykMMgg8c16n4b1q20+6W5dt8MkWwlCCR05x+Fc78UraC1+IGqx28EcKeYW2xoFGSTk4Hc1x5RSCSoz9KwoV/Zpu17m9ah7RpXtY99/wCEy0j+9N/3x/8AXrhfH+swavEotwf4I0U43Md2ScCvPPLT+4v5V2XwttLa5+IOlRz28UqeaG2yIGGQRg4NVPFrlaUfxFHC2km5bH//2Q==", jpeg.getContent()); + assertTrue(images.contains(jpeg)); + + Image gif = documentation.getImages().stream().filter(i -> i.getName().equals("image.gif")).findFirst().get(); + assertEquals("image/gif", gif.getType()); + assertEquals("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", gif.getContent()); + assertTrue(images.contains(gif)); + } + + @Test + public void test_importDocumentation_AddsAllImages_Recursively() { + Documentation documentation = workspace.getDocumentation(); + assertTrue(documentation.getImages().isEmpty()); + + imageImporter.importDocumentation(workspace, new File("./src/test/resources/docs/images")); + assertEquals(9, documentation.getImages().size()); + + Image pngInDirectory = documentation.getImages().stream().filter(i -> i.getName().equals("image.png")).findFirst().get(); + assertEquals("image/png", pngInDirectory.getType()); + assertTrue(pngInDirectory.getContent().startsWith("iVBORw0KGgoAAAANSUhEUgAAACAAAAAaCAYAAADWm14/AAAD")); + + Image jpgInDirectory = documentation.getImages().stream().filter(i -> i.getName().equals("image.jpg")).findFirst().get(); + assertEquals("image/jpeg", jpgInDirectory.getType()); + assertEquals("/9j/4AAQSkZJRgABAgAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAAaACADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDxzUdRu9Vv5r69nea4mcu7uxJyTnv2GeBT49I1CWNXS1cqwyDkDP5mqLfdP0NeueF9Pt9Q1AR3Kb40h3begJ4HNerhqEal+boebiK0qdra3PNP7D1L/nzf/vpf8afbnVvD17BqEPnWs0UgZJVbHIOccHvjoete8/8ACP6R/wA+EP5V598R9Ot9PttttHsSRUbaOgIcDitZ4anytpszhXqcyTS1PNHVl3KykMMgg8c16n4b1q20+6W5dt8MkWwlCCR05x+Fc78UraC1+IGqx28EcKeYW2xoFGSTk4Hc1x5RSCSoz9KwoV/Zpu17m9ah7RpXtY99/wCEy0j+9N/3x/8AXrhfH+swavEotwf4I0U43Md2ScCvPPLT+4v5V2XwttLa5+IOlRz28UqeaG2yIGGQRg4NVPFrlaUfxFHC2km5bH//2Q==", jpgInDirectory.getContent()); + + Image jpegInDirectory = documentation.getImages().stream().filter(i -> i.getName().equals("image.jpeg")).findFirst().get(); + assertEquals("image/jpeg", jpegInDirectory.getType()); + assertEquals("/9j/4AAQSkZJRgABAgAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAAaACADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDxzUdRu9Vv5r69nea4mcu7uxJyTnv2GeBT49I1CWNXS1cqwyDkDP5mqLfdP0NeueF9Pt9Q1AR3Kb40h3begJ4HNerhqEal+boebiK0qdra3PNP7D1L/nzf/vpf8afbnVvD17BqEPnWs0UgZJVbHIOccHvjoete8/8ACP6R/wA+EP5V598R9Ot9PttttHsSRUbaOgIcDitZ4anytpszhXqcyTS1PNHVl3KykMMgg8c16n4b1q20+6W5dt8MkWwlCCR05x+Fc78UraC1+IGqx28EcKeYW2xoFGSTk4Hc1x5RSCSoz9KwoV/Zpu17m9ah7RpXtY99/wCEy0j+9N/3x/8AXrhfH+swavEotwf4I0U43Md2ScCvPPLT+4v5V2XwttLa5+IOlRz28UqeaG2yIGGQRg4NVPFrlaUfxFHC2km5bH//2Q==", jpegInDirectory.getContent()); + + Image gifInDirectory = documentation.getImages().stream().filter(i -> i.getName().equals("image.gif")).findFirst().get(); + assertEquals("image/gif", gifInDirectory.getType()); + assertEquals("R0lGODlhIAAaAPcAAAAAAAACCwAFHAAGFAAGIwAIFgAKHAAKJgAMKgAOMwAPPAARHwAUOQ0UHQIVMgMVJQMVLAoVKwwVJBEVHgAYOAQYJwkYORAYJQIZKwoZJQMaMgoaKwobMxQcKQsjRwMnVxcoPBsoOAAtWx8vQAAwXwIzaQ9HehZLhEVMWRRNjB5Ng0VNYUtQWkFRXhZShBVTjRRVkxlVjhtVlBtWmQpXoxFXmxZYmRFZpBhZjxpZlRValRlamAdcrgxcqg1cpCFdmw1esRReqhleqx9eoyhenhNgrAxitBlirxNktCZknw5luxllsyJlrBJmuhhmuCNnsCVnpBRptRRpvBxpryNprhVqwhtrvBxrtSJrtRxtwCVuuyFytCZ0xid0uit0wCV4xi97xDh9xDGAyzuAy0KBy0SCw0iDw0aG0EuGyjuI2EuM1EiN2EOO2EaP1USR26SipZ+kqKSmsqamrGGq76SquG+w9Gy0/Im05nK183m1+Xa2+YS27YW28YS3+Iq38Iu37HO59Iq57HS6/oq683277IS79IW77ZG77YS8+3u9/Iu++Y7A9X7B/4PB8oLC/ozC/PX3///39f///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAIAAaAEcI/wAlxUEhYQMGCAghaMDw4KCDCBAwVEgIYYNCDRxWxIGU0AFCDAhOMFnSREkVIEB69PDh48YNlTyMKGly5QSBCw4cYDgI4QECGFaaINkCqFCgQkiNDkKEaNAeRWaCwCDQQE6AAhY1IKTg4AABAgcOCPhKloCAs2QdGAggZ+dChg8yQNyw4cEDCRIiRMhQAYOGvxYrPNhwAAKHnQnjHnhxRQqSK4mQGkpaaJBlp4jOBJFxQILWhoMlIJBxJCgSMHWYIkK6FFEiRoLYBNGSojOLASNAa+XwFrHHiBhy5oQQwe6GEANaIOSQUIMDBSVO5MgBQ8aMGTJkxMg+w4YNGDhckP9g0BMxwsEHYlxBIsWIGDuMVDNVNAhpIjc9rMw4EGFi4gw/GVEFEl7o8cgdhkxGmWSIoCEEDAg8MBxihMEghRJSXCEIInwkZUggRxUyGSJhIAHDARlA4NFgGKQoQhJUXHGFFUgEYeONQQghRBBLWPEEER9AUFUAA1j0gIq/AafiRzwlttMDAgxAR4pHPpkQQ31RtJNwHvWXgXFKfrTABg4gABYCaKYZFpoJJFAmBAQgoFVxCGnlnAMlDIEFe1b02ScWgGJhRRZ9RjEFFCLE1dFHCLywpxRN8MBFGmyw0UYbamS6hhtfCOhECgRIYF5PGSx2RRRR9DCHI5FR5scfgUy/pgcWS8hAgEFNKqZDFFIA0QUehfyRIGWWjUjGZhH6RxwEANYgxYBa5LHHIcOKmBQhjRSCRg0/3GrBik/+FJQRv1ZGGYhMDbJIIWUUYUNnCBwmGHpASSGFqo/04SGIhazmiCB77nDARFrBJQEBIi2BBBI0YBFGGRBHTAYZY2jRwxJMqCBABysIAAKTwYlgwgsvwADDDtmZXF12271wgggeGMBCJG+gcJGdGGyQwc4ZNIAXzxlIMEEDO9OFAhySBAQAOw==", gifInDirectory.getContent()); + + Image svgInDirectory = documentation.getImages().stream().filter(i -> i.getName().equals("image.svg")).findFirst().get(); + assertEquals("image/svg+xml", svgInDirectory.getType()); + assertEquals("PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAiIGhlaWdodD0iMTAwIj4KICA8Y2lyY2xlIGN4PSI1MCIgY3k9IjUwIiByPSI0MCIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLXdpZHRoPSIzIiBmaWxsPSJyZWQiIC8+Cjwvc3ZnPiA=", svgInDirectory.getContent()); + + Image pngInSubDirectory = documentation.getImages().stream().filter(i -> i.getName().equals("images/image.png")).findFirst().get(); + assertEquals("image/png", pngInSubDirectory.getType()); + assertTrue(pngInSubDirectory.getContent().startsWith("iVBORw0KGgoAAAANSUhEUgAAACAAAAAaCAYAAADWm14/AAAD")); + + Image jpgInSubDirectory = documentation.getImages().stream().filter(i -> i.getName().equals("images/image.jpg")).findFirst().get(); + assertEquals("image/jpeg", jpgInSubDirectory.getType()); + assertEquals("/9j/4AAQSkZJRgABAgAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAAaACADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDxzUdRu9Vv5r69nea4mcu7uxJyTnv2GeBT49I1CWNXS1cqwyDkDP5mqLfdP0NeueF9Pt9Q1AR3Kb40h3begJ4HNerhqEal+boebiK0qdra3PNP7D1L/nzf/vpf8afbnVvD17BqEPnWs0UgZJVbHIOccHvjoete8/8ACP6R/wA+EP5V598R9Ot9PttttHsSRUbaOgIcDitZ4anytpszhXqcyTS1PNHVl3KykMMgg8c16n4b1q20+6W5dt8MkWwlCCR05x+Fc78UraC1+IGqx28EcKeYW2xoFGSTk4Hc1x5RSCSoz9KwoV/Zpu17m9ah7RpXtY99/wCEy0j+9N/3x/8AXrhfH+swavEotwf4I0U43Md2ScCvPPLT+4v5V2XwttLa5+IOlRz28UqeaG2yIGGQRg4NVPFrlaUfxFHC2km5bH//2Q==", jpgInSubDirectory.getContent()); + + Image jpegInSubDirectory = documentation.getImages().stream().filter(i -> i.getName().equals("images/image.jpeg")).findFirst().get(); + assertEquals("image/jpeg", jpegInSubDirectory.getType()); + assertEquals("/9j/4AAQSkZJRgABAgAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAAaACADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDxzUdRu9Vv5r69nea4mcu7uxJyTnv2GeBT49I1CWNXS1cqwyDkDP5mqLfdP0NeueF9Pt9Q1AR3Kb40h3begJ4HNerhqEal+boebiK0qdra3PNP7D1L/nzf/vpf8afbnVvD17BqEPnWs0UgZJVbHIOccHvjoete8/8ACP6R/wA+EP5V598R9Ot9PttttHsSRUbaOgIcDitZ4anytpszhXqcyTS1PNHVl3KykMMgg8c16n4b1q20+6W5dt8MkWwlCCR05x+Fc78UraC1+IGqx28EcKeYW2xoFGSTk4Hc1x5RSCSoz9KwoV/Zpu17m9ah7RpXtY99/wCEy0j+9N/3x/8AXrhfH+swavEotwf4I0U43Md2ScCvPPLT+4v5V2XwttLa5+IOlRz28UqeaG2yIGGQRg4NVPFrlaUfxFHC2km5bH//2Q==", jpegInSubDirectory.getContent()); + + Image gifInSubDirectory = documentation.getImages().stream().filter(i -> i.getName().equals("images/image.gif")).findFirst().get(); + assertEquals("image/gif", gifInSubDirectory.getType()); + assertEquals("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", gifInSubDirectory.getContent()); + } + + @Test + public void test_importDocumentation_AddsASingleImage() { + Documentation documentation = workspace.getDocumentation(); + assertTrue(documentation.getImages().isEmpty()); + + imageImporter.importDocumentation(workspace, new File("./src/test/resources/docs/images/image.png")); + assertEquals(1, documentation.getImages().size()); + + Image png = documentation.getImages().stream().filter(i -> i.getName().equals("image.png")).findFirst().get(); + assertEquals("image/png", png.getType()); + assertTrue(png.getContent().startsWith("iVBORw0KGgoAAAANSUhEUgAAACAAAAAaCAYAAADWm14/AAAD")); + } + + @Test + public void test_importDocumentation_IgnoresHiddenFolders() throws Exception { + Documentation documentation = workspace.getDocumentation(); + + File tempDirectory = Files.createTempDirectory("test").toFile(); + File hiddenFolder = new File(tempDirectory, ".structurizr"); + hiddenFolder.mkdir(); + + File source = new File("./src/test/resources/docs/images/images/image.png"); + File destination = new File(hiddenFolder, "image.png"); + Files.copy(source.toPath(), destination.toPath()); + assertTrue(destination.exists()); + + imageImporter.importDocumentation(workspace, tempDirectory); + assertEquals(0, documentation.getImages().size()); + } + +} \ No newline at end of file diff --git a/structurizr-import/src/test/java/com/structurizr/importer/documentation/FormatFinderTests.java b/structurizr-import/src/test/java/com/structurizr/importer/documentation/FormatFinderTests.java new file mode 100644 index 000000000..14c530a71 --- /dev/null +++ b/structurizr-import/src/test/java/com/structurizr/importer/documentation/FormatFinderTests.java @@ -0,0 +1,38 @@ +package com.structurizr.importer.documentation; + +import com.structurizr.documentation.Format; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.io.File; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +public class FormatFinderTests { + + @Test + public void test_findFormat_ThrowsAnException_WhenAFileIsNotSpecified() { + try { + FormatFinder.findFormat(null); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("A file must be specified.", iae.getMessage()); + } + } + + @Test + public void test_findFormat_ReturnsMarkdown_WhenAMarkdownFileIsSpecified() { + Assertions.assertEquals(Format.Markdown, FormatFinder.findFormat(new File("foo.md"))); + assertEquals(Format.Markdown, FormatFinder.findFormat(new File("foo.markdown"))); + assertEquals(Format.Markdown, FormatFinder.findFormat(new File("foo.text"))); + } + + @Test + public void test_findFormat_ReturnsAsciiDoc_WhenAnAsciiDocFileIsSpecified() { + assertEquals(Format.AsciiDoc, FormatFinder.findFormat(new File("foo.adoc"))); + assertEquals(Format.AsciiDoc, FormatFinder.findFormat(new File("foo.asciidoc"))); + assertEquals(Format.AsciiDoc, FormatFinder.findFormat(new File("foo.asc"))); + } + +} diff --git a/structurizr-import/src/test/java/com/structurizr/importer/documentation/RecursiveDefaultDocumentImporterTests.java b/structurizr-import/src/test/java/com/structurizr/importer/documentation/RecursiveDefaultDocumentImporterTests.java new file mode 100644 index 000000000..76cc55dca --- /dev/null +++ b/structurizr-import/src/test/java/com/structurizr/importer/documentation/RecursiveDefaultDocumentImporterTests.java @@ -0,0 +1,51 @@ +package com.structurizr.importer.documentation; + +import com.structurizr.Workspace; +import com.structurizr.documentation.Format; +import com.structurizr.documentation.Section; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.util.Collection; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class RecursiveDefaultDocumentImporterTests { + + private Workspace workspace; + private RecursiveDefaultDocumentationImporter documentationImporter; + + @BeforeEach + public void setUp() { + documentationImporter = new RecursiveDefaultDocumentationImporter(); + workspace = new Workspace("Name", "Description"); + } + + @Test + public void test_importDocumentation_WithRecursiveSetToTrue() { + File directory = new File("./src/test/resources/docs/docs"); + + documentationImporter.importDocumentation(workspace, directory); + Collection
    sections = workspace.getDocumentation().getSections(); + assertEquals(7, sections.size()); + + assertSection(Format.Markdown, "## Section 1", 1, "01-section-1.md", sections.stream().filter(s -> s.getOrder() == 1).findFirst().get()); + assertSection(Format.Markdown, "## Section 2", 2, "02-section-2.markdown", sections.stream().filter(s -> s.getOrder() == 2).findFirst().get()); + assertSection(Format.Markdown, "## Section 3", 3, "03-section-3.text", sections.stream().filter(s -> s.getOrder() == 3).findFirst().get()); + assertSection(Format.AsciiDoc, "== Section 4", 4, "04-section-4.adoc", sections.stream().filter(s -> s.getOrder() == 4).findFirst().get()); + assertSection(Format.AsciiDoc, "== Section 5", 5, "05-section-5.asciidoc", sections.stream().filter(s -> s.getOrder() == 5).findFirst().get()); + assertSection(Format.AsciiDoc, "== Section 6", 6, "06-section-6.asc", sections.stream().filter(s -> s.getOrder() == 6).findFirst().get()); + assertSection(Format.Markdown, "## Section 7", 7, "07-subdirectory/01-section-1.md", sections.stream().filter(s -> s.getOrder() == 7).findFirst().get()); + } + + private void assertSection(Format format, String content, int order, String filename, Section section) { + assertTrue(workspace.getDocumentation().getSections().contains(section)); + assertEquals(format, section.getFormat()); + assertEquals(content, section.getContent()); + assertEquals(order, section.getOrder()); + assertEquals(filename, section.getFilename()); + } + +} \ No newline at end of file diff --git a/structurizr-import/src/test/resources/adrs/0001-record-architecture-decisions.md b/structurizr-import/src/test/resources/adrs/0001-record-architecture-decisions.md new file mode 100644 index 000000000..f30860000 --- /dev/null +++ b/structurizr-import/src/test/resources/adrs/0001-record-architecture-decisions.md @@ -0,0 +1,19 @@ +# 1. Record architecture decisions + +Date: 2016-02-12 + +## Status + +Accepted + +## Context + +We need to record the architectural decisions made on this project. + +## Decision + +We will use Architecture Decision Records, as described by Michael Nygard in this article: http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions + +## Consequences + +See Michael Nygard's article, linked above. diff --git a/structurizr-import/src/test/resources/adrs/0002-implement-as-shell-scripts.md b/structurizr-import/src/test/resources/adrs/0002-implement-as-shell-scripts.md new file mode 100644 index 000000000..8e6ea15e6 --- /dev/null +++ b/structurizr-import/src/test/resources/adrs/0002-implement-as-shell-scripts.md @@ -0,0 +1,28 @@ +# 2. Implement as shell scripts + +Date: 2016-02-12 + +## Status + +Accepted + +## Context + +ADRs are plain text files stored in a subdirectory of the project. + +The tool needs to create new files and apply small edits to +the Status section of existing files. + +## Decision + +The tool is implemented as shell scripts that use standard Unix +tools -- grep, sed, awk, etc. + +## Consequences + +The tool won't support Windows. Being plain text files, ADRs can +be created by hand and edited in any text editor. This tool just +makes the process more convenient. + +Development will have to cope with differences between Unix +variants, particularly Linux and MacOS X. diff --git a/structurizr-import/src/test/resources/adrs/0003-single-command-with-subcommands.md b/structurizr-import/src/test/resources/adrs/0003-single-command-with-subcommands.md new file mode 100644 index 000000000..f64db8da1 --- /dev/null +++ b/structurizr-import/src/test/resources/adrs/0003-single-command-with-subcommands.md @@ -0,0 +1,45 @@ +# 3. Single command with subcommands + +Date: 2016-02-12 + +## Status + +Accepted + +## Context + +The tool provides a number of related commands to create +and manipulate architecture decision records. + +How can the user find out about the commands that are available? + +## Decision + +The tool defines a single command, called `adr`. + +The first argument to `adr` (the subcommand) specifies the +action to perform. Further arguments are interpreted by the +subcommand. + +Running `adr` without any arguments lists the available +subcommands. + +Subcommands are implemented as scripts in the same +directory as the `adr` script. E.g. the subcommand `new` is +implemented as the script `adr-new`, the subcommand `help` +as the script `adr-help` and so on. + +Helper scripts that are part of the implementation but not +subcommands follow a different naming convention, so that +subcommands can be listed by filtering and transforming script +file names. + +## Consequences + +Users can more easily explore the capabilities of the tool. + +Users are already used to this style of command-line tool. For +example, Git works this way. + +Each subcommand can be implemented in the most appropriate +language. diff --git a/structurizr-import/src/test/resources/adrs/0004-markdown-format.md b/structurizr-import/src/test/resources/adrs/0004-markdown-format.md new file mode 100644 index 000000000..160d1689f --- /dev/null +++ b/structurizr-import/src/test/resources/adrs/0004-markdown-format.md @@ -0,0 +1,40 @@ +# 4. Markdown format + +Date: 2016-02-12 + +## Status + +Accepted + +## Context + +The decision records must be stored in a plain text format: + +* This works well with version control systems. + +* It allows the tool to modify the status of records and insert + hyperlinks when one decision supercedes another. + +* Decisions can be read in the terminal, IDE, version control + browser, etc. + +People will want to use some formatting: lists, code examples, +and so on. + +People will want to view the decision records in a more readable +format than plain text, and maybe print them out. + + +## Decision + +Record architecture decisions in [Markdown format](https://daringfireball.net/projects/markdown/). + +## Consequences + +Decisions can be read in the terminal. + +Decisions will be formatted nicely and hyperlinked by the +browsers of project hosting sites like GitHub and Bitbucket. + +Tools like [Pandoc](http://pandoc.org/) can be used to convert +the decision records into HTML or PDF. diff --git a/structurizr-import/src/test/resources/adrs/0005-help-comments.md b/structurizr-import/src/test/resources/adrs/0005-help-comments.md new file mode 100644 index 000000000..b19bf0fb1 --- /dev/null +++ b/structurizr-import/src/test/resources/adrs/0005-help-comments.md @@ -0,0 +1,42 @@ +# 5. Help comments + +Date: 2016-02-13 + +## Status + +Accepted + +Amended by [9. Help scripts](0009-help-scripts.md) + +## Context + +The tool will have a `help` subcommand to provide documentation +for users. + +It's nice to have usage documentation in the script files +themselves, in comments. When reading the code, that's the first +place to look for information about how to run a script. + +## Decision + +Write usage documentation in comments in the source file. + +Distinguish between documentation comments and normal comments. +Documentation comments have two hash characters at the start of +the line. + +The `adr help` command can parse comments out from the script +using the standard Unix tools `grep` and `cut`. + +## Consequences + +No need to maintain help text in a separate file. + +Help text can easily be kept up to date as the script is edited. + +There's no automated check that the help text is up to date. The +tests do not work well as documentation for users, and the help +text is not easily cross-checked against the code. + +This won't work if any subcommands are not implemented as scripts +that use '#' as a comment character. diff --git a/structurizr-import/src/test/resources/adrs/0006-packaging-and-distribution-in-other-version-control-repositories.md b/structurizr-import/src/test/resources/adrs/0006-packaging-and-distribution-in-other-version-control-repositories.md new file mode 100644 index 000000000..4a3485f79 --- /dev/null +++ b/structurizr-import/src/test/resources/adrs/0006-packaging-and-distribution-in-other-version-control-repositories.md @@ -0,0 +1,41 @@ +# 6. Packaging and distribution in other version control repositories + +Date: 2016-02-16 + +## Status + +Accepted + +## Context + +Users want to install adr-tools with their preferred package +manager. For example, Ubuntu users use `apt`, RedHat users use +`yum` and Mac OS X users use [Homebrew](http://brew.sh). + +The developers of `adr-tools` don't know how, nor have permissions, +to use all these packaging and distribution systems. Therefore packaging +and distribution must be done by "downstream" parties. + +The developers of the tool should not favour any one particular +packaging and distribution solution. + +## Decision + +The `adr-tools` project will not contain any packaging or +distribution scripts and config. + +Packaging and distribution will be managed by other projects in +separate version control repositories. + +## Consequences + +The git repo of this project will be simpler. + +Eventually, users will not have to use Git to get the software. + +We will have to tag releases in the `adr-tools` repository so that +packaging projects know what can be published and how it should be +identified. + +We will document how users can install the software in this +project's README file. diff --git a/structurizr-import/src/test/resources/adrs/0007-invoke-adr-config-executable-to-get-configuration.md b/structurizr-import/src/test/resources/adrs/0007-invoke-adr-config-executable-to-get-configuration.md new file mode 100644 index 000000000..a649b2356 --- /dev/null +++ b/structurizr-import/src/test/resources/adrs/0007-invoke-adr-config-executable-to-get-configuration.md @@ -0,0 +1,31 @@ +# 7. Invoke adr-config executable to get configuration + +Date: 2016-12-17 + +## Status + +Accepted + +## Context + +Packagers (e.g. Homebrew developers) want to configure adr-tools to match the conventions of their installation. + +Currently, this is done by sourcing a file `config.sh`, which should sit beside the `adr` executable. + +This name is too common. + +The `config.sh` file is not executable, and so doesn't belong in a bin directory. + +## Decision + +Replace `config.sh` with an executable, named `adr-config` that outputs configuration. + +Each script in ADR Tools will eval the output of `adr-config` to configure itself. + +## Consequences + +Configuration within ADR Tools is a little more complicated. + +Packagers can write their own implementation of `adr-config` that outputs configuration that matches the platform's installation conventions, and deploy it next to the `adr` script. + +To make development easier, the implementation of `adr-config` in the project's src/ directory will output configuration that lets the tool to run from the src/ directory without any installation step. (Packagers should not include this script in a deployable package.) diff --git a/structurizr-import/src/test/resources/adrs/0008-use-iso-8601-format-for-dates.md b/structurizr-import/src/test/resources/adrs/0008-use-iso-8601-format-for-dates.md new file mode 100644 index 000000000..4146f11df --- /dev/null +++ b/structurizr-import/src/test/resources/adrs/0008-use-iso-8601-format-for-dates.md @@ -0,0 +1,43 @@ +# 8. Use ISO 8601 Format for Dates + +Date: 2017-02-21 + +## Status + +Accepted + +## Context + +`adr-tools` seeks to communicate the history of architectural decisions of a +project. An important component of the history is the time at which a decision +was made. + +To communicate effectively, `adr-tools` should present information as +unambiguously as possible. That means that culture-neutral data formats should +be preferred over culture-specific formats. + +Existing `adr-tools` deployments format dates as `dd/mm/yyyy` by default. That +formatting is common formatting in the United Kingdom (where the `adr-tools` +project was originally written), but is easily confused with the `mm/dd/yyyy` +format preferred in the United States. + +The default date format may be overridden by setting `ADR_DATE` in `config.sh`. + +## Decision + +`adr-tools` will use the ISO 8601 format for dates: `yyyy-mm-dd` + +## Consequences + +Dates are displayed in a standard, culture-neutral format. + +The UK-style and ISO 8601 formats can be distinguished by their separator +character. The UK-style dates used a slash (`/`), while the ISO dates use a +hyphen (`-`). + +Prior to this decision, `adr-tools` was deployed using the UK format for dates. +After adopting the ISO 8601 format, existing deployments of `adr-tools` must do +one of the following: + + * Accept mixed formatting of dates within their documentation library. + * Update existing documents to use ISO 8601 dates by running `adr upgrade-repository` diff --git a/structurizr-import/src/test/resources/adrs/0009-help-scripts.md b/structurizr-import/src/test/resources/adrs/0009-help-scripts.md new file mode 100644 index 000000000..0146d127c --- /dev/null +++ b/structurizr-import/src/test/resources/adrs/0009-help-scripts.md @@ -0,0 +1,28 @@ +# 9. Help scripts + +Date: 2018-06-26 + +## Status + +Accepted + +Amends [5. Help comments](0005-help-comments.md) + +## Context + +Currently help text is generated by extracting specially formatted comments from the top of the command script. + +This makes it easy for developers of the tool: documentation and code is all in one place. + +But, it means that help text cannot include calculated values, such as the location of files. + +## Decision + +Where necessary, help text can be generated by a script. + +The script will be called _adr_help__ + +## Consequences + +Help scripts can include helper scripts to locate files, giving more accurate instructions to the user that reflect how the tool is deployed in their environment. + diff --git a/structurizr-import/src/test/resources/diagrams/kroki/diagram.dot b/structurizr-import/src/test/resources/diagrams/kroki/diagram.dot new file mode 100644 index 000000000..3f2b18926 --- /dev/null +++ b/structurizr-import/src/test/resources/diagrams/kroki/diagram.dot @@ -0,0 +1 @@ +digraph G {Hello->World} diff --git a/structurizr-import/src/test/resources/diagrams/mermaid/class.mmd b/structurizr-import/src/test/resources/diagrams/mermaid/class.mmd new file mode 100644 index 000000000..eee0f009f --- /dev/null +++ b/structurizr-import/src/test/resources/diagrams/mermaid/class.mmd @@ -0,0 +1,21 @@ +classDiagram + Animal <|-- Duck + Animal <|-- Fish + Animal <|-- Zebra + Animal : +int age + Animal : +String gender + Animal: +isMammal() + Animal: +mate() + class Duck{ + +String beakColor + +swim() + +quack() + } + class Fish{ + -int sizeInFeet + -canEat() + } + class Zebra{ + +bool is_wild + +run() + } diff --git a/structurizr-import/src/test/resources/diagrams/mermaid/flowchart.mmd b/structurizr-import/src/test/resources/diagrams/mermaid/flowchart.mmd new file mode 100644 index 000000000..a5bd75ae5 --- /dev/null +++ b/structurizr-import/src/test/resources/diagrams/mermaid/flowchart.mmd @@ -0,0 +1,6 @@ +flowchart TD + A[Christmas] -->|Get money| B(Go shopping) + B --> C{Let me think} + C -->|One| D[Laptop] + C -->|Two| E[iPhone] + C -->|Three| F[fa:fa-car Car] \ No newline at end of file diff --git a/structurizr-import/src/test/resources/diagrams/plantuml/with-title.puml b/structurizr-import/src/test/resources/diagrams/plantuml/with-title.puml new file mode 100644 index 000000000..caf9f13a5 --- /dev/null +++ b/structurizr-import/src/test/resources/diagrams/plantuml/with-title.puml @@ -0,0 +1,4 @@ +@startuml +title Sequence diagram example +Bob -> Alice : hello +@enduml \ No newline at end of file diff --git a/structurizr-import/src/test/resources/diagrams/plantuml/without-title.puml b/structurizr-import/src/test/resources/diagrams/plantuml/without-title.puml new file mode 100644 index 000000000..1da6ac585 --- /dev/null +++ b/structurizr-import/src/test/resources/diagrams/plantuml/without-title.puml @@ -0,0 +1,3 @@ +@startuml +Bob -> Alice : hello +@enduml \ No newline at end of file diff --git a/structurizr-import/src/test/resources/docs/docs/01-section-1.md b/structurizr-import/src/test/resources/docs/docs/01-section-1.md new file mode 100644 index 000000000..4721380c0 --- /dev/null +++ b/structurizr-import/src/test/resources/docs/docs/01-section-1.md @@ -0,0 +1 @@ +## Section 1 \ No newline at end of file diff --git a/structurizr-import/src/test/resources/docs/docs/02-section-2.markdown b/structurizr-import/src/test/resources/docs/docs/02-section-2.markdown new file mode 100644 index 000000000..2fc0e3f87 --- /dev/null +++ b/structurizr-import/src/test/resources/docs/docs/02-section-2.markdown @@ -0,0 +1 @@ +## Section 2 \ No newline at end of file diff --git a/structurizr-import/src/test/resources/docs/docs/03-section-3.text b/structurizr-import/src/test/resources/docs/docs/03-section-3.text new file mode 100644 index 000000000..e847bfa94 --- /dev/null +++ b/structurizr-import/src/test/resources/docs/docs/03-section-3.text @@ -0,0 +1 @@ +## Section 3 \ No newline at end of file diff --git a/structurizr-import/src/test/resources/docs/docs/04-section-4.adoc b/structurizr-import/src/test/resources/docs/docs/04-section-4.adoc new file mode 100644 index 000000000..062d5dd9a --- /dev/null +++ b/structurizr-import/src/test/resources/docs/docs/04-section-4.adoc @@ -0,0 +1 @@ +== Section 4 \ No newline at end of file diff --git a/structurizr-import/src/test/resources/docs/docs/05-section-5.asciidoc b/structurizr-import/src/test/resources/docs/docs/05-section-5.asciidoc new file mode 100644 index 000000000..1a501bdbf --- /dev/null +++ b/structurizr-import/src/test/resources/docs/docs/05-section-5.asciidoc @@ -0,0 +1 @@ +== Section 5 \ No newline at end of file diff --git a/structurizr-import/src/test/resources/docs/docs/06-section-6.asc b/structurizr-import/src/test/resources/docs/docs/06-section-6.asc new file mode 100644 index 000000000..8728c5661 --- /dev/null +++ b/structurizr-import/src/test/resources/docs/docs/06-section-6.asc @@ -0,0 +1 @@ +== Section 6 \ No newline at end of file diff --git a/structurizr-import/src/test/resources/docs/docs/07-subdirectory/01-section-1.md b/structurizr-import/src/test/resources/docs/docs/07-subdirectory/01-section-1.md new file mode 100644 index 000000000..af328a67d --- /dev/null +++ b/structurizr-import/src/test/resources/docs/docs/07-subdirectory/01-section-1.md @@ -0,0 +1 @@ +## Section 7 \ No newline at end of file diff --git a/structurizr-import/src/test/resources/docs/docs/images/image.gif b/structurizr-import/src/test/resources/docs/docs/images/image.gif new file mode 100644 index 0000000000000000000000000000000000000000..c06542adaf2a63a53499dfe7bedc85e442e8d2b2 GIT binary patch literal 4682 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 fQ@dnt$vnQ|8D{SZ|*E1 literal 0 HcmV?d00001 diff --git a/structurizr-import/src/test/resources/docs/docs/images/image.jpg b/structurizr-import/src/test/resources/docs/docs/images/image.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0b9e9c39da3f93ae710234a051f94428f6c66ff3 GIT binary patch literal 1467 zcmex=kR1VRQz zDF#+Bn}NZLQ5wz;V$^`DVPart&tzbMs)+*9AOJKOq#H^zFJMHNxPS?+T44b*oGk^? z_Wvz|MLoA#DyHnP8$!323`E1Vw_ae#K|QlE+HwU zs-~`?sbyknW^Q3==I-I?6&w;879J59m7J2AmY$KBRa{b9R$ftA)!fqB*51+C zHEHscsne#9glAUcUPH>GPMb-@gC&`3vMPV0c2j1tcLpL-Us)&|gd}EX*wIAb&A3m4iGk z$ik{<$R^|%$evgztYp;4A>uS~;l_iU%Emz-M3agxa*3&!JXHM%@*3D@#CfcVET6$W zhVa*I24@B)Fkof^#u76#Fs4|5fR&vU1lT#)!GHq_xWIrL1bE@#KhO|Hpcz2TY(T`z z$<7IvBbxF5E-+a#{lCS)!^{XwOw57|_6&cDK2Go1Zs%jMqV6FfsF9&`z~p)gfaUG|*z zH#xBr1v`FRo2C@hleuDl+1>oRcE_LAc+P!P5VTlIFlegVKkYBu({|6FCI4adKE?kG z7q}k(`{($d;ds`&UiBmQo9`)Hw#i?n`>(NndF)}+wQr1bk8Ipg(33dfX?&if{L@mE z@b~(EG~c($f6=}1pW#05e}<#s&#%AT{NOD6!CUT=AO0@P&aIkO&c>>;$YXj&%Q8;| z1LeMkFR$aa8MhgoUv+h^=J{EMQ>OmRd9yw}W#;q5if+SSXEm<$Sf;AX-MMbv+tTu9 z_5UyY@!Hc=F{!#zIdy87i^kz>_L!9t76*Aals8J4{+j;5{ZW1UAH_9}kEA@3&nOcS=vSIAPQE=4jzo&BRrgmbY)7lCti3b!_Cz fQ@dnt$vnQ|8D{SZ|*E1 literal 0 HcmV?d00001 diff --git a/structurizr-import/src/test/resources/docs/docs/images/image.png b/structurizr-import/src/test/resources/docs/docs/images/image.png new file mode 100644 index 0000000000000000000000000000000000000000..644aef77b1dfbe2bad3a82b5e15421d22f95a5e1 GIT binary patch literal 1563 zcmeAS@N?(olHy`uVBq!ia0vp^3P3Ey!3HF+&5pANQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fIRt zPXT0ZVp4u-iLH_n$Rap^xU(cP4PjGWG1OZ?59)(t^bPe4^x1XJ8hJbBT*g_UUO;IJRJu zciBOKlo>nx&c7+~FP(n><-D1)WiOV6-}S0mwru&ipZkhe1}_hhXnS^AJN)C%pQnHP z+8rNID$;Z!!MrUx=*6)EC%21zJh{`@qWYQ1mDcX*XLk0VV|rzvwb_*Amcz=s4eUH` zi#(Kaa+vn+RxtC5js5lIyS1#0jag@!bxZ17A*)=W!zDQe|1a6)nB+*kc1}z?IYsw_ zdkn*${gb?%r6;=_-8QM<#?(m-vug@YtJQAmQNE?VWi}obe2sd^`_rXPRSRmpQ(R*E^A<}@vgo%*nN#>>+lxFC zGWJ#|mS!5-v8iP!@A$6!CI9+@Z|5dYIj88bUHZS`o5jl??EP8DrKh-HH_NnhEVehk z$CY_-aJSib+8MBXdvZEMIO6_p#?bjE6&G+^tYRr|oMs(5V}r9v_bVQmNjEx@S`^K< zzFB+6v8b zyX{HJ?Y|e_JV;yI%sHLy!(5@cLN(_$7d!AwZc%=q@a9RgJ!f4~8UMZ7=NwsEi`KpU zwA@0Y%j3s4E4}-59g^o=HLSHdqvRS5uV%hcWxQASSHWg|m%@v$YvrT=T;aa0m-zCk z<)1D0j(7sL7u1G+?+$KoH?2ka*sMZd))i?wCohc>9OqFGzBZ${SO=xQoq$Tal#>G=TFMAmlAXH*nT{`W)=2VfW4yW<&O1}U*~3u%)iTi$2A{8!q| z+xOwi8&5g4kEX6sH5R<`&yq#^biuOt#?YJWuK59cmrd8D>(zYj;BmiW$z?a^@+l+l zk3He)JDm3X6t^m$9yl+;Vfxi8b?<*<|9JK4yJv9K{+su*&feI#z^XIjEnCg81GXhh zVK==eb!^aCexPMXO~;ovofVEn=cmUheYm*LvH0Nn9siFUTlrml{`G0WlVSy}c+1@Q zgIWSzF2<#({G9S^!tX;ihxn{lzmtvm@kr>(gQQQA`fArV>#1FuqQ<*aLCp2;IiG{N q8-<@;tohWdyk|zuiSCo^KiE&kES&#X{o@T#5$x&e=d#Wzp$P!Pn~`<^ literal 0 HcmV?d00001 diff --git a/structurizr-import/src/test/resources/docs/images/image.gif b/structurizr-import/src/test/resources/docs/images/image.gif new file mode 100644 index 0000000000000000000000000000000000000000..c06542adaf2a63a53499dfe7bedc85e442e8d2b2 GIT binary patch literal 4682 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 fQ@dnt$vnQ|8D{SZ|*E1 literal 0 HcmV?d00001 diff --git a/structurizr-import/src/test/resources/docs/images/image.jpg b/structurizr-import/src/test/resources/docs/images/image.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0b9e9c39da3f93ae710234a051f94428f6c66ff3 GIT binary patch literal 1467 zcmex=kR1VRQz zDF#+Bn}NZLQ5wz;V$^`DVPart&tzbMs)+*9AOJKOq#H^zFJMHNxPS?+T44b*oGk^? z_Wvz|MLoA#DyHnP8$!323`E1Vw_ae#K|QlE+HwU zs-~`?sbyknW^Q3==I-I?6&w;879J59m7J2AmY$KBRa{b9R$ftA)!fqB*51+C zHEHscsne#9glAUcUPH>GPMb-@gC&`3vMPV0c2j1tcLpL-Us)&|gd}EX*wIAb&A3m4iGk z$ik{<$R^|%$evgztYp;4A>uS~;l_iU%Emz-M3agxa*3&!JXHM%@*3D@#CfcVET6$W zhVa*I24@B)Fkof^#u76#Fs4|5fR&vU1lT#)!GHq_xWIrL1bE@#KhO|Hpcz2TY(T`z z$<7IvBbxF5E-+a#{lCS)!^{XwOw57|_6&cDK2Go1Zs%jMqV6FfsF9&`z~p)gfaUG|*z zH#xBr1v`FRo2C@hleuDl+1>oRcE_LAc+P!P5VTlIFlegVKkYBu({|6FCI4adKE?kG z7q}k(`{($d;ds`&UiBmQo9`)Hw#i?n`>(NndF)}+wQr1bk8Ipg(33dfX?&if{L@mE z@b~(EG~c($f6=}1pW#05e}<#s&#%AT{NOD6!CUT=AO0@P&aIkO&c>>;$YXj&%Q8;| z1LeMkFR$aa8MhgoUv+h^=J{EMQ>OmRd9yw}W#;q5if+SSXEm<$Sf;AX-MMbv+tTu9 z_5UyY@!Hc=F{!#zIdy87i^kz>_L!9t76*Aals8J4{+j;5{ZW1UAH_9}kEA@3&nOcS=vSIAPQE=4jzo&BRrgmbY)7lCti3b!_Cz fQ@dnt$vnQ|8D{SZ|*E1 literal 0 HcmV?d00001 diff --git a/structurizr-import/src/test/resources/docs/images/image.png b/structurizr-import/src/test/resources/docs/images/image.png new file mode 100644 index 0000000000000000000000000000000000000000..644aef77b1dfbe2bad3a82b5e15421d22f95a5e1 GIT binary patch literal 1563 zcmeAS@N?(olHy`uVBq!ia0vp^3P3Ey!3HF+&5pANQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fIRt zPXT0ZVp4u-iLH_n$Rap^xU(cP4PjGWG1OZ?59)(t^bPe4^x1XJ8hJbBT*g_UUO;IJRJu zciBOKlo>nx&c7+~FP(n><-D1)WiOV6-}S0mwru&ipZkhe1}_hhXnS^AJN)C%pQnHP z+8rNID$;Z!!MrUx=*6)EC%21zJh{`@qWYQ1mDcX*XLk0VV|rzvwb_*Amcz=s4eUH` zi#(Kaa+vn+RxtC5js5lIyS1#0jag@!bxZ17A*)=W!zDQe|1a6)nB+*kc1}z?IYsw_ zdkn*${gb?%r6;=_-8QM<#?(m-vug@YtJQAmQNE?VWi}obe2sd^`_rXPRSRmpQ(R*E^A<}@vgo%*nN#>>+lxFC zGWJ#|mS!5-v8iP!@A$6!CI9+@Z|5dYIj88bUHZS`o5jl??EP8DrKh-HH_NnhEVehk z$CY_-aJSib+8MBXdvZEMIO6_p#?bjE6&G+^tYRr|oMs(5V}r9v_bVQmNjEx@S`^K< zzFB+6v8b zyX{HJ?Y|e_JV;yI%sHLy!(5@cLN(_$7d!AwZc%=q@a9RgJ!f4~8UMZ7=NwsEi`KpU zwA@0Y%j3s4E4}-59g^o=HLSHdqvRS5uV%hcWxQASSHWg|m%@v$YvrT=T;aa0m-zCk z<)1D0j(7sL7u1G+?+$KoH?2ka*sMZd))i?wCohc>9OqFGzBZ${SO=xQoq$Tal#>G=TFMAmlAXH*nT{`W)=2VfW4yW<&O1}U*~3u%)iTi$2A{8!q| z+xOwi8&5g4kEX6sH5R<`&yq#^biuOt#?YJWuK59cmrd8D>(zYj;BmiW$z?a^@+l+l zk3He)JDm3X6t^m$9yl+;Vfxi8b?<*<|9JK4yJv9K{+su*&feI#z^XIjEnCg81GXhh zVK==eb!^aCexPMXO~;ovofVEn=cmUheYm*LvH0Nn9siFUTlrml{`G0WlVSy}c+1@Q zgIWSzF2<#({G9S^!tX;ihxn{lzmtvm@kr>(gQQQA`fArV>#1FuqQ<*aLCp2;IiG{N q8-<@;tohWdyk|zuiSCo^KiE&kES&#X{o@T#5$x&e=d#Wzp$P!Pn~`<^ literal 0 HcmV?d00001 diff --git a/structurizr-import/src/test/resources/docs/images/image.svg b/structurizr-import/src/test/resources/docs/images/image.svg new file mode 100644 index 000000000..1d526413e --- /dev/null +++ b/structurizr-import/src/test/resources/docs/images/image.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/structurizr-import/src/test/resources/docs/images/images/image.gif b/structurizr-import/src/test/resources/docs/images/images/image.gif new file mode 100644 index 0000000000000000000000000000000000000000..c06542adaf2a63a53499dfe7bedc85e442e8d2b2 GIT binary patch literal 4682 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 fQ@dnt$vnQ|8D{SZ|*E1 literal 0 HcmV?d00001 diff --git a/structurizr-import/src/test/resources/docs/images/images/image.jpg b/structurizr-import/src/test/resources/docs/images/images/image.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0b9e9c39da3f93ae710234a051f94428f6c66ff3 GIT binary patch literal 1467 zcmex=kR1VRQz zDF#+Bn}NZLQ5wz;V$^`DVPart&tzbMs)+*9AOJKOq#H^zFJMHNxPS?+T44b*oGk^? z_Wvz|MLoA#DyHnP8$!323`E1Vw_ae#K|QlE+HwU zs-~`?sbyknW^Q3==I-I?6&w;879J59m7J2AmY$KBRa{b9R$ftA)!fqB*51+C zHEHscsne#9glAUcUPH>GPMb-@gC&`3vMPV0c2j1tcLpL-Us)&|gd}EX*wIAb&A3m4iGk z$ik{<$R^|%$evgztYp;4A>uS~;l_iU%Emz-M3agxa*3&!JXHM%@*3D@#CfcVET6$W zhVa*I24@B)Fkof^#u76#Fs4|5fR&vU1lT#)!GHq_xWIrL1bE@#KhO|Hpcz2TY(T`z z$<7IvBbxF5E-+a#{lCS)!^{XwOw57|_6&cDK2Go1Zs%jMqV6FfsF9&`z~p)gfaUG|*z zH#xBr1v`FRo2C@hleuDl+1>oRcE_LAc+P!P5VTlIFlegVKkYBu({|6FCI4adKE?kG z7q}k(`{($d;ds`&UiBmQo9`)Hw#i?n`>(NndF)}+wQr1bk8Ipg(33dfX?&if{L@mE z@b~(EG~c($f6=}1pW#05e}<#s&#%AT{NOD6!CUT=AO0@P&aIkO&c>>;$YXj&%Q8;| z1LeMkFR$aa8MhgoUv+h^=J{EMQ>OmRd9yw}W#;q5if+SSXEm<$Sf;AX-MMbv+tTu9 z_5UyY@!Hc=F{!#zIdy87i^kz>_L!9t76*Aals8J4{+j;5{ZW1UAH_9}kEA@3&nOcS=vSIAPQE=4jzo&BRrgmbY)7lCti3b!_Cz fQ@dnt$vnQ|8D{SZ|*E1 literal 0 HcmV?d00001 diff --git a/structurizr-import/src/test/resources/docs/images/images/image.png b/structurizr-import/src/test/resources/docs/images/images/image.png new file mode 100644 index 0000000000000000000000000000000000000000..644aef77b1dfbe2bad3a82b5e15421d22f95a5e1 GIT binary patch literal 1563 zcmeAS@N?(olHy`uVBq!ia0vp^3P3Ey!3HF+&5pANQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fIRt zPXT0ZVp4u-iLH_n$Rap^xU(cP4PjGWG1OZ?59)(t^bPe4^x1XJ8hJbBT*g_UUO;IJRJu zciBOKlo>nx&c7+~FP(n><-D1)WiOV6-}S0mwru&ipZkhe1}_hhXnS^AJN)C%pQnHP z+8rNID$;Z!!MrUx=*6)EC%21zJh{`@qWYQ1mDcX*XLk0VV|rzvwb_*Amcz=s4eUH` zi#(Kaa+vn+RxtC5js5lIyS1#0jag@!bxZ17A*)=W!zDQe|1a6)nB+*kc1}z?IYsw_ zdkn*${gb?%r6;=_-8QM<#?(m-vug@YtJQAmQNE?VWi}obe2sd^`_rXPRSRmpQ(R*E^A<}@vgo%*nN#>>+lxFC zGWJ#|mS!5-v8iP!@A$6!CI9+@Z|5dYIj88bUHZS`o5jl??EP8DrKh-HH_NnhEVehk z$CY_-aJSib+8MBXdvZEMIO6_p#?bjE6&G+^tYRr|oMs(5V}r9v_bVQmNjEx@S`^K< zzFB+6v8b zyX{HJ?Y|e_JV;yI%sHLy!(5@cLN(_$7d!AwZc%=q@a9RgJ!f4~8UMZ7=NwsEi`KpU zwA@0Y%j3s4E4}-59g^o=HLSHdqvRS5uV%hcWxQASSHWg|m%@v$YvrT=T;aa0m-zCk z<)1D0j(7sL7u1G+?+$KoH?2ka*sMZd))i?wCohc>9OqFGzBZ${SO=xQoq$Tal#>G=TFMAmlAXH*nT{`W)=2VfW4yW<&O1}U*~3u%)iTi$2A{8!q| z+xOwi8&5g4kEX6sH5R<`&yq#^biuOt#?YJWuK59cmrd8D>(zYj;BmiW$z?a^@+l+l zk3He)JDm3X6t^m$9yl+;Vfxi8b?<*<|9JK4yJv9K{+su*&feI#z^XIjEnCg81GXhh zVK==eb!^aCexPMXO~;ovofVEn=cmUheYm*LvH0Nn9siFUTlrml{`G0WlVSy}c+1@Q zgIWSzF2<#({G9S^!tX;ihxn{lzmtvm@kr>(gQQQA`fArV>#1FuqQ<*aLCp2;IiG{N q8-<@;tohWdyk|zuiSCo^KiE&kES&#X{o@T#5$x&e=d#Wzp$P!Pn~`<^ literal 0 HcmV?d00001 diff --git a/structurizr-import/src/test/resources/docs/images/noimages/readme.md b/structurizr-import/src/test/resources/docs/images/noimages/readme.md new file mode 100644 index 000000000..6e5b3dd54 --- /dev/null +++ b/structurizr-import/src/test/resources/docs/images/noimages/readme.md @@ -0,0 +1 @@ +These are not the images you are looking for... From 4e7e075545fba998ba1634b53ae8af330f8f27a6 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 9 Jan 2024 15:01:54 +0000 Subject: [PATCH 438/717] Update READMEs. --- structurizr-client/README.md | 8 +++++++- structurizr-core/README.md | 2 ++ structurizr-dsl/README.md | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/structurizr-client/README.md b/structurizr-client/README.md index 60a155cf1..ea9734085 100644 --- a/structurizr-client/README.md +++ b/structurizr-client/README.md @@ -2,4 +2,10 @@ [![Maven Central](https://img.shields.io/maven-central/v/com.structurizr/structurizr-client.svg?label=Maven%20Central)](https://search.maven.org/artifact/com.structurizr/structurizr-client) -- [Documentation](https://docs.structurizr.com/java/workspace-api) +This library provides an API client for the workspace and admin APIs provided by the Structurizr cloud service and on-premises installation. + +- [Cloud service - Workspace API](https://docs.structurizr.com/cloud/workspace-api) +- [Cloud service - Admin API](https://docs.structurizr.com/cloud/admin-api) +- [On-premises installation - Workspace API](https://docs.structurizr.com/onpremises/workspace-api) +- [On-premises installation - Admin API](https://docs.structurizr.com/onpremises/admin-api) +- [Workspace API client](https://docs.structurizr.com/java/workspace-api) diff --git a/structurizr-core/README.md b/structurizr-core/README.md index 23e5b133d..2a1d0b062 100644 --- a/structurizr-core/README.md +++ b/structurizr-core/README.md @@ -2,5 +2,7 @@ [![Maven Central](https://img.shields.io/maven-central/v/com.structurizr/structurizr-core.svg?label=Maven%20Central)](https://search.maven.org/artifact/com.structurizr/structurizr-core) +This library provides the core functionality of Structurizr related to creating workspaces. + - [Documentation](https://docs.structurizr.com/java) diff --git a/structurizr-dsl/README.md b/structurizr-dsl/README.md index ae9e61d84..2108a6503 100644 --- a/structurizr-dsl/README.md +++ b/structurizr-dsl/README.md @@ -2,7 +2,7 @@ [![Maven Central](https://img.shields.io/maven-central/v/com.structurizr/structurizr-dsl.svg?label=Maven%20Central)](https://search.maven.org/artifact/com.structurizr/structurizr-dsl) -This GitHub repository contains an implementation of the Structurizr DSL - a way to create Structurizr software +This library is the implementation of the Structurizr DSL - a way to create Structurizr software architecture models based upon the [C4 model](https://c4model.com) using a textual domain specific language (DSL). The Structurizr DSL has appeared on the [ThoughtWorks Tech Radar - Techniques - Diagrams as code](https://www.thoughtworks.com/radar/techniques/diagrams-as-code) From ab302967e2d596b20268637908568874f38191f0 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 9 Jan 2024 15:02:58 +0000 Subject: [PATCH 439/717] Exclude structurizr-graph tests from running via GitHub Actions. --- .github/workflows/gradle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index ab8bc8cd1..68c76f168 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -28,4 +28,4 @@ jobs: - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build with Gradle - run: ./gradlew + run: ./gradlew -x :structurizr-graphviz:test From 99d4ccc78c83424ffc4ee8e6e4b8e0238ee3b8ae Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 9 Jan 2024 17:25:24 +0000 Subject: [PATCH 440/717] Removes the `Workspace.countAndLogWarnings()` method with an initial version of something more flexible. --- settings.gradle | 1 + structurizr-assistant/README.md | 3 + structurizr-assistant/build.gradle | 7 ++ .../com/structurizr/assistant/Assistant.java | 20 +++++ .../assistant/DefaultAssistant.java | 65 +++++++++++++++ .../com/structurizr/assistant/Inspection.java | 50 ++++++++++++ .../com/structurizr/assistant/Priority.java | 9 +++ .../structurizr/assistant/Recommendation.java | 32 ++++++++ .../model/ComponentDescriptionInspection.java | 16 ++++ .../assistant/model/ComponentInspection.java | 21 +++++ .../model/ComponentTechnologyInspection.java | 28 +++++++ .../model/ContainerDescriptionInspection.java | 16 ++++ .../assistant/model/ContainerInspection.java | 21 +++++ .../model/ContainerTechnologyInspection.java | 28 +++++++ .../DeploymentNodeDescriptionInspection.java | 16 ++++ .../model/DeploymentNodeInspection.java | 21 +++++ .../DeploymentNodeTechnologyInspection.java | 28 +++++++ .../model/ElementDescriptionInspection.java | 23 ++++++ .../assistant/model/ElementInspection.java | 30 +++++++ ...lementNotIncludedInAnyViewsInspection.java | 44 +++++++++++ .../model/EmptyDeploymentNodeInspection.java | 27 +++++++ .../assistant/model/EmptyModelInspection.java | 26 ++++++ ...frastructureNodeDescriptionInspection.java | 15 ++++ .../model/InfrastructureNodeInspection.java | 21 +++++ ...nfrastructureNodeTechnologyInspection.java | 28 +++++++ .../assistant/model/ModelInspection.java | 24 ++++++ ...ipleSoftwareSystemsDetailedInspection.java | 34 ++++++++ .../model/OrphanedElementInspection.java | 44 +++++++++++ .../model/PersonDescriptionInspection.java | 16 ++++ .../RelationshipDescriptionInspection.java | 37 +++++++++ .../model/RelationshipInspection.java | 36 +++++++++ .../RelationshipTechnologyInspection.java | 37 +++++++++ .../SoftwareSystemDescriptionInspection.java | 16 ++++ ...sForMultipleSoftwareSystemsInspection.java | 34 ++++++++ .../assistant/view/EmptyViewsInspection.java | 26 ++++++ ...sForMultipleSoftwareSystemsInspection.java | 34 ++++++++ .../assistant/view/ViewsInspection.java | 23 ++++++ .../workspace/WorkspaceInspection.java | 23 ++++++ .../workspace/WorkspaceScopeInspection.java | 26 ++++++ .../ComponentDescriptionInspectionTests.java | 41 ++++++++++ .../ComponentTechnologyInspectionTests.java | 41 ++++++++++ .../ContainerDescriptionInspectionTests.java | 38 +++++++++ .../ContainerTechnologyInspectionTests.java | 38 +++++++++ ...loymentNodeDescriptionInspectionTests.java | 35 ++++++++ ...ploymentNodeTechnologyInspectionTests.java | 35 ++++++++ ...tNotIncludedInAnyViewsInspectionTests.java | 36 +++++++++ .../model/EmptyDeploymentNodeCheckTests.java | 71 +++++++++++++++++ .../model/EmptyModelInspectionTests.java | 33 ++++++++ ...ructureNodeDescriptionInspectionTests.java | 37 +++++++++ ...tructureNodeTechnologyInspectionTests.java | 37 +++++++++ ...oftwareSystemsDetailedInspectionTests.java | 79 +++++++++++++++++++ .../model/OrphanedElementInspectionTests.java | 40 ++++++++++ .../PersonDescriptionInspectionTests.java | 35 ++++++++ ...elationshipDescriptionInspectionTests.java | 57 +++++++++++++ ...RelationshipTechnologyInspectionTests.java | 55 +++++++++++++ ...twareSystemDescriptionInspectionTests.java | 35 ++++++++ ...ultipleSoftwareSystemsInspectionTests.java | 40 ++++++++++ .../view/EmptyViewsInspectionTests.java | 33 ++++++++ ...ultipleSoftwareSystemsInspectionTests.java | 40 ++++++++++ .../WorkspaceScopeInspectionTests.java | 34 ++++++++ .../structurizr/api/WorkspaceApiClient.java | 2 - .../main/java/com/structurizr/Workspace.java | 68 ---------------- .../java/com/structurizr/WorkspaceTests.java | 20 ----- 63 files changed, 1896 insertions(+), 90 deletions(-) create mode 100644 structurizr-assistant/README.md create mode 100644 structurizr-assistant/build.gradle create mode 100644 structurizr-assistant/src/main/java/com/structurizr/assistant/Assistant.java create mode 100644 structurizr-assistant/src/main/java/com/structurizr/assistant/DefaultAssistant.java create mode 100644 structurizr-assistant/src/main/java/com/structurizr/assistant/Inspection.java create mode 100644 structurizr-assistant/src/main/java/com/structurizr/assistant/Priority.java create mode 100644 structurizr-assistant/src/main/java/com/structurizr/assistant/Recommendation.java create mode 100644 structurizr-assistant/src/main/java/com/structurizr/assistant/model/ComponentDescriptionInspection.java create mode 100644 structurizr-assistant/src/main/java/com/structurizr/assistant/model/ComponentInspection.java create mode 100644 structurizr-assistant/src/main/java/com/structurizr/assistant/model/ComponentTechnologyInspection.java create mode 100644 structurizr-assistant/src/main/java/com/structurizr/assistant/model/ContainerDescriptionInspection.java create mode 100644 structurizr-assistant/src/main/java/com/structurizr/assistant/model/ContainerInspection.java create mode 100644 structurizr-assistant/src/main/java/com/structurizr/assistant/model/ContainerTechnologyInspection.java create mode 100644 structurizr-assistant/src/main/java/com/structurizr/assistant/model/DeploymentNodeDescriptionInspection.java create mode 100644 structurizr-assistant/src/main/java/com/structurizr/assistant/model/DeploymentNodeInspection.java create mode 100644 structurizr-assistant/src/main/java/com/structurizr/assistant/model/DeploymentNodeTechnologyInspection.java create mode 100644 structurizr-assistant/src/main/java/com/structurizr/assistant/model/ElementDescriptionInspection.java create mode 100644 structurizr-assistant/src/main/java/com/structurizr/assistant/model/ElementInspection.java create mode 100644 structurizr-assistant/src/main/java/com/structurizr/assistant/model/ElementNotIncludedInAnyViewsInspection.java create mode 100644 structurizr-assistant/src/main/java/com/structurizr/assistant/model/EmptyDeploymentNodeInspection.java create mode 100644 structurizr-assistant/src/main/java/com/structurizr/assistant/model/EmptyModelInspection.java create mode 100644 structurizr-assistant/src/main/java/com/structurizr/assistant/model/InfrastructureNodeDescriptionInspection.java create mode 100644 structurizr-assistant/src/main/java/com/structurizr/assistant/model/InfrastructureNodeInspection.java create mode 100644 structurizr-assistant/src/main/java/com/structurizr/assistant/model/InfrastructureNodeTechnologyInspection.java create mode 100644 structurizr-assistant/src/main/java/com/structurizr/assistant/model/ModelInspection.java create mode 100644 structurizr-assistant/src/main/java/com/structurizr/assistant/model/MultipleSoftwareSystemsDetailedInspection.java create mode 100644 structurizr-assistant/src/main/java/com/structurizr/assistant/model/OrphanedElementInspection.java create mode 100644 structurizr-assistant/src/main/java/com/structurizr/assistant/model/PersonDescriptionInspection.java create mode 100644 structurizr-assistant/src/main/java/com/structurizr/assistant/model/RelationshipDescriptionInspection.java create mode 100644 structurizr-assistant/src/main/java/com/structurizr/assistant/model/RelationshipInspection.java create mode 100644 structurizr-assistant/src/main/java/com/structurizr/assistant/model/RelationshipTechnologyInspection.java create mode 100644 structurizr-assistant/src/main/java/com/structurizr/assistant/model/SoftwareSystemDescriptionInspection.java create mode 100644 structurizr-assistant/src/main/java/com/structurizr/assistant/view/ContainerViewsForMultipleSoftwareSystemsInspection.java create mode 100644 structurizr-assistant/src/main/java/com/structurizr/assistant/view/EmptyViewsInspection.java create mode 100644 structurizr-assistant/src/main/java/com/structurizr/assistant/view/SystemContextViewsForMultipleSoftwareSystemsInspection.java create mode 100644 structurizr-assistant/src/main/java/com/structurizr/assistant/view/ViewsInspection.java create mode 100644 structurizr-assistant/src/main/java/com/structurizr/assistant/workspace/WorkspaceInspection.java create mode 100644 structurizr-assistant/src/main/java/com/structurizr/assistant/workspace/WorkspaceScopeInspection.java create mode 100644 structurizr-assistant/src/test/java/com/structurizr/assistant/model/ComponentDescriptionInspectionTests.java create mode 100644 structurizr-assistant/src/test/java/com/structurizr/assistant/model/ComponentTechnologyInspectionTests.java create mode 100644 structurizr-assistant/src/test/java/com/structurizr/assistant/model/ContainerDescriptionInspectionTests.java create mode 100644 structurizr-assistant/src/test/java/com/structurizr/assistant/model/ContainerTechnologyInspectionTests.java create mode 100644 structurizr-assistant/src/test/java/com/structurizr/assistant/model/DeploymentNodeDescriptionInspectionTests.java create mode 100644 structurizr-assistant/src/test/java/com/structurizr/assistant/model/DeploymentNodeTechnologyInspectionTests.java create mode 100644 structurizr-assistant/src/test/java/com/structurizr/assistant/model/ElementNotIncludedInAnyViewsInspectionTests.java create mode 100644 structurizr-assistant/src/test/java/com/structurizr/assistant/model/EmptyDeploymentNodeCheckTests.java create mode 100644 structurizr-assistant/src/test/java/com/structurizr/assistant/model/EmptyModelInspectionTests.java create mode 100644 structurizr-assistant/src/test/java/com/structurizr/assistant/model/InfrastructureNodeDescriptionInspectionTests.java create mode 100644 structurizr-assistant/src/test/java/com/structurizr/assistant/model/InfrastructureNodeTechnologyInspectionTests.java create mode 100644 structurizr-assistant/src/test/java/com/structurizr/assistant/model/MultipleSoftwareSystemsDetailedInspectionTests.java create mode 100644 structurizr-assistant/src/test/java/com/structurizr/assistant/model/OrphanedElementInspectionTests.java create mode 100644 structurizr-assistant/src/test/java/com/structurizr/assistant/model/PersonDescriptionInspectionTests.java create mode 100644 structurizr-assistant/src/test/java/com/structurizr/assistant/model/RelationshipDescriptionInspectionTests.java create mode 100644 structurizr-assistant/src/test/java/com/structurizr/assistant/model/RelationshipTechnologyInspectionTests.java create mode 100644 structurizr-assistant/src/test/java/com/structurizr/assistant/model/SoftwareSystemDescriptionInspectionTests.java create mode 100644 structurizr-assistant/src/test/java/com/structurizr/assistant/view/ContainerViewsForMultipleSoftwareSystemsInspectionTests.java create mode 100644 structurizr-assistant/src/test/java/com/structurizr/assistant/view/EmptyViewsInspectionTests.java create mode 100644 structurizr-assistant/src/test/java/com/structurizr/assistant/view/SystemContextViewsForMultipleSoftwareSystemsInspectionTests.java create mode 100644 structurizr-assistant/src/test/java/com/structurizr/assistant/workspace/WorkspaceScopeInspectionTests.java diff --git a/settings.gradle b/settings.gradle index 3b97826de..e06f2f882 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,5 +1,6 @@ rootProject.name = 'structurizr-java' +include 'structurizr-assistant' include 'structurizr-client' include 'structurizr-core' include 'structurizr-dsl' diff --git a/structurizr-assistant/README.md b/structurizr-assistant/README.md new file mode 100644 index 000000000..790b286e8 --- /dev/null +++ b/structurizr-assistant/README.md @@ -0,0 +1,3 @@ +# structurizr-assistant + +[![Maven Central](https://img.shields.io/maven-central/v/com.structurizr/structurizr-assistant.svg?label=Maven%20Central)](https://search.maven.org/artifact/com.structurizr/structurizr-assistant) diff --git a/structurizr-assistant/build.gradle b/structurizr-assistant/build.gradle new file mode 100644 index 000000000..b6bde77a2 --- /dev/null +++ b/structurizr-assistant/build.gradle @@ -0,0 +1,7 @@ +dependencies { + + api project(':structurizr-core') + + testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2' + +} \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/Assistant.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/Assistant.java new file mode 100644 index 000000000..aba2a5523 --- /dev/null +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/Assistant.java @@ -0,0 +1,20 @@ +package com.structurizr.assistant; + +import java.util.ArrayList; +import java.util.Collection; + +abstract class Assistant { + + private final Collection recommendations = new ArrayList<>(); + + public Collection getRecommendations() { + return new ArrayList<>(recommendations); + } + + protected void add(Recommendation recommendation) { + if (recommendation != null) { + recommendations.add(recommendation); + } + } + +} \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/DefaultAssistant.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/DefaultAssistant.java new file mode 100644 index 000000000..ec1114489 --- /dev/null +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/DefaultAssistant.java @@ -0,0 +1,65 @@ +package com.structurizr.assistant; + +import com.structurizr.Workspace; +import com.structurizr.assistant.model.*; +import com.structurizr.assistant.view.ContainerViewsForMultipleSoftwareSystemsInspection; +import com.structurizr.assistant.view.EmptyViewsInspection; +import com.structurizr.assistant.view.SystemContextViewsForMultipleSoftwareSystemsInspection; +import com.structurizr.assistant.workspace.WorkspaceScopeInspection; +import com.structurizr.model.*; + +public class DefaultAssistant extends Assistant { + + private static final String ALL_RECOMMENDATIONS = "structurizr.recommendations"; + + public DefaultAssistant(Workspace workspace) { + if (workspace.getProperties().getOrDefault(ALL_RECOMMENDATIONS, "true").equalsIgnoreCase("false")) { + // skip all inspections + return; + } + + runWorkspaceInspections(workspace); + runModelInspections(workspace); + runViewInspections(workspace); + } + + private void runWorkspaceInspections(Workspace workspace) { + add(new WorkspaceScopeInspection(workspace).run()); + } + + private void runModelInspections(Workspace workspace) { + add(new EmptyModelInspection(workspace).run()); + add(new MultipleSoftwareSystemsDetailedInspection(workspace).run()); + ElementNotIncludedInAnyViewsInspection elementNotIncludedInAnyViewsCheck = new ElementNotIncludedInAnyViewsInspection(workspace); + OrphanedElementInspection orphanedElementCheck = new OrphanedElementInspection(workspace); + for (Element element : workspace.getModel().getElements()) { + add(elementNotIncludedInAnyViewsCheck.run(element)); + add(orphanedElementCheck.run(element)); + + if (element instanceof Person) { + add(new PersonDescriptionInspection(workspace).run(element)); + } + + if (element instanceof SoftwareSystem) { + add(new SoftwareSystemDescriptionInspection(workspace).run(element)); + } + + if (element instanceof Container) { + add(new ContainerDescriptionInspection(workspace).run(element)); + add(new ContainerTechnologyInspection(workspace).run(element)); + } + + if (element instanceof Component) { + add(new ComponentDescriptionInspection(workspace).run(element)); + add(new ComponentTechnologyInspection(workspace).run(element)); + } + } + } + + private void runViewInspections(Workspace workspace) { + add(new EmptyViewsInspection(workspace).run()); + add(new SystemContextViewsForMultipleSoftwareSystemsInspection(workspace).run()); + add(new ContainerViewsForMultipleSoftwareSystemsInspection(workspace).run()); + } + +} \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/Inspection.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/Inspection.java new file mode 100644 index 000000000..22458a136 --- /dev/null +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/Inspection.java @@ -0,0 +1,50 @@ +package com.structurizr.assistant; + +import com.structurizr.PropertyHolder; +import com.structurizr.Workspace; + +public abstract class Inspection { + + private static final String STRUCTURIZR_RECOMMENDATIONS_PREFIX = "structurizr.recommendations."; + + private final Workspace workspace; + + protected Inspection(Workspace workspace) { + this.workspace = workspace; + } + + protected abstract String getType(); + + protected Workspace getWorkspace() { + return workspace; + } + + protected boolean isEnabled(String type, PropertyHolder... propertyHolders) { + String value = "true"; + + for (PropertyHolder propertyHolder : propertyHolders) { + if (propertyHolder != null) { + value = propertyHolder.getProperties().getOrDefault(STRUCTURIZR_RECOMMENDATIONS_PREFIX + type, value); + } + } + + return !value.equalsIgnoreCase("false"); + } + + protected Recommendation noRecommendation() { + return null; + } + + protected Recommendation lowPriorityRecommendation(String description) { + return new Recommendation(STRUCTURIZR_RECOMMENDATIONS_PREFIX + getType(), Priority.Low, description); + } + + protected Recommendation mediumPriorityRecommendation(String description) { + return new Recommendation(STRUCTURIZR_RECOMMENDATIONS_PREFIX + getType(), Priority.Medium, description); + } + + protected Recommendation highPriorityRecommendation(String description) { + return new Recommendation(STRUCTURIZR_RECOMMENDATIONS_PREFIX + getType(), Priority.High, description); + } + +} \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/Priority.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/Priority.java new file mode 100644 index 000000000..5a2c3085e --- /dev/null +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/Priority.java @@ -0,0 +1,9 @@ +package com.structurizr.assistant; + +public enum Priority { + + Low, + Medium, + High + +} \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/Recommendation.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/Recommendation.java new file mode 100644 index 000000000..1e35b3c63 --- /dev/null +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/Recommendation.java @@ -0,0 +1,32 @@ +package com.structurizr.assistant; + +public final class Recommendation { + + private final String type; + private final Priority priority; + private final String description; + + Recommendation(String type, Priority priority, String description) { + this.type = type; + this.priority = priority; + this.description = description; + } + + public String getType() { + return type; + } + + public Priority getPriority() { + return priority; + } + + public String getDescription() { + return description; + } + + @Override + public String toString() { + return type + " | " + priority + " | " + description; + } + +} \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/model/ComponentDescriptionInspection.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/ComponentDescriptionInspection.java new file mode 100644 index 000000000..ccacdefbf --- /dev/null +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/ComponentDescriptionInspection.java @@ -0,0 +1,16 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; + +public class ComponentDescriptionInspection extends ElementDescriptionInspection { + + public ComponentDescriptionInspection(Workspace workspace) { + super(workspace); + } + + @Override + protected String getType() { + return "model.component.description"; + } + +} \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/model/ComponentInspection.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/ComponentInspection.java new file mode 100644 index 000000000..4c64146f4 --- /dev/null +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/ComponentInspection.java @@ -0,0 +1,21 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Recommendation; +import com.structurizr.model.Component; +import com.structurizr.model.Element; + +abstract class ComponentInspection extends ElementInspection { + + public ComponentInspection(Workspace workspace) { + super(workspace); + } + + @Override + protected Recommendation inspect(Element element) { + return inspect((Component)element); + } + + protected abstract Recommendation inspect(Component component); + +} \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/model/ComponentTechnologyInspection.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/ComponentTechnologyInspection.java new file mode 100644 index 000000000..780acd976 --- /dev/null +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/ComponentTechnologyInspection.java @@ -0,0 +1,28 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Recommendation; +import com.structurizr.model.Component; +import com.structurizr.util.StringUtils; + +public class ComponentTechnologyInspection extends ComponentInspection { + + public ComponentTechnologyInspection(Workspace workspace) { + super(workspace); + } + + @Override + protected Recommendation inspect(Component component) { + if (StringUtils.isNullOrEmpty(component.getDescription())) { + return lowPriorityRecommendation("Add a technology to the " + terminologyFor(component).toLowerCase() + " named \"" + component.getName() + "\"."); + } + + return noRecommendation(); + } + + @Override + protected String getType() { + return "model.component.technology"; + } + +} \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/model/ContainerDescriptionInspection.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/ContainerDescriptionInspection.java new file mode 100644 index 000000000..ac4d59f8e --- /dev/null +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/ContainerDescriptionInspection.java @@ -0,0 +1,16 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; + +public class ContainerDescriptionInspection extends ElementDescriptionInspection { + + public ContainerDescriptionInspection(Workspace workspace) { + super(workspace); + } + + @Override + protected String getType() { + return "model.container.description"; + } + +} \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/model/ContainerInspection.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/ContainerInspection.java new file mode 100644 index 000000000..4c7dea0fd --- /dev/null +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/ContainerInspection.java @@ -0,0 +1,21 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Recommendation; +import com.structurizr.model.Container; +import com.structurizr.model.Element; + +abstract class ContainerInspection extends ElementInspection { + + public ContainerInspection(Workspace workspace) { + super(workspace); + } + + @Override + protected Recommendation inspect(Element element) { + return inspect((Container)element); + } + + protected abstract Recommendation inspect(Container container); + +} \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/model/ContainerTechnologyInspection.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/ContainerTechnologyInspection.java new file mode 100644 index 000000000..34baaa0fc --- /dev/null +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/ContainerTechnologyInspection.java @@ -0,0 +1,28 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Recommendation; +import com.structurizr.model.Container; +import com.structurizr.util.StringUtils; + +public class ContainerTechnologyInspection extends ContainerInspection { + + public ContainerTechnologyInspection(Workspace workspace) { + super(workspace); + } + + @Override + protected Recommendation inspect(Container container) { + if (StringUtils.isNullOrEmpty(container.getTechnology())) { + return mediumPriorityRecommendation("Add a technology to the " + terminologyFor(container).toLowerCase() + " named \"" + container.getName() + "\"."); + } + + return noRecommendation(); + } + + @Override + protected String getType() { + return "model.container.technology"; + } + +} \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/model/DeploymentNodeDescriptionInspection.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/DeploymentNodeDescriptionInspection.java new file mode 100644 index 000000000..5dfccec44 --- /dev/null +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/DeploymentNodeDescriptionInspection.java @@ -0,0 +1,16 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; + +public class DeploymentNodeDescriptionInspection extends ElementDescriptionInspection { + + public DeploymentNodeDescriptionInspection(Workspace workspace) { + super(workspace); + } + + @Override + protected String getType() { + return "model.deploymentnode.description"; + } + +} \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/model/DeploymentNodeInspection.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/DeploymentNodeInspection.java new file mode 100644 index 000000000..bd87be417 --- /dev/null +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/DeploymentNodeInspection.java @@ -0,0 +1,21 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Recommendation; +import com.structurizr.model.DeploymentNode; +import com.structurizr.model.Element; + +abstract class DeploymentNodeInspection extends ElementInspection { + + public DeploymentNodeInspection(Workspace workspace) { + super(workspace); + } + + @Override + protected Recommendation inspect(Element element) { + return inspect((DeploymentNode)element); + } + + protected abstract Recommendation inspect(DeploymentNode deploymentNode); + +} \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/model/DeploymentNodeTechnologyInspection.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/DeploymentNodeTechnologyInspection.java new file mode 100644 index 000000000..eeb768821 --- /dev/null +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/DeploymentNodeTechnologyInspection.java @@ -0,0 +1,28 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Recommendation; +import com.structurizr.model.DeploymentNode; +import com.structurizr.util.StringUtils; + +public class DeploymentNodeTechnologyInspection extends DeploymentNodeInspection { + + public DeploymentNodeTechnologyInspection(Workspace workspace) { + super(workspace); + } + + @Override + protected Recommendation inspect(DeploymentNode deploymentNode) { + if (StringUtils.isNullOrEmpty(deploymentNode.getDescription())) { + return mediumPriorityRecommendation("Add a technology to the " + terminologyFor(deploymentNode).toLowerCase() + " named \"" + deploymentNode.getName() + "\"."); + } + + return noRecommendation(); + } + + @Override + protected String getType() { + return "model.deploymentnode.technology"; + } + +} \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/model/ElementDescriptionInspection.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/ElementDescriptionInspection.java new file mode 100644 index 000000000..85b024d81 --- /dev/null +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/ElementDescriptionInspection.java @@ -0,0 +1,23 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Recommendation; +import com.structurizr.model.Element; +import com.structurizr.util.StringUtils; + +abstract class ElementDescriptionInspection extends ElementInspection { + + public ElementDescriptionInspection(Workspace workspace) { + super(workspace); + } + + @Override + protected Recommendation inspect(Element element) { + if (StringUtils.isNullOrEmpty(element.getDescription())) { + return mediumPriorityRecommendation("Add a description to the " + terminologyFor(element).toLowerCase() + " named \"" + element.getName() + "\"."); + } + + return noRecommendation(); + } + +} \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/model/ElementInspection.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/ElementInspection.java new file mode 100644 index 000000000..475b7121d --- /dev/null +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/ElementInspection.java @@ -0,0 +1,30 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Inspection; +import com.structurizr.assistant.Recommendation; +import com.structurizr.model.Element; + +abstract class ElementInspection extends Inspection { + + public ElementInspection(Workspace workspace) { + super(workspace); + } + + public final Recommendation run(Element element) { + Element parentElement = element.getParent(); + + if (isEnabled(getType(), getWorkspace(), getWorkspace().getModel(), parentElement, element)) { + return inspect(element); + } + + return noRecommendation(); + } + + protected String terminologyFor(Element element) { + return getWorkspace().getViews().getConfiguration().getTerminology().findTerminology(element).toLowerCase(); + } + + protected abstract Recommendation inspect(Element element); + +} \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/model/ElementNotIncludedInAnyViewsInspection.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/ElementNotIncludedInAnyViewsInspection.java new file mode 100644 index 000000000..930d1374c --- /dev/null +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/ElementNotIncludedInAnyViewsInspection.java @@ -0,0 +1,44 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Recommendation; +import com.structurizr.model.Element; +import com.structurizr.view.ElementView; +import com.structurizr.view.ModelView; +import com.structurizr.view.View; + +import java.util.HashSet; +import java.util.Set; + +public class ElementNotIncludedInAnyViewsInspection extends ElementInspection { + + private final Set elementsInViews = new HashSet<>(); + + public ElementNotIncludedInAnyViewsInspection(Workspace workspace) { + super(workspace); + + for (View view : workspace.getViews().getViews()) { + if (view instanceof ModelView) { + ModelView modelView = (ModelView)view; + for (ElementView elementView : modelView.getElements()) { + elementsInViews.add(elementView.getId()); + } + } + } + } + + @Override + protected Recommendation inspect(Element element) { + if (!elementsInViews.contains(element.getId())) { + return lowPriorityRecommendation("The " + terminologyFor(element) + " named \"" + element.getName() + "\" is not included on any views - add it to a view."); + } + + return noRecommendation(); + } + + @Override + protected String getType() { + return "model.element.noview"; + } + +} \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/model/EmptyDeploymentNodeInspection.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/EmptyDeploymentNodeInspection.java new file mode 100644 index 000000000..b3c46582f --- /dev/null +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/EmptyDeploymentNodeInspection.java @@ -0,0 +1,27 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Recommendation; +import com.structurizr.model.DeploymentNode; + +public class EmptyDeploymentNodeInspection extends DeploymentNodeInspection { + + public EmptyDeploymentNodeInspection(Workspace workspace) { + super(workspace); + } + + @Override + protected Recommendation inspect(DeploymentNode deploymentNode) { + if (!deploymentNode.hasChildren() && !deploymentNode.hasSoftwareSystemInstances() && !deploymentNode.hasContainerInstances() && !deploymentNode.hasInfrastructureNodes()) { + return lowPriorityRecommendation("The " + terminologyFor(deploymentNode).toLowerCase() + " named \"" + deploymentNode.getName() + "\" is empty."); + } + + return noRecommendation(); + } + + @Override + protected String getType() { + return "model.deploymentnode.empty"; + } + +} \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/model/EmptyModelInspection.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/EmptyModelInspection.java new file mode 100644 index 000000000..d31042e8d --- /dev/null +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/EmptyModelInspection.java @@ -0,0 +1,26 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Recommendation; + +public class EmptyModelInspection extends ModelInspection { + + public EmptyModelInspection(Workspace workspace) { + super(workspace); + } + + @Override + protected Recommendation inspect(Workspace workspace) { + if (workspace.getModel().isEmpty()) { + return highPriorityRecommendation("Add some elements to the model."); + } + + return noRecommendation(); + } + + @Override + protected String getType() { + return "model.empty"; + } + +} \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/model/InfrastructureNodeDescriptionInspection.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/InfrastructureNodeDescriptionInspection.java new file mode 100644 index 000000000..265b961ac --- /dev/null +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/InfrastructureNodeDescriptionInspection.java @@ -0,0 +1,15 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; + +public class InfrastructureNodeDescriptionInspection extends ElementDescriptionInspection { + + public InfrastructureNodeDescriptionInspection(Workspace workspace) { + super(workspace); + } + + protected String getType() { + return "model.infrastructurenode.description"; + } + +} \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/model/InfrastructureNodeInspection.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/InfrastructureNodeInspection.java new file mode 100644 index 000000000..62097ea0f --- /dev/null +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/InfrastructureNodeInspection.java @@ -0,0 +1,21 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Recommendation; +import com.structurizr.model.Element; +import com.structurizr.model.InfrastructureNode; + +abstract class InfrastructureNodeInspection extends ElementInspection { + + public InfrastructureNodeInspection(Workspace workspace) { + super(workspace); + } + + @Override + protected Recommendation inspect(Element element) { + return inspect((InfrastructureNode)element); + } + + protected abstract Recommendation inspect(InfrastructureNode infrastructureNode); + +} \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/model/InfrastructureNodeTechnologyInspection.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/InfrastructureNodeTechnologyInspection.java new file mode 100644 index 000000000..951dc0fb1 --- /dev/null +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/InfrastructureNodeTechnologyInspection.java @@ -0,0 +1,28 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Recommendation; +import com.structurizr.model.InfrastructureNode; +import com.structurizr.util.StringUtils; + +public class InfrastructureNodeTechnologyInspection extends InfrastructureNodeInspection { + + public InfrastructureNodeTechnologyInspection(Workspace workspace) { + super(workspace); + } + + @Override + protected Recommendation inspect(InfrastructureNode infrastructureNode) { + if (StringUtils.isNullOrEmpty(infrastructureNode.getTechnology())) { + return mediumPriorityRecommendation("Add a technology to the " + terminologyFor(infrastructureNode).toLowerCase() + " named \"" + infrastructureNode.getName() + "\"."); + } + + return noRecommendation(); + } + + @Override + protected String getType() { + return "model.infrastructurenode.technology"; + } + +} \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/model/ModelInspection.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/ModelInspection.java new file mode 100644 index 000000000..4b8ab75ac --- /dev/null +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/ModelInspection.java @@ -0,0 +1,24 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Inspection; +import com.structurizr.assistant.Recommendation; +import com.structurizr.model.Element; + +abstract class ModelInspection extends Inspection { + + public ModelInspection(Workspace workspace) { + super(workspace); + } + + public final Recommendation run() { + if (isEnabled(getType(), getWorkspace(), getWorkspace().getModel())) { + return inspect(getWorkspace()); + } + + return noRecommendation(); + } + + protected abstract Recommendation inspect(Workspace workspace); + +} \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/model/MultipleSoftwareSystemsDetailedInspection.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/MultipleSoftwareSystemsDetailedInspection.java new file mode 100644 index 000000000..34fc04be9 --- /dev/null +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/MultipleSoftwareSystemsDetailedInspection.java @@ -0,0 +1,34 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Recommendation; +import com.structurizr.model.SoftwareSystem; + +public class MultipleSoftwareSystemsDetailedInspection extends ModelInspection { + + public MultipleSoftwareSystemsDetailedInspection(Workspace workspace) { + super(workspace); + } + + @Override + protected Recommendation inspect(Workspace workspace) { + int softwareSystemsWithDetails = 0; + for (SoftwareSystem softwareSystem : workspace.getModel().getSoftwareSystems()) { + if (softwareSystem.hasContainers() || !softwareSystem.getDocumentation().isEmpty()) { + softwareSystemsWithDetails++; + } + } + + if (softwareSystemsWithDetails > 1) { + return highPriorityRecommendation("This workspace describes the internal details of " + softwareSystemsWithDetails + " software systems. It is recommended that a workspace contains the model, views, and documentation for a single software system only."); + } + + return noRecommendation(); + } + + @Override + protected String getType() { + return "workspace.scope"; + } + +} \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/model/OrphanedElementInspection.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/OrphanedElementInspection.java new file mode 100644 index 000000000..7cc09eb51 --- /dev/null +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/OrphanedElementInspection.java @@ -0,0 +1,44 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Recommendation; +import com.structurizr.model.DeploymentNode; +import com.structurizr.model.Element; +import com.structurizr.model.Relationship; + +import java.util.HashSet; +import java.util.Set; + +public class OrphanedElementInspection extends ElementInspection { + + private final Set elementsWithRelationships = new HashSet<>(); + + public OrphanedElementInspection(Workspace workspace) { + super(workspace); + + for (Relationship relationship : workspace.getModel().getRelationships()) { + elementsWithRelationships.add(relationship.getSourceId()); + elementsWithRelationships.add(relationship.getDestinationId()); + } + } + + @Override + protected Recommendation inspect(Element element) { + if (element instanceof DeploymentNode) { + // deployment nodes typically won't have relationships to/from them + return noRecommendation(); + } + + if (!elementsWithRelationships.contains(element.getId())) { + return mediumPriorityRecommendation("The " + terminologyFor(element).toLowerCase() + " named \"" + element.getName() + "\" is orphaned - add a relationship to/from it, or consider removing it from the model."); + } + + return noRecommendation(); + } + + @Override + protected String getType() { + return "model.element.orphaned"; + } + +} \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/model/PersonDescriptionInspection.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/PersonDescriptionInspection.java new file mode 100644 index 000000000..9f88eca21 --- /dev/null +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/PersonDescriptionInspection.java @@ -0,0 +1,16 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; + +public class PersonDescriptionInspection extends ElementDescriptionInspection { + + public PersonDescriptionInspection(Workspace workspace) { + super(workspace); + } + + @Override + protected String getType() { + return "model.person.description"; + } + +} \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/model/RelationshipDescriptionInspection.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/RelationshipDescriptionInspection.java new file mode 100644 index 000000000..7c8bb9aa6 --- /dev/null +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/RelationshipDescriptionInspection.java @@ -0,0 +1,37 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Recommendation; +import com.structurizr.model.Component; +import com.structurizr.model.Element; +import com.structurizr.model.Relationship; +import com.structurizr.util.StringUtils; + +public class RelationshipDescriptionInspection extends RelationshipInspection { + + public RelationshipDescriptionInspection(Workspace workspace) { + super(workspace); + } + + @Override + protected Recommendation inspect(Relationship relationship) { + Element source = relationship.getSource(); + Element destination = relationship.getDestination(); + + if (StringUtils.isNullOrEmpty(relationship.getDescription())) { + if (source instanceof Component && destination instanceof Component) { + return lowPriorityRecommendation("Add a description to the relationship between the " + terminologyFor(source) + " named \"" + source.getName() + "\" and the " + terminologyFor(destination) + " named \"" + destination.getName() + "\"."); + } else { + return mediumPriorityRecommendation("Add a description to the relationship between the " + terminologyFor(source) + " named \"" + source.getName() + "\" and the " + terminologyFor(destination) + " named \"" + destination.getName() + "\"."); + } + } + + return noRecommendation(); + } + + @Override + protected String getType() { + return "model.relationship.description"; + } + +} \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/model/RelationshipInspection.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/RelationshipInspection.java new file mode 100644 index 000000000..406d80b41 --- /dev/null +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/RelationshipInspection.java @@ -0,0 +1,36 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Inspection; +import com.structurizr.assistant.Recommendation; +import com.structurizr.model.Element; +import com.structurizr.model.Relationship; +import com.structurizr.util.StringUtils; + +public abstract class RelationshipInspection extends Inspection { + + public RelationshipInspection(Workspace workspace) { + super(workspace); + } + + public final Recommendation run(Relationship relationship) { + Element source = relationship.getSource(); + Relationship linkedRelationship = null; + if (!StringUtils.isNullOrEmpty(relationship.getLinkedRelationshipId())) { + getWorkspace().getModel().getRelationship(relationship.getLinkedRelationshipId()); + } + + if (isEnabled(getType(), getWorkspace(), getWorkspace().getModel(), source.getParent(), source, linkedRelationship, relationship)) { + return inspect(relationship); + } + + return noRecommendation(); + } + + protected String terminologyFor(Element element) { + return getWorkspace().getViews().getConfiguration().getTerminology().findTerminology(element).toLowerCase(); + } + + protected abstract Recommendation inspect(Relationship relationship); + +} \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/model/RelationshipTechnologyInspection.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/RelationshipTechnologyInspection.java new file mode 100644 index 000000000..b563a70cc --- /dev/null +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/RelationshipTechnologyInspection.java @@ -0,0 +1,37 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Recommendation; +import com.structurizr.model.Container; +import com.structurizr.model.Element; +import com.structurizr.model.Relationship; +import com.structurizr.util.StringUtils; + +public class RelationshipTechnologyInspection extends RelationshipInspection { + + public RelationshipTechnologyInspection(Workspace workspace) { + super(workspace); + } + + @Override + protected Recommendation inspect(Relationship relationship) { + Element source = relationship.getSource(); + Element destination = relationship.getDestination(); + + if (StringUtils.isNullOrEmpty(relationship.getTechnology())) { + if (source instanceof Container && destination instanceof Container) { + return mediumPriorityRecommendation("Add a technology to the relationship between the " + terminologyFor(source) + " named \"" + source.getName() + "\" and the " + terminologyFor(destination) + " named \"" + destination.getName() + "\"."); + } else { + return lowPriorityRecommendation("Add a technology to the relationship between the " + terminologyFor(source) + " named \"" + source.getName() + "\" and the " + terminologyFor(destination) + " named \"" + destination.getName() + "\"."); + } + } + + return noRecommendation(); + } + + @Override + protected String getType() { + return "model.relationship.technology"; + } + +} \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/model/SoftwareSystemDescriptionInspection.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/SoftwareSystemDescriptionInspection.java new file mode 100644 index 000000000..abc505cc1 --- /dev/null +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/SoftwareSystemDescriptionInspection.java @@ -0,0 +1,16 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; + +public class SoftwareSystemDescriptionInspection extends ElementDescriptionInspection { + + public SoftwareSystemDescriptionInspection(Workspace workspace) { + super(workspace); + } + + @Override + protected String getType() { + return "model.softwaresystem.description"; + } + +} \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/view/ContainerViewsForMultipleSoftwareSystemsInspection.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/view/ContainerViewsForMultipleSoftwareSystemsInspection.java new file mode 100644 index 000000000..2e33015c0 --- /dev/null +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/view/ContainerViewsForMultipleSoftwareSystemsInspection.java @@ -0,0 +1,34 @@ +package com.structurizr.assistant.view; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Recommendation; +import com.structurizr.view.ContainerView; + +import java.util.HashSet; +import java.util.Set; + +public class ContainerViewsForMultipleSoftwareSystemsInspection extends ViewsInspection { + + public ContainerViewsForMultipleSoftwareSystemsInspection(Workspace workspace) { + super(workspace); + } + + @Override + public Recommendation inspect(Workspace workspace) { + Set softwareSystemsWithContainerViews = new HashSet<>(); + for (ContainerView view : workspace.getViews().getContainerViews()) { + softwareSystemsWithContainerViews.add(view.getSoftwareSystemId()); + } + if (softwareSystemsWithContainerViews.size() > 1) { + return highPriorityRecommendation("Container views exist for " + softwareSystemsWithContainerViews.size() + " software systems. It is recommended that a workspace includes container views for a single software system only."); + } + + return noRecommendation(); + } + + @Override + protected String getType() { + return "workspace.scope"; + } + +} \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/view/EmptyViewsInspection.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/view/EmptyViewsInspection.java new file mode 100644 index 000000000..b00c8ad4b --- /dev/null +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/view/EmptyViewsInspection.java @@ -0,0 +1,26 @@ +package com.structurizr.assistant.view; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Recommendation; + +public class EmptyViewsInspection extends ViewsInspection { + + public EmptyViewsInspection(Workspace workspace) { + super(workspace); + } + + @Override + public Recommendation inspect(Workspace workspace) { + if (workspace.getViews().isEmpty()) { + return highPriorityRecommendation("Add some views to the workspace."); + } + + return noRecommendation(); + } + + @Override + protected String getType() { + return "views.empty"; + } + +} \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/view/SystemContextViewsForMultipleSoftwareSystemsInspection.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/view/SystemContextViewsForMultipleSoftwareSystemsInspection.java new file mode 100644 index 000000000..4e7c926a5 --- /dev/null +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/view/SystemContextViewsForMultipleSoftwareSystemsInspection.java @@ -0,0 +1,34 @@ +package com.structurizr.assistant.view; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Recommendation; +import com.structurizr.view.SystemContextView; + +import java.util.HashSet; +import java.util.Set; + +public class SystemContextViewsForMultipleSoftwareSystemsInspection extends ViewsInspection { + + public SystemContextViewsForMultipleSoftwareSystemsInspection(Workspace workspace) { + super(workspace); + } + + @Override + public Recommendation inspect(Workspace workspace) { + Set softwareSystemsWithSystemContextViews = new HashSet<>(); + for (SystemContextView view : workspace.getViews().getSystemContextViews()) { + softwareSystemsWithSystemContextViews.add(view.getSoftwareSystemId()); + } + if (softwareSystemsWithSystemContextViews.size() > 1) { + return highPriorityRecommendation("System context views exist for " + softwareSystemsWithSystemContextViews.size() + " software systems. It is recommended that a workspace includes system context views for a single software system only."); + } + + return noRecommendation(); + } + + @Override + protected String getType() { + return "workspace.scope"; + } + +} \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/view/ViewsInspection.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/view/ViewsInspection.java new file mode 100644 index 000000000..bd7af5b0b --- /dev/null +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/view/ViewsInspection.java @@ -0,0 +1,23 @@ +package com.structurizr.assistant.view; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Inspection; +import com.structurizr.assistant.Recommendation; + +abstract class ViewsInspection extends Inspection { + + public ViewsInspection(Workspace workspace) { + super(workspace); + } + + public final Recommendation run() { + if (isEnabled(getType(), getWorkspace(), getWorkspace().getViews().getConfiguration())) { + return inspect(getWorkspace()); + } + + return noRecommendation(); + } + + protected abstract Recommendation inspect(Workspace workspace); + +} \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/workspace/WorkspaceInspection.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/workspace/WorkspaceInspection.java new file mode 100644 index 000000000..1e6e5c63c --- /dev/null +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/workspace/WorkspaceInspection.java @@ -0,0 +1,23 @@ +package com.structurizr.assistant.workspace; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Inspection; +import com.structurizr.assistant.Recommendation; + +abstract class WorkspaceInspection extends Inspection { + + public WorkspaceInspection(Workspace workspace) { + super(workspace); + } + + public final Recommendation run() { + if (isEnabled(getType(), getWorkspace())) { + return inspect(getWorkspace()); + } + + return null; + } + + protected abstract Recommendation inspect(Workspace workspace); + +} \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/workspace/WorkspaceScopeInspection.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/workspace/WorkspaceScopeInspection.java new file mode 100644 index 000000000..076edf608 --- /dev/null +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/workspace/WorkspaceScopeInspection.java @@ -0,0 +1,26 @@ +package com.structurizr.assistant.workspace; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Recommendation; + +public class WorkspaceScopeInspection extends WorkspaceInspection { + + public WorkspaceScopeInspection(Workspace workspace) { + super(workspace); + } + + @Override + public Recommendation inspect(Workspace workspace) { + if (workspace.getConfiguration().getScope() == null) { + return highPriorityRecommendation("This workspace has no defined scope. It is recommended that the workspace scope is set to \"Landscape\" or \"SoftwareSystem\"."); + } + + return noRecommendation(); + } + + @Override + protected String getType() { + return "workspace.scope"; + } + +} \ No newline at end of file diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/ComponentDescriptionInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/ComponentDescriptionInspectionTests.java new file mode 100644 index 000000000..eb17e0c0f --- /dev/null +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/ComponentDescriptionInspectionTests.java @@ -0,0 +1,41 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Priority; +import com.structurizr.assistant.Recommendation; +import com.structurizr.model.Component; +import com.structurizr.model.Container; +import com.structurizr.model.SoftwareSystem; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class ComponentDescriptionInspectionTests { + + @Test + public void run_WithoutDescription() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + Component component = container.addComponent("Name"); + + Recommendation recommendation = new ComponentDescriptionInspection(workspace).run(component); + Assertions.assertEquals(Priority.Medium, recommendation.getPriority()); + assertEquals("structurizr.recommendations.model.component.description", recommendation.getType()); + assertEquals("Add a description to the component named \"Name\".", recommendation.getDescription()); + } + + @Test + public void run_WithDescription() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + Component component = container.addComponent("Name", "Description"); + + Recommendation recommendation = new ComponentDescriptionInspection(workspace).run(component); + assertNull(recommendation); + } + +} diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/ComponentTechnologyInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/ComponentTechnologyInspectionTests.java new file mode 100644 index 000000000..f5f1826a2 --- /dev/null +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/ComponentTechnologyInspectionTests.java @@ -0,0 +1,41 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Priority; +import com.structurizr.assistant.Recommendation; +import com.structurizr.model.Component; +import com.structurizr.model.Container; +import com.structurizr.model.SoftwareSystem; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class ComponentTechnologyInspectionTests { + + @Test + public void run_WithoutDescription() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + Component component = container.addComponent("Name"); + + Recommendation recommendation = new ComponentTechnologyInspection(workspace).run(component); + Assertions.assertEquals(Priority.Low, recommendation.getPriority()); + assertEquals("structurizr.recommendations.model.component.technology", recommendation.getType()); + assertEquals("Add a technology to the component named \"Name\".", recommendation.getDescription()); + } + + @Test + public void run_WithDescription() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + Component component = container.addComponent("Name", "Description", "Technology"); + + Recommendation recommendation = new ComponentTechnologyInspection(workspace).run(component); + assertNull(recommendation); + } + +} diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/ContainerDescriptionInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/ContainerDescriptionInspectionTests.java new file mode 100644 index 000000000..9b0655c04 --- /dev/null +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/ContainerDescriptionInspectionTests.java @@ -0,0 +1,38 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Priority; +import com.structurizr.assistant.Recommendation; +import com.structurizr.model.Container; +import com.structurizr.model.SoftwareSystem; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class ContainerDescriptionInspectionTests { + + @Test + public void run_WithoutDescription() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Name"); + + Recommendation recommendation = new ContainerDescriptionInspection(workspace).run(container); + Assertions.assertEquals(Priority.Medium, recommendation.getPriority()); + assertEquals("structurizr.recommendations.model.container.description", recommendation.getType()); + assertEquals("Add a description to the container named \"Name\".", recommendation.getDescription()); + } + + @Test + public void run_WithDescription() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Name", "Description"); + + Recommendation recommendation = new ContainerDescriptionInspection(workspace).run(container); + assertNull(recommendation); + } + +} diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/ContainerTechnologyInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/ContainerTechnologyInspectionTests.java new file mode 100644 index 000000000..3f2e08854 --- /dev/null +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/ContainerTechnologyInspectionTests.java @@ -0,0 +1,38 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Priority; +import com.structurizr.assistant.Recommendation; +import com.structurizr.model.Container; +import com.structurizr.model.SoftwareSystem; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class ContainerTechnologyInspectionTests { + + @Test + public void run_WithoutDescription() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Name"); + + Recommendation recommendation = new ContainerTechnologyInspection(workspace).run(container); + Assertions.assertEquals(Priority.Medium, recommendation.getPriority()); + assertEquals("structurizr.recommendations.model.container.technology", recommendation.getType()); + assertEquals("Add a technology to the container named \"Name\".", recommendation.getDescription()); + } + + @Test + public void run_WithDescription() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Name", "Description", "Technology"); + + Recommendation recommendation = new ContainerTechnologyInspection(workspace).run(container); + assertNull(recommendation); + } + +} diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/DeploymentNodeDescriptionInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/DeploymentNodeDescriptionInspectionTests.java new file mode 100644 index 000000000..e495234e2 --- /dev/null +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/DeploymentNodeDescriptionInspectionTests.java @@ -0,0 +1,35 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Priority; +import com.structurizr.assistant.Recommendation; +import com.structurizr.model.DeploymentNode; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class DeploymentNodeDescriptionInspectionTests { + + @Test + public void run_WithoutDescription() { + Workspace workspace = new Workspace("Name", "Description"); + DeploymentNode deploymentNode = workspace.getModel().addDeploymentNode("Name"); + + Recommendation recommendation = new DeploymentNodeDescriptionInspection(workspace).run(deploymentNode); + Assertions.assertEquals(Priority.Medium, recommendation.getPriority()); + assertEquals("structurizr.recommendations.model.deploymentnode.description", recommendation.getType()); + assertEquals("Add a description to the deployment node named \"Name\".", recommendation.getDescription()); + } + + @Test + public void run_WithDescription() { + Workspace workspace = new Workspace("Name", "Description"); + DeploymentNode deploymentNode = workspace.getModel().addDeploymentNode("Name", "Description", "Technology"); + + Recommendation recommendation = new DeploymentNodeDescriptionInspection(workspace).run(deploymentNode); + assertNull(recommendation); + } + +} diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/DeploymentNodeTechnologyInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/DeploymentNodeTechnologyInspectionTests.java new file mode 100644 index 000000000..5c0aa5660 --- /dev/null +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/DeploymentNodeTechnologyInspectionTests.java @@ -0,0 +1,35 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Priority; +import com.structurizr.assistant.Recommendation; +import com.structurizr.model.DeploymentNode; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class DeploymentNodeTechnologyInspectionTests { + + @Test + public void run_WithoutDescription() { + Workspace workspace = new Workspace("Name", "Description"); + DeploymentNode deploymentNode = workspace.getModel().addDeploymentNode("Name"); + + Recommendation recommendation = new DeploymentNodeTechnologyInspection(workspace).run(deploymentNode); + Assertions.assertEquals(Priority.Medium, recommendation.getPriority()); + assertEquals("structurizr.recommendations.model.deploymentnode.technology", recommendation.getType()); + assertEquals("Add a technology to the deployment node named \"Name\".", recommendation.getDescription()); + } + + @Test + public void run_WithDescription() { + Workspace workspace = new Workspace("Name", "Description"); + DeploymentNode deploymentNode = workspace.getModel().addDeploymentNode("Name", "Description", "Technology"); + + Recommendation recommendation = new DeploymentNodeTechnologyInspection(workspace).run(deploymentNode); + assertNull(recommendation); + } + +} diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/ElementNotIncludedInAnyViewsInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/ElementNotIncludedInAnyViewsInspectionTests.java new file mode 100644 index 000000000..d92e2913a --- /dev/null +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/ElementNotIncludedInAnyViewsInspectionTests.java @@ -0,0 +1,36 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Priority; +import com.structurizr.assistant.Recommendation; +import com.structurizr.model.SoftwareSystem; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class ElementNotIncludedInAnyViewsInspectionTests { + + @Test + public void run_NotInViews() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name"); + + Recommendation recommendation = new ElementNotIncludedInAnyViewsInspection(workspace).run(softwareSystem); + Assertions.assertEquals(Priority.Low, recommendation.getPriority()); + assertEquals("structurizr.recommendations.model.element.noview", recommendation.getType()); + assertEquals("The software system named \"Name\" is not included on any views - add it to a view.", recommendation.getDescription()); + } + + @Test + public void run_InViews() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name"); + workspace.getViews().createSystemLandscapeView("key", "Description").addAllElements(); + + Recommendation recommendation = new ElementNotIncludedInAnyViewsInspection(workspace).run(softwareSystem); + assertNull(recommendation); + } + +} diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/EmptyDeploymentNodeCheckTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/EmptyDeploymentNodeCheckTests.java new file mode 100644 index 000000000..2bde35613 --- /dev/null +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/EmptyDeploymentNodeCheckTests.java @@ -0,0 +1,71 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Priority; +import com.structurizr.assistant.Recommendation; +import com.structurizr.model.Container; +import com.structurizr.model.DeploymentNode; +import com.structurizr.model.SoftwareSystem; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class EmptyDeploymentNodeCheckTests { + + @Test + public void run_Empty() { + Workspace workspace = new Workspace("Name", "Description"); + DeploymentNode deploymentNode = workspace.getModel().addDeploymentNode("Name"); + + Recommendation recommendation = new EmptyDeploymentNodeInspection(workspace).run(deploymentNode); + Assertions.assertEquals(Priority.Low, recommendation.getPriority()); + assertEquals("structurizr.recommendations.model.deploymentnode.empty", recommendation.getType()); + assertEquals("The deployment node named \"Name\" is empty.", recommendation.getDescription()); + } + + @Test + public void run_WithDeploymentNode() { + Workspace workspace = new Workspace("Name", "Description"); + DeploymentNode deploymentNode = workspace.getModel().addDeploymentNode("Deployment Node", "Description", "Technology"); + deploymentNode.addDeploymentNode("Deployment Node"); + + Recommendation recommendation = new EmptyDeploymentNodeInspection(workspace).run(deploymentNode); + assertNull(recommendation); + } + + @Test + public void run_WithInfrastructureNode() { + Workspace workspace = new Workspace("Name", "Description"); + DeploymentNode deploymentNode = workspace.getModel().addDeploymentNode("Deployment Node", "Description", "Technology"); + deploymentNode.addInfrastructureNode("Infrastructure Node"); + + Recommendation recommendation = new EmptyDeploymentNodeInspection(workspace).run(deploymentNode); + assertNull(recommendation); + } + + @Test + public void run_WithSoftwareSystemInstance() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); + DeploymentNode deploymentNode = workspace.getModel().addDeploymentNode("Deployment Node", "Description", "Technology"); + deploymentNode.add(softwareSystem); + + Recommendation recommendation = new EmptyDeploymentNodeInspection(workspace).run(deploymentNode); + assertNull(recommendation); + } + + @Test + public void run_WithContainerInstance() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + DeploymentNode deploymentNode = workspace.getModel().addDeploymentNode("Deployment Node", "Description", "Technology"); + deploymentNode.add(container); + + Recommendation recommendation = new EmptyDeploymentNodeInspection(workspace).run(deploymentNode); + assertNull(recommendation); + } + +} diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/EmptyModelInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/EmptyModelInspectionTests.java new file mode 100644 index 000000000..e022667e6 --- /dev/null +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/EmptyModelInspectionTests.java @@ -0,0 +1,33 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Priority; +import com.structurizr.assistant.Recommendation; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class EmptyModelInspectionTests { + + @Test + public void run_WhenThereAreNoElements() { + Workspace workspace = new Workspace("Name", "Description"); + + Recommendation recommendation = new EmptyModelInspection(workspace).run(); + Assertions.assertEquals(Priority.High, recommendation.getPriority()); + assertEquals("structurizr.recommendations.model.empty", recommendation.getType()); + assertEquals("Add some elements to the model.", recommendation.getDescription()); + } + + @Test + public void run_WhenThereAreElements() { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getModel().addSoftwareSystem("Name"); + + Recommendation recommendation = new EmptyModelInspection(workspace).run(); + assertNull(recommendation); + } + +} diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/InfrastructureNodeDescriptionInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/InfrastructureNodeDescriptionInspectionTests.java new file mode 100644 index 000000000..b9257dd0c --- /dev/null +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/InfrastructureNodeDescriptionInspectionTests.java @@ -0,0 +1,37 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Priority; +import com.structurizr.assistant.Recommendation; +import com.structurizr.model.InfrastructureNode; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class InfrastructureNodeDescriptionInspectionTests { + + @Test + public void run_WithoutDescription() { + Workspace workspace = new Workspace("Name", "Description"); + InfrastructureNode infrastructureNode = workspace.getModel().addDeploymentNode("Deployment Node") + .addInfrastructureNode("Name"); + + Recommendation recommendation = new InfrastructureNodeDescriptionInspection(workspace).run(infrastructureNode); + Assertions.assertEquals(Priority.Medium, recommendation.getPriority()); + assertEquals("structurizr.recommendations.model.infrastructurenode.description", recommendation.getType()); + assertEquals("Add a description to the infrastructure node named \"Name\".", recommendation.getDescription()); + } + + @Test + public void run_WithDescription() { + Workspace workspace = new Workspace("Name", "Description"); + InfrastructureNode infrastructureNode = workspace.getModel().addDeploymentNode("Deployment Node") + .addInfrastructureNode("Name", "Description", "Technology"); + + Recommendation recommendation = new InfrastructureNodeDescriptionInspection(workspace).run(infrastructureNode); + assertNull(recommendation); + } + +} diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/InfrastructureNodeTechnologyInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/InfrastructureNodeTechnologyInspectionTests.java new file mode 100644 index 000000000..fd990cdae --- /dev/null +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/InfrastructureNodeTechnologyInspectionTests.java @@ -0,0 +1,37 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Priority; +import com.structurizr.assistant.Recommendation; +import com.structurizr.model.InfrastructureNode; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class InfrastructureNodeTechnologyInspectionTests { + + @Test + public void run_WithoutDescription() { + Workspace workspace = new Workspace("Name", "Description"); + InfrastructureNode infrastructureNode = workspace.getModel().addDeploymentNode("Deployment Node") + .addInfrastructureNode("Name"); + + Recommendation recommendation = new InfrastructureNodeTechnologyInspection(workspace).run(infrastructureNode); + Assertions.assertEquals(Priority.Medium, recommendation.getPriority()); + assertEquals("structurizr.recommendations.model.infrastructurenode.technology", recommendation.getType()); + assertEquals("Add a technology to the infrastructure node named \"Name\".", recommendation.getDescription()); + } + + @Test + public void run_WithDescription() { + Workspace workspace = new Workspace("Name", "Description"); + InfrastructureNode infrastructureNode = workspace.getModel().addDeploymentNode("Deployment Node") + .addInfrastructureNode("Name", "Description", "Technology"); + + Recommendation recommendation = new InfrastructureNodeTechnologyInspection(workspace).run(infrastructureNode); + assertNull(recommendation); + } + +} diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/MultipleSoftwareSystemsDetailedInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/MultipleSoftwareSystemsDetailedInspectionTests.java new file mode 100644 index 000000000..3dcce3c0d --- /dev/null +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/MultipleSoftwareSystemsDetailedInspectionTests.java @@ -0,0 +1,79 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Priority; +import com.structurizr.assistant.Recommendation; +import com.structurizr.documentation.Decision; +import com.structurizr.documentation.Format; +import com.structurizr.documentation.Section; +import com.structurizr.model.SoftwareSystem; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class MultipleSoftwareSystemsDetailedInspectionTests { + + @Test + public void run_MultipleSoftwareSystemsWithContainers() { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getModel().addSoftwareSystem("A").addContainer("Container"); + workspace.getModel().addSoftwareSystem("B").addContainer("Container"); + + Recommendation recommendation = new MultipleSoftwareSystemsDetailedInspection(workspace).run(); + Assertions.assertEquals(Priority.High, recommendation.getPriority()); + assertEquals("structurizr.recommendations.workspace.scope", recommendation.getType()); + assertEquals("This workspace describes the internal details of 2 software systems. It is recommended that a workspace contains the model, views, and documentation for a single software system only.", recommendation.getDescription()); + } + + @Test + public void run_MultipleSoftwareSystemsWithDocumentation() { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getModel().addSoftwareSystem("A").getDocumentation().addSection(new Section(Format.Markdown, "# Section 1")); + workspace.getModel().addSoftwareSystem("B").getDocumentation().addSection(new Section(Format.Markdown, "# Section 1")); + + Recommendation recommendation = new MultipleSoftwareSystemsDetailedInspection(workspace).run(); + Assertions.assertEquals(Priority.High, recommendation.getPriority()); + assertEquals("structurizr.recommendations.workspace.scope", recommendation.getType()); + assertEquals("This workspace describes the internal details of 2 software systems. It is recommended that a workspace contains the model, views, and documentation for a single software system only.", recommendation.getDescription()); + } + + @Test + public void run_MultipleSoftwareSystemsWithDecisions() { + Workspace workspace = new Workspace("Name", "Description"); + Decision decision = new Decision("1"); + decision.setTitle("Decision 1"); + decision.setFormat(Format.Markdown); + decision.setContent("Content"); + decision.setStatus("Accepted"); + workspace.getModel().addSoftwareSystem("A").getDocumentation().addDecision(decision); + workspace.getModel().addSoftwareSystem("B").getDocumentation().addDecision(decision); + + Recommendation recommendation = new MultipleSoftwareSystemsDetailedInspection(workspace).run(); + Assertions.assertEquals(Priority.High, recommendation.getPriority()); + assertEquals("structurizr.recommendations.workspace.scope", recommendation.getType()); + assertEquals("This workspace describes the internal details of 2 software systems. It is recommended that a workspace contains the model, views, and documentation for a single software system only.", recommendation.getDescription()); + } + + @Test + public void run_SingleSoftwareSystem() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem a = workspace.getModel().addSoftwareSystem("A"); + a.addContainer("Container"); + + a.getDocumentation().addSection(new Section(Format.Markdown, "# Section 1")); + + Decision decision = new Decision("1"); + decision.setTitle("Decision 1"); + decision.setFormat(Format.Markdown); + decision.setContent("Content"); + decision.setStatus("Accepted"); + + a.getDocumentation().addDecision(decision); + + Recommendation recommendation = new MultipleSoftwareSystemsDetailedInspection(workspace).run(); + assertNull(recommendation); + } + +} diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/OrphanedElementInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/OrphanedElementInspectionTests.java new file mode 100644 index 000000000..a92153dea --- /dev/null +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/OrphanedElementInspectionTests.java @@ -0,0 +1,40 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Priority; +import com.structurizr.assistant.Recommendation; +import com.structurizr.model.SoftwareSystem; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class OrphanedElementInspectionTests { + + @Test + public void run_WithOrphan() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem a = workspace.getModel().addSoftwareSystem("A"); + + Recommendation recommendation = new OrphanedElementInspection(workspace).run(a); + Assertions.assertEquals(Priority.Medium, recommendation.getPriority()); + assertEquals("structurizr.recommendations.model.element.orphaned", recommendation.getType()); + assertEquals("The software system named \"A\" is orphaned - add a relationship to/from it, or consider removing it from the model.", recommendation.getDescription()); + } + + @Test + public void run_WithoutOrphan() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem a = workspace.getModel().addSoftwareSystem("A"); + SoftwareSystem b = workspace.getModel().addSoftwareSystem("B"); + a.uses(b, "Uses"); + + Recommendation recommendation = new OrphanedElementInspection(workspace).run(a); + assertNull(recommendation); + + recommendation = new OrphanedElementInspection(workspace).run(b); + assertNull(recommendation); + } + +} diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/PersonDescriptionInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/PersonDescriptionInspectionTests.java new file mode 100644 index 000000000..03a4b07e6 --- /dev/null +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/PersonDescriptionInspectionTests.java @@ -0,0 +1,35 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Priority; +import com.structurizr.assistant.Recommendation; +import com.structurizr.model.Person; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class PersonDescriptionInspectionTests { + + @Test + public void run_WithoutDescription() { + Workspace workspace = new Workspace("Name", "Description"); + Person person = workspace.getModel().addPerson("Name"); + + Recommendation recommendation = new PersonDescriptionInspection(workspace).run(person); + Assertions.assertEquals(Priority.Medium, recommendation.getPriority()); + assertEquals("structurizr.recommendations.model.person.description", recommendation.getType()); + assertEquals("Add a description to the person named \"Name\".", recommendation.getDescription()); + } + + @Test + public void run_WithDescription() { + Workspace workspace = new Workspace("Name", "Description"); + Person person = workspace.getModel().addPerson("Name", "Description"); + + Recommendation recommendation = new PersonDescriptionInspection(workspace).run(person); + assertNull(recommendation); + } + +} diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/RelationshipDescriptionInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/RelationshipDescriptionInspectionTests.java new file mode 100644 index 000000000..69b46f4af --- /dev/null +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/RelationshipDescriptionInspectionTests.java @@ -0,0 +1,57 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Priority; +import com.structurizr.assistant.Recommendation; +import com.structurizr.model.Component; +import com.structurizr.model.Container; +import com.structurizr.model.Relationship; +import com.structurizr.model.SoftwareSystem; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class RelationshipDescriptionInspectionTests { + + @Test + public void run_WithoutDescription_MediumPriority() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem a = workspace.getModel().addSoftwareSystem("A"); + SoftwareSystem b = workspace.getModel().addSoftwareSystem("B"); + Relationship relationship = a.uses(b, ""); + + Recommendation recommendation = new RelationshipDescriptionInspection(workspace).run(relationship); + Assertions.assertEquals(Priority.Medium, recommendation.getPriority()); + assertEquals("structurizr.recommendations.model.relationship.description", recommendation.getType()); + assertEquals("Add a description to the relationship between the software system named \"A\" and the software system named \"B\".", recommendation.getDescription()); + } + + @Test + public void run_WithoutDescription_LowPriority() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + Component a = container.addComponent("A"); + Component b = container.addComponent("B"); + Relationship relationship = a.uses(b, ""); + + Recommendation recommendation = new RelationshipDescriptionInspection(workspace).run(relationship); + Assertions.assertEquals(Priority.Low, recommendation.getPriority()); + assertEquals("structurizr.recommendations.model.relationship.description", recommendation.getType()); + assertEquals("Add a description to the relationship between the component named \"A\" and the component named \"B\".", recommendation.getDescription()); + } + + @Test + public void run_WithDescription() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem a = workspace.getModel().addSoftwareSystem("A"); + SoftwareSystem b = workspace.getModel().addSoftwareSystem("B"); + Relationship relationship = a.uses(b, "Description"); + + Recommendation recommendation = new RelationshipDescriptionInspection(workspace).run(relationship); + assertNull(recommendation); + } + +} diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/RelationshipTechnologyInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/RelationshipTechnologyInspectionTests.java new file mode 100644 index 000000000..2fe68d6c5 --- /dev/null +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/RelationshipTechnologyInspectionTests.java @@ -0,0 +1,55 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Priority; +import com.structurizr.assistant.Recommendation; +import com.structurizr.model.Container; +import com.structurizr.model.Relationship; +import com.structurizr.model.SoftwareSystem; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class RelationshipTechnologyInspectionTests { + + @Test + public void run_WithoutDescription_LowPriority() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem a = workspace.getModel().addSoftwareSystem("A"); + SoftwareSystem b = workspace.getModel().addSoftwareSystem("B"); + Relationship relationship = a.uses(b, "Description"); + + Recommendation recommendation = new RelationshipTechnologyInspection(workspace).run(relationship); + Assertions.assertEquals(Priority.Low, recommendation.getPriority()); + assertEquals("structurizr.recommendations.model.relationship.technology", recommendation.getType()); + assertEquals("Add a technology to the relationship between the software system named \"A\" and the software system named \"B\".", recommendation.getDescription()); + } + + @Test + public void run_WithoutDescription_MediumPriority() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); + Container a = softwareSystem.addContainer("A"); + Container b = softwareSystem.addContainer("B"); + Relationship relationship = a.uses(b, "Description"); + + Recommendation recommendation = new RelationshipTechnologyInspection(workspace).run(relationship); + Assertions.assertEquals(Priority.Medium, recommendation.getPriority()); + assertEquals("structurizr.recommendations.model.relationship.technology", recommendation.getType()); + assertEquals("Add a technology to the relationship between the container named \"A\" and the container named \"B\".", recommendation.getDescription()); + } + + @Test + public void run_WithDescription() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem a = workspace.getModel().addSoftwareSystem("A"); + SoftwareSystem b = workspace.getModel().addSoftwareSystem("B"); + Relationship relationship = a.uses(b, "Description", "Technology"); + + Recommendation recommendation = new RelationshipTechnologyInspection(workspace).run(relationship); + assertNull(recommendation); + } + +} diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/SoftwareSystemDescriptionInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/SoftwareSystemDescriptionInspectionTests.java new file mode 100644 index 000000000..49389b30d --- /dev/null +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/SoftwareSystemDescriptionInspectionTests.java @@ -0,0 +1,35 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Priority; +import com.structurizr.assistant.Recommendation; +import com.structurizr.model.SoftwareSystem; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class SoftwareSystemDescriptionInspectionTests { + + @Test + public void run_WithoutDescription() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name"); + + Recommendation recommendation = new SoftwareSystemDescriptionInspection(workspace).run(softwareSystem); + Assertions.assertEquals(Priority.Medium, recommendation.getPriority()); + assertEquals("structurizr.recommendations.model.softwaresystem.description", recommendation.getType()); + assertEquals("Add a description to the software system named \"Name\".", recommendation.getDescription()); + } + + @Test + public void run_WithDescription() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name", "Description"); + + Recommendation recommendation = new SoftwareSystemDescriptionInspection(workspace).run(softwareSystem); + assertNull(recommendation); + } + +} diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/view/ContainerViewsForMultipleSoftwareSystemsInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/view/ContainerViewsForMultipleSoftwareSystemsInspectionTests.java new file mode 100644 index 000000000..8b389b481 --- /dev/null +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/view/ContainerViewsForMultipleSoftwareSystemsInspectionTests.java @@ -0,0 +1,40 @@ +package com.structurizr.assistant.view; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Priority; +import com.structurizr.assistant.Recommendation; +import com.structurizr.model.SoftwareSystem; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class ContainerViewsForMultipleSoftwareSystemsInspectionTests { + + @Test + public void run_MultipleSoftwareSystems() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem a = workspace.getModel().addSoftwareSystem("A"); + workspace.getViews().createContainerView(a, "Containers-A", "Description"); + SoftwareSystem b = workspace.getModel().addSoftwareSystem("B"); + workspace.getViews().createContainerView(b, "Containers-B", "Description"); + + Recommendation recommendation = new ContainerViewsForMultipleSoftwareSystemsInspection(workspace).run(); + Assertions.assertEquals(Priority.High, recommendation.getPriority()); + assertEquals("structurizr.recommendations.workspace.scope", recommendation.getType()); + assertEquals("Container views exist for 2 software systems. It is recommended that a workspace includes container views for a single software system only.", recommendation.getDescription()); + } + + @Test + public void run_SingleSoftwareSystem() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem a = workspace.getModel().addSoftwareSystem("A"); + workspace.getViews().createContainerView(a, "Containers-A", "Description"); + SoftwareSystem b = workspace.getModel().addSoftwareSystem("B"); + + Recommendation recommendation = new ContainerViewsForMultipleSoftwareSystemsInspection(workspace).run(); + assertNull(recommendation); + } + +} diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/view/EmptyViewsInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/view/EmptyViewsInspectionTests.java new file mode 100644 index 000000000..2d5c9b40c --- /dev/null +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/view/EmptyViewsInspectionTests.java @@ -0,0 +1,33 @@ +package com.structurizr.assistant.view; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Priority; +import com.structurizr.assistant.Recommendation; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class EmptyViewsInspectionTests { + + @Test + public void run_WhenThereAreNoViews() { + Workspace workspace = new Workspace("Name", "Description"); + + Recommendation recommendation = new EmptyViewsInspection(workspace).run(); + Assertions.assertEquals(Priority.High, recommendation.getPriority()); + assertEquals("structurizr.recommendations.views.empty", recommendation.getType()); + assertEquals("Add some views to the workspace.", recommendation.getDescription()); + } + + @Test + public void run_WhenThereAreViews() { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getViews().createSystemLandscapeView("key", "Description"); + + Recommendation recommendation = new EmptyViewsInspection(workspace).run(); + assertNull(recommendation); + } + +} diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/view/SystemContextViewsForMultipleSoftwareSystemsInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/view/SystemContextViewsForMultipleSoftwareSystemsInspectionTests.java new file mode 100644 index 000000000..48ec21171 --- /dev/null +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/view/SystemContextViewsForMultipleSoftwareSystemsInspectionTests.java @@ -0,0 +1,40 @@ +package com.structurizr.assistant.view; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Priority; +import com.structurizr.assistant.Recommendation; +import com.structurizr.model.SoftwareSystem; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class SystemContextViewsForMultipleSoftwareSystemsInspectionTests { + + @Test + public void run_MultipleSoftwareSystems() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem a = workspace.getModel().addSoftwareSystem("A"); + workspace.getViews().createSystemContextView(a, "SystemContext-A", "Description"); + SoftwareSystem b = workspace.getModel().addSoftwareSystem("B"); + workspace.getViews().createSystemContextView(b, "SystemContext-B", "Description"); + + Recommendation recommendation = new SystemContextViewsForMultipleSoftwareSystemsInspection(workspace).run(); + Assertions.assertEquals(Priority.High, recommendation.getPriority()); + assertEquals("structurizr.recommendations.workspace.scope", recommendation.getType()); + assertEquals("System context views exist for 2 software systems. It is recommended that a workspace includes system context views for a single software system only.", recommendation.getDescription()); + } + + @Test + public void run_SingleSoftwareSystem() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem a = workspace.getModel().addSoftwareSystem("A"); + workspace.getViews().createSystemContextView(a, "SystemContext-A", "Description"); + SoftwareSystem b = workspace.getModel().addSoftwareSystem("B"); + + Recommendation recommendation = new SystemContextViewsForMultipleSoftwareSystemsInspection(workspace).run(); + assertNull(recommendation); + } + +} diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/workspace/WorkspaceScopeInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/workspace/WorkspaceScopeInspectionTests.java new file mode 100644 index 000000000..d48e93c77 --- /dev/null +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/workspace/WorkspaceScopeInspectionTests.java @@ -0,0 +1,34 @@ +package com.structurizr.assistant.workspace; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Priority; +import com.structurizr.assistant.Recommendation; +import com.structurizr.configuration.WorkspaceScope; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class WorkspaceScopeInspectionTests { + + @Test + public void run_WithUnscopedWorkspace() { + Workspace workspace = new Workspace("Name", "Description"); + + Recommendation recommendation = new WorkspaceScopeInspection(workspace).run(); + Assertions.assertEquals(Priority.High, recommendation.getPriority()); + assertEquals("structurizr.recommendations.workspace.scope", recommendation.getType()); + assertEquals("This workspace has no defined scope. It is recommended that the workspace scope is set to \"Landscape\" or \"SoftwareSystem\".", recommendation.getDescription()); + } + + @Test + public void run_WithScopedWorkspace() { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getConfiguration().setScope(WorkspaceScope.SoftwareSystem); + + Recommendation recommendation = new WorkspaceScopeInspection(workspace).run(); + assertNull(recommendation); + } + +} diff --git a/structurizr-client/src/main/java/com/structurizr/api/WorkspaceApiClient.java b/structurizr-client/src/main/java/com/structurizr/api/WorkspaceApiClient.java index 29bbb8332..d12adf3a0 100644 --- a/structurizr-client/src/main/java/com/structurizr/api/WorkspaceApiClient.java +++ b/structurizr-client/src/main/java/com/structurizr/api/WorkspaceApiClient.java @@ -296,8 +296,6 @@ public void putWorkspace(long workspaceId, Workspace workspace) throws Structuri workspace.setLastModifiedAgent(agent); workspace.setLastModifiedUser(getUser()); - workspace.countAndLogWarnings(); - HttpPut httpPut = new HttpPut(url + WORKSPACE_PATH + "/" + workspaceId); StringWriter stringWriter = new StringWriter(); diff --git a/structurizr-core/src/main/java/com/structurizr/Workspace.java b/structurizr-core/src/main/java/com/structurizr/Workspace.java index 8dd320eb7..d48ead6e1 100644 --- a/structurizr-core/src/main/java/com/structurizr/Workspace.java +++ b/structurizr-core/src/main/java/com/structurizr/Workspace.java @@ -163,74 +163,6 @@ public boolean isEmpty() { return model.isEmpty() && viewSet.isEmpty() && documentation.isEmpty(); } - /** - * Counts and logs any warnings within the workspace (e.g. missing element descriptions). - * - * @return the number of warnings - */ - public int countAndLogWarnings() { - final List warnings = new LinkedList<>(); - - // find elements with a missing description - getModel().getElements().stream() - .filter(e -> !(e instanceof SoftwareSystemInstance) && !(e instanceof ContainerInstance) && !(e instanceof DeploymentNode) && !(e instanceof InfrastructureNode)) - .filter(e -> e.getDescription() == null || e.getDescription().trim().length() == 0) - .forEach(e -> warnings.add(e.getCanonicalName() + " is missing a description.")); - - // find containers with a missing technology - getModel().getElements().stream() - .filter(e -> e instanceof Container) - .map(e -> (Container)e) - .filter(c -> c.getTechnology() == null || c.getTechnology().trim().length() == 0) - .forEach(c -> warnings.add(c.getCanonicalName() + " is missing a technology.")); - - // find components with a missing technology - getModel().getElements().stream() - .filter(e -> e instanceof Component) - .map(e -> (Component)e) - .filter(c -> c.getTechnology() == null || c.getTechnology().trim().length() == 0) - .forEach(c -> warnings.add(c.getCanonicalName() + " is missing a technology.")); - - // find component relationships with a missing description - for (Relationship relationship : getModel().getRelationships()) { - if (relationship.getSource() instanceof Component && relationship.getDestination() instanceof Component && - relationship.getSource().getParent().equals(relationship.getDestination().getParent())) { - // ignore component-component relationships inside the same container because these are - // often identified using reflection and won't have a description - // (i.e. let's not flood the user with warnings) - } else { - if (relationship.getDescription() == null || relationship.getDescription().trim().length() == 0) { - warnings.add("The relationship between " + relationship.getSource().getCanonicalName() + " and " + relationship.getDestination().getCanonicalName() + " is missing a description."); - } - } - } - - // diagram keys have not been specified - this is only applicable to - // workspaces created with the early versions of Structurizr for Java - getViews().getSystemLandscapeViews().stream() - .filter(v -> v.getKey() == null) - .forEach(v -> warnings.add("System Landscape view \"" + v.getName() + "\": Missing key")); - getViews().getSystemContextViews().stream() - .filter(v -> v.getKey() == null) - .forEach(v -> warnings.add("System Context view \"" + v.getName() + "\": Missing key")); - getViews().getContainerViews().stream() - .filter(v -> v.getKey() == null) - .forEach(v -> warnings.add("Container view \"" + v.getName() + "\": Missing key")); - getViews().getComponentViews().stream() - .filter(v -> v.getKey() == null) - .forEach(v -> warnings.add("Component view \"" + v.getName() + "\": Missing key")); - getViews().getDynamicViews().stream() - .filter(v -> v.getKey() == null) - .forEach(v -> warnings.add("Dynamic view \"" + v.getName() + "\": Missing key")); - getViews().getDeploymentViews().stream() - .filter(v -> v.getKey() == null) - .forEach(v -> warnings.add("Deployment view \"" + v.getName() + "\": Missing key")); - - warnings.forEach(log::warn); - - return warnings.size(); - } - /** * Trims the workspace by removing all unused elements. */ diff --git a/structurizr-core/src/test/java/com/structurizr/WorkspaceTests.java b/structurizr-core/src/test/java/com/structurizr/WorkspaceTests.java index 4a5230338..d0f5f979e 100644 --- a/structurizr-core/src/test/java/com/structurizr/WorkspaceTests.java +++ b/structurizr-core/src/test/java/com/structurizr/WorkspaceTests.java @@ -1,10 +1,8 @@ package com.structurizr; -import com.structurizr.configuration.WorkspaceScope; import com.structurizr.documentation.Decision; import com.structurizr.documentation.Format; import com.structurizr.model.*; -import com.structurizr.view.SystemContextView; import com.structurizr.view.SystemLandscapeView; import org.junit.jupiter.api.Test; @@ -46,24 +44,6 @@ void isEmpty_ReturnsFalse_WhenThereIsDocumentation() throws Exception { assertFalse(workspace.isEmpty()); } - @Test - void countAndLogWarnings() { - Workspace workspace = new Workspace("Name", "Description"); - SoftwareSystem softwareSystem1 = workspace.getModel().addSoftwareSystem("Software System 1", null); - SoftwareSystem softwareSystem2 = workspace.getModel().addSoftwareSystem("Software System 2", " "); - Container container1 = softwareSystem1.addContainer("Name", "Description", null); - Container container2 = softwareSystem2.addContainer("Name", "Description", " "); - container1.uses(container2, null, null); - container2.uses(container1, " ", " "); - - Component component1A = container1.addComponent("A", null, null); - Component component1B = container1.addComponent("B", "", ""); - component1A.uses(component1B, null); - component1B.uses(component1A, ""); - - assertEquals(10, workspace.countAndLogWarnings()); - } - @Test void hydrate_DoesNotCrash() { Workspace workspace = new Workspace("Name", "Description"); From ad9db0f900fe3eb075677710d08552d68e8d8a7e Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 9 Jan 2024 17:41:24 +0000 Subject: [PATCH 441/717] Missing assertion. --- .../src/test/java/com/structurizr/WorkspaceTests.java | 1 + 1 file changed, 1 insertion(+) diff --git a/structurizr-core/src/test/java/com/structurizr/WorkspaceTests.java b/structurizr-core/src/test/java/com/structurizr/WorkspaceTests.java index d0f5f979e..b5b741da1 100644 --- a/structurizr-core/src/test/java/com/structurizr/WorkspaceTests.java +++ b/structurizr-core/src/test/java/com/structurizr/WorkspaceTests.java @@ -253,6 +253,7 @@ void trim_WhenAllElementsAreUnused() { workspace.trim(); assertEquals(0, workspace.getModel().getElements().size()); + assertEquals(0, workspace.getModel().getRelationships().size()); } @Test From abdc7ec3bdeeb3775f13385704bbc2b4b4e7cc25 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 10 Jan 2024 08:34:31 +0000 Subject: [PATCH 442/717] Removes the deprecated "location" and "enterprise" concept. --- changelog.md | 1 + .../java/com/structurizr/model/Location.java | 8 - .../java/com/structurizr/model/Model.java | 74 +- .../java/com/structurizr/model/Person.java | 8 +- .../com/structurizr/model/SoftwareSystem.java | 8 +- .../com/structurizr/view/ComponentView.java | 13 +- .../com/structurizr/view/ContainerView.java | 13 +- .../com/structurizr/view/DynamicView.java | 13 +- .../structurizr/view/SystemContextView.java | 12 +- .../structurizr/view/SystemLandscapeView.java | 12 +- .../com/structurizr/view/Terminology.java | 2 +- .../java/com/structurizr/view/ViewSet.java | 4 - .../com/structurizr/model/PersonTests.java | 7 - .../view/SystemContextViewTests.java | 8 - .../view/SystemLandscapeViewTests.java | 8 - .../structurizr/dsl/ComponentViewParser.java | 1 - .../structurizr/dsl/ContainerViewParser.java | 1 - .../structurizr/dsl/DynamicViewParser.java | 2 - .../structurizr/dsl/EnterpriseDslContext.java | 23 - .../com/structurizr/dsl/EnterpriseParser.java | 30 - .../com/structurizr/dsl/ModelDslContext.java | 10 - .../com/structurizr/dsl/PersonParser.java | 5 - .../structurizr/dsl/SoftwareSystemParser.java | 5 - .../structurizr/dsl/StructurizrDslParser.java | 18 +- .../structurizr/dsl/StructurizrDslTokens.java | 1 - .../dsl/SystemContextViewParser.java | 5 +- .../dsl/SystemLandscapeViewParser.java | 5 +- .../dsl/TerminologyDslContext.java | 1 - .../structurizr/dsl/TerminologyParser.java | 9 - .../dsl/ComponentViewParserTests.java | 3 - .../dsl/ContainerViewParserTests.java | 3 - .../java/com/structurizr/dsl/DslTests.java | 16 - .../dsl/EnterpriseParserTests.java | 63 - .../structurizr/dsl/ModelDslContextTests.java | 77 - .../structurizr/dsl/PersonParserTests.java | 20 +- .../dsl/SoftwareSystemParserTests.java | 20 +- .../dsl/TerminologyParserTests.java | 17 - .../src/test/resources/dsl/big-bank-plc.dsl | 2 +- .../model/people-and-software-systems.dsl | 2 +- .../src/test/resources/dsl/groups-nested.dsl | 8 - .../src/test/resources/dsl/test.dsl | 3 +- .../export/AbstractDiagramExporter.java | 68 +- .../export/plantuml/C4PlantUMLExporter.java | 16 +- .../export/dot/36141-Components.dot | 48 +- .../export/dot/36141-Containers.dot | 34 +- .../dot/36141-DevelopmentDeployment.dot | 42 +- .../export/dot/36141-LiveDeployment.dot | 58 +- .../structurizr/export/dot/36141-SignIn.dot | 22 +- .../export/dot/36141-SystemContext.dot | 25 +- .../export/dot/36141-SystemLandscape.dot | 39 +- .../export/dot/groups-Components.dot | 4 +- .../export/dot/groups-Containers.dot | 4 +- .../export/dot/groups-SystemLandscape.dot | 41 +- .../export/ilograph/36141.ilograph | 428 +- .../export/mermaid/36141-Components.mmd | 68 +- .../export/mermaid/36141-Containers.mmd | 46 +- .../mermaid/36141-DevelopmentDeployment.mmd | 48 +- .../export/mermaid/36141-LiveDeployment.mmd | 72 +- .../export/mermaid/36141-SignIn-sequence.mmd | 20 +- .../export/mermaid/36141-SignIn.mmd | 32 +- .../export/mermaid/36141-SystemContext.mmd | 25 +- .../export/mermaid/36141-SystemLandscape.mmd | 40 +- .../export/mermaid/groups-Components.mmd | 2 +- .../export/mermaid/groups-Containers.mmd | 2 +- .../export/mermaid/groups-SystemLandscape.mmd | 24 +- ...ructurizrPlantUMLDiagramExporterTests.java | 6 +- .../plantuml/c4plantuml/36141-Components.puml | 12 +- .../plantuml/c4plantuml/36141-Containers.puml | 18 +- .../36141-DevelopmentDeployment.puml | 26 +- .../c4plantuml/36141-LiveDeployment.puml | 32 +- .../c4plantuml/36141-SignIn-sequence.puml | 4 +- .../plantuml/c4plantuml/36141-SignIn.puml | 6 +- .../c4plantuml/36141-SystemContext.puml | 14 +- .../c4plantuml/36141-SystemLandscape.puml | 19 +- .../c4plantuml/groups-Components.puml | 4 +- .../c4plantuml/groups-Containers.puml | 4 +- .../c4plantuml/groups-SystemLandscape.puml | 21 +- .../structurizr/36141-Components.puml | 12 +- .../structurizr/36141-Containers.puml | 12 +- .../36141-DevelopmentDeployment.puml | 14 +- .../structurizr/36141-LiveDeployment.puml | 16 +- .../plantuml/structurizr/36141-SignIn.puml | 6 +- .../structurizr/36141-SystemContext.puml | 13 +- .../structurizr/36141-SystemLandscape.puml | 19 +- .../structurizr/groups-Components.puml | 2 +- .../structurizr/groups-Containers.puml | 2 +- .../structurizr/groups-SystemLandscape.puml | 29 +- .../src/test/resources/groups.dsl | 59 + .../src/test/resources/groups.json | 142 +- .../structurizr-36141-workspace.json | 3549 ++++++++--------- .../graphviz/DOTExporterTests.java | 91 +- .../structurizr/graphviz/SVGReaderTests.java | 4 +- 92 files changed, 2495 insertions(+), 3390 deletions(-) delete mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/EnterpriseDslContext.java delete mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/EnterpriseParser.java delete mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/EnterpriseParserTests.java delete mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/ModelDslContextTests.java create mode 100644 structurizr-export/src/test/resources/groups.dsl diff --git a/changelog.md b/changelog.md index cc90bf20c..7e95c9fb2 100644 --- a/changelog.md +++ b/changelog.md @@ -3,4 +3,5 @@ ## 2.0.0 (unreleased) - structurizr-core: Adds `Workspace.trim()` to trim a workspace of unused elements (i.e. those not associated with any views). +- structurizr-core: Removes the deprecated location and enterprise concepts from `Model`. diff --git a/structurizr-core/src/main/java/com/structurizr/model/Location.java b/structurizr-core/src/main/java/com/structurizr/model/Location.java index e216c7f14..29b0193bc 100644 --- a/structurizr-core/src/main/java/com/structurizr/model/Location.java +++ b/structurizr-core/src/main/java/com/structurizr/model/Location.java @@ -1,13 +1,5 @@ package com.structurizr.model; -/** - * Represents the location of an element with regards to a specific viewpoint. - * - * Diagram renderers may use this information in a different way, but generally it will be used to mark - * an element as being outside of the enterprise boundary. For example, "our customers are external to our enterprise": - * - https://github.com/structurizr/examples/blob/main/java/src/main/java/com/structurizr/example/bigbankplc/BigBankPlc.java#L36 - * - https://structurizr.com/share/28201/diagrams#SystemLandscape - */ public enum Location { Internal, diff --git a/structurizr-core/src/main/java/com/structurizr/model/Model.java b/structurizr-core/src/main/java/com/structurizr/model/Model.java index 73ac3a65d..866faf7d0 100644 --- a/structurizr-core/src/main/java/com/structurizr/model/Model.java +++ b/structurizr-core/src/main/java/com/structurizr/model/Model.java @@ -33,23 +33,13 @@ public final class Model implements PropertyHolder { Model() { } - /** - * Gets the enterprise associated with this model. - * - * @return an Enterprise instance, or null if one has not been set - */ + @Deprecated public Enterprise getEnterprise() { return enterprise; } - /** - * Sets the enterprise associated with this model. - * This is typically used in conjunction with {@link Location}. - * - * @param enterprise an Enterprise instance - */ @Deprecated - public void setEnterprise(Enterprise enterprise) { + void setEnterprise(Enterprise enterprise) { this.enterprise = enterprise; } @@ -75,36 +65,6 @@ public SoftwareSystem addSoftwareSystem(@Nonnull String name) { public SoftwareSystem addSoftwareSystem(@Nonnull String name, @Nullable String description) { if (getSoftwareSystemWithName(name) == null) { SoftwareSystem softwareSystem = new SoftwareSystem(); - softwareSystem.setLocation(Location.Unspecified); - softwareSystem.setName(name); - softwareSystem.setDescription(description); - - softwareSystems.add(softwareSystem); - - softwareSystem.setId(idGenerator.generateId(softwareSystem)); - addElementToInternalStructures(softwareSystem); - - return softwareSystem; - } else { - throw new IllegalArgumentException("A top-level element named '" + name + "' already exists."); - } - } - - /** - * Creates a software system and adds it to the model. - * - * @param location the location of the software system (e.g. internal, external, etc) - * @param name the name of the software system - * @param description a short description of the software system - * @return the SoftwareSystem instance created and added to the model (or null) - * @throws IllegalArgumentException if a software system with the same name already exists - */ - @Nonnull - @Deprecated - public SoftwareSystem addSoftwareSystem(@Nullable Location location, @Nonnull String name, @Nullable String description) { - if (getSoftwareSystemWithName(name) == null) { - SoftwareSystem softwareSystem = new SoftwareSystem(); - softwareSystem.setLocation(location); softwareSystem.setName(name); softwareSystem.setDescription(description); @@ -143,36 +103,6 @@ public Person addPerson(@Nonnull String name) { public Person addPerson(@Nonnull String name, @Nullable String description) { if (getPersonWithName(name) == null) { Person person = new Person(); - person.setLocation(Location.Unspecified); - person.setName(name); - person.setDescription(description); - - people.add(person); - - person.setId(idGenerator.generateId(person)); - addElementToInternalStructures(person); - - return person; - } else { - throw new IllegalArgumentException("A top-level element named '" + name + "' already exists."); - } - } - - /** - * Creates a person and adds it to the model. - * - * @param location the location of the person (e.g. internal, external, etc) - * @param name the name of the person (e.g. "Admin User" or "Bob the Business User") - * @param description a short description of the person - * @return the Person instance created and added to the model (or null) - * @throws IllegalArgumentException if a person with the same name already exists - */ - @Nonnull - @Deprecated - public Person addPerson(Location location, @Nonnull String name, @Nullable String description) { - if (getPersonWithName(name) == null) { - Person person = new Person(); - person.setLocation(location); person.setName(name); person.setDescription(description); diff --git a/structurizr-core/src/main/java/com/structurizr/model/Person.java b/structurizr-core/src/main/java/com/structurizr/model/Person.java index 858463bae..dd9916897 100644 --- a/structurizr-core/src/main/java/com/structurizr/model/Person.java +++ b/structurizr-core/src/main/java/com/structurizr/model/Person.java @@ -23,17 +23,13 @@ public Element getParent() { Person() { } - /** - * Gets the location of this person. - * - * @return a Location - */ + @Deprecated public Location getLocation() { return location; } @Deprecated - public void setLocation(Location location) { + void setLocation(Location location) { if (location != null) { this.location = location; } else { diff --git a/structurizr-core/src/main/java/com/structurizr/model/SoftwareSystem.java b/structurizr-core/src/main/java/com/structurizr/model/SoftwareSystem.java index 6b0135397..74cf5d85d 100644 --- a/structurizr-core/src/main/java/com/structurizr/model/SoftwareSystem.java +++ b/structurizr-core/src/main/java/com/structurizr/model/SoftwareSystem.java @@ -36,11 +36,7 @@ public Element getParent() { SoftwareSystem() { } - /** - * Gets the location of this software system. - * - * @return a Location instance - */ + @Deprecated public Location getLocation() { return location; } @@ -51,7 +47,7 @@ public Location getLocation() { * @param location a Location instance */ @Deprecated - public void setLocation(Location location) { + void setLocation(Location location) { if (location != null) { this.location = location; } else { diff --git a/structurizr-core/src/main/java/com/structurizr/view/ComponentView.java b/structurizr-core/src/main/java/com/structurizr/view/ComponentView.java index f3eb65b7a..d03756b37 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/ComponentView.java +++ b/structurizr-core/src/main/java/com/structurizr/view/ComponentView.java @@ -294,22 +294,13 @@ protected boolean canBeRemoved(Element element) { return true; } - /** - * Determines whether container boundaries should be visible for "external" components (those outside the container in scope). - * - * @return true if external container boundaries are visible, false otherwise - */ + @Deprecated public boolean getExternalContainerBoundariesVisible() { return externalContainerBoundariesVisible; } - /** - * Sets whether container boundaries should be visible for "external" components (those outside the container in scope). - * - * @param externalContainerBoundariesVisible true if external container boundaries should be visible, false otherwise - */ @Deprecated - public void setExternalSoftwareSystemBoundariesVisible(boolean externalContainerBoundariesVisible) { + void setExternalSoftwareSystemBoundariesVisible(boolean externalContainerBoundariesVisible) { this.externalContainerBoundariesVisible = externalContainerBoundariesVisible; } diff --git a/structurizr-core/src/main/java/com/structurizr/view/ContainerView.java b/structurizr-core/src/main/java/com/structurizr/view/ContainerView.java index 3536b96fd..b4d02fb9a 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/ContainerView.java +++ b/structurizr-core/src/main/java/com/structurizr/view/ContainerView.java @@ -200,22 +200,13 @@ protected boolean canBeRemoved(Element element) { return true; } - /** - * Determines whether software system boundaries should be visible for "external" containers (those outside the software system in scope). - * - * @return true if external software system boundaries are visible, false otherwise - */ + @Deprecated public boolean getExternalSoftwareSystemBoundariesVisible() { return externalSoftwareSystemBoundariesVisible; } - /** - * Sets whether software system boundaries should be visible for "external" containers (those outside the software system in scope). - * - * @param externalSoftwareSystemBoundariesVisible true if external software system boundaries should be visible, false otherwise - */ @Deprecated - public void setExternalSoftwareSystemBoundariesVisible(boolean externalSoftwareSystemBoundariesVisible) { + void setExternalSoftwareSystemBoundariesVisible(boolean externalSoftwareSystemBoundariesVisible) { this.externalSoftwareSystemBoundariesVisible = externalSoftwareSystemBoundariesVisible; } diff --git a/structurizr-core/src/main/java/com/structurizr/view/DynamicView.java b/structurizr-core/src/main/java/com/structurizr/view/DynamicView.java index 79331d3ce..04d0fbdfb 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/DynamicView.java +++ b/structurizr-core/src/main/java/com/structurizr/view/DynamicView.java @@ -372,22 +372,13 @@ private boolean isNumeric(String str) { } } - /** - * Determines whether software system/container boundaries should be visible for "external" containers/components (those outside the element in scope). - * - * @return true if external boundaries are visible, false otherwise - */ + @Deprecated public boolean getExternalBoundariesVisible() { return externalBoundariesVisible; } - /** - * Sets whether software system/container boundaries should be visible for "external" containers/components (those outside the element in scope). - * - * @param externalBoundariesVisible true if external boundaries should be visible, false otherwise - */ @Deprecated - public void setExternalBoundariesVisible(boolean externalBoundariesVisible) { + void setExternalBoundariesVisible(boolean externalBoundariesVisible) { this.externalBoundariesVisible = externalBoundariesVisible; } diff --git a/structurizr-core/src/main/java/com/structurizr/view/SystemContextView.java b/structurizr-core/src/main/java/com/structurizr/view/SystemContextView.java index a96595596..bef228fd8 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/SystemContextView.java +++ b/structurizr-core/src/main/java/com/structurizr/view/SystemContextView.java @@ -72,23 +72,13 @@ public void addNearestNeighbours(@Nonnull Element element) { } } - /** - * Determines whether the enterprise boundary (to differentiate "internal" elements from "external" elements") should be visible on the resulting diagram. - * - * @return true if the enterprise boundary is visible, false otherwise - */ @Deprecated public boolean isEnterpriseBoundaryVisible() { return enterpriseBoundaryVisible; } - /** - * Sets whether the enterprise boundary (to differentiate "internal" elements from "external" elements") should be visible on the resulting diagram. - * - * @param enterpriseBoundaryVisible true if the enterprise boundary should be visible, false otherwise - */ @Deprecated - public void setEnterpriseBoundaryVisible(boolean enterpriseBoundaryVisible) { + void setEnterpriseBoundaryVisible(boolean enterpriseBoundaryVisible) { this.enterpriseBoundaryVisible = enterpriseBoundaryVisible; } diff --git a/structurizr-core/src/main/java/com/structurizr/view/SystemLandscapeView.java b/structurizr-core/src/main/java/com/structurizr/view/SystemLandscapeView.java index d040911fe..ce719e07f 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/SystemLandscapeView.java +++ b/structurizr-core/src/main/java/com/structurizr/view/SystemLandscapeView.java @@ -88,23 +88,13 @@ public void addNearestNeighbours(@Nonnull Element element) { } } - /** - * Determines whether the enterprise boundary (to differentiate "internal" elements from "external" elements") should be visible on the resulting diagram. - * - * @return true if the enterprise boundary is visible, false otherwise - */ @Deprecated public boolean isEnterpriseBoundaryVisible() { return enterpriseBoundaryVisible; } - /** - * Sets whether the enterprise boundary (to differentiate "internal" elements from "external" elements") should be visible on the resulting diagram. - * - * @param enterpriseBoundaryVisible true if the enterprise boundary should be visible, false otherwise - */ @Deprecated - public void setEnterpriseBoundaryVisible(boolean enterpriseBoundaryVisible) { + void setEnterpriseBoundaryVisible(boolean enterpriseBoundaryVisible) { this.enterpriseBoundaryVisible = enterpriseBoundaryVisible; } diff --git a/structurizr-core/src/main/java/com/structurizr/view/Terminology.java b/structurizr-core/src/main/java/com/structurizr/view/Terminology.java index f9c0fbc2b..b8daf3e1e 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/Terminology.java +++ b/structurizr-core/src/main/java/com/structurizr/view/Terminology.java @@ -41,7 +41,7 @@ public String getEnterprise() { } @Deprecated - public void setEnterprise(String enterprise) { + void setEnterprise(String enterprise) { this.enterprise = enterprise; } diff --git a/structurizr-core/src/main/java/com/structurizr/view/ViewSet.java b/structurizr-core/src/main/java/com/structurizr/view/ViewSet.java index 79559dd62..f33690f93 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/ViewSet.java +++ b/structurizr-core/src/main/java/com/structurizr/view/ViewSet.java @@ -854,7 +854,6 @@ public void createDefaultViews() { SystemLandscapeView systemLandscapeView = createSystemLandscapeView(generateViewKey(SYSTEM_LANDSCAPE_VIEW_TYPE), ""); systemLandscapeView.addDefaultElements(); systemLandscapeView.enableAutomaticLayout(AutomaticLayout.RankDirection.TopBottom, 300, 300); - systemLandscapeView.setEnterpriseBoundaryVisible(true); if (!model.getSoftwareSystems().isEmpty()) { List softwareSystems = new ArrayList<>(model.getSoftwareSystems()); @@ -866,7 +865,6 @@ public void createDefaultViews() { SystemContextView systemContextView = createSystemContextView(softwareSystem, systemContextViewKey, ""); systemContextView.addDefaultElements(); systemContextView.enableAutomaticLayout(AutomaticLayout.RankDirection.TopBottom, 300, 300); - systemContextView.setEnterpriseBoundaryVisible(true); if (softwareSystem.getContainers().size() > 0) { List containers = new ArrayList<>(softwareSystem.getContainers()); @@ -876,7 +874,6 @@ public void createDefaultViews() { ContainerView containerView = createContainerView(softwareSystem, containerViewKey, ""); containerView.addDefaultElements(); containerView.enableAutomaticLayout(AutomaticLayout.RankDirection.TopBottom, 300, 300); - containerView.setExternalSoftwareSystemBoundariesVisible(true); for (Container container : containers) { if (container.getComponents().size() > 0) { @@ -884,7 +881,6 @@ public void createDefaultViews() { ComponentView componentView = createComponentView(container, componentViewKey, ""); componentView.addDefaultElements(); componentView.enableAutomaticLayout(AutomaticLayout.RankDirection.TopBottom, 300, 300); - componentView.setExternalSoftwareSystemBoundariesVisible(true); } } } diff --git a/structurizr-core/src/test/java/com/structurizr/model/PersonTests.java b/structurizr-core/src/test/java/com/structurizr/model/PersonTests.java index 4a7460364..63fd8a6ee 100644 --- a/structurizr-core/src/test/java/com/structurizr/model/PersonTests.java +++ b/structurizr-core/src/test/java/com/structurizr/model/PersonTests.java @@ -87,11 +87,4 @@ void interactsWith_AddsARelationshipWhenTheDescriptionAndTechnologyAndInteractio assertEquals(InteractionStyle.Asynchronous, relationship.getInteractionStyle()); } - @Test - void setLocation_SetsTheLocationToUnspecified_WhenNullIsPassed() { - Person person = model.addPerson("Person", "Description"); - person.setLocation(null); - assertEquals(Location.Unspecified, person.getLocation()); - } - } \ No newline at end of file diff --git a/structurizr-core/src/test/java/com/structurizr/view/SystemContextViewTests.java b/structurizr-core/src/test/java/com/structurizr/view/SystemContextViewTests.java index 9de221b2f..87f5a631a 100644 --- a/structurizr-core/src/test/java/com/structurizr/view/SystemContextViewTests.java +++ b/structurizr-core/src/test/java/com/structurizr/view/SystemContextViewTests.java @@ -272,14 +272,6 @@ void addPersonWithoutRelationships_DoesNotAddRelationships() { assertEquals(0, view.getRelationships().size()); } - @Test - void isEnterpriseBoundaryVisible() { - assertTrue(view.isEnterpriseBoundaryVisible()); // default is true - - view.setEnterpriseBoundaryVisible(false); - assertFalse(view.isEnterpriseBoundaryVisible()); - } - @Test void addDefaultElements() { CustomElement element = model.addCustomElement("Custom"); diff --git a/structurizr-core/src/test/java/com/structurizr/view/SystemLandscapeViewTests.java b/structurizr-core/src/test/java/com/structurizr/view/SystemLandscapeViewTests.java index dade9b288..274afed64 100644 --- a/structurizr-core/src/test/java/com/structurizr/view/SystemLandscapeViewTests.java +++ b/structurizr-core/src/test/java/com/structurizr/view/SystemLandscapeViewTests.java @@ -82,14 +82,6 @@ void addAllElements_AddsAllSoftwareSystemsAndPeople_WhenThereAreSomeSoftwareSyst assertTrue(view.getElements().contains(new ElementView(person))); } - @Test - void isEnterpriseBoundaryVisible() { - assertTrue(view.isEnterpriseBoundaryVisible()); // default is true - - view.setEnterpriseBoundaryVisible(false); - assertFalse(view.isEnterpriseBoundaryVisible()); - } - @Test void addNearestNeighbours_ThrowsAnException_WhenANullElementIsSpecified() { try { diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentViewParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentViewParser.java index 3acd88669..b0e1207df 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentViewParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentViewParser.java @@ -54,7 +54,6 @@ ComponentView parse(DslContext context, Tokens tokens) { } ComponentView view = workspace.getViews().createComponentView(container, key, description); - view.setExternalSoftwareSystemBoundariesVisible(true); return view; } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerViewParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerViewParser.java index b1033eb43..60ea0e7d7 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerViewParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerViewParser.java @@ -54,7 +54,6 @@ ContainerView parse(DslContext context, Tokens tokens) { } ContainerView view = workspace.getViews().createContainerView(softwareSystem, key, description); - view.setExternalSoftwareSystemBoundariesVisible(true); return view; } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DynamicViewParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DynamicViewParser.java index 78b23103e..461dee1fe 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/DynamicViewParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DynamicViewParser.java @@ -81,8 +81,6 @@ DynamicView parse(DslContext context, Tokens tokens) { } } - view.setExternalBoundariesVisible(true); - return view; } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/EnterpriseDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/EnterpriseDslContext.java deleted file mode 100644 index 3fe12598e..000000000 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/EnterpriseDslContext.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.structurizr.dsl; - -final class EnterpriseDslContext extends GroupableDslContext { - - EnterpriseDslContext() { - super(); - } - - EnterpriseDslContext(ElementGroup group) { - super(group); - } - - @Override - protected String[] getPermittedTokens() { - return new String[] { - StructurizrDslTokens.GROUP_TOKEN, - StructurizrDslTokens.PERSON_TOKEN, - StructurizrDslTokens.SOFTWARE_SYSTEM_TOKEN, - StructurizrDslTokens.RELATIONSHIP_TOKEN - }; - } - -} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/EnterpriseParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/EnterpriseParser.java deleted file mode 100644 index 54cbd3bfb..000000000 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/EnterpriseParser.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.structurizr.dsl; - -import com.structurizr.Workspace; -import com.structurizr.model.Enterprise; - -final class EnterpriseParser extends AbstractParser { - - private static final String GRAMMAR = "enterprise [name]"; - - private static final int NAME_INDEX = 1; - - void parse(DslContext context, Tokens tokens) { - Workspace workspace = context.getWorkspace(); - - if (tokens.hasMoreThan(NAME_INDEX)) { - throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); - } else if (tokens.includes(NAME_INDEX)) { - String name = tokens.get(1); - - if (workspace.getModel().getEnterprise() == null) { - workspace.getModel().setEnterprise(new Enterprise(name)); - } else if (!name.equals(workspace.getModel().getEnterprise().getName())) { - throw new RuntimeException("The name of the enterprise has already been set"); - } - } else { - // do nothing ... this will just create an EnterpriseDslContext, so that people and software systems can be marked as "internal" - } - } - -} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelDslContext.java index e803d4fea..f03fbb426 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelDslContext.java @@ -12,19 +12,9 @@ final class ModelDslContext extends GroupableDslContext { super(group); } - @Override - void end() { - // the location is only set to internal when created inside an "enterprise" block, let's assume the rest are external if an enterprise has been specified - if (getWorkspace().getModel().getEnterprise() != null) { - getWorkspace().getModel().getPeople().stream().filter(p -> p.getLocation() != Location.Internal).forEach(p -> p.setLocation(Location.External)); - getWorkspace().getModel().getSoftwareSystems().stream().filter(ss -> ss.getLocation() != Location.Internal).forEach(ss -> ss.setLocation(Location.External)); - } - } - @Override protected String[] getPermittedTokens() { return new String[] { - StructurizrDslTokens.ENTERPRISE_TOKEN, StructurizrDslTokens.GROUP_TOKEN, StructurizrDslTokens.PERSON_TOKEN, StructurizrDslTokens.SOFTWARE_SYSTEM_TOKEN, diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonParser.java index 9ec33bfa6..c2103af51 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonParser.java @@ -1,6 +1,5 @@ package com.structurizr.dsl; -import com.structurizr.model.Location; import com.structurizr.model.Person; final class PersonParser extends AbstractParser { @@ -44,10 +43,6 @@ Person parse(GroupableDslContext context, Tokens tokens) { person.addTags(tags.split(",")); } - if (context instanceof EnterpriseDslContext) { - person.setLocation(Location.Internal); - } - if (context.hasGroup()) { person.setGroup(context.getGroup().getName()); context.getGroup().addElement(person); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemParser.java index 9e3214bbd..46cc0c9c4 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemParser.java @@ -1,6 +1,5 @@ package com.structurizr.dsl; -import com.structurizr.model.Location; import com.structurizr.model.SoftwareSystem; final class SoftwareSystemParser extends AbstractParser { @@ -44,10 +43,6 @@ SoftwareSystem parse(GroupableDslContext context, Tokens tokens) { softwareSystem.addTags(tags.split(",")); } - if (context instanceof EnterpriseDslContext) { - softwareSystem.setLocation(Location.Internal); - } - if (context.hasGroup()) { softwareSystem.setGroup(context.getGroup().getName()); context.getGroup().addElement(softwareSystem); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index 17fbedb4d..db957889f 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -305,7 +305,7 @@ void parse(List lines, File dslFile) throws StructurizrDslParserExceptio } else if (inContext(ExternalScriptDslContext.class)) { new ScriptParser().parseParameter(getContext(ExternalScriptDslContext.class), tokens); - } else if (tokens.size() > 2 && RELATIONSHIP_TOKEN.equals(tokens.get(1)) && (inContext(ModelDslContext.class) || inContext(EnterpriseDslContext.class) || inContext(CustomElementDslContext.class) || inContext(PersonDslContext.class) || inContext(SoftwareSystemDslContext.class) || inContext(ContainerDslContext.class) || inContext(ComponentDslContext.class) || inContext(DeploymentEnvironmentDslContext.class) || inContext(DeploymentNodeDslContext.class) || inContext(InfrastructureNodeDslContext.class) || inContext(SoftwareSystemInstanceDslContext.class) || inContext(ContainerInstanceDslContext.class))) { + } else if (tokens.size() > 2 && RELATIONSHIP_TOKEN.equals(tokens.get(1)) && (inContext(ModelDslContext.class) || inContext(CustomElementDslContext.class) || inContext(PersonDslContext.class) || inContext(SoftwareSystemDslContext.class) || inContext(ContainerDslContext.class) || inContext(ComponentDslContext.class) || inContext(DeploymentEnvironmentDslContext.class) || inContext(DeploymentNodeDslContext.class) || inContext(InfrastructureNodeDslContext.class) || inContext(SoftwareSystemInstanceDslContext.class) || inContext(ContainerInstanceDslContext.class))) { Relationship relationship = new ExplicitRelationshipParser().parse(getContext(), tokens.withoutContextStartToken()); if (shouldStartContext(tokens)) { @@ -367,7 +367,7 @@ void parse(List lines, File dslFile) throws StructurizrDslParserExceptio registerIdentifier(identifier, customElement); - } else if (PERSON_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ModelDslContext.class) || inContext(EnterpriseDslContext.class))) { + } else if (PERSON_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ModelDslContext.class))) { Person person = new PersonParser().parse(getContext(GroupableDslContext.class), tokens.withoutContextStartToken()); if (shouldStartContext(tokens)) { @@ -376,7 +376,7 @@ void parse(List lines, File dslFile) throws StructurizrDslParserExceptio registerIdentifier(identifier, person); - } else if (SOFTWARE_SYSTEM_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ModelDslContext.class) || inContext(EnterpriseDslContext.class))) { + } else if (SOFTWARE_SYSTEM_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ModelDslContext.class))) { SoftwareSystem softwareSystem = new SoftwareSystemParser().parse(getContext(GroupableDslContext.class), tokens.withoutContextStartToken()); if (shouldStartContext(tokens)) { @@ -408,11 +408,6 @@ void parse(List lines, File dslFile) throws StructurizrDslParserExceptio startContext(new ModelDslContext(group)); registerIdentifier(identifier, group); - } else if (GROUP_TOKEN.equalsIgnoreCase(firstToken) && inContext(EnterpriseDslContext.class)) { - ElementGroup group = new GroupParser().parse(getContext(EnterpriseDslContext.class), tokens); - - startContext(new EnterpriseDslContext(group)); - registerIdentifier(identifier, group); } else if (GROUP_TOKEN.equalsIgnoreCase(firstToken) && inContext(SoftwareSystemDslContext.class)) { ElementGroup group = new GroupParser().parse(getContext(SoftwareSystemDslContext.class), tokens); @@ -618,10 +613,6 @@ void parse(List lines, File dslFile) throws StructurizrDslParserExceptio } else if (RELATIONSHIP_STYLE_ROUTING_TOKEN.equalsIgnoreCase(firstToken) && inContext(RelationshipStyleDslContext.class)) { new RelationshipStyleParser().parseRouting(getContext(RelationshipStyleDslContext.class), tokens); - } else if (ENTERPRISE_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelDslContext.class)) { - new EnterpriseParser().parse(getContext(), tokens.withoutContextStartToken()); - startContext(new EnterpriseDslContext()); - } else if (DEPLOYMENT_ENVIRONMENT_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelDslContext.class)) { String environment = new DeploymentEnvironmentParser().parse(tokens.withoutContextStartToken()); @@ -815,9 +806,6 @@ void parse(List lines, File dslFile) throws StructurizrDslParserExceptio } else if (TERMINOLOGY_TOKEN.equalsIgnoreCase(firstToken) && inContext(ViewsDslContext.class)) { startContext(new TerminologyDslContext()); - } else if (ENTERPRISE_TOKEN.equalsIgnoreCase(firstToken) && inContext(TerminologyDslContext.class)) { - new TerminologyParser().parseEnterprise(getContext(), tokens); - } else if (PERSON_TOKEN.equalsIgnoreCase(firstToken) && inContext(TerminologyDslContext.class)) { new TerminologyParser().parsePerson(getContext(), tokens); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java index 946185e21..acae127aa 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java @@ -27,7 +27,6 @@ class StructurizrDslTokens { static final String SCOPE_TOKEN = "scope"; static final String MODEL_TOKEN = "model"; static final String VIEWS_TOKEN = "views"; - static final String ENTERPRISE_TOKEN = "enterprise"; static final String DEPLOYMENT_ENVIRONMENT_TOKEN = "deploymentEnvironment"; static final String DEPLOYMENT_GROUP_TOKEN = "deploymentGroup"; static final String DEPLOYMENT_NODE_TOKEN = "deploymentNode"; diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/SystemContextViewParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/SystemContextViewParser.java index 4dc004a95..d4a1330f0 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/SystemContextViewParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/SystemContextViewParser.java @@ -53,10 +53,7 @@ SystemContextView parse(DslContext context, Tokens tokens) { description = tokens.get(DESCRIPTION_INDEX); } - SystemContextView view = workspace.getViews().createSystemContextView(softwareSystem, key, description); - view.setEnterpriseBoundaryVisible(true); - - return view; + return workspace.getViews().createSystemContextView(softwareSystem, key, description); } } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/SystemLandscapeViewParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/SystemLandscapeViewParser.java index e03919f72..344f2a3d3 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/SystemLandscapeViewParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/SystemLandscapeViewParser.java @@ -34,10 +34,7 @@ SystemLandscapeView parse(DslContext context, Tokens tokens) { description = tokens.get(DESCRIPTION_INDEX); } - SystemLandscapeView view = workspace.getViews().createSystemLandscapeView(key, description); - view.setEnterpriseBoundaryVisible(true); - - return view; + return workspace.getViews().createSystemLandscapeView(key, description); } } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/TerminologyDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/TerminologyDslContext.java index 27e69e8fb..be42d7490 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/TerminologyDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/TerminologyDslContext.java @@ -5,7 +5,6 @@ final class TerminologyDslContext extends DslContext { @Override protected String[] getPermittedTokens() { return new String[] { - StructurizrDslTokens.ENTERPRISE_TOKEN, StructurizrDslTokens.PERSON_TOKEN, StructurizrDslTokens.SOFTWARE_SYSTEM_TOKEN, StructurizrDslTokens.CONTAINER_TOKEN, diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/TerminologyParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/TerminologyParser.java index b189f5d86..d95e2409c 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/TerminologyParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/TerminologyParser.java @@ -4,15 +4,6 @@ final class TerminologyParser extends AbstractParser { private final static int TERM_INDEX = 1; - void parseEnterprise(DslContext context, Tokens tokens) { - // enterprise - if (!tokens.includes(TERM_INDEX)) { - throw new RuntimeException("Expected: enterprise "); - } - - context.getWorkspace().getViews().getConfiguration().getTerminology().setEnterprise(tokens.get(TERM_INDEX)); - } - void parsePerson(DslContext context, Tokens tokens) { // person if (!tokens.includes(TERM_INDEX)) { diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentViewParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentViewParserTests.java index a67d1d6d9..aac6950e9 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentViewParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentViewParserTests.java @@ -73,7 +73,6 @@ void test_parse_CreatesAComponentView() { assertEquals(1, views.size()); assertEquals("Component-001", views.get(0).getKey()); assertEquals("", views.get(0).getDescription()); - assertTrue(views.get(0).getExternalContainerBoundariesVisible()); } @Test @@ -89,7 +88,6 @@ void test_parse_CreatesAComponentViewWithAKey() { assertEquals(1, views.size()); assertEquals("key", views.get(0).getKey()); assertEquals("", views.get(0).getDescription()); - assertTrue(views.get(0).getExternalContainerBoundariesVisible()); } @Test @@ -105,7 +103,6 @@ void test_parse_CreatesAComponentViewWithAKeyAndDescription() { assertEquals(1, views.size()); assertEquals("key", views.get(0).getKey()); assertEquals("Description", views.get(0).getDescription()); - assertTrue(views.get(0).getExternalContainerBoundariesVisible()); } } \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ContainerViewParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ContainerViewParserTests.java index 3044d5900..5d24c2a9d 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ContainerViewParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ContainerViewParserTests.java @@ -73,7 +73,6 @@ void test_parse_CreatesAContainerView() { assertEquals(1, views.size()); assertEquals("Container-001", views.get(0).getKey()); assertEquals("", views.get(0).getDescription()); - assertTrue(views.get(0).getExternalSoftwareSystemBoundariesVisible()); } @Test @@ -89,7 +88,6 @@ void test_parse_CreatesAContainerViewWithAKey() { assertEquals(1, views.size()); assertEquals("key", views.get(0).getKey()); assertEquals("", views.get(0).getDescription()); - assertTrue(views.get(0).getExternalSoftwareSystemBoundariesVisible()); } @Test @@ -105,7 +103,6 @@ void test_parse_CreatesAContainerViewWithAKeyAndDescription() { assertEquals(1, views.size()); assertEquals("key", views.get(0).getKey()); assertEquals("Description", views.get(0).getDescription()); - assertTrue(views.get(0).getExternalSoftwareSystemBoundariesVisible()); } } \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index b8f2002b8..ed76f3eff 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -24,7 +24,6 @@ void test_test() throws Exception { parser.parse(new File("src/test/resources/dsl/test.dsl")); assertFalse(parser.getWorkspace().isEmpty()); - assertEquals("Organisation - Group", parser.getWorkspace().getModel().getEnterprise().getName()); } @Test @@ -147,10 +146,6 @@ void test_bigbankplc() throws Exception { Workspace workspace = parser.getWorkspace(); - assertEquals(Location.External, workspace.getModel().getPersonWithName("Personal Banking Customer").getLocation()); - assertEquals(Location.Internal, workspace.getModel().getPersonWithName("Customer Service Staff").getLocation()); - assertEquals(Location.Internal, workspace.getModel().getPersonWithName("Back Office Staff").getLocation()); - assertEquals(51, workspace.getModel().getElements().size()); assertEquals(3, workspace.getModel().getPeople().size()); assertEquals(4, workspace.getModel().getSoftwareSystems().size()); @@ -208,10 +203,6 @@ void test_bigbankplc_systemlandscape() throws Exception { Workspace workspace = parser.getWorkspace(); - assertEquals(Location.External, workspace.getModel().getPersonWithName("Personal Banking Customer").getLocation()); - assertEquals(Location.Internal, workspace.getModel().getPersonWithName("Customer Service Staff").getLocation()); - assertEquals(Location.Internal, workspace.getModel().getPersonWithName("Back Office Staff").getLocation()); - assertEquals(7, workspace.getModel().getElements().size()); assertEquals(3, workspace.getModel().getPeople().size()); assertEquals(4, workspace.getModel().getSoftwareSystems().size()); @@ -241,10 +232,6 @@ void test_bigbankplc_internetbankingsystem() throws Exception { Workspace workspace = parser.getWorkspace(); - assertEquals(Location.External, workspace.getModel().getPersonWithName("Personal Banking Customer").getLocation()); - assertEquals(Location.Internal, workspace.getModel().getPersonWithName("Customer Service Staff").getLocation()); - assertEquals(Location.Internal, workspace.getModel().getPersonWithName("Back Office Staff").getLocation()); - assertEquals(51, workspace.getModel().getElements().size()); assertEquals(3, workspace.getModel().getPeople().size()); assertEquals(4, workspace.getModel().getSoftwareSystems().size()); @@ -603,9 +590,6 @@ void test_nested_groups() throws Exception { SoftwareSystem c = parser.getWorkspace().getModel().getSoftwareSystemWithName("C"); assertEquals("Organisation", c.getGroup()); - - SoftwareSystem d = parser.getWorkspace().getModel().getSoftwareSystemWithName("D"); - assertEquals("Department A/Team 1", d.getGroup()); } @Test diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/EnterpriseParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/EnterpriseParserTests.java deleted file mode 100644 index fa28b7cec..000000000 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/EnterpriseParserTests.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.structurizr.dsl; - -import com.structurizr.model.Enterprise; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.*; - -class EnterpriseParserTests extends AbstractTests { - - private EnterpriseParser parser = new EnterpriseParser(); - - @Test - void test_parse_SetsTheEnterpriseName_WhenItHasNotBeenSet() { - assertNull(workspace.getModel().getEnterprise()); - parser.parse(context(), tokens("enterprise", "New Name")); - assertEquals("New Name", workspace.getModel().getEnterprise().getName()); - } - - @Test - void test_parse_ThrowsAnException_WhenTheEnterpriseNameHasAlreadyBeenSet() { - workspace.getModel().setEnterprise(new Enterprise("My Enterprise")); - try { - parser.parse(context(), tokens("enterprise", "name")); - fail(); - } catch (Exception e) { - assertEquals("The name of the enterprise has already been set", e.getMessage()); - } - } - - @Test - void test_parse_DoesNothing_WhenANameIsSpecifiedButIsTheSameAsTheExistingEnterpriseName() { - workspace.getModel().setEnterprise(new Enterprise("My Enterprise")); - parser.parse(context(), tokens("My Enterprise")); - - assertEquals("My Enterprise", workspace.getModel().getEnterprise().getName()); - } - - @Test - void test_parse_DoesNothing_WhenNoNameIsSpecified() { - parser.parse(context(), tokens("enterprise")); - - assertNull(workspace.getModel().getEnterprise()); - } - - @Test - void test_parse_DoesNothing_WhenNoNameIsSpecifiedAndTheEnterpriseNameHasAlreadyBeenSet() { - workspace.getModel().setEnterprise(new Enterprise("My Enterprise")); - parser.parse(context(), tokens("enterprise")); - - assertEquals("My Enterprise", workspace.getModel().getEnterprise().getName()); - } - - @Test - void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { - try { - parser.parse(context(), tokens("enterprise", "name", "extra")); - fail(); - } catch (Exception e) { - assertEquals("Too many tokens, expected: enterprise [name]", e.getMessage()); - } - } - -} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ModelDslContextTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ModelDslContextTests.java deleted file mode 100644 index 08e976738..000000000 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ModelDslContextTests.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.structurizr.dsl; - -import com.structurizr.model.Enterprise; -import com.structurizr.model.Location; -import com.structurizr.model.Person; -import com.structurizr.model.SoftwareSystem; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -class ModelDslContextTests extends AbstractTests { - - @Test - void end_DoesNothing_WhenNoPeopleAreMarkedAsInternal() { - ModelDslContext context = new ModelDslContext(); - context.setWorkspace(workspace); - - Person user1 = workspace.getModel().addPerson("Name 1"); - Person user2 = workspace.getModel().addPerson("Name 2"); - assertEquals(Location.Unspecified, user1.getLocation()); - assertEquals(Location.Unspecified, user2.getLocation()); - - context.end(); - assertEquals(Location.Unspecified, user1.getLocation()); - assertEquals(Location.Unspecified, user2.getLocation()); - } - - @Test - void end_MarksAllOtherPeopleAsExternal_WhenSomePeopleAreMarkedAsInternal() { - ModelDslContext context = new ModelDslContext(); - context.setWorkspace(workspace); - workspace.getModel().setEnterprise(new Enterprise("Name")); - - Person user1 = workspace.getModel().addPerson("Name 1"); - Person user2 = workspace.getModel().addPerson("Name 2"); - user2.setLocation(Location.Internal); - assertEquals(Location.Unspecified, user1.getLocation()); - assertEquals(Location.Internal, user2.getLocation()); - - context.end(); - assertEquals(Location.External, user1.getLocation()); - assertEquals(Location.Internal, user2.getLocation()); - } - - @Test - void end_DoesNothing_WhenNoSoftwareSystemsAreMarkedAsInternal() { - ModelDslContext context = new ModelDslContext(); - context.setWorkspace(workspace); - - SoftwareSystem softwareSystem1 = workspace.getModel().addSoftwareSystem("Name 1"); - SoftwareSystem softwareSystem2 = workspace.getModel().addSoftwareSystem("Name 2"); - assertEquals(Location.Unspecified, softwareSystem1.getLocation()); - assertEquals(Location.Unspecified, softwareSystem2.getLocation()); - - context.end(); - assertEquals(Location.Unspecified, softwareSystem1.getLocation()); - assertEquals(Location.Unspecified, softwareSystem2.getLocation()); - } - - @Test - void end_MarksAllOtherSoftwareSystemsAsExternal_WhenSomeSoftwareSystemsAreMarkedAsInternal() { - ModelDslContext context = new ModelDslContext(); - context.setWorkspace(workspace); - workspace.getModel().setEnterprise(new Enterprise("Name")); - - SoftwareSystem softwareSystem1 = workspace.getModel().addSoftwareSystem("Name 1"); - SoftwareSystem softwareSystem2 = workspace.getModel().addSoftwareSystem("Name 2"); - softwareSystem1.setLocation(Location.Internal); - assertEquals(Location.Internal, softwareSystem1.getLocation()); - assertEquals(Location.Unspecified, softwareSystem2.getLocation()); - - context.end(); - assertEquals(Location.Internal, softwareSystem1.getLocation()); - assertEquals(Location.External, softwareSystem2.getLocation()); - } - -} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/PersonParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/PersonParserTests.java index f24c1c20d..ed6454623 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/PersonParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/PersonParserTests.java @@ -1,6 +1,5 @@ package com.structurizr.dsl; -import com.structurizr.model.Location; import com.structurizr.model.Person; import org.junit.jupiter.api.Test; @@ -8,7 +7,7 @@ class PersonParserTests extends AbstractTests { - private PersonParser parser = new PersonParser(); + private final PersonParser parser = new PersonParser(); @Test void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { @@ -38,7 +37,6 @@ void test_parse_CreatesAPerson() { Person user = model.getPersonWithName("User"); assertNotNull(user); assertEquals("", user.getDescription()); - assertEquals(Location.Unspecified, user.getLocation()); assertEquals("Element,Person", user.getTags()); } @@ -50,7 +48,6 @@ void test_parse_CreatesAPersonWithADescription() { Person user = model.getPersonWithName("User"); assertNotNull(user); assertEquals("Description", user.getDescription()); - assertEquals(Location.Unspecified, user.getLocation()); assertEquals("Element,Person", user.getTags()); } @@ -62,22 +59,7 @@ void test_parse_CreatesAPersonWithADescriptionAndTags() { Person user = model.getPersonWithName("User"); assertNotNull(user); assertEquals("Description", user.getDescription()); - assertEquals(Location.Unspecified, user.getLocation()); assertEquals("Element,Person,Tag 1,Tag 2", user.getTags()); } - @Test - void test_parse_CreatesAnInternalPerson() { - EnterpriseDslContext context = new EnterpriseDslContext(); - context.setWorkspace(workspace); - parser.parse(context, tokens("person", "User")); - - assertEquals(1, model.getElements().size()); - Person user = model.getPersonWithName("User"); - assertNotNull(user); - assertEquals("", user.getDescription()); - assertEquals(Location.Internal, user.getLocation()); - assertEquals("Element,Person", user.getTags()); - } - } \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/SoftwareSystemParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/SoftwareSystemParserTests.java index adbc2dfa8..d11724335 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/SoftwareSystemParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/SoftwareSystemParserTests.java @@ -1,6 +1,5 @@ package com.structurizr.dsl; -import com.structurizr.model.Location; import com.structurizr.model.SoftwareSystem; import org.junit.jupiter.api.Test; @@ -8,7 +7,7 @@ class SoftwareSystemParserTests extends AbstractTests { - private SoftwareSystemParser parser = new SoftwareSystemParser(); + private final SoftwareSystemParser parser = new SoftwareSystemParser(); @Test void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { @@ -38,7 +37,6 @@ void test_parse_CreatesASoftwareSystem() { SoftwareSystem softwareSystem = model.getSoftwareSystemWithName("Name"); assertNotNull(softwareSystem); assertEquals("", softwareSystem.getDescription()); - assertEquals(Location.Unspecified, softwareSystem.getLocation()); assertEquals("Element,Software System", softwareSystem.getTags()); } @@ -50,7 +48,6 @@ void test_parse_CreatesASoftwareSystemWithADescription() { SoftwareSystem softwareSystem = model.getSoftwareSystemWithName("Name"); assertNotNull(softwareSystem); assertEquals("Description", softwareSystem.getDescription()); - assertEquals(Location.Unspecified, softwareSystem.getLocation()); assertEquals("Element,Software System", softwareSystem.getTags()); } @@ -62,22 +59,7 @@ void test_parse_CreatesASoftwareSystemWithADescriptionAndTags() { SoftwareSystem softwareSystem = model.getSoftwareSystemWithName("Name"); assertNotNull(softwareSystem); assertEquals("Description", softwareSystem.getDescription()); - assertEquals(Location.Unspecified, softwareSystem.getLocation()); assertEquals("Element,Software System,Tag 1,Tag 2", softwareSystem.getTags()); } - @Test - void test_parse_CreatesAnInternalSoftwareSystem() { - EnterpriseDslContext context = new EnterpriseDslContext(); - context.setWorkspace(workspace); - parser.parse(context, tokens("softwareSystem", "Name")); - - assertEquals(1, model.getElements().size()); - SoftwareSystem softwareSystem = model.getSoftwareSystemWithName("Name"); - assertNotNull(softwareSystem); - assertEquals("", softwareSystem.getDescription()); - assertEquals(Location.Internal, softwareSystem.getLocation()); - assertEquals("Element,Software System", softwareSystem.getTags()); - } - } \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/TerminologyParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/TerminologyParserTests.java index dbff12115..5d4592627 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/TerminologyParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/TerminologyParserTests.java @@ -9,23 +9,6 @@ class TerminologyParserTests extends AbstractTests { private TerminologyParser parser = new TerminologyParser(); - @Test - void test_parseEnterprise_ThrowsAnException_WhenNoTermIsSpecified() { - try { - parser.parseEnterprise(context(), tokens("enterprise")); - fail(); - } catch (Exception e) { - assertEquals("Expected: enterprise ", e.getMessage()); - } - } - - @Test - void test_parseEnterprise_SetsTheTerm_WhenOneIsSpecified() { - parser.parseEnterprise(context(), tokens("enterprise", "TERM")); - - assertEquals("TERM", workspace.getViews().getConfiguration().getTerminology().getEnterprise()); - } - @Test void test_parsePerson_ThrowsAnException_WhenNoTermIsSpecified() { try { diff --git a/structurizr-dsl/src/test/resources/dsl/big-bank-plc.dsl b/structurizr-dsl/src/test/resources/dsl/big-bank-plc.dsl index 2e6783160..6cca65aed 100644 --- a/structurizr-dsl/src/test/resources/dsl/big-bank-plc.dsl +++ b/structurizr-dsl/src/test/resources/dsl/big-bank-plc.dsl @@ -9,7 +9,7 @@ workspace "Big Bank plc" "This is an example workspace to illustrate the key fea model { customer = person "Personal Banking Customer" "A customer of the bank, with personal bank accounts." "Customer" - enterprise "Big Bank plc" { + group "Big Bank plc" { supportStaff = person "Customer Service Staff" "Customer service staff within the bank." "Bank Staff" backoffice = person "Back Office Staff" "Administration and support staff within the bank." "Bank Staff" diff --git a/structurizr-dsl/src/test/resources/dsl/big-bank-plc/model/people-and-software-systems.dsl b/structurizr-dsl/src/test/resources/dsl/big-bank-plc/model/people-and-software-systems.dsl index b4ff925b0..d5d051be7 100644 --- a/structurizr-dsl/src/test/resources/dsl/big-bank-plc/model/people-and-software-systems.dsl +++ b/structurizr-dsl/src/test/resources/dsl/big-bank-plc/model/people-and-software-systems.dsl @@ -1,6 +1,6 @@ customer = person "Personal Banking Customer" "A customer of the bank, with personal bank accounts." "Customer" -enterprise "Big Bank plc" { +group "Big Bank plc" { supportStaff = person "Customer Service Staff" "Customer service staff within the bank." "Bank Staff" backoffice = person "Back Office Staff" "Administration and support staff within the bank." "Bank Staff" diff --git a/structurizr-dsl/src/test/resources/dsl/groups-nested.dsl b/structurizr-dsl/src/test/resources/dsl/groups-nested.dsl index 4e98e49a3..47ac91617 100644 --- a/structurizr-dsl/src/test/resources/dsl/groups-nested.dsl +++ b/structurizr-dsl/src/test/resources/dsl/groups-nested.dsl @@ -27,14 +27,6 @@ workspace { c = softwareSystem "C" } - - enterprise "Enterprise" { - group "Department A" { - group "Team 1" { - d = softwareSystem "D" - } - } - } } views { diff --git a/structurizr-dsl/src/test/resources/dsl/test.dsl b/structurizr-dsl/src/test/resources/dsl/test.dsl index cab02b522..4748da87f 100644 --- a/structurizr-dsl/src/test/resources/dsl/test.dsl +++ b/structurizr-dsl/src/test/resources/dsl/test.dsl @@ -39,7 +39,7 @@ workspace "Name" "Description" { } } - enterprise "${ORGANISATION_NAME} - ${GROUP_NAME}" { + group "${ORGANISATION_NAME} - ${GROUP_NAME}" { softwareSystem = softwareSystem "Software System" "Description" "Tag" { webApplication = container "Web Application" "Description" "Technology" "Tag" { homePageController = component "HomePageController" "Description" "Spring MVC Controller" "Tag" { @@ -317,7 +317,6 @@ workspace "Name" "Description" { } terminology { - enterprise "Enterprise" person "Person" softwareSystem "Software System" container "Container" diff --git a/structurizr-export/src/main/java/com/structurizr/export/AbstractDiagramExporter.java b/structurizr-export/src/main/java/com/structurizr/export/AbstractDiagramExporter.java index 9c2202bd8..b06c62f5a 100644 --- a/structurizr-export/src/main/java/com/structurizr/export/AbstractDiagramExporter.java +++ b/structurizr-export/src/main/java/com/structurizr/export/AbstractDiagramExporter.java @@ -130,7 +130,21 @@ public Diagram export(SystemLandscapeView view) { private Diagram export(SystemLandscapeView view, Integer animationStep) { this.frame = animationStep; - return export(view, view.isEnterpriseBoundaryVisible()); + + IndentingWriter writer = new IndentingWriter(); + writeHeader(view, writer); + + List elements = new ArrayList<>(); + for (ElementView elementView : view.getElements()) { + elements.add((GroupableElement)elementView.getElement()); + } + writeElements(view, elements, writer); + + writer.writeLine(); + writeRelationships(view, writer); + writeFooter(view, writer); + + return createDiagram(view, writer.toString()); } public Diagram export(SystemContextView view) { @@ -149,59 +163,15 @@ public Diagram export(SystemContextView view) { private Diagram export(SystemContextView view, Integer animationStep) { this.frame = animationStep; - return export(view, view.isEnterpriseBoundaryVisible()); - } - private Diagram export(ModelView view, boolean enterpriseBoundaryIsVisible) { IndentingWriter writer = new IndentingWriter(); writeHeader(view, writer); - boolean showEnterpriseBoundary = - enterpriseBoundaryIsVisible && - (view.getElements().stream().map(ElementView::getElement).anyMatch(e -> e instanceof Person && ((Person)e).getLocation() == Location.Internal) || - view.getElements().stream().map(ElementView::getElement).anyMatch(e -> e instanceof SoftwareSystem && ((SoftwareSystem)e).getLocation() == Location.Internal)); - - if (showEnterpriseBoundary) { - String enterpriseName = "Enterprise"; - if (view.getModel().getEnterprise() != null) { - enterpriseName = view.getModel().getEnterprise().getName(); - } - - startEnterpriseBoundary(view, enterpriseName, writer); - - List elementsInsideEnterpriseBoundary = new ArrayList<>(); - for (ElementView elementView : view.getElements()) { - if (elementView.getElement() instanceof Person && ((Person)elementView.getElement()).getLocation() == Location.Internal) { - elementsInsideEnterpriseBoundary.add((StaticStructureElement)elementView.getElement()); - } - if (elementView.getElement() instanceof SoftwareSystem && ((SoftwareSystem)elementView.getElement()).getLocation() == Location.Internal) { - elementsInsideEnterpriseBoundary.add((StaticStructureElement)elementView.getElement()); - } - } - writeElements(view, elementsInsideEnterpriseBoundary, writer); - - endEnterpriseBoundary(view, writer); - - List elementsOutsideEnterpriseBoundary = new ArrayList<>(); - for (ElementView elementView : view.getElements()) { - if (elementView.getElement() instanceof Person && ((Person)elementView.getElement()).getLocation() != Location.Internal) { - elementsOutsideEnterpriseBoundary.add((StaticStructureElement)elementView.getElement()); - } - if (elementView.getElement() instanceof SoftwareSystem && ((SoftwareSystem)elementView.getElement()).getLocation() != Location.Internal) { - elementsOutsideEnterpriseBoundary.add((StaticStructureElement)elementView.getElement()); - } - if (elementView.getElement() instanceof CustomElement) { - elementsOutsideEnterpriseBoundary.add((CustomElement)elementView.getElement()); - } - } - writeElements(view, elementsOutsideEnterpriseBoundary, writer); - } else { - List elements = new ArrayList<>(); - for (ElementView elementView : view.getElements()) { - elements.add((GroupableElement)elementView.getElement()); - } - writeElements(view, elements, writer); + List elements = new ArrayList<>(); + for (ElementView elementView : view.getElements()) { + elements.add((GroupableElement)elementView.getElement()); } + writeElements(view, elements, writer); writer.writeLine(); writeRelationships(view, writer); diff --git a/structurizr-export/src/main/java/com/structurizr/export/plantuml/C4PlantUMLExporter.java b/structurizr-export/src/main/java/com/structurizr/export/plantuml/C4PlantUMLExporter.java index bf67727da..fa74211fe 100644 --- a/structurizr-export/src/main/java/com/structurizr/export/plantuml/C4PlantUMLExporter.java +++ b/structurizr-export/src/main/java/com/structurizr/export/plantuml/C4PlantUMLExporter.java @@ -493,27 +493,19 @@ protected void writeElement(ModelView view, Element element, IndentingWriter wri if (element instanceof Person) { Person person = (Person)element; - String location = ""; - if (person.getLocation() == Location.External) { - location = "_Ext"; - } // Person(alias, label, ?descr, ?sprite, ?tags, ?link, ?type) writer.writeLine( - String.format("Person%s(%s, \"%s\", $descr=\"%s\", $tags=\"%s\", $link=\"%s\")", - location, id, name, description, tagsOf(view, elementToWrite), url) + String.format("Person(%s, \"%s\", $descr=\"%s\", $tags=\"%s\", $link=\"%s\")", + id, name, description, tagsOf(view, elementToWrite), url) ); } else if (element instanceof SoftwareSystem) { SoftwareSystem softwareSystem = (SoftwareSystem)element; - String location = ""; - if (softwareSystem.getLocation() == Location.External) { - location = "_Ext"; - } // System(alias, label, ?descr, ?sprite, ?tags, ?link, ?type) writer.writeLine( - String.format("System%s(%s, \"%s\", $descr=\"%s\", $tags=\"%s\", $link=\"%s\")", - location, id, name, description, tagsOf(view, elementToWrite), url) + String.format("System(%s, \"%s\", $descr=\"%s\", $tags=\"%s\", $link=\"%s\")", + id, name, description, tagsOf(view, elementToWrite), url) ); } else if (element instanceof Container) { Container container = (Container)element; diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/36141-Components.dot b/structurizr-export/src/test/java/com/structurizr/export/dot/36141-Components.dot index 76c5ff5e9..b2869d886 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/dot/36141-Components.dot +++ b/structurizr-export/src/test/java/com/structurizr/export/dot/36141-Components.dot @@ -6,12 +6,12 @@ digraph { label=<
    Internet Banking System - API Application - Components
    The component diagram for the API Application.> 4 [id=4,shape=rect, label=<Mainframe Banking
    System

    [Software System]

    Stores all of the core banking
    information about customers,
    accounts, transactions, etc.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] - 17 [id=17,shape=rect, label=<Single-Page
    Application

    [Container: JavaScript and Angular]

    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] - 6 [id=6,shape=rect, label=<E-mail System
    [Software System]

    The internal Microsoft
    Exchange e-mail system.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] - 18 [id=18,shape=rect, label=<Mobile App
    [Container: Xamarin]

    Provides a limited subset of
    the Internet banking
    functionality to customers via
    their mobile device.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] - 21 [id=21,shape=cylinder, label=<Database
    [Container: Oracle Database Schema]

    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + 5 [id=5,shape=rect, label=<E-mail System
    [Software System]

    The internal Microsoft
    Exchange e-mail system.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] + 18 [id=18,shape=cylinder, label=<Database
    [Container: Oracle Database Schema]

    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + 8 [id=8,shape=rect, label=<Single-Page
    Application

    [Container: JavaScript and Angular]

    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + 9 [id=9,shape=rect, label=<Mobile App
    [Container: Xamarin]

    Provides a limited subset of
    the Internet banking
    functionality to customers via
    their mobile device.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] - subgraph cluster_20 { + subgraph cluster_11 { margin=25 label=<
    API Application

    [Container: Java and Spring MVC]> labelloc=b @@ -19,25 +19,25 @@ digraph { fontcolor="#444444" fillcolor="#444444" - 29 [id=29,shape=rect, label=<Sign In Controller
    [Component: Spring MVC Rest Controller]

    Allows users to sign in to the
    Internet Banking System.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] - 30 [id=30,shape=rect, label=<Accounts Summary
    Controller

    [Component: Spring MVC Rest Controller]

    Provides customers with a
    summary of their bank
    accounts.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] - 31 [id=31,shape=rect, label=<Reset Password
    Controller

    [Component: Spring MVC Rest Controller]

    Allows users to reset their
    passwords with a single use
    URL.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] - 32 [id=32,shape=rect, label=<Security Component
    [Component: Spring Bean]

    Provides functionality related
    to signing in, changing
    passwords, etc.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] - 33 [id=33,shape=rect, label=<Mainframe Banking
    System Facade

    [Component: Spring Bean]

    A facade onto the mainframe
    banking system.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] - 34 [id=34,shape=rect, label=<E-mail Component
    [Component: Spring Bean]

    Sends e-mails to users.>, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] + 12 [id=12,shape=rect, label=<Sign In Controller
    [Component: Spring MVC Rest Controller]

    Allows users to sign in to the
    Internet Banking System.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] + 13 [id=13,shape=rect, label=<Accounts Summary
    Controller

    [Component: Spring MVC Rest Controller]

    Provides customers with a
    summary of their bank
    accounts.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] + 14 [id=14,shape=rect, label=<Reset Password
    Controller

    [Component: Spring MVC Rest Controller]

    Allows users to reset their
    passwords with a single use
    URL.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] + 15 [id=15,shape=rect, label=<Security Component
    [Component: Spring Bean]

    Provides functionality related
    to signing in, changing
    passwords, etc.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] + 16 [id=16,shape=rect, label=<Mainframe Banking
    System Facade

    [Component: Spring Bean]

    A facade onto the mainframe
    banking system.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] + 17 [id=17,shape=rect, label=<E-mail Component
    [Component: Spring Bean]

    Sends e-mails to users.>, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] } - 17 -> 29 [id=35, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] - 17 -> 31 [id=37, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] - 17 -> 30 [id=38, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] - 18 -> 29 [id=39, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] - 18 -> 31 [id=41, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] - 18 -> 30 [id=42, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] - 29 -> 32 [id=43, label=<Uses>, style="dashed", color="#707070", fontcolor="#707070"] - 30 -> 33 [id=44, label=<Uses>, style="dashed", color="#707070", fontcolor="#707070"] - 31 -> 32 [id=45, label=<Uses>, style="dashed", color="#707070", fontcolor="#707070"] - 31 -> 34 [id=46, label=<Uses>, style="dashed", color="#707070", fontcolor="#707070"] - 32 -> 21 [id=47, label=<Reads from and writes to
    [JDBC]>, style="dashed", color="#707070", fontcolor="#707070"] - 33 -> 4 [id=48, label=<Uses
    [XML/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] - 34 -> 6 [id=49, label=<Sends e-mail using>, style="dashed", color="#707070", fontcolor="#707070"] + 8 -> 12 [id=32, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] + 8 -> 13 [id=34, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] + 8 -> 14 [id=35, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] + 9 -> 12 [id=36, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] + 9 -> 13 [id=38, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] + 9 -> 14 [id=39, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] + 12 -> 15 [id=40, label=<Uses>, style="dashed", color="#707070", fontcolor="#707070"] + 13 -> 16 [id=41, label=<Uses>, style="dashed", color="#707070", fontcolor="#707070"] + 14 -> 15 [id=42, label=<Uses>, style="dashed", color="#707070", fontcolor="#707070"] + 14 -> 17 [id=43, label=<Uses>, style="dashed", color="#707070", fontcolor="#707070"] + 15 -> 18 [id=44, label=<Reads from and writes to
    [SQL/TCP]>, style="dashed", color="#707070", fontcolor="#707070"] + 16 -> 4 [id=46, label=<Makes API calls to
    [XML/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] + 17 -> 5 [id=48, label=<Sends e-mail using>, style="dashed", color="#707070", fontcolor="#707070"] } \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/36141-Containers.dot b/structurizr-export/src/test/java/com/structurizr/export/dot/36141-Containers.dot index c4548fdc9..a0deff955 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/dot/36141-Containers.dot +++ b/structurizr-export/src/test/java/com/structurizr/export/dot/36141-Containers.dot @@ -7,9 +7,9 @@ digraph { 1 [id=1,shape=rect, label=<Personal Banking
    Customer

    [Person]

    A customer of the bank, with
    personal bank accounts.
    >, style=filled, color="#052e56", fillcolor="#08427b", fontcolor="#ffffff"] 4 [id=4,shape=rect, label=<Mainframe Banking
    System

    [Software System]

    Stores all of the core banking
    information about customers,
    accounts, transactions, etc.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] - 6 [id=6,shape=rect, label=<E-mail System
    [Software System]

    The internal Microsoft
    Exchange e-mail system.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] + 5 [id=5,shape=rect, label=<E-mail System
    [Software System]

    The internal Microsoft
    Exchange e-mail system.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] - subgraph cluster_2 { + subgraph cluster_7 { margin=25 label=<
    Internet Banking System

    [Software System]> labelloc=b @@ -17,21 +17,21 @@ digraph { fontcolor="#444444" fillcolor="#444444" - 17 [id=17,shape=rect, label=<Single-Page
    Application

    [Container: JavaScript and Angular]

    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] - 18 [id=18,shape=rect, label=<Mobile App
    [Container: Xamarin]

    Provides a limited subset of
    the Internet banking
    functionality to customers via
    their mobile device.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] - 19 [id=19,shape=rect, label=<Web Application
    [Container: Java and Spring MVC]

    Delivers the static content
    and the Internet banking
    single page application.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] - 20 [id=20,shape=rect, label=<API Application
    [Container: Java and Spring MVC]

    Provides Internet banking
    functionality via a JSON/HTTPS
    API.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] - 21 [id=21,shape=cylinder, label=<Database
    [Container: Oracle Database Schema]

    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + 10 [id=10,shape=rect, label=<Web Application
    [Container: Java and Spring MVC]

    Delivers the static content
    and the Internet banking
    single page application.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + 11 [id=11,shape=rect, label=<API Application
    [Container: Java and Spring MVC]

    Provides Internet banking
    functionality via a JSON/HTTPS
    API.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + 18 [id=18,shape=cylinder, label=<Database
    [Container: Oracle Database Schema]

    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + 8 [id=8,shape=rect, label=<Single-Page
    Application

    [Container: JavaScript and Angular]

    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + 9 [id=9,shape=rect, label=<Mobile App
    [Container: Xamarin]

    Provides a limited subset of
    the Internet banking
    functionality to customers via
    their mobile device.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] } - 1 -> 19 [id=22, label=<Visits bigbank.com/ib
    using

    [HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] - 1 -> 17 [id=23, label=<Views account balances,
    and makes payments using
    >, style="dashed", color="#707070", fontcolor="#707070"] - 1 -> 18 [id=24, label=<Views account balances,
    and makes payments using
    >, style="dashed", color="#707070", fontcolor="#707070"] - 19 -> 17 [id=25, label=<Delivers to the customer's
    web browser
    >, style="dashed", color="#707070", fontcolor="#707070"] - 20 -> 21 [id=26, label=<Reads from and writes to
    [JDBC]>, style="dashed", color="#707070", fontcolor="#707070"] - 20 -> 4 [id=27, label=<Makes API calls to
    [XML/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] - 20 -> 6 [id=28, label=<Sends e-mail using
    [SMTP]>, style="dashed", color="#707070", fontcolor="#707070"] - 17 -> 20 [id=36, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] - 18 -> 20 [id=40, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] - 6 -> 1 [id=8, label=<Sends e-mails to>, style="dashed", color="#707070", fontcolor="#707070"] + 5 -> 1 [id=22, label=<Sends e-mails to>, style="dashed", color="#707070", fontcolor="#707070"] + 1 -> 10 [id=28, label=<Visits bigbank.com/ib
    using

    [HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] + 1 -> 8 [id=29, label=<Views account balances,
    and makes payments using
    >, style="dashed", color="#707070", fontcolor="#707070"] + 1 -> 9 [id=30, label=<Views account balances,
    and makes payments using
    >, style="dashed", color="#707070", fontcolor="#707070"] + 10 -> 8 [id=31, label=<Delivers to the customer's
    web browser
    >, style="dashed", color="#707070", fontcolor="#707070"] + 8 -> 11 [id=33, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] + 9 -> 11 [id=37, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] + 11 -> 18 [id=45, label=<Reads from and writes to
    [SQL/TCP]>, style="dashed", color="#707070", fontcolor="#707070"] + 11 -> 4 [id=47, label=<Makes API calls to
    [XML/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] + 11 -> 5 [id=49, label=<Sends e-mail using>, style="dashed", color="#707070", fontcolor="#707070"] } \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/36141-DevelopmentDeployment.dot b/structurizr-export/src/test/java/com/structurizr/export/dot/36141-DevelopmentDeployment.dot index 0b86e2c80..784eaeae4 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/dot/36141-DevelopmentDeployment.dot +++ b/structurizr-export/src/test/java/com/structurizr/export/dot/36141-DevelopmentDeployment.dot @@ -14,6 +14,17 @@ digraph { fillcolor="#ffffff" subgraph cluster_51 { + margin=25 + label=<Web Browser
    [Deployment Node: Chrome, Firefox, Safari, or Edge]> + labelloc=b + color="#888888" + fontcolor="#000000" + fillcolor="#ffffff" + + 52 [id=52,shape=rect, label=<Single-Page
    Application

    [Container: JavaScript and Angular]

    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + } + + subgraph cluster_53 { margin=25 label=<Docker Container - Web Server
    [Deployment Node: Docker]> labelloc=b @@ -21,7 +32,7 @@ digraph { fontcolor="#000000" fillcolor="#ffffff" - subgraph cluster_52 { + subgraph cluster_54 { margin=25 label=<Apache Tomcat
    [Deployment Node: Apache Tomcat 8.x]> labelloc=b @@ -29,8 +40,8 @@ digraph { fontcolor="#000000" fillcolor="#ffffff" - 53 [id=53,shape=rect, label=<Web Application
    [Container: Java and Spring MVC]

    Delivers the static content
    and the Internet banking
    single page application.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] - 54 [id=54,shape=rect, label=<API Application
    [Container: Java and Spring MVC]

    Provides Internet banking
    functionality via a JSON/HTTPS
    API.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + 55 [id=55,shape=rect, label=<Web Application
    [Container: Java and Spring MVC]

    Delivers the static content
    and the Internet banking
    single page application.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + 57 [id=57,shape=rect, label=<API Application
    [Container: Java and Spring MVC]

    Provides Internet banking
    functionality via a JSON/HTTPS
    API.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] } } @@ -56,20 +67,9 @@ digraph { } - subgraph cluster_63 { - margin=25 - label=<Web Browser
    [Deployment Node: Chrome, Firefox, Safari, or Edge]> - labelloc=b - color="#888888" - fontcolor="#000000" - fillcolor="#ffffff" - - 64 [id=64,shape=rect, label=<Single-Page
    Application

    [Container: JavaScript and Angular]

    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] - } - } - subgraph cluster_55 { + subgraph cluster_63 { margin=25 label=<Big Bank plc
    [Deployment Node: Big Bank plc data center]> labelloc=b @@ -77,7 +77,7 @@ digraph { fontcolor="#000000" fillcolor="#ffffff" - subgraph cluster_56 { + subgraph cluster_64 { margin=25 label=<bigbank-dev001
    [Deployment Node]> labelloc=b @@ -85,13 +85,13 @@ digraph { fontcolor="#000000" fillcolor="#ffffff" - 57 [id=57,shape=rect, label=<Mainframe Banking
    System

    [Software System]

    Stores all of the core banking
    information about customers,
    accounts, transactions, etc.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] + 65 [id=65,shape=rect, label=<Mainframe Banking
    System

    [Software System]

    Stores all of the core banking
    information about customers,
    accounts, transactions, etc.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] } } - 54 -> 57 [id=58, label=<Makes API calls to
    [XML/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] - 54 -> 61 [id=62, label=<Reads from and writes to
    [JDBC]>, style="dashed", color="#707070", fontcolor="#707070"] - 64 -> 54 [id=65, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] - 53 -> 64 [id=66, label=<Delivers to the customer's
    web browser
    >, style="dashed", color="#707070", fontcolor="#707070"] + 55 -> 52 [id=56, label=<Delivers to the customer's
    web browser
    >, style="dashed", color="#707070", fontcolor="#707070"] + 52 -> 57 [id=58, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] + 57 -> 61 [id=62, label=<Reads from and writes to
    [SQL/TCP]>, style="dashed", color="#707070", fontcolor="#707070"] + 57 -> 65 [id=66, label=<Makes API calls to
    [XML/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] } \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/36141-LiveDeployment.dot b/structurizr-export/src/test/java/com/structurizr/export/dot/36141-LiveDeployment.dot index 841b0dc42..5ac04100c 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/dot/36141-LiveDeployment.dot +++ b/structurizr-export/src/test/java/com/structurizr/export/dot/36141-LiveDeployment.dot @@ -46,17 +46,6 @@ digraph { fillcolor="#ffffff" subgraph cluster_73 { - margin=25 - label=<bigbank-prod001
    [Deployment Node]> - labelloc=b - color="#888888" - fontcolor="#000000" - fillcolor="#ffffff" - - 74 [id=74,shape=rect, label=<Mainframe Banking
    System

    [Software System]

    Stores all of the core banking
    information about customers,
    accounts, transactions, etc.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] - } - - subgraph cluster_75 { margin=25 label=<bigbank-web***
    [Deployment Node: Ubuntu 16.04 LTS]> labelloc=b @@ -64,7 +53,7 @@ digraph { fontcolor="#000000" fillcolor="#ffffff" - subgraph cluster_76 { + subgraph cluster_74 { margin=25 label=<Apache Tomcat
    [Deployment Node: Apache Tomcat 8.x]> labelloc=b @@ -72,12 +61,12 @@ digraph { fontcolor="#000000" fillcolor="#ffffff" - 77 [id=77,shape=rect, label=<Web Application
    [Container: Java and Spring MVC]

    Delivers the static content
    and the Internet banking
    single page application.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + 75 [id=75,shape=rect, label=<Web Application
    [Container: Java and Spring MVC]

    Delivers the static content
    and the Internet banking
    single page application.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] } } - subgraph cluster_79 { + subgraph cluster_77 { margin=25 label=<bigbank-api***
    [Deployment Node: Ubuntu 16.04 LTS]> labelloc=b @@ -85,7 +74,7 @@ digraph { fontcolor="#000000" fillcolor="#ffffff" - subgraph cluster_80 { + subgraph cluster_78 { margin=25 label=<Apache Tomcat
    [Deployment Node: Apache Tomcat 8.x]> labelloc=b @@ -93,12 +82,12 @@ digraph { fontcolor="#000000" fillcolor="#ffffff" - 81 [id=81,shape=rect, label=<API Application
    [Container: Java and Spring MVC]

    Provides Internet banking
    functionality via a JSON/HTTPS
    API.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + 79 [id=79,shape=rect, label=<API Application
    [Container: Java and Spring MVC]

    Provides Internet banking
    functionality via a JSON/HTTPS
    API.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] } } - subgraph cluster_85 { + subgraph cluster_82 { margin=25 label=<bigbank-db01
    [Deployment Node: Ubuntu 16.04 LTS]> labelloc=b @@ -106,7 +95,7 @@ digraph { fontcolor="#000000" fillcolor="#ffffff" - subgraph cluster_86 { + subgraph cluster_83 { margin=25 label=<Oracle - Primary
    [Deployment Node: Oracle 12c]> labelloc=b @@ -114,12 +103,12 @@ digraph { fontcolor="#000000" fillcolor="#ffffff" - 87 [id=87,shape=cylinder, label=<Database
    [Container: Oracle Database Schema]

    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + 84 [id=84,shape=cylinder, label=<Database
    [Container: Oracle Database Schema]

    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] } } - subgraph cluster_89 { + subgraph cluster_86 { margin=25 label=<bigbank-db02
    [Deployment Node: Ubuntu 16.04 LTS]> labelloc=b @@ -127,7 +116,7 @@ digraph { fontcolor="#000000" fillcolor="#ffffff" - subgraph cluster_90 { + subgraph cluster_87 { margin=25 label=<Oracle - Secondary
    [Deployment Node: Oracle 12c]> labelloc=b @@ -135,18 +124,29 @@ digraph { fontcolor="#000000" fillcolor="#ffffff" - 91 [id=91,shape=cylinder, label=<Database
    [Container: Oracle Database Schema]

    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + 88 [id=88,shape=cylinder, label=<Database
    [Container: Oracle Database Schema]

    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] } } + subgraph cluster_90 { + margin=25 + label=<bigbank-prod001
    [Deployment Node]> + labelloc=b + color="#888888" + fontcolor="#000000" + fillcolor="#ffffff" + + 91 [id=91,shape=rect, label=<Mainframe Banking
    System

    [Software System]

    Stores all of the core banking
    information about customers,
    accounts, transactions, etc.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] + } + } - 77 -> 71 [id=78, label=<Delivers to the customer's
    web browser
    >, style="dashed", color="#707070", fontcolor="#707070"] - 68 -> 81 [id=82, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] - 71 -> 81 [id=83, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] - 81 -> 74 [id=84, label=<Makes API calls to
    [XML/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] - 81 -> 87 [id=88, label=<Reads from and writes to
    [JDBC]>, style="dashed", color="#707070", fontcolor="#707070"] - 81 -> 91 [id=92, label=<Reads from and writes to
    [JDBC]>, style="dashed", color="#707070", fontcolor="#707070"] - 87 -> 91 [id=93, label=<Replicates data to>, style="dashed", color="#707070", fontcolor="#707070",ltail=cluster_86,lhead=cluster_90] + 75 -> 71 [id=76, label=<Delivers to the customer's
    web browser
    >, style="dashed", color="#707070", fontcolor="#707070"] + 68 -> 79 [id=80, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] + 71 -> 79 [id=81, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] + 79 -> 84 [id=85, label=<Reads from and writes to
    [SQL/TCP]>, style="dashed", color="#707070", fontcolor="#707070"] + 79 -> 88 [id=89, label=<Reads from and writes to
    [SQL/TCP]>, style="dashed", color="#707070", fontcolor="#707070"] + 79 -> 91 [id=92, label=<Makes API calls to
    [XML/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] + 84 -> 88 [id=93, label=<Replicates data to>, style="dashed", color="#707070", fontcolor="#707070",ltail=cluster_83,lhead=cluster_87] } \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/36141-SignIn.dot b/structurizr-export/src/test/java/com/structurizr/export/dot/36141-SignIn.dot index bcf42801d..25109d81f 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/dot/36141-SignIn.dot +++ b/structurizr-export/src/test/java/com/structurizr/export/dot/36141-SignIn.dot @@ -5,7 +5,7 @@ digraph { edge [fontname="Arial"] label=<
    API Application - Dynamic
    Summarises how the sign in feature works in the single-page application.> - subgraph cluster_20 { + subgraph cluster_11 { margin=25 label=<
    API Application

    [Container: Java and Spring MVC]> labelloc=b @@ -13,17 +13,17 @@ digraph { fontcolor="#444444" fillcolor="#444444" - 29 [id=29,shape=rect, label=<Sign In Controller
    [Component: Spring MVC Rest Controller]

    Allows users to sign in to the
    Internet Banking System.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] - 32 [id=32,shape=rect, label=<Security Component
    [Component: Spring Bean]

    Provides functionality related
    to signing in, changing
    passwords, etc.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] + 12 [id=12,shape=rect, label=<Sign In Controller
    [Component: Spring MVC Rest Controller]

    Allows users to sign in to the
    Internet Banking System.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] + 15 [id=15,shape=rect, label=<Security Component
    [Component: Spring Bean]

    Provides functionality related
    to signing in, changing
    passwords, etc.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] } - 17 [id=17,shape=rect, label=<Single-Page
    Application

    [Container: JavaScript and Angular]

    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] - 21 [id=21,shape=cylinder, label=<Database
    [Container: Oracle Database Schema]

    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + 18 [id=18,shape=cylinder, label=<Database
    [Container: Oracle Database Schema]

    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + 8 [id=8,shape=rect, label=<Single-Page
    Application

    [Container: JavaScript and Angular]

    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] - 17 -> 29 [id=35, label=<1. Submits credentials to
    [JSON/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] - 29 -> 32 [id=43, label=<2. Validates credentials
    using
    >, style="dashed", color="#707070", fontcolor="#707070"] - 32 -> 21 [id=47, label=<3. select * from users
    where username = ?

    [JDBC]>, style="dashed", color="#707070", fontcolor="#707070"] - 21 -> 32 [id=47, label=<4. Returns user data to
    [JDBC]>, style="dashed", color="#707070", fontcolor="#707070"] - 32 -> 29 [id=43, label=<5. Returns true if the
    hashed password matches
    >, style="dashed", color="#707070", fontcolor="#707070"] - 29 -> 17 [id=35, label=<6. Sends back an
    authentication token to

    [JSON/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] + 8 -> 12 [id=32, label=<1. Submits credentials to
    [JSON/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] + 12 -> 15 [id=40, label=<2. Validates credentials
    using
    >, style="dashed", color="#707070", fontcolor="#707070"] + 15 -> 18 [id=44, label=<3. select * from users
    where username = ?

    [SQL/TCP]>, style="dashed", color="#707070", fontcolor="#707070"] + 18 -> 15 [id=44, label=<4. Returns user data to
    [SQL/TCP]>, style="dashed", color="#707070", fontcolor="#707070"] + 15 -> 12 [id=40, label=<5. Returns true if the
    hashed password matches
    >, style="dashed", color="#707070", fontcolor="#707070"] + 12 -> 8 [id=32, label=<6. Sends back an
    authentication token to

    [JSON/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] } \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/36141-SystemContext.dot b/structurizr-export/src/test/java/com/structurizr/export/dot/36141-SystemContext.dot index 5f1280e06..73780f17e 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/dot/36141-SystemContext.dot +++ b/structurizr-export/src/test/java/com/structurizr/export/dot/36141-SystemContext.dot @@ -5,13 +5,24 @@ digraph { edge [fontname="Arial"] label=<
    Internet Banking System - System Context
    The system context diagram for the Internet Banking System.> + subgraph "cluster_group_Big Bank plc" { + margin=25 + label=<
    Big Bank plc
    > + labelloc=b + color="#cccccc" + fontcolor="#cccccc" + fillcolor="#ffffff" + style="dashed" + + 4 [id=4,shape=rect, label=<Mainframe Banking
    System

    [Software System]

    Stores all of the core banking
    information about customers,
    accounts, transactions, etc.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] + 5 [id=5,shape=rect, label=<E-mail System
    [Software System]

    The internal Microsoft
    Exchange e-mail system.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] + 7 [id=7,shape=rect, label=<Internet Banking
    System

    [Software System]

    Allows customers to view
    information about their bank
    accounts, and make payments.
    >, style=filled, color="#0b4884", fillcolor="#1168bd", fontcolor="#ffffff"] + } + 1 [id=1,shape=rect, label=<Personal Banking
    Customer

    [Person]

    A customer of the bank, with
    personal bank accounts.
    >, style=filled, color="#052e56", fillcolor="#08427b", fontcolor="#ffffff"] - 2 [id=2,shape=rect, label=<Internet Banking
    System

    [Software System]

    Allows customers to view
    information about their bank
    accounts, and make payments.
    >, style=filled, color="#0b4884", fillcolor="#1168bd", fontcolor="#ffffff"] - 4 [id=4,shape=rect, label=<Mainframe Banking
    System

    [Software System]

    Stores all of the core banking
    information about customers,
    accounts, transactions, etc.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] - 6 [id=6,shape=rect, label=<E-mail System
    [Software System]

    The internal Microsoft
    Exchange e-mail system.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] - 1 -> 2 [id=3, label=<Views account balances,
    and makes payments using
    >, style="dashed", color="#707070", fontcolor="#707070"] - 2 -> 4 [id=5, label=<Gets account information
    from, and makes payments
    using
    >, style="dashed", color="#707070", fontcolor="#707070"] - 2 -> 6 [id=7, label=<Sends e-mail using>, style="dashed", color="#707070", fontcolor="#707070"] - 6 -> 1 [id=8, label=<Sends e-mails to>, style="dashed", color="#707070", fontcolor="#707070"] + 1 -> 7 [id=19, label=<Views account balances,
    and makes payments using
    >, style="dashed", color="#707070", fontcolor="#707070"] + 7 -> 4 [id=20, label=<Gets account information
    from, and makes payments
    using
    >, style="dashed", color="#707070", fontcolor="#707070"] + 7 -> 5 [id=21, label=<Sends e-mail using>, style="dashed", color="#707070", fontcolor="#707070"] + 5 -> 1 [id=22, label=<Sends e-mails to>, style="dashed", color="#707070", fontcolor="#707070"] } \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/36141-SystemLandscape.dot b/structurizr-export/src/test/java/com/structurizr/export/dot/36141-SystemLandscape.dot index b82be15da..2b691cc0d 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/dot/36141-SystemLandscape.dot +++ b/structurizr-export/src/test/java/com/structurizr/export/dot/36141-SystemLandscape.dot @@ -3,33 +3,34 @@ digraph { graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] node [fontname="Arial", shape=box, margin="0.4,0.3"] edge [fontname="Arial"] - label=<
    System Landscape
    The system landscape diagram for Big Bank plc.> + label=<
    System Landscape> - subgraph cluster_enterprise { + subgraph "cluster_group_Big Bank plc" { margin=25 - label=<
    Big Bank plc

    [Enterprise]> + label=<
    Big Bank plc
    > labelloc=b - color="#444444" - fontcolor="#444444" + color="#cccccc" + fontcolor="#cccccc" fillcolor="#ffffff" + style="dashed" - 12 [id=12,shape=rect, label=<Customer Service
    Staff

    [Person]

    Customer service staff within
    the bank.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] - 15 [id=15,shape=rect, label=<Back Office Staff
    [Person]

    Administration and support
    staff within the bank.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] - 2 [id=2,shape=rect, label=<Internet Banking
    System

    [Software System]

    Allows customers to view
    information about their bank
    accounts, and make payments.
    >, style=filled, color="#0b4884", fillcolor="#1168bd", fontcolor="#ffffff"] + 2 [id=2,shape=rect, label=<Customer Service
    Staff

    [Person]

    Customer service staff within
    the bank.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] + 3 [id=3,shape=rect, label=<Back Office Staff
    [Person]

    Administration and support
    staff within the bank.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] 4 [id=4,shape=rect, label=<Mainframe Banking
    System

    [Software System]

    Stores all of the core banking
    information about customers,
    accounts, transactions, etc.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] - 6 [id=6,shape=rect, label=<E-mail System
    [Software System]

    The internal Microsoft
    Exchange e-mail system.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] - 9 [id=9,shape=rect, label=<ATM
    [Software System]

    Allows customers to withdraw
    cash.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] + 5 [id=5,shape=rect, label=<E-mail System
    [Software System]

    The internal Microsoft
    Exchange e-mail system.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] + 6 [id=6,shape=rect, label=<ATM
    [Software System]

    Allows customers to withdraw
    cash.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] + 7 [id=7,shape=rect, label=<Internet Banking
    System

    [Software System]

    Allows customers to view
    information about their bank
    accounts, and make payments.
    >, style=filled, color="#0b4884", fillcolor="#1168bd", fontcolor="#ffffff"] } 1 [id=1,shape=rect, label=<Personal Banking
    Customer

    [Person]

    A customer of the bank, with
    personal bank accounts.
    >, style=filled, color="#052e56", fillcolor="#08427b", fontcolor="#ffffff"] - 9 -> 4 [id=10, label=<Uses>, style="dashed", color="#707070", fontcolor="#707070"] - 1 -> 9 [id=11, label=<Withdraws cash using>, style="dashed", color="#707070", fontcolor="#707070"] - 12 -> 4 [id=13, label=<Uses>, style="dashed", color="#707070", fontcolor="#707070"] - 1 -> 12 [id=14, label=<Asks questions to
    [Telephone]>, style="dashed", color="#707070", fontcolor="#707070"] - 15 -> 4 [id=16, label=<Uses>, style="dashed", color="#707070", fontcolor="#707070"] - 1 -> 2 [id=3, label=<Views account balances,
    and makes payments using
    >, style="dashed", color="#707070", fontcolor="#707070"] - 2 -> 4 [id=5, label=<Gets account information
    from, and makes payments
    using
    >, style="dashed", color="#707070", fontcolor="#707070"] - 2 -> 6 [id=7, label=<Sends e-mail using>, style="dashed", color="#707070", fontcolor="#707070"] - 6 -> 1 [id=8, label=<Sends e-mails to>, style="dashed", color="#707070", fontcolor="#707070"] + 1 -> 7 [id=19, label=<Views account balances,
    and makes payments using
    >, style="dashed", color="#707070", fontcolor="#707070"] + 7 -> 4 [id=20, label=<Gets account information
    from, and makes payments
    using
    >, style="dashed", color="#707070", fontcolor="#707070"] + 7 -> 5 [id=21, label=<Sends e-mail using>, style="dashed", color="#707070", fontcolor="#707070"] + 5 -> 1 [id=22, label=<Sends e-mails to>, style="dashed", color="#707070", fontcolor="#707070"] + 1 -> 2 [id=23, label=<Asks questions to
    [Telephone]>, style="dashed", color="#707070", fontcolor="#707070"] + 2 -> 4 [id=24, label=<Uses>, style="dashed", color="#707070", fontcolor="#707070"] + 1 -> 6 [id=25, label=<Withdraws cash using>, style="dashed", color="#707070", fontcolor="#707070"] + 6 -> 4 [id=26, label=<Uses>, style="dashed", color="#707070", fontcolor="#707070"] + 3 -> 4 [id=27, label=<Uses>, style="dashed", color="#707070", fontcolor="#707070"] } \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/groups-Components.dot b/structurizr-export/src/test/java/com/structurizr/export/dot/groups-Components.dot index efa20b99e..cc868ffe8 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/dot/groups-Components.dot +++ b/structurizr-export/src/test/java/com/structurizr/export/dot/groups-Components.dot @@ -15,9 +15,9 @@ digraph { fontcolor="#444444" fillcolor="#444444" - subgraph "cluster_group_Group 4" { + subgraph "cluster_group_Group 5" { margin=25 - label=<
    Group 4
    > + label=<
    Group 5
    > labelloc=b color="#cccccc" fontcolor="#cccccc" diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/groups-Containers.dot b/structurizr-export/src/test/java/com/structurizr/export/dot/groups-Containers.dot index fcddeeed4..013ecfbf2 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/dot/groups-Containers.dot +++ b/structurizr-export/src/test/java/com/structurizr/export/dot/groups-Containers.dot @@ -15,9 +15,9 @@ digraph { fontcolor="#444444" fillcolor="#444444" - subgraph "cluster_group_Group 3" { + subgraph "cluster_group_Group 4" { margin=25 - label=<
    Group 3
    > + label=<
    Group 4
    > labelloc=b color="#cccccc" fontcolor="#cccccc" diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/groups-SystemLandscape.dot b/structurizr-export/src/test/java/com/structurizr/export/dot/groups-SystemLandscape.dot index 5daee1e84..5147526da 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/dot/groups-SystemLandscape.dot +++ b/structurizr-export/src/test/java/com/structurizr/export/dot/groups-SystemLandscape.dot @@ -5,39 +5,40 @@ digraph { edge [fontname="Arial"] label=<
    System Landscape> - subgraph cluster_enterprise { + subgraph "cluster_group_Group 1" { margin=25 - label=<
    Enterprise

    [Enterprise]> + label=<
    Group 1
    > labelloc=b - color="#444444" - fontcolor="#444444" + color="#cccccc" + fontcolor="#cccccc" fillcolor="#ffffff" + style="dashed" - subgraph "cluster_group_Group 2" { - margin=25 - label=<
    Group 2
    > - labelloc=b - color="#cccccc" - fontcolor="#cccccc" - fillcolor="#ffffff" - style="dashed" - - 4 [id=4,shape=rect, label=<D
    [Software System]>, style=filled, color="#9a9a9a", fillcolor="#dddddd", fontcolor="#000000"] - } - - 3 [id=3,shape=rect, label=<C
    [Software System]>, style=filled, color="#9a9a9a", fillcolor="#dddddd", fontcolor="#000000"] + 2 [id=2,shape=rect, label=<B
    [Software System]>, style=filled, color="#9a9a9a", fillcolor="#dddddd", fontcolor="#000000"] } - subgraph "cluster_group_Group 1" { + subgraph "cluster_group_Group 2" { margin=25 - label=<
    Group 1
    > + label=<
    Group 2
    > labelloc=b color="#cccccc" fontcolor="#cccccc" fillcolor="#ffffff" style="dashed" - 2 [id=2,shape=rect, label=<B
    [Software System]>, style=filled, color="#9a9a9a", fillcolor="#dddddd", fontcolor="#000000"] + 3 [id=3,shape=rect, label=<C
    [Software System]>, style=filled, color="#9a9a9a", fillcolor="#dddddd", fontcolor="#000000"] + subgraph "cluster_group_Group 3" { + margin=25 + label=<
    Group 3
    > + labelloc=b + color="#cccccc" + fontcolor="#cccccc" + fillcolor="#ffffff" + style="dashed" + + 4 [id=4,shape=rect, label=<D
    [Software System]>, style=filled, color="#9a9a9a", fillcolor="#dddddd", fontcolor="#000000"] + } + } 1 [id=1,shape=rect, label=<A
    [Software System]>, style=filled, color="#9a9a9a", fillcolor="#dddddd", fontcolor="#000000"] diff --git a/structurizr-export/src/test/java/com/structurizr/export/ilograph/36141.ilograph b/structurizr-export/src/test/java/com/structurizr/export/ilograph/36141.ilograph index 2452b8dbc..31f0a6f7e 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/ilograph/36141.ilograph +++ b/structurizr-export/src/test/java/com/structurizr/export/ilograph/36141.ilograph @@ -7,21 +7,42 @@ resources: backgroundColor: "#08427b" color: "#ffffff" - - id: "12" + - id: "2" name: "Customer Service Staff" subtitle: "[Person]" description: "Customer service staff within the bank." backgroundColor: "#999999" color: "#ffffff" - - id: "15" + - id: "3" name: "Back Office Staff" subtitle: "[Person]" description: "Administration and support staff within the bank." backgroundColor: "#999999" color: "#ffffff" - - id: "2" + - id: "4" + name: "Mainframe Banking System" + subtitle: "[Software System]" + description: "Stores all of the core banking information about customers, accounts, transactions, etc." + backgroundColor: "#999999" + color: "#ffffff" + + - id: "5" + name: "E-mail System" + subtitle: "[Software System]" + description: "The internal Microsoft Exchange e-mail system." + backgroundColor: "#999999" + color: "#ffffff" + + - id: "6" + name: "ATM" + subtitle: "[Software System]" + description: "Allows customers to withdraw cash." + backgroundColor: "#999999" + color: "#ffffff" + + - id: "7" name: "Internet Banking System" subtitle: "[Software System]" description: "Allows customers to view information about their bank accounts, and make payments." @@ -29,28 +50,14 @@ resources: color: "#ffffff" children: - - id: "17" - name: "Single-Page Application" - subtitle: "[Container: JavaScript and Angular]" - description: "Provides all of the Internet banking functionality to customers via their web browser." - backgroundColor: "#438dd5" - color: "#ffffff" - - - id: "18" - name: "Mobile App" - subtitle: "[Container: Xamarin]" - description: "Provides a limited subset of the Internet banking functionality to customers via their mobile device." - backgroundColor: "#438dd5" - color: "#ffffff" - - - id: "19" + - id: "10" name: "Web Application" subtitle: "[Container: Java and Spring MVC]" description: "Delivers the static content and the Internet banking single page application." backgroundColor: "#438dd5" color: "#ffffff" - - id: "20" + - id: "11" name: "API Application" subtitle: "[Container: Java and Spring MVC]" description: "Provides Internet banking functionality via a JSON/HTTPS API." @@ -58,108 +65,112 @@ resources: color: "#ffffff" children: - - id: "29" + - id: "12" name: "Sign In Controller" subtitle: "[Component: Spring MVC Rest Controller]" description: "Allows users to sign in to the Internet Banking System." backgroundColor: "#85bbf0" color: "#000000" - - id: "30" + - id: "13" name: "Accounts Summary Controller" subtitle: "[Component: Spring MVC Rest Controller]" description: "Provides customers with a summary of their bank accounts." backgroundColor: "#85bbf0" color: "#000000" - - id: "31" + - id: "14" name: "Reset Password Controller" subtitle: "[Component: Spring MVC Rest Controller]" description: "Allows users to reset their passwords with a single use URL." backgroundColor: "#85bbf0" color: "#000000" - - id: "32" + - id: "15" name: "Security Component" subtitle: "[Component: Spring Bean]" description: "Provides functionality related to signing in, changing passwords, etc." backgroundColor: "#85bbf0" color: "#000000" - - id: "33" + - id: "16" name: "Mainframe Banking System Facade" subtitle: "[Component: Spring Bean]" description: "A facade onto the mainframe banking system." backgroundColor: "#85bbf0" color: "#000000" - - id: "34" + - id: "17" name: "E-mail Component" subtitle: "[Component: Spring Bean]" description: "Sends e-mails to users." backgroundColor: "#85bbf0" color: "#000000" - - id: "21" + - id: "18" name: "Database" subtitle: "[Container: Oracle Database Schema]" description: "Stores user registration information, hashed authentication credentials, access logs, etc." backgroundColor: "#438dd5" color: "#ffffff" - - id: "4" - name: "Mainframe Banking System" - subtitle: "[Software System]" - description: "Stores all of the core banking information about customers, accounts, transactions, etc." - backgroundColor: "#999999" - color: "#ffffff" - - - id: "6" - name: "E-mail System" - subtitle: "[Software System]" - description: "The internal Microsoft Exchange e-mail system." - backgroundColor: "#999999" - color: "#ffffff" + - id: "8" + name: "Single-Page Application" + subtitle: "[Container: JavaScript and Angular]" + description: "Provides all of the Internet banking functionality to customers via their web browser." + backgroundColor: "#438dd5" + color: "#ffffff" - - id: "9" - name: "ATM" - subtitle: "[Software System]" - description: "Allows customers to withdraw cash." - backgroundColor: "#999999" - color: "#ffffff" + - id: "9" + name: "Mobile App" + subtitle: "[Container: Xamarin]" + description: "Provides a limited subset of the Internet banking functionality to customers via their mobile device." + backgroundColor: "#438dd5" + color: "#ffffff" - id: "50" name: "Developer Laptop" subtitle: "[Deployment Node: Microsoft Windows 10 or Apple macOS]" - description: "A developer laptop." backgroundColor: "#ffffff" color: "#000000" children: - id: "51" + name: "Web Browser" + subtitle: "[Deployment Node: Chrome, Firefox, Safari, or Edge]" + backgroundColor: "#ffffff" + color: "#000000" + + children: + - id: "52" + name: "Single-Page Application" + subtitle: "[Container: JavaScript and Angular]" + description: "Provides all of the Internet banking functionality to customers via their web browser." + backgroundColor: "#438dd5" + color: "#ffffff" + + - id: "53" name: "Docker Container - Web Server" subtitle: "[Deployment Node: Docker]" - description: "A Docker container." backgroundColor: "#ffffff" color: "#000000" children: - - id: "52" + - id: "54" name: "Apache Tomcat" subtitle: "[Deployment Node: Apache Tomcat 8.x]" - description: "An open source Java EE web server." backgroundColor: "#ffffff" color: "#000000" children: - - id: "53" + - id: "55" name: "Web Application" subtitle: "[Container: Java and Spring MVC]" description: "Delivers the static content and the Internet banking single page application." backgroundColor: "#438dd5" color: "#ffffff" - - id: "54" + - id: "57" name: "API Application" subtitle: "[Container: Java and Spring MVC]" description: "Provides Internet banking functionality via a JSON/HTTPS API." @@ -169,7 +180,6 @@ resources: - id: "59" name: "Docker Container - Database Server" subtitle: "[Deployment Node: Docker]" - description: "A Docker container." backgroundColor: "#ffffff" color: "#000000" @@ -177,7 +187,6 @@ resources: - id: "60" name: "Database Server" subtitle: "[Deployment Node: Oracle 12c]" - description: "A development database." backgroundColor: "#ffffff" color: "#000000" @@ -189,35 +198,21 @@ resources: backgroundColor: "#438dd5" color: "#ffffff" - - id: "63" - name: "Web Browser" - subtitle: "[Deployment Node: Chrome, Firefox, Safari, or Edge]" - backgroundColor: "#ffffff" - color: "#000000" - - children: - - id: "64" - name: "Single-Page Application" - subtitle: "[Container: JavaScript and Angular]" - description: "Provides all of the Internet banking functionality to customers via their web browser." - backgroundColor: "#438dd5" - color: "#ffffff" - - - id: "55" + - id: "63" name: "Big Bank plc" subtitle: "[Deployment Node: Big Bank plc data center]" backgroundColor: "#ffffff" color: "#000000" children: - - id: "56" + - id: "64" name: "bigbank-dev001" subtitle: "[Deployment Node]" backgroundColor: "#ffffff" color: "#000000" children: - - id: "57" + - id: "65" name: "Mainframe Banking System" subtitle: "[Software System]" description: "Stores all of the core banking information about customers, accounts, transactions, etc." @@ -267,365 +262,356 @@ resources: children: - id: "73" - name: "bigbank-prod001" - subtitle: "[Deployment Node]" - backgroundColor: "#ffffff" - color: "#000000" - - children: - - id: "74" - name: "Mainframe Banking System" - subtitle: "[Software System]" - description: "Stores all of the core banking information about customers, accounts, transactions, etc." - backgroundColor: "#999999" - color: "#ffffff" - - - id: "75" name: "bigbank-web***" subtitle: "[Deployment Node: Ubuntu 16.04 LTS]" - description: "A web server residing in the web server farm, accessed via F5 BIG-IP LTMs." backgroundColor: "#ffffff" color: "#000000" children: - - id: "76" + - id: "74" name: "Apache Tomcat" subtitle: "[Deployment Node: Apache Tomcat 8.x]" - description: "An open source Java EE web server." backgroundColor: "#ffffff" color: "#000000" children: - - id: "77" + - id: "75" name: "Web Application" subtitle: "[Container: Java and Spring MVC]" description: "Delivers the static content and the Internet banking single page application." backgroundColor: "#438dd5" color: "#ffffff" - - id: "79" + - id: "77" name: "bigbank-api***" subtitle: "[Deployment Node: Ubuntu 16.04 LTS]" - description: "A web server residing in the web server farm, accessed via F5 BIG-IP LTMs." backgroundColor: "#ffffff" color: "#000000" children: - - id: "80" + - id: "78" name: "Apache Tomcat" subtitle: "[Deployment Node: Apache Tomcat 8.x]" - description: "An open source Java EE web server." backgroundColor: "#ffffff" color: "#000000" children: - - id: "81" + - id: "79" name: "API Application" subtitle: "[Container: Java and Spring MVC]" description: "Provides Internet banking functionality via a JSON/HTTPS API." backgroundColor: "#438dd5" color: "#ffffff" - - id: "85" + - id: "82" name: "bigbank-db01" subtitle: "[Deployment Node: Ubuntu 16.04 LTS]" - description: "The primary database server." backgroundColor: "#ffffff" color: "#000000" children: - - id: "86" + - id: "83" name: "Oracle - Primary" subtitle: "[Deployment Node: Oracle 12c]" - description: "The primary, live database server." backgroundColor: "#ffffff" color: "#000000" children: - - id: "87" + - id: "84" name: "Database" subtitle: "[Container: Oracle Database Schema]" description: "Stores user registration information, hashed authentication credentials, access logs, etc." backgroundColor: "#438dd5" color: "#ffffff" - - id: "89" + - id: "86" name: "bigbank-db02" subtitle: "[Deployment Node: Ubuntu 16.04 LTS]" - description: "The secondary database server." backgroundColor: "#ffffff" color: "#000000" children: - - id: "90" + - id: "87" name: "Oracle - Secondary" subtitle: "[Deployment Node: Oracle 12c]" - description: "A secondary, standby database server, used for failover purposes only." backgroundColor: "#ffffff" color: "#000000" children: - - id: "91" + - id: "88" name: "Database" subtitle: "[Container: Oracle Database Schema]" description: "Stores user registration information, hashed authentication credentials, access logs, etc." backgroundColor: "#438dd5" color: "#ffffff" + - id: "90" + name: "bigbank-prod001" + subtitle: "[Deployment Node]" + backgroundColor: "#ffffff" + color: "#000000" + + children: + - id: "91" + name: "Mainframe Banking System" + subtitle: "[Software System]" + description: "Stores all of the core banking information about customers, accounts, transactions, etc." + backgroundColor: "#999999" + color: "#ffffff" + perspectives: - name: Static Structure relations: - from: "1" - to: "9" - label: "Withdraws cash using" + to: "7" + label: "Views account balances, and makes payments using" color: "#707070" - from: "1" - to: "12" + to: "2" label: "Asks questions to" description: "Telephone" color: "#707070" - from: "1" - to: "2" - label: "Views account balances, and makes payments using" + to: "6" + label: "Withdraws cash using" color: "#707070" - - from: "12" + - from: "2" to: "4" label: "Uses" color: "#707070" - - from: "15" + - from: "3" to: "4" label: "Uses" color: "#707070" - - from: "2" - to: "4" - label: "Gets account information from, and makes payments using" - color: "#707070" - - - from: "2" - to: "6" - label: "Sends e-mail using" - color: "#707070" - - - from: "6" + - from: "5" to: "1" label: "Sends e-mails to" color: "#707070" - - from: "9" + - from: "6" to: "4" label: "Uses" color: "#707070" + - from: "7" + to: "4" + label: "Gets account information from, and makes payments using" + color: "#707070" + + - from: "7" + to: "5" + label: "Sends e-mail using" + color: "#707070" + - from: "1" - to: "19" + to: "10" label: "Visits bigbank.com/ib using" description: "HTTPS" color: "#707070" - from: "1" - to: "17" + to: "8" label: "Views account balances, and makes payments using" color: "#707070" - from: "1" - to: "18" + to: "9" label: "Views account balances, and makes payments using" color: "#707070" - - from: "17" - to: "20" + - from: "10" + to: "8" + label: "Delivers to the customer's web browser" + color: "#707070" + + - from: "11" + to: "18" + label: "Reads from and writes to" + description: "SQL/TCP" + color: "#707070" + + - from: "11" + to: "4" + label: "Makes API calls to" + description: "XML/HTTPS" + color: "#707070" + + - from: "11" + to: "5" + label: "Sends e-mail using" + color: "#707070" + + - from: "8" + to: "11" label: "Makes API calls to" description: "JSON/HTTPS" color: "#707070" - - from: "18" - to: "20" + - from: "9" + to: "11" label: "Makes API calls to" description: "JSON/HTTPS" color: "#707070" - - from: "19" + - from: "12" + to: "15" + label: "Uses" + color: "#707070" + + - from: "13" + to: "16" + label: "Uses" + color: "#707070" + + - from: "14" + to: "15" + label: "Uses" + color: "#707070" + + - from: "14" to: "17" - label: "Delivers to the customer's web browser" + label: "Uses" color: "#707070" - - from: "20" - to: "21" + - from: "15" + to: "18" label: "Reads from and writes to" - description: "JDBC" + description: "SQL/TCP" color: "#707070" - - from: "20" + - from: "16" to: "4" label: "Makes API calls to" description: "XML/HTTPS" color: "#707070" - - from: "20" - to: "6" + - from: "17" + to: "5" label: "Sends e-mail using" - description: "SMTP" color: "#707070" - - from: "17" - to: "29" + - from: "8" + to: "12" label: "Makes API calls to" description: "JSON/HTTPS" color: "#707070" - - from: "17" - to: "31" + - from: "8" + to: "13" label: "Makes API calls to" description: "JSON/HTTPS" color: "#707070" - - from: "17" - to: "30" + - from: "8" + to: "14" label: "Makes API calls to" description: "JSON/HTTPS" color: "#707070" - - from: "18" - to: "29" + - from: "9" + to: "12" label: "Makes API calls to" description: "JSON/HTTPS" color: "#707070" - - from: "18" - to: "31" + - from: "9" + to: "13" label: "Makes API calls to" description: "JSON/HTTPS" color: "#707070" - - from: "18" - to: "30" + - from: "9" + to: "14" label: "Makes API calls to" description: "JSON/HTTPS" color: "#707070" - - from: "29" - to: "32" - label: "Uses" - color: "#707070" - - - from: "30" - to: "33" - label: "Uses" - color: "#707070" - - - from: "31" - to: "32" - label: "Uses" - color: "#707070" - - - from: "31" - to: "34" - label: "Uses" - color: "#707070" - - - from: "32" - to: "21" - label: "Reads from and writes to" - description: "JDBC" - color: "#707070" - - - from: "33" - to: "4" - label: "Uses" - description: "XML/HTTPS" - color: "#707070" - - - from: "34" - to: "6" - label: "Sends e-mail using" - color: "#707070" - - name: Dynamic - API Application - Dynamic sequence: - start: "17" + start: "8" steps: - - to: "29" + - to: "12" label: "1. Submits credentials to" description: "JSON/HTTPS" color: "#707070" - - to: "32" + - to: "15" label: "2. Validates credentials using" color: "#707070" - - to: "21" + - to: "18" label: "3. select * from users where username = ?" - description: "JDBC" + description: "SQL/TCP" color: "#707070" - - to: "32" + - to: "15" label: "4. Returns user data to" - description: "JDBC" + description: "SQL/TCP" color: "#707070" - - to: "29" + - to: "12" label: "5. Returns true if the hashed password matches" color: "#707070" - - to: "17" + - to: "8" label: "6. Sends back an authentication token to" description: "JSON/HTTPS" color: "#707070" - name: Deployment - Development relations: - - from: "53" - to: "64" - label: "Delivers to the customer's web browser" - color: "#707070" - - from: "54" + - from: "52" to: "57" label: "Makes API calls to" - description: "XML/HTTPS" + description: "JSON/HTTPS" color: "#707070" - - from: "54" + - from: "55" + to: "52" + label: "Delivers to the customer's web browser" + color: "#707070" + - from: "57" to: "61" label: "Reads from and writes to" - description: "JDBC" + description: "SQL/TCP" color: "#707070" - - from: "64" - to: "54" + - from: "57" + to: "65" label: "Makes API calls to" - description: "JSON/HTTPS" + description: "XML/HTTPS" color: "#707070" - name: Deployment - Live relations: - from: "68" - to: "81" + to: "79" label: "Makes API calls to" description: "JSON/HTTPS" color: "#707070" - from: "71" - to: "81" + to: "79" label: "Makes API calls to" description: "JSON/HTTPS" color: "#707070" - - from: "77" + - from: "75" to: "71" label: "Delivers to the customer's web browser" color: "#707070" - - from: "81" - to: "74" - label: "Makes API calls to" - description: "XML/HTTPS" - color: "#707070" - - from: "81" - to: "87" + - from: "79" + to: "84" label: "Reads from and writes to" - description: "JDBC" + description: "SQL/TCP" color: "#707070" - - from: "81" - to: "91" + - from: "79" + to: "88" label: "Reads from and writes to" - description: "JDBC" + description: "SQL/TCP" color: "#707070" + - from: "79" + to: "91" + label: "Makes API calls to" + description: "XML/HTTPS" + color: "#707070" \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-Components.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-Components.mmd index dcae0ac7f..8a96174a1 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-Components.mmd +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-Components.mmd @@ -6,43 +6,43 @@ graph TB 4["
    Mainframe Banking System
    [Software System]
    Stores all of the core
    banking information about
    customers, accounts,
    transactions, etc.
    "] style 4 fill:#999999,stroke:#6b6b6b,color:#ffffff - 17["
    Single-Page Application
    [Container: JavaScript and Angular]
    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    "] - style 17 fill:#438dd5,stroke:#2e6295,color:#ffffff - 6["
    E-mail System
    [Software System]
    The internal Microsoft
    Exchange e-mail system.
    "] - style 6 fill:#999999,stroke:#6b6b6b,color:#ffffff - 18["
    Mobile App
    [Container: Xamarin]
    Provides a limited subset of
    the Internet banking
    functionality to customers
    via their mobile device.
    "] + 5["
    E-mail System
    [Software System]
    The internal Microsoft
    Exchange e-mail system.
    "] + style 5 fill:#999999,stroke:#6b6b6b,color:#ffffff + 18[("
    Database
    [Container: Oracle Database Schema]
    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    ")] style 18 fill:#438dd5,stroke:#2e6295,color:#ffffff - 21[("
    Database
    [Container: Oracle Database Schema]
    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    ")] - style 21 fill:#438dd5,stroke:#2e6295,color:#ffffff + 8["
    Single-Page Application
    [Container: JavaScript and Angular]
    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    "] + style 8 fill:#438dd5,stroke:#2e6295,color:#ffffff + 9["
    Mobile App
    [Container: Xamarin]
    Provides a limited subset of
    the Internet banking
    functionality to customers
    via their mobile device.
    "] + style 9 fill:#438dd5,stroke:#2e6295,color:#ffffff - subgraph 20 [API Application] - style 20 fill:#ffffff,stroke:#2e6295,color:#2e6295 + subgraph 11 [API Application] + style 11 fill:#ffffff,stroke:#2e6295,color:#2e6295 - 29["
    Sign In Controller
    [Component: Spring MVC Rest Controller]
    Allows users to sign in to
    the Internet Banking System.
    "] - style 29 fill:#85bbf0,stroke:#5d82a8,color:#000000 - 30["
    Accounts Summary Controller
    [Component: Spring MVC Rest Controller]
    Provides customers with a
    summary of their bank
    accounts.
    "] - style 30 fill:#85bbf0,stroke:#5d82a8,color:#000000 - 31["
    Reset Password Controller
    [Component: Spring MVC Rest Controller]
    Allows users to reset their
    passwords with a single use
    URL.
    "] - style 31 fill:#85bbf0,stroke:#5d82a8,color:#000000 - 32["
    Security Component
    [Component: Spring Bean]
    Provides functionality
    related to signing in,
    changing passwords, etc.
    "] - style 32 fill:#85bbf0,stroke:#5d82a8,color:#000000 - 33["
    Mainframe Banking System Facade
    [Component: Spring Bean]
    A facade onto the mainframe
    banking system.
    "] - style 33 fill:#85bbf0,stroke:#5d82a8,color:#000000 - 34["
    E-mail Component
    [Component: Spring Bean]
    Sends e-mails to users.
    "] - style 34 fill:#85bbf0,stroke:#5d82a8,color:#000000 + 12["
    Sign In Controller
    [Component: Spring MVC Rest Controller]
    Allows users to sign in to
    the Internet Banking System.
    "] + style 12 fill:#85bbf0,stroke:#5d82a8,color:#000000 + 13["
    Accounts Summary Controller
    [Component: Spring MVC Rest Controller]
    Provides customers with a
    summary of their bank
    accounts.
    "] + style 13 fill:#85bbf0,stroke:#5d82a8,color:#000000 + 14["
    Reset Password Controller
    [Component: Spring MVC Rest Controller]
    Allows users to reset their
    passwords with a single use
    URL.
    "] + style 14 fill:#85bbf0,stroke:#5d82a8,color:#000000 + 15["
    Security Component
    [Component: Spring Bean]
    Provides functionality
    related to signing in,
    changing passwords, etc.
    "] + style 15 fill:#85bbf0,stroke:#5d82a8,color:#000000 + 16["
    Mainframe Banking System Facade
    [Component: Spring Bean]
    A facade onto the mainframe
    banking system.
    "] + style 16 fill:#85bbf0,stroke:#5d82a8,color:#000000 + 17["
    E-mail Component
    [Component: Spring Bean]
    Sends e-mails to users.
    "] + style 17 fill:#85bbf0,stroke:#5d82a8,color:#000000 end - 17-. "
    Makes API calls to
    [JSON/HTTPS]
    " .->29 - 17-. "
    Makes API calls to
    [JSON/HTTPS]
    " .->31 - 17-. "
    Makes API calls to
    [JSON/HTTPS]
    " .->30 - 18-. "
    Makes API calls to
    [JSON/HTTPS]
    " .->29 - 18-. "
    Makes API calls to
    [JSON/HTTPS]
    " .->31 - 18-. "
    Makes API calls to
    [JSON/HTTPS]
    " .->30 - 29-. "
    Uses
    " .->32 - 30-. "
    Uses
    " .->33 - 31-. "
    Uses
    " .->32 - 31-. "
    Uses
    " .->34 - 32-. "
    Reads from and writes to
    [JDBC]
    " .->21 - 33-. "
    Uses
    [XML/HTTPS]
    " .->4 - 34-. "
    Sends e-mail using
    " .->6 + 8-. "
    Makes API calls to
    [JSON/HTTPS]
    " .->12 + 8-. "
    Makes API calls to
    [JSON/HTTPS]
    " .->13 + 8-. "
    Makes API calls to
    [JSON/HTTPS]
    " .->14 + 9-. "
    Makes API calls to
    [JSON/HTTPS]
    " .->12 + 9-. "
    Makes API calls to
    [JSON/HTTPS]
    " .->13 + 9-. "
    Makes API calls to
    [JSON/HTTPS]
    " .->14 + 12-. "
    Uses
    " .->15 + 13-. "
    Uses
    " .->16 + 14-. "
    Uses
    " .->15 + 14-. "
    Uses
    " .->17 + 15-. "
    Reads from and writes to
    [SQL/TCP]
    " .->18 + 16-. "
    Makes API calls to
    [XML/HTTPS]
    " .->4 + 17-. "
    Sends e-mail using
    " .->5 end \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-Containers.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-Containers.mmd index 4cda955bd..86d53a05a 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-Containers.mmd +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-Containers.mmd @@ -8,32 +8,32 @@ graph TB style 1 fill:#08427b,stroke:#052e56,color:#ffffff 4["
    Mainframe Banking System
    [Software System]
    Stores all of the core
    banking information about
    customers, accounts,
    transactions, etc.
    "] style 4 fill:#999999,stroke:#6b6b6b,color:#ffffff - 6["
    E-mail System
    [Software System]
    The internal Microsoft
    Exchange e-mail system.
    "] - style 6 fill:#999999,stroke:#6b6b6b,color:#ffffff + 5["
    E-mail System
    [Software System]
    The internal Microsoft
    Exchange e-mail system.
    "] + style 5 fill:#999999,stroke:#6b6b6b,color:#ffffff - subgraph 2 [Internet Banking System] - style 2 fill:#ffffff,stroke:#0b4884,color:#0b4884 + subgraph 7 [Internet Banking System] + style 7 fill:#ffffff,stroke:#0b4884,color:#0b4884 - 17["
    Single-Page Application
    [Container: JavaScript and Angular]
    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    "] - style 17 fill:#438dd5,stroke:#2e6295,color:#ffffff - 18["
    Mobile App
    [Container: Xamarin]
    Provides a limited subset of
    the Internet banking
    functionality to customers
    via their mobile device.
    "] + 10["
    Web Application
    [Container: Java and Spring MVC]
    Delivers the static content
    and the Internet banking
    single page application.
    "] + style 10 fill:#438dd5,stroke:#2e6295,color:#ffffff + 11["
    API Application
    [Container: Java and Spring MVC]
    Provides Internet banking
    functionality via a
    JSON/HTTPS API.
    "] + style 11 fill:#438dd5,stroke:#2e6295,color:#ffffff + 18[("
    Database
    [Container: Oracle Database Schema]
    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    ")] style 18 fill:#438dd5,stroke:#2e6295,color:#ffffff - 19["
    Web Application
    [Container: Java and Spring MVC]
    Delivers the static content
    and the Internet banking
    single page application.
    "] - style 19 fill:#438dd5,stroke:#2e6295,color:#ffffff - 20["
    API Application
    [Container: Java and Spring MVC]
    Provides Internet banking
    functionality via a
    JSON/HTTPS API.
    "] - style 20 fill:#438dd5,stroke:#2e6295,color:#ffffff - 21[("
    Database
    [Container: Oracle Database Schema]
    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    ")] - style 21 fill:#438dd5,stroke:#2e6295,color:#ffffff + 8["
    Single-Page Application
    [Container: JavaScript and Angular]
    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    "] + style 8 fill:#438dd5,stroke:#2e6295,color:#ffffff + 9["
    Mobile App
    [Container: Xamarin]
    Provides a limited subset of
    the Internet banking
    functionality to customers
    via their mobile device.
    "] + style 9 fill:#438dd5,stroke:#2e6295,color:#ffffff end - 1-. "
    Visits bigbank.com/ib using
    [HTTPS]
    " .->19 - 1-. "
    Views account balances, and
    makes payments using
    " .->17 - 1-. "
    Views account balances, and
    makes payments using
    " .->18 - 19-. "
    Delivers to the customer's
    web browser
    " .->17 - 20-. "
    Reads from and writes to
    [JDBC]
    " .->21 - 20-. "
    Makes API calls to
    [XML/HTTPS]
    " .->4 - 20-. "
    Sends e-mail using
    [SMTP]
    " .->6 - 17-. "
    Makes API calls to
    [JSON/HTTPS]
    " .->20 - 18-. "
    Makes API calls to
    [JSON/HTTPS]
    " .->20 - 6-. "
    Sends e-mails to
    " .->1 + 5-. "
    Sends e-mails to
    " .->1 + 1-. "
    Visits bigbank.com/ib using
    [HTTPS]
    " .->10 + 1-. "
    Views account balances, and
    makes payments using
    " .->8 + 1-. "
    Views account balances, and
    makes payments using
    " .->9 + 10-. "
    Delivers to the customer's
    web browser
    " .->8 + 8-. "
    Makes API calls to
    [JSON/HTTPS]
    " .->11 + 9-. "
    Makes API calls to
    [JSON/HTTPS]
    " .->11 + 11-. "
    Reads from and writes to
    [SQL/TCP]
    " .->18 + 11-. "
    Makes API calls to
    [XML/HTTPS]
    " .->4 + 11-. "
    Sends e-mail using
    " .->5 end \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-DevelopmentDeployment.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-DevelopmentDeployment.mmd index 14b3194e6..b933bd4fb 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-DevelopmentDeployment.mmd +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-DevelopmentDeployment.mmd @@ -7,16 +7,23 @@ graph TB subgraph 50 [Developer Laptop] style 50 fill:#ffffff,stroke:#888888,color:#000000 - subgraph 51 [Docker Container - Web Server] + subgraph 51 [Web Browser] style 51 fill:#ffffff,stroke:#888888,color:#000000 - subgraph 52 [Apache Tomcat] - style 52 fill:#ffffff,stroke:#888888,color:#000000 + 52["
    Single-Page Application
    [Container: JavaScript and Angular]
    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    "] + style 52 fill:#438dd5,stroke:#2e6295,color:#ffffff + end + + subgraph 53 [Docker Container - Web Server] + style 53 fill:#ffffff,stroke:#888888,color:#000000 + + subgraph 54 [Apache Tomcat] + style 54 fill:#ffffff,stroke:#888888,color:#000000 - 53["
    Web Application
    [Container: Java and Spring MVC]
    Delivers the static content
    and the Internet banking
    single page application.
    "] - style 53 fill:#438dd5,stroke:#2e6295,color:#ffffff - 54["
    API Application
    [Container: Java and Spring MVC]
    Provides Internet banking
    functionality via a
    JSON/HTTPS API.
    "] - style 54 fill:#438dd5,stroke:#2e6295,color:#ffffff + 55["
    Web Application
    [Container: Java and Spring MVC]
    Delivers the static content
    and the Internet banking
    single page application.
    "] + style 55 fill:#438dd5,stroke:#2e6295,color:#ffffff + 57["
    API Application
    [Container: Java and Spring MVC]
    Provides Internet banking
    functionality via a
    JSON/HTTPS API.
    "] + style 57 fill:#438dd5,stroke:#2e6295,color:#ffffff end end @@ -33,29 +40,22 @@ graph TB end - subgraph 63 [Web Browser] - style 63 fill:#ffffff,stroke:#888888,color:#000000 - - 64["
    Single-Page Application
    [Container: JavaScript and Angular]
    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    "] - style 64 fill:#438dd5,stroke:#2e6295,color:#ffffff - end - end - subgraph 55 [Big Bank plc] - style 55 fill:#ffffff,stroke:#888888,color:#000000 + subgraph 63 [Big Bank plc] + style 63 fill:#ffffff,stroke:#888888,color:#000000 - subgraph 56 [bigbank-dev001] - style 56 fill:#ffffff,stroke:#888888,color:#000000 + subgraph 64 [bigbank-dev001] + style 64 fill:#ffffff,stroke:#888888,color:#000000 - 57["
    Mainframe Banking System
    [Software System]
    Stores all of the core
    banking information about
    customers, accounts,
    transactions, etc.
    "] - style 57 fill:#999999,stroke:#6b6b6b,color:#ffffff + 65["
    Mainframe Banking System
    [Software System]
    Stores all of the core
    banking information about
    customers, accounts,
    transactions, etc.
    "] + style 65 fill:#999999,stroke:#6b6b6b,color:#ffffff end end - 54-. "
    Makes API calls to
    [XML/HTTPS]
    " .->57 - 54-. "
    Reads from and writes to
    [JDBC]
    " .->61 - 64-. "
    Makes API calls to
    [JSON/HTTPS]
    " .->54 - 53-. "
    Delivers to the customer's
    web browser
    " .->64 + 55-. "
    Delivers to the customer's
    web browser
    " .->52 + 52-. "
    Makes API calls to
    [JSON/HTTPS]
    " .->57 + 57-. "
    Reads from and writes to
    [SQL/TCP]
    " .->61 + 57-. "
    Makes API calls to
    [XML/HTTPS]
    " .->65 end \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-LiveDeployment.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-LiveDeployment.mmd index 73b4fb23c..e4a56d3be 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-LiveDeployment.mmd +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-LiveDeployment.mmd @@ -26,67 +26,67 @@ graph TB subgraph 72 [Big Bank plc] style 72 fill:#ffffff,stroke:#888888,color:#000000 - subgraph 73 [bigbank-prod001] + subgraph 73 [bigbank-web***] style 73 fill:#ffffff,stroke:#888888,color:#000000 - 74["
    Mainframe Banking System
    [Software System]
    Stores all of the core
    banking information about
    customers, accounts,
    transactions, etc.
    "] - style 74 fill:#999999,stroke:#6b6b6b,color:#ffffff - end - - subgraph 75 [bigbank-web***] - style 75 fill:#ffffff,stroke:#888888,color:#000000 - - subgraph 76 [Apache Tomcat] - style 76 fill:#ffffff,stroke:#888888,color:#000000 + subgraph 74 [Apache Tomcat] + style 74 fill:#ffffff,stroke:#888888,color:#000000 - 77["
    Web Application
    [Container: Java and Spring MVC]
    Delivers the static content
    and the Internet banking
    single page application.
    "] - style 77 fill:#438dd5,stroke:#2e6295,color:#ffffff + 75["
    Web Application
    [Container: Java and Spring MVC]
    Delivers the static content
    and the Internet banking
    single page application.
    "] + style 75 fill:#438dd5,stroke:#2e6295,color:#ffffff end end - subgraph 79 [bigbank-api***] - style 79 fill:#ffffff,stroke:#888888,color:#000000 + subgraph 77 [bigbank-api***] + style 77 fill:#ffffff,stroke:#888888,color:#000000 - subgraph 80 [Apache Tomcat] - style 80 fill:#ffffff,stroke:#888888,color:#000000 + subgraph 78 [Apache Tomcat] + style 78 fill:#ffffff,stroke:#888888,color:#000000 - 81["
    API Application
    [Container: Java and Spring MVC]
    Provides Internet banking
    functionality via a
    JSON/HTTPS API.
    "] - style 81 fill:#438dd5,stroke:#2e6295,color:#ffffff + 79["
    API Application
    [Container: Java and Spring MVC]
    Provides Internet banking
    functionality via a
    JSON/HTTPS API.
    "] + style 79 fill:#438dd5,stroke:#2e6295,color:#ffffff end end - subgraph 85 [bigbank-db01] - style 85 fill:#ffffff,stroke:#888888,color:#000000 + subgraph 82 [bigbank-db01] + style 82 fill:#ffffff,stroke:#888888,color:#000000 - subgraph 86 [Oracle - Primary] - style 86 fill:#ffffff,stroke:#888888,color:#000000 + subgraph 83 [Oracle - Primary] + style 83 fill:#ffffff,stroke:#888888,color:#000000 - 87[("
    Database
    [Container: Oracle Database Schema]
    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    ")] - style 87 fill:#438dd5,stroke:#2e6295,color:#ffffff + 84[("
    Database
    [Container: Oracle Database Schema]
    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    ")] + style 84 fill:#438dd5,stroke:#2e6295,color:#ffffff end end - subgraph 89 [bigbank-db02] - style 89 fill:#ffffff,stroke:#888888,color:#000000 + subgraph 86 [bigbank-db02] + style 86 fill:#ffffff,stroke:#888888,color:#000000 - subgraph 90 [Oracle - Secondary] - style 90 fill:#ffffff,stroke:#888888,color:#000000 + subgraph 87 [Oracle - Secondary] + style 87 fill:#ffffff,stroke:#888888,color:#000000 - 91[("
    Database
    [Container: Oracle Database Schema]
    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    ")] - style 91 fill:#438dd5,stroke:#2e6295,color:#ffffff + 88[("
    Database
    [Container: Oracle Database Schema]
    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    ")] + style 88 fill:#438dd5,stroke:#2e6295,color:#ffffff end end + subgraph 90 [bigbank-prod001] + style 90 fill:#ffffff,stroke:#888888,color:#000000 + + 91["
    Mainframe Banking System
    [Software System]
    Stores all of the core
    banking information about
    customers, accounts,
    transactions, etc.
    "] + style 91 fill:#999999,stroke:#6b6b6b,color:#ffffff + end + end - 77-. "
    Delivers to the customer's
    web browser
    " .->71 - 68-. "
    Makes API calls to
    [JSON/HTTPS]
    " .->81 - 71-. "
    Makes API calls to
    [JSON/HTTPS]
    " .->81 - 81-. "
    Makes API calls to
    [XML/HTTPS]
    " .->74 - 81-. "
    Reads from and writes to
    [JDBC]
    " .->87 - 81-. "
    Reads from and writes to
    [JDBC]
    " .->91 + 75-. "
    Delivers to the customer's
    web browser
    " .->71 + 68-. "
    Makes API calls to
    [JSON/HTTPS]
    " .->79 + 71-. "
    Makes API calls to
    [JSON/HTTPS]
    " .->79 + 79-. "
    Reads from and writes to
    [SQL/TCP]
    " .->84 + 79-. "
    Reads from and writes to
    [SQL/TCP]
    " .->88 + 79-. "
    Makes API calls to
    [XML/HTTPS]
    " .->91 end \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SignIn-sequence.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SignIn-sequence.mmd index 655ff882c..f25511e2f 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SignIn-sequence.mmd +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SignIn-sequence.mmd @@ -1,13 +1,13 @@ sequenceDiagram - participant 17 as Single-Page Application
    [Container: JavaScript and Angular] - participant 29 as Sign In Controller
    [Component: Spring MVC Rest Controller] - participant 32 as Security Component
    [Component: Spring Bean] - participant 21 as Database
    [Container: Oracle Database Schema] + participant 8 as Single-Page Application
    [Container: JavaScript and Angular] + participant 12 as Sign In Controller
    [Component: Spring MVC Rest Controller] + participant 15 as Security Component
    [Component: Spring Bean] + participant 18 as Database
    [Container: Oracle Database Schema] - 17->>29: Submits credentials to
    [JSON/HTTPS] - 29->>32: Validates credentials using - 32->>21: select * from users where username = ?
    [JDBC] - 21-->>32: Returns user data to
    [JDBC] - 32-->>29: Returns true if the hashed password matches - 29-->>17: Sends back an authentication token to
    [JSON/HTTPS] \ No newline at end of file + 8->>12: Submits credentials to
    [JSON/HTTPS] + 12->>15: Validates credentials using + 15->>18: select * from users where username = ?
    [SQL/TCP] + 18-->>15: Returns user data to
    [SQL/TCP] + 15-->>12: Returns true if the hashed password matches + 12-->>8: Sends back an authentication token to
    [JSON/HTTPS] \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SignIn.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SignIn.mmd index ad523b076..ffccd9df7 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SignIn.mmd +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SignIn.mmd @@ -4,24 +4,24 @@ graph TB subgraph diagram ["API Application - Dynamic"] style diagram fill:#ffffff,stroke:#ffffff - subgraph 20 [API Application] - style 20 fill:#ffffff,stroke:#2e6295,color:#2e6295 + subgraph 11 [API Application] + style 11 fill:#ffffff,stroke:#2e6295,color:#2e6295 - 29["
    Sign In Controller
    [Component: Spring MVC Rest Controller]
    Allows users to sign in to
    the Internet Banking System.
    "] - style 29 fill:#85bbf0,stroke:#5d82a8,color:#000000 - 32["
    Security Component
    [Component: Spring Bean]
    Provides functionality
    related to signing in,
    changing passwords, etc.
    "] - style 32 fill:#85bbf0,stroke:#5d82a8,color:#000000 + 12["
    Sign In Controller
    [Component: Spring MVC Rest Controller]
    Allows users to sign in to
    the Internet Banking System.
    "] + style 12 fill:#85bbf0,stroke:#5d82a8,color:#000000 + 15["
    Security Component
    [Component: Spring Bean]
    Provides functionality
    related to signing in,
    changing passwords, etc.
    "] + style 15 fill:#85bbf0,stroke:#5d82a8,color:#000000 end - 17["
    Single-Page Application
    [Container: JavaScript and Angular]
    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    "] - style 17 fill:#438dd5,stroke:#2e6295,color:#ffffff - 21[("
    Database
    [Container: Oracle Database Schema]
    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    ")] - style 21 fill:#438dd5,stroke:#2e6295,color:#ffffff + 18[("
    Database
    [Container: Oracle Database Schema]
    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    ")] + style 18 fill:#438dd5,stroke:#2e6295,color:#ffffff + 8["
    Single-Page Application
    [Container: JavaScript and Angular]
    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    "] + style 8 fill:#438dd5,stroke:#2e6295,color:#ffffff - 17-. "
    1. Submits credentials to
    [JSON/HTTPS]
    " .->29 - 29-. "
    2. Validates credentials
    using
    " .->32 - 32-. "
    3. select * from users where
    username = ?
    [JDBC]
    " .->21 - 21-. "
    4. Returns user data to
    [JDBC]
    " .->32 - 32-. "
    5. Returns true if the hashed
    password matches
    " .->29 - 29-. "
    6. Sends back an
    authentication token to
    [JSON/HTTPS]
    " .->17 + 8-. "
    1. Submits credentials to
    [JSON/HTTPS]
    " .->12 + 12-. "
    2. Validates credentials
    using
    " .->15 + 15-. "
    3. select * from users where
    username = ?
    [SQL/TCP]
    " .->18 + 18-. "
    4. Returns user data to
    [SQL/TCP]
    " .->15 + 15-. "
    5. Returns true if the hashed
    password matches
    " .->12 + 12-. "
    6. Sends back an
    authentication token to
    [JSON/HTTPS]
    " .->8 end \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SystemContext.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SystemContext.mmd index 184335671..66a0a966a 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SystemContext.mmd +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SystemContext.mmd @@ -4,17 +4,22 @@ graph TB subgraph diagram ["Internet Banking System - System Context"] style diagram fill:#ffffff,stroke:#ffffff + subgraph group1 [Big Bank plc] + style group1 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 + + 4["
    Mainframe Banking System
    [Software System]
    Stores all of the core
    banking information about
    customers, accounts,
    transactions, etc.
    "] + style 4 fill:#999999,stroke:#6b6b6b,color:#ffffff + 5["
    E-mail System
    [Software System]
    The internal Microsoft
    Exchange e-mail system.
    "] + style 5 fill:#999999,stroke:#6b6b6b,color:#ffffff + 7["
    Internet Banking System
    [Software System]
    Allows customers to view
    information about their bank
    accounts, and make payments.
    "] + style 7 fill:#1168bd,stroke:#0b4884,color:#ffffff + end + 1["
    Personal Banking Customer
    [Person]
    A customer of the bank, with
    personal bank accounts.
    "] style 1 fill:#08427b,stroke:#052e56,color:#ffffff - 2["
    Internet Banking System
    [Software System]
    Allows customers to view
    information about their bank
    accounts, and make payments.
    "] - style 2 fill:#1168bd,stroke:#0b4884,color:#ffffff - 4["
    Mainframe Banking System
    [Software System]
    Stores all of the core
    banking information about
    customers, accounts,
    transactions, etc.
    "] - style 4 fill:#999999,stroke:#6b6b6b,color:#ffffff - 6["
    E-mail System
    [Software System]
    The internal Microsoft
    Exchange e-mail system.
    "] - style 6 fill:#999999,stroke:#6b6b6b,color:#ffffff - 1-. "
    Views account balances, and
    makes payments using
    " .->2 - 2-. "
    Gets account information
    from, and makes payments
    using
    " .->4 - 2-. "
    Sends e-mail using
    " .->6 - 6-. "
    Sends e-mails to
    " .->1 + 1-. "
    Views account balances, and
    makes payments using
    " .->7 + 7-. "
    Gets account information
    from, and makes payments
    using
    " .->4 + 7-. "
    Sends e-mail using
    " .->5 + 5-. "
    Sends e-mails to
    " .->1 end \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SystemLandscape.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SystemLandscape.mmd index 9e382989d..f763af979 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SystemLandscape.mmd +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SystemLandscape.mmd @@ -4,33 +4,33 @@ graph TB subgraph diagram ["System Landscape"] style diagram fill:#ffffff,stroke:#ffffff - subgraph enterprise [Big Bank plc] - style enterprise fill:#ffffff,stroke:#444444,color:#444444 + subgraph group1 [Big Bank plc] + style group1 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 - 12["
    Customer Service Staff
    [Person]
    Customer service staff within
    the bank.
    "] - style 12 fill:#999999,stroke:#6b6b6b,color:#ffffff - 15["
    Back Office Staff
    [Person]
    Administration and support
    staff within the bank.
    "] - style 15 fill:#999999,stroke:#6b6b6b,color:#ffffff - 2["
    Internet Banking System
    [Software System]
    Allows customers to view
    information about their bank
    accounts, and make payments.
    "] - style 2 fill:#1168bd,stroke:#0b4884,color:#ffffff + 2["
    Customer Service Staff
    [Person]
    Customer service staff within
    the bank.
    "] + style 2 fill:#999999,stroke:#6b6b6b,color:#ffffff + 3["
    Back Office Staff
    [Person]
    Administration and support
    staff within the bank.
    "] + style 3 fill:#999999,stroke:#6b6b6b,color:#ffffff 4["
    Mainframe Banking System
    [Software System]
    Stores all of the core
    banking information about
    customers, accounts,
    transactions, etc.
    "] style 4 fill:#999999,stroke:#6b6b6b,color:#ffffff - 6["
    E-mail System
    [Software System]
    The internal Microsoft
    Exchange e-mail system.
    "] + 5["
    E-mail System
    [Software System]
    The internal Microsoft
    Exchange e-mail system.
    "] + style 5 fill:#999999,stroke:#6b6b6b,color:#ffffff + 6["
    ATM
    [Software System]
    Allows customers to withdraw
    cash.
    "] style 6 fill:#999999,stroke:#6b6b6b,color:#ffffff - 9["
    ATM
    [Software System]
    Allows customers to withdraw
    cash.
    "] - style 9 fill:#999999,stroke:#6b6b6b,color:#ffffff + 7["
    Internet Banking System
    [Software System]
    Allows customers to view
    information about their bank
    accounts, and make payments.
    "] + style 7 fill:#1168bd,stroke:#0b4884,color:#ffffff end 1["
    Personal Banking Customer
    [Person]
    A customer of the bank, with
    personal bank accounts.
    "] style 1 fill:#08427b,stroke:#052e56,color:#ffffff - 9-. "
    Uses
    " .->4 - 1-. "
    Withdraws cash using
    " .->9 - 12-. "
    Uses
    " .->4 - 1-. "
    Asks questions to
    [Telephone]
    " .->12 - 15-. "
    Uses
    " .->4 - 1-. "
    Views account balances, and
    makes payments using
    " .->2 - 2-. "
    Gets account information
    from, and makes payments
    using
    " .->4 - 2-. "
    Sends e-mail using
    " .->6 - 6-. "
    Sends e-mails to
    " .->1 + 1-. "
    Views account balances, and
    makes payments using
    " .->7 + 7-. "
    Gets account information
    from, and makes payments
    using
    " .->4 + 7-. "
    Sends e-mail using
    " .->5 + 5-. "
    Sends e-mails to
    " .->1 + 1-. "
    Asks questions to
    [Telephone]
    " .->2 + 2-. "
    Uses
    " .->4 + 1-. "
    Withdraws cash using
    " .->6 + 6-. "
    Uses
    " .->4 + 3-. "
    Uses
    " .->4 end \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-Components.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-Components.mmd index 79c389eca..8a379ec14 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-Components.mmd +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-Components.mmd @@ -10,7 +10,7 @@ graph TB subgraph 6 [F] style 6 fill:#ffffff,stroke:#9a9a9a,color:#9a9a9a - subgraph group1 [Group 4] + subgraph group1 [Group 5] style group1 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 8["
    H
    [Component]
    "] diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-Containers.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-Containers.mmd index 3c5abba9d..c11fc9560 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-Containers.mmd +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-Containers.mmd @@ -10,7 +10,7 @@ graph TB subgraph 4 [D] style 4 fill:#ffffff,stroke:#9a9a9a,color:#9a9a9a - subgraph group1 [Group 3] + subgraph group1 [Group 4] style group1 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 6["
    F
    [Container]
    "] diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-SystemLandscape.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-SystemLandscape.mmd index 5ca147d80..24cb88486 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-SystemLandscape.mmd +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-SystemLandscape.mmd @@ -4,25 +4,25 @@ graph TB subgraph diagram ["System Landscape"] style diagram fill:#ffffff,stroke:#ffffff - subgraph enterprise [Enterprise] - style enterprise fill:#ffffff,stroke:#444444,color:#444444 + subgraph group1 [Group 1] + style group1 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 - subgraph group1 [Group 2] - style group1 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 + 2["
    B
    [Software System]
    "] + style 2 fill:#dddddd,stroke:#9a9a9a,color:#000000 + end - 4["
    D
    [Software System]
    "] - style 4 fill:#dddddd,stroke:#9a9a9a,color:#000000 - end + subgraph group2 [Group 2] + style group2 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 3["
    C
    [Software System]
    "] style 3 fill:#dddddd,stroke:#9a9a9a,color:#000000 - end + subgraph group3 [Group 3] + style group3 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 - subgraph group2 [Group 1] - style group2 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 + 4["
    D
    [Software System]
    "] + style 4 fill:#dddddd,stroke:#9a9a9a,color:#000000 + end - 2["
    B
    [Software System]
    "] - style 2 fill:#dddddd,stroke:#9a9a9a,color:#000000 end 1["
    A
    [Software System]
    "] diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java index 0acf637cb..4effe64c2 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java @@ -28,7 +28,7 @@ public void test_BigBankPlcExample() throws Exception { Diagram diagram = diagrams.stream().filter(d -> d.getKey().equals("SystemLandscape")).findFirst().get(); String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemLandscape.puml")); assertEquals(expected, diagram.getDefinition()); - assertEquals(3, diagram.getFrames().size()); + assertEquals(0, diagram.getFrames().size()); //assertEquals("", diagram.getLegend().getDefinition()); @@ -55,12 +55,12 @@ public void test_BigBankPlcExample() throws Exception { diagram = diagrams.stream().filter(md -> md.getKey().equals("DevelopmentDeployment")).findFirst().get(); expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/36141-DevelopmentDeployment.puml")); assertEquals(expected, diagram.getDefinition()); - assertEquals(4, diagram.getFrames().size()); + assertEquals(3, diagram.getFrames().size()); diagram = diagrams.stream().filter(md -> md.getKey().equals("LiveDeployment")).findFirst().get(); expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/36141-LiveDeployment.puml")); assertEquals(expected, diagram.getDefinition()); - assertEquals(6, diagram.getFrames().size()); + assertEquals(5, diagram.getFrames().size()); // and the sequence diagram version workspace.getViews().getConfiguration().addProperty(exporter.PLANTUML_SEQUENCE_DIAGRAM_PROPERTY, "true"); diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Components.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Components.puml index 47509b6c7..3ae85ef6e 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Components.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Components.puml @@ -20,10 +20,10 @@ AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle AddBoundaryTag("Container", $bgColor="#ffffff", $borderColor="#2e6295", $fontColor="#2e6295", $shadowing="", $borderStyle="Solid", $borderThickness="1") System(MainframeBankingSystem, "Mainframe Banking System", $descr="Stores all of the core banking information about customers, accounts, transactions, etc.", $tags="Software System,Existing System", $link="") -Container(InternetBankingSystem.SinglePageApplication, "Single-Page Application", $techn="JavaScript and Angular", $descr="Provides all of the Internet banking functionality to customers via their web browser.", $tags="Container,Web Browser", $link="") System(EmailSystem, "E-mail System", $descr="The internal Microsoft Exchange e-mail system.", $tags="Software System,Existing System", $link="") -Container(InternetBankingSystem.MobileApp, "Mobile App", $techn="Xamarin", $descr="Provides a limited subset of the Internet banking functionality to customers via their mobile device.", $tags="Container,Mobile App", $link="") ContainerDb(InternetBankingSystem.Database, "Database", $techn="Oracle Database Schema", $descr="Stores user registration information, hashed authentication credentials, access logs, etc.", $tags="Container,Database", $link="") +Container(InternetBankingSystem.SinglePageApplication, "Single-Page Application", $techn="JavaScript and Angular", $descr="Provides all of the Internet banking functionality to customers via their web browser.", $tags="Container,Web Browser", $link="") +Container(InternetBankingSystem.MobileApp, "Mobile App", $techn="Xamarin", $descr="Provides a limited subset of the Internet banking functionality to customers via their mobile device.", $tags="Container,Mobile App", $link="") Container_Boundary("InternetBankingSystem.APIApplication_boundary", "API Application", $tags="Container") { Component(InternetBankingSystem.APIApplication.SignInController, "Sign In Controller", $techn="Spring MVC Rest Controller", $descr="Allows users to sign in to the Internet Banking System.", $tags="Component", $link="") @@ -35,17 +35,17 @@ Container_Boundary("InternetBankingSystem.APIApplication_boundary", "API Applica } Rel(InternetBankingSystem.SinglePageApplication, InternetBankingSystem.APIApplication.SignInController, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") -Rel(InternetBankingSystem.SinglePageApplication, InternetBankingSystem.APIApplication.ResetPasswordController, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") Rel(InternetBankingSystem.SinglePageApplication, InternetBankingSystem.APIApplication.AccountsSummaryController, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") +Rel(InternetBankingSystem.SinglePageApplication, InternetBankingSystem.APIApplication.ResetPasswordController, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") Rel(InternetBankingSystem.MobileApp, InternetBankingSystem.APIApplication.SignInController, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") -Rel(InternetBankingSystem.MobileApp, InternetBankingSystem.APIApplication.ResetPasswordController, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") Rel(InternetBankingSystem.MobileApp, InternetBankingSystem.APIApplication.AccountsSummaryController, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") +Rel(InternetBankingSystem.MobileApp, InternetBankingSystem.APIApplication.ResetPasswordController, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") Rel(InternetBankingSystem.APIApplication.SignInController, InternetBankingSystem.APIApplication.SecurityComponent, "Uses", $techn="", $tags="Relationship", $link="") Rel(InternetBankingSystem.APIApplication.AccountsSummaryController, InternetBankingSystem.APIApplication.MainframeBankingSystemFacade, "Uses", $techn="", $tags="Relationship", $link="") Rel(InternetBankingSystem.APIApplication.ResetPasswordController, InternetBankingSystem.APIApplication.SecurityComponent, "Uses", $techn="", $tags="Relationship", $link="") Rel(InternetBankingSystem.APIApplication.ResetPasswordController, InternetBankingSystem.APIApplication.EmailComponent, "Uses", $techn="", $tags="Relationship", $link="") -Rel(InternetBankingSystem.APIApplication.SecurityComponent, InternetBankingSystem.Database, "Reads from and writes to", $techn="JDBC", $tags="Relationship", $link="") -Rel(InternetBankingSystem.APIApplication.MainframeBankingSystemFacade, MainframeBankingSystem, "Uses", $techn="XML/HTTPS", $tags="Relationship", $link="") +Rel(InternetBankingSystem.APIApplication.SecurityComponent, InternetBankingSystem.Database, "Reads from and writes to", $techn="SQL/TCP", $tags="Relationship", $link="") +Rel(InternetBankingSystem.APIApplication.MainframeBankingSystemFacade, MainframeBankingSystem, "Makes API calls to", $techn="XML/HTTPS", $tags="Relationship", $link="") Rel(InternetBankingSystem.APIApplication.EmailComponent, EmailSystem, "Sends e-mail using", $techn="", $tags="Relationship", $link="") SHOW_LEGEND(true) diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Containers.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Containers.puml index 9badc190c..70127ca7a 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Containers.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Containers.puml @@ -11,36 +11,36 @@ top to bottom direction AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") AddElementTag("Container,Mobile App", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Person", $bgColor="#08427b", $borderColor="#052e56", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") AddElementTag("Container", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Person,Customer", $bgColor="#08427b", $borderColor="#052e56", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") AddBoundaryTag("Software System", $bgColor="#ffffff", $borderColor="#0b4884", $fontColor="#0b4884", $shadowing="", $borderStyle="Solid", $borderThickness="1") -Person_Ext(PersonalBankingCustomer, "Personal Banking Customer", $descr="A customer of the bank, with personal bank accounts.", $tags="Person", $link="") +Person(PersonalBankingCustomer, "Personal Banking Customer", $descr="A customer of the bank, with personal bank accounts.", $tags="Person,Customer", $link="") System(MainframeBankingSystem, "Mainframe Banking System", $descr="Stores all of the core banking information about customers, accounts, transactions, etc.", $tags="Software System,Existing System", $link="") System(EmailSystem, "E-mail System", $descr="The internal Microsoft Exchange e-mail system.", $tags="Software System,Existing System", $link="") System_Boundary("InternetBankingSystem_boundary", "Internet Banking System", $tags="Software System") { - Container(InternetBankingSystem.SinglePageApplication, "Single-Page Application", $techn="JavaScript and Angular", $descr="Provides all of the Internet banking functionality to customers via their web browser.", $tags="Container,Web Browser", $link="") - Container(InternetBankingSystem.MobileApp, "Mobile App", $techn="Xamarin", $descr="Provides a limited subset of the Internet banking functionality to customers via their mobile device.", $tags="Container,Mobile App", $link="") Container(InternetBankingSystem.WebApplication, "Web Application", $techn="Java and Spring MVC", $descr="Delivers the static content and the Internet banking single page application.", $tags="Container", $link="") Container(InternetBankingSystem.APIApplication, "API Application", $techn="Java and Spring MVC", $descr="Provides Internet banking functionality via a JSON/HTTPS API.", $tags="Container", $link="") ContainerDb(InternetBankingSystem.Database, "Database", $techn="Oracle Database Schema", $descr="Stores user registration information, hashed authentication credentials, access logs, etc.", $tags="Container,Database", $link="") + Container(InternetBankingSystem.SinglePageApplication, "Single-Page Application", $techn="JavaScript and Angular", $descr="Provides all of the Internet banking functionality to customers via their web browser.", $tags="Container,Web Browser", $link="") + Container(InternetBankingSystem.MobileApp, "Mobile App", $techn="Xamarin", $descr="Provides a limited subset of the Internet banking functionality to customers via their mobile device.", $tags="Container,Mobile App", $link="") } +Rel(EmailSystem, PersonalBankingCustomer, "Sends e-mails to", $techn="", $tags="Relationship", $link="") Rel(PersonalBankingCustomer, InternetBankingSystem.WebApplication, "Visits bigbank.com/ib using", $techn="HTTPS", $tags="Relationship", $link="") Rel(PersonalBankingCustomer, InternetBankingSystem.SinglePageApplication, "Views account balances, and makes payments using", $techn="", $tags="Relationship", $link="") Rel(PersonalBankingCustomer, InternetBankingSystem.MobileApp, "Views account balances, and makes payments using", $techn="", $tags="Relationship", $link="") Rel(InternetBankingSystem.WebApplication, InternetBankingSystem.SinglePageApplication, "Delivers to the customer's web browser", $techn="", $tags="Relationship", $link="") -Rel(InternetBankingSystem.APIApplication, InternetBankingSystem.Database, "Reads from and writes to", $techn="JDBC", $tags="Relationship", $link="") -Rel(InternetBankingSystem.APIApplication, MainframeBankingSystem, "Makes API calls to", $techn="XML/HTTPS", $tags="Relationship", $link="") -Rel(InternetBankingSystem.APIApplication, EmailSystem, "Sends e-mail using", $techn="SMTP", $tags="Relationship", $link="") Rel(InternetBankingSystem.SinglePageApplication, InternetBankingSystem.APIApplication, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") Rel(InternetBankingSystem.MobileApp, InternetBankingSystem.APIApplication, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") -Rel(EmailSystem, PersonalBankingCustomer, "Sends e-mails to", $techn="", $tags="Relationship", $link="") +Rel(InternetBankingSystem.APIApplication, InternetBankingSystem.Database, "Reads from and writes to", $techn="SQL/TCP", $tags="Relationship", $link="") +Rel(InternetBankingSystem.APIApplication, MainframeBankingSystem, "Makes API calls to", $techn="XML/HTTPS", $tags="Relationship", $link="") +Rel(InternetBankingSystem.APIApplication, EmailSystem, "Sends e-mail using", $techn="", $tags="Relationship", $link="") SHOW_LEGEND(true) @enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-DevelopmentDeployment.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-DevelopmentDeployment.puml index d3e9a8d69..f3e11defa 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-DevelopmentDeployment.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-DevelopmentDeployment.puml @@ -12,31 +12,31 @@ top to bottom direction AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") AddElementTag("Element", $bgColor="#ffffff", $borderColor="#888888", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") AddElementTag("Container", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") -Deployment_Node(Development.DeveloperLaptop, "Developer Laptop", $type="Microsoft Windows 10 or Apple macOS", $descr="A developer laptop.", $tags="Element", $link="") { - Deployment_Node(Development.DeveloperLaptop.DockerContainerWebServer, "Docker Container - Web Server", $type="Docker", $descr="A Docker container.", $tags="Element", $link="") { - Deployment_Node(Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat, "Apache Tomcat", $type="Apache Tomcat 8.x", $descr="An open source Java EE web server.", $tags="Element", $link="") { +Deployment_Node(Development.DeveloperLaptop, "Developer Laptop", $type="Microsoft Windows 10 or Apple macOS", $descr="", $tags="Element", $link="") { + Deployment_Node(Development.DeveloperLaptop.WebBrowser, "Web Browser", $type="Chrome, Firefox, Safari, or Edge", $descr="", $tags="Element", $link="") { + Container(Development.DeveloperLaptop.WebBrowser.SinglePageApplication_1, "Single-Page Application", $techn="JavaScript and Angular", $descr="Provides all of the Internet banking functionality to customers via their web browser.", $tags="Container,Web Browser", $link="") + } + + Deployment_Node(Development.DeveloperLaptop.DockerContainerWebServer, "Docker Container - Web Server", $type="Docker", $descr="", $tags="Element", $link="") { + Deployment_Node(Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat, "Apache Tomcat", $type="Apache Tomcat 8.x", $descr="", $tags="Element", $link="") { Container(Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.WebApplication_1, "Web Application", $techn="Java and Spring MVC", $descr="Delivers the static content and the Internet banking single page application.", $tags="Container", $link="") Container(Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.APIApplication_1, "API Application", $techn="Java and Spring MVC", $descr="Provides Internet banking functionality via a JSON/HTTPS API.", $tags="Container", $link="") } } - Deployment_Node(Development.DeveloperLaptop.DockerContainerDatabaseServer, "Docker Container - Database Server", $type="Docker", $descr="A Docker container.", $tags="Element", $link="") { - Deployment_Node(Development.DeveloperLaptop.DockerContainerDatabaseServer.DatabaseServer, "Database Server", $type="Oracle 12c", $descr="A development database.", $tags="Element", $link="") { + Deployment_Node(Development.DeveloperLaptop.DockerContainerDatabaseServer, "Docker Container - Database Server", $type="Docker", $descr="", $tags="Element", $link="") { + Deployment_Node(Development.DeveloperLaptop.DockerContainerDatabaseServer.DatabaseServer, "Database Server", $type="Oracle 12c", $descr="", $tags="Element", $link="") { ContainerDb(Development.DeveloperLaptop.DockerContainerDatabaseServer.DatabaseServer.Database_1, "Database", $techn="Oracle Database Schema", $descr="Stores user registration information, hashed authentication credentials, access logs, etc.", $tags="Container,Database", $link="") } } - Deployment_Node(Development.DeveloperLaptop.WebBrowser, "Web Browser", $type="Chrome, Firefox, Safari, or Edge", $descr="", $tags="Element", $link="") { - Container(Development.DeveloperLaptop.WebBrowser.SinglePageApplication_1, "Single-Page Application", $techn="JavaScript and Angular", $descr="Provides all of the Internet banking functionality to customers via their web browser.", $tags="Container,Web Browser", $link="") - } - } Deployment_Node(Development.BigBankplc, "Big Bank plc", $type="Big Bank plc data center", $descr="", $tags="Element", $link="") { @@ -46,10 +46,10 @@ Deployment_Node(Development.BigBankplc, "Big Bank plc", $type="Big Bank plc data } -Rel(Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.APIApplication_1, Development.BigBankplc.bigbankdev001.MainframeBankingSystem_1, "Makes API calls to", $techn="XML/HTTPS", $tags="Relationship", $link="") -Rel(Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.APIApplication_1, Development.DeveloperLaptop.DockerContainerDatabaseServer.DatabaseServer.Database_1, "Reads from and writes to", $techn="JDBC", $tags="Relationship", $link="") -Rel(Development.DeveloperLaptop.WebBrowser.SinglePageApplication_1, Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.APIApplication_1, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") Rel(Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.WebApplication_1, Development.DeveloperLaptop.WebBrowser.SinglePageApplication_1, "Delivers to the customer's web browser", $techn="", $tags="Relationship", $link="") +Rel(Development.DeveloperLaptop.WebBrowser.SinglePageApplication_1, Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.APIApplication_1, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") +Rel(Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.APIApplication_1, Development.DeveloperLaptop.DockerContainerDatabaseServer.DatabaseServer.Database_1, "Reads from and writes to", $techn="SQL/TCP", $tags="Relationship", $link="") +Rel(Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.APIApplication_1, Development.BigBankplc.bigbankdev001.MainframeBankingSystem_1, "Makes API calls to", $techn="XML/HTTPS", $tags="Relationship", $link="") SHOW_LEGEND(true) @enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-LiveDeployment.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-LiveDeployment.puml index 6a33ec429..d3b2df46c 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-LiveDeployment.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-LiveDeployment.puml @@ -13,12 +13,10 @@ AddElementTag("Failover", $bgColor="#ffffff", $borderColor="#888888", $fontColor AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") AddElementTag("Element", $bgColor="#ffffff", $borderColor="#888888", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") AddElementTag("Container,Mobile App", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Container,Database,Failover", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") AddElementTag("Container", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddRelTag("Failover", $textColor="#707070", $lineColor="#707070", $lineStyle = "") AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") Deployment_Node(Live.Customersmobiledevice, "Customer's mobile device", $type="Apple iOS or Android", $descr="", $tags="Element", $link="") { @@ -33,46 +31,46 @@ Deployment_Node(Live.Customerscomputer, "Customer's computer", $type="Microsoft } Deployment_Node(Live.BigBankplc, "Big Bank plc", $type="Big Bank plc data center", $descr="", $tags="Element", $link="") { - Deployment_Node(Live.BigBankplc.bigbankprod001, "bigbank-prod001", $type="", $descr="", $tags="Element", $link="") { - System(Live.BigBankplc.bigbankprod001.MainframeBankingSystem_1, "Mainframe Banking System", $descr="Stores all of the core banking information about customers, accounts, transactions, etc.", $tags="Software System,Existing System", $link="") - } - - Deployment_Node(Live.BigBankplc.bigbankweb, "bigbank-web*** (x4)", $type="Ubuntu 16.04 LTS", $descr="A web server residing in the web server farm, accessed via F5 BIG-IP LTMs.", $tags="Element", $link="") { - Deployment_Node(Live.BigBankplc.bigbankweb.ApacheTomcat, "Apache Tomcat", $type="Apache Tomcat 8.x", $descr="An open source Java EE web server.", $tags="Element", $link="") { + Deployment_Node(Live.BigBankplc.bigbankweb, "bigbank-web*** (x4)", $type="Ubuntu 16.04 LTS", $descr="", $tags="Element", $link="") { + Deployment_Node(Live.BigBankplc.bigbankweb.ApacheTomcat, "Apache Tomcat", $type="Apache Tomcat 8.x", $descr="", $tags="Element", $link="") { Container(Live.BigBankplc.bigbankweb.ApacheTomcat.WebApplication_1, "Web Application", $techn="Java and Spring MVC", $descr="Delivers the static content and the Internet banking single page application.", $tags="Container", $link="") } } - Deployment_Node(Live.BigBankplc.bigbankapi, "bigbank-api*** (x8)", $type="Ubuntu 16.04 LTS", $descr="A web server residing in the web server farm, accessed via F5 BIG-IP LTMs.", $tags="Element", $link="") { - Deployment_Node(Live.BigBankplc.bigbankapi.ApacheTomcat, "Apache Tomcat", $type="Apache Tomcat 8.x", $descr="An open source Java EE web server.", $tags="Element", $link="") { + Deployment_Node(Live.BigBankplc.bigbankapi, "bigbank-api*** (x8)", $type="Ubuntu 16.04 LTS", $descr="", $tags="Element", $link="") { + Deployment_Node(Live.BigBankplc.bigbankapi.ApacheTomcat, "Apache Tomcat", $type="Apache Tomcat 8.x", $descr="", $tags="Element", $link="") { Container(Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1, "API Application", $techn="Java and Spring MVC", $descr="Provides Internet banking functionality via a JSON/HTTPS API.", $tags="Container", $link="") } } - Deployment_Node(Live.BigBankplc.bigbankdb01, "bigbank-db01", $type="Ubuntu 16.04 LTS", $descr="The primary database server.", $tags="Element", $link="") { - Deployment_Node(Live.BigBankplc.bigbankdb01.OraclePrimary, "Oracle - Primary", $type="Oracle 12c", $descr="The primary, live database server.", $tags="Element", $link="") { + Deployment_Node(Live.BigBankplc.bigbankdb01, "bigbank-db01", $type="Ubuntu 16.04 LTS", $descr="", $tags="Element", $link="") { + Deployment_Node(Live.BigBankplc.bigbankdb01.OraclePrimary, "Oracle - Primary", $type="Oracle 12c", $descr="", $tags="Element", $link="") { ContainerDb(Live.BigBankplc.bigbankdb01.OraclePrimary.Database_1, "Database", $techn="Oracle Database Schema", $descr="Stores user registration information, hashed authentication credentials, access logs, etc.", $tags="Container,Database", $link="") } } - Deployment_Node(Live.BigBankplc.bigbankdb02, "bigbank-db02", $type="Ubuntu 16.04 LTS", $descr="The secondary database server.", $tags="Failover", $link="") { - Deployment_Node(Live.BigBankplc.bigbankdb02.OracleSecondary, "Oracle - Secondary", $type="Oracle 12c", $descr="A secondary, standby database server, used for failover purposes only.", $tags="Failover", $link="") { - ContainerDb(Live.BigBankplc.bigbankdb02.OracleSecondary.Database_2, "Database", $techn="Oracle Database Schema", $descr="Stores user registration information, hashed authentication credentials, access logs, etc.", $tags="Container,Database,Failover", $link="") + Deployment_Node(Live.BigBankplc.bigbankdb02, "bigbank-db02", $type="Ubuntu 16.04 LTS", $descr="", $tags="Failover", $link="") { + Deployment_Node(Live.BigBankplc.bigbankdb02.OracleSecondary, "Oracle - Secondary", $type="Oracle 12c", $descr="", $tags="Failover", $link="") { + ContainerDb(Live.BigBankplc.bigbankdb02.OracleSecondary.Database_1, "Database", $techn="Oracle Database Schema", $descr="Stores user registration information, hashed authentication credentials, access logs, etc.", $tags="Container,Database", $link="") } } + Deployment_Node(Live.BigBankplc.bigbankprod001, "bigbank-prod001", $type="", $descr="", $tags="Element", $link="") { + System(Live.BigBankplc.bigbankprod001.MainframeBankingSystem_1, "Mainframe Banking System", $descr="Stores all of the core banking information about customers, accounts, transactions, etc.", $tags="Software System,Existing System", $link="") + } + } Rel(Live.BigBankplc.bigbankweb.ApacheTomcat.WebApplication_1, Live.Customerscomputer.WebBrowser.SinglePageApplication_1, "Delivers to the customer's web browser", $techn="", $tags="Relationship", $link="") Rel(Live.Customersmobiledevice.MobileApp_1, Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") Rel(Live.Customerscomputer.WebBrowser.SinglePageApplication_1, Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") +Rel(Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1, Live.BigBankplc.bigbankdb01.OraclePrimary.Database_1, "Reads from and writes to", $techn="SQL/TCP", $tags="Relationship", $link="") +Rel(Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1, Live.BigBankplc.bigbankdb02.OracleSecondary.Database_1, "Reads from and writes to", $techn="SQL/TCP", $tags="Relationship", $link="") Rel(Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1, Live.BigBankplc.bigbankprod001.MainframeBankingSystem_1, "Makes API calls to", $techn="XML/HTTPS", $tags="Relationship", $link="") -Rel(Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1, Live.BigBankplc.bigbankdb01.OraclePrimary.Database_1, "Reads from and writes to", $techn="JDBC", $tags="Relationship", $link="") -Rel(Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1, Live.BigBankplc.bigbankdb02.OracleSecondary.Database_2, "Reads from and writes to", $techn="JDBC", $tags="Failover", $link="") Rel(Live.BigBankplc.bigbankdb01.OraclePrimary, Live.BigBankplc.bigbankdb02.OracleSecondary, "Replicates data to", $techn="", $tags="Relationship", $link="") SHOW_LEGEND(true) diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn-sequence.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn-sequence.puml index 57f8c67a1..3ee74b0c7 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn-sequence.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn-sequence.puml @@ -17,8 +17,8 @@ ContainerDb(InternetBankingSystem.Database, "Database", $techn="Oracle Database Rel(InternetBankingSystem.SinglePageApplication, InternetBankingSystem.APIApplication.SignInController, "Submits credentials to", $techn="JSON/HTTPS", $tags="Relationship", $link="") Rel(InternetBankingSystem.APIApplication.SignInController, InternetBankingSystem.APIApplication.SecurityComponent, "Validates credentials using", $techn="", $tags="Relationship", $link="") -Rel(InternetBankingSystem.APIApplication.SecurityComponent, InternetBankingSystem.Database, "select * from users where username = ?", $techn="JDBC", $tags="Relationship", $link="") -Rel(InternetBankingSystem.Database, InternetBankingSystem.APIApplication.SecurityComponent, "Returns user data to", $techn="JDBC", $tags="Relationship", $link="") +Rel(InternetBankingSystem.APIApplication.SecurityComponent, InternetBankingSystem.Database, "select * from users where username = ?", $techn="SQL/TCP", $tags="Relationship", $link="") +Rel(InternetBankingSystem.Database, InternetBankingSystem.APIApplication.SecurityComponent, "Returns user data to", $techn="SQL/TCP", $tags="Relationship", $link="") Rel(InternetBankingSystem.APIApplication.SecurityComponent, InternetBankingSystem.APIApplication.SignInController, "Returns true if the hashed password matches", $techn="", $tags="Relationship", $link="") Rel(InternetBankingSystem.APIApplication.SignInController, InternetBankingSystem.SinglePageApplication, "Sends back an authentication token to", $techn="JSON/HTTPS", $tags="Relationship", $link="") diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn.puml index ee3f343b4..1789c9fa5 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn.puml @@ -22,13 +22,13 @@ Container_Boundary("InternetBankingSystem.APIApplication_boundary", "API Applica Component(InternetBankingSystem.APIApplication.SecurityComponent, "Security Component", $techn="Spring Bean", $descr="Provides functionality related to signing in, changing passwords, etc.", $tags="Component", $link="") } -Container(InternetBankingSystem.SinglePageApplication, "Single-Page Application", $techn="JavaScript and Angular", $descr="Provides all of the Internet banking functionality to customers via their web browser.", $tags="Container,Web Browser", $link="") ContainerDb(InternetBankingSystem.Database, "Database", $techn="Oracle Database Schema", $descr="Stores user registration information, hashed authentication credentials, access logs, etc.", $tags="Container,Database", $link="") +Container(InternetBankingSystem.SinglePageApplication, "Single-Page Application", $techn="JavaScript and Angular", $descr="Provides all of the Internet banking functionality to customers via their web browser.", $tags="Container,Web Browser", $link="") Rel(InternetBankingSystem.SinglePageApplication, InternetBankingSystem.APIApplication.SignInController, "1. Submits credentials to", $techn="JSON/HTTPS", $tags="Relationship", $link="") Rel(InternetBankingSystem.APIApplication.SignInController, InternetBankingSystem.APIApplication.SecurityComponent, "2. Validates credentials using", $techn="", $tags="Relationship", $link="") -Rel(InternetBankingSystem.APIApplication.SecurityComponent, InternetBankingSystem.Database, "3. select * from users where username = ?", $techn="JDBC", $tags="Relationship", $link="") -Rel(InternetBankingSystem.Database, InternetBankingSystem.APIApplication.SecurityComponent, "4. Returns user data to", $techn="JDBC", $tags="Relationship", $link="") +Rel(InternetBankingSystem.APIApplication.SecurityComponent, InternetBankingSystem.Database, "3. select * from users where username = ?", $techn="SQL/TCP", $tags="Relationship", $link="") +Rel(InternetBankingSystem.Database, InternetBankingSystem.APIApplication.SecurityComponent, "4. Returns user data to", $techn="SQL/TCP", $tags="Relationship", $link="") Rel(InternetBankingSystem.APIApplication.SecurityComponent, InternetBankingSystem.APIApplication.SignInController, "5. Returns true if the hashed password matches", $techn="", $tags="Relationship", $link="") Rel(InternetBankingSystem.APIApplication.SignInController, InternetBankingSystem.SinglePageApplication, "6. Sends back an authentication token to", $techn="JSON/HTTPS", $tags="Relationship", $link="") diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemContext.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemContext.puml index e4b9cce22..eb64d4554 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemContext.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemContext.puml @@ -9,14 +9,18 @@ top to bottom direction AddElementTag("Software System", $bgColor="#1168bd", $borderColor="#0b4884", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Person", $bgColor="#08427b", $borderColor="#052e56", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Person,Customer", $bgColor="#08427b", $borderColor="#052e56", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") -Person_Ext(PersonalBankingCustomer, "Personal Banking Customer", $descr="A customer of the bank, with personal bank accounts.", $tags="Person", $link="") -System(InternetBankingSystem, "Internet Banking System", $descr="Allows customers to view information about their bank accounts, and make payments.", $tags="Software System", $link="") -System(MainframeBankingSystem, "Mainframe Banking System", $descr="Stores all of the core banking information about customers, accounts, transactions, etc.", $tags="Software System,Existing System", $link="") -System(EmailSystem, "E-mail System", $descr="The internal Microsoft Exchange e-mail system.", $tags="Software System,Existing System", $link="") +AddBoundaryTag("Big Bank plc", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="Dashed", $borderThickness="1") +Boundary(group_1, "Big Bank plc", $tags="Big Bank plc") { + System(MainframeBankingSystem, "Mainframe Banking System", $descr="Stores all of the core banking information about customers, accounts, transactions, etc.", $tags="Software System,Existing System", $link="") + System(EmailSystem, "E-mail System", $descr="The internal Microsoft Exchange e-mail system.", $tags="Software System,Existing System", $link="") + System(InternetBankingSystem, "Internet Banking System", $descr="Allows customers to view information about their bank accounts, and make payments.", $tags="Software System", $link="") +} + +Person(PersonalBankingCustomer, "Personal Banking Customer", $descr="A customer of the bank, with personal bank accounts.", $tags="Person,Customer", $link="") Rel(PersonalBankingCustomer, InternetBankingSystem, "Views account balances, and makes payments using", $techn="", $tags="Relationship", $link="") Rel(InternetBankingSystem, MainframeBankingSystem, "Gets account information from, and makes payments using", $techn="", $tags="Relationship", $link="") diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemLandscape.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemLandscape.puml index 8f4464816..6a4972739 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemLandscape.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemLandscape.puml @@ -10,30 +10,31 @@ top to bottom direction AddElementTag("Software System", $bgColor="#1168bd", $borderColor="#0b4884", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") AddElementTag("Person,Bank Staff", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Person", $bgColor="#08427b", $borderColor="#052e56", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Person,Customer", $bgColor="#08427b", $borderColor="#052e56", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") -Enterprise_Boundary(enterprise, "Big Bank plc") { +AddBoundaryTag("Big Bank plc", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="Dashed", $borderThickness="1") +Boundary(group_1, "Big Bank plc", $tags="Big Bank plc") { Person(CustomerServiceStaff, "Customer Service Staff", $descr="Customer service staff within the bank.", $tags="Person,Bank Staff", $link="") Person(BackOfficeStaff, "Back Office Staff", $descr="Administration and support staff within the bank.", $tags="Person,Bank Staff", $link="") - System(InternetBankingSystem, "Internet Banking System", $descr="Allows customers to view information about their bank accounts, and make payments.", $tags="Software System", $link="") System(MainframeBankingSystem, "Mainframe Banking System", $descr="Stores all of the core banking information about customers, accounts, transactions, etc.", $tags="Software System,Existing System", $link="") System(EmailSystem, "E-mail System", $descr="The internal Microsoft Exchange e-mail system.", $tags="Software System,Existing System", $link="") System(ATM, "ATM", $descr="Allows customers to withdraw cash.", $tags="Software System,Existing System", $link="") + System(InternetBankingSystem, "Internet Banking System", $descr="Allows customers to view information about their bank accounts, and make payments.", $tags="Software System", $link="") } -Person_Ext(PersonalBankingCustomer, "Personal Banking Customer", $descr="A customer of the bank, with personal bank accounts.", $tags="Person", $link="") +Person(PersonalBankingCustomer, "Personal Banking Customer", $descr="A customer of the bank, with personal bank accounts.", $tags="Person,Customer", $link="") -Rel(ATM, MainframeBankingSystem, "Uses", $techn="", $tags="Relationship", $link="") -Rel(PersonalBankingCustomer, ATM, "Withdraws cash using", $techn="", $tags="Relationship", $link="") -Rel(CustomerServiceStaff, MainframeBankingSystem, "Uses", $techn="", $tags="Relationship", $link="") -Rel(PersonalBankingCustomer, CustomerServiceStaff, "Asks questions to", $techn="Telephone", $tags="Relationship", $link="") -Rel(BackOfficeStaff, MainframeBankingSystem, "Uses", $techn="", $tags="Relationship", $link="") Rel(PersonalBankingCustomer, InternetBankingSystem, "Views account balances, and makes payments using", $techn="", $tags="Relationship", $link="") Rel(InternetBankingSystem, MainframeBankingSystem, "Gets account information from, and makes payments using", $techn="", $tags="Relationship", $link="") Rel(InternetBankingSystem, EmailSystem, "Sends e-mail using", $techn="", $tags="Relationship", $link="") Rel(EmailSystem, PersonalBankingCustomer, "Sends e-mails to", $techn="", $tags="Relationship", $link="") +Rel(PersonalBankingCustomer, CustomerServiceStaff, "Asks questions to", $techn="Telephone", $tags="Relationship", $link="") +Rel(CustomerServiceStaff, MainframeBankingSystem, "Uses", $techn="", $tags="Relationship", $link="") +Rel(PersonalBankingCustomer, ATM, "Withdraws cash using", $techn="", $tags="Relationship", $link="") +Rel(ATM, MainframeBankingSystem, "Uses", $techn="", $tags="Relationship", $link="") +Rel(BackOfficeStaff, MainframeBankingSystem, "Uses", $techn="", $tags="Relationship", $link="") SHOW_LEGEND(true) @enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Components.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Components.puml index 5707e4f47..b09edaf21 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Components.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Components.puml @@ -11,8 +11,8 @@ top to bottom direction System(C, "C", $descr="", $tags="", $link="") Container_Boundary("D.F_boundary", "F", $tags="") { - AddBoundaryTag("Group 4", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="Dashed", $borderThickness="1") - Boundary(group_1, "Group 4", $tags="Group 4") { + AddBoundaryTag("Group 5", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="Dashed", $borderThickness="1") + Boundary(group_1, "Group 5", $tags="Group 5") { Component(D.F.H, "H", $techn="", $descr="", $tags="", $link="") } diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Containers.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Containers.puml index 42af97606..2b7569d6b 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Containers.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Containers.puml @@ -11,8 +11,8 @@ top to bottom direction System(C, "C", $descr="", $tags="", $link="") System_Boundary("D_boundary", "D", $tags="") { - AddBoundaryTag("Group 3", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="Dashed", $borderThickness="1") - Boundary(group_1, "Group 3", $tags="Group 3") { + AddBoundaryTag("Group 4", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="Dashed", $borderThickness="1") + Boundary(group_1, "Group 4", $tags="Group 4") { Container(D.F, "F", $techn="", $descr="", $tags="", $link="") } diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-SystemLandscape.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-SystemLandscape.puml index add8abb27..a1620f49f 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-SystemLandscape.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-SystemLandscape.puml @@ -7,21 +7,22 @@ top to bottom direction !include !include -Enterprise_Boundary(enterprise, "Enterprise") { - AddBoundaryTag("Group 2", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="Dashed", $borderThickness="1") - Boundary(group_1, "Group 2", $tags="Group 2") { - System(D, "D", $descr="", $tags="", $link="") - } +AddBoundaryTag("Group 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="Dashed", $borderThickness="1") +Boundary(group_1, "Group 1", $tags="Group 1") { + System(B, "B", $descr="", $tags="", $link="") +} +AddBoundaryTag("Group 2", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="Dashed", $borderThickness="1") +Boundary(group_2, "Group 2", $tags="Group 2") { System(C, "C", $descr="", $tags="", $link="") -} + AddBoundaryTag("Group 2/Group 3", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="Dashed", $borderThickness="1") + Boundary(group_3, "Group 3", $tags="Group 2/Group 3") { + System(D, "D", $descr="", $tags="", $link="") + } -AddBoundaryTag("Group 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="Dashed", $borderThickness="1") -Boundary(group_2, "Group 1", $tags="Group 1") { - System_Ext(B, "B", $descr="", $tags="", $link="") } -System_Ext(A, "A", $descr="", $tags="", $link="") +System(A, "A", $descr="", $tags="", $link="") Rel(B, C, "", $techn="", $tags="", $link="") Rel(C, D, "", $techn="", $tags="", $link="") diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-Components.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-Components.puml index edc664115..149271a67 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-Components.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-Components.puml @@ -86,10 +86,10 @@ skinparam rectangle<> { } rectangle "==Mainframe Banking System\n[Software System]\n\nStores all of the core banking information about customers, accounts, transactions, etc." <> as MainframeBankingSystem -rectangle "==Single-Page Application\n[Container: JavaScript and Angular]\n\nProvides all of the Internet banking functionality to customers via their web browser." <> as InternetBankingSystem.SinglePageApplication rectangle "==E-mail System\n[Software System]\n\nThe internal Microsoft Exchange e-mail system." <> as EmailSystem -rectangle "==Mobile App\n[Container: Xamarin]\n\nProvides a limited subset of the Internet banking functionality to customers via their mobile device." <> as InternetBankingSystem.MobileApp database "==Database\n[Container: Oracle Database Schema]\n\nStores user registration information, hashed authentication credentials, access logs, etc." <> as InternetBankingSystem.Database +rectangle "==Single-Page Application\n[Container: JavaScript and Angular]\n\nProvides all of the Internet banking functionality to customers via their web browser." <> as InternetBankingSystem.SinglePageApplication +rectangle "==Mobile App\n[Container: Xamarin]\n\nProvides a limited subset of the Internet banking functionality to customers via their mobile device." <> as InternetBankingSystem.MobileApp rectangle "API Application\n[Container: Java and Spring MVC]" <> { rectangle "==Sign In Controller\n[Component: Spring MVC Rest Controller]\n\nAllows users to sign in to the Internet Banking System." <> as InternetBankingSystem.APIApplication.SignInController @@ -101,16 +101,16 @@ rectangle "API Application\n[Container: Java and Spring MVC]" << } InternetBankingSystem.SinglePageApplication .[#707070,thickness=2].> InternetBankingSystem.APIApplication.SignInController : "Makes API calls to\n[JSON/HTTPS]" -InternetBankingSystem.SinglePageApplication .[#707070,thickness=2].> InternetBankingSystem.APIApplication.ResetPasswordController : "Makes API calls to\n[JSON/HTTPS]" InternetBankingSystem.SinglePageApplication .[#707070,thickness=2].> InternetBankingSystem.APIApplication.AccountsSummaryController : "Makes API calls to\n[JSON/HTTPS]" +InternetBankingSystem.SinglePageApplication .[#707070,thickness=2].> InternetBankingSystem.APIApplication.ResetPasswordController : "Makes API calls to\n[JSON/HTTPS]" InternetBankingSystem.MobileApp .[#707070,thickness=2].> InternetBankingSystem.APIApplication.SignInController : "Makes API calls to\n[JSON/HTTPS]" -InternetBankingSystem.MobileApp .[#707070,thickness=2].> InternetBankingSystem.APIApplication.ResetPasswordController : "Makes API calls to\n[JSON/HTTPS]" InternetBankingSystem.MobileApp .[#707070,thickness=2].> InternetBankingSystem.APIApplication.AccountsSummaryController : "Makes API calls to\n[JSON/HTTPS]" +InternetBankingSystem.MobileApp .[#707070,thickness=2].> InternetBankingSystem.APIApplication.ResetPasswordController : "Makes API calls to\n[JSON/HTTPS]" InternetBankingSystem.APIApplication.SignInController .[#707070,thickness=2].> InternetBankingSystem.APIApplication.SecurityComponent : "Uses" InternetBankingSystem.APIApplication.AccountsSummaryController .[#707070,thickness=2].> InternetBankingSystem.APIApplication.MainframeBankingSystemFacade : "Uses" InternetBankingSystem.APIApplication.ResetPasswordController .[#707070,thickness=2].> InternetBankingSystem.APIApplication.SecurityComponent : "Uses" InternetBankingSystem.APIApplication.ResetPasswordController .[#707070,thickness=2].> InternetBankingSystem.APIApplication.EmailComponent : "Uses" -InternetBankingSystem.APIApplication.SecurityComponent .[#707070,thickness=2].> InternetBankingSystem.Database : "Reads from and writes to\n[JDBC]" -InternetBankingSystem.APIApplication.MainframeBankingSystemFacade .[#707070,thickness=2].> MainframeBankingSystem : "Uses\n[XML/HTTPS]" +InternetBankingSystem.APIApplication.SecurityComponent .[#707070,thickness=2].> InternetBankingSystem.Database : "Reads from and writes to\n[SQL/TCP]" +InternetBankingSystem.APIApplication.MainframeBankingSystemFacade .[#707070,thickness=2].> MainframeBankingSystem : "Makes API calls to\n[XML/HTTPS]" InternetBankingSystem.APIApplication.EmailComponent .[#707070,thickness=2].> EmailSystem : "Sends e-mail using" @enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-Containers.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-Containers.puml index 300b0d9e8..40963b9e9 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-Containers.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-Containers.puml @@ -72,21 +72,21 @@ rectangle "==Mainframe Banking System\n[Software System]\n\nStor rectangle "==E-mail System\n[Software System]\n\nThe internal Microsoft Exchange e-mail system." <> as EmailSystem rectangle "Internet Banking System\n[Software System]" <> { - rectangle "==Single-Page Application\n[Container: JavaScript and Angular]\n\nProvides all of the Internet banking functionality to customers via their web browser." <> as InternetBankingSystem.SinglePageApplication - rectangle "==Mobile App\n[Container: Xamarin]\n\nProvides a limited subset of the Internet banking functionality to customers via their mobile device." <> as InternetBankingSystem.MobileApp rectangle "==Web Application\n[Container: Java and Spring MVC]\n\nDelivers the static content and the Internet banking single page application." <> as InternetBankingSystem.WebApplication rectangle "==API Application\n[Container: Java and Spring MVC]\n\nProvides Internet banking functionality via a JSON/HTTPS API." <> as InternetBankingSystem.APIApplication database "==Database\n[Container: Oracle Database Schema]\n\nStores user registration information, hashed authentication credentials, access logs, etc." <> as InternetBankingSystem.Database + rectangle "==Single-Page Application\n[Container: JavaScript and Angular]\n\nProvides all of the Internet banking functionality to customers via their web browser." <> as InternetBankingSystem.SinglePageApplication + rectangle "==Mobile App\n[Container: Xamarin]\n\nProvides a limited subset of the Internet banking functionality to customers via their mobile device." <> as InternetBankingSystem.MobileApp } +EmailSystem .[#707070,thickness=2].> PersonalBankingCustomer : "Sends e-mails to" PersonalBankingCustomer .[#707070,thickness=2].> InternetBankingSystem.WebApplication : "Visits bigbank.com/ib using\n[HTTPS]" PersonalBankingCustomer .[#707070,thickness=2].> InternetBankingSystem.SinglePageApplication : "Views account balances, and makes payments using" PersonalBankingCustomer .[#707070,thickness=2].> InternetBankingSystem.MobileApp : "Views account balances, and makes payments using" InternetBankingSystem.WebApplication .[#707070,thickness=2].> InternetBankingSystem.SinglePageApplication : "Delivers to the customer's web browser" -InternetBankingSystem.APIApplication .[#707070,thickness=2].> InternetBankingSystem.Database : "Reads from and writes to\n[JDBC]" -InternetBankingSystem.APIApplication .[#707070,thickness=2].> MainframeBankingSystem : "Makes API calls to\n[XML/HTTPS]" -InternetBankingSystem.APIApplication .[#707070,thickness=2].> EmailSystem : "Sends e-mail using\n[SMTP]" InternetBankingSystem.SinglePageApplication .[#707070,thickness=2].> InternetBankingSystem.APIApplication : "Makes API calls to\n[JSON/HTTPS]" InternetBankingSystem.MobileApp .[#707070,thickness=2].> InternetBankingSystem.APIApplication : "Makes API calls to\n[JSON/HTTPS]" -EmailSystem .[#707070,thickness=2].> PersonalBankingCustomer : "Sends e-mails to" +InternetBankingSystem.APIApplication .[#707070,thickness=2].> InternetBankingSystem.Database : "Reads from and writes to\n[SQL/TCP]" +InternetBankingSystem.APIApplication .[#707070,thickness=2].> MainframeBankingSystem : "Makes API calls to\n[XML/HTTPS]" +InternetBankingSystem.APIApplication .[#707070,thickness=2].> EmailSystem : "Sends e-mail using" @enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-DevelopmentDeployment.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-DevelopmentDeployment.puml index e35d6fa8c..59d7c4b30 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-DevelopmentDeployment.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-DevelopmentDeployment.puml @@ -93,6 +93,10 @@ skinparam rectangle<> { } rectangle "Developer Laptop\n[Deployment Node: Microsoft Windows 10 or Apple macOS]" <> as Development.DeveloperLaptop { + rectangle "Web Browser\n[Deployment Node: Chrome, Firefox, Safari, or Edge]" <> as Development.DeveloperLaptop.WebBrowser { + rectangle "==Single-Page Application\n[Container: JavaScript and Angular]\n\nProvides all of the Internet banking functionality to customers via their web browser." <> as Development.DeveloperLaptop.WebBrowser.SinglePageApplication_1 + } + rectangle "Docker Container - Web Server\n[Deployment Node: Docker]" <> as Development.DeveloperLaptop.DockerContainerWebServer { rectangle "Apache Tomcat\n[Deployment Node: Apache Tomcat 8.x]" <> as Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat { rectangle "==Web Application\n[Container: Java and Spring MVC]\n\nDelivers the static content and the Internet banking single page application." <> as Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.WebApplication_1 @@ -108,10 +112,6 @@ rectangle "Developer Laptop\n[Deployment Node: Microsoft Windows 10 or } - rectangle "Web Browser\n[Deployment Node: Chrome, Firefox, Safari, or Edge]" <> as Development.DeveloperLaptop.WebBrowser { - rectangle "==Single-Page Application\n[Container: JavaScript and Angular]\n\nProvides all of the Internet banking functionality to customers via their web browser." <> as Development.DeveloperLaptop.WebBrowser.SinglePageApplication_1 - } - } rectangle "Big Bank plc\n[Deployment Node: Big Bank plc data center]" <> as Development.BigBankplc { @@ -121,8 +121,8 @@ rectangle "Big Bank plc\n[Deployment Node: Big Bank plc data center]
    Development.BigBankplc.bigbankdev001.MainframeBankingSystem_1 : "Makes API calls to\n[XML/HTTPS]" -Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.APIApplication_1 .[#707070,thickness=2].> Development.DeveloperLaptop.DockerContainerDatabaseServer.DatabaseServer.Database_1 : "Reads from and writes to\n[JDBC]" -Development.DeveloperLaptop.WebBrowser.SinglePageApplication_1 .[#707070,thickness=2].> Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.APIApplication_1 : "Makes API calls to\n[JSON/HTTPS]" Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.WebApplication_1 .[#707070,thickness=2].> Development.DeveloperLaptop.WebBrowser.SinglePageApplication_1 : "Delivers to the customer's web browser" +Development.DeveloperLaptop.WebBrowser.SinglePageApplication_1 .[#707070,thickness=2].> Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.APIApplication_1 : "Makes API calls to\n[JSON/HTTPS]" +Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.APIApplication_1 .[#707070,thickness=2].> Development.DeveloperLaptop.DockerContainerDatabaseServer.DatabaseServer.Database_1 : "Reads from and writes to\n[SQL/TCP]" +Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.APIApplication_1 .[#707070,thickness=2].> Development.BigBankplc.bigbankdev001.MainframeBankingSystem_1 : "Makes API calls to\n[XML/HTTPS]" @enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-LiveDeployment.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-LiveDeployment.puml index 72427ad33..b7611a0b1 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-LiveDeployment.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-LiveDeployment.puml @@ -49,7 +49,7 @@ skinparam rectangle<> { BorderColor #888888 shadowing false } -skinparam database<> { +skinparam database<> { BackgroundColor #438dd5 FontColor #ffffff BorderColor #2e6295 @@ -146,10 +146,6 @@ rectangle "Customer's computer\n[Deployment Node: Microsoft Windows or } rectangle "Big Bank plc\n[Deployment Node: Big Bank plc data center]" <> as Live.BigBankplc { - rectangle "bigbank-prod001\n[Deployment Node]" <> as Live.BigBankplc.bigbankprod001 { - rectangle "==Mainframe Banking System\n[Software System]\n\nStores all of the core banking information about customers, accounts, transactions, etc." <> as Live.BigBankplc.bigbankprod001.MainframeBankingSystem_1 - } - rectangle "bigbank-web*** (x4)\n[Deployment Node: Ubuntu 16.04 LTS]" <> as Live.BigBankplc.bigbankweb { rectangle "Apache Tomcat\n[Deployment Node: Apache Tomcat 8.x]" <> as Live.BigBankplc.bigbankweb.ApacheTomcat { rectangle "==Web Application\n[Container: Java and Spring MVC]\n\nDelivers the static content and the Internet banking single page application." <> as Live.BigBankplc.bigbankweb.ApacheTomcat.WebApplication_1 @@ -173,18 +169,22 @@ rectangle "Big Bank plc\n[Deployment Node: Big Bank plc data center]
    [Deployment Node: Ubuntu 16.04 LTS]" <> as Live.BigBankplc.bigbankdb02 { rectangle "Oracle - Secondary\n[Deployment Node: Oracle 12c]" <> as Live.BigBankplc.bigbankdb02.OracleSecondary { - database "==Database\n[Container: Oracle Database Schema]\n\nStores user registration information, hashed authentication credentials, access logs, etc." <> as Live.BigBankplc.bigbankdb02.OracleSecondary.Database_2 + database "==Database\n[Container: Oracle Database Schema]\n\nStores user registration information, hashed authentication credentials, access logs, etc." <> as Live.BigBankplc.bigbankdb02.OracleSecondary.Database_1 } } + rectangle "bigbank-prod001\n[Deployment Node]" <> as Live.BigBankplc.bigbankprod001 { + rectangle "==Mainframe Banking System\n[Software System]\n\nStores all of the core banking information about customers, accounts, transactions, etc." <> as Live.BigBankplc.bigbankprod001.MainframeBankingSystem_1 + } + } Live.BigBankplc.bigbankweb.ApacheTomcat.WebApplication_1 .[#707070,thickness=2].> Live.Customerscomputer.WebBrowser.SinglePageApplication_1 : "Delivers to the customer's web browser" Live.Customersmobiledevice.MobileApp_1 .[#707070,thickness=2].> Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1 : "Makes API calls to\n[JSON/HTTPS]" Live.Customerscomputer.WebBrowser.SinglePageApplication_1 .[#707070,thickness=2].> Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1 : "Makes API calls to\n[JSON/HTTPS]" +Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1 .[#707070,thickness=2].> Live.BigBankplc.bigbankdb01.OraclePrimary.Database_1 : "Reads from and writes to\n[SQL/TCP]" +Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1 .[#707070,thickness=2].> Live.BigBankplc.bigbankdb02.OracleSecondary.Database_1 : "Reads from and writes to\n[SQL/TCP]" Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1 .[#707070,thickness=2].> Live.BigBankplc.bigbankprod001.MainframeBankingSystem_1 : "Makes API calls to\n[XML/HTTPS]" -Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1 .[#707070,thickness=2].> Live.BigBankplc.bigbankdb01.OraclePrimary.Database_1 : "Reads from and writes to\n[JDBC]" -Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1 .[#707070,thickness=2].> Live.BigBankplc.bigbankdb02.OracleSecondary.Database_2 : "Reads from and writes to\n[JDBC]" Live.BigBankplc.bigbankdb01.OraclePrimary .[#707070,thickness=2].> Live.BigBankplc.bigbankdb02.OracleSecondary : "Replicates data to" @enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SignIn.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SignIn.puml index 687ce9d7e..aad19e957 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SignIn.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SignIn.puml @@ -48,13 +48,13 @@ rectangle "API Application\n[Container: Java and Spring MVC]" << rectangle "==Security Component\n[Component: Spring Bean]\n\nProvides functionality related to signing in, changing passwords, etc." <> as InternetBankingSystem.APIApplication.SecurityComponent } -rectangle "==Single-Page Application\n[Container: JavaScript and Angular]\n\nProvides all of the Internet banking functionality to customers via their web browser." <> as InternetBankingSystem.SinglePageApplication database "==Database\n[Container: Oracle Database Schema]\n\nStores user registration information, hashed authentication credentials, access logs, etc." <> as InternetBankingSystem.Database +rectangle "==Single-Page Application\n[Container: JavaScript and Angular]\n\nProvides all of the Internet banking functionality to customers via their web browser." <> as InternetBankingSystem.SinglePageApplication InternetBankingSystem.SinglePageApplication .[#707070,thickness=2].> InternetBankingSystem.APIApplication.SignInController : "1. Submits credentials to\n[JSON/HTTPS]" InternetBankingSystem.APIApplication.SignInController .[#707070,thickness=2].> InternetBankingSystem.APIApplication.SecurityComponent : "2. Validates credentials using" -InternetBankingSystem.APIApplication.SecurityComponent .[#707070,thickness=2].> InternetBankingSystem.Database : "3. select * from users where username = ?\n[JDBC]" -InternetBankingSystem.APIApplication.SecurityComponent <.[#707070,thickness=2]. InternetBankingSystem.Database : "4. Returns user data to\n[JDBC]" +InternetBankingSystem.APIApplication.SecurityComponent .[#707070,thickness=2].> InternetBankingSystem.Database : "3. select * from users where username = ?\n[SQL/TCP]" +InternetBankingSystem.APIApplication.SecurityComponent <.[#707070,thickness=2]. InternetBankingSystem.Database : "4. Returns user data to\n[SQL/TCP]" InternetBankingSystem.APIApplication.SignInController <.[#707070,thickness=2]. InternetBankingSystem.APIApplication.SecurityComponent : "5. Returns true if the hashed password matches" InternetBankingSystem.SinglePageApplication <.[#707070,thickness=2]. InternetBankingSystem.APIApplication.SignInController : "6. Sends back an authentication token to\n[JSON/HTTPS]" @enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemContext.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemContext.puml index 36302bdb3..d4f946a23 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemContext.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemContext.puml @@ -38,10 +38,17 @@ skinparam person<> { shadowing false } +rectangle "Big Bank plc" <> { + skinparam RectangleBorderColor<> #cccccc + skinparam RectangleFontColor<> #cccccc + skinparam RectangleBorderStyle<> dashed + + rectangle "==Mainframe Banking System\n[Software System]\n\nStores all of the core banking information about customers, accounts, transactions, etc." <> as MainframeBankingSystem + rectangle "==E-mail System\n[Software System]\n\nThe internal Microsoft Exchange e-mail system." <> as EmailSystem + rectangle "==Internet Banking System\n[Software System]\n\nAllows customers to view information about their bank accounts, and make payments." <> as InternetBankingSystem +} + person "==Personal Banking Customer\n[Person]\n\nA customer of the bank, with personal bank accounts." <> as PersonalBankingCustomer -rectangle "==Internet Banking System\n[Software System]\n\nAllows customers to view information about their bank accounts, and make payments." <> as InternetBankingSystem -rectangle "==Mainframe Banking System\n[Software System]\n\nStores all of the core banking information about customers, accounts, transactions, etc." <> as MainframeBankingSystem -rectangle "==E-mail System\n[Software System]\n\nThe internal Microsoft Exchange e-mail system." <> as EmailSystem PersonalBankingCustomer .[#707070,thickness=2].> InternetBankingSystem : "Views account balances, and makes payments using" InternetBankingSystem .[#707070,thickness=2].> MainframeBankingSystem : "Gets account information from, and makes payments using" diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemLandscape.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemLandscape.puml index 511b1a16c..b042bb6a5 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemLandscape.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemLandscape.puml @@ -56,27 +56,28 @@ skinparam person<> { shadowing false } -rectangle "Big Bank plc" <> { - skinparam RectangleBorderColor<> #444444 - skinparam RectangleFontColor<> #444444 +rectangle "Big Bank plc" <> { + skinparam RectangleBorderColor<> #cccccc + skinparam RectangleFontColor<> #cccccc + skinparam RectangleBorderStyle<> dashed person "==Customer Service Staff\n[Person]\n\nCustomer service staff within the bank." <> as CustomerServiceStaff person "==Back Office Staff\n[Person]\n\nAdministration and support staff within the bank." <> as BackOfficeStaff - rectangle "==Internet Banking System\n[Software System]\n\nAllows customers to view information about their bank accounts, and make payments." <> as InternetBankingSystem rectangle "==Mainframe Banking System\n[Software System]\n\nStores all of the core banking information about customers, accounts, transactions, etc." <> as MainframeBankingSystem rectangle "==E-mail System\n[Software System]\n\nThe internal Microsoft Exchange e-mail system." <> as EmailSystem rectangle "==ATM\n[Software System]\n\nAllows customers to withdraw cash." <> as ATM + rectangle "==Internet Banking System\n[Software System]\n\nAllows customers to view information about their bank accounts, and make payments." <> as InternetBankingSystem } person "==Personal Banking Customer\n[Person]\n\nA customer of the bank, with personal bank accounts." <> as PersonalBankingCustomer -ATM .[#707070,thickness=2].> MainframeBankingSystem : "Uses" -PersonalBankingCustomer .[#707070,thickness=2].> ATM : "Withdraws cash using" -CustomerServiceStaff .[#707070,thickness=2].> MainframeBankingSystem : "Uses" -PersonalBankingCustomer .[#707070,thickness=2].> CustomerServiceStaff : "Asks questions to\n[Telephone]" -BackOfficeStaff .[#707070,thickness=2].> MainframeBankingSystem : "Uses" PersonalBankingCustomer .[#707070,thickness=2].> InternetBankingSystem : "Views account balances, and makes payments using" InternetBankingSystem .[#707070,thickness=2].> MainframeBankingSystem : "Gets account information from, and makes payments using" InternetBankingSystem .[#707070,thickness=2].> EmailSystem : "Sends e-mail using" EmailSystem .[#707070,thickness=2].> PersonalBankingCustomer : "Sends e-mails to" +PersonalBankingCustomer .[#707070,thickness=2].> CustomerServiceStaff : "Asks questions to\n[Telephone]" +CustomerServiceStaff .[#707070,thickness=2].> MainframeBankingSystem : "Uses" +PersonalBankingCustomer .[#707070,thickness=2].> ATM : "Withdraws cash using" +ATM .[#707070,thickness=2].> MainframeBankingSystem : "Uses" +BackOfficeStaff .[#707070,thickness=2].> MainframeBankingSystem : "Uses" @enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-Components.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-Components.puml index 0cc6d9bd4..04af47b24 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-Components.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-Components.puml @@ -40,7 +40,7 @@ skinparam rectangle<> { rectangle "==C\n[Software System]" <> as C rectangle "F\n[Container]" <> { - rectangle "Group 4" <> { + rectangle "Group 5" <> { skinparam RectangleBorderColor<> #cccccc skinparam RectangleFontColor<> #cccccc skinparam RectangleBorderStyle<> dashed diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-Containers.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-Containers.puml index fd5a74bf5..f7d532695 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-Containers.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-Containers.puml @@ -40,7 +40,7 @@ skinparam rectangle<> { rectangle "==C\n[Software System]" <> as C rectangle "D\n[Software System]" <> { - rectangle "Group 3" <> { + rectangle "Group 4" <> { skinparam RectangleBorderColor<> #cccccc skinparam RectangleFontColor<> #cccccc skinparam RectangleBorderStyle<> dashed diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-SystemLandscape.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-SystemLandscape.puml index 094f24a31..c967286be 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-SystemLandscape.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-SystemLandscape.puml @@ -38,27 +38,28 @@ skinparam rectangle<> { shadowing false } -rectangle "Enterprise" <> { - skinparam RectangleBorderColor<> #444444 - skinparam RectangleFontColor<> #444444 +rectangle "Group 1" <> { + skinparam RectangleBorderColor<> #cccccc + skinparam RectangleFontColor<> #cccccc + skinparam RectangleBorderStyle<> dashed - rectangle "Group 2" <> { - skinparam RectangleBorderColor<> #cccccc - skinparam RectangleFontColor<> #cccccc - skinparam RectangleBorderStyle<> dashed - - rectangle "==D\n[Software System]" <> as D - } - - rectangle "==C\n[Software System]" <> as C + rectangle "==B\n[Software System]" <> as B } -rectangle "Group 1" <> { +rectangle "Group 2" <> { skinparam RectangleBorderColor<> #cccccc skinparam RectangleFontColor<> #cccccc skinparam RectangleBorderStyle<> dashed - rectangle "==B\n[Software System]" <> as B + rectangle "==C\n[Software System]" <> as C + rectangle "Group 3" <> { + skinparam RectangleBorderColor<> #cccccc + skinparam RectangleFontColor<> #cccccc + skinparam RectangleBorderStyle<> dashed + + rectangle "==D\n[Software System]" <> as D + } + } rectangle "==A\n[Software System]" <
    > as A diff --git a/structurizr-export/src/test/resources/groups.dsl b/structurizr-export/src/test/resources/groups.dsl new file mode 100644 index 000000000..c2f39a5bc --- /dev/null +++ b/structurizr-export/src/test/resources/groups.dsl @@ -0,0 +1,59 @@ +workspace { + + model { + properties { + structurizr.groupSeparator / + } + + a = softwareSystem "A" + + group "Group 1" { + b = softwareSystem "B" + } + + group "Group 2" { + c = softwareSystem "C" + + group "Group 3" { + d = softwareSystem "D" { + e = container "E" + + group "Group 4" { + f = container "F" { + g = component "G" + + group "Group 5" { + h = component "H" + } + } + } + } + } + } + + a -> b + b -> c + c -> e + c -> g + c -> h + + } + + views { + systemlandscape "SystemLandscape" { + include * + autolayout + } + + container d "Containers" { + include * + autolayout + } + + component f "Components" { + include * + autolayout + } + } + +} diff --git a/structurizr-export/src/test/resources/groups.json b/structurizr-export/src/test/resources/groups.json index 62c3303b8..9ffc544f4 100644 --- a/structurizr-export/src/test/resources/groups.json +++ b/structurizr-export/src/test/resources/groups.json @@ -3,110 +3,165 @@ "name" : "Name", "description" : "Description", "properties" : { - "structurizr.dsl" : "d29ya3NwYWNlIHsKCiAgICBtb2RlbCB7CiAgICAgICAgYSA9IHNvZnR3YXJlU3lzdGVtICJBIgogICAgICAgIAogICAgICAgIGdyb3VwICJHcm91cCAxIiB7CiAgICAgICAgICAgIGIgPSBzb2Z0d2FyZVN5c3RlbSAiQiIKICAgICAgICB9CgogICAgICAgIGVudGVycHJpc2UgIkVudGVycHJpc2UiIHsKICAgICAgICAgICAgYyA9IHNvZnR3YXJlU3lzdGVtICJDIgoKICAgICAgICAgICAgZ3JvdXAgIkdyb3VwIDIiIHsKICAgICAgICAgICAgICAgIGQgPSBzb2Z0d2FyZVN5c3RlbSAiRCIgewogICAgICAgICAgICAgICAgICAgIGUgPSBjb250YWluZXIgIkUiCiAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgZ3JvdXAgIkdyb3VwIDMiIHsKICAgICAgICAgICAgICAgICAgICAgICAgZiA9IGNvbnRhaW5lciAiRiIgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgZyA9IGNvbXBvbmVudCAiRyIKICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgIkdyb3VwIDQiIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoID0gY29tcG9uZW50ICJIIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIAogICAgICAgIGEgLT4gYgogICAgICAgIGIgLT4gYwogICAgICAgIGMgLT4gZQogICAgICAgIGMgLT4gZwogICAgICAgIGMgLT4gaAoKICAgIH0KICAgIAogICAgdmlld3MgewogICAgICAgIHN5c3RlbWxhbmRzY2FwZSAiU3lzdGVtTGFuZHNjYXBlIiB7CiAgICAgICAgICAgIGluY2x1ZGUgKgogICAgICAgICAgICBhdXRvbGF5b3V0CiAgICAgICAgfQogICAgICAgIAogICAgICAgIGNvbnRhaW5lciBkICJDb250YWluZXJzIiB7CiAgICAgICAgICAgIGluY2x1ZGUgKgogICAgICAgICAgICBhdXRvbGF5b3V0CiAgICAgICAgfQoKICAgICAgICBjb21wb25lbnQgZiAiQ29tcG9uZW50cyIgewogICAgICAgICAgICBpbmNsdWRlICoKICAgICAgICAgICAgYXV0b2xheW91dAogICAgICAgIH0KICAgIH0KCn0K" + "structurizr.dsl" : "d29ya3NwYWNlIHsKCiAgICBtb2RlbCB7CiAgICAgICAgcHJvcGVydGllcyB7CiAgICAgICAgICAgIHN0cnVjdHVyaXpyLmdyb3VwU2VwYXJhdG9yIC8KICAgICAgICB9CgogICAgICAgIGEgPSBzb2Z0d2FyZVN5c3RlbSAiQSIKCiAgICAgICAgZ3JvdXAgIkdyb3VwIDEiIHsKICAgICAgICAgICAgYiA9IHNvZnR3YXJlU3lzdGVtICJCIgogICAgICAgIH0KCiAgICAgICAgZ3JvdXAgIkdyb3VwIDIiIHsKICAgICAgICAgICAgYyA9IHNvZnR3YXJlU3lzdGVtICJDIgoKICAgICAgICAgICAgZ3JvdXAgIkdyb3VwIDMiIHsKICAgICAgICAgICAgICAgIGQgPSBzb2Z0d2FyZVN5c3RlbSAiRCIgewogICAgICAgICAgICAgICAgICAgIGUgPSBjb250YWluZXIgIkUiCgogICAgICAgICAgICAgICAgICAgIGdyb3VwICJHcm91cCA0IiB7CiAgICAgICAgICAgICAgICAgICAgICAgIGYgPSBjb250YWluZXIgIkYiIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGcgPSBjb21wb25lbnQgIkciCgogICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgIkdyb3VwIDUiIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoID0gY29tcG9uZW50ICJIIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICBhIC0+IGIKICAgICAgICBiIC0+IGMKICAgICAgICBjIC0+IGUKICAgICAgICBjIC0+IGcKICAgICAgICBjIC0+IGgKCiAgICB9CgogICAgdmlld3MgewogICAgICAgIHN5c3RlbWxhbmRzY2FwZSAiU3lzdGVtTGFuZHNjYXBlIiB7CiAgICAgICAgICAgIGluY2x1ZGUgKgogICAgICAgICAgICBhdXRvbGF5b3V0CiAgICAgICAgfQoKICAgICAgICBjb250YWluZXIgZCAiQ29udGFpbmVycyIgewogICAgICAgICAgICBpbmNsdWRlICoKICAgICAgICAgICAgYXV0b2xheW91dAogICAgICAgIH0KCiAgICAgICAgY29tcG9uZW50IGYgIkNvbXBvbmVudHMiIHsKICAgICAgICAgICAgaW5jbHVkZSAqCiAgICAgICAgICAgIGF1dG9sYXlvdXQKICAgICAgICB9CiAgICB9Cgp9Cg==" }, "configuration" : { }, "model" : { - "enterprise" : { - "name" : "Enterprise" - }, "softwareSystems" : [ { "id" : "1", "tags" : "Element,Software System", + "properties" : { + "structurizr.dsl.identifier" : "a" + }, "name" : "A", "relationships" : [ { "id" : "9", "tags" : "Relationship", + "properties" : { + "structurizr.dsl.identifier" : "370d771e-c1ab-4243-b9cd-2325145b20bc" + }, "sourceId" : "1", "destinationId" : "2" } ], - "location" : "External" + "location" : "Unspecified", + "documentation" : { } }, { "id" : "2", "tags" : "Element,Software System", + "properties" : { + "structurizr.dsl.identifier" : "b" + }, "name" : "B", "relationships" : [ { "id" : "10", "tags" : "Relationship", + "properties" : { + "structurizr.dsl.identifier" : "923c8280-f475-422a-9fef-2b69220bc8e6" + }, "sourceId" : "2", "destinationId" : "3" } ], "group" : "Group 1", - "location" : "External" + "location" : "Unspecified", + "documentation" : { } }, { "id" : "3", "tags" : "Element,Software System", + "properties" : { + "structurizr.dsl.identifier" : "c" + }, "name" : "C", "relationships" : [ { "id" : "11", "tags" : "Relationship", + "properties" : { + "structurizr.dsl.identifier" : "cd5c9209-d4f5-4ffa-a323-dc0d5612cdb8" + }, "sourceId" : "3", "destinationId" : "5" }, { "id" : "12", - "tags" : "Relationship", "sourceId" : "3", - "destinationId" : "4" + "destinationId" : "4", + "linkedRelationshipId" : "11" }, { "id" : "13", "tags" : "Relationship", + "properties" : { + "structurizr.dsl.identifier" : "0f23b6e5-9276-4468-b3fb-9381f53c8cd7" + }, "sourceId" : "3", "destinationId" : "7" }, { "id" : "14", - "tags" : "Relationship", "sourceId" : "3", - "destinationId" : "6" + "destinationId" : "6", + "linkedRelationshipId" : "13" }, { "id" : "15", "tags" : "Relationship", + "properties" : { + "structurizr.dsl.identifier" : "b0b99a2e-129b-4f75-81c0-de9c67916229" + }, "sourceId" : "3", "destinationId" : "8" } ], - "location" : "Internal" + "group" : "Group 2", + "location" : "Unspecified", + "documentation" : { } }, { "id" : "4", "tags" : "Element,Software System", + "properties" : { + "structurizr.dsl.identifier" : "d" + }, "name" : "D", - "group" : "Group 2", - "location" : "Internal", + "group" : "Group 2/Group 3", + "location" : "Unspecified", "containers" : [ { - "id" : "5", - "tags" : "Element,Container", - "name" : "E" - }, { "id" : "6", "tags" : "Element,Container", + "properties" : { + "structurizr.dsl.identifier" : "f" + }, "name" : "F", - "group" : "Group 3", + "group" : "Group 4", "components" : [ { "id" : "8", "tags" : "Element,Component", + "properties" : { + "structurizr.dsl.identifier" : "h" + }, "name" : "H", - "group" : "Group 4", - "size" : 0 + "group" : "Group 5", + "documentation" : { } }, { "id" : "7", "tags" : "Element,Component", + "properties" : { + "structurizr.dsl.identifier" : "g" + }, "name" : "G", - "size" : 0 - } ] - } ] - } ] + "documentation" : { } + } ], + "documentation" : { } + }, { + "id" : "5", + "tags" : "Element,Container", + "properties" : { + "structurizr.dsl.identifier" : "e" + }, + "name" : "E", + "documentation" : { } + } ], + "documentation" : { } + } ], + "properties" : { + "structurizr.groupSeparator" : "/" + } }, "documentation" : { }, "views" : { "systemLandscapeViews" : [ { "key" : "SystemLandscape", + "order" : 1, "automaticLayout" : { "implementation" : "Graphviz", "rankDirection" : "TopBottom", "rankSeparation" : 300, "nodeSeparation" : 300, "edgeSeparation" : 0, - "vertices" : false + "vertices" : false, + "applied" : false }, "enterpriseBoundaryVisible" : true, + "relationships" : [ { + "id" : "12" + }, { + "id" : "9" + }, { + "id" : "10" + } ], "elements" : [ { "id" : "1", "x" : 0, @@ -123,27 +178,27 @@ "id" : "4", "x" : 0, "y" : 0 - } ], - "relationships" : [ { - "id" : "12" - }, { - "id" : "9" - }, { - "id" : "10" } ] } ], "containerViews" : [ { - "softwareSystemId" : "4", "key" : "Containers", + "order" : 2, + "softwareSystemId" : "4", "automaticLayout" : { "implementation" : "Graphviz", "rankDirection" : "TopBottom", "rankSeparation" : 300, "nodeSeparation" : 300, "edgeSeparation" : 0, - "vertices" : false + "vertices" : false, + "applied" : false }, "externalSoftwareSystemBoundariesVisible" : true, + "relationships" : [ { + "id" : "14" + }, { + "id" : "11" + } ], "elements" : [ { "id" : "3", "x" : 0, @@ -156,25 +211,27 @@ "id" : "6", "x" : 0, "y" : 0 - } ], - "relationships" : [ { - "id" : "14" - }, { - "id" : "11" } ] } ], "componentViews" : [ { "key" : "Components", + "order" : 3, "automaticLayout" : { "implementation" : "Graphviz", "rankDirection" : "TopBottom", "rankSeparation" : 300, "nodeSeparation" : 300, "edgeSeparation" : 0, - "vertices" : false + "vertices" : false, + "applied" : false }, "containerId" : "6", "externalContainerBoundariesVisible" : true, + "relationships" : [ { + "id" : "15" + }, { + "id" : "13" + } ], "elements" : [ { "id" : "3", "x" : 0, @@ -187,11 +244,6 @@ "id" : "8", "x" : 0, "y" : 0 - } ], - "relationships" : [ { - "id" : "15" - }, { - "id" : "13" } ] } ], "configuration" : { diff --git a/structurizr-export/src/test/resources/structurizr-36141-workspace.json b/structurizr-export/src/test/resources/structurizr-36141-workspace.json index 0a52ae906..d0162591f 100644 --- a/structurizr-export/src/test/resources/structurizr-36141-workspace.json +++ b/structurizr-export/src/test/resources/structurizr-36141-workspace.json @@ -1,1999 +1,1590 @@ { - "id": 36141, - "name": "Big Bank plc", - "description": "This is an example workspace to illustrate the key features of Structurizr, based around a fictional online banking system.", - "model": { - "enterprise": { - "name": "Big Bank plc" + "id" : 0, + "name" : "Big Bank plc", + "description" : "This is an example workspace to illustrate the key features of Structurizr, via the DSL, based around a fictional online banking system.", + "properties" : { + "structurizr.dsl" : "LyoKICogVGhpcyBpcyBhIGNvbWJpbmVkIHZlcnNpb24gb2YgdGhlIGZvbGxvd2luZyB3b3Jrc3BhY2VzLCB3aXRoIGF1dG9tYXRpYyBsYXlvdXQgZW5hYmxlZDoKICoKICogLSAiQmlnIEJhbmsgcGxjIC0gU3lzdGVtIExhbmRzY2FwZSIgKGh0dHBzOi8vc3RydWN0dXJpenIuY29tL3NoYXJlLzI4MjAxLykKICogLSAiQmlnIEJhbmsgcGxjIC0gSW50ZXJuZXQgQmFua2luZyBTeXN0ZW0iIChodHRwczovL3N0cnVjdHVyaXpyLmNvbS9zaGFyZS8zNjE0MS8pCiovCndvcmtzcGFjZSAiQmlnIEJhbmsgcGxjIiAiVGhpcyBpcyBhbiBleGFtcGxlIHdvcmtzcGFjZSB0byBpbGx1c3RyYXRlIHRoZSBrZXkgZmVhdHVyZXMgb2YgU3RydWN0dXJpenIsIHZpYSB0aGUgRFNMLCBiYXNlZCBhcm91bmQgYSBmaWN0aW9uYWwgb25saW5lIGJhbmtpbmcgc3lzdGVtLiIgewoKICAgIG1vZGVsIHsKICAgICAgICBjdXN0b21lciA9IHBlcnNvbiAiUGVyc29uYWwgQmFua2luZyBDdXN0b21lciIgIkEgY3VzdG9tZXIgb2YgdGhlIGJhbmssIHdpdGggcGVyc29uYWwgYmFuayBhY2NvdW50cy4iICJDdXN0b21lciIKCiAgICAgICAgZ3JvdXAgIkJpZyBCYW5rIHBsYyIgewogICAgICAgICAgICBzdXBwb3J0U3RhZmYgPSBwZXJzb24gIkN1c3RvbWVyIFNlcnZpY2UgU3RhZmYiICJDdXN0b21lciBzZXJ2aWNlIHN0YWZmIHdpdGhpbiB0aGUgYmFuay4iICJCYW5rIFN0YWZmIgogICAgICAgICAgICBiYWNrb2ZmaWNlID0gcGVyc29uICJCYWNrIE9mZmljZSBTdGFmZiIgIkFkbWluaXN0cmF0aW9uIGFuZCBzdXBwb3J0IHN0YWZmIHdpdGhpbiB0aGUgYmFuay4iICJCYW5rIFN0YWZmIgoKICAgICAgICAgICAgbWFpbmZyYW1lID0gc29mdHdhcmVzeXN0ZW0gIk1haW5mcmFtZSBCYW5raW5nIFN5c3RlbSIgIlN0b3JlcyBhbGwgb2YgdGhlIGNvcmUgYmFua2luZyBpbmZvcm1hdGlvbiBhYm91dCBjdXN0b21lcnMsIGFjY291bnRzLCB0cmFuc2FjdGlvbnMsIGV0Yy4iICJFeGlzdGluZyBTeXN0ZW0iCiAgICAgICAgICAgIGVtYWlsID0gc29mdHdhcmVzeXN0ZW0gIkUtbWFpbCBTeXN0ZW0iICJUaGUgaW50ZXJuYWwgTWljcm9zb2Z0IEV4Y2hhbmdlIGUtbWFpbCBzeXN0ZW0uIiAiRXhpc3RpbmcgU3lzdGVtIgogICAgICAgICAgICBhdG0gPSBzb2Z0d2FyZXN5c3RlbSAiQVRNIiAiQWxsb3dzIGN1c3RvbWVycyB0byB3aXRoZHJhdyBjYXNoLiIgIkV4aXN0aW5nIFN5c3RlbSIKCiAgICAgICAgICAgIGludGVybmV0QmFua2luZ1N5c3RlbSA9IHNvZnR3YXJlc3lzdGVtICJJbnRlcm5ldCBCYW5raW5nIFN5c3RlbSIgIkFsbG93cyBjdXN0b21lcnMgdG8gdmlldyBpbmZvcm1hdGlvbiBhYm91dCB0aGVpciBiYW5rIGFjY291bnRzLCBhbmQgbWFrZSBwYXltZW50cy4iIHsKICAgICAgICAgICAgICAgIHNpbmdsZVBhZ2VBcHBsaWNhdGlvbiA9IGNvbnRhaW5lciAiU2luZ2xlLVBhZ2UgQXBwbGljYXRpb24iICJQcm92aWRlcyBhbGwgb2YgdGhlIEludGVybmV0IGJhbmtpbmcgZnVuY3Rpb25hbGl0eSB0byBjdXN0b21lcnMgdmlhIHRoZWlyIHdlYiBicm93c2VyLiIgIkphdmFTY3JpcHQgYW5kIEFuZ3VsYXIiICJXZWIgQnJvd3NlciIKICAgICAgICAgICAgICAgIG1vYmlsZUFwcCA9IGNvbnRhaW5lciAiTW9iaWxlIEFwcCIgIlByb3ZpZGVzIGEgbGltaXRlZCBzdWJzZXQgb2YgdGhlIEludGVybmV0IGJhbmtpbmcgZnVuY3Rpb25hbGl0eSB0byBjdXN0b21lcnMgdmlhIHRoZWlyIG1vYmlsZSBkZXZpY2UuIiAiWGFtYXJpbiIgIk1vYmlsZSBBcHAiCiAgICAgICAgICAgICAgICB3ZWJBcHBsaWNhdGlvbiA9IGNvbnRhaW5lciAiV2ViIEFwcGxpY2F0aW9uIiAiRGVsaXZlcnMgdGhlIHN0YXRpYyBjb250ZW50IGFuZCB0aGUgSW50ZXJuZXQgYmFua2luZyBzaW5nbGUgcGFnZSBhcHBsaWNhdGlvbi4iICJKYXZhIGFuZCBTcHJpbmcgTVZDIgogICAgICAgICAgICAgICAgYXBpQXBwbGljYXRpb24gPSBjb250YWluZXIgIkFQSSBBcHBsaWNhdGlvbiIgIlByb3ZpZGVzIEludGVybmV0IGJhbmtpbmcgZnVuY3Rpb25hbGl0eSB2aWEgYSBKU09OL0hUVFBTIEFQSS4iICJKYXZhIGFuZCBTcHJpbmcgTVZDIiB7CiAgICAgICAgICAgICAgICAgICAgc2lnbmluQ29udHJvbGxlciA9IGNvbXBvbmVudCAiU2lnbiBJbiBDb250cm9sbGVyIiAiQWxsb3dzIHVzZXJzIHRvIHNpZ24gaW4gdG8gdGhlIEludGVybmV0IEJhbmtpbmcgU3lzdGVtLiIgIlNwcmluZyBNVkMgUmVzdCBDb250cm9sbGVyIgogICAgICAgICAgICAgICAgICAgIGFjY291bnRzU3VtbWFyeUNvbnRyb2xsZXIgPSBjb21wb25lbnQgIkFjY291bnRzIFN1bW1hcnkgQ29udHJvbGxlciIgIlByb3ZpZGVzIGN1c3RvbWVycyB3aXRoIGEgc3VtbWFyeSBvZiB0aGVpciBiYW5rIGFjY291bnRzLiIgIlNwcmluZyBNVkMgUmVzdCBDb250cm9sbGVyIgogICAgICAgICAgICAgICAgICAgIHJlc2V0UGFzc3dvcmRDb250cm9sbGVyID0gY29tcG9uZW50ICJSZXNldCBQYXNzd29yZCBDb250cm9sbGVyIiAiQWxsb3dzIHVzZXJzIHRvIHJlc2V0IHRoZWlyIHBhc3N3b3JkcyB3aXRoIGEgc2luZ2xlIHVzZSBVUkwuIiAiU3ByaW5nIE1WQyBSZXN0IENvbnRyb2xsZXIiCiAgICAgICAgICAgICAgICAgICAgc2VjdXJpdHlDb21wb25lbnQgPSBjb21wb25lbnQgIlNlY3VyaXR5IENvbXBvbmVudCIgIlByb3ZpZGVzIGZ1bmN0aW9uYWxpdHkgcmVsYXRlZCB0byBzaWduaW5nIGluLCBjaGFuZ2luZyBwYXNzd29yZHMsIGV0Yy4iICJTcHJpbmcgQmVhbiIKICAgICAgICAgICAgICAgICAgICBtYWluZnJhbWVCYW5raW5nU3lzdGVtRmFjYWRlID0gY29tcG9uZW50ICJNYWluZnJhbWUgQmFua2luZyBTeXN0ZW0gRmFjYWRlIiAiQSBmYWNhZGUgb250byB0aGUgbWFpbmZyYW1lIGJhbmtpbmcgc3lzdGVtLiIgIlNwcmluZyBCZWFuIgogICAgICAgICAgICAgICAgICAgIGVtYWlsQ29tcG9uZW50ID0gY29tcG9uZW50ICJFLW1haWwgQ29tcG9uZW50IiAiU2VuZHMgZS1tYWlscyB0byB1c2Vycy4iICJTcHJpbmcgQmVhbiIKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIGRhdGFiYXNlID0gY29udGFpbmVyICJEYXRhYmFzZSIgIlN0b3JlcyB1c2VyIHJlZ2lzdHJhdGlvbiBpbmZvcm1hdGlvbiwgaGFzaGVkIGF1dGhlbnRpY2F0aW9uIGNyZWRlbnRpYWxzLCBhY2Nlc3MgbG9ncywgZXRjLiIgIk9yYWNsZSBEYXRhYmFzZSBTY2hlbWEiICJEYXRhYmFzZSIKICAgICAgICAgICAgfQogICAgICAgIH0KCiAgICAgICAgIyByZWxhdGlvbnNoaXBzIGJldHdlZW4gcGVvcGxlIGFuZCBzb2Z0d2FyZSBzeXN0ZW1zCiAgICAgICAgY3VzdG9tZXIgLT4gaW50ZXJuZXRCYW5raW5nU3lzdGVtICJWaWV3cyBhY2NvdW50IGJhbGFuY2VzLCBhbmQgbWFrZXMgcGF5bWVudHMgdXNpbmciCiAgICAgICAgaW50ZXJuZXRCYW5raW5nU3lzdGVtIC0+IG1haW5mcmFtZSAiR2V0cyBhY2NvdW50IGluZm9ybWF0aW9uIGZyb20sIGFuZCBtYWtlcyBwYXltZW50cyB1c2luZyIKICAgICAgICBpbnRlcm5ldEJhbmtpbmdTeXN0ZW0gLT4gZW1haWwgIlNlbmRzIGUtbWFpbCB1c2luZyIKICAgICAgICBlbWFpbCAtPiBjdXN0b21lciAiU2VuZHMgZS1tYWlscyB0byIKICAgICAgICBjdXN0b21lciAtPiBzdXBwb3J0U3RhZmYgIkFza3MgcXVlc3Rpb25zIHRvIiAiVGVsZXBob25lIgogICAgICAgIHN1cHBvcnRTdGFmZiAtPiBtYWluZnJhbWUgIlVzZXMiCiAgICAgICAgY3VzdG9tZXIgLT4gYXRtICJXaXRoZHJhd3MgY2FzaCB1c2luZyIKICAgICAgICBhdG0gLT4gbWFpbmZyYW1lICJVc2VzIgogICAgICAgIGJhY2tvZmZpY2UgLT4gbWFpbmZyYW1lICJVc2VzIgoKICAgICAgICAjIHJlbGF0aW9uc2hpcHMgdG8vZnJvbSBjb250YWluZXJzCiAgICAgICAgY3VzdG9tZXIgLT4gd2ViQXBwbGljYXRpb24gIlZpc2l0cyBiaWdiYW5rLmNvbS9pYiB1c2luZyIgIkhUVFBTIgogICAgICAgIGN1c3RvbWVyIC0+IHNpbmdsZVBhZ2VBcHBsaWNhdGlvbiAiVmlld3MgYWNjb3VudCBiYWxhbmNlcywgYW5kIG1ha2VzIHBheW1lbnRzIHVzaW5nIgogICAgICAgIGN1c3RvbWVyIC0+IG1vYmlsZUFwcCAiVmlld3MgYWNjb3VudCBiYWxhbmNlcywgYW5kIG1ha2VzIHBheW1lbnRzIHVzaW5nIgogICAgICAgIHdlYkFwcGxpY2F0aW9uIC0+IHNpbmdsZVBhZ2VBcHBsaWNhdGlvbiAiRGVsaXZlcnMgdG8gdGhlIGN1c3RvbWVyJ3Mgd2ViIGJyb3dzZXIiCgogICAgICAgICMgcmVsYXRpb25zaGlwcyB0by9mcm9tIGNvbXBvbmVudHMKICAgICAgICBzaW5nbGVQYWdlQXBwbGljYXRpb24gLT4gc2lnbmluQ29udHJvbGxlciAiTWFrZXMgQVBJIGNhbGxzIHRvIiAiSlNPTi9IVFRQUyIKICAgICAgICBzaW5nbGVQYWdlQXBwbGljYXRpb24gLT4gYWNjb3VudHNTdW1tYXJ5Q29udHJvbGxlciAiTWFrZXMgQVBJIGNhbGxzIHRvIiAiSlNPTi9IVFRQUyIKICAgICAgICBzaW5nbGVQYWdlQXBwbGljYXRpb24gLT4gcmVzZXRQYXNzd29yZENvbnRyb2xsZXIgIk1ha2VzIEFQSSBjYWxscyB0byIgIkpTT04vSFRUUFMiCiAgICAgICAgbW9iaWxlQXBwIC0+IHNpZ25pbkNvbnRyb2xsZXIgIk1ha2VzIEFQSSBjYWxscyB0byIgIkpTT04vSFRUUFMiCiAgICAgICAgbW9iaWxlQXBwIC0+IGFjY291bnRzU3VtbWFyeUNvbnRyb2xsZXIgIk1ha2VzIEFQSSBjYWxscyB0byIgIkpTT04vSFRUUFMiCiAgICAgICAgbW9iaWxlQXBwIC0+IHJlc2V0UGFzc3dvcmRDb250cm9sbGVyICJNYWtlcyBBUEkgY2FsbHMgdG8iICJKU09OL0hUVFBTIgogICAgICAgIHNpZ25pbkNvbnRyb2xsZXIgLT4gc2VjdXJpdHlDb21wb25lbnQgIlVzZXMiCiAgICAgICAgYWNjb3VudHNTdW1tYXJ5Q29udHJvbGxlciAtPiBtYWluZnJhbWVCYW5raW5nU3lzdGVtRmFjYWRlICJVc2VzIgogICAgICAgIHJlc2V0UGFzc3dvcmRDb250cm9sbGVyIC0+IHNlY3VyaXR5Q29tcG9uZW50ICJVc2VzIgogICAgICAgIHJlc2V0UGFzc3dvcmRDb250cm9sbGVyIC0+IGVtYWlsQ29tcG9uZW50ICJVc2VzIgogICAgICAgIHNlY3VyaXR5Q29tcG9uZW50IC0+IGRhdGFiYXNlICJSZWFkcyBmcm9tIGFuZCB3cml0ZXMgdG8iICJTUUwvVENQIgogICAgICAgIG1haW5mcmFtZUJhbmtpbmdTeXN0ZW1GYWNhZGUgLT4gbWFpbmZyYW1lICJNYWtlcyBBUEkgY2FsbHMgdG8iICJYTUwvSFRUUFMiCiAgICAgICAgZW1haWxDb21wb25lbnQgLT4gZW1haWwgIlNlbmRzIGUtbWFpbCB1c2luZyIKCiAgICAgICAgZGVwbG95bWVudEVudmlyb25tZW50ICJEZXZlbG9wbWVudCIgewogICAgICAgICAgICBkZXBsb3ltZW50Tm9kZSAiRGV2ZWxvcGVyIExhcHRvcCIgIiIgIk1pY3Jvc29mdCBXaW5kb3dzIDEwIG9yIEFwcGxlIG1hY09TIiB7CiAgICAgICAgICAgICAgICBkZXBsb3ltZW50Tm9kZSAiV2ViIEJyb3dzZXIiICIiICJDaHJvbWUsIEZpcmVmb3gsIFNhZmFyaSwgb3IgRWRnZSIgewogICAgICAgICAgICAgICAgICAgIGRldmVsb3BlclNpbmdsZVBhZ2VBcHBsaWNhdGlvbkluc3RhbmNlID0gY29udGFpbmVySW5zdGFuY2Ugc2luZ2xlUGFnZUFwcGxpY2F0aW9uCiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICBkZXBsb3ltZW50Tm9kZSAiRG9ja2VyIENvbnRhaW5lciAtIFdlYiBTZXJ2ZXIiICIiICJEb2NrZXIiIHsKICAgICAgICAgICAgICAgICAgICBkZXBsb3ltZW50Tm9kZSAiQXBhY2hlIFRvbWNhdCIgIiIgIkFwYWNoZSBUb21jYXQgOC54IiB7CiAgICAgICAgICAgICAgICAgICAgICAgIGRldmVsb3BlcldlYkFwcGxpY2F0aW9uSW5zdGFuY2UgPSBjb250YWluZXJJbnN0YW5jZSB3ZWJBcHBsaWNhdGlvbgogICAgICAgICAgICAgICAgICAgICAgICBkZXZlbG9wZXJBcGlBcHBsaWNhdGlvbkluc3RhbmNlID0gY29udGFpbmVySW5zdGFuY2UgYXBpQXBwbGljYXRpb24KICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICBkZXBsb3ltZW50Tm9kZSAiRG9ja2VyIENvbnRhaW5lciAtIERhdGFiYXNlIFNlcnZlciIgIiIgIkRvY2tlciIgewogICAgICAgICAgICAgICAgICAgIGRlcGxveW1lbnROb2RlICJEYXRhYmFzZSBTZXJ2ZXIiICIiICJPcmFjbGUgMTJjIiB7CiAgICAgICAgICAgICAgICAgICAgICAgIGRldmVsb3BlckRhdGFiYXNlSW5zdGFuY2UgPSBjb250YWluZXJJbnN0YW5jZSBkYXRhYmFzZQogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgICAgICBkZXBsb3ltZW50Tm9kZSAiQmlnIEJhbmsgcGxjIiAiIiAiQmlnIEJhbmsgcGxjIGRhdGEgY2VudGVyIiAiIiB7CiAgICAgICAgICAgICAgICBkZXBsb3ltZW50Tm9kZSAiYmlnYmFuay1kZXYwMDEiICIiICIiICIiIHsKICAgICAgICAgICAgICAgICAgICBzb2Z0d2FyZVN5c3RlbUluc3RhbmNlIG1haW5mcmFtZQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CgogICAgICAgIH0KCiAgICAgICAgZGVwbG95bWVudEVudmlyb25tZW50ICJMaXZlIiB7CiAgICAgICAgICAgIGRlcGxveW1lbnROb2RlICJDdXN0b21lcidzIG1vYmlsZSBkZXZpY2UiICIiICJBcHBsZSBpT1Mgb3IgQW5kcm9pZCIgewogICAgICAgICAgICAgICAgbGl2ZU1vYmlsZUFwcEluc3RhbmNlID0gY29udGFpbmVySW5zdGFuY2UgbW9iaWxlQXBwCiAgICAgICAgICAgIH0KICAgICAgICAgICAgZGVwbG95bWVudE5vZGUgIkN1c3RvbWVyJ3MgY29tcHV0ZXIiICIiICJNaWNyb3NvZnQgV2luZG93cyBvciBBcHBsZSBtYWNPUyIgewogICAgICAgICAgICAgICAgZGVwbG95bWVudE5vZGUgIldlYiBCcm93c2VyIiAiIiAiQ2hyb21lLCBGaXJlZm94LCBTYWZhcmksIG9yIEVkZ2UiIHsKICAgICAgICAgICAgICAgICAgICBsaXZlU2luZ2xlUGFnZUFwcGxpY2F0aW9uSW5zdGFuY2UgPSBjb250YWluZXJJbnN0YW5jZSBzaW5nbGVQYWdlQXBwbGljYXRpb24KICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQoKICAgICAgICAgICAgZGVwbG95bWVudE5vZGUgIkJpZyBCYW5rIHBsYyIgIiIgIkJpZyBCYW5rIHBsYyBkYXRhIGNlbnRlciIgewogICAgICAgICAgICAgICAgZGVwbG95bWVudE5vZGUgImJpZ2Jhbmstd2ViKioqIiAiIiAiVWJ1bnR1IDE2LjA0IExUUyIgIiIgNCB7CiAgICAgICAgICAgICAgICAgICAgZGVwbG95bWVudE5vZGUgIkFwYWNoZSBUb21jYXQiICIiICJBcGFjaGUgVG9tY2F0IDgueCIgewogICAgICAgICAgICAgICAgICAgICAgICBsaXZlV2ViQXBwbGljYXRpb25JbnN0YW5jZSA9IGNvbnRhaW5lckluc3RhbmNlIHdlYkFwcGxpY2F0aW9uCiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgZGVwbG95bWVudE5vZGUgImJpZ2JhbmstYXBpKioqIiAiIiAiVWJ1bnR1IDE2LjA0IExUUyIgIiIgOCB7CiAgICAgICAgICAgICAgICAgICAgZGVwbG95bWVudE5vZGUgIkFwYWNoZSBUb21jYXQiICIiICJBcGFjaGUgVG9tY2F0IDgueCIgewogICAgICAgICAgICAgICAgICAgICAgICBsaXZlQXBpQXBwbGljYXRpb25JbnN0YW5jZSA9IGNvbnRhaW5lckluc3RhbmNlIGFwaUFwcGxpY2F0aW9uCiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIGRlcGxveW1lbnROb2RlICJiaWdiYW5rLWRiMDEiICIiICJVYnVudHUgMTYuMDQgTFRTIiB7CiAgICAgICAgICAgICAgICAgICAgcHJpbWFyeURhdGFiYXNlU2VydmVyID0gZGVwbG95bWVudE5vZGUgIk9yYWNsZSAtIFByaW1hcnkiICIiICJPcmFjbGUgMTJjIiB7CiAgICAgICAgICAgICAgICAgICAgICAgIGxpdmVQcmltYXJ5RGF0YWJhc2VJbnN0YW5jZSA9IGNvbnRhaW5lckluc3RhbmNlIGRhdGFiYXNlCiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgZGVwbG95bWVudE5vZGUgImJpZ2JhbmstZGIwMiIgIiIgIlVidW50dSAxNi4wNCBMVFMiICJGYWlsb3ZlciIgewogICAgICAgICAgICAgICAgICAgIHNlY29uZGFyeURhdGFiYXNlU2VydmVyID0gZGVwbG95bWVudE5vZGUgIk9yYWNsZSAtIFNlY29uZGFyeSIgIiIgIk9yYWNsZSAxMmMiICJGYWlsb3ZlciIgewogICAgICAgICAgICAgICAgICAgICAgICBsaXZlU2Vjb25kYXJ5RGF0YWJhc2VJbnN0YW5jZSA9IGNvbnRhaW5lckluc3RhbmNlIGRhdGFiYXNlICJGYWlsb3ZlciIKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICBkZXBsb3ltZW50Tm9kZSAiYmlnYmFuay1wcm9kMDAxIiAiIiAiIiAiIiB7CiAgICAgICAgICAgICAgICAgICAgc29mdHdhcmVTeXN0ZW1JbnN0YW5jZSBtYWluZnJhbWUKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQoKICAgICAgICAgICAgcHJpbWFyeURhdGFiYXNlU2VydmVyIC0+IHNlY29uZGFyeURhdGFiYXNlU2VydmVyICJSZXBsaWNhdGVzIGRhdGEgdG8iCiAgICAgICAgfQogICAgfQoKICAgIHZpZXdzIHsKICAgICAgICBzeXN0ZW1sYW5kc2NhcGUgIlN5c3RlbUxhbmRzY2FwZSIgewogICAgICAgICAgICBpbmNsdWRlICoKICAgICAgICAgICAgYXV0b0xheW91dAogICAgICAgIH0KCiAgICAgICAgc3lzdGVtY29udGV4dCBpbnRlcm5ldEJhbmtpbmdTeXN0ZW0gIlN5c3RlbUNvbnRleHQiIHsKICAgICAgICAgICAgaW5jbHVkZSAqCiAgICAgICAgICAgIGFuaW1hdGlvbiB7CiAgICAgICAgICAgICAgICBpbnRlcm5ldEJhbmtpbmdTeXN0ZW0KICAgICAgICAgICAgICAgIGN1c3RvbWVyCiAgICAgICAgICAgICAgICBtYWluZnJhbWUKICAgICAgICAgICAgICAgIGVtYWlsCiAgICAgICAgICAgIH0KICAgICAgICAgICAgYXV0b0xheW91dAogICAgICAgICAgICBkZXNjcmlwdGlvbiAiVGhlIHN5c3RlbSBjb250ZXh0IGRpYWdyYW0gZm9yIHRoZSBJbnRlcm5ldCBCYW5raW5nIFN5c3RlbS4iCiAgICAgICAgICAgIHByb3BlcnRpZXMgewogICAgICAgICAgICAgICAgc3RydWN0dXJpenIuZ3JvdXBzIGZhbHNlCiAgICAgICAgICAgIH0KICAgICAgICB9CgogICAgICAgIGNvbnRhaW5lciBpbnRlcm5ldEJhbmtpbmdTeXN0ZW0gIkNvbnRhaW5lcnMiIHsKICAgICAgICAgICAgaW5jbHVkZSAqCiAgICAgICAgICAgIGFuaW1hdGlvbiB7CiAgICAgICAgICAgICAgICBjdXN0b21lciBtYWluZnJhbWUgZW1haWwKICAgICAgICAgICAgICAgIHdlYkFwcGxpY2F0aW9uCiAgICAgICAgICAgICAgICBzaW5nbGVQYWdlQXBwbGljYXRpb24KICAgICAgICAgICAgICAgIG1vYmlsZUFwcAogICAgICAgICAgICAgICAgYXBpQXBwbGljYXRpb24KICAgICAgICAgICAgICAgIGRhdGFiYXNlCiAgICAgICAgICAgIH0KICAgICAgICAgICAgYXV0b0xheW91dAogICAgICAgICAgICBkZXNjcmlwdGlvbiAiVGhlIGNvbnRhaW5lciBkaWFncmFtIGZvciB0aGUgSW50ZXJuZXQgQmFua2luZyBTeXN0ZW0uIgogICAgICAgIH0KCiAgICAgICAgY29tcG9uZW50IGFwaUFwcGxpY2F0aW9uICJDb21wb25lbnRzIiB7CiAgICAgICAgICAgIGluY2x1ZGUgKgogICAgICAgICAgICBhbmltYXRpb24gewogICAgICAgICAgICAgICAgc2luZ2xlUGFnZUFwcGxpY2F0aW9uIG1vYmlsZUFwcCBkYXRhYmFzZSBlbWFpbCBtYWluZnJhbWUKICAgICAgICAgICAgICAgIHNpZ25pbkNvbnRyb2xsZXIgc2VjdXJpdHlDb21wb25lbnQKICAgICAgICAgICAgICAgIGFjY291bnRzU3VtbWFyeUNvbnRyb2xsZXIgbWFpbmZyYW1lQmFua2luZ1N5c3RlbUZhY2FkZQogICAgICAgICAgICAgICAgcmVzZXRQYXNzd29yZENvbnRyb2xsZXIgZW1haWxDb21wb25lbnQKICAgICAgICAgICAgfQogICAgICAgICAgICBhdXRvTGF5b3V0CiAgICAgICAgICAgIGRlc2NyaXB0aW9uICJUaGUgY29tcG9uZW50IGRpYWdyYW0gZm9yIHRoZSBBUEkgQXBwbGljYXRpb24uIgogICAgICAgIH0KCiAgICAgICAgaW1hZ2UgbWFpbmZyYW1lQmFua2luZ1N5c3RlbUZhY2FkZSAiTWFpbmZyYW1lQmFua2luZ1N5c3RlbUZhY2FkZSIgewogICAgICAgICAgICBpbWFnZSBodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vc3RydWN0dXJpenIvZXhhbXBsZXMvbWFpbi9kc2wvYmlnLWJhbmstcGxjL2ludGVybmV0LWJhbmtpbmctc3lzdGVtL21haW5mcmFtZS1iYW5raW5nLXN5c3RlbS1mYWNhZGUucG5nCiAgICAgICAgICAgIHRpdGxlICJbQ29kZV0gTWFpbmZyYW1lIEJhbmtpbmcgU3lzdGVtIEZhY2FkZSIKICAgICAgICB9CgogICAgICAgIGR5bmFtaWMgYXBpQXBwbGljYXRpb24gIlNpZ25JbiIgIlN1bW1hcmlzZXMgaG93IHRoZSBzaWduIGluIGZlYXR1cmUgd29ya3MgaW4gdGhlIHNpbmdsZS1wYWdlIGFwcGxpY2F0aW9uLiIgewogICAgICAgICAgICBzaW5nbGVQYWdlQXBwbGljYXRpb24gLT4gc2lnbmluQ29udHJvbGxlciAiU3VibWl0cyBjcmVkZW50aWFscyB0byIKICAgICAgICAgICAgc2lnbmluQ29udHJvbGxlciAtPiBzZWN1cml0eUNvbXBvbmVudCAiVmFsaWRhdGVzIGNyZWRlbnRpYWxzIHVzaW5nIgogICAgICAgICAgICBzZWN1cml0eUNvbXBvbmVudCAtPiBkYXRhYmFzZSAic2VsZWN0ICogZnJvbSB1c2VycyB3aGVyZSB1c2VybmFtZSA9ID8iCiAgICAgICAgICAgIGRhdGFiYXNlIC0+IHNlY3VyaXR5Q29tcG9uZW50ICJSZXR1cm5zIHVzZXIgZGF0YSB0byIKICAgICAgICAgICAgc2VjdXJpdHlDb21wb25lbnQgLT4gc2lnbmluQ29udHJvbGxlciAiUmV0dXJucyB0cnVlIGlmIHRoZSBoYXNoZWQgcGFzc3dvcmQgbWF0Y2hlcyIKICAgICAgICAgICAgc2lnbmluQ29udHJvbGxlciAtPiBzaW5nbGVQYWdlQXBwbGljYXRpb24gIlNlbmRzIGJhY2sgYW4gYXV0aGVudGljYXRpb24gdG9rZW4gdG8iCiAgICAgICAgICAgIGF1dG9MYXlvdXQKICAgICAgICAgICAgZGVzY3JpcHRpb24gIlN1bW1hcmlzZXMgaG93IHRoZSBzaWduIGluIGZlYXR1cmUgd29ya3MgaW4gdGhlIHNpbmdsZS1wYWdlIGFwcGxpY2F0aW9uLiIKICAgICAgICB9CgogICAgICAgIGRlcGxveW1lbnQgaW50ZXJuZXRCYW5raW5nU3lzdGVtICJEZXZlbG9wbWVudCIgIkRldmVsb3BtZW50RGVwbG95bWVudCIgewogICAgICAgICAgICBpbmNsdWRlICoKICAgICAgICAgICAgYW5pbWF0aW9uIHsKICAgICAgICAgICAgICAgIGRldmVsb3BlclNpbmdsZVBhZ2VBcHBsaWNhdGlvbkluc3RhbmNlCiAgICAgICAgICAgICAgICBkZXZlbG9wZXJXZWJBcHBsaWNhdGlvbkluc3RhbmNlIGRldmVsb3BlckFwaUFwcGxpY2F0aW9uSW5zdGFuY2UKICAgICAgICAgICAgICAgIGRldmVsb3BlckRhdGFiYXNlSW5zdGFuY2UKICAgICAgICAgICAgfQogICAgICAgICAgICBhdXRvTGF5b3V0CiAgICAgICAgICAgIGRlc2NyaXB0aW9uICJBbiBleGFtcGxlIGRldmVsb3BtZW50IGRlcGxveW1lbnQgc2NlbmFyaW8gZm9yIHRoZSBJbnRlcm5ldCBCYW5raW5nIFN5c3RlbS4iCiAgICAgICAgfQoKICAgICAgICBkZXBsb3ltZW50IGludGVybmV0QmFua2luZ1N5c3RlbSAiTGl2ZSIgIkxpdmVEZXBsb3ltZW50IiB7CiAgICAgICAgICAgIGluY2x1ZGUgKgogICAgICAgICAgICBhbmltYXRpb24gewogICAgICAgICAgICAgICAgbGl2ZVNpbmdsZVBhZ2VBcHBsaWNhdGlvbkluc3RhbmNlCiAgICAgICAgICAgICAgICBsaXZlTW9iaWxlQXBwSW5zdGFuY2UKICAgICAgICAgICAgICAgIGxpdmVXZWJBcHBsaWNhdGlvbkluc3RhbmNlIGxpdmVBcGlBcHBsaWNhdGlvbkluc3RhbmNlCiAgICAgICAgICAgICAgICBsaXZlUHJpbWFyeURhdGFiYXNlSW5zdGFuY2UKICAgICAgICAgICAgICAgIGxpdmVTZWNvbmRhcnlEYXRhYmFzZUluc3RhbmNlCiAgICAgICAgICAgIH0KICAgICAgICAgICAgYXV0b0xheW91dAogICAgICAgICAgICBkZXNjcmlwdGlvbiAiQW4gZXhhbXBsZSBsaXZlIGRlcGxveW1lbnQgc2NlbmFyaW8gZm9yIHRoZSBJbnRlcm5ldCBCYW5raW5nIFN5c3RlbS4iCiAgICAgICAgfQoKICAgICAgICBzdHlsZXMgewogICAgICAgICAgICBlbGVtZW50ICJQZXJzb24iIHsKICAgICAgICAgICAgICAgIGNvbG9yICNmZmZmZmYKICAgICAgICAgICAgICAgIGZvbnRTaXplIDIyCiAgICAgICAgICAgICAgICBzaGFwZSBQZXJzb24KICAgICAgICAgICAgfQogICAgICAgICAgICBlbGVtZW50ICJDdXN0b21lciIgewogICAgICAgICAgICAgICAgYmFja2dyb3VuZCAjMDg0MjdiCiAgICAgICAgICAgIH0KICAgICAgICAgICAgZWxlbWVudCAiQmFuayBTdGFmZiIgewogICAgICAgICAgICAgICAgYmFja2dyb3VuZCAjOTk5OTk5CiAgICAgICAgICAgIH0KICAgICAgICAgICAgZWxlbWVudCAiU29mdHdhcmUgU3lzdGVtIiB7CiAgICAgICAgICAgICAgICBiYWNrZ3JvdW5kICMxMTY4YmQKICAgICAgICAgICAgICAgIGNvbG9yICNmZmZmZmYKICAgICAgICAgICAgfQogICAgICAgICAgICBlbGVtZW50ICJFeGlzdGluZyBTeXN0ZW0iIHsKICAgICAgICAgICAgICAgIGJhY2tncm91bmQgIzk5OTk5OQogICAgICAgICAgICAgICAgY29sb3IgI2ZmZmZmZgogICAgICAgICAgICB9CiAgICAgICAgICAgIGVsZW1lbnQgIkNvbnRhaW5lciIgewogICAgICAgICAgICAgICAgYmFja2dyb3VuZCAjNDM4ZGQ1CiAgICAgICAgICAgICAgICBjb2xvciAjZmZmZmZmCiAgICAgICAgICAgIH0KICAgICAgICAgICAgZWxlbWVudCAiV2ViIEJyb3dzZXIiIHsKICAgICAgICAgICAgICAgIHNoYXBlIFdlYkJyb3dzZXIKICAgICAgICAgICAgfQogICAgICAgICAgICBlbGVtZW50ICJNb2JpbGUgQXBwIiB7CiAgICAgICAgICAgICAgICBzaGFwZSBNb2JpbGVEZXZpY2VMYW5kc2NhcGUKICAgICAgICAgICAgfQogICAgICAgICAgICBlbGVtZW50ICJEYXRhYmFzZSIgewogICAgICAgICAgICAgICAgc2hhcGUgQ3lsaW5kZXIKICAgICAgICAgICAgfQogICAgICAgICAgICBlbGVtZW50ICJDb21wb25lbnQiIHsKICAgICAgICAgICAgICAgIGJhY2tncm91bmQgIzg1YmJmMAogICAgICAgICAgICAgICAgY29sb3IgIzAwMDAwMAogICAgICAgICAgICB9CiAgICAgICAgICAgIGVsZW1lbnQgIkZhaWxvdmVyIiB7CiAgICAgICAgICAgICAgICBvcGFjaXR5IDI1CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICB9Cn0K" + }, + "configuration" : { }, + "model" : { + "people" : [ { + "id" : "1", + "tags" : "Element,Person,Customer", + "properties" : { + "structurizr.dsl.identifier" : "customer" + }, + "name" : "Personal Banking Customer", + "description" : "A customer of the bank, with personal bank accounts.", + "relationships" : [ { + "id" : "19", + "tags" : "Relationship", + "properties" : { + "structurizr.dsl.identifier" : "6d299902-3f78-4a39-a8ca-3a61e3e50f3e" }, - "people": [ - { - "id": "15", - "tags": "Element,Person,Bank Staff", - "name": "Back Office Staff", - "description": "Administration and support staff within the bank.", - "relationships": [ - { - "id": "16", - "tags": "Relationship", - "sourceId": "15", - "destinationId": "4", - "description": "Uses" - } - ], - "location": "Internal" + "sourceId" : "1", + "destinationId" : "7", + "description" : "Views account balances, and makes payments using" + }, { + "id" : "23", + "tags" : "Relationship", + "properties" : { + "structurizr.dsl.identifier" : "e61b034b-4617-4f42-8831-b171bdd5f7df" + }, + "sourceId" : "1", + "destinationId" : "2", + "description" : "Asks questions to", + "technology" : "Telephone" + }, { + "id" : "25", + "tags" : "Relationship", + "properties" : { + "structurizr.dsl.identifier" : "2ebe5df0-e547-4c9b-9403-a83561d958ea" + }, + "sourceId" : "1", + "destinationId" : "6", + "description" : "Withdraws cash using" + }, { + "id" : "28", + "tags" : "Relationship", + "properties" : { + "structurizr.dsl.identifier" : "4a08e84b-acb3-4fc4-a7f1-8ebe621a0f0f" + }, + "sourceId" : "1", + "destinationId" : "10", + "description" : "Visits bigbank.com/ib using", + "technology" : "HTTPS" + }, { + "id" : "29", + "tags" : "Relationship", + "properties" : { + "structurizr.dsl.identifier" : "025a49a0-4557-44bb-9724-2d7ea4d7e1ed" + }, + "sourceId" : "1", + "destinationId" : "8", + "description" : "Views account balances, and makes payments using" + }, { + "id" : "30", + "tags" : "Relationship", + "properties" : { + "structurizr.dsl.identifier" : "c6295d75-dd8c-494e-8d69-096540a25b4b" + }, + "sourceId" : "1", + "destinationId" : "9", + "description" : "Views account balances, and makes payments using" + } ], + "location" : "Unspecified" + }, { + "id" : "2", + "tags" : "Element,Person,Bank Staff", + "properties" : { + "structurizr.dsl.identifier" : "supportstaff" + }, + "name" : "Customer Service Staff", + "description" : "Customer service staff within the bank.", + "relationships" : [ { + "id" : "24", + "tags" : "Relationship", + "properties" : { + "structurizr.dsl.identifier" : "ad399353-807e-4350-ad5f-2359c54844db" + }, + "sourceId" : "2", + "destinationId" : "4", + "description" : "Uses" + } ], + "group" : "Big Bank plc", + "location" : "Unspecified" + }, { + "id" : "3", + "tags" : "Element,Person,Bank Staff", + "properties" : { + "structurizr.dsl.identifier" : "backoffice" + }, + "name" : "Back Office Staff", + "description" : "Administration and support staff within the bank.", + "relationships" : [ { + "id" : "27", + "tags" : "Relationship", + "properties" : { + "structurizr.dsl.identifier" : "a4243927-b19d-423f-9f2c-6d84ba3604e3" + }, + "sourceId" : "3", + "destinationId" : "4", + "description" : "Uses" + } ], + "group" : "Big Bank plc", + "location" : "Unspecified" + } ], + "softwareSystems" : [ { + "id" : "4", + "tags" : "Element,Software System,Existing System", + "properties" : { + "structurizr.dsl.identifier" : "mainframe" + }, + "name" : "Mainframe Banking System", + "description" : "Stores all of the core banking information about customers, accounts, transactions, etc.", + "group" : "Big Bank plc", + "location" : "Unspecified", + "documentation" : { } + }, { + "id" : "5", + "tags" : "Element,Software System,Existing System", + "properties" : { + "structurizr.dsl.identifier" : "email" + }, + "name" : "E-mail System", + "description" : "The internal Microsoft Exchange e-mail system.", + "relationships" : [ { + "id" : "22", + "tags" : "Relationship", + "properties" : { + "structurizr.dsl.identifier" : "93b2b770-fec1-40f4-ab14-cec2eb3c75d6" + }, + "sourceId" : "5", + "destinationId" : "1", + "description" : "Sends e-mails to" + } ], + "group" : "Big Bank plc", + "location" : "Unspecified", + "documentation" : { } + }, { + "id" : "6", + "tags" : "Element,Software System,Existing System", + "properties" : { + "structurizr.dsl.identifier" : "atm" + }, + "name" : "ATM", + "description" : "Allows customers to withdraw cash.", + "relationships" : [ { + "id" : "26", + "tags" : "Relationship", + "properties" : { + "structurizr.dsl.identifier" : "e2e730e2-ce51-4154-9847-b8d73823cdc2" + }, + "sourceId" : "6", + "destinationId" : "4", + "description" : "Uses" + } ], + "group" : "Big Bank plc", + "location" : "Unspecified", + "documentation" : { } + }, { + "id" : "7", + "tags" : "Element,Software System", + "properties" : { + "structurizr.dsl.identifier" : "internetbankingsystem" + }, + "name" : "Internet Banking System", + "description" : "Allows customers to view information about their bank accounts, and make payments.", + "relationships" : [ { + "id" : "20", + "tags" : "Relationship", + "properties" : { + "structurizr.dsl.identifier" : "bd46add8-7f56-4639-9f1c-1569a681ad92" + }, + "sourceId" : "7", + "destinationId" : "4", + "description" : "Gets account information from, and makes payments using" + }, { + "id" : "21", + "tags" : "Relationship", + "properties" : { + "structurizr.dsl.identifier" : "c58d1eb9-d9da-4f37-b9f5-e2035c651abd" + }, + "sourceId" : "7", + "destinationId" : "5", + "description" : "Sends e-mail using" + } ], + "group" : "Big Bank plc", + "location" : "Unspecified", + "containers" : [ { + "id" : "18", + "tags" : "Element,Container,Database", + "properties" : { + "structurizr.dsl.identifier" : "database" + }, + "name" : "Database", + "description" : "Stores user registration information, hashed authentication credentials, access logs, etc.", + "technology" : "Oracle Database Schema", + "documentation" : { } + }, { + "id" : "9", + "tags" : "Element,Container,Mobile App", + "properties" : { + "structurizr.dsl.identifier" : "mobileapp" + }, + "name" : "Mobile App", + "description" : "Provides a limited subset of the Internet banking functionality to customers via their mobile device.", + "relationships" : [ { + "id" : "36", + "tags" : "Relationship", + "properties" : { + "structurizr.dsl.identifier" : "5fce96a5-2f6c-41e1-9263-26174a28ced1" + }, + "sourceId" : "9", + "destinationId" : "12", + "description" : "Makes API calls to", + "technology" : "JSON/HTTPS" + }, { + "id" : "37", + "sourceId" : "9", + "destinationId" : "11", + "description" : "Makes API calls to", + "technology" : "JSON/HTTPS", + "linkedRelationshipId" : "36" + }, { + "id" : "38", + "tags" : "Relationship", + "properties" : { + "structurizr.dsl.identifier" : "3ee42ae6-31e2-4644-8d27-0d9f8fde6919" + }, + "sourceId" : "9", + "destinationId" : "13", + "description" : "Makes API calls to", + "technology" : "JSON/HTTPS" + }, { + "id" : "39", + "tags" : "Relationship", + "properties" : { + "structurizr.dsl.identifier" : "65f4415c-7575-4230-bd32-6d8d754d01ca" + }, + "sourceId" : "9", + "destinationId" : "14", + "description" : "Makes API calls to", + "technology" : "JSON/HTTPS" + } ], + "technology" : "Xamarin", + "documentation" : { } + }, { + "id" : "10", + "tags" : "Element,Container", + "properties" : { + "structurizr.dsl.identifier" : "webapplication" + }, + "name" : "Web Application", + "description" : "Delivers the static content and the Internet banking single page application.", + "relationships" : [ { + "id" : "31", + "tags" : "Relationship", + "properties" : { + "structurizr.dsl.identifier" : "792ce67f-78f1-4c65-933b-84eece4334e2" + }, + "sourceId" : "10", + "destinationId" : "8", + "description" : "Delivers to the customer's web browser" + } ], + "technology" : "Java and Spring MVC", + "documentation" : { } + }, { + "id" : "11", + "tags" : "Element,Container", + "properties" : { + "structurizr.dsl.identifier" : "apiapplication" + }, + "name" : "API Application", + "description" : "Provides Internet banking functionality via a JSON/HTTPS API.", + "relationships" : [ { + "id" : "45", + "sourceId" : "11", + "destinationId" : "18", + "description" : "Reads from and writes to", + "technology" : "SQL/TCP", + "linkedRelationshipId" : "44" + }, { + "id" : "47", + "sourceId" : "11", + "destinationId" : "4", + "description" : "Makes API calls to", + "technology" : "XML/HTTPS", + "linkedRelationshipId" : "46" + }, { + "id" : "49", + "sourceId" : "11", + "destinationId" : "5", + "description" : "Sends e-mail using", + "linkedRelationshipId" : "48" + } ], + "technology" : "Java and Spring MVC", + "components" : [ { + "id" : "16", + "tags" : "Element,Component", + "properties" : { + "structurizr.dsl.identifier" : "mainframebankingsystemfacade" + }, + "name" : "Mainframe Banking System Facade", + "description" : "A facade onto the mainframe banking system.", + "relationships" : [ { + "id" : "46", + "tags" : "Relationship", + "properties" : { + "structurizr.dsl.identifier" : "2f9ddb3a-77a2-433c-9257-49bfe5bb0766" }, - { - "id": "12", - "tags": "Element,Person,Bank Staff", - "name": "Customer Service Staff", - "description": "Customer service staff within the bank.", - "relationships": [ - { - "id": "13", - "tags": "Relationship", - "sourceId": "12", - "destinationId": "4", - "description": "Uses" - } - ], - "location": "Internal" + "sourceId" : "16", + "destinationId" : "4", + "description" : "Makes API calls to", + "technology" : "XML/HTTPS" + } ], + "technology" : "Spring Bean", + "documentation" : { } + }, { + "id" : "12", + "tags" : "Element,Component", + "properties" : { + "structurizr.dsl.identifier" : "signincontroller" + }, + "name" : "Sign In Controller", + "description" : "Allows users to sign in to the Internet Banking System.", + "relationships" : [ { + "id" : "40", + "tags" : "Relationship", + "properties" : { + "structurizr.dsl.identifier" : "2f405114-17b0-4223-aaa1-12f87d1ca8ba" }, - { - "id": "1", - "tags": "Element,Person", - "name": "Personal Banking Customer", - "description": "A customer of the bank, with personal bank accounts.", - "relationships": [ - { - "id": "3", - "tags": "Relationship", - "sourceId": "1", - "destinationId": "2", - "description": "Views account balances, and makes payments using" - }, - { - "id": "23", - "tags": "Relationship", - "sourceId": "1", - "destinationId": "17", - "description": "Views account balances, and makes payments using" - }, - { - "id": "22", - "tags": "Relationship", - "sourceId": "1", - "destinationId": "19", - "description": "Visits bigbank.com/ib using", - "technology": "HTTPS" - }, - { - "id": "24", - "tags": "Relationship", - "sourceId": "1", - "destinationId": "18", - "description": "Views account balances, and makes payments using" - }, - { - "id": "11", - "tags": "Relationship", - "sourceId": "1", - "destinationId": "9", - "description": "Withdraws cash using" - }, - { - "id": "14", - "tags": "Relationship,Synchronous", - "sourceId": "1", - "destinationId": "12", - "description": "Asks questions to", - "technology": "Telephone", - "interactionStyle": "Synchronous" - } - ], - "location": "External" - } - ], - "softwareSystems": [ - { - "id": "9", - "tags": "Element,Software System,Existing System", - "name": "ATM", - "description": "Allows customers to withdraw cash.", - "relationships": [ - { - "id": "10", - "tags": "Relationship", - "sourceId": "9", - "destinationId": "4", - "description": "Uses" - } - ], - "location": "Internal" + "sourceId" : "12", + "destinationId" : "15", + "description" : "Uses" + } ], + "technology" : "Spring MVC Rest Controller", + "documentation" : { } + }, { + "id" : "13", + "tags" : "Element,Component", + "properties" : { + "structurizr.dsl.identifier" : "accountssummarycontroller" + }, + "name" : "Accounts Summary Controller", + "description" : "Provides customers with a summary of their bank accounts.", + "relationships" : [ { + "id" : "41", + "tags" : "Relationship", + "properties" : { + "structurizr.dsl.identifier" : "9920b188-70d1-4f83-b51d-a2f7eb4c6ffc" }, - { - "id": "6", - "tags": "Element,Software System,Existing System", - "name": "E-mail System", - "description": "The internal Microsoft Exchange e-mail system.", - "relationships": [ - { - "id": "8", - "tags": "Relationship", - "sourceId": "6", - "destinationId": "1", - "description": "Sends e-mails to" - } - ], - "location": "Internal" + "sourceId" : "13", + "destinationId" : "16", + "description" : "Uses" + } ], + "technology" : "Spring MVC Rest Controller", + "documentation" : { } + }, { + "id" : "15", + "tags" : "Element,Component", + "properties" : { + "structurizr.dsl.identifier" : "securitycomponent" + }, + "name" : "Security Component", + "description" : "Provides functionality related to signing in, changing passwords, etc.", + "relationships" : [ { + "id" : "44", + "tags" : "Relationship", + "properties" : { + "structurizr.dsl.identifier" : "67d3a4e5-1e3f-4046-a121-c0a342a26d25" }, - { - "id": "2", - "tags": "Element,Software System", - "name": "Internet Banking System", - "description": "Allows customers to view information about their bank accounts, and make payments.", - "relationships": [ - { - "id": "5", - "tags": "Relationship", - "sourceId": "2", - "destinationId": "4", - "description": "Gets account information from, and makes payments using" - }, - { - "id": "7", - "tags": "Relationship", - "sourceId": "2", - "destinationId": "6", - "description": "Sends e-mail using" - } - ], - "location": "Internal", - "containers": [ - { - "id": "20", - "tags": "Element,Container", - "name": "API Application", - "description": "Provides Internet banking functionality via a JSON/HTTPS API.", - "relationships": [ - { - "id": "26", - "tags": "Relationship", - "sourceId": "20", - "destinationId": "21", - "description": "Reads from and writes to", - "technology": "JDBC" - }, - { - "id": "27", - "tags": "Relationship", - "sourceId": "20", - "destinationId": "4", - "description": "Makes API calls to", - "technology": "XML/HTTPS" - }, - { - "id": "28", - "tags": "Relationship", - "sourceId": "20", - "destinationId": "6", - "description": "Sends e-mail using", - "technology": "SMTP" - } - ], - "technology": "Java and Spring MVC", - "components": [ - { - "id": "30", - "tags": "Element,Component", - "name": "Accounts Summary Controller", - "description": "Provides customers with a summary of their bank accounts.", - "relationships": [ - { - "id": "44", - "tags": "Relationship", - "sourceId": "30", - "destinationId": "33", - "description": "Uses" - } - ], - "technology": "Spring MVC Rest Controller", - "size": 0 - }, - { - "id": "34", - "tags": "Element,Component", - "name": "E-mail Component", - "description": "Sends e-mails to users.", - "relationships": [ - { - "id": "49", - "tags": "Relationship", - "sourceId": "34", - "destinationId": "6", - "description": "Sends e-mail using" - } - ], - "technology": "Spring Bean", - "size": 0 - }, - { - "id": "33", - "tags": "Element,Component", - "name": "Mainframe Banking System Facade", - "description": "A facade onto the mainframe banking system.", - "relationships": [ - { - "id": "48", - "tags": "Relationship", - "sourceId": "33", - "destinationId": "4", - "description": "Uses", - "technology": "XML/HTTPS" - } - ], - "technology": "Spring Bean", - "size": 0 - }, - { - "id": "31", - "tags": "Element,Component", - "name": "Reset Password Controller", - "description": "Allows users to reset their passwords with a single use URL.", - "relationships": [ - { - "id": "45", - "tags": "Relationship", - "sourceId": "31", - "destinationId": "32", - "description": "Uses" - }, - { - "id": "46", - "tags": "Relationship", - "sourceId": "31", - "destinationId": "34", - "description": "Uses" - } - ], - "technology": "Spring MVC Rest Controller", - "size": 0 - }, - { - "id": "32", - "tags": "Element,Component", - "name": "Security Component", - "description": "Provides functionality related to signing in, changing passwords, etc.", - "relationships": [ - { - "id": "47", - "tags": "Relationship", - "sourceId": "32", - "destinationId": "21", - "description": "Reads from and writes to", - "technology": "JDBC" - } - ], - "technology": "Spring Bean", - "size": 0 - }, - { - "id": "29", - "tags": "Element,Component", - "name": "Sign In Controller", - "description": "Allows users to sign in to the Internet Banking System.", - "relationships": [ - { - "id": "43", - "tags": "Relationship", - "sourceId": "29", - "destinationId": "32", - "description": "Uses" - } - ], - "technology": "Spring MVC Rest Controller", - "size": 0 - } - ] - }, - { - "id": "21", - "tags": "Element,Container,Database", - "name": "Database", - "description": "Stores user registration information, hashed authentication credentials, access logs, etc.", - "technology": "Oracle Database Schema" - }, - { - "id": "18", - "tags": "Element,Container,Mobile App", - "name": "Mobile App", - "description": "Provides a limited subset of the Internet banking functionality to customers via their mobile device.", - "relationships": [ - { - "id": "39", - "tags": "Relationship", - "sourceId": "18", - "destinationId": "29", - "description": "Makes API calls to", - "technology": "JSON/HTTPS" - }, - { - "id": "41", - "tags": "Relationship", - "sourceId": "18", - "destinationId": "31", - "description": "Makes API calls to", - "technology": "JSON/HTTPS" - }, - { - "id": "42", - "tags": "Relationship", - "sourceId": "18", - "destinationId": "30", - "description": "Makes API calls to", - "technology": "JSON/HTTPS" - }, - { - "id": "40", - "tags": "Relationship", - "sourceId": "18", - "destinationId": "20", - "description": "Makes API calls to", - "technology": "JSON/HTTPS" - } - ], - "technology": "Xamarin" - }, - { - "id": "17", - "tags": "Element,Container,Web Browser", - "name": "Single-Page Application", - "description": "Provides all of the Internet banking functionality to customers via their web browser.", - "relationships": [ - { - "id": "38", - "tags": "Relationship", - "sourceId": "17", - "destinationId": "30", - "description": "Makes API calls to", - "technology": "JSON/HTTPS" - }, - { - "id": "36", - "tags": "Relationship", - "sourceId": "17", - "destinationId": "20", - "description": "Makes API calls to", - "technology": "JSON/HTTPS" - }, - { - "id": "37", - "tags": "Relationship", - "sourceId": "17", - "destinationId": "31", - "description": "Makes API calls to", - "technology": "JSON/HTTPS" - }, - { - "id": "35", - "tags": "Relationship", - "sourceId": "17", - "destinationId": "29", - "description": "Makes API calls to", - "technology": "JSON/HTTPS" - } - ], - "technology": "JavaScript and Angular" - }, - { - "id": "19", - "tags": "Element,Container", - "name": "Web Application", - "description": "Delivers the static content and the Internet banking single page application.", - "relationships": [ - { - "id": "25", - "tags": "Relationship", - "sourceId": "19", - "destinationId": "17", - "description": "Delivers to the customer's web browser" - } - ], - "technology": "Java and Spring MVC" - } - ] + "sourceId" : "15", + "destinationId" : "18", + "description" : "Reads from and writes to", + "technology" : "SQL/TCP" + } ], + "technology" : "Spring Bean", + "documentation" : { } + }, { + "id" : "14", + "tags" : "Element,Component", + "properties" : { + "structurizr.dsl.identifier" : "resetpasswordcontroller" + }, + "name" : "Reset Password Controller", + "description" : "Allows users to reset their passwords with a single use URL.", + "relationships" : [ { + "id" : "42", + "tags" : "Relationship", + "properties" : { + "structurizr.dsl.identifier" : "bd04aed2-60d9-45f9-a75a-bd570b5a9cc4" }, - { - "id": "4", - "tags": "Element,Software System,Existing System", - "name": "Mainframe Banking System", - "description": "Stores all of the core banking information about customers, accounts, transactions, etc.", - "location": "Internal" - } - ], - "deploymentNodes": [ - { - "id": "55", - "tags": "Element,Deployment Node", - "name": "Big Bank plc", - "environment": "Development", - "technology": "Big Bank plc data center", - "instances": 1, - "children": [ - { - "id": "56", - "tags": "Element,Deployment Node", - "name": "bigbank-dev001", - "environment": "Development", - "instances": 1, - "softwareSystemInstances": [ - { - "id": "57", - "tags": "Software System Instance", - "environment": "Development", - "instanceId": 1, - "softwareSystemId": "4", - "properties": {} - } - ], - "children": [], - "containerInstances": [], - "infrastructureNodes": [] - } - ], - "softwareSystemInstances": [], - "containerInstances": [], - "infrastructureNodes": [] + "sourceId" : "14", + "destinationId" : "15", + "description" : "Uses" + }, { + "id" : "43", + "tags" : "Relationship", + "properties" : { + "structurizr.dsl.identifier" : "aa43b36b-ddc2-4aad-a021-d54f79abd36b" }, - { - "id": "50", - "tags": "Element,Deployment Node", - "name": "Developer Laptop", - "description": "A developer laptop.", - "environment": "Development", - "technology": "Microsoft Windows 10 or Apple macOS", - "instances": 1, - "children": [ - { - "id": "59", - "tags": "Element,Deployment Node", - "name": "Docker Container - Database Server", - "description": "A Docker container.", - "environment": "Development", - "technology": "Docker", - "instances": 1, - "children": [ - { - "id": "60", - "tags": "Element,Deployment Node", - "name": "Database Server", - "description": "A development database.", - "environment": "Development", - "technology": "Oracle 12c", - "instances": 1, - "containerInstances": [ - { - "id": "61", - "tags": "Container Instance", - "environment": "Development", - "instanceId": 1, - "containerId": "21", - "properties": {} - } - ], - "children": [], - "softwareSystemInstances": [], - "infrastructureNodes": [] - } - ], - "softwareSystemInstances": [], - "containerInstances": [], - "infrastructureNodes": [] - }, - { - "id": "51", - "tags": "Element,Deployment Node", - "name": "Docker Container - Web Server", - "description": "A Docker container.", - "environment": "Development", - "technology": "Docker", - "instances": 1, - "children": [ - { - "id": "52", - "tags": "Element,Deployment Node", - "properties": { - "Java Version": "8", - "Xms": "1024M", - "Xmx": "512M" - }, - "name": "Apache Tomcat", - "description": "An open source Java EE web server.", - "environment": "Development", - "technology": "Apache Tomcat 8.x", - "instances": 1, - "containerInstances": [ - { - "id": "54", - "tags": "Container Instance", - "relationships": [ - { - "id": "62", - "sourceId": "54", - "destinationId": "61", - "description": "Reads from and writes to", - "technology": "JDBC", - "linkedRelationshipId": "26" - }, - { - "id": "58", - "sourceId": "54", - "destinationId": "57", - "description": "Makes API calls to", - "technology": "XML/HTTPS", - "linkedRelationshipId": "27" - } - ], - "environment": "Development", - "instanceId": 1, - "containerId": "20", - "properties": {} - }, - { - "id": "53", - "tags": "Container Instance", - "relationships": [ - { - "id": "66", - "sourceId": "53", - "destinationId": "64", - "description": "Delivers to the customer's web browser", - "linkedRelationshipId": "25" - } - ], - "environment": "Development", - "instanceId": 1, - "containerId": "19", - "properties": {} - } - ], - "children": [], - "softwareSystemInstances": [], - "infrastructureNodes": [] - } - ], - "softwareSystemInstances": [], - "containerInstances": [], - "infrastructureNodes": [] - }, - { - "id": "63", - "tags": "Element,Deployment Node", - "name": "Web Browser", - "environment": "Development", - "technology": "Chrome, Firefox, Safari, or Edge", - "instances": 1, - "containerInstances": [ - { - "id": "64", - "tags": "Container Instance", - "relationships": [ - { - "id": "65", - "sourceId": "64", - "destinationId": "54", - "description": "Makes API calls to", - "technology": "JSON/HTTPS", - "linkedRelationshipId": "36" - } - ], - "environment": "Development", - "instanceId": 1, - "containerId": "17", - "properties": {} - } - ], - "children": [], - "softwareSystemInstances": [], - "infrastructureNodes": [] - } - ], - "softwareSystemInstances": [], - "containerInstances": [], - "infrastructureNodes": [] + "sourceId" : "14", + "destinationId" : "17", + "description" : "Uses" + } ], + "technology" : "Spring MVC Rest Controller", + "documentation" : { } + }, { + "id" : "17", + "tags" : "Element,Component", + "properties" : { + "structurizr.dsl.identifier" : "emailcomponent" + }, + "name" : "E-mail Component", + "description" : "Sends e-mails to users.", + "relationships" : [ { + "id" : "48", + "tags" : "Relationship", + "properties" : { + "structurizr.dsl.identifier" : "3c022388-9eb7-4e47-b524-f480fc361897" }, - { - "id": "72", - "tags": "Element,Deployment Node", - "name": "Big Bank plc", - "environment": "Live", - "technology": "Big Bank plc data center", - "instances": 1, - "children": [ - { - "id": "79", - "tags": "Element,Deployment Node", - "properties": { - "Location": "London and Reading" - }, - "name": "bigbank-api***", - "description": "A web server residing in the web server farm, accessed via F5 BIG-IP LTMs.", - "environment": "Live", - "technology": "Ubuntu 16.04 LTS", - "instances": 8, - "children": [ - { - "id": "80", - "tags": "Element,Deployment Node", - "properties": { - "Java Version": "8", - "Xms": "1024M", - "Xmx": "512M" - }, - "name": "Apache Tomcat", - "description": "An open source Java EE web server.", - "environment": "Live", - "technology": "Apache Tomcat 8.x", - "instances": 1, - "containerInstances": [ - { - "id": "81", - "tags": "Container Instance", - "relationships": [ - { - "id": "88", - "sourceId": "81", - "destinationId": "87", - "description": "Reads from and writes to", - "technology": "JDBC", - "linkedRelationshipId": "26" - }, - { - "id": "84", - "sourceId": "81", - "destinationId": "74", - "description": "Makes API calls to", - "technology": "XML/HTTPS", - "linkedRelationshipId": "27" - }, - { - "id": "92", - "tags": "Failover", - "sourceId": "81", - "destinationId": "91", - "description": "Reads from and writes to", - "technology": "JDBC", - "linkedRelationshipId": "26" - } - ], - "environment": "Live", - "instanceId": 1, - "containerId": "20", - "properties": {} - } - ], - "children": [], - "softwareSystemInstances": [], - "infrastructureNodes": [] - } - ], - "softwareSystemInstances": [], - "containerInstances": [], - "infrastructureNodes": [] - }, - { - "id": "85", - "tags": "Element,Deployment Node", - "properties": { - "Location": "London" - }, - "name": "bigbank-db01", - "description": "The primary database server.", - "environment": "Live", - "technology": "Ubuntu 16.04 LTS", - "instances": 1, - "children": [ - { - "id": "86", - "tags": "Element,Deployment Node", - "name": "Oracle - Primary", - "description": "The primary, live database server.", - "relationships": [ - { - "id": "93", - "tags": "Relationship,Synchronous", - "sourceId": "86", - "destinationId": "90", - "description": "Replicates data to", - "interactionStyle": "Synchronous" - } - ], - "environment": "Live", - "technology": "Oracle 12c", - "instances": 1, - "containerInstances": [ - { - "id": "87", - "tags": "Container Instance", - "environment": "Live", - "instanceId": 1, - "containerId": "21", - "properties": {} - } - ], - "children": [], - "softwareSystemInstances": [], - "infrastructureNodes": [] - } - ], - "softwareSystemInstances": [], - "containerInstances": [], - "infrastructureNodes": [] - }, - { - "id": "89", - "tags": "Element,Deployment Node,Failover", - "properties": { - "Location": "Reading" - }, - "name": "bigbank-db02", - "description": "The secondary database server.", - "environment": "Live", - "technology": "Ubuntu 16.04 LTS", - "instances": 1, - "children": [ - { - "id": "90", - "tags": "Element,Deployment Node,Failover", - "name": "Oracle - Secondary", - "description": "A secondary, standby database server, used for failover purposes only.", - "environment": "Live", - "technology": "Oracle 12c", - "instances": 1, - "containerInstances": [ - { - "id": "91", - "tags": "Container Instance,Failover", - "environment": "Live", - "instanceId": 2, - "containerId": "21", - "properties": {} - } - ], - "children": [], - "softwareSystemInstances": [], - "infrastructureNodes": [] - } - ], - "softwareSystemInstances": [], - "containerInstances": [], - "infrastructureNodes": [] - }, - { - "id": "73", - "tags": "Element,Deployment Node", - "name": "bigbank-prod001", - "environment": "Live", - "instances": 1, - "softwareSystemInstances": [ - { - "id": "74", - "tags": "Software System Instance", - "environment": "Live", - "instanceId": 1, - "softwareSystemId": "4", - "properties": {} - } - ], - "children": [], - "containerInstances": [], - "infrastructureNodes": [] - }, - { - "id": "75", - "tags": "Element,Deployment Node", - "properties": { - "Location": "London and Reading" - }, - "name": "bigbank-web***", - "description": "A web server residing in the web server farm, accessed via F5 BIG-IP LTMs.", - "environment": "Live", - "technology": "Ubuntu 16.04 LTS", - "instances": 4, - "children": [ - { - "id": "76", - "tags": "Element,Deployment Node", - "properties": { - "Java Version": "8", - "Xms": "1024M", - "Xmx": "512M" - }, - "name": "Apache Tomcat", - "description": "An open source Java EE web server.", - "environment": "Live", - "technology": "Apache Tomcat 8.x", - "instances": 1, - "containerInstances": [ - { - "id": "77", - "tags": "Container Instance", - "relationships": [ - { - "id": "78", - "sourceId": "77", - "destinationId": "71", - "description": "Delivers to the customer's web browser", - "linkedRelationshipId": "25" - } - ], - "environment": "Live", - "instanceId": 1, - "containerId": "19", - "properties": {} - } - ], - "children": [], - "softwareSystemInstances": [], - "infrastructureNodes": [] - } - ], - "softwareSystemInstances": [], - "containerInstances": [], - "infrastructureNodes": [] - } - ], - "softwareSystemInstances": [], - "containerInstances": [], - "infrastructureNodes": [] + "sourceId" : "17", + "destinationId" : "5", + "description" : "Sends e-mail using" + } ], + "technology" : "Spring Bean", + "documentation" : { } + } ], + "documentation" : { } + }, { + "id" : "8", + "tags" : "Element,Container,Web Browser", + "properties" : { + "structurizr.dsl.identifier" : "singlepageapplication" + }, + "name" : "Single-Page Application", + "description" : "Provides all of the Internet banking functionality to customers via their web browser.", + "relationships" : [ { + "id" : "32", + "tags" : "Relationship", + "properties" : { + "structurizr.dsl.identifier" : "adb4007f-e5e8-41b9-ac61-d027fac89cdb" + }, + "sourceId" : "8", + "destinationId" : "12", + "description" : "Makes API calls to", + "technology" : "JSON/HTTPS" + }, { + "id" : "33", + "sourceId" : "8", + "destinationId" : "11", + "description" : "Makes API calls to", + "technology" : "JSON/HTTPS", + "linkedRelationshipId" : "32" + }, { + "id" : "34", + "tags" : "Relationship", + "properties" : { + "structurizr.dsl.identifier" : "235c5bc1-8163-4415-abb2-9f24d7a03155" + }, + "sourceId" : "8", + "destinationId" : "13", + "description" : "Makes API calls to", + "technology" : "JSON/HTTPS" + }, { + "id" : "35", + "tags" : "Relationship", + "properties" : { + "structurizr.dsl.identifier" : "607c5ef3-186c-4f68-a4f1-3be26eb4591f" + }, + "sourceId" : "8", + "destinationId" : "14", + "description" : "Makes API calls to", + "technology" : "JSON/HTTPS" + } ], + "technology" : "JavaScript and Angular", + "documentation" : { } + } ], + "documentation" : { } + } ], + "deploymentNodes" : [ { + "id" : "50", + "tags" : "Element,Deployment Node", + "properties" : { + "structurizr.dsl.identifier" : "25239481-454a-4edd-bca5-7ee77ced54e1" + }, + "name" : "Developer Laptop", + "environment" : "Development", + "technology" : "Microsoft Windows 10 or Apple macOS", + "instances" : "1", + "children" : [ { + "id" : "53", + "tags" : "Element,Deployment Node", + "properties" : { + "structurizr.dsl.identifier" : "e8ce8a91-df97-4486-b2a0-dcd171cd2ee3" + }, + "name" : "Docker Container - Web Server", + "environment" : "Development", + "technology" : "Docker", + "instances" : "1", + "children" : [ { + "id" : "54", + "tags" : "Element,Deployment Node", + "properties" : { + "structurizr.dsl.identifier" : "af91db02-1a49-4a73-848a-ff230caf459b" + }, + "name" : "Apache Tomcat", + "environment" : "Development", + "technology" : "Apache Tomcat 8.x", + "instances" : "1", + "containerInstances" : [ { + "id" : "55", + "tags" : "Container Instance", + "properties" : { + "structurizr.dsl.identifier" : "developerwebapplicationinstance" }, - { - "id": "69", - "tags": "Element,Deployment Node", - "name": "Customer's computer", - "environment": "Live", - "technology": "Microsoft Windows or Apple macOS", - "instances": 1, - "children": [ - { - "id": "70", - "tags": "Element,Deployment Node", - "name": "Web Browser", - "environment": "Live", - "technology": "Chrome, Firefox, Safari, or Edge", - "instances": 1, - "containerInstances": [ - { - "id": "71", - "tags": "Container Instance", - "relationships": [ - { - "id": "83", - "sourceId": "71", - "destinationId": "81", - "description": "Makes API calls to", - "technology": "JSON/HTTPS", - "linkedRelationshipId": "36" - } - ], - "environment": "Live", - "instanceId": 1, - "containerId": "17", - "properties": {} - } - ], - "children": [], - "softwareSystemInstances": [], - "infrastructureNodes": [] - } - ], - "softwareSystemInstances": [], - "containerInstances": [], - "infrastructureNodes": [] + "relationships" : [ { + "id" : "56", + "sourceId" : "55", + "destinationId" : "52", + "description" : "Delivers to the customer's web browser", + "linkedRelationshipId" : "31" + } ], + "environment" : "Development", + "deploymentGroups" : [ "Default" ], + "instanceId" : 1, + "containerId" : "10" + }, { + "id" : "57", + "tags" : "Container Instance", + "properties" : { + "structurizr.dsl.identifier" : "developerapiapplicationinstance" }, - { - "id": "67", - "tags": "Element,Deployment Node", - "name": "Customer's mobile device", - "environment": "Live", - "technology": "Apple iOS or Android", - "instances": 1, - "containerInstances": [ - { - "id": "68", - "tags": "Container Instance", - "relationships": [ - { - "id": "82", - "sourceId": "68", - "destinationId": "81", - "description": "Makes API calls to", - "technology": "JSON/HTTPS", - "linkedRelationshipId": "40" - } - ], - "environment": "Live", - "instanceId": 1, - "containerId": "18", - "properties": {} - } - ], - "children": [], - "softwareSystemInstances": [], - "infrastructureNodes": [] - } - ] - }, - "documentation": { - "sections": [ - { - "elementId": "2", - "title": "Context", - "order": 1, - "format": "Markdown", - "content": "Here is some context about the Internet Banking System...\n![](embed:SystemLandscape)\n![](embed:SystemContext)\n### Internet Banking System\n...\n### Mainframe Banking System\n...\n" + "relationships" : [ { + "id" : "62", + "sourceId" : "57", + "destinationId" : "61", + "description" : "Reads from and writes to", + "technology" : "SQL/TCP", + "linkedRelationshipId" : "45" + }, { + "id" : "66", + "sourceId" : "57", + "destinationId" : "65", + "description" : "Makes API calls to", + "technology" : "XML/HTTPS", + "linkedRelationshipId" : "47" + } ], + "environment" : "Development", + "deploymentGroups" : [ "Default" ], + "instanceId" : 1, + "containerId" : "11" + } ] + } ] + }, { + "id" : "51", + "tags" : "Element,Deployment Node", + "properties" : { + "structurizr.dsl.identifier" : "b7da5a3d-2f5f-41f4-ab17-a81118c842ad" + }, + "name" : "Web Browser", + "environment" : "Development", + "technology" : "Chrome, Firefox, Safari, or Edge", + "instances" : "1", + "containerInstances" : [ { + "id" : "52", + "tags" : "Container Instance", + "properties" : { + "structurizr.dsl.identifier" : "developersinglepageapplicationinstance" + }, + "relationships" : [ { + "id" : "58", + "sourceId" : "52", + "destinationId" : "57", + "description" : "Makes API calls to", + "technology" : "JSON/HTTPS", + "linkedRelationshipId" : "33" + } ], + "environment" : "Development", + "deploymentGroups" : [ "Default" ], + "instanceId" : 1, + "containerId" : "8" + } ] + }, { + "id" : "59", + "tags" : "Element,Deployment Node", + "properties" : { + "structurizr.dsl.identifier" : "5f95573e-5028-40e6-9b55-b1402dc184b4" + }, + "name" : "Docker Container - Database Server", + "environment" : "Development", + "technology" : "Docker", + "instances" : "1", + "children" : [ { + "id" : "60", + "tags" : "Element,Deployment Node", + "properties" : { + "structurizr.dsl.identifier" : "562a146b-9830-4fef-ab67-cadbede1cabb" + }, + "name" : "Database Server", + "environment" : "Development", + "technology" : "Oracle 12c", + "instances" : "1", + "containerInstances" : [ { + "id" : "61", + "tags" : "Container Instance", + "properties" : { + "structurizr.dsl.identifier" : "developerdatabaseinstance" }, - { - "elementId": "19", - "title": "Components", - "order": 3, - "format": "Markdown", - "content": "Here is some information about the API Application...\n![](embed:Components)\n### Sign in process\nHere is some information about the Sign In Controller, including how the sign in process works...\n![](embed:SignIn)" + "environment" : "Development", + "deploymentGroups" : [ "Default" ], + "instanceId" : 1, + "containerId" : "18" + } ] + } ] + } ] + }, { + "id" : "63", + "tags" : "Element,Deployment Node,", + "properties" : { + "structurizr.dsl.identifier" : "77f85941-3884-4918-bfb5-a55a6d2d7448" + }, + "name" : "Big Bank plc", + "environment" : "Development", + "technology" : "Big Bank plc data center", + "instances" : "1", + "children" : [ { + "id" : "64", + "tags" : "Element,Deployment Node,", + "properties" : { + "structurizr.dsl.identifier" : "9e854be8-32f3-4d69-88fd-245871775b15" + }, + "name" : "bigbank-dev001", + "environment" : "Development", + "instances" : "1", + "softwareSystemInstances" : [ { + "id" : "65", + "tags" : "Software System Instance", + "properties" : { + "structurizr.dsl.identifier" : "437ac7c3-ebbb-44ce-8a60-80bef8b6fb6e" + }, + "environment" : "Development", + "deploymentGroups" : [ "Default" ], + "instanceId" : 1, + "softwareSystemId" : "4" + } ] + } ] + }, { + "id" : "67", + "tags" : "Element,Deployment Node", + "properties" : { + "structurizr.dsl.identifier" : "80be1ff8-fe6c-413b-aac6-9bb6cb9b85c7" + }, + "name" : "Customer's mobile device", + "environment" : "Live", + "technology" : "Apple iOS or Android", + "instances" : "1", + "containerInstances" : [ { + "id" : "68", + "tags" : "Container Instance", + "properties" : { + "structurizr.dsl.identifier" : "livemobileappinstance" + }, + "relationships" : [ { + "id" : "80", + "sourceId" : "68", + "destinationId" : "79", + "description" : "Makes API calls to", + "technology" : "JSON/HTTPS", + "linkedRelationshipId" : "37" + } ], + "environment" : "Live", + "deploymentGroups" : [ "Default" ], + "instanceId" : 1, + "containerId" : "9" + } ] + }, { + "id" : "69", + "tags" : "Element,Deployment Node", + "properties" : { + "structurizr.dsl.identifier" : "493075d0-96ce-4274-98e2-5327fc48aeb6" + }, + "name" : "Customer's computer", + "environment" : "Live", + "technology" : "Microsoft Windows or Apple macOS", + "instances" : "1", + "children" : [ { + "id" : "70", + "tags" : "Element,Deployment Node", + "properties" : { + "structurizr.dsl.identifier" : "45b6f442-c8bf-4923-8270-b9325afe0581" + }, + "name" : "Web Browser", + "environment" : "Live", + "technology" : "Chrome, Firefox, Safari, or Edge", + "instances" : "1", + "containerInstances" : [ { + "id" : "71", + "tags" : "Container Instance", + "properties" : { + "structurizr.dsl.identifier" : "livesinglepageapplicationinstance" + }, + "relationships" : [ { + "id" : "81", + "sourceId" : "71", + "destinationId" : "79", + "description" : "Makes API calls to", + "technology" : "JSON/HTTPS", + "linkedRelationshipId" : "33" + } ], + "environment" : "Live", + "deploymentGroups" : [ "Default" ], + "instanceId" : 1, + "containerId" : "8" + } ] + } ] + }, { + "id" : "72", + "tags" : "Element,Deployment Node", + "properties" : { + "structurizr.dsl.identifier" : "e7539131-ef26-4370-beda-31449e4c6b07" + }, + "name" : "Big Bank plc", + "environment" : "Live", + "technology" : "Big Bank plc data center", + "instances" : "1", + "children" : [ { + "id" : "86", + "tags" : "Element,Deployment Node,Failover", + "properties" : { + "structurizr.dsl.identifier" : "edeb7dc9-7738-4516-9c81-c879369b2ab5" + }, + "name" : "bigbank-db02", + "environment" : "Live", + "technology" : "Ubuntu 16.04 LTS", + "instances" : "1", + "children" : [ { + "id" : "87", + "tags" : "Element,Deployment Node,Failover", + "properties" : { + "structurizr.dsl.identifier" : "secondarydatabaseserver" + }, + "name" : "Oracle - Secondary", + "environment" : "Live", + "technology" : "Oracle 12c", + "instances" : "1", + "containerInstances" : [ { + "id" : "88", + "tags" : "Container Instance", + "properties" : { + "structurizr.dsl.identifier" : "livesecondarydatabaseinstance" }, - { - "elementId": "2", - "title": "Development Environment", - "order": 4, - "format": "AsciiDoc", - "content": "Here is some information about how to set up a development environment for the Internet Banking System...\nimage::embed:DevelopmentDeployment[]" + "environment" : "Live", + "deploymentGroups" : [ "Default" ], + "instanceId" : 1, + "containerId" : "18" + } ] + } ] + }, { + "id" : "82", + "tags" : "Element,Deployment Node", + "properties" : { + "structurizr.dsl.identifier" : "7ec5cdd8-2cfe-46f6-8298-575bf81c2463" + }, + "name" : "bigbank-db01", + "environment" : "Live", + "technology" : "Ubuntu 16.04 LTS", + "instances" : "1", + "children" : [ { + "id" : "83", + "tags" : "Element,Deployment Node", + "properties" : { + "structurizr.dsl.identifier" : "primarydatabaseserver" + }, + "name" : "Oracle - Primary", + "relationships" : [ { + "id" : "93", + "tags" : "Relationship", + "properties" : { + "structurizr.dsl.identifier" : "982513db-e0af-4cde-a468-b98ff002d363" }, - { - "elementId": "2", - "title": "Containers", - "order": 2, - "format": "Markdown", - "content": "Here is some information about the containers within the Internet Banking System...\n![](embed:Containers)\n### Web Application\n...\n### Database\n...\n" + "sourceId" : "83", + "destinationId" : "87", + "description" : "Replicates data to" + } ], + "environment" : "Live", + "technology" : "Oracle 12c", + "instances" : "1", + "containerInstances" : [ { + "id" : "84", + "tags" : "Container Instance", + "properties" : { + "structurizr.dsl.identifier" : "liveprimarydatabaseinstance" }, - { - "elementId": "2", - "title": "Deployment", - "order": 5, - "format": "AsciiDoc", - "content": "Here is some information about the live deployment environment for the Internet Banking System...\nimage::embed:LiveDeployment[]" - } - ], - "template": { - "name": "Software Guidebook", - "author": "Simon Brown", - "url": "https://leanpub.com/visualising-software-architecture" + "environment" : "Live", + "deploymentGroups" : [ "Default" ], + "instanceId" : 1, + "containerId" : "18" + } ] + } ] + }, { + "id" : "77", + "tags" : "Element,Deployment Node,", + "properties" : { + "structurizr.dsl.identifier" : "291edaeb-88f1-4c8e-9a26-54d0c113b1ae" }, - "decisions": [], - "images": [] - }, - "views": { - "systemLandscapeViews": [ - { - "description": "The system landscape diagram for Big Bank plc.", - "key": "SystemLandscape", - "paperSize": "A5_Landscape", - "animations": [ - { - "order": 1, - "elements": [ - "1", - "2", - "4", - "6" - ], - "relationships": [ - "3", - "5", - "7", - "8" - ] - }, - { - "order": 2, - "elements": [ - "9" - ], - "relationships": [ - "11", - "10" - ] - }, - { - "order": 3, - "elements": [ - "12", - "15" - ], - "relationships": [ - "13", - "14", - "16" - ] - } - ], - "enterpriseBoundaryVisible": true, - "elements": [ - { - "id": "1", - "x": 87, - "y": 643 - }, - { - "id": "12", - "x": 1947, - "y": 36 - }, - { - "id": "2", - "x": 1012, - "y": 813 - }, - { - "id": "4", - "x": 1922, - "y": 693 - }, - { - "id": "15", - "x": 1947, - "y": 1241 - }, - { - "id": "6", - "x": 1012, - "y": 1326 - }, - { - "id": "9", - "x": 1012, - "y": 301 - } - ], - "relationships": [ - { - "id": "16" - }, - { - "id": "3" - }, - { - "id": "14", - "vertices": [ - { - "x": 285, - "y": 240 - } - ] - }, - { - "id": "5" - }, - { - "id": "13" - }, - { - "id": "11" - }, - { - "id": "7" - }, - { - "id": "8" - }, - { - "id": "10" - } - ] - } - ], - "systemContextViews": [ - { - "softwareSystemId": "2", - "description": "The system context diagram for the Internet Banking System.", - "key": "SystemContext", - "paperSize": "A5_Landscape", - "animations": [ - { - "order": 1, - "elements": [ - "2" - ], - "relationships": [] - }, - { - "order": 2, - "elements": [ - "1" - ], - "relationships": [ - "3" - ] - }, - { - "order": 3, - "elements": [ - "4" - ], - "relationships": [ - "5" - ] - }, - { - "order": 4, - "elements": [ - "6" - ], - "relationships": [ - "7", - "8" - ] - } - ], - "enterpriseBoundaryVisible": false, - "elements": [ - { - "id": "1", - "x": 632, - "y": 69 - }, - { - "id": "2", - "x": 607, - "y": 714 - }, - { - "id": "4", - "x": 607, - "y": 1259 - }, - { - "id": "6", - "x": 1422, - "y": 714 - } - ], - "relationships": [ - { - "id": "3" - }, - { - "id": "5" - }, - { - "id": "7" - }, - { - "id": "8" - } - ] - } - ], - "containerViews": [ - { - "softwareSystemId": "2", - "description": "The container diagram for the Internet Banking System.", - "key": "Containers", - "paperSize": "A5_Landscape", - "animations": [ - { - "order": 1, - "elements": [ - "1", - "4", - "6" - ], - "relationships": [ - "8" - ] - }, - { - "order": 2, - "elements": [ - "19" - ], - "relationships": [ - "22" - ] - }, - { - "order": 3, - "elements": [ - "17" - ], - "relationships": [ - "23", - "25" - ] - }, - { - "order": 4, - "elements": [ - "18" - ], - "relationships": [ - "24" - ] - }, - { - "order": 5, - "elements": [ - "20" - ], - "relationships": [ - "36", - "27", - "28", - "40" - ] - }, - { - "order": 6, - "elements": [ - "21" - ], - "relationships": [ - "26" - ] - } - ], - "externalSoftwareSystemBoundariesVisible": false, - "elements": [ - { - "id": "1", - "x": 1056, - "y": 24 - }, - { - "id": "4", - "x": 2012, - "y": 1214 - }, - { - "id": "17", - "x": 780, - "y": 664 - }, - { - "id": "6", - "x": 2012, - "y": 664 - }, - { - "id": "18", - "x": 1283, - "y": 664 - }, - { - "id": "19", - "x": 37, - "y": 664 - }, - { - "id": "20", - "x": 1031, - "y": 1214 - }, - { - "id": "21", - "x": 37, - "y": 1214 - } - ], - "relationships": [ - { - "id": "28" - }, - { - "id": "27" - }, - { - "id": "26" - }, - { - "id": "25" - }, - { - "id": "36" - }, - { - "id": "40" - }, - { - "id": "24" - }, - { - "id": "23" - }, - { - "id": "22" - }, - { - "id": "8" - } - ] - } - ], - "componentViews": [ - { - "description": "The component diagram for the API Application.", - "key": "Components", - "paperSize": "A5_Landscape", - "animations": [ - { - "order": 1, - "elements": [ - "4", - "17", - "6", - "18", - "21" - ], - "relationships": [] - }, - { - "order": 2, - "elements": [ - "29", - "32" - ], - "relationships": [ - "35", - "47", - "39", - "43" - ] - }, - { - "order": 3, - "elements": [ - "33", - "30" - ], - "relationships": [ - "44", - "48", - "38", - "42" - ] - }, - { - "order": 4, - "elements": [ - "34", - "31" - ], - "relationships": [ - "45", - "46", - "37", - "49", - "41" - ] - } - ], - "containerId": "20", - "externalContainerBoundariesVisible": false, - "elements": [ - { - "id": "33", - "x": 1925, - "y": 817 - }, - { - "id": "34", - "x": 1015, - "y": 817 - }, - { - "id": "4", - "x": 1925, - "y": 1307 - }, - { - "id": "17", - "x": 560, - "y": 10 - }, - { - "id": "6", - "x": 1015, - "y": 1307 - }, - { - "id": "18", - "x": 1470, - "y": 11 - }, - { - "id": "29", - "x": 105, - "y": 436 - }, - { - "id": "30", - "x": 1925, - "y": 436 - }, - { - "id": "31", - "x": 1015, - "y": 436 - }, - { - "id": "21", - "x": 105, - "y": 1307 - }, - { - "id": "32", - "x": 105, - "y": 817 - } - ], - "relationships": [ - { - "id": "41", - "position": 40 - }, - { - "id": "42", - "position": 40 - }, - { - "id": "43", - "position": 55 - }, - { - "id": "37", - "position": 45 - }, - { - "id": "35", - "position": 35 - }, - { - "id": "44", - "position": 50 - }, - { - "id": "45" - }, - { - "id": "46" - }, - { - "id": "47", - "position": 60 - }, - { - "id": "48" - }, - { - "id": "38", - "position": 85 - }, - { - "id": "49" - }, - { - "id": "39", - "position": 85 - } - ] - } - ], - "dynamicViews": [ - { - "description": "Summarises how the sign in feature works in the single-page application.", - "key": "SignIn", - "paperSize": "A5_Landscape", - "elementId": "20", - "relationships": [ - { - "id": "35", - "description": "Submits credentials to", - "order": "1", - "response": false, - "vertices": [ - { - "x": 1238, - "y": 236 - } - ], - "routing": "Curved", - "position": 50 - }, - { - "id": "43", - "description": "Validates credentials using", - "order": "2", - "response": false, - "vertices": [ - { - "x": 2065, - "y": 845 - } - ], - "routing": "Curved" - }, - { - "id": "47", - "description": "select * from users where username = ?", - "order": "3", - "response": false, - "vertices": [ - { - "x": 1218, - "y": 1416 - } - ], - "routing": "Curved" - }, - { - "id": "47", - "description": "Returns user data to", - "order": "4", - "response": true, - "vertices": [ - { - "x": 1240, - "y": 1220 - } - ], - "routing": "Curved" - }, - { - "id": "43", - "description": "Returns true if the hashed password matches", - "order": "5", - "response": true, - "vertices": [ - { - "x": 1828, - "y": 841 - } - ], - "routing": "Curved" - }, - { - "id": "35", - "description": "Sends back an authentication token to", - "order": "6", - "response": true, - "vertices": [ - { - "x": 1210, - "y": 450 - } - ], - "routing": "Curved" - } - ], - "elements": [ - { - "id": "17", - "x": 290, - "y": 192 - }, - { - "id": "29", - "x": 1720, - "y": 192 - }, - { - "id": "32", - "x": 1720, - "y": 1182 - }, - { - "id": "21", - "x": 290, - "y": 1182 - } - ] - } - ], - "deploymentViews": [ - { - "softwareSystemId": "2", - "description": "An example live deployment scenario for the Internet Banking System.", - "key": "LiveDeployment", - "paperSize": "A4_Landscape", - "environment": "Live", - "animations": [ - { - "order": 1, - "elements": [ - "69", - "70", - "71" - ] - }, - { - "order": 2, - "elements": [ - "67", - "68" - ] - }, - { - "order": 3, - "elements": [ - "77", - "79", - "80", - "81", - "72", - "75", - "76" - ], - "relationships": [ - "78", - "82", - "83" - ] - }, - { - "order": 4, - "elements": [ - "85", - "86", - "87" - ], - "relationships": [ - "88" - ] - }, - { - "order": 5, - "elements": [ - "89", - "90", - "91" - ], - "relationships": [ - "92", - "93" - ] - }, - { - "order": 6, - "elements": [ - "74" - ], - "relationships": [ - "84" - ] - } - ], - "elements": [ - { - "id": "77", - "x": 1504, - "y": 184 - }, - { - "id": "89", - "x": 0, - "y": 0 - }, - { - "id": "67", - "x": 0, - "y": 0 - }, - { - "id": "79", - "x": 0, - "y": 0 - }, - { - "id": "68", - "x": 424, - "y": 1071 - }, - { - "id": "69", - "x": 0, - "y": 0 - }, - { - "id": "90", - "x": 0, - "y": 0 - }, - { - "id": "91", - "x": 2584, - "y": 184 - }, - { - "id": "80", - "x": 0, - "y": 0 - }, - { - "id": "81", - "x": 1504, - "y": 1071 - }, - { - "id": "70", - "x": 0, - "y": 0 - }, - { - "id": "71", - "x": 424, - "y": 184 - }, - { - "id": "72", - "x": 0, - "y": 0 - }, - { - "id": "73", - "x": 0, - "y": 0 - }, - { - "id": "74", - "x": 2584, - "y": 1959 - }, - { - "id": "85", - "x": 0, - "y": 0 - }, - { - "id": "86", - "x": 0, - "y": 0 - }, - { - "id": "75", - "x": 0, - "y": 0 - }, - { - "id": "87", - "x": 2584, - "y": 1071 - }, - { - "id": "76", - "x": 0, - "y": 0 - } - ], - "relationships": [ - { - "id": "93" - }, - { - "id": "82" - }, - { - "id": "83" - }, - { - "id": "92" - }, - { - "id": "84" - }, - { - "id": "78" - }, - { - "id": "88" - } - ] + "name" : "bigbank-api***", + "environment" : "Live", + "technology" : "Ubuntu 16.04 LTS", + "instances" : "8", + "children" : [ { + "id" : "78", + "tags" : "Element,Deployment Node", + "properties" : { + "structurizr.dsl.identifier" : "a89ebe72-8ae6-43bf-8708-cc142a531f6b" + }, + "name" : "Apache Tomcat", + "environment" : "Live", + "technology" : "Apache Tomcat 8.x", + "instances" : "1", + "containerInstances" : [ { + "id" : "79", + "tags" : "Container Instance", + "properties" : { + "structurizr.dsl.identifier" : "liveapiapplicationinstance" }, - { - "softwareSystemId": "2", - "description": "An example development deployment scenario for the Internet Banking System.", - "key": "DevelopmentDeployment", - "paperSize": "A5_Landscape", - "environment": "Development", - "animations": [ - { - "order": 1, - "elements": [ - "50", - "63", - "64" - ] - }, - { - "order": 2, - "elements": [ - "51", - "52", - "53", - "54" - ], - "relationships": [ - "66", - "65" - ] - }, - { - "order": 3, - "elements": [ - "59", - "60", - "61" - ], - "relationships": [ - "62" - ] - }, - { - "order": 4, - "elements": [ - "57" - ], - "relationships": [ - "58" - ] - } - ], - "elements": [ - { - "id": "55", - "x": 0, - "y": 0 - }, - { - "id": "56", - "x": 0, - "y": 0 - }, - { - "id": "57", - "x": 1827, - "y": 1236 - }, - { - "id": "59", - "x": 0, - "y": 0 - }, - { - "id": "60", - "x": 0, - "y": 0 - }, - { - "id": "61", - "x": 1827, - "y": 176 - }, - { - "id": "50", - "x": 0, - "y": 0 - }, - { - "id": "51", - "x": 0, - "y": 0 - }, - { - "id": "63", - "x": 0, - "y": 0 - }, - { - "id": "52", - "x": 0, - "y": 0 - }, - { - "id": "64", - "x": 152, - "y": 346 - }, - { - "id": "53", - "x": 989, - "y": 176 - }, - { - "id": "54", - "x": 989, - "y": 516 - } - ], - "relationships": [ - { - "id": "62", - "position": 50 - }, - { - "id": "65" - }, - { - "id": "66" - }, - { - "id": "58" - } - ] - } - ], - "configuration": { - "branding": {}, - "styles": { - "elements": [ - { - "tag": "Software System", - "background": "#1168bd", - "color": "#ffffff" - }, - { - "tag": "Container", - "background": "#438dd5", - "color": "#ffffff" - }, - { - "tag": "Component", - "background": "#85bbf0", - "color": "#000000" - }, - { - "tag": "Person", - "background": "#08427b", - "color": "#ffffff", - "fontSize": 22, - "shape": "Person" - }, - { - "tag": "Existing System", - "background": "#999999", - "color": "#ffffff" - }, - { - "tag": "Bank Staff", - "background": "#999999", - "color": "#ffffff" - }, - { - "tag": "Web Browser", - "shape": "WebBrowser" - }, - { - "tag": "Mobile App", - "shape": "MobileDeviceLandscape" - }, - { - "tag": "Database", - "shape": "Cylinder" - }, - { - "tag": "Failover", - "opacity": 25 - } - ], - "relationships": [ - { - "tag": "Failover", - "position": 70, - "opacity": 25 - } - ] - }, - "terminology": {}, - "lastSavedView": "SignIn", - "themes": [] + "relationships" : [ { + "id" : "85", + "sourceId" : "79", + "destinationId" : "84", + "description" : "Reads from and writes to", + "technology" : "SQL/TCP", + "linkedRelationshipId" : "45" + }, { + "id" : "89", + "sourceId" : "79", + "destinationId" : "88", + "description" : "Reads from and writes to", + "technology" : "SQL/TCP", + "linkedRelationshipId" : "45" + }, { + "id" : "92", + "sourceId" : "79", + "destinationId" : "91", + "description" : "Makes API calls to", + "technology" : "XML/HTTPS", + "linkedRelationshipId" : "47" + } ], + "environment" : "Live", + "deploymentGroups" : [ "Default" ], + "instanceId" : 1, + "containerId" : "11" + } ] + } ] + }, { + "id" : "90", + "tags" : "Element,Deployment Node,", + "properties" : { + "structurizr.dsl.identifier" : "cdb15b14-58b4-4ca2-aa56-a162198908c8" + }, + "name" : "bigbank-prod001", + "environment" : "Live", + "instances" : "1", + "softwareSystemInstances" : [ { + "id" : "91", + "tags" : "Software System Instance", + "properties" : { + "structurizr.dsl.identifier" : "d3633a49-63a4-4495-80b3-e173391180bb" + }, + "environment" : "Live", + "deploymentGroups" : [ "Default" ], + "instanceId" : 1, + "softwareSystemId" : "4" + } ] + }, { + "id" : "73", + "tags" : "Element,Deployment Node,", + "properties" : { + "structurizr.dsl.identifier" : "f55b7219-d698-485d-91c1-ee303f3f5386" }, - "filteredViews": [] + "name" : "bigbank-web***", + "environment" : "Live", + "technology" : "Ubuntu 16.04 LTS", + "instances" : "4", + "children" : [ { + "id" : "74", + "tags" : "Element,Deployment Node", + "properties" : { + "structurizr.dsl.identifier" : "1d58ef82-2528-4ded-84b2-af35a7f18009" + }, + "name" : "Apache Tomcat", + "environment" : "Live", + "technology" : "Apache Tomcat 8.x", + "instances" : "1", + "containerInstances" : [ { + "id" : "75", + "tags" : "Container Instance", + "properties" : { + "structurizr.dsl.identifier" : "livewebapplicationinstance" + }, + "relationships" : [ { + "id" : "76", + "sourceId" : "75", + "destinationId" : "71", + "description" : "Delivers to the customer's web browser", + "linkedRelationshipId" : "31" + } ], + "environment" : "Live", + "deploymentGroups" : [ "Default" ], + "instanceId" : 1, + "containerId" : "10" + } ] + } ] + } ] + } ] + }, + "documentation" : { }, + "views" : { + "systemLandscapeViews" : [ { + "key" : "SystemLandscape", + "order" : 1, + "automaticLayout" : { + "implementation" : "Graphviz", + "rankDirection" : "TopBottom", + "rankSeparation" : 300, + "nodeSeparation" : 300, + "edgeSeparation" : 0, + "vertices" : false, + "applied" : false + }, + "enterpriseBoundaryVisible" : true, + "relationships" : [ { + "id" : "27" + }, { + "id" : "26" + }, { + "id" : "25" + }, { + "id" : "24" + }, { + "id" : "23" + }, { + "id" : "22" + }, { + "id" : "21" + }, { + "id" : "20" + }, { + "id" : "19" + } ], + "elements" : [ { + "id" : "1", + "x" : 0, + "y" : 0 + }, { + "id" : "2", + "x" : 0, + "y" : 0 + }, { + "id" : "3", + "x" : 0, + "y" : 0 + }, { + "id" : "4", + "x" : 0, + "y" : 0 + }, { + "id" : "5", + "x" : 0, + "y" : 0 + }, { + "id" : "6", + "x" : 0, + "y" : 0 + }, { + "id" : "7", + "x" : 0, + "y" : 0 + } ] + } ], + "systemContextViews" : [ { + "key" : "SystemContext", + "order" : 2, + "description" : "The system context diagram for the Internet Banking System.", + "properties" : { + "structurizr.groups" : "false" + }, + "softwareSystemId" : "7", + "automaticLayout" : { + "implementation" : "Graphviz", + "rankDirection" : "TopBottom", + "rankSeparation" : 300, + "nodeSeparation" : 300, + "edgeSeparation" : 0, + "vertices" : false, + "applied" : false + }, + "animations" : [ { + "order" : 1, + "elements" : [ "7" ] + }, { + "order" : 2, + "elements" : [ "1" ], + "relationships" : [ "19" ] + }, { + "order" : 3, + "elements" : [ "4" ], + "relationships" : [ "20" ] + }, { + "order" : 4, + "elements" : [ "5" ], + "relationships" : [ "22", "21" ] + } ], + "enterpriseBoundaryVisible" : true, + "relationships" : [ { + "id" : "22" + }, { + "id" : "21" + }, { + "id" : "20" + }, { + "id" : "19" + } ], + "elements" : [ { + "id" : "1", + "x" : 0, + "y" : 0 + }, { + "id" : "4", + "x" : 0, + "y" : 0 + }, { + "id" : "5", + "x" : 0, + "y" : 0 + }, { + "id" : "7", + "x" : 0, + "y" : 0 + } ] + } ], + "containerViews" : [ { + "key" : "Containers", + "order" : 3, + "description" : "The container diagram for the Internet Banking System.", + "softwareSystemId" : "7", + "automaticLayout" : { + "implementation" : "Graphviz", + "rankDirection" : "TopBottom", + "rankSeparation" : 300, + "nodeSeparation" : 300, + "edgeSeparation" : 0, + "vertices" : false, + "applied" : false + }, + "animations" : [ { + "order" : 1, + "elements" : [ "1", "4", "5" ], + "relationships" : [ "22" ] + }, { + "order" : 2, + "elements" : [ "10" ], + "relationships" : [ "28" ] + }, { + "order" : 3, + "elements" : [ "8" ], + "relationships" : [ "29", "31" ] + }, { + "order" : 4, + "elements" : [ "9" ], + "relationships" : [ "30" ] + }, { + "order" : 5, + "elements" : [ "11" ], + "relationships" : [ "33", "47", "37", "49" ] + }, { + "order" : 6, + "elements" : [ "18" ], + "relationships" : [ "45" ] + } ], + "externalSoftwareSystemBoundariesVisible" : true, + "relationships" : [ { + "id" : "29" + }, { + "id" : "28" + }, { + "id" : "37" + }, { + "id" : "22" + }, { + "id" : "33" + }, { + "id" : "45" + }, { + "id" : "31" + }, { + "id" : "30" + }, { + "id" : "47" + }, { + "id" : "49" + } ], + "elements" : [ { + "id" : "11", + "x" : 0, + "y" : 0 + }, { + "id" : "1", + "x" : 0, + "y" : 0 + }, { + "id" : "4", + "x" : 0, + "y" : 0 + }, { + "id" : "5", + "x" : 0, + "y" : 0 + }, { + "id" : "18", + "x" : 0, + "y" : 0 + }, { + "id" : "8", + "x" : 0, + "y" : 0 + }, { + "id" : "9", + "x" : 0, + "y" : 0 + }, { + "id" : "10", + "x" : 0, + "y" : 0 + } ] + } ], + "componentViews" : [ { + "key" : "Components", + "order" : 4, + "description" : "The component diagram for the API Application.", + "automaticLayout" : { + "implementation" : "Graphviz", + "rankDirection" : "TopBottom", + "rankSeparation" : 300, + "nodeSeparation" : 300, + "edgeSeparation" : 0, + "vertices" : false, + "applied" : false + }, + "animations" : [ { + "order" : 1, + "elements" : [ "4", "5", "18", "8", "9" ] + }, { + "order" : 2, + "elements" : [ "12", "15" ], + "relationships" : [ "44", "36", "40", "32" ] + }, { + "order" : 3, + "elements" : [ "13", "16" ], + "relationships" : [ "34", "46", "38", "41" ] + }, { + "order" : 4, + "elements" : [ "14", "17" ], + "relationships" : [ "35", "48", "39", "42", "43" ] + } ], + "containerId" : "11", + "externalContainerBoundariesVisible" : true, + "relationships" : [ { + "id" : "40" + }, { + "id" : "41" + }, { + "id" : "42" + }, { + "id" : "43" + }, { + "id" : "32" + }, { + "id" : "36" + }, { + "id" : "35" + }, { + "id" : "34" + }, { + "id" : "44" + }, { + "id" : "46" + }, { + "id" : "48" + }, { + "id" : "38" + }, { + "id" : "39" + } ], + "elements" : [ { + "id" : "12", + "x" : 0, + "y" : 0 + }, { + "id" : "13", + "x" : 0, + "y" : 0 + }, { + "id" : "14", + "x" : 0, + "y" : 0 + }, { + "id" : "4", + "x" : 0, + "y" : 0 + }, { + "id" : "15", + "x" : 0, + "y" : 0 + }, { + "id" : "16", + "x" : 0, + "y" : 0 + }, { + "id" : "5", + "x" : 0, + "y" : 0 + }, { + "id" : "17", + "x" : 0, + "y" : 0 + }, { + "id" : "18", + "x" : 0, + "y" : 0 + }, { + "id" : "8", + "x" : 0, + "y" : 0 + }, { + "id" : "9", + "x" : 0, + "y" : 0 + } ] + } ], + "dynamicViews" : [ { + "key" : "SignIn", + "order" : 6, + "description" : "Summarises how the sign in feature works in the single-page application.", + "automaticLayout" : { + "implementation" : "Graphviz", + "rankDirection" : "TopBottom", + "rankSeparation" : 300, + "nodeSeparation" : 300, + "edgeSeparation" : 0, + "vertices" : false, + "applied" : false + }, + "elementId" : "11", + "externalBoundariesVisible" : true, + "relationships" : [ { + "id" : "32", + "description" : "Submits credentials to", + "order" : "1", + "response" : false + }, { + "id" : "40", + "description" : "Validates credentials using", + "order" : "2", + "response" : false + }, { + "id" : "44", + "description" : "select * from users where username = ?", + "order" : "3", + "response" : false + }, { + "id" : "44", + "description" : "Returns user data to", + "order" : "4", + "response" : true + }, { + "id" : "40", + "description" : "Returns true if the hashed password matches", + "order" : "5", + "response" : true + }, { + "id" : "32", + "description" : "Sends back an authentication token to", + "order" : "6", + "response" : true + } ], + "elements" : [ { + "id" : "12", + "x" : 0, + "y" : 0 + }, { + "id" : "15", + "x" : 0, + "y" : 0 + }, { + "id" : "18", + "x" : 0, + "y" : 0 + }, { + "id" : "8", + "x" : 0, + "y" : 0 + } ] + } ], + "deploymentViews" : [ { + "key" : "LiveDeployment", + "order" : 8, + "description" : "An example live deployment scenario for the Internet Banking System.", + "softwareSystemId" : "7", + "automaticLayout" : { + "implementation" : "Graphviz", + "rankDirection" : "TopBottom", + "rankSeparation" : 300, + "nodeSeparation" : 300, + "edgeSeparation" : 0, + "vertices" : false, + "applied" : false + }, + "environment" : "Live", + "animations" : [ { + "order" : 1, + "elements" : [ "69", "70", "71" ] + }, { + "order" : 2, + "elements" : [ "67", "68" ] + }, { + "order" : 3, + "elements" : [ "77", "78", "79", "72", "73", "74", "75" ], + "relationships" : [ "80", "81", "76" ] + }, { + "order" : 4, + "elements" : [ "82", "83", "84" ], + "relationships" : [ "85" ] + }, { + "order" : 5, + "elements" : [ "88", "86", "87" ], + "relationships" : [ "89", "93" ] + } ], + "relationships" : [ { + "id" : "93" + }, { + "id" : "80" + }, { + "id" : "81" + }, { + "id" : "92" + }, { + "id" : "76" + }, { + "id" : "85" + }, { + "id" : "89" + } ], + "elements" : [ { + "id" : "88", + "x" : 0, + "y" : 0 + }, { + "id" : "77", + "x" : 0, + "y" : 0 + }, { + "id" : "67", + "x" : 0, + "y" : 0 + }, { + "id" : "78", + "x" : 0, + "y" : 0 + }, { + "id" : "68", + "x" : 0, + "y" : 0 + }, { + "id" : "79", + "x" : 0, + "y" : 0 + }, { + "id" : "69", + "x" : 0, + "y" : 0 + }, { + "id" : "90", + "x" : 0, + "y" : 0 + }, { + "id" : "91", + "x" : 0, + "y" : 0 + }, { + "id" : "70", + "x" : 0, + "y" : 0 + }, { + "id" : "71", + "x" : 0, + "y" : 0 + }, { + "id" : "82", + "x" : 0, + "y" : 0 + }, { + "id" : "83", + "x" : 0, + "y" : 0 + }, { + "id" : "72", + "x" : 0, + "y" : 0 + }, { + "id" : "84", + "x" : 0, + "y" : 0 + }, { + "id" : "73", + "x" : 0, + "y" : 0 + }, { + "id" : "74", + "x" : 0, + "y" : 0 + }, { + "id" : "86", + "x" : 0, + "y" : 0 + }, { + "id" : "75", + "x" : 0, + "y" : 0 + }, { + "id" : "87", + "x" : 0, + "y" : 0 + } ] + }, { + "key" : "DevelopmentDeployment", + "order" : 7, + "description" : "An example development deployment scenario for the Internet Banking System.", + "softwareSystemId" : "7", + "automaticLayout" : { + "implementation" : "Graphviz", + "rankDirection" : "TopBottom", + "rankSeparation" : 300, + "nodeSeparation" : 300, + "edgeSeparation" : 0, + "vertices" : false, + "applied" : false + }, + "environment" : "Development", + "animations" : [ { + "order" : 1, + "elements" : [ "50", "51", "52" ] + }, { + "order" : 2, + "elements" : [ "55", "57", "53", "54" ], + "relationships" : [ "56", "58" ] + }, { + "order" : 3, + "elements" : [ "59", "60", "61" ], + "relationships" : [ "62" ] + } ], + "relationships" : [ { + "id" : "62" + }, { + "id" : "56" + }, { + "id" : "66" + }, { + "id" : "58" + } ], + "elements" : [ { + "id" : "55", + "x" : 0, + "y" : 0 + }, { + "id" : "57", + "x" : 0, + "y" : 0 + }, { + "id" : "59", + "x" : 0, + "y" : 0 + }, { + "id" : "60", + "x" : 0, + "y" : 0 + }, { + "id" : "61", + "x" : 0, + "y" : 0 + }, { + "id" : "50", + "x" : 0, + "y" : 0 + }, { + "id" : "51", + "x" : 0, + "y" : 0 + }, { + "id" : "52", + "x" : 0, + "y" : 0 + }, { + "id" : "63", + "x" : 0, + "y" : 0 + }, { + "id" : "53", + "x" : 0, + "y" : 0 + }, { + "id" : "64", + "x" : 0, + "y" : 0 + }, { + "id" : "54", + "x" : 0, + "y" : 0 + }, { + "id" : "65", + "x" : 0, + "y" : 0 + } ] + } ], + "imageViews" : [ { + "key" : "MainframeBankingSystemFacade", + "order" : 5, + "title" : "[Code] Mainframe Banking System Facade", + "elementId" : "16", + "content" : "https://raw.githubusercontent.com/structurizr/examples/main/dsl/big-bank-plc/internet-banking-system/mainframe-banking-system-facade.png", + "contentType" : "image/png" + } ], + "configuration" : { + "branding" : { }, + "styles" : { + "elements" : [ { + "tag" : "Person", + "color" : "#ffffff", + "fontSize" : 22, + "shape" : "Person" + }, { + "tag" : "Customer", + "background" : "#08427b" + }, { + "tag" : "Bank Staff", + "background" : "#999999" + }, { + "tag" : "Software System", + "background" : "#1168bd", + "color" : "#ffffff" + }, { + "tag" : "Existing System", + "background" : "#999999", + "color" : "#ffffff" + }, { + "tag" : "Container", + "background" : "#438dd5", + "color" : "#ffffff" + }, { + "tag" : "Web Browser", + "shape" : "WebBrowser" + }, { + "tag" : "Mobile App", + "shape" : "MobileDeviceLandscape" + }, { + "tag" : "Database", + "shape" : "Cylinder" + }, { + "tag" : "Component", + "background" : "#85bbf0", + "color" : "#000000" + }, { + "tag" : "Failover", + "opacity" : 25 + } ] + }, + "terminology" : { } } + } } \ No newline at end of file diff --git a/structurizr-graphviz/src/test/java/com/structurizr/graphviz/DOTExporterTests.java b/structurizr-graphviz/src/test/java/com/structurizr/graphviz/DOTExporterTests.java index b3ffc35de..0f9214949 100644 --- a/structurizr-graphviz/src/test/java/com/structurizr/graphviz/DOTExporterTests.java +++ b/structurizr-graphviz/src/test/java/com/structurizr/graphviz/DOTExporterTests.java @@ -43,19 +43,16 @@ public void test_writeCustomView() { } @Test - public void test_writeSystemLandscapeViewWithNoEnterpriseBoundary() { + public void test_writeSystemLandscapeView() { Workspace workspace = new Workspace("Name", ""); CustomElement box = workspace.getModel().addCustomElement("Box"); Person user = workspace.getModel().addPerson("User", ""); - user.setLocation(Location.External); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", ""); - softwareSystem.setLocation(Location.Internal); user.uses(softwareSystem, "Uses"); SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("SystemLandscape", ""); view.addAllElements(); view.add(box); - view.setEnterpriseBoundaryVisible(false); DOTExporter exporter = new DOTExporter(RankDirection.TopBottom, 300, 300); Diagram diagram = exporter.export(view); @@ -88,7 +85,6 @@ public void test_writeSystemLandscapeViewWithGroupedElements() throws Exception SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("SystemLandscape", ""); view.addAllElements(); view.add(box); - view.setEnterpriseBoundaryVisible(false); DOTExporter exporter = new DOTExporter(RankDirection.TopBottom, 300, 300); Diagram diagram = exporter.export(view); @@ -179,21 +175,18 @@ public void test_writeSystemLandscapeViewWithNestedGroupedElements() throws Exce } @Test - public void test_writeSystemLandscapeViewWithNoEnterpriseBoundaryInGermanLocale() throws Exception { + public void test_writeSystemLandscapeViewInGermanLocale() throws Exception { // ranksep=1.0 was being output as ranksep=1,0 Locale.setDefault(new Locale("de", "DE")); Workspace workspace = new Workspace("Name", ""); CustomElement box = workspace.getModel().addCustomElement("Box"); Person user = workspace.getModel().addPerson("User", ""); - user.setLocation(Location.External); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", ""); - softwareSystem.setLocation(Location.Internal); user.uses(softwareSystem, "Uses"); SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("SystemLandscape", ""); view.addAllElements(); view.add(box); - view.setEnterpriseBoundaryVisible(false); DOTExporter exporter = new DOTExporter(RankDirection.TopBottom, 300, 300); Diagram diagram = exporter.export(view); @@ -214,56 +207,16 @@ public void test_writeSystemLandscapeViewWithNoEnterpriseBoundaryInGermanLocale( } @Test - public void test_writeSystemLandscapeViewWithAnEnterpriseBoundary() throws Exception { + public void test_writeSystemContextView() throws Exception { Workspace workspace = new Workspace("Name", ""); CustomElement box = workspace.getModel().addCustomElement("Box"); Person user = workspace.getModel().addPerson("User", ""); - user.setLocation(Location.External); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", ""); - softwareSystem.setLocation(Location.Internal); - user.uses(softwareSystem, "Uses"); - - SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("SystemLandscape", ""); - view.addAllElements(); - view.add(box); - view.setEnterpriseBoundaryVisible(true); - - DOTExporter exporter = new DOTExporter(RankDirection.TopBottom, 300, 300); - Diagram diagram = exporter.export(view); - - String content = diagram.getDefinition(); - assertEquals("digraph {\n" + - " compound=true\n" + - " graph [splines=polyline,rankdir=TB,ranksep=1.0,nodesep=1.0,fontsize=5]\n" + - " node [shape=box,fontsize=5]\n" + - " edge []\n" + - "\n" + - " subgraph cluster_enterprise {\n" + - " margin=25\n" + - " 3 [width=1.500000,height=1.000000,fixedsize=true,id=3,label=\"3: Software System\"]\n" + - " }\n" + - "\n" + - " 1 [width=1.500000,height=1.000000,fixedsize=true,id=1,label=\"1: Box\"]\n" + - " 2 [width=1.500000,height=1.000000,fixedsize=true,id=2,label=\"2: User\"]\n" + - "\n" + - " 2 -> 3 [id=4]\n" + - "}", content); - } - - @Test - public void test_writeSystemContextViewWithNoEnterpriseBoundary() throws Exception { - Workspace workspace = new Workspace("Name", ""); - CustomElement box = workspace.getModel().addCustomElement("Box"); - Person user = workspace.getModel().addPerson("User", ""); - user.setLocation(Location.External); - SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", ""); - softwareSystem.setLocation(Location.Internal); user.uses(softwareSystem, "Uses"); SystemContextView view = workspace.getViews().createSystemContextView(softwareSystem, "SystemContext", ""); view.addAllElements(); view.add(box); - view.setEnterpriseBoundaryVisible(false); DOTExporter exporter = new DOTExporter(RankDirection.TopBottom, 300, 300); Diagram diagram = exporter.export(view); @@ -284,43 +237,6 @@ public void test_writeSystemContextViewWithNoEnterpriseBoundary() throws Excepti } - @Test - public void test_writeSystemContextViewWithAnEnterpriseBoundary() throws Exception { - Workspace workspace = new Workspace("Name", ""); - CustomElement box = workspace.getModel().addCustomElement("Box"); - Person user = workspace.getModel().addPerson("User", ""); - user.setLocation(Location.External); - SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", ""); - softwareSystem.setLocation(Location.Internal); - user.uses(softwareSystem, "Uses"); - - SystemContextView view = workspace.getViews().createSystemContextView(softwareSystem, "SystemContext", ""); - view.addAllElements(); - view.add(box); - view.setEnterpriseBoundaryVisible(true); - - DOTExporter exporter = new DOTExporter(RankDirection.TopBottom, 300, 300); - Diagram diagram = exporter.export(view); - - String content = diagram.getDefinition(); - assertEquals("digraph {\n" + - " compound=true\n" + - " graph [splines=polyline,rankdir=TB,ranksep=1.0,nodesep=1.0,fontsize=5]\n" + - " node [shape=box,fontsize=5]\n" + - " edge []\n" + - "\n" + - " subgraph cluster_enterprise {\n" + - " margin=25\n" + - " 3 [width=1.500000,height=1.000000,fixedsize=true,id=3,label=\"3: Software System\"]\n" + - " }\n" + - "\n" + - " 1 [width=1.500000,height=1.000000,fixedsize=true,id=1,label=\"1: Box\"]\n" + - " 2 [width=1.500000,height=1.000000,fixedsize=true,id=2,label=\"2: User\"]\n" + - "\n" + - " 2 -> 3 [id=4]\n" + - "}", content); - } - @Test public void test_writeSystemContextViewWithGroupedElements() throws Exception { Workspace workspace = new Workspace("Name", ""); @@ -334,7 +250,6 @@ public void test_writeSystemContextViewWithGroupedElements() throws Exception { SystemContextView view = workspace.getViews().createSystemContextView(softwareSystem, "SystemContext", ""); view.addAllElements(); view.add(box); - view.setEnterpriseBoundaryVisible(false); DOTExporter exporter = new DOTExporter(RankDirection.TopBottom, 300, 300); Diagram diagram = exporter.export(view); diff --git a/structurizr-graphviz/src/test/java/com/structurizr/graphviz/SVGReaderTests.java b/structurizr-graphviz/src/test/java/com/structurizr/graphviz/SVGReaderTests.java index 0c5ecc86b..041472503 100644 --- a/structurizr-graphviz/src/test/java/com/structurizr/graphviz/SVGReaderTests.java +++ b/structurizr-graphviz/src/test/java/com/structurizr/graphviz/SVGReaderTests.java @@ -5,6 +5,7 @@ import com.structurizr.model.Person; import com.structurizr.model.SoftwareSystem; import com.structurizr.model.Tags; +import com.structurizr.util.WorkspaceUtils; import com.structurizr.view.PaperSize; import com.structurizr.view.Shape; import com.structurizr.view.SystemContextView; @@ -49,14 +50,11 @@ public void test_readView() throws Exception { private static Workspace createWorkspace() { Workspace workspace = new Workspace("Name", ""); Person user = workspace.getModel().addPerson("User", ""); - user.setLocation(Location.External); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", ""); - softwareSystem.setLocation(Location.Internal); user.uses(softwareSystem, "Uses"); SystemContextView view = workspace.getViews().createSystemContextView(softwareSystem, "SystemContext", ""); view.addAllElements(); - view.setEnterpriseBoundaryVisible(true); workspace.getViews().getConfiguration().getStyles().addElementStyle(Tags.PERSON).shape(Shape.Person); From 27ede3e9ffac97ac398c468c5141b69a675f9da8 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 10 Jan 2024 08:38:42 +0000 Subject: [PATCH 443/717] Removes `StructurizrClient` (use `WorkspaceApiClient` instead). --- ...> WorkspaceApiClientIntegrationTests.java} | 30 +++---- .../structurizr/api/StructurizrClient.java | 54 ------------- ...ests.java => WorkspaceApiClientTests.java} | 80 ++++++++----------- 3 files changed, 50 insertions(+), 114 deletions(-) rename structurizr-client/src/integrationTest/java/com/structurizr/api/{StructurizrClientIntegrationTests.java => WorkspaceApiClientIntegrationTests.java} (81%) delete mode 100644 structurizr-client/src/main/java/com/structurizr/api/StructurizrClient.java rename structurizr-client/src/test/java/com/structurizr/api/{StructurizrClientTests.java => WorkspaceApiClientTests.java} (55%) diff --git a/structurizr-client/src/integrationTest/java/com/structurizr/api/StructurizrClientIntegrationTests.java b/structurizr-client/src/integrationTest/java/com/structurizr/api/WorkspaceApiClientIntegrationTests.java similarity index 81% rename from structurizr-client/src/integrationTest/java/com/structurizr/api/StructurizrClientIntegrationTests.java rename to structurizr-client/src/integrationTest/java/com/structurizr/api/WorkspaceApiClientIntegrationTests.java index 48712d3ce..e54981f04 100644 --- a/structurizr-client/src/integrationTest/java/com/structurizr/api/StructurizrClientIntegrationTests.java +++ b/structurizr-client/src/integrationTest/java/com/structurizr/api/WorkspaceApiClientIntegrationTests.java @@ -17,19 +17,19 @@ import static org.junit.jupiter.api.Assertions.*; -public class StructurizrClientIntegrationTests { +public class WorkspaceApiClientIntegrationTests { - private StructurizrClient structurizrClient; - private File workspaceArchiveLocation = new File(System.getProperty("java.io.tmpdir"), "structurizr"); + private WorkspaceApiClient client; + private final File workspaceArchiveLocation = new File(System.getProperty("java.io.tmpdir"), "structurizr"); @BeforeEach void setUp() { - structurizrClient = new StructurizrClient("81ace434-94a1-486f-a786-37bbeaa44e08", "a8673e21-7b6f-4f52-be65-adb7248be86b"); - structurizrClient.setWorkspaceArchiveLocation(workspaceArchiveLocation); + client = new WorkspaceApiClient("81ace434-94a1-486f-a786-37bbeaa44e08", "a8673e21-7b6f-4f52-be65-adb7248be86b"); + client.setWorkspaceArchiveLocation(workspaceArchiveLocation); workspaceArchiveLocation.mkdirs(); clearWorkspaceArchive(); assertEquals(0, workspaceArchiveLocation.listFiles().length); - structurizrClient.setMergeFromRemote(false); + client.setMergeFromRemote(false); } @AfterEach @@ -59,9 +59,9 @@ void putAndGetWorkspace_WithoutEncryption() throws Exception { SystemContextView systemContextView = workspace.getViews().createSystemContextView(softwareSystem, "SystemContext", "Description"); systemContextView.addAllElements(); - structurizrClient.putWorkspace(20081, workspace); + client.putWorkspace(20081, workspace); - workspace = structurizrClient.getWorkspace(20081); + workspace = client.getWorkspace(20081); assertNotNull(workspace.getModel().getSoftwareSystemWithName("Software System")); assertNotNull(workspace.getModel().getPersonWithName("Person")); assertEquals(1, workspace.getModel().getRelationships().size()); @@ -78,7 +78,7 @@ void putAndGetWorkspace_WithoutEncryption() throws Exception { @Test void putAndGetWorkspace_WithEncryption() throws Exception { - structurizrClient.setEncryptionStrategy(new AesEncryptionStrategy("password")); + client.setEncryptionStrategy(new AesEncryptionStrategy("password")); Workspace workspace = new Workspace("Structurizr client library tests - with encryption", "A test workspace for the Structurizr client library"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); Person person = workspace.getModel().addPerson("Person", "Description"); @@ -86,9 +86,9 @@ void putAndGetWorkspace_WithEncryption() throws Exception { SystemContextView systemContextView = workspace.getViews().createSystemContextView(softwareSystem, "SystemContext", "Description"); systemContextView.addAllElements(); - structurizrClient.putWorkspace(20081, workspace); + client.putWorkspace(20081, workspace); - workspace = structurizrClient.getWorkspace(20081); + workspace = client.getWorkspace(20081); assertNotNull(workspace.getModel().getSoftwareSystemWithName("Software System")); assertNotNull(workspace.getModel().getPersonWithName("Person")); assertEquals(1, workspace.getModel().getRelationships().size()); @@ -105,15 +105,15 @@ void putAndGetWorkspace_WithEncryption() throws Exception { @Test void lockWorkspace() throws Exception { - structurizrClient.unlockWorkspace(20081); - assertTrue(structurizrClient.lockWorkspace(20081)); + client.unlockWorkspace(20081); + assertTrue(client.lockWorkspace(20081)); } @Test void unlockWorkspace() throws Exception { - structurizrClient.lockWorkspace(20081); - assertTrue(structurizrClient.unlockWorkspace(20081)); + client.lockWorkspace(20081); + assertTrue(client.unlockWorkspace(20081)); } } \ No newline at end of file diff --git a/structurizr-client/src/main/java/com/structurizr/api/StructurizrClient.java b/structurizr-client/src/main/java/com/structurizr/api/StructurizrClient.java deleted file mode 100644 index 422bad04c..000000000 --- a/structurizr-client/src/main/java/com/structurizr/api/StructurizrClient.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.structurizr.api; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Properties; - -/** - * A client for the Structurizr workspace API that allows you to get and put Structurizr workspaces in a JSON format. - * - * @deprecated Use WorkspaceApiClient instead - */ -@Deprecated -public class StructurizrClient extends WorkspaceApiClient { - - private static final String STRUCTURIZR_API_URL = "structurizr.api.url"; - private static final String STRUCTURIZR_API_KEY = "structurizr.api.key"; - private static final String STRUCTURIZR_API_SECRET = "structurizr.api.secret"; - - /** - * Creates a new Structurizr client based upon configuration in a structurizr.properties file - * on the classpath with the following name-value pairs: - * - structurizr.api.url - * - structurizr.api.key - * - structurizr.api.secret - * - * @throws StructurizrClientException if something goes wrong - */ - public StructurizrClient() throws StructurizrClientException { - try (InputStream in = - WorkspaceApiClient.class.getClassLoader().getResourceAsStream("structurizr.properties")) { - Properties properties = new Properties(); - if (in != null) { - properties.load(in); - - setUrl(properties.getProperty(STRUCTURIZR_API_URL)); - setApiKey(properties.getProperty(STRUCTURIZR_API_KEY)); - setApiSecret(properties.getProperty(STRUCTURIZR_API_SECRET)); - } else { - throw new StructurizrClientException("Could not find a structurizr.properties file on the classpath."); - } - } catch (IOException e) { - throw new StructurizrClientException(e); - } - } - - public StructurizrClient(String apiKey, String apiSecret) { - super(apiKey, apiSecret); - } - - public StructurizrClient(String url, String apiKey, String apiSecret) { - super(url, apiKey, apiSecret); - } - -} \ No newline at end of file diff --git a/structurizr-client/src/test/java/com/structurizr/api/StructurizrClientTests.java b/structurizr-client/src/test/java/com/structurizr/api/WorkspaceApiClientTests.java similarity index 55% rename from structurizr-client/src/test/java/com/structurizr/api/StructurizrClientTests.java rename to structurizr-client/src/test/java/com/structurizr/api/WorkspaceApiClientTests.java index 4b7fdef59..0e2aeca51 100644 --- a/structurizr-client/src/test/java/com/structurizr/api/StructurizrClientTests.java +++ b/structurizr-client/src/test/java/com/structurizr/api/WorkspaceApiClientTests.java @@ -5,38 +5,38 @@ import static org.junit.jupiter.api.Assertions.*; -public class StructurizrClientTests { +public class WorkspaceApiClientTests { - private StructurizrClient structurizrClient; + private WorkspaceApiClient client; @Test void construction_WithTwoParameters() { - structurizrClient = new StructurizrClient("key", "secret"); - assertEquals("https://api.structurizr.com", structurizrClient.getUrl()); - assertEquals("key", structurizrClient.getApiKey()); - assertEquals("secret", structurizrClient.getApiSecret()); + client = new WorkspaceApiClient("key", "secret"); + assertEquals("https://api.structurizr.com", client.getUrl()); + assertEquals("key", client.getApiKey()); + assertEquals("secret", client.getApiSecret()); } @Test void construction_WithThreeParameters() { - structurizrClient = new StructurizrClient("https://localhost", "key", "secret"); - assertEquals("https://localhost", structurizrClient.getUrl()); - assertEquals("key", structurizrClient.getApiKey()); - assertEquals("secret", structurizrClient.getApiSecret()); + client = new WorkspaceApiClient("https://localhost", "key", "secret"); + assertEquals("https://localhost", client.getUrl()); + assertEquals("key", client.getApiKey()); + assertEquals("secret", client.getApiSecret()); } @Test void construction_WithThreeParameters_TruncatesTheApiUrl_WhenTheApiUrlHasATrailingSlashCharacter() { - structurizrClient = new StructurizrClient("https://localhost/", "key", "secret"); - assertEquals("https://localhost", structurizrClient.getUrl()); - assertEquals("key", structurizrClient.getApiKey()); - assertEquals("secret", structurizrClient.getApiSecret()); + client = new WorkspaceApiClient("https://localhost/", "key", "secret"); + assertEquals("https://localhost", client.getUrl()); + assertEquals("key", client.getApiKey()); + assertEquals("secret", client.getApiSecret()); } @Test void construction_ThrowsAnException_WhenANullApiKeyIsUsed() { try { - structurizrClient = new StructurizrClient(null, "secret"); + client = new WorkspaceApiClient(null, "secret"); fail(); } catch (IllegalArgumentException iae) { assertEquals("The API key must not be null or empty.", iae.getMessage()); @@ -46,7 +46,7 @@ void construction_ThrowsAnException_WhenANullApiKeyIsUsed() { @Test void construction_ThrowsAnException_WhenAnEmptyApiKeyIsUsed() { try { - structurizrClient = new StructurizrClient(" ", "secret"); + client = new WorkspaceApiClient(" ", "secret"); fail(); } catch (IllegalArgumentException iae) { assertEquals("The API key must not be null or empty.", iae.getMessage()); @@ -56,7 +56,7 @@ void construction_ThrowsAnException_WhenAnEmptyApiKeyIsUsed() { @Test void construction_ThrowsAnException_WhenANullApiSecretIsUsed() { try { - structurizrClient = new StructurizrClient("key", null); + client = new WorkspaceApiClient("key", null); fail(); } catch (IllegalArgumentException iae) { assertEquals("The API secret must not be null or empty.", iae.getMessage()); @@ -66,7 +66,7 @@ void construction_ThrowsAnException_WhenANullApiSecretIsUsed() { @Test void construction_ThrowsAnException_WhenAnEmptyApiSecretIsUsed() { try { - structurizrClient = new StructurizrClient("key", " "); + client = new WorkspaceApiClient("key", " "); fail(); } catch (IllegalArgumentException iae) { assertEquals("The API secret must not be null or empty.", iae.getMessage()); @@ -76,7 +76,7 @@ void construction_ThrowsAnException_WhenAnEmptyApiSecretIsUsed() { @Test void construction_ThrowsAnException_WhenANullApiUrlIsUsed() { try { - structurizrClient = new StructurizrClient(null, "key", "secret"); + client = new WorkspaceApiClient(null, "key", "secret"); fail(); } catch (IllegalArgumentException iae) { assertEquals("The API URL must not be null or empty.", iae.getMessage()); @@ -86,7 +86,7 @@ void construction_ThrowsAnException_WhenANullApiUrlIsUsed() { @Test void construction_ThrowsAnException_WhenAnEmptyApiUrlIsUsed() { try { - structurizrClient = new StructurizrClient(" ", "key", "secret"); + client = new WorkspaceApiClient(" ", "key", "secret"); fail(); } catch (IllegalArgumentException iae) { assertEquals("The API URL must not be null or empty.", iae.getMessage()); @@ -96,8 +96,8 @@ void construction_ThrowsAnException_WhenAnEmptyApiUrlIsUsed() { @Test void getWorkspace_ThrowsAnException_WhenTheWorkspaceIdIsNotValid() throws Exception { try { - structurizrClient = new StructurizrClient("key", "secret"); - structurizrClient.getWorkspace(0); + client = new WorkspaceApiClient("key", "secret"); + client.getWorkspace(0); fail(); } catch (IllegalArgumentException iae) { assertEquals("The workspace ID must be a positive integer.", iae.getMessage()); @@ -107,8 +107,8 @@ void getWorkspace_ThrowsAnException_WhenTheWorkspaceIdIsNotValid() throws Except @Test void putWorkspace_ThrowsAnException_WhenTheWorkspaceIdIsNotValid() throws Exception { try { - structurizrClient = new StructurizrClient("key", "secret"); - structurizrClient.putWorkspace(0, new Workspace("Name", "Description")); + client = new WorkspaceApiClient("key", "secret"); + client.putWorkspace(0, new Workspace("Name", "Description")); fail(); } catch (IllegalArgumentException iae) { assertEquals("The workspace ID must be a positive integer.", iae.getMessage()); @@ -118,43 +118,33 @@ void putWorkspace_ThrowsAnException_WhenTheWorkspaceIdIsNotValid() throws Except @Test void putWorkspace_ThrowsAnException_WhenANullWorkspaceIsSpecified() throws Exception { try { - structurizrClient = new StructurizrClient("key", "secret"); - structurizrClient.putWorkspace(1234, null); + client = new WorkspaceApiClient("key", "secret"); + client.putWorkspace(1234, null); fail(); } catch (IllegalArgumentException iae) { assertEquals("The workspace must not be null.", iae.getMessage()); } } - @Test - void constructionWithAPropertiesFile_ThrowsAnException_WhenNoPropertiesAreFound() { - try { - structurizrClient = new StructurizrClient(); - fail(); - } catch (Exception e) { - assertEquals("Could not find a structurizr.properties file on the classpath.", e.getMessage()); - } - } - @Test void getAgent() { - structurizrClient = new StructurizrClient("key", "secret"); - assertTrue(structurizrClient.getAgent().startsWith("structurizr-java/")); + client = new WorkspaceApiClient("key", "secret"); + assertTrue(client.getAgent().startsWith("structurizr-java/")); } @Test void setAgent() { - structurizrClient = new StructurizrClient("key", "secret"); - structurizrClient.setAgent("new_agent"); - assertEquals("new_agent", structurizrClient.getAgent()); + client = new WorkspaceApiClient("key", "secret"); + client.setAgent("new_agent"); + assertEquals("new_agent", client.getAgent()); } @Test void setAgent_ThrowsAnException_WhenPassedNull() { - structurizrClient = new StructurizrClient("key", "secret"); + client = new WorkspaceApiClient("key", "secret"); try { - structurizrClient.setAgent(null); + client.setAgent(null); fail(); } catch (Exception e) { assertEquals("An agent must be provided.", e.getMessage()); @@ -163,10 +153,10 @@ void setAgent_ThrowsAnException_WhenPassedNull() { @Test void setAgent_ThrowsAnException_WhenPassedAnEmptyString() { - structurizrClient = new StructurizrClient("key", "secret"); + client = new WorkspaceApiClient("key", "secret"); try { - structurizrClient.setAgent(" "); + client.setAgent(" "); fail(); } catch (Exception e) { assertEquals("An agent must be provided.", e.getMessage()); From 5389f42002a441de1dd14b57902c6f4b12976313 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 10 Jan 2024 08:42:00 +0000 Subject: [PATCH 444/717] Clean up of deprecated items, updated changelog. --- changelog.md | 3 ++- .../main/java/com/structurizr/model/Enterprise.java | 2 +- .../main/java/com/structurizr/view/Configuration.java | 10 ---------- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/changelog.md b/changelog.md index 7e95c9fb2..f4b17d08d 100644 --- a/changelog.md +++ b/changelog.md @@ -2,6 +2,7 @@ ## 2.0.0 (unreleased) +- Removes the deprecated concepts (e.g. location and enterprise. - structurizr-core: Adds `Workspace.trim()` to trim a workspace of unused elements (i.e. those not associated with any views). -- structurizr-core: Removes the deprecated location and enterprise concepts from `Model`. +- structurizr-client: Removes `StructurizrClient` (use `WorkspaceApiClient` instead). diff --git a/structurizr-core/src/main/java/com/structurizr/model/Enterprise.java b/structurizr-core/src/main/java/com/structurizr/model/Enterprise.java index 5a4b58f2a..ff841e0fb 100644 --- a/structurizr-core/src/main/java/com/structurizr/model/Enterprise.java +++ b/structurizr-core/src/main/java/com/structurizr/model/Enterprise.java @@ -17,7 +17,7 @@ public final class Enterprise { * @throws IllegalArgumentException if the name is not specified */ @Deprecated - public Enterprise(String name) { + Enterprise(String name) { if (name == null || name.trim().length() == 0) { throw new IllegalArgumentException("Name must be specified."); } diff --git a/structurizr-core/src/main/java/com/structurizr/view/Configuration.java b/structurizr-core/src/main/java/com/structurizr/view/Configuration.java index dc9043da6..9d1a0c3fe 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/Configuration.java +++ b/structurizr-core/src/main/java/com/structurizr/view/Configuration.java @@ -38,16 +38,6 @@ public Styles getStyles() { return styles; } - @JsonIgnore - @Deprecated - public String getTheme() { - if (themes == null || themes.size() == 0) { - return null; - } - - return themes.get(0); - } - /** * Sets the theme used to render views. * From e6d97bf1278da8bafe73b8faf03c437c1e8fbda3 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 10 Jan 2024 08:44:57 +0000 Subject: [PATCH 445/717] Fix tests. --- .../java/com/structurizr/view/ConfigurationTests.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/structurizr-core/src/test/java/com/structurizr/view/ConfigurationTests.java b/structurizr-core/src/test/java/com/structurizr/view/ConfigurationTests.java index ff3490c42..5a15e24dc 100644 --- a/structurizr-core/src/test/java/com/structurizr/view/ConfigurationTests.java +++ b/structurizr-core/src/test/java/com/structurizr/view/ConfigurationTests.java @@ -36,14 +36,14 @@ void copyConfigurationFrom() { void setTheme_WithAUrl() { Configuration configuration = new Configuration(); configuration.setTheme("https://example.com/theme.json"); - assertEquals("https://example.com/theme.json", configuration.getTheme()); + assertEquals("https://example.com/theme.json", configuration.getThemes()[0]); } @Test void setTheme_WithAUrlThatHasATrailingSpace() { Configuration configuration = new Configuration(); configuration.setTheme("https://example.com/theme.json "); - assertEquals("https://example.com/theme.json", configuration.getTheme()); + assertEquals("https://example.com/theme.json", configuration.getThemes()[0]); } @Test @@ -58,14 +58,14 @@ void setTheme_ThrowsAnIllegalArgumentException_WhenAnInvalidUrlIsSpecified() { void setTheme_DoesNothing_WhenANullUrlIsSpecified() { Configuration configuration = new Configuration(); configuration.setTheme(null); - assertNull(configuration.getTheme()); + assertEquals(0, configuration.getThemes().length); } @Test void setTheme_DoesNothing_WhenAnEmptyUrlIsSpecified() { Configuration configuration = new Configuration(); configuration.setTheme(" "); - assertNull(configuration.getTheme()); + assertEquals(0, configuration.getThemes().length); } } \ No newline at end of file From 9162b16d32c561c816e7d3d21f6d03fefbace9ae Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 10 Jan 2024 08:46:38 +0000 Subject: [PATCH 446/717] Adds a note about structurizr-assistant. --- README.md | 3 ++- changelog.md | 1 + structurizr-assistant/README.md | 4 ++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 41abcb2b6..b66dac88e 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,9 @@ This repository contains the source code for the following libraries: - [structurizr-core](structurizr-core) - [structurizr-dsl](structurizr-dsl) - [structurizr-export](structurizr-export) -- [structurizr-graphviz](structurizr-graphviz) - [structurizr-import](structurizr-import) +- [structurizr-graphviz](structurizr-graphviz) +- [structurizr-assistant](structurizr-assistant) - [Documentation](https://docs.structurizr.com) - [Changelog](changelog.md) \ No newline at end of file diff --git a/changelog.md b/changelog.md index f4b17d08d..dc9cdf4bd 100644 --- a/changelog.md +++ b/changelog.md @@ -5,4 +5,5 @@ - Removes the deprecated concepts (e.g. location and enterprise. - structurizr-core: Adds `Workspace.trim()` to trim a workspace of unused elements (i.e. those not associated with any views). - structurizr-client: Removes `StructurizrClient` (use `WorkspaceApiClient` instead). +- structurizr-assistant: Initial version. diff --git a/structurizr-assistant/README.md b/structurizr-assistant/README.md index 790b286e8..816a01919 100644 --- a/structurizr-assistant/README.md +++ b/structurizr-assistant/README.md @@ -1,3 +1,7 @@ # structurizr-assistant [![Maven Central](https://img.shields.io/maven-central/v/com.structurizr/structurizr-assistant.svg?label=Maven%20Central)](https://search.maven.org/artifact/com.structurizr/structurizr-assistant) + +This library provides some utilities that will make recommendations on how to improve a Structurizr workspace. + +- [Documentation](https://docs.structurizr.com/workspaces) \ No newline at end of file From 538a51ed0501422690cab2128687b1991b5b5e33 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 10 Jan 2024 10:47:57 +0000 Subject: [PATCH 447/717] structurizr-graphviz -> structurizr-autolayout (because the former has version numbers 2.0.0+) --- .github/workflows/gradle.yml | 2 +- README.md | 2 +- settings.gradle | 2 +- {structurizr-graphviz => structurizr-autolayout}/README.md | 6 +++--- .../build.gradle | 0 .../com/structurizr/autolayout}/graphviz/Constants.java | 2 +- .../com/structurizr/autolayout}/graphviz/DOTDiagram.java | 2 +- .../com/structurizr/autolayout}/graphviz/DOTExporter.java | 2 +- .../autolayout}/graphviz/GraphvizAutomaticLayout.java | 2 +- .../com/structurizr/autolayout}/graphviz/RankDirection.java | 2 +- .../com/structurizr/autolayout}/graphviz/SVGReader.java | 2 +- .../structurizr/autolayout}/graphviz/DOTExporterTests.java | 4 +++- .../autolayout}/graphviz/GraphvizAutomaticLayoutTests.java | 3 ++- .../structurizr/autolayout}/graphviz/SVGReaderTests.java | 5 ++--- .../src/test/resources/graphviz/SystemContext.dot | 0 .../src/test/resources/graphviz/SystemContext.dot.svg | 0 .../src/test/resources/structurizr-54915-workspace.json | 0 17 files changed, 19 insertions(+), 17 deletions(-) rename {structurizr-graphviz => structurizr-autolayout}/README.md (77%) rename {structurizr-graphviz => structurizr-autolayout}/build.gradle (100%) rename {structurizr-graphviz/src/main/java/com/structurizr => structurizr-autolayout/src/main/java/com/structurizr/autolayout}/graphviz/Constants.java (91%) rename {structurizr-graphviz/src/main/java/com/structurizr => structurizr-autolayout/src/main/java/com/structurizr/autolayout}/graphviz/DOTDiagram.java (86%) rename {structurizr-graphviz/src/main/java/com/structurizr => structurizr-autolayout/src/main/java/com/structurizr/autolayout}/graphviz/DOTExporter.java (99%) rename {structurizr-graphviz/src/main/java/com/structurizr => structurizr-autolayout/src/main/java/com/structurizr/autolayout}/graphviz/GraphvizAutomaticLayout.java (99%) rename {structurizr-graphviz/src/main/java/com/structurizr => structurizr-autolayout/src/main/java/com/structurizr/autolayout}/graphviz/RankDirection.java (87%) rename {structurizr-graphviz/src/main/java/com/structurizr => structurizr-autolayout/src/main/java/com/structurizr/autolayout}/graphviz/SVGReader.java (99%) rename {structurizr-graphviz/src/test/java/com/structurizr => structurizr-autolayout/src/test/java/com/structurizr/autolayout}/graphviz/DOTExporterTests.java (99%) rename {structurizr-graphviz/src/test/java/com/structurizr => structurizr-autolayout/src/test/java/com/structurizr/autolayout}/graphviz/GraphvizAutomaticLayoutTests.java (94%) rename {structurizr-graphviz/src/test/java/com/structurizr => structurizr-autolayout/src/test/java/com/structurizr/autolayout}/graphviz/SVGReaderTests.java (95%) rename {structurizr-graphviz => structurizr-autolayout}/src/test/resources/graphviz/SystemContext.dot (100%) rename {structurizr-graphviz => structurizr-autolayout}/src/test/resources/graphviz/SystemContext.dot.svg (100%) rename {structurizr-graphviz => structurizr-autolayout}/src/test/resources/structurizr-54915-workspace.json (100%) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 68c76f168..07cd9183c 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -28,4 +28,4 @@ jobs: - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build with Gradle - run: ./gradlew -x :structurizr-graphviz:test + run: ./gradlew -x :structurizr-autolayout:test diff --git a/README.md b/README.md index b66dac88e..7fe8a09ff 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ This repository contains the source code for the following libraries: - [structurizr-dsl](structurizr-dsl) - [structurizr-export](structurizr-export) - [structurizr-import](structurizr-import) -- [structurizr-graphviz](structurizr-graphviz) +- [structurizr-autolayout](structurizr-autolayout) - [structurizr-assistant](structurizr-assistant) - [Documentation](https://docs.structurizr.com) diff --git a/settings.gradle b/settings.gradle index e06f2f882..235846a6b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,5 +5,5 @@ include 'structurizr-client' include 'structurizr-core' include 'structurizr-dsl' include 'structurizr-export' -include 'structurizr-graphviz' +include 'structurizr-autolayout' include 'structurizr-import' diff --git a/structurizr-graphviz/README.md b/structurizr-autolayout/README.md similarity index 77% rename from structurizr-graphviz/README.md rename to structurizr-autolayout/README.md index bed0d0a76..92e58e3e6 100644 --- a/structurizr-graphviz/README.md +++ b/structurizr-autolayout/README.md @@ -1,6 +1,6 @@ -# structurizr-graphviz +# structurizr-autolayout -[![Maven Central](https://img.shields.io/maven-central/v/com.structurizr/structurizr-graphviz.svg?label=Maven%20Central)](https://search.maven.org/artifact/com.structurizr/structurizr-graphviz) +[![Maven Central](https://img.shields.io/maven-central/v/com.structurizr/structurizr-autolayout.svg?label=Maven%20Central)](https://search.maven.org/artifact/com.structurizr/structurizr-autolayout) This library provides automatic facilities for Structurizr views. It's a wrapper around the [Graphviz tool](http://www.graphviz.org), @@ -17,7 +17,7 @@ GraphvizAutomaticLayout graphviz = new GraphvizAutomaticLayout(); graphviz.apply(workspace); ``` -The ```structurizr-graphviz``` library does the following for every view in the workspace: +The ```structurizr-autolayout``` library does the following for every view in the workspace: 1. Export the view to a DOT file. 2. Run Graphviz (via the ```dot``` command), with the output format set to SVG. diff --git a/structurizr-graphviz/build.gradle b/structurizr-autolayout/build.gradle similarity index 100% rename from structurizr-graphviz/build.gradle rename to structurizr-autolayout/build.gradle diff --git a/structurizr-graphviz/src/main/java/com/structurizr/graphviz/Constants.java b/structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/Constants.java similarity index 91% rename from structurizr-graphviz/src/main/java/com/structurizr/graphviz/Constants.java rename to structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/Constants.java index 37340f9cb..34cf8efc5 100644 --- a/structurizr-graphviz/src/main/java/com/structurizr/graphviz/Constants.java +++ b/structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/Constants.java @@ -1,4 +1,4 @@ -package com.structurizr.graphviz; +package com.structurizr.autolayout.graphviz; /** * Some constants used when applying graphviz. diff --git a/structurizr-graphviz/src/main/java/com/structurizr/graphviz/DOTDiagram.java b/structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/DOTDiagram.java similarity index 86% rename from structurizr-graphviz/src/main/java/com/structurizr/graphviz/DOTDiagram.java rename to structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/DOTDiagram.java index 40b232858..0e2cc9e7b 100644 --- a/structurizr-graphviz/src/main/java/com/structurizr/graphviz/DOTDiagram.java +++ b/structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/DOTDiagram.java @@ -1,4 +1,4 @@ -package com.structurizr.graphviz; +package com.structurizr.autolayout.graphviz; import com.structurizr.export.Diagram; import com.structurizr.view.ModelView; diff --git a/structurizr-graphviz/src/main/java/com/structurizr/graphviz/DOTExporter.java b/structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/DOTExporter.java similarity index 99% rename from structurizr-graphviz/src/main/java/com/structurizr/graphviz/DOTExporter.java rename to structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/DOTExporter.java index c3c73b6cc..bff97a7e3 100644 --- a/structurizr-graphviz/src/main/java/com/structurizr/graphviz/DOTExporter.java +++ b/structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/DOTExporter.java @@ -1,4 +1,4 @@ -package com.structurizr.graphviz; +package com.structurizr.autolayout.graphviz; import com.structurizr.export.AbstractDiagramExporter; import com.structurizr.export.Diagram; diff --git a/structurizr-graphviz/src/main/java/com/structurizr/graphviz/GraphvizAutomaticLayout.java b/structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/GraphvizAutomaticLayout.java similarity index 99% rename from structurizr-graphviz/src/main/java/com/structurizr/graphviz/GraphvizAutomaticLayout.java rename to structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/GraphvizAutomaticLayout.java index defdc86f0..07ca03630 100644 --- a/structurizr-graphviz/src/main/java/com/structurizr/graphviz/GraphvizAutomaticLayout.java +++ b/structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/GraphvizAutomaticLayout.java @@ -1,4 +1,4 @@ -package com.structurizr.graphviz; +package com.structurizr.autolayout.graphviz; import com.structurizr.Workspace; import com.structurizr.export.Diagram; diff --git a/structurizr-graphviz/src/main/java/com/structurizr/graphviz/RankDirection.java b/structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/RankDirection.java similarity index 87% rename from structurizr-graphviz/src/main/java/com/structurizr/graphviz/RankDirection.java rename to structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/RankDirection.java index 4bbe7486e..e3c4e366c 100644 --- a/structurizr-graphviz/src/main/java/com/structurizr/graphviz/RankDirection.java +++ b/structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/RankDirection.java @@ -1,4 +1,4 @@ -package com.structurizr.graphviz; +package com.structurizr.autolayout.graphviz; /** * The various rank directions used by graphviz. diff --git a/structurizr-graphviz/src/main/java/com/structurizr/graphviz/SVGReader.java b/structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/SVGReader.java similarity index 99% rename from structurizr-graphviz/src/main/java/com/structurizr/graphviz/SVGReader.java rename to structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/SVGReader.java index 4fde3f242..359c2edb5 100644 --- a/structurizr-graphviz/src/main/java/com/structurizr/graphviz/SVGReader.java +++ b/structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/SVGReader.java @@ -1,4 +1,4 @@ -package com.structurizr.graphviz; +package com.structurizr.autolayout.graphviz; import com.structurizr.model.DeploymentNode; import com.structurizr.model.Element; diff --git a/structurizr-graphviz/src/test/java/com/structurizr/graphviz/DOTExporterTests.java b/structurizr-autolayout/src/test/java/com/structurizr/autolayout/graphviz/DOTExporterTests.java similarity index 99% rename from structurizr-graphviz/src/test/java/com/structurizr/graphviz/DOTExporterTests.java rename to structurizr-autolayout/src/test/java/com/structurizr/autolayout/graphviz/DOTExporterTests.java index 0f9214949..367015556 100644 --- a/structurizr-graphviz/src/test/java/com/structurizr/graphviz/DOTExporterTests.java +++ b/structurizr-autolayout/src/test/java/com/structurizr/autolayout/graphviz/DOTExporterTests.java @@ -1,6 +1,8 @@ -package com.structurizr.graphviz; +package com.structurizr.autolayout.graphviz; import com.structurizr.Workspace; +import com.structurizr.autolayout.graphviz.DOTExporter; +import com.structurizr.autolayout.graphviz.RankDirection; import com.structurizr.export.Diagram; import com.structurizr.model.*; import com.structurizr.util.WorkspaceUtils; diff --git a/structurizr-graphviz/src/test/java/com/structurizr/graphviz/GraphvizAutomaticLayoutTests.java b/structurizr-autolayout/src/test/java/com/structurizr/autolayout/graphviz/GraphvizAutomaticLayoutTests.java similarity index 94% rename from structurizr-graphviz/src/test/java/com/structurizr/graphviz/GraphvizAutomaticLayoutTests.java rename to structurizr-autolayout/src/test/java/com/structurizr/autolayout/graphviz/GraphvizAutomaticLayoutTests.java index c92385bb4..233423401 100644 --- a/structurizr-graphviz/src/test/java/com/structurizr/graphviz/GraphvizAutomaticLayoutTests.java +++ b/structurizr-autolayout/src/test/java/com/structurizr/autolayout/graphviz/GraphvizAutomaticLayoutTests.java @@ -1,6 +1,7 @@ -package com.structurizr.graphviz; +package com.structurizr.autolayout.graphviz; import com.structurizr.Workspace; +import com.structurizr.autolayout.graphviz.GraphvizAutomaticLayout; import com.structurizr.model.Person; import com.structurizr.model.SoftwareSystem; import com.structurizr.model.Tags; diff --git a/structurizr-graphviz/src/test/java/com/structurizr/graphviz/SVGReaderTests.java b/structurizr-autolayout/src/test/java/com/structurizr/autolayout/graphviz/SVGReaderTests.java similarity index 95% rename from structurizr-graphviz/src/test/java/com/structurizr/graphviz/SVGReaderTests.java rename to structurizr-autolayout/src/test/java/com/structurizr/autolayout/graphviz/SVGReaderTests.java index 041472503..c74a59937 100644 --- a/structurizr-graphviz/src/test/java/com/structurizr/graphviz/SVGReaderTests.java +++ b/structurizr-autolayout/src/test/java/com/structurizr/autolayout/graphviz/SVGReaderTests.java @@ -1,11 +1,10 @@ -package com.structurizr.graphviz; +package com.structurizr.autolayout.graphviz; import com.structurizr.Workspace; -import com.structurizr.model.Location; +import com.structurizr.autolayout.graphviz.SVGReader; import com.structurizr.model.Person; import com.structurizr.model.SoftwareSystem; import com.structurizr.model.Tags; -import com.structurizr.util.WorkspaceUtils; import com.structurizr.view.PaperSize; import com.structurizr.view.Shape; import com.structurizr.view.SystemContextView; diff --git a/structurizr-graphviz/src/test/resources/graphviz/SystemContext.dot b/structurizr-autolayout/src/test/resources/graphviz/SystemContext.dot similarity index 100% rename from structurizr-graphviz/src/test/resources/graphviz/SystemContext.dot rename to structurizr-autolayout/src/test/resources/graphviz/SystemContext.dot diff --git a/structurizr-graphviz/src/test/resources/graphviz/SystemContext.dot.svg b/structurizr-autolayout/src/test/resources/graphviz/SystemContext.dot.svg similarity index 100% rename from structurizr-graphviz/src/test/resources/graphviz/SystemContext.dot.svg rename to structurizr-autolayout/src/test/resources/graphviz/SystemContext.dot.svg diff --git a/structurizr-graphviz/src/test/resources/structurizr-54915-workspace.json b/structurizr-autolayout/src/test/resources/structurizr-54915-workspace.json similarity index 100% rename from structurizr-graphviz/src/test/resources/structurizr-54915-workspace.json rename to structurizr-autolayout/src/test/resources/structurizr-54915-workspace.json From 4f5aa81a5cfb743cd85312dbfc4221b975c0b86d Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 10 Jan 2024 16:31:58 +0000 Subject: [PATCH 448/717] Adds a public `parse(String, File)` method ... closes #230 --- .../structurizr/dsl/StructurizrDslParser.java | 115 ++++++++++-------- 1 file changed, 64 insertions(+), 51 deletions(-) diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index db957889f..8bfa5bc9e 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -35,12 +35,12 @@ public final class StructurizrDslParser extends StructurizrDslTokens { private Charset characterEncoding = StandardCharsets.UTF_8; private IdentifierScope identifierScope = IdentifierScope.Flat; - private Stack contextStack; - private Set parsedTokens = new HashSet<>(); - private IdentifiersRegister identifiersRegister; - private Map constants; + private final Stack contextStack; + private final Set parsedTokens = new HashSet<>(); + private final IdentifiersRegister identifiersRegister; + private final Map constants; - private List dslSourceLines = new ArrayList<>(); + private final List dslSourceLines = new ArrayList<>(); private Workspace workspace; private boolean extendingWorkspace = false; @@ -121,27 +121,22 @@ void parse(DslParserContext context, File path) throws StructurizrDslParserExcep } /** - * Parses the specified Structurizr DSL file(s), adding the parsed content to the workspace. - * If "path" represents a single file, that single file will be parsed. - * If "path" represents a directory, all files in that directory (recursively) will be parsed. + * Parses the specified Structurizr DSL file. * - * @param path a File object representing a file or directory + * @param dslFile a File object representing a DSL file * @throws StructurizrDslParserException when something goes wrong */ - public void parse(File path) throws StructurizrDslParserException { - if (path == null) { + public void parse(File dslFile) throws StructurizrDslParserException { + if (dslFile == null) { throw new StructurizrDslParserException("A file must be specified"); } - if (!path.exists()) { - throw new StructurizrDslParserException("The file at " + path.getAbsolutePath() + " does not exist"); + if (!dslFile.exists()) { + throw new StructurizrDslParserException("The file at " + dslFile.getAbsolutePath() + " does not exist"); } - List files = FileUtils.findFiles(path); try { - for (File file : files) { - parse(Files.readAllLines(file.toPath(), characterEncoding), file); - } + parse(Files.readAllLines(dslFile.toPath(), characterEncoding), dslFile); } catch (IOException e) { throw new StructurizrDslParserException(e.getMessage()); } @@ -154,51 +149,38 @@ void parse(DslParserContext context, String dsl) throws StructurizrDslParserExce } /** - * Parses the specified Structurizr DSL fragment, adding the parsed content to the workspace. + * Parses the specified Structurizr DSL, adding the parsed content to the workspace. * - * @param dsl a DSL fragment + * @param dsl a Structurizr DSL definition, as a single String * @throws StructurizrDslParserException when something goes wrong */ public void parse(String dsl) throws StructurizrDslParserException { + parse(dsl, new File(".")); + } + + /** + * Parses the specified Structurizr DSL, adding the parsed content to the workspace. + * + * @param dsl a Structurizr DSL definition, as a single String + * @param dslFile a File representing the DSL file, and therefore where includes/images/etc should be loaded relative to + * @throws StructurizrDslParserException when something goes wrong + */ + public void parse(String dsl, File dslFile) throws StructurizrDslParserException { if (StringUtils.isNullOrEmpty(dsl)) { throw new StructurizrDslParserException("A DSL fragment must be specified"); } List lines = Arrays.asList(dsl.split("\\r?\\n")); - parse(lines, new File(".")); - } - - private List preProcessLines(List lines) { - List dslLines = new ArrayList<>(); - - int lineNumber = 1; - StringBuilder buf = new StringBuilder(); - boolean lineComplete = true; - - for (String line : lines) { - if (line.endsWith(MULTI_LINE_SEPARATOR)) { - buf.append(line, 0, line.length()-1); - lineComplete = false; - } else { - if (lineComplete) { - buf.append(line); - } else { - buf.append(line.stripLeading()); - lineComplete = true; - } - } - - if (lineComplete) { - dslLines.add(new DslLine(buf.toString(), lineNumber)); - buf = new StringBuilder(); - } - - lineNumber++; - } - - return dslLines; + parse(lines, dslFile); } + /** + * Parses a list of Structurizr DSL lines. + * + * @param lines a Structurizr DSL definition, as a List of String objects (one per line) + * @param dslFile a File representing the DSL file, and therefore where includes/images/etc should be loaded relative to + * @throws StructurizrDslParserException when something goes wrong + */ void parse(List lines, File dslFile) throws StructurizrDslParserException { List dslLines = preProcessLines(lines); @@ -930,6 +912,37 @@ void parse(List lines, File dslFile) throws StructurizrDslParserExceptio } } + private List preProcessLines(List lines) { + List dslLines = new ArrayList<>(); + + int lineNumber = 1; + StringBuilder buf = new StringBuilder(); + boolean lineComplete = true; + + for (String line : lines) { + if (line.endsWith(MULTI_LINE_SEPARATOR)) { + buf.append(line, 0, line.length()-1); + lineComplete = false; + } else { + if (lineComplete) { + buf.append(line); + } else { + buf.append(line.stripLeading()); + lineComplete = true; + } + } + + if (lineComplete) { + dslLines.add(new DslLine(buf.toString(), lineNumber)); + buf = new StringBuilder(); + } + + lineNumber++; + } + + return dslLines; + } + private String substituteStrings(String token) { Matcher m = STRING_SUBSTITUTION_PATTERN.matcher(token); while (m.find()) { From c6daf729048115141ebc9ab7cd1994995f4f0863 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 11 Jan 2024 09:09:04 +0000 Subject: [PATCH 449/717] Refactoring of priority enum. --- .../main/java/com/structurizr/assistant/Inspection.java | 6 +++--- .../main/java/com/structurizr/assistant/Priority.java | 9 --------- .../java/com/structurizr/assistant/Recommendation.java | 6 ++++++ .../structurizr/assistant/model/ComponentInspection.java | 2 +- .../structurizr/assistant/model/ContainerInspection.java | 2 +- .../model/ComponentDescriptionInspectionTests.java | 3 +-- .../model/ComponentTechnologyInspectionTests.java | 3 +-- .../model/ContainerDescriptionInspectionTests.java | 3 +-- .../model/ContainerTechnologyInspectionTests.java | 3 +-- .../model/DeploymentNodeDescriptionInspectionTests.java | 3 +-- .../model/DeploymentNodeTechnologyInspectionTests.java | 3 +-- .../ElementNotIncludedInAnyViewsInspectionTests.java | 3 +-- .../assistant/model/EmptyDeploymentNodeCheckTests.java | 3 +-- .../assistant/model/EmptyModelInspectionTests.java | 3 +-- .../InfrastructureNodeDescriptionInspectionTests.java | 3 +-- .../InfrastructureNodeTechnologyInspectionTests.java | 3 +-- .../MultipleSoftwareSystemsDetailedInspectionTests.java | 7 +++---- .../assistant/model/OrphanedElementInspectionTests.java | 3 +-- .../model/PersonDescriptionInspectionTests.java | 3 +-- .../model/RelationshipDescriptionInspectionTests.java | 5 ++--- .../model/RelationshipTechnologyInspectionTests.java | 5 ++--- .../model/SoftwareSystemDescriptionInspectionTests.java | 3 +-- ...erViewsForMultipleSoftwareSystemsInspectionTests.java | 3 +-- .../assistant/view/EmptyViewsInspectionTests.java | 3 +-- ...xtViewsForMultipleSoftwareSystemsInspectionTests.java | 3 +-- .../workspace/WorkspaceScopeInspectionTests.java | 3 +-- 26 files changed, 36 insertions(+), 60 deletions(-) delete mode 100644 structurizr-assistant/src/main/java/com/structurizr/assistant/Priority.java diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/Inspection.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/Inspection.java index 22458a136..23442bea2 100644 --- a/structurizr-assistant/src/main/java/com/structurizr/assistant/Inspection.java +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/Inspection.java @@ -36,15 +36,15 @@ protected Recommendation noRecommendation() { } protected Recommendation lowPriorityRecommendation(String description) { - return new Recommendation(STRUCTURIZR_RECOMMENDATIONS_PREFIX + getType(), Priority.Low, description); + return new Recommendation(STRUCTURIZR_RECOMMENDATIONS_PREFIX + getType(), Recommendation.Priority.Low, description); } protected Recommendation mediumPriorityRecommendation(String description) { - return new Recommendation(STRUCTURIZR_RECOMMENDATIONS_PREFIX + getType(), Priority.Medium, description); + return new Recommendation(STRUCTURIZR_RECOMMENDATIONS_PREFIX + getType(), Recommendation.Priority.Medium, description); } protected Recommendation highPriorityRecommendation(String description) { - return new Recommendation(STRUCTURIZR_RECOMMENDATIONS_PREFIX + getType(), Priority.High, description); + return new Recommendation(STRUCTURIZR_RECOMMENDATIONS_PREFIX + getType(), Recommendation.Priority.High, description); } } \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/Priority.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/Priority.java deleted file mode 100644 index 5a2c3085e..000000000 --- a/structurizr-assistant/src/main/java/com/structurizr/assistant/Priority.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.structurizr.assistant; - -public enum Priority { - - Low, - Medium, - High - -} \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/Recommendation.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/Recommendation.java index 1e35b3c63..11f957724 100644 --- a/structurizr-assistant/src/main/java/com/structurizr/assistant/Recommendation.java +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/Recommendation.java @@ -29,4 +29,10 @@ public String toString() { return type + " | " + priority + " | " + description; } + public enum Priority { + Low, + Medium, + High + } + } \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/model/ComponentInspection.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/ComponentInspection.java index 4c64146f4..f09576439 100644 --- a/structurizr-assistant/src/main/java/com/structurizr/assistant/model/ComponentInspection.java +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/ComponentInspection.java @@ -12,7 +12,7 @@ public ComponentInspection(Workspace workspace) { } @Override - protected Recommendation inspect(Element element) { + protected final Recommendation inspect(Element element) { return inspect((Component)element); } diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/model/ContainerInspection.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/ContainerInspection.java index 4c7dea0fd..5dcebd052 100644 --- a/structurizr-assistant/src/main/java/com/structurizr/assistant/model/ContainerInspection.java +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/ContainerInspection.java @@ -12,7 +12,7 @@ public ContainerInspection(Workspace workspace) { } @Override - protected Recommendation inspect(Element element) { + protected final Recommendation inspect(Element element) { return inspect((Container)element); } diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/ComponentDescriptionInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/ComponentDescriptionInspectionTests.java index eb17e0c0f..5402ae992 100644 --- a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/ComponentDescriptionInspectionTests.java +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/ComponentDescriptionInspectionTests.java @@ -1,7 +1,6 @@ package com.structurizr.assistant.model; import com.structurizr.Workspace; -import com.structurizr.assistant.Priority; import com.structurizr.assistant.Recommendation; import com.structurizr.model.Component; import com.structurizr.model.Container; @@ -22,7 +21,7 @@ public void run_WithoutDescription() { Component component = container.addComponent("Name"); Recommendation recommendation = new ComponentDescriptionInspection(workspace).run(component); - Assertions.assertEquals(Priority.Medium, recommendation.getPriority()); + Assertions.assertEquals(Recommendation.Priority.Medium, recommendation.getPriority()); assertEquals("structurizr.recommendations.model.component.description", recommendation.getType()); assertEquals("Add a description to the component named \"Name\".", recommendation.getDescription()); } diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/ComponentTechnologyInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/ComponentTechnologyInspectionTests.java index f5f1826a2..bd5eee769 100644 --- a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/ComponentTechnologyInspectionTests.java +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/ComponentTechnologyInspectionTests.java @@ -1,7 +1,6 @@ package com.structurizr.assistant.model; import com.structurizr.Workspace; -import com.structurizr.assistant.Priority; import com.structurizr.assistant.Recommendation; import com.structurizr.model.Component; import com.structurizr.model.Container; @@ -22,7 +21,7 @@ public void run_WithoutDescription() { Component component = container.addComponent("Name"); Recommendation recommendation = new ComponentTechnologyInspection(workspace).run(component); - Assertions.assertEquals(Priority.Low, recommendation.getPriority()); + Assertions.assertEquals(Recommendation.Priority.Low, recommendation.getPriority()); assertEquals("structurizr.recommendations.model.component.technology", recommendation.getType()); assertEquals("Add a technology to the component named \"Name\".", recommendation.getDescription()); } diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/ContainerDescriptionInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/ContainerDescriptionInspectionTests.java index 9b0655c04..abd8a2c90 100644 --- a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/ContainerDescriptionInspectionTests.java +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/ContainerDescriptionInspectionTests.java @@ -1,7 +1,6 @@ package com.structurizr.assistant.model; import com.structurizr.Workspace; -import com.structurizr.assistant.Priority; import com.structurizr.assistant.Recommendation; import com.structurizr.model.Container; import com.structurizr.model.SoftwareSystem; @@ -20,7 +19,7 @@ public void run_WithoutDescription() { Container container = softwareSystem.addContainer("Name"); Recommendation recommendation = new ContainerDescriptionInspection(workspace).run(container); - Assertions.assertEquals(Priority.Medium, recommendation.getPriority()); + Assertions.assertEquals(Recommendation.Priority.Medium, recommendation.getPriority()); assertEquals("structurizr.recommendations.model.container.description", recommendation.getType()); assertEquals("Add a description to the container named \"Name\".", recommendation.getDescription()); } diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/ContainerTechnologyInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/ContainerTechnologyInspectionTests.java index 3f2e08854..af1164c2a 100644 --- a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/ContainerTechnologyInspectionTests.java +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/ContainerTechnologyInspectionTests.java @@ -1,7 +1,6 @@ package com.structurizr.assistant.model; import com.structurizr.Workspace; -import com.structurizr.assistant.Priority; import com.structurizr.assistant.Recommendation; import com.structurizr.model.Container; import com.structurizr.model.SoftwareSystem; @@ -20,7 +19,7 @@ public void run_WithoutDescription() { Container container = softwareSystem.addContainer("Name"); Recommendation recommendation = new ContainerTechnologyInspection(workspace).run(container); - Assertions.assertEquals(Priority.Medium, recommendation.getPriority()); + Assertions.assertEquals(Recommendation.Priority.Medium, recommendation.getPriority()); assertEquals("structurizr.recommendations.model.container.technology", recommendation.getType()); assertEquals("Add a technology to the container named \"Name\".", recommendation.getDescription()); } diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/DeploymentNodeDescriptionInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/DeploymentNodeDescriptionInspectionTests.java index e495234e2..5f7ac2cda 100644 --- a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/DeploymentNodeDescriptionInspectionTests.java +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/DeploymentNodeDescriptionInspectionTests.java @@ -1,7 +1,6 @@ package com.structurizr.assistant.model; import com.structurizr.Workspace; -import com.structurizr.assistant.Priority; import com.structurizr.assistant.Recommendation; import com.structurizr.model.DeploymentNode; import org.junit.jupiter.api.Assertions; @@ -18,7 +17,7 @@ public void run_WithoutDescription() { DeploymentNode deploymentNode = workspace.getModel().addDeploymentNode("Name"); Recommendation recommendation = new DeploymentNodeDescriptionInspection(workspace).run(deploymentNode); - Assertions.assertEquals(Priority.Medium, recommendation.getPriority()); + Assertions.assertEquals(Recommendation.Priority.Medium, recommendation.getPriority()); assertEquals("structurizr.recommendations.model.deploymentnode.description", recommendation.getType()); assertEquals("Add a description to the deployment node named \"Name\".", recommendation.getDescription()); } diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/DeploymentNodeTechnologyInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/DeploymentNodeTechnologyInspectionTests.java index 5c0aa5660..ef63a920f 100644 --- a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/DeploymentNodeTechnologyInspectionTests.java +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/DeploymentNodeTechnologyInspectionTests.java @@ -1,7 +1,6 @@ package com.structurizr.assistant.model; import com.structurizr.Workspace; -import com.structurizr.assistant.Priority; import com.structurizr.assistant.Recommendation; import com.structurizr.model.DeploymentNode; import org.junit.jupiter.api.Assertions; @@ -18,7 +17,7 @@ public void run_WithoutDescription() { DeploymentNode deploymentNode = workspace.getModel().addDeploymentNode("Name"); Recommendation recommendation = new DeploymentNodeTechnologyInspection(workspace).run(deploymentNode); - Assertions.assertEquals(Priority.Medium, recommendation.getPriority()); + Assertions.assertEquals(Recommendation.Priority.Medium, recommendation.getPriority()); assertEquals("structurizr.recommendations.model.deploymentnode.technology", recommendation.getType()); assertEquals("Add a technology to the deployment node named \"Name\".", recommendation.getDescription()); } diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/ElementNotIncludedInAnyViewsInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/ElementNotIncludedInAnyViewsInspectionTests.java index d92e2913a..29996f387 100644 --- a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/ElementNotIncludedInAnyViewsInspectionTests.java +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/ElementNotIncludedInAnyViewsInspectionTests.java @@ -1,7 +1,6 @@ package com.structurizr.assistant.model; import com.structurizr.Workspace; -import com.structurizr.assistant.Priority; import com.structurizr.assistant.Recommendation; import com.structurizr.model.SoftwareSystem; import org.junit.jupiter.api.Assertions; @@ -18,7 +17,7 @@ public void run_NotInViews() { SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name"); Recommendation recommendation = new ElementNotIncludedInAnyViewsInspection(workspace).run(softwareSystem); - Assertions.assertEquals(Priority.Low, recommendation.getPriority()); + Assertions.assertEquals(Recommendation.Priority.Low, recommendation.getPriority()); assertEquals("structurizr.recommendations.model.element.noview", recommendation.getType()); assertEquals("The software system named \"Name\" is not included on any views - add it to a view.", recommendation.getDescription()); } diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/EmptyDeploymentNodeCheckTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/EmptyDeploymentNodeCheckTests.java index 2bde35613..20509e17f 100644 --- a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/EmptyDeploymentNodeCheckTests.java +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/EmptyDeploymentNodeCheckTests.java @@ -1,7 +1,6 @@ package com.structurizr.assistant.model; import com.structurizr.Workspace; -import com.structurizr.assistant.Priority; import com.structurizr.assistant.Recommendation; import com.structurizr.model.Container; import com.structurizr.model.DeploymentNode; @@ -20,7 +19,7 @@ public void run_Empty() { DeploymentNode deploymentNode = workspace.getModel().addDeploymentNode("Name"); Recommendation recommendation = new EmptyDeploymentNodeInspection(workspace).run(deploymentNode); - Assertions.assertEquals(Priority.Low, recommendation.getPriority()); + Assertions.assertEquals(Recommendation.Priority.Low, recommendation.getPriority()); assertEquals("structurizr.recommendations.model.deploymentnode.empty", recommendation.getType()); assertEquals("The deployment node named \"Name\" is empty.", recommendation.getDescription()); } diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/EmptyModelInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/EmptyModelInspectionTests.java index e022667e6..3f6e7afe7 100644 --- a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/EmptyModelInspectionTests.java +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/EmptyModelInspectionTests.java @@ -1,7 +1,6 @@ package com.structurizr.assistant.model; import com.structurizr.Workspace; -import com.structurizr.assistant.Priority; import com.structurizr.assistant.Recommendation; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -16,7 +15,7 @@ public void run_WhenThereAreNoElements() { Workspace workspace = new Workspace("Name", "Description"); Recommendation recommendation = new EmptyModelInspection(workspace).run(); - Assertions.assertEquals(Priority.High, recommendation.getPriority()); + Assertions.assertEquals(Recommendation.Priority.High, recommendation.getPriority()); assertEquals("structurizr.recommendations.model.empty", recommendation.getType()); assertEquals("Add some elements to the model.", recommendation.getDescription()); } diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/InfrastructureNodeDescriptionInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/InfrastructureNodeDescriptionInspectionTests.java index b9257dd0c..a74694c76 100644 --- a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/InfrastructureNodeDescriptionInspectionTests.java +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/InfrastructureNodeDescriptionInspectionTests.java @@ -1,7 +1,6 @@ package com.structurizr.assistant.model; import com.structurizr.Workspace; -import com.structurizr.assistant.Priority; import com.structurizr.assistant.Recommendation; import com.structurizr.model.InfrastructureNode; import org.junit.jupiter.api.Assertions; @@ -19,7 +18,7 @@ public void run_WithoutDescription() { .addInfrastructureNode("Name"); Recommendation recommendation = new InfrastructureNodeDescriptionInspection(workspace).run(infrastructureNode); - Assertions.assertEquals(Priority.Medium, recommendation.getPriority()); + Assertions.assertEquals(Recommendation.Priority.Medium, recommendation.getPriority()); assertEquals("structurizr.recommendations.model.infrastructurenode.description", recommendation.getType()); assertEquals("Add a description to the infrastructure node named \"Name\".", recommendation.getDescription()); } diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/InfrastructureNodeTechnologyInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/InfrastructureNodeTechnologyInspectionTests.java index fd990cdae..b22a991f8 100644 --- a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/InfrastructureNodeTechnologyInspectionTests.java +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/InfrastructureNodeTechnologyInspectionTests.java @@ -1,7 +1,6 @@ package com.structurizr.assistant.model; import com.structurizr.Workspace; -import com.structurizr.assistant.Priority; import com.structurizr.assistant.Recommendation; import com.structurizr.model.InfrastructureNode; import org.junit.jupiter.api.Assertions; @@ -19,7 +18,7 @@ public void run_WithoutDescription() { .addInfrastructureNode("Name"); Recommendation recommendation = new InfrastructureNodeTechnologyInspection(workspace).run(infrastructureNode); - Assertions.assertEquals(Priority.Medium, recommendation.getPriority()); + Assertions.assertEquals(Recommendation.Priority.Medium, recommendation.getPriority()); assertEquals("structurizr.recommendations.model.infrastructurenode.technology", recommendation.getType()); assertEquals("Add a technology to the infrastructure node named \"Name\".", recommendation.getDescription()); } diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/MultipleSoftwareSystemsDetailedInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/MultipleSoftwareSystemsDetailedInspectionTests.java index 3dcce3c0d..86e238522 100644 --- a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/MultipleSoftwareSystemsDetailedInspectionTests.java +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/MultipleSoftwareSystemsDetailedInspectionTests.java @@ -1,7 +1,6 @@ package com.structurizr.assistant.model; import com.structurizr.Workspace; -import com.structurizr.assistant.Priority; import com.structurizr.assistant.Recommendation; import com.structurizr.documentation.Decision; import com.structurizr.documentation.Format; @@ -22,7 +21,7 @@ public void run_MultipleSoftwareSystemsWithContainers() { workspace.getModel().addSoftwareSystem("B").addContainer("Container"); Recommendation recommendation = new MultipleSoftwareSystemsDetailedInspection(workspace).run(); - Assertions.assertEquals(Priority.High, recommendation.getPriority()); + Assertions.assertEquals(Recommendation.Priority.High, recommendation.getPriority()); assertEquals("structurizr.recommendations.workspace.scope", recommendation.getType()); assertEquals("This workspace describes the internal details of 2 software systems. It is recommended that a workspace contains the model, views, and documentation for a single software system only.", recommendation.getDescription()); } @@ -34,7 +33,7 @@ public void run_MultipleSoftwareSystemsWithDocumentation() { workspace.getModel().addSoftwareSystem("B").getDocumentation().addSection(new Section(Format.Markdown, "# Section 1")); Recommendation recommendation = new MultipleSoftwareSystemsDetailedInspection(workspace).run(); - Assertions.assertEquals(Priority.High, recommendation.getPriority()); + Assertions.assertEquals(Recommendation.Priority.High, recommendation.getPriority()); assertEquals("structurizr.recommendations.workspace.scope", recommendation.getType()); assertEquals("This workspace describes the internal details of 2 software systems. It is recommended that a workspace contains the model, views, and documentation for a single software system only.", recommendation.getDescription()); } @@ -51,7 +50,7 @@ public void run_MultipleSoftwareSystemsWithDecisions() { workspace.getModel().addSoftwareSystem("B").getDocumentation().addDecision(decision); Recommendation recommendation = new MultipleSoftwareSystemsDetailedInspection(workspace).run(); - Assertions.assertEquals(Priority.High, recommendation.getPriority()); + Assertions.assertEquals(Recommendation.Priority.High, recommendation.getPriority()); assertEquals("structurizr.recommendations.workspace.scope", recommendation.getType()); assertEquals("This workspace describes the internal details of 2 software systems. It is recommended that a workspace contains the model, views, and documentation for a single software system only.", recommendation.getDescription()); } diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/OrphanedElementInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/OrphanedElementInspectionTests.java index a92153dea..04d40014b 100644 --- a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/OrphanedElementInspectionTests.java +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/OrphanedElementInspectionTests.java @@ -1,7 +1,6 @@ package com.structurizr.assistant.model; import com.structurizr.Workspace; -import com.structurizr.assistant.Priority; import com.structurizr.assistant.Recommendation; import com.structurizr.model.SoftwareSystem; import org.junit.jupiter.api.Assertions; @@ -18,7 +17,7 @@ public void run_WithOrphan() { SoftwareSystem a = workspace.getModel().addSoftwareSystem("A"); Recommendation recommendation = new OrphanedElementInspection(workspace).run(a); - Assertions.assertEquals(Priority.Medium, recommendation.getPriority()); + Assertions.assertEquals(Recommendation.Priority.Medium, recommendation.getPriority()); assertEquals("structurizr.recommendations.model.element.orphaned", recommendation.getType()); assertEquals("The software system named \"A\" is orphaned - add a relationship to/from it, or consider removing it from the model.", recommendation.getDescription()); } diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/PersonDescriptionInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/PersonDescriptionInspectionTests.java index 03a4b07e6..e71b68671 100644 --- a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/PersonDescriptionInspectionTests.java +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/PersonDescriptionInspectionTests.java @@ -1,7 +1,6 @@ package com.structurizr.assistant.model; import com.structurizr.Workspace; -import com.structurizr.assistant.Priority; import com.structurizr.assistant.Recommendation; import com.structurizr.model.Person; import org.junit.jupiter.api.Assertions; @@ -18,7 +17,7 @@ public void run_WithoutDescription() { Person person = workspace.getModel().addPerson("Name"); Recommendation recommendation = new PersonDescriptionInspection(workspace).run(person); - Assertions.assertEquals(Priority.Medium, recommendation.getPriority()); + Assertions.assertEquals(Recommendation.Priority.Medium, recommendation.getPriority()); assertEquals("structurizr.recommendations.model.person.description", recommendation.getType()); assertEquals("Add a description to the person named \"Name\".", recommendation.getDescription()); } diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/RelationshipDescriptionInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/RelationshipDescriptionInspectionTests.java index 69b46f4af..d17de1c38 100644 --- a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/RelationshipDescriptionInspectionTests.java +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/RelationshipDescriptionInspectionTests.java @@ -1,7 +1,6 @@ package com.structurizr.assistant.model; import com.structurizr.Workspace; -import com.structurizr.assistant.Priority; import com.structurizr.assistant.Recommendation; import com.structurizr.model.Component; import com.structurizr.model.Container; @@ -23,7 +22,7 @@ public void run_WithoutDescription_MediumPriority() { Relationship relationship = a.uses(b, ""); Recommendation recommendation = new RelationshipDescriptionInspection(workspace).run(relationship); - Assertions.assertEquals(Priority.Medium, recommendation.getPriority()); + Assertions.assertEquals(Recommendation.Priority.Medium, recommendation.getPriority()); assertEquals("structurizr.recommendations.model.relationship.description", recommendation.getType()); assertEquals("Add a description to the relationship between the software system named \"A\" and the software system named \"B\".", recommendation.getDescription()); } @@ -38,7 +37,7 @@ public void run_WithoutDescription_LowPriority() { Relationship relationship = a.uses(b, ""); Recommendation recommendation = new RelationshipDescriptionInspection(workspace).run(relationship); - Assertions.assertEquals(Priority.Low, recommendation.getPriority()); + Assertions.assertEquals(Recommendation.Priority.Low, recommendation.getPriority()); assertEquals("structurizr.recommendations.model.relationship.description", recommendation.getType()); assertEquals("Add a description to the relationship between the component named \"A\" and the component named \"B\".", recommendation.getDescription()); } diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/RelationshipTechnologyInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/RelationshipTechnologyInspectionTests.java index 2fe68d6c5..d956cf29e 100644 --- a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/RelationshipTechnologyInspectionTests.java +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/RelationshipTechnologyInspectionTests.java @@ -1,7 +1,6 @@ package com.structurizr.assistant.model; import com.structurizr.Workspace; -import com.structurizr.assistant.Priority; import com.structurizr.assistant.Recommendation; import com.structurizr.model.Container; import com.structurizr.model.Relationship; @@ -22,7 +21,7 @@ public void run_WithoutDescription_LowPriority() { Relationship relationship = a.uses(b, "Description"); Recommendation recommendation = new RelationshipTechnologyInspection(workspace).run(relationship); - Assertions.assertEquals(Priority.Low, recommendation.getPriority()); + Assertions.assertEquals(Recommendation.Priority.Low, recommendation.getPriority()); assertEquals("structurizr.recommendations.model.relationship.technology", recommendation.getType()); assertEquals("Add a technology to the relationship between the software system named \"A\" and the software system named \"B\".", recommendation.getDescription()); } @@ -36,7 +35,7 @@ public void run_WithoutDescription_MediumPriority() { Relationship relationship = a.uses(b, "Description"); Recommendation recommendation = new RelationshipTechnologyInspection(workspace).run(relationship); - Assertions.assertEquals(Priority.Medium, recommendation.getPriority()); + Assertions.assertEquals(Recommendation.Priority.Medium, recommendation.getPriority()); assertEquals("structurizr.recommendations.model.relationship.technology", recommendation.getType()); assertEquals("Add a technology to the relationship between the container named \"A\" and the container named \"B\".", recommendation.getDescription()); } diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/SoftwareSystemDescriptionInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/SoftwareSystemDescriptionInspectionTests.java index 49389b30d..49242a971 100644 --- a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/SoftwareSystemDescriptionInspectionTests.java +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/SoftwareSystemDescriptionInspectionTests.java @@ -1,7 +1,6 @@ package com.structurizr.assistant.model; import com.structurizr.Workspace; -import com.structurizr.assistant.Priority; import com.structurizr.assistant.Recommendation; import com.structurizr.model.SoftwareSystem; import org.junit.jupiter.api.Assertions; @@ -18,7 +17,7 @@ public void run_WithoutDescription() { SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name"); Recommendation recommendation = new SoftwareSystemDescriptionInspection(workspace).run(softwareSystem); - Assertions.assertEquals(Priority.Medium, recommendation.getPriority()); + Assertions.assertEquals(Recommendation.Priority.Medium, recommendation.getPriority()); assertEquals("structurizr.recommendations.model.softwaresystem.description", recommendation.getType()); assertEquals("Add a description to the software system named \"Name\".", recommendation.getDescription()); } diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/view/ContainerViewsForMultipleSoftwareSystemsInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/view/ContainerViewsForMultipleSoftwareSystemsInspectionTests.java index 8b389b481..92ef519a6 100644 --- a/structurizr-assistant/src/test/java/com/structurizr/assistant/view/ContainerViewsForMultipleSoftwareSystemsInspectionTests.java +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/view/ContainerViewsForMultipleSoftwareSystemsInspectionTests.java @@ -1,7 +1,6 @@ package com.structurizr.assistant.view; import com.structurizr.Workspace; -import com.structurizr.assistant.Priority; import com.structurizr.assistant.Recommendation; import com.structurizr.model.SoftwareSystem; import org.junit.jupiter.api.Assertions; @@ -21,7 +20,7 @@ public void run_MultipleSoftwareSystems() { workspace.getViews().createContainerView(b, "Containers-B", "Description"); Recommendation recommendation = new ContainerViewsForMultipleSoftwareSystemsInspection(workspace).run(); - Assertions.assertEquals(Priority.High, recommendation.getPriority()); + Assertions.assertEquals(Recommendation.Priority.High, recommendation.getPriority()); assertEquals("structurizr.recommendations.workspace.scope", recommendation.getType()); assertEquals("Container views exist for 2 software systems. It is recommended that a workspace includes container views for a single software system only.", recommendation.getDescription()); } diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/view/EmptyViewsInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/view/EmptyViewsInspectionTests.java index 2d5c9b40c..977ee83c4 100644 --- a/structurizr-assistant/src/test/java/com/structurizr/assistant/view/EmptyViewsInspectionTests.java +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/view/EmptyViewsInspectionTests.java @@ -1,7 +1,6 @@ package com.structurizr.assistant.view; import com.structurizr.Workspace; -import com.structurizr.assistant.Priority; import com.structurizr.assistant.Recommendation; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -16,7 +15,7 @@ public void run_WhenThereAreNoViews() { Workspace workspace = new Workspace("Name", "Description"); Recommendation recommendation = new EmptyViewsInspection(workspace).run(); - Assertions.assertEquals(Priority.High, recommendation.getPriority()); + Assertions.assertEquals(Recommendation.Priority.High, recommendation.getPriority()); assertEquals("structurizr.recommendations.views.empty", recommendation.getType()); assertEquals("Add some views to the workspace.", recommendation.getDescription()); } diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/view/SystemContextViewsForMultipleSoftwareSystemsInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/view/SystemContextViewsForMultipleSoftwareSystemsInspectionTests.java index 48ec21171..331af6966 100644 --- a/structurizr-assistant/src/test/java/com/structurizr/assistant/view/SystemContextViewsForMultipleSoftwareSystemsInspectionTests.java +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/view/SystemContextViewsForMultipleSoftwareSystemsInspectionTests.java @@ -1,7 +1,6 @@ package com.structurizr.assistant.view; import com.structurizr.Workspace; -import com.structurizr.assistant.Priority; import com.structurizr.assistant.Recommendation; import com.structurizr.model.SoftwareSystem; import org.junit.jupiter.api.Assertions; @@ -21,7 +20,7 @@ public void run_MultipleSoftwareSystems() { workspace.getViews().createSystemContextView(b, "SystemContext-B", "Description"); Recommendation recommendation = new SystemContextViewsForMultipleSoftwareSystemsInspection(workspace).run(); - Assertions.assertEquals(Priority.High, recommendation.getPriority()); + Assertions.assertEquals(Recommendation.Priority.High, recommendation.getPriority()); assertEquals("structurizr.recommendations.workspace.scope", recommendation.getType()); assertEquals("System context views exist for 2 software systems. It is recommended that a workspace includes system context views for a single software system only.", recommendation.getDescription()); } diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/workspace/WorkspaceScopeInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/workspace/WorkspaceScopeInspectionTests.java index d48e93c77..10df6726b 100644 --- a/structurizr-assistant/src/test/java/com/structurizr/assistant/workspace/WorkspaceScopeInspectionTests.java +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/workspace/WorkspaceScopeInspectionTests.java @@ -1,7 +1,6 @@ package com.structurizr.assistant.workspace; import com.structurizr.Workspace; -import com.structurizr.assistant.Priority; import com.structurizr.assistant.Recommendation; import com.structurizr.configuration.WorkspaceScope; import org.junit.jupiter.api.Assertions; @@ -17,7 +16,7 @@ public void run_WithUnscopedWorkspace() { Workspace workspace = new Workspace("Name", "Description"); Recommendation recommendation = new WorkspaceScopeInspection(workspace).run(); - Assertions.assertEquals(Priority.High, recommendation.getPriority()); + Assertions.assertEquals(Recommendation.Priority.High, recommendation.getPriority()); assertEquals("structurizr.recommendations.workspace.scope", recommendation.getType()); assertEquals("This workspace has no defined scope. It is recommended that the workspace scope is set to \"Landscape\" or \"SoftwareSystem\".", recommendation.getDescription()); } From 0de6e7c197cb8455c345de818bf6fb19b9a666e3 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 11 Jan 2024 09:11:04 +0000 Subject: [PATCH 450/717] Adds documentation/decisions inspections for software systems that have containers. --- .../assistant/DefaultAssistant.java | 2 + .../SoftwareSystemDecisionsInspection.java | 27 +++++++++++ ...SoftwareSystemDocumentationInspection.java | 27 +++++++++++ .../model/SoftwareSystemInspection.java | 22 +++++++++ ...SoftwareSystemDecisionInspectionTests.java | 47 +++++++++++++++++++ ...areSystemDocumentationInspectionTests.java | 40 ++++++++++++++++ 6 files changed, 165 insertions(+) create mode 100644 structurizr-assistant/src/main/java/com/structurizr/assistant/model/SoftwareSystemDecisionsInspection.java create mode 100644 structurizr-assistant/src/main/java/com/structurizr/assistant/model/SoftwareSystemDocumentationInspection.java create mode 100644 structurizr-assistant/src/main/java/com/structurizr/assistant/model/SoftwareSystemInspection.java create mode 100644 structurizr-assistant/src/test/java/com/structurizr/assistant/model/SoftwareSystemDecisionInspectionTests.java create mode 100644 structurizr-assistant/src/test/java/com/structurizr/assistant/model/SoftwareSystemDocumentationInspectionTests.java diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/DefaultAssistant.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/DefaultAssistant.java index ec1114489..4af07a797 100644 --- a/structurizr-assistant/src/main/java/com/structurizr/assistant/DefaultAssistant.java +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/DefaultAssistant.java @@ -42,6 +42,8 @@ private void runModelInspections(Workspace workspace) { if (element instanceof SoftwareSystem) { add(new SoftwareSystemDescriptionInspection(workspace).run(element)); + add(new SoftwareSystemDocumentationInspection(workspace).run(element)); + add(new SoftwareSystemDecisionsInspection(workspace).run(element)); } if (element instanceof Container) { diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/model/SoftwareSystemDecisionsInspection.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/SoftwareSystemDecisionsInspection.java new file mode 100644 index 000000000..7705e844d --- /dev/null +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/SoftwareSystemDecisionsInspection.java @@ -0,0 +1,27 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Recommendation; +import com.structurizr.model.SoftwareSystem; + +public class SoftwareSystemDecisionsInspection extends SoftwareSystemInspection { + + public SoftwareSystemDecisionsInspection(Workspace workspace) { + super(workspace); + } + + @Override + protected Recommendation inspect(SoftwareSystem softwareSystem) { + if (softwareSystem.hasContainers() && softwareSystem.getDocumentation().getDecisions().isEmpty()) { + return highPriorityRecommendation("The " + terminologyFor(softwareSystem).toLowerCase() + " named \"" + softwareSystem.getName() + "\" has containers, but is missing decisions."); + } + + return noRecommendation(); + } + + @Override + protected String getType() { + return "model.softwaresystem.decisions"; + } + +} \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/model/SoftwareSystemDocumentationInspection.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/SoftwareSystemDocumentationInspection.java new file mode 100644 index 000000000..bca6ac926 --- /dev/null +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/SoftwareSystemDocumentationInspection.java @@ -0,0 +1,27 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Recommendation; +import com.structurizr.model.SoftwareSystem; + +public class SoftwareSystemDocumentationInspection extends SoftwareSystemInspection { + + public SoftwareSystemDocumentationInspection(Workspace workspace) { + super(workspace); + } + + @Override + protected Recommendation inspect(SoftwareSystem softwareSystem) { + if (softwareSystem.hasContainers() && softwareSystem.getDocumentation().getSections().isEmpty()) { + return highPriorityRecommendation("The " + terminologyFor(softwareSystem).toLowerCase() + " named \"" + softwareSystem.getName() + "\" has containers, but is missing documentation."); + } + + return noRecommendation(); + } + + @Override + protected String getType() { + return "model.softwaresystem.documentation"; + } + +} \ No newline at end of file diff --git a/structurizr-assistant/src/main/java/com/structurizr/assistant/model/SoftwareSystemInspection.java b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/SoftwareSystemInspection.java new file mode 100644 index 000000000..4ce0dea23 --- /dev/null +++ b/structurizr-assistant/src/main/java/com/structurizr/assistant/model/SoftwareSystemInspection.java @@ -0,0 +1,22 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Recommendation; +import com.structurizr.model.Container; +import com.structurizr.model.Element; +import com.structurizr.model.SoftwareSystem; + +abstract class SoftwareSystemInspection extends ElementInspection { + + public SoftwareSystemInspection(Workspace workspace) { + super(workspace); + } + + @Override + protected final Recommendation inspect(Element element) { + return inspect((SoftwareSystem)element); + } + + protected abstract Recommendation inspect(SoftwareSystem softwareSystem); + +} \ No newline at end of file diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/SoftwareSystemDecisionInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/SoftwareSystemDecisionInspectionTests.java new file mode 100644 index 000000000..1e79c4a68 --- /dev/null +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/SoftwareSystemDecisionInspectionTests.java @@ -0,0 +1,47 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Recommendation; +import com.structurizr.documentation.Decision; +import com.structurizr.documentation.Format; +import com.structurizr.documentation.Section; +import com.structurizr.model.Container; +import com.structurizr.model.SoftwareSystem; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class SoftwareSystemDecisionInspectionTests { + + @Test + public void run_WithoutDecision() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Name"); + + Recommendation recommendation = new SoftwareSystemDecisionsInspection(workspace).run(softwareSystem); + Assertions.assertEquals(Recommendation.Priority.High, recommendation.getPriority()); + assertEquals("structurizr.recommendations.model.softwaresystem.decisions", recommendation.getType()); + assertEquals("The software system named \"Software System\" has containers, but is missing decisions.", recommendation.getDescription()); + } + + @Test + public void run_WithDecision() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Name", "Description"); + + Decision decision = new Decision("1"); + decision.setFormat(Format.Markdown); + decision.setTitle("Decision 1"); + decision.setContent("Content"); + decision.setStatus("Accepted"); + softwareSystem.getDocumentation().addDecision(decision); + + Recommendation recommendation = new SoftwareSystemDecisionsInspection(workspace).run(softwareSystem); + assertNull(recommendation); + } + +} diff --git a/structurizr-assistant/src/test/java/com/structurizr/assistant/model/SoftwareSystemDocumentationInspectionTests.java b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/SoftwareSystemDocumentationInspectionTests.java new file mode 100644 index 000000000..f0a8b34ad --- /dev/null +++ b/structurizr-assistant/src/test/java/com/structurizr/assistant/model/SoftwareSystemDocumentationInspectionTests.java @@ -0,0 +1,40 @@ +package com.structurizr.assistant.model; + +import com.structurizr.Workspace; +import com.structurizr.assistant.Recommendation; +import com.structurizr.documentation.Format; +import com.structurizr.documentation.Section; +import com.structurizr.model.Container; +import com.structurizr.model.SoftwareSystem; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class SoftwareSystemDocumentationInspectionTests { + + @Test + public void run_WithoutDocumentation() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Name"); + + Recommendation recommendation = new SoftwareSystemDocumentationInspection(workspace).run(softwareSystem); + Assertions.assertEquals(Recommendation.Priority.High, recommendation.getPriority()); + assertEquals("structurizr.recommendations.model.softwaresystem.documentation", recommendation.getType()); + assertEquals("The software system named \"Software System\" has containers, but is missing documentation.", recommendation.getDescription()); + } + + @Test + public void run_WithDocumentation() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Name", "Description"); + softwareSystem.getDocumentation().addSection(new Section(Format.Markdown, "# Section 1")); + + Recommendation recommendation = new SoftwareSystemDocumentationInspection(workspace).run(softwareSystem); + assertNull(recommendation); + } + +} From 08e4434d044fd3e2195ac0efebf86551891bf7cd Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 11 Jan 2024 13:01:21 +0000 Subject: [PATCH 451/717] Adds support for decisions managed by Log4brains, also adds `!decisions` as a synonym for `!adrs`. --- changelog.md | 2 + .../structurizr/dsl/ComponentDslContext.java | 2 + .../structurizr/dsl/ContainerDslContext.java | 2 +- .../{AdrsParser.java => DecisionsParser.java} | 33 ++- .../dsl/SoftwareSystemDslContext.java | 2 +- .../structurizr/dsl/StructurizrDslParser.java | 16 +- .../structurizr/dsl/StructurizrDslTokens.java | 1 + .../structurizr/dsl/WorkspaceDslContext.java | 2 +- ...erTests.java => DecisionsParserTests.java} | 8 +- .../java/com/structurizr/dsl/DslTests.java | 15 +- .../0001-record-architecture-decisions.md | 0 .../0002-implement-as-shell-scripts.md | 0 .../0003-single-command-with-subcommands.md | 0 .../adrtools}/0004-markdown-format.md | 0 .../adrtools}/0005-help-comments.md | 0 ...n-in-other-version-control-repositories.md | 0 ...-config-executable-to-get-configuration.md | 0 .../0008-use-iso-8601-format-for-dates.md | 0 .../adrtools}/0009-help-scripts.md | 0 .../adrtools}/0010-asciidoc-format.md | 0 ...40111-use-log4brains-to-manage-the-adrs.md | 22 ++ ...markdown-architectural-decision-records.md | 42 ++++ .../log4brains/20240113-decision-3.md | 7 + .../log4brains/20240114-decision-4.md | 11 + .../dsl/{adrs => decisions}/workspace.dsl | 8 +- .../AbstractDecisionImporter.java | 72 ++++++ .../AdrToolsDecisionImporter.java | 24 +- .../Log4brainsDecisionImporter.java | 209 ++++++++++++++++ .../AdrToolsDecisionImporterTests.java | 8 +- .../Log4brainsDecisionImporterTests.java | 232 ++++++++++++++++++ .../0001-record-architecture-decisions.md | 0 .../0002-implement-as-shell-scripts.md | 0 .../0003-single-command-with-subcommands.md | 0 .../adrtools}/0004-markdown-format.md | 0 .../adrtools}/0005-help-comments.md | 0 ...n-in-other-version-control-repositories.md | 0 ...-config-executable-to-get-configuration.md | 0 .../0008-use-iso-8601-format-for-dates.md | 0 .../adrtools}/0009-help-scripts.md | 0 ...40111-use-log4brains-to-manage-the-adrs.md | 22 ++ ...markdown-architectural-decision-records.md | 42 ++++ .../log4brains/20240113-decision-3.md | 7 + .../log4brains/20240114-decision-4.md | 11 + 43 files changed, 740 insertions(+), 60 deletions(-) rename structurizr-dsl/src/main/java/com/structurizr/dsl/{AdrsParser.java => DecisionsParser.java} (61%) rename structurizr-dsl/src/test/java/com/structurizr/dsl/{AdrsParserTests.java => DecisionsParserTests.java} (61%) rename structurizr-dsl/src/test/resources/dsl/{adrs/adrs => decisions/adrtools}/0001-record-architecture-decisions.md (100%) rename structurizr-dsl/src/test/resources/dsl/{adrs/adrs => decisions/adrtools}/0002-implement-as-shell-scripts.md (100%) rename structurizr-dsl/src/test/resources/dsl/{adrs/adrs => decisions/adrtools}/0003-single-command-with-subcommands.md (100%) rename structurizr-dsl/src/test/resources/dsl/{adrs/adrs => decisions/adrtools}/0004-markdown-format.md (100%) rename structurizr-dsl/src/test/resources/dsl/{adrs/adrs => decisions/adrtools}/0005-help-comments.md (100%) rename structurizr-dsl/src/test/resources/dsl/{adrs/adrs => decisions/adrtools}/0006-packaging-and-distribution-in-other-version-control-repositories.md (100%) rename structurizr-dsl/src/test/resources/dsl/{adrs/adrs => decisions/adrtools}/0007-invoke-adr-config-executable-to-get-configuration.md (100%) rename structurizr-dsl/src/test/resources/dsl/{adrs/adrs => decisions/adrtools}/0008-use-iso-8601-format-for-dates.md (100%) rename structurizr-dsl/src/test/resources/dsl/{adrs/adrs => decisions/adrtools}/0009-help-scripts.md (100%) rename structurizr-dsl/src/test/resources/dsl/{adrs/adrs => decisions/adrtools}/0010-asciidoc-format.md (100%) create mode 100644 structurizr-dsl/src/test/resources/dsl/decisions/log4brains/20240111-use-log4brains-to-manage-the-adrs.md create mode 100644 structurizr-dsl/src/test/resources/dsl/decisions/log4brains/20240111-use-markdown-architectural-decision-records.md create mode 100644 structurizr-dsl/src/test/resources/dsl/decisions/log4brains/20240113-decision-3.md create mode 100644 structurizr-dsl/src/test/resources/dsl/decisions/log4brains/20240114-decision-4.md rename structurizr-dsl/src/test/resources/dsl/{adrs => decisions}/workspace.dsl (52%) create mode 100644 structurizr-import/src/main/java/com/structurizr/importer/documentation/AbstractDecisionImporter.java create mode 100644 structurizr-import/src/main/java/com/structurizr/importer/documentation/Log4brainsDecisionImporter.java create mode 100644 structurizr-import/src/test/java/com/structurizr/importer/documentation/Log4brainsDecisionImporterTests.java rename structurizr-import/src/test/resources/{adrs => decisions/adrtools}/0001-record-architecture-decisions.md (100%) rename structurizr-import/src/test/resources/{adrs => decisions/adrtools}/0002-implement-as-shell-scripts.md (100%) rename structurizr-import/src/test/resources/{adrs => decisions/adrtools}/0003-single-command-with-subcommands.md (100%) rename structurizr-import/src/test/resources/{adrs => decisions/adrtools}/0004-markdown-format.md (100%) rename structurizr-import/src/test/resources/{adrs => decisions/adrtools}/0005-help-comments.md (100%) rename structurizr-import/src/test/resources/{adrs => decisions/adrtools}/0006-packaging-and-distribution-in-other-version-control-repositories.md (100%) rename structurizr-import/src/test/resources/{adrs => decisions/adrtools}/0007-invoke-adr-config-executable-to-get-configuration.md (100%) rename structurizr-import/src/test/resources/{adrs => decisions/adrtools}/0008-use-iso-8601-format-for-dates.md (100%) rename structurizr-import/src/test/resources/{adrs => decisions/adrtools}/0009-help-scripts.md (100%) create mode 100644 structurizr-import/src/test/resources/decisions/log4brains/20240111-use-log4brains-to-manage-the-adrs.md create mode 100644 structurizr-import/src/test/resources/decisions/log4brains/20240111-use-markdown-architectural-decision-records.md create mode 100644 structurizr-import/src/test/resources/decisions/log4brains/20240113-decision-3.md create mode 100644 structurizr-import/src/test/resources/decisions/log4brains/20240114-decision-4.md diff --git a/changelog.md b/changelog.md index dc9cdf4bd..40c04c51c 100644 --- a/changelog.md +++ b/changelog.md @@ -5,5 +5,7 @@ - Removes the deprecated concepts (e.g. location and enterprise. - structurizr-core: Adds `Workspace.trim()` to trim a workspace of unused elements (i.e. those not associated with any views). - structurizr-client: Removes `StructurizrClient` (use `WorkspaceApiClient` instead). +- structurizr-import: Adds support for importing decisions managed by Log4brains. +- structurizr-dsl: Adds `!decisions` as a synonym for `!adrs`. - structurizr-assistant: Initial version. diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentDslContext.java index 39e079084..d6e5da57f 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentDslContext.java @@ -29,6 +29,8 @@ GroupableElement getElement() { @Override protected String[] getPermittedTokens() { return new String[] { + StructurizrDslTokens.DOCS_TOKEN, + StructurizrDslTokens.DECISIONS_TOKEN, StructurizrDslTokens.DESCRIPTION_TOKEN, StructurizrDslTokens.TECHNOLOGY_TOKEN, StructurizrDslTokens.TAGS_TOKEN, diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerDslContext.java index 2099a3b15..6a30bf792 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerDslContext.java @@ -36,7 +36,7 @@ GroupableElement getElement() { protected String[] getPermittedTokens() { return new String[] { StructurizrDslTokens.DOCS_TOKEN, - StructurizrDslTokens.ADRS_TOKEN, + StructurizrDslTokens.DECISIONS_TOKEN, StructurizrDslTokens.GROUP_TOKEN, StructurizrDslTokens.COMPONENT_TOKEN, StructurizrDslTokens.DESCRIPTION_TOKEN, diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/AdrsParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DecisionsParser.java similarity index 61% rename from structurizr-dsl/src/main/java/com/structurizr/dsl/AdrsParser.java rename to structurizr-dsl/src/main/java/com/structurizr/dsl/DecisionsParser.java index 110e23462..5557d07d0 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/AdrsParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DecisionsParser.java @@ -6,15 +6,25 @@ import java.io.File; import java.lang.reflect.Constructor; +import java.util.HashMap; +import java.util.Map; -final class AdrsParser extends AbstractParser { +final class DecisionsParser extends AbstractParser { - private static final String DEFAULT_DECISION_IMPORTER = "com.structurizr.importer.documentation.AdrToolsDecisionImporter"; + private static final Map DECISION_IMPORTERS = new HashMap<>(); - private static final String GRAMMAR = "!adrs "; + private static final String ADRTOOLS_DECISION_IMPORTER = "adrtools"; + private static final String LOG4BRAINS_DECISION_IMPORTER = "log4brains"; + + static { + DECISION_IMPORTERS.put(ADRTOOLS_DECISION_IMPORTER, "com.structurizr.importer.documentation.AdrToolsDecisionImporter"); + DECISION_IMPORTERS.put(LOG4BRAINS_DECISION_IMPORTER, "com.structurizr.importer.documentation.Log4brainsDecisionImporter"); + } + + private static final String GRAMMAR = "!decisions "; private static final int PATH_INDEX = 1; - private static final int FQN_INDEX = 2; + private static final int TYPE_OR_FQN_INDEX = 2; void parse(WorkspaceDslContext context, File dslFile, Tokens tokens) { parse(context, context.getWorkspace(), dslFile, tokens); @@ -35,7 +45,7 @@ void parse(ComponentDslContext context, File dslFile, Tokens tokens) { private void parse(DslContext context, Documentable documentable, File dslFile, Tokens tokens) { // !adrs - if (tokens.hasMoreThan(FQN_INDEX)) { + if (tokens.hasMoreThan(TYPE_OR_FQN_INDEX)) { throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); } @@ -43,9 +53,10 @@ private void parse(DslContext context, Documentable documentable, File dslFile, throw new RuntimeException("Expected: " + GRAMMAR); } - String fullyQualifiedClassName = DEFAULT_DECISION_IMPORTER; - if (tokens.includes(FQN_INDEX)) { - fullyQualifiedClassName = tokens.get(FQN_INDEX); + String fullyQualifiedClassName = DECISION_IMPORTERS.get(ADRTOOLS_DECISION_IMPORTER); + if (tokens.includes(TYPE_OR_FQN_INDEX)) { + String typeOrFullyQualifiedName = tokens.get(TYPE_OR_FQN_INDEX); + fullyQualifiedClassName = DECISION_IMPORTERS.getOrDefault(typeOrFullyQualifiedName, typeOrFullyQualifiedName); } if (dslFile != null) { @@ -65,14 +76,14 @@ private void parse(DslContext context, Documentable documentable, File dslFile, DocumentationImporter decisionImporter = (DocumentationImporter)constructor.newInstance(); decisionImporter.importDocumentation(documentable, path); - if (!tokens.includes(FQN_INDEX)) { + if (!tokens.includes(TYPE_OR_FQN_INDEX)) { DefaultImageImporter imageImporter = new DefaultImageImporter(); imageImporter.importDocumentation(documentable, path); } } catch (ClassNotFoundException cnfe) { - throw new RuntimeException("Error importing ADRs from " + path.getAbsolutePath() + ": " + fullyQualifiedClassName + " was not found"); + throw new RuntimeException("Error importing decisions from " + path.getAbsolutePath() + ": " + fullyQualifiedClassName + " was not found"); } catch (Exception e) { - throw new RuntimeException("Error importing ADRs from " + path.getAbsolutePath() + ": " + e.getMessage()); + throw new RuntimeException("Error importing decisions from " + path.getAbsolutePath() + ": " + e.getMessage()); } } } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemDslContext.java index e85280d0c..d86cbdaeb 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemDslContext.java @@ -36,7 +36,7 @@ GroupableElement getElement() { protected String[] getPermittedTokens() { return new String[] { StructurizrDslTokens.DOCS_TOKEN, - StructurizrDslTokens.ADRS_TOKEN, + StructurizrDslTokens.DECISIONS_TOKEN, StructurizrDslTokens.GROUP_TOKEN, StructurizrDslTokens.CONTAINER_TOKEN, StructurizrDslTokens.DESCRIPTION_TOKEN, diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index 8bfa5bc9e..f90a0fb9d 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -844,24 +844,24 @@ void parse(List lines, File dslFile) throws StructurizrDslParserExceptio new DocsParser().parse(getContext(ComponentDslContext.class), dslFile, tokens); } - } else if (ADRS_TOKEN.equalsIgnoreCase(firstToken) && inContext(WorkspaceDslContext.class)) { + } else if ((ADRS_TOKEN.equalsIgnoreCase(firstToken) || DECISIONS_TOKEN.equalsIgnoreCase(firstToken)) && inContext(WorkspaceDslContext.class)) { if (!restricted) { - new AdrsParser().parse(getContext(WorkspaceDslContext.class), dslFile, tokens); + new DecisionsParser().parse(getContext(WorkspaceDslContext.class), dslFile, tokens); } - } else if (ADRS_TOKEN.equalsIgnoreCase(firstToken) && inContext(SoftwareSystemDslContext.class)) { + } else if ((ADRS_TOKEN.equalsIgnoreCase(firstToken) || DECISIONS_TOKEN.equalsIgnoreCase(firstToken)) && inContext(SoftwareSystemDslContext.class)) { if (!restricted) { - new AdrsParser().parse(getContext(SoftwareSystemDslContext.class), dslFile, tokens); + new DecisionsParser().parse(getContext(SoftwareSystemDslContext.class), dslFile, tokens); } - } else if (ADRS_TOKEN.equalsIgnoreCase(firstToken) && inContext(ContainerDslContext.class)) { + } else if ((ADRS_TOKEN.equalsIgnoreCase(firstToken) || DECISIONS_TOKEN.equalsIgnoreCase(firstToken)) && inContext(ContainerDslContext.class)) { if (!restricted) { - new AdrsParser().parse(getContext(ContainerDslContext.class), dslFile, tokens); + new DecisionsParser().parse(getContext(ContainerDslContext.class), dslFile, tokens); } - } else if (ADRS_TOKEN.equalsIgnoreCase(firstToken) && inContext(ComponentDslContext.class)) { + } else if ((ADRS_TOKEN.equalsIgnoreCase(firstToken) || DECISIONS_TOKEN.equalsIgnoreCase(firstToken)) && inContext(ComponentDslContext.class)) { if (!restricted) { - new AdrsParser().parse(getContext(ComponentDslContext.class), dslFile, tokens); + new DecisionsParser().parse(getContext(ComponentDslContext.class), dslFile, tokens); } } else if (CONSTANT_TOKEN.equalsIgnoreCase(firstToken)) { diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java index acae127aa..98e031292 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java @@ -97,6 +97,7 @@ class StructurizrDslTokens { static final String INCLUDE_FILE_TOKEN = "!include"; static final String DOCS_TOKEN = "!docs"; static final String ADRS_TOKEN = "!adrs"; + static final String DECISIONS_TOKEN = "!decisions"; static final String CONSTANT_TOKEN = "!constant"; static final String IDENTIFIERS_TOKEN = "!identifiers"; static final String IMPLIED_RELATIONSHIPS_TOKEN = "!impliedRelationships"; diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/WorkspaceDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/WorkspaceDslContext.java index 5a5ab7c4e..74fbbe6f8 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/WorkspaceDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/WorkspaceDslContext.java @@ -9,7 +9,7 @@ protected String[] getPermittedTokens() { StructurizrDslTokens.DESCRIPTION_TOKEN, StructurizrDslTokens.PROPERTIES_TOKEN, StructurizrDslTokens.DOCS_TOKEN, - StructurizrDslTokens.ADRS_TOKEN, + StructurizrDslTokens.DECISIONS_TOKEN, StructurizrDslTokens.IDENTIFIERS_TOKEN, StructurizrDslTokens.IMPLIED_RELATIONSHIPS_TOKEN, StructurizrDslTokens.MODEL_TOKEN, diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/AdrsParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DecisionsParserTests.java similarity index 61% rename from structurizr-dsl/src/test/java/com/structurizr/dsl/AdrsParserTests.java rename to structurizr-dsl/src/test/java/com/structurizr/dsl/DecisionsParserTests.java index 240a3a621..e1c913609 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/AdrsParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DecisionsParserTests.java @@ -5,17 +5,17 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; -class AdrsParserTests extends AbstractTests { +class DecisionsParserTests extends AbstractTests { - private AdrsParser parser = new AdrsParser(); + private final DecisionsParser parser = new DecisionsParser(); @Test void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { try { - parser.parse(new WorkspaceDslContext(), null, tokens("adrs", "path", "fqn", "extra")); + parser.parse(new WorkspaceDslContext(), null, tokens("decisions", "path", "fqn", "extra")); fail(); } catch (Exception e) { - assertEquals("Too many tokens, expected: !adrs ", e.getMessage()); + assertEquals("Too many tokens, expected: !decisions ", e.getMessage()); } } diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index ed76f3eff..4394875a7 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -697,18 +697,21 @@ void test_docs() throws Exception { } @Test - void test_adrs() throws Exception { + void test_decisions() throws Exception { StructurizrDslParser parser = new StructurizrDslParser(); - parser.parse(new File("src/test/resources/dsl/adrs/workspace.dsl")); + parser.parse(new File("src/test/resources/dsl/decisions/workspace.dsl")); SoftwareSystem softwareSystem = parser.getWorkspace().getModel().getSoftwareSystemWithName("Software System"); Container container = softwareSystem.getContainerWithName("Container"); Component component = container.getComponentWithName("Component"); + // adrtools decisions assertEquals(10, parser.getWorkspace().getDocumentation().getDecisions().size()); assertEquals(10, softwareSystem.getDocumentation().getDecisions().size()); assertEquals(10, container.getDocumentation().getDecisions().size()); - assertEquals(10, component.getDocumentation().getDecisions().size()); + + // log4brains decisions + assertEquals(4, component.getDocumentation().getDecisions().size()); } @Test @@ -764,7 +767,7 @@ void test_unexpectedTokensInWorkspace() { parser.parse(dslFile); fail(); } catch (StructurizrDslParserException e) { - assertEquals("Unexpected tokens (expected: name, description, properties, !docs, !adrs, !identifiers, !impliedRelationships, model, views, configuration) at line 3 of " + dslFile.getAbsolutePath() + ": softwareSystem \"Name\"", e.getMessage()); + assertEquals("Unexpected tokens (expected: name, description, properties, !docs, !decisions, !identifiers, !impliedRelationships, model, views, configuration) at line 3 of " + dslFile.getAbsolutePath() + ": softwareSystem \"Name\"", e.getMessage()); } } @@ -777,7 +780,7 @@ void test_urlNotPermittedInGroup() { parser.parse(dslFile); fail(); } catch (StructurizrDslParserException e) { - assertEquals("Unexpected tokens (expected: !docs, !adrs, group, container, description, tags, url, properties, perspectives, ->) at line 6 of " + dslFile.getAbsolutePath() + ": url \"https://example.com\"", e.getMessage()); + assertEquals("Unexpected tokens (expected: !docs, !decisions, group, container, description, tags, url, properties, perspectives, ->) at line 6 of " + dslFile.getAbsolutePath() + ": url \"https://example.com\"", e.getMessage()); } } @@ -966,7 +969,7 @@ void test_MultiLineWithError() { fail(); } catch (StructurizrDslParserException e) { // check that the error message includes the original line number - assertEquals("Unexpected tokens (expected: !docs, !adrs, group, container, description, tags, url, properties, perspectives, ->) at line 8 of " + dslFile.getAbsolutePath() + ": component \"Component\" // components not permitted inside software systems", e.getMessage()); + assertEquals("Unexpected tokens (expected: !docs, !decisions, group, container, description, tags, url, properties, perspectives, ->) at line 8 of " + dslFile.getAbsolutePath() + ": component \"Component\" // components not permitted inside software systems", e.getMessage()); } } diff --git a/structurizr-dsl/src/test/resources/dsl/adrs/adrs/0001-record-architecture-decisions.md b/structurizr-dsl/src/test/resources/dsl/decisions/adrtools/0001-record-architecture-decisions.md similarity index 100% rename from structurizr-dsl/src/test/resources/dsl/adrs/adrs/0001-record-architecture-decisions.md rename to structurizr-dsl/src/test/resources/dsl/decisions/adrtools/0001-record-architecture-decisions.md diff --git a/structurizr-dsl/src/test/resources/dsl/adrs/adrs/0002-implement-as-shell-scripts.md b/structurizr-dsl/src/test/resources/dsl/decisions/adrtools/0002-implement-as-shell-scripts.md similarity index 100% rename from structurizr-dsl/src/test/resources/dsl/adrs/adrs/0002-implement-as-shell-scripts.md rename to structurizr-dsl/src/test/resources/dsl/decisions/adrtools/0002-implement-as-shell-scripts.md diff --git a/structurizr-dsl/src/test/resources/dsl/adrs/adrs/0003-single-command-with-subcommands.md b/structurizr-dsl/src/test/resources/dsl/decisions/adrtools/0003-single-command-with-subcommands.md similarity index 100% rename from structurizr-dsl/src/test/resources/dsl/adrs/adrs/0003-single-command-with-subcommands.md rename to structurizr-dsl/src/test/resources/dsl/decisions/adrtools/0003-single-command-with-subcommands.md diff --git a/structurizr-dsl/src/test/resources/dsl/adrs/adrs/0004-markdown-format.md b/structurizr-dsl/src/test/resources/dsl/decisions/adrtools/0004-markdown-format.md similarity index 100% rename from structurizr-dsl/src/test/resources/dsl/adrs/adrs/0004-markdown-format.md rename to structurizr-dsl/src/test/resources/dsl/decisions/adrtools/0004-markdown-format.md diff --git a/structurizr-dsl/src/test/resources/dsl/adrs/adrs/0005-help-comments.md b/structurizr-dsl/src/test/resources/dsl/decisions/adrtools/0005-help-comments.md similarity index 100% rename from structurizr-dsl/src/test/resources/dsl/adrs/adrs/0005-help-comments.md rename to structurizr-dsl/src/test/resources/dsl/decisions/adrtools/0005-help-comments.md diff --git a/structurizr-dsl/src/test/resources/dsl/adrs/adrs/0006-packaging-and-distribution-in-other-version-control-repositories.md b/structurizr-dsl/src/test/resources/dsl/decisions/adrtools/0006-packaging-and-distribution-in-other-version-control-repositories.md similarity index 100% rename from structurizr-dsl/src/test/resources/dsl/adrs/adrs/0006-packaging-and-distribution-in-other-version-control-repositories.md rename to structurizr-dsl/src/test/resources/dsl/decisions/adrtools/0006-packaging-and-distribution-in-other-version-control-repositories.md diff --git a/structurizr-dsl/src/test/resources/dsl/adrs/adrs/0007-invoke-adr-config-executable-to-get-configuration.md b/structurizr-dsl/src/test/resources/dsl/decisions/adrtools/0007-invoke-adr-config-executable-to-get-configuration.md similarity index 100% rename from structurizr-dsl/src/test/resources/dsl/adrs/adrs/0007-invoke-adr-config-executable-to-get-configuration.md rename to structurizr-dsl/src/test/resources/dsl/decisions/adrtools/0007-invoke-adr-config-executable-to-get-configuration.md diff --git a/structurizr-dsl/src/test/resources/dsl/adrs/adrs/0008-use-iso-8601-format-for-dates.md b/structurizr-dsl/src/test/resources/dsl/decisions/adrtools/0008-use-iso-8601-format-for-dates.md similarity index 100% rename from structurizr-dsl/src/test/resources/dsl/adrs/adrs/0008-use-iso-8601-format-for-dates.md rename to structurizr-dsl/src/test/resources/dsl/decisions/adrtools/0008-use-iso-8601-format-for-dates.md diff --git a/structurizr-dsl/src/test/resources/dsl/adrs/adrs/0009-help-scripts.md b/structurizr-dsl/src/test/resources/dsl/decisions/adrtools/0009-help-scripts.md similarity index 100% rename from structurizr-dsl/src/test/resources/dsl/adrs/adrs/0009-help-scripts.md rename to structurizr-dsl/src/test/resources/dsl/decisions/adrtools/0009-help-scripts.md diff --git a/structurizr-dsl/src/test/resources/dsl/adrs/adrs/0010-asciidoc-format.md b/structurizr-dsl/src/test/resources/dsl/decisions/adrtools/0010-asciidoc-format.md similarity index 100% rename from structurizr-dsl/src/test/resources/dsl/adrs/adrs/0010-asciidoc-format.md rename to structurizr-dsl/src/test/resources/dsl/decisions/adrtools/0010-asciidoc-format.md diff --git a/structurizr-dsl/src/test/resources/dsl/decisions/log4brains/20240111-use-log4brains-to-manage-the-adrs.md b/structurizr-dsl/src/test/resources/dsl/decisions/log4brains/20240111-use-log4brains-to-manage-the-adrs.md new file mode 100644 index 000000000..26cceeb7a --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/decisions/log4brains/20240111-use-log4brains-to-manage-the-adrs.md @@ -0,0 +1,22 @@ +# Use Log4brains to manage the ADRs + +- Status: accepted +- Date: 2024-01-10 +- Tags: dev-tools, doc + +## Context and Problem Statement + +We want to record architectural decisions made in this project. +Which tool(s) should we use to manage these records? + +## Considered Options + +- [Log4brains](https://github.com/thomvaill/log4brains): architecture knowledge base (command-line + static site generator) +- [ADR Tools](https://github.com/npryce/adr-tools): command-line to create ADRs +- [ADR Tools Python](https://bitbucket.org/tinkerer_/adr-tools-python/src/master/): command-line to create ADRs +- [adr-viewer](https://github.com/mrwilson/adr-viewer): static site generator +- [adr-log](https://adr.github.io/adr-log/): command-line to create a TOC of ADRs + +## Decision Outcome + +Chosen option: "Log4brains", because it includes the features of all the other tools, and even more. diff --git a/structurizr-dsl/src/test/resources/dsl/decisions/log4brains/20240111-use-markdown-architectural-decision-records.md b/structurizr-dsl/src/test/resources/dsl/decisions/log4brains/20240111-use-markdown-architectural-decision-records.md new file mode 100644 index 000000000..f5ee1949a --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/decisions/log4brains/20240111-use-markdown-architectural-decision-records.md @@ -0,0 +1,42 @@ +# Use Markdown Architectural Decision Records + +- Status: accepted +- Date: 2024-01-10 +- Tags: doc + +## Context and Problem Statement + +We want to record architectural decisions made in this project. +Which format and structure should these records follow? + +## Considered Options + +- [MADR](https://adr.github.io/madr/) 2.1.2 with Log4brains patch +- [MADR](https://adr.github.io/madr/) 2.1.2 – The original Markdown Architectural Decision Records +- [Michael Nygard's template](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions) – The first incarnation of the term "ADR" +- [Sustainable Architectural Decisions](https://www.infoq.com/articles/sustainable-architectural-design-decisions) – The Y-Statements +- Other templates listed at +- Formless – No conventions for file format and structure + +## Decision Outcome + +Chosen option: "MADR 2.1.2 with Log4brains patch", because + +- Implicit assumptions should be made explicit. + Design documentation is important to enable people understanding the decisions later on. + See also [A rational design process: How and why to fake it](https://doi.org/10.1109/TSE.1986.6312940). +- The MADR format is lean and fits our development style. +- The MADR structure is comprehensible and facilitates usage & maintenance. +- The MADR project is vivid. +- Version 2.1.2 is the latest one available when starting to document ADRs. +- The Log4brains patch adds more features, like tags. + +The "Log4brains patch" performs the following modifications to the original template: + +- Change the ADR filenames format (`NNN-adr-name` becomes `YYYYMMDD-adr-name`), to avoid conflicts during Git merges. +- Add a `draft` status, to enable collaborative writing. +- Add a `Tags` field. + +## Links + +- Relates to [Use Log4brains to manage the ADRs](20240111-use-log4brains-to-manage-the-adrs.md) diff --git a/structurizr-dsl/src/test/resources/dsl/decisions/log4brains/20240113-decision-3.md b/structurizr-dsl/src/test/resources/dsl/decisions/log4brains/20240113-decision-3.md new file mode 100644 index 000000000..4a2c40918 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/decisions/log4brains/20240113-decision-3.md @@ -0,0 +1,7 @@ +# Decision 3 + +- Status: superseded by [20240111-decision-4](20240114-decision-4.md) + +## Context and Problem Statement + +Describe the context and problem statement, e.g., in free form using two to three sentences. You may want to articulate the problem in form of a question. diff --git a/structurizr-dsl/src/test/resources/dsl/decisions/log4brains/20240114-decision-4.md b/structurizr-dsl/src/test/resources/dsl/decisions/log4brains/20240114-decision-4.md new file mode 100644 index 000000000..bff063a29 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/decisions/log4brains/20240114-decision-4.md @@ -0,0 +1,11 @@ +# Decision 4 + +- Status: accepted + +## Context and Problem Statement + +Describe the context and problem statement, e.g., in free form using two to three sentences. You may want to articulate the problem in form of a question. + +## Links + +- Supersedes [20240111-decision-3](20240113-decision-3.md) diff --git a/structurizr-dsl/src/test/resources/dsl/adrs/workspace.dsl b/structurizr-dsl/src/test/resources/dsl/decisions/workspace.dsl similarity index 52% rename from structurizr-dsl/src/test/resources/dsl/adrs/workspace.dsl rename to structurizr-dsl/src/test/resources/dsl/decisions/workspace.dsl index a27b08488..df35859e3 100644 --- a/structurizr-dsl/src/test/resources/dsl/adrs/workspace.dsl +++ b/structurizr-dsl/src/test/resources/dsl/decisions/workspace.dsl @@ -1,16 +1,16 @@ workspace { - !adrs adrs com.structurizr.example.ExampleDecisionImporter + !adrs adrtools com.structurizr.example.ExampleDecisionImporter model { softwareSystem = softwareSystem "Software System" { - !adrs adrs + !decisions adrtools container "Container" { - !adrs adrs + !decisions adrtools adrtools component "Component" { - !adrs adrs + !decisions log4brains log4brains } } } diff --git a/structurizr-import/src/main/java/com/structurizr/importer/documentation/AbstractDecisionImporter.java b/structurizr-import/src/main/java/com/structurizr/importer/documentation/AbstractDecisionImporter.java new file mode 100644 index 000000000..9d02ebea9 --- /dev/null +++ b/structurizr-import/src/main/java/com/structurizr/importer/documentation/AbstractDecisionImporter.java @@ -0,0 +1,72 @@ +package com.structurizr.importer.documentation; + +import com.structurizr.documentation.Decision; +import com.structurizr.documentation.Documentable; +import com.structurizr.documentation.Format; +import com.structurizr.util.StringUtils; + +import java.io.File; +import java.net.URLEncoder; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Imports architecture decision records created/managed by adr-tools (https://github.com/npryce/adr-tools). + * The format for ADRs is as follows: + * + * Filename: {DECISION_ID:0000}-*.md + * + * Content: + * # {DECISION_ID}. {DECISION_TITLE} + * + * Date: {DECISION_DATE:YYYY-MM-DD} + * + * ## Status + * + * {DECISION_STATUS and links} + * + * ## Context + * ... + */ +public abstract class AbstractDecisionImporter implements DocumentationImporter { + + protected TimeZone timeZone = TimeZone.getDefault(); + protected Charset characterEncoding = StandardCharsets.UTF_8; + + /** + * Sets the time zone to use when parsing dates (the default is UTC) + * + * @param timeZone a time zone as a String (e.g. "Europe/London" or "UTC") + */ + public void setTimeZone(String timeZone) { + this.timeZone = TimeZone.getTimeZone(timeZone); + } + + /** + * Sets the time zone to use when parsing dates. + * + * @param timeZone a TimeZone instance + */ + public void setTimeZone(TimeZone timeZone) { + this.timeZone = timeZone; + } + + /** + * Provides a way to change the character encoding used by the DSL parser. + * + * @param characterEncoding a Charset instance + */ + public void setCharacterEncoding(Charset characterEncoding) { + if (characterEncoding == null) { + throw new IllegalArgumentException("A character encoding must be specified"); + } + + this.characterEncoding = characterEncoding; + } + +} \ No newline at end of file diff --git a/structurizr-import/src/main/java/com/structurizr/importer/documentation/AdrToolsDecisionImporter.java b/structurizr-import/src/main/java/com/structurizr/importer/documentation/AdrToolsDecisionImporter.java index 0a14e90e3..d159bde45 100644 --- a/structurizr-import/src/main/java/com/structurizr/importer/documentation/AdrToolsDecisionImporter.java +++ b/structurizr-import/src/main/java/com/structurizr/importer/documentation/AdrToolsDecisionImporter.java @@ -7,6 +7,7 @@ import java.io.File; import java.net.URLEncoder; +import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.text.SimpleDateFormat; @@ -32,7 +33,7 @@ * ## Context * ... */ -public class AdrToolsDecisionImporter implements DocumentationImporter { +public class AdrToolsDecisionImporter extends AbstractDecisionImporter { private static final String STATUS_PROPOSED = "Proposed"; private static final String STATUS_SUPERSEDED = "Superseded"; @@ -45,7 +46,6 @@ public class AdrToolsDecisionImporter implements DocumentationImporter { private static final Pattern LINK_REGEX = Pattern.compile("(.*) \\[.*]\\((.*)\\)"); private String dateFormat = "yyyy-MM-dd"; - private TimeZone timeZone = TimeZone.getDefault(); /** * Sets the date format to use when parsing dates (the default is "yyyy-MM-dd"). @@ -56,24 +56,6 @@ public void setDateFormat(String dateFormat) { this.dateFormat = dateFormat; } - /** - * Sets the time zone to use when parsing dates (the default is UTC) - * - * @param timeZone a time zone as a String (e.g. "Europe/London" or "UTC") - */ - public void setTimeZone(String timeZone) { - this.timeZone = TimeZone.getTimeZone(timeZone); - } - - /** - * Sets the time zone to use when parsing dates. - * - * @param timeZone a TimeZone instance - */ - public void setTimeZone(TimeZone timeZone) { - this.timeZone = timeZone; - } - /** * Imports Markdown files from the specified path, one per decision. * @@ -131,7 +113,7 @@ protected Decision importDecision(File file) throws Exception { String id = extractIntegerIDFromFileName(file); Decision decision = new Decision(id); - String content = new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8); + String content = Files.readString(file.toPath(), characterEncoding); content = content.replace("\r", ""); decision.setContent(content); diff --git a/structurizr-import/src/main/java/com/structurizr/importer/documentation/Log4brainsDecisionImporter.java b/structurizr-import/src/main/java/com/structurizr/importer/documentation/Log4brainsDecisionImporter.java new file mode 100644 index 000000000..b6bc324f8 --- /dev/null +++ b/structurizr-import/src/main/java/com/structurizr/importer/documentation/Log4brainsDecisionImporter.java @@ -0,0 +1,209 @@ +package com.structurizr.importer.documentation; + +import com.structurizr.documentation.Decision; +import com.structurizr.documentation.Documentable; +import com.structurizr.documentation.Format; + +import java.io.File; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Imports architecture decision records created/managed by Log4brains (https://github.com/thomvaill/log4brains). + * See https://github.com/thomvaill/log4brains/blob/master/docs/adr/template.md for the template. + */ +public class Log4brainsDecisionImporter extends AbstractDecisionImporter { + + private static final String DATE_PREFIX = "- Date: "; + private static final String STATUS_PREFIX = "- Status: "; + private static final Pattern STATUS_LINK_REGEX = Pattern.compile("- Status: (.*) \\[.*]\\((.*)\\)"); + private static final String SUPERSEDED = "superseded"; + private static final String LINKS_HEADING = "## Links"; + + private static final Pattern LINK_REGEX = Pattern.compile("- (.*) \\[.*]\\((.*)\\)"); + + private static final String DATE_FORMAT_IN_FILENAME = "yyyyMMdd"; + private static final String DATE_FORMAT_IN_CONTENT = "yyyy-MM-dd"; + + /** + * Imports Markdown files from the specified path, one per decision. + * + * @param documentable the item that documentation should be associated with + * @param path the path to import documentation from + */ + @Override + public void importDocumentation(Documentable documentable, File path) { + if (documentable == null) { + throw new IllegalArgumentException("A workspace, software system, container, or component must be specified."); + } + + if (path == null) { + throw new IllegalArgumentException("A path must be specified."); + } else if (!path.exists()) { + throw new IllegalArgumentException(path.getAbsolutePath() + " does not exist."); + } + + if (!path.isDirectory()) { + throw new IllegalArgumentException(path.getAbsolutePath() + " is not a directory."); + } + + try { + Map decisionsById = new LinkedHashMap<>(); + + File[] markdownFiles = path.listFiles((dir, name) -> name.matches("\\d{4}\\d{2}\\d{2}-.+?.md")); + if (markdownFiles != null) { + Map decisionsByFilename = new HashMap<>(); + + Arrays.sort(markdownFiles, Comparator.comparing(File::getName)); + + int decisionId = 1; + for (File file : markdownFiles) { + Decision decision = importDecision(decisionId, file); + documentable.getDocumentation().addDecision(decision); + + decisionsById.put(decision.getId(), decision); + decisionsByFilename.put(file.getName(), decision); + decisionId++; + } + + for (Decision decision : decisionsById.values()) { + extractLinks(decision, decisionsByFilename); + + // and replace file references, for example "0008-some-decision.md" -> "#8" + String content = decision.getContent(); + for (String filename : decisionsByFilename.keySet()) { + content = content.replace(filename, calculateUrl(decisionsByFilename.get(filename))); + } + decision.setContent(content); + } + } + } catch (Exception e) { + throw new DocumentationImportException(e); + } + } + + protected Decision importDecision(int id, File file) throws Exception { + Decision decision = new Decision("" + id); + + String content = Files.readString(file.toPath(), characterEncoding); + content = content.replace("\r", ""); + decision.setContent(content); + + String[] lines = content.split("\\n"); + decision.setTitle(extractTitle(lines)); + + decision.setDate(extractDateFromFilename(file)); + Date dateFromContent = extractDate(lines); + if (dateFromContent != null) { + decision.setDate(dateFromContent); + } + + decision.setStatus(extractStatus(lines)); + decision.setFormat(Format.Markdown); + + return decision; + } + + protected Date extractDateFromFilename(File file) throws Exception { + SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT_IN_FILENAME); + sdf.setTimeZone(timeZone); + + return sdf.parse(file.getName().substring(0, DATE_FORMAT_IN_FILENAME.length())); + } + + protected String extractTitle(String[] lines) { + // the title is assumed to be the first line of the content, in the format: + // # {DECISION_TITLE} + String titleLine = lines[0]; + + return titleLine.substring(2); + } + + protected Date extractDate(String[] lines) throws Exception { + // the date can optionally be on a line of its own, in the format: + // - Date: {DECISION_DATE:YYYY-MM-DD} + SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT_IN_CONTENT); + sdf.setTimeZone(timeZone); + + for (String line : lines) { + if (line.startsWith(DATE_PREFIX)) { + String dateAsString = line.substring(DATE_PREFIX.length()); + + return sdf.parse(dateAsString); + } + } + + return null; + } + + protected String extractStatus(String[] lines) { + // the date is on a line of its own, in the format: + // - Status: {DECISION_STATUS} + for (String line : lines) { + if (line.startsWith(STATUS_PREFIX)) { + String status = line.substring(STATUS_PREFIX.length()); + if (status.startsWith(SUPERSEDED)) { + // superseded by [slug](filename) + return SUPERSEDED; + } else { + return status; + } + } + } + + return ""; + } + + protected void extractLinks(Decision decision, Map decisionsByFilename) { + // extracts links from: + // 1. the status line + // 2. the final ## Links section (if present) + boolean inLinksSection = false; + String[] lines = decision.getContent().split("\\n"); + for (String line : lines) { + if (line.startsWith(STATUS_PREFIX)) { + Matcher matcher = STATUS_LINK_REGEX.matcher(line); + if (matcher.find()) { + String linkDescription = matcher.group(1); + String markdownFile = matcher.group(2); + + Decision targetDecision = decisionsByFilename.get(markdownFile); + if (targetDecision != null) { + decision.addLink(targetDecision, linkDescription); + } + } + } + + if (line.startsWith(LINKS_HEADING)) { + inLinksSection = true; + } + + if (inLinksSection) { + Matcher matcher = LINK_REGEX.matcher(line); + if (matcher.find()) { + String linkDescription = matcher.group(1); + String markdownFile = matcher.group(2); + + Decision targetDecision = decisionsByFilename.get(markdownFile); + if (targetDecision != null) { + decision.addLink(targetDecision, linkDescription); + } + } + } + } + } + + protected String calculateUrl(Decision decision) throws Exception { + return "#" + urlEncode(decision.getId()); + } + + protected String urlEncode(String value) throws Exception { + return URLEncoder.encode(value, StandardCharsets.UTF_8.toString()).replaceAll("\\+", "%20"); + } + +} \ No newline at end of file diff --git a/structurizr-import/src/test/java/com/structurizr/importer/documentation/AdrToolsDecisionImporterTests.java b/structurizr-import/src/test/java/com/structurizr/importer/documentation/AdrToolsDecisionImporterTests.java index 8b31bbbc0..099c1edbb 100644 --- a/structurizr-import/src/test/java/com/structurizr/importer/documentation/AdrToolsDecisionImporterTests.java +++ b/structurizr-import/src/test/java/com/structurizr/importer/documentation/AdrToolsDecisionImporterTests.java @@ -17,6 +17,8 @@ public class AdrToolsDecisionImporterTests { + private static final File DECISIONS_FOLDER = new File("./src/test/resources/decisions/adrtools"); + private AdrToolsDecisionImporter decisionImporter; private Workspace workspace; private Documentation documentation; @@ -72,7 +74,7 @@ public void test_importDocumentation_ThrowsAnException_WhenAPathIsSpecifiedButIt @Test public void test_importDecisions() { - decisionImporter.importDocumentation(workspace, new File("./src/test/resources/adrs")); + decisionImporter.importDocumentation(workspace, DECISIONS_FOLDER); assertEquals(9, documentation.getDecisions().size()); @@ -108,7 +110,7 @@ public void test_importDecisions() { @Test public void test_importDocumentation_CapturesLinksBetweenDecisions() { - decisionImporter.importDocumentation(workspace, new File("./src/test/resources/adrs")); + decisionImporter.importDocumentation(workspace, DECISIONS_FOLDER); Decision decision5 = documentation.getDecisions().stream().filter(d -> d.getId().equals("5")).findFirst().get(); assertEquals(1, decision5.getLinks().size()); @@ -119,7 +121,7 @@ public void test_importDocumentation_CapturesLinksBetweenDecisions() { @Test public void test_importDocumentation_RewritesLinksBetweenDecisions() { - decisionImporter.importDocumentation(workspace, new File("./src/test/resources/adrs")); + decisionImporter.importDocumentation(workspace, DECISIONS_FOLDER); Decision decision5 = documentation.getDecisions().stream().filter(d -> d.getId().equals("5")).findFirst().get(); assertTrue(decision5.getContent().contains("Amended by [9. Help scripts](#9)")); diff --git a/structurizr-import/src/test/java/com/structurizr/importer/documentation/Log4brainsDecisionImporterTests.java b/structurizr-import/src/test/java/com/structurizr/importer/documentation/Log4brainsDecisionImporterTests.java new file mode 100644 index 000000000..56d95c6d1 --- /dev/null +++ b/structurizr-import/src/test/java/com/structurizr/importer/documentation/Log4brainsDecisionImporterTests.java @@ -0,0 +1,232 @@ +package com.structurizr.importer.documentation; + +import com.structurizr.Workspace; +import com.structurizr.documentation.Decision; +import com.structurizr.documentation.Documentation; +import com.structurizr.documentation.Format; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.text.SimpleDateFormat; +import java.util.TimeZone; + +import static org.junit.jupiter.api.Assertions.*; + + +public class Log4brainsDecisionImporterTests { + + private static final File DECISIONS_FOLDER = new File("./src/test/resources/decisions/log4brains"); + + private Log4brainsDecisionImporter decisionImporter; + private Workspace workspace; + private Documentation documentation; + + @BeforeEach + public void setUp() { + decisionImporter = new Log4brainsDecisionImporter(); + workspace = new Workspace("Name", "Description"); + documentation = workspace.getDocumentation(); + } + + @Test + public void test_importDocumentation_ThrowsAnException_WhenANullDocumentableIsSpecified() { + try { + decisionImporter.importDocumentation(null, null); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("A workspace, software system, container, or component must be specified.", iae.getMessage()); + } + } + + @Test + public void test_importDocumentation_ThrowsAnException_WhenADirectoryIsNotSpecified() { + try { + decisionImporter.importDocumentation(workspace, null); + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("A path must be specified.", iae.getMessage()); + } + } + + @Test + public void test_importDocumentation_ThrowsAnException_WhenADirectoryIsSpecifiedButItDoesNotExist() { + try { + File directory = new File("foo"); + assertFalse(directory.exists()); + decisionImporter.importDocumentation(workspace, directory); + fail(); + } catch (IllegalArgumentException iae) { + assertTrue(iae.getMessage().endsWith("foo does not exist.")); + } + } + + @Test + public void test_importDocumentation_ThrowsAnException_WhenAPathIsSpecifiedButItIsNotADirectory() { + try { + decisionImporter.importDocumentation(workspace, new File("build.gradle")); + fail(); + } catch (IllegalArgumentException iae) { + assertTrue(iae.getMessage().endsWith("/build.gradle is not a directory.")); + } + } + + @Test + public void test_importDecisions() { + decisionImporter.importDocumentation(workspace, DECISIONS_FOLDER); + SimpleDateFormat sdf = new SimpleDateFormat("dd-MMM-yyyy"); + sdf.setTimeZone(TimeZone.getTimeZone("UTC")); + + assertEquals(4, documentation.getDecisions().size()); + + // I think these first two decisions are found in the wrong order, which is a consequence of Log4brains not having decision IDs + + Decision decision1 = documentation.getDecisions().stream().filter(d -> d.getId().equals("1")).findFirst().get(); + assertEquals("1", decision1.getId()); + assertEquals("Use Log4brains to manage the ADRs", decision1.getTitle()); + sdf.setTimeZone(TimeZone.getTimeZone("UTC")); + assertEquals("10-Jan-2024", sdf.format(decision1.getDate())); + assertEquals("accepted", decision1.getStatus()); + Assertions.assertEquals(Format.Markdown, decision1.getFormat()); + assertEquals(""" +# Use Log4brains to manage the ADRs + +- Status: accepted +- Date: 2024-01-10 +- Tags: dev-tools, doc + +## Context and Problem Statement + +We want to record architectural decisions made in this project. +Which tool(s) should we use to manage these records? + +## Considered Options + +- [Log4brains](https://github.com/thomvaill/log4brains): architecture knowledge base (command-line + static site generator) +- [ADR Tools](https://github.com/npryce/adr-tools): command-line to create ADRs +- [ADR Tools Python](https://bitbucket.org/tinkerer_/adr-tools-python/src/master/): command-line to create ADRs +- [adr-viewer](https://github.com/mrwilson/adr-viewer): static site generator +- [adr-log](https://adr.github.io/adr-log/): command-line to create a TOC of ADRs + +## Decision Outcome + +Chosen option: "Log4brains", because it includes the features of all the other tools, and even more. +""", + decision1.getContent()); + + Decision decision2 = documentation.getDecisions().stream().filter(d -> d.getId().equals("2")).findFirst().get(); + assertEquals("2", decision2.getId()); + assertEquals("Use Markdown Architectural Decision Records", decision2.getTitle()); + assertEquals("10-Jan-2024", sdf.format(decision2.getDate())); + assertEquals("accepted", decision2.getStatus()); + Assertions.assertEquals(Format.Markdown, decision2.getFormat()); + assertEquals(""" +# Use Markdown Architectural Decision Records + +- Status: accepted +- Date: 2024-01-10 +- Tags: doc + +## Context and Problem Statement + +We want to record architectural decisions made in this project. +Which format and structure should these records follow? + +## Considered Options + +- [MADR](https://adr.github.io/madr/) 2.1.2 with Log4brains patch +- [MADR](https://adr.github.io/madr/) 2.1.2 – The original Markdown Architectural Decision Records +- [Michael Nygard's template](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions) – The first incarnation of the term "ADR" +- [Sustainable Architectural Decisions](https://www.infoq.com/articles/sustainable-architectural-design-decisions) – The Y-Statements +- Other templates listed at +- Formless – No conventions for file format and structure + +## Decision Outcome + +Chosen option: "MADR 2.1.2 with Log4brains patch", because + +- Implicit assumptions should be made explicit. + Design documentation is important to enable people understanding the decisions later on. + See also [A rational design process: How and why to fake it](https://doi.org/10.1109/TSE.1986.6312940). +- The MADR format is lean and fits our development style. +- The MADR structure is comprehensible and facilitates usage & maintenance. +- The MADR project is vivid. +- Version 2.1.2 is the latest one available when starting to document ADRs. +- The Log4brains patch adds more features, like tags. + +The "Log4brains patch" performs the following modifications to the original template: + +- Change the ADR filenames format (`NNN-adr-name` becomes `YYYYMMDD-adr-name`), to avoid conflicts during Git merges. +- Add a `draft` status, to enable collaborative writing. +- Add a `Tags` field. + +## Links + +- Relates to [Use Log4brains to manage the ADRs](#1) +""", + decision2.getContent()); + + Decision decision3 = documentation.getDecisions().stream().filter(d -> d.getId().equals("3")).findFirst().get(); + assertEquals("3", decision3.getId()); + assertEquals("Decision 3", decision3.getTitle()); + assertEquals("13-Jan-2024", sdf.format(decision3.getDate())); + assertEquals("superseded", decision3.getStatus()); + Assertions.assertEquals(Format.Markdown, decision3.getFormat()); + assertEquals(""" +# Decision 3 + +- Status: superseded by [20240111-decision-4](#4) + +## Context and Problem Statement + +Describe the context and problem statement, e.g., in free form using two to three sentences. You may want to articulate the problem in form of a question. +""", + decision3.getContent()); + + Decision decision4 = documentation.getDecisions().stream().filter(d -> d.getId().equals("4")).findFirst().get(); + assertEquals("4", decision4.getId()); + assertEquals("Decision 4", decision4.getTitle()); + assertEquals("14-Jan-2024", sdf.format(decision4.getDate())); + assertEquals("accepted", decision4.getStatus()); + Assertions.assertEquals(Format.Markdown, decision4.getFormat()); + assertEquals(""" +# Decision 4 + +- Status: accepted + +## Context and Problem Statement + +Describe the context and problem statement, e.g., in free form using two to three sentences. You may want to articulate the problem in form of a question. + +## Links + +- Supersedes [20240111-decision-3](#3) +""", + decision4.getContent()); + } + + @Test + public void test_importDocumentation_CapturesLinksBetweenDecisions() { + decisionImporter.importDocumentation(workspace, DECISIONS_FOLDER); + + Decision decision2 = documentation.getDecisions().stream().filter(d -> d.getId().equals("2")).findFirst().get(); + assertEquals(1, decision2.getLinks().size()); + Decision.Link link = decision2.getLinks().iterator().next(); + assertEquals("1", link.getId()); + assertEquals("Relates to", link.getDescription()); + + Decision decision3 = documentation.getDecisions().stream().filter(d -> d.getId().equals("3")).findFirst().get(); + assertEquals(1, decision3.getLinks().size()); + link = decision3.getLinks().iterator().next(); + assertEquals("4", link.getId()); + assertEquals("superseded by", link.getDescription()); + + Decision decision4 = documentation.getDecisions().stream().filter(d -> d.getId().equals("4")).findFirst().get(); + assertEquals(1, decision4.getLinks().size()); + link = decision4.getLinks().iterator().next(); + assertEquals("3", link.getId()); + assertEquals("Supersedes", link.getDescription()); + } + +} \ No newline at end of file diff --git a/structurizr-import/src/test/resources/adrs/0001-record-architecture-decisions.md b/structurizr-import/src/test/resources/decisions/adrtools/0001-record-architecture-decisions.md similarity index 100% rename from structurizr-import/src/test/resources/adrs/0001-record-architecture-decisions.md rename to structurizr-import/src/test/resources/decisions/adrtools/0001-record-architecture-decisions.md diff --git a/structurizr-import/src/test/resources/adrs/0002-implement-as-shell-scripts.md b/structurizr-import/src/test/resources/decisions/adrtools/0002-implement-as-shell-scripts.md similarity index 100% rename from structurizr-import/src/test/resources/adrs/0002-implement-as-shell-scripts.md rename to structurizr-import/src/test/resources/decisions/adrtools/0002-implement-as-shell-scripts.md diff --git a/structurizr-import/src/test/resources/adrs/0003-single-command-with-subcommands.md b/structurizr-import/src/test/resources/decisions/adrtools/0003-single-command-with-subcommands.md similarity index 100% rename from structurizr-import/src/test/resources/adrs/0003-single-command-with-subcommands.md rename to structurizr-import/src/test/resources/decisions/adrtools/0003-single-command-with-subcommands.md diff --git a/structurizr-import/src/test/resources/adrs/0004-markdown-format.md b/structurizr-import/src/test/resources/decisions/adrtools/0004-markdown-format.md similarity index 100% rename from structurizr-import/src/test/resources/adrs/0004-markdown-format.md rename to structurizr-import/src/test/resources/decisions/adrtools/0004-markdown-format.md diff --git a/structurizr-import/src/test/resources/adrs/0005-help-comments.md b/structurizr-import/src/test/resources/decisions/adrtools/0005-help-comments.md similarity index 100% rename from structurizr-import/src/test/resources/adrs/0005-help-comments.md rename to structurizr-import/src/test/resources/decisions/adrtools/0005-help-comments.md diff --git a/structurizr-import/src/test/resources/adrs/0006-packaging-and-distribution-in-other-version-control-repositories.md b/structurizr-import/src/test/resources/decisions/adrtools/0006-packaging-and-distribution-in-other-version-control-repositories.md similarity index 100% rename from structurizr-import/src/test/resources/adrs/0006-packaging-and-distribution-in-other-version-control-repositories.md rename to structurizr-import/src/test/resources/decisions/adrtools/0006-packaging-and-distribution-in-other-version-control-repositories.md diff --git a/structurizr-import/src/test/resources/adrs/0007-invoke-adr-config-executable-to-get-configuration.md b/structurizr-import/src/test/resources/decisions/adrtools/0007-invoke-adr-config-executable-to-get-configuration.md similarity index 100% rename from structurizr-import/src/test/resources/adrs/0007-invoke-adr-config-executable-to-get-configuration.md rename to structurizr-import/src/test/resources/decisions/adrtools/0007-invoke-adr-config-executable-to-get-configuration.md diff --git a/structurizr-import/src/test/resources/adrs/0008-use-iso-8601-format-for-dates.md b/structurizr-import/src/test/resources/decisions/adrtools/0008-use-iso-8601-format-for-dates.md similarity index 100% rename from structurizr-import/src/test/resources/adrs/0008-use-iso-8601-format-for-dates.md rename to structurizr-import/src/test/resources/decisions/adrtools/0008-use-iso-8601-format-for-dates.md diff --git a/structurizr-import/src/test/resources/adrs/0009-help-scripts.md b/structurizr-import/src/test/resources/decisions/adrtools/0009-help-scripts.md similarity index 100% rename from structurizr-import/src/test/resources/adrs/0009-help-scripts.md rename to structurizr-import/src/test/resources/decisions/adrtools/0009-help-scripts.md diff --git a/structurizr-import/src/test/resources/decisions/log4brains/20240111-use-log4brains-to-manage-the-adrs.md b/structurizr-import/src/test/resources/decisions/log4brains/20240111-use-log4brains-to-manage-the-adrs.md new file mode 100644 index 000000000..26cceeb7a --- /dev/null +++ b/structurizr-import/src/test/resources/decisions/log4brains/20240111-use-log4brains-to-manage-the-adrs.md @@ -0,0 +1,22 @@ +# Use Log4brains to manage the ADRs + +- Status: accepted +- Date: 2024-01-10 +- Tags: dev-tools, doc + +## Context and Problem Statement + +We want to record architectural decisions made in this project. +Which tool(s) should we use to manage these records? + +## Considered Options + +- [Log4brains](https://github.com/thomvaill/log4brains): architecture knowledge base (command-line + static site generator) +- [ADR Tools](https://github.com/npryce/adr-tools): command-line to create ADRs +- [ADR Tools Python](https://bitbucket.org/tinkerer_/adr-tools-python/src/master/): command-line to create ADRs +- [adr-viewer](https://github.com/mrwilson/adr-viewer): static site generator +- [adr-log](https://adr.github.io/adr-log/): command-line to create a TOC of ADRs + +## Decision Outcome + +Chosen option: "Log4brains", because it includes the features of all the other tools, and even more. diff --git a/structurizr-import/src/test/resources/decisions/log4brains/20240111-use-markdown-architectural-decision-records.md b/structurizr-import/src/test/resources/decisions/log4brains/20240111-use-markdown-architectural-decision-records.md new file mode 100644 index 000000000..f5ee1949a --- /dev/null +++ b/structurizr-import/src/test/resources/decisions/log4brains/20240111-use-markdown-architectural-decision-records.md @@ -0,0 +1,42 @@ +# Use Markdown Architectural Decision Records + +- Status: accepted +- Date: 2024-01-10 +- Tags: doc + +## Context and Problem Statement + +We want to record architectural decisions made in this project. +Which format and structure should these records follow? + +## Considered Options + +- [MADR](https://adr.github.io/madr/) 2.1.2 with Log4brains patch +- [MADR](https://adr.github.io/madr/) 2.1.2 – The original Markdown Architectural Decision Records +- [Michael Nygard's template](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions) – The first incarnation of the term "ADR" +- [Sustainable Architectural Decisions](https://www.infoq.com/articles/sustainable-architectural-design-decisions) – The Y-Statements +- Other templates listed at +- Formless – No conventions for file format and structure + +## Decision Outcome + +Chosen option: "MADR 2.1.2 with Log4brains patch", because + +- Implicit assumptions should be made explicit. + Design documentation is important to enable people understanding the decisions later on. + See also [A rational design process: How and why to fake it](https://doi.org/10.1109/TSE.1986.6312940). +- The MADR format is lean and fits our development style. +- The MADR structure is comprehensible and facilitates usage & maintenance. +- The MADR project is vivid. +- Version 2.1.2 is the latest one available when starting to document ADRs. +- The Log4brains patch adds more features, like tags. + +The "Log4brains patch" performs the following modifications to the original template: + +- Change the ADR filenames format (`NNN-adr-name` becomes `YYYYMMDD-adr-name`), to avoid conflicts during Git merges. +- Add a `draft` status, to enable collaborative writing. +- Add a `Tags` field. + +## Links + +- Relates to [Use Log4brains to manage the ADRs](20240111-use-log4brains-to-manage-the-adrs.md) diff --git a/structurizr-import/src/test/resources/decisions/log4brains/20240113-decision-3.md b/structurizr-import/src/test/resources/decisions/log4brains/20240113-decision-3.md new file mode 100644 index 000000000..4a2c40918 --- /dev/null +++ b/structurizr-import/src/test/resources/decisions/log4brains/20240113-decision-3.md @@ -0,0 +1,7 @@ +# Decision 3 + +- Status: superseded by [20240111-decision-4](20240114-decision-4.md) + +## Context and Problem Statement + +Describe the context and problem statement, e.g., in free form using two to three sentences. You may want to articulate the problem in form of a question. diff --git a/structurizr-import/src/test/resources/decisions/log4brains/20240114-decision-4.md b/structurizr-import/src/test/resources/decisions/log4brains/20240114-decision-4.md new file mode 100644 index 000000000..bff063a29 --- /dev/null +++ b/structurizr-import/src/test/resources/decisions/log4brains/20240114-decision-4.md @@ -0,0 +1,11 @@ +# Decision 4 + +- Status: accepted + +## Context and Problem Statement + +Describe the context and problem statement, e.g., in free form using two to three sentences. You may want to articulate the problem in form of a question. + +## Links + +- Supersedes [20240111-decision-3](20240113-decision-3.md) From bfa8bbeb041bffd4db8bc543a32e8dfddbf0fd31 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 11 Jan 2024 14:28:10 +0000 Subject: [PATCH 452/717] Adds support for MADR - closes #222. --- .../com/structurizr/dsl/DecisionsParser.java | 2 + .../java/com/structurizr/dsl/DslTests.java | 4 +- .../0000-use-markdown-any-decision-records.md | 30 +++ .../madr/0001-use-CC0-or-MIT-as-license.md | 49 ++++ .../0002-do-not-use-numbers-in-headings.md | 24 ++ .../madr/0003-provide-own-madr-tools.md | 33 +++ .../decisions/madr/0004-write-own-toc-tool.md | 29 +++ .../madr/0005-use-dashes-in-filenames.md | 26 ++ .../madr/0006-use-names-as-identifier.md | 24 ++ .../0007-do-not-emphasize-line-headings.md | 23 ++ .../decisions/madr/0008-add-status-field.md | 100 ++++++++ .../dsl/decisions/madr/0008-example-badge.png | Bin 0 -> 2073 bytes .../madr/0008-example-separate-heading.png | Bin 0 -> 2141 bytes .../dsl/decisions/madr/0008-example-table.png | Bin 0 -> 1334 bytes ...pport-links-between-adrs-inside-an-adrs.md | 79 ++++++ .../decisions/madr/0010-support-categories.md | 106 ++++++++ .../madr/0011-use-asterisk-as-list-marker.md | 20 ++ ...-use-curly-braces-to-denote-placeholder.md | 46 ++++ .../dsl/decisions/madr/0013-example.png | Bin 0 -> 23190 bytes ...013-use-yaml-front-matter-for-meta-data.md | 65 +++++ .../madr/0014-allow-neutral-arguments.md | 62 +++++ ...015-include-consulting-informed-of-raci.md | 44 ++++ .../0016-outcome-before-detailed-pros-cons.md | 74 ++++++ ...se-same-format-for-outcomes-and-options.md | 26 ++ .../madr/0018-use-confirmation-as-heading.md | 30 +++ .../resources/dsl/decisions/workspace.dsl | 2 +- .../AdrToolsDecisionImporter.java | 8 +- .../Log4brainsDecisionImporter.java | 8 +- .../documentation/MadrDecisionImporter.java | 172 +++++++++++++ .../MadrDecisionImporterTests.java | 237 ++++++++++++++++++ .../0000-use-markdown-any-decision-records.md | 30 +++ .../madr/0001-use-CC0-or-MIT-as-license.md | 49 ++++ .../0002-do-not-use-numbers-in-headings.md | 24 ++ .../madr/0003-provide-own-madr-tools.md | 33 +++ .../decisions/madr/0004-write-own-toc-tool.md | 29 +++ .../madr/0005-use-dashes-in-filenames.md | 26 ++ .../madr/0006-use-names-as-identifier.md | 24 ++ .../0007-do-not-emphasize-line-headings.md | 23 ++ .../decisions/madr/0008-add-status-field.md | 100 ++++++++ .../decisions/madr/0008-example-badge.png | Bin 0 -> 2073 bytes .../madr/0008-example-separate-heading.png | Bin 0 -> 2141 bytes .../decisions/madr/0008-example-table.png | Bin 0 -> 1334 bytes ...pport-links-between-adrs-inside-an-adrs.md | 79 ++++++ .../decisions/madr/0010-support-categories.md | 106 ++++++++ .../madr/0011-use-asterisk-as-list-marker.md | 20 ++ ...-use-curly-braces-to-denote-placeholder.md | 46 ++++ .../resources/decisions/madr/0013-example.png | Bin 0 -> 23190 bytes ...013-use-yaml-front-matter-for-meta-data.md | 65 +++++ .../madr/0014-allow-neutral-arguments.md | 62 +++++ ...015-include-consulting-informed-of-raci.md | 44 ++++ .../0016-outcome-before-detailed-pros-cons.md | 74 ++++++ ...se-same-format-for-outcomes-and-options.md | 26 ++ .../madr/0018-use-confirmation-as-heading.md | 30 +++ 53 files changed, 2203 insertions(+), 10 deletions(-) create mode 100644 structurizr-dsl/src/test/resources/dsl/decisions/madr/0000-use-markdown-any-decision-records.md create mode 100644 structurizr-dsl/src/test/resources/dsl/decisions/madr/0001-use-CC0-or-MIT-as-license.md create mode 100644 structurizr-dsl/src/test/resources/dsl/decisions/madr/0002-do-not-use-numbers-in-headings.md create mode 100644 structurizr-dsl/src/test/resources/dsl/decisions/madr/0003-provide-own-madr-tools.md create mode 100644 structurizr-dsl/src/test/resources/dsl/decisions/madr/0004-write-own-toc-tool.md create mode 100644 structurizr-dsl/src/test/resources/dsl/decisions/madr/0005-use-dashes-in-filenames.md create mode 100644 structurizr-dsl/src/test/resources/dsl/decisions/madr/0006-use-names-as-identifier.md create mode 100644 structurizr-dsl/src/test/resources/dsl/decisions/madr/0007-do-not-emphasize-line-headings.md create mode 100644 structurizr-dsl/src/test/resources/dsl/decisions/madr/0008-add-status-field.md create mode 100644 structurizr-dsl/src/test/resources/dsl/decisions/madr/0008-example-badge.png create mode 100644 structurizr-dsl/src/test/resources/dsl/decisions/madr/0008-example-separate-heading.png create mode 100644 structurizr-dsl/src/test/resources/dsl/decisions/madr/0008-example-table.png create mode 100644 structurizr-dsl/src/test/resources/dsl/decisions/madr/0009-support-links-between-adrs-inside-an-adrs.md create mode 100644 structurizr-dsl/src/test/resources/dsl/decisions/madr/0010-support-categories.md create mode 100644 structurizr-dsl/src/test/resources/dsl/decisions/madr/0011-use-asterisk-as-list-marker.md create mode 100644 structurizr-dsl/src/test/resources/dsl/decisions/madr/0012-use-curly-braces-to-denote-placeholder.md create mode 100644 structurizr-dsl/src/test/resources/dsl/decisions/madr/0013-example.png create mode 100644 structurizr-dsl/src/test/resources/dsl/decisions/madr/0013-use-yaml-front-matter-for-meta-data.md create mode 100644 structurizr-dsl/src/test/resources/dsl/decisions/madr/0014-allow-neutral-arguments.md create mode 100644 structurizr-dsl/src/test/resources/dsl/decisions/madr/0015-include-consulting-informed-of-raci.md create mode 100644 structurizr-dsl/src/test/resources/dsl/decisions/madr/0016-outcome-before-detailed-pros-cons.md create mode 100644 structurizr-dsl/src/test/resources/dsl/decisions/madr/0017-use-same-format-for-outcomes-and-options.md create mode 100644 structurizr-dsl/src/test/resources/dsl/decisions/madr/0018-use-confirmation-as-heading.md create mode 100644 structurizr-import/src/main/java/com/structurizr/importer/documentation/MadrDecisionImporter.java create mode 100644 structurizr-import/src/test/java/com/structurizr/importer/documentation/MadrDecisionImporterTests.java create mode 100644 structurizr-import/src/test/resources/decisions/madr/0000-use-markdown-any-decision-records.md create mode 100644 structurizr-import/src/test/resources/decisions/madr/0001-use-CC0-or-MIT-as-license.md create mode 100644 structurizr-import/src/test/resources/decisions/madr/0002-do-not-use-numbers-in-headings.md create mode 100644 structurizr-import/src/test/resources/decisions/madr/0003-provide-own-madr-tools.md create mode 100644 structurizr-import/src/test/resources/decisions/madr/0004-write-own-toc-tool.md create mode 100644 structurizr-import/src/test/resources/decisions/madr/0005-use-dashes-in-filenames.md create mode 100644 structurizr-import/src/test/resources/decisions/madr/0006-use-names-as-identifier.md create mode 100644 structurizr-import/src/test/resources/decisions/madr/0007-do-not-emphasize-line-headings.md create mode 100644 structurizr-import/src/test/resources/decisions/madr/0008-add-status-field.md create mode 100644 structurizr-import/src/test/resources/decisions/madr/0008-example-badge.png create mode 100644 structurizr-import/src/test/resources/decisions/madr/0008-example-separate-heading.png create mode 100644 structurizr-import/src/test/resources/decisions/madr/0008-example-table.png create mode 100644 structurizr-import/src/test/resources/decisions/madr/0009-support-links-between-adrs-inside-an-adrs.md create mode 100644 structurizr-import/src/test/resources/decisions/madr/0010-support-categories.md create mode 100644 structurizr-import/src/test/resources/decisions/madr/0011-use-asterisk-as-list-marker.md create mode 100644 structurizr-import/src/test/resources/decisions/madr/0012-use-curly-braces-to-denote-placeholder.md create mode 100644 structurizr-import/src/test/resources/decisions/madr/0013-example.png create mode 100644 structurizr-import/src/test/resources/decisions/madr/0013-use-yaml-front-matter-for-meta-data.md create mode 100644 structurizr-import/src/test/resources/decisions/madr/0014-allow-neutral-arguments.md create mode 100644 structurizr-import/src/test/resources/decisions/madr/0015-include-consulting-informed-of-raci.md create mode 100644 structurizr-import/src/test/resources/decisions/madr/0016-outcome-before-detailed-pros-cons.md create mode 100644 structurizr-import/src/test/resources/decisions/madr/0017-use-same-format-for-outcomes-and-options.md create mode 100644 structurizr-import/src/test/resources/decisions/madr/0018-use-confirmation-as-heading.md diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DecisionsParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DecisionsParser.java index 5557d07d0..0db52d27a 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/DecisionsParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DecisionsParser.java @@ -15,9 +15,11 @@ final class DecisionsParser extends AbstractParser { private static final String ADRTOOLS_DECISION_IMPORTER = "adrtools"; private static final String LOG4BRAINS_DECISION_IMPORTER = "log4brains"; + private static final String MADR_DECISION_IMPORTER = "madr"; static { DECISION_IMPORTERS.put(ADRTOOLS_DECISION_IMPORTER, "com.structurizr.importer.documentation.AdrToolsDecisionImporter"); + DECISION_IMPORTERS.put(MADR_DECISION_IMPORTER, "com.structurizr.importer.documentation.MadrDecisionImporter"); DECISION_IMPORTERS.put(LOG4BRAINS_DECISION_IMPORTER, "com.structurizr.importer.documentation.Log4brainsDecisionImporter"); } diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index 4394875a7..3d06a9661 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -708,7 +708,9 @@ void test_decisions() throws Exception { // adrtools decisions assertEquals(10, parser.getWorkspace().getDocumentation().getDecisions().size()); assertEquals(10, softwareSystem.getDocumentation().getDecisions().size()); - assertEquals(10, container.getDocumentation().getDecisions().size()); + + // madr decisions + assertEquals(19, container.getDocumentation().getDecisions().size()); // log4brains decisions assertEquals(4, component.getDocumentation().getDecisions().size()); diff --git a/structurizr-dsl/src/test/resources/dsl/decisions/madr/0000-use-markdown-any-decision-records.md b/structurizr-dsl/src/test/resources/dsl/decisions/madr/0000-use-markdown-any-decision-records.md new file mode 100644 index 000000000..df5c4f9d0 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/decisions/madr/0000-use-markdown-any-decision-records.md @@ -0,0 +1,30 @@ +--- +parent: Decisions +nav_order: 0 +--- +# Use Markdown Any Decision Records + +## Context and Problem Statement + +We want to record any decisions made in this project independent whether decisions concern the architecture ("architectural decision record"), the code, or other fields. +Which format and structure should these records follow? + +## Considered Options + +* [MADR](https://adr.github.io/madr/) 3.0.0 – The Markdown Any Decision Records +* [Michael Nygard's template](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions) – The first incarnation of the term "ADR" +* [Sustainable Architectural Decisions](https://www.infoq.com/articles/sustainable-architectural-design-decisions) – The Y-Statements +* Other templates listed at +* Formless – No conventions for file format and structure + +## Decision Outcome + +Chosen option: "MADR 3.0.0", because + +* Implicit assumptions should be made explicit. + Design documentation is important to enable people understanding the decisions later on. + See also [A rational design process: How and why to fake it](https://doi.org/10.1109/TSE.1986.6312940). +* MADR allows for structured capturing of any decision. +* The MADR format is lean and fits our development style. +* The MADR structure is comprehensible and facilitates usage & maintenance. +* The MADR project is vivid. diff --git a/structurizr-dsl/src/test/resources/dsl/decisions/madr/0001-use-CC0-or-MIT-as-license.md b/structurizr-dsl/src/test/resources/dsl/decisions/madr/0001-use-CC0-or-MIT-as-license.md new file mode 100644 index 000000000..09ec7aaf0 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/decisions/madr/0001-use-CC0-or-MIT-as-license.md @@ -0,0 +1,49 @@ +--- +parent: Decisions +nav_order: 1 +--- +# Dual License the Work + +## Context and Problem Statement + +Everything needs to be licensed, otherwise the default copyright laws apply. +For instance, in Germany that means users may not alter anything without explicitly asking for permission. +For more information see . + +We want to have MADR used without any hassle and that users can just go ahead and write MADRs. + +## Considered Options + +* [CC0](https://creativecommons.org/share-your-work/public-domain/cc0/) +* BSD3 +* MIT +* Dual license with MIT and CC0 +* No license +* Other open source licenses + +## Decision Outcome + +Chosen option: "Dual license", because this lets users choose whether CC0 or MIT fits better on their work. + +## Pros and Cons of the Options + +## CC0 + +* Good, because this license donates the content to "public domain" and does so as legally as possible. +* Bad, because it does not contain attribution - and [attribution is important](https://opensource.stackexchange.com/a/9126/5671). + +## BSD3 + +* Bad, because it [is unclear whether it can be used for documentation](https://opensource.stackexchange.com/a/9545/5671) + +## MIT + +* Good, because it [explicitly may be used for documentation](https://opensource.stackexchange.com/a/9545/5671) +* Good, because it is lean. + +## Dual license with MIT and CC0 + +With the SPDX identifier `MIT OR CC0-1.0`, the receiver of the documents can decide which license thay want to use. + +* Good, because offers freedom at the receiver +* Bad, because dual licensing is not widely known diff --git a/structurizr-dsl/src/test/resources/dsl/decisions/madr/0002-do-not-use-numbers-in-headings.md b/structurizr-dsl/src/test/resources/dsl/decisions/madr/0002-do-not-use-numbers-in-headings.md new file mode 100644 index 000000000..9f3197305 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/decisions/madr/0002-do-not-use-numbers-in-headings.md @@ -0,0 +1,24 @@ +--- +parent: Decisions +nav_order: 2 +--- +# Do Not Use Numbers in Headings + +## Context and Problem Statement + +How to render the first line in an ADR? +ADRs have to take a unique identifier. + +## Considered Options + +* Use the title only +* Add the ADR number in front of the title (e.g., "# 2. Do not use numbers in headings") + +## Decision Outcome + +Chosen option: "Use the title only", because + +* This is common in other markdown files, too. + One does not add numbering manually at the markdown files, but tries to get the numbers injected by the rendering framework or CSS. +* Enables renaming of ADRs (before publication) easily +* Allows copy'n'paste of ADRs from other repositories without having to worry about the numbers. diff --git a/structurizr-dsl/src/test/resources/dsl/decisions/madr/0003-provide-own-madr-tools.md b/structurizr-dsl/src/test/resources/dsl/decisions/madr/0003-provide-own-madr-tools.md new file mode 100644 index 000000000..00750fd68 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/decisions/madr/0003-provide-own-madr-tools.md @@ -0,0 +1,33 @@ +--- +parent: Decisions +nav_order: 3 +status: on hold +--- +# Write Own MADR Tooling + +## Context and Problem Statement + +Developers seem to like tooling to create ADRs. +That tooling should support MADR. +The tooling should be easy to install. + +## Considered Options + +* Include in [adr-tools](https://github.com/npryce/adr-tools), 924 stars as of 2018-06-14 +* Include in [adr-j](https://github.com/adoble/adr-j), 2 stars as of 2018-06-14 +* Include in [adr](https://github.com/phodal/adr), 72 stars as of 2018-06-14 +* Write own MADR tooling +* No tool support + +## Decision Outcome + +Chosen option: "Write own MADR tooling", because + +* adding MADR support to `adr-tools` [was rejected](https://github.com/npryce/adr-tools/pull/43) +* other tooling seem to a) modify MADR or b) do not keep up with changes on MADR. + +We accept that this comes with maintenance cost. + +## More Information + +An overview on current tooling of MADR is available at . diff --git a/structurizr-dsl/src/test/resources/dsl/decisions/madr/0004-write-own-toc-tool.md b/structurizr-dsl/src/test/resources/dsl/decisions/madr/0004-write-own-toc-tool.md new file mode 100644 index 000000000..038204407 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/decisions/madr/0004-write-own-toc-tool.md @@ -0,0 +1,29 @@ +--- +parent: Decisions +nav_order: 4 +--- +# Write Own TOC Tool + +## Context and Problem Statement + +ADRs have to be indexed somehow. E.g., for offering a website showing all ADRs. + +## Considered Options + +* Write own tool `adr-log` +* Use `adr-tools`' TOC functionality + +## Decision Outcome + +Chosen option: "Write own tool `adr-log`", because + +* we want to have the format `ADR-0001 - Title` in the TOC. +* `adr-tools` offers `title` only. + +We accept that changing `adr-tools` would also be possible. +It is prepared to included header and footer: . + +### Consequences + +* Good, because `adr-log` is installable using `npm install -g adr-log`, which is easier than installing `adr-tools`. +* Bad, because another tool has to be maintained diff --git a/structurizr-dsl/src/test/resources/dsl/decisions/madr/0005-use-dashes-in-filenames.md b/structurizr-dsl/src/test/resources/dsl/decisions/madr/0005-use-dashes-in-filenames.md new file mode 100644 index 000000000..4c94869e8 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/decisions/madr/0005-use-dashes-in-filenames.md @@ -0,0 +1,26 @@ +--- +parent: Decisions +nav_order: 5 +--- +# Use Dashes in Filenames + +## Context and Problem Statement + +What is the pattern of the filename where an ADR is stored? + +## Considered Options + +* `NNNN-title-with-dashes.md` - format used by [adr-tools](https://github.com/npryce/adr-tools) +* `YYYY-MM-DD Title` - see + +## Decision Outcome + +Chosen option: "`NNNN-title-with-dashes.md`", because + +* `NNNN` provides a unique number, which can be used for referencing in the forms + * `ADR-0001` in plain text and + * by `@ADR(1)` Java code (enabled by [e-adr](https://adr.github.io/e-adr/)) +* The creation time of an ADR is of historical interest only, if it gets updated somehow. + The arguments are similar than the ones by [Does Git have keyword expansion?](https://git.wiki.kernel.org/index.php/GitFaq#Does_Git_have_keyword_expansion.3F) +* Having no spaces in filenames eases working in the command line +* This is exactly the format offered by [adr-tools](https://github.com/npryce/adr-tools) diff --git a/structurizr-dsl/src/test/resources/dsl/decisions/madr/0006-use-names-as-identifier.md b/structurizr-dsl/src/test/resources/dsl/decisions/madr/0006-use-names-as-identifier.md new file mode 100644 index 000000000..1734742b8 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/decisions/madr/0006-use-names-as-identifier.md @@ -0,0 +1,24 @@ +--- +parent: Decisions +nav_order: 6 +--- +# Use Names as Identifier + +## Context and Problem Statement + +An option is listed at "Considered Options" and repeated at "Pros and Cons of the Options". Finally, the chosen option is stated at "Decision Outcome". + +## Decision Drivers + +* Easy to read +* Easy to write +* Avoid copy and paste errors + +## Considered Options + +* Repeat all option names if they occur +* Assign an identifier to an option, e.g., `[A] Use gradle as build tool` + +## Decision Outcome + +Chosen option: "Repeat all option names if they occur", because 1) there is no markdown standard for identifiers, 2) the document is harder to read if there are multiple options which must be remembered. diff --git a/structurizr-dsl/src/test/resources/dsl/decisions/madr/0007-do-not-emphasize-line-headings.md b/structurizr-dsl/src/test/resources/dsl/decisions/madr/0007-do-not-emphasize-line-headings.md new file mode 100644 index 000000000..c12abeab3 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/decisions/madr/0007-do-not-emphasize-line-headings.md @@ -0,0 +1,23 @@ +--- +parent: Decisions +nav_order: 7 +--- +# Do Not Emphasize Line Headings + +## Context and Problem Statement + +MADR contains lines such as `Chosen option: "[option 1]"`. Should "Chosen option" be emphasized? + +## Decision Drivers + +* MADR should be easy to read +* MADR should be easy to write + +## Considered Options + +* Do not emphasize line headings +* Emphasize line headings + +## Decision Outcome + +Chosen option: "Do not emphasize line headings", because 1) these headings always are put at the beginning of a line and followed by a colon. Thus, they are already easy to identified as line heading. 2) Readers not familiar with Markdown might be confused by stars in the text. diff --git a/structurizr-dsl/src/test/resources/dsl/decisions/madr/0008-add-status-field.md b/structurizr-dsl/src/test/resources/dsl/decisions/madr/0008-add-status-field.md new file mode 100644 index 000000000..bae2bb479 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/decisions/madr/0008-add-status-field.md @@ -0,0 +1,100 @@ +--- +parent: Decisions +nav_order: 8 +--- +# Add Status Field + +## Context and Problem Statement + +Technical Story: + +ADRs have a status. Should this be tracked? And if it should, how should we track it? + +## Considered Options + +* Use YAML front matter +* Use badge +* Use text line +* Use separate heading +* Use table +* Do not add status + +## Decision Outcome + +Chosen option: "Use YAML front matter", because comes out best (see below). + +## Pros and Cons of the Options + +### Use YAML front matter + +Example: + +```markdown +--- +parent: Decisions +nav_order: 3 +status: on hold +--- +# Write own MADR tooling +``` + +* Good, because YAML front matter is supported by most Markdown parsers + +### Use badge + +#### Examples + +* ![Example "Use Angular" with "status: accepted"](0008-example-badge.png) +* [![Example "status: superseded"](https://img.shields.io/badge/status-superseeded_by_ADR_0001-orange.svg?style=flat-square)](https://github.com/adr/madr/blob/main/docs/decisions/0001-use-CC0-as-license.md) + +--- + +* Good, because plain markdown +* Good, because looks good +* Bad, because hard to read in markdown source +* Bad, because relies on the online service or [local badges have to be generated](https://github.com/badges/shields#using-the-badge-library) +* Bad, because at local usages, many badges have to be generated (superseeded-by-ADR-0006, for each ADR number) +* Bad, because not easy to write + +### Use text line + +Example: `Status: Accepted` + +* Good, because plain markdown +* Good, because easy to read +* Good, because easy to write +* Good, because looks OK in both markdown-source (MD) and in rendered versions (HTML, PDF) +* Good, because no dependencies on external tools +* Good, because single line indicates the current state +* Bad, because "Status" line needs to be maintained +* Bad, because uses space at the beginning. When users read MADR, they should directly dive into the context and problem and not into the status + +### Use separate heading + +Example: ![example for separate heading](0008-example-separate-heading.png) + +* Good, because plain markdown +* Good, because easy to write +* Bad, because it uses much space: At least three lines: heading, status, separating empty line + +### Use table + +Example: ![example for table](0008-example-table.png) + +* Good, because history can be included +* Good, because multiple entries can be made +* Good, because already implemented in `adr-tools` fork +* Bad, because not covered by the [CommonMark specification 0.28 (2017-08-01)](http://spec.commonmark.org/0.28/) +* Bad, because hard to read +* Bad, because outdated entries cannot be easily identified +* Bad, because needs more markdown training + +### Do not add status + +* Good, because MADR is kept lean +* Bad, because users demand state field +* Bad, because not in line with other ADR templates + +## More Information + +See [ADR-0013](0013-use-yaml-front-matter-for-meta-data.md) for more reasoning on using YAML front matter. diff --git a/structurizr-dsl/src/test/resources/dsl/decisions/madr/0008-example-badge.png b/structurizr-dsl/src/test/resources/dsl/decisions/madr/0008-example-badge.png new file mode 100644 index 0000000000000000000000000000000000000000..20ced2835dea4f5b760aad8f2a739d60e56e8c6e GIT binary patch literal 2073 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 QUjyJ^>ughI6?hBy8%Xr<1ONa4 literal 0 HcmV?d00001 diff --git a/structurizr-dsl/src/test/resources/dsl/decisions/madr/0008-example-table.png b/structurizr-dsl/src/test/resources/dsl/decisions/madr/0008-example-table.png new file mode 100644 index 0000000000000000000000000000000000000000..768c2d145f80bdb098ec8d00cb2d4e53a24927bf GIT binary patch literal 1334 zcmeAS@N?(olHy`uVBq!ia0vp^CxO_Xg9%8U^DUdsz`*jy)5S5QV$Rz+-s=_{2)I@o z=1=n6Hp!7KlFKPcm17#yn-&kX2psKfZS)*>@N>kpPPVcpu3EnX{lx}I>T)KY!^ZCM_*UM^hdF$5u zEV~&t#ktQY^($wN66EYjI4i zVsvtKqMOK#swl=uD}rM)r*(Q4mlsWwZa=i|+0j!+r`a$1bL^e8(Khi%hi}}e^9xO{ zJ0G66o@e4On{9HnTTSli@WxigHEwQy8L(!zaPedBsmHhM<9cWD_L!T*p^dI*C2vRB zMnCj9eQF<9@i(WZ#%FV$Uw^jwwQTwcwdl&{U(Y9>c7Fcm%#$xnR_FJ>Zkc*}OQ*rE z34+_c@g&|{6qjn%Q!f+!{g>5e!%D;cgtQrQiShIAnyhOyY|GhTs(qj3TV#&!?oS6M z%q?5Ps6DrILq30LOjl_9#BeT0p z)*Ly!=u2n61yA%gALF*BvfDS}uI*2*w!Hi5;=TjV`$gl6cBm;DmTu6?%<~Km-<5g! z^7dB~-0vTp_L6^l9nd*PW)|AWcAaoA`K@}}^yVeE{c7cvn=-wpS2uUL9iQ>)!Se|d z1k@ysxV)Mo-y!$3t#`w;<)|Z7KLow zyF0q*5L;#Sy-9cSBfWRpsxdci+9Px6`{^yudDJ(>FX>}_U;0h7JxZ8);o+d`M=F`0 z#QXd8W$UQyd*a~xM`(wY#iref;hxJjHuC*#XDfbFExv8W-_?hw)vy2D@pyx)f3Az1 zmG$3O{MLDa$=+#Mn--RDd@Ad7{iNokwGKz;&D?%xdH&S%=59T@+p2ecILsO;luFKm3PDYzPd~$wnUc;@5qqE(PAJZ~=`K$X!TA!t5QTxK5 zN&n|$=o!De`|6)Twvx%qyUR}QS5IcycI0c`OUhx>W_aqKcp4`i$exaS3j3^P6 + +## Considered Options + +* Include in section "More Information" +* Use tables +* Use heading together with a bullet list directly after status +* Use heading together with a bullet list directly after "Decision Outcome" +* Use heading together with a bullet list at the end +* Do not add links + +## Decision Outcome + +Chosen option: "Include in section 'More Information'", because comes out best (see below). + +## Pros and Cons of the Options + +### Include in section "More Information" + +Example: + +```markdown +## More Information + +[ADR-0008](0008-add-status-field.md) reasons on adding meta data (such as status). +``` + +* Good, because provides freedom to the user +* Bad, because parsing gets harder + +### Use tables + +* Good, because easy to write +* Good, because history is shown (enabled by concept) +* Good, because [current `adr-tools`' support](https://github.com/npryce/adr-tools/pull/43) uses tables to describe links. +* Bad, because not supported by the CommonMark spec +* Bad, because unclear whether a link was super seeded by another one +* Bad, because valid links not clear at first sight (there might be outdated links shown) + +### Use heading together with a bullet list directly after status + +Example: +![grafik](https://user-images.githubusercontent.com/1366654/36787434-6a63e318-1c8a-11e8-8824-4dd7b3d0f2c6.png) + +* Good, because easy to write +* Good, because supported by the CommonMark spec +* Bad, because not consistent with the status label (refs ) + +### Use heading together with a bullet list directly after "Decision Outcome" + +* Good, because easy to write +* Good, because supported by the CommonMark spec +* Good, because the options are first introduced and then the links +* Good, because consistent with position of "Decision Outcome" +* Bad, because reader might get distracted: He might expect explanation of the options instead of links to something else +* Bad, because not consistent with scientific papers, where related work and future work are coming after the discussion of the content. + +### Use heading together with a bullet list at the end + +* Good, because easy to write +* Good, because supported by the CommonMark spec +* Good, because the options and pros/cons are kept together with the option list. +* Good, because consistent with pattern format + +### Do not add links + +* Good, because template stays minimal diff --git a/structurizr-dsl/src/test/resources/dsl/decisions/madr/0010-support-categories.md b/structurizr-dsl/src/test/resources/dsl/decisions/madr/0010-support-categories.md new file mode 100644 index 000000000..a9160f5d9 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/decisions/madr/0010-support-categories.md @@ -0,0 +1,106 @@ +--- +parent: Decisions +nav_order: 10 +--- +# Support Categories + +## Context and Problem Statement + +ADRs are recorded. The number of ADRs grows and the context/topic/scope of ADRs might be different (e.g., frontend, backend) + +## Decision Drivers + +* Easy to find groups ADRs in hundreds of ADRs +* Easy to group +* Easy to create +* Good finding without external tooling +* Keep newcomers in mind (should be doable in <10 minutes) +* Keep template lean + +## Considered Options + +* Use labels +* Add `* Category: CATEGORY` directly under the heading (similar to ) +* Use YAML front matter +* Encode category in filename +* Use subfolders with local IDs +* Use subfolders with global IDs +* Don't do it. + +## Decision Outcome + +Chosen option: "Use subfolders with local IDs", because comes out best (see below). + +## Pros and Cons of the Options + +### Use labels + +Example: + +Use Angular ![category-frontend](https://img.shields.io/badge/category-frontend-blue.svg?style=flat-square) + +`![category-frontend](https://img.shields.io/badge/category-frontend-blue.svg?style=flat-square)` + +* Good, because full markdown +* Good, because linking to an overview page is possible (using markdown) +* Bad, because not straight-forward to parse +* Bad, because no simple filtering using `ls` or Windows Explorer is possible + +### Add `* Category: CATEGORY` directly under the heading + +* Good, because full markdown +* Good, because linking to an overview page is possible (using markdown) +* Good, because straight-forward to parse +* Bad, because no simple filtering using `ls` or Windows Explorer is possible + +### Use YAML front matter + +Example: + +```yaml +--- +category: frontend +--- +``` + +* Good, because nearly straight-forward to parse +* Good, because Jekyll supports it +* Bad, because YAML front matter is not part of the [CommonMarc Spec](http://spec.commonmark.org/) +* Bad, because no simple filtering using `ls` or Windows Explorer is possible + +### Encode category in filename + +Example: `0050--frontend--title-with-dashes.md` + +* Good, because programmatic filtering is possible +* Good, because `ls -la | grep --category--` works +* Bad, because plain file list in Windows explorer cannot be filtered +* Bad, because as bad as [TagSpaces](https://www.tagspaces.org/), which stores the tags in the filenames in brackets. E.g., `demo[demotag secondtag].md`. + +### Use subfolders with local IDs + +Optionally "to-be-categorized" folder. + +One level of subfolder, not nested + +#### Examples + +* `docs/decisions/smar/0000-secure-entities.md` +* `docs/decisions/smar/0001-flexible-properties-selection.md` + +#### Pros/cons + +* Good, because grouping is done by folders (which are natural for grouping) +* Good, because typos can easily be spotted +* Bad, because there is no unique number identifying an ADR +* Bad, because two indices have to be maintained (`adr-log` needs to be updated) +* Bad, because [e-adr](https://github.com/adr/e-adr) needs to be adapted to `@ADR("category", number)` (not that bad) +* Bad, because when category is unknown it is hard to find the right folder +* Bad, because using categories might be hampering newcomers + +### Use subfolders with global IDs + +#### Examples + +* `docs/decisions/smar/0005-secure-entities.md` +* `docs/decisions/smar/0047-flexible-properties-selection.md` diff --git a/structurizr-dsl/src/test/resources/dsl/decisions/madr/0011-use-asterisk-as-list-marker.md b/structurizr-dsl/src/test/resources/dsl/decisions/madr/0011-use-asterisk-as-list-marker.md new file mode 100644 index 000000000..60956c008 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/decisions/madr/0011-use-asterisk-as-list-marker.md @@ -0,0 +1,20 @@ +--- +parent: Decisions +nav_order: 11 +--- +# Use Asterisk as List Marker + +## Context and Problem Statement + +Lists in Markdown can be indicated by `*` (asterisk) or `-` (hyphen). + +## Considered Options + +* Use an asterisk +* Use a hyphen + +## Decision Outcome + +Chosen option: "Use an asterisk", because an asterisk does not have a meaning of "good" or "bad", whereas a hyphen `-` could be read as indicator of something negative (in contrast to `+`, which could be more be read as "good"). + +According to the [Markdown Style Guide](http://www.cirosantilli.com/markdown-style-guide/), an asterisk as list marker is more readable (see [readability profile](http://www.cirosantilli.com/markdown-style-guide/#readability-profile)). diff --git a/structurizr-dsl/src/test/resources/dsl/decisions/madr/0012-use-curly-braces-to-denote-placeholder.md b/structurizr-dsl/src/test/resources/dsl/decisions/madr/0012-use-curly-braces-to-denote-placeholder.md new file mode 100644 index 000000000..c73921b26 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/decisions/madr/0012-use-curly-braces-to-denote-placeholder.md @@ -0,0 +1,46 @@ +--- +parent: Decisions +nav_order: 12 +--- +# Use Curly Braces to Denote Placeholders + +## Context and Problem Statement + +When crafting an ADR placeholders need to be replaced by real values. +How to mark the placeholders? + +## Considered Options + +* Use curly braces +* Use square brackets +* Use less-than and greater-than + +## Decision Outcome + +Chosen option: "Use curly braces", because comes out best (see below). + +## Pros and Cons of the Options + +### Use curly braces + +Example: `{option 1}`. + +* Good, because [consistent to mustache templates](https://krasimirtsonev.com/blog/article/markdown-smart-placeholders). +* Good, because no confusion with markdown notation for links + +### Use square brackets + +Example: `[option 1]`. + +* Good, because used in MADR 1.x and MADR 2.x +* Bad, because confusion with markdown notation for links +* Bad, because some users did not remove the brackets. Example: `Date: [2021-03-12]` or `Good, because [user no longer activatess shortcut accidently when entering task]`. + +### Use less-than and greater-than + +Example: `Documentation - * on the Structurizr website for more details. + * See Documentation + * and Decisions for more details. */ public final class Documentation { private List
    sections = new ArrayList<>(); - private Set decisions = new HashSet<>(); - private Set images = new HashSet<>(); + private Set decisions = new TreeSet<>(); + private Set images = new TreeSet<>(); public Documentation() { } @@ -76,12 +76,12 @@ void setSections(Collection
    sections) { * @return a Set of Decision objects */ public Set getDecisions() { - return new HashSet<>(decisions); + return new TreeSet<>(decisions); } void setDecisions(Set decisions) { if (decisions != null) { - this.decisions = new HashSet<>(decisions); + this.decisions = new TreeSet<>(decisions); } } @@ -136,12 +136,12 @@ public void addImage(Image image) { * @return a Set of {@link Image} objects */ public Set getImages() { - return new HashSet<>(images); + return new TreeSet<>(images); } void setImages(Set images) { if (images != null) { - this.images = new HashSet<>(images); + this.images = new TreeSet<>(images); } } @@ -155,8 +155,8 @@ public boolean isEmpty() { */ public void clear() { sections = new ArrayList<>(); - decisions = new HashSet<>(); - images = new HashSet<>(); + decisions = new TreeSet<>(); + images = new TreeSet<>(); } } \ No newline at end of file diff --git a/structurizr-core/src/main/java/com/structurizr/documentation/Image.java b/structurizr-core/src/main/java/com/structurizr/documentation/Image.java index b76845f13..b28404215 100644 --- a/structurizr-core/src/main/java/com/structurizr/documentation/Image.java +++ b/structurizr-core/src/main/java/com/structurizr/documentation/Image.java @@ -3,7 +3,7 @@ /** * Represents a base64 encoded image (png/jpg/gif). */ -public final class Image { +public final class Image implements Comparable { private String name; private String content; @@ -42,4 +42,9 @@ void setType(String type) { this.type = type; } + @Override + public int compareTo(Image image) { + return getName().compareTo(image.getName()); + } + } \ No newline at end of file diff --git a/structurizr-core/src/main/java/com/structurizr/model/Container.java b/structurizr-core/src/main/java/com/structurizr/model/Container.java index d6aff51b8..5e7f799f0 100644 --- a/structurizr-core/src/main/java/com/structurizr/model/Container.java +++ b/structurizr-core/src/main/java/com/structurizr/model/Container.java @@ -15,7 +15,7 @@ public final class Container extends StaticStructureElement implements Documenta private SoftwareSystem parent; private String technology; - private Set components = new LinkedHashSet<>(); + private Set components = new TreeSet<>(); private Documentation documentation = new Documentation(); @@ -118,12 +118,12 @@ void remove(Component component) { * @return a Set of Component objects */ public Set getComponents() { - return new HashSet<>(components); + return new TreeSet<>(components); } void setComponents(Set components) { if (components != null) { - this.components = new HashSet<>(components); + this.components = new TreeSet<>(components); } } diff --git a/structurizr-core/src/main/java/com/structurizr/model/DeploymentNode.java b/structurizr-core/src/main/java/com/structurizr/model/DeploymentNode.java index e40aaeac5..41f872174 100644 --- a/structurizr-core/src/main/java/com/structurizr/model/DeploymentNode.java +++ b/structurizr-core/src/main/java/com/structurizr/model/DeploymentNode.java @@ -25,10 +25,10 @@ public final class DeploymentNode extends DeploymentElement { private String technology; private String instances = "1"; - private Set children = new HashSet<>(); - private Set infrastructureNodes = new HashSet<>(); - private Set softwareSystemInstances = new HashSet<>(); - private Set containerInstances = new HashSet<>(); + private Set children = new TreeSet<>(); + private Set infrastructureNodes = new TreeSet<>(); + private Set softwareSystemInstances = new TreeSet<>(); + private Set containerInstances = new TreeSet<>(); /** * Adds a software system instance to this deployment node, replicating relationships. @@ -306,12 +306,12 @@ public Relationship uses(InfrastructureNode destination, String description, Str * @return a Set of DeploymentNode objects */ public Set getChildren() { - return new HashSet<>(children); + return new TreeSet<>(children); } void setChildren(Set children) { if (children != null) { - this.children = new HashSet<>(children); + this.children = new TreeSet<>(children); } } @@ -321,12 +321,12 @@ void setChildren(Set children) { * @return a Set of InfrastructureNode objects */ public Set getInfrastructureNodes() { - return new HashSet<>(infrastructureNodes); + return new TreeSet<>(infrastructureNodes); } void setInfrastructureNodes(Set infrastructureNodes) { if (infrastructureNodes != null) { - this.infrastructureNodes = new HashSet<>(infrastructureNodes); + this.infrastructureNodes = new TreeSet<>(infrastructureNodes); } } @@ -376,12 +376,12 @@ public boolean hasContainerInstances() { * @return a Set of SoftwareSystemInstance objects */ public Set getSoftwareSystemInstances() { - return new HashSet<>(softwareSystemInstances); + return new TreeSet<>(softwareSystemInstances); } void setSoftwareSystemInstances(Set softwareSystemInstances) { if (softwareSystemInstances != null) { - this.softwareSystemInstances = new HashSet<>(softwareSystemInstances); + this.softwareSystemInstances = new TreeSet<>(softwareSystemInstances); } } @@ -391,12 +391,12 @@ void setSoftwareSystemInstances(Set softwareSystemInstan * @return a Set of ContainerInstance objects */ public Set getContainerInstances() { - return new HashSet<>(containerInstances); + return new TreeSet<>(containerInstances); } void setContainerInstances(Set containerInstances) { if (containerInstances != null) { - this.containerInstances = new HashSet<>(containerInstances); + this.containerInstances = new TreeSet<>(containerInstances); } } diff --git a/structurizr-core/src/main/java/com/structurizr/model/Element.java b/structurizr-core/src/main/java/com/structurizr/model/Element.java index a80ad2a75..7294b30bd 100644 --- a/structurizr-core/src/main/java/com/structurizr/model/Element.java +++ b/structurizr-core/src/main/java/com/structurizr/model/Element.java @@ -4,7 +4,8 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; -import java.util.*; +import java.util.Set; +import java.util.TreeSet; /** * This is the superclass for all model elements. @@ -16,7 +17,7 @@ public abstract class Element extends ModelItem { private String name; private String description; - private Set relationships = new LinkedHashSet<>(); + private Set relationships = new TreeSet<>(); protected Element() { } @@ -83,12 +84,12 @@ public void setDescription(String description) { * @return a Set of Relationship objects, or an empty set if none exist */ public Set getRelationships() { - return new LinkedHashSet<>(relationships); + return new TreeSet<>(relationships); } void setRelationships(Set relationships) { if (relationships != null) { - this.relationships = new LinkedHashSet<>(relationships); + this.relationships = new TreeSet<>(relationships); } } @@ -153,7 +154,7 @@ public Relationship getEfferentRelationshipWith(Element element) { * @return a Set of Relationship objects; empty if no relationships exist */ public Set getEfferentRelationshipsWith(Element element) { - Set set = new HashSet<>(); + Set set = new TreeSet<>(); if (element != null) { for (Relationship relationship : relationships) { diff --git a/structurizr-core/src/main/java/com/structurizr/model/HttpHealthCheck.java b/structurizr-core/src/main/java/com/structurizr/model/HttpHealthCheck.java index 525e615c5..4ff1620f0 100644 --- a/structurizr-core/src/main/java/com/structurizr/model/HttpHealthCheck.java +++ b/structurizr-core/src/main/java/com/structurizr/model/HttpHealthCheck.java @@ -2,11 +2,12 @@ import java.util.HashMap; import java.util.Map; +import java.util.TreeMap; /** * Describes a HTTP based health check. */ -public final class HttpHealthCheck { +public final class HttpHealthCheck implements Comparable { /** a name for the health check */ private String name; @@ -15,7 +16,7 @@ public final class HttpHealthCheck { private String url; /** the headers that should be sent in the HTTP request */ - private Map headers = new HashMap<>(); + private final Map headers = new TreeMap<>(); /** the polling interval, in seconds */ private int interval; @@ -130,4 +131,15 @@ public int hashCode() { return result; } + @Override + public int compareTo(HttpHealthCheck healthCheck) { + int result = getName().compareTo(healthCheck.getName()); + + if (result == 0) { + result = getUrl().compareTo(healthCheck.getUrl()); + } + + return result; + } + } \ No newline at end of file diff --git a/structurizr-core/src/main/java/com/structurizr/model/InfrastructureNode.java b/structurizr-core/src/main/java/com/structurizr/model/InfrastructureNode.java index 1910241be..c9c293c18 100644 --- a/structurizr-core/src/main/java/com/structurizr/model/InfrastructureNode.java +++ b/structurizr-core/src/main/java/com/structurizr/model/InfrastructureNode.java @@ -2,7 +2,9 @@ import com.fasterxml.jackson.annotation.JsonIgnore; -import java.util.*; +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.Set; /** *

    diff --git a/structurizr-core/src/main/java/com/structurizr/model/Model.java b/structurizr-core/src/main/java/com/structurizr/model/Model.java index 3903fa767..3e49dd9e0 100644 --- a/structurizr-core/src/main/java/com/structurizr/model/Model.java +++ b/structurizr-core/src/main/java/com/structurizr/model/Model.java @@ -16,18 +16,18 @@ public final class Model implements PropertyHolder { private IdGenerator idGenerator = new SequentialIntegerIdGeneratorStrategy(); - private final Set elements = new LinkedHashSet<>(); + private final Set elements = new TreeSet<>(); private final Map elementsById = new HashMap<>(); - private final Set relationships = new LinkedHashSet<>(); + private final Set relationships = new TreeSet<>(); private final Map relationshipsById = new HashMap<>(); private Enterprise enterprise; - private Set people = new LinkedHashSet<>(); - private Set softwareSystems = new LinkedHashSet<>(); - private Set deploymentNodes = new LinkedHashSet<>(); - private Set customElements = new LinkedHashSet<>(); + private Set people = new TreeSet<>(); + private Set softwareSystems = new TreeSet<>(); + private Set deploymentNodes = new TreeSet<>(); + private Set customElements = new TreeSet<>(); private ImpliedRelationshipsStrategy impliedRelationshipsStrategy = new DefaultImpliedRelationshipsStrategy(); @@ -70,10 +70,9 @@ public SoftwareSystem addSoftwareSystem(@Nonnull String name, @Nullable String d SoftwareSystem softwareSystem = new SoftwareSystem(); softwareSystem.setName(name); softwareSystem.setDescription(description); + softwareSystem.setId(idGenerator.generateId(softwareSystem)); softwareSystems.add(softwareSystem); - - softwareSystem.setId(idGenerator.generateId(softwareSystem)); addElementToInternalStructures(softwareSystem); return softwareSystem; @@ -108,10 +107,9 @@ public Person addPerson(@Nonnull String name, @Nullable String description) { Person person = new Person(); person.setName(name); person.setDescription(description); + person.setId(idGenerator.generateId(person)); people.add(person); - - person.setId(idGenerator.generateId(person)); addElementToInternalStructures(person); return person; @@ -167,11 +165,11 @@ Container addContainer(SoftwareSystem parent, @Nonnull String name, @Nullable St container.setName(name); container.setDescription(description); container.setTechnology(technology); + container.setId(idGenerator.generateId(container)); container.setParent(parent); parent.add(container); - container.setId(idGenerator.generateId(container)); addElementToInternalStructures(container); return container; @@ -186,11 +184,11 @@ Component addComponent(Container parent, String name, String description, String component.setName(name); component.setDescription(description); component.setTechnology(technology); + component.setId(idGenerator.generateId(component)); component.setParent(parent); parent.add(component); - component.setId(idGenerator.generateId(component)); addElementToInternalStructures(component); return component; @@ -310,7 +308,7 @@ private void removeRelationshipFromInternalStructures(Relationship relationship) @JsonIgnore @Nonnull public Set getElements() { - return new LinkedHashSet<>(elements); + return new TreeSet<>(elements); } /** @@ -337,7 +335,7 @@ public Element getElement(@Nonnull String id) { @JsonIgnore @Nonnull public Set getRelationships() { - return new LinkedHashSet<>(this.relationships); + return new TreeSet<>(this.relationships); } /** @@ -363,12 +361,12 @@ public Relationship getRelationship(@Nonnull String id) { */ @Nonnull public Set getCustomElements() { - return new LinkedHashSet<>(customElements); + return new TreeSet<>(customElements); } void setCustomElements(Set customElements) { if (customElements != null) { - this.customElements = new LinkedHashSet<>(customElements); + this.customElements = new TreeSet<>(customElements); } } @@ -379,12 +377,12 @@ void setCustomElements(Set customElements) { */ @Nonnull public Set getPeople() { - return new LinkedHashSet<>(people); + return new TreeSet<>(people); } void setPeople(Set people) { if (people != null) { - this.people = new LinkedHashSet<>(people); + this.people = new TreeSet<>(people); } } @@ -395,12 +393,12 @@ void setPeople(Set people) { */ @Nonnull public Set getSoftwareSystems() { - return new LinkedHashSet<>(softwareSystems); + return new TreeSet<>(softwareSystems); } void setSoftwareSystems(Set softwareSystems) { if (softwareSystems != null) { - this.softwareSystems = new LinkedHashSet<>(softwareSystems); + this.softwareSystems = new TreeSet<>(softwareSystems); } } @@ -411,12 +409,12 @@ void setSoftwareSystems(Set softwareSystems) { */ @Nonnull public Set getDeploymentNodes() { - return new LinkedHashSet<>(deploymentNodes); + return new TreeSet<>(deploymentNodes); } void setDeploymentNodes(Set deploymentNodes) { if (deploymentNodes != null) { - this.deploymentNodes = new LinkedHashSet<>(deploymentNodes); + this.deploymentNodes = new TreeSet<>(deploymentNodes); } } @@ -791,6 +789,8 @@ DeploymentNode addDeploymentNode(DeploymentNode parent, @Nullable String environ deploymentNode.setParent(parent); deploymentNode.setInstances(instances); deploymentNode.setEnvironment(environment); + deploymentNode.setId(idGenerator.generateId(deploymentNode)); + if (properties != null) { deploymentNode.setProperties(properties); } @@ -799,7 +799,6 @@ DeploymentNode addDeploymentNode(DeploymentNode parent, @Nullable String environ deploymentNodes.add(deploymentNode); } - deploymentNode.setId(idGenerator.generateId(deploymentNode)); addElementToInternalStructures(deploymentNode); return deploymentNode; @@ -821,11 +820,12 @@ InfrastructureNode addInfrastructureNode(DeploymentNode parent, @Nonnull String infrastructureNode.setTechnology(technology); infrastructureNode.setParent(parent); infrastructureNode.setEnvironment(parent.getEnvironment()); + infrastructureNode.setId(idGenerator.generateId(infrastructureNode)); + if (properties != null) { infrastructureNode.setProperties(properties); } - infrastructureNode.setId(idGenerator.generateId(infrastructureNode)); addElementToInternalStructures(infrastructureNode); return infrastructureNode; diff --git a/structurizr-core/src/main/java/com/structurizr/model/ModelItem.java b/structurizr-core/src/main/java/com/structurizr/model/ModelItem.java index 2be654cd6..7f936d238 100644 --- a/structurizr-core/src/main/java/com/structurizr/model/ModelItem.java +++ b/structurizr-core/src/main/java/com/structurizr/model/ModelItem.java @@ -11,14 +11,14 @@ /** * The base class for elements and relationships. */ -public abstract class ModelItem implements PropertyHolder { +public abstract class ModelItem implements PropertyHolder, Comparable { private String id = ""; - private Set tags = new LinkedHashSet<>(); + private final Set tags = new LinkedHashSet<>(); private String url; private Map properties = new HashMap<>(); - private Set perspectives = new HashSet<>(); + private final Set perspectives = new TreeSet<>(); @JsonIgnore public abstract String getCanonicalName(); @@ -35,7 +35,7 @@ public String getId() { return id; } - void setId(String id) { + protected void setId(String id) { this.id = id; } @@ -174,7 +174,7 @@ void setProperties(Map properties) { * @return a Set of Perspective objects (empty if there are none) */ public Set getPerspectives() { - return new HashSet<>(perspectives); + return new TreeSet<>(perspectives); } void setPerspectives(Set perspectives) { @@ -227,4 +227,16 @@ public Perspective addPerspective(String name, String description, String value) return perspective; } + @Override + public int compareTo(ModelItem modelItem) { + try { + int id1 = Integer.parseInt(getId()); + int id2 = Integer.parseInt(modelItem.getId()); + + return id1 - id2; + } catch (NumberFormatException nfe) { + return getId().compareTo(modelItem.getId()); + } + } + } \ No newline at end of file diff --git a/structurizr-core/src/main/java/com/structurizr/model/Perspective.java b/structurizr-core/src/main/java/com/structurizr/model/Perspective.java index b20285392..5b55e757e 100644 --- a/structurizr-core/src/main/java/com/structurizr/model/Perspective.java +++ b/structurizr-core/src/main/java/com/structurizr/model/Perspective.java @@ -4,7 +4,7 @@ * Represents an architectural perspective, that can be applied to elements and relationships. * See https://www.viewpoints-and-perspectives.info/home/perspectives/ for more details of this concept. */ -public final class Perspective { +public final class Perspective implements Comparable { private String name; private String description; @@ -73,4 +73,9 @@ public int hashCode() { return getName().hashCode(); } + @Override + public int compareTo(Perspective perspective) { + return getName().compareTo(perspective.getName()); + } + } \ No newline at end of file diff --git a/structurizr-core/src/main/java/com/structurizr/model/SoftwareSystem.java b/structurizr-core/src/main/java/com/structurizr/model/SoftwareSystem.java index 74cf5d85d..005cdda30 100644 --- a/structurizr-core/src/main/java/com/structurizr/model/SoftwareSystem.java +++ b/structurizr-core/src/main/java/com/structurizr/model/SoftwareSystem.java @@ -7,9 +7,9 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.Arrays; -import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Set; +import java.util.TreeSet; /** * Represents a "software system" in the C4 model. @@ -18,7 +18,7 @@ public final class SoftwareSystem extends StaticStructureElement implements Docu private Location location = Location.Unspecified; - private Set containers = new LinkedHashSet<>(); + private Set containers = new TreeSet<>(); private Documentation documentation = new Documentation(); @@ -66,12 +66,12 @@ void add(Container container) { */ @Nonnull public Set getContainers() { - return new HashSet<>(containers); + return new TreeSet<>(containers); } void setContainers(Set containers) { if (containers != null) { - this.containers = new HashSet<>(containers); + this.containers = new TreeSet<>(containers); } } diff --git a/structurizr-core/src/main/java/com/structurizr/model/StaticStructureElementInstance.java b/structurizr-core/src/main/java/com/structurizr/model/StaticStructureElementInstance.java index 6ed26d0c2..d7c5d3ae7 100644 --- a/structurizr-core/src/main/java/com/structurizr/model/StaticStructureElementInstance.java +++ b/structurizr-core/src/main/java/com/structurizr/model/StaticStructureElementInstance.java @@ -6,8 +6,8 @@ import javax.annotation.Nonnull; import java.util.Collections; -import java.util.HashSet; import java.util.Set; +import java.util.TreeSet; /** * Represents a deployment instance of a {@link SoftwareSystem} or {@link Container}, which can be added to a {@link DeploymentNode}. @@ -17,9 +17,9 @@ public abstract class StaticStructureElementInstance extends DeploymentElement { private static final int DEFAULT_HEALTH_CHECK_INTERVAL_IN_SECONDS = 60; private static final long DEFAULT_HEALTH_CHECK_TIMEOUT_IN_MILLISECONDS = 0; - private Set deploymentGroups = new HashSet<>(); + private Set deploymentGroups = new TreeSet<>(); private int instanceId; - private Set healthChecks = new HashSet<>(); + private Set healthChecks = new TreeSet<>(); StaticStructureElementInstance() { } @@ -53,15 +53,15 @@ public Set getDeploymentGroups() { if (deploymentGroups.isEmpty()) { return Collections.singleton(DEFAULT_DEPLOYMENT_GROUP); } else { - return new HashSet<>(deploymentGroups); + return new TreeSet<>(deploymentGroups); } } void setDeploymentGroups(Set deploymentGroups) { if (deploymentGroups != null) { - this.deploymentGroups = new HashSet<>(deploymentGroups); + this.deploymentGroups = new TreeSet<>(deploymentGroups); } else { - this.deploymentGroups = new HashSet<>(); + this.deploymentGroups = new TreeSet<>(); } } @@ -123,7 +123,7 @@ public void setName(String name) { */ @Nonnull public Set getHealthChecks() { - return new HashSet<>(healthChecks); + return new TreeSet<>(healthChecks); } void setHealthChecks(Set healthChecks) { diff --git a/structurizr-core/src/main/java/com/structurizr/view/Animation.java b/structurizr-core/src/main/java/com/structurizr/view/Animation.java index c15f89dd0..0e08bb070 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/Animation.java +++ b/structurizr-core/src/main/java/com/structurizr/view/Animation.java @@ -3,8 +3,8 @@ import com.structurizr.model.Element; import com.structurizr.model.Relationship; -import java.util.HashSet; import java.util.Set; +import java.util.TreeSet; /** * A wrapper for a collection of animation steps. @@ -12,8 +12,8 @@ public final class Animation { private int order; - private Set elements = new HashSet<>(); - private Set relationships = new HashSet<>(); + private Set elements = new TreeSet<>(); + private Set relationships = new TreeSet<>(); Animation() { } @@ -39,22 +39,22 @@ void setOrder(int order) { } public Set getElements() { - return new HashSet<>(elements); + return new TreeSet<>(elements); } void setElements(Set elements) { if (elements != null) { - this.elements = new HashSet<>(elements); + this.elements = new TreeSet<>(elements); } } public Set getRelationships() { - return new HashSet<>(relationships); + return new TreeSet<>(relationships); } void setRelationships(Set relationships) { if (relationships != null) { - this.relationships = new HashSet<>(relationships); + this.relationships = new TreeSet<>(relationships); } } diff --git a/structurizr-core/src/main/java/com/structurizr/view/Configuration.java b/structurizr-core/src/main/java/com/structurizr/view/Configuration.java index 9d1a0c3fe..87780a332 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/Configuration.java +++ b/structurizr-core/src/main/java/com/structurizr/view/Configuration.java @@ -1,7 +1,6 @@ package com.structurizr.view; import com.fasterxml.jackson.annotation.JsonGetter; -import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonSetter; import com.structurizr.PropertyHolder; import com.structurizr.util.Url; diff --git a/structurizr-core/src/main/java/com/structurizr/view/ElementView.java b/structurizr-core/src/main/java/com/structurizr/view/ElementView.java index b3a4487a7..09518ebc6 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/ElementView.java +++ b/structurizr-core/src/main/java/com/structurizr/view/ElementView.java @@ -6,7 +6,7 @@ /** * Represents an instance of an Element in a View. */ -public final class ElementView { +public final class ElementView implements Comparable { private Element element; private String id; @@ -100,4 +100,16 @@ void copyLayoutInformationFrom(ElementView source) { } } + @Override + public int compareTo(ElementView elementView) { + try { + int id1 = Integer.parseInt(getId()); + int id2 = Integer.parseInt(elementView.getId()); + + return id1 - id2; + } catch (NumberFormatException nfe) { + return getId().compareTo(elementView.getId()); + } + } + } diff --git a/structurizr-core/src/main/java/com/structurizr/view/FilteredView.java b/structurizr-core/src/main/java/com/structurizr/view/FilteredView.java index 64b70a507..084090afe 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/FilteredView.java +++ b/structurizr-core/src/main/java/com/structurizr/view/FilteredView.java @@ -3,8 +3,8 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import java.util.Arrays; -import java.util.HashSet; import java.util.Set; +import java.util.TreeSet; /** * Represents a view on top of a view, which can be used to include or exclude specific elements. @@ -15,7 +15,7 @@ public final class FilteredView extends View { private String baseViewKey; private FilterMode mode = FilterMode.Exclude; - private Set tags = new HashSet<>(); + private final Set tags = new TreeSet<>(); FilteredView() { } @@ -58,7 +58,7 @@ void setMode(FilterMode mode) { } public Set getTags() { - return new HashSet<>(tags); + return new TreeSet<>(tags); } @Override diff --git a/structurizr-core/src/main/java/com/structurizr/view/ModelView.java b/structurizr-core/src/main/java/com/structurizr/view/ModelView.java index c80ddb72a..98f5a446a 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/ModelView.java +++ b/structurizr-core/src/main/java/com/structurizr/view/ModelView.java @@ -7,9 +7,9 @@ import javax.annotation.Nonnull; import java.util.HashSet; -import java.util.LinkedHashSet; import java.util.Optional; import java.util.Set; +import java.util.TreeSet; import java.util.stream.Collectors; /** @@ -34,8 +34,8 @@ public abstract class ModelView extends View { private AutomaticLayout automaticLayout = null; private boolean mergeFromRemote = true; - private Set elementViews = new LinkedHashSet<>(); - private Set relationshipViews = new LinkedHashSet<>(); + private Set elementViews = new TreeSet<>(); + private Set relationshipViews = new TreeSet<>(); private LayoutMergeStrategy layoutMergeStrategy = new DefaultLayoutMergeStrategy(); @@ -308,12 +308,12 @@ public void removeRelationshipsNotConnectedToElement(Element element) { * @return a Set of ElementView objects */ public Set getElements() { - return new HashSet<>(elementViews); + return new TreeSet<>(elementViews); } void setElements(Set elementViews) { if (elementViews != null) { - this.elementViews = new HashSet<>(elementViews); + this.elementViews = new TreeSet<>(elementViews); } } @@ -323,12 +323,12 @@ void setElements(Set elementViews) { * @return a Set of RelationshipView objects */ public Set getRelationships() { - return new HashSet<>(this.relationshipViews); + return new TreeSet<>(this.relationshipViews); } void setRelationships(Set relationshipViews) { if (relationshipViews != null) { - this.relationshipViews = new HashSet<>(relationshipViews); + this.relationshipViews = new TreeSet<>(relationshipViews); } } diff --git a/structurizr-core/src/main/java/com/structurizr/view/RelationshipView.java b/structurizr-core/src/main/java/com/structurizr/view/RelationshipView.java index e9d01b480..38b0311fd 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/RelationshipView.java +++ b/structurizr-core/src/main/java/com/structurizr/view/RelationshipView.java @@ -7,14 +7,14 @@ import com.structurizr.util.Url; import java.util.Collection; -import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.Set; +import java.util.TreeSet; /** * This class represents an instance of a Relationship on a View. */ -public final class RelationshipView { +public final class RelationshipView implements Comparable { private static final int START_OF_LINE = 0; private static final int END_OF_LINE = 100; @@ -25,7 +25,7 @@ public final class RelationshipView { private String url; private String order; private Boolean response; - private Set vertices = new LinkedHashSet<>(); + private Set vertices = new TreeSet<>(); @JsonInclude(value = JsonInclude.Include.NON_NULL) private Routing routing; @@ -168,7 +168,7 @@ public Collection getVertices() { */ public void setVertices(Collection vertices) { if (vertices != null) { - this.vertices = new LinkedHashSet<>(vertices); + this.vertices = new TreeSet<>(vertices); } } @@ -254,4 +254,12 @@ public String toString() { return ""; } + @Override + public int compareTo(RelationshipView relationshipView) { + String identifier1 = getId() + "/" + getOrder(); + String identifier2 = relationshipView.getId() + "/" + relationshipView.getOrder(); + + return identifier1.compareTo(identifier2); + } + } \ No newline at end of file diff --git a/structurizr-core/src/main/java/com/structurizr/view/Vertex.java b/structurizr-core/src/main/java/com/structurizr/view/Vertex.java index eda796e23..84e2a30c8 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/Vertex.java +++ b/structurizr-core/src/main/java/com/structurizr/view/Vertex.java @@ -5,7 +5,7 @@ /** * The X, Y coordinate of a bend in a line. */ -public final class Vertex { +public final class Vertex implements Comparable { private int x; private int y; @@ -63,4 +63,14 @@ public int hashCode() { return Objects.hash(x, y); } + @Override + public int compareTo(Vertex vertex) { + int result = getX() - vertex.getX(); + if (result == 0) { + result = getY() - vertex.getY(); + } + + return result; + } + } \ No newline at end of file diff --git a/structurizr-core/src/main/java/com/structurizr/view/View.java b/structurizr-core/src/main/java/com/structurizr/view/View.java index 2ddfcc6b0..510437490 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/View.java +++ b/structurizr-core/src/main/java/com/structurizr/view/View.java @@ -11,7 +11,7 @@ /** * The superclass for all views. */ -public abstract class View implements PropertyHolder { +public abstract class View implements PropertyHolder, Comparable { private String key; private boolean generatedKey = false; @@ -160,4 +160,9 @@ void setProperties(Map properties) { } } + @Override + public int compareTo(View view) { + return getOrder() - view.getOrder(); + } + } \ No newline at end of file diff --git a/structurizr-core/src/main/java/com/structurizr/view/ViewSet.java b/structurizr-core/src/main/java/com/structurizr/view/ViewSet.java index 68b4e749f..cab1d8a44 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/ViewSet.java +++ b/structurizr-core/src/main/java/com/structurizr/view/ViewSet.java @@ -33,16 +33,16 @@ public final class ViewSet { private Model model; - private Collection customViews = new HashSet<>(); - private Collection systemLandscapeViews = new HashSet<>(); - private Collection systemContextViews = new HashSet<>(); - private Collection containerViews = new HashSet<>(); - private Collection componentViews = new HashSet<>(); - private Collection dynamicViews = new HashSet<>(); - private Collection deploymentViews = new HashSet<>(); - private Collection imageViews = new HashSet<>(); + private Collection customViews = new TreeSet<>(); + private Collection systemLandscapeViews = new TreeSet<>(); + private Collection systemContextViews = new TreeSet<>(); + private Collection containerViews = new TreeSet<>(); + private Collection componentViews = new TreeSet<>(); + private Collection dynamicViews = new TreeSet<>(); + private Collection deploymentViews = new TreeSet<>(); + private Collection imageViews = new TreeSet<>(); - private Collection filteredViews = new HashSet<>(); + private Collection filteredViews = new TreeSet<>(); private Configuration configuration = new Configuration(); @@ -501,12 +501,12 @@ ImageView getImageViewWithKey(String key) { * @return a Collection of CustomView objects */ public Collection getCustomViews() { - return new HashSet<>(customViews); + return new TreeSet<>(customViews); } void setCustomViews(Set customViews) { if (customViews != null) { - this.customViews = new HashSet<>(customViews); + this.customViews = new TreeSet<>(customViews); } } @@ -516,12 +516,12 @@ void setCustomViews(Set customViews) { * @return a Collection of SystemLandscapeView objects */ public Collection getSystemLandscapeViews() { - return new HashSet<>(systemLandscapeViews); + return new TreeSet<>(systemLandscapeViews); } void setSystemLandscapeViews(Set systemLandscapeViews) { if (systemLandscapeViews != null) { - this.systemLandscapeViews = new HashSet<>(systemLandscapeViews); + this.systemLandscapeViews = new TreeSet<>(systemLandscapeViews); } } @@ -531,7 +531,7 @@ void setSystemLandscapeViews(Set systemLandscapeViews) { @JsonSetter("enterpriseContextViews") void setEnterpriseContextViews(Collection enterpriseContextViews) { if (enterpriseContextViews != null) { - this.systemLandscapeViews = new HashSet<>(enterpriseContextViews); + this.systemLandscapeViews = new TreeSet<>(enterpriseContextViews); } } @@ -541,12 +541,12 @@ void setEnterpriseContextViews(Collection enterpriseContext * @return a Collection of SystemContextView objects */ public Collection getSystemContextViews() { - return new HashSet<>(systemContextViews); + return new TreeSet<>(systemContextViews); } void setSystemContextViews(Set systemContextViews) { if (systemContextViews != null) { - this.systemContextViews = new HashSet<>(systemContextViews); + this.systemContextViews = new TreeSet<>(systemContextViews); } } @@ -556,12 +556,12 @@ void setSystemContextViews(Set systemContextViews) { * @return a Collection of ContainerView objects */ public Collection getContainerViews() { - return new HashSet<>(containerViews); + return new TreeSet<>(containerViews); } void setContainerViews(Set containerViews) { if (containerViews != null) { - this.containerViews = new HashSet<>(containerViews); + this.containerViews = new TreeSet<>(containerViews); } } @@ -571,12 +571,12 @@ void setContainerViews(Set containerViews) { * @return a Collection of ComponentView objects */ public Collection getComponentViews() { - return new HashSet<>(componentViews); + return new TreeSet<>(componentViews); } void setComponentViews(Set componentViews) { if (componentViews != null) { - this.componentViews = new HashSet<>(componentViews); + this.componentViews = new TreeSet<>(componentViews); } } @@ -586,22 +586,22 @@ void setComponentViews(Set componentViews) { * @return a Collection of DynamicView objects */ public Collection getDynamicViews() { - return new HashSet<>(dynamicViews); + return new TreeSet<>(dynamicViews); } void setDynamicViews(Set dynamicViews) { if (dynamicViews != null) { - this.dynamicViews = new HashSet<>(dynamicViews); + this.dynamicViews = new TreeSet<>(dynamicViews); } } public Collection getFilteredViews() { - return new HashSet<>(filteredViews); + return new TreeSet<>(filteredViews); } void setFilteredViews(Set filteredViews) { if (filteredViews != null) { - this.filteredViews = new HashSet<>(filteredViews); + this.filteredViews = new TreeSet<>(filteredViews); } } @@ -611,12 +611,12 @@ void setFilteredViews(Set filteredViews) { * @return a Collection of DeploymentView objects */ public Collection getDeploymentViews() { - return new HashSet<>(deploymentViews); + return new TreeSet<>(deploymentViews); } void setDeploymentViews(Set deploymentViews) { if (deploymentViews != null) { - this.deploymentViews = new HashSet<>(deploymentViews); + this.deploymentViews = new TreeSet<>(deploymentViews); } } @@ -626,12 +626,12 @@ void setDeploymentViews(Set deploymentViews) { * @return a Collection of ImageView objects */ public Collection getImageViews() { - return new HashSet<>(imageViews); + return new TreeSet<>(imageViews); } void setImageView(Set imageViews) { if (imageViews != null) { - this.imageViews = new HashSet<>(imageViews); + this.imageViews = new TreeSet<>(imageViews); } } @@ -642,7 +642,7 @@ void setImageView(Set imageViews) { */ @JsonIgnore public Collection getViews() { - HashSet views = new HashSet<>(); + Set views = new TreeSet<>(); views.addAll(getCustomViews()); views.addAll(getSystemLandscapeViews()); @@ -1045,7 +1045,7 @@ public void createDefaultViews() { } private Set getSoftwareSystemInstances(DeploymentNode deploymentNode) { - Set softwareSystemInstances = new HashSet<>(deploymentNode.getSoftwareSystemInstances()); + Set softwareSystemInstances = new TreeSet<>(deploymentNode.getSoftwareSystemInstances()); for (DeploymentNode child : deploymentNode.getChildren()) { softwareSystemInstances.addAll(getSoftwareSystemInstances(child)); @@ -1055,7 +1055,7 @@ private Set getSoftwareSystemInstances(DeploymentNode de } private Set getContainerInstances(DeploymentNode deploymentNode) { - Set containerInstances = new HashSet<>(deploymentNode.getContainerInstances()); + Set containerInstances = new TreeSet<>(deploymentNode.getContainerInstances()); for (DeploymentNode child : deploymentNode.getChildren()) { containerInstances.addAll(getContainerInstances(child)); @@ -1068,14 +1068,14 @@ private Set getContainerInstances(DeploymentNode deploymentNo * Removes all views and configuration. */ public void clear() { - customViews = new HashSet<>(); - systemLandscapeViews = new HashSet<>(); - systemContextViews = new HashSet<>(); - containerViews = new HashSet<>(); - componentViews = new HashSet<>(); - dynamicViews = new HashSet<>(); - deploymentViews = new HashSet<>(); - filteredViews = new HashSet<>(); + customViews = new TreeSet<>(); + systemLandscapeViews = new TreeSet<>(); + systemContextViews = new TreeSet<>(); + containerViews = new TreeSet<>(); + componentViews = new TreeSet<>(); + dynamicViews = new TreeSet<>(); + deploymentViews = new TreeSet<>(); + filteredViews = new TreeSet<>(); configuration = new Configuration(); } diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/36141-Components.dot b/structurizr-export/src/test/java/com/structurizr/export/dot/36141-Components.dot index b2869d886..679e725e2 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/dot/36141-Components.dot +++ b/structurizr-export/src/test/java/com/structurizr/export/dot/36141-Components.dot @@ -7,9 +7,9 @@ digraph { 4 [id=4,shape=rect, label=<Mainframe Banking
    System

    [Software System]

    Stores all of the core banking
    information about customers,
    accounts, transactions, etc.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] 5 [id=5,shape=rect, label=<E-mail System
    [Software System]

    The internal Microsoft
    Exchange e-mail system.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] - 18 [id=18,shape=cylinder, label=<Database
    [Container: Oracle Database Schema]

    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] 8 [id=8,shape=rect, label=<Single-Page
    Application

    [Container: JavaScript and Angular]

    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] 9 [id=9,shape=rect, label=<Mobile App
    [Container: Xamarin]

    Provides a limited subset of
    the Internet banking
    functionality to customers via
    their mobile device.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + 18 [id=18,shape=cylinder, label=<Database
    [Container: Oracle Database Schema]

    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] subgraph cluster_11 { margin=25 diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/36141-SignIn.dot b/structurizr-export/src/test/java/com/structurizr/export/dot/36141-SignIn.dot index 25109d81f..3e840c871 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/dot/36141-SignIn.dot +++ b/structurizr-export/src/test/java/com/structurizr/export/dot/36141-SignIn.dot @@ -17,8 +17,8 @@ digraph { 15 [id=15,shape=rect, label=<Security Component
    [Component: Spring Bean]

    Provides functionality related
    to signing in, changing
    passwords, etc.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] } - 18 [id=18,shape=cylinder, label=<Database
    [Container: Oracle Database Schema]

    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] 8 [id=8,shape=rect, label=<Single-Page
    Application

    [Container: JavaScript and Angular]

    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + 18 [id=18,shape=cylinder, label=<Database
    [Container: Oracle Database Schema]

    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] 8 -> 12 [id=32, label=<1. Submits credentials to
    [JSON/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] 12 -> 15 [id=40, label=<2. Validates credentials
    using
    >, style="dashed", color="#707070", fontcolor="#707070"] diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-Components.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-Components.mmd index 8a96174a1..7f759f871 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-Components.mmd +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-Components.mmd @@ -8,12 +8,12 @@ graph TB style 4 fill:#999999,stroke:#6b6b6b,color:#ffffff 5["

    E-mail System
    [Software System]
    The internal Microsoft
    Exchange e-mail system.
    "] style 5 fill:#999999,stroke:#6b6b6b,color:#ffffff - 18[("
    Database
    [Container: Oracle Database Schema]
    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    ")] - style 18 fill:#438dd5,stroke:#2e6295,color:#ffffff 8["
    Single-Page Application
    [Container: JavaScript and Angular]
    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    "] style 8 fill:#438dd5,stroke:#2e6295,color:#ffffff 9["
    Mobile App
    [Container: Xamarin]
    Provides a limited subset of
    the Internet banking
    functionality to customers
    via their mobile device.
    "] style 9 fill:#438dd5,stroke:#2e6295,color:#ffffff + 18[("
    Database
    [Container: Oracle Database Schema]
    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    ")] + style 18 fill:#438dd5,stroke:#2e6295,color:#ffffff subgraph 11 [API Application] style 11 fill:#ffffff,stroke:#2e6295,color:#2e6295 diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SignIn.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SignIn.mmd index ffccd9df7..1715a20f8 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SignIn.mmd +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SignIn.mmd @@ -13,10 +13,10 @@ graph TB style 15 fill:#85bbf0,stroke:#5d82a8,color:#000000 end - 18[("
    Database
    [Container: Oracle Database Schema]
    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    ")] - style 18 fill:#438dd5,stroke:#2e6295,color:#ffffff 8["
    Single-Page Application
    [Container: JavaScript and Angular]
    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    "] style 8 fill:#438dd5,stroke:#2e6295,color:#ffffff + 18[("
    Database
    [Container: Oracle Database Schema]
    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    ")] + style 18 fill:#438dd5,stroke:#2e6295,color:#ffffff 8-. "
    1. Submits credentials to
    [JSON/HTTPS]
    " .->12 12-. "
    2. Validates credentials
    using
    " .->15 diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Components.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Components.puml index 3ae85ef6e..a0d351686 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Components.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Components.puml @@ -21,9 +21,9 @@ AddBoundaryTag("Container", $bgColor="#ffffff", $borderColor="#2e6295", $fontCol System(MainframeBankingSystem, "Mainframe Banking System", $descr="Stores all of the core banking information about customers, accounts, transactions, etc.", $tags="Software System,Existing System", $link="") System(EmailSystem, "E-mail System", $descr="The internal Microsoft Exchange e-mail system.", $tags="Software System,Existing System", $link="") -ContainerDb(InternetBankingSystem.Database, "Database", $techn="Oracle Database Schema", $descr="Stores user registration information, hashed authentication credentials, access logs, etc.", $tags="Container,Database", $link="") Container(InternetBankingSystem.SinglePageApplication, "Single-Page Application", $techn="JavaScript and Angular", $descr="Provides all of the Internet banking functionality to customers via their web browser.", $tags="Container,Web Browser", $link="") Container(InternetBankingSystem.MobileApp, "Mobile App", $techn="Xamarin", $descr="Provides a limited subset of the Internet banking functionality to customers via their mobile device.", $tags="Container,Mobile App", $link="") +ContainerDb(InternetBankingSystem.Database, "Database", $techn="Oracle Database Schema", $descr="Stores user registration information, hashed authentication credentials, access logs, etc.", $tags="Container,Database", $link="") Container_Boundary("InternetBankingSystem.APIApplication_boundary", "API Application", $tags="Container") { Component(InternetBankingSystem.APIApplication.SignInController, "Sign In Controller", $techn="Spring MVC Rest Controller", $descr="Allows users to sign in to the Internet Banking System.", $tags="Component", $link="") diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Containers.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Containers.puml index 70127ca7a..44fc2b9c6 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Containers.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Containers.puml @@ -11,8 +11,8 @@ top to bottom direction AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") AddElementTag("Container,Mobile App", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Container", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Container", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") AddElementTag("Person,Customer", $bgColor="#08427b", $borderColor="#052e56", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-DevelopmentDeployment.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-DevelopmentDeployment.puml index f3e11defa..68f04bd91 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-DevelopmentDeployment.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-DevelopmentDeployment.puml @@ -12,8 +12,8 @@ top to bottom direction AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") AddElementTag("Element", $bgColor="#ffffff", $borderColor="#888888", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Container", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Container", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-LiveDeployment.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-LiveDeployment.puml index d3b2df46c..decda55a1 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-LiveDeployment.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-LiveDeployment.puml @@ -14,8 +14,8 @@ AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColo AddElementTag("Element", $bgColor="#ffffff", $borderColor="#888888", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") AddElementTag("Container,Mobile App", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Container", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Container", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn.puml index 1789c9fa5..b86a5b2eb 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn.puml @@ -22,8 +22,8 @@ Container_Boundary("InternetBankingSystem.APIApplication_boundary", "API Applica Component(InternetBankingSystem.APIApplication.SecurityComponent, "Security Component", $techn="Spring Bean", $descr="Provides functionality related to signing in, changing passwords, etc.", $tags="Component", $link="") } -ContainerDb(InternetBankingSystem.Database, "Database", $techn="Oracle Database Schema", $descr="Stores user registration information, hashed authentication credentials, access logs, etc.", $tags="Container,Database", $link="") Container(InternetBankingSystem.SinglePageApplication, "Single-Page Application", $techn="JavaScript and Angular", $descr="Provides all of the Internet banking functionality to customers via their web browser.", $tags="Container,Web Browser", $link="") +ContainerDb(InternetBankingSystem.Database, "Database", $techn="Oracle Database Schema", $descr="Stores user registration information, hashed authentication credentials, access logs, etc.", $tags="Container,Database", $link="") Rel(InternetBankingSystem.SinglePageApplication, InternetBankingSystem.APIApplication.SignInController, "1. Submits credentials to", $techn="JSON/HTTPS", $tags="Relationship", $link="") Rel(InternetBankingSystem.APIApplication.SignInController, InternetBankingSystem.APIApplication.SecurityComponent, "2. Validates credentials using", $techn="", $tags="Relationship", $link="") diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-Components.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-Components.puml index 149271a67..abd0c8043 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-Components.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-Components.puml @@ -87,9 +87,9 @@ skinparam rectangle<> { rectangle "==Mainframe Banking System\n[Software System]\n\nStores all of the core banking information about customers, accounts, transactions, etc." <> as MainframeBankingSystem rectangle "==E-mail System\n[Software System]\n\nThe internal Microsoft Exchange e-mail system." <> as EmailSystem -database "==Database\n[Container: Oracle Database Schema]\n\nStores user registration information, hashed authentication credentials, access logs, etc." <> as InternetBankingSystem.Database rectangle "==Single-Page Application\n[Container: JavaScript and Angular]\n\nProvides all of the Internet banking functionality to customers via their web browser." <> as InternetBankingSystem.SinglePageApplication rectangle "==Mobile App\n[Container: Xamarin]\n\nProvides a limited subset of the Internet banking functionality to customers via their mobile device." <> as InternetBankingSystem.MobileApp +database "==Database\n[Container: Oracle Database Schema]\n\nStores user registration information, hashed authentication credentials, access logs, etc." <> as InternetBankingSystem.Database rectangle "API Application\n[Container: Java and Spring MVC]" <> { rectangle "==Sign In Controller\n[Component: Spring MVC Rest Controller]\n\nAllows users to sign in to the Internet Banking System." <> as InternetBankingSystem.APIApplication.SignInController diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-LiveDeployment.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-LiveDeployment.puml index b7611a0b1..413f4b3a3 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-LiveDeployment.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-LiveDeployment.puml @@ -19,13 +19,13 @@ skinparam rectangle<> BorderColor #2e6295 shadowing false } -skinparam rectangle<> { +skinparam rectangle<> { BackgroundColor #ffffff FontColor #000000 BorderColor #888888 shadowing false } -skinparam rectangle<> { +skinparam rectangle<> { BackgroundColor #ffffff FontColor #000000 BorderColor #888888 @@ -49,13 +49,13 @@ skinparam rectangle<> { BorderColor #888888 shadowing false } -skinparam database<> { +skinparam database<> { BackgroundColor #438dd5 FontColor #ffffff BorderColor #2e6295 shadowing false } -skinparam database<> { +skinparam database<> { BackgroundColor #438dd5 FontColor #ffffff BorderColor #2e6295 diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SignIn.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SignIn.puml index aad19e957..fc0b86f0e 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SignIn.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SignIn.puml @@ -48,8 +48,8 @@ rectangle "API Application\n[Container: Java and Spring MVC]" << rectangle "==Security Component\n[Component: Spring Bean]\n\nProvides functionality related to signing in, changing passwords, etc." <> as InternetBankingSystem.APIApplication.SecurityComponent } -database "==Database\n[Container: Oracle Database Schema]\n\nStores user registration information, hashed authentication credentials, access logs, etc." <> as InternetBankingSystem.Database rectangle "==Single-Page Application\n[Container: JavaScript and Angular]\n\nProvides all of the Internet banking functionality to customers via their web browser." <> as InternetBankingSystem.SinglePageApplication +database "==Database\n[Container: Oracle Database Schema]\n\nStores user registration information, hashed authentication credentials, access logs, etc." <> as InternetBankingSystem.Database InternetBankingSystem.SinglePageApplication .[#707070,thickness=2].> InternetBankingSystem.APIApplication.SignInController : "1. Submits credentials to\n[JSON/HTTPS]" InternetBankingSystem.APIApplication.SignInController .[#707070,thickness=2].> InternetBankingSystem.APIApplication.SecurityComponent : "2. Validates credentials using" From 586a32b15f972612a08a7fbf5ce84464fc29162b Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 1 Mar 2024 17:16:09 +0000 Subject: [PATCH 498/717] Fixes #258. --- changelog.md | 1 + .../plantuml/StructurizrPlantUMLExporter.java | 2 +- ...ructurizrPlantUMLDiagramExporterTests.java | 345 ++++++++++-------- .../structurizr/36141-SystemContext.puml | 2 +- .../structurizr/36141-SystemLandscape.puml | 2 +- ...mic-view-container-scoped-with-groups.puml | 4 +- ...ew-software-system-scoped-with-groups.puml | 4 +- .../dynamic-view-unscoped-with-groups.puml | 4 +- .../plantuml/structurizr/group-styles-1.puml | 6 +- .../plantuml/structurizr/group-styles-2.puml | 6 +- .../structurizr/groups-Components.puml | 2 +- .../structurizr/groups-Containers.puml | 2 +- .../structurizr/groups-SystemLandscape.puml | 6 +- .../plantuml/structurizr/nested-groups.puml | 10 +- 14 files changed, 224 insertions(+), 172 deletions(-) diff --git a/changelog.md b/changelog.md index f28646c24..812dc8f25 100644 --- a/changelog.md +++ b/changelog.md @@ -6,6 +6,7 @@ - structurizr-client: Fixes https://github.com/structurizr/java/issues/257 (Serialization to JSON is not deterministic). - structurizr-dsl: Fixes https://github.com/structurizr/java/issues/252 (DSL parser does not seem to handle curly brackets balance). - structurizr-dsl: Deprecates `!constant`, adds `!const` and `!var` (see https://github.com/structurizr/java/issues/253). +- structurizr-export: Fixes https://github.com/structurizr/java/issues/258 (Plantuml renderer: Group and system of same name yields puml code resulting in error). ## 2.0.0 (22nd February 2024) diff --git a/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java b/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java index 97d92a818..320213b11 100644 --- a/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java +++ b/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java @@ -194,7 +194,7 @@ protected void startGroupBoundary(ModelView view, String group, IndentingWriter icon = "\\n\\n"; } - writer.writeLine(String.format("rectangle \"%s%s\" <> {", groupName, icon, groupId)); + writer.writeLine(String.format("rectangle \"%s%s\" <> as group%s {", groupName, icon, groupId, groupId)); writer.indent(); writer.writeLine(String.format("skinparam RectangleBorderColor<> %s", groupId, color)); writer.writeLine(String.format("skinparam RectangleFontColor<> %s", groupId, color)); diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java index 4effe64c2..19869f588 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java @@ -611,38 +611,39 @@ public void staticDiagramsAreUnchangedWhenSequenceDiagramsAreEnabled() { StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(); Diagram diagram; - String expected = "@startuml\n" + - "set separator none\n" + - "title System Landscape\n" + - "\n" + - "top to bottom direction\n" + - "\n" + - "skinparam {\n" + - " arrowFontSize 10\n" + - " defaultTextAlignment center\n" + - " wrapWidth 200\n" + - " maxMessageSize 100\n" + - "}\n" + - "\n" + - "hide stereotype\n" + - "\n" + - "skinparam rectangle<> {\n" + - " BackgroundColor #dddddd\n" + - " FontColor #000000\n" + - " BorderColor #9a9a9a\n" + - " shadowing false\n" + - "}\n" + - "\n" + - "rectangle \"Group\" <> {\n" + - " skinparam RectangleBorderColor<> #cccccc\n" + - " skinparam RectangleFontColor<> #cccccc\n" + - " skinparam RectangleBorderStyle<> dashed\n" + - "\n" + - " rectangle \"==Software System\\n[Software System]\" <> as SoftwareSystem\n" + - "}\n" + - "\n" + - "\n" + - "@enduml"; + String expected = """ + @startuml + set separator none + title System Landscape + + top to bottom direction + + skinparam { + arrowFontSize 10 + defaultTextAlignment center + wrapWidth 200 + maxMessageSize 100 + } + + hide stereotype + + skinparam rectangle<> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false + } + + rectangle "Group" <> as group1 { + skinparam RectangleBorderColor<> #cccccc + skinparam RectangleFontColor<> #cccccc + skinparam RectangleBorderStyle<> dashed + + rectangle "==Software System\\n[Software System]" <> as SoftwareSystem + } + + + @enduml"""; diagram = exporter.export(view); assertEquals(expected, diagram.getDefinition()); @@ -789,59 +790,60 @@ public void test_writeContainerViewWithGroupedElements_WithAndWithoutAGroupSepar ContainerView view = workspace.getViews().createContainerView(softwareSystem, "Containers", ""); view.addAllElements(); - String expectedResult = "@startuml\n" + - "set separator none\n" + - "title Software System - Containers\n" + - "\n" + - "top to bottom direction\n" + - "\n" + - "skinparam {\n" + - " arrowFontSize 10\n" + - " defaultTextAlignment center\n" + - " wrapWidth 200\n" + - " maxMessageSize 100\n" + - "}\n" + - "\n" + - "hide stereotype\n" + - "\n" + - "skinparam rectangle<> {\n" + - " BackgroundColor #dddddd\n" + - " FontColor #000000\n" + - " BorderColor #9a9a9a\n" + - " shadowing false\n" + - "}\n" + - "skinparam rectangle<> {\n" + - " BackgroundColor #dddddd\n" + - " FontColor #000000\n" + - " BorderColor #9a9a9a\n" + - " shadowing false\n" + - "}\n" + - "skinparam rectangle<> {\n" + - " BorderColor #9a9a9a\n" + - " FontColor #9a9a9a\n" + - " shadowing false\n" + - "}\n" + - "\n" + - "rectangle \"Software System\\n[Software System]\" <> {\n" + - " rectangle \"Group 1\" <> {\n" + - " skinparam RectangleBorderColor<> #cccccc\n" + - " skinparam RectangleFontColor<> #cccccc\n" + - " skinparam RectangleBorderStyle<> dashed\n" + - "\n" + - " rectangle \"==Container 1\\n[Container]\" <> as SoftwareSystem.Container1\n" + - " }\n" + - "\n" + - " rectangle \"Group 2\" <> {\n" + - " skinparam RectangleBorderColor<> #cccccc\n" + - " skinparam RectangleFontColor<> #cccccc\n" + - " skinparam RectangleBorderStyle<> dashed\n" + - "\n" + - " rectangle \"==Container 2\\n[Container]\" <> as SoftwareSystem.Container2\n" + - " }\n" + - "\n" + - "}\n" + - "\n" + - "@enduml"; + String expectedResult = """ + @startuml + set separator none + title Software System - Containers + + top to bottom direction + + skinparam { + arrowFontSize 10 + defaultTextAlignment center + wrapWidth 200 + maxMessageSize 100 + } + + hide stereotype + + skinparam rectangle<> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false + } + skinparam rectangle<> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false + } + skinparam rectangle<> { + BorderColor #9a9a9a + FontColor #9a9a9a + shadowing false + } + + rectangle "Software System\\n[Software System]" <> { + rectangle "Group 1" <> as group1 { + skinparam RectangleBorderColor<> #cccccc + skinparam RectangleFontColor<> #cccccc + skinparam RectangleBorderStyle<> dashed + + rectangle "==Container 1\\n[Container]" <> as SoftwareSystem.Container1 + } + + rectangle "Group 2" <> as group2 { + skinparam RectangleBorderColor<> #cccccc + skinparam RectangleFontColor<> #cccccc + skinparam RectangleBorderStyle<> dashed + + rectangle "==Container 2\\n[Container]" <> as SoftwareSystem.Container2 + } + + } + + @enduml"""; StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(); Diagram diagram = exporter.export(view); @@ -874,67 +876,68 @@ public void deploymentView_WithGroups() { view.add(infrastructureNode2); view.add(softwareSystemInstance); - String expectedResult = "@startuml\n" + - "set separator none\n" + - "title Deployment - Default\n" + - "\n" + - "top to bottom direction\n" + - "\n" + - "skinparam {\n" + - " arrowFontSize 10\n" + - " defaultTextAlignment center\n" + - " wrapWidth 200\n" + - " maxMessageSize 100\n" + - "}\n" + - "\n" + - "hide stereotype\n" + - "\n" + - "skinparam rectangle<> {\n" + - " BackgroundColor #dddddd\n" + - " FontColor #000000\n" + - " BorderColor #9a9a9a\n" + - " shadowing false\n" + - "}\n" + - "skinparam rectangle<> {\n" + - " BackgroundColor #dddddd\n" + - " FontColor #000000\n" + - " BorderColor #9a9a9a\n" + - " shadowing false\n" + - "}\n" + - "skinparam rectangle<> {\n" + - " BackgroundColor #ffffff\n" + - " FontColor #000000\n" + - " BorderColor #888888\n" + - " shadowing false\n" + - "}\n" + - "skinparam rectangle<> {\n" + - " BackgroundColor #dddddd\n" + - " FontColor #000000\n" + - " BorderColor #9a9a9a\n" + - " shadowing false\n" + - "}\n" + - "\n" + - "rectangle \"Group 1\" <> {\n" + - " skinparam RectangleBorderColor<> #cccccc\n" + - " skinparam RectangleFontColor<> #cccccc\n" + - " skinparam RectangleBorderStyle<> dashed\n" + - "\n" + - " rectangle \"Server 1\\n[Deployment Node]\" <> as Default.Server1 {\n" + - " rectangle \"Group 2\" <> {\n" + - " skinparam RectangleBorderColor<> #cccccc\n" + - " skinparam RectangleFontColor<> #cccccc\n" + - " skinparam RectangleBorderStyle<> dashed\n" + - "\n" + - " rectangle \"==Infrastructure Node 2\\n[Infrastructure Node]\" <> as Default.Server1.InfrastructureNode2\n" + - " rectangle \"==Software System\\n[Software System]\" <> as Default.Server1.SoftwareSystem_1\n" + - " }\n" + - "\n" + - " rectangle \"==Infrastructure Node 1\\n[Infrastructure Node]\" <> as Default.Server1.InfrastructureNode1\n" + - " }\n" + - "\n" + - "}\n" + - "\n" + - "@enduml"; + String expectedResult = """ + @startuml + set separator none + title Deployment - Default + + top to bottom direction + + skinparam { + arrowFontSize 10 + defaultTextAlignment center + wrapWidth 200 + maxMessageSize 100 + } + + hide stereotype + + skinparam rectangle<> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false + } + skinparam rectangle<> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false + } + skinparam rectangle<> { + BackgroundColor #ffffff + FontColor #000000 + BorderColor #888888 + shadowing false + } + skinparam rectangle<> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false + } + + rectangle "Group 1" <> as group1 { + skinparam RectangleBorderColor<> #cccccc + skinparam RectangleFontColor<> #cccccc + skinparam RectangleBorderStyle<> dashed + + rectangle "Server 1\\n[Deployment Node]" <> as Default.Server1 { + rectangle "Group 2" <> as group2 { + skinparam RectangleBorderColor<> #cccccc + skinparam RectangleFontColor<> #cccccc + skinparam RectangleBorderStyle<> dashed + + rectangle "==Infrastructure Node 2\\n[Infrastructure Node]" <> as Default.Server1.InfrastructureNode2 + rectangle "==Software System\\n[Software System]" <> as Default.Server1.SoftwareSystem_1 + } + + rectangle "==Infrastructure Node 1\\n[Infrastructure Node]" <> as Default.Server1.InfrastructureNode1 + } + + } + + @enduml"""; StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(); Diagram diagram = exporter.export(view); @@ -1026,6 +1029,54 @@ public void test_ElementInstanceUrl() { rectangle "==A\\n[Software System]" <> as Default.Node.A_1 [[https://example.com/url2]] } -@enduml""", new StructurizrPlantUMLExporter().export(view).getDefinition()); } +@enduml""", new StructurizrPlantUMLExporter().export(view).getDefinition()); + + } + + @Test + void groupAndSoftwareSystemNameAreTheSame() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name"); + softwareSystem.setGroup("Name"); + + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "Description"); + view.add(softwareSystem); + + StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(); + Diagram diagram = exporter.export(view); + assertEquals(""" + @startuml + set separator none + title System Landscape + + top to bottom direction + + skinparam { + arrowFontSize 10 + defaultTextAlignment center + wrapWidth 200 + maxMessageSize 100 + } + + hide stereotype + + skinparam rectangle<> { + BackgroundColor #dddddd + FontColor #000000 + BorderColor #9a9a9a + shadowing false + } + + rectangle "Name" <> as group1 { + skinparam RectangleBorderColor<> #cccccc + skinparam RectangleFontColor<> #cccccc + skinparam RectangleBorderStyle<> dashed + + rectangle "==Name\\n[Software System]" <> as Name + } + + + @enduml""", diagram.getDefinition()); + } } \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemContext.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemContext.puml index d4f946a23..5b0dc94ec 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemContext.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemContext.puml @@ -38,7 +38,7 @@ skinparam person<> { shadowing false } -rectangle "Big Bank plc" <> { +rectangle "Big Bank plc" <> as group1 { skinparam RectangleBorderColor<> #cccccc skinparam RectangleFontColor<> #cccccc skinparam RectangleBorderStyle<> dashed diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemLandscape.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemLandscape.puml index b042bb6a5..0518cb173 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemLandscape.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemLandscape.puml @@ -56,7 +56,7 @@ skinparam person<> { shadowing false } -rectangle "Big Bank plc" <> { +rectangle "Big Bank plc" <> as group1 { skinparam RectangleBorderColor<> #cccccc skinparam RectangleFontColor<> #cccccc skinparam RectangleBorderStyle<> dashed diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-container-scoped-with-groups.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-container-scoped-with-groups.puml index bd0fade3d..304b863f3 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-container-scoped-with-groups.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-container-scoped-with-groups.puml @@ -37,7 +37,7 @@ skinparam rectangle<> { } rectangle "A\n[Container]" <> { - rectangle "Group 1" <> { + rectangle "Group 1" <> as group1 { skinparam RectangleBorderColor<> #cccccc skinparam RectangleFontColor<> #cccccc skinparam RectangleBorderStyle<> dashed @@ -48,7 +48,7 @@ rectangle "A\n[Container]" <> { } rectangle "B\n[Container]" <> { - rectangle "Group 2" <> { + rectangle "Group 2" <> as group2 { skinparam RectangleBorderColor<> #cccccc skinparam RectangleFontColor<> #cccccc skinparam RectangleBorderStyle<> dashed diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-software-system-scoped-with-groups.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-software-system-scoped-with-groups.puml index 13cc2141b..a430197fb 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-software-system-scoped-with-groups.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-software-system-scoped-with-groups.puml @@ -37,7 +37,7 @@ skinparam rectangle<> { } rectangle "A\n[Software System]" <> { - rectangle "Group 1" <> { + rectangle "Group 1" <> as group1 { skinparam RectangleBorderColor<> #cccccc skinparam RectangleFontColor<> #cccccc skinparam RectangleBorderStyle<> dashed @@ -48,7 +48,7 @@ rectangle "A\n[Software System]" <> { } rectangle "B\n[Software System]" <> { - rectangle "Group 2" <> { + rectangle "Group 2" <> as group2 { skinparam RectangleBorderColor<> #cccccc skinparam RectangleFontColor<> #cccccc skinparam RectangleBorderStyle<> dashed diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-unscoped-with-groups.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-unscoped-with-groups.puml index 5b7656db8..8dc424024 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-unscoped-with-groups.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-unscoped-with-groups.puml @@ -26,7 +26,7 @@ skinparam rectangle<> { shadowing false } -rectangle "Group 1" <> { +rectangle "Group 1" <> as group1 { skinparam RectangleBorderColor<> #cccccc skinparam RectangleFontColor<> #cccccc skinparam RectangleBorderStyle<> dashed @@ -34,7 +34,7 @@ rectangle "Group 1" <> { rectangle "==A\n[Software System]" <> as A } -rectangle "Group 2" <> { +rectangle "Group 2" <> as group2 { skinparam RectangleBorderColor<> #cccccc skinparam RectangleFontColor<> #cccccc skinparam RectangleBorderStyle<> dashed diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/group-styles-1.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/group-styles-1.puml index 8406d0994..997f1738d 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/group-styles-1.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/group-styles-1.puml @@ -32,7 +32,7 @@ skinparam rectangle<> { shadowing false } -rectangle "Group 1\n\n" <> { +rectangle "Group 1\n\n" <> as group1 { skinparam RectangleBorderColor<> #111111 skinparam RectangleFontColor<> #111111 skinparam RectangleBorderStyle<> dashed @@ -40,7 +40,7 @@ rectangle "Group 1\n\n" <> rectangle "==User 1\n[Person]" <> as User1 } -rectangle "Group 2\n\n" <> { +rectangle "Group 2\n\n" <> as group2 { skinparam RectangleBorderColor<> #222222 skinparam RectangleFontColor<> #222222 skinparam RectangleBorderStyle<> dashed @@ -48,7 +48,7 @@ rectangle "Group 2\n\n" <> rectangle "==User 2\n[Person]" <> as User2 } -rectangle "Group 3" <> { +rectangle "Group 3" <> as group3 { skinparam RectangleBorderColor<> #cccccc skinparam RectangleFontColor<> #cccccc skinparam RectangleBorderStyle<> dashed diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/group-styles-2.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/group-styles-2.puml index 5c51fe4d3..82c0a1b75 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/group-styles-2.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/group-styles-2.puml @@ -32,7 +32,7 @@ skinparam rectangle<> { shadowing false } -rectangle "Group 1\n\n" <> { +rectangle "Group 1\n\n" <> as group1 { skinparam RectangleBorderColor<> #111111 skinparam RectangleFontColor<> #111111 skinparam RectangleBorderStyle<> dashed @@ -40,7 +40,7 @@ rectangle "Group 1\n\n" <> rectangle "==User 1\n[Person]" <> as User1 } -rectangle "Group 2\n\n" <> { +rectangle "Group 2\n\n" <> as group2 { skinparam RectangleBorderColor<> #222222 skinparam RectangleFontColor<> #222222 skinparam RectangleBorderStyle<> dashed @@ -48,7 +48,7 @@ rectangle "Group 2\n\n" <> rectangle "==User 2\n[Person]" <> as User2 } -rectangle "Group 3" <> { +rectangle "Group 3" <> as group3 { skinparam RectangleBorderColor<> #aabbcc skinparam RectangleFontColor<> #aabbcc skinparam RectangleBorderStyle<> dashed diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-Components.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-Components.puml index 04af47b24..8528285cb 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-Components.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-Components.puml @@ -40,7 +40,7 @@ skinparam rectangle<> { rectangle "==C\n[Software System]" <> as C rectangle "F\n[Container]" <> { - rectangle "Group 5" <> { + rectangle "Group 5" <> as group1 { skinparam RectangleBorderColor<> #cccccc skinparam RectangleFontColor<> #cccccc skinparam RectangleBorderStyle<> dashed diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-Containers.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-Containers.puml index f7d532695..fe0ad43b9 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-Containers.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-Containers.puml @@ -40,7 +40,7 @@ skinparam rectangle<> { rectangle "==C\n[Software System]" <> as C rectangle "D\n[Software System]" <> { - rectangle "Group 4" <> { + rectangle "Group 4" <> as group1 { skinparam RectangleBorderColor<> #cccccc skinparam RectangleFontColor<> #cccccc skinparam RectangleBorderStyle<> dashed diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-SystemLandscape.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-SystemLandscape.puml index c967286be..7c398d745 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-SystemLandscape.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-SystemLandscape.puml @@ -38,7 +38,7 @@ skinparam rectangle<> { shadowing false } -rectangle "Group 1" <> { +rectangle "Group 1" <> as group1 { skinparam RectangleBorderColor<> #cccccc skinparam RectangleFontColor<> #cccccc skinparam RectangleBorderStyle<> dashed @@ -46,13 +46,13 @@ rectangle "Group 1" <> { rectangle "==B\n[Software System]" <> as B } -rectangle "Group 2" <> { +rectangle "Group 2" <> as group2 { skinparam RectangleBorderColor<> #cccccc skinparam RectangleFontColor<> #cccccc skinparam RectangleBorderStyle<> dashed rectangle "==C\n[Software System]" <> as C - rectangle "Group 3" <> { + rectangle "Group 3" <> as group3 { skinparam RectangleBorderColor<> #cccccc skinparam RectangleFontColor<> #cccccc skinparam RectangleBorderStyle<> dashed diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/nested-groups.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/nested-groups.puml index 3c3d12a53..04e94c3e8 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/nested-groups.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/nested-groups.puml @@ -44,19 +44,19 @@ skinparam rectangle<> { shadowing false } -rectangle "Organisation 1" <> { +rectangle "Organisation 1" <> as group1 { skinparam RectangleBorderColor<> #cccccc skinparam RectangleFontColor<> #cccccc skinparam RectangleBorderStyle<> dashed rectangle "==Organisation 1\n[Software System]" <> as Organisation1 - rectangle "Department 1" <> { + rectangle "Department 1" <> as group2 { skinparam RectangleBorderColor<> #cccccc skinparam RectangleFontColor<> #cccccc skinparam RectangleBorderStyle<> dashed rectangle "==Department 1\n[Software System]" <> as Department1 - rectangle "Team 1" <> { + rectangle "Team 1" <> as group3 { skinparam RectangleBorderColor<> #ff0000 skinparam RectangleFontColor<> #ff0000 skinparam RectangleBorderStyle<> dashed @@ -64,7 +64,7 @@ rectangle "Organisation 1" <> { rectangle "==Team 1\n[Software System]" <> as Team1 } - rectangle "Team 2" <> { + rectangle "Team 2" <> as group4 { skinparam RectangleBorderColor<> #0000ff skinparam RectangleFontColor<> #0000ff skinparam RectangleBorderStyle<> dashed @@ -76,7 +76,7 @@ rectangle "Organisation 1" <> { } -rectangle "Organisation 2" <> { +rectangle "Organisation 2" <> as group5 { skinparam RectangleBorderColor<> #cccccc skinparam RectangleFontColor<> #cccccc skinparam RectangleBorderStyle<> dashed From 06d25b89011b82bf975524a73d3809adccfda06c Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 1 Mar 2024 17:16:27 +0000 Subject: [PATCH 499/717] Updates version number. --- build.gradle | 2 +- changelog.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index e939207e8..c1f5fd9f1 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '2.0.0' + version = '2.1.0' repositories { mavenCentral() diff --git a/changelog.md b/changelog.md index 812dc8f25..a7ed9887a 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,6 @@ # Changelog -## unreleased +## 2.1.0 (unreleased) - structurizr-core: `ViewSet.isEmpty()` was missing a check for image views. - structurizr-client: Fixes https://github.com/structurizr/java/issues/257 (Serialization to JSON is not deterministic). From b80802d0e6643d8981d895ea0b1552af6d982d3c Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 2 Mar 2024 08:50:01 +0000 Subject: [PATCH 500/717] Promotes `ModelView.copyLayoutInformationFrom()` to be public, to allow individual view layout information to be merged. --- changelog.md | 1 + .../src/main/java/com/structurizr/view/ModelView.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index a7ed9887a..9d0f97b0b 100644 --- a/changelog.md +++ b/changelog.md @@ -3,6 +3,7 @@ ## 2.1.0 (unreleased) - structurizr-core: `ViewSet.isEmpty()` was missing a check for image views. +- structurizr-core: Promotes `ModelView.copyLayoutInformationFrom()` to be public, to allow individual view layout information to be merged. - structurizr-client: Fixes https://github.com/structurizr/java/issues/257 (Serialization to JSON is not deterministic). - structurizr-dsl: Fixes https://github.com/structurizr/java/issues/252 (DSL parser does not seem to handle curly brackets balance). - structurizr-dsl: Deprecates `!constant`, adds `!const` and `!var` (see https://github.com/structurizr/java/issues/253). diff --git a/structurizr-core/src/main/java/com/structurizr/view/ModelView.java b/structurizr-core/src/main/java/com/structurizr/view/ModelView.java index 98f5a446a..1128dcd9b 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/ModelView.java +++ b/structurizr-core/src/main/java/com/structurizr/view/ModelView.java @@ -369,7 +369,7 @@ public void setLayoutMergeStrategy(LayoutMergeStrategy layoutMergeStrategy) { * * @param source the source View */ - void copyLayoutInformationFrom(@Nonnull ModelView source) { + public void copyLayoutInformationFrom(@Nonnull ModelView source) { layoutMergeStrategy.copyLayoutInformation(source, this); } From f9be616ee105d1cc0d25074905387a1ce1912b52 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 2 Mar 2024 09:06:24 +0000 Subject: [PATCH 501/717] Updated release date. --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 9d0f97b0b..def4a45e0 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 2.1.0 (unreleased) +## 2.1.0 (2nd March 2024) - structurizr-core: `ViewSet.isEmpty()` was missing a check for image views. - structurizr-core: Promotes `ModelView.copyLayoutInformationFrom()` to be public, to allow individual view layout information to be merged. From b3fd678f3f2e47be9c1aeba927f17390febb9071 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 3 Mar 2024 09:28:37 +0000 Subject: [PATCH 502/717] structurizr-core: Fixes problem with ordering of relationship view vertices. --- build.gradle | 2 +- changelog.md | 4 ++++ .../java/com/structurizr/view/RelationshipView.java | 11 ++++------- .../src/main/java/com/structurizr/view/Vertex.java | 12 +----------- 4 files changed, 10 insertions(+), 19 deletions(-) diff --git a/build.gradle b/build.gradle index c1f5fd9f1..b877748c2 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '2.1.0' + version = '2.1.1' repositories { mavenCentral() diff --git a/changelog.md b/changelog.md index def4a45e0..17077fa59 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 2.1.1 (3rd March 2024) + +- structurizr-core: Fixes problem with ordering of relationship view vertices. + ## 2.1.0 (2nd March 2024) - structurizr-core: `ViewSet.isEmpty()` was missing a check for image views. diff --git a/structurizr-core/src/main/java/com/structurizr/view/RelationshipView.java b/structurizr-core/src/main/java/com/structurizr/view/RelationshipView.java index 38b0311fd..29d96c2e1 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/RelationshipView.java +++ b/structurizr-core/src/main/java/com/structurizr/view/RelationshipView.java @@ -6,10 +6,7 @@ import com.structurizr.util.StringUtils; import com.structurizr.util.Url; -import java.util.Collection; -import java.util.LinkedList; -import java.util.Set; -import java.util.TreeSet; +import java.util.*; /** * This class represents an instance of a Relationship on a View. @@ -25,7 +22,7 @@ public final class RelationshipView implements Comparable { private String url; private String order; private Boolean response; - private Set vertices = new TreeSet<>(); + private List vertices = new ArrayList<>(); @JsonInclude(value = JsonInclude.Include.NON_NULL) private Routing routing; @@ -158,7 +155,7 @@ void setResponse(Boolean response) { * @return a collection of Vertex objects */ public Collection getVertices() { - return new LinkedList<>(vertices); + return new ArrayList<>(vertices); } /** @@ -168,7 +165,7 @@ public Collection getVertices() { */ public void setVertices(Collection vertices) { if (vertices != null) { - this.vertices = new TreeSet<>(vertices); + this.vertices = new ArrayList<>(vertices); } } diff --git a/structurizr-core/src/main/java/com/structurizr/view/Vertex.java b/structurizr-core/src/main/java/com/structurizr/view/Vertex.java index 84e2a30c8..eda796e23 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/Vertex.java +++ b/structurizr-core/src/main/java/com/structurizr/view/Vertex.java @@ -5,7 +5,7 @@ /** * The X, Y coordinate of a bend in a line. */ -public final class Vertex implements Comparable { +public final class Vertex { private int x; private int y; @@ -63,14 +63,4 @@ public int hashCode() { return Objects.hash(x, y); } - @Override - public int compareTo(Vertex vertex) { - int result = getX() - vertex.getX(); - if (result == 0) { - result = getY() - vertex.getY(); - } - - return result; - } - } \ No newline at end of file From 5ae28b0cca6239f7a7c07d6cf33b35f9949bd793 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 15 Mar 2024 15:04:35 +0000 Subject: [PATCH 503/717] Adds some code to deal with old workspaces, or those created by third party tooling that are missing view `order` properties. --- .../api/BackwardsCompatibilityTests.java | 8 ++++++ .../views-without-order.json | 27 +++++++++++++++++++ .../main/java/com/structurizr/view/View.java | 7 ++++- 3 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 structurizr-client/src/integrationTest/resources/backwardsCompatibility/views-without-order.json diff --git a/structurizr-client/src/integrationTest/java/com/structurizr/api/BackwardsCompatibilityTests.java b/structurizr-client/src/integrationTest/java/com/structurizr/api/BackwardsCompatibilityTests.java index e029b5eca..6ae4cb020 100644 --- a/structurizr-client/src/integrationTest/java/com/structurizr/api/BackwardsCompatibilityTests.java +++ b/structurizr-client/src/integrationTest/java/com/structurizr/api/BackwardsCompatibilityTests.java @@ -48,4 +48,12 @@ void documentation() throws Exception { {"configuration":{},"description":"Description","documentation":{"sections":[{"content":"## Heading 1","format":"Markdown","order":1,"title":""}]},"id":0,"model":{},"name":"Name","views":{"configuration":{"branding":{},"styles":{},"terminology":{}}}}""", WorkspaceUtils.toJson(workspace, false)); } + @Test + void viewsWithoutOrderProperties() throws Exception { + File file = new File(PATH_TO_WORKSPACE_FILES, "views-without-order.json"); + Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(file); + + assertEquals(2, workspace.getViews().getSystemLandscapeViews().size()); + } + } \ No newline at end of file diff --git a/structurizr-client/src/integrationTest/resources/backwardsCompatibility/views-without-order.json b/structurizr-client/src/integrationTest/resources/backwardsCompatibility/views-without-order.json new file mode 100644 index 000000000..c84331f31 --- /dev/null +++ b/structurizr-client/src/integrationTest/resources/backwardsCompatibility/views-without-order.json @@ -0,0 +1,27 @@ +{ + "description" : "Description", + "id" : 0, + "model" : { + "people" : [ { + "id" : "1", + "name" : "User", + "tags" : "Element,Person" + } ] + }, + "name" : "Name", + "views" : { + "systemLandscapeViews" : [ { + "elements" : [ { + "id" : "1" + } ], + "enterpriseBoundaryVisible" : true, + "key" : "SystemLandscape-001" + }, { + "elements" : [ { + "id" : "1" + } ], + "enterpriseBoundaryVisible" : true, + "key" : "SystemLandscape-002" + } ] + } +} \ No newline at end of file diff --git a/structurizr-core/src/main/java/com/structurizr/view/View.java b/structurizr-core/src/main/java/com/structurizr/view/View.java index 510437490..c34e2bf58 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/View.java +++ b/structurizr-core/src/main/java/com/structurizr/view/View.java @@ -162,7 +162,12 @@ void setProperties(Map properties) { @Override public int compareTo(View view) { - return getOrder() - view.getOrder(); + int result = getOrder() - view.getOrder(); + if (result == 0) { + result = getKey().compareToIgnoreCase(view.getKey()); + } + + return result; } } \ No newline at end of file From 03205d6b6416800fed5dd65f00eeac8c4873ace5 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 23 Mar 2024 09:37:57 +0000 Subject: [PATCH 504/717] C4-PlantUML borderStyle now lower case (#263). --- changelog.md | 4 ++++ .../export/plantuml/C4PlantUMLExporter.java | 11 +++++----- .../C4PlantUMLDiagramExporterTests.java | 2 +- .../plantuml/c4plantuml/36141-Components.puml | 12 +++++------ .../plantuml/c4plantuml/36141-Containers.puml | 14 ++++++------- .../36141-DevelopmentDeployment.puml | 10 +++++----- .../c4plantuml/36141-LiveDeployment.puml | 14 ++++++------- .../c4plantuml/36141-SignIn-sequence.puml | 6 +++--- .../plantuml/c4plantuml/36141-SignIn.puml | 8 ++++---- .../c4plantuml/36141-SystemContext.puml | 8 ++++---- .../c4plantuml/36141-SystemLandscape.puml | 10 +++++----- ...-AmazonWebServicesDeployment-WithTags.puml | 20 +++++++++---------- .../plantuml/c4plantuml/group-styles-1.puml | 6 +++--- .../plantuml/c4plantuml/group-styles-2.puml | 6 +++--- .../c4plantuml/groups-Components.puml | 2 +- .../c4plantuml/groups-Containers.puml | 2 +- .../c4plantuml/groups-SystemLandscape.puml | 6 +++--- .../nested-groups-with-dot-separator.puml | 6 +++--- .../plantuml/c4plantuml/nested-groups.puml | 10 +++++----- 19 files changed, 80 insertions(+), 77 deletions(-) diff --git a/changelog.md b/changelog.md index 17077fa59..a28edc075 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,9 @@ # Changelog +## unreleased + +- structurizr-export: https://github.com/structurizr/java/issues/263 (Fixes C4PlantUMLExporter not following C4-PlantUML best practices with c4plantuml.tags true) + ## 2.1.1 (3rd March 2024) - structurizr-core: Fixes problem with ordering of relationship view vertices. diff --git a/structurizr-export/src/main/java/com/structurizr/export/plantuml/C4PlantUMLExporter.java b/structurizr-export/src/main/java/com/structurizr/export/plantuml/C4PlantUMLExporter.java index fa74211fe..d35ea3755 100644 --- a/structurizr-export/src/main/java/com/structurizr/export/plantuml/C4PlantUMLExporter.java +++ b/structurizr-export/src/main/java/com/structurizr/export/plantuml/C4PlantUMLExporter.java @@ -194,7 +194,7 @@ protected void writeHeader(ModelView view, IndentingWriter writer) { elementStyle.getColor(), sprite, elementStyle.getProperties().getOrDefault(C4PLANTUML_SHADOW, ""), - elementStyle.getBorder(), + elementStyle.getBorder().toString().toLowerCase(), borderThickness )); } @@ -241,7 +241,7 @@ protected void writeHeader(ModelView view, IndentingWriter writer) { elementStyle.getStroke(), elementStyle.getStroke(), elementStyle.getProperties().getOrDefault(C4PLANTUML_SHADOW, ""), - elementStyle.getBorder(), + elementStyle.getBorder().toString().toLowerCase(), borderThickness )); } @@ -288,7 +288,6 @@ protected void startGroupBoundary(ModelView view, String group, IndentingWriter } String color = "#cccccc"; - String borderStyle = "Dashed"; int borderThickness = 1; // String icon = ""; @@ -302,9 +301,9 @@ protected void startGroupBoundary(ModelView view, String group, IndentingWriter } if (elementStyleForGroup != null && !StringUtils.isNullOrEmpty(elementStyleForGroup.getStroke())) { - borderStyle = elementStyleForGroup.getStroke(); + color = elementStyleForGroup.getStroke(); } else if (elementStyleForAllGroups != null && !StringUtils.isNullOrEmpty(elementStyleForAllGroups.getStroke())) { - borderStyle = elementStyleForAllGroups.getStroke(); + color = elementStyleForAllGroups.getStroke(); } if (elementStyleForGroup != null && elementStyleForGroup.getStrokeWidth() != null) { @@ -330,7 +329,7 @@ protected void startGroupBoundary(ModelView view, String group, IndentingWriter group, color, color, - borderStyle, + Border.Dashed.toString().toLowerCase(), borderThickness) ); diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java index 8066396c2..49fae0fe2 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java @@ -717,7 +717,7 @@ public void borderStyling() { "!include \n" + "!include \n" + "\n" + - "AddElementTag(\"Element\", $bgColor=\"#dddddd\", $borderColor=\"#008000\", $fontColor=\"#000000\", $sprite=\"\", $shadowing=\"\", $borderStyle=\"Dashed\", $borderThickness=\"2\")\n" + + "AddElementTag(\"Element\", $bgColor=\"#dddddd\", $borderColor=\"#008000\", $fontColor=\"#000000\", $sprite=\"\", $shadowing=\"\", $borderStyle=\"dashed\", $borderThickness=\"2\")\n" + "\n" + "System(Name, \"Name\", $descr=\"\", $tags=\"Element\", $link=\"\")\n" + "\n" + diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Components.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Components.puml index a0d351686..90f8f4f7c 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Components.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Components.puml @@ -9,15 +9,15 @@ top to bottom direction !include !include -AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Component", $bgColor="#85bbf0", $borderColor="#5d82a8", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Container,Mobile App", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Component", $bgColor="#85bbf0", $borderColor="#5d82a8", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Container,Mobile App", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") -AddBoundaryTag("Container", $bgColor="#ffffff", $borderColor="#2e6295", $fontColor="#2e6295", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddBoundaryTag("Container", $bgColor="#ffffff", $borderColor="#2e6295", $fontColor="#2e6295", $shadowing="", $borderStyle="solid", $borderThickness="1") System(MainframeBankingSystem, "Mainframe Banking System", $descr="Stores all of the core banking information about customers, accounts, transactions, etc.", $tags="Software System,Existing System", $link="") System(EmailSystem, "E-mail System", $descr="The internal Microsoft Exchange e-mail system.", $tags="Software System,Existing System", $link="") diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Containers.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Containers.puml index 44fc2b9c6..2107ee2ae 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Containers.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Containers.puml @@ -8,16 +8,16 @@ top to bottom direction !include !include -AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Container,Mobile App", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Container", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Person,Customer", $bgColor="#08427b", $borderColor="#052e56", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Container,Mobile App", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Container", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Person,Customer", $bgColor="#08427b", $borderColor="#052e56", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") -AddBoundaryTag("Software System", $bgColor="#ffffff", $borderColor="#0b4884", $fontColor="#0b4884", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddBoundaryTag("Software System", $bgColor="#ffffff", $borderColor="#0b4884", $fontColor="#0b4884", $shadowing="", $borderStyle="solid", $borderThickness="1") Person(PersonalBankingCustomer, "Personal Banking Customer", $descr="A customer of the bank, with personal bank accounts.", $tags="Person,Customer", $link="") System(MainframeBankingSystem, "Mainframe Banking System", $descr="Stores all of the core banking information about customers, accounts, transactions, etc.", $tags="Software System,Existing System", $link="") diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-DevelopmentDeployment.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-DevelopmentDeployment.puml index 68f04bd91..556bd25a6 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-DevelopmentDeployment.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-DevelopmentDeployment.puml @@ -9,11 +9,11 @@ top to bottom direction !include !include -AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Element", $bgColor="#ffffff", $borderColor="#888888", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Container", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Element", $bgColor="#ffffff", $borderColor="#888888", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Container", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-LiveDeployment.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-LiveDeployment.puml index decda55a1..f5a085b48 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-LiveDeployment.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-LiveDeployment.puml @@ -9,13 +9,13 @@ top to bottom direction !include !include -AddElementTag("Failover", $bgColor="#ffffff", $borderColor="#888888", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Element", $bgColor="#ffffff", $borderColor="#888888", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Container,Mobile App", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Container", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Failover", $bgColor="#ffffff", $borderColor="#888888", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Element", $bgColor="#ffffff", $borderColor="#888888", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Container,Mobile App", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Container", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn-sequence.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn-sequence.puml index 3ee74b0c7..77915aea0 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn-sequence.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn-sequence.puml @@ -4,9 +4,9 @@ title API Application - Dynamic !include -AddElementTag("Component", $bgColor="#85bbf0", $borderColor="#5d82a8", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Component", $bgColor="#85bbf0", $borderColor="#5d82a8", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn.puml index b86a5b2eb..c06a5ce77 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn.puml @@ -9,13 +9,13 @@ top to bottom direction !include !include -AddElementTag("Component", $bgColor="#85bbf0", $borderColor="#5d82a8", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Component", $bgColor="#85bbf0", $borderColor="#5d82a8", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") -AddBoundaryTag("Container", $bgColor="#ffffff", $borderColor="#2e6295", $fontColor="#2e6295", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddBoundaryTag("Container", $bgColor="#ffffff", $borderColor="#2e6295", $fontColor="#2e6295", $shadowing="", $borderStyle="solid", $borderThickness="1") Container_Boundary("InternetBankingSystem.APIApplication_boundary", "API Application", $tags="Container") { Component(InternetBankingSystem.APIApplication.SignInController, "Sign In Controller", $techn="Spring MVC Rest Controller", $descr="Allows users to sign in to the Internet Banking System.", $tags="Component", $link="") diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemContext.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemContext.puml index eb64d4554..94b45a62f 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemContext.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemContext.puml @@ -7,13 +7,13 @@ top to bottom direction !include !include -AddElementTag("Software System", $bgColor="#1168bd", $borderColor="#0b4884", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Person,Customer", $bgColor="#08427b", $borderColor="#052e56", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Software System", $bgColor="#1168bd", $borderColor="#0b4884", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Person,Customer", $bgColor="#08427b", $borderColor="#052e56", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") -AddBoundaryTag("Big Bank plc", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="Dashed", $borderThickness="1") +AddBoundaryTag("Big Bank plc", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed", $borderThickness="1") Boundary(group_1, "Big Bank plc", $tags="Big Bank plc") { System(MainframeBankingSystem, "Mainframe Banking System", $descr="Stores all of the core banking information about customers, accounts, transactions, etc.", $tags="Software System,Existing System", $link="") System(EmailSystem, "E-mail System", $descr="The internal Microsoft Exchange e-mail system.", $tags="Software System,Existing System", $link="") diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemLandscape.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemLandscape.puml index 6a4972739..60fa50dbe 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemLandscape.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemLandscape.puml @@ -7,14 +7,14 @@ top to bottom direction !include !include -AddElementTag("Software System", $bgColor="#1168bd", $borderColor="#0b4884", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Person,Bank Staff", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Person,Customer", $bgColor="#08427b", $borderColor="#052e56", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Software System", $bgColor="#1168bd", $borderColor="#0b4884", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Person,Bank Staff", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Person,Customer", $bgColor="#08427b", $borderColor="#052e56", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") -AddBoundaryTag("Big Bank plc", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="Dashed", $borderThickness="1") +AddBoundaryTag("Big Bank plc", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed", $borderThickness="1") Boundary(group_1, "Big Bank plc", $tags="Big Bank plc") { Person(CustomerServiceStaff, "Customer Service Staff", $descr="Customer service staff within the bank.", $tags="Person,Bank Staff", $link="") Person(BackOfficeStaff, "Back Office Staff", $descr="Administration and support staff within the bank.", $tags="Person,Bank Staff", $link="") diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/54915-AmazonWebServicesDeployment-WithTags.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/54915-AmazonWebServicesDeployment-WithTags.puml index db81378bc..32852ce35 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/54915-AmazonWebServicesDeployment-WithTags.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/54915-AmazonWebServicesDeployment-WithTags.puml @@ -9,16 +9,16 @@ left to right direction !include !include -AddElementTag("Container,Application", $bgColor="#ffffff", $borderColor="#b2b2b2", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Amazon Web Services - RDS", $bgColor="#ffffff", $borderColor="#3b48cc", $fontColor="#3b48cc", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Amazon-RDS_light-bg@4x.png{scale=0.1}", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Amazon Web Services - Auto Scaling", $bgColor="#ffffff", $borderColor="#cc2264", $fontColor="#cc2264", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/AWS-Auto-Scaling_light-bg@4x.png{scale=0.1}", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Amazon Web Services - Route 53", $bgColor="#ffffff", $borderColor="#693cc5", $fontColor="#693cc5", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Amazon-Route-53_light-bg@4x.png{scale=0.1}", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Amazon Web Services - EC2", $bgColor="#ffffff", $borderColor="#d86613", $fontColor="#d86613", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Amazon-EC2_light-bg@4x.png{scale=0.1}", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Amazon Web Services - Region", $bgColor="#ffffff", $borderColor="#147eba", $fontColor="#147eba", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Region_light-bg@4x.png{scale=0.21428571428571427}", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Amazon Web Services - Elastic Load Balancing", $bgColor="#ffffff", $borderColor="#693cc5", $fontColor="#693cc5", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Elastic-Load-Balancing_light-bg@4x.png{scale=0.1}", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Amazon Web Services - RDS MySQL instance", $bgColor="#ffffff", $borderColor="#3b48cc", $fontColor="#3b48cc", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Amazon-RDS_MySQL_instance_light-bg@4x.png{scale=0.15}", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Container,Database", $bgColor="#ffffff", $borderColor="#b2b2b2", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="Solid", $borderThickness="1") -AddElementTag("Amazon Web Services - Cloud", $bgColor="#ffffff", $borderColor="#232f3e", $fontColor="#232f3e", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/AWS-Cloud_light-bg@4x.png{scale=0.21428571428571427}", $shadowing="", $borderStyle="Solid", $borderThickness="1") +AddElementTag("Container,Application", $bgColor="#ffffff", $borderColor="#b2b2b2", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Amazon Web Services - RDS", $bgColor="#ffffff", $borderColor="#3b48cc", $fontColor="#3b48cc", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Amazon-RDS_light-bg@4x.png{scale=0.1}", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Amazon Web Services - Auto Scaling", $bgColor="#ffffff", $borderColor="#cc2264", $fontColor="#cc2264", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/AWS-Auto-Scaling_light-bg@4x.png{scale=0.1}", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Amazon Web Services - Route 53", $bgColor="#ffffff", $borderColor="#693cc5", $fontColor="#693cc5", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Amazon-Route-53_light-bg@4x.png{scale=0.1}", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Amazon Web Services - EC2", $bgColor="#ffffff", $borderColor="#d86613", $fontColor="#d86613", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Amazon-EC2_light-bg@4x.png{scale=0.1}", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Amazon Web Services - Region", $bgColor="#ffffff", $borderColor="#147eba", $fontColor="#147eba", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Region_light-bg@4x.png{scale=0.21428571428571427}", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Amazon Web Services - Elastic Load Balancing", $bgColor="#ffffff", $borderColor="#693cc5", $fontColor="#693cc5", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Elastic-Load-Balancing_light-bg@4x.png{scale=0.1}", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Amazon Web Services - RDS MySQL instance", $bgColor="#ffffff", $borderColor="#3b48cc", $fontColor="#3b48cc", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Amazon-RDS_MySQL_instance_light-bg@4x.png{scale=0.15}", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Container,Database", $bgColor="#ffffff", $borderColor="#b2b2b2", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Amazon Web Services - Cloud", $bgColor="#ffffff", $borderColor="#232f3e", $fontColor="#232f3e", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/AWS-Cloud_light-bg@4x.png{scale=0.21428571428571427}", $shadowing="", $borderStyle="solid", $borderThickness="1") AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/group-styles-1.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/group-styles-1.puml index 94c6a07ac..05d0c97eb 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/group-styles-1.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/group-styles-1.puml @@ -7,17 +7,17 @@ top to bottom direction !include !include -AddBoundaryTag("Group 1", $borderColor="#111111", $fontColor="#111111", $borderStyle="Dashed", $borderThickness="1") +AddBoundaryTag("Group 1", $borderColor="#111111", $fontColor="#111111", $borderStyle="dashed", $borderThickness="1") Boundary(group_1, "Group 1", $tags="Group 1") { Person(User1, "User 1", $descr="", $tags="", $link="") } -AddBoundaryTag("Group 2", $borderColor="#222222", $fontColor="#222222", $borderStyle="Dashed", $borderThickness="1") +AddBoundaryTag("Group 2", $borderColor="#222222", $fontColor="#222222", $borderStyle="dashed", $borderThickness="1") Boundary(group_2, "Group 2", $tags="Group 2") { Person(User2, "User 2", $descr="", $tags="", $link="") } -AddBoundaryTag("Group 3", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="Dashed", $borderThickness="1") +AddBoundaryTag("Group 3", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed", $borderThickness="1") Boundary(group_3, "Group 3", $tags="Group 3") { Person(User3, "User 3", $descr="", $tags="", $link="") } diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/group-styles-2.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/group-styles-2.puml index 085b61b85..94d7e5c81 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/group-styles-2.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/group-styles-2.puml @@ -7,17 +7,17 @@ top to bottom direction !include !include -AddBoundaryTag("Group 1", $borderColor="#111111", $fontColor="#111111", $borderStyle="Dashed", $borderThickness="1") +AddBoundaryTag("Group 1", $borderColor="#111111", $fontColor="#111111", $borderStyle="dashed", $borderThickness="1") Boundary(group_1, "Group 1", $tags="Group 1") { Person(User1, "User 1", $descr="", $tags="", $link="") } -AddBoundaryTag("Group 2", $borderColor="#222222", $fontColor="#222222", $borderStyle="Dashed", $borderThickness="1") +AddBoundaryTag("Group 2", $borderColor="#222222", $fontColor="#222222", $borderStyle="dashed", $borderThickness="1") Boundary(group_2, "Group 2", $tags="Group 2") { Person(User2, "User 2", $descr="", $tags="", $link="") } -AddBoundaryTag("Group 3", $borderColor="#aabbcc", $fontColor="#aabbcc", $borderStyle="Dashed", $borderThickness="1") +AddBoundaryTag("Group 3", $borderColor="#aabbcc", $fontColor="#aabbcc", $borderStyle="dashed", $borderThickness="1") Boundary(group_3, "Group 3", $tags="Group 3") { Person(User3, "User 3", $descr="", $tags="", $link="") } diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Components.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Components.puml index b09edaf21..4cb63cacb 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Components.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Components.puml @@ -11,7 +11,7 @@ top to bottom direction System(C, "C", $descr="", $tags="", $link="") Container_Boundary("D.F_boundary", "F", $tags="") { - AddBoundaryTag("Group 5", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="Dashed", $borderThickness="1") + AddBoundaryTag("Group 5", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed", $borderThickness="1") Boundary(group_1, "Group 5", $tags="Group 5") { Component(D.F.H, "H", $techn="", $descr="", $tags="", $link="") } diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Containers.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Containers.puml index 2b7569d6b..76539e198 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Containers.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Containers.puml @@ -11,7 +11,7 @@ top to bottom direction System(C, "C", $descr="", $tags="", $link="") System_Boundary("D_boundary", "D", $tags="") { - AddBoundaryTag("Group 4", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="Dashed", $borderThickness="1") + AddBoundaryTag("Group 4", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed", $borderThickness="1") Boundary(group_1, "Group 4", $tags="Group 4") { Container(D.F, "F", $techn="", $descr="", $tags="", $link="") } diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-SystemLandscape.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-SystemLandscape.puml index a1620f49f..3aee39e27 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-SystemLandscape.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-SystemLandscape.puml @@ -7,15 +7,15 @@ top to bottom direction !include !include -AddBoundaryTag("Group 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="Dashed", $borderThickness="1") +AddBoundaryTag("Group 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed", $borderThickness="1") Boundary(group_1, "Group 1", $tags="Group 1") { System(B, "B", $descr="", $tags="", $link="") } -AddBoundaryTag("Group 2", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="Dashed", $borderThickness="1") +AddBoundaryTag("Group 2", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed", $borderThickness="1") Boundary(group_2, "Group 2", $tags="Group 2") { System(C, "C", $descr="", $tags="", $link="") - AddBoundaryTag("Group 2/Group 3", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="Dashed", $borderThickness="1") + AddBoundaryTag("Group 2/Group 3", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed", $borderThickness="1") Boundary(group_3, "Group 3", $tags="Group 2/Group 3") { System(D, "D", $descr="", $tags="", $link="") } diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/nested-groups-with-dot-separator.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/nested-groups-with-dot-separator.puml index cda61f1b3..a22d44fe2 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/nested-groups-with-dot-separator.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/nested-groups-with-dot-separator.puml @@ -7,11 +7,11 @@ top to bottom direction !include !include -AddBoundaryTag("Organisation 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="Dashed", $borderThickness="1") +AddBoundaryTag("Organisation 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed", $borderThickness="1") Boundary(group_1, "Organisation 1", $tags="Organisation 1") { - AddBoundaryTag("Organisation 1.Department 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="Dashed", $borderThickness="1") + AddBoundaryTag("Organisation 1.Department 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed", $borderThickness="1") Boundary(group_2, "Department 1", $tags="Organisation 1.Department 1") { - AddBoundaryTag("Organisation 1.Department 1.Team 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="Dashed", $borderThickness="1") + AddBoundaryTag("Organisation 1.Department 1.Team 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed", $borderThickness="1") Boundary(group_3, "Team 1", $tags="Organisation 1.Department 1.Team 1") { System(Team1, "Team 1", $descr="", $tags="", $link="") } diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/nested-groups.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/nested-groups.puml index 783dc9fb8..7ad912df6 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/nested-groups.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/nested-groups.puml @@ -7,18 +7,18 @@ top to bottom direction !include !include -AddBoundaryTag("Organisation 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="Dashed", $borderThickness="1") +AddBoundaryTag("Organisation 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed", $borderThickness="1") Boundary(group_1, "Organisation 1", $tags="Organisation 1") { System(Organisation1, "Organisation 1", $descr="", $tags="", $link="") - AddBoundaryTag("Organisation 1/Department 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="Dashed", $borderThickness="1") + AddBoundaryTag("Organisation 1/Department 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed", $borderThickness="1") Boundary(group_2, "Department 1", $tags="Organisation 1/Department 1") { System(Department1, "Department 1", $descr="", $tags="", $link="") - AddBoundaryTag("Organisation 1/Department 1/Team 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="Dashed", $borderThickness="1") + AddBoundaryTag("Organisation 1/Department 1/Team 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed", $borderThickness="1") Boundary(group_3, "Team 1", $tags="Organisation 1/Department 1/Team 1") { System(Team1, "Team 1", $descr="", $tags="", $link="") } - AddBoundaryTag("Organisation 1/Department 1/Team 2", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="Dashed", $borderThickness="1") + AddBoundaryTag("Organisation 1/Department 1/Team 2", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed", $borderThickness="1") Boundary(group_4, "Team 2", $tags="Organisation 1/Department 1/Team 2") { System(Team2, "Team 2", $descr="", $tags="", $link="") } @@ -27,7 +27,7 @@ Boundary(group_1, "Organisation 1", $tags="Organisation 1") { } -AddBoundaryTag("Organisation 2", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="Dashed", $borderThickness="1") +AddBoundaryTag("Organisation 2", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed", $borderThickness="1") Boundary(group_5, "Organisation 2", $tags="Organisation 2") { System(Organisation2, "Organisation 2", $descr="", $tags="", $link="") } From 39bf3d0890afc4caa52cf3302269a12fc2a4236b Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 23 Mar 2024 09:43:34 +0000 Subject: [PATCH 505/717] Suppresses $borderThickness="1" (#263). --- .../export/plantuml/C4PlantUMLExporter.java | 22 +++++++++++++------ .../plantuml/c4plantuml/36141-Components.puml | 12 +++++----- .../plantuml/c4plantuml/36141-Containers.puml | 14 ++++++------ .../36141-DevelopmentDeployment.puml | 10 ++++----- .../c4plantuml/36141-LiveDeployment.puml | 14 ++++++------ .../c4plantuml/36141-SignIn-sequence.puml | 6 ++--- .../plantuml/c4plantuml/36141-SignIn.puml | 8 +++---- .../c4plantuml/36141-SystemContext.puml | 8 +++---- .../c4plantuml/36141-SystemLandscape.puml | 10 ++++----- ...-AmazonWebServicesDeployment-WithTags.puml | 20 ++++++++--------- .../plantuml/c4plantuml/group-styles-1.puml | 6 ++--- .../plantuml/c4plantuml/group-styles-2.puml | 6 ++--- .../c4plantuml/groups-Components.puml | 2 +- .../c4plantuml/groups-Containers.puml | 2 +- .../c4plantuml/groups-SystemLandscape.puml | 6 ++--- .../nested-groups-with-dot-separator.puml | 6 ++--- .../plantuml/c4plantuml/nested-groups.puml | 10 ++++----- 17 files changed, 85 insertions(+), 77 deletions(-) diff --git a/structurizr-export/src/main/java/com/structurizr/export/plantuml/C4PlantUMLExporter.java b/structurizr-export/src/main/java/com/structurizr/export/plantuml/C4PlantUMLExporter.java index d35ea3755..7566c7b32 100644 --- a/structurizr-export/src/main/java/com/structurizr/export/plantuml/C4PlantUMLExporter.java +++ b/structurizr-export/src/main/java/com/structurizr/export/plantuml/C4PlantUMLExporter.java @@ -187,7 +187,7 @@ protected void writeHeader(ModelView view, IndentingWriter writer) { borderThickness = elementStyle.getStrokeWidth(); } - writer.writeLine(String.format("AddElementTag(\"%s\", $bgColor=\"%s\", $borderColor=\"%s\", $fontColor=\"%s\", $sprite=\"%s\", $shadowing=\"%s\", $borderStyle=\"%s\", $borderThickness=\"%s\")", + String line = String.format("AddElementTag(\"%s\", $bgColor=\"%s\", $borderColor=\"%s\", $fontColor=\"%s\", $sprite=\"%s\", $shadowing=\"%s\", $borderStyle=\"%s\", $borderThickness=\"%s\")", tagList, elementStyle.getBackground(), elementStyle.getStroke(), @@ -196,7 +196,10 @@ protected void writeHeader(ModelView view, IndentingWriter writer) { elementStyle.getProperties().getOrDefault(C4PLANTUML_SHADOW, ""), elementStyle.getBorder().toString().toLowerCase(), borderThickness - )); + ); + + line = line.replace(", $borderThickness=\"1\")", ")"); + writer.writeLine(line); } } @@ -235,7 +238,7 @@ protected void writeHeader(ModelView view, IndentingWriter writer) { borderThickness = elementStyle.getStrokeWidth(); } - writer.writeLine(String.format("AddBoundaryTag(\"%s\", $bgColor=\"%s\", $borderColor=\"%s\", $fontColor=\"%s\", $shadowing=\"%s\", $borderStyle=\"%s\", $borderThickness=\"%s\")", + String line = String.format("AddBoundaryTag(\"%s\", $bgColor=\"%s\", $borderColor=\"%s\", $fontColor=\"%s\", $shadowing=\"%s\", $borderStyle=\"%s\", $borderThickness=\"%s\")", tagList, "#ffffff", elementStyle.getStroke(), @@ -243,7 +246,10 @@ protected void writeHeader(ModelView view, IndentingWriter writer) { elementStyle.getProperties().getOrDefault(C4PLANTUML_SHADOW, ""), elementStyle.getBorder().toString().toLowerCase(), borderThickness - )); + ); + + line = line.replace(", $borderThickness=\"1\")", ")"); + writer.writeLine(line); } } } @@ -325,13 +331,15 @@ protected void startGroupBoundary(ModelView view, String group, IndentingWriter // icon = "\\n\\n"; // } - writer.writeLine(String.format("AddBoundaryTag(\"%s\", $borderColor=\"%s\", $fontColor=\"%s\", $borderStyle=\"%s\", $borderThickness=\"%s\")", + String line = String.format("AddBoundaryTag(\"%s\", $borderColor=\"%s\", $fontColor=\"%s\", $borderStyle=\"%s\", $borderThickness=\"%s\")", group, color, color, Border.Dashed.toString().toLowerCase(), - borderThickness) - ); + borderThickness); + + line = line.replace(", $borderThickness=\"1\")", ")"); + writer.writeLine(line); writer.writeLine(String.format("Boundary(group_%s, \"%s\", $tags=\"%s\") {", groupId, groupName, group)); writer.indent(); diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Components.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Components.puml index 90f8f4f7c..7bce7a631 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Components.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Components.puml @@ -9,15 +9,15 @@ top to bottom direction !include !include -AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") -AddElementTag("Component", $bgColor="#85bbf0", $borderColor="#5d82a8", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") -AddElementTag("Container,Mobile App", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") -AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") -AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") +AddElementTag("Component", $bgColor="#85bbf0", $borderColor="#5d82a8", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="solid") +AddElementTag("Container,Mobile App", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") +AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") +AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") -AddBoundaryTag("Container", $bgColor="#ffffff", $borderColor="#2e6295", $fontColor="#2e6295", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddBoundaryTag("Container", $bgColor="#ffffff", $borderColor="#2e6295", $fontColor="#2e6295", $shadowing="", $borderStyle="solid") System(MainframeBankingSystem, "Mainframe Banking System", $descr="Stores all of the core banking information about customers, accounts, transactions, etc.", $tags="Software System,Existing System", $link="") System(EmailSystem, "E-mail System", $descr="The internal Microsoft Exchange e-mail system.", $tags="Software System,Existing System", $link="") diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Containers.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Containers.puml index 2107ee2ae..789bfdee0 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Containers.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Containers.puml @@ -8,16 +8,16 @@ top to bottom direction !include !include -AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") -AddElementTag("Container,Mobile App", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") -AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") -AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") -AddElementTag("Container", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") -AddElementTag("Person,Customer", $bgColor="#08427b", $borderColor="#052e56", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") +AddElementTag("Container,Mobile App", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") +AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") +AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") +AddElementTag("Container", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") +AddElementTag("Person,Customer", $bgColor="#08427b", $borderColor="#052e56", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") -AddBoundaryTag("Software System", $bgColor="#ffffff", $borderColor="#0b4884", $fontColor="#0b4884", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddBoundaryTag("Software System", $bgColor="#ffffff", $borderColor="#0b4884", $fontColor="#0b4884", $shadowing="", $borderStyle="solid") Person(PersonalBankingCustomer, "Personal Banking Customer", $descr="A customer of the bank, with personal bank accounts.", $tags="Person,Customer", $link="") System(MainframeBankingSystem, "Mainframe Banking System", $descr="Stores all of the core banking information about customers, accounts, transactions, etc.", $tags="Software System,Existing System", $link="") diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-DevelopmentDeployment.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-DevelopmentDeployment.puml index 556bd25a6..0158ada25 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-DevelopmentDeployment.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-DevelopmentDeployment.puml @@ -9,11 +9,11 @@ top to bottom direction !include !include -AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") -AddElementTag("Element", $bgColor="#ffffff", $borderColor="#888888", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") -AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") -AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") -AddElementTag("Container", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") +AddElementTag("Element", $bgColor="#ffffff", $borderColor="#888888", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="solid") +AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") +AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") +AddElementTag("Container", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-LiveDeployment.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-LiveDeployment.puml index f5a085b48..177f25979 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-LiveDeployment.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-LiveDeployment.puml @@ -9,13 +9,13 @@ top to bottom direction !include !include -AddElementTag("Failover", $bgColor="#ffffff", $borderColor="#888888", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") -AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") -AddElementTag("Element", $bgColor="#ffffff", $borderColor="#888888", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") -AddElementTag("Container,Mobile App", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") -AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") -AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") -AddElementTag("Container", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Failover", $bgColor="#ffffff", $borderColor="#888888", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="solid") +AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") +AddElementTag("Element", $bgColor="#ffffff", $borderColor="#888888", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="solid") +AddElementTag("Container,Mobile App", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") +AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") +AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") +AddElementTag("Container", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn-sequence.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn-sequence.puml index 77915aea0..306185a43 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn-sequence.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn-sequence.puml @@ -4,9 +4,9 @@ title API Application - Dynamic !include -AddElementTag("Component", $bgColor="#85bbf0", $borderColor="#5d82a8", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") -AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") -AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Component", $bgColor="#85bbf0", $borderColor="#5d82a8", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="solid") +AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") +AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn.puml index c06a5ce77..2ed68bacd 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn.puml @@ -9,13 +9,13 @@ top to bottom direction !include !include -AddElementTag("Component", $bgColor="#85bbf0", $borderColor="#5d82a8", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") -AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") -AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Component", $bgColor="#85bbf0", $borderColor="#5d82a8", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="solid") +AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") +AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") -AddBoundaryTag("Container", $bgColor="#ffffff", $borderColor="#2e6295", $fontColor="#2e6295", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddBoundaryTag("Container", $bgColor="#ffffff", $borderColor="#2e6295", $fontColor="#2e6295", $shadowing="", $borderStyle="solid") Container_Boundary("InternetBankingSystem.APIApplication_boundary", "API Application", $tags="Container") { Component(InternetBankingSystem.APIApplication.SignInController, "Sign In Controller", $techn="Spring MVC Rest Controller", $descr="Allows users to sign in to the Internet Banking System.", $tags="Component", $link="") diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemContext.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemContext.puml index 94b45a62f..5151c41db 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemContext.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemContext.puml @@ -7,13 +7,13 @@ top to bottom direction !include !include -AddElementTag("Software System", $bgColor="#1168bd", $borderColor="#0b4884", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") -AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") -AddElementTag("Person,Customer", $bgColor="#08427b", $borderColor="#052e56", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Software System", $bgColor="#1168bd", $borderColor="#0b4884", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") +AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") +AddElementTag("Person,Customer", $bgColor="#08427b", $borderColor="#052e56", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") -AddBoundaryTag("Big Bank plc", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed", $borderThickness="1") +AddBoundaryTag("Big Bank plc", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") Boundary(group_1, "Big Bank plc", $tags="Big Bank plc") { System(MainframeBankingSystem, "Mainframe Banking System", $descr="Stores all of the core banking information about customers, accounts, transactions, etc.", $tags="Software System,Existing System", $link="") System(EmailSystem, "E-mail System", $descr="The internal Microsoft Exchange e-mail system.", $tags="Software System,Existing System", $link="") diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemLandscape.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemLandscape.puml index 60fa50dbe..c49923d47 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemLandscape.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemLandscape.puml @@ -7,14 +7,14 @@ top to bottom direction !include !include -AddElementTag("Software System", $bgColor="#1168bd", $borderColor="#0b4884", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") -AddElementTag("Person,Bank Staff", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") -AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") -AddElementTag("Person,Customer", $bgColor="#08427b", $borderColor="#052e56", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Software System", $bgColor="#1168bd", $borderColor="#0b4884", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") +AddElementTag("Person,Bank Staff", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") +AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") +AddElementTag("Person,Customer", $bgColor="#08427b", $borderColor="#052e56", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") -AddBoundaryTag("Big Bank plc", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed", $borderThickness="1") +AddBoundaryTag("Big Bank plc", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") Boundary(group_1, "Big Bank plc", $tags="Big Bank plc") { Person(CustomerServiceStaff, "Customer Service Staff", $descr="Customer service staff within the bank.", $tags="Person,Bank Staff", $link="") Person(BackOfficeStaff, "Back Office Staff", $descr="Administration and support staff within the bank.", $tags="Person,Bank Staff", $link="") diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/54915-AmazonWebServicesDeployment-WithTags.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/54915-AmazonWebServicesDeployment-WithTags.puml index 32852ce35..420931723 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/54915-AmazonWebServicesDeployment-WithTags.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/54915-AmazonWebServicesDeployment-WithTags.puml @@ -9,16 +9,16 @@ left to right direction !include !include -AddElementTag("Container,Application", $bgColor="#ffffff", $borderColor="#b2b2b2", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") -AddElementTag("Amazon Web Services - RDS", $bgColor="#ffffff", $borderColor="#3b48cc", $fontColor="#3b48cc", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Amazon-RDS_light-bg@4x.png{scale=0.1}", $shadowing="", $borderStyle="solid", $borderThickness="1") -AddElementTag("Amazon Web Services - Auto Scaling", $bgColor="#ffffff", $borderColor="#cc2264", $fontColor="#cc2264", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/AWS-Auto-Scaling_light-bg@4x.png{scale=0.1}", $shadowing="", $borderStyle="solid", $borderThickness="1") -AddElementTag("Amazon Web Services - Route 53", $bgColor="#ffffff", $borderColor="#693cc5", $fontColor="#693cc5", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Amazon-Route-53_light-bg@4x.png{scale=0.1}", $shadowing="", $borderStyle="solid", $borderThickness="1") -AddElementTag("Amazon Web Services - EC2", $bgColor="#ffffff", $borderColor="#d86613", $fontColor="#d86613", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Amazon-EC2_light-bg@4x.png{scale=0.1}", $shadowing="", $borderStyle="solid", $borderThickness="1") -AddElementTag("Amazon Web Services - Region", $bgColor="#ffffff", $borderColor="#147eba", $fontColor="#147eba", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Region_light-bg@4x.png{scale=0.21428571428571427}", $shadowing="", $borderStyle="solid", $borderThickness="1") -AddElementTag("Amazon Web Services - Elastic Load Balancing", $bgColor="#ffffff", $borderColor="#693cc5", $fontColor="#693cc5", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Elastic-Load-Balancing_light-bg@4x.png{scale=0.1}", $shadowing="", $borderStyle="solid", $borderThickness="1") -AddElementTag("Amazon Web Services - RDS MySQL instance", $bgColor="#ffffff", $borderColor="#3b48cc", $fontColor="#3b48cc", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Amazon-RDS_MySQL_instance_light-bg@4x.png{scale=0.15}", $shadowing="", $borderStyle="solid", $borderThickness="1") -AddElementTag("Container,Database", $bgColor="#ffffff", $borderColor="#b2b2b2", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="solid", $borderThickness="1") -AddElementTag("Amazon Web Services - Cloud", $bgColor="#ffffff", $borderColor="#232f3e", $fontColor="#232f3e", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/AWS-Cloud_light-bg@4x.png{scale=0.21428571428571427}", $shadowing="", $borderStyle="solid", $borderThickness="1") +AddElementTag("Container,Application", $bgColor="#ffffff", $borderColor="#b2b2b2", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="solid") +AddElementTag("Amazon Web Services - RDS", $bgColor="#ffffff", $borderColor="#3b48cc", $fontColor="#3b48cc", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Amazon-RDS_light-bg@4x.png{scale=0.1}", $shadowing="", $borderStyle="solid") +AddElementTag("Amazon Web Services - Auto Scaling", $bgColor="#ffffff", $borderColor="#cc2264", $fontColor="#cc2264", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/AWS-Auto-Scaling_light-bg@4x.png{scale=0.1}", $shadowing="", $borderStyle="solid") +AddElementTag("Amazon Web Services - Route 53", $bgColor="#ffffff", $borderColor="#693cc5", $fontColor="#693cc5", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Amazon-Route-53_light-bg@4x.png{scale=0.1}", $shadowing="", $borderStyle="solid") +AddElementTag("Amazon Web Services - EC2", $bgColor="#ffffff", $borderColor="#d86613", $fontColor="#d86613", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Amazon-EC2_light-bg@4x.png{scale=0.1}", $shadowing="", $borderStyle="solid") +AddElementTag("Amazon Web Services - Region", $bgColor="#ffffff", $borderColor="#147eba", $fontColor="#147eba", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Region_light-bg@4x.png{scale=0.21428571428571427}", $shadowing="", $borderStyle="solid") +AddElementTag("Amazon Web Services - Elastic Load Balancing", $bgColor="#ffffff", $borderColor="#693cc5", $fontColor="#693cc5", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Elastic-Load-Balancing_light-bg@4x.png{scale=0.1}", $shadowing="", $borderStyle="solid") +AddElementTag("Amazon Web Services - RDS MySQL instance", $bgColor="#ffffff", $borderColor="#3b48cc", $fontColor="#3b48cc", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Amazon-RDS_MySQL_instance_light-bg@4x.png{scale=0.15}", $shadowing="", $borderStyle="solid") +AddElementTag("Container,Database", $bgColor="#ffffff", $borderColor="#b2b2b2", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="solid") +AddElementTag("Amazon Web Services - Cloud", $bgColor="#ffffff", $borderColor="#232f3e", $fontColor="#232f3e", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/AWS-Cloud_light-bg@4x.png{scale=0.21428571428571427}", $shadowing="", $borderStyle="solid") AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/group-styles-1.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/group-styles-1.puml index 05d0c97eb..180f8c3aa 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/group-styles-1.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/group-styles-1.puml @@ -7,17 +7,17 @@ top to bottom direction !include !include -AddBoundaryTag("Group 1", $borderColor="#111111", $fontColor="#111111", $borderStyle="dashed", $borderThickness="1") +AddBoundaryTag("Group 1", $borderColor="#111111", $fontColor="#111111", $borderStyle="dashed") Boundary(group_1, "Group 1", $tags="Group 1") { Person(User1, "User 1", $descr="", $tags="", $link="") } -AddBoundaryTag("Group 2", $borderColor="#222222", $fontColor="#222222", $borderStyle="dashed", $borderThickness="1") +AddBoundaryTag("Group 2", $borderColor="#222222", $fontColor="#222222", $borderStyle="dashed") Boundary(group_2, "Group 2", $tags="Group 2") { Person(User2, "User 2", $descr="", $tags="", $link="") } -AddBoundaryTag("Group 3", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed", $borderThickness="1") +AddBoundaryTag("Group 3", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") Boundary(group_3, "Group 3", $tags="Group 3") { Person(User3, "User 3", $descr="", $tags="", $link="") } diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/group-styles-2.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/group-styles-2.puml index 94d7e5c81..ff23c0a76 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/group-styles-2.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/group-styles-2.puml @@ -7,17 +7,17 @@ top to bottom direction !include !include -AddBoundaryTag("Group 1", $borderColor="#111111", $fontColor="#111111", $borderStyle="dashed", $borderThickness="1") +AddBoundaryTag("Group 1", $borderColor="#111111", $fontColor="#111111", $borderStyle="dashed") Boundary(group_1, "Group 1", $tags="Group 1") { Person(User1, "User 1", $descr="", $tags="", $link="") } -AddBoundaryTag("Group 2", $borderColor="#222222", $fontColor="#222222", $borderStyle="dashed", $borderThickness="1") +AddBoundaryTag("Group 2", $borderColor="#222222", $fontColor="#222222", $borderStyle="dashed") Boundary(group_2, "Group 2", $tags="Group 2") { Person(User2, "User 2", $descr="", $tags="", $link="") } -AddBoundaryTag("Group 3", $borderColor="#aabbcc", $fontColor="#aabbcc", $borderStyle="dashed", $borderThickness="1") +AddBoundaryTag("Group 3", $borderColor="#aabbcc", $fontColor="#aabbcc", $borderStyle="dashed") Boundary(group_3, "Group 3", $tags="Group 3") { Person(User3, "User 3", $descr="", $tags="", $link="") } diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Components.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Components.puml index 4cb63cacb..ff496fc56 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Components.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Components.puml @@ -11,7 +11,7 @@ top to bottom direction System(C, "C", $descr="", $tags="", $link="") Container_Boundary("D.F_boundary", "F", $tags="") { - AddBoundaryTag("Group 5", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed", $borderThickness="1") + AddBoundaryTag("Group 5", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") Boundary(group_1, "Group 5", $tags="Group 5") { Component(D.F.H, "H", $techn="", $descr="", $tags="", $link="") } diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Containers.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Containers.puml index 76539e198..2f92cc66f 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Containers.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Containers.puml @@ -11,7 +11,7 @@ top to bottom direction System(C, "C", $descr="", $tags="", $link="") System_Boundary("D_boundary", "D", $tags="") { - AddBoundaryTag("Group 4", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed", $borderThickness="1") + AddBoundaryTag("Group 4", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") Boundary(group_1, "Group 4", $tags="Group 4") { Container(D.F, "F", $techn="", $descr="", $tags="", $link="") } diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-SystemLandscape.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-SystemLandscape.puml index 3aee39e27..0e045ac3d 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-SystemLandscape.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-SystemLandscape.puml @@ -7,15 +7,15 @@ top to bottom direction !include !include -AddBoundaryTag("Group 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed", $borderThickness="1") +AddBoundaryTag("Group 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") Boundary(group_1, "Group 1", $tags="Group 1") { System(B, "B", $descr="", $tags="", $link="") } -AddBoundaryTag("Group 2", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed", $borderThickness="1") +AddBoundaryTag("Group 2", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") Boundary(group_2, "Group 2", $tags="Group 2") { System(C, "C", $descr="", $tags="", $link="") - AddBoundaryTag("Group 2/Group 3", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed", $borderThickness="1") + AddBoundaryTag("Group 2/Group 3", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") Boundary(group_3, "Group 3", $tags="Group 2/Group 3") { System(D, "D", $descr="", $tags="", $link="") } diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/nested-groups-with-dot-separator.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/nested-groups-with-dot-separator.puml index a22d44fe2..25c92424c 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/nested-groups-with-dot-separator.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/nested-groups-with-dot-separator.puml @@ -7,11 +7,11 @@ top to bottom direction !include !include -AddBoundaryTag("Organisation 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed", $borderThickness="1") +AddBoundaryTag("Organisation 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") Boundary(group_1, "Organisation 1", $tags="Organisation 1") { - AddBoundaryTag("Organisation 1.Department 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed", $borderThickness="1") + AddBoundaryTag("Organisation 1.Department 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") Boundary(group_2, "Department 1", $tags="Organisation 1.Department 1") { - AddBoundaryTag("Organisation 1.Department 1.Team 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed", $borderThickness="1") + AddBoundaryTag("Organisation 1.Department 1.Team 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") Boundary(group_3, "Team 1", $tags="Organisation 1.Department 1.Team 1") { System(Team1, "Team 1", $descr="", $tags="", $link="") } diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/nested-groups.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/nested-groups.puml index 7ad912df6..17b40ad7c 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/nested-groups.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/nested-groups.puml @@ -7,18 +7,18 @@ top to bottom direction !include !include -AddBoundaryTag("Organisation 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed", $borderThickness="1") +AddBoundaryTag("Organisation 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") Boundary(group_1, "Organisation 1", $tags="Organisation 1") { System(Organisation1, "Organisation 1", $descr="", $tags="", $link="") - AddBoundaryTag("Organisation 1/Department 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed", $borderThickness="1") + AddBoundaryTag("Organisation 1/Department 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") Boundary(group_2, "Department 1", $tags="Organisation 1/Department 1") { System(Department1, "Department 1", $descr="", $tags="", $link="") - AddBoundaryTag("Organisation 1/Department 1/Team 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed", $borderThickness="1") + AddBoundaryTag("Organisation 1/Department 1/Team 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") Boundary(group_3, "Team 1", $tags="Organisation 1/Department 1/Team 1") { System(Team1, "Team 1", $descr="", $tags="", $link="") } - AddBoundaryTag("Organisation 1/Department 1/Team 2", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed", $borderThickness="1") + AddBoundaryTag("Organisation 1/Department 1/Team 2", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") Boundary(group_4, "Team 2", $tags="Organisation 1/Department 1/Team 2") { System(Team2, "Team 2", $descr="", $tags="", $link="") } @@ -27,7 +27,7 @@ Boundary(group_1, "Organisation 1", $tags="Organisation 1") { } -AddBoundaryTag("Organisation 2", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed", $borderThickness="1") +AddBoundaryTag("Organisation 2", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") Boundary(group_5, "Organisation 2", $tags="Organisation 2") { System(Organisation2, "Organisation 2", $descr="", $tags="", $link="") } From e509974d61f13385845485a70a479f75f9b93c64 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 30 Apr 2024 15:31:57 +0100 Subject: [PATCH 506/717] Updated to reflect release. --- build.gradle | 2 +- changelog.md | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index b877748c2..15453d377 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '2.1.1' + version = '2.1.2' repositories { mavenCentral() diff --git a/changelog.md b/changelog.md index a28edc075..739b77a11 100644 --- a/changelog.md +++ b/changelog.md @@ -1,8 +1,9 @@ # Changelog -## unreleased +## 2.1.2 (30th April 2024) -- structurizr-export: https://github.com/structurizr/java/issues/263 (Fixes C4PlantUMLExporter not following C4-PlantUML best practices with c4plantuml.tags true) +- structurizr-core: Adds better backwards compatibility to deal with old workspaces and those created by third party tooling that are missing view `order` property on views. +- structurizr-export: Fixes https://github.com/structurizr/java/issues/263 (C4PlantUMLExporter not following C4-PlantUML best practices with c4plantuml.tags true). ## 2.1.1 (3rd March 2024) From f5780f2e0c2a95cec314771bd5d8baf022f129b3 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 30 May 2024 10:49:01 +0100 Subject: [PATCH 507/717] Fixes #298 --- changelog.md | 4 ++++ .../src/main/java/com/structurizr/view/Terminology.java | 3 +++ .../src/test/java/com/structurizr/view/TerminologyTests.java | 2 ++ 3 files changed, 9 insertions(+) diff --git a/changelog.md b/changelog.md index 739b77a11..8c1760d4e 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,9 @@ # Changelog +## unreleased + +- structurizr-core: Fixes https://github.com/structurizr/java/issues/298 (Unknown model item type on 'element') + ## 2.1.2 (30th April 2024) - structurizr-core: Adds better backwards compatibility to deal with old workspaces and those created by third party tooling that are missing view `order` property on views. diff --git a/structurizr-core/src/main/java/com/structurizr/view/Terminology.java b/structurizr-core/src/main/java/com/structurizr/view/Terminology.java index b8daf3e1e..575a16a5e 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/Terminology.java +++ b/structurizr-core/src/main/java/com/structurizr/view/Terminology.java @@ -134,6 +134,9 @@ public String findTerminology(ModelItem modelItem) { return !StringUtils.isNullOrEmpty(getDeploymentNode()) ? getDeploymentNode() : "Deployment Node"; } else if (modelItem instanceof InfrastructureNode) { return !StringUtils.isNullOrEmpty(getInfrastructureNode()) ? getInfrastructureNode() : "Infrastructure Node"; + } else if (modelItem instanceof CustomElement) { + String terminology = ((CustomElement)modelItem).getMetadata(); + return !StringUtils.isNullOrEmpty(terminology) ? terminology : "Element"; } throw new IllegalArgumentException("Unknown model item type."); diff --git a/structurizr-core/src/test/java/com/structurizr/view/TerminologyTests.java b/structurizr-core/src/test/java/com/structurizr/view/TerminologyTests.java index 7f19c8959..316a23bb7 100644 --- a/structurizr-core/src/test/java/com/structurizr/view/TerminologyTests.java +++ b/structurizr-core/src/test/java/com/structurizr/view/TerminologyTests.java @@ -12,6 +12,7 @@ public class TerminologyTests { void findTerminology() { Workspace workspace = new Workspace("Name", "Description"); Terminology terminology = workspace.getViews().getConfiguration().getTerminology(); + CustomElement element = workspace.getModel().addCustomElement("Element", "Hardware Device", "Description"); Person person = workspace.getModel().addPerson("Name"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name"); Container container = softwareSystem.addContainer("Container"); @@ -22,6 +23,7 @@ void findTerminology() { ContainerInstance containerInstance = deploymentNode.add(container); Relationship relationship = person.uses(softwareSystem, "Uses"); + assertEquals("Hardware Device", terminology.findTerminology(element)); assertEquals("Person", terminology.findTerminology(person)); assertEquals("Software System", terminology.findTerminology(softwareSystem)); assertEquals("Container", terminology.findTerminology(container)); From 746020021b10b2ae429a457491e4827c46f70561 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 16 Jun 2024 13:51:47 +0100 Subject: [PATCH 508/717] Updated to reflect release. --- build.gradle | 2 +- changelog.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 15453d377..8420a6958 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '2.1.2' + version = '2.1.3' repositories { mavenCentral() diff --git a/changelog.md b/changelog.md index 8c1760d4e..f5366e144 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,6 @@ # Changelog -## unreleased +## 2.1.3 (16th June 2024) - structurizr-core: Fixes https://github.com/structurizr/java/issues/298 (Unknown model item type on 'element') From b4211a9674fdf681f87fca2584d5facd57c7dedf Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 18 Jun 2024 14:25:28 +0100 Subject: [PATCH 509/717] Fixes https://github.com/structurizr/java/issues/306 --- build.gradle | 2 +- changelog.md | 7 ++++++- .../main/java/com/structurizr/model/Model.java | 5 +---- .../test/java/com/structurizr/WorkspaceTests.java | 15 +++++++++++++++ 4 files changed, 23 insertions(+), 6 deletions(-) diff --git a/build.gradle b/build.gradle index 8420a6958..8588325a7 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '2.1.3' + version = '2.1.4' repositories { mavenCentral() diff --git a/changelog.md b/changelog.md index f5366e144..82310e5f6 100644 --- a/changelog.md +++ b/changelog.md @@ -1,8 +1,13 @@ # Changelog + +## 2.1.4 (18th June 2024) + +- structurizr-core: Fixes https://github.com/structurizr/java/issues/306 (Workspace.trim() is not correctly removing relationships when the destination of a relationship is removed from the workspace). + ## 2.1.3 (16th June 2024) -- structurizr-core: Fixes https://github.com/structurizr/java/issues/298 (Unknown model item type on 'element') +- structurizr-core: Fixes https://github.com/structurizr/java/issues/298 (Unknown model item type on 'element'). ## 2.1.2 (30th April 2024) diff --git a/structurizr-core/src/main/java/com/structurizr/model/Model.java b/structurizr-core/src/main/java/com/structurizr/model/Model.java index 3e49dd9e0..25d6c9d2d 100644 --- a/structurizr-core/src/main/java/com/structurizr/model/Model.java +++ b/structurizr-core/src/main/java/com/structurizr/model/Model.java @@ -1149,12 +1149,9 @@ private void removeElement(Element element) { // remove any relationships to/from the element for (Relationship relationship : getRelationships()) { - if (relationship.getSource() == element) { + if (relationship.getSource() == element || relationship.getDestination() == element) { removeRelationshipFromInternalStructures(relationship); relationship.getSource().remove(relationship); - } else if (relationship.getDestination() == element) { - removeRelationshipFromInternalStructures(relationship); - relationship.getDestination().remove(relationship); } } diff --git a/structurizr-core/src/test/java/com/structurizr/WorkspaceTests.java b/structurizr-core/src/test/java/com/structurizr/WorkspaceTests.java index b5b741da1..c22b582be 100644 --- a/structurizr-core/src/test/java/com/structurizr/WorkspaceTests.java +++ b/structurizr-core/src/test/java/com/structurizr/WorkspaceTests.java @@ -285,4 +285,19 @@ void trim_WhenSomeElementsAreUnused() { assertTrue(workspace.getModel().contains(bc)); } + @Test + void trim_WhenTheDestinationOfAnElementIsRemoved() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem a = workspace.getModel().addSoftwareSystem("A"); + SoftwareSystem b = workspace.getModel().addSoftwareSystem("B"); + a.uses(b, "Uses"); + + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "Description"); + view.add(a); + + workspace.trim(); + + assertEquals(0, a.getRelationships().size()); + } + } \ No newline at end of file From d455e142aa6d3a429abc6e9c9aba69ebd6b6612a Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 27 Jun 2024 18:35:44 +0100 Subject: [PATCH 510/717] Adds support for element/relationship property expressions (closes #297). --- build.gradle | 2 +- changelog.md | 3 + .../java/com/structurizr/model/ModelItem.java | 11 ++ .../dsl/AbstractExpressionParser.java | 42 ++++++++ .../dsl/StructurizrDslExpressions.java | 2 + .../dsl/AbstractExpressionParserTests.java | 102 ++++++++++-------- .../com/structurizr/dsl/AbstractTests.java | 4 +- 7 files changed, 119 insertions(+), 47 deletions(-) diff --git a/build.gradle b/build.gradle index 8588325a7..2913f3c76 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '2.1.4' + version = '2.2.0' repositories { mavenCentral() diff --git a/changelog.md b/changelog.md index 82310e5f6..e547a12e5 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,8 @@ # Changelog +## 2.2.0 (unreleased) + +- structurizr-dsl: Adds support for element/relationship property expressions (https://github.com/structurizr/java/issues/297). ## 2.1.4 (18th June 2024) diff --git a/structurizr-core/src/main/java/com/structurizr/model/ModelItem.java b/structurizr-core/src/main/java/com/structurizr/model/ModelItem.java index 7f936d238..3047740ec 100644 --- a/structurizr-core/src/main/java/com/structurizr/model/ModelItem.java +++ b/structurizr-core/src/main/java/com/structurizr/model/ModelItem.java @@ -104,6 +104,17 @@ public boolean hasTag(String tag) { return getTagsAsSet().contains(tag.trim()); } + /** + * Determines whether this model item has the given property with the given value. + * + * @param name the name of the property + * @param value the value of the property + * @return true if the named property is present with the given value, false otherwise + */ + public boolean hasProperty(String name, String value) { + return getProperties().containsKey(name) && getProperties().get(name).equals(value); + } + /** * Gets the URL where more information about this item can be found. * diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractExpressionParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractExpressionParser.java index 07f7a36c1..181d51db8 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractExpressionParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractExpressionParser.java @@ -24,11 +24,13 @@ static boolean isExpression(String token) { token.startsWith(ELEMENT_TYPE_EQUALS_EXPRESSION.toLowerCase()) || token.startsWith(ELEMENT_TAG_EQUALS_EXPRESSION.toLowerCase()) || token.startsWith(ELEMENT_TAG_NOT_EQUALS_EXPRESSION.toLowerCase()) || + token.matches(ELEMENT_PROPERTY_EQUALS_EXPRESSION) || token.startsWith(ELEMENT_PARENT_EQUALS_EXPRESSION.toLowerCase()) || token.startsWith(RELATIONSHIP) || token.endsWith(RELATIONSHIP) || token.contains(RELATIONSHIP) || token.startsWith(ELEMENT_EQUALS_EXPRESSION) || token.startsWith(RELATIONSHIP_TAG_EQUALS_EXPRESSION.toLowerCase()) || token.startsWith(RELATIONSHIP_TAG_NOT_EQUALS_EXPRESSION.toLowerCase()) || + token.matches(RELATIONSHIP_PROPERTY_EQUALS_EXPRESSION) || token.startsWith(RELATIONSHIP_SOURCE_EQUALS_EXPRESSION.toLowerCase()) || token.startsWith(RELATIONSHIP_DESTINATION_EQUALS_EXPRESSION.toLowerCase()) || token.startsWith(RELATIONSHIP_EQUALS_EXPRESSION); @@ -169,6 +171,15 @@ private Set evaluateExpression(String expr, DslContext context) { modelItems.add(element); } }); + } else if (expr.matches(ELEMENT_PROPERTY_EQUALS_EXPRESSION)) { + String propertyName = expr.substring(expr.indexOf("[")+1, expr.indexOf("]")); + String propertyValue = expr.substring(expr.indexOf("==")+2); + + context.getWorkspace().getModel().getElements().forEach(element -> { + if (hasProperty(element, propertyName, propertyValue)) { + modelItems.add(element); + } + }); } else if (expr.startsWith(RELATIONSHIP_TAG_EQUALS_EXPRESSION)) { String[] tags = expr.substring(RELATIONSHIP_TAG_EQUALS_EXPRESSION.length()).split(","); context.getWorkspace().getModel().getRelationships().forEach(relationship -> { @@ -183,6 +194,15 @@ private Set evaluateExpression(String expr, DslContext context) { modelItems.add(relationship); } }); + } else if (expr.matches(RELATIONSHIP_PROPERTY_EQUALS_EXPRESSION)) { + String propertyName = expr.substring(expr.indexOf("[")+1, expr.indexOf("]")); + String propertyValue = expr.substring(expr.indexOf("==")+2); + + context.getWorkspace().getModel().getRelationships().forEach(relationship -> { + if (hasProperty(relationship, propertyName, propertyValue)) { + modelItems.add(relationship); + } + }); } else if (expr.startsWith(RELATIONSHIP_SOURCE_EQUALS_EXPRESSION)) { String identifier = expr.substring(RELATIONSHIP_SOURCE_EQUALS_EXPRESSION.length()); Set sourceElements = new HashSet<>(); @@ -276,6 +296,28 @@ private boolean hasAllTags(ModelItem modelItem, String[] tags) { return result; } + private boolean hasProperty(ModelItem modelItem, String name, String value) { + boolean result = modelItem.hasProperty(name, value); + + if (!result) { + // perhaps the property is instead on a related model item? + if (modelItem instanceof StaticStructureElementInstance) { + StaticStructureElementInstance elementInstance = (StaticStructureElementInstance)modelItem; + result = elementInstance.getElement().hasProperty(name, value); + } else if (modelItem instanceof Relationship) { + Relationship relationship = (Relationship)modelItem; + if (!StringUtils.isNullOrEmpty(relationship.getLinkedRelationshipId())) { + Relationship linkedRelationship = relationship.getModel().getRelationship(relationship.getLinkedRelationshipId()); + if (linkedRelationship != null) { + result = linkedRelationship.hasProperty(name, value); + } + } + } + } + + return result; + } + protected abstract Set findAfferentCouplings(Element element); protected Set findAfferentCouplings(Element element, Class typeOfElement) { diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslExpressions.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslExpressions.java index 8b205bf28..4626e5934 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslExpressions.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslExpressions.java @@ -5,12 +5,14 @@ class StructurizrDslExpressions { static final String ELEMENT_TYPE_EQUALS_EXPRESSION = "element.type=="; static final String ELEMENT_TAG_EQUALS_EXPRESSION = "element.tag=="; static final String ELEMENT_TAG_NOT_EQUALS_EXPRESSION = "element.tag!="; + static final String ELEMENT_PROPERTY_EQUALS_EXPRESSION = "element\\.properties\\[.*]==.*"; static final String ELEMENT_EQUALS_EXPRESSION = "element=="; static final String ELEMENT_PARENT_EQUALS_EXPRESSION = "element.parent=="; static final String RELATIONSHIP_TAG_EQUALS_EXPRESSION = "relationship.tag=="; static final String RELATIONSHIP_TAG_NOT_EQUALS_EXPRESSION = "relationship.tag!="; + static final String RELATIONSHIP_PROPERTY_EQUALS_EXPRESSION = "relationship\\.properties\\[.*]==.*"; static final String RELATIONSHIP_SOURCE_EQUALS_EXPRESSION = "relationship.source=="; static final String RELATIONSHIP_DESTINATION_EQUALS_EXPRESSION = "relationship.destination=="; diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/AbstractExpressionParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/AbstractExpressionParserTests.java index 7a81051ce..32bf6fdfb 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/AbstractExpressionParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/AbstractExpressionParserTests.java @@ -12,7 +12,7 @@ class AbstractExpressionParserTests extends AbstractTests { - private StaticViewExpressionParser parser = new StaticViewExpressionParser(); + private final StaticViewExpressionParser parser = new StaticViewExpressionParser(); @Test void test_parseExpression_ThrowsAnException_WhenTheRelationshipSourceIsSpecifiedUsingLongSyntaxButDoesNotExist() { @@ -385,35 +385,36 @@ void test_parseExpression_ReturnsAllRelationships_WhenUsingTheWildcardRelationsh } @Test - void test_parseExpression_ReturnsElements_WhenUsingAnElementTagExpression() { + void test_parseExpression_ReturnsElementsAndElementInstances_WhenUsingAnElementTagEqualsExpression() { model.addPerson("User"); - SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); - - SystemLandscapeView view = views.createSystemLandscapeView("key", "Description"); - SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); - context.setWorkspace(workspace); - context.setIdentifierRegister(new IdentifiersRegister()); + SoftwareSystem ss = model.addSoftwareSystem("Software System"); + SoftwareSystemInstance ssi = model.addDeploymentNode("DN").add(ss); - Set elements = parser.parseExpression("element.tag==Software System", context); - assertEquals(1, elements.size()); - assertTrue(elements.contains(softwareSystem)); + Set elements = parser.parseExpression("element.tag==Software System", context()); + assertEquals(2, elements.size()); + assertTrue(elements.contains(ss)); // this is tagged "Software System" + assertTrue(elements.contains(ssi)); // this is not tagged "Software System", but the element it's based upon is } @Test - void test_parseExpression_ReturnsElementInstances_WhenUsingAnElementTagExpression() { - model.addPerson("User"); - SoftwareSystem ss = model.addSoftwareSystem("Software System"); - SoftwareSystemInstance ssi = model.addDeploymentNode("DN").add(ss); + void test_parseExpression_ReturnsElementsAndElementInstances_WhenUsingAnElementPropertyEqualsExpression() { + SoftwareSystem a = model.addSoftwareSystem("A"); + a.addProperty("Technical Debt", "Low"); + SoftwareSystem b = model.addSoftwareSystem("B"); + b.addProperty("Technical Debt", "Medium"); + SoftwareSystem c = model.addSoftwareSystem("C"); + c.addProperty("Technical Debt", "High"); + SoftwareSystem d = model.addSoftwareSystem("D"); - DeploymentView view = views.createDeploymentView("key", "Description"); - DeploymentViewDslContext context = new DeploymentViewDslContext(view); - context.setWorkspace(workspace); - context.setIdentifierRegister(new IdentifiersRegister()); + DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node"); + SoftwareSystemInstance ai = deploymentNode.add(a); + SoftwareSystemInstance bi = deploymentNode.add(b); + SoftwareSystemInstance ci = deploymentNode.add(c); - Set elements = parser.parseExpression("element.tag==Software System", context); + Set elements = parser.parseExpression("element.properties[Technical Debt]==High", context()); assertEquals(2, elements.size()); - assertTrue(elements.contains(ss)); // this is tagged "Software System" - assertTrue(elements.contains(ssi)); // this is not tagged "Software System", but the element it's based upon is + assertTrue(elements.contains(c)); // this has the property + assertTrue(elements.contains(ci)); // this doesn't have the property, but the element it's based upon does } @Test @@ -465,42 +466,53 @@ void test_parseExpression_ReturnsElements_WhenUsingAnElementParentExpression() { } @Test - void test_parseExpression_ReturnsRelationships_WhenUsingARelationshipTagExpression() { + void test_parseExpression_ReturnsRelationshipsAndImpliedRelationships_WhenUsingARelationshipTagEqualsExpression() { + Person user = model.addPerson("User"); SoftwareSystem a = model.addSoftwareSystem("A"); + Container aa = a.addContainer("AA"); SoftwareSystem b = model.addSoftwareSystem("B"); - Relationship r = a.uses(b, "Uses"); - r.addTags("Tag 1"); + Container bb = b.addContainer("BB"); + SoftwareSystem c = model.addSoftwareSystem("C"); + Container cc = c.addContainer("CC"); - SystemLandscapeView view = views.createSystemLandscapeView("key", "Description"); - SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); - context.setWorkspace(workspace); - context.setIdentifierRegister(new IdentifiersRegister()); + Relationship r1 = user.uses(aa, "Uses"); + r1.addTags("Tag 1"); + Relationship r2 = user.uses(bb, "Uses"); + r2.addTags("Tag 2"); + Relationship r3 = user.uses(cc, "Uses"); - Set relationships = parser.parseExpression("relationship.tag==Tag 1", context); - assertEquals(1, relationships.size()); - assertTrue(relationships.contains(r)); + Set relationships = parser.parseExpression("relationship.tag==Tag 1", context()); + assertEquals(2, relationships.size()); + assertTrue(relationships.contains(r1)); + + Relationship impliedRelationship = user.getEfferentRelationshipWith(a); + assertTrue(relationships.contains(impliedRelationship)); } @Test - void test_parseExpression_ReturnsRelationships_WhenUsingARelationshipTagExpressionAndTheTagIsSetOnTheLinkedRelationship() { + void test_parseExpression_ReturnsRelationshipsAndImpliedRelationships_WhenUsingARelationshipPropertyEqualsExpression() { + Person user = model.addPerson("User"); SoftwareSystem a = model.addSoftwareSystem("A"); + Container aa = a.addContainer("AA"); SoftwareSystem b = model.addSoftwareSystem("B"); - Relationship r = a.uses(b, "Uses"); - r.addTags("Tag 1"); + Container bb = b.addContainer("BB"); + SoftwareSystem c = model.addSoftwareSystem("C"); + Container cc = c.addContainer("CC"); - DeploymentNode dn = model.addDeploymentNode("DN"); - SoftwareSystemInstance ai = dn.add(a); - SoftwareSystemInstance bi = dn.add(b); + Relationship r1 = user.uses(aa, "Uses"); + r1.addProperty("Secure", "Yes"); + Relationship r2 = user.uses(bb, "Uses"); + r2.addProperty("Secure", "No"); + Relationship r3 = user.uses(cc, "Uses"); - DeploymentView view = views.createDeploymentView("key", "Description"); - DeploymentViewDslContext context = new DeploymentViewDslContext(view); - context.setWorkspace(workspace); - context.setIdentifierRegister(new IdentifiersRegister()); + assertEquals(6, model.getRelationships().size()); - Set relationships = parser.parseExpression("relationship.tag==Tag 1", context); + Set relationships = parser.parseExpression("relationship.properties[Secure]==Yes", context()); assertEquals(2, relationships.size()); - assertTrue(relationships.contains(r)); - assertTrue(relationships.contains(ai.getRelationships().iterator().next())); + assertTrue(relationships.contains(r1)); + + Relationship impliedRelationship = user.getEfferentRelationshipWith(a); + assertTrue(relationships.contains(impliedRelationship)); } } \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/AbstractTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/AbstractTests.java index 3408890d4..005d8aca8 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/AbstractTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/AbstractTests.java @@ -14,9 +14,11 @@ abstract class AbstractTests { protected Model model = workspace.getModel(); protected ViewSet views = workspace.getViews(); - protected ModelDslContext context() { + AbstractTests() { model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); + } + protected ModelDslContext context() { ModelDslContext context = new ModelDslContext(); context.setWorkspace(workspace); From 17964c534fe72ec74627d236108e119396a881d6 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 30 Jun 2024 10:27:03 +0100 Subject: [PATCH 511/717] Typo. --- .../src/main/java/com/structurizr/view/ViewSet.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structurizr-core/src/main/java/com/structurizr/view/ViewSet.java b/structurizr-core/src/main/java/com/structurizr/view/ViewSet.java index cab1d8a44..ff395e202 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/ViewSet.java +++ b/structurizr-core/src/main/java/com/structurizr/view/ViewSet.java @@ -54,7 +54,7 @@ public final class ViewSet { } /** - * Creates a custom view view. + * Creates a custom view. * * @param key the key for the view (must be unique) * @param title a title of the view From 1366d3d4615b3b13165b809c2e5b20f784efa920 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 30 Jun 2024 13:58:39 +0100 Subject: [PATCH 512/717] structurizr-dsl: Adds a way to specify the implied relationships strategy by a fully qualified class name via `!impliedRelationships`. --- changelog.md | 1 + .../dsl/ImpliedRelationshipsParser.java | 48 +++++++++++++++---- .../structurizr/dsl/StructurizrDslParser.java | 2 +- .../dsl/ImpliedRelationshipsParserTests.java | 48 ++++++++++++++++--- .../src/test/resources/dsl/test.dsl | 4 ++ 5 files changed, 86 insertions(+), 17 deletions(-) diff --git a/changelog.md b/changelog.md index e547a12e5..fb07a4e31 100644 --- a/changelog.md +++ b/changelog.md @@ -3,6 +3,7 @@ ## 2.2.0 (unreleased) - structurizr-dsl: Adds support for element/relationship property expressions (https://github.com/structurizr/java/issues/297). +- structurizr-dsl: Adds a way to specify the implied relationships strategy by a fully qualified class name via `!impliedRelationships`. ## 2.1.4 (18th June 2024) diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImpliedRelationshipsParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImpliedRelationshipsParser.java index f66785ffe..14e781495 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImpliedRelationshipsParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImpliedRelationshipsParser.java @@ -2,32 +2,60 @@ import com.structurizr.model.CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy; import com.structurizr.model.DefaultImpliedRelationshipsStrategy; +import com.structurizr.model.ImpliedRelationshipsStrategy; -import java.util.ArrayList; -import java.util.List; +import java.io.File; +import java.lang.reflect.Constructor; +import java.util.Set; final class ImpliedRelationshipsParser extends AbstractParser { - private static final String GRAMMAR = "!impliedRelationships "; + private static final String GRAMMAR = "!impliedRelationships "; - private static final int FLAG_INDEX = 1; + private static final Set BUILT_IN_IMPLIED_RELATIONSHIPS_STRATEGIES = Set.of( + "com.structurizr.model.DefaultImpliedRelationshipsStrategy", + "com.structurizr.model.CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy", + "com.structurizr.model.CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategy" + ); + + private static final int OPTION_INDEX = 1; + private static final String TRUE = "true"; private static final String FALSE = "false"; - void parse(DslContext context, Tokens tokens) { - // impliedRelationships + void parse(DslContext context, Tokens tokens, File dslFile, boolean restricted) { + // impliedRelationships - if (tokens.hasMoreThan(FLAG_INDEX)) { + if (tokens.hasMoreThan(OPTION_INDEX)) { throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); } - if (!tokens.includes(FLAG_INDEX)) { + if (!tokens.includes(OPTION_INDEX)) { throw new RuntimeException("Expected: " + GRAMMAR); } - if (tokens.get(FLAG_INDEX).equalsIgnoreCase(FALSE)) { + String option = tokens.get(OPTION_INDEX); + + if (option.equalsIgnoreCase(FALSE)) { context.getWorkspace().getModel().setImpliedRelationshipsStrategy(new DefaultImpliedRelationshipsStrategy()); - } else { + } else if (option.equalsIgnoreCase(TRUE)) { context.getWorkspace().getModel().setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); + } else { + if (restricted) { + if (!BUILT_IN_IMPLIED_RELATIONSHIPS_STRATEGIES.contains(option)) { + throw new RuntimeException("The implied relationships strategy " + option + " is not available when the DSL parser is running in restricted mode"); + } + } + + try { + Class impliedRelationshipsStrategyClass = context.loadClass(option, dslFile); + Constructor constructor = impliedRelationshipsStrategyClass.getDeclaredConstructor(); + ImpliedRelationshipsStrategy impliedRelationshipsStrategy = (ImpliedRelationshipsStrategy)constructor.newInstance(); + context.getWorkspace().getModel().setImpliedRelationshipsStrategy(impliedRelationshipsStrategy); + } catch (ClassNotFoundException cnfe) { + throw new RuntimeException("Error loading implied relationships strategy: " + option + " was not found"); + } catch (Exception e) { + throw new RuntimeException("Error loading implied relationships strategy: " + e.getMessage()); + } } } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index 080f52f23..ca6edc3a6 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -492,7 +492,7 @@ void parse(List lines, File dslFile, boolean include) throws Structurizr startContext(new WorkspaceDslContext()); parsedTokens.add(WORKSPACE_TOKEN); } else if (IMPLIED_RELATIONSHIPS_TOKEN.equalsIgnoreCase(firstToken) || IMPLIED_RELATIONSHIPS_TOKEN.substring(1).equalsIgnoreCase(firstToken)) { - new ImpliedRelationshipsParser().parse(getContext(), tokens); + new ImpliedRelationshipsParser().parse(getContext(), tokens, dslFile, restricted); } else if (NAME_TOKEN.equalsIgnoreCase(firstToken) && inContext(WorkspaceDslContext.class)) { new WorkspaceParser().parseName(getContext(), tokens); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ImpliedRelationshipsParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ImpliedRelationshipsParserTests.java index a8e4e8585..eb7ded0f6 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ImpliedRelationshipsParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ImpliedRelationshipsParserTests.java @@ -2,6 +2,8 @@ import org.junit.jupiter.api.Test; +import java.io.File; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; @@ -12,35 +14,69 @@ class ImpliedRelationshipsParserTests extends AbstractTests { @Test void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { try { - parser.parse(context(), tokens("!impliedRelationships", "boolean", "extra")); + parser.parse(context(), tokens("!impliedRelationships", "boolean", "extra"), null, false); fail(); } catch (Exception e) { - assertEquals("Too many tokens, expected: !impliedRelationships ", e.getMessage()); + assertEquals("Too many tokens, expected: !impliedRelationships ", e.getMessage()); } } @Test void test_parse_ThrowsAnException_WhenNoFlagIsSpecified() { try { - parser.parse(context(), tokens("!impliedRelationships")); + parser.parse(context(), tokens("!impliedRelationships"), null, false); fail(); } catch (Exception e) { - assertEquals("Expected: !impliedRelationships ", e.getMessage()); + assertEquals("Expected: !impliedRelationships ", e.getMessage()); } } @Test void test_parse_SetsTheStrategy_WhenFalseIsSpecified() { - parser.parse(context(), tokens("!impliedRelationships", "false")); + parser.parse(context(), tokens("!impliedRelationships", "false"), null, false); assertEquals("com.structurizr.model.DefaultImpliedRelationshipsStrategy", workspace.getModel().getImpliedRelationshipsStrategy().getClass().getCanonicalName()); } @Test void test_parse_SetsTheStrategy_WhenTrueIsSpecified() { - parser.parse(context(), tokens("!impliedRelationships", "true")); + parser.parse(context(), tokens("!impliedRelationships", "true"), null, false); assertEquals("com.structurizr.model.CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy", workspace.getModel().getImpliedRelationshipsStrategy().getClass().getCanonicalName()); } + @Test + void test_parse_SetsTheStrategy_WhenABuiltInStrategyIsUsedInUnrestrictedMode() { + parser.parse(context(), tokens("!impliedRelationships", "com.structurizr.model.CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy"), new File("."), false); + + assertEquals("com.structurizr.model.CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy", workspace.getModel().getImpliedRelationshipsStrategy().getClass().getCanonicalName()); + } + + @Test + void test_parse_SetsTheStrategy_WhenABuiltInStrategyIsUsedInRestrictedMode() { + parser.parse(context(), tokens("!impliedRelationships", "com.structurizr.model.CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy"), new File("."), true); + + assertEquals("com.structurizr.model.CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy", workspace.getModel().getImpliedRelationshipsStrategy().getClass().getCanonicalName()); + } + + @Test + void test_parse_ThrowsAnException_WhenACustomStrategyIsUsedInRestrictedMode() { + try { + parser.parse(context(), tokens("!impliedRelationships", "com.example.CustomImpliedRelationshipsStrategy"), new File("."), true); + fail(); + } catch (Exception e) { + assertEquals("The implied relationships strategy com.example.CustomImpliedRelationshipsStrategy is not available when the DSL parser is running in restricted mode", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenACustomStrategyIsUsedInUnrestrictedModeButCannotBeLoaded() { + try { + parser.parse(context(), tokens("!impliedRelationships", "com.example.CustomImpliedRelationshipsStrategy"), new File("."), false); + fail(); + } catch (Exception e) { + assertEquals("Error loading implied relationships strategy: com.example.CustomImpliedRelationshipsStrategy was not found", e.getMessage()); + } + } + } \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/test.dsl b/structurizr-dsl/src/test/resources/dsl/test.dsl index 3fe2f64d1..26fb5761c 100644 --- a/structurizr-dsl/src/test/resources/dsl/test.dsl +++ b/structurizr-dsl/src/test/resources/dsl/test.dsl @@ -23,6 +23,10 @@ workspace "Name" "Description" { // single line comment model { + !impliedRelationships false + !impliedRelationships "com.structurizr.model.CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategy" + !impliedRelationships true + properties { "Name" "Value" } From e0440ddd04361a4782d55cdd7f9236a15c75c5f3 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 2 Jul 2024 09:29:24 +0100 Subject: [PATCH 513/717] Adds the ability to include single files as documentation (closes #303). --- changelog.md | 1 + .../java/com/structurizr/dsl/DocsParser.java | 6 +--- .../java/com/structurizr/dsl/DslTests.java | 33 +++++++++++++++++-- .../resources/dsl/docs/docs/01-context.md | 5 --- .../dsl/docs/docs/softwaresystem/1.md | 3 ++ .../docs/docs/softwaresystem/container/1.md | 3 ++ .../softwaresystem/container/component/1.md | 3 ++ .../resources/dsl/docs/docs/workspace/1.md | 3 ++ .../src/test/resources/dsl/docs/workspace.dsl | 8 ++--- 9 files changed, 48 insertions(+), 17 deletions(-) delete mode 100644 structurizr-dsl/src/test/resources/dsl/docs/docs/01-context.md create mode 100644 structurizr-dsl/src/test/resources/dsl/docs/docs/softwaresystem/1.md create mode 100644 structurizr-dsl/src/test/resources/dsl/docs/docs/softwaresystem/container/1.md create mode 100644 structurizr-dsl/src/test/resources/dsl/docs/docs/softwaresystem/container/component/1.md create mode 100644 structurizr-dsl/src/test/resources/dsl/docs/docs/workspace/1.md diff --git a/changelog.md b/changelog.md index fb07a4e31..9bd3d7285 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,7 @@ - structurizr-dsl: Adds support for element/relationship property expressions (https://github.com/structurizr/java/issues/297). - structurizr-dsl: Adds a way to specify the implied relationships strategy by a fully qualified class name via `!impliedRelationships`. +- structurizr-dsl: Adds the ability to include single files as documentation (https://github.com/structurizr/java/issues/303). ## 2.1.4 (18th June 2024) diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DocsParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DocsParser.java index 4eaba7a23..c758781f9 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/DocsParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DocsParser.java @@ -55,17 +55,13 @@ private void parse(DslContext context, Documentable documentable, File dslFile, throw new RuntimeException("Documentation path " + path + " does not exist"); } - if (!path.isDirectory()) { - throw new RuntimeException("Documentation path " + path + " is not a directory"); - } - try { Class documentationImporterClass = context.loadClass(fullyQualifiedClassName, dslFile); Constructor constructor = documentationImporterClass.getDeclaredConstructor(); DocumentationImporter documentationImporter = (DocumentationImporter)constructor.newInstance(); documentationImporter.importDocumentation(documentable, path); - if (!tokens.includes(FQN_INDEX)) { + if (!tokens.includes(FQN_INDEX) && path.isDirectory()) { DefaultImageImporter imageImporter = new DefaultImageImporter(); imageImporter.importDocumentation(documentable, path); } diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index e8f7fa5c1..45579713f 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -1,6 +1,7 @@ package com.structurizr.dsl; import com.structurizr.Workspace; +import com.structurizr.documentation.Section; import com.structurizr.model.*; import com.structurizr.view.*; import org.junit.jupiter.api.Test; @@ -12,6 +13,7 @@ import java.nio.file.Files; import java.util.ArrayList; import java.util.Base64; +import java.util.Collection; import java.util.List; import static org.junit.jupiter.api.Assertions.*; @@ -692,9 +694,34 @@ void test_docs() throws Exception { Container container = softwareSystem.getContainerWithName("Container"); Component component = container.getComponentWithName("Component"); - assertEquals(1, parser.getWorkspace().getDocumentation().getSections().size()); - assertEquals(1, softwareSystem.getDocumentation().getSections().size()); - assertEquals(1, container.getDocumentation().getSections().size()); + Collection
    sections = parser.getWorkspace().getDocumentation().getSections(); + assertEquals(1, sections.size()); + assertEquals(""" + ## Workspace + + Content...""", sections.iterator().next().getContent()); + + sections = softwareSystem.getDocumentation().getSections(); + assertEquals(1, sections.size()); + assertEquals(""" + ## Software System + + Content...""", sections.iterator().next().getContent()); + + sections = container.getDocumentation().getSections(); + assertEquals(1, sections.size()); + assertEquals(""" + ## Container + + Content...""", sections.iterator().next().getContent()); + + sections = component.getDocumentation().getSections(); + assertEquals(1, sections.size()); + assertEquals(""" + ## Component + + Content...""", sections.iterator().next().getContent()); + assertEquals(1, component.getDocumentation().getSections().size()); } diff --git a/structurizr-dsl/src/test/resources/dsl/docs/docs/01-context.md b/structurizr-dsl/src/test/resources/dsl/docs/docs/01-context.md deleted file mode 100644 index ddb90b8f7..000000000 --- a/structurizr-dsl/src/test/resources/dsl/docs/docs/01-context.md +++ /dev/null @@ -1,5 +0,0 @@ -## Context - -Here is a description of my software system... - -![](embed:Diagram1) \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/docs/docs/softwaresystem/1.md b/structurizr-dsl/src/test/resources/dsl/docs/docs/softwaresystem/1.md new file mode 100644 index 000000000..7210af0e9 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/docs/docs/softwaresystem/1.md @@ -0,0 +1,3 @@ +## Software System + +Content... \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/docs/docs/softwaresystem/container/1.md b/structurizr-dsl/src/test/resources/dsl/docs/docs/softwaresystem/container/1.md new file mode 100644 index 000000000..7af941d8d --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/docs/docs/softwaresystem/container/1.md @@ -0,0 +1,3 @@ +## Container + +Content... \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/docs/docs/softwaresystem/container/component/1.md b/structurizr-dsl/src/test/resources/dsl/docs/docs/softwaresystem/container/component/1.md new file mode 100644 index 000000000..4f7859729 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/docs/docs/softwaresystem/container/component/1.md @@ -0,0 +1,3 @@ +## Component + +Content... \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/docs/docs/workspace/1.md b/structurizr-dsl/src/test/resources/dsl/docs/docs/workspace/1.md new file mode 100644 index 000000000..ae8e89c20 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/docs/docs/workspace/1.md @@ -0,0 +1,3 @@ +## Workspace + +Content... \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/docs/workspace.dsl b/structurizr-dsl/src/test/resources/dsl/docs/workspace.dsl index 937977cc4..1f2a383f0 100644 --- a/structurizr-dsl/src/test/resources/dsl/docs/workspace.dsl +++ b/structurizr-dsl/src/test/resources/dsl/docs/workspace.dsl @@ -1,17 +1,17 @@ workspace { - !docs docs com.structurizr.example.ExampleDocumentationImporter + !docs docs/workspace com.structurizr.example.ExampleDocumentationImporter model { user = person "User" softwareSystem = softwareSystem "Software System" { - !docs docs + !docs docs/softwaresystem container "Container" { - !docs docs + !docs docs/softwaresystem/container component "Component" { - !docs docs + !docs docs/softwaresystem/container/component/1.md } } } From 2cc351d061551630c5c7b690081438beeb2f29aa Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 2 Jul 2024 11:03:24 +0100 Subject: [PATCH 514/717] Updated to reflect release. --- changelog.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index 9bd3d7285..f9f5224db 100644 --- a/changelog.md +++ b/changelog.md @@ -1,9 +1,9 @@ # Changelog -## 2.2.0 (unreleased) +## 2.2.0 (2nd July 2024) - structurizr-dsl: Adds support for element/relationship property expressions (https://github.com/structurizr/java/issues/297). -- structurizr-dsl: Adds a way to specify the implied relationships strategy by a fully qualified class name via `!impliedRelationships`. +- structurizr-dsl: Adds a way to specify the implied relationships strategy via a fully qualified class name when using `!impliedRelationships`. - structurizr-dsl: Adds the ability to include single files as documentation (https://github.com/structurizr/java/issues/303). ## 2.1.4 (18th June 2024) From 53519eba7c72114e67abe739086b223e41b281f2 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 19 Jul 2024 11:03:41 +0100 Subject: [PATCH 515/717] Fixes #312. --- changelog.md | 4 ++++ .../java/com/structurizr/dsl/StructurizrDslParser.java | 4 ++++ .../src/test/resources/dsl/workspace-with-bom-model.dsl | 6 ++++++ .../src/test/resources/dsl/workspace-with-bom.dsl | 7 +------ 4 files changed, 15 insertions(+), 6 deletions(-) create mode 100644 structurizr-dsl/src/test/resources/dsl/workspace-with-bom-model.dsl diff --git a/changelog.md b/changelog.md index f9f5224db..6b9a9fcdc 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,9 @@ # Changelog +## unreleased + +- structurizr-dsl: Fixes https://github.com/structurizr/java/issues/312 (!include doesn't work with files encoded as UTF-8 BOM). + ## 2.2.0 (2nd July 2024) - structurizr-dsl: Adds support for element/relationship property expressions (https://github.com/structurizr/java/issues/297). diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index ca6edc3a6..e61738f58 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -248,6 +248,10 @@ void parse(List lines, File dslFile, boolean include) throws Structurizr for (IncludedFile includedFile : context.getFiles()) { List paddedLines = new ArrayList<>(); for (String unpaddedLine : includedFile.getLines()) { + if (unpaddedLine.startsWith(BOM)) { + // this caters for files encoded as "UTF-8 with BOM" + unpaddedLine = unpaddedLine.substring(1); + } paddedLines.add(leadingSpace + unpaddedLine); } diff --git a/structurizr-dsl/src/test/resources/dsl/workspace-with-bom-model.dsl b/structurizr-dsl/src/test/resources/dsl/workspace-with-bom-model.dsl new file mode 100644 index 000000000..e54770cfa --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/workspace-with-bom-model.dsl @@ -0,0 +1,6 @@ + model { + user = person "User" "A user of my software system." + softwareSystem = softwareSystem "Software System" "My software system, code-named \"X\"." + + user -> softwareSystem "Uses" + } \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/workspace-with-bom.dsl b/structurizr-dsl/src/test/resources/dsl/workspace-with-bom.dsl index c5556fd63..b0f7a64b0 100644 --- a/structurizr-dsl/src/test/resources/dsl/workspace-with-bom.dsl +++ b/structurizr-dsl/src/test/resources/dsl/workspace-with-bom.dsl @@ -1,11 +1,6 @@ workspace "Getting Started" "This is a model of my software system." { - model { - user = person "User" "A user of my software system." - softwareSystem = softwareSystem "Software System" "My software system, code-named \"X\"." - - user -> softwareSystem "Uses" - } + !include workspace-with-bom-model.dsl views { systemContext softwareSystem "SystemContext" "An example of a System Context diagram." { From ca88ead1d34f838060655a3d706f3dee78ad5244 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 20 Jul 2024 17:07:39 +0100 Subject: [PATCH 516/717] Adds a way to explicitly specify the order of relationships in dynamic views. --- changelog.md | 1 + .../structurizr/view/RelationshipView.java | 2 +- .../dsl/DynamicViewContentParser.java | 36 ++++++++++++++----- .../main/java/com/structurizr/dsl/Tokens.java | 4 +++ .../java/com/structurizr/dsl/DslTests.java | 16 ++++++--- .../dsl/DynamicViewContentParserTests.java | 4 +-- .../dynamic-view-with-explicit-ordering.dsl | 19 ++++++++++ 7 files changed, 67 insertions(+), 15 deletions(-) create mode 100644 structurizr-dsl/src/test/resources/dsl/dynamic-view-with-explicit-ordering.dsl diff --git a/changelog.md b/changelog.md index 6b9a9fcdc..5363e0137 100644 --- a/changelog.md +++ b/changelog.md @@ -3,6 +3,7 @@ ## unreleased - structurizr-dsl: Fixes https://github.com/structurizr/java/issues/312 (!include doesn't work with files encoded as UTF-8 BOM). +- structurizr-dsl: Adds a way to explicitly specify the order of relationships in dynamic views. ## 2.2.0 (2nd July 2024) diff --git a/structurizr-core/src/main/java/com/structurizr/view/RelationshipView.java b/structurizr-core/src/main/java/com/structurizr/view/RelationshipView.java index 29d96c2e1..97a222738 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/RelationshipView.java +++ b/structurizr-core/src/main/java/com/structurizr/view/RelationshipView.java @@ -132,7 +132,7 @@ public String getOrder() { * * @param order the order, as a String */ - void setOrder(String order) { + public void setOrder(String order) { this.order = order; } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DynamicViewContentParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DynamicViewContentParser.java index e96f6babf..1843c148e 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/DynamicViewContentParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DynamicViewContentParser.java @@ -4,13 +4,16 @@ import com.structurizr.model.Element; import com.structurizr.model.Relationship; import com.structurizr.model.StaticStructureElement; +import com.structurizr.util.StringUtils; import com.structurizr.view.DynamicView; import com.structurizr.view.RelationshipView; final class DynamicViewContentParser extends AbstractParser { - private static final String GRAMMAR_1 = " -> [description] [technology]"; - private static final String GRAMMAR_2 = " [description]"; + private static final String GRAMMAR_1 = "[order:] -> [description] [technology]"; + private static final String GRAMMAR_2 = "[order:] [description]"; + + private static final String ORDER_DELIMITER = ":"; private static final int SOURCE_IDENTIFIER_INDEX = 0; private static final int RELATIONSHIP_TOKEN_INDEX = 1; @@ -22,6 +25,15 @@ final class DynamicViewContentParser extends AbstractParser { RelationshipView parseRelationship(DynamicViewDslContext context, Tokens tokens) { DynamicView view = context.getView(); + RelationshipView relationshipView = null; + String order = null; + + if (tokens.size() > 0 && tokens.get(0).endsWith(ORDER_DELIMITER)) { + // the optional [order:] token + order = tokens.get(0); + order = order.substring(0, order.length()-ORDER_DELIMITER.length()); + tokens.remove(0); + } if (tokens.size() > 1 && StructurizrDslTokens.RELATIONSHIP_TOKEN.equals(tokens.get(RELATIONSHIP_TOKEN_INDEX))) { // -> [description] [technology] @@ -65,16 +77,16 @@ RelationshipView parseRelationship(DynamicViewDslContext context, Tokens tokens) } if (sourceElement instanceof StaticStructureElement && destinationElement instanceof StaticStructureElement) { - return view.add((StaticStructureElement) sourceElement, description, technology, (StaticStructureElement) destinationElement); + relationshipView = view.add((StaticStructureElement) sourceElement, description, technology, (StaticStructureElement) destinationElement); } else if (sourceElement instanceof StaticStructureElement && destinationElement instanceof CustomElement) { - return view.add((StaticStructureElement) sourceElement, description, technology, (CustomElement) destinationElement); + relationshipView = view.add((StaticStructureElement) sourceElement, description, technology, (CustomElement) destinationElement); } else if (sourceElement instanceof CustomElement && destinationElement instanceof StaticStructureElement) { - return view.add((CustomElement) sourceElement, description, technology, (StaticStructureElement) destinationElement); + relationshipView = view.add((CustomElement) sourceElement, description, technology, (StaticStructureElement) destinationElement); } else if (sourceElement instanceof CustomElement && destinationElement instanceof CustomElement) { - return view.add((CustomElement) sourceElement, description, technology, (CustomElement) destinationElement); + relationshipView = view.add((CustomElement) sourceElement, description, technology, (CustomElement) destinationElement); } } else { - // [description] [technology] + // [order] [description] [technology] String relationshipId = tokens.get(RELATIONSHIP_IDENTIFIER_INDEX); Relationship relationship = context.getRelationship(relationshipId); @@ -91,7 +103,15 @@ RelationshipView parseRelationship(DynamicViewDslContext context, Tokens tokens) description = tokens.get(RELATIONSHIP_IDENTIFIER_INDEX+1); } - return view.add(relationship, description); + relationshipView = view.add(relationship, description); + } + + if (relationshipView != null) { + if (!StringUtils.isNullOrEmpty(order)) { + relationshipView.setOrder(order); + } + + return relationshipView; } throw new RuntimeException("The specified relationship could not be added"); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/Tokens.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/Tokens.java index f00c1cfc7..ba337b3bf 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/Tokens.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/Tokens.java @@ -14,6 +14,10 @@ String get(int index) { return tokens.get(index).trim().replaceAll("\\\\\"", "\"").trim().replaceAll("\\\\n", "\n"); } + void remove(int index) { + tokens.remove(index); + } + int size() { return tokens.size(); } diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index 45579713f..e34e718c1 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -11,10 +11,7 @@ import java.io.File; import java.nio.charset.StandardCharsets; import java.nio.file.Files; -import java.util.ArrayList; -import java.util.Base64; -import java.util.Collection; -import java.util.List; +import java.util.*; import static org.junit.jupiter.api.Assertions.*; @@ -866,6 +863,17 @@ void test_dynamicViewWithCustomElements() throws Exception { parser.parse(new File("src/test/resources/dsl/dynamic-view-with-custom-elements.dsl")); } + @Test + void test_dynamicViewWithExplicitOrdering() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/dynamic-view-with-explicit-ordering.dsl")); + DynamicView view = parser.getWorkspace().getViews().getDynamicViews().iterator().next(); + Set relationships = view.getRelationships(); + Iterator it = relationships.iterator(); + assertEquals("2", it.next().getOrder()); + assertEquals("3", it.next().getOrder()); + } + @Test void test_workspaceProperties() throws Exception { StructurizrDslParser parser = new StructurizrDslParser(); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DynamicViewContentParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DynamicViewContentParserTests.java index 70bba7b50..a94047a89 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DynamicViewContentParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DynamicViewContentParserTests.java @@ -19,7 +19,7 @@ void test_parseRelationship_ThrowsAnException_WhenThereAreTooManyTokens() { parser.parseRelationship(new DynamicViewDslContext(null), tokens("source", "->", "destination", "description", "technology", "extra")); fail(); } catch (Exception e) { - assertEquals("Too many tokens, expected: -> [description] [technology]", e.getMessage()); + assertEquals("Too many tokens, expected: [order:] -> [description] [technology]", e.getMessage()); } } @@ -29,7 +29,7 @@ void test_parseRelationship_ThrowsAnException_WhenTheDestinationIdentifierIsMiss parser.parseRelationship(new DynamicViewDslContext(null), tokens("source", "->")); fail(); } catch (Exception e) { - assertEquals("Expected: -> [description] [technology]", e.getMessage()); + assertEquals("Expected: [order:] -> [description] [technology]", e.getMessage()); } } diff --git a/structurizr-dsl/src/test/resources/dsl/dynamic-view-with-explicit-ordering.dsl b/structurizr-dsl/src/test/resources/dsl/dynamic-view-with-explicit-ordering.dsl new file mode 100644 index 000000000..45bed69c8 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/dynamic-view-with-explicit-ordering.dsl @@ -0,0 +1,19 @@ +workspace { + + model { + a = softwareSystem "A" + b = softwareSystem "B" + c = softwareSystem "C" + + a -> b + b -> c + } + + views { + dynamic * { + 2: a -> b + 3: b -> c + } + } + +} \ No newline at end of file From 9986f3023d789a7402a616f51939942f462477cb Mon Sep 17 00:00:00 2001 From: klu2 <172195+klu2@users.noreply.github.com> Date: Fri, 9 Aug 2024 12:15:53 +0200 Subject: [PATCH 517/717] #318 return an immutable map in all implementations of PropertyHolder --- .../src/main/java/com/structurizr/AbstractWorkspace.java | 3 ++- .../src/main/java/com/structurizr/model/Model.java | 2 +- .../src/main/java/com/structurizr/model/ModelItem.java | 2 +- .../src/main/java/com/structurizr/view/AbstractStyle.java | 3 ++- .../src/main/java/com/structurizr/view/Configuration.java | 7 ++----- .../src/main/java/com/structurizr/view/View.java | 3 ++- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/structurizr-core/src/main/java/com/structurizr/AbstractWorkspace.java b/structurizr-core/src/main/java/com/structurizr/AbstractWorkspace.java index 82f750ace..6bb57bbcd 100644 --- a/structurizr-core/src/main/java/com/structurizr/AbstractWorkspace.java +++ b/structurizr-core/src/main/java/com/structurizr/AbstractWorkspace.java @@ -3,6 +3,7 @@ import com.structurizr.configuration.WorkspaceConfiguration; import java.lang.reflect.Constructor; +import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Map; @@ -237,7 +238,7 @@ public void clearConfiguration() { * @return a Map (String, String) (empty if there are no properties) */ public Map getProperties() { - return new HashMap<>(properties); + return Collections.unmodifiableMap(properties); } /** diff --git a/structurizr-core/src/main/java/com/structurizr/model/Model.java b/structurizr-core/src/main/java/com/structurizr/model/Model.java index 25d6c9d2d..6ecb81675 100644 --- a/structurizr-core/src/main/java/com/structurizr/model/Model.java +++ b/structurizr-core/src/main/java/com/structurizr/model/Model.java @@ -1022,7 +1022,7 @@ public void setImpliedRelationshipsStrategy(ImpliedRelationshipsStrategy implied * @return a Map (String, String) (empty if there are no properties) */ public Map getProperties() { - return new HashMap<>(properties); + return Collections.unmodifiableMap(properties); } /** diff --git a/structurizr-core/src/main/java/com/structurizr/model/ModelItem.java b/structurizr-core/src/main/java/com/structurizr/model/ModelItem.java index 3047740ec..398eacdc1 100644 --- a/structurizr-core/src/main/java/com/structurizr/model/ModelItem.java +++ b/structurizr-core/src/main/java/com/structurizr/model/ModelItem.java @@ -152,7 +152,7 @@ public void setUrl(String url) { * @return a Map (String, String) (empty if there are no properties) */ public Map getProperties() { - return new HashMap<>(properties); + return Collections.unmodifiableMap(properties); } /** diff --git a/structurizr-core/src/main/java/com/structurizr/view/AbstractStyle.java b/structurizr-core/src/main/java/com/structurizr/view/AbstractStyle.java index 28ed28f22..2ad6bb291 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/AbstractStyle.java +++ b/structurizr-core/src/main/java/com/structurizr/view/AbstractStyle.java @@ -2,6 +2,7 @@ import com.structurizr.PropertyHolder; +import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -15,7 +16,7 @@ public abstract class AbstractStyle implements PropertyHolder { * @return a Map (String, String) (empty if there are no properties) */ public Map getProperties() { - return new HashMap<>(properties); + return Collections.unmodifiableMap(properties); } /** diff --git a/structurizr-core/src/main/java/com/structurizr/view/Configuration.java b/structurizr-core/src/main/java/com/structurizr/view/Configuration.java index 87780a332..267c79ba3 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/Configuration.java +++ b/structurizr-core/src/main/java/com/structurizr/view/Configuration.java @@ -5,10 +5,7 @@ import com.structurizr.PropertyHolder; import com.structurizr.util.Url; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; /** * Configuration associated with how information in the workspace is rendered. @@ -203,7 +200,7 @@ public void setViewSortOrder(ViewSortOrder viewSortOrder) { * @return a Map (String, String) (empty if there are no properties) */ public Map getProperties() { - return new HashMap<>(properties); + return Collections.unmodifiableMap(properties); } /** diff --git a/structurizr-core/src/main/java/com/structurizr/view/View.java b/structurizr-core/src/main/java/com/structurizr/view/View.java index c34e2bf58..f6ee5539d 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/View.java +++ b/structurizr-core/src/main/java/com/structurizr/view/View.java @@ -5,6 +5,7 @@ import com.structurizr.PropertyHolder; import javax.annotation.Nonnull; +import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -133,7 +134,7 @@ public ViewSet getViewSet() { * @return a Map (String, String) (empty if there are no properties) */ public Map getProperties() { - return new HashMap<>(properties); + return Collections.unmodifiableMap(properties); } /** From ec75900e813105f1417da9a438a3b612560d1e14 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 15 Aug 2024 09:34:44 +0100 Subject: [PATCH 518/717] Adds name-value properties to dynamic view relationship views (https://github.com/structurizr/java/issues/316). --- changelog.md | 2 + .../structurizr/view/RelationshipView.java | 37 ++++++- .../view/RelationshipViewTests.java | 98 +++++++++++++++++++ .../structurizr/dsl/StructurizrDslParser.java | 3 + .../src/test/resources/dsl/test.dsl | 3 + 5 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 structurizr-core/src/test/java/com/structurizr/view/RelationshipViewTests.java diff --git a/changelog.md b/changelog.md index 5363e0137..fd80cc2b1 100644 --- a/changelog.md +++ b/changelog.md @@ -2,6 +2,8 @@ ## unreleased +- structurizr-core: Adds name-value properties to dynamic view relationship views (https://github.com/structurizr/java/issues/316). +- structurizr-dsl: Adds name-value properties to dynamic view relationship views. - structurizr-dsl: Fixes https://github.com/structurizr/java/issues/312 (!include doesn't work with files encoded as UTF-8 BOM). - structurizr-dsl: Adds a way to explicitly specify the order of relationships in dynamic views. diff --git a/structurizr-core/src/main/java/com/structurizr/view/RelationshipView.java b/structurizr-core/src/main/java/com/structurizr/view/RelationshipView.java index 97a222738..9bee72b15 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/RelationshipView.java +++ b/structurizr-core/src/main/java/com/structurizr/view/RelationshipView.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; +import com.structurizr.PropertyHolder; import com.structurizr.model.Relationship; import com.structurizr.util.StringUtils; import com.structurizr.util.Url; @@ -11,7 +12,7 @@ /** * This class represents an instance of a Relationship on a View. */ -public final class RelationshipView implements Comparable { +public final class RelationshipView implements PropertyHolder, Comparable { private static final int START_OF_LINE = 0; private static final int END_OF_LINE = 100; @@ -19,6 +20,7 @@ public final class RelationshipView implements Comparable { private Relationship relationship; private String id; private String description; + private Map properties = new HashMap<>(); private String url; private String order; private Boolean response; @@ -87,6 +89,39 @@ void setDescription(String description) { this.description = description; } + /** + * Gets the collection of name-value property pairs associated with this relationship view, as a Map. + * + * @return a Map (String, String) (empty if there are no properties) + */ + public Map getProperties() { + return Collections.unmodifiableMap(properties); + } + + /** + * Adds a name-value pair property to this relationship view. + * + * @param name the name of the property + * @param value the value of the property + */ + public void addProperty(String name, String value) { + if (name == null || name.trim().length() == 0) { + throw new IllegalArgumentException("A property name must be specified."); + } + + if (value == null || value.trim().length() == 0) { + throw new IllegalArgumentException("A property value must be specified."); + } + + properties.put(name, value); + } + + void setProperties(Map properties) { + if (properties != null) { + this.properties = new HashMap<>(properties); + } + } + /** * Gets the URL where more information about this relationship instance can be found. * diff --git a/structurizr-core/src/test/java/com/structurizr/view/RelationshipViewTests.java b/structurizr-core/src/test/java/com/structurizr/view/RelationshipViewTests.java new file mode 100644 index 000000000..635414970 --- /dev/null +++ b/structurizr-core/src/test/java/com/structurizr/view/RelationshipViewTests.java @@ -0,0 +1,98 @@ +package com.structurizr.view; + +import com.structurizr.AbstractWorkspaceTestBase; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.*; + +public class RelationshipViewTests extends AbstractWorkspaceTestBase { + + @Test + void getProperties_ReturnsAnEmptyMap_WhenNoPropertiesHaveBeenAdded() { + RelationshipView relationshipView = new RelationshipView(); + assertEquals(0, relationshipView.getProperties().size()); + } + + @Test + void getProperties_ReturnsAnUnmodifiableMap() { + RelationshipView relationshipView = new RelationshipView(); + try { + relationshipView.getProperties().put("name", "value"); + fail(); + } catch (Exception e) { + assertTrue(e instanceof UnsupportedOperationException); + } + } + + @Test + void addProperty_ThrowsAnException_WhenTheNameIsNull() { + try { + RelationshipView relationshipView = new RelationshipView(); + relationshipView.addProperty(null, "value"); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("A property name must be specified.", e.getMessage()); + } + } + + @Test + void addProperty_ThrowsAnException_WhenTheNameIsEmpty() { + try { + RelationshipView relationshipView = new RelationshipView(); + relationshipView.addProperty(" ", "value"); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("A property name must be specified.", e.getMessage()); + } + } + + @Test + void addProperty_ThrowsAnException_WhenTheValueIsNull() { + try { + RelationshipView relationshipView = new RelationshipView(); + relationshipView.addProperty("name", null); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("A property value must be specified.", e.getMessage()); + } + } + + @Test + void addProperty_ThrowsAnException_WhenTheValueIsEmpty() { + try { + RelationshipView relationshipView = new RelationshipView(); + relationshipView.addProperty("name", " "); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("A property value must be specified.", e.getMessage()); + } + } + + @Test + void addProperty_AddsTheProperty_WhenANameAndValueAreSpecified() { + RelationshipView relationshipView = new RelationshipView(); + relationshipView.addProperty("name", "value"); + assertEquals("value", relationshipView.getProperties().get("name")); + } + + @Test + void setProperties_DoesNothing_WhenNullIsSpecified() { + RelationshipView relationshipView = new RelationshipView(); + relationshipView.setProperties(null); + assertEquals(0, relationshipView.getProperties().size()); + } + + @Test + void setProperties_SetsTheProperties_WhenANonEmptyMapIsSpecified() { + RelationshipView relationshipView = new RelationshipView(); + Map properties = new HashMap<>(); + properties.put("name", "value"); + relationshipView.setProperties(properties); + assertEquals(1, relationshipView.getProperties().size()); + assertEquals("value", relationshipView.getProperties().get("name")); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index e61738f58..11633d6ec 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -469,6 +469,9 @@ void parse(List lines, File dslFile, boolean include) throws Structurizr } else if (PROPERTIES_TOKEN.equalsIgnoreCase(firstToken) && inContext(ViewDslContext.class)) { startContext(new PropertiesDslContext(getContext(ViewDslContext.class).getView())); + } else if (PROPERTIES_TOKEN.equalsIgnoreCase(firstToken) && inContext(DynamicViewRelationshipContext.class)) { + startContext(new PropertiesDslContext(getContext((DynamicViewRelationshipContext.class)).getRelationshipView())); + } else if (PROPERTIES_TOKEN.equalsIgnoreCase(firstToken) && inContext(ElementStyleDslContext.class)) { startContext(new PropertiesDslContext(getContext((ElementStyleDslContext.class)).getStyle())); diff --git a/structurizr-dsl/src/test/resources/dsl/test.dsl b/structurizr-dsl/src/test/resources/dsl/test.dsl index 26fb5761c..b33be93e0 100644 --- a/structurizr-dsl/src/test/resources/dsl/test.dsl +++ b/structurizr-dsl/src/test/resources/dsl/test.dsl @@ -237,6 +237,9 @@ workspace "Name" "Description" { user -> homePageController "Requests via web browser" homePageController -> user { url "https://structurizr.com" + properties { + "Name" "Value" + } } autoLayout From 51f90413f59747e99fec26d41266f7fe59b7a2ae Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 18 Aug 2024 11:21:02 +0100 Subject: [PATCH 519/717] Initial rewrite of old structurizr-analysis library. --- build.gradle | 2 +- settings.gradle | 5 +- structurizr-component/README.md | 9 ++ structurizr-component/build.gradle | 11 ++ .../component/ComponentFinder.java | 133 ++++++++++++++++++ .../component/ComponentFinderBuilder.java | 68 +++++++++ .../component/ComponentFinderStrategy.java | 54 +++++++ .../ComponentFinderStrategyBuilder.java | 56 ++++++++ .../component/DiscoveredComponent.java | 70 +++++++++ .../java/com/structurizr/component/Type.java | 93 ++++++++++++ .../structurizr/component/TypeRepository.java | 22 +++ .../component/filter/DefaultTypeFilter.java | 14 ++ .../ExcludeAbstractClassTypeFilter.java | 14 ++ .../filter/ExcludeTypesByRegexFilter.java | 27 ++++ .../component/filter/TypeFilter.java | 12 ++ .../matcher/AbstractTypeMatcher.java | 19 +++ .../matcher/AnnotationTypeMatcher.java | 48 +++++++ .../component/matcher/ExtendsTypeMatcher.java | 40 ++++++ .../matcher/ImplementsTypeMatcher.java | 32 +++++ .../matcher/NameSuffixTypeMatcher.java | 27 ++++ .../component/matcher/RegexTypeMatcher.java | 43 ++++++ .../component/matcher/TypeMatcher.java | 14 ++ .../CommonsLangCamelCaseNamingStrategy.java | 17 +++ .../naming/DefaultNamingStrategy.java | 14 ++ .../naming/FullyQualifiedNamingStrategy.java | 15 ++ .../component/naming/NamingStrategy.java | 12 ++ .../provider/DirectoryTypeProvider.java | 64 +++++++++ .../provider/JarFileTypeProvider.java | 61 ++++++++ .../provider/JavadocCommentFilter.java | 35 +++++ .../provider/SourceCodeTypeProvider.java | 86 +++++++++++ .../component/provider/TypeProvider.java | 14 ++ ...sInSamePackageSupportingTypesStrategy.java | 22 +++ ...eferencedTypesSupportingTypesStrategy.java | 17 +++ .../DefaultSupportingTypesStrategy.java | 18 +++ .../supporting/SupportingTypesStrategy.java | 14 ++ .../AbstractWorkspaceTestBase.java | 12 ++ .../component/example/Example.java | 50 +++++++ .../controller/CustomerController.java | 19 +++ .../component/example/domain/Customer.java | 4 + .../repository/CustomerRepository.java | 14 ++ .../repository/CustomerRepositoryImpl.java | 15 ++ .../provider/JavadocCommentFilterTests.java | 60 ++++++++ 42 files changed, 1373 insertions(+), 3 deletions(-) create mode 100644 structurizr-component/README.md create mode 100644 structurizr-component/build.gradle create mode 100644 structurizr-component/src/main/java/com/structurizr/component/ComponentFinder.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/ComponentFinderBuilder.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategy.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategyBuilder.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/DiscoveredComponent.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/Type.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/TypeRepository.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/filter/DefaultTypeFilter.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/filter/ExcludeAbstractClassTypeFilter.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/filter/ExcludeTypesByRegexFilter.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/filter/TypeFilter.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/matcher/AbstractTypeMatcher.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/matcher/AnnotationTypeMatcher.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/matcher/ExtendsTypeMatcher.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/matcher/ImplementsTypeMatcher.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/matcher/NameSuffixTypeMatcher.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/matcher/RegexTypeMatcher.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/matcher/TypeMatcher.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/naming/CommonsLangCamelCaseNamingStrategy.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/naming/DefaultNamingStrategy.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/naming/FullyQualifiedNamingStrategy.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/naming/NamingStrategy.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/provider/DirectoryTypeProvider.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/provider/JarFileTypeProvider.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/provider/JavadocCommentFilter.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/provider/SourceCodeTypeProvider.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/provider/TypeProvider.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/supporting/AllReferencedTypesInSamePackageSupportingTypesStrategy.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/supporting/AllReferencedTypesSupportingTypesStrategy.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/supporting/DefaultSupportingTypesStrategy.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/supporting/SupportingTypesStrategy.java create mode 100644 structurizr-component/src/test/java/com/structurizr/AbstractWorkspaceTestBase.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/example/Example.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/example/controller/CustomerController.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/example/domain/Customer.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/example/repository/CustomerRepository.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/example/repository/CustomerRepositoryImpl.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/provider/JavadocCommentFilterTests.java diff --git a/build.gradle b/build.gradle index 2913f3c76..1f96c1f5e 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '2.2.0' + version = '2.3.0' repositories { mavenCentral() diff --git a/settings.gradle b/settings.gradle index 70e6822a8..57df82f6d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,9 +1,10 @@ rootProject.name = 'structurizr-java' -include 'structurizr-inspection' +include 'structurizr-autolayout' include 'structurizr-client' +include 'structurizr-component' include 'structurizr-core' include 'structurizr-dsl' include 'structurizr-export' -include 'structurizr-autolayout' include 'structurizr-import' +include 'structurizr-inspection' \ No newline at end of file diff --git a/structurizr-component/README.md b/structurizr-component/README.md new file mode 100644 index 000000000..0e319527b --- /dev/null +++ b/structurizr-component/README.md @@ -0,0 +1,9 @@ +# structurizr-component + +[![Maven Central](https://img.shields.io/maven-central/v/com.structurizr/structurizr-component.svg?label=Maven%20Central)](https://search.maven.org/artifact/com.structurizr/structurizr-component) + +This library provides a facility to discover components in a Java codebase, via a combination of +[Apache Commons BCEL](https://commons.apache.org/proper/commons-bcel/) and [JavaParser](https://javaparser.org), +using a pluggable and customisable set of matching and filtering rules. + +__Unreleased, experimental, and potentially subject to change - see tests for an example.__ \ No newline at end of file diff --git a/structurizr-component/build.gradle b/structurizr-component/build.gradle new file mode 100644 index 000000000..3a2fd6c68 --- /dev/null +++ b/structurizr-component/build.gradle @@ -0,0 +1,11 @@ +dependencies { + + api project(':structurizr-core') + implementation 'org.apache.bcel:bcel:6.8.1' + implementation 'com.github.javaparser:javaparser-symbol-solver-core:3.26.1' + + testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2' + +} + +description = 'Component finder for Java code' \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinder.java b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinder.java new file mode 100644 index 000000000..4abd0dc99 --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinder.java @@ -0,0 +1,133 @@ +package com.structurizr.component; + +import com.structurizr.component.provider.TypeProvider; +import com.structurizr.model.Component; +import com.structurizr.model.Container; +import com.structurizr.util.StringUtils; +import org.apache.bcel.Repository; +import org.apache.bcel.classfile.ConstantPool; +import org.apache.bcel.classfile.Method; +import org.apache.bcel.generic.*; + +import java.util.*; + +/** + * Allows you to find components in a Java codebase based upon a set of pluggable and customisable rules. + * Use the {@link ComponentFinderBuilder} to create an instance of this class. + */ +public final class ComponentFinder { + + private static final String COMPONENT_TYPE_PROPERTY_NAME = "component.type"; + private static final String COMPONENT_SOURCE_PROPERTY_NAME = "component.src"; + + private final TypeRepository typeRepository = new TypeRepository(); + private final Container container; + private final List componentFinderStrategies = new ArrayList<>(); + + ComponentFinder(Container container, Collection typeProviders, List componentFinderStrategies) { + if (container == null) { + throw new IllegalArgumentException("A container must be specified."); + } + + this.container = container; + + for (TypeProvider typeProvider : typeProviders) { + Set types = typeProvider.getTypes(); + for (com.structurizr.component.Type type : types) { + if (type.getJavaClass() != null) { + // this is the BCEL identified type + typeRepository.add(type); + } else { + // this is the source code identified type + com.structurizr.component.Type bcelType = typeRepository.getType(type.getFullyQualifiedName()); + if (bcelType != null) { + bcelType.setDescription(type.getDescription()); + bcelType.setSource(type.getSource()); + } + } + } + } + + Repository.clearCache(); + for (com.structurizr.component.Type type : typeRepository.getTypes()) { + if (type.getJavaClass() != null) { + Repository.addClass(type.getJavaClass()); + findDependencies(type); + } + } + + this.componentFinderStrategies.addAll(componentFinderStrategies); + } + + private void findDependencies(com.structurizr.component.Type type) { + ConstantPool cp = type.getJavaClass().getConstantPool(); + ConstantPoolGen cpg = new ConstantPoolGen(cp); + for (Method m : type.getJavaClass().getMethods()) { + MethodGen mg = new MethodGen(m, type.getJavaClass().getClassName(), cpg); + InstructionList il = mg.getInstructionList(); + if (il == null) { + continue; + } + + InstructionHandle[] instructionHandles = il.getInstructionHandles(); + for (InstructionHandle instructionHandle : instructionHandles) { + Instruction instruction = instructionHandle.getInstruction(); + if (!(instruction instanceof InvokeInstruction)) { + continue; + } + + InvokeInstruction invokeInstruction = (InvokeInstruction)instruction; + ReferenceType referenceType = invokeInstruction.getReferenceType(cpg); + if (!(referenceType instanceof ObjectType)) { + continue; + } + + ObjectType objectType = (ObjectType)referenceType; + String referencedClassName = objectType.getClassName(); + com.structurizr.component.Type referencedType = typeRepository.getType(referencedClassName); + if (referencedType != null) { + type.addDependency(referencedType); + } + } + } + } + + /** + * Find components, using all configured rules, in the order they were added. + */ + public void findComponents() { + Set discoveredComponents = new HashSet<>(); + Map componentMap = new HashMap<>(); + + for (ComponentFinderStrategy componentFinderStrategy : componentFinderStrategies) { + discoveredComponents.addAll(componentFinderStrategy.findComponents(typeRepository)); + } + + for (DiscoveredComponent discoveredComponent : discoveredComponents) { + Component component = container.addComponent(discoveredComponent.getName()); + component.addProperty(COMPONENT_TYPE_PROPERTY_NAME, discoveredComponent.getPrimaryType().getFullyQualifiedName()); + if (!StringUtils.isNullOrEmpty(discoveredComponent.getPrimaryType().getSource())) { + component.addProperty(COMPONENT_SOURCE_PROPERTY_NAME, discoveredComponent.getPrimaryType().getSource()); + } + component.setDescription(discoveredComponent.getDescription()); + component.setTechnology(discoveredComponent.getTechnology()); + componentMap.put(discoveredComponent, component); + } + + // find dependencies between all components + for (DiscoveredComponent discoveredComponent : discoveredComponents) { + Set typeDependencies = discoveredComponent.getAllDependencies(); + for (Type typeDependency : typeDependencies) { + for (DiscoveredComponent c : discoveredComponents) { + if (c != discoveredComponent) { + if (c.getAllTypes().contains(typeDependency)) { + Component componentDependency = componentMap.get(c); + componentMap.get(discoveredComponent).uses(componentDependency, ""); + } + } + } + } + } + } + +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderBuilder.java b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderBuilder.java new file mode 100644 index 000000000..f685451b5 --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderBuilder.java @@ -0,0 +1,68 @@ +package com.structurizr.component; + +import com.structurizr.component.provider.DirectoryTypeProvider; +import com.structurizr.component.provider.JarFileTypeProvider; +import com.structurizr.component.provider.SourceCodeTypeProvider; +import com.structurizr.component.provider.TypeProvider; +import com.structurizr.model.Container; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** + * Provides a way to create a {@link ComponentFinder} instance. + */ +public class ComponentFinderBuilder { + + private Container container; + private final List typeProviders = new ArrayList<>(); + private final List componentFinderStrategies = new ArrayList<>(); + + public ComponentFinderBuilder forContainer(Container container) { + this.container = container; + + return this; + } + + public ComponentFinderBuilder fromJarFile(String filename) { + return fromJarFile(new File(filename)); + } + + public ComponentFinderBuilder fromJarFile(File file) { + this.typeProviders.add(new JarFileTypeProvider(file)); + + return this; + } + + public ComponentFinderBuilder fromDirectory(String path) { + return fromDirectory(new File(path)); + } + + public ComponentFinderBuilder fromDirectory(File path) { + this.typeProviders.add(new DirectoryTypeProvider(path)); + + return this; + } + + public ComponentFinderBuilder fromSourceCode(String path) { + return fromSourceCode(new File(path)); + } + + public ComponentFinderBuilder fromSourceCode(File path) { + this.typeProviders.add(new SourceCodeTypeProvider(path)); + + return this; + } + + public ComponentFinderBuilder withStrategy(ComponentFinderStrategy componentFinderStrategy) { + this.componentFinderStrategies.add(componentFinderStrategy); + + return this; + } + + public ComponentFinder build() { + return new ComponentFinder(container, typeProviders, componentFinderStrategies); + } + +} diff --git a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategy.java new file mode 100644 index 000000000..c6273bc91 --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategy.java @@ -0,0 +1,54 @@ +package com.structurizr.component; + +import com.structurizr.component.filter.TypeFilter; +import com.structurizr.component.matcher.TypeMatcher; +import com.structurizr.component.naming.NamingStrategy; +import com.structurizr.component.supporting.SupportingTypesStrategy; + +import java.util.HashSet; +import java.util.Set; + +/** + * A component finder strategy is a wrapper for a combination of the following: + * - {@link TypeMatcher} + * - {@link TypeFilter} + * - {@link SupportingTypesStrategy} + * - {@link NamingStrategy} + * + * Use the {@link ComponentFinderStrategyBuilder} to create an instance of this class. + */ +class ComponentFinderStrategy { + + private final TypeMatcher typeMatcher; + private final TypeFilter typeFilter; + private final SupportingTypesStrategy supportingTypesStrategy; + private final NamingStrategy namingStrategy; + + ComponentFinderStrategy(TypeMatcher typeMatcher, TypeFilter typeFilter, SupportingTypesStrategy supportingTypesStrategy, NamingStrategy namingStrategy) { + this.typeMatcher = typeMatcher; + this.typeFilter = typeFilter; + this.supportingTypesStrategy = supportingTypesStrategy; + this.namingStrategy = namingStrategy; + } + + Set findComponents(TypeRepository typeRepository) { + Set components = new HashSet<>(); + + Set types = typeRepository.getTypes(); + for (Type type : types) { + if (typeMatcher.matches(type) && typeFilter.accept(type)) { + DiscoveredComponent component = new DiscoveredComponent(namingStrategy.nameOf(type), type); + component.setDescription(type.getDescription()); + component.setTechnology(typeMatcher.getTechnology()); + components.add(component); + + // now find supporting types + Set supportingTypes = supportingTypesStrategy.findSupportingTypes(type); + component.addSupportingTypes(supportingTypes); + } + } + + return components; + } + +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategyBuilder.java b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategyBuilder.java new file mode 100644 index 000000000..87719be1a --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategyBuilder.java @@ -0,0 +1,56 @@ +package com.structurizr.component; + +import com.structurizr.component.filter.DefaultTypeFilter; +import com.structurizr.component.filter.TypeFilter; +import com.structurizr.component.matcher.TypeMatcher; +import com.structurizr.component.naming.DefaultNamingStrategy; +import com.structurizr.component.naming.NamingStrategy; +import com.structurizr.component.supporting.DefaultSupportingTypesStrategy; +import com.structurizr.component.supporting.SupportingTypesStrategy; + +/** + * Provides a way to create a {@link ComponentFinderStrategy} instance. + */ +public final class ComponentFinderStrategyBuilder { + + private TypeMatcher typeMatcher; + private TypeFilter typeFilter = new DefaultTypeFilter(); + private SupportingTypesStrategy supportingTypesStrategy = new DefaultSupportingTypesStrategy(); + private NamingStrategy namingStrategy = new DefaultNamingStrategy(); + + public ComponentFinderStrategyBuilder() { + } + + public ComponentFinderStrategyBuilder matchedBy(TypeMatcher typeMatcher) { + this.typeMatcher = typeMatcher; + + return this; + } + + public ComponentFinderStrategyBuilder filteredBy(TypeFilter typeFilter) { + this.typeFilter = typeFilter; + + return this; + } + + public ComponentFinderStrategyBuilder supportedBy(SupportingTypesStrategy supportingTypesStrategy) { + this.supportingTypesStrategy = supportingTypesStrategy; + + return this; + } + + public ComponentFinderStrategyBuilder namedBy(NamingStrategy namingStrategy) { + this.namingStrategy = namingStrategy; + + return this; + } + + public ComponentFinderStrategy build() { + if (typeMatcher == null) { + throw new RuntimeException("A type matcher must be specified"); + } + + return new ComponentFinderStrategy(typeMatcher, typeFilter, supportingTypesStrategy, namingStrategy); + } + +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/DiscoveredComponent.java b/structurizr-component/src/main/java/com/structurizr/component/DiscoveredComponent.java new file mode 100644 index 000000000..19091412b --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/DiscoveredComponent.java @@ -0,0 +1,70 @@ +package com.structurizr.component; + +import java.util.HashSet; +import java.util.Set; + +final class DiscoveredComponent { + + private final Type primaryType; + private final String name; + private String description; + private String technology; + private final Set supportingTypes = new HashSet<>(); + + DiscoveredComponent(String name, Type primaryType) { + this.name = name; + this.primaryType = primaryType; + } + + void addSupportingTypes(Set types) { + supportingTypes.addAll(types); + } + + Type getPrimaryType() { + return primaryType; + } + + String getName() { + return this.name; + } + + String getDescription() { + return description; + } + + void setDescription(String description) { + this.description = description; + } + + String getTechnology() { + return technology; + } + + void setTechnology(String technology) { + this.technology = technology; + } + + Set getSupportingTypes() { + return new HashSet<>(supportingTypes); + } + + Set getAllTypes() { + Set types = new HashSet<>(); + + types.add(primaryType); + types.addAll(supportingTypes); + + return types; + } + + Set getAllDependencies() { + Set dependencies = new HashSet<>(); + + for (Type type : getAllTypes()) { + dependencies.addAll(type.getDependencies()); + } + + return dependencies; + } + +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/Type.java b/structurizr-component/src/main/java/com/structurizr/component/Type.java new file mode 100644 index 000000000..33a355248 --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/Type.java @@ -0,0 +1,93 @@ +package com.structurizr.component; + +import org.apache.bcel.classfile.JavaClass; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +/** + * Represents a Java type (e.g. class or interface) - it's a wrapper around a BCEL JavaClass. + */ +public final class Type { + + private JavaClass javaClass; + private String description; + private String source; + private final Set dependencies = new HashSet<>(); + + private final String fullyQualifiedName; + + public Type(JavaClass javaClass) { + this(javaClass.getClassName()); + + this.javaClass = javaClass; + } + + public Type(String fullyQualifiedName) { + this.fullyQualifiedName = fullyQualifiedName; + } + + public String getFullyQualifiedName() { + return fullyQualifiedName; + } + + public String getName() { + return fullyQualifiedName.substring(fullyQualifiedName.lastIndexOf(".")+1); + } + + public String getPackageName() { + return getFullyQualifiedName().substring(0, getFullyQualifiedName().lastIndexOf(".")); + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getSource() { + return source; + } + + public void setSource(String source) { + this.source = source; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Type type = (Type) o; + return fullyQualifiedName.equals(type.fullyQualifiedName); + } + + @Override + public int hashCode() { + return Objects.hash(fullyQualifiedName); + } + + public JavaClass getJavaClass() { + return this.javaClass; + } + + public void addDependency(Type type) { + this.dependencies.add(type); + } + + public Set getDependencies() { + return new HashSet<>(dependencies); + } + + public boolean isAbstract() { + return javaClass.isAbstract(); + } + + @Override + public String toString() { + return this.fullyQualifiedName; + } + +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/TypeRepository.java b/structurizr-component/src/main/java/com/structurizr/component/TypeRepository.java new file mode 100644 index 000000000..670e1da44 --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/TypeRepository.java @@ -0,0 +1,22 @@ +package com.structurizr.component; + +import java.util.HashSet; +import java.util.Set; + +final class TypeRepository { + + private final Set types = new HashSet<>(); + + public void add(Type type) { + this.types.add(type); + } + + public Set getTypes() { + return new HashSet<>(types); + } + + Type getType(String fullyQualifiedClassName) { + return types.stream().filter(t -> t.getFullyQualifiedName().equals(fullyQualifiedClassName)).findFirst().orElse(null); + } + +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/filter/DefaultTypeFilter.java b/structurizr-component/src/main/java/com/structurizr/component/filter/DefaultTypeFilter.java new file mode 100644 index 000000000..e7c79d302 --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/filter/DefaultTypeFilter.java @@ -0,0 +1,14 @@ +package com.structurizr.component.filter; + +import com.structurizr.component.Type; + +/** + * A type filter that accepts all types. + */ +public class DefaultTypeFilter implements TypeFilter { + + public boolean accept(Type type) { + return true; + } + +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/filter/ExcludeAbstractClassTypeFilter.java b/structurizr-component/src/main/java/com/structurizr/component/filter/ExcludeAbstractClassTypeFilter.java new file mode 100644 index 000000000..162b99313 --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/filter/ExcludeAbstractClassTypeFilter.java @@ -0,0 +1,14 @@ +package com.structurizr.component.filter; + +import com.structurizr.component.Type; + +/** + * A type filter that excludes abstract types. + */ +public class ExcludeAbstractClassTypeFilter implements TypeFilter { + + public boolean accept(Type type) { + return !type.isAbstract(); + } + +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/filter/ExcludeTypesByRegexFilter.java b/structurizr-component/src/main/java/com/structurizr/component/filter/ExcludeTypesByRegexFilter.java new file mode 100644 index 000000000..497daefd1 --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/filter/ExcludeTypesByRegexFilter.java @@ -0,0 +1,27 @@ +package com.structurizr.component.filter; + +import com.structurizr.component.Type; + +/** + * A type filter that excludes by matching a regex against the fully qualified type name. + */ +public class ExcludeTypesByRegexFilter implements TypeFilter { + + private final String[] regexes; + + public ExcludeTypesByRegexFilter(String... regexes) { + this.regexes = regexes; + } + + @Override + public boolean accept(Type type) { + for (String regex : regexes) { + if (type.getFullyQualifiedName().matches(regex)) { + return false; + } + } + + return true; + } + +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/filter/TypeFilter.java b/structurizr-component/src/main/java/com/structurizr/component/filter/TypeFilter.java new file mode 100644 index 000000000..4d36b407a --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/filter/TypeFilter.java @@ -0,0 +1,12 @@ +package com.structurizr.component.filter; + +import com.structurizr.component.Type; + +/** + * Determines whether a given type should be accepted or not. + */ +public interface TypeFilter { + + boolean accept(Type type); + +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/matcher/AbstractTypeMatcher.java b/structurizr-component/src/main/java/com/structurizr/component/matcher/AbstractTypeMatcher.java new file mode 100644 index 000000000..f2296d5e5 --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/matcher/AbstractTypeMatcher.java @@ -0,0 +1,19 @@ +package com.structurizr.component.matcher; + +/** + * A superclass for TypeMatcher implementations. + */ +public abstract class AbstractTypeMatcher implements TypeMatcher { + + private final String technology; + + public AbstractTypeMatcher(String technology) { + this.technology = technology; + } + + @Override + public String getTechnology() { + return technology; + } + +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/matcher/AnnotationTypeMatcher.java b/structurizr-component/src/main/java/com/structurizr/component/matcher/AnnotationTypeMatcher.java new file mode 100644 index 000000000..f3eda8594 --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/matcher/AnnotationTypeMatcher.java @@ -0,0 +1,48 @@ +package com.structurizr.component.matcher; + +import com.structurizr.component.Type; +import com.structurizr.util.StringUtils; +import org.apache.bcel.classfile.AnnotationEntry; + +import java.lang.annotation.Annotation; + +/** + * Matches types based upon the presence of a type-level annotation. + */ +public class AnnotationTypeMatcher extends AbstractTypeMatcher { + + private final String annotationType; + + public AnnotationTypeMatcher(String annotationType, String technology) { + super(technology); + + if (StringUtils.isNullOrEmpty(annotationType)) { + throw new IllegalArgumentException("An annotation type must be supplied"); + } + + this.annotationType = "L" + annotationType.replace(".", "/") + ";"; + } + + public AnnotationTypeMatcher(Class annotation, String technology) { + super(technology); + + if (annotation == null) { + throw new IllegalArgumentException("An annotation must be supplied"); + } + + this.annotationType = "L" + annotation.getCanonicalName().replace(".", "/") + ";"; + } + + @Override + public boolean matches(Type type) { + AnnotationEntry[] annotationEntries = type.getJavaClass().getAnnotationEntries(); + for (AnnotationEntry annotationEntry : annotationEntries) { + if (annotationType.equals(annotationEntry.getAnnotationType())) { + return true; + } + } + + return false; + } + +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/matcher/ExtendsTypeMatcher.java b/structurizr-component/src/main/java/com/structurizr/component/matcher/ExtendsTypeMatcher.java new file mode 100644 index 000000000..bfff7159a --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/matcher/ExtendsTypeMatcher.java @@ -0,0 +1,40 @@ +package com.structurizr.component.matcher; + +import com.structurizr.component.Type; +import org.apache.bcel.classfile.JavaClass; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Matches types where the type extends the specified class. + */ +public class ExtendsTypeMatcher extends AbstractTypeMatcher { + + private static final Log log = LogFactory.getLog(ExtendsTypeMatcher.class); + + private final String className; + + public ExtendsTypeMatcher(String className, String technology) { + super(technology); + + this.className = className; + } + + @Override + public boolean matches(Type type) { + JavaClass javaClass = type.getJavaClass(); + try { + Set superClasses = Stream.of(javaClass.getSuperClasses()).map(JavaClass::getClassName).collect(Collectors.toSet()); + return superClasses.contains(className); + } catch (ClassNotFoundException e) { + log.warn("Cannot find super classes of " + type.getFullyQualifiedName(), e); + } + + return false; + } + +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/matcher/ImplementsTypeMatcher.java b/structurizr-component/src/main/java/com/structurizr/component/matcher/ImplementsTypeMatcher.java new file mode 100644 index 000000000..918bcd344 --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/matcher/ImplementsTypeMatcher.java @@ -0,0 +1,32 @@ +package com.structurizr.component.matcher; + +import com.structurizr.component.Type; +import org.apache.bcel.classfile.JavaClass; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.Set; + +/** + * Matches types where the type implements the specified interface. + */ +public class ImplementsTypeMatcher extends AbstractTypeMatcher { + + private static final Log log = LogFactory.getLog(ImplementsTypeMatcher.class); + + private final String interfaceName; + + public ImplementsTypeMatcher(String interfaceName, String technology) { + super(technology); + + this.interfaceName = interfaceName; + } + + @Override + public boolean matches(Type type) { + JavaClass javaClass = type.getJavaClass(); + Set interfaceNames = Set.of(javaClass.getInterfaceNames()); + return interfaceNames.contains(interfaceName); + } + +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/matcher/NameSuffixTypeMatcher.java b/structurizr-component/src/main/java/com/structurizr/component/matcher/NameSuffixTypeMatcher.java new file mode 100644 index 000000000..41bdd76be --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/matcher/NameSuffixTypeMatcher.java @@ -0,0 +1,27 @@ +package com.structurizr.component.matcher; + +import com.structurizr.component.Type; + +/** + * Matches types where the name of the type ends with the specified suffix. + */ +public class NameSuffixTypeMatcher extends AbstractTypeMatcher { + + private final String suffix; + + public NameSuffixTypeMatcher(String suffix, String technology) { + super(technology); + + if (suffix == null || suffix.trim().length() == 0) { + throw new IllegalArgumentException("A suffix must be supplied"); + } + + this.suffix = suffix; + } + + @Override + public boolean matches(Type type) { + return type.getFullyQualifiedName().endsWith(suffix); + } + +} diff --git a/structurizr-component/src/main/java/com/structurizr/component/matcher/RegexTypeMatcher.java b/structurizr-component/src/main/java/com/structurizr/component/matcher/RegexTypeMatcher.java new file mode 100644 index 000000000..e39bbf737 --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/matcher/RegexTypeMatcher.java @@ -0,0 +1,43 @@ +package com.structurizr.component.matcher; + +import com.structurizr.component.Type; + +import java.util.regex.Pattern; + +/** + * Matches types using a regex against the fully qualified type name. + */ +public class RegexTypeMatcher extends AbstractTypeMatcher { + + private final Pattern regex; + + public RegexTypeMatcher(String regex, String technology) { + super(technology); + + if (regex == null) { + throw new IllegalArgumentException("A regex must be supplied"); + } + + this.regex = Pattern.compile(regex); + } + + public RegexTypeMatcher(Pattern regex, String technology) { + super(technology); + + if (regex == null) { + throw new IllegalArgumentException("A regex must be supplied"); + } + + this.regex = regex; + } + + @Override + public boolean matches(Type type) { + if (type != null && type.getFullyQualifiedName() != null) { + return Pattern.matches(regex.pattern(), type.getFullyQualifiedName()); + } else { + return false; + } + } + +} diff --git a/structurizr-component/src/main/java/com/structurizr/component/matcher/TypeMatcher.java b/structurizr-component/src/main/java/com/structurizr/component/matcher/TypeMatcher.java new file mode 100644 index 000000000..572d159d0 --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/matcher/TypeMatcher.java @@ -0,0 +1,14 @@ +package com.structurizr.component.matcher; + +import com.structurizr.component.Type; + +/** + * Determines whether a given type matches the rules for being identified as a component. + */ +public interface TypeMatcher { + + boolean matches(Type type); + + String getTechnology(); + +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/naming/CommonsLangCamelCaseNamingStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/naming/CommonsLangCamelCaseNamingStrategy.java new file mode 100644 index 000000000..5ee05339c --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/naming/CommonsLangCamelCaseNamingStrategy.java @@ -0,0 +1,17 @@ +package com.structurizr.component.naming; + +import com.structurizr.component.Type; + +/** + * Uses Apache commons-lang to split a camel/Pascal cased name into separate words + * (e.g. "CustomerRepository" -> "Customer Repository"). + */ +public class CommonsLangCamelCaseNamingStrategy implements NamingStrategy { + + @Override + public String nameOf(Type type) { + String[] parts = org.apache.commons.lang3.StringUtils.splitByCharacterTypeCamelCase(type.getName()); + return String.join(" ", parts); + } + +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/naming/DefaultNamingStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/naming/DefaultNamingStrategy.java new file mode 100644 index 000000000..5c3d126b2 --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/naming/DefaultNamingStrategy.java @@ -0,0 +1,14 @@ +package com.structurizr.component.naming; + +import com.structurizr.component.Type; + +/** + * Uses the simple/short name of the type (i.e. without the package name). + */ +public class DefaultNamingStrategy implements NamingStrategy { + + public String nameOf(Type type) { + return type.getName(); + } + +} diff --git a/structurizr-component/src/main/java/com/structurizr/component/naming/FullyQualifiedNamingStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/naming/FullyQualifiedNamingStrategy.java new file mode 100644 index 000000000..a1e947497 --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/naming/FullyQualifiedNamingStrategy.java @@ -0,0 +1,15 @@ +package com.structurizr.component.naming; + +import com.structurizr.component.Type; + +/** + * Uses the fully qualified type name. + */ +public class FullyQualifiedNamingStrategy implements NamingStrategy { + + @Override + public String nameOf(Type type) { + return type.getFullyQualifiedName(); + } + +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/naming/NamingStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/naming/NamingStrategy.java new file mode 100644 index 000000000..35f0efab5 --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/naming/NamingStrategy.java @@ -0,0 +1,12 @@ +package com.structurizr.component.naming; + +import com.structurizr.component.Type; + +/** + * Provides a way to map a fully qualified type name to a component name. + */ +public interface NamingStrategy { + + String nameOf(Type type); + +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/provider/DirectoryTypeProvider.java b/structurizr-component/src/main/java/com/structurizr/component/provider/DirectoryTypeProvider.java new file mode 100644 index 000000000..05c55f596 --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/provider/DirectoryTypeProvider.java @@ -0,0 +1,64 @@ +package com.structurizr.component.provider; + +import com.structurizr.component.Type; +import org.apache.bcel.classfile.ClassParser; +import org.apache.bcel.classfile.JavaClass; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.File; +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; + +/** + * A type repository that uses Apache Commons BCEL to load Java classes from a local directory. + */ +public final class DirectoryTypeProvider implements TypeProvider { + + private static final Log log = LogFactory.getLog(DirectoryTypeProvider.class); + private static final String CLASS_FILE_EXTENSION = ".class"; + + private final File directory; + + public DirectoryTypeProvider(File directory) { + this.directory = directory; + } + + public Set getTypes() { + Set types = new HashSet<>(); + + Set files = findClassFiles(directory); + for (File file : files) { + ClassParser parser = new ClassParser(file.getAbsolutePath()); + try { + JavaClass javaClass = parser.parse(); + types.add(new Type(javaClass)); + } catch (IOException e) { + log.warn(e); + } + } + + return types; + } + + private Set findClassFiles(File path) { + Set classFiles = new HashSet<>(); + if (path.isDirectory()) { + File[] files = path.listFiles(); + if (files != null) { + for (File file : files) { + classFiles.addAll(findClassFiles(file)); + } + } + } else { + if (path.getName().endsWith(CLASS_FILE_EXTENSION)) { + classFiles.add(path); + } + } + + return classFiles; + } + + +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/provider/JarFileTypeProvider.java b/structurizr-component/src/main/java/com/structurizr/component/provider/JarFileTypeProvider.java new file mode 100644 index 000000000..5904bf9a6 --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/provider/JarFileTypeProvider.java @@ -0,0 +1,61 @@ +package com.structurizr.component.provider; + +import com.structurizr.component.Type; +import org.apache.bcel.classfile.ClassParser; +import org.apache.bcel.classfile.JavaClass; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.File; +import java.io.IOException; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Set; +import java.util.jar.JarEntry; + +/** + * A type repository that uses Apache Commons BCEL to load Java classes from a local JAR file. + */ +public final class JarFileTypeProvider implements TypeProvider { + + private static final Log log = LogFactory.getLog(JarFileTypeProvider.class); + private static final String CLASS_FILE_EXTENSION = ".class"; + + private final File jarFile; + + public JarFileTypeProvider(File file) { + this.jarFile = file; + } + + public Set getTypes() { + Set types = new HashSet<>(); + java.util.jar.JarFile jar = null; + try { + jar = new java.util.jar.JarFile(jarFile); + Enumeration entries = jar.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + if (!entry.getName().endsWith(CLASS_FILE_EXTENSION)) { + continue; + } + + ClassParser parser = new ClassParser(jarFile.getAbsolutePath(), entry.getName()); + JavaClass javaClass = parser.parse(); + types.add(new Type(javaClass)); + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (jar != null) { + try { + jar.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + return types; + } + +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/provider/JavadocCommentFilter.java b/structurizr-component/src/main/java/com/structurizr/component/provider/JavadocCommentFilter.java new file mode 100644 index 000000000..c89b3fc34 --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/provider/JavadocCommentFilter.java @@ -0,0 +1,35 @@ +package com.structurizr.component.provider; + +/** + * Cleans up Javadoc comments for inclusion in the software architecture model. + */ +class JavadocCommentFilter { + + private final Integer maxCommentLength; + + JavadocCommentFilter(Integer maxCommentLength) { + if (maxCommentLength != null && maxCommentLength < 1) { + throw new IllegalArgumentException("Maximum comment length must be greater than 0."); + } + + this.maxCommentLength = maxCommentLength; + } + + String filterAndTruncate(String s) { + if (s == null) { + return null; + } + + s = s.replaceAll("\\n", " "); + s = s.replaceAll("(?s)<.*?>", ""); + s = s.replaceAll("\\{@link (\\S*)\\}", "$1"); + s = s.replaceAll("\\{@link (\\S*) (.*?)\\}", "$2"); + + if (maxCommentLength != null && s.length() > maxCommentLength) { + return s.substring(0, maxCommentLength-3) + "..."; + } else { + return s; + } + } + +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/provider/SourceCodeTypeProvider.java b/structurizr-component/src/main/java/com/structurizr/component/provider/SourceCodeTypeProvider.java new file mode 100644 index 000000000..a512a8bbe --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/provider/SourceCodeTypeProvider.java @@ -0,0 +1,86 @@ +package com.structurizr.component.provider; + +import com.github.javaparser.ParserConfiguration; +import com.github.javaparser.StaticJavaParser; +import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; +import com.github.javaparser.ast.comments.JavadocComment; +import com.github.javaparser.ast.visitor.VoidVisitorAdapter; +import com.structurizr.component.Type; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.File; +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; + +/** + * A type provider that uses JavaParser to read Javadoc comments from source code. + */ +public final class SourceCodeTypeProvider implements TypeProvider { + + private static final Log log = LogFactory.getLog(SourceCodeTypeProvider.class); + private static final String JAVA_FILE_EXTENSION = ".java"; + private static final int DEFAULT_DESCRIPTION_LENGTH = 60; + + private final Set types = new HashSet<>(); + + public SourceCodeTypeProvider(File path) { + this(path, DEFAULT_DESCRIPTION_LENGTH); + } + + public SourceCodeTypeProvider(File path, int maximumDescriptionLength) { + StaticJavaParser.getParserConfiguration().setLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_21); + + parse(path, maximumDescriptionLength); + } + + @Override + public Set getTypes() { + return new HashSet<>(types); + } + + private void parse(File path, int maximumDescriptionLength) { + if (path.isDirectory()) { + File[] files = path.listFiles(); + if (files != null) { + for (File file : files) { + try { + parse(file, maximumDescriptionLength); + } catch (Exception e) { + log.warn("Error parsing " + file.getAbsolutePath(), e); + } + } + } + } else { + if (path.getName().endsWith(JAVA_FILE_EXTENSION)) { + try { + new VoidVisitorAdapter<>() { + @Override + public void visit(ClassOrInterfaceDeclaration n, Object arg) { + if (n.getFullyQualifiedName().isPresent()) { + String fullyQualifiedName = n.getFullyQualifiedName().get(); + Type type = new Type(fullyQualifiedName); + type.setSource(path.getAbsolutePath()); + + if (n.getComment().isPresent() && n.getComment().get() instanceof JavadocComment) { + JavadocComment javadocComment = (JavadocComment) n.getComment().get(); + String description = javadocComment.parse().getDescription().toText(); + + type.setDescription(new JavadocCommentFilter(maximumDescriptionLength).filterAndTruncate(description)); + } + + types.add(type); + } + } + }.visit(StaticJavaParser.parse(path), null); + } catch (IOException e) { + log.warn("Error parsing source code", e); + } + } else { + log.debug("Ignoring " + path.getAbsolutePath()); + } + } + } + +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/provider/TypeProvider.java b/structurizr-component/src/main/java/com/structurizr/component/provider/TypeProvider.java new file mode 100644 index 000000000..4172e4409 --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/provider/TypeProvider.java @@ -0,0 +1,14 @@ +package com.structurizr.component.provider; + +import com.structurizr.component.Type; + +import java.util.Set; + +/** + * Provides a way to load Java types. + */ +public interface TypeProvider { + + Set getTypes(); + +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/supporting/AllReferencedTypesInSamePackageSupportingTypesStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/supporting/AllReferencedTypesInSamePackageSupportingTypesStrategy.java new file mode 100644 index 000000000..5791d7943 --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/supporting/AllReferencedTypesInSamePackageSupportingTypesStrategy.java @@ -0,0 +1,22 @@ +package com.structurizr.component.supporting; + +import com.structurizr.component.Type; + +import java.util.Set; +import java.util.stream.Collectors; + +/** + * A strategy that finds all referenced types in the same package as the component type. + */ +public class AllReferencedTypesInSamePackageSupportingTypesStrategy implements SupportingTypesStrategy { + + @Override + public Set findSupportingTypes(Type type) { + String packageName = type.getPackageName(); + + return type.getDependencies().stream() + .filter(dependency -> dependency.getPackageName().startsWith(packageName)) + .collect(Collectors.toSet()); + } + +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/supporting/AllReferencedTypesSupportingTypesStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/supporting/AllReferencedTypesSupportingTypesStrategy.java new file mode 100644 index 000000000..28738a7aa --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/supporting/AllReferencedTypesSupportingTypesStrategy.java @@ -0,0 +1,17 @@ +package com.structurizr.component.supporting; + +import com.structurizr.component.Type; + +import java.util.Set; + +/** + * A strategy that finds all referenced types, irrespective of package. + */ +public class AllReferencedTypesSupportingTypesStrategy implements SupportingTypesStrategy { + + @Override + public Set findSupportingTypes(Type type) { + return type.getDependencies(); + } + +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/supporting/DefaultSupportingTypesStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/supporting/DefaultSupportingTypesStrategy.java new file mode 100644 index 000000000..58f1ec8cd --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/supporting/DefaultSupportingTypesStrategy.java @@ -0,0 +1,18 @@ +package com.structurizr.component.supporting; + +import com.structurizr.component.Type; + +import java.util.Collections; +import java.util.Set; + +/** + * A strategy that returns an empty set of supporting types. + */ +public class DefaultSupportingTypesStrategy implements SupportingTypesStrategy { + + @Override + public Set findSupportingTypes(Type type) { + return Collections.emptySet(); + } + +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/supporting/SupportingTypesStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/supporting/SupportingTypesStrategy.java new file mode 100644 index 000000000..1d226daca --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/supporting/SupportingTypesStrategy.java @@ -0,0 +1,14 @@ +package com.structurizr.component.supporting; + +import com.structurizr.component.Type; + +import java.util.Set; + +/** + * Provides a strategy to identify the types that support a component. + */ +public interface SupportingTypesStrategy { + + Set findSupportingTypes(Type type); + +} \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/AbstractWorkspaceTestBase.java b/structurizr-component/src/test/java/com/structurizr/AbstractWorkspaceTestBase.java new file mode 100644 index 000000000..91be93b65 --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/AbstractWorkspaceTestBase.java @@ -0,0 +1,12 @@ +package com.structurizr; + +import com.structurizr.model.Model; +import com.structurizr.view.ViewSet; + +public class AbstractWorkspaceTestBase { + + protected Workspace workspace = new Workspace("Name", "Description"); + protected Model model = workspace.getModel(); + protected ViewSet views = workspace.getViews(); + +} diff --git a/structurizr-component/src/test/java/com/structurizr/component/example/Example.java b/structurizr-component/src/test/java/com/structurizr/component/example/Example.java new file mode 100644 index 000000000..99af51453 --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/example/Example.java @@ -0,0 +1,50 @@ +package com.structurizr.component.example; + +import com.structurizr.Workspace; +import com.structurizr.component.ComponentFinderBuilder; +import com.structurizr.component.ComponentFinderStrategyBuilder; +import com.structurizr.component.matcher.NameSuffixTypeMatcher; +import com.structurizr.component.naming.CommonsLangCamelCaseNamingStrategy; +import com.structurizr.model.Component; +import com.structurizr.model.Container; +import com.structurizr.model.Relationship; +import com.structurizr.model.SoftwareSystem; + +public class Example { + + public static void main(String[] args) { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + + new ComponentFinderBuilder() + .forContainer(container) + .fromDirectory("structurizr-component/build/classes/java/test") + .fromSourceCode("structurizr-component/src/test/java") + .withStrategy( + new ComponentFinderStrategyBuilder() + .matchedBy(new NameSuffixTypeMatcher("Controller", "Web MVC Controller")) + .namedBy(new CommonsLangCamelCaseNamingStrategy()) + .build() + ) + .withStrategy( + new ComponentFinderStrategyBuilder() + .matchedBy(new NameSuffixTypeMatcher("Repository", "Data Repository")) + .namedBy(new CommonsLangCamelCaseNamingStrategy()) + .build() + ) + .build().findComponents(); + + for (Component component : container.getComponents()) { + System.out.println(component.getName()); + System.out.println(" - Description: " + component.getDescription()); + System.out.println(" - Technology: " + component.getTechnology()); + System.out.println(" - Type: " + component.getProperties().get("component.type")); + System.out.println(" - Source: " + component.getProperties().get("component.src")); + for (Relationship relationship : component.getRelationships()) { + System.out.println(" -> " + relationship.getDestination().getName()); + } + } + } + +} diff --git a/structurizr-component/src/test/java/com/structurizr/component/example/controller/CustomerController.java b/structurizr-component/src/test/java/com/structurizr/component/example/controller/CustomerController.java new file mode 100644 index 000000000..409e4dd8c --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/example/controller/CustomerController.java @@ -0,0 +1,19 @@ +package com.structurizr.component.example.controller; + +import com.structurizr.component.example.repository.CustomerRepository; + +/** + * Allows users to view a list of customers. + */ +public class CustomerController { + + private CustomerRepository customerRepository; + + public CustomerController(CustomerRepository customerRepository) { + this.customerRepository = customerRepository; + } + + void showCustomersPage() { + customerRepository.getCustomers(); + } +} diff --git a/structurizr-component/src/test/java/com/structurizr/component/example/domain/Customer.java b/structurizr-component/src/test/java/com/structurizr/component/example/domain/Customer.java new file mode 100644 index 000000000..ca9637df3 --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/example/domain/Customer.java @@ -0,0 +1,4 @@ +package com.structurizr.component.example.domain; + +public class Customer { +} diff --git a/structurizr-component/src/test/java/com/structurizr/component/example/repository/CustomerRepository.java b/structurizr-component/src/test/java/com/structurizr/component/example/repository/CustomerRepository.java new file mode 100644 index 000000000..d7040cd2e --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/example/repository/CustomerRepository.java @@ -0,0 +1,14 @@ +package com.structurizr.component.example.repository; + +import com.structurizr.component.example.domain.Customer; + +import java.util.List; + +/** + * Provides a way to access customer data. + */ +public interface CustomerRepository { + + List getCustomers(); + +} \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/example/repository/CustomerRepositoryImpl.java b/structurizr-component/src/test/java/com/structurizr/component/example/repository/CustomerRepositoryImpl.java new file mode 100644 index 000000000..43ef69bdc --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/example/repository/CustomerRepositoryImpl.java @@ -0,0 +1,15 @@ +package com.structurizr.component.example.repository; + +import com.structurizr.component.example.domain.Customer; + +import java.util.ArrayList; +import java.util.List; + +public class CustomerRepositoryImpl implements CustomerRepository { + + @Override + public List getCustomers() { + return new ArrayList<>(); + } + +} \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/provider/JavadocCommentFilterTests.java b/structurizr-component/src/test/java/com/structurizr/component/provider/JavadocCommentFilterTests.java new file mode 100644 index 000000000..265a888d6 --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/provider/JavadocCommentFilterTests.java @@ -0,0 +1,60 @@ +package com.structurizr.component.provider; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class JavadocCommentFilterTests { + + @Test + public void test_construction_ThrowsAnIllegalArgumentException_WhenZeroIsSpecified() { + assertThrowsExactly(IllegalArgumentException.class, () -> new JavadocCommentFilter(0)); + } + + @Test + public void test_construction_ThrowsAnIllegalArgumentException_WhenANegativeNumberIsSpecified() { + assertThrowsExactly(IllegalArgumentException.class, () -> new JavadocCommentFilter(-1)); + } + + @Test + public void test_filterAndTruncate_ReturnsNull_WhenGivenNull() { + assertNull(new JavadocCommentFilter(null).filterAndTruncate(null)); + } + + @Test + public void test_filterAndTruncate_ReturnsTheOriginalText_WhenNoMaxLengthHasBeenSpecified() + { + assertEquals("Here is some text.", new JavadocCommentFilter(null).filterAndTruncate("Here is some text.")); + } + + @Test + public void test_filterAndTruncate_TruncatesTheTextWhenAMaxLengthHasBeenSpecified() + { + assertEquals("Here...", new JavadocCommentFilter(7).filterAndTruncate("Here is some text.")); + } + + @Test + public void test_filterAndTruncate_FiltersJavadocLinkTags() + { + assertEquals("Uses SomeClass and AnotherClass to do some work.", new JavadocCommentFilter(null).filterAndTruncate("Uses {@link SomeClass} and {@link AnotherClass} to do some work.")); + } + + @Test + public void test_filterAndTruncate_FiltersJavadocLinkTagsWithLabels() + { + assertEquals("Uses some class and another class to do some work.", new JavadocCommentFilter(null).filterAndTruncate("Uses {@link SomeClass some class} and {@link AnotherClass another class} to do some work.")); + } + + @Test + public void test_filterAndTruncate_FiltersHtml() + { + assertEquals("Uses SomeClass and AnotherClass to do some work.", new JavadocCommentFilter(null).filterAndTruncate("Uses SomeClass and AnotherClass to do some work.")); + } + + @Test + public void test_filterAndTruncate_FiltersLineBreaks() + { + assertEquals("Uses SomeClass and AnotherClass to do some work.", new JavadocCommentFilter(null).filterAndTruncate("Uses SomeClass and AnotherClass\nto do some work.")); + } + +} \ No newline at end of file From 002e1ac3510c6f7fe41b7487af4922852e1fdfb1 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 18 Aug 2024 11:26:53 +0100 Subject: [PATCH 520/717] . --- README.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 4cb2d0dc1..d486bd395 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,14 @@ This repository contains the source code for the following libraries: -- [structurizr-client](structurizr-client) -- [structurizr-core](structurizr-core) -- [structurizr-dsl](structurizr-dsl) -- [structurizr-export](structurizr-export) -- [structurizr-import](structurizr-import) -- [structurizr-autolayout](structurizr-autolayout) -- [structurizr-inspection](structurizr-inspection) +- [structurizr-client](structurizr-client): JSON serialisation/deserialisation utilities, and clients for the cloud service/on-premises workspace/admin APIs. +- [structurizr-core](structurizr-core): The core library for creating a workspace with Java code. +- [structurizr-component](structurizr-component): A library to discover components from Java code. +- [structurizr-dsl](structurizr-dsl): A text-based DSL wrapper around Structurizr for Java. +- [structurizr-export](structurizr-export): Export models and views to external formats (e.g. PlantUML, Mermaid, etc). +- [structurizr-import](structurizr-import): Utilities to import diagrams and documentation into a Structurizr workspace. +- [structurizr-autolayout](structurizr-autolayout): Apply Graphviz automatic layout to views. +- [structurizr-inspection](structurizr-inspection): A Checkstyle inspired approach to verifying workspace content. - [Documentation](https://docs.structurizr.com) - [Changelog](changelog.md) \ No newline at end of file From b590494ce35453b3004606fcecdc64bdb34d06dd Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 18 Aug 2024 11:36:10 +0100 Subject: [PATCH 521/717] Renaming of type providers. --- .../component/ComponentFinderBuilder.java | 42 ++++++++++--------- ...r.java => ClassDirectoryTypeProvider.java} | 6 +-- ...der.java => ClassJarFileTypeProvider.java} | 6 +-- ....java => SourceDirectoryTypeProvider.java} | 8 ++-- .../component/example/Example.java | 4 +- 5 files changed, 34 insertions(+), 32 deletions(-) rename structurizr-component/src/main/java/com/structurizr/component/provider/{DirectoryTypeProvider.java => ClassDirectoryTypeProvider.java} (88%) rename structurizr-component/src/main/java/com/structurizr/component/provider/{JarFileTypeProvider.java => ClassJarFileTypeProvider.java} (89%) rename structurizr-component/src/main/java/com/structurizr/component/provider/{SourceCodeTypeProvider.java => SourceDirectoryTypeProvider.java} (91%) diff --git a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderBuilder.java b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderBuilder.java index f685451b5..a4f8f9535 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderBuilder.java +++ b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderBuilder.java @@ -1,8 +1,8 @@ package com.structurizr.component; -import com.structurizr.component.provider.DirectoryTypeProvider; -import com.structurizr.component.provider.JarFileTypeProvider; -import com.structurizr.component.provider.SourceCodeTypeProvider; +import com.structurizr.component.provider.ClassDirectoryTypeProvider; +import com.structurizr.component.provider.ClassJarFileTypeProvider; +import com.structurizr.component.provider.SourceDirectoryTypeProvider; import com.structurizr.component.provider.TypeProvider; import com.structurizr.model.Container; @@ -15,6 +15,8 @@ */ public class ComponentFinderBuilder { + private static final String JAR_FILE_EXTENSION = ".jar"; + private Container container; private final List typeProviders = new ArrayList<>(); private final List componentFinderStrategies = new ArrayList<>(); @@ -25,32 +27,32 @@ public ComponentFinderBuilder forContainer(Container container) { return this; } - public ComponentFinderBuilder fromJarFile(String filename) { - return fromJarFile(new File(filename)); - } - - public ComponentFinderBuilder fromJarFile(File file) { - this.typeProviders.add(new JarFileTypeProvider(file)); - - return this; + public ComponentFinderBuilder fromClasses(String path) { + return fromClasses(new File(path)); } - public ComponentFinderBuilder fromDirectory(String path) { - return fromDirectory(new File(path)); - } + public ComponentFinderBuilder fromClasses(File path) { + if (!path.exists()) { + throw new IllegalArgumentException(path.getAbsolutePath() + " does not exist"); + } - public ComponentFinderBuilder fromDirectory(File path) { - this.typeProviders.add(new DirectoryTypeProvider(path)); + if (path.isDirectory()) { + this.typeProviders.add(new ClassDirectoryTypeProvider(path)); + } else if (path.getName().endsWith(JAR_FILE_EXTENSION)) { + this.typeProviders.add(new ClassJarFileTypeProvider(path)); + } else { + throw new IllegalArgumentException("Expected a directory of classes or a .jar file: " + path.getAbsolutePath()); + } return this; } - public ComponentFinderBuilder fromSourceCode(String path) { - return fromSourceCode(new File(path)); + public ComponentFinderBuilder fromSource(String path) { + return fromSource(new File(path)); } - public ComponentFinderBuilder fromSourceCode(File path) { - this.typeProviders.add(new SourceCodeTypeProvider(path)); + public ComponentFinderBuilder fromSource(File path) { + this.typeProviders.add(new SourceDirectoryTypeProvider(path)); return this; } diff --git a/structurizr-component/src/main/java/com/structurizr/component/provider/DirectoryTypeProvider.java b/structurizr-component/src/main/java/com/structurizr/component/provider/ClassDirectoryTypeProvider.java similarity index 88% rename from structurizr-component/src/main/java/com/structurizr/component/provider/DirectoryTypeProvider.java rename to structurizr-component/src/main/java/com/structurizr/component/provider/ClassDirectoryTypeProvider.java index 05c55f596..a4c5b8339 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/provider/DirectoryTypeProvider.java +++ b/structurizr-component/src/main/java/com/structurizr/component/provider/ClassDirectoryTypeProvider.java @@ -14,14 +14,14 @@ /** * A type repository that uses Apache Commons BCEL to load Java classes from a local directory. */ -public final class DirectoryTypeProvider implements TypeProvider { +public final class ClassDirectoryTypeProvider implements TypeProvider { - private static final Log log = LogFactory.getLog(DirectoryTypeProvider.class); + private static final Log log = LogFactory.getLog(ClassDirectoryTypeProvider.class); private static final String CLASS_FILE_EXTENSION = ".class"; private final File directory; - public DirectoryTypeProvider(File directory) { + public ClassDirectoryTypeProvider(File directory) { this.directory = directory; } diff --git a/structurizr-component/src/main/java/com/structurizr/component/provider/JarFileTypeProvider.java b/structurizr-component/src/main/java/com/structurizr/component/provider/ClassJarFileTypeProvider.java similarity index 89% rename from structurizr-component/src/main/java/com/structurizr/component/provider/JarFileTypeProvider.java rename to structurizr-component/src/main/java/com/structurizr/component/provider/ClassJarFileTypeProvider.java index 5904bf9a6..71951c04a 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/provider/JarFileTypeProvider.java +++ b/structurizr-component/src/main/java/com/structurizr/component/provider/ClassJarFileTypeProvider.java @@ -16,14 +16,14 @@ /** * A type repository that uses Apache Commons BCEL to load Java classes from a local JAR file. */ -public final class JarFileTypeProvider implements TypeProvider { +public final class ClassJarFileTypeProvider implements TypeProvider { - private static final Log log = LogFactory.getLog(JarFileTypeProvider.class); + private static final Log log = LogFactory.getLog(ClassJarFileTypeProvider.class); private static final String CLASS_FILE_EXTENSION = ".class"; private final File jarFile; - public JarFileTypeProvider(File file) { + public ClassJarFileTypeProvider(File file) { this.jarFile = file; } diff --git a/structurizr-component/src/main/java/com/structurizr/component/provider/SourceCodeTypeProvider.java b/structurizr-component/src/main/java/com/structurizr/component/provider/SourceDirectoryTypeProvider.java similarity index 91% rename from structurizr-component/src/main/java/com/structurizr/component/provider/SourceCodeTypeProvider.java rename to structurizr-component/src/main/java/com/structurizr/component/provider/SourceDirectoryTypeProvider.java index a512a8bbe..f68630a6c 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/provider/SourceCodeTypeProvider.java +++ b/structurizr-component/src/main/java/com/structurizr/component/provider/SourceDirectoryTypeProvider.java @@ -17,19 +17,19 @@ /** * A type provider that uses JavaParser to read Javadoc comments from source code. */ -public final class SourceCodeTypeProvider implements TypeProvider { +public final class SourceDirectoryTypeProvider implements TypeProvider { - private static final Log log = LogFactory.getLog(SourceCodeTypeProvider.class); + private static final Log log = LogFactory.getLog(SourceDirectoryTypeProvider.class); private static final String JAVA_FILE_EXTENSION = ".java"; private static final int DEFAULT_DESCRIPTION_LENGTH = 60; private final Set types = new HashSet<>(); - public SourceCodeTypeProvider(File path) { + public SourceDirectoryTypeProvider(File path) { this(path, DEFAULT_DESCRIPTION_LENGTH); } - public SourceCodeTypeProvider(File path, int maximumDescriptionLength) { + public SourceDirectoryTypeProvider(File path, int maximumDescriptionLength) { StaticJavaParser.getParserConfiguration().setLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_21); parse(path, maximumDescriptionLength); diff --git a/structurizr-component/src/test/java/com/structurizr/component/example/Example.java b/structurizr-component/src/test/java/com/structurizr/component/example/Example.java index 99af51453..f50b90369 100644 --- a/structurizr-component/src/test/java/com/structurizr/component/example/Example.java +++ b/structurizr-component/src/test/java/com/structurizr/component/example/Example.java @@ -19,8 +19,8 @@ public static void main(String[] args) { new ComponentFinderBuilder() .forContainer(container) - .fromDirectory("structurizr-component/build/classes/java/test") - .fromSourceCode("structurizr-component/src/test/java") + .fromClasses("structurizr-component/build/classes/java/test") + .fromSource("structurizr-component/src/test/java") .withStrategy( new ComponentFinderStrategyBuilder() .matchedBy(new NameSuffixTypeMatcher("Controller", "Web MVC Controller")) From bdff6a19e27e4c4b323bdb5c41fc32faf9ca4d96 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 18 Aug 2024 12:16:33 +0100 Subject: [PATCH 522/717] Adds a way to visit discovered components ... this can be used for post-processing after creation. --- .../component/ComponentFinder.java | 6 +++ .../component/ComponentFinderStrategy.java | 11 +++++- .../ComponentFinderStrategyBuilder.java | 11 +++++- .../component/DiscoveredComponent.java | 10 +++++ .../component/visitor/ComponentVisitor.java | 12 ++++++ .../visitor/DefaultComponentVisitor.java | 14 +++++++ .../component/example/Example.java | 39 ++++++++++++------- 7 files changed, 87 insertions(+), 16 deletions(-) create mode 100644 structurizr-component/src/main/java/com/structurizr/component/visitor/ComponentVisitor.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/visitor/DefaultComponentVisitor.java diff --git a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinder.java b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinder.java index 4abd0dc99..c8d47ba00 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinder.java +++ b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinder.java @@ -128,6 +128,12 @@ public void findComponents() { } } } + + // now visit all components + for (DiscoveredComponent discoveredComponent : componentMap.keySet()) { + Component component = componentMap.get(discoveredComponent); + discoveredComponent.getComponentFinderStrategy().visit(component); + } } } \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategy.java index c6273bc91..80d7094af 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategy.java +++ b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategy.java @@ -4,6 +4,8 @@ import com.structurizr.component.matcher.TypeMatcher; import com.structurizr.component.naming.NamingStrategy; import com.structurizr.component.supporting.SupportingTypesStrategy; +import com.structurizr.component.visitor.ComponentVisitor; +import com.structurizr.model.Component; import java.util.HashSet; import java.util.Set; @@ -23,12 +25,14 @@ class ComponentFinderStrategy { private final TypeFilter typeFilter; private final SupportingTypesStrategy supportingTypesStrategy; private final NamingStrategy namingStrategy; + private final ComponentVisitor componentVisitor; - ComponentFinderStrategy(TypeMatcher typeMatcher, TypeFilter typeFilter, SupportingTypesStrategy supportingTypesStrategy, NamingStrategy namingStrategy) { + ComponentFinderStrategy(TypeMatcher typeMatcher, TypeFilter typeFilter, SupportingTypesStrategy supportingTypesStrategy, NamingStrategy namingStrategy, ComponentVisitor componentVisitor) { this.typeMatcher = typeMatcher; this.typeFilter = typeFilter; this.supportingTypesStrategy = supportingTypesStrategy; this.namingStrategy = namingStrategy; + this.componentVisitor = componentVisitor; } Set findComponents(TypeRepository typeRepository) { @@ -40,6 +44,7 @@ Set findComponents(TypeRepository typeRepository) { DiscoveredComponent component = new DiscoveredComponent(namingStrategy.nameOf(type), type); component.setDescription(type.getDescription()); component.setTechnology(typeMatcher.getTechnology()); + component.setComponentFinderStrategy(this); components.add(component); // now find supporting types @@ -51,4 +56,8 @@ Set findComponents(TypeRepository typeRepository) { return components; } + void visit(Component component) { + this.componentVisitor.visit(component); + } + } \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategyBuilder.java b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategyBuilder.java index 87719be1a..97a919843 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategyBuilder.java +++ b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategyBuilder.java @@ -7,6 +7,8 @@ import com.structurizr.component.naming.NamingStrategy; import com.structurizr.component.supporting.DefaultSupportingTypesStrategy; import com.structurizr.component.supporting.SupportingTypesStrategy; +import com.structurizr.component.visitor.ComponentVisitor; +import com.structurizr.component.visitor.DefaultComponentVisitor; /** * Provides a way to create a {@link ComponentFinderStrategy} instance. @@ -17,6 +19,7 @@ public final class ComponentFinderStrategyBuilder { private TypeFilter typeFilter = new DefaultTypeFilter(); private SupportingTypesStrategy supportingTypesStrategy = new DefaultSupportingTypesStrategy(); private NamingStrategy namingStrategy = new DefaultNamingStrategy(); + private ComponentVisitor componentVisitor = new DefaultComponentVisitor(); public ComponentFinderStrategyBuilder() { } @@ -45,12 +48,18 @@ public ComponentFinderStrategyBuilder namedBy(NamingStrategy namingStrategy) { return this; } + public ComponentFinderStrategyBuilder forEach(ComponentVisitor componentVisitor) { + this.componentVisitor = componentVisitor; + + return this; + } + public ComponentFinderStrategy build() { if (typeMatcher == null) { throw new RuntimeException("A type matcher must be specified"); } - return new ComponentFinderStrategy(typeMatcher, typeFilter, supportingTypesStrategy, namingStrategy); + return new ComponentFinderStrategy(typeMatcher, typeFilter, supportingTypesStrategy, namingStrategy, componentVisitor); } } \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/DiscoveredComponent.java b/structurizr-component/src/main/java/com/structurizr/component/DiscoveredComponent.java index 19091412b..8eddb2e08 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/DiscoveredComponent.java +++ b/structurizr-component/src/main/java/com/structurizr/component/DiscoveredComponent.java @@ -11,6 +11,8 @@ final class DiscoveredComponent { private String technology; private final Set supportingTypes = new HashSet<>(); + private ComponentFinderStrategy componentFinderStrategy; + DiscoveredComponent(String name, Type primaryType) { this.name = name; this.primaryType = primaryType; @@ -67,4 +69,12 @@ Set getAllDependencies() { return dependencies; } + ComponentFinderStrategy getComponentFinderStrategy() { + return componentFinderStrategy; + } + + void setComponentFinderStrategy(ComponentFinderStrategy componentFinderStrategy) { + this.componentFinderStrategy = componentFinderStrategy; + } + } \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/visitor/ComponentVisitor.java b/structurizr-component/src/main/java/com/structurizr/component/visitor/ComponentVisitor.java new file mode 100644 index 000000000..9c6a98646 --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/visitor/ComponentVisitor.java @@ -0,0 +1,12 @@ +package com.structurizr.component.visitor; + +import com.structurizr.model.Component; + +/** + * Provides a way to visit each discovered component. + */ +public interface ComponentVisitor { + + void visit(Component component); + +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/visitor/DefaultComponentVisitor.java b/structurizr-component/src/main/java/com/structurizr/component/visitor/DefaultComponentVisitor.java new file mode 100644 index 000000000..49b687cf6 --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/visitor/DefaultComponentVisitor.java @@ -0,0 +1,14 @@ +package com.structurizr.component.visitor; + +import com.structurizr.model.Component; + +/** + * No-op implementation of a component visitor. + */ +public class DefaultComponentVisitor implements ComponentVisitor { + + @Override + public void visit(Component component) { + } + +} \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/example/Example.java b/structurizr-component/src/test/java/com/structurizr/component/example/Example.java index f50b90369..fe13a91a8 100644 --- a/structurizr-component/src/test/java/com/structurizr/component/example/Example.java +++ b/structurizr-component/src/test/java/com/structurizr/component/example/Example.java @@ -5,45 +5,56 @@ import com.structurizr.component.ComponentFinderStrategyBuilder; import com.structurizr.component.matcher.NameSuffixTypeMatcher; import com.structurizr.component.naming.CommonsLangCamelCaseNamingStrategy; -import com.structurizr.model.Component; -import com.structurizr.model.Container; -import com.structurizr.model.Relationship; -import com.structurizr.model.SoftwareSystem; +import com.structurizr.model.*; public class Example { public static void main(String[] args) { Workspace workspace = new Workspace("Name", "Description"); + workspace.getModel().setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); + + Person user = workspace.getModel().addPerson("User"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); - Container container = softwareSystem.addContainer("Container"); + Container webApplication = softwareSystem.addContainer("Web Application"); + Container databaseSchema = softwareSystem.addContainer("Database Schema"); new ComponentFinderBuilder() - .forContainer(container) + .forContainer(webApplication) .fromClasses("structurizr-component/build/classes/java/test") .fromSource("structurizr-component/src/test/java") .withStrategy( new ComponentFinderStrategyBuilder() .matchedBy(new NameSuffixTypeMatcher("Controller", "Web MVC Controller")) .namedBy(new CommonsLangCamelCaseNamingStrategy()) + .forEach(component -> user.uses(component, "Uses")) .build() ) .withStrategy( new ComponentFinderStrategyBuilder() .matchedBy(new NameSuffixTypeMatcher("Repository", "Data Repository")) .namedBy(new CommonsLangCamelCaseNamingStrategy()) + .forEach(component -> component.uses(databaseSchema, "Reads from and writes to")) .build() ) .build().findComponents(); - for (Component component : container.getComponents()) { - System.out.println(component.getName()); - System.out.println(" - Description: " + component.getDescription()); + for (Element element : workspace.getModel().getElements()) { + print(element); + } + } + + private static void print(Element element) { + System.out.println("[" + element.getClass().getSimpleName() + "] " + element.getName()); + System.out.println(" - Description: " + element.getDescription()); + + if (element instanceof Component component) { System.out.println(" - Technology: " + component.getTechnology()); - System.out.println(" - Type: " + component.getProperties().get("component.type")); - System.out.println(" - Source: " + component.getProperties().get("component.src")); - for (Relationship relationship : component.getRelationships()) { - System.out.println(" -> " + relationship.getDestination().getName()); - } + System.out.println(" - Type: " + element.getProperties().get("component.type")); + System.out.println(" - Source: " + element.getProperties().get("component.src")); + } + + for (Relationship relationship : element.getRelationships()) { + System.out.println(" -> [" + relationship.getDestination().getClass().getSimpleName() + "] " + relationship.getDestination().getName()); } } From 4730125a597ff4678f6109cf6ca2c328de2a95f9 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 18 Aug 2024 22:35:51 +0100 Subject: [PATCH 523/717] A few enhancements. --- .../component/ComponentFinder.java | 12 ++++++-- .../component/ComponentFinderStrategy.java | 11 +++++++- .../ComponentFinderStrategyBuilder.java | 1 + .../structurizr/component/TypeRepository.java | 6 ++-- .../component/filter/DefaultTypeFilter.java | 5 ++++ .../ExcludeAbstractClassTypeFilter.java | 5 ++++ .../filter/ExcludeTypesByRegexFilter.java | 19 +++++++------ .../filter/IncludeTypesByRegexFilter.java | 28 +++++++++++++++++++ .../matcher/AnnotationTypeMatcher.java | 7 +++++ .../component/matcher/ExtendsTypeMatcher.java | 7 +++++ .../matcher/ImplementsTypeMatcher.java | 7 +++++ .../matcher/NameSuffixTypeMatcher.java | 7 +++++ .../component/matcher/RegexTypeMatcher.java | 7 +++++ .../CommonsLangCamelCaseNamingStrategy.java | 17 ----------- .../naming/DefaultNamingStrategy.java | 9 ++++-- .../naming/SimpleNamingStrategy.java | 14 ++++++++++ .../component/example/Example.java | 4 +-- 17 files changed, 128 insertions(+), 38 deletions(-) create mode 100644 structurizr-component/src/main/java/com/structurizr/component/filter/IncludeTypesByRegexFilter.java delete mode 100644 structurizr-component/src/main/java/com/structurizr/component/naming/CommonsLangCamelCaseNamingStrategy.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/naming/SimpleNamingStrategy.java diff --git a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinder.java b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinder.java index c8d47ba00..b12b7f04c 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinder.java +++ b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinder.java @@ -8,6 +8,8 @@ import org.apache.bcel.classfile.ConstantPool; import org.apache.bcel.classfile.Method; import org.apache.bcel.generic.*; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import java.util.*; @@ -17,6 +19,8 @@ */ public final class ComponentFinder { + private static final Log log = LogFactory.getLog(ComponentFinder.class); + private static final String COMPONENT_TYPE_PROPERTY_NAME = "component.type"; private static final String COMPONENT_SOURCE_PROPERTY_NAME = "component.src"; @@ -96,11 +100,15 @@ private void findDependencies(com.structurizr.component.Type type) { * Find components, using all configured rules, in the order they were added. */ public void findComponents() { - Set discoveredComponents = new HashSet<>(); + Set discoveredComponents = new LinkedHashSet<>(); Map componentMap = new HashMap<>(); for (ComponentFinderStrategy componentFinderStrategy : componentFinderStrategies) { - discoveredComponents.addAll(componentFinderStrategy.findComponents(typeRepository)); + Set set = componentFinderStrategy.findComponents(typeRepository); + if (set.isEmpty()) { + throw new RuntimeException("No components were found by " + componentFinderStrategy); + } + discoveredComponents.addAll(set); } for (DiscoveredComponent discoveredComponent : discoveredComponents) { diff --git a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategy.java index 80d7094af..1da4f7de7 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategy.java +++ b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategy.java @@ -8,6 +8,7 @@ import com.structurizr.model.Component; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.Set; /** @@ -36,7 +37,7 @@ class ComponentFinderStrategy { } Set findComponents(TypeRepository typeRepository) { - Set components = new HashSet<>(); + Set components = new LinkedHashSet<>(); Set types = typeRepository.getTypes(); for (Type type : types) { @@ -60,4 +61,12 @@ void visit(Component component) { this.componentVisitor.visit(component); } + @Override + public String toString() { + return "ComponentFinderStrategy{" + + "typeMatcher=" + typeMatcher + + ", typeFilter=" + typeFilter + + '}'; + } + } \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategyBuilder.java b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategyBuilder.java index 97a919843..8027be99f 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategyBuilder.java +++ b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategyBuilder.java @@ -4,6 +4,7 @@ import com.structurizr.component.filter.TypeFilter; import com.structurizr.component.matcher.TypeMatcher; import com.structurizr.component.naming.DefaultNamingStrategy; +import com.structurizr.component.naming.SimpleNamingStrategy; import com.structurizr.component.naming.NamingStrategy; import com.structurizr.component.supporting.DefaultSupportingTypesStrategy; import com.structurizr.component.supporting.SupportingTypesStrategy; diff --git a/structurizr-component/src/main/java/com/structurizr/component/TypeRepository.java b/structurizr-component/src/main/java/com/structurizr/component/TypeRepository.java index 670e1da44..45f80f365 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/TypeRepository.java +++ b/structurizr-component/src/main/java/com/structurizr/component/TypeRepository.java @@ -1,18 +1,18 @@ package com.structurizr.component; -import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.Set; final class TypeRepository { - private final Set types = new HashSet<>(); + private final Set types = new LinkedHashSet<>(); public void add(Type type) { this.types.add(type); } public Set getTypes() { - return new HashSet<>(types); + return new LinkedHashSet<>(types); } Type getType(String fullyQualifiedClassName) { diff --git a/structurizr-component/src/main/java/com/structurizr/component/filter/DefaultTypeFilter.java b/structurizr-component/src/main/java/com/structurizr/component/filter/DefaultTypeFilter.java index e7c79d302..64e9370d1 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/filter/DefaultTypeFilter.java +++ b/structurizr-component/src/main/java/com/structurizr/component/filter/DefaultTypeFilter.java @@ -11,4 +11,9 @@ public boolean accept(Type type) { return true; } + @Override + public String toString() { + return "DefaultTypeFilter{}"; + } + } \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/filter/ExcludeAbstractClassTypeFilter.java b/structurizr-component/src/main/java/com/structurizr/component/filter/ExcludeAbstractClassTypeFilter.java index 162b99313..dad267ef1 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/filter/ExcludeAbstractClassTypeFilter.java +++ b/structurizr-component/src/main/java/com/structurizr/component/filter/ExcludeAbstractClassTypeFilter.java @@ -11,4 +11,9 @@ public boolean accept(Type type) { return !type.isAbstract(); } + @Override + public String toString() { + return "ExcludeAbstractClassTypeFilter{}"; + } + } \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/filter/ExcludeTypesByRegexFilter.java b/structurizr-component/src/main/java/com/structurizr/component/filter/ExcludeTypesByRegexFilter.java index 497daefd1..0f697b6e8 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/filter/ExcludeTypesByRegexFilter.java +++ b/structurizr-component/src/main/java/com/structurizr/component/filter/ExcludeTypesByRegexFilter.java @@ -7,21 +7,22 @@ */ public class ExcludeTypesByRegexFilter implements TypeFilter { - private final String[] regexes; + private final String regex; - public ExcludeTypesByRegexFilter(String... regexes) { - this.regexes = regexes; + public ExcludeTypesByRegexFilter(String regex) { + this.regex = regex; } @Override public boolean accept(Type type) { - for (String regex : regexes) { - if (type.getFullyQualifiedName().matches(regex)) { - return false; - } - } + return !type.getFullyQualifiedName().matches(regex); + } - return true; + @Override + public String toString() { + return "ExcludeTypesByRegexFilter{" + + "regex='" + regex + '\'' + + '}'; } } \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/filter/IncludeTypesByRegexFilter.java b/structurizr-component/src/main/java/com/structurizr/component/filter/IncludeTypesByRegexFilter.java new file mode 100644 index 000000000..dc76b1f41 --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/filter/IncludeTypesByRegexFilter.java @@ -0,0 +1,28 @@ +package com.structurizr.component.filter; + +import com.structurizr.component.Type; + +/** + * A type filter that includes by matching a regex against the fully qualified type name. + */ +public class IncludeTypesByRegexFilter implements TypeFilter { + + private final String regex; + + public IncludeTypesByRegexFilter(String regex) { + this.regex = regex; + } + + @Override + public boolean accept(Type type) { + return type.getFullyQualifiedName().matches(regex); + } + + @Override + public String toString() { + return "IncludeTypesByRegexFilter{" + + "regex='" + regex + '\'' + + '}'; + } + +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/matcher/AnnotationTypeMatcher.java b/structurizr-component/src/main/java/com/structurizr/component/matcher/AnnotationTypeMatcher.java index f3eda8594..2674204d8 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/matcher/AnnotationTypeMatcher.java +++ b/structurizr-component/src/main/java/com/structurizr/component/matcher/AnnotationTypeMatcher.java @@ -45,4 +45,11 @@ public boolean matches(Type type) { return false; } + @Override + public String toString() { + return "AnnotationTypeMatcher{" + + "annotationType='" + annotationType + '\'' + + '}'; + } + } \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/matcher/ExtendsTypeMatcher.java b/structurizr-component/src/main/java/com/structurizr/component/matcher/ExtendsTypeMatcher.java index bfff7159a..2ae97afe7 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/matcher/ExtendsTypeMatcher.java +++ b/structurizr-component/src/main/java/com/structurizr/component/matcher/ExtendsTypeMatcher.java @@ -37,4 +37,11 @@ public boolean matches(Type type) { return false; } + @Override + public String toString() { + return "ExtendsTypeMatcher{" + + "className='" + className + '\'' + + '}'; + } + } \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/matcher/ImplementsTypeMatcher.java b/structurizr-component/src/main/java/com/structurizr/component/matcher/ImplementsTypeMatcher.java index 918bcd344..643a6e361 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/matcher/ImplementsTypeMatcher.java +++ b/structurizr-component/src/main/java/com/structurizr/component/matcher/ImplementsTypeMatcher.java @@ -29,4 +29,11 @@ public boolean matches(Type type) { return interfaceNames.contains(interfaceName); } + @Override + public String toString() { + return "ImplementsTypeMatcher{" + + "interfaceName='" + interfaceName + '\'' + + '}'; + } + } \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/matcher/NameSuffixTypeMatcher.java b/structurizr-component/src/main/java/com/structurizr/component/matcher/NameSuffixTypeMatcher.java index 41bdd76be..e1a17b5db 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/matcher/NameSuffixTypeMatcher.java +++ b/structurizr-component/src/main/java/com/structurizr/component/matcher/NameSuffixTypeMatcher.java @@ -24,4 +24,11 @@ public boolean matches(Type type) { return type.getFullyQualifiedName().endsWith(suffix); } + @Override + public String toString() { + return "NameSuffixTypeMatcher{" + + "suffix='" + suffix + '\'' + + '}'; + } + } diff --git a/structurizr-component/src/main/java/com/structurizr/component/matcher/RegexTypeMatcher.java b/structurizr-component/src/main/java/com/structurizr/component/matcher/RegexTypeMatcher.java index e39bbf737..565606102 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/matcher/RegexTypeMatcher.java +++ b/structurizr-component/src/main/java/com/structurizr/component/matcher/RegexTypeMatcher.java @@ -40,4 +40,11 @@ public boolean matches(Type type) { } } + @Override + public String toString() { + return "RegexTypeMatcher{" + + "regex=" + regex + + '}'; + } + } diff --git a/structurizr-component/src/main/java/com/structurizr/component/naming/CommonsLangCamelCaseNamingStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/naming/CommonsLangCamelCaseNamingStrategy.java deleted file mode 100644 index 5ee05339c..000000000 --- a/structurizr-component/src/main/java/com/structurizr/component/naming/CommonsLangCamelCaseNamingStrategy.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.structurizr.component.naming; - -import com.structurizr.component.Type; - -/** - * Uses Apache commons-lang to split a camel/Pascal cased name into separate words - * (e.g. "CustomerRepository" -> "Customer Repository"). - */ -public class CommonsLangCamelCaseNamingStrategy implements NamingStrategy { - - @Override - public String nameOf(Type type) { - String[] parts = org.apache.commons.lang3.StringUtils.splitByCharacterTypeCamelCase(type.getName()); - return String.join(" ", parts); - } - -} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/naming/DefaultNamingStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/naming/DefaultNamingStrategy.java index 5c3d126b2..622060ca0 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/naming/DefaultNamingStrategy.java +++ b/structurizr-component/src/main/java/com/structurizr/component/naming/DefaultNamingStrategy.java @@ -3,12 +3,15 @@ import com.structurizr.component.Type; /** - * Uses the simple/short name of the type (i.e. without the package name). + * Uses Apache commons-lang to split a camel/Pascal cased name into separate words + * (e.g. "CustomerRepository" -> "Customer Repository"). */ public class DefaultNamingStrategy implements NamingStrategy { + @Override public String nameOf(Type type) { - return type.getName(); + String[] parts = org.apache.commons.lang3.StringUtils.splitByCharacterTypeCamelCase(type.getName()); + return String.join(" ", parts); } -} +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/naming/SimpleNamingStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/naming/SimpleNamingStrategy.java new file mode 100644 index 000000000..7b60b58de --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/naming/SimpleNamingStrategy.java @@ -0,0 +1,14 @@ +package com.structurizr.component.naming; + +import com.structurizr.component.Type; + +/** + * Uses the simple/short name of the type (i.e. without the package name). + */ +public class SimpleNamingStrategy implements NamingStrategy { + + public String nameOf(Type type) { + return type.getName(); + } + +} diff --git a/structurizr-component/src/test/java/com/structurizr/component/example/Example.java b/structurizr-component/src/test/java/com/structurizr/component/example/Example.java index fe13a91a8..7172f5fdb 100644 --- a/structurizr-component/src/test/java/com/structurizr/component/example/Example.java +++ b/structurizr-component/src/test/java/com/structurizr/component/example/Example.java @@ -4,7 +4,7 @@ import com.structurizr.component.ComponentFinderBuilder; import com.structurizr.component.ComponentFinderStrategyBuilder; import com.structurizr.component.matcher.NameSuffixTypeMatcher; -import com.structurizr.component.naming.CommonsLangCamelCaseNamingStrategy; +import com.structurizr.component.naming.DefaultNamingStrategy; import com.structurizr.model.*; public class Example { @@ -25,14 +25,12 @@ public static void main(String[] args) { .withStrategy( new ComponentFinderStrategyBuilder() .matchedBy(new NameSuffixTypeMatcher("Controller", "Web MVC Controller")) - .namedBy(new CommonsLangCamelCaseNamingStrategy()) .forEach(component -> user.uses(component, "Uses")) .build() ) .withStrategy( new ComponentFinderStrategyBuilder() .matchedBy(new NameSuffixTypeMatcher("Repository", "Data Repository")) - .namedBy(new CommonsLangCamelCaseNamingStrategy()) .forEach(component -> component.uses(databaseSchema, "Reads from and writes to")) .build() ) From 3d7e92b4a9f04a9074efaa57a393dc459aa432ad Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 18 Aug 2024 22:49:23 +0100 Subject: [PATCH 524/717] structurizr-dsl: Adds support for element technology expressions (e.g. "element.technology==Java"). Also some refactoring. --- changelog.md | 1 + .../dsl/CustomViewExpressionParser.java | 2 +- .../dsl/DeploymentViewExpressionParser.java | 2 +- ...ssionParser.java => ExpressionParser.java} | 74 ++++++++++++++++--- .../dsl/ModelViewContentParser.java | 2 +- .../dsl/StaticViewExpressionParser.java | 2 +- .../dsl/StructurizrDslExpressions.java | 2 + ...rTests.java => ExpressionParserTests.java} | 30 +++++++- 8 files changed, 98 insertions(+), 17 deletions(-) rename structurizr-dsl/src/main/java/com/structurizr/dsl/{AbstractExpressionParser.java => ExpressionParser.java} (75%) rename structurizr-dsl/src/test/java/com/structurizr/dsl/{AbstractExpressionParserTests.java => ExpressionParserTests.java} (94%) diff --git a/changelog.md b/changelog.md index fd80cc2b1..b5076e453 100644 --- a/changelog.md +++ b/changelog.md @@ -6,6 +6,7 @@ - structurizr-dsl: Adds name-value properties to dynamic view relationship views. - structurizr-dsl: Fixes https://github.com/structurizr/java/issues/312 (!include doesn't work with files encoded as UTF-8 BOM). - structurizr-dsl: Adds a way to explicitly specify the order of relationships in dynamic views. +- structurizr-dsl: Adds support for element technology expressions (e.g. "element.technology==Java"). ## 2.2.0 (2nd July 2024) diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomViewExpressionParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomViewExpressionParser.java index 585a23899..16e5be56c 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomViewExpressionParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomViewExpressionParser.java @@ -8,7 +8,7 @@ import static com.structurizr.dsl.StructurizrDslExpressions.ELEMENT_TYPE_EQUALS_EXPRESSION; -final class CustomViewExpressionParser extends AbstractExpressionParser { +final class CustomViewExpressionParser extends ExpressionParser { @Override protected Set evaluateElementTypeExpression(String expr, DslContext context) { diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentViewExpressionParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentViewExpressionParser.java index 1919888d4..33e9a5b37 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentViewExpressionParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentViewExpressionParser.java @@ -7,7 +7,7 @@ import static com.structurizr.dsl.StructurizrDslExpressions.ELEMENT_TYPE_EQUALS_EXPRESSION; -final class DeploymentViewExpressionParser extends AbstractExpressionParser { +final class DeploymentViewExpressionParser extends ExpressionParser { @Override protected Set evaluateElementTypeExpression(String expr, DslContext context) { diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractExpressionParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ExpressionParser.java similarity index 75% rename from structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractExpressionParser.java rename to structurizr-dsl/src/main/java/com/structurizr/dsl/ExpressionParser.java index 181d51db8..9599c79f1 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractExpressionParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ExpressionParser.java @@ -1,9 +1,6 @@ package com.structurizr.dsl; -import com.structurizr.model.Element; -import com.structurizr.model.ModelItem; -import com.structurizr.model.Relationship; -import com.structurizr.model.StaticStructureElementInstance; +import com.structurizr.model.*; import com.structurizr.util.StringUtils; import java.util.HashSet; @@ -13,7 +10,7 @@ import static com.structurizr.dsl.StructurizrDslExpressions.*; -abstract class AbstractExpressionParser { +class ExpressionParser { private static final String WILDCARD = "*"; @@ -24,6 +21,8 @@ static boolean isExpression(String token) { token.startsWith(ELEMENT_TYPE_EQUALS_EXPRESSION.toLowerCase()) || token.startsWith(ELEMENT_TAG_EQUALS_EXPRESSION.toLowerCase()) || token.startsWith(ELEMENT_TAG_NOT_EQUALS_EXPRESSION.toLowerCase()) || + token.startsWith(ELEMENT_TECHNOLOGY_EQUALS_EXPRESSION.toLowerCase()) || + token.startsWith(ELEMENT_TECHNOLOGY_NOT_EQUALS_EXPRESSION.toLowerCase()) || token.matches(ELEMENT_PROPERTY_EQUALS_EXPRESSION) || token.startsWith(ELEMENT_PARENT_EQUALS_EXPRESSION.toLowerCase()) || token.startsWith(RELATIONSHIP) || token.endsWith(RELATIONSHIP) || token.contains(RELATIONSHIP) || @@ -52,10 +51,10 @@ final Set parseExpression(String expr, DslContext context) { Set modelItems1 = evaluateExpression(expressions[0], context); Set modelItems2 = evaluateExpression(expressions[1], context); - Set elements = new HashSet<>(modelItems1); - elements.addAll(modelItems2); + Set modelItems = new HashSet<>(modelItems1); + modelItems.addAll(modelItems2); - return elements; + return modelItems; } else { return evaluateExpression(expr, context); } @@ -171,6 +170,20 @@ private Set evaluateExpression(String expr, DslContext context) { modelItems.add(element); } }); + } else if (expr.toLowerCase().startsWith(ELEMENT_TECHNOLOGY_EQUALS_EXPRESSION.toLowerCase())) { + String technology = expr.substring(ELEMENT_TECHNOLOGY_EQUALS_EXPRESSION.length()); + modelItems.addAll(context.getWorkspace().getModel().getElements().stream().filter(e -> e instanceof Container).map(e -> (Container)e).filter(c -> technology.equals(c.getTechnology())).collect(Collectors.toSet())); + modelItems.addAll(context.getWorkspace().getModel().getElements().stream().filter(e -> e instanceof Component).map(e -> (Component)e).filter(c -> technology.equals(c.getTechnology())).collect(Collectors.toSet())); + modelItems.addAll(context.getWorkspace().getModel().getElements().stream().filter(e -> e instanceof DeploymentNode).map(e -> (DeploymentNode)e).filter(dn -> technology.equals(dn.getTechnology())).collect(Collectors.toSet())); + modelItems.addAll(context.getWorkspace().getModel().getElements().stream().filter(e -> e instanceof InfrastructureNode).map(e -> (InfrastructureNode)e).filter(in -> technology.equals(in.getTechnology())).collect(Collectors.toSet())); + modelItems.addAll(context.getWorkspace().getModel().getElements().stream().filter(e -> e instanceof ContainerInstance).map(e -> (ContainerInstance)e).filter(c -> technology.equals(c.getContainer().getTechnology())).collect(Collectors.toSet())); + } else if (expr.toLowerCase().startsWith(ELEMENT_TECHNOLOGY_NOT_EQUALS_EXPRESSION)) { + String technology = expr.substring(ELEMENT_TECHNOLOGY_NOT_EQUALS_EXPRESSION.length()); + modelItems.addAll(context.getWorkspace().getModel().getElements().stream().filter(e -> e instanceof Container).map(e -> (Container)e).filter(c -> !technology.equals(c.getTechnology())).collect(Collectors.toSet())); + modelItems.addAll(context.getWorkspace().getModel().getElements().stream().filter(e -> e instanceof Component).map(e -> (Component)e).filter(c -> !technology.equals(c.getTechnology())).collect(Collectors.toSet())); + modelItems.addAll(context.getWorkspace().getModel().getElements().stream().filter(e -> e instanceof DeploymentNode).map(e -> (DeploymentNode)e).filter(dn -> !technology.equals(dn.getTechnology())).collect(Collectors.toSet())); + modelItems.addAll(context.getWorkspace().getModel().getElements().stream().filter(e -> e instanceof InfrastructureNode).map(e -> (InfrastructureNode)e).filter(in -> !technology.equals(in.getTechnology())).collect(Collectors.toSet())); + modelItems.addAll(context.getWorkspace().getModel().getElements().stream().filter(e -> e instanceof ContainerInstance).map(e -> (ContainerInstance)e).filter(c -> !technology.equals(c.getContainer().getTechnology())).collect(Collectors.toSet())); } else if (expr.matches(ELEMENT_PROPERTY_EQUALS_EXPRESSION)) { String propertyName = expr.substring(expr.indexOf("[")+1, expr.indexOf("]")); String propertyValue = expr.substring(expr.indexOf("==")+2); @@ -266,7 +279,42 @@ private Set evaluateExpression(String expr, DslContext context) { return modelItems; } - protected abstract Set evaluateElementTypeExpression(String expr, DslContext context); + protected Set evaluateElementTypeExpression(String expr, DslContext context) { + Set elements = new LinkedHashSet<>(); + + String type = expr.substring(ELEMENT_TYPE_EQUALS_EXPRESSION.length()); + switch (type.toLowerCase()) { + case "custom": + context.getWorkspace().getModel().getElements().stream().filter(e -> e instanceof CustomElement).forEach(elements::add); + break; + case "person": + context.getWorkspace().getModel().getElements().stream().filter(e -> e instanceof Person).forEach(elements::add); + break; + case "softwaresystem": + context.getWorkspace().getModel().getElements().stream().filter(e -> e instanceof SoftwareSystem).forEach(elements::add); + break; + case "container": + context.getWorkspace().getModel().getElements().stream().filter(e -> e instanceof Container).forEach(elements::add); + break; + case "component": + context.getWorkspace().getModel().getElements().stream().filter(e -> e instanceof Component).forEach(elements::add); + break; + case "deploymentnode": + context.getWorkspace().getModel().getElements().stream().filter(e -> e instanceof DeploymentNode).forEach(elements::add); + break; + case "infrastructurenode": + context.getWorkspace().getModel().getElements().stream().filter(e -> e instanceof InfrastructureNode).forEach(elements::add); + break; + case "softwaresysteminstance": + context.getWorkspace().getModel().getElements().stream().filter(e -> e instanceof SoftwareSystemInstance).forEach(elements::add); + break; + case "containerinstance": + context.getWorkspace().getModel().getElements().stream().filter(e -> e instanceof ContainerInstance).forEach(elements::add); + break; + } + + return elements; + } private boolean hasAllTags(ModelItem modelItem, String[] tags) { boolean result = true; @@ -318,7 +366,9 @@ private boolean hasProperty(ModelItem modelItem, String name, String value) { return result; } - protected abstract Set findAfferentCouplings(Element element); + protected Set findAfferentCouplings(Element element) { + return new LinkedHashSet<>(findAfferentCouplings(element, Element.class)); + } protected Set findAfferentCouplings(Element element, Class typeOfElement) { Set elements = new LinkedHashSet<>(); @@ -331,7 +381,9 @@ protected Set findAfferentCouplings(Element element return elements; } - protected abstract Set findEfferentCouplings(Element element); + protected Set findEfferentCouplings(Element element) { + return new LinkedHashSet<>(findEfferentCouplings(element, Element.class)); + } protected Set findEfferentCouplings(Element element, Class typeOfElement) { Set elements = new LinkedHashSet<>(); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelViewContentParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelViewContentParser.java index 44212f52b..2f89808c0 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelViewContentParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelViewContentParser.java @@ -9,7 +9,7 @@ abstract class ModelViewContentParser extends AbstractParser { protected static final String ELEMENT_WILDCARD = "element==*"; protected boolean isExpression(String token) { - return AbstractExpressionParser.isExpression(token.toLowerCase()); + return ExpressionParser.isExpression(token.toLowerCase()); } protected void removeRelationshipFromView(Relationship relationship, ModelView view) { diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StaticViewExpressionParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StaticViewExpressionParser.java index 756dd3799..dfa32f147 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StaticViewExpressionParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StaticViewExpressionParser.java @@ -7,7 +7,7 @@ import static com.structurizr.dsl.StructurizrDslExpressions.ELEMENT_TYPE_EQUALS_EXPRESSION; -final class StaticViewExpressionParser extends AbstractExpressionParser { +final class StaticViewExpressionParser extends ExpressionParser { @Override protected Set evaluateElementTypeExpression(String expr, DslContext context) { diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslExpressions.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslExpressions.java index 4626e5934..a10aa0924 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslExpressions.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslExpressions.java @@ -3,6 +3,8 @@ class StructurizrDslExpressions { static final String ELEMENT_TYPE_EQUALS_EXPRESSION = "element.type=="; + static final String ELEMENT_TECHNOLOGY_EQUALS_EXPRESSION = "element.technology=="; + static final String ELEMENT_TECHNOLOGY_NOT_EQUALS_EXPRESSION = "element.technology!="; static final String ELEMENT_TAG_EQUALS_EXPRESSION = "element.tag=="; static final String ELEMENT_TAG_NOT_EQUALS_EXPRESSION = "element.tag!="; static final String ELEMENT_PROPERTY_EQUALS_EXPRESSION = "element\\.properties\\[.*]==.*"; diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/AbstractExpressionParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ExpressionParserTests.java similarity index 94% rename from structurizr-dsl/src/test/java/com/structurizr/dsl/AbstractExpressionParserTests.java rename to structurizr-dsl/src/test/java/com/structurizr/dsl/ExpressionParserTests.java index 32bf6fdfb..fcf91a830 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/AbstractExpressionParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ExpressionParserTests.java @@ -10,9 +10,9 @@ import static org.junit.jupiter.api.Assertions.*; -class AbstractExpressionParserTests extends AbstractTests { +class ExpressionParserTests extends AbstractTests { - private final StaticViewExpressionParser parser = new StaticViewExpressionParser(); + private final ExpressionParser parser = new ExpressionParser(); @Test void test_parseExpression_ThrowsAnException_WhenTheRelationshipSourceIsSpecifiedUsingLongSyntaxButDoesNotExist() { @@ -396,6 +396,32 @@ void test_parseExpression_ReturnsElementsAndElementInstances_WhenUsingAnElementT assertTrue(elements.contains(ssi)); // this is not tagged "Software System", but the element it's based upon is } + @Test + void test_parseExpression_ReturnsElementsAndElementInstances_WhenUsingAnElementTechnologyEqualsExpression() { + model.addPerson("User"); + SoftwareSystem ss = model.addSoftwareSystem("Software System"); + Container c = ss.addContainer("Container"); + c.setTechnology("Java"); + DeploymentNode dn = model.addDeploymentNode("DN"); + dn.setTechnology("EC2"); + InfrastructureNode in = dn.addInfrastructureNode("Infrastructure Node"); + in.setTechnology("ELB"); + ContainerInstance ci = dn.add(c); + + Set elements = parser.parseExpression("element.technology==Java", context()); + assertEquals(2, elements.size()); + assertTrue(elements.contains(c)); // this has a technology property of "Java" + assertTrue(elements.contains(ci)); // this has no technology property, but the element it's based upon is + + elements = parser.parseExpression("element.technology==EC2", context()); + assertEquals(1, elements.size()); + assertTrue(elements.contains(dn)); + + elements = parser.parseExpression("element.technology==ELB", context()); + assertEquals(1, elements.size()); + assertTrue(elements.contains(in)); + } + @Test void test_parseExpression_ReturnsElementsAndElementInstances_WhenUsingAnElementPropertyEqualsExpression() { SoftwareSystem a = model.addSoftwareSystem("A"); From 80cc375893af044215375ab50420bba19a837038 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 18 Aug 2024 22:49:47 +0100 Subject: [PATCH 525/717] Remove import. --- .../src/main/java/com/structurizr/dsl/ModelDslContext.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelDslContext.java index ab9ea3ad2..7903af346 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelDslContext.java @@ -1,7 +1,5 @@ package com.structurizr.dsl; -import com.structurizr.model.Location; - final class ModelDslContext extends GroupableDslContext { ModelDslContext() { From db3da9b269e9e6814449564647f9a5bb64ac3c16 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 18 Aug 2024 22:53:02 +0100 Subject: [PATCH 526/717] Adds a way to get constant values from the DSL parser. --- .../main/java/com/structurizr/dsl/StructurizrDslParser.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index 11633d6ec..50a2fc62f 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -1062,6 +1062,10 @@ private void registerIdentifier(String identifier, Relationship relationship) { relationship.addProperty(STRUCTURIZR_DSL_IDENTIFIER_PROPERTY_NAME, identifiersRegister.findIdentifier(relationship)); } + public String getConstant(String name) { + return constantsAndVariables.get(name).getValue(); + } + private boolean inContext(Class clazz) { if (contextStack.empty()) { return false; From f3786375dd783f05135e2a7edb572d39cff49e3e Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 19 Aug 2024 08:12:00 +0100 Subject: [PATCH 527/717] Tidy and some tests. --- .../java/com/structurizr/component/Type.java | 40 +++++++++---------- .../com/structurizr/component/TypeTests.java | 23 +++++++++++ .../component/example/Example.java | 1 - ...mePackageSupportingTypesStrategyTests.java | 29 ++++++++++++++ ...ncedTypesSupportingTypesStrategyTests.java | 30 ++++++++++++++ .../DefaultSupportingTypesStrategyTests.java | 21 ++++++++++ 6 files changed, 123 insertions(+), 21 deletions(-) create mode 100644 structurizr-component/src/test/java/com/structurizr/component/TypeTests.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/supporting/AllReferencedTypesInSamePackageSupportingTypesStrategyTests.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/supporting/AllReferencedTypesSupportingTypesStrategyTests.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/supporting/DefaultSupportingTypesStrategyTests.java diff --git a/structurizr-component/src/main/java/com/structurizr/component/Type.java b/structurizr-component/src/main/java/com/structurizr/component/Type.java index 33a355248..56c5cca3f 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/Type.java +++ b/structurizr-component/src/main/java/com/structurizr/component/Type.java @@ -3,6 +3,7 @@ import org.apache.bcel.classfile.JavaClass; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.Objects; import java.util.Set; @@ -11,21 +12,20 @@ */ public final class Type { - private JavaClass javaClass; + private final JavaClass javaClass; + private final String fullyQualifiedName; private String description; private String source; - private final Set dependencies = new HashSet<>(); - - private final String fullyQualifiedName; + private final Set dependencies = new LinkedHashSet<>(); public Type(JavaClass javaClass) { - this(javaClass.getClassName()); - + this.fullyQualifiedName = javaClass.getClassName(); this.javaClass = javaClass; } public Type(String fullyQualifiedName) { this.fullyQualifiedName = fullyQualifiedName; + this.javaClass = null; } public String getFullyQualifiedName() { @@ -56,19 +56,6 @@ public void setSource(String source) { this.source = source; } - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - Type type = (Type) o; - return fullyQualifiedName.equals(type.fullyQualifiedName); - } - - @Override - public int hashCode() { - return Objects.hash(fullyQualifiedName); - } - public JavaClass getJavaClass() { return this.javaClass; } @@ -78,13 +65,26 @@ public void addDependency(Type type) { } public Set getDependencies() { - return new HashSet<>(dependencies); + return new LinkedHashSet<>(dependencies); } public boolean isAbstract() { return javaClass.isAbstract(); } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Type type = (Type) o; + return fullyQualifiedName.equals(type.fullyQualifiedName); + } + + @Override + public int hashCode() { + return Objects.hash(fullyQualifiedName); + } + @Override public String toString() { return this.fullyQualifiedName; diff --git a/structurizr-component/src/test/java/com/structurizr/component/TypeTests.java b/structurizr-component/src/test/java/com/structurizr/component/TypeTests.java new file mode 100644 index 000000000..d73f761ba --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/TypeTests.java @@ -0,0 +1,23 @@ +package com.structurizr.component; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class TypeTests { + + private Type type; + + @Test + void name() { + type = new Type("com.example.ClassName"); + assertEquals("ClassName", type.getName()); + } + + @Test + void packageName() { + type = new Type("com.example.ClassName"); + assertEquals("com.example", type.getPackageName()); + } + +} \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/example/Example.java b/structurizr-component/src/test/java/com/structurizr/component/example/Example.java index 7172f5fdb..292199f39 100644 --- a/structurizr-component/src/test/java/com/structurizr/component/example/Example.java +++ b/structurizr-component/src/test/java/com/structurizr/component/example/Example.java @@ -4,7 +4,6 @@ import com.structurizr.component.ComponentFinderBuilder; import com.structurizr.component.ComponentFinderStrategyBuilder; import com.structurizr.component.matcher.NameSuffixTypeMatcher; -import com.structurizr.component.naming.DefaultNamingStrategy; import com.structurizr.model.*; public class Example { diff --git a/structurizr-component/src/test/java/com/structurizr/component/supporting/AllReferencedTypesInSamePackageSupportingTypesStrategyTests.java b/structurizr-component/src/test/java/com/structurizr/component/supporting/AllReferencedTypesInSamePackageSupportingTypesStrategyTests.java new file mode 100644 index 000000000..39db1619c --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/supporting/AllReferencedTypesInSamePackageSupportingTypesStrategyTests.java @@ -0,0 +1,29 @@ +package com.structurizr.component.supporting; + +import com.structurizr.component.Type; +import org.junit.jupiter.api.Test; + +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class AllReferencedTypesInSamePackageSupportingTypesStrategyTests { + + @Test + void findSupportingTypes() { + Type type = new Type("com.example.a.A"); + + Type dependency1 = new Type("com.example.a.AImpl"); + Type dependency2 = new Type("com.example.util.SomeUtils"); + type.addDependency(dependency1); + type.addDependency(dependency2); + + type.addDependency(new Type("com.example.util.SomeUtils")); + + Set supportingTypes = new AllReferencedTypesInSamePackageSupportingTypesStrategy().findSupportingTypes(type); + assertEquals(1, supportingTypes.size()); + assertTrue(supportingTypes.contains(dependency1)); + } + +} \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/supporting/AllReferencedTypesSupportingTypesStrategyTests.java b/structurizr-component/src/test/java/com/structurizr/component/supporting/AllReferencedTypesSupportingTypesStrategyTests.java new file mode 100644 index 000000000..e0afc769a --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/supporting/AllReferencedTypesSupportingTypesStrategyTests.java @@ -0,0 +1,30 @@ +package com.structurizr.component.supporting; + +import com.structurizr.component.Type; +import org.junit.jupiter.api.Test; + +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class AllReferencedTypesSupportingTypesStrategyTests { + + @Test + void findSupportingTypes() { + Type type = new Type("com.example.a.A"); + + Type dependency1 = new Type("com.example.a.AImpl"); + Type dependency2 = new Type("com.example.util.SomeUtils"); + type.addDependency(dependency1); + type.addDependency(dependency2); + + type.addDependency(new Type("com.example.util.SomeUtils")); + + Set supportingTypes = new AllReferencedTypesSupportingTypesStrategy().findSupportingTypes(type); + assertEquals(2, supportingTypes.size()); + assertTrue(supportingTypes.contains(dependency1)); + assertTrue(supportingTypes.contains(dependency2)); + } + +} \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/supporting/DefaultSupportingTypesStrategyTests.java b/structurizr-component/src/test/java/com/structurizr/component/supporting/DefaultSupportingTypesStrategyTests.java new file mode 100644 index 000000000..6ff35a779 --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/supporting/DefaultSupportingTypesStrategyTests.java @@ -0,0 +1,21 @@ +package com.structurizr.component.supporting; + +import com.structurizr.component.Type; +import org.junit.jupiter.api.Test; + +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class DefaultSupportingTypesStrategyTests { + + @Test + void findSupportingTypes() { + Type type = new Type("com.example.a.A"); + type.addDependency(new Type("com.example.a.AImpl")); + + Set supportingTypes = new DefaultSupportingTypesStrategy().findSupportingTypes(type); + assertTrue(supportingTypes.isEmpty()); + } + +} \ No newline at end of file From 4bab88ff95d86049116bdb38f9d1f71336b645a7 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 19 Aug 2024 08:31:11 +0100 Subject: [PATCH 528/717] Tidy and more tests. --- .../matcher/AnnotationTypeMatcher.java | 8 +++++ .../component/matcher/ExtendsTypeMatcher.java | 8 +++++ .../matcher/ImplementsTypeMatcher.java | 8 +++++ .../matcher/NameSuffixTypeMatcher.java | 4 +++ .../component/matcher/RegexTypeMatcher.java | 21 ++++------- .../matcher/NameSuffixTypeMatcherTests.java | 36 +++++++++++++++++++ .../matcher/RegexSuffixTypeMatcherTests.java | 36 +++++++++++++++++++ .../naming/DefaultNamingStrategyTests.java | 15 ++++++++ .../FullyQualifiedNamingStrategyTests.java | 15 ++++++++ .../naming/SimpleNamingStrategyTests.java | 15 ++++++++ 10 files changed, 151 insertions(+), 15 deletions(-) create mode 100644 structurizr-component/src/test/java/com/structurizr/component/matcher/NameSuffixTypeMatcherTests.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/matcher/RegexSuffixTypeMatcherTests.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/naming/DefaultNamingStrategyTests.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/naming/FullyQualifiedNamingStrategyTests.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/naming/SimpleNamingStrategyTests.java diff --git a/structurizr-component/src/main/java/com/structurizr/component/matcher/AnnotationTypeMatcher.java b/structurizr-component/src/main/java/com/structurizr/component/matcher/AnnotationTypeMatcher.java index 2674204d8..671fc2b42 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/matcher/AnnotationTypeMatcher.java +++ b/structurizr-component/src/main/java/com/structurizr/component/matcher/AnnotationTypeMatcher.java @@ -35,6 +35,14 @@ public AnnotationTypeMatcher(Class annotation, String tech @Override public boolean matches(Type type) { + if (type == null) { + throw new IllegalArgumentException("A type must be specified"); + } + + if (type.getJavaClass() == null) { + throw new IllegalArgumentException("This type matcher requires a BCEL JavaClass"); + } + AnnotationEntry[] annotationEntries = type.getJavaClass().getAnnotationEntries(); for (AnnotationEntry annotationEntry : annotationEntries) { if (annotationType.equals(annotationEntry.getAnnotationType())) { diff --git a/structurizr-component/src/main/java/com/structurizr/component/matcher/ExtendsTypeMatcher.java b/structurizr-component/src/main/java/com/structurizr/component/matcher/ExtendsTypeMatcher.java index 2ae97afe7..497f6cde3 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/matcher/ExtendsTypeMatcher.java +++ b/structurizr-component/src/main/java/com/structurizr/component/matcher/ExtendsTypeMatcher.java @@ -26,6 +26,14 @@ public ExtendsTypeMatcher(String className, String technology) { @Override public boolean matches(Type type) { + if (type == null) { + throw new IllegalArgumentException("A type must be specified"); + } + + if (type.getJavaClass() == null) { + throw new IllegalArgumentException("This type matcher requires a BCEL JavaClass"); + } + JavaClass javaClass = type.getJavaClass(); try { Set superClasses = Stream.of(javaClass.getSuperClasses()).map(JavaClass::getClassName).collect(Collectors.toSet()); diff --git a/structurizr-component/src/main/java/com/structurizr/component/matcher/ImplementsTypeMatcher.java b/structurizr-component/src/main/java/com/structurizr/component/matcher/ImplementsTypeMatcher.java index 643a6e361..91fc9b624 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/matcher/ImplementsTypeMatcher.java +++ b/structurizr-component/src/main/java/com/structurizr/component/matcher/ImplementsTypeMatcher.java @@ -24,6 +24,14 @@ public ImplementsTypeMatcher(String interfaceName, String technology) { @Override public boolean matches(Type type) { + if (type == null) { + throw new IllegalArgumentException("A type must be specified"); + } + + if (type.getJavaClass() == null) { + throw new IllegalArgumentException("This type matcher requires a BCEL JavaClass"); + } + JavaClass javaClass = type.getJavaClass(); Set interfaceNames = Set.of(javaClass.getInterfaceNames()); return interfaceNames.contains(interfaceName); diff --git a/structurizr-component/src/main/java/com/structurizr/component/matcher/NameSuffixTypeMatcher.java b/structurizr-component/src/main/java/com/structurizr/component/matcher/NameSuffixTypeMatcher.java index e1a17b5db..01f27091a 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/matcher/NameSuffixTypeMatcher.java +++ b/structurizr-component/src/main/java/com/structurizr/component/matcher/NameSuffixTypeMatcher.java @@ -21,6 +21,10 @@ public NameSuffixTypeMatcher(String suffix, String technology) { @Override public boolean matches(Type type) { + if (type == null) { + throw new IllegalArgumentException("A type must be specified"); + } + return type.getFullyQualifiedName().endsWith(suffix); } diff --git a/structurizr-component/src/main/java/com/structurizr/component/matcher/RegexTypeMatcher.java b/structurizr-component/src/main/java/com/structurizr/component/matcher/RegexTypeMatcher.java index 565606102..0f9c8a8f9 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/matcher/RegexTypeMatcher.java +++ b/structurizr-component/src/main/java/com/structurizr/component/matcher/RegexTypeMatcher.java @@ -1,6 +1,7 @@ package com.structurizr.component.matcher; import com.structurizr.component.Type; +import com.structurizr.util.StringUtils; import java.util.regex.Pattern; @@ -14,30 +15,20 @@ public class RegexTypeMatcher extends AbstractTypeMatcher { public RegexTypeMatcher(String regex, String technology) { super(technology); - if (regex == null) { + if (StringUtils.isNullOrEmpty(regex)) { throw new IllegalArgumentException("A regex must be supplied"); } this.regex = Pattern.compile(regex); } - public RegexTypeMatcher(Pattern regex, String technology) { - super(technology); - - if (regex == null) { - throw new IllegalArgumentException("A regex must be supplied"); - } - - this.regex = regex; - } - @Override public boolean matches(Type type) { - if (type != null && type.getFullyQualifiedName() != null) { - return Pattern.matches(regex.pattern(), type.getFullyQualifiedName()); - } else { - return false; + if (type == null) { + throw new IllegalArgumentException("A type must be specified"); } + + return Pattern.matches(regex.pattern(), type.getFullyQualifiedName()); } @Override diff --git a/structurizr-component/src/test/java/com/structurizr/component/matcher/NameSuffixTypeMatcherTests.java b/structurizr-component/src/test/java/com/structurizr/component/matcher/NameSuffixTypeMatcherTests.java new file mode 100644 index 000000000..80dff112e --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/matcher/NameSuffixTypeMatcherTests.java @@ -0,0 +1,36 @@ +package com.structurizr.component.matcher; + +import com.structurizr.component.Type; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class NameSuffixTypeMatcherTests { + + @Test + void construction_ThrowsAnException_WhenPassedANullSuffix() { + assertThrowsExactly(IllegalArgumentException.class, () -> new NameSuffixTypeMatcher(null, "Technology")); + } + + @Test + void construction_ThrowsAnException_WhenPassedAnEmptySuffix() { + assertThrowsExactly(IllegalArgumentException.class, () -> new NameSuffixTypeMatcher("", "Technology")); + assertThrowsExactly(IllegalArgumentException.class, () -> new NameSuffixTypeMatcher(" ", "Technology")); + } + + @Test + void matches_ThrowsAnException_WhenPassedNull() { + assertThrowsExactly(IllegalArgumentException.class, () -> new NameSuffixTypeMatcher("Suffix", "Technology").matches(null)); + } + + @Test + void matches_ReturnsFalse_WhenThereIsNoMatch() { + assertFalse(new NameSuffixTypeMatcher("Component", "Technology").matches(new Type("com.example.SomeClass"))); + } + + @Test + void matches_ReturnsTrue_WhenThereIsAMatch() { + assertTrue(new NameSuffixTypeMatcher("Component", "Technology").matches(new Type("com.example.SomeComponent"))); + } + +} \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/matcher/RegexSuffixTypeMatcherTests.java b/structurizr-component/src/test/java/com/structurizr/component/matcher/RegexSuffixTypeMatcherTests.java new file mode 100644 index 000000000..a54da7b7c --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/matcher/RegexSuffixTypeMatcherTests.java @@ -0,0 +1,36 @@ +package com.structurizr.component.matcher; + +import com.structurizr.component.Type; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class RegexSuffixTypeMatcherTests { + + @Test + void construction_ThrowsAnException_WhenPassedANullRegex() { + assertThrowsExactly(IllegalArgumentException.class, () -> new RegexTypeMatcher(null, "Technology")); + } + + @Test + void construction_ThrowsAnException_WhenPassedAnEmptyRegex() { + assertThrowsExactly(IllegalArgumentException.class, () -> new RegexTypeMatcher("", "Technology")); + assertThrowsExactly(IllegalArgumentException.class, () -> new RegexTypeMatcher(" ", "Technology")); + } + + @Test + void matches_ThrowsAnException_WhenPassedNull() { + assertThrowsExactly(IllegalArgumentException.class, () -> new RegexTypeMatcher(".*Controller", "Technology").matches(null)); + } + + @Test + void matches_ReturnsFalse_WhenThereIsNoMatch() { + assertFalse(new RegexTypeMatcher(".*Controller", "Technology").matches(new Type("com.example.SomeClass"))); + } + + @Test + void matches_ReturnsTrue_WhenThereIsAMatch() { + assertTrue(new RegexTypeMatcher(".*Controller", "Technology").matches(new Type("com.example.SomeController"))); + } + +} \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/naming/DefaultNamingStrategyTests.java b/structurizr-component/src/test/java/com/structurizr/component/naming/DefaultNamingStrategyTests.java new file mode 100644 index 000000000..f6f8f726d --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/naming/DefaultNamingStrategyTests.java @@ -0,0 +1,15 @@ +package com.structurizr.component.naming; + +import com.structurizr.component.Type; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class DefaultNamingStrategyTests { + + @Test + void nameOf() { + assertEquals("Class Name", new DefaultNamingStrategy().nameOf(new Type("com.example.ClassName"))); + } + +} \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/naming/FullyQualifiedNamingStrategyTests.java b/structurizr-component/src/test/java/com/structurizr/component/naming/FullyQualifiedNamingStrategyTests.java new file mode 100644 index 000000000..c3d72fa2e --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/naming/FullyQualifiedNamingStrategyTests.java @@ -0,0 +1,15 @@ +package com.structurizr.component.naming; + +import com.structurizr.component.Type; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class FullyQualifiedNamingStrategyTests { + + @Test + void nameOf() { + assertEquals("com.example.ClassName", new FullyQualifiedNamingStrategy().nameOf(new Type("com.example.ClassName"))); + } + +} \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/naming/SimpleNamingStrategyTests.java b/structurizr-component/src/test/java/com/structurizr/component/naming/SimpleNamingStrategyTests.java new file mode 100644 index 000000000..a85aae823 --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/naming/SimpleNamingStrategyTests.java @@ -0,0 +1,15 @@ +package com.structurizr.component.naming; + +import com.structurizr.component.Type; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class SimpleNamingStrategyTests { + + @Test + void nameOf() { + assertEquals("ClassName", new SimpleNamingStrategy().nameOf(new Type("com.example.ClassName"))); + } + +} \ No newline at end of file From 3db0c028e4d996dc644cb4d826e52791a65a939a Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 19 Aug 2024 11:40:32 +0100 Subject: [PATCH 529/717] Some tweaks to support a Spring Modulith style approach, where packages are considered to be components. --- .../component/ComponentFinder.java | 7 ++++-- .../structurizr/component/TypeRepository.java | 2 +- .../matcher/AnnotationTypeMatcher.java | 2 +- .../component/matcher/ExtendsTypeMatcher.java | 2 +- .../matcher/ImplementsTypeMatcher.java | 2 +- .../naming/DefaultPackageNamingStrategy.java | 25 +++++++++++++++++++ .../provider/SourceDirectoryTypeProvider.java | 24 ++++++++++++++++++ ...ypesInPackageSupportingTypesStrategy.java} | 5 ++-- ...eferencedTypesSupportingTypesStrategy.java | 3 ++- ...TypesInPackageSupportingTypesStrategy.java | 23 +++++++++++++++++ ...esUnderPackageSupportingTypesStrategy.java | 23 +++++++++++++++++ .../DefaultSupportingTypesStrategy.java | 3 ++- .../supporting/SupportingTypesStrategy.java | 3 ++- .../DefaultPackageNamingStrategyTests.java | 15 +++++++++++ ...nPackageSupportingTypesStrategyTests.java} | 4 +-- ...ncedTypesSupportingTypesStrategyTests.java | 2 +- .../DefaultSupportingTypesStrategyTests.java | 2 +- 17 files changed, 132 insertions(+), 15 deletions(-) create mode 100644 structurizr-component/src/main/java/com/structurizr/component/naming/DefaultPackageNamingStrategy.java rename structurizr-component/src/main/java/com/structurizr/component/supporting/{AllReferencedTypesInSamePackageSupportingTypesStrategy.java => AllReferencedTypesInPackageSupportingTypesStrategy.java} (68%) create mode 100644 structurizr-component/src/main/java/com/structurizr/component/supporting/AllTypesInPackageSupportingTypesStrategy.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/supporting/AllTypesUnderPackageSupportingTypesStrategy.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/naming/DefaultPackageNamingStrategyTests.java rename structurizr-component/src/test/java/com/structurizr/component/supporting/{AllReferencedTypesInSamePackageSupportingTypesStrategyTests.java => AllReferencedTypesInPackageSupportingTypesStrategyTests.java} (79%) diff --git a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinder.java b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinder.java index b12b7f04c..ef6edac08 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinder.java +++ b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinder.java @@ -34,7 +34,12 @@ public final class ComponentFinder { } this.container = container; + this.componentFinderStrategies.addAll(componentFinderStrategies); + + findTypes(typeProviders); + } + private void findTypes(Collection typeProviders) { for (TypeProvider typeProvider : typeProviders) { Set types = typeProvider.getTypes(); for (com.structurizr.component.Type type : types) { @@ -59,8 +64,6 @@ public final class ComponentFinder { findDependencies(type); } } - - this.componentFinderStrategies.addAll(componentFinderStrategies); } private void findDependencies(com.structurizr.component.Type type) { diff --git a/structurizr-component/src/main/java/com/structurizr/component/TypeRepository.java b/structurizr-component/src/main/java/com/structurizr/component/TypeRepository.java index 45f80f365..faa707b57 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/TypeRepository.java +++ b/structurizr-component/src/main/java/com/structurizr/component/TypeRepository.java @@ -3,7 +3,7 @@ import java.util.LinkedHashSet; import java.util.Set; -final class TypeRepository { +public final class TypeRepository { private final Set types = new LinkedHashSet<>(); diff --git a/structurizr-component/src/main/java/com/structurizr/component/matcher/AnnotationTypeMatcher.java b/structurizr-component/src/main/java/com/structurizr/component/matcher/AnnotationTypeMatcher.java index 671fc2b42..ceadefbf1 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/matcher/AnnotationTypeMatcher.java +++ b/structurizr-component/src/main/java/com/structurizr/component/matcher/AnnotationTypeMatcher.java @@ -40,7 +40,7 @@ public boolean matches(Type type) { } if (type.getJavaClass() == null) { - throw new IllegalArgumentException("This type matcher requires a BCEL JavaClass"); + return false; } AnnotationEntry[] annotationEntries = type.getJavaClass().getAnnotationEntries(); diff --git a/structurizr-component/src/main/java/com/structurizr/component/matcher/ExtendsTypeMatcher.java b/structurizr-component/src/main/java/com/structurizr/component/matcher/ExtendsTypeMatcher.java index 497f6cde3..ce402eb40 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/matcher/ExtendsTypeMatcher.java +++ b/structurizr-component/src/main/java/com/structurizr/component/matcher/ExtendsTypeMatcher.java @@ -31,7 +31,7 @@ public boolean matches(Type type) { } if (type.getJavaClass() == null) { - throw new IllegalArgumentException("This type matcher requires a BCEL JavaClass"); + return false; } JavaClass javaClass = type.getJavaClass(); diff --git a/structurizr-component/src/main/java/com/structurizr/component/matcher/ImplementsTypeMatcher.java b/structurizr-component/src/main/java/com/structurizr/component/matcher/ImplementsTypeMatcher.java index 91fc9b624..c06f73614 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/matcher/ImplementsTypeMatcher.java +++ b/structurizr-component/src/main/java/com/structurizr/component/matcher/ImplementsTypeMatcher.java @@ -29,7 +29,7 @@ public boolean matches(Type type) { } if (type.getJavaClass() == null) { - throw new IllegalArgumentException("This type matcher requires a BCEL JavaClass"); + return false; } JavaClass javaClass = type.getJavaClass(); diff --git a/structurizr-component/src/main/java/com/structurizr/component/naming/DefaultPackageNamingStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/naming/DefaultPackageNamingStrategy.java new file mode 100644 index 000000000..4f93efecd --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/naming/DefaultPackageNamingStrategy.java @@ -0,0 +1,25 @@ +package com.structurizr.component.naming; + +import com.structurizr.component.Type; + +/** + * Uses Apache commons-lang to split a camel-cased package name into separate words + * (e.g. "com.example.order.package-info" -> "Order"). + */ +public class DefaultPackageNamingStrategy implements NamingStrategy { + + @Override + public String nameOf(Type type) { + String packageName = type.getPackageName(); + if (packageName.contains(".")) { + packageName = packageName.substring(packageName.lastIndexOf(".") + 1); + } + + String[] parts = org.apache.commons.lang3.StringUtils.splitByCharacterTypeCamelCase(packageName); + String name = String.join(" ", parts); + name = name.substring(0, 1).toUpperCase() + name.substring(1); + + return name; + } + +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/provider/SourceDirectoryTypeProvider.java b/structurizr-component/src/main/java/com/structurizr/component/provider/SourceDirectoryTypeProvider.java index f68630a6c..808006c48 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/provider/SourceDirectoryTypeProvider.java +++ b/structurizr-component/src/main/java/com/structurizr/component/provider/SourceDirectoryTypeProvider.java @@ -2,6 +2,8 @@ import com.github.javaparser.ParserConfiguration; import com.github.javaparser.StaticJavaParser; +import com.github.javaparser.ast.Node; +import com.github.javaparser.ast.PackageDeclaration; import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; import com.github.javaparser.ast.comments.JavadocComment; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; @@ -69,6 +71,28 @@ public void visit(ClassOrInterfaceDeclaration n, Object arg) { type.setDescription(new JavadocCommentFilter(maximumDescriptionLength).filterAndTruncate(description)); } + types.add(type); + } + } + + @Override + public void visit(PackageDeclaration n, Object arg) { + String PACKAGE_INFO_JAVA_SOURCE = "package-info.java"; + String PACKAGE_INFO_SUFFIX = ".package-info"; + + if (path.getName().endsWith(PACKAGE_INFO_JAVA_SOURCE)) { + String fullyQualifiedName = n.getName().asString() + PACKAGE_INFO_SUFFIX; + + Type type = new Type(fullyQualifiedName); + type.setSource(path.getAbsolutePath()); + + Node rootNode = n.findRootNode(); + if (rootNode != null && rootNode.getComment().isPresent() && rootNode.getComment().get() instanceof JavadocComment) { + JavadocComment javadocComment = (JavadocComment)rootNode.getComment().get(); + String description = javadocComment.parse().getDescription().toText(); + + type.setDescription(new JavadocCommentFilter(maximumDescriptionLength).filterAndTruncate(description)); + } types.add(type); } diff --git a/structurizr-component/src/main/java/com/structurizr/component/supporting/AllReferencedTypesInSamePackageSupportingTypesStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/supporting/AllReferencedTypesInPackageSupportingTypesStrategy.java similarity index 68% rename from structurizr-component/src/main/java/com/structurizr/component/supporting/AllReferencedTypesInSamePackageSupportingTypesStrategy.java rename to structurizr-component/src/main/java/com/structurizr/component/supporting/AllReferencedTypesInPackageSupportingTypesStrategy.java index 5791d7943..2e34875e2 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/supporting/AllReferencedTypesInSamePackageSupportingTypesStrategy.java +++ b/structurizr-component/src/main/java/com/structurizr/component/supporting/AllReferencedTypesInPackageSupportingTypesStrategy.java @@ -1,6 +1,7 @@ package com.structurizr.component.supporting; import com.structurizr.component.Type; +import com.structurizr.component.TypeRepository; import java.util.Set; import java.util.stream.Collectors; @@ -8,10 +9,10 @@ /** * A strategy that finds all referenced types in the same package as the component type. */ -public class AllReferencedTypesInSamePackageSupportingTypesStrategy implements SupportingTypesStrategy { +public class AllReferencedTypesInPackageSupportingTypesStrategy implements SupportingTypesStrategy { @Override - public Set findSupportingTypes(Type type) { + public Set findSupportingTypes(Type type, TypeRepository typeRepository) { String packageName = type.getPackageName(); return type.getDependencies().stream() diff --git a/structurizr-component/src/main/java/com/structurizr/component/supporting/AllReferencedTypesSupportingTypesStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/supporting/AllReferencedTypesSupportingTypesStrategy.java index 28738a7aa..e2b4724ce 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/supporting/AllReferencedTypesSupportingTypesStrategy.java +++ b/structurizr-component/src/main/java/com/structurizr/component/supporting/AllReferencedTypesSupportingTypesStrategy.java @@ -1,6 +1,7 @@ package com.structurizr.component.supporting; import com.structurizr.component.Type; +import com.structurizr.component.TypeRepository; import java.util.Set; @@ -10,7 +11,7 @@ public class AllReferencedTypesSupportingTypesStrategy implements SupportingTypesStrategy { @Override - public Set findSupportingTypes(Type type) { + public Set findSupportingTypes(Type type, TypeRepository typeRepository) { return type.getDependencies(); } diff --git a/structurizr-component/src/main/java/com/structurizr/component/supporting/AllTypesInPackageSupportingTypesStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/supporting/AllTypesInPackageSupportingTypesStrategy.java new file mode 100644 index 000000000..cc998f5ed --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/supporting/AllTypesInPackageSupportingTypesStrategy.java @@ -0,0 +1,23 @@ +package com.structurizr.component.supporting; + +import com.structurizr.component.Type; +import com.structurizr.component.TypeRepository; + +import java.util.Set; +import java.util.stream.Collectors; + +/** + * A strategy that finds all referenced types in the same package as the component type. + */ +public class AllTypesInPackageSupportingTypesStrategy implements SupportingTypesStrategy { + + @Override + public Set findSupportingTypes(Type type, TypeRepository typeRepository) { + String packageName = type.getPackageName(); + + return typeRepository.getTypes().stream() + .filter(dependency -> dependency.getPackageName().equals(packageName)) + .collect(Collectors.toSet()); + } + +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/supporting/AllTypesUnderPackageSupportingTypesStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/supporting/AllTypesUnderPackageSupportingTypesStrategy.java new file mode 100644 index 000000000..59f7a2a3a --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/supporting/AllTypesUnderPackageSupportingTypesStrategy.java @@ -0,0 +1,23 @@ +package com.structurizr.component.supporting; + +import com.structurizr.component.Type; +import com.structurizr.component.TypeRepository; + +import java.util.Set; +import java.util.stream.Collectors; + +/** + * A strategy that finds all referenced types in the same package as the component type. + */ +public class AllTypesUnderPackageSupportingTypesStrategy implements SupportingTypesStrategy { + + @Override + public Set findSupportingTypes(Type type, TypeRepository typeRepository) { + String packageName = type.getPackageName(); + + return typeRepository.getTypes().stream() + .filter(dependency -> dependency.getPackageName().startsWith(packageName)) + .collect(Collectors.toSet()); + } + +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/supporting/DefaultSupportingTypesStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/supporting/DefaultSupportingTypesStrategy.java index 58f1ec8cd..65639a110 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/supporting/DefaultSupportingTypesStrategy.java +++ b/structurizr-component/src/main/java/com/structurizr/component/supporting/DefaultSupportingTypesStrategy.java @@ -1,6 +1,7 @@ package com.structurizr.component.supporting; import com.structurizr.component.Type; +import com.structurizr.component.TypeRepository; import java.util.Collections; import java.util.Set; @@ -11,7 +12,7 @@ public class DefaultSupportingTypesStrategy implements SupportingTypesStrategy { @Override - public Set findSupportingTypes(Type type) { + public Set findSupportingTypes(Type type, TypeRepository typeRepository) { return Collections.emptySet(); } diff --git a/structurizr-component/src/main/java/com/structurizr/component/supporting/SupportingTypesStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/supporting/SupportingTypesStrategy.java index 1d226daca..e3d6f21a3 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/supporting/SupportingTypesStrategy.java +++ b/structurizr-component/src/main/java/com/structurizr/component/supporting/SupportingTypesStrategy.java @@ -1,6 +1,7 @@ package com.structurizr.component.supporting; import com.structurizr.component.Type; +import com.structurizr.component.TypeRepository; import java.util.Set; @@ -9,6 +10,6 @@ */ public interface SupportingTypesStrategy { - Set findSupportingTypes(Type type); + Set findSupportingTypes(Type type, TypeRepository typeRepository); } \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/naming/DefaultPackageNamingStrategyTests.java b/structurizr-component/src/test/java/com/structurizr/component/naming/DefaultPackageNamingStrategyTests.java new file mode 100644 index 000000000..7adf582ae --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/naming/DefaultPackageNamingStrategyTests.java @@ -0,0 +1,15 @@ +package com.structurizr.component.naming; + +import com.structurizr.component.Type; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class DefaultPackageNamingStrategyTests { + + @Test + void nameOf() { + assertEquals("Order Management", new DefaultPackageNamingStrategy().nameOf(new Type("com.example.orderManagement.package-info"))); + } + +} \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/supporting/AllReferencedTypesInSamePackageSupportingTypesStrategyTests.java b/structurizr-component/src/test/java/com/structurizr/component/supporting/AllReferencedTypesInPackageSupportingTypesStrategyTests.java similarity index 79% rename from structurizr-component/src/test/java/com/structurizr/component/supporting/AllReferencedTypesInSamePackageSupportingTypesStrategyTests.java rename to structurizr-component/src/test/java/com/structurizr/component/supporting/AllReferencedTypesInPackageSupportingTypesStrategyTests.java index 39db1619c..761b08d84 100644 --- a/structurizr-component/src/test/java/com/structurizr/component/supporting/AllReferencedTypesInSamePackageSupportingTypesStrategyTests.java +++ b/structurizr-component/src/test/java/com/structurizr/component/supporting/AllReferencedTypesInPackageSupportingTypesStrategyTests.java @@ -8,7 +8,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -public class AllReferencedTypesInSamePackageSupportingTypesStrategyTests { +public class AllReferencedTypesInPackageSupportingTypesStrategyTests { @Test void findSupportingTypes() { @@ -21,7 +21,7 @@ void findSupportingTypes() { type.addDependency(new Type("com.example.util.SomeUtils")); - Set supportingTypes = new AllReferencedTypesInSamePackageSupportingTypesStrategy().findSupportingTypes(type); + Set supportingTypes = new AllReferencedTypesInPackageSupportingTypesStrategy().findSupportingTypes(type, null); assertEquals(1, supportingTypes.size()); assertTrue(supportingTypes.contains(dependency1)); } diff --git a/structurizr-component/src/test/java/com/structurizr/component/supporting/AllReferencedTypesSupportingTypesStrategyTests.java b/structurizr-component/src/test/java/com/structurizr/component/supporting/AllReferencedTypesSupportingTypesStrategyTests.java index e0afc769a..80f8a15f9 100644 --- a/structurizr-component/src/test/java/com/structurizr/component/supporting/AllReferencedTypesSupportingTypesStrategyTests.java +++ b/structurizr-component/src/test/java/com/structurizr/component/supporting/AllReferencedTypesSupportingTypesStrategyTests.java @@ -21,7 +21,7 @@ void findSupportingTypes() { type.addDependency(new Type("com.example.util.SomeUtils")); - Set supportingTypes = new AllReferencedTypesSupportingTypesStrategy().findSupportingTypes(type); + Set supportingTypes = new AllReferencedTypesSupportingTypesStrategy().findSupportingTypes(type, null); assertEquals(2, supportingTypes.size()); assertTrue(supportingTypes.contains(dependency1)); assertTrue(supportingTypes.contains(dependency2)); diff --git a/structurizr-component/src/test/java/com/structurizr/component/supporting/DefaultSupportingTypesStrategyTests.java b/structurizr-component/src/test/java/com/structurizr/component/supporting/DefaultSupportingTypesStrategyTests.java index 6ff35a779..ff83615a2 100644 --- a/structurizr-component/src/test/java/com/structurizr/component/supporting/DefaultSupportingTypesStrategyTests.java +++ b/structurizr-component/src/test/java/com/structurizr/component/supporting/DefaultSupportingTypesStrategyTests.java @@ -14,7 +14,7 @@ void findSupportingTypes() { Type type = new Type("com.example.a.A"); type.addDependency(new Type("com.example.a.AImpl")); - Set supportingTypes = new DefaultSupportingTypesStrategy().findSupportingTypes(type); + Set supportingTypes = new DefaultSupportingTypesStrategy().findSupportingTypes(type, null); assertTrue(supportingTypes.isEmpty()); } From ff86a22716ab019443cc0b9b9ccd0779e10dd658 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 19 Aug 2024 11:43:01 +0100 Subject: [PATCH 530/717] . --- .../java/com/structurizr/component/ComponentFinderStrategy.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategy.java index 1da4f7de7..04d499361 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategy.java +++ b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategy.java @@ -49,7 +49,7 @@ Set findComponents(TypeRepository typeRepository) { components.add(component); // now find supporting types - Set supportingTypes = supportingTypesStrategy.findSupportingTypes(type); + Set supportingTypes = supportingTypesStrategy.findSupportingTypes(type, typeRepository); component.addSupportingTypes(supportingTypes); } } From 21f42f8ee6c6c73d3999693a1eaf7bb18959b2a4 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 19 Aug 2024 13:13:58 +0100 Subject: [PATCH 531/717] Turned the example into a test. --- .../component/example/Example.java | 58 --------------- .../component/example/ExampleTests.java | 72 +++++++++++++++++++ .../example/repository/IgnoredRepository.java | 5 ++ 3 files changed, 77 insertions(+), 58 deletions(-) delete mode 100644 structurizr-component/src/test/java/com/structurizr/component/example/Example.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/example/ExampleTests.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/example/repository/IgnoredRepository.java diff --git a/structurizr-component/src/test/java/com/structurizr/component/example/Example.java b/structurizr-component/src/test/java/com/structurizr/component/example/Example.java deleted file mode 100644 index 292199f39..000000000 --- a/structurizr-component/src/test/java/com/structurizr/component/example/Example.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.structurizr.component.example; - -import com.structurizr.Workspace; -import com.structurizr.component.ComponentFinderBuilder; -import com.structurizr.component.ComponentFinderStrategyBuilder; -import com.structurizr.component.matcher.NameSuffixTypeMatcher; -import com.structurizr.model.*; - -public class Example { - - public static void main(String[] args) { - Workspace workspace = new Workspace("Name", "Description"); - workspace.getModel().setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); - - Person user = workspace.getModel().addPerson("User"); - SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); - Container webApplication = softwareSystem.addContainer("Web Application"); - Container databaseSchema = softwareSystem.addContainer("Database Schema"); - - new ComponentFinderBuilder() - .forContainer(webApplication) - .fromClasses("structurizr-component/build/classes/java/test") - .fromSource("structurizr-component/src/test/java") - .withStrategy( - new ComponentFinderStrategyBuilder() - .matchedBy(new NameSuffixTypeMatcher("Controller", "Web MVC Controller")) - .forEach(component -> user.uses(component, "Uses")) - .build() - ) - .withStrategy( - new ComponentFinderStrategyBuilder() - .matchedBy(new NameSuffixTypeMatcher("Repository", "Data Repository")) - .forEach(component -> component.uses(databaseSchema, "Reads from and writes to")) - .build() - ) - .build().findComponents(); - - for (Element element : workspace.getModel().getElements()) { - print(element); - } - } - - private static void print(Element element) { - System.out.println("[" + element.getClass().getSimpleName() + "] " + element.getName()); - System.out.println(" - Description: " + element.getDescription()); - - if (element instanceof Component component) { - System.out.println(" - Technology: " + component.getTechnology()); - System.out.println(" - Type: " + element.getProperties().get("component.type")); - System.out.println(" - Source: " + element.getProperties().get("component.src")); - } - - for (Relationship relationship : element.getRelationships()) { - System.out.println(" -> [" + relationship.getDestination().getClass().getSimpleName() + "] " + relationship.getDestination().getName()); - } - } - -} diff --git a/structurizr-component/src/test/java/com/structurizr/component/example/ExampleTests.java b/structurizr-component/src/test/java/com/structurizr/component/example/ExampleTests.java new file mode 100644 index 000000000..4a72fd1be --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/example/ExampleTests.java @@ -0,0 +1,72 @@ +package com.structurizr.component.example; + +import com.structurizr.Workspace; +import com.structurizr.component.ComponentFinderBuilder; +import com.structurizr.component.ComponentFinderStrategyBuilder; +import com.structurizr.component.filter.ExcludeTypesByRegexFilter; +import com.structurizr.component.matcher.NameSuffixTypeMatcher; +import com.structurizr.component.matcher.RegexTypeMatcher; +import com.structurizr.component.naming.SimpleNamingStrategy; +import com.structurizr.model.Component; +import com.structurizr.model.Container; +import com.structurizr.model.Person; +import com.structurizr.model.SoftwareSystem; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class ExampleTests { + + @Test + void run() { + Workspace workspace = new Workspace("Name", "Description"); + + Person user = workspace.getModel().addPerson("User"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); + Container webApplication = softwareSystem.addContainer("Web Application"); + Container databaseSchema = softwareSystem.addContainer("Database Schema"); + + new ComponentFinderBuilder() + .forContainer(webApplication) + .fromClasses("build/classes/java/test") + .fromSource("src/test/java") + .withStrategy( + new ComponentFinderStrategyBuilder() + .matchedBy(new NameSuffixTypeMatcher("Controller", "Web MVC Controller")) + .forEach(component -> user.uses(component, "Uses")) + .build() + ) + .withStrategy( + new ComponentFinderStrategyBuilder() + .matchedBy(new RegexTypeMatcher(".*\\..*Repository", "Data Repository")) + .filteredBy(new ExcludeTypesByRegexFilter(".*Ignored.*")) + .forEach(component -> component.uses(databaseSchema, "Reads from and writes to")) + .namedBy(new SimpleNamingStrategy()) + .build() + ) + .build().findComponents(); + + assertEquals(2, webApplication.getComponents().size()); + + Component controller = webApplication.getComponentWithName("Customer Controller"); + assertNotNull(controller); + assertEquals("Allows users to view a list of customers.", controller.getDescription()); + assertEquals("Web MVC Controller", controller.getTechnology()); + assertEquals("com.structurizr.component.example.controller.CustomerController", controller.getProperties().get("component.type")); + assertTrue(controller.getProperties().get("component.src").endsWith("/src/test/java/com/structurizr/component/example/controller/CustomerController.java")); + + Component repository = webApplication.getComponentWithName("CustomerRepository"); + assertNotNull(repository); + assertEquals("Provides a way to access customer data.", repository.getDescription()); + assertEquals("Data Repository", repository.getTechnology()); + assertEquals("com.structurizr.component.example.repository.CustomerRepository", repository.getProperties().get("component.type")); + assertTrue(repository.getProperties().get("component.src").endsWith("/src/test/java/com/structurizr/component/example/repository/CustomerRepository.java")); + + assertEquals(1, controller.getRelationships().size()); + assertNotNull(controller.getEfferentRelationshipWith(repository)); + + assertNotNull(user.getEfferentRelationshipWith(controller)); + assertNotNull(repository.getEfferentRelationshipWith(databaseSchema)); + } + +} \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/example/repository/IgnoredRepository.java b/structurizr-component/src/test/java/com/structurizr/component/example/repository/IgnoredRepository.java new file mode 100644 index 000000000..b4365d13f --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/example/repository/IgnoredRepository.java @@ -0,0 +1,5 @@ +package com.structurizr.component.example.repository; + +public interface IgnoredRepository { + +} \ No newline at end of file From 76ebaf49cd8caac3470d210fe396a60e0e3bcc89 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 20 Aug 2024 08:57:41 +0100 Subject: [PATCH 532/717] Tidy and more tests. --- .../java/com/structurizr/component/Type.java | 9 +++ .../component/matcher/ExtendsTypeMatcher.java | 4 ++ .../matcher/ImplementsTypeMatcher.java | 4 ++ .../component/example/ExampleTests.java | 72 ------------------- .../controller/CustomerController.java | 19 ----- .../component/example/domain/Customer.java | 4 -- .../repository/CustomerRepository.java | 14 ---- .../repository/CustomerRepositoryImpl.java | 15 ---- .../example/repository/IgnoredRepository.java | 5 -- .../matcher/AnnotationTypeMatcherTests.java | 60 ++++++++++++++++ .../matcher/ExtendsTypeMatcherTests.java | 55 ++++++++++++++ .../matcher/ImplementsTypeMatcherTests.java | 54 ++++++++++++++ .../annotationTypeMatcher/Controller.java | 4 ++ .../CustomerController.java | 5 ++ .../annotationTypeMatcher/Repository.java | 4 ++ .../AbstractController.java | 4 ++ .../AbstractRepository.java | 4 ++ .../CustomerController.java | 4 ++ .../implementsTypeMatcher/Controller.java | 4 ++ .../CustomerController.java | 4 ++ .../implementsTypeMatcher/Repository.java | 4 ++ 21 files changed, 223 insertions(+), 129 deletions(-) delete mode 100644 structurizr-component/src/test/java/com/structurizr/component/example/ExampleTests.java delete mode 100644 structurizr-component/src/test/java/com/structurizr/component/example/controller/CustomerController.java delete mode 100644 structurizr-component/src/test/java/com/structurizr/component/example/domain/Customer.java delete mode 100644 structurizr-component/src/test/java/com/structurizr/component/example/repository/CustomerRepository.java delete mode 100644 structurizr-component/src/test/java/com/structurizr/component/example/repository/CustomerRepositoryImpl.java delete mode 100644 structurizr-component/src/test/java/com/structurizr/component/example/repository/IgnoredRepository.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/matcher/AnnotationTypeMatcherTests.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/matcher/ExtendsTypeMatcherTests.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/matcher/ImplementsTypeMatcherTests.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/matcher/annotationTypeMatcher/Controller.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/matcher/annotationTypeMatcher/CustomerController.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/matcher/annotationTypeMatcher/Repository.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/matcher/extendsTypeMatcher/AbstractController.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/matcher/extendsTypeMatcher/AbstractRepository.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/matcher/extendsTypeMatcher/CustomerController.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/matcher/implementsTypeMatcher/Controller.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/matcher/implementsTypeMatcher/CustomerController.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/matcher/implementsTypeMatcher/Repository.java diff --git a/structurizr-component/src/main/java/com/structurizr/component/Type.java b/structurizr-component/src/main/java/com/structurizr/component/Type.java index 56c5cca3f..0f0eed870 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/Type.java +++ b/structurizr-component/src/main/java/com/structurizr/component/Type.java @@ -1,5 +1,6 @@ package com.structurizr.component; +import com.structurizr.util.StringUtils; import org.apache.bcel.classfile.JavaClass; import java.util.HashSet; @@ -19,11 +20,19 @@ public final class Type { private final Set dependencies = new LinkedHashSet<>(); public Type(JavaClass javaClass) { + if (javaClass == null) { + throw new IllegalArgumentException("A BCEL JavaClass must be supplied"); + } + this.fullyQualifiedName = javaClass.getClassName(); this.javaClass = javaClass; } public Type(String fullyQualifiedName) { + if (StringUtils.isNullOrEmpty(fullyQualifiedName)) { + throw new IllegalArgumentException("A fully qualified name must be supplied"); + } + this.fullyQualifiedName = fullyQualifiedName; this.javaClass = null; } diff --git a/structurizr-component/src/main/java/com/structurizr/component/matcher/ExtendsTypeMatcher.java b/structurizr-component/src/main/java/com/structurizr/component/matcher/ExtendsTypeMatcher.java index ce402eb40..103d18b54 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/matcher/ExtendsTypeMatcher.java +++ b/structurizr-component/src/main/java/com/structurizr/component/matcher/ExtendsTypeMatcher.java @@ -21,6 +21,10 @@ public class ExtendsTypeMatcher extends AbstractTypeMatcher { public ExtendsTypeMatcher(String className, String technology) { super(technology); + if (className == null || className.trim().length() == 0) { + throw new IllegalArgumentException("A fully qualified class name must be supplied"); + } + this.className = className; } diff --git a/structurizr-component/src/main/java/com/structurizr/component/matcher/ImplementsTypeMatcher.java b/structurizr-component/src/main/java/com/structurizr/component/matcher/ImplementsTypeMatcher.java index c06f73614..54aecdcb7 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/matcher/ImplementsTypeMatcher.java +++ b/structurizr-component/src/main/java/com/structurizr/component/matcher/ImplementsTypeMatcher.java @@ -19,6 +19,10 @@ public class ImplementsTypeMatcher extends AbstractTypeMatcher { public ImplementsTypeMatcher(String interfaceName, String technology) { super(technology); + if (interfaceName == null || interfaceName.trim().length() == 0) { + throw new IllegalArgumentException("A fully qualified interface name must be supplied"); + } + this.interfaceName = interfaceName; } diff --git a/structurizr-component/src/test/java/com/structurizr/component/example/ExampleTests.java b/structurizr-component/src/test/java/com/structurizr/component/example/ExampleTests.java deleted file mode 100644 index 4a72fd1be..000000000 --- a/structurizr-component/src/test/java/com/structurizr/component/example/ExampleTests.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.structurizr.component.example; - -import com.structurizr.Workspace; -import com.structurizr.component.ComponentFinderBuilder; -import com.structurizr.component.ComponentFinderStrategyBuilder; -import com.structurizr.component.filter.ExcludeTypesByRegexFilter; -import com.structurizr.component.matcher.NameSuffixTypeMatcher; -import com.structurizr.component.matcher.RegexTypeMatcher; -import com.structurizr.component.naming.SimpleNamingStrategy; -import com.structurizr.model.Component; -import com.structurizr.model.Container; -import com.structurizr.model.Person; -import com.structurizr.model.SoftwareSystem; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.*; - -public class ExampleTests { - - @Test - void run() { - Workspace workspace = new Workspace("Name", "Description"); - - Person user = workspace.getModel().addPerson("User"); - SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); - Container webApplication = softwareSystem.addContainer("Web Application"); - Container databaseSchema = softwareSystem.addContainer("Database Schema"); - - new ComponentFinderBuilder() - .forContainer(webApplication) - .fromClasses("build/classes/java/test") - .fromSource("src/test/java") - .withStrategy( - new ComponentFinderStrategyBuilder() - .matchedBy(new NameSuffixTypeMatcher("Controller", "Web MVC Controller")) - .forEach(component -> user.uses(component, "Uses")) - .build() - ) - .withStrategy( - new ComponentFinderStrategyBuilder() - .matchedBy(new RegexTypeMatcher(".*\\..*Repository", "Data Repository")) - .filteredBy(new ExcludeTypesByRegexFilter(".*Ignored.*")) - .forEach(component -> component.uses(databaseSchema, "Reads from and writes to")) - .namedBy(new SimpleNamingStrategy()) - .build() - ) - .build().findComponents(); - - assertEquals(2, webApplication.getComponents().size()); - - Component controller = webApplication.getComponentWithName("Customer Controller"); - assertNotNull(controller); - assertEquals("Allows users to view a list of customers.", controller.getDescription()); - assertEquals("Web MVC Controller", controller.getTechnology()); - assertEquals("com.structurizr.component.example.controller.CustomerController", controller.getProperties().get("component.type")); - assertTrue(controller.getProperties().get("component.src").endsWith("/src/test/java/com/structurizr/component/example/controller/CustomerController.java")); - - Component repository = webApplication.getComponentWithName("CustomerRepository"); - assertNotNull(repository); - assertEquals("Provides a way to access customer data.", repository.getDescription()); - assertEquals("Data Repository", repository.getTechnology()); - assertEquals("com.structurizr.component.example.repository.CustomerRepository", repository.getProperties().get("component.type")); - assertTrue(repository.getProperties().get("component.src").endsWith("/src/test/java/com/structurizr/component/example/repository/CustomerRepository.java")); - - assertEquals(1, controller.getRelationships().size()); - assertNotNull(controller.getEfferentRelationshipWith(repository)); - - assertNotNull(user.getEfferentRelationshipWith(controller)); - assertNotNull(repository.getEfferentRelationshipWith(databaseSchema)); - } - -} \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/example/controller/CustomerController.java b/structurizr-component/src/test/java/com/structurizr/component/example/controller/CustomerController.java deleted file mode 100644 index 409e4dd8c..000000000 --- a/structurizr-component/src/test/java/com/structurizr/component/example/controller/CustomerController.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.structurizr.component.example.controller; - -import com.structurizr.component.example.repository.CustomerRepository; - -/** - * Allows users to view a list of customers. - */ -public class CustomerController { - - private CustomerRepository customerRepository; - - public CustomerController(CustomerRepository customerRepository) { - this.customerRepository = customerRepository; - } - - void showCustomersPage() { - customerRepository.getCustomers(); - } -} diff --git a/structurizr-component/src/test/java/com/structurizr/component/example/domain/Customer.java b/structurizr-component/src/test/java/com/structurizr/component/example/domain/Customer.java deleted file mode 100644 index ca9637df3..000000000 --- a/structurizr-component/src/test/java/com/structurizr/component/example/domain/Customer.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.structurizr.component.example.domain; - -public class Customer { -} diff --git a/structurizr-component/src/test/java/com/structurizr/component/example/repository/CustomerRepository.java b/structurizr-component/src/test/java/com/structurizr/component/example/repository/CustomerRepository.java deleted file mode 100644 index d7040cd2e..000000000 --- a/structurizr-component/src/test/java/com/structurizr/component/example/repository/CustomerRepository.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.structurizr.component.example.repository; - -import com.structurizr.component.example.domain.Customer; - -import java.util.List; - -/** - * Provides a way to access customer data. - */ -public interface CustomerRepository { - - List getCustomers(); - -} \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/example/repository/CustomerRepositoryImpl.java b/structurizr-component/src/test/java/com/structurizr/component/example/repository/CustomerRepositoryImpl.java deleted file mode 100644 index 43ef69bdc..000000000 --- a/structurizr-component/src/test/java/com/structurizr/component/example/repository/CustomerRepositoryImpl.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.structurizr.component.example.repository; - -import com.structurizr.component.example.domain.Customer; - -import java.util.ArrayList; -import java.util.List; - -public class CustomerRepositoryImpl implements CustomerRepository { - - @Override - public List getCustomers() { - return new ArrayList<>(); - } - -} \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/example/repository/IgnoredRepository.java b/structurizr-component/src/test/java/com/structurizr/component/example/repository/IgnoredRepository.java deleted file mode 100644 index b4365d13f..000000000 --- a/structurizr-component/src/test/java/com/structurizr/component/example/repository/IgnoredRepository.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.structurizr.component.example.repository; - -public interface IgnoredRepository { - -} \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/matcher/AnnotationTypeMatcherTests.java b/structurizr-component/src/test/java/com/structurizr/component/matcher/AnnotationTypeMatcherTests.java new file mode 100644 index 000000000..fc124dfec --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/matcher/AnnotationTypeMatcherTests.java @@ -0,0 +1,60 @@ +package com.structurizr.component.matcher; + +import com.structurizr.component.Type; +import org.apache.bcel.classfile.ClassParser; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.lang.annotation.Annotation; + +import static org.junit.jupiter.api.Assertions.*; + +public class AnnotationTypeMatcherTests { + + @Test + void construction_ThrowsAnException_WhenPassedANullName() { + assertThrowsExactly(IllegalArgumentException.class, () -> new AnnotationTypeMatcher((String)null, "Technology")); + } + + @Test + void construction_ThrowsAnException_WhenPassedAnEmptyName() { + assertThrowsExactly(IllegalArgumentException.class, () -> new AnnotationTypeMatcher("", "Technology")); + assertThrowsExactly(IllegalArgumentException.class, () -> new AnnotationTypeMatcher(" ", "Technology")); + } + + @Test + void construction_ThrowsAnException_WhenPassedANullClass() { + assertThrowsExactly(IllegalArgumentException.class, () -> new AnnotationTypeMatcher((Class) null, "Technology")); + } + + @Test + void matches_ThrowsAnException_WhenPassedNull() { + assertThrowsExactly(IllegalArgumentException.class, () -> new AnnotationTypeMatcher("com.example.AnnotationName", "Technology").matches(null)); + } + + @Test + void matches_ReturnsFalse_WhenThereIsNoUnderlyingJavaClass() { + Type type = new Type("com.structurizr.component.matcher.annotationTypeMatcher.CustomerController"); + + assertFalse(new AnnotationTypeMatcher("com.structurizr.component.matcher.annotationTypeMatcher.Controller", "Technology").matches(type)); + } + + @Test + void matches_ReturnsFalse_WhenThereIsNoMatch() throws Exception { + File classes = new File("build/classes/java/test"); + ClassParser parser = new ClassParser(new File(classes, "com/structurizr/component/matcher/annotationTypeMatcher/CustomerController.class").getAbsolutePath()); + Type type = new Type(parser.parse()); + + assertFalse(new AnnotationTypeMatcher("com.structurizr.component.matcher.annotationTypeMatcher.Repository", "Technology").matches(type)); + } + + @Test + void matches_ReturnsTrue_WhenThereIsAMatch() throws Exception { + File classes = new File("build/classes/java/test"); + ClassParser parser = new ClassParser(new File(classes, "com/structurizr/component/matcher/annotationTypeMatcher/CustomerController.class").getAbsolutePath()); + Type type = new Type(parser.parse()); + + assertTrue(new AnnotationTypeMatcher("com.structurizr.component.matcher.annotationTypeMatcher.Controller", "Technology").matches(type)); + } + +} \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/matcher/ExtendsTypeMatcherTests.java b/structurizr-component/src/test/java/com/structurizr/component/matcher/ExtendsTypeMatcherTests.java new file mode 100644 index 000000000..40b71bcb5 --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/matcher/ExtendsTypeMatcherTests.java @@ -0,0 +1,55 @@ +package com.structurizr.component.matcher; + +import com.structurizr.component.Type; +import org.apache.bcel.classfile.ClassParser; +import org.apache.bcel.classfile.JavaClass; +import org.junit.jupiter.api.Test; + +import java.io.File; + +import static org.junit.jupiter.api.Assertions.*; + +public class ExtendsTypeMatcherTests { + + @Test + void construction_ThrowsAnException_WhenPassedANullName() { + assertThrowsExactly(IllegalArgumentException.class, () -> new ExtendsTypeMatcher(null, "Technology")); + } + + @Test + void construction_ThrowsAnException_WhenPassedAnEmptyName() { + assertThrowsExactly(IllegalArgumentException.class, () -> new ExtendsTypeMatcher("", "Technology")); + assertThrowsExactly(IllegalArgumentException.class, () -> new ExtendsTypeMatcher(" ", "Technology")); + } + + @Test + void matches_ThrowsAnException_WhenPassedNull() { + assertThrowsExactly(IllegalArgumentException.class, () -> new ExtendsTypeMatcher("com.example.ClassName", "Technology").matches(null)); + } + + @Test + void matches_ReturnsFalse_WhenThereIsNoUnderlyingJavaClass() { + Type type = new Type("com.structurizr.component.matcher.extendsTypeMatcher.CustomerController"); + + assertFalse(new ExtendsTypeMatcher("com.structurizr.component.matcher.extendsTypeMatcher.AbstractController", "Technology").matches(type)); + } + + @Test + void matches_ReturnsFalse_WhenThereIsNoMatch() throws Exception { + File classes = new File("build/classes/java/test"); + ClassParser parser = new ClassParser(new File(classes, "com/structurizr/component/matcher/extendsTypeMatcher/CustomerController.class").getAbsolutePath()); + Type type = new Type(parser.parse()); + + assertFalse(new ExtendsTypeMatcher("com.structurizr.component.matcher.extendsTypeMatcher.AbstractRepository", "Technology").matches(type)); + } + + @Test + void matches_ReturnsTrue_WhenThereIsAMatch() throws Exception { + File classes = new File("build/classes/java/test"); + ClassParser parser = new ClassParser(new File(classes, "com/structurizr/component/matcher/extendsTypeMatcher/CustomerController.class").getAbsolutePath()); + Type type = new Type(parser.parse()); + + assertTrue(new ExtendsTypeMatcher("com.structurizr.component.matcher.extendsTypeMatcher.AbstractController", "Technology").matches(type)); + } + +} \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/matcher/ImplementsTypeMatcherTests.java b/structurizr-component/src/test/java/com/structurizr/component/matcher/ImplementsTypeMatcherTests.java new file mode 100644 index 000000000..4ece7e585 --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/matcher/ImplementsTypeMatcherTests.java @@ -0,0 +1,54 @@ +package com.structurizr.component.matcher; + +import com.structurizr.component.Type; +import org.apache.bcel.classfile.ClassParser; +import org.junit.jupiter.api.Test; + +import java.io.File; + +import static org.junit.jupiter.api.Assertions.*; + +public class ImplementsTypeMatcherTests { + + @Test + void construction_ThrowsAnException_WhenPassedANullName() { + assertThrowsExactly(IllegalArgumentException.class, () -> new ImplementsTypeMatcher(null, "Technology")); + } + + @Test + void construction_ThrowsAnException_WhenPassedAnEmptyName() { + assertThrowsExactly(IllegalArgumentException.class, () -> new ImplementsTypeMatcher("", "Technology")); + assertThrowsExactly(IllegalArgumentException.class, () -> new ImplementsTypeMatcher(" ", "Technology")); + } + + @Test + void matches_ThrowsAnException_WhenPassedNull() { + assertThrowsExactly(IllegalArgumentException.class, () -> new ImplementsTypeMatcher("com.example.InterfaceName", "Technology").matches(null)); + } + + @Test + void matches_ReturnsFalse_WhenThereIsNoUnderlyingJavaClass() { + Type type = new Type("com.structurizr.component.matcher.implementsTypeMatcher.CustomerController"); + + assertFalse(new ImplementsTypeMatcher("com.structurizr.component.matcher.implementsTypeMatcher.Controller", "Technology").matches(type)); + } + + @Test + void matches_ReturnsFalse_WhenThereIsNoMatch() throws Exception { + File classes = new File("build/classes/java/test"); + ClassParser parser = new ClassParser(new File(classes, "com/structurizr/component/matcher/implementsTypeMatcher/CustomerController.class").getAbsolutePath()); + Type type = new Type(parser.parse()); + + assertFalse(new ImplementsTypeMatcher("com.structurizr.component.matcher.implementsTypeMatcher.Repository", "Technology").matches(type)); + } + + @Test + void matches_ReturnsTrue_WhenThereIsAMatch() throws Exception { + File classes = new File("build/classes/java/test"); + ClassParser parser = new ClassParser(new File(classes, "com/structurizr/component/matcher/implementsTypeMatcher/CustomerController.class").getAbsolutePath()); + Type type = new Type(parser.parse()); + + assertTrue(new ImplementsTypeMatcher("com.structurizr.component.matcher.implementsTypeMatcher.Controller", "Technology").matches(type)); + } + +} \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/matcher/annotationTypeMatcher/Controller.java b/structurizr-component/src/test/java/com/structurizr/component/matcher/annotationTypeMatcher/Controller.java new file mode 100644 index 000000000..967ee7eee --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/matcher/annotationTypeMatcher/Controller.java @@ -0,0 +1,4 @@ +package com.structurizr.component.matcher.annotationTypeMatcher; + +public @interface Controller { +} diff --git a/structurizr-component/src/test/java/com/structurizr/component/matcher/annotationTypeMatcher/CustomerController.java b/structurizr-component/src/test/java/com/structurizr/component/matcher/annotationTypeMatcher/CustomerController.java new file mode 100644 index 000000000..a9a6f3d1c --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/matcher/annotationTypeMatcher/CustomerController.java @@ -0,0 +1,5 @@ +package com.structurizr.component.matcher.annotationTypeMatcher; + +@Controller +public class CustomerController { +} diff --git a/structurizr-component/src/test/java/com/structurizr/component/matcher/annotationTypeMatcher/Repository.java b/structurizr-component/src/test/java/com/structurizr/component/matcher/annotationTypeMatcher/Repository.java new file mode 100644 index 000000000..09a13b504 --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/matcher/annotationTypeMatcher/Repository.java @@ -0,0 +1,4 @@ +package com.structurizr.component.matcher.annotationTypeMatcher; + +public @interface Repository { +} diff --git a/structurizr-component/src/test/java/com/structurizr/component/matcher/extendsTypeMatcher/AbstractController.java b/structurizr-component/src/test/java/com/structurizr/component/matcher/extendsTypeMatcher/AbstractController.java new file mode 100644 index 000000000..b70b98848 --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/matcher/extendsTypeMatcher/AbstractController.java @@ -0,0 +1,4 @@ +package com.structurizr.component.matcher.extendsTypeMatcher; + +abstract class AbstractController { +} diff --git a/structurizr-component/src/test/java/com/structurizr/component/matcher/extendsTypeMatcher/AbstractRepository.java b/structurizr-component/src/test/java/com/structurizr/component/matcher/extendsTypeMatcher/AbstractRepository.java new file mode 100644 index 000000000..467507cfc --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/matcher/extendsTypeMatcher/AbstractRepository.java @@ -0,0 +1,4 @@ +package com.structurizr.component.matcher.extendsTypeMatcher; + +abstract class AbstractRepository { +} diff --git a/structurizr-component/src/test/java/com/structurizr/component/matcher/extendsTypeMatcher/CustomerController.java b/structurizr-component/src/test/java/com/structurizr/component/matcher/extendsTypeMatcher/CustomerController.java new file mode 100644 index 000000000..6edec8bd5 --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/matcher/extendsTypeMatcher/CustomerController.java @@ -0,0 +1,4 @@ +package com.structurizr.component.matcher.extendsTypeMatcher; + +class CustomerController extends AbstractController { +} diff --git a/structurizr-component/src/test/java/com/structurizr/component/matcher/implementsTypeMatcher/Controller.java b/structurizr-component/src/test/java/com/structurizr/component/matcher/implementsTypeMatcher/Controller.java new file mode 100644 index 000000000..a616ef84c --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/matcher/implementsTypeMatcher/Controller.java @@ -0,0 +1,4 @@ +package com.structurizr.component.matcher.implementsTypeMatcher; + +interface Controller { +} diff --git a/structurizr-component/src/test/java/com/structurizr/component/matcher/implementsTypeMatcher/CustomerController.java b/structurizr-component/src/test/java/com/structurizr/component/matcher/implementsTypeMatcher/CustomerController.java new file mode 100644 index 000000000..d6c47e64f --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/matcher/implementsTypeMatcher/CustomerController.java @@ -0,0 +1,4 @@ +package com.structurizr.component.matcher.implementsTypeMatcher; + +class CustomerController implements Controller { +} diff --git a/structurizr-component/src/test/java/com/structurizr/component/matcher/implementsTypeMatcher/Repository.java b/structurizr-component/src/test/java/com/structurizr/component/matcher/implementsTypeMatcher/Repository.java new file mode 100644 index 000000000..ba17b8d88 --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/matcher/implementsTypeMatcher/Repository.java @@ -0,0 +1,4 @@ +package com.structurizr.component.matcher.implementsTypeMatcher; + +interface Repository { +} From e8ba1f3a85831193f3db99cb97951ac793d04246 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 20 Aug 2024 09:43:59 +0100 Subject: [PATCH 533/717] More tests. --- .../provider/ClassDirectoryTypeProvider.java | 18 +++++++-- .../provider/ClassJarFileTypeProvider.java | 4 +- .../provider/SourceDirectoryTypeProvider.java | 32 +++++++++++---- .../ClassDirectoryTypeProviderTests.java | 39 +++++++++++++++++++ .../SourceDirectoryTypeProviderTests.java | 39 +++++++++++++++++++ 5 files changed, 119 insertions(+), 13 deletions(-) create mode 100644 structurizr-component/src/test/java/com/structurizr/component/provider/ClassDirectoryTypeProviderTests.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/provider/SourceDirectoryTypeProviderTests.java diff --git a/structurizr-component/src/main/java/com/structurizr/component/provider/ClassDirectoryTypeProvider.java b/structurizr-component/src/main/java/com/structurizr/component/provider/ClassDirectoryTypeProvider.java index a4c5b8339..1c18fb9d5 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/provider/ClassDirectoryTypeProvider.java +++ b/structurizr-component/src/main/java/com/structurizr/component/provider/ClassDirectoryTypeProvider.java @@ -8,7 +8,7 @@ import java.io.File; import java.io.IOException; -import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.Set; /** @@ -22,11 +22,23 @@ public final class ClassDirectoryTypeProvider implements TypeProvider { private final File directory; public ClassDirectoryTypeProvider(File directory) { + if (directory == null) { + throw new IllegalArgumentException("A directory must be supplied"); + } + + if (!directory.exists()) { + throw new IllegalArgumentException(directory.getAbsolutePath() + " does not exist"); + } + + if (!directory.isDirectory()) { + throw new IllegalArgumentException(directory.getAbsolutePath() + " is not a directory"); + } + this.directory = directory; } public Set getTypes() { - Set types = new HashSet<>(); + Set types = new LinkedHashSet<>(); Set files = findClassFiles(directory); for (File file : files) { @@ -43,7 +55,7 @@ public Set getTypes() { } private Set findClassFiles(File path) { - Set classFiles = new HashSet<>(); + Set classFiles = new LinkedHashSet<>(); if (path.isDirectory()) { File[] files = path.listFiles(); if (files != null) { diff --git a/structurizr-component/src/main/java/com/structurizr/component/provider/ClassJarFileTypeProvider.java b/structurizr-component/src/main/java/com/structurizr/component/provider/ClassJarFileTypeProvider.java index 71951c04a..10f00de7e 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/provider/ClassJarFileTypeProvider.java +++ b/structurizr-component/src/main/java/com/structurizr/component/provider/ClassJarFileTypeProvider.java @@ -9,7 +9,7 @@ import java.io.File; import java.io.IOException; import java.util.Enumeration; -import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.Set; import java.util.jar.JarEntry; @@ -28,7 +28,7 @@ public ClassJarFileTypeProvider(File file) { } public Set getTypes() { - Set types = new HashSet<>(); + Set types = new LinkedHashSet<>(); java.util.jar.JarFile jar = null; try { jar = new java.util.jar.JarFile(jarFile); diff --git a/structurizr-component/src/main/java/com/structurizr/component/provider/SourceDirectoryTypeProvider.java b/structurizr-component/src/main/java/com/structurizr/component/provider/SourceDirectoryTypeProvider.java index 808006c48..f8d8db07f 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/provider/SourceDirectoryTypeProvider.java +++ b/structurizr-component/src/main/java/com/structurizr/component/provider/SourceDirectoryTypeProvider.java @@ -13,7 +13,7 @@ import java.io.File; import java.io.IOException; -import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.Set; /** @@ -25,21 +25,37 @@ public final class SourceDirectoryTypeProvider implements TypeProvider { private static final String JAVA_FILE_EXTENSION = ".java"; private static final int DEFAULT_DESCRIPTION_LENGTH = 60; - private final Set types = new HashSet<>(); + private final File directory; + private final int maximumDescriptionLength; + private final Set types = new LinkedHashSet<>(); - public SourceDirectoryTypeProvider(File path) { - this(path, DEFAULT_DESCRIPTION_LENGTH); + public SourceDirectoryTypeProvider(File directory) { + this(directory, DEFAULT_DESCRIPTION_LENGTH); } - public SourceDirectoryTypeProvider(File path, int maximumDescriptionLength) { - StaticJavaParser.getParserConfiguration().setLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_21); + public SourceDirectoryTypeProvider(File directory, int maximumDescriptionLength) { + if (directory == null) { + throw new IllegalArgumentException("A directory must be supplied"); + } + + if (!directory.exists()) { + throw new IllegalArgumentException(directory.getAbsolutePath() + " does not exist"); + } - parse(path, maximumDescriptionLength); + if (!directory.isDirectory()) { + throw new IllegalArgumentException(directory.getAbsolutePath() + " is not a directory"); + } + + this.directory = directory; + this.maximumDescriptionLength = maximumDescriptionLength; + StaticJavaParser.getParserConfiguration().setLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_21); } @Override public Set getTypes() { - return new HashSet<>(types); + parse(directory, maximumDescriptionLength); + + return new LinkedHashSet<>(types); } private void parse(File path, int maximumDescriptionLength) { diff --git a/structurizr-component/src/test/java/com/structurizr/component/provider/ClassDirectoryTypeProviderTests.java b/structurizr-component/src/test/java/com/structurizr/component/provider/ClassDirectoryTypeProviderTests.java new file mode 100644 index 000000000..60d90a8cd --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/provider/ClassDirectoryTypeProviderTests.java @@ -0,0 +1,39 @@ +package com.structurizr.component.provider; + +import com.structurizr.component.Type; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.*; + +public class ClassDirectoryTypeProviderTests { + + private static final File classes = new File("build/classes/java/test"); + + @Test + void construction_ThrowsAnException_WhenPassedANullDirectory() { + assertThrowsExactly(IllegalArgumentException.class, () -> new ClassDirectoryTypeProvider(null)); + } + + @Test + void construction_ThrowsAnException_WhenPassedAPathThatDoesNotExist() { + assertThrowsExactly(IllegalArgumentException.class, () -> new ClassDirectoryTypeProvider(new File(classes, "com/example"))); + } + + @Test + void construction_ThrowsAnException_WhenPassedAFile() { + assertThrowsExactly(IllegalArgumentException.class, () -> new ClassDirectoryTypeProvider(new File(classes, "com/structurizr/component/provider/ClassDirectoryTypeProviderTests.class"))); + } + + @Test + void getTypes() { + TypeProvider typeProvider = new ClassDirectoryTypeProvider(classes); + Set types = typeProvider.getTypes(); + + assertTrue(types.size() > 0); + assertNotNull(types.stream().filter(t -> t.getFullyQualifiedName().equals("com.structurizr.component.provider.ClassDirectoryTypeProviderTests"))); + } + +} \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/provider/SourceDirectoryTypeProviderTests.java b/structurizr-component/src/test/java/com/structurizr/component/provider/SourceDirectoryTypeProviderTests.java new file mode 100644 index 000000000..6230ec6c6 --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/provider/SourceDirectoryTypeProviderTests.java @@ -0,0 +1,39 @@ +package com.structurizr.component.provider; + +import com.structurizr.component.Type; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.*; + +public class SourceDirectoryTypeProviderTests { + + private static final File classes = new File("src/main/java"); + + @Test + void construction_ThrowsAnException_WhenPassedANullDirectory() { + assertThrowsExactly(IllegalArgumentException.class, () -> new SourceDirectoryTypeProvider(null)); + } + + @Test + void construction_ThrowsAnException_WhenPassedAPathThatDoesNotExist() { + assertThrowsExactly(IllegalArgumentException.class, () -> new SourceDirectoryTypeProvider(new File(classes, "com/example"))); + } + + @Test + void construction_ThrowsAnException_WhenPassedAFile() { + assertThrowsExactly(IllegalArgumentException.class, () -> new SourceDirectoryTypeProvider(new File(classes, "com/structurizr/component/provider/SourceDirectoryTypeProviderTests.java"))); + } + + @Test + void getTypes() { + TypeProvider typeProvider = new SourceDirectoryTypeProvider(classes); + Set types = typeProvider.getTypes(); + + assertTrue(types.size() > 0); + assertNotNull(types.stream().filter(t -> t.getFullyQualifiedName().equals("com.structurizr.component.provider.SourceDirectoryTypeProviderTests"))); + } + +} \ No newline at end of file From 1a003d85f02f614222f3119f4e64fe9df954656d Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 20 Aug 2024 09:59:59 +0100 Subject: [PATCH 534/717] More tests. --- .../java/com/structurizr/component/Type.java | 7 ++-- ... => ExcludeAbstractClassesTypeFilter.java} | 4 +-- .../filter/ExcludeTypesByRegexFilter.java | 5 +++ .../filter/IncludeTypesByRegexFilter.java | 5 +++ .../component/matcher/ExtendsTypeMatcher.java | 3 +- .../matcher/ImplementsTypeMatcher.java | 3 +- .../matcher/NameSuffixTypeMatcher.java | 3 +- .../filter/DefaultTypeFilterTests.java | 15 +++++++++ .../ExcludeAbstractClassesFilterTests.java | 31 ++++++++++++++++++ .../ExcludeTypesByRegexFilterTests.java | 32 +++++++++++++++++++ .../IncludeTypesByRegexFilterTests.java | 32 +++++++++++++++++++ 11 files changed, 131 insertions(+), 9 deletions(-) rename structurizr-component/src/main/java/com/structurizr/component/filter/{ExcludeAbstractClassTypeFilter.java => ExcludeAbstractClassesTypeFilter.java} (72%) create mode 100644 structurizr-component/src/test/java/com/structurizr/component/filter/DefaultTypeFilterTests.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/filter/ExcludeAbstractClassesFilterTests.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/filter/ExcludeTypesByRegexFilterTests.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/filter/IncludeTypesByRegexFilterTests.java diff --git a/structurizr-component/src/main/java/com/structurizr/component/Type.java b/structurizr-component/src/main/java/com/structurizr/component/Type.java index 0f0eed870..3c9fafe01 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/Type.java +++ b/structurizr-component/src/main/java/com/structurizr/component/Type.java @@ -3,7 +3,6 @@ import com.structurizr.util.StringUtils; import org.apache.bcel.classfile.JavaClass; -import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Objects; import java.util.Set; @@ -11,7 +10,7 @@ /** * Represents a Java type (e.g. class or interface) - it's a wrapper around a BCEL JavaClass. */ -public final class Type { +public class Type { private final JavaClass javaClass; private final String fullyQualifiedName; @@ -77,8 +76,8 @@ public Set getDependencies() { return new LinkedHashSet<>(dependencies); } - public boolean isAbstract() { - return javaClass.isAbstract(); + public boolean isAbstractClass() { + return javaClass.isAbstract() && javaClass.isClass(); } @Override diff --git a/structurizr-component/src/main/java/com/structurizr/component/filter/ExcludeAbstractClassTypeFilter.java b/structurizr-component/src/main/java/com/structurizr/component/filter/ExcludeAbstractClassesTypeFilter.java similarity index 72% rename from structurizr-component/src/main/java/com/structurizr/component/filter/ExcludeAbstractClassTypeFilter.java rename to structurizr-component/src/main/java/com/structurizr/component/filter/ExcludeAbstractClassesTypeFilter.java index dad267ef1..2a2d979c9 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/filter/ExcludeAbstractClassTypeFilter.java +++ b/structurizr-component/src/main/java/com/structurizr/component/filter/ExcludeAbstractClassesTypeFilter.java @@ -5,10 +5,10 @@ /** * A type filter that excludes abstract types. */ -public class ExcludeAbstractClassTypeFilter implements TypeFilter { +public class ExcludeAbstractClassesTypeFilter implements TypeFilter { public boolean accept(Type type) { - return !type.isAbstract(); + return !type.isAbstractClass(); } @Override diff --git a/structurizr-component/src/main/java/com/structurizr/component/filter/ExcludeTypesByRegexFilter.java b/structurizr-component/src/main/java/com/structurizr/component/filter/ExcludeTypesByRegexFilter.java index 0f697b6e8..52e7d2843 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/filter/ExcludeTypesByRegexFilter.java +++ b/structurizr-component/src/main/java/com/structurizr/component/filter/ExcludeTypesByRegexFilter.java @@ -1,6 +1,7 @@ package com.structurizr.component.filter; import com.structurizr.component.Type; +import com.structurizr.util.StringUtils; /** * A type filter that excludes by matching a regex against the fully qualified type name. @@ -10,6 +11,10 @@ public class ExcludeTypesByRegexFilter implements TypeFilter { private final String regex; public ExcludeTypesByRegexFilter(String regex) { + if (StringUtils.isNullOrEmpty(regex)) { + throw new IllegalArgumentException("A regex must be supplied"); + } + this.regex = regex; } diff --git a/structurizr-component/src/main/java/com/structurizr/component/filter/IncludeTypesByRegexFilter.java b/structurizr-component/src/main/java/com/structurizr/component/filter/IncludeTypesByRegexFilter.java index dc76b1f41..3de92d287 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/filter/IncludeTypesByRegexFilter.java +++ b/structurizr-component/src/main/java/com/structurizr/component/filter/IncludeTypesByRegexFilter.java @@ -1,6 +1,7 @@ package com.structurizr.component.filter; import com.structurizr.component.Type; +import com.structurizr.util.StringUtils; /** * A type filter that includes by matching a regex against the fully qualified type name. @@ -10,6 +11,10 @@ public class IncludeTypesByRegexFilter implements TypeFilter { private final String regex; public IncludeTypesByRegexFilter(String regex) { + if (StringUtils.isNullOrEmpty(regex)) { + throw new IllegalArgumentException("A regex must be supplied"); + } + this.regex = regex; } diff --git a/structurizr-component/src/main/java/com/structurizr/component/matcher/ExtendsTypeMatcher.java b/structurizr-component/src/main/java/com/structurizr/component/matcher/ExtendsTypeMatcher.java index 103d18b54..043ca3cec 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/matcher/ExtendsTypeMatcher.java +++ b/structurizr-component/src/main/java/com/structurizr/component/matcher/ExtendsTypeMatcher.java @@ -1,6 +1,7 @@ package com.structurizr.component.matcher; import com.structurizr.component.Type; +import com.structurizr.util.StringUtils; import org.apache.bcel.classfile.JavaClass; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -21,7 +22,7 @@ public class ExtendsTypeMatcher extends AbstractTypeMatcher { public ExtendsTypeMatcher(String className, String technology) { super(technology); - if (className == null || className.trim().length() == 0) { + if (StringUtils.isNullOrEmpty(className)) { throw new IllegalArgumentException("A fully qualified class name must be supplied"); } diff --git a/structurizr-component/src/main/java/com/structurizr/component/matcher/ImplementsTypeMatcher.java b/structurizr-component/src/main/java/com/structurizr/component/matcher/ImplementsTypeMatcher.java index 54aecdcb7..1deb3b6ff 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/matcher/ImplementsTypeMatcher.java +++ b/structurizr-component/src/main/java/com/structurizr/component/matcher/ImplementsTypeMatcher.java @@ -1,6 +1,7 @@ package com.structurizr.component.matcher; import com.structurizr.component.Type; +import com.structurizr.util.StringUtils; import org.apache.bcel.classfile.JavaClass; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -19,7 +20,7 @@ public class ImplementsTypeMatcher extends AbstractTypeMatcher { public ImplementsTypeMatcher(String interfaceName, String technology) { super(technology); - if (interfaceName == null || interfaceName.trim().length() == 0) { + if (StringUtils.isNullOrEmpty(interfaceName)) { throw new IllegalArgumentException("A fully qualified interface name must be supplied"); } diff --git a/structurizr-component/src/main/java/com/structurizr/component/matcher/NameSuffixTypeMatcher.java b/structurizr-component/src/main/java/com/structurizr/component/matcher/NameSuffixTypeMatcher.java index 01f27091a..078a1a511 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/matcher/NameSuffixTypeMatcher.java +++ b/structurizr-component/src/main/java/com/structurizr/component/matcher/NameSuffixTypeMatcher.java @@ -1,6 +1,7 @@ package com.structurizr.component.matcher; import com.structurizr.component.Type; +import com.structurizr.util.StringUtils; /** * Matches types where the name of the type ends with the specified suffix. @@ -12,7 +13,7 @@ public class NameSuffixTypeMatcher extends AbstractTypeMatcher { public NameSuffixTypeMatcher(String suffix, String technology) { super(technology); - if (suffix == null || suffix.trim().length() == 0) { + if (StringUtils.isNullOrEmpty(suffix)) { throw new IllegalArgumentException("A suffix must be supplied"); } diff --git a/structurizr-component/src/test/java/com/structurizr/component/filter/DefaultTypeFilterTests.java b/structurizr-component/src/test/java/com/structurizr/component/filter/DefaultTypeFilterTests.java new file mode 100644 index 000000000..6352fb213 --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/filter/DefaultTypeFilterTests.java @@ -0,0 +1,15 @@ +package com.structurizr.component.filter; + +import com.structurizr.component.Type; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class DefaultTypeFilterTests { + + @Test + void filter_ReturnsTrue() { + assertTrue(new DefaultTypeFilter().accept(new Type("com.example.Class"))); + } + +} \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/filter/ExcludeAbstractClassesFilterTests.java b/structurizr-component/src/test/java/com/structurizr/component/filter/ExcludeAbstractClassesFilterTests.java new file mode 100644 index 000000000..1b3b8c22f --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/filter/ExcludeAbstractClassesFilterTests.java @@ -0,0 +1,31 @@ +package com.structurizr.component.filter; + +import com.structurizr.component.Type; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class ExcludeAbstractClassesFilterTests { + + @Test + void filter_ReturnsTrue_WhenTheTypeIsNotAbstract() { + assertTrue(new ExcludeAbstractClassesTypeFilter().accept(new Type("com.example.Class") { + @Override + public boolean isAbstractClass() { + return false; + } + })); + } + + @Test + void filter_ReturnsFalse_WhenTheTypeIsAbstract() { + assertFalse(new ExcludeAbstractClassesTypeFilter().accept(new Type("com.example.Class") { + @Override + public boolean isAbstractClass() { + return true; + } + })); + } + +} \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/filter/ExcludeTypesByRegexFilterTests.java b/structurizr-component/src/test/java/com/structurizr/component/filter/ExcludeTypesByRegexFilterTests.java new file mode 100644 index 000000000..4a0b459fa --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/filter/ExcludeTypesByRegexFilterTests.java @@ -0,0 +1,32 @@ +package com.structurizr.component.filter; + +import com.structurizr.component.Type; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class ExcludeTypesByRegexFilterTests { + + @Test + void construction_ThrowsAnException_WhenPassedANullSuffix() { + assertThrowsExactly(IllegalArgumentException.class, () -> new ExcludeTypesByRegexFilter(null)); + } + + @Test + void construction_ThrowsAnException_WhenPassedAnEmptySuffix() { + assertThrowsExactly(IllegalArgumentException.class, () -> new ExcludeTypesByRegexFilter("")); + assertThrowsExactly(IllegalArgumentException.class, () -> new ExcludeTypesByRegexFilter(" ")); + } + + + @Test + void filter_ReturnsTrue_WhenTheTypeDoesNotMatchRegex() { + assertTrue(new ExcludeTypesByRegexFilter(".*Utils").accept(new Type("com.example.CustomerComponent"))); + } + + @Test + void filter_ReturnsFalse_WhenTheTypeMatchesRegex() { + assertFalse(new ExcludeTypesByRegexFilter(".*Utils").accept(new Type("com.example.DateUtils"))); + } + +} \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/filter/IncludeTypesByRegexFilterTests.java b/structurizr-component/src/test/java/com/structurizr/component/filter/IncludeTypesByRegexFilterTests.java new file mode 100644 index 000000000..25b223c54 --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/filter/IncludeTypesByRegexFilterTests.java @@ -0,0 +1,32 @@ +package com.structurizr.component.filter; + +import com.structurizr.component.Type; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class IncludeTypesByRegexFilterTests { + + @Test + void construction_ThrowsAnException_WhenPassedANullSuffix() { + assertThrowsExactly(IllegalArgumentException.class, () -> new IncludeTypesByRegexFilter(null)); + } + + @Test + void construction_ThrowsAnException_WhenPassedAnEmptySuffix() { + assertThrowsExactly(IllegalArgumentException.class, () -> new IncludeTypesByRegexFilter("")); + assertThrowsExactly(IllegalArgumentException.class, () -> new IncludeTypesByRegexFilter(" ")); + } + + + @Test + void filter_ReturnsFalse_WhenTheTypeDoesNotMatchRegex() { + assertFalse(new IncludeTypesByRegexFilter(".*Component").accept(new Type("com.example.DateUtils"))); + } + + @Test + void filter_ReturnsTrue_WhenTheTypeMatchesRegex() { + assertTrue(new IncludeTypesByRegexFilter(".*Component").accept(new Type("com.example.CustomerComponent"))); + } + +} \ No newline at end of file From 01d169e09207ad8cabf2a21ca2d59cb77021079b Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 20 Aug 2024 10:06:26 +0100 Subject: [PATCH 535/717] More tests. --- ...InPackageSupportingTypesStrategyTests.java | 2 -- ...ncedTypesSupportingTypesStrategyTests.java | 2 -- ...InPackageSupportingTypesStrategyTests.java | 35 ++++++++++++++++++ ...erPackageSupportingTypesStrategyTests.java | 36 +++++++++++++++++++ 4 files changed, 71 insertions(+), 4 deletions(-) create mode 100644 structurizr-component/src/test/java/com/structurizr/component/supporting/AllTypesInPackageSupportingTypesStrategyTests.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/supporting/AllTypesUnderPackageSupportingTypesStrategyTests.java diff --git a/structurizr-component/src/test/java/com/structurizr/component/supporting/AllReferencedTypesInPackageSupportingTypesStrategyTests.java b/structurizr-component/src/test/java/com/structurizr/component/supporting/AllReferencedTypesInPackageSupportingTypesStrategyTests.java index 761b08d84..5562ed190 100644 --- a/structurizr-component/src/test/java/com/structurizr/component/supporting/AllReferencedTypesInPackageSupportingTypesStrategyTests.java +++ b/structurizr-component/src/test/java/com/structurizr/component/supporting/AllReferencedTypesInPackageSupportingTypesStrategyTests.java @@ -19,8 +19,6 @@ void findSupportingTypes() { type.addDependency(dependency1); type.addDependency(dependency2); - type.addDependency(new Type("com.example.util.SomeUtils")); - Set supportingTypes = new AllReferencedTypesInPackageSupportingTypesStrategy().findSupportingTypes(type, null); assertEquals(1, supportingTypes.size()); assertTrue(supportingTypes.contains(dependency1)); diff --git a/structurizr-component/src/test/java/com/structurizr/component/supporting/AllReferencedTypesSupportingTypesStrategyTests.java b/structurizr-component/src/test/java/com/structurizr/component/supporting/AllReferencedTypesSupportingTypesStrategyTests.java index 80f8a15f9..6f3122bcc 100644 --- a/structurizr-component/src/test/java/com/structurizr/component/supporting/AllReferencedTypesSupportingTypesStrategyTests.java +++ b/structurizr-component/src/test/java/com/structurizr/component/supporting/AllReferencedTypesSupportingTypesStrategyTests.java @@ -19,8 +19,6 @@ void findSupportingTypes() { type.addDependency(dependency1); type.addDependency(dependency2); - type.addDependency(new Type("com.example.util.SomeUtils")); - Set supportingTypes = new AllReferencedTypesSupportingTypesStrategy().findSupportingTypes(type, null); assertEquals(2, supportingTypes.size()); assertTrue(supportingTypes.contains(dependency1)); diff --git a/structurizr-component/src/test/java/com/structurizr/component/supporting/AllTypesInPackageSupportingTypesStrategyTests.java b/structurizr-component/src/test/java/com/structurizr/component/supporting/AllTypesInPackageSupportingTypesStrategyTests.java new file mode 100644 index 000000000..cf36e4c00 --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/supporting/AllTypesInPackageSupportingTypesStrategyTests.java @@ -0,0 +1,35 @@ +package com.structurizr.component.supporting; + +import com.structurizr.component.Type; +import com.structurizr.component.TypeRepository; +import org.junit.jupiter.api.Test; + +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class AllTypesInPackageSupportingTypesStrategyTests { + + @Test + void findSupportingTypes() { + Type type = new Type("com.example.a.package-info"); + + Type dependency1 = new Type("com.example.a.AImpl"); + Type dependency2 = new Type("com.example.a.internal.AInternal"); + Type dependency3 = new Type("com.example.util.SomeUtils"); + type.addDependency(dependency1); + type.addDependency(dependency2); + type.addDependency(dependency3); + + TypeRepository typeRepository = new TypeRepository(); + typeRepository.add(dependency1); + typeRepository.add(dependency2); + typeRepository.add(dependency3); + + Set supportingTypes = new AllTypesInPackageSupportingTypesStrategy().findSupportingTypes(type, typeRepository); + assertEquals(1, supportingTypes.size()); + assertTrue(supportingTypes.contains(dependency1)); + } + +} \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/supporting/AllTypesUnderPackageSupportingTypesStrategyTests.java b/structurizr-component/src/test/java/com/structurizr/component/supporting/AllTypesUnderPackageSupportingTypesStrategyTests.java new file mode 100644 index 000000000..7cf97d4c3 --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/supporting/AllTypesUnderPackageSupportingTypesStrategyTests.java @@ -0,0 +1,36 @@ +package com.structurizr.component.supporting; + +import com.structurizr.component.Type; +import com.structurizr.component.TypeRepository; +import org.junit.jupiter.api.Test; + +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class AllTypesUnderPackageSupportingTypesStrategyTests { + + @Test + void findSupportingTypes() { + Type type = new Type("com.example.a.package-info"); + + Type dependency1 = new Type("com.example.a.AImpl"); + Type dependency2 = new Type("com.example.a.internal.AInternal"); + Type dependency3 = new Type("com.example.util.SomeUtils"); + type.addDependency(dependency1); + type.addDependency(dependency2); + type.addDependency(dependency3); + + TypeRepository typeRepository = new TypeRepository(); + typeRepository.add(dependency1); + typeRepository.add(dependency2); + typeRepository.add(dependency3); + + Set supportingTypes = new AllTypesUnderPackageSupportingTypesStrategy().findSupportingTypes(type, typeRepository); + assertEquals(2, supportingTypes.size()); + assertTrue(supportingTypes.contains(dependency1)); + assertTrue(supportingTypes.contains(dependency2)); + } + +} \ No newline at end of file From 6fbaa6395ca02756b374df089b03dfdf27f4241c Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 20 Aug 2024 10:17:47 +0100 Subject: [PATCH 536/717] More tests. --- .../component/ComponentFinder.java | 4 -- .../component/ComponentFinderBuilder.java | 12 +++++ .../ComponentFinderBuilderTests.java | 47 +++++++++++++++++++ .../ComponentFinderStrategyBuilderTests.java | 14 ++++++ .../SourceDirectoryTypeProviderTests.java | 8 ++-- 5 files changed, 77 insertions(+), 8 deletions(-) create mode 100644 structurizr-component/src/test/java/com/structurizr/component/ComponentFinderBuilderTests.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/ComponentFinderStrategyBuilderTests.java diff --git a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinder.java b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinder.java index ef6edac08..5234800fc 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinder.java +++ b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinder.java @@ -29,10 +29,6 @@ public final class ComponentFinder { private final List componentFinderStrategies = new ArrayList<>(); ComponentFinder(Container container, Collection typeProviders, List componentFinderStrategies) { - if (container == null) { - throw new IllegalArgumentException("A container must be specified."); - } - this.container = container; this.componentFinderStrategies.addAll(componentFinderStrategies); diff --git a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderBuilder.java b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderBuilder.java index a4f8f9535..86cd0a843 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderBuilder.java +++ b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderBuilder.java @@ -64,6 +64,18 @@ public ComponentFinderBuilder withStrategy(ComponentFinderStrategy componentFind } public ComponentFinder build() { + if (container == null) { + throw new RuntimeException("A container must be specified"); + } + + if (typeProviders.isEmpty()) { + throw new RuntimeException("One or more type providers must be configured"); + } + + if (componentFinderStrategies.isEmpty()) { + throw new RuntimeException("One or more component finder strategies must be configured"); + } + return new ComponentFinder(container, typeProviders, componentFinderStrategies); } diff --git a/structurizr-component/src/test/java/com/structurizr/component/ComponentFinderBuilderTests.java b/structurizr-component/src/test/java/com/structurizr/component/ComponentFinderBuilderTests.java new file mode 100644 index 000000000..13206859a --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/ComponentFinderBuilderTests.java @@ -0,0 +1,47 @@ +package com.structurizr.component; + +import com.structurizr.Workspace; +import com.structurizr.model.Container; +import org.junit.jupiter.api.Test; + +import java.io.File; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +public class ComponentFinderBuilderTests { + + @Test + void build_ThrowsAnException_WhenAContainerHasNotBeenSpecified() { + try { + new ComponentFinderBuilder().build(); + fail(); + } catch (RuntimeException e) { + assertEquals("A container must be specified", e.getMessage()); + } + } + + @Test + void build_ThrowsAnException_WhenATypeProviderHasNotBeenConfigured() { + Container container = new Workspace("Name", "Description").getModel().addSoftwareSystem("Software System").addContainer("Container"); + try { + new ComponentFinderBuilder().forContainer(container).build(); + fail(); + } catch (RuntimeException e) { + assertEquals("One or more type providers must be configured", e.getMessage()); + } + } + + @Test + void build_ThrowsAnException_WhenAComponentFinderStrategyHasNotBeenConfigured() { + Container container = new Workspace("Name", "Description").getModel().addSoftwareSystem("Software System").addContainer("Container"); + File sources = new File("src/main/java"); + try { + new ComponentFinderBuilder().forContainer(container).fromSource(sources).build(); + fail(); + } catch (RuntimeException e) { + assertEquals("One or more component finder strategies must be configured", e.getMessage()); + } + } + +} \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/ComponentFinderStrategyBuilderTests.java b/structurizr-component/src/test/java/com/structurizr/component/ComponentFinderStrategyBuilderTests.java new file mode 100644 index 000000000..b08a60b2a --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/ComponentFinderStrategyBuilderTests.java @@ -0,0 +1,14 @@ +package com.structurizr.component; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertThrowsExactly; + +public class ComponentFinderStrategyBuilderTests { + + @Test + void build_ThrowsAnException_WhenATypeMatcherHasNotBeenConfgured() { + assertThrowsExactly(RuntimeException.class, () -> new ComponentFinderStrategyBuilder().build()); + } + +} \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/provider/SourceDirectoryTypeProviderTests.java b/structurizr-component/src/test/java/com/structurizr/component/provider/SourceDirectoryTypeProviderTests.java index 6230ec6c6..564258a54 100644 --- a/structurizr-component/src/test/java/com/structurizr/component/provider/SourceDirectoryTypeProviderTests.java +++ b/structurizr-component/src/test/java/com/structurizr/component/provider/SourceDirectoryTypeProviderTests.java @@ -10,7 +10,7 @@ public class SourceDirectoryTypeProviderTests { - private static final File classes = new File("src/main/java"); + private static final File sources = new File("src/main/java"); @Test void construction_ThrowsAnException_WhenPassedANullDirectory() { @@ -19,17 +19,17 @@ void construction_ThrowsAnException_WhenPassedANullDirectory() { @Test void construction_ThrowsAnException_WhenPassedAPathThatDoesNotExist() { - assertThrowsExactly(IllegalArgumentException.class, () -> new SourceDirectoryTypeProvider(new File(classes, "com/example"))); + assertThrowsExactly(IllegalArgumentException.class, () -> new SourceDirectoryTypeProvider(new File(sources, "com/example"))); } @Test void construction_ThrowsAnException_WhenPassedAFile() { - assertThrowsExactly(IllegalArgumentException.class, () -> new SourceDirectoryTypeProvider(new File(classes, "com/structurizr/component/provider/SourceDirectoryTypeProviderTests.java"))); + assertThrowsExactly(IllegalArgumentException.class, () -> new SourceDirectoryTypeProvider(new File(sources, "com/structurizr/component/provider/SourceDirectoryTypeProviderTests.java"))); } @Test void getTypes() { - TypeProvider typeProvider = new SourceDirectoryTypeProvider(classes); + TypeProvider typeProvider = new SourceDirectoryTypeProvider(sources); Set types = typeProvider.getTypes(); assertTrue(types.size() > 0); From 33c13a9a2b3f6177f67e0414f00269acf56ae7ad Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 20 Aug 2024 10:19:31 +0100 Subject: [PATCH 537/717] Typo. --- .../component/ComponentFinderStrategyBuilderTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structurizr-component/src/test/java/com/structurizr/component/ComponentFinderStrategyBuilderTests.java b/structurizr-component/src/test/java/com/structurizr/component/ComponentFinderStrategyBuilderTests.java index b08a60b2a..6b578022f 100644 --- a/structurizr-component/src/test/java/com/structurizr/component/ComponentFinderStrategyBuilderTests.java +++ b/structurizr-component/src/test/java/com/structurizr/component/ComponentFinderStrategyBuilderTests.java @@ -7,7 +7,7 @@ public class ComponentFinderStrategyBuilderTests { @Test - void build_ThrowsAnException_WhenATypeMatcherHasNotBeenConfgured() { + void build_ThrowsAnException_WhenATypeMatcherHasNotBeenConfigured() { assertThrowsExactly(RuntimeException.class, () -> new ComponentFinderStrategyBuilder().build()); } From ed4ee43cba5fbf4101670b0f06d980d2a6d231ce Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 20 Aug 2024 11:08:22 +0100 Subject: [PATCH 538/717] Adds "tag" as an alternative to "tags". --- .../src/main/java/com/structurizr/dsl/StructurizrDslParser.java | 2 +- .../src/main/java/com/structurizr/dsl/StructurizrDslTokens.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index 50a2fc62f..cffcebef4 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -427,7 +427,7 @@ void parse(List lines, File dslFile, boolean include) throws Structurizr DeploymentNode deploymentNode = getContext(DeploymentNodeDslContext.class).getDeploymentNode(); startContext(new DeploymentNodeDslContext(deploymentNode, group)); registerIdentifier(identifier, group); - } else if (TAGS_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelItemDslContext.class) && !getContext(ModelItemDslContext.class).hasGroup()) { + } else if ((TAGS_TOKEN.equalsIgnoreCase(firstToken) || TAG_TOKEN.equalsIgnoreCase(firstToken)) && inContext(ModelItemDslContext.class) && !getContext(ModelItemDslContext.class).hasGroup()) { new ModelItemParser().parseTags(getContext(ModelItemDslContext.class), tokens); } else if (DESCRIPTION_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelItemDslContext.class) && getContext(ModelItemDslContext.class).getModelItem() instanceof Element && !getContext(ModelItemDslContext.class).hasGroup()) { diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java index 35ace2259..c1301def4 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java @@ -19,6 +19,7 @@ class StructurizrDslTokens { static final String TECHNOLOGY_TOKEN = "technology"; static final String INSTANCES_TOKEN = "instances"; static final String TAGS_TOKEN = "tags"; + static final String TAG_TOKEN = "tag"; static final String URL_TOKEN = "url"; static final String PROPERTIES_TOKEN = "properties"; static final String PERSPECTIVES_TOKEN = "perspectives"; From 52ec9be442477af82d83281184aa54e88669b261 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 20 Aug 2024 11:09:14 +0100 Subject: [PATCH 539/717] Adds a way to add constants to the parser programmatically. --- .../structurizr/dsl/StructurizrDslParser.java | 41 ++++++++++++++++--- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index cffcebef4..cca16737a 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -889,11 +889,11 @@ void parse(List lines, File dslFile, boolean include) throws Structurizr } else if (CONST_TOKEN.equalsIgnoreCase(firstToken)) { NameValuePair nameValuePair = new NameValueParser().parseConstant(tokens); - - if (constantsAndVariables.containsKey(nameValuePair.getName())) { - throw new StructurizrDslParserException("A constant/variable \"" + nameValuePair.getName() + "\" already exists"); + try { + addConstant(nameValuePair); + } catch (IllegalArgumentException e) { + throw new StructurizrDslParserException(e.getMessage()); } - constantsAndVariables.put(nameValuePair.getName(), nameValuePair); } else if (VAR_TOKEN.equalsIgnoreCase(firstToken)) { NameValuePair nameValuePair = new NameValueParser().parseVariable(tokens); @@ -1062,8 +1062,39 @@ private void registerIdentifier(String identifier, Relationship relationship) { relationship.addProperty(STRUCTURIZR_DSL_IDENTIFIER_PROPERTY_NAME, identifiersRegister.findIdentifier(relationship)); } + /** + * Gets the named constant. + * + * @param name the name of the constant + * @return the value, or an empty string if the named constant doesn't exist + */ public String getConstant(String name) { - return constantsAndVariables.get(name).getValue(); + NameValuePair nameValuePair = constantsAndVariables.get(name); + if (nameValuePair != null) { + return nameValuePair.getValue(); + } else { + return ""; + } + } + + /** + * Adds a constant to the parser. + * @param name the name of the constant + * @param value the value of the constant + */ + public void addConstant(String name, String value) { + if (StringUtils.isNullOrEmpty(name)) { + throw new IllegalArgumentException("A constant name must be specified"); + } + + addConstant(new NameValuePair(name, value)); + } + + private void addConstant(NameValuePair nameValuePair) { + if (constantsAndVariables.containsKey(nameValuePair.getName())) { + throw new IllegalArgumentException("A constant/variable \"" + nameValuePair.getName() + "\" already exists"); + } + constantsAndVariables.put(nameValuePair.getName(), nameValuePair); } private boolean inContext(Class clazz) { From e30f5e84636ed0e3bc187e8f173588cb709bd276 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 20 Aug 2024 11:41:13 +0100 Subject: [PATCH 540/717] Adds an `!elements` keyword that can be used to find a set of elements via an expression. --- changelog.md | 1 + .../structurizr/dsl/ElementsDslContext.java | 33 +++++++++++++ .../com/structurizr/dsl/ElementsParser.java | 28 +++++++++++ .../structurizr/dsl/ModelItemsDslContext.java | 26 +++++++++++ .../structurizr/dsl/StructurizrDslParser.java | 7 +++ .../structurizr/dsl/StructurizrDslTokens.java | 1 + .../structurizr/dsl/ElementsParserTests.java | 46 +++++++++++++++++++ .../src/test/resources/dsl/test.dsl | 3 ++ 8 files changed, 145 insertions(+) create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ElementsDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ElementsParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemsDslContext.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/ElementsParserTests.java diff --git a/changelog.md b/changelog.md index b5076e453..0d0498992 100644 --- a/changelog.md +++ b/changelog.md @@ -7,6 +7,7 @@ - structurizr-dsl: Fixes https://github.com/structurizr/java/issues/312 (!include doesn't work with files encoded as UTF-8 BOM). - structurizr-dsl: Adds a way to explicitly specify the order of relationships in dynamic views. - structurizr-dsl: Adds support for element technology expressions (e.g. "element.technology==Java"). +- structurizr-dsl: Adds an `!elements` keyword that can be used to find a set of elements via an expression. ## 2.2.0 (2nd July 2024) diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementsDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementsDslContext.java new file mode 100644 index 000000000..d0aec535b --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementsDslContext.java @@ -0,0 +1,33 @@ +package com.structurizr.dsl; + +import com.structurizr.model.Element; +import com.structurizr.model.ModelItem; + +import java.util.Set; +import java.util.stream.Collectors; + +class ElementsDslContext extends ModelItemsDslContext { + + private final Set elements; + + ElementsDslContext(DslContext parentDslContext, Set elements) { + super(parentDslContext); + + this.elements = elements; + } + + Set getElements() { + return elements; + } + + @Override + Set getModelItems() { + return elements.stream().map(e -> (ModelItem)e).collect(Collectors.toSet()); + } + + @Override + protected String[] getPermittedTokens() { + return new String[0]; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementsParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementsParser.java new file mode 100644 index 000000000..2bfccf11b --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementsParser.java @@ -0,0 +1,28 @@ +package com.structurizr.dsl; + +import com.structurizr.model.Element; +import com.structurizr.model.ModelItem; + +import java.util.Set; +import java.util.stream.Collectors; + +final class ElementsParser extends AbstractParser { + + private static final String GRAMMAR = "!elements "; + + private final static int EXPRESSION_INDEX = 1; + + Set parse(DslContext context, Tokens tokens) { + // !elements + + if (tokens.hasMoreThan(EXPRESSION_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } + + String expression = tokens.get(1); + Set modelItems = new ExpressionParser().parseExpression(expression, context); + + return modelItems.stream().filter(mi -> mi instanceof Element).map(mi -> (Element)mi).collect(Collectors.toSet()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemsDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemsDslContext.java new file mode 100644 index 000000000..1caf3055a --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemsDslContext.java @@ -0,0 +1,26 @@ +package com.structurizr.dsl; + +import com.structurizr.model.ModelItem; + +import java.util.Set; + +abstract class ModelItemsDslContext extends DslContext { + + private final DslContext parentDslContext; + + ModelItemsDslContext(DslContext parentDslContext) { + this.parentDslContext = parentDslContext; + } + + DslContext getParentDslContext() { + return parentDslContext; + } + + abstract Set getModelItems(); + + @Override + protected String[] getPermittedTokens() { + return new String[0]; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index cca16737a..d34d77bae 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -348,6 +348,13 @@ void parse(List lines, File dslFile, boolean include) throws Structurizr } } + } else if (ELEMENTS_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ModelDslContext.class) || inContext(ModelItemDslContext.class))) { + Set elements = new ElementsParser().parse(getContext(), tokens.withoutContextStartToken()); + + if (shouldStartContext(tokens)) { + startContext(new ElementsDslContext(getContext(), elements)); + } + } else if (CUSTOM_ELEMENT_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ModelDslContext.class))) { CustomElement customElement = new CustomElementParser().parse(getContext(GroupableDslContext.class), tokens.withoutContextStartToken()); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java index c1301def4..ab7aff0b1 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java @@ -106,6 +106,7 @@ class StructurizrDslTokens { static final String IDENTIFIERS_TOKEN = "!identifiers"; static final String IMPLIED_RELATIONSHIPS_TOKEN = "!impliedRelationships"; static final String REF_TOKEN = "!ref"; + static final String ELEMENTS_TOKEN = "!elements"; static final String EXTEND_TOKEN = "!extend"; static final String PLUGIN_TOKEN = "!plugin"; static final String SCRIPT_TOKEN = "!script"; diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementsParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementsParserTests.java new file mode 100644 index 000000000..3af081588 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementsParserTests.java @@ -0,0 +1,46 @@ +package com.structurizr.dsl; + +import com.structurizr.model.Component; +import com.structurizr.model.Container; +import com.structurizr.model.Element; +import com.structurizr.model.SoftwareSystem; +import org.junit.jupiter.api.Test; + +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.*; + +class ElementsParserTests extends AbstractTests { + + private final ElementsParser parser = new ElementsParser(); + + @Test + void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parse(context(), tokens("!elements", "expression", "tokens")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: !elements ", e.getMessage()); + } + } + + @Test + void test_parse_FindsElementsByExpression() { + Container application = model.addSoftwareSystem("Software System").addContainer("Application"); + Component componentA = application.addComponent("A"); + Component componentB = application.addComponent("B"); + Component componentC = application.addComponent("C"); + + ModelItemDslContext context = new ContainerDslContext(application); + context.setWorkspace(workspace); + IdentifiersRegister register = new IdentifiersRegister(); + register.register("application", application); + context.setIdentifierRegister(register); + + Set elements = parser.parse(context, tokens("!elements", "element.parent==application")); + assertTrue(elements.contains(componentA)); + assertTrue(elements.contains(componentB)); + assertTrue(elements.contains(componentC)); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/test.dsl b/structurizr-dsl/src/test/resources/dsl/test.dsl index b33be93e0..d71238d07 100644 --- a/structurizr-dsl/src/test/resources/dsl/test.dsl +++ b/structurizr-dsl/src/test/resources/dsl/test.dsl @@ -66,6 +66,9 @@ workspace "Name" "Description" { perspectives { "Security" "A description..." } + + !elements "element.parent==webApplication" { + } } url "https://structurizr.com" From 987e0f26985447c488b368542580a20a0b7def3a Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 20 Aug 2024 11:53:40 +0100 Subject: [PATCH 541/717] Adds a way to set tags for a set of elements found via `!elements`. --- .../com/structurizr/dsl/ModelItemsParser.java | 24 ++++++++++ .../structurizr/dsl/StructurizrDslParser.java | 3 ++ .../dsl/ModelItemsParserTests.java | 48 +++++++++++++++++++ .../src/test/resources/dsl/test.dsl | 3 +- 4 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemsParser.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/ModelItemsParserTests.java diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemsParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemsParser.java new file mode 100644 index 000000000..c34ceb33d --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemsParser.java @@ -0,0 +1,24 @@ +package com.structurizr.dsl; + +import com.structurizr.model.ModelItem; + +final class ModelItemsParser extends AbstractParser { + + private final static int TAGS_INDEX = 1; + + void parseTags(ModelItemsDslContext context, Tokens tokens) { + // tags [tags] + if (!tokens.includes(TAGS_INDEX)) { + throw new RuntimeException("Expected: tags [tags]"); + } + + for (int i = TAGS_INDEX; i < tokens.size(); i++) { + String tags = tokens.get(i); + + for (ModelItem modelItem : context.getModelItems()) { + modelItem.addTags(tags.split(",")); + } + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index d34d77bae..a3bbac50c 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -437,6 +437,9 @@ void parse(List lines, File dslFile, boolean include) throws Structurizr } else if ((TAGS_TOKEN.equalsIgnoreCase(firstToken) || TAG_TOKEN.equalsIgnoreCase(firstToken)) && inContext(ModelItemDslContext.class) && !getContext(ModelItemDslContext.class).hasGroup()) { new ModelItemParser().parseTags(getContext(ModelItemDslContext.class), tokens); + } else if ((TAGS_TOKEN.equalsIgnoreCase(firstToken) || TAG_TOKEN.equalsIgnoreCase(firstToken)) && inContext(ModelItemsDslContext.class)) { + new ModelItemsParser().parseTags(getContext(ModelItemsDslContext.class), tokens); + } else if (DESCRIPTION_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelItemDslContext.class) && getContext(ModelItemDslContext.class).getModelItem() instanceof Element && !getContext(ModelItemDslContext.class).hasGroup()) { new ModelItemParser().parseDescription(getContext(ModelItemDslContext.class), tokens); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ModelItemsParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ModelItemsParserTests.java new file mode 100644 index 000000000..004d345cf --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ModelItemsParserTests.java @@ -0,0 +1,48 @@ +package com.structurizr.dsl; + +import com.structurizr.model.Element; +import com.structurizr.model.SoftwareSystem; +import org.junit.jupiter.api.Test; + +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.*; + +class ModelItemsParserTests extends AbstractTests { + + private final ModelItemsParser parser = new ModelItemsParser(); + + @Test + void test_parseTags_ThrowsAnException_WhenNoTagsAreSpecified() { + try { + parser.parseTags(null, tokens("tags")); + fail(); + } catch (Exception e) { + assertEquals("Expected: tags [tags]", e.getMessage()); + } + } + + @Test + void test_parseTags_AddsTheTags_WhenTagsAreSpecified() { + SoftwareSystem a = model.addSoftwareSystem("A"); + SoftwareSystem b = model.addSoftwareSystem("B"); + SoftwareSystem c = model.addSoftwareSystem("C"); + + ElementsDslContext context = new ElementsDslContext(null, Set.of(a, b, c)); + + parser.parseTags(context, tokens("tags", "Tag 1")); + for (Element element : context.getElements()) { + assertEquals(3, element.getTagsAsSet().size()); + assertTrue(element.getTagsAsSet().contains("Tag 1")); + } + + parser.parseTags(context, tokens("tags", "Tag 1, Tag 2, Tag 3")); + for (Element element : context.getElements()) { + assertEquals(5, element.getTagsAsSet().size()); + assertTrue(element.getTagsAsSet().contains("Tag 1")); + assertTrue(element.getTagsAsSet().contains("Tag 2")); + assertTrue(element.getTagsAsSet().contains("Tag 3")); + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/test.dsl b/structurizr-dsl/src/test/resources/dsl/test.dsl index d71238d07..994ea2d39 100644 --- a/structurizr-dsl/src/test/resources/dsl/test.dsl +++ b/structurizr-dsl/src/test/resources/dsl/test.dsl @@ -67,7 +67,8 @@ workspace "Name" "Description" { "Security" "A description..." } - !elements "element.parent==webApplication" { + !elements "element.parent==webApplication && element.technology==Spring MVC Controller" { + tags "Spring MVC Controller" } } From d2ccb98ff656c943307330592f7eb7dae970d20c Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 20 Aug 2024 13:34:34 +0100 Subject: [PATCH 542/717] Tidy up of how groups are handled. --- .../dsl/ContainerInstanceDslContext.java | 8 +++++- .../structurizr/dsl/CustomElementParser.java | 2 +- .../dsl/DeploymentEnvironmentDslContext.java | 17 +++++++++-- .../structurizr/dsl/ElementDslContext.java | 9 ++++++ .../com/structurizr/dsl/ElementGroup.java | 14 ++-------- .../java/com/structurizr/dsl/GroupParser.java | 14 ++++++++-- .../structurizr/dsl/GroupableDslContext.java | 20 ++----------- .../dsl/GroupableElementDslContext.java | 18 ++++++++++-- .../dsl/InfrastructureNodeDslContext.java | 10 +++++-- .../com/structurizr/dsl/ModelDslContext.java | 17 +++++++++-- .../structurizr/dsl/ModelItemDslContext.java | 6 +--- .../com/structurizr/dsl/ModelItemParser.java | 6 ++-- .../com/structurizr/dsl/PersonParser.java | 2 +- .../dsl/SoftwareSystemInstanceDslContext.java | 8 +++++- .../structurizr/dsl/SoftwareSystemParser.java | 2 +- ...ticStructureElementInstanceDslContext.java | 2 +- .../structurizr/dsl/StructurizrDslParser.java | 28 ++++++++++++------- .../com/structurizr/dsl/GroupParserTests.java | 4 +-- .../structurizr/dsl/ModelItemParserTests.java | 6 ++-- 19 files changed, 121 insertions(+), 72 deletions(-) create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ElementDslContext.java diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerInstanceDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerInstanceDslContext.java index 4268f656b..a8f1bc8bd 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerInstanceDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerInstanceDslContext.java @@ -1,12 +1,13 @@ package com.structurizr.dsl; import com.structurizr.model.ContainerInstance; +import com.structurizr.model.Element; import com.structurizr.model.ModelItem; import com.structurizr.model.StaticStructureElementInstance; final class ContainerInstanceDslContext extends StaticStructureElementInstanceDslContext { - private ContainerInstance containerInstance; + private final ContainerInstance containerInstance; ContainerInstanceDslContext(ContainerInstance containerInstance) { this.containerInstance = containerInstance; @@ -21,6 +22,11 @@ ModelItem getModelItem() { return getContainerInstance(); } + @Override + Element getElement() { + return getContainerInstance(); + } + @Override StaticStructureElementInstance getElementInstance() { return getContainerInstance(); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomElementParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomElementParser.java index bef0a7707..493b573b9 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomElementParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomElementParser.java @@ -13,7 +13,7 @@ final class CustomElementParser extends AbstractParser { private final static int DESCRIPTION_INDEX = 3; private final static int TAGS_INDEX = 4; - CustomElement parse(GroupableDslContext context, Tokens tokens) { + CustomElement parse(ModelDslContext context, Tokens tokens) { // element [metadata] [description] [tags] if (tokens.hasMoreThan(TAGS_INDEX)) { diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentEnvironmentDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentEnvironmentDslContext.java index c35027242..ceb1c30b2 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentEnvironmentDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentEnvironmentDslContext.java @@ -1,23 +1,34 @@ package com.structurizr.dsl; -final class DeploymentEnvironmentDslContext extends GroupableDslContext { +final class DeploymentEnvironmentDslContext extends DslContext implements GroupableDslContext { private final String environment; + private final ElementGroup group; DeploymentEnvironmentDslContext(String environment) { - super(null); this.environment = environment; + this.group = null; } DeploymentEnvironmentDslContext(String environment, ElementGroup group) { - super(group); this.environment = environment; + this.group = group; } String getEnvironment() { return environment; } + @Override + public boolean hasGroup() { + return group != null; + } + + @Override + public ElementGroup getGroup() { + return group; + } + @Override protected String[] getPermittedTokens() { return new String[] { diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementDslContext.java new file mode 100644 index 000000000..11a0594f9 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementDslContext.java @@ -0,0 +1,9 @@ +package com.structurizr.dsl; + +import com.structurizr.model.Element; + +abstract class ElementDslContext extends ModelItemDslContext { + + abstract Element getElement(); + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementGroup.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementGroup.java index 751dc1b0f..7c5895cef 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementGroup.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementGroup.java @@ -9,28 +9,18 @@ class ElementGroup extends Element { - private static final String STRUCTURIZR_GROUP_SEPARATOR_PROPERTY_NAME = "structurizr.groupSeparator"; - private Element parent; private final ElementGroup parentGroup; private final String name; private final Set elements = new HashSet<>(); - ElementGroup(Model model, String name) { - setModel(model); + ElementGroup(String name) { this.name = name; this.parentGroup = null; } - ElementGroup(Model model, String name, ElementGroup parentGroup) { - setModel(model); - String groupSeparator = getModel().getProperties().getOrDefault(STRUCTURIZR_GROUP_SEPARATOR_PROPERTY_NAME, ""); - - if (StringUtils.isNullOrEmpty(groupSeparator)) { - throw new RuntimeException("To use nested groups, please define a model property named " + STRUCTURIZR_GROUP_SEPARATOR_PROPERTY_NAME); - } - + ElementGroup(String name, String groupSeparator, ElementGroup parentGroup) { this.name = parentGroup.getName() + groupSeparator + name; this.parentGroup = parentGroup; } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/GroupParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/GroupParser.java index 8ff6e0fa9..255f3db9d 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/GroupParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/GroupParser.java @@ -1,7 +1,11 @@ package com.structurizr.dsl; +import com.structurizr.util.StringUtils; + class GroupParser { + private static final String STRUCTURIZR_GROUP_SEPARATOR_PROPERTY_NAME = "structurizr.groupSeparator"; + private static final String GRAMMAR = "group {"; private final static int NAME_INDEX = 1; @@ -24,9 +28,15 @@ ElementGroup parse(GroupableDslContext dslContext, Tokens tokens) { ElementGroup group; if (dslContext.hasGroup()) { - group = new ElementGroup(dslContext.getWorkspace().getModel(), tokens.get(NAME_INDEX), dslContext.getGroup()); + String groupSeparator = ((DslContext)dslContext).getWorkspace().getModel().getProperties().getOrDefault(STRUCTURIZR_GROUP_SEPARATOR_PROPERTY_NAME, ""); + + if (StringUtils.isNullOrEmpty(groupSeparator)) { + throw new RuntimeException("To use nested groups, please define a model property named " + STRUCTURIZR_GROUP_SEPARATOR_PROPERTY_NAME); + } + + group = new ElementGroup(tokens.get(NAME_INDEX), groupSeparator, dslContext.getGroup()); } else { - group = new ElementGroup(dslContext.getWorkspace().getModel(), tokens.get(NAME_INDEX)); + group = new ElementGroup(tokens.get(NAME_INDEX)); } return group; diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/GroupableDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/GroupableDslContext.java index 5ac594434..594ac14b6 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/GroupableDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/GroupableDslContext.java @@ -1,23 +1,9 @@ package com.structurizr.dsl; -abstract class GroupableDslContext extends DslContext { +interface GroupableDslContext { - private ElementGroup group; + boolean hasGroup(); - GroupableDslContext() { - this(null); - } - - GroupableDslContext(ElementGroup group) { - this.group = group; - } - - boolean hasGroup() { - return group != null; - } - - ElementGroup getGroup() { - return group; - } + ElementGroup getGroup(); } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/GroupableElementDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/GroupableElementDslContext.java index a26b58cd6..bc6b337d1 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/GroupableElementDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/GroupableElementDslContext.java @@ -2,14 +2,26 @@ import com.structurizr.model.GroupableElement; -abstract class GroupableElementDslContext extends ModelItemDslContext { +abstract class GroupableElementDslContext extends ElementDslContext implements GroupableDslContext { + + private final ElementGroup group; GroupableElementDslContext() { - super(); + this.group = null; } GroupableElementDslContext(ElementGroup group) { - super(group); + this.group = group; + } + + @Override + public boolean hasGroup() { + return group != null; + } + + @Override + public ElementGroup getGroup() { + return group; } abstract GroupableElement getElement(); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeDslContext.java index 97487a2a5..bda1154ee 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeDslContext.java @@ -1,11 +1,12 @@ package com.structurizr.dsl; +import com.structurizr.model.GroupableElement; import com.structurizr.model.InfrastructureNode; import com.structurizr.model.ModelItem; -final class InfrastructureNodeDslContext extends ModelItemDslContext { +final class InfrastructureNodeDslContext extends GroupableElementDslContext { - private InfrastructureNode infrastructureNode; + private final InfrastructureNode infrastructureNode; InfrastructureNodeDslContext(InfrastructureNode infrastructureNode) { this.infrastructureNode = infrastructureNode; @@ -20,6 +21,11 @@ ModelItem getModelItem() { return getInfrastructureNode(); } + @Override + GroupableElement getElement() { + return infrastructureNode; + } + @Override protected String[] getPermittedTokens() { return new String[] { diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelDslContext.java index 7903af346..795354332 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelDslContext.java @@ -1,13 +1,24 @@ package com.structurizr.dsl; -final class ModelDslContext extends GroupableDslContext { +final class ModelDslContext extends DslContext implements GroupableDslContext { + + private ElementGroup group; ModelDslContext() { - super(null); } ModelDslContext(ElementGroup group) { - super(group); + this.group = group; + } + + @Override + public boolean hasGroup() { + return group != null; + } + + @Override + public ElementGroup getGroup() { + return group; } @Override diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemDslContext.java index 72150de5a..823702ffc 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemDslContext.java @@ -2,16 +2,12 @@ import com.structurizr.model.ModelItem; -abstract class ModelItemDslContext extends GroupableDslContext { +abstract class ModelItemDslContext extends DslContext { ModelItemDslContext() { super(); } - ModelItemDslContext(ElementGroup group) { - super(group); - } - abstract ModelItem getModelItem(); } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemParser.java index 823728bf0..f98cb241d 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemParser.java @@ -1,7 +1,5 @@ package com.structurizr.dsl; -import com.structurizr.model.Element; - final class ModelItemParser extends AbstractParser { private final static int DESCRIPTION_INDEX = 1; @@ -26,7 +24,7 @@ void parseTags(ModelItemDslContext context, Tokens tokens) { } } - void parseDescription(ModelItemDslContext context, Tokens tokens) { + void parseDescription(ElementDslContext context, Tokens tokens) { // description if (tokens.hasMoreThan(DESCRIPTION_INDEX)) { throw new RuntimeException("Too many tokens, expected: description "); @@ -37,7 +35,7 @@ void parseDescription(ModelItemDslContext context, Tokens tokens) { } String description = tokens.get(DESCRIPTION_INDEX); - ((Element)context.getModelItem()).setDescription(description); + context.getElement().setDescription(description); } void parseUrl(ModelItemDslContext context, Tokens tokens) { diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonParser.java index c2103af51..aecc819eb 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonParser.java @@ -10,7 +10,7 @@ final class PersonParser extends AbstractParser { private final static int DESCRIPTION_INDEX = 2; private final static int TAGS_INDEX = 3; - Person parse(GroupableDslContext context, Tokens tokens) { + Person parse(ModelDslContext context, Tokens tokens) { // person [description] [tags] if (tokens.hasMoreThan(TAGS_INDEX)) { diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemInstanceDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemInstanceDslContext.java index 9da1e6760..607db5485 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemInstanceDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemInstanceDslContext.java @@ -1,12 +1,13 @@ package com.structurizr.dsl; +import com.structurizr.model.Element; import com.structurizr.model.SoftwareSystemInstance; import com.structurizr.model.ModelItem; import com.structurizr.model.StaticStructureElementInstance; final class SoftwareSystemInstanceDslContext extends StaticStructureElementInstanceDslContext { - private SoftwareSystemInstance softwareSystemInstance; + private final SoftwareSystemInstance softwareSystemInstance; SoftwareSystemInstanceDslContext(SoftwareSystemInstance softwareSystemInstance) { this.softwareSystemInstance = softwareSystemInstance; @@ -21,6 +22,11 @@ ModelItem getModelItem() { return getSoftwareSystemInstance(); } + @Override + Element getElement() { + return getSoftwareSystemInstance(); + } + @Override StaticStructureElementInstance getElementInstance() { return getSoftwareSystemInstance(); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemParser.java index 46cc0c9c4..243cd6d6c 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemParser.java @@ -10,7 +10,7 @@ final class SoftwareSystemParser extends AbstractParser { private final static int DESCRIPTION_INDEX = 2; private final static int TAGS_INDEX = 3; - SoftwareSystem parse(GroupableDslContext context, Tokens tokens) { + SoftwareSystem parse(ModelDslContext context, Tokens tokens) { // softwareSystem [description] [tags] if (tokens.hasMoreThan(TAGS_INDEX)) { diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StaticStructureElementInstanceDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StaticStructureElementInstanceDslContext.java index 1a01b973d..7066961c3 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StaticStructureElementInstanceDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StaticStructureElementInstanceDslContext.java @@ -2,7 +2,7 @@ import com.structurizr.model.StaticStructureElementInstance; -abstract class StaticStructureElementInstanceDslContext extends ModelItemDslContext { +abstract class StaticStructureElementInstanceDslContext extends ElementDslContext { abstract StaticStructureElementInstance getElementInstance(); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index a3bbac50c..d1e2cf10c 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -356,7 +356,7 @@ void parse(List lines, File dslFile, boolean include) throws Structurizr } } else if (CUSTOM_ELEMENT_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ModelDslContext.class))) { - CustomElement customElement = new CustomElementParser().parse(getContext(GroupableDslContext.class), tokens.withoutContextStartToken()); + CustomElement customElement = new CustomElementParser().parse(getContext(ModelDslContext.class), tokens.withoutContextStartToken()); if (shouldStartContext(tokens)) { startContext(new CustomElementDslContext(customElement)); @@ -365,7 +365,7 @@ void parse(List lines, File dslFile, boolean include) throws Structurizr registerIdentifier(identifier, customElement); } else if (PERSON_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ModelDslContext.class))) { - Person person = new PersonParser().parse(getContext(GroupableDslContext.class), tokens.withoutContextStartToken()); + Person person = new PersonParser().parse(getContext(ModelDslContext.class), tokens.withoutContextStartToken()); if (shouldStartContext(tokens)) { startContext(new PersonDslContext(person)); @@ -374,7 +374,7 @@ void parse(List lines, File dslFile, boolean include) throws Structurizr registerIdentifier(identifier, person); } else if (SOFTWARE_SYSTEM_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ModelDslContext.class))) { - SoftwareSystem softwareSystem = new SoftwareSystemParser().parse(getContext(GroupableDslContext.class), tokens.withoutContextStartToken()); + SoftwareSystem softwareSystem = new SoftwareSystemParser().parse(getContext(ModelDslContext.class), tokens.withoutContextStartToken()); if (shouldStartContext(tokens)) { startContext(new SoftwareSystemDslContext(softwareSystem)); @@ -434,14 +434,14 @@ void parse(List lines, File dslFile, boolean include) throws Structurizr DeploymentNode deploymentNode = getContext(DeploymentNodeDslContext.class).getDeploymentNode(); startContext(new DeploymentNodeDslContext(deploymentNode, group)); registerIdentifier(identifier, group); - } else if ((TAGS_TOKEN.equalsIgnoreCase(firstToken) || TAG_TOKEN.equalsIgnoreCase(firstToken)) && inContext(ModelItemDslContext.class) && !getContext(ModelItemDslContext.class).hasGroup()) { + } else if ((TAGS_TOKEN.equalsIgnoreCase(firstToken) || TAG_TOKEN.equalsIgnoreCase(firstToken)) && inContext(ModelItemDslContext.class) && !isGroup(getContext())) { new ModelItemParser().parseTags(getContext(ModelItemDslContext.class), tokens); } else if ((TAGS_TOKEN.equalsIgnoreCase(firstToken) || TAG_TOKEN.equalsIgnoreCase(firstToken)) && inContext(ModelItemsDslContext.class)) { new ModelItemsParser().parseTags(getContext(ModelItemsDslContext.class), tokens); - } else if (DESCRIPTION_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelItemDslContext.class) && getContext(ModelItemDslContext.class).getModelItem() instanceof Element && !getContext(ModelItemDslContext.class).hasGroup()) { - new ModelItemParser().parseDescription(getContext(ModelItemDslContext.class), tokens); + } else if (DESCRIPTION_TOKEN.equalsIgnoreCase(firstToken) && inContext(ElementDslContext.class) && !isGroup(getContext())) { + new ModelItemParser().parseDescription(getContext(ElementDslContext.class), tokens); } else if (TECHNOLOGY_TOKEN.equalsIgnoreCase(firstToken) && inContext(ContainerDslContext.class) && !getContext(ContainerDslContext.class).hasGroup()) { new ContainerParser().parseTechnology(getContext(ContainerDslContext.class), tokens); @@ -458,7 +458,7 @@ void parse(List lines, File dslFile, boolean include) throws Structurizr } else if (INSTANCES_TOKEN.equalsIgnoreCase(firstToken) && inContext(DeploymentNodeDslContext.class)) { new DeploymentNodeParser().parseInstances(getContext(DeploymentNodeDslContext.class), tokens); - } else if (URL_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelItemDslContext.class) && !getContext(ModelItemDslContext.class).hasGroup()) { + } else if (URL_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelItemDslContext.class) && !isGroup(getContext())) { new ModelItemParser().parseUrl(getContext(ModelItemDslContext.class), tokens); } else if (PROPERTIES_TOKEN.equalsIgnoreCase(firstToken) && inContext(WorkspaceDslContext.class)) { @@ -467,10 +467,10 @@ void parse(List lines, File dslFile, boolean include) throws Structurizr } else if (PROPERTIES_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelDslContext.class)) { startContext(new PropertiesDslContext(workspace.getModel())); - } else if (PROPERTIES_TOKEN.equalsIgnoreCase(firstToken) && inContext(ConfigurationDslContext.class) && !getContext(ModelItemDslContext.class).hasGroup()) { + } else if (PROPERTIES_TOKEN.equalsIgnoreCase(firstToken) && inContext(ConfigurationDslContext.class)) { startContext(new PropertiesDslContext(getContext(ConfigurationDslContext.class).getWorkspace())); - } else if (PROPERTIES_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelItemDslContext.class) && !getContext(ModelItemDslContext.class).hasGroup()) { + } else if (PROPERTIES_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelItemDslContext.class) && !isGroup(getContext())) { startContext(new PropertiesDslContext(getContext(ModelItemDslContext.class).getModelItem())); } else if (PROPERTIES_TOKEN.equalsIgnoreCase(firstToken) && inContext(ViewsDslContext.class)) { @@ -491,7 +491,7 @@ void parse(List lines, File dslFile, boolean include) throws Structurizr } else if (inContext(PropertiesDslContext.class)) { new PropertyParser().parse(getContext(PropertiesDslContext.class), tokens); - } else if (PERSPECTIVES_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelItemDslContext.class) && !getContext(ModelItemDslContext.class).hasGroup()) { + } else if (PERSPECTIVES_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelItemDslContext.class) && !isGroup(getContext())) { startContext(new ModelItemPerspectivesDslContext(getContext(ModelItemDslContext.class).getModelItem())); } else if (inContext(ModelItemPerspectivesDslContext.class)) { @@ -1053,6 +1053,14 @@ private void endContext() throws StructurizrDslParserException { } } + private boolean isGroup(DslContext context) { + if (context instanceof GroupableDslContext) { + return ((GroupableDslContext)context).hasGroup(); + } + + return false; + } + /** * Gets the identifier register in use (this is the mapping of DSL identifiers to elements/relationships). * diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/GroupParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/GroupParserTests.java index 0186144de..ef1af486a 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/GroupParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/GroupParserTests.java @@ -47,7 +47,7 @@ void parse() { @Test void parse_NestedGroup_ThrowsAnExceptionWhenNestedGroupsAreNotConfigured() { - ModelDslContext context = new ModelDslContext(new ElementGroup(workspace.getModel(), "Group 1")); + ModelDslContext context = new ModelDslContext(new ElementGroup("Group 1")); context.setWorkspace(workspace); try { @@ -61,7 +61,7 @@ void parse_NestedGroup_ThrowsAnExceptionWhenNestedGroupsAreNotConfigured() { @Test void parse_NestedGroup() { workspace.getModel().addProperty("structurizr.groupSeparator", "/"); - ModelDslContext context = new ModelDslContext(new ElementGroup(workspace.getModel(), "Group 1")); + ModelDslContext context = new ModelDslContext(new ElementGroup("Group 1")); context.setWorkspace(workspace); ElementGroup group = parser.parse(context, tokens("group", "Group 2", "{")); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ModelItemParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ModelItemParserTests.java index 379ce8e29..011826924 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ModelItemParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ModelItemParserTests.java @@ -44,7 +44,7 @@ void test_parseTags_AddsTheTags_WhenTagsAreSpecified() { @Test void test_parseDescription_ThrowsAnException_WhenThereAreTooManyTokens() { try { - ModelItemDslContext context = new SoftwareSystemDslContext(null); + SoftwareSystemDslContext context = new SoftwareSystemDslContext(null); parser.parseDescription(context, tokens("description", "description", "extra")); fail(); } catch (Exception e) { @@ -56,7 +56,7 @@ void test_parseDescription_ThrowsAnException_WhenThereAreTooManyTokens() { void test_parseDescription_ThrowsAnException_WhenNoDescriptionIsSpecified() { try { SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); - ModelItemDslContext context = new SoftwareSystemDslContext(softwareSystem); + SoftwareSystemDslContext context = new SoftwareSystemDslContext(softwareSystem); parser.parseDescription(context, tokens("description")); fail(); } catch (Exception e) { @@ -67,7 +67,7 @@ void test_parseDescription_ThrowsAnException_WhenNoDescriptionIsSpecified() { @Test void test_parseDescription_SetsTheDescription_WhenADescriptionIsSpecified() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", ""); - ModelItemDslContext context = new SoftwareSystemDslContext(softwareSystem); + SoftwareSystemDslContext context = new SoftwareSystemDslContext(softwareSystem); parser.parseDescription(context, tokens("description", "Description")); assertEquals("Description", softwareSystem.getDescription()); From aeb71a6210d5534f27aac4b41374e67c978c69f5 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 20 Aug 2024 14:20:01 +0100 Subject: [PATCH 543/717] Adds the ability to bulk create relationships via the `!elements` keyword. --- .../java/com/structurizr/dsl/DslContext.java | 27 ++++-- .../dsl/ExplicitRelationshipParser.java | 97 +++++++++++++++---- .../dsl/ImplicitRelationshipParser.java | 50 +++++++++- .../structurizr/dsl/StructurizrDslParser.java | 14 ++- .../java/com/structurizr/dsl/DslTests.java | 9 ++ .../dsl/ImplicitRelationshipParserTests.java | 20 ++-- .../test/resources/dsl/bulk-operations.dsl | 59 +++++++++++ 7 files changed, 229 insertions(+), 47 deletions(-) create mode 100644 structurizr-dsl/src/test/resources/dsl/bulk-operations.dsl diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DslContext.java index ef529853c..a5d6f02a2 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/DslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DslContext.java @@ -49,18 +49,25 @@ Element getElement(String identifier, Class type) { identifier = identifier.toLowerCase(); if (identifiersRegister.getIdentifierScope() == IdentifierScope.Hierarchical) { - if (this instanceof ModelItemDslContext) { - ModelItemDslContext modelItemDslContext = (ModelItemDslContext)this; - if (modelItemDslContext.getModelItem() instanceof Element) { - Element parent = (Element)modelItemDslContext.getModelItem(); - while (parent != null && element == null) { - String parentIdentifier = identifiersRegister.findIdentifier(parent); + ElementDslContext elementDslContext = null; + if (this instanceof ElementDslContext) { + elementDslContext = (ElementDslContext)this; + } else if (this instanceof ElementsDslContext) { + ElementsDslContext elementsDslContext = (ElementsDslContext)this; + if (elementsDslContext.getParentDslContext() instanceof ElementDslContext) { + elementDslContext = (ElementDslContext)elementsDslContext.getParentDslContext(); + } + } - element = identifiersRegister.getElement(parentIdentifier + "." + identifier); - parent = parent.getParent(); + if (elementDslContext != null) { + Element parent = elementDslContext.getElement(); + while (parent != null && element == null) { + String parentIdentifier = identifiersRegister.findIdentifier(parent); - element = checkElementType(element, type); - } + element = identifiersRegister.getElement(parentIdentifier + "." + identifier); + parent = parent.getParent(); + + element = checkElementType(element, type); } } else if (this instanceof DeploymentEnvironmentDslContext) { DeploymentEnvironmentDslContext deploymentEnvironmentDslContext = (DeploymentEnvironmentDslContext)this; diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ExplicitRelationshipParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ExplicitRelationshipParser.java index 055167d93..4771af040 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ExplicitRelationshipParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ExplicitRelationshipParser.java @@ -1,6 +1,11 @@ package com.structurizr.dsl; -import com.structurizr.model.*; +import com.structurizr.model.Element; +import com.structurizr.model.Relationship; + +import javax.lang.model.util.Elements; +import java.util.LinkedHashSet; +import java.util.Set; final class ExplicitRelationshipParser extends AbstractRelationshipParser { @@ -24,30 +29,55 @@ Relationship parse(DslContext context, Tokens tokens) { } String sourceId = tokens.get(SOURCE_IDENTIFIER_INDEX); + Element sourceElement = findElement(sourceId, context); + if (sourceElement == null) { + throw new RuntimeException("The source element \"" + sourceId + "\" does not exist"); + } + String destinationId = tokens.get(DESTINATION_IDENTIFIER_INDEX); + Element destinationElement = findElement(destinationId, context); + if (destinationElement == null) { + throw new RuntimeException("The destination element \"" + destinationId + "\" does not exist"); + } - Element sourceElement = context.getElement(sourceId); - Element destinationElement = context.getElement(destinationId); + String description = ""; + if (tokens.includes(DESCRIPTION_INDEX)) { + description = tokens.get(DESCRIPTION_INDEX); + } - if (sourceElement == null) { - if (StructurizrDslTokens.THIS_TOKEN.equalsIgnoreCase(sourceId) && context instanceof GroupableElementDslContext) { - GroupableElementDslContext groupableElementDslContext = (GroupableElementDslContext)context; - sourceElement = groupableElementDslContext.getElement(); - } else { - throw new RuntimeException("The source element \"" + sourceId + "\" does not exist"); - } + String technology = ""; + if (tokens.includes(TECHNOLOGY_INDEX)) { + technology = tokens.get(TECHNOLOGY_INDEX); } - if (destinationElement == null) { - if (StructurizrDslTokens.THIS_TOKEN.equalsIgnoreCase(destinationId) && context instanceof ModelItemDslContext) { - ModelItemDslContext modelItemDslContext = (ModelItemDslContext) context; - if (modelItemDslContext.getModelItem() instanceof Element) { - destinationElement = (Element)modelItemDslContext.getModelItem(); - } - } + String[] tags = new String[0]; + if (tokens.includes(TAGS_INDEX)) { + tags = tokens.get(TAGS_INDEX).split(","); } - if (destinationElement == null) { + return createRelationship(sourceElement, description, technology, tags, destinationElement); + } + + void parse(ElementsDslContext context, Tokens tokens) { + // -> [description] [technology] [tags] + + if (tokens.hasMoreThan(TAGS_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } + + if (!tokens.includes(DESTINATION_IDENTIFIER_INDEX)) { + throw new RuntimeException("Expected: " + GRAMMAR); + } + + String sourceId = tokens.get(SOURCE_IDENTIFIER_INDEX); + Set sourceElements = findElements(sourceId, context); + if (sourceElements.isEmpty()) { + throw new RuntimeException("The source element \"" + sourceId + "\" does not exist"); + } + + String destinationId = tokens.get(DESTINATION_IDENTIFIER_INDEX); + Set destinationElements = findElements(destinationId, context); + if (destinationElements.isEmpty()) { throw new RuntimeException("The destination element \"" + destinationId + "\" does not exist"); } @@ -66,7 +96,36 @@ Relationship parse(DslContext context, Tokens tokens) { tags = tokens.get(TAGS_INDEX).split(","); } - return createRelationship(sourceElement, description, technology, tags, destinationElement); + for (Element sourceElement : sourceElements) { + for (Element destinationElement : destinationElements) { + createRelationship(sourceElement, description, technology, tags, destinationElement); + } + } + } + + private Element findElement(String identifier, DslContext context) { + Element element = context.getElement(identifier); + + if (element == null && StructurizrDslTokens.THIS_TOKEN.equalsIgnoreCase(identifier) && context instanceof ElementDslContext) { + element = ((ElementDslContext)context).getElement(); + } + + return element; + } + + private Set findElements(String identifier, ElementsDslContext context) { + Element element = context.getElement(identifier); + Set elements = new LinkedHashSet<>(); + + if (element == null) { + if (StructurizrDslTokens.THIS_TOKEN.equalsIgnoreCase(identifier)) { + elements.addAll(context.getElements()); + } + } else { + elements.add(element); + } + + return elements; } } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImplicitRelationshipParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImplicitRelationshipParser.java index d076dffa2..448ee67d1 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImplicitRelationshipParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImplicitRelationshipParser.java @@ -1,6 +1,9 @@ package com.structurizr.dsl; -import com.structurizr.model.*; +import com.structurizr.model.Element; +import com.structurizr.model.Relationship; + +import java.util.Set; final class ImplicitRelationshipParser extends AbstractRelationshipParser { @@ -11,7 +14,7 @@ final class ImplicitRelationshipParser extends AbstractRelationshipParser { private final static int TECHNOLOGY_INDEX = 3; private final static int TAGS_INDEX = 4; - Relationship parse(ModelItemDslContext context, Tokens tokens) { + Relationship parse(ElementDslContext context, Tokens tokens) { // -> [description] [technology] [tags] if (tokens.hasMoreThan(TAGS_INDEX)) { @@ -24,9 +27,9 @@ Relationship parse(ModelItemDslContext context, Tokens tokens) { String destinationId = tokens.get(DESTINATION_IDENTIFIER_INDEX); - Element sourceElement = (Element)context.getModelItem(); - Element destinationElement = context.getElement(destinationId); + Element sourceElement = context.getElement(); + Element destinationElement = context.getElement(destinationId); if (destinationElement == null) { throw new RuntimeException("The destination element \"" + destinationId + "\" does not exist"); } @@ -49,4 +52,43 @@ Relationship parse(ModelItemDslContext context, Tokens tokens) { return createRelationship(sourceElement, description, technology, tags, destinationElement); } + void parse(ElementsDslContext context, Tokens tokens) { + // -> [description] [technology] [tags] + + if (tokens.hasMoreThan(TAGS_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } + + if (!tokens.includes(DESTINATION_IDENTIFIER_INDEX)) { + throw new RuntimeException("Expected: " + GRAMMAR); + } + + Set sourceElements = context.getElements(); + + String destinationId = tokens.get(DESTINATION_IDENTIFIER_INDEX); + Element destinationElement = context.getElement(destinationId); + if (destinationElement == null) { + throw new RuntimeException("The destination element \"" + destinationId + "\" does not exist"); + } + + String description = ""; + if (tokens.includes(DESCRIPTION_INDEX)) { + description = tokens.get(DESCRIPTION_INDEX); + } + + String technology = ""; + if (tokens.includes(TECHNOLOGY_INDEX)) { + technology = tokens.get(TECHNOLOGY_INDEX); + } + + String[] tags = new String[0]; + if (tokens.includes(TAGS_INDEX)) { + tags = tokens.get(TAGS_INDEX).split(","); + } + + for (Element sourceElement : sourceElements) { + createRelationship(sourceElement, description, technology, tags, destinationElement); + } + } + } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index d1e2cf10c..41812077c 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -295,7 +295,7 @@ void parse(List lines, File dslFile, boolean include) throws Structurizr } else if (inContext(ExternalScriptDslContext.class)) { new ScriptParser().parseParameter(getContext(ExternalScriptDslContext.class), tokens); - } else if (tokens.size() > 2 && RELATIONSHIP_TOKEN.equals(tokens.get(1)) && (inContext(ModelDslContext.class) || inContext(CustomElementDslContext.class) || inContext(PersonDslContext.class) || inContext(SoftwareSystemDslContext.class) || inContext(ContainerDslContext.class) || inContext(ComponentDslContext.class) || inContext(DeploymentEnvironmentDslContext.class) || inContext(DeploymentNodeDslContext.class) || inContext(InfrastructureNodeDslContext.class) || inContext(SoftwareSystemInstanceDslContext.class) || inContext(ContainerInstanceDslContext.class))) { + } else if (tokens.size() > 2 && RELATIONSHIP_TOKEN.equals(tokens.get(1)) && (inContext(ModelDslContext.class) || inContext(DeploymentEnvironmentDslContext.class) || inContext(ElementDslContext.class))) { Relationship relationship = new ExplicitRelationshipParser().parse(getContext(), tokens.withoutContextStartToken()); if (shouldStartContext(tokens)) { @@ -304,8 +304,8 @@ void parse(List lines, File dslFile, boolean include) throws Structurizr registerIdentifier(identifier, relationship); - } else if (tokens.size() >= 2 && RELATIONSHIP_TOKEN.equals(tokens.get(0)) && (inContext(CustomElementDslContext.class) || inContext(PersonDslContext.class) || inContext(SoftwareSystemDslContext.class) || inContext(ContainerDslContext.class) || inContext(ComponentDslContext.class) || inContext(DeploymentNodeDslContext.class) || inContext(InfrastructureNodeDslContext.class) || inContext(SoftwareSystemInstanceDslContext.class) || inContext(ContainerInstanceDslContext.class))) { - Relationship relationship = new ImplicitRelationshipParser().parse(getContext(ModelItemDslContext.class), tokens.withoutContextStartToken()); + } else if (tokens.size() >= 2 && RELATIONSHIP_TOKEN.equals(tokens.get(0)) && inContext(ElementDslContext.class)) { + Relationship relationship = new ImplicitRelationshipParser().parse(getContext(ElementDslContext.class), tokens.withoutContextStartToken()); if (shouldStartContext(tokens)) { startContext(new RelationshipDslContext(relationship)); @@ -313,6 +313,12 @@ void parse(List lines, File dslFile, boolean include) throws Structurizr registerIdentifier(identifier, relationship); + } else if (tokens.size() > 2 && RELATIONSHIP_TOKEN.equals(tokens.get(1)) && inContext(ElementsDslContext.class)) { + new ExplicitRelationshipParser().parse(getContext(ElementsDslContext.class), tokens.withoutContextStartToken()); + + } else if (tokens.size() >= 2 && RELATIONSHIP_TOKEN.equals(tokens.get(0)) && inContext(ElementsDslContext.class)) { + new ImplicitRelationshipParser().parse(getContext(ElementsDslContext.class), tokens.withoutContextStartToken()); + } else if ((REF_TOKEN.equalsIgnoreCase(firstToken) || EXTEND_TOKEN.equalsIgnoreCase(firstToken)) && (inContext(ModelDslContext.class))) { ModelItem modelItem = new RefParser().parse(getContext(), tokens.withoutContextStartToken()); @@ -348,7 +354,7 @@ void parse(List lines, File dslFile, boolean include) throws Structurizr } } - } else if (ELEMENTS_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ModelDslContext.class) || inContext(ModelItemDslContext.class))) { + } else if (ELEMENTS_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ModelDslContext.class) || inContext(ElementDslContext.class))) { Set elements = new ElementsParser().parse(getContext(), tokens.withoutContextStartToken()); if (shouldStartContext(tokens)) { diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index e34e718c1..680f6d972 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -1143,4 +1143,13 @@ void test_Var_CannotOverrideConst() { } } + + @Test + void test_bulkOperations() throws Exception { + File dslFile = new File("src/test/resources/dsl/bulk-operations.dsl"); + + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(dslFile); + } + } \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ImplicitRelationshipParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ImplicitRelationshipParserTests.java index 25f7d194b..c0786cf19 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ImplicitRelationshipParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ImplicitRelationshipParserTests.java @@ -9,8 +9,8 @@ class ImplicitRelationshipParserTests extends AbstractTests { private ImplicitRelationshipParser parser = new ImplicitRelationshipParser(); - private ModelItemDslContext context(Person person) { - ModelItemDslContext context = new PersonDslContext(person); + private ElementDslContext context(Person person) { + PersonDslContext context = new PersonDslContext(person); context.setWorkspace(workspace); model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); @@ -20,7 +20,7 @@ private ModelItemDslContext context(Person person) { @Test void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { try { - parser.parse(context(null), tokens("->", "destination", "description", "technology", "tags", "extra")); + parser.parse((ElementDslContext)null, tokens("->", "destination", "description", "technology", "tags", "extra")); fail(); } catch (Exception e) { assertEquals("Too many tokens, expected: -> [description] [technology] [tags]", e.getMessage()); @@ -30,7 +30,7 @@ void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { @Test void test_parse_ThrowsAnException_WhenTheDestinationIdentifierIsMissing() { try { - parser.parse(context(null), tokens("->")); + parser.parse((ElementDslContext)null, tokens("->")); fail(); } catch (Exception e) { assertEquals("Expected: -> [description] [technology] [tags]", e.getMessage()); @@ -40,7 +40,7 @@ void test_parse_ThrowsAnException_WhenTheDestinationIdentifierIsMissing() { @Test void test_parse_ThrowsAnException_WhenTheDestinationElementIsNotDefined() { Person user = model.addPerson("User", "Description"); - ModelItemDslContext context = context(user); + ElementDslContext context = context(user); IdentifiersRegister elements = new IdentifiersRegister(); context.setIdentifierRegister(elements); @@ -56,7 +56,7 @@ void test_parse_ThrowsAnException_WhenTheDestinationElementIsNotDefined() { void test_parse_AddsTheRelationship() { Person user = model.addPerson("User", "Description"); SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); - ModelItemDslContext context = context(user); + ElementDslContext context = context(user); IdentifiersRegister elements = new IdentifiersRegister(); elements.register("destination", softwareSystem); @@ -79,7 +79,7 @@ void test_parse_AddsTheRelationship() { void test_parse_AddsTheRelationshipWithADescription() { Person user = model.addPerson("User", "Description"); SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); - ModelItemDslContext context = context(user); + ElementDslContext context = context(user); IdentifiersRegister elements = new IdentifiersRegister(); elements.register("destination", softwareSystem); @@ -102,7 +102,7 @@ void test_parse_AddsTheRelationshipWithADescription() { void test_parse_AddsTheRelationshipWithADescriptionAndTechnology() { Person user = model.addPerson("User", "Description"); SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); - ModelItemDslContext context = context(user); + ElementDslContext context = context(user); IdentifiersRegister elements = new IdentifiersRegister(); elements.register("destination", softwareSystem); @@ -124,7 +124,7 @@ void test_parse_AddsTheRelationshipWithADescriptionAndTechnology() { void test_parse_AddsTheRelationshipWithADescriptionAndTechnologyAndTags() { Person user = model.addPerson("User", "Description"); SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); - ModelItemDslContext context = context(user); + ElementDslContext context = context(user); IdentifiersRegister elements = new IdentifiersRegister(); elements.register("destination", softwareSystem); @@ -148,7 +148,7 @@ void test_parse_AddsTheRelationshipAndImplicitRelationshipsWithADescriptionAndTe Person user = model.addPerson("User", "Description"); SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); Container container = softwareSystem.addContainer("Container", "Description", "Technology"); - ModelItemDslContext context = context(user); + ElementDslContext context = context(user); IdentifiersRegister elements = new IdentifiersRegister(); elements.register("destination", container); diff --git a/structurizr-dsl/src/test/resources/dsl/bulk-operations.dsl b/structurizr-dsl/src/test/resources/dsl/bulk-operations.dsl new file mode 100644 index 000000000..95fdcaa27 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/bulk-operations.dsl @@ -0,0 +1,59 @@ +workspace { + + model { + user = person "User" + + !identifiers flat + + softwareSystem1 = softwareSystem "Software System 1" { + application1 = container "Application" { + component "ComponentA" + component "ComponentB" + component "ComponentC" + } + + databaseSchema1 = container "Database Schema" + + !elements "element.parent==application1" { + tags "Tag 1" + user -> this "Uses 1" + this -> databaseSchema1 "Uses 1" + } + + !elements "element.parent==application1" { + -> databaseSchema1 "Uses 2" + } + } + + !identifiers hierarchical + + softwareSystem2 = softwareSystem "Software System 2" { + application2 = container "Application" { + component "ComponentA" + component "ComponentB" + component "ComponentC" + } + + databaseSchema2 = container "Database Schema" + + !elements "element.parent==application2" { + tags "Tag 1" + user -> this "Uses" + this -> softwareSystem2.databaseSchema2 "Uses 1" + } + + !elements "element.parent==application2" { + this -> databaseSchema2 "Uses 2" + } + + !elements "element.parent==application2" { + -> softwareSystem2.databaseSchema2 "Uses 3" + } + + !elements "element.parent==application2" { + -> databaseSchema2 "Uses 4" + } + } + } + +} \ No newline at end of file From 477bac967b7046abdea50470eb4b1376587605dc Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 20 Aug 2024 16:45:23 +0100 Subject: [PATCH 544/717] Adds an `!relationships` keyword that can be used to find a set of relationships via an expression. --- changelog.md | 1 + .../dsl/ExplicitRelationshipParser.java | 7 ++- .../dsl/ImplicitRelationshipParser.java | 8 +++- .../structurizr/dsl/ModelItemsDslContext.java | 4 ++ .../dsl/RelationshipsDslContext.java | 32 +++++++++++++ .../structurizr/dsl/RelationshipsParser.java | 29 ++++++++++++ .../structurizr/dsl/StructurizrDslParser.java | 19 +++++++- .../structurizr/dsl/StructurizrDslTokens.java | 2 + .../structurizr/dsl/ElementsParserTests.java | 2 +- .../dsl/RelationshipsParserTests.java | 45 +++++++++++++++++++ .../test/resources/dsl/bulk-operations.dsl | 24 +++++++--- 11 files changed, 160 insertions(+), 13 deletions(-) create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipsDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipsParser.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/RelationshipsParserTests.java diff --git a/changelog.md b/changelog.md index 0d0498992..903f6b2fb 100644 --- a/changelog.md +++ b/changelog.md @@ -8,6 +8,7 @@ - structurizr-dsl: Adds a way to explicitly specify the order of relationships in dynamic views. - structurizr-dsl: Adds support for element technology expressions (e.g. "element.technology==Java"). - structurizr-dsl: Adds an `!elements` keyword that can be used to find a set of elements via an expression. +- structurizr-dsl: Adds a `!relationships` keyword that can be used to find a set of relationships via an expression. ## 2.2.0 (2nd July 2024) diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ExplicitRelationshipParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ExplicitRelationshipParser.java index 4771af040..489958f19 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ExplicitRelationshipParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ExplicitRelationshipParser.java @@ -58,7 +58,7 @@ Relationship parse(DslContext context, Tokens tokens) { return createRelationship(sourceElement, description, technology, tags, destinationElement); } - void parse(ElementsDslContext context, Tokens tokens) { + Set parse(ElementsDslContext context, Tokens tokens) { // -> [description] [technology] [tags] if (tokens.hasMoreThan(TAGS_INDEX)) { @@ -96,11 +96,14 @@ void parse(ElementsDslContext context, Tokens tokens) { tags = tokens.get(TAGS_INDEX).split(","); } + Set relationships = new LinkedHashSet<>(); for (Element sourceElement : sourceElements) { for (Element destinationElement : destinationElements) { - createRelationship(sourceElement, description, technology, tags, destinationElement); + relationships.add(createRelationship(sourceElement, description, technology, tags, destinationElement)); } } + + return relationships; } private Element findElement(String identifier, DslContext context) { diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImplicitRelationshipParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImplicitRelationshipParser.java index 448ee67d1..19448e25d 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImplicitRelationshipParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImplicitRelationshipParser.java @@ -3,6 +3,7 @@ import com.structurizr.model.Element; import com.structurizr.model.Relationship; +import java.util.LinkedHashSet; import java.util.Set; final class ImplicitRelationshipParser extends AbstractRelationshipParser { @@ -52,7 +53,7 @@ Relationship parse(ElementDslContext context, Tokens tokens) { return createRelationship(sourceElement, description, technology, tags, destinationElement); } - void parse(ElementsDslContext context, Tokens tokens) { + Set parse(ElementsDslContext context, Tokens tokens) { // -> [description] [technology] [tags] if (tokens.hasMoreThan(TAGS_INDEX)) { @@ -86,9 +87,12 @@ void parse(ElementsDslContext context, Tokens tokens) { tags = tokens.get(TAGS_INDEX).split(","); } + Set relationships = new LinkedHashSet<>(); for (Element sourceElement : sourceElements) { - createRelationship(sourceElement, description, technology, tags, destinationElement); + relationships.add(createRelationship(sourceElement, description, technology, tags, destinationElement)); } + + return relationships; } } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemsDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemsDslContext.java index 1caf3055a..b4bedc9bc 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemsDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemsDslContext.java @@ -8,6 +8,10 @@ abstract class ModelItemsDslContext extends DslContext { private final DslContext parentDslContext; + public ModelItemsDslContext() { + this.parentDslContext = null; + } + ModelItemsDslContext(DslContext parentDslContext) { this.parentDslContext = parentDslContext; } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipsDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipsDslContext.java new file mode 100644 index 000000000..3797be712 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipsDslContext.java @@ -0,0 +1,32 @@ +package com.structurizr.dsl; + +import com.structurizr.model.ModelItem; +import com.structurizr.model.Relationship; + +import java.util.Set; +import java.util.stream.Collectors; + +class RelationshipsDslContext extends ModelItemsDslContext { + + private final Set relationships; + + RelationshipsDslContext(DslContext parentDslContext, Set relationships) { + super(parentDslContext); + this.relationships = relationships; + } + + Set getRelationships() { + return relationships; + } + + @Override + Set getModelItems() { + return relationships.stream().map(e -> (ModelItem)e).collect(Collectors.toSet()); + } + + @Override + protected String[] getPermittedTokens() { + return new String[0]; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipsParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipsParser.java new file mode 100644 index 000000000..eb0e0cf7b --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipsParser.java @@ -0,0 +1,29 @@ +package com.structurizr.dsl; + +import com.structurizr.model.Element; +import com.structurizr.model.ModelItem; +import com.structurizr.model.Relationship; + +import java.util.Set; +import java.util.stream.Collectors; + +final class RelationshipsParser extends AbstractParser { + + private static final String GRAMMAR = "!relationships "; + + private final static int EXPRESSION_INDEX = 1; + + Set parse(DslContext context, Tokens tokens) { + // !relationships + + if (tokens.hasMoreThan(EXPRESSION_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } + + String expression = tokens.get(1); + Set modelItems = new ExpressionParser().parseExpression(expression, context); + + return modelItems.stream().filter(mi -> mi instanceof Relationship).map(mi -> (Relationship)mi).collect(Collectors.toSet()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index 41812077c..d1d423ed0 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -314,10 +314,18 @@ void parse(List lines, File dslFile, boolean include) throws Structurizr registerIdentifier(identifier, relationship); } else if (tokens.size() > 2 && RELATIONSHIP_TOKEN.equals(tokens.get(1)) && inContext(ElementsDslContext.class)) { - new ExplicitRelationshipParser().parse(getContext(ElementsDslContext.class), tokens.withoutContextStartToken()); + Set relationships = new ExplicitRelationshipParser().parse(getContext(ElementsDslContext.class), tokens.withoutContextStartToken()); + + if (shouldStartContext(tokens)) { + startContext(new RelationshipsDslContext(getContext(), relationships)); + } } else if (tokens.size() >= 2 && RELATIONSHIP_TOKEN.equals(tokens.get(0)) && inContext(ElementsDslContext.class)) { - new ImplicitRelationshipParser().parse(getContext(ElementsDslContext.class), tokens.withoutContextStartToken()); + Set relationships = new ImplicitRelationshipParser().parse(getContext(ElementsDslContext.class), tokens.withoutContextStartToken()); + + if (shouldStartContext(tokens)) { + startContext(new RelationshipsDslContext(getContext(), relationships)); + } } else if ((REF_TOKEN.equalsIgnoreCase(firstToken) || EXTEND_TOKEN.equalsIgnoreCase(firstToken)) && (inContext(ModelDslContext.class))) { ModelItem modelItem = new RefParser().parse(getContext(), tokens.withoutContextStartToken()); @@ -361,6 +369,13 @@ void parse(List lines, File dslFile, boolean include) throws Structurizr startContext(new ElementsDslContext(getContext(), elements)); } + } else if (RELATIONSHIPS_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ModelDslContext.class) || inContext(ElementDslContext.class))) { + Set relationships = new RelationshipsParser().parse(getContext(), tokens.withoutContextStartToken()); + + if (shouldStartContext(tokens)) { + startContext(new RelationshipsDslContext(getContext(), relationships)); + } + } else if (CUSTOM_ELEMENT_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ModelDslContext.class))) { CustomElement customElement = new CustomElementParser().parse(getContext(ModelDslContext.class), tokens.withoutContextStartToken()); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java index ab7aff0b1..4ef709312 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java @@ -107,6 +107,8 @@ class StructurizrDslTokens { static final String IMPLIED_RELATIONSHIPS_TOKEN = "!impliedRelationships"; static final String REF_TOKEN = "!ref"; static final String ELEMENTS_TOKEN = "!elements"; + static final String RELATIONSHIPS_TOKEN = "!relationships"; + static final String EXTEND_TOKEN = "!extend"; static final String PLUGIN_TOKEN = "!plugin"; static final String SCRIPT_TOKEN = "!script"; diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementsParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementsParserTests.java index 3af081588..6e437b972 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementsParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementsParserTests.java @@ -31,7 +31,7 @@ void test_parse_FindsElementsByExpression() { Component componentB = application.addComponent("B"); Component componentC = application.addComponent("C"); - ModelItemDslContext context = new ContainerDslContext(application); + ContainerDslContext context = new ContainerDslContext(application); context.setWorkspace(workspace); IdentifiersRegister register = new IdentifiersRegister(); register.register("application", application); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/RelationshipsParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/RelationshipsParserTests.java new file mode 100644 index 000000000..c3e685ccc --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/RelationshipsParserTests.java @@ -0,0 +1,45 @@ +package com.structurizr.dsl; + +import com.structurizr.model.Relationship; +import com.structurizr.model.SoftwareSystem; +import org.junit.jupiter.api.Test; + +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.*; + +class RelationshipsParserTests extends AbstractTests { + + private final RelationshipsParser parser = new RelationshipsParser(); + + @Test + void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parse(context(), tokens("!relationships", "expression", "tokens")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: !relationships ", e.getMessage()); + } + } + + @Test + void test_parse_FindsRelationshipsByExpression() { + SoftwareSystem a = model.addSoftwareSystem("A"); + SoftwareSystem b = model.addSoftwareSystem("B"); + SoftwareSystem c = model.addSoftwareSystem("C"); + + Relationship relationship1 = a.uses(b, "Uses"); + Relationship relationship2 = b.uses(c, "Uses"); + Relationship relationship3 = a.uses(c, "Uses"); + + ModelDslContext context = context(); + IdentifiersRegister register = new IdentifiersRegister(); + register.register("c", c); + context.setIdentifierRegister(register); + + Set relationships = parser.parse(context, tokens("!relationships", "*->c")); + assertTrue(relationships.contains(relationship2)); + assertTrue(relationships.contains(relationship3)); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/bulk-operations.dsl b/structurizr-dsl/src/test/resources/dsl/bulk-operations.dsl index 95fdcaa27..d1833badc 100644 --- a/structurizr-dsl/src/test/resources/dsl/bulk-operations.dsl +++ b/structurizr-dsl/src/test/resources/dsl/bulk-operations.dsl @@ -17,11 +17,15 @@ workspace { !elements "element.parent==application1" { tags "Tag 1" user -> this "Uses 1" - this -> databaseSchema1 "Uses 1" + this -> databaseSchema1 "Uses 1" { + tags "Tag" + } } !elements "element.parent==application1" { - -> databaseSchema1 "Uses 2" + -> databaseSchema1 "Uses 2" { + tags "Tag" + } } } @@ -39,19 +43,27 @@ workspace { !elements "element.parent==application2" { tags "Tag 1" user -> this "Uses" - this -> softwareSystem2.databaseSchema2 "Uses 1" + this -> softwareSystem2.databaseSchema2 "Uses 1" { + tags "Tag" + } } !elements "element.parent==application2" { - this -> databaseSchema2 "Uses 2" + this -> databaseSchema2 "Uses 2" { + tags "Tag" + } } !elements "element.parent==application2" { - -> softwareSystem2.databaseSchema2 "Uses 3" + -> softwareSystem2.databaseSchema2 "Uses 3" { + tags "Tag" + } } !elements "element.parent==application2" { - -> databaseSchema2 "Uses 4" + -> databaseSchema2 "Uses 4" { + tags "Tag" + } } } } From 0f2b05fb75a2a9f84131ac5ee44914857b48c1b6 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 20 Aug 2024 17:03:18 +0100 Subject: [PATCH 545/717] Initial version of DSL wrapper for the component finder. --- changelog.md | 2 + structurizr-dsl/build.gradle | 1 + .../dsl/ComponentFinderDslContext.java | 32 ++++ .../dsl/ComponentFinderParser.java | 28 ++++ .../ComponentFinderStrategyDslContext.java | 34 ++++ .../dsl/ComponentFinderStrategyParser.java | 158 ++++++++++++++++++ .../structurizr/dsl/StructurizrDslParser.java | 30 ++++ .../structurizr/dsl/StructurizrDslTokens.java | 9 + .../java/com/structurizr/dsl/DslTests.java | 56 +++++++ .../test/resources/dsl/spring-petclinic.dsl | 96 +++++++++++ 10 files changed, 446 insertions(+) create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyParser.java create mode 100644 structurizr-dsl/src/test/resources/dsl/spring-petclinic.dsl diff --git a/changelog.md b/changelog.md index 903f6b2fb..16c2ac577 100644 --- a/changelog.md +++ b/changelog.md @@ -3,12 +3,14 @@ ## unreleased - structurizr-core: Adds name-value properties to dynamic view relationship views (https://github.com/structurizr/java/issues/316). +- structurizr-component: Initial rewrite of the original `structurizr-analysis` library - provides a way to automatically find components in a Java codebase. - structurizr-dsl: Adds name-value properties to dynamic view relationship views. - structurizr-dsl: Fixes https://github.com/structurizr/java/issues/312 (!include doesn't work with files encoded as UTF-8 BOM). - structurizr-dsl: Adds a way to explicitly specify the order of relationships in dynamic views. - structurizr-dsl: Adds support for element technology expressions (e.g. "element.technology==Java"). - structurizr-dsl: Adds an `!elements` keyword that can be used to find a set of elements via an expression. - structurizr-dsl: Adds a `!relationships` keyword that can be used to find a set of relationships via an expression. +- structurizr-dsl: Adds a DSL wrapper around the `structurizr-component` component finder. ## 2.2.0 (2nd July 2024) diff --git a/structurizr-dsl/build.gradle b/structurizr-dsl/build.gradle index 3416670df..2b6eaabbb 100644 --- a/structurizr-dsl/build.gradle +++ b/structurizr-dsl/build.gradle @@ -2,6 +2,7 @@ dependencies { api project(':structurizr-client') api project(':structurizr-import') + api project(':structurizr-component') testImplementation 'org.codehaus.groovy:groovy-jsr223:3.0.19' testImplementation 'org.jetbrains.kotlin:kotlin-scripting-jsr223:1.8.10' diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderDslContext.java new file mode 100644 index 000000000..c63b2fc3c --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderDslContext.java @@ -0,0 +1,32 @@ +package com.structurizr.dsl; + +import com.structurizr.component.ComponentFinderBuilder; +import com.structurizr.model.Container; + +final class ComponentFinderDslContext extends DslContext { + + private final ComponentFinderBuilder componentFinderBuilder = new ComponentFinderBuilder(); + + ComponentFinderDslContext(Container container) { + componentFinderBuilder.forContainer(container); + } + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.COMPONENT_FINDER_CLASSES_TOKEN, + StructurizrDslTokens.COMPONENT_FINDER_SOURCE_TOKEN, + StructurizrDslTokens.COMPONENT_FINDER_STRATEGY_TOKEN + }; + } + + ComponentFinderBuilder getComponentFinderBuilder() { + return this.componentFinderBuilder; + } + + @Override + void end() { + componentFinderBuilder.build().findComponents(); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderParser.java new file mode 100644 index 000000000..61d0d49d6 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderParser.java @@ -0,0 +1,28 @@ +package com.structurizr.dsl; + +final class ComponentFinderParser extends AbstractParser { + + private static final String CLASSES_GRAMMAR = "classes "; + private static final String SOURCE_GRAMMAR = "source "; + + void parseClasses(ComponentFinderDslContext context, Tokens tokens) { + // classes + + if (tokens.hasMoreThan(1)) { + throw new RuntimeException("Too many tokens, expected: " + CLASSES_GRAMMAR); + } + + context.getComponentFinderBuilder().fromClasses(tokens.get(1)); + } + + void parseSource(ComponentFinderDslContext context, Tokens tokens) { + // source + + if (tokens.hasMoreThan(1)) { + throw new RuntimeException("Too many tokens, expected: " + SOURCE_GRAMMAR); + } + + context.getComponentFinderBuilder().fromSource(tokens.get(1)); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyDslContext.java new file mode 100644 index 000000000..fede404b4 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyDslContext.java @@ -0,0 +1,34 @@ +package com.structurizr.dsl; + +import com.structurizr.component.ComponentFinderBuilder; +import com.structurizr.component.ComponentFinderStrategyBuilder; + +final class ComponentFinderStrategyDslContext extends DslContext { + + private final ComponentFinderBuilder componentFinderBuilder; + private final ComponentFinderStrategyBuilder componentFinderStrategyBuilder = new ComponentFinderStrategyBuilder(); + + ComponentFinderStrategyDslContext(ComponentFinderBuilder componentFinderBuilder) { + this.componentFinderBuilder = componentFinderBuilder; + } + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.COMPONENT_FINDER_STRATEGY_MATCHER_TOKEN, + StructurizrDslTokens.COMPONENT_FINDER_STRATEGY_FILTER_TOKEN, + StructurizrDslTokens.COMPONENT_FINDER_STRATEGY_SUPPORTING_TYPES_TOKEN, + StructurizrDslTokens.COMPONENT_FINDER_STRATEGY_NAMING_TOKEN + }; + } + + ComponentFinderStrategyBuilder getComponentFinderStrategyBuilder() { + return this.componentFinderStrategyBuilder; + } + + @Override + void end() { + componentFinderBuilder.withStrategy(componentFinderStrategyBuilder.build()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyParser.java new file mode 100644 index 000000000..32bd19a64 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyParser.java @@ -0,0 +1,158 @@ +package com.structurizr.dsl; + +import com.structurizr.component.filter.ExcludeTypesByRegexFilter; +import com.structurizr.component.filter.IncludeTypesByRegexFilter; +import com.structurizr.component.matcher.*; +import com.structurizr.component.naming.DefaultPackageNamingStrategy; +import com.structurizr.component.naming.SimpleNamingStrategy; +import com.structurizr.component.naming.FullyQualifiedNamingStrategy; +import com.structurizr.component.supporting.AllReferencedTypesInPackageSupportingTypesStrategy; +import com.structurizr.component.supporting.AllReferencedTypesSupportingTypesStrategy; +import com.structurizr.component.supporting.AllTypesInPackageSupportingTypesStrategy; +import com.structurizr.component.supporting.AllTypesUnderPackageSupportingTypesStrategy; + +final class ComponentFinderStrategyParser extends AbstractParser { + + private static final String MATCHER_GRAMMAR = "matcher [parameters]"; + private static final String FILTER_GRAMMAR = "filter [parameters]"; + private static final String SUPPORTING_TYPES_GRAMMAR = "supportingTypes [parameters]"; + private static final String NAMING_GRAMMAR = "naming "; + + void parseMatcher(ComponentFinderStrategyDslContext context, Tokens tokens) { + if (tokens.size() < 2) { + throw new RuntimeException("Too few tokens, expected: " + MATCHER_GRAMMAR); + } + + String type = tokens.get(1).toLowerCase(); + switch (type) { + case "annotation": + if (tokens.size() == 4) { + String name = tokens.get(2); + String technology = tokens.get(3); + + context.getComponentFinderStrategyBuilder().matchedBy(new AnnotationTypeMatcher(name, technology)); + } else { + throw new RuntimeException("Expected: " + MATCHER_GRAMMAR); + } + break; + case "extends": + if (tokens.size() == 4) { + String name = tokens.get(2); + String technology = tokens.get(3); + + context.getComponentFinderStrategyBuilder().matchedBy(new ExtendsTypeMatcher(name, technology)); + } else { + throw new RuntimeException("Expected: " + MATCHER_GRAMMAR); + } + break; + case "implements": + if (tokens.size() == 4) { + String name = tokens.get(2); + String technology = tokens.get(3); + + context.getComponentFinderStrategyBuilder().matchedBy(new ImplementsTypeMatcher(name, technology)); + } else { + throw new RuntimeException("Expected: " + MATCHER_GRAMMAR); + } + break; + case "namesuffix": + if (tokens.size() == 4) { + String suffix = tokens.get(2); + String technology = tokens.get(3); + + context.getComponentFinderStrategyBuilder().matchedBy(new NameSuffixTypeMatcher(suffix, technology)); + } else { + throw new RuntimeException("Expected: " + MATCHER_GRAMMAR); + } + break; + case "regex": + if (tokens.size() == 4) { + String regex = tokens.get(2); + String technology = tokens.get(3); + + context.getComponentFinderStrategyBuilder().matchedBy(new RegexTypeMatcher(regex, technology)); + } else { + throw new RuntimeException("Expected: " + MATCHER_GRAMMAR); + } + break; + default: + throw new IllegalArgumentException("Unknown matcher: " + type); + } + } + + void parseFilter(ComponentFinderStrategyDslContext context, Tokens tokens) { + if (tokens.size() < 2) { + throw new RuntimeException("Too few tokens, expected: " + FILTER_GRAMMAR); + } + + String type = tokens.get(1).toLowerCase(); + switch (type) { + case "includeregex": + if (tokens.size() == 3) { + String regex = tokens.get(2); + + context.getComponentFinderStrategyBuilder().filteredBy(new IncludeTypesByRegexFilter(regex)); + } else { + throw new RuntimeException("Expected: " + FILTER_GRAMMAR); + } + break; + case "excluderegex": + if (tokens.size() == 3) { + String regex = tokens.get(2); + + context.getComponentFinderStrategyBuilder().filteredBy(new ExcludeTypesByRegexFilter(regex)); + } else { + throw new RuntimeException("Expected: " + FILTER_GRAMMAR); + } + break; + default: + throw new IllegalArgumentException("Unknown filter: " + type); + } + } + + void parseSupportingTypes(ComponentFinderStrategyDslContext context, Tokens tokens) { + if (tokens.size() < 2) { + throw new RuntimeException("Too few tokens, expected: " + SUPPORTING_TYPES_GRAMMAR); + } + + String type = tokens.get(1).toLowerCase(); + switch (type) { + case "referenced": + context.getComponentFinderStrategyBuilder().supportedBy(new AllReferencedTypesSupportingTypesStrategy()); + break; + case "referencedinpackage": + context.getComponentFinderStrategyBuilder().supportedBy(new AllReferencedTypesInPackageSupportingTypesStrategy()); + break; + case "inpackage": + context.getComponentFinderStrategyBuilder().supportedBy(new AllTypesInPackageSupportingTypesStrategy()); + break; + case "underpackage": + context.getComponentFinderStrategyBuilder().supportedBy(new AllTypesUnderPackageSupportingTypesStrategy()); + break; + default: + throw new IllegalArgumentException("Unknown supporting types strategy: " + type); + } + } + + void parseNaming(ComponentFinderStrategyDslContext context, Tokens tokens) { + if (tokens.size() < 1) { + throw new RuntimeException("Too few tokens, expected: " + NAMING_GRAMMAR); + } + + String type = tokens.get(1).toLowerCase(); + switch (type) { + case "name": + context.getComponentFinderStrategyBuilder().namedBy(new SimpleNamingStrategy()); + break; + case "fqn": + context.getComponentFinderStrategyBuilder().namedBy(new FullyQualifiedNamingStrategy()); + break; + case "package": + context.getComponentFinderStrategyBuilder().namedBy(new DefaultPackageNamingStrategy()); + break; + default: + throw new IllegalArgumentException("Unknown naming strategy: " + type); + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index d1d423ed0..f44406559 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -421,6 +421,36 @@ void parse(List lines, File dslFile, boolean include) throws Structurizr registerIdentifier(identifier, component); + } else if (COMPONENT_FINDER_TOKEN.equalsIgnoreCase(firstToken) && inContext(ContainerDslContext.class)) { + if (!restricted) { + if (shouldStartContext(tokens)) { + startContext(new ComponentFinderDslContext(getContext(ContainerDslContext.class).getContainer())); + } + } + + } else if (COMPONENT_FINDER_CLASSES_TOKEN.equalsIgnoreCase(firstToken) && inContext(ComponentFinderDslContext.class)) { + new ComponentFinderParser().parseClasses(getContext(ComponentFinderDslContext.class), tokens); + + } else if (COMPONENT_FINDER_SOURCE_TOKEN.equalsIgnoreCase(firstToken) && inContext(ComponentFinderDslContext.class)) { + new ComponentFinderParser().parseSource(getContext(ComponentFinderDslContext.class), tokens); + + } else if (COMPONENT_FINDER_STRATEGY_TOKEN.equalsIgnoreCase(firstToken) && inContext(ComponentFinderDslContext.class)) { + if (shouldStartContext(tokens)) { + startContext(new ComponentFinderStrategyDslContext(getContext(ComponentFinderDslContext.class).getComponentFinderBuilder())); + } + + } else if (COMPONENT_FINDER_STRATEGY_MATCHER_TOKEN.equalsIgnoreCase(firstToken) && inContext(ComponentFinderStrategyDslContext.class)) { + new ComponentFinderStrategyParser().parseMatcher(getContext(ComponentFinderStrategyDslContext.class), tokens); + + } else if (COMPONENT_FINDER_STRATEGY_FILTER_TOKEN.equalsIgnoreCase(firstToken) && inContext(ComponentFinderStrategyDslContext.class)) { + new ComponentFinderStrategyParser().parseFilter(getContext(ComponentFinderStrategyDslContext.class), tokens); + + } else if (COMPONENT_FINDER_STRATEGY_SUPPORTING_TYPES_TOKEN.equalsIgnoreCase(firstToken) && inContext(ComponentFinderStrategyDslContext.class)) { + new ComponentFinderStrategyParser().parseSupportingTypes(getContext(ComponentFinderStrategyDslContext.class), tokens); + + } else if (COMPONENT_FINDER_STRATEGY_NAMING_TOKEN.equalsIgnoreCase(firstToken) && inContext(ComponentFinderStrategyDslContext.class)) { + new ComponentFinderStrategyParser().parseNaming(getContext(ComponentFinderStrategyDslContext.class), tokens); + } else if (ENTERPRISE_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelDslContext.class)) { throw new RuntimeException("The enterprise keyword was previously deprecated, and has now been removed - please use group instead (https://docs.structurizr.com/dsl/language#group)"); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java index 4ef709312..1962ec6b0 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java @@ -113,4 +113,13 @@ class StructurizrDslTokens { static final String PLUGIN_TOKEN = "!plugin"; static final String SCRIPT_TOKEN = "!script"; + static final String COMPONENT_FINDER_TOKEN = "!components"; + static final String COMPONENT_FINDER_CLASSES_TOKEN = "classes"; + static final String COMPONENT_FINDER_SOURCE_TOKEN = "source"; + static final String COMPONENT_FINDER_STRATEGY_TOKEN = "strategy"; + static final String COMPONENT_FINDER_STRATEGY_MATCHER_TOKEN = "matcher"; + static final String COMPONENT_FINDER_STRATEGY_FILTER_TOKEN = "filter"; + static final String COMPONENT_FINDER_STRATEGY_SUPPORTING_TYPES_TOKEN = "supportingTypes"; + static final String COMPONENT_FINDER_STRATEGY_NAMING_TOKEN = "naming"; + } \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index 680f6d972..15ec0e457 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -1143,6 +1143,62 @@ void test_Var_CannotOverrideConst() { } } + @Test + void springPetClinic() throws Exception { + File path = new File("/Users/simon/sandbox/spring-petclinic"); + if (path.exists()) { + System.out.println("Running Spring PetClinic example..."); + StructurizrDslParser parser = new StructurizrDslParser(); + parser.addConstant("SPRING_PETCLINIC_DIR", path.getAbsolutePath()); + parser.parse(new File("src/test/resources/dsl/spring-petclinic.dsl")); + + Container webApplication = (Container)parser.getIdentifiersRegister().getElement("springPetClinic.webApplication"); + assertEquals(7, webApplication.getComponents().size()); + + Component welcomeController = webApplication.getComponentWithName("Welcome Controller"); + assertNotNull(welcomeController); + assertEquals("org.springframework.samples.petclinic.system.WelcomeController", welcomeController.getProperties().get("component.type")); + assertEquals(new File(path, "src/main/java/org/springframework/samples/petclinic/system/WelcomeController.java").getAbsolutePath(), welcomeController.getProperties().get("component.src")); + + Component ownerController = webApplication.getComponentWithName("Owner Controller"); + assertNotNull(ownerController); + assertEquals("org.springframework.samples.petclinic.owner.OwnerController", ownerController.getProperties().get("component.type")); + assertEquals(new File(path, "src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java").getAbsolutePath(), ownerController.getProperties().get("component.src")); + + Component petController = webApplication.getComponentWithName("Pet Controller"); + assertNotNull(petController); + assertEquals("org.springframework.samples.petclinic.owner.PetController", petController.getProperties().get("component.type")); + assertEquals(new File(path, "src/main/java/org/springframework/samples/petclinic/owner/PetController.java").getAbsolutePath(), petController.getProperties().get("component.src")); + + Component vetController = webApplication.getComponentWithName("Vet Controller"); + assertNotNull(vetController); + assertEquals("org.springframework.samples.petclinic.vet.VetController", vetController.getProperties().get("component.type")); + assertEquals(new File(path, "src/main/java/org/springframework/samples/petclinic/vet/VetController.java").getAbsolutePath(), vetController.getProperties().get("component.src")); + + Component visitController = webApplication.getComponentWithName("Visit Controller"); + assertNotNull(visitController); + assertEquals("org.springframework.samples.petclinic.owner.VisitController", visitController.getProperties().get("component.type")); + assertEquals(new File(path, "src/main/java/org/springframework/samples/petclinic/owner/VisitController.java").getAbsolutePath(), visitController.getProperties().get("component.src")); + + Component ownerRepository = webApplication.getComponentWithName("Owner Repository"); + assertNotNull(ownerRepository); + assertEquals("org.springframework.samples.petclinic.owner.OwnerRepository", ownerRepository.getProperties().get("component.type")); + assertEquals(new File(path, "src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java").getAbsolutePath(), ownerRepository.getProperties().get("component.src")); + + Component vetRepository = webApplication.getComponentWithName("Vet Repository"); + assertNotNull(vetRepository); + assertEquals("org.springframework.samples.petclinic.vet.VetRepository", vetRepository.getProperties().get("component.type")); + assertEquals(new File(path, "src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java").getAbsolutePath(), vetRepository.getProperties().get("component.src")); + + assertTrue(welcomeController.getRelationships().isEmpty()); + + assertNotNull(petController.getEfferentRelationshipWith(ownerRepository)); + assertNotNull(visitController.getEfferentRelationshipWith(ownerRepository)); + assertNotNull(ownerController.getEfferentRelationshipWith(ownerRepository)); + + assertNotNull(vetController.getEfferentRelationshipWith(vetRepository)); + } + } @Test void test_bulkOperations() throws Exception { diff --git a/structurizr-dsl/src/test/resources/dsl/spring-petclinic.dsl b/structurizr-dsl/src/test/resources/dsl/spring-petclinic.dsl new file mode 100644 index 000000000..d1bbc3021 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/spring-petclinic.dsl @@ -0,0 +1,96 @@ + workspace "Spring PetClinic" "A C4 model of the Spring PetClinic sample app (https://github.com/spring-projects/spring-petclinic/)" { + + !identifiers hierarchical + + model { + clinicEmployee = person "Clinic Employee" "An employee of the clinic." + springPetClinic = softwareSystem "Spring PetClinic" "Allows employees to view and manage information regarding the veterinarians, the clients, and their pets." { + relationalDatabaseSchema = container "Relational Database Schema" { + description "Stores information regarding the veterinarians, the clients, and their pets." + technology "Relational Database Schema" + tag "Relational Database Schema" + } + + webApplication = container "Web Application" { + description "Allows employees to view and manage information regarding the veterinarians, the clients, and their pets." + technology "Java and Spring" + + !components { + classes "${SPRING_PETCLINIC_DIR}/target/spring-petclinic-3.3.0-SNAPSHOT.jar" + source "${SPRING_PETCLINIC_DIR}/src/main/java" + strategy { + matcher annotation "org.springframework.stereotype.Controller" "Spring MVC Controller" + filter excludeRegex ".*.CrashController" + } + strategy { + matcher implements "org.springframework.data.repository.Repository" "Spring Data Repository" + } + } + + !script groovy { + element.components.each { it.url = it.properties["component.src"].replace(context.dslParser.getConstant("SPRING_PETCLINIC_DIR") + "/src/main/java", "https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java") } + } + + !elements "element.parent==webApplication && element.technology==Spring MVC Controller" { + clinicEmployee -> this "Uses" + tag "Spring MVC Controller" + } + + !elements "element.parent==webApplication && element.technology==Spring Data Repository" { + -> relationalDatabaseSchema "Reads from and writes to" + tag "Spring Data Repository" + } + } + } + } + + views { + systemContext springPetClinic "SystemContext" { + include * + autolayout + } + + container springPetClinic "Containers" { + include * + autolayout + } + + component springPetClinic.webApplication "Components" { + include * + autolayout + } + + styles { + element "Person" { + shape person + background #519823 + color #FFFFFF + } + + element "Software System" { + background #6CB33E + color #FFFFFF + } + + element "Container" { + background #91D366 + color #FFFFFF + } + + element "Relational Database Schema" { + shape cylinder + } + + element "Spring MVC Controller" { + background #D4F3C0 + color #000000 + } + + element "Spring Data Repository" { + background #95D46C + color #000000 + } + } + } + +} \ No newline at end of file From 69262cc78fafda837f34f71a13811b67529e4475 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 21 Aug 2024 09:21:49 +0100 Subject: [PATCH 546/717] Makes the Spring PetClinic example easier to run. --- .../java/com/structurizr/dsl/DslTests.java | 30 ++++++++++++------- .../test/resources/dsl/spring-petclinic.dsl | 10 +++++-- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index 15ec0e457..bb5cc5041 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -3,6 +3,7 @@ import com.structurizr.Workspace; import com.structurizr.documentation.Section; import com.structurizr.model.*; +import com.structurizr.util.StringUtils; import com.structurizr.view.*; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -1145,11 +1146,11 @@ void test_Var_CannotOverrideConst() { @Test void springPetClinic() throws Exception { - File path = new File("/Users/simon/sandbox/spring-petclinic"); - if (path.exists()) { + String springPetClinicHome = System.getenv().getOrDefault("SPRING_PETCLINIC_HOME", ""); + System.out.println(springPetClinicHome); + if (!StringUtils.isNullOrEmpty(springPetClinicHome)) { System.out.println("Running Spring PetClinic example..."); StructurizrDslParser parser = new StructurizrDslParser(); - parser.addConstant("SPRING_PETCLINIC_DIR", path.getAbsolutePath()); parser.parse(new File("src/test/resources/dsl/spring-petclinic.dsl")); Container webApplication = (Container)parser.getIdentifiersRegister().getElement("springPetClinic.webApplication"); @@ -1158,37 +1159,44 @@ void springPetClinic() throws Exception { Component welcomeController = webApplication.getComponentWithName("Welcome Controller"); assertNotNull(welcomeController); assertEquals("org.springframework.samples.petclinic.system.WelcomeController", welcomeController.getProperties().get("component.type")); - assertEquals(new File(path, "src/main/java/org/springframework/samples/petclinic/system/WelcomeController.java").getAbsolutePath(), welcomeController.getProperties().get("component.src")); + assertEquals(new File(springPetClinicHome, "src/main/java/org/springframework/samples/petclinic/system/WelcomeController.java").getAbsolutePath(), welcomeController.getProperties().get("component.src")); + assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/system/WelcomeController.java", welcomeController.getUrl()); Component ownerController = webApplication.getComponentWithName("Owner Controller"); assertNotNull(ownerController); assertEquals("org.springframework.samples.petclinic.owner.OwnerController", ownerController.getProperties().get("component.type")); - assertEquals(new File(path, "src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java").getAbsolutePath(), ownerController.getProperties().get("component.src")); + assertEquals(new File(springPetClinicHome, "src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java").getAbsolutePath(), ownerController.getProperties().get("component.src")); + assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java", ownerController.getUrl()); Component petController = webApplication.getComponentWithName("Pet Controller"); assertNotNull(petController); assertEquals("org.springframework.samples.petclinic.owner.PetController", petController.getProperties().get("component.type")); - assertEquals(new File(path, "src/main/java/org/springframework/samples/petclinic/owner/PetController.java").getAbsolutePath(), petController.getProperties().get("component.src")); + assertEquals(new File(springPetClinicHome, "src/main/java/org/springframework/samples/petclinic/owner/PetController.java").getAbsolutePath(), petController.getProperties().get("component.src")); + assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/owner/PetController.java", petController.getUrl()); Component vetController = webApplication.getComponentWithName("Vet Controller"); assertNotNull(vetController); assertEquals("org.springframework.samples.petclinic.vet.VetController", vetController.getProperties().get("component.type")); - assertEquals(new File(path, "src/main/java/org/springframework/samples/petclinic/vet/VetController.java").getAbsolutePath(), vetController.getProperties().get("component.src")); + assertEquals(new File(springPetClinicHome, "src/main/java/org/springframework/samples/petclinic/vet/VetController.java").getAbsolutePath(), vetController.getProperties().get("component.src")); + assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/vet/VetController.java", vetController.getUrl()); Component visitController = webApplication.getComponentWithName("Visit Controller"); assertNotNull(visitController); assertEquals("org.springframework.samples.petclinic.owner.VisitController", visitController.getProperties().get("component.type")); - assertEquals(new File(path, "src/main/java/org/springframework/samples/petclinic/owner/VisitController.java").getAbsolutePath(), visitController.getProperties().get("component.src")); + assertEquals(new File(springPetClinicHome, "src/main/java/org/springframework/samples/petclinic/owner/VisitController.java").getAbsolutePath(), visitController.getProperties().get("component.src")); + assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/owner/VisitController.java", visitController.getUrl()); Component ownerRepository = webApplication.getComponentWithName("Owner Repository"); assertNotNull(ownerRepository); assertEquals("org.springframework.samples.petclinic.owner.OwnerRepository", ownerRepository.getProperties().get("component.type")); - assertEquals(new File(path, "src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java").getAbsolutePath(), ownerRepository.getProperties().get("component.src")); + assertEquals(new File(springPetClinicHome, "src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java").getAbsolutePath(), ownerRepository.getProperties().get("component.src")); + assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java", ownerRepository.getUrl()); Component vetRepository = webApplication.getComponentWithName("Vet Repository"); assertNotNull(vetRepository); assertEquals("org.springframework.samples.petclinic.vet.VetRepository", vetRepository.getProperties().get("component.type")); - assertEquals(new File(path, "src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java").getAbsolutePath(), vetRepository.getProperties().get("component.src")); + assertEquals(new File(springPetClinicHome, "src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java").getAbsolutePath(), vetRepository.getProperties().get("component.src")); + assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java", vetRepository.getUrl()); assertTrue(welcomeController.getRelationships().isEmpty()); @@ -1197,6 +1205,8 @@ void springPetClinic() throws Exception { assertNotNull(ownerController.getEfferentRelationshipWith(ownerRepository)); assertNotNull(vetController.getEfferentRelationshipWith(vetRepository)); + } else { + System.out.println("Skipping Spring PetClinic example..."); } } diff --git a/structurizr-dsl/src/test/resources/dsl/spring-petclinic.dsl b/structurizr-dsl/src/test/resources/dsl/spring-petclinic.dsl index d1bbc3021..279c77f73 100644 --- a/structurizr-dsl/src/test/resources/dsl/spring-petclinic.dsl +++ b/structurizr-dsl/src/test/resources/dsl/spring-petclinic.dsl @@ -1,5 +1,9 @@ workspace "Spring PetClinic" "A C4 model of the Spring PetClinic sample app (https://github.com/spring-projects/spring-petclinic/)" { + // this example requires an environment variable as follows: + // - Name: SPRING_PETCLINIC_HOME + // - Value: the full path to the location of the spring-petclinic example (e.g. /Users/simon/spring-petclinic) + !identifiers hierarchical model { @@ -16,8 +20,8 @@ technology "Java and Spring" !components { - classes "${SPRING_PETCLINIC_DIR}/target/spring-petclinic-3.3.0-SNAPSHOT.jar" - source "${SPRING_PETCLINIC_DIR}/src/main/java" + classes "${SPRING_PETCLINIC_HOME}/target/spring-petclinic-3.3.0-SNAPSHOT.jar" + source "${SPRING_PETCLINIC_HOME}/src/main/java" strategy { matcher annotation "org.springframework.stereotype.Controller" "Spring MVC Controller" filter excludeRegex ".*.CrashController" @@ -28,7 +32,7 @@ } !script groovy { - element.components.each { it.url = it.properties["component.src"].replace(context.dslParser.getConstant("SPRING_PETCLINIC_DIR") + "/src/main/java", "https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java") } + element.components.each { it.url = it.properties["component.src"].replace(System.getenv("SPRING_PETCLINIC_HOME") + "/src/main/java", "https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java") } } !elements "element.parent==webApplication && element.technology==Spring MVC Controller" { From 726f6ebe0e805cbe5e51e2505e302d3c933c79f1 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 21 Aug 2024 09:27:39 +0100 Subject: [PATCH 547/717] Makes the Spring PetClinic example easier to run from Lite. --- .gitignore | 2 ++ structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java | 2 +- .../{spring-petclinic.dsl => spring-petclinic/workspace.dsl} | 0 3 files changed, 3 insertions(+), 1 deletion(-) rename structurizr-dsl/src/test/resources/dsl/{spring-petclinic.dsl => spring-petclinic/workspace.dsl} (100%) diff --git a/.gitignore b/.gitignore index 7068eac3c..30458f2b7 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,5 @@ bin # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* +structurizr-dsl/src/test/resources/dsl/spring-petclinic/.structurizr +structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.json \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index bb5cc5041..3518da1f8 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -1151,7 +1151,7 @@ void springPetClinic() throws Exception { if (!StringUtils.isNullOrEmpty(springPetClinicHome)) { System.out.println("Running Spring PetClinic example..."); StructurizrDslParser parser = new StructurizrDslParser(); - parser.parse(new File("src/test/resources/dsl/spring-petclinic.dsl")); + parser.parse(new File("src/test/resources/dsl/spring-petclinic/workspace.dsl")); Container webApplication = (Container)parser.getIdentifiersRegister().getElement("springPetClinic.webApplication"); assertEquals(7, webApplication.getComponents().size()); diff --git a/structurizr-dsl/src/test/resources/dsl/spring-petclinic.dsl b/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl similarity index 100% rename from structurizr-dsl/src/test/resources/dsl/spring-petclinic.dsl rename to structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl From 6c8689a044f1a736fef971a1a2fa63eac2587d2a Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 22 Aug 2024 21:04:05 +0100 Subject: [PATCH 548/717] Adds discovered components into the DSL parser identifiers register. --- .../com/structurizr/component/ComponentFinder.java | 6 +++++- .../structurizr/dsl/ComponentFinderDslContext.java | 13 +++++++++++-- .../com/structurizr/dsl/IdentifiersRegister.java | 9 +++++++++ .../com/structurizr/dsl/StructurizrDslParser.java | 6 +++--- .../src/test/java/com/structurizr/dsl/DslTests.java | 7 +++++++ 5 files changed, 35 insertions(+), 6 deletions(-) diff --git a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinder.java b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinder.java index 5234800fc..541d2c082 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinder.java +++ b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinder.java @@ -98,9 +98,10 @@ private void findDependencies(com.structurizr.component.Type type) { /** * Find components, using all configured rules, in the order they were added. */ - public void findComponents() { + public Set findComponents() { Set discoveredComponents = new LinkedHashSet<>(); Map componentMap = new HashMap<>(); + Set componentSet = new LinkedHashSet<>(); for (ComponentFinderStrategy componentFinderStrategy : componentFinderStrategies) { Set set = componentFinderStrategy.findComponents(typeRepository); @@ -119,6 +120,7 @@ public void findComponents() { component.setDescription(discoveredComponent.getDescription()); component.setTechnology(discoveredComponent.getTechnology()); componentMap.put(discoveredComponent, component); + componentSet.add(component); } // find dependencies between all components @@ -141,6 +143,8 @@ public void findComponents() { Component component = componentMap.get(discoveredComponent); discoveredComponent.getComponentFinderStrategy().visit(component); } + + return componentSet; } } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderDslContext.java index c63b2fc3c..0172fd657 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderDslContext.java @@ -1,13 +1,19 @@ package com.structurizr.dsl; import com.structurizr.component.ComponentFinderBuilder; +import com.structurizr.model.Component; import com.structurizr.model.Container; +import java.util.Set; + final class ComponentFinderDslContext extends DslContext { private final ComponentFinderBuilder componentFinderBuilder = new ComponentFinderBuilder(); - ComponentFinderDslContext(Container container) { + private final StructurizrDslParser dslParser; + + ComponentFinderDslContext(StructurizrDslParser dslParser, Container container) { + this.dslParser = dslParser; componentFinderBuilder.forContainer(container); } @@ -26,7 +32,10 @@ ComponentFinderBuilder getComponentFinderBuilder() { @Override void end() { - componentFinderBuilder.build().findComponents(); + Set components = componentFinderBuilder.build().findComponents(); + for (Component component : components) { + dslParser.registerIdentifier(IdentifiersRegister.toIdentifier(component.getName()), component); + } } } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/IdentifiersRegister.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/IdentifiersRegister.java index 1d5524552..654538752 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/IdentifiersRegister.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/IdentifiersRegister.java @@ -219,4 +219,13 @@ void validateIdentifierName(String identifier) { } } + static String toIdentifier(String s) { + String identifierName = s.replaceAll("[^a-zA-Z0-9_-]", ""); + if (identifierName.startsWith("-")) { + identifierName = identifierName.substring(1); + } + + return identifierName; + } + } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index f44406559..a162091f8 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -424,7 +424,7 @@ void parse(List lines, File dslFile, boolean include) throws Structurizr } else if (COMPONENT_FINDER_TOKEN.equalsIgnoreCase(firstToken) && inContext(ContainerDslContext.class)) { if (!restricted) { if (shouldStartContext(tokens)) { - startContext(new ComponentFinderDslContext(getContext(ContainerDslContext.class).getContainer())); + startContext(new ComponentFinderDslContext(this, getContext(ContainerDslContext.class).getContainer())); } } @@ -1121,12 +1121,12 @@ public IdentifiersRegister getIdentifiersRegister() { return identifiersRegister; } - private void registerIdentifier(String identifier, Element element) { + void registerIdentifier(String identifier, Element element) { identifiersRegister.register(identifier, element); element.addProperty(STRUCTURIZR_DSL_IDENTIFIER_PROPERTY_NAME, identifiersRegister.findIdentifier(element)); } - private void registerIdentifier(String identifier, Relationship relationship) { + void registerIdentifier(String identifier, Relationship relationship) { identifiersRegister.register(identifier, relationship); relationship.addProperty(STRUCTURIZR_DSL_IDENTIFIER_PROPERTY_NAME, identifiersRegister.findIdentifier(relationship)); } diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index 3518da1f8..981a5f5ae 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -1161,42 +1161,49 @@ void springPetClinic() throws Exception { assertEquals("org.springframework.samples.petclinic.system.WelcomeController", welcomeController.getProperties().get("component.type")); assertEquals(new File(springPetClinicHome, "src/main/java/org/springframework/samples/petclinic/system/WelcomeController.java").getAbsolutePath(), welcomeController.getProperties().get("component.src")); assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/system/WelcomeController.java", welcomeController.getUrl()); + assertSame(welcomeController, parser.getIdentifiersRegister().getElement("springPetClinic.webApplication.welcomecontroller")); Component ownerController = webApplication.getComponentWithName("Owner Controller"); assertNotNull(ownerController); assertEquals("org.springframework.samples.petclinic.owner.OwnerController", ownerController.getProperties().get("component.type")); assertEquals(new File(springPetClinicHome, "src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java").getAbsolutePath(), ownerController.getProperties().get("component.src")); assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java", ownerController.getUrl()); + assertSame(ownerController, parser.getIdentifiersRegister().getElement("springPetClinic.webApplication.ownerController")); Component petController = webApplication.getComponentWithName("Pet Controller"); assertNotNull(petController); assertEquals("org.springframework.samples.petclinic.owner.PetController", petController.getProperties().get("component.type")); assertEquals(new File(springPetClinicHome, "src/main/java/org/springframework/samples/petclinic/owner/PetController.java").getAbsolutePath(), petController.getProperties().get("component.src")); assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/owner/PetController.java", petController.getUrl()); + assertSame(petController, parser.getIdentifiersRegister().getElement("springPetClinic.webApplication.petcontroller")); Component vetController = webApplication.getComponentWithName("Vet Controller"); assertNotNull(vetController); assertEquals("org.springframework.samples.petclinic.vet.VetController", vetController.getProperties().get("component.type")); assertEquals(new File(springPetClinicHome, "src/main/java/org/springframework/samples/petclinic/vet/VetController.java").getAbsolutePath(), vetController.getProperties().get("component.src")); assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/vet/VetController.java", vetController.getUrl()); + assertSame(vetController, parser.getIdentifiersRegister().getElement("springPetClinic.webApplication.vetcontroller")); Component visitController = webApplication.getComponentWithName("Visit Controller"); assertNotNull(visitController); assertEquals("org.springframework.samples.petclinic.owner.VisitController", visitController.getProperties().get("component.type")); assertEquals(new File(springPetClinicHome, "src/main/java/org/springframework/samples/petclinic/owner/VisitController.java").getAbsolutePath(), visitController.getProperties().get("component.src")); assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/owner/VisitController.java", visitController.getUrl()); + assertSame(visitController, parser.getIdentifiersRegister().getElement("springPetClinic.webApplication.visitcontroller")); Component ownerRepository = webApplication.getComponentWithName("Owner Repository"); assertNotNull(ownerRepository); assertEquals("org.springframework.samples.petclinic.owner.OwnerRepository", ownerRepository.getProperties().get("component.type")); assertEquals(new File(springPetClinicHome, "src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java").getAbsolutePath(), ownerRepository.getProperties().get("component.src")); assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java", ownerRepository.getUrl()); + assertSame(ownerRepository, parser.getIdentifiersRegister().getElement("springPetClinic.webApplication.ownerrepository")); Component vetRepository = webApplication.getComponentWithName("Vet Repository"); assertNotNull(vetRepository); assertEquals("org.springframework.samples.petclinic.vet.VetRepository", vetRepository.getProperties().get("component.type")); assertEquals(new File(springPetClinicHome, "src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java").getAbsolutePath(), vetRepository.getProperties().get("component.src")); assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java", vetRepository.getUrl()); + assertSame(vetRepository, parser.getIdentifiersRegister().getElement("springPetClinic.webApplication.vetrepository")); assertTrue(welcomeController.getRelationships().isEmpty()); From f6b55e51f37202b2e6a637fa3b878ba7d4097f00 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 24 Aug 2024 15:13:40 +0100 Subject: [PATCH 549/717] More tidy up and tests. --- .../component/ComponentFinderStrategy.java | 6 +- .../ComponentFinderStrategyBuilder.java | 21 +- .../matcher/AbstractTypeMatcher.java | 19 -- .../matcher/AnnotationTypeMatcher.java | 10 +- .../component/matcher/ExtendsTypeMatcher.java | 6 +- .../matcher/ImplementsTypeMatcher.java | 6 +- .../matcher/NameSuffixTypeMatcher.java | 6 +- .../component/matcher/RegexTypeMatcher.java | 6 +- .../component/matcher/TypeMatcher.java | 2 - .../naming/DefaultNamingStrategy.java | 5 + .../DefaultSupportingTypesStrategy.java | 5 + .../visitor/DefaultComponentVisitor.java | 5 + .../matcher/AnnotationTypeMatcherTests.java | 16 +- .../matcher/ExtendsTypeMatcherTests.java | 14 +- .../matcher/ImplementsTypeMatcherTests.java | 14 +- .../matcher/NameSuffixTypeMatcherTests.java | 12 +- .../matcher/RegexSuffixTypeMatcherTests.java | 12 +- .../ComponentFinderStrategyDslContext.java | 1 + .../dsl/ComponentFinderStrategyParser.java | 78 +++++--- .../java/com/structurizr/dsl/DslContext.java | 4 +- .../com/structurizr/dsl/ElementsParser.java | 8 +- .../dsl/ImpliedRelationshipsParser.java | 4 +- .../structurizr/dsl/RelationshipsParser.java | 9 +- .../structurizr/dsl/StructurizrDslParser.java | 11 +- .../structurizr/dsl/StructurizrDslTokens.java | 1 + .../ComponentFinderStrategyParserTests.java | 180 ++++++++++++++++++ .../structurizr/dsl/ElementsParserTests.java | 10 + .../dsl/ImpliedRelationshipsParserTests.java | 7 + .../dsl/RelationshipsParserTests.java | 10 + .../CustomImpliedRelationshipsStrategy.java | 13 ++ .../dsl/example/CustomTypeMatcher.java | 18 ++ .../dsl/spring-petclinic/workspace.dsl | 6 +- 32 files changed, 404 insertions(+), 121 deletions(-) delete mode 100644 structurizr-component/src/main/java/com/structurizr/component/matcher/AbstractTypeMatcher.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderStrategyParserTests.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/example/CustomImpliedRelationshipsStrategy.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/example/CustomTypeMatcher.java diff --git a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategy.java index 04d499361..91ca7d1e4 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategy.java +++ b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategy.java @@ -22,13 +22,15 @@ */ class ComponentFinderStrategy { + private final String technology; private final TypeMatcher typeMatcher; private final TypeFilter typeFilter; private final SupportingTypesStrategy supportingTypesStrategy; private final NamingStrategy namingStrategy; private final ComponentVisitor componentVisitor; - ComponentFinderStrategy(TypeMatcher typeMatcher, TypeFilter typeFilter, SupportingTypesStrategy supportingTypesStrategy, NamingStrategy namingStrategy, ComponentVisitor componentVisitor) { + ComponentFinderStrategy(String technology, TypeMatcher typeMatcher, TypeFilter typeFilter, SupportingTypesStrategy supportingTypesStrategy, NamingStrategy namingStrategy, ComponentVisitor componentVisitor) { + this.technology = technology; this.typeMatcher = typeMatcher; this.typeFilter = typeFilter; this.supportingTypesStrategy = supportingTypesStrategy; @@ -44,7 +46,7 @@ Set findComponents(TypeRepository typeRepository) { if (typeMatcher.matches(type) && typeFilter.accept(type)) { DiscoveredComponent component = new DiscoveredComponent(namingStrategy.nameOf(type), type); component.setDescription(type.getDescription()); - component.setTechnology(typeMatcher.getTechnology()); + component.setTechnology(this.technology); component.setComponentFinderStrategy(this); components.add(component); diff --git a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategyBuilder.java b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategyBuilder.java index 8027be99f..c2a7ee4a0 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategyBuilder.java +++ b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategyBuilder.java @@ -16,6 +16,7 @@ */ public final class ComponentFinderStrategyBuilder { + private String technology; private TypeMatcher typeMatcher; private TypeFilter typeFilter = new DefaultTypeFilter(); private SupportingTypesStrategy supportingTypesStrategy = new DefaultSupportingTypesStrategy(); @@ -25,6 +26,12 @@ public final class ComponentFinderStrategyBuilder { public ComponentFinderStrategyBuilder() { } + public ComponentFinderStrategyBuilder forTechnology(String technology) { + this.technology = technology; + + return this; + } + public ComponentFinderStrategyBuilder matchedBy(TypeMatcher typeMatcher) { this.typeMatcher = typeMatcher; @@ -60,7 +67,19 @@ public ComponentFinderStrategy build() { throw new RuntimeException("A type matcher must be specified"); } - return new ComponentFinderStrategy(typeMatcher, typeFilter, supportingTypesStrategy, namingStrategy, componentVisitor); + return new ComponentFinderStrategy(technology, typeMatcher, typeFilter, supportingTypesStrategy, namingStrategy, componentVisitor); + } + + @Override + public String toString() { + return "ComponentFinderStrategyBuilder{" + + "technology='" + technology + '\'' + + ", typeMatcher=" + typeMatcher + + ", typeFilter=" + typeFilter + + ", supportingTypesStrategy=" + supportingTypesStrategy + + ", namingStrategy=" + namingStrategy + + ", componentVisitor=" + componentVisitor + + '}'; } } \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/matcher/AbstractTypeMatcher.java b/structurizr-component/src/main/java/com/structurizr/component/matcher/AbstractTypeMatcher.java deleted file mode 100644 index f2296d5e5..000000000 --- a/structurizr-component/src/main/java/com/structurizr/component/matcher/AbstractTypeMatcher.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.structurizr.component.matcher; - -/** - * A superclass for TypeMatcher implementations. - */ -public abstract class AbstractTypeMatcher implements TypeMatcher { - - private final String technology; - - public AbstractTypeMatcher(String technology) { - this.technology = technology; - } - - @Override - public String getTechnology() { - return technology; - } - -} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/matcher/AnnotationTypeMatcher.java b/structurizr-component/src/main/java/com/structurizr/component/matcher/AnnotationTypeMatcher.java index ceadefbf1..e418ce84c 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/matcher/AnnotationTypeMatcher.java +++ b/structurizr-component/src/main/java/com/structurizr/component/matcher/AnnotationTypeMatcher.java @@ -9,13 +9,11 @@ /** * Matches types based upon the presence of a type-level annotation. */ -public class AnnotationTypeMatcher extends AbstractTypeMatcher { +public class AnnotationTypeMatcher implements TypeMatcher { private final String annotationType; - public AnnotationTypeMatcher(String annotationType, String technology) { - super(technology); - + public AnnotationTypeMatcher(String annotationType) { if (StringUtils.isNullOrEmpty(annotationType)) { throw new IllegalArgumentException("An annotation type must be supplied"); } @@ -23,9 +21,7 @@ public AnnotationTypeMatcher(String annotationType, String technology) { this.annotationType = "L" + annotationType.replace(".", "/") + ";"; } - public AnnotationTypeMatcher(Class annotation, String technology) { - super(technology); - + public AnnotationTypeMatcher(Class annotation) { if (annotation == null) { throw new IllegalArgumentException("An annotation must be supplied"); } diff --git a/structurizr-component/src/main/java/com/structurizr/component/matcher/ExtendsTypeMatcher.java b/structurizr-component/src/main/java/com/structurizr/component/matcher/ExtendsTypeMatcher.java index 043ca3cec..81bad21fe 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/matcher/ExtendsTypeMatcher.java +++ b/structurizr-component/src/main/java/com/structurizr/component/matcher/ExtendsTypeMatcher.java @@ -13,15 +13,13 @@ /** * Matches types where the type extends the specified class. */ -public class ExtendsTypeMatcher extends AbstractTypeMatcher { +public class ExtendsTypeMatcher implements TypeMatcher { private static final Log log = LogFactory.getLog(ExtendsTypeMatcher.class); private final String className; - public ExtendsTypeMatcher(String className, String technology) { - super(technology); - + public ExtendsTypeMatcher(String className) { if (StringUtils.isNullOrEmpty(className)) { throw new IllegalArgumentException("A fully qualified class name must be supplied"); } diff --git a/structurizr-component/src/main/java/com/structurizr/component/matcher/ImplementsTypeMatcher.java b/structurizr-component/src/main/java/com/structurizr/component/matcher/ImplementsTypeMatcher.java index 1deb3b6ff..4c6c24aaa 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/matcher/ImplementsTypeMatcher.java +++ b/structurizr-component/src/main/java/com/structurizr/component/matcher/ImplementsTypeMatcher.java @@ -11,15 +11,13 @@ /** * Matches types where the type implements the specified interface. */ -public class ImplementsTypeMatcher extends AbstractTypeMatcher { +public class ImplementsTypeMatcher implements TypeMatcher { private static final Log log = LogFactory.getLog(ImplementsTypeMatcher.class); private final String interfaceName; - public ImplementsTypeMatcher(String interfaceName, String technology) { - super(technology); - + public ImplementsTypeMatcher(String interfaceName) { if (StringUtils.isNullOrEmpty(interfaceName)) { throw new IllegalArgumentException("A fully qualified interface name must be supplied"); } diff --git a/structurizr-component/src/main/java/com/structurizr/component/matcher/NameSuffixTypeMatcher.java b/structurizr-component/src/main/java/com/structurizr/component/matcher/NameSuffixTypeMatcher.java index 078a1a511..9f89ca07c 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/matcher/NameSuffixTypeMatcher.java +++ b/structurizr-component/src/main/java/com/structurizr/component/matcher/NameSuffixTypeMatcher.java @@ -6,13 +6,11 @@ /** * Matches types where the name of the type ends with the specified suffix. */ -public class NameSuffixTypeMatcher extends AbstractTypeMatcher { +public class NameSuffixTypeMatcher implements TypeMatcher { private final String suffix; - public NameSuffixTypeMatcher(String suffix, String technology) { - super(technology); - + public NameSuffixTypeMatcher(String suffix) { if (StringUtils.isNullOrEmpty(suffix)) { throw new IllegalArgumentException("A suffix must be supplied"); } diff --git a/structurizr-component/src/main/java/com/structurizr/component/matcher/RegexTypeMatcher.java b/structurizr-component/src/main/java/com/structurizr/component/matcher/RegexTypeMatcher.java index 0f9c8a8f9..d55583a56 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/matcher/RegexTypeMatcher.java +++ b/structurizr-component/src/main/java/com/structurizr/component/matcher/RegexTypeMatcher.java @@ -8,13 +8,11 @@ /** * Matches types using a regex against the fully qualified type name. */ -public class RegexTypeMatcher extends AbstractTypeMatcher { +public class RegexTypeMatcher implements TypeMatcher { private final Pattern regex; - public RegexTypeMatcher(String regex, String technology) { - super(technology); - + public RegexTypeMatcher(String regex) { if (StringUtils.isNullOrEmpty(regex)) { throw new IllegalArgumentException("A regex must be supplied"); } diff --git a/structurizr-component/src/main/java/com/structurizr/component/matcher/TypeMatcher.java b/structurizr-component/src/main/java/com/structurizr/component/matcher/TypeMatcher.java index 572d159d0..4293c937a 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/matcher/TypeMatcher.java +++ b/structurizr-component/src/main/java/com/structurizr/component/matcher/TypeMatcher.java @@ -9,6 +9,4 @@ public interface TypeMatcher { boolean matches(Type type); - String getTechnology(); - } \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/naming/DefaultNamingStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/naming/DefaultNamingStrategy.java index 622060ca0..61cc11b64 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/naming/DefaultNamingStrategy.java +++ b/structurizr-component/src/main/java/com/structurizr/component/naming/DefaultNamingStrategy.java @@ -14,4 +14,9 @@ public String nameOf(Type type) { return String.join(" ", parts); } + @Override + public String toString() { + return "DefaultNamingStrategy{}"; + } + } \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/supporting/DefaultSupportingTypesStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/supporting/DefaultSupportingTypesStrategy.java index 65639a110..134d2cbae 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/supporting/DefaultSupportingTypesStrategy.java +++ b/structurizr-component/src/main/java/com/structurizr/component/supporting/DefaultSupportingTypesStrategy.java @@ -16,4 +16,9 @@ public Set findSupportingTypes(Type type, TypeRepository typeRepository) { return Collections.emptySet(); } + @Override + public String toString() { + return "DefaultSupportingTypesStrategy{}"; + } + } \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/visitor/DefaultComponentVisitor.java b/structurizr-component/src/main/java/com/structurizr/component/visitor/DefaultComponentVisitor.java index 49b687cf6..054d7e9c9 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/visitor/DefaultComponentVisitor.java +++ b/structurizr-component/src/main/java/com/structurizr/component/visitor/DefaultComponentVisitor.java @@ -11,4 +11,9 @@ public class DefaultComponentVisitor implements ComponentVisitor { public void visit(Component component) { } + @Override + public String toString() { + return "DefaultComponentVisitor{}"; + } + } \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/matcher/AnnotationTypeMatcherTests.java b/structurizr-component/src/test/java/com/structurizr/component/matcher/AnnotationTypeMatcherTests.java index fc124dfec..6e9e0ce11 100644 --- a/structurizr-component/src/test/java/com/structurizr/component/matcher/AnnotationTypeMatcherTests.java +++ b/structurizr-component/src/test/java/com/structurizr/component/matcher/AnnotationTypeMatcherTests.java @@ -13,30 +13,30 @@ public class AnnotationTypeMatcherTests { @Test void construction_ThrowsAnException_WhenPassedANullName() { - assertThrowsExactly(IllegalArgumentException.class, () -> new AnnotationTypeMatcher((String)null, "Technology")); + assertThrowsExactly(IllegalArgumentException.class, () -> new AnnotationTypeMatcher((String)null)); } @Test void construction_ThrowsAnException_WhenPassedAnEmptyName() { - assertThrowsExactly(IllegalArgumentException.class, () -> new AnnotationTypeMatcher("", "Technology")); - assertThrowsExactly(IllegalArgumentException.class, () -> new AnnotationTypeMatcher(" ", "Technology")); + assertThrowsExactly(IllegalArgumentException.class, () -> new AnnotationTypeMatcher("")); + assertThrowsExactly(IllegalArgumentException.class, () -> new AnnotationTypeMatcher(" ")); } @Test void construction_ThrowsAnException_WhenPassedANullClass() { - assertThrowsExactly(IllegalArgumentException.class, () -> new AnnotationTypeMatcher((Class) null, "Technology")); + assertThrowsExactly(IllegalArgumentException.class, () -> new AnnotationTypeMatcher((Class) null)); } @Test void matches_ThrowsAnException_WhenPassedNull() { - assertThrowsExactly(IllegalArgumentException.class, () -> new AnnotationTypeMatcher("com.example.AnnotationName", "Technology").matches(null)); + assertThrowsExactly(IllegalArgumentException.class, () -> new AnnotationTypeMatcher("com.example.AnnotationName").matches(null)); } @Test void matches_ReturnsFalse_WhenThereIsNoUnderlyingJavaClass() { Type type = new Type("com.structurizr.component.matcher.annotationTypeMatcher.CustomerController"); - assertFalse(new AnnotationTypeMatcher("com.structurizr.component.matcher.annotationTypeMatcher.Controller", "Technology").matches(type)); + assertFalse(new AnnotationTypeMatcher("com.structurizr.component.matcher.annotationTypeMatcher.Controller").matches(type)); } @Test @@ -45,7 +45,7 @@ void matches_ReturnsFalse_WhenThereIsNoMatch() throws Exception { ClassParser parser = new ClassParser(new File(classes, "com/structurizr/component/matcher/annotationTypeMatcher/CustomerController.class").getAbsolutePath()); Type type = new Type(parser.parse()); - assertFalse(new AnnotationTypeMatcher("com.structurizr.component.matcher.annotationTypeMatcher.Repository", "Technology").matches(type)); + assertFalse(new AnnotationTypeMatcher("com.structurizr.component.matcher.annotationTypeMatcher.Repository").matches(type)); } @Test @@ -54,7 +54,7 @@ void matches_ReturnsTrue_WhenThereIsAMatch() throws Exception { ClassParser parser = new ClassParser(new File(classes, "com/structurizr/component/matcher/annotationTypeMatcher/CustomerController.class").getAbsolutePath()); Type type = new Type(parser.parse()); - assertTrue(new AnnotationTypeMatcher("com.structurizr.component.matcher.annotationTypeMatcher.Controller", "Technology").matches(type)); + assertTrue(new AnnotationTypeMatcher("com.structurizr.component.matcher.annotationTypeMatcher.Controller").matches(type)); } } \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/matcher/ExtendsTypeMatcherTests.java b/structurizr-component/src/test/java/com/structurizr/component/matcher/ExtendsTypeMatcherTests.java index 40b71bcb5..66481383d 100644 --- a/structurizr-component/src/test/java/com/structurizr/component/matcher/ExtendsTypeMatcherTests.java +++ b/structurizr-component/src/test/java/com/structurizr/component/matcher/ExtendsTypeMatcherTests.java @@ -13,25 +13,25 @@ public class ExtendsTypeMatcherTests { @Test void construction_ThrowsAnException_WhenPassedANullName() { - assertThrowsExactly(IllegalArgumentException.class, () -> new ExtendsTypeMatcher(null, "Technology")); + assertThrowsExactly(IllegalArgumentException.class, () -> new ExtendsTypeMatcher(null)); } @Test void construction_ThrowsAnException_WhenPassedAnEmptyName() { - assertThrowsExactly(IllegalArgumentException.class, () -> new ExtendsTypeMatcher("", "Technology")); - assertThrowsExactly(IllegalArgumentException.class, () -> new ExtendsTypeMatcher(" ", "Technology")); + assertThrowsExactly(IllegalArgumentException.class, () -> new ExtendsTypeMatcher("")); + assertThrowsExactly(IllegalArgumentException.class, () -> new ExtendsTypeMatcher(" ")); } @Test void matches_ThrowsAnException_WhenPassedNull() { - assertThrowsExactly(IllegalArgumentException.class, () -> new ExtendsTypeMatcher("com.example.ClassName", "Technology").matches(null)); + assertThrowsExactly(IllegalArgumentException.class, () -> new ExtendsTypeMatcher("com.example.ClassName").matches(null)); } @Test void matches_ReturnsFalse_WhenThereIsNoUnderlyingJavaClass() { Type type = new Type("com.structurizr.component.matcher.extendsTypeMatcher.CustomerController"); - assertFalse(new ExtendsTypeMatcher("com.structurizr.component.matcher.extendsTypeMatcher.AbstractController", "Technology").matches(type)); + assertFalse(new ExtendsTypeMatcher("com.structurizr.component.matcher.extendsTypeMatcher.AbstractController").matches(type)); } @Test @@ -40,7 +40,7 @@ void matches_ReturnsFalse_WhenThereIsNoMatch() throws Exception { ClassParser parser = new ClassParser(new File(classes, "com/structurizr/component/matcher/extendsTypeMatcher/CustomerController.class").getAbsolutePath()); Type type = new Type(parser.parse()); - assertFalse(new ExtendsTypeMatcher("com.structurizr.component.matcher.extendsTypeMatcher.AbstractRepository", "Technology").matches(type)); + assertFalse(new ExtendsTypeMatcher("com.structurizr.component.matcher.extendsTypeMatcher.AbstractRepository").matches(type)); } @Test @@ -49,7 +49,7 @@ void matches_ReturnsTrue_WhenThereIsAMatch() throws Exception { ClassParser parser = new ClassParser(new File(classes, "com/structurizr/component/matcher/extendsTypeMatcher/CustomerController.class").getAbsolutePath()); Type type = new Type(parser.parse()); - assertTrue(new ExtendsTypeMatcher("com.structurizr.component.matcher.extendsTypeMatcher.AbstractController", "Technology").matches(type)); + assertTrue(new ExtendsTypeMatcher("com.structurizr.component.matcher.extendsTypeMatcher.AbstractController").matches(type)); } } \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/matcher/ImplementsTypeMatcherTests.java b/structurizr-component/src/test/java/com/structurizr/component/matcher/ImplementsTypeMatcherTests.java index 4ece7e585..af5acd807 100644 --- a/structurizr-component/src/test/java/com/structurizr/component/matcher/ImplementsTypeMatcherTests.java +++ b/structurizr-component/src/test/java/com/structurizr/component/matcher/ImplementsTypeMatcherTests.java @@ -12,25 +12,25 @@ public class ImplementsTypeMatcherTests { @Test void construction_ThrowsAnException_WhenPassedANullName() { - assertThrowsExactly(IllegalArgumentException.class, () -> new ImplementsTypeMatcher(null, "Technology")); + assertThrowsExactly(IllegalArgumentException.class, () -> new ImplementsTypeMatcher(null)); } @Test void construction_ThrowsAnException_WhenPassedAnEmptyName() { - assertThrowsExactly(IllegalArgumentException.class, () -> new ImplementsTypeMatcher("", "Technology")); - assertThrowsExactly(IllegalArgumentException.class, () -> new ImplementsTypeMatcher(" ", "Technology")); + assertThrowsExactly(IllegalArgumentException.class, () -> new ImplementsTypeMatcher("")); + assertThrowsExactly(IllegalArgumentException.class, () -> new ImplementsTypeMatcher(" ")); } @Test void matches_ThrowsAnException_WhenPassedNull() { - assertThrowsExactly(IllegalArgumentException.class, () -> new ImplementsTypeMatcher("com.example.InterfaceName", "Technology").matches(null)); + assertThrowsExactly(IllegalArgumentException.class, () -> new ImplementsTypeMatcher("com.example.InterfaceName").matches(null)); } @Test void matches_ReturnsFalse_WhenThereIsNoUnderlyingJavaClass() { Type type = new Type("com.structurizr.component.matcher.implementsTypeMatcher.CustomerController"); - assertFalse(new ImplementsTypeMatcher("com.structurizr.component.matcher.implementsTypeMatcher.Controller", "Technology").matches(type)); + assertFalse(new ImplementsTypeMatcher("com.structurizr.component.matcher.implementsTypeMatcher.Controller").matches(type)); } @Test @@ -39,7 +39,7 @@ void matches_ReturnsFalse_WhenThereIsNoMatch() throws Exception { ClassParser parser = new ClassParser(new File(classes, "com/structurizr/component/matcher/implementsTypeMatcher/CustomerController.class").getAbsolutePath()); Type type = new Type(parser.parse()); - assertFalse(new ImplementsTypeMatcher("com.structurizr.component.matcher.implementsTypeMatcher.Repository", "Technology").matches(type)); + assertFalse(new ImplementsTypeMatcher("com.structurizr.component.matcher.implementsTypeMatcher.Repository").matches(type)); } @Test @@ -48,7 +48,7 @@ void matches_ReturnsTrue_WhenThereIsAMatch() throws Exception { ClassParser parser = new ClassParser(new File(classes, "com/structurizr/component/matcher/implementsTypeMatcher/CustomerController.class").getAbsolutePath()); Type type = new Type(parser.parse()); - assertTrue(new ImplementsTypeMatcher("com.structurizr.component.matcher.implementsTypeMatcher.Controller", "Technology").matches(type)); + assertTrue(new ImplementsTypeMatcher("com.structurizr.component.matcher.implementsTypeMatcher.Controller").matches(type)); } } \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/matcher/NameSuffixTypeMatcherTests.java b/structurizr-component/src/test/java/com/structurizr/component/matcher/NameSuffixTypeMatcherTests.java index 80dff112e..b0055a443 100644 --- a/structurizr-component/src/test/java/com/structurizr/component/matcher/NameSuffixTypeMatcherTests.java +++ b/structurizr-component/src/test/java/com/structurizr/component/matcher/NameSuffixTypeMatcherTests.java @@ -9,28 +9,28 @@ public class NameSuffixTypeMatcherTests { @Test void construction_ThrowsAnException_WhenPassedANullSuffix() { - assertThrowsExactly(IllegalArgumentException.class, () -> new NameSuffixTypeMatcher(null, "Technology")); + assertThrowsExactly(IllegalArgumentException.class, () -> new NameSuffixTypeMatcher(null)); } @Test void construction_ThrowsAnException_WhenPassedAnEmptySuffix() { - assertThrowsExactly(IllegalArgumentException.class, () -> new NameSuffixTypeMatcher("", "Technology")); - assertThrowsExactly(IllegalArgumentException.class, () -> new NameSuffixTypeMatcher(" ", "Technology")); + assertThrowsExactly(IllegalArgumentException.class, () -> new NameSuffixTypeMatcher("")); + assertThrowsExactly(IllegalArgumentException.class, () -> new NameSuffixTypeMatcher(" ")); } @Test void matches_ThrowsAnException_WhenPassedNull() { - assertThrowsExactly(IllegalArgumentException.class, () -> new NameSuffixTypeMatcher("Suffix", "Technology").matches(null)); + assertThrowsExactly(IllegalArgumentException.class, () -> new NameSuffixTypeMatcher("Suffix").matches(null)); } @Test void matches_ReturnsFalse_WhenThereIsNoMatch() { - assertFalse(new NameSuffixTypeMatcher("Component", "Technology").matches(new Type("com.example.SomeClass"))); + assertFalse(new NameSuffixTypeMatcher("Component").matches(new Type("com.example.SomeClass"))); } @Test void matches_ReturnsTrue_WhenThereIsAMatch() { - assertTrue(new NameSuffixTypeMatcher("Component", "Technology").matches(new Type("com.example.SomeComponent"))); + assertTrue(new NameSuffixTypeMatcher("Component").matches(new Type("com.example.SomeComponent"))); } } \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/matcher/RegexSuffixTypeMatcherTests.java b/structurizr-component/src/test/java/com/structurizr/component/matcher/RegexSuffixTypeMatcherTests.java index a54da7b7c..2c757c025 100644 --- a/structurizr-component/src/test/java/com/structurizr/component/matcher/RegexSuffixTypeMatcherTests.java +++ b/structurizr-component/src/test/java/com/structurizr/component/matcher/RegexSuffixTypeMatcherTests.java @@ -9,28 +9,28 @@ public class RegexSuffixTypeMatcherTests { @Test void construction_ThrowsAnException_WhenPassedANullRegex() { - assertThrowsExactly(IllegalArgumentException.class, () -> new RegexTypeMatcher(null, "Technology")); + assertThrowsExactly(IllegalArgumentException.class, () -> new RegexTypeMatcher(null)); } @Test void construction_ThrowsAnException_WhenPassedAnEmptyRegex() { - assertThrowsExactly(IllegalArgumentException.class, () -> new RegexTypeMatcher("", "Technology")); - assertThrowsExactly(IllegalArgumentException.class, () -> new RegexTypeMatcher(" ", "Technology")); + assertThrowsExactly(IllegalArgumentException.class, () -> new RegexTypeMatcher("")); + assertThrowsExactly(IllegalArgumentException.class, () -> new RegexTypeMatcher(" ")); } @Test void matches_ThrowsAnException_WhenPassedNull() { - assertThrowsExactly(IllegalArgumentException.class, () -> new RegexTypeMatcher(".*Controller", "Technology").matches(null)); + assertThrowsExactly(IllegalArgumentException.class, () -> new RegexTypeMatcher(".*Controller").matches(null)); } @Test void matches_ReturnsFalse_WhenThereIsNoMatch() { - assertFalse(new RegexTypeMatcher(".*Controller", "Technology").matches(new Type("com.example.SomeClass"))); + assertFalse(new RegexTypeMatcher(".*Controller").matches(new Type("com.example.SomeClass"))); } @Test void matches_ReturnsTrue_WhenThereIsAMatch() { - assertTrue(new RegexTypeMatcher(".*Controller", "Technology").matches(new Type("com.example.SomeController"))); + assertTrue(new RegexTypeMatcher(".*Controller").matches(new Type("com.example.SomeController"))); } } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyDslContext.java index fede404b4..2b2fd405f 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyDslContext.java @@ -15,6 +15,7 @@ final class ComponentFinderStrategyDslContext extends DslContext { @Override protected String[] getPermittedTokens() { return new String[] { + StructurizrDslTokens.COMPONENT_FINDER_STRATEGY_TECHNOLOGY_TOKEN, StructurizrDslTokens.COMPONENT_FINDER_STRATEGY_MATCHER_TOKEN, StructurizrDslTokens.COMPONENT_FINDER_STRATEGY_FILTER_TOKEN, StructurizrDslTokens.COMPONENT_FINDER_STRATEGY_SUPPORTING_TYPES_TOKEN, diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyParser.java index 32bd19a64..61d89c4b8 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyParser.java @@ -11,76 +11,98 @@ import com.structurizr.component.supporting.AllTypesInPackageSupportingTypesStrategy; import com.structurizr.component.supporting.AllTypesUnderPackageSupportingTypesStrategy; +import java.io.File; +import java.lang.reflect.Constructor; + final class ComponentFinderStrategyParser extends AbstractParser { + private static final String TECHNOLOGY_GRAMMAR = "technology "; + private static final String MATCHER_GRAMMAR = "matcher [parameters]"; + private static final String MATCHER_ANNOTATION_GRAMMAR = "matcher annotation "; + private static final String MATCHER_EXTENDS_GRAMMAR = "matcher extends "; + private static final String MATCHER_IMPLEMENTS_GRAMMAR = "matcher implements "; + private static final String MATCHER_NAMESUFFIX_GRAMMAR = "matcher namesuffix "; + private static final String MATCHER_REGEX_GRAMMAR = "matcher regex "; + private static final String FILTER_GRAMMAR = "filter [parameters]"; private static final String SUPPORTING_TYPES_GRAMMAR = "supportingTypes [parameters]"; private static final String NAMING_GRAMMAR = "naming "; - void parseMatcher(ComponentFinderStrategyDslContext context, Tokens tokens) { + void parseTechnology(ComponentFinderStrategyDslContext context, Tokens tokens) { + if (tokens.size() != 2) { + throw new RuntimeException("Expected: " + TECHNOLOGY_GRAMMAR); + } + + String name = tokens.get(1); + context.getComponentFinderStrategyBuilder().forTechnology(name); + } + + void parseMatcher(ComponentFinderStrategyDslContext context, Tokens tokens, File dslFile) { if (tokens.size() < 2) { throw new RuntimeException("Too few tokens, expected: " + MATCHER_GRAMMAR); } - String type = tokens.get(1).toLowerCase(); - switch (type) { + String type = tokens.get(1); + switch (type.toLowerCase()) { case "annotation": - if (tokens.size() == 4) { + if (tokens.size() == 3) { String name = tokens.get(2); - String technology = tokens.get(3); - context.getComponentFinderStrategyBuilder().matchedBy(new AnnotationTypeMatcher(name, technology)); + context.getComponentFinderStrategyBuilder().matchedBy(new AnnotationTypeMatcher(name)); } else { - throw new RuntimeException("Expected: " + MATCHER_GRAMMAR); + throw new RuntimeException("Expected: " + MATCHER_ANNOTATION_GRAMMAR); } break; case "extends": - if (tokens.size() == 4) { + if (tokens.size() == 3) { String name = tokens.get(2); - String technology = tokens.get(3); - context.getComponentFinderStrategyBuilder().matchedBy(new ExtendsTypeMatcher(name, technology)); + context.getComponentFinderStrategyBuilder().matchedBy(new ExtendsTypeMatcher(name)); } else { - throw new RuntimeException("Expected: " + MATCHER_GRAMMAR); + throw new RuntimeException("Expected: " + MATCHER_EXTENDS_GRAMMAR); } break; case "implements": - if (tokens.size() == 4) { + if (tokens.size() == 3) { String name = tokens.get(2); - String technology = tokens.get(3); - context.getComponentFinderStrategyBuilder().matchedBy(new ImplementsTypeMatcher(name, technology)); + context.getComponentFinderStrategyBuilder().matchedBy(new ImplementsTypeMatcher(name)); } else { - throw new RuntimeException("Expected: " + MATCHER_GRAMMAR); + throw new RuntimeException("Expected: " + MATCHER_IMPLEMENTS_GRAMMAR); } break; case "namesuffix": - if (tokens.size() == 4) { + if (tokens.size() == 3) { String suffix = tokens.get(2); - String technology = tokens.get(3); - context.getComponentFinderStrategyBuilder().matchedBy(new NameSuffixTypeMatcher(suffix, technology)); + context.getComponentFinderStrategyBuilder().matchedBy(new NameSuffixTypeMatcher(suffix)); } else { - throw new RuntimeException("Expected: " + MATCHER_GRAMMAR); + throw new RuntimeException("Expected: " + MATCHER_NAMESUFFIX_GRAMMAR); } break; case "regex": - if (tokens.size() == 4) { + if (tokens.size() == 3) { String regex = tokens.get(2); - String technology = tokens.get(3); - context.getComponentFinderStrategyBuilder().matchedBy(new RegexTypeMatcher(regex, technology)); + context.getComponentFinderStrategyBuilder().matchedBy(new RegexTypeMatcher(regex)); } else { - throw new RuntimeException("Expected: " + MATCHER_GRAMMAR); + throw new RuntimeException("Expected: " + MATCHER_REGEX_GRAMMAR); } break; default: - throw new IllegalArgumentException("Unknown matcher: " + type); + try { + Class typeMatcherClass = context.loadClass(type, dslFile); + Constructor constructor = typeMatcherClass.getDeclaredConstructor(); + TypeMatcher typeMatcher = constructor.newInstance(); + context.getComponentFinderStrategyBuilder().matchedBy(typeMatcher); + } catch (Exception e) { + throw new RuntimeException("Type matcher \"" + type + "\" could not be loaded - " + e.getClass() + ": " + e.getMessage()); + } } } - void parseFilter(ComponentFinderStrategyDslContext context, Tokens tokens) { + void parseFilter(ComponentFinderStrategyDslContext context, Tokens tokens, File dslFile) { if (tokens.size() < 2) { throw new RuntimeException("Too few tokens, expected: " + FILTER_GRAMMAR); } @@ -110,7 +132,7 @@ void parseFilter(ComponentFinderStrategyDslContext context, Tokens tokens) { } } - void parseSupportingTypes(ComponentFinderStrategyDslContext context, Tokens tokens) { + void parseSupportingTypes(ComponentFinderStrategyDslContext context, Tokens tokens, File dslFile) { if (tokens.size() < 2) { throw new RuntimeException("Too few tokens, expected: " + SUPPORTING_TYPES_GRAMMAR); } @@ -134,8 +156,8 @@ void parseSupportingTypes(ComponentFinderStrategyDslContext context, Tokens toke } } - void parseNaming(ComponentFinderStrategyDslContext context, Tokens tokens) { - if (tokens.size() < 1) { + void parseNaming(ComponentFinderStrategyDslContext context, Tokens tokens, File dslFile) { + if (tokens.size() < 2) { throw new RuntimeException("Too few tokens, expected: " + NAMING_GRAMMAR); } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DslContext.java index a5d6f02a2..2711c9138 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/DslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DslContext.java @@ -103,7 +103,7 @@ Relationship getRelationship(String identifier) { return identifiersRegister.getRelationship(identifier.toLowerCase()); } - protected Class loadClass(String fqn, File dslFile) throws Exception { + protected Class loadClass(String fqn, File dslFile) throws Exception { File pluginsDirectory = new File(dslFile.getParent(), PLUGINS_DIRECTORY_NAME); URL[] urls = new URL[0]; @@ -122,7 +122,7 @@ protected Class loadClass(String fqn, File dslFile) throws Exception { } URLClassLoader childClassLoader = new URLClassLoader(urls, getClass().getClassLoader()); - return childClassLoader.loadClass(fqn); + return (Class) childClassLoader.loadClass(fqn); } void end() { diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementsParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementsParser.java index 2bfccf11b..f205241ce 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementsParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementsParser.java @@ -22,7 +22,13 @@ Set parse(DslContext context, Tokens tokens) { String expression = tokens.get(1); Set modelItems = new ExpressionParser().parseExpression(expression, context); - return modelItems.stream().filter(mi -> mi instanceof Element).map(mi -> (Element)mi).collect(Collectors.toSet()); + Set elements = modelItems.stream().filter(mi -> mi instanceof Element).map(mi -> (Element)mi).collect(Collectors.toSet()); + + if (elements.isEmpty()) { + throw new RuntimeException("No elements found for expression \"" + expression + "\""); + } + + return elements; } } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImpliedRelationshipsParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImpliedRelationshipsParser.java index 14e781495..b647e5b3d 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImpliedRelationshipsParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImpliedRelationshipsParser.java @@ -47,8 +47,8 @@ void parse(DslContext context, Tokens tokens, File dslFile, boolean restricted) } try { - Class impliedRelationshipsStrategyClass = context.loadClass(option, dslFile); - Constructor constructor = impliedRelationshipsStrategyClass.getDeclaredConstructor(); + Class impliedRelationshipsStrategyClass = context.loadClass(option, dslFile); + Constructor constructor = impliedRelationshipsStrategyClass.getDeclaredConstructor(); ImpliedRelationshipsStrategy impliedRelationshipsStrategy = (ImpliedRelationshipsStrategy)constructor.newInstance(); context.getWorkspace().getModel().setImpliedRelationshipsStrategy(impliedRelationshipsStrategy); } catch (ClassNotFoundException cnfe) { diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipsParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipsParser.java index eb0e0cf7b..890a295d6 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipsParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipsParser.java @@ -23,7 +23,14 @@ Set parse(DslContext context, Tokens tokens) { String expression = tokens.get(1); Set modelItems = new ExpressionParser().parseExpression(expression, context); - return modelItems.stream().filter(mi -> mi instanceof Relationship).map(mi -> (Relationship)mi).collect(Collectors.toSet()); + Set relationships = modelItems.stream().filter(mi -> mi instanceof Relationship).map(mi -> (Relationship)mi).collect(Collectors.toSet()); + + + if (relationships.isEmpty()) { + throw new RuntimeException("No relationships found for expression \"" + expression + "\""); + } + + return relationships; } } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index a162091f8..472df90e3 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -439,17 +439,20 @@ void parse(List lines, File dslFile, boolean include) throws Structurizr startContext(new ComponentFinderStrategyDslContext(getContext(ComponentFinderDslContext.class).getComponentFinderBuilder())); } + } else if (COMPONENT_FINDER_STRATEGY_TECHNOLOGY_TOKEN.equalsIgnoreCase(firstToken) && inContext(ComponentFinderStrategyDslContext.class)) { + new ComponentFinderStrategyParser().parseTechnology(getContext(ComponentFinderStrategyDslContext.class), tokens); + } else if (COMPONENT_FINDER_STRATEGY_MATCHER_TOKEN.equalsIgnoreCase(firstToken) && inContext(ComponentFinderStrategyDslContext.class)) { - new ComponentFinderStrategyParser().parseMatcher(getContext(ComponentFinderStrategyDslContext.class), tokens); + new ComponentFinderStrategyParser().parseMatcher(getContext(ComponentFinderStrategyDslContext.class), tokens, dslFile); } else if (COMPONENT_FINDER_STRATEGY_FILTER_TOKEN.equalsIgnoreCase(firstToken) && inContext(ComponentFinderStrategyDslContext.class)) { - new ComponentFinderStrategyParser().parseFilter(getContext(ComponentFinderStrategyDslContext.class), tokens); + new ComponentFinderStrategyParser().parseFilter(getContext(ComponentFinderStrategyDslContext.class), tokens, dslFile); } else if (COMPONENT_FINDER_STRATEGY_SUPPORTING_TYPES_TOKEN.equalsIgnoreCase(firstToken) && inContext(ComponentFinderStrategyDslContext.class)) { - new ComponentFinderStrategyParser().parseSupportingTypes(getContext(ComponentFinderStrategyDslContext.class), tokens); + new ComponentFinderStrategyParser().parseSupportingTypes(getContext(ComponentFinderStrategyDslContext.class), tokens, dslFile); } else if (COMPONENT_FINDER_STRATEGY_NAMING_TOKEN.equalsIgnoreCase(firstToken) && inContext(ComponentFinderStrategyDslContext.class)) { - new ComponentFinderStrategyParser().parseNaming(getContext(ComponentFinderStrategyDslContext.class), tokens); + new ComponentFinderStrategyParser().parseNaming(getContext(ComponentFinderStrategyDslContext.class), tokens, dslFile); } else if (ENTERPRISE_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelDslContext.class)) { throw new RuntimeException("The enterprise keyword was previously deprecated, and has now been removed - please use group instead (https://docs.structurizr.com/dsl/language#group)"); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java index 1962ec6b0..9fd5fb14f 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java @@ -117,6 +117,7 @@ class StructurizrDslTokens { static final String COMPONENT_FINDER_CLASSES_TOKEN = "classes"; static final String COMPONENT_FINDER_SOURCE_TOKEN = "source"; static final String COMPONENT_FINDER_STRATEGY_TOKEN = "strategy"; + static final String COMPONENT_FINDER_STRATEGY_TECHNOLOGY_TOKEN = "technology"; static final String COMPONENT_FINDER_STRATEGY_MATCHER_TOKEN = "matcher"; static final String COMPONENT_FINDER_STRATEGY_FILTER_TOKEN = "filter"; static final String COMPONENT_FINDER_STRATEGY_SUPPORTING_TYPES_TOKEN = "supportingTypes"; diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderStrategyParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderStrategyParserTests.java new file mode 100644 index 000000000..ba92a3351 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderStrategyParserTests.java @@ -0,0 +1,180 @@ +package com.structurizr.dsl; + +import com.structurizr.component.ComponentFinderBuilder; +import com.structurizr.component.matcher.AnnotationTypeMatcher; +import org.junit.jupiter.api.Test; + +import java.io.File; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +class ComponentFinderStrategyParserTests extends AbstractTests { + + private final ComponentFinderStrategyParser parser = new ComponentFinderStrategyParser(); + private final ComponentFinderBuilder componentFinderBuilder = new ComponentFinderBuilder(); + private final ComponentFinderStrategyDslContext context = new ComponentFinderStrategyDslContext(componentFinderBuilder); + + @Test + void test_parseTechnology_ThrowsAnException_WhenThereAreTooFewTokens() { + try { + parser.parseTechnology(context, tokens("technology")); + fail(); + } catch (Exception e) { + assertEquals("Expected: technology ", e.getMessage()); + } + } + + @Test + void test_parseTechnology_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parseTechnology(context, tokens("technology", "name", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Expected: technology ", e.getMessage()); + } + } + + @Test + void test_parseTechnology() { + parser.parseTechnology(context, tokens("technology", "name")); + assertEquals("ComponentFinderStrategyBuilder{technology='name', typeMatcher=null, typeFilter=DefaultTypeFilter{}, supportingTypesStrategy=DefaultSupportingTypesStrategy{}, namingStrategy=DefaultNamingStrategy{}, componentVisitor=DefaultComponentVisitor{}}", context.getComponentFinderStrategyBuilder().toString()); + } + + @Test + void test_parseMatcher_ThrowsAnException_WhenNoTypeIsSpecified() { + try { + parser.parseMatcher(context, tokens("matcher"), null); + fail(); + } catch (Exception e) { + assertEquals("Too few tokens, expected: matcher [parameters]", e.getMessage()); + } + } + + @Test + void test_parseMatcher_WhenTheAnnotationTypeMatcherIsUsedAndThereAreTooFewTokens() { + try { + parser.parseMatcher(context, tokens("matcher", "annotation"), null); + fail(); + } catch (Exception e) { + assertEquals("Expected: matcher annotation ", e.getMessage()); + } + } + + @Test + void test_parseMatcher_WhenTheAnnotationTypeMatcherIsUsed() { + parser.parseMatcher(context, tokens("matcher", "annotation", "com.example.Component"), null); + assertEquals("ComponentFinderStrategyBuilder{technology='null', typeMatcher=AnnotationTypeMatcher{annotationType='Lcom/example/Component;'}, typeFilter=DefaultTypeFilter{}, supportingTypesStrategy=DefaultSupportingTypesStrategy{}, namingStrategy=DefaultNamingStrategy{}, componentVisitor=DefaultComponentVisitor{}}", context.getComponentFinderStrategyBuilder().toString()); + } + + @Test + void test_parseMatcher_WhenTheExtendsTypeMatcherIsUsedAndThereAreTooFewTokens() { + try { + parser.parseMatcher(context, tokens("matcher", "extends"), null); + fail(); + } catch (Exception e) { + assertEquals("Expected: matcher extends ", e.getMessage()); + } + } + + @Test + void test_parseMatcher_WhenTheExtendsTypeMatcherIsUsed() { + parser.parseMatcher(context, tokens("matcher", "extends", "com.example.Component"), null); + assertEquals("ComponentFinderStrategyBuilder{technology='null', typeMatcher=ExtendsTypeMatcher{className='com.example.Component'}, typeFilter=DefaultTypeFilter{}, supportingTypesStrategy=DefaultSupportingTypesStrategy{}, namingStrategy=DefaultNamingStrategy{}, componentVisitor=DefaultComponentVisitor{}}", context.getComponentFinderStrategyBuilder().toString()); + } + + @Test + void test_parseMatcher_WhenTheImplementsTypeMatcherIsUsedAndThereAreTooFewTokens() { + try { + parser.parseMatcher(context, tokens("matcher", "implements"), null); + fail(); + } catch (Exception e) { + assertEquals("Expected: matcher implements ", e.getMessage()); + } + } + + @Test + void test_parseMatcher_WhenTheImplementsTypeMatcherIsUsed() { + parser.parseMatcher(context, tokens("matcher", "implements", "com.example.Component"), null); + assertEquals("ComponentFinderStrategyBuilder{technology='null', typeMatcher=ImplementsTypeMatcher{interfaceName='com.example.Component'}, typeFilter=DefaultTypeFilter{}, supportingTypesStrategy=DefaultSupportingTypesStrategy{}, namingStrategy=DefaultNamingStrategy{}, componentVisitor=DefaultComponentVisitor{}}", context.getComponentFinderStrategyBuilder().toString()); + } + + @Test + void test_parseMatcher_WhenTheNameSuffixTypeMatcherIsUsedAndThereAreTooFewTokens() { + try { + parser.parseMatcher(context, tokens("matcher", "namesuffix"), null); + fail(); + } catch (Exception e) { + assertEquals("Expected: matcher namesuffix ", e.getMessage()); + } + } + + @Test + void test_parseMatcher_WhenTheNameSuffixTypeMatcherIsUsed() { + parser.parseMatcher(context, tokens("matcher", "namesuffix", "Component"), null); + assertEquals("ComponentFinderStrategyBuilder{technology='null', typeMatcher=NameSuffixTypeMatcher{suffix='Component'}, typeFilter=DefaultTypeFilter{}, supportingTypesStrategy=DefaultSupportingTypesStrategy{}, namingStrategy=DefaultNamingStrategy{}, componentVisitor=DefaultComponentVisitor{}}", context.getComponentFinderStrategyBuilder().toString()); + } + + @Test + void test_parseMatcher_WhenTheRegexTypeMatcherIsUsedAndThereAreTooFewTokens() { + try { + parser.parseMatcher(context, tokens("matcher", "regex"), null); + fail(); + } catch (Exception e) { + assertEquals("Expected: matcher regex ", e.getMessage()); + } + } + + @Test + void test_parseMatcher_WhenTheRegexTypeMatcherIsUsed() { + parser.parseMatcher(context, tokens("matcher", "regex", ".*Component"), null); + assertEquals("ComponentFinderStrategyBuilder{technology='null', typeMatcher=RegexTypeMatcher{regex=.*Component}, typeFilter=DefaultTypeFilter{}, supportingTypesStrategy=DefaultSupportingTypesStrategy{}, namingStrategy=DefaultNamingStrategy{}, componentVisitor=DefaultComponentVisitor{}}", context.getComponentFinderStrategyBuilder().toString()); + } + + @Test + void test_parseMatcher_WhenACustomTypeMatcherIsUsedButCannotBeLoaded() { + try { + parser.parseMatcher(context, tokens("matcher", "com.example.CustomTypeMatcher"), new File(".")); + fail(); + } catch (Exception e) { + assertEquals("Type matcher \"com.example.CustomTypeMatcher\" could not be loaded - class java.lang.ClassNotFoundException: com.example.CustomTypeMatcher", e.getMessage()); + } + } + + @Test + void test_parseMatcher_WhenACustomTypeMatcherIsUsed() { + parser.parseMatcher(context, tokens("matcher", "com.structurizr.dsl.example.CustomTypeMatcher"), new File(".")); + assertEquals("ComponentFinderStrategyBuilder{technology='null', typeMatcher=CustomTypeMatcher{}, typeFilter=DefaultTypeFilter{}, supportingTypesStrategy=DefaultSupportingTypesStrategy{}, namingStrategy=DefaultNamingStrategy{}, componentVisitor=DefaultComponentVisitor{}}", context.getComponentFinderStrategyBuilder().toString()); + } + + @Test + void test_parseFilter_ThrowsAnException_WhenNoTypeIsSpecified() { + try { + parser.parseFilter(context, tokens("filter"), null); + fail(); + } catch (Exception e) { + assertEquals("Too few tokens, expected: filter [parameters]", e.getMessage()); + } + } + + @Test + void test_parseSupportingTypes_ThrowsAnException_WhenNoTypeIsSpecified() { + try { + parser.parseSupportingTypes(context, tokens("supportingTypes"), null); + fail(); + } catch (Exception e) { + assertEquals("Too few tokens, expected: supportingTypes [parameters]", e.getMessage()); + } + } + + @Test + void test_parseNaming_ThrowsAnException_WhenNoTypeIsSpecified() { + try { + parser.parseNaming(context, tokens("naming"), null); + fail(); + } catch (Exception e) { + assertEquals("Too few tokens, expected: naming ", e.getMessage()); + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementsParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementsParserTests.java index 6e437b972..9eb68b7f8 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementsParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementsParserTests.java @@ -24,6 +24,16 @@ void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { } } + @Test + void test_parse_ThrowsAnException_WhenThereAreNoElementsFound() { + try { + parser.parse(context(), tokens("!elements", "expression")); + fail(); + } catch (Exception e) { + assertEquals("No elements found for expression \"expression\"", e.getMessage()); + } + } + @Test void test_parse_FindsElementsByExpression() { Container application = model.addSoftwareSystem("Software System").addContainer("Application"); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ImpliedRelationshipsParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ImpliedRelationshipsParserTests.java index eb7ded0f6..eae9a875c 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ImpliedRelationshipsParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ImpliedRelationshipsParserTests.java @@ -79,4 +79,11 @@ void test_parse_ThrowsAnException_WhenACustomStrategyIsUsedInUnrestrictedModeBut } } + @Test + void test_parse_SetsTheStrategy_WhenACustomStrategyIsUsedInUnrestrictedMode() { + parser.parse(context(), tokens("!impliedRelationships", "com.structurizr.dsl.example.CustomImpliedRelationshipsStrategy"), new File("."), false); + + assertEquals("com.structurizr.dsl.example.CustomImpliedRelationshipsStrategy", workspace.getModel().getImpliedRelationshipsStrategy().getClass().getCanonicalName()); + } + } \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/RelationshipsParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/RelationshipsParserTests.java index c3e685ccc..b1f42db8a 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/RelationshipsParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/RelationshipsParserTests.java @@ -22,6 +22,16 @@ void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { } } + @Test + void test_parse_ThrowsAnException_WhenThereAreNoRelationshipsFound() { + try { + parser.parse(context(), tokens("!relationships", "expression")); + fail(); + } catch (Exception e) { + assertEquals("No relationships found for expression \"expression\"", e.getMessage()); + } + } + @Test void test_parse_FindsRelationshipsByExpression() { SoftwareSystem a = model.addSoftwareSystem("A"); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/example/CustomImpliedRelationshipsStrategy.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/example/CustomImpliedRelationshipsStrategy.java new file mode 100644 index 000000000..bb45da945 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/example/CustomImpliedRelationshipsStrategy.java @@ -0,0 +1,13 @@ +package com.structurizr.dsl.example; + +import com.structurizr.model.ImpliedRelationshipsStrategy; +import com.structurizr.model.Relationship; + +public class CustomImpliedRelationshipsStrategy implements ImpliedRelationshipsStrategy { + + @Override + public void createImpliedRelationships(Relationship relationship) { + + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/example/CustomTypeMatcher.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/example/CustomTypeMatcher.java new file mode 100644 index 000000000..6530ae000 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/example/CustomTypeMatcher.java @@ -0,0 +1,18 @@ +package com.structurizr.dsl.example; + +import com.structurizr.component.Type; +import com.structurizr.component.matcher.TypeMatcher; + +public class CustomTypeMatcher implements TypeMatcher { + + @Override + public boolean matches(Type type) { + return false; + } + + @Override + public String toString() { + return "CustomTypeMatcher{}"; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl b/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl index 279c77f73..f672c28e9 100644 --- a/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl +++ b/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl @@ -23,11 +23,13 @@ classes "${SPRING_PETCLINIC_HOME}/target/spring-petclinic-3.3.0-SNAPSHOT.jar" source "${SPRING_PETCLINIC_HOME}/src/main/java" strategy { - matcher annotation "org.springframework.stereotype.Controller" "Spring MVC Controller" + technology "Spring MVC Controller" + matcher annotation "org.springframework.stereotype.Controller" filter excludeRegex ".*.CrashController" } strategy { - matcher implements "org.springframework.data.repository.Repository" "Spring Data Repository" + technology "Spring Data Repository" + matcher implements "org.springframework.data.repository.Repository" } } From bc93a0f9f658d5a037de2b4248e93a68e5b5480e Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 24 Aug 2024 15:51:28 +0100 Subject: [PATCH 550/717] Adds support for custom type matchers with a parameter. --- .../dsl/ComponentFinderStrategyParser.java | 11 +++++++++-- .../dsl/ComponentFinderStrategyParserTests.java | 9 ++++++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyParser.java index 61d89c4b8..8f2f7daf9 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyParser.java @@ -93,8 +93,15 @@ void parseMatcher(ComponentFinderStrategyDslContext context, Tokens tokens, File default: try { Class typeMatcherClass = context.loadClass(type, dslFile); - Constructor constructor = typeMatcherClass.getDeclaredConstructor(); - TypeMatcher typeMatcher = constructor.newInstance(); + + TypeMatcher typeMatcher; + if (tokens.size() == 3) { + String parameter = tokens.get(2); + typeMatcher = typeMatcherClass.getDeclaredConstructor(String.class).newInstance(parameter); + } else { + typeMatcher = typeMatcherClass.getDeclaredConstructor().newInstance(); + } + context.getComponentFinderStrategyBuilder().matchedBy(typeMatcher); } catch (Exception e) { throw new RuntimeException("Type matcher \"" + type + "\" could not be loaded - " + e.getClass() + ": " + e.getMessage()); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderStrategyParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderStrategyParserTests.java index ba92a3351..ad95614e9 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderStrategyParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderStrategyParserTests.java @@ -2,6 +2,7 @@ import com.structurizr.component.ComponentFinderBuilder; import com.structurizr.component.matcher.AnnotationTypeMatcher; +import com.structurizr.component.matcher.NameSuffixTypeMatcher; import org.junit.jupiter.api.Test; import java.io.File; @@ -142,11 +143,17 @@ void test_parseMatcher_WhenACustomTypeMatcherIsUsedButCannotBeLoaded() { } @Test - void test_parseMatcher_WhenACustomTypeMatcherIsUsed() { + void test_parseMatcher_WhenACustomTypeMatcherIsUsedWithoutParameters() { parser.parseMatcher(context, tokens("matcher", "com.structurizr.dsl.example.CustomTypeMatcher"), new File(".")); assertEquals("ComponentFinderStrategyBuilder{technology='null', typeMatcher=CustomTypeMatcher{}, typeFilter=DefaultTypeFilter{}, supportingTypesStrategy=DefaultSupportingTypesStrategy{}, namingStrategy=DefaultNamingStrategy{}, componentVisitor=DefaultComponentVisitor{}}", context.getComponentFinderStrategyBuilder().toString()); } + @Test + void test_parseMatcher_WhenACustomTypeMatcherIsUsedWithAParameter() { + parser.parseMatcher(context, tokens("matcher", NameSuffixTypeMatcher.class.getCanonicalName(), "Component"), new File(".")); + assertEquals("ComponentFinderStrategyBuilder{technology='null', typeMatcher=NameSuffixTypeMatcher{suffix='Component'}, typeFilter=DefaultTypeFilter{}, supportingTypesStrategy=DefaultSupportingTypesStrategy{}, namingStrategy=DefaultNamingStrategy{}, componentVisitor=DefaultComponentVisitor{}}", context.getComponentFinderStrategyBuilder().toString()); + } + @Test void test_parseFilter_ThrowsAnException_WhenNoTypeIsSpecified() { try { From 8d5751150a4af172fe82366eca3a61df5712675b Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 25 Aug 2024 11:25:30 +0100 Subject: [PATCH 551/717] More tidy up ... also provides a way to iterate over each discovered component in the DSL. --- .../ComponentFinderStrategyBuilder.java | 13 +++--- .../component/DiscoveredComponent.java | 7 +++ .../ComponentFinderStrategyDslContext.java | 3 +- ...ponentFinderStrategyForEachDslContext.java | 30 +++++++++++++ .../dsl/ComponentFinderStrategyParser.java | 3 +- .../structurizr/dsl/StructurizrDslParser.java | 45 +++++++++++-------- .../structurizr/dsl/StructurizrDslTokens.java | 1 + .../java/com/structurizr/dsl/DslTests.java | 28 +++++++++--- .../dsl/spring-petclinic/workspace.dsl | 20 ++++----- 9 files changed, 104 insertions(+), 46 deletions(-) create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyForEachDslContext.java diff --git a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategyBuilder.java b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategyBuilder.java index c2a7ee4a0..88adf2efe 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategyBuilder.java +++ b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategyBuilder.java @@ -4,7 +4,6 @@ import com.structurizr.component.filter.TypeFilter; import com.structurizr.component.matcher.TypeMatcher; import com.structurizr.component.naming.DefaultNamingStrategy; -import com.structurizr.component.naming.SimpleNamingStrategy; import com.structurizr.component.naming.NamingStrategy; import com.structurizr.component.supporting.DefaultSupportingTypesStrategy; import com.structurizr.component.supporting.SupportingTypesStrategy; @@ -26,12 +25,6 @@ public final class ComponentFinderStrategyBuilder { public ComponentFinderStrategyBuilder() { } - public ComponentFinderStrategyBuilder forTechnology(String technology) { - this.technology = technology; - - return this; - } - public ComponentFinderStrategyBuilder matchedBy(TypeMatcher typeMatcher) { this.typeMatcher = typeMatcher; @@ -56,6 +49,12 @@ public ComponentFinderStrategyBuilder namedBy(NamingStrategy namingStrategy) { return this; } + public ComponentFinderStrategyBuilder asTechnology(String technology) { + this.technology = technology; + + return this; + } + public ComponentFinderStrategyBuilder forEach(ComponentVisitor componentVisitor) { this.componentVisitor = componentVisitor; diff --git a/structurizr-component/src/main/java/com/structurizr/component/DiscoveredComponent.java b/structurizr-component/src/main/java/com/structurizr/component/DiscoveredComponent.java index 8eddb2e08..79bba4d51 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/DiscoveredComponent.java +++ b/structurizr-component/src/main/java/com/structurizr/component/DiscoveredComponent.java @@ -77,4 +77,11 @@ void setComponentFinderStrategy(ComponentFinderStrategy componentFinderStrategy) this.componentFinderStrategy = componentFinderStrategy; } + @Override + public String toString() { + return "DiscoveredComponent{" + + "name='" + name + '\'' + + '}'; + } + } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyDslContext.java index 2b2fd405f..762d4c383 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyDslContext.java @@ -19,7 +19,8 @@ protected String[] getPermittedTokens() { StructurizrDslTokens.COMPONENT_FINDER_STRATEGY_MATCHER_TOKEN, StructurizrDslTokens.COMPONENT_FINDER_STRATEGY_FILTER_TOKEN, StructurizrDslTokens.COMPONENT_FINDER_STRATEGY_SUPPORTING_TYPES_TOKEN, - StructurizrDslTokens.COMPONENT_FINDER_STRATEGY_NAMING_TOKEN + StructurizrDslTokens.COMPONENT_FINDER_STRATEGY_NAMING_TOKEN, + StructurizrDslTokens.COMPONENT_FINDER_STRATEGY_FOREACH_TOKEN }; } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyForEachDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyForEachDslContext.java new file mode 100644 index 000000000..ff2db4158 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyForEachDslContext.java @@ -0,0 +1,30 @@ +package com.structurizr.dsl; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +final class ComponentFinderStrategyForEachDslContext extends DslContext { + + private final List dslLines = new ArrayList<>(); + + ComponentFinderStrategyForEachDslContext(ComponentFinderStrategyDslContext dslContext, StructurizrDslParser dslParser) { + dslContext.getComponentFinderStrategyBuilder().forEach(component -> { + try { + dslParser.parse(dslLines, new ComponentDslContext(component)); + } catch (StructurizrDslParserException e) { + throw new RuntimeException(e); + } + }); + } + + void addLine(String line) { + this.dslLines.add(line); + } + + @Override + protected String[] getPermittedTokens() { + return new String[] {}; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyParser.java index 8f2f7daf9..030fc3ff3 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyParser.java @@ -12,7 +12,6 @@ import com.structurizr.component.supporting.AllTypesUnderPackageSupportingTypesStrategy; import java.io.File; -import java.lang.reflect.Constructor; final class ComponentFinderStrategyParser extends AbstractParser { @@ -35,7 +34,7 @@ void parseTechnology(ComponentFinderStrategyDslContext context, Tokens tokens) { } String name = tokens.get(1); - context.getComponentFinderStrategyBuilder().forTechnology(name); + context.getComponentFinderStrategyBuilder().asTechnology(name); } void parseMatcher(ComponentFinderStrategyDslContext context, Tokens tokens, File dslFile) { diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index 472df90e3..6efeb2db6 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -108,14 +108,7 @@ public Workspace getWorkspace() { } private String getParsedDsl() { - StringBuilder buf = new StringBuilder(); - - for (String line : dslSourceLines) { - buf.append(line); - buf.append(System.lineSeparator()); - } - - return buf.toString(); + return String.join(System.lineSeparator(), dslSourceLines); } void parse(DslParserContext context, File path) throws StructurizrDslParserException { @@ -140,7 +133,7 @@ public void parse(File dslFile) throws StructurizrDslParserException { } try { - parse(Files.readAllLines(dslFile.toPath(), characterEncoding), dslFile, false); + parse(Files.readAllLines(dslFile.toPath(), characterEncoding), dslFile, false, true); } catch (IOException e) { throw new StructurizrDslParserException(e.getMessage()); } @@ -175,7 +168,13 @@ public void parse(String dsl, File dslFile) throws StructurizrDslParserException } List lines = Arrays.asList(dsl.split("\\r?\\n")); - parse(lines, dslFile, false); + parse(lines, dslFile, false, true); + } + + void parse(List lines, DslContext dslContext) throws StructurizrDslParserException { + startContext(dslContext); + parse(lines, null, true, false); + endContext(); } /** @@ -185,13 +184,12 @@ public void parse(String dsl, File dslFile) throws StructurizrDslParserException * @param dslFile a File representing the DSL file, and therefore where includes/images/etc should be loaded relative to * @throws StructurizrDslParserException when something goes wrong */ - void parse(List lines, File dslFile, boolean include) throws StructurizrDslParserException { + void parse(List lines, File dslFile, boolean fragment, boolean includeInDslSourceLines) throws StructurizrDslParserException { List dslLines = preProcessLines(lines); for (DslLine dslLine : dslLines) { - boolean includeInDslSourceLines = true; - String line = dslLine.getSource(); + String lineForDslSource = line; if (line.startsWith(BOM)) { // this caters for files encoded as "UTF-8 with BOM" @@ -255,12 +253,13 @@ void parse(List lines, File dslFile, boolean include) throws Structurizr paddedLines.add(leadingSpace + unpaddedLine); } - parse(paddedLines, includedFile.getFile(), true); + parse(paddedLines, includedFile.getFile(), true, true); } - - includeInDslSourceLines = false; } + // include the !include in the parser DSL as: # !include ... + lineForDslSource = null; + } else if (PLUGIN_TOKEN.equalsIgnoreCase(firstToken)) { if (!restricted) { String fullyQualifiedClassName = new PluginParser().parse(getContext(), tokens.withoutContextStartToken()); @@ -454,6 +453,14 @@ void parse(List lines, File dslFile, boolean include) throws Structurizr } else if (COMPONENT_FINDER_STRATEGY_NAMING_TOKEN.equalsIgnoreCase(firstToken) && inContext(ComponentFinderStrategyDslContext.class)) { new ComponentFinderStrategyParser().parseNaming(getContext(ComponentFinderStrategyDslContext.class), tokens, dslFile); + } else if (COMPONENT_FINDER_STRATEGY_FOREACH_TOKEN.equalsIgnoreCase(firstToken) && inContext(ComponentFinderStrategyDslContext.class)) { + if (shouldStartContext(tokens)) { + startContext(new ComponentFinderStrategyForEachDslContext(getContext(ComponentFinderStrategyDslContext.class), this)); + } + + } else if (inContext(ComponentFinderStrategyForEachDslContext.class)) { + getContext(ComponentFinderStrategyForEachDslContext.class).addLine(line); + } else if (ENTERPRISE_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelDslContext.class)) { throw new RuntimeException("The enterprise keyword was previously deprecated, and has now been removed - please use group instead (https://docs.structurizr.com/dsl/language#group)"); @@ -998,8 +1005,8 @@ void parse(List lines, File dslFile, boolean include) throws Structurizr } } - if (includeInDslSourceLines) { - dslSourceLines.add(line); + if (includeInDslSourceLines && lineForDslSource != null) { + dslSourceLines.add(lineForDslSource); } } catch (Exception e) { if (e.getMessage() != null) { @@ -1010,7 +1017,7 @@ void parse(List lines, File dslFile, boolean include) throws Structurizr } } - if (!include && !contextStack.empty()) { + if (!fragment && !contextStack.empty()) { throw new StructurizrDslParserException("Unexpected end of DSL content - are one or more closing curly braces missing?"); } } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java index 9fd5fb14f..5e26d24df 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java @@ -122,5 +122,6 @@ class StructurizrDslTokens { static final String COMPONENT_FINDER_STRATEGY_FILTER_TOKEN = "filter"; static final String COMPONENT_FINDER_STRATEGY_SUPPORTING_TYPES_TOKEN = "supportingTypes"; static final String COMPONENT_FINDER_STRATEGY_NAMING_TOKEN = "naming"; + static final String COMPONENT_FINDER_STRATEGY_FOREACH_TOKEN = "forEach"; } \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index 981a5f5ae..f3db3bb16 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -332,7 +332,7 @@ void test_includeLocalFile() throws Exception { " }\n" + " }\n" + "\n" + - "}\n", new String(Base64.getDecoder().decode(workspace.getProperties().get("structurizr.dsl")))); + "}", new String(Base64.getDecoder().decode(workspace.getProperties().get("structurizr.dsl")))); } @Test @@ -380,7 +380,7 @@ void test_includeLocalDirectory() throws Exception { " }\n" + " }\n" + "\n" + - "}\n", new String(Base64.getDecoder().decode(workspace.getProperties().get("structurizr.dsl")))); + "}", new String(Base64.getDecoder().decode(workspace.getProperties().get("structurizr.dsl")))); } @Test @@ -415,7 +415,7 @@ void test_includeUrl() throws Exception { " }\n" + " }\n" + "\n" + - "}\n", new String(Base64.getDecoder().decode(workspace.getProperties().get("structurizr.dsl")))); + "}", new String(Base64.getDecoder().decode(workspace.getProperties().get("structurizr.dsl")))); } @Test @@ -1150,10 +1150,16 @@ void springPetClinic() throws Exception { System.out.println(springPetClinicHome); if (!StringUtils.isNullOrEmpty(springPetClinicHome)) { System.out.println("Running Spring PetClinic example..."); + + File workspaceFile = new File("src/test/resources/dsl/spring-petclinic/workspace.dsl"); StructurizrDslParser parser = new StructurizrDslParser(); - parser.parse(new File("src/test/resources/dsl/spring-petclinic/workspace.dsl")); + parser.parse(workspaceFile); + + Person clinicEmployee = (Person)parser.getIdentifiersRegister().getElement("clinicEmployee"); Container webApplication = (Container)parser.getIdentifiersRegister().getElement("springPetClinic.webApplication"); + Container relationalDatabaseSchema = (Container)parser.getIdentifiersRegister().getElement("springPetClinic.relationalDatabaseSchema"); + assertEquals(7, webApplication.getComponents().size()); Component welcomeController = webApplication.getComponentWithName("Welcome Controller"); @@ -1162,6 +1168,7 @@ void springPetClinic() throws Exception { assertEquals(new File(springPetClinicHome, "src/main/java/org/springframework/samples/petclinic/system/WelcomeController.java").getAbsolutePath(), welcomeController.getProperties().get("component.src")); assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/system/WelcomeController.java", welcomeController.getUrl()); assertSame(welcomeController, parser.getIdentifiersRegister().getElement("springPetClinic.webApplication.welcomecontroller")); + assertTrue(clinicEmployee.hasEfferentRelationshipWith(welcomeController)); Component ownerController = webApplication.getComponentWithName("Owner Controller"); assertNotNull(ownerController); @@ -1169,6 +1176,7 @@ void springPetClinic() throws Exception { assertEquals(new File(springPetClinicHome, "src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java").getAbsolutePath(), ownerController.getProperties().get("component.src")); assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java", ownerController.getUrl()); assertSame(ownerController, parser.getIdentifiersRegister().getElement("springPetClinic.webApplication.ownerController")); + assertTrue(clinicEmployee.hasEfferentRelationshipWith(ownerController)); Component petController = webApplication.getComponentWithName("Pet Controller"); assertNotNull(petController); @@ -1176,6 +1184,7 @@ void springPetClinic() throws Exception { assertEquals(new File(springPetClinicHome, "src/main/java/org/springframework/samples/petclinic/owner/PetController.java").getAbsolutePath(), petController.getProperties().get("component.src")); assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/owner/PetController.java", petController.getUrl()); assertSame(petController, parser.getIdentifiersRegister().getElement("springPetClinic.webApplication.petcontroller")); + assertTrue(clinicEmployee.hasEfferentRelationshipWith(petController)); Component vetController = webApplication.getComponentWithName("Vet Controller"); assertNotNull(vetController); @@ -1183,6 +1192,7 @@ void springPetClinic() throws Exception { assertEquals(new File(springPetClinicHome, "src/main/java/org/springframework/samples/petclinic/vet/VetController.java").getAbsolutePath(), vetController.getProperties().get("component.src")); assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/vet/VetController.java", vetController.getUrl()); assertSame(vetController, parser.getIdentifiersRegister().getElement("springPetClinic.webApplication.vetcontroller")); + assertTrue(clinicEmployee.hasEfferentRelationshipWith(vetController)); Component visitController = webApplication.getComponentWithName("Visit Controller"); assertNotNull(visitController); @@ -1190,6 +1200,7 @@ void springPetClinic() throws Exception { assertEquals(new File(springPetClinicHome, "src/main/java/org/springframework/samples/petclinic/owner/VisitController.java").getAbsolutePath(), visitController.getProperties().get("component.src")); assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/owner/VisitController.java", visitController.getUrl()); assertSame(visitController, parser.getIdentifiersRegister().getElement("springPetClinic.webApplication.visitcontroller")); + assertTrue(clinicEmployee.hasEfferentRelationshipWith(visitController)); Component ownerRepository = webApplication.getComponentWithName("Owner Repository"); assertNotNull(ownerRepository); @@ -1197,6 +1208,7 @@ void springPetClinic() throws Exception { assertEquals(new File(springPetClinicHome, "src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java").getAbsolutePath(), ownerRepository.getProperties().get("component.src")); assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java", ownerRepository.getUrl()); assertSame(ownerRepository, parser.getIdentifiersRegister().getElement("springPetClinic.webApplication.ownerrepository")); + assertTrue(ownerRepository.hasEfferentRelationshipWith(relationalDatabaseSchema)); Component vetRepository = webApplication.getComponentWithName("Vet Repository"); assertNotNull(vetRepository); @@ -1204,14 +1216,18 @@ void springPetClinic() throws Exception { assertEquals(new File(springPetClinicHome, "src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java").getAbsolutePath(), vetRepository.getProperties().get("component.src")); assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java", vetRepository.getUrl()); assertSame(vetRepository, parser.getIdentifiersRegister().getElement("springPetClinic.webApplication.vetrepository")); + assertTrue(vetRepository.hasEfferentRelationshipWith(relationalDatabaseSchema)); assertTrue(welcomeController.getRelationships().isEmpty()); - assertNotNull(petController.getEfferentRelationshipWith(ownerRepository)); assertNotNull(visitController.getEfferentRelationshipWith(ownerRepository)); assertNotNull(ownerController.getEfferentRelationshipWith(ownerRepository)); - assertNotNull(vetController.getEfferentRelationshipWith(vetRepository)); + + // this checks that the component forEach { ... } lines don't get repeated in the outputted DSL source + String content = Files.readString(workspaceFile.toPath()); + assertEquals(content, new String(Base64.getDecoder().decode(parser.getWorkspace().getProperties().get("structurizr.dsl")))); + } else { System.out.println("Skipping Spring PetClinic example..."); } diff --git a/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl b/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl index f672c28e9..abf7e451a 100644 --- a/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl +++ b/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl @@ -1,4 +1,4 @@ - workspace "Spring PetClinic" "A C4 model of the Spring PetClinic sample app (https://github.com/spring-projects/spring-petclinic/)" { +workspace "Spring PetClinic" "A C4 model of the Spring PetClinic sample app (https://github.com/spring-projects/spring-petclinic/)" { // this example requires an environment variable as follows: // - Name: SPRING_PETCLINIC_HOME @@ -26,26 +26,24 @@ technology "Spring MVC Controller" matcher annotation "org.springframework.stereotype.Controller" filter excludeRegex ".*.CrashController" + forEach { + clinicEmployee -> this "Uses" + tag "Spring MVC Controller" + } } strategy { technology "Spring Data Repository" matcher implements "org.springframework.data.repository.Repository" + forEach { + -> relationalDatabaseSchema "Reads from and writes to" + tag "Spring Data Repository" + } } } !script groovy { element.components.each { it.url = it.properties["component.src"].replace(System.getenv("SPRING_PETCLINIC_HOME") + "/src/main/java", "https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java") } } - - !elements "element.parent==webApplication && element.technology==Spring MVC Controller" { - clinicEmployee -> this "Uses" - tag "Spring MVC Controller" - } - - !elements "element.parent==webApplication && element.technology==Spring Data Repository" { - -> relationalDatabaseSchema "Reads from and writes to" - tag "Spring Data Repository" - } } } } From 2ed0ccaa624e25a06475593a285f45e637813530 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 26 Aug 2024 13:00:44 +0100 Subject: [PATCH 552/717] Initial commit of a utility to load a Structurizr workspace into a Neo4j database. --- settings.gradle | 3 +- structurizr-neo4j/README.md | 5 +++ structurizr-neo4j/build.gradle | 9 +++++ .../com/structurizr/neo4j/SimpleLoader.java | 36 +++++++++++++++++++ .../test/java/com/structurizr/Example.java | 28 +++++++++++++++ 5 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 structurizr-neo4j/README.md create mode 100644 structurizr-neo4j/build.gradle create mode 100644 structurizr-neo4j/src/main/java/com/structurizr/neo4j/SimpleLoader.java create mode 100644 structurizr-neo4j/src/test/java/com/structurizr/Example.java diff --git a/settings.gradle b/settings.gradle index 57df82f6d..8035bdd02 100644 --- a/settings.gradle +++ b/settings.gradle @@ -7,4 +7,5 @@ include 'structurizr-core' include 'structurizr-dsl' include 'structurizr-export' include 'structurizr-import' -include 'structurizr-inspection' \ No newline at end of file +include 'structurizr-inspection' +include 'structurizr-neo4j' \ No newline at end of file diff --git a/structurizr-neo4j/README.md b/structurizr-neo4j/README.md new file mode 100644 index 000000000..c01eb1a5e --- /dev/null +++ b/structurizr-neo4j/README.md @@ -0,0 +1,5 @@ +# structurizr-neo4j + +[![Maven Central](https://img.shields.io/maven-central/v/com.structurizr/structurizr-neo4j.svg?label=Maven%20Central)](https://search.maven.org/artifact/com.structurizr/structurizr-neo4j) + +This library provides utilities to import a Structurizr workspace into Neo4j. \ No newline at end of file diff --git a/structurizr-neo4j/build.gradle b/structurizr-neo4j/build.gradle new file mode 100644 index 000000000..2c06e792e --- /dev/null +++ b/structurizr-neo4j/build.gradle @@ -0,0 +1,9 @@ +dependencies { + + api project(':structurizr-core') + implementation 'org.neo4j.driver:neo4j-java-driver:5.23.0' + + testImplementation project(':structurizr-client') + testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2' + +} \ No newline at end of file diff --git a/structurizr-neo4j/src/main/java/com/structurizr/neo4j/SimpleLoader.java b/structurizr-neo4j/src/main/java/com/structurizr/neo4j/SimpleLoader.java new file mode 100644 index 000000000..727b06c5e --- /dev/null +++ b/structurizr-neo4j/src/main/java/com/structurizr/neo4j/SimpleLoader.java @@ -0,0 +1,36 @@ +package com.structurizr.neo4j; + +import com.structurizr.Workspace; +import com.structurizr.model.Element; +import com.structurizr.model.Relationship; +import com.structurizr.util.StringUtils; +import org.neo4j.driver.Driver; +import org.neo4j.driver.SessionConfig; + +public class SimpleLoader { + + public void load(Workspace workspace, Driver driver, String database) { + try (var session = driver.session(SessionConfig.builder().withDatabase(database).build())) { + for (Element element : workspace.getModel().getElements()) { + session.run(String.format( + "CREATE ( :Element { id: '%s', name: \"%s\", type: \"%s\" })", + element.getId(), element.getName(), element.getClass().getSimpleName().toLowerCase() + )); + } + + session.run("CREATE INDEX element_index FOR (n:Element) ON (n.id)"); + + for (Relationship relationship : workspace.getModel().getRelationships()) { + session.run(String.format( + """ + MATCH ( from:Element { id: '%s' } ), ( to:Element { id: '%s' } ) + CREATE (from)-[:HAS_RELATIONSHIP_WITH {role: '%s'}]->(to)""", + relationship.getSource().getId(), + relationship.getDestination().getId(), + !StringUtils.isNullOrEmpty(relationship.getDescription()) ? relationship.getDescription() : "uses" + )); + } + } + } + +} \ No newline at end of file diff --git a/structurizr-neo4j/src/test/java/com/structurizr/Example.java b/structurizr-neo4j/src/test/java/com/structurizr/Example.java new file mode 100644 index 000000000..4ef9f5290 --- /dev/null +++ b/structurizr-neo4j/src/test/java/com/structurizr/Example.java @@ -0,0 +1,28 @@ +package com.structurizr; + +import com.structurizr.neo4j.SimpleLoader; +import com.structurizr.util.WorkspaceUtils; +import org.neo4j.driver.AuthTokens; +import org.neo4j.driver.Driver; +import org.neo4j.driver.GraphDatabase; +import org.neo4j.driver.Result; + +import java.io.File; + +public class Example { + + public static void main(String[] args) throws Exception { + Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("workspace.json")); + + try (Driver driver = GraphDatabase.driver("bolt://localhost:7687", AuthTokens.basic("neo4j", "password"))) { + try (var session = driver.session()) { + session.run("DROP DATABASE structurizr IF EXISTS"); + Result result = session.run("CREATE DATABASE structurizr"); + System.out.println(result.consume()); + } + + new SimpleLoader().load(workspace, driver, "structurizr"); + } + } + +} \ No newline at end of file From 2a8602dfe0d3b23c2eedd3c4406fa380670c0924 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 30 Aug 2024 10:57:21 +0100 Subject: [PATCH 553/717] Adds support for local theme files to be specified via `theme` (https://github.com/structurizr/java/issues/331). --- changelog.md | 1 + .../java/com/structurizr/view/ThemeUtils.java | 102 +++++++++++++----- .../com/structurizr/util/ThemeUtilsTests.java | 35 ++++++ .../src/test/resources/logo.png | Bin 0 -> 9262 bytes .../src/test/resources/theme.json | 12 +++ .../java/com/structurizr/view/Styles.java | 26 +++++ .../com/structurizr/view/StylesTests.java | 17 +++ .../structurizr/dsl/StructurizrDslParser.java | 4 +- .../java/com/structurizr/dsl/ThemeParser.java | 51 ++++++--- .../com/structurizr/dsl/ThemeParserTests.java | 61 ++++++++--- .../src/test/resources/themes/theme.json | 11 ++ 11 files changed, 262 insertions(+), 58 deletions(-) create mode 100644 structurizr-client/src/test/java/com/structurizr/util/ThemeUtilsTests.java create mode 100644 structurizr-client/src/test/resources/logo.png create mode 100644 structurizr-client/src/test/resources/theme.json create mode 100644 structurizr-dsl/src/test/resources/themes/theme.json diff --git a/changelog.md b/changelog.md index 16c2ac577..ed1e98deb 100644 --- a/changelog.md +++ b/changelog.md @@ -11,6 +11,7 @@ - structurizr-dsl: Adds an `!elements` keyword that can be used to find a set of elements via an expression. - structurizr-dsl: Adds a `!relationships` keyword that can be used to find a set of relationships via an expression. - structurizr-dsl: Adds a DSL wrapper around the `structurizr-component` component finder. +- structurizr-dsl: Adds support for local theme files to be specified via `theme` (https://github.com/structurizr/java/issues/331). ## 2.2.0 (2nd July 2024) diff --git a/structurizr-client/src/main/java/com/structurizr/view/ThemeUtils.java b/structurizr-client/src/main/java/com/structurizr/view/ThemeUtils.java index fff6ef9ed..283ec0f3a 100644 --- a/structurizr-client/src/main/java/com/structurizr/view/ThemeUtils.java +++ b/structurizr-client/src/main/java/com/structurizr/view/ThemeUtils.java @@ -6,17 +6,22 @@ import com.fasterxml.jackson.databind.SerializationFeature; import com.structurizr.Workspace; import com.structurizr.io.WorkspaceWriterException; +import com.structurizr.model.Relationship; +import com.structurizr.util.ImageUtils; import com.structurizr.util.StringUtils; +import com.structurizr.util.Url; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.config.ConnectionConfig; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; +import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.client5.http.impl.io.BasicHttpClientConnectionManager; import org.apache.hc.core5.http.io.entity.EntityUtils; import java.io.*; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.util.concurrent.TimeUnit; /** @@ -65,7 +70,7 @@ public static String toJson(Workspace workspace) throws Exception { } /** - * Loads (and inlines) the element and relationship styles from the themes defined in the workspace, into the workspace itself. + * Loads the element and relationship styles from the themes defined in the workspace, into the workspace itself. * This implementation simply copies the styles from all themes into the workspace. * This uses a default timeout value of 10000ms. * @@ -77,7 +82,7 @@ public static void loadThemes(Workspace workspace) throws Exception { } /** - * Loads (and inlines) the element and relationship styles from the themes defined in the workspace, into the workspace itself. + * Loads the element and relationship styles from the themes defined in the workspace, into the workspace itself. * This implementation simply copies the styles from all themes into the workspace. * * @param workspace a Workspace object @@ -85,32 +90,11 @@ public static void loadThemes(Workspace workspace) throws Exception { * @throws Exception if something goes wrong */ public static void loadThemes(Workspace workspace, int timeoutInMilliseconds) throws Exception { - for (String url : workspace.getViews().getConfiguration().getThemes()) { - ConnectionConfig connectionConfig = ConnectionConfig.custom() - .setConnectTimeout(timeoutInMilliseconds, TimeUnit.MILLISECONDS) - .setSocketTimeout(timeoutInMilliseconds, TimeUnit.MILLISECONDS) - .build(); - - BasicHttpClientConnectionManager cm = new BasicHttpClientConnectionManager(); - cm.setConnectionConfig(connectionConfig); - - CloseableHttpClient httpClient = HttpClientBuilder.create() - .useSystemProperties() - .setConnectionManager(cm) - .build(); - - HttpGet httpGet = new HttpGet(url); - - CloseableHttpResponse response = httpClient.execute(httpGet); - if (response.getCode() == HTTP_OK_STATUS) { - String json = EntityUtils.toString(response.getEntity()); - - ObjectMapper objectMapper = new ObjectMapper(); - objectMapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT); - objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - - Theme theme = objectMapper.readValue(json, Theme.class); - String baseUrl = url.substring(0, url.lastIndexOf('/') + 1); + for (String themeLocation : workspace.getViews().getConfiguration().getThemes()) { + if (Url.isUrl(themeLocation)) { + String json = loadFrom(themeLocation, timeoutInMilliseconds); + Theme theme = fromJson(json); + String baseUrl = themeLocation.substring(0, themeLocation.lastIndexOf('/') + 1); for (ElementStyle elementStyle : theme.getElements()) { String icon = elementStyle.getIcon(); @@ -128,9 +112,69 @@ public static void loadThemes(Workspace workspace, int timeoutInMilliseconds) th workspace.getViews().getConfiguration().getStyles().addStylesFromTheme(theme); } + } + } - httpClient.close(); + /** + * Inlines the element and relationship styles from the specified file, adding the styles into the workspace + * and overriding any properties already set. + * + * @param workspace the Workspace to load the theme into + * @param file a File object representing a theme (a JSON file) + * @throws Exception if something goes wrong + */ + public static void inlineTheme(Workspace workspace, File file) throws Exception { + String json = Files.readString(file.toPath()); + Theme theme = fromJson(json); + + for (ElementStyle elementStyle : theme.getElements()) { + String icon = elementStyle.getIcon(); + if (!StringUtils.isNullOrEmpty(icon)) { + if (icon.startsWith("http")) { + // okay, image served over HTTP + } else if (icon.startsWith("data:image")) { + // also okay, data URI + } else { + // convert the relative icon filename into a data URI + elementStyle.setIcon(ImageUtils.getImageAsDataUri(new File(file.getParentFile(), icon))); + } + } } + + workspace.getViews().getConfiguration().getStyles().inlineTheme(theme); + } + + private static String loadFrom(String url, int timeoutInMilliseconds) throws Exception { + ConnectionConfig connectionConfig = ConnectionConfig.custom() + .setConnectTimeout(timeoutInMilliseconds, TimeUnit.MILLISECONDS) + .setSocketTimeout(timeoutInMilliseconds, TimeUnit.MILLISECONDS) + .build(); + + BasicHttpClientConnectionManager cm = new BasicHttpClientConnectionManager(); + cm.setConnectionConfig(connectionConfig); + + try (CloseableHttpClient httpClient = HttpClientBuilder.create() + .useSystemProperties() + .setConnectionManager(cm) + .build()) { + + HttpGet httpGet = new HttpGet(url); + + CloseableHttpResponse response = httpClient.execute(httpGet); + if (response.getCode() == HTTP_OK_STATUS) { + return EntityUtils.toString(response.getEntity()); + } + } + + return ""; + } + + private static Theme fromJson(String json) throws Exception { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + + return objectMapper.readValue(json, Theme.class); } private static void write(Workspace workspace, Writer writer) throws Exception { diff --git a/structurizr-client/src/test/java/com/structurizr/util/ThemeUtilsTests.java b/structurizr-client/src/test/java/com/structurizr/util/ThemeUtilsTests.java new file mode 100644 index 000000000..03c6cfbcf --- /dev/null +++ b/structurizr-client/src/test/java/com/structurizr/util/ThemeUtilsTests.java @@ -0,0 +1,35 @@ +package com.structurizr.util; + +import com.structurizr.Workspace; +import com.structurizr.view.ThemeUtils; +import org.junit.jupiter.api.Test; + +import java.io.File; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class ThemeUtilsTests { + + @Test + void inlineTheme() throws Exception { + File themeFile = new File("src/test/resources/theme.json"); + + try { + Workspace theme = new Workspace("Theme", ""); + theme.getViews().getConfiguration().getStyles().addElementStyle("Tag").background("#ff0000").icon("logo.png"); + theme.getViews().getConfiguration().getStyles().addRelationshipStyle("Tag").color("#00ff00"); + ThemeUtils.toJson(theme, themeFile); + } catch (Exception e) { + throw new RuntimeException(e); + } + + Workspace workspace = new Workspace("Name", "Description"); + ThemeUtils.inlineTheme(workspace, themeFile); + + assertEquals(0, workspace.getViews().getConfiguration().getThemes().length); + assertEquals("#ff0000", workspace.getViews().getConfiguration().getStyles().getElementStyle("Tag").getBackground()); + assertEquals("data:image/png;base64,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", workspace.getViews().getConfiguration().getStyles().getElementStyle("Tag").getIcon()); + assertEquals("#00ff00", workspace.getViews().getConfiguration().getStyles().getRelationshipStyle("Tag").getColor()); + } + +} \ No newline at end of file diff --git a/structurizr-client/src/test/resources/logo.png b/structurizr-client/src/test/resources/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..763d19bf5c7d827bec4f6c31ff8e8660f09aab54 GIT binary patch literal 9262 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 lines, File dslFile, boolean fragment, boolean includeIn new DynamicViewRelationshipParser().parseUrl(getContext(DynamicViewRelationshipContext.class), tokens.withoutContextStartToken()); } else if (THEME_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ViewsDslContext.class) || inContext(StylesDslContext.class))) { - new ThemeParser().parseTheme(getContext(), tokens); + new ThemeParser().parseTheme(getContext(), dslFile, tokens); } else if (THEMES_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ViewsDslContext.class) || inContext(StylesDslContext.class))) { - new ThemeParser().parseThemes(getContext(), tokens); + new ThemeParser().parseThemes(getContext(), dslFile, tokens); } else if (TERMINOLOGY_TOKEN.equalsIgnoreCase(firstToken) && inContext(ViewsDslContext.class)) { startContext(new TerminologyDslContext()); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ThemeParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ThemeParser.java index 9cc5286af..c16c531c5 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ThemeParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ThemeParser.java @@ -1,41 +1,66 @@ package com.structurizr.dsl; +import com.structurizr.util.Url; +import com.structurizr.view.ThemeUtils; + +import java.io.File; + final class ThemeParser extends AbstractParser { + private static final String DEFAULT_THEME_NAME = "default"; private static final String DEFAULT_THEME_URL = "https://static.structurizr.com/themes/default/theme.json"; private final static int FIRST_THEME_INDEX = 1; - void parseTheme(DslContext context, Tokens tokens) { - // theme + void parseTheme(DslContext context, File dslFile, Tokens tokens) { + // theme if (tokens.hasMoreThan(FIRST_THEME_INDEX)) { - throw new RuntimeException("Too many tokens, expected: theme "); + throw new RuntimeException("Too many tokens, expected: theme "); } if (!tokens.includes(FIRST_THEME_INDEX)) { - throw new RuntimeException("Expected: theme "); + throw new RuntimeException("Expected: theme "); } - addTheme(context, tokens.get(FIRST_THEME_INDEX)); + addTheme(context, dslFile, tokens.get(FIRST_THEME_INDEX)); } - void parseThemes(DslContext context, Tokens tokens) { - // themes [url] ... [url] + void parseThemes(DslContext context, File dslFile, Tokens tokens) { + // themes [url|file] ... [url|file] if (!tokens.includes(FIRST_THEME_INDEX)) { - throw new RuntimeException("Expected: themes [url] ... [url]"); + throw new RuntimeException("Expected: themes [url|file] ... [url|file]"); } for (int i = FIRST_THEME_INDEX; i < tokens.size(); i++) { - addTheme(context, tokens.get(i)); + addTheme(context, dslFile, tokens.get(i)); } } - private void addTheme(DslContext context, String url) { - if ("default".equalsIgnoreCase(url)) { - url = DEFAULT_THEME_URL; + private void addTheme(DslContext context, File dslFile, String theme) { + if (DEFAULT_THEME_NAME.equalsIgnoreCase(theme)) { + theme = DEFAULT_THEME_URL; } - context.getWorkspace().getViews().getConfiguration().addTheme(url); + if (Url.isUrl(theme)) { + // this adds the theme to the list of theme URLs in the workspace + context.getWorkspace().getViews().getConfiguration().addTheme(theme); + } else { + // this inlines the file-based theme into the workspace + File file = new File(dslFile.getParentFile(), theme); + if (file.exists()) { + if (file.isFile()) { + try { + ThemeUtils.inlineTheme(context.getWorkspace(), file); + } catch (Exception e) { + throw new RuntimeException("Error loading theme from " + file.getAbsolutePath() + ": " + e.getMessage()); + } + } else { + throw new RuntimeException(file.getAbsolutePath() + " is not a file"); + } + } else { + throw new RuntimeException(file.getAbsolutePath() + " does not exist"); + } + } } } \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ThemeParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ThemeParserTests.java index 46463d603..3a3c9312b 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ThemeParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ThemeParserTests.java @@ -2,36 +2,37 @@ import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; +import java.io.File; + +import static org.junit.jupiter.api.Assertions.*; class ThemeParserTests extends AbstractTests { - private ThemeParser parser = new ThemeParser(); + private final ThemeParser parser = new ThemeParser(); @Test void test_parseTheme_ThrowsAnException_WhenThereAreTooManyTokens() { try { - parser.parseTheme(context(), tokens("theme", "url", "extra")); + parser.parseTheme(context(), null, tokens("theme", "url", "extra")); fail(); } catch (Exception e) { - assertEquals("Too many tokens, expected: theme ", e.getMessage()); + assertEquals("Too many tokens, expected: theme ", e.getMessage()); } } @Test void test_parseTheme_ThrowsAnException_WhenNoThemeIsSpecified() { try { - parser.parseTheme(context(), tokens("theme")); + parser.parseTheme(context(), null, tokens("theme")); fail(); } catch (Exception e) { - assertEquals("Expected: theme ", e.getMessage()); + assertEquals("Expected: theme ", e.getMessage()); } } @Test void test_parseTheme_AddsTheTheme_WhenAThemeIsSpecified() { - parser.parseTheme(context(), tokens("theme", "http://example.com/1")); + parser.parseTheme(context(), null, tokens("theme", "http://example.com/1")); assertEquals(1, workspace.getViews().getConfiguration().getThemes().length); assertEquals("http://example.com/1", workspace.getViews().getConfiguration().getThemes()[0]); @@ -39,7 +40,7 @@ void test_parseTheme_AddsTheTheme_WhenAThemeIsSpecified() { @Test void test_parseTheme_AddsTheTheme_WhenTheDefaultThemeIsSpecified() { - parser.parseTheme(context(), tokens("theme", "default")); + parser.parseTheme(context(), null, tokens("theme", "default")); assertEquals(1, workspace.getViews().getConfiguration().getThemes().length); assertEquals("https://static.structurizr.com/themes/default/theme.json", workspace.getViews().getConfiguration().getThemes()[0]); @@ -48,16 +49,16 @@ void test_parseTheme_AddsTheTheme_WhenTheDefaultThemeIsSpecified() { @Test void test_parseThemes_ThrowsAnException_WhenNoThemesAreSpecified() { try { - parser.parseThemes(context(), tokens("themes")); + parser.parseThemes(context(), null, tokens("themes")); fail(); } catch (Exception e) { - assertEquals("Expected: themes [url] ... [url]", e.getMessage()); + assertEquals("Expected: themes [url|file] ... [url|file]", e.getMessage()); } } @Test void test_parseThemes_AddsTheTheme_WhenOneThemeIsSpecified() { - parser.parseThemes(context(), tokens("themes", "http://example.com/1")); + parser.parseThemes(context(), null, tokens("themes", "http://example.com/1")); assertEquals(1, workspace.getViews().getConfiguration().getThemes().length); assertEquals("http://example.com/1", workspace.getViews().getConfiguration().getThemes()[0]); @@ -65,7 +66,7 @@ void test_parseThemes_AddsTheTheme_WhenOneThemeIsSpecified() { @Test void test_parseThemes_AddsTheThemes_WhenMultipleThemesAreSpecified() { - parser.parseThemes(context(), tokens("themes", "http://example.com/1", "http://example.com/2", "http://example.com/3")); + parser.parseThemes(context(), null, tokens("themes", "http://example.com/1", "http://example.com/2", "http://example.com/3")); assertEquals(3, workspace.getViews().getConfiguration().getThemes().length); assertEquals("http://example.com/1", workspace.getViews().getConfiguration().getThemes()[0]); @@ -75,10 +76,42 @@ void test_parseThemes_AddsTheThemes_WhenMultipleThemesAreSpecified() { @Test void test_parseThemes_AddsTheTheme_WhenTheDefaultThemeIsSpecified() { - parser.parseThemes(context(), tokens("themes", "default")); + parser.parseThemes(context(), null, tokens("themes", "default")); assertEquals(1, workspace.getViews().getConfiguration().getThemes().length); assertEquals("https://static.structurizr.com/themes/default/theme.json", workspace.getViews().getConfiguration().getThemes()[0]); } + @Test + void test_parseTheme_ThrowsAnException_WhenTheThemeFileDoesNotExist() { + File dslFile = new File("src/test/resources/themes/workspace.dsl"); + try { + parser.parseTheme(context(), dslFile, tokens("theme", "my-theme.json")); + fail(); + } catch (Exception e) { + assertTrue(e.getMessage().endsWith("/src/test/resources/themes/my-theme.json does not exist")); + } + } + + @Test + void test_parseTheme_ThrowsAnException_WhenTheThemeFileIsADirectory() { + File dslFile = new File("src/test/resources/workspace.dsl"); + try { + parser.parseTheme(context(), dslFile, tokens("theme", "themes")); + fail(); + } catch (Exception e) { + assertTrue(e.getMessage().endsWith("/src/test/resources/themes is not a file")); + } + } + + @Test + void test_parseTheme_InlinesTheTheme_WhenAThemeFileIsSpecified() { + File dslFile = new File("src/test/resources/themes/workspace.dsl"); + parser.parseTheme(context(), dslFile, tokens("theme", "theme.json")); + + assertEquals(0, workspace.getViews().getConfiguration().getThemes().length); + assertEquals("#ff0000", workspace.getViews().getConfiguration().getStyles().getElementStyle("Tag").getBackground()); + assertEquals("#00ff00", workspace.getViews().getConfiguration().getStyles().getRelationshipStyle("Tag").getColor()); + } + } \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/themes/theme.json b/structurizr-dsl/src/test/resources/themes/theme.json new file mode 100644 index 000000000..4f9774996 --- /dev/null +++ b/structurizr-dsl/src/test/resources/themes/theme.json @@ -0,0 +1,11 @@ +{ + "name" : "Theme", + "elements" : [ { + "tag" : "Tag", + "background" : "#ff0000" + } ], + "relationships" : [ { + "tag" : "Tag", + "color" : "#00ff00" + } ] +} \ No newline at end of file From a6e531fefe72bc212439a7a5483af48d654af184 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 1 Sep 2024 09:55:36 +0100 Subject: [PATCH 554/717] Adds support for icons to the Ilograph exporter (#332). --- .../structurizr/export/ilograph/IlographExporter.java | 11 +++++++++++ .../com/structurizr/export/ilograph/54915.ilograph | 8 ++++++++ ...aphWriterTests.java => IlographExporterTests.java} | 9 ++++----- 3 files changed, 23 insertions(+), 5 deletions(-) rename structurizr-export/src/test/java/com/structurizr/export/ilograph/{IlographWriterTests.java => IlographExporterTests.java} (90%) diff --git a/structurizr-export/src/main/java/com/structurizr/export/ilograph/IlographExporter.java b/structurizr-export/src/main/java/com/structurizr/export/ilograph/IlographExporter.java index b091d40dc..38e225582 100644 --- a/structurizr-export/src/main/java/com/structurizr/export/ilograph/IlographExporter.java +++ b/structurizr-export/src/main/java/com/structurizr/export/ilograph/IlographExporter.java @@ -16,6 +16,8 @@ */ public class IlographExporter extends AbstractWorkspaceExporter { + public static final String ILOGRAPH_ICON = "ilograph.icon"; + public WorkspaceExport export(Workspace workspace) { IndentingWriter writer = new IndentingWriter(); writer.writeLine("resources:"); @@ -216,6 +218,15 @@ private void writeElement(IndentingWriter writer, Workspace workspace, Element e writer.writeLine(String.format("backgroundColor: \"%s\"", elementStyle.getBackground())); } writer.writeLine(String.format("color: \"%s\"", elementStyle.getColor())); + + String icon = elementStyle.getProperties().get(ILOGRAPH_ICON); + if (StringUtils.isNullOrEmpty(icon)) { + icon = elementStyle.getIcon(); + } + if (!StringUtils.isNullOrEmpty(icon)) { + writer.writeLine(String.format("icon: \"%s\"", icon)); + } + writer.writeLine(); writer.outdent(); } diff --git a/structurizr-export/src/test/java/com/structurizr/export/ilograph/54915.ilograph b/structurizr-export/src/test/java/com/structurizr/export/ilograph/54915.ilograph index 9689909c5..13ee0a0bb 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/ilograph/54915.ilograph +++ b/structurizr-export/src/test/java/com/structurizr/export/ilograph/54915.ilograph @@ -27,6 +27,7 @@ resources: subtitle: "[Deployment Node]" backgroundColor: "#ffffff" color: "#232f3e" + icon: "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/AWS-Cloud_light-bg@4x.png" children: - id: "6" @@ -34,6 +35,7 @@ resources: subtitle: "[Deployment Node]" backgroundColor: "#ffffff" color: "#147eba" + icon: "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Region_light-bg@4x.png" children: - id: "12" @@ -41,6 +43,7 @@ resources: subtitle: "[Deployment Node]" backgroundColor: "#ffffff" color: "#3b48cc" + icon: "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Amazon-RDS_light-bg@4x.png" children: - id: "13" @@ -48,6 +51,7 @@ resources: subtitle: "[Deployment Node]" backgroundColor: "#ffffff" color: "#3b48cc" + icon: "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Amazon-RDS_MySQL_instance_light-bg@4x.png" children: - id: "14" @@ -62,6 +66,7 @@ resources: subtitle: "[Deployment Node]" backgroundColor: "#ffffff" color: "#cc2264" + icon: "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/AWS-Auto-Scaling_light-bg@4x.png" children: - id: "10" @@ -69,6 +74,7 @@ resources: subtitle: "[Deployment Node]" backgroundColor: "#ffffff" color: "#d86613" + icon: "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Amazon-EC2_light-bg@4x.png" children: - id: "11" @@ -84,6 +90,7 @@ resources: description: "Highly available and scalable cloud DNS service." backgroundColor: "#ffffff" color: "#693cc5" + icon: "AWS/Networking/Route-53.svg" - id: "8" name: "Elastic Load Balancer" @@ -91,6 +98,7 @@ resources: description: "Automatically distributes incoming application traffic." backgroundColor: "#ffffff" color: "#693cc5" + icon: "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Elastic-Load-Balancing_light-bg@4x.png" perspectives: - name: Static Structure diff --git a/structurizr-export/src/test/java/com/structurizr/export/ilograph/IlographWriterTests.java b/structurizr-export/src/test/java/com/structurizr/export/ilograph/IlographExporterTests.java similarity index 90% rename from structurizr-export/src/test/java/com/structurizr/export/ilograph/IlographWriterTests.java rename to structurizr-export/src/test/java/com/structurizr/export/ilograph/IlographExporterTests.java index 3cca4626d..346b687b2 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/ilograph/IlographWriterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/ilograph/IlographExporterTests.java @@ -2,21 +2,18 @@ import com.structurizr.Workspace; import com.structurizr.export.AbstractExporterTests; -import com.structurizr.export.Diagram; import com.structurizr.export.WorkspaceExport; -import com.structurizr.export.dot.DOTExporter; import com.structurizr.model.CustomElement; import com.structurizr.model.Model; import com.structurizr.util.WorkspaceUtils; -import com.structurizr.view.CustomView; import com.structurizr.view.ThemeUtils; import org.junit.jupiter.api.Test; import java.io.File; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; -public class IlographWriterTests extends AbstractExporterTests { +public class IlographExporterTests extends AbstractExporterTests { @Test public void test_BigBankPlcExample() throws Exception { @@ -31,6 +28,8 @@ public void test_BigBankPlcExample() throws Exception { @Test public void test_AmazonWebServicesExample() throws Exception { Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/structurizr-54915-workspace.json")); + workspace.getViews().getConfiguration().getStyles().addElementStyle("Amazon Web Services - Route 53").addProperty(IlographExporter.ILOGRAPH_ICON, "AWS/Networking/Route-53.svg"); + ThemeUtils.loadThemes(workspace); IlographExporter ilographExporter = new IlographExporter(); WorkspaceExport export = ilographExporter.export(workspace); From 5de236b248d34211296d26b332235f0aaa362072 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 1 Sep 2024 09:55:46 +0100 Subject: [PATCH 555/717] . --- changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.md b/changelog.md index ed1e98deb..355ec1b0a 100644 --- a/changelog.md +++ b/changelog.md @@ -12,6 +12,7 @@ - structurizr-dsl: Adds a `!relationships` keyword that can be used to find a set of relationships via an expression. - structurizr-dsl: Adds a DSL wrapper around the `structurizr-component` component finder. - structurizr-dsl: Adds support for local theme files to be specified via `theme` (https://github.com/structurizr/java/issues/331). +- structurizr-export: Adds support for icons to the Ilograph exporter. ## 2.2.0 (2nd July 2024) From 2a4eea01261eb2b360a1e10e899ccc2f6088ea2d Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 2 Sep 2024 10:45:28 +0100 Subject: [PATCH 556/717] Removes empty line. --- .../java/com/structurizr/export/ilograph/IlographExporter.java | 1 - .../src/test/java/com/structurizr/export/ilograph/36141.ilograph | 1 - .../src/test/java/com/structurizr/export/ilograph/54915.ilograph | 1 - .../com/structurizr/export/ilograph/IlographExporterTests.java | 1 - 4 files changed, 4 deletions(-) diff --git a/structurizr-export/src/main/java/com/structurizr/export/ilograph/IlographExporter.java b/structurizr-export/src/main/java/com/structurizr/export/ilograph/IlographExporter.java index 38e225582..420631d8d 100644 --- a/structurizr-export/src/main/java/com/structurizr/export/ilograph/IlographExporter.java +++ b/structurizr-export/src/main/java/com/structurizr/export/ilograph/IlographExporter.java @@ -21,7 +21,6 @@ public class IlographExporter extends AbstractWorkspaceExporter { public WorkspaceExport export(Workspace workspace) { IndentingWriter writer = new IndentingWriter(); writer.writeLine("resources:"); - writer.writeLine(); writer.indent(); Model model = workspace.getModel(); diff --git a/structurizr-export/src/test/java/com/structurizr/export/ilograph/36141.ilograph b/structurizr-export/src/test/java/com/structurizr/export/ilograph/36141.ilograph index 31f0a6f7e..6a416a4d3 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/ilograph/36141.ilograph +++ b/structurizr-export/src/test/java/com/structurizr/export/ilograph/36141.ilograph @@ -1,5 +1,4 @@ resources: - - id: "1" name: "Personal Banking Customer" subtitle: "[Person]" diff --git a/structurizr-export/src/test/java/com/structurizr/export/ilograph/54915.ilograph b/structurizr-export/src/test/java/com/structurizr/export/ilograph/54915.ilograph index 13ee0a0bb..411ac3c5e 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/ilograph/54915.ilograph +++ b/structurizr-export/src/test/java/com/structurizr/export/ilograph/54915.ilograph @@ -1,5 +1,4 @@ resources: - - id: "1" name: "Spring PetClinic" subtitle: "[Software System]" diff --git a/structurizr-export/src/test/java/com/structurizr/export/ilograph/IlographExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/ilograph/IlographExporterTests.java index 346b687b2..323d74200 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/ilograph/IlographExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/ilograph/IlographExporterTests.java @@ -49,7 +49,6 @@ public void test_renderCustomElements() throws Exception { WorkspaceExport export = new IlographExporter().export(workspace); assertEquals("resources:\n" + - "\n" + " - id: \"1\"\n" + " name: \"A\"\n" + " subtitle: \"\"\n" + From 8dbdd7897bac3aa77c18f29cb9132fab49f4dcd6 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 2 Sep 2024 10:47:31 +0100 Subject: [PATCH 557/717] Adds support for imports to the Ilograph exporter (#332). --- changelog.md | 3 +- .../export/ilograph/IlographExporter.java | 31 +++++++++++++++++++ .../ilograph/IlographExporterTests.java | 23 ++++++++++++-- 3 files changed, 54 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index 355ec1b0a..695191c84 100644 --- a/changelog.md +++ b/changelog.md @@ -12,7 +12,8 @@ - structurizr-dsl: Adds a `!relationships` keyword that can be used to find a set of relationships via an expression. - structurizr-dsl: Adds a DSL wrapper around the `structurizr-component` component finder. - structurizr-dsl: Adds support for local theme files to be specified via `theme` (https://github.com/structurizr/java/issues/331). -- structurizr-export: Adds support for icons to the Ilograph exporter. +- structurizr-export: Adds support for icons to the Ilograph exporter (https://github.com/structurizr/java/issues/332). +- structurizr-export: Adds support for imports to the Ilograph exporter (https://github.com/structurizr/java/issues/332). ## 2.2.0 (2nd July 2024) diff --git a/structurizr-export/src/main/java/com/structurizr/export/ilograph/IlographExporter.java b/structurizr-export/src/main/java/com/structurizr/export/ilograph/IlographExporter.java index 420631d8d..06aefe67d 100644 --- a/structurizr-export/src/main/java/com/structurizr/export/ilograph/IlographExporter.java +++ b/structurizr-export/src/main/java/com/structurizr/export/ilograph/IlographExporter.java @@ -16,10 +16,41 @@ */ public class IlographExporter extends AbstractWorkspaceExporter { + public static final String ILOGRAPH_IMPORTS = "ilograph.imports"; public static final String ILOGRAPH_ICON = "ilograph.icon"; public WorkspaceExport export(Workspace workspace) { IndentingWriter writer = new IndentingWriter(); + + // Ilograph imports can be specified in the form: + // + // AWS:ilograph/aws + // + // Which gets exported as: + // + // imports: + // - from: ilograph/aws + // namespace: AWS + String commaSeparatedListOfImports = workspace.getProperties().get(ILOGRAPH_IMPORTS); + if (!StringUtils.isNullOrEmpty(commaSeparatedListOfImports)) { + writer.writeLine("imports:"); + + String[] ilographImports = commaSeparatedListOfImports.split(","); + for (String ilographImport : ilographImports) { + String[] parts = ilographImport.split(":"); + if (parts.length == 2) { + String namespace = parts[0]; + String from = parts[1]; + + writer.writeLine("- from: " + from); + writer.indent(); + writer.writeLine("namespace: " + namespace); + writer.outdent(); + } + } + writer.writeLine(); + } + writer.writeLine("resources:"); writer.indent(); diff --git a/structurizr-export/src/test/java/com/structurizr/export/ilograph/IlographExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/ilograph/IlographExporterTests.java index 323d74200..a42da19bb 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/ilograph/IlographExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/ilograph/IlographExporterTests.java @@ -26,7 +26,7 @@ public void test_BigBankPlcExample() throws Exception { } @Test - public void test_AmazonWebServicesExample() throws Exception { + void test_AmazonWebServicesExample() throws Exception { Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/structurizr-54915-workspace.json")); workspace.getViews().getConfiguration().getStyles().addElementStyle("Amazon Web Services - Route 53").addProperty(IlographExporter.ILOGRAPH_ICON, "AWS/Networking/Route-53.svg"); @@ -39,7 +39,7 @@ public void test_AmazonWebServicesExample() throws Exception { } @Test - public void test_renderCustomElements() throws Exception { + void test_renderCustomElements() { Workspace workspace = new Workspace("Name", "Description"); Model model = workspace.getModel(); @@ -71,4 +71,23 @@ public void test_renderCustomElements() throws Exception { " color: \"#707070\"\n", export.getDefinition()); } + @Test + void test_imports() { + Workspace workspace = new Workspace("Name", "Description"); + workspace.addProperty(IlographExporter.ILOGRAPH_IMPORTS, "NAMESPACE1:path1,NAMESPACE2:path2"); + + WorkspaceExport export = new IlographExporter().export(workspace); + assertEquals(""" +imports: +- from: path1 + namespace: NAMESPACE1 +- from: path2 + namespace: NAMESPACE2 + +resources: +perspectives: + - name: Static Structure + relations:""", export.getDefinition()); + } + } \ No newline at end of file From 30da167b7cf720d062cbc2391fc9468e412c19d9 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 9 Sep 2024 14:36:08 +0100 Subject: [PATCH 558/717] Adds support for workspace branches (on-premises installation only). --- .../structurizr/api/WorkspaceApiClient.java | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/structurizr-client/src/main/java/com/structurizr/api/WorkspaceApiClient.java b/structurizr-client/src/main/java/com/structurizr/api/WorkspaceApiClient.java index d12adf3a0..159a8fffd 100644 --- a/structurizr-client/src/main/java/com/structurizr/api/WorkspaceApiClient.java +++ b/structurizr-client/src/main/java/com/structurizr/api/WorkspaceApiClient.java @@ -32,7 +32,6 @@ import java.text.SimpleDateFormat; import java.util.Base64; import java.util.Date; -import java.util.Properties; /** * A client for the Structurizr workspace API that allows you to get and put Structurizr workspaces in a JSON format. @@ -45,6 +44,7 @@ public class WorkspaceApiClient extends AbstractApiClient { private String apiKey; private String apiSecret; + private String branch = ""; private EncryptionStrategy encryptionStrategy; @@ -111,6 +111,14 @@ protected void setApiSecret(String apiSecret) { this.apiSecret = apiSecret; } + public String getBranch() { + return branch; + } + + public void setBranch(String branch) { + this.branch = branch; + } + /** * Gets the location where a copy of the workspace is archived when it is retrieved from the server. * @@ -224,7 +232,14 @@ public Workspace getWorkspace(long workspaceId) throws StructurizrClientExceptio try (CloseableHttpClient httpClient = HttpClients.createSystem()) { log.info("Getting workspace with ID " + workspaceId); - HttpGet httpGet = new HttpGet(url + WORKSPACE_PATH + "/" + workspaceId); + + HttpGet httpGet; + if (StringUtils.isNullOrEmpty(branch)) { + httpGet = new HttpGet(url + WORKSPACE_PATH + "/" + workspaceId); + } else { + httpGet = new HttpGet(url + WORKSPACE_PATH + "/" + workspaceId + "/branch/" + branch); + } + addHeaders(httpGet, "", ""); debugRequest(httpGet, null); @@ -296,7 +311,12 @@ public void putWorkspace(long workspaceId, Workspace workspace) throws Structuri workspace.setLastModifiedAgent(agent); workspace.setLastModifiedUser(getUser()); - HttpPut httpPut = new HttpPut(url + WORKSPACE_PATH + "/" + workspaceId); + HttpPut httpPut; + if (StringUtils.isNullOrEmpty(branch)) { + httpPut = new HttpPut(url + WORKSPACE_PATH + "/" + workspaceId); + } else { + httpPut = new HttpPut(url + WORKSPACE_PATH + "/" + workspaceId + "/branch/" + branch); + } StringWriter stringWriter = new StringWriter(); if (encryptionStrategy == null) { From be6b7205c5af76c0596112fdbbcb82eec24dd48d Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 9 Sep 2024 14:38:03 +0100 Subject: [PATCH 559/717] Update changelog. --- build.gradle | 2 +- changelog.md | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 1f96c1f5e..127bc7d21 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '2.3.0' + version = '3.0.0' repositories { mavenCentral() diff --git a/changelog.md b/changelog.md index 695191c84..51173a070 100644 --- a/changelog.md +++ b/changelog.md @@ -1,7 +1,8 @@ # Changelog -## unreleased +## 3.0.0 (unreleased) +- structurizr-client: Adds support for [workspace branches](https://docs.structurizr.com/onpremises/workspace-branches) (on-premises installation only). - structurizr-core: Adds name-value properties to dynamic view relationship views (https://github.com/structurizr/java/issues/316). - structurizr-component: Initial rewrite of the original `structurizr-analysis` library - provides a way to automatically find components in a Java codebase. - structurizr-dsl: Adds name-value properties to dynamic view relationship views. From 3fdab62d530cf4bd3d566ebbb6bf48c98a4f557b Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 9 Sep 2024 14:43:39 +0100 Subject: [PATCH 560/717] Removes deprecated `!constant` keyword. --- changelog.md | 1 + .../com/structurizr/dsl/StructurizrDslParser.java | 8 +------- .../src/test/java/com/structurizr/dsl/DslTests.java | 13 +++++++++++++ structurizr-dsl/src/test/resources/dsl/constant.dsl | 5 +++++ 4 files changed, 20 insertions(+), 7 deletions(-) create mode 100644 structurizr-dsl/src/test/resources/dsl/constant.dsl diff --git a/changelog.md b/changelog.md index 51173a070..e5aec5043 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,7 @@ - structurizr-client: Adds support for [workspace branches](https://docs.structurizr.com/onpremises/workspace-branches) (on-premises installation only). - structurizr-core: Adds name-value properties to dynamic view relationship views (https://github.com/structurizr/java/issues/316). - structurizr-component: Initial rewrite of the original `structurizr-analysis` library - provides a way to automatically find components in a Java codebase. +- structurizr-dsl: Removes deprecated `!constant` keyword. - structurizr-dsl: Adds name-value properties to dynamic view relationship views. - structurizr-dsl: Fixes https://github.com/structurizr/java/issues/312 (!include doesn't work with files encoded as UTF-8 BOM). - structurizr-dsl: Adds a way to explicitly specify the order of relationships in dynamic views. diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index 4a1be28fc..c0ec6c095 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -950,13 +950,7 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn } } else if (CONSTANT_TOKEN.equalsIgnoreCase(firstToken)) { - log.warn("!constant has been deprecated and will be removed in a future release - please use !const or !var instead"); - NameValuePair nameValuePair = new NameValueParser().parseConstant(tokens); - - if (constantsAndVariables.containsKey(nameValuePair.getName())) { - log.warn("A constant \"" + nameValuePair.getName() + "\" already exists"); - } - constantsAndVariables.put(nameValuePair.getName(), nameValuePair); + throw new RuntimeException("!constant was previously deprecated, and has now been removed - please use !const or !var instead"); } else if (CONST_TOKEN.equalsIgnoreCase(firstToken)) { NameValuePair nameValuePair = new NameValueParser().parseConstant(tokens); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index f3db3bb16..b5bc86418 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -1096,6 +1096,19 @@ void test_Enterprise() { } } + @Test + void test_Constant() { + File dslFile = new File("src/test/resources/dsl/constant.dsl"); + + try { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(dslFile); + fail(); + } catch (StructurizrDslParserException e) { + assertEquals("!constant was previously deprecated, and has now been removed - please use !const or !var instead at line 3 of " + dslFile.getAbsolutePath() + ": !constant NAME VALUE", e.getMessage()); + } + } + @Test void test_UnbalancedCurlyBraces() { try { diff --git a/structurizr-dsl/src/test/resources/dsl/constant.dsl b/structurizr-dsl/src/test/resources/dsl/constant.dsl new file mode 100644 index 000000000..714ce0d27 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/constant.dsl @@ -0,0 +1,5 @@ +workspace { + + !constant NAME VALUE + +} \ No newline at end of file From 43ec4c8c64045d19d9246a482c61bfbf395a1abf Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 11 Sep 2024 09:55:25 +0100 Subject: [PATCH 561/717] Better error messages for image views. --- .../dsl/ImageViewContentParser.java | 8 +++ .../java/com/structurizr/dsl/DslTests.java | 14 ++++ .../dsl/ImageViewContentParserTests.java | 64 +++++++++++++++++++ .../src/test/resources/dsl/image-view.dsl | 9 +++ 4 files changed, 95 insertions(+) create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewContentParserTests.java create mode 100644 structurizr-dsl/src/test/resources/dsl/image-view.dsl diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewContentParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewContentParser.java index 424a0b53f..f59f5fcb5 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewContentParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewContentParser.java @@ -52,6 +52,8 @@ void parsePlantUML(ImageViewDslContext context, File dslFile, Tokens tokens) { if (!restricted) { File file = new File(dslFile.getParentFile(), source); new PlantUMLImporter().importDiagram(context.getView(), file); + } else { + throw new RuntimeException("PlantUML source must be specified as a URL when running in restricted mode"); } } } catch (Exception e) { @@ -84,6 +86,8 @@ void parseMermaid(ImageViewDslContext context, File dslFile, Tokens tokens) { if (!restricted) { File file = new File(dslFile.getParentFile(), source); new MermaidImporter().importDiagram(context.getView(), file); + } else { + throw new RuntimeException("Mermaid source must be specified as a URL when running in restricted mode"); } } } catch (Exception e) { @@ -117,6 +121,8 @@ void parseKroki(ImageViewDslContext context, File dslFile, Tokens tokens) { if (!restricted) { File file = new File(dslFile.getParentFile(), source); new KrokiImporter().importDiagram(context.getView(), format, file); + } else { + throw new RuntimeException("Kroki source must be specified as a URL when running in restricted mode"); } } } catch (Exception e) { @@ -149,6 +155,8 @@ void parseImage(ImageViewDslContext context, File dslFile, Tokens tokens) { File file = new File(dslFile.getParentFile(), source); context.getView().setContent(ImageUtils.getImageAsDataUri(file)); context.getView().setTitle(file.getName()); + } else { + throw new RuntimeException("Images must be specified as a URL when running in restricted mode"); } } } catch (Exception e) { diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index b5bc86418..5e3f112b9 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -1254,4 +1254,18 @@ void test_bulkOperations() throws Exception { parser.parse(dslFile); } + @Test + void test_ImageView_WhenParserIsInRestrictedMode() { + File dslFile = new File("src/test/resources/dsl/image-view.dsl"); + + try { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.setRestricted(true); + parser.parse(dslFile); + fail(); + } catch (StructurizrDslParserException e) { + assertEquals("Images must be specified as a URL when running in restricted mode at line 5 of " + dslFile.getAbsolutePath() + ": image image.png", e.getMessage()); + } + } + } \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewContentParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewContentParserTests.java new file mode 100644 index 000000000..4e693973e --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewContentParserTests.java @@ -0,0 +1,64 @@ +package com.structurizr.dsl; + +import com.structurizr.view.ImageView; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +class ImageViewContentParserTests extends AbstractTests { + + private ImageViewContentParser parser; + private ImageView imageView; + + @BeforeEach + void setUp() { + imageView = workspace.getViews().createImageView("key"); + } + + @Test + void test_parsePlantUML_ThrowsAnException_WhenUsingAFileNameInRestrictedMode() { + try { + parser = new ImageViewContentParser(true); + parser.parsePlantUML(new ImageViewDslContext(imageView), null, tokens("plantuml", "image.puml")); + fail(); + } catch (Exception e) { + assertEquals("PlantUML source must be specified as a URL when running in restricted mode", e.getMessage()); + } + } + + @Test + void test_parseMermaid_ThrowsAnException_WhenUsingAFileNameInRestrictedMode() { + try { + parser = new ImageViewContentParser(true); + parser.parseMermaid(new ImageViewDslContext(imageView), null, tokens("mermaid", "image.puml")); + fail(); + } catch (Exception e) { + assertEquals("Mermaid source must be specified as a URL when running in restricted mode", e.getMessage()); + } + } + + @Test + void test_parseKroki_ThrowsAnException_WhenUsingAFileNameInRestrictedMode() { + try { + parser = new ImageViewContentParser(true); + parser.parseKroki(new ImageViewDslContext(imageView), null, tokens("kroki", "plantuml", "image.puml")); + fail(); + } catch (Exception e) { + assertEquals("Kroki source must be specified as a URL when running in restricted mode", e.getMessage()); + } + } + + @Test + void test_parseImage_ThrowsAnException_WhenUsingAFileNameInRestrictedMode() { + try { + parser = new ImageViewContentParser(true); + parser.parseImage(new ImageViewDslContext(imageView), null, tokens("image", "image.png")); + fail(); + } catch (Exception e) { + assertEquals("Images must be specified as a URL when running in restricted mode", e.getMessage()); + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/image-view.dsl b/structurizr-dsl/src/test/resources/dsl/image-view.dsl new file mode 100644 index 000000000..27633c053 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/image-view.dsl @@ -0,0 +1,9 @@ +workspace { + + views { + image * "Image" { + image image.png + } + } + +} \ No newline at end of file From 9c5da2cbbec024c76feb39a4972ad9630a51346a Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 11 Sep 2024 09:55:45 +0100 Subject: [PATCH 562/717] Removes revision property. --- .../encryption/EncryptedWorkspace.java | 1 - .../com/structurizr/AbstractWorkspace.java | 19 ------------------- 2 files changed, 20 deletions(-) diff --git a/structurizr-client/src/main/java/com/structurizr/encryption/EncryptedWorkspace.java b/structurizr-client/src/main/java/com/structurizr/encryption/EncryptedWorkspace.java index ba89d409e..7a62ebb94 100644 --- a/structurizr-client/src/main/java/com/structurizr/encryption/EncryptedWorkspace.java +++ b/structurizr-client/src/main/java/com/structurizr/encryption/EncryptedWorkspace.java @@ -55,7 +55,6 @@ private void init(Workspace workspace, String plaintext, EncryptionStrategy encr setName(workspace.getName()); setDescription(workspace.getDescription()); setVersion(workspace.getVersion()); - setRevision(workspace.getRevision()); setLastModifiedUser(workspace.getLastModifiedUser()); setLastModifiedAgent(workspace.getLastModifiedAgent()); diff --git a/structurizr-core/src/main/java/com/structurizr/AbstractWorkspace.java b/structurizr-core/src/main/java/com/structurizr/AbstractWorkspace.java index 6bb57bbcd..755cfa1bb 100644 --- a/structurizr-core/src/main/java/com/structurizr/AbstractWorkspace.java +++ b/structurizr-core/src/main/java/com/structurizr/AbstractWorkspace.java @@ -17,7 +17,6 @@ public abstract class AbstractWorkspace implements PropertyHolder { private String name; private String description; private String version; - private Long revision; private Date lastModifiedDate; private String lastModifiedUser; private String lastModifiedAgent; @@ -111,24 +110,6 @@ public void setVersion(String version) { } - /** - * Gets the revision number of this workspace. - * - * @return the revision number - */ - public Long getRevision() { - return revision; - } - - /** - * Sets the revision number of this workspace. - * - * @param revision a number - */ - public void setRevision(Long revision) { - this.revision = revision; - } - /** * Gets the last modified date of this workspace. * From aef27cc876bb8c4c88e2d8c5ce151e56c90a1d94 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 11 Sep 2024 12:02:22 +0100 Subject: [PATCH 563/717] Workspace branches are also supported by the upcoming cloud service release (paid feature). --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index e5aec5043..61fcddab9 100644 --- a/changelog.md +++ b/changelog.md @@ -2,7 +2,7 @@ ## 3.0.0 (unreleased) -- structurizr-client: Adds support for [workspace branches](https://docs.structurizr.com/onpremises/workspace-branches) (on-premises installation only). +- structurizr-client: Adds support to get/put workspace branches on the [cloud service](https://docs.structurizr.com/cloud/workspace-branches) and [on-premises installation](https://docs.structurizr.com/onpremises/workspace-branches). - structurizr-core: Adds name-value properties to dynamic view relationship views (https://github.com/structurizr/java/issues/316). - structurizr-component: Initial rewrite of the original `structurizr-analysis` library - provides a way to automatically find components in a Java codebase. - structurizr-dsl: Removes deprecated `!constant` keyword. From 87b6ff5b0e9427040c511d2fd4f389289aebc6b6 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 11 Sep 2024 12:02:56 +0100 Subject: [PATCH 564/717] Adds an example and test for the new dynamic view parallel sequences syntax. --- .../java/com/structurizr/dsl/DslTests.java | 15 ++++++++++ .../src/test/resources/dsl/parallel3.dsl | 28 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 structurizr-dsl/src/test/resources/dsl/parallel3.dsl diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index 5e3f112b9..1d3665732 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -555,6 +555,21 @@ void test_parallel2() throws Exception { assertEquals("3", relationships.get(3).getOrder()); } + @Test + void test_parallel3() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/parallel3.dsl")); + + assertFalse(parser.getWorkspace().isEmpty()); + DynamicView view = parser.getWorkspace().getViews().getDynamicViews().iterator().next(); + List relationships = new ArrayList<>(view.getRelationships()); + assertEquals(4, relationships.size()); + assertEquals("1", relationships.get(0).getOrder()); + assertEquals("2", relationships.get(1).getOrder()); + assertEquals("2", relationships.get(2).getOrder()); + assertEquals("3", relationships.get(3).getOrder()); + } + @Test void test_groups() throws Exception { StructurizrDslParser parser = new StructurizrDslParser(); diff --git a/structurizr-dsl/src/test/resources/dsl/parallel3.dsl b/structurizr-dsl/src/test/resources/dsl/parallel3.dsl new file mode 100644 index 000000000..0fafca38e --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/parallel3.dsl @@ -0,0 +1,28 @@ +workspace { + + model { + a = softwareSystem "A" + b = softwareSystem "B" + c = softwareSystem "C" + d = softwareSystem "D" + e = softwareSystem "E" + + a -> b + b -> c + b -> d + b -> e + } + + views { + + dynamic * { + 1: a -> b "Makes a request to" + 2: b -> c "Gets data from" + 2: b -> d "Gets data from" + 3: b -> e "Sends data to" + + autoLayout + } + } + +} \ No newline at end of file From 09d7f89c1ac16ffe4e49e0bb5984456e3551ed1e Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 11 Sep 2024 13:04:11 +0100 Subject: [PATCH 565/717] . --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 61fcddab9..0af4373a5 100644 --- a/changelog.md +++ b/changelog.md @@ -9,7 +9,7 @@ - structurizr-dsl: Adds name-value properties to dynamic view relationship views. - structurizr-dsl: Fixes https://github.com/structurizr/java/issues/312 (!include doesn't work with files encoded as UTF-8 BOM). - structurizr-dsl: Adds a way to explicitly specify the order of relationships in dynamic views. -- structurizr-dsl: Adds support for element technology expressions (e.g. "element.technology==Java"). +- structurizr-dsl: Adds support for element technology expressions (e.g. `element.technology==Java` and `element.technology!=Java`). - structurizr-dsl: Adds an `!elements` keyword that can be used to find a set of elements via an expression. - structurizr-dsl: Adds a `!relationships` keyword that can be used to find a set of relationships via an expression. - structurizr-dsl: Adds a DSL wrapper around the `structurizr-component` component finder. From becf089d56d5d8cee34f1329c4df79dcad982338 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 13 Sep 2024 18:43:34 +0100 Subject: [PATCH 566/717] A few tweaks to the component finder strategy, and how it's exposed via the DSL. --- .../component/ComponentFinderStrategy.java | 8 +- .../ComponentFinderStrategyBuilder.java | 77 +++++++++++- .../component/matcher/RegexTypeMatcher.java | 2 +- .../ComponentFinderStrategyBuilderTests.java | 119 +++++++++++++++++- .../dsl/ComponentFinderStrategyParser.java | 95 ++++++++------ .../ComponentFinderStrategyParserTests.java | 70 ++++++++--- .../dsl/spring-petclinic/workspace.dsl | 2 +- 7 files changed, 305 insertions(+), 68 deletions(-) diff --git a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategy.java index 91ca7d1e4..3d5886f2c 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategy.java +++ b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategy.java @@ -20,7 +20,7 @@ * * Use the {@link ComponentFinderStrategyBuilder} to create an instance of this class. */ -class ComponentFinderStrategy { +public final class ComponentFinderStrategy { private final String technology; private final TypeMatcher typeMatcher; @@ -66,8 +66,12 @@ void visit(Component component) { @Override public String toString() { return "ComponentFinderStrategy{" + - "typeMatcher=" + typeMatcher + + "technology=" + (technology == null ? null : "'" + technology + "'") + + ", typeMatcher=" + typeMatcher + ", typeFilter=" + typeFilter + + ", supportingTypesStrategy=" + supportingTypesStrategy + + ", namingStrategy=" + namingStrategy + + ", componentVisitor=" + componentVisitor + '}'; } diff --git a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategyBuilder.java b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategyBuilder.java index 88adf2efe..a8dcbb9f2 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategyBuilder.java +++ b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategyBuilder.java @@ -9,6 +9,7 @@ import com.structurizr.component.supporting.SupportingTypesStrategy; import com.structurizr.component.visitor.ComponentVisitor; import com.structurizr.component.visitor.DefaultComponentVisitor; +import com.structurizr.util.StringUtils; /** * Provides a way to create a {@link ComponentFinderStrategy} instance. @@ -17,45 +18,93 @@ public final class ComponentFinderStrategyBuilder { private String technology; private TypeMatcher typeMatcher; - private TypeFilter typeFilter = new DefaultTypeFilter(); - private SupportingTypesStrategy supportingTypesStrategy = new DefaultSupportingTypesStrategy(); - private NamingStrategy namingStrategy = new DefaultNamingStrategy(); - private ComponentVisitor componentVisitor = new DefaultComponentVisitor(); + private TypeFilter typeFilter; + private SupportingTypesStrategy supportingTypesStrategy; + private NamingStrategy namingStrategy; + private ComponentVisitor componentVisitor; public ComponentFinderStrategyBuilder() { } public ComponentFinderStrategyBuilder matchedBy(TypeMatcher typeMatcher) { + if (typeMatcher == null) { + throw new IllegalArgumentException("A type matcher must be provided"); + } + + if (this.typeMatcher != null) { + throw new IllegalArgumentException("A type matcher has already been configured"); + } + this.typeMatcher = typeMatcher; return this; } public ComponentFinderStrategyBuilder filteredBy(TypeFilter typeFilter) { + if (typeFilter == null) { + throw new IllegalArgumentException("A type filter must be provided"); + } + + if (this.typeFilter != null) { + throw new IllegalArgumentException("A type filter has already been configured"); + } + this.typeFilter = typeFilter; return this; } public ComponentFinderStrategyBuilder supportedBy(SupportingTypesStrategy supportingTypesStrategy) { + if (supportingTypesStrategy == null) { + throw new IllegalArgumentException("A supporting types strategy must be provided"); + } + + if (this.supportingTypesStrategy != null) { + throw new IllegalArgumentException("A supporting types strategy has already been configured"); + } + this.supportingTypesStrategy = supportingTypesStrategy; return this; } public ComponentFinderStrategyBuilder namedBy(NamingStrategy namingStrategy) { + if (namingStrategy == null) { + throw new IllegalArgumentException("A naming strategy must be provided"); + } + + if (this.namingStrategy != null) { + throw new IllegalArgumentException("A naming strategy has already been configured"); + } + this.namingStrategy = namingStrategy; return this; } public ComponentFinderStrategyBuilder asTechnology(String technology) { + if (StringUtils.isNullOrEmpty(technology)) { + throw new IllegalArgumentException("A technology must be provided"); + } + + if (!StringUtils.isNullOrEmpty(this.technology)) { + throw new IllegalArgumentException("A technology has already been configured"); + } + this.technology = technology; return this; } public ComponentFinderStrategyBuilder forEach(ComponentVisitor componentVisitor) { + if (componentVisitor == null) { + throw new IllegalArgumentException("A component visitor must be provided"); + } + + if (this.componentVisitor != null) { + throw new IllegalArgumentException("A component visitor has already been configured"); + } + this.componentVisitor = componentVisitor; return this; @@ -63,7 +112,23 @@ public ComponentFinderStrategyBuilder forEach(ComponentVisitor componentVisitor) public ComponentFinderStrategy build() { if (typeMatcher == null) { - throw new RuntimeException("A type matcher must be specified"); + throw new RuntimeException("A type matcher must be provided"); + } + + if (typeFilter == null) { + typeFilter = new DefaultTypeFilter(); + } + + if (supportingTypesStrategy == null) { + supportingTypesStrategy = new DefaultSupportingTypesStrategy(); + } + + if (namingStrategy == null) { + namingStrategy = new DefaultNamingStrategy(); + } + + if (componentVisitor == null) { + componentVisitor = new DefaultComponentVisitor(); } return new ComponentFinderStrategy(technology, typeMatcher, typeFilter, supportingTypesStrategy, namingStrategy, componentVisitor); @@ -72,7 +137,7 @@ public ComponentFinderStrategy build() { @Override public String toString() { return "ComponentFinderStrategyBuilder{" + - "technology='" + technology + '\'' + + "technology=" + (technology == null ? null : "'" + technology + "'") + ", typeMatcher=" + typeMatcher + ", typeFilter=" + typeFilter + ", supportingTypesStrategy=" + supportingTypesStrategy + diff --git a/structurizr-component/src/main/java/com/structurizr/component/matcher/RegexTypeMatcher.java b/structurizr-component/src/main/java/com/structurizr/component/matcher/RegexTypeMatcher.java index d55583a56..12bc4a586 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/matcher/RegexTypeMatcher.java +++ b/structurizr-component/src/main/java/com/structurizr/component/matcher/RegexTypeMatcher.java @@ -32,7 +32,7 @@ public boolean matches(Type type) { @Override public String toString() { return "RegexTypeMatcher{" + - "regex=" + regex + + "regex='" + regex + "'" + '}'; } diff --git a/structurizr-component/src/test/java/com/structurizr/component/ComponentFinderStrategyBuilderTests.java b/structurizr-component/src/test/java/com/structurizr/component/ComponentFinderStrategyBuilderTests.java index 6b578022f..764a304a0 100644 --- a/structurizr-component/src/test/java/com/structurizr/component/ComponentFinderStrategyBuilderTests.java +++ b/structurizr-component/src/test/java/com/structurizr/component/ComponentFinderStrategyBuilderTests.java @@ -1,8 +1,15 @@ package com.structurizr.component; +import com.structurizr.component.filter.ExcludeTypesByRegexFilter; +import com.structurizr.component.filter.IncludeTypesByRegexFilter; +import com.structurizr.component.matcher.NameSuffixTypeMatcher; +import com.structurizr.component.naming.FullyQualifiedNamingStrategy; +import com.structurizr.component.naming.SimpleNamingStrategy; +import com.structurizr.component.supporting.AllTypesInPackageSupportingTypesStrategy; +import com.structurizr.component.supporting.AllTypesUnderPackageSupportingTypesStrategy; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertThrowsExactly; +import static org.junit.jupiter.api.Assertions.*; public class ComponentFinderStrategyBuilderTests { @@ -11,4 +18,114 @@ void build_ThrowsAnException_WhenATypeMatcherHasNotBeenConfigured() { assertThrowsExactly(RuntimeException.class, () -> new ComponentFinderStrategyBuilder().build()); } + @Test + void matchedBy_ThrowsAnException_WhenPassedNull() { + try { + new ComponentFinderStrategyBuilder().matchedBy(null); + fail(); + } catch (Exception e) { + assertEquals("A type matcher must be provided", e.getMessage()); + } + } + + @Test + void matchedBy_ThrowsAnException_WhenCalledTwice() { + try { + new ComponentFinderStrategyBuilder().matchedBy(new NameSuffixTypeMatcher("X")).matchedBy(new NameSuffixTypeMatcher("Y")); + fail(); + } catch (Exception e) { + assertEquals("A type matcher has already been configured", e.getMessage()); + } + } + + @Test + void filteredBy_ThrowsAnException_WhenPassedNull() { + try { + new ComponentFinderStrategyBuilder().filteredBy(null); + fail(); + } catch (Exception e) { + assertEquals("A type filter must be provided", e.getMessage()); + } + } + + @Test + void filteredBy_ThrowsAnException_WhenCalledTwice() { + try { + new ComponentFinderStrategyBuilder().filteredBy(new IncludeTypesByRegexFilter(".*")).filteredBy(new ExcludeTypesByRegexFilter(".*")); + fail(); + } catch (Exception e) { + assertEquals("A type filter has already been configured", e.getMessage()); + } + } + + @Test + void supportedBy_ThrowsAnException_WhenPassedNull() { + try { + new ComponentFinderStrategyBuilder().supportedBy(null); + fail(); + } catch (Exception e) { + assertEquals("A supporting types strategy must be provided", e.getMessage()); + } + } + + @Test + void supportedBy_ThrowsAnException_WhenCalledTwice() { + try { + new ComponentFinderStrategyBuilder().supportedBy(new AllTypesInPackageSupportingTypesStrategy()).supportedBy(new AllTypesUnderPackageSupportingTypesStrategy()); + fail(); + } catch (Exception e) { + assertEquals("A supporting types strategy has already been configured", e.getMessage()); + } + } + + @Test + void namedBy_ThrowsAnException_WhenPassedNull() { + try { + new ComponentFinderStrategyBuilder().namedBy(null); + fail(); + } catch (Exception e) { + assertEquals("A naming strategy must be provided", e.getMessage()); + } + } + + @Test + void namedBy_ThrowsAnException_WhenCalledTwice() { + try { + new ComponentFinderStrategyBuilder().namedBy(new SimpleNamingStrategy()).namedBy(new FullyQualifiedNamingStrategy()); + fail(); + } catch (Exception e) { + assertEquals("A naming strategy has already been configured", e.getMessage()); + } + } + + @Test + void asTechnology_ThrowsAnException_WhenPassedNull() { + try { + new ComponentFinderStrategyBuilder().asTechnology(null); + fail(); + } catch (Exception e) { + assertEquals("A technology must be provided", e.getMessage()); + } + } + + @Test + void asTechnology_ThrowsAnException_WhenPassedAnEmptyString() { + try { + new ComponentFinderStrategyBuilder().asTechnology(""); + fail(); + } catch (Exception e) { + assertEquals("A technology must be provided", e.getMessage()); + } + } + + @Test + void asTechnology_ThrowsAnException_WhenCalledTwice() { + try { + new ComponentFinderStrategyBuilder().asTechnology("X").asTechnology("Y"); + fail(); + } catch (Exception e) { + assertEquals("A technology has already been configured", e.getMessage()); + } + } + } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyParser.java index 030fc3ff3..bb345bc40 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyParser.java @@ -6,27 +6,43 @@ import com.structurizr.component.naming.DefaultPackageNamingStrategy; import com.structurizr.component.naming.SimpleNamingStrategy; import com.structurizr.component.naming.FullyQualifiedNamingStrategy; -import com.structurizr.component.supporting.AllReferencedTypesInPackageSupportingTypesStrategy; -import com.structurizr.component.supporting.AllReferencedTypesSupportingTypesStrategy; -import com.structurizr.component.supporting.AllTypesInPackageSupportingTypesStrategy; -import com.structurizr.component.supporting.AllTypesUnderPackageSupportingTypesStrategy; +import com.structurizr.component.supporting.*; import java.io.File; +import java.util.List; final class ComponentFinderStrategyParser extends AbstractParser { private static final String TECHNOLOGY_GRAMMAR = "technology "; - private static final String MATCHER_GRAMMAR = "matcher [parameters]"; + private static final String MATCHER_ANNOTATION = "annotation"; + private static final String MATCHER_EXTENDS = "extends"; + private static final String MATCHER_IMPLEMENTS = "implements"; + private static final String MATCHER_NAME_SUFFIX = "name-suffix"; + private static final String MATCHER_FQN_REGEX = "fqn-regex"; + private static final String MATCHER_GRAMMAR = "matcher <" + String.join("|", List.of(MATCHER_ANNOTATION, MATCHER_EXTENDS, MATCHER_IMPLEMENTS, MATCHER_NAME_SUFFIX, MATCHER_FQN_REGEX)) + "> [parameters]"; private static final String MATCHER_ANNOTATION_GRAMMAR = "matcher annotation "; private static final String MATCHER_EXTENDS_GRAMMAR = "matcher extends "; private static final String MATCHER_IMPLEMENTS_GRAMMAR = "matcher implements "; - private static final String MATCHER_NAMESUFFIX_GRAMMAR = "matcher namesuffix "; - private static final String MATCHER_REGEX_GRAMMAR = "matcher regex "; - - private static final String FILTER_GRAMMAR = "filter [parameters]"; - private static final String SUPPORTING_TYPES_GRAMMAR = "supportingTypes [parameters]"; - private static final String NAMING_GRAMMAR = "naming "; + private static final String MATCHER_NAMESUFFIX_GRAMMAR = "matcher name-suffix "; + private static final String MATCHER_REGEX_GRAMMAR = "matcher fqn-regex "; + + private static final String FILTER_INCLUDE = "include"; + private static final String FILTER_EXCLUDE = "exclude"; + private static final String FILTER_FQN_REGEX = "fqn-regex"; + private static final String FILTER_GRAMMAR = "filter <" + FILTER_INCLUDE + "|" + FILTER_EXCLUDE + "> <" + FILTER_FQN_REGEX + "> [parameters]"; + + private static final String SUPPORTING_TYPES_ALL_REFERENCED = "all-referenced"; + private static final String SUPPORTING_TYPES_REFERENCED_IN_PACKAGE = "referenced-in-package"; + private static final String SUPPORTING_TYPES_IN_PACKAGE = "in-package"; + private static final String SUPPORTING_TYPES_UNDER_PACKAGE = "under-package"; + private static final String SUPPORTING_TYPES_NONE = "none"; + private static final String SUPPORTING_TYPES_GRAMMAR = "supportingTypes <" + String.join("|", List.of(SUPPORTING_TYPES_ALL_REFERENCED, SUPPORTING_TYPES_REFERENCED_IN_PACKAGE, SUPPORTING_TYPES_IN_PACKAGE, SUPPORTING_TYPES_UNDER_PACKAGE, SUPPORTING_TYPES_NONE)) + "> [parameters]"; + + private static final String NAMING_NAME = "name"; + private static final String NAMING_FQN = "fqn"; + private static final String NAMING_PACKAGE = "package"; + private static final String NAMING_GRAMMAR = "naming <" + String.join("|", List.of(NAMING_NAME, NAMING_FQN, NAMING_PACKAGE)) + ">"; void parseTechnology(ComponentFinderStrategyDslContext context, Tokens tokens) { if (tokens.size() != 2) { @@ -44,7 +60,7 @@ void parseMatcher(ComponentFinderStrategyDslContext context, Tokens tokens, File String type = tokens.get(1); switch (type.toLowerCase()) { - case "annotation": + case MATCHER_ANNOTATION: if (tokens.size() == 3) { String name = tokens.get(2); @@ -53,7 +69,7 @@ void parseMatcher(ComponentFinderStrategyDslContext context, Tokens tokens, File throw new RuntimeException("Expected: " + MATCHER_ANNOTATION_GRAMMAR); } break; - case "extends": + case MATCHER_EXTENDS: if (tokens.size() == 3) { String name = tokens.get(2); @@ -62,7 +78,7 @@ void parseMatcher(ComponentFinderStrategyDslContext context, Tokens tokens, File throw new RuntimeException("Expected: " + MATCHER_EXTENDS_GRAMMAR); } break; - case "implements": + case MATCHER_IMPLEMENTS: if (tokens.size() == 3) { String name = tokens.get(2); @@ -71,7 +87,7 @@ void parseMatcher(ComponentFinderStrategyDslContext context, Tokens tokens, File throw new RuntimeException("Expected: " + MATCHER_IMPLEMENTS_GRAMMAR); } break; - case "namesuffix": + case MATCHER_NAME_SUFFIX: if (tokens.size() == 3) { String suffix = tokens.get(2); @@ -80,7 +96,7 @@ void parseMatcher(ComponentFinderStrategyDslContext context, Tokens tokens, File throw new RuntimeException("Expected: " + MATCHER_NAMESUFFIX_GRAMMAR); } break; - case "regex": + case MATCHER_FQN_REGEX: if (tokens.size() == 3) { String regex = tokens.get(2); @@ -109,26 +125,26 @@ void parseMatcher(ComponentFinderStrategyDslContext context, Tokens tokens, File } void parseFilter(ComponentFinderStrategyDslContext context, Tokens tokens, File dslFile) { - if (tokens.size() < 2) { + if (tokens.size() < 3) { throw new RuntimeException("Too few tokens, expected: " + FILTER_GRAMMAR); } - String type = tokens.get(1).toLowerCase(); - switch (type) { - case "includeregex": - if (tokens.size() == 3) { - String regex = tokens.get(2); + String includeOrExclude = tokens.get(1).toLowerCase(); + if (!"include".equalsIgnoreCase(includeOrExclude) && !"exclude".equalsIgnoreCase(includeOrExclude)) { + throw new RuntimeException("Filter mode should be \"" + FILTER_INCLUDE + "\" or \"" + FILTER_EXCLUDE + "\": " + FILTER_GRAMMAR); + } - context.getComponentFinderStrategyBuilder().filteredBy(new IncludeTypesByRegexFilter(regex)); - } else { - throw new RuntimeException("Expected: " + FILTER_GRAMMAR); - } - break; - case "excluderegex": - if (tokens.size() == 3) { - String regex = tokens.get(2); + String type = tokens.get(2).toLowerCase(); + switch (type) { + case FILTER_FQN_REGEX: + if (tokens.size() == 4) { + String regex = tokens.get(3); - context.getComponentFinderStrategyBuilder().filteredBy(new ExcludeTypesByRegexFilter(regex)); + if (FILTER_INCLUDE.equalsIgnoreCase(includeOrExclude)) { + context.getComponentFinderStrategyBuilder().filteredBy(new IncludeTypesByRegexFilter(regex)); + } else { + context.getComponentFinderStrategyBuilder().filteredBy(new ExcludeTypesByRegexFilter(regex)); + } } else { throw new RuntimeException("Expected: " + FILTER_GRAMMAR); } @@ -145,18 +161,21 @@ void parseSupportingTypes(ComponentFinderStrategyDslContext context, Tokens toke String type = tokens.get(1).toLowerCase(); switch (type) { - case "referenced": + case SUPPORTING_TYPES_ALL_REFERENCED: context.getComponentFinderStrategyBuilder().supportedBy(new AllReferencedTypesSupportingTypesStrategy()); break; - case "referencedinpackage": + case SUPPORTING_TYPES_REFERENCED_IN_PACKAGE: context.getComponentFinderStrategyBuilder().supportedBy(new AllReferencedTypesInPackageSupportingTypesStrategy()); break; - case "inpackage": + case SUPPORTING_TYPES_IN_PACKAGE: context.getComponentFinderStrategyBuilder().supportedBy(new AllTypesInPackageSupportingTypesStrategy()); break; - case "underpackage": + case SUPPORTING_TYPES_UNDER_PACKAGE: context.getComponentFinderStrategyBuilder().supportedBy(new AllTypesUnderPackageSupportingTypesStrategy()); break; + case SUPPORTING_TYPES_NONE: + context.getComponentFinderStrategyBuilder().supportedBy(new DefaultSupportingTypesStrategy()); + break; default: throw new IllegalArgumentException("Unknown supporting types strategy: " + type); } @@ -169,13 +188,13 @@ void parseNaming(ComponentFinderStrategyDslContext context, Tokens tokens, File String type = tokens.get(1).toLowerCase(); switch (type) { - case "name": + case NAMING_NAME: context.getComponentFinderStrategyBuilder().namedBy(new SimpleNamingStrategy()); break; - case "fqn": + case NAMING_FQN: context.getComponentFinderStrategyBuilder().namedBy(new FullyQualifiedNamingStrategy()); break; - case "package": + case NAMING_PACKAGE: context.getComponentFinderStrategyBuilder().namedBy(new DefaultPackageNamingStrategy()); break; default: diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderStrategyParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderStrategyParserTests.java index ad95614e9..beaa5db9f 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderStrategyParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderStrategyParserTests.java @@ -39,7 +39,7 @@ void test_parseTechnology_ThrowsAnException_WhenThereAreTooManyTokens() { @Test void test_parseTechnology() { parser.parseTechnology(context, tokens("technology", "name")); - assertEquals("ComponentFinderStrategyBuilder{technology='name', typeMatcher=null, typeFilter=DefaultTypeFilter{}, supportingTypesStrategy=DefaultSupportingTypesStrategy{}, namingStrategy=DefaultNamingStrategy{}, componentVisitor=DefaultComponentVisitor{}}", context.getComponentFinderStrategyBuilder().toString()); + assertEquals("ComponentFinderStrategyBuilder{technology='name', typeMatcher=null, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); } @Test @@ -48,7 +48,7 @@ void test_parseMatcher_ThrowsAnException_WhenNoTypeIsSpecified() { parser.parseMatcher(context, tokens("matcher"), null); fail(); } catch (Exception e) { - assertEquals("Too few tokens, expected: matcher [parameters]", e.getMessage()); + assertEquals("Too few tokens, expected: matcher [parameters]", e.getMessage()); } } @@ -65,7 +65,7 @@ void test_parseMatcher_WhenTheAnnotationTypeMatcherIsUsedAndThereAreTooFewTokens @Test void test_parseMatcher_WhenTheAnnotationTypeMatcherIsUsed() { parser.parseMatcher(context, tokens("matcher", "annotation", "com.example.Component"), null); - assertEquals("ComponentFinderStrategyBuilder{technology='null', typeMatcher=AnnotationTypeMatcher{annotationType='Lcom/example/Component;'}, typeFilter=DefaultTypeFilter{}, supportingTypesStrategy=DefaultSupportingTypesStrategy{}, namingStrategy=DefaultNamingStrategy{}, componentVisitor=DefaultComponentVisitor{}}", context.getComponentFinderStrategyBuilder().toString()); + assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=AnnotationTypeMatcher{annotationType='Lcom/example/Component;'}, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); } @Test @@ -81,7 +81,7 @@ void test_parseMatcher_WhenTheExtendsTypeMatcherIsUsedAndThereAreTooFewTokens() @Test void test_parseMatcher_WhenTheExtendsTypeMatcherIsUsed() { parser.parseMatcher(context, tokens("matcher", "extends", "com.example.Component"), null); - assertEquals("ComponentFinderStrategyBuilder{technology='null', typeMatcher=ExtendsTypeMatcher{className='com.example.Component'}, typeFilter=DefaultTypeFilter{}, supportingTypesStrategy=DefaultSupportingTypesStrategy{}, namingStrategy=DefaultNamingStrategy{}, componentVisitor=DefaultComponentVisitor{}}", context.getComponentFinderStrategyBuilder().toString()); + assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=ExtendsTypeMatcher{className='com.example.Component'}, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); } @Test @@ -97,39 +97,39 @@ void test_parseMatcher_WhenTheImplementsTypeMatcherIsUsedAndThereAreTooFewTokens @Test void test_parseMatcher_WhenTheImplementsTypeMatcherIsUsed() { parser.parseMatcher(context, tokens("matcher", "implements", "com.example.Component"), null); - assertEquals("ComponentFinderStrategyBuilder{technology='null', typeMatcher=ImplementsTypeMatcher{interfaceName='com.example.Component'}, typeFilter=DefaultTypeFilter{}, supportingTypesStrategy=DefaultSupportingTypesStrategy{}, namingStrategy=DefaultNamingStrategy{}, componentVisitor=DefaultComponentVisitor{}}", context.getComponentFinderStrategyBuilder().toString()); + assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=ImplementsTypeMatcher{interfaceName='com.example.Component'}, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); } @Test void test_parseMatcher_WhenTheNameSuffixTypeMatcherIsUsedAndThereAreTooFewTokens() { try { - parser.parseMatcher(context, tokens("matcher", "namesuffix"), null); + parser.parseMatcher(context, tokens("matcher", "name-suffix"), null); fail(); } catch (Exception e) { - assertEquals("Expected: matcher namesuffix ", e.getMessage()); + assertEquals("Expected: matcher name-suffix ", e.getMessage()); } } @Test void test_parseMatcher_WhenTheNameSuffixTypeMatcherIsUsed() { - parser.parseMatcher(context, tokens("matcher", "namesuffix", "Component"), null); - assertEquals("ComponentFinderStrategyBuilder{technology='null', typeMatcher=NameSuffixTypeMatcher{suffix='Component'}, typeFilter=DefaultTypeFilter{}, supportingTypesStrategy=DefaultSupportingTypesStrategy{}, namingStrategy=DefaultNamingStrategy{}, componentVisitor=DefaultComponentVisitor{}}", context.getComponentFinderStrategyBuilder().toString()); + parser.parseMatcher(context, tokens("matcher", "name-suffix", "Component"), null); + assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=NameSuffixTypeMatcher{suffix='Component'}, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); } @Test - void test_parseMatcher_WhenTheRegexTypeMatcherIsUsedAndThereAreTooFewTokens() { + void test_parseMatcher_WhenTheFullyQualifiedNameRegexTypeMatcherIsUsedAndThereAreTooFewTokens() { try { - parser.parseMatcher(context, tokens("matcher", "regex"), null); + parser.parseMatcher(context, tokens("matcher", "fqn-regex"), null); fail(); } catch (Exception e) { - assertEquals("Expected: matcher regex ", e.getMessage()); + assertEquals("Expected: matcher fqn-regex ", e.getMessage()); } } @Test void test_parseMatcher_WhenTheRegexTypeMatcherIsUsed() { - parser.parseMatcher(context, tokens("matcher", "regex", ".*Component"), null); - assertEquals("ComponentFinderStrategyBuilder{technology='null', typeMatcher=RegexTypeMatcher{regex=.*Component}, typeFilter=DefaultTypeFilter{}, supportingTypesStrategy=DefaultSupportingTypesStrategy{}, namingStrategy=DefaultNamingStrategy{}, componentVisitor=DefaultComponentVisitor{}}", context.getComponentFinderStrategyBuilder().toString()); + parser.parseMatcher(context, tokens("matcher", "fqn-regex", ".*Component"), null); + assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=RegexTypeMatcher{regex='.*Component'}, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); } @Test @@ -145,32 +145,64 @@ void test_parseMatcher_WhenACustomTypeMatcherIsUsedButCannotBeLoaded() { @Test void test_parseMatcher_WhenACustomTypeMatcherIsUsedWithoutParameters() { parser.parseMatcher(context, tokens("matcher", "com.structurizr.dsl.example.CustomTypeMatcher"), new File(".")); - assertEquals("ComponentFinderStrategyBuilder{technology='null', typeMatcher=CustomTypeMatcher{}, typeFilter=DefaultTypeFilter{}, supportingTypesStrategy=DefaultSupportingTypesStrategy{}, namingStrategy=DefaultNamingStrategy{}, componentVisitor=DefaultComponentVisitor{}}", context.getComponentFinderStrategyBuilder().toString()); + assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=CustomTypeMatcher{}, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); } @Test void test_parseMatcher_WhenACustomTypeMatcherIsUsedWithAParameter() { parser.parseMatcher(context, tokens("matcher", NameSuffixTypeMatcher.class.getCanonicalName(), "Component"), new File(".")); - assertEquals("ComponentFinderStrategyBuilder{technology='null', typeMatcher=NameSuffixTypeMatcher{suffix='Component'}, typeFilter=DefaultTypeFilter{}, supportingTypesStrategy=DefaultSupportingTypesStrategy{}, namingStrategy=DefaultNamingStrategy{}, componentVisitor=DefaultComponentVisitor{}}", context.getComponentFinderStrategyBuilder().toString()); + assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=NameSuffixTypeMatcher{suffix='Component'}, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); } @Test - void test_parseFilter_ThrowsAnException_WhenNoTypeIsSpecified() { + void test_parseFilter_ThrowsAnException_WhenNoModeAndTypeAreSpecified() { try { parser.parseFilter(context, tokens("filter"), null); fail(); } catch (Exception e) { - assertEquals("Too few tokens, expected: filter [parameters]", e.getMessage()); + assertEquals("Too few tokens, expected: filter [parameters]", e.getMessage()); + } + } + + @Test + void test_parseFilter_ThrowsAnException_WhenNoTypeIsSpecified() { + try { + parser.parseFilter(context, tokens("filter", "include"), null); + fail(); + } catch (Exception e) { + assertEquals("Too few tokens, expected: filter [parameters]", e.getMessage()); + } + } + + @Test + void test_parseFilter_ThrowsAnException_WhenInvalidModeIsSpecified() { + try { + parser.parseFilter(context, tokens("filter", "mode", "fqn-regex"), null); + fail(); + } catch (Exception e) { + assertEquals("Filter mode should be \"include\" or \"exclude\": filter [parameters]", e.getMessage()); } } + @Test + void test_parseFilter_WhenIncludeFullyQualifiedNameRegexTypeFilterIsUsed() { + parser.parseFilter(context, tokens("filter", "include", "fqn-regex", ".*"), new File(".")); + assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=null, typeFilter=IncludeTypesByRegexFilter{regex='.*'}, supportingTypesStrategy=null, namingStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); + } + + @Test + void test_parseFilter_WhenExcludeFullyQualifiedNameRegexTypeFilterIsUsed() { + parser.parseFilter(context, tokens("filter", "exclude", "fqn-regex", ".*"), new File(".")); + assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=null, typeFilter=ExcludeTypesByRegexFilter{regex='.*'}, supportingTypesStrategy=null, namingStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); + } + @Test void test_parseSupportingTypes_ThrowsAnException_WhenNoTypeIsSpecified() { try { parser.parseSupportingTypes(context, tokens("supportingTypes"), null); fail(); } catch (Exception e) { - assertEquals("Too few tokens, expected: supportingTypes [parameters]", e.getMessage()); + assertEquals("Too few tokens, expected: supportingTypes [parameters]", e.getMessage()); } } diff --git a/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl b/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl index abf7e451a..8446171cb 100644 --- a/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl +++ b/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl @@ -25,7 +25,7 @@ workspace "Spring PetClinic" "A C4 model of the Spring PetClinic sample app (htt strategy { technology "Spring MVC Controller" matcher annotation "org.springframework.stereotype.Controller" - filter excludeRegex ".*.CrashController" + filter exclude fqn-regex ".*.CrashController" forEach { clinicEmployee -> this "Uses" tag "Spring MVC Controller" From 1e2b19c854c1327eb22a405e4365266a6b20a74d Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 17 Sep 2024 08:45:01 +0100 Subject: [PATCH 567/717] Adds a way to customise how component descriptions are set; also some tidy-up/renaming. --- .../component/ComponentFinderStrategy.java | 9 +- .../ComponentFinderStrategyBuilder.java | 28 +++++- .../DefaultDescriptionStrategy.java | 21 +++++ .../description/DescriptionStrategy.java | 9 ++ .../FirstSentenceDescriptionStrategy.java | 28 ++++++ .../TruncatedDescriptionStrategy.java | 42 +++++++++ ...gStrategy.java => TypeNamingStrategy.java} | 9 +- .../provider/JavadocCommentFilter.java | 18 +--- .../provider/SourceDirectoryTypeProvider.java | 17 ++-- .../ComponentFinderStrategyBuilderTests.java | 72 +++++++++++---- ...FirstSentenceDescriptionStrategyTests.java | 24 +++++ .../TruncatedDescriptionStrategyTests.java | 36 ++++++++ ...ests.java => TypeNamingStrategyTests.java} | 4 +- .../provider/JavadocCommentFilterTests.java | 40 +++------ .../ComponentFinderStrategyDslContext.java | 2 +- ...ponentFinderStrategyForEachDslContext.java | 1 - .../dsl/ComponentFinderStrategyParser.java | 64 +++++++++---- .../structurizr/dsl/StructurizrDslParser.java | 7 +- .../structurizr/dsl/StructurizrDslTokens.java | 3 +- .../ComponentFinderStrategyParserTests.java | 89 ++++++++++++++++--- .../java/com/structurizr/dsl/DslTests.java | 1 + .../dsl/spring-petclinic/workspace.dsl | 1 + 22 files changed, 410 insertions(+), 115 deletions(-) create mode 100644 structurizr-component/src/main/java/com/structurizr/component/description/DefaultDescriptionStrategy.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/description/DescriptionStrategy.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/description/FirstSentenceDescriptionStrategy.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/description/TruncatedDescriptionStrategy.java rename structurizr-component/src/main/java/com/structurizr/component/naming/{SimpleNamingStrategy.java => TypeNamingStrategy.java} (61%) create mode 100644 structurizr-component/src/test/java/com/structurizr/component/description/FirstSentenceDescriptionStrategyTests.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/description/TruncatedDescriptionStrategyTests.java rename structurizr-component/src/test/java/com/structurizr/component/naming/{SimpleNamingStrategyTests.java => TypeNamingStrategyTests.java} (60%) diff --git a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategy.java index 3d5886f2c..774f10ba5 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategy.java +++ b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategy.java @@ -1,5 +1,7 @@ package com.structurizr.component; +import com.structurizr.component.description.DefaultDescriptionStrategy; +import com.structurizr.component.description.DescriptionStrategy; import com.structurizr.component.filter.TypeFilter; import com.structurizr.component.matcher.TypeMatcher; import com.structurizr.component.naming.NamingStrategy; @@ -27,14 +29,16 @@ public final class ComponentFinderStrategy { private final TypeFilter typeFilter; private final SupportingTypesStrategy supportingTypesStrategy; private final NamingStrategy namingStrategy; + private final DescriptionStrategy descriptionStrategy; private final ComponentVisitor componentVisitor; - ComponentFinderStrategy(String technology, TypeMatcher typeMatcher, TypeFilter typeFilter, SupportingTypesStrategy supportingTypesStrategy, NamingStrategy namingStrategy, ComponentVisitor componentVisitor) { + ComponentFinderStrategy(String technology, TypeMatcher typeMatcher, TypeFilter typeFilter, SupportingTypesStrategy supportingTypesStrategy, NamingStrategy namingStrategy, DescriptionStrategy descriptionStrategy, ComponentVisitor componentVisitor) { this.technology = technology; this.typeMatcher = typeMatcher; this.typeFilter = typeFilter; this.supportingTypesStrategy = supportingTypesStrategy; this.namingStrategy = namingStrategy; + this.descriptionStrategy = descriptionStrategy; this.componentVisitor = componentVisitor; } @@ -45,7 +49,7 @@ Set findComponents(TypeRepository typeRepository) { for (Type type : types) { if (typeMatcher.matches(type) && typeFilter.accept(type)) { DiscoveredComponent component = new DiscoveredComponent(namingStrategy.nameOf(type), type); - component.setDescription(type.getDescription()); + component.setDescription(descriptionStrategy.descriptionOf(type)); component.setTechnology(this.technology); component.setComponentFinderStrategy(this); components.add(component); @@ -71,6 +75,7 @@ public String toString() { ", typeFilter=" + typeFilter + ", supportingTypesStrategy=" + supportingTypesStrategy + ", namingStrategy=" + namingStrategy + + ", descriptionStrategy=" + descriptionStrategy + ", componentVisitor=" + componentVisitor + '}'; } diff --git a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategyBuilder.java b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategyBuilder.java index a8dcbb9f2..eab3e25c2 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategyBuilder.java +++ b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategyBuilder.java @@ -1,5 +1,7 @@ package com.structurizr.component; +import com.structurizr.component.description.DefaultDescriptionStrategy; +import com.structurizr.component.description.DescriptionStrategy; import com.structurizr.component.filter.DefaultTypeFilter; import com.structurizr.component.filter.TypeFilter; import com.structurizr.component.matcher.TypeMatcher; @@ -21,6 +23,7 @@ public final class ComponentFinderStrategyBuilder { private TypeFilter typeFilter; private SupportingTypesStrategy supportingTypesStrategy; private NamingStrategy namingStrategy; + private DescriptionStrategy descriptionStrategy; private ComponentVisitor componentVisitor; public ComponentFinderStrategyBuilder() { @@ -68,7 +71,7 @@ public ComponentFinderStrategyBuilder supportedBy(SupportingTypesStrategy suppor return this; } - public ComponentFinderStrategyBuilder namedBy(NamingStrategy namingStrategy) { + public ComponentFinderStrategyBuilder withName(NamingStrategy namingStrategy) { if (namingStrategy == null) { throw new IllegalArgumentException("A naming strategy must be provided"); } @@ -82,7 +85,21 @@ public ComponentFinderStrategyBuilder namedBy(NamingStrategy namingStrategy) { return this; } - public ComponentFinderStrategyBuilder asTechnology(String technology) { + public ComponentFinderStrategyBuilder withDescription(DescriptionStrategy descriptionStrategy) { + if (descriptionStrategy == null) { + throw new IllegalArgumentException("A description strategy must be provided"); + } + + if (this.descriptionStrategy != null) { + throw new IllegalArgumentException("A description strategy has already been configured"); + } + + this.descriptionStrategy = descriptionStrategy; + + return this; + } + + public ComponentFinderStrategyBuilder forTechnology(String technology) { if (StringUtils.isNullOrEmpty(technology)) { throw new IllegalArgumentException("A technology must be provided"); } @@ -127,11 +144,15 @@ public ComponentFinderStrategy build() { namingStrategy = new DefaultNamingStrategy(); } + if (descriptionStrategy == null) { + descriptionStrategy = new DefaultDescriptionStrategy(); + } + if (componentVisitor == null) { componentVisitor = new DefaultComponentVisitor(); } - return new ComponentFinderStrategy(technology, typeMatcher, typeFilter, supportingTypesStrategy, namingStrategy, componentVisitor); + return new ComponentFinderStrategy(technology, typeMatcher, typeFilter, supportingTypesStrategy, namingStrategy, descriptionStrategy, componentVisitor); } @Override @@ -142,6 +163,7 @@ public String toString() { ", typeFilter=" + typeFilter + ", supportingTypesStrategy=" + supportingTypesStrategy + ", namingStrategy=" + namingStrategy + + ", descriptionStrategy=" + descriptionStrategy + ", componentVisitor=" + componentVisitor + '}'; } diff --git a/structurizr-component/src/main/java/com/structurizr/component/description/DefaultDescriptionStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/description/DefaultDescriptionStrategy.java new file mode 100644 index 000000000..f2e1ef0ff --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/description/DefaultDescriptionStrategy.java @@ -0,0 +1,21 @@ +package com.structurizr.component.description; + +import com.structurizr.component.Type; +import com.structurizr.component.naming.NamingStrategy; + +/** + * Uses the type description as-is. + */ +public class DefaultDescriptionStrategy implements DescriptionStrategy { + + @Override + public String descriptionOf(Type type) { + return type.getDescription(); + } + + @Override + public String toString() { + return "DefaultDescriptionStrategy{}"; + } + +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/description/DescriptionStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/description/DescriptionStrategy.java new file mode 100644 index 000000000..c255a73f4 --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/description/DescriptionStrategy.java @@ -0,0 +1,9 @@ +package com.structurizr.component.description; + +import com.structurizr.component.Type; + +public interface DescriptionStrategy { + + String descriptionOf(Type type); + +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/description/FirstSentenceDescriptionStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/description/FirstSentenceDescriptionStrategy.java new file mode 100644 index 000000000..accb274f6 --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/description/FirstSentenceDescriptionStrategy.java @@ -0,0 +1,28 @@ +package com.structurizr.component.description; + +import com.structurizr.component.Type; +import com.structurizr.util.StringUtils; + +/** + * Uses the first sentence of the type description, or the description as-is if there are no sentences. + */ +public class FirstSentenceDescriptionStrategy implements DescriptionStrategy { + + @Override + public String descriptionOf(Type type) { + String description = type.getDescription(); + + int index = description.indexOf('.'); + if (index == -1) { + return description; + } else { + return description.substring(0, index+1); + } + } + + @Override + public String toString() { + return "FirstSentenceDescriptionStrategy{}"; + } + +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/description/TruncatedDescriptionStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/description/TruncatedDescriptionStrategy.java new file mode 100644 index 000000000..a006ec504 --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/description/TruncatedDescriptionStrategy.java @@ -0,0 +1,42 @@ +package com.structurizr.component.description; + +import com.structurizr.component.Type; +import com.structurizr.util.StringUtils; + +/** + * Truncates the type description to the max length, appending "..." when truncated. + */ +public class TruncatedDescriptionStrategy implements DescriptionStrategy { + + private final int maxLength; + + public TruncatedDescriptionStrategy(int maxLength) { + if (maxLength < 1) { + throw new IllegalArgumentException("Max length must be greater than 0"); + } + + this.maxLength = maxLength; + } + + @Override + public String descriptionOf(Type type) { + String description = type.getDescription(); + + if (StringUtils.isNullOrEmpty(description)) { + return description; + } + + if (description.length() > maxLength) { + return description.substring(0, maxLength) + "..."; + } else { + return description; + } + } + + @Override + public String toString() { + return "TruncatedDescriptionStrategy{" + + "maxLength=" + maxLength + + '}'; + } +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/naming/SimpleNamingStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/naming/TypeNamingStrategy.java similarity index 61% rename from structurizr-component/src/main/java/com/structurizr/component/naming/SimpleNamingStrategy.java rename to structurizr-component/src/main/java/com/structurizr/component/naming/TypeNamingStrategy.java index 7b60b58de..47ffe3b90 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/naming/SimpleNamingStrategy.java +++ b/structurizr-component/src/main/java/com/structurizr/component/naming/TypeNamingStrategy.java @@ -5,10 +5,15 @@ /** * Uses the simple/short name of the type (i.e. without the package name). */ -public class SimpleNamingStrategy implements NamingStrategy { +public class TypeNamingStrategy implements NamingStrategy { public String nameOf(Type type) { return type.getName(); } -} + @Override + public String toString() { + return "TypeNamingStrategy{}"; + } + +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/provider/JavadocCommentFilter.java b/structurizr-component/src/main/java/com/structurizr/component/provider/JavadocCommentFilter.java index c89b3fc34..51b02900b 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/provider/JavadocCommentFilter.java +++ b/structurizr-component/src/main/java/com/structurizr/component/provider/JavadocCommentFilter.java @@ -5,17 +5,7 @@ */ class JavadocCommentFilter { - private final Integer maxCommentLength; - - JavadocCommentFilter(Integer maxCommentLength) { - if (maxCommentLength != null && maxCommentLength < 1) { - throw new IllegalArgumentException("Maximum comment length must be greater than 0."); - } - - this.maxCommentLength = maxCommentLength; - } - - String filterAndTruncate(String s) { + String filter(String s) { if (s == null) { return null; } @@ -25,11 +15,7 @@ String filterAndTruncate(String s) { s = s.replaceAll("\\{@link (\\S*)\\}", "$1"); s = s.replaceAll("\\{@link (\\S*) (.*?)\\}", "$2"); - if (maxCommentLength != null && s.length() > maxCommentLength) { - return s.substring(0, maxCommentLength-3) + "..."; - } else { - return s; - } + return s; } } \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/provider/SourceDirectoryTypeProvider.java b/structurizr-component/src/main/java/com/structurizr/component/provider/SourceDirectoryTypeProvider.java index f8d8db07f..6bce2bb1c 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/provider/SourceDirectoryTypeProvider.java +++ b/structurizr-component/src/main/java/com/structurizr/component/provider/SourceDirectoryTypeProvider.java @@ -23,17 +23,11 @@ public final class SourceDirectoryTypeProvider implements TypeProvider { private static final Log log = LogFactory.getLog(SourceDirectoryTypeProvider.class); private static final String JAVA_FILE_EXTENSION = ".java"; - private static final int DEFAULT_DESCRIPTION_LENGTH = 60; private final File directory; - private final int maximumDescriptionLength; private final Set types = new LinkedHashSet<>(); public SourceDirectoryTypeProvider(File directory) { - this(directory, DEFAULT_DESCRIPTION_LENGTH); - } - - public SourceDirectoryTypeProvider(File directory, int maximumDescriptionLength) { if (directory == null) { throw new IllegalArgumentException("A directory must be supplied"); } @@ -47,24 +41,23 @@ public SourceDirectoryTypeProvider(File directory, int maximumDescriptionLength) } this.directory = directory; - this.maximumDescriptionLength = maximumDescriptionLength; StaticJavaParser.getParserConfiguration().setLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_21); } @Override public Set getTypes() { - parse(directory, maximumDescriptionLength); + parse(directory); return new LinkedHashSet<>(types); } - private void parse(File path, int maximumDescriptionLength) { + private void parse(File path) { if (path.isDirectory()) { File[] files = path.listFiles(); if (files != null) { for (File file : files) { try { - parse(file, maximumDescriptionLength); + parse(file); } catch (Exception e) { log.warn("Error parsing " + file.getAbsolutePath(), e); } @@ -85,7 +78,7 @@ public void visit(ClassOrInterfaceDeclaration n, Object arg) { JavadocComment javadocComment = (JavadocComment) n.getComment().get(); String description = javadocComment.parse().getDescription().toText(); - type.setDescription(new JavadocCommentFilter(maximumDescriptionLength).filterAndTruncate(description)); + type.setDescription(new JavadocCommentFilter().filter(description)); } types.add(type); } @@ -107,7 +100,7 @@ public void visit(PackageDeclaration n, Object arg) { JavadocComment javadocComment = (JavadocComment)rootNode.getComment().get(); String description = javadocComment.parse().getDescription().toText(); - type.setDescription(new JavadocCommentFilter(maximumDescriptionLength).filterAndTruncate(description)); + type.setDescription(new JavadocCommentFilter().filter(description)); } types.add(type); diff --git a/structurizr-component/src/test/java/com/structurizr/component/ComponentFinderStrategyBuilderTests.java b/structurizr-component/src/test/java/com/structurizr/component/ComponentFinderStrategyBuilderTests.java index 764a304a0..cb770a312 100644 --- a/structurizr-component/src/test/java/com/structurizr/component/ComponentFinderStrategyBuilderTests.java +++ b/structurizr-component/src/test/java/com/structurizr/component/ComponentFinderStrategyBuilderTests.java @@ -1,10 +1,12 @@ package com.structurizr.component; +import com.structurizr.component.description.FirstSentenceDescriptionStrategy; +import com.structurizr.component.description.TruncatedDescriptionStrategy; import com.structurizr.component.filter.ExcludeTypesByRegexFilter; import com.structurizr.component.filter.IncludeTypesByRegexFilter; import com.structurizr.component.matcher.NameSuffixTypeMatcher; import com.structurizr.component.naming.FullyQualifiedNamingStrategy; -import com.structurizr.component.naming.SimpleNamingStrategy; +import com.structurizr.component.naming.TypeNamingStrategy; import com.structurizr.component.supporting.AllTypesInPackageSupportingTypesStrategy; import com.structurizr.component.supporting.AllTypesUnderPackageSupportingTypesStrategy; import org.junit.jupiter.api.Test; @@ -13,11 +15,6 @@ public class ComponentFinderStrategyBuilderTests { - @Test - void build_ThrowsAnException_WhenATypeMatcherHasNotBeenConfigured() { - assertThrowsExactly(RuntimeException.class, () -> new ComponentFinderStrategyBuilder().build()); - } - @Test void matchedBy_ThrowsAnException_WhenPassedNull() { try { @@ -79,9 +76,9 @@ void supportedBy_ThrowsAnException_WhenCalledTwice() { } @Test - void namedBy_ThrowsAnException_WhenPassedNull() { + void withName_ThrowsAnException_WhenPassedNull() { try { - new ComponentFinderStrategyBuilder().namedBy(null); + new ComponentFinderStrategyBuilder().withName(null); fail(); } catch (Exception e) { assertEquals("A naming strategy must be provided", e.getMessage()); @@ -89,9 +86,9 @@ void namedBy_ThrowsAnException_WhenPassedNull() { } @Test - void namedBy_ThrowsAnException_WhenCalledTwice() { + void withName_ThrowsAnException_WhenCalledTwice() { try { - new ComponentFinderStrategyBuilder().namedBy(new SimpleNamingStrategy()).namedBy(new FullyQualifiedNamingStrategy()); + new ComponentFinderStrategyBuilder().withName(new TypeNamingStrategy()).withName(new FullyQualifiedNamingStrategy()); fail(); } catch (Exception e) { assertEquals("A naming strategy has already been configured", e.getMessage()); @@ -99,9 +96,29 @@ void namedBy_ThrowsAnException_WhenCalledTwice() { } @Test - void asTechnology_ThrowsAnException_WhenPassedNull() { + void withDescription_ThrowsAnException_WhenPassedNull() { + try { + new ComponentFinderStrategyBuilder().withDescription(null); + fail(); + } catch (Exception e) { + assertEquals("A description strategy must be provided", e.getMessage()); + } + } + + @Test + void withDescription_ThrowsAnException_WhenCalledTwice() { + try { + new ComponentFinderStrategyBuilder().withDescription(new TruncatedDescriptionStrategy(50)).withDescription(new FirstSentenceDescriptionStrategy()); + fail(); + } catch (Exception e) { + assertEquals("A description strategy has already been configured", e.getMessage()); + } + } + + @Test + void forTechnology_ThrowsAnException_WhenPassedNull() { try { - new ComponentFinderStrategyBuilder().asTechnology(null); + new ComponentFinderStrategyBuilder().forTechnology(null); fail(); } catch (Exception e) { assertEquals("A technology must be provided", e.getMessage()); @@ -109,9 +126,9 @@ void asTechnology_ThrowsAnException_WhenPassedNull() { } @Test - void asTechnology_ThrowsAnException_WhenPassedAnEmptyString() { + void forTechnology_ThrowsAnException_WhenPassedAnEmptyString() { try { - new ComponentFinderStrategyBuilder().asTechnology(""); + new ComponentFinderStrategyBuilder().forTechnology(""); fail(); } catch (Exception e) { assertEquals("A technology must be provided", e.getMessage()); @@ -119,13 +136,36 @@ void asTechnology_ThrowsAnException_WhenPassedAnEmptyString() { } @Test - void asTechnology_ThrowsAnException_WhenCalledTwice() { + void forTechnology_ThrowsAnException_WhenCalledTwice() { try { - new ComponentFinderStrategyBuilder().asTechnology("X").asTechnology("Y"); + new ComponentFinderStrategyBuilder().forTechnology("X").forTechnology("Y"); fail(); } catch (Exception e) { assertEquals("A technology has already been configured", e.getMessage()); } } + @Test + void build_ThrowsAnException_WhenATypeMatcherHasNotBeenConfigured() { + try { + new ComponentFinderStrategyBuilder().build(); + fail(); + } catch (Exception e) { + assertEquals("A type matcher must be provided", e.getMessage()); + } + } + + @Test + void build() { + ComponentFinderStrategy strategy = new ComponentFinderStrategyBuilder() + .forTechnology("Spring MVC Controller") + .matchedBy(new NameSuffixTypeMatcher("Controller")) + .filteredBy(new IncludeTypesByRegexFilter("com.example.web.\\.*")) + .withName(new TypeNamingStrategy()) + .withDescription(new FirstSentenceDescriptionStrategy()) + .build(); + + assertEquals("ComponentFinderStrategy{technology='Spring MVC Controller', typeMatcher=NameSuffixTypeMatcher{suffix='Controller'}, typeFilter=IncludeTypesByRegexFilter{regex='com.example.web.\\.*'}, supportingTypesStrategy=DefaultSupportingTypesStrategy{}, namingStrategy=TypeNamingStrategy{}, descriptionStrategy=FirstSentenceDescriptionStrategy{}, componentVisitor=DefaultComponentVisitor{}}", strategy.toString()); + } + } \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/description/FirstSentenceDescriptionStrategyTests.java b/structurizr-component/src/test/java/com/structurizr/component/description/FirstSentenceDescriptionStrategyTests.java new file mode 100644 index 000000000..f9c2515e1 --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/description/FirstSentenceDescriptionStrategyTests.java @@ -0,0 +1,24 @@ +package com.structurizr.component.description; + +import com.structurizr.component.Type; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class FirstSentenceDescriptionStrategyTests { + + @Test + void descriptionOf_WhenThereIsASentence() { + Type type = new Type("com.example.ClassName"); + type.setDescription("This is the first sentence. And this is the second."); + assertEquals("This is the first sentence.", new FirstSentenceDescriptionStrategy().descriptionOf(type)); + } + + @Test + void descriptionOf_WhenThereIsNotASentence() { + Type type = new Type("com.example.ClassName"); + type.setDescription("This is just lots of text without any punctuation"); + assertEquals("This is just lots of text without any punctuation", new FirstSentenceDescriptionStrategy().descriptionOf(type)); + } + +} \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/description/TruncatedDescriptionStrategyTests.java b/structurizr-component/src/test/java/com/structurizr/component/description/TruncatedDescriptionStrategyTests.java new file mode 100644 index 000000000..358e78020 --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/description/TruncatedDescriptionStrategyTests.java @@ -0,0 +1,36 @@ +package com.structurizr.component.description; + +import com.structurizr.component.Type; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class TruncatedDescriptionStrategyTests { + + @Test + public void test_construction_ThrowsAnIllegalArgumentException_WhenZeroIsSpecified() { + try { + new TruncatedDescriptionStrategy(0); + } catch (Exception e) { + assertEquals("Max length must be greater than 0", e.getMessage()); + } + } + + @Test + public void test_construction_ThrowsAnIllegalArgumentException_WhenANegativeNumberIsSpecified() { + try { + new TruncatedDescriptionStrategy(-1); + } catch (Exception e) { + assertEquals("Max length must be greater than 0", e.getMessage()); + } + } + + @Test + public void test_descriptionOf_TruncatesTheDescription() + { + Type type = new Type("Name"); + type.setDescription("Here is some text."); + assertEquals("Here...", new TruncatedDescriptionStrategy(4).descriptionOf(type)); + } + +} \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/naming/SimpleNamingStrategyTests.java b/structurizr-component/src/test/java/com/structurizr/component/naming/TypeNamingStrategyTests.java similarity index 60% rename from structurizr-component/src/test/java/com/structurizr/component/naming/SimpleNamingStrategyTests.java rename to structurizr-component/src/test/java/com/structurizr/component/naming/TypeNamingStrategyTests.java index a85aae823..b86749322 100644 --- a/structurizr-component/src/test/java/com/structurizr/component/naming/SimpleNamingStrategyTests.java +++ b/structurizr-component/src/test/java/com/structurizr/component/naming/TypeNamingStrategyTests.java @@ -5,11 +5,11 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -public class SimpleNamingStrategyTests { +public class TypeNamingStrategyTests { @Test void nameOf() { - assertEquals("ClassName", new SimpleNamingStrategy().nameOf(new Type("com.example.ClassName"))); + assertEquals("ClassName", new TypeNamingStrategy().nameOf(new Type("com.example.ClassName"))); } } \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/provider/JavadocCommentFilterTests.java b/structurizr-component/src/test/java/com/structurizr/component/provider/JavadocCommentFilterTests.java index 265a888d6..fa1cc0b39 100644 --- a/structurizr-component/src/test/java/com/structurizr/component/provider/JavadocCommentFilterTests.java +++ b/structurizr-component/src/test/java/com/structurizr/component/provider/JavadocCommentFilterTests.java @@ -7,54 +7,38 @@ public class JavadocCommentFilterTests { @Test - public void test_construction_ThrowsAnIllegalArgumentException_WhenZeroIsSpecified() { - assertThrowsExactly(IllegalArgumentException.class, () -> new JavadocCommentFilter(0)); + public void test_filter_ReturnsNull_WhenGivenNull() { + assertNull(new JavadocCommentFilter().filter(null)); } @Test - public void test_construction_ThrowsAnIllegalArgumentException_WhenANegativeNumberIsSpecified() { - assertThrowsExactly(IllegalArgumentException.class, () -> new JavadocCommentFilter(-1)); - } - - @Test - public void test_filterAndTruncate_ReturnsNull_WhenGivenNull() { - assertNull(new JavadocCommentFilter(null).filterAndTruncate(null)); - } - - @Test - public void test_filterAndTruncate_ReturnsTheOriginalText_WhenNoMaxLengthHasBeenSpecified() - { - assertEquals("Here is some text.", new JavadocCommentFilter(null).filterAndTruncate("Here is some text.")); - } - - @Test - public void test_filterAndTruncate_TruncatesTheTextWhenAMaxLengthHasBeenSpecified() + public void test_filter_ReturnsTheOriginalText_WhenNoMaxLengthHasBeenSpecified() { - assertEquals("Here...", new JavadocCommentFilter(7).filterAndTruncate("Here is some text.")); + assertEquals("Here is some text.", new JavadocCommentFilter().filter("Here is some text.")); } @Test - public void test_filterAndTruncate_FiltersJavadocLinkTags() + public void test_filter_FiltersJavadocLinkTags() { - assertEquals("Uses SomeClass and AnotherClass to do some work.", new JavadocCommentFilter(null).filterAndTruncate("Uses {@link SomeClass} and {@link AnotherClass} to do some work.")); + assertEquals("Uses SomeClass and AnotherClass to do some work.", new JavadocCommentFilter().filter("Uses {@link SomeClass} and {@link AnotherClass} to do some work.")); } @Test - public void test_filterAndTruncate_FiltersJavadocLinkTagsWithLabels() + public void test_filter_FiltersJavadocLinkTagsWithLabels() { - assertEquals("Uses some class and another class to do some work.", new JavadocCommentFilter(null).filterAndTruncate("Uses {@link SomeClass some class} and {@link AnotherClass another class} to do some work.")); + assertEquals("Uses some class and another class to do some work.", new JavadocCommentFilter().filter("Uses {@link SomeClass some class} and {@link AnotherClass another class} to do some work.")); } @Test - public void test_filterAndTruncate_FiltersHtml() + public void test_filter_FiltersHtml() { - assertEquals("Uses SomeClass and AnotherClass to do some work.", new JavadocCommentFilter(null).filterAndTruncate("Uses SomeClass and AnotherClass to do some work.")); + assertEquals("Uses SomeClass and AnotherClass to do some work.", new JavadocCommentFilter().filter("Uses SomeClass and AnotherClass to do some work.")); } @Test - public void test_filterAndTruncate_FiltersLineBreaks() + public void test_filter_FiltersLineBreaks() { - assertEquals("Uses SomeClass and AnotherClass to do some work.", new JavadocCommentFilter(null).filterAndTruncate("Uses SomeClass and AnotherClass\nto do some work.")); + assertEquals("Uses SomeClass and AnotherClass to do some work.", new JavadocCommentFilter().filter("Uses SomeClass and AnotherClass\nto do some work.")); } } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyDslContext.java index 762d4c383..2336fa006 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyDslContext.java @@ -19,7 +19,7 @@ protected String[] getPermittedTokens() { StructurizrDslTokens.COMPONENT_FINDER_STRATEGY_MATCHER_TOKEN, StructurizrDslTokens.COMPONENT_FINDER_STRATEGY_FILTER_TOKEN, StructurizrDslTokens.COMPONENT_FINDER_STRATEGY_SUPPORTING_TYPES_TOKEN, - StructurizrDslTokens.COMPONENT_FINDER_STRATEGY_NAMING_TOKEN, + StructurizrDslTokens.COMPONENT_FINDER_STRATEGY_NAME_TOKEN, StructurizrDslTokens.COMPONENT_FINDER_STRATEGY_FOREACH_TOKEN }; } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyForEachDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyForEachDslContext.java index ff2db4158..2e600b353 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyForEachDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyForEachDslContext.java @@ -1,6 +1,5 @@ package com.structurizr.dsl; -import java.io.File; import java.util.ArrayList; import java.util.List; diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyParser.java index bb345bc40..97d3cbf61 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyParser.java @@ -1,10 +1,12 @@ package com.structurizr.dsl; +import com.structurizr.component.description.FirstSentenceDescriptionStrategy; +import com.structurizr.component.description.TruncatedDescriptionStrategy; import com.structurizr.component.filter.ExcludeTypesByRegexFilter; import com.structurizr.component.filter.IncludeTypesByRegexFilter; import com.structurizr.component.matcher.*; import com.structurizr.component.naming.DefaultPackageNamingStrategy; -import com.structurizr.component.naming.SimpleNamingStrategy; +import com.structurizr.component.naming.TypeNamingStrategy; import com.structurizr.component.naming.FullyQualifiedNamingStrategy; import com.structurizr.component.supporting.*; @@ -39,10 +41,15 @@ final class ComponentFinderStrategyParser extends AbstractParser { private static final String SUPPORTING_TYPES_NONE = "none"; private static final String SUPPORTING_TYPES_GRAMMAR = "supportingTypes <" + String.join("|", List.of(SUPPORTING_TYPES_ALL_REFERENCED, SUPPORTING_TYPES_REFERENCED_IN_PACKAGE, SUPPORTING_TYPES_IN_PACKAGE, SUPPORTING_TYPES_UNDER_PACKAGE, SUPPORTING_TYPES_NONE)) + "> [parameters]"; - private static final String NAMING_NAME = "name"; - private static final String NAMING_FQN = "fqn"; - private static final String NAMING_PACKAGE = "package"; - private static final String NAMING_GRAMMAR = "naming <" + String.join("|", List.of(NAMING_NAME, NAMING_FQN, NAMING_PACKAGE)) + ">"; + private static final String NAME_TYPE_NAME = "type-name"; + private static final String NAME_FQN = "fqn"; + private static final String NAME_PACKAGE = "package"; + private static final String NAME_GRAMMAR = "name <" + String.join("|", List.of(NAME_TYPE_NAME, NAME_FQN, NAME_PACKAGE)) + ">"; + + private static final String DESCRIPTION_TRUNCATED = "truncated"; + private static final String DESCRIPTION_FIRST_SENTENCE = "first-sentence"; + private static final String DESCRIPTION_GRAMMAR = "description <" + String.join("|", List.of(DESCRIPTION_FIRST_SENTENCE, DESCRIPTION_TRUNCATED)) + ">"; + private static final String DESCRIPTION_TRUNCATED_GRAMMAR = "description truncated "; void parseTechnology(ComponentFinderStrategyDslContext context, Tokens tokens) { if (tokens.size() != 2) { @@ -50,7 +57,7 @@ void parseTechnology(ComponentFinderStrategyDslContext context, Tokens tokens) { } String name = tokens.get(1); - context.getComponentFinderStrategyBuilder().asTechnology(name); + context.getComponentFinderStrategyBuilder().forTechnology(name); } void parseMatcher(ComponentFinderStrategyDslContext context, Tokens tokens, File dslFile) { @@ -181,24 +188,51 @@ void parseSupportingTypes(ComponentFinderStrategyDslContext context, Tokens toke } } - void parseNaming(ComponentFinderStrategyDslContext context, Tokens tokens, File dslFile) { + void parseName(ComponentFinderStrategyDslContext context, Tokens tokens, File dslFile) { if (tokens.size() < 2) { - throw new RuntimeException("Too few tokens, expected: " + NAMING_GRAMMAR); + throw new RuntimeException("Too few tokens, expected: " + NAME_GRAMMAR); } String type = tokens.get(1).toLowerCase(); switch (type) { - case NAMING_NAME: - context.getComponentFinderStrategyBuilder().namedBy(new SimpleNamingStrategy()); + case NAME_TYPE_NAME: + context.getComponentFinderStrategyBuilder().withName(new TypeNamingStrategy()); + break; + case NAME_FQN: + context.getComponentFinderStrategyBuilder().withName(new FullyQualifiedNamingStrategy()); break; - case NAMING_FQN: - context.getComponentFinderStrategyBuilder().namedBy(new FullyQualifiedNamingStrategy()); + case NAME_PACKAGE: + context.getComponentFinderStrategyBuilder().withName(new DefaultPackageNamingStrategy()); + break; + default: + throw new IllegalArgumentException("Unknown name strategy: " + type); + } + } + + void parseDescription(ComponentFinderStrategyDslContext context, Tokens tokens, File dslFile) { + if (tokens.size() < 2) { + throw new RuntimeException("Too few tokens, expected: " + DESCRIPTION_GRAMMAR); + } + + String type = tokens.get(1).toLowerCase(); + switch (type) { + case DESCRIPTION_FIRST_SENTENCE: + context.getComponentFinderStrategyBuilder().withDescription(new FirstSentenceDescriptionStrategy()); break; - case NAMING_PACKAGE: - context.getComponentFinderStrategyBuilder().namedBy(new DefaultPackageNamingStrategy()); + case DESCRIPTION_TRUNCATED: + if (tokens.size() < 3) { + throw new RuntimeException("Too few tokens, expected: " + DESCRIPTION_TRUNCATED_GRAMMAR); + } + + try { + int maxLength = Integer.parseInt(tokens.get(2)); + context.getComponentFinderStrategyBuilder().withDescription(new TruncatedDescriptionStrategy(maxLength)); + } catch (NumberFormatException e) { + throw new RuntimeException("Max length must be an integer"); + } break; default: - throw new IllegalArgumentException("Unknown naming strategy: " + type); + throw new IllegalArgumentException("Unknown description strategy: " + type); } } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index c0ec6c095..6ed663556 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -450,8 +450,11 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn } else if (COMPONENT_FINDER_STRATEGY_SUPPORTING_TYPES_TOKEN.equalsIgnoreCase(firstToken) && inContext(ComponentFinderStrategyDslContext.class)) { new ComponentFinderStrategyParser().parseSupportingTypes(getContext(ComponentFinderStrategyDslContext.class), tokens, dslFile); - } else if (COMPONENT_FINDER_STRATEGY_NAMING_TOKEN.equalsIgnoreCase(firstToken) && inContext(ComponentFinderStrategyDslContext.class)) { - new ComponentFinderStrategyParser().parseNaming(getContext(ComponentFinderStrategyDslContext.class), tokens, dslFile); + } else if (COMPONENT_FINDER_STRATEGY_NAME_TOKEN.equalsIgnoreCase(firstToken) && inContext(ComponentFinderStrategyDslContext.class)) { + new ComponentFinderStrategyParser().parseName(getContext(ComponentFinderStrategyDslContext.class), tokens, dslFile); + + } else if (COMPONENT_FINDER_STRATEGY_DESCRIPTION_TOKEN.equalsIgnoreCase(firstToken) && inContext(ComponentFinderStrategyDslContext.class)) { + new ComponentFinderStrategyParser().parseDescription(getContext(ComponentFinderStrategyDslContext.class), tokens, dslFile); } else if (COMPONENT_FINDER_STRATEGY_FOREACH_TOKEN.equalsIgnoreCase(firstToken) && inContext(ComponentFinderStrategyDslContext.class)) { if (shouldStartContext(tokens)) { diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java index 5e26d24df..f8d11e5ec 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java @@ -121,7 +121,8 @@ class StructurizrDslTokens { static final String COMPONENT_FINDER_STRATEGY_MATCHER_TOKEN = "matcher"; static final String COMPONENT_FINDER_STRATEGY_FILTER_TOKEN = "filter"; static final String COMPONENT_FINDER_STRATEGY_SUPPORTING_TYPES_TOKEN = "supportingTypes"; - static final String COMPONENT_FINDER_STRATEGY_NAMING_TOKEN = "naming"; + static final String COMPONENT_FINDER_STRATEGY_NAME_TOKEN = "name"; + static final String COMPONENT_FINDER_STRATEGY_DESCRIPTION_TOKEN = "description"; static final String COMPONENT_FINDER_STRATEGY_FOREACH_TOKEN = "forEach"; } \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderStrategyParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderStrategyParserTests.java index beaa5db9f..7dee0f92c 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderStrategyParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderStrategyParserTests.java @@ -1,7 +1,6 @@ package com.structurizr.dsl; import com.structurizr.component.ComponentFinderBuilder; -import com.structurizr.component.matcher.AnnotationTypeMatcher; import com.structurizr.component.matcher.NameSuffixTypeMatcher; import org.junit.jupiter.api.Test; @@ -39,7 +38,7 @@ void test_parseTechnology_ThrowsAnException_WhenThereAreTooManyTokens() { @Test void test_parseTechnology() { parser.parseTechnology(context, tokens("technology", "name")); - assertEquals("ComponentFinderStrategyBuilder{technology='name', typeMatcher=null, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); + assertEquals("ComponentFinderStrategyBuilder{technology='name', typeMatcher=null, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); } @Test @@ -65,7 +64,7 @@ void test_parseMatcher_WhenTheAnnotationTypeMatcherIsUsedAndThereAreTooFewTokens @Test void test_parseMatcher_WhenTheAnnotationTypeMatcherIsUsed() { parser.parseMatcher(context, tokens("matcher", "annotation", "com.example.Component"), null); - assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=AnnotationTypeMatcher{annotationType='Lcom/example/Component;'}, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); + assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=AnnotationTypeMatcher{annotationType='Lcom/example/Component;'}, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); } @Test @@ -81,7 +80,7 @@ void test_parseMatcher_WhenTheExtendsTypeMatcherIsUsedAndThereAreTooFewTokens() @Test void test_parseMatcher_WhenTheExtendsTypeMatcherIsUsed() { parser.parseMatcher(context, tokens("matcher", "extends", "com.example.Component"), null); - assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=ExtendsTypeMatcher{className='com.example.Component'}, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); + assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=ExtendsTypeMatcher{className='com.example.Component'}, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); } @Test @@ -97,7 +96,7 @@ void test_parseMatcher_WhenTheImplementsTypeMatcherIsUsedAndThereAreTooFewTokens @Test void test_parseMatcher_WhenTheImplementsTypeMatcherIsUsed() { parser.parseMatcher(context, tokens("matcher", "implements", "com.example.Component"), null); - assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=ImplementsTypeMatcher{interfaceName='com.example.Component'}, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); + assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=ImplementsTypeMatcher{interfaceName='com.example.Component'}, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); } @Test @@ -113,7 +112,7 @@ void test_parseMatcher_WhenTheNameSuffixTypeMatcherIsUsedAndThereAreTooFewTokens @Test void test_parseMatcher_WhenTheNameSuffixTypeMatcherIsUsed() { parser.parseMatcher(context, tokens("matcher", "name-suffix", "Component"), null); - assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=NameSuffixTypeMatcher{suffix='Component'}, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); + assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=NameSuffixTypeMatcher{suffix='Component'}, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); } @Test @@ -129,7 +128,7 @@ void test_parseMatcher_WhenTheFullyQualifiedNameRegexTypeMatcherIsUsedAndThereAr @Test void test_parseMatcher_WhenTheRegexTypeMatcherIsUsed() { parser.parseMatcher(context, tokens("matcher", "fqn-regex", ".*Component"), null); - assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=RegexTypeMatcher{regex='.*Component'}, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); + assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=RegexTypeMatcher{regex='.*Component'}, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); } @Test @@ -145,13 +144,13 @@ void test_parseMatcher_WhenACustomTypeMatcherIsUsedButCannotBeLoaded() { @Test void test_parseMatcher_WhenACustomTypeMatcherIsUsedWithoutParameters() { parser.parseMatcher(context, tokens("matcher", "com.structurizr.dsl.example.CustomTypeMatcher"), new File(".")); - assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=CustomTypeMatcher{}, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); + assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=CustomTypeMatcher{}, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); } @Test void test_parseMatcher_WhenACustomTypeMatcherIsUsedWithAParameter() { parser.parseMatcher(context, tokens("matcher", NameSuffixTypeMatcher.class.getCanonicalName(), "Component"), new File(".")); - assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=NameSuffixTypeMatcher{suffix='Component'}, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); + assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=NameSuffixTypeMatcher{suffix='Component'}, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); } @Test @@ -187,13 +186,13 @@ void test_parseFilter_ThrowsAnException_WhenInvalidModeIsSpecified() { @Test void test_parseFilter_WhenIncludeFullyQualifiedNameRegexTypeFilterIsUsed() { parser.parseFilter(context, tokens("filter", "include", "fqn-regex", ".*"), new File(".")); - assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=null, typeFilter=IncludeTypesByRegexFilter{regex='.*'}, supportingTypesStrategy=null, namingStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); + assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=null, typeFilter=IncludeTypesByRegexFilter{regex='.*'}, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); } @Test void test_parseFilter_WhenExcludeFullyQualifiedNameRegexTypeFilterIsUsed() { parser.parseFilter(context, tokens("filter", "exclude", "fqn-regex", ".*"), new File(".")); - assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=null, typeFilter=ExcludeTypesByRegexFilter{regex='.*'}, supportingTypesStrategy=null, namingStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); + assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=null, typeFilter=ExcludeTypesByRegexFilter{regex='.*'}, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); } @Test @@ -207,13 +206,75 @@ void test_parseSupportingTypes_ThrowsAnException_WhenNoTypeIsSpecified() { } @Test - void test_parseNaming_ThrowsAnException_WhenNoTypeIsSpecified() { + void test_parseName_ThrowsAnException_WhenNoTypeIsSpecified() { try { - parser.parseNaming(context, tokens("naming"), null); + parser.parseName(context, tokens("name"), null); fail(); } catch (Exception e) { - assertEquals("Too few tokens, expected: naming ", e.getMessage()); + assertEquals("Too few tokens, expected: name ", e.getMessage()); } } + @Test + void test_parseName() { + parser.parseName(context, tokens("name", "type-name"), null); + assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=null, typeFilter=null, supportingTypesStrategy=null, namingStrategy=TypeNamingStrategy{}, descriptionStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); + } + + @Test + void test_parseDescription_ThrowsAnException_WhenNoTypeIsSpecified() { + try { + parser.parseDescription(context, tokens("description"), null); + fail(); + } catch (Exception e) { + assertEquals("Too few tokens, expected: description ", e.getMessage()); + } + } + + @Test + void test_parseDescription_FirstSentence() { + parser.parseDescription(context, tokens("description", "first-sentence"), null); + assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=null, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=FirstSentenceDescriptionStrategy{}, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); + } + + @Test + void test_parseDescription_Truncated_ThrowsAnException_WhenNoMaxLengthIsSpecified() { + try { + parser.parseDescription(context, tokens("description", "truncated"), null); + fail(); + } catch (Exception e) { + assertEquals("Too few tokens, expected: description truncated ", e.getMessage()); + } + } + + @Test + void test_parseDescription_Truncated_ThrowsAnException_WhenAnInvalidMaxLengthIsSpecified() { + try { + parser.parseDescription(context, tokens("description", "truncated", "invalid"), null); + fail(); + } catch (Exception e) { + assertEquals("Max length must be an integer", e.getMessage()); + } + + try { + parser.parseDescription(context, tokens("description", "truncated", "-1"), null); + fail(); + } catch (Exception e) { + assertEquals("Max length must be greater than 0", e.getMessage()); + } + + try { + parser.parseDescription(context, tokens("description", "truncated", "0"), null); + fail(); + } catch (Exception e) { + assertEquals("Max length must be greater than 0", e.getMessage()); + } + } + + @Test + void test_parseDescription_Truncated() { + parser.parseDescription(context, tokens("description", "truncated", "50"), null); + assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=null, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=TruncatedDescriptionStrategy{maxLength=50}, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); + } + } \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index 1d3665732..0dcdc835b 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -1232,6 +1232,7 @@ void springPetClinic() throws Exception { Component ownerRepository = webApplication.getComponentWithName("Owner Repository"); assertNotNull(ownerRepository); + assertEquals("Repository class for Owner domain objects All method names are compliant with Spring Data naming conventions so this interface can easily be extended for Spring Data.", ownerRepository.getDescription()); assertEquals("org.springframework.samples.petclinic.owner.OwnerRepository", ownerRepository.getProperties().get("component.type")); assertEquals(new File(springPetClinicHome, "src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java").getAbsolutePath(), ownerRepository.getProperties().get("component.src")); assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java", ownerRepository.getUrl()); diff --git a/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl b/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl index 8446171cb..fd2f20a78 100644 --- a/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl +++ b/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl @@ -34,6 +34,7 @@ workspace "Spring PetClinic" "A C4 model of the Spring PetClinic sample app (htt strategy { technology "Spring Data Repository" matcher implements "org.springframework.data.repository.Repository" + description first-sentence forEach { -> relationalDatabaseSchema "Reads from and writes to" tag "Spring Data Repository" From e12096e075bbdc0e13930acc357f327d1fd00951 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 17 Sep 2024 08:55:50 +0100 Subject: [PATCH 568/717] Extra assertions. --- .../src/test/java/com/structurizr/dsl/DslTests.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index 0dcdc835b..27204a8b6 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -1237,15 +1237,16 @@ void springPetClinic() throws Exception { assertEquals(new File(springPetClinicHome, "src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java").getAbsolutePath(), ownerRepository.getProperties().get("component.src")); assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java", ownerRepository.getUrl()); assertSame(ownerRepository, parser.getIdentifiersRegister().getElement("springPetClinic.webApplication.ownerrepository")); - assertTrue(ownerRepository.hasEfferentRelationshipWith(relationalDatabaseSchema)); + assertTrue(ownerRepository.hasEfferentRelationshipWith(relationalDatabaseSchema, "Reads from and writes to")); Component vetRepository = webApplication.getComponentWithName("Vet Repository"); assertNotNull(vetRepository); + assertEquals("Repository class for Vet domain objects All method names are compliant with Spring Data naming conventions so this interface can easily be extended for Spring Data.", vetRepository.getDescription()); assertEquals("org.springframework.samples.petclinic.vet.VetRepository", vetRepository.getProperties().get("component.type")); assertEquals(new File(springPetClinicHome, "src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java").getAbsolutePath(), vetRepository.getProperties().get("component.src")); assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java", vetRepository.getUrl()); assertSame(vetRepository, parser.getIdentifiersRegister().getElement("springPetClinic.webApplication.vetrepository")); - assertTrue(vetRepository.hasEfferentRelationshipWith(relationalDatabaseSchema)); + assertTrue(vetRepository.hasEfferentRelationshipWith(relationalDatabaseSchema, "Reads from and writes to")); assertTrue(welcomeController.getRelationships().isEmpty()); assertNotNull(petController.getEfferentRelationshipWith(ownerRepository)); From 3872cfeb004159cad39f90d42c38c86d1a3cdcf6 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 17 Sep 2024 10:54:54 +0100 Subject: [PATCH 569/717] More tidy up + adds a way to customise component URLs without using a script. --- .gitignore | 4 +- .../component/ComponentFinder.java | 1 + .../component/ComponentFinderStrategy.java | 9 ++-- .../ComponentFinderStrategyBuilder.java | 28 ++++++++-- .../component/DiscoveredComponent.java | 9 ++++ .../provider/SourceDirectoryTypeProvider.java | 16 +++++- .../component/url/DefaultUrlStrategy.java | 17 ++++++ .../component/url/PrefixUrlStrategy.java | 34 ++++++++++++ .../component/url/UrlStrategy.java | 9 ++++ .../ComponentFinderStrategyBuilderTests.java | 16 +++--- .../dsl/ComponentFinderStrategyParser.java | 27 +++++++++- .../structurizr/dsl/StructurizrDslParser.java | 3 ++ .../structurizr/dsl/StructurizrDslTokens.java | 1 + .../ComponentFinderStrategyParserTests.java | 52 ++++++++++++++----- .../java/com/structurizr/dsl/DslTests.java | 14 ++--- .../dsl/spring-petclinic/workspace.dsl | 6 +-- 16 files changed, 204 insertions(+), 42 deletions(-) create mode 100644 structurizr-component/src/main/java/com/structurizr/component/url/DefaultUrlStrategy.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/url/PrefixUrlStrategy.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/url/UrlStrategy.java diff --git a/.gitignore b/.gitignore index 30458f2b7..a5e1d9c61 100644 --- a/.gitignore +++ b/.gitignore @@ -28,4 +28,6 @@ bin hs_err_pid* structurizr-dsl/src/test/resources/dsl/spring-petclinic/.structurizr -structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.json \ No newline at end of file +structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.json + +**/structurizr.properties \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinder.java b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinder.java index 541d2c082..1055c453e 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinder.java +++ b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinder.java @@ -119,6 +119,7 @@ public Set findComponents() { } component.setDescription(discoveredComponent.getDescription()); component.setTechnology(discoveredComponent.getTechnology()); + component.setUrl(discoveredComponent.getUrl()); componentMap.put(discoveredComponent, component); componentSet.add(component); } diff --git a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategy.java index 774f10ba5..d1dc8f87a 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategy.java +++ b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategy.java @@ -1,15 +1,14 @@ package com.structurizr.component; -import com.structurizr.component.description.DefaultDescriptionStrategy; import com.structurizr.component.description.DescriptionStrategy; import com.structurizr.component.filter.TypeFilter; import com.structurizr.component.matcher.TypeMatcher; import com.structurizr.component.naming.NamingStrategy; import com.structurizr.component.supporting.SupportingTypesStrategy; +import com.structurizr.component.url.UrlStrategy; import com.structurizr.component.visitor.ComponentVisitor; import com.structurizr.model.Component; -import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Set; @@ -30,15 +29,17 @@ public final class ComponentFinderStrategy { private final SupportingTypesStrategy supportingTypesStrategy; private final NamingStrategy namingStrategy; private final DescriptionStrategy descriptionStrategy; + private final UrlStrategy urlStrategy; private final ComponentVisitor componentVisitor; - ComponentFinderStrategy(String technology, TypeMatcher typeMatcher, TypeFilter typeFilter, SupportingTypesStrategy supportingTypesStrategy, NamingStrategy namingStrategy, DescriptionStrategy descriptionStrategy, ComponentVisitor componentVisitor) { + ComponentFinderStrategy(String technology, TypeMatcher typeMatcher, TypeFilter typeFilter, SupportingTypesStrategy supportingTypesStrategy, NamingStrategy namingStrategy, DescriptionStrategy descriptionStrategy, UrlStrategy urlStrategy, ComponentVisitor componentVisitor) { this.technology = technology; this.typeMatcher = typeMatcher; this.typeFilter = typeFilter; this.supportingTypesStrategy = supportingTypesStrategy; this.namingStrategy = namingStrategy; this.descriptionStrategy = descriptionStrategy; + this.urlStrategy = urlStrategy; this.componentVisitor = componentVisitor; } @@ -51,6 +52,7 @@ Set findComponents(TypeRepository typeRepository) { DiscoveredComponent component = new DiscoveredComponent(namingStrategy.nameOf(type), type); component.setDescription(descriptionStrategy.descriptionOf(type)); component.setTechnology(this.technology); + component.setUrl(urlStrategy.urlOf(type)); component.setComponentFinderStrategy(this); components.add(component); @@ -76,6 +78,7 @@ public String toString() { ", supportingTypesStrategy=" + supportingTypesStrategy + ", namingStrategy=" + namingStrategy + ", descriptionStrategy=" + descriptionStrategy + + ", urlStrategy=" + urlStrategy + ", componentVisitor=" + componentVisitor + '}'; } diff --git a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategyBuilder.java b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategyBuilder.java index eab3e25c2..6f692dc30 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategyBuilder.java +++ b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategyBuilder.java @@ -9,6 +9,8 @@ import com.structurizr.component.naming.NamingStrategy; import com.structurizr.component.supporting.DefaultSupportingTypesStrategy; import com.structurizr.component.supporting.SupportingTypesStrategy; +import com.structurizr.component.url.DefaultUrlStrategy; +import com.structurizr.component.url.UrlStrategy; import com.structurizr.component.visitor.ComponentVisitor; import com.structurizr.component.visitor.DefaultComponentVisitor; import com.structurizr.util.StringUtils; @@ -18,12 +20,13 @@ */ public final class ComponentFinderStrategyBuilder { - private String technology; private TypeMatcher typeMatcher; private TypeFilter typeFilter; private SupportingTypesStrategy supportingTypesStrategy; private NamingStrategy namingStrategy; private DescriptionStrategy descriptionStrategy; + private String technology; + private UrlStrategy urlStrategy; private ComponentVisitor componentVisitor; public ComponentFinderStrategyBuilder() { @@ -99,7 +102,7 @@ public ComponentFinderStrategyBuilder withDescription(DescriptionStrategy descri return this; } - public ComponentFinderStrategyBuilder forTechnology(String technology) { + public ComponentFinderStrategyBuilder withTechnology(String technology) { if (StringUtils.isNullOrEmpty(technology)) { throw new IllegalArgumentException("A technology must be provided"); } @@ -113,6 +116,20 @@ public ComponentFinderStrategyBuilder forTechnology(String technology) { return this; } + public ComponentFinderStrategyBuilder withUrl(UrlStrategy urlStrategy) { + if (urlStrategy == null) { + throw new IllegalArgumentException("A URL strategy must be provided"); + } + + if (this.urlStrategy != null) { + throw new IllegalArgumentException("A url strategy has already been configured"); + } + + this.urlStrategy = urlStrategy; + + return this; + } + public ComponentFinderStrategyBuilder forEach(ComponentVisitor componentVisitor) { if (componentVisitor == null) { throw new IllegalArgumentException("A component visitor must be provided"); @@ -148,11 +165,15 @@ public ComponentFinderStrategy build() { descriptionStrategy = new DefaultDescriptionStrategy(); } + if (urlStrategy == null) { + urlStrategy = new DefaultUrlStrategy(); + } + if (componentVisitor == null) { componentVisitor = new DefaultComponentVisitor(); } - return new ComponentFinderStrategy(technology, typeMatcher, typeFilter, supportingTypesStrategy, namingStrategy, descriptionStrategy, componentVisitor); + return new ComponentFinderStrategy(technology, typeMatcher, typeFilter, supportingTypesStrategy, namingStrategy, descriptionStrategy, urlStrategy, componentVisitor); } @Override @@ -164,6 +185,7 @@ public String toString() { ", supportingTypesStrategy=" + supportingTypesStrategy + ", namingStrategy=" + namingStrategy + ", descriptionStrategy=" + descriptionStrategy + + ", urlStrategy=" + urlStrategy + ", componentVisitor=" + componentVisitor + '}'; } diff --git a/structurizr-component/src/main/java/com/structurizr/component/DiscoveredComponent.java b/structurizr-component/src/main/java/com/structurizr/component/DiscoveredComponent.java index 79bba4d51..70443932c 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/DiscoveredComponent.java +++ b/structurizr-component/src/main/java/com/structurizr/component/DiscoveredComponent.java @@ -9,6 +9,7 @@ final class DiscoveredComponent { private final String name; private String description; private String technology; + private String url; private final Set supportingTypes = new HashSet<>(); private ComponentFinderStrategy componentFinderStrategy; @@ -46,6 +47,14 @@ void setTechnology(String technology) { this.technology = technology; } + String getUrl() { + return url; + } + + void setUrl(String url) { + this.url = url; + } + Set getSupportingTypes() { return new HashSet<>(supportingTypes); } diff --git a/structurizr-component/src/main/java/com/structurizr/component/provider/SourceDirectoryTypeProvider.java b/structurizr-component/src/main/java/com/structurizr/component/provider/SourceDirectoryTypeProvider.java index 6bce2bb1c..96f934f5c 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/provider/SourceDirectoryTypeProvider.java +++ b/structurizr-component/src/main/java/com/structurizr/component/provider/SourceDirectoryTypeProvider.java @@ -72,7 +72,7 @@ public void visit(ClassOrInterfaceDeclaration n, Object arg) { if (n.getFullyQualifiedName().isPresent()) { String fullyQualifiedName = n.getFullyQualifiedName().get(); Type type = new Type(fullyQualifiedName); - type.setSource(path.getAbsolutePath()); + type.setSource(relativePath(path)); if (n.getComment().isPresent() && n.getComment().get() instanceof JavadocComment) { JavadocComment javadocComment = (JavadocComment) n.getComment().get(); @@ -93,7 +93,7 @@ public void visit(PackageDeclaration n, Object arg) { String fullyQualifiedName = n.getName().asString() + PACKAGE_INFO_SUFFIX; Type type = new Type(fullyQualifiedName); - type.setSource(path.getAbsolutePath()); + type.setSource(relativePath(path)); Node rootNode = n.findRootNode(); if (rootNode != null && rootNode.getComment().isPresent() && rootNode.getComment().get() instanceof JavadocComment) { @@ -116,4 +116,16 @@ public void visit(PackageDeclaration n, Object arg) { } } + private String relativePath(File path) { + String relativePath = path.getAbsolutePath().replace(directory.getAbsolutePath(), ""); + + String pathSeparator = System.getProperty("file.separator"); + + if (relativePath.startsWith(pathSeparator)) { + relativePath = relativePath.substring(1); + } + + return relativePath; + } + } \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/url/DefaultUrlStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/url/DefaultUrlStrategy.java new file mode 100644 index 000000000..445b4c4cc --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/url/DefaultUrlStrategy.java @@ -0,0 +1,17 @@ +package com.structurizr.component.url; + +import com.structurizr.component.Type; + +public class DefaultUrlStrategy implements UrlStrategy { + + @Override + public String urlOf(Type type) { + return null; + } + + @Override + public String toString() { + return "DefaultUrlStrategy{}"; + } + +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/url/PrefixUrlStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/url/PrefixUrlStrategy.java new file mode 100644 index 000000000..503be9b2c --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/url/PrefixUrlStrategy.java @@ -0,0 +1,34 @@ +package com.structurizr.component.url; + +import com.structurizr.component.Type; +import com.structurizr.util.StringUtils; + +public class PrefixUrlStrategy implements UrlStrategy { + + private final String prefix; + + public PrefixUrlStrategy(String prefix) { + if (StringUtils.isNullOrEmpty(prefix)) { + throw new IllegalArgumentException("A prefix must be supplied"); + } + + if (!prefix.endsWith("/")) { + prefix = prefix + "/"; + } + + this.prefix = prefix; + } + + @Override + public String urlOf(Type type) { + return prefix + type.getSource(); + } + + @Override + public String toString() { + return "PrefixUrlStrategy{" + + "prefix='" + prefix + '\'' + + '}'; + } + +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/url/UrlStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/url/UrlStrategy.java new file mode 100644 index 000000000..7d91b07ae --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/url/UrlStrategy.java @@ -0,0 +1,9 @@ +package com.structurizr.component.url; + +import com.structurizr.component.Type; + +public interface UrlStrategy { + + String urlOf(Type type); + +} \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/ComponentFinderStrategyBuilderTests.java b/structurizr-component/src/test/java/com/structurizr/component/ComponentFinderStrategyBuilderTests.java index cb770a312..e71590dfe 100644 --- a/structurizr-component/src/test/java/com/structurizr/component/ComponentFinderStrategyBuilderTests.java +++ b/structurizr-component/src/test/java/com/structurizr/component/ComponentFinderStrategyBuilderTests.java @@ -116,9 +116,9 @@ void withDescription_ThrowsAnException_WhenCalledTwice() { } @Test - void forTechnology_ThrowsAnException_WhenPassedNull() { + void withTechnology_ThrowsAnException_WhenPassedNull() { try { - new ComponentFinderStrategyBuilder().forTechnology(null); + new ComponentFinderStrategyBuilder().withTechnology(null); fail(); } catch (Exception e) { assertEquals("A technology must be provided", e.getMessage()); @@ -126,9 +126,9 @@ void forTechnology_ThrowsAnException_WhenPassedNull() { } @Test - void forTechnology_ThrowsAnException_WhenPassedAnEmptyString() { + void withTechnology_ThrowsAnException_WhenPassedAnEmptyString() { try { - new ComponentFinderStrategyBuilder().forTechnology(""); + new ComponentFinderStrategyBuilder().withTechnology(""); fail(); } catch (Exception e) { assertEquals("A technology must be provided", e.getMessage()); @@ -136,9 +136,9 @@ void forTechnology_ThrowsAnException_WhenPassedAnEmptyString() { } @Test - void forTechnology_ThrowsAnException_WhenCalledTwice() { + void withTechnology_ThrowsAnException_WhenCalledTwice() { try { - new ComponentFinderStrategyBuilder().forTechnology("X").forTechnology("Y"); + new ComponentFinderStrategyBuilder().withTechnology("X").withTechnology("Y"); fail(); } catch (Exception e) { assertEquals("A technology has already been configured", e.getMessage()); @@ -158,14 +158,14 @@ void build_ThrowsAnException_WhenATypeMatcherHasNotBeenConfigured() { @Test void build() { ComponentFinderStrategy strategy = new ComponentFinderStrategyBuilder() - .forTechnology("Spring MVC Controller") + .withTechnology("Spring MVC Controller") .matchedBy(new NameSuffixTypeMatcher("Controller")) .filteredBy(new IncludeTypesByRegexFilter("com.example.web.\\.*")) .withName(new TypeNamingStrategy()) .withDescription(new FirstSentenceDescriptionStrategy()) .build(); - assertEquals("ComponentFinderStrategy{technology='Spring MVC Controller', typeMatcher=NameSuffixTypeMatcher{suffix='Controller'}, typeFilter=IncludeTypesByRegexFilter{regex='com.example.web.\\.*'}, supportingTypesStrategy=DefaultSupportingTypesStrategy{}, namingStrategy=TypeNamingStrategy{}, descriptionStrategy=FirstSentenceDescriptionStrategy{}, componentVisitor=DefaultComponentVisitor{}}", strategy.toString()); + assertEquals("ComponentFinderStrategy{technology='Spring MVC Controller', typeMatcher=NameSuffixTypeMatcher{suffix='Controller'}, typeFilter=IncludeTypesByRegexFilter{regex='com.example.web.\\.*'}, supportingTypesStrategy=DefaultSupportingTypesStrategy{}, namingStrategy=TypeNamingStrategy{}, descriptionStrategy=FirstSentenceDescriptionStrategy{}, urlStrategy=DefaultUrlStrategy{}, componentVisitor=DefaultComponentVisitor{}}", strategy.toString()); } } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyParser.java index 97d3cbf61..f5b566f8d 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyParser.java @@ -9,6 +9,7 @@ import com.structurizr.component.naming.TypeNamingStrategy; import com.structurizr.component.naming.FullyQualifiedNamingStrategy; import com.structurizr.component.supporting.*; +import com.structurizr.component.url.PrefixUrlStrategy; import java.io.File; import java.util.List; @@ -51,13 +52,17 @@ final class ComponentFinderStrategyParser extends AbstractParser { private static final String DESCRIPTION_GRAMMAR = "description <" + String.join("|", List.of(DESCRIPTION_FIRST_SENTENCE, DESCRIPTION_TRUNCATED)) + ">"; private static final String DESCRIPTION_TRUNCATED_GRAMMAR = "description truncated "; + private static final String URL_PREFIX = "prefix"; + private static final String URL_GRAMMAR = "url <" + String.join("|", List.of(URL_PREFIX)) + ">"; + private static final String URL_PREFIX_GRAMMAR = "url prefix "; + void parseTechnology(ComponentFinderStrategyDslContext context, Tokens tokens) { if (tokens.size() != 2) { throw new RuntimeException("Expected: " + TECHNOLOGY_GRAMMAR); } String name = tokens.get(1); - context.getComponentFinderStrategyBuilder().forTechnology(name); + context.getComponentFinderStrategyBuilder().withTechnology(name); } void parseMatcher(ComponentFinderStrategyDslContext context, Tokens tokens, File dslFile) { @@ -236,4 +241,24 @@ void parseDescription(ComponentFinderStrategyDslContext context, Tokens tokens, } } + void parseUrl(ComponentFinderStrategyDslContext context, Tokens tokens, File dslFile) { + if (tokens.size() < 2) { + throw new RuntimeException("Too few tokens, expected: " + URL_GRAMMAR); + } + + String type = tokens.get(1).toLowerCase(); + switch (type) { + case URL_PREFIX: + if (tokens.size() < 3) { + throw new RuntimeException("Too few tokens, expected: " + URL_PREFIX_GRAMMAR); + } + + String prefix = tokens.get(2); + context.getComponentFinderStrategyBuilder().withUrl(new PrefixUrlStrategy(prefix)); + break; + default: + throw new IllegalArgumentException("Unknown URL strategy: " + type); + } + } + } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index 6ed663556..abbaa8a65 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -456,6 +456,9 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn } else if (COMPONENT_FINDER_STRATEGY_DESCRIPTION_TOKEN.equalsIgnoreCase(firstToken) && inContext(ComponentFinderStrategyDslContext.class)) { new ComponentFinderStrategyParser().parseDescription(getContext(ComponentFinderStrategyDslContext.class), tokens, dslFile); + } else if (COMPONENT_FINDER_STRATEGY_URL_TOKEN.equalsIgnoreCase(firstToken) && inContext(ComponentFinderStrategyDslContext.class)) { + new ComponentFinderStrategyParser().parseUrl(getContext(ComponentFinderStrategyDslContext.class), tokens, dslFile); + } else if (COMPONENT_FINDER_STRATEGY_FOREACH_TOKEN.equalsIgnoreCase(firstToken) && inContext(ComponentFinderStrategyDslContext.class)) { if (shouldStartContext(tokens)) { startContext(new ComponentFinderStrategyForEachDslContext(getContext(ComponentFinderStrategyDslContext.class), this)); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java index f8d11e5ec..fa3829934 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java @@ -123,6 +123,7 @@ class StructurizrDslTokens { static final String COMPONENT_FINDER_STRATEGY_SUPPORTING_TYPES_TOKEN = "supportingTypes"; static final String COMPONENT_FINDER_STRATEGY_NAME_TOKEN = "name"; static final String COMPONENT_FINDER_STRATEGY_DESCRIPTION_TOKEN = "description"; + static final String COMPONENT_FINDER_STRATEGY_URL_TOKEN = "url"; static final String COMPONENT_FINDER_STRATEGY_FOREACH_TOKEN = "forEach"; } \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderStrategyParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderStrategyParserTests.java index 7dee0f92c..dac5d6733 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderStrategyParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderStrategyParserTests.java @@ -38,7 +38,7 @@ void test_parseTechnology_ThrowsAnException_WhenThereAreTooManyTokens() { @Test void test_parseTechnology() { parser.parseTechnology(context, tokens("technology", "name")); - assertEquals("ComponentFinderStrategyBuilder{technology='name', typeMatcher=null, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); + assertEquals("ComponentFinderStrategyBuilder{technology='name', typeMatcher=null, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=null, urlStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); } @Test @@ -64,7 +64,7 @@ void test_parseMatcher_WhenTheAnnotationTypeMatcherIsUsedAndThereAreTooFewTokens @Test void test_parseMatcher_WhenTheAnnotationTypeMatcherIsUsed() { parser.parseMatcher(context, tokens("matcher", "annotation", "com.example.Component"), null); - assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=AnnotationTypeMatcher{annotationType='Lcom/example/Component;'}, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); + assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=AnnotationTypeMatcher{annotationType='Lcom/example/Component;'}, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=null, urlStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); } @Test @@ -80,7 +80,7 @@ void test_parseMatcher_WhenTheExtendsTypeMatcherIsUsedAndThereAreTooFewTokens() @Test void test_parseMatcher_WhenTheExtendsTypeMatcherIsUsed() { parser.parseMatcher(context, tokens("matcher", "extends", "com.example.Component"), null); - assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=ExtendsTypeMatcher{className='com.example.Component'}, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); + assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=ExtendsTypeMatcher{className='com.example.Component'}, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=null, urlStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); } @Test @@ -96,7 +96,7 @@ void test_parseMatcher_WhenTheImplementsTypeMatcherIsUsedAndThereAreTooFewTokens @Test void test_parseMatcher_WhenTheImplementsTypeMatcherIsUsed() { parser.parseMatcher(context, tokens("matcher", "implements", "com.example.Component"), null); - assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=ImplementsTypeMatcher{interfaceName='com.example.Component'}, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); + assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=ImplementsTypeMatcher{interfaceName='com.example.Component'}, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=null, urlStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); } @Test @@ -112,7 +112,7 @@ void test_parseMatcher_WhenTheNameSuffixTypeMatcherIsUsedAndThereAreTooFewTokens @Test void test_parseMatcher_WhenTheNameSuffixTypeMatcherIsUsed() { parser.parseMatcher(context, tokens("matcher", "name-suffix", "Component"), null); - assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=NameSuffixTypeMatcher{suffix='Component'}, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); + assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=NameSuffixTypeMatcher{suffix='Component'}, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=null, urlStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); } @Test @@ -128,7 +128,7 @@ void test_parseMatcher_WhenTheFullyQualifiedNameRegexTypeMatcherIsUsedAndThereAr @Test void test_parseMatcher_WhenTheRegexTypeMatcherIsUsed() { parser.parseMatcher(context, tokens("matcher", "fqn-regex", ".*Component"), null); - assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=RegexTypeMatcher{regex='.*Component'}, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); + assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=RegexTypeMatcher{regex='.*Component'}, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=null, urlStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); } @Test @@ -144,13 +144,13 @@ void test_parseMatcher_WhenACustomTypeMatcherIsUsedButCannotBeLoaded() { @Test void test_parseMatcher_WhenACustomTypeMatcherIsUsedWithoutParameters() { parser.parseMatcher(context, tokens("matcher", "com.structurizr.dsl.example.CustomTypeMatcher"), new File(".")); - assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=CustomTypeMatcher{}, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); + assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=CustomTypeMatcher{}, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=null, urlStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); } @Test void test_parseMatcher_WhenACustomTypeMatcherIsUsedWithAParameter() { parser.parseMatcher(context, tokens("matcher", NameSuffixTypeMatcher.class.getCanonicalName(), "Component"), new File(".")); - assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=NameSuffixTypeMatcher{suffix='Component'}, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); + assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=NameSuffixTypeMatcher{suffix='Component'}, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=null, urlStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); } @Test @@ -186,13 +186,13 @@ void test_parseFilter_ThrowsAnException_WhenInvalidModeIsSpecified() { @Test void test_parseFilter_WhenIncludeFullyQualifiedNameRegexTypeFilterIsUsed() { parser.parseFilter(context, tokens("filter", "include", "fqn-regex", ".*"), new File(".")); - assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=null, typeFilter=IncludeTypesByRegexFilter{regex='.*'}, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); + assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=null, typeFilter=IncludeTypesByRegexFilter{regex='.*'}, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=null, urlStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); } @Test void test_parseFilter_WhenExcludeFullyQualifiedNameRegexTypeFilterIsUsed() { parser.parseFilter(context, tokens("filter", "exclude", "fqn-regex", ".*"), new File(".")); - assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=null, typeFilter=ExcludeTypesByRegexFilter{regex='.*'}, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); + assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=null, typeFilter=ExcludeTypesByRegexFilter{regex='.*'}, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=null, urlStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); } @Test @@ -218,7 +218,7 @@ void test_parseName_ThrowsAnException_WhenNoTypeIsSpecified() { @Test void test_parseName() { parser.parseName(context, tokens("name", "type-name"), null); - assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=null, typeFilter=null, supportingTypesStrategy=null, namingStrategy=TypeNamingStrategy{}, descriptionStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); + assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=null, typeFilter=null, supportingTypesStrategy=null, namingStrategy=TypeNamingStrategy{}, descriptionStrategy=null, urlStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); } @Test @@ -234,7 +234,7 @@ void test_parseDescription_ThrowsAnException_WhenNoTypeIsSpecified() { @Test void test_parseDescription_FirstSentence() { parser.parseDescription(context, tokens("description", "first-sentence"), null); - assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=null, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=FirstSentenceDescriptionStrategy{}, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); + assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=null, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=FirstSentenceDescriptionStrategy{}, urlStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); } @Test @@ -274,7 +274,33 @@ void test_parseDescription_Truncated_ThrowsAnException_WhenAnInvalidMaxLengthIsS @Test void test_parseDescription_Truncated() { parser.parseDescription(context, tokens("description", "truncated", "50"), null); - assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=null, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=TruncatedDescriptionStrategy{maxLength=50}, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); + assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=null, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=TruncatedDescriptionStrategy{maxLength=50}, urlStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); + } + + @Test + void test_parseUrl_ThrowsAnException_WhenNoTypeIsSpecified() { + try { + parser.parseUrl(context, tokens("url"), null); + fail(); + } catch (Exception e) { + assertEquals("Too few tokens, expected: url ", e.getMessage()); + } + } + + @Test + void test_parseUrl_Prefix_ThrowsAnException_WhenNoPrefixIsSpecified() { + try { + parser.parseUrl(context, tokens("url", "prefix"), null); + fail(); + } catch (Exception e) { + assertEquals("Too few tokens, expected: url prefix ", e.getMessage()); + } + } + + @Test + void test_parseUrl_Prefix() { + parser.parseUrl(context, tokens("url", "prefix", "https://example.com"), null); + assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=null, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=null, urlStrategy=PrefixUrlStrategy{prefix='https://example.com/'}, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); } } \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index 27204a8b6..e5f22175b 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -1193,7 +1193,7 @@ void springPetClinic() throws Exception { Component welcomeController = webApplication.getComponentWithName("Welcome Controller"); assertNotNull(welcomeController); assertEquals("org.springframework.samples.petclinic.system.WelcomeController", welcomeController.getProperties().get("component.type")); - assertEquals(new File(springPetClinicHome, "src/main/java/org/springframework/samples/petclinic/system/WelcomeController.java").getAbsolutePath(), welcomeController.getProperties().get("component.src")); + assertEquals("org/springframework/samples/petclinic/system/WelcomeController.java", welcomeController.getProperties().get("component.src")); assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/system/WelcomeController.java", welcomeController.getUrl()); assertSame(welcomeController, parser.getIdentifiersRegister().getElement("springPetClinic.webApplication.welcomecontroller")); assertTrue(clinicEmployee.hasEfferentRelationshipWith(welcomeController)); @@ -1201,7 +1201,7 @@ void springPetClinic() throws Exception { Component ownerController = webApplication.getComponentWithName("Owner Controller"); assertNotNull(ownerController); assertEquals("org.springframework.samples.petclinic.owner.OwnerController", ownerController.getProperties().get("component.type")); - assertEquals(new File(springPetClinicHome, "src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java").getAbsolutePath(), ownerController.getProperties().get("component.src")); + assertEquals("org/springframework/samples/petclinic/owner/OwnerController.java", ownerController.getProperties().get("component.src")); assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java", ownerController.getUrl()); assertSame(ownerController, parser.getIdentifiersRegister().getElement("springPetClinic.webApplication.ownerController")); assertTrue(clinicEmployee.hasEfferentRelationshipWith(ownerController)); @@ -1209,7 +1209,7 @@ void springPetClinic() throws Exception { Component petController = webApplication.getComponentWithName("Pet Controller"); assertNotNull(petController); assertEquals("org.springframework.samples.petclinic.owner.PetController", petController.getProperties().get("component.type")); - assertEquals(new File(springPetClinicHome, "src/main/java/org/springframework/samples/petclinic/owner/PetController.java").getAbsolutePath(), petController.getProperties().get("component.src")); + assertEquals("org/springframework/samples/petclinic/owner/PetController.java", petController.getProperties().get("component.src")); assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/owner/PetController.java", petController.getUrl()); assertSame(petController, parser.getIdentifiersRegister().getElement("springPetClinic.webApplication.petcontroller")); assertTrue(clinicEmployee.hasEfferentRelationshipWith(petController)); @@ -1217,7 +1217,7 @@ void springPetClinic() throws Exception { Component vetController = webApplication.getComponentWithName("Vet Controller"); assertNotNull(vetController); assertEquals("org.springframework.samples.petclinic.vet.VetController", vetController.getProperties().get("component.type")); - assertEquals(new File(springPetClinicHome, "src/main/java/org/springframework/samples/petclinic/vet/VetController.java").getAbsolutePath(), vetController.getProperties().get("component.src")); + assertEquals("org/springframework/samples/petclinic/vet/VetController.java", vetController.getProperties().get("component.src")); assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/vet/VetController.java", vetController.getUrl()); assertSame(vetController, parser.getIdentifiersRegister().getElement("springPetClinic.webApplication.vetcontroller")); assertTrue(clinicEmployee.hasEfferentRelationshipWith(vetController)); @@ -1225,7 +1225,7 @@ void springPetClinic() throws Exception { Component visitController = webApplication.getComponentWithName("Visit Controller"); assertNotNull(visitController); assertEquals("org.springframework.samples.petclinic.owner.VisitController", visitController.getProperties().get("component.type")); - assertEquals(new File(springPetClinicHome, "src/main/java/org/springframework/samples/petclinic/owner/VisitController.java").getAbsolutePath(), visitController.getProperties().get("component.src")); + assertEquals("org/springframework/samples/petclinic/owner/VisitController.java", visitController.getProperties().get("component.src")); assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/owner/VisitController.java", visitController.getUrl()); assertSame(visitController, parser.getIdentifiersRegister().getElement("springPetClinic.webApplication.visitcontroller")); assertTrue(clinicEmployee.hasEfferentRelationshipWith(visitController)); @@ -1234,7 +1234,7 @@ void springPetClinic() throws Exception { assertNotNull(ownerRepository); assertEquals("Repository class for Owner domain objects All method names are compliant with Spring Data naming conventions so this interface can easily be extended for Spring Data.", ownerRepository.getDescription()); assertEquals("org.springframework.samples.petclinic.owner.OwnerRepository", ownerRepository.getProperties().get("component.type")); - assertEquals(new File(springPetClinicHome, "src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java").getAbsolutePath(), ownerRepository.getProperties().get("component.src")); + assertEquals("org/springframework/samples/petclinic/owner/OwnerRepository.java", ownerRepository.getProperties().get("component.src")); assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java", ownerRepository.getUrl()); assertSame(ownerRepository, parser.getIdentifiersRegister().getElement("springPetClinic.webApplication.ownerrepository")); assertTrue(ownerRepository.hasEfferentRelationshipWith(relationalDatabaseSchema, "Reads from and writes to")); @@ -1243,7 +1243,7 @@ void springPetClinic() throws Exception { assertNotNull(vetRepository); assertEquals("Repository class for Vet domain objects All method names are compliant with Spring Data naming conventions so this interface can easily be extended for Spring Data.", vetRepository.getDescription()); assertEquals("org.springframework.samples.petclinic.vet.VetRepository", vetRepository.getProperties().get("component.type")); - assertEquals(new File(springPetClinicHome, "src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java").getAbsolutePath(), vetRepository.getProperties().get("component.src")); + assertEquals("org/springframework/samples/petclinic/vet/VetRepository.java", vetRepository.getProperties().get("component.src")); assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java", vetRepository.getUrl()); assertSame(vetRepository, parser.getIdentifiersRegister().getElement("springPetClinic.webApplication.vetrepository")); assertTrue(vetRepository.hasEfferentRelationshipWith(relationalDatabaseSchema, "Reads from and writes to")); diff --git a/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl b/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl index fd2f20a78..844f372c4 100644 --- a/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl +++ b/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl @@ -26,6 +26,7 @@ workspace "Spring PetClinic" "A C4 model of the Spring PetClinic sample app (htt technology "Spring MVC Controller" matcher annotation "org.springframework.stereotype.Controller" filter exclude fqn-regex ".*.CrashController" + url prefix "https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java" forEach { clinicEmployee -> this "Uses" tag "Spring MVC Controller" @@ -35,16 +36,13 @@ workspace "Spring PetClinic" "A C4 model of the Spring PetClinic sample app (htt technology "Spring Data Repository" matcher implements "org.springframework.data.repository.Repository" description first-sentence + url prefix "https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java" forEach { -> relationalDatabaseSchema "Reads from and writes to" tag "Spring Data Repository" } } } - - !script groovy { - element.components.each { it.url = it.properties["component.src"].replace(System.getenv("SPRING_PETCLINIC_HOME") + "/src/main/java", "https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java") } - } } } } From a185ea69da875a9791a647a25baf1a8b8592701e Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 17 Sep 2024 13:44:35 +0100 Subject: [PATCH 570/717] "url prefix" -> "url prefix-src" --- .../component/url/DefaultUrlStrategy.java | 3 +++ ...lStrategy.java => PrefixSourceUrlStrategy.java} | 7 +++++-- .../com/structurizr/component/url/UrlStrategy.java | 3 +++ .../dsl/ComponentFinderStrategyParser.java | 14 +++++++------- .../dsl/ComponentFinderStrategyParserTests.java | 8 ++++---- .../resources/dsl/spring-petclinic/workspace.dsl | 4 ++-- 6 files changed, 24 insertions(+), 15 deletions(-) rename structurizr-component/src/main/java/com/structurizr/component/url/{PrefixUrlStrategy.java => PrefixSourceUrlStrategy.java} (78%) diff --git a/structurizr-component/src/main/java/com/structurizr/component/url/DefaultUrlStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/url/DefaultUrlStrategy.java index 445b4c4cc..2e6fa6d1e 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/url/DefaultUrlStrategy.java +++ b/structurizr-component/src/main/java/com/structurizr/component/url/DefaultUrlStrategy.java @@ -2,6 +2,9 @@ import com.structurizr.component.Type; +/** + * Generates a null URL. + */ public class DefaultUrlStrategy implements UrlStrategy { @Override diff --git a/structurizr-component/src/main/java/com/structurizr/component/url/PrefixUrlStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/url/PrefixSourceUrlStrategy.java similarity index 78% rename from structurizr-component/src/main/java/com/structurizr/component/url/PrefixUrlStrategy.java rename to structurizr-component/src/main/java/com/structurizr/component/url/PrefixSourceUrlStrategy.java index 503be9b2c..6419161d5 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/url/PrefixUrlStrategy.java +++ b/structurizr-component/src/main/java/com/structurizr/component/url/PrefixSourceUrlStrategy.java @@ -3,11 +3,14 @@ import com.structurizr.component.Type; import com.structurizr.util.StringUtils; -public class PrefixUrlStrategy implements UrlStrategy { +/** + * Adds a given prefix to the component source location. + */ +public class PrefixSourceUrlStrategy implements UrlStrategy { private final String prefix; - public PrefixUrlStrategy(String prefix) { + public PrefixSourceUrlStrategy(String prefix) { if (StringUtils.isNullOrEmpty(prefix)) { throw new IllegalArgumentException("A prefix must be supplied"); } diff --git a/structurizr-component/src/main/java/com/structurizr/component/url/UrlStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/url/UrlStrategy.java index 7d91b07ae..e066b3c1f 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/url/UrlStrategy.java +++ b/structurizr-component/src/main/java/com/structurizr/component/url/UrlStrategy.java @@ -2,6 +2,9 @@ import com.structurizr.component.Type; +/** + * Provides a way customise how component URLs are generated. + */ public interface UrlStrategy { String urlOf(Type type); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyParser.java index f5b566f8d..e84800882 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyParser.java @@ -9,7 +9,7 @@ import com.structurizr.component.naming.TypeNamingStrategy; import com.structurizr.component.naming.FullyQualifiedNamingStrategy; import com.structurizr.component.supporting.*; -import com.structurizr.component.url.PrefixUrlStrategy; +import com.structurizr.component.url.PrefixSourceUrlStrategy; import java.io.File; import java.util.List; @@ -52,9 +52,9 @@ final class ComponentFinderStrategyParser extends AbstractParser { private static final String DESCRIPTION_GRAMMAR = "description <" + String.join("|", List.of(DESCRIPTION_FIRST_SENTENCE, DESCRIPTION_TRUNCATED)) + ">"; private static final String DESCRIPTION_TRUNCATED_GRAMMAR = "description truncated "; - private static final String URL_PREFIX = "prefix"; - private static final String URL_GRAMMAR = "url <" + String.join("|", List.of(URL_PREFIX)) + ">"; - private static final String URL_PREFIX_GRAMMAR = "url prefix "; + private static final String URL_PREFIX_SRC = "prefix-src"; + private static final String URL_GRAMMAR = "url <" + String.join("|", List.of(URL_PREFIX_SRC)) + ">"; + private static final String URL_PREFIX_SRC_GRAMMAR = "url prefix-src "; void parseTechnology(ComponentFinderStrategyDslContext context, Tokens tokens) { if (tokens.size() != 2) { @@ -248,13 +248,13 @@ void parseUrl(ComponentFinderStrategyDslContext context, Tokens tokens, File dsl String type = tokens.get(1).toLowerCase(); switch (type) { - case URL_PREFIX: + case URL_PREFIX_SRC: if (tokens.size() < 3) { - throw new RuntimeException("Too few tokens, expected: " + URL_PREFIX_GRAMMAR); + throw new RuntimeException("Too few tokens, expected: " + URL_PREFIX_SRC_GRAMMAR); } String prefix = tokens.get(2); - context.getComponentFinderStrategyBuilder().withUrl(new PrefixUrlStrategy(prefix)); + context.getComponentFinderStrategyBuilder().withUrl(new PrefixSourceUrlStrategy(prefix)); break; default: throw new IllegalArgumentException("Unknown URL strategy: " + type); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderStrategyParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderStrategyParserTests.java index dac5d6733..17d9cf0a7 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderStrategyParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderStrategyParserTests.java @@ -283,23 +283,23 @@ void test_parseUrl_ThrowsAnException_WhenNoTypeIsSpecified() { parser.parseUrl(context, tokens("url"), null); fail(); } catch (Exception e) { - assertEquals("Too few tokens, expected: url ", e.getMessage()); + assertEquals("Too few tokens, expected: url ", e.getMessage()); } } @Test void test_parseUrl_Prefix_ThrowsAnException_WhenNoPrefixIsSpecified() { try { - parser.parseUrl(context, tokens("url", "prefix"), null); + parser.parseUrl(context, tokens("url", "prefix-src"), null); fail(); } catch (Exception e) { - assertEquals("Too few tokens, expected: url prefix ", e.getMessage()); + assertEquals("Too few tokens, expected: url prefix-src ", e.getMessage()); } } @Test void test_parseUrl_Prefix() { - parser.parseUrl(context, tokens("url", "prefix", "https://example.com"), null); + parser.parseUrl(context, tokens("url", "prefix-src", "https://example.com"), null); assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=null, typeFilter=null, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=null, urlStrategy=PrefixUrlStrategy{prefix='https://example.com/'}, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); } diff --git a/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl b/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl index 844f372c4..3638bcc0f 100644 --- a/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl +++ b/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl @@ -26,7 +26,7 @@ workspace "Spring PetClinic" "A C4 model of the Spring PetClinic sample app (htt technology "Spring MVC Controller" matcher annotation "org.springframework.stereotype.Controller" filter exclude fqn-regex ".*.CrashController" - url prefix "https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java" + url prefix-src "https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java" forEach { clinicEmployee -> this "Uses" tag "Spring MVC Controller" @@ -36,7 +36,7 @@ workspace "Spring PetClinic" "A C4 model of the Spring PetClinic sample app (htt technology "Spring Data Repository" matcher implements "org.springframework.data.repository.Repository" description first-sentence - url prefix "https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java" + url prefix-src "https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java" forEach { -> relationalDatabaseSchema "Reads from and writes to" tag "Spring Data Repository" From 069f6ea46dd0005a70d801e08e587f0da5c72b17 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 17 Sep 2024 13:44:44 +0100 Subject: [PATCH 571/717] Docs. --- .../structurizr/component/description/DescriptionStrategy.java | 3 +++ .../java/com/structurizr/component/naming/NamingStrategy.java | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/structurizr-component/src/main/java/com/structurizr/component/description/DescriptionStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/description/DescriptionStrategy.java index c255a73f4..cf8129d2c 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/description/DescriptionStrategy.java +++ b/structurizr-component/src/main/java/com/structurizr/component/description/DescriptionStrategy.java @@ -2,6 +2,9 @@ import com.structurizr.component.Type; +/** + * Provides a way customise how component descriptions are generated. + */ public interface DescriptionStrategy { String descriptionOf(Type type); diff --git a/structurizr-component/src/main/java/com/structurizr/component/naming/NamingStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/naming/NamingStrategy.java index 35f0efab5..1520d7313 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/naming/NamingStrategy.java +++ b/structurizr-component/src/main/java/com/structurizr/component/naming/NamingStrategy.java @@ -3,7 +3,7 @@ import com.structurizr.component.Type; /** - * Provides a way to map a fully qualified type name to a component name. + * Provides a way customise how component names are generated. */ public interface NamingStrategy { From 9529188608e5ad08efc3a25a0fcea5d7933bcb96 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 17 Sep 2024 14:39:18 +0100 Subject: [PATCH 572/717] Adds a full component finder test. --- .../component/SpringPetClinicTests.java | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 structurizr-component/src/test/java/com/structurizr/component/SpringPetClinicTests.java diff --git a/structurizr-component/src/test/java/com/structurizr/component/SpringPetClinicTests.java b/structurizr-component/src/test/java/com/structurizr/component/SpringPetClinicTests.java new file mode 100644 index 000000000..e8d70c0f9 --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/SpringPetClinicTests.java @@ -0,0 +1,129 @@ +package com.structurizr.component; + +import com.structurizr.Workspace; +import com.structurizr.component.description.FirstSentenceDescriptionStrategy; +import com.structurizr.component.filter.ExcludeTypesByRegexFilter; +import com.structurizr.component.matcher.AnnotationTypeMatcher; +import com.structurizr.component.matcher.ImplementsTypeMatcher; +import com.structurizr.component.url.PrefixSourceUrlStrategy; +import com.structurizr.model.Component; +import com.structurizr.model.Container; +import com.structurizr.model.Person; +import com.structurizr.model.SoftwareSystem; +import com.structurizr.util.StringUtils; +import org.junit.jupiter.api.Test; + +import java.io.File; + +import static org.junit.jupiter.api.Assertions.*; + +public class SpringPetClinicTests { + + @Test + void springPetClinic() { + String springPetClinicHome = System.getenv().getOrDefault("SPRING_PETCLINIC_HOME", ""); + System.out.println(springPetClinicHome); + if (!StringUtils.isNullOrEmpty(springPetClinicHome)) { + System.out.println("Running Spring PetClinic example..."); + + Workspace workspace = new Workspace("Spring PetClinic", "Description"); + Person clinicEmployee = workspace.getModel().addPerson("Clinic Employee"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Spring PetClinic"); + Container webApplication = softwareSystem.addContainer("Web Application"); + Container relationalDatabaseSchema = softwareSystem.addContainer("Relational Database Schema"); + + ComponentFinder componentFinder = new ComponentFinderBuilder() + .forContainer(webApplication) + .fromClasses(new File(springPetClinicHome, "target/spring-petclinic-3.3.0-SNAPSHOT.jar")) + .fromSource(new File(springPetClinicHome, "src/main/java")) + .withStrategy( + new ComponentFinderStrategyBuilder() + .matchedBy(new AnnotationTypeMatcher("org.springframework.stereotype.Controller")) + .filteredBy(new ExcludeTypesByRegexFilter(".*.CrashController")) + .withTechnology("Spring MVC Controller") + .withUrl(new PrefixSourceUrlStrategy("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java")) + .forEach((component -> { + clinicEmployee.uses(component, "Uses"); + component.addTags(component.getTechnology()); + })) + .build() + ) + .withStrategy( + new ComponentFinderStrategyBuilder() + .matchedBy(new ImplementsTypeMatcher("org.springframework.data.repository.Repository")) + .withDescription(new FirstSentenceDescriptionStrategy()) + .withTechnology("Spring Data Repository") + .withUrl(new PrefixSourceUrlStrategy("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java")) + .forEach((component -> { + component.uses(relationalDatabaseSchema, "Reads from and writes to"); + component.addTags(component.getTechnology()); + })) + .build() + ) + .build(); + + componentFinder.findComponents(); + assertEquals(7, webApplication.getComponents().size()); + + Component welcomeController = webApplication.getComponentWithName("Welcome Controller"); + assertNotNull(welcomeController); + assertEquals("org.springframework.samples.petclinic.system.WelcomeController", welcomeController.getProperties().get("component.type")); + assertEquals("org/springframework/samples/petclinic/system/WelcomeController.java", welcomeController.getProperties().get("component.src")); + assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/system/WelcomeController.java", welcomeController.getUrl()); + assertTrue(clinicEmployee.hasEfferentRelationshipWith(welcomeController)); + + Component ownerController = webApplication.getComponentWithName("Owner Controller"); + assertNotNull(ownerController); + assertEquals("org.springframework.samples.petclinic.owner.OwnerController", ownerController.getProperties().get("component.type")); + assertEquals("org/springframework/samples/petclinic/owner/OwnerController.java", ownerController.getProperties().get("component.src")); + assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java", ownerController.getUrl()); + assertTrue(clinicEmployee.hasEfferentRelationshipWith(ownerController)); + + Component petController = webApplication.getComponentWithName("Pet Controller"); + assertNotNull(petController); + assertEquals("org.springframework.samples.petclinic.owner.PetController", petController.getProperties().get("component.type")); + assertEquals("org/springframework/samples/petclinic/owner/PetController.java", petController.getProperties().get("component.src")); + assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/owner/PetController.java", petController.getUrl()); + assertTrue(clinicEmployee.hasEfferentRelationshipWith(petController)); + + Component vetController = webApplication.getComponentWithName("Vet Controller"); + assertNotNull(vetController); + assertEquals("org.springframework.samples.petclinic.vet.VetController", vetController.getProperties().get("component.type")); + assertEquals("org/springframework/samples/petclinic/vet/VetController.java", vetController.getProperties().get("component.src")); + assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/vet/VetController.java", vetController.getUrl()); + assertTrue(clinicEmployee.hasEfferentRelationshipWith(vetController)); + + Component visitController = webApplication.getComponentWithName("Visit Controller"); + assertNotNull(visitController); + assertEquals("org.springframework.samples.petclinic.owner.VisitController", visitController.getProperties().get("component.type")); + assertEquals("org/springframework/samples/petclinic/owner/VisitController.java", visitController.getProperties().get("component.src")); + assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/owner/VisitController.java", visitController.getUrl()); + assertTrue(clinicEmployee.hasEfferentRelationshipWith(visitController)); + + Component ownerRepository = webApplication.getComponentWithName("Owner Repository"); + assertNotNull(ownerRepository); + assertEquals("Repository class for Owner domain objects All method names are compliant with Spring Data naming conventions so this interface can easily be extended for Spring Data.", ownerRepository.getDescription()); + assertEquals("org.springframework.samples.petclinic.owner.OwnerRepository", ownerRepository.getProperties().get("component.type")); + assertEquals("org/springframework/samples/petclinic/owner/OwnerRepository.java", ownerRepository.getProperties().get("component.src")); + assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java", ownerRepository.getUrl()); + assertTrue(ownerRepository.hasEfferentRelationshipWith(relationalDatabaseSchema, "Reads from and writes to")); + + Component vetRepository = webApplication.getComponentWithName("Vet Repository"); + assertNotNull(vetRepository); + assertEquals("Repository class for Vet domain objects All method names are compliant with Spring Data naming conventions so this interface can easily be extended for Spring Data.", vetRepository.getDescription()); + assertEquals("org.springframework.samples.petclinic.vet.VetRepository", vetRepository.getProperties().get("component.type")); + assertEquals("org/springframework/samples/petclinic/vet/VetRepository.java", vetRepository.getProperties().get("component.src")); + assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java", vetRepository.getUrl()); + assertTrue(vetRepository.hasEfferentRelationshipWith(relationalDatabaseSchema, "Reads from and writes to")); + + assertTrue(welcomeController.getRelationships().isEmpty()); + assertNotNull(petController.getEfferentRelationshipWith(ownerRepository)); + assertNotNull(visitController.getEfferentRelationshipWith(ownerRepository)); + assertNotNull(ownerController.getEfferentRelationshipWith(ownerRepository)); + assertNotNull(vetController.getEfferentRelationshipWith(vetRepository)); + } else { + System.out.println("Skipping Spring PetClinic example..."); + } + } + +} From 63cb000ef854a9a1dc8bcf2c8782495be86d8dd4 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 18 Sep 2024 15:08:31 +0100 Subject: [PATCH 573/717] A few fixes, plus some refactoring to make it easier to debug what's happening when the component finder is running. --- logging.properties | 12 +++ settings.gradle | 1 + structurizr-annotation/README.md | 4 + structurizr-annotation/build.gradle | 3 + .../structurizr/annotation/Properties.java | 15 ++++ .../com/structurizr/annotation/Property.java | 17 ++++ .../java/com/structurizr/annotation/Tag.java | 16 ++++ .../java/com/structurizr/annotation/Tags.java | 15 ++++ structurizr-component/build.gradle | 1 + .../component/ComponentFinder.java | 90 ++++++------------- .../component/ComponentFinderBuilder.java | 25 +++++- .../component/ComponentFinderStrategy.java | 38 +++++++- .../component/DiscoveredComponent.java | 21 ++++- .../java/com/structurizr/component/Type.java | 65 +++++++++++++- .../component/TypeDependencyFinder.java | 53 +++++++++++ .../com/structurizr/component/TypeFinder.java | 46 ++++++++++ .../FirstSentenceDescriptionStrategy.java | 8 +- ...ExcludeFullyQualifiedNameRegexFilter.java} | 6 +- ...IncludeFullyQualifiedNameRegexFilter.java} | 6 +- .../provider/ClassDirectoryTypeProvider.java | 6 ++ .../provider/ClassJarFileTypeProvider.java | 7 ++ .../provider/SourceDirectoryTypeProvider.java | 7 ++ .../ComponentFinderStrategyBuilderTests.java | 10 +-- .../component/ComponentFinderTests.java | 64 +++++++++++++ .../component/SpringPetClinicTests.java | 8 +- .../com/structurizr/component/TypeTests.java | 57 +++++++++++- ...FirstSentenceDescriptionStrategyTests.java | 15 ++++ .../component/example/Controller.java | 4 + .../component/example/ExampleController.java | 12 +++ .../component/example/ExampleRepository.java | 4 + .../component/example/Repository.java | 4 + ...deFullyQualifiedNameRegexFilterTests.java} | 12 +-- ...deFullyQualifiedNameRegexFilterTests.java} | 12 +-- .../ClassDirectoryTypeProviderTests.java | 2 +- .../component/types/TypeWithProperties.java | 9 ++ .../component/types/TypeWithProperty.java | 7 ++ .../component/types/TypeWithTag.java | 7 ++ .../component/types/TypeWithTags.java | 9 ++ .../dsl/ComponentFinderDslContext.java | 2 +- .../dsl/ComponentFinderParser.java | 38 ++++++++ .../dsl/ComponentFinderStrategyParser.java | 8 +- .../structurizr/dsl/StructurizrDslParser.java | 3 + .../structurizr/dsl/StructurizrDslTokens.java | 1 + .../dsl/ComponentFinderParserTests.java | 59 ++++++++++++ .../ComponentFinderStrategyParserTests.java | 4 +- .../dsl/spring-petclinic/workspace.dsl | 1 + 46 files changed, 706 insertions(+), 108 deletions(-) create mode 100644 logging.properties create mode 100644 structurizr-annotation/README.md create mode 100644 structurizr-annotation/build.gradle create mode 100644 structurizr-annotation/src/main/java/com/structurizr/annotation/Properties.java create mode 100644 structurizr-annotation/src/main/java/com/structurizr/annotation/Property.java create mode 100644 structurizr-annotation/src/main/java/com/structurizr/annotation/Tag.java create mode 100644 structurizr-annotation/src/main/java/com/structurizr/annotation/Tags.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/TypeDependencyFinder.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/TypeFinder.java rename structurizr-component/src/main/java/com/structurizr/component/filter/{ExcludeTypesByRegexFilter.java => ExcludeFullyQualifiedNameRegexFilter.java} (77%) rename structurizr-component/src/main/java/com/structurizr/component/filter/{IncludeTypesByRegexFilter.java => IncludeFullyQualifiedNameRegexFilter.java} (77%) create mode 100644 structurizr-component/src/test/java/com/structurizr/component/ComponentFinderTests.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/example/Controller.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/example/ExampleController.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/example/ExampleRepository.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/example/Repository.java rename structurizr-component/src/test/java/com/structurizr/component/filter/{ExcludeTypesByRegexFilterTests.java => ExcludeFullyQualifiedNameRegexFilterTests.java} (60%) rename structurizr-component/src/test/java/com/structurizr/component/filter/{IncludeTypesByRegexFilterTests.java => IncludeFullyQualifiedNameRegexFilterTests.java} (60%) create mode 100644 structurizr-component/src/test/java/com/structurizr/component/types/TypeWithProperties.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/types/TypeWithProperty.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/types/TypeWithTag.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/types/TypeWithTags.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderParserTests.java diff --git a/logging.properties b/logging.properties new file mode 100644 index 000000000..f6c3fb46c --- /dev/null +++ b/logging.properties @@ -0,0 +1,12 @@ +# Logging +handlers = java.util.logging.ConsoleHandler +.level = INFO + +java.util.logging.SimpleFormatter.format=%2$s: %5$s%n + +java.util.logging.ConsoleHandler.level = ALL + +com.structurizr.component.ComponentFinder.level = ALL +com.structurizr.component.TypeFinder.level = ALL +com.structurizr.component.TypeDependencyFinder.level = ALL +com.structurizr.component.ComponentFinderStrategy.level = ALL diff --git a/settings.gradle b/settings.gradle index 8035bdd02..092c80c08 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,5 +1,6 @@ rootProject.name = 'structurizr-java' +include 'structurizr-annotation' include 'structurizr-autolayout' include 'structurizr-client' include 'structurizr-component' diff --git a/structurizr-annotation/README.md b/structurizr-annotation/README.md new file mode 100644 index 000000000..a8626a374 --- /dev/null +++ b/structurizr-annotation/README.md @@ -0,0 +1,4 @@ +# structurizr-annotation + +[![Maven Central](https://img.shields.io/maven-central/v/com.structurizr/structurizr-annotation.svg?label=Maven%20Central)](https://search.maven.org/artifact/com.structurizr/structurizr-annotation) + diff --git a/structurizr-annotation/build.gradle b/structurizr-annotation/build.gradle new file mode 100644 index 000000000..0ce6a1661 --- /dev/null +++ b/structurizr-annotation/build.gradle @@ -0,0 +1,3 @@ +dependencies { + +} \ No newline at end of file diff --git a/structurizr-annotation/src/main/java/com/structurizr/annotation/Properties.java b/structurizr-annotation/src/main/java/com/structurizr/annotation/Properties.java new file mode 100644 index 000000000..b477c2dd1 --- /dev/null +++ b/structurizr-annotation/src/main/java/com/structurizr/annotation/Properties.java @@ -0,0 +1,15 @@ +package com.structurizr.annotation; + +import java.lang.annotation.*; + +/** + * A wrapper for @Property annotations. + */ +@Documented +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface Properties { + + Property[] value(); + +} \ No newline at end of file diff --git a/structurizr-annotation/src/main/java/com/structurizr/annotation/Property.java b/structurizr-annotation/src/main/java/com/structurizr/annotation/Property.java new file mode 100644 index 000000000..405f62414 --- /dev/null +++ b/structurizr-annotation/src/main/java/com/structurizr/annotation/Property.java @@ -0,0 +1,17 @@ +package com.structurizr.annotation; + +import java.lang.annotation.*; + +/** + * A type-level annotation that can be used to add a name-value property to the model element represented by the type. + */ +@Documented +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Repeatable(Properties.class) +public @interface Property { + + String name(); + String value(); + +} \ No newline at end of file diff --git a/structurizr-annotation/src/main/java/com/structurizr/annotation/Tag.java b/structurizr-annotation/src/main/java/com/structurizr/annotation/Tag.java new file mode 100644 index 000000000..5da192346 --- /dev/null +++ b/structurizr-annotation/src/main/java/com/structurizr/annotation/Tag.java @@ -0,0 +1,16 @@ +package com.structurizr.annotation; + +import java.lang.annotation.*; + +/** + * A type-level annotation that can be used to add a tag to the model element represented by the type. + */ +@Documented +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Repeatable(Tags.class) +public @interface Tag { + + String name(); + +} \ No newline at end of file diff --git a/structurizr-annotation/src/main/java/com/structurizr/annotation/Tags.java b/structurizr-annotation/src/main/java/com/structurizr/annotation/Tags.java new file mode 100644 index 000000000..0af4f4b2f --- /dev/null +++ b/structurizr-annotation/src/main/java/com/structurizr/annotation/Tags.java @@ -0,0 +1,15 @@ +package com.structurizr.annotation; + +import java.lang.annotation.*; + +/** + * A wrapper for @Tag annotations. + */ +@Documented +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface Tags { + + Tag[] value(); + +} \ No newline at end of file diff --git a/structurizr-component/build.gradle b/structurizr-component/build.gradle index 3a2fd6c68..6c49d8797 100644 --- a/structurizr-component/build.gradle +++ b/structurizr-component/build.gradle @@ -4,6 +4,7 @@ dependencies { implementation 'org.apache.bcel:bcel:6.8.1' implementation 'com.github.javaparser:javaparser-symbol-solver-core:3.26.1' + testImplementation project(':structurizr-annotation') testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2' } diff --git a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinder.java b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinder.java index 1055c453e..c73e0228b 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinder.java +++ b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinder.java @@ -1,13 +1,11 @@ package com.structurizr.component; +import com.structurizr.component.filter.TypeFilter; import com.structurizr.component.provider.TypeProvider; import com.structurizr.model.Component; import com.structurizr.model.Container; import com.structurizr.util.StringUtils; import org.apache.bcel.Repository; -import org.apache.bcel.classfile.ConstantPool; -import org.apache.bcel.classfile.Method; -import org.apache.bcel.generic.*; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -28,83 +26,40 @@ public final class ComponentFinder { private final Container container; private final List componentFinderStrategies = new ArrayList<>(); - ComponentFinder(Container container, Collection typeProviders, List componentFinderStrategies) { + ComponentFinder(Container container, TypeFilter typeFilter, Collection typeProviders, List componentFinderStrategies) { this.container = container; this.componentFinderStrategies.addAll(componentFinderStrategies); - findTypes(typeProviders); - } - - private void findTypes(Collection typeProviders) { + log.debug("Initialising component finder:"); + log.debug(" - for: " + container.getCanonicalName()); for (TypeProvider typeProvider : typeProviders) { - Set types = typeProvider.getTypes(); - for (com.structurizr.component.Type type : types) { - if (type.getJavaClass() != null) { - // this is the BCEL identified type - typeRepository.add(type); - } else { - // this is the source code identified type - com.structurizr.component.Type bcelType = typeRepository.getType(type.getFullyQualifiedName()); - if (bcelType != null) { - bcelType.setDescription(type.getDescription()); - bcelType.setSource(type.getSource()); - } - } - } + log.debug(" - from: " + typeProvider); + } + log.debug(" - filtered by: " + typeFilter); + for (ComponentFinderStrategy strategy : componentFinderStrategies) { + log.debug(" - with strategy: " + strategy); } + new TypeFinder().run(typeProviders, typeFilter, typeRepository); Repository.clearCache(); - for (com.structurizr.component.Type type : typeRepository.getTypes()) { + for (Type type : typeRepository.getTypes()) { if (type.getJavaClass() != null) { Repository.addClass(type.getJavaClass()); - findDependencies(type); - } - } - } - - private void findDependencies(com.structurizr.component.Type type) { - ConstantPool cp = type.getJavaClass().getConstantPool(); - ConstantPoolGen cpg = new ConstantPoolGen(cp); - for (Method m : type.getJavaClass().getMethods()) { - MethodGen mg = new MethodGen(m, type.getJavaClass().getClassName(), cpg); - InstructionList il = mg.getInstructionList(); - if (il == null) { - continue; - } - - InstructionHandle[] instructionHandles = il.getInstructionHandles(); - for (InstructionHandle instructionHandle : instructionHandles) { - Instruction instruction = instructionHandle.getInstruction(); - if (!(instruction instanceof InvokeInstruction)) { - continue; - } - - InvokeInstruction invokeInstruction = (InvokeInstruction)instruction; - ReferenceType referenceType = invokeInstruction.getReferenceType(cpg); - if (!(referenceType instanceof ObjectType)) { - continue; - } - - ObjectType objectType = (ObjectType)referenceType; - String referencedClassName = objectType.getClassName(); - com.structurizr.component.Type referencedType = typeRepository.getType(referencedClassName); - if (referencedType != null) { - type.addDependency(referencedType); - } + new TypeDependencyFinder().run(type, typeRepository); } } } /** - * Find components, using all configured rules, in the order they were added. + * Find components, using all configured strategies, in the order they were added. */ - public Set findComponents() { + public Set run() { Set discoveredComponents = new LinkedHashSet<>(); Map componentMap = new HashMap<>(); Set componentSet = new LinkedHashSet<>(); for (ComponentFinderStrategy componentFinderStrategy : componentFinderStrategies) { - Set set = componentFinderStrategy.findComponents(typeRepository); + Set set = componentFinderStrategy.run(typeRepository); if (set.isEmpty()) { throw new RuntimeException("No components were found by " + componentFinderStrategy); } @@ -120,28 +75,41 @@ public Set findComponents() { component.setDescription(discoveredComponent.getDescription()); component.setTechnology(discoveredComponent.getTechnology()); component.setUrl(discoveredComponent.getUrl()); + + component.addTags(discoveredComponent.getTags().toArray(new String[0])); + for (String name : discoveredComponent.getProperties().keySet()) { + component.addProperty(name, discoveredComponent.getProperties().get(name)); + } + componentMap.put(discoveredComponent, component); componentSet.add(component); } // find dependencies between all components for (DiscoveredComponent discoveredComponent : discoveredComponents) { + Component component = componentMap.get(discoveredComponent); + log.debug("Component dependencies for \"" + component.getName() + "\":"); Set typeDependencies = discoveredComponent.getAllDependencies(); for (Type typeDependency : typeDependencies) { for (DiscoveredComponent c : discoveredComponents) { if (c != discoveredComponent) { if (c.getAllTypes().contains(typeDependency)) { Component componentDependency = componentMap.get(c); - componentMap.get(discoveredComponent).uses(componentDependency, ""); + log.debug(" -> " + componentDependency.getName()); + component.uses(componentDependency, ""); } } } } + if (component.getRelationships().isEmpty()) { + log.debug(" - none"); + } } // now visit all components for (DiscoveredComponent discoveredComponent : componentMap.keySet()) { Component component = componentMap.get(discoveredComponent); + log.debug("Visiting \"" + component.getName() + "\""); discoveredComponent.getComponentFinderStrategy().visit(component); } diff --git a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderBuilder.java b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderBuilder.java index 86cd0a843..21f1a37e1 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderBuilder.java +++ b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderBuilder.java @@ -1,5 +1,7 @@ package com.structurizr.component; +import com.structurizr.component.filter.DefaultTypeFilter; +import com.structurizr.component.filter.TypeFilter; import com.structurizr.component.provider.ClassDirectoryTypeProvider; import com.structurizr.component.provider.ClassJarFileTypeProvider; import com.structurizr.component.provider.SourceDirectoryTypeProvider; @@ -19,6 +21,7 @@ public class ComponentFinderBuilder { private Container container; private final List typeProviders = new ArrayList<>(); + private TypeFilter typeFilter; private final List componentFinderStrategies = new ArrayList<>(); public ComponentFinderBuilder forContainer(Container container) { @@ -57,6 +60,12 @@ public ComponentFinderBuilder fromSource(File path) { return this; } + public ComponentFinderBuilder filteredBy(TypeFilter typeFilter) { + this.typeFilter = typeFilter; + + return this; + } + public ComponentFinderBuilder withStrategy(ComponentFinderStrategy componentFinderStrategy) { this.componentFinderStrategies.add(componentFinderStrategy); @@ -72,11 +81,25 @@ public ComponentFinder build() { throw new RuntimeException("One or more type providers must be configured"); } + if (typeFilter == null) { + typeFilter = new DefaultTypeFilter(); + } + if (componentFinderStrategies.isEmpty()) { throw new RuntimeException("One or more component finder strategies must be configured"); } - return new ComponentFinder(container, typeProviders, componentFinderStrategies); + return new ComponentFinder(container, typeFilter, typeProviders, componentFinderStrategies); + } + + @Override + public String toString() { + return "ComponentFinderBuilder{" + + "container=" + container + + ", typeProviders=" + typeProviders + + ", typeFilter=" + typeFilter + + ", componentFinderStrategies=" + componentFinderStrategies + + '}'; } } diff --git a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategy.java index d1dc8f87a..6862e7abe 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategy.java +++ b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinderStrategy.java @@ -8,8 +8,11 @@ import com.structurizr.component.url.UrlStrategy; import com.structurizr.component.visitor.ComponentVisitor; import com.structurizr.model.Component; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import java.util.LinkedHashSet; +import java.util.Map; import java.util.Set; /** @@ -23,6 +26,8 @@ */ public final class ComponentFinderStrategy { + private static final Log log = LogFactory.getLog(ComponentFinderStrategy.class); + private final String technology; private final TypeMatcher typeMatcher; private final TypeFilter typeFilter; @@ -43,22 +48,49 @@ public final class ComponentFinderStrategy { this.componentVisitor = componentVisitor; } - Set findComponents(TypeRepository typeRepository) { + Set run(TypeRepository typeRepository) { Set components = new LinkedHashSet<>(); + log.debug("Running " + this.toString()); Set types = typeRepository.getTypes(); for (Type type : types) { - if (typeMatcher.matches(type) && typeFilter.accept(type)) { + + boolean matched = typeMatcher.matches(type); + boolean accepted = typeFilter.accept(type); + + if (matched) { + if (accepted) { + log.debug(" + " + type.getFullyQualifiedName() + " (matched=true, accepted=true)"); + } else { + log.debug(" - " + type.getFullyQualifiedName() + " (matched=true, accepted=false)"); + } + } else { + log.debug(" - " + type.getFullyQualifiedName() + " (matched=false)"); + } + + if (matched && accepted) { DiscoveredComponent component = new DiscoveredComponent(namingStrategy.nameOf(type), type); component.setDescription(descriptionStrategy.descriptionOf(type)); component.setTechnology(this.technology); component.setUrl(urlStrategy.urlOf(type)); + component.addTags(type.getTags()); + Map properties = type.getProperties(); + for (String name : properties.keySet()) { + component.addProperty(name, properties.get(name)); + } component.setComponentFinderStrategy(this); components.add(component); // now find supporting types Set supportingTypes = supportingTypesStrategy.findSupportingTypes(type, typeRepository); - component.addSupportingTypes(supportingTypes); + if (supportingTypes.isEmpty()) { + log.debug(" - none"); + } else { + for (Type supportingType : supportingTypes) { + log.debug(" + supporting type: " + supportingType.getFullyQualifiedName()); + } + component.addSupportingTypes(supportingTypes); + } } } diff --git a/structurizr-component/src/main/java/com/structurizr/component/DiscoveredComponent.java b/structurizr-component/src/main/java/com/structurizr/component/DiscoveredComponent.java index 70443932c..cd77da3c1 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/DiscoveredComponent.java +++ b/structurizr-component/src/main/java/com/structurizr/component/DiscoveredComponent.java @@ -1,7 +1,6 @@ package com.structurizr.component; -import java.util.HashSet; -import java.util.Set; +import java.util.*; final class DiscoveredComponent { @@ -10,6 +9,8 @@ final class DiscoveredComponent { private String description; private String technology; private String url; + private final List tags = new ArrayList<>(); + private final Map properties = new HashMap<>(); private final Set supportingTypes = new HashSet<>(); private ComponentFinderStrategy componentFinderStrategy; @@ -55,6 +56,22 @@ void setUrl(String url) { this.url = url; } + void addTags(List tags) { + this.tags.addAll(tags); + } + + List getTags() { + return List.copyOf(tags); + } + + void addProperty(String key, String value) { + properties.put(key, value); + } + + Map getProperties() { + return Map.copyOf(properties); + } + Set getSupportingTypes() { return new HashSet<>(supportingTypes); } diff --git a/structurizr-component/src/main/java/com/structurizr/component/Type.java b/structurizr-component/src/main/java/com/structurizr/component/Type.java index 3c9fafe01..f267a0e82 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/Type.java +++ b/structurizr-component/src/main/java/com/structurizr/component/Type.java @@ -1,17 +1,21 @@ package com.structurizr.component; import com.structurizr.util.StringUtils; -import org.apache.bcel.classfile.JavaClass; +import org.apache.bcel.classfile.*; -import java.util.LinkedHashSet; -import java.util.Objects; -import java.util.Set; +import java.util.*; /** * Represents a Java type (e.g. class or interface) - it's a wrapper around a BCEL JavaClass. */ public class Type { + private static final String STRUCTURIZR_TAG_ANNOTATION = "Lcom/structurizr/annotation/Tag;"; + private static final String STRUCTURIZR_TAGS_ANNOTATION = "Lcom/structurizr/annotation/Tags;"; + + private static final String STRUCTURIZR_PROPERTY_ANNOTATION = "Lcom/structurizr/annotation/Property;"; + private static final String STRUCTURIZR_PROPERTIES_ANNOTATION = "Lcom/structurizr/annotation/Properties;"; + private final JavaClass javaClass; private final String fullyQualifiedName; private String description; @@ -76,10 +80,63 @@ public Set getDependencies() { return new LinkedHashSet<>(dependencies); } + public boolean hasDependency(Type type) { + return dependencies.contains(type); + } + public boolean isAbstractClass() { return javaClass.isAbstract() && javaClass.isClass(); } + public List getTags() { + List tags = new ArrayList<>(); + + AnnotationEntry[] annotationEntries = javaClass.getAnnotationEntries(); + for (AnnotationEntry annotationEntry : annotationEntries) { + if (STRUCTURIZR_TAG_ANNOTATION.equals(annotationEntry.getAnnotationType())) { + ElementValuePair elementValuePair = annotationEntry.getElementValuePairs()[0]; + String tag = elementValuePair.getValue().stringifyValue(); + tags.add(tag); + } else if (STRUCTURIZR_TAGS_ANNOTATION.equals(annotationEntry.getAnnotationType())) { + ElementValuePair elementValuePair = annotationEntry.getElementValuePairs()[0]; + ArrayElementValue elementValue = (ArrayElementValue)elementValuePair.getValue(); + for (ElementValue value : elementValue.getElementValuesArray()) { + AnnotationElementValue annotationElementValue = (AnnotationElementValue)value; + AnnotationEntry tagAannotationEntry = annotationElementValue.getAnnotationEntry(); + String tag = tagAannotationEntry.getElementValuePairs()[0].getValue().stringifyValue(); + tags.add(tag); + } + } + } + + return tags; + } + + public Map getProperties() { + Map properties = new HashMap<>(); + + AnnotationEntry[] annotationEntries = javaClass.getAnnotationEntries(); + for (AnnotationEntry annotationEntry : annotationEntries) { + if (STRUCTURIZR_PROPERTY_ANNOTATION.equals(annotationEntry.getAnnotationType())) { + String name = annotationEntry.getElementValuePairs()[0].getValue().stringifyValue(); + String value = annotationEntry.getElementValuePairs()[1].getValue().stringifyValue(); + properties.put(name, value); + } else if (STRUCTURIZR_PROPERTIES_ANNOTATION.equals(annotationEntry.getAnnotationType())) { + ArrayElementValue arrayElementValue = (ArrayElementValue)annotationEntry.getElementValuePairs()[0].getValue(); + for (ElementValue elementValue : arrayElementValue.getElementValuesArray()) { + AnnotationElementValue annotationElementValue = (AnnotationElementValue)elementValue; + AnnotationEntry tagAannotationEntry = annotationElementValue.getAnnotationEntry(); + + String name = tagAannotationEntry.getElementValuePairs()[0].getValue().stringifyValue(); + String value = tagAannotationEntry.getElementValuePairs()[1].getValue().stringifyValue(); + properties.put(name, value); + } + } + } + + return properties; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/structurizr-component/src/main/java/com/structurizr/component/TypeDependencyFinder.java b/structurizr-component/src/main/java/com/structurizr/component/TypeDependencyFinder.java new file mode 100644 index 000000000..2b8352b4d --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/TypeDependencyFinder.java @@ -0,0 +1,53 @@ +package com.structurizr.component; + +import org.apache.bcel.classfile.ConstantPool; +import org.apache.bcel.classfile.Method; +import org.apache.bcel.generic.*; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +class TypeDependencyFinder { + + private static final Log log = LogFactory.getLog(TypeDependencyFinder.class); + + void run(Type type, TypeRepository typeRepository) { + log.debug("Type dependencies for " + type.getFullyQualifiedName() + ":"); + ConstantPool cp = type.getJavaClass().getConstantPool(); + ConstantPoolGen cpg = new ConstantPoolGen(cp); + for (Method m : type.getJavaClass().getMethods()) { + MethodGen mg = new MethodGen(m, type.getJavaClass().getClassName(), cpg); + InstructionList il = mg.getInstructionList(); + if (il == null) { + continue; + } + + InstructionHandle[] instructionHandles = il.getInstructionHandles(); + for (InstructionHandle instructionHandle : instructionHandles) { + Instruction instruction = instructionHandle.getInstruction(); + if (!(instruction instanceof InvokeInstruction)) { + continue; + } + + InvokeInstruction invokeInstruction = (InvokeInstruction)instruction; + ReferenceType referenceType = invokeInstruction.getReferenceType(cpg); + if (!(referenceType instanceof ObjectType)) { + continue; + } + + ObjectType objectType = (ObjectType)referenceType; + String referencedClassName = objectType.getClassName(); + com.structurizr.component.Type referencedType = typeRepository.getType(referencedClassName); + if (referencedType != null && !referencedType.getFullyQualifiedName().equals(type.getFullyQualifiedName()) && !type.hasDependency(referencedType)) { + log.debug(" + " + referencedType.getFullyQualifiedName()); + + type.addDependency(referencedType); + } + } + } + + if (type.getDependencies().isEmpty()) { + log.debug(" - none"); + } + } + +} diff --git a/structurizr-component/src/main/java/com/structurizr/component/TypeFinder.java b/structurizr-component/src/main/java/com/structurizr/component/TypeFinder.java new file mode 100644 index 000000000..d1b843464 --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/TypeFinder.java @@ -0,0 +1,46 @@ +package com.structurizr.component; + +import com.structurizr.component.filter.TypeFilter; +import com.structurizr.component.provider.TypeProvider; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.Collection; +import java.util.Set; + +class TypeFinder { + + private static final Log log = LogFactory.getLog(TypeFinder.class); + + void run(Collection typeProviders, TypeFilter typeFilter, TypeRepository typeRepository) { + for (TypeProvider typeProvider : typeProviders) { + log.debug("Running " + typeProvider.toString()); + + Set types = typeProvider.getTypes(); + for (com.structurizr.component.Type type : types) { + + boolean accepted = typeFilter.accept(type); + if (accepted) { + log.debug(" + " + type.getFullyQualifiedName() + " (accepted=true)"); + } else { + log.debug(" - " + type.getFullyQualifiedName() + " (accepted=false)"); + } + + if (accepted) { + if (type.getJavaClass() != null) { + // this is the BCEL identified type + typeRepository.add(type); + } else { + // this is the source code identified type + Type bcelType = typeRepository.getType(type.getFullyQualifiedName()); + if (bcelType != null) { + bcelType.setDescription(type.getDescription()); + bcelType.setSource(type.getSource()); + } + } + } + } + } + } + +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/description/FirstSentenceDescriptionStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/description/FirstSentenceDescriptionStrategy.java index accb274f6..bdeccb146 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/description/FirstSentenceDescriptionStrategy.java +++ b/structurizr-component/src/main/java/com/structurizr/component/description/FirstSentenceDescriptionStrategy.java @@ -12,11 +12,15 @@ public class FirstSentenceDescriptionStrategy implements DescriptionStrategy { public String descriptionOf(Type type) { String description = type.getDescription(); + if (StringUtils.isNullOrEmpty(description)) { + return ""; + } + int index = description.indexOf('.'); if (index == -1) { - return description; + return description.trim(); } else { - return description.substring(0, index+1); + return description.trim().substring(0, index+1); } } diff --git a/structurizr-component/src/main/java/com/structurizr/component/filter/ExcludeTypesByRegexFilter.java b/structurizr-component/src/main/java/com/structurizr/component/filter/ExcludeFullyQualifiedNameRegexFilter.java similarity index 77% rename from structurizr-component/src/main/java/com/structurizr/component/filter/ExcludeTypesByRegexFilter.java rename to structurizr-component/src/main/java/com/structurizr/component/filter/ExcludeFullyQualifiedNameRegexFilter.java index 52e7d2843..c1ddaa9df 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/filter/ExcludeTypesByRegexFilter.java +++ b/structurizr-component/src/main/java/com/structurizr/component/filter/ExcludeFullyQualifiedNameRegexFilter.java @@ -6,11 +6,11 @@ /** * A type filter that excludes by matching a regex against the fully qualified type name. */ -public class ExcludeTypesByRegexFilter implements TypeFilter { +public class ExcludeFullyQualifiedNameRegexFilter implements TypeFilter { private final String regex; - public ExcludeTypesByRegexFilter(String regex) { + public ExcludeFullyQualifiedNameRegexFilter(String regex) { if (StringUtils.isNullOrEmpty(regex)) { throw new IllegalArgumentException("A regex must be supplied"); } @@ -25,7 +25,7 @@ public boolean accept(Type type) { @Override public String toString() { - return "ExcludeTypesByRegexFilter{" + + return "ExcludeFullyQualifiedNameRegexFilter{" + "regex='" + regex + '\'' + '}'; } diff --git a/structurizr-component/src/main/java/com/structurizr/component/filter/IncludeTypesByRegexFilter.java b/structurizr-component/src/main/java/com/structurizr/component/filter/IncludeFullyQualifiedNameRegexFilter.java similarity index 77% rename from structurizr-component/src/main/java/com/structurizr/component/filter/IncludeTypesByRegexFilter.java rename to structurizr-component/src/main/java/com/structurizr/component/filter/IncludeFullyQualifiedNameRegexFilter.java index 3de92d287..dcf3a9597 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/filter/IncludeTypesByRegexFilter.java +++ b/structurizr-component/src/main/java/com/structurizr/component/filter/IncludeFullyQualifiedNameRegexFilter.java @@ -6,11 +6,11 @@ /** * A type filter that includes by matching a regex against the fully qualified type name. */ -public class IncludeTypesByRegexFilter implements TypeFilter { +public class IncludeFullyQualifiedNameRegexFilter implements TypeFilter { private final String regex; - public IncludeTypesByRegexFilter(String regex) { + public IncludeFullyQualifiedNameRegexFilter(String regex) { if (StringUtils.isNullOrEmpty(regex)) { throw new IllegalArgumentException("A regex must be supplied"); } @@ -25,7 +25,7 @@ public boolean accept(Type type) { @Override public String toString() { - return "IncludeTypesByRegexFilter{" + + return "IncludeFullyQualifiedNameRegexFilter{" + "regex='" + regex + '\'' + '}'; } diff --git a/structurizr-component/src/main/java/com/structurizr/component/provider/ClassDirectoryTypeProvider.java b/structurizr-component/src/main/java/com/structurizr/component/provider/ClassDirectoryTypeProvider.java index 1c18fb9d5..a25606763 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/provider/ClassDirectoryTypeProvider.java +++ b/structurizr-component/src/main/java/com/structurizr/component/provider/ClassDirectoryTypeProvider.java @@ -72,5 +72,11 @@ private Set findClassFiles(File path) { return classFiles; } + @Override + public String toString() { + return "ClassDirectoryTypeProvider{" + + "directory=" + directory + + '}'; + } } \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/provider/ClassJarFileTypeProvider.java b/structurizr-component/src/main/java/com/structurizr/component/provider/ClassJarFileTypeProvider.java index 10f00de7e..908f2c34b 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/provider/ClassJarFileTypeProvider.java +++ b/structurizr-component/src/main/java/com/structurizr/component/provider/ClassJarFileTypeProvider.java @@ -58,4 +58,11 @@ public Set getTypes() { return types; } + @Override + public String toString() { + return "ClassJarFileTypeProvider{" + + "jarFile=" + jarFile + + '}'; + } + } \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/provider/SourceDirectoryTypeProvider.java b/structurizr-component/src/main/java/com/structurizr/component/provider/SourceDirectoryTypeProvider.java index 96f934f5c..94a0a986c 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/provider/SourceDirectoryTypeProvider.java +++ b/structurizr-component/src/main/java/com/structurizr/component/provider/SourceDirectoryTypeProvider.java @@ -128,4 +128,11 @@ private String relativePath(File path) { return relativePath; } + @Override + public String toString() { + return "SourceDirectoryTypeProvider{" + + "directory=" + directory + + '}'; + } + } \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/ComponentFinderStrategyBuilderTests.java b/structurizr-component/src/test/java/com/structurizr/component/ComponentFinderStrategyBuilderTests.java index e71590dfe..b7937f09b 100644 --- a/structurizr-component/src/test/java/com/structurizr/component/ComponentFinderStrategyBuilderTests.java +++ b/structurizr-component/src/test/java/com/structurizr/component/ComponentFinderStrategyBuilderTests.java @@ -2,8 +2,8 @@ import com.structurizr.component.description.FirstSentenceDescriptionStrategy; import com.structurizr.component.description.TruncatedDescriptionStrategy; -import com.structurizr.component.filter.ExcludeTypesByRegexFilter; -import com.structurizr.component.filter.IncludeTypesByRegexFilter; +import com.structurizr.component.filter.ExcludeFullyQualifiedNameRegexFilter; +import com.structurizr.component.filter.IncludeFullyQualifiedNameRegexFilter; import com.structurizr.component.matcher.NameSuffixTypeMatcher; import com.structurizr.component.naming.FullyQualifiedNamingStrategy; import com.structurizr.component.naming.TypeNamingStrategy; @@ -48,7 +48,7 @@ void filteredBy_ThrowsAnException_WhenPassedNull() { @Test void filteredBy_ThrowsAnException_WhenCalledTwice() { try { - new ComponentFinderStrategyBuilder().filteredBy(new IncludeTypesByRegexFilter(".*")).filteredBy(new ExcludeTypesByRegexFilter(".*")); + new ComponentFinderStrategyBuilder().filteredBy(new IncludeFullyQualifiedNameRegexFilter(".*")).filteredBy(new ExcludeFullyQualifiedNameRegexFilter(".*")); fail(); } catch (Exception e) { assertEquals("A type filter has already been configured", e.getMessage()); @@ -160,12 +160,12 @@ void build() { ComponentFinderStrategy strategy = new ComponentFinderStrategyBuilder() .withTechnology("Spring MVC Controller") .matchedBy(new NameSuffixTypeMatcher("Controller")) - .filteredBy(new IncludeTypesByRegexFilter("com.example.web.\\.*")) + .filteredBy(new IncludeFullyQualifiedNameRegexFilter("com.example.web.\\.*")) .withName(new TypeNamingStrategy()) .withDescription(new FirstSentenceDescriptionStrategy()) .build(); - assertEquals("ComponentFinderStrategy{technology='Spring MVC Controller', typeMatcher=NameSuffixTypeMatcher{suffix='Controller'}, typeFilter=IncludeTypesByRegexFilter{regex='com.example.web.\\.*'}, supportingTypesStrategy=DefaultSupportingTypesStrategy{}, namingStrategy=TypeNamingStrategy{}, descriptionStrategy=FirstSentenceDescriptionStrategy{}, urlStrategy=DefaultUrlStrategy{}, componentVisitor=DefaultComponentVisitor{}}", strategy.toString()); + assertEquals("ComponentFinderStrategy{technology='Spring MVC Controller', typeMatcher=NameSuffixTypeMatcher{suffix='Controller'}, typeFilter=IncludeFullyQualifiedNameRegexFilter{regex='com.example.web.\\.*'}, supportingTypesStrategy=DefaultSupportingTypesStrategy{}, namingStrategy=TypeNamingStrategy{}, descriptionStrategy=FirstSentenceDescriptionStrategy{}, urlStrategy=DefaultUrlStrategy{}, componentVisitor=DefaultComponentVisitor{}}", strategy.toString()); } } \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/ComponentFinderTests.java b/structurizr-component/src/test/java/com/structurizr/component/ComponentFinderTests.java new file mode 100644 index 000000000..5e7ad05ce --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/ComponentFinderTests.java @@ -0,0 +1,64 @@ +package com.structurizr.component; + +import com.structurizr.Workspace; +import com.structurizr.component.description.FirstSentenceDescriptionStrategy; +import com.structurizr.component.filter.IncludeFullyQualifiedNameRegexFilter; +import com.structurizr.component.matcher.ImplementsTypeMatcher; +import com.structurizr.component.naming.TypeNamingStrategy; +import com.structurizr.model.Component; +import com.structurizr.model.Container; +import com.structurizr.model.SoftwareSystem; +import org.junit.jupiter.api.Test; + +import java.io.File; + +import static com.github.javaparser.utils.Utils.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class ComponentFinderTests { + + @Test + void run() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name"); + Container container = softwareSystem.addContainer("Name"); + + ComponentFinder componentFinder = new ComponentFinderBuilder() + .forContainer(container) + .fromClasses(new File("build/classes/java/test")) + .fromSource(new File("src/test/java")) + .filteredBy(new IncludeFullyQualifiedNameRegexFilter("com\\.structurizr\\.component\\.example\\..*")) + .withStrategy(new ComponentFinderStrategyBuilder() + .withTechnology("Web Controller") + .matchedBy(new ImplementsTypeMatcher("com.structurizr.component.example.Controller")) + .filteredBy(new IncludeFullyQualifiedNameRegexFilter("com\\.structurizr\\.component\\.example\\..*")) + .withName(new TypeNamingStrategy()) + .withDescription(new FirstSentenceDescriptionStrategy()) + .build() + ) + .withStrategy(new ComponentFinderStrategyBuilder() + .withTechnology("Data Repository") + .matchedBy(new ImplementsTypeMatcher("com.structurizr.component.example.Repository")) + .filteredBy(new IncludeFullyQualifiedNameRegexFilter("com\\.structurizr\\.component\\.example\\..*")) + .withName(new TypeNamingStrategy()) + .withDescription(new FirstSentenceDescriptionStrategy()) + .build() + ) + .build(); + + componentFinder.run(); + + assertEquals(2, container.getComponents().size()); + Component exampleController = container.getComponentWithName("ExampleController"); + assertNotNull(exampleController); + assertTrue(exampleController.hasTag("Controller")); + assertEquals("https://example.com", exampleController.getProperties().get("Documentation")); + + Component exampleRepository = container.getComponentWithName("ExampleRepository"); + assertNotNull(exampleRepository); + + assertTrue(exampleController.hasEfferentRelationshipWith(exampleRepository)); + } + +} diff --git a/structurizr-component/src/test/java/com/structurizr/component/SpringPetClinicTests.java b/structurizr-component/src/test/java/com/structurizr/component/SpringPetClinicTests.java index e8d70c0f9..a4158f36f 100644 --- a/structurizr-component/src/test/java/com/structurizr/component/SpringPetClinicTests.java +++ b/structurizr-component/src/test/java/com/structurizr/component/SpringPetClinicTests.java @@ -2,7 +2,8 @@ import com.structurizr.Workspace; import com.structurizr.component.description.FirstSentenceDescriptionStrategy; -import com.structurizr.component.filter.ExcludeTypesByRegexFilter; +import com.structurizr.component.filter.ExcludeFullyQualifiedNameRegexFilter; +import com.structurizr.component.filter.IncludeFullyQualifiedNameRegexFilter; import com.structurizr.component.matcher.AnnotationTypeMatcher; import com.structurizr.component.matcher.ImplementsTypeMatcher; import com.structurizr.component.url.PrefixSourceUrlStrategy; @@ -36,10 +37,11 @@ void springPetClinic() { .forContainer(webApplication) .fromClasses(new File(springPetClinicHome, "target/spring-petclinic-3.3.0-SNAPSHOT.jar")) .fromSource(new File(springPetClinicHome, "src/main/java")) + .filteredBy(new IncludeFullyQualifiedNameRegexFilter("org\\.springframework\\.samples\\.petclinic\\..*")) .withStrategy( new ComponentFinderStrategyBuilder() .matchedBy(new AnnotationTypeMatcher("org.springframework.stereotype.Controller")) - .filteredBy(new ExcludeTypesByRegexFilter(".*.CrashController")) + .filteredBy(new ExcludeFullyQualifiedNameRegexFilter(".*.CrashController")) .withTechnology("Spring MVC Controller") .withUrl(new PrefixSourceUrlStrategy("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java")) .forEach((component -> { @@ -62,7 +64,7 @@ void springPetClinic() { ) .build(); - componentFinder.findComponents(); + componentFinder.run(); assertEquals(7, webApplication.getComponents().size()); Component welcomeController = webApplication.getComponentWithName("Welcome Controller"); diff --git a/structurizr-component/src/test/java/com/structurizr/component/TypeTests.java b/structurizr-component/src/test/java/com/structurizr/component/TypeTests.java index d73f761ba..1df5874d5 100644 --- a/structurizr-component/src/test/java/com/structurizr/component/TypeTests.java +++ b/structurizr-component/src/test/java/com/structurizr/component/TypeTests.java @@ -1,8 +1,15 @@ package com.structurizr.component; +import com.structurizr.component.matcher.ImplementsTypeMatcher; +import org.apache.bcel.classfile.ClassParser; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertEquals; +import java.io.File; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import static org.junit.jupiter.api.Assertions.*; public class TypeTests { @@ -20,4 +27,52 @@ void packageName() { assertEquals("com.example", type.getPackageName()); } + @Test + void getTags_WhenTypeHasOneTag() throws Exception { + File classes = new File("build/classes/java/test"); + ClassParser parser = new ClassParser(new File(classes, "com/structurizr/component/types/TypeWithTag.class").getAbsolutePath()); + Type type = new Type(parser.parse()); + + List tags = type.getTags(); + assertEquals(1, tags.size()); + assertTrue(tags.contains("Tag 1")); + } + + @Test + void getTags_WhenTypeHasManyTags() throws Exception { + File classes = new File("build/classes/java/test"); + ClassParser parser = new ClassParser(new File(classes, "com/structurizr/component/types/TypeWithTags.class").getAbsolutePath()); + Type type = new Type(parser.parse()); + + List tags = type.getTags(); + assertEquals(3, tags.size()); + assertEquals("Tag 1", tags.get(0)); + assertEquals("Tag 2", tags.get(1)); + assertEquals("Tag 3", tags.get(2)); + } + + @Test + void getTags_WhenTypeHasOneProperty() throws Exception { + File classes = new File("build/classes/java/test"); + ClassParser parser = new ClassParser(new File(classes, "com/structurizr/component/types/TypeWithProperty.class").getAbsolutePath()); + Type type = new Type(parser.parse()); + + Map properties = type.getProperties(); + assertEquals(1, properties.size()); + assertEquals("Value", properties.get("Name")); + } + + @Test + void getTags_WhenTypeHasManyProperties() throws Exception { + File classes = new File("build/classes/java/test"); + ClassParser parser = new ClassParser(new File(classes, "com/structurizr/component/types/TypeWithProperties.class").getAbsolutePath()); + Type type = new Type(parser.parse()); + + Map properties = type.getProperties(); + assertEquals(3, properties.size()); + assertEquals("Value1", properties.get("Name1")); + assertEquals("Value2", properties.get("Name2")); + assertEquals("Value3", properties.get("Name3")); + } + } \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/description/FirstSentenceDescriptionStrategyTests.java b/structurizr-component/src/test/java/com/structurizr/component/description/FirstSentenceDescriptionStrategyTests.java index f9c2515e1..4039cf8f8 100644 --- a/structurizr-component/src/test/java/com/structurizr/component/description/FirstSentenceDescriptionStrategyTests.java +++ b/structurizr-component/src/test/java/com/structurizr/component/description/FirstSentenceDescriptionStrategyTests.java @@ -4,9 +4,24 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; public class FirstSentenceDescriptionStrategyTests { + @Test + void descriptionOf_WhenTheDescriptionIsNull() { + Type type = new Type("com.example.ClassName"); + type.setDescription(null); + assertEquals("", new FirstSentenceDescriptionStrategy().descriptionOf(type)); + } + + @Test + void descriptionOf_WhenTheDescriptionIsEmpty() { + Type type = new Type("com.example.ClassName"); + type.setDescription(" "); + assertEquals("", new FirstSentenceDescriptionStrategy().descriptionOf(type)); + } + @Test void descriptionOf_WhenThereIsASentence() { Type type = new Type("com.example.ClassName"); diff --git a/structurizr-component/src/test/java/com/structurizr/component/example/Controller.java b/structurizr-component/src/test/java/com/structurizr/component/example/Controller.java new file mode 100644 index 000000000..ebaf960c0 --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/example/Controller.java @@ -0,0 +1,4 @@ +package com.structurizr.component.example; + +interface Controller { +} diff --git a/structurizr-component/src/test/java/com/structurizr/component/example/ExampleController.java b/structurizr-component/src/test/java/com/structurizr/component/example/ExampleController.java new file mode 100644 index 000000000..f6ff0569b --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/example/ExampleController.java @@ -0,0 +1,12 @@ +package com.structurizr.component.example; + +import com.structurizr.annotation.Property; +import com.structurizr.annotation.Tag; + +@Tag(name = "Controller") +@Property(name = "Documentation", value = "https://example.com") +class ExampleController implements Controller { + + private Repository exampleRepository = new ExampleRepository(); + +} \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/example/ExampleRepository.java b/structurizr-component/src/test/java/com/structurizr/component/example/ExampleRepository.java new file mode 100644 index 000000000..ce3afddf3 --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/example/ExampleRepository.java @@ -0,0 +1,4 @@ +package com.structurizr.component.example; + +public class ExampleRepository implements Repository { +} diff --git a/structurizr-component/src/test/java/com/structurizr/component/example/Repository.java b/structurizr-component/src/test/java/com/structurizr/component/example/Repository.java new file mode 100644 index 000000000..276ed87a7 --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/example/Repository.java @@ -0,0 +1,4 @@ +package com.structurizr.component.example; + +interface Repository { +} diff --git a/structurizr-component/src/test/java/com/structurizr/component/filter/ExcludeTypesByRegexFilterTests.java b/structurizr-component/src/test/java/com/structurizr/component/filter/ExcludeFullyQualifiedNameRegexFilterTests.java similarity index 60% rename from structurizr-component/src/test/java/com/structurizr/component/filter/ExcludeTypesByRegexFilterTests.java rename to structurizr-component/src/test/java/com/structurizr/component/filter/ExcludeFullyQualifiedNameRegexFilterTests.java index 4a0b459fa..c7d156e8b 100644 --- a/structurizr-component/src/test/java/com/structurizr/component/filter/ExcludeTypesByRegexFilterTests.java +++ b/structurizr-component/src/test/java/com/structurizr/component/filter/ExcludeFullyQualifiedNameRegexFilterTests.java @@ -5,28 +5,28 @@ import static org.junit.jupiter.api.Assertions.*; -public class ExcludeTypesByRegexFilterTests { +public class ExcludeFullyQualifiedNameRegexFilterTests { @Test void construction_ThrowsAnException_WhenPassedANullSuffix() { - assertThrowsExactly(IllegalArgumentException.class, () -> new ExcludeTypesByRegexFilter(null)); + assertThrowsExactly(IllegalArgumentException.class, () -> new ExcludeFullyQualifiedNameRegexFilter(null)); } @Test void construction_ThrowsAnException_WhenPassedAnEmptySuffix() { - assertThrowsExactly(IllegalArgumentException.class, () -> new ExcludeTypesByRegexFilter("")); - assertThrowsExactly(IllegalArgumentException.class, () -> new ExcludeTypesByRegexFilter(" ")); + assertThrowsExactly(IllegalArgumentException.class, () -> new ExcludeFullyQualifiedNameRegexFilter("")); + assertThrowsExactly(IllegalArgumentException.class, () -> new ExcludeFullyQualifiedNameRegexFilter(" ")); } @Test void filter_ReturnsTrue_WhenTheTypeDoesNotMatchRegex() { - assertTrue(new ExcludeTypesByRegexFilter(".*Utils").accept(new Type("com.example.CustomerComponent"))); + assertTrue(new ExcludeFullyQualifiedNameRegexFilter(".*Utils").accept(new Type("com.example.CustomerComponent"))); } @Test void filter_ReturnsFalse_WhenTheTypeMatchesRegex() { - assertFalse(new ExcludeTypesByRegexFilter(".*Utils").accept(new Type("com.example.DateUtils"))); + assertFalse(new ExcludeFullyQualifiedNameRegexFilter(".*Utils").accept(new Type("com.example.DateUtils"))); } } \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/filter/IncludeTypesByRegexFilterTests.java b/structurizr-component/src/test/java/com/structurizr/component/filter/IncludeFullyQualifiedNameRegexFilterTests.java similarity index 60% rename from structurizr-component/src/test/java/com/structurizr/component/filter/IncludeTypesByRegexFilterTests.java rename to structurizr-component/src/test/java/com/structurizr/component/filter/IncludeFullyQualifiedNameRegexFilterTests.java index 25b223c54..dfa1ed908 100644 --- a/structurizr-component/src/test/java/com/structurizr/component/filter/IncludeTypesByRegexFilterTests.java +++ b/structurizr-component/src/test/java/com/structurizr/component/filter/IncludeFullyQualifiedNameRegexFilterTests.java @@ -5,28 +5,28 @@ import static org.junit.jupiter.api.Assertions.*; -public class IncludeTypesByRegexFilterTests { +public class IncludeFullyQualifiedNameRegexFilterTests { @Test void construction_ThrowsAnException_WhenPassedANullSuffix() { - assertThrowsExactly(IllegalArgumentException.class, () -> new IncludeTypesByRegexFilter(null)); + assertThrowsExactly(IllegalArgumentException.class, () -> new IncludeFullyQualifiedNameRegexFilter(null)); } @Test void construction_ThrowsAnException_WhenPassedAnEmptySuffix() { - assertThrowsExactly(IllegalArgumentException.class, () -> new IncludeTypesByRegexFilter("")); - assertThrowsExactly(IllegalArgumentException.class, () -> new IncludeTypesByRegexFilter(" ")); + assertThrowsExactly(IllegalArgumentException.class, () -> new IncludeFullyQualifiedNameRegexFilter("")); + assertThrowsExactly(IllegalArgumentException.class, () -> new IncludeFullyQualifiedNameRegexFilter(" ")); } @Test void filter_ReturnsFalse_WhenTheTypeDoesNotMatchRegex() { - assertFalse(new IncludeTypesByRegexFilter(".*Component").accept(new Type("com.example.DateUtils"))); + assertFalse(new IncludeFullyQualifiedNameRegexFilter(".*Component").accept(new Type("com.example.DateUtils"))); } @Test void filter_ReturnsTrue_WhenTheTypeMatchesRegex() { - assertTrue(new IncludeTypesByRegexFilter(".*Component").accept(new Type("com.example.CustomerComponent"))); + assertTrue(new IncludeFullyQualifiedNameRegexFilter(".*Component").accept(new Type("com.example.CustomerComponent"))); } } \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/provider/ClassDirectoryTypeProviderTests.java b/structurizr-component/src/test/java/com/structurizr/component/provider/ClassDirectoryTypeProviderTests.java index 60d90a8cd..924ecabe9 100644 --- a/structurizr-component/src/test/java/com/structurizr/component/provider/ClassDirectoryTypeProviderTests.java +++ b/structurizr-component/src/test/java/com/structurizr/component/provider/ClassDirectoryTypeProviderTests.java @@ -32,7 +32,7 @@ void getTypes() { TypeProvider typeProvider = new ClassDirectoryTypeProvider(classes); Set types = typeProvider.getTypes(); - assertTrue(types.size() > 0); + assertFalse(types.isEmpty()); assertNotNull(types.stream().filter(t -> t.getFullyQualifiedName().equals("com.structurizr.component.provider.ClassDirectoryTypeProviderTests"))); } diff --git a/structurizr-component/src/test/java/com/structurizr/component/types/TypeWithProperties.java b/structurizr-component/src/test/java/com/structurizr/component/types/TypeWithProperties.java new file mode 100644 index 000000000..f9f0157db --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/types/TypeWithProperties.java @@ -0,0 +1,9 @@ +package com.structurizr.component.types; + +import com.structurizr.annotation.Property; + +@Property(name = "Name1", value = "Value1") +@Property(name = "Name2", value = "Value2") +@Property(name = "Name3", value = "Value3") +public class TypeWithProperties { +} diff --git a/structurizr-component/src/test/java/com/structurizr/component/types/TypeWithProperty.java b/structurizr-component/src/test/java/com/structurizr/component/types/TypeWithProperty.java new file mode 100644 index 000000000..d6de25773 --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/types/TypeWithProperty.java @@ -0,0 +1,7 @@ +package com.structurizr.component.types; + +import com.structurizr.annotation.Property; + +@Property(name = "Name", value = "Value") +public class TypeWithProperty { +} diff --git a/structurizr-component/src/test/java/com/structurizr/component/types/TypeWithTag.java b/structurizr-component/src/test/java/com/structurizr/component/types/TypeWithTag.java new file mode 100644 index 000000000..74cea4cdb --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/types/TypeWithTag.java @@ -0,0 +1,7 @@ +package com.structurizr.component.types; + +import com.structurizr.annotation.Tag; + +@Tag(name = "Tag 1") +public class TypeWithTag { +} diff --git a/structurizr-component/src/test/java/com/structurizr/component/types/TypeWithTags.java b/structurizr-component/src/test/java/com/structurizr/component/types/TypeWithTags.java new file mode 100644 index 000000000..b577d6ca8 --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/types/TypeWithTags.java @@ -0,0 +1,9 @@ +package com.structurizr.component.types; + +import com.structurizr.annotation.Tag; + +@Tag(name = "Tag 1") +@Tag(name = "Tag 2") +@Tag(name = "Tag 3") +public class TypeWithTags { +} diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderDslContext.java index 0172fd657..89f030ff8 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderDslContext.java @@ -32,7 +32,7 @@ ComponentFinderBuilder getComponentFinderBuilder() { @Override void end() { - Set components = componentFinderBuilder.build().findComponents(); + Set components = componentFinderBuilder.build().run(); for (Component component : components) { dslParser.registerIdentifier(IdentifiersRegister.toIdentifier(component.getName()), component); } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderParser.java index 61d0d49d6..f0931bc5a 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderParser.java @@ -1,10 +1,18 @@ package com.structurizr.dsl; +import com.structurizr.component.filter.ExcludeFullyQualifiedNameRegexFilter; +import com.structurizr.component.filter.IncludeFullyQualifiedNameRegexFilter; + final class ComponentFinderParser extends AbstractParser { private static final String CLASSES_GRAMMAR = "classes "; private static final String SOURCE_GRAMMAR = "source "; + private static final String FILTER_INCLUDE = "include"; + private static final String FILTER_EXCLUDE = "exclude"; + private static final String FILTER_FQN_REGEX = "fqn-regex"; + private static final String FILTER_GRAMMAR = "filter <" + FILTER_INCLUDE + "|" + FILTER_EXCLUDE + "> <" + FILTER_FQN_REGEX + "> [parameters]"; + void parseClasses(ComponentFinderDslContext context, Tokens tokens) { // classes @@ -25,4 +33,34 @@ void parseSource(ComponentFinderDslContext context, Tokens tokens) { context.getComponentFinderBuilder().fromSource(tokens.get(1)); } + void parseFilter(ComponentFinderDslContext context, Tokens tokens) { + if (tokens.size() < 3) { + throw new RuntimeException("Too few tokens, expected: " + FILTER_GRAMMAR); + } + + String includeOrExclude = tokens.get(1).toLowerCase(); + if (!"include".equalsIgnoreCase(includeOrExclude) && !"exclude".equalsIgnoreCase(includeOrExclude)) { + throw new RuntimeException("Filter mode should be \"" + FILTER_INCLUDE + "\" or \"" + FILTER_EXCLUDE + "\": " + FILTER_GRAMMAR); + } + + String type = tokens.get(2).toLowerCase(); + switch (type) { + case FILTER_FQN_REGEX: + if (tokens.size() == 4) { + String regex = tokens.get(3); + + if (FILTER_INCLUDE.equalsIgnoreCase(includeOrExclude)) { + context.getComponentFinderBuilder().filteredBy(new IncludeFullyQualifiedNameRegexFilter(regex)); + } else { + context.getComponentFinderBuilder().filteredBy(new ExcludeFullyQualifiedNameRegexFilter(regex)); + } + } else { + throw new RuntimeException("Expected: " + FILTER_GRAMMAR); + } + break; + default: + throw new IllegalArgumentException("Unknown filter: " + type); + } + } + } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyParser.java index e84800882..340f437e8 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyParser.java @@ -2,8 +2,8 @@ import com.structurizr.component.description.FirstSentenceDescriptionStrategy; import com.structurizr.component.description.TruncatedDescriptionStrategy; -import com.structurizr.component.filter.ExcludeTypesByRegexFilter; -import com.structurizr.component.filter.IncludeTypesByRegexFilter; +import com.structurizr.component.filter.ExcludeFullyQualifiedNameRegexFilter; +import com.structurizr.component.filter.IncludeFullyQualifiedNameRegexFilter; import com.structurizr.component.matcher.*; import com.structurizr.component.naming.DefaultPackageNamingStrategy; import com.structurizr.component.naming.TypeNamingStrategy; @@ -153,9 +153,9 @@ void parseFilter(ComponentFinderStrategyDslContext context, Tokens tokens, File String regex = tokens.get(3); if (FILTER_INCLUDE.equalsIgnoreCase(includeOrExclude)) { - context.getComponentFinderStrategyBuilder().filteredBy(new IncludeTypesByRegexFilter(regex)); + context.getComponentFinderStrategyBuilder().filteredBy(new IncludeFullyQualifiedNameRegexFilter(regex)); } else { - context.getComponentFinderStrategyBuilder().filteredBy(new ExcludeTypesByRegexFilter(regex)); + context.getComponentFinderStrategyBuilder().filteredBy(new ExcludeFullyQualifiedNameRegexFilter(regex)); } } else { throw new RuntimeException("Expected: " + FILTER_GRAMMAR); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index abbaa8a65..6b7e307d5 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -433,6 +433,9 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn } else if (COMPONENT_FINDER_SOURCE_TOKEN.equalsIgnoreCase(firstToken) && inContext(ComponentFinderDslContext.class)) { new ComponentFinderParser().parseSource(getContext(ComponentFinderDslContext.class), tokens); + } else if (COMPONENT_FINDER_FILTER_TOKEN.equalsIgnoreCase(firstToken) && inContext(ComponentFinderDslContext.class)) { + new ComponentFinderParser().parseFilter(getContext(ComponentFinderDslContext.class), tokens); + } else if (COMPONENT_FINDER_STRATEGY_TOKEN.equalsIgnoreCase(firstToken) && inContext(ComponentFinderDslContext.class)) { if (shouldStartContext(tokens)) { startContext(new ComponentFinderStrategyDslContext(getContext(ComponentFinderDslContext.class).getComponentFinderBuilder())); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java index fa3829934..696162601 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java @@ -116,6 +116,7 @@ class StructurizrDslTokens { static final String COMPONENT_FINDER_TOKEN = "!components"; static final String COMPONENT_FINDER_CLASSES_TOKEN = "classes"; static final String COMPONENT_FINDER_SOURCE_TOKEN = "source"; + static final String COMPONENT_FINDER_FILTER_TOKEN = "filter"; static final String COMPONENT_FINDER_STRATEGY_TOKEN = "strategy"; static final String COMPONENT_FINDER_STRATEGY_TECHNOLOGY_TOKEN = "technology"; static final String COMPONENT_FINDER_STRATEGY_MATCHER_TOKEN = "matcher"; diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderParserTests.java new file mode 100644 index 000000000..414de072a --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderParserTests.java @@ -0,0 +1,59 @@ +package com.structurizr.dsl; + +import com.structurizr.component.ComponentFinderBuilder; +import com.structurizr.component.matcher.NameSuffixTypeMatcher; +import org.junit.jupiter.api.Test; + +import java.io.File; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +class ComponentFinderParserTests extends AbstractTests { + + private final ComponentFinderParser parser = new ComponentFinderParser(); + private final ComponentFinderDslContext context = new ComponentFinderDslContext(null, null); + + @Test + void test_parseFilter_ThrowsAnException_WhenNoModeAndTypeAreSpecified() { + try { + parser.parseFilter(context, tokens("filter")); + fail(); + } catch (Exception e) { + assertEquals("Too few tokens, expected: filter [parameters]", e.getMessage()); + } + } + + @Test + void test_parseFilter_ThrowsAnException_WhenNoTypeIsSpecified() { + try { + parser.parseFilter(context, tokens("filter", "include")); + fail(); + } catch (Exception e) { + assertEquals("Too few tokens, expected: filter [parameters]", e.getMessage()); + } + } + + @Test + void test_parseFilter_ThrowsAnException_WhenInvalidModeIsSpecified() { + try { + parser.parseFilter(context, tokens("filter", "mode", "fqn-regex")); + fail(); + } catch (Exception e) { + assertEquals("Filter mode should be \"include\" or \"exclude\": filter [parameters]", e.getMessage()); + } + } + + @Test + void test_parseFilter_WhenIncludeFullyQualifiedNameRegexTypeFilterIsUsed() { + parser.parseFilter(context, tokens("filter", "include", "fqn-regex", ".*")); + assertEquals("ComponentFinderBuilder{container=null, typeProviders=[], typeFilter=IncludeFullyQualifiedNameRegexFilter{regex='.*'}, componentFinderStrategies=[]}", context.getComponentFinderBuilder().toString()); + } + + @Test + void test_parseFilter_WhenExcludeFullyQualifiedNameRegexTypeFilterIsUsed() { + parser.parseFilter(context, tokens("filter", "exclude", "fqn-regex", ".*")); + assertEquals("ComponentFinderBuilder{container=null, typeProviders=[], typeFilter=ExcludeFullyQualifiedNameRegexFilter{regex='.*'}, componentFinderStrategies=[]}", context.getComponentFinderBuilder().toString()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderStrategyParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderStrategyParserTests.java index 17d9cf0a7..6ea5eb258 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderStrategyParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderStrategyParserTests.java @@ -186,13 +186,13 @@ void test_parseFilter_ThrowsAnException_WhenInvalidModeIsSpecified() { @Test void test_parseFilter_WhenIncludeFullyQualifiedNameRegexTypeFilterIsUsed() { parser.parseFilter(context, tokens("filter", "include", "fqn-regex", ".*"), new File(".")); - assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=null, typeFilter=IncludeTypesByRegexFilter{regex='.*'}, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=null, urlStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); + assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=null, typeFilter=IncludeFullyQualifiedNameRegexFilter{regex='.*'}, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=null, urlStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); } @Test void test_parseFilter_WhenExcludeFullyQualifiedNameRegexTypeFilterIsUsed() { parser.parseFilter(context, tokens("filter", "exclude", "fqn-regex", ".*"), new File(".")); - assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=null, typeFilter=ExcludeTypesByRegexFilter{regex='.*'}, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=null, urlStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); + assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=null, typeFilter=ExcludeFullyQualifiedNameRegexFilter{regex='.*'}, supportingTypesStrategy=null, namingStrategy=null, descriptionStrategy=null, urlStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); } @Test diff --git a/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl b/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl index 3638bcc0f..9144e5fce 100644 --- a/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl +++ b/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl @@ -22,6 +22,7 @@ workspace "Spring PetClinic" "A C4 model of the Spring PetClinic sample app (htt !components { classes "${SPRING_PETCLINIC_HOME}/target/spring-petclinic-3.3.0-SNAPSHOT.jar" source "${SPRING_PETCLINIC_HOME}/src/main/java" + filter include fqn-regex "org.springframework.samples.petclinic..*" strategy { technology "Spring MVC Controller" matcher annotation "org.springframework.stereotype.Controller" From bb68483118e7704d6ac15c28cb3dfcbc162b06f3 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 18 Sep 2024 15:09:06 +0100 Subject: [PATCH 574/717] Allows !extend to be used hierarchically. --- .../structurizr/dsl/StructurizrDslParser.java | 2 +- .../java/com/structurizr/dsl/DslTests.java | 14 +++++++++ .../resources/dsl/extend-hierarchical.dsl | 31 +++++++++++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 structurizr-dsl/src/test/resources/dsl/extend-hierarchical.dsl diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index 6b7e307d5..d5fa307c7 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -326,7 +326,7 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn startContext(new RelationshipsDslContext(getContext(), relationships)); } - } else if ((REF_TOKEN.equalsIgnoreCase(firstToken) || EXTEND_TOKEN.equalsIgnoreCase(firstToken)) && (inContext(ModelDslContext.class))) { + } else if ((REF_TOKEN.equalsIgnoreCase(firstToken) || EXTEND_TOKEN.equalsIgnoreCase(firstToken)) && (inContext(ModelItemDslContext.class) || inContext(ModelDslContext.class))) { ModelItem modelItem = new RefParser().parse(getContext(), tokens.withoutContextStartToken()); if (shouldStartContext(tokens)) { diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index e5f22175b..0c72b1d7c 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -1285,4 +1285,18 @@ void test_ImageView_WhenParserIsInRestrictedMode() { } } + @Test + void test_extendHierachical() throws Exception { + File dslFile = new File("src/test/resources/dsl/extend-hierarchical.dsl"); + + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(dslFile); + + Component component = parser.getWorkspace().getModel().getSoftwareSystemWithName("A").getContainerWithName("B").getComponentWithName("C"); + assertEquals("Value1", component.getProperties().get("Name1")); + assertEquals("Value2", component.getProperties().get("Name2")); + assertEquals("Value3", component.getProperties().get("Name3")); + } + + } \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/extend-hierarchical.dsl b/structurizr-dsl/src/test/resources/dsl/extend-hierarchical.dsl new file mode 100644 index 000000000..c6ed64556 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/extend-hierarchical.dsl @@ -0,0 +1,31 @@ +workspace { + + !identifiers hierarchical + + model { + a = softwareSystem "A" { + b = container "B" { + c = component "C" + + !extend c { + properties { + "Name1" "Value1" + } + } + } + + !extend b.c { + properties { + "Name2" "Value2" + } + } + } + + !extend a.b.c { + properties { + "Name3" "Value3" + } + } + } + +} \ No newline at end of file From f26f63d966a7af19843ff9913d52dd17a9b45974 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 18 Sep 2024 15:47:03 +0100 Subject: [PATCH 575/717] Deprecates `!ref` and `!extend` in favour of `!element` and `!relationship`. --- changelog.md | 3 + .../structurizr/dsl/FindElementParser.java | 48 ++++++++++++++ ...ntsParser.java => FindElementsParser.java} | 2 +- .../dsl/FindRelationshipParser.java | 33 ++++++++++ ...rser.java => FindRelationshipsParser.java} | 4 +- .../structurizr/dsl/StructurizrDslParser.java | 24 +++++-- .../structurizr/dsl/StructurizrDslTokens.java | 12 ++-- .../java/com/structurizr/dsl/DslTests.java | 31 +++++---- .../dsl/FindElementParserTests.java | 64 +++++++++++++++++++ ...ests.java => FindElementsParserTests.java} | 5 +- .../dsl/FindRelationshipParserTests.java | 58 +++++++++++++++++ ...java => FindRelationshipsParserTests.java} | 4 +- .../dsl/deployment-environment-empty.dsl | 2 +- .../extend/extend-workspace-from-dsl-file.dsl | 2 +- .../extend/extend-workspace-from-dsl-url.dsl | 2 +- .../extend-workspace-from-json-file.dsl | 8 +-- .../extend/extend-workspace-from-json-url.dsl | 8 +-- ...ical.dsl => find-element-hierarchical.dsl} | 6 +- .../dsl/{ref.dsl => find-element.dsl} | 8 +-- 19 files changed, 271 insertions(+), 53 deletions(-) create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/FindElementParser.java rename structurizr-dsl/src/main/java/com/structurizr/dsl/{ElementsParser.java => FindElementsParser.java} (94%) create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/FindRelationshipParser.java rename structurizr-dsl/src/main/java/com/structurizr/dsl/{RelationshipsParser.java => FindRelationshipsParser.java} (91%) create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/FindElementParserTests.java rename structurizr-dsl/src/test/java/com/structurizr/dsl/{ElementsParserTests.java => FindElementsParserTests.java} (92%) create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/FindRelationshipParserTests.java rename structurizr-dsl/src/test/java/com/structurizr/dsl/{RelationshipsParserTests.java => FindRelationshipsParserTests.java} (92%) rename structurizr-dsl/src/test/resources/dsl/{extend-hierarchical.dsl => find-element-hierarchical.dsl} (85%) rename structurizr-dsl/src/test/resources/dsl/{ref.dsl => find-element.dsl} (83%) diff --git a/changelog.md b/changelog.md index 0af4373a5..19fd510c0 100644 --- a/changelog.md +++ b/changelog.md @@ -10,7 +10,10 @@ - structurizr-dsl: Fixes https://github.com/structurizr/java/issues/312 (!include doesn't work with files encoded as UTF-8 BOM). - structurizr-dsl: Adds a way to explicitly specify the order of relationships in dynamic views. - structurizr-dsl: Adds support for element technology expressions (e.g. `element.technology==Java` and `element.technology!=Java`). +- structurizr-dsl: Deprecates `!ref` and `!extend`. +- structurizr-dsl: Adds an `!element` keyword that can be used to find a single element by identifier or canonical name (replaces `!ref` and `!extend`). - structurizr-dsl: Adds an `!elements` keyword that can be used to find a set of elements via an expression. +- structurizr-dsl: Adds an `!relationship` keyword that can be used to find a single relationship by identifier (replaces `!ref` and `!extend`). - structurizr-dsl: Adds a `!relationships` keyword that can be used to find a set of relationships via an expression. - structurizr-dsl: Adds a DSL wrapper around the `structurizr-component` component finder. - structurizr-dsl: Adds support for local theme files to be specified via `theme` (https://github.com/structurizr/java/issues/331). diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/FindElementParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/FindElementParser.java new file mode 100644 index 000000000..74532148e --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/FindElementParser.java @@ -0,0 +1,48 @@ +package com.structurizr.dsl; + +import com.structurizr.model.Element; +import com.structurizr.model.StaticStructureElement; + +final class FindElementParser extends AbstractParser { + + private static final String GRAMMAR = "!element "; + + private final static int IDENTIFIER_INDEX = 1; + + Element parse(DslContext context, Tokens tokens) { + // !element + + if (tokens.hasMoreThan(IDENTIFIER_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } + + if (!tokens.includes(IDENTIFIER_INDEX)) { + throw new RuntimeException("Expected: " + GRAMMAR); + } + + String s = tokens.get(IDENTIFIER_INDEX); + + Element element; + + if (s.contains("://")) { + element = context.getWorkspace().getModel().getElementWithCanonicalName(s); + } else { + element = context.getElement(s); + } + + if (element == null) { + throw new RuntimeException("An element identified by \"" + s + "\" could not be found"); + } + + if (context instanceof GroupableDslContext && element instanceof StaticStructureElement) { + GroupableDslContext groupableDslContext = (GroupableDslContext)context; + StaticStructureElement staticStructureElement = (StaticStructureElement)element; + if (groupableDslContext.hasGroup()) { + staticStructureElement.setGroup(groupableDslContext.getGroup().getName()); + } + } + + return element; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementsParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/FindElementsParser.java similarity index 94% rename from structurizr-dsl/src/main/java/com/structurizr/dsl/ElementsParser.java rename to structurizr-dsl/src/main/java/com/structurizr/dsl/FindElementsParser.java index f205241ce..47dc0f748 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementsParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/FindElementsParser.java @@ -6,7 +6,7 @@ import java.util.Set; import java.util.stream.Collectors; -final class ElementsParser extends AbstractParser { +final class FindElementsParser extends AbstractParser { private static final String GRAMMAR = "!elements "; diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/FindRelationshipParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/FindRelationshipParser.java new file mode 100644 index 000000000..3ee77016b --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/FindRelationshipParser.java @@ -0,0 +1,33 @@ +package com.structurizr.dsl; + +import com.structurizr.model.Relationship; + +final class FindRelationshipParser extends AbstractParser { + + private static final String GRAMMAR = "!relationship "; + + private final static int IDENTIFIER_INDEX = 1; + + Relationship parse(DslContext context, Tokens tokens) { + // !relationship + + if (tokens.hasMoreThan(IDENTIFIER_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } + + if (!tokens.includes(IDENTIFIER_INDEX)) { + throw new RuntimeException("Expected: " + GRAMMAR); + } + + String s = tokens.get(IDENTIFIER_INDEX); + + Relationship relationship = context.getRelationship(s); + + if (relationship == null) { + throw new RuntimeException("A relationship identified by \"" + s + "\" could not be found"); + } + + return relationship; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipsParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/FindRelationshipsParser.java similarity index 91% rename from structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipsParser.java rename to structurizr-dsl/src/main/java/com/structurizr/dsl/FindRelationshipsParser.java index 890a295d6..2bc4c7d31 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipsParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/FindRelationshipsParser.java @@ -1,13 +1,12 @@ package com.structurizr.dsl; -import com.structurizr.model.Element; import com.structurizr.model.ModelItem; import com.structurizr.model.Relationship; import java.util.Set; import java.util.stream.Collectors; -final class RelationshipsParser extends AbstractParser { +final class FindRelationshipsParser extends AbstractParser { private static final String GRAMMAR = "!relationships "; @@ -25,7 +24,6 @@ Set parse(DslContext context, Tokens tokens) { Set relationships = modelItems.stream().filter(mi -> mi instanceof Relationship).map(mi -> (Relationship)mi).collect(Collectors.toSet()); - if (relationships.isEmpty()) { throw new RuntimeException("No relationships found for expression \"" + expression + "\""); } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index d5fa307c7..80a32c223 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -326,8 +326,20 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn startContext(new RelationshipsDslContext(getContext(), relationships)); } - } else if ((REF_TOKEN.equalsIgnoreCase(firstToken) || EXTEND_TOKEN.equalsIgnoreCase(firstToken)) && (inContext(ModelItemDslContext.class) || inContext(ModelDslContext.class))) { - ModelItem modelItem = new RefParser().parse(getContext(), tokens.withoutContextStartToken()); + } else if ((FIND_ELEMENT_TOKEN.equalsIgnoreCase(firstToken) || FIND_RELATIONSHIP_TOKEN.equalsIgnoreCase(firstToken) || REF_TOKEN.equalsIgnoreCase(firstToken) || EXTEND_TOKEN.equalsIgnoreCase(firstToken)) && (inContext(ModelItemDslContext.class) || inContext(ModelDslContext.class))) { + ModelItem modelItem = null; + + if (REF_TOKEN.equalsIgnoreCase(firstToken)) { + log.warn(REF_TOKEN + " has been deprecated and will be removed in a future release - please use !element or !relationship instead"); + modelItem = new RefParser().parse(getContext(), tokens.withoutContextStartToken()); + } else if (EXTEND_TOKEN.equalsIgnoreCase(firstToken)) { + log.warn(EXTEND_TOKEN + " has been deprecated and will be removed in a future release - please use !element or !relationship instead"); + modelItem = new RefParser().parse(getContext(), tokens.withoutContextStartToken()); + } else if (FIND_ELEMENT_TOKEN.equalsIgnoreCase(firstToken)) { + modelItem = new FindElementParser().parse(getContext(), tokens.withoutContextStartToken()); + } else if (FIND_RELATIONSHIP_TOKEN.equalsIgnoreCase(firstToken)) { + modelItem = new FindRelationshipParser().parse(getContext(), tokens.withoutContextStartToken()); + } if (shouldStartContext(tokens)) { if (modelItem instanceof Person) { @@ -361,15 +373,15 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn } } - } else if (ELEMENTS_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ModelDslContext.class) || inContext(ElementDslContext.class))) { - Set elements = new ElementsParser().parse(getContext(), tokens.withoutContextStartToken()); + } else if (FIND_ELEMENTS_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ModelDslContext.class) || inContext(ElementDslContext.class))) { + Set elements = new FindElementsParser().parse(getContext(), tokens.withoutContextStartToken()); if (shouldStartContext(tokens)) { startContext(new ElementsDslContext(getContext(), elements)); } - } else if (RELATIONSHIPS_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ModelDslContext.class) || inContext(ElementDslContext.class))) { - Set relationships = new RelationshipsParser().parse(getContext(), tokens.withoutContextStartToken()); + } else if (FIND_RELATIONSHIPS_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ModelDslContext.class) || inContext(ElementDslContext.class))) { + Set relationships = new FindRelationshipsParser().parse(getContext(), tokens.withoutContextStartToken()); if (shouldStartContext(tokens)) { startContext(new RelationshipsDslContext(getContext(), relationships)); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java index 696162601..e17ad379a 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java @@ -105,11 +105,15 @@ class StructurizrDslTokens { static final String VAR_TOKEN = "!var"; static final String IDENTIFIERS_TOKEN = "!identifiers"; static final String IMPLIED_RELATIONSHIPS_TOKEN = "!impliedRelationships"; - static final String REF_TOKEN = "!ref"; - static final String ELEMENTS_TOKEN = "!elements"; - static final String RELATIONSHIPS_TOKEN = "!relationships"; - static final String EXTEND_TOKEN = "!extend"; + static final String REF_TOKEN = "!ref"; // deprecated + static final String EXTEND_TOKEN = "!extend"; // deprecated + + static final String FIND_ELEMENT_TOKEN = "!element"; + static final String FIND_ELEMENTS_TOKEN = "!elements"; + static final String FIND_RELATIONSHIP_TOKEN = "!relationship"; + static final String FIND_RELATIONSHIPS_TOKEN = "!relationships"; + static final String PLUGIN_TOKEN = "!plugin"; static final String SCRIPT_TOKEN = "!script"; diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index 0c72b1d7c..682753f62 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -516,15 +516,28 @@ void test_extendWorkspaceFromDslFiles() throws Exception { } @Test - void test_ref() throws Exception { + void test_findElement() throws Exception { StructurizrDslParser parser = new StructurizrDslParser(); - parser.parse(new File("src/test/resources/dsl/ref.dsl")); + parser.parse(new File("src/test/resources/dsl/find-element.dsl")); assertNotNull(parser.getWorkspace().getModel().getElementWithCanonicalName("InfrastructureNode://Live/Amazon Web Services/New deployment node/New infrastructure node")); assertNotNull(parser.getWorkspace().getModel().getElementWithCanonicalName("InfrastructureNode://Live/Amazon Web Services/US-East-1/New deployment node 1/New infrastructure node 1")); assertNotNull(parser.getWorkspace().getModel().getElementWithCanonicalName("InfrastructureNode://Live/Amazon Web Services/US-East-1/New deployment node 2/New infrastructure node 2")); } + @Test + void test_findElement_Hierachical() throws Exception { + File dslFile = new File("src/test/resources/dsl/find-element-hierarchical.dsl"); + + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(dslFile); + + Component component = parser.getWorkspace().getModel().getSoftwareSystemWithName("A").getContainerWithName("B").getComponentWithName("C"); + assertEquals("Value1", component.getProperties().get("Name1")); + assertEquals("Value2", component.getProperties().get("Name2")); + assertEquals("Value3", component.getProperties().get("Name3")); + } + @Test void test_parallel1() throws Exception { StructurizrDslParser parser = new StructurizrDslParser(); @@ -1285,18 +1298,4 @@ void test_ImageView_WhenParserIsInRestrictedMode() { } } - @Test - void test_extendHierachical() throws Exception { - File dslFile = new File("src/test/resources/dsl/extend-hierarchical.dsl"); - - StructurizrDslParser parser = new StructurizrDslParser(); - parser.parse(dslFile); - - Component component = parser.getWorkspace().getModel().getSoftwareSystemWithName("A").getContainerWithName("B").getComponentWithName("C"); - assertEquals("Value1", component.getProperties().get("Name1")); - assertEquals("Value2", component.getProperties().get("Name2")); - assertEquals("Value3", component.getProperties().get("Name3")); - } - - } \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/FindElementParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/FindElementParserTests.java new file mode 100644 index 000000000..f9ff1d3ba --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/FindElementParserTests.java @@ -0,0 +1,64 @@ +package com.structurizr.dsl; + +import com.structurizr.model.ModelItem; +import com.structurizr.model.Person; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class FindElementParserTests extends AbstractTests { + + private final FindElementParser parser = new FindElementParser(); + + @Test + void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parse(context(), tokens("!element", "name", "tokens")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: !element ", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheIdentifierOrCanonicalNameIsNotSpecified() { + try { + parser.parse(context(), tokens("!element")); + fail(); + } catch (Exception e) { + assertEquals("Expected: !element ", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheReferencedElementCannotBeFound() { + try { + parser.parse(context(), tokens("!element", "Person://User")); + fail(); + } catch (Exception e) { + assertEquals("An element identified by \"Person://User\" could not be found", e.getMessage()); + } + } + + @Test + void test_parse_FindsAnElementByCanonicalName() { + Person user = workspace.getModel().addPerson("User"); + ModelItem element = parser.parse(context(), tokens("!element", "Person://User")); + + assertSame(user, element); + } + + @Test + void test_parse_FindsAnElementByIdentifier() { + Person user = workspace.getModel().addPerson("User"); + + ModelDslContext context = context(); + IdentifiersRegister register = new IdentifiersRegister(); + register.register("user", user); + context.setIdentifierRegister(register); + + ModelItem modelItem = parser.parse(context, tokens("!element", "user")); + assertSame(modelItem, user); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementsParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/FindElementsParserTests.java similarity index 92% rename from structurizr-dsl/src/test/java/com/structurizr/dsl/ElementsParserTests.java rename to structurizr-dsl/src/test/java/com/structurizr/dsl/FindElementsParserTests.java index 9eb68b7f8..49bf3ddf2 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementsParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/FindElementsParserTests.java @@ -3,16 +3,15 @@ import com.structurizr.model.Component; import com.structurizr.model.Container; import com.structurizr.model.Element; -import com.structurizr.model.SoftwareSystem; import org.junit.jupiter.api.Test; import java.util.Set; import static org.junit.jupiter.api.Assertions.*; -class ElementsParserTests extends AbstractTests { +class FindElementsParserTests extends AbstractTests { - private final ElementsParser parser = new ElementsParser(); + private final FindElementsParser parser = new FindElementsParser(); @Test void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/FindRelationshipParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/FindRelationshipParserTests.java new file mode 100644 index 000000000..e66cb93d3 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/FindRelationshipParserTests.java @@ -0,0 +1,58 @@ +package com.structurizr.dsl; + +import com.structurizr.model.ModelItem; +import com.structurizr.model.Person; +import com.structurizr.model.Relationship; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class FindRelationshipParserTests extends AbstractTests { + + private final FindRelationshipParser parser = new FindRelationshipParser(); + + @Test + void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parse(context(), tokens("!relationship", "name", "tokens")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: !relationship ", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheIdentifierIsNotSpecified() { + try { + parser.parse(context(), tokens("!relationship")); + fail(); + } catch (Exception e) { + assertEquals("Expected: !relationship ", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheReferencedRelationshipCannotBeFound() { + try { + parser.parse(context(), tokens("!relationship", "rel")); + fail(); + } catch (Exception e) { + assertEquals("A relationship identified by \"rel\" could not be found", e.getMessage()); + } + } + + @Test + void test_parse_FindsARelationshipByIdentifier() { + Person user = workspace.getModel().addPerson("User"); + Relationship relationship = user.interactsWith(user, "Description"); + + ModelDslContext context = context(); + IdentifiersRegister register = new IdentifiersRegister(); + register.register("rel", relationship); + context.setIdentifierRegister(register); + + ModelItem modelItem = parser.parse(context, tokens("!relationship", "rel")); + assertSame(modelItem, relationship); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/RelationshipsParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/FindRelationshipsParserTests.java similarity index 92% rename from structurizr-dsl/src/test/java/com/structurizr/dsl/RelationshipsParserTests.java rename to structurizr-dsl/src/test/java/com/structurizr/dsl/FindRelationshipsParserTests.java index b1f42db8a..916f23407 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/RelationshipsParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/FindRelationshipsParserTests.java @@ -8,9 +8,9 @@ import static org.junit.jupiter.api.Assertions.*; -class RelationshipsParserTests extends AbstractTests { +class FindRelationshipsParserTests extends AbstractTests { - private final RelationshipsParser parser = new RelationshipsParser(); + private final FindRelationshipsParser parser = new FindRelationshipsParser(); @Test void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { diff --git a/structurizr-dsl/src/test/resources/dsl/deployment-environment-empty.dsl b/structurizr-dsl/src/test/resources/dsl/deployment-environment-empty.dsl index 484323ede..11566eb50 100644 --- a/structurizr-dsl/src/test/resources/dsl/deployment-environment-empty.dsl +++ b/structurizr-dsl/src/test/resources/dsl/deployment-environment-empty.dsl @@ -2,7 +2,7 @@ workspace { model { de = deploymentEnvironment "DeploymentEnvironment" - !ref de { + !element de { dn = deploymentNode "DeploymentNode" } } diff --git a/structurizr-dsl/src/test/resources/dsl/extend/extend-workspace-from-dsl-file.dsl b/structurizr-dsl/src/test/resources/dsl/extend/extend-workspace-from-dsl-file.dsl index 4260ec1e2..5465ce720 100644 --- a/structurizr-dsl/src/test/resources/dsl/extend/extend-workspace-from-dsl-file.dsl +++ b/structurizr-dsl/src/test/resources/dsl/extend/extend-workspace-from-dsl-file.dsl @@ -1,7 +1,7 @@ workspace extends workspace.dsl { model { - !ref softwareSystem1 { + !element softwareSystem1 { webapp = container "Web Application" } diff --git a/structurizr-dsl/src/test/resources/dsl/extend/extend-workspace-from-dsl-url.dsl b/structurizr-dsl/src/test/resources/dsl/extend/extend-workspace-from-dsl-url.dsl index d5a24910a..086f56c12 100644 --- a/structurizr-dsl/src/test/resources/dsl/extend/extend-workspace-from-dsl-url.dsl +++ b/structurizr-dsl/src/test/resources/dsl/extend/extend-workspace-from-dsl-url.dsl @@ -1,7 +1,7 @@ workspace extends https://raw.githubusercontent.com/structurizr/dsl/master/src/test/dsl/extend/workspace.dsl { model { - !ref softwareSystem1 { + !element softwareSystem1 { webapp = container "Web Application" } diff --git a/structurizr-dsl/src/test/resources/dsl/extend/extend-workspace-from-json-file.dsl b/structurizr-dsl/src/test/resources/dsl/extend/extend-workspace-from-json-file.dsl index 06e3ffc8b..e43dd69a7 100644 --- a/structurizr-dsl/src/test/resources/dsl/extend/extend-workspace-from-json-file.dsl +++ b/structurizr-dsl/src/test/resources/dsl/extend/extend-workspace-from-json-file.dsl @@ -1,13 +1,13 @@ workspace extends workspace.json { model { - // !extend with DSL identifier - !extend softwareSystem1 { + // !element with DSL identifier + !element softwareSystem1 { webapp1 = container "Web Application 1" } - // !extend with canonical name - !extend "SoftwareSystem://Software System 1" { + // !element with canonical name + !element "SoftwareSystem://Software System 1" { webapp2 = container "Web Application 2" } diff --git a/structurizr-dsl/src/test/resources/dsl/extend/extend-workspace-from-json-url.dsl b/structurizr-dsl/src/test/resources/dsl/extend/extend-workspace-from-json-url.dsl index e84912308..d59fa686c 100644 --- a/structurizr-dsl/src/test/resources/dsl/extend/extend-workspace-from-json-url.dsl +++ b/structurizr-dsl/src/test/resources/dsl/extend/extend-workspace-from-json-url.dsl @@ -1,13 +1,13 @@ workspace extends https://raw.githubusercontent.com/structurizr/dsl/master/src/test/dsl/extend/workspace.json { model { - // !extend with DSL identifier - !extend softwareSystem1 { + // !element with DSL identifier + !element softwareSystem1 { webapp1 = container "Web Application 1" } - // !extend with canonical name - !extend "SoftwareSystem://Software System 1" { + // !element with canonical name + !element "SoftwareSystem://Software System 1" { webapp2 = container "Web Application 2" } diff --git a/structurizr-dsl/src/test/resources/dsl/extend-hierarchical.dsl b/structurizr-dsl/src/test/resources/dsl/find-element-hierarchical.dsl similarity index 85% rename from structurizr-dsl/src/test/resources/dsl/extend-hierarchical.dsl rename to structurizr-dsl/src/test/resources/dsl/find-element-hierarchical.dsl index c6ed64556..ecc74c77d 100644 --- a/structurizr-dsl/src/test/resources/dsl/extend-hierarchical.dsl +++ b/structurizr-dsl/src/test/resources/dsl/find-element-hierarchical.dsl @@ -7,21 +7,21 @@ workspace { b = container "B" { c = component "C" - !extend c { + !element c { properties { "Name1" "Value1" } } } - !extend b.c { + !element b.c { properties { "Name2" "Value2" } } } - !extend a.b.c { + !element a.b.c { properties { "Name3" "Value3" } diff --git a/structurizr-dsl/src/test/resources/dsl/ref.dsl b/structurizr-dsl/src/test/resources/dsl/find-element.dsl similarity index 83% rename from structurizr-dsl/src/test/resources/dsl/ref.dsl rename to structurizr-dsl/src/test/resources/dsl/find-element.dsl index cf6ce5a1f..a4b0e66cd 100644 --- a/structurizr-dsl/src/test/resources/dsl/ref.dsl +++ b/structurizr-dsl/src/test/resources/dsl/find-element.dsl @@ -2,7 +2,7 @@ workspace extends amazon-web-services.dsl { model { - !ref "DeploymentNode://Live/Amazon Web Services" { + !element "DeploymentNode://Live/Amazon Web Services" { deploymentNode "New deployment node" { infrastructureNode "New infrastructure node" { -> route53 @@ -10,7 +10,7 @@ workspace extends amazon-web-services.dsl { } } - !ref "DeploymentNode://Live/Amazon Web Services/US-East-1" { + !element "DeploymentNode://Live/Amazon Web Services/US-East-1" { deploymentNode "New deployment node 1" { infrastructureNode "New infrastructure node 1" { -> route53 @@ -18,7 +18,7 @@ workspace extends amazon-web-services.dsl { } } - !ref region { + !element region { deploymentNode "New deployment node 2" { infrastructureNode "New infrastructure node 2" { -> route53 @@ -26,7 +26,7 @@ workspace extends amazon-web-services.dsl { } } - !ref live { + !element live { deploymentNode "New deployment node 3" { infrastructureNode "New infrastructure node 3" { -> route53 From e8dfc59d3bc8f8f668ddd9df51878902e1ad5359 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 18 Sep 2024 17:22:44 +0100 Subject: [PATCH 576/717] Adds a @Component annotation. --- structurizr-annotation/README.md | 6 ++++++ .../java/com/structurizr/annotation/Component.java | 12 ++++++++++++ .../matcher/AnnotationTypeMatcherTests.java | 3 ++- .../matcher/annotationTypeMatcher/Controller.java | 4 ---- .../annotationTypeMatcher/CustomerController.java | 4 +++- 5 files changed, 23 insertions(+), 6 deletions(-) create mode 100644 structurizr-annotation/src/main/java/com/structurizr/annotation/Component.java delete mode 100644 structurizr-component/src/test/java/com/structurizr/component/matcher/annotationTypeMatcher/Controller.java diff --git a/structurizr-annotation/README.md b/structurizr-annotation/README.md index a8626a374..d57047c20 100644 --- a/structurizr-annotation/README.md +++ b/structurizr-annotation/README.md @@ -2,3 +2,9 @@ [![Maven Central](https://img.shields.io/maven-central/v/com.structurizr/structurizr-annotation.svg?label=Maven%20Central)](https://search.maven.org/artifact/com.structurizr/structurizr-annotation) +This library defines some custom annotations that you can add to your code. +These serve to either make it explicit how components should be extracted from your codebase (e.g. `@Component`), +or they help supplement the software architecture model (e.g. `@Property`, `@Tag`). + +- This library has no dependencies. +- All annotations have a runtime retention policy, so they will be present in the compiled bytecode. \ No newline at end of file diff --git a/structurizr-annotation/src/main/java/com/structurizr/annotation/Component.java b/structurizr-annotation/src/main/java/com/structurizr/annotation/Component.java new file mode 100644 index 000000000..12fc05230 --- /dev/null +++ b/structurizr-annotation/src/main/java/com/structurizr/annotation/Component.java @@ -0,0 +1,12 @@ +package com.structurizr.annotation; + +import java.lang.annotation.*; + +/** + * A type-level annotation that can be used to indicate the type represents a component. + */ +@Documented +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface Component { +} \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/matcher/AnnotationTypeMatcherTests.java b/structurizr-component/src/test/java/com/structurizr/component/matcher/AnnotationTypeMatcherTests.java index 6e9e0ce11..415b00420 100644 --- a/structurizr-component/src/test/java/com/structurizr/component/matcher/AnnotationTypeMatcherTests.java +++ b/structurizr-component/src/test/java/com/structurizr/component/matcher/AnnotationTypeMatcherTests.java @@ -1,5 +1,6 @@ package com.structurizr.component.matcher; +import com.structurizr.annotation.Component; import com.structurizr.component.Type; import org.apache.bcel.classfile.ClassParser; import org.junit.jupiter.api.Test; @@ -54,7 +55,7 @@ void matches_ReturnsTrue_WhenThereIsAMatch() throws Exception { ClassParser parser = new ClassParser(new File(classes, "com/structurizr/component/matcher/annotationTypeMatcher/CustomerController.class").getAbsolutePath()); Type type = new Type(parser.parse()); - assertTrue(new AnnotationTypeMatcher("com.structurizr.component.matcher.annotationTypeMatcher.Controller").matches(type)); + assertTrue(new AnnotationTypeMatcher(Component.class.getName()).matches(type)); } } \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/matcher/annotationTypeMatcher/Controller.java b/structurizr-component/src/test/java/com/structurizr/component/matcher/annotationTypeMatcher/Controller.java deleted file mode 100644 index 967ee7eee..000000000 --- a/structurizr-component/src/test/java/com/structurizr/component/matcher/annotationTypeMatcher/Controller.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.structurizr.component.matcher.annotationTypeMatcher; - -public @interface Controller { -} diff --git a/structurizr-component/src/test/java/com/structurizr/component/matcher/annotationTypeMatcher/CustomerController.java b/structurizr-component/src/test/java/com/structurizr/component/matcher/annotationTypeMatcher/CustomerController.java index a9a6f3d1c..2cd2b65e4 100644 --- a/structurizr-component/src/test/java/com/structurizr/component/matcher/annotationTypeMatcher/CustomerController.java +++ b/structurizr-component/src/test/java/com/structurizr/component/matcher/annotationTypeMatcher/CustomerController.java @@ -1,5 +1,7 @@ package com.structurizr.component.matcher.annotationTypeMatcher; -@Controller +import com.structurizr.annotation.Component; + +@Component public class CustomerController { } From 6c39ea608a6e9f462944590fccc48981b905d45f Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 18 Sep 2024 19:02:14 +0100 Subject: [PATCH 577/717] Adds DB URL. --- .../src/test/resources/dsl/spring-petclinic/workspace.dsl | 1 + 1 file changed, 1 insertion(+) diff --git a/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl b/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl index 9144e5fce..b40a2d4bb 100644 --- a/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl +++ b/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl @@ -12,6 +12,7 @@ workspace "Spring PetClinic" "A C4 model of the Spring PetClinic sample app (htt relationalDatabaseSchema = container "Relational Database Schema" { description "Stores information regarding the veterinarians, the clients, and their pets." technology "Relational Database Schema" + url "https://github.com/spring-projects/spring-petclinic/tree/main/src/main/resources/db" tag "Relational Database Schema" } From ce11bf3966568fa7c5c96dc8cce148b60ba4f361 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 18 Sep 2024 19:03:54 +0100 Subject: [PATCH 578/717] Fixes #337. --- changelog.md | 1 + .../mermaid/MermaidDiagramExporter.java | 10 +- .../export/mermaid/36141-Components.mmd | 2 +- .../export/mermaid/36141-Containers.mmd | 2 +- .../mermaid/36141-DevelopmentDeployment.mmd | 16 +- .../export/mermaid/36141-LiveDeployment.mmd | 26 +-- .../export/mermaid/36141-SignIn.mmd | 2 +- .../export/mermaid/36141-SystemContext.mmd | 2 +- .../export/mermaid/36141-SystemLandscape.mmd | 2 +- .../54915-AmazonWebServicesDeployment.mmd | 12 +- .../mermaid/MermaidDiagramExporterTests.java | 208 +++++++++--------- .../export/mermaid/groups-Components.mmd | 4 +- .../export/mermaid/groups-Containers.mmd | 4 +- .../export/mermaid/groups-SystemLandscape.mmd | 6 +- .../export/mermaid/nested-groups.mmd | 10 +- 15 files changed, 156 insertions(+), 151 deletions(-) diff --git a/changelog.md b/changelog.md index 19fd510c0..b3c01af79 100644 --- a/changelog.md +++ b/changelog.md @@ -19,6 +19,7 @@ - structurizr-dsl: Adds support for local theme files to be specified via `theme` (https://github.com/structurizr/java/issues/331). - structurizr-export: Adds support for icons to the Ilograph exporter (https://github.com/structurizr/java/issues/332). - structurizr-export: Adds support for imports to the Ilograph exporter (https://github.com/structurizr/java/issues/332). +- structurizr-export: Fixes https://github.com/structurizr/java/issues/337 (Malformed subgraph name in Mermaid render). ## 2.2.0 (2nd July 2024) diff --git a/structurizr-export/src/main/java/com/structurizr/export/mermaid/MermaidDiagramExporter.java b/structurizr-export/src/main/java/com/structurizr/export/mermaid/MermaidDiagramExporter.java index 54b6b2652..cb0569258 100644 --- a/structurizr-export/src/main/java/com/structurizr/export/mermaid/MermaidDiagramExporter.java +++ b/structurizr-export/src/main/java/com/structurizr/export/mermaid/MermaidDiagramExporter.java @@ -80,7 +80,7 @@ protected void writeFooter(ModelView view, IndentingWriter writer) { @Override protected void startEnterpriseBoundary(ModelView view, String enterpriseName, IndentingWriter writer) { - writer.writeLine("subgraph enterprise [" + enterpriseName + "]"); + writer.writeLine("subgraph enterprise [\"" + enterpriseName + "\"]"); writer.indent(); writer.writeLine("style enterprise fill:#ffffff,stroke:#444444,color:#444444"); writer.writeLine(); @@ -118,7 +118,7 @@ protected void startGroupBoundary(ModelView view, String group, IndentingWriter color = elementStyle.getColor(); } - writer.writeLine(String.format("subgraph group%s [" + groupName + "]", groupId)); + writer.writeLine(String.format("subgraph group%s [\"" + groupName + "\"]", groupId)); writer.indent(); writer.writeLine(String.format("style group%s fill:#ffffff,stroke:%s,color:%s,stroke-dasharray:5", groupId, color, color)); writer.writeLine(); @@ -136,7 +136,7 @@ protected void startSoftwareSystemBoundary(ModelView view, SoftwareSystem softwa ElementStyle elementStyle = view.getViewSet().getConfiguration().getStyles().findElementStyle(softwareSystem); String color = elementStyle.getStroke(); - writer.writeLine(String.format("subgraph %s [%s]", softwareSystem.getId(), softwareSystem.getName())); + writer.writeLine(String.format("subgraph %s [\"%s\"]", softwareSystem.getId(), softwareSystem.getName())); writer.indent(); writer.writeLine(String.format("style %s fill:#ffffff,stroke:%s,color:%s", softwareSystem.getId(), color, color)); writer.writeLine(); @@ -154,7 +154,7 @@ protected void startContainerBoundary(ModelView view, Container container, Inden ElementStyle elementStyle = view.getViewSet().getConfiguration().getStyles().findElementStyle(container); String color = elementStyle.getStroke(); - writer.writeLine(String.format("subgraph %s [%s]", container.getId(), container.getName())); + writer.writeLine(String.format("subgraph %s [\"%s\"]", container.getId(), container.getName())); writer.indent(); writer.writeLine(String.format("style %s fill:#ffffff,stroke:%s,color:%s", container.getId(), color, color)); writer.writeLine(); @@ -171,7 +171,7 @@ protected void endContainerBoundary(ModelView view, IndentingWriter writer) { protected void startDeploymentNodeBoundary(DeploymentView view, DeploymentNode deploymentNode, IndentingWriter writer) { ElementStyle elementStyle = view.getViewSet().getConfiguration().getStyles().findElementStyle(deploymentNode); - writer.writeLine(String.format("subgraph %s [%s]", deploymentNode.getId(), deploymentNode.getName())); + writer.writeLine(String.format("subgraph %s [\"%s\"]", deploymentNode.getId(), deploymentNode.getName())); writer.indent(); writer.writeLine(String.format("style %s fill:#ffffff,stroke:%s,color:%s", deploymentNode.getId(), elementStyle.getStroke(), elementStyle.getColor())); writer.writeLine(); diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-Components.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-Components.mmd index 7f759f871..505e7fa62 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-Components.mmd +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-Components.mmd @@ -15,7 +15,7 @@ graph TB 18[("
    Database
    [Container: Oracle Database Schema]
    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    ")] style 18 fill:#438dd5,stroke:#2e6295,color:#ffffff - subgraph 11 [API Application] + subgraph 11 ["API Application"] style 11 fill:#ffffff,stroke:#2e6295,color:#2e6295 12["
    Sign In Controller
    [Component: Spring MVC Rest Controller]
    Allows users to sign in to
    the Internet Banking System.
    "] diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-Containers.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-Containers.mmd index 86d53a05a..e962db529 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-Containers.mmd +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-Containers.mmd @@ -11,7 +11,7 @@ graph TB 5["
    E-mail System
    [Software System]
    The internal Microsoft
    Exchange e-mail system.
    "] style 5 fill:#999999,stroke:#6b6b6b,color:#ffffff - subgraph 7 [Internet Banking System] + subgraph 7 ["Internet Banking System"] style 7 fill:#ffffff,stroke:#0b4884,color:#0b4884 10["
    Web Application
    [Container: Java and Spring MVC]
    Delivers the static content
    and the Internet banking
    single page application.
    "] diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-DevelopmentDeployment.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-DevelopmentDeployment.mmd index b933bd4fb..0347c49a8 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-DevelopmentDeployment.mmd +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-DevelopmentDeployment.mmd @@ -4,20 +4,20 @@ graph TB subgraph diagram ["Internet Banking System - Deployment - Development"] style diagram fill:#ffffff,stroke:#ffffff - subgraph 50 [Developer Laptop] + subgraph 50 ["Developer Laptop"] style 50 fill:#ffffff,stroke:#888888,color:#000000 - subgraph 51 [Web Browser] + subgraph 51 ["Web Browser"] style 51 fill:#ffffff,stroke:#888888,color:#000000 52["
    Single-Page Application
    [Container: JavaScript and Angular]
    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    "] style 52 fill:#438dd5,stroke:#2e6295,color:#ffffff end - subgraph 53 [Docker Container - Web Server] + subgraph 53 ["Docker Container - Web Server"] style 53 fill:#ffffff,stroke:#888888,color:#000000 - subgraph 54 [Apache Tomcat] + subgraph 54 ["Apache Tomcat"] style 54 fill:#ffffff,stroke:#888888,color:#000000 55["
    Web Application
    [Container: Java and Spring MVC]
    Delivers the static content
    and the Internet banking
    single page application.
    "] @@ -28,10 +28,10 @@ graph TB end - subgraph 59 [Docker Container - Database Server] + subgraph 59 ["Docker Container - Database Server"] style 59 fill:#ffffff,stroke:#888888,color:#000000 - subgraph 60 [Database Server] + subgraph 60 ["Database Server"] style 60 fill:#ffffff,stroke:#888888,color:#000000 61[("
    Database
    [Container: Oracle Database Schema]
    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    ")] @@ -42,10 +42,10 @@ graph TB end - subgraph 63 [Big Bank plc] + subgraph 63 ["Big Bank plc"] style 63 fill:#ffffff,stroke:#888888,color:#000000 - subgraph 64 [bigbank-dev001] + subgraph 64 ["bigbank-dev001"] style 64 fill:#ffffff,stroke:#888888,color:#000000 65["
    Mainframe Banking System
    [Software System]
    Stores all of the core
    banking information about
    customers, accounts,
    transactions, etc.
    "] diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-LiveDeployment.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-LiveDeployment.mmd index e4a56d3be..e4cedb00e 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-LiveDeployment.mmd +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-LiveDeployment.mmd @@ -4,17 +4,17 @@ graph TB subgraph diagram ["Internet Banking System - Deployment - Live"] style diagram fill:#ffffff,stroke:#ffffff - subgraph 67 [Customer's mobile device] + subgraph 67 ["Customer's mobile device"] style 67 fill:#ffffff,stroke:#888888,color:#000000 68["
    Mobile App
    [Container: Xamarin]
    Provides a limited subset of
    the Internet banking
    functionality to customers
    via their mobile device.
    "] style 68 fill:#438dd5,stroke:#2e6295,color:#ffffff end - subgraph 69 [Customer's computer] + subgraph 69 ["Customer's computer"] style 69 fill:#ffffff,stroke:#888888,color:#000000 - subgraph 70 [Web Browser] + subgraph 70 ["Web Browser"] style 70 fill:#ffffff,stroke:#888888,color:#000000 71["
    Single-Page Application
    [Container: JavaScript and Angular]
    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    "] @@ -23,13 +23,13 @@ graph TB end - subgraph 72 [Big Bank plc] + subgraph 72 ["Big Bank plc"] style 72 fill:#ffffff,stroke:#888888,color:#000000 - subgraph 73 [bigbank-web***] + subgraph 73 ["bigbank-web***"] style 73 fill:#ffffff,stroke:#888888,color:#000000 - subgraph 74 [Apache Tomcat] + subgraph 74 ["Apache Tomcat"] style 74 fill:#ffffff,stroke:#888888,color:#000000 75["
    Web Application
    [Container: Java and Spring MVC]
    Delivers the static content
    and the Internet banking
    single page application.
    "] @@ -38,10 +38,10 @@ graph TB end - subgraph 77 [bigbank-api***] + subgraph 77 ["bigbank-api***"] style 77 fill:#ffffff,stroke:#888888,color:#000000 - subgraph 78 [Apache Tomcat] + subgraph 78 ["Apache Tomcat"] style 78 fill:#ffffff,stroke:#888888,color:#000000 79["
    API Application
    [Container: Java and Spring MVC]
    Provides Internet banking
    functionality via a
    JSON/HTTPS API.
    "] @@ -50,10 +50,10 @@ graph TB end - subgraph 82 [bigbank-db01] + subgraph 82 ["bigbank-db01"] style 82 fill:#ffffff,stroke:#888888,color:#000000 - subgraph 83 [Oracle - Primary] + subgraph 83 ["Oracle - Primary"] style 83 fill:#ffffff,stroke:#888888,color:#000000 84[("
    Database
    [Container: Oracle Database Schema]
    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    ")] @@ -62,10 +62,10 @@ graph TB end - subgraph 86 [bigbank-db02] + subgraph 86 ["bigbank-db02"] style 86 fill:#ffffff,stroke:#888888,color:#000000 - subgraph 87 [Oracle - Secondary] + subgraph 87 ["Oracle - Secondary"] style 87 fill:#ffffff,stroke:#888888,color:#000000 88[("
    Database
    [Container: Oracle Database Schema]
    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    ")] @@ -74,7 +74,7 @@ graph TB end - subgraph 90 [bigbank-prod001] + subgraph 90 ["bigbank-prod001"] style 90 fill:#ffffff,stroke:#888888,color:#000000 91["
    Mainframe Banking System
    [Software System]
    Stores all of the core
    banking information about
    customers, accounts,
    transactions, etc.
    "] diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SignIn.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SignIn.mmd index 1715a20f8..378a93c7a 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SignIn.mmd +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SignIn.mmd @@ -4,7 +4,7 @@ graph TB subgraph diagram ["API Application - Dynamic"] style diagram fill:#ffffff,stroke:#ffffff - subgraph 11 [API Application] + subgraph 11 ["API Application"] style 11 fill:#ffffff,stroke:#2e6295,color:#2e6295 12["
    Sign In Controller
    [Component: Spring MVC Rest Controller]
    Allows users to sign in to
    the Internet Banking System.
    "] diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SystemContext.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SystemContext.mmd index 66a0a966a..1dd22f429 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SystemContext.mmd +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SystemContext.mmd @@ -4,7 +4,7 @@ graph TB subgraph diagram ["Internet Banking System - System Context"] style diagram fill:#ffffff,stroke:#ffffff - subgraph group1 [Big Bank plc] + subgraph group1 ["Big Bank plc"] style group1 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 4["
    Mainframe Banking System
    [Software System]
    Stores all of the core
    banking information about
    customers, accounts,
    transactions, etc.
    "] diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SystemLandscape.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SystemLandscape.mmd index f763af979..83023a9e9 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SystemLandscape.mmd +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SystemLandscape.mmd @@ -4,7 +4,7 @@ graph TB subgraph diagram ["System Landscape"] style diagram fill:#ffffff,stroke:#ffffff - subgraph group1 [Big Bank plc] + subgraph group1 ["Big Bank plc"] style group1 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 2["
    Customer Service Staff
    [Person]
    Customer service staff within
    the bank.
    "] diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/54915-AmazonWebServicesDeployment.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/54915-AmazonWebServicesDeployment.mmd index dac7a2f2e..4af05234c 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/54915-AmazonWebServicesDeployment.mmd +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/54915-AmazonWebServicesDeployment.mmd @@ -4,16 +4,16 @@ graph LR subgraph diagram ["Spring PetClinic - Deployment - Live"] style diagram fill:#ffffff,stroke:#ffffff - subgraph 5 [Amazon Web Services] + subgraph 5 ["Amazon Web Services"] style 5 fill:#ffffff,stroke:#232f3e,color:#232f3e - subgraph 6 [US-East-1] + subgraph 6 ["US-East-1"] style 6 fill:#ffffff,stroke:#147eba,color:#147eba - subgraph 12 [Amazon RDS] + subgraph 12 ["Amazon RDS"] style 12 fill:#ffffff,stroke:#3b48cc,color:#3b48cc - subgraph 13 [MySQL] + subgraph 13 ["MySQL"] style 13 fill:#ffffff,stroke:#3b48cc,color:#3b48cc 14[("
    Database
    [Container: Relational database schema]
    Stores information regarding
    the veterinarians, the
    clients, and their pets.
    ")] @@ -26,10 +26,10 @@ graph LR style 7 fill:#ffffff,stroke:#693cc5,color:#693cc5 8("
    Elastic Load Balancer
    [Infrastructure Node]
    Automatically distributes
    incoming application traffic.
    ") style 8 fill:#ffffff,stroke:#693cc5,color:#693cc5 - subgraph 9 [Autoscaling group] + subgraph 9 ["Autoscaling group"] style 9 fill:#ffffff,stroke:#cc2264,color:#cc2264 - subgraph 10 [Amazon EC2] + subgraph 10 ["Amazon EC2"] style 10 fill:#ffffff,stroke:#d86613,color:#d86613 11("
    Web Application
    [Container: Java and Spring Boot]
    Allows employees to view and
    manage information regarding
    the veterinarians, the
    clients, and their pets.
    ") diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/MermaidDiagramExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/mermaid/MermaidDiagramExporterTests.java index e01c6ca53..b5b1a8774 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/MermaidDiagramExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/MermaidDiagramExporterTests.java @@ -142,28 +142,29 @@ public void test_renderContainerDiagramWithExternalContainers() { containerView.add(container2); Diagram diagram = new MermaidDiagramExporter().export(containerView); - assertEquals("graph TB\n" + - " linkStyle default fill:#ffffff\n" + - "\n" + - " subgraph diagram [\"Software System 1 - Containers\"]\n" + - " style diagram fill:#ffffff,stroke:#ffffff\n" + - "\n" + - " subgraph 1 [Software System 1]\n" + - " style 1 fill:#ffffff,stroke:#9a9a9a,color:#9a9a9a\n" + - "\n" + - " 2[\"
    Container 1
    [Container]
    \"]\n" + - " style 2 fill:#dddddd,stroke:#9a9a9a,color:#000000\n" + - " end\n" + - "\n" + - " subgraph 3 [Software System 2]\n" + - " style 3 fill:#ffffff,stroke:#9a9a9a,color:#9a9a9a\n" + - "\n" + - " 4[\"
    Container 2
    [Container]
    \"]\n" + - " style 4 fill:#dddddd,stroke:#9a9a9a,color:#000000\n" + - " end\n" + - "\n" + - " 2-. \"
    Uses
    \" .->4\n" + - " end", diagram.getDefinition()); + assertEquals(""" +graph TB + linkStyle default fill:#ffffff + + subgraph diagram ["Software System 1 - Containers"] + style diagram fill:#ffffff,stroke:#ffffff + + subgraph 1 ["Software System 1"] + style 1 fill:#ffffff,stroke:#9a9a9a,color:#9a9a9a + + 2["
    Container 1
    [Container]
    "] + style 2 fill:#dddddd,stroke:#9a9a9a,color:#000000 + end + + subgraph 3 ["Software System 2"] + style 3 fill:#ffffff,stroke:#9a9a9a,color:#9a9a9a + + 4["
    Container 2
    [Container]
    "] + style 4 fill:#dddddd,stroke:#9a9a9a,color:#000000 + end + + 2-. "
    Uses
    " .->4 + end""", diagram.getDefinition()); } @Test @@ -183,28 +184,29 @@ public void test_renderComponentDiagramWithExternalComponents() { componentView.add(component2); Diagram diagram = new MermaidDiagramExporter().export(componentView); - assertEquals("graph TB\n" + - " linkStyle default fill:#ffffff\n" + - "\n" + - " subgraph diagram [\"Software System 1 - Container 1 - Components\"]\n" + - " style diagram fill:#ffffff,stroke:#ffffff\n" + - "\n" + - " subgraph 2 [Container 1]\n" + - " style 2 fill:#ffffff,stroke:#9a9a9a,color:#9a9a9a\n" + - "\n" + - " 3[\"
    Component 1
    [Component]
    \"]\n" + - " style 3 fill:#dddddd,stroke:#9a9a9a,color:#000000\n" + - " end\n" + - "\n" + - " subgraph 5 [Container 2]\n" + - " style 5 fill:#ffffff,stroke:#9a9a9a,color:#9a9a9a\n" + - "\n" + - " 6[\"
    Component 2
    [Component]
    \"]\n" + - " style 6 fill:#dddddd,stroke:#9a9a9a,color:#000000\n" + - " end\n" + - "\n" + - " 3-. \"
    Uses
    \" .->6\n" + - " end", diagram.getDefinition()); + assertEquals(""" +graph TB + linkStyle default fill:#ffffff + + subgraph diagram ["Software System 1 - Container 1 - Components"] + style diagram fill:#ffffff,stroke:#ffffff + + subgraph 2 ["Container 1"] + style 2 fill:#ffffff,stroke:#9a9a9a,color:#9a9a9a + + 3["
    Component 1
    [Component]
    "] + style 3 fill:#dddddd,stroke:#9a9a9a,color:#000000 + end + + subgraph 5 ["Container 2"] + style 5 fill:#ffffff,stroke:#9a9a9a,color:#9a9a9a + + 6["
    Component 2
    [Component]
    "] + style 6 fill:#dddddd,stroke:#9a9a9a,color:#000000 + end + + 3-. "
    Uses
    " .->6 + end""", diagram.getDefinition()); } @Test @@ -222,68 +224,70 @@ public void test_renderGroupStyles() { MermaidDiagramExporter exporter = new MermaidDiagramExporter(); Diagram diagram = exporter.export(view); - assertEquals("graph TB\n" + - " linkStyle default fill:#ffffff\n" + - "\n" + - " subgraph diagram [\"System Landscape\"]\n" + - " style diagram fill:#ffffff,stroke:#ffffff\n" + - "\n" + - " subgraph group1 [Group 1]\n" + - " style group1 fill:#ffffff,stroke:#111111,color:#111111,stroke-dasharray:5\n" + - "\n" + - " 1[\"
    User 1
    [Person]
    \"]\n" + - " style 1 fill:#dddddd,stroke:#9a9a9a,color:#000000\n" + - " end\n" + - "\n" + - " subgraph group2 [Group 2]\n" + - " style group2 fill:#ffffff,stroke:#222222,color:#222222,stroke-dasharray:5\n" + - "\n" + - " 2[\"
    User 2
    [Person]
    \"]\n" + - " style 2 fill:#dddddd,stroke:#9a9a9a,color:#000000\n" + - " end\n" + - "\n" + - " subgraph group3 [Group 3]\n" + - " style group3 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5\n" + - "\n" + - " 3[\"
    User 3
    [Person]
    \"]\n" + - " style 3 fill:#dddddd,stroke:#9a9a9a,color:#000000\n" + - " end\n" + - "\n" + - "\n" + - " end", diagram.getDefinition()); + assertEquals(""" +graph TB + linkStyle default fill:#ffffff + + subgraph diagram ["System Landscape"] + style diagram fill:#ffffff,stroke:#ffffff + + subgraph group1 ["Group 1"] + style group1 fill:#ffffff,stroke:#111111,color:#111111,stroke-dasharray:5 + + 1["
    User 1
    [Person]
    "] + style 1 fill:#dddddd,stroke:#9a9a9a,color:#000000 + end + + subgraph group2 ["Group 2"] + style group2 fill:#ffffff,stroke:#222222,color:#222222,stroke-dasharray:5 + + 2["
    User 2
    [Person]
    "] + style 2 fill:#dddddd,stroke:#9a9a9a,color:#000000 + end + + subgraph group3 ["Group 3"] + style group3 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 + + 3["
    User 3
    [Person]
    "] + style 3 fill:#dddddd,stroke:#9a9a9a,color:#000000 + end + + + end""", diagram.getDefinition()); workspace.getViews().getConfiguration().getStyles().addElementStyle("Group").color("#aabbcc"); diagram = exporter.export(view); - assertEquals("graph TB\n" + - " linkStyle default fill:#ffffff\n" + - "\n" + - " subgraph diagram [\"System Landscape\"]\n" + - " style diagram fill:#ffffff,stroke:#ffffff\n" + - "\n" + - " subgraph group1 [Group 1]\n" + - " style group1 fill:#ffffff,stroke:#111111,color:#111111,stroke-dasharray:5\n" + - "\n" + - " 1[\"
    User 1
    [Person]
    \"]\n" + - " style 1 fill:#dddddd,stroke:#9a9a9a,color:#000000\n" + - " end\n" + - "\n" + - " subgraph group2 [Group 2]\n" + - " style group2 fill:#ffffff,stroke:#222222,color:#222222,stroke-dasharray:5\n" + - "\n" + - " 2[\"
    User 2
    [Person]
    \"]\n" + - " style 2 fill:#dddddd,stroke:#9a9a9a,color:#000000\n" + - " end\n" + - "\n" + - " subgraph group3 [Group 3]\n" + - " style group3 fill:#ffffff,stroke:#aabbcc,color:#aabbcc,stroke-dasharray:5\n" + - "\n" + - " 3[\"
    User 3
    [Person]
    \"]\n" + - " style 3 fill:#dddddd,stroke:#9a9a9a,color:#000000\n" + - " end\n" + - "\n" + - "\n" + - " end", diagram.getDefinition()); + assertEquals(""" +graph TB + linkStyle default fill:#ffffff + + subgraph diagram ["System Landscape"] + style diagram fill:#ffffff,stroke:#ffffff + + subgraph group1 ["Group 1"] + style group1 fill:#ffffff,stroke:#111111,color:#111111,stroke-dasharray:5 + + 1["
    User 1
    [Person]
    "] + style 1 fill:#dddddd,stroke:#9a9a9a,color:#000000 + end + + subgraph group2 ["Group 2"] + style group2 fill:#ffffff,stroke:#222222,color:#222222,stroke-dasharray:5 + + 2["
    User 2
    [Person]
    "] + style 2 fill:#dddddd,stroke:#9a9a9a,color:#000000 + end + + subgraph group3 ["Group 3"] + style group3 fill:#ffffff,stroke:#aabbcc,color:#aabbcc,stroke-dasharray:5 + + 3["
    User 3
    [Person]
    "] + style 3 fill:#dddddd,stroke:#9a9a9a,color:#000000 + end + + + end""", diagram.getDefinition()); } @Test diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-Components.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-Components.mmd index 8a379ec14..62d5a7742 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-Components.mmd +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-Components.mmd @@ -7,10 +7,10 @@ graph TB 3["
    C
    [Software System]
    "] style 3 fill:#dddddd,stroke:#9a9a9a,color:#000000 - subgraph 6 [F] + subgraph 6 ["F"] style 6 fill:#ffffff,stroke:#9a9a9a,color:#9a9a9a - subgraph group1 [Group 5] + subgraph group1 ["Group 5"] style group1 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 8["
    H
    [Component]
    "] diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-Containers.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-Containers.mmd index c11fc9560..768970480 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-Containers.mmd +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-Containers.mmd @@ -7,10 +7,10 @@ graph TB 3["
    C
    [Software System]
    "] style 3 fill:#dddddd,stroke:#9a9a9a,color:#000000 - subgraph 4 [D] + subgraph 4 ["D"] style 4 fill:#ffffff,stroke:#9a9a9a,color:#9a9a9a - subgraph group1 [Group 4] + subgraph group1 ["Group 4"] style group1 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 6["
    F
    [Container]
    "] diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-SystemLandscape.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-SystemLandscape.mmd index 24cb88486..65efa9926 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-SystemLandscape.mmd +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-SystemLandscape.mmd @@ -4,19 +4,19 @@ graph TB subgraph diagram ["System Landscape"] style diagram fill:#ffffff,stroke:#ffffff - subgraph group1 [Group 1] + subgraph group1 ["Group 1"] style group1 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 2["
    B
    [Software System]
    "] style 2 fill:#dddddd,stroke:#9a9a9a,color:#000000 end - subgraph group2 [Group 2] + subgraph group2 ["Group 2"] style group2 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 3["
    C
    [Software System]
    "] style 3 fill:#dddddd,stroke:#9a9a9a,color:#000000 - subgraph group3 [Group 3] + subgraph group3 ["Group 3"] style group3 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 4["
    D
    [Software System]
    "] diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/nested-groups.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/nested-groups.mmd index e48b4e3ec..74a0cfe4d 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/nested-groups.mmd +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/nested-groups.mmd @@ -4,24 +4,24 @@ graph TB subgraph diagram ["System Landscape"] style diagram fill:#ffffff,stroke:#ffffff - subgraph group1 [Organisation 1] + subgraph group1 ["Organisation 1"] style group1 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 3["
    Organisation 1
    [Software System]
    "] style 3 fill:#dddddd,stroke:#9a9a9a,color:#000000 - subgraph group2 [Department 1] + subgraph group2 ["Department 1"] style group2 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 5["
    Department 1
    [Software System]
    "] style 5 fill:#dddddd,stroke:#9a9a9a,color:#000000 - subgraph group3 [Team 1] + subgraph group3 ["Team 1"] style group3 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 1["
    Team 1
    [Software System]
    "] style 1 fill:#dddddd,stroke:#9a9a9a,color:#000000 end - subgraph group4 [Team 2] + subgraph group4 ["Team 2"] style group4 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 2["
    Team 2
    [Software System]
    "] @@ -32,7 +32,7 @@ graph TB end - subgraph group5 [Organisation 2] + subgraph group5 ["Organisation 2"] style group5 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 4["
    Organisation 2
    [Software System]
    "] From 71de823160bd45f461d63f8a15ec7aa3cb376064 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 18 Sep 2024 23:51:26 +0100 Subject: [PATCH 579/717] Adds assertions re: URLs. --- .../java/com/structurizr/component/ComponentFinderTests.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/structurizr-component/src/test/java/com/structurizr/component/ComponentFinderTests.java b/structurizr-component/src/test/java/com/structurizr/component/ComponentFinderTests.java index 5e7ad05ce..68ff4698c 100644 --- a/structurizr-component/src/test/java/com/structurizr/component/ComponentFinderTests.java +++ b/structurizr-component/src/test/java/com/structurizr/component/ComponentFinderTests.java @@ -5,6 +5,7 @@ import com.structurizr.component.filter.IncludeFullyQualifiedNameRegexFilter; import com.structurizr.component.matcher.ImplementsTypeMatcher; import com.structurizr.component.naming.TypeNamingStrategy; +import com.structurizr.component.url.PrefixSourceUrlStrategy; import com.structurizr.model.Component; import com.structurizr.model.Container; import com.structurizr.model.SoftwareSystem; @@ -35,6 +36,7 @@ void run() { .filteredBy(new IncludeFullyQualifiedNameRegexFilter("com\\.structurizr\\.component\\.example\\..*")) .withName(new TypeNamingStrategy()) .withDescription(new FirstSentenceDescriptionStrategy()) + .withUrl(new PrefixSourceUrlStrategy("https://example.com/src/main/java")) .build() ) .withStrategy(new ComponentFinderStrategyBuilder() @@ -43,6 +45,7 @@ void run() { .filteredBy(new IncludeFullyQualifiedNameRegexFilter("com\\.structurizr\\.component\\.example\\..*")) .withName(new TypeNamingStrategy()) .withDescription(new FirstSentenceDescriptionStrategy()) + .withUrl(new PrefixSourceUrlStrategy("https://example.com/src/main/java")) .build() ) .build(); @@ -52,11 +55,13 @@ void run() { assertEquals(2, container.getComponents().size()); Component exampleController = container.getComponentWithName("ExampleController"); assertNotNull(exampleController); + assertEquals("https://example.com/src/main/java/com/structurizr/component/example/ExampleController.java", exampleController.getUrl()); assertTrue(exampleController.hasTag("Controller")); assertEquals("https://example.com", exampleController.getProperties().get("Documentation")); Component exampleRepository = container.getComponentWithName("ExampleRepository"); assertNotNull(exampleRepository); + assertEquals("https://example.com/src/main/java/com/structurizr/component/example/ExampleRepository.java", exampleRepository.getUrl()); assertTrue(exampleController.hasEfferentRelationshipWith(exampleRepository)); } From 3c83773348f9252938d0df2d5e5b1f40ff7b252c Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 18 Sep 2024 23:58:12 +0100 Subject: [PATCH 580/717] Fixes for Windows file separators. --- .../url/PrefixSourceUrlStrategy.java | 2 +- .../url/PrefixSourceUrlStrategyTests.java | 26 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 structurizr-component/src/test/java/com/structurizr/component/url/PrefixSourceUrlStrategyTests.java diff --git a/structurizr-component/src/main/java/com/structurizr/component/url/PrefixSourceUrlStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/url/PrefixSourceUrlStrategy.java index 6419161d5..1fd3047d0 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/url/PrefixSourceUrlStrategy.java +++ b/structurizr-component/src/main/java/com/structurizr/component/url/PrefixSourceUrlStrategy.java @@ -24,7 +24,7 @@ public PrefixSourceUrlStrategy(String prefix) { @Override public String urlOf(Type type) { - return prefix + type.getSource(); + return prefix + (type.getSource().replaceAll("\\\\", "/")); } @Override diff --git a/structurizr-component/src/test/java/com/structurizr/component/url/PrefixSourceUrlStrategyTests.java b/structurizr-component/src/test/java/com/structurizr/component/url/PrefixSourceUrlStrategyTests.java new file mode 100644 index 000000000..d4de1065c --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/url/PrefixSourceUrlStrategyTests.java @@ -0,0 +1,26 @@ +package com.structurizr.component.url; + +import com.structurizr.component.Type; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class PrefixSourceUrlStrategyTests { + + @Test + void test_urlOf_WhenTheSourceUsesForwardSlashFileSeparators() { + Type type = new Type("com.example.ClassName"); + type.setSource("com/example/ClassName.java"); + + assertEquals("https://example.com/src/main/java/com/example/ClassName.java", new PrefixSourceUrlStrategy("https://example.com/src/main/java").urlOf(type)); + } + + @Test + void test_urlOf_WhenTheSourceUsesBackslashFileSeparators() { + Type type = new Type("com.example.ClassName"); + type.setSource("com\\example\\ClassName.java"); + + assertEquals("https://example.com/src/main/java/com/example/ClassName.java", new PrefixSourceUrlStrategy("https://example.com/src/main/java").urlOf(type)); + } + +} \ No newline at end of file From 12ddd83421fe073ba7fa7284aba4fe182e842ad0 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 19 Sep 2024 09:03:08 +0100 Subject: [PATCH 581/717] It now doesn't matter to the component finder what the ordering of source or class type providers is. --- .../java/com/structurizr/component/Type.java | 6 ++- .../com/structurizr/component/TypeFinder.java | 12 +---- .../structurizr/component/TypeRepository.java | 15 +++++- .../url/PrefixSourceUrlStrategy.java | 6 ++- .../component/ComponentFinderTests.java | 2 +- .../component/TypeRepositoryTests.java | 52 +++++++++++++++++++ 6 files changed, 78 insertions(+), 15 deletions(-) create mode 100644 structurizr-component/src/test/java/com/structurizr/component/TypeRepositoryTests.java diff --git a/structurizr-component/src/main/java/com/structurizr/component/Type.java b/structurizr-component/src/main/java/com/structurizr/component/Type.java index f267a0e82..3e3be19ec 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/Type.java +++ b/structurizr-component/src/main/java/com/structurizr/component/Type.java @@ -16,7 +16,7 @@ public class Type { private static final String STRUCTURIZR_PROPERTY_ANNOTATION = "Lcom/structurizr/annotation/Property;"; private static final String STRUCTURIZR_PROPERTIES_ANNOTATION = "Lcom/structurizr/annotation/Properties;"; - private final JavaClass javaClass; + private JavaClass javaClass = null; private final String fullyQualifiedName; private String description; private String source; @@ -72,6 +72,10 @@ public JavaClass getJavaClass() { return this.javaClass; } + void setJavaClass(JavaClass javaClass) { + this.javaClass = javaClass; + } + public void addDependency(Type type) { this.dependencies.add(type); } diff --git a/structurizr-component/src/main/java/com/structurizr/component/TypeFinder.java b/structurizr-component/src/main/java/com/structurizr/component/TypeFinder.java index d1b843464..5ee4dcf3f 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/TypeFinder.java +++ b/structurizr-component/src/main/java/com/structurizr/component/TypeFinder.java @@ -27,17 +27,7 @@ void run(Collection typeProviders, TypeFilter typeFilter, TypeRepo } if (accepted) { - if (type.getJavaClass() != null) { - // this is the BCEL identified type - typeRepository.add(type); - } else { - // this is the source code identified type - Type bcelType = typeRepository.getType(type.getFullyQualifiedName()); - if (bcelType != null) { - bcelType.setDescription(type.getDescription()); - bcelType.setSource(type.getSource()); - } - } + typeRepository.add(type); } } } diff --git a/structurizr-component/src/main/java/com/structurizr/component/TypeRepository.java b/structurizr-component/src/main/java/com/structurizr/component/TypeRepository.java index faa707b57..33683165e 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/TypeRepository.java +++ b/structurizr-component/src/main/java/com/structurizr/component/TypeRepository.java @@ -8,7 +8,20 @@ public final class TypeRepository { private final Set types = new LinkedHashSet<>(); public void add(Type type) { - this.types.add(type); + Type t = getType(type.getFullyQualifiedName()); + if (t == null) { + // type isn't yet registered, so add it + types.add(type); + } else { + if (type.getJavaClass() != null) { + // this is the BCEL identified type + t.setJavaClass(type.getJavaClass()); + } else { + // this is the source code identified type + t.setDescription(type.getDescription()); + t.setSource(type.getSource()); + } + } } public Set getTypes() { diff --git a/structurizr-component/src/main/java/com/structurizr/component/url/PrefixSourceUrlStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/url/PrefixSourceUrlStrategy.java index 1fd3047d0..5aa951cd5 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/url/PrefixSourceUrlStrategy.java +++ b/structurizr-component/src/main/java/com/structurizr/component/url/PrefixSourceUrlStrategy.java @@ -24,7 +24,11 @@ public PrefixSourceUrlStrategy(String prefix) { @Override public String urlOf(Type type) { - return prefix + (type.getSource().replaceAll("\\\\", "/")); + if (type.getSource() != null) { + return prefix + (type.getSource().replaceAll("\\\\", "/")); + } else { + return null; + } } @Override diff --git a/structurizr-component/src/test/java/com/structurizr/component/ComponentFinderTests.java b/structurizr-component/src/test/java/com/structurizr/component/ComponentFinderTests.java index 68ff4698c..6b1689f04 100644 --- a/structurizr-component/src/test/java/com/structurizr/component/ComponentFinderTests.java +++ b/structurizr-component/src/test/java/com/structurizr/component/ComponentFinderTests.java @@ -27,8 +27,8 @@ void run() { ComponentFinder componentFinder = new ComponentFinderBuilder() .forContainer(container) - .fromClasses(new File("build/classes/java/test")) .fromSource(new File("src/test/java")) + .fromClasses(new File("build/classes/java/test")) .filteredBy(new IncludeFullyQualifiedNameRegexFilter("com\\.structurizr\\.component\\.example\\..*")) .withStrategy(new ComponentFinderStrategyBuilder() .withTechnology("Web Controller") diff --git a/structurizr-component/src/test/java/com/structurizr/component/TypeRepositoryTests.java b/structurizr-component/src/test/java/com/structurizr/component/TypeRepositoryTests.java new file mode 100644 index 000000000..53cdedc0b --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/TypeRepositoryTests.java @@ -0,0 +1,52 @@ +package com.structurizr.component; + +import org.apache.bcel.classfile.ClassParser; +import org.apache.bcel.classfile.JavaClass; +import org.junit.jupiter.api.Test; + +import java.io.File; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; + +public class TypeRepositoryTests { + + @Test + void add_MergesClassInformation() throws Exception { + String fqn = "com.structurizr.component.TypeRepositoryTests"; + File classes = new File("build/classes/java/test"); + ClassParser parser = new ClassParser(new File(classes, "com/structurizr/component/TypeRepositoryTests.class").getAbsolutePath()); + JavaClass javaClass = parser.parse(); + + Type classType = new Type(javaClass); + Type sourceType = new Type(fqn); + sourceType.setSource("source path"); + + TypeRepository typeRepository = new TypeRepository(); + typeRepository.add(sourceType); // source first + typeRepository.add(classType); + + assertSame(javaClass, typeRepository.getType(fqn).getJavaClass()); + assertEquals("source path", typeRepository.getType(fqn).getSource()); + } + + @Test + void add_MergesSourceInformation() throws Exception { + String fqn = "com.structurizr.component.TypeRepositoryTests"; + File classes = new File("build/classes/java/test"); + ClassParser parser = new ClassParser(new File(classes, "com/structurizr/component/TypeRepositoryTests.class").getAbsolutePath()); + JavaClass javaClass = parser.parse(); + + Type classType = new Type(javaClass); + Type sourceType = new Type(fqn); + sourceType.setSource("source path"); + + TypeRepository typeRepository = new TypeRepository(); + typeRepository.add(classType); // class first + typeRepository.add(sourceType); + + assertSame(javaClass, typeRepository.getType(fqn).getJavaClass()); + assertEquals("source path", typeRepository.getType(fqn).getSource()); + } + +} \ No newline at end of file From 24c5cf6912e251ec731263abcf86fd4d1492b02f Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 19 Sep 2024 09:30:54 +0100 Subject: [PATCH 582/717] Closes #336. --- .../java/com/structurizr/model/Model.java | 26 +++++++++++- .../com/structurizr/model/ModelTests.java | 42 ++++++++++++++++--- .../structurizr/dsl/FindElementParser.java | 3 +- .../dsl/FindRelationshipParser.java | 12 ++++-- .../dsl/FindRelationshipParserTests.java | 24 ++++++++--- 5 files changed, 90 insertions(+), 17 deletions(-) diff --git a/structurizr-core/src/main/java/com/structurizr/model/Model.java b/structurizr-core/src/main/java/com/structurizr/model/Model.java index 6ecb81675..111d124c1 100644 --- a/structurizr-core/src/main/java/com/structurizr/model/Model.java +++ b/structurizr-core/src/main/java/com/structurizr/model/Model.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.structurizr.PropertyHolder; import com.structurizr.WorkspaceValidationException; +import com.structurizr.util.StringUtils; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -937,12 +938,12 @@ private void replicateElementRelationships(StaticStructureElementInstance elemen /** * Gets the element with the specified canonical name. * - * @param canonicalName the canonical name (e.g. /SoftwareSystem/Container) + * @param canonicalName the canonical name * @return the Element with the given canonical name, or null if one doesn't exist * @throws IllegalArgumentException if the canonical name is null or empty */ public Element getElementWithCanonicalName(String canonicalName) { - if (canonicalName == null || canonicalName.trim().length() == 0) { + if (StringUtils.isNullOrEmpty(canonicalName)) { throw new IllegalArgumentException("A canonical name must be specified."); } @@ -955,6 +956,27 @@ public Element getElementWithCanonicalName(String canonicalName) { return null; } + /** + * Gets the relationship with the specified canonical name. + * + * @param canonicalName the canonical name + * @return the Relationship with the given canonical name, or null if one doesn't exist + * @throws IllegalArgumentException if the canonical name is null or empty + */ + public Relationship getRelationshipWithCanonicalName(String canonicalName) { + if (StringUtils.isNullOrEmpty(canonicalName)) { + throw new IllegalArgumentException("A canonical name must be specified."); + } + + for (Relationship relationship : getRelationships()) { + if (relationship.getCanonicalName().equals(canonicalName)) { + return relationship; + } + } + + return null; + } + /** * Sets the ID generator associated with this model. * diff --git a/structurizr-core/src/test/java/com/structurizr/model/ModelTests.java b/structurizr-core/src/test/java/com/structurizr/model/ModelTests.java index 36454db16..3bda19147 100644 --- a/structurizr-core/src/test/java/com/structurizr/model/ModelTests.java +++ b/structurizr-core/src/test/java/com/structurizr/model/ModelTests.java @@ -583,16 +583,48 @@ void getElementWithCanonicalName_ThrowsAnException_WhenAnEmptyCanonicalNameIsSpe @Test void getElementWithCanonicalName_ReturnsNull_WhenAnElementWithTheSpecifiedCanonicalNameDoesNotExist() { - assertNull(model.getElementWithCanonicalName("Software System")); + assertNull(model.getElementWithCanonicalName("SoftwareSystem://A")); } @Test void getElementWithCanonicalName_ReturnsTheElement_WhenAnElementWithTheSpecifiedCanonicalNameExists() { - SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); - Container container = softwareSystem.addContainer("Web Application", "Description", "Technology"); + SoftwareSystem a = model.addSoftwareSystem("A"); + Container b = a.addContainer("B"); - assertSame(softwareSystem, model.getElementWithCanonicalName("SoftwareSystem://Software System")); - assertSame(container, model.getElementWithCanonicalName("Container://Software System.Web Application")); + assertSame(a, model.getElementWithCanonicalName("SoftwareSystem://A")); + assertSame(b, model.getElementWithCanonicalName("Container://A.B")); + } + + @Test + void getRelationshipWithCanonicalName_ThrowsAnException_WhenANullCanonicalNameIsSpecified() { + try { + model.getRelationshipWithCanonicalName(null); + } catch (IllegalArgumentException iae) { + assertEquals("A canonical name must be specified.", iae.getMessage()); + } + } + + @Test + void getRelationshipWithCanonicalName_ThrowsAnException_WhenAnEmptyCanonicalNameIsSpecified() { + try { + model.getRelationshipWithCanonicalName(" "); + } catch (IllegalArgumentException iae) { + assertEquals("A canonical name must be specified.", iae.getMessage()); + } + } + + @Test + void getRelationshipWithCanonicalName_ReturnsNull_WhenARelationshipWithTheSpecifiedCanonicalNameDoesNotExist() { + assertNull(model.getRelationshipWithCanonicalName("Relationship://SoftwareSystem://A -> SoftwareSystem://B (Uses)")); + } + + @Test + void getRelationshipWithCanonicalName_ReturnsTheRelationship_WhenARelationshipWithTheSpecifiedCanonicalNameExists() { + SoftwareSystem a = model.addSoftwareSystem("A"); + SoftwareSystem b = model.addSoftwareSystem("B"); + Relationship r = a.uses(b, "Uses"); + + assertSame(r, model.getRelationshipWithCanonicalName("Relationship://SoftwareSystem://A -> SoftwareSystem://B (Uses)")); } @Test diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/FindElementParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/FindElementParser.java index 74532148e..a6737a585 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/FindElementParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/FindElementParser.java @@ -20,10 +20,9 @@ Element parse(DslContext context, Tokens tokens) { throw new RuntimeException("Expected: " + GRAMMAR); } - String s = tokens.get(IDENTIFIER_INDEX); - Element element; + String s = tokens.get(IDENTIFIER_INDEX); if (s.contains("://")) { element = context.getWorkspace().getModel().getElementWithCanonicalName(s); } else { diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/FindRelationshipParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/FindRelationshipParser.java index 3ee77016b..9643e57b8 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/FindRelationshipParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/FindRelationshipParser.java @@ -1,10 +1,11 @@ package com.structurizr.dsl; +import com.structurizr.model.Element; import com.structurizr.model.Relationship; final class FindRelationshipParser extends AbstractParser { - private static final String GRAMMAR = "!relationship "; + private static final String GRAMMAR = "!relationship "; private final static int IDENTIFIER_INDEX = 1; @@ -19,9 +20,14 @@ Relationship parse(DslContext context, Tokens tokens) { throw new RuntimeException("Expected: " + GRAMMAR); } - String s = tokens.get(IDENTIFIER_INDEX); + Relationship relationship; - Relationship relationship = context.getRelationship(s); + String s = tokens.get(IDENTIFIER_INDEX); + if (s.startsWith("Relationship://")) { + relationship = context.getWorkspace().getModel().getRelationshipWithCanonicalName(s); + } else { + relationship = context.getRelationship(s); + } if (relationship == null) { throw new RuntimeException("A relationship identified by \"" + s + "\" could not be found"); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/FindRelationshipParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/FindRelationshipParserTests.java index e66cb93d3..91d9f6dd2 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/FindRelationshipParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/FindRelationshipParserTests.java @@ -3,6 +3,7 @@ import com.structurizr.model.ModelItem; import com.structurizr.model.Person; import com.structurizr.model.Relationship; +import com.structurizr.model.SoftwareSystem; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; @@ -17,17 +18,17 @@ void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { parser.parse(context(), tokens("!relationship", "name", "tokens")); fail(); } catch (Exception e) { - assertEquals("Too many tokens, expected: !relationship ", e.getMessage()); + assertEquals("Too many tokens, expected: !relationship ", e.getMessage()); } } @Test - void test_parse_ThrowsAnException_WhenTheIdentifierIsNotSpecified() { + void test_parse_ThrowsAnException_WhenTheIdentifierOrCanonicalNameIsNotSpecified() { try { parser.parse(context(), tokens("!relationship")); fail(); } catch (Exception e) { - assertEquals("Expected: !relationship ", e.getMessage()); + assertEquals("Expected: !relationship ", e.getMessage()); } } @@ -41,10 +42,23 @@ void test_parse_ThrowsAnException_WhenTheReferencedRelationshipCannotBeFound() { } } + @Test + void test_parse_FindsARelationshipByCanonicalName() { + SoftwareSystem a = model.addSoftwareSystem("A"); + SoftwareSystem b = model.addSoftwareSystem("B"); + Relationship relationship = a.uses(b, "Description"); + + ModelDslContext context = context(); + + ModelItem modelItem = parser.parse(context, tokens("!relationship", "Relationship://SoftwareSystem://A -> SoftwareSystem://B (Description)")); + assertSame(modelItem, relationship); + } + @Test void test_parse_FindsARelationshipByIdentifier() { - Person user = workspace.getModel().addPerson("User"); - Relationship relationship = user.interactsWith(user, "Description"); + SoftwareSystem a = model.addSoftwareSystem("A"); + SoftwareSystem b = model.addSoftwareSystem("B"); + Relationship relationship = a.uses(b, "Description"); ModelDslContext context = context(); IdentifiersRegister register = new IdentifiersRegister(); From a52e8f93870238ae10bc70a055a946088e98e414 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 19 Sep 2024 10:23:21 +0100 Subject: [PATCH 583/717] structurizr-dsl: An exception is now thrown when trying to use disallowed features in restricted mode (e.g. `!docs`, `!include `, etc). --- changelog.md | 1 + .../structurizr/dsl/StructurizrDslParser.java | 28 +++++++ .../java/com/structurizr/dsl/DslTests.java | 77 +++++++++++++++++-- 3 files changed, 100 insertions(+), 6 deletions(-) diff --git a/changelog.md b/changelog.md index b3c01af79..9052a7c63 100644 --- a/changelog.md +++ b/changelog.md @@ -17,6 +17,7 @@ - structurizr-dsl: Adds a `!relationships` keyword that can be used to find a set of relationships via an expression. - structurizr-dsl: Adds a DSL wrapper around the `structurizr-component` component finder. - structurizr-dsl: Adds support for local theme files to be specified via `theme` (https://github.com/structurizr/java/issues/331). +- structurizr-dsl: An exception is now thrown when trying to use disallowed features in restricted mode (e.g. `!docs`, `!include `, etc). - structurizr-export: Adds support for icons to the Ilograph exporter (https://github.com/structurizr/java/issues/332). - structurizr-export: Adds support for imports to the Ilograph exporter (https://github.com/structurizr/java/issues/332). - structurizr-export: Fixes https://github.com/structurizr/java/issues/337 (Malformed subgraph name in Mermaid render). diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index 80a32c223..1d1d0e759 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -255,6 +255,8 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn parse(paddedLines, includedFile.getFile(), true, true); } + } else { + throwRestrictedModeException(firstToken + " "); } // include the !include in the parser DSL as: # !include ... @@ -268,6 +270,8 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn // run the plugin immediately, without looking for parameters endContext(); } + } else { + throwRestrictedModeException(firstToken); } } else if (inContext(PluginDslContext.class)) { @@ -289,6 +293,8 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn endContext(); } } + } else { + throwRestrictedModeException(firstToken); } } else if (inContext(ExternalScriptDslContext.class)) { @@ -437,6 +443,8 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn if (shouldStartContext(tokens)) { startContext(new ComponentFinderDslContext(this, getContext(ContainerDslContext.class).getContainer())); } + } else { + throwRestrictedModeException(firstToken); } } else if (COMPONENT_FINDER_CLASSES_TOKEN.equalsIgnoreCase(firstToken) && inContext(ComponentFinderDslContext.class)) { @@ -933,41 +941,57 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn } else if (DOCS_TOKEN.equalsIgnoreCase(firstToken) && inContext(WorkspaceDslContext.class)) { if (!restricted) { new DocsParser().parse(getContext(WorkspaceDslContext.class), dslFile, tokens); + } else { + throwRestrictedModeException(firstToken); } } else if (DOCS_TOKEN.equalsIgnoreCase(firstToken) && inContext(SoftwareSystemDslContext.class)) { if (!restricted) { new DocsParser().parse(getContext(SoftwareSystemDslContext.class), dslFile, tokens); + } else { + throwRestrictedModeException(firstToken); } } else if (DOCS_TOKEN.equalsIgnoreCase(firstToken) && inContext(ContainerDslContext.class)) { if (!restricted) { new DocsParser().parse(getContext(ContainerDslContext.class), dslFile, tokens); + } else { + throwRestrictedModeException(firstToken); } } else if (DOCS_TOKEN.equalsIgnoreCase(firstToken) && inContext(ComponentDslContext.class)) { if (!restricted) { new DocsParser().parse(getContext(ComponentDslContext.class), dslFile, tokens); + } else { + throwRestrictedModeException(firstToken); } } else if ((ADRS_TOKEN.equalsIgnoreCase(firstToken) || DECISIONS_TOKEN.equalsIgnoreCase(firstToken)) && inContext(WorkspaceDslContext.class)) { if (!restricted) { new DecisionsParser().parse(getContext(WorkspaceDslContext.class), dslFile, tokens); + } else { + throwRestrictedModeException(firstToken); } } else if ((ADRS_TOKEN.equalsIgnoreCase(firstToken) || DECISIONS_TOKEN.equalsIgnoreCase(firstToken)) && inContext(SoftwareSystemDslContext.class)) { if (!restricted) { new DecisionsParser().parse(getContext(SoftwareSystemDslContext.class), dslFile, tokens); + } else { + throwRestrictedModeException(firstToken); } } else if ((ADRS_TOKEN.equalsIgnoreCase(firstToken) || DECISIONS_TOKEN.equalsIgnoreCase(firstToken)) && inContext(ContainerDslContext.class)) { if (!restricted) { new DecisionsParser().parse(getContext(ContainerDslContext.class), dslFile, tokens); + } else { + throwRestrictedModeException(firstToken); } } else if ((ADRS_TOKEN.equalsIgnoreCase(firstToken) || DECISIONS_TOKEN.equalsIgnoreCase(firstToken)) && inContext(ComponentDslContext.class)) { if (!restricted) { new DecisionsParser().parse(getContext(ComponentDslContext.class), dslFile, tokens); + } else { + throwRestrictedModeException(firstToken); } } else if (CONSTANT_TOKEN.equalsIgnoreCase(firstToken)) { @@ -1068,6 +1092,10 @@ private List preProcessLines(List lines) { return dslLines; } + private void throwRestrictedModeException(String firstToken) { + throw new RuntimeException(firstToken + " is not available when the parser is running in restricted mode"); + } + private String substituteStrings(String token) { Matcher m = STRING_SUBSTITUTION_PATTERN.matcher(token); while (m.find()) { diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index 682753f62..7ff0184aa 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -419,13 +419,18 @@ void test_includeUrl() throws Exception { } @Test - void test_include_WhenRunningInRestrictedMode() throws Exception { - StructurizrDslParser parser = new StructurizrDslParser(); - parser.setRestricted(true); + void test_includeLocalFile_ThrowsAnException_WhenRunningInRestrictedMode() { + try { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.setRestricted(true); - // the model include will be ignored, so no software systems - parser.parse(new File("src/test/resources/dsl/include-file.dsl")); - assertEquals(0, model.getSoftwareSystems().size()); + // the model include will be ignored, so no software systems + parser.parse(new File("src/test/resources/dsl/include-file.dsl")); + fail(); + } catch (Exception e) { + System.out.println(e.getMessage()); + assertTrue(e.getMessage().startsWith("!include is not available when the parser is running in restricted mode")); + } } @ParameterizedTest @@ -663,6 +668,19 @@ void test_hierarchicalIdentifiersAndDeploymentNodes_WhenSoftwareContainerClashes parser.parse(new File("src/test/resources/dsl/hierarchical-identifiers-and-deployment-nodes-3.dsl")); } + @Test + void test_plugin_ThrowsAnException_WhenTheParserIsRunningInRestrictedMode() { + try { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.setRestricted(true); + parser.parse(new File("src/test/resources/dsl/plugin-without-parameters.dsl")); + fail(); + } catch (Exception e) { + System.out.println(e.getMessage()); + assertTrue(e.getMessage().startsWith("!plugin is not available when the parser is running in restricted mode")); + } + } + @Test void test_pluginWithoutParameters() throws Exception { StructurizrDslParser parser = new StructurizrDslParser(); @@ -679,6 +697,18 @@ void test_pluginWithParameters() throws Exception { assertNotNull(parser.getWorkspace().getModel().getPersonWithName("Java")); } + @Test + void test_script_ThrowsAnException_WhenTheParserIsInRestrictedMode() { + try { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.setRestricted(true); + parser.parse(new File("src/test/resources/dsl/script-external.dsl")); + fail(); + } catch (Exception e) { + assertTrue(e.getMessage().startsWith("!script is not available when the parser is running in restricted mode")); + } + } + @Test void test_script() throws Exception { StructurizrDslParser parser = new StructurizrDslParser(); @@ -751,6 +781,18 @@ void test_docs() throws Exception { assertEquals(1, component.getDocumentation().getSections().size()); } + @Test + void test_docs_ThrowsAnException_WhenTheParserIsInRestrictedMode() { + try { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.setRestricted(true); + parser.parse(new File("src/test/resources/dsl/docs/workspace.dsl")); + fail(); + } catch (Exception e) { + assertTrue(e.getMessage().startsWith("!docs is not available when the parser is running in restricted mode")); + } + } + @Test void test_decisions() throws Exception { StructurizrDslParser parser = new StructurizrDslParser(); @@ -771,6 +813,18 @@ void test_decisions() throws Exception { assertEquals(4, component.getDocumentation().getDecisions().size()); } + @Test + void test_decisions_ThrowsAnException_WhenTheParserIsInRestrictedMode() { + try { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.setRestricted(true); + parser.parse(new File("src/test/resources/dsl/decisions/workspace.dsl")); + fail(); + } catch (Exception e) { + assertTrue(e.getMessage().startsWith("!adrs is not available when the parser is running in restricted mode")); + } + } + @Test void test_this() throws Exception { StructurizrDslParser parser = new StructurizrDslParser(); @@ -1192,6 +1246,17 @@ void springPetClinic() throws Exception { if (!StringUtils.isNullOrEmpty(springPetClinicHome)) { System.out.println("Running Spring PetClinic example..."); + try { + File workspaceFile = new File("src/test/resources/dsl/spring-petclinic/workspace.dsl"); + StructurizrDslParser parser = new StructurizrDslParser(); + parser.setRestricted(true); + parser.parse(workspaceFile); + fail(); + } catch (Exception e) { + System.out.println(e.getMessage()); + assertTrue(e.getMessage().startsWith("!components is not available when the parser is running in restricted mode")); + } + File workspaceFile = new File("src/test/resources/dsl/spring-petclinic/workspace.dsl"); StructurizrDslParser parser = new StructurizrDslParser(); parser.parse(workspaceFile); From be69af2a78f0d3d1166b4bcec732db7ff16539ef Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 19 Sep 2024 10:56:05 +0100 Subject: [PATCH 584/717] Update docs. --- changelog.md | 2 +- structurizr-component/README.md | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index 9052a7c63..1192cda71 100644 --- a/changelog.md +++ b/changelog.md @@ -15,7 +15,7 @@ - structurizr-dsl: Adds an `!elements` keyword that can be used to find a set of elements via an expression. - structurizr-dsl: Adds an `!relationship` keyword that can be used to find a single relationship by identifier (replaces `!ref` and `!extend`). - structurizr-dsl: Adds a `!relationships` keyword that can be used to find a set of relationships via an expression. -- structurizr-dsl: Adds a DSL wrapper around the `structurizr-component` component finder. +- structurizr-dsl: Adds a DSL wrapper around the `structurizr-component` component finder (`!components`). - structurizr-dsl: Adds support for local theme files to be specified via `theme` (https://github.com/structurizr/java/issues/331). - structurizr-dsl: An exception is now thrown when trying to use disallowed features in restricted mode (e.g. `!docs`, `!include `, etc). - structurizr-export: Adds support for icons to the Ilograph exporter (https://github.com/structurizr/java/issues/332). diff --git a/structurizr-component/README.md b/structurizr-component/README.md index 0e319527b..b9f556ae5 100644 --- a/structurizr-component/README.md +++ b/structurizr-component/README.md @@ -5,5 +5,9 @@ This library provides a facility to discover components in a Java codebase, via a combination of [Apache Commons BCEL](https://commons.apache.org/proper/commons-bcel/) and [JavaParser](https://javaparser.org), using a pluggable and customisable set of matching and filtering rules. +It is also available via the Structurizr DSL `!component` keyword. -__Unreleased, experimental, and potentially subject to change - see tests for an example.__ \ No newline at end of file +See the following tests for an example: + +- https://github.com/structurizr/java/blob/master/structurizr-component/src/test/java/com/structurizr/component/SpringPetClinicTests.java +- https://github.com/structurizr/java/blob/master/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl \ No newline at end of file From 6697b0882f42d124350814c1e330bcd35f02a3e3 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 19 Sep 2024 11:26:47 +0100 Subject: [PATCH 585/717] Updated to reflect release date. --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 1192cda71..3a510bf1e 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 3.0.0 (unreleased) +## 3.0.0 (19th September 2024) - structurizr-client: Adds support to get/put workspace branches on the [cloud service](https://docs.structurizr.com/cloud/workspace-branches) and [on-premises installation](https://docs.structurizr.com/onpremises/workspace-branches). - structurizr-core: Adds name-value properties to dynamic view relationship views (https://github.com/structurizr/java/issues/316). From 75d3d131bda538b6e666298cecb3f8e8ecc97fe0 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 20 Sep 2024 14:08:24 +0100 Subject: [PATCH 586/717] structurizr-client: Workspace archive file now includes the branch name in the filename. --- build.gradle | 2 +- changelog.md | 4 ++++ .../src/main/java/com/structurizr/api/WorkspaceApiClient.java | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 127bc7d21..bc0b040b4 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '3.0.0' + version = '3.0.1' repositories { mavenCentral() diff --git a/changelog.md b/changelog.md index 3a510bf1e..f3410decc 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 3.0.1 (unreleased) + +- structurizr-client: Workspace archive file now includes the branch name in the filename. + ## 3.0.0 (19th September 2024) - structurizr-client: Adds support to get/put workspace branches on the [cloud service](https://docs.structurizr.com/cloud/workspace-branches) and [on-premises installation](https://docs.structurizr.com/onpremises/workspace-branches). diff --git a/structurizr-client/src/main/java/com/structurizr/api/WorkspaceApiClient.java b/structurizr-client/src/main/java/com/structurizr/api/WorkspaceApiClient.java index 159a8fffd..1ab4a7158 100644 --- a/structurizr-client/src/main/java/com/structurizr/api/WorkspaceApiClient.java +++ b/structurizr-client/src/main/java/com/structurizr/api/WorkspaceApiClient.java @@ -415,7 +415,7 @@ private void debugArchivedWorkspaceLocation(File archiveFile) { private String createArchiveFileName(long workspaceId) { SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss"); - return "structurizr-" + workspaceId + "-" + sdf.format(new Date()) + ".json"; + return "structurizr-" + workspaceId + "-" + (StringUtils.isNullOrEmpty(branch) ? "" : (branch + "-")) + sdf.format(new Date()) + ".json"; } public void setUser(String user) { From 4fe04a6e9a8c3024694c373dbe0069333a694018 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 25 Sep 2024 16:18:31 +0100 Subject: [PATCH 587/717] Typo. --- structurizr-component/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structurizr-component/README.md b/structurizr-component/README.md index b9f556ae5..06bb920af 100644 --- a/structurizr-component/README.md +++ b/structurizr-component/README.md @@ -5,7 +5,7 @@ This library provides a facility to discover components in a Java codebase, via a combination of [Apache Commons BCEL](https://commons.apache.org/proper/commons-bcel/) and [JavaParser](https://javaparser.org), using a pluggable and customisable set of matching and filtering rules. -It is also available via the Structurizr DSL `!component` keyword. +It is also available via the Structurizr DSL `!components` keyword. See the following tests for an example: From e6a78c705c042c86f0aa466725b1014cbf4eff23 Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Thu, 26 Sep 2024 23:17:43 +0100 Subject: [PATCH 588/717] Bump dependencies. --- build.gradle | 2 +- changelog.md | 2 +- structurizr-autolayout/build.gradle | 2 +- structurizr-client/build.gradle | 6 +++--- structurizr-component/build.gradle | 6 +++--- structurizr-core/build.gradle | 8 ++++---- structurizr-dsl/build.gradle | 10 +++++----- structurizr-export/build.gradle | 2 +- structurizr-import/build.gradle | 2 +- structurizr-inspection/build.gradle | 2 +- structurizr-neo4j/build.gradle | 4 ++-- 11 files changed, 23 insertions(+), 23 deletions(-) diff --git a/build.gradle b/build.gradle index bc0b040b4..21ae1b226 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '3.0.1' + version = '3.1.0' repositories { mavenCentral() diff --git a/changelog.md b/changelog.md index f3410decc..568f80a34 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 3.0.1 (unreleased) +## 3.1.0 (unreleased) - structurizr-client: Workspace archive file now includes the branch name in the filename. diff --git a/structurizr-autolayout/build.gradle b/structurizr-autolayout/build.gradle index c40b607ba..19510a149 100644 --- a/structurizr-autolayout/build.gradle +++ b/structurizr-autolayout/build.gradle @@ -3,7 +3,7 @@ dependencies { api project(':structurizr-export') testImplementation project(':structurizr-client') - testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2' + testImplementation 'org.junit.jupiter:junit-jupiter:5.11.0' } diff --git a/structurizr-client/build.gradle b/structurizr-client/build.gradle index f6a2c657e..32f0fb02b 100644 --- a/structurizr-client/build.gradle +++ b/structurizr-client/build.gradle @@ -2,11 +2,11 @@ dependencies { api project(':structurizr-core') - api 'com.fasterxml.jackson.core:jackson-databind:2.16.0' - api 'org.apache.httpcomponents.client5:httpclient5:5.2.1' + api 'com.fasterxml.jackson.core:jackson-databind:2.17.2' + api 'org.apache.httpcomponents.client5:httpclient5:5.4' api 'javax.xml.bind:jaxb-api:2.4.0-b180830.0359' - testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2' + testImplementation 'org.junit.jupiter:junit-jupiter:5.11.0' } diff --git a/structurizr-component/build.gradle b/structurizr-component/build.gradle index 6c49d8797..538c74020 100644 --- a/structurizr-component/build.gradle +++ b/structurizr-component/build.gradle @@ -1,11 +1,11 @@ dependencies { api project(':structurizr-core') - implementation 'org.apache.bcel:bcel:6.8.1' - implementation 'com.github.javaparser:javaparser-symbol-solver-core:3.26.1' + implementation 'org.apache.bcel:bcel:6.10.0' + implementation 'com.github.javaparser:javaparser-symbol-solver-core:3.26.2' testImplementation project(':structurizr-annotation') - testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2' + testImplementation 'org.junit.jupiter:junit-jupiter:5.11.0' } diff --git a/structurizr-core/build.gradle b/structurizr-core/build.gradle index dae5405a4..80ccbf6dd 100644 --- a/structurizr-core/build.gradle +++ b/structurizr-core/build.gradle @@ -1,10 +1,10 @@ dependencies { - api 'com.fasterxml.jackson.core:jackson-annotations:2.16.0' + api 'com.fasterxml.jackson.core:jackson-annotations:2.17.2' api 'com.google.code.findbugs:jsr305:3.0.2' - api 'commons-logging:commons-logging:1.2' + api 'commons-logging:commons-logging:1.3.4' - testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2' - testImplementation 'org.assertj:assertj-core:3.24.2' + testImplementation 'org.junit.jupiter:junit-jupiter:5.11.0' + testImplementation 'org.assertj:assertj-core:3.26.3' } \ No newline at end of file diff --git a/structurizr-dsl/build.gradle b/structurizr-dsl/build.gradle index 2b6eaabbb..1a38536b6 100644 --- a/structurizr-dsl/build.gradle +++ b/structurizr-dsl/build.gradle @@ -4,12 +4,12 @@ dependencies { api project(':structurizr-import') api project(':structurizr-component') - testImplementation 'org.codehaus.groovy:groovy-jsr223:3.0.19' - testImplementation 'org.jetbrains.kotlin:kotlin-scripting-jsr223:1.8.10' - testImplementation 'org.jruby:jruby-core:9.4.4.0' + testImplementation 'org.codehaus.groovy:groovy-jsr223:3.0.22' + testImplementation 'org.jetbrains.kotlin:kotlin-scripting-jsr223:1.9.25' + testImplementation 'org.jruby:jruby-core:9.4.8.0' - testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.9.2' - testImplementation 'org.junit.jupiter:junit-jupiter-params:5.9.2' + testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.11.0' + testImplementation 'org.junit.jupiter:junit-jupiter-params:5.11.0' } description = 'Structurizr DSL' \ No newline at end of file diff --git a/structurizr-export/build.gradle b/structurizr-export/build.gradle index 0729fcb26..67a1a4e33 100644 --- a/structurizr-export/build.gradle +++ b/structurizr-export/build.gradle @@ -3,7 +3,7 @@ dependencies { api project(':structurizr-core') testImplementation project(':structurizr-client') - testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2' + testImplementation 'org.junit.jupiter:junit-jupiter:5.11.0' } diff --git a/structurizr-import/build.gradle b/structurizr-import/build.gradle index bf562441f..a1dd4218c 100644 --- a/structurizr-import/build.gradle +++ b/structurizr-import/build.gradle @@ -2,7 +2,7 @@ dependencies { api project(':structurizr-core') - testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2' + testImplementation 'org.junit.jupiter:junit-jupiter:5.11.0' } diff --git a/structurizr-inspection/build.gradle b/structurizr-inspection/build.gradle index 1dfe8a378..25ca05b8e 100644 --- a/structurizr-inspection/build.gradle +++ b/structurizr-inspection/build.gradle @@ -3,6 +3,6 @@ dependencies { api project(':structurizr-core') testImplementation project(':structurizr-dsl') - testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2' + testImplementation 'org.junit.jupiter:junit-jupiter:5.11.0' } \ No newline at end of file diff --git a/structurizr-neo4j/build.gradle b/structurizr-neo4j/build.gradle index 2c06e792e..a8990a997 100644 --- a/structurizr-neo4j/build.gradle +++ b/structurizr-neo4j/build.gradle @@ -1,9 +1,9 @@ dependencies { api project(':structurizr-core') - implementation 'org.neo4j.driver:neo4j-java-driver:5.23.0' + implementation 'org.neo4j.driver:neo4j-java-driver:5.24.0' testImplementation project(':structurizr-client') - testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2' + testImplementation 'org.junit.jupiter:junit-jupiter:5.11.0' } \ No newline at end of file From d5e0d201e5fc9e1d6e6d4f3ea6d694e2706afbb0 Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Thu, 26 Sep 2024 23:19:17 +0100 Subject: [PATCH 589/717] Adds a couple more supporting types strategies. --- changelog.md | 4 ++ .../java/com/structurizr/component/Type.java | 20 ++++++++ ...tionWithPrefixSupportingTypesStrategy.java | 38 +++++++++++++++ ...tionWithSuffixSupportingTypesStrategy.java | 38 +++++++++++++++ ...ithPrefixSupportingTypesStrategyTests.java | 44 ++++++++++++++++++ ...ithSuffixSupportingTypesStrategyTests.java | 46 +++++++++++++++++++ .../implementation/ExampleRepository.java | 4 ++ .../implementation/ExampleRepositoryImpl.java | 4 ++ .../implementation/JdbcExampleRepository.java | 4 ++ .../dsl/ComponentFinderStrategyParser.java | 22 ++++++++- .../ComponentFinderStrategyParserTests.java | 34 +++++++++++++- 11 files changed, 256 insertions(+), 2 deletions(-) create mode 100644 structurizr-component/src/main/java/com/structurizr/component/supporting/ImplementationWithPrefixSupportingTypesStrategy.java create mode 100644 structurizr-component/src/main/java/com/structurizr/component/supporting/ImplementationWithSuffixSupportingTypesStrategy.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/supporting/ImplementationWithPrefixSupportingTypesStrategyTests.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/supporting/ImplementationWithSuffixSupportingTypesStrategyTests.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/supporting/implementation/ExampleRepository.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/supporting/implementation/ExampleRepositoryImpl.java create mode 100644 structurizr-component/src/test/java/com/structurizr/component/supporting/implementation/JdbcExampleRepository.java diff --git a/changelog.md b/changelog.md index 568f80a34..7db1bca08 100644 --- a/changelog.md +++ b/changelog.md @@ -3,6 +3,10 @@ ## 3.1.0 (unreleased) - structurizr-client: Workspace archive file now includes the branch name in the filename. +- structurizr-component: Adds `ImplementationWithPrefixSupportingTypesStrategy`. +- structurizr-component: Adds `ImplementationWithSuffixSupportingTypesStrategy`. +- structurizr-dsl: Adds `supportingTypes implementation-prefix `. +- structurizr-dsl: Adds `supportingTypes implementation-suffix `. ## 3.0.0 (19th September 2024) diff --git a/structurizr-component/src/main/java/com/structurizr/component/Type.java b/structurizr-component/src/main/java/com/structurizr/component/Type.java index 3e3be19ec..dea55129f 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/Type.java +++ b/structurizr-component/src/main/java/com/structurizr/component/Type.java @@ -2,6 +2,8 @@ import com.structurizr.util.StringUtils; import org.apache.bcel.classfile.*; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import java.util.*; @@ -10,6 +12,8 @@ */ public class Type { + private static final Log log = LogFactory.getLog(Type.class); + private static final String STRUCTURIZR_TAG_ANNOTATION = "Lcom/structurizr/annotation/Tag;"; private static final String STRUCTURIZR_TAGS_ANNOTATION = "Lcom/structurizr/annotation/Tags;"; @@ -92,6 +96,10 @@ public boolean isAbstractClass() { return javaClass.isAbstract() && javaClass.isClass(); } + public boolean isInterface() { + return javaClass.isInterface(); + } + public List getTags() { List tags = new ArrayList<>(); @@ -159,4 +167,16 @@ public String toString() { return this.fullyQualifiedName; } + public boolean implementsInterface(Type type) { + if (javaClass != null) { + try { + return javaClass.implementationOf(type.javaClass); + } catch (ClassNotFoundException e) { + log.warn(e); + } + } + + return false; + } + } \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/supporting/ImplementationWithPrefixSupportingTypesStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/supporting/ImplementationWithPrefixSupportingTypesStrategy.java new file mode 100644 index 000000000..1d0b64fe5 --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/supporting/ImplementationWithPrefixSupportingTypesStrategy.java @@ -0,0 +1,38 @@ +package com.structurizr.component.supporting; + +import com.structurizr.component.Type; +import com.structurizr.component.TypeRepository; + +import java.util.Set; +import java.util.stream.Collectors; + +/** + * A strategy that, given an interface, finds the implementation class with the specified prefix. + */ +public class ImplementationWithPrefixSupportingTypesStrategy implements SupportingTypesStrategy { + + private final String prefix; + + public ImplementationWithPrefixSupportingTypesStrategy(String prefix) { + this.prefix = prefix; + } + + @Override + public Set findSupportingTypes(Type type, TypeRepository typeRepository) { + if (!type.isInterface()) { + throw new IllegalArgumentException("The type " + type.getFullyQualifiedName() + " is not an interface"); + } + + return typeRepository.getTypes().stream() + .filter(dependency -> dependency.implementsInterface(type) && dependency.getName().equals(prefix + type.getName())) + .collect(Collectors.toSet()); + } + + @Override + public String toString() { + return "ImplementationWithPrefixSupportingTypesStrategy{" + + "prefix='" + prefix + '\'' + + '}'; + } + +} \ No newline at end of file diff --git a/structurizr-component/src/main/java/com/structurizr/component/supporting/ImplementationWithSuffixSupportingTypesStrategy.java b/structurizr-component/src/main/java/com/structurizr/component/supporting/ImplementationWithSuffixSupportingTypesStrategy.java new file mode 100644 index 000000000..f14006cea --- /dev/null +++ b/structurizr-component/src/main/java/com/structurizr/component/supporting/ImplementationWithSuffixSupportingTypesStrategy.java @@ -0,0 +1,38 @@ +package com.structurizr.component.supporting; + +import com.structurizr.component.Type; +import com.structurizr.component.TypeRepository; + +import java.util.Set; +import java.util.stream.Collectors; + +/** + * A strategy that, given an interface, finds the implementation class with the specified suffix. + */ +public class ImplementationWithSuffixSupportingTypesStrategy implements SupportingTypesStrategy { + + private final String suffix; + + public ImplementationWithSuffixSupportingTypesStrategy(String suffix) { + this.suffix = suffix; + } + + @Override + public Set findSupportingTypes(Type type, TypeRepository typeRepository) { + if (!type.isInterface()) { + throw new IllegalArgumentException("The type " + type.getFullyQualifiedName() + " is not an interface"); + } + + return typeRepository.getTypes().stream() + .filter(dependency -> dependency.implementsInterface(type) && dependency.getName().equals(type.getName() + suffix)) + .collect(Collectors.toSet()); + } + + @Override + public String toString() { + return "ImplementationWithSuffixSupportingTypesStrategy{" + + "suffix='" + suffix + '\'' + + '}'; + } + +} \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/supporting/ImplementationWithPrefixSupportingTypesStrategyTests.java b/structurizr-component/src/test/java/com/structurizr/component/supporting/ImplementationWithPrefixSupportingTypesStrategyTests.java new file mode 100644 index 000000000..6afccd312 --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/supporting/ImplementationWithPrefixSupportingTypesStrategyTests.java @@ -0,0 +1,44 @@ +package com.structurizr.component.supporting; + +import com.structurizr.component.Type; +import com.structurizr.component.TypeRepository; +import org.apache.bcel.classfile.ClassParser; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.*; + +public class ImplementationWithPrefixSupportingTypesStrategyTests { + + private final File classes = new File("build/classes/java/test"); + + @Test + void findSupportingTypes() throws Exception { + Type interfaceType = new Type(new ClassParser(new File(classes, "com/structurizr/component/supporting/implementation/ExampleRepository.class").getAbsolutePath()).parse()); + Type implementationTypeWithPrefix = new Type(new ClassParser(new File(classes, "com/structurizr/component/supporting/implementation/JdbcExampleRepository.class").getAbsolutePath()).parse()); + Type implementationTypeWithSuffix = new Type(new ClassParser(new File(classes, "com/structurizr/component/supporting/implementation/ExampleRepositoryImpl.class").getAbsolutePath()).parse()); + + TypeRepository typeRepository = new TypeRepository(); + typeRepository.add(interfaceType); + typeRepository.add(implementationTypeWithPrefix); + typeRepository.add(implementationTypeWithSuffix); + + Set supportingTypes = new ImplementationWithPrefixSupportingTypesStrategy("Jdbc").findSupportingTypes(interfaceType, typeRepository); + assertEquals(1, supportingTypes.size()); + assertTrue(supportingTypes.contains(implementationTypeWithPrefix)); + } + + @Test + void findSupportingTypes_ThrowsAnException_WhenTheTypeIsNotAnInterface() throws Exception { + try { + Type type = new Type(new ClassParser(new File(classes, "com/structurizr/component/supporting/implementation/JdbcExampleRepository.class").getAbsolutePath()).parse()); + new ImplementationWithPrefixSupportingTypesStrategy("Impl").findSupportingTypes(type, null); + fail(); + } catch (Exception e) { + assertEquals("The type com.structurizr.component.supporting.implementation.JdbcExampleRepository is not an interface", e.getMessage()); + } + } + +} \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/supporting/ImplementationWithSuffixSupportingTypesStrategyTests.java b/structurizr-component/src/test/java/com/structurizr/component/supporting/ImplementationWithSuffixSupportingTypesStrategyTests.java new file mode 100644 index 000000000..1929432ad --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/supporting/ImplementationWithSuffixSupportingTypesStrategyTests.java @@ -0,0 +1,46 @@ +package com.structurizr.component.supporting; + +import com.structurizr.component.Type; +import com.structurizr.component.TypeRepository; +import org.apache.bcel.classfile.ClassFormatException; +import org.apache.bcel.classfile.ClassParser; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.io.IOException; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.*; + +public class ImplementationWithSuffixSupportingTypesStrategyTests { + + private final File classes = new File("build/classes/java/test"); + + @Test + void findSupportingTypes() throws Exception { + Type interfaceType = new Type(new ClassParser(new File(classes, "com/structurizr/component/supporting/implementation/ExampleRepository.class").getAbsolutePath()).parse()); + Type implementationTypeWithPrefix = new Type(new ClassParser(new File(classes, "com/structurizr/component/supporting/implementation/JdbcExampleRepository.class").getAbsolutePath()).parse()); + Type implementationTypeWithSuffix = new Type(new ClassParser(new File(classes, "com/structurizr/component/supporting/implementation/ExampleRepositoryImpl.class").getAbsolutePath()).parse()); + + TypeRepository typeRepository = new TypeRepository(); + typeRepository.add(interfaceType); + typeRepository.add(implementationTypeWithPrefix); + typeRepository.add(implementationTypeWithSuffix); + + Set supportingTypes = new ImplementationWithSuffixSupportingTypesStrategy("Impl").findSupportingTypes(interfaceType, typeRepository); + assertEquals(1, supportingTypes.size()); + assertTrue(supportingTypes.contains(implementationTypeWithSuffix)); + } + + @Test + void findSupportingTypes_ThrowsAnException_WhenTheTypeIsNotAnInterface() throws Exception { + try { + Type type = new Type(new ClassParser(new File(classes, "com/structurizr/component/supporting/implementation/JdbcExampleRepository.class").getAbsolutePath()).parse()); + new ImplementationWithSuffixSupportingTypesStrategy("Impl").findSupportingTypes(type, null); + fail(); + } catch (Exception e) { + assertEquals("The type com.structurizr.component.supporting.implementation.JdbcExampleRepository is not an interface", e.getMessage()); + } + } + +} \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/supporting/implementation/ExampleRepository.java b/structurizr-component/src/test/java/com/structurizr/component/supporting/implementation/ExampleRepository.java new file mode 100644 index 000000000..6765edae1 --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/supporting/implementation/ExampleRepository.java @@ -0,0 +1,4 @@ +package com.structurizr.component.supporting.implementation; + +public interface ExampleRepository { +} diff --git a/structurizr-component/src/test/java/com/structurizr/component/supporting/implementation/ExampleRepositoryImpl.java b/structurizr-component/src/test/java/com/structurizr/component/supporting/implementation/ExampleRepositoryImpl.java new file mode 100644 index 000000000..14447809b --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/supporting/implementation/ExampleRepositoryImpl.java @@ -0,0 +1,4 @@ +package com.structurizr.component.supporting.implementation; + +public class ExampleRepositoryImpl implements ExampleRepository { +} \ No newline at end of file diff --git a/structurizr-component/src/test/java/com/structurizr/component/supporting/implementation/JdbcExampleRepository.java b/structurizr-component/src/test/java/com/structurizr/component/supporting/implementation/JdbcExampleRepository.java new file mode 100644 index 000000000..3a565ab90 --- /dev/null +++ b/structurizr-component/src/test/java/com/structurizr/component/supporting/implementation/JdbcExampleRepository.java @@ -0,0 +1,4 @@ +package com.structurizr.component.supporting.implementation; + +public class JdbcExampleRepository implements ExampleRepository { +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyParser.java index 340f437e8..9c73a68b9 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyParser.java @@ -39,8 +39,12 @@ final class ComponentFinderStrategyParser extends AbstractParser { private static final String SUPPORTING_TYPES_REFERENCED_IN_PACKAGE = "referenced-in-package"; private static final String SUPPORTING_TYPES_IN_PACKAGE = "in-package"; private static final String SUPPORTING_TYPES_UNDER_PACKAGE = "under-package"; + private static final String SUPPORTING_TYPES_IMPLEMENTATION_WITH_PREFIX = "implementation-prefix"; + private static final String SUPPORTING_TYPES_IMPLEMENTATION_WITH_SUFFIX = "implementation-suffix"; private static final String SUPPORTING_TYPES_NONE = "none"; - private static final String SUPPORTING_TYPES_GRAMMAR = "supportingTypes <" + String.join("|", List.of(SUPPORTING_TYPES_ALL_REFERENCED, SUPPORTING_TYPES_REFERENCED_IN_PACKAGE, SUPPORTING_TYPES_IN_PACKAGE, SUPPORTING_TYPES_UNDER_PACKAGE, SUPPORTING_TYPES_NONE)) + "> [parameters]"; + private static final String SUPPORTING_TYPES_GRAMMAR = "supportingTypes <" + String.join("|", List.of(SUPPORTING_TYPES_ALL_REFERENCED, SUPPORTING_TYPES_REFERENCED_IN_PACKAGE, SUPPORTING_TYPES_IN_PACKAGE, SUPPORTING_TYPES_UNDER_PACKAGE, SUPPORTING_TYPES_IMPLEMENTATION_WITH_PREFIX, SUPPORTING_TYPES_IMPLEMENTATION_WITH_SUFFIX, SUPPORTING_TYPES_NONE)) + "> [parameters]"; + private static final String SUPPORTING_TYPES_IMPLEMENTATION_WITH_PREFIX_GRAMMAR = "supportingTypes implementation-prefix "; + private static final String SUPPORTING_TYPES_IMPLEMENTATION_WITH_SUFFIX_GRAMMAR = "supportingTypes implementation-suffix "; private static final String NAME_TYPE_NAME = "type-name"; private static final String NAME_FQN = "fqn"; @@ -185,6 +189,22 @@ void parseSupportingTypes(ComponentFinderStrategyDslContext context, Tokens toke case SUPPORTING_TYPES_UNDER_PACKAGE: context.getComponentFinderStrategyBuilder().supportedBy(new AllTypesUnderPackageSupportingTypesStrategy()); break; + case SUPPORTING_TYPES_IMPLEMENTATION_WITH_PREFIX: + if (tokens.size() < 3) { + throw new RuntimeException("Too few tokens, expected: " + SUPPORTING_TYPES_IMPLEMENTATION_WITH_PREFIX_GRAMMAR); + } + + String prefix = tokens.get(2); + context.getComponentFinderStrategyBuilder().supportedBy(new ImplementationWithPrefixSupportingTypesStrategy(prefix)); + break; + case SUPPORTING_TYPES_IMPLEMENTATION_WITH_SUFFIX: + if (tokens.size() < 3) { + throw new RuntimeException("Too few tokens, expected: " + SUPPORTING_TYPES_IMPLEMENTATION_WITH_SUFFIX_GRAMMAR); + } + + String suffix = tokens.get(2); + context.getComponentFinderStrategyBuilder().supportedBy(new ImplementationWithSuffixSupportingTypesStrategy(suffix)); + break; case SUPPORTING_TYPES_NONE: context.getComponentFinderStrategyBuilder().supportedBy(new DefaultSupportingTypesStrategy()); break; diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderStrategyParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderStrategyParserTests.java index 6ea5eb258..f953aaaee 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderStrategyParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderStrategyParserTests.java @@ -201,10 +201,42 @@ void test_parseSupportingTypes_ThrowsAnException_WhenNoTypeIsSpecified() { parser.parseSupportingTypes(context, tokens("supportingTypes"), null); fail(); } catch (Exception e) { - assertEquals("Too few tokens, expected: supportingTypes [parameters]", e.getMessage()); + assertEquals("Too few tokens, expected: supportingTypes [parameters]", e.getMessage()); } } + @Test + void test_parseSupportingTypes_ThrowsAnException_WhenImplementationSuffixIsUsedWithoutASuffix() { + try { + parser.parseSupportingTypes(context, tokens("supportingTypes", "implementation-suffix"), null); + fail(); + } catch (Exception e) { + assertEquals("Too few tokens, expected: supportingTypes implementation-suffix ", e.getMessage()); + } + } + + @Test + void test_parseSupportingTypes_ImplementationSuffix() { + parser.parseSupportingTypes(context, tokens("supportingTypes", "implementation-suffix", "Impl"), null); + assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=null, typeFilter=null, supportingTypesStrategy=ImplementationWithSuffixSupportingTypesStrategy{suffix='Impl'}, namingStrategy=null, descriptionStrategy=null, urlStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); + } + + @Test + void test_parseSupportingTypes_ThrowsAnException_WhenImplementationPrefixIsUsedWithoutAPrefix() { + try { + parser.parseSupportingTypes(context, tokens("supportingTypes", "implementation-prefix"), null); + fail(); + } catch (Exception e) { + assertEquals("Too few tokens, expected: supportingTypes implementation-prefix ", e.getMessage()); + } + } + + @Test + void test_parseSupportingTypes_ImplementationPrefix() { + parser.parseSupportingTypes(context, tokens("supportingTypes", "implementation-prefix", "Jdbc"), null); + assertEquals("ComponentFinderStrategyBuilder{technology=null, typeMatcher=null, typeFilter=null, supportingTypesStrategy=ImplementationWithPrefixSupportingTypesStrategy{prefix='Jdbc'}, namingStrategy=null, descriptionStrategy=null, urlStrategy=null, componentVisitor=null}", context.getComponentFinderStrategyBuilder().toString()); + } + @Test void test_parseName_ThrowsAnException_WhenNoTypeIsSpecified() { try { From ff37cc29fd4a9bfd9a79b669bae9406a95e1f750 Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Fri, 27 Sep 2024 15:11:11 +0100 Subject: [PATCH 590/717] See https://github.com/structurizr/java/issues/344 --- .../dsl/ExternalScriptDslContext.java | 8 +-- .../com/structurizr/dsl/ScriptDslContext.java | 67 ++++++++++++------- 2 files changed, 45 insertions(+), 30 deletions(-) diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ExternalScriptDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ExternalScriptDslContext.java index 772e36545..b8e8c4070 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ExternalScriptDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ExternalScriptDslContext.java @@ -1,9 +1,6 @@ package com.structurizr.dsl; import java.io.File; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.util.List; class ExternalScriptDslContext extends ScriptDslContext { @@ -23,10 +20,7 @@ void end() { throw new RuntimeException("Script file " + scriptFile.getCanonicalPath() + " does not exist"); } - String fileExtension = filename.substring(filename.lastIndexOf('.') + 1); - List lines = Files.readAllLines(scriptFile.toPath(), StandardCharsets.UTF_8); - - run(this, fileExtension, lines); + run(this, scriptFile); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("Error running script at " + filename + ", caused by " + e.getClass().getName() + ": " + e.getMessage()); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ScriptDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ScriptDslContext.java index 6e11529f1..6463e8324 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ScriptDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ScriptDslContext.java @@ -3,10 +3,9 @@ import com.structurizr.model.Element; import com.structurizr.model.Relationship; -import javax.script.Bindings; -import javax.script.ScriptEngine; -import javax.script.ScriptEngineManager; +import javax.script.*; import java.io.File; +import java.io.FileReader; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -48,32 +47,54 @@ void run(DslContext context, String extension, List lines) throws Except if (engine != null) { Bindings bindings = engine.createBindings(); - bindings.put(WORKSPACE_VARIABLE_NAME, context.getWorkspace()); - - if (parentContext instanceof ViewDslContext) { - bindings.put(VIEW_VARIABLE_NAME, ((ViewDslContext)parentContext).getView()); - } else if (parentContext instanceof ModelItemDslContext) { - ModelItemDslContext modelItemDslContext = (ModelItemDslContext)parentContext; - if (modelItemDslContext.getModelItem() instanceof Element) { - bindings.put(ELEMENT_VARIABLE_NAME, modelItemDslContext.getModelItem()); - } else if (modelItemDslContext.getModelItem() instanceof Relationship) { - bindings.put(RELATIONSHIP_VARIABLE_NAME, modelItemDslContext.getModelItem()); - } - } + populateBindings(bindings, context); - // bind a context object - StructurizrDslScriptContext scriptContext = new StructurizrDslScriptContext(dslParser, dslFile, getWorkspace(), parameters); - bindings.put(CONTEXT_VARIABLE_NAME, scriptContext); + engine.eval(script.toString(), bindings); + } else { + throw new RuntimeException("Could not load a scripting engine for extension \"" + extension + "\""); + } + } - // and any custom parameters - for (String name : parameters.keySet()) { - bindings.put(name, parameters.get(name)); - } + void run(DslContext context, File scriptFile) throws Exception { + String extension = scriptFile.getName().substring(scriptFile.getName().lastIndexOf('.') + 1); + ScriptEngineManager manager = new ScriptEngineManager(); + ScriptEngine engine = manager.getEngineByExtension(extension); - engine.eval(script.toString(), bindings); + if (engine != null) { + Bindings bindings = engine.createBindings(); + populateBindings(bindings, context); + + ScriptContext scriptContext = new SimpleScriptContext(); + scriptContext.setBindings(bindings, ScriptContext.ENGINE_SCOPE); + scriptContext.setAttribute(ScriptEngine.FILENAME, scriptFile.getAbsolutePath(), ScriptContext.ENGINE_SCOPE); + engine.eval(new FileReader(scriptFile), scriptContext); } else { throw new RuntimeException("Could not load a scripting engine for extension \"" + extension + "\""); } } + private void populateBindings(Bindings bindings, DslContext context) { + bindings.put(WORKSPACE_VARIABLE_NAME, context.getWorkspace()); + + if (parentContext instanceof ViewDslContext) { + bindings.put(VIEW_VARIABLE_NAME, ((ViewDslContext)parentContext).getView()); + } else if (parentContext instanceof ModelItemDslContext) { + ModelItemDslContext modelItemDslContext = (ModelItemDslContext)parentContext; + if (modelItemDslContext.getModelItem() instanceof Element) { + bindings.put(ELEMENT_VARIABLE_NAME, modelItemDslContext.getModelItem()); + } else if (modelItemDslContext.getModelItem() instanceof Relationship) { + bindings.put(RELATIONSHIP_VARIABLE_NAME, modelItemDslContext.getModelItem()); + } + } + + // bind a context object + StructurizrDslScriptContext scriptContext = new StructurizrDslScriptContext(dslParser, dslFile, getWorkspace(), parameters); + bindings.put(CONTEXT_VARIABLE_NAME, scriptContext); + + // and any custom parameters + for (String name : parameters.keySet()) { + bindings.put(name, parameters.get(name)); + } + } + } \ No newline at end of file From ba5779566e9a8e4b0baf584ee39c4fd2261aa309 Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Sat, 5 Oct 2024 12:35:57 +0100 Subject: [PATCH 591/717] Fixes #346. --- changelog.md | 1 + .../src/main/java/com/structurizr/dsl/StructurizrDslParser.java | 2 +- structurizr-dsl/src/test/resources/dsl/test.dsl | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 7db1bca08..b4d3c9760 100644 --- a/changelog.md +++ b/changelog.md @@ -7,6 +7,7 @@ - structurizr-component: Adds `ImplementationWithSuffixSupportingTypesStrategy`. - structurizr-dsl: Adds `supportingTypes implementation-prefix `. - structurizr-dsl: Adds `supportingTypes implementation-suffix `. +- structurizr-dsl: Fixes https://github.com/structurizr/java/issues/346 (`// comment \` joins lines). ## 3.0.0 (19th September 2024) diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index 1d1d0e759..a09b0cd13 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -1069,7 +1069,7 @@ private List preProcessLines(List lines) { boolean lineComplete = true; for (String line : lines) { - if (line.endsWith(MULTI_LINE_SEPARATOR)) { + if (!COMMENT_PATTERN.matcher(line).matches() && line.endsWith(MULTI_LINE_SEPARATOR)) { buf.append(line, 0, line.length()-1); lineComplete = false; } else { diff --git a/structurizr-dsl/src/test/resources/dsl/test.dsl b/structurizr-dsl/src/test/resources/dsl/test.dsl index 994ea2d39..7deeaf866 100644 --- a/structurizr-dsl/src/test/resources/dsl/test.dsl +++ b/structurizr-dsl/src/test/resources/dsl/test.dsl @@ -27,6 +27,7 @@ workspace "Name" "Description" { !impliedRelationships "com.structurizr.model.CreateImpliedRelationshipsUnlessSameRelationshipExistsStrategy" !impliedRelationships true + // single line comment with long line split character \ properties { "Name" "Value" } From d57e6aeefe806212a795e8b5cba3c58e03fa5604 Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Sat, 5 Oct 2024 12:47:35 +0100 Subject: [PATCH 592/717] Anonymous identifiers for relationships (i.e. relationships not assigned to an identifier) are excluded from the model, and therefore also excluded from the serialised JSON. --- .../com/structurizr/dsl/StructurizrDslParser.java | 5 ++++- .../test/java/com/structurizr/dsl/DslTests.java | 14 ++++++++++++++ .../dsl/relationship-without-identifier.dsl | 9 +++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 structurizr-dsl/src/test/resources/dsl/relationship-without-identifier.dsl diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index a09b0cd13..dc11d5217 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -1181,7 +1181,10 @@ void registerIdentifier(String identifier, Element element) { void registerIdentifier(String identifier, Relationship relationship) { identifiersRegister.register(identifier, relationship); - relationship.addProperty(STRUCTURIZR_DSL_IDENTIFIER_PROPERTY_NAME, identifiersRegister.findIdentifier(relationship)); + + if (!StringUtils.isNullOrEmpty(identifier)) { + relationship.addProperty(STRUCTURIZR_DSL_IDENTIFIER_PROPERTY_NAME, identifiersRegister.findIdentifier(relationship)); + } } /** diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index 7ff0184aa..41c50e880 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -1007,6 +1007,20 @@ void test_identifiers() throws Exception { assertNull(impliedRelationship.getProperties().get("structurizr.dsl.identifier")); } + @Test + void test_relationshipWithoutIdentifier() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/relationship-without-identifier.dsl")); + + Workspace workspace = parser.getWorkspace(); + IdentifiersRegister register = parser.getIdentifiersRegister(); + assertEquals(1, workspace.getModel().getRelationships().size()); + Relationship relationship = workspace.getModel().getRelationships().iterator().next(); + + assertTrue(register.findIdentifier(relationship).matches("[\\w]{8}-[\\w]{4}-[\\w]{4}-[\\w]{4}-[\\w]{12}")); + assertNull(relationship.getProperties().get("structurizr.dsl.identifier")); // identifier is not included in model + } + @Test void test_imageViews_ViaFiles() throws Exception { StructurizrDslParser parser = new StructurizrDslParser(); diff --git a/structurizr-dsl/src/test/resources/dsl/relationship-without-identifier.dsl b/structurizr-dsl/src/test/resources/dsl/relationship-without-identifier.dsl new file mode 100644 index 000000000..b07a91125 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/relationship-without-identifier.dsl @@ -0,0 +1,9 @@ +workspace { + + model { + a = softwareSystem "A" + b = softwareSystem "B" + a -> b + } + +} \ No newline at end of file From 3d223174e988197a5296639fda827c9fc3b58ac0 Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Sat, 5 Oct 2024 12:48:21 +0100 Subject: [PATCH 593/717] . --- changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.md b/changelog.md index b4d3c9760..8925cb7cb 100644 --- a/changelog.md +++ b/changelog.md @@ -8,6 +8,7 @@ - structurizr-dsl: Adds `supportingTypes implementation-prefix `. - structurizr-dsl: Adds `supportingTypes implementation-suffix `. - structurizr-dsl: Fixes https://github.com/structurizr/java/issues/346 (`// comment \` joins lines). +- structurizr-dsl: Anonymous identifiers for relationships (i.e. relationships not assigned to an identifier) are excluded from the model, and therefore also excluded from the serialised JSON. ## 3.0.0 (19th September 2024) From f103a7a876905b04712dbbd9779aa28568b0dbb6 Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Sun, 6 Oct 2024 12:59:19 +0100 Subject: [PATCH 594/717] Removes duplicate PlantUML/Mermaid encoders, adds support for Mermaid compression, makes SVG the default formats. --- .../java/com/structurizr/dsl/DslTests.java | 8 +- .../dsl/ImageViewContentParserTests.java | 8 +- .../dsl/image-views/workspace-via-file.dsl | 1 + .../dsl/image-views/workspace-via-url.dsl | 1 + .../export/mermaid/MermaidEncoder.java | 18 ----- .../export/plantuml/PlantUMLEncoder.java | 73 ------------------- .../diagrams/mermaid/MermaidEncoder.java | 32 +++++++- .../diagrams/mermaid/MermaidImporter.java | 14 +++- .../diagrams/plantuml/PlantUMLEncoder.java | 4 +- .../diagrams/plantuml/PlantUMLImporter.java | 6 +- .../diagrams/mermaid/MermaidEncoderTests.java | 13 +++- .../mermaid/MermaidImporterTests.java | 21 +++--- .../plantuml/PlantUMLImporterTests.java | 24 +++--- 13 files changed, 92 insertions(+), 131 deletions(-) delete mode 100644 structurizr-export/src/main/java/com/structurizr/export/mermaid/MermaidEncoder.java delete mode 100644 structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLEncoder.java diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index 41c50e880..62125ba96 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -1031,13 +1031,13 @@ void test_imageViews_ViaFiles() throws Exception { ImageView plantumlView = (ImageView)workspace.getViews().getViewWithKey("plantuml"); assertEquals("diagram.puml", plantumlView.getTitle()); - assertEquals("http://localhost:7777/png/SoWkIImgAStDuNBAJrBGjLDmpCbCJbMmKiX8pSd9vt98pKi1IW80", plantumlView.getContent()); - assertEquals("image/png", plantumlView.getContentType()); + assertEquals("http://localhost:7777/svg/SoWkIImgAStDuNBAJrBGjLDmpCbCJbMmKiX8pSd9vt98pKi1IW80", plantumlView.getContent()); + assertEquals("image/svg+xml", plantumlView.getContentType()); ImageView mermaidView = (ImageView)workspace.getViews().getViewWithKey("mermaid"); assertEquals("diagram.mmd", mermaidView.getTitle()); - assertEquals("http://localhost:8888/img/Zmxvd2NoYXJ0IFRECiAgICBTdGFydCAtLT4gU3RvcA==?type=png", mermaidView.getContent()); - assertEquals("image/png", mermaidView.getContentType()); + assertEquals("http://localhost:8888/svg/Zmxvd2NoYXJ0IFRECiAgICBTdGFydCAtLT4gU3RvcA==", mermaidView.getContent()); + assertEquals("image/svg+xml", mermaidView.getContentType()); ImageView krokiView = (ImageView)workspace.getViews().getViewWithKey("kroki"); assertEquals("diagram.dot", krokiView.getTitle()); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewContentParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewContentParserTests.java index 4e693973e..fe2eef5eb 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewContentParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewContentParserTests.java @@ -20,8 +20,10 @@ void setUp() { @Test void test_parsePlantUML_ThrowsAnException_WhenUsingAFileNameInRestrictedMode() { try { + ImageViewDslContext context = new ImageViewDslContext(imageView); + context.setWorkspace(workspace); parser = new ImageViewContentParser(true); - parser.parsePlantUML(new ImageViewDslContext(imageView), null, tokens("plantuml", "image.puml")); + parser.parsePlantUML(context, null, tokens("plantuml", "image.puml")); fail(); } catch (Exception e) { assertEquals("PlantUML source must be specified as a URL when running in restricted mode", e.getMessage()); @@ -31,8 +33,10 @@ void test_parsePlantUML_ThrowsAnException_WhenUsingAFileNameInRestrictedMode() { @Test void test_parseMermaid_ThrowsAnException_WhenUsingAFileNameInRestrictedMode() { try { + ImageViewDslContext context = new ImageViewDslContext(imageView); + context.setWorkspace(workspace); parser = new ImageViewContentParser(true); - parser.parseMermaid(new ImageViewDslContext(imageView), null, tokens("mermaid", "image.puml")); + parser.parseMermaid(context, null, tokens("mermaid", "image.puml")); fail(); } catch (Exception e) { assertEquals("Mermaid source must be specified as a URL when running in restricted mode", e.getMessage()); diff --git a/structurizr-dsl/src/test/resources/dsl/image-views/workspace-via-file.dsl b/structurizr-dsl/src/test/resources/dsl/image-views/workspace-via-file.dsl index 93ec34d37..3cfeed7e9 100644 --- a/structurizr-dsl/src/test/resources/dsl/image-views/workspace-via-file.dsl +++ b/structurizr-dsl/src/test/resources/dsl/image-views/workspace-via-file.dsl @@ -4,6 +4,7 @@ workspace { properties { "plantuml.url" "http://localhost:7777" "mermaid.url" "http://localhost:8888" + "mermaid.compress" "false" "kroki.url" "http://localhost:9999" } diff --git a/structurizr-dsl/src/test/resources/dsl/image-views/workspace-via-url.dsl b/structurizr-dsl/src/test/resources/dsl/image-views/workspace-via-url.dsl index 99fe3859b..f42f657ac 100644 --- a/structurizr-dsl/src/test/resources/dsl/image-views/workspace-via-url.dsl +++ b/structurizr-dsl/src/test/resources/dsl/image-views/workspace-via-url.dsl @@ -6,6 +6,7 @@ workspace { "plantuml.format" "svg" "mermaid.url" "http://localhost:8888" "mermaid.format" "svg" + "mermaid.compress" "false" "kroki.url" "http://localhost:9999" "kroki.format" "svg" } diff --git a/structurizr-export/src/main/java/com/structurizr/export/mermaid/MermaidEncoder.java b/structurizr-export/src/main/java/com/structurizr/export/mermaid/MermaidEncoder.java deleted file mode 100644 index 3f1e9fe9d..000000000 --- a/structurizr-export/src/main/java/com/structurizr/export/mermaid/MermaidEncoder.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.structurizr.export.mermaid; - -import java.nio.charset.StandardCharsets; -import java.util.Base64; - -/** - * Encodes a Mermaid diagram definition to base64 format, for use with image URLs, etc. - */ -public class MermaidEncoder { - - private static final String TEMPLATE = "{ \"code\":\"%s\", \"mermaid\":{\"theme\":\"default\", \"securityLevel\": \"loose\"}}"; - - public String encode(String mermaidDefinition) { - String s = String.format(TEMPLATE, mermaidDefinition.replaceAll("\n", "\\\\n").replaceAll("\"", "\\\\\"")); - return Base64.getEncoder().encodeToString(s.getBytes(StandardCharsets.UTF_8)); - } - -} \ No newline at end of file diff --git a/structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLEncoder.java b/structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLEncoder.java deleted file mode 100644 index 1b5d24da7..000000000 --- a/structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLEncoder.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.structurizr.export.plantuml; - -import java.io.ByteArrayOutputStream; -import java.nio.charset.StandardCharsets; -import java.util.zip.Deflater; -import java.util.zip.DeflaterOutputStream; - -/** - * A Java implementation of http://plantuml.com/code-javascript-synchronous - * that uses Java's built-in Deflate algorithm. - */ -public class PlantUMLEncoder { - - public String encode(String plantUMLDefinition) throws Exception { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION, true); - - DeflaterOutputStream dos = new DeflaterOutputStream(baos, deflater, true); - dos.write(plantUMLDefinition.getBytes(StandardCharsets.UTF_8)); - dos.finish(); - - return encode(baos.toByteArray()); - } - - private String encode(byte[] bytes) { - StringBuilder buf = new StringBuilder(); - for (int i = 0; i < bytes.length; i += 3) { - int b1 = (bytes[i]) & 0xFF; - int b2 = (i + 1 < bytes.length ? bytes[i + 1] : (byte)0) & 0xFF; - int b3 = (i + 2 < bytes.length ? bytes[i + 2] : (byte)0) & 0xFF; - - append3bytes(buf, b1, b2, b3); - } - - return buf.toString(); - } - - private char encode6bit(byte b) { - if (b < 10) { - return (char) ('0' + b); - } - b -= 10; - if (b < 26) { - return (char) ('A' + b); - } - b -= 26; - if (b < 26) { - return (char) ('a' + b); - } - b -= 26; - if (b == 0) { - return '-'; - } - if (b == 1) { - return '_'; - } - - return '?'; - } - - private void append3bytes(StringBuilder buf, int b1, int b2, int b3) { - int c1 = b1 >> 2; - int c2 = (b1 & 0x3) << 4 | b2 >> 4; - int c3 = (b2 & 0xF) << 2 | b3 >> 6; - int c4 = b3 & 0x3F; - - buf.append(encode6bit((byte)(c1 & 0x3F))); - buf.append(encode6bit((byte)(c2 & 0x3F))); - buf.append(encode6bit((byte)(c3 & 0x3F))); - buf.append(encode6bit((byte)(c4 & 0x3F))); - } - -} \ No newline at end of file diff --git a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/mermaid/MermaidEncoder.java b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/mermaid/MermaidEncoder.java index 8bbc4bc91..553d33d78 100644 --- a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/mermaid/MermaidEncoder.java +++ b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/mermaid/MermaidEncoder.java @@ -1,15 +1,45 @@ package com.structurizr.importer.diagrams.mermaid; +import java.io.ByteArrayOutputStream; import java.nio.charset.StandardCharsets; import java.util.Base64; +import java.util.zip.Deflater; +import java.util.zip.DeflaterOutputStream; /** * Encodes a Mermaid diagram definition to base64 format, for use with image URLs, etc. */ -public class MermaidEncoder { +public final class MermaidEncoder { + + private static final String TEMPLATE = "{ \"code\":\"%s\", \"mermaid\":{\"theme\":\"default\"}}"; public String encode(String mermaidDefinition) { + return this.encode(mermaidDefinition, false); + } + + public String encode(String mermaidDefinition, boolean compress) { + if (compress) { + try { + String content = String.format(TEMPLATE, mermaidDefinition.replaceAll("\n", "\\\\n").replaceAll("\"", "\\\\\"")); + byte[] compressedDefinition = compress(content); + return "pako:" + Base64.getUrlEncoder().encodeToString(compressedDefinition); + } catch(Exception e) { + e.printStackTrace(); + } + } + return Base64.getUrlEncoder().encodeToString(mermaidDefinition.getBytes(StandardCharsets.UTF_8)); } + private byte[] compress(String content) throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + Deflater deflater = new Deflater(); + + DeflaterOutputStream dos = new DeflaterOutputStream(baos, deflater, true); + dos.write(content.getBytes(StandardCharsets.UTF_8)); + dos.finish(); + + return baos.toByteArray(); + } + } \ No newline at end of file diff --git a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/mermaid/MermaidImporter.java b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/mermaid/MermaidImporter.java index 9168fff71..c7fa23b44 100644 --- a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/mermaid/MermaidImporter.java +++ b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/mermaid/MermaidImporter.java @@ -10,8 +10,9 @@ public class MermaidImporter extends AbstractDiagramImporter { - private static final String MERMAID_URL_PROPERTY = "mermaid.url"; - private static final String MERMAID_FORMAT_PROPERTY = "mermaid.format"; + public static final String MERMAID_URL_PROPERTY = "mermaid.url"; + public static final String MERMAID_FORMAT_PROPERTY = "mermaid.format"; + public static final String MERMAID_COMPRESS_PROPERTY = "mermaid.compress"; public void importDiagram(ImageView view, File file) throws Exception { String content = new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8); @@ -28,14 +29,19 @@ public void importDiagram(ImageView view, String content) { String format = getViewOrViewSetProperty(view, MERMAID_FORMAT_PROPERTY); if (StringUtils.isNullOrEmpty(format)) { - format = PNG_FORMAT; + format = SVG_FORMAT; } if (!format.equals(PNG_FORMAT) && !format.equals(SVG_FORMAT)) { throw new IllegalArgumentException(String.format("Expected a format of %s or %s", PNG_FORMAT, SVG_FORMAT)); } - String encodedMermaid = new MermaidEncoder().encode(content); + String compress = getViewOrViewSetProperty(view, MERMAID_COMPRESS_PROPERTY); + if (StringUtils.isNullOrEmpty(compress)) { + compress = "true"; + } + + String encodedMermaid = new MermaidEncoder().encode(content, compress.equalsIgnoreCase("true")); String url; if (format.equals(PNG_FORMAT)) { url = String.format("%s/img/%s?type=png", mermaidServer, encodedMermaid); diff --git a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/plantuml/PlantUMLEncoder.java b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/plantuml/PlantUMLEncoder.java index 20238926b..96088b150 100644 --- a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/plantuml/PlantUMLEncoder.java +++ b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/plantuml/PlantUMLEncoder.java @@ -9,9 +9,9 @@ * A Java implementation of http://plantuml.com/code-javascript-synchronous * that uses Java's built-in Deflate algorithm. */ -class PlantUMLEncoder { +public final class PlantUMLEncoder { - String encode(String plantUMLDefinition) throws Exception { + public String encode(String plantUMLDefinition) throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION, true); diff --git a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporter.java b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporter.java index 56ed5a835..3ed7651e0 100644 --- a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporter.java +++ b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporter.java @@ -10,8 +10,8 @@ public class PlantUMLImporter extends AbstractDiagramImporter { - private static final String PLANTUML_URL_PROPERTY = "plantuml.url"; - private static final String PLANTUML_FORMAT_PROPERTY = "plantuml.format"; + public static final String PLANTUML_URL_PROPERTY = "plantuml.url"; + public static final String PLANTUML_FORMAT_PROPERTY = "plantuml.format"; private static final String TITLE_STRING = "title "; private static final String NEWLINE = "\n"; @@ -30,7 +30,7 @@ public void importDiagram(ImageView view, String content) throws Exception { String format = getViewOrViewSetProperty(view, PLANTUML_FORMAT_PROPERTY); if (StringUtils.isNullOrEmpty(format)) { - format = PNG_FORMAT; + format = SVG_FORMAT; } if (!format.equals(PNG_FORMAT) && !format.equals(SVG_FORMAT)) { diff --git a/structurizr-import/src/test/java/com/structurizr/importer/diagrams/mermaid/MermaidEncoderTests.java b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/mermaid/MermaidEncoderTests.java index b249e45ff..b4b69190b 100644 --- a/structurizr-import/src/test/java/com/structurizr/importer/diagrams/mermaid/MermaidEncoderTests.java +++ b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/mermaid/MermaidEncoderTests.java @@ -3,7 +3,6 @@ import org.junit.jupiter.api.Test; import java.io.File; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -14,7 +13,16 @@ public class MermaidEncoderTests { public void encode_flowchart() throws Exception { File file = new File("./src/test/resources/diagrams/mermaid/flowchart.mmd"); String mermaid = Files.readString(file.toPath()); - assertEquals("Zmxvd2NoYXJ0IFRECiAgICBBW0NocmlzdG1hc10gLS0-fEdldCBtb25leXwgQihHbyBzaG9wcGluZykKICAgIEIgLS0-IEN7TGV0IG1lIHRoaW5rfQogICAgQyAtLT58T25lfCBEW0xhcHRvcF0KICAgIEMgLS0-fFR3b3wgRVtpUGhvbmVdCiAgICBDIC0tPnxUaHJlZXwgRltmYTpmYS1jYXIgQ2FyXQ==", new MermaidEncoder().encode(mermaid)); + String encodedMermaid = new MermaidEncoder().encode(mermaid); + assertEquals("Zmxvd2NoYXJ0IFRECiAgICBBW0NocmlzdG1hc10gLS0-fEdldCBtb25leXwgQihHbyBzaG9wcGluZykKICAgIEIgLS0-IEN7TGV0IG1lIHRoaW5rfQogICAgQyAtLT58T25lfCBEW0xhcHRvcF0KICAgIEMgLS0-fFR3b3wgRVtpUGhvbmVdCiAgICBDIC0tPnxUaHJlZXwgRltmYTpmYS1jYXIgQ2FyXQ==", encodedMermaid); + } + + @Test + public void encode_flowchart_compressed() throws Exception { + File file = new File("./src/test/resources/diagrams/mermaid/flowchart.mmd"); + String mermaid = Files.readString(file.toPath()); + String encodedMermaid = new MermaidEncoder().encode(mermaid, true); + assertEquals("pako:eJxVj70OgjAUhV_lppMm8gIMJlKUhUQHtspwAxfbSH9SaoihvLsgi571-85JzgSssS2xlHW9HRuJPkCV3w0sOQkuvRqCxqGGJDnGggJoa-gdIdsVFgZpnVPmsd_8bJWAT-WqEQSpzHPeEP_2r4Yi5KJEF6yrf0k12ghnoW5ymf8n0tPSuogO0w6TBj1w9DU7ANPkNaqWpRMLkvR6oqUOX31g8_wBLY9E1w==", encodedMermaid); } @Test @@ -22,7 +30,6 @@ public void encode_class() throws Exception { File file = new File("./src/test/resources/diagrams/mermaid/class.mmd"); String mermaid = Files.readString(file.toPath()); assertEquals("Y2xhc3NEaWFncmFtCiAgICBBbmltYWwgPHwtLSBEdWNrCiAgICBBbmltYWwgPHwtLSBGaXNoCiAgICBBbmltYWwgPHwtLSBaZWJyYQogICAgQW5pbWFsIDogK2ludCBhZ2UKICAgIEFuaW1hbCA6ICtTdHJpbmcgZ2VuZGVyCiAgICBBbmltYWw6ICtpc01hbW1hbCgpCiAgICBBbmltYWw6ICttYXRlKCkKICAgIGNsYXNzIER1Y2t7CiAgICAgICtTdHJpbmcgYmVha0NvbG9yCiAgICAgICtzd2ltKCkKICAgICAgK3F1YWNrKCkKICAgIH0KICAgIGNsYXNzIEZpc2h7CiAgICAgIC1pbnQgc2l6ZUluRmVldAogICAgICAtY2FuRWF0KCkKICAgIH0KICAgIGNsYXNzIFplYnJhewogICAgICArYm9vbCBpc193aWxkCiAgICAgICtydW4oKQogICAgfQo=", new MermaidEncoder().encode(mermaid)); - } } \ No newline at end of file diff --git a/structurizr-import/src/test/java/com/structurizr/importer/diagrams/mermaid/MermaidImporterTests.java b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/mermaid/MermaidImporterTests.java index 3cccf9ac8..5293f6c8f 100644 --- a/structurizr-import/src/test/java/com/structurizr/importer/diagrams/mermaid/MermaidImporterTests.java +++ b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/mermaid/MermaidImporterTests.java @@ -13,7 +13,8 @@ public class MermaidImporterTests { @Test public void importDiagram() throws Exception { Workspace workspace = new Workspace("Name", "Description"); - workspace.getViews().getConfiguration().addProperty("mermaid.url", "https://mermaid.ink"); + workspace.getViews().getConfiguration().addProperty(MermaidImporter.MERMAID_URL_PROPERTY, "https://mermaid.ink"); + workspace.getViews().getConfiguration().addProperty(MermaidImporter.MERMAID_COMPRESS_PROPERTY, "false"); ImageView view = workspace.getViews().createImageView("key"); new MermaidImporter().importDiagram(view, new File("./src/test/resources/diagrams/mermaid/flowchart.mmd")); @@ -21,15 +22,16 @@ public void importDiagram() throws Exception { assertNull(view.getElement()); assertNull(view.getElementId()); assertEquals("flowchart.mmd", view.getTitle()); - assertEquals("https://mermaid.ink/img/Zmxvd2NoYXJ0IFRECiAgICBBW0NocmlzdG1hc10gLS0-fEdldCBtb25leXwgQihHbyBzaG9wcGluZykKICAgIEIgLS0-IEN7TGV0IG1lIHRoaW5rfQogICAgQyAtLT58T25lfCBEW0xhcHRvcF0KICAgIEMgLS0-fFR3b3wgRVtpUGhvbmVdCiAgICBDIC0tPnxUaHJlZXwgRltmYTpmYS1jYXIgQ2FyXQ==?type=png", view.getContent()); - assertEquals("image/png", view.getContentType()); + assertEquals("https://mermaid.ink/svg/Zmxvd2NoYXJ0IFRECiAgICBBW0NocmlzdG1hc10gLS0-fEdldCBtb25leXwgQihHbyBzaG9wcGluZykKICAgIEIgLS0-IEN7TGV0IG1lIHRoaW5rfQogICAgQyAtLT58T25lfCBEW0xhcHRvcF0KICAgIEMgLS0-fFR3b3wgRVtpUGhvbmVdCiAgICBDIC0tPnxUaHJlZXwgRltmYTpmYS1jYXIgQ2FyXQ==", view.getContent()); + assertEquals("image/svg+xml", view.getContentType()); } @Test public void importDiagram_AsPNG() throws Exception { Workspace workspace = new Workspace("Name", "Description"); - workspace.getViews().getConfiguration().addProperty("mermaid.url", "https://mermaid.ink"); - workspace.getViews().getConfiguration().addProperty("mermaid.format", "png"); + workspace.getViews().getConfiguration().addProperty(MermaidImporter.MERMAID_URL_PROPERTY, "https://mermaid.ink"); + workspace.getViews().getConfiguration().addProperty(MermaidImporter.MERMAID_FORMAT_PROPERTY, "png"); + workspace.getViews().getConfiguration().addProperty(MermaidImporter.MERMAID_COMPRESS_PROPERTY, "false"); ImageView view = workspace.getViews().createImageView("key"); new MermaidImporter().importDiagram(view, new File("./src/test/resources/diagrams/mermaid/flowchart.mmd")); @@ -44,8 +46,9 @@ public void importDiagram_AsPNG() throws Exception { @Test public void importDiagram_AsSVG() throws Exception { Workspace workspace = new Workspace("Name", "Description"); - workspace.getViews().getConfiguration().addProperty("mermaid.url", "https://mermaid.ink"); - workspace.getViews().getConfiguration().addProperty("mermaid.format", "svg"); + workspace.getViews().getConfiguration().addProperty(MermaidImporter.MERMAID_URL_PROPERTY, "https://mermaid.ink"); + workspace.getViews().getConfiguration().addProperty(MermaidImporter.MERMAID_FORMAT_PROPERTY, "svg"); + workspace.getViews().getConfiguration().addProperty(MermaidImporter.MERMAID_COMPRESS_PROPERTY, "false"); ImageView view = workspace.getViews().createImageView("key"); new MermaidImporter().importDiagram(view, new File("./src/test/resources/diagrams/mermaid/flowchart.mmd")); @@ -73,8 +76,8 @@ public void importDiagram_WhenTheMermaidUrlIsNotDefined() throws Exception { @Test public void importDiagram_WhenAnInvalidFormatIsSpecified() throws Exception { Workspace workspace = new Workspace("Name", "Description"); - workspace.getViews().getConfiguration().addProperty("mermaid.url", "https://mermaid.ink"); - workspace.getViews().getConfiguration().addProperty("mermaid.format", "jpg"); + workspace.getViews().getConfiguration().addProperty(MermaidImporter.MERMAID_URL_PROPERTY, "https://mermaid.ink"); + workspace.getViews().getConfiguration().addProperty(MermaidImporter.MERMAID_FORMAT_PROPERTY, "jpg"); ImageView view = workspace.getViews().createImageView("key"); try { diff --git a/structurizr-import/src/test/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporterTests.java b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporterTests.java index 7e51dcd50..ea21bf2aa 100644 --- a/structurizr-import/src/test/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporterTests.java +++ b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporterTests.java @@ -13,7 +13,7 @@ public class PlantUMLImporterTests { @Test public void importDiagram_WhenATitleIsDefined() throws Exception { Workspace workspace = new Workspace("Name", "Description"); - workspace.getViews().getConfiguration().addProperty("plantuml.url", "https://plantuml.com/plantuml"); + workspace.getViews().getConfiguration().addProperty(PlantUMLImporter.PLANTUML_URL_PROPERTY, "https://plantuml.com/plantuml"); ImageView view = workspace.getViews().createImageView("key"); new PlantUMLImporter().importDiagram(view, new File("./src/test/resources/diagrams/plantuml/with-title.puml")); @@ -21,14 +21,14 @@ public void importDiagram_WhenATitleIsDefined() throws Exception { assertNull(view.getElement()); assertNull(view.getElementId()); assertEquals("Sequence diagram example", view.getTitle()); - assertEquals("https://plantuml.com/plantuml/png/SoWkIImgAStDuIh9BCb9LGXEBInDpKjELKZ9J4mlIinLIAr8p2t8IULooazIqBLJSCp914fQAMIavkJaSpcavgK0zG80", view.getContent()); - assertEquals("image/png", view.getContentType()); + assertEquals("https://plantuml.com/plantuml/svg/SoWkIImgAStDuIh9BCb9LGXEBInDpKjELKZ9J4mlIinLIAr8p2t8IULooazIqBLJSCp914fQAMIavkJaSpcavgK0zG80", view.getContent()); + assertEquals("image/svg+xml", view.getContentType()); } @Test public void importDiagram_WhenATitleIsNotDefined() throws Exception { Workspace workspace = new Workspace("Name", "Description"); - workspace.getViews().getConfiguration().addProperty("plantuml.url", "https://plantuml.com/plantuml"); + workspace.getViews().getConfiguration().addProperty(PlantUMLImporter.PLANTUML_URL_PROPERTY, "https://plantuml.com/plantuml"); ImageView view = workspace.getViews().createImageView("key"); new PlantUMLImporter().importDiagram(view, new File("./src/test/resources/diagrams/plantuml/without-title.puml")); @@ -36,15 +36,15 @@ public void importDiagram_WhenATitleIsNotDefined() throws Exception { assertNull(view.getElement()); assertNull(view.getElementId()); assertEquals("without-title.puml", view.getTitle()); - assertEquals("https://plantuml.com/plantuml/png/SoWkIImgAStDuNBAJrBGjLDmpCbCJbMmKiX8pSd9vt98pKi1IW80", view.getContent()); - assertEquals("image/png", view.getContentType()); + assertEquals("https://plantuml.com/plantuml/svg/SoWkIImgAStDuNBAJrBGjLDmpCbCJbMmKiX8pSd9vt98pKi1IW80", view.getContent()); + assertEquals("image/svg+xml", view.getContentType()); } @Test public void importDiagram_AsPNG() throws Exception { Workspace workspace = new Workspace("Name", "Description"); - workspace.getViews().getConfiguration().addProperty("plantuml.url", "https://plantuml.com/plantuml"); - workspace.getViews().getConfiguration().addProperty("plantuml.format", "png"); + workspace.getViews().getConfiguration().addProperty(PlantUMLImporter.PLANTUML_URL_PROPERTY, "https://plantuml.com/plantuml"); + workspace.getViews().getConfiguration().addProperty(PlantUMLImporter.PLANTUML_FORMAT_PROPERTY, "png"); ImageView view = workspace.getViews().createImageView("key"); new PlantUMLImporter().importDiagram(view, new File("./src/test/resources/diagrams/plantuml/with-title.puml")); @@ -55,8 +55,8 @@ public void importDiagram_AsPNG() throws Exception { @Test public void importDiagram_AsSVG() throws Exception { Workspace workspace = new Workspace("Name", "Description"); - workspace.getViews().getConfiguration().addProperty("plantuml.url", "https://plantuml.com/plantuml"); - workspace.getViews().getConfiguration().addProperty("plantuml.format", "svg"); + workspace.getViews().getConfiguration().addProperty(PlantUMLImporter.PLANTUML_URL_PROPERTY, "https://plantuml.com/plantuml"); + workspace.getViews().getConfiguration().addProperty(PlantUMLImporter.PLANTUML_FORMAT_PROPERTY, "svg"); ImageView view = workspace.getViews().createImageView("key"); new PlantUMLImporter().importDiagram(view, new File("./src/test/resources/diagrams/plantuml/with-title.puml")); @@ -80,8 +80,8 @@ public void importDiagram_WhenThePlantUMLURLIsNotSpecified() throws Exception { @Test public void importDiagram_WhenAnInvalidFormatIsSpecified() throws Exception { Workspace workspace = new Workspace("Name", "Description"); - workspace.getViews().getConfiguration().addProperty("plantuml.url", "https://plantuml.com/plantuml"); - workspace.getViews().getConfiguration().addProperty("plantuml.format", "jpg"); + workspace.getViews().getConfiguration().addProperty(PlantUMLImporter.PLANTUML_URL_PROPERTY, "https://plantuml.com/plantuml"); + workspace.getViews().getConfiguration().addProperty(PlantUMLImporter.PLANTUML_FORMAT_PROPERTY, "jpg"); ImageView view = workspace.getViews().createImageView("key"); try { From 34cd1491e0acb52a8e23df62769b9923aa0acaad Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Sun, 6 Oct 2024 13:02:38 +0100 Subject: [PATCH 595/717] Adds a way to configure whether the DSL source is retained via a workspace property named `structurizr.dsl.source` - `true` (default) or `false`. --- changelog.md | 1 + .../java/com/structurizr/dsl/DslUtils.java | 15 +++----- .../structurizr/dsl/StructurizrDslParser.java | 5 ++- .../java/com/structurizr/dsl/DslTests.java | 38 +++++++++++++++++++ .../src/test/resources/dsl/source-child.dsl | 7 ++++ .../resources/dsl/source-not-retained.dsl | 11 ++++++ .../src/test/resources/dsl/source-parent.dsl | 7 ++++ 7 files changed, 74 insertions(+), 10 deletions(-) create mode 100644 structurizr-dsl/src/test/resources/dsl/source-child.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/source-not-retained.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/source-parent.dsl diff --git a/changelog.md b/changelog.md index 8925cb7cb..e08f54ed5 100644 --- a/changelog.md +++ b/changelog.md @@ -9,6 +9,7 @@ - structurizr-dsl: Adds `supportingTypes implementation-suffix `. - structurizr-dsl: Fixes https://github.com/structurizr/java/issues/346 (`// comment \` joins lines). - structurizr-dsl: Anonymous identifiers for relationships (i.e. relationships not assigned to an identifier) are excluded from the model, and therefore also excluded from the serialised JSON. +- structurizr-dsl: Adds a way to configure whether the DSL source is retained via a workspace property named `structurizr.dsl.source` - `true` (default) or `false`. ## 3.0.0 (19th September 2024) diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DslUtils.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DslUtils.java index 16e73789e..e20b936f2 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/DslUtils.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DslUtils.java @@ -11,7 +11,8 @@ */ public class DslUtils { - private static final String STRUCTURIZR_DSL_PROPERTY_NAME = "structurizr.dsl"; + static final String STRUCTURIZR_DSL_PROPERTY_NAME = "structurizr.dsl"; + static final String STRUCTURIZR_DSL_RETAIN_SOURCE_PROPERTY_NAME = "structurizr.dsl.source"; /** * Gets the DSL associated with a workspace. @@ -21,13 +22,11 @@ public class DslUtils { */ public static String getDsl(Workspace workspace) { String base64 = workspace.getProperties().get(STRUCTURIZR_DSL_PROPERTY_NAME); - String dsl = ""; - if (!StringUtils.isNullOrEmpty(base64)) { - dsl = new String(Base64.getDecoder().decode(base64)); + return new String(Base64.getDecoder().decode(base64)); } - return dsl; + return ""; } /** @@ -37,12 +36,10 @@ public static String getDsl(Workspace workspace) { * @param dsl the DSL string */ public static void setDsl(Workspace workspace, String dsl) { - String base64 = ""; if (!StringUtils.isNullOrEmpty(dsl)) { - base64 = Base64.getEncoder().encodeToString(dsl.getBytes(StandardCharsets.UTF_8)); + String base64 = Base64.getEncoder().encodeToString(dsl.getBytes(StandardCharsets.UTF_8)); + workspace.addProperty(STRUCTURIZR_DSL_PROPERTY_NAME, base64); } - - workspace.addProperty(STRUCTURIZR_DSL_PROPERTY_NAME, base64); } } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index dc11d5217..50f30b356 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -101,7 +101,10 @@ public void setRestricted(boolean restricted) { */ public Workspace getWorkspace() { if (workspace != null) { - DslUtils.setDsl(workspace, getParsedDsl()); + String value = workspace.getProperties().get(DslUtils.STRUCTURIZR_DSL_RETAIN_SOURCE_PROPERTY_NAME); + if (value == null || value.equalsIgnoreCase("true")) { + DslUtils.setDsl(workspace, getParsedDsl()); + } } return workspace; diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index 62125ba96..0c73de740 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -1377,4 +1377,42 @@ void test_ImageView_WhenParserIsInRestrictedMode() { } } + @Test + void test_sourceIsRetained() throws Exception { + File parentDslFile = new File("src/test/resources/dsl/source-parent.dsl"); + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(parentDslFile); + Workspace workspace = parser.getWorkspace(); + assertEquals(""" +workspace { + + model { + a = softwareSystem "A" + } + +}""", DslUtils.getDsl(workspace)); + + File childDslFile = new File("src/test/resources/dsl/source-child.dsl"); + parser = new StructurizrDslParser(); + parser.parse(childDslFile); + workspace = parser.getWorkspace(); + assertEquals(""" +workspace extends source-parent.dsl { + + model { + b = softwareSystem "B" + } + +}""", DslUtils.getDsl(workspace)); + } + + @Test + void test_sourceIsNotRetained() throws Exception { + File parentDslFile = new File("src/test/resources/dsl/source-not-retained.dsl"); + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(parentDslFile); + Workspace workspace = parser.getWorkspace(); + assertNull(workspace.getProperties().get(DslUtils.STRUCTURIZR_DSL_PROPERTY_NAME)); + } + } \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/source-child.dsl b/structurizr-dsl/src/test/resources/dsl/source-child.dsl new file mode 100644 index 000000000..54ca1a6de --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/source-child.dsl @@ -0,0 +1,7 @@ +workspace extends source-parent.dsl { + + model { + b = softwareSystem "B" + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/source-not-retained.dsl b/structurizr-dsl/src/test/resources/dsl/source-not-retained.dsl new file mode 100644 index 000000000..a09c05b91 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/source-not-retained.dsl @@ -0,0 +1,11 @@ +workspace { + + properties { + structurizr.dsl.source false + } + + model { + a = softwareSystem "A" + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/source-parent.dsl b/structurizr-dsl/src/test/resources/dsl/source-parent.dsl new file mode 100644 index 000000000..a19e355ac --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/source-parent.dsl @@ -0,0 +1,7 @@ +workspace { + + model { + a = softwareSystem "A" + } + +} \ No newline at end of file From b412261494684383c5ce34d54527bd7d7543e56a Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Sun, 6 Oct 2024 13:53:24 +0100 Subject: [PATCH 596/717] structurizr-dsl: Adds the ability to define a PlantUML/Mermaid image view that is an export of a workspace view. --- changelog.md | 1 + structurizr-dsl/build.gradle | 1 + .../dsl/ImageViewContentParser.java | 61 ++++++++++++------- .../dsl/ImageViewContentParserTests.java | 54 ++++++++++++++++ .../export/AbstractDiagramExporter.java | 20 ++++++ .../plantuml/PlantUMLEncoderTests.java | 5 +- 6 files changed, 117 insertions(+), 25 deletions(-) diff --git a/changelog.md b/changelog.md index e08f54ed5..fee0fefc8 100644 --- a/changelog.md +++ b/changelog.md @@ -10,6 +10,7 @@ - structurizr-dsl: Fixes https://github.com/structurizr/java/issues/346 (`// comment \` joins lines). - structurizr-dsl: Anonymous identifiers for relationships (i.e. relationships not assigned to an identifier) are excluded from the model, and therefore also excluded from the serialised JSON. - structurizr-dsl: Adds a way to configure whether the DSL source is retained via a workspace property named `structurizr.dsl.source` - `true` (default) or `false`. +- structurizr-dsl: Adds the ability to define a PlantUML/Mermaid image view that is an export of a workspace view. ## 3.0.0 (19th September 2024) diff --git a/structurizr-dsl/build.gradle b/structurizr-dsl/build.gradle index 1a38536b6..b5d1a6a55 100644 --- a/structurizr-dsl/build.gradle +++ b/structurizr-dsl/build.gradle @@ -2,6 +2,7 @@ dependencies { api project(':structurizr-client') api project(':structurizr-import') + api project(':structurizr-export') api project(':structurizr-component') testImplementation 'org.codehaus.groovy:groovy-jsr223:3.0.22' diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewContentParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewContentParser.java index f59f5fcb5..2866f9b16 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewContentParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewContentParser.java @@ -1,24 +1,25 @@ package com.structurizr.dsl; +import com.structurizr.export.mermaid.MermaidDiagramExporter; +import com.structurizr.export.plantuml.StructurizrPlantUMLExporter; import com.structurizr.importer.diagrams.kroki.KrokiImporter; import com.structurizr.importer.diagrams.mermaid.MermaidImporter; import com.structurizr.importer.diagrams.plantuml.PlantUMLImporter; import com.structurizr.util.ImageUtils; import com.structurizr.util.Url; import com.structurizr.view.ImageView; +import com.structurizr.view.ModelView; +import com.structurizr.view.View; import java.io.File; final class ImageViewContentParser extends AbstractParser { - private static final String PLANTUML_GRAMMAR = "plantuml "; - private static final String MERMAID_GRAMMAR = "mermaid "; + private static final String PLANTUML_GRAMMAR = "plantuml "; + private static final String MERMAID_GRAMMAR = "mermaid "; private static final String KROKI_GRAMMAR = "kroki "; private static final String IMAGE_GRAMMAR = "image "; - private static final int TITLE_INDEX = 1; - private static final int DESCRIPTION_INDEX = 1; - private static final int PLANTUML_SOURCE_INDEX = 1; private static final int MERMAID_SOURCE_INDEX = 1; private static final int KROKI_FORMAT_INDEX = 1; @@ -32,7 +33,7 @@ final class ImageViewContentParser extends AbstractParser { } void parsePlantUML(ImageViewDslContext context, File dslFile, Tokens tokens) { - // plantuml + // plantuml if (tokens.hasMoreThan(PLANTUML_SOURCE_INDEX)) { throw new RuntimeException("Too many tokens, expected: " + PLANTUML_GRAMMAR); @@ -44,16 +45,23 @@ void parsePlantUML(ImageViewDslContext context, File dslFile, Tokens tokens) { String source = tokens.get(PLANTUML_SOURCE_INDEX); try { - if (Url.isUrl(source)) { - RemoteContent content = readFromUrl(source); - new PlantUMLImporter().importDiagram(context.getView(), content.getContent()); - context.getView().setTitle(source.substring(source.lastIndexOf("/")+1)); + View viewWithKey = context.getWorkspace().getViews().getViewWithKey(source); + if (viewWithKey instanceof ModelView) { + StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(); + String plantuml = exporter.export((ModelView)viewWithKey).getDefinition(); + new PlantUMLImporter().importDiagram(context.getView(), plantuml); } else { - if (!restricted) { - File file = new File(dslFile.getParentFile(), source); - new PlantUMLImporter().importDiagram(context.getView(), file); + if (Url.isUrl(source)) { + RemoteContent content = readFromUrl(source); + new PlantUMLImporter().importDiagram(context.getView(), content.getContent()); + context.getView().setTitle(source.substring(source.lastIndexOf("/") + 1)); } else { - throw new RuntimeException("PlantUML source must be specified as a URL when running in restricted mode"); + if (!restricted) { + File file = new File(dslFile.getParentFile(), source); + new PlantUMLImporter().importDiagram(context.getView(), file); + } else { + throw new RuntimeException("PlantUML source must be specified as a URL when running in restricted mode"); + } } } } catch (Exception e) { @@ -66,7 +74,7 @@ void parsePlantUML(ImageViewDslContext context, File dslFile, Tokens tokens) { } void parseMermaid(ImageViewDslContext context, File dslFile, Tokens tokens) { - // mermaid + // mermaid if (tokens.hasMoreThan(MERMAID_SOURCE_INDEX)) { throw new RuntimeException("Too many tokens, expected: " + MERMAID_GRAMMAR); @@ -78,16 +86,23 @@ void parseMermaid(ImageViewDslContext context, File dslFile, Tokens tokens) { String source = tokens.get(MERMAID_SOURCE_INDEX); try { - if (Url.isUrl(source)) { - RemoteContent content = readFromUrl(source); - new MermaidImporter().importDiagram(context.getView(), content.getContent()); - context.getView().setTitle(source.substring(source.lastIndexOf("/")+1)); + View viewWithKey = context.getWorkspace().getViews().getViewWithKey(source); + if (viewWithKey instanceof ModelView) { + MermaidDiagramExporter exporter = new MermaidDiagramExporter(); + String mermaid = exporter.export((ModelView)viewWithKey).getDefinition(); + new MermaidImporter().importDiagram(context.getView(), mermaid); } else { - if (!restricted) { - File file = new File(dslFile.getParentFile(), source); - new MermaidImporter().importDiagram(context.getView(), file); + if (Url.isUrl(source)) { + RemoteContent content = readFromUrl(source); + new MermaidImporter().importDiagram(context.getView(), content.getContent()); + context.getView().setTitle(source.substring(source.lastIndexOf("/") + 1)); } else { - throw new RuntimeException("Mermaid source must be specified as a URL when running in restricted mode"); + if (!restricted) { + File file = new File(dslFile.getParentFile(), source); + new MermaidImporter().importDiagram(context.getView(), file); + } else { + throw new RuntimeException("Mermaid source must be specified as a URL when running in restricted mode"); + } } } } catch (Exception e) { diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewContentParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewContentParserTests.java index fe2eef5eb..ca65bcadc 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewContentParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewContentParserTests.java @@ -1,5 +1,7 @@ package com.structurizr.dsl; +import com.structurizr.importer.diagrams.mermaid.MermaidImporter; +import com.structurizr.importer.diagrams.plantuml.PlantUMLImporter; import com.structurizr.view.ImageView; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -17,6 +19,19 @@ void setUp() { imageView = workspace.getViews().createImageView("key"); } + @Test + void test_parsePlantUML_ThrowsAnException_WithTooFewTokens() { + try { + ImageViewDslContext context = new ImageViewDslContext(imageView); + context.setWorkspace(workspace); + parser = new ImageViewContentParser(true); + parser.parsePlantUML(context, null, tokens("plantuml")); + fail(); + } catch (Exception e) { + assertEquals("Expected: plantuml ", e.getMessage()); + } + } + @Test void test_parsePlantUML_ThrowsAnException_WhenUsingAFileNameInRestrictedMode() { try { @@ -30,6 +45,32 @@ void test_parsePlantUML_ThrowsAnException_WhenUsingAFileNameInRestrictedMode() { } } + @Test + void test_parsePlantUML_WithViewKey() { + ImageViewDslContext context = new ImageViewDslContext(imageView); + context.setWorkspace(workspace); + workspace.getModel().addSoftwareSystem("A"); + workspace.getViews().createSystemLandscapeView("SystemLandscape", "Description").addAllElements(); + workspace.getViews().getConfiguration().addProperty(PlantUMLImporter.PLANTUML_URL_PROPERTY, "https://plantuml.com/plantuml"); + + parser = new ImageViewContentParser(true); + parser.parsePlantUML(context, null, tokens("plantuml", "SystemLandscape")); + assertEquals("https://plantuml.com/plantuml/svg/HP2nJiD038RtUmghF00f6oYD6f2OO2eI0p2Od9EUUh4Zdwkq8DwTkrB0bZpylsL_zZePgkt7w18P99fGqKI1XSbPi4YmEIQZ4HwGVUfm8kTC9Z21Tp6J4NnGwYm8EvTsWSk44JuT0AhAV2zic_11iAoovAd7VRGdEbWRmy0ZiK6N2sbsPyNfENZRmbLLkaSyF59AED1vGkM-dDi6Jv2HbCIE1UT_Qm517YBLTTiq9uXRx7Q3ofxzdSHys8K_HNOAsLchJb6wHJtfMRt6abbDM_Go1nwWnvYeGFnjWiLgrRvodJBXpR9gNZRIsupw-xUt-h9OpG9-c311wzoQsEUdVmC0", imageView.getContent()); + } + + @Test + void test_parseMermaid_ThrowsAnException_WithTooFewTokens() { + try { + ImageViewDslContext context = new ImageViewDslContext(imageView); + context.setWorkspace(workspace); + parser = new ImageViewContentParser(true); + parser.parseMermaid(context, null, tokens("plantuml")); + fail(); + } catch (Exception e) { + assertEquals("Expected: mermaid ", e.getMessage()); + } + } + @Test void test_parseMermaid_ThrowsAnException_WhenUsingAFileNameInRestrictedMode() { try { @@ -43,6 +84,19 @@ void test_parseMermaid_ThrowsAnException_WhenUsingAFileNameInRestrictedMode() { } } + @Test + void test_parseMermaid_WithViewKey() { + ImageViewDslContext context = new ImageViewDslContext(imageView); + context.setWorkspace(workspace); + workspace.getModel().addSoftwareSystem("A"); + workspace.getViews().createSystemLandscapeView("SystemLandscape", "Description").addAllElements(); + workspace.getViews().getConfiguration().addProperty(MermaidImporter.MERMAID_URL_PROPERTY, "https://mermaid.ink"); + + parser = new ImageViewContentParser(true); + parser.parseMermaid(context, null, tokens("mermaid", "SystemLandscape")); + assertEquals("https://mermaid.ink/svg/pako:eJxlkMtuwjAQRX9lNAhlE9SwqupCpLLuLt0RFiYeJxZ-RLYppYh_bxJHVR93NrM4c3U0N8DGCUKGred9B2-72gJoZU9VvGoCQZKfdQSptGYLOaW2IxPOx3QiFB8WA_saq2uIZOCVWxEa3lONhxEd4FQ2kz_L8hC9O9HvboD10LYR6j1dbjPpbFxdSLVdZHB0WmTly-ZhAMp_VFCfxOCxWD6D4b5VdhVdz6DoP7JyXzkZL9wTJNVD6vjjuZ4NxZRvwyc-Tt447TxbFFOSL1mBOaAhb7gSyG4YOzLjV-f_4f3-BQMfekI=", imageView.getContent()); + } + @Test void test_parseKroki_ThrowsAnException_WhenUsingAFileNameInRestrictedMode() { try { diff --git a/structurizr-export/src/main/java/com/structurizr/export/AbstractDiagramExporter.java b/structurizr-export/src/main/java/com/structurizr/export/AbstractDiagramExporter.java index b06c62f5a..56cef1709 100644 --- a/structurizr-export/src/main/java/com/structurizr/export/AbstractDiagramExporter.java +++ b/structurizr-export/src/main/java/com/structurizr/export/AbstractDiagramExporter.java @@ -80,6 +80,26 @@ public final Collection export(Workspace workspace) { return diagrams; } + public Diagram export(ModelView view) { + if (view instanceof SystemLandscapeView) { + return export((SystemLandscapeView)view); + } else if (view instanceof SystemContextView) { + return export((SystemContextView)view); + } else if (view instanceof ContainerView) { + return export((ContainerView)view); + } else if (view instanceof ComponentView) { + return export((ComponentView)view); + } else if (view instanceof DynamicView) { + return export((DynamicView)view); + } else if (view instanceof DeploymentView) { + return export((DeploymentView)view); + } else if (view instanceof CustomView) { + return export((CustomView)view); + } else { + throw new RuntimeException(view.getClass().getName() + " is not supported"); + } + } + public Diagram export(CustomView view) { Diagram diagram = export(view, null); diff --git a/structurizr-import/src/test/java/com/structurizr/importer/diagrams/plantuml/PlantUMLEncoderTests.java b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/plantuml/PlantUMLEncoderTests.java index 867cf0bb2..e13f848ee 100644 --- a/structurizr-import/src/test/java/com/structurizr/importer/diagrams/plantuml/PlantUMLEncoderTests.java +++ b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/plantuml/PlantUMLEncoderTests.java @@ -13,8 +13,9 @@ public class PlantUMLEncoderTests { @Test public void encode() throws Exception { File file = new File("./src/test/resources/diagrams/plantuml/with-title.puml"); - String mermaid = new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8); - assertEquals("SoWkIImgAStDuIh9BCb9LGXEBInDpKjELKZ9J4mlIinLIAr8p2t8IULooazIqBLJSCp914fQAMIavkJaSpcavgK0zG80", new PlantUMLEncoder().encode(mermaid)); + String plantuml = new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8); + String encodedPlantuml = new PlantUMLEncoder().encode(plantuml); + assertEquals("SoWkIImgAStDuIh9BCb9LGXEBInDpKjELKZ9J4mlIinLIAr8p2t8IULooazIqBLJSCp914fQAMIavkJaSpcavgK0zG80", encodedPlantuml); } } \ No newline at end of file From b507b5d7d38610a0a268d6326fe7c5f72b62646b Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Sun, 6 Oct 2024 14:46:43 +0100 Subject: [PATCH 597/717] structurizr-dsl: Adds support for `url`, `properties`, and `perspectives` nested inside `!elements` and `!relationships`. --- changelog.md | 1 + .../structurizr/dsl/ElementsDslContext.java | 9 ++- .../com/structurizr/dsl/ModelItemParser.java | 26 ------- .../dsl/ModelItemPerspectivesDslContext.java | 22 ------ .../com/structurizr/dsl/ModelItemsParser.java | 17 +++++ .../structurizr/dsl/PerspectiveParser.java | 35 +++++++++ .../dsl/PerspectivesDslContext.java | 29 ++++++++ .../structurizr/dsl/PropertiesDslContext.java | 15 ++-- .../com/structurizr/dsl/PropertyParser.java | 6 +- .../dsl/RelationshipsDslContext.java | 8 ++- .../structurizr/dsl/StructurizrDslParser.java | 16 ++++- .../structurizr/dsl/ModelItemParserTests.java | 58 --------------- .../dsl/PerspectiveParserTests.java | 72 +++++++++++++++++++ .../src/test/resources/dsl/test.dsl | 22 +++++- 14 files changed, 219 insertions(+), 117 deletions(-) delete mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemPerspectivesDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/PerspectiveParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/PerspectivesDslContext.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/PerspectiveParserTests.java diff --git a/changelog.md b/changelog.md index fee0fefc8..96cdedfee 100644 --- a/changelog.md +++ b/changelog.md @@ -11,6 +11,7 @@ - structurizr-dsl: Anonymous identifiers for relationships (i.e. relationships not assigned to an identifier) are excluded from the model, and therefore also excluded from the serialised JSON. - structurizr-dsl: Adds a way to configure whether the DSL source is retained via a workspace property named `structurizr.dsl.source` - `true` (default) or `false`. - structurizr-dsl: Adds the ability to define a PlantUML/Mermaid image view that is an export of a workspace view. +- structurizr-dsl: Adds support for `url`, `properties`, and `perspectives` nested inside `!elements` and `!relationships`. ## 3.0.0 (19th September 2024) diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementsDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementsDslContext.java index d0aec535b..7e056c159 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementsDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementsDslContext.java @@ -27,7 +27,14 @@ Set getModelItems() { @Override protected String[] getPermittedTokens() { - return new String[0]; + return new String[] { + StructurizrDslTokens.RELATIONSHIP_TOKEN, + StructurizrDslTokens.TAG_TOKEN, + StructurizrDslTokens.TAGS_TOKEN, + StructurizrDslTokens.URL_TOKEN, + StructurizrDslTokens.PROPERTIES_TOKEN, + StructurizrDslTokens.PERSPECTIVES_TOKEN + }; } } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemParser.java index f98cb241d..e4c48ac90 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemParser.java @@ -8,10 +8,6 @@ final class ModelItemParser extends AbstractParser { private final static int URL_INDEX = 1; - private final static int PERSPECTIVE_NAME_INDEX = 0; - private final static int PERSPECTIVE_DESCRIPTION_INDEX = 1; - private final static int PERSPECTIVE_VALUE_INDEX = 2; - void parseTags(ModelItemDslContext context, Tokens tokens) { // tags [tags] if (!tokens.includes(TAGS_INDEX)) { @@ -52,26 +48,4 @@ void parseUrl(ModelItemDslContext context, Tokens tokens) { context.getModelItem().setUrl(url); } - void parsePerspective(ModelItemPerspectivesDslContext context, Tokens tokens) { - // [value] - - if (tokens.hasMoreThan(PERSPECTIVE_VALUE_INDEX)) { - throw new RuntimeException("Too many tokens, expected: [value]"); - } - - if (!tokens.includes(PERSPECTIVE_DESCRIPTION_INDEX)) { - throw new RuntimeException("Expected: [value]"); - } - - String name = tokens.get(PERSPECTIVE_NAME_INDEX); - String description = tokens.get(PERSPECTIVE_DESCRIPTION_INDEX); - String value = ""; - - if (tokens.includes(PERSPECTIVE_VALUE_INDEX)) { - value = tokens.get(PERSPECTIVE_VALUE_INDEX); - } - - context.getModelItem().addPerspective(name, description, value); - } - } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemPerspectivesDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemPerspectivesDslContext.java deleted file mode 100644 index 37647a492..000000000 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemPerspectivesDslContext.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.structurizr.dsl; - -import com.structurizr.model.ModelItem; - -final class ModelItemPerspectivesDslContext extends DslContext { - - private ModelItem modelItem; - - public ModelItemPerspectivesDslContext(ModelItem modelItem) { - this.modelItem = modelItem; - } - - ModelItem getModelItem() { - return this.modelItem; - } - - @Override - protected String[] getPermittedTokens() { - return new String[0]; - } - -} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemsParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemsParser.java index c34ceb33d..2c44be60b 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemsParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelItemsParser.java @@ -5,6 +5,7 @@ final class ModelItemsParser extends AbstractParser { private final static int TAGS_INDEX = 1; + private final static int URL_INDEX = 1; void parseTags(ModelItemsDslContext context, Tokens tokens) { // tags [tags] @@ -21,4 +22,20 @@ void parseTags(ModelItemsDslContext context, Tokens tokens) { } } + void parseUrl(ModelItemsDslContext context, Tokens tokens) { + // url + if (tokens.hasMoreThan(URL_INDEX)) { + throw new RuntimeException("Too many tokens, expected: url "); + } + + if (!tokens.includes(URL_INDEX)) { + throw new RuntimeException("Expected: url "); + } + + String url = tokens.get(URL_INDEX); + for (ModelItem modelItem : context.getModelItems()) { + modelItem.setUrl(url); + } + } + } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/PerspectiveParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/PerspectiveParser.java new file mode 100644 index 000000000..3a38ad3ab --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/PerspectiveParser.java @@ -0,0 +1,35 @@ +package com.structurizr.dsl; + +import com.structurizr.model.ModelItem; + +final class PerspectiveParser extends AbstractParser { + + private final static int PERSPECTIVE_NAME_INDEX = 0; + private final static int PERSPECTIVE_DESCRIPTION_INDEX = 1; + private final static int PERSPECTIVE_VALUE_INDEX = 2; + + void parse(PerspectivesDslContext context, Tokens tokens) { + // [value] + + if (tokens.hasMoreThan(PERSPECTIVE_VALUE_INDEX)) { + throw new RuntimeException("Too many tokens, expected: [value]"); + } + + if (!tokens.includes(PERSPECTIVE_DESCRIPTION_INDEX)) { + throw new RuntimeException("Expected: [value]"); + } + + String name = tokens.get(PERSPECTIVE_NAME_INDEX); + String description = tokens.get(PERSPECTIVE_DESCRIPTION_INDEX); + String value = ""; + + if (tokens.includes(PERSPECTIVE_VALUE_INDEX)) { + value = tokens.get(PERSPECTIVE_VALUE_INDEX); + } + + for (ModelItem modelItem : context.getModelItems()) { + modelItem.addPerspective(name, description, value); + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/PerspectivesDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/PerspectivesDslContext.java new file mode 100644 index 000000000..50ff2e6d3 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/PerspectivesDslContext.java @@ -0,0 +1,29 @@ +package com.structurizr.dsl; + +import com.structurizr.model.ModelItem; + +import java.util.ArrayList; +import java.util.Collection; + +final class PerspectivesDslContext extends DslContext { + + private final Collection modelItems = new ArrayList<>(); + + PerspectivesDslContext(ModelItem modelItem) { + this.modelItems.add(modelItem); + } + + PerspectivesDslContext(Collection modelItems) { + this.modelItems.addAll(modelItems); + } + + Collection getModelItems() { + return this.modelItems; + } + + @Override + protected String[] getPermittedTokens() { + return new String[0]; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/PropertiesDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/PropertiesDslContext.java index e1f8184fd..6b31503de 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/PropertiesDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/PropertiesDslContext.java @@ -2,16 +2,23 @@ import com.structurizr.PropertyHolder; +import java.util.ArrayList; +import java.util.Collection; + final class PropertiesDslContext extends DslContext { - private PropertyHolder propertyHolder; + private final Collection propertyHolders = new ArrayList<>(); public PropertiesDslContext(PropertyHolder propertyHolder) { - this.propertyHolder = propertyHolder; + this.propertyHolders.add(propertyHolder); + } + + public PropertiesDslContext(Collection propertyHolders) { + this.propertyHolders.addAll(propertyHolders); } - PropertyHolder getPropertyHolder() { - return this.propertyHolder; + Collection getPropertyHolders() { + return this.propertyHolders; } @Override diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/PropertyParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/PropertyParser.java index 2129a2ddd..0a1ba6fb5 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/PropertyParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/PropertyParser.java @@ -1,5 +1,7 @@ package com.structurizr.dsl; +import com.structurizr.PropertyHolder; + final class PropertyParser extends AbstractParser { private final static int PROPERTY_NAME_INDEX = 0; @@ -19,7 +21,9 @@ void parse(PropertiesDslContext context, Tokens tokens) { String name = tokens.get(PROPERTY_NAME_INDEX); String value = tokens.get(PROPERTY_VALUE_INDEX); - context.getPropertyHolder().addProperty(name, value); + for (PropertyHolder propertyHolder : context.getPropertyHolders()) { + propertyHolder.addProperty(name, value); + } } } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipsDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipsDslContext.java index 3797be712..9a830de7f 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipsDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipsDslContext.java @@ -26,7 +26,13 @@ Set getModelItems() { @Override protected String[] getPermittedTokens() { - return new String[0]; + return new String[] { + StructurizrDslTokens.TAG_TOKEN, + StructurizrDslTokens.TAGS_TOKEN, + StructurizrDslTokens.URL_TOKEN, + StructurizrDslTokens.PROPERTIES_TOKEN, + StructurizrDslTokens.PERSPECTIVES_TOKEN + }; } } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index 50f30b356..9581c6d5e 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -1,5 +1,6 @@ package com.structurizr.dsl; +import com.structurizr.PropertyHolder; import com.structurizr.Workspace; import com.structurizr.model.*; import com.structurizr.util.StringUtils; @@ -554,6 +555,9 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn } else if (URL_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelItemDslContext.class) && !isGroup(getContext())) { new ModelItemParser().parseUrl(getContext(ModelItemDslContext.class), tokens); + } else if (URL_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelItemsDslContext.class)) { + new ModelItemsParser().parseUrl(getContext(ModelItemsDslContext.class), tokens); + } else if (PROPERTIES_TOKEN.equalsIgnoreCase(firstToken) && inContext(WorkspaceDslContext.class)) { startContext(new PropertiesDslContext(workspace)); @@ -566,6 +570,9 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn } else if (PROPERTIES_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelItemDslContext.class) && !isGroup(getContext())) { startContext(new PropertiesDslContext(getContext(ModelItemDslContext.class).getModelItem())); + } else if (PROPERTIES_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelItemsDslContext.class)) { + startContext(new PropertiesDslContext(getContext(ModelItemsDslContext.class).getModelItems().stream().map(mi -> (PropertyHolder)mi).toList())); + } else if (PROPERTIES_TOKEN.equalsIgnoreCase(firstToken) && inContext(ViewsDslContext.class)) { startContext(new PropertiesDslContext(workspace.getViews().getConfiguration())); @@ -585,10 +592,13 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn new PropertyParser().parse(getContext(PropertiesDslContext.class), tokens); } else if (PERSPECTIVES_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelItemDslContext.class) && !isGroup(getContext())) { - startContext(new ModelItemPerspectivesDslContext(getContext(ModelItemDslContext.class).getModelItem())); + startContext(new PerspectivesDslContext(getContext(ModelItemDslContext.class).getModelItem())); + + } else if (PERSPECTIVES_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelItemsDslContext.class)) { + startContext(new PerspectivesDslContext(getContext(ModelItemsDslContext.class).getModelItems())); - } else if (inContext(ModelItemPerspectivesDslContext.class)) { - new ModelItemParser().parsePerspective(getContext(ModelItemPerspectivesDslContext.class), tokens); + } else if (inContext(PerspectivesDslContext.class)) { + new PerspectiveParser().parse(getContext(PerspectivesDslContext.class), tokens); } else if (WORKSPACE_TOKEN.equalsIgnoreCase(firstToken) && contextStack.empty()) { if (parsedTokens.contains(WORKSPACE_TOKEN)) { diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ModelItemParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ModelItemParserTests.java index 011826924..fb1091782 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ModelItemParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ModelItemParserTests.java @@ -1,6 +1,5 @@ package com.structurizr.dsl; -import com.structurizr.model.Perspective; import com.structurizr.model.SoftwareSystem; import org.junit.jupiter.api.Test; @@ -105,61 +104,4 @@ void test_parseUrl_SetsTheUrl_WhenAUrlIsSpecified() { assertEquals("http://example.com", softwareSystem.getUrl()); } - @Test - void test_parsePerspective_ThrowsAnException_WhenThereAreTooManyTokens() { - try { - ModelItemPerspectivesDslContext context = new ModelItemPerspectivesDslContext(null); - parser.parsePerspective(context, tokens("name", "description", "value", "extra")); - fail(); - } catch (Exception e) { - assertEquals("Too many tokens, expected: [value]", e.getMessage()); - } - } - - @Test - void test_parsePerspective_ThrowsAnException_WhenNoNameIsSpecified() { - try { - SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); - ModelItemPerspectivesDslContext context = new ModelItemPerspectivesDslContext(softwareSystem); - parser.parsePerspective(context, tokens()); - fail(); - } catch (Exception e) { - assertEquals("Expected: [value]", e.getMessage()); - } - } - - @Test - void test_parsePerspective_ThrowsAnException_WhenNoDescriptionIsSpecified() { - try { - SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); - ModelItemPerspectivesDslContext context = new ModelItemPerspectivesDslContext(softwareSystem); - parser.parsePerspective(context, tokens("name")); - fail(); - } catch (Exception e) { - assertEquals("Expected: [value]", e.getMessage()); - } - } - - @Test - void test_parsePerspective_AddsThePerspective_WhenADescriptionIsSpecified() { - SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); - ModelItemPerspectivesDslContext context = new ModelItemPerspectivesDslContext(softwareSystem); - parser.parsePerspective(context, tokens("Security", "Description")); - - Perspective perspective = softwareSystem.getPerspectives().stream().filter(p -> p.getName().equals("Security")).findFirst().get(); - assertEquals("Description", perspective.getDescription()); - assertEquals("", perspective.getValue()); - } - - @Test - void test_parsePerspective_AddsThePerspective_WhenADescriptionAndValueIsSpecified() { - SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); - ModelItemPerspectivesDslContext context = new ModelItemPerspectivesDslContext(softwareSystem); - parser.parsePerspective(context, tokens("Security", "Description", "Value")); - - Perspective perspective = softwareSystem.getPerspectives().stream().filter(p -> p.getName().equals("Security")).findFirst().get(); - assertEquals("Description", perspective.getDescription()); - assertEquals("Value", perspective.getValue()); - } - } \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/PerspectiveParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/PerspectiveParserTests.java new file mode 100644 index 000000000..3f11603dc --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/PerspectiveParserTests.java @@ -0,0 +1,72 @@ +package com.structurizr.dsl; + +import com.structurizr.model.ModelItem; +import com.structurizr.model.Perspective; +import com.structurizr.model.SoftwareSystem; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +class PerspectiveParserTests extends AbstractTests { + + private final PerspectiveParser parser = new PerspectiveParser(); + + @Test + void test_parsePerspective_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + PerspectivesDslContext context = new PerspectivesDslContext((ModelItem)null); + parser.parse(context, tokens("name", "description", "value", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: [value]", e.getMessage()); + } + } + + @Test + void test_parsePerspective_ThrowsAnException_WhenNoNameIsSpecified() { + try { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); + PerspectivesDslContext context = new PerspectivesDslContext(softwareSystem); + parser.parse(context, tokens()); + fail(); + } catch (Exception e) { + assertEquals("Expected: [value]", e.getMessage()); + } + } + + @Test + void test_parsePerspective_ThrowsAnException_WhenNoDescriptionIsSpecified() { + try { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); + PerspectivesDslContext context = new PerspectivesDslContext(softwareSystem); + parser.parse(context, tokens("name")); + fail(); + } catch (Exception e) { + assertEquals("Expected: [value]", e.getMessage()); + } + } + + @Test + void test_parsePerspective_AddsThePerspective_WhenADescriptionIsSpecified() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); + PerspectivesDslContext context = new PerspectivesDslContext(softwareSystem); + parser.parse(context, tokens("Security", "Description")); + + Perspective perspective = softwareSystem.getPerspectives().stream().filter(p -> p.getName().equals("Security")).findFirst().get(); + assertEquals("Description", perspective.getDescription()); + assertEquals("", perspective.getValue()); + } + + @Test + void test_parsePerspective_AddsThePerspective_WhenADescriptionAndValueIsSpecified() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Name", "Description"); + PerspectivesDslContext context = new PerspectivesDslContext(softwareSystem); + parser.parse(context, tokens("Security", "Description", "Value")); + + Perspective perspective = softwareSystem.getPerspectives().stream().filter(p -> p.getName().equals("Security")).findFirst().get(); + assertEquals("Description", perspective.getDescription()); + assertEquals("Value", perspective.getValue()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/test.dsl b/structurizr-dsl/src/test/resources/dsl/test.dsl index 7deeaf866..045aa311d 100644 --- a/structurizr-dsl/src/test/resources/dsl/test.dsl +++ b/structurizr-dsl/src/test/resources/dsl/test.dsl @@ -69,7 +69,15 @@ workspace "Name" "Description" { } !elements "element.parent==webApplication && element.technology==Spring MVC Controller" { - tags "Spring MVC Controller" + tag "Tag 1" + tags "Tag 2, Tag 3" + url "https://example.com" + properties { + "type" "Spring MVC Controller" + } + perspectives { + "Owner" "Team A" + } } } @@ -142,6 +150,18 @@ workspace "Name" "Description" { } } + + !relationships "*->*" { + tag "Tag 1" + tags "Tag 2, Tag 3" + url "https://example.com" + properties { + name value + } + perspectives { + name value + } + } } views { From 1e6c2a35fb8202da9cf323157bd3b2f2e485ee4f Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Wed, 9 Oct 2024 15:36:34 +0100 Subject: [PATCH 598/717] Closes #347. --- changelog.md | 1 + .../dsl/DeploymentViewExpressionParser.java | 19 +++++++ .../com/structurizr/dsl/ExpressionParser.java | 2 +- .../DeploymentViewExpressionParserTests.java | 53 +++++++++++++++++++ 4 files changed, 74 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 96cdedfee..2975057df 100644 --- a/changelog.md +++ b/changelog.md @@ -12,6 +12,7 @@ - structurizr-dsl: Adds a way to configure whether the DSL source is retained via a workspace property named `structurizr.dsl.source` - `true` (default) or `false`. - structurizr-dsl: Adds the ability to define a PlantUML/Mermaid image view that is an export of a workspace view. - structurizr-dsl: Adds support for `url`, `properties`, and `perspectives` nested inside `!elements` and `!relationships`. +- structurizr-dsl: Fixes https://github.com/structurizr/java/issues/347 (`->container->` expression does not work as expected in deployment view). ## 3.0.0 (19th September 2024) diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentViewExpressionParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentViewExpressionParser.java index 33e9a5b37..ea6266d86 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentViewExpressionParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentViewExpressionParser.java @@ -4,6 +4,7 @@ import java.util.LinkedHashSet; import java.util.Set; +import java.util.stream.Collectors; import static com.structurizr.dsl.StructurizrDslExpressions.ELEMENT_TYPE_EQUALS_EXPRESSION; @@ -43,6 +44,24 @@ protected Set evaluateElementTypeExpression(String expr, DslContext con return elements; } + @Override + protected Set getElements(String identifier, DslContext context) { + Set elements = new LinkedHashSet<>(); + for (Element element : super.getElements(identifier, context)) { + if (element instanceof SoftwareSystem) { + Set elementInstances = context.getWorkspace().getModel().getElements().stream().filter(e -> e instanceof SoftwareSystemInstance).map(e -> (SoftwareSystemInstance) e).filter(ssi -> ssi.getSoftwareSystem().equals(element)).collect(Collectors.toSet()); + elements.addAll(elementInstances); + } else if (element instanceof Container) { + Set elementInstances = context.getWorkspace().getModel().getElements().stream().filter(e -> e instanceof ContainerInstance).map(e -> (ContainerInstance)e).filter(ci -> ci.getContainer().equals(element)).collect(Collectors.toSet()); + elements.addAll(elementInstances); + } else { + elements.add(element); + } + } + + return elements; + } + protected Set findAfferentCouplings(Element element) { Set elements = new LinkedHashSet<>(); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ExpressionParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ExpressionParser.java index 9599c79f1..8ee65bbcf 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ExpressionParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ExpressionParser.java @@ -419,7 +419,7 @@ protected Set parseIdentifier(String identifier, DslContext context) } } - private Set getElements(String identifier, DslContext context) { + protected Set getElements(String identifier, DslContext context) { Set elements = new HashSet<>(); Element element = context.getElement(identifier); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentViewExpressionParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentViewExpressionParserTests.java index bf4a417a0..95eef0d04 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentViewExpressionParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentViewExpressionParserTests.java @@ -217,4 +217,57 @@ void test_parseExpression_ReturnsElements_WhenBooleanOrUsed() { assertTrue(elements.contains(containerInstance)); } + @Test + void test_parseExpression_ReturnsSoftwareSystemInstanceDependencies_WhenASoftwareSystemExpressionIsUsed() { + SoftwareSystem a = model.addSoftwareSystem("A"); + SoftwareSystem b = model.addSoftwareSystem("B"); + a.uses(b, ""); + + DeploymentNode deploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + InfrastructureNode infrastructureNode = deploymentNode.addInfrastructureNode("Infrastructure Node"); + SoftwareSystemInstance softwareSystemInstanceA = deploymentNode.add(a); + SoftwareSystemInstance softwareSystemInstanceB = deploymentNode.add(b); + infrastructureNode.uses(softwareSystemInstanceA, "", ""); + + DeploymentViewDslContext context = new DeploymentViewDslContext(null); + context.setWorkspace(workspace); + + IdentifiersRegister identifiersRegister = new IdentifiersRegister(); + identifiersRegister.register("a", a); + context.setIdentifierRegister(identifiersRegister); + + Set elements = parser.parseExpression("->a->", context); + assertEquals(3, elements.size()); + assertTrue(elements.contains(infrastructureNode)); + assertTrue(elements.contains(softwareSystemInstanceA)); + assertTrue(elements.contains(softwareSystemInstanceB)); + } + + @Test + void test_parseExpression_ReturnsContainerInstanceDependencies_WhenAContainerExpressionIsUsed() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + Container container1 = softwareSystem.addContainer("Container 1"); + Container container2 = softwareSystem.addContainer("Container 2"); + container1.uses(container2, ""); + + DeploymentNode deploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + InfrastructureNode infrastructureNode = deploymentNode.addInfrastructureNode("Infrastructure Node"); + ContainerInstance containerInstance1 = deploymentNode.add(container1); + ContainerInstance containerInstance2 = deploymentNode.add(container2); + infrastructureNode.uses(containerInstance1, "", ""); + + DeploymentViewDslContext context = new DeploymentViewDslContext(null); + context.setWorkspace(workspace); + + IdentifiersRegister identifiersRegister = new IdentifiersRegister(); + identifiersRegister.register("c1", container1); + context.setIdentifierRegister(identifiersRegister); + + Set elements = parser.parseExpression("->c1->", context); + assertEquals(3, elements.size()); + assertTrue(elements.contains(infrastructureNode)); + assertTrue(elements.contains(containerInstance1)); + assertTrue(elements.contains(containerInstance2)); + } + } \ No newline at end of file From cb8a407ba82626c4dc4fe6152b07161e02a3d2dc Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Thu, 17 Oct 2024 14:13:50 +0200 Subject: [PATCH 599/717] WorkspaceAPIClient: Allows "main" to be used to refer to the main/default branch. --- .../src/main/java/com/structurizr/api/WorkspaceApiClient.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/structurizr-client/src/main/java/com/structurizr/api/WorkspaceApiClient.java b/structurizr-client/src/main/java/com/structurizr/api/WorkspaceApiClient.java index 1ab4a7158..3b7ce5c95 100644 --- a/structurizr-client/src/main/java/com/structurizr/api/WorkspaceApiClient.java +++ b/structurizr-client/src/main/java/com/structurizr/api/WorkspaceApiClient.java @@ -39,6 +39,7 @@ public class WorkspaceApiClient extends AbstractApiClient { private static final Log log = LogFactory.getLog(WorkspaceApiClient.class); + private static final String MAIN_BRANCH = "main"; private String user; @@ -234,7 +235,7 @@ public Workspace getWorkspace(long workspaceId) throws StructurizrClientExceptio log.info("Getting workspace with ID " + workspaceId); HttpGet httpGet; - if (StringUtils.isNullOrEmpty(branch)) { + if (StringUtils.isNullOrEmpty(branch) || branch.equalsIgnoreCase(MAIN_BRANCH)) { httpGet = new HttpGet(url + WORKSPACE_PATH + "/" + workspaceId); } else { httpGet = new HttpGet(url + WORKSPACE_PATH + "/" + workspaceId + "/branch/" + branch); From f3638363d7eba76f3784691d74e8e3d744743020 Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Wed, 23 Oct 2024 11:26:09 +0100 Subject: [PATCH 600/717] Adds support for `!elements group` (https://github.com/structurizr/java/issues/351). --- changelog.md | 1 + .../com/structurizr/dsl/ExpressionParser.java | 6 ++++ .../java/com/structurizr/dsl/DslTests.java | 29 +++++++++++++++- .../dsl/find-elements-in-flat-group.dsl | 17 ++++++++++ .../dsl/find-elements-in-nested-group.dsl | 34 +++++++++++++++++++ 5 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 structurizr-dsl/src/test/resources/dsl/find-elements-in-flat-group.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/find-elements-in-nested-group.dsl diff --git a/changelog.md b/changelog.md index 2975057df..29cc8c172 100644 --- a/changelog.md +++ b/changelog.md @@ -13,6 +13,7 @@ - structurizr-dsl: Adds the ability to define a PlantUML/Mermaid image view that is an export of a workspace view. - structurizr-dsl: Adds support for `url`, `properties`, and `perspectives` nested inside `!elements` and `!relationships`. - structurizr-dsl: Fixes https://github.com/structurizr/java/issues/347 (`->container->` expression does not work as expected in deployment view). +- structurizr-dsl: Adds support for `!elements group` (https://github.com/structurizr/java/issues/351). ## 3.0.0 (19th September 2024) diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ExpressionParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ExpressionParser.java index 8ee65bbcf..cac370945 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ExpressionParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ExpressionParser.java @@ -274,6 +274,12 @@ private Set evaluateExpression(String expr, DslContext context) { modelItems.add(relationship); } }); + } else { + // fallback that the expression is an identifier + Set elements = getElements(expr, context); + if (!elements.isEmpty()) { + modelItems.addAll(elements); + } } return modelItems; diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index 0c73de740..ec37f2afd 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -531,7 +531,7 @@ void test_findElement() throws Exception { } @Test - void test_findElement_Hierachical() throws Exception { + void test_findElement_Hierarchical() throws Exception { File dslFile = new File("src/test/resources/dsl/find-element-hierarchical.dsl"); StructurizrDslParser parser = new StructurizrDslParser(); @@ -543,6 +543,33 @@ void test_findElement_Hierachical() throws Exception { assertEquals("Value3", component.getProperties().get("Name3")); } + @Test + void test_findElements_InFlatGroup() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/find-elements-in-flat-group.dsl")); + + Person user = parser.getWorkspace().getModel().getPersonWithName("User"); + assertTrue(user.hasEfferentRelationshipWith(parser.getWorkspace().getModel().getSoftwareSystemWithName("A"), "Uses")); + assertTrue(user.hasEfferentRelationshipWith(parser.getWorkspace().getModel().getSoftwareSystemWithName("B"), "Uses")); + assertTrue(user.hasEfferentRelationshipWith(parser.getWorkspace().getModel().getSoftwareSystemWithName("C"), "Uses")); + } + + @Test + void test_findElements_InNestedGroup() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/find-elements-in-nested-group.dsl")); + + Person user1 = parser.getWorkspace().getModel().getPersonWithName("User 1"); + assertTrue(user1.hasEfferentRelationshipWith(parser.getWorkspace().getModel().getSoftwareSystemWithName("A"), "Uses")); + assertTrue(user1.hasEfferentRelationshipWith(parser.getWorkspace().getModel().getSoftwareSystemWithName("B"), "Uses")); + assertTrue(user1.hasEfferentRelationshipWith(parser.getWorkspace().getModel().getSoftwareSystemWithName("C"), "Uses")); + + Person user2 = parser.getWorkspace().getModel().getPersonWithName("User 2"); + assertTrue(user2.hasEfferentRelationshipWith(parser.getWorkspace().getModel().getSoftwareSystemWithName("A"), "Uses")); + assertFalse(user2.hasEfferentRelationshipWith(parser.getWorkspace().getModel().getSoftwareSystemWithName("B"), "Uses")); + assertFalse(user2.hasEfferentRelationshipWith(parser.getWorkspace().getModel().getSoftwareSystemWithName("C"), "Uses")); + } + @Test void test_parallel1() throws Exception { StructurizrDslParser parser = new StructurizrDslParser(); diff --git a/structurizr-dsl/src/test/resources/dsl/find-elements-in-flat-group.dsl b/structurizr-dsl/src/test/resources/dsl/find-elements-in-flat-group.dsl new file mode 100644 index 000000000..a64eccbc5 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/find-elements-in-flat-group.dsl @@ -0,0 +1,17 @@ +workspace { + + model { + user = person "User" + + group = group "Group" { + softwareSystem "A" + softwareSystem "B" + softwareSystem "C" + } + + !elements group { + user -> this "Uses" + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/find-elements-in-nested-group.dsl b/structurizr-dsl/src/test/resources/dsl/find-elements-in-nested-group.dsl new file mode 100644 index 000000000..daae8b271 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/find-elements-in-nested-group.dsl @@ -0,0 +1,34 @@ +workspace { + + model { + properties { + "structurizr.groupSeparator" "/" + } + + user1 = person "User 1" + user2 = person "User 2" + + department1 = group "Department 1" { + team1 = group "Team 1" { + softwareSystem "A" + } + + team2 = group "Team 2" { + softwareSystem "B" + } + + team3 = group "Team 3" { + softwareSystem "C" + } + } + + !elements department1 { + user1 -> this "Uses" + } + + !elements team1 { + user2 -> this "Uses" + } + } + +} \ No newline at end of file From a3aa43439f0ce68fdc350ff9ea1e514ab2d66a2d Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Mon, 4 Nov 2024 10:32:34 +0000 Subject: [PATCH 601/717] Updated for release. --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 29cc8c172..62e8b3181 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 3.1.0 (unreleased) +## 3.1.0 (4th November 2024) - structurizr-client: Workspace archive file now includes the branch name in the filename. - structurizr-component: Adds `ImplementationWithPrefixSupportingTypesStrategy`. From 0351c2dce30dd1d98d2ef4dc438db747bf646dea Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Mon, 4 Nov 2024 10:41:14 +0000 Subject: [PATCH 602/717] Bumps dependencies. --- structurizr-autolayout/build.gradle | 2 +- structurizr-client/build.gradle | 6 +++--- structurizr-component/build.gradle | 2 +- structurizr-core/build.gradle | 4 ++-- structurizr-dsl/build.gradle | 4 ++-- structurizr-export/build.gradle | 2 +- structurizr-import/build.gradle | 2 +- structurizr-inspection/build.gradle | 2 +- structurizr-neo4j/build.gradle | 4 ++-- 9 files changed, 14 insertions(+), 14 deletions(-) diff --git a/structurizr-autolayout/build.gradle b/structurizr-autolayout/build.gradle index 19510a149..631c38fcc 100644 --- a/structurizr-autolayout/build.gradle +++ b/structurizr-autolayout/build.gradle @@ -3,7 +3,7 @@ dependencies { api project(':structurizr-export') testImplementation project(':structurizr-client') - testImplementation 'org.junit.jupiter:junit-jupiter:5.11.0' + testImplementation 'org.junit.jupiter:junit-jupiter:5.11.3' } diff --git a/structurizr-client/build.gradle b/structurizr-client/build.gradle index 32f0fb02b..a67e1adf7 100644 --- a/structurizr-client/build.gradle +++ b/structurizr-client/build.gradle @@ -2,11 +2,11 @@ dependencies { api project(':structurizr-core') - api 'com.fasterxml.jackson.core:jackson-databind:2.17.2' - api 'org.apache.httpcomponents.client5:httpclient5:5.4' + api 'com.fasterxml.jackson.core:jackson-databind:2.18.1' + api 'org.apache.httpcomponents.client5:httpclient5:5.4.1' api 'javax.xml.bind:jaxb-api:2.4.0-b180830.0359' - testImplementation 'org.junit.jupiter:junit-jupiter:5.11.0' + testImplementation 'org.junit.jupiter:junit-jupiter:5.11.3' } diff --git a/structurizr-component/build.gradle b/structurizr-component/build.gradle index 538c74020..3c365fbb5 100644 --- a/structurizr-component/build.gradle +++ b/structurizr-component/build.gradle @@ -5,7 +5,7 @@ dependencies { implementation 'com.github.javaparser:javaparser-symbol-solver-core:3.26.2' testImplementation project(':structurizr-annotation') - testImplementation 'org.junit.jupiter:junit-jupiter:5.11.0' + testImplementation 'org.junit.jupiter:junit-jupiter:5.11.3' } diff --git a/structurizr-core/build.gradle b/structurizr-core/build.gradle index 80ccbf6dd..d188aa2be 100644 --- a/structurizr-core/build.gradle +++ b/structurizr-core/build.gradle @@ -1,10 +1,10 @@ dependencies { - api 'com.fasterxml.jackson.core:jackson-annotations:2.17.2' + api 'com.fasterxml.jackson.core:jackson-annotations:2.18.1' api 'com.google.code.findbugs:jsr305:3.0.2' api 'commons-logging:commons-logging:1.3.4' - testImplementation 'org.junit.jupiter:junit-jupiter:5.11.0' + testImplementation 'org.junit.jupiter:junit-jupiter:5.11.3' testImplementation 'org.assertj:assertj-core:3.26.3' } \ No newline at end of file diff --git a/structurizr-dsl/build.gradle b/structurizr-dsl/build.gradle index b5d1a6a55..b452a6366 100644 --- a/structurizr-dsl/build.gradle +++ b/structurizr-dsl/build.gradle @@ -9,8 +9,8 @@ dependencies { testImplementation 'org.jetbrains.kotlin:kotlin-scripting-jsr223:1.9.25' testImplementation 'org.jruby:jruby-core:9.4.8.0' - testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.11.0' - testImplementation 'org.junit.jupiter:junit-jupiter-params:5.11.0' + testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.11.3' + testImplementation 'org.junit.jupiter:junit-jupiter-params:5.11.3' } description = 'Structurizr DSL' \ No newline at end of file diff --git a/structurizr-export/build.gradle b/structurizr-export/build.gradle index 67a1a4e33..ddef771b2 100644 --- a/structurizr-export/build.gradle +++ b/structurizr-export/build.gradle @@ -3,7 +3,7 @@ dependencies { api project(':structurizr-core') testImplementation project(':structurizr-client') - testImplementation 'org.junit.jupiter:junit-jupiter:5.11.0' + testImplementation 'org.junit.jupiter:junit-jupiter:5.11.3' } diff --git a/structurizr-import/build.gradle b/structurizr-import/build.gradle index a1dd4218c..eeb4cdc43 100644 --- a/structurizr-import/build.gradle +++ b/structurizr-import/build.gradle @@ -2,7 +2,7 @@ dependencies { api project(':structurizr-core') - testImplementation 'org.junit.jupiter:junit-jupiter:5.11.0' + testImplementation 'org.junit.jupiter:junit-jupiter:5.11.3' } diff --git a/structurizr-inspection/build.gradle b/structurizr-inspection/build.gradle index 25ca05b8e..bf3f60229 100644 --- a/structurizr-inspection/build.gradle +++ b/structurizr-inspection/build.gradle @@ -3,6 +3,6 @@ dependencies { api project(':structurizr-core') testImplementation project(':structurizr-dsl') - testImplementation 'org.junit.jupiter:junit-jupiter:5.11.0' + testImplementation 'org.junit.jupiter:junit-jupiter:5.11.3' } \ No newline at end of file diff --git a/structurizr-neo4j/build.gradle b/structurizr-neo4j/build.gradle index a8990a997..f6518e3c2 100644 --- a/structurizr-neo4j/build.gradle +++ b/structurizr-neo4j/build.gradle @@ -1,9 +1,9 @@ dependencies { api project(':structurizr-core') - implementation 'org.neo4j.driver:neo4j-java-driver:5.24.0' + implementation 'org.neo4j.driver:neo4j-java-driver:5.26.1' testImplementation project(':structurizr-client') - testImplementation 'org.junit.jupiter:junit-jupiter:5.11.0' + testImplementation 'org.junit.jupiter:junit-jupiter:5.11.3' } \ No newline at end of file From b26d5b1abf02c44f6a7622848c7f09454301bb36 Mon Sep 17 00:00:00 2001 From: Maksim Beliaev Date: Mon, 18 Nov 2024 17:25:31 +0100 Subject: [PATCH 603/717] Update README.md for plantuml exporter Removed the referenced repository that is archived and deprecated. --- .../src/main/java/com/structurizr/export/plantuml/README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/structurizr-export/src/main/java/com/structurizr/export/plantuml/README.md b/structurizr-export/src/main/java/com/structurizr/export/plantuml/README.md index 616e11ca9..8474c6296 100644 --- a/structurizr-export/src/main/java/com/structurizr/export/plantuml/README.md +++ b/structurizr-export/src/main/java/com/structurizr/export/plantuml/README.md @@ -2,6 +2,4 @@ There are two PlantUML exporters in this package - [StructurizrPlantUMLExporter](StructurizrPlantUMLExporter.java) and [C4PlantUMLExporter](C4PlantUMLExporter.java). -If neither of these provide the features you are looking for, an alternative PlantUML exporter can be found at [https://github.com/cloudflightio/structurizr-export-c4plantuml](https://github.com/cloudflightio/structurizr-export-c4plantuml). - -See https://docs.structurizr.com/export for more. \ No newline at end of file +See https://docs.structurizr.com/export for more. From 69b7d399ae8d3c57bcc88a948baeef535cd062db Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Sat, 23 Nov 2024 09:35:29 +0000 Subject: [PATCH 604/717] structurizr-dsl: Adds support for `element!=` expressions. --- build.gradle | 2 +- changelog.md | 4 ++ .../com/structurizr/dsl/ExpressionParser.java | 47 +++++++++++++------ .../dsl/StructurizrDslExpressions.java | 1 + .../dsl/ExpressionParserTests.java | 43 +++++++++++++++++ 5 files changed, 82 insertions(+), 15 deletions(-) diff --git a/build.gradle b/build.gradle index 21ae1b226..96d8f743e 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '3.1.0' + version = '3.2.0' repositories { mavenCentral() diff --git a/changelog.md b/changelog.md index 62e8b3181..3ac74faf8 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 3.2.0 (unreleased) + +- structurizr-dsl: Adds support for `element!=` expressions. + ## 3.1.0 (4th November 2024) - structurizr-client: Workspace archive file now includes the branch name in the filename. diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ExpressionParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ExpressionParser.java index cac370945..31c39eb98 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ExpressionParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ExpressionParser.java @@ -18,21 +18,22 @@ static boolean isExpression(String token) { token = token.toLowerCase(); return + token.startsWith(ELEMENT_EQUALS_EXPRESSION.toLowerCase()) || + token.startsWith(ELEMENT_NOT_EQUALS_EXPRESSION.toLowerCase()) || token.startsWith(ELEMENT_TYPE_EQUALS_EXPRESSION.toLowerCase()) || - token.startsWith(ELEMENT_TAG_EQUALS_EXPRESSION.toLowerCase()) || - token.startsWith(ELEMENT_TAG_NOT_EQUALS_EXPRESSION.toLowerCase()) || - token.startsWith(ELEMENT_TECHNOLOGY_EQUALS_EXPRESSION.toLowerCase()) || - token.startsWith(ELEMENT_TECHNOLOGY_NOT_EQUALS_EXPRESSION.toLowerCase()) || - token.matches(ELEMENT_PROPERTY_EQUALS_EXPRESSION) || - token.startsWith(ELEMENT_PARENT_EQUALS_EXPRESSION.toLowerCase()) || - token.startsWith(RELATIONSHIP) || token.endsWith(RELATIONSHIP) || token.contains(RELATIONSHIP) || - token.startsWith(ELEMENT_EQUALS_EXPRESSION) || - token.startsWith(RELATIONSHIP_TAG_EQUALS_EXPRESSION.toLowerCase()) || - token.startsWith(RELATIONSHIP_TAG_NOT_EQUALS_EXPRESSION.toLowerCase()) || - token.matches(RELATIONSHIP_PROPERTY_EQUALS_EXPRESSION) || - token.startsWith(RELATIONSHIP_SOURCE_EQUALS_EXPRESSION.toLowerCase()) || - token.startsWith(RELATIONSHIP_DESTINATION_EQUALS_EXPRESSION.toLowerCase()) || - token.startsWith(RELATIONSHIP_EQUALS_EXPRESSION); + token.startsWith(ELEMENT_TAG_EQUALS_EXPRESSION.toLowerCase()) || + token.startsWith(ELEMENT_TAG_NOT_EQUALS_EXPRESSION.toLowerCase()) || + token.startsWith(ELEMENT_TECHNOLOGY_EQUALS_EXPRESSION.toLowerCase()) || + token.startsWith(ELEMENT_TECHNOLOGY_NOT_EQUALS_EXPRESSION.toLowerCase()) || + token.matches(ELEMENT_PROPERTY_EQUALS_EXPRESSION) || + token.startsWith(ELEMENT_PARENT_EQUALS_EXPRESSION.toLowerCase()) || + token.startsWith(RELATIONSHIP) || token.endsWith(RELATIONSHIP) || token.contains(RELATIONSHIP) || + token.startsWith(RELATIONSHIP_TAG_EQUALS_EXPRESSION.toLowerCase()) || + token.startsWith(RELATIONSHIP_TAG_NOT_EQUALS_EXPRESSION.toLowerCase()) || + token.matches(RELATIONSHIP_PROPERTY_EQUALS_EXPRESSION) || + token.startsWith(RELATIONSHIP_SOURCE_EQUALS_EXPRESSION.toLowerCase()) || + token.startsWith(RELATIONSHIP_DESTINATION_EQUALS_EXPRESSION.toLowerCase()) || + token.startsWith(RELATIONSHIP_EQUALS_EXPRESSION); } @@ -71,6 +72,24 @@ private Set evaluateExpression(String expr, DslContext context) { } else { modelItems.addAll(parseIdentifier(expr, context)); } + } else if (expr.startsWith(ELEMENT_NOT_EQUALS_EXPRESSION)) { + expr = expr.substring(ELEMENT_NOT_EQUALS_EXPRESSION.length()); + + if (isExpression(expr)) { + Set mi = evaluateExpression(expr, context); + context.getWorkspace().getModel().getElements().forEach(element -> { + if (!mi.contains(element)) { + modelItems.add(element); + } + }); + } else { + Set mi = parseIdentifier(expr, context); + context.getWorkspace().getModel().getElements().forEach(element -> { + if (!mi.contains(element)) { + modelItems.add(element); + } + }); + } } else if (expr.startsWith(RELATIONSHIP_EQUALS_EXPRESSION)) { expr = expr.substring(RELATIONSHIP_EQUALS_EXPRESSION.length()); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslExpressions.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslExpressions.java index a10aa0924..ba9ae26fb 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslExpressions.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslExpressions.java @@ -10,6 +10,7 @@ class StructurizrDslExpressions { static final String ELEMENT_PROPERTY_EQUALS_EXPRESSION = "element\\.properties\\[.*]==.*"; static final String ELEMENT_EQUALS_EXPRESSION = "element=="; + static final String ELEMENT_NOT_EQUALS_EXPRESSION = "element!="; static final String ELEMENT_PARENT_EQUALS_EXPRESSION = "element.parent=="; static final String RELATIONSHIP_TAG_EQUALS_EXPRESSION = "relationship.tag=="; diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ExpressionParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ExpressionParserTests.java index fcf91a830..83bb4a3ab 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ExpressionParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ExpressionParserTests.java @@ -541,4 +541,47 @@ void test_parseExpression_ReturnsRelationshipsAndImpliedRelationships_WhenUsingA assertTrue(relationships.contains(impliedRelationship)); } + @Test + void test_parseExpression_ReturnsElements_WhenUsingElementNotEqualsIdentifier() { + SoftwareSystem a = model.addSoftwareSystem("A"); + SoftwareSystem b = model.addSoftwareSystem("B"); + SoftwareSystem c = model.addSoftwareSystem("C"); + + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(null); + context.setWorkspace(workspace); + + IdentifiersRegister map = new IdentifiersRegister(); + map.register("a", a); + map.register("b", b); + map.register("c", c); + context.setIdentifierRegister(map); + + Set elements = parser.parseExpression("element!=c", context); + assertEquals(2, elements.size()); + assertTrue(elements.contains(a)); + assertTrue(elements.contains(b)); + } + + @Test + void test_parseExpression_ReturnsElements_WhenUsingElementNotEqualsExpression() { + SoftwareSystem a = model.addSoftwareSystem("A"); + SoftwareSystem b = model.addSoftwareSystem("B"); + SoftwareSystem c = model.addSoftwareSystem("C"); + Relationship aToB = a.uses(b, "Uses"); + Relationship bToC = b.uses(c, "Uses"); + + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(null); + context.setWorkspace(workspace); + + IdentifiersRegister map = new IdentifiersRegister(); + map.register("a", a); + map.register("b", b); + map.register("c", c); + context.setIdentifierRegister(map); + + Set elements = parser.parseExpression("element!=->b", context); + assertEquals(1, elements.size()); + assertTrue(elements.contains(c)); + } + } \ No newline at end of file From 5cc29d00bb8aa1801afb0b77efbd011efc26d378 Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Sat, 23 Nov 2024 10:01:53 +0000 Subject: [PATCH 605/717] structurizr-dsl: `!elements` and `!relationships` now work inside deployment environment blocks. --- changelog.md | 1 + .../main/java/com/structurizr/dsl/StructurizrDslParser.java | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index 3ac74faf8..03139f0aa 100644 --- a/changelog.md +++ b/changelog.md @@ -3,6 +3,7 @@ ## 3.2.0 (unreleased) - structurizr-dsl: Adds support for `element!=` expressions. +- structurizr-dsl: `!elements` and `!relationships` now work inside deployment environment blocks. ## 3.1.0 (4th November 2024) diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index 9581c6d5e..9f6d4af23 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -383,14 +383,14 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn } } - } else if (FIND_ELEMENTS_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ModelDslContext.class) || inContext(ElementDslContext.class))) { + } else if (FIND_ELEMENTS_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ModelDslContext.class) || inContext(DeploymentEnvironmentDslContext.class) || inContext(ElementDslContext.class))) { Set elements = new FindElementsParser().parse(getContext(), tokens.withoutContextStartToken()); if (shouldStartContext(tokens)) { startContext(new ElementsDslContext(getContext(), elements)); } - } else if (FIND_RELATIONSHIPS_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ModelDslContext.class) || inContext(ElementDslContext.class))) { + } else if (FIND_RELATIONSHIPS_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ModelDslContext.class) || inContext(DeploymentEnvironmentDslContext.class) || inContext(ElementDslContext.class))) { Set relationships = new FindRelationshipsParser().parse(getContext(), tokens.withoutContextStartToken()); if (shouldStartContext(tokens)) { From d95879f292b42a858717b73f94a2804fd2ea4349 Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Sun, 24 Nov 2024 13:02:01 +0000 Subject: [PATCH 606/717] Make version overridable from command line. --- build.gradle | 1 - gradle.properties | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 96d8f743e..536ce612a 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,6 @@ subprojects { proj -> description = 'Structurizr' group = 'com.structurizr' - version = '3.2.0' repositories { mavenCentral() diff --git a/gradle.properties b/gradle.properties index fe55ae08c..8af3d8f3b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,3 +5,4 @@ signing.secretKeyRingFile=/some/path ossrhUsername=username ossrhPassword=password +version=3.2.0 \ No newline at end of file From b6fd8cdb3e2dabddb2206409c90c5f63dfc647ab Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Sat, 30 Nov 2024 15:21:17 +0000 Subject: [PATCH 607/717] structurizr-dsl: `description` and `technology` now work inside `!elements` blocks. --- changelog.md | 1 + .../structurizr/dsl/ElementsDslContext.java | 2 + .../com/structurizr/dsl/ElementsParser.java | 50 +++++++++++++ .../structurizr/dsl/StructurizrDslParser.java | 6 ++ .../structurizr/dsl/ElementsParserTests.java | 70 +++++++++++++++++++ 5 files changed, 129 insertions(+) create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ElementsParser.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/ElementsParserTests.java diff --git a/changelog.md b/changelog.md index 03139f0aa..cd6faebbf 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,7 @@ - structurizr-dsl: Adds support for `element!=` expressions. - structurizr-dsl: `!elements` and `!relationships` now work inside deployment environment blocks. +- structurizr-dsl: `description` and `technology` now work inside `!elements` blocks. ## 3.1.0 (4th November 2024) diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementsDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementsDslContext.java index 7e056c159..3ef8706e3 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementsDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementsDslContext.java @@ -29,6 +29,8 @@ Set getModelItems() { protected String[] getPermittedTokens() { return new String[] { StructurizrDslTokens.RELATIONSHIP_TOKEN, + StructurizrDslTokens.DESCRIPTION_TOKEN, + StructurizrDslTokens.TECHNOLOGY_TOKEN, StructurizrDslTokens.TAG_TOKEN, StructurizrDslTokens.TAGS_TOKEN, StructurizrDslTokens.URL_TOKEN, diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementsParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementsParser.java new file mode 100644 index 000000000..b2862307e --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementsParser.java @@ -0,0 +1,50 @@ +package com.structurizr.dsl; + +import com.structurizr.model.*; + +final class ElementsParser extends AbstractParser { + + private final static int DESCRIPTION_INDEX = 1; + private final static int TECHNOLOGY_INDEX = 1; + + void parseDescription(ElementsDslContext context, Tokens tokens) { + // description + if (tokens.hasMoreThan(DESCRIPTION_INDEX)) { + throw new RuntimeException("Too many tokens, expected: description "); + } + + if (!tokens.includes(DESCRIPTION_INDEX)) { + throw new RuntimeException("Expected: description "); + } + + String description = tokens.get(DESCRIPTION_INDEX); + for (Element element : context.getElements()) { + element.setDescription(description); + } + } + + void parseTechnology(ElementsDslContext context, Tokens tokens) { + // technology + if (tokens.hasMoreThan(TECHNOLOGY_INDEX)) { + throw new RuntimeException("Too many tokens, expected: technology "); + } + + if (!tokens.includes(TECHNOLOGY_INDEX)) { + throw new RuntimeException("Expected: technology "); + } + + String technology = tokens.get(TECHNOLOGY_INDEX); + for (Element element : context.getElements()) { + if (element instanceof Container) { + ((Container)element).setTechnology(technology); + } else if (element instanceof Component) { + ((Component)element).setTechnology(technology); + } else if (element instanceof DeploymentNode) { + ((DeploymentNode)element).setTechnology(technology); + } else if (element instanceof InfrastructureNode) { + ((InfrastructureNode)element).setTechnology(technology); + } + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index 9f6d4af23..f0faf6f4f 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -537,6 +537,9 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn } else if (DESCRIPTION_TOKEN.equalsIgnoreCase(firstToken) && inContext(ElementDslContext.class) && !isGroup(getContext())) { new ModelItemParser().parseDescription(getContext(ElementDslContext.class), tokens); + } else if (DESCRIPTION_TOKEN.equalsIgnoreCase(firstToken) && inContext(ElementsDslContext.class)) { + new ElementsParser().parseDescription(getContext(ElementsDslContext.class), tokens); + } else if (TECHNOLOGY_TOKEN.equalsIgnoreCase(firstToken) && inContext(ContainerDslContext.class) && !getContext(ContainerDslContext.class).hasGroup()) { new ContainerParser().parseTechnology(getContext(ContainerDslContext.class), tokens); @@ -549,6 +552,9 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn } else if (TECHNOLOGY_TOKEN.equalsIgnoreCase(firstToken) && inContext(InfrastructureNodeDslContext.class)) { new InfrastructureNodeParser().parseTechnology(getContext(InfrastructureNodeDslContext.class), tokens); + } else if (TECHNOLOGY_TOKEN.equalsIgnoreCase(firstToken) && inContext(ElementsDslContext.class)) { + new ElementsParser().parseTechnology(getContext(ElementsDslContext.class), tokens); + } else if (INSTANCES_TOKEN.equalsIgnoreCase(firstToken) && inContext(DeploymentNodeDslContext.class)) { new DeploymentNodeParser().parseInstances(getContext(DeploymentNodeDslContext.class), tokens); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementsParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementsParserTests.java new file mode 100644 index 000000000..1c1836204 --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementsParserTests.java @@ -0,0 +1,70 @@ +package com.structurizr.dsl; + +import com.structurizr.model.*; +import org.junit.jupiter.api.Test; + +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +class ElementsParserTests extends AbstractTests { + + private final ElementsParser parser = new ElementsParser(); + + @Test + void test_parseTechnology_ThrowsAnException_WhenNoTechnologyIsSpecified() { + try { + parser.parseTechnology(null, tokens("technology")); + fail(); + } catch (Exception e) { + assertEquals("Expected: technology ", e.getMessage()); + } + } + + @Test + void test_parseTechnology() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + Component component = container.addComponent("Component"); + DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node"); + InfrastructureNode infrastructureNode = deploymentNode.addInfrastructureNode("Infrastructure Node"); + + ElementsDslContext context = new ElementsDslContext(null, Set.of(softwareSystem, container, component, deploymentNode, infrastructureNode)); + + parser.parseTechnology(context, tokens("technology", "Technology")); + assertEquals("Technology", container.getTechnology()); + assertEquals("Technology", component.getTechnology()); + assertEquals("Technology", deploymentNode.getTechnology()); + assertEquals("Technology", infrastructureNode.getTechnology()); + } + + @Test + void test_parseDescription_ThrowsAnException_WhenNoDescriptionIsSpecified() { + try { + parser.parseDescription(null, tokens("description")); + fail(); + } catch (Exception e) { + assertEquals("Expected: description ", e.getMessage()); + } + } + + @Test + void test_parseDescription() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + Component component = container.addComponent("Component"); + DeploymentNode deploymentNode = model.addDeploymentNode("Deployment Node"); + InfrastructureNode infrastructureNode = deploymentNode.addInfrastructureNode("Infrastructure Node"); + + ElementsDslContext context = new ElementsDslContext(null, Set.of(softwareSystem, container, component, deploymentNode, infrastructureNode)); + + parser.parseDescription(context, tokens("description", "Description")); + assertEquals("Description", softwareSystem.getDescription()); + assertEquals("Description", container.getDescription()); + assertEquals("Description", component.getDescription()); + assertEquals("Description", deploymentNode.getDescription()); + assertEquals("Description", infrastructureNode.getDescription()); + } + +} \ No newline at end of file From a734e984457478277358d7ce80f3f534cbb54a22 Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Thu, 5 Dec 2024 15:36:22 +0000 Subject: [PATCH 608/717] Ensures restricted mode setting is propagated to spawned parsers. --- .../src/main/java/com/structurizr/dsl/DslParserContext.java | 4 ++-- .../src/main/java/com/structurizr/dsl/WorkspaceParser.java | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DslParserContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DslParserContext.java index f7f7491d8..7fd4eea7b 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/DslParserContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DslParserContext.java @@ -4,8 +4,8 @@ final class DslParserContext extends DslContext { - private boolean restricted; - private File file; + private final boolean restricted; + private final File file; DslParserContext(File file, boolean restricted) { this.file = file; diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/WorkspaceParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/WorkspaceParser.java index 7778007a2..44f11b2c7 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/WorkspaceParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/WorkspaceParser.java @@ -46,6 +46,7 @@ Workspace parse(DslParserContext context, Tokens tokens) { } else { String dsl = content.getContent(); StructurizrDslParser structurizrDslParser = new StructurizrDslParser(); + structurizrDslParser.setRestricted(context.isRestricted()); structurizrDslParser.parse(context, dsl); workspace = structurizrDslParser.getWorkspace(); } From b51c9a7c3f3709de14bcd45e795945601cb99281 Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Fri, 6 Dec 2024 09:45:14 +0000 Subject: [PATCH 609/717] Updated to reflect release. --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index cd6faebbf..a949f37e1 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,6 @@ # Changelog -## 3.2.0 (unreleased) +## 3.2.0 (6th December 2024) - structurizr-dsl: Adds support for `element!=` expressions. - structurizr-dsl: `!elements` and `!relationships` now work inside deployment environment blocks. From 0ef815a404a9ff3f3a87750709bdedce17fc9989 Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Fri, 6 Dec 2024 09:53:43 +0000 Subject: [PATCH 610/717] Bump dependency. --- structurizr-neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structurizr-neo4j/build.gradle b/structurizr-neo4j/build.gradle index f6518e3c2..aa2bcf209 100644 --- a/structurizr-neo4j/build.gradle +++ b/structurizr-neo4j/build.gradle @@ -1,7 +1,7 @@ dependencies { api project(':structurizr-core') - implementation 'org.neo4j.driver:neo4j-java-driver:5.26.1' + implementation 'org.neo4j.driver:neo4j-java-driver:5.27.0' testImplementation project(':structurizr-client') testImplementation 'org.junit.jupiter:junit-jupiter:5.11.3' From 1d6cda2334252d2f6b0aaf0fa5b9d9564c443b10 Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Tue, 10 Dec 2024 09:24:57 +0000 Subject: [PATCH 611/717] Fixes #362. --- changelog.md | 4 ++++ gradle.properties | 2 +- .../src/main/java/com/structurizr/model/Model.java | 7 +++---- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/changelog.md b/changelog.md index a949f37e1..e3a3806d7 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 3.2.1 (10th December 2024) + +- structurizr-core: Fixes https://github.com/structurizr/java/issues/362 (Ordering of replicated relationships in deployment environment is non-deterministic). + ## 3.2.0 (6th December 2024) - structurizr-dsl: Adds support for `element!=` expressions. diff --git a/gradle.properties b/gradle.properties index 8af3d8f3b..c1670d1fb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,4 +5,4 @@ signing.secretKeyRingFile=/some/path ossrhUsername=username ossrhPassword=password -version=3.2.0 \ No newline at end of file +version=3.2.1 \ No newline at end of file diff --git a/structurizr-core/src/main/java/com/structurizr/model/Model.java b/structurizr-core/src/main/java/com/structurizr/model/Model.java index 111d124c1..57d947b2d 100644 --- a/structurizr-core/src/main/java/com/structurizr/model/Model.java +++ b/structurizr-core/src/main/java/com/structurizr/model/Model.java @@ -902,12 +902,11 @@ private void replicateElementRelationships(StaticStructureElementInstance elemen StaticStructureElement element = elementInstance.getElement(); // find all StaticStructureElementInstance objects in the same deployment environment and deployment group - Set elementInstances = getElements().stream() + TreeSet elementInstances = getElements().stream() .filter(e -> e instanceof StaticStructureElementInstance) - .map(e -> (StaticStructureElementInstance)e) + .map(e -> (StaticStructureElementInstance) e) .filter(ssei -> ssei.getEnvironment().equals(elementInstance.getEnvironment())) - .filter(ssei -> ssei.inSameDeploymentGroup(elementInstance)) - .collect(Collectors.toSet()); + .filter(ssei -> ssei.inSameDeploymentGroup(elementInstance)).collect(Collectors.toCollection(TreeSet::new)); // and replicate the relationships to/from the element instance for (StaticStructureElementInstance ssei : elementInstances) { From 98371df012175b8670b586e5c72c04b35d3bda1b Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Sun, 15 Dec 2024 13:28:24 +0000 Subject: [PATCH 612/717] Initial POC re: archetypes. --- gradle.properties | 2 +- .../java/com/structurizr/dsl/Archetype.java | 66 ++++++ .../structurizr/dsl/ArchetypeDslContext.java | 15 ++ .../com/structurizr/dsl/ArchetypeParser.java | 62 +++++ .../structurizr/dsl/ArchetypesDslContext.java | 14 ++ .../dsl/ComponentArchetypeDslContext.java | 19 ++ .../dsl/ContainerArchetypeDslContext.java | 19 ++ .../DeploymentNodeArchetypeDslContext.java | 19 ++ ...InfrastructureNodeArchetypeDslContext.java | 19 ++ .../dsl/PersonArchetypeDslContext.java | 18 ++ .../SoftwareSystemArchetypeDslContext.java | 18 ++ .../structurizr/dsl/StructurizrDslParser.java | 216 ++++++++++++++++-- .../structurizr/dsl/StructurizrDslTokens.java | 1 + .../java/com/structurizr/dsl/DslTests.java | 12 + .../src/test/resources/dsl/archetypes.dsl | 39 ++++ 15 files changed, 525 insertions(+), 14 deletions(-) create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/Archetype.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ArchetypeDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ArchetypeParser.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ArchetypesDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentArchetypeDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerArchetypeDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeArchetypeDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeArchetypeDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/PersonArchetypeDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemArchetypeDslContext.java create mode 100644 structurizr-dsl/src/test/resources/dsl/archetypes.dsl diff --git a/gradle.properties b/gradle.properties index c1670d1fb..3b854bb7b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,4 +5,4 @@ signing.secretKeyRingFile=/some/path ossrhUsername=username ossrhPassword=password -version=3.2.1 \ No newline at end of file +version=4.0.0 \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/Archetype.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/Archetype.java new file mode 100644 index 000000000..18ead6aa2 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/Archetype.java @@ -0,0 +1,66 @@ +package com.structurizr.dsl; + +import com.structurizr.util.StringUtils; +import com.structurizr.util.TagUtils; + +import java.util.LinkedHashSet; +import java.util.Set; + +final class Archetype { + + private final String name; + private final String type; + private String description; + private String technology; + private final Set tags = new LinkedHashSet<>(); + + Archetype(String name, String type) { + if (StringUtils.isNullOrEmpty(name)) { + name = type; + } + + this.name = name.toLowerCase(); + this.type = type; + } + + String getName() { + return name; + } + + String getType() { + return type; + } + + String getDescription() { + return description; + } + + void setDescription(String description) { + this.description = description; + } + + String getTechnology() { + return technology; + } + + void setTechnology(String technology) { + this.technology = technology; + } + + void addTags(String... tags) { + if (tags == null) { + return; + } + + for (String tag : tags) { + if (tag != null) { + this.tags.add(tag.trim()); + } + } + } + + Set getTags() { + return new LinkedHashSet<>(tags); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ArchetypeDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ArchetypeDslContext.java new file mode 100644 index 000000000..0f1dbb71f --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ArchetypeDslContext.java @@ -0,0 +1,15 @@ +package com.structurizr.dsl; + +abstract class ArchetypeDslContext extends DslContext { + + private final Archetype archetype; + + ArchetypeDslContext(Archetype archetype) { + this.archetype = archetype; + } + + Archetype getArchetype() { + return archetype; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ArchetypeParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ArchetypeParser.java new file mode 100644 index 000000000..3c78a1ba6 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ArchetypeParser.java @@ -0,0 +1,62 @@ +package com.structurizr.dsl; + +final class ArchetypeParser extends AbstractParser { + + private final static int NAME_INDEX = 1; + private final static int VALUE_INDEX = 1; + + void parseTag(ArchetypeDslContext context, Tokens tokens) { + // tag + if (tokens.hasMoreThan(VALUE_INDEX)) { + throw new RuntimeException("Too many tokens, expected: tag "); + } + + if (!tokens.includes(NAME_INDEX)) { + throw new RuntimeException("Expected: tag "); + } + + String tag = tokens.get(VALUE_INDEX); + context.getArchetype().addTags(tag); + } + + void parseTags(ArchetypeDslContext context, Tokens tokens) { + // tags [tags] + if (!tokens.includes(NAME_INDEX)) { + throw new RuntimeException("Expected: tags [tags]"); + } + + for (int i = NAME_INDEX; i < tokens.size(); i++) { + String tags = tokens.get(i); + context.getArchetype().addTags(tags.split(",")); + } + } + + void parseDescription(ArchetypeDslContext context, Tokens tokens) { + // description + if (tokens.hasMoreThan(VALUE_INDEX)) { + throw new RuntimeException("Too many tokens, expected: description "); + } + + if (!tokens.includes(NAME_INDEX)) { + throw new RuntimeException("Expected: description "); + } + + String description = tokens.get(VALUE_INDEX); + context.getArchetype().setDescription(description); + } + + void parseTechnology(ArchetypeDslContext context, Tokens tokens) { + // technology + if (tokens.hasMoreThan(VALUE_INDEX)) { + throw new RuntimeException("Too many tokens, expected: technology "); + } + + if (!tokens.includes(NAME_INDEX)) { + throw new RuntimeException("Expected: technology "); + } + + String technology = tokens.get(VALUE_INDEX); + context.getArchetype().setTechnology(technology); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ArchetypesDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ArchetypesDslContext.java new file mode 100644 index 000000000..d457913f7 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ArchetypesDslContext.java @@ -0,0 +1,14 @@ +package com.structurizr.dsl; + +final class ArchetypesDslContext extends DslContext { + + ArchetypesDslContext() { + } + + @Override + protected String[] getPermittedTokens() { + return new String[] { + }; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentArchetypeDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentArchetypeDslContext.java new file mode 100644 index 000000000..d361f92e7 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentArchetypeDslContext.java @@ -0,0 +1,19 @@ +package com.structurizr.dsl; + +final class ComponentArchetypeDslContext extends ArchetypeDslContext { + + ComponentArchetypeDslContext(Archetype archetype) { + super(archetype); + } + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.DESCRIPTION_TOKEN, + StructurizrDslTokens.TECHNOLOGY_TOKEN, + StructurizrDslTokens.TAG_TOKEN, + StructurizrDslTokens.TAGS_TOKEN, + }; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerArchetypeDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerArchetypeDslContext.java new file mode 100644 index 000000000..3bd97646a --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerArchetypeDslContext.java @@ -0,0 +1,19 @@ +package com.structurizr.dsl; + +final class ContainerArchetypeDslContext extends ArchetypeDslContext { + + ContainerArchetypeDslContext(Archetype archetype) { + super(archetype); + } + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.DESCRIPTION_TOKEN, + StructurizrDslTokens.TECHNOLOGY_TOKEN, + StructurizrDslTokens.TAG_TOKEN, + StructurizrDslTokens.TAGS_TOKEN, + }; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeArchetypeDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeArchetypeDslContext.java new file mode 100644 index 000000000..895cb9a3d --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeArchetypeDslContext.java @@ -0,0 +1,19 @@ +package com.structurizr.dsl; + +final class DeploymentNodeArchetypeDslContext extends ArchetypeDslContext { + + DeploymentNodeArchetypeDslContext(Archetype archetype) { + super(archetype); + } + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.DESCRIPTION_TOKEN, + StructurizrDslTokens.TECHNOLOGY_TOKEN, + StructurizrDslTokens.TAG_TOKEN, + StructurizrDslTokens.TAGS_TOKEN, + }; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeArchetypeDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeArchetypeDslContext.java new file mode 100644 index 000000000..b5023744b --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeArchetypeDslContext.java @@ -0,0 +1,19 @@ +package com.structurizr.dsl; + +final class InfrastructureNodeArchetypeDslContext extends ArchetypeDslContext { + + InfrastructureNodeArchetypeDslContext(Archetype archetype) { + super(archetype); + } + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.DESCRIPTION_TOKEN, + StructurizrDslTokens.TECHNOLOGY_TOKEN, + StructurizrDslTokens.TAG_TOKEN, + StructurizrDslTokens.TAGS_TOKEN, + }; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonArchetypeDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonArchetypeDslContext.java new file mode 100644 index 000000000..a095ff456 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonArchetypeDslContext.java @@ -0,0 +1,18 @@ +package com.structurizr.dsl; + +final class PersonArchetypeDslContext extends ArchetypeDslContext { + + PersonArchetypeDslContext(Archetype archetype) { + super(archetype); + } + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.DESCRIPTION_TOKEN, + StructurizrDslTokens.TAG_TOKEN, + StructurizrDslTokens.TAGS_TOKEN, + }; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemArchetypeDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemArchetypeDslContext.java new file mode 100644 index 000000000..8df3f4f60 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemArchetypeDslContext.java @@ -0,0 +1,18 @@ +package com.structurizr.dsl; + +final class SoftwareSystemArchetypeDslContext extends ArchetypeDslContext { + + SoftwareSystemArchetypeDslContext(Archetype archetype) { + super(archetype); + } + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.DESCRIPTION_TOKEN, + StructurizrDslTokens.TAG_TOKEN, + StructurizrDslTokens.TAGS_TOKEN, + }; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index f0faf6f4f..32b75a186 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -4,6 +4,7 @@ import com.structurizr.Workspace; import com.structurizr.model.*; import com.structurizr.util.StringUtils; +import com.structurizr.util.TagUtils; import com.structurizr.view.*; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -45,6 +46,17 @@ public final class StructurizrDslParser extends StructurizrDslTokens { private final IdentifiersRegister identifiersRegister; private final Map constantsAndVariables; + private boolean archetypesEnabled = false; + private final Map> archetypes = Map.of( + StructurizrDslTokens.GROUP_TOKEN, new HashMap<>(), + StructurizrDslTokens.PERSON_TOKEN, new HashMap<>(), + StructurizrDslTokens.SOFTWARE_SYSTEM_TOKEN, new HashMap<>(), + StructurizrDslTokens.CONTAINER_TOKEN, new HashMap<>(), + StructurizrDslTokens.COMPONENT_TOKEN, new HashMap<>(), + StructurizrDslTokens.DEPLOYMENT_NODE_TOKEN, new HashMap<>(), + StructurizrDslTokens.INFRASTRUCTURE_NODE_TOKEN, new HashMap<>() + ); + private final List dslSourceLines = new ArrayList<>(); private Workspace workspace; private boolean extendingWorkspace = false; @@ -218,7 +230,7 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn Tokens tokens = new Tokens(listOfTokens); String identifier = null; - if (tokens.size() > 3 && ASSIGNMENT_OPERATOR_TOKEN.equals(tokens.get(1))) { + if (tokens.size() >= 3 && ASSIGNMENT_OPERATOR_TOKEN.equals(tokens.get(1))) { identifier = tokens.get(0); identifiersRegister.validateIdentifierName(identifier); @@ -406,8 +418,11 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn registerIdentifier(identifier, customElement); - } else if (PERSON_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ModelDslContext.class))) { + } else if (isElementKeywordOrArchetype(firstToken, PERSON_TOKEN) && (inContext(ModelDslContext.class))) { Person person = new PersonParser().parse(getContext(ModelDslContext.class), tokens.withoutContextStartToken()); + if (archetypesEnabled) { + applyArchetype(firstToken, person); + } if (shouldStartContext(tokens)) { startContext(new PersonDslContext(person)); @@ -415,8 +430,11 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn registerIdentifier(identifier, person); - } else if (SOFTWARE_SYSTEM_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ModelDslContext.class))) { + } else if (isElementKeywordOrArchetype(firstToken, SOFTWARE_SYSTEM_TOKEN) && (inContext(ModelDslContext.class))) { SoftwareSystem softwareSystem = new SoftwareSystemParser().parse(getContext(ModelDslContext.class), tokens.withoutContextStartToken()); + if (archetypesEnabled) { + applyArchetype(firstToken, softwareSystem); + } if (shouldStartContext(tokens)) { startContext(new SoftwareSystemDslContext(softwareSystem)); @@ -424,8 +442,11 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn registerIdentifier(identifier, softwareSystem); - } else if (CONTAINER_TOKEN.equalsIgnoreCase(firstToken) && inContext(SoftwareSystemDslContext.class)) { + } else if (isElementKeywordOrArchetype(firstToken, CONTAINER_TOKEN) && inContext(SoftwareSystemDslContext.class)) { Container container = new ContainerParser().parse(getContext(SoftwareSystemDslContext.class), tokens.withoutContextStartToken()); + if (archetypesEnabled) { + applyArchetype(firstToken, container); + } if (shouldStartContext(tokens)) { startContext(new ContainerDslContext(container)); @@ -433,8 +454,11 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn registerIdentifier(identifier, container); - } else if (COMPONENT_TOKEN.equalsIgnoreCase(firstToken) && inContext(ContainerDslContext.class)) { + } else if (isElementKeywordOrArchetype(firstToken, COMPONENT_TOKEN) && inContext(ContainerDslContext.class)) { Component component = new ComponentParser().parse(getContext(ContainerDslContext.class), tokens.withoutContextStartToken()); + if (archetypesEnabled) { + applyArchetype(firstToken, component); + } if (shouldStartContext(tokens)) { startContext(new ComponentDslContext(component)); @@ -497,32 +521,32 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn } else if (ENTERPRISE_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelDslContext.class)) { throw new RuntimeException("The enterprise keyword was previously deprecated, and has now been removed - please use group instead (https://docs.structurizr.com/dsl/language#group)"); - } else if (GROUP_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelDslContext.class)) { + } else if (isElementKeywordOrArchetype(firstToken, GROUP_TOKEN) && inContext(ModelDslContext.class)) { ElementGroup group = new GroupParser().parse(getContext(ModelDslContext.class), tokens); startContext(new ModelDslContext(group)); registerIdentifier(identifier, group); - } else if (GROUP_TOKEN.equalsIgnoreCase(firstToken) && inContext(SoftwareSystemDslContext.class)) { + } else if (isElementKeywordOrArchetype(firstToken, GROUP_TOKEN) && inContext(SoftwareSystemDslContext.class)) { ElementGroup group = new GroupParser().parse(getContext(SoftwareSystemDslContext.class), tokens); SoftwareSystem softwareSystem = getContext(SoftwareSystemDslContext.class).getSoftwareSystem(); group.setParent(softwareSystem); startContext(new SoftwareSystemDslContext(softwareSystem, group)); registerIdentifier(identifier, group); - } else if (GROUP_TOKEN.equalsIgnoreCase(firstToken) && inContext(ContainerDslContext.class)) { + } else if (isElementKeywordOrArchetype(firstToken, GROUP_TOKEN) && inContext(ContainerDslContext.class)) { ElementGroup group = new GroupParser().parse(getContext(ContainerDslContext.class), tokens); Container container = getContext(ContainerDslContext.class).getContainer(); group.setParent(container); startContext(new ContainerDslContext(container, group)); registerIdentifier(identifier, group); - } else if (GROUP_TOKEN.equalsIgnoreCase(firstToken) && inContext(DeploymentEnvironmentDslContext.class)) { + } else if (isElementKeywordOrArchetype(firstToken, GROUP_TOKEN) && inContext(DeploymentEnvironmentDslContext.class)) { ElementGroup group = new GroupParser().parse(getContext(DeploymentEnvironmentDslContext.class), tokens); String environment = getContext(DeploymentEnvironmentDslContext.class).getEnvironment(); startContext(new DeploymentEnvironmentDslContext(environment, group)); registerIdentifier(identifier, group); - } else if (GROUP_TOKEN.equalsIgnoreCase(firstToken) && inContext(DeploymentNodeDslContext.class)) { + } else if (isElementKeywordOrArchetype(firstToken, GROUP_TOKEN) && inContext(DeploymentNodeDslContext.class)) { ElementGroup group = new GroupParser().parse(getContext(DeploymentNodeDslContext.class), tokens); DeploymentNode deploymentNode = getContext(DeploymentNodeDslContext.class).getDeploymentNode(); @@ -634,6 +658,81 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn startContext(new ModelDslContext()); parsedTokens.add(MODEL_TOKEN); + } else if (ARCHETYPES_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelDslContext.class) && archetypesEnabled) { + startContext(new ArchetypesDslContext()); + + } else if (isElementKeywordOrArchetype(firstToken, GROUP_TOKEN) && inContext(ArchetypesDslContext.class)) { + Archetype archetype = new Archetype(identifier, GROUP_TOKEN); + extendArchetype(archetype, firstToken); + addArchetype(archetype); + + } else if (isElementKeywordOrArchetype(firstToken, PERSON_TOKEN) && inContext(ArchetypesDslContext.class)) { + Archetype archetype = new Archetype(identifier, PERSON_TOKEN); + extendArchetype(archetype, firstToken); + addArchetype(archetype); + + + if (shouldStartContext(tokens)) { + startContext(new PersonArchetypeDslContext(archetype)); + } + + } else if (isElementKeywordOrArchetype(firstToken, SOFTWARE_SYSTEM_TOKEN) && inContext(ArchetypesDslContext.class)) { + Archetype archetype = new Archetype(identifier, SOFTWARE_SYSTEM_TOKEN); + extendArchetype(archetype, firstToken); + addArchetype(archetype); + + if (shouldStartContext(tokens)) { + startContext(new SoftwareSystemArchetypeDslContext(archetype)); + } + + } else if (isElementKeywordOrArchetype(firstToken, CONTAINER_TOKEN) && inContext(ArchetypesDslContext.class)) { + Archetype archetype = new Archetype(identifier, CONTAINER_TOKEN); + extendArchetype(archetype, firstToken); + addArchetype(archetype); + + if (shouldStartContext(tokens)) { + startContext(new ContainerArchetypeDslContext(archetype)); + } + + } else if (isElementKeywordOrArchetype(firstToken, COMPONENT_TOKEN) && inContext(ArchetypesDslContext.class)) { + Archetype archetype = new Archetype(identifier, COMPONENT_TOKEN); + extendArchetype(archetype, firstToken); + addArchetype(archetype); + + if (shouldStartContext(tokens)) { + startContext(new ComponentArchetypeDslContext(archetype)); + } + + } else if (isElementKeywordOrArchetype(firstToken, DEPLOYMENT_NODE_TOKEN) && inContext(ArchetypesDslContext.class)) { + Archetype archetype = new Archetype(identifier, DEPLOYMENT_NODE_TOKEN); + extendArchetype(archetype, firstToken); + addArchetype(archetype); + + if (shouldStartContext(tokens)) { + startContext(new DeploymentNodeArchetypeDslContext(archetype)); + } + + } else if (isElementKeywordOrArchetype(firstToken, INFRASTRUCTURE_NODE_TOKEN) && inContext(ArchetypesDslContext.class)) { + Archetype archetype = new Archetype(identifier, INFRASTRUCTURE_NODE_TOKEN); + extendArchetype(archetype, firstToken); + addArchetype(archetype); + + if (shouldStartContext(tokens)) { + startContext(new InfrastructureNodeArchetypeDslContext(archetype)); + } + + } else if (DESCRIPTION_TOKEN.equalsIgnoreCase(firstToken) && inContext(ArchetypeDslContext.class)) { + new ArchetypeParser().parseDescription(getContext(ArchetypeDslContext.class), tokens); + + } else if (TECHNOLOGY_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ContainerArchetypeDslContext.class) || inContext(ComponentArchetypeDslContext.class) || inContext(DeploymentNodeArchetypeDslContext.class) || inContext(InfrastructureNodeArchetypeDslContext.class))) { + new ArchetypeParser().parseTechnology(getContext(ArchetypeDslContext.class), tokens); + + } else if (TAG_TOKEN.equalsIgnoreCase(firstToken) && inContext(ArchetypeDslContext.class)) { + new ArchetypeParser().parseTag(getContext(ArchetypeDslContext.class), tokens); + + } else if (TAGS_TOKEN.equalsIgnoreCase(firstToken) && inContext(ArchetypeDslContext.class)) { + new ArchetypeParser().parseTags(getContext(ArchetypeDslContext.class), tokens); + } else if (VIEWS_TOKEN.equalsIgnoreCase(firstToken) && inContext(WorkspaceDslContext.class)) { if (parsedTokens.contains(VIEWS_TOKEN)) { throw new RuntimeException("Multiple view sets are not permitted in a DSL definition"); @@ -742,24 +841,33 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn registerIdentifier(identifier, new DeploymentGroup(group)); - } else if (DEPLOYMENT_NODE_TOKEN.equalsIgnoreCase(firstToken) && inContext(DeploymentEnvironmentDslContext.class)) { + } else if (isElementKeywordOrArchetype(firstToken, DEPLOYMENT_NODE_TOKEN) && inContext(DeploymentEnvironmentDslContext.class)) { DeploymentNode deploymentNode = new DeploymentNodeParser().parse(getContext(DeploymentEnvironmentDslContext.class), tokens.withoutContextStartToken()); + if (archetypesEnabled) { + applyArchetype(firstToken, deploymentNode); + } if (shouldStartContext(tokens)) { startContext(new DeploymentNodeDslContext(deploymentNode)); } registerIdentifier(identifier, deploymentNode); - } else if (DEPLOYMENT_NODE_TOKEN.equalsIgnoreCase(firstToken) && inContext(DeploymentNodeDslContext.class)) { + } else if (isElementKeywordOrArchetype(firstToken, DEPLOYMENT_NODE_TOKEN) && inContext(DeploymentNodeDslContext.class)) { DeploymentNode deploymentNode = new DeploymentNodeParser().parse(getContext(DeploymentNodeDslContext.class), tokens.withoutContextStartToken()); + if (archetypesEnabled) { + applyArchetype(firstToken, deploymentNode); + } if (shouldStartContext(tokens)) { startContext(new DeploymentNodeDslContext(deploymentNode)); } registerIdentifier(identifier, deploymentNode); - } else if (INFRASTRUCTURE_NODE_TOKEN.equalsIgnoreCase(firstToken) && inContext(DeploymentNodeDslContext.class)) { + } else if (isElementKeywordOrArchetype(firstToken, INFRASTRUCTURE_NODE_TOKEN) && inContext(DeploymentNodeDslContext.class)) { InfrastructureNode infrastructureNode = new InfrastructureNodeParser().parse(getContext(DeploymentNodeDslContext.class), tokens.withoutContextStartToken()); + if (archetypesEnabled) { + applyArchetype(firstToken, infrastructureNode); + } if (shouldStartContext(tokens)) { startContext(new InfrastructureNodeDslContext(infrastructureNode)); @@ -1184,6 +1292,88 @@ private boolean isGroup(DslContext context) { return false; } + public void setArchetypesEnabled(boolean archetypesEnabled) { + this.archetypesEnabled = archetypesEnabled; + } + + private boolean isElementKeywordOrArchetype(String token, String keyword) { + if (archetypesEnabled) { + if (token.equalsIgnoreCase(keyword)) { + return true; + } else { + return (archetypes.get(keyword).containsKey(token.toLowerCase())); + } + } else { + return token.equalsIgnoreCase(keyword); + } + } + + private void addArchetype(Archetype archetype) { + archetypes.get(archetype.getType()).put(archetype.getName(), archetype); + } + + private void extendArchetype(Archetype archetype, String archetypeName) { + archetypeName = archetypeName.toLowerCase(); + Archetype parentArchetype = archetypes.get(archetype.getType()).get(archetypeName); + if (parentArchetype != null) { + archetype.setDescription(parentArchetype.getDescription()); + archetype.setTechnology(parentArchetype.getTechnology()); + archetype.addTags(parentArchetype.getTags().toArray(new String[0])); + } + } + + private void applyArchetype(String archetypeName, Person person) { + Archetype archetype = archetypes.get(StructurizrDslTokens.PERSON_TOKEN).get(archetypeName); + if (archetype != null) { + person.setDescription(archetype.getDescription()); + person.addTags(archetype.getTags().toArray(new String[0])); + } + } + + private void applyArchetype(String archetypeName, SoftwareSystem softwareSystem) { + Archetype archetype = archetypes.get(StructurizrDslTokens.SOFTWARE_SYSTEM_TOKEN).get(archetypeName); + if (archetype != null) { + softwareSystem.setDescription(archetype.getDescription()); + softwareSystem.addTags(archetype.getTags().toArray(new String[0])); + } + } + + private void applyArchetype(String archetypeName, Container container) { + Archetype archetype = archetypes.get(StructurizrDslTokens.CONTAINER_TOKEN).get(archetypeName); + if (archetype != null) { + container.setTechnology(archetype.getTechnology()); + container.setDescription(archetype.getDescription()); + container.addTags(archetype.getTags().toArray(new String[0])); + } + } + + private void applyArchetype(String archetypeName, Component component) { + Archetype archetype = archetypes.get(StructurizrDslTokens.COMPONENT_TOKEN).get(archetypeName); + if (archetype != null) { + component.setTechnology(archetype.getTechnology()); + component.setDescription(archetype.getDescription()); + component.addTags(archetype.getTags().toArray(new String[0])); + } + } + + private void applyArchetype(String archetypeName, DeploymentNode deploymentNode) { + Archetype archetype = archetypes.get(StructurizrDslTokens.DEPLOYMENT_NODE_TOKEN).get(archetypeName); + if (archetype != null) { + deploymentNode.setTechnology(archetype.getTechnology()); + deploymentNode.setDescription(archetype.getDescription()); + deploymentNode.addTags(archetype.getTags().toArray(new String[0])); + } + } + + private void applyArchetype(String archetypeName, InfrastructureNode infrastructureNode) { + Archetype archetype = archetypes.get(StructurizrDslTokens.INFRASTRUCTURE_NODE_TOKEN).get(archetypeName); + if (archetype != null) { + infrastructureNode.setTechnology(archetype.getTechnology()); + infrastructureNode.setDescription(archetype.getDescription()); + infrastructureNode.addTags(archetype.getTags().toArray(new String[0])); + } + } + /** * Gets the identifier register in use (this is the mapping of DSL identifiers to elements/relationships). * diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java index e17ad379a..f44de980b 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java @@ -27,6 +27,7 @@ class StructurizrDslTokens { static final String EXTENDS_TOKEN = "extends"; static final String SCOPE_TOKEN = "scope"; static final String MODEL_TOKEN = "model"; + static final String ARCHETYPES_TOKEN = "archetypes"; static final String VIEWS_TOKEN = "views"; static final String ENTERPRISE_TOKEN = "enterprise"; static final String DEPLOYMENT_ENVIRONMENT_TOKEN = "deploymentEnvironment"; diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index ec37f2afd..c9548254c 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -4,6 +4,7 @@ import com.structurizr.documentation.Section; import com.structurizr.model.*; import com.structurizr.util.StringUtils; +import com.structurizr.util.WorkspaceUtils; import com.structurizr.view.*; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -1442,4 +1443,15 @@ void test_sourceIsNotRetained() throws Exception { assertNull(workspace.getProperties().get(DslUtils.STRUCTURIZR_DSL_PROPERTY_NAME)); } + @Test + void test_archetypes() throws Exception { + File parentDslFile = new File("src/test/resources/dsl/archetypes.dsl"); + StructurizrDslParser parser = new StructurizrDslParser(); + parser.setArchetypesEnabled(true); + parser.parse(parentDslFile); + Workspace workspace = parser.getWorkspace(); + + WorkspaceUtils.printWorkspaceAsJson(workspace); + } + } \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/archetypes.dsl b/structurizr-dsl/src/test/resources/dsl/archetypes.dsl new file mode 100644 index 000000000..1ad2d6089 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/archetypes.dsl @@ -0,0 +1,39 @@ +workspace { + + model { + archetypes { + application = container + datastore = container + + microservice = group + + springBootApplication = application { + technology "Spring Boot" + tags "Spring Boot" + } + + restController = component { + technology "Spring MVC REST Controller" + tag "Spring MVC REST Controller" + } + repository = component { + technology "Spring Data Repository" + tag "Spring Data Repository" + } + } + + x = softwareSystem "X" { + customerService = microservice "Customer Service" { + db = datastore "Customer database" + api = springBootApplication "Customer API" { + customerController = restController "Customer Controller" + customerRepository = repository "Customer Repository" { + customerController -> this + this -> db + } + } + } + } + } + +} \ No newline at end of file From 11ff1e804ba23e69f89e3e6854a94bfe37a2d003 Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Wed, 8 Jan 2025 10:27:27 +0000 Subject: [PATCH 613/717] Adds a way to more easily enable/disable features; fixes tests. --- .../java/com/structurizr/util/Features.java | 26 ++++++++++++++ .../java/com/structurizr/dsl/Features.java | 7 ++++ .../structurizr/dsl/StructurizrDslParser.java | 36 +++++++++---------- .../java/com/structurizr/dsl/DslTests.java | 22 ++++++++++-- .../src/test/resources/dsl/archetypes.dsl | 8 +++-- 5 files changed, 76 insertions(+), 23 deletions(-) create mode 100644 structurizr-core/src/main/java/com/structurizr/util/Features.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/Features.java diff --git a/structurizr-core/src/main/java/com/structurizr/util/Features.java b/structurizr-core/src/main/java/com/structurizr/util/Features.java new file mode 100644 index 000000000..44c615cfd --- /dev/null +++ b/structurizr-core/src/main/java/com/structurizr/util/Features.java @@ -0,0 +1,26 @@ +package com.structurizr.util; + +import java.util.HashMap; +import java.util.Map; + +public class Features { + + private final Map features = new HashMap<>(); + + public void enable(String feature) { + features.put(feature, true); + } + + public void disable(String feature) { + features.put(feature, false); + } + + public void configure(String feature, boolean enabled) { + features.put(feature, enabled); + } + + public boolean isEnabled(String feature) { + return features.getOrDefault(feature, false); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/Features.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/Features.java new file mode 100644 index 000000000..03a99eb4f --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/Features.java @@ -0,0 +1,7 @@ +package com.structurizr.dsl; + +public final class Features extends com.structurizr.util.Features { + + public static final String ARCHETYPES = "structurizr.feature.dsl.archetypes"; + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index 32b75a186..cbd43f361 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -45,8 +45,8 @@ public final class StructurizrDslParser extends StructurizrDslTokens { private final Set parsedTokens = new HashSet<>(); private final IdentifiersRegister identifiersRegister; private final Map constantsAndVariables; + private final Features features = new Features(); - private boolean archetypesEnabled = false; private final Map> archetypes = Map.of( StructurizrDslTokens.GROUP_TOKEN, new HashMap<>(), StructurizrDslTokens.PERSON_TOKEN, new HashMap<>(), @@ -420,7 +420,7 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn } else if (isElementKeywordOrArchetype(firstToken, PERSON_TOKEN) && (inContext(ModelDslContext.class))) { Person person = new PersonParser().parse(getContext(ModelDslContext.class), tokens.withoutContextStartToken()); - if (archetypesEnabled) { + if (features.isEnabled(Features.ARCHETYPES)) { applyArchetype(firstToken, person); } @@ -432,7 +432,7 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn } else if (isElementKeywordOrArchetype(firstToken, SOFTWARE_SYSTEM_TOKEN) && (inContext(ModelDslContext.class))) { SoftwareSystem softwareSystem = new SoftwareSystemParser().parse(getContext(ModelDslContext.class), tokens.withoutContextStartToken()); - if (archetypesEnabled) { + if (features.isEnabled(Features.ARCHETYPES)) { applyArchetype(firstToken, softwareSystem); } @@ -444,7 +444,7 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn } else if (isElementKeywordOrArchetype(firstToken, CONTAINER_TOKEN) && inContext(SoftwareSystemDslContext.class)) { Container container = new ContainerParser().parse(getContext(SoftwareSystemDslContext.class), tokens.withoutContextStartToken()); - if (archetypesEnabled) { + if (features.isEnabled(Features.ARCHETYPES)) { applyArchetype(firstToken, container); } @@ -456,7 +456,7 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn } else if (isElementKeywordOrArchetype(firstToken, COMPONENT_TOKEN) && inContext(ContainerDslContext.class)) { Component component = new ComponentParser().parse(getContext(ContainerDslContext.class), tokens.withoutContextStartToken()); - if (archetypesEnabled) { + if (features.isEnabled(Features.ARCHETYPES)) { applyArchetype(firstToken, component); } @@ -658,7 +658,7 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn startContext(new ModelDslContext()); parsedTokens.add(MODEL_TOKEN); - } else if (ARCHETYPES_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelDslContext.class) && archetypesEnabled) { + } else if (ARCHETYPES_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelDslContext.class) && features.isEnabled(Features.ARCHETYPES)) { startContext(new ArchetypesDslContext()); } else if (isElementKeywordOrArchetype(firstToken, GROUP_TOKEN) && inContext(ArchetypesDslContext.class)) { @@ -843,7 +843,7 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn } else if (isElementKeywordOrArchetype(firstToken, DEPLOYMENT_NODE_TOKEN) && inContext(DeploymentEnvironmentDslContext.class)) { DeploymentNode deploymentNode = new DeploymentNodeParser().parse(getContext(DeploymentEnvironmentDslContext.class), tokens.withoutContextStartToken()); - if (archetypesEnabled) { + if (features.isEnabled(Features.ARCHETYPES)) { applyArchetype(firstToken, deploymentNode); } @@ -854,7 +854,7 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn registerIdentifier(identifier, deploymentNode); } else if (isElementKeywordOrArchetype(firstToken, DEPLOYMENT_NODE_TOKEN) && inContext(DeploymentNodeDslContext.class)) { DeploymentNode deploymentNode = new DeploymentNodeParser().parse(getContext(DeploymentNodeDslContext.class), tokens.withoutContextStartToken()); - if (archetypesEnabled) { + if (features.isEnabled(Features.ARCHETYPES)) { applyArchetype(firstToken, deploymentNode); } @@ -865,7 +865,7 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn registerIdentifier(identifier, deploymentNode); } else if (isElementKeywordOrArchetype(firstToken, INFRASTRUCTURE_NODE_TOKEN) && inContext(DeploymentNodeDslContext.class)) { InfrastructureNode infrastructureNode = new InfrastructureNodeParser().parse(getContext(DeploymentNodeDslContext.class), tokens.withoutContextStartToken()); - if (archetypesEnabled) { + if (features.isEnabled(Features.ARCHETYPES)) { applyArchetype(firstToken, infrastructureNode); } @@ -1292,12 +1292,12 @@ private boolean isGroup(DslContext context) { return false; } - public void setArchetypesEnabled(boolean archetypesEnabled) { - this.archetypesEnabled = archetypesEnabled; + public Features getFeatures() { + return features; } private boolean isElementKeywordOrArchetype(String token, String keyword) { - if (archetypesEnabled) { + if (features.isEnabled(Features.ARCHETYPES)) { if (token.equalsIgnoreCase(keyword)) { return true; } else { @@ -1323,7 +1323,7 @@ private void extendArchetype(Archetype archetype, String archetypeName) { } private void applyArchetype(String archetypeName, Person person) { - Archetype archetype = archetypes.get(StructurizrDslTokens.PERSON_TOKEN).get(archetypeName); + Archetype archetype = archetypes.get(StructurizrDslTokens.PERSON_TOKEN).get(archetypeName.toLowerCase()); if (archetype != null) { person.setDescription(archetype.getDescription()); person.addTags(archetype.getTags().toArray(new String[0])); @@ -1331,7 +1331,7 @@ private void applyArchetype(String archetypeName, Person person) { } private void applyArchetype(String archetypeName, SoftwareSystem softwareSystem) { - Archetype archetype = archetypes.get(StructurizrDslTokens.SOFTWARE_SYSTEM_TOKEN).get(archetypeName); + Archetype archetype = archetypes.get(StructurizrDslTokens.SOFTWARE_SYSTEM_TOKEN).get(archetypeName.toLowerCase()); if (archetype != null) { softwareSystem.setDescription(archetype.getDescription()); softwareSystem.addTags(archetype.getTags().toArray(new String[0])); @@ -1339,7 +1339,7 @@ private void applyArchetype(String archetypeName, SoftwareSystem softwareSystem) } private void applyArchetype(String archetypeName, Container container) { - Archetype archetype = archetypes.get(StructurizrDslTokens.CONTAINER_TOKEN).get(archetypeName); + Archetype archetype = archetypes.get(StructurizrDslTokens.CONTAINER_TOKEN).get(archetypeName.toLowerCase()); if (archetype != null) { container.setTechnology(archetype.getTechnology()); container.setDescription(archetype.getDescription()); @@ -1348,7 +1348,7 @@ private void applyArchetype(String archetypeName, Container container) { } private void applyArchetype(String archetypeName, Component component) { - Archetype archetype = archetypes.get(StructurizrDslTokens.COMPONENT_TOKEN).get(archetypeName); + Archetype archetype = archetypes.get(StructurizrDslTokens.COMPONENT_TOKEN).get(archetypeName.toLowerCase()); if (archetype != null) { component.setTechnology(archetype.getTechnology()); component.setDescription(archetype.getDescription()); @@ -1357,7 +1357,7 @@ private void applyArchetype(String archetypeName, Component component) { } private void applyArchetype(String archetypeName, DeploymentNode deploymentNode) { - Archetype archetype = archetypes.get(StructurizrDslTokens.DEPLOYMENT_NODE_TOKEN).get(archetypeName); + Archetype archetype = archetypes.get(StructurizrDslTokens.DEPLOYMENT_NODE_TOKEN).get(archetypeName.toLowerCase()); if (archetype != null) { deploymentNode.setTechnology(archetype.getTechnology()); deploymentNode.setDescription(archetype.getDescription()); @@ -1366,7 +1366,7 @@ private void applyArchetype(String archetypeName, DeploymentNode deploymentNode) } private void applyArchetype(String archetypeName, InfrastructureNode infrastructureNode) { - Archetype archetype = archetypes.get(StructurizrDslTokens.INFRASTRUCTURE_NODE_TOKEN).get(archetypeName); + Archetype archetype = archetypes.get(StructurizrDslTokens.INFRASTRUCTURE_NODE_TOKEN).get(archetypeName.toLowerCase()); if (archetype != null) { infrastructureNode.setTechnology(archetype.getTechnology()); infrastructureNode.setDescription(archetype.getDescription()); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index c9548254c..278a48d9b 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -1444,14 +1444,30 @@ void test_sourceIsNotRetained() throws Exception { } @Test - void test_archetypes() throws Exception { + void test_archetypes_WhenDisabled() throws Exception { + try { + File parentDslFile = new File("src/test/resources/dsl/archetypes.dsl"); + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(parentDslFile); + fail(); + } catch (StructurizrDslParserException e) { + assertTrue(e.getMessage().startsWith("Unexpected tokens (expected: !identifiers, group, person, softwareSystem, deploymentEnvironment, element, ->) at line 4")); + assertTrue(e.getMessage().endsWith("archetypes {")); + } + } + + @Test + void test_archetypes_WhenEnabled() throws Exception { File parentDslFile = new File("src/test/resources/dsl/archetypes.dsl"); StructurizrDslParser parser = new StructurizrDslParser(); - parser.setArchetypesEnabled(true); + parser.getFeatures().enable(Features.ARCHETYPES); parser.parse(parentDslFile); Workspace workspace = parser.getWorkspace(); - WorkspaceUtils.printWorkspaceAsJson(workspace); + Container customerApi = workspace.getModel().getSoftwareSystemWithName("X").getContainerWithName("Customer API"); + assertTrue(customerApi.getTagsAsSet().contains("Application")); + assertTrue(customerApi.getTagsAsSet().contains("Spring Boot")); + assertEquals("Spring Boot", customerApi.getTechnology()); } } \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/archetypes.dsl b/structurizr-dsl/src/test/resources/dsl/archetypes.dsl index 1ad2d6089..d7524e06e 100644 --- a/structurizr-dsl/src/test/resources/dsl/archetypes.dsl +++ b/structurizr-dsl/src/test/resources/dsl/archetypes.dsl @@ -2,8 +2,12 @@ workspace { model { archetypes { - application = container - datastore = container + application = container { + tag "Application" + } + datastore = container { + tag "Datastore" + } microservice = group From f00c975dffdf247c5d7cbdc9055a8a20d3dc952e Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Thu, 16 Jan 2025 13:43:37 +0000 Subject: [PATCH 614/717] Improves request/response logging of WorkspaceApiClient. --- .../structurizr/api/WorkspaceApiClient.java | 37 ++++++++++++------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/structurizr-client/src/main/java/com/structurizr/api/WorkspaceApiClient.java b/structurizr-client/src/main/java/com/structurizr/api/WorkspaceApiClient.java index 3b7ce5c95..8524f753d 100644 --- a/structurizr-client/src/main/java/com/structurizr/api/WorkspaceApiClient.java +++ b/structurizr-client/src/main/java/com/structurizr/api/WorkspaceApiClient.java @@ -22,6 +22,7 @@ import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpStatus; +import org.apache.hc.core5.http.ParseException; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.entity.StringEntity; @@ -201,11 +202,10 @@ private boolean manageLockForWorkspace(long workspaceId, boolean lock) throws St debugRequest(httpRequest, null); try (CloseableHttpResponse response = httpClient.execute(httpRequest)) { - debugResponse(response); + String json = EntityUtils.toString(response.getEntity()); + debugResponse(response, json); - String responseText = EntityUtils.toString(response.getEntity()); - ApiResponse apiResponse = ApiResponse.parse(responseText); - log.info(responseText); + ApiResponse apiResponse = ApiResponse.parse(json); if (response.getCode() == HttpStatus.SC_OK) { return apiResponse.isSuccess(); @@ -245,9 +245,9 @@ public Workspace getWorkspace(long workspaceId) throws StructurizrClientExceptio debugRequest(httpGet, null); try (CloseableHttpResponse response = httpClient.execute(httpGet)) { - debugResponse(response); - String json = EntityUtils.toString(response.getEntity()); + debugResponse(response, json); + if (response.getCode() == HttpStatus.SC_OK) { archiveWorkspace(workspaceId, json); @@ -339,10 +339,9 @@ public void putWorkspace(long workspaceId, Workspace workspace) throws Structuri log.info("Putting workspace with ID " + workspaceId); try (CloseableHttpResponse response = httpClient.execute(httpPut)) { String json = EntityUtils.toString(response.getEntity()); - if (response.getCode() == HttpStatus.SC_OK) { - debugResponse(response); - log.info(json); - } else { + debugResponse(response, json); + + if (response.getCode() != HttpStatus.SC_OK) { ApiResponse apiResponse = ApiResponse.parse(json); throw new StructurizrClientException(apiResponse.getMessage()); } @@ -355,19 +354,29 @@ public void putWorkspace(long workspaceId, Workspace workspace) throws Structuri private void debugRequest(HttpUriRequestBase httpRequest, String content) { if (log.isDebugEnabled()) { - log.debug(httpRequest.getMethod() + " " + httpRequest.getPath()); + log.debug("Request"); + log.debug("HTTP method: " + httpRequest.getMethod()); + log.debug("Path: " + httpRequest.getPath()); Header[] headers = httpRequest.getHeaders(); for (Header header : headers) { - log.debug(header.getName() + ": " + header.getValue()); + log.debug("Header: " + header.getName() + "=" + header.getValue()); } if (content != null) { + log.debug("---Start content---"); log.debug(content); + log.debug("---End content---"); } } } - private void debugResponse(CloseableHttpResponse response) { - log.debug(response.getCode()); + private void debugResponse(CloseableHttpResponse response, String content) { + log.debug("Response"); + log.debug("HTTP status code: " + response.getCode()); + if (content != null) { + log.debug("---Start content---"); + log.debug(content); + log.debug("---End content---"); + } } private void addHeaders(HttpUriRequestBase httpRequest, String content, String contentType) throws Exception { From 461595b719a45c67b81886f18a74f3123ee5bba3 Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Tue, 11 Feb 2025 09:40:47 +0000 Subject: [PATCH 615/717] Fixes tests. --- .../src/test/java/com/structurizr/view/ThemeUtilsTests.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/structurizr-client/src/test/java/com/structurizr/view/ThemeUtilsTests.java b/structurizr-client/src/test/java/com/structurizr/view/ThemeUtilsTests.java index ae1f66800..5f4882881 100644 --- a/structurizr-client/src/test/java/com/structurizr/view/ThemeUtilsTests.java +++ b/structurizr-client/src/test/java/com/structurizr/view/ThemeUtilsTests.java @@ -39,7 +39,7 @@ void loadThemes_LoadsThemesWhenThemesAreDefined() throws Exception { assertNotNull(style); assertEquals("#d6242d", style.getStroke()); assertEquals("#d6242d", style.getColor()); - assertEquals("https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Alexa-For-Business_light-bg@4x.png", style.getIcon()); + assertEquals("https://static.structurizr.com/themes/amazon-web-services-2020.04.30/alexa-for-business.png", style.getIcon()); } @Test @@ -141,7 +141,7 @@ void loadThemes_ReplacesRelativeIconReferences() throws Exception { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name"); softwareSystem.addTags("Amazon Web Services - Alexa For Business"); - workspace.getViews().getConfiguration().setThemes("https://raw.githubusercontent.com/structurizr/themes/master/amazon-web-services-2020.04.30/theme.json"); + workspace.getViews().getConfiguration().setThemes("https://static.structurizr.com/themes/amazon-web-services-2020.04.30/theme.json"); ThemeUtils.loadThemes(workspace); @@ -153,7 +153,7 @@ void loadThemes_ReplacesRelativeIconReferences() throws Exception { assertNotNull(style); assertEquals("#d6242d", style.getStroke()); assertEquals("#d6242d", style.getColor()); - assertEquals("https://raw.githubusercontent.com/structurizr/themes/master/amazon-web-services-2020.04.30/Alexa-For-Business_light-bg@4x.png", style.getIcon()); + assertEquals("https://static.structurizr.com/themes/amazon-web-services-2020.04.30/alexa-for-business.png", style.getIcon()); } } \ No newline at end of file From a998bfaacbe3cb2500ec559f21728347000ee8d0 Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Tue, 11 Feb 2025 09:44:13 +0000 Subject: [PATCH 616/717] Fixes tests. --- .../structurizr/export/ilograph/54915.ilograph | 14 +++++++------- ...915-AmazonWebServicesDeployment-WithTags.puml | 16 ++++++++-------- ...54915-AmazonWebServicesDeployment-Legend.puml | 16 ++++++++-------- .../54915-AmazonWebServicesDeployment.puml | 16 ++++++++-------- 4 files changed, 31 insertions(+), 31 deletions(-) diff --git a/structurizr-export/src/test/java/com/structurizr/export/ilograph/54915.ilograph b/structurizr-export/src/test/java/com/structurizr/export/ilograph/54915.ilograph index 411ac3c5e..5b37222b4 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/ilograph/54915.ilograph +++ b/structurizr-export/src/test/java/com/structurizr/export/ilograph/54915.ilograph @@ -26,7 +26,7 @@ resources: subtitle: "[Deployment Node]" backgroundColor: "#ffffff" color: "#232f3e" - icon: "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/AWS-Cloud_light-bg@4x.png" + icon: "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/aws-cloud.png" children: - id: "6" @@ -34,7 +34,7 @@ resources: subtitle: "[Deployment Node]" backgroundColor: "#ffffff" color: "#147eba" - icon: "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Region_light-bg@4x.png" + icon: "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/region.png" children: - id: "12" @@ -42,7 +42,7 @@ resources: subtitle: "[Deployment Node]" backgroundColor: "#ffffff" color: "#3b48cc" - icon: "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Amazon-RDS_light-bg@4x.png" + icon: "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/amazon-rds.png" children: - id: "13" @@ -50,7 +50,7 @@ resources: subtitle: "[Deployment Node]" backgroundColor: "#ffffff" color: "#3b48cc" - icon: "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Amazon-RDS_MySQL_instance_light-bg@4x.png" + icon: "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/amazon-rds-mysql-instance.png" children: - id: "14" @@ -65,7 +65,7 @@ resources: subtitle: "[Deployment Node]" backgroundColor: "#ffffff" color: "#cc2264" - icon: "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/AWS-Auto-Scaling_light-bg@4x.png" + icon: "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/aws-auto-scaling.png" children: - id: "10" @@ -73,7 +73,7 @@ resources: subtitle: "[Deployment Node]" backgroundColor: "#ffffff" color: "#d86613" - icon: "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Amazon-EC2_light-bg@4x.png" + icon: "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/amazon-ec2.png" children: - id: "11" @@ -97,7 +97,7 @@ resources: description: "Automatically distributes incoming application traffic." backgroundColor: "#ffffff" color: "#693cc5" - icon: "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Elastic-Load-Balancing_light-bg@4x.png" + icon: "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/elastic-load-balancing.png" perspectives: - name: Static Structure diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/54915-AmazonWebServicesDeployment-WithTags.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/54915-AmazonWebServicesDeployment-WithTags.puml index 420931723..da23c6199 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/54915-AmazonWebServicesDeployment-WithTags.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/54915-AmazonWebServicesDeployment-WithTags.puml @@ -10,15 +10,15 @@ left to right direction !include AddElementTag("Container,Application", $bgColor="#ffffff", $borderColor="#b2b2b2", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="solid") -AddElementTag("Amazon Web Services - RDS", $bgColor="#ffffff", $borderColor="#3b48cc", $fontColor="#3b48cc", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Amazon-RDS_light-bg@4x.png{scale=0.1}", $shadowing="", $borderStyle="solid") -AddElementTag("Amazon Web Services - Auto Scaling", $bgColor="#ffffff", $borderColor="#cc2264", $fontColor="#cc2264", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/AWS-Auto-Scaling_light-bg@4x.png{scale=0.1}", $shadowing="", $borderStyle="solid") -AddElementTag("Amazon Web Services - Route 53", $bgColor="#ffffff", $borderColor="#693cc5", $fontColor="#693cc5", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Amazon-Route-53_light-bg@4x.png{scale=0.1}", $shadowing="", $borderStyle="solid") -AddElementTag("Amazon Web Services - EC2", $bgColor="#ffffff", $borderColor="#d86613", $fontColor="#d86613", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Amazon-EC2_light-bg@4x.png{scale=0.1}", $shadowing="", $borderStyle="solid") -AddElementTag("Amazon Web Services - Region", $bgColor="#ffffff", $borderColor="#147eba", $fontColor="#147eba", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Region_light-bg@4x.png{scale=0.21428571428571427}", $shadowing="", $borderStyle="solid") -AddElementTag("Amazon Web Services - Elastic Load Balancing", $bgColor="#ffffff", $borderColor="#693cc5", $fontColor="#693cc5", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Elastic-Load-Balancing_light-bg@4x.png{scale=0.1}", $shadowing="", $borderStyle="solid") -AddElementTag("Amazon Web Services - RDS MySQL instance", $bgColor="#ffffff", $borderColor="#3b48cc", $fontColor="#3b48cc", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/Amazon-RDS_MySQL_instance_light-bg@4x.png{scale=0.15}", $shadowing="", $borderStyle="solid") +AddElementTag("Amazon Web Services - RDS", $bgColor="#ffffff", $borderColor="#3b48cc", $fontColor="#3b48cc", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/amazon-rds.png{scale=0.1}", $shadowing="", $borderStyle="solid") +AddElementTag("Amazon Web Services - Auto Scaling", $bgColor="#ffffff", $borderColor="#cc2264", $fontColor="#cc2264", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/aws-auto-scaling.png{scale=0.1}", $shadowing="", $borderStyle="solid") +AddElementTag("Amazon Web Services - Route 53", $bgColor="#ffffff", $borderColor="#693cc5", $fontColor="#693cc5", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/amazon-route-53.png{scale=0.1}", $shadowing="", $borderStyle="solid") +AddElementTag("Amazon Web Services - EC2", $bgColor="#ffffff", $borderColor="#d86613", $fontColor="#d86613", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/amazon-ec2.png{scale=0.1}", $shadowing="", $borderStyle="solid") +AddElementTag("Amazon Web Services - Region", $bgColor="#ffffff", $borderColor="#147eba", $fontColor="#147eba", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/region.png{scale=0.21428571428571427}", $shadowing="", $borderStyle="solid") +AddElementTag("Amazon Web Services - Elastic Load Balancing", $bgColor="#ffffff", $borderColor="#693cc5", $fontColor="#693cc5", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/elastic-load-balancing.png{scale=0.1}", $shadowing="", $borderStyle="solid") +AddElementTag("Amazon Web Services - RDS MySQL instance", $bgColor="#ffffff", $borderColor="#3b48cc", $fontColor="#3b48cc", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/amazon-rds-mysql-instance.png{scale=0.15}", $shadowing="", $borderStyle="solid") AddElementTag("Container,Database", $bgColor="#ffffff", $borderColor="#b2b2b2", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="solid") -AddElementTag("Amazon Web Services - Cloud", $bgColor="#ffffff", $borderColor="#232f3e", $fontColor="#232f3e", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/AWS-Cloud_light-bg@4x.png{scale=0.21428571428571427}", $shadowing="", $borderStyle="solid") +AddElementTag("Amazon Web Services - Cloud", $bgColor="#ffffff", $borderColor="#232f3e", $fontColor="#232f3e", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/aws-cloud.png{scale=0.21428571428571427}", $shadowing="", $borderStyle="solid") AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/54915-AmazonWebServicesDeployment-Legend.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/54915-AmazonWebServicesDeployment-Legend.puml index 3532dad2d..12bb6a1ce 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/54915-AmazonWebServicesDeployment-Legend.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/54915-AmazonWebServicesDeployment-Legend.puml @@ -22,7 +22,7 @@ skinparam rectangle<<1>> { BorderColor #cc2264 roundCorner 20 } -rectangle "==Amazon Web Services - Auto Scaling\n\n" <<1>> +rectangle "==Amazon Web Services - Auto Scaling\n\n" <<1>> skinparam rectangle<<2>> { BackgroundColor #ffffff @@ -30,7 +30,7 @@ skinparam rectangle<<2>> { BorderColor #232f3e roundCorner 20 } -rectangle "==Amazon Web Services - Cloud\n\n" <<2>> +rectangle "==Amazon Web Services - Cloud\n\n" <<2>> skinparam rectangle<<3>> { BackgroundColor #ffffff @@ -38,7 +38,7 @@ skinparam rectangle<<3>> { BorderColor #d86613 roundCorner 20 } -rectangle "==Amazon Web Services - EC2\n\n" <<3>> +rectangle "==Amazon Web Services - EC2\n\n" <<3>> skinparam rectangle<<4>> { BackgroundColor #ffffff @@ -46,7 +46,7 @@ skinparam rectangle<<4>> { BorderColor #693cc5 roundCorner 20 } -rectangle "==Amazon Web Services - Elastic Load Balancing\n\n" <<4>> +rectangle "==Amazon Web Services - Elastic Load Balancing\n\n" <<4>> skinparam rectangle<<5>> { BackgroundColor #ffffff @@ -54,7 +54,7 @@ skinparam rectangle<<5>> { BorderColor #3b48cc roundCorner 20 } -rectangle "==Amazon Web Services - RDS\n\n" <<5>> +rectangle "==Amazon Web Services - RDS\n\n" <<5>> skinparam rectangle<<6>> { BackgroundColor #ffffff @@ -62,7 +62,7 @@ skinparam rectangle<<6>> { BorderColor #3b48cc roundCorner 20 } -rectangle "==Amazon Web Services - RDS MySQL instance\n\n" <<6>> +rectangle "==Amazon Web Services - RDS MySQL instance\n\n" <<6>> skinparam rectangle<<7>> { BackgroundColor #ffffff @@ -70,7 +70,7 @@ skinparam rectangle<<7>> { BorderColor #147eba roundCorner 20 } -rectangle "==Amazon Web Services - Region\n\n" <<7>> +rectangle "==Amazon Web Services - Region\n\n" <<7>> skinparam rectangle<<8>> { BackgroundColor #ffffff @@ -78,7 +78,7 @@ skinparam rectangle<<8>> { BorderColor #693cc5 roundCorner 20 } -rectangle "==Amazon Web Services - Route 53\n\n" <<8>> +rectangle "==Amazon Web Services - Route 53\n\n" <<8>> skinparam rectangle<<9>> { BackgroundColor #ffffff diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/54915-AmazonWebServicesDeployment.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/54915-AmazonWebServicesDeployment.puml index b0799c732..a32f9c329 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/54915-AmazonWebServicesDeployment.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/54915-AmazonWebServicesDeployment.puml @@ -83,19 +83,19 @@ skinparam rectangle<[Deployment Node]\n\n" <> as Live.AmazonWebServices { - rectangle "US-East-1\n[Deployment Node]\n\n" <> as Live.AmazonWebServices.USEast1 { - rectangle "Amazon RDS\n[Deployment Node]\n\n" <> as Live.AmazonWebServices.USEast1.AmazonRDS { - rectangle "MySQL\n[Deployment Node]\n\n" <> as Live.AmazonWebServices.USEast1.AmazonRDS.MySQL { +rectangle "Amazon Web Services\n[Deployment Node]\n\n" <> as Live.AmazonWebServices { + rectangle "US-East-1\n[Deployment Node]\n\n" <> as Live.AmazonWebServices.USEast1 { + rectangle "Amazon RDS\n[Deployment Node]\n\n" <> as Live.AmazonWebServices.USEast1.AmazonRDS { + rectangle "MySQL\n[Deployment Node]\n\n" <> as Live.AmazonWebServices.USEast1.AmazonRDS.MySQL { database "==Database\n[Container: Relational database schema]\n\nStores information regarding the veterinarians, the clients, and their pets." <> as Live.AmazonWebServices.USEast1.AmazonRDS.MySQL.Database_1 } } - rectangle "==Route 53\n[Infrastructure Node]\n\nHighly available and scalable cloud DNS service.\n\n" <> as Live.AmazonWebServices.USEast1.Route53 - rectangle "==Elastic Load Balancer\n[Infrastructure Node]\n\nAutomatically distributes incoming application traffic.\n\n" <> as Live.AmazonWebServices.USEast1.ElasticLoadBalancer - rectangle "Autoscaling group\n[Deployment Node]\n\n" <> as Live.AmazonWebServices.USEast1.Autoscalinggroup { - rectangle "Amazon EC2\n[Deployment Node]\n\n" <> as Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2 { + rectangle "==Route 53\n[Infrastructure Node]\n\nHighly available and scalable cloud DNS service.\n\n" <> as Live.AmazonWebServices.USEast1.Route53 + rectangle "==Elastic Load Balancer\n[Infrastructure Node]\n\nAutomatically distributes incoming application traffic.\n\n" <> as Live.AmazonWebServices.USEast1.ElasticLoadBalancer + rectangle "Autoscaling group\n[Deployment Node]\n\n" <> as Live.AmazonWebServices.USEast1.Autoscalinggroup { + rectangle "Amazon EC2\n[Deployment Node]\n\n" <> as Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2 { rectangle "==Web Application\n[Container: Java and Spring Boot]\n\nAllows employees to view and manage information regarding the veterinarians, the clients, and their pets." <> as Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2.WebApplication_1 } From 95ac0ab589fea127f14066ea57060653bbd972f5 Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Fri, 14 Feb 2025 10:09:20 +0000 Subject: [PATCH 617/717] Fixes #374. --- changelog.md | 4 ++++ .../java/com/structurizr/dsl/DslParserContext.java | 10 ++++++++-- .../java/com/structurizr/dsl/StructurizrDslParser.java | 2 +- .../main/java/com/structurizr/dsl/WorkspaceParser.java | 2 ++ .../src/test/java/com/structurizr/dsl/DslTests.java | 2 ++ 5 files changed, 17 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index e3a3806d7..db6a5505b 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,9 @@ # Changelog +## (unreleased) + +- structurizr-dsl: Fixes https://github.com/structurizr/java/issues/374 (!identifiers hierarchical isn't propagated when extending a workspace). + ## 3.2.1 (10th December 2024) - structurizr-core: Fixes https://github.com/structurizr/java/issues/362 (Ordering of replicated relationships in deployment environment is non-deterministic). diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DslParserContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DslParserContext.java index 7fd4eea7b..ed964b481 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/DslParserContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DslParserContext.java @@ -4,14 +4,20 @@ final class DslParserContext extends DslContext { - private final boolean restricted; + private final StructurizrDslParser parser; private final File file; + private final boolean restricted; - DslParserContext(File file, boolean restricted) { + DslParserContext(StructurizrDslParser parser, File file, boolean restricted) { + this.parser = parser; this.file = file; this.restricted = restricted; } + StructurizrDslParser getParser() { + return parser; + } + File getFile() { return file; } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index cbd43f361..99e5f88bf 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -634,7 +634,7 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn if (parsedTokens.contains(WORKSPACE_TOKEN)) { throw new RuntimeException("Multiple workspaces are not permitted in a DSL definition"); } - DslParserContext dslParserContext = new DslParserContext(dslFile, restricted); + DslParserContext dslParserContext = new DslParserContext(this, dslFile, restricted); dslParserContext.setIdentifierRegister(identifiersRegister); workspace = new WorkspaceParser().parse(dslParserContext, tokens.withoutContextStartToken()); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/WorkspaceParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/WorkspaceParser.java index 44f11b2c7..7f70fde3a 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/WorkspaceParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/WorkspaceParser.java @@ -49,6 +49,7 @@ Workspace parse(DslParserContext context, Tokens tokens) { structurizrDslParser.setRestricted(context.isRestricted()); structurizrDslParser.parse(context, dsl); workspace = structurizrDslParser.getWorkspace(); + context.getParser().setIdentifierScope(structurizrDslParser.getIdentifierScope()); } } else { if (context.isRestricted()) { @@ -72,6 +73,7 @@ Workspace parse(DslParserContext context, Tokens tokens) { StructurizrDslParser structurizrDslParser = new StructurizrDslParser(); structurizrDslParser.parse(context, file); workspace = structurizrDslParser.getWorkspace(); + context.getParser().setIdentifierScope(structurizrDslParser.getIdentifierScope()); } } } diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index 278a48d9b..8ca22ff83 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -479,6 +479,8 @@ void test_extendWorkspaceFromDsl(String dslFile) throws Exception { parser.parse(new File(dslFile)); Workspace workspace = parser.getWorkspace(); + assertEquals(IdentifierScope.Hierarchical, parser.getIdentifierScope()); + Model model = workspace.getModel(); assertEquals(CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy.class, model.getImpliedRelationshipsStrategy().getClass()); From 4e27be67449376c020591d0509542cdd3c1e44a3 Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Mon, 17 Feb 2025 13:40:06 +0000 Subject: [PATCH 618/717] Spring PetClinic 3.3.0 -> 3.4.0. --- .../java/com/structurizr/component/SpringPetClinicTests.java | 5 +++-- .../src/test/resources/dsl/spring-petclinic/workspace.dsl | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/structurizr-component/src/test/java/com/structurizr/component/SpringPetClinicTests.java b/structurizr-component/src/test/java/com/structurizr/component/SpringPetClinicTests.java index a4158f36f..4df29513a 100644 --- a/structurizr-component/src/test/java/com/structurizr/component/SpringPetClinicTests.java +++ b/structurizr-component/src/test/java/com/structurizr/component/SpringPetClinicTests.java @@ -6,6 +6,7 @@ import com.structurizr.component.filter.IncludeFullyQualifiedNameRegexFilter; import com.structurizr.component.matcher.AnnotationTypeMatcher; import com.structurizr.component.matcher.ImplementsTypeMatcher; +import com.structurizr.component.matcher.NameSuffixTypeMatcher; import com.structurizr.component.url.PrefixSourceUrlStrategy; import com.structurizr.model.Component; import com.structurizr.model.Container; @@ -35,7 +36,7 @@ void springPetClinic() { ComponentFinder componentFinder = new ComponentFinderBuilder() .forContainer(webApplication) - .fromClasses(new File(springPetClinicHome, "target/spring-petclinic-3.3.0-SNAPSHOT.jar")) + .fromClasses(new File(springPetClinicHome, "target/spring-petclinic-3.4.0-SNAPSHOT.jar")) .fromSource(new File(springPetClinicHome, "src/main/java")) .filteredBy(new IncludeFullyQualifiedNameRegexFilter("org\\.springframework\\.samples\\.petclinic\\..*")) .withStrategy( @@ -52,7 +53,7 @@ void springPetClinic() { ) .withStrategy( new ComponentFinderStrategyBuilder() - .matchedBy(new ImplementsTypeMatcher("org.springframework.data.repository.Repository")) + .matchedBy(new NameSuffixTypeMatcher("Repository")) .withDescription(new FirstSentenceDescriptionStrategy()) .withTechnology("Spring Data Repository") .withUrl(new PrefixSourceUrlStrategy("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java")) diff --git a/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl b/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl index b40a2d4bb..00202026f 100644 --- a/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl +++ b/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl @@ -21,7 +21,7 @@ workspace "Spring PetClinic" "A C4 model of the Spring PetClinic sample app (htt technology "Java and Spring" !components { - classes "${SPRING_PETCLINIC_HOME}/target/spring-petclinic-3.3.0-SNAPSHOT.jar" + classes "${SPRING_PETCLINIC_HOME}/target/spring-petclinic-3.4.0-SNAPSHOT.jar" source "${SPRING_PETCLINIC_HOME}/src/main/java" filter include fqn-regex "org.springframework.samples.petclinic..*" strategy { @@ -36,7 +36,7 @@ workspace "Spring PetClinic" "A C4 model of the Spring PetClinic sample app (htt } strategy { technology "Spring Data Repository" - matcher implements "org.springframework.data.repository.Repository" + matcher name-suffix "Repository" description first-sentence url prefix-src "https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java" forEach { From 5b64d91be0fca0b21960ceae01d0b7899bea59b5 Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Mon, 17 Feb 2025 15:24:29 +0000 Subject: [PATCH 619/717] structurizr-dsl: Adds the ability to use the `group` keyword inside a component definition, to set the group name of that component. --- changelog.md | 3 +- .../structurizr/dsl/ComponentDslContext.java | 1 + .../java/com/structurizr/dsl/GroupParser.java | 41 +++++++-- .../structurizr/dsl/StructurizrDslParser.java | 13 +-- .../java/com/structurizr/dsl/DslTests.java | 6 ++ .../com/structurizr/dsl/GroupParserTests.java | 84 ++++++++++++++++--- .../src/test/resources/dsl/groups-nested.dsl | 11 ++- 7 files changed, 134 insertions(+), 25 deletions(-) diff --git a/changelog.md b/changelog.md index db6a5505b..4251bb653 100644 --- a/changelog.md +++ b/changelog.md @@ -1,8 +1,9 @@ # Changelog -## (unreleased) +## v4.0.0 (unreleased) - structurizr-dsl: Fixes https://github.com/structurizr/java/issues/374 (!identifiers hierarchical isn't propagated when extending a workspace). +- structurizr-dsl: Adds the ability to use the `group` keyword inside a component definition, to set the group name of that component. ## 3.2.1 (10th December 2024) diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentDslContext.java index d6e5da57f..f207e3ebe 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentDslContext.java @@ -37,6 +37,7 @@ protected String[] getPermittedTokens() { StructurizrDslTokens.URL_TOKEN, StructurizrDslTokens.PROPERTIES_TOKEN, StructurizrDslTokens.PERSPECTIVES_TOKEN, + StructurizrDslTokens.GROUP_TOKEN, StructurizrDslTokens.RELATIONSHIP_TOKEN }; } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/GroupParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/GroupParser.java index 255f3db9d..251500fb6 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/GroupParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/GroupParser.java @@ -1,29 +1,32 @@ package com.structurizr.dsl; +import com.structurizr.model.Component; import com.structurizr.util.StringUtils; class GroupParser { private static final String STRUCTURIZR_GROUP_SEPARATOR_PROPERTY_NAME = "structurizr.groupSeparator"; - private static final String GRAMMAR = "group {"; + private static final String GRAMMAR_AS_CONTEXT = "group {"; + private static final String GRAMMAR_AS_PROPERTY = "group "; private final static int NAME_INDEX = 1; private final static int BRACE_INDEX = 2; - ElementGroup parse(GroupableDslContext dslContext, Tokens tokens) { + + ElementGroup parseContext(GroupableDslContext dslContext, Tokens tokens) { // group { if (tokens.hasMoreThan(BRACE_INDEX)) { - throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR_AS_CONTEXT); } if (!tokens.includes(BRACE_INDEX)) { - throw new RuntimeException("Expected: " + GRAMMAR); + throw new RuntimeException("Expected: " + GRAMMAR_AS_CONTEXT); } if (!DslContext.CONTEXT_START_TOKEN.equalsIgnoreCase(tokens.get(BRACE_INDEX))) { - throw new RuntimeException("Expected: " + GRAMMAR); + throw new RuntimeException("Expected: " + GRAMMAR_AS_CONTEXT); } ElementGroup group; @@ -42,4 +45,32 @@ ElementGroup parse(GroupableDslContext dslContext, Tokens tokens) { return group; } + void parseProperty(ComponentDslContext dslContext, Tokens tokens) { + // group + + if (tokens.includes(BRACE_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR_AS_PROPERTY); + } + + if (!tokens.includes(NAME_INDEX)) { + throw new RuntimeException("Expected: " + GRAMMAR_AS_PROPERTY); + } + + String group = tokens.get(NAME_INDEX); + + Component component = dslContext.getComponent(); + String existingGroup = component.getGroup(); + + if (!StringUtils.isNullOrEmpty(existingGroup)) { + String groupSeparator = dslContext.getWorkspace().getModel().getProperties().getOrDefault(STRUCTURIZR_GROUP_SEPARATOR_PROPERTY_NAME, ""); + if (StringUtils.isNullOrEmpty(groupSeparator)) { + throw new RuntimeException("To use nested groups, please define a model property named " + STRUCTURIZR_GROUP_SEPARATOR_PROPERTY_NAME); + } + + group = existingGroup + groupSeparator + group; + } + + component.setGroup(group); + } + } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index 99e5f88bf..db2d0e034 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -522,32 +522,32 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn throw new RuntimeException("The enterprise keyword was previously deprecated, and has now been removed - please use group instead (https://docs.structurizr.com/dsl/language#group)"); } else if (isElementKeywordOrArchetype(firstToken, GROUP_TOKEN) && inContext(ModelDslContext.class)) { - ElementGroup group = new GroupParser().parse(getContext(ModelDslContext.class), tokens); + ElementGroup group = new GroupParser().parseContext(getContext(ModelDslContext.class), tokens); startContext(new ModelDslContext(group)); registerIdentifier(identifier, group); } else if (isElementKeywordOrArchetype(firstToken, GROUP_TOKEN) && inContext(SoftwareSystemDslContext.class)) { - ElementGroup group = new GroupParser().parse(getContext(SoftwareSystemDslContext.class), tokens); + ElementGroup group = new GroupParser().parseContext(getContext(SoftwareSystemDslContext.class), tokens); SoftwareSystem softwareSystem = getContext(SoftwareSystemDslContext.class).getSoftwareSystem(); group.setParent(softwareSystem); startContext(new SoftwareSystemDslContext(softwareSystem, group)); registerIdentifier(identifier, group); } else if (isElementKeywordOrArchetype(firstToken, GROUP_TOKEN) && inContext(ContainerDslContext.class)) { - ElementGroup group = new GroupParser().parse(getContext(ContainerDslContext.class), tokens); + ElementGroup group = new GroupParser().parseContext(getContext(ContainerDslContext.class), tokens); Container container = getContext(ContainerDslContext.class).getContainer(); group.setParent(container); startContext(new ContainerDslContext(container, group)); registerIdentifier(identifier, group); } else if (isElementKeywordOrArchetype(firstToken, GROUP_TOKEN) && inContext(DeploymentEnvironmentDslContext.class)) { - ElementGroup group = new GroupParser().parse(getContext(DeploymentEnvironmentDslContext.class), tokens); + ElementGroup group = new GroupParser().parseContext(getContext(DeploymentEnvironmentDslContext.class), tokens); String environment = getContext(DeploymentEnvironmentDslContext.class).getEnvironment(); startContext(new DeploymentEnvironmentDslContext(environment, group)); registerIdentifier(identifier, group); } else if (isElementKeywordOrArchetype(firstToken, GROUP_TOKEN) && inContext(DeploymentNodeDslContext.class)) { - ElementGroup group = new GroupParser().parse(getContext(DeploymentNodeDslContext.class), tokens); + ElementGroup group = new GroupParser().parseContext(getContext(DeploymentNodeDslContext.class), tokens); DeploymentNode deploymentNode = getContext(DeploymentNodeDslContext.class).getDeploymentNode(); startContext(new DeploymentNodeDslContext(deploymentNode, group)); @@ -630,6 +630,9 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn } else if (inContext(PerspectivesDslContext.class)) { new PerspectiveParser().parse(getContext(PerspectivesDslContext.class), tokens); + } else if (GROUP_TOKEN.equalsIgnoreCase(firstToken) && inContext(ComponentDslContext.class)) { + new GroupParser().parseProperty(getContext(ComponentDslContext.class), tokens); + } else if (WORKSPACE_TOKEN.equalsIgnoreCase(firstToken) && contextStack.empty()) { if (parsedTokens.contains(WORKSPACE_TOKEN)) { throw new RuntimeException("Multiple workspaces are not permitted in a DSL definition"); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index 8ca22ff83..920373cf9 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -641,6 +641,12 @@ void test_nested_groups() throws Exception { Container aApi = a.getContainerWithName("A API"); assertEquals("Capability 1/Service A", aApi.getGroup()); + Component aApiEndpoint = aApi.getComponentWithName("API Endpoint"); + assertEquals("a-api.jar/API Layer", aApiEndpoint.getGroup()); + + Component aApiRepository = aApi.getComponentWithName("Repository"); + assertEquals("a-api.jar/Data Layer", aApiRepository.getGroup()); + Container aDatabase = a.getContainerWithName("A Database"); assertEquals("Capability 1/Service A", aDatabase.getGroup()); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/GroupParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/GroupParserTests.java index ef1af486a..b4415f1d1 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/GroupParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/GroupParserTests.java @@ -1,17 +1,18 @@ package com.structurizr.dsl; +import com.structurizr.model.Component; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; class GroupParserTests extends AbstractTests { - private GroupParser parser = new GroupParser(); + private final GroupParser parser = new GroupParser(); @Test - void parse_ThrowsAnException_WhenThereAreTooManyTokens() { + void parseContext_ThrowsAnException_WhenThereAreTooManyTokens() { try { - parser.parse(null, tokens("group", "name", "{", "extra")); + parser.parseContext(null, tokens("group", "name", "{", "extra")); fail(); } catch (Exception e) { assertEquals("Too many tokens, expected: group {", e.getMessage()); @@ -19,9 +20,9 @@ void parse_ThrowsAnException_WhenThereAreTooManyTokens() { } @Test - void parse_ThrowsAnException_WhenTheNameIsMissing() { + void parseContext_ThrowsAnException_WhenTheNameIsMissing() { try { - parser.parse(null, tokens("group")); + parser.parseContext(null, tokens("group")); fail(); } catch (Exception e) { assertEquals("Expected: group {", e.getMessage()); @@ -29,9 +30,9 @@ void parse_ThrowsAnException_WhenTheNameIsMissing() { } @Test - void parse_ThrowsAnException_WhenTheBraceIsMissing() { + void parseContext_ThrowsAnException_WhenTheBraceIsMissing() { try { - parser.parse(null, tokens("group", "Name", "foo")); + parser.parseContext(null, tokens("group", "Name", "foo")); fail(); } catch (Exception e) { assertEquals("Expected: group {", e.getMessage()); @@ -39,19 +40,19 @@ void parse_ThrowsAnException_WhenTheBraceIsMissing() { } @Test - void parse() { - ElementGroup group = parser.parse(context(), tokens("group", "Group 1", "{")); + void parseContext() { + ElementGroup group = parser.parseContext(context(), tokens("group", "Group 1", "{")); assertEquals("Group 1", group.getName()); assertTrue(group.getElements().isEmpty()); } @Test - void parse_NestedGroup_ThrowsAnExceptionWhenNestedGroupsAreNotConfigured() { + void parseContext_NestedGroup_ThrowsAnExceptionWhenNestedGroupsAreNotConfigured() { ModelDslContext context = new ModelDslContext(new ElementGroup("Group 1")); context.setWorkspace(workspace); try { - parser.parse(context, tokens("group", "Group 2", "{")); + parser.parseContext(context, tokens("group", "Group 2", "{")); fail(); } catch (Exception e) { assertEquals("To use nested groups, please define a model property named structurizr.groupSeparator", e.getMessage()); @@ -59,14 +60,71 @@ void parse_NestedGroup_ThrowsAnExceptionWhenNestedGroupsAreNotConfigured() { } @Test - void parse_NestedGroup() { + void parseContext_NestedGroup() { workspace.getModel().addProperty("structurizr.groupSeparator", "/"); ModelDslContext context = new ModelDslContext(new ElementGroup("Group 1")); context.setWorkspace(workspace); - ElementGroup group = parser.parse(context, tokens("group", "Group 2", "{")); + ElementGroup group = parser.parseContext(context, tokens("group", "Group 2", "{")); assertEquals("Group 1/Group 2", group.getName()); assertTrue(group.getElements().isEmpty()); } + @Test + void parseProperty_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parseProperty(null, tokens("group", "name", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: group ", e.getMessage()); + } + } + + @Test + void parseProperty_ThrowsAnException_WhenTheNameIsMissing() { + try { + parser.parseProperty(null, tokens("group")); + fail(); + } catch (Exception e) { + assertEquals("Expected: group ", e.getMessage()); + } + } + + @Test + void parseProperty() { + Component component = workspace.getModel().addSoftwareSystem("Name").addContainer("Name").addComponent("Name"); + ComponentDslContext context = new ComponentDslContext(component); + context.setWorkspace(workspace); + + parser.parseProperty(context, tokens("group", "Group 1")); + assertEquals("Group 1", component.getGroup()); + } + + @Test + void parseProperty_NestedGroup_ThrowsAnExceptionWhenNestedGroupsAreNotConfigured() { + Component component = workspace.getModel().addSoftwareSystem("Name").addContainer("Name").addComponent("Name"); + component.setGroup("Group 1"); + ComponentDslContext context = new ComponentDslContext(component); + context.setWorkspace(workspace); + + try { + parser.parseProperty(context, tokens("group", "Group 2")); + fail(); + } catch (Exception e) { + assertEquals("To use nested groups, please define a model property named structurizr.groupSeparator", e.getMessage()); + } + } + + @Test + void parseProperty_NestedGroup() { + workspace.getModel().addProperty("structurizr.groupSeparator", "/"); + Component component = workspace.getModel().addSoftwareSystem("Name").addContainer("Name").addComponent("Name"); + component.setGroup("Group 1"); + ComponentDslContext context = new ComponentDslContext(component); + context.setWorkspace(workspace); + + parser.parseProperty(context, tokens("group", "Group 2")); + assertEquals("Group 1/Group 2", component.getGroup()); + } + } \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/groups-nested.dsl b/structurizr-dsl/src/test/resources/dsl/groups-nested.dsl index 47ac91617..6a355bb33 100644 --- a/structurizr-dsl/src/test/resources/dsl/groups-nested.dsl +++ b/structurizr-dsl/src/test/resources/dsl/groups-nested.dsl @@ -10,7 +10,16 @@ workspace { a = softwareSystem "A" { group "Capability 1" { group "Service A" { - container "A API" + container "A API" { + group "a-api.jar" { + component "API Endpoint" { + group "API Layer" + } + component "Repository" { + group "Data Layer" + } + } + } container "A Database" } group "Service B" { From 677d5e14d30ee427809e4c2367ede0b3ed203209 Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Mon, 17 Feb 2025 15:31:29 +0000 Subject: [PATCH 620/717] structurizr-dsl: Adds the ability to use the `group` keyword inside the component finder strategy `forEach` block. --- changelog.md | 1 + .../structurizr/dsl/ComponentFinderDslContext.java | 11 ++++++++--- .../dsl/ComponentFinderStrategyDslContext.java | 13 ++++++++----- .../ComponentFinderStrategyForEachDslContext.java | 8 +++++++- .../com/structurizr/dsl/StructurizrDslParser.java | 5 ++--- .../structurizr/dsl/ComponentFinderParserTests.java | 2 +- .../dsl/ComponentFinderStrategyParserTests.java | 4 +--- .../src/test/java/com/structurizr/dsl/DslTests.java | 7 +++++++ .../resources/dsl/spring-petclinic/workspace.dsl | 2 ++ 9 files changed, 37 insertions(+), 16 deletions(-) diff --git a/changelog.md b/changelog.md index 4251bb653..e703945db 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,7 @@ - structurizr-dsl: Fixes https://github.com/structurizr/java/issues/374 (!identifiers hierarchical isn't propagated when extending a workspace). - structurizr-dsl: Adds the ability to use the `group` keyword inside a component definition, to set the group name of that component. +- structurizr-dsl: Adds the ability to use the `group` keyword inside the component finder strategy `forEach` block. ## 3.2.1 (10th December 2024) diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderDslContext.java index 89f030ff8..96dc8c077 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderDslContext.java @@ -2,7 +2,6 @@ import com.structurizr.component.ComponentFinderBuilder; import com.structurizr.model.Component; -import com.structurizr.model.Container; import java.util.Set; @@ -11,10 +10,12 @@ final class ComponentFinderDslContext extends DslContext { private final ComponentFinderBuilder componentFinderBuilder = new ComponentFinderBuilder(); private final StructurizrDslParser dslParser; + private final ContainerDslContext containerDslContext; - ComponentFinderDslContext(StructurizrDslParser dslParser, Container container) { + ComponentFinderDslContext(StructurizrDslParser dslParser, ContainerDslContext containerDslContext) { this.dslParser = dslParser; - componentFinderBuilder.forContainer(container); + this.containerDslContext = containerDslContext; + componentFinderBuilder.forContainer(containerDslContext.getContainer()); } @Override @@ -26,6 +27,10 @@ protected String[] getPermittedTokens() { }; } + ContainerDslContext getContainerDslContext() { + return containerDslContext; + } + ComponentFinderBuilder getComponentFinderBuilder() { return this.componentFinderBuilder; } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyDslContext.java index 2336fa006..805de90b3 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyDslContext.java @@ -1,15 +1,14 @@ package com.structurizr.dsl; -import com.structurizr.component.ComponentFinderBuilder; import com.structurizr.component.ComponentFinderStrategyBuilder; final class ComponentFinderStrategyDslContext extends DslContext { - private final ComponentFinderBuilder componentFinderBuilder; + private final ComponentFinderDslContext componentFinderDslContext; private final ComponentFinderStrategyBuilder componentFinderStrategyBuilder = new ComponentFinderStrategyBuilder(); - ComponentFinderStrategyDslContext(ComponentFinderBuilder componentFinderBuilder) { - this.componentFinderBuilder = componentFinderBuilder; + ComponentFinderStrategyDslContext(ComponentFinderDslContext componentFinderDslContext) { + this.componentFinderDslContext = componentFinderDslContext; } @Override @@ -28,9 +27,13 @@ ComponentFinderStrategyBuilder getComponentFinderStrategyBuilder() { return this.componentFinderStrategyBuilder; } + ComponentFinderDslContext getComponentFinderDslContext() { + return this.componentFinderDslContext; + } + @Override void end() { - componentFinderBuilder.withStrategy(componentFinderStrategyBuilder.build()); + componentFinderDslContext.getComponentFinderBuilder().withStrategy(componentFinderStrategyBuilder.build()); } } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyForEachDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyForEachDslContext.java index 2e600b353..fe765bf44 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyForEachDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderStrategyForEachDslContext.java @@ -10,6 +10,12 @@ final class ComponentFinderStrategyForEachDslContext extends DslContext { ComponentFinderStrategyForEachDslContext(ComponentFinderStrategyDslContext dslContext, StructurizrDslParser dslParser) { dslContext.getComponentFinderStrategyBuilder().forEach(component -> { try { + ContainerDslContext containerDslContext = dslContext.getComponentFinderDslContext().getContainerDslContext(); + if (containerDslContext.hasGroup()) { + component.setGroup(containerDslContext.getGroup().getName()); + containerDslContext.getGroup().addElement(component); + } + dslParser.parse(dslLines, new ComponentDslContext(component)); } catch (StructurizrDslParserException e) { throw new RuntimeException(e); @@ -23,7 +29,7 @@ void addLine(String line) { @Override protected String[] getPermittedTokens() { - return new String[] {}; + return new ComponentDslContext(null).getPermittedTokens(); } } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index db2d0e034..e8906be8b 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -4,7 +4,6 @@ import com.structurizr.Workspace; import com.structurizr.model.*; import com.structurizr.util.StringUtils; -import com.structurizr.util.TagUtils; import com.structurizr.view.*; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -469,7 +468,7 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn } else if (COMPONENT_FINDER_TOKEN.equalsIgnoreCase(firstToken) && inContext(ContainerDslContext.class)) { if (!restricted) { if (shouldStartContext(tokens)) { - startContext(new ComponentFinderDslContext(this, getContext(ContainerDslContext.class).getContainer())); + startContext(new ComponentFinderDslContext(this, getContext(ContainerDslContext.class))); } } else { throwRestrictedModeException(firstToken); @@ -486,7 +485,7 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn } else if (COMPONENT_FINDER_STRATEGY_TOKEN.equalsIgnoreCase(firstToken) && inContext(ComponentFinderDslContext.class)) { if (shouldStartContext(tokens)) { - startContext(new ComponentFinderStrategyDslContext(getContext(ComponentFinderDslContext.class).getComponentFinderBuilder())); + startContext(new ComponentFinderStrategyDslContext(getContext(ComponentFinderDslContext.class))); } } else if (COMPONENT_FINDER_STRATEGY_TECHNOLOGY_TOKEN.equalsIgnoreCase(firstToken) && inContext(ComponentFinderStrategyDslContext.class)) { diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderParserTests.java index 414de072a..ec43b6f57 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderParserTests.java @@ -12,7 +12,7 @@ class ComponentFinderParserTests extends AbstractTests { private final ComponentFinderParser parser = new ComponentFinderParser(); - private final ComponentFinderDslContext context = new ComponentFinderDslContext(null, null); + private final ComponentFinderDslContext context = new ComponentFinderDslContext(null, new ContainerDslContext(null)); @Test void test_parseFilter_ThrowsAnException_WhenNoModeAndTypeAreSpecified() { diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderStrategyParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderStrategyParserTests.java index f953aaaee..a136790ee 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderStrategyParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentFinderStrategyParserTests.java @@ -1,6 +1,5 @@ package com.structurizr.dsl; -import com.structurizr.component.ComponentFinderBuilder; import com.structurizr.component.matcher.NameSuffixTypeMatcher; import org.junit.jupiter.api.Test; @@ -12,8 +11,7 @@ class ComponentFinderStrategyParserTests extends AbstractTests { private final ComponentFinderStrategyParser parser = new ComponentFinderStrategyParser(); - private final ComponentFinderBuilder componentFinderBuilder = new ComponentFinderBuilder(); - private final ComponentFinderStrategyDslContext context = new ComponentFinderStrategyDslContext(componentFinderBuilder); + private final ComponentFinderStrategyDslContext context = new ComponentFinderStrategyDslContext(new ComponentFinderDslContext(null, new ContainerDslContext(null))); @Test void test_parseTechnology_ThrowsAnException_WhenThereAreTooFewTokens() { diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index 920373cf9..d85175225 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -1324,6 +1324,7 @@ void springPetClinic() throws Exception { assertEquals("org/springframework/samples/petclinic/system/WelcomeController.java", welcomeController.getProperties().get("component.src")); assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/system/WelcomeController.java", welcomeController.getUrl()); assertSame(welcomeController, parser.getIdentifiersRegister().getElement("springPetClinic.webApplication.welcomecontroller")); + assertEquals("Web Controllers", welcomeController.getGroup()); assertTrue(clinicEmployee.hasEfferentRelationshipWith(welcomeController)); Component ownerController = webApplication.getComponentWithName("Owner Controller"); @@ -1332,6 +1333,7 @@ void springPetClinic() throws Exception { assertEquals("org/springframework/samples/petclinic/owner/OwnerController.java", ownerController.getProperties().get("component.src")); assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java", ownerController.getUrl()); assertSame(ownerController, parser.getIdentifiersRegister().getElement("springPetClinic.webApplication.ownerController")); + assertEquals("Web Controllers", ownerController.getGroup()); assertTrue(clinicEmployee.hasEfferentRelationshipWith(ownerController)); Component petController = webApplication.getComponentWithName("Pet Controller"); @@ -1340,6 +1342,7 @@ void springPetClinic() throws Exception { assertEquals("org/springframework/samples/petclinic/owner/PetController.java", petController.getProperties().get("component.src")); assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/owner/PetController.java", petController.getUrl()); assertSame(petController, parser.getIdentifiersRegister().getElement("springPetClinic.webApplication.petcontroller")); + assertEquals("Web Controllers", petController.getGroup()); assertTrue(clinicEmployee.hasEfferentRelationshipWith(petController)); Component vetController = webApplication.getComponentWithName("Vet Controller"); @@ -1348,6 +1351,7 @@ void springPetClinic() throws Exception { assertEquals("org/springframework/samples/petclinic/vet/VetController.java", vetController.getProperties().get("component.src")); assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/vet/VetController.java", vetController.getUrl()); assertSame(vetController, parser.getIdentifiersRegister().getElement("springPetClinic.webApplication.vetcontroller")); + assertEquals("Web Controllers", vetController.getGroup()); assertTrue(clinicEmployee.hasEfferentRelationshipWith(vetController)); Component visitController = webApplication.getComponentWithName("Visit Controller"); @@ -1356,6 +1360,7 @@ void springPetClinic() throws Exception { assertEquals("org/springframework/samples/petclinic/owner/VisitController.java", visitController.getProperties().get("component.src")); assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/owner/VisitController.java", visitController.getUrl()); assertSame(visitController, parser.getIdentifiersRegister().getElement("springPetClinic.webApplication.visitcontroller")); + assertEquals("Web Controllers", visitController.getGroup()); assertTrue(clinicEmployee.hasEfferentRelationshipWith(visitController)); Component ownerRepository = webApplication.getComponentWithName("Owner Repository"); @@ -1365,6 +1370,7 @@ void springPetClinic() throws Exception { assertEquals("org/springframework/samples/petclinic/owner/OwnerRepository.java", ownerRepository.getProperties().get("component.src")); assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java", ownerRepository.getUrl()); assertSame(ownerRepository, parser.getIdentifiersRegister().getElement("springPetClinic.webApplication.ownerrepository")); + assertEquals("Data Repositories", ownerRepository.getGroup()); assertTrue(ownerRepository.hasEfferentRelationshipWith(relationalDatabaseSchema, "Reads from and writes to")); Component vetRepository = webApplication.getComponentWithName("Vet Repository"); @@ -1374,6 +1380,7 @@ void springPetClinic() throws Exception { assertEquals("org/springframework/samples/petclinic/vet/VetRepository.java", vetRepository.getProperties().get("component.src")); assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java", vetRepository.getUrl()); assertSame(vetRepository, parser.getIdentifiersRegister().getElement("springPetClinic.webApplication.vetrepository")); + assertEquals("Data Repositories", vetRepository.getGroup()); assertTrue(vetRepository.hasEfferentRelationshipWith(relationalDatabaseSchema, "Reads from and writes to")); assertTrue(welcomeController.getRelationships().isEmpty()); diff --git a/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl b/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl index 00202026f..8809a0700 100644 --- a/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl +++ b/structurizr-dsl/src/test/resources/dsl/spring-petclinic/workspace.dsl @@ -32,6 +32,7 @@ workspace "Spring PetClinic" "A C4 model of the Spring PetClinic sample app (htt forEach { clinicEmployee -> this "Uses" tag "Spring MVC Controller" + group "Web Controllers" } } strategy { @@ -42,6 +43,7 @@ workspace "Spring PetClinic" "A C4 model of the Spring PetClinic sample app (htt forEach { -> relationalDatabaseSchema "Reads from and writes to" tag "Spring Data Repository" + group "Data Repositories" } } } From c6fb6453f8cf1401364703c47222f4132d209238 Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Tue, 18 Feb 2025 13:54:27 +0000 Subject: [PATCH 621/717] Improves archetypes support - defaults can be overridden, adds support for relationships. --- .../com/structurizr/model/Relationship.java | 2 +- .../java/com/structurizr/dsl/Archetype.java | 5 +- .../dsl/ComponentArchetypeDslContext.java | 2 +- .../com/structurizr/dsl/ComponentParser.java | 17 +- .../dsl/ContainerArchetypeDslContext.java | 2 +- .../com/structurizr/dsl/ContainerParser.java | 17 +- .../DeploymentNodeArchetypeDslContext.java | 2 +- .../structurizr/dsl/DeploymentNodeParser.java | 24 +-- .../dsl/ElementArchetypeDslContext.java | 9 + .../dsl/ExplicitRelationshipParser.java | 16 +- .../dsl/ImplicitRelationshipParser.java | 16 +- ...InfrastructureNodeArchetypeDslContext.java | 2 +- .../dsl/InfrastructureNodeParser.java | 11 +- .../dsl/PersonArchetypeDslContext.java | 2 +- .../com/structurizr/dsl/PersonParser.java | 11 +- .../dsl/RelationshipArchetypeDslContext.java | 19 ++ .../SoftwareSystemArchetypeDslContext.java | 2 +- .../structurizr/dsl/SoftwareSystemParser.java | 11 +- .../structurizr/dsl/StructurizrDslParser.java | 162 ++++++++---------- .../structurizr/dsl/StructurizrDslTokens.java | 3 + .../structurizr/dsl/ComponentParserTests.java | 17 +- .../structurizr/dsl/ContainerParserTests.java | 13 +- .../dsl/DeploymentNodeParserTests.java | 19 +- .../java/com/structurizr/dsl/DslTests.java | 49 ++++++ .../dsl/ExplicitRelationshipParserTests.java | 23 +-- .../dsl/ImplicitRelationshipParserTests.java | 17 +- .../dsl/InfrastructureNodeParserTests.java | 13 +- .../structurizr/dsl/PersonParserTests.java | 11 +- .../dsl/SoftwareSystemParserTests.java | 11 +- .../resources/dsl/archetypes-for-defaults.dsl | 22 +++ .../dsl/archetypes-for-extension.dsl | 29 ++++ .../src/test/resources/dsl/archetypes.dsl | 10 +- 32 files changed, 354 insertions(+), 215 deletions(-) create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/ElementArchetypeDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipArchetypeDslContext.java create mode 100644 structurizr-dsl/src/test/resources/dsl/archetypes-for-defaults.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/archetypes-for-extension.dsl diff --git a/structurizr-core/src/main/java/com/structurizr/model/Relationship.java b/structurizr-core/src/main/java/com/structurizr/model/Relationship.java index 5c3dc2b2e..e8e0fee0f 100644 --- a/structurizr-core/src/main/java/com/structurizr/model/Relationship.java +++ b/structurizr-core/src/main/java/com/structurizr/model/Relationship.java @@ -113,7 +113,7 @@ void setDescription(String description) { } /** - * Gets the technology associated with this relationship (e.g. HTTPS, JDBC, etc). + * Gets the technology associated with this relationship (e.g. HTTPS, etc). * * @return the technology as a String, * or null if a technology is not specified diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/Archetype.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/Archetype.java index 18ead6aa2..a6781b426 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/Archetype.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/Archetype.java @@ -1,7 +1,6 @@ package com.structurizr.dsl; import com.structurizr.util.StringUtils; -import com.structurizr.util.TagUtils; import java.util.LinkedHashSet; import java.util.Set; @@ -10,8 +9,8 @@ final class Archetype { private final String name; private final String type; - private String description; - private String technology; + private String description = ""; + private String technology = ""; private final Set tags = new LinkedHashSet<>(); Archetype(String name, String type) { diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentArchetypeDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentArchetypeDslContext.java index d361f92e7..a204af35c 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentArchetypeDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentArchetypeDslContext.java @@ -1,6 +1,6 @@ package com.structurizr.dsl; -final class ComponentArchetypeDslContext extends ArchetypeDslContext { +final class ComponentArchetypeDslContext extends ElementArchetypeDslContext { ComponentArchetypeDslContext(Archetype archetype) { super(archetype); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentParser.java index 9940d837a..6600845f1 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentParser.java @@ -12,7 +12,7 @@ final class ComponentParser extends AbstractParser { private final static int TECHNOLOGY_INDEX = 3; private final static int TAGS_INDEX = 4; - Component parse(ContainerDslContext context, Tokens tokens) { + Component parse(ContainerDslContext context, Tokens tokens, Archetype archetype) { // component [description] [technology] [tags] if (tokens.hasMoreThan(TAGS_INDEX)) { @@ -35,20 +35,23 @@ Component parse(ContainerDslContext context, Tokens tokens) { component = container.addComponent(name); } + String description = archetype.getDescription(); if (tokens.includes(DESCRIPTION_INDEX)) { - String description = tokens.get(DESCRIPTION_INDEX); - component.setDescription(description); + description = tokens.get(DESCRIPTION_INDEX); } + component.setDescription(description); + String technology = archetype.getTechnology(); if (tokens.includes(TECHNOLOGY_INDEX)) { - String technology = tokens.get(TECHNOLOGY_INDEX); - component.setTechnology(technology); + technology = tokens.get(TECHNOLOGY_INDEX); } + component.setTechnology(technology); + String[] tags = archetype.getTags().toArray(new String[0]); if (tokens.includes(TAGS_INDEX)) { - String tags = tokens.get(TAGS_INDEX); - component.addTags(tags.split(",")); + tags = tokens.get(TAGS_INDEX).split(","); } + component.addTags(tags); if (context.hasGroup()) { component.setGroup(context.getGroup().getName()); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerArchetypeDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerArchetypeDslContext.java index 3bd97646a..df4c4d015 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerArchetypeDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerArchetypeDslContext.java @@ -1,6 +1,6 @@ package com.structurizr.dsl; -final class ContainerArchetypeDslContext extends ArchetypeDslContext { +final class ContainerArchetypeDslContext extends ElementArchetypeDslContext { ContainerArchetypeDslContext(Archetype archetype) { super(archetype); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerParser.java index eb7f38372..c937849de 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerParser.java @@ -12,7 +12,7 @@ final class ContainerParser extends AbstractParser { private final static int TECHNOLOGY_INDEX = 3; private final static int TAGS_INDEX = 4; - Container parse(SoftwareSystemDslContext context, Tokens tokens) { + Container parse(SoftwareSystemDslContext context, Tokens tokens, Archetype archetype) { // container [description] [technology] [tags] if (tokens.hasMoreThan(TAGS_INDEX)) { @@ -35,20 +35,23 @@ Container parse(SoftwareSystemDslContext context, Tokens tokens) { container = softwareSystem.addContainer(name); } + String description = archetype.getDescription(); if (tokens.includes(DESCRIPTION_INDEX)) { - String description = tokens.get(DESCRIPTION_INDEX); - container.setDescription(description); + description = tokens.get(DESCRIPTION_INDEX); } + container.setDescription(description); + String technology = archetype.getTechnology(); if (tokens.includes(TECHNOLOGY_INDEX)) { - String technology = tokens.get(TECHNOLOGY_INDEX); - container.setTechnology(technology); + technology = tokens.get(TECHNOLOGY_INDEX); } + container.setTechnology(technology); + String[] tags = archetype.getTags().toArray(new String[0]); if (tokens.includes(TAGS_INDEX)) { - String tags = tokens.get(TAGS_INDEX); - container.addTags(tags.split(",")); + tags = tokens.get(TAGS_INDEX).split(","); } + container.addTags(tags); if (context.hasGroup()) { container.setGroup(context.getGroup().getName()); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeArchetypeDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeArchetypeDslContext.java index 895cb9a3d..65b411b5c 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeArchetypeDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeArchetypeDslContext.java @@ -1,6 +1,6 @@ package com.structurizr.dsl; -final class DeploymentNodeArchetypeDslContext extends ArchetypeDslContext { +final class DeploymentNodeArchetypeDslContext extends ElementArchetypeDslContext { DeploymentNodeArchetypeDslContext(Archetype archetype) { super(archetype); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeParser.java index ba88d73af..7e1bb3569 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeParser.java @@ -12,7 +12,7 @@ final class DeploymentNodeParser extends AbstractParser { private static final int TAGS_INDEX = 4; private static final int INSTANCES_INDEX = 5; - DeploymentNode parse(DeploymentEnvironmentDslContext context, Tokens tokens) { + DeploymentNode parse(DeploymentEnvironmentDslContext context, Tokens tokens, Archetype archetype) { // deploymentNode [description] [technology] [tags] [instances] if (tokens.hasMoreThan(INSTANCES_INDEX)) { @@ -26,23 +26,23 @@ DeploymentNode parse(DeploymentEnvironmentDslContext context, Tokens tokens) { DeploymentNode deploymentNode = null; String name = tokens.get(NAME_INDEX); - String description = ""; + String description = archetype.getDescription(); if (tokens.includes(DESCRIPTION_INDEX)) { description = tokens.get(DESCRIPTION_INDEX); } - String technology = ""; + String technology = archetype.getTechnology(); if (tokens.includes(TECHNOLOGY_INDEX)) { technology = tokens.get(TECHNOLOGY_INDEX); } deploymentNode = context.getWorkspace().getModel().addDeploymentNode(context.getEnvironment(), name, description, technology); - String tags = ""; + String[] tags = archetype.getTags().toArray(new String[0]); if (tokens.includes(TAGS_INDEX)) { - tags = tokens.get(TAGS_INDEX); - deploymentNode.addTags(tags.split(",")); + tags = tokens.get(TAGS_INDEX).split(","); } + deploymentNode.addTags(tags); String instances = "1"; if (tokens.includes(INSTANCES_INDEX)) { @@ -58,7 +58,7 @@ DeploymentNode parse(DeploymentEnvironmentDslContext context, Tokens tokens) { return deploymentNode; } - DeploymentNode parse(DeploymentNodeDslContext context, Tokens tokens) { + DeploymentNode parse(DeploymentNodeDslContext context, Tokens tokens, Archetype archetype) { // deploymentNode [description] [technology] [tags] [instances] if (tokens.hasMoreThan(INSTANCES_INDEX)) { @@ -72,12 +72,12 @@ DeploymentNode parse(DeploymentNodeDslContext context, Tokens tokens) { DeploymentNode deploymentNode = null; String name = tokens.get(NAME_INDEX); - String description = ""; + String description = archetype.getDescription(); if (tokens.includes(DESCRIPTION_INDEX)) { description = tokens.get(DESCRIPTION_INDEX); } - String technology = ""; + String technology = archetype.getTechnology(); if (tokens.includes(TECHNOLOGY_INDEX)) { technology = tokens.get(TECHNOLOGY_INDEX); } @@ -85,11 +85,11 @@ DeploymentNode parse(DeploymentNodeDslContext context, Tokens tokens) { DeploymentNode parent = context.getDeploymentNode(); deploymentNode = parent.addDeploymentNode(name, description, technology); - String tags = ""; + String[] tags = archetype.getTags().toArray(new String[0]); if (tokens.includes(TAGS_INDEX)) { - tags = tokens.get(TAGS_INDEX); - deploymentNode.addTags(tags.split(",")); + tags = tokens.get(TAGS_INDEX).split(","); } + deploymentNode.addTags(tags); String instances = "1"; if (tokens.includes(INSTANCES_INDEX)) { diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementArchetypeDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementArchetypeDslContext.java new file mode 100644 index 000000000..40a53783c --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementArchetypeDslContext.java @@ -0,0 +1,9 @@ +package com.structurizr.dsl; + +abstract class ElementArchetypeDslContext extends ArchetypeDslContext { + + ElementArchetypeDslContext(Archetype archetype) { + super(archetype); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ExplicitRelationshipParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ExplicitRelationshipParser.java index 489958f19..f67820aee 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ExplicitRelationshipParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ExplicitRelationshipParser.java @@ -17,7 +17,7 @@ final class ExplicitRelationshipParser extends AbstractRelationshipParser { private final static int TECHNOLOGY_INDEX = 4; private final static int TAGS_INDEX = 5; - Relationship parse(DslContext context, Tokens tokens) { + Relationship parse(DslContext context, Tokens tokens, Archetype archetype) { // -> [description] [technology] [tags] if (tokens.hasMoreThan(TAGS_INDEX)) { @@ -40,17 +40,17 @@ Relationship parse(DslContext context, Tokens tokens) { throw new RuntimeException("The destination element \"" + destinationId + "\" does not exist"); } - String description = ""; + String description = archetype.getDescription(); if (tokens.includes(DESCRIPTION_INDEX)) { description = tokens.get(DESCRIPTION_INDEX); } - String technology = ""; + String technology = archetype.getTechnology(); if (tokens.includes(TECHNOLOGY_INDEX)) { technology = tokens.get(TECHNOLOGY_INDEX); } - String[] tags = new String[0]; + String[] tags = archetype.getTags().toArray(new String[0]); if (tokens.includes(TAGS_INDEX)) { tags = tokens.get(TAGS_INDEX).split(","); } @@ -58,7 +58,7 @@ Relationship parse(DslContext context, Tokens tokens) { return createRelationship(sourceElement, description, technology, tags, destinationElement); } - Set parse(ElementsDslContext context, Tokens tokens) { + Set parse(ElementsDslContext context, Tokens tokens, Archetype archetype) { // -> [description] [technology] [tags] if (tokens.hasMoreThan(TAGS_INDEX)) { @@ -81,17 +81,17 @@ Set parse(ElementsDslContext context, Tokens tokens) { throw new RuntimeException("The destination element \"" + destinationId + "\" does not exist"); } - String description = ""; + String description = archetype.getDescription(); if (tokens.includes(DESCRIPTION_INDEX)) { description = tokens.get(DESCRIPTION_INDEX); } - String technology = ""; + String technology = archetype.getTechnology(); if (tokens.includes(TECHNOLOGY_INDEX)) { technology = tokens.get(TECHNOLOGY_INDEX); } - String[] tags = new String[0]; + String[] tags = archetype.getTags().toArray(new String[0]); if (tokens.includes(TAGS_INDEX)) { tags = tokens.get(TAGS_INDEX).split(","); } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImplicitRelationshipParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImplicitRelationshipParser.java index 19448e25d..e5dd02246 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImplicitRelationshipParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImplicitRelationshipParser.java @@ -15,7 +15,7 @@ final class ImplicitRelationshipParser extends AbstractRelationshipParser { private final static int TECHNOLOGY_INDEX = 3; private final static int TAGS_INDEX = 4; - Relationship parse(ElementDslContext context, Tokens tokens) { + Relationship parse(ElementDslContext context, Tokens tokens, Archetype archetype) { // -> [description] [technology] [tags] if (tokens.hasMoreThan(TAGS_INDEX)) { @@ -35,17 +35,17 @@ Relationship parse(ElementDslContext context, Tokens tokens) { throw new RuntimeException("The destination element \"" + destinationId + "\" does not exist"); } - String description = ""; + String description = archetype.getDescription(); if (tokens.includes(DESCRIPTION_INDEX)) { description = tokens.get(DESCRIPTION_INDEX); } - String technology = ""; + String technology = archetype.getTechnology(); if (tokens.includes(TECHNOLOGY_INDEX)) { technology = tokens.get(TECHNOLOGY_INDEX); } - String[] tags = new String[0]; + String[] tags = archetype.getTags().toArray(new String[0]); if (tokens.includes(TAGS_INDEX)) { tags = tokens.get(TAGS_INDEX).split(","); } @@ -53,7 +53,7 @@ Relationship parse(ElementDslContext context, Tokens tokens) { return createRelationship(sourceElement, description, technology, tags, destinationElement); } - Set parse(ElementsDslContext context, Tokens tokens) { + Set parse(ElementsDslContext context, Tokens tokens, Archetype archetype) { // -> [description] [technology] [tags] if (tokens.hasMoreThan(TAGS_INDEX)) { @@ -72,17 +72,17 @@ Set parse(ElementsDslContext context, Tokens tokens) { throw new RuntimeException("The destination element \"" + destinationId + "\" does not exist"); } - String description = ""; + String description = archetype.getDescription(); if (tokens.includes(DESCRIPTION_INDEX)) { description = tokens.get(DESCRIPTION_INDEX); } - String technology = ""; + String technology = archetype.getTechnology(); if (tokens.includes(TECHNOLOGY_INDEX)) { technology = tokens.get(TECHNOLOGY_INDEX); } - String[] tags = new String[0]; + String[] tags = archetype.getTags().toArray(new String[0]); if (tokens.includes(TAGS_INDEX)) { tags = tokens.get(TAGS_INDEX).split(","); } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeArchetypeDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeArchetypeDslContext.java index b5023744b..197da828c 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeArchetypeDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeArchetypeDslContext.java @@ -1,6 +1,6 @@ package com.structurizr.dsl; -final class InfrastructureNodeArchetypeDslContext extends ArchetypeDslContext { +final class InfrastructureNodeArchetypeDslContext extends ElementArchetypeDslContext { InfrastructureNodeArchetypeDslContext(Archetype archetype) { super(archetype); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeParser.java index 9f20ba9a5..126c90776 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeParser.java @@ -12,7 +12,7 @@ final class InfrastructureNodeParser extends AbstractParser { private static final int TECHNOLOGY_INDEX = 3; private static final int TAGS_INDEX = 4; - InfrastructureNode parse(DeploymentNodeDslContext context, Tokens tokens) { + InfrastructureNode parse(DeploymentNodeDslContext context, Tokens tokens, Archetype archetype) { // infrastructureNode [description] [technology] [tags] if (tokens.hasMoreThan(TAGS_INDEX)) { @@ -27,22 +27,23 @@ InfrastructureNode parse(DeploymentNodeDslContext context, Tokens tokens) { InfrastructureNode infrastructureNode; String name = tokens.get(NAME_INDEX); - String description = ""; + String description = archetype.getDescription(); if (tokens.includes(DESCRIPTION_INDEX)) { description = tokens.get(DESCRIPTION_INDEX); } - String technology = ""; + String technology = archetype.getTechnology(); if (tokens.includes(TECHNOLOGY_INDEX)) { technology = tokens.get(TECHNOLOGY_INDEX); } infrastructureNode = deploymentNode.addInfrastructureNode(name, description, technology); + String[] tags = archetype.getTags().toArray(new String[0]); if (tokens.includes(TAGS_INDEX)) { - String tags = tokens.get(TAGS_INDEX); - infrastructureNode.addTags(tags.split(",")); + tags = tokens.get(TAGS_INDEX).split(","); } + infrastructureNode.addTags(tags); if (context.hasGroup()) { infrastructureNode.setGroup(context.getGroup().getName()); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonArchetypeDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonArchetypeDslContext.java index a095ff456..0ecd4b4d1 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonArchetypeDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonArchetypeDslContext.java @@ -1,6 +1,6 @@ package com.structurizr.dsl; -final class PersonArchetypeDslContext extends ArchetypeDslContext { +final class PersonArchetypeDslContext extends ElementArchetypeDslContext { PersonArchetypeDslContext(Archetype archetype) { super(archetype); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonParser.java index aecc819eb..f16209982 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonParser.java @@ -10,7 +10,7 @@ final class PersonParser extends AbstractParser { private final static int DESCRIPTION_INDEX = 2; private final static int TAGS_INDEX = 3; - Person parse(ModelDslContext context, Tokens tokens) { + Person parse(ModelDslContext context, Tokens tokens, Archetype archetype) { // person [description] [tags] if (tokens.hasMoreThan(TAGS_INDEX)) { @@ -32,16 +32,17 @@ Person parse(ModelDslContext context, Tokens tokens) { person = context.getWorkspace().getModel().addPerson(name); } - String description = ""; + String description = archetype.getDescription(); if (tokens.includes(DESCRIPTION_INDEX)) { description = tokens.get(DESCRIPTION_INDEX); - person.setDescription(description); } + person.setDescription(description); + String[] tags = archetype.getTags().toArray(new String[0]); if (tokens.includes(TAGS_INDEX)) { - String tags = tokens.get(TAGS_INDEX); - person.addTags(tags.split(",")); + tags = tokens.get(TAGS_INDEX).split(","); } + person.addTags(tags); if (context.hasGroup()) { person.setGroup(context.getGroup().getName()); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipArchetypeDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipArchetypeDslContext.java new file mode 100644 index 000000000..4bd231e68 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipArchetypeDslContext.java @@ -0,0 +1,19 @@ +package com.structurizr.dsl; + +final class RelationshipArchetypeDslContext extends ArchetypeDslContext { + + RelationshipArchetypeDslContext(Archetype archetype) { + super(archetype); + } + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.DESCRIPTION_TOKEN, + StructurizrDslTokens.TECHNOLOGY_TOKEN, + StructurizrDslTokens.TAG_TOKEN, + StructurizrDslTokens.TAGS_TOKEN, + }; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemArchetypeDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemArchetypeDslContext.java index 8df3f4f60..5170b064a 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemArchetypeDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemArchetypeDslContext.java @@ -1,6 +1,6 @@ package com.structurizr.dsl; -final class SoftwareSystemArchetypeDslContext extends ArchetypeDslContext { +final class SoftwareSystemArchetypeDslContext extends ElementArchetypeDslContext { SoftwareSystemArchetypeDslContext(Archetype archetype) { super(archetype); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemParser.java index 243cd6d6c..cdbef98a5 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemParser.java @@ -10,7 +10,7 @@ final class SoftwareSystemParser extends AbstractParser { private final static int DESCRIPTION_INDEX = 2; private final static int TAGS_INDEX = 3; - SoftwareSystem parse(ModelDslContext context, Tokens tokens) { + SoftwareSystem parse(ModelDslContext context, Tokens tokens, Archetype archetype) { // softwareSystem [description] [tags] if (tokens.hasMoreThan(TAGS_INDEX)) { @@ -32,16 +32,17 @@ SoftwareSystem parse(ModelDslContext context, Tokens tokens) { softwareSystem = context.getWorkspace().getModel().addSoftwareSystem(name); } - String description = ""; + String description = archetype.getDescription(); if (tokens.includes(DESCRIPTION_INDEX)) { description = tokens.get(DESCRIPTION_INDEX); - softwareSystem.setDescription(description); } + softwareSystem.setDescription(description); + String[] tags = archetype.getTags().toArray(new String[0]); if (tokens.includes(TAGS_INDEX)) { - String tags = tokens.get(TAGS_INDEX); - softwareSystem.addTags(tags.split(",")); + tags = tokens.get(TAGS_INDEX).split(","); } + softwareSystem.addTags(tags); if (context.hasGroup()) { softwareSystem.setGroup(context.getGroup().getName()); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index e8906be8b..8423e6ac6 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -53,7 +53,8 @@ public final class StructurizrDslParser extends StructurizrDslTokens { StructurizrDslTokens.CONTAINER_TOKEN, new HashMap<>(), StructurizrDslTokens.COMPONENT_TOKEN, new HashMap<>(), StructurizrDslTokens.DEPLOYMENT_NODE_TOKEN, new HashMap<>(), - StructurizrDslTokens.INFRASTRUCTURE_NODE_TOKEN, new HashMap<>() + StructurizrDslTokens.INFRASTRUCTURE_NODE_TOKEN, new HashMap<>(), + StructurizrDslTokens.RELATIONSHIP_TOKEN, new HashMap<>() ); private final List dslSourceLines = new ArrayList<>(); @@ -315,8 +316,11 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn } else if (inContext(ExternalScriptDslContext.class)) { new ScriptParser().parseParameter(getContext(ExternalScriptDslContext.class), tokens); - } else if (tokens.size() > 2 && RELATIONSHIP_TOKEN.equals(tokens.get(1)) && (inContext(ModelDslContext.class) || inContext(DeploymentEnvironmentDslContext.class) || inContext(ElementDslContext.class))) { - Relationship relationship = new ExplicitRelationshipParser().parse(getContext(), tokens.withoutContextStartToken()); + } else if (tokens.size() > 2 && isRelationshipKeywordOrArchetype(tokens.get(1)) && (inContext(ModelDslContext.class) || inContext(DeploymentEnvironmentDslContext.class) || inContext(ElementDslContext.class))) { + // explicit without archetype: a -> b + // explicit with archetype: a --https-> b + Archetype archetype = getArchetype(RELATIONSHIP_TOKEN, tokens.get(1)); + Relationship relationship = new ExplicitRelationshipParser().parse(getContext(), tokens.withoutContextStartToken(), archetype); if (shouldStartContext(tokens)) { startContext(new RelationshipDslContext(relationship)); @@ -324,8 +328,11 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn registerIdentifier(identifier, relationship); - } else if (tokens.size() >= 2 && RELATIONSHIP_TOKEN.equals(tokens.get(0)) && inContext(ElementDslContext.class)) { - Relationship relationship = new ImplicitRelationshipParser().parse(getContext(ElementDslContext.class), tokens.withoutContextStartToken()); + } else if (tokens.size() >= 2 && isRelationshipKeywordOrArchetype(tokens.get(0)) && inContext(ElementDslContext.class)) { + // implicit without archetype: -> this + // implicit with archetype: --https-> this + Archetype archetype = getArchetype(RELATIONSHIP_TOKEN, tokens.get(1)); + Relationship relationship = new ImplicitRelationshipParser().parse(getContext(ElementDslContext.class), tokens.withoutContextStartToken(), archetype); if (shouldStartContext(tokens)) { startContext(new RelationshipDslContext(relationship)); @@ -333,15 +340,17 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn registerIdentifier(identifier, relationship); - } else if (tokens.size() > 2 && RELATIONSHIP_TOKEN.equals(tokens.get(1)) && inContext(ElementsDslContext.class)) { - Set relationships = new ExplicitRelationshipParser().parse(getContext(ElementsDslContext.class), tokens.withoutContextStartToken()); + } else if (tokens.size() > 2 && isRelationshipKeywordOrArchetype(tokens.get(1)) && inContext(ElementsDslContext.class)) { + Archetype archetype = getArchetype(RELATIONSHIP_TOKEN, tokens.get(1)); + Set relationships = new ExplicitRelationshipParser().parse(getContext(ElementsDslContext.class), tokens.withoutContextStartToken(), archetype); if (shouldStartContext(tokens)) { startContext(new RelationshipsDslContext(getContext(), relationships)); } - } else if (tokens.size() >= 2 && RELATIONSHIP_TOKEN.equals(tokens.get(0)) && inContext(ElementsDslContext.class)) { - Set relationships = new ImplicitRelationshipParser().parse(getContext(ElementsDslContext.class), tokens.withoutContextStartToken()); + } else if (tokens.size() >= 2 && isRelationshipKeywordOrArchetype(tokens.get(0)) && inContext(ElementsDslContext.class)) { + Archetype archetype = getArchetype(RELATIONSHIP_TOKEN, tokens.get(1)); + Set relationships = new ImplicitRelationshipParser().parse(getContext(ElementsDslContext.class), tokens.withoutContextStartToken(), archetype); if (shouldStartContext(tokens)) { startContext(new RelationshipsDslContext(getContext(), relationships)); @@ -418,10 +427,8 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn registerIdentifier(identifier, customElement); } else if (isElementKeywordOrArchetype(firstToken, PERSON_TOKEN) && (inContext(ModelDslContext.class))) { - Person person = new PersonParser().parse(getContext(ModelDslContext.class), tokens.withoutContextStartToken()); - if (features.isEnabled(Features.ARCHETYPES)) { - applyArchetype(firstToken, person); - } + Archetype archetype = getArchetype(PERSON_TOKEN, firstToken); + Person person = new PersonParser().parse(getContext(ModelDslContext.class), tokens.withoutContextStartToken(), archetype); if (shouldStartContext(tokens)) { startContext(new PersonDslContext(person)); @@ -430,10 +437,8 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn registerIdentifier(identifier, person); } else if (isElementKeywordOrArchetype(firstToken, SOFTWARE_SYSTEM_TOKEN) && (inContext(ModelDslContext.class))) { - SoftwareSystem softwareSystem = new SoftwareSystemParser().parse(getContext(ModelDslContext.class), tokens.withoutContextStartToken()); - if (features.isEnabled(Features.ARCHETYPES)) { - applyArchetype(firstToken, softwareSystem); - } + Archetype archetype = getArchetype(SOFTWARE_SYSTEM_TOKEN, firstToken); + SoftwareSystem softwareSystem = new SoftwareSystemParser().parse(getContext(ModelDslContext.class), tokens.withoutContextStartToken(), archetype); if (shouldStartContext(tokens)) { startContext(new SoftwareSystemDslContext(softwareSystem)); @@ -442,10 +447,8 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn registerIdentifier(identifier, softwareSystem); } else if (isElementKeywordOrArchetype(firstToken, CONTAINER_TOKEN) && inContext(SoftwareSystemDslContext.class)) { - Container container = new ContainerParser().parse(getContext(SoftwareSystemDslContext.class), tokens.withoutContextStartToken()); - if (features.isEnabled(Features.ARCHETYPES)) { - applyArchetype(firstToken, container); - } + Archetype archetype = getArchetype(CONTAINER_TOKEN, firstToken); + Container container = new ContainerParser().parse(getContext(SoftwareSystemDslContext.class), tokens.withoutContextStartToken(), archetype); if (shouldStartContext(tokens)) { startContext(new ContainerDslContext(container)); @@ -454,10 +457,8 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn registerIdentifier(identifier, container); } else if (isElementKeywordOrArchetype(firstToken, COMPONENT_TOKEN) && inContext(ContainerDslContext.class)) { - Component component = new ComponentParser().parse(getContext(ContainerDslContext.class), tokens.withoutContextStartToken()); - if (features.isEnabled(Features.ARCHETYPES)) { - applyArchetype(firstToken, component); - } + Archetype archetype = getArchetype(COMPONENT_TOKEN, firstToken); + Component component = new ComponentParser().parse(getContext(ContainerDslContext.class), tokens.withoutContextStartToken(), archetype); if (shouldStartContext(tokens)) { startContext(new ComponentDslContext(component)); @@ -723,10 +724,19 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn startContext(new InfrastructureNodeArchetypeDslContext(archetype)); } + } else if (isRelationshipKeywordOrArchetype(firstToken) && inContext(ArchetypesDslContext.class)) { + Archetype archetype = new Archetype(identifier, RELATIONSHIP_TOKEN); + extendArchetype(archetype, firstToken); + addArchetype(archetype); + + if (shouldStartContext(tokens)) { + startContext(new RelationshipArchetypeDslContext(archetype)); + } + } else if (DESCRIPTION_TOKEN.equalsIgnoreCase(firstToken) && inContext(ArchetypeDslContext.class)) { new ArchetypeParser().parseDescription(getContext(ArchetypeDslContext.class), tokens); - } else if (TECHNOLOGY_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ContainerArchetypeDslContext.class) || inContext(ComponentArchetypeDslContext.class) || inContext(DeploymentNodeArchetypeDslContext.class) || inContext(InfrastructureNodeArchetypeDslContext.class))) { + } else if (TECHNOLOGY_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ContainerArchetypeDslContext.class) || inContext(ComponentArchetypeDslContext.class) || inContext(DeploymentNodeArchetypeDslContext.class) || inContext(InfrastructureNodeArchetypeDslContext.class) || inContext(RelationshipArchetypeDslContext.class))) { new ArchetypeParser().parseTechnology(getContext(ArchetypeDslContext.class), tokens); } else if (TAG_TOKEN.equalsIgnoreCase(firstToken) && inContext(ArchetypeDslContext.class)) { @@ -844,10 +854,8 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn registerIdentifier(identifier, new DeploymentGroup(group)); } else if (isElementKeywordOrArchetype(firstToken, DEPLOYMENT_NODE_TOKEN) && inContext(DeploymentEnvironmentDslContext.class)) { - DeploymentNode deploymentNode = new DeploymentNodeParser().parse(getContext(DeploymentEnvironmentDslContext.class), tokens.withoutContextStartToken()); - if (features.isEnabled(Features.ARCHETYPES)) { - applyArchetype(firstToken, deploymentNode); - } + Archetype archetype = getArchetype(DEPLOYMENT_NODE_TOKEN, firstToken); + DeploymentNode deploymentNode = new DeploymentNodeParser().parse(getContext(DeploymentEnvironmentDslContext.class), tokens.withoutContextStartToken(), archetype); if (shouldStartContext(tokens)) { startContext(new DeploymentNodeDslContext(deploymentNode)); @@ -855,10 +863,8 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn registerIdentifier(identifier, deploymentNode); } else if (isElementKeywordOrArchetype(firstToken, DEPLOYMENT_NODE_TOKEN) && inContext(DeploymentNodeDslContext.class)) { - DeploymentNode deploymentNode = new DeploymentNodeParser().parse(getContext(DeploymentNodeDslContext.class), tokens.withoutContextStartToken()); - if (features.isEnabled(Features.ARCHETYPES)) { - applyArchetype(firstToken, deploymentNode); - } + Archetype archetype = getArchetype(DEPLOYMENT_NODE_TOKEN, firstToken); + DeploymentNode deploymentNode = new DeploymentNodeParser().parse(getContext(DeploymentNodeDslContext.class), tokens.withoutContextStartToken(), archetype); if (shouldStartContext(tokens)) { startContext(new DeploymentNodeDslContext(deploymentNode)); @@ -866,10 +872,8 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn registerIdentifier(identifier, deploymentNode); } else if (isElementKeywordOrArchetype(firstToken, INFRASTRUCTURE_NODE_TOKEN) && inContext(DeploymentNodeDslContext.class)) { - InfrastructureNode infrastructureNode = new InfrastructureNodeParser().parse(getContext(DeploymentNodeDslContext.class), tokens.withoutContextStartToken()); - if (features.isEnabled(Features.ARCHETYPES)) { - applyArchetype(firstToken, infrastructureNode); - } + Archetype archetype = getArchetype(INFRASTRUCTURE_NODE_TOKEN, firstToken); + InfrastructureNode infrastructureNode = new InfrastructureNodeParser().parse(getContext(DeploymentNodeDslContext.class), tokens.withoutContextStartToken(), archetype); if (shouldStartContext(tokens)) { startContext(new InfrastructureNodeDslContext(infrastructureNode)); @@ -1310,69 +1314,49 @@ private boolean isElementKeywordOrArchetype(String token, String keyword) { } } - private void addArchetype(Archetype archetype) { - archetypes.get(archetype.getType()).put(archetype.getName(), archetype); - } - - private void extendArchetype(Archetype archetype, String archetypeName) { - archetypeName = archetypeName.toLowerCase(); - Archetype parentArchetype = archetypes.get(archetype.getType()).get(archetypeName); - if (parentArchetype != null) { - archetype.setDescription(parentArchetype.getDescription()); - archetype.setTechnology(parentArchetype.getTechnology()); - archetype.addTags(parentArchetype.getTags().toArray(new String[0])); + private boolean isRelationshipKeywordOrArchetype(String token) { + if (features.isEnabled(Features.ARCHETYPES)) { + if (token.equalsIgnoreCase(RELATIONSHIP_TOKEN)) { + return true; + } else if (token.startsWith(RELATIONSHIP_ARCHETYPE_PREFIX) && token.endsWith(RELATIONSHIP_ARCHETYPE_SUFFIX)) { + token = token.substring(RELATIONSHIP_ARCHETYPE_PREFIX.length(), token.length()-RELATIONSHIP_ARCHETYPE_SUFFIX.length()); + return (archetypes.get(RELATIONSHIP_TOKEN).containsKey(token.toLowerCase())); + } } - } - private void applyArchetype(String archetypeName, Person person) { - Archetype archetype = archetypes.get(StructurizrDslTokens.PERSON_TOKEN).get(archetypeName.toLowerCase()); - if (archetype != null) { - person.setDescription(archetype.getDescription()); - person.addTags(archetype.getTags().toArray(new String[0])); - } + return token.equalsIgnoreCase(RELATIONSHIP_TOKEN); } - private void applyArchetype(String archetypeName, SoftwareSystem softwareSystem) { - Archetype archetype = archetypes.get(StructurizrDslTokens.SOFTWARE_SYSTEM_TOKEN).get(archetypeName.toLowerCase()); - if (archetype != null) { - softwareSystem.setDescription(archetype.getDescription()); - softwareSystem.addTags(archetype.getTags().toArray(new String[0])); - } + private void addArchetype(Archetype archetype) { + archetypes.get(archetype.getType()).put(archetype.getName(), archetype); } - private void applyArchetype(String archetypeName, Container container) { - Archetype archetype = archetypes.get(StructurizrDslTokens.CONTAINER_TOKEN).get(archetypeName.toLowerCase()); - if (archetype != null) { - container.setTechnology(archetype.getTechnology()); - container.setDescription(archetype.getDescription()); - container.addTags(archetype.getTags().toArray(new String[0])); - } - } + private Archetype getArchetype(String archetypeType, String archetypeName) { + Archetype archetype = null; - private void applyArchetype(String archetypeName, Component component) { - Archetype archetype = archetypes.get(StructurizrDslTokens.COMPONENT_TOKEN).get(archetypeName.toLowerCase()); - if (archetype != null) { - component.setTechnology(archetype.getTechnology()); - component.setDescription(archetype.getDescription()); - component.addTags(archetype.getTags().toArray(new String[0])); + if (features.isEnabled(Features.ARCHETYPES)) { + if (RELATIONSHIP_TOKEN.equals(archetypeType)) { + if (archetypeName.startsWith(RELATIONSHIP_ARCHETYPE_PREFIX) && archetypeName.endsWith(RELATIONSHIP_ARCHETYPE_SUFFIX)) { + archetypeName = archetypeName.substring(RELATIONSHIP_ARCHETYPE_PREFIX.length(), archetypeName.length() - RELATIONSHIP_ARCHETYPE_SUFFIX.length()); + } + } + archetype = archetypes.get(archetypeType).get(archetypeName.toLowerCase()); } - } - private void applyArchetype(String archetypeName, DeploymentNode deploymentNode) { - Archetype archetype = archetypes.get(StructurizrDslTokens.DEPLOYMENT_NODE_TOKEN).get(archetypeName.toLowerCase()); - if (archetype != null) { - deploymentNode.setTechnology(archetype.getTechnology()); - deploymentNode.setDescription(archetype.getDescription()); - deploymentNode.addTags(archetype.getTags().toArray(new String[0])); + if (archetype == null) { + archetype = new Archetype(archetypeName, archetypeType); } + + return archetype; } - private void applyArchetype(String archetypeName, InfrastructureNode infrastructureNode) { - Archetype archetype = archetypes.get(StructurizrDslTokens.INFRASTRUCTURE_NODE_TOKEN).get(archetypeName.toLowerCase()); - if (archetype != null) { - infrastructureNode.setTechnology(archetype.getTechnology()); - infrastructureNode.setDescription(archetype.getDescription()); - infrastructureNode.addTags(archetype.getTags().toArray(new String[0])); + private void extendArchetype(Archetype archetype, String archetypeName) { + archetypeName = archetypeName.toLowerCase(); + Archetype parentArchetype = archetypes.get(archetype.getType()).get(archetypeName); + if (parentArchetype != null) { + archetype.setDescription(parentArchetype.getDescription()); + archetype.setTechnology(parentArchetype.getTechnology()); + archetype.addTags(parentArchetype.getTags().toArray(new String[0])); } } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java index f44de980b..fef341aad 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java @@ -132,4 +132,7 @@ class StructurizrDslTokens { static final String COMPONENT_FINDER_STRATEGY_URL_TOKEN = "url"; static final String COMPONENT_FINDER_STRATEGY_FOREACH_TOKEN = "forEach"; + static final String RELATIONSHIP_ARCHETYPE_PREFIX = "--"; + static final String RELATIONSHIP_ARCHETYPE_SUFFIX = "->"; + } \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentParserTests.java index 77e4993f2..77ddc23fa 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentParserTests.java @@ -10,11 +10,12 @@ class ComponentParserTests extends AbstractTests { private ComponentParser parser = new ComponentParser(); + private Archetype archetype = new Archetype("name", "type"); @Test void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { try { - parser.parse(new ContainerDslContext(null), tokens("container", "name", "description", "technology", "tags", "extra")); + parser.parse(new ContainerDslContext(null), tokens("container", "name", "description", "technology", "tags", "extra"), archetype); fail(); } catch (Exception e) { assertEquals("Too many tokens, expected: component [description] [technology] [tags]", e.getMessage()); @@ -24,7 +25,7 @@ void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { @Test void test_parse_ThrowsAnException_WhenTheNameIsNotSpecified() { try { - parser.parse(new ContainerDslContext(null), tokens("container")); + parser.parse(new ContainerDslContext(null), tokens("container"), archetype); fail(); } catch (Exception e) { assertEquals("Expected: component [description] [technology] [tags]", e.getMessage()); @@ -36,13 +37,13 @@ void test_parse_CreatesAComponent() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); Container container = softwareSystem.addContainer("Container", "Description", "Technology"); ContainerDslContext context = new ContainerDslContext(container); - parser.parse(context, tokens("component", "Name")); + parser.parse(context, tokens("component", "Name"), archetype); assertEquals(3, model.getElements().size()); Component component = container.getComponentWithName("Name"); assertNotNull(component); assertEquals("", component.getDescription()); - assertEquals(null, component.getTechnology()); + assertEquals("", component.getTechnology()); assertEquals("Element,Component", component.getTags()); } @@ -51,13 +52,13 @@ void test_parse_CreatesAComponentWithADescription() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); Container container = softwareSystem.addContainer("Container", "Description", "Technology"); ContainerDslContext context = new ContainerDslContext(container); - parser.parse(context, tokens("component", "Name", "Description")); + parser.parse(context, tokens("component", "Name", "Description"), archetype); assertEquals(3, model.getElements().size()); Component component = container.getComponentWithName("Name"); assertNotNull(component); assertEquals("Description", component.getDescription()); - assertEquals(null, component.getTechnology()); + assertEquals("", component.getTechnology()); assertEquals("Element,Component", component.getTags()); } @@ -66,7 +67,7 @@ void test_parse_CreatesAComponentWithADescriptionAndTechnology() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); Container container = softwareSystem.addContainer("Container", "Description", "Technology"); ContainerDslContext context = new ContainerDslContext(container); - parser.parse(context, tokens("component", "Name", "Description", "Technology")); + parser.parse(context, tokens("component", "Name", "Description", "Technology"), archetype); assertEquals(3, model.getElements().size()); Component component = container.getComponentWithName("Name"); @@ -81,7 +82,7 @@ void test_parse_CreatesAComponentWithADescriptionAndTechnologyAndTags() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); Container container = softwareSystem.addContainer("Container", "Description", "Technology"); ContainerDslContext context = new ContainerDslContext(container); - parser.parse(context, tokens("component", "Name", "Description", "Technology", "Tag 1, Tag 2")); + parser.parse(context, tokens("component", "Name", "Description", "Technology", "Tag 1, Tag 2"), archetype); assertEquals(3, model.getElements().size()); Component component = container.getComponentWithName("Name"); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ContainerParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ContainerParserTests.java index 79fc43a77..758f7503c 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ContainerParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ContainerParserTests.java @@ -9,11 +9,12 @@ class ContainerParserTests extends AbstractTests { private ContainerParser parser = new ContainerParser(); + private Archetype archetype = new Archetype("name", "type"); @Test void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { try { - parser.parse(new SoftwareSystemDslContext(null), tokens("container", "name", "description", "technology", "tags", "extra")); + parser.parse(new SoftwareSystemDslContext(null), tokens("container", "name", "description", "technology", "tags", "extra"), archetype); fail(); } catch (Exception e) { assertEquals("Too many tokens, expected: container [description] [technology] [tags]", e.getMessage()); @@ -23,7 +24,7 @@ void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { @Test void test_parse_ThrowsAnException_WhenTheNameIsNotSpecified() { try { - parser.parse(new SoftwareSystemDslContext(null), tokens("container")); + parser.parse(new SoftwareSystemDslContext(null), tokens("container"), archetype); fail(); } catch (Exception e) { assertEquals("Expected: container [description] [technology] [tags]", e.getMessage()); @@ -34,7 +35,7 @@ void test_parse_ThrowsAnException_WhenTheNameIsNotSpecified() { void test_parse_CreatesAContainer() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); SoftwareSystemDslContext context = new SoftwareSystemDslContext(softwareSystem); - parser.parse(context, tokens("container", "Name")); + parser.parse(context, tokens("container", "Name"), archetype); assertEquals(2, model.getElements().size()); Container container = softwareSystem.getContainerWithName("Name"); @@ -48,7 +49,7 @@ void test_parse_CreatesAContainer() { void test_parse_CreatesAContainerWithADescription() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); SoftwareSystemDslContext context = new SoftwareSystemDslContext(softwareSystem); - parser.parse(context, tokens("container", "Name", "Description")); + parser.parse(context, tokens("container", "Name", "Description"), archetype); assertEquals(2, model.getElements().size()); Container container = softwareSystem.getContainerWithName("Name"); @@ -62,7 +63,7 @@ void test_parse_CreatesAContainerWithADescription() { void test_parse_CreatesAContainerWithADescriptionAndTechnology() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); SoftwareSystemDslContext context = new SoftwareSystemDslContext(softwareSystem); - parser.parse(context, tokens("container", "Name", "Description", "Technology")); + parser.parse(context, tokens("container", "Name", "Description", "Technology"), archetype); assertEquals(2, model.getElements().size()); Container container = softwareSystem.getContainerWithName("Name"); @@ -76,7 +77,7 @@ void test_parse_CreatesAContainerWithADescriptionAndTechnology() { void test_parse_CreatesAContainerWithADescriptionAndTechnologyAndTags() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); SoftwareSystemDslContext context = new SoftwareSystemDslContext(softwareSystem); - parser.parse(context, tokens("container", "Name", "Description", "Technology", "Tag 1, Tag 2")); + parser.parse(context, tokens("container", "Name", "Description", "Technology", "Tag 1, Tag 2"), archetype); assertEquals(2, model.getElements().size()); Container container = softwareSystem.getContainerWithName("Name"); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentNodeParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentNodeParserTests.java index d9ec9be15..21bbd052c 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentNodeParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentNodeParserTests.java @@ -9,11 +9,12 @@ class DeploymentNodeParserTests extends AbstractTests { private DeploymentNodeParser parser = new DeploymentNodeParser(); + private Archetype archetype = new Archetype("name", "type"); @Test void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { try { - parser.parse(new DeploymentEnvironmentDslContext("env"), tokens("deploymentNode", "name", "description", "technology", "tags", "instances", "extra")); + parser.parse(new DeploymentEnvironmentDslContext("env"), tokens("deploymentNode", "name", "description", "technology", "tags", "instances", "extra"), archetype); fail(); } catch (Exception e) { assertEquals("Too many tokens, expected: deploymentNode [description] [technology] [tags] [instances] {", e.getMessage()); @@ -23,7 +24,7 @@ void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { @Test void test_parse_ThrowsAnException_WhenTheNameIsNotSpecified() { try { - parser.parse(new DeploymentEnvironmentDslContext("env"), tokens("deploymentNode")); + parser.parse(new DeploymentEnvironmentDslContext("env"), tokens("deploymentNode"), archetype); fail(); } catch (Exception e) { assertEquals("Expected: deploymentNode [description] [technology] [tags] [instances] {", e.getMessage()); @@ -34,7 +35,7 @@ void test_parse_ThrowsAnException_WhenTheNameIsNotSpecified() { void test_parse_CreatesADeploymentNode() { DeploymentEnvironmentDslContext context = new DeploymentEnvironmentDslContext("Live"); context.setWorkspace(workspace); - parser.parse(context, tokens("deploymentNode", "Name")); + parser.parse(context, tokens("deploymentNode", "Name"), archetype); assertEquals(1, model.getElements().size()); DeploymentNode deploymentNode = model.getDeploymentNodeWithName("Name", "Live"); @@ -50,7 +51,7 @@ void test_parse_CreatesADeploymentNode() { void test_parse_CreatesADeploymentNodeWithADescription() { DeploymentEnvironmentDslContext context = new DeploymentEnvironmentDslContext("Live"); context.setWorkspace(workspace); - parser.parse(context, tokens("deploymentNode", "Name", "Description")); + parser.parse(context, tokens("deploymentNode", "Name", "Description"), archetype); assertEquals(1, model.getElements().size()); DeploymentNode deploymentNode = model.getDeploymentNodeWithName("Name", "Live"); @@ -66,7 +67,7 @@ void test_parse_CreatesADeploymentNodeWithADescription() { void test_parse_CreatesADeploymentNodeWithADescriptionAndTechnology() { DeploymentEnvironmentDslContext context = new DeploymentEnvironmentDslContext("Live"); context.setWorkspace(workspace); - parser.parse(context, tokens("deploymentNode", "Name", "Description", "Technology")); + parser.parse(context, tokens("deploymentNode", "Name", "Description", "Technology"), archetype); assertEquals(1, model.getElements().size()); DeploymentNode deploymentNode = model.getDeploymentNodeWithName("Name", "Live"); @@ -82,7 +83,7 @@ void test_parse_CreatesADeploymentNodeWithADescriptionAndTechnology() { void test_parse_CreatesADeploymentNodeWithADescriptionAndTechnologyAndTags() { DeploymentEnvironmentDslContext context = new DeploymentEnvironmentDslContext("Live"); context.setWorkspace(workspace); - parser.parse(context, tokens("deploymentNode", "Name", "Description", "Technology", "Tag 1, Tag 2")); + parser.parse(context, tokens("deploymentNode", "Name", "Description", "Technology", "Tag 1, Tag 2"), archetype); assertEquals(1, model.getElements().size()); DeploymentNode deploymentNode = model.getDeploymentNodeWithName("Name", "Live"); @@ -98,7 +99,7 @@ void test_parse_CreatesADeploymentNodeWithADescriptionAndTechnologyAndTags() { void test_parse_CreatesADeploymentNodeWithADescriptionAndTechnologyAndTagsAndInstances() { DeploymentEnvironmentDslContext context = new DeploymentEnvironmentDslContext("Live"); context.setWorkspace(workspace); - parser.parse(context, tokens("deploymentNode", "Name", "Description", "Technology", "Tag 1, Tag 2", "8")); + parser.parse(context, tokens("deploymentNode", "Name", "Description", "Technology", "Tag 1, Tag 2", "8"), archetype); assertEquals(1, model.getElements().size()); DeploymentNode deploymentNode = model.getDeploymentNodeWithName("Name", "Live"); @@ -116,7 +117,7 @@ void test_parse_ThrowsAnException_WhenTheNumberOfInstancesIsNotValid() { context.setWorkspace(workspace); try { - parser.parse(context, tokens("deploymentNode", "Name", "Description", "Technology", "Tag 1, Tag 2", "abc")); + parser.parse(context, tokens("deploymentNode", "Name", "Description", "Technology", "Tag 1, Tag 2", "abc"), archetype); System.out.println(model.getDeploymentNodes().iterator().next().getInstances()); fail(); } catch (Exception e) { @@ -129,7 +130,7 @@ void test_parse_CreatesAChildDeploymentNode() { DeploymentNode parent = model.addDeploymentNode("Live", "Parent", "Description", "Technology"); DeploymentNodeDslContext context = new DeploymentNodeDslContext(parent); context.setWorkspace(workspace); - parser.parse(context, tokens("deploymentNode", "Name")); + parser.parse(context, tokens("deploymentNode", "Name"), archetype); assertEquals(2, model.getElements().size()); DeploymentNode deploymentNode = parent.getDeploymentNodeWithName("Name"); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index d85175225..4b33251a2 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -1483,6 +1483,55 @@ void test_archetypes_WhenEnabled() throws Exception { assertTrue(customerApi.getTagsAsSet().contains("Application")); assertTrue(customerApi.getTagsAsSet().contains("Spring Boot")); assertEquals("Spring Boot", customerApi.getTechnology()); + + Relationship relationship = workspace.getModel().getSoftwareSystemWithName("A").getEfferentRelationshipWith(workspace.getModel().getSoftwareSystemWithName("X")); + assertEquals("HTTPS", relationship.getTechnology()); + } + + @Test + void test_archetypesForDefaults() throws Exception { + File parentDslFile = new File("src/test/resources/dsl/archetypes-for-defaults.dsl"); + StructurizrDslParser parser = new StructurizrDslParser(); + parser.getFeatures().enable(Features.ARCHETYPES); + parser.parse(parentDslFile); + Workspace workspace = parser.getWorkspace(); + + SoftwareSystem a = workspace.getModel().getSoftwareSystemWithName("A"); + assertEquals("Default Description", a.getDescription()); + assertTrue(a.hasTag("Default Tag")); + + SoftwareSystem b = workspace.getModel().getSoftwareSystemWithName("B"); + assertEquals("Default Description", b.getDescription()); + assertTrue(b.hasTag("Default Tag")); + + Relationship r = a.getEfferentRelationshipWith(b); + assertEquals("Default Description", r.getDescription()); + assertEquals("Default Technology", r.getTechnology()); + assertTrue(r.hasTag("Default Tag")); + } + + @Test + void test_archetypesForExtension() throws Exception { + File parentDslFile = new File("src/test/resources/dsl/archetypes-for-extension.dsl"); + StructurizrDslParser parser = new StructurizrDslParser(); + parser.getFeatures().enable(Features.ARCHETYPES); + parser.parse(parentDslFile); + Workspace workspace = parser.getWorkspace(); + + SoftwareSystem a = workspace.getModel().getSoftwareSystemWithName("A"); + assertEquals("Description of A.", a.getDescription()); + assertTrue(a.hasTag("Default Tag")); + + SoftwareSystem b = workspace.getModel().getSoftwareSystemWithName("B"); + assertEquals("Description of B.", b.getDescription()); + assertTrue(b.hasTag("Default Tag")); + assertTrue(b.hasTag("External Software System")); + + Relationship r = a.getEfferentRelationshipWith(b); + assertEquals("Makes API calls to", r.getDescription()); + assertEquals("HTTPS", r.getTechnology()); + assertTrue(r.hasTag("Default Tag")); + assertTrue(r.hasTag("HTTPS")); } } \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ExplicitRelationshipParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ExplicitRelationshipParserTests.java index 1c9b5c401..0ac671673 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ExplicitRelationshipParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ExplicitRelationshipParserTests.java @@ -8,11 +8,12 @@ class ExplicitRelationshipParserTests extends AbstractTests { private ExplicitRelationshipParser parser = new ExplicitRelationshipParser(); + private Archetype archetype = new Archetype("name", "type"); @Test void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { try { - parser.parse(context(), tokens("source", "->", "destination", "description", "technology", "tags", "extra")); + parser.parse(context(), tokens("source", "->", "destination", "description", "technology", "tags", "extra"), archetype); fail(); } catch (Exception e) { assertEquals("Too many tokens, expected: -> [description] [technology] [tags]", e.getMessage()); @@ -22,7 +23,7 @@ void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { @Test void test_parse_ThrowsAnException_WhenTheDestinationIdentifierIsMissing() { try { - parser.parse(context(), tokens("source", "->")); + parser.parse(context(), tokens("source", "->"), archetype); fail(); } catch (Exception e) { assertEquals("Expected: -> [description] [technology] [tags]", e.getMessage()); @@ -32,7 +33,7 @@ void test_parse_ThrowsAnException_WhenTheDestinationIdentifierIsMissing() { @Test void test_parse_ThrowsAnException_WhenTheSourceElementIsNotDefined() { try { - parser.parse(context(), tokens("source", "->", "destination")); + parser.parse(context(), tokens("source", "->", "destination"), archetype); fail(); } catch (Exception e) { assertEquals("The source element \"source\" does not exist", e.getMessage()); @@ -47,7 +48,7 @@ void test_parse_ThrowsAnException_WhenTheDestinationElementIsNotDefined() { context.setIdentifierRegister(elements); try { - parser.parse(context, tokens("source", "->", "destination")); + parser.parse(context, tokens("source", "->", "destination"), archetype); fail(); } catch (Exception e) { assertEquals("The destination element \"destination\" does not exist", e.getMessage()); @@ -67,7 +68,7 @@ void test_parse_AddsTheRelationship() { assertEquals(0, model.getRelationships().size()); - parser.parse(context, tokens("source", "->", "destination")); + parser.parse(context, tokens("source", "->", "destination"), archetype); assertEquals(1, model.getRelationships().size()); Relationship r = model.getRelationships().iterator().next(); @@ -91,7 +92,7 @@ void test_parse_AddsTheRelationshipWithADescription() { assertEquals(0, model.getRelationships().size()); - parser.parse(context, tokens("source", "->", "destination", "Uses")); + parser.parse(context, tokens("source", "->", "destination", "Uses"), archetype); assertEquals(1, model.getRelationships().size()); Relationship r = model.getRelationships().iterator().next(); @@ -115,7 +116,7 @@ void test_parse_AddsTheRelationshipWithADescriptionAndTechnology() { assertEquals(0, model.getRelationships().size()); - parser.parse(context, tokens("source", "->", "destination", "Uses", "HTTP")); + parser.parse(context, tokens("source", "->", "destination", "Uses", "HTTP"), archetype); assertEquals(1, model.getRelationships().size()); Relationship r = model.getRelationships().iterator().next(); @@ -138,7 +139,7 @@ void test_parse_AddsTheRelationshipWithADescriptionAndTechnologyAndTags() { assertEquals(0, model.getRelationships().size()); - parser.parse(context, tokens("source", "->", "destination", "Uses", "HTTP", "Tag 1,Tag 2")); + parser.parse(context, tokens("source", "->", "destination", "Uses", "HTTP", "Tag 1,Tag 2"), archetype); assertEquals(1, model.getRelationships().size()); Relationship r = model.getRelationships().iterator().next(); @@ -163,7 +164,7 @@ void test_parse_AddsTheRelationshipAndImplicitRelationshipsWithADescriptionAndTe assertEquals(0, model.getRelationships().size()); - parser.parse(context, tokens("source", "->", "destination", "Uses", "HTTP", "Tag 1,Tag 2")); + parser.parse(context, tokens("source", "->", "destination", "Uses", "HTTP", "Tag 1,Tag 2"), archetype); assertEquals(2, model.getRelationships().size()); // this is the relationship that was created @@ -195,7 +196,7 @@ void test_parse_AddsTheRelationship_WithASourceOfThis() { assertEquals(0, model.getRelationships().size()); - parser.parse(context, tokens("this", "->", "destination")); + parser.parse(context, tokens("this", "->", "destination"), archetype); assertEquals(1, model.getRelationships().size()); Relationship r = model.getRelationships().iterator().next(); @@ -218,7 +219,7 @@ void test_parse_AddsTheRelationship_WithADestinationOfThis() { assertEquals(0, model.getRelationships().size()); - parser.parse(context, tokens("source", "->", "this")); + parser.parse(context, tokens("source", "->", "this"), archetype); assertEquals(1, model.getRelationships().size()); Relationship r = model.getRelationships().iterator().next(); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ImplicitRelationshipParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ImplicitRelationshipParserTests.java index c0786cf19..a84c7fd06 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ImplicitRelationshipParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ImplicitRelationshipParserTests.java @@ -8,6 +8,7 @@ class ImplicitRelationshipParserTests extends AbstractTests { private ImplicitRelationshipParser parser = new ImplicitRelationshipParser(); + private Archetype archetype = new Archetype("name", "type"); private ElementDslContext context(Person person) { PersonDslContext context = new PersonDslContext(person); @@ -20,7 +21,7 @@ private ElementDslContext context(Person person) { @Test void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { try { - parser.parse((ElementDslContext)null, tokens("->", "destination", "description", "technology", "tags", "extra")); + parser.parse((ElementDslContext)null, tokens("->", "destination", "description", "technology", "tags", "extra"), archetype); fail(); } catch (Exception e) { assertEquals("Too many tokens, expected: -> [description] [technology] [tags]", e.getMessage()); @@ -30,7 +31,7 @@ void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { @Test void test_parse_ThrowsAnException_WhenTheDestinationIdentifierIsMissing() { try { - parser.parse((ElementDslContext)null, tokens("->")); + parser.parse((ElementDslContext)null, tokens("->"), archetype); fail(); } catch (Exception e) { assertEquals("Expected: -> [description] [technology] [tags]", e.getMessage()); @@ -45,7 +46,7 @@ void test_parse_ThrowsAnException_WhenTheDestinationElementIsNotDefined() { context.setIdentifierRegister(elements); try { - parser.parse(context, tokens("->", "destination")); + parser.parse(context, tokens("->", "destination"), archetype); fail(); } catch (Exception e) { assertEquals("The destination element \"destination\" does not exist", e.getMessage()); @@ -64,7 +65,7 @@ void test_parse_AddsTheRelationship() { assertEquals(0, model.getRelationships().size()); - parser.parse(context, tokens("->", "destination")); + parser.parse(context, tokens("->", "destination"), archetype); assertEquals(1, model.getRelationships().size()); Relationship r = model.getRelationships().iterator().next(); @@ -87,7 +88,7 @@ void test_parse_AddsTheRelationshipWithADescription() { assertEquals(0, model.getRelationships().size()); - parser.parse(context, tokens("->", "destination", "Uses")); + parser.parse(context, tokens("->", "destination", "Uses"), archetype); assertEquals(1, model.getRelationships().size()); Relationship r = model.getRelationships().iterator().next(); @@ -110,7 +111,7 @@ void test_parse_AddsTheRelationshipWithADescriptionAndTechnology() { assertEquals(0, model.getRelationships().size()); - parser.parse(context, tokens("->", "destination", "Uses", "HTTP")); + parser.parse(context, tokens("->", "destination", "Uses", "HTTP"), archetype); assertEquals(1, model.getRelationships().size()); Relationship r = model.getRelationships().iterator().next(); @@ -132,7 +133,7 @@ void test_parse_AddsTheRelationshipWithADescriptionAndTechnologyAndTags() { assertEquals(0, model.getRelationships().size()); - parser.parse(context, tokens("->", "destination", "Uses", "HTTP", "Tag 1,Tag 2")); + parser.parse(context, tokens("->", "destination", "Uses", "HTTP", "Tag 1,Tag 2"), archetype); assertEquals(1, model.getRelationships().size()); Relationship r = model.getRelationships().iterator().next(); @@ -156,7 +157,7 @@ void test_parse_AddsTheRelationshipAndImplicitRelationshipsWithADescriptionAndTe assertEquals(0, model.getRelationships().size()); - parser.parse(context, tokens("->", "destination", "Uses", "HTTP", "Tag 1,Tag 2")); + parser.parse(context, tokens("->", "destination", "Uses", "HTTP", "Tag 1,Tag 2"), archetype); assertEquals(2, model.getRelationships().size()); // this is the relationship that was created diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/InfrastructureNodeParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/InfrastructureNodeParserTests.java index 9c0aa769c..d631709cc 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/InfrastructureNodeParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/InfrastructureNodeParserTests.java @@ -9,11 +9,12 @@ class InfrastructureNodeParserTests extends AbstractTests { private InfrastructureNodeParser parser = new InfrastructureNodeParser(); + private Archetype archetype = new Archetype("name", "type"); @Test void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { try { - parser.parse(new DeploymentNodeDslContext(null), tokens("infrastructureNode", "name", "description", "technology", "tags", "extra")); + parser.parse(new DeploymentNodeDslContext(null), tokens("infrastructureNode", "name", "description", "technology", "tags", "extra"), archetype); fail(); } catch (Exception e) { assertEquals("Too many tokens, expected: infrastructureNode [description] [technology] [tags]", e.getMessage()); @@ -23,7 +24,7 @@ void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { @Test void test_parse_ThrowsAnException_WhenTheNameIsNotSpecified() { try { - parser.parse(new DeploymentNodeDslContext(null), tokens("infrastructureNode")); + parser.parse(new DeploymentNodeDslContext(null), tokens("infrastructureNode"), archetype); fail(); } catch (Exception e) { assertEquals("Expected: infrastructureNode [description] [technology] [tags]", e.getMessage()); @@ -35,7 +36,7 @@ void test_parse_CreatesAnInfrastructureNode() { DeploymentNode deploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); DeploymentNodeDslContext context = new DeploymentNodeDslContext(deploymentNode); - parser.parse(context, tokens("infrastructureNode", "Name")); + parser.parse(context, tokens("infrastructureNode", "Name"), archetype); assertEquals(2, model.getElements().size()); assertEquals(1, deploymentNode.getInfrastructureNodes().size()); @@ -52,7 +53,7 @@ void test_parse_CreatesAnInfrastructureNodeWithADescription() { DeploymentNode deploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); DeploymentNodeDslContext context = new DeploymentNodeDslContext(deploymentNode); - parser.parse(context, tokens("infrastructureNode", "Name", "Description")); + parser.parse(context, tokens("infrastructureNode", "Name", "Description"), archetype); assertEquals(2, model.getElements().size()); assertEquals(1, deploymentNode.getInfrastructureNodes().size()); @@ -69,7 +70,7 @@ void test_parse_CreatesAnInfrastructureNodeWithADescriptionAndTechnology() { DeploymentNode deploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); DeploymentNodeDslContext context = new DeploymentNodeDslContext(deploymentNode); - parser.parse(context, tokens("infrastructureNode", "Name", "Description", "Technology")); + parser.parse(context, tokens("infrastructureNode", "Name", "Description", "Technology"), archetype); assertEquals(2, model.getElements().size()); assertEquals(1, deploymentNode.getInfrastructureNodes().size()); @@ -86,7 +87,7 @@ void test_parse_CreatesAnInfrastructureNodeWithADescriptionAndTechnologyAndTags( DeploymentNode deploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); DeploymentNodeDslContext context = new DeploymentNodeDslContext(deploymentNode); - parser.parse(context, tokens("infrastructureNode", "Name", "Description", "Technology", "Tag 1, Tag 2")); + parser.parse(context, tokens("infrastructureNode", "Name", "Description", "Technology", "Tag 1, Tag 2"), archetype); assertEquals(2, model.getElements().size()); assertEquals(1, deploymentNode.getInfrastructureNodes().size()); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/PersonParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/PersonParserTests.java index ed6454623..dac705b99 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/PersonParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/PersonParserTests.java @@ -8,11 +8,12 @@ class PersonParserTests extends AbstractTests { private final PersonParser parser = new PersonParser(); + private Archetype archetype = new Archetype("name", "type"); @Test void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { try { - parser.parse(context(), tokens("person", "name", "description", "tags", "tokens")); + parser.parse(context(), tokens("person", "name", "description", "tags", "tokens"), archetype); fail(); } catch (Exception e) { assertEquals("Too many tokens, expected: person [description] [tags]", e.getMessage()); @@ -22,7 +23,7 @@ void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { @Test void test_parse_ThrowsAnException_WhenTheNameIsNotSpecified() { try { - parser.parse(context(), tokens("person")); + parser.parse(context(), tokens("person"), archetype); fail(); } catch (Exception e) { assertEquals("Expected: person [description] [tags]", e.getMessage()); @@ -31,7 +32,7 @@ void test_parse_ThrowsAnException_WhenTheNameIsNotSpecified() { @Test void test_parse_CreatesAPerson() { - parser.parse(context(), tokens("person", "User")); + parser.parse(context(), tokens("person", "User"), archetype); assertEquals(1, model.getElements().size()); Person user = model.getPersonWithName("User"); @@ -42,7 +43,7 @@ void test_parse_CreatesAPerson() { @Test void test_parse_CreatesAPersonWithADescription() { - parser.parse(context(), tokens("person", "User", "Description")); + parser.parse(context(), tokens("person", "User", "Description"), archetype); assertEquals(1, model.getElements().size()); Person user = model.getPersonWithName("User"); @@ -53,7 +54,7 @@ void test_parse_CreatesAPersonWithADescription() { @Test void test_parse_CreatesAPersonWithADescriptionAndTags() { - parser.parse(context(), tokens("person", "User", "Description", "Tag 1, Tag 2")); + parser.parse(context(), tokens("person", "User", "Description", "Tag 1, Tag 2"), archetype); assertEquals(1, model.getElements().size()); Person user = model.getPersonWithName("User"); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/SoftwareSystemParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/SoftwareSystemParserTests.java index d11724335..b33974520 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/SoftwareSystemParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/SoftwareSystemParserTests.java @@ -8,11 +8,12 @@ class SoftwareSystemParserTests extends AbstractTests { private final SoftwareSystemParser parser = new SoftwareSystemParser(); + private Archetype archetype = new Archetype("name", "type"); @Test void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { try { - parser.parse(context(), tokens("softwareSystem", "name", "description", "tags", "extra")); + parser.parse(context(), tokens("softwareSystem", "name", "description", "tags", "extra"), archetype); fail(); } catch (Exception e) { assertEquals("Too many tokens, expected: softwareSystem [description] [tags]", e.getMessage()); @@ -22,7 +23,7 @@ void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { @Test void test_parse_ThrowsAnException_WhenTheNameIsNotSpecified() { try { - parser.parse(context(), tokens("softwareSystem")); + parser.parse(context(), tokens("softwareSystem"), archetype); fail(); } catch (Exception e) { assertEquals("Expected: softwareSystem [description] [tags]", e.getMessage()); @@ -31,7 +32,7 @@ void test_parse_ThrowsAnException_WhenTheNameIsNotSpecified() { @Test void test_parse_CreatesASoftwareSystem() { - parser.parse(context(), tokens("softwareSystem", "Name")); + parser.parse(context(), tokens("softwareSystem", "Name"), archetype); assertEquals(1, model.getElements().size()); SoftwareSystem softwareSystem = model.getSoftwareSystemWithName("Name"); @@ -42,7 +43,7 @@ void test_parse_CreatesASoftwareSystem() { @Test void test_parse_CreatesASoftwareSystemWithADescription() { - parser.parse(context(), tokens("softwareSystem", "Name", "Description")); + parser.parse(context(), tokens("softwareSystem", "Name", "Description"), archetype); assertEquals(1, model.getElements().size()); SoftwareSystem softwareSystem = model.getSoftwareSystemWithName("Name"); @@ -53,7 +54,7 @@ void test_parse_CreatesASoftwareSystemWithADescription() { @Test void test_parse_CreatesASoftwareSystemWithADescriptionAndTags() { - parser.parse(context(), tokens("softwareSystem", "Name", "Description", "Tag 1, Tag 2")); + parser.parse(context(), tokens("softwareSystem", "Name", "Description", "Tag 1, Tag 2"), archetype); assertEquals(1, model.getElements().size()); SoftwareSystem softwareSystem = model.getSoftwareSystemWithName("Name"); diff --git a/structurizr-dsl/src/test/resources/dsl/archetypes-for-defaults.dsl b/structurizr-dsl/src/test/resources/dsl/archetypes-for-defaults.dsl new file mode 100644 index 000000000..63ed7ce07 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/archetypes-for-defaults.dsl @@ -0,0 +1,22 @@ +workspace { + + model { + archetypes { + softwaresystem { + description "Default Description" + tag "Default Tag" + } + + -> { + description "Default Description" + technology "Default Technology" + tag "Default Tag" + } + } + + a = softwareSystem "A" + b = softwareSystem "B" + a -> b + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/archetypes-for-extension.dsl b/structurizr-dsl/src/test/resources/dsl/archetypes-for-extension.dsl new file mode 100644 index 000000000..ac5116163 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/archetypes-for-extension.dsl @@ -0,0 +1,29 @@ +workspace { + + model { + archetypes { + softwaresystem { + tag "Default Tag" + } + + externalSoftwareSystem = softwareSystem { + tag "External Software System" + } + + -> { + technology "Default Technology" + tag "Default Tag" + } + + https = -> { + technology "HTTPS" + tag "HTTPS" + } + } + + a = softwareSystem "A" "Description of A." + b = externalSoftwareSystem "B" "Description of B." + a --https-> b "Makes API calls to" + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/archetypes.dsl b/structurizr-dsl/src/test/resources/dsl/archetypes.dsl index d7524e06e..d892dcb5d 100644 --- a/structurizr-dsl/src/test/resources/dsl/archetypes.dsl +++ b/structurizr-dsl/src/test/resources/dsl/archetypes.dsl @@ -24,13 +24,21 @@ workspace { technology "Spring Data Repository" tag "Spring Data Repository" } + + https = -> { + technology "HTTPS" + } } + a = softwareSystem "A" + x = softwareSystem "X" { customerService = microservice "Customer Service" { db = datastore "Customer database" api = springBootApplication "Customer API" { - customerController = restController "Customer Controller" + customerController = restController "Customer Controller" { + a --https-> this "Makes API calls using" + } customerRepository = repository "Customer Repository" { customerController -> this this -> db From 1d23a0d62648def03a10737547d2dd160c199e6e Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Tue, 18 Feb 2025 14:44:00 +0000 Subject: [PATCH 622/717] Adds properties to archetypes. --- .../java/com/structurizr/model/ModelItem.java | 11 ++++++ .../java/com/structurizr/dsl/Archetype.java | 37 +++++++++++++++++-- .../dsl/ComponentArchetypeDslContext.java | 1 + .../com/structurizr/dsl/ComponentParser.java | 2 + .../dsl/ContainerArchetypeDslContext.java | 1 + .../com/structurizr/dsl/ContainerParser.java | 2 + .../DeploymentNodeArchetypeDslContext.java | 1 + .../structurizr/dsl/DeploymentNodeParser.java | 4 ++ .../dsl/ExplicitRelationshipParser.java | 10 ++++- .../dsl/ImplicitRelationshipParser.java | 10 ++++- ...InfrastructureNodeArchetypeDslContext.java | 1 + .../dsl/InfrastructureNodeParser.java | 2 + .../dsl/PersonArchetypeDslContext.java | 1 + .../com/structurizr/dsl/PersonParser.java | 2 + .../dsl/RelationshipArchetypeDslContext.java | 1 + .../SoftwareSystemArchetypeDslContext.java | 1 + .../structurizr/dsl/SoftwareSystemParser.java | 2 + .../structurizr/dsl/StructurizrDslParser.java | 7 ++++ .../java/com/structurizr/dsl/DslTests.java | 3 ++ .../resources/dsl/archetypes-for-defaults.dsl | 8 ++++ 20 files changed, 100 insertions(+), 7 deletions(-) diff --git a/structurizr-core/src/main/java/com/structurizr/model/ModelItem.java b/structurizr-core/src/main/java/com/structurizr/model/ModelItem.java index 398eacdc1..f9d789481 100644 --- a/structurizr-core/src/main/java/com/structurizr/model/ModelItem.java +++ b/structurizr-core/src/main/java/com/structurizr/model/ModelItem.java @@ -173,6 +173,17 @@ public void addProperty(String name, String value) { properties.put(name, value); } + /** + * Adds a collection of name-value pair properties to this model item. + * + * @param properties Map of properties + */ + public void addProperties(Map properties) { + for (String key : properties.keySet()) { + this.addProperty(key, properties.get(key)); + } + } + void setProperties(Map properties) { if (properties != null) { this.properties = new HashMap<>(properties); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/Archetype.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/Archetype.java index a6781b426..999ece4a4 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/Archetype.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/Archetype.java @@ -1,11 +1,12 @@ package com.structurizr.dsl; +import com.structurizr.PropertyHolder; +import com.structurizr.model.Perspective; import com.structurizr.util.StringUtils; -import java.util.LinkedHashSet; -import java.util.Set; +import java.util.*; -final class Archetype { +final class Archetype implements PropertyHolder { private final String name; private final String type; @@ -13,6 +14,9 @@ final class Archetype { private String technology = ""; private final Set tags = new LinkedHashSet<>(); + private Map properties = new HashMap<>(); + private final Set perspectives = new TreeSet<>(); + Archetype(String name, String type) { if (StringUtils.isNullOrEmpty(name)) { name = type; @@ -62,4 +66,31 @@ Set getTags() { return new LinkedHashSet<>(tags); } + /** + * Gets the collection of name-value property pairs associated with this model item, as a Map. + * + * @return a Map (String, String) (empty if there are no properties) + */ + public Map getProperties() { + return Collections.unmodifiableMap(properties); + } + + /** + * Adds a name-value pair property to this model item. + * + * @param name the name of the property + * @param value the value of the property + */ + public void addProperty(String name, String value) { + if (StringUtils.isNullOrEmpty(name)) { + throw new IllegalArgumentException("A property name must be specified."); + } + + if (StringUtils.isNullOrEmpty(value)) { + throw new IllegalArgumentException("A property value must be specified."); + } + + properties.put(name, value); + } + } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentArchetypeDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentArchetypeDslContext.java index a204af35c..44474ce46 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentArchetypeDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentArchetypeDslContext.java @@ -13,6 +13,7 @@ protected String[] getPermittedTokens() { StructurizrDslTokens.TECHNOLOGY_TOKEN, StructurizrDslTokens.TAG_TOKEN, StructurizrDslTokens.TAGS_TOKEN, + StructurizrDslTokens.PROPERTIES_TOKEN }; } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentParser.java index 6600845f1..b22d36c05 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentParser.java @@ -53,6 +53,8 @@ Component parse(ContainerDslContext context, Tokens tokens, Archetype archetype) } component.addTags(tags); + component.addProperties(archetype.getProperties()); + if (context.hasGroup()) { component.setGroup(context.getGroup().getName()); context.getGroup().addElement(component); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerArchetypeDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerArchetypeDslContext.java index df4c4d015..3432f62a3 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerArchetypeDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerArchetypeDslContext.java @@ -13,6 +13,7 @@ protected String[] getPermittedTokens() { StructurizrDslTokens.TECHNOLOGY_TOKEN, StructurizrDslTokens.TAG_TOKEN, StructurizrDslTokens.TAGS_TOKEN, + StructurizrDslTokens.PROPERTIES_TOKEN }; } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerParser.java index c937849de..ad570e722 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerParser.java @@ -53,6 +53,8 @@ Container parse(SoftwareSystemDslContext context, Tokens tokens, Archetype arche } container.addTags(tags); + container.addProperties(archetype.getProperties()); + if (context.hasGroup()) { container.setGroup(context.getGroup().getName()); context.getGroup().addElement(container); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeArchetypeDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeArchetypeDslContext.java index 65b411b5c..82037bd0b 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeArchetypeDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeArchetypeDslContext.java @@ -13,6 +13,7 @@ protected String[] getPermittedTokens() { StructurizrDslTokens.TECHNOLOGY_TOKEN, StructurizrDslTokens.TAG_TOKEN, StructurizrDslTokens.TAGS_TOKEN, + StructurizrDslTokens.PROPERTIES_TOKEN }; } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeParser.java index 7e1bb3569..b551076d9 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeParser.java @@ -44,6 +44,8 @@ DeploymentNode parse(DeploymentEnvironmentDslContext context, Tokens tokens, Arc } deploymentNode.addTags(tags); + deploymentNode.addProperties(archetype.getProperties()); + String instances = "1"; if (tokens.includes(INSTANCES_INDEX)) { instances = tokens.get(INSTANCES_INDEX); @@ -91,6 +93,8 @@ DeploymentNode parse(DeploymentNodeDslContext context, Tokens tokens, Archetype } deploymentNode.addTags(tags); + deploymentNode.addProperties(archetype.getProperties()); + String instances = "1"; if (tokens.includes(INSTANCES_INDEX)) { instances = tokens.get(INSTANCES_INDEX); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ExplicitRelationshipParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ExplicitRelationshipParser.java index f67820aee..18e166433 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ExplicitRelationshipParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ExplicitRelationshipParser.java @@ -55,7 +55,10 @@ Relationship parse(DslContext context, Tokens tokens, Archetype archetype) { tags = tokens.get(TAGS_INDEX).split(","); } - return createRelationship(sourceElement, description, technology, tags, destinationElement); + Relationship relationship = createRelationship(sourceElement, description, technology, tags, destinationElement); + relationship.addProperties(archetype.getProperties()); + + return relationship; } Set parse(ElementsDslContext context, Tokens tokens, Archetype archetype) { @@ -99,7 +102,10 @@ Set parse(ElementsDslContext context, Tokens tokens, Archetype arc Set relationships = new LinkedHashSet<>(); for (Element sourceElement : sourceElements) { for (Element destinationElement : destinationElements) { - relationships.add(createRelationship(sourceElement, description, technology, tags, destinationElement)); + Relationship relationship = createRelationship(sourceElement, description, technology, tags, destinationElement); + relationship.addProperties(archetype.getProperties()); + + relationships.add(relationship); } } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImplicitRelationshipParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImplicitRelationshipParser.java index e5dd02246..73349d877 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImplicitRelationshipParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImplicitRelationshipParser.java @@ -50,7 +50,10 @@ Relationship parse(ElementDslContext context, Tokens tokens, Archetype archetype tags = tokens.get(TAGS_INDEX).split(","); } - return createRelationship(sourceElement, description, technology, tags, destinationElement); + Relationship relationship = createRelationship(sourceElement, description, technology, tags, destinationElement); + relationship.addProperties(archetype.getProperties()); + + return relationship; } Set parse(ElementsDslContext context, Tokens tokens, Archetype archetype) { @@ -89,7 +92,10 @@ Set parse(ElementsDslContext context, Tokens tokens, Archetype arc Set relationships = new LinkedHashSet<>(); for (Element sourceElement : sourceElements) { - relationships.add(createRelationship(sourceElement, description, technology, tags, destinationElement)); + Relationship relationship = createRelationship(sourceElement, description, technology, tags, destinationElement); + relationship.addProperties(archetype.getProperties()); + + relationships.add(relationship); } return relationships; diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeArchetypeDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeArchetypeDslContext.java index 197da828c..b123d80ce 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeArchetypeDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeArchetypeDslContext.java @@ -13,6 +13,7 @@ protected String[] getPermittedTokens() { StructurizrDslTokens.TECHNOLOGY_TOKEN, StructurizrDslTokens.TAG_TOKEN, StructurizrDslTokens.TAGS_TOKEN, + StructurizrDslTokens.PROPERTIES_TOKEN }; } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeParser.java index 126c90776..a18baed55 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeParser.java @@ -45,6 +45,8 @@ InfrastructureNode parse(DeploymentNodeDslContext context, Tokens tokens, Archet } infrastructureNode.addTags(tags); + infrastructureNode.addProperties(archetype.getProperties()); + if (context.hasGroup()) { infrastructureNode.setGroup(context.getGroup().getName()); context.getGroup().addElement(infrastructureNode); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonArchetypeDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonArchetypeDslContext.java index 0ecd4b4d1..78edf577b 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonArchetypeDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonArchetypeDslContext.java @@ -12,6 +12,7 @@ protected String[] getPermittedTokens() { StructurizrDslTokens.DESCRIPTION_TOKEN, StructurizrDslTokens.TAG_TOKEN, StructurizrDslTokens.TAGS_TOKEN, + StructurizrDslTokens.PROPERTIES_TOKEN }; } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonParser.java index f16209982..4b0d6c9b5 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonParser.java @@ -44,6 +44,8 @@ Person parse(ModelDslContext context, Tokens tokens, Archetype archetype) { } person.addTags(tags); + person.addProperties(archetype.getProperties()); + if (context.hasGroup()) { person.setGroup(context.getGroup().getName()); context.getGroup().addElement(person); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipArchetypeDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipArchetypeDslContext.java index 4bd231e68..798b883c7 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipArchetypeDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipArchetypeDslContext.java @@ -13,6 +13,7 @@ protected String[] getPermittedTokens() { StructurizrDslTokens.TECHNOLOGY_TOKEN, StructurizrDslTokens.TAG_TOKEN, StructurizrDslTokens.TAGS_TOKEN, + StructurizrDslTokens.PROPERTIES_TOKEN }; } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemArchetypeDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemArchetypeDslContext.java index 5170b064a..137e55e3b 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemArchetypeDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemArchetypeDslContext.java @@ -12,6 +12,7 @@ protected String[] getPermittedTokens() { StructurizrDslTokens.DESCRIPTION_TOKEN, StructurizrDslTokens.TAG_TOKEN, StructurizrDslTokens.TAGS_TOKEN, + StructurizrDslTokens.PROPERTIES_TOKEN }; } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemParser.java index cdbef98a5..affb4ff68 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemParser.java @@ -44,6 +44,8 @@ SoftwareSystem parse(ModelDslContext context, Tokens tokens, Archetype archetype } softwareSystem.addTags(tags); + softwareSystem.addProperties(archetype.getProperties()); + if (context.hasGroup()) { softwareSystem.setGroup(context.getGroup().getName()); context.getGroup().addElement(softwareSystem); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index 8423e6ac6..fd80f187e 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -745,6 +745,13 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn } else if (TAGS_TOKEN.equalsIgnoreCase(firstToken) && inContext(ArchetypeDslContext.class)) { new ArchetypeParser().parseTags(getContext(ArchetypeDslContext.class), tokens); + } else if (PROPERTIES_TOKEN.equalsIgnoreCase(firstToken) && inContext(ArchetypeDslContext.class)) { + Archetype archetype = getContext(ArchetypeDslContext.class).getArchetype(); + startContext(new PropertiesDslContext(archetype)); + + } else if (inContext(PropertiesDslContext.class)) { + new PropertyParser().parse(getContext(PropertiesDslContext.class), tokens); + } else if (VIEWS_TOKEN.equalsIgnoreCase(firstToken) && inContext(WorkspaceDslContext.class)) { if (parsedTokens.contains(VIEWS_TOKEN)) { throw new RuntimeException("Multiple view sets are not permitted in a DSL definition"); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index 4b33251a2..b2a5c7a64 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -1499,15 +1499,18 @@ void test_archetypesForDefaults() throws Exception { SoftwareSystem a = workspace.getModel().getSoftwareSystemWithName("A"); assertEquals("Default Description", a.getDescription()); assertTrue(a.hasTag("Default Tag")); + assertTrue(a.hasProperty("Default Property Name", "Default Property Value")); SoftwareSystem b = workspace.getModel().getSoftwareSystemWithName("B"); assertEquals("Default Description", b.getDescription()); assertTrue(b.hasTag("Default Tag")); + assertTrue(b.hasProperty("Default Property Name", "Default Property Value")); Relationship r = a.getEfferentRelationshipWith(b); assertEquals("Default Description", r.getDescription()); assertEquals("Default Technology", r.getTechnology()); assertTrue(r.hasTag("Default Tag")); + assertTrue(r.hasProperty("Default Property Name", "Default Property Value")); } @Test diff --git a/structurizr-dsl/src/test/resources/dsl/archetypes-for-defaults.dsl b/structurizr-dsl/src/test/resources/dsl/archetypes-for-defaults.dsl index 63ed7ce07..b12400ae9 100644 --- a/structurizr-dsl/src/test/resources/dsl/archetypes-for-defaults.dsl +++ b/structurizr-dsl/src/test/resources/dsl/archetypes-for-defaults.dsl @@ -5,12 +5,20 @@ workspace { softwaresystem { description "Default Description" tag "Default Tag" + + properties { + "Default Property Name" "Default Property Value" + } } -> { description "Default Description" technology "Default Technology" tag "Default Tag" + + properties { + "Default Property Name" "Default Property Value" + } } } From e8149373bb0773bc861e5c2601fa47ec7706575e Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Tue, 18 Feb 2025 17:06:59 +0000 Subject: [PATCH 623/717] Adds perspectives to archetypes. --- .../com/structurizr/PerspectivesHolder.java | 40 +++++++++++++ .../java/com/structurizr/PropertyHolder.java | 4 +- .../java/com/structurizr/model/ModelItem.java | 14 ++++- .../com/structurizr/model/Perspective.java | 2 +- .../java/com/structurizr/dsl/Archetype.java | 58 +++++++++++++++++-- .../dsl/ComponentArchetypeDslContext.java | 3 +- .../com/structurizr/dsl/ComponentParser.java | 1 + .../dsl/ContainerArchetypeDslContext.java | 3 +- .../com/structurizr/dsl/ContainerParser.java | 1 + .../DeploymentNodeArchetypeDslContext.java | 3 +- .../structurizr/dsl/DeploymentNodeParser.java | 2 + .../dsl/ExplicitRelationshipParser.java | 2 + .../dsl/ImplicitRelationshipParser.java | 2 + ...InfrastructureNodeArchetypeDslContext.java | 3 +- .../dsl/InfrastructureNodeParser.java | 1 + .../dsl/PersonArchetypeDslContext.java | 3 +- .../com/structurizr/dsl/PersonParser.java | 1 + .../structurizr/dsl/PerspectiveParser.java | 6 +- .../dsl/PerspectivesDslContext.java | 15 ++--- .../dsl/RelationshipArchetypeDslContext.java | 3 +- .../SoftwareSystemArchetypeDslContext.java | 3 +- .../structurizr/dsl/SoftwareSystemParser.java | 1 + .../structurizr/dsl/StructurizrDslParser.java | 6 +- .../java/com/structurizr/dsl/DslTests.java | 3 + .../resources/dsl/archetypes-for-defaults.dsl | 8 +++ 25 files changed, 161 insertions(+), 27 deletions(-) create mode 100644 structurizr-core/src/main/java/com/structurizr/PerspectivesHolder.java diff --git a/structurizr-core/src/main/java/com/structurizr/PerspectivesHolder.java b/structurizr-core/src/main/java/com/structurizr/PerspectivesHolder.java new file mode 100644 index 000000000..1df88a62c --- /dev/null +++ b/structurizr-core/src/main/java/com/structurizr/PerspectivesHolder.java @@ -0,0 +1,40 @@ +package com.structurizr; + +import com.structurizr.model.Perspective; +import com.structurizr.util.StringUtils; + +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +public interface PerspectivesHolder { + + /** + * Gets the set of perspectives associated with this object. + * + * @return a Set of Perspective objects (empty if there are none) + */ + Set getPerspectives(); + + /** + * Adds a perspective to this object. + * + * @param name the name of the perspective (e.g. "Security", must be unique) + * @param description the description of the perspective + * @return a Perspective object + * @throws IllegalArgumentException if perspective details are not specified, or the named perspective exists already + */ + Perspective addPerspective(String name, String description); + + /** + * Adds a perspective to this object. + * + * @param name the name of the perspective (e.g. "Technical Debt", must be unique) + * @param description the description of the perspective (e.g. "High") + * @param value the value of the perspective + * @return a Perspective object + * @throws IllegalArgumentException if perspective details are not specified, or the named perspective exists already + */ + Perspective addPerspective(String name, String description, String value); + +} \ No newline at end of file diff --git a/structurizr-core/src/main/java/com/structurizr/PropertyHolder.java b/structurizr-core/src/main/java/com/structurizr/PropertyHolder.java index ec22c5567..b90616a91 100644 --- a/structurizr-core/src/main/java/com/structurizr/PropertyHolder.java +++ b/structurizr-core/src/main/java/com/structurizr/PropertyHolder.java @@ -5,14 +5,14 @@ public interface PropertyHolder { /** - * Gets the collection of name-value property pairs associated with this workspace, as a Map. + * Gets the collection of name-value property pairs associated with this object, as a Map. * * @return a Map (String, String) (empty if there are no properties) */ public Map getProperties(); /** - * Adds a name-value pair property to this workspace. + * Adds a name-value pair property to this object. * * @param name the name of the property * @param value the value of the property diff --git a/structurizr-core/src/main/java/com/structurizr/model/ModelItem.java b/structurizr-core/src/main/java/com/structurizr/model/ModelItem.java index f9d789481..9cba7e71a 100644 --- a/structurizr-core/src/main/java/com/structurizr/model/ModelItem.java +++ b/structurizr-core/src/main/java/com/structurizr/model/ModelItem.java @@ -1,6 +1,7 @@ package com.structurizr.model; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.structurizr.PerspectivesHolder; import com.structurizr.PropertyHolder; import com.structurizr.util.StringUtils; import com.structurizr.util.TagUtils; @@ -11,7 +12,7 @@ /** * The base class for elements and relationships. */ -public abstract class ModelItem implements PropertyHolder, Comparable { +public abstract class ModelItem implements PropertyHolder, PerspectivesHolder, Comparable { private String id = ""; private final Set tags = new LinkedHashSet<>(); @@ -249,6 +250,17 @@ public Perspective addPerspective(String name, String description, String value) return perspective; } + /** + * Adds a collection of name-value pair properties to this model item. + * + * @param perspectives Set of Perspective objects + */ + public void addPerspectives(Set perspectives) { + for (Perspective perspective : perspectives) { + addPerspective(perspective.getName(), perspective.getDescription(), perspective.getValue()); + } + } + @Override public int compareTo(ModelItem modelItem) { try { diff --git a/structurizr-core/src/main/java/com/structurizr/model/Perspective.java b/structurizr-core/src/main/java/com/structurizr/model/Perspective.java index 5b55e757e..fdb9e95f5 100644 --- a/structurizr-core/src/main/java/com/structurizr/model/Perspective.java +++ b/structurizr-core/src/main/java/com/structurizr/model/Perspective.java @@ -13,7 +13,7 @@ public final class Perspective implements Comparable { Perspective() { } - Perspective(String name, String description, String value) { + public Perspective(String name, String description, String value) { this.name = name; this.description = description; this.value = value; diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/Archetype.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/Archetype.java index 999ece4a4..2a0b0fbe3 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/Archetype.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/Archetype.java @@ -1,12 +1,13 @@ package com.structurizr.dsl; +import com.structurizr.PerspectivesHolder; import com.structurizr.PropertyHolder; import com.structurizr.model.Perspective; import com.structurizr.util.StringUtils; import java.util.*; -final class Archetype implements PropertyHolder { +final class Archetype implements PropertyHolder, PerspectivesHolder { private final String name; private final String type; @@ -14,7 +15,7 @@ final class Archetype implements PropertyHolder { private String technology = ""; private final Set tags = new LinkedHashSet<>(); - private Map properties = new HashMap<>(); + private final Map properties = new HashMap<>(); private final Set perspectives = new TreeSet<>(); Archetype(String name, String type) { @@ -67,7 +68,7 @@ Set getTags() { } /** - * Gets the collection of name-value property pairs associated with this model item, as a Map. + * Gets the collection of name-value property pairs associated with this archetype, as a Map. * * @return a Map (String, String) (empty if there are no properties) */ @@ -76,7 +77,7 @@ public Map getProperties() { } /** - * Adds a name-value pair property to this model item. + * Adds a name-value pair property to this archetype. * * @param name the name of the property * @param value the value of the property @@ -93,4 +94,53 @@ public void addProperty(String name, String value) { properties.put(name, value); } + /** + * Gets the set of perspectives associated with this archetype. + * + * @return a Set of Perspective objects (empty if there are none) + */ + public Set getPerspectives() { + return new TreeSet<>(perspectives); + } + + /** + * Adds a perspective to this archetype. + * + * @param name the name of the perspective (e.g. "Security", must be unique) + * @param description the description of the perspective + * @return a Perspective object + * @throws IllegalArgumentException if perspective details are not specified, or the named perspective exists already + */ + public Perspective addPerspective(String name, String description) { + return addPerspective(name, description, ""); + } + + /** + * Adds a perspective to this archetype. + * + * @param name the name of the perspective (e.g. "Technical Debt", must be unique) + * @param description the description of the perspective (e.g. "High") + * @param value the value of the perspective + * @return a Perspective object + * @throws IllegalArgumentException if perspective details are not specified, or the named perspective exists already + */ + public Perspective addPerspective(String name, String description, String value) { + if (StringUtils.isNullOrEmpty(name)) { + throw new IllegalArgumentException("A name must be specified."); + } + + if (StringUtils.isNullOrEmpty(description)) { + throw new IllegalArgumentException("A description must be specified."); + } + + if (perspectives.stream().anyMatch(p -> p.getName().equals(name))) { + throw new IllegalArgumentException("A perspective named \"" + name + "\" already exists."); + } + + Perspective perspective = new Perspective(name, description, value); + perspectives.add(perspective); + + return perspective; + } + } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentArchetypeDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentArchetypeDslContext.java index 44474ce46..f81a925cb 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentArchetypeDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentArchetypeDslContext.java @@ -13,7 +13,8 @@ protected String[] getPermittedTokens() { StructurizrDslTokens.TECHNOLOGY_TOKEN, StructurizrDslTokens.TAG_TOKEN, StructurizrDslTokens.TAGS_TOKEN, - StructurizrDslTokens.PROPERTIES_TOKEN + StructurizrDslTokens.PROPERTIES_TOKEN, + StructurizrDslTokens.PERSPECTIVES_TOKEN }; } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentParser.java index b22d36c05..0b587d2e8 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentParser.java @@ -54,6 +54,7 @@ Component parse(ContainerDslContext context, Tokens tokens, Archetype archetype) component.addTags(tags); component.addProperties(archetype.getProperties()); + component.addPerspectives(archetype.getPerspectives()); if (context.hasGroup()) { component.setGroup(context.getGroup().getName()); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerArchetypeDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerArchetypeDslContext.java index 3432f62a3..676f1d548 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerArchetypeDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerArchetypeDslContext.java @@ -13,7 +13,8 @@ protected String[] getPermittedTokens() { StructurizrDslTokens.TECHNOLOGY_TOKEN, StructurizrDslTokens.TAG_TOKEN, StructurizrDslTokens.TAGS_TOKEN, - StructurizrDslTokens.PROPERTIES_TOKEN + StructurizrDslTokens.PROPERTIES_TOKEN, + StructurizrDslTokens.PERSPECTIVES_TOKEN }; } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerParser.java index ad570e722..c8abc1ec8 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerParser.java @@ -54,6 +54,7 @@ Container parse(SoftwareSystemDslContext context, Tokens tokens, Archetype arche container.addTags(tags); container.addProperties(archetype.getProperties()); + container.addPerspectives(archetype.getPerspectives()); if (context.hasGroup()) { container.setGroup(context.getGroup().getName()); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeArchetypeDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeArchetypeDslContext.java index 82037bd0b..0a5b3dc4a 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeArchetypeDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeArchetypeDslContext.java @@ -13,7 +13,8 @@ protected String[] getPermittedTokens() { StructurizrDslTokens.TECHNOLOGY_TOKEN, StructurizrDslTokens.TAG_TOKEN, StructurizrDslTokens.TAGS_TOKEN, - StructurizrDslTokens.PROPERTIES_TOKEN + StructurizrDslTokens.PROPERTIES_TOKEN, + StructurizrDslTokens.PERSPECTIVES_TOKEN }; } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeParser.java index b551076d9..b2a5a2c3d 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeParser.java @@ -45,6 +45,7 @@ DeploymentNode parse(DeploymentEnvironmentDslContext context, Tokens tokens, Arc deploymentNode.addTags(tags); deploymentNode.addProperties(archetype.getProperties()); + deploymentNode.addPerspectives(archetype.getPerspectives()); String instances = "1"; if (tokens.includes(INSTANCES_INDEX)) { @@ -94,6 +95,7 @@ DeploymentNode parse(DeploymentNodeDslContext context, Tokens tokens, Archetype deploymentNode.addTags(tags); deploymentNode.addProperties(archetype.getProperties()); + deploymentNode.addPerspectives(archetype.getPerspectives()); String instances = "1"; if (tokens.includes(INSTANCES_INDEX)) { diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ExplicitRelationshipParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ExplicitRelationshipParser.java index 18e166433..de846e382 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ExplicitRelationshipParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ExplicitRelationshipParser.java @@ -57,6 +57,7 @@ Relationship parse(DslContext context, Tokens tokens, Archetype archetype) { Relationship relationship = createRelationship(sourceElement, description, technology, tags, destinationElement); relationship.addProperties(archetype.getProperties()); + relationship.addPerspectives(archetype.getPerspectives()); return relationship; } @@ -104,6 +105,7 @@ Set parse(ElementsDslContext context, Tokens tokens, Archetype arc for (Element destinationElement : destinationElements) { Relationship relationship = createRelationship(sourceElement, description, technology, tags, destinationElement); relationship.addProperties(archetype.getProperties()); + relationship.addPerspectives(archetype.getPerspectives()); relationships.add(relationship); } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImplicitRelationshipParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImplicitRelationshipParser.java index 73349d877..e3fb1df93 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImplicitRelationshipParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImplicitRelationshipParser.java @@ -52,6 +52,7 @@ Relationship parse(ElementDslContext context, Tokens tokens, Archetype archetype Relationship relationship = createRelationship(sourceElement, description, technology, tags, destinationElement); relationship.addProperties(archetype.getProperties()); + relationship.addPerspectives(archetype.getPerspectives()); return relationship; } @@ -94,6 +95,7 @@ Set parse(ElementsDslContext context, Tokens tokens, Archetype arc for (Element sourceElement : sourceElements) { Relationship relationship = createRelationship(sourceElement, description, technology, tags, destinationElement); relationship.addProperties(archetype.getProperties()); + relationship.addPerspectives(archetype.getPerspectives()); relationships.add(relationship); } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeArchetypeDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeArchetypeDslContext.java index b123d80ce..fc6f06ae5 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeArchetypeDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeArchetypeDslContext.java @@ -13,7 +13,8 @@ protected String[] getPermittedTokens() { StructurizrDslTokens.TECHNOLOGY_TOKEN, StructurizrDslTokens.TAG_TOKEN, StructurizrDslTokens.TAGS_TOKEN, - StructurizrDslTokens.PROPERTIES_TOKEN + StructurizrDslTokens.PROPERTIES_TOKEN, + StructurizrDslTokens.PERSPECTIVES_TOKEN }; } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeParser.java index a18baed55..964f989e9 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeParser.java @@ -46,6 +46,7 @@ InfrastructureNode parse(DeploymentNodeDslContext context, Tokens tokens, Archet infrastructureNode.addTags(tags); infrastructureNode.addProperties(archetype.getProperties()); + infrastructureNode.addPerspectives(archetype.getPerspectives()); if (context.hasGroup()) { infrastructureNode.setGroup(context.getGroup().getName()); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonArchetypeDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonArchetypeDslContext.java index 78edf577b..b8eefe9f8 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonArchetypeDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonArchetypeDslContext.java @@ -12,7 +12,8 @@ protected String[] getPermittedTokens() { StructurizrDslTokens.DESCRIPTION_TOKEN, StructurizrDslTokens.TAG_TOKEN, StructurizrDslTokens.TAGS_TOKEN, - StructurizrDslTokens.PROPERTIES_TOKEN + StructurizrDslTokens.PROPERTIES_TOKEN, + StructurizrDslTokens.PERSPECTIVES_TOKEN }; } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonParser.java index 4b0d6c9b5..8525e5d36 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonParser.java @@ -45,6 +45,7 @@ Person parse(ModelDslContext context, Tokens tokens, Archetype archetype) { person.addTags(tags); person.addProperties(archetype.getProperties()); + person.addPerspectives(archetype.getPerspectives()); if (context.hasGroup()) { person.setGroup(context.getGroup().getName()); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/PerspectiveParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/PerspectiveParser.java index 3a38ad3ab..6fcc92c1d 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/PerspectiveParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/PerspectiveParser.java @@ -1,6 +1,6 @@ package com.structurizr.dsl; -import com.structurizr.model.ModelItem; +import com.structurizr.PerspectivesHolder; final class PerspectiveParser extends AbstractParser { @@ -27,8 +27,8 @@ void parse(PerspectivesDslContext context, Tokens tokens) { value = tokens.get(PERSPECTIVE_VALUE_INDEX); } - for (ModelItem modelItem : context.getModelItems()) { - modelItem.addPerspective(name, description, value); + for (PerspectivesHolder perspectivesHolder : context.getPerspectivesHolders()) { + perspectivesHolder.addPerspective(name, description, value); } } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/PerspectivesDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/PerspectivesDslContext.java index 50ff2e6d3..fe1338f57 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/PerspectivesDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/PerspectivesDslContext.java @@ -1,5 +1,6 @@ package com.structurizr.dsl; +import com.structurizr.PerspectivesHolder; import com.structurizr.model.ModelItem; import java.util.ArrayList; @@ -7,18 +8,18 @@ final class PerspectivesDslContext extends DslContext { - private final Collection modelItems = new ArrayList<>(); + private final Collection perspectivesHolders = new ArrayList<>(); - PerspectivesDslContext(ModelItem modelItem) { - this.modelItems.add(modelItem); + PerspectivesDslContext(PerspectivesHolder perspectivesHolder) { + this.perspectivesHolders.add(perspectivesHolder); } - PerspectivesDslContext(Collection modelItems) { - this.modelItems.addAll(modelItems); + PerspectivesDslContext(Collection perspectivesHolders) { + this.perspectivesHolders.addAll(perspectivesHolders); } - Collection getModelItems() { - return this.modelItems; + Collection getPerspectivesHolders() { + return this.perspectivesHolders; } @Override diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipArchetypeDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipArchetypeDslContext.java index 798b883c7..d25d329ae 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipArchetypeDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipArchetypeDslContext.java @@ -13,7 +13,8 @@ protected String[] getPermittedTokens() { StructurizrDslTokens.TECHNOLOGY_TOKEN, StructurizrDslTokens.TAG_TOKEN, StructurizrDslTokens.TAGS_TOKEN, - StructurizrDslTokens.PROPERTIES_TOKEN + StructurizrDslTokens.PROPERTIES_TOKEN, + StructurizrDslTokens.PERSPECTIVES_TOKEN }; } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemArchetypeDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemArchetypeDslContext.java index 137e55e3b..0283d5716 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemArchetypeDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemArchetypeDslContext.java @@ -12,7 +12,8 @@ protected String[] getPermittedTokens() { StructurizrDslTokens.DESCRIPTION_TOKEN, StructurizrDslTokens.TAG_TOKEN, StructurizrDslTokens.TAGS_TOKEN, - StructurizrDslTokens.PROPERTIES_TOKEN + StructurizrDslTokens.PROPERTIES_TOKEN, + StructurizrDslTokens.PERSPECTIVES_TOKEN }; } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemParser.java index affb4ff68..fd1ef8949 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemParser.java @@ -45,6 +45,7 @@ SoftwareSystem parse(ModelDslContext context, Tokens tokens, Archetype archetype softwareSystem.addTags(tags); softwareSystem.addProperties(archetype.getProperties()); + softwareSystem.addPerspectives(archetype.getPerspectives()); if (context.hasGroup()) { softwareSystem.setGroup(context.getGroup().getName()); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index fd80f187e..c2a68603a 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -1,5 +1,6 @@ package com.structurizr.dsl; +import com.structurizr.PerspectivesHolder; import com.structurizr.PropertyHolder; import com.structurizr.Workspace; import com.structurizr.model.*; @@ -749,8 +750,9 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn Archetype archetype = getContext(ArchetypeDslContext.class).getArchetype(); startContext(new PropertiesDslContext(archetype)); - } else if (inContext(PropertiesDslContext.class)) { - new PropertyParser().parse(getContext(PropertiesDslContext.class), tokens); + } else if (PERSPECTIVES_TOKEN.equalsIgnoreCase(firstToken) && inContext(ArchetypeDslContext.class)) { + Archetype archetype = getContext(ArchetypeDslContext.class).getArchetype(); + startContext(new PerspectivesDslContext(archetype)); } else if (VIEWS_TOKEN.equalsIgnoreCase(firstToken) && inContext(WorkspaceDslContext.class)) { if (parsedTokens.contains(VIEWS_TOKEN)) { diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index b2a5c7a64..9b766fa9f 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -1500,17 +1500,20 @@ void test_archetypesForDefaults() throws Exception { assertEquals("Default Description", a.getDescription()); assertTrue(a.hasTag("Default Tag")); assertTrue(a.hasProperty("Default Property Name", "Default Property Value")); + assertEquals("Default Perspective Description", (a.getPerspectives().stream().filter(p -> p.getName().equals("Default Perspective Name")).findFirst().get().getDescription())); SoftwareSystem b = workspace.getModel().getSoftwareSystemWithName("B"); assertEquals("Default Description", b.getDescription()); assertTrue(b.hasTag("Default Tag")); assertTrue(b.hasProperty("Default Property Name", "Default Property Value")); + assertEquals("Default Perspective Description", (b.getPerspectives().stream().filter(p -> p.getName().equals("Default Perspective Name")).findFirst().get().getDescription())); Relationship r = a.getEfferentRelationshipWith(b); assertEquals("Default Description", r.getDescription()); assertEquals("Default Technology", r.getTechnology()); assertTrue(r.hasTag("Default Tag")); assertTrue(r.hasProperty("Default Property Name", "Default Property Value")); + assertEquals("Default Perspective Description", (r.getPerspectives().stream().filter(p -> p.getName().equals("Default Perspective Name")).findFirst().get().getDescription())); } @Test diff --git a/structurizr-dsl/src/test/resources/dsl/archetypes-for-defaults.dsl b/structurizr-dsl/src/test/resources/dsl/archetypes-for-defaults.dsl index b12400ae9..cc6d71aee 100644 --- a/structurizr-dsl/src/test/resources/dsl/archetypes-for-defaults.dsl +++ b/structurizr-dsl/src/test/resources/dsl/archetypes-for-defaults.dsl @@ -9,6 +9,10 @@ workspace { properties { "Default Property Name" "Default Property Value" } + + perspectives { + "Default Perspective Name" "Default Perspective Description" + } } -> { @@ -19,6 +23,10 @@ workspace { properties { "Default Property Name" "Default Property Value" } + + perspectives { + "Default Perspective Name" "Default Perspective Description" + } } } From d9a6f1cdc778fea029e1df95d8faa2cdee063cb9 Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Wed, 19 Feb 2025 09:30:55 +0000 Subject: [PATCH 624/717] structurizr-dsl: Adds a reluctant version of `include *` (`include *?`) for system context, container, and component views. --- changelog.md | 3 + .../com/structurizr/view/ComponentView.java | 13 ++++ .../com/structurizr/view/ContainerView.java | 13 ++++ .../java/com/structurizr/view/ModelView.java | 14 ++++ .../java/com/structurizr/view/StaticView.java | 9 ++- .../structurizr/view/SystemContextView.java | 13 ++++ .../structurizr/view/SystemLandscapeView.java | 13 +++- .../structurizr/view/ComponentViewTests.java | 64 +++++++++++++++++++ .../structurizr/view/ContainerViewTests.java | 62 ++++++++++++++++++ .../view/SystemContextViewTests.java | 38 +++++++++++ .../dsl/ModelViewContentParser.java | 1 + .../dsl/StaticViewContentParser.java | 16 +++-- .../dsl/StaticViewContentParserTests.java | 44 ++++++++++++- 13 files changed, 292 insertions(+), 11 deletions(-) diff --git a/changelog.md b/changelog.md index e703945db..c4b7acf70 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,9 @@ - structurizr-dsl: Fixes https://github.com/structurizr/java/issues/374 (!identifiers hierarchical isn't propagated when extending a workspace). - structurizr-dsl: Adds the ability to use the `group` keyword inside a component definition, to set the group name of that component. - structurizr-dsl: Adds the ability to use the `group` keyword inside the component finder strategy `forEach` block. +- structurizr-dsl: Adds a reluctant version of `include *` (`include *?`) for system context views that only adds relationships to/from the scoped software system. +- structurizr-dsl: Adds a reluctant version of `include *` (`include *?`) for container views that only adds relationships to/from the containers in the scoped software system. +- structurizr-dsl: Adds a reluctant version of `include *` (`include *?`) for component views that only adds relationships to/from the components in the scoped container. ## 3.2.1 (10th December 2024) diff --git a/structurizr-core/src/main/java/com/structurizr/view/ComponentView.java b/structurizr-core/src/main/java/com/structurizr/view/ComponentView.java index d03756b37..b543dc876 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/ComponentView.java +++ b/structurizr-core/src/main/java/com/structurizr/view/ComponentView.java @@ -156,6 +156,15 @@ public String getName() { */ @Override public void addDefaultElements() { + addDefaultElements(true); + } + + /** + * Adds the default set of elements and relationships to this view. + * + * @param greedy true (add all relationships) or false (adds relationships to/from the components in the scoped container only) + */ + public void addDefaultElements(boolean greedy) { for (Component component : getContainer().getComponents()) { add(component); @@ -169,6 +178,10 @@ public void addDefaultElements() { addNearestNeighbours(component, Person.class); addNearestNeighbours(component, SoftwareSystem.class); } + + if (!greedy) { + removeRelationshipsNotConnectedToElements(getContainer().getComponents()); + } } /** diff --git a/structurizr-core/src/main/java/com/structurizr/view/ContainerView.java b/structurizr-core/src/main/java/com/structurizr/view/ContainerView.java index b4d02fb9a..664164974 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/ContainerView.java +++ b/structurizr-core/src/main/java/com/structurizr/view/ContainerView.java @@ -85,12 +85,25 @@ public String getName() { */ @Override public void addDefaultElements() { + addDefaultElements(true); + } + + /** + * Adds the default set of elements and relationships to this view. + * + * @param greedy true (add all relationships) or false (adds relationships to/from the containers in the scoped software system only) + */ + public void addDefaultElements(boolean greedy) { for (Container container : getSoftwareSystem().getContainers()) { add(container); addNearestNeighbours(container, CustomElement.class); addNearestNeighbours(container, Person.class); addNearestNeighbours(container, SoftwareSystem.class); } + + if (!greedy) { + removeRelationshipsNotConnectedToElements(getSoftwareSystem().getContainers()); + } } /** diff --git a/structurizr-core/src/main/java/com/structurizr/view/ModelView.java b/structurizr-core/src/main/java/com/structurizr/view/ModelView.java index 1128dcd9b..5ced49fed 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/ModelView.java +++ b/structurizr-core/src/main/java/com/structurizr/view/ModelView.java @@ -302,6 +302,20 @@ public void removeRelationshipsNotConnectedToElement(Element element) { } } + /** + * Removes relationships that are not connected to the specified elements. + * + * @param elements the Set of Element objects to test against + */ + public void removeRelationshipsNotConnectedToElements(Set elements) { + if (elements != null) { + getRelationships().stream() + .map(RelationshipView::getRelationship) + .filter(r -> !elements.contains(r.getSource()) && !elements.contains(r.getDestination())) + .forEach(this::remove); + } + } + /** * Gets the set of elements in this view. * diff --git a/structurizr-core/src/main/java/com/structurizr/view/StaticView.java b/structurizr-core/src/main/java/com/structurizr/view/StaticView.java index f7ea1d9dd..1be47ef96 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/StaticView.java +++ b/structurizr-core/src/main/java/com/structurizr/view/StaticView.java @@ -25,10 +25,17 @@ public abstract class StaticView extends ModelView implements AnimatedView { } /** - * Adds the default set of elements to this view. + * Adds the default set of elements and relationships to this view. */ public abstract void addDefaultElements(); + /** + * Adds the default set of elements and relationships to this view. + * + * @param greedy true (add all relationships) or false (depends on view type) + */ + public abstract void addDefaultElements(boolean greedy); + /** * Adds all software systems in the model to this view. */ diff --git a/structurizr-core/src/main/java/com/structurizr/view/SystemContextView.java b/structurizr-core/src/main/java/com/structurizr/view/SystemContextView.java index bef228fd8..513fe02c1 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/SystemContextView.java +++ b/structurizr-core/src/main/java/com/structurizr/view/SystemContextView.java @@ -39,9 +39,22 @@ public String getName() { */ @Override public void addDefaultElements() { + addDefaultElements(true); + } + + /** + * Adds the default set of elements and relationships to this view. + * + * @param greedy true (add all relationships) or false (adds relationships to/from the scoped software system only) + */ + public void addDefaultElements(boolean greedy) { addNearestNeighbours(getSoftwareSystem(), CustomElement.class); addNearestNeighbours(getSoftwareSystem(), Person.class); addNearestNeighbours(getSoftwareSystem(), SoftwareSystem.class); + + if (!greedy) { + removeRelationshipsNotConnectedToElement(getSoftwareSystem()); + } } /** diff --git a/structurizr-core/src/main/java/com/structurizr/view/SystemLandscapeView.java b/structurizr-core/src/main/java/com/structurizr/view/SystemLandscapeView.java index ce719e07f..359245180 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/SystemLandscapeView.java +++ b/structurizr-core/src/main/java/com/structurizr/view/SystemLandscapeView.java @@ -54,6 +54,15 @@ void setModel(Model model) { */ @Override public void addDefaultElements() { + addDefaultElements(true); + } + + /** + * Adds the default set of elements and relationships to this view. + * + * @param greedy true (add all relationships) or false (add all relationships) + */ + public void addDefaultElements(boolean greedy) { addAllSoftwareSystems(); addAllPeople(); @@ -61,8 +70,8 @@ public void addDefaultElements() { } /** - * Adds all software systems and all people to this view. - */ + * Adds all software systems and all people to this view. + */ @Override public void addAllElements() { addAllSoftwareSystems(); diff --git a/structurizr-core/src/test/java/com/structurizr/view/ComponentViewTests.java b/structurizr-core/src/test/java/com/structurizr/view/ComponentViewTests.java index 9c80529d7..db21ee52e 100644 --- a/structurizr-core/src/test/java/com/structurizr/view/ComponentViewTests.java +++ b/structurizr-core/src/test/java/com/structurizr/view/ComponentViewTests.java @@ -614,6 +614,70 @@ void addDefaultElements() { assertFalse(view.getElements().contains(new ElementView(component2))); } + @Test + void addDefaultElements_WhenGreedyIsTrue() { + model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); + + SoftwareSystem a = model.addSoftwareSystem("A"); + Container a1 = a.addContainer("A1", "", ""); + Component aa1 = a1.addComponent("AA1", "", ""); + Component aa2 = a1.addComponent("AA2", "", ""); + SoftwareSystem b = model.addSoftwareSystem("B"); + SoftwareSystem c = model.addSoftwareSystem("C"); + + aa1.uses(aa2, "Uses"); + aa1.uses(b, "Uses"); + aa2.uses(c, "Uses"); + b.uses(c, "Uses"); + + view = new ComponentView(a1, "components", "Description"); + view.addDefaultElements(true); + + assertEquals(4, view.getElements().size()); + assertTrue(view.getElements().contains(new ElementView(aa1))); + assertTrue(view.getElements().contains(new ElementView(aa2))); + assertTrue(view.getElements().contains(new ElementView(b))); + assertTrue(view.getElements().contains(new ElementView(c))); + + assertEquals(4, view.getRelationships().size()); + assertNotNull(view.getRelationshipView(aa1.getEfferentRelationshipWith(aa2))); + assertNotNull(view.getRelationshipView(aa1.getEfferentRelationshipWith(b))); + assertNotNull(view.getRelationshipView(aa2.getEfferentRelationshipWith(c))); + assertNotNull(view.getRelationshipView(b.getEfferentRelationshipWith(c))); + } + + @Test + void addDefaultElements_WhenGreedyIsFalse() { + model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); + + SoftwareSystem a = model.addSoftwareSystem("A"); + Container a1 = a.addContainer("A1", "", ""); + Component aa1 = a1.addComponent("AA1", "", ""); + Component aa2 = a1.addComponent("AA2", "", ""); + SoftwareSystem b = model.addSoftwareSystem("B"); + SoftwareSystem c = model.addSoftwareSystem("C"); + + aa1.uses(aa2, "Uses"); + aa1.uses(b, "Uses"); + aa2.uses(c, "Uses"); + b.uses(c, "Uses"); + + view = new ComponentView(a1, "components", "Description"); + view.addDefaultElements(false); + + assertEquals(4, view.getElements().size()); + assertTrue(view.getElements().contains(new ElementView(aa1))); + assertTrue(view.getElements().contains(new ElementView(aa2))); + assertTrue(view.getElements().contains(new ElementView(b))); + assertTrue(view.getElements().contains(new ElementView(c))); + + assertEquals(3, view.getRelationships().size()); + assertNotNull(view.getRelationshipView(aa1.getEfferentRelationshipWith(aa2))); + assertNotNull(view.getRelationshipView(aa1.getEfferentRelationshipWith(b))); + assertNotNull(view.getRelationshipView(aa2.getEfferentRelationshipWith(c))); + assertNull(view.getRelationshipView(b.getEfferentRelationshipWith(c))); + } + @Test void addSoftwareSystem_ThrowsAnException_WhenTheSoftwareSystemIsTheScopeOfTheView() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); diff --git a/structurizr-core/src/test/java/com/structurizr/view/ContainerViewTests.java b/structurizr-core/src/test/java/com/structurizr/view/ContainerViewTests.java index 356cbd0dc..a9caf817d 100644 --- a/structurizr-core/src/test/java/com/structurizr/view/ContainerViewTests.java +++ b/structurizr-core/src/test/java/com/structurizr/view/ContainerViewTests.java @@ -306,6 +306,68 @@ void addDefaultElements() { assertFalse(view.getElements().contains(new ElementView(container2))); } + @Test + void addDefaultElements_WhenGreedyIsTrue() { + model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); + + SoftwareSystem a = model.addSoftwareSystem("A"); + Container a1 = a.addContainer("A1"); + Container a2 = a.addContainer("A2"); + SoftwareSystem b = model.addSoftwareSystem("B"); + SoftwareSystem c = model.addSoftwareSystem("C"); + + a1.uses(a2, "Uses"); + a1.uses(b, "Uses"); + a2.uses(c, "Uses"); + b.uses(c, "Uses"); + + view = new ContainerView(a, "containers", "Description"); + view.addDefaultElements(true); + + assertEquals(4, view.getElements().size()); + assertTrue(view.getElements().contains(new ElementView(a1))); + assertTrue(view.getElements().contains(new ElementView(a2))); + assertTrue(view.getElements().contains(new ElementView(b))); + assertTrue(view.getElements().contains(new ElementView(c))); + + assertEquals(4, view.getRelationships().size()); + assertNotNull(view.getRelationshipView(a1.getEfferentRelationshipWith(a2))); + assertNotNull(view.getRelationshipView(a1.getEfferentRelationshipWith(b))); + assertNotNull(view.getRelationshipView(a2.getEfferentRelationshipWith(c))); + assertNotNull(view.getRelationshipView(b.getEfferentRelationshipWith(c))); + } + + @Test + void addDefaultElements_WhenGreedyIsFalse() { + model.setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); + + SoftwareSystem a = model.addSoftwareSystem("A"); + Container a1 = a.addContainer("A1"); + Container a2 = a.addContainer("A2"); + SoftwareSystem b = model.addSoftwareSystem("B"); + SoftwareSystem c = model.addSoftwareSystem("C"); + + a1.uses(a2, "Uses"); + a1.uses(b, "Uses"); + a2.uses(c, "Uses"); + b.uses(c, "Uses"); + + view = new ContainerView(a, "containers", "Description"); + view.addDefaultElements(false); + + assertEquals(4, view.getElements().size()); + assertTrue(view.getElements().contains(new ElementView(a1))); + assertTrue(view.getElements().contains(new ElementView(a2))); + assertTrue(view.getElements().contains(new ElementView(b))); + assertTrue(view.getElements().contains(new ElementView(c))); + + assertEquals(3, view.getRelationships().size()); + assertNotNull(view.getRelationshipView(a1.getEfferentRelationshipWith(a2))); + assertNotNull(view.getRelationshipView(a1.getEfferentRelationshipWith(b))); + assertNotNull(view.getRelationshipView(a2.getEfferentRelationshipWith(c))); + assertNull(view.getRelationshipView(b.getEfferentRelationshipWith(c))); + } + @Test void addSoftwareSystem_ThrowsAnException_WhenTheSoftwareSystemIsTheScopeOfTheView() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); diff --git a/structurizr-core/src/test/java/com/structurizr/view/SystemContextViewTests.java b/structurizr-core/src/test/java/com/structurizr/view/SystemContextViewTests.java index 87f5a631a..bc7694728 100644 --- a/structurizr-core/src/test/java/com/structurizr/view/SystemContextViewTests.java +++ b/structurizr-core/src/test/java/com/structurizr/view/SystemContextViewTests.java @@ -305,4 +305,42 @@ void addDefaultElements() { assertTrue(view.getElements().contains(new ElementView(softwareSystem2))); } + @Test + void addDefaultElements_WhenGreedyIsTrue() { + SoftwareSystem a = model.addSoftwareSystem("A"); + SoftwareSystem b = model.addSoftwareSystem("B"); + SoftwareSystem c = model.addSoftwareSystem("C"); + + Relationship ab = a.uses(b, "Uses"); + Relationship ac = a.uses(c, "Uses"); + Relationship bc = b.uses(c, "Uses"); + + view = views.createSystemContextView(a, "key", "description"); + view.addDefaultElements(true); + + assertEquals(3, view.getElements().size()); + assertNotNull(view.getRelationshipView(ab)); + assertNotNull(view.getRelationshipView(ac)); + assertNotNull(view.getRelationshipView(bc)); + } + + @Test + void addDefaultElements_WhenGreedyIsFalse() { + SoftwareSystem a = model.addSoftwareSystem("A"); + SoftwareSystem b = model.addSoftwareSystem("B"); + SoftwareSystem c = model.addSoftwareSystem("C"); + + Relationship ab = a.uses(b, "Uses"); + Relationship ac = a.uses(c, "Uses"); + Relationship bc = b.uses(c, "Uses"); + + view = views.createSystemContextView(a, "key", "description"); + view.addDefaultElements(false); + + assertEquals(3, view.getElements().size()); + assertNotNull(view.getRelationshipView(ab)); + assertNotNull(view.getRelationshipView(ac)); + assertNull(view.getRelationshipView(bc)); + } + } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelViewContentParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelViewContentParser.java index 2f89808c0..4f16ebcdb 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelViewContentParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelViewContentParser.java @@ -6,6 +6,7 @@ abstract class ModelViewContentParser extends AbstractParser { protected static final String WILDCARD = "*"; + protected static final String WILDCARD_RELUCTANT = "*?"; protected static final String ELEMENT_WILDCARD = "element==*"; protected boolean isExpression(String token) { diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StaticViewContentParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StaticViewContentParser.java index 85d7dec9d..a6c653d0b 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StaticViewContentParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StaticViewContentParser.java @@ -2,10 +2,7 @@ import com.structurizr.model.*; import com.structurizr.util.StringUtils; -import com.structurizr.view.ComponentView; -import com.structurizr.view.ContainerView; -import com.structurizr.view.ElementNotPermittedInViewException; -import com.structurizr.view.StaticView; +import com.structurizr.view.*; final class StaticViewContentParser extends ModelViewContentParser { @@ -13,7 +10,11 @@ final class StaticViewContentParser extends ModelViewContentParser { void parseInclude(StaticViewDslContext context, Tokens tokens) { if (!tokens.includes(FIRST_IDENTIFIER_INDEX)) { - throw new RuntimeException("Expected: include <*|identifier|expression> [*|identifier|expression...]"); + if (context.getView() instanceof SystemContextView || context.getView() instanceof ContainerView || context.getView() instanceof ComponentView) { + throw new RuntimeException("Expected: include <*|*?|identifier|expression> [*|identifier|expression...]"); + } else { + throw new RuntimeException("Expected: include <*|identifier|expression> [*|identifier|expression...]"); + } } StaticView view = context.getView(); @@ -24,7 +25,10 @@ void parseInclude(StaticViewDslContext context, Tokens tokens) { if (token.equals(WILDCARD) || token.equals(ELEMENT_WILDCARD)) { // include * or include element==* - view.addDefaultElements(); + view.addDefaultElements(true); + } else if (token.equals(WILDCARD_RELUCTANT)) { + // include *? + view.addDefaultElements(false); } else if (isExpression(token)) { new StaticViewExpressionParser().parseExpression(token, context).forEach(mi -> addModelItemToView(mi, view, null)); } else { diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/StaticViewContentParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/StaticViewContentParserTests.java index 82d572de7..043a723fe 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/StaticViewContentParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/StaticViewContentParserTests.java @@ -76,7 +76,7 @@ void test_parseInclude_ThrowsAnException_WhenTryingToAddAComponentToASystemLands } @Test - void test_parseInclude_AddsAllPeopleAndSoftwareSystemsToASystemLandscapeView_WhenTheWildcardIsSpecified() { + void test_parseInclude_AddsAllPeopleAndSoftwareSystemsToASystemLandscapeView_WhenTheGreedyWildcardIsSpecified() { Person user = model.addPerson("User", "Description"); SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1", "Description"); softwareSystem1.addContainer("Container 1", "Description", "Technology"); @@ -94,6 +94,25 @@ void test_parseInclude_AddsAllPeopleAndSoftwareSystemsToASystemLandscapeView_Whe assertEquals(2, view.getRelationships().size()); } + @Test + void test_parseInclude_AddsAllPeopleAndSoftwareSystemsToASystemLandscapeView_WhenTheReluctantWildcardIsSpecified() { + Person user = model.addPerson("User", "Description"); + SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1", "Description"); + softwareSystem1.addContainer("Container 1", "Description", "Technology"); + SoftwareSystem softwareSystem2 = model.addSoftwareSystem("Software System 2", "Description"); + user.uses(softwareSystem1, "Uses"); + softwareSystem1.uses(softwareSystem2, "Uses"); + + SystemLandscapeView view = views.createSystemLandscapeView("key", "Description"); + SystemLandscapeViewDslContext context = new SystemLandscapeViewDslContext(view); + context.setWorkspace(workspace); + + parser.parseInclude(context, tokens("include", "*?")); + + assertEquals(3, view.getElements().size()); + assertEquals(2, view.getRelationships().size()); + } + @Test void test_parseInclude_AddsTheSpecifiedElementsToASystemLandscapeView() { Person user = model.addPerson("User", "Description"); @@ -126,12 +145,13 @@ void test_parseInclude_AddsTheSpecifiedElementsToASystemLandscapeView() { } @Test - void test_parseInclude_AddsNearestNeighboursToASystemContextView_WhenTheWildcardIsSpecified() { + void test_parseInclude_AddsNearestNeighboursToASystemContextView_WhenTheGreedyWildcardIsSpecified() { Person user = model.addPerson("User", "Description"); SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1", "Description"); softwareSystem1.addContainer("Container 1", "Description", "Technology"); SoftwareSystem softwareSystem2 = model.addSoftwareSystem("Software System 2", "Description"); user.uses(softwareSystem1, "Uses"); + user.uses(softwareSystem2, "Uses"); softwareSystem1.uses(softwareSystem2, "Uses"); SystemContextView view = views.createSystemContextView(softwareSystem1, "key", "Description"); @@ -140,6 +160,26 @@ void test_parseInclude_AddsNearestNeighboursToASystemContextView_WhenTheWildcard parser.parseInclude(context, tokens("include", "*")); + assertEquals(3, view.getElements().size()); + assertEquals(3, view.getRelationships().size()); + } + + @Test + void test_parseInclude_AddsNearestNeighboursToASystemContextView_WhenTheReluctantWildcardIsSpecified() { + Person user = model.addPerson("User", "Description"); + SoftwareSystem softwareSystem1 = model.addSoftwareSystem("Software System 1", "Description"); + softwareSystem1.addContainer("Container 1", "Description", "Technology"); + SoftwareSystem softwareSystem2 = model.addSoftwareSystem("Software System 2", "Description"); + user.uses(softwareSystem1, "Uses"); + user.uses(softwareSystem2, "Uses"); + softwareSystem1.uses(softwareSystem2, "Uses"); + + SystemContextView view = views.createSystemContextView(softwareSystem1, "key", "Description"); + SystemContextViewDslContext context = new SystemContextViewDslContext(view); + context.setWorkspace(workspace); + + parser.parseInclude(context, tokens("include", "*?")); + assertEquals(3, view.getElements().size()); assertEquals(2, view.getRelationships().size()); } From a3af9314dfc0e3f58235be1331696e47ed61dcc3 Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Thu, 20 Feb 2025 09:08:35 +0000 Subject: [PATCH 625/717] Fixes an issue extending relationship archetypes. --- .../com/structurizr/dsl/StructurizrDslParser.java | 11 +++++------ .../src/test/java/com/structurizr/dsl/DslTests.java | 1 + .../test/resources/dsl/archetypes-for-extension.dsl | 6 +++++- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index c2a68603a..1c9750175 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -1361,12 +1361,11 @@ private Archetype getArchetype(String archetypeType, String archetypeName) { private void extendArchetype(Archetype archetype, String archetypeName) { archetypeName = archetypeName.toLowerCase(); - Archetype parentArchetype = archetypes.get(archetype.getType()).get(archetypeName); - if (parentArchetype != null) { - archetype.setDescription(parentArchetype.getDescription()); - archetype.setTechnology(parentArchetype.getTechnology()); - archetype.addTags(parentArchetype.getTags().toArray(new String[0])); - } + Archetype parentArchetype = getArchetype(archetype.getType(), archetypeName); + + archetype.setDescription(parentArchetype.getDescription()); + archetype.setTechnology(parentArchetype.getTechnology()); + archetype.addTags(parentArchetype.getTags().toArray(new String[0])); } /** diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index 9b766fa9f..b92c0da3c 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -1537,6 +1537,7 @@ void test_archetypesForExtension() throws Exception { assertEquals("Makes API calls to", r.getDescription()); assertEquals("HTTPS", r.getTechnology()); assertTrue(r.hasTag("Default Tag")); + assertTrue(r.hasTag("Synchronous")); assertTrue(r.hasTag("HTTPS")); } diff --git a/structurizr-dsl/src/test/resources/dsl/archetypes-for-extension.dsl b/structurizr-dsl/src/test/resources/dsl/archetypes-for-extension.dsl index ac5116163..105e25b44 100644 --- a/structurizr-dsl/src/test/resources/dsl/archetypes-for-extension.dsl +++ b/structurizr-dsl/src/test/resources/dsl/archetypes-for-extension.dsl @@ -15,7 +15,11 @@ workspace { tag "Default Tag" } - https = -> { + sync = -> { + tag "Synchronous" + } + + https = --sync-> { technology "HTTPS" tag "HTTPS" } From 183cbfd7bb309b7383a8a8031b92bfd3c48d9055 Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Fri, 21 Feb 2025 22:32:52 +0000 Subject: [PATCH 626/717] GraphvizAutomaticLayout.apply(Workspace) now only applies automatic layout to views that have Graphviz configured. --- .../autolayout/graphviz/DOTExporter.java | 39 ++------- .../graphviz/GraphvizAutomaticLayout.java | 63 ++++++++++----- .../autolayout/graphviz/DOTExporterTests.java | 79 ++++++++++--------- .../GraphvizAutomaticLayoutTests.java | 19 +++-- 4 files changed, 104 insertions(+), 96 deletions(-) diff --git a/structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/DOTExporter.java b/structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/DOTExporter.java index bff97a7e3..013cb3446 100644 --- a/structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/DOTExporter.java +++ b/structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/DOTExporter.java @@ -20,16 +20,16 @@ class DOTExporter extends AbstractDiagramExporter { private static final int CLUSTER_INTERNAL_MARGIN = 25; private Locale locale = Locale.US; - private RankDirection rankDirection; - private double rankSeparation; - private double nodeSeparation; + private final RankDirection rankDirection; + private final double rankSeparation; + private final double nodeSeparation; private int groupId = 1; DOTExporter(RankDirection rankDirection, double rankSeparation, double nodeSeparation) { - this.rankDirection = rankDirection; - this.rankSeparation = rankSeparation; - this.nodeSeparation = nodeSeparation; + this.rankDirection = rankDirection != null ? rankDirection : RankDirection.TopBottom; + this.rankSeparation = rankSeparation / Constants.STRUCTURIZR_DPI; + this.nodeSeparation = nodeSeparation / Constants.STRUCTURIZR_DPI; } void setLocale(Locale locale) { @@ -38,33 +38,6 @@ void setLocale(Locale locale) { @Override protected void writeHeader(ModelView view, IndentingWriter writer) { - if (view.getAutomaticLayout() != null) { - if (view.getAutomaticLayout().getRankDirection() == null) { - rankDirection = RankDirection.TopBottom; - } else { - switch (view.getAutomaticLayout().getRankDirection()) { - case TopBottom: - rankDirection = RankDirection.TopBottom; - break; - case BottomTop: - rankDirection = RankDirection.BottomTop; - break; - case LeftRight: - rankDirection = RankDirection.LeftRight; - break; - case RightLeft: - rankDirection = RankDirection.RightLeft; - break; - } - } - - rankSeparation = view.getAutomaticLayout().getRankSeparation(); - nodeSeparation = view.getAutomaticLayout().getNodeSeparation(); - } - - rankSeparation = rankSeparation / Constants.STRUCTURIZR_DPI; - nodeSeparation = nodeSeparation / Constants.STRUCTURIZR_DPI; - writer.writeLine("digraph {"); writer.indent(); writer.writeLine("compound=true"); diff --git a/structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/GraphvizAutomaticLayout.java b/structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/GraphvizAutomaticLayout.java index 07ca03630..efd3c5c7e 100644 --- a/structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/GraphvizAutomaticLayout.java +++ b/structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/GraphvizAutomaticLayout.java @@ -30,8 +30,8 @@ public class GraphvizAutomaticLayout { private final File path; private RankDirection rankDirection = RankDirection.TopBottom; - private double rankSeparation = 1.0; - private double nodeSeparation = 1.0; + private double rankSeparation = 300; + private double nodeSeparation = 300; private int margin = 400; private boolean changePaperSize = true; @@ -75,8 +75,21 @@ public void setLocale(Locale locale) { this.locale = locale; } - private DOTExporter createDOTExporter() { - DOTExporter exporter = new DOTExporter(rankDirection, rankSeparation, nodeSeparation); + private DOTExporter createDOTExporter(AutomaticLayout automaticLayout) { + DOTExporter exporter; + + if (automaticLayout == null) { + // use the configured defaults + exporter = new DOTExporter(rankDirection, rankSeparation, nodeSeparation); + } else { + // use the values from the automatic layout configuration associated with the view + exporter = new DOTExporter( + RankDirection.valueOf(automaticLayout.getRankDirection().name()), + automaticLayout.getRankSeparation(), + automaticLayout.getNodeSeparation() + ); + } + exporter.setLocale(locale); return exporter; @@ -130,7 +143,7 @@ private void runGraphviz(View view) throws Exception { public void apply(CustomView view) throws Exception { log.debug("Running Graphviz for view with key " + view.getKey()); - Diagram diagram = createDOTExporter().export(view); + Diagram diagram = createDOTExporter(view.getAutomaticLayout()).export(view); writeFile(diagram); runGraphviz(view); createSVGReader().parseAndApplyLayout(view); @@ -138,7 +151,7 @@ public void apply(CustomView view) throws Exception { public void apply(SystemLandscapeView view) throws Exception { log.debug("Running Graphviz for view with key " + view.getKey()); - Diagram diagram = createDOTExporter().export(view); + Diagram diagram = createDOTExporter(view.getAutomaticLayout()).export(view); writeFile(diagram); runGraphviz(view); createSVGReader().parseAndApplyLayout(view); @@ -146,7 +159,7 @@ public void apply(SystemLandscapeView view) throws Exception { public void apply(SystemContextView view) throws Exception { log.debug("Running Graphviz for view with key " + view.getKey()); - Diagram diagram = createDOTExporter().export(view); + Diagram diagram = createDOTExporter(view.getAutomaticLayout()).export(view); writeFile(diagram); runGraphviz(view); createSVGReader().parseAndApplyLayout(view); @@ -154,7 +167,7 @@ public void apply(SystemContextView view) throws Exception { public void apply(ContainerView view) throws Exception { log.debug("Running Graphviz for view with key " + view.getKey()); - Diagram diagram = createDOTExporter().export(view); + Diagram diagram = createDOTExporter(view.getAutomaticLayout()).export(view); writeFile(diagram); runGraphviz(view); createSVGReader().parseAndApplyLayout(view); @@ -162,7 +175,7 @@ public void apply(ContainerView view) throws Exception { public void apply(ComponentView view) throws Exception { log.debug("Running Graphviz for view with key " + view.getKey()); - Diagram diagram = createDOTExporter().export(view); + Diagram diagram = createDOTExporter(view.getAutomaticLayout()).export(view); writeFile(diagram); runGraphviz(view); createSVGReader().parseAndApplyLayout(view); @@ -170,7 +183,7 @@ public void apply(ComponentView view) throws Exception { public void apply(DynamicView view) throws Exception { log.debug("Running Graphviz for view with key " + view.getKey()); - Diagram diagram = createDOTExporter().export(view); + Diagram diagram = createDOTExporter(view.getAutomaticLayout()).export(view); writeFile(diagram); runGraphviz(view); createSVGReader().parseAndApplyLayout(view); @@ -178,7 +191,7 @@ public void apply(DynamicView view) throws Exception { public void apply(DeploymentView view) throws Exception { log.debug("Running Graphviz for view with key " + view.getKey()); - Diagram diagram = createDOTExporter().export(view); + Diagram diagram = createDOTExporter(view.getAutomaticLayout()).export(view); writeFile(diagram); runGraphviz(view); createSVGReader().parseAndApplyLayout(view); @@ -186,31 +199,45 @@ public void apply(DeploymentView view) throws Exception { public void apply(Workspace workspace) throws Exception { for (CustomView view : workspace.getViews().getCustomViews()) { - apply(view); + if (view.getAutomaticLayout() != null && view.getAutomaticLayout().getImplementation() == AutomaticLayout.Implementation.Graphviz) { + apply(view); + } } for (SystemLandscapeView view : workspace.getViews().getSystemLandscapeViews()) { - apply(view); + if (view.getAutomaticLayout() != null && view.getAutomaticLayout().getImplementation() == AutomaticLayout.Implementation.Graphviz) { + apply(view); + } } for (SystemContextView view : workspace.getViews().getSystemContextViews()) { - apply(view); + if (view.getAutomaticLayout() != null && view.getAutomaticLayout().getImplementation() == AutomaticLayout.Implementation.Graphviz) { + apply(view); + } } for (ContainerView view : workspace.getViews().getContainerViews()) { - apply(view); + if (view.getAutomaticLayout() != null && view.getAutomaticLayout().getImplementation() == AutomaticLayout.Implementation.Graphviz) { + apply(view); + } } for (ComponentView view : workspace.getViews().getComponentViews()) { - apply(view); + if (view.getAutomaticLayout() != null && view.getAutomaticLayout().getImplementation() == AutomaticLayout.Implementation.Graphviz) { + apply(view); + } } for (DynamicView view : workspace.getViews().getDynamicViews()) { - apply(view); + if (view.getAutomaticLayout() != null && view.getAutomaticLayout().getImplementation() == AutomaticLayout.Implementation.Graphviz) { + apply(view); + } } for (DeploymentView view : workspace.getViews().getDeploymentViews()) { - apply(view); + if (view.getAutomaticLayout() != null && view.getAutomaticLayout().getImplementation() == AutomaticLayout.Implementation.Graphviz) { + apply(view); + } } } diff --git a/structurizr-autolayout/src/test/java/com/structurizr/autolayout/graphviz/DOTExporterTests.java b/structurizr-autolayout/src/test/java/com/structurizr/autolayout/graphviz/DOTExporterTests.java index 367015556..fc58220ce 100644 --- a/structurizr-autolayout/src/test/java/com/structurizr/autolayout/graphviz/DOTExporterTests.java +++ b/structurizr-autolayout/src/test/java/com/structurizr/autolayout/graphviz/DOTExporterTests.java @@ -483,49 +483,50 @@ public void test_writeContainerViewWithGroupedElements_WithAndWithoutAGroupSepar @Test public void test_AmazonWebServicesExample() throws Exception { Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("src/test/resources/structurizr-54915-workspace.json")); - DOTExporter exporter = new DOTExporter(RankDirection.TopBottom, 300, 300); + DOTExporter exporter = new DOTExporter(RankDirection.LeftRight, 300, 300); Diagram diagram = exporter.export(workspace.getViews().getDeploymentViews().iterator().next()); String content = diagram.getDefinition(); - String expectedResult = "digraph {\n" + - " compound=true\n" + - " graph [splines=polyline,rankdir=LR,ranksep=1.0,nodesep=1.0,fontsize=5]\n" + - " node [shape=box,fontsize=5]\n" + - " edge []\n" + - "\n" + - " subgraph cluster_5 {\n" + - " margin=25\n" + - " subgraph cluster_6 {\n" + - " margin=25\n" + - " subgraph cluster_12 {\n" + - " margin=25\n" + - " subgraph cluster_13 {\n" + - " margin=25\n" + - " 14 [width=1.500000,height=1.000000,fixedsize=true,id=14,label=\"14: Database\"]\n" + - " }\n" + - "\n" + - " }\n" + - "\n" + - " 7 [width=1.500000,height=1.000000,fixedsize=true,id=7,label=\"7: Route 53\"]\n" + - " 8 [width=1.500000,height=1.000000,fixedsize=true,id=8,label=\"8: Elastic Load Balancer\"]\n" + - " subgraph cluster_9 {\n" + - " margin=25\n" + - " subgraph cluster_10 {\n" + - " margin=25\n" + - " 11 [width=1.500000,height=1.000000,fixedsize=true,id=11,label=\"11: Web Application\"]\n" + - " }\n" + - "\n" + - " }\n" + - "\n" + - " }\n" + - "\n" + - " }\n" + - "\n" + - " 11 -> 14 [id=15]\n" + - " 7 -> 8 [id=16]\n" + - " 8 -> 11 [id=17]\n" + - "}"; + String expectedResult = """ + digraph { + compound=true + graph [splines=polyline,rankdir=LR,ranksep=1.0,nodesep=1.0,fontsize=5] + node [shape=box,fontsize=5] + edge [] + + subgraph cluster_5 { + margin=25 + subgraph cluster_6 { + margin=25 + subgraph cluster_12 { + margin=25 + subgraph cluster_13 { + margin=25 + 14 [width=1.500000,height=1.000000,fixedsize=true,id=14,label="14: Database"] + } + + } + + 7 [width=1.500000,height=1.000000,fixedsize=true,id=7,label="7: Route 53"] + 8 [width=1.500000,height=1.000000,fixedsize=true,id=8,label="8: Elastic Load Balancer"] + subgraph cluster_9 { + margin=25 + subgraph cluster_10 { + margin=25 + 11 [width=1.500000,height=1.000000,fixedsize=true,id=11,label="11: Web Application"] + } + + } + + } + + } + + 11 -> 14 [id=15] + 7 -> 8 [id=16] + 8 -> 11 [id=17] + }"""; assertEquals(expectedResult, content); } diff --git a/structurizr-autolayout/src/test/java/com/structurizr/autolayout/graphviz/GraphvizAutomaticLayoutTests.java b/structurizr-autolayout/src/test/java/com/structurizr/autolayout/graphviz/GraphvizAutomaticLayoutTests.java index 233423401..c499674a4 100644 --- a/structurizr-autolayout/src/test/java/com/structurizr/autolayout/graphviz/GraphvizAutomaticLayoutTests.java +++ b/structurizr-autolayout/src/test/java/com/structurizr/autolayout/graphviz/GraphvizAutomaticLayoutTests.java @@ -5,6 +5,7 @@ import com.structurizr.model.Person; import com.structurizr.model.SoftwareSystem; import com.structurizr.model.Tags; +import com.structurizr.view.AutomaticLayout; import com.structurizr.view.Shape; import com.structurizr.view.SystemContextView; import org.junit.jupiter.api.Test; @@ -17,7 +18,10 @@ public class GraphvizAutomaticLayoutTests { @Test - public void test() throws Exception { + public void apply_Workspace() throws Exception { + File tempDir = Files.createTempDirectory("graphviz").toFile(); + GraphvizAutomaticLayout graphviz = new GraphvizAutomaticLayout(tempDir); + Workspace workspace = new Workspace("Name", ""); Person user = workspace.getModel().addPerson("User"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); @@ -33,12 +37,15 @@ public void test() throws Exception { assertEquals(0, view.getElementView(softwareSystem).getX()); assertEquals(0, view.getElementView(softwareSystem).getY()); - File tempDir = Files.createTempDirectory("graphviz").toFile(); - GraphvizAutomaticLayout graphviz = new GraphvizAutomaticLayout(tempDir); - graphviz.setRankSeparation(300); - graphviz.setNodeSeparation(300); - graphviz.setMargin(400); + graphviz.apply(workspace); + + // no change - the view doesn't have automatic layout configured + assertEquals(0, view.getElementView(user).getX()); + assertEquals(0, view.getElementView(user).getY()); + assertEquals(0, view.getElementView(softwareSystem).getX()); + assertEquals(0, view.getElementView(softwareSystem).getY()); + view.enableAutomaticLayout(AutomaticLayout.RankDirection.TopBottom); graphviz.apply(workspace); assertEquals(233, view.getElementView(user).getX()); From db96f9ad6181e63b2294072e382119c2feb8909a Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Wed, 12 Mar 2025 08:41:58 +0000 Subject: [PATCH 627/717] structurizr-dsl: Removes deprecated `!ref` and `!extend` keywords. --- changelog.md | 1 + .../main/java/com/structurizr/dsl/StructurizrDslParser.java | 6 ++---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/changelog.md b/changelog.md index c4b7acf70..d9501024d 100644 --- a/changelog.md +++ b/changelog.md @@ -8,6 +8,7 @@ - structurizr-dsl: Adds a reluctant version of `include *` (`include *?`) for system context views that only adds relationships to/from the scoped software system. - structurizr-dsl: Adds a reluctant version of `include *` (`include *?`) for container views that only adds relationships to/from the containers in the scoped software system. - structurizr-dsl: Adds a reluctant version of `include *` (`include *?`) for component views that only adds relationships to/from the components in the scoped container. +- structurizr-dsl: Removes deprecated `!ref` and `!extend` keywords. ## 3.2.1 (10th December 2024) diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index 1c9750175..acf83a3b2 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -361,11 +361,9 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn ModelItem modelItem = null; if (REF_TOKEN.equalsIgnoreCase(firstToken)) { - log.warn(REF_TOKEN + " has been deprecated and will be removed in a future release - please use !element or !relationship instead"); - modelItem = new RefParser().parse(getContext(), tokens.withoutContextStartToken()); + throw new RuntimeException(REF_TOKEN + " was previously deprecated, and has now been removed - please use " + FIND_ELEMENT_TOKEN + " or " + FIND_RELATIONSHIP_TOKEN + " instead"); } else if (EXTEND_TOKEN.equalsIgnoreCase(firstToken)) { - log.warn(EXTEND_TOKEN + " has been deprecated and will be removed in a future release - please use !element or !relationship instead"); - modelItem = new RefParser().parse(getContext(), tokens.withoutContextStartToken()); + throw new RuntimeException(EXTEND_TOKEN + " was previously deprecated, and has now been removed - please use " + FIND_ELEMENT_TOKEN + " or " + FIND_RELATIONSHIP_TOKEN + " instead"); } else if (FIND_ELEMENT_TOKEN.equalsIgnoreCase(firstToken)) { modelItem = new FindElementParser().parse(getContext(), tokens.withoutContextStartToken()); } else if (FIND_RELATIONSHIP_TOKEN.equalsIgnoreCase(firstToken)) { From 1f7212606c8b1258c8ad327ed3af7341304fa34b Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Mon, 17 Mar 2025 09:18:48 +0000 Subject: [PATCH 628/717] structurizr-dsl: Adds support for Java style `"""` multi-line text blocks. --- changelog.md | 1 + .../structurizr/dsl/StructurizrDslParser.java | 31 +++++++++++++++++-- .../java/com/structurizr/dsl/DslTests.java | 12 +++++++ .../src/test/resources/dsl/text-block.dsl | 13 ++++++++ 4 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 structurizr-dsl/src/test/resources/dsl/text-block.dsl diff --git a/changelog.md b/changelog.md index d9501024d..86ca83d9f 100644 --- a/changelog.md +++ b/changelog.md @@ -9,6 +9,7 @@ - structurizr-dsl: Adds a reluctant version of `include *` (`include *?`) for container views that only adds relationships to/from the containers in the scoped software system. - structurizr-dsl: Adds a reluctant version of `include *` (`include *?`) for component views that only adds relationships to/from the components in the scoped container. - structurizr-dsl: Removes deprecated `!ref` and `!extend` keywords. +- structurizr-dsl: Adds support for Java style `"""` multi-line text blocks. ## 3.2.1 (10th December 2024) diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index acf83a3b2..594b0316d 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -34,6 +34,7 @@ public final class StructurizrDslParser extends StructurizrDslTokens { private static final String MULTI_LINE_COMMENT_START_TOKEN = "/*"; private static final String MULTI_LINE_COMMENT_END_TOKEN = "*/"; private static final String MULTI_LINE_SEPARATOR = "\\"; + private static final String TEXT_BLOCK_MARKER = "\"\"\""; private static final Pattern STRING_SUBSTITUTION_PATTERN = Pattern.compile("(\\$\\{[a-zA-Z0-9-_.]+?})"); @@ -1207,11 +1208,37 @@ private List preProcessLines(List lines) { int lineNumber = 1; StringBuilder buf = new StringBuilder(); boolean lineComplete = true; + boolean textBlock = false; + int textBlockLeadingSpace = -1; for (String line : lines) { - if (!COMMENT_PATTERN.matcher(line).matches() && line.endsWith(MULTI_LINE_SEPARATOR)) { - buf.append(line, 0, line.length()-1); + if (textBlock) { + if (line.endsWith(TEXT_BLOCK_MARKER)) { + buf.append("\""); + textBlock = false; + textBlockLeadingSpace = -1; + lineComplete = true; + } else { + if (textBlockLeadingSpace == -1) { + textBlockLeadingSpace = 0; + for (int i = 0; i < line.length(); i++) { + if (Character.isWhitespace(line.charAt(i))) { + textBlockLeadingSpace++; + } else { + break; + } + } + } + buf.append(line, textBlockLeadingSpace, line.length()); + buf.append("\n"); + } + } else if (!COMMENT_PATTERN.matcher(line).matches() && line.endsWith(MULTI_LINE_SEPARATOR)) { + buf.append(line, 0, line.length() - 1); + lineComplete = false; + } else if (!COMMENT_PATTERN.matcher(line).matches() && line.endsWith(TEXT_BLOCK_MARKER)) { + buf.append(line, 0, line.length() - 2); lineComplete = false; + textBlock = true; } else { if (lineComplete) { buf.append(line); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index b92c0da3c..01b2f261b 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -1541,4 +1541,16 @@ void test_archetypesForExtension() throws Exception { assertTrue(r.hasTag("HTTPS")); } + @Test + void test_textBlock() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/text-block.dsl")); + + SoftwareSystem softwareSystem = parser.getWorkspace().getModel().getSoftwareSystemWithName("Name"); + assertEquals(""" + - Line 1 + - Line 2 + - Line 3""", softwareSystem.getDescription()); + } + } \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/text-block.dsl b/structurizr-dsl/src/test/resources/dsl/text-block.dsl new file mode 100644 index 000000000..a96042760 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/text-block.dsl @@ -0,0 +1,13 @@ +workspace { + + model { + softwareSystem = softwareSystem "Name" { + description """ + - Line 1 + - Line 2 + - Line 3 + """ + } + } + +} \ No newline at end of file From 2b857d8f7205410be69b528a38156ecca36f24ad Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Fri, 28 Mar 2025 08:32:45 +0000 Subject: [PATCH 629/717] Removes the feature flag around archetypes. --- changelog.md | 1 + .../java/com/structurizr/dsl/Features.java | 3 -- .../structurizr/dsl/StructurizrDslParser.java | 36 ++++++++----------- .../java/com/structurizr/dsl/DslTests.java | 18 +--------- 4 files changed, 16 insertions(+), 42 deletions(-) diff --git a/changelog.md b/changelog.md index 86ca83d9f..b7d3dd120 100644 --- a/changelog.md +++ b/changelog.md @@ -10,6 +10,7 @@ - structurizr-dsl: Adds a reluctant version of `include *` (`include *?`) for component views that only adds relationships to/from the components in the scoped container. - structurizr-dsl: Removes deprecated `!ref` and `!extend` keywords. - structurizr-dsl: Adds support for Java style `"""` multi-line text blocks. +- structurizr-dsl: Adds support for defining element and relationship archetypes. ## 3.2.1 (10th December 2024) diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/Features.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/Features.java index 03a99eb4f..3d40aabb4 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/Features.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/Features.java @@ -1,7 +1,4 @@ package com.structurizr.dsl; public final class Features extends com.structurizr.util.Features { - - public static final String ARCHETYPES = "structurizr.feature.dsl.archetypes"; - } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index 594b0316d..c529246ae 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -661,7 +661,7 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn startContext(new ModelDslContext()); parsedTokens.add(MODEL_TOKEN); - } else if (ARCHETYPES_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelDslContext.class) && features.isEnabled(Features.ARCHETYPES)) { + } else if (ARCHETYPES_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelDslContext.class)) { startContext(new ArchetypesDslContext()); } else if (isElementKeywordOrArchetype(firstToken, GROUP_TOKEN) && inContext(ArchetypesDslContext.class)) { @@ -1337,28 +1337,22 @@ public Features getFeatures() { } private boolean isElementKeywordOrArchetype(String token, String keyword) { - if (features.isEnabled(Features.ARCHETYPES)) { - if (token.equalsIgnoreCase(keyword)) { - return true; - } else { - return (archetypes.get(keyword).containsKey(token.toLowerCase())); - } + if (token.equalsIgnoreCase(keyword)) { + return true; } else { - return token.equalsIgnoreCase(keyword); + return (archetypes.get(keyword).containsKey(token.toLowerCase())); } } private boolean isRelationshipKeywordOrArchetype(String token) { - if (features.isEnabled(Features.ARCHETYPES)) { - if (token.equalsIgnoreCase(RELATIONSHIP_TOKEN)) { - return true; - } else if (token.startsWith(RELATIONSHIP_ARCHETYPE_PREFIX) && token.endsWith(RELATIONSHIP_ARCHETYPE_SUFFIX)) { - token = token.substring(RELATIONSHIP_ARCHETYPE_PREFIX.length(), token.length()-RELATIONSHIP_ARCHETYPE_SUFFIX.length()); - return (archetypes.get(RELATIONSHIP_TOKEN).containsKey(token.toLowerCase())); - } + if (token.equalsIgnoreCase(RELATIONSHIP_TOKEN)) { + return true; + } else if (token.startsWith(RELATIONSHIP_ARCHETYPE_PREFIX) && token.endsWith(RELATIONSHIP_ARCHETYPE_SUFFIX)) { + token = token.substring(RELATIONSHIP_ARCHETYPE_PREFIX.length(), token.length()-RELATIONSHIP_ARCHETYPE_SUFFIX.length()); + return (archetypes.get(RELATIONSHIP_TOKEN).containsKey(token.toLowerCase())); } - return token.equalsIgnoreCase(RELATIONSHIP_TOKEN); + return false; } private void addArchetype(Archetype archetype) { @@ -1368,14 +1362,12 @@ private void addArchetype(Archetype archetype) { private Archetype getArchetype(String archetypeType, String archetypeName) { Archetype archetype = null; - if (features.isEnabled(Features.ARCHETYPES)) { - if (RELATIONSHIP_TOKEN.equals(archetypeType)) { - if (archetypeName.startsWith(RELATIONSHIP_ARCHETYPE_PREFIX) && archetypeName.endsWith(RELATIONSHIP_ARCHETYPE_SUFFIX)) { - archetypeName = archetypeName.substring(RELATIONSHIP_ARCHETYPE_PREFIX.length(), archetypeName.length() - RELATIONSHIP_ARCHETYPE_SUFFIX.length()); - } + if (RELATIONSHIP_TOKEN.equals(archetypeType)) { + if (archetypeName.startsWith(RELATIONSHIP_ARCHETYPE_PREFIX) && archetypeName.endsWith(RELATIONSHIP_ARCHETYPE_SUFFIX)) { + archetypeName = archetypeName.substring(RELATIONSHIP_ARCHETYPE_PREFIX.length(), archetypeName.length() - RELATIONSHIP_ARCHETYPE_SUFFIX.length()); } - archetype = archetypes.get(archetypeType).get(archetypeName.toLowerCase()); } + archetype = archetypes.get(archetypeType).get(archetypeName.toLowerCase()); if (archetype == null) { archetype = new Archetype(archetypeName, archetypeType); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index 01b2f261b..be661dd21 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -1459,23 +1459,9 @@ void test_sourceIsNotRetained() throws Exception { } @Test - void test_archetypes_WhenDisabled() throws Exception { - try { - File parentDslFile = new File("src/test/resources/dsl/archetypes.dsl"); - StructurizrDslParser parser = new StructurizrDslParser(); - parser.parse(parentDslFile); - fail(); - } catch (StructurizrDslParserException e) { - assertTrue(e.getMessage().startsWith("Unexpected tokens (expected: !identifiers, group, person, softwareSystem, deploymentEnvironment, element, ->) at line 4")); - assertTrue(e.getMessage().endsWith("archetypes {")); - } - } - - @Test - void test_archetypes_WhenEnabled() throws Exception { + void test_archetypes() throws Exception { File parentDslFile = new File("src/test/resources/dsl/archetypes.dsl"); StructurizrDslParser parser = new StructurizrDslParser(); - parser.getFeatures().enable(Features.ARCHETYPES); parser.parse(parentDslFile); Workspace workspace = parser.getWorkspace(); @@ -1492,7 +1478,6 @@ void test_archetypes_WhenEnabled() throws Exception { void test_archetypesForDefaults() throws Exception { File parentDslFile = new File("src/test/resources/dsl/archetypes-for-defaults.dsl"); StructurizrDslParser parser = new StructurizrDslParser(); - parser.getFeatures().enable(Features.ARCHETYPES); parser.parse(parentDslFile); Workspace workspace = parser.getWorkspace(); @@ -1520,7 +1505,6 @@ void test_archetypesForDefaults() throws Exception { void test_archetypesForExtension() throws Exception { File parentDslFile = new File("src/test/resources/dsl/archetypes-for-extension.dsl"); StructurizrDslParser parser = new StructurizrDslParser(); - parser.getFeatures().enable(Features.ARCHETYPES); parser.parse(parentDslFile); Workspace workspace = parser.getWorkspace(); From 36114825e5ce9f8be92876ca8478e4256baf3eea Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 28 Mar 2025 10:04:59 +0000 Subject: [PATCH 630/717] Updated to reflect release. --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index b7d3dd120..fd5fc022b 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,6 @@ # Changelog -## v4.0.0 (unreleased) +## v4.0.0 (28th March 2025) - structurizr-dsl: Fixes https://github.com/structurizr/java/issues/374 (!identifiers hierarchical isn't propagated when extending a workspace). - structurizr-dsl: Adds the ability to use the `group` keyword inside a component definition, to set the group name of that component. From 471726d6810c9dfdafed41cde7a0fe241691c0e4 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 28 Mar 2025 10:50:36 +0000 Subject: [PATCH 631/717] Bump dependencies. --- build.gradle | 6 ++++++ gradle/wrapper/gradle-wrapper.properties | 2 +- structurizr-autolayout/build.gradle | 1 - structurizr-client/build.gradle | 6 ++---- structurizr-component/build.gradle | 1 - structurizr-core/build.gradle | 7 +++---- structurizr-dsl/build.gradle | 6 ++---- structurizr-export/build.gradle | 1 - structurizr-import/build.gradle | 2 -- structurizr-inspection/build.gradle | 1 - structurizr-neo4j/build.gradle | 3 +-- 11 files changed, 15 insertions(+), 21 deletions(-) diff --git a/build.gradle b/build.gradle index 536ce612a..876b332b5 100644 --- a/build.gradle +++ b/build.gradle @@ -13,6 +13,12 @@ subprojects { proj -> mavenCentral() } + dependencies { + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.11.3' + testImplementation 'org.junit.jupiter:junit-jupiter-params:5.11.3' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.11.3' + } + test { useJUnitPlatform() } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index aa991fcea..3994438e2 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/structurizr-autolayout/build.gradle b/structurizr-autolayout/build.gradle index 631c38fcc..23decccd6 100644 --- a/structurizr-autolayout/build.gradle +++ b/structurizr-autolayout/build.gradle @@ -3,7 +3,6 @@ dependencies { api project(':structurizr-export') testImplementation project(':structurizr-client') - testImplementation 'org.junit.jupiter:junit-jupiter:5.11.3' } diff --git a/structurizr-client/build.gradle b/structurizr-client/build.gradle index a67e1adf7..e078cd9cc 100644 --- a/structurizr-client/build.gradle +++ b/structurizr-client/build.gradle @@ -2,12 +2,10 @@ dependencies { api project(':structurizr-core') - api 'com.fasterxml.jackson.core:jackson-databind:2.18.1' - api 'org.apache.httpcomponents.client5:httpclient5:5.4.1' + api 'com.fasterxml.jackson.core:jackson-databind:2.18.3' + api 'org.apache.httpcomponents.client5:httpclient5:5.4.3' api 'javax.xml.bind:jaxb-api:2.4.0-b180830.0359' - testImplementation 'org.junit.jupiter:junit-jupiter:5.11.3' - } sourceSets { diff --git a/structurizr-component/build.gradle b/structurizr-component/build.gradle index 3c365fbb5..1c296c092 100644 --- a/structurizr-component/build.gradle +++ b/structurizr-component/build.gradle @@ -5,7 +5,6 @@ dependencies { implementation 'com.github.javaparser:javaparser-symbol-solver-core:3.26.2' testImplementation project(':structurizr-annotation') - testImplementation 'org.junit.jupiter:junit-jupiter:5.11.3' } diff --git a/structurizr-core/build.gradle b/structurizr-core/build.gradle index d188aa2be..362d12137 100644 --- a/structurizr-core/build.gradle +++ b/structurizr-core/build.gradle @@ -1,10 +1,9 @@ dependencies { - api 'com.fasterxml.jackson.core:jackson-annotations:2.18.1' + api 'com.fasterxml.jackson.core:jackson-annotations:2.18.3' api 'com.google.code.findbugs:jsr305:3.0.2' - api 'commons-logging:commons-logging:1.3.4' + api 'commons-logging:commons-logging:1.3.5' - testImplementation 'org.junit.jupiter:junit-jupiter:5.11.3' - testImplementation 'org.assertj:assertj-core:3.26.3' + testImplementation 'org.assertj:assertj-core:3.27.3' } \ No newline at end of file diff --git a/structurizr-dsl/build.gradle b/structurizr-dsl/build.gradle index b452a6366..262cdde49 100644 --- a/structurizr-dsl/build.gradle +++ b/structurizr-dsl/build.gradle @@ -5,12 +5,10 @@ dependencies { api project(':structurizr-export') api project(':structurizr-component') - testImplementation 'org.codehaus.groovy:groovy-jsr223:3.0.22' + testImplementation 'org.codehaus.groovy:groovy-jsr223:3.0.24' testImplementation 'org.jetbrains.kotlin:kotlin-scripting-jsr223:1.9.25' - testImplementation 'org.jruby:jruby-core:9.4.8.0' + testImplementation 'org.jruby:jruby-core:9.4.12.0' - testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.11.3' - testImplementation 'org.junit.jupiter:junit-jupiter-params:5.11.3' } description = 'Structurizr DSL' \ No newline at end of file diff --git a/structurizr-export/build.gradle b/structurizr-export/build.gradle index ddef771b2..f908d3fb5 100644 --- a/structurizr-export/build.gradle +++ b/structurizr-export/build.gradle @@ -3,7 +3,6 @@ dependencies { api project(':structurizr-core') testImplementation project(':structurizr-client') - testImplementation 'org.junit.jupiter:junit-jupiter:5.11.3' } diff --git a/structurizr-import/build.gradle b/structurizr-import/build.gradle index eeb4cdc43..7b5c3b32f 100644 --- a/structurizr-import/build.gradle +++ b/structurizr-import/build.gradle @@ -2,8 +2,6 @@ dependencies { api project(':structurizr-core') - testImplementation 'org.junit.jupiter:junit-jupiter:5.11.3' - } description = 'Utilities to import diagrams and documentation into a Structurizr workspace' \ No newline at end of file diff --git a/structurizr-inspection/build.gradle b/structurizr-inspection/build.gradle index bf3f60229..72a7ec6ba 100644 --- a/structurizr-inspection/build.gradle +++ b/structurizr-inspection/build.gradle @@ -3,6 +3,5 @@ dependencies { api project(':structurizr-core') testImplementation project(':structurizr-dsl') - testImplementation 'org.junit.jupiter:junit-jupiter:5.11.3' } \ No newline at end of file diff --git a/structurizr-neo4j/build.gradle b/structurizr-neo4j/build.gradle index aa2bcf209..4703501bf 100644 --- a/structurizr-neo4j/build.gradle +++ b/structurizr-neo4j/build.gradle @@ -1,9 +1,8 @@ dependencies { api project(':structurizr-core') - implementation 'org.neo4j.driver:neo4j-java-driver:5.27.0' + implementation 'org.neo4j.driver:neo4j-java-driver:5.28.4' testImplementation project(':structurizr-client') - testImplementation 'org.junit.jupiter:junit-jupiter:5.11.3' } \ No newline at end of file From 3e3a97a24c8872c09a318c094c48025f39ff34cb Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Sat, 29 Mar 2025 08:17:54 +0000 Subject: [PATCH 632/717] structurizr-dsl: Allows archetypes to be used via workspace extension. --- changelog.md | 4 +++ gradle.properties | 2 +- .../structurizr/dsl/StructurizrDslParser.java | 10 ++++-- .../com/structurizr/dsl/WorkspaceParser.java | 4 +-- .../java/com/structurizr/dsl/DslTests.java | 27 +++++++++++++++ ...hetypes-from-workspace-extension-child.dsl | 9 +++++ ...etypes-from-workspace-extension-parent.dsl | 34 +++++++++++++++++++ 7 files changed, 84 insertions(+), 6 deletions(-) create mode 100644 structurizr-dsl/src/test/resources/dsl/archetypes-from-workspace-extension-child.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/archetypes-from-workspace-extension-parent.dsl diff --git a/changelog.md b/changelog.md index fd5fc022b..3789f2f40 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,9 @@ # Changelog +## v4.0.1 (unreleased) + +- structurizr-dsl: Allows archetypes to be used via workspace extension. + ## v4.0.0 (28th March 2025) - structurizr-dsl: Fixes https://github.com/structurizr/java/issues/374 (!identifiers hierarchical isn't propagated when extending a workspace). diff --git a/gradle.properties b/gradle.properties index 3b854bb7b..1f68eb29c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,4 +5,4 @@ signing.secretKeyRingFile=/some/path ossrhUsername=username ossrhPassword=password -version=4.0.0 \ No newline at end of file +version=4.0.1 \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index c529246ae..856edfd80 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -1,6 +1,5 @@ package com.structurizr.dsl; -import com.structurizr.PerspectivesHolder; import com.structurizr.PropertyHolder; import com.structurizr.Workspace; import com.structurizr.model.*; @@ -48,7 +47,7 @@ public final class StructurizrDslParser extends StructurizrDslTokens { private final Map constantsAndVariables; private final Features features = new Features(); - private final Map> archetypes = Map.of( + private Map> archetypes = Map.of( StructurizrDslTokens.GROUP_TOKEN, new HashMap<>(), StructurizrDslTokens.PERSON_TOKEN, new HashMap<>(), StructurizrDslTokens.SOFTWARE_SYSTEM_TOKEN, new HashMap<>(), @@ -74,6 +73,11 @@ public StructurizrDslParser() { constantsAndVariables = new HashMap<>(); } + void configureFrom(StructurizrDslParser parser) { + setIdentifierScope(parser.getIdentifierScope()); + archetypes = parser.archetypes; + } + /** * Provides a way to change the character encoding used by the DSL parser. * @@ -91,7 +95,7 @@ IdentifierScope getIdentifierScope() { return identifierScope; } - void setIdentifierScope(IdentifierScope identifierScope) { + private void setIdentifierScope(IdentifierScope identifierScope) { if (identifierScope == null) { identifierScope = IdentifierScope.Flat; } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/WorkspaceParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/WorkspaceParser.java index 7f70fde3a..56bc1035e 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/WorkspaceParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/WorkspaceParser.java @@ -49,7 +49,7 @@ Workspace parse(DslParserContext context, Tokens tokens) { structurizrDslParser.setRestricted(context.isRestricted()); structurizrDslParser.parse(context, dsl); workspace = structurizrDslParser.getWorkspace(); - context.getParser().setIdentifierScope(structurizrDslParser.getIdentifierScope()); + context.getParser().configureFrom(structurizrDslParser); } } else { if (context.isRestricted()) { @@ -73,7 +73,7 @@ Workspace parse(DslParserContext context, Tokens tokens) { StructurizrDslParser structurizrDslParser = new StructurizrDslParser(); structurizrDslParser.parse(context, file); workspace = structurizrDslParser.getWorkspace(); - context.getParser().setIdentifierScope(structurizrDslParser.getIdentifierScope()); + context.getParser().configureFrom(structurizrDslParser); } } } diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index be661dd21..7be992a73 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -1501,6 +1501,33 @@ void test_archetypesForDefaults() throws Exception { assertEquals("Default Perspective Description", (r.getPerspectives().stream().filter(p -> p.getName().equals("Default Perspective Name")).findFirst().get().getDescription())); } + @Test + void test_archetypesFromWorkspaceExtension() throws Exception { + File parentDslFile = new File("src/test/resources/dsl/archetypes-from-workspace-extension-child.dsl"); + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(parentDslFile); + Workspace workspace = parser.getWorkspace(); + + SoftwareSystem a = workspace.getModel().getSoftwareSystemWithName("A"); + assertEquals("Default Description", a.getDescription()); + assertTrue(a.hasTag("Default Tag")); + assertTrue(a.hasProperty("Default Property Name", "Default Property Value")); + assertEquals("Default Perspective Description", (a.getPerspectives().stream().filter(p -> p.getName().equals("Default Perspective Name")).findFirst().get().getDescription())); + + SoftwareSystem b = workspace.getModel().getSoftwareSystemWithName("B"); + assertEquals("Default Description", b.getDescription()); + assertTrue(b.hasTag("Default Tag")); + assertTrue(b.hasProperty("Default Property Name", "Default Property Value")); + assertEquals("Default Perspective Description", (b.getPerspectives().stream().filter(p -> p.getName().equals("Default Perspective Name")).findFirst().get().getDescription())); + + Relationship r = a.getEfferentRelationshipWith(b); + assertEquals("Default Description", r.getDescription()); + assertEquals("Default Technology", r.getTechnology()); + assertTrue(r.hasTag("Default Tag")); + assertTrue(r.hasProperty("Default Property Name", "Default Property Value")); + assertEquals("Default Perspective Description", (r.getPerspectives().stream().filter(p -> p.getName().equals("Default Perspective Name")).findFirst().get().getDescription())); + } + @Test void test_archetypesForExtension() throws Exception { File parentDslFile = new File("src/test/resources/dsl/archetypes-for-extension.dsl"); diff --git a/structurizr-dsl/src/test/resources/dsl/archetypes-from-workspace-extension-child.dsl b/structurizr-dsl/src/test/resources/dsl/archetypes-from-workspace-extension-child.dsl new file mode 100644 index 000000000..0b5d03293 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/archetypes-from-workspace-extension-child.dsl @@ -0,0 +1,9 @@ +workspace extends archetypes-from-workspace-extension-parent.dsl { + + model { + a = softwareSystem "A" + b = softwareSystem "B" + a -> b + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/archetypes-from-workspace-extension-parent.dsl b/structurizr-dsl/src/test/resources/dsl/archetypes-from-workspace-extension-parent.dsl new file mode 100644 index 000000000..2ae521527 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/archetypes-from-workspace-extension-parent.dsl @@ -0,0 +1,34 @@ +workspace { + + model { + archetypes { + softwaresystem { + description "Default Description" + tag "Default Tag" + + properties { + "Default Property Name" "Default Property Value" + } + + perspectives { + "Default Perspective Name" "Default Perspective Description" + } + } + + -> { + description "Default Description" + technology "Default Technology" + tag "Default Tag" + + properties { + "Default Property Name" "Default Property Value" + } + + perspectives { + "Default Perspective Name" "Default Perspective Description" + } + } + } + } + +} \ No newline at end of file From 8a9db84ec42ce9b28756ca4afb6ae4b3ee31160f Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Sat, 29 Mar 2025 13:50:15 +0000 Subject: [PATCH 633/717] Closes #392. --- changelog.md | 1 + .../java/com/structurizr/util/ImageUtils.java | 6 +-- .../com/structurizr/util/ImageUtilsTests.java | 3 +- .../com/structurizr/dsl/BrandingParser.java | 7 +--- .../structurizr/dsl/ElementStyleParser.java | 7 +--- .../java/com/structurizr/dsl/IconUtils.java | 38 ------------------- 6 files changed, 10 insertions(+), 52 deletions(-) delete mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/IconUtils.java diff --git a/changelog.md b/changelog.md index 3789f2f40..6045876c8 100644 --- a/changelog.md +++ b/changelog.md @@ -3,6 +3,7 @@ ## v4.0.1 (unreleased) - structurizr-dsl: Allows archetypes to be used via workspace extension. +- structurizr-dsl: Fixes https://github.com/structurizr/java/issues/392 (SVG not supported in base 64 encoding not mentioned in documentation). ## v4.0.0 (28th March 2025) diff --git a/structurizr-core/src/main/java/com/structurizr/util/ImageUtils.java b/structurizr-core/src/main/java/com/structurizr/util/ImageUtils.java index a73e43269..21cc4f25a 100644 --- a/structurizr-core/src/main/java/com/structurizr/util/ImageUtils.java +++ b/structurizr-core/src/main/java/com/structurizr/util/ImageUtils.java @@ -136,18 +136,18 @@ public static void validateImage(String imageDescriptor) { return; } - if (imageDescriptor.toLowerCase().endsWith(".png") || imageDescriptor.toLowerCase().endsWith(".jpg") || imageDescriptor.toLowerCase().endsWith(".jpeg") || imageDescriptor.toLowerCase().endsWith(".gif")) { + if (imageDescriptor.toLowerCase().endsWith(".png") || imageDescriptor.toLowerCase().endsWith(".jpg") || imageDescriptor.toLowerCase().endsWith(".jpeg") || imageDescriptor.toLowerCase().endsWith(".gif") || imageDescriptor.toLowerCase().endsWith(".svg")) { // it's just a filename return; } if (imageDescriptor.startsWith(DATA_URI_PREFIX)) { if (ImageUtils.isSupportedDataUri(imageDescriptor)) { - // it's a PNG/JPG data URI + // it's a PNG/JPG/SVG data URI return; } else { // it's a data URI, but not supported - throw new IllegalArgumentException("Only PNG and JPG data URIs are supported: " + imageDescriptor); + throw new IllegalArgumentException("Only PNG, JPG, and SVG data URIs are supported: " + imageDescriptor); } } diff --git a/structurizr-core/src/test/java/com/structurizr/util/ImageUtilsTests.java b/structurizr-core/src/test/java/com/structurizr/util/ImageUtilsTests.java index fea1a3397..fe5b5883f 100644 --- a/structurizr-core/src/test/java/com/structurizr/util/ImageUtilsTests.java +++ b/structurizr-core/src/test/java/com/structurizr/util/ImageUtilsTests.java @@ -214,6 +214,7 @@ void validateImage() { ImageUtils.validateImage("image.jpg"); ImageUtils.validateImage("image.jpeg"); ImageUtils.validateImage("image.gif"); + ImageUtils.validateImage("image.svg"); ImageUtils.validateImage("data:image/svg+xml;utf8,iVBORw0KGg"); //disallowed @@ -221,7 +222,7 @@ void validateImage() { ImageUtils.validateImage("data:image/other"); fail(); } catch (Exception e) { - assertEquals("Only PNG and JPG data URIs are supported: data:image/other", e.getMessage()); + assertEquals("Only PNG, JPG, and SVG data URIs are supported: data:image/other", e.getMessage()); } } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/BrandingParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/BrandingParser.java index 62c08fd4b..b4f10353d 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/BrandingParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/BrandingParser.java @@ -24,11 +24,8 @@ void parseLogo(BrandingDslContext context, Tokens tokens, boolean restricted) { String path = tokens.get(1); if (path.startsWith("data:image/") || path.startsWith("https://") || path.startsWith("http://")) { - if (IconUtils.isSupported(path)) { - context.getWorkspace().getViews().getConfiguration().getBranding().setLogo(path); - } else { - throw new IllegalArgumentException("Only PNG and JPG URLs and data URIs are supported: " + path); - } + ImageUtils.validateImage(path); + context.getWorkspace().getViews().getConfiguration().getBranding().setLogo(path); } else { if (!restricted) { File file = new File(context.getFile().getParent(), path); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementStyleParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementStyleParser.java index 1769ec5c9..039abab6c 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementStyleParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementStyleParser.java @@ -297,11 +297,8 @@ void parseIcon(ElementStyleDslContext context, Tokens tokens, boolean restricted String path = tokens.get(1); if (path.startsWith("data:image/") || path.startsWith("https://") || path.startsWith("http://")) { - if (IconUtils.isSupported(path)) { - style.setIcon(path); - } else { - throw new IllegalArgumentException("Only PNG and JPG URLs/data URIs are supported: " + path); - } + ImageUtils.validateImage(path); + style.setIcon(path); } else { if (!restricted) { File file = new File(context.getFile().getParent(), path); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/IconUtils.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/IconUtils.java deleted file mode 100644 index b503c0d5f..000000000 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/IconUtils.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.structurizr.dsl; - -import com.structurizr.util.Url; - -class IconUtils { - - public static boolean isSupported(String url) { - url = url.trim(); - - if (Url.isUrl(url) && isSupportedUrl(url)) { - // all good - return true; - } - - if (url.startsWith("data:image")) { - if (isSupportedDataUri(url)) { - // all good - return true; - } else { - // it's a data URI, but not supported - return false; - } - } - - return false; - } - - private static boolean isSupportedDataUri(String uri) { - return uri.startsWith("data:image/png;base64,") || uri.startsWith("data:image/jpeg;base64,"); - } - - private static boolean isSupportedUrl(String url) { - url = url.toLowerCase(); - - return url.endsWith(".png") || url.endsWith(".jpg") || url.endsWith(".jpeg"); - } - -} \ No newline at end of file From 7c448e189f86df57bbdcebc43122e2cf9d9f03ac Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Mon, 7 Apr 2025 10:09:12 +0100 Subject: [PATCH 634/717] structurizr-export: Adds support for rank and node separation to the StructurizrPlantUMLExporter. --- changelog.md | 1 + .../export/plantuml/StructurizrPlantUMLExporter.java | 6 ++++++ .../export/plantuml/structurizr/36141-Components.puml | 2 ++ .../export/plantuml/structurizr/36141-Containers.puml | 2 ++ .../plantuml/structurizr/36141-DevelopmentDeployment.puml | 2 ++ .../export/plantuml/structurizr/36141-LiveDeployment.puml | 2 ++ .../export/plantuml/structurizr/36141-SignIn.puml | 2 ++ .../export/plantuml/structurizr/36141-SystemContext.puml | 2 ++ .../export/plantuml/structurizr/36141-SystemLandscape.puml | 2 ++ .../structurizr/54915-AmazonWebServicesDeployment.puml | 2 ++ .../export/plantuml/structurizr/groups-Components.puml | 2 ++ .../export/plantuml/structurizr/groups-Containers.puml | 2 ++ .../export/plantuml/structurizr/groups-SystemLandscape.puml | 2 ++ 13 files changed, 29 insertions(+) diff --git a/changelog.md b/changelog.md index 6045876c8..ada09c66d 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,7 @@ - structurizr-dsl: Allows archetypes to be used via workspace extension. - structurizr-dsl: Fixes https://github.com/structurizr/java/issues/392 (SVG not supported in base 64 encoding not mentioned in documentation). +- structurizr-export: Adds support for rank and node separation to the StructurizrPlantUMLExporter. ## v4.0.0 (28th March 2025) diff --git a/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java b/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java index 320213b11..2c8d3c775 100644 --- a/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java +++ b/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java @@ -42,6 +42,12 @@ protected void writeHeader(ModelView view, IndentingWriter writer) { writer.writeLine("top to bottom direction"); break; } + + // the default 300px rank separation in the Structurizr UI is equivalent to a default of 60 in PlantUML + writer.writeLine("skinparam ranksep " + view.getAutomaticLayout().getRankSeparation() / (300/60)); + + // the default 300px node separation in the Structurizr UI is equivalent to a default of 30 in PlantUML + writer.writeLine("skinparam nodesep " + view.getAutomaticLayout().getNodeSeparation() / (300/30)); } else { writer.writeLine("top to bottom direction"); } diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-Components.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-Components.puml index abd0c8043..09c551dda 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-Components.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-Components.puml @@ -3,6 +3,8 @@ set separator none title Internet Banking System - API Application - Components top to bottom direction +skinparam ranksep 60 +skinparam nodesep 30 skinparam { arrowFontSize 10 diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-Containers.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-Containers.puml index 40963b9e9..dab94a130 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-Containers.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-Containers.puml @@ -3,6 +3,8 @@ set separator none title Internet Banking System - Containers top to bottom direction +skinparam ranksep 60 +skinparam nodesep 30 skinparam { arrowFontSize 10 diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-DevelopmentDeployment.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-DevelopmentDeployment.puml index 59d7c4b30..b09bc9df7 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-DevelopmentDeployment.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-DevelopmentDeployment.puml @@ -3,6 +3,8 @@ set separator none title Internet Banking System - Deployment - Development top to bottom direction +skinparam ranksep 60 +skinparam nodesep 30 skinparam { arrowFontSize 10 diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-LiveDeployment.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-LiveDeployment.puml index 413f4b3a3..3f6d368ca 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-LiveDeployment.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-LiveDeployment.puml @@ -3,6 +3,8 @@ set separator none title Internet Banking System - Deployment - Live top to bottom direction +skinparam ranksep 60 +skinparam nodesep 30 skinparam { arrowFontSize 10 diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SignIn.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SignIn.puml index fc0b86f0e..f70ca4a45 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SignIn.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SignIn.puml @@ -3,6 +3,8 @@ set separator none title API Application - Dynamic top to bottom direction +skinparam ranksep 60 +skinparam nodesep 30 skinparam { arrowFontSize 10 diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemContext.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemContext.puml index 5b0dc94ec..a3a399968 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemContext.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemContext.puml @@ -3,6 +3,8 @@ set separator none title Internet Banking System - System Context top to bottom direction +skinparam ranksep 60 +skinparam nodesep 30 skinparam { arrowFontSize 10 diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemLandscape.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemLandscape.puml index 0518cb173..89d127718 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemLandscape.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemLandscape.puml @@ -3,6 +3,8 @@ set separator none title System Landscape top to bottom direction +skinparam ranksep 60 +skinparam nodesep 30 skinparam { arrowFontSize 10 diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/54915-AmazonWebServicesDeployment.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/54915-AmazonWebServicesDeployment.puml index a32f9c329..f1753149b 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/54915-AmazonWebServicesDeployment.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/54915-AmazonWebServicesDeployment.puml @@ -3,6 +3,8 @@ set separator none title Spring PetClinic - Deployment - Live left to right direction +skinparam ranksep 60 +skinparam nodesep 30 skinparam { arrowFontSize 10 diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-Components.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-Components.puml index 8528285cb..3a899a68f 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-Components.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-Components.puml @@ -3,6 +3,8 @@ set separator none title D - F - Components top to bottom direction +skinparam ranksep 60 +skinparam nodesep 30 skinparam { arrowFontSize 10 diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-Containers.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-Containers.puml index fe0ad43b9..6b63e7509 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-Containers.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-Containers.puml @@ -3,6 +3,8 @@ set separator none title D - Containers top to bottom direction +skinparam ranksep 60 +skinparam nodesep 30 skinparam { arrowFontSize 10 diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-SystemLandscape.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-SystemLandscape.puml index 7c398d745..09aa97ddd 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-SystemLandscape.puml +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-SystemLandscape.puml @@ -3,6 +3,8 @@ set separator none title System Landscape top to bottom direction +skinparam ranksep 60 +skinparam nodesep 30 skinparam { arrowFontSize 10 From 1bc95cc9d6f160d1d8ba8f6029c5e76c9d3a226e Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 8 Apr 2025 17:03:30 +0100 Subject: [PATCH 635/717] structurizr-dsl: Adds archetype support for custom elements. --- changelog.md | 1 + .../java/com/structurizr/dsl/Archetype.java | 9 ++++++++ .../com/structurizr/dsl/ArchetypeParser.java | 14 +++++++++++++ .../dsl/CustomElementArchetypeDslContext.java | 21 +++++++++++++++++++ .../structurizr/dsl/CustomElementParser.java | 11 +++++----- .../structurizr/dsl/StructurizrDslParser.java | 19 +++++++++++++++-- .../structurizr/dsl/StructurizrDslTokens.java | 1 + .../dsl/CustomElementParserTests.java | 13 ++++++------ .../java/com/structurizr/dsl/DslTests.java | 12 +++++++++++ .../dsl/archetypes-for-custom-elements.dsl | 17 +++++++++++++++ 10 files changed, 105 insertions(+), 13 deletions(-) create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/CustomElementArchetypeDslContext.java create mode 100644 structurizr-dsl/src/test/resources/dsl/archetypes-for-custom-elements.dsl diff --git a/changelog.md b/changelog.md index ada09c66d..2caaad35e 100644 --- a/changelog.md +++ b/changelog.md @@ -3,6 +3,7 @@ ## v4.0.1 (unreleased) - structurizr-dsl: Allows archetypes to be used via workspace extension. +- structurizr-dsl: Adds archetype support for custom elements. - structurizr-dsl: Fixes https://github.com/structurizr/java/issues/392 (SVG not supported in base 64 encoding not mentioned in documentation). - structurizr-export: Adds support for rank and node separation to the StructurizrPlantUMLExporter. diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/Archetype.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/Archetype.java index 2a0b0fbe3..71a92fa0a 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/Archetype.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/Archetype.java @@ -11,6 +11,7 @@ final class Archetype implements PropertyHolder, PerspectivesHolder { private final String name; private final String type; + private String metadata = ""; private String description = ""; private String technology = ""; private final Set tags = new LinkedHashSet<>(); @@ -35,6 +36,14 @@ String getType() { return type; } + String getMetadata() { + return metadata; + } + + void setMetadata(String metadata) { + this.metadata = metadata; + } + String getDescription() { return description; } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ArchetypeParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ArchetypeParser.java index 3c78a1ba6..1b5c00668 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ArchetypeParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ArchetypeParser.java @@ -31,6 +31,20 @@ void parseTags(ArchetypeDslContext context, Tokens tokens) { } } + void parseMetadata(ArchetypeDslContext context, Tokens tokens) { + // metadata + if (tokens.hasMoreThan(VALUE_INDEX)) { + throw new RuntimeException("Too many tokens, expected: metadata "); + } + + if (!tokens.includes(NAME_INDEX)) { + throw new RuntimeException("Expected: metadata "); + } + + String metadata = tokens.get(VALUE_INDEX); + context.getArchetype().setMetadata(metadata); + } + void parseDescription(ArchetypeDslContext context, Tokens tokens) { // description if (tokens.hasMoreThan(VALUE_INDEX)) { diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomElementArchetypeDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomElementArchetypeDslContext.java new file mode 100644 index 000000000..4bad680b8 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomElementArchetypeDslContext.java @@ -0,0 +1,21 @@ +package com.structurizr.dsl; + +final class CustomElementArchetypeDslContext extends ElementArchetypeDslContext { + + CustomElementArchetypeDslContext(Archetype archetype) { + super(archetype); + } + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.METADATA_TOKEN, + StructurizrDslTokens.DESCRIPTION_TOKEN, + StructurizrDslTokens.TAG_TOKEN, + StructurizrDslTokens.TAGS_TOKEN, + StructurizrDslTokens.PROPERTIES_TOKEN, + StructurizrDslTokens.PERSPECTIVES_TOKEN + }; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomElementParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomElementParser.java index 493b573b9..341918a87 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomElementParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomElementParser.java @@ -13,7 +13,7 @@ final class CustomElementParser extends AbstractParser { private final static int DESCRIPTION_INDEX = 3; private final static int TAGS_INDEX = 4; - CustomElement parse(ModelDslContext context, Tokens tokens) { + CustomElement parse(ModelDslContext context, Tokens tokens, Archetype archetype) { // element [metadata] [description] [tags] if (tokens.hasMoreThan(TAGS_INDEX)) { @@ -26,22 +26,23 @@ CustomElement parse(ModelDslContext context, Tokens tokens) { String name = tokens.get(NAME_INDEX); - String metadata = ""; + String metadata = archetype.getMetadata(); if (tokens.includes(METADATA_INDEX)) { metadata = tokens.get(METADATA_INDEX); } - String description = ""; + String description = archetype.getDescription(); if (tokens.includes(DESCRIPTION_INDEX)) { description = tokens.get(DESCRIPTION_INDEX); } CustomElement customElement = context.getWorkspace().getModel().addCustomElement(name, metadata, description); + String[] tags = archetype.getTags().toArray(new String[0]); if (tokens.includes(TAGS_INDEX)) { - String tags = tokens.get(TAGS_INDEX); - customElement.addTags(tags.split(",")); + tags = tokens.get(TAGS_INDEX).split(","); } + customElement.addTags(tags); if (context.hasGroup()) { customElement.setGroup(context.getGroup().getName()); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index 856edfd80..89e9f7adf 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -49,6 +49,7 @@ public final class StructurizrDslParser extends StructurizrDslTokens { private Map> archetypes = Map.of( StructurizrDslTokens.GROUP_TOKEN, new HashMap<>(), + StructurizrDslTokens.CUSTOM_ELEMENT_TOKEN, new HashMap<>(), StructurizrDslTokens.PERSON_TOKEN, new HashMap<>(), StructurizrDslTokens.SOFTWARE_SYSTEM_TOKEN, new HashMap<>(), StructurizrDslTokens.CONTAINER_TOKEN, new HashMap<>(), @@ -421,8 +422,9 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn startContext(new RelationshipsDslContext(getContext(), relationships)); } - } else if (CUSTOM_ELEMENT_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ModelDslContext.class))) { - CustomElement customElement = new CustomElementParser().parse(getContext(ModelDslContext.class), tokens.withoutContextStartToken()); + } else if (isElementKeywordOrArchetype(firstToken, CUSTOM_ELEMENT_TOKEN) && (inContext(ModelDslContext.class))) { + Archetype archetype = getArchetype(CUSTOM_ELEMENT_TOKEN, firstToken); + CustomElement customElement = new CustomElementParser().parse(getContext(ModelDslContext.class), tokens.withoutContextStartToken(), archetype); if (shouldStartContext(tokens)) { startContext(new CustomElementDslContext(customElement)); @@ -673,6 +675,15 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn extendArchetype(archetype, firstToken); addArchetype(archetype); + } else if (isElementKeywordOrArchetype(firstToken, CUSTOM_ELEMENT_TOKEN) && inContext(ArchetypesDslContext.class)) { + Archetype archetype = new Archetype(identifier, CUSTOM_ELEMENT_TOKEN); + extendArchetype(archetype, firstToken); + addArchetype(archetype); + + if (shouldStartContext(tokens)) { + startContext(new CustomElementArchetypeDslContext(archetype)); + } + } else if (isElementKeywordOrArchetype(firstToken, PERSON_TOKEN) && inContext(ArchetypesDslContext.class)) { Archetype archetype = new Archetype(identifier, PERSON_TOKEN); extendArchetype(archetype, firstToken); @@ -737,6 +748,9 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn startContext(new RelationshipArchetypeDslContext(archetype)); } + } else if (METADATA_TOKEN.equalsIgnoreCase(firstToken) && inContext(CustomElementArchetypeDslContext.class)) { + new ArchetypeParser().parseMetadata(getContext(ArchetypeDslContext.class), tokens); + } else if (DESCRIPTION_TOKEN.equalsIgnoreCase(firstToken) && inContext(ArchetypeDslContext.class)) { new ArchetypeParser().parseDescription(getContext(ArchetypeDslContext.class), tokens); @@ -1384,6 +1398,7 @@ private void extendArchetype(Archetype archetype, String archetypeName) { archetypeName = archetypeName.toLowerCase(); Archetype parentArchetype = getArchetype(archetype.getType(), archetypeName); + archetype.setMetadata(parentArchetype.getMetadata()); archetype.setDescription(parentArchetype.getDescription()); archetype.setTechnology(parentArchetype.getTechnology()); archetype.addTags(parentArchetype.getTags().toArray(new String[0])); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java index fef341aad..ae2d1ecd8 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java @@ -15,6 +15,7 @@ class StructurizrDslTokens { static final String COMPONENT_TOKEN = "component"; static final String GROUP_TOKEN = "group"; static final String NAME_TOKEN = "name"; + static final String METADATA_TOKEN = "metadata"; static final String DESCRIPTION_TOKEN = "description"; static final String TECHNOLOGY_TOKEN = "technology"; static final String INSTANCES_TOKEN = "instances"; diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/CustomElementParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/CustomElementParserTests.java index 37c108b19..c216fbba8 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/CustomElementParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/CustomElementParserTests.java @@ -8,11 +8,12 @@ class CustomElementParserTests extends AbstractTests { private CustomElementParser parser = new CustomElementParser(); + private Archetype archetype = new Archetype("name", "type"); @Test void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { try { - parser.parse(context(), tokens("element", "name", "metadata", "description", "tags", "extra")); + parser.parse(context(), tokens("element", "name", "metadata", "description", "tags", "extra"), archetype); fail(); } catch (Exception e) { assertEquals("Too many tokens, expected: element [metadata] [description] [tags]", e.getMessage()); @@ -22,7 +23,7 @@ void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { @Test void test_parse_ThrowsAnException_WhenTheNameIsNotSpecified() { try { - parser.parse(context(), tokens("element")); + parser.parse(context(), tokens("element"), archetype); fail(); } catch (Exception e) { assertEquals("Expected: element [metadata] [description] [tags]", e.getMessage()); @@ -31,7 +32,7 @@ void test_parse_ThrowsAnException_WhenTheNameIsNotSpecified() { @Test void test_parse_CreatesACustomElement() { - parser.parse(context(), tokens("element", "Name")); + parser.parse(context(), tokens("element", "Name"), archetype); assertEquals(1, model.getElements().size()); CustomElement element = model.getCustomElementWithName("Name"); @@ -42,7 +43,7 @@ void test_parse_CreatesACustomElement() { @Test void test_parse_CreatesACustomElementWithMetadata() { - parser.parse(context(), tokens("element", "Name", "Box")); + parser.parse(context(), tokens("element", "Name", "Box"), archetype); assertEquals(1, model.getElements().size()); CustomElement element = model.getCustomElementWithName("Name"); @@ -54,7 +55,7 @@ void test_parse_CreatesACustomElementWithMetadata() { @Test void test_parse_CreatesACustomElementWithMetadataAndDescription() { - parser.parse(context(), tokens("element", "Name", "Box", "Description")); + parser.parse(context(), tokens("element", "Name", "Box", "Description"), archetype); assertEquals(1, model.getElements().size()); CustomElement element = model.getCustomElementWithName("Name"); @@ -66,7 +67,7 @@ void test_parse_CreatesACustomElementWithMetadataAndDescription() { @Test void test_parse_CreatesACustomElementWithMetadataAndDescriptionAndTags() { - parser.parse(context(), tokens("element", "Name", "Box", "Description", "Tag 1, Tag 2")); + parser.parse(context(), tokens("element", "Name", "Box", "Description", "Tag 1, Tag 2"), archetype); assertEquals(1, model.getElements().size()); CustomElement element = model.getCustomElementWithName("Name"); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index 7be992a73..cb957a545 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -1474,6 +1474,18 @@ void test_archetypes() throws Exception { assertEquals("HTTPS", relationship.getTechnology()); } + @Test + void test_archetypesForCustomElements() throws Exception { + File parentDslFile = new File("src/test/resources/dsl/archetypes-for-custom-elements.dsl"); + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(parentDslFile); + Workspace workspace = parser.getWorkspace(); + + CustomElement b = workspace.getModel().getCustomElementWithName("B"); + assertEquals("Hardware System", b.getMetadata()); + assertTrue(b.getTagsAsSet().contains("Hardware System")); + } + @Test void test_archetypesForDefaults() throws Exception { File parentDslFile = new File("src/test/resources/dsl/archetypes-for-defaults.dsl"); diff --git a/structurizr-dsl/src/test/resources/dsl/archetypes-for-custom-elements.dsl b/structurizr-dsl/src/test/resources/dsl/archetypes-for-custom-elements.dsl new file mode 100644 index 000000000..c3d2a3cf7 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/archetypes-for-custom-elements.dsl @@ -0,0 +1,17 @@ +workspace { + + model { + archetypes { + hardwareSystem = element { + metadata "Hardware System" + tag "Hardware System" + } + } + + a = softwareSystem "A" + b = hardwareSystem "B" + + a -> b "Gets data from" + } + +} \ No newline at end of file From c44696f143cbcccff0b3fd102a8bf89177ad832e Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Wed, 9 Apr 2025 12:47:42 +0100 Subject: [PATCH 636/717] structurizr-dsl: Adds support for setting the symbols surrounding element/relationship metadata used when rendering diagrams. --- changelog.md | 1 + .../structurizr/dsl/StructurizrDslParser.java | 3 ++ .../structurizr/dsl/StructurizrDslTokens.java | 1 + .../dsl/TerminologyDslContext.java | 3 +- .../structurizr/dsl/TerminologyParser.java | 31 ++++++++++++++ .../dsl/TerminologyParserTests.java | 42 +++++++++++++++++++ .../src/test/resources/dsl/test.dsl | 1 + 7 files changed, 81 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 2caaad35e..029b93e2c 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,7 @@ - structurizr-dsl: Allows archetypes to be used via workspace extension. - structurizr-dsl: Adds archetype support for custom elements. - structurizr-dsl: Fixes https://github.com/structurizr/java/issues/392 (SVG not supported in base 64 encoding not mentioned in documentation). +- structurizr-dsl: Adds support for setting the symbols surrounding element/relationship metadata used when rendering diagrams. - structurizr-export: Adds support for rank and node separation to the StructurizrPlantUMLExporter. ## v4.0.0 (28th March 2025) diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index 89e9f7adf..563d40dcd 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -1082,6 +1082,9 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn } else if (TERMINOLOGY_RELATIONSHIP_TOKEN.equalsIgnoreCase(firstToken) && inContext(TerminologyDslContext.class)) { new TerminologyParser().parseRelationship(getContext(), tokens); + } else if (METADATA_SYMBOLS_TOKEN.equalsIgnoreCase(firstToken) && inContext(TerminologyDslContext.class)) { + new TerminologyParser().parseMetadataSymbols(getContext(), tokens); + } else if (CONFIGURATION_TOKEN.equalsIgnoreCase(firstToken) && inContext(WorkspaceDslContext.class)) { startContext(new ConfigurationDslContext()); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java index ae2d1ecd8..9b295eff0 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java @@ -95,6 +95,7 @@ class StructurizrDslTokens { static final String VISIBILITY_TOKEN = "visibility"; static final String TERMINOLOGY_TOKEN = "terminology"; static final String TERMINOLOGY_RELATIONSHIP_TOKEN = "relationship"; + static final String METADATA_SYMBOLS_TOKEN = "metadata"; static final String USERS_TOKEN = "users"; static final String THIS_TOKEN = "this"; diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/TerminologyDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/TerminologyDslContext.java index be42d7490..32bc07e81 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/TerminologyDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/TerminologyDslContext.java @@ -11,7 +11,8 @@ protected String[] getPermittedTokens() { StructurizrDslTokens.COMPONENT_TOKEN, StructurizrDslTokens.DEPLOYMENT_NODE_TOKEN, StructurizrDslTokens.INFRASTRUCTURE_NODE_TOKEN, - StructurizrDslTokens.TERMINOLOGY_RELATIONSHIP_TOKEN + StructurizrDslTokens.TERMINOLOGY_RELATIONSHIP_TOKEN, + StructurizrDslTokens.METADATA_SYMBOLS_TOKEN }; } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/TerminologyParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/TerminologyParser.java index d95e2409c..1fdaab43b 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/TerminologyParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/TerminologyParser.java @@ -1,8 +1,13 @@ package com.structurizr.dsl; +import com.structurizr.view.MetadataSymbols; + +import java.util.*; + final class TerminologyParser extends AbstractParser { private final static int TERM_INDEX = 1; + private final static int SYMBOL_TYPE_INDEX = 1; void parsePerson(DslContext context, Tokens tokens) { // person @@ -67,4 +72,30 @@ void parseRelationship(DslContext context, Tokens tokens) { context.getWorkspace().getViews().getConfiguration().getTerminology().setRelationship(tokens.get(TERM_INDEX)); } + void parseMetadataSymbols(DslContext context, Tokens tokens) { + Map symbols = new LinkedHashMap<>(); + symbols.put("square", MetadataSymbols.SquareBrackets); + symbols.put("round", MetadataSymbols.RoundBrackets); + symbols.put("curly", MetadataSymbols.CurlyBrackets); + symbols.put("angle", MetadataSymbols.AngleBrackets); + symbols.put("double-angle", MetadataSymbols.DoubleAngleBrackets); + symbols.put("none", MetadataSymbols.None); + + String symbolsAsString = String.join("|", symbols.keySet()); + + // metadata + if (!tokens.includes(SYMBOL_TYPE_INDEX)) { + throw new RuntimeException("Expected: metadata <" + symbolsAsString + ">"); + } + + String symbolAsString = tokens.get(SYMBOL_TYPE_INDEX).toLowerCase(); + MetadataSymbols symbol = symbols.get(symbolAsString); + if (symbol != null) { + context.getWorkspace().getViews().getConfiguration().setMetadataSymbols(symbol); + } else { + throw new RuntimeException("The symbol type \"" + symbolAsString + "\" is not valid"); + } + + } + } \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/TerminologyParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/TerminologyParserTests.java index 5d4592627..d0438c6b6 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/TerminologyParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/TerminologyParserTests.java @@ -1,5 +1,6 @@ package com.structurizr.dsl; +import com.structurizr.view.MetadataSymbols; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -128,4 +129,45 @@ void test_parseRelationship_SetsTheTerm_WhenOneIsSpecified() { assertEquals("TERM", workspace.getViews().getConfiguration().getTerminology().getRelationship()); } + @Test + void test_parseMetadataSymbols_ThrowsAnException_WhenNoSymbolTypeIsSpecified() { + try { + parser.parseMetadataSymbols(context(), tokens("metadata")); + fail(); + } catch (Exception e) { + assertEquals("Expected: metadata ", e.getMessage()); + } + } + + @Test + void test_parseMetadataSymbols_ThrowsAnException_WhenAnInvalidSymbolTypeIsSpecified() { + try { + parser.parseMetadataSymbols(context(), tokens("metadata", "invalid")); + fail(); + } catch (Exception e) { + assertEquals("The symbol type \"invalid\" is not valid", e.getMessage()); + } + } + + @Test + void test_parseMetadataSymbols_SetsTheMetadataSymbols_WhenSpecified() { + parser.parseMetadataSymbols(context(), tokens("metadata", "square")); + assertEquals(MetadataSymbols.SquareBrackets, workspace.getViews().getConfiguration().getMetadataSymbols()); + + parser.parseMetadataSymbols(context(), tokens("metadata", "round")); + assertEquals(MetadataSymbols.RoundBrackets, workspace.getViews().getConfiguration().getMetadataSymbols()); + + parser.parseMetadataSymbols(context(), tokens("metadata", "curly")); + assertEquals(MetadataSymbols.CurlyBrackets, workspace.getViews().getConfiguration().getMetadataSymbols()); + + parser.parseMetadataSymbols(context(), tokens("metadata", "angle")); + assertEquals(MetadataSymbols.AngleBrackets, workspace.getViews().getConfiguration().getMetadataSymbols()); + + parser.parseMetadataSymbols(context(), tokens("metadata", "double-angle")); + assertEquals(MetadataSymbols.DoubleAngleBrackets, workspace.getViews().getConfiguration().getMetadataSymbols()); + + parser.parseMetadataSymbols(context(), tokens("metadata", "none")); + assertEquals(MetadataSymbols.None, workspace.getViews().getConfiguration().getMetadataSymbols()); + } + } \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/test.dsl b/structurizr-dsl/src/test/resources/dsl/test.dsl index 045aa311d..c6150affd 100644 --- a/structurizr-dsl/src/test/resources/dsl/test.dsl +++ b/structurizr-dsl/src/test/resources/dsl/test.dsl @@ -359,6 +359,7 @@ workspace "Name" "Description" { deploymentNode "Deployment Node" infrastructureNode "Infrastructure Node" relationship "Relationship" + metadata angle } properties { From 518da43784c5227adbee5baa7443a3be02759c94 Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Thu, 10 Apr 2025 08:22:51 +0100 Subject: [PATCH 637/717] Fixes #399. --- changelog.md | 1 + .../com/structurizr/dsl/ComponentParser.java | 10 ++- .../com/structurizr/dsl/ContainerParser.java | 10 ++- .../structurizr/dsl/CustomElementParser.java | 10 ++- .../structurizr/dsl/DeploymentNodeParser.java | 85 +++++++------------ .../dsl/ExplicitRelationshipParser.java | 9 +- .../dsl/ImplicitRelationshipParser.java | 9 +- .../dsl/InfrastructureNodeParser.java | 10 ++- .../com/structurizr/dsl/PersonParser.java | 10 ++- .../structurizr/dsl/SoftwareSystemParser.java | 10 ++- .../structurizr/dsl/ComponentParserTests.java | 20 +++++ .../structurizr/dsl/ContainerParserTests.java | 19 +++++ .../dsl/CustomElementParserTests.java | 16 ++++ .../dsl/DeploymentNodeParserTests.java | 43 ++++++++++ .../dsl/ExplicitRelationshipParserTests.java | 29 +++++++ .../dsl/ImplicitRelationshipParserTests.java | 28 ++++++ .../dsl/InfrastructureNodeParserTests.java | 22 +++++ .../structurizr/dsl/PersonParserTests.java | 15 ++++ .../dsl/SoftwareSystemParserTests.java | 15 ++++ 19 files changed, 287 insertions(+), 84 deletions(-) diff --git a/changelog.md b/changelog.md index 029b93e2c..f90c8ca3d 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,7 @@ - structurizr-dsl: Allows archetypes to be used via workspace extension. - structurizr-dsl: Adds archetype support for custom elements. +- structurizr-dsl: Fixes https://github.com/structurizr/java/issues/399 (Archetype tags sometimes missing). - structurizr-dsl: Fixes https://github.com/structurizr/java/issues/392 (SVG not supported in base 64 encoding not mentioned in documentation). - structurizr-dsl: Adds support for setting the symbols surrounding element/relationship metadata used when rendering diagrams. - structurizr-export: Adds support for rank and node separation to the StructurizrPlantUMLExporter. diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentParser.java index 0b587d2e8..cb3f88098 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentParser.java @@ -3,6 +3,10 @@ import com.structurizr.model.Component; import com.structurizr.model.Container; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + final class ComponentParser extends AbstractParser { private static final String GRAMMAR = "component [description] [technology] [tags]"; @@ -47,11 +51,11 @@ Component parse(ContainerDslContext context, Tokens tokens, Archetype archetype) } component.setTechnology(technology); - String[] tags = archetype.getTags().toArray(new String[0]); + List tags = new ArrayList<>(archetype.getTags()); if (tokens.includes(TAGS_INDEX)) { - tags = tokens.get(TAGS_INDEX).split(","); + tags.addAll(Arrays.asList(tokens.get(TAGS_INDEX).split(","))); } - component.addTags(tags); + component.addTags(tags.toArray(new String[0])); component.addProperties(archetype.getProperties()); component.addPerspectives(archetype.getPerspectives()); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerParser.java index c8abc1ec8..33e81e553 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerParser.java @@ -3,6 +3,10 @@ import com.structurizr.model.Container; import com.structurizr.model.SoftwareSystem; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + final class ContainerParser extends AbstractParser { private static final String GRAMMAR = "container [description] [technology] [tags]"; @@ -47,11 +51,11 @@ Container parse(SoftwareSystemDslContext context, Tokens tokens, Archetype arche } container.setTechnology(technology); - String[] tags = archetype.getTags().toArray(new String[0]); + List tags = new ArrayList<>(archetype.getTags()); if (tokens.includes(TAGS_INDEX)) { - tags = tokens.get(TAGS_INDEX).split(","); + tags.addAll(Arrays.asList(tokens.get(TAGS_INDEX).split(","))); } - container.addTags(tags); + container.addTags(tags.toArray(new String[0])); container.addProperties(archetype.getProperties()); container.addPerspectives(archetype.getPerspectives()); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomElementParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomElementParser.java index 341918a87..06d9e6e65 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomElementParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomElementParser.java @@ -4,6 +4,10 @@ import com.structurizr.model.Location; import com.structurizr.model.Person; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + final class CustomElementParser extends AbstractParser { private static final String GRAMMAR = "element [metadata] [description] [tags]"; @@ -38,11 +42,11 @@ CustomElement parse(ModelDslContext context, Tokens tokens, Archetype archetype) CustomElement customElement = context.getWorkspace().getModel().addCustomElement(name, metadata, description); - String[] tags = archetype.getTags().toArray(new String[0]); + List tags = new ArrayList<>(archetype.getTags()); if (tokens.includes(TAGS_INDEX)) { - tags = tokens.get(TAGS_INDEX).split(","); + tags.addAll(Arrays.asList(tokens.get(TAGS_INDEX).split(","))); } - customElement.addTags(tags); + customElement.addTags(tags.toArray(new String[0])); if (context.hasGroup()) { customElement.setGroup(context.getGroup().getName()); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeParser.java index b2a5a2c3d..93866302b 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeParser.java @@ -1,6 +1,11 @@ package com.structurizr.dsl; import com.structurizr.model.DeploymentNode; +import com.structurizr.model.Model; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; final class DeploymentNodeParser extends AbstractParser { @@ -13,55 +18,14 @@ final class DeploymentNodeParser extends AbstractParser { private static final int INSTANCES_INDEX = 5; DeploymentNode parse(DeploymentEnvironmentDslContext context, Tokens tokens, Archetype archetype) { - // deploymentNode [description] [technology] [tags] [instances] - - if (tokens.hasMoreThan(INSTANCES_INDEX)) { - throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); - } - - if (!tokens.includes(NAME_INDEX)) { - throw new RuntimeException("Expected: " + GRAMMAR); - } - - DeploymentNode deploymentNode = null; - String name = tokens.get(NAME_INDEX); - - String description = archetype.getDescription(); - if (tokens.includes(DESCRIPTION_INDEX)) { - description = tokens.get(DESCRIPTION_INDEX); - } - - String technology = archetype.getTechnology(); - if (tokens.includes(TECHNOLOGY_INDEX)) { - technology = tokens.get(TECHNOLOGY_INDEX); - } - - deploymentNode = context.getWorkspace().getModel().addDeploymentNode(context.getEnvironment(), name, description, technology); - - String[] tags = archetype.getTags().toArray(new String[0]); - if (tokens.includes(TAGS_INDEX)) { - tags = tokens.get(TAGS_INDEX).split(","); - } - deploymentNode.addTags(tags); - - deploymentNode.addProperties(archetype.getProperties()); - deploymentNode.addPerspectives(archetype.getPerspectives()); - - String instances = "1"; - if (tokens.includes(INSTANCES_INDEX)) { - instances = tokens.get(INSTANCES_INDEX); - deploymentNode.setInstances(instances); - } - - if (context.hasGroup()) { - deploymentNode.setGroup(context.getGroup().getName()); - context.getGroup().addElement(deploymentNode); - } - - return deploymentNode; + return parse(context, null, tokens, archetype); } DeploymentNode parse(DeploymentNodeDslContext context, Tokens tokens, Archetype archetype) { + return parse(null, context, tokens, archetype); + } + + DeploymentNode parse(DeploymentEnvironmentDslContext deploymentEnvironmentDslContext, DeploymentNodeDslContext deploymentNodeDslContext, Tokens tokens, Archetype archetype) { // deploymentNode [description] [technology] [tags] [instances] if (tokens.hasMoreThan(INSTANCES_INDEX)) { @@ -85,14 +49,28 @@ DeploymentNode parse(DeploymentNodeDslContext context, Tokens tokens, Archetype technology = tokens.get(TECHNOLOGY_INDEX); } - DeploymentNode parent = context.getDeploymentNode(); - deploymentNode = parent.addDeploymentNode(name, description, technology); + if (deploymentEnvironmentDslContext != null) { + // add a root deployment node + deploymentNode = deploymentEnvironmentDslContext.getWorkspace().getModel().addDeploymentNode(deploymentEnvironmentDslContext.getEnvironment(), name, description, technology); - String[] tags = archetype.getTags().toArray(new String[0]); + if (deploymentEnvironmentDslContext.hasGroup()) { + deploymentNode.setGroup(deploymentEnvironmentDslContext.getGroup().getName()); + deploymentEnvironmentDslContext.getGroup().addElement(deploymentNode); + } + } else { + deploymentNode = deploymentNodeDslContext.getDeploymentNode().addDeploymentNode(name, description, technology); + + if (deploymentNodeDslContext.hasGroup()) { + deploymentNode.setGroup(deploymentNodeDslContext.getGroup().getName()); + deploymentNodeDslContext.getGroup().addElement(deploymentNode); + } + } + + List tags = new ArrayList<>(archetype.getTags()); if (tokens.includes(TAGS_INDEX)) { - tags = tokens.get(TAGS_INDEX).split(","); + tags.addAll(Arrays.asList(tokens.get(TAGS_INDEX).split(","))); } - deploymentNode.addTags(tags); + deploymentNode.addTags(tags.toArray(new String[0])); deploymentNode.addProperties(archetype.getProperties()); deploymentNode.addPerspectives(archetype.getPerspectives()); @@ -103,11 +81,6 @@ DeploymentNode parse(DeploymentNodeDslContext context, Tokens tokens, Archetype deploymentNode.setInstances(instances); } - if (context.hasGroup()) { - deploymentNode.setGroup(context.getGroup().getName()); - context.getGroup().addElement(deploymentNode); - } - return deploymentNode; } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ExplicitRelationshipParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ExplicitRelationshipParser.java index de846e382..38e225b22 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ExplicitRelationshipParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ExplicitRelationshipParser.java @@ -4,8 +4,7 @@ import com.structurizr.model.Relationship; import javax.lang.model.util.Elements; -import java.util.LinkedHashSet; -import java.util.Set; +import java.util.*; final class ExplicitRelationshipParser extends AbstractRelationshipParser { @@ -50,12 +49,12 @@ Relationship parse(DslContext context, Tokens tokens, Archetype archetype) { technology = tokens.get(TECHNOLOGY_INDEX); } - String[] tags = archetype.getTags().toArray(new String[0]); + List tags = new ArrayList<>(archetype.getTags()); if (tokens.includes(TAGS_INDEX)) { - tags = tokens.get(TAGS_INDEX).split(","); + tags.addAll(Arrays.asList(tokens.get(TAGS_INDEX).split(","))); } - Relationship relationship = createRelationship(sourceElement, description, technology, tags, destinationElement); + Relationship relationship = createRelationship(sourceElement, description, technology, tags.toArray(new String[0]), destinationElement); relationship.addProperties(archetype.getProperties()); relationship.addPerspectives(archetype.getPerspectives()); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImplicitRelationshipParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImplicitRelationshipParser.java index e3fb1df93..7c10b7015 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImplicitRelationshipParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImplicitRelationshipParser.java @@ -3,8 +3,7 @@ import com.structurizr.model.Element; import com.structurizr.model.Relationship; -import java.util.LinkedHashSet; -import java.util.Set; +import java.util.*; final class ImplicitRelationshipParser extends AbstractRelationshipParser { @@ -45,12 +44,12 @@ Relationship parse(ElementDslContext context, Tokens tokens, Archetype archetype technology = tokens.get(TECHNOLOGY_INDEX); } - String[] tags = archetype.getTags().toArray(new String[0]); + List tags = new ArrayList<>(archetype.getTags()); if (tokens.includes(TAGS_INDEX)) { - tags = tokens.get(TAGS_INDEX).split(","); + tags.addAll(Arrays.asList(tokens.get(TAGS_INDEX).split(","))); } - Relationship relationship = createRelationship(sourceElement, description, technology, tags, destinationElement); + Relationship relationship = createRelationship(sourceElement, description, technology, tags.toArray(new String[0]), destinationElement); relationship.addProperties(archetype.getProperties()); relationship.addPerspectives(archetype.getPerspectives()); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeParser.java index 964f989e9..8d8165a65 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/InfrastructureNodeParser.java @@ -3,6 +3,10 @@ import com.structurizr.model.DeploymentNode; import com.structurizr.model.InfrastructureNode; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + final class InfrastructureNodeParser extends AbstractParser { private static final String GRAMMAR = "infrastructureNode [description] [technology] [tags]"; @@ -39,11 +43,11 @@ InfrastructureNode parse(DeploymentNodeDslContext context, Tokens tokens, Archet infrastructureNode = deploymentNode.addInfrastructureNode(name, description, technology); - String[] tags = archetype.getTags().toArray(new String[0]); + List tags = new ArrayList<>(archetype.getTags()); if (tokens.includes(TAGS_INDEX)) { - tags = tokens.get(TAGS_INDEX).split(","); + tags.addAll(Arrays.asList(tokens.get(TAGS_INDEX).split(","))); } - infrastructureNode.addTags(tags); + infrastructureNode.addTags(tags.toArray(new String[0])); infrastructureNode.addProperties(archetype.getProperties()); infrastructureNode.addPerspectives(archetype.getPerspectives()); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonParser.java index 8525e5d36..992fbf6ca 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/PersonParser.java @@ -2,6 +2,10 @@ import com.structurizr.model.Person; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + final class PersonParser extends AbstractParser { private static final String GRAMMAR = "person [description] [tags]"; @@ -38,11 +42,11 @@ Person parse(ModelDslContext context, Tokens tokens, Archetype archetype) { } person.setDescription(description); - String[] tags = archetype.getTags().toArray(new String[0]); + List tags = new ArrayList<>(archetype.getTags()); if (tokens.includes(TAGS_INDEX)) { - tags = tokens.get(TAGS_INDEX).split(","); + tags.addAll(Arrays.asList(tokens.get(TAGS_INDEX).split(","))); } - person.addTags(tags); + person.addTags(tags.toArray(new String[0])); person.addProperties(archetype.getProperties()); person.addPerspectives(archetype.getPerspectives()); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemParser.java index fd1ef8949..ffae2403e 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemParser.java @@ -2,6 +2,10 @@ import com.structurizr.model.SoftwareSystem; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + final class SoftwareSystemParser extends AbstractParser { private static final String GRAMMAR = "softwareSystem [description] [tags]"; @@ -38,11 +42,11 @@ SoftwareSystem parse(ModelDslContext context, Tokens tokens, Archetype archetype } softwareSystem.setDescription(description); - String[] tags = archetype.getTags().toArray(new String[0]); + List tags = new ArrayList<>(archetype.getTags()); if (tokens.includes(TAGS_INDEX)) { - tags = tokens.get(TAGS_INDEX).split(","); + tags.addAll(Arrays.asList(tokens.get(TAGS_INDEX).split(","))); } - softwareSystem.addTags(tags); + softwareSystem.addTags(tags.toArray(new String[0])); softwareSystem.addProperties(archetype.getProperties()); softwareSystem.addPerspectives(archetype.getPerspectives()); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentParserTests.java index 77ddc23fa..babac9aab 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ComponentParserTests.java @@ -92,6 +92,26 @@ void test_parse_CreatesAComponentWithADescriptionAndTechnologyAndTags() { assertEquals("Element,Component,Tag 1,Tag 2", component.getTags()); } + @Test + void test_parse_CreatesAComponentWithADescriptionAndTechnologyAndTagsBasedUponAnArchetype() { + archetype = new Archetype("name", "type"); + archetype.setDescription("Default Description"); + archetype.setTechnology("Default Technology"); + archetype.addTags("Default Tag"); + + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + Container container = softwareSystem.addContainer("Container", "Description", "Technology"); + ContainerDslContext context = new ContainerDslContext(container); + parser.parse(context, tokens("component", "Name", "Description", "Technology", "Tag 1, Tag 2"), archetype); + + assertEquals(3, model.getElements().size()); + Component component = container.getComponentWithName("Name"); + assertNotNull(component); + assertEquals("Description", component.getDescription()); // overridden from archetype + assertEquals("Technology", component.getTechnology()); // overridden from archetype + assertEquals("Element,Component,Default Tag,Tag 1,Tag 2", component.getTags()); + } + @Test void test_parseTechnology_ThrowsAnException_WhenThereAreTooManyTokens() { try { diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ContainerParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ContainerParserTests.java index 758f7503c..3215df02c 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ContainerParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ContainerParserTests.java @@ -87,6 +87,25 @@ void test_parse_CreatesAContainerWithADescriptionAndTechnologyAndTags() { assertEquals("Element,Container,Tag 1,Tag 2", container.getTags()); } + @Test + void test_parse_CreatesAContainerWithADescriptionAndTechnologyAndTagsBasedUponAnArchetype() { + archetype = new Archetype("name", "type"); + archetype.setDescription("Default Description"); + archetype.setTechnology("Default Technology"); + archetype.addTags("Default Tag"); + + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + SoftwareSystemDslContext context = new SoftwareSystemDslContext(softwareSystem); + parser.parse(context, tokens("container", "Name", "Description", "Technology", "Tag 1, Tag 2"), archetype); + + assertEquals(2, model.getElements().size()); + Container container = softwareSystem.getContainerWithName("Name"); + assertNotNull(container); + assertEquals("Description", container.getDescription()); // overridden from archetype + assertEquals("Technology", container.getTechnology()); // overridden from archetype + assertEquals("Element,Container,Default Tag,Tag 1,Tag 2", container.getTags()); + } + @Test void test_parseTechnology_ThrowsAnException_WhenThereAreTooManyTokens() { try { diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/CustomElementParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/CustomElementParserTests.java index c216fbba8..27d48b963 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/CustomElementParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/CustomElementParserTests.java @@ -77,4 +77,20 @@ void test_parse_CreatesACustomElementWithMetadataAndDescriptionAndTags() { assertEquals("Element,Tag 1,Tag 2", element.getTags()); } + @Test + void test_parse_CreatesACustomElementWithMetadataAndDescriptionAndTagsBasedUponAnArchetype() { + archetype = new Archetype("name", "type"); + archetype.setDescription("Default Description"); + archetype.addTags("Default Tag"); + + parser.parse(context(), tokens("element", "Name", "Box", "Description", "Tag 1, Tag 2"), archetype); + + assertEquals(1, model.getElements().size()); + CustomElement element = model.getCustomElementWithName("Name"); + assertNotNull(element); + assertEquals("Box", element.getMetadata()); + assertEquals("Description", element.getDescription()); // overridden from archetype + assertEquals("Element,Default Tag,Tag 1,Tag 2", element.getTags()); + } + } \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentNodeParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentNodeParserTests.java index 21bbd052c..f5694274e 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentNodeParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentNodeParserTests.java @@ -111,6 +111,27 @@ void test_parse_CreatesADeploymentNodeWithADescriptionAndTechnologyAndTagsAndIns assertEquals("Live", deploymentNode.getEnvironment()); } + @Test + void test_parse_CreatesADeploymentNodeWithADescriptionAndTechnologyAndTagsAndInstancesBasedUponAnArchetype() { + archetype = new Archetype("name", "type"); + archetype.setDescription("Default Description"); + archetype.setTechnology("Default Technology"); + archetype.addTags("Default Tag"); + + DeploymentEnvironmentDslContext context = new DeploymentEnvironmentDslContext("Live"); + context.setWorkspace(workspace); + parser.parse(context, tokens("deploymentNode", "Name", "Description", "Technology", "Tag 1, Tag 2", "8"), archetype); + + assertEquals(1, model.getElements().size()); + DeploymentNode deploymentNode = model.getDeploymentNodeWithName("Name", "Live"); + assertNotNull(deploymentNode); + assertEquals("Description", deploymentNode.getDescription()); // overridden from archetype + assertEquals("Technology", deploymentNode.getTechnology()); // overridden from archetype + assertEquals("Element,Deployment Node,Default Tag,Tag 1,Tag 2", deploymentNode.getTags()); + assertEquals("8", deploymentNode.getInstances()); + assertEquals("Live", deploymentNode.getEnvironment()); + } + @Test void test_parse_ThrowsAnException_WhenTheNumberOfInstancesIsNotValid() { DeploymentEnvironmentDslContext context = new DeploymentEnvironmentDslContext("Live"); @@ -142,6 +163,28 @@ void test_parse_CreatesAChildDeploymentNode() { assertEquals("Live", deploymentNode.getEnvironment()); } + @Test + void test_parse_CreatesAChildDeploymentNodeWithADescriptionAndTechnologyAndTagsAndInstancesBasedUponAnArchetype() { + archetype = new Archetype("name", "type"); + archetype.setDescription("Default Description"); + archetype.setTechnology("Default Technology"); + archetype.addTags("Default Tag"); + + DeploymentNode parent = model.addDeploymentNode("Live", "Parent", "Description", "Technology"); + DeploymentNodeDslContext context = new DeploymentNodeDslContext(parent); + context.setWorkspace(workspace); + parser.parse(context, tokens("deploymentNode", "Name", "Description", "Technology", "Tag 1, Tag 2", "8"), archetype); + + assertEquals(2, model.getElements().size()); + DeploymentNode deploymentNode = parent.getDeploymentNodeWithName("Name"); + assertNotNull(deploymentNode); + assertEquals("Description", deploymentNode.getDescription()); // overridden from archetype + assertEquals("Technology", deploymentNode.getTechnology()); // overridden from archetype + assertEquals("Element,Deployment Node,Default Tag,Tag 1,Tag 2", deploymentNode.getTags()); + assertEquals("8", deploymentNode.getInstances()); + assertEquals("Live", deploymentNode.getEnvironment()); + } + @Test void test_parseTechnology_ThrowsAnException_WhenThereAreTooManyTokens() { try { diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ExplicitRelationshipParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ExplicitRelationshipParserTests.java index 0ac671673..1cd8f213d 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ExplicitRelationshipParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ExplicitRelationshipParserTests.java @@ -150,6 +150,35 @@ void test_parse_AddsTheRelationshipWithADescriptionAndTechnologyAndTags() { assertEquals("Relationship,Tag 1,Tag 2", r.getTags()); } + @Test + void test_parse_AddsTheRelationshipWithADescriptionAndTechnologyAndTagsBasedUponAnArchetype() { + archetype = new Archetype("name", "type"); + archetype.setDescription("Default Description"); + archetype.setTechnology("Default Technology"); + archetype.addTags("Default Tag"); + + Person user = model.addPerson("User", "Description"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + DslContext context = context(); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("source", user); + elements.register("destination", softwareSystem); + context.setIdentifierRegister(elements); + + assertEquals(0, model.getRelationships().size()); + + parser.parse(context, tokens("source", "->", "destination", "Uses", "HTTP", "Tag 1,Tag 2"), archetype); + + assertEquals(1, model.getRelationships().size()); + Relationship r = model.getRelationships().iterator().next(); + assertSame(user, r.getSource()); + assertSame(softwareSystem, r.getDestination()); + assertEquals("Uses", r.getDescription()); // overridden from archetype + assertEquals("HTTP", r.getTechnology()); // overridden from archetype + assertEquals("Relationship,Default Tag,Tag 1,Tag 2", r.getTags()); + } + @Test void test_parse_AddsTheRelationshipAndImplicitRelationshipsWithADescriptionAndTechnologyAndTags() { Person user = model.addPerson("User", "Description"); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ImplicitRelationshipParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ImplicitRelationshipParserTests.java index a84c7fd06..fe6b84a61 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ImplicitRelationshipParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ImplicitRelationshipParserTests.java @@ -144,6 +144,34 @@ void test_parse_AddsTheRelationshipWithADescriptionAndTechnologyAndTags() { assertEquals("Relationship,Tag 1,Tag 2", r.getTags()); } + @Test + void test_parse_AddsTheRelationshipWithADescriptionAndTechnologyAndTagsBasedUponAnArchetype() { + archetype = new Archetype("name", "type"); + archetype.setDescription("Default Description"); + archetype.setTechnology("Default Technology"); + archetype.addTags("Default Tag"); + + Person user = model.addPerson("User", "Description"); + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + ElementDslContext context = context(user); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("destination", softwareSystem); + context.setIdentifierRegister(elements); + + assertEquals(0, model.getRelationships().size()); + + parser.parse(context, tokens("->", "destination", "Uses", "HTTP", "Tag 1,Tag 2"), archetype); + + assertEquals(1, model.getRelationships().size()); + Relationship r = model.getRelationships().iterator().next(); + assertSame(user, r.getSource()); + assertSame(softwareSystem, r.getDestination()); + assertEquals("Uses", r.getDescription()); // overridden from archetype + assertEquals("HTTP", r.getTechnology()); // overridden from archetype + assertEquals("Relationship,Default Tag,Tag 1,Tag 2", r.getTags()); + } + @Test void test_parse_AddsTheRelationshipAndImplicitRelationshipsWithADescriptionAndTechnologyAndTags() { Person user = model.addPerson("User", "Description"); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/InfrastructureNodeParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/InfrastructureNodeParserTests.java index d631709cc..1fbd90759 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/InfrastructureNodeParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/InfrastructureNodeParserTests.java @@ -99,6 +99,28 @@ void test_parse_CreatesAnInfrastructureNodeWithADescriptionAndTechnologyAndTags( assertEquals("Live", infrastructureNode.getEnvironment()); } + @Test + void test_parse_CreatesAnInfrastructureNodeWithADescriptionAndTechnologyAndTagsBasedUponAnArchetype() { + archetype = new Archetype("name", "type"); + archetype.setDescription("Default Description"); + archetype.setTechnology("Default Technology"); + archetype.addTags("Default Tag"); + + DeploymentNode deploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + DeploymentNodeDslContext context = new DeploymentNodeDslContext(deploymentNode); + + parser.parse(context, tokens("infrastructureNode", "Name", "Description", "Technology", "Tag 1, Tag 2"), archetype); + + assertEquals(2, model.getElements().size()); + assertEquals(1, deploymentNode.getInfrastructureNodes().size()); + InfrastructureNode infrastructureNode = deploymentNode.getInfrastructureNodeWithName("Name"); + assertNotNull(infrastructureNode); + assertEquals("Description", infrastructureNode.getDescription()); // overridden from archetype + assertEquals("Technology", infrastructureNode.getTechnology()); // overridden from archetype + assertEquals("Element,Infrastructure Node,Default Tag,Tag 1,Tag 2", infrastructureNode.getTags()); + assertEquals("Live", infrastructureNode.getEnvironment()); + } + @Test void test_parseTechnology_ThrowsAnException_WhenThereAreTooManyTokens() { try { diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/PersonParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/PersonParserTests.java index dac705b99..38960e0e0 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/PersonParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/PersonParserTests.java @@ -63,4 +63,19 @@ void test_parse_CreatesAPersonWithADescriptionAndTags() { assertEquals("Element,Person,Tag 1,Tag 2", user.getTags()); } + @Test + void test_parse_CreatesAPersonWithADescriptionAndTagsBasedUponAnArchetype() { + archetype = new Archetype("name", "type"); + archetype.setDescription("Default Description"); + archetype.addTags("Default Tag"); + + parser.parse(context(), tokens("person", "User", "Description", "Tag 1, Tag 2"), archetype); + + assertEquals(1, model.getElements().size()); + Person user = model.getPersonWithName("User"); + assertNotNull(user); + assertEquals("Description", user.getDescription()); // overridden from archetype + assertEquals("Element,Person,Default Tag,Tag 1,Tag 2", user.getTags()); + } + } \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/SoftwareSystemParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/SoftwareSystemParserTests.java index b33974520..29f9b7840 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/SoftwareSystemParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/SoftwareSystemParserTests.java @@ -63,4 +63,19 @@ void test_parse_CreatesASoftwareSystemWithADescriptionAndTags() { assertEquals("Element,Software System,Tag 1,Tag 2", softwareSystem.getTags()); } + @Test + void test_parse_CreatesASoftwareSystemWithADescriptionAndTagsBasedUponAnArchetype() { + archetype = new Archetype("name", "type"); + archetype.setDescription("Default Description"); + archetype.addTags("Default Tag"); + + parser.parse(context(), tokens("softwareSystem", "Name", "Description", "Tag 1, Tag 2"), archetype); + + assertEquals(1, model.getElements().size()); + SoftwareSystem softwareSystem = model.getSoftwareSystemWithName("Name"); + assertNotNull(softwareSystem); + assertEquals("Description", softwareSystem.getDescription()); // overridden from archetype + assertEquals("Element,Software System,Default Tag,Tag 1,Tag 2", softwareSystem.getTags()); + } + } \ No newline at end of file From 4e8cfbfb064e03221ba614a0c7dd1f126892b41e Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Wed, 23 Apr 2025 09:30:34 +0100 Subject: [PATCH 638/717] Fixes #413. --- changelog.md | 1 + .../src/main/java/com/structurizr/api/WorkspaceApiClient.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index f90c8ca3d..173f44310 100644 --- a/changelog.md +++ b/changelog.md @@ -2,6 +2,7 @@ ## v4.0.1 (unreleased) +- structurizr-client: Fixes https://github.com/structurizr/java/issues/413 (Cannot push to main branch, when branch feature is activated). - structurizr-dsl: Allows archetypes to be used via workspace extension. - structurizr-dsl: Adds archetype support for custom elements. - structurizr-dsl: Fixes https://github.com/structurizr/java/issues/399 (Archetype tags sometimes missing). diff --git a/structurizr-client/src/main/java/com/structurizr/api/WorkspaceApiClient.java b/structurizr-client/src/main/java/com/structurizr/api/WorkspaceApiClient.java index 8524f753d..ce68fa41f 100644 --- a/structurizr-client/src/main/java/com/structurizr/api/WorkspaceApiClient.java +++ b/structurizr-client/src/main/java/com/structurizr/api/WorkspaceApiClient.java @@ -313,7 +313,7 @@ public void putWorkspace(long workspaceId, Workspace workspace) throws Structuri workspace.setLastModifiedUser(getUser()); HttpPut httpPut; - if (StringUtils.isNullOrEmpty(branch)) { + if (StringUtils.isNullOrEmpty(branch) || branch.equalsIgnoreCase(MAIN_BRANCH)) { httpPut = new HttpPut(url + WORKSPACE_PATH + "/" + workspaceId); } else { httpPut = new HttpPut(url + WORKSPACE_PATH + "/" + workspaceId + "/branch/" + branch); From 346095e4168f1f3222ab4897fe3a26be04e09674 Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Sat, 26 Apr 2025 16:27:52 +0100 Subject: [PATCH 639/717] Closes #408. --- changelog.md | 2 + .../com/structurizr/view/DeploymentView.java | 6 +- .../DeploymentViewAnimationStepParser.java | 63 ++++++++++------ .../dsl/DeploymentViewContentParser.java | 7 +- ...eploymentViewAnimationStepParserTests.java | 41 +++++++++- .../dsl/DeploymentViewContentParserTests.java | 4 +- .../java/com/structurizr/dsl/DslTests.java | 22 ++++++ .../resources/dsl/deployment-animation.dsl | 75 +++++++++++++++++++ 8 files changed, 187 insertions(+), 33 deletions(-) create mode 100644 structurizr-dsl/src/test/resources/dsl/deployment-animation.dsl diff --git a/changelog.md b/changelog.md index 173f44310..b03ad7746 100644 --- a/changelog.md +++ b/changelog.md @@ -8,6 +8,8 @@ - structurizr-dsl: Fixes https://github.com/structurizr/java/issues/399 (Archetype tags sometimes missing). - structurizr-dsl: Fixes https://github.com/structurizr/java/issues/392 (SVG not supported in base 64 encoding not mentioned in documentation). - structurizr-dsl: Adds support for setting the symbols surrounding element/relationship metadata used when rendering diagrams. +- structurizr-dsl: Fixes https://github.com/structurizr/java/issues/408 (Animation steps cannot be added to deployment views via static structure element references). +- structurizr-dsl: Adds support for specifying view animation steps via element expressions (deployment views only; others to follow). - structurizr-export: Adds support for rank and node separation to the StructurizrPlantUMLExporter. ## v4.0.0 (28th March 2025) diff --git a/structurizr-core/src/main/java/com/structurizr/view/DeploymentView.java b/structurizr-core/src/main/java/com/structurizr/view/DeploymentView.java index 04638445e..e38113e9a 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/DeploymentView.java +++ b/structurizr-core/src/main/java/com/structurizr/view/DeploymentView.java @@ -403,14 +403,14 @@ private void addAnimationStep(Element... elements) { } } - if (elementsInThisAnimationStep.size() == 0) { - throw new IllegalArgumentException("None of the specified container instances exist in this view."); + if (elementsInThisAnimationStep.isEmpty()) { + throw new IllegalArgumentException("None of the specified elements exist in this view."); } for (RelationshipView relationshipView : this.getRelationships()) { if ( (elementsInThisAnimationStep.contains(relationshipView.getRelationship().getSource()) && elementIdsInPreviousAnimationSteps.contains(relationshipView.getRelationship().getDestination().getId())) || - (elementIdsInPreviousAnimationSteps.contains(relationshipView.getRelationship().getSource().getId()) && elementsInThisAnimationStep.contains(relationshipView.getRelationship().getDestination())) + (elementIdsInPreviousAnimationSteps.contains(relationshipView.getRelationship().getSource().getId()) && elementsInThisAnimationStep.contains(relationshipView.getRelationship().getDestination())) ) { relationshipsInThisAnimationStep.add(relationshipView.getRelationship()); } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentViewAnimationStepParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentViewAnimationStepParser.java index 93598de55..c85e23c89 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentViewAnimationStepParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentViewAnimationStepParser.java @@ -1,59 +1,78 @@ package com.structurizr.dsl; -import com.structurizr.model.ContainerInstance; -import com.structurizr.model.Element; -import com.structurizr.model.InfrastructureNode; -import com.structurizr.model.StaticStructureElementInstance; +import com.structurizr.model.*; import com.structurizr.view.DeploymentView; import java.util.ArrayList; import java.util.List; +import java.util.Set; final class DeploymentViewAnimationStepParser extends AbstractParser { + private static final String GRAMMAR = " [identifier|element expression...]"; + void parse(DeploymentViewDslContext context, Tokens tokens) { - // animationStep [identifier...] + // animationStep [identifier|element expression...] if (!tokens.includes(1)) { - throw new RuntimeException("Expected: animationStep [identifier...]"); + throw new RuntimeException("Expected: animationStep " + GRAMMAR); } parse(context, context.getView(), tokens, 1); } void parse(DeploymentViewAnimationDslContext context, Tokens tokens) { - // animationStep [identifier...] + // [identifier|element expression...] if (!tokens.includes(0)) { - throw new RuntimeException("Expected: [identifier...]"); + throw new RuntimeException("Expected: " + GRAMMAR); } parse(context, context.getView(), tokens, 0); } - void parse(DslContext context, DeploymentView view, Tokens tokens, int startIndex) { + private void parse(DslContext context, DeploymentView view, Tokens tokens, int startIndex) { List staticStructureElementInstances = new ArrayList<>(); List infrastructureNodes = new ArrayList<>(); for (int i = startIndex; i < tokens.size(); i++) { - String identifier = tokens.get(i); - - Element element = context.getElement(identifier); - if (element == null) { - throw new RuntimeException("The element \"" + identifier + "\" does not exist"); - } - - if (element instanceof StaticStructureElementInstance) { - staticStructureElementInstances.add((StaticStructureElementInstance)element); - } - - if (element instanceof InfrastructureNode) { - infrastructureNodes.add((InfrastructureNode)element); + String token = tokens.get(i); + + if (ExpressionParser.isExpression(token.toLowerCase())) { + Set elements = new DeploymentViewExpressionParser().parseExpression(token, context); + + for (ModelItem element : elements) { + if (element instanceof StaticStructureElementInstance) { + staticStructureElementInstances.add((StaticStructureElementInstance)element); + } + + if (element instanceof InfrastructureNode) { + infrastructureNodes.add((InfrastructureNode)element); + } + } + } else { + Set elements = new DeploymentViewExpressionParser().parseIdentifier(token, context); + + if (elements.isEmpty()) { + throw new RuntimeException("The element \"" + token + "\" does not exist"); + } + + for (ModelItem element : elements) { + if (element instanceof StaticStructureElementInstance) { + staticStructureElementInstances.add((StaticStructureElementInstance)element); + } + + if (element instanceof InfrastructureNode) { + infrastructureNodes.add((InfrastructureNode)element); + } + } } } if (!(staticStructureElementInstances.isEmpty() && infrastructureNodes.isEmpty())) { view.addAnimation(staticStructureElementInstances.toArray(new StaticStructureElementInstance[0]), infrastructureNodes.toArray(new InfrastructureNode[0])); + } else { + throw new RuntimeException("No software system instances, container instances, or infrastructure nodes were found"); } } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentViewContentParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentViewContentParser.java index 00a955543..755f50e14 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentViewContentParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentViewContentParser.java @@ -14,12 +14,11 @@ final class DeploymentViewContentParser extends ModelViewContentParser { void parseInclude(DeploymentViewDslContext context, Tokens tokens) { if (!tokens.includes(FIRST_IDENTIFIER_INDEX)) { - throw new RuntimeException("Expected: include <*|identifier> [*|identifier...]"); + throw new RuntimeException("Expected: include <*|identifier|expression> [*|identifier|expression...]"); } DeploymentView view = context.getView(); - // include [identifier...] for (int i = FIRST_IDENTIFIER_INDEX; i < tokens.size(); i++) { String token = tokens.get(i); @@ -36,12 +35,12 @@ void parseInclude(DeploymentViewDslContext context, Tokens tokens) { void parseExclude(DeploymentViewDslContext context, Tokens tokens) { if (!tokens.includes(FIRST_IDENTIFIER_INDEX)) { - throw new RuntimeException("Expected: exclude [identifier...]"); + throw new RuntimeException("Expected: exclude [identifier|expression...]"); } DeploymentView view = context.getView(); - // exclude [identifier...] + for (int i = FIRST_IDENTIFIER_INDEX; i < tokens.size(); i++) { String token = tokens.get(i); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentViewAnimationStepParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentViewAnimationStepParserTests.java index 359931273..a11bbf992 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentViewAnimationStepParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentViewAnimationStepParserTests.java @@ -1,5 +1,7 @@ package com.structurizr.dsl; +import com.structurizr.model.DeploymentNode; +import com.structurizr.view.DeploymentView; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -15,7 +17,7 @@ void test_parseExplicit_ThrowsAnException_WhenElementsAreMissing() { parser.parse((DeploymentViewDslContext)null, tokens("animationStep")); fail(); } catch (Exception e) { - assertEquals("Expected: animationStep [identifier...]", e.getMessage()); + assertEquals("Expected: animationStep [identifier|element expression...]", e.getMessage()); } } @@ -25,7 +27,42 @@ void test_parseImplicit_ThrowsAnException_WhenElementsAreMissing() { parser.parse((DeploymentViewAnimationDslContext)null, tokens()); fail(); } catch (Exception e) { - assertEquals("Expected: [identifier...]", e.getMessage()); + assertEquals("Expected: [identifier|element expression...]", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheElementDoesNotExist() { + DeploymentView view = workspace.getViews().createDeploymentView("key", "Description"); + + DeploymentViewAnimationDslContext context = new DeploymentViewAnimationDslContext(view); + IdentifiersRegister map = new IdentifiersRegister(); + context.setIdentifierRegister(map); + + try { + parser.parse(context, tokens("dn")); + fail(); + } catch (Exception e) { + assertEquals("The element/relationship \"dn\" does not exist", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenNoAnimatableElementsAreFound() { + DeploymentNode deploymentNode = workspace.getModel().addDeploymentNode("Deployment Node"); + DeploymentView view = workspace.getViews().createDeploymentView("key", "Description"); + view.add(deploymentNode); + + DeploymentViewAnimationDslContext context = new DeploymentViewAnimationDslContext(view); + IdentifiersRegister map = new IdentifiersRegister(); + map.register("dn", deploymentNode); + context.setIdentifierRegister(map); + + try { + parser.parse(context, tokens("dn")); + fail(); + } catch (Exception e) { + assertEquals("No software system instances, container instances, or infrastructure nodes were found", e.getMessage()); } } diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentViewContentParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentViewContentParserTests.java index 729ce189e..4bf5d0f30 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentViewContentParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DeploymentViewContentParserTests.java @@ -17,7 +17,7 @@ void test_parseInclude_ThrowsAnException_WhenTheNoElementsAreSpecified() { parser.parseInclude(new DeploymentViewDslContext(null), tokens("include")); fail(); } catch (RuntimeException iae) { - assertEquals("Expected: include <*|identifier> [*|identifier...]", iae.getMessage()); + assertEquals("Expected: include <*|identifier|expression> [*|identifier|expression...]", iae.getMessage()); } } @@ -210,7 +210,7 @@ void test_parseExclude_ThrowsAnException_WhenTheNoElementsAreSpecified() { parser.parseExclude(new DeploymentViewDslContext(null), tokens("exclude")); fail(); } catch (RuntimeException iae) { - assertEquals("Expected: exclude [identifier...]", iae.getMessage()); + assertEquals("Expected: exclude [identifier|expression...]", iae.getMessage()); } } diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index cb957a545..d9d567826 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -1576,4 +1576,26 @@ void test_textBlock() throws Exception { - Line 3""", softwareSystem.getDescription()); } + @Test + void test_deploymentAnimation() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/deployment-animation.dsl")); + + Workspace workspace = parser.getWorkspace(); + Container webapp = workspace.getModel().getSoftwareSystemWithName("Software System").getContainerWithName("Web Application"); + Container db = workspace.getModel().getSoftwareSystemWithName("Software System").getContainerWithName("Database Schema"); + ContainerInstance webappInstance = workspace.getModel().getDeploymentNodeWithName("Deployment Node", "Live").getContainerInstances().stream().filter(ci -> ci.getContainer().equals(webapp)).findFirst().get(); + ContainerInstance dbInstance = workspace.getModel().getDeploymentNodeWithName("Deployment Node", "Live").getContainerInstances().stream().filter(ci -> ci.getContainer().equals(db)).findFirst().get(); + + for (DeploymentView deploymentView : workspace.getViews().getDeploymentViews()) { + assertEquals(2, deploymentView.getAnimations().size()); + + // step 1 + assertTrue(deploymentView.getAnimations().get(0).getElements().contains(webappInstance.getId())); + + // step 2 + assertTrue(deploymentView.getAnimations().get(1).getElements().contains(dbInstance.getId())); + } + } + } \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/deployment-animation.dsl b/structurizr-dsl/src/test/resources/dsl/deployment-animation.dsl new file mode 100644 index 000000000..a46afbbfd --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/deployment-animation.dsl @@ -0,0 +1,75 @@ +workspace { + + model { + ss = softwaresystem "Software System" { + webapp = container "Web Application" { + tag "UI" + } + db = container "Database Schema" { + tag "DB" + } + } + + webapp -> db + + live = deploymentEnvironment "Live" { + dn = deploymentNode "Deployment Node" { + webappInstance = containerInstance webapp + dbInstance = containerInstance db + } + } + } + + views { + deployment ss "Live" { + include * + + // add animation steps via container instance identifiers + animation { + webappInstance + dbInstance + } + } + + deployment ss "Live" { + include * + + // add animation steps via container identifiers + animation { + webapp + db + } + } + + deployment ss "Live" { + include * + + // add animation steps via element expressions + animation { + webapp + webapp-> + } + } + + deployment ss "Live" { + include * + + // add animation steps via element expressions + animation { + webappInstance + webappInstance-> + } + } + + deployment ss "Live" { + include * + + // add animation steps via element expressions + animation { + element.tag==UI + element.tag==DB + } + } + } + +} \ No newline at end of file From af225d0b1143584d2534fd0840395a49d3bde69c Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Sat, 26 Apr 2025 16:33:14 +0100 Subject: [PATCH 640/717] Fixes failing test. --- .../src/test/java/com/structurizr/view/DeploymentViewTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structurizr-core/src/test/java/com/structurizr/view/DeploymentViewTests.java b/structurizr-core/src/test/java/com/structurizr/view/DeploymentViewTests.java index dd4e3c5e1..0fcafacca 100644 --- a/structurizr-core/src/test/java/com/structurizr/view/DeploymentViewTests.java +++ b/structurizr-core/src/test/java/com/structurizr/view/DeploymentViewTests.java @@ -347,7 +347,7 @@ void addAnimationStep_ThrowsAnException_WhenContainerInstancesAreSpecifiedButNon deploymentView.addAnimation(webApplicationInstance, databaseInstance); fail(); } catch (IllegalArgumentException iae) { - assertEquals("None of the specified container instances exist in this view.", iae.getMessage()); + assertEquals("None of the specified elements exist in this view.", iae.getMessage()); } } From 7d28dd5fd0e2ce1aee629137e0ed5965a8469823 Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Sat, 26 Apr 2025 17:31:56 +0100 Subject: [PATCH 641/717] Adds support for specifying view animation steps via element expressions. --- changelog.md | 2 +- .../java/com/structurizr/view/CustomView.java | 2 +- .../java/com/structurizr/view/StaticView.java | 2 +- .../dsl/CustomViewAnimationStepParser.java | 49 +++++++++++------ .../dsl/StaticViewAnimationStepParser.java | 54 +++++++++++++------ .../CustomViewAnimationStepParserTests.java | 21 +++++++- .../java/com/structurizr/dsl/DslTests.java | 44 ++++++++++++++- .../StaticViewAnimationStepParserTests.java | 21 +++++++- .../resources/dsl/custom-view-animation.dsl | 32 +++++++++++ ...tion.dsl => deployment-view-animation.dsl} | 0 .../resources/dsl/static-view-animation.dsl | 32 +++++++++++ 11 files changed, 219 insertions(+), 40 deletions(-) create mode 100644 structurizr-dsl/src/test/resources/dsl/custom-view-animation.dsl rename structurizr-dsl/src/test/resources/dsl/{deployment-animation.dsl => deployment-view-animation.dsl} (100%) create mode 100644 structurizr-dsl/src/test/resources/dsl/static-view-animation.dsl diff --git a/changelog.md b/changelog.md index b03ad7746..fca42ceb7 100644 --- a/changelog.md +++ b/changelog.md @@ -9,7 +9,7 @@ - structurizr-dsl: Fixes https://github.com/structurizr/java/issues/392 (SVG not supported in base 64 encoding not mentioned in documentation). - structurizr-dsl: Adds support for setting the symbols surrounding element/relationship metadata used when rendering diagrams. - structurizr-dsl: Fixes https://github.com/structurizr/java/issues/408 (Animation steps cannot be added to deployment views via static structure element references). -- structurizr-dsl: Adds support for specifying view animation steps via element expressions (deployment views only; others to follow). +- structurizr-dsl: Adds support for specifying view animation steps via element expressions. - structurizr-export: Adds support for rank and node separation to the StructurizrPlantUMLExporter. ## v4.0.0 (28th March 2025) diff --git a/structurizr-core/src/main/java/com/structurizr/view/CustomView.java b/structurizr-core/src/main/java/com/structurizr/view/CustomView.java index f5b9447ef..81558f604 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/CustomView.java +++ b/structurizr-core/src/main/java/com/structurizr/view/CustomView.java @@ -110,7 +110,7 @@ public void addAnimation(CustomElement... elements) { } } - if (elementsInThisAnimationStep.size() == 0) { + if (elementsInThisAnimationStep.isEmpty()) { throw new IllegalArgumentException("None of the specified elements exist in this view."); } diff --git a/structurizr-core/src/main/java/com/structurizr/view/StaticView.java b/structurizr-core/src/main/java/com/structurizr/view/StaticView.java index 1be47ef96..d5934af05 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/StaticView.java +++ b/structurizr-core/src/main/java/com/structurizr/view/StaticView.java @@ -218,7 +218,7 @@ public void addAnimation(Element... elements) { } } - if (elementsInThisAnimationStep.size() == 0) { + if (elementsInThisAnimationStep.isEmpty()) { throw new IllegalArgumentException("None of the specified elements exist in this view."); } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomViewAnimationStepParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomViewAnimationStepParser.java index c455c18d4..e0e32202b 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomViewAnimationStepParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/CustomViewAnimationStepParser.java @@ -1,51 +1,70 @@ package com.structurizr.dsl; -import com.structurizr.model.CustomElement; -import com.structurizr.model.Element; +import com.structurizr.model.*; import com.structurizr.view.CustomView; import java.util.ArrayList; import java.util.List; +import java.util.Set; final class CustomViewAnimationStepParser extends AbstractParser { + private static final String GRAMMAR = " [identifier|element expression...]"; + void parse(CustomViewDslContext context, Tokens tokens) { - // animationStep [identifier...] + // animationStep [identifier|element expression...] if (!tokens.includes(1)) { - throw new RuntimeException("Expected: animationStep [identifier...]"); + throw new RuntimeException("Expected: animationStep " + GRAMMAR); } parse(context, context.getCustomView(), tokens, 1); } void parse(CustomViewAnimationDslContext context, Tokens tokens) { - // [identifier...] + // [identifier|element expression...] if (!tokens.includes(0)) { - throw new RuntimeException("Expected: [identifier...]"); + throw new RuntimeException("Expected: " + GRAMMAR); } parse(context, context.getView(), tokens, 0); } void parse(DslContext context, CustomView view, Tokens tokens, int startIndex) { - List elements = new ArrayList<>(); + List customElements = new ArrayList<>(); for (int i = startIndex; i < tokens.size(); i++) { - String elementIdentifier = tokens.get(i); + String token = tokens.get(i); - Element element = context.getElement(elementIdentifier); - if (element == null) { - throw new RuntimeException("The element \"" + elementIdentifier + "\" does not exist"); - } + if (ExpressionParser.isExpression(token.toLowerCase())) { + Set elements = new CustomViewExpressionParser().parseExpression(token, context); + + for (ModelItem element : elements) { + if (element instanceof CustomElement) { + customElements.add((CustomElement)element); + } + } + } else { + Set elements = new CustomViewExpressionParser().parseIdentifier(token, context); - if (element instanceof CustomElement) { - elements.add((CustomElement)element); + if (elements.isEmpty()) { + throw new RuntimeException("The element \"" + token + "\" does not exist"); + } + + for (ModelItem element : elements) { + if (element instanceof CustomElement) { + customElements.add((CustomElement)element); + } + } } } - view.addAnimation(elements.toArray(new CustomElement[0])); + if (!customElements.isEmpty()) { + view.addAnimation(customElements.toArray(new CustomElement[0])); + } else { + throw new RuntimeException("No custom elements were found"); + } } } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StaticViewAnimationStepParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StaticViewAnimationStepParser.java index 04f76a118..9ce90cfee 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StaticViewAnimationStepParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StaticViewAnimationStepParser.java @@ -1,50 +1,72 @@ package com.structurizr.dsl; import com.structurizr.model.Element; +import com.structurizr.model.ModelItem; +import com.structurizr.model.StaticStructureElement; import com.structurizr.view.StaticView; import java.util.ArrayList; import java.util.List; +import java.util.Set; final class StaticViewAnimationStepParser extends AbstractParser { + private static final String GRAMMAR = " [identifier|element expression...]"; + void parse(StaticViewDslContext context, Tokens tokens) { - // animationStep [identifier...] + // animationStep [identifier|element expression...] if (!tokens.includes(1)) { - throw new RuntimeException("Expected: animationStep [identifier...]"); + throw new RuntimeException("Expected: animationStep " + GRAMMAR); } parse(context, context.getView(), tokens, 1); } void parse(StaticViewAnimationDslContext context, Tokens tokens) { - // [identifier...] + // [identifier|element expression...] if (!tokens.includes(0)) { - throw new RuntimeException("Expected: [identifier...]"); + throw new RuntimeException("Expected: " + GRAMMAR); } parse(context, context.getView(), tokens, 0); } - void parse(DslContext context, StaticView view, Tokens tokens, int startIndex) { - // [identifier...] - - List elements = new ArrayList<>(); + private void parse(DslContext context, StaticView view, Tokens tokens, int startIndex) { + List staticStructureElements = new ArrayList<>(); for (int i = startIndex; i < tokens.size(); i++) { - String elementIdentifier = tokens.get(i); - - Element element = context.getElement(elementIdentifier); - if (element == null) { - throw new RuntimeException("The element \"" + elementIdentifier + "\" does not exist"); + String token = tokens.get(i); + + if (ExpressionParser.isExpression(token.toLowerCase())) { + Set elements = new StaticViewExpressionParser().parseExpression(token, context); + + for (ModelItem element : elements) { + if (element instanceof StaticStructureElement) { + staticStructureElements.add((StaticStructureElement)element); + } + } + } else { + Set elements = new StaticViewExpressionParser().parseIdentifier(token, context); + + if (elements.isEmpty()) { + throw new RuntimeException("The element \"" + token + "\" does not exist"); + } + + for (ModelItem element : elements) { + if (element instanceof StaticStructureElement) { + staticStructureElements.add((StaticStructureElement)element); + } + } } - - elements.add(element); } - view.addAnimation(elements.toArray(new Element[0])); + if (!staticStructureElements.isEmpty()) { + view.addAnimation(staticStructureElements.toArray(new Element[0])); + } else { + throw new RuntimeException("No elements were found"); + } } } \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/CustomViewAnimationStepParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/CustomViewAnimationStepParserTests.java index b820ae888..b1cd19d46 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/CustomViewAnimationStepParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/CustomViewAnimationStepParserTests.java @@ -1,5 +1,6 @@ package com.structurizr.dsl; +import com.structurizr.view.CustomView; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -15,7 +16,7 @@ void test_parseExplicit_ThrowsAnException_WhenElementsAreMissing() { parser.parse((CustomViewDslContext)null, tokens("animationStep")); fail(); } catch (Exception e) { - assertEquals("Expected: animationStep [identifier...]", e.getMessage()); + assertEquals("Expected: animationStep [identifier|element expression...]", e.getMessage()); } } @@ -25,7 +26,23 @@ void test_parseImplicit_ThrowsAnException_WhenElementsAreMissing() { parser.parse((CustomViewAnimationDslContext) null, tokens()); fail(); } catch (Exception e) { - assertEquals("Expected: [identifier...]", e.getMessage()); + assertEquals("Expected: [identifier|element expression...]", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheElementDoesNotExist() { + CustomView view = workspace.getViews().createCustomView("key", "Title", "Description"); + + CustomViewAnimationDslContext context = new CustomViewAnimationDslContext(view); + IdentifiersRegister map = new IdentifiersRegister(); + context.setIdentifierRegister(map); + + try { + parser.parse(context, tokens("e")); + fail(); + } catch (Exception e) { + assertEquals("The element/relationship \"e\" does not exist", e.getMessage()); } } diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index d9d567826..5a082b7d5 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -1577,9 +1577,49 @@ void test_textBlock() throws Exception { } @Test - void test_deploymentAnimation() throws Exception { + void test_customViewAnimation() throws Exception { StructurizrDslParser parser = new StructurizrDslParser(); - parser.parse(new File("src/test/resources/dsl/deployment-animation.dsl")); + parser.parse(new File("src/test/resources/dsl/custom-view-animation.dsl")); + + Workspace workspace = parser.getWorkspace(); + CustomElement a = workspace.getModel().getCustomElementWithName("A"); + CustomElement b = workspace.getModel().getCustomElementWithName("B"); + + for (CustomView view : workspace.getViews().getCustomViews()) { + assertEquals(2, view.getAnimations().size()); + + // step 1 + assertTrue(view.getAnimations().get(0).getElements().contains(a.getId())); + + // step 2 + assertTrue(view.getAnimations().get(1).getElements().contains(b.getId())); + } + } + + @Test + void test_staticViewAnimation() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/static-view-animation.dsl")); + + Workspace workspace = parser.getWorkspace(); + SoftwareSystem a = workspace.getModel().getSoftwareSystemWithName("A"); + SoftwareSystem b = workspace.getModel().getSoftwareSystemWithName("B"); + + for (SystemLandscapeView view : workspace.getViews().getSystemLandscapeViews()) { + assertEquals(2, view.getAnimations().size()); + + // step 1 + assertTrue(view.getAnimations().get(0).getElements().contains(a.getId())); + + // step 2 + assertTrue(view.getAnimations().get(1).getElements().contains(b.getId())); + } + } + + @Test + void test_deploymentViewAnimation() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/deployment-view-animation.dsl")); Workspace workspace = parser.getWorkspace(); Container webapp = workspace.getModel().getSoftwareSystemWithName("Software System").getContainerWithName("Web Application"); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/StaticViewAnimationStepParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/StaticViewAnimationStepParserTests.java index 557f1e378..61b0a0b4a 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/StaticViewAnimationStepParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/StaticViewAnimationStepParserTests.java @@ -1,5 +1,6 @@ package com.structurizr.dsl; +import com.structurizr.view.SystemLandscapeView; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -15,7 +16,7 @@ void test_parseExplicit_ThrowsAnException_WhenElementsAreMissing() { parser.parse((StaticViewDslContext)null, tokens("animationStep")); fail(); } catch (Exception e) { - assertEquals("Expected: animationStep [identifier...]", e.getMessage()); + assertEquals("Expected: animationStep [identifier|element expression...]", e.getMessage()); } } @@ -25,7 +26,23 @@ void test_parseImplicit_ThrowsAnException_WhenElementsAreMissing() { parser.parse((StaticViewAnimationDslContext) null, tokens()); fail(); } catch (Exception e) { - assertEquals("Expected: [identifier...]", e.getMessage()); + assertEquals("Expected: [identifier|element expression...]", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheElementDoesNotExist() { + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "Description"); + + StaticViewAnimationDslContext context = new StaticViewAnimationDslContext(view); + IdentifiersRegister map = new IdentifiersRegister(); + context.setIdentifierRegister(map); + + try { + parser.parse(context, tokens("user")); + fail(); + } catch (Exception e) { + assertEquals("The element/relationship \"user\" does not exist", e.getMessage()); } } diff --git a/structurizr-dsl/src/test/resources/dsl/custom-view-animation.dsl b/structurizr-dsl/src/test/resources/dsl/custom-view-animation.dsl new file mode 100644 index 000000000..fe1679cca --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/custom-view-animation.dsl @@ -0,0 +1,32 @@ +workspace { + + model { + a = element "A" + b = element "B" + + a -> b + } + + views { + custom { + include * + + // add animation steps via element identifiers + animation { + a + b + } + } + + custom { + include * + + // add animation steps via element expressions + animation { + a + a-> + } + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/deployment-animation.dsl b/structurizr-dsl/src/test/resources/dsl/deployment-view-animation.dsl similarity index 100% rename from structurizr-dsl/src/test/resources/dsl/deployment-animation.dsl rename to structurizr-dsl/src/test/resources/dsl/deployment-view-animation.dsl diff --git a/structurizr-dsl/src/test/resources/dsl/static-view-animation.dsl b/structurizr-dsl/src/test/resources/dsl/static-view-animation.dsl new file mode 100644 index 000000000..e68edd0d5 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/static-view-animation.dsl @@ -0,0 +1,32 @@ +workspace { + + model { + a = softwareSystem "A" + b = softwareSystem "B" + + a -> b + } + + views { + systemLandscape { + include * + + // add animation steps via element identifiers + animation { + a + b + } + } + + systemLandscape { + include * + + // add animation steps via element expressions + animation { + a + a-> + } + } + } + +} \ No newline at end of file From 0e14d602f4f7f63ba7fdd86d5ee6d475c672bd86 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 2 May 2025 15:47:48 +0100 Subject: [PATCH 642/717] Fixes #404. --- changelog.md | 1 + .../dsl/ContainerInstanceParser.java | 17 ++--- .../dsl/DeploymentEnvironmentDslContext.java | 8 +- .../com/structurizr/dsl/DeploymentGroup.java | 8 +- .../structurizr/dsl/DeploymentNodeParser.java | 2 +- .../java/com/structurizr/dsl/DslContext.java | 6 +- .../dsl/SoftwareSystemInstanceParser.java | 17 ++--- .../dsl/StaticStructureInstanceParser.java | 34 +++++++++ .../structurizr/dsl/StructurizrDslParser.java | 6 +- .../dsl/ContainerInstanceParserTests.java | 10 ++- .../java/com/structurizr/dsl/DslTests.java | 74 +++++++++++++++++++ .../SoftwareSystemInstanceParserTests.java | 10 ++- ...-groups.dsl => deployment-groups-flat.dsl} | 20 +---- .../dsl/deployment-groups-hierarchical.dsl | 51 +++++++++++++ 14 files changed, 210 insertions(+), 54 deletions(-) create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/StaticStructureInstanceParser.java rename structurizr-dsl/src/test/resources/dsl/{deployment-groups.dsl => deployment-groups-flat.dsl} (70%) create mode 100644 structurizr-dsl/src/test/resources/dsl/deployment-groups-hierarchical.dsl diff --git a/changelog.md b/changelog.md index fca42ceb7..971f41c74 100644 --- a/changelog.md +++ b/changelog.md @@ -10,6 +10,7 @@ - structurizr-dsl: Adds support for setting the symbols surrounding element/relationship metadata used when rendering diagrams. - structurizr-dsl: Fixes https://github.com/structurizr/java/issues/408 (Animation steps cannot be added to deployment views via static structure element references). - structurizr-dsl: Adds support for specifying view animation steps via element expressions. +- structurizr-dsl: Fixes https://github.com/structurizr/java/issues/404 (deploymentGroup does not obey !identifiers hierarchical). - structurizr-export: Adds support for rank and node separation to the StructurizrPlantUMLExporter. ## v4.0.0 (28th March 2025) diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerInstanceParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerInstanceParser.java index c58287956..04e52c35e 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerInstanceParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ContainerInstanceParser.java @@ -1,11 +1,14 @@ package com.structurizr.dsl; -import com.structurizr.model.*; +import com.structurizr.model.Container; +import com.structurizr.model.ContainerInstance; +import com.structurizr.model.DeploymentNode; +import com.structurizr.model.Element; import java.util.HashSet; import java.util.Set; -final class ContainerInstanceParser extends AbstractParser { +final class ContainerInstanceParser extends StaticStructureInstanceParser { private static final String GRAMMAR = "containerInstance [deploymentGroups] [tags]"; @@ -35,15 +38,7 @@ ContainerInstance parse(DeploymentNodeDslContext context, Tokens tokens) { Set deploymentGroups = new HashSet<>(); if (tokens.includes(DEPLOYMENT_GROUPS_TOKEN)) { - String token = tokens.get(DEPLOYMENT_GROUPS_TOKEN); - - String[] deploymentGroupReferences = token.split(","); - for (String deploymentGroupReference : deploymentGroupReferences) { - Element e = context.getElement(deploymentGroupReference); - if (e instanceof DeploymentGroup) { - deploymentGroups.add(e.getName()); - } - } + deploymentGroups = getDeploymentGroups(context, tokens.get(DEPLOYMENT_GROUPS_TOKEN)); } ContainerInstance containerInstance = deploymentNode.add((Container)element, deploymentGroups.toArray(new String[]{})); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentEnvironmentDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentEnvironmentDslContext.java index ceb1c30b2..4cc53b0fc 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentEnvironmentDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentEnvironmentDslContext.java @@ -2,20 +2,20 @@ final class DeploymentEnvironmentDslContext extends DslContext implements GroupableDslContext { - private final String environment; + private final DeploymentEnvironment environment; private final ElementGroup group; DeploymentEnvironmentDslContext(String environment) { - this.environment = environment; + this.environment = new DeploymentEnvironment(environment); this.group = null; } DeploymentEnvironmentDslContext(String environment, ElementGroup group) { - this.environment = environment; + this.environment = new DeploymentEnvironment(environment); this.group = group; } - String getEnvironment() { + DeploymentEnvironment getEnvironment() { return environment; } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentGroup.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentGroup.java index 0027da22f..a89e9fb49 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentGroup.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentGroup.java @@ -6,9 +6,11 @@ class DeploymentGroup extends Element { - private String name; + private final Element parent; + private final String name; - DeploymentGroup(String name) { + DeploymentGroup(DeploymentEnvironment deploymentEnvironment, String name) { + this.parent = deploymentEnvironment; this.name = name; } @@ -24,7 +26,7 @@ public String getCanonicalName() { @Override public Element getParent() { - return null; + return parent; } @Override diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeParser.java index 93866302b..a6aa7d859 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeParser.java @@ -51,7 +51,7 @@ DeploymentNode parse(DeploymentEnvironmentDslContext deploymentEnvironmentDslCon if (deploymentEnvironmentDslContext != null) { // add a root deployment node - deploymentNode = deploymentEnvironmentDslContext.getWorkspace().getModel().addDeploymentNode(deploymentEnvironmentDslContext.getEnvironment(), name, description, technology); + deploymentNode = deploymentEnvironmentDslContext.getWorkspace().getModel().addDeploymentNode(deploymentEnvironmentDslContext.getEnvironment().getName(), name, description, technology); if (deploymentEnvironmentDslContext.hasGroup()) { deploymentNode.setGroup(deploymentEnvironmentDslContext.getGroup().getName()); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DslContext.java index 2711c9138..41417ba8e 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/DslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DslContext.java @@ -40,6 +40,10 @@ void setIdentifierRegister(IdentifiersRegister identifersRegister) { this.identifiersRegister = identifersRegister; } + String findIdentifier(Element element) { + return identifiersRegister.findIdentifier(element); + } + Element getElement(String identifier) { return getElement(identifier, null); } @@ -71,7 +75,7 @@ Element getElement(String identifier, Class type) { } } else if (this instanceof DeploymentEnvironmentDslContext) { DeploymentEnvironmentDslContext deploymentEnvironmentDslContext = (DeploymentEnvironmentDslContext)this; - DeploymentEnvironment deploymentEnvironment = new DeploymentEnvironment(deploymentEnvironmentDslContext.getEnvironment()); + DeploymentEnvironment deploymentEnvironment = deploymentEnvironmentDslContext.getEnvironment(); String parentIdentifier = identifiersRegister.findIdentifier(deploymentEnvironment); element = identifiersRegister.getElement(parentIdentifier + "." + identifier); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemInstanceParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemInstanceParser.java index dee362c64..88f5da70d 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemInstanceParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/SoftwareSystemInstanceParser.java @@ -1,11 +1,14 @@ package com.structurizr.dsl; -import com.structurizr.model.*; +import com.structurizr.model.DeploymentNode; +import com.structurizr.model.Element; +import com.structurizr.model.SoftwareSystem; +import com.structurizr.model.SoftwareSystemInstance; import java.util.HashSet; import java.util.Set; -final class SoftwareSystemInstanceParser extends AbstractParser { +final class SoftwareSystemInstanceParser extends StaticStructureInstanceParser { private static final String GRAMMAR = "softwareSystemInstance [deploymentGroups] [tags]"; @@ -36,15 +39,7 @@ SoftwareSystemInstance parse(DeploymentNodeDslContext context, Tokens tokens) { Set deploymentGroups = new HashSet<>(); if (tokens.includes(DEPLOYMENT_GROUPS_TOKEN)) { - String token = tokens.get(DEPLOYMENT_GROUPS_TOKEN); - - String[] deploymentGroupReferences = token.split(","); - for (String deploymentGroupReference : deploymentGroupReferences) { - Element e = context.getElement(deploymentGroupReference); - if (e instanceof DeploymentGroup) { - deploymentGroups.add(e.getName()); - } - } + deploymentGroups = getDeploymentGroups(context, tokens.get(DEPLOYMENT_GROUPS_TOKEN)); } SoftwareSystemInstance softwareSystemInstance = deploymentNode.add((SoftwareSystem)element, deploymentGroups.toArray(new String[]{})); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StaticStructureInstanceParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StaticStructureInstanceParser.java new file mode 100644 index 000000000..c1b191be9 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StaticStructureInstanceParser.java @@ -0,0 +1,34 @@ +package com.structurizr.dsl; + +import com.structurizr.model.Element; + +import java.util.HashSet; +import java.util.Set; + +abstract class StaticStructureInstanceParser extends AbstractParser { + + protected Set getDeploymentGroups(DeploymentNodeDslContext context, String token) { + Set deploymentGroups = new HashSet<>(); + String[] deploymentGroupReferences = token.split(","); + for (String deploymentGroupReference : deploymentGroupReferences) { + Element e = context.getElement(deploymentGroupReference, DeploymentGroup.class); + + if (e == null) { + // try to find deployment group via hierarchical identifier + String deploymentEnvironmentName = context.getDeploymentNode().getEnvironment(); + String deploymentEnvironmentIdentifier = context.findIdentifier(new DeploymentEnvironment(deploymentEnvironmentName)); + + e = context.getElement(deploymentEnvironmentIdentifier + "." + deploymentGroupReference, DeploymentGroup.class); + } + + if (e instanceof DeploymentGroup) { + deploymentGroups.add(e.getName()); + } else { + // backwards compatibility - deployment environment name rather than identifier + } + } + + return deploymentGroups; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index 563d40dcd..4664c0266 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -549,8 +549,8 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn } else if (isElementKeywordOrArchetype(firstToken, GROUP_TOKEN) && inContext(DeploymentEnvironmentDslContext.class)) { ElementGroup group = new GroupParser().parseContext(getContext(DeploymentEnvironmentDslContext.class), tokens); - String environment = getContext(DeploymentEnvironmentDslContext.class).getEnvironment(); - startContext(new DeploymentEnvironmentDslContext(environment, group)); + DeploymentEnvironment environment = getContext(DeploymentEnvironmentDslContext.class).getEnvironment(); + startContext(new DeploymentEnvironmentDslContext(environment.getName(), group)); registerIdentifier(identifier, group); } else if (isElementKeywordOrArchetype(firstToken, GROUP_TOKEN) && inContext(DeploymentNodeDslContext.class)) { ElementGroup group = new GroupParser().parseContext(getContext(DeploymentNodeDslContext.class), tokens); @@ -877,7 +877,7 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn } else if (DEPLOYMENT_GROUP_TOKEN.equalsIgnoreCase(firstToken) && inContext(DeploymentEnvironmentDslContext.class)) { String group = new DeploymentGroupParser().parse(tokens.withoutContextStartToken()); - registerIdentifier(identifier, new DeploymentGroup(group)); + registerIdentifier(identifier, new DeploymentGroup(getContext(DeploymentEnvironmentDslContext.class).getEnvironment(), group)); } else if (isElementKeywordOrArchetype(firstToken, DEPLOYMENT_NODE_TOKEN) && inContext(DeploymentEnvironmentDslContext.class)) { Archetype archetype = getArchetype(DEPLOYMENT_NODE_TOKEN, firstToken); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ContainerInstanceParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ContainerInstanceParserTests.java index 073602d61..ba62530c0 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ContainerInstanceParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ContainerInstanceParserTests.java @@ -103,10 +103,13 @@ void test_parse_CreatesAContainerInstanceInTheSpecifiedDeploymentGroup() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); Container container = softwareSystem.addContainer("Container", "Description", "Technology"); DeploymentNode deploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + DeploymentNodeDslContext context = new DeploymentNodeDslContext(deploymentNode); + IdentifiersRegister elements = new IdentifiersRegister(); elements.register("container", container); - elements.register("group", new DeploymentGroup("Group")); + elements.register("live", new DeploymentEnvironment("Live")); + elements.register("group", new DeploymentGroup(new DeploymentEnvironment("Live"), "Group")); context.setIdentifierRegister(elements); parser.parse(context, tokens("containerInstance", "container", "group")); @@ -126,10 +129,13 @@ void test_parse_CreatesAContainerInstanceInTheSpecifiedDeploymentGroupWithTags() SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); Container container = softwareSystem.addContainer("Container", "Description", "Technology"); DeploymentNode deploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + DeploymentNodeDslContext context = new DeploymentNodeDslContext(deploymentNode); + IdentifiersRegister elements = new IdentifiersRegister(); elements.register("container", container); - elements.register("group", new DeploymentGroup("Group")); + elements.register("live", new DeploymentEnvironment("Live")); + elements.register("group", new DeploymentGroup(new DeploymentEnvironment("Live"), "Group")); context.setIdentifierRegister(elements); parser.parse(context, tokens("containerInstance", "container", "group", "Tag 1, Tag 2")); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index 5a082b7d5..8761713fd 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -1638,4 +1638,78 @@ void test_deploymentViewAnimation() throws Exception { } } + @Test + void test_deploymentGroups_Flat() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/deployment-groups-flat.dsl")); + + Workspace workspace = parser.getWorkspace(); + + Container api = workspace.getModel().getSoftwareSystemWithName("Software System").getContainerWithName("API"); + Container db = workspace.getModel().getSoftwareSystemWithName("Software System").getContainerWithName("DB"); + + DeploymentNode server1 = workspace.getModel().getDeploymentNodeWithName("Server 1", "WithoutDeploymentGroups"); + ContainerInstance apiInstance1 = server1.getContainerInstances().stream().filter(ci -> ci.getContainer().equals(api)).findFirst().get(); + ContainerInstance dbInstance1 = server1.getContainerInstances().stream().filter(ci -> ci.getContainer().equals(db)).findFirst().get(); + + DeploymentNode server2 = workspace.getModel().getDeploymentNodeWithName("Server 2", "WithoutDeploymentGroups"); + ContainerInstance apiInstance2 = server2.getContainerInstances().stream().filter(ci -> ci.getContainer().equals(api)).findFirst().get(); + ContainerInstance dbInstance2 = server2.getContainerInstances().stream().filter(ci -> ci.getContainer().equals(db)).findFirst().get(); + + assertTrue(apiInstance1.hasEfferentRelationshipWith(dbInstance1)); + assertTrue(apiInstance1.hasEfferentRelationshipWith(dbInstance2)); + assertTrue(apiInstance2.hasEfferentRelationshipWith(dbInstance2)); + assertTrue(apiInstance2.hasEfferentRelationshipWith(dbInstance1)); + + server1 = workspace.getModel().getDeploymentNodeWithName("Server 1", "WithDeploymentGroups"); + apiInstance1 = server1.getContainerInstances().stream().filter(ci -> ci.getContainer().equals(api)).findFirst().get(); + dbInstance1 = server1.getContainerInstances().stream().filter(ci -> ci.getContainer().equals(db)).findFirst().get(); + + server2 = workspace.getModel().getDeploymentNodeWithName("Server 2", "WithDeploymentGroups"); + apiInstance2 = server2.getContainerInstances().stream().filter(ci -> ci.getContainer().equals(api)).findFirst().get(); + dbInstance2 = server2.getContainerInstances().stream().filter(ci -> ci.getContainer().equals(db)).findFirst().get(); + + assertTrue(apiInstance1.hasEfferentRelationshipWith(dbInstance1)); + assertFalse(apiInstance1.hasEfferentRelationshipWith(dbInstance2)); + assertTrue(apiInstance2.hasEfferentRelationshipWith(dbInstance2)); + assertFalse(apiInstance2.hasEfferentRelationshipWith(dbInstance1)); + } + + @Test + void test_deploymentGroups_Hierarchical() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/deployment-groups-hierarchical.dsl")); + + Workspace workspace = parser.getWorkspace(); + + Container api = workspace.getModel().getSoftwareSystemWithName("Software System").getContainerWithName("API"); + Container db = workspace.getModel().getSoftwareSystemWithName("Software System").getContainerWithName("DB"); + + DeploymentNode server1 = workspace.getModel().getDeploymentNodeWithName("Server 1", "WithoutDeploymentGroups"); + ContainerInstance apiInstance1 = server1.getContainerInstances().stream().filter(ci -> ci.getContainer().equals(api)).findFirst().get(); + ContainerInstance dbInstance1 = server1.getContainerInstances().stream().filter(ci -> ci.getContainer().equals(db)).findFirst().get(); + + DeploymentNode server2 = workspace.getModel().getDeploymentNodeWithName("Server 2", "WithoutDeploymentGroups"); + ContainerInstance apiInstance2 = server2.getContainerInstances().stream().filter(ci -> ci.getContainer().equals(api)).findFirst().get(); + ContainerInstance dbInstance2 = server2.getContainerInstances().stream().filter(ci -> ci.getContainer().equals(db)).findFirst().get(); + + assertTrue(apiInstance1.hasEfferentRelationshipWith(dbInstance1)); + assertTrue(apiInstance1.hasEfferentRelationshipWith(dbInstance2)); + assertTrue(apiInstance2.hasEfferentRelationshipWith(dbInstance2)); + assertTrue(apiInstance2.hasEfferentRelationshipWith(dbInstance1)); + + server1 = workspace.getModel().getDeploymentNodeWithName("Server 1", "WithDeploymentGroups"); + apiInstance1 = server1.getContainerInstances().stream().filter(ci -> ci.getContainer().equals(api)).findFirst().get(); + dbInstance1 = server1.getContainerInstances().stream().filter(ci -> ci.getContainer().equals(db)).findFirst().get(); + + server2 = workspace.getModel().getDeploymentNodeWithName("Server 2", "WithDeploymentGroups"); + apiInstance2 = server2.getContainerInstances().stream().filter(ci -> ci.getContainer().equals(api)).findFirst().get(); + dbInstance2 = server2.getContainerInstances().stream().filter(ci -> ci.getContainer().equals(db)).findFirst().get(); + + assertTrue(apiInstance1.hasEfferentRelationshipWith(dbInstance1)); + assertFalse(apiInstance1.hasEfferentRelationshipWith(dbInstance2)); + assertTrue(apiInstance2.hasEfferentRelationshipWith(dbInstance2)); + assertFalse(apiInstance2.hasEfferentRelationshipWith(dbInstance1)); + } + } \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/SoftwareSystemInstanceParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/SoftwareSystemInstanceParserTests.java index 1d0b7e278..c25ad5e43 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/SoftwareSystemInstanceParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/SoftwareSystemInstanceParserTests.java @@ -100,10 +100,13 @@ void test_parse_CreatesASoftwareSystemInstanceInTheDefaultDeploymentGroupWithTag void test_parse_CreatesASoftwareSystemInstanceInTheSpecifiedDeploymentGroup() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); DeploymentNode deploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + DeploymentNodeDslContext context = new DeploymentNodeDslContext(deploymentNode); + IdentifiersRegister elements = new IdentifiersRegister(); elements.register("softwaresystem", softwareSystem); - elements.register("group", new DeploymentGroup("Group")); + elements.register("live", new DeploymentEnvironment("Live")); + elements.register("group", new DeploymentGroup(new DeploymentEnvironment("Live"), "Group")); context.setIdentifierRegister(elements); parser.parse(context, tokens("softwareSystemInstance", "softwareSystem", "group")); @@ -122,10 +125,13 @@ void test_parse_CreatesASoftwareSystemInstanceInTheSpecifiedDeploymentGroup() { void test_parse_CreatesASoftwareSystemInstanceInTheSpecifiedDeploymentGroupWithTags() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); DeploymentNode deploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + DeploymentNodeDslContext context = new DeploymentNodeDslContext(deploymentNode); + IdentifiersRegister elements = new IdentifiersRegister(); elements.register("softwaresystem", softwareSystem); - elements.register("group", new DeploymentGroup("Group")); + elements.register("live", new DeploymentEnvironment("Live")); + elements.register("group", new DeploymentGroup(new DeploymentEnvironment("Live"), "Group")); context.setIdentifierRegister(elements); parser.parse(context, tokens("softwareSystemInstance", "softwareSystem", "group", "Tag 1, Tag 2")); diff --git a/structurizr-dsl/src/test/resources/dsl/deployment-groups.dsl b/structurizr-dsl/src/test/resources/dsl/deployment-groups-flat.dsl similarity index 70% rename from structurizr-dsl/src/test/resources/dsl/deployment-groups.dsl rename to structurizr-dsl/src/test/resources/dsl/deployment-groups-flat.dsl index e58f506ee..287b4abe6 100644 --- a/structurizr-dsl/src/test/resources/dsl/deployment-groups.dsl +++ b/structurizr-dsl/src/test/resources/dsl/deployment-groups-flat.dsl @@ -2,13 +2,13 @@ workspace { model { softwareSystem = softwareSystem "Software System" { - database = container "Database" - api = container "Service API" { + database = container "DB" + api = container "API" { -> database "Uses" } } - deploymentEnvironment "Example 1" { + deploymentEnvironment "WithoutDeploymentGroups" { deploymentNode "Server 1" { containerInstance api containerInstance database @@ -19,7 +19,7 @@ workspace { } } - deploymentEnvironment "Example 2" { + deploymentEnvironment "WithDeploymentGroups" { serviceInstance1 = deploymentGroup "Service Instance 1" serviceInstance2 = deploymentGroup "Service Instance 2" deploymentNode "Server 1" { @@ -33,16 +33,4 @@ workspace { } } - views { - deployment * "Example 1" { - include * - autolayout - } - - deployment * "Example 2" { - include * - autolayout - } - } - } \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/deployment-groups-hierarchical.dsl b/structurizr-dsl/src/test/resources/dsl/deployment-groups-hierarchical.dsl new file mode 100644 index 000000000..202ea2a71 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/deployment-groups-hierarchical.dsl @@ -0,0 +1,51 @@ +workspace { + + !identifiers hierarchical + + model { + softwareSystem = softwareSystem "Software System" { + database = container "DB" + api = container "API" { + -> database "Uses" + } + } + + deploymentEnvironment "WithoutDeploymentGroups" { + deploymentNode "Server 1" { + containerInstance softwareSystem.api + containerInstance softwareSystem.database + } + deploymentNode "Server 2" { + containerInstance softwareSystem.api + containerInstance softwareSystem.database + } + } + + deploymentEnvironment "WithDeploymentGroups" { + serviceInstance1 = deploymentGroup "Service Instance 1" + serviceInstance2 = deploymentGroup "Service Instance 2" + deploymentNode "Server 1" { + containerInstance softwareSystem.api serviceInstance1 + containerInstance softwareSystem.database serviceInstance1 + } + deploymentNode "Server 2" { + containerInstance softwareSystem.api serviceInstance2 + containerInstance softwareSystem.database serviceInstance2 + } + } + + deploymentEnvironment "WithDeploymentGroupsAgain" { + serviceInstance1 = deploymentGroup "Service Instance 1" + serviceInstance2 = deploymentGroup "Service Instance 2" + deploymentNode "Server 1" { + containerInstance softwareSystem.api serviceInstance1 + containerInstance softwareSystem.database serviceInstance1 + } + deploymentNode "Server 2" { + containerInstance softwareSystem.api serviceInstance2 + containerInstance softwareSystem.database serviceInstance2 + } + } + } + +} \ No newline at end of file From fab1600c6cd186df70c6da39dd260983fc964e0b Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 2 May 2025 15:49:17 +0100 Subject: [PATCH 643/717] . --- changelog.md | 2 +- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index 971f41c74..f523c14ee 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,6 @@ # Changelog -## v4.0.1 (unreleased) +## v4.1.0 (unreleased) - structurizr-client: Fixes https://github.com/structurizr/java/issues/413 (Cannot push to main branch, when branch feature is activated). - structurizr-dsl: Allows archetypes to be used via workspace extension. diff --git a/gradle.properties b/gradle.properties index 1f68eb29c..42ec5628d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,4 +5,4 @@ signing.secretKeyRingFile=/some/path ossrhUsername=username ossrhPassword=password -version=4.0.1 \ No newline at end of file +version=4.1.0 \ No newline at end of file From e328063b777d052c8682565dce25c395b594579b Mon Sep 17 00:00:00 2001 From: Simon Brown <1009874+simonbrowndotje@users.noreply.github.com> Date: Wed, 28 May 2025 10:45:10 +0100 Subject: [PATCH 644/717] Updated to reflect release. --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index f523c14ee..1d2028922 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,6 @@ # Changelog -## v4.1.0 (unreleased) +## v4.1.0 (28th May 2025) - structurizr-client: Fixes https://github.com/structurizr/java/issues/413 (Cannot push to main branch, when branch feature is activated). - structurizr-dsl: Allows archetypes to be used via workspace extension. From 1569be340f1d65be51f1b31cf2f4bb9eeb2b8adc Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 25 Jun 2025 09:09:57 +0100 Subject: [PATCH 645/717] Adds support for `iconPosition` on element styles (options are `Top`, `Bottom`, `Left`). --- changelog.md | 4 ++ gradle.properties | 2 +- .../com/structurizr/view/ElementStyle.java | 21 +++++++++++ .../com/structurizr/view/IconPosition.java | 9 +++++ .../dsl/ElementStyleDslContext.java | 1 + .../structurizr/dsl/ElementStyleParser.java | 30 +++++++++++++++ .../structurizr/dsl/StructurizrDslParser.java | 3 ++ .../structurizr/dsl/StructurizrDslTokens.java | 1 + .../dsl/ElementStyleParserTests.java | 37 +++++++++++++++++++ 9 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 structurizr-core/src/main/java/com/structurizr/view/IconPosition.java diff --git a/changelog.md b/changelog.md index 1d2028922..59110d172 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,9 @@ # Changelog +## v4.2.0 (unreleased) + +- structurizr-dsl: Adds support for `iconPosition` on element styles (options are `Top`, `Bottom`, `Left`). + ## v4.1.0 (28th May 2025) - structurizr-client: Fixes https://github.com/structurizr/java/issues/413 (Cannot push to main branch, when branch feature is activated). diff --git a/gradle.properties b/gradle.properties index 42ec5628d..c99ca35d7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,4 +5,4 @@ signing.secretKeyRingFile=/some/path ossrhUsername=username ossrhPassword=password -version=4.1.0 \ No newline at end of file +version=4.2.0 \ No newline at end of file diff --git a/structurizr-core/src/main/java/com/structurizr/view/ElementStyle.java b/structurizr-core/src/main/java/com/structurizr/view/ElementStyle.java index 70299a5fe..16a83c76c 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/ElementStyle.java +++ b/structurizr-core/src/main/java/com/structurizr/view/ElementStyle.java @@ -41,6 +41,9 @@ public final class ElementStyle extends AbstractStyle { @JsonInclude(value = JsonInclude.Include.NON_NULL) private String icon; + @JsonInclude(value = JsonInclude.Include.NON_NULL) + private IconPosition iconPosition; + @JsonInclude(value = JsonInclude.Include.NON_NULL) private Border border; @@ -290,6 +293,24 @@ public ElementStyle icon(String icon) { return this; } + /** + * Gets the icon position to use when rendering the element. + * + * @return an IconPosition, or null if not specified + */ + public IconPosition getIconPosition() { + return iconPosition; + } + + public void setIconPosition(IconPosition iconPosition) { + this.iconPosition = iconPosition; + } + + public ElementStyle iconPosition(IconPosition iconPosition) { + setIconPosition(iconPosition); + return this; + } + /** * Gets the border used when rendering the element. * diff --git a/structurizr-core/src/main/java/com/structurizr/view/IconPosition.java b/structurizr-core/src/main/java/com/structurizr/view/IconPosition.java new file mode 100644 index 000000000..38ee1ff93 --- /dev/null +++ b/structurizr-core/src/main/java/com/structurizr/view/IconPosition.java @@ -0,0 +1,9 @@ +package com.structurizr.view; + +public enum IconPosition { + + Top, + Bottom, + Left + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementStyleDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementStyleDslContext.java index 1ab73cbe3..f99834d07 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementStyleDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementStyleDslContext.java @@ -27,6 +27,7 @@ protected String[] getPermittedTokens() { return new String[] { StructurizrDslTokens.ELEMENT_STYLE_SHAPE_TOKEN, StructurizrDslTokens.ELEMENT_STYLE_ICON_TOKEN, + StructurizrDslTokens.ELEMENT_STYLE_ICON_POSITION_TOKEN, StructurizrDslTokens.ELEMENT_STYLE_WIDTH_TOKEN, StructurizrDslTokens.ELEMENT_STYLE_HEIGHT_TOKEN, StructurizrDslTokens.ELEMENT_STYLE_BACKGROUND_TOKEN, diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementStyleParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementStyleParser.java index 039abab6c..c27f8eb9f 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementStyleParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementStyleParser.java @@ -5,6 +5,7 @@ import com.structurizr.util.StringUtils; import com.structurizr.view.Border; import com.structurizr.view.ElementStyle; +import com.structurizr.view.IconPosition; import com.structurizr.view.Shape; import java.io.File; @@ -318,4 +319,33 @@ void parseIcon(ElementStyleDslContext context, Tokens tokens, boolean restricted } } + void parseIconPosition(ElementStyleDslContext context, Tokens tokens) { + Map iconPositions = new HashMap<>(); + String iconPositionsAsString = ""; + for (IconPosition iconPosition : IconPosition.values()) { + iconPositions.put(iconPosition.toString().toLowerCase(), iconPosition); + iconPositionsAsString += iconPosition; + iconPositionsAsString += "|"; + } + iconPositionsAsString = iconPositionsAsString.substring(0, iconPositionsAsString.length()-1); + + ElementStyle style = context.getStyle(); + + if (tokens.hasMoreThan(FIRST_PROPERTY_INDEX)) { + throw new RuntimeException("Too many tokens, expected: iconPosition <" + iconPositionsAsString + ">"); + } + + if (tokens.includes(FIRST_PROPERTY_INDEX)) { + String iconPosition = tokens.get(1).toLowerCase(); + + if (iconPositions.containsKey(iconPosition)) { + style.setIconPosition(iconPositions.get(iconPosition)); + } else { + throw new RuntimeException("The icon position \"" + iconPosition + "\" is not valid"); + } + } else { + throw new RuntimeException("Expected: iconPosition <" + iconPositionsAsString + ">"); + } + } + } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index 4664c0266..809a9e4d1 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -834,6 +834,9 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn } else if (ELEMENT_STYLE_ICON_TOKEN.equalsIgnoreCase(firstToken) && inContext(ElementStyleDslContext.class)) { new ElementStyleParser().parseIcon(getContext(ElementStyleDslContext.class), tokens, restricted); + } else if (ELEMENT_STYLE_ICON_POSITION_TOKEN.equalsIgnoreCase(firstToken) && inContext(ElementStyleDslContext.class)) { + new ElementStyleParser().parseIconPosition(getContext(ElementStyleDslContext.class), tokens); + } else if (RELATIONSHIP_STYLE_TOKEN.equalsIgnoreCase(firstToken) && inContext(StylesDslContext.class)) { RelationshipStyle relationshipStyle = new RelationshipStyleParser().parseRelationshipStyle(getContext(), tokens.withoutContextStartToken()); startContext(new RelationshipStyleDslContext(relationshipStyle)); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java index 9b295eff0..67bc67f56 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java @@ -71,6 +71,7 @@ class StructurizrDslTokens { static final String ELEMENT_STYLE_COLOUR_TOKEN = "colour"; static final String ELEMENT_STYLE_COLOR_TOKEN = "color"; static final String ELEMENT_STYLE_ICON_TOKEN = "icon"; + static final String ELEMENT_STYLE_ICON_POSITION_TOKEN = "iconPosition"; static final String ELEMENT_STYLE_OPACITY_TOKEN = "opacity"; static final String ELEMENT_STYLE_BORDER_TOKEN = "border"; static final String ELEMENT_STYLE_FONT_SIZE_TOKEN = "fontSize"; diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementStyleParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementStyleParserTests.java index 50086e8dc..a3519604e 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementStyleParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementStyleParserTests.java @@ -2,6 +2,7 @@ import com.structurizr.view.Border; import com.structurizr.view.ElementStyle; +import com.structurizr.view.IconPosition; import com.structurizr.view.Shape; import org.junit.jupiter.api.Test; @@ -543,4 +544,40 @@ void test_parseIcon_SetsTheIconFromAFile() { assertTrue(elementStyle.getIcon().startsWith("data:image/png;base64,")); } + @Test + void test_parseIconPosition_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parseIconPosition(elementStyleDslContext(), tokens("iconPosition", "top", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: iconPosition ", e.getMessage()); + } + } + + @Test + void test_parseIconPosition_ThrowsAnException_WhenTheShapeIsMissing() { + try { + parser.parseIconPosition(elementStyleDslContext(), tokens("iconPosition")); + fail(); + } catch (Exception e) { + assertEquals("Expected: iconPosition ", e.getMessage()); + } + } + + @Test + void test_parseIconPosition_ThrowsAnException_WhenTheShapeIsNotValid() { + try { + parser.parseIconPosition(elementStyleDslContext(), tokens("iconPosition", "right")); + fail(); + } catch (Exception e) { + assertEquals("The icon position \"right\" is not valid", e.getMessage()); + } + } + + @Test + void test_parseIconPosition_SetsTheIconPosition() { + parser.parseIconPosition(elementStyleDslContext(), tokens("iconPosition", "top")); + assertEquals(IconPosition.Top, elementStyle.getIconPosition()); + } + } \ No newline at end of file From 4df83011b480ed50933adb025201eda8023d0cc4 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 26 Jun 2025 15:20:26 +0100 Subject: [PATCH 646/717] Adds support for defining element and relationship styles for light and dark mode (re: #368). --- changelog.md | 1 + .../com/structurizr/view/AbstractStyle.java | 20 +++++++ .../com/structurizr/view/ColorScheme.java | 11 ++++ .../com/structurizr/view/ElementStyle.java | 5 ++ .../structurizr/view/RelationshipStyle.java | 5 ++ .../java/com/structurizr/view/Styles.java | 54 ++++++++++++++++--- .../com/structurizr/view/StylesTests.java | 44 ++++++++++++++- .../structurizr/dsl/ElementStyleParser.java | 11 ++-- .../dsl/RelationshipStyleParser.java | 10 ++-- .../structurizr/dsl/StructurizrDslParser.java | 10 +++- .../structurizr/dsl/StructurizrDslTokens.java | 2 + .../com/structurizr/dsl/StylesDslContext.java | 16 ++++++ .../java/com/structurizr/dsl/DslTests.java | 27 +++++++++- .../dsl/ElementStyleParserTests.java | 17 ++++-- .../dsl/RelationshipStyleParserTests.java | 17 ++++-- .../src/test/resources/dsl/color-schemes.dsl | 31 +++++++++++ .../src/test/resources/dsl/test.dsl | 21 ++++++++ 17 files changed, 267 insertions(+), 35 deletions(-) create mode 100644 structurizr-core/src/main/java/com/structurizr/view/ColorScheme.java create mode 100644 structurizr-dsl/src/test/resources/dsl/color-schemes.dsl diff --git a/changelog.md b/changelog.md index 59110d172..f0355de16 100644 --- a/changelog.md +++ b/changelog.md @@ -3,6 +3,7 @@ ## v4.2.0 (unreleased) - structurizr-dsl: Adds support for `iconPosition` on element styles (options are `Top`, `Bottom`, `Left`). +- structurizr-dsl: Adds support for defining element and relationship styles for light and dark mode. ## v4.1.0 (28th May 2025) diff --git a/structurizr-core/src/main/java/com/structurizr/view/AbstractStyle.java b/structurizr-core/src/main/java/com/structurizr/view/AbstractStyle.java index 2ad6bb291..39ad047be 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/AbstractStyle.java +++ b/structurizr-core/src/main/java/com/structurizr/view/AbstractStyle.java @@ -8,8 +8,28 @@ public abstract class AbstractStyle implements PropertyHolder { + private ColorScheme colorScheme = null; + private Map properties = new HashMap<>(); + /** + * Gets the color scheme of this style. + * + * @return a ColorScheme, or null if not specified (i.e. applies to light and dark). + */ + public ColorScheme getColorScheme() { + return colorScheme; + } + + /** + * Sets the color scheme of this style. + * + * @param colorScheme a ColorScheme, or null if not specified (i.e. applies to light and dark). + */ + void setColorScheme(ColorScheme colorScheme) { + this.colorScheme = colorScheme; + } + /** * Gets the collection of name-value property pairs associated with this workspace, as a Map. * diff --git a/structurizr-core/src/main/java/com/structurizr/view/ColorScheme.java b/structurizr-core/src/main/java/com/structurizr/view/ColorScheme.java new file mode 100644 index 000000000..4548594bc --- /dev/null +++ b/structurizr-core/src/main/java/com/structurizr/view/ColorScheme.java @@ -0,0 +1,11 @@ +package com.structurizr.view; + +/** + * Represents light or dark mode color schemes. + */ +public enum ColorScheme { + + Light, + Dark + +} \ No newline at end of file diff --git a/structurizr-core/src/main/java/com/structurizr/view/ElementStyle.java b/structurizr-core/src/main/java/com/structurizr/view/ElementStyle.java index 16a83c76c..90bd169c3 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/ElementStyle.java +++ b/structurizr-core/src/main/java/com/structurizr/view/ElementStyle.java @@ -63,6 +63,11 @@ public final class ElementStyle extends AbstractStyle { this.tag = tag; } + ElementStyle(String tag, ColorScheme colorScheme) { + this.tag = tag; + setColorScheme(colorScheme); + } + public ElementStyle(String tag, Integer width, Integer height, String background, String color, Integer fontSize) { this(tag, width, height, background, color, fontSize, null); } diff --git a/structurizr-core/src/main/java/com/structurizr/view/RelationshipStyle.java b/structurizr-core/src/main/java/com/structurizr/view/RelationshipStyle.java index d32e6ee30..3fbae753e 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/RelationshipStyle.java +++ b/structurizr-core/src/main/java/com/structurizr/view/RelationshipStyle.java @@ -54,6 +54,11 @@ public final class RelationshipStyle extends AbstractStyle { this.tag = tag; } + RelationshipStyle(String tag, ColorScheme colorScheme) { + this.tag = tag; + setColorScheme(colorScheme); + } + public String getTag() { return tag; } diff --git a/structurizr-core/src/main/java/com/structurizr/view/Styles.java b/structurizr-core/src/main/java/com/structurizr/view/Styles.java index 79780d94d..805ed188b 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/Styles.java +++ b/structurizr-core/src/main/java/com/structurizr/view/Styles.java @@ -29,8 +29,12 @@ public void add(ElementStyle elementStyle) { throw new IllegalArgumentException("A tag must be specified."); } - if (elements.stream().anyMatch(es -> es.getTag().equals(elementStyle.getTag()))) { - throw new IllegalArgumentException("An element style for the tag \"" + elementStyle.getTag() + "\" already exists."); + if (elements.stream().anyMatch(es -> es.getTag().equals(elementStyle.getTag()) && es.getColorScheme() == elementStyle.getColorScheme())) { + if (elementStyle.getColorScheme() == null) { + throw new IllegalArgumentException("An element style for the tag \"" + elementStyle.getTag() + "\" already exists."); + } else { + throw new IllegalArgumentException("An element style for the tag \"" + elementStyle.getTag() + "\" and color scheme " + elementStyle.getColorScheme() + " already exists."); + } } this.elements.add(elementStyle); @@ -38,7 +42,11 @@ public void add(ElementStyle elementStyle) { } public ElementStyle addElementStyle(String tag) { - ElementStyle elementStyle = new ElementStyle(tag); + return addElementStyle(tag, null); + } + + public ElementStyle addElementStyle(String tag, ColorScheme colorScheme) { + ElementStyle elementStyle = new ElementStyle(tag, colorScheme); add(elementStyle); return elementStyle; @@ -68,8 +76,12 @@ public void add(RelationshipStyle relationshipStyle) { throw new IllegalArgumentException("A tag must be specified."); } - if (relationships.stream().anyMatch(es -> es.getTag().equals(relationshipStyle.getTag()))) { - throw new IllegalArgumentException("A relationship style for the tag \"" + relationshipStyle.getTag() + "\" already exists."); + if (relationships.stream().anyMatch(rs -> rs.getTag().equals(relationshipStyle.getTag()) && rs.getColorScheme() == relationshipStyle.getColorScheme())) { + if (relationshipStyle.getColorScheme() == null) { + throw new IllegalArgumentException("A relationship style for the tag \"" + relationshipStyle.getTag() + "\" already exists."); + } else { + throw new IllegalArgumentException("A relationship style for the tag \"" + relationshipStyle.getTag() + "\" and color scheme " + relationshipStyle.getColorScheme() + " already exists."); + } } this.relationships.add(relationshipStyle); @@ -77,7 +89,11 @@ public void add(RelationshipStyle relationshipStyle) { } public RelationshipStyle addRelationshipStyle(String tag) { - RelationshipStyle relationshipStyle = new RelationshipStyle(tag); + return addRelationshipStyle(tag, null); + } + + public RelationshipStyle addRelationshipStyle(String tag, ColorScheme colorScheme) { + RelationshipStyle relationshipStyle = new RelationshipStyle(tag, colorScheme); add(relationshipStyle); return relationshipStyle; @@ -90,11 +106,22 @@ public RelationshipStyle addRelationshipStyle(String tag) { * @return an ElementStyle instance, or null if no element style has been defined in this workspace */ public ElementStyle getElementStyle(String tag) { + return getElementStyle(tag, null); + } + + /** + * Gets the element style that has been defined (in this workspace) for the given tag and color scheme. + * + * @param tag the tag (a String) + * @param colorScheme the ColorScheme (can be null) + * @return an ElementStyle instance, or null if no element style has been defined in this workspace + */ + public ElementStyle getElementStyle(String tag, ColorScheme colorScheme) { if (StringUtils.isNullOrEmpty(tag)) { throw new IllegalArgumentException("A tag must be specified."); } - return elements.stream().filter(es -> es.getTag().equals(tag)).findFirst().orElse(null); + return elements.stream().filter(es -> es.getTag().equals(tag) && es.getColorScheme() == colorScheme).findFirst().orElse(null); } /** @@ -141,11 +168,22 @@ public ElementStyle findElementStyle(String tag) { * @return an RelationshipStyle instance, or null if no relationship style has been defined in this workspace */ public RelationshipStyle getRelationshipStyle(String tag) { + return getRelationshipStyle(tag, null); + } + + /** + * Gets the relationship style that has been defined (in this workspace) for the given tag and color scheme. + * + * @param tag the tag (a String) + * @param colorScheme the ColorScheme (can be null) + * @return an RelationshipStyle instance, or null if no relationship style has been defined in this workspace + */ + public RelationshipStyle getRelationshipStyle(String tag, ColorScheme colorScheme) { if (StringUtils.isNullOrEmpty(tag)) { throw new IllegalArgumentException("A tag must be specified."); } - return relationships.stream().filter(rs -> rs.getTag().equals(tag)).findFirst().orElse(null); + return relationships.stream().filter(rs -> rs.getTag().equals(tag) && rs.getColorScheme() == colorScheme).findFirst().orElse(null); } /** diff --git a/structurizr-core/src/test/java/com/structurizr/view/StylesTests.java b/structurizr-core/src/test/java/com/structurizr/view/StylesTests.java index c9b6aa1fe..caf878673 100644 --- a/structurizr-core/src/test/java/com/structurizr/view/StylesTests.java +++ b/structurizr-core/src/test/java/com/structurizr/view/StylesTests.java @@ -10,7 +10,7 @@ public class StylesTests extends AbstractWorkspaceTestBase { - private Styles styles = new Styles(); + private final Styles styles = new Styles(); @Test void findElementStyle_ReturnsTheDefaultStyle_WhenPassedNull() { @@ -263,6 +263,27 @@ void addElementStyleByTag_ThrowsAnException_WhenAStyleWithTheSameTagExistsAlread } } + @Test + void addElementStyleByTag_ThrowsAnException_WhenAStyleWithTheSameTagAndColorSchemeExistsAlready() { + try { + styles.addElementStyle(Tags.SOFTWARE_SYSTEM, ColorScheme.Dark); + styles.addElementStyle(Tags.SOFTWARE_SYSTEM, ColorScheme.Dark); + + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("An element style for the tag \"Software System\" and color scheme Dark already exists.", iae.getMessage()); + } + } + + @Test + void addElementStyleByTag_WithDifferentColorSchemes() { + styles.addElementStyle(Tags.SOFTWARE_SYSTEM); + styles.addElementStyle(Tags.SOFTWARE_SYSTEM, ColorScheme.Dark); + styles.addElementStyle(Tags.SOFTWARE_SYSTEM, ColorScheme.Light); + + assertEquals(3, styles.getElements().size()); + } + @Test void addElementStyle_ThrowsAnException_WhenAStyleWithTheSameTagExistsAlready() { try { @@ -311,6 +332,27 @@ void addRelationshipStyleByTag_ThrowsAnException_WhenAStyleWithTheSameTagExistsA } } + @Test + void addRelationshipStyleByTag_ThrowsAnException_WhenAStyleWithTheSameTagAndColorSchemeExistsAlready() { + try { + styles.addRelationshipStyle(Tags.RELATIONSHIP, ColorScheme.Light); + styles.addRelationshipStyle(Tags.RELATIONSHIP, ColorScheme.Light); + + fail(); + } catch (IllegalArgumentException iae) { + assertEquals("A relationship style for the tag \"Relationship\" and color scheme Light already exists.", iae.getMessage()); + } + } + + @Test + void addRelationshipStyleByTag_WithDifferentColorSchemes() { + styles.addRelationshipStyle(Tags.RELATIONSHIP); + styles.addRelationshipStyle(Tags.RELATIONSHIP, ColorScheme.Dark); + styles.addRelationshipStyle(Tags.RELATIONSHIP, ColorScheme.Light); + + assertEquals(3, styles.getRelationships().size()); + } + @Test void addRelationshipStyle_ThrowsAnException_WhenAStyleWithTheSameTagExistsAlready() { try { diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementStyleParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementStyleParser.java index c27f8eb9f..dd76e5804 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementStyleParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementStyleParser.java @@ -3,10 +3,7 @@ import com.structurizr.Workspace; import com.structurizr.util.ImageUtils; import com.structurizr.util.StringUtils; -import com.structurizr.view.Border; -import com.structurizr.view.ElementStyle; -import com.structurizr.view.IconPosition; -import com.structurizr.view.Shape; +import com.structurizr.view.*; import java.io.File; import java.io.IOException; @@ -17,7 +14,7 @@ final class ElementStyleParser extends AbstractParser { private static final int FIRST_PROPERTY_INDEX = 1; - ElementStyle parseElementStyle(DslContext context, Tokens tokens) { + ElementStyle parseElementStyle(StylesDslContext context, Tokens tokens) { if (tokens.hasMoreThan(FIRST_PROPERTY_INDEX)) { throw new RuntimeException("Too many tokens, expected: element {"); } else if (tokens.includes(FIRST_PROPERTY_INDEX)) { @@ -28,9 +25,9 @@ ElementStyle parseElementStyle(DslContext context, Tokens tokens) { } Workspace workspace = context.getWorkspace(); - ElementStyle elementStyle = workspace.getViews().getConfiguration().getStyles().getElementStyle(tag); + ElementStyle elementStyle = workspace.getViews().getConfiguration().getStyles().getElementStyle(tag, context.getColorScheme()); if (elementStyle == null) { - elementStyle = workspace.getViews().getConfiguration().getStyles().addElementStyle(tag); + elementStyle = workspace.getViews().getConfiguration().getStyles().addElementStyle(tag, context.getColorScheme()); } return elementStyle; diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipStyleParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipStyleParser.java index 6c260c3bf..dd8d28a72 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipStyleParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/RelationshipStyleParser.java @@ -2,9 +2,7 @@ import com.structurizr.Workspace; import com.structurizr.util.StringUtils; -import com.structurizr.view.LineStyle; -import com.structurizr.view.RelationshipStyle; -import com.structurizr.view.Routing; +import com.structurizr.view.*; import java.util.HashMap; import java.util.Map; @@ -13,7 +11,7 @@ final class RelationshipStyleParser extends AbstractParser { private static final int FIRST_PROPERTY_INDEX = 1; - RelationshipStyle parseRelationshipStyle(DslContext context, Tokens tokens) { + RelationshipStyle parseRelationshipStyle(StylesDslContext context, Tokens tokens) { if (tokens.hasMoreThan(FIRST_PROPERTY_INDEX)) { throw new RuntimeException("Too many tokens, expected: relationship {"); } @@ -26,9 +24,9 @@ RelationshipStyle parseRelationshipStyle(DslContext context, Tokens tokens) { } Workspace workspace = context.getWorkspace(); - RelationshipStyle relationshipStyle = workspace.getViews().getConfiguration().getStyles().getRelationshipStyle(tag); + RelationshipStyle relationshipStyle = workspace.getViews().getConfiguration().getStyles().getRelationshipStyle(tag, context.getColorScheme()); if (relationshipStyle == null) { - relationshipStyle = workspace.getViews().getConfiguration().getStyles().addRelationshipStyle(tag); + relationshipStyle = workspace.getViews().getConfiguration().getStyles().addRelationshipStyle(tag, context.getColorScheme()); } return relationshipStyle; diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index 809a9e4d1..636516db7 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -791,8 +791,14 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn } else if (STYLES_TOKEN.equalsIgnoreCase(firstToken) && inContext(ViewsDslContext.class)) { startContext(new StylesDslContext()); + } else if (LIGHT_COLOR_SCHEME_TOKEN.equalsIgnoreCase(firstToken) && inContext(StylesDslContext.class)) { + startContext(new StylesDslContext(ColorScheme.Light)); + + } else if (DARK_COLOR_SCHEME_TOKEN.equalsIgnoreCase(firstToken) && inContext(StylesDslContext.class)) { + startContext(new StylesDslContext(ColorScheme.Dark)); + } else if (ELEMENT_STYLE_TOKEN.equalsIgnoreCase(firstToken) && inContext(StylesDslContext.class)) { - ElementStyle elementStyle = new ElementStyleParser().parseElementStyle(getContext(), tokens.withoutContextStartToken()); + ElementStyle elementStyle = new ElementStyleParser().parseElementStyle(getContext(StylesDslContext.class), tokens.withoutContextStartToken()); startContext(new ElementStyleDslContext(elementStyle, dslFile)); } else if (ELEMENT_STYLE_BACKGROUND_TOKEN.equalsIgnoreCase(firstToken) && inContext(ElementStyleDslContext.class)) { @@ -838,7 +844,7 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn new ElementStyleParser().parseIconPosition(getContext(ElementStyleDslContext.class), tokens); } else if (RELATIONSHIP_STYLE_TOKEN.equalsIgnoreCase(firstToken) && inContext(StylesDslContext.class)) { - RelationshipStyle relationshipStyle = new RelationshipStyleParser().parseRelationshipStyle(getContext(), tokens.withoutContextStartToken()); + RelationshipStyle relationshipStyle = new RelationshipStyleParser().parseRelationshipStyle(getContext(StylesDslContext.class), tokens.withoutContextStartToken()); startContext(new RelationshipStyleDslContext(relationshipStyle)); } else if (RELATIONSHIP_STYLE_THICKNESS_TOKEN.equalsIgnoreCase(firstToken) && inContext(RelationshipStyleDslContext.class)) { diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java index 67bc67f56..7b38a7c11 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java @@ -60,6 +60,8 @@ class StructurizrDslTokens { static final String KROKI_TOKEN = "kroki"; static final String IMAGE_TOKEN = "image"; static final String STYLES_TOKEN = "styles"; + static final String LIGHT_COLOR_SCHEME_TOKEN = "light"; + static final String DARK_COLOR_SCHEME_TOKEN = "dark"; static final String BRANDING_TOKEN = "branding"; static final String BRANDING_LOGO_TOKEN = "logo"; static final String BRANDING_FONT_TOKEN = "font"; diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StylesDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StylesDslContext.java index a27234faf..a374a54df 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StylesDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StylesDslContext.java @@ -1,7 +1,23 @@ package com.structurizr.dsl; +import com.structurizr.view.ColorScheme; + final class StylesDslContext extends DslContext { + private final ColorScheme colorScheme; + + StylesDslContext() { + colorScheme = null; + } + + StylesDslContext(ColorScheme colorScheme) { + this.colorScheme = colorScheme; + } + + ColorScheme getColorScheme() { + return colorScheme; + } + @Override protected String[] getPermittedTokens() { return new String[] { diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index 8761713fd..a613b53c7 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -4,7 +4,6 @@ import com.structurizr.documentation.Section; import com.structurizr.model.*; import com.structurizr.util.StringUtils; -import com.structurizr.util.WorkspaceUtils; import com.structurizr.view.*; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -1712,4 +1711,30 @@ void test_deploymentGroups_Hierarchical() throws Exception { assertFalse(apiInstance2.hasEfferentRelationshipWith(dbInstance1)); } + @Test + void test_colorSchemes() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/color-schemes.dsl")); + + Workspace workspace = parser.getWorkspace(); + + ElementStyle elementStyle = workspace.getViews().getConfiguration().getStyles().getElementStyle("Element"); + assertEquals(Shape.RoundedBox, elementStyle.getShape()); + + elementStyle = workspace.getViews().getConfiguration().getStyles().getElementStyle("Element", ColorScheme.Light); + assertEquals("#000000", elementStyle.getColor()); + + elementStyle = workspace.getViews().getConfiguration().getStyles().getElementStyle("Element", ColorScheme.Dark); + assertEquals("#ffffff", elementStyle.getColor()); + + RelationshipStyle relationshipStyle = workspace.getViews().getConfiguration().getStyles().getRelationshipStyle("Relationship"); + assertEquals(LineStyle.Solid, relationshipStyle.getStyle()); + + relationshipStyle = workspace.getViews().getConfiguration().getStyles().getRelationshipStyle("Relationship", ColorScheme.Light); + assertEquals("#000000", relationshipStyle.getColor()); + + relationshipStyle = workspace.getViews().getConfiguration().getStyles().getRelationshipStyle("Relationship", ColorScheme.Dark); + assertEquals("#ffffff", relationshipStyle.getColor()); + } + } \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementStyleParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementStyleParserTests.java index a3519604e..c876abd0f 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementStyleParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementStyleParserTests.java @@ -23,10 +23,17 @@ private ElementStyleDslContext elementStyleDslContext() { return context; } + private StylesDslContext stylesDslContext() { + StylesDslContext context = new StylesDslContext(); + context.setWorkspace(workspace); + + return context; + } + @Test void test_parseElementStyle_ThrowsAnException_WhenThereAreTooManyTokens() { try { - parser.parseElementStyle(context(), tokens("element", "tag", "extra")); + parser.parseElementStyle(stylesDslContext(), tokens("element", "tag", "extra")); fail(); } catch (Exception e) { assertEquals("Too many tokens, expected: element {", e.getMessage()); @@ -36,7 +43,7 @@ void test_parseElementStyle_ThrowsAnException_WhenThereAreTooManyTokens() { @Test void test_parseElementStyle_ThrowsAnException_WhenTheTagIsMissing() { try { - parser.parseElementStyle(context(), tokens("element")); + parser.parseElementStyle(stylesDslContext(), tokens("element")); fail(); } catch (Exception e) { assertEquals("Expected: element {", e.getMessage()); @@ -46,7 +53,7 @@ void test_parseElementStyle_ThrowsAnException_WhenTheTagIsMissing() { @Test void test_parseElementStyle_ThrowsAnException_WhenTheTagIsEmpty() { try { - parser.parseElementStyle(context(), tokens("element", "")); + parser.parseElementStyle(stylesDslContext(), tokens("element", "")); fail(); } catch (Exception e) { assertEquals("A tag must be specified", e.getMessage()); @@ -55,7 +62,7 @@ void test_parseElementStyle_ThrowsAnException_WhenTheTagIsEmpty() { @Test void test_parseElementStyle_CreatesAnElementStyle() { - parser.parseElementStyle(context(), tokens("element", "Element")); + parser.parseElementStyle(stylesDslContext(), tokens("element", "Element")); ElementStyle style = workspace.getViews().getConfiguration().getStyles().getElements().stream().filter(es -> "Element".equals(es.getTag())).findFirst().get(); assertNotNull(style); @@ -64,7 +71,7 @@ void test_parseElementStyle_CreatesAnElementStyle() { @Test void test_parseElementStyle_FindsAnExistingElementStyle() { ElementStyle style = workspace.getViews().getConfiguration().getStyles().addElementStyle("Tag"); - assertSame(style, parser.parseElementStyle(context(), tokens("element", "Tag"))); + assertSame(style, parser.parseElementStyle(stylesDslContext(), tokens("element", "Tag"))); } @Test diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/RelationshipStyleParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/RelationshipStyleParserTests.java index 8736c5acb..645d747b2 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/RelationshipStyleParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/RelationshipStyleParserTests.java @@ -21,10 +21,17 @@ private RelationshipStyleDslContext relationshipStyleDslContext() { return context; } + private StylesDslContext stylesDslContext() { + StylesDslContext context = new StylesDslContext(); + context.setWorkspace(workspace); + + return context; + } + @Test void test_parseRelationshipStyle_ThrowsAnException_WhenThereAreTooManyTokens() { try { - parser.parseRelationshipStyle(context(), tokens("relationship", "tag", "extra")); + parser.parseRelationshipStyle(stylesDslContext(), tokens("relationship", "tag", "extra")); fail(); } catch (Exception e) { assertEquals("Too many tokens, expected: relationship {", e.getMessage()); @@ -34,7 +41,7 @@ void test_parseRelationshipStyle_ThrowsAnException_WhenThereAreTooManyTokens() { @Test void test_parseRelationshipStyle_ThrowsAnException_WhenTheTagIsMissing() { try { - parser.parseRelationshipStyle(context(), tokens("relationship")); + parser.parseRelationshipStyle(stylesDslContext(), tokens("relationship")); fail(); } catch (Exception e) { assertEquals("Expected: relationship {", e.getMessage()); @@ -44,7 +51,7 @@ void test_parseRelationshipStyle_ThrowsAnException_WhenTheTagIsMissing() { @Test void test_parseRelationshipStyle_ThrowsAnException_WhenTheTagIsEmpty() { try { - parser.parseRelationshipStyle(context(), tokens("relationship", "")); + parser.parseRelationshipStyle(stylesDslContext(), tokens("relationship", "")); fail(); } catch (Exception e) { assertEquals("A tag must be specified", e.getMessage()); @@ -53,7 +60,7 @@ void test_parseRelationshipStyle_ThrowsAnException_WhenTheTagIsEmpty() { @Test void test_parseRelationshipStyle_CreatesAnRelationshipStyle() { - parser.parseRelationshipStyle(context(), tokens("relationship", "Relationship")); + parser.parseRelationshipStyle(stylesDslContext(), tokens("relationship", "Relationship")); RelationshipStyle style = workspace.getViews().getConfiguration().getStyles().getRelationships().stream().filter(es -> "Relationship".equals(es.getTag())).findFirst().get(); assertNotNull(style); @@ -62,7 +69,7 @@ void test_parseRelationshipStyle_CreatesAnRelationshipStyle() { @Test void test_parseRelationshipStyle_FindsAnExistingRelationshipStyle() { RelationshipStyle style = workspace.getViews().getConfiguration().getStyles().addRelationshipStyle("Tag"); - assertSame(style, parser.parseRelationshipStyle(context(), tokens("relationship", "Tag"))); + assertSame(style, parser.parseRelationshipStyle(stylesDslContext(), tokens("relationship", "Tag"))); } @Test diff --git a/structurizr-dsl/src/test/resources/dsl/color-schemes.dsl b/structurizr-dsl/src/test/resources/dsl/color-schemes.dsl new file mode 100644 index 000000000..aeeb22134 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/color-schemes.dsl @@ -0,0 +1,31 @@ +workspace { + + views { + styles { + element "Element" { + shape roundedbox + } + relationship "Relationship" { + style solid + } + + light { + element "Element" { + colour #000000 + } + relationship "Relationship" { + colour #000000 + } + } + + dark { + element "Element" { + colour #ffffff + } + relationship "Relationship" { + colour #ffffff + } + } + } + } +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/test.dsl b/structurizr-dsl/src/test/resources/dsl/test.dsl index c6150affd..286853104 100644 --- a/structurizr-dsl/src/test/resources/dsl/test.dsl +++ b/structurizr-dsl/src/test/resources/dsl/test.dsl @@ -308,6 +308,7 @@ workspace "Name" "Description" { element "Element" { shape roundedbox icon logo.png + iconPosition left width 450 height 300 background #ffffff @@ -339,6 +340,26 @@ workspace "Name" "Description" { } } + light { + element "Element" { + background #ffffff + } + + relationship "Relationship" { + color #777777 + } + } + + dark { + element "Element" { + background #000000 + } + + relationship "Relationship" { + color #777777 + } + } + theme https://example.com/theme1 themes https://example.com/theme2 https://example.com/theme3 } From 48ed19c58cebac99366f9710a59be8b42ce6ad9c Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 27 Jun 2025 14:03:10 +0100 Subject: [PATCH 647/717] Adds a `Bucket` shape. --- changelog.md | 1 + .../src/main/java/com/structurizr/view/Shape.java | 1 + .../java/com/structurizr/dsl/ElementStyleParserTests.java | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index f0355de16..e22ef0c29 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,7 @@ - structurizr-dsl: Adds support for `iconPosition` on element styles (options are `Top`, `Bottom`, `Left`). - structurizr-dsl: Adds support for defining element and relationship styles for light and dark mode. +- structurizr-dsl: Adds a `Bucket` shape. ## v4.1.0 (28th May 2025) diff --git a/structurizr-core/src/main/java/com/structurizr/view/Shape.java b/structurizr-core/src/main/java/com/structurizr/view/Shape.java index 33232c99f..4fcb6f8b3 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/Shape.java +++ b/structurizr-core/src/main/java/com/structurizr/view/Shape.java @@ -9,6 +9,7 @@ public enum Shape { Hexagon, Diamond, Cylinder, + Bucket, Pipe, Person, Robot, diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementStyleParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementStyleParserTests.java index c876abd0f..294ad0b6e 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementStyleParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementStyleParserTests.java @@ -80,7 +80,7 @@ void test_parseShape_ThrowsAnException_WhenThereAreTooManyTokens() { parser.parseShape(elementStyleDslContext(), tokens("shape", "shape", "extra")); fail(); } catch (Exception e) { - assertEquals("Too many tokens, expected: shape ", e.getMessage()); + assertEquals("Too many tokens, expected: shape ", e.getMessage()); } } @@ -90,7 +90,7 @@ void test_parseShape_ThrowsAnException_WhenTheShapeIsMissing() { parser.parseShape(elementStyleDslContext(), tokens("shape")); fail(); } catch (Exception e) { - assertEquals("Expected: shape ", e.getMessage()); + assertEquals("Expected: shape ", e.getMessage()); } } From 90fd0905a14f91e340736a79aa2fc726b20477cf Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 27 Jul 2025 22:49:29 +0100 Subject: [PATCH 648/717] Adds a "Shell" shape. --- changelog.md | 1 + .../src/main/java/com/structurizr/view/Shape.java | 1 + .../java/com/structurizr/dsl/ElementStyleParserTests.java | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index e22ef0c29..4c7b5a7d8 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,7 @@ - structurizr-dsl: Adds support for `iconPosition` on element styles (options are `Top`, `Bottom`, `Left`). - structurizr-dsl: Adds support for defining element and relationship styles for light and dark mode. - structurizr-dsl: Adds a `Bucket` shape. +- structurizr-dsl: Adds a `Shell` shape. ## v4.1.0 (28th May 2025) diff --git a/structurizr-core/src/main/java/com/structurizr/view/Shape.java b/structurizr-core/src/main/java/com/structurizr/view/Shape.java index 4fcb6f8b3..2bddbd52d 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/Shape.java +++ b/structurizr-core/src/main/java/com/structurizr/view/Shape.java @@ -16,6 +16,7 @@ public enum Shape { Folder, WebBrowser, Window, + Shell, MobileDevicePortrait, MobileDeviceLandscape, Component diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementStyleParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementStyleParserTests.java index 294ad0b6e..bc25ff100 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementStyleParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementStyleParserTests.java @@ -80,7 +80,7 @@ void test_parseShape_ThrowsAnException_WhenThereAreTooManyTokens() { parser.parseShape(elementStyleDslContext(), tokens("shape", "shape", "extra")); fail(); } catch (Exception e) { - assertEquals("Too many tokens, expected: shape ", e.getMessage()); + assertEquals("Too many tokens, expected: shape ", e.getMessage()); } } @@ -90,7 +90,7 @@ void test_parseShape_ThrowsAnException_WhenTheShapeIsMissing() { parser.parseShape(elementStyleDslContext(), tokens("shape")); fail(); } catch (Exception e) { - assertEquals("Expected: shape ", e.getMessage()); + assertEquals("Expected: shape ", e.getMessage()); } } From 7eacd41d9643c9064275f39a11dab03818f2a4d8 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 29 Jul 2025 10:03:47 +0100 Subject: [PATCH 649/717] Adds a "Terminal" shape. --- changelog.md | 1 + .../src/main/java/com/structurizr/view/Shape.java | 1 + .../java/com/structurizr/dsl/ElementStyleParserTests.java | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index 4c7b5a7d8..6748a732e 100644 --- a/changelog.md +++ b/changelog.md @@ -6,6 +6,7 @@ - structurizr-dsl: Adds support for defining element and relationship styles for light and dark mode. - structurizr-dsl: Adds a `Bucket` shape. - structurizr-dsl: Adds a `Shell` shape. +- structurizr-dsl: Adds a `Terminal` shape. ## v4.1.0 (28th May 2025) diff --git a/structurizr-core/src/main/java/com/structurizr/view/Shape.java b/structurizr-core/src/main/java/com/structurizr/view/Shape.java index 2bddbd52d..40b2c1b34 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/Shape.java +++ b/structurizr-core/src/main/java/com/structurizr/view/Shape.java @@ -16,6 +16,7 @@ public enum Shape { Folder, WebBrowser, Window, + Terminal, Shell, MobileDevicePortrait, MobileDeviceLandscape, diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementStyleParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementStyleParserTests.java index bc25ff100..83e72cb77 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementStyleParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementStyleParserTests.java @@ -80,7 +80,7 @@ void test_parseShape_ThrowsAnException_WhenThereAreTooManyTokens() { parser.parseShape(elementStyleDslContext(), tokens("shape", "shape", "extra")); fail(); } catch (Exception e) { - assertEquals("Too many tokens, expected: shape ", e.getMessage()); + assertEquals("Too many tokens, expected: shape ", e.getMessage()); } } @@ -90,7 +90,7 @@ void test_parseShape_ThrowsAnException_WhenTheShapeIsMissing() { parser.parseShape(elementStyleDslContext(), tokens("shape")); fail(); } catch (Exception e) { - assertEquals("Expected: shape ", e.getMessage()); + assertEquals("Expected: shape ", e.getMessage()); } } From 2546f308c6470db23045682bd01c822c23285f70 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 1 Aug 2025 12:22:33 +0100 Subject: [PATCH 650/717] Adds an 'instanceOf' keyword (an alternative for `softwareSystemInstance` and `containerInstance`). --- changelog.md | 1 + .../dsl/DeploymentNodeDslContext.java | 1 + .../com/structurizr/dsl/InstanceOfParser.java | 40 ++++++ .../structurizr/dsl/StructurizrDslParser.java | 15 +++ .../structurizr/dsl/StructurizrDslTokens.java | 1 + .../dsl/ContainerInstanceParserTests.java | 2 +- .../dsl/InstanceOfParserTests.java | 115 ++++++++++++++++++ .../SoftwareSystemInstanceParserTests.java | 2 +- .../src/test/resources/dsl/test.dsl | 24 ++++ 9 files changed, 199 insertions(+), 2 deletions(-) create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/InstanceOfParser.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/InstanceOfParserTests.java diff --git a/changelog.md b/changelog.md index 6748a732e..6f3d8f8a2 100644 --- a/changelog.md +++ b/changelog.md @@ -7,6 +7,7 @@ - structurizr-dsl: Adds a `Bucket` shape. - structurizr-dsl: Adds a `Shell` shape. - structurizr-dsl: Adds a `Terminal` shape. +- structurizr-dsl: Adds an 'instanceOf' keyword (an alternative for `softwareSystemInstance` and `containerInstance`). ## v4.1.0 (28th May 2025) diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeDslContext.java index 0cfecda3d..a347f75c7 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentNodeDslContext.java @@ -38,6 +38,7 @@ protected String[] getPermittedTokens() { StructurizrDslTokens.GROUP_TOKEN, StructurizrDslTokens.DEPLOYMENT_NODE_TOKEN, StructurizrDslTokens.INFRASTRUCTURE_NODE_TOKEN, + StructurizrDslTokens.INSTANCE_OF_TOKEN, StructurizrDslTokens.SOFTWARE_SYSTEM_INSTANCE_TOKEN, StructurizrDslTokens.CONTAINER_INSTANCE_TOKEN, StructurizrDslTokens.RELATIONSHIP_TOKEN, diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/InstanceOfParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/InstanceOfParser.java new file mode 100644 index 000000000..2a0d4264c --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/InstanceOfParser.java @@ -0,0 +1,40 @@ +package com.structurizr.dsl; + +import com.structurizr.model.*; + +final class InstanceOfParser { + + private static final String GRAMMAR = "instanceOf [deploymentGroups] [tags]"; + + private static final int IDENTIFIER_INDEX = 1; + private static final int TAGS_INDEX = 3; + + StaticStructureElementInstance parse(DeploymentNodeDslContext context, Tokens tokens) { + // instanceOf [tags] + // instanceOf [deploymentGroup] [tags] + + if (tokens.hasMoreThan(TAGS_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } + + if (!tokens.includes(IDENTIFIER_INDEX)) { + throw new RuntimeException("Expected: " + GRAMMAR); + } + + String elementIdentifier = tokens.get(IDENTIFIER_INDEX); + + Element element = context.getElement(elementIdentifier); + if (element == null) { + throw new RuntimeException("The element \"" + elementIdentifier + "\" does not exist"); + } + + if (element instanceof SoftwareSystem) { + return new SoftwareSystemInstanceParser().parse(context, tokens); + } else if (element instanceof Container) { + return new ContainerInstanceParser().parse(context, tokens); + } else { + throw new RuntimeException("The element \"" + elementIdentifier + "\" must be a software system or a container"); + } + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index 636516db7..b68d40cee 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -916,6 +916,21 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn registerIdentifier(identifier, infrastructureNode); + } else if (INSTANCE_OF_TOKEN.equalsIgnoreCase(firstToken) && inContext(DeploymentNodeDslContext.class)) { + StaticStructureElementInstance instance = new InstanceOfParser().parse(getContext(DeploymentNodeDslContext.class), tokens.withoutContextStartToken()); + + if (instance instanceof SoftwareSystemInstance) { + if (shouldStartContext(tokens)) { + startContext(new SoftwareSystemInstanceDslContext((SoftwareSystemInstance)instance)); + } + } else if (instance instanceof ContainerInstance) { + if (shouldStartContext(tokens)) { + startContext(new ContainerInstanceDslContext((ContainerInstance)instance)); + } + } + + registerIdentifier(identifier, instance); + } else if (SOFTWARE_SYSTEM_INSTANCE_TOKEN.equalsIgnoreCase(firstToken) && inContext(DeploymentNodeDslContext.class)) { SoftwareSystemInstance softwareSystemInstance = new SoftwareSystemInstanceParser().parse(getContext(DeploymentNodeDslContext.class), tokens.withoutContextStartToken()); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java index 7b38a7c11..59f33b275 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java @@ -35,6 +35,7 @@ class StructurizrDslTokens { static final String DEPLOYMENT_GROUP_TOKEN = "deploymentGroup"; static final String DEPLOYMENT_NODE_TOKEN = "deploymentNode"; static final String INFRASTRUCTURE_NODE_TOKEN = "infrastructureNode"; + static final String INSTANCE_OF_TOKEN = "instanceOf"; static final String SOFTWARE_SYSTEM_INSTANCE_TOKEN = "softwareSystemInstance"; static final String CONTAINER_INSTANCE_TOKEN = "containerInstance"; static final String HEALTH_CHECK_TOKEN = "healthCheck"; diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ContainerInstanceParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ContainerInstanceParserTests.java index ba62530c0..98dde6ece 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ContainerInstanceParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ContainerInstanceParserTests.java @@ -7,7 +7,7 @@ class ContainerInstanceParserTests extends AbstractTests { - private ContainerInstanceParser parser = new ContainerInstanceParser(); + private final ContainerInstanceParser parser = new ContainerInstanceParser(); @Test void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/InstanceOfParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/InstanceOfParserTests.java new file mode 100644 index 000000000..494e90fff --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/InstanceOfParserTests.java @@ -0,0 +1,115 @@ +package com.structurizr.dsl; + +import com.structurizr.model.*; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class InstanceOfParserTests extends AbstractTests { + + private final InstanceOfParser parser = new InstanceOfParser(); + + @Test + void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parse(new DeploymentNodeDslContext(null), tokens("instanceOf", "identifier", "deploymentGroups", "tags", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: instanceOf [deploymentGroups] [tags]", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheIdentifierIsNotSpecified() { + try { + parser.parse(new DeploymentNodeDslContext(null), tokens("instanceOf")); + fail(); + } catch (Exception e) { + assertEquals("Expected: instanceOf [deploymentGroups] [tags]", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheElementDoesNotExist() { + try { + parser.parse(new DeploymentNodeDslContext(null), tokens("instanceOf", "softwareSystem")); + fail(); + } catch (Exception e) { + assertEquals("The element \"softwareSystem\" does not exist", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheElementIsNotASoftwareSystem() { + DeploymentNodeDslContext context = new DeploymentNodeDslContext(null); + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("softwaresystem", model.addPerson("Name", "Description")); + context.setIdentifierRegister(elements); + + try { + parser.parse(context, tokens("instanceOf", "softwareSystem")); + fail(); + } catch (Exception e) { + assertEquals("The element \"softwareSystem\" must be a software system or a container", e.getMessage()); + } + } + + @Test + void test_parse_CreatesASoftwareSystemInstance() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + DeploymentNode deploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + DeploymentNodeDslContext context = new DeploymentNodeDslContext(deploymentNode); + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("softwaresystem", softwareSystem); + context.setIdentifierRegister(elements); + + parser.parse(context, tokens("instanceOf", "softwareSystem")); + + assertEquals(3, model.getElements().size()); + assertEquals(1, deploymentNode.getSoftwareSystemInstances().size()); + SoftwareSystemInstance softwareSystemInstance = deploymentNode.getSoftwareSystemInstances().iterator().next(); + assertSame(softwareSystem, softwareSystemInstance.getSoftwareSystem()); + assertEquals("Software System Instance", softwareSystemInstance.getTags()); + assertEquals("Live", softwareSystemInstance.getEnvironment()); + assertEquals(1, softwareSystemInstance.getDeploymentGroups().size()); + assertEquals("Default", softwareSystemInstance.getDeploymentGroups().iterator().next()); + } + + @Test + void test_parse_ThrowsAnException_WhenTheElementIsNotAContainer() { + DeploymentNodeDslContext context = new DeploymentNodeDslContext(null); + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("container", model.addPerson("Name", "Description")); + context.setIdentifierRegister(elements); + + try { + parser.parse(context, tokens("instanceOf", "container")); + fail(); + } catch (Exception e) { + assertEquals("The element \"container\" must be a software system or a container", e.getMessage()); + } + } + + @Test + void test_parse_CreatesAContainerInstance() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", "Description"); + Container container = softwareSystem.addContainer("Container", "Description", "Technology"); + DeploymentNode deploymentNode = model.addDeploymentNode("Live", "Deployment Node", "Description", "Technology"); + DeploymentNodeDslContext context = new DeploymentNodeDslContext(deploymentNode); + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("container", container); + context.setIdentifierRegister(elements); + + parser.parse(context, tokens("instanceOf", "container")); + + assertEquals(4, model.getElements().size()); + assertEquals(1, deploymentNode.getContainerInstances().size()); + ContainerInstance containerInstance = deploymentNode.getContainerInstances().iterator().next(); + assertSame(container, containerInstance.getContainer()); + assertEquals("Container Instance", containerInstance.getTags()); + assertEquals("Live", containerInstance.getEnvironment()); + assertEquals(1, containerInstance.getDeploymentGroups().size()); + assertEquals("Default", containerInstance.getDeploymentGroups().iterator().next()); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/SoftwareSystemInstanceParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/SoftwareSystemInstanceParserTests.java index c25ad5e43..290c56a0b 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/SoftwareSystemInstanceParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/SoftwareSystemInstanceParserTests.java @@ -7,7 +7,7 @@ class SoftwareSystemInstanceParserTests extends AbstractTests { - private SoftwareSystemInstanceParser parser = new SoftwareSystemInstanceParser(); + private final SoftwareSystemInstanceParser parser = new SoftwareSystemInstanceParser(); @Test void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { diff --git a/structurizr-dsl/src/test/resources/dsl/test.dsl b/structurizr-dsl/src/test/resources/dsl/test.dsl index 286853104..d2de4ed88 100644 --- a/structurizr-dsl/src/test/resources/dsl/test.dsl +++ b/structurizr-dsl/src/test/resources/dsl/test.dsl @@ -109,6 +109,18 @@ workspace "Name" "Description" { healthCheck "Check 2" "https://example.com/health" 60 healthCheck "Check 2" "https://example.com/health" 120 1000 } + instanceOf softwareSystem { + url "https://structurizr.com" + properties { + "Name" "Value" + } + perspectives { + "Security" "A description..." + } + healthCheck "Check 1" "https://example.com/health" + healthCheck "Check 2" "https://example.com/health" 60 + healthCheck "Check 2" "https://example.com/health" 120 1000 + } } } @@ -138,6 +150,18 @@ workspace "Name" "Description" { healthCheck "Check 2" "https://example.com/health" 60 healthCheck "Check 2" "https://example.com/health" 120 1000 } + instanceOf webApplication { + url "https://structurizr.com" + properties { + "Name" "Value" + } + perspectives { + "Security" "A description..." + } + healthCheck "Check 1" "https://example.com/health" + healthCheck "Check 2" "https://example.com/health" 60 + healthCheck "Check 2" "https://example.com/health" 120 1000 + } } url "https://structurizr.com" From 2e9274f6f8be498bd30788c196d4271c804222a2 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 3 Aug 2025 08:47:46 +0100 Subject: [PATCH 651/717] Adds missing tests, fixes typo in message. --- .../com/structurizr/dsl/AbstractParser.java | 12 ------- .../structurizr/dsl/AbstractViewParser.java | 12 +++++++ .../dsl/AbstractViewParserTests.java | 34 +++++++++++++++++++ 3 files changed, 46 insertions(+), 12 deletions(-) create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/AbstractViewParserTests.java diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractParser.java index 2e9ede6b2..5d41ec735 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractParser.java @@ -4,24 +4,12 @@ import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; import org.apache.hc.client5.http.impl.classic.HttpClients; -import org.apache.hc.core5.http.ContentType; -import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.io.entity.EntityUtils; -import java.util.regex.Pattern; - abstract class AbstractParser { private static final int HTTP_OK_STATUS = 200; - private static final Pattern VIEW_KEY_PATTERN = Pattern.compile("[\\w-]+"); - - void validateViewKey(String key) { - if (!VIEW_KEY_PATTERN.matcher(key).matches()) { - throw new RuntimeException("View keys can only contain the following characters: a-zA-0-9_-"); - } - } - String removeNonWordCharacters(String name) { return name.replaceAll("\\W", ""); } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractViewParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractViewParser.java index 8f66d3e9b..c47bfabe8 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractViewParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractViewParser.java @@ -1,4 +1,16 @@ package com.structurizr.dsl; +import java.util.regex.Pattern; + abstract class AbstractViewParser extends AbstractParser { + + private static final String PERMITTED_CHARACTERS_IN_VIEW_KEY = "a-zA-Z0-9_-"; + private static final Pattern VIEW_KEY_PATTERN = Pattern.compile("[" + PERMITTED_CHARACTERS_IN_VIEW_KEY + "]+"); + + void validateViewKey(String key) { + if (!VIEW_KEY_PATTERN.matcher(key).matches()) { + throw new RuntimeException("View keys can only contain the following characters: a-zA-Z0-9_-"); + } + } + } \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/AbstractViewParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/AbstractViewParserTests.java new file mode 100644 index 000000000..7322b1acc --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/AbstractViewParserTests.java @@ -0,0 +1,34 @@ +package com.structurizr.dsl; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +class AbstractViewParserTests { + + private final AbstractViewParser parser = new SystemLandscapeViewParser(); + + @Test + void test_validateViewKey() { + parser.validateViewKey("key"); + parser.validateViewKey("key123"); + parser.validateViewKey("Key123"); + parser.validateViewKey("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-"); + + try { + parser.validateViewKey("abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789"); + fail(); + } catch (Exception e) { + assertEquals("View keys can only contain the following characters: a-zA-Z0-9_-", e.getMessage()); + } + + try { + parser.validateViewKey("abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789"); + fail(); + } catch (Exception e) { + assertEquals("View keys can only contain the following characters: a-zA-Z0-9_-", e.getMessage()); + } + } + +} \ No newline at end of file From b7b76b54be7b1ddd06c0b93553f141ea699a6767 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 3 Aug 2025 09:24:05 +0100 Subject: [PATCH 652/717] Adds the ability to remove a relationship from the workspace. --- .../main/java/com/structurizr/Workspace.java | 30 +++++++++++++++++++ .../java/com/structurizr/model/Model.java | 13 ++++++-- .../java/com/structurizr/view/ModelView.java | 16 ++++++++++ .../java/com/structurizr/WorkspaceTests.java | 28 +++++++++++++++++ 4 files changed, 85 insertions(+), 2 deletions(-) diff --git a/structurizr-core/src/main/java/com/structurizr/Workspace.java b/structurizr-core/src/main/java/com/structurizr/Workspace.java index 98ab55e46..46e2b7fdd 100644 --- a/structurizr-core/src/main/java/com/structurizr/Workspace.java +++ b/structurizr-core/src/main/java/com/structurizr/Workspace.java @@ -310,6 +310,36 @@ void remove(DeploymentNode deploymentNode) { } } + /** + * Removes a relationship from the workspace. + * + * @param relationship the Relationship to remove + */ + public void remove(Relationship relationship) { + if (relationship == null) { + throw new IllegalArgumentException("A relationship must be specified."); + } + + // remove the relationship from views + for (View view : viewSet.getViews()) { + if (view instanceof ModelView) { + ModelView modelView = (ModelView)view; + if (modelView.isRelationshipInView(relationship)) { + modelView.remove(relationship); + } + } + } + + // now remove the relationship itself + try { + Method method = Model.class.getDeclaredMethod("remove", Relationship.class); + method.setAccessible(true); + method.invoke(model, relationship); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + private boolean isElementAssociatedWithAnyViews(Element element) { boolean result = false; diff --git a/structurizr-core/src/main/java/com/structurizr/model/Model.java b/structurizr-core/src/main/java/com/structurizr/model/Model.java index 57d947b2d..95964fa88 100644 --- a/structurizr-core/src/main/java/com/structurizr/model/Model.java +++ b/structurizr-core/src/main/java/com/structurizr/model/Model.java @@ -1171,8 +1171,7 @@ private void removeElement(Element element) { // remove any relationships to/from the element for (Relationship relationship : getRelationships()) { if (relationship.getSource() == element || relationship.getDestination() == element) { - removeRelationshipFromInternalStructures(relationship); - relationship.getSource().remove(relationship); + remove(relationship); } } @@ -1180,4 +1179,14 @@ private void removeElement(Element element) { elements.remove(element); } + /** + * Removes a relationship from the model. + * + * @param relationship the Relationship to remove + */ + void remove(Relationship relationship) { + removeRelationshipFromInternalStructures(relationship); + relationship.getSource().remove(relationship); + } + } \ No newline at end of file diff --git a/structurizr-core/src/main/java/com/structurizr/view/ModelView.java b/structurizr-core/src/main/java/com/structurizr/view/ModelView.java index 5ced49fed..90f0a5bdc 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/ModelView.java +++ b/structurizr-core/src/main/java/com/structurizr/view/ModelView.java @@ -272,10 +272,26 @@ protected RelationshipView addRelationship(Relationship relationship) { return null; } + /** + * Determines whether the specified element exists in this view. + * + * @param element the Element to look for + * @return true if the element exists in the view, false otherwise + */ public boolean isElementInView(Element element) { return this.elementViews.stream().anyMatch(ev -> ev.getElement().equals(element)); } + /** + * Determines whether the specified relationship exists in this view. + * + * @param relationship the Relationship to look for + * @return true if the relationship exists in the view, false otherwise + */ + public boolean isRelationshipInView(Relationship relationship) { + return this.relationshipViews.stream().anyMatch(rv -> rv.getRelationship().equals(relationship)); + } + /** * Removes a relationship from this view. * diff --git a/structurizr-core/src/test/java/com/structurizr/WorkspaceTests.java b/structurizr-core/src/test/java/com/structurizr/WorkspaceTests.java index c22b582be..ad55331ad 100644 --- a/structurizr-core/src/test/java/com/structurizr/WorkspaceTests.java +++ b/structurizr-core/src/test/java/com/structurizr/WorkspaceTests.java @@ -300,4 +300,32 @@ void trim_WhenTheDestinationOfAnElementIsRemoved() { assertEquals(0, a.getRelationships().size()); } + @Test + void removeRelationship_ThrowsAnException_WhenNoRelationshipIsSpecified() { + Workspace workspace = new Workspace("Name", "Description"); + try { + workspace.remove((Relationship)null); + fail(); + } catch (Exception e) { + assertEquals("A relationship must be specified.", e.getMessage()); + } + } + + @Test + void removeRelationship() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem a = workspace.getModel().addSoftwareSystem("A"); + SoftwareSystem b = workspace.getModel().addSoftwareSystem("B"); + Relationship relationship = a.uses(b, "Uses"); + + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "Description"); + view.addDefaultElements(); + + workspace.remove(relationship); + + assertEquals(0, a.getRelationships().size()); + assertFalse(a.hasEfferentRelationshipWith(b)); + assertFalse(view.isRelationshipInView(relationship)); + } + } \ No newline at end of file From d8467b34782c2748338229e5802b6515761c3d00 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 20 Aug 2025 13:02:43 +0100 Subject: [PATCH 653/717] Relationships to/from software system/container instances can be now defined by using the software system/container identifier. --- .../dsl/AbstractRelationshipParser.java | 12 ++ .../dsl/ExplicitRelationshipParser.java | 59 +++++++- .../dsl/ImplicitRelationshipParser.java | 45 +++++- .../structurizr/dsl/StructurizrDslParser.java | 34 +++-- .../dsl/ExplicitRelationshipParserTests.java | 134 +++++++++++++++++- .../dsl/ImplicitRelationshipParserTests.java | 65 +++++++++ 6 files changed, 326 insertions(+), 23 deletions(-) diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractRelationshipParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractRelationshipParser.java index d8ffb28c6..c9b5adfdc 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractRelationshipParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractRelationshipParser.java @@ -2,6 +2,10 @@ import com.structurizr.model.*; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; + abstract class AbstractRelationshipParser extends AbstractParser { protected Relationship createRelationship(Element sourceElement, String description, String technology, String[] tags, Element destinationElement) { @@ -34,4 +38,12 @@ protected Relationship createRelationship(Element sourceElement, String descript return relationship; } + protected Set findSoftwareSystemInstances(SoftwareSystem softwareSystem, String deploymentEnvironment) { + return softwareSystem.getModel().getElements().stream().filter(e -> e instanceof SoftwareSystemInstance).map(e -> (SoftwareSystemInstance) e).filter(ssi -> ssi.getSoftwareSystem().equals(softwareSystem) && ssi.getEnvironment().equals(deploymentEnvironment)).collect(Collectors.toSet()); + } + + protected Set findContainerInstances(Container container, String deploymentEnvironment) { + return container.getModel().getElements().stream().filter(e -> e instanceof ContainerInstance).map(e -> (ContainerInstance) e).filter(ci -> ci.getContainer().equals(container) && ci.getEnvironment().equals(deploymentEnvironment)).collect(Collectors.toSet()); + } + } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ExplicitRelationshipParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ExplicitRelationshipParser.java index 38e225b22..affc90fb1 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ExplicitRelationshipParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ExplicitRelationshipParser.java @@ -1,7 +1,6 @@ package com.structurizr.dsl; -import com.structurizr.model.Element; -import com.structurizr.model.Relationship; +import com.structurizr.model.*; import javax.lang.model.util.Elements; import java.util.*; @@ -16,9 +15,11 @@ final class ExplicitRelationshipParser extends AbstractRelationshipParser { private final static int TECHNOLOGY_INDEX = 4; private final static int TAGS_INDEX = 5; - Relationship parse(DslContext context, Tokens tokens, Archetype archetype) { + Set parse(DslContext context, Tokens tokens, Archetype archetype) { // -> [description] [technology] [tags] + Set relationships = new HashSet<>(); + if (tokens.hasMoreThan(TAGS_INDEX)) { throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); } @@ -54,11 +55,55 @@ Relationship parse(DslContext context, Tokens tokens, Archetype archetype) { tags.addAll(Arrays.asList(tokens.get(TAGS_INDEX).split(","))); } - Relationship relationship = createRelationship(sourceElement, description, technology, tags.toArray(new String[0]), destinationElement); - relationship.addProperties(archetype.getProperties()); - relationship.addPerspectives(archetype.getPerspectives()); + Set sourceElements = new HashSet<>(); + Set destinationElements = new HashSet<>(); + + if (context instanceof DeploymentEnvironmentDslContext || context instanceof DeploymentNodeDslContext) { + String deploymentEnvironment; + if (context instanceof DeploymentEnvironmentDslContext) { + deploymentEnvironment = ((DeploymentEnvironmentDslContext)context).getEnvironment().getName(); + } else { + deploymentEnvironment = ((DeploymentNodeDslContext)context).getDeploymentNode().getEnvironment(); + } + + if (sourceElement instanceof SoftwareSystem) { + // find the software system instances in the deployment environment + sourceElements = findSoftwareSystemInstances((SoftwareSystem)sourceElement, deploymentEnvironment); + } else if (sourceElement instanceof Container) { + // find the container instances in the deployment environment + sourceElements = findContainerInstances((Container)sourceElement, deploymentEnvironment); + } else { + sourceElements.add(sourceElement); + } + + if (destinationElement instanceof SoftwareSystem) { + // find the software system instances in the deployment environment + destinationElements = findSoftwareSystemInstances((SoftwareSystem)destinationElement, deploymentEnvironment); + } else if (destinationElement instanceof Container) { + // find the container instances in the deployment environment + destinationElements = findContainerInstances((Container)destinationElement, deploymentEnvironment); + } else { + destinationElements.add(destinationElement); + } + + for (Element se : sourceElements) { + for (Element de : destinationElements) { + Relationship relationship = createRelationship(se, description, technology, tags.toArray(new String[0]), de); + relationship.addProperties(archetype.getProperties()); + relationship.addPerspectives(archetype.getPerspectives()); + + relationships.add(relationship); + } + } + } else { + Relationship relationship = createRelationship(sourceElement, description, technology, tags.toArray(new String[0]), destinationElement); + relationship.addProperties(archetype.getProperties()); + relationship.addPerspectives(archetype.getPerspectives()); - return relationship; + relationships.add(relationship); + } + + return relationships; } Set parse(ElementsDslContext context, Tokens tokens, Archetype archetype) { diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImplicitRelationshipParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImplicitRelationshipParser.java index 7c10b7015..e97e8cb8d 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImplicitRelationshipParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImplicitRelationshipParser.java @@ -1,7 +1,9 @@ package com.structurizr.dsl; +import com.structurizr.model.Container; import com.structurizr.model.Element; import com.structurizr.model.Relationship; +import com.structurizr.model.SoftwareSystem; import java.util.*; @@ -14,9 +16,11 @@ final class ImplicitRelationshipParser extends AbstractRelationshipParser { private final static int TECHNOLOGY_INDEX = 3; private final static int TAGS_INDEX = 4; - Relationship parse(ElementDslContext context, Tokens tokens, Archetype archetype) { + Set parse(ElementDslContext context, Tokens tokens, Archetype archetype) { // -> [description] [technology] [tags] + Set relationships = new HashSet<>(); + if (tokens.hasMoreThan(TAGS_INDEX)) { throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); } @@ -49,11 +53,42 @@ Relationship parse(ElementDslContext context, Tokens tokens, Archetype archetype tags.addAll(Arrays.asList(tokens.get(TAGS_INDEX).split(","))); } - Relationship relationship = createRelationship(sourceElement, description, technology, tags.toArray(new String[0]), destinationElement); - relationship.addProperties(archetype.getProperties()); - relationship.addPerspectives(archetype.getPerspectives()); + Set sourceElements = new HashSet<>(); + Set destinationElements = new HashSet<>(); + + if (context instanceof InfrastructureNodeDslContext) { + String deploymentEnvironment = ((InfrastructureNodeDslContext)context).getInfrastructureNode().getEnvironment(); + + sourceElements.add(sourceElement); + + if (destinationElement instanceof SoftwareSystem) { + // find the software system instances in the deployment environment + destinationElements = findSoftwareSystemInstances((SoftwareSystem)destinationElement, deploymentEnvironment); + } else if (destinationElement instanceof Container) { + // find the container instances in the deployment environment + destinationElements = findContainerInstances((Container)destinationElement, deploymentEnvironment); + } else { + destinationElements.add(destinationElement); + } + + for (Element se : sourceElements) { + for (Element de : destinationElements) { + Relationship relationship = createRelationship(se, description, technology, tags.toArray(new String[0]), de); + relationship.addProperties(archetype.getProperties()); + relationship.addPerspectives(archetype.getPerspectives()); + + relationships.add(relationship); + } + } + } else { + Relationship relationship = createRelationship(sourceElement, description, technology, tags.toArray(new String[0]), destinationElement); + relationship.addProperties(archetype.getProperties()); + relationship.addPerspectives(archetype.getPerspectives()); - return relationship; + relationships.add(relationship); + } + + return relationships; } Set parse(ElementsDslContext context, Tokens tokens, Archetype archetype) { diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index b68d40cee..322b25b67 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -327,25 +327,39 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn // explicit without archetype: a -> b // explicit with archetype: a --https-> b Archetype archetype = getArchetype(RELATIONSHIP_TOKEN, tokens.get(1)); - Relationship relationship = new ExplicitRelationshipParser().parse(getContext(), tokens.withoutContextStartToken(), archetype); + Set relationships = new ExplicitRelationshipParser().parse(getContext(), tokens.withoutContextStartToken(), archetype); - if (shouldStartContext(tokens)) { - startContext(new RelationshipDslContext(relationship)); - } + if (relationships.size() == 1) { + Relationship relationship = relationships.iterator().next(); + registerIdentifier(identifier, relationship); - registerIdentifier(identifier, relationship); + if (shouldStartContext(tokens)) { + startContext(new RelationshipDslContext(relationship)); + } + } else { + if (shouldStartContext(tokens)) { + startContext(new RelationshipsDslContext(getContext(), relationships)); + } + } } else if (tokens.size() >= 2 && isRelationshipKeywordOrArchetype(tokens.get(0)) && inContext(ElementDslContext.class)) { // implicit without archetype: -> this // implicit with archetype: --https-> this Archetype archetype = getArchetype(RELATIONSHIP_TOKEN, tokens.get(1)); - Relationship relationship = new ImplicitRelationshipParser().parse(getContext(ElementDslContext.class), tokens.withoutContextStartToken(), archetype); + Set relationships = new ImplicitRelationshipParser().parse(getContext(ElementDslContext.class), tokens.withoutContextStartToken(), archetype); - if (shouldStartContext(tokens)) { - startContext(new RelationshipDslContext(relationship)); - } + if (relationships.size() == 1) { + Relationship relationship = relationships.iterator().next(); + registerIdentifier(identifier, relationship); - registerIdentifier(identifier, relationship); + if (shouldStartContext(tokens)) { + startContext(new RelationshipDslContext(relationship)); + } + } else { + if (shouldStartContext(tokens)) { + startContext(new RelationshipsDslContext(getContext(), relationships)); + } + } } else if (tokens.size() > 2 && isRelationshipKeywordOrArchetype(tokens.get(1)) && inContext(ElementsDslContext.class)) { Archetype archetype = getArchetype(RELATIONSHIP_TOKEN, tokens.get(1)); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ExplicitRelationshipParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ExplicitRelationshipParserTests.java index 1cd8f213d..a1f540ea4 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ExplicitRelationshipParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ExplicitRelationshipParserTests.java @@ -3,11 +3,13 @@ import com.structurizr.model.*; import org.junit.jupiter.api.Test; +import java.util.Set; + import static org.junit.jupiter.api.Assertions.*; class ExplicitRelationshipParserTests extends AbstractTests { - private ExplicitRelationshipParser parser = new ExplicitRelationshipParser(); + private final ExplicitRelationshipParser parser = new ExplicitRelationshipParser(); private Archetype archetype = new Archetype("name", "type"); @Test @@ -259,4 +261,134 @@ void test_parse_AddsTheRelationship_WithADestinationOfThis() { assertEquals("Relationship", r.getTags()); } + @Test + void test_parse_AddsTheRelationshipToAllSoftwareSystemInstancesInTheDeploymentEnvironment() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + + DeploymentNode devDeploymentNode = model.addDeploymentNode("dev", "Deployment Node", "Description", "Technology"); + devDeploymentNode.addInfrastructureNode("Infrastructure Node"); + devDeploymentNode.add(softwareSystem); + devDeploymentNode.add(softwareSystem); + + DeploymentNode liveDeploymentNode = model.addDeploymentNode("live", "Deployment Node", "Description", "Technology"); + InfrastructureNode liveInfrastructureNode = liveDeploymentNode.addInfrastructureNode("Infrastructure Node"); + SoftwareSystemInstance liveSoftwareSystemInstance1 = liveDeploymentNode.add(softwareSystem); + SoftwareSystemInstance liveSoftwareSystemInstance2 = liveDeploymentNode.add(softwareSystem); + + DeploymentEnvironmentDslContext context = new DeploymentEnvironmentDslContext("live"); + context.setWorkspace(workspace); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("softwareSystem", softwareSystem); + elements.register("liveInfrastructureNode", liveInfrastructureNode); + context.setIdentifierRegister(elements); + + assertEquals(0, model.getRelationships().size()); + + Set relationships = parser.parse(context, tokens("liveInfrastructureNode", "->", "softwareSystem"), archetype); + + assertEquals(2, relationships.size()); + assertEquals(2, model.getRelationships().size()); + assertTrue(liveInfrastructureNode.hasEfferentRelationshipWith(liveSoftwareSystemInstance1)); + assertTrue(liveInfrastructureNode.hasEfferentRelationshipWith(liveSoftwareSystemInstance2)); + } + + @Test + void test_parse_AddsTheRelationshipFromAllSoftwareSystemInstancesInTheDeploymentEnvironment() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + + DeploymentNode devDeploymentNode = model.addDeploymentNode("dev", "Deployment Node", "Description", "Technology"); + devDeploymentNode.addInfrastructureNode("Infrastructure Node"); + devDeploymentNode.add(softwareSystem); + devDeploymentNode.add(softwareSystem); + + DeploymentNode liveDeploymentNode = model.addDeploymentNode("live", "Deployment Node", "Description", "Technology"); + InfrastructureNode liveInfrastructureNode = liveDeploymentNode.addInfrastructureNode("Infrastructure Node"); + SoftwareSystemInstance liveSoftwareSystemInstance1 = liveDeploymentNode.add(softwareSystem); + SoftwareSystemInstance liveSoftwareSystemInstance2 = liveDeploymentNode.add(softwareSystem); + + DeploymentEnvironmentDslContext context = new DeploymentEnvironmentDslContext("live"); + context.setWorkspace(workspace); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("softwareSystem", softwareSystem); + elements.register("liveInfrastructureNode", liveInfrastructureNode); + context.setIdentifierRegister(elements); + + assertEquals(0, model.getRelationships().size()); + + Set relationships = parser.parse(context, tokens("softwareSystem", "->", "liveInfrastructureNode"), archetype); + + assertEquals(2, relationships.size()); + assertEquals(2, model.getRelationships().size()); + assertTrue(liveSoftwareSystemInstance1.hasEfferentRelationshipWith(liveInfrastructureNode)); + assertTrue(liveSoftwareSystemInstance2.hasEfferentRelationshipWith(liveInfrastructureNode)); + } + + @Test + void test_parse_AddsTheRelationshipToAllContainerInstancesInTheDeploymentEnvironment() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + + DeploymentNode devDeploymentNode = model.addDeploymentNode("dev", "Deployment Node", "Description", "Technology"); + devDeploymentNode.addInfrastructureNode("Infrastructure Node"); + devDeploymentNode.add(container); + devDeploymentNode.add(container); + + DeploymentNode liveDeploymentNode = model.addDeploymentNode("live", "Deployment Node", "Description", "Technology"); + InfrastructureNode liveInfrastructureNode = liveDeploymentNode.addInfrastructureNode("Infrastructure Node"); + ContainerInstance liveContainerInstance1 = liveDeploymentNode.add(container); + ContainerInstance liveContainerInstance2 = liveDeploymentNode.add(container); + + DeploymentNodeDslContext context = new DeploymentNodeDslContext(liveDeploymentNode); + context.setWorkspace(workspace); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("container", container); + elements.register("liveInfrastructureNode", liveInfrastructureNode); + context.setIdentifierRegister(elements); + + assertEquals(0, model.getRelationships().size()); + + Set relationships = parser.parse(context, tokens("liveInfrastructureNode", "->", "container"), archetype); + + assertEquals(2, relationships.size()); + assertEquals(2, model.getRelationships().size()); + assertTrue(liveInfrastructureNode.hasEfferentRelationshipWith(liveContainerInstance1)); + assertTrue(liveInfrastructureNode.hasEfferentRelationshipWith(liveContainerInstance2)); + } + + @Test + void test_parse_AddsTheRelationshipFromAllContainerInstancesInTheDeploymentEnvironment() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + + DeploymentNode devDeploymentNode = model.addDeploymentNode("dev", "Deployment Node", "Description", "Technology"); + devDeploymentNode.addInfrastructureNode("Infrastructure Node"); + devDeploymentNode.add(container); + devDeploymentNode.add(container); + + DeploymentNode liveDeploymentNode = model.addDeploymentNode("live", "Deployment Node", "Description", "Technology"); + InfrastructureNode liveInfrastructureNode = liveDeploymentNode.addInfrastructureNode("Infrastructure Node"); + ContainerInstance liveContainerInstance1 = liveDeploymentNode.add(container); + ContainerInstance liveContainerInstance2 = liveDeploymentNode.add(container); + + DeploymentNodeDslContext context = new DeploymentNodeDslContext(liveDeploymentNode); + context.setWorkspace(workspace); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("container", container); + elements.register("liveInfrastructureNode", liveInfrastructureNode); + context.setIdentifierRegister(elements); + + assertEquals(0, model.getRelationships().size()); + + Set relationships = parser.parse(context, tokens("container", "->", "liveInfrastructureNode"), archetype); + + assertEquals(2, relationships.size()); + assertEquals(2, model.getRelationships().size()); + assertTrue(liveContainerInstance1.hasEfferentRelationshipWith(liveInfrastructureNode)); + assertTrue(liveContainerInstance2.hasEfferentRelationshipWith(liveInfrastructureNode)); + } + } \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ImplicitRelationshipParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ImplicitRelationshipParserTests.java index fe6b84a61..706454d33 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ImplicitRelationshipParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ImplicitRelationshipParserTests.java @@ -3,6 +3,8 @@ import com.structurizr.model.*; import org.junit.jupiter.api.Test; +import java.util.Set; + import static org.junit.jupiter.api.Assertions.*; class ImplicitRelationshipParserTests extends AbstractTests { @@ -205,4 +207,67 @@ void test_parse_AddsTheRelationshipAndImplicitRelationshipsWithADescriptionAndTe assertEquals("", r.getTags()); } + @Test + void test_parse_AddsTheRelationshipToAllSoftwareSystemInstancesInTheDeploymentEnvironment() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + + DeploymentNode devDeploymentNode = model.addDeploymentNode("dev", "Deployment Node", "Description", "Technology"); + devDeploymentNode.addInfrastructureNode("Infrastructure Node"); + devDeploymentNode.add(softwareSystem); + devDeploymentNode.add(softwareSystem); + + DeploymentNode liveDeploymentNode = model.addDeploymentNode("live", "Deployment Node", "Description", "Technology"); + InfrastructureNode liveInfrastructureNode = liveDeploymentNode.addInfrastructureNode("Infrastructure Node"); + SoftwareSystemInstance liveSoftwareSystemInstance1 = liveDeploymentNode.add(softwareSystem); + SoftwareSystemInstance liveSoftwareSystemInstance2 = liveDeploymentNode.add(softwareSystem); + + InfrastructureNodeDslContext context = new InfrastructureNodeDslContext(liveInfrastructureNode); + context.setWorkspace(workspace); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("softwareSystem", softwareSystem); + context.setIdentifierRegister(elements); + + assertEquals(0, model.getRelationships().size()); + + Set relationships = parser.parse(context, tokens("->", "softwareSystem"), archetype); + + assertEquals(2, relationships.size()); + assertEquals(2, model.getRelationships().size()); + assertTrue(liveInfrastructureNode.hasEfferentRelationshipWith(liveSoftwareSystemInstance1)); + assertTrue(liveInfrastructureNode.hasEfferentRelationshipWith(liveSoftwareSystemInstance2)); + } + + @Test + void test_parse_AddsTheRelationshipToAllContainerInstancesInTheDeploymentEnvironment() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + Container container = softwareSystem.addContainer("Container"); + + DeploymentNode devDeploymentNode = model.addDeploymentNode("dev", "Deployment Node", "Description", "Technology"); + devDeploymentNode.addInfrastructureNode("Infrastructure Node"); + devDeploymentNode.add(container); + devDeploymentNode.add(container); + + DeploymentNode liveDeploymentNode = model.addDeploymentNode("live", "Deployment Node", "Description", "Technology"); + InfrastructureNode liveInfrastructureNode = liveDeploymentNode.addInfrastructureNode("Infrastructure Node"); + ContainerInstance liveContainerInstance1 = liveDeploymentNode.add(container); + ContainerInstance liveContainerInstance2 = liveDeploymentNode.add(container); + + InfrastructureNodeDslContext context = new InfrastructureNodeDslContext(liveInfrastructureNode); + context.setWorkspace(workspace); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("container", container); + context.setIdentifierRegister(elements); + + assertEquals(0, model.getRelationships().size()); + + Set relationships = parser.parse(context, tokens("->", "container"), archetype); + + assertEquals(2, relationships.size()); + assertEquals(2, model.getRelationships().size()); + assertTrue(liveInfrastructureNode.hasEfferentRelationshipWith(liveContainerInstance1)); + assertTrue(liveInfrastructureNode.hasEfferentRelationshipWith(liveContainerInstance2)); + } + } \ No newline at end of file From d76d18c08daf2e87589c67502d51761468a72bac Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 20 Aug 2025 13:15:12 +0100 Subject: [PATCH 654/717] Fixes #435. --- changelog.md | 2 ++ .../structurizr/dsl/StructurizrDslParser.java | 2 +- .../test/java/com/structurizr/dsl/DslTests.java | 16 ++++++++++++++++ .../archetypes-for-implicit-relationships.dsl | 15 +++++++++++++++ 4 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 structurizr-dsl/src/test/resources/dsl/archetypes-for-implicit-relationships.dsl diff --git a/changelog.md b/changelog.md index 6f3d8f8a2..ca8a70096 100644 --- a/changelog.md +++ b/changelog.md @@ -8,6 +8,8 @@ - structurizr-dsl: Adds a `Shell` shape. - structurizr-dsl: Adds a `Terminal` shape. - structurizr-dsl: Adds an 'instanceOf' keyword (an alternative for `softwareSystemInstance` and `containerInstance`). +- structurizr-dsl: Relationships to/from software system/container instances can be now defined by using the software system/container identifier. +- structurizr-dsl: Fixes https://github.com/structurizr/java/issues/435 (Relationship archetype not applied to implicit-source relationships). ## v4.1.0 (28th May 2025) diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index 322b25b67..2a686828c 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -345,7 +345,7 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn } else if (tokens.size() >= 2 && isRelationshipKeywordOrArchetype(tokens.get(0)) && inContext(ElementDslContext.class)) { // implicit without archetype: -> this // implicit with archetype: --https-> this - Archetype archetype = getArchetype(RELATIONSHIP_TOKEN, tokens.get(1)); + Archetype archetype = getArchetype(RELATIONSHIP_TOKEN, tokens.get(0)); Set relationships = new ImplicitRelationshipParser().parse(getContext(ElementDslContext.class), tokens.withoutContextStartToken(), archetype); if (relationships.size() == 1) { diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index a613b53c7..9bcc2beeb 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -1563,6 +1563,22 @@ void test_archetypesForExtension() throws Exception { assertTrue(r.hasTag("HTTPS")); } + @Test + void test_archetypesForImplicitRelationships() throws Exception { + File parentDslFile = new File("src/test/resources/dsl/archetypes-for-implicit-relationships.dsl"); + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(parentDslFile); + Workspace workspace = parser.getWorkspace(); + + SoftwareSystem a = workspace.getModel().getSoftwareSystemWithName("A"); + SoftwareSystem b = workspace.getModel().getSoftwareSystemWithName("B"); + + Relationship r = b.getEfferentRelationshipWith(a); + assertEquals("Makes API calls to", r.getDescription()); + assertEquals("HTTPS", r.getTechnology()); + assertTrue(r.hasTag("HTTPS")); + } + @Test void test_textBlock() throws Exception { StructurizrDslParser parser = new StructurizrDslParser(); diff --git a/structurizr-dsl/src/test/resources/dsl/archetypes-for-implicit-relationships.dsl b/structurizr-dsl/src/test/resources/dsl/archetypes-for-implicit-relationships.dsl new file mode 100644 index 000000000..8a642f3bd --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/archetypes-for-implicit-relationships.dsl @@ -0,0 +1,15 @@ +workspace { + model { + archetypes { + https = -> { + technology "HTTPS" + tag "HTTPS" + } + } + + a = softwareSystem "A" + b = softwareSystem "B" { + --https-> a "Makes API calls to" + } + } +} \ No newline at end of file From c527e093d51c85044fccfd47536b52c9cb8f7be9 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 20 Aug 2025 17:43:50 +0100 Subject: [PATCH 655/717] Adds support for removing relationships between software system instance/container instances, with a view to redefining them via infrastructure nodes. --- changelog.md | 1 + .../dsl/AbstractRelationshipParser.java | 4 +- .../dsl/DeploymentEnvironmentDslContext.java | 2 +- .../dsl/ExplicitRelationshipParser.java | 16 +- .../dsl/ImplicitRelationshipParser.java | 4 +- ...shipInDeploymentEnvironmentDslContext.java | 26 ++ .../structurizr/dsl/NoRelationshipParser.java | 98 ++++++ .../structurizr/dsl/StructurizrDslParser.java | 13 + .../structurizr/dsl/StructurizrDslTokens.java | 1 + .../java/com/structurizr/dsl/DslTests.java | 49 +++ .../dsl/ExplicitRelationshipParserTests.java | 54 +++ .../dsl/NoRelationshipParserTests.java | 331 ++++++++++++++++++ .../test/resources/dsl/no-relationship.dsl | 88 +++++ 13 files changed, 678 insertions(+), 9 deletions(-) create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/NoRelationshipInDeploymentEnvironmentDslContext.java create mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/NoRelationshipParser.java create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/NoRelationshipParserTests.java create mode 100644 structurizr-dsl/src/test/resources/dsl/no-relationship.dsl diff --git a/changelog.md b/changelog.md index ca8a70096..1eadaa858 100644 --- a/changelog.md +++ b/changelog.md @@ -10,6 +10,7 @@ - structurizr-dsl: Adds an 'instanceOf' keyword (an alternative for `softwareSystemInstance` and `containerInstance`). - structurizr-dsl: Relationships to/from software system/container instances can be now defined by using the software system/container identifier. - structurizr-dsl: Fixes https://github.com/structurizr/java/issues/435 (Relationship archetype not applied to implicit-source relationships). +- structurizr-dsl: Adds support for removing relationships between software system instance/container instances, with a view to redefining them via infrastructure nodes. ## v4.1.0 (28th May 2025) diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractRelationshipParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractRelationshipParser.java index c9b5adfdc..556ac6a7e 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractRelationshipParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractRelationshipParser.java @@ -38,11 +38,11 @@ protected Relationship createRelationship(Element sourceElement, String descript return relationship; } - protected Set findSoftwareSystemInstances(SoftwareSystem softwareSystem, String deploymentEnvironment) { + protected Set findSoftwareSystemInstances(SoftwareSystem softwareSystem, String deploymentEnvironment) { return softwareSystem.getModel().getElements().stream().filter(e -> e instanceof SoftwareSystemInstance).map(e -> (SoftwareSystemInstance) e).filter(ssi -> ssi.getSoftwareSystem().equals(softwareSystem) && ssi.getEnvironment().equals(deploymentEnvironment)).collect(Collectors.toSet()); } - protected Set findContainerInstances(Container container, String deploymentEnvironment) { + protected Set findContainerInstances(Container container, String deploymentEnvironment) { return container.getModel().getElements().stream().filter(e -> e instanceof ContainerInstance).map(e -> (ContainerInstance) e).filter(ci -> ci.getContainer().equals(container) && ci.getEnvironment().equals(deploymentEnvironment)).collect(Collectors.toSet()); } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentEnvironmentDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentEnvironmentDslContext.java index 4cc53b0fc..ff54ccfe8 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentEnvironmentDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DeploymentEnvironmentDslContext.java @@ -1,6 +1,6 @@ package com.structurizr.dsl; -final class DeploymentEnvironmentDslContext extends DslContext implements GroupableDslContext { +class DeploymentEnvironmentDslContext extends DslContext implements GroupableDslContext { private final DeploymentEnvironment environment; private final ElementGroup group; diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ExplicitRelationshipParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ExplicitRelationshipParser.java index affc90fb1..2158d596d 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ExplicitRelationshipParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ExplicitRelationshipParser.java @@ -43,11 +43,19 @@ Set parse(DslContext context, Tokens tokens, Archetype archetype) String description = archetype.getDescription(); if (tokens.includes(DESCRIPTION_INDEX)) { description = tokens.get(DESCRIPTION_INDEX); + } else { + if (context instanceof NoRelationshipInDeploymentEnvironmentDslContext) { + description = ((NoRelationshipInDeploymentEnvironmentDslContext)context).getRelationship().getDescription(); + } } String technology = archetype.getTechnology(); if (tokens.includes(TECHNOLOGY_INDEX)) { technology = tokens.get(TECHNOLOGY_INDEX); + } else { + if (context instanceof NoRelationshipInDeploymentEnvironmentDslContext) { + technology = ((NoRelationshipInDeploymentEnvironmentDslContext)context).getRelationship().getTechnology(); + } } List tags = new ArrayList<>(archetype.getTags()); @@ -68,20 +76,20 @@ Set parse(DslContext context, Tokens tokens, Archetype archetype) if (sourceElement instanceof SoftwareSystem) { // find the software system instances in the deployment environment - sourceElements = findSoftwareSystemInstances((SoftwareSystem)sourceElement, deploymentEnvironment); + sourceElements.addAll(findSoftwareSystemInstances((SoftwareSystem)sourceElement, deploymentEnvironment)); } else if (sourceElement instanceof Container) { // find the container instances in the deployment environment - sourceElements = findContainerInstances((Container)sourceElement, deploymentEnvironment); + sourceElements.addAll(findContainerInstances((Container)sourceElement, deploymentEnvironment)); } else { sourceElements.add(sourceElement); } if (destinationElement instanceof SoftwareSystem) { // find the software system instances in the deployment environment - destinationElements = findSoftwareSystemInstances((SoftwareSystem)destinationElement, deploymentEnvironment); + destinationElements.addAll(findSoftwareSystemInstances((SoftwareSystem)destinationElement, deploymentEnvironment)); } else if (destinationElement instanceof Container) { // find the container instances in the deployment environment - destinationElements = findContainerInstances((Container)destinationElement, deploymentEnvironment); + destinationElements.addAll(findContainerInstances((Container)destinationElement, deploymentEnvironment)); } else { destinationElements.add(destinationElement); } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImplicitRelationshipParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImplicitRelationshipParser.java index e97e8cb8d..6d761096a 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImplicitRelationshipParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImplicitRelationshipParser.java @@ -63,10 +63,10 @@ Set parse(ElementDslContext context, Tokens tokens, Archetype arch if (destinationElement instanceof SoftwareSystem) { // find the software system instances in the deployment environment - destinationElements = findSoftwareSystemInstances((SoftwareSystem)destinationElement, deploymentEnvironment); + destinationElements.addAll(findSoftwareSystemInstances((SoftwareSystem)destinationElement, deploymentEnvironment)); } else if (destinationElement instanceof Container) { // find the container instances in the deployment environment - destinationElements = findContainerInstances((Container)destinationElement, deploymentEnvironment); + destinationElements.addAll(findContainerInstances((Container)destinationElement, deploymentEnvironment)); } else { destinationElements.add(destinationElement); } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/NoRelationshipInDeploymentEnvironmentDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/NoRelationshipInDeploymentEnvironmentDslContext.java new file mode 100644 index 000000000..c694fe977 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/NoRelationshipInDeploymentEnvironmentDslContext.java @@ -0,0 +1,26 @@ +package com.structurizr.dsl; + +import com.structurizr.model.Relationship; + +final class NoRelationshipInDeploymentEnvironmentDslContext extends DeploymentEnvironmentDslContext { + + private final Relationship relationship; + + NoRelationshipInDeploymentEnvironmentDslContext(DeploymentEnvironmentDslContext parent, Relationship relationship) { + super(parent.getEnvironment().getName(), parent.getGroup()); + + this.relationship = relationship; + } + + Relationship getRelationship() { + return relationship; + } + + @Override + protected String[] getPermittedTokens() { + return new String[] { + StructurizrDslTokens.RELATIONSHIP_TOKEN + }; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/NoRelationshipParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/NoRelationshipParser.java new file mode 100644 index 000000000..989e4ca93 --- /dev/null +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/NoRelationshipParser.java @@ -0,0 +1,98 @@ +package com.structurizr.dsl; + +import com.structurizr.model.*; + +import java.util.*; + +final class NoRelationshipParser extends AbstractRelationshipParser { + + private static final String GRAMMAR = " -/> [description]"; + + private static final int SOURCE_IDENTIFIER_INDEX = 0; + private static final int DESTINATION_IDENTIFIER_INDEX = 2; + private static final int DESCRIPTION_IDENTIFIER_INDEX = 3; + + Set parse(DeploymentEnvironmentDslContext context, Tokens tokens) { + // -/> [description] + + Set relationships = new HashSet<>(); + + if (tokens.hasMoreThan(DESCRIPTION_IDENTIFIER_INDEX)) { + throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); + } + + if (!tokens.includes(DESTINATION_IDENTIFIER_INDEX)) { + throw new RuntimeException("Not enough tokens, expected: " + GRAMMAR); + } + + String sourceId = tokens.get(SOURCE_IDENTIFIER_INDEX); + Element sourceElement = context.getElement(sourceId); + Set sourceElements = new HashSet<>(); + + if (sourceElement == null) { + throw new RuntimeException("The source element \"" + sourceId + "\" does not exist"); + } else if (sourceElement instanceof SoftwareSystem) { + sourceElements = findSoftwareSystemInstances((SoftwareSystem)sourceElement, context.getEnvironment().getName()); + } else if (sourceElement instanceof Container) { + sourceElements = findContainerInstances((Container)sourceElement, context.getEnvironment().getName()); + } else if (sourceElement instanceof StaticStructureElementInstance) { + sourceElements.add((StaticStructureElementInstance)sourceElement); + } else { + throw new RuntimeException("The source element \"" + sourceId + "\" is not valid - expecting a software system, software system instance, container, or container instance"); + } + + String destinationId = tokens.get(DESTINATION_IDENTIFIER_INDEX); + Element destinationElement = context.getElement(destinationId); + Set destinationElements = new HashSet<>(); + + if (destinationElement == null) { + throw new RuntimeException("The destination element \"" + destinationId + "\" does not exist"); + } else if (destinationElement instanceof SoftwareSystem) { + destinationElements = findSoftwareSystemInstances((SoftwareSystem)destinationElement, context.getEnvironment().getName()); + } else if (destinationElement instanceof Container) { + destinationElements = findContainerInstances((Container)destinationElement, context.getEnvironment().getName()); + } else if (destinationElement instanceof StaticStructureElementInstance) { + destinationElements.add((StaticStructureElementInstance)destinationElement); + } else { + throw new RuntimeException("The destination element \"" + destinationId + "\" is not valid - expecting a software system, software system instance, container, or container instance"); + } + + String description = null; + + if (tokens.includes(DESCRIPTION_IDENTIFIER_INDEX)) { + description = tokens.get(DESCRIPTION_IDENTIFIER_INDEX); + } + + int count = 0; + for (Element se : sourceElements) { + for (Element de : destinationElements) { + Relationship relationship; + + do { + if (description != null) { + relationship = se.getEfferentRelationshipWith(de, description); + } else { + relationship = se.getEfferentRelationshipWith(de); + } + + if (relationship != null && relationship.getLinkedRelationshipId() != null) { + context.getWorkspace().remove(relationship); + relationships.add(relationship); + count++; + } + } while (relationship != null); + } + } + + if (count == 0) { + if (description != null) { + throw new RuntimeException("A relationship between \"" + sourceId + "\" and \"" + destinationId + "\" with description \"" + description + "\" does not exist"); + } else { + throw new RuntimeException("A relationship between \"" + sourceId + "\" and \"" + destinationId + "\" does not exist"); + } + } + + return relationships; + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index 2a686828c..b2ce23e56 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -323,6 +323,19 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn } else if (inContext(ExternalScriptDslContext.class)) { new ScriptParser().parseParameter(getContext(ExternalScriptDslContext.class), tokens); + } else if (tokens.size() >= 4 && tokens.get(1).equals(NO_RELATIONSHIP_TOKEN) && shouldStartContext(tokens) && inContext(DeploymentEnvironmentDslContext.class)) { + // source -/> destination { + // or + // source -/> destination "description" { + + // remove source -> destination (between instances) in the deployment model + Set relationships = new NoRelationshipParser().parse(getContext(DeploymentEnvironmentDslContext.class), tokens.withoutContextStartToken()); + + // find the static element -> static element relationship that the removed relationships were based upon + Relationship relationship = workspace.getModel().getRelationship(relationships.iterator().next().getLinkedRelationshipId()); + + startContext(new NoRelationshipInDeploymentEnvironmentDslContext(getContext(DeploymentEnvironmentDslContext.class), relationship)); + } else if (tokens.size() > 2 && isRelationshipKeywordOrArchetype(tokens.get(1)) && (inContext(ModelDslContext.class) || inContext(DeploymentEnvironmentDslContext.class) || inContext(ElementDslContext.class))) { // explicit without archetype: a -> b // explicit with archetype: a --https-> b diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java index 59f33b275..a35b8828f 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java @@ -11,6 +11,7 @@ class StructurizrDslTokens { static final String PERSON_TOKEN = "person"; static final String SOFTWARE_SYSTEM_TOKEN = "softwareSystem"; static final String RELATIONSHIP_TOKEN = "->"; + static final String NO_RELATIONSHIP_TOKEN = "-/>"; static final String CONTAINER_TOKEN = "container"; static final String COMPONENT_TOKEN = "component"; static final String GROUP_TOKEN = "group"; diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index 9bcc2beeb..b0c1b64f8 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -1753,4 +1753,53 @@ void test_colorSchemes() throws Exception { assertEquals("#ffffff", relationshipStyle.getColor()); } + @Test + void test_noRelationship() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/no-relationship.dsl")); + + Workspace workspace = parser.getWorkspace(); + + Container ui = (Container)parser.getIdentifiersRegister().getElement("ss.ui"); + Container backend = (Container)parser.getIdentifiersRegister().getElement("ss.backend"); + + // environment One: ui -> backend + ContainerInstance uiInstance = workspace.getModel().getElements().stream().filter(e -> e instanceof ContainerInstance).map(e -> (ContainerInstance)e).filter(ci -> ci.getEnvironment().equals("One") && ci.getContainer().equals(ui)).findFirst().get(); + ContainerInstance backendInstance = workspace.getModel().getElements().stream().filter(e -> e instanceof ContainerInstance).map(e -> (ContainerInstance)e).filter(ci -> ci.getEnvironment().equals("One") && ci.getContainer().equals(backend)).findFirst().get(); + + assertNotNull(uiInstance); + assertNotNull(backendInstance); + assertTrue(uiInstance.hasEfferentRelationshipWith(backendInstance)); + + // environment Two: ui -> load balancer -> backend + uiInstance = workspace.getModel().getElements().stream().filter(e -> e instanceof ContainerInstance).map(e -> (ContainerInstance)e).filter(ci -> ci.getEnvironment().equals("Two") && ci.getContainer().equals(ui)).findFirst().get(); + backendInstance = workspace.getModel().getElements().stream().filter(e -> e instanceof ContainerInstance).map(e -> (ContainerInstance)e).filter(ci -> ci.getEnvironment().equals("Two") && ci.getContainer().equals(backend)).findFirst().get(); + InfrastructureNode loadBalancer = workspace.getModel().getElements().stream().filter(e -> e instanceof InfrastructureNode).map(e -> (InfrastructureNode)e).filter(ci -> ci.getEnvironment().equals("Two")).findFirst().get(); + + assertNotNull(uiInstance); + assertNotNull(backendInstance); + assertFalse(uiInstance.hasEfferentRelationshipWith(backendInstance)); + + assertEquals("Makes API requests to", uiInstance.getEfferentRelationshipWith(loadBalancer).getDescription()); + assertEquals("JSON/HTTPS", uiInstance.getEfferentRelationshipWith(loadBalancer).getTechnology()); + + assertEquals("Forwards API requests to", loadBalancer.getEfferentRelationshipWith(backendInstance).getDescription()); + assertEquals("", loadBalancer.getEfferentRelationshipWith(backendInstance).getTechnology()); + + // environment Three: ui -> load balancer -> backend + uiInstance = workspace.getModel().getElements().stream().filter(e -> e instanceof ContainerInstance).map(e -> (ContainerInstance)e).filter(ci -> ci.getEnvironment().equals("Three") && ci.getContainer().equals(ui)).findFirst().get(); + backendInstance = workspace.getModel().getElements().stream().filter(e -> e instanceof ContainerInstance).map(e -> (ContainerInstance)e).filter(ci -> ci.getEnvironment().equals("Three") && ci.getContainer().equals(backend)).findFirst().get(); + loadBalancer = workspace.getModel().getElements().stream().filter(e -> e instanceof InfrastructureNode).map(e -> (InfrastructureNode)e).filter(ci -> ci.getEnvironment().equals("Three")).findFirst().get(); + + assertNotNull(uiInstance); + assertNotNull(backendInstance); + assertFalse(uiInstance.hasEfferentRelationshipWith(backendInstance)); + + assertEquals("Makes API requests to", uiInstance.getEfferentRelationshipWith(loadBalancer).getDescription()); + assertEquals("JSON/HTTPS", uiInstance.getEfferentRelationshipWith(loadBalancer).getTechnology()); + + assertEquals("Forwards API requests to", loadBalancer.getEfferentRelationshipWith(backendInstance).getDescription()); + assertEquals("", loadBalancer.getEfferentRelationshipWith(backendInstance).getTechnology()); + } + } \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ExplicitRelationshipParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ExplicitRelationshipParserTests.java index a1f540ea4..ac4b7cead 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ExplicitRelationshipParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ExplicitRelationshipParserTests.java @@ -391,4 +391,58 @@ void test_parse_AddsTheRelationshipFromAllContainerInstancesInTheDeploymentEnvir assertTrue(liveContainerInstance2.hasEfferentRelationshipWith(liveInfrastructureNode)); } + @Test + void test_parse_AddsAViaRelationshipUsingTheDescriptionAndTechnologyOfTheRemovedRelationship() { + SoftwareSystem ss = model.addSoftwareSystem("SS"); + Container a = ss.addContainer("A"); + Container b = ss.addContainer("B"); + Relationship relationship = a.uses(b, "Makes API calls using", "JSON/HTTPS"); + + DeploymentNode liveDeploymentNode = model.addDeploymentNode("live", "Deployment Node", "Description", "Technology"); + InfrastructureNode infrastructureNode = liveDeploymentNode.addInfrastructureNode("Infrastructure Node"); + ContainerInstance aInstance = liveDeploymentNode.add(a); + ContainerInstance bInstance = liveDeploymentNode.add(b); + + NoRelationshipInDeploymentEnvironmentDslContext context = new NoRelationshipInDeploymentEnvironmentDslContext(new DeploymentEnvironmentDslContext("live"), relationship); + context.setWorkspace(workspace); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("a", aInstance); + elements.register("infrastructureNode", infrastructureNode); + context.setIdentifierRegister(elements); + + parser.parse(context, tokens("a", "->", "infrastructureNode"), archetype); + + Relationship aInstanceToInfrastructureNode = aInstance.getEfferentRelationshipWith(infrastructureNode); + assertEquals("Makes API calls using", aInstanceToInfrastructureNode.getDescription()); + assertEquals("JSON/HTTPS", aInstanceToInfrastructureNode.getTechnology()); + } + + @Test + void test_parse_AddsAViaRelationshipUOverridingTheDescriptionAndTechnologyOfTheRemovedRelationship() { + SoftwareSystem ss = model.addSoftwareSystem("SS"); + Container a = ss.addContainer("A"); + Container b = ss.addContainer("B"); + Relationship relationship = a.uses(b, "Makes API calls using", "JSON/HTTPS"); + + DeploymentNode liveDeploymentNode = model.addDeploymentNode("live", "Deployment Node", "Description", "Technology"); + InfrastructureNode infrastructureNode = liveDeploymentNode.addInfrastructureNode("Infrastructure Node"); + ContainerInstance aInstance = liveDeploymentNode.add(a); + ContainerInstance bInstance = liveDeploymentNode.add(b); + + NoRelationshipInDeploymentEnvironmentDslContext context = new NoRelationshipInDeploymentEnvironmentDslContext(new DeploymentEnvironmentDslContext("live"), relationship); + context.setWorkspace(workspace); + + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("a", aInstance); + elements.register("infrastructureNode", infrastructureNode); + context.setIdentifierRegister(elements); + + parser.parse(context, tokens("a", "->", "infrastructureNode", "New description", "New technology"), archetype); + + Relationship aInstanceToInfrastructureNode = aInstance.getEfferentRelationshipWith(infrastructureNode); + assertEquals("New description", aInstanceToInfrastructureNode.getDescription()); + assertEquals("New technology", aInstanceToInfrastructureNode.getTechnology()); + } + } \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/NoRelationshipParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/NoRelationshipParserTests.java new file mode 100644 index 000000000..58fcb32fd --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/NoRelationshipParserTests.java @@ -0,0 +1,331 @@ +package com.structurizr.dsl; + +import com.structurizr.model.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.*; + +class NoRelationshipParserTests extends AbstractTests { + + private static final String DEPLOYMENT_ENVIRONMENT = "live"; + + private final NoRelationshipParser parser = new NoRelationshipParser(); + private DeploymentEnvironmentDslContext context; + + @BeforeEach + void setUp() { + context = new DeploymentEnvironmentDslContext(DEPLOYMENT_ENVIRONMENT); + context.setWorkspace(workspace); + } + + @Test + void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parse(null, tokens("source", "-/>", "destination", "description", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: -/> [description]", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheDestinationIdentifierIsMissing() { + try { + parser.parse(null, tokens("source", "-/>")); + fail(); + } catch (Exception e) { + assertEquals("Not enough tokens, expected: -/> [description]", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheSourceElementIsNotDefined() { + try { + parser.parse(context, tokens("a", "-/>", "b")); + fail(); + } catch (Exception e) { + assertEquals("The source element \"a\" does not exist", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheSourceElementIsNotAStaticStructureElementInstance() { + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("a", model.addPerson("User")); + context.setIdentifierRegister(elements); + + try { + parser.parse(context, tokens("a", "->", "b")); + fail(); + } catch (Exception e) { + assertEquals("The source element \"a\" is not valid - expecting a software system, software system instance, container, or container instance", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheDestinationElementIsNotDefined() { + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("a", model.addSoftwareSystem("A")); + context.setIdentifierRegister(elements); + + try { + parser.parse(context, tokens("a", "-/>", "b")); + fail(); + } catch (Exception e) { + assertEquals("The destination element \"b\" does not exist", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenTheDestinationElementIsNotAStaticStructureElementInstance() { + IdentifiersRegister elements = new IdentifiersRegister(); + elements.register("a", model.addSoftwareSystem("A")); + elements.register("b", model.addPerson("User")); + context.setIdentifierRegister(elements); + + try { + parser.parse(context, tokens("a", "->", "b")); + fail(); + } catch (Exception e) { + assertEquals("The destination element \"b\" is not valid - expecting a software system, software system instance, container, or container instance", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenARelationshipDoesNotExist() { + IdentifiersRegister elements = new IdentifiersRegister(); + + SoftwareSystem a = model.addSoftwareSystem("A"); + SoftwareSystem b = model.addSoftwareSystem("B"); + + DeploymentNode deploymentNode = model.addDeploymentNode(DEPLOYMENT_ENVIRONMENT, "Deployment Node", "Description", "Technology"); + deploymentNode.add(a); + deploymentNode.add(b); + + elements.register("a", a); + elements.register("b", b); + context.setIdentifierRegister(elements); + + try { + parser.parse(context, tokens("a", "-/>", "b")); + fail(); + } catch (Exception e) { + assertEquals("A relationship between \"a\" and \"b\" does not exist", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenARelationshipWithDescriptionDoesNotExist() { + IdentifiersRegister elements = new IdentifiersRegister(); + + SoftwareSystem a = model.addSoftwareSystem("A"); + SoftwareSystem b = model.addSoftwareSystem("B"); + + DeploymentNode deploymentNode = model.addDeploymentNode(DEPLOYMENT_ENVIRONMENT, "Deployment Node", "Description", "Technology"); + deploymentNode.add(a); + deploymentNode.add(b); + + elements.register("a", a); + elements.register("b", b); + context.setIdentifierRegister(elements); + + try { + parser.parse(context, tokens("a", "-/>", "b", "Description")); + fail(); + } catch (Exception e) { + assertEquals("A relationship between \"a\" and \"b\" with description \"Description\" does not exist", e.getMessage()); + } + } + + @Test + void test_parse_RemovesAllRelationshipsBetweenSoftwareSystemInstances_WhenUsingSoftwareSystemInstanceIdentifiersAndNoDescription() { + IdentifiersRegister elements = new IdentifiersRegister(); + + SoftwareSystem a = model.addSoftwareSystem("A"); + SoftwareSystem b = model.addSoftwareSystem("B"); + a.uses(b, "Description 1", "Technology"); + a.uses(b, "Description 2", "Technology"); + + DeploymentNode deploymentNode = model.addDeploymentNode(DEPLOYMENT_ENVIRONMENT, "Deployment Node", "Description", "Technology"); + SoftwareSystemInstance aInstance = deploymentNode.add(a); + SoftwareSystemInstance bInstance = deploymentNode.add(b); + Relationship relationship1 = aInstance.getEfferentRelationshipWith(bInstance, "Description 1"); + Relationship relationship2 = aInstance.getEfferentRelationshipWith(bInstance, "Description 2"); + + elements.register("aInstance", aInstance); + elements.register("bInstance", bInstance); + context.setIdentifierRegister(elements); + + assertTrue(aInstance.hasEfferentRelationshipWith(bInstance, "Description 1")); + assertTrue(aInstance.hasEfferentRelationshipWith(bInstance, "Description 2")); + + Set relationshipsRemoved = parser.parse(context, tokens("aInstance", "-/>", "bInstance")); + + assertFalse(aInstance.hasEfferentRelationshipWith(bInstance, "Description 1")); + assertFalse(aInstance.hasEfferentRelationshipWith(bInstance, "Description 2")); + assertEquals(2, relationshipsRemoved.size()); + assertTrue(relationshipsRemoved.contains(relationship1)); + assertTrue(relationshipsRemoved.contains(relationship2)); + } + + @Test + void test_parse_RemovesAllRelationshipsBetweenSoftwareSystemInstances_WhenUsingSoftwareSystemIdentifiersAndNoDescription() { + IdentifiersRegister elements = new IdentifiersRegister(); + + SoftwareSystem a = model.addSoftwareSystem("A"); + SoftwareSystem b = model.addSoftwareSystem("B"); + a.uses(b, "Description 1", "Technology"); + a.uses(b, "Description 2", "Technology"); + + DeploymentNode deploymentNode = model.addDeploymentNode(DEPLOYMENT_ENVIRONMENT, "Deployment Node", "Description", "Technology"); + SoftwareSystemInstance aInstance = deploymentNode.add(a); + SoftwareSystemInstance bInstance = deploymentNode.add(b); + Relationship relationship1 = aInstance.getEfferentRelationshipWith(bInstance, "Description 1"); + Relationship relationship2 = aInstance.getEfferentRelationshipWith(bInstance, "Description 2"); + + elements.register("a", a); + elements.register("b", b); + context.setIdentifierRegister(elements); + + assertTrue(aInstance.hasEfferentRelationshipWith(bInstance, "Description 1")); + assertTrue(aInstance.hasEfferentRelationshipWith(bInstance, "Description 2")); + + Set relationshipsRemoved = parser.parse(context, tokens("a", "-/>", "b")); + + assertFalse(aInstance.hasEfferentRelationshipWith(bInstance, "Description 1")); + assertFalse(aInstance.hasEfferentRelationshipWith(bInstance, "Description 2")); + assertEquals(2, relationshipsRemoved.size()); + assertTrue(relationshipsRemoved.contains(relationship1)); + assertTrue(relationshipsRemoved.contains(relationship2)); + } + + @Test + void test_parse_RemovesAllRelationshipsBetweenContainerInstances_WhenUsingContainerIdentifiersAndNoDescription() { + IdentifiersRegister elements = new IdentifiersRegister(); + + SoftwareSystem ss = model.addSoftwareSystem("A"); + Container a = ss.addContainer("A"); + Container b = ss.addContainer("B"); + a.uses(b, "Description 1", "Technology"); + a.uses(b, "Description 2", "Technology"); + + DeploymentNode deploymentNode = model.addDeploymentNode(DEPLOYMENT_ENVIRONMENT, "Deployment Node", "Description", "Technology"); + ContainerInstance aInstance = deploymentNode.add(a); + ContainerInstance bInstance = deploymentNode.add(b); + Relationship relationship1 = aInstance.getEfferentRelationshipWith(bInstance, "Description 1"); + Relationship relationship2 = aInstance.getEfferentRelationshipWith(bInstance, "Description 2"); + + elements.register("a", a); + elements.register("b", b); + context.setIdentifierRegister(elements); + + assertTrue(aInstance.hasEfferentRelationshipWith(bInstance, "Description 1")); + assertTrue(aInstance.hasEfferentRelationshipWith(bInstance, "Description 2")); + + Set relationshipsRemoved = parser.parse(context, tokens("a", "-/>", "b")); + + assertFalse(aInstance.hasEfferentRelationshipWith(bInstance, "Description 1")); + assertFalse(aInstance.hasEfferentRelationshipWith(bInstance, "Description 2")); + assertEquals(2, relationshipsRemoved.size()); + assertTrue(relationshipsRemoved.contains(relationship1)); + assertTrue(relationshipsRemoved.contains(relationship2)); + } + + @Test + void test_parse_RemovesTheRelationshipBetweenSoftwareSystemInstances_WhenUsingSoftwareSystemInstanceIdentifiersAndADescription() { + IdentifiersRegister elements = new IdentifiersRegister(); + + SoftwareSystem a = model.addSoftwareSystem("A"); + SoftwareSystem b = model.addSoftwareSystem("B"); + a.uses(b, "Description 1", "Technology"); + a.uses(b, "Description 2", "Technology"); + + DeploymentNode deploymentNode = model.addDeploymentNode(DEPLOYMENT_ENVIRONMENT, "Deployment Node", "Description", "Technology"); + SoftwareSystemInstance aInstance = deploymentNode.add(a); + SoftwareSystemInstance bInstance = deploymentNode.add(b); + Relationship relationship1 = aInstance.getEfferentRelationshipWith(bInstance, "Description 1"); + Relationship relationship2 = aInstance.getEfferentRelationshipWith(bInstance, "Description 2"); + + elements.register("aInstance", aInstance); + elements.register("bInstance", bInstance); + context.setIdentifierRegister(elements); + + assertTrue(aInstance.hasEfferentRelationshipWith(bInstance, "Description 1")); + assertTrue(aInstance.hasEfferentRelationshipWith(bInstance, "Description 2")); + + Set relationshipsRemoved = parser.parse(context, tokens("aInstance", "-/>", "bInstance", "Description 1")); + + assertFalse(aInstance.hasEfferentRelationshipWith(bInstance, "Description 1")); + assertTrue(aInstance.hasEfferentRelationshipWith(bInstance, "Description 2")); + assertEquals(1, relationshipsRemoved.size()); + assertTrue(relationshipsRemoved.contains(relationship1)); + assertFalse(relationshipsRemoved.contains(relationship2)); + } + + @Test + void test_parse_RemovesTheRelationshipBetweenSoftwareSystemInstances_WhenUsingSoftwareSystemIdentifiersAndADescription() { + IdentifiersRegister elements = new IdentifiersRegister(); + + SoftwareSystem a = model.addSoftwareSystem("A"); + SoftwareSystem b = model.addSoftwareSystem("B"); + a.uses(b, "Description 1", "Technology"); + a.uses(b, "Description 2", "Technology"); + + DeploymentNode deploymentNode = model.addDeploymentNode(DEPLOYMENT_ENVIRONMENT, "Deployment Node", "Description", "Technology"); + SoftwareSystemInstance aInstance = deploymentNode.add(a); + SoftwareSystemInstance bInstance = deploymentNode.add(b); + Relationship relationship1 = aInstance.getEfferentRelationshipWith(bInstance, "Description 1"); + Relationship relationship2 = aInstance.getEfferentRelationshipWith(bInstance, "Description 2"); + + elements.register("a", a); + elements.register("b", b); + context.setIdentifierRegister(elements); + + assertTrue(aInstance.hasEfferentRelationshipWith(bInstance, "Description 1")); + assertTrue(aInstance.hasEfferentRelationshipWith(bInstance, "Description 2")); + + Set relationshipsRemoved = parser.parse(context, tokens("a", "-/>", "b", "Description 1")); + + assertFalse(aInstance.hasEfferentRelationshipWith(bInstance, "Description 1")); + assertTrue(aInstance.hasEfferentRelationshipWith(bInstance, "Description 2")); + assertEquals(1, relationshipsRemoved.size()); + assertTrue(relationshipsRemoved.contains(relationship1)); + assertFalse(relationshipsRemoved.contains(relationship2)); + } + + @Test + void test_parse_RemovesTheRelationshipBetweenContainerInstances_WhenUsingContainerIdentifiersAndADescription() { + IdentifiersRegister elements = new IdentifiersRegister(); + + SoftwareSystem ss = model.addSoftwareSystem("A"); + Container a = ss.addContainer("A"); + Container b = ss.addContainer("B"); + a.uses(b, "Description 1", "Technology"); + a.uses(b, "Description 2", "Technology"); + + DeploymentNode deploymentNode = model.addDeploymentNode(DEPLOYMENT_ENVIRONMENT, "Deployment Node", "Description", "Technology"); + ContainerInstance aInstance = deploymentNode.add(a); + ContainerInstance bInstance = deploymentNode.add(b); + Relationship relationship1 = aInstance.getEfferentRelationshipWith(bInstance, "Description 1"); + Relationship relationship2 = aInstance.getEfferentRelationshipWith(bInstance, "Description 2"); + + elements.register("a", a); + elements.register("b", b); + context.setIdentifierRegister(elements); + + assertTrue(aInstance.hasEfferentRelationshipWith(bInstance, "Description 1")); + assertTrue(aInstance.hasEfferentRelationshipWith(bInstance, "Description 2")); + + Set relationshipsRemoved = parser.parse(context, tokens("a", "-/>", "b", "Description 1")); + + assertFalse(aInstance.hasEfferentRelationshipWith(bInstance, "Description 1")); + assertTrue(aInstance.hasEfferentRelationshipWith(bInstance, "Description 2")); + assertEquals(1, relationshipsRemoved.size()); + assertTrue(relationshipsRemoved.contains(relationship1)); + assertFalse(relationshipsRemoved.contains(relationship2)); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/no-relationship.dsl b/structurizr-dsl/src/test/resources/dsl/no-relationship.dsl new file mode 100644 index 000000000..fdf96ed8a --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/no-relationship.dsl @@ -0,0 +1,88 @@ +workspace { + + !identifiers hierarchical + + model { + ss = softwareSystem "Software System" { + ui = container "UI" "Description" "JavaScript and React" + backend = container "Backend" "Description" "Spring Boot" + + ui -> backend "Makes API requests to" "JSON/HTTPS" + } + + // ui -> backend + one = deploymentEnvironment "One" { + deploymentNode "Developer's Computer" { + deploymentNode "Web Browser" { + instanceOf ss.ui + } + instanceOf ss.backend + } + } + + // ui -> loadbalancer -> backend + // configured via container identifiers + two = deploymentEnvironment "Two" { + deploymentNode "User's Computer" { + deploymentNode "Web Browser" { + instanceOf ss.ui + } + } + dc = deploymentNode "Data Center" { + loadBalancer = infrastructureNode "Load Balancer" + deploymentNode "Server" { + instanceOf ss.backend + } + } + + ss.ui -/> ss.backend { + ss.ui -> dc.loadBalancer + dc.loadBalancer -> ss.backend "Forwards API requests to" "" + } + } + + // ui -> loadbalancer -> backend + // configured via container instance identifiers + three = deploymentEnvironment "Three" { + computer = deploymentNode "User's Computer" { + webbrowser = deploymentNode "Web Browser" { + ui = instanceOf ss.ui + } + } + datacenter = deploymentNode "Data Center" { + loadbalancer = infrastructureNode "Load Balancer" + server = deploymentNode "Server" { + backend = instanceOf ss.backend + } + } + + computer.webbrowser.ui -/> datacenter.server.backend { + computer.webbrowser.ui -> datacenter.loadbalancer + datacenter.loadbalancer -> datacenter.server.backend "Forwards API requests to" "" + } + } + } + + views { + container ss { + include * + autolayout lr + } + + deployment ss one { + include * + autolayout lr + } + + deployment ss two { + include * + autolayout lr + } + + deployment ss three { + include * + autolayout lr + } + } + +} \ No newline at end of file From 324bb7f6ddcf469aa570b0d30901731ea486cc82 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 26 Aug 2025 15:07:24 +0100 Subject: [PATCH 656/717] Fixes #437. --- changelog.md | 1 + .../main/java/com/structurizr/component/ComponentFinder.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 1eadaa858..f2741e277 100644 --- a/changelog.md +++ b/changelog.md @@ -2,6 +2,7 @@ ## v4.2.0 (unreleased) +- structurizr-java: Fixes https://github.com/structurizr/java/issues/437 (Make ComponentFinder.run() not fail on empty Set). - structurizr-dsl: Adds support for `iconPosition` on element styles (options are `Top`, `Bottom`, `Left`). - structurizr-dsl: Adds support for defining element and relationship styles for light and dark mode. - structurizr-dsl: Adds a `Bucket` shape. diff --git a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinder.java b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinder.java index c73e0228b..387b66711 100644 --- a/structurizr-component/src/main/java/com/structurizr/component/ComponentFinder.java +++ b/structurizr-component/src/main/java/com/structurizr/component/ComponentFinder.java @@ -61,7 +61,7 @@ public Set run() { for (ComponentFinderStrategy componentFinderStrategy : componentFinderStrategies) { Set set = componentFinderStrategy.run(typeRepository); if (set.isEmpty()) { - throw new RuntimeException("No components were found by " + componentFinderStrategy); + log.debug("No components were found by " + componentFinderStrategy); } discoveredComponents.addAll(set); } From aa6c734a603b668ccec397b18b15ce3ed2ac18a5 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 28 Aug 2025 08:25:17 +0100 Subject: [PATCH 657/717] Adds support for a `jump` property on relationship styles. --- changelog.md | 1 + .../structurizr/view/RelationshipStyle.java | 17 ++++++++ .../structurizr/view/RelationshipView.java | 22 ++++++++++ .../dsl/RelationshipStyleParser.java | 23 +++++++++++ .../structurizr/dsl/StructurizrDslParser.java | 3 ++ .../structurizr/dsl/StructurizrDslTokens.java | 1 + .../dsl/RelationshipStyleParserTests.java | 41 ++++++++++++++++++- 7 files changed, 107 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index f2741e277..6f6955154 100644 --- a/changelog.md +++ b/changelog.md @@ -12,6 +12,7 @@ - structurizr-dsl: Relationships to/from software system/container instances can be now defined by using the software system/container identifier. - structurizr-dsl: Fixes https://github.com/structurizr/java/issues/435 (Relationship archetype not applied to implicit-source relationships). - structurizr-dsl: Adds support for removing relationships between software system instance/container instances, with a view to redefining them via infrastructure nodes. +- structurizr-dsl: Adds support for a `jump` property on relationship styles. ## v4.1.0 (28th May 2025) diff --git a/structurizr-core/src/main/java/com/structurizr/view/RelationshipStyle.java b/structurizr-core/src/main/java/com/structurizr/view/RelationshipStyle.java index 3fbae753e..be966314e 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/RelationshipStyle.java +++ b/structurizr-core/src/main/java/com/structurizr/view/RelationshipStyle.java @@ -39,6 +39,10 @@ public final class RelationshipStyle extends AbstractStyle { @JsonInclude(value = JsonInclude.Include.NON_NULL) private Routing routing; + /** whether the line should jump over others */ + @JsonInclude(value = JsonInclude.Include.NON_NULL) + private Boolean jump; + /** the position of the annotation along the line; 0 (start) to 100 (end) */ @JsonInclude(value = JsonInclude.Include.NON_NULL) private Integer position; @@ -142,6 +146,19 @@ public RelationshipStyle routing(Routing routing) { return this; } + public Boolean getJump() { + return jump; + } + + public void setJump(Boolean jump) { + this.jump = jump; + } + + public RelationshipStyle jump(boolean jump) { + setJump(jump); + return this; + } + public Integer getFontSize() { return fontSize; } diff --git a/structurizr-core/src/main/java/com/structurizr/view/RelationshipView.java b/structurizr-core/src/main/java/com/structurizr/view/RelationshipView.java index 9bee72b15..b9305de3e 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/RelationshipView.java +++ b/structurizr-core/src/main/java/com/structurizr/view/RelationshipView.java @@ -29,6 +29,9 @@ public final class RelationshipView implements PropertyHolder, Comparable + RelationshipStyle style = context.getStyle(); + + if (tokens.hasMoreThan(FIRST_PROPERTY_INDEX)) { + throw new RuntimeException("Too many tokens, expected: jump "); + } + + if (tokens.includes(FIRST_PROPERTY_INDEX)) { + String jump = tokens.get(1); + + if ("true".equalsIgnoreCase(jump)) { + style.setJump(true); + } else if ("false".equalsIgnoreCase(jump)) { + style.setJump(false); + } else { + throw new RuntimeException("Jump must be true or false"); + } + } else { + throw new RuntimeException("Expected: jump "); + } + } + } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index b2ce23e56..58fcc3e08 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -901,6 +901,9 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn } else if (RELATIONSHIP_STYLE_ROUTING_TOKEN.equalsIgnoreCase(firstToken) && inContext(RelationshipStyleDslContext.class)) { new RelationshipStyleParser().parseRouting(getContext(RelationshipStyleDslContext.class), tokens); + } else if (RELATIONSHIP_STYLE_JUMP_TOKEN.equalsIgnoreCase(firstToken) && inContext(RelationshipStyleDslContext.class)) { + new RelationshipStyleParser().parseJump(getContext(RelationshipStyleDslContext.class), tokens); + } else if (DEPLOYMENT_ENVIRONMENT_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelDslContext.class)) { String environment = new DeploymentEnvironmentParser().parse(tokens.withoutContextStartToken()); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java index a35b8828f..9ddcd5958 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslTokens.java @@ -90,6 +90,7 @@ class StructurizrDslTokens { static final String RELATIONSHIP_STYLE_DASHED_TOKEN = "dashed"; static final String RELATIONSHIP_STYLE_OPACITY_TOKEN = "opacity"; static final String RELATIONSHIP_STYLE_ROUTING_TOKEN = "routing"; + static final String RELATIONSHIP_STYLE_JUMP_TOKEN = "jump"; static final String RELATIONSHIP_STYLE_LINE_STYLE_TOKEN = "style"; static final String RELATIONSHIP_STYLE_FONT_SIZE_TOKEN = "fontSize"; static final String RELATIONSHIP_STYLE_WIDTH_TOKEN = "width"; diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/RelationshipStyleParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/RelationshipStyleParserTests.java index 645d747b2..d59d7081d 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/RelationshipStyleParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/RelationshipStyleParserTests.java @@ -1,6 +1,5 @@ package com.structurizr.dsl; -import com.structurizr.view.Border; import com.structurizr.view.LineStyle; import com.structurizr.view.RelationshipStyle; import com.structurizr.view.Routing; @@ -396,4 +395,44 @@ void test_parseRouting_SetsTheRouting() { assertEquals(Routing.Curved, relationshipStyle.getRouting()); } + @Test + void test_parseJump_ThrowsAnException_WhenThereAreTooManyTokens() { + try { + parser.parseJump(relationshipStyleDslContext(), tokens("jump", "boolean", "extra")); + fail(); + } catch (Exception e) { + assertEquals("Too many tokens, expected: jump ", e.getMessage()); + } + } + + @Test + void test_parseJump_ThrowsAnException_WhenTheValueIsMissing() { + try { + parser.parseJump(relationshipStyleDslContext(), tokens("jump")); + fail(); + } catch (Exception e) { + assertEquals("Expected: jump ", e.getMessage()); + } + } + + @Test + void test_parseJump_ThrowsAnException_WhenTheValueIsNotValid() { + try { + parser.parseJump(relationshipStyleDslContext(), tokens("jump", "abc")); + fail(); + } catch (Exception e) { + assertEquals("Jump must be true or false", e.getMessage()); + } + } + + @Test + void test_parseJump_SetsTheJump() { + RelationshipStyleDslContext context = relationshipStyleDslContext(); + parser.parseJump(context, tokens("jump", "false")); + assertEquals(false, relationshipStyle.getJump()); + + parser.parseJump(context, tokens("jump", "true")); + assertEquals(true, relationshipStyle.getJump()); + } + } \ No newline at end of file From 768ea227d07fab8a92f57eed70b645356420bb0b Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 30 Aug 2025 09:54:49 +0100 Subject: [PATCH 658/717] Inspections can be disabled via a workspace property. --- changelog.md | 1 + .../com/structurizr/inspection/DefaultInspector.java | 8 +++++--- .../inspection/DefaultInspectorTests.java | 12 ++++++++++++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index 6f6955154..5b0d7e937 100644 --- a/changelog.md +++ b/changelog.md @@ -13,6 +13,7 @@ - structurizr-dsl: Fixes https://github.com/structurizr/java/issues/435 (Relationship archetype not applied to implicit-source relationships). - structurizr-dsl: Adds support for removing relationships between software system instance/container instances, with a view to redefining them via infrastructure nodes. - structurizr-dsl: Adds support for a `jump` property on relationship styles. +- structurizr-inspection: Adds a way to disable inspections via a workspace property named `structurizr.inspection` (`false` to disable). ## v4.1.0 (28th May 2025) diff --git a/structurizr-inspection/src/main/java/com/structurizr/inspection/DefaultInspector.java b/structurizr-inspection/src/main/java/com/structurizr/inspection/DefaultInspector.java index daa269cad..c038a2369 100644 --- a/structurizr-inspection/src/main/java/com/structurizr/inspection/DefaultInspector.java +++ b/structurizr-inspection/src/main/java/com/structurizr/inspection/DefaultInspector.java @@ -15,9 +15,11 @@ public class DefaultInspector extends Inspector { public DefaultInspector(Workspace workspace) { super(workspace); - runWorkspaceInspections(); - runModelInspections(); - runViewInspections(); + if (!"false".equalsIgnoreCase(workspace.getProperties().get("structurizr.inspection"))) { + runWorkspaceInspections(); + runModelInspections(); + runViewInspections(); + } } private void runWorkspaceInspections() { diff --git a/structurizr-inspection/src/test/java/com/structurizr/inspection/DefaultInspectorTests.java b/structurizr-inspection/src/test/java/com/structurizr/inspection/DefaultInspectorTests.java index 457e66325..562a9affd 100644 --- a/structurizr-inspection/src/test/java/com/structurizr/inspection/DefaultInspectorTests.java +++ b/structurizr-inspection/src/test/java/com/structurizr/inspection/DefaultInspectorTests.java @@ -36,6 +36,18 @@ void test_EmptyWorkspace() { assertEquals("This workspace has no views.", violation.getMessage()); } + @Test + void test_EmptyWorkspace_WhenInspectionsAreDisabled() { + Workspace workspace = new Workspace("Name", "Description"); + workspace.addProperty("structurizr.inspection", "false"); + + DefaultInspector inspector = new DefaultInspector(workspace); + List violations = inspector.getViolations(); + + assertEquals(0, inspector.getNumberOfInspections()); + assertEquals(0, violations.size()); + } + @Test void test() throws Exception { StructurizrDslParser parser = new StructurizrDslParser(); From 3710689b7a31353e627c6817f534152fea810235 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 30 Aug 2025 09:56:07 +0100 Subject: [PATCH 659/717] Default inspector adds a summary of error/warning/info/ignore counts as workspace properties. --- changelog.md | 1 + .../structurizr/inspection/DefaultInspector.java | 13 +++++++++++++ .../inspection/DefaultInspectorTests.java | 13 ++++++++++++- 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 5b0d7e937..7e820fd14 100644 --- a/changelog.md +++ b/changelog.md @@ -14,6 +14,7 @@ - structurizr-dsl: Adds support for removing relationships between software system instance/container instances, with a view to redefining them via infrastructure nodes. - structurizr-dsl: Adds support for a `jump` property on relationship styles. - structurizr-inspection: Adds a way to disable inspections via a workspace property named `structurizr.inspection` (`false` to disable). +- structurizr-inspection: Default inspector adds a summary of error/warning/info/ignore counts as workspace properties. ## v4.1.0 (28th May 2025) diff --git a/structurizr-inspection/src/main/java/com/structurizr/inspection/DefaultInspector.java b/structurizr-inspection/src/main/java/com/structurizr/inspection/DefaultInspector.java index c038a2369..e691825ab 100644 --- a/structurizr-inspection/src/main/java/com/structurizr/inspection/DefaultInspector.java +++ b/structurizr-inspection/src/main/java/com/structurizr/inspection/DefaultInspector.java @@ -10,8 +10,15 @@ import com.structurizr.model.*; import com.structurizr.view.*; +import java.util.List; + public class DefaultInspector extends Inspector { + private static final String INSPECTION_SUMMARY_NUMBER_OF_ERROR = "structurizr.inspection.error"; + private static final String INSPECTION_SUMMARY_NUMBER_OF_WARNING = "structurizr.inspection.warning"; + private static final String INSPECTION_SUMMARY_NUMBER_OF_INFO = "structurizr.inspection.info"; + private static final String INSPECTION_SUMMARY_NUMBER_OF_IGNORE = "structurizr.inspection.ignore"; + public DefaultInspector(Workspace workspace) { super(workspace); @@ -19,6 +26,12 @@ public DefaultInspector(Workspace workspace) { runWorkspaceInspections(); runModelInspections(); runViewInspections(); + + List violations = getViolations(); + workspace.addProperty(INSPECTION_SUMMARY_NUMBER_OF_ERROR, "" + violations.stream().filter(r -> r.getSeverity() == Severity.ERROR).count()); + workspace.addProperty(INSPECTION_SUMMARY_NUMBER_OF_WARNING, "" + violations.stream().filter(r -> r.getSeverity() == Severity.WARNING).count()); + workspace.addProperty(INSPECTION_SUMMARY_NUMBER_OF_INFO, "" + violations.stream().filter(r -> r.getSeverity() == Severity.INFO).count()); + workspace.addProperty(INSPECTION_SUMMARY_NUMBER_OF_IGNORE, "" + violations.stream().filter(r -> r.getSeverity() == Severity.IGNORE).count()); } } diff --git a/structurizr-inspection/src/test/java/com/structurizr/inspection/DefaultInspectorTests.java b/structurizr-inspection/src/test/java/com/structurizr/inspection/DefaultInspectorTests.java index 562a9affd..619f12d22 100644 --- a/structurizr-inspection/src/test/java/com/structurizr/inspection/DefaultInspectorTests.java +++ b/structurizr-inspection/src/test/java/com/structurizr/inspection/DefaultInspectorTests.java @@ -9,12 +9,14 @@ import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; public class DefaultInspectorTests { @Test void test_EmptyWorkspace() { - DefaultInspector inspector = new DefaultInspector(new Workspace("Name", "Description")); + Workspace workspace = new Workspace("Name", "Description"); + DefaultInspector inspector = new DefaultInspector(workspace); List violations = inspector.getViolations(); assertEquals(9, inspector.getNumberOfInspections()); @@ -34,6 +36,11 @@ void test_EmptyWorkspace() { assertEquals(Severity.ERROR, violation.getSeverity()); assertEquals("views.empty", violation.getType()); assertEquals("This workspace has no views.", violation.getMessage()); + + assertEquals("3", workspace.getProperties().get("structurizr.inspection.error")); + assertEquals("0", workspace.getProperties().get("structurizr.inspection.warning")); + assertEquals("0", workspace.getProperties().get("structurizr.inspection.info")); + assertEquals("0", workspace.getProperties().get("structurizr.inspection.ignore")); } @Test @@ -46,6 +53,10 @@ void test_EmptyWorkspace_WhenInspectionsAreDisabled() { assertEquals(0, inspector.getNumberOfInspections()); assertEquals(0, violations.size()); + assertNull(workspace.getProperties().get("structurizr.inspection.error")); + assertNull(workspace.getProperties().get("structurizr.inspection.warning")); + assertNull(workspace.getProperties().get("structurizr.inspection.info")); + assertNull(workspace.getProperties().get("structurizr.inspection.ignore")); } @Test From 3b737651be44b52ec6f8327aecf05a8f8d05bf3c Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 3 Sep 2025 09:13:13 +0100 Subject: [PATCH 660/717] structurizr-inspection: Fixes `model.deploymentnode.technology` (it was checking the description property rather than technology). --- changelog.md | 1 + .../inspection/model/DeploymentNodeTechnologyInspection.java | 2 +- .../model/DeploymentNodeTechnologyInspectionTests.java | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index 7e820fd14..2eabf5e2d 100644 --- a/changelog.md +++ b/changelog.md @@ -15,6 +15,7 @@ - structurizr-dsl: Adds support for a `jump` property on relationship styles. - structurizr-inspection: Adds a way to disable inspections via a workspace property named `structurizr.inspection` (`false` to disable). - structurizr-inspection: Default inspector adds a summary of error/warning/info/ignore counts as workspace properties. +- structurizr-inspection: Fixes `model.deploymentnode.technology` (it was checking the description property rather than technology). ## v4.1.0 (28th May 2025) diff --git a/structurizr-inspection/src/main/java/com/structurizr/inspection/model/DeploymentNodeTechnologyInspection.java b/structurizr-inspection/src/main/java/com/structurizr/inspection/model/DeploymentNodeTechnologyInspection.java index 189293528..8919c90df 100644 --- a/structurizr-inspection/src/main/java/com/structurizr/inspection/model/DeploymentNodeTechnologyInspection.java +++ b/structurizr-inspection/src/main/java/com/structurizr/inspection/model/DeploymentNodeTechnologyInspection.java @@ -13,7 +13,7 @@ public DeploymentNodeTechnologyInspection(Inspector inspector) { @Override protected Violation inspect(DeploymentNode deploymentNode) { - if (StringUtils.isNullOrEmpty(deploymentNode.getDescription())) { + if (StringUtils.isNullOrEmpty(deploymentNode.getTechnology())) { return violation("The " + terminologyFor(deploymentNode).toLowerCase() + " \"" + nameOf(deploymentNode) + "\" is missing a technology."); } diff --git a/structurizr-inspection/src/test/java/com/structurizr/inspection/model/DeploymentNodeTechnologyInspectionTests.java b/structurizr-inspection/src/test/java/com/structurizr/inspection/model/DeploymentNodeTechnologyInspectionTests.java index 2b46c8f75..dc7844b8e 100644 --- a/structurizr-inspection/src/test/java/com/structurizr/inspection/model/DeploymentNodeTechnologyInspectionTests.java +++ b/structurizr-inspection/src/test/java/com/structurizr/inspection/model/DeploymentNodeTechnologyInspectionTests.java @@ -17,7 +17,7 @@ @Test public void run_WithoutDescription() { Workspace workspace = new Workspace("Name", "Description"); - DeploymentNode deploymentNode = workspace.getModel().addDeploymentNode("Name"); + DeploymentNode deploymentNode = workspace.getModel().addDeploymentNode("Name", "Description", ""); Violation violation = new DeploymentNodeTechnologyInspection(new DefaultInspector(workspace)).run(deploymentNode); Assertions.assertEquals(Severity.ERROR, violation.getSeverity()); From 30c61e5a19f1dabe639c9462a361704c2650450e Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 3 Sep 2025 09:13:33 +0100 Subject: [PATCH 661/717] Adds `archetypes` to the set of permitted tokens. --- .../src/main/java/com/structurizr/dsl/ModelDslContext.java | 1 + 1 file changed, 1 insertion(+) diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelDslContext.java index 795354332..56460da9f 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ModelDslContext.java @@ -24,6 +24,7 @@ public ElementGroup getGroup() { @Override protected String[] getPermittedTokens() { return new String[] { + StructurizrDslTokens.ARCHETYPES_TOKEN, StructurizrDslTokens.IDENTIFIERS_TOKEN, StructurizrDslTokens.GROUP_TOKEN, StructurizrDslTokens.PERSON_TOKEN, From 8f8a4597c6161aac83e23f1bc2a324ad2a41a34a Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 4 Sep 2025 12:34:59 +0100 Subject: [PATCH 662/717] structurizr-import: Adds support for `plantuml.inline`, `mermaid.inline`, and `kroki.inline` properties to inline the resulting PNG/SVG file into the workspace. --- changelog.md | 1 + .../java/com/structurizr/util/ImageUtils.java | 33 ++++++++++++ structurizr-import/build.gradle | 15 +++++- .../diagrams/kroki/KrokiImporter.java | 18 +++++-- .../diagrams/mermaid/MermaidImporter.java | 16 +++++- .../diagrams/plantuml/PlantUMLImporter.java | 16 +++++- .../diagrams/kroki/KrokiImporterTests.java | 51 ++++++++++++++++--- .../mermaid/MermaidImporterTests.java | 39 ++++++++++++++ .../plantuml/PlantUMLImporterTests.java | 29 +++++++++++ 9 files changed, 204 insertions(+), 14 deletions(-) diff --git a/changelog.md b/changelog.md index 2eabf5e2d..43d0c76ea 100644 --- a/changelog.md +++ b/changelog.md @@ -13,6 +13,7 @@ - structurizr-dsl: Fixes https://github.com/structurizr/java/issues/435 (Relationship archetype not applied to implicit-source relationships). - structurizr-dsl: Adds support for removing relationships between software system instance/container instances, with a view to redefining them via infrastructure nodes. - structurizr-dsl: Adds support for a `jump` property on relationship styles. +- structurizr-import: Adds support for `plantuml.inline`, `mermaid.inline`, and `kroki.inline` properties to inline the resulting PNG/SVG file into the workspace. - structurizr-inspection: Adds a way to disable inspections via a workspace property named `structurizr.inspection` (`false` to disable). - structurizr-inspection: Default inspector adds a summary of error/warning/info/ignore counts as workspace properties. - structurizr-inspection: Fixes `model.deploymentnode.technology` (it was checking the description property rather than technology). diff --git a/structurizr-core/src/main/java/com/structurizr/util/ImageUtils.java b/structurizr-core/src/main/java/com/structurizr/util/ImageUtils.java index 21cc4f25a..bed31efc7 100644 --- a/structurizr-core/src/main/java/com/structurizr/util/ImageUtils.java +++ b/structurizr-core/src/main/java/com/structurizr/util/ImageUtils.java @@ -8,6 +8,9 @@ import java.io.IOException; import java.net.URL; import java.net.URLConnection; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; import java.nio.file.Files; import java.util.Base64; @@ -124,6 +127,36 @@ public static String getImageAsDataUri(File file) throws IOException { return DATA_URI_PREFIX + contentType + ";base64," + base64Content; } + public static String getSvgAsDataUri(@Nonnull URL url) throws Exception { + HttpRequest request = HttpRequest.newBuilder() + .uri(url.toURI()) + .header("accept", CONTENT_TYPE_IMAGE_SVG) + .build(); + HttpClient client = HttpClient.newBuilder() + .followRedirects(HttpClient.Redirect.ALWAYS) + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + String svg = response.body(); + + return DATA_URI_PREFIX + CONTENT_TYPE_IMAGE_SVG + ";base64," + Base64.getEncoder().encodeToString(svg.getBytes()); + } + + public static String getPngAsDataUri(@Nonnull URL url) throws Exception { + HttpRequest request = HttpRequest.newBuilder() + .uri(url.toURI()) + .header("accept", CONTENT_TYPE_IMAGE_PNG) + .build(); + HttpClient client = HttpClient.newBuilder() + .followRedirects(HttpClient.Redirect.ALWAYS) + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofByteArray()); + byte[] png = response.body(); + + return DATA_URI_PREFIX + CONTENT_TYPE_IMAGE_PNG + ";base64," + Base64.getEncoder().encodeToString(png); + } + public static void validateImage(String imageDescriptor) { if (StringUtils.isNullOrEmpty(imageDescriptor)) { return; diff --git a/structurizr-import/build.gradle b/structurizr-import/build.gradle index 7b5c3b32f..d1f719fe1 100644 --- a/structurizr-import/build.gradle +++ b/structurizr-import/build.gradle @@ -4,4 +4,17 @@ dependencies { } -description = 'Utilities to import diagrams and documentation into a Structurizr workspace' \ No newline at end of file +description = 'Utilities to import diagrams and documentation into a Structurizr workspace' + +test { + useJUnitPlatform { + excludeTags "IntegrationTest" + } +} + +tasks.register("integrationTest", Test) { + useJUnitPlatform { + includeTags "IntegrationTest" + } + mustRunAfter check +} \ No newline at end of file diff --git a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/kroki/KrokiImporter.java b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/kroki/KrokiImporter.java index 6c01587e4..5277bd99b 100644 --- a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/kroki/KrokiImporter.java +++ b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/kroki/KrokiImporter.java @@ -1,17 +1,20 @@ package com.structurizr.importer.diagrams.kroki; import com.structurizr.importer.diagrams.AbstractDiagramImporter; +import com.structurizr.util.ImageUtils; import com.structurizr.util.StringUtils; import com.structurizr.view.ImageView; import java.io.File; +import java.net.URL; import java.nio.charset.StandardCharsets; import java.nio.file.Files; public class KrokiImporter extends AbstractDiagramImporter { - private static final String KROKI_URL_PROPERTY = "kroki.url"; - private static final String KROKI_FORMAT_PROPERTY = "kroki.format"; + public static final String KROKI_URL_PROPERTY = "kroki.url"; + public static final String KROKI_FORMAT_PROPERTY = "kroki.format"; + public static final String KROKI_INLINE_PROPERTY = "kroki.inline"; public void importDiagram(ImageView view, String format, File file) throws Exception { String content = new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8); @@ -38,7 +41,16 @@ public void importDiagram(ImageView view, String format, String content) throws String encodedDiagram = new KrokiEncoder().encode(content); String url = String.format("%s/%s/%s/%s", krokiServer, format, imageFormat, encodedDiagram); - view.setContent(url); + String inline = getViewOrViewSetProperty(view, KROKI_INLINE_PROPERTY); + if ("true".equals(inline)) { + if (imageFormat.equals(SVG_FORMAT)) { + view.setContent(ImageUtils.getSvgAsDataUri(new URL(url))); + } else { + view.setContent(ImageUtils.getPngAsDataUri(new URL(url))); + } + } else { + view.setContent(url); + } view.setContentType(CONTENT_TYPES_BY_FORMAT.get(imageFormat)); } diff --git a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/mermaid/MermaidImporter.java b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/mermaid/MermaidImporter.java index c7fa23b44..2be38290c 100644 --- a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/mermaid/MermaidImporter.java +++ b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/mermaid/MermaidImporter.java @@ -1,10 +1,12 @@ package com.structurizr.importer.diagrams.mermaid; import com.structurizr.importer.diagrams.AbstractDiagramImporter; +import com.structurizr.util.ImageUtils; import com.structurizr.util.StringUtils; import com.structurizr.view.ImageView; import java.io.File; +import java.net.URL; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -13,6 +15,7 @@ public class MermaidImporter extends AbstractDiagramImporter { public static final String MERMAID_URL_PROPERTY = "mermaid.url"; public static final String MERMAID_FORMAT_PROPERTY = "mermaid.format"; public static final String MERMAID_COMPRESS_PROPERTY = "mermaid.compress"; + public static final String MERMAID_INLINE_PROPERTY = "mermaid.inline"; public void importDiagram(ImageView view, File file) throws Exception { String content = new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8); @@ -21,7 +24,7 @@ public void importDiagram(ImageView view, File file) throws Exception { importDiagram(view, content); } - public void importDiagram(ImageView view, String content) { + public void importDiagram(ImageView view, String content) throws Exception { String mermaidServer = getViewOrViewSetProperty(view, MERMAID_URL_PROPERTY); if (StringUtils.isNullOrEmpty(mermaidServer)) { throw new IllegalArgumentException("Please define a view/viewset property named " + MERMAID_URL_PROPERTY + " to specify your Mermaid server"); @@ -49,7 +52,16 @@ public void importDiagram(ImageView view, String content) { url = String.format("%s/svg/%s", mermaidServer, encodedMermaid); } - view.setContent(url); + String inline = getViewOrViewSetProperty(view, MERMAID_INLINE_PROPERTY); + if ("true".equals(inline)) { + if (format.equals(SVG_FORMAT)) { + view.setContent(ImageUtils.getSvgAsDataUri(new URL(url))); + } else { + view.setContent(ImageUtils.getPngAsDataUri(new URL(url))); + } + } else { + view.setContent(url); + } view.setContentType(CONTENT_TYPES_BY_FORMAT.get(format)); } diff --git a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporter.java b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporter.java index 3ed7651e0..24176d02b 100644 --- a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporter.java +++ b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporter.java @@ -1,10 +1,13 @@ package com.structurizr.importer.diagrams.plantuml; import com.structurizr.importer.diagrams.AbstractDiagramImporter; +import com.structurizr.util.ImageUtils; import com.structurizr.util.StringUtils; +import com.structurizr.util.Url; import com.structurizr.view.ImageView; import java.io.File; +import java.net.URL; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -12,6 +15,7 @@ public class PlantUMLImporter extends AbstractDiagramImporter { public static final String PLANTUML_URL_PROPERTY = "plantuml.url"; public static final String PLANTUML_FORMAT_PROPERTY = "plantuml.format"; + public static final String PLANTUML_INLINE_PROPERTY = "plantuml.inline"; private static final String TITLE_STRING = "title "; private static final String NEWLINE = "\n"; @@ -39,7 +43,17 @@ public void importDiagram(ImageView view, String content) throws Exception { String encodedPlantUML = new PlantUMLEncoder().encode(content); String url = String.format("%s/%s/%s", plantUMLServer, format, encodedPlantUML); - view.setContent(url); + + String inline = getViewOrViewSetProperty(view, PLANTUML_INLINE_PROPERTY); + if ("true".equals(inline)) { + if (format.equals(SVG_FORMAT)) { + view.setContent(ImageUtils.getSvgAsDataUri(new URL(url))); + } else { + view.setContent(ImageUtils.getPngAsDataUri(new URL(url))); + } + } else { + view.setContent(url); + } view.setContentType(CONTENT_TYPES_BY_FORMAT.get(format)); String[] lines = content.split(NEWLINE); diff --git a/structurizr-import/src/test/java/com/structurizr/importer/diagrams/kroki/KrokiImporterTests.java b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/kroki/KrokiImporterTests.java index cd3b0c6a8..cfcb4d461 100644 --- a/structurizr-import/src/test/java/com/structurizr/importer/diagrams/kroki/KrokiImporterTests.java +++ b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/kroki/KrokiImporterTests.java @@ -2,6 +2,7 @@ import com.structurizr.Workspace; import com.structurizr.view.ImageView; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import java.io.File; @@ -13,7 +14,7 @@ public class KrokiImporterTests { @Test public void importDiagram() throws Exception { Workspace workspace = new Workspace("Name", "Description"); - workspace.getViews().getConfiguration().addProperty("kroki.url", "https://kroki.io"); + workspace.getViews().getConfiguration().addProperty(KrokiImporter.KROKI_URL_PROPERTY, "https://kroki.io"); ImageView view = workspace.getViews().createImageView("key"); new KrokiImporter().importDiagram(view, "graphviz", new File("./src/test/resources/diagrams/kroki/diagram.dot")); @@ -28,8 +29,8 @@ public void importDiagram() throws Exception { @Test public void importDiagram_AsPNG() throws Exception { Workspace workspace = new Workspace("Name", "Description"); - workspace.getViews().getConfiguration().addProperty("kroki.url", "https://kroki.io"); - workspace.getViews().getConfiguration().addProperty("kroki.format", "png"); + workspace.getViews().getConfiguration().addProperty(KrokiImporter.KROKI_URL_PROPERTY, "https://kroki.io"); + workspace.getViews().getConfiguration().addProperty(KrokiImporter.KROKI_FORMAT_PROPERTY, "png"); ImageView view = workspace.getViews().createImageView("key"); new KrokiImporter().importDiagram(view, "graphviz", new File("./src/test/resources/diagrams/kroki/diagram.dot")); @@ -41,11 +42,29 @@ public void importDiagram_AsPNG() throws Exception { assertEquals("image/png", view.getContentType()); } + @Test + @Tag("IntegrationTest") + public void importDiagram_AsInlinePNG() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getViews().getConfiguration().addProperty(KrokiImporter.KROKI_URL_PROPERTY, "https://kroki.io"); + workspace.getViews().getConfiguration().addProperty(KrokiImporter.KROKI_FORMAT_PROPERTY, "png"); + workspace.getViews().getConfiguration().addProperty(KrokiImporter.KROKI_INLINE_PROPERTY, "true"); + ImageView view = workspace.getViews().createImageView("key"); + + new KrokiImporter().importDiagram(view, "graphviz", new File("./src/test/resources/diagrams/kroki/diagram.dot")); + assertEquals("key", view.getKey()); + assertNull(view.getElement()); + assertNull(view.getElementId()); + assertEquals("diagram.dot", view.getTitle()); + assertEquals("data:image/png;base64,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", view.getContent()); + assertEquals("image/png", view.getContentType()); + } + @Test public void importDiagram_AsSVG() throws Exception { Workspace workspace = new Workspace("Name", "Description"); - workspace.getViews().getConfiguration().addProperty("kroki.url", "https://kroki.io"); - workspace.getViews().getConfiguration().addProperty("kroki.format", "svg"); + workspace.getViews().getConfiguration().addProperty(KrokiImporter.KROKI_URL_PROPERTY, "https://kroki.io"); + workspace.getViews().getConfiguration().addProperty(KrokiImporter.KROKI_FORMAT_PROPERTY, "svg"); ImageView view = workspace.getViews().createImageView("key"); new KrokiImporter().importDiagram(view, "graphviz", new File("./src/test/resources/diagrams/kroki/diagram.dot")); @@ -57,6 +76,24 @@ public void importDiagram_AsSVG() throws Exception { assertEquals("image/svg+xml", view.getContentType()); } + @Test + @Tag("IntegrationTest") + public void importDiagram_AsInlineSVG() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getViews().getConfiguration().addProperty(KrokiImporter.KROKI_URL_PROPERTY, "https://kroki.io"); + workspace.getViews().getConfiguration().addProperty(KrokiImporter.KROKI_FORMAT_PROPERTY, "svg"); + workspace.getViews().getConfiguration().addProperty(KrokiImporter.KROKI_INLINE_PROPERTY, "true"); + ImageView view = workspace.getViews().createImageView("key"); + + new KrokiImporter().importDiagram(view, "graphviz", new File("./src/test/resources/diagrams/kroki/diagram.dot")); + assertEquals("key", view.getKey()); + assertNull(view.getElement()); + assertNull(view.getElementId()); + assertEquals("diagram.dot", view.getTitle()); + assertEquals("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIKICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPgo8IS0tIEdlbmVyYXRlZCBieSBncmFwaHZpeiB2ZXJzaW9uIDkuMC4wICgyMDIzMDkxMS4xODI3KQogLS0+CjwhLS0gVGl0bGU6IEcgUGFnZXM6IDEgLS0+Cjxzdmcgd2lkdGg9Ijg5cHQiIGhlaWdodD0iMTE2cHQiCiB2aWV3Qm94PSIwLjAwIDAuMDAgODkuMzcgMTE2LjAwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj4KPGcgaWQ9ImdyYXBoMCIgY2xhc3M9ImdyYXBoIiB0cmFuc2Zvcm09InNjYWxlKDEgMSkgcm90YXRlKDApIHRyYW5zbGF0ZSg0IDExMikiPgo8dGl0bGU+RzwvdGl0bGU+Cjxwb2x5Z29uIGZpbGw9IndoaXRlIiBzdHJva2U9Im5vbmUiIHBvaW50cz0iLTQsNCAtNCwtMTEyIDg1LjM3LC0xMTIgODUuMzcsNCAtNCw0Ii8+CjwhLS0gSGVsbG8gLS0+CjxnIGlkPSJub2RlMSIgY2xhc3M9Im5vZGUiPgo8dGl0bGU+SGVsbG88L3RpdGxlPgo8ZWxsaXBzZSBmaWxsPSJub25lIiBzdHJva2U9ImJsYWNrIiBjeD0iNDAuNjkiIGN5PSItOTAiIHJ4PSIzNy41MyIgcnk9IjE4Ii8+Cjx0ZXh0IHRleHQtYW5jaG9yPSJtaWRkbGUiIHg9IjQwLjY5IiB5PSItODUuMzMiIGZvbnQtZmFtaWx5PSJUaW1lcyxzZXJpZiIgZm9udC1zaXplPSIxNC4wMCI+SGVsbG88L3RleHQ+CjwvZz4KPCEtLSBXb3JsZCAtLT4KPGcgaWQ9Im5vZGUyIiBjbGFzcz0ibm9kZSI+Cjx0aXRsZT5Xb3JsZDwvdGl0bGU+CjxlbGxpcHNlIGZpbGw9Im5vbmUiIHN0cm9rZT0iYmxhY2siIGN4PSI0MC42OSIgY3k9Ii0xOCIgcng9IjQwLjY5IiByeT0iMTgiLz4KPHRleHQgdGV4dC1hbmNob3I9Im1pZGRsZSIgeD0iNDAuNjkiIHk9Ii0xMy4zMiIgZm9udC1mYW1pbHk9IlRpbWVzLHNlcmlmIiBmb250LXNpemU9IjE0LjAwIj5Xb3JsZDwvdGV4dD4KPC9nPgo8IS0tIEhlbGxvJiM0NTsmZ3Q7V29ybGQgLS0+CjxnIGlkPSJlZGdlMSIgY2xhc3M9ImVkZ2UiPgo8dGl0bGU+SGVsbG8mIzQ1OyZndDtXb3JsZDwvdGl0bGU+CjxwYXRoIGZpbGw9Im5vbmUiIHN0cm9rZT0iYmxhY2siIGQ9Ik00MC42OSwtNzEuN0M0MC42OSwtNjQuNDEgNDAuNjksLTU1LjczIDQwLjY5LC00Ny41NCIvPgo8cG9seWdvbiBmaWxsPSJibGFjayIgc3Ryb2tlPSJibGFjayIgcG9pbnRzPSI0NC4xOSwtNDcuNjIgNDAuNjksLTM3LjYyIDM3LjE5LC00Ny42MiA0NC4xOSwtNDcuNjIiLz4KPC9nPgo8L2c+Cjwvc3ZnPgo=", view.getContent()); + assertEquals("image/svg+xml", view.getContentType()); + } + @Test public void importDiagram_WhenTheKrokiUrlIsNotDefined() throws Exception { Workspace workspace = new Workspace("Name", "Description"); @@ -73,8 +110,8 @@ public void importDiagram_WhenTheKrokiUrlIsNotDefined() throws Exception { @Test public void importDiagram_WhenAnInvalidFormatIsSpecified() throws Exception { Workspace workspace = new Workspace("Name", "Description"); - workspace.getViews().getConfiguration().addProperty("kroki.url", "https://mermaid.ink"); - workspace.getViews().getConfiguration().addProperty("kroki.format", "jpg"); + workspace.getViews().getConfiguration().addProperty(KrokiImporter.KROKI_URL_PROPERTY, "https://kroki.io"); + workspace.getViews().getConfiguration().addProperty(KrokiImporter.KROKI_FORMAT_PROPERTY, "jpg"); ImageView view = workspace.getViews().createImageView("key"); try { diff --git a/structurizr-import/src/test/java/com/structurizr/importer/diagrams/mermaid/MermaidImporterTests.java b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/mermaid/MermaidImporterTests.java index 5293f6c8f..bf8762878 100644 --- a/structurizr-import/src/test/java/com/structurizr/importer/diagrams/mermaid/MermaidImporterTests.java +++ b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/mermaid/MermaidImporterTests.java @@ -2,6 +2,7 @@ import com.structurizr.Workspace; import com.structurizr.view.ImageView; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import java.io.File; @@ -43,6 +44,25 @@ public void importDiagram_AsPNG() throws Exception { assertEquals("image/png", view.getContentType()); } + @Test + @Tag("IntegrationTest") + public void importDiagram_AsInlinePNG() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getViews().getConfiguration().addProperty(MermaidImporter.MERMAID_URL_PROPERTY, "https://mermaid.ink"); + workspace.getViews().getConfiguration().addProperty(MermaidImporter.MERMAID_FORMAT_PROPERTY, "png"); + workspace.getViews().getConfiguration().addProperty(MermaidImporter.MERMAID_COMPRESS_PROPERTY, "true"); + workspace.getViews().getConfiguration().addProperty(MermaidImporter.MERMAID_INLINE_PROPERTY, "true"); + ImageView view = workspace.getViews().createImageView("key"); + + new MermaidImporter().importDiagram(view, new File("./src/test/resources/diagrams/mermaid/flowchart.mmd")); + assertEquals("key", view.getKey()); + assertNull(view.getElement()); + assertNull(view.getElementId()); + assertEquals("flowchart.mmd", view.getTitle()); + assertEquals("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAbkAAAIFCAYAAABPtIO7AAAQAElEQVR4nOzdCXxTVdoG8DdJd9oCZW0B2fdSCqi4AwqiwrAosqoDisggCKigbMqisinIIqCIKIqyC4gIoyPIIPrJXspeQBbZKZTubZbvfQ9NJy1tbaG5SW6e//wyN01u0mBv7nPOe86914cAAAB0yocAAAB0CiEHAAC6hZADAADdQsgBAIBuIeQAAEC3EHIAAKBbCDnQzMwhR7sYiCIJIIvFZlo1ZEaNGAJwEoQcaCYgwNinfLXAJ0qV9yeAk/sTM65fzozjuwg5cBqEHGiqSr0QqhoZTADXLmakc8gRgDMh5AAAQLcQcgAAoFsIOQAA0C2EHAAA6BZCDgAAdAshBwAAuoWQAwAA3ULIAQCAbiHkAABAtxByAACgWwg5AADQLYQcAADoFkIOAAB0CyEHAAC6hZADAADdQsgBAIBuIeQAAEC3EHIAAKBbRgLwImazmdatW0Fjx75GXbu2VrcRI16mH35YTTabLXu99eu/pbZt71TrF8WJE3Hqdfv27SYAcD2EHHiN8+fPUv/+3WnBgllUs2YdGjJkNPXtO5gCAgLpww/foRkz3qXbVbJkaerVqy+VL1+xUOv/+ecxevbZ9gQAzoFyJXiNjz6aTBcvnqOZMxdRtWo1sx9/9NF/0KZNG2nSpFHUvPmDdO+9LehWhYWVoeee61/o9Y8cOUAA4DwIOfAKCQnXaMeO3+jpp5/LEXB2rVq1VctGjZrmeDw+/jJNnDiSDhyIoUqV7lCvf/zxTuq5CROGk8lk4l5bOC1fvojGjJmi1pHe4vvvz+f3akJJSYm0aNE8+uOPrXT1ajzVqdOAHn74cfUe8vjixZ+q95ISZ79+Q6lZs3vopZe60fTpn9Fnn81SZc8KFcK5rPpPatz4Tho37nX6669TVLduQxowYJh6PyE9QinD7tmznS5cOEtVq9agxx7rRO3bd8n+t/zxx6/qcx45sp/DuCw1aNCYnn9+EJUpU5YA9ArlSvAKBw/GkNVqpbvvfiDfdSTogoNDsn/28fHh3t8U6tmzL02ZMk8Fy+zZk7g3eD7reV81Bvfnn3EcPtMoMrLJTe/5wQfjVEAOGjSCPv10BdWrF0mzZk1Uj0mPT0JTSpsbN+6gp57qpd5TzJ37Pj3zTD/asGG7CiMpsc6aNYlef30sfffdNvLz81efzW7evA9o587faODAN+idd2aqgJs9e7IKNnH06CEO4cEUHX0XzZ+/ggNyOB0/foQ/31gC0DP05MArXL58US0LO1YmZNKJ9ITuuus+9XO5chXp559/oEOHYtX7GAwG1WuaNetLHtcLUOtcvXolx3vs27dLBZn00MQLLwyiBx9szWN3pQr83a1aPaYCSTz0UGsup25Qn0VCUjz44CP08cfT1GQZ+RwjR06klJRkqlgxQj0vvb6NG9dy73UbB/v9tH//HvUZe/R4noxGo/r80guUgAbQM4QcQAEcy5elSpVWy/T0tOzHqlSpnh1weWnYMJpWrvxKlUujoppy2N3L4VKf/k6VKtWy75coEayW1avXyn5MJstkZmZSRkYG+fv7q7Bbs2aJ6rmdOXMye73w8ErZnyMtLY3eemsINWnSnO655yEurVZRYQigZyhXgleQMShx4cK5orxMlSz/x3DT8xIwBZHyYufOPVUp8e23X6Vu3drQF1/M/dtDE6S3lZvBkPfXVcqwUoqU8bjnnx/IobpJlT8bNmycvU7t2vVUGbNMmXJqrO/55zvTm28O4B7eXgLQM4QceIV69Rqp5dat/8l3na+/XkCnT5+k4hQSEqpKhPPmLeHxr095rKyj+j2rVy+h4hIXd4gOH96vJq7cf3+r7HFFmfTiSMquQ4eOoUWLvlPhm5iYoHp2FouFAPQKIQdeQab2yziXzEDMa9r+L7/8qHpYBw4UX8/m+vUELiEuVWVCGTeLjIxWQSQlQgmm4iKlUFG2bPnsx06ePK5udnv37qTt27ep+9Kba9OmPb300msqCK9cuUQAeoUxOfAar7wygs6dO0OvvvoCde/+vJrin56eTj/++B1t2fKTOkZOdv7FRUqdX331iZr8Ib8vPLwy7dr1uwo4KSsKOeRADlPYtm2zGt+TMCwqOVxAfteKFV+qg9uvXYunOXOmqsku9vKshPeSJZ9xyL6qenvnz/+lxvAk8OQGoFcIOfAaQUEl1PFr69ev4l7Nr7R27VLVC6pVqy6Pm/VQAZHXWNjt/L4xY6bS3LlTVbAKOUbvxReHUNu2HdTPckiDTAqR49/kkAHpbRaVzJR84413VKB26fKwmlAyfPgEFZ7yvn37duHQ+5r/rVe5bPo+zZz5Hvn5+VHLlm1p6tRP1LF+AHpV9GYjwC365M1j3zd7rPwTVSODCWDrivOJpw8mDRg0o/ZXBOAk6MkBAIBuIeQAAEC3EHIAAKBbCDkAANAthBwAAOgWQg4AAHQLIQcAALqFkAMAAN1CyAEAgG4h5AAAQLcQcgAAoFsIOQAA0C2EHAAA6BZCDgAAdAshBwAAuoWQAwAA3ULIAQCAbiHkAABAtxByAACgWwg5AADQLYQcaOr04SS6Hp9BAAmX0v0JwMkQcqCZtDTrwtMHk7afPkhe7ci5/zwVElDhcHjpyFjychYyxRCAExkIADTVtGnTb2w225rdu3cvIQBwKvTkAABAtxByAACgWwg5AADQLYQcAADolpEAQFMGg+GaxWLJJABwOvTkADRms9lKmUwmXwIAp0PIAWiMQ+6i1WrFEfEAGkDIAWiMy5XljUajHwGA0yHkAABAtxByABrjcuVV7s2hXAmgAYQcgMY44Epz0KFcCaABhBwAAOgWQg4AAHQLIQegMS5XnuVbGgGA0yHkALQXbrVaAwgAnA4hB6A9G+FajgCaQMgBaMzGjEYjQg5AAwg5AI1xxsVzuRInaAbQAEIOQGNynBzf/AkAnA4hBwAAuoWQAwAA3ULIAWiMx+Qu8ALHyQFoACEHoDG51A4vcJwcgAYwjRl06dKlS+EWi2UnuaGkpKRSvr6+6f7+/qnkZoxG49Ly5csPJQCdQE8OAAB0CyEHoDHuLVm4ZGkjAHA6IwGApqxWq8lms2GoAEAD6MkBAIBuIeRA9zIzM+mHH34I2rVrl39cXJxfWlqaISIiwhwdHZ3+1FNPJZUsWVLT0iFKlQDaQciBrv3111+msWPHhl29etXUoUOHpFatWqVmZGQYdu7c6b9x48agX3/9NXD8+PFXKlWqZCnse3777bdBR44c8XvjjTeuEQC4NYQc6NqMGTNKXblyxTR9+vRLVatWzQ6yNm3apB47dszn9ddfL7tmzZoSAwYMuF7Y9zx69KgfAYBHQMiBbsXHxxv379/vJyVJx4Czq1mzpnnevHkXK1SoYHV8nEubgRs2bChx6tQpnzvuuMP8wAMPpHbp0iWZy4zEoVjm4MGDKuS2bNkSOG3atEt169Y1537vbt26VejRo0ci9yR91q9fXyI0NNTarFmzNAnTyZMn+3NPMig8PDz46aefTmrbtm328XJbt271/+abb0L4db4hISHWatWqZQ4cOPCa/TNOmDChtCwffvjhFA7w0lJ6rV27dsYLL7xwvUGDBurKBlKeXbhwYciOHTsCJODr1auX0a5du+T77rsvXZ5/7bXXyvj5+dkmTpwY7/iZ33777dJGo7EFAegIZleCbh04cMBXls2bN8/3FFq5A+7HH38MnD17dqkaNWpkLliw4OI///nP6+vWrSvx0Ucfhcrz77///pVatWplPvTQQ6nff//9ubwCTvj6+kpZM7hy5cpmXp7r1atX4qZNm4KGDx9e5v777zcvXbo0gUMnjd+3VGJiopppuX37dj8OwLCWLVumfv755+e5HBp/6dIl06xZs0rZ39fHx8fGPUnfn3/+OejDDz+8tHLlynMSWBy22evw5y/JnzlYgu2zzz67wL8ndcqUKWGbN29WZ1nhXmxKbGysPwdg9vefw5J2794d0LRp01MEoCMIOdAt6cXIsly5coUeb/v3v/8dJD2fwYMHJ4SFhVl5p58hPTIevyshPUMqgurVq2d27NgxhUOIZCxQHqtTp04G9wwtHFYkYWaxWOjkyZOqovLVV1+F3n333Wncu0suVaqULTIyMrNv374JHD7+3Hv0tb9vamqqkXtj1yIiIiwSpi1atEg9d+6cT0pKiiE9PZ1++eWXoE6dOiXJ75ZJNRx2qfw7U5YsWRIir+deYGpAQICNgzLQ/p7//e9/1X0O7zMEoCMIOdA9my3nZEYp+fGOP9zxJo9brVaSCSVNmjRJd1yfgy5dnouJiSnSWFylSpWye3klSpRQH4LLpmb7weD8mOpFXr9+XX0PpTwqIej4Hhy4qgR5+PBhX8f3DQoKyv5HBQcHq/eRHqGsJ+XKu+66K0fvtVGjRhmnT5/2SUhIMEjoPvjgg6lSbrU/v23btoA777wzrXTp0riYK+gKxuRAt8qUKaN6cBcuXPDhsmR2ePTs2TNRSnlyn8et/NesWRMs97kXZDCbzSRjYnLL/X7Xrl0rUqNQxvBy44CT0DXyLUeZNCkpySCzPv39/XMksj3MkpOTjQ7vm+8hCBx0ar0333yzbF7PS++We3fmJ554Ipl7q+Vk9qn8d9qzZ48/9w6vEoDOIORAt2Qihslkot9++y0gKioqO+Rkwon9Ppf5TPb7gYGBNinjSflPJpvkfj/Hntntyn3GE3u4yUQSx8c53NTP9sD+O2XLllXh2b9//4S8Pm/FihXV+/C4olnGFuX4QVnKv5vHLtMJQGcQcqBbMqYmYfX999+X4PGvlLwmiUgvz/FnLidmSrDIWJz9MSn/nT171lS+fHkrOYmMrclkl0OHDklJNNn+eGxsrCqRynOFeZ8qVaqYZSKK3Hf8N8h4opRtHcucrVu3Tlm9enUw/9t8pHwpnwFAbzAmB7o2aNCgBJliL+W7RYsWBe/atctPbnKYgEyll1Jlt27dEu3r9+7dO/GPP/4I4GAMtI/Dvfvuu6VHjRpVlsuJah3uDZllhqO8T1EnowguN1rzKjlKCXX79u0BK1asKCHja/L+CxYsCG3YsGEGj9UVqhcpISb/nmXLloXwZ/eVzyyzKkeOHFlm1qxZJR3XfeSRR1Ll88vElrZt26YQgA6hJwe6JiXISZMmXVm/fn2Q7Mx//PHHEqmpqQYp5ZUqVcoi0/CrV6+eHSBS1pTHli5dGsKhGMrjdEYJSQ65eC4pqnV4PCtFpum//fbbZd56660r3GPMKMpnkjE5WeR+XI6Xu3z5skmCd+HChaFcerQ0btw4vU+fPolUBN27d0/mnp95+fLlIfv27fPn4LPKv2Ho0KE5ztAigcj/3nQZp3Ms4QLoCc6EDrrkzhdNTUlJCZHeHAdwMrmQ9PKeffbZCnxLbN++verJ4aKpoDfoyQG4hssamDLZRmZVrl27toT0aLlMilIl6BZCDsDLyEHgX3/9dQiXKDOlDJvXoQ4AeoGtG3TJncuVPCaojsvjpQ3TdAAAEABJREFUcmUSuRmUK0Fv0JMDcAFcGRxAGwg5AADQLYQcAADoFkomABqLjo4ezGNfpXbt2jWOAMCpcMYTAI2ZTCY5f1YJAgCnQ8gBaMzKDAYDvnsAGsCYHIDGOOAybLkvcgcAToHWJIDG5PABVqQLsALArUFPDkBjRqPRzEGH7x6ABvBFA9AYB5xcuNREAOB0CDkAjXHIXefFBQIAp0PIAWiMx+MCeFGFAMDpMPEEQGPck8MhBAAaQU8OQHty/bYrBABOh5AD0Bj34kzcm4sgAHA6lEwANMYBZ+agQwMTQAP4ogFozGKxJHPInSEAcDr05AA05uvrK8fI1SIAcDqEHIDGuCeXwYtUAgCnQ7kSQGNcqpSTV5YiAHA6hByAxoxGo8Vqtd7XpEmTPXKmZu7ZBezdu7cuAUCxQ8gBaKBRo0Zfcbj14pv6OWvZWP6Pc+4UAYBTYEwOQAPcW3ufw+ykPeTsuEcni58JAJwCIQeggQMHDuzhgNuUx1N/cdDNIgBwCoQcgEakN2ez2Y7af5ZeHP+8PSYmZhcBgFMg5AA0snfv3v0cbD9mlShlLO4c359MAOA0CDkADZnN5mkcbnFZvbgd3Iv7nQDAaQwE4EYm9j/RMsgvcyRvmmUNJirSsWQGiy3NZjIEkJX/ZyxkA85C6WQif3W/kK8zWCjVZqLAHA/a+J0MBVzt20pmfmc1mzk943oZsyWjRIBf6EWT0S+JX5fvLGebldIMRgq4+Ql+v4JeZ7FZDaacl/PhUJXj8/72O2+wGtNtRmuR/pvkYKFLNrPt6OA5dZ4hABdDyIHbmDfsxG6b1VzfP8TH6ONr8FXnBSkKiRgL3diqbYV7icSAzWr/oZCvs/8eRxID1vxfYuP/GfL4uuX4/Xl+PgM/byv04w6/8OZvdyH/fTajBF3RXuPI5Gej9FRKS0+1GH2NxldfmlrjIwJwEYQcuIW5w+N+9fX1ubPT0Kp+BLqQkpRB62aeyrSm05sDP6o9jQBcACEHLjdzcNySwGBDp85Da/gT6M6SiccyMpJL1Xh1Xtm/CEBjmHgCLsclu8fr3B2KgNOpkmV8bSa/qysJwAUQcuB6Bmtgw/vLEuhTyfK+/iaToRoBuABCDlxqZi2bP1kMvgS6Jacys1mtZQjABXCCZgBwKjXwbzBYCcAFEHIA4FTqCASbDVUjcAmEHAA4mYFs6MmBi6B1BS5VMY7MtqIebQwexkYG9OTARdCTA5c6X4t8DDhcU99sqidnIQAXQMgBgHMZVE/ORAAugJADAOfinhyhJwcugjo5uJR/HFkxIqdz3JMj9OTARdCTA9dqhhOoegPMrgRXQcgBgNPJsBwBuADKleBS6QnYBnWPq5VWgw0hBy6BHQy4VLwfj9YQ6BrHmxF/ZHARlCvBpcIyyIAmPgA4C3py4FL+JQkTErwAJp6Aq6AnBy51LpUMZYqpK5eQcI2++245xcbupkOHYikwMJCqVKlOLVq0oSeeeJK7jM7tM06aNJouXjxH06YtIFc7cSKO+vfvTu+/P58aNWpCrobeOrgKQg5cSsqVxXHqyv/7v600ZcoYKlOmHLVt25E6deqhAmfHjt9ozpyp9PvvW2jMmKnk5+dH3qBkydLUq1dfKl++IrmanLbNhpFXcBGEHHi81NRUeu+9EVS5clWaOvUTCgoKyn6uQ4eu9O9/f0cffDCOA28b3XdfS/IGYWFl6Lnn+pM7kBNwGzA0Ai6CkAOPt337r5SWlkp9+76SI+DsHn30H9Skyd1UrlyFHI8vXvwp/fTTOrp8+SI/V5GioprRK6+MUFeyzktSUiItWjSP/vhjK129Gk916jSghx9+nB5/vFP2Oj4+vrR3706aPHk0l0+vUo0adejll4dTvXqR2ev89tsv9NVXn9CpUycoNLQU1axZlwYOfCO719W5cwvq3r0PHTlygLZu/Zn/TSVUyXH48AkUHBxSqHVylyvXrl1GX3/9qWoETJgwnE6ePE7Vq9eiJ5/spf77CKvVSrNnT+bPt5l8ff2oVavHqEGDxvTWW0Pom282quAE8DRoXYFL3TiE4PZKWYcO7VNlyMaN78x3ndwBJ2H13XfL6MUXb+zAe/ceQFu2/EirVi3O9z2kN3jgQAwNGjSCPv10hQquWbMmqsfsLl06T99/v4LeeGMCvfPOTMrMzOAxuvHZ5bpdu/6Pxo8fRo880o6Dbj2NGjVJlVVnzZqU/R4+Pj7qc8g44oYN27mXOptOn/5TlV2Lso4jCV8J6Y8+mkJDh45Rr3nwwdbqs128eF6tI++3fv0q+te/hnHYfcVjmkH0xRdz1HP5BX/hyIicEeeuBJdAyIHL3e6ldm70xCoUekcsO/tly76gnj37qvKl9Hweeqg1dezYjXs7CziYMvN83b59uzgYHqFmze5Rv++FFwbRhx9+rsYB7S5dusC9wZEqcKX32LFjd9Vrun49QT3/xRdz6YEHHuYeVE8eNyvFPaUoeumlV1XvUHpldtIDlN8jk2Xq129E7dp1USHs+NkKs44jefyZZ/qpdeU1bdq0V+F77Nhh9fyPP65Tn03+W4SGllQ9Rekh3i4ZdLXhYHBwEZQrwaXCA8mWQbcvr4kN3bs/qsqKdlFRTVW57syZk2qH71hCFLVr16fk5CQ6e/Y0Va1a46b3a9gwmlau/ErN4pT3atbsXi5Z1s+xjgSPvaQoJMhEenqaWp44cVQFpSMpe4rDh/dn35cSpqNKlaqoz3zu3Bm6447qf7tOfurWbZh9Pzg4VC0l9C0WiwrjRx/tkGP9Bx54hMN9N90O+cvgoqngKgg5cCk5hCCMbk/ZsuXVOJeMKTn25kaPnsI7b7O6v3Tp56p0KOLjL6ulv39AjveR8pxITU3J8/e8/vpYWrduBW3evFGFXYkSwar316vXi6p8KOxLO8fDFiRA09PT8/29KSnJ2Y/lXicgIDD7PQqzjv1+bvkdRiGvkYZC7p6bPaRvh0w7wXFy4CpoXYFLFccZT6SHlZGRQdu3b8vxeGRktCobyq106f9NmpBwEjJZxZE9ZMLCyub5e0JCQqlHj+dp3rwlPD73KT32WEdV3ly9egkVhj2UCvN7U1KScqxjf41jeBVmncKyB63ZnLPUGR9/hW6XlCvRkwNXwYYHLlUc565s3vxBVaqbP/9DVUrMTUpx9skVQkqKJpOJ9u/fm2M9OYBcSo3SM8xNxtTWrFnKQZKmekMSoP36DVUBGhd3iApDenlSEnWcqCLsP1evXjv7sZiYnTnWiYs7rF4fEVGlSOsUlq+vrxpn/PPPYzke//33XwjAkyHkwOVutycnO/Zx4z5UJbeBA5+hH35YTXv37lC3b775jF56qRudPn2COnfuqdaXHtkjjzxBS5Z8pg4ST0y8Tj/99D2tXbtUTanPawKL/A6Z9v/uu2+ocJQejrxGAk4Cr7CkvLlt22b69ttv1O+Vz/jxx9MoOvouqlXrf2NsMplm5crFKqBl1qTMemzR4lHuDfoXaZ2iuOeeh9S/aefO31XpUt5bPuPtQrkSXAljcuBS6ownxaBKlaqqjCjHg23atEGFj5QHIyIqcwg1Ub0ux2Po+vd/TYXZxIkjuURnpvDwytStWx/q2vWfeb6/jFXJGVPmzp1Kr776gnqsWrWa6hCEtm07FPpztm7dToXTihVf8uf9QB0bJzMk+/QZmGO9xx/vTAcPxtAnn0xXP0sIDhgwrMjrFIXMvDx//i8aOXKg+u8mvVRpGMhhBtLTu1VGDkwjricHLoIND1zqY7L5pg+Oy+g5phbBDU8//Yg6LZmclut21ikqKcXKcX5VqlTLfmz58kWqN7xq1Wa6Vb+vvUAn9l43D5pR59aTEuAWoVwJLpVei4xoabkHCbSXX+6lJtLI2Obmzf9Ws0jbt+9Ct82Ag8HBNVCuBJeSiSdhSDm38Oyz/dSpyORUZ599NovKlq1AHTp0UweF3zab1UQALoCQA5dShxDgBPU5LF/+n2JZ51bIOTQB9ATlSnAp6ckR6JpB/c9oJgAXQMiBS50NJBvOaqhvNvU/K6pG4BLY8MClIhPIiHIlADgLenLgUv5xhIOEAcBpEHLgUnIIAYHOyZ/YgEMIwCWwgwGXQk/OG8if2IZDCMAlEHLgUuca4Kw7+meQa/ygMQMugZADlwo/IFchwMwTfbPJVW2xrwGXwOxKcKkbp/VCZ07fVE8OY3LgEmhdgUsVx/XkwN2pnhzG5MAl0JMDlyqOK4MDAOQHPTlwKfTkAMCZ0JMDl0JPDgCcCT05cKlX4gzpBpMhk0C3rFYr72mMVwjABRBy4HJcr0zdvxX7QL26djEj02q2niAAF0DIgcuZzbYNR3YkZRDo0vXLmRR+vnY7AnABhBy43NBZtbtZMix/rJp2EkGnIwnxqbRsYlymOc36atflhngCcAGM+YPbmPtG3C6L2dYwMMjPZvK3+ltzXWbTYDByadOa6zEDqYccrtejHstrziZv7TarocBLkRuM8n4530uGlPJ8DTcR5WRVthsrkuM8UTWbxmagAs/mwk/xeGSO32f/nfJyaz7zTm/8+7LeIK/n83nG/t8l3/8+jh8+6/m8183vN9zg42OglGRKNWdkGjMzbP2HzqrzOQG4CEIO3MrUwaebB9gyxljJFmE02sIcn7NZjWkGozUgx2M2MhttZLQZHaoSNpnIwo8YKOcByDaSs26oy1Tf9IttZFU3g1rDx/H9DRJneb7GZraqXOSo4ve2Of4+fl3W77rpIGir1WKwWDOC/UyB1+QwacffZ3+tRIjBkM/sZ/5d/HFsNkPeFyLlcLTIZ8rj81r4fY3cKLAYTYb8Z1bLZ7f/bsf72W9DGfzZ/PJ7udVqO8//LQ4OmVGnDwG4GEIOQGONGzdu6OPjs2Tnzp2NCACcCsfJAWiMS4C+vMBhEwAaQMgBaA8hB6ARhByAC9hstlgCAKfDIQQAGjOZTMG8qEYA4HToyQG4xjECAKdDyAFoL4hv4QQAToeQA9AYj8dZDAYDzuUIoAGEHIDGOOBCOOgqEAA4HSaeAGjMzHhxkgDA6dCTA9CY0WgM5t4cenIAGkDIAWiMA84k43IEAE6HciWAxrIC7jIBgNOhJwegvRLcmwsmAHA6hByAxjjgbFar9ToBgNMh5AC0F8hB50cA4HQYkwPQmOHGBU0x8QRAA+jJAWjMYrGkctBh4gmABtCTA9CYyWQK5UVJAgCnQ08OQHsGmXxCAOB0CDkAjXG58jqHXDoBgNMh5AA0xgFX2mg0YnYlgAYQcgAa44AzWK1WlCsBNICQA9AYB9w1m82GSV8AGkDIAWiMy5VhMvOEAMDpEHIAAKBbCDkAjUm5ksflUK4E0ABCDkBjcjA4Bx2+ewAawBcNQGM2m82KnhyANhByABqTA8G5J4eJJwAaQMgBaE++d0EEAE6HkAPQmNlsTuJqJQ4GB9AAQg5AYz4+PgE8LleGAMDpEHIAGuOAs2RdOBUAnAwhB6AxDrhEDrrrBKY2Ou4AABAASURBVABOh5AD0J5cgaAcAYDTIeQANGaxWBJ4cYkAwOkQcgAay7qW3B0EAE6Hsy4AaEx6cjwuh54cgAbQkwPQmMlk8rXZbFUJAJwOPTkAjVmt1kRenCMAcDr05AA0xmNycoxcn6ZNmz7MSwOHXsCePXvqEgAUO4QcgAaio6NX8zhcR/vPWRcGL5X14xECAKdAuRJAA5mZmVN5cUKusJMVcAr34uQclj8SADgFQg5AA/v37//VZrP9xrfcT8Wlp6fPIwBwCoQcgHakN3fK/gP34uT268GDB2MJAJwCIQegkT2MQ+1nCTfBZcs/+f5sAgCnQcgBaIhDbRKHW5wEHZcud+7bt28nAYDTIOQANBQTE3PYYrGs55C7aDabZxIAOBUOIQDQ2EuPf9M4OKSEX2a6rfYL+6ptIQBwGvTkADQ0f9TxDXe3rdTsiZfuKFWqvN+UBW/9+QIBgNMg5AA0IgHXpHWZ+6s1Cg2Wn1t0Dw9D0AE4F0IOQAO5A84OQQfgXAg5ACfLL+DsEHQAzoOQA3Civws4OwQdgHMg5ACcpLABZ4egAyh+CDkAJ5g/6sQPTVqXLXTA2UnQlSzvO/XTESf+SQBw2xByAMXsRsCVeaBao5AiBZxdy+4RpUtF+CHoAIoBQg6gGN1uwNm17BFRDkEHcPsQcgDFpLgCzs4edJ+MjOtNAHBLDAQAt624A87R5iVnL8WfTR3e771anxMAFAlCDuA2LRhz/MfGD5e9xxkBZ4egA7g1CDmA2yABF/Vw2QeqNwoJICdD0AEUHcbkAG6RlgEnWnaPKBcWETgFY3QAhYeQA7gFWgecnT3o5o841ocA4G8h5ACKyFUBZydBVzoicDKCDuDvIeQAisDVAWfXskc4gg6gEBByAIXkLgFnZw+6j4fH4YBxgHwg5AAKwd0Czk6CrkzloIkIOoC8IeQA/oa7Bpxdy57h4RJ0c9/ErEuA3BByAAVw94Czk6ArWynwXQQdQE4IOYB8eErA2bXqERGBoAPICSEHkAdPCzg7BB1ATgg5gFw8NeDs7EE3B5NRAHDuSgBHnh5wjjZ/c+7cxdMpIwZMqfUFAXgp9OQAsugp4ETLHuHh5asETUSPDrwZQg6A9Bdwdgg68HYIOfB6eg04OwQdeDOEHHg1vQecHYIOvBVCDryWtwScHYIOvBFCDryStwWcHYIOvA0OIQCv460B50gOL4g/lzq633s1PyMAHUPIgVdBwP3PL0vPXbryV+qbCDrQM4QceA0E3M1+WXLucvxfqcNfnFhzIQHoEMbkwCsg4PLWont42bBKgVNwhXHQK4Qc6B4CrmAIOtAzhBzoGgKucBB0oFcIOdAtBFzRIOhAjxByoEsIuFuDoAO9QciB7iDgbg+CDvQEIQe6goArHgg60AuEHOgGAq54IehADxByoAsIOOdA0IGnQ8iBx0PAOReCDjwZQg48GgJOGwg68FQIOfBYCDhtSdCVqhQw5ZORcb0JwEMg5MAjIeBco1X3iLKlIwKnIujAUyDkwOMg4FwLQQeeBJfaAY+CgHMfm5acvZxwIX1E3wk1PiUAN4WQA4+BgHM/W5advRp/Ln04gg7cFcqV4BEQcO7poa4RpcPC/ad8OuZ4XwJwQwg5cHsIOPeGoAN3hpADt4aA8wwIOnBXCDlwWwg4z4KgA3eEkAO3hIDzTAg6cDcIOXA7CDjPhqADd4KQA7eCgNMHBB24CxwnB24DAac/W5aevXr1XOawF96ptoAAXAA9OXALCDh9eqhbROnS4b5TF4z+8wUCcAGEHLgcAk7fEHTgSgg5cCkEnHdA0IGrIOTAZRBw3gVBB66AkAOXQMB5JwQdaA0hB5r77O0Tm5wRcCtXLqa2be+kxMTrpGc9ez5OCxd+VOA6Tz/9CC1eXPgLA6xevYQef/xu0gKCDrSEkANNqYBrVfZ+d+7BrV27jKZOfZvcxbvvvkkbNqwp0mu6dHmWIiObkLtC0IFWEHKgGXvAVYsM9iU3duTIAXInt/J5unXrTY0bNyN3Zg+6L8adfIkAnAQhB5pwp4D797+/o8GDe1PHjg+o5apVX5PNZlPPDRvWj378cR399NP3qvR59OihPN+ja9fW9N13y2nevA/Uet26taFp08ZTSkoKjR37mnrshReeUu9T2N+dF3mf8+fP0vTpE+jJJ1tmP+7j40tr1iyl9u3vpc6dW9CYMYPp+vWE7Ocdy5XSM+3e/VE6ffpP6tevq3rP/v27q8+SH4vFQm++OYCef74zmc1mchYJupByCDpwHoQcOJ07BdzPP2+gDz4YR7Vq1aPPP19Lffq8TN9++7UKKzF16idUr14ktW7djjZu3EG1a9fL830kZJYt+4KqVKnGYbeNevd+mddfq0KyVavH6Pvvf6cWLdqocEpKSizU787L2rW/quXQoWM4EDdnP/7f//5EyclJXMqcRa+++hbFxu6hL76Ym+9nlc/w0UdT1Pts2LCdHnywtQrlixfP5/kaee7o0YP8/rP59T7kTA91DQ9B0IGzIOTAqdytRLlhw2pq1KgJDRr0JpUuHUbR0XfRc8/1V72dq1fji/ReElbt2j1Ffn5+KtBEgwZR6r4EQ4sWj6pe0KlTJ4r9dwcFlaCePV/gkuSdHFiP0L33tuCg253v+pmZmfTMM/2ofv1GZDAYqE2b9qoHeezY4ZvWlR7gL7/8m8aP/5DCwyuRFlTQlfWd+vn4k/0IoBgh5MBp5gyLm9/8HxXudJeAs1qtdODAXmrW7N4cj0vYyHMFhURepBdnJ6EjqlWrmf1YYGCQWspsz+L+3Q0bRuf4uWTJUpSRkV7ga+rWbZh9Pzg4VC3tvUwJPrlJb3PRonk0fPgE/h2NSUsPdQsPqVA14NUZQ44+SwDFxLl1CPBqA6bWevFj47FWrZ6t5BtW0d+fXCwjI0P1aD7/fI665XbtWtF6UxIKNz9m1OR35y4h5vVZbv5s+a8jvToZh3v//RuzSv39tZ/8uuc/ly+fOZy8bvCHtb8kgGKCkAOnemlyzVof07E4DrrKrg66gIAA1buS8TYp8eUWHl6ZnMWVv7soBg8eRfv27eKxw7H08cfLVFlVCxJwJ/YmfvHiezVeJ4BihJADp3OnoKtZs44q0clYlp30sM6f/4vKlatAzuTK310YRqOR2rbtQA891EYF3eTJo2nSpDnkbAg4cCaMyYEmJOg2ffnXmfjz6enkZDK+tXfvjhw3++SPPn0G0m+/bVYHV98YC9tD7703gseg+quSooiIqEKHDsXSnj3bizwhpCCF+d25+XOboGzZ8rRr1+/q3+HM6fx2gYGBNHr0FIqJ2UkrVnxFzoSAA2dDTw40o1WPTo5Ty03KhMOGjaPIyGiaPXsxLV26kBYsmElpaalUv34UjRs3TQWKeOKJJ9X0+REjXlZT9EuXbk7FoTC/Oy89ejyvJoNs376NvvxyHWlBDp3o1etFWrhwtvrvERQURMUNAQdawJXBQXMfv+EepUtwHQQcaAXlStCclqVLcD8IONASQg5cAkHnnRBwoDWEHLgMgs67IODAFRBy4FIIOu+AgANXQciByyHo9A0BB66EkAO3gKDTJwQcuBpCDtwGgk5fEHDgDhBy4FYQdPqAgAN3gZADt4Og82wIOHAnCDlwSwg6z4SAA3eDkAO3haDzLAg4cEcIOXBrCDrPgIADd4WQA7eHoHNvCDhwZwg58AgIOveEgAN3h5ADj4Ggcy8IOPAECDnwKPagu3o+w/mXyIZ8IeDAU+CiqeCRPhlx/HirXpWqlK7oh6vbawwBB54EPTnwSP0m1qixafFfp9Gj0xYCDjwNQg48FoJOWwg48EQIOfBoCDptIODAUyHkwOMh6JwLAQeeDCEHuoCgcw4EHHg6EwHoxNnUrWVLJT1SO6JmSFBgsAkNuNu05+cr17b8Z8uB+T883+/KlSsZBOCBsCMAjxcVFRXZrFmzQzabLe1fk+tWRo/u9knAnYpN/OK77e+8FBwcfIb/+/6TADwQjpMDj8Y73/G86GQ2m7vs3bv3iP1xHEd36+wB9/z46kPsjzVt2nQhL8ru2rWrMy/RgACPgZADj9S4ceOGPj4+K6xW6+Ldu3e/k9c6CLqiyyvg7LhB0Y4X33KP+RkOu2UE4AEQcuBxmjRpMs5oND6ZmZnZJSYm5nBB6yLoCq+ggHPEvbolvDBw0HUjADeHkAOPweHWwGAwrODbNzt37pxQ2Nch6P5eYQPOjnt1T/Pia+7VdeawW0cAbgohBx6Bew9jOdy68N0uHHCHqIgQdPkrasA5MPHf5VteXuGg60MAbgghB26Nd6L1eSG9t6UcbuPpNiDobnYbAZcta+blzKxe3c8E4EYQcuC2eOf5Fu84ZdynC+88D1IxQND9T3EEnF3dunVDSpQoIb26w/y3epkA3ARCDtwOh1s9XqzggFvBO8yxVMwQdMUbcI74b/cvXryV1av7nQBcDCEHboV3kmN4B9mDb1127959gJzEm4POWQFnFxkZWcHPz096db9y0A0jABdCyIFbiIqKquvr6yvHva3icHubNOCNQefsgHPEDZbXePEy/0078990LwG4AEIOXK5JkyajjUZjr6yzluwnDXlT0GkZcHbR0dHVTCaT9Oq+27lz51sEoDGEHLhM48aN6/j4+Izlu3Gu3AF6Q9C5IuAcSUPGYDBE8t3RXMKMIwCNIOTAJZo2bTqKd3rPpqend46NjS2WmZO3Q89B5+qAs5OSNDdqvuO7n3LQTSEADSDkQFNcvqrNpUk57m0t997GkBvRY9C5S8A54rG6SbxoybfOvA2cIwAnQsiBZnjnNoIXvenGWUv2kRvSU9C5Y8DZcan6bhmr48bOu7wtzCEAJ0HIgdNxuNWkG8e9recy1Shyc3oIOncOOEdctp7Ni/rXr1/vHBcXd50AihlCDpyqSZMm/+LW+qtZx715zDRyTw46Twk4O24EteKFzMB8kXt1ywmgGCHkwCl4x3UHB9sqvn3D4fYBeSAVdM9EVC1dwd9IHsLTAs4RN4je4/HaBhx0nQigmCDkoNhxCWooLwZzD+5J3mHtIg/2ycjjJ1v1iqjsCUHnyQFnx0HXgYNujcVi6bRnz541BHCbEHJQbBo0aFAxICBgFYfbbxxur5FOeELQ6SHgHHElQMqXCbwd9SaA24CQg2LBvbeXOdxGcXnyST2emNedg05vAWfHvbrnuFc302q1duSS9y8EcAsQcnBbGjVqVNrPz0/G3mI53AaRjrlj0Ok14Oxq1aoVWrJkyTW8fe3l7UuX/0ZwLoQc3DIuKfXlxWRuaT/pLS1tdwo6vQecI97WXuGgk1m6HXGyZygKhBwUGY+9+QUGBn7CO5wMbl33Iy/jDkG3d9OVpJP7Ehd4Q8DZyYxdXkivbjlvd+8RQCEg5KBIeOytKy8Wmc3mJ2JiYn4mL+XKoJOAO7U/6Ys+Y6sNJC8k5z3lRaeMjIz2sbGxFwigAAg5KDTeuXzDCwO3orsTuCRkRWomAAAQAElEQVTovD3g7HhbbGYwGNbx3TE7d+78lADygZCDv8VlokfpRpmoNwfcUoJsWgYdAu5mHHaf8KIib5cdCCAPCDkoEAfcXA636tevX+8YFxeXTnATLYIOAZc/Drr23KuTS/i0517d9wTgACEHeZKzxPv4+EjvbSy3kj8mKJAzgw4BVzgcdhJ0Z3l7fYkAsiDk4Cbce5vIi1bp6ekdMbBfeM4IOgRc0XDQvci9unF0o1fn0aeUg+KBkINsckkc7rlN49u23bt3TyYosuIMOgTcrck6vdw6q9X65Z49e2YQeDWEHCgccIM53AampaU9fuDAgTiCW1YcQYeAu32yTfOih8VieYzD7hqBV0LIeTk5sJtbvev57j4eyxhKUCxuJ+gQcMUn6wrkG7gBN4CrE0sIvA5Czovx+MWTvPiab09wwHntgd3OcitBh4BzDu7Vfc1BZ+bt/DkCr4KQ81IccAt5EcJf+i4ETlOUoEPAORdv888YDIbpHHaP8Xa/k8ArIOS8TNahAXIs0es7d+78gsDpChN0CDht1KtXr0xQUJCUL9dw+fIdAt1DyHkRLtlM4EWb5OTkdocOHbpCoJmCgg4Bpz3+LoznoGvN34W2hw8fTiTQLYScF5Ap1YGBgT/wl3oFl2neJXCJvIIOAec6XL68h8uXG/l78SJ/L5YR6BJCTuf4i9ybF135izwC1+FyPcegQ8C5B/6OyKzLS3q/6K+3QsjpWNaMsjT+8j5P4DYk6CrVLlHm4snUzxFw7iE6OrqX0Wh8NzMz8+F9+/YdJ9ANE4Hu8Bc2ulKlSrFWq3U6995QnnQz5UNqf3vi/M7LtcOqjl++eY6FwOXOnz+/LyIiYpXJZPqJlynnzp3DKcF0Aj05neHSy3AeZ+iempra+sCBA/EEbof/RvV5IeOjDQncTtble0Jx3UR90PyqxuA8/OX8iQMubOfOnU0RcO6L/0Y+vDATuCUOt368WMXl/ktRUVGRBB4NIacDclFTDjgr332PA+5NArdmsVhMHHQoU7oxmW2ZnJxcz9fX9+usc2CCh0LIeTj+Ar5vs9le5S+lEafm8gxGo9HEfzOEnJuTY0m50RjFf6tq3IhcS+CREHIeqlatWuX4i7fXarXKRSIfI/AY6Ml5lqwTl3/CDcrk6Ojouwg8CkLOAzVp0qRLyZIlP+cd5TO7d++eRuBxuHFyjMBjcNCtu3DhQlnuhb/FYfc6gcdAyHkY/oLN5C9aVy6jtOPbPgKPw3+/AG6gVCLwKGfOnEnlsPsHly/LcxVlFYFHQMh5iAYNGgTzF2sPf8GOcLh1JfBkvnzLJPBIHHRymM4ibnCej4qKqk7g1hByHoDLk48HBgae5bvP8RdsNoFH4zE5G+8k/yTwWNzQXJ2ZmRnl6+v7Ezc+exG4LR8Ct8atxUm8aMRfqlACXZByJS/CCTxaTEzMRV7U5JD7kr+n9/N3dACB20FPzn0Z+MvzK5cn42X8jUA3OOSM/He1EugCV1eetVqtMRx0u6tVqxZA4FYQcm6Iy5MtOODMvCN8nb9AUwj0xoxypb7s3r17Hgdd7zJlysTzd/dhAreBcqWb4YAbzQ39R7j3hpNn6xQ3XkpwyKFcqTNZl7IKktPr8ff4Plx53D2gJ+dG+MvxDQecHwdcKwLd4oDDGU90jKsvreV7zOXLzwlcDiHnBqKiosrzF+Ic7/gWcMC9RaBrEnAcdBcJdEu+x/x3/oYbrif4+12CwGUQci7GX4K2vr6+Menp6dFc3viJwBsE8a0kga5xj24jB11LHx+f89yIbU7gEhiTcyHe8N/kL0ELbvVVJPAaKFd6D264nuRFCDdmf+Pvu1RqPiXQFHpyLsIb/DKr1SoXZnycwKtwwOEEzV6Gv+f38t/9bjktH4GmEHIa441cZl8d4YBbxq28kQRehwMujXd4fxF4FbkYq5yWj/cBmwg0g5DTUHR09P28kcuEgyc44FYQeCUOOTl7TTkCryOn5eMG7li56nhkZGQFAqdDyGmkSZMmz5tMpkm8kQfzLY7Aa6Fc6d24gfuLXHXc399/D+8XHiJwKoScBrg8Kdd8q8yDzg8SeD1MPIGsq47LCQG6c9A9R+A0CDknkwkmvDjFrbfxBEDqKgQpHHIXCLwe7xcGGI3Ghzno3iBwCoScE3EPbptMMOHy5IcEkIV3aqW4N1eaAEgdON5btgneX2A/4QQG8mAXLlx4ilvEml9AlDfIweXLlz9f0Drcgzsln40D7ncCXTp79uwo3haiqIiOHz9eLzMz07du3bpFvrL75cuXn42MjMwg0B3eZ7zC+4z7eZ/RTX4+f/78UnJz3FjLrFChwjPkxjz6YHAu+9TnnYwrxrlC+JZnyHHZIYI/01+8E6sSExNzhkC3TCbTXXLsExVReHh4ML9OGpilqIjCwsLkO4uQ0yHu0c2U0/txj24LB51MSHH7MXyuVJnJzeGMJ8WocePGd3PLZhVvrB7dQwansxFAHnjfsTwr6I4SFAuMyRWT6OjoTj4+PjO5BVaZAArAvTgjN4YIIC8cdFt5+3js6tWrON1fMUDIFQNudfXhjbIzb5z3EADAbeJ9ybFSpUpduH79ehmC26KbciUP5vusX78+6PDhw35//fWXT+XKlc316tXL6NChQzLfd9oxSRxw/XjRfPfu3f8k8Crr1q0LnzNnToEXP73nnnvSxowZc5UAimjSpEklt27d6sd3893G+vXrl9CkSZP0f/3rX+Xffvvt+LvvvjudIAddhNyiRYuCly5dGiJ/7DZt2qSUK1fOcvHiRRNvIIEDBw4s/+abb8bzzqbY//hcOx/ApacoLlG+QOB1mjdvfiU8PPyK/edVq1YFc2PL9/XXX88OtZIlS1oJ4BZ069YtqW3btilyPzU1NWzu3Lm2KlWqZD799NNJ9nWkMZ+WlobadwE8PuQ4YPwk4B599NGUwYMHJzg+949//CPl3XffLT158uTSH3300aWIiIhi69FxDy7KarU24h5cfwKvxI2pjLJly2bPdPz5558tvr6+PrxtFDj7kUvbNr4h/KBANWrUcJy5eH7BggXhXMK05t6+Tp06ZSLIl8eH3ObNmwODgoJs3G2/nvs5o9FIL7/8ckLv3r0rrFmzpgR36a8fO3bM55VXXik3derUyxyOwTt27AgICwuz3nfffakvvvjidR+fG/9Jrly5Yvz4449DpfwpLaXo6Oj0nj17JlatWtUyduxYOTavGQfckwRQAN5mKjz22GPJzz33nGp9X7t2zdCrV68Q7gVmvvXWWyn29Z555pkK7dq1S+rRo0dySkqKYcaMGSX379/vn5SUZOTWembr1q1TOnXqlELgzSzcsA7k5bX8Vpg+fXrJn376Kah06dJWLl2m8r5O7Rft+71Ro0bFc4m9VEhIiJV7hpcyMzNp4cKFIbIf5H2eSYZ4eDtM5v1hduWrMOu4M4+feHLw4EG/Ro0apQcGBuY5LVsCrG7duhmHDh2S2jZxS1utN2vWrFItW7ZMXb169bnXXnvtKo+vlNi0aZNsQGQ2m4lLnGUOHDjgz8GYwBvFJSk78Xrl/vzzz6D4+Hg5YBMBB38rKioqXRpK9p95u/EvU6aMjR/LbmDyGLLp6tWrRi5/q53G6NGjw86fP+8zcuTIeN65XLj33nvT5s+fX5K3R18Cr8YN91RuKOV59YLFixeHNGzYMGPChAlXOnTokLRx48YS//nPfwLkOW68q/2eVL3kuUGDBqmgnD17dkne9wVLaH322WcXpLE/ZcqUMO48BNjftzDruDOPD7nLly+beKdRYBmSS0oWaYE4PiZ/qFatWqVx6Mn0/4zy5ctbjh49qnYi+/bt8zt79qzPq6++elXG8vj9rdILDA0NtXEohsycOXMwARRC48aN06WBxWO36mfetvx5mzJLb03CTR6LiYnx423LWqdOHfNvv/3mL6HIre5rDRo0yJQWOff8kqSh9vXXX4cQeD3uhV3hoLvpUk2RkZHpPGyTKuXMrl27Jst+T6oB8pz9kBXZHuU52bbS09Ppl19+CeIKQVLHjh1TuCFv4yBLfeCBB1KWLFmitrXCrOPudHEIgX0HUhS1a9fOdPy5RIkSVtnxyP3Y2Fg/KVs61r65TOBTv359G++wMJYChSa9Myl3y+xf+VkqD7yDMct4izSm5DHuoalqhNznSoGPn5+frWbNmjnOJME/Z3LJCT05kDPtmHl/lcCl7BxnzJFenOPPUpLMyMg5PMwNqewHuDHlK6XIu+66K81xHd4WM06fPu2TkJBgKMQ6bp8hHj8mJ7046c0VtE5evT0Zr8uPjINIyZJbLDdN3ZUWNwEUElcIrOHh4WZpOMk2eObMGR859yRXDSwSbjxelypl8c6dO6sxOy6FmwICAm5qtUk5HrPowI4rUBkWi8UnOTm5JP+oth37fIKCcAMq+35iYqLaCfLQTNm81pXq19+twz1KhJyzce8qg0s8gampqYa8xuX4jyStEb/27dsnFfY9eRzP4u/vb+OxkXj5md+7NP+cwMFo5VYUARQFj8tlyDYoM+OqVKli5u1UtlvzokWL/GUiCo+/mWTcTdbNL8ykyiClSwLIwo0h3ixSQrmnFUi3gMuZanvq379/QqVKlW46B2XFihUt/N6GgtaRw7XIzXl8yHF4JW/atClo3rx5oUOHDk3I/TwP2IdK+YfrycmFfU8pDXEt2lChQgUL16BDOOCuSsuJx+lMsqMigCKQcZCFCxeGSkmcS0pSljRwb87M21MJmQkXERFhlnFfWVfG3lauXBl85MgRHxmjs78H9/z85BgpAnAQFBR0nXt0t3TZJmlwyb5R7jsOzXA1wShDQDJrvTDrkJvz+DE53imYBw4ceE12FjIr7ddff/WXY+dkKT//97//DZRZahxYhQ6nO++8M0MOGZg+fXqZq1evWrgkkLl69eqgIUOGlPvhhx+CCKAIZFxOSuYyBZvH4zLkODnpsVWrVi3z+++/LyEzMO3ryhkrpHE1e/bsUjx+5yuHsixYsCAkLi7O96mnnip0Qw28B/fortMtkIDq1q1b4rJly0JiYmJ8ZfxOZkzy/rLMrFmzShZ2HXenizOeyLhG9erVMzmASixevDj03LlzpjvuuMMsreIBAwYk3MpB4GPGjEn+7rvvQj/44AN/bkUHy7jKgw8+mIIdDRRVcHCwrUaNGmriCLeG07kF7C+X2pFJADLNm3t62S1kme0rxzJxsIUOGzasrBzyItvyG2+8cVXKnlYrCgmQN64+hfLiUlFe071792SZBLV8+fIQmfnLoWatXbt2BlfFrhVlHXfm0QPZXO4ZzeNkA8gJEhISynOp8mJez/HvbFG+fHlcCsPLXbhwYfWtXE9OJgv4+PhkcBk8lYqIQ64WN9pwULgX4LHas0VZPzU1tQQvZG5Coecf3C7e/s3cAbiD3BiuQpCHxMTEMG6teEQrBQBAcLglczkxwGw241ATBwi5XNLS0oJMJlOmTDQhit9ATgAAEABJREFUACfIuio4QLELCQmJzzqsALIg5BzIzodDLoR7cYkE4CQy8YQAnEAOc/Lz80tNSUnB2XGyIOQcJCUllUaZEpwNPTlwJilbyrFzPH6Lg3oJIZeNa9n+crocbgXhooMA4NGksZ6amhpMgJCz4w2iZEBAAA4PAACPJ3MKpCfHPTo/8nIeXTbhso/P/v37b/tyD0OGDOlhsVhqz5o1a3xh1m/YsGEyxlVg27ZtgaGhoUUuCQ0YMGAG74S2zJgxYyUVUWRkpGbTw8G1YmNjb6snNmrUqIbx8fFj5s6d252cyN23SYwNkDojxSlu8dwXExNzhgCcrGnTpl/yYuOuXbu+IgAn4m1tAzfIp+3cufPf5KW8vlzJG0FX7hFuQ8ABgA5N4f3bcPJiGJPj6hFvBDMIQDsy9ouTLYPTcbXgZ+7JVYyOjq5NXsqrQy4qKqoubwAVdu/e/RsBaCeYtztM7wZNcCP+C6PR2Je8lFeHnMlketpqtX5KABrigDPwdoeJS6CJtLQ0GQN+jLyUV4cc72t68G09AWhLJnwh5EATBw4cOM/7OXPjxo2bkBfy2pDjP3glXvhyzfogAWjIbDaf5VuRr0AAcBvWc+WqNXkhrw05btncx7XqHwhAY7yzqeTj4xNIABrh8vgvvL+LJC/ktSHHA7H38e04AQDoXGZm5j7e3z1KXsibx+SCLBbLDgLQGLeqz6BcCVqKjY29wD257Q0aNAgjL+PNIdecQ+4cAWiMW9SVUa4EFyjj7+9fhbyMN4/JVc/IyLhEAADeIYH3e+HkZXzIS3HXPfbw4cO4OCpojre9s3xLIwAN8TZ3gG+3fUJ7T+OtISf/7hQCcAFuTUfwwut2NuBavN3JtTK9bp/vreVKA7dofAnABXhng54caI63OaPVavW6fb5X9uRq1apl5B3NPQTgAryzieDtDz050JScqNlkMgWRl/Ga68k1btx4g9FolBMym3knIz25GlnHydmkV7d79+5qBOAkvP2d4+1NXXmet7fyvB3KffuV6K28/dUhgGLWtGnTc9x7k4s8y4/l+JbG219i1s8W3u7qks55TU8uIyPjg8DAwK/5j1s26w8sasr/8R/9BAE4EQfcJd7uGjlseyHyf7ztkcVimUcATsDb13leRPP2Z38olG/lZbtj88kLeE199uDBgz/yYk/ux7P+2H8QgBNxa3o65THZiUPvON8mE4AT8Lb1PgfcTSce4P3eGW5cvU9ewKsGIeWPyn/cy46P8c9/8g5oGgE40d69exfy4lgeT32/Z8+ePwnACXbt2rWYF0fzeOrfvE0eIS/gVSHHf9SNvIjJ9fA2fhw9OXA6blB9yItUh5/jMjMzpxOAE/F2Jj02x+3utNlsnkpewuumk3JvbgovrmT9eIZ7cTMIQAM8yP8Z72CyW89yFYyYmBiMB4NT8Xb3pZz8wv4z7/N+2rdv3yHyEl4XctKb4z/4XrnPy/9DLw60xC3oWbxI5W3vWEpKCnpxoIn09PTpvM0l8O14amqqV40BFzi7csYrcZEmIt1dg+jYxV9/P3N1d9OqZZrvff6Bu7qT3vgYdg2cVtOj6+0zBx1tYjQY9Di9OXnL4Y+uB/qVPnpX9Z7NqS01J50xG4ybhsyocYE82KwhR1sarIaKpB+2LUfmJgb4hBy/u8YzTXi7081Vwi1EsYNn1orN7/kCQ85otHUJDvMdEVrGL510JKJuG/5/udEw0plrF9N9kxLMI/iuR4ecr7+xb1BpnxdCSvlmkM50r6s2u/uzbrpy8VSqrzXd9g++69Ehx9vf5JJl/SL9g0wW0onudV+TRfOsmy5cj8/wS7ySOYnv3lrIiWqRIX6RD4X5EXiEHRsvpR/ZnkCezmYgY83oUP96zUv5E3iMjQtPX7vyVyZ5OoORjI1algmqWB1XRHJn+7bEUyzfCuK1VyEAAAD9Q8gBAIBuIeQAAEC3EHIAAKBbCDkAANAthBwAAOgWQg4AAHQLIQcAALqFkAMAAN1CyAEAgG4h5AAAQLcQcgAAoFsIOQAA0C2EHAAA6BZCDgAAdAshBwAAuoWQAwAA3ULIAQC4qRUrvqKfflpHJ07E5fl8jRq16dFHO1Dnzj0I8ubWITd27Gt04cJZmjv3GwLQ0tNPP0KdOvWgXr360tGjh2jgwGdyPG8ymahixUoUFdWU+vUbSkFBJdTjkyaNposXz9G0aQsI4Hb8+OM6mj//wwLXOX78KM2b9wGVKlWaWrV6jP7OyZPHad26FXTgQAydPn2CypcPp8aN7+SQ7EmVK99BeoSeXJZ3332TmjW7lx57rCMBdOnyLNWr1yjHY889158iI6PV/eTkJIqN3UMbN66hs2fP0JQp8wigOK1c+ZVaPv/8QOra9Z9kMBhyPG+z2WjZsi/os89m09Kln/9tyMk6Cxd+RE2bNqcnnniSypQpR3Fxh+j771fQ5s0b6f3351P16rVIbxByWY4cOaBCDkB069b7pseqVq2hWr12993XkipVuoNmznxP9fZq165HAMXh9OmTqkRZokQwPfXUMzcFnJDH5DkJL1lXXlOlStU83+/QoVgVhm3atKfXXns7+/3uuedBVeocMeJlDrmx9NFHX5HeeHzI/fnnMdX93rNnuyptyo7oscc6Ufv2XdTz9lLTmDFT6KuvPlEbQ1hYWWrZ8lF66aVX1Tpt297YcU2fPoE++WQ6rVq1Wf3822+/qNecOnWCQkNLUc2adfm93uAufkX1fOfOLah79z4qILdu/VmVrBo1akLDh0+g4OAQAs/lWK4sSM2addTy0qXz2SHn4+NLe/fupMmTR1NCwlUeN6lDL788nHuGkdmvW7z4UzXWcvnyRSpXriKXPZvRK6+MIKPRqJ7v2rW16jkmJFxT22BAQCDdeee91L//69wCL6vWiY+/Qh9/PI1LT3spPT1NNdJ69uyb744OPIf0rIT0znx88t9Ny3MtW7bl3thKfs0GevbZl/Jcb9OmDWpd2eflDkwJ0kGDRpDZnJn92O+//1d9htjY3ZSYmEB160aq74K9kSf70f79u9P48R/Shx++w9vePfT662PJHRnJw0k9eufO31T4vPPOTBVws2dPpj/++FU9b99AvvlmAY/xTaPvvtvGf5zXeLmcfvhhtXpu7dob6w4dOiY74Hbt+j/+Aw6jRx5pxzuZ9TRq1CQ11jJr1qTs3y3vvWrVYtX137BhO7333mxuTf1Jc+ZMJfAO8vcWZcuWz35MAk9KQG+8MUFtk5mZGTxGN16Vl8SiRfN4+1tGL744hLfLjdS79wDasuVHtS3ZSVAuX75Ihd7y5f+hTz9dqcqjX375sXreYrFwY+olionZyeE4kr8HS6l06TAaPPifqnwKnksaSLJ9CGnASyO8oJsEnH1deW1e9u/fyw3wphQSEprn89JAq1//Rnk+LS1NNdAyMtJp2LBxKsiqVKlGb701VDWshGyf4uuvP1WlfSmnuiuPD7mRIyfSxIlzKDr6LtXKkB5c7dr1aceObTnWu//+h6lixQjy8/OjFi3aqFavtG7y88UXc+mBBx6mJ5/sSSVLlqIGDaJUK+iPP7aqnpudtNKlFSOtI9lI2rXronZYmZmZBPp25colNW4iOwjZ5uwuXbqggke2xyZN7qaOHburAf/r1xMoKSlRjaNIj0vKndLjf+ih1rxON95hLMix3UREVKEePZ5X60jvTXpyR48eVM9J4EnASpDeddd9XJ0oo0JTttVvv/2awHO9//7bqgdfVNeuXeXG1Lg8n7t8+UJ2BervBAQEcKNpCTeYRqltWG6ybaWlpXJY7lHr2HuDsu976qledMcd1cldeXy5UlrHa9YsUT23M2dOZj8eHl4px3pSanRUqVIV+vnnH/J93xMnjtKDDz6S47E6dRqo5eHD+7Pv5/W+sqM6d+6MW//hoegmTBh+02MVKoTTuHHTc5SApOHjWK6W4BFSUoyPv6y2D8fSpZCQlMksZ8+eViV3+2OOpBWekpKk7svOxtfXVzXu7OQzSNlz375dBJ7r4sXzajl9+mdq//bqqy8UuL7jeufPn6XikJKSrMbwpFIg26ydlN8d5d5G3ZFHh5zVauWxtsGqHCQzkKTFITuXvDYKGdNw5O8foHYqeZHH09PT1TqOAgOD1FI2AMf3yev35Pfe4LkcZ1cK+VtLYyf3GEfuMRTH5+07jPy2rdTUlDxfl5v0CCUs7ePJjmQ6OXg++zZRHOtJOf3ChXNUGBKyr73WV1UhRox4T1WoZFts1+6em9b18/Mnd+fRISfTX6VXNWnSHPUHsZMdgOMYiUhOTszxs7SqcwefnX0HJN1zR/Zwk4kr/3ssZ5jZX5Pfe4Pnyj278lbIIL8ozLZVEFlPtrHx46ff9JzRaCLwfDKxo7jWq18/itavX6XG1KS0nduxY0dUBaBDh65Zwy0Z9Prr4zhAb+zHpBTqqTx6TM5et3YMNBn7kFtu0u12FBd3mKpVy/uYEGmJSzdcDph0ZP+5evXaBb6vvF7GUwByk1KmHEguEwEcyRRvqULkbpzlR2Z1SlDKzEz7uInc5ODe3CV0gHbtnlLLOXOmqAqYo9TUVJox411au3Ypmc1mNXYcHByaHXBi69b/kKdy+56cfJH37t1x0+OVK1dTLWsJlBUrvqS+fQdzayNezWyUwdDcXfMdO36j7du3qUH6bds2q/ccMmS0es7f31/tXHbt+l2N5TVsGK0mAshxI99++w21bt2Ojh8/oqZryxhIrVr/24nIFPCVKxdTp07d1XiKtJZatHhUvSdAbjKu9sgjT9CSJZ9xQ6iy2tb+7//+q3YwXbv2zj6E4O9I5UK2ZTnsRWbASfVBWuAyYeqZZ/qp7RE8398dwmInh6QUpFq1mqpnJrMmhw3rR08+2Us1quRQADnOTipdEybMVBPzpBEvZXWZtdm2bUfavfsPdZOxZft4oSdx+5CT6dDDh/e/6XGZ7i9nJ3njjXfUcURdujysJn3IMWryBxo37nUOvi5q6r+Qg3s///wjGj36FbUjkZ3A4493yn4/mcUmU7slCL/8cp0KNgkwCVA5TEFmJkl49ukzMMfnePzxznTwYIw6vk5ICA4YMIwA8iOHsMg2OHHiSNVyDg+vzNtnnyJPw5ap3bIjeu+9EbwN7uOGX1V6+OHHEXA6IuPAhfF3ISdatWqrJsPJNrN48XwVcHJsr/T8ZU6DfTKUrHfq1HHer86nmTMnqv3ea6+N5X3hIhWIiYnX1UHonsJQ0JOzhhwd2+ihsLcjHwojT2U/aFFOWSMHahenwh4wrKUdGy+lH9me8OYrH9b+kDzYnGHH5ka1DOtfr3kpAs+xceHpa1f+ynz6lQ9r/kQe7OMRx7bf/2T4nRWraz+2/uKLT6sTUMgptmQqv7Neowf7tsRT7Jb4cYM+rD02v3VwWi8AADcyf/5yKqpbeY23QMgBAIBu6T7kpPu+ceMOcgY53RIAALgv9OQAAEC3EHIAAKBbCDkAAA7AGeoAAADqSURBVNAthBwAAOgWQg4AAHQLIQcAALqFkAMAAN1CyAEAgG4h5AAAQLcQcgAAoFsIOQAA0C2EHAAA6BZCDgAAdAshBwAAuoWQAwAA3ULIAQCAbiHkAABAt/425K5dyqCT+5MIPEPSlUwT6cS18+nY9jxMRorVj3Ti4skUSk+xELivBM6nv1NgyNmIYi+eSFnPNwKPYTOQ4TB5OKvZ9vvZo8l38I3Ac1gtZLFZbefJw1kzaV3cjoSLBG5Pcqqg5w0EAACgUxiTAwAA3ULIAQCAbiHkAABAtxByAACgWwg5AADQLYQcAADo1v8DAAD//5WW8eMAAAAGSURBVAMAgtYFEvb42VEAAAAASUVORK5CYII=", view.getContent()); + assertEquals("image/png", view.getContentType()); + } + @Test public void importDiagram_AsSVG() throws Exception { Workspace workspace = new Workspace("Name", "Description"); @@ -60,6 +80,25 @@ public void importDiagram_AsSVG() throws Exception { assertEquals("image/svg+xml", view.getContentType()); } + @Test + @Tag("IntegrationTest") + public void importDiagram_AsInlineSVG() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getViews().getConfiguration().addProperty(MermaidImporter.MERMAID_URL_PROPERTY, "https://mermaid.ink"); + workspace.getViews().getConfiguration().addProperty(MermaidImporter.MERMAID_FORMAT_PROPERTY, "svg"); + workspace.getViews().getConfiguration().addProperty(MermaidImporter.MERMAID_COMPRESS_PROPERTY, "false"); + workspace.getViews().getConfiguration().addProperty(MermaidImporter.MERMAID_INLINE_PROPERTY, "true"); + ImageView view = workspace.getViews().createImageView("key"); + + new MermaidImporter().importDiagram(view, new File("./src/test/resources/diagrams/mermaid/flowchart.mmd")); + assertEquals("key", view.getKey()); + assertNull(view.getElement()); + assertNull(view.getElementId()); + assertEquals("flowchart.mmd", view.getTitle()); + assertEquals("data:image/svg+xml;base64,PHN2ZyBpZD0ibWVybWFpZC1zdmciIHdpZHRoPSIxMDAlIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGNsYXNzPSJmbG93Y2hhcnQiIHN0eWxlPSJtYXgtd2lkdGg6IDQ0MXB4OyIgdmlld0JveD0iMCAwIDQ0MSA1MTcuMTU2MjUiIHJvbGU9ImdyYXBoaWNzLWRvY3VtZW50IGRvY3VtZW50IiBhcmlhLXJvbGVkZXNjcmlwdGlvbj0iZmxvd2NoYXJ0LXYyIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+PHN0eWxlIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hodG1sIj5AaW1wb3J0IHVybCgiaHR0cHM6Ly9jZG5qcy5jbG91ZGZsYXJlLmNvbS9hamF4L2xpYnMvZm9udC1hd2Vzb21lLzYuNy4yL2Nzcy9hbGwubWluLmNzcyIpOzwvc3R5bGU+PHN0eWxlPiNtZXJtYWlkLXN2Z3tmb250LWZhbWlseToidHJlYnVjaGV0IG1zIix2ZXJkYW5hLGFyaWFsLHNhbnMtc2VyaWY7Zm9udC1zaXplOjE2cHg7ZmlsbDojMzMzO31Aa2V5ZnJhbWVzIGVkZ2UtYW5pbWF0aW9uLWZyYW1le2Zyb217c3Ryb2tlLWRhc2hvZmZzZXQ6MDt9fUBrZXlmcmFtZXMgZGFzaHt0b3tzdHJva2UtZGFzaG9mZnNldDowO319I21lcm1haWQtc3ZnIC5lZGdlLWFuaW1hdGlvbi1zbG93e3N0cm9rZS1kYXNoYXJyYXk6OSw1IWltcG9ydGFudDtzdHJva2UtZGFzaG9mZnNldDo5MDA7YW5pbWF0aW9uOmRhc2ggNTBzIGxpbmVhciBpbmZpbml0ZTtzdHJva2UtbGluZWNhcDpyb3VuZDt9I21lcm1haWQtc3ZnIC5lZGdlLWFuaW1hdGlvbi1mYXN0e3N0cm9rZS1kYXNoYXJyYXk6OSw1IWltcG9ydGFudDtzdHJva2UtZGFzaG9mZnNldDo5MDA7YW5pbWF0aW9uOmRhc2ggMjBzIGxpbmVhciBpbmZpbml0ZTtzdHJva2UtbGluZWNhcDpyb3VuZDt9I21lcm1haWQtc3ZnIC5lcnJvci1pY29ue2ZpbGw6IzU1MjIyMjt9I21lcm1haWQtc3ZnIC5lcnJvci10ZXh0e2ZpbGw6IzU1MjIyMjtzdHJva2U6IzU1MjIyMjt9I21lcm1haWQtc3ZnIC5lZGdlLXRoaWNrbmVzcy1ub3JtYWx7c3Ryb2tlLXdpZHRoOjFweDt9I21lcm1haWQtc3ZnIC5lZGdlLXRoaWNrbmVzcy10aGlja3tzdHJva2Utd2lkdGg6My41cHg7fSNtZXJtYWlkLXN2ZyAuZWRnZS1wYXR0ZXJuLXNvbGlke3N0cm9rZS1kYXNoYXJyYXk6MDt9I21lcm1haWQtc3ZnIC5lZGdlLXRoaWNrbmVzcy1pbnZpc2libGV7c3Ryb2tlLXdpZHRoOjA7ZmlsbDpub25lO30jbWVybWFpZC1zdmcgLmVkZ2UtcGF0dGVybi1kYXNoZWR7c3Ryb2tlLWRhc2hhcnJheTozO30jbWVybWFpZC1zdmcgLmVkZ2UtcGF0dGVybi1kb3R0ZWR7c3Ryb2tlLWRhc2hhcnJheToyO30jbWVybWFpZC1zdmcgLm1hcmtlcntmaWxsOiMzMzMzMzM7c3Ryb2tlOiMzMzMzMzM7fSNtZXJtYWlkLXN2ZyAubWFya2VyLmNyb3Nze3N0cm9rZTojMzMzMzMzO30jbWVybWFpZC1zdmcgc3Zne2ZvbnQtZmFtaWx5OiJ0cmVidWNoZXQgbXMiLHZlcmRhbmEsYXJpYWwsc2Fucy1zZXJpZjtmb250LXNpemU6MTZweDt9I21lcm1haWQtc3ZnIHB7bWFyZ2luOjA7fSNtZXJtYWlkLXN2ZyAubGFiZWx7Zm9udC1mYW1pbHk6InRyZWJ1Y2hldCBtcyIsdmVyZGFuYSxhcmlhbCxzYW5zLXNlcmlmO2NvbG9yOiMzMzM7fSNtZXJtYWlkLXN2ZyAuY2x1c3Rlci1sYWJlbCB0ZXh0e2ZpbGw6IzMzMzt9I21lcm1haWQtc3ZnIC5jbHVzdGVyLWxhYmVsIHNwYW57Y29sb3I6IzMzMzt9I21lcm1haWQtc3ZnIC5jbHVzdGVyLWxhYmVsIHNwYW4gcHtiYWNrZ3JvdW5kLWNvbG9yOnRyYW5zcGFyZW50O30jbWVybWFpZC1zdmcgLmxhYmVsIHRleHQsI21lcm1haWQtc3ZnIHNwYW57ZmlsbDojMzMzO2NvbG9yOiMzMzM7fSNtZXJtYWlkLXN2ZyAubm9kZSByZWN0LCNtZXJtYWlkLXN2ZyAubm9kZSBjaXJjbGUsI21lcm1haWQtc3ZnIC5ub2RlIGVsbGlwc2UsI21lcm1haWQtc3ZnIC5ub2RlIHBvbHlnb24sI21lcm1haWQtc3ZnIC5ub2RlIHBhdGh7ZmlsbDojRUNFQ0ZGO3N0cm9rZTojOTM3MERCO3N0cm9rZS13aWR0aDoxcHg7fSNtZXJtYWlkLXN2ZyAucm91Z2gtbm9kZSAubGFiZWwgdGV4dCwjbWVybWFpZC1zdmcgLm5vZGUgLmxhYmVsIHRleHQsI21lcm1haWQtc3ZnIC5pbWFnZS1zaGFwZSAubGFiZWwsI21lcm1haWQtc3ZnIC5pY29uLXNoYXBlIC5sYWJlbHt0ZXh0LWFuY2hvcjptaWRkbGU7fSNtZXJtYWlkLXN2ZyAubm9kZSAua2F0ZXggcGF0aHtmaWxsOiMwMDA7c3Ryb2tlOiMwMDA7c3Ryb2tlLXdpZHRoOjFweDt9I21lcm1haWQtc3ZnIC5yb3VnaC1ub2RlIC5sYWJlbCwjbWVybWFpZC1zdmcgLm5vZGUgLmxhYmVsLCNtZXJtYWlkLXN2ZyAuaW1hZ2Utc2hhcGUgLmxhYmVsLCNtZXJtYWlkLXN2ZyAuaWNvbi1zaGFwZSAubGFiZWx7dGV4dC1hbGlnbjpjZW50ZXI7fSNtZXJtYWlkLXN2ZyAubm9kZS5jbGlja2FibGV7Y3Vyc29yOnBvaW50ZXI7fSNtZXJtYWlkLXN2ZyAucm9vdCAuYW5jaG9yIHBhdGh7ZmlsbDojMzMzMzMzIWltcG9ydGFudDtzdHJva2Utd2lkdGg6MDtzdHJva2U6IzMzMzMzMzt9I21lcm1haWQtc3ZnIC5hcnJvd2hlYWRQYXRoe2ZpbGw6IzMzMzMzMzt9I21lcm1haWQtc3ZnIC5lZGdlUGF0aCAucGF0aHtzdHJva2U6IzMzMzMzMztzdHJva2Utd2lkdGg6Mi4wcHg7fSNtZXJtYWlkLXN2ZyAuZmxvd2NoYXJ0LWxpbmt7c3Ryb2tlOiMzMzMzMzM7ZmlsbDpub25lO30jbWVybWFpZC1zdmcgLmVkZ2VMYWJlbHtiYWNrZ3JvdW5kLWNvbG9yOnJnYmEoMjMyLDIzMiwyMzIsIDAuOCk7dGV4dC1hbGlnbjpjZW50ZXI7fSNtZXJtYWlkLXN2ZyAuZWRnZUxhYmVsIHB7YmFja2dyb3VuZC1jb2xvcjpyZ2JhKDIzMiwyMzIsMjMyLCAwLjgpO30jbWVybWFpZC1zdmcgLmVkZ2VMYWJlbCByZWN0e29wYWNpdHk6MC41O2JhY2tncm91bmQtY29sb3I6cmdiYSgyMzIsMjMyLDIzMiwgMC44KTtmaWxsOnJnYmEoMjMyLDIzMiwyMzIsIDAuOCk7fSNtZXJtYWlkLXN2ZyAubGFiZWxCa2d7YmFja2dyb3VuZC1jb2xvcjpyZ2JhKDIzMiwgMjMyLCAyMzIsIDAuNSk7fSNtZXJtYWlkLXN2ZyAuY2x1c3RlciByZWN0e2ZpbGw6I2ZmZmZkZTtzdHJva2U6I2FhYWEzMztzdHJva2Utd2lkdGg6MXB4O30jbWVybWFpZC1zdmcgLmNsdXN0ZXIgdGV4dHtmaWxsOiMzMzM7fSNtZXJtYWlkLXN2ZyAuY2x1c3RlciBzcGFue2NvbG9yOiMzMzM7fSNtZXJtYWlkLXN2ZyBkaXYubWVybWFpZFRvb2x0aXB7cG9zaXRpb246YWJzb2x1dGU7dGV4dC1hbGlnbjpjZW50ZXI7bWF4LXdpZHRoOjIwMHB4O3BhZGRpbmc6MnB4O2ZvbnQtZmFtaWx5OiJ0cmVidWNoZXQgbXMiLHZlcmRhbmEsYXJpYWwsc2Fucy1zZXJpZjtmb250LXNpemU6MTJweDtiYWNrZ3JvdW5kOmhzbCg4MCwgMTAwJSwgOTYuMjc0NTA5ODAzOSUpO2JvcmRlcjoxcHggc29saWQgI2FhYWEzMztib3JkZXItcmFkaXVzOjJweDtwb2ludGVyLWV2ZW50czpub25lO3otaW5kZXg6MTAwO30jbWVybWFpZC1zdmcgLmZsb3djaGFydFRpdGxlVGV4dHt0ZXh0LWFuY2hvcjptaWRkbGU7Zm9udC1zaXplOjE4cHg7ZmlsbDojMzMzO30jbWVybWFpZC1zdmcgcmVjdC50ZXh0e2ZpbGw6bm9uZTtzdHJva2Utd2lkdGg6MDt9I21lcm1haWQtc3ZnIC5pY29uLXNoYXBlLCNtZXJtYWlkLXN2ZyAuaW1hZ2Utc2hhcGV7YmFja2dyb3VuZC1jb2xvcjpyZ2JhKDIzMiwyMzIsMjMyLCAwLjgpO3RleHQtYWxpZ246Y2VudGVyO30jbWVybWFpZC1zdmcgLmljb24tc2hhcGUgcCwjbWVybWFpZC1zdmcgLmltYWdlLXNoYXBlIHB7YmFja2dyb3VuZC1jb2xvcjpyZ2JhKDIzMiwyMzIsMjMyLCAwLjgpO3BhZGRpbmc6MnB4O30jbWVybWFpZC1zdmcgLmljb24tc2hhcGUgcmVjdCwjbWVybWFpZC1zdmcgLmltYWdlLXNoYXBlIHJlY3R7b3BhY2l0eTowLjU7YmFja2dyb3VuZC1jb2xvcjpyZ2JhKDIzMiwyMzIsMjMyLCAwLjgpO2ZpbGw6cmdiYSgyMzIsMjMyLDIzMiwgMC44KTt9I21lcm1haWQtc3ZnIC5sYWJlbC1pY29ue2Rpc3BsYXk6aW5saW5lLWJsb2NrO2hlaWdodDoxZW07b3ZlcmZsb3c6dmlzaWJsZTt2ZXJ0aWNhbC1hbGlnbjotMC4xMjVlbTt9I21lcm1haWQtc3ZnIC5ub2RlIC5sYWJlbC1pY29uIHBhdGh7ZmlsbDpjdXJyZW50Q29sb3I7c3Ryb2tlOnJldmVydDtzdHJva2Utd2lkdGg6cmV2ZXJ0O30jbWVybWFpZC1zdmcgOnJvb3R7LS1tZXJtYWlkLWZvbnQtZmFtaWx5OiJ0cmVidWNoZXQgbXMiLHZlcmRhbmEsYXJpYWwsc2Fucy1zZXJpZjt9PC9zdHlsZT48Zz48bWFya2VyIGlkPSJtZXJtYWlkLXN2Z19mbG93Y2hhcnQtdjItcG9pbnRFbmQiIGNsYXNzPSJtYXJrZXIgZmxvd2NoYXJ0LXYyIiB2aWV3Qm94PSIwIDAgMTAgMTAiIHJlZlg9IjUiIHJlZlk9IjUiIG1hcmtlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgbWFya2VyV2lkdGg9IjgiIG1hcmtlckhlaWdodD0iOCIgb3JpZW50PSJhdXRvIj48cGF0aCBkPSJNIDAgMCBMIDEwIDUgTCAwIDEwIHoiIGNsYXNzPSJhcnJvd01hcmtlclBhdGgiIHN0eWxlPSJzdHJva2Utd2lkdGg6IDE7IHN0cm9rZS1kYXNoYXJyYXk6IDEsIDA7Ii8+PC9tYXJrZXI+PG1hcmtlciBpZD0ibWVybWFpZC1zdmdfZmxvd2NoYXJ0LXYyLXBvaW50U3RhcnQiIGNsYXNzPSJtYXJrZXIgZmxvd2NoYXJ0LXYyIiB2aWV3Qm94PSIwIDAgMTAgMTAiIHJlZlg9IjQuNSIgcmVmWT0iNSIgbWFya2VyVW5pdHM9InVzZXJTcGFjZU9uVXNlIiBtYXJrZXJXaWR0aD0iOCIgbWFya2VySGVpZ2h0PSI4IiBvcmllbnQ9ImF1dG8iPjxwYXRoIGQ9Ik0gMCA1IEwgMTAgMTAgTCAxMCAwIHoiIGNsYXNzPSJhcnJvd01hcmtlclBhdGgiIHN0eWxlPSJzdHJva2Utd2lkdGg6IDE7IHN0cm9rZS1kYXNoYXJyYXk6IDEsIDA7Ii8+PC9tYXJrZXI+PG1hcmtlciBpZD0ibWVybWFpZC1zdmdfZmxvd2NoYXJ0LXYyLWNpcmNsZUVuZCIgY2xhc3M9Im1hcmtlciBmbG93Y2hhcnQtdjIiIHZpZXdCb3g9IjAgMCAxMCAxMCIgcmVmWD0iMTEiIHJlZlk9IjUiIG1hcmtlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgbWFya2VyV2lkdGg9IjExIiBtYXJrZXJIZWlnaHQ9IjExIiBvcmllbnQ9ImF1dG8iPjxjaXJjbGUgY3g9IjUiIGN5PSI1IiByPSI1IiBjbGFzcz0iYXJyb3dNYXJrZXJQYXRoIiBzdHlsZT0ic3Ryb2tlLXdpZHRoOiAxOyBzdHJva2UtZGFzaGFycmF5OiAxLCAwOyIvPjwvbWFya2VyPjxtYXJrZXIgaWQ9Im1lcm1haWQtc3ZnX2Zsb3djaGFydC12Mi1jaXJjbGVTdGFydCIgY2xhc3M9Im1hcmtlciBmbG93Y2hhcnQtdjIiIHZpZXdCb3g9IjAgMCAxMCAxMCIgcmVmWD0iLTEiIHJlZlk9IjUiIG1hcmtlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgbWFya2VyV2lkdGg9IjExIiBtYXJrZXJIZWlnaHQ9IjExIiBvcmllbnQ9ImF1dG8iPjxjaXJjbGUgY3g9IjUiIGN5PSI1IiByPSI1IiBjbGFzcz0iYXJyb3dNYXJrZXJQYXRoIiBzdHlsZT0ic3Ryb2tlLXdpZHRoOiAxOyBzdHJva2UtZGFzaGFycmF5OiAxLCAwOyIvPjwvbWFya2VyPjxtYXJrZXIgaWQ9Im1lcm1haWQtc3ZnX2Zsb3djaGFydC12Mi1jcm9zc0VuZCIgY2xhc3M9Im1hcmtlciBjcm9zcyBmbG93Y2hhcnQtdjIiIHZpZXdCb3g9IjAgMCAxMSAxMSIgcmVmWD0iMTIiIHJlZlk9IjUuMiIgbWFya2VyVW5pdHM9InVzZXJTcGFjZU9uVXNlIiBtYXJrZXJXaWR0aD0iMTEiIG1hcmtlckhlaWdodD0iMTEiIG9yaWVudD0iYXV0byI+PHBhdGggZD0iTSAxLDEgbCA5LDkgTSAxMCwxIGwgLTksOSIgY2xhc3M9ImFycm93TWFya2VyUGF0aCIgc3R5bGU9InN0cm9rZS13aWR0aDogMjsgc3Ryb2tlLWRhc2hhcnJheTogMSwgMDsiLz48L21hcmtlcj48bWFya2VyIGlkPSJtZXJtYWlkLXN2Z19mbG93Y2hhcnQtdjItY3Jvc3NTdGFydCIgY2xhc3M9Im1hcmtlciBjcm9zcyBmbG93Y2hhcnQtdjIiIHZpZXdCb3g9IjAgMCAxMSAxMSIgcmVmWD0iLTEiIHJlZlk9IjUuMiIgbWFya2VyVW5pdHM9InVzZXJTcGFjZU9uVXNlIiBtYXJrZXJXaWR0aD0iMTEiIG1hcmtlckhlaWdodD0iMTEiIG9yaWVudD0iYXV0byI+PHBhdGggZD0iTSAxLDEgbCA5LDkgTSAxMCwxIGwgLTksOSIgY2xhc3M9ImFycm93TWFya2VyUGF0aCIgc3R5bGU9InN0cm9rZS13aWR0aDogMjsgc3Ryb2tlLWRhc2hhcnJheTogMSwgMDsiLz48L21hcmtlcj48ZyBjbGFzcz0icm9vdCI+PGcgY2xhc3M9ImNsdXN0ZXJzIi8+PGcgY2xhc3M9ImVkZ2VQYXRocyI+PHBhdGggZD0iTTIyMS44NTIsNjJMMjIxLjg1Miw2OC4xNjdDMjIxLjg1Miw3NC4zMzMsMjIxLjg1Miw4Ni42NjcsMjIxLjkyNiw5OC40MTdDMjIyLDExMC4xNjcsMjIyLjE0OSwxMjEuMzM0LDIyMi4yMjQsMTI2LjkxN0wyMjIuMjk4LDEzMi41IiBpZD0iTF9BX0JfMCIgY2xhc3M9IiBlZGdlLXRoaWNrbmVzcy1ub3JtYWwgZWRnZS1wYXR0ZXJuLXNvbGlkIGVkZ2UtdGhpY2tuZXNzLW5vcm1hbCBlZGdlLXBhdHRlcm4tc29saWQgZmxvd2NoYXJ0LWxpbmsiIHN0eWxlPSIiIG1hcmtlci1lbmQ9InVybCgjbWVybWFpZC1zdmdfZmxvd2NoYXJ0LXYyLXBvaW50RW5kKSIvPjxwYXRoIGQ9Ik0yMjIuMzUyLDE5MC41TDIyMi4yNjgsMTk0LjU4M0MyMjIuMTg1LDE5OC42NjcsMjIyLjAxOCwyMDYuODMzLDIyMi4wMDUsMjE0LjVDMjIxLjk5MiwyMjIuMTY3LDIyMi4xMzMsMjI5LjMzNCwyMjIuMjAzLDIzMi45MTdMMjIyLjI3MywyMzYuNTAxIiBpZD0iTF9CX0NfMCIgY2xhc3M9IiBlZGdlLXRoaWNrbmVzcy1ub3JtYWwgZWRnZS1wYXR0ZXJuLXNvbGlkIGVkZ2UtdGhpY2tuZXNzLW5vcm1hbCBlZGdlLXBhdHRlcm4tc29saWQgZmxvd2NoYXJ0LWxpbmsiIHN0eWxlPSIiIG1hcmtlci1lbmQ9InVybCgjbWVybWFpZC1zdmdfZmxvd2NoYXJ0LXYyLXBvaW50RW5kKSIvPjxwYXRoIGQ9Ik0xODAuMjE1LDMzOS41MTlMMTYwLjU5LDM1Mi42MjVDMTQwLjk2NiwzNjUuNzMyLDEwMS43MTcsMzkxLjk0NCw4Mi4wOTMsNDEwLjU1QzYyLjQ2OSw0MjkuMTU2LDYyLjQ2OSw0NDAuMTU2LDYyLjQ2OSw0NDUuNjU2TDYyLjQ2OSw0NTEuMTU2IiBpZD0iTF9DX0RfMCIgY2xhc3M9IiBlZGdlLXRoaWNrbmVzcy1ub3JtYWwgZWRnZS1wYXR0ZXJuLXNvbGlkIGVkZ2UtdGhpY2tuZXNzLW5vcm1hbCBlZGdlLXBhdHRlcm4tc29saWQgZmxvd2NoYXJ0LWxpbmsiIHN0eWxlPSIiIG1hcmtlci1lbmQ9InVybCgjbWVybWFpZC1zdmdfZmxvd2NoYXJ0LXYyLXBvaW50RW5kKSIvPjxwYXRoIGQ9Ik0yMjIuMzUyLDM4MS42NTZMMjIyLjI2OCwzODcuNzRDMjIyLjE4NSwzOTMuODIzLDIyMi4wMTgsNDA1Ljk5LDIyMS45MzUsNDE3LjU3M0MyMjEuODUyLDQyOS4xNTYsMjIxLjg1Miw0NDAuMTU2LDIyMS44NTIsNDQ1LjY1NkwyMjEuODUyLDQ1MS4xNTYiIGlkPSJMX0NfRV8wIiBjbGFzcz0iIGVkZ2UtdGhpY2tuZXNzLW5vcm1hbCBlZGdlLXBhdHRlcm4tc29saWQgZWRnZS10aGlja25lc3Mtbm9ybWFsIGVkZ2UtcGF0dGVybi1zb2xpZCBmbG93Y2hhcnQtbGluayIgc3R5bGU9IiIgbWFya2VyLWVuZD0idXJsKCNtZXJtYWlkLXN2Z19mbG93Y2hhcnQtdjItcG9pbnRFbmQpIi8+PHBhdGggZD0iTTI2NC4zNDQsMzM5LjY2NEwyODMuNiwzNTIuNzQ2QzMwMi44NTcsMzY1LjgyOCwzNDEuMzcsMzkxLjk5MiwzNjAuNjI2LDQxMC41NzRDMzc5Ljg4Myw0MjkuMTU2LDM3OS44ODMsNDQwLjE1NiwzNzkuODgzLDQ0NS42NTZMMzc5Ljg4Myw0NTEuMTU2IiBpZD0iTF9DX0ZfMCIgY2xhc3M9IiBlZGdlLXRoaWNrbmVzcy1ub3JtYWwgZWRnZS1wYXR0ZXJuLXNvbGlkIGVkZ2UtdGhpY2tuZXNzLW5vcm1hbCBlZGdlLXBhdHRlcm4tc29saWQgZmxvd2NoYXJ0LWxpbmsiIHN0eWxlPSIiIG1hcmtlci1lbmQ9InVybCgjbWVybWFpZC1zdmdfZmxvd2NoYXJ0LXYyLXBvaW50RW5kKSIvPjwvZz48ZyBjbGFzcz0iZWRnZUxhYmVscyI+PGcgY2xhc3M9ImVkZ2VMYWJlbCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjIxLjg1MTU2MjUsIDk5KSI+PGcgY2xhc3M9ImxhYmVsIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMzkuMTMyODEyNSwgLTEyKSI+PGZvcmVpZ25PYmplY3Qgd2lkdGg9Ijc4LjI2NTYyNSIgaGVpZ2h0PSIyNCI+PGRpdiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94aHRtbCIgY2xhc3M9ImxhYmVsQmtnIiBzdHlsZT0iZGlzcGxheTogdGFibGUtY2VsbDsgd2hpdGUtc3BhY2U6IG5vd3JhcDsgbGluZS1oZWlnaHQ6IDEuNTsgbWF4LXdpZHRoOiAyMDBweDsgdGV4dC1hbGlnbjogY2VudGVyOyI+PHNwYW4gY2xhc3M9ImVkZ2VMYWJlbCAiPjxwPkdldCBtb25leTwvcD48L3NwYW4+PC9kaXY+PC9mb3JlaWduT2JqZWN0PjwvZz48L2c+PGcgY2xhc3M9ImVkZ2VMYWJlbCI+PGcgY2xhc3M9ImxhYmVsIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwLCAwKSI+PGZvcmVpZ25PYmplY3Qgd2lkdGg9IjAiIGhlaWdodD0iMCI+PGRpdiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94aHRtbCIgY2xhc3M9ImxhYmVsQmtnIiBzdHlsZT0iZGlzcGxheTogdGFibGUtY2VsbDsgd2hpdGUtc3BhY2U6IG5vd3JhcDsgbGluZS1oZWlnaHQ6IDEuNTsgbWF4LXdpZHRoOiAyMDBweDsgdGV4dC1hbGlnbjogY2VudGVyOyI+PHNwYW4gY2xhc3M9ImVkZ2VMYWJlbCAiPjwvc3Bhbj48L2Rpdj48L2ZvcmVpZ25PYmplY3Q+PC9nPjwvZz48ZyBjbGFzcz0iZWRnZUxhYmVsIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg2Mi40Njg3NSwgNDE4LjE1NjI1KSI+PGcgY2xhc3M9ImxhYmVsIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMTUuMTI1LCAtMTIpIj48Zm9yZWlnbk9iamVjdCB3aWR0aD0iMzAuMjUiIGhlaWdodD0iMjQiPjxkaXYgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGh0bWwiIGNsYXNzPSJsYWJlbEJrZyIgc3R5bGU9ImRpc3BsYXk6IHRhYmxlLWNlbGw7IHdoaXRlLXNwYWNlOiBub3dyYXA7IGxpbmUtaGVpZ2h0OiAxLjU7IG1heC13aWR0aDogMjAwcHg7IHRleHQtYWxpZ246IGNlbnRlcjsiPjxzcGFuIGNsYXNzPSJlZGdlTGFiZWwgIj48cD5PbmU8L3A+PC9zcGFuPjwvZGl2PjwvZm9yZWlnbk9iamVjdD48L2c+PC9nPjxnIGNsYXNzPSJlZGdlTGFiZWwiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDIyMS44NTE1NjI1LCA0MTguMTU2MjUpIj48ZyBjbGFzcz0ibGFiZWwiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNC42NzE4NzUsIC0xMikiPjxmb3JlaWduT2JqZWN0IHdpZHRoPSIyOS4zNDM3NSIgaGVpZ2h0PSIyNCI+PGRpdiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94aHRtbCIgY2xhc3M9ImxhYmVsQmtnIiBzdHlsZT0iZGlzcGxheTogdGFibGUtY2VsbDsgd2hpdGUtc3BhY2U6IG5vd3JhcDsgbGluZS1oZWlnaHQ6IDEuNTsgbWF4LXdpZHRoOiAyMDBweDsgdGV4dC1hbGlnbjogY2VudGVyOyI+PHNwYW4gY2xhc3M9ImVkZ2VMYWJlbCAiPjxwPlR3bzwvcD48L3NwYW4+PC9kaXY+PC9mb3JlaWduT2JqZWN0PjwvZz48L2c+PGcgY2xhc3M9ImVkZ2VMYWJlbCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMzc5Ljg4MjgxMjUsIDQxOC4xNTYyNSkiPjxnIGNsYXNzPSJsYWJlbCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTIwLjg5ODQzNzUsIC0xMikiPjxmb3JlaWduT2JqZWN0IHdpZHRoPSI0MS43OTY4NzUiIGhlaWdodD0iMjQiPjxkaXYgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGh0bWwiIGNsYXNzPSJsYWJlbEJrZyIgc3R5bGU9ImRpc3BsYXk6IHRhYmxlLWNlbGw7IHdoaXRlLXNwYWNlOiBub3dyYXA7IGxpbmUtaGVpZ2h0OiAxLjU7IG1heC13aWR0aDogMjAwcHg7IHRleHQtYWxpZ246IGNlbnRlcjsiPjxzcGFuIGNsYXNzPSJlZGdlTGFiZWwgIj48cD5UaHJlZTwvcD48L3NwYW4+PC9kaXY+PC9mb3JlaWduT2JqZWN0PjwvZz48L2c+PC9nPjxnIGNsYXNzPSJub2RlcyI+PGcgY2xhc3M9Im5vZGUgZGVmYXVsdCAgIiBpZD0iZmxvd2NoYXJ0LUEtMCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjIxLjg1MTU2MjUsIDM1KSI+PHJlY3QgY2xhc3M9ImJhc2ljIGxhYmVsLWNvbnRhaW5lciIgc3R5bGU9IiIgeD0iLTY2LjAwNzgxMjUiIHk9Ii0yNyIgd2lkdGg9IjEzMi4wMTU2MjUiIGhlaWdodD0iNTQiLz48ZyBjbGFzcz0ibGFiZWwiIHN0eWxlPSIiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0zNi4wMDc4MTI1LCAtMTIpIj48cmVjdC8+PGZvcmVpZ25PYmplY3Qgd2lkdGg9IjcyLjAxNTYyNSIgaGVpZ2h0PSIyNCI+PGRpdiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94aHRtbCIgc3R5bGU9ImRpc3BsYXk6IHRhYmxlLWNlbGw7IHdoaXRlLXNwYWNlOiBub3dyYXA7IGxpbmUtaGVpZ2h0OiAxLjU7IG1heC13aWR0aDogMjAwcHg7IHRleHQtYWxpZ246IGNlbnRlcjsiPjxzcGFuIGNsYXNzPSJub2RlTGFiZWwgIj48cD5DaHJpc3RtYXM8L3A+PC9zcGFuPjwvZGl2PjwvZm9yZWlnbk9iamVjdD48L2c+PC9nPjxnIGNsYXNzPSJub2RlIGRlZmF1bHQgICIgaWQ9ImZsb3djaGFydC1CLTEiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDIyMS44NTE1NjI1LCAxNjMpIj48ZyBjbGFzcz0iYmFzaWMgbGFiZWwtY29udGFpbmVyIG91dGVyLXBhdGgiPjxwYXRoIGQ9Ik0tNTUuMzY3MTg3NSAtMjcgQy0zMC4yMDQ5NTY4MTg5MTIyMzcgLTI3LCAtNS4wNDI3MjYxMzc4MjQ0NzQgLTI3LCA1NS4zNjcxODc1IC0yNyBDNTUuMzY3MTg3NSAtMjcsIDU1LjM2NzE4NzUgLTI3LCA1NS4zNjcxODc1IC0yNyBDNTUuNDgxOTY0MTg0NzQ3NjkgLTI2Ljk5NTI1MjgwMTIwMzY4LCA1NS41OTY3NDA4Njk0OTUzOCAtMjYuOTkwNTA1NjAyNDA3MzY0LCA1NS43ODAwODQyMjczNjE2NiAtMjYuOTgyOTIyNDY1MDMzMzQ3IEM1NS45MzU4OTE1NDUzNjU0NSAtMjYuOTYzNTAxMTAzMDk4MDYsIDU2LjA5MTY5ODg2MzM2OTIzIC0yNi45NDQwNzk3NDExNjI3NzQsIDU2LjE5MDE2MDQ1MTQwMzY3IC0yNi45MzE4MDY1MTcwMTM2MTIgQzU2LjMwNTQ4MjkxMDI0NDczIC0yNi45MDc2MjU5NDAyMDMzNzgsIDU2LjQyMDgwNTM2OTA4NTc4NCAtMjYuODgzNDQ1MzYzMzkzMTQzLCA1Ni41OTQ2MTQ5MzU3MDM5OTQgLTI2Ljg0NzAwMTMyOTY5NjY1MyBDNTYuNzIyNDc4MjM5MzM3MjQgLTI2LjgwODkzNDc3NDkwMDkyNiwgNTYuODUwMzQxNTQyOTcwNSAtMjYuNzcwODY4MjIwMTA1MTk2LCA1Ni45OTA2ODQ4NDYwMjM0MiAtMjYuNzI5MDg2MjA4NTAzMTczIEM1Ny4wNjg1Njk5NzEzMDQ2NzYgLTI2LjY5ODY5NTMzODMxMjEzLCA1Ny4xNDY0NTUwOTY1ODU5MyAtMjYuNjY4MzA0NDY4MTIxMDg2LCA1Ny4zNzU2NjQ2MjMyNjQ4NDYgLTI2LjU3ODg2NjYzMzI3NTI4NiBDNTcuNDk5MzQ0NDA2MTA4MjIgLTI2LjUxODQwMzI3MTc0NDQzLCA1Ny42MjMwMjQxODg5NTE2MDQgLTI2LjQ1NzkzOTkxMDIxMzU3NSwgNTcuNzQ2OTI0NDY1MTg1MzY2IC0yNi4zOTczNjg3NTYwMzI0NDYgQzU3Ljg0MjcxNTMwMjI0MzkyIC0yNi4zNDAyODk4MDY0NzMzMTUsIDU3LjkzODUwNjEzOTMwMjQ3NiAtMjYuMjgzMjEwODU2OTE0MTg0LCA1OC4xMDE5MjgyOTA2MTIxMzYgLTI2LjE4NTgzMjM5MTMxMjY0NCBDNTguMjA3MjQ3MDcwNTc5MiAtMjYuMTEwNjM2MjI3NzQyNDA0LCA1OC4zMTI1NjU4NTA1NDYyNyAtMjYuMDM1NDQwMDY0MTcyMTY0LCA1OC40MzgyNTEwNjM0NDgzNCAtMjUuOTQ1NzAyNTQ2OTgxOTcgQzU4LjU1NDEyNjAwMDEwNzc2NSAtMjUuODQ3NTYxNDgxODM0NTMsIDU4LjY3MDAwMDkzNjc2NzE5IC0yNS43NDk0MjA0MTY2ODcwODQsIDU4Ljc1MzU5NTM1ODEyODcwNiAtMjUuNjc4NjE5NTUzMzY1NjU3IEM1OC44NTUwMzgwMjg2MDczNiAtMjUuNTc3MTc2ODgyODg3MDAyLCA1OC45NTY0ODA2OTkwODYwMTUgLTI1LjQ3NTczNDIxMjQwODM0OCwgNTkuMDQ1ODA3MDUzMzY1NjYgLTI1LjM4NjQwNzg1ODEyODcwNiBDNTkuMTAwMjg1MzM2Njc2NjggLTI1LjMyMjA4NTQ3MDU2ODU2NywgNTkuMTU0NzYzNjE5OTg3NyAtMjUuMjU3NzYzMDgzMDA4NDMsIDU5LjMxMjg5MDA0Njk4MTk3IC0yNS4wNzEwNjM1NjM0NDgzNCBDNTkuMzY1NDk3Nzc5ODYwNTkgLTI0Ljk5NzM4MTg0OTEwODgyNywgNTkuNDE4MTA1NTEyNzM5MjIgLTI0LjkyMzcwMDEzNDc2OTMxLCA1OS41NTMwMTk4OTEzMTI2NDQgLTI0LjczNDc0MDc5MDYxMjEzNiBDNTkuNjM2MTkyMjU2NjkxNzg1IC0yNC41OTUxNTk1NTE2MjA0NDQsIDU5LjcxOTM2NDYyMjA3MDkzNCAtMjQuNDU1NTc4MzEyNjI4NzU2LCA1OS43NjQ1NTYyNTYwMzI0NSAtMjQuMzc5NzM2OTY1MTg1MzcgQzU5LjgyMTI3NTk1MTk2NDQ3IC0yNC4yNjM3MTQ5NzI3MjI0NSwgNTkuODc3OTk1NjQ3ODk2NTA1IC0yNC4xNDc2OTI5ODAyNTk1MzUsIDU5Ljk0NjA1NDEzMzI3NTI5IC0yNC4wMDg0NzcxMjMyNjQ4NDYgQzU5Ljk4NjIxNTc4NTA3MDAyIC0yMy45MDU1NTE2MzA3Nzc0LCA2MC4wMjYzNzc0MzY4NjQ3NiAtMjMuODAyNjI2MTM4Mjg5OTUyLCA2MC4wOTYyNzM3MDg1MDMxNzYgLTIzLjYyMzQ5NzM0NjAyMzQxNyBDNjAuMTMyNzU0ODU4NTU5ODIgLTIzLjUwMDk1OTMyMzAzNjcyNCwgNjAuMTY5MjM2MDA4NjE2NDY1IC0yMy4zNzg0MjEzMDAwNTAwMzIsIDYwLjIxNDE4ODgyOTY5NjY1IC0yMy4yMjc0Mjc0MzU3MDM5OTQgQzYwLjIzMzE0MDkyMjIwMzk3IC0yMy4xMzcwNDA3NjE5MTcwMiwgNjAuMjUyMDkzMDE0NzExMyAtMjMuMDQ2NjU0MDg4MTMwMDQ3LCA2MC4yOTg5OTQwMTcwMTM2MSAtMjIuODIyOTcyOTUxNDAzNjcgQzYwLjMxNjkwMjMyNTk2MjgxIC0yMi42NzkzMDQwNTc0OTkzOSwgNjAuMzM0ODEwNjM0OTEyIC0yMi41MzU2MzUxNjM1OTUxMSwgNjAuMzUwMTA5OTY1MDMzMzUgLTIyLjQxMjg5NjcyNzM2MTY2MiBDNjAuMzU0NDU4NTI3OTA5ODkgLTIyLjMwNzc1ODE3MDU0MDc1OCwgNjAuMzU4ODA3MDkwNzg2NDQgLTIyLjIwMjYxOTYxMzcxOTg1NCwgNjAuMzY3MTg3NSAtMjIgQzYwLjM2NzE4NzUgLTIyLCA2MC4zNjcxODc1IC0yMiwgNjAuMzY3MTg3NSAtMjIgQzYwLjM2NzE4NzUgLTYuNjA5NzgyODc3MTEyOTA4LCA2MC4zNjcxODc1IDguNzgwNDM0MjQ1Nzc0MTg1LCA2MC4zNjcxODc1IDIyIEM2MC4zNjcxODc1IDIyLCA2MC4zNjcxODc1IDIyLCA2MC4zNjcxODc1IDIyIEM2MC4zNjMxMDk0MTQzMTA1NjQgMjIuMDk4NTk5MDIxMzY2Njg1LCA2MC4zNTkwMzEzMjg2MjExMzYgMjIuMTk3MTk4MDQyNzMzMzczLCA2MC4zNTAxMDk5NjUwMzMzNSAyMi40MTI4OTY3MjczNjE2NjIgQzYwLjMzODgxNDQxMzMzMjkxIDIyLjUwMzUxNDk2NjI3MjA3NSwgNjAuMzI3NTE4ODYxNjMyNDg0IDIyLjU5NDEzMzIwNTE4MjQ4NywgNjAuMjk4OTk0MDE3MDEzNjEgMjIuODIyOTcyOTUxNDAzNjcgQzYwLjI2NTY3MzY2NDU5NjI3IDIyLjk4MTg4NDk5ODg4NTk5LCA2MC4yMzIzNTMzMTIxNzg5NCAyMy4xNDA3OTcwNDYzNjgzMSwgNjAuMjE0MTg4ODI5Njk2NjUgMjMuMjI3NDI3NDM1NzAzOTk0IEM2MC4xODQ1MDc0NDQxNTI1NDQgMjMuMzI3MTI1NDUyMzA3OTk2LCA2MC4xNTQ4MjYwNTg2MDg0NCAyMy40MjY4MjM0Njg5MTIsIDYwLjA5NjI3MzcwODUwMzE3NiAyMy42MjM0OTczNDYwMjM0MTcgQzYwLjA0MjY3MTUwOTM2NTI5IDIzLjc2MDg2ODAwOTI4Mzg0LCA1OS45ODkwNjkzMTAyMjc0MSAyMy44OTgyMzg2NzI1NDQyNjcsIDU5Ljk0NjA1NDEzMzI3NTI5IDI0LjAwODQ3NzEyMzI2NDg0NiBDNTkuOTAyMDYyMjEyOTQ3NjU1IDI0LjA5ODQ2NDAzNDU5ODA2LCA1OS44NTgwNzAyOTI2MjAwMiAyNC4xODg0NTA5NDU5MzEyNzYsIDU5Ljc2NDU1NjI1NjAzMjQ1IDI0LjM3OTczNjk2NTE4NTM2NiBDNTkuNjg1NjQ4MDMxNTg5MTUgMjQuNTEyMTYyMDUyMzQyODg3LCA1OS42MDY3Mzk4MDcxNDU4NSAyNC42NDQ1ODcxMzk1MDA0MDUsIDU5LjU1MzAxOTg5MTMxMjY0NCAyNC43MzQ3NDA3OTA2MTIxMzMgQzU5LjQ1OTc1Njc2ODgzODQyNSAyNC44NjUzNjM5MjA5NDk0OCwgNTkuMzY2NDkzNjQ2MzY0MjA1IDI0Ljk5NTk4NzA1MTI4NjgyNSwgNTkuMzEyODkwMDQ2OTgxOTcgMjUuMDcxMDYzNTYzNDQ4MzQgQzU5LjIzNzczNjUyMDE2NTk0IDI1LjE1OTc5NzE2NDg4NjgxLCA1OS4xNjI1ODI5OTMzNDk5MSAyNS4yNDg1MzA3NjYzMjUyODUsIDU5LjA0NTgwNzA1MzM2NTY2IDI1LjM4NjQwNzg1ODEyODcwNiBDNTguOTMzMTA4MjAxNDY3NzkgMjUuNDk5MTA2NzEwMDI2NTc0LCA1OC44MjA0MDkzNDk1Njk5MiAyNS42MTE4MDU1NjE5MjQ0NCwgNTguNzUzNTk1MzU4MTI4NzA2IDI1LjY3ODYxOTU1MzM2NTY1NyBDNTguNjUxOTM0NzA3MDgxMDU0IDI1Ljc2NDcyMTczMjcwNjg0NiwgNTguNTUwMjc0MDU2MDMzNCAyNS44NTA4MjM5MTIwNDgwNCwgNTguNDM4MjUxMDYzNDQ4MzQgMjUuOTQ1NzAyNTQ2OTgxOTcgQzU4LjM2MjY5NzIzMjc3NTEgMjUuOTk5NjQ2OTQ1MjMxMTcsIDU4LjI4NzE0MzQwMjEwMTg1NCAyNi4wNTM1OTEzNDM0ODAzNywgNTguMTAxOTI4MjkwNjEyMTM2IDI2LjE4NTgzMjM5MTMxMjY0NCBDNTguMDAxNzI2ODI4NjczODIgMjYuMjQ1NTM5NTAyODQxODIyLCA1Ny45MDE1MjUzNjY3MzU1IDI2LjMwNTI0NjYxNDM3MSwgNTcuNzQ2OTI0NDY1MTg1MzY2IDI2LjM5NzM2ODc1NjAzMjQ0NiBDNTcuNjU5NzUyNzU1OTA3NzI1IDI2LjQzOTk4NDQwNzkzNzI2LCA1Ny41NzI1ODEwNDY2MzAwOSAyNi40ODI2MDAwNTk4NDIwNzQsIDU3LjM3NTY2NDYyMzI2NDg0NiAyNi41Nzg4NjY2MzMyNzUyODYgQzU3LjI4NzYxMjIwOTIyMDkzIDI2LjYxMzIyNDc5MTg1MjQ2MywgNTcuMTk5NTU5Nzk1MTc3MDIgMjYuNjQ3NTgyOTUwNDI5NjM3LCA1Ni45OTA2ODQ4NDYwMjM0MiAyNi43MjkwODYyMDg1MDMxNzMgQzU2Ljg5MDc2NzQ0NzIzMTc1IDI2Ljc1ODgzMjkwNjk1NDM1LCA1Ni43OTA4NTAwNDg0NDAwODUgMjYuNzg4NTc5NjA1NDA1NTI3LCA1Ni41OTQ2MTQ5MzU3MDM5OTQgMjYuODQ3MDAxMzI5Njk2NjUzIEM1Ni40OTAzMjk4MTI5NjQwNSAyNi44Njg4Njc2MjA0OTQ4MTgsIDU2LjM4NjA0NDY5MDIyNDEgMjYuODkwNzMzOTExMjkyOTg3LCA1Ni4xOTAxNjA0NTE0MDM2NyAyNi45MzE4MDY1MTcwMTM2MTIgQzU2LjAyOTUyMzkyMjY4NTQ3IDI2Ljk1MTgyOTgzOTQzNjY4NywgNTUuODY4ODg3MzkzOTY3MjggMjYuOTcxODUzMTYxODU5NzYyLCA1NS43ODAwODQyMjczNjE2NiAyNi45ODI5MjI0NjUwMzMzNDcgQzU1LjY1MTg4NTcyODY5NTI2IDI2Ljk4ODIyNDc5NDE1OTY1NiwgNTUuNTIzNjg3MjMwMDI4ODYgMjYuOTkzNTI3MTIzMjg1OTcsIDU1LjM2NzE4NzUgMjcgQzU1LjM2NzE4NzUgMjcsIDU1LjM2NzE4NzUgMjcsIDU1LjM2NzE4NzUgMjcgQzI4LjU1MDIzMDcyMDQwMjE2NSAyNywgMS43MzMyNzM5NDA4MDQzMzEgMjcsIC01NS4zNjcxODc1IDI3IEMtNTUuMzY3MTg3NSAyNywgLTU1LjM2NzE4NzUgMjcsIC01NS4zNjcxODc1IDI3IEMtNTUuNDkwNDAxNzQxOTI5Nzk0IDI2Ljk5NDkwMzgyMTI1NzIxOCwgLTU1LjYxMzYxNTk4Mzg1OTU4IDI2Ljk4OTgwNzY0MjUxNDQzLCAtNTUuNzgwMDg0MjI3MzYxNjYgMjYuOTgyOTIyNDY1MDMzMzQ3IEMtNTUuODcwMDc2NjQ2NjAxMTM0IDI2Ljk3MTcwNDkyMTY3MzU0NSwgLTU1Ljk2MDA2OTA2NTg0MDYgMjYuOTYwNDg3Mzc4MzEzNzQ0LCAtNTYuMTkwMTYwNDUxNDAzNjcgMjYuOTMxODA2NTE3MDEzNjEyIEMtNTYuMzA0MDA5NDM2MDQ0NDk1IDI2LjkwNzkzNDg5NTI1MzcxNywgLTU2LjQxNzg1ODQyMDY4NTMyIDI2Ljg4NDA2MzI3MzQ5MzgyMywgLTU2LjU5NDYxNDkzNTcwMzk5NCAyNi44NDcwMDEzMjk2OTY2NTMgQy01Ni42ODIyNTYyMjI4OTgwOCAyNi44MjA5MDkzODgwMTcxOTIsIC01Ni43Njk4OTc1MTAwOTIxNyAyNi43OTQ4MTc0NDYzMzc3MywgLTU2Ljk5MDY4NDg0NjAyMzQyIDI2LjcyOTA4NjIwODUwMzE3MyBDLTU3LjE0MDk1ODA2MzM4MzEyIDI2LjY3MDQ0OTQxNzEzMjg0MywgLTU3LjI5MTIzMTI4MDc0MjgxNCAyNi42MTE4MTI2MjU3NjI1MTQsIC01Ny4zNzU2NjQ2MjMyNjQ4NDYgMjYuNTc4ODY2NjMzMjc1Mjg2IEMtNTcuNDUxOTQ5NzMxMjY5NDUgMjYuNTQxNTczMTE2NDMxNTIyLCAtNTcuNTI4MjM0ODM5Mjc0MDU2IDI2LjUwNDI3OTU5OTU4Nzc1NCwgLTU3Ljc0NjkyNDQ2NTE4NTM2NiAyNi4zOTczNjg3NTYwMzI0NDYgQy01Ny44NDc2ODIwMzkzMTI2NyAyNi4zMzczMzAyNzM1NjQ2NzgsIC01Ny45NDg0Mzk2MTM0Mzk5OCAyNi4yNzcyOTE3OTEwOTY5MDcsIC01OC4xMDE5MjgyOTA2MTIxMzYgMjYuMTg1ODMyMzkxMzEyNjQ0IEMtNTguMjI4NDIzMjg3ODk3MzEgMjYuMDk1NTE2Njk5MTg1ODcsIC01OC4zNTQ5MTgyODUxODI0ODUgMjYuMDA1MjAxMDA3MDU5MDkyLCAtNTguNDM4MjUxMDYzNDQ4MzQgMjUuOTQ1NzAyNTQ2OTgxOTcgQy01OC41NjIxNTI4MDA2NzQ1NDYgMjUuODQwNzYzMTI4NTQwMzYzLCAtNTguNjg2MDU0NTM3OTAwNzUgMjUuNzM1ODIzNzEwMDk4NzU3LCAtNTguNzUzNTk1MzU4MTI4NzA2IDI1LjY3ODYxOTU1MzM2NTY2IEMtNTguODE2MzM1MTU3ODUxMjg1IDI1LjYxNTg3OTc1MzY0MzA4MiwgLTU4Ljg3OTA3NDk1NzU3Mzg1NiAyNS41NTMxMzk5NTM5MjA1MDcsIC01OS4wNDU4MDcwNTMzNjU2NiAyNS4zODY0MDc4NTgxMjg3MDYgQy01OS4xMzc0NDE0MTkyMjEwOCAyNS4yNzgyMTUzNjY4MDY0MjgsIC01OS4yMjkwNzU3ODUwNzY1IDI1LjE3MDAyMjg3NTQ4NDE1LCAtNTkuMzEyODkwMDQ2OTgxOTcgMjUuMDcxMDYzNTYzNDQ4MzQgQy01OS4zNzkwNzc1MDY0MTUxIDI0Ljk3ODM2MjI1ODY3ODkwMywgLTU5LjQ0NTI2NDk2NTg0ODI0IDI0Ljg4NTY2MDk1MzkwOTQ2NSwgLTU5LjU1MzAxOTg5MTMxMjY0NCAyNC43MzQ3NDA3OTA2MTIxMzMgQy01OS42MTg1NzgzNDUyNTk3NiAyNC42MjQ3MTk1MDkwNjUwMTMsIC01OS42ODQxMzY3OTkyMDY4OCAyNC41MTQ2OTgyMjc1MTc4OTQsIC01OS43NjQ1NTYyNTYwMzI0NCAyNC4zNzk3MzY5NjUxODUzNyBDLTU5LjgxMzYxMTQ1NzU2NzY3IDI0LjI3OTM5Mjk0NjU5OTUyLCAtNTkuODYyNjY2NjU5MTAyOSAyNC4xNzkwNDg5MjgwMTM2NjgsIC01OS45NDYwNTQxMzMyNzUyOCAyNC4wMDg0NzcxMjMyNjQ4NSBDLTU5Ljk5MDQ3OTQzNTIyMDk2IDIzLjg5NDYyNDgzMTkwNDExNCwgLTYwLjAzNDkwNDczNzE2NjY1IDIzLjc4MDc3MjU0MDU0MzM4LCAtNjAuMDk2MjczNzA4NTAzMTc2IDIzLjYyMzQ5NzM0NjAyMzQxNyBDLTYwLjEyNDc3MzE2MTY2NzAxNSAyMy41Mjc3NjkzNzAzMDIzNzMsIC02MC4xNTMyNzI2MTQ4MzA4NSAyMy40MzIwNDEzOTQ1ODEzMjYsIC02MC4yMTQxODg4Mjk2OTY2NSAyMy4yMjc0Mjc0MzU3MDM5OTQgQy02MC4yMzY3MDE1MjUyNjU3IDIzLjEyMDA1OTQ2NzI0OTkyNCwgLTYwLjI1OTIxNDIyMDgzNDc1IDIzLjAxMjY5MTQ5ODc5NTg1LCAtNjAuMjk4OTk0MDE3MDEzNjEgMjIuODIyOTcyOTUxNDAzNjcgQy02MC4zMTEyNjI3MjE5NDc4NyAyMi43MjQ1NDc2MTg2NDA5MSwgLTYwLjMyMzUzMTQyNjg4MjEzIDIyLjYyNjEyMjI4NTg3ODE0NywgLTYwLjM1MDEwOTk2NTAzMzM1IDIyLjQxMjg5NjcyNzM2MTY2MiBDLTYwLjM1NjM5NjM0OTE4NDI0IDIyLjI2MDkwNTk3MTc5MjQyMiwgLTYwLjM2MjY4MjczMzMzNTEzNCAyMi4xMDg5MTUyMTYyMjMxNzgsIC02MC4zNjcxODc1IDIyIEMtNjAuMzY3MTg3NSAyMiwgLTYwLjM2NzE4NzUgMjIsIC02MC4zNjcxODc1IDIyIEMtNjAuMzY3MTg3NSA5LjcyMDAzMjY0MjAyNzc2MiwgLTYwLjM2NzE4NzUgLTIuNTU5OTM0NzE1OTQ0NDc1NCwgLTYwLjM2NzE4NzUgLTIyIEMtNjAuMzY3MTg3NSAtMjIsIC02MC4zNjcxODc1IC0yMiwgLTYwLjM2NzE4NzUgLTIyIEMtNjAuMzYyODU3NDk5OTA1MzQgLTIyLjEwNDY4OTc1MDEzMzc5NCwgLTYwLjM1ODUyNzQ5OTgxMDY4NCAtMjIuMjA5Mzc5NTAwMjY3NTg0LCAtNjAuMzUwMTA5OTY1MDMzMzUgLTIyLjQxMjg5NjcyNzM2MTY2IEMtNjAuMzM1ODAwMTMxODUyODYgLTIyLjUyNzY5Njk1MjgyMjgxNSwgLTYwLjMyMTQ5MDI5ODY3MjM2NiAtMjIuNjQyNDk3MTc4MjgzOTc1LCAtNjAuMjk4OTk0MDE3MDEzNjEgLTIyLjgyMjk3Mjk1MTQwMzY3IEMtNjAuMjgxMzQyNDkxMTA3NzM2IC0yMi45MDcxNTY5Mzg2NDgzNzQsIC02MC4yNjM2OTA5NjUyMDE4NSAtMjIuOTkxMzQwOTI1ODkzMDgsIC02MC4yMTQxODg4Mjk2OTY2NSAtMjMuMjI3NDI3NDM1NzAzOTk0IEMtNjAuMTY4MDA3MzI5NjMwMjg1IC0yMy4zODI1NDgzNjAwMTQzNzIsIC02MC4xMjE4MjU4Mjk1NjM5MiAtMjMuNTM3NjY5Mjg0MzI0NzQ2LCAtNjAuMDk2MjczNzA4NTAzMTc2IC0yMy42MjM0OTczNDYwMjM0MTcgQy02MC4wNTQ2NTAzNjUzNTQ0NSAtMjMuNzMwMTY4ODMyNDA2ODIsIC02MC4wMTMwMjcwMjIyMDU3MSAtMjMuODM2ODQwMzE4NzkwMjI3LCAtNTkuOTQ2MDU0MTMzMjc1MjkgLTI0LjAwODQ3NzEyMzI2NDg0NiBDLTU5Ljg3NTQxNjk3NjMwOTc2IC0yNC4xNTI5Njc3MzcyOTkxMzYsIC01OS44MDQ3Nzk4MTkzNDQyMjYgLTI0LjI5NzQ1ODM1MTMzMzQyLCAtNTkuNzY0NTU2MjU2MDMyNDUgLTI0LjM3OTczNjk2NTE4NTM2NiBDLTU5LjcwMDYyMTA5NTI1Nzk5IC0yNC40ODcwMzQwMDkyODU5LCAtNTkuNjM2Njg1OTM0NDgzNTQgLTI0LjU5NDMzMTA1MzM4NjQzLCAtNTkuNTUzMDE5ODkxMzEyNjQ0IC0yNC43MzQ3NDA3OTA2MTIxMzMgQy01OS40OTM3MDc1NjU0MzEwMiAtMjQuODE3ODEyODcxNzE2NzI1LCAtNTkuNDM0Mzk1MjM5NTQ5NCAtMjQuOTAwODg0OTUyODIxMzIsIC01OS4zMTI4OTAwNDY5ODE5NyAtMjUuMDcxMDYzNTYzNDQ4MzQgQy01OS4yMjI0MTUyNTI0OTI2MSAtMjUuMTc3ODg2OTUxNDQ1Nzk2LCAtNTkuMTMxOTQwNDU4MDAzMjYgLTI1LjI4NDcxMDMzOTQ0MzI1NCwgLTU5LjA0NTgwNzA1MzM2NTY2IC0yNS4zODY0MDc4NTgxMjg3MDYgQy01OC45NTQ4NjY5NzI1Njg4NDQgLTI1LjQ3NzM0NzkzODkyNTUyLCAtNTguODYzOTI2ODkxNzcyMDMgLTI1LjU2ODI4ODAxOTcyMjMzLCAtNTguNzUzNTk1MzU4MTI4NzA2IC0yNS42Nzg2MTk1NTMzNjU2NTcgQy01OC42ODU3ODExMzMyMTU0OSAtMjUuNzM2MDU1MjcyMDU1MTI1LCAtNTguNjE3OTY2OTA4MzAyMjggLTI1Ljc5MzQ5MDk5MDc0NDU5OCwgLTU4LjQzODI1MTA2MzQ0ODM0IC0yNS45NDU3MDI1NDY5ODE5NjYgQy01OC4zNjQ4NDkyMDg0NjE2MzUgLTI1Ljk5ODExMDQ2NDEyMzI5MywgLTU4LjI5MTQ0NzM1MzQ3NDkyIC0yNi4wNTA1MTgzODEyNjQ2MiwgLTU4LjEwMTkyODI5MDYxMjEzNiAtMjYuMTg1ODMyMzkxMzEyNjQ0IEMtNTcuOTc1NTMyNDU5MzkwMDIgLTI2LjI2MTE0Nzk1OTAyNjA5NywgLTU3Ljg0OTEzNjYyODE2NzkwNSAtMjYuMzM2NDYzNTI2NzM5NTQ2LCAtNTcuNzQ2OTI0NDY1MTg1MzY2IC0yNi4zOTczNjg3NTYwMzI0NDYgQy01Ny42MDkzMjgwNzM5MTc1NSAtMjYuNDY0NjM1NTMyODU5Njg1LCAtNTcuNDcxNzMxNjgyNjQ5NzQgLTI2LjUzMTkwMjMwOTY4NjkyLCAtNTcuMzc1NjY0NjIzMjY0ODQ2IC0yNi41Nzg4NjY2MzMyNzUyODYgQy01Ny4yNzQzNzA3ODI1MDY2OSAtMjYuNjE4MzkxNjEyNTg5NTg3LCAtNTcuMTczMDc2OTQxNzQ4NTQgLTI2LjY1NzkxNjU5MTkwMzg4NCwgLTU2Ljk5MDY4NDg0NjAyMzQyIC0yNi43MjkwODYyMDg1MDMxNzMgQy01Ni44ODU5NjU3MDA2Njk2MTUgLTI2Ljc2MDI2MjQ0ODg0MzQ1MiwgLTU2Ljc4MTI0NjU1NTMxNTgyIC0yNi43OTE0Mzg2ODkxODM3MywgLTU2LjU5NDYxNDkzNTcwMzk5NCAtMjYuODQ3MDAxMzI5Njk2NjUzIEMtNTYuNDkwMjQ4NTEyNjIwOTIgLTI2Ljg2ODg4NDY2NzM4NDEzNywgLTU2LjM4NTg4MjA4OTUzNzg2IC0yNi44OTA3NjgwMDUwNzE2MTcsIC01Ni4xOTAxNjA0NTE0MDM2NyAtMjYuOTMxODA2NTE3MDEzNjEyIEMtNTYuMDg2MzE4NTkyNjY3NjEgLTI2Ljk0NDc1MDM5MTIwNjMzLCAtNTUuOTgyNDc2NzMzOTMxNTU2IC0yNi45NTc2OTQyNjUzOTkwNTIsIC01NS43ODAwODQyMjczNjE2NiAtMjYuOTgyOTIyNDY1MDMzMzQ3IEMtNTUuNjk1MzIyNTMxNTM1NjIgLTI2Ljk4NjQyODIzNDcwNDk4LCAtNTUuNjEwNTYwODM1NzA5NTg1IC0yNi45ODk5MzQwMDQzNzY2MTgsIC01NS4zNjcxODc1IC0yNyBDLTU1LjM2NzE4NzUgLTI3LCAtNTUuMzY3MTg3NSAtMjcsIC01NS4zNjcxODc1IC0yNyIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjAiIGZpbGw9IiNFQ0VDRkYiIHN0eWxlPSIiLz48cGF0aCBkPSJNLTU1LjM2NzE4NzUgLTI3IEMtMjAuMjQyNzk3ODM4MTEyNzA4IC0yNywgMTQuODgxNTkxODIzNzc0NTg1IC0yNywgNTUuMzY3MTg3NSAtMjcgTS01NS4zNjcxODc1IC0yNyBDLTEyLjEwNTEzNzc2Mzc0NzU2NCAtMjcsIDMxLjE1NjkxMTk3MjUwNDg3MiAtMjcsIDU1LjM2NzE4NzUgLTI3IE01NS4zNjcxODc1IC0yNyBDNTUuMzY3MTg3NSAtMjcsIDU1LjM2NzE4NzUgLTI3LCA1NS4zNjcxODc1IC0yNyBNNTUuMzY3MTg3NSAtMjcgQzU1LjM2NzE4NzUgLTI3LCA1NS4zNjcxODc1IC0yNywgNTUuMzY3MTg3NSAtMjcgTTU1LjM2NzE4NzUgLTI3IEM1NS40Njg1MTUyMzY1NTI1NCAtMjYuOTk1ODA5MDUzODE1NjUsIDU1LjU2OTg0Mjk3MzEwNTA4IC0yNi45OTE2MTgxMDc2MzEyOTcsIDU1Ljc4MDA4NDIyNzM2MTY2IC0yNi45ODI5MjI0NjUwMzMzNDcgTTU1LjM2NzE4NzUgLTI3IEM1NS40ODYyNzY3MzcyNDMzMSAtMjYuOTk1MDc0NDMyNzEzMDY4LCA1NS42MDUzNjU5NzQ0ODY2MiAtMjYuOTkwMTQ4ODY1NDI2MTMyLCA1NS43ODAwODQyMjczNjE2NiAtMjYuOTgyOTIyNDY1MDMzMzQ3IE01NS43ODAwODQyMjczNjE2NiAtMjYuOTgyOTIyNDY1MDMzMzQ3IEM1NS44NjU4ODcxNDgzNDkwNCAtMjYuOTcyMjI3MTQyMDg2MDYyLCA1NS45NTE2OTAwNjkzMzY0MSAtMjYuOTYxNTMxODE5MTM4NzgsIDU2LjE5MDE2MDQ1MTQwMzY3IC0yNi45MzE4MDY1MTcwMTM2MTIgTTU1Ljc4MDA4NDIyNzM2MTY2IC0yNi45ODI5MjI0NjUwMzMzNDcgQzU1LjkxNjIwMzA1NzQ0NDk3NCAtMjYuOTY1OTU1MjcwNTU3OTc3LCA1Ni4wNTIzMjE4ODc1MjgyOSAtMjYuOTQ4OTg4MDc2MDgyNjEsIDU2LjE5MDE2MDQ1MTQwMzY3IC0yNi45MzE4MDY1MTcwMTM2MTIgTTU2LjE5MDE2MDQ1MTQwMzY3IC0yNi45MzE4MDY1MTcwMTM2MTIgQzU2LjMzMTIyODgxNDEwMDc3NiAtMjYuOTAyMjI3NTkxODUwNDUsIDU2LjQ3MjI5NzE3Njc5Nzg4IC0yNi44NzI2NDg2NjY2ODcyODcsIDU2LjU5NDYxNDkzNTcwMzk5NCAtMjYuODQ3MDAxMzI5Njk2NjUzIE01Ni4xOTAxNjA0NTE0MDM2NyAtMjYuOTMxODA2NTE3MDEzNjEyIEM1Ni4yNzkzNjMxMzA3MTE4MiAtMjYuOTEzMTAyNjgyMDQxOTUsIDU2LjM2ODU2NTgxMDAxOTk3IC0yNi44OTQzOTg4NDcwNzAyODUsIDU2LjU5NDYxNDkzNTcwMzk5NCAtMjYuODQ3MDAxMzI5Njk2NjUzIE01Ni41OTQ2MTQ5MzU3MDM5OTQgLTI2Ljg0NzAwMTMyOTY5NjY1MyBDNTYuNzE1NTI0NTQwNjYzNTQgLTI2LjgxMTAwNDk4MDY5MTU5NiwgNTYuODM2NDM0MTQ1NjIzMDcgLTI2Ljc3NTAwODYzMTY4NjUzNiwgNTYuOTkwNjg0ODQ2MDIzNDIgLTI2LjcyOTA4NjIwODUwMzE3MyBNNTYuNTk0NjE0OTM1NzAzOTk0IC0yNi44NDcwMDEzMjk2OTY2NTMgQzU2LjcxNDI3NDc0MDkzMDA4IC0yNi44MTEzNzcwNjIxOTMzNjUsIDU2LjgzMzkzNDU0NjE1NjE2NSAtMjYuNzc1NzUyNzk0NjkwMDc4LCA1Ni45OTA2ODQ4NDYwMjM0MiAtMjYuNzI5MDg2MjA4NTAzMTczIE01Ni45OTA2ODQ4NDYwMjM0MiAtMjYuNzI5MDg2MjA4NTAzMTczIEM1Ny4xMzU2MTAyMTcwNjk4NSAtMjYuNjcyNTM2MTUzMjM5NjU1LCA1Ny4yODA1MzU1ODgxMTYyOSAtMjYuNjE1OTg2MDk3OTc2MTQsIDU3LjM3NTY2NDYyMzI2NDg0NiAtMjYuNTc4ODY2NjMzMjc1Mjg2IE01Ni45OTA2ODQ4NDYwMjM0MiAtMjYuNzI5MDg2MjA4NTAzMTczIEM1Ny4xNDAxNTYyNTQzNzQxNyAtMjYuNjcwNzYyMjgzOTc5LCA1Ny4yODk2Mjc2NjI3MjQ5MSAtMjYuNjEyNDM4MzU5NDU0ODIsIDU3LjM3NTY2NDYyMzI2NDg0NiAtMjYuNTc4ODY2NjMzMjc1Mjg2IE01Ny4zNzU2NjQ2MjMyNjQ4NDYgLTI2LjU3ODg2NjYzMzI3NTI4NiBDNTcuNDc0MzUyNDc4NzY0MTggLTI2LjUzMDYyMTA4MDUzNzU1NywgNTcuNTczMDQwMzM0MjYzNTI0IC0yNi40ODIzNzU1Mjc3OTk4MjUsIDU3Ljc0NjkyNDQ2NTE4NTM2NiAtMjYuMzk3MzY4NzU2MDMyNDQ2IE01Ny4zNzU2NjQ2MjMyNjQ4NDYgLTI2LjU3ODg2NjYzMzI3NTI4NiBDNTcuNDg3Mjg4NjYyNjM2NiAtMjYuNTI0Mjk2OTY1NTk4NzU0LCA1Ny41OTg5MTI3MDIwMDgzNTYgLTI2LjQ2OTcyNzI5NzkyMjIyMywgNTcuNzQ2OTI0NDY1MTg1MzY2IC0yNi4zOTczNjg3NTYwMzI0NDYgTTU3Ljc0NjkyNDQ2NTE4NTM2NiAtMjYuMzk3MzY4NzU2MDMyNDQ2IEM1Ny44MzkwOTM1MDM0MzY3IC0yNi4zNDI0NDc5MzAxMjg3NTYsIDU3LjkzMTI2MjU0MTY4ODAzIC0yNi4yODc1MjcxMDQyMjUwNjcsIDU4LjEwMTkyODI5MDYxMjEzNiAtMjYuMTg1ODMyMzkxMzEyNjQ0IE01Ny43NDY5MjQ0NjUxODUzNjYgLTI2LjM5NzM2ODc1NjAzMjQ0NiBDNTcuODI5OTI3ODU5OTM0MDMgLTI2LjM0NzkwOTQ2ODE5NjgwNCwgNTcuOTEyOTMxMjU0NjgyNzA0IC0yNi4yOTg0NTAxODAzNjExNjUsIDU4LjEwMTkyODI5MDYxMjEzNiAtMjYuMTg1ODMyMzkxMzEyNjQ0IE01OC4xMDE5MjgyOTA2MTIxMzYgLTI2LjE4NTgzMjM5MTMxMjY0NCBDNTguMTczNjUzMzA4ODYyNTUgLTI2LjEzNDYyMTcxMjU4OTIyLCA1OC4yNDUzNzgzMjcxMTI5NiAtMjYuMDgzNDExMDMzODY1Nzk0LCA1OC40MzgyNTEwNjM0NDgzNCAtMjUuOTQ1NzAyNTQ2OTgxOTcgTTU4LjEwMTkyODI5MDYxMjEzNiAtMjYuMTg1ODMyMzkxMzEyNjQ0IEM1OC4xOTE4OTA1Njc1MDcwMSAtMjYuMTIxNjAwNTYwMTkzMzE1LCA1OC4yODE4NTI4NDQ0MDE4OSAtMjYuMDU3MzY4NzI5MDczOTksIDU4LjQzODI1MTA2MzQ0ODM0IC0yNS45NDU3MDI1NDY5ODE5NyBNNTguNDM4MjUxMDYzNDQ4MzQgLTI1Ljk0NTcwMjU0Njk4MTk3IEM1OC41MjYwOTM4MjY2NzgzNyAtMjUuODcxMzAzNTIxNjQxMTU2LCA1OC42MTM5MzY1ODk5MDg0MDUgLTI1Ljc5NjkwNDQ5NjMwMDM0LCA1OC43NTM1OTUzNTgxMjg3MDYgLTI1LjY3ODYxOTU1MzM2NTY1NyBNNTguNDM4MjUxMDYzNDQ4MzQgLTI1Ljk0NTcwMjU0Njk4MTk3IEM1OC41MDk4MTA0MjQ2Mjc2IC0yNS44ODUwOTQ4NTk2NzgxNiwgNTguNTgxMzY5Nzg1ODA2ODY0IC0yNS44MjQ0ODcxNzIzNzQzNDcsIDU4Ljc1MzU5NTM1ODEyODcwNiAtMjUuNjc4NjE5NTUzMzY1NjU3IE01OC43NTM1OTUzNTgxMjg3MDYgLTI1LjY3ODYxOTU1MzM2NTY1NyBDNTguODQzNDgyODQ4MjU2NDcgLTI1LjU4ODczMjA2MzIzNzg5NSwgNTguOTMzMzcwMzM4Mzg0MjMgLTI1LjQ5ODg0NDU3MzExMDEzLCA1OS4wNDU4MDcwNTMzNjU2NiAtMjUuMzg2NDA3ODU4MTI4NzA2IE01OC43NTM1OTUzNTgxMjg3MDYgLTI1LjY3ODYxOTU1MzM2NTY1NyBDNTguODcwMDQxMjA3OTAxOTIgLTI1LjU2MjE3MzcwMzU5MjQ0MywgNTguOTg2NDg3MDU3Njc1MTM0IC0yNS40NDU3Mjc4NTM4MTkyMzIsIDU5LjA0NTgwNzA1MzM2NTY2IC0yNS4zODY0MDc4NTgxMjg3MDYgTTU5LjA0NTgwNzA1MzM2NTY2IC0yNS4zODY0MDc4NTgxMjg3MDYgQzU5LjEzMjAyMzI2ODgzMjY3NSAtMjUuMjg0NjEyNTY0ODgxNzEyLCA1OS4yMTgyMzk0ODQyOTk2OTQgLTI1LjE4MjgxNzI3MTYzNDcyLCA1OS4zMTI4OTAwNDY5ODE5NyAtMjUuMDcxMDYzNTYzNDQ4MzQgTTU5LjA0NTgwNzA1MzM2NTY2IC0yNS4zODY0MDc4NTgxMjg3MDYgQzU5LjExMTE5MDI0MTMxNjEyIC0yNS4zMDkyMTAwNzM5OTA0NDIsIDU5LjE3NjU3MzQyOTI2NjU5IC0yNS4yMzIwMTIyODk4NTIxNzUsIDU5LjMxMjg5MDA0Njk4MTk3IC0yNS4wNzEwNjM1NjM0NDgzNCBNNTkuMzEyODkwMDQ2OTgxOTcgLTI1LjA3MTA2MzU2MzQ0ODM0IEM1OS4zNjY3MjA5MTA1NTE5OSAtMjQuOTk1NjY4NzQ3OTg3MTQzLCA1OS40MjA1NTE3NzQxMjIwMSAtMjQuOTIwMjczOTMyNTI1OTQ0LCA1OS41NTMwMTk4OTEzMTI2NDQgLTI0LjczNDc0MDc5MDYxMjEzNiBNNTkuMzEyODkwMDQ2OTgxOTcgLTI1LjA3MTA2MzU2MzQ0ODM0IEM1OS4zOTA3NTQwMDY5MjY1NiAtMjQuOTYyMDA4MzAyMTc2ODI3LCA1OS40Njg2MTc5NjY4NzExMzYgLTI0Ljg1Mjk1MzA0MDkwNTMxMiwgNTkuNTUzMDE5ODkxMzEyNjQ0IC0yNC43MzQ3NDA3OTA2MTIxMzYgTTU5LjU1MzAxOTg5MTMxMjY0NCAtMjQuNzM0NzQwNzkwNjEyMTM2IEM1OS42MjMxMzIzMzUwOTU3MSAtMjQuNjE3MDc2OTI4MDEzNDkzLCA1OS42OTMyNDQ3Nzg4Nzg3OCAtMjQuNDk5NDEzMDY1NDE0ODUzLCA1OS43NjQ1NTYyNTYwMzI0NSAtMjQuMzc5NzM2OTY1MTg1MzcgTTU5LjU1MzAxOTg5MTMxMjY0NCAtMjQuNzM0NzQwNzkwNjEyMTM2IEM1OS42MDA5NjkxMzYzMTM3NjUgLTI0LjY1NDI3MTU3NDcxOTc3LCA1OS42NDg5MTgzODEzMTQ4OSAtMjQuNTczODAyMzU4ODI3NDA0LCA1OS43NjQ1NTYyNTYwMzI0NSAtMjQuMzc5NzM2OTY1MTg1MzcgTTU5Ljc2NDU1NjI1NjAzMjQ1IC0yNC4zNzk3MzY5NjUxODUzNyBDNTkuODMzNzk2NTI5MzcwNTQgLTI0LjIzODEwMzcyMjI4MTcxLCA1OS45MDMwMzY4MDI3MDg2MzQgLTI0LjA5NjQ3MDQ3OTM3ODA1NiwgNTkuOTQ2MDU0MTMzMjc1MjkgLTI0LjAwODQ3NzEyMzI2NDg0NiBNNTkuNzY0NTU2MjU2MDMyNDUgLTI0LjM3OTczNjk2NTE4NTM3IEM1OS44MTI4NTE0MTA5OTIxNiAtMjQuMjgwOTQ3NjQ2NzE5MjQ1LCA1OS44NjExNDY1NjU5NTE4NyAtMjQuMTgyMTU4MzI4MjUzMTIsIDU5Ljk0NjA1NDEzMzI3NTI5IC0yNC4wMDg0NzcxMjMyNjQ4NDYgTTU5Ljk0NjA1NDEzMzI3NTI5IC0yNC4wMDg0NzcxMjMyNjQ4NDYgQzU5Ljk5Nzk5OTg5MjYzNjU2IC0yMy44NzUzNTE1NTEzMzMyMjUsIDYwLjA0OTk0NTY1MTk5NzgyNCAtMjMuNzQyMjI1OTc5NDAxNiwgNjAuMDk2MjczNzA4NTAzMTc2IC0yMy42MjM0OTczNDYwMjM0MTcgTTU5Ljk0NjA1NDEzMzI3NTI5IC0yNC4wMDg0NzcxMjMyNjQ4NDYgQzU5Ljk5MjQ0NTA2MDg3MDQ3IC0yMy44ODk1ODczNjUwOTIzNTMsIDYwLjAzODgzNTk4ODQ2NTY2NCAtMjMuNzcwNjk3NjA2OTE5ODYsIDYwLjA5NjI3MzcwODUwMzE3NiAtMjMuNjIzNDk3MzQ2MDIzNDE3IE02MC4wOTYyNzM3MDg1MDMxNzYgLTIzLjYyMzQ5NzM0NjAyMzQxNyBDNjAuMTIzNjI3MTk1OTcxNDE2IC0yMy41MzE2MTg2MDEyMjEwOTgsIDYwLjE1MDk4MDY4MzQzOTY2IC0yMy40Mzk3Mzk4NTY0MTg3OCwgNjAuMjE0MTg4ODI5Njk2NjUgLTIzLjIyNzQyNzQzNTcwMzk5NCBNNjAuMDk2MjczNzA4NTAzMTc2IC0yMy42MjM0OTczNDYwMjM0MTcgQzYwLjEzNjgzMTc1MjQ0MDk3NiAtMjMuNDg3MjY1Mjc3ODgzMzI4LCA2MC4xNzczODk3OTYzNzg3OCAtMjMuMzUxMDMzMjA5NzQzMjQsIDYwLjIxNDE4ODgyOTY5NjY1IC0yMy4yMjc0Mjc0MzU3MDM5OTQgTTYwLjIxNDE4ODgyOTY5NjY1IC0yMy4yMjc0Mjc0MzU3MDM5OTQgQzYwLjIzNjE2MTUzMjk3Nzk0NSAtMjMuMTIyNjM0ODA4NTYwNzQ3LCA2MC4yNTgxMzQyMzYyNTkyNCAtMjMuMDE3ODQyMTgxNDE3NSwgNjAuMjk4OTk0MDE3MDEzNjEgLTIyLjgyMjk3Mjk1MTQwMzY3IE02MC4yMTQxODg4Mjk2OTY2NSAtMjMuMjI3NDI3NDM1NzAzOTk0IEM2MC4yMzE0OTkzMTQ3MTY3OCAtMjMuMTQ0ODY5OTQ3MDI1NDQsIDYwLjI0ODgwOTc5OTczNjkgLTIzLjA2MjMxMjQ1ODM0Njg4OCwgNjAuMjk4OTk0MDE3MDEzNjEgLTIyLjgyMjk3Mjk1MTQwMzY3IE02MC4yOTg5OTQwMTcwMTM2MSAtMjIuODIyOTcyOTUxNDAzNjcgQzYwLjMxNjY3MzgwODg0NDE4IC0yMi42ODExMzczMjk1MTY1NzcsIDYwLjMzNDM1MzYwMDY3NDc0NiAtMjIuNTM5MzAxNzA3NjI5NDgsIDYwLjM1MDEwOTk2NTAzMzM1IC0yMi40MTI4OTY3MjczNjE2NjIgTTYwLjI5ODk5NDAxNzAxMzYxIC0yMi44MjI5NzI5NTE0MDM2NyBDNjAuMzEwMzg3NTU5MjA5NiAtMjIuNzMxNTY4NTg2NTU4NjgsIDYwLjMyMTc4MTEwMTQwNTU4NCAtMjIuNjQwMTY0MjIxNzEzNjkzLCA2MC4zNTAxMDk5NjUwMzMzNSAtMjIuNDEyODk2NzI3MzYxNjYyIE02MC4zNTAxMDk5NjUwMzMzNSAtMjIuNDEyODk2NzI3MzYxNjYyIEM2MC4zNTY3MzA5NDkwMDU5IC0yMi4yNTI4MTYwOTM5NzI5MDcsIDYwLjM2MzM1MTkzMjk3ODQ0NiAtMjIuMDkyNzM1NDYwNTg0MTQ4LCA2MC4zNjcxODc1IC0yMiBNNjAuMzUwMTA5OTY1MDMzMzUgLTIyLjQxMjg5NjcyNzM2MTY2MiBDNjAuMzUzOTgyMzQwMjUyODcgLTIyLjMxOTI3MTMyNjYwMDk0LCA2MC4zNTc4NTQ3MTU0NzI0IC0yMi4yMjU2NDU5MjU4NDAyMiwgNjAuMzY3MTg3NSAtMjIgTTYwLjM2NzE4NzUgLTIyIEM2MC4zNjcxODc1IC0yMiwgNjAuMzY3MTg3NSAtMjIsIDYwLjM2NzE4NzUgLTIyIE02MC4zNjcxODc1IC0yMiBDNjAuMzY3MTg3NSAtMjIsIDYwLjM2NzE4NzUgLTIyLCA2MC4zNjcxODc1IC0yMiBNNjAuMzY3MTg3NSAtMjIgQzYwLjM2NzE4NzUgLTEwLjM4OTE3MTUxNDIyNDUzMiwgNjAuMzY3MTg3NSAxLjIyMTY1Njk3MTU1MDkzNTEsIDYwLjM2NzE4NzUgMjIgTTYwLjM2NzE4NzUgLTIyIEM2MC4zNjcxODc1IC0xMC45NTU2NjIwMjIwMzk5OTcsIDYwLjM2NzE4NzUgMC4wODg2NzU5NTU5MjAwMDY1NCwgNjAuMzY3MTg3NSAyMiBNNjAuMzY3MTg3NSAyMiBDNjAuMzY3MTg3NSAyMiwgNjAuMzY3MTg3NSAyMiwgNjAuMzY3MTg3NSAyMiBNNjAuMzY3MTg3NSAyMiBDNjAuMzY3MTg3NSAyMiwgNjAuMzY3MTg3NSAyMiwgNjAuMzY3MTg3NSAyMiBNNjAuMzY3MTg3NSAyMiBDNjAuMzYyOTg3NDI5NzQ2ODEgMjIuMTAxNTQ4MzM2MTk4MjEzLCA2MC4zNTg3ODczNTk0OTM2MjYgMjIuMjAzMDk2NjcyMzk2NDMsIDYwLjM1MDEwOTk2NTAzMzM1IDIyLjQxMjg5NjcyNzM2MTY2MiBNNjAuMzY3MTg3NSAyMiBDNjAuMzYzMTk3NDcwMDg2NTA0IDIyLjA5NjQ3MDAyODk5Mjc1LCA2MC4zNTkyMDc0NDAxNzMwMSAyMi4xOTI5NDAwNTc5ODU1MDYsIDYwLjM1MDEwOTk2NTAzMzM1IDIyLjQxMjg5NjcyNzM2MTY2MiBNNjAuMzUwMTA5OTY1MDMzMzUgMjIuNDEyODk2NzI3MzYxNjYyIEM2MC4zMzAwMTYwODE0MTExOSAyMi41NzQwOTkzMzEyNzE3NCwgNjAuMzA5OTIyMTk3Nzg5MDM1IDIyLjczNTMwMTkzNTE4MTgxMywgNjAuMjk4OTk0MDE3MDEzNjEgMjIuODIyOTcyOTUxNDAzNjcgTTYwLjM1MDEwOTk2NTAzMzM1IDIyLjQxMjg5NjcyNzM2MTY2MiBDNjAuMzMyNzE3NDc5NDU0ODIgMjIuNTUyNDI3NDQzMDk4MjUsIDYwLjMxNTMyNDk5Mzg3NjI5IDIyLjY5MTk1ODE1ODgzNDg0LCA2MC4yOTg5OTQwMTcwMTM2MSAyMi44MjI5NzI5NTE0MDM2NyBNNjAuMjk4OTk0MDE3MDEzNjEgMjIuODIyOTcyOTUxNDAzNjcgQzYwLjI3NjIzMTM1Mzg3MjA2IDIyLjkzMTUzMzA2OTg3NjI0NSwgNjAuMjUzNDY4NjkwNzMwNTEgMjMuMDQwMDkzMTg4MzQ4ODE2LCA2MC4yMTQxODg4Mjk2OTY2NSAyMy4yMjc0Mjc0MzU3MDM5OTQgTTYwLjI5ODk5NDAxNzAxMzYxIDIyLjgyMjk3Mjk1MTQwMzY3IEM2MC4yNzcyOTI1Nzc2OTc2IDIyLjkyNjQ3MTg2MTM3Mzk3NiwgNjAuMjU1NTkxMTM4MzgxNTggMjMuMDI5OTcwNzcxMzQ0MjgsIDYwLjIxNDE4ODgyOTY5NjY1IDIzLjIyNzQyNzQzNTcwMzk5NCBNNjAuMjE0MTg4ODI5Njk2NjUgMjMuMjI3NDI3NDM1NzAzOTk0IEM2MC4xNzg5NzE4ODQzMjMyMiAyMy4zNDU3MTkwNzAwMTM0OTQsIDYwLjE0Mzc1NDkzODk0OTc5IDIzLjQ2NDAxMDcwNDMyMjk5LCA2MC4wOTYyNzM3MDg1MDMxNzYgMjMuNjIzNDk3MzQ2MDIzNDE3IE02MC4yMTQxODg4Mjk2OTY2NSAyMy4yMjc0Mjc0MzU3MDM5OTQgQzYwLjE3NzA4NTY1MzM3NzczIDIzLjM1MjA1NDgwODA3NTQyLCA2MC4xMzk5ODI0NzcwNTg4MSAyMy40NzY2ODIxODA0NDY4NDcsIDYwLjA5NjI3MzcwODUwMzE3NiAyMy42MjM0OTczNDYwMjM0MTcgTTYwLjA5NjI3MzcwODUwMzE3NiAyMy42MjM0OTczNDYwMjM0MTcgQzYwLjA2MzM2NzU4NjA0NTQ2NiAyMy43MDc4Mjg1MTA0MTY4MSwgNjAuMDMwNDYxNDYzNTg3NzU2IDIzLjc5MjE1OTY3NDgxMDIsIDU5Ljk0NjA1NDEzMzI3NTI5IDI0LjAwODQ3NzEyMzI2NDg0NiBNNjAuMDk2MjczNzA4NTAzMTc2IDIzLjYyMzQ5NzM0NjAyMzQxNyBDNjAuMDQyMTU5NDc4NjU3ODIgMjMuNzYyMTgwMjMxNTI1MiwgNTkuOTg4MDQ1MjQ4ODEyNDcgMjMuOTAwODYzMTE3MDI2OTgzLCA1OS45NDYwNTQxMzMyNzUyOSAyNC4wMDg0NzcxMjMyNjQ4NDYgTTU5Ljk0NjA1NDEzMzI3NTI5IDI0LjAwODQ3NzEyMzI2NDg0NiBDNTkuODg5MTA1NTc1NDIyNjEgMjQuMTI0OTY3MjYwMjY5MDM1LCA1OS44MzIxNTcwMTc1Njk5MjYgMjQuMjQxNDU3Mzk3MjczMjIzLCA1OS43NjQ1NTYyNTYwMzI0NSAyNC4zNzk3MzY5NjUxODUzNjYgTTU5Ljk0NjA1NDEzMzI3NTI5IDI0LjAwODQ3NzEyMzI2NDg0NiBDNTkuOTAwNDc0Njg2NTEzNzYgMjQuMTAxNzExMzcxODIyMywgNTkuODU0ODk1MjM5NzUyMjQgMjQuMTk0OTQ1NjIwMzc5NzU1LCA1OS43NjQ1NTYyNTYwMzI0NSAyNC4zNzk3MzY5NjUxODUzNjYgTTU5Ljc2NDU1NjI1NjAzMjQ1IDI0LjM3OTczNjk2NTE4NTM2NiBDNTkuNzEzNjQxNjIyNTI1NTggMjQuNDY1MTgyNzQ1MTA2MzgyLCA1OS42NjI3MjY5ODkwMTg3MSAyNC41NTA2Mjg1MjUwMjc0LCA1OS41NTMwMTk4OTEzMTI2NDQgMjQuNzM0NzQwNzkwNjEyMTMzIE01OS43NjQ1NTYyNTYwMzI0NSAyNC4zNzk3MzY5NjUxODUzNjYgQzU5LjY4OTUxMzkzNzYxMDk1NCAyNC41MDU2NzQyMjQ5MjcwMywgNTkuNjE0NDcxNjE5MTg5NDUgMjQuNjMxNjExNDg0NjY4NjkyLCA1OS41NTMwMTk4OTEzMTI2NDQgMjQuNzM0NzQwNzkwNjEyMTMzIE01OS41NTMwMTk4OTEzMTI2NDQgMjQuNzM0NzQwNzkwNjEyMTMzIEM1OS40NzAwNzQ1ODc5ODEzOCAyNC44NTA5MTI5MTYxMDQ3NzUsIDU5LjM4NzEyOTI4NDY1MDExIDI0Ljk2NzA4NTA0MTU5NzQyLCA1OS4zMTI4OTAwNDY5ODE5NyAyNS4wNzEwNjM1NjM0NDgzNCBNNTkuNTUzMDE5ODkxMzEyNjQ0IDI0LjczNDc0MDc5MDYxMjEzMyBDNTkuNDg3MTkzNjE4OTYzNTY2IDI0LjgyNjkzNjIyMTM5NTA5OCwgNTkuNDIxMzY3MzQ2NjE0NDkgMjQuOTE5MTMxNjUyMTc4MDY2LCA1OS4zMTI4OTAwNDY5ODE5NyAyNS4wNzEwNjM1NjM0NDgzNCBNNTkuMzEyODkwMDQ2OTgxOTcgMjUuMDcxMDYzNTYzNDQ4MzQgQzU5LjI1NzY2OTMyNDY0ODMyNiAyNS4xMzYyNjI1NDcxMDYwNTMsIDU5LjIwMjQ0ODYwMjMxNDY4IDI1LjIwMTQ2MTUzMDc2Mzc2NSwgNTkuMDQ1ODA3MDUzMzY1NjYgMjUuMzg2NDA3ODU4MTI4NzA2IE01OS4zMTI4OTAwNDY5ODE5NyAyNS4wNzEwNjM1NjM0NDgzNCBDNTkuMjExMjI3MzQzNTcyODIgMjUuMTkxMDk2NDkwNTQ2MzIsIDU5LjEwOTU2NDY0MDE2MzY3NiAyNS4zMTExMjk0MTc2NDQzLCA1OS4wNDU4MDcwNTMzNjU2NiAyNS4zODY0MDc4NTgxMjg3MDYgTTU5LjA0NTgwNzA1MzM2NTY2IDI1LjM4NjQwNzg1ODEyODcwNiBDNTguOTM4NzA5MzQ0NzMyODMgMjUuNDkzNTA1NTY2NzYxNTM0LCA1OC44MzE2MTE2MzYxIDI1LjYwMDYwMzI3NTM5NDM2MywgNTguNzUzNTk1MzU4MTI4NzA2IDI1LjY3ODYxOTU1MzM2NTY1NyBNNTkuMDQ1ODA3MDUzMzY1NjYgMjUuMzg2NDA3ODU4MTI4NzA2IEM1OC45MzYzMjM5ODExODM2OSAyNS40OTU4OTA5MzAzMTA2NzIsIDU4LjgyNjg0MDkwOTAwMTcyNCAyNS42MDUzNzQwMDI0OTI2NCwgNTguNzUzNTk1MzU4MTI4NzA2IDI1LjY3ODYxOTU1MzM2NTY1NyBNNTguNzUzNTk1MzU4MTI4NzA2IDI1LjY3ODYxOTU1MzM2NTY1NyBDNTguNjc1NDMwMTIwNzQzMzIgMjUuNzQ0ODIyMTMyNDE5MDYzLCA1OC41OTcyNjQ4ODMzNTc5NCAyNS44MTEwMjQ3MTE0NzI0NzQsIDU4LjQzODI1MTA2MzQ0ODM0IDI1Ljk0NTcwMjU0Njk4MTk3IE01OC43NTM1OTUzNTgxMjg3MDYgMjUuNjc4NjE5NTUzMzY1NjU3IEM1OC42Njk3NTAwNDg2MjYxOCAyNS43NDk2MzI5MDgxMDMyOCwgNTguNTg1OTA0NzM5MTIzNjQgMjUuODIwNjQ2MjYyODQwOTAzLCA1OC40MzgyNTEwNjM0NDgzNCAyNS45NDU3MDI1NDY5ODE5NyBNNTguNDM4MjUxMDYzNDQ4MzQgMjUuOTQ1NzAyNTQ2OTgxOTcgQzU4LjM1ODc4NjE5NjMyNjY3IDI2LjAwMjQzOTM3MTU4MjgzNSwgNTguMjc5MzIxMzI5MjA1IDI2LjA1OTE3NjE5NjE4MzcwMywgNTguMTAxOTI4MjkwNjEyMTM2IDI2LjE4NTgzMjM5MTMxMjY0NCBNNTguNDM4MjUxMDYzNDQ4MzQgMjUuOTQ1NzAyNTQ2OTgxOTcgQzU4LjM2MDgyODk3NTc0MzgzNiAyNi4wMDA5ODA4NTUxMTI3NSwgNTguMjgzNDA2ODg4MDM5MzMgMjYuMDU2MjU5MTYzMjQzNTM1LCA1OC4xMDE5MjgyOTA2MTIxMzYgMjYuMTg1ODMyMzkxMzEyNjQ0IE01OC4xMDE5MjgyOTA2MTIxMzYgMjYuMTg1ODMyMzkxMzEyNjQ0IEM1Ny45NzM1NTYxMjY4OTg1NSAyNi4yNjIzMjU1OTc1Nzc1MSwgNTcuODQ1MTgzOTYzMTg0OTY1IDI2LjMzODgxODgwMzg0MjM4MywgNTcuNzQ2OTI0NDY1MTg1MzY2IDI2LjM5NzM2ODc1NjAzMjQ0NiBNNTguMTAxOTI4MjkwNjEyMTM2IDI2LjE4NTgzMjM5MTMxMjY0NCBDNTguMDI3NTc5OTQ3MDEzMzEgMjYuMjMwMTM0Mzg4MDgzODgzLCA1Ny45NTMyMzE2MDM0MTQ0OCAyNi4yNzQ0MzYzODQ4NTUxMjUsIDU3Ljc0NjkyNDQ2NTE4NTM2NiAyNi4zOTczNjg3NTYwMzI0NDYgTTU3Ljc0NjkyNDQ2NTE4NTM2NiAyNi4zOTczNjg3NTYwMzI0NDYgQzU3LjYyMDQxMDYxMDE0MzQ0IDI2LjQ1OTIxNzYxMTAzNjg5OCwgNTcuNDkzODk2NzU1MTAxNTIgMjYuNTIxMDY2NDY2MDQxMzUsIDU3LjM3NTY2NDYyMzI2NDg0NiAyNi41Nzg4NjY2MzMyNzUyODYgTTU3Ljc0NjkyNDQ2NTE4NTM2NiAyNi4zOTczNjg3NTYwMzI0NDYgQzU3LjY0MjUxNzk3NDcwMTAyIDI2LjQ0ODQwOTk3OTA2MTczMywgNTcuNTM4MTExNDg0MjE2NjggMjYuNDk5NDUxMjAyMDkxMDIsIDU3LjM3NTY2NDYyMzI2NDg0NiAyNi41Nzg4NjY2MzMyNzUyODYgTTU3LjM3NTY2NDYyMzI2NDg0NiAyNi41Nzg4NjY2MzMyNzUyODYgQzU3LjI4NTE1MjMyMzM5MjcxIDI2LjYxNDE4NDY0MjI4MTE2LCA1Ny4xOTQ2NDAwMjM1MjA1NyAyNi42NDk1MDI2NTEyODcwMzMsIDU2Ljk5MDY4NDg0NjAyMzQyIDI2LjcyOTA4NjIwODUwMzE3MyBNNTcuMzc1NjY0NjIzMjY0ODQ2IDI2LjU3ODg2NjYzMzI3NTI4NiBDNTcuMjg2NDI3OTE5MjY2NzkgMjYuNjEzNjg2OTAzMjI2NTUsIDU3LjE5NzE5MTIxNTI2ODc0IDI2LjY0ODUwNzE3MzE3NzgxNiwgNTYuOTkwNjg0ODQ2MDIzNDIgMjYuNzI5MDg2MjA4NTAzMTczIE01Ni45OTA2ODQ4NDYwMjM0MiAyNi43MjkwODYyMDg1MDMxNzMgQzU2Ljg4NDIzMDIxMDE5OTY0IDI2Ljc2MDc3OTEyNjc0MjM5MiwgNTYuNzc3Nzc1NTc0Mzc1ODY2IDI2Ljc5MjQ3MjA0NDk4MTYxLCA1Ni41OTQ2MTQ5MzU3MDM5OTQgMjYuODQ3MDAxMzI5Njk2NjUzIE01Ni45OTA2ODQ4NDYwMjM0MiAyNi43MjkwODYyMDg1MDMxNzMgQzU2Ljg3NzI1NTQyMTI0MTQxNSAyNi43NjI4NTU2MTEzODI4MDgsIDU2Ljc2MzgyNTk5NjQ1OTQxIDI2Ljc5NjYyNTAxNDI2MjQ0LCA1Ni41OTQ2MTQ5MzU3MDM5OTQgMjYuODQ3MDAxMzI5Njk2NjUzIE01Ni41OTQ2MTQ5MzU3MDM5OTQgMjYuODQ3MDAxMzI5Njk2NjUzIEM1Ni41MDYxNTUwMjAwMDczIDI2Ljg2NTU0OTQyMzUxNjYsIDU2LjQxNzY5NTEwNDMxMDYgMjYuODg0MDk3NTE3MzM2NTQ2LCA1Ni4xOTAxNjA0NTE0MDM2NyAyNi45MzE4MDY1MTcwMTM2MTIgTTU2LjU5NDYxNDkzNTcwMzk5NCAyNi44NDcwMDEzMjk2OTY2NTMgQzU2LjQ3ODc1NDU5NDg0Nzk3IDI2Ljg3MTI5NDY4ODUwNTM3LCA1Ni4zNjI4OTQyNTM5OTE5NCAyNi44OTU1ODgwNDczMTQwODUsIDU2LjE5MDE2MDQ1MTQwMzY3IDI2LjkzMTgwNjUxNzAxMzYxMiBNNTYuMTkwMTYwNDUxNDAzNjcgMjYuOTMxODA2NTE3MDEzNjEyIEM1Ni4wNDg4Njg5MjU1Mzg2MzUgMjYuOTQ5NDE4NDg3MzQ1NzYsIDU1LjkwNzU3NzM5OTY3MzU5IDI2Ljk2NzAzMDQ1NzY3NzkwNCwgNTUuNzgwMDg0MjI3MzYxNjYgMjYuOTgyOTIyNDY1MDMzMzQ3IE01Ni4xOTAxNjA0NTE0MDM2NyAyNi45MzE4MDY1MTcwMTM2MTIgQzU2LjA3Nzk5NTA0Mzk5MzAyIDI2Ljk0NTc4NzkyMDQ2NjYxNCwgNTUuOTY1ODI5NjM2NTgyMzggMjYuOTU5NzY5MzIzOTE5NjE2LCA1NS43ODAwODQyMjczNjE2NiAyNi45ODI5MjI0NjUwMzMzNDcgTTU1Ljc4MDA4NDIyNzM2MTY2IDI2Ljk4MjkyMjQ2NTAzMzM0NyBDNTUuNjgwNTI4MDE2NjgyNzg2IDI2Ljk4NzA0MDE0MDM2NTU4LCA1NS41ODA5NzE4MDYwMDM5MSAyNi45OTExNTc4MTU2OTc4MDcsIDU1LjM2NzE4NzUgMjcgTTU1Ljc4MDA4NDIyNzM2MTY2IDI2Ljk4MjkyMjQ2NTAzMzM0NyBDNTUuNjgyMjQ3Nzc4OTM5NjMgMjYuOTg2OTY5MDEwNDcyNDg1LCA1NS41ODQ0MTEzMzA1MTc2IDI2Ljk5MTAxNTU1NTkxMTYyMywgNTUuMzY3MTg3NSAyNyBNNTUuMzY3MTg3NSAyNyBDNTUuMzY3MTg3NSAyNywgNTUuMzY3MTg3NSAyNywgNTUuMzY3MTg3NSAyNyBNNTUuMzY3MTg3NSAyNyBDNTUuMzY3MTg3NSAyNywgNTUuMzY3MTg3NSAyNywgNTUuMzY3MTg3NSAyNyBNNTUuMzY3MTg3NSAyNyBDMTEuMzI3ODIxNDU3NTYzMjEgMjcsIC0zMi43MTE1NDQ1ODQ4NzM1OCAyNywgLTU1LjM2NzE4NzUgMjcgTTU1LjM2NzE4NzUgMjcgQzI3LjkyOTA3NDcyNTYwMTY5NCAyNywgMC40OTA5NjE5NTEyMDMzODg1IDI3LCAtNTUuMzY3MTg3NSAyNyBNLTU1LjM2NzE4NzUgMjcgQy01NS4zNjcxODc1IDI3LCAtNTUuMzY3MTg3NSAyNywgLTU1LjM2NzE4NzUgMjcgTS01NS4zNjcxODc1IDI3IEMtNTUuMzY3MTg3NSAyNywgLTU1LjM2NzE4NzUgMjcsIC01NS4zNjcxODc1IDI3IE0tNTUuMzY3MTg3NSAyNyBDLTU1LjUwNjM2NTY3MjAzNTM3IDI2Ljk5NDI0MzU0ODIyMzk5LCAtNTUuNjQ1NTQzODQ0MDcwNzQ0IDI2Ljk4ODQ4NzA5NjQ0Nzk4MywgLTU1Ljc4MDA4NDIyNzM2MTY2IDI2Ljk4MjkyMjQ2NTAzMzM0NyBNLTU1LjM2NzE4NzUgMjcgQy01NS40NTg4OTU3Mzg3MDc1OSAyNi45OTYyMDY5MTkxODk1MzYsIC01NS41NTA2MDM5Nzc0MTUxOCAyNi45OTI0MTM4MzgzNzkwNzIsIC01NS43ODAwODQyMjczNjE2NiAyNi45ODI5MjI0NjUwMzMzNDcgTS01NS43ODAwODQyMjczNjE2NiAyNi45ODI5MjI0NjUwMzMzNDcgQy01NS45MDE2Nzc1Nzc2MzU5ODYgMjYuOTY3NzY1ODY5NzI3OTU1LCAtNTYuMDIzMjcwOTI3OTEwMzEgMjYuOTUyNjA5Mjc0NDIyNTY0LCAtNTYuMTkwMTYwNDUxNDAzNjcgMjYuOTMxODA2NTE3MDEzNjEyIE0tNTUuNzgwMDg0MjI3MzYxNjYgMjYuOTgyOTIyNDY1MDMzMzQ3IEMtNTUuOTAwOTczNjAzOTE5NTUgMjYuOTY3ODUzNjE5OTYwMiwgLTU2LjAyMTg2Mjk4MDQ3NzQ1IDI2Ljk1Mjc4NDc3NDg4NzA0NiwgLTU2LjE5MDE2MDQ1MTQwMzY3IDI2LjkzMTgwNjUxNzAxMzYxMiBNLTU2LjE5MDE2MDQ1MTQwMzY3IDI2LjkzMTgwNjUxNzAxMzYxMiBDLTU2LjMxNTY0MTA5MTE2OTA0IDI2LjkwNTQ5NTk5MzY0NjkwMiwgLTU2LjQ0MTEyMTczMDkzNDQyIDI2Ljg3OTE4NTQ3MDI4MDE5LCAtNTYuNTk0NjE0OTM1NzAzOTk0IDI2Ljg0NzAwMTMyOTY5NjY1MyBNLTU2LjE5MDE2MDQ1MTQwMzY3IDI2LjkzMTgwNjUxNzAxMzYxMiBDLTU2LjMxNDc1NTQ3Mzg3OTQ1IDI2LjkwNTY4MTY4ODA2NTA2MiwgLTU2LjQzOTM1MDQ5NjM1NTIzIDI2Ljg3OTU1Njg1OTExNjUxNiwgLTU2LjU5NDYxNDkzNTcwMzk5NCAyNi44NDcwMDEzMjk2OTY2NTMgTS01Ni41OTQ2MTQ5MzU3MDM5OTQgMjYuODQ3MDAxMzI5Njk2NjUzIEMtNTYuNjg3MDE1MzM3MzM2Mzc2IDI2LjgxOTQ5MjUzODI2MTI3LCAtNTYuNzc5NDE1NzM4OTY4NzUgMjYuNzkxOTgzNzQ2ODI1ODg1LCAtNTYuOTkwNjg0ODQ2MDIzNDIgMjYuNzI5MDg2MjA4NTAzMTczIE0tNTYuNTk0NjE0OTM1NzAzOTk0IDI2Ljg0NzAwMTMyOTY5NjY1MyBDLTU2LjcyNTE2MDU2ODg2MDQ3IDI2LjgwODEzNjIxMDgwMjU5LCAtNTYuODU1NzA2MjAyMDE2OTQgMjYuNzY5MjcxMDkxOTA4NTMsIC01Ni45OTA2ODQ4NDYwMjM0MiAyNi43MjkwODYyMDg1MDMxNzMgTS01Ni45OTA2ODQ4NDYwMjM0MiAyNi43MjkwODYyMDg1MDMxNzMgQy01Ny4wOTcxODQ2Mjk0Njc2NiAyNi42ODc1Mjk4NjQwNDkyMDcsIC01Ny4yMDM2ODQ0MTI5MTE5IDI2LjY0NTk3MzUxOTU5NTI0LCAtNTcuMzc1NjY0NjIzMjY0ODQ2IDI2LjU3ODg2NjYzMzI3NTI4NiBNLTU2Ljk5MDY4NDg0NjAyMzQyIDI2LjcyOTA4NjIwODUwMzE3MyBDLTU3LjEwNTEzODQ2NjY2ODc5IDI2LjY4NDQyNjI2NzE0MjUxMywgLTU3LjIxOTU5MjA4NzMxNDE3IDI2LjYzOTc2NjMyNTc4MTg1NiwgLTU3LjM3NTY2NDYyMzI2NDg0NiAyNi41Nzg4NjY2MzMyNzUyODYgTS01Ny4zNzU2NjQ2MjMyNjQ4NDYgMjYuNTc4ODY2NjMzMjc1Mjg2IEMtNTcuNDcxNTI4MzU1NDc4OTggMjYuNTMyMDAxNzEwMjgzNzg3LCAtNTcuNTY3MzkyMDg3NjkzMTE1IDI2LjQ4NTEzNjc4NzI5MjI4NywgLTU3Ljc0NjkyNDQ2NTE4NTM2NiAyNi4zOTczNjg3NTYwMzI0NDYgTS01Ny4zNzU2NjQ2MjMyNjQ4NDYgMjYuNTc4ODY2NjMzMjc1Mjg2IEMtNTcuNTE2ODAxODczNjk3NTk1IDI2LjUwOTg2ODgzNTg4MTA4NiwgLTU3LjY1NzkzOTEyNDEzMDM0NCAyNi40NDA4NzEwMzg0ODY4ODMsIC01Ny43NDY5MjQ0NjUxODUzNjYgMjYuMzk3MzY4NzU2MDMyNDQ2IE0tNTcuNzQ2OTI0NDY1MTg1MzY2IDI2LjM5NzM2ODc1NjAzMjQ0NiBDLTU3Ljg1MzU5NjQ4MzIwMjM0NSAyNi4zMzM4MDYwMjk5NjQ2MDYsIC01Ny45NjAyNjg1MDEyMTkzMiAyNi4yNzAyNDMzMDM4OTY3NywgLTU4LjEwMTkyODI5MDYxMjEzNiAyNi4xODU4MzIzOTEzMTI2NDQgTS01Ny43NDY5MjQ0NjUxODUzNjYgMjYuMzk3MzY4NzU2MDMyNDQ2IEMtNTcuODgwNTI4MDEwMTg2MjEgMjYuMzE3NzU4MzIzMTMzMDc3LCAtNTguMDE0MTMxNTU1MTg3MDUgMjYuMjM4MTQ3ODkwMjMzNzEyLCAtNTguMTAxOTI4MjkwNjEyMTM2IDI2LjE4NTgzMjM5MTMxMjY0NCBNLTU4LjEwMTkyODI5MDYxMjEzNiAyNi4xODU4MzIzOTEzMTI2NDQgQy01OC4yMTQ4OTAxOTE0OTQ5NiAyNi4xMDUxNzkxNDQyOTc3NiwgLTU4LjMyNzg1MjA5MjM3Nzc4IDI2LjAyNDUyNTg5NzI4Mjg3NCwgLTU4LjQzODI1MTA2MzQ0ODM0IDI1Ljk0NTcwMjU0Njk4MTk3IE0tNTguMTAxOTI4MjkwNjEyMTM2IDI2LjE4NTgzMjM5MTMxMjY0NCBDLTU4LjE4OTIyNDkxNjM4MjUyIDI2LjEyMzUwMzc5ODUxNDU0NSwgLTU4LjI3NjUyMTU0MjE1MjkgMjYuMDYxMTc1MjA1NzE2NDQ1LCAtNTguNDM4MjUxMDYzNDQ4MzQgMjUuOTQ1NzAyNTQ2OTgxOTcgTS01OC40MzgyNTEwNjM0NDgzNCAyNS45NDU3MDI1NDY5ODE5NyBDLTU4LjUwNzEyNzYxOTg1MTQ0IDI1Ljg4NzM2NzA3OTQxNzAxLCAtNTguNTc2MDA0MTc2MjU0NTM2IDI1LjgyOTAzMTYxMTg1MjA1MiwgLTU4Ljc1MzU5NTM1ODEyODcwNiAyNS42Nzg2MTk1NTMzNjU2NiBNLTU4LjQzODI1MTA2MzQ0ODM0IDI1Ljk0NTcwMjU0Njk4MTk3IEMtNTguNTAyMzA5MTcwNTg4MDIgMjUuODkxNDQ4MDk3NzY5MjQyLCAtNTguNTY2MzY3Mjc3NzI3NjkgMjUuODM3MTkzNjQ4NTU2NTE0LCAtNTguNzUzNTk1MzU4MTI4NzA2IDI1LjY3ODYxOTU1MzM2NTY2IE0tNTguNzUzNTk1MzU4MTI4NzA2IDI1LjY3ODYxOTU1MzM2NTY2IEMtNTguODQ5MTc1MzE1MTMwNzEgMjUuNTgzMDM5NTk2MzYzNjU1LCAtNTguOTQ0NzU1MjcyMTMyNzIgMjUuNDg3NDU5NjM5MzYxNjUsIC01OS4wNDU4MDcwNTMzNjU2NiAyNS4zODY0MDc4NTgxMjg3MDYgTS01OC43NTM1OTUzNTgxMjg3MDYgMjUuNjc4NjE5NTUzMzY1NjYgQy01OC44Mjc0MzgwNDE3MjIwOSAyNS42MDQ3NzY4Njk3NzIyNzcsIC01OC45MDEyODA3MjUzMTU0NyAyNS41MzA5MzQxODYxNzg4OTMsIC01OS4wNDU4MDcwNTMzNjU2NiAyNS4zODY0MDc4NTgxMjg3MDYgTS01OS4wNDU4MDcwNTMzNjU2NiAyNS4zODY0MDc4NTgxMjg3MDYgQy01OS4xMjI2OTYyMDk4NzQwMSAyNS4yOTU2MjUwMDI1ODQ4MywgLTU5LjE5OTU4NTM2NjM4MjM2NSAyNS4yMDQ4NDIxNDcwNDA5NiwgLTU5LjMxMjg5MDA0Njk4MTk3IDI1LjA3MTA2MzU2MzQ0ODM0IE0tNTkuMDQ1ODA3MDUzMzY1NjYgMjUuMzg2NDA3ODU4MTI4NzA2IEMtNTkuMTQxNjE4Nzc4ODUzOTc0IDI1LjI3MzI4MzE2NzYwNzg4LCAtNTkuMjM3NDMwNTA0MzQyMjkgMjUuMTYwMTU4NDc3MDg3MDQ4LCAtNTkuMzEyODkwMDQ2OTgxOTcgMjUuMDcxMDYzNTYzNDQ4MzQgTS01OS4zMTI4OTAwNDY5ODE5NyAyNS4wNzEwNjM1NjM0NDgzNCBDLTU5LjM3NTI5MjQ2ODA4Nzc0IDI0Ljk4MzY2MzUzNDY5OTM3LCAtNTkuNDM3Njk0ODg5MTkzNTIgMjQuODk2MjYzNTA1OTUwMzk3LCAtNTkuNTUzMDE5ODkxMzEyNjQ0IDI0LjczNDc0MDc5MDYxMjEzMyBNLTU5LjMxMjg5MDA0Njk4MTk3IDI1LjA3MTA2MzU2MzQ0ODM0IEMtNTkuMzk3MTgzNjMwNjgwNjcgMjQuOTUzMDAzMDUzODA2Njk3LCAtNTkuNDgxNDc3MjE0Mzc5Mzc1IDI0LjgzNDk0MjU0NDE2NTA1MiwgLTU5LjU1MzAxOTg5MTMxMjY0NCAyNC43MzQ3NDA3OTA2MTIxMzMgTS01OS41NTMwMTk4OTEzMTI2NDQgMjQuNzM0NzQwNzkwNjEyMTMzIEMtNTkuNjI4NTA1MjE3NzY2NzQgMjQuNjA4MDYwMDY3NDY0MTg3LCAtNTkuNzAzOTkwNTQ0MjIwODMgMjQuNDgxMzc5MzQ0MzE2MjQsIC01OS43NjQ1NTYyNTYwMzI0NCAyNC4zNzk3MzY5NjUxODUzNyBNLTU5LjU1MzAxOTg5MTMxMjY0NCAyNC43MzQ3NDA3OTA2MTIxMzMgQy01OS42MzQxNTIxMDU2ODMzMiAyNC41OTg1ODMzNjY3ODA2MDIsIC01OS43MTUyODQzMjAwNTM5OTYgMjQuNDYyNDI1OTQyOTQ5MDc1LCAtNTkuNzY0NTU2MjU2MDMyNDQgMjQuMzc5NzM2OTY1MTg1MzcgTS01OS43NjQ1NTYyNTYwMzI0NCAyNC4zNzk3MzY5NjUxODUzNyBDLTU5LjgxNTk2MzgwNzkyMTE2NSAyNC4yNzQ1ODExMzcwNDIwNCwgLTU5Ljg2NzM3MTM1OTgwOTg4IDI0LjE2OTQyNTMwODg5ODcxMywgLTU5Ljk0NjA1NDEzMzI3NTI4IDI0LjAwODQ3NzEyMzI2NDg1IE0tNTkuNzY0NTU2MjU2MDMyNDQgMjQuMzc5NzM2OTY1MTg1MzcgQy01OS44MTE1MjQyNDQyMzQzMDYgMjQuMjgzNjYyNDA5NzEzNzUyLCAtNTkuODU4NDkyMjMyNDM2MTYgMjQuMTg3NTg3ODU0MjQyMTM1LCAtNTkuOTQ2MDU0MTMzMjc1MjggMjQuMDA4NDc3MTIzMjY0ODUgTS01OS45NDYwNTQxMzMyNzUyOCAyNC4wMDg0NzcxMjMyNjQ4NSBDLTU5Ljk4NzUxMTgzNjA5NDk2IDIzLjkwMjIzMDEzNjY2Mzc1LCAtNjAuMDI4OTY5NTM4OTE0NjQgMjMuNzk1OTgzMTUwMDYyNjU0LCAtNjAuMDk2MjczNzA4NTAzMTc2IDIzLjYyMzQ5NzM0NjAyMzQxNyBNLTU5Ljk0NjA1NDEzMzI3NTI4IDI0LjAwODQ3NzEyMzI2NDg1IEMtNTkuOTgwMzc3NjQ0OTM4NDY0IDIzLjkyMDUxMzUwMTY1MTY5OCwgLTYwLjAxNDcwMTE1NjYwMTY0IDIzLjgzMjU0OTg4MDAzODU1LCAtNjAuMDk2MjczNzA4NTAzMTc2IDIzLjYyMzQ5NzM0NjAyMzQxNyBNLTYwLjA5NjI3MzcwODUwMzE3NiAyMy42MjM0OTczNDYwMjM0MTcgQy02MC4xMzY4Mzk3NTE1MDkyIDIzLjQ4NzIzODQwOTQ4NjgwMywgLTYwLjE3NzQwNTc5NDUxNTIyIDIzLjM1MDk3OTQ3Mjk1MDE5LCAtNjAuMjE0MTg4ODI5Njk2NjUgMjMuMjI3NDI3NDM1NzAzOTk0IE0tNjAuMDk2MjczNzA4NTAzMTc2IDIzLjYyMzQ5NzM0NjAyMzQxNyBDLTYwLjEyOTQwMDYxMjk3NTg1IDIzLjUxMjIyNjAzNTM0Nzk2NSwgLTYwLjE2MjUyNzUxNzQ0ODUzIDIzLjQwMDk1NDcyNDY3MjUxMywgLTYwLjIxNDE4ODgyOTY5NjY1IDIzLjIyNzQyNzQzNTcwMzk5NCBNLTYwLjIxNDE4ODgyOTY5NjY1IDIzLjIyNzQyNzQzNTcwMzk5NCBDLTYwLjIzNDk5NDY3NTE0NjMzIDIzLjEyODE5OTgwODczOTAxLCAtNjAuMjU1ODAwNTIwNTk2MDEgMjMuMDI4OTcyMTgxNzc0MDIzLCAtNjAuMjk4OTk0MDE3MDEzNjEgMjIuODIyOTcyOTUxNDAzNjcgTS02MC4yMTQxODg4Mjk2OTY2NSAyMy4yMjc0Mjc0MzU3MDM5OTQgQy02MC4yMzI0Mjg1MDMwNzQ3MSAyMy4xNDA0Mzg0NDQ1NDI5NCwgLTYwLjI1MDY2ODE3NjQ1Mjc1NiAyMy4wNTM0NDk0NTMzODE4ODQsIC02MC4yOTg5OTQwMTcwMTM2MSAyMi44MjI5NzI5NTE0MDM2NyBNLTYwLjI5ODk5NDAxNzAxMzYxIDIyLjgyMjk3Mjk1MTQwMzY3IEMtNjAuMzE3MTQyNzY3NDEwNDUgMjIuNjc3Mzc1MTIyODk1MzM4LCAtNjAuMzM1MjkxNTE3ODA3MyAyMi41MzE3NzcyOTQzODcwMSwgLTYwLjM1MDEwOTk2NTAzMzM1IDIyLjQxMjg5NjcyNzM2MTY2MiBNLTYwLjI5ODk5NDAxNzAxMzYxIDIyLjgyMjk3Mjk1MTQwMzY3IEMtNjAuMzE4MjcyODkwMjUxNDYgMjIuNjY4MzA4NzQ0ODgwMzYsIC02MC4zMzc1NTE3NjM0ODkyOTQgMjIuNTEzNjQ0NTM4MzU3MDUsIC02MC4zNTAxMDk5NjUwMzMzNSAyMi40MTI4OTY3MjczNjE2NjIgTS02MC4zNTAxMDk5NjUwMzMzNSAyMi40MTI4OTY3MjczNjE2NjIgQy02MC4zNTQ0NjI2NDExMjkwMjUgMjIuMzA3NjU4NzIyMDcxMDU3LCAtNjAuMzU4ODE1MzE3MjI0NyAyMi4yMDI0MjA3MTY3ODA0NTUsIC02MC4zNjcxODc1IDIyIE0tNjAuMzUwMTA5OTY1MDMzMzUgMjIuNDEyODk2NzI3MzYxNjYyIEMtNjAuMzU1OTI0NTY4NjAxNjcgMjIuMjcyMzEyNTc0NjE2MjIsIC02MC4zNjE3MzkxNzIxNyAyMi4xMzE3Mjg0MjE4NzA3OCwgLTYwLjM2NzE4NzUgMjIgTS02MC4zNjcxODc1IDIyIEMtNjAuMzY3MTg3NSAyMiwgLTYwLjM2NzE4NzUgMjIsIC02MC4zNjcxODc1IDIyIE0tNjAuMzY3MTg3NSAyMiBDLTYwLjM2NzE4NzUgMjIsIC02MC4zNjcxODc1IDIyLCAtNjAuMzY3MTg3NSAyMiBNLTYwLjM2NzE4NzUgMjIgQy02MC4zNjcxODc1IDUuMjA5NjMxOTkzOTAxNzg3LCAtNjAuMzY3MTg3NSAtMTEuNTgwNzM2MDEyMTk2NDI1LCAtNjAuMzY3MTg3NSAtMjIgTS02MC4zNjcxODc1IDIyIEMtNjAuMzY3MTg3NSA1LjM3NDAwMTYwMTcxOTE0NCwgLTYwLjM2NzE4NzUgLTExLjI1MTk5Njc5NjU2MTcxMywgLTYwLjM2NzE4NzUgLTIyIE0tNjAuMzY3MTg3NSAtMjIgQy02MC4zNjcxODc1IC0yMiwgLTYwLjM2NzE4NzUgLTIyLCAtNjAuMzY3MTg3NSAtMjIgTS02MC4zNjcxODc1IC0yMiBDLTYwLjM2NzE4NzUgLTIyLCAtNjAuMzY3MTg3NSAtMjIsIC02MC4zNjcxODc1IC0yMiBNLTYwLjM2NzE4NzUgLTIyIEMtNjAuMzYzNjY5MjAwNjY1OTUgLTIyLjA4NTA2NDYzNTEzMjkxNiwgLTYwLjM2MDE1MDkwMTMzMTkgLTIyLjE3MDEyOTI3MDI2NTgzNiwgLTYwLjM1MDEwOTk2NTAzMzM1IC0yMi40MTI4OTY3MjczNjE2NiBNLTYwLjM2NzE4NzUgLTIyIEMtNjAuMzYzNTM4MjU1NzU1MDk1IC0yMi4wODgyMzA1OTExODM0NDYsIC02MC4zNTk4ODkwMTE1MTAxOSAtMjIuMTc2NDYxMTgyMzY2ODkzLCAtNjAuMzUwMTA5OTY1MDMzMzUgLTIyLjQxMjg5NjcyNzM2MTY2IE0tNjAuMzUwMTA5OTY1MDMzMzUgLTIyLjQxMjg5NjcyNzM2MTY2IEMtNjAuMzM5NDk4MzUwMzU0NDMgLTIyLjQ5ODAyODEwMTE3MTYsIC02MC4zMjg4ODY3MzU2NzU1MiAtMjIuNTgzMTU5NDc0OTgxNTQsIC02MC4yOTg5OTQwMTcwMTM2MSAtMjIuODIyOTcyOTUxNDAzNjcgTS02MC4zNTAxMDk5NjUwMzMzNSAtMjIuNDEyODk2NzI3MzYxNjYgQy02MC4zMzIyMTI0MjYzNTk4NzQgLTIyLjU1NjQ3OTIxNzAzODQ1OCwgLTYwLjMxNDMxNDg4NzY4NjQgLTIyLjcwMDA2MTcwNjcxNTI1NywgLTYwLjI5ODk5NDAxNzAxMzYxIC0yMi44MjI5NzI5NTE0MDM2NyBNLTYwLjI5ODk5NDAxNzAxMzYxIC0yMi44MjI5NzI5NTE0MDM2NyBDLTYwLjI4MDc4NTQ4MjM1NjQ4NSAtMjIuOTA5ODEzNDM1MTk0OTMsIC02MC4yNjI1NzY5NDc2OTkzNiAtMjIuOTk2NjUzOTE4OTg2MTk0LCAtNjAuMjE0MTg4ODI5Njk2NjUgLTIzLjIyNzQyNzQzNTcwMzk5NCBNLTYwLjI5ODk5NDAxNzAxMzYxIC0yMi44MjI5NzI5NTE0MDM2NyBDLTYwLjI3NzUwMDA0MTE5MzMzNSAtMjIuOTI1NDgyNDIyNTkyNzc2LCAtNjAuMjU2MDA2MDY1MzczMDYgLTIzLjAyNzk5MTg5Mzc4MTg3NywgLTYwLjIxNDE4ODgyOTY5NjY1IC0yMy4yMjc0Mjc0MzU3MDM5OTQgTS02MC4yMTQxODg4Mjk2OTY2NSAtMjMuMjI3NDI3NDM1NzAzOTk0IEMtNjAuMTc3OTEwMTYyODQ1NTMgLTIzLjM0OTI4NTMyOTU5MjM5NCwgLTYwLjE0MTYzMTQ5NTk5NDQxIC0yMy40NzExNDMyMjM0ODA3OTQsIC02MC4wOTYyNzM3MDg1MDMxNzYgLTIzLjYyMzQ5NzM0NjAyMzQxNyBNLTYwLjIxNDE4ODgyOTY5NjY1IC0yMy4yMjc0Mjc0MzU3MDM5OTQgQy02MC4xNzE0MTMxNjc3OTUwMSAtMjMuMzcxMTA4MzUxMjc5MTksIC02MC4xMjg2Mzc1MDU4OTMzNiAtMjMuNTE0Nzg5MjY2ODU0Mzg2LCAtNjAuMDk2MjczNzA4NTAzMTc2IC0yMy42MjM0OTczNDYwMjM0MTcgTS02MC4wOTYyNzM3MDg1MDMxNzYgLTIzLjYyMzQ5NzM0NjAyMzQxNyBDLTYwLjA0MzQ4Nzg1NjM4Nzk1IC0yMy43NTg3NzU4OTExNjYzNjIsIC01OS45OTA3MDIwMDQyNzI3MiAtMjMuODk0MDU0NDM2MzA5MzA3LCAtNTkuOTQ2MDU0MTMzMjc1MjkgLTI0LjAwODQ3NzEyMzI2NDg0NiBNLTYwLjA5NjI3MzcwODUwMzE3NiAtMjMuNjIzNDk3MzQ2MDIzNDE3IEMtNjAuMDQ2Nzc2ODI4OTExNCAtMjMuNzUwMzQ2OTc2OTc1MTgsIC01OS45OTcyNzk5NDkzMTk2MiAtMjMuODc3MTk2NjA3OTI2OTQ4LCAtNTkuOTQ2MDU0MTMzMjc1MjkgLTI0LjAwODQ3NzEyMzI2NDg0NiBNLTU5Ljk0NjA1NDEzMzI3NTI5IC0yNC4wMDg0NzcxMjMyNjQ4NDYgQy01OS44ODA5NTcwOTAwOTc4NDQgLTI0LjE0MTYzNTI1MzQxMzE5NSwgLTU5LjgxNTg2MDA0NjkyMDQgLTI0LjI3NDc5MzM4MzU2MTU0OCwgLTU5Ljc2NDU1NjI1NjAzMjQ1IC0yNC4zNzk3MzY5NjUxODUzNjYgTS01OS45NDYwNTQxMzMyNzUyOSAtMjQuMDA4NDc3MTIzMjY0ODQ2IEMtNTkuODgzOTMyMDkxMTM3NzIgLTI0LjEzNTU0OTc5MTUxOTMyOCwgLTU5LjgyMTgxMDA0OTAwMDE2IC0yNC4yNjI2MjI0NTk3NzM4MDUsIC01OS43NjQ1NTYyNTYwMzI0NSAtMjQuMzc5NzM2OTY1MTg1MzY2IE0tNTkuNzY0NTU2MjU2MDMyNDUgLTI0LjM3OTczNjk2NTE4NTM2NiBDLTU5LjcwMTIzNDY5OTYwMzM4IC0yNC40ODYwMDQyNDgzMjYzNiwgLTU5LjYzNzkxMzE0MzE3NDMxNiAtMjQuNTkyMjcxNTMxNDY3MzUsIC01OS41NTMwMTk4OTEzMTI2NDQgLTI0LjczNDc0MDc5MDYxMjEzMyBNLTU5Ljc2NDU1NjI1NjAzMjQ1IC0yNC4zNzk3MzY5NjUxODUzNjYgQy01OS43MDg2OTYxNDMzNjY2NiAtMjQuNDczNDgyMzMwMDEzNzM4LCAtNTkuNjUyODM2MDMwNzAwODcgLTI0LjU2NzIyNzY5NDg0MjExLCAtNTkuNTUzMDE5ODkxMzEyNjQ0IC0yNC43MzQ3NDA3OTA2MTIxMzMgTS01OS41NTMwMTk4OTEzMTI2NDQgLTI0LjczNDc0MDc5MDYxMjEzMyBDLTU5LjUwNDYwNzU5MTM5NDYyNCAtMjQuODAyNTQ2NDM1NDgzOTMzLCAtNTkuNDU2MTk1MjkxNDc2NjA0IC0yNC44NzAzNTIwODAzNTU3MywgLTU5LjMxMjg5MDA0Njk4MTk3IC0yNS4wNzEwNjM1NjM0NDgzNCBNLTU5LjU1MzAxOTg5MTMxMjY0NCAtMjQuNzM0NzQwNzkwNjEyMTMzIEMtNTkuNDYxNzM5MzQwODUyMjEgLTI0Ljg2MjU4NzE1NjA3NTExLCAtNTkuMzcwNDU4NzkwMzkxNzggLTI0Ljk5MDQzMzUyMTUzODA5LCAtNTkuMzEyODkwMDQ2OTgxOTcgLTI1LjA3MTA2MzU2MzQ0ODM0IE0tNTkuMzEyODkwMDQ2OTgxOTcgLTI1LjA3MTA2MzU2MzQ0ODM0IEMtNTkuMjE4MTkwNTgwNzAyODc0IC0yNS4xODI4NzUwMTIwMDIzNzMsIC01OS4xMjM0OTExMTQ0MjM3ODYgLTI1LjI5NDY4NjQ2MDU1NjQwNiwgLTU5LjA0NTgwNzA1MzM2NTY2IC0yNS4zODY0MDc4NTgxMjg3MDYgTS01OS4zMTI4OTAwNDY5ODE5NyAtMjUuMDcxMDYzNTYzNDQ4MzQgQy01OS4yMjI2MDEwMDQ0NjUyMiAtMjUuMTc3NjY3NjM0NTA2MTY4LCAtNTkuMTMyMzExOTYxOTQ4NDcgLTI1LjI4NDI3MTcwNTU2NCwgLTU5LjA0NTgwNzA1MzM2NTY2IC0yNS4zODY0MDc4NTgxMjg3MDYgTS01OS4wNDU4MDcwNTMzNjU2NiAtMjUuMzg2NDA3ODU4MTI4NzA2IEMtNTguOTMxNzg1Njk1MjEzNzU1IC0yNS41MDA0MjkyMTYyODA2MDgsIC01OC44MTc3NjQzMzcwNjE4NSAtMjUuNjE0NDUwNTc0NDMyNTE0LCAtNTguNzUzNTk1MzU4MTI4NzA2IC0yNS42Nzg2MTk1NTMzNjU2NTcgTS01OS4wNDU4MDcwNTMzNjU2NiAtMjUuMzg2NDA3ODU4MTI4NzA2IEMtNTguOTcwMTE5MjM1NDczNjYgLTI1LjQ2MjA5NTY3NjAyMDcwNiwgLTU4Ljg5NDQzMTQxNzU4MTY2IC0yNS41Mzc3ODM0OTM5MTI3MDIsIC01OC43NTM1OTUzNTgxMjg3MDYgLTI1LjY3ODYxOTU1MzM2NTY1NyBNLTU4Ljc1MzU5NTM1ODEyODcwNiAtMjUuNjc4NjE5NTUzMzY1NjU3IEMtNTguNjgyMjg2MDcwNDgyNzYgLTI1LjczOTAxNTQzOTE5MDgsIC01OC42MTA5NzY3ODI4MzY4MTUgLTI1Ljc5OTQxMTMyNTAxNTk0NiwgLTU4LjQzODI1MTA2MzQ0ODM0IC0yNS45NDU3MDI1NDY5ODE5NjYgTS01OC43NTM1OTUzNTgxMjg3MDYgLTI1LjY3ODYxOTU1MzM2NTY1NyBDLTU4LjYzMjY3OTcwNTA1MjQ0NiAtMjUuNzgxMDI5ODg3NTQxNTUsIC01OC41MTE3NjQwNTE5NzYxOSAtMjUuODgzNDQwMjIxNzE3NDQ2LCAtNTguNDM4MjUxMDYzNDQ4MzQgLTI1Ljk0NTcwMjU0Njk4MTk2NiBNLTU4LjQzODI1MTA2MzQ0ODM0IC0yNS45NDU3MDI1NDY5ODE5NjYgQy01OC4zMTQxNDQyNzEwMDEzNTQgLTI2LjAzNDMxMzA5MzYyODQ0NSwgLTU4LjE5MDAzNzQ3ODU1NDM2IC0yNi4xMjI5MjM2NDAyNzQ5MjMsIC01OC4xMDE5MjgyOTA2MTIxMzYgLTI2LjE4NTgzMjM5MTMxMjY0NCBNLTU4LjQzODI1MTA2MzQ0ODM0IC0yNS45NDU3MDI1NDY5ODE5NjYgQy01OC4zMjgzMTEyOTA1MjI5NSAtMjYuMDI0MTk4MDM2MTA4OTYsIC01OC4yMTgzNzE1MTc1OTc1NTQgLTI2LjEwMjY5MzUyNTIzNTk1MywgLTU4LjEwMTkyODI5MDYxMjEzNiAtMjYuMTg1ODMyMzkxMzEyNjQ0IE0tNTguMTAxOTI4MjkwNjEyMTM2IC0yNi4xODU4MzIzOTEzMTI2NDQgQy01Ny45NjYxNjgzOTcwNzc4NjUgLTI2LjI2NjcyNzcyOTA0MTY0OCwgLTU3LjgzMDQwODUwMzU0MzU5NSAtMjYuMzQ3NjIzMDY2NzcwNjUsIC01Ny43NDY5MjQ0NjUxODUzNjYgLTI2LjM5NzM2ODc1NjAzMjQ0NiBNLTU4LjEwMTkyODI5MDYxMjEzNiAtMjYuMTg1ODMyMzkxMzEyNjQ0IEMtNTguMDA3NzU4MjQzMjkyODYgLTI2LjI0MTk0NTU1OTgxNTcyNiwgLTU3LjkxMzU4ODE5NTk3MzU4IC0yNi4yOTgwNTg3MjgzMTg4MSwgLTU3Ljc0NjkyNDQ2NTE4NTM2NiAtMjYuMzk3MzY4NzU2MDMyNDQ2IE0tNTcuNzQ2OTI0NDY1MTg1MzY2IC0yNi4zOTczNjg3NTYwMzI0NDYgQy01Ny42MjExMjgwMDk0NTA2NCAtMjYuNDU4ODY2ODk1ODg2MjQsIC01Ny40OTUzMzE1NTM3MTU5MTQgLTI2LjUyMDM2NTAzNTc0MDAzNSwgLTU3LjM3NTY2NDYyMzI2NDg0NiAtMjYuNTc4ODY2NjMzMjc1Mjg2IE0tNTcuNzQ2OTI0NDY1MTg1MzY2IC0yNi4zOTczNjg3NTYwMzI0NDYgQy01Ny42MDY4ODkwODc4NTgyOCAtMjYuNDY1ODI3ODgwNDg5MDIzLCAtNTcuNDY2ODUzNzEwNTMxMTkgLTI2LjUzNDI4NzAwNDk0NTYsIC01Ny4zNzU2NjQ2MjMyNjQ4NDYgLTI2LjU3ODg2NjYzMzI3NTI4NiBNLTU3LjM3NTY2NDYyMzI2NDg0NiAtMjYuNTc4ODY2NjMzMjc1Mjg2IEMtNTcuMjIzODIwNDkwODE4MzYgLTI2LjYzODExNjM5NzYxNDYwMywgLTU3LjA3MTk3NjM1ODM3MTg4IC0yNi42OTczNjYxNjE5NTM5MjMsIC01Ni45OTA2ODQ4NDYwMjM0MiAtMjYuNzI5MDg2MjA4NTAzMTczIE0tNTcuMzc1NjY0NjIzMjY0ODQ2IC0yNi41Nzg4NjY2MzMyNzUyODYgQy01Ny4yNDQ5MzM4NDM5MDY1OCAtMjYuNjI5ODc3OTQxNjc0NzI0LCAtNTcuMTE0MjAzMDY0NTQ4MzEgLTI2LjY4MDg4OTI1MDA3NDE2MywgLTU2Ljk5MDY4NDg0NjAyMzQyIC0yNi43MjkwODYyMDg1MDMxNzMgTS01Ni45OTA2ODQ4NDYwMjM0MiAtMjYuNzI5MDg2MjA4NTAzMTczIEMtNTYuODc5MjE4NjQ4MDM0OTMgLTI2Ljc2MjI3MTEzMzQ0Mjc4OCwgLTU2Ljc2Nzc1MjQ1MDA0NjQ0IC0yNi43OTU0NTYwNTgzODI0LCAtNTYuNTk0NjE0OTM1NzAzOTk0IC0yNi44NDcwMDEzMjk2OTY2NTMgTS01Ni45OTA2ODQ4NDYwMjM0MiAtMjYuNzI5MDg2MjA4NTAzMTczIEMtNTYuODkzMDQzMzQ2MTkwNTk1IC0yNi43NTgxNTUzNDI0Nzc1NjUsIC01Ni43OTU0MDE4NDYzNTc3NiAtMjYuNzg3MjI0NDc2NDUxOTU3LCAtNTYuNTk0NjE0OTM1NzAzOTk0IC0yNi44NDcwMDEzMjk2OTY2NTMgTS01Ni41OTQ2MTQ5MzU3MDM5OTQgLTI2Ljg0NzAwMTMyOTY5NjY1MyBDLTU2LjQ0NjYxODI5MjY5MDU3IC0yNi44NzgwMzI5NjI0NzY2NiwgLTU2LjI5ODYyMTY0OTY3NzE0NSAtMjYuOTA5MDY0NTk1MjU2NjY2LCAtNTYuMTkwMTYwNDUxNDAzNjcgLTI2LjkzMTgwNjUxNzAxMzYxMiBNLTU2LjU5NDYxNDkzNTcwMzk5NCAtMjYuODQ3MDAxMzI5Njk2NjUzIEMtNTYuNDQ2NjE3NzEzOTc0NjkgLTI2Ljg3ODAzMzA4MzgyMDYyLCAtNTYuMjk4NjIwNDkyMjQ1MzkgLTI2LjkwOTA2NDgzNzk0NDU4NCwgLTU2LjE5MDE2MDQ1MTQwMzY3IC0yNi45MzE4MDY1MTcwMTM2MTIgTS01Ni4xOTAxNjA0NTE0MDM2NyAtMjYuOTMxODA2NTE3MDEzNjEyIEMtNTYuMDgxMzQxNzUzNTI1MjIgLTI2Ljk0NTM3MDc1MzU1ODQ3NSwgLTU1Ljk3MjUyMzA1NTY0Njc4IC0yNi45NTg5MzQ5OTAxMDMzMzcsIC01NS43ODAwODQyMjczNjE2NiAtMjYuOTgyOTIyNDY1MDMzMzQ3IE0tNTYuMTkwMTYwNDUxNDAzNjcgLTI2LjkzMTgwNjUxNzAxMzYxMiBDLTU2LjA4NzE2NTYxNzkwMDI3NiAtMjYuOTQ0NjQ0ODA5NjIxMTg4LCAtNTUuOTg0MTcwNzg0Mzk2ODggLTI2Ljk1NzQ4MzEwMjIyODc2NCwgLTU1Ljc4MDA4NDIyNzM2MTY2IC0yNi45ODI5MjI0NjUwMzMzNDcgTS01NS43ODAwODQyMjczNjE2NiAtMjYuOTgyOTIyNDY1MDMzMzQ3IEMtNTUuNjYwMjczMzEzNzI0MjQgLTI2Ljk4Nzg3Nzg4MTA3NjczNCwgLTU1LjU0MDQ2MjQwMDA4NjgyNCAtMjYuOTkyODMzMjk3MTIwMTE4LCAtNTUuMzY3MTg3NSAtMjcgTS01NS43ODAwODQyMjczNjE2NiAtMjYuOTgyOTIyNDY1MDMzMzQ3IEMtNTUuNjg0ODA4ODg5NjgwMDg1IC0yNi45ODY4NjMwODIxNDg3MDUsIC01NS41ODk1MzM1NTE5OTg1MTQgLTI2Ljk5MDgwMzY5OTI2NDA2MywgLTU1LjM2NzE4NzUgLTI3IE0tNTUuMzY3MTg3NSAtMjcgQy01NS4zNjcxODc1IC0yNywgLTU1LjM2NzE4NzUgLTI3LCAtNTUuMzY3MTg3NSAtMjcgTS01NS4zNjcxODc1IC0yNyBDLTU1LjM2NzE4NzUgLTI3LCAtNTUuMzY3MTg3NSAtMjcsIC01NS4zNjcxODc1IC0yNyIgc3Ryb2tlPSIjOTM3MERCIiBzdHJva2Utd2lkdGg9IjEuMyIgZmlsbD0ibm9uZSIgc3Ryb2tlLWRhc2hhcnJheT0iMCAwIiBzdHlsZT0iIi8+PC9nPjxnIGNsYXNzPSJsYWJlbCIgc3R5bGU9IiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTQ1LjM2NzE4NzUsIC0xMikiPjxyZWN0Lz48Zm9yZWlnbk9iamVjdCB3aWR0aD0iOTAuNzM0Mzc1IiBoZWlnaHQ9IjI0Ij48ZGl2IHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hodG1sIiBzdHlsZT0iZGlzcGxheTogdGFibGUtY2VsbDsgd2hpdGUtc3BhY2U6IG5vd3JhcDsgbGluZS1oZWlnaHQ6IDEuNTsgbWF4LXdpZHRoOiAyMDBweDsgdGV4dC1hbGlnbjogY2VudGVyOyI+PHNwYW4gY2xhc3M9Im5vZGVMYWJlbCAiPjxwPkdvIHNob3BwaW5nPC9wPjwvc3Bhbj48L2Rpdj48L2ZvcmVpZ25PYmplY3Q+PC9nPjwvZz48ZyBjbGFzcz0ibm9kZSBkZWZhdWx0ICAiIGlkPSJmbG93Y2hhcnQtQy0zIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyMjEuODUxNTYyNSwgMzEwLjU3ODEyNSkiPjxwb2x5Z29uIHBvaW50cz0iNzAuNTc4MTI1LDAgMTQxLjE1NjI1LC03MC41NzgxMjUgNzAuNTc4MTI1LC0xNDEuMTU2MjUgMCwtNzAuNTc4MTI1IiBjbGFzcz0ibGFiZWwtY29udGFpbmVyIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtNzAuMDc4MTI1LCA3MC41NzgxMjUpIi8+PGcgY2xhc3M9ImxhYmVsIiBzdHlsZT0iIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtNDMuNTc4MTI1LCAtMTIpIj48cmVjdC8+PGZvcmVpZ25PYmplY3Qgd2lkdGg9Ijg3LjE1NjI1IiBoZWlnaHQ9IjI0Ij48ZGl2IHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hodG1sIiBzdHlsZT0iZGlzcGxheTogdGFibGUtY2VsbDsgd2hpdGUtc3BhY2U6IG5vd3JhcDsgbGluZS1oZWlnaHQ6IDEuNTsgbWF4LXdpZHRoOiAyMDBweDsgdGV4dC1hbGlnbjogY2VudGVyOyI+PHNwYW4gY2xhc3M9Im5vZGVMYWJlbCAiPjxwPkxldCBtZSB0aGluazwvcD48L3NwYW4+PC9kaXY+PC9mb3JlaWduT2JqZWN0PjwvZz48L2c+PGcgY2xhc3M9Im5vZGUgZGVmYXVsdCAgIiBpZD0iZmxvd2NoYXJ0LUQtNSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNjIuNDY4NzUsIDQ4Mi4xNTYyNSkiPjxyZWN0IGNsYXNzPSJiYXNpYyBsYWJlbC1jb250YWluZXIiIHN0eWxlPSIiIHg9Ii01NC40Njg3NSIgeT0iLTI3IiB3aWR0aD0iMTA4LjkzNzUiIGhlaWdodD0iNTQiLz48ZyBjbGFzcz0ibGFiZWwiIHN0eWxlPSIiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yNC40Njg3NSwgLTEyKSI+PHJlY3QvPjxmb3JlaWduT2JqZWN0IHdpZHRoPSI0OC45Mzc1IiBoZWlnaHQ9IjI0Ij48ZGl2IHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hodG1sIiBzdHlsZT0iZGlzcGxheTogdGFibGUtY2VsbDsgd2hpdGUtc3BhY2U6IG5vd3JhcDsgbGluZS1oZWlnaHQ6IDEuNTsgbWF4LXdpZHRoOiAyMDBweDsgdGV4dC1hbGlnbjogY2VudGVyOyI+PHNwYW4gY2xhc3M9Im5vZGVMYWJlbCAiPjxwPkxhcHRvcDwvcD48L3NwYW4+PC9kaXY+PC9mb3JlaWduT2JqZWN0PjwvZz48L2c+PGcgY2xhc3M9Im5vZGUgZGVmYXVsdCAgIiBpZD0iZmxvd2NoYXJ0LUUtNyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjIxLjg1MTU2MjUsIDQ4Mi4xNTYyNSkiPjxyZWN0IGNsYXNzPSJiYXNpYyBsYWJlbC1jb250YWluZXIiIHN0eWxlPSIiIHg9Ii01NC45MTQwNjI1IiB5PSItMjciIHdpZHRoPSIxMDkuODI4MTI1IiBoZWlnaHQ9IjU0Ii8+PGcgY2xhc3M9ImxhYmVsIiBzdHlsZT0iIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjQuOTE0MDYyNSwgLTEyKSI+PHJlY3QvPjxmb3JlaWduT2JqZWN0IHdpZHRoPSI0OS44MjgxMjUiIGhlaWdodD0iMjQiPjxkaXYgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGh0bWwiIHN0eWxlPSJkaXNwbGF5OiB0YWJsZS1jZWxsOyB3aGl0ZS1zcGFjZTogbm93cmFwOyBsaW5lLWhlaWdodDogMS41OyBtYXgtd2lkdGg6IDIwMHB4OyB0ZXh0LWFsaWduOiBjZW50ZXI7Ij48c3BhbiBjbGFzcz0ibm9kZUxhYmVsICI+PHA+aVBob25lPC9wPjwvc3Bhbj48L2Rpdj48L2ZvcmVpZ25PYmplY3Q+PC9nPjwvZz48ZyBjbGFzcz0ibm9kZSBkZWZhdWx0ICAiIGlkPSJmbG93Y2hhcnQtRi05IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgzNzkuODgyODEyNSwgNDgyLjE1NjI1KSI+PHJlY3QgY2xhc3M9ImJhc2ljIGxhYmVsLWNvbnRhaW5lciIgc3R5bGU9IiIgeD0iLTUzLjExNzE4NzUiIHk9Ii0yNyIgd2lkdGg9IjEwNi4yMzQzNzUiIGhlaWdodD0iNTQiLz48ZyBjbGFzcz0ibGFiZWwiIHN0eWxlPSIiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMy4xMTcxODc1LCAtMTIpIj48cmVjdC8+PGZvcmVpZ25PYmplY3Qgd2lkdGg9IjQ2LjIzNDM3NSIgaGVpZ2h0PSIyNCI+PGRpdiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94aHRtbCIgc3R5bGU9ImRpc3BsYXk6IHRhYmxlLWNlbGw7IHdoaXRlLXNwYWNlOiBub3dyYXA7IGxpbmUtaGVpZ2h0OiAxLjU7IG1heC13aWR0aDogMjAwcHg7IHRleHQtYWxpZ246IGNlbnRlcjsiPjxzcGFuIGNsYXNzPSJub2RlTGFiZWwgIj48cD48aSBjbGFzcz0iZmEgZmEtY2FyIj48L2k+IENhcjwvcD48L3NwYW4+PC9kaXY+PC9mb3JlaWduT2JqZWN0PjwvZz48L2c+PC9nPjwvZz48L2c+PC9zdmc+", view.getContent()); + assertEquals("image/svg+xml", view.getContentType()); + } + @Test public void importDiagram_WhenTheMermaidUrlIsNotDefined() throws Exception { Workspace workspace = new Workspace("Name", "Description"); diff --git a/structurizr-import/src/test/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporterTests.java b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporterTests.java index ea21bf2aa..0de289d83 100644 --- a/structurizr-import/src/test/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporterTests.java +++ b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporterTests.java @@ -2,6 +2,7 @@ import com.structurizr.Workspace; import com.structurizr.view.ImageView; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import java.io.File; @@ -52,6 +53,20 @@ public void importDiagram_AsPNG() throws Exception { assertEquals("image/png", view.getContentType()); } + @Test + @Tag("IntegrationTest") + public void importDiagram_AsInlinePNG() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getViews().getConfiguration().addProperty(PlantUMLImporter.PLANTUML_URL_PROPERTY, "https://plantuml.com/plantuml"); + workspace.getViews().getConfiguration().addProperty(PlantUMLImporter.PLANTUML_FORMAT_PROPERTY, "png"); + workspace.getViews().getConfiguration().addProperty(PlantUMLImporter.PLANTUML_INLINE_PROPERTY, "true"); + ImageView view = workspace.getViews().createImageView("key"); + + new PlantUMLImporter().importDiagram(view, new File("./src/test/resources/diagrams/plantuml/with-title.puml")); + assertEquals("data:image/png;base64,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", view.getContent()); + assertEquals("image/png", view.getContentType()); + } + @Test public void importDiagram_AsSVG() throws Exception { Workspace workspace = new Workspace("Name", "Description"); @@ -64,6 +79,20 @@ public void importDiagram_AsSVG() throws Exception { assertEquals("image/svg+xml", view.getContentType()); } + @Test + @Tag("IntegrationTest") + public void importDiagram_AsInlineSVG() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getViews().getConfiguration().addProperty(PlantUMLImporter.PLANTUML_URL_PROPERTY, "https://plantuml.com/plantuml"); + workspace.getViews().getConfiguration().addProperty(PlantUMLImporter.PLANTUML_FORMAT_PROPERTY, "svg"); + workspace.getViews().getConfiguration().addProperty(PlantUMLImporter.PLANTUML_INLINE_PROPERTY, "true"); + ImageView view = workspace.getViews().createImageView("key"); + + new PlantUMLImporter().importDiagram(view, new File("./src/test/resources/diagrams/plantuml/with-title.puml")); + assertEquals("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiBjb250ZW50U3R5bGVUeXBlPSJ0ZXh0L2NzcyIgZGF0YS1kaWFncmFtLXR5cGU9IlNFUVVFTkNFIiBoZWlnaHQ9IjE1OHB4IiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIiBzdHlsZT0id2lkdGg6MjQ2cHg7aGVpZ2h0OjE1OHB4O2JhY2tncm91bmQ6I0ZGRkZGRjsiIHZlcnNpb249IjEuMSIgdmlld0JveD0iMCAwIDI0NiAxNTgiIHdpZHRoPSIyNDZweCIgem9vbUFuZFBhbj0ibWFnbmlmeSI+PHRpdGxlPlNlcXVlbmNlIGRpYWdyYW0gZXhhbXBsZTwvdGl0bGU+PGRlZnMvPjxnPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmb250LXdlaWdodD0iYm9sZCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIyMTguNjc0OCIgeD0iMTAiIHk9IjI3Ljk5NTEiPlNlcXVlbmNlIGRpYWdyYW0gZXhhbXBsZTwvdGV4dD48Zz48dGl0bGU+Qm9iPC90aXRsZT48cmVjdCBmaWxsPSIjMDAwMDAwIiBmaWxsLW9wYWNpdHk9IjAuMDAwMDAiIGhlaWdodD0iNDkuMTMyOCIgd2lkdGg9IjgiIHg9Ijg2LjQ3NzUiIHk9IjczLjU5MzgiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjAuNTtzdHJva2UtZGFzaGFycmF5OjUsNTsiIHgxPSI4OS45NDkyIiB4Mj0iODkuOTQ5MiIgeTE9IjczLjU5MzgiIHkyPSIxMjIuNzI2NiIvPjwvZz48Zz48dGl0bGU+QWxpY2U8L3RpdGxlPjxyZWN0IGZpbGw9IiMwMDAwMDAiIGZpbGwtb3BhY2l0eT0iMC4wMDAwMCIgaGVpZ2h0PSI0OS4xMzI4IiB3aWR0aD0iOCIgeD0iMTQxLjg5MjEiIHk9IjczLjU5MzgiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjAuNTtzdHJva2UtZGFzaGFycmF5OjUsNTsiIHgxPSIxNDUuMDU4NiIgeDI9IjE0NS4wNTg2IiB5MT0iNzMuNTkzOCIgeTI9IjEyMi43MjY2Ii8+PC9nPjxnIGNsYXNzPSJwYXJ0aWNpcGFudCBwYXJ0aWNpcGFudC1oZWFkIiBkYXRhLXBhcnRpY2lwYW50PSJCb2IiPjxyZWN0IGZpbGw9IiNFMkUyRjAiIGhlaWdodD0iMzAuMjk2OSIgcng9IjIuNSIgcnk9IjIuNSIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDowLjU7IiB3aWR0aD0iNDEuMDU2NiIgeD0iNjkuOTQ5MiIgeT0iNDIuMjk2OSIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjI3LjA1NjYiIHg9Ijc2Ljk0OTIiIHk9IjYyLjI5MiI+Qm9iPC90ZXh0PjwvZz48ZyBjbGFzcz0icGFydGljaXBhbnQgcGFydGljaXBhbnQtdGFpbCIgZGF0YS1wYXJ0aWNpcGFudD0iQm9iIj48cmVjdCBmaWxsPSIjRTJFMkYwIiBoZWlnaHQ9IjMwLjI5NjkiIHJ4PSIyLjUiIHJ5PSIyLjUiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MC41OyIgd2lkdGg9IjQxLjA1NjYiIHg9IjY5Ljk0OTIiIHk9IjEyMS43MjY2Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTQiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iMjcuMDU2NiIgeD0iNzYuOTQ5MiIgeT0iMTQxLjcyMTciPkJvYjwvdGV4dD48L2c+PGcgY2xhc3M9InBhcnRpY2lwYW50IHBhcnRpY2lwYW50LWhlYWQiIGRhdGEtcGFydGljaXBhbnQ9IkFsaWNlIj48cmVjdCBmaWxsPSIjRTJFMkYwIiBoZWlnaHQ9IjMwLjI5NjkiIHJ4PSIyLjUiIHJ5PSIyLjUiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MC41OyIgd2lkdGg9IjQ3LjY2NyIgeD0iMTIyLjA1ODYiIHk9IjQyLjI5NjkiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIzMy42NjciIHg9IjEyOS4wNTg2IiB5PSI2Mi4yOTIiPkFsaWNlPC90ZXh0PjwvZz48ZyBjbGFzcz0icGFydGljaXBhbnQgcGFydGljaXBhbnQtdGFpbCIgZGF0YS1wYXJ0aWNpcGFudD0iQWxpY2UiPjxyZWN0IGZpbGw9IiNFMkUyRjAiIGhlaWdodD0iMzAuMjk2OSIgcng9IjIuNSIgcnk9IjIuNSIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDowLjU7IiB3aWR0aD0iNDcuNjY3IiB4PSIxMjIuMDU4NiIgeT0iMTIxLjcyNjYiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIzMy42NjciIHg9IjEyOS4wNTg2IiB5PSIxNDEuNzIxNyI+QWxpY2U8L3RleHQ+PC9nPjxnIGNsYXNzPSJtZXNzYWdlIiBkYXRhLXBhcnRpY2lwYW50LTE9IkJvYiIgZGF0YS1wYXJ0aWNpcGFudC0yPSJBbGljZSI+PHBvbHlnb24gZmlsbD0iIzE4MTgxOCIgcG9pbnRzPSIxMzMuODkyMSwxMDAuNzI2NiwxNDMuODkyMSwxMDQuNzI2NiwxMzMuODkyMSwxMDguNzI2NiwxMzcuODkyMSwxMDQuNzI2NiIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MTsiIHgxPSI5MC40Nzc1IiB4Mj0iMTM5Ljg5MjEiIHkxPSIxMDQuNzI2NiIgeTI9IjEwNC43MjY2Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTMiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iMzEuNDE0NiIgeD0iOTcuNDc3NSIgeT0iOTkuNjYwNiI+aGVsbG88L3RleHQ+PC9nPjwhLS1TUkM9W0F5YWlvS2JMMjR1akI0dERJcXZMSUNiQ0oyekFwNUw4aEtaQ0JTWDl2TkJBSnJCR2pMRG1wQ2E0SWJlZlBBSmN2RUczMDAwMF0tLT48L2c+PC9zdmc+", view.getContent()); + assertEquals("image/svg+xml", view.getContentType()); + } + @Test public void importDiagram_WhenThePlantUMLURLIsNotSpecified() throws Exception { Workspace workspace = new Workspace("Name", "Description"); From 56b72cd1ae64610a7b8ce8dc6b830ff770da6a05 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 4 Sep 2025 12:44:55 +0100 Subject: [PATCH 663/717] Gradle test target now works offline by excluding tests tagged "IntegrationTest". --- build.gradle | 11 ++++++++++- .../api/WorkspaceApiClientIntegrationTests.java | 5 +++++ .../com/structurizr/view/ThemeUtilsTests.java | 3 +++ .../test/java/com/structurizr/dsl/DslTests.java | 5 +++++ .../export/dot/DOTDiagramExporterTests.java | 2 ++ .../export/ilograph/IlographExporterTests.java | 2 ++ .../mermaid/MermaidDiagramExporterTests.java | 2 ++ .../plantuml/C4PlantUMLDiagramExporterTests.java | 3 +++ .../StructurizrPlantUMLDiagramExporterTests.java | 2 ++ structurizr-import/build.gradle | 15 +-------------- 10 files changed, 35 insertions(+), 15 deletions(-) diff --git a/build.gradle b/build.gradle index 876b332b5..86760cb7a 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,16 @@ subprojects { proj -> } test { - useJUnitPlatform() + useJUnitPlatform { + excludeTags "IntegrationTest" + } + } + + tasks.register("integrationTest", Test) { + useJUnitPlatform { + includeTags "IntegrationTest" + } + mustRunAfter check } compileJava.options.encoding = 'UTF-8' diff --git a/structurizr-client/src/integrationTest/java/com/structurizr/api/WorkspaceApiClientIntegrationTests.java b/structurizr-client/src/integrationTest/java/com/structurizr/api/WorkspaceApiClientIntegrationTests.java index e54981f04..3b4628c6c 100644 --- a/structurizr-client/src/integrationTest/java/com/structurizr/api/WorkspaceApiClientIntegrationTests.java +++ b/structurizr-client/src/integrationTest/java/com/structurizr/api/WorkspaceApiClientIntegrationTests.java @@ -10,6 +10,7 @@ import com.structurizr.view.SystemContextView; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import java.io.File; @@ -51,6 +52,7 @@ private File getArchivedWorkspace() { } @Test + @Tag("IntegrationTest") void putAndGetWorkspace_WithoutEncryption() throws Exception { Workspace workspace = new Workspace("Structurizr client library tests - without encryption", "A test workspace for the Structurizr client library"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", "Description"); @@ -77,6 +79,7 @@ void putAndGetWorkspace_WithoutEncryption() throws Exception { } @Test + @Tag("IntegrationTest") void putAndGetWorkspace_WithEncryption() throws Exception { client.setEncryptionStrategy(new AesEncryptionStrategy("password")); Workspace workspace = new Workspace("Structurizr client library tests - with encryption", "A test workspace for the Structurizr client library"); @@ -104,6 +107,7 @@ void putAndGetWorkspace_WithEncryption() throws Exception { } @Test + @Tag("IntegrationTest") void lockWorkspace() throws Exception { client.unlockWorkspace(20081); assertTrue(client.lockWorkspace(20081)); @@ -111,6 +115,7 @@ void lockWorkspace() throws Exception { @Test + @Tag("IntegrationTest") void unlockWorkspace() throws Exception { client.lockWorkspace(20081); assertTrue(client.unlockWorkspace(20081)); diff --git a/structurizr-client/src/test/java/com/structurizr/view/ThemeUtilsTests.java b/structurizr-client/src/test/java/com/structurizr/view/ThemeUtilsTests.java index 5f4882881..3ebc16983 100644 --- a/structurizr-client/src/test/java/com/structurizr/view/ThemeUtilsTests.java +++ b/structurizr-client/src/test/java/com/structurizr/view/ThemeUtilsTests.java @@ -4,6 +4,7 @@ import com.structurizr.model.Relationship; import com.structurizr.model.SoftwareSystem; import com.structurizr.model.Tags; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import java.util.ArrayList; @@ -23,6 +24,7 @@ void loadThemes_DoesNothingWhenNoThemesAreDefined() throws Exception { } @Test + @Tag("IntegrationTest") void loadThemes_LoadsThemesWhenThemesAreDefined() throws Exception { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name"); @@ -137,6 +139,7 @@ void findRelationshipStyle_WithThemes() { } @Test + @Tag("IntegrationTest") void loadThemes_ReplacesRelativeIconReferences() throws Exception { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name"); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index b0c1b64f8..57ed5efbd 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -5,6 +5,7 @@ import com.structurizr.model.*; import com.structurizr.util.StringUtils; import com.structurizr.view.*; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -396,6 +397,7 @@ void test_includeLocalDirectory_WhenThereAreHiddenFiles() throws Exception { } @Test + @Tag("IntegrationTest") void test_includeUrl() throws Exception { StructurizrDslParser parser = new StructurizrDslParser(); parser.parse(new File("src/test/resources/dsl/include-url.dsl")); @@ -434,6 +436,7 @@ void test_includeLocalFile_ThrowsAnException_WhenRunningInRestrictedMode() { } @ParameterizedTest + @Tag("IntegrationTest") @ValueSource(strings = { "src/test/resources/dsl/extend/extend-workspace-from-json-file.dsl", "src/test/resources/dsl/extend/extend-workspace-from-json-url.dsl" }) void test_extendWorkspaceFromJson(String dslFile) throws Exception { StructurizrDslParser parser = new StructurizrDslParser(); @@ -472,6 +475,7 @@ void test_extendWorkspaceFromJsonFile_WhenRunningInRestrictedMode() throws Excep } @ParameterizedTest + @Tag("IntegrationTest") @ValueSource(strings = { "src/test/resources/dsl/extend/extend-workspace-from-dsl-file.dsl", "src/test/resources/dsl/extend/extend-workspace-from-dsl-url.dsl" }) void test_extendWorkspaceFromDsl(String dslFile) throws Exception { StructurizrDslParser parser = new StructurizrDslParser(); @@ -1091,6 +1095,7 @@ void test_imageViews_ViaFiles() throws Exception { } @Test + @Tag("IntegrationTest") void test_imageViews_ViaUrls() throws Exception { StructurizrDslParser parser = new StructurizrDslParser(); parser.parse(new File("src/test/resources/dsl/image-views/workspace-via-url.dsl")); diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/DOTDiagramExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/dot/DOTDiagramExporterTests.java index 39f175254..b19a96f36 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/dot/DOTDiagramExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/dot/DOTDiagramExporterTests.java @@ -6,6 +6,7 @@ import com.structurizr.model.*; import com.structurizr.util.WorkspaceUtils; import com.structurizr.view.*; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import java.io.File; @@ -53,6 +54,7 @@ public void test_BigBankPlcExample() throws Exception { } @Test + @Tag("IntegrationTest") public void test_AmazonWebServicesExample() throws Exception { Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/structurizr-54915-workspace.json")); ThemeUtils.loadThemes(workspace); diff --git a/structurizr-export/src/test/java/com/structurizr/export/ilograph/IlographExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/ilograph/IlographExporterTests.java index a42da19bb..4761312d2 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/ilograph/IlographExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/ilograph/IlographExporterTests.java @@ -7,6 +7,7 @@ import com.structurizr.model.Model; import com.structurizr.util.WorkspaceUtils; import com.structurizr.view.ThemeUtils; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import java.io.File; @@ -26,6 +27,7 @@ public void test_BigBankPlcExample() throws Exception { } @Test + @Tag("IntegrationTest") void test_AmazonWebServicesExample() throws Exception { Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/structurizr-54915-workspace.json")); workspace.getViews().getConfiguration().getStyles().addElementStyle("Amazon Web Services - Route 53").addProperty(IlographExporter.ILOGRAPH_ICON, "AWS/Networking/Route-53.svg"); diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/MermaidDiagramExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/mermaid/MermaidDiagramExporterTests.java index b5b1a8774..ed49a4431 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/MermaidDiagramExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/MermaidDiagramExporterTests.java @@ -6,6 +6,7 @@ import com.structurizr.model.*; import com.structurizr.util.WorkspaceUtils; import com.structurizr.view.*; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import java.io.File; @@ -60,6 +61,7 @@ public void test_BigBankPlcExample() throws Exception { } @Test + @Tag("IntegrationTest") public void test_AmazonWebServicesExample() throws Exception { Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/structurizr-54915-workspace.json")); ThemeUtils.loadThemes(workspace); diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java index 49fae0fe2..b544dbdcf 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java @@ -6,6 +6,7 @@ import com.structurizr.model.*; import com.structurizr.util.WorkspaceUtils; import com.structurizr.view.*; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import java.io.File; @@ -61,6 +62,7 @@ public void test_BigBankPlcExample() throws Exception { } @Test + @Tag("IntegrationTest") public void test_AmazonWebServicesExampleWithoutTags() throws Exception { Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/structurizr-54915-workspace.json")); ThemeUtils.loadThemes(workspace); @@ -77,6 +79,7 @@ public void test_AmazonWebServicesExampleWithoutTags() throws Exception { } @Test + @Tag("IntegrationTest") public void test_AmazonWebServicesExampleWithTags() throws Exception { Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/structurizr-54915-workspace.json")); ThemeUtils.loadThemes(workspace); diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java index 19869f588..136e48cb9 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java @@ -6,6 +6,7 @@ import com.structurizr.model.*; import com.structurizr.util.WorkspaceUtils; import com.structurizr.view.*; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import java.io.File; @@ -71,6 +72,7 @@ public void test_BigBankPlcExample() throws Exception { } @Test + @Tag("IntegrationTest") public void test_AmazonWebServicesExample() throws Exception { Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/structurizr-54915-workspace.json")); ThemeUtils.loadThemes(workspace); diff --git a/structurizr-import/build.gradle b/structurizr-import/build.gradle index d1f719fe1..7b5c3b32f 100644 --- a/structurizr-import/build.gradle +++ b/structurizr-import/build.gradle @@ -4,17 +4,4 @@ dependencies { } -description = 'Utilities to import diagrams and documentation into a Structurizr workspace' - -test { - useJUnitPlatform { - excludeTags "IntegrationTest" - } -} - -tasks.register("integrationTest", Test) { - useJUnitPlatform { - includeTags "IntegrationTest" - } - mustRunAfter check -} \ No newline at end of file +description = 'Utilities to import diagrams and documentation into a Structurizr workspace' \ No newline at end of file From c9d20b3605caf54613bf94b9cc35dac442bfc6ba Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 4 Sep 2025 12:47:26 +0100 Subject: [PATCH 664/717] . --- structurizr-dsl/src/test/resources/dsl/test.dsl | 1 + 1 file changed, 1 insertion(+) diff --git a/structurizr-dsl/src/test/resources/dsl/test.dsl b/structurizr-dsl/src/test/resources/dsl/test.dsl index d2de4ed88..cf0ef4407 100644 --- a/structurizr-dsl/src/test/resources/dsl/test.dsl +++ b/structurizr-dsl/src/test/resources/dsl/test.dsl @@ -355,6 +355,7 @@ workspace "Name" "Description" { colour #777777 dashed true routing curved + jump true fontSize 24 width 400 position 50 From dbe5d11c553ee7464d08dc0459ac7e329432cac1 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 4 Sep 2025 14:14:30 +0100 Subject: [PATCH 665/717] structurizr-dsl: PlantUML, Mermaid, and Kroki image views can now be defined by an inline source block. --- changelog.md | 1 + .../java/com/structurizr/dsl/DslLine.java | 5 + .../dsl/ImageViewContentParser.java | 115 +++++++++++------- .../structurizr/dsl/StructurizrDslParser.java | 27 +++- .../main/java/com/structurizr/dsl/Tokens.java | 2 +- .../java/com/structurizr/dsl/DslTests.java | 24 ++++ .../dsl/ImageViewContentParserTests.java | 19 ++- .../src/test/resources/dsl/image-view.dsl | 4 +- .../dsl/image-views/workspace-via-source.dsl | 34 ++++++ 9 files changed, 178 insertions(+), 53 deletions(-) create mode 100644 structurizr-dsl/src/test/resources/dsl/image-views/workspace-via-source.dsl diff --git a/changelog.md b/changelog.md index 43d0c76ea..59077a0d7 100644 --- a/changelog.md +++ b/changelog.md @@ -13,6 +13,7 @@ - structurizr-dsl: Fixes https://github.com/structurizr/java/issues/435 (Relationship archetype not applied to implicit-source relationships). - structurizr-dsl: Adds support for removing relationships between software system instance/container instances, with a view to redefining them via infrastructure nodes. - structurizr-dsl: Adds support for a `jump` property on relationship styles. +- structurizr-dsl: PlantUML, Mermaid, and Kroki image views can now be defined by an inline source block. - structurizr-import: Adds support for `plantuml.inline`, `mermaid.inline`, and `kroki.inline` properties to inline the resulting PNG/SVG file into the workspace. - structurizr-inspection: Adds a way to disable inspections via a workspace property named `structurizr.inspection` (`false` to disable). - structurizr-inspection: Default inspector adds a summary of error/warning/info/ignore counts as workspace properties. diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DslLine.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DslLine.java index 6c8c99944..ca2ad4ec0 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/DslLine.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DslLine.java @@ -21,4 +21,9 @@ int getLineNumber() { return lineNumber; } + @Override + public String toString() { + return source; + } + } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewContentParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewContentParser.java index 2866f9b16..e73a235ed 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewContentParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewContentParser.java @@ -15,9 +15,9 @@ final class ImageViewContentParser extends AbstractParser { - private static final String PLANTUML_GRAMMAR = "plantuml "; - private static final String MERMAID_GRAMMAR = "mermaid "; - private static final String KROKI_GRAMMAR = "kroki "; + private static final String PLANTUML_GRAMMAR = "plantuml "; + private static final String MERMAID_GRAMMAR = "mermaid "; + private static final String KROKI_GRAMMAR = "kroki "; private static final String IMAGE_GRAMMAR = "image "; private static final int PLANTUML_SOURCE_INDEX = 1; @@ -33,7 +33,7 @@ final class ImageViewContentParser extends AbstractParser { } void parsePlantUML(ImageViewDslContext context, File dslFile, Tokens tokens) { - // plantuml + // plantuml if (tokens.hasMoreThan(PLANTUML_SOURCE_INDEX)) { throw new RuntimeException("Too many tokens, expected: " + PLANTUML_GRAMMAR); @@ -45,22 +45,31 @@ void parsePlantUML(ImageViewDslContext context, File dslFile, Tokens tokens) { String source = tokens.get(PLANTUML_SOURCE_INDEX); try { - View viewWithKey = context.getWorkspace().getViews().getViewWithKey(source); - if (viewWithKey instanceof ModelView) { - StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(); - String plantuml = exporter.export((ModelView)viewWithKey).getDefinition(); - new PlantUMLImporter().importDiagram(context.getView(), plantuml); + if (source.contains("\n")) { + // inline source + new PlantUMLImporter().importDiagram(context.getView(), source); } else { - if (Url.isUrl(source)) { - RemoteContent content = readFromUrl(source); - new PlantUMLImporter().importDiagram(context.getView(), content.getContent()); - context.getView().setTitle(source.substring(source.lastIndexOf("/") + 1)); + View viewWithKey = context.getWorkspace().getViews().getViewWithKey(source); + if (viewWithKey instanceof ModelView) { + StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(); + String plantuml = exporter.export((ModelView) viewWithKey).getDefinition(); + new PlantUMLImporter().importDiagram(context.getView(), plantuml); } else { - if (!restricted) { - File file = new File(dslFile.getParentFile(), source); - new PlantUMLImporter().importDiagram(context.getView(), file); + if (Url.isUrl(source)) { + RemoteContent content = readFromUrl(source); + new PlantUMLImporter().importDiagram(context.getView(), content.getContent()); + context.getView().setTitle(source.substring(source.lastIndexOf("/") + 1)); } else { - throw new RuntimeException("PlantUML source must be specified as a URL when running in restricted mode"); + if (!restricted) { + File file = new File(dslFile.getParentFile(), source); + if (file.exists()) { + new PlantUMLImporter().importDiagram(context.getView(), file); + } else { + throw new RuntimeException("The file at " + file.getAbsolutePath() + " does not exist"); + } + } else { + throw new RuntimeException("PlantUML source must be specified as a URL when running in restricted mode"); + } } } } @@ -74,7 +83,7 @@ void parsePlantUML(ImageViewDslContext context, File dslFile, Tokens tokens) { } void parseMermaid(ImageViewDslContext context, File dslFile, Tokens tokens) { - // mermaid + // mermaid if (tokens.hasMoreThan(MERMAID_SOURCE_INDEX)) { throw new RuntimeException("Too many tokens, expected: " + MERMAID_GRAMMAR); @@ -86,22 +95,31 @@ void parseMermaid(ImageViewDslContext context, File dslFile, Tokens tokens) { String source = tokens.get(MERMAID_SOURCE_INDEX); try { - View viewWithKey = context.getWorkspace().getViews().getViewWithKey(source); - if (viewWithKey instanceof ModelView) { - MermaidDiagramExporter exporter = new MermaidDiagramExporter(); - String mermaid = exporter.export((ModelView)viewWithKey).getDefinition(); - new MermaidImporter().importDiagram(context.getView(), mermaid); + if (source.contains("\n")) { + // inline source + new MermaidImporter().importDiagram(context.getView(), source); } else { - if (Url.isUrl(source)) { - RemoteContent content = readFromUrl(source); - new MermaidImporter().importDiagram(context.getView(), content.getContent()); - context.getView().setTitle(source.substring(source.lastIndexOf("/") + 1)); + View viewWithKey = context.getWorkspace().getViews().getViewWithKey(source); + if (viewWithKey instanceof ModelView) { + MermaidDiagramExporter exporter = new MermaidDiagramExporter(); + String mermaid = exporter.export((ModelView) viewWithKey).getDefinition(); + new MermaidImporter().importDiagram(context.getView(), mermaid); } else { - if (!restricted) { - File file = new File(dslFile.getParentFile(), source); - new MermaidImporter().importDiagram(context.getView(), file); + if (Url.isUrl(source)) { + RemoteContent content = readFromUrl(source); + new MermaidImporter().importDiagram(context.getView(), content.getContent()); + context.getView().setTitle(source.substring(source.lastIndexOf("/") + 1)); } else { - throw new RuntimeException("Mermaid source must be specified as a URL when running in restricted mode"); + if (!restricted) { + File file = new File(dslFile.getParentFile(), source); + if (file.exists()) { + new MermaidImporter().importDiagram(context.getView(), file); + } else { + throw new RuntimeException("The file at " + file.getAbsolutePath() + " does not exist"); + } + } else { + throw new RuntimeException("Mermaid source must be specified as a URL when running in restricted mode"); + } } } } @@ -115,7 +133,7 @@ void parseMermaid(ImageViewDslContext context, File dslFile, Tokens tokens) { } void parseKroki(ImageViewDslContext context, File dslFile, Tokens tokens) { - // kroki + // kroki if (tokens.hasMoreThan(KROKI_SOURCE_INDEX)) { throw new RuntimeException("Too many tokens, expected: " + KROKI_GRAMMAR); @@ -128,16 +146,25 @@ void parseKroki(ImageViewDslContext context, File dslFile, Tokens tokens) { String source = tokens.get(KROKI_SOURCE_INDEX); try { - if (Url.isUrl(source)) { - RemoteContent content = readFromUrl(source); - new KrokiImporter().importDiagram(context.getView(), format, content.getContent()); - context.getView().setTitle(source.substring(source.lastIndexOf("/")+1)); + if (source.contains("\n")) { + // inline source + new KrokiImporter().importDiagram(context.getView(), format, source); } else { - if (!restricted) { - File file = new File(dslFile.getParentFile(), source); - new KrokiImporter().importDiagram(context.getView(), format, file); + if (Url.isUrl(source)) { + RemoteContent content = readFromUrl(source); + new KrokiImporter().importDiagram(context.getView(), format, content.getContent()); + context.getView().setTitle(source.substring(source.lastIndexOf("/") + 1)); } else { - throw new RuntimeException("Kroki source must be specified as a URL when running in restricted mode"); + if (!restricted) { + File file = new File(dslFile.getParentFile(), source); + if (file.exists()) { + new KrokiImporter().importDiagram(context.getView(), format, file); + } else { + throw new RuntimeException("The file at " + file.getAbsolutePath() + " does not exist"); + } + } else { + throw new RuntimeException("Kroki source must be specified as a URL when running in restricted mode"); + } } } } catch (Exception e) { @@ -168,8 +195,12 @@ void parseImage(ImageViewDslContext context, File dslFile, Tokens tokens) { } else { if (!restricted) { File file = new File(dslFile.getParentFile(), source); - context.getView().setContent(ImageUtils.getImageAsDataUri(file)); - context.getView().setTitle(file.getName()); + if (file.exists()) { + context.getView().setContent(ImageUtils.getImageAsDataUri(file)); + context.getView().setTitle(file.getName()); + } else { + throw new RuntimeException("The file at " + file.getAbsolutePath() + " does not exist"); + } } else { throw new RuntimeException("Images must be specified as a URL when running in restricted mode"); } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index 58fcc3e08..d7d33ef80 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -36,6 +36,7 @@ public final class StructurizrDslParser extends StructurizrDslTokens { private static final String TEXT_BLOCK_MARKER = "\"\"\""; private static final Pattern STRING_SUBSTITUTION_PATTERN = Pattern.compile("(\\$\\{[a-zA-Z0-9-_.]+?})"); + private static final String STRING_SUBSTITUTION_TEMPLATE = "${%s}"; private static final String STRUCTURIZR_DSL_IDENTIFIER_PROPERTY_NAME = "structurizr.dsl.identifier"; @@ -1289,7 +1290,7 @@ private List preProcessLines(List lines) { for (String line : lines) { if (textBlock) { if (line.endsWith(TEXT_BLOCK_MARKER)) { - buf.append("\""); + buf.append(TEXT_BLOCK_MARKER); textBlock = false; textBlockLeadingSpace = -1; lineComplete = true; @@ -1304,14 +1305,18 @@ private List preProcessLines(List lines) { } } } - buf.append(line, textBlockLeadingSpace, line.length()); - buf.append("\n"); + if (StringUtils.isNullOrEmpty(line)) { + buf.append("\n"); + } else { + buf.append(line, textBlockLeadingSpace, line.length()); + buf.append("\n"); + } } } else if (!COMMENT_PATTERN.matcher(line).matches() && line.endsWith(MULTI_LINE_SEPARATOR)) { buf.append(line, 0, line.length() - 1); lineComplete = false; } else if (!COMMENT_PATTERN.matcher(line).matches() && line.endsWith(TEXT_BLOCK_MARKER)) { - buf.append(line, 0, line.length() - 2); + buf.append(line, 0, line.length()); lineComplete = false; textBlock = true; } else { @@ -1324,7 +1329,19 @@ private List preProcessLines(List lines) { } if (lineComplete) { - dslLines.add(new DslLine(buf.toString(), lineNumber)); + // replace the text block with a constant (that will become substituted later) + // (this makes it possible for text blocks to include double-quote characters) + String s = buf.toString(); + if (s.endsWith(TEXT_BLOCK_MARKER)) { + String[] parts = s.split(TEXT_BLOCK_MARKER); + String constantName = UUID.randomUUID().toString(); + String constantValue = parts[1].substring(0, parts[1].length() - 1); // remove final line break + addConstant(constantName, constantValue); + dslLines.add(new DslLine(parts[0] + "\"" + String.format(STRING_SUBSTITUTION_TEMPLATE, constantName) + "\"", lineNumber)); + } else { + dslLines.add(new DslLine(buf.toString(), lineNumber)); + } + buf = new StringBuilder(); } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/Tokens.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/Tokens.java index ba337b3bf..55fe452ba 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/Tokens.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/Tokens.java @@ -11,7 +11,7 @@ final class Tokens { } String get(int index) { - return tokens.get(index).trim().replaceAll("\\\\\"", "\"").trim().replaceAll("\\\\n", "\n"); + return tokens.get(index).replaceAll("\\\\\"", "\"").replaceAll("\\\\n", "\n"); } void remove(int index) { diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index 57ed5efbd..30a884caf 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -1129,6 +1129,30 @@ void test_imageViews_ViaUrls() throws Exception { assertEquals("image/svg+xml", svgView.getContentType()); } + @Test + void test_imageViews_ViaSource() throws Exception { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File("src/test/resources/dsl/image-views/workspace-via-source.dsl")); + + Workspace workspace = parser.getWorkspace(); + assertEquals(3, workspace.getViews().getImageViews().size()); + + ImageView plantumlView = (ImageView)workspace.getViews().getViewWithKey("plantuml"); + assertNull(plantumlView.getTitle()); + assertEquals("http://localhost:7777/svg/SoWkIImgAStDuNBAJrBGjLDmpCbCJbMmKiX8pSd9vt98pKi1IW80", plantumlView.getContent()); + assertEquals("image/svg+xml", plantumlView.getContentType()); + + ImageView mermaidView = (ImageView)workspace.getViews().getViewWithKey("mermaid"); + assertNull(mermaidView.getTitle()); + assertEquals("http://localhost:8888/svg/Zmxvd2NoYXJ0IFRECiAgICBTdGFydCAtLT4gU3RvcA==", mermaidView.getContent()); + assertEquals("image/svg+xml", mermaidView.getContentType()); + + ImageView krokiView = (ImageView)workspace.getViews().getViewWithKey("kroki"); + assertNull(krokiView.getTitle()); + assertEquals("http://localhost:9999/graphviz/png/eNpLyUwvSizIUHBXqPZIzcnJ17ULzy_KSanlAgB1EAjQ", krokiView.getContent()); + assertEquals("image/png", krokiView.getContentType()); + } + @Test void test_EmptyDeploymentEnvironment() throws Exception { StructurizrDslParser parser = new StructurizrDslParser(); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewContentParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewContentParserTests.java index ca65bcadc..52e59ccb3 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewContentParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewContentParserTests.java @@ -28,7 +28,7 @@ void test_parsePlantUML_ThrowsAnException_WithTooFewTokens() { parser.parsePlantUML(context, null, tokens("plantuml")); fail(); } catch (Exception e) { - assertEquals("Expected: plantuml ", e.getMessage()); + assertEquals("Expected: plantuml ", e.getMessage()); } } @@ -64,10 +64,10 @@ void test_parseMermaid_ThrowsAnException_WithTooFewTokens() { ImageViewDslContext context = new ImageViewDslContext(imageView); context.setWorkspace(workspace); parser = new ImageViewContentParser(true); - parser.parseMermaid(context, null, tokens("plantuml")); + parser.parseMermaid(context, null, tokens("mermaid")); fail(); } catch (Exception e) { - assertEquals("Expected: mermaid ", e.getMessage()); + assertEquals("Expected: mermaid ", e.getMessage()); } } @@ -97,6 +97,19 @@ void test_parseMermaid_WithViewKey() { assertEquals("https://mermaid.ink/svg/pako:eJxlkMtuwjAQRX9lNAhlE9SwqupCpLLuLt0RFiYeJxZ-RLYppYh_bxJHVR93NrM4c3U0N8DGCUKGred9B2-72gJoZU9VvGoCQZKfdQSptGYLOaW2IxPOx3QiFB8WA_saq2uIZOCVWxEa3lONhxEd4FQ2kz_L8hC9O9HvboD10LYR6j1dbjPpbFxdSLVdZHB0WmTly-ZhAMp_VFCfxOCxWD6D4b5VdhVdz6DoP7JyXzkZL9wTJNVD6vjjuZ4NxZRvwyc-Tt447TxbFFOSL1mBOaAhb7gSyG4YOzLjV-f_4f3-BQMfekI=", imageView.getContent()); } + @Test + void test_parseKroki_ThrowsAnException_WithTooFewTokens() { + try { + ImageViewDslContext context = new ImageViewDslContext(imageView); + context.setWorkspace(workspace); + parser = new ImageViewContentParser(true); + parser.parseKroki(context, null, tokens("kroki")); + fail(); + } catch (Exception e) { + assertEquals("Expected: kroki ", e.getMessage()); + } + } + @Test void test_parseKroki_ThrowsAnException_WhenUsingAFileNameInRestrictedMode() { try { diff --git a/structurizr-dsl/src/test/resources/dsl/image-view.dsl b/structurizr-dsl/src/test/resources/dsl/image-view.dsl index 27633c053..c443c4b61 100644 --- a/structurizr-dsl/src/test/resources/dsl/image-view.dsl +++ b/structurizr-dsl/src/test/resources/dsl/image-view.dsl @@ -2,8 +2,8 @@ workspace { views { image * "Image" { - image image.png - } + image image.png } + } } \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/image-views/workspace-via-source.dsl b/structurizr-dsl/src/test/resources/dsl/image-views/workspace-via-source.dsl new file mode 100644 index 000000000..c342857ce --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/image-views/workspace-via-source.dsl @@ -0,0 +1,34 @@ +workspace { + + views { + properties { + "plantuml.url" "http://localhost:7777" + "mermaid.url" "http://localhost:8888" + "mermaid.compress" "false" + "kroki.url" "http://localhost:9999" + } + + image * "plantuml" { + plantuml """ + @startuml + Bob -> Alice : hello + @enduml + """ + } + + image * "mermaid" { + mermaid """ + flowchart TD + Start --> Stop + """ + } + + image * "kroki" { + kroki graphviz """ + digraph G {Hello->World} + + """ + } + } + +} \ No newline at end of file From 990b1c2f9e519a1e94e7d114b74a767919d0c459 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 5 Sep 2025 13:41:15 +0100 Subject: [PATCH 666/717] structurizr-dsl: Constants and variables are inherited when extending a DSL workspace. --- changelog.md | 1 + .../com/structurizr/dsl/StructurizrDslParser.java | 3 ++- .../src/test/java/com/structurizr/dsl/DslTests.java | 12 ++++++++++++ ...-and-variables-from-workspace-extension-child.dsl | 7 +++++++ ...and-variables-from-workspace-extension-parent.dsl | 6 ++++++ 5 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 structurizr-dsl/src/test/resources/dsl/constants-and-variables-from-workspace-extension-child.dsl create mode 100644 structurizr-dsl/src/test/resources/dsl/constants-and-variables-from-workspace-extension-parent.dsl diff --git a/changelog.md b/changelog.md index 59077a0d7..b2c46c6fd 100644 --- a/changelog.md +++ b/changelog.md @@ -14,6 +14,7 @@ - structurizr-dsl: Adds support for removing relationships between software system instance/container instances, with a view to redefining them via infrastructure nodes. - structurizr-dsl: Adds support for a `jump` property on relationship styles. - structurizr-dsl: PlantUML, Mermaid, and Kroki image views can now be defined by an inline source block. +- structurizr-dsl: Constants and variables are inherited when extending a DSL workspace. - structurizr-import: Adds support for `plantuml.inline`, `mermaid.inline`, and `kroki.inline` properties to inline the resulting PNG/SVG file into the workspace. - structurizr-inspection: Adds a way to disable inspections via a workspace property named `structurizr.inspection` (`false` to disable). - structurizr-inspection: Default inspector adds a summary of error/warning/info/ignore counts as workspace properties. diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index d7d33ef80..4cc143efe 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -45,7 +45,7 @@ public final class StructurizrDslParser extends StructurizrDslTokens { private final Stack contextStack; private final Set parsedTokens = new HashSet<>(); private final IdentifiersRegister identifiersRegister; - private final Map constantsAndVariables; + private Map constantsAndVariables; private final Features features = new Features(); private Map> archetypes = Map.of( @@ -78,6 +78,7 @@ public StructurizrDslParser() { void configureFrom(StructurizrDslParser parser) { setIdentifierScope(parser.getIdentifierScope()); archetypes = parser.archetypes; + constantsAndVariables = parser.constantsAndVariables; } /** diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index 30a884caf..d90e5a46d 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -1269,6 +1269,18 @@ void test_Constant() { } } + @Test + void test_ConstantsAndVariablesFromWorkspaceExtension() throws Exception { + File dslFile = new File("src/test/resources/dsl/constants-and-variables-from-workspace-extension-child.dsl"); + + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(dslFile); + + SoftwareSystem softwareSystem = parser.getWorkspace().getModel().getSoftwareSystemWithName("Name"); + assertNotNull(softwareSystem); + assertEquals("Description", softwareSystem.getDescription()); + } + @Test void test_UnbalancedCurlyBraces() { try { diff --git a/structurizr-dsl/src/test/resources/dsl/constants-and-variables-from-workspace-extension-child.dsl b/structurizr-dsl/src/test/resources/dsl/constants-and-variables-from-workspace-extension-child.dsl new file mode 100644 index 000000000..aa10fdab4 --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/constants-and-variables-from-workspace-extension-child.dsl @@ -0,0 +1,7 @@ +workspace extends constants-and-variables-from-workspace-extension-parent.dsl { + + model { + softwareSystem "${NAME}" "${DESCRIPTION}" + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/constants-and-variables-from-workspace-extension-parent.dsl b/structurizr-dsl/src/test/resources/dsl/constants-and-variables-from-workspace-extension-parent.dsl new file mode 100644 index 000000000..442ce70ea --- /dev/null +++ b/structurizr-dsl/src/test/resources/dsl/constants-and-variables-from-workspace-extension-parent.dsl @@ -0,0 +1,6 @@ +workspace { + + !const "NAME" "Name" + !var "DESCRIPTION" "Description" + +} \ No newline at end of file From d40de39c805437b9c890989e5bb7b9c4b370d236 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 11 Sep 2025 08:57:22 +0100 Subject: [PATCH 667/717] . --- structurizr-dsl/src/test/resources/dsl/include/model.dsl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/structurizr-dsl/src/test/resources/dsl/include/model.dsl b/structurizr-dsl/src/test/resources/dsl/include/model.dsl index 6bdd6ec8b..71e89755d 100644 --- a/structurizr-dsl/src/test/resources/dsl/include/model.dsl +++ b/structurizr-dsl/src/test/resources/dsl/include/model.dsl @@ -1,3 +1 @@ -softwareSystem = softwareSystem "Software System" { - !docs docs -} \ No newline at end of file +softwareSystem = softwareSystem "Software System" \ No newline at end of file From 91a64b30fda286b2bf5df7af967f6ff3c2ba7848 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 11 Sep 2025 09:18:59 +0100 Subject: [PATCH 668/717] structurizr-dsl: DSL source is only stored in the JSON workspace when the DSL is deemed as "portable" (i.e. no files, plugins, scripts). --- changelog.md | 3 +- gradle.properties | 2 +- .../com/structurizr/AbstractWorkspace.java | 18 +- .../com/structurizr/dsl/BrandingParser.java | 1 + .../dsl/ComponentFinderDslContext.java | 1 + .../com/structurizr/dsl/DecisionsParser.java | 2 + .../java/com/structurizr/dsl/DocsParser.java | 2 + .../java/com/structurizr/dsl/DslContext.java | 9 + .../java/com/structurizr/dsl/DslLine.java | 4 +- .../java/com/structurizr/dsl/DslUtils.java | 9 + .../structurizr/dsl/ElementStyleParser.java | 1 + .../dsl/ImageViewContentParser.java | 4 + .../dsl/ImpliedRelationshipsParser.java | 4 + .../com/structurizr/dsl/IncludeParser.java | 28 ++- .../structurizr/dsl/IncludedDslContext.java | 33 --- .../com/structurizr/dsl/PluginDslContext.java | 1 + .../com/structurizr/dsl/ScriptDslContext.java | 1 + .../structurizr/dsl/StructurizrDslParser.java | 44 ++-- .../com/structurizr/dsl/WorkspaceParser.java | 3 + .../java/com/structurizr/dsl/DslTests.java | 204 +++++++++++++----- .../structurizr/dsl/IncludeParserTests.java | 6 +- 21 files changed, 251 insertions(+), 129 deletions(-) delete mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/IncludedDslContext.java diff --git a/changelog.md b/changelog.md index b2c46c6fd..068d5d942 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,6 @@ # Changelog -## v4.2.0 (unreleased) +## v5.0.0 (unreleased) - structurizr-java: Fixes https://github.com/structurizr/java/issues/437 (Make ComponentFinder.run() not fail on empty Set). - structurizr-dsl: Adds support for `iconPosition` on element styles (options are `Top`, `Bottom`, `Left`). @@ -15,6 +15,7 @@ - structurizr-dsl: Adds support for a `jump` property on relationship styles. - structurizr-dsl: PlantUML, Mermaid, and Kroki image views can now be defined by an inline source block. - structurizr-dsl: Constants and variables are inherited when extending a DSL workspace. +- structurizr-dsl: DSL source is only stored in the JSON workspace when the DSL is deemed as "portable" (i.e. no files, plugins, scripts). - structurizr-import: Adds support for `plantuml.inline`, `mermaid.inline`, and `kroki.inline` properties to inline the resulting PNG/SVG file into the workspace. - structurizr-inspection: Adds a way to disable inspections via a workspace property named `structurizr.inspection` (`false` to disable). - structurizr-inspection: Default inspector adds a summary of error/warning/info/ignore counts as workspace properties. diff --git a/gradle.properties b/gradle.properties index c99ca35d7..638aa1b58 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,4 +5,4 @@ signing.secretKeyRingFile=/some/path ossrhUsername=username ossrhPassword=password -version=4.2.0 \ No newline at end of file +version=5.0.0 \ No newline at end of file diff --git a/structurizr-core/src/main/java/com/structurizr/AbstractWorkspace.java b/structurizr-core/src/main/java/com/structurizr/AbstractWorkspace.java index 755cfa1bb..9f067fb6f 100644 --- a/structurizr-core/src/main/java/com/structurizr/AbstractWorkspace.java +++ b/structurizr-core/src/main/java/com/structurizr/AbstractWorkspace.java @@ -1,6 +1,7 @@ package com.structurizr; import com.structurizr.configuration.WorkspaceConfiguration; +import com.structurizr.util.StringUtils; import java.lang.reflect.Constructor; import java.util.Collections; @@ -229,17 +230,30 @@ public Map getProperties() { * @param value the value of the property */ public void addProperty(String name, String value) { - if (name == null || name.trim().length() == 0) { + if (StringUtils.isNullOrEmpty(name)) { throw new IllegalArgumentException("A property name must be specified."); } - if (value == null || value.trim().length() == 0) { + if (StringUtils.isNullOrEmpty(value)) { throw new IllegalArgumentException("A property value must be specified."); } properties.put(name, value); } + /** + * Removes a name-value pair property from this workspace. + * + * @param name the name of the property to remove + */ + public void removeProperty(String name) { + if (StringUtils.isNullOrEmpty(name)) { + throw new IllegalArgumentException("A property name must be specified."); + } + + properties.remove(name); + } + void setProperties(Map properties) { if (properties != null) { this.properties = new HashMap<>(properties); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/BrandingParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/BrandingParser.java index b4f10353d..5b82b6594 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/BrandingParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/BrandingParser.java @@ -30,6 +30,7 @@ void parseLogo(BrandingDslContext context, Tokens tokens, boolean restricted) { if (!restricted) { File file = new File(context.getFile().getParent(), path); if (file.exists() && !file.isDirectory()) { + context.setDslPortable(false); try { String dataUri = ImageUtils.getImageAsDataUri(file); context.getWorkspace().getViews().getConfiguration().getBranding().setLogo(dataUri); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderDslContext.java index 96dc8c077..60eaf4a32 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ComponentFinderDslContext.java @@ -16,6 +16,7 @@ final class ComponentFinderDslContext extends DslContext { this.dslParser = dslParser; this.containerDslContext = containerDslContext; componentFinderBuilder.forContainer(containerDslContext.getContainer()); + setDslPortable(false); } @Override diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DecisionsParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DecisionsParser.java index 0db52d27a..cf380e068 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/DecisionsParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DecisionsParser.java @@ -47,6 +47,8 @@ void parse(ComponentDslContext context, File dslFile, Tokens tokens) { private void parse(DslContext context, Documentable documentable, File dslFile, Tokens tokens) { // !adrs + context.setDslPortable(false); + if (tokens.hasMoreThan(TYPE_OR_FQN_INDEX)) { throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DocsParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DocsParser.java index c758781f9..8cb4e6d51 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/DocsParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DocsParser.java @@ -35,6 +35,8 @@ void parse(ComponentDslContext context, File dslFile, Tokens tokens) { private void parse(DslContext context, Documentable documentable, File dslFile, Tokens tokens) { // !docs + context.setDslPortable(false); + if (tokens.hasMoreThan(FQN_INDEX)) { throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DslContext.java index 41417ba8e..4998d6864 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/DslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DslContext.java @@ -17,6 +17,7 @@ abstract class DslContext { private Workspace workspace; private boolean extendingWorkspace; + private boolean dslPortable = true; protected IdentifiersRegister identifiersRegister = new IdentifiersRegister(); @@ -36,6 +37,14 @@ void setExtendingWorkspace(boolean extendingWorkspace) { this.extendingWorkspace = extendingWorkspace; } + boolean isDslPortable() { + return dslPortable; + } + + void setDslPortable(boolean bool) { + this.dslPortable = bool; + } + void setIdentifierRegister(IdentifiersRegister identifersRegister) { this.identifiersRegister = identifersRegister; } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DslLine.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DslLine.java index ca2ad4ec0..b39952fa5 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/DslLine.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DslLine.java @@ -8,8 +8,8 @@ class DslLine { private final String source; private final int lineNumber; - DslLine(String source, int lineNumber) { - this.source = source; + DslLine(String processedSource, int lineNumber) { + this.source = processedSource; this.lineNumber = lineNumber; } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DslUtils.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DslUtils.java index e20b936f2..33662ac87 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/DslUtils.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DslUtils.java @@ -42,4 +42,13 @@ public static void setDsl(Workspace workspace, String dsl) { } } + /** + * Clears the DSL associated with a workspace. + * + * @param workspace a Workspace object + */ + public static void clearDsl(Workspace workspace) { + workspace.removeProperty(STRUCTURIZR_DSL_PROPERTY_NAME); + } + } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementStyleParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementStyleParser.java index dd76e5804..dd83c431e 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementStyleParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementStyleParser.java @@ -303,6 +303,7 @@ void parseIcon(ElementStyleDslContext context, Tokens tokens, boolean restricted if (file.exists() && !file.isDirectory()) { try { style.setIcon(ImageUtils.getImageAsDataUri(file)); + context.setDslPortable(false); } catch (IOException e) { throw new RuntimeException(e); } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewContentParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewContentParser.java index e73a235ed..5c049f4f6 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewContentParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewContentParser.java @@ -63,6 +63,7 @@ void parsePlantUML(ImageViewDslContext context, File dslFile, Tokens tokens) { if (!restricted) { File file = new File(dslFile.getParentFile(), source); if (file.exists()) { + context.setDslPortable(false); new PlantUMLImporter().importDiagram(context.getView(), file); } else { throw new RuntimeException("The file at " + file.getAbsolutePath() + " does not exist"); @@ -113,6 +114,7 @@ void parseMermaid(ImageViewDslContext context, File dslFile, Tokens tokens) { if (!restricted) { File file = new File(dslFile.getParentFile(), source); if (file.exists()) { + context.setDslPortable(false); new MermaidImporter().importDiagram(context.getView(), file); } else { throw new RuntimeException("The file at " + file.getAbsolutePath() + " does not exist"); @@ -158,6 +160,7 @@ void parseKroki(ImageViewDslContext context, File dslFile, Tokens tokens) { if (!restricted) { File file = new File(dslFile.getParentFile(), source); if (file.exists()) { + context.setDslPortable(false); new KrokiImporter().importDiagram(context.getView(), format, file); } else { throw new RuntimeException("The file at " + file.getAbsolutePath() + " does not exist"); @@ -196,6 +199,7 @@ void parseImage(ImageViewDslContext context, File dslFile, Tokens tokens) { if (!restricted) { File file = new File(dslFile.getParentFile(), source); if (file.exists()) { + context.setDslPortable(false); context.getView().setContent(ImageUtils.getImageAsDataUri(file)); context.getView().setTitle(file.getName()); } else { diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImpliedRelationshipsParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImpliedRelationshipsParser.java index b647e5b3d..e880f25a7 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImpliedRelationshipsParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImpliedRelationshipsParser.java @@ -46,6 +46,10 @@ void parse(DslContext context, Tokens tokens, File dslFile, boolean restricted) } } + if (!BUILT_IN_IMPLIED_RELATIONSHIPS_STRATEGIES.contains(option)) { + context.setDslPortable(false); + } + try { Class impliedRelationshipsStrategyClass = context.loadClass(option, dslFile); Constructor constructor = impliedRelationshipsStrategyClass.getDeclaredConstructor(); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/IncludeParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/IncludeParser.java index 93252d57d..bfea57783 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/IncludeParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/IncludeParser.java @@ -4,6 +4,7 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -13,9 +14,11 @@ final class IncludeParser extends AbstractParser { private static final int SOURCE_INDEX = 1; - void parse(IncludedDslContext context, Tokens tokens) { + List parse(DslContext context, File dslFile, Tokens tokens) { // !include + List includedFiles = new ArrayList<>(); + if (tokens.hasMoreThan(SOURCE_INDEX)) { throw new RuntimeException("Too many tokens, expected: " + GRAMMAR); } @@ -28,28 +31,33 @@ void parse(IncludedDslContext context, Tokens tokens) { if (source.startsWith("https://") || source.startsWith("http://")) { RemoteContent content = readFromUrl(source); List lines = Arrays.asList(content.getContent().split("\n")); - context.addFile(context.getParentFile(), lines); + includedFiles.add(new IncludedFile(dslFile, lines)); } else { - if (context.getParentFile() != null) { - File path = new File(context.getParentFile().getParent(), source); + if (dslFile != null) { + File path = new File(dslFile.getParent(), source); try { if (!path.exists()) { throw new RuntimeException(path.getCanonicalPath() + " could not be found"); } - readFiles(context, path); + includedFiles.addAll(readFiles(path)); + context.setDslPortable(false); } catch (IOException e) { throw new RuntimeException("Error including " + path.getAbsolutePath() + ": " + e.getMessage()); } } } + + return includedFiles; } - private void readFiles(IncludedDslContext context, File path) throws IOException { + private List readFiles(File path) throws IOException { + List includedFiles = new ArrayList<>(); + if (path.isHidden() || path.getName().startsWith(".")) { // ignore - return; + return includedFiles; } if (path.isDirectory()) { @@ -58,16 +66,18 @@ private void readFiles(IncludedDslContext context, File path) throws IOException Arrays.sort(files); for (File file : files) { - readFiles(context, file); + includedFiles.addAll(readFiles(file)); } } } else { try { - context.addFile(path, Files.readAllLines(path.toPath(), StandardCharsets.UTF_8)); + includedFiles.add(new IncludedFile(path, Files.readAllLines(path.toPath(), StandardCharsets.UTF_8))); } catch (IOException e) { throw new RuntimeException("Error reading file at " + path.getAbsolutePath() + ": " + e.getMessage()); } } + + return includedFiles; } } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/IncludedDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/IncludedDslContext.java deleted file mode 100644 index 42c86d00a..000000000 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/IncludedDslContext.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.structurizr.dsl; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -final class IncludedDslContext extends DslContext { - - private final File parentFile; - private final List files = new ArrayList<>(); - - IncludedDslContext(File parentFile) { - this.parentFile = parentFile; - } - - File getParentFile() { - return parentFile; - } - - void addFile(File file, List lines) { - this.files.add(new IncludedFile(file, lines)); - } - - List getFiles() { - return new ArrayList<>(files); - } - - @Override - protected String[] getPermittedTokens() { - return new String[0]; - } - -} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/PluginDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/PluginDslContext.java index a648c2e80..7885db081 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/PluginDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/PluginDslContext.java @@ -15,6 +15,7 @@ class PluginDslContext extends DslContext { this.fullyQualifiedClassName = fullyQualifiedClassName; this.dslFile = dslFile; this.dslParser = dslParser; + setDslPortable(false); } void addParameter(String name, String value) { diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ScriptDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ScriptDslContext.java index 6463e8324..ee25451ec 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ScriptDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ScriptDslContext.java @@ -29,6 +29,7 @@ abstract class ScriptDslContext extends DslContext { this.parentContext = parentContext; this.dslFile = dslFile; this.dslParser = dslParser; + setDslPortable(false); } void addParameter(String name, String value) { diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index 4cc143efe..7eb8bd7a7 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -60,6 +60,7 @@ public final class StructurizrDslParser extends StructurizrDslTokens { StructurizrDslTokens.RELATIONSHIP_TOKEN, new HashMap<>() ); + private boolean dslPortable = true; private final List dslSourceLines = new ArrayList<>(); private Workspace workspace; private boolean extendingWorkspace = false; @@ -123,9 +124,11 @@ public void setRestricted(boolean restricted) { */ public Workspace getWorkspace() { if (workspace != null) { - String value = workspace.getProperties().get(DslUtils.STRUCTURIZR_DSL_RETAIN_SOURCE_PROPERTY_NAME); - if (value == null || value.equalsIgnoreCase("true")) { - DslUtils.setDsl(workspace, getParsedDsl()); + if (dslPortable) { + String value = workspace.getProperties().get(DslUtils.STRUCTURIZR_DSL_RETAIN_SOURCE_PROPERTY_NAME); + if (value == null || value.equalsIgnoreCase("true")) { + DslUtils.setDsl(workspace, getParsedDsl()); + } } } @@ -210,11 +213,14 @@ void parse(List lines, DslContext dslContext) throws StructurizrDslParse * @throws StructurizrDslParserException when something goes wrong */ void parse(List lines, File dslFile, boolean fragment, boolean includeInDslSourceLines) throws StructurizrDslParserException { + if (includeInDslSourceLines) { + dslSourceLines.addAll(lines); + } + List dslLines = preProcessLines(lines); for (DslLine dslLine : dslLines) { String line = dslLine.getSource(); - String lineForDslSource = line; if (line.startsWith(BOM)) { // this caters for files encoded as "UTF-8 with BOM" @@ -266,9 +272,8 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn if (!restricted || tokens.get(1).startsWith("https://") || tokens.get(1).startsWith("http://")) { String leadingSpace = line.substring(0, line.indexOf(INCLUDE_FILE_TOKEN)); - IncludedDslContext context = new IncludedDslContext(dslFile); - new IncludeParser().parse(context, tokens); - for (IncludedFile includedFile : context.getFiles()) { + List files = new IncludeParser().parse(getContext(), dslFile, tokens); + for (IncludedFile includedFile : files) { List paddedLines = new ArrayList<>(); for (String unpaddedLine : includedFile.getLines()) { if (unpaddedLine.startsWith(BOM)) { @@ -278,15 +283,12 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn paddedLines.add(leadingSpace + unpaddedLine); } - parse(paddedLines, includedFile.getFile(), true, true); + parse(paddedLines, includedFile.getFile(), true, false); } } else { throwRestrictedModeException(firstToken + " "); } - // include the !include in the parser DSL as: # !include ... - lineForDslSource = null; - } else if (PLUGIN_TOKEN.equalsIgnoreCase(firstToken)) { if (!restricted) { String fullyQualifiedClassName = new PluginParser().parse(getContext(), tokens.withoutContextStartToken()); @@ -677,8 +679,11 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn workspace = new WorkspaceParser().parse(dslParserContext, tokens.withoutContextStartToken()); extendingWorkspace = !workspace.getModel().isEmpty(); - startContext(new WorkspaceDslContext()); + WorkspaceDslContext context = new WorkspaceDslContext(); + context.setDslPortable(dslParserContext.isDslPortable()); + startContext(context); parsedTokens.add(WORKSPACE_TOKEN); + } else if (IMPLIED_RELATIONSHIPS_TOKEN.equalsIgnoreCase(firstToken) || IMPLIED_RELATIONSHIPS_TOKEN.substring(1).equalsIgnoreCase(firstToken)) { new ImpliedRelationshipsParser().parse(getContext(), tokens, dslFile, restricted); @@ -1261,10 +1266,6 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn } } } - - if (includeInDslSourceLines && lineForDslSource != null) { - dslSourceLines.add(lineForDslSource); - } } catch (Exception e) { if (e.getMessage() != null) { throw new StructurizrDslParserException(e.getMessage(), dslFile, dslLine.getLineNumber(), line); @@ -1332,15 +1333,16 @@ private List preProcessLines(List lines) { if (lineComplete) { // replace the text block with a constant (that will become substituted later) // (this makes it possible for text blocks to include double-quote characters) - String s = buf.toString(); - if (s.endsWith(TEXT_BLOCK_MARKER)) { - String[] parts = s.split(TEXT_BLOCK_MARKER); + String source = buf.toString(); + + if (source.endsWith(TEXT_BLOCK_MARKER)) { + String[] parts = source.split(TEXT_BLOCK_MARKER); String constantName = UUID.randomUUID().toString(); String constantValue = parts[1].substring(0, parts[1].length() - 1); // remove final line break addConstant(constantName, constantValue); dslLines.add(new DslLine(parts[0] + "\"" + String.format(STRING_SUBSTITUTION_TEMPLATE, constantName) + "\"", lineNumber)); } else { - dslLines.add(new DslLine(buf.toString(), lineNumber)); + dslLines.add(new DslLine(source, lineNumber)); } buf = new StringBuilder(); @@ -1412,6 +1414,8 @@ private void endContext() throws StructurizrDslParserException { if (!contextStack.empty()) { DslContext context = contextStack.pop(); context.end(); + + dslPortable &= context.isDslPortable(); } else { throw new StructurizrDslParserException("Unexpected end of context"); } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/WorkspaceParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/WorkspaceParser.java index 56bc1035e..e116de60a 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/WorkspaceParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/WorkspaceParser.java @@ -75,6 +75,9 @@ Workspace parse(DslParserContext context, Tokens tokens) { workspace = structurizrDslParser.getWorkspace(); context.getParser().configureFrom(structurizrDslParser); } + + DslUtils.clearDsl(workspace); + context.setDslPortable(false); } } } catch (Exception e) { diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index d90e5a46d..43fcb6e0f 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -4,6 +4,7 @@ import com.structurizr.documentation.Section; import com.structurizr.model.*; import com.structurizr.util.StringUtils; +import com.structurizr.util.WorkspaceUtils; import com.structurizr.view.*; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @@ -325,15 +326,7 @@ void test_includeLocalFile() throws Exception { assertEquals(1, model.getSoftwareSystems().size()); assertNotNull(model.getSoftwareSystemWithName("Software System")); - assertEquals("workspace {\n" + - "\n" + - " model {\n" + - " softwareSystem = softwareSystem \"Software System\" {\n" + - " !docs docs\n" + - " }\n" + - " }\n" + - "\n" + - "}", new String(Base64.getDecoder().decode(workspace.getProperties().get("structurizr.dsl")))); + assertEquals("", DslUtils.getDsl(workspace)); } @Test @@ -362,26 +355,7 @@ void test_includeLocalDirectory() throws Exception { assertNotNull(softwareSystem3); assertEquals(1, softwareSystem3.getDocumentation().getSections().size()); - assertEquals("workspace {\n" + - "\n" + - " model {\n" + - " !var SOFTWARE_SYSTEM_NAME \"Software System 1\"\n" + - " softwareSystem \"${SOFTWARE_SYSTEM_NAME}\" {\n" + - " !docs ../../docs\n" + - " }\n" + - "\n" + - " !var SOFTWARE_SYSTEM_NAME \"Software System 2\"\n" + - " softwareSystem \"${SOFTWARE_SYSTEM_NAME}\" {\n" + - " !docs ../../docs\n" + - " }\n" + - "\n" + - " !var SOFTWARE_SYSTEM_NAME \"Software System 3\"\n" + - " softwareSystem \"${SOFTWARE_SYSTEM_NAME}\" {\n" + - " !docs ../../docs\n" + - " }\n" + - " }\n" + - "\n" + - "}", new String(Base64.getDecoder().decode(workspace.getProperties().get("structurizr.dsl")))); + assertEquals("", DslUtils.getDsl(workspace)); } @Test @@ -404,20 +378,18 @@ void test_includeUrl() throws Exception { Workspace workspace = parser.getWorkspace(); Model model = workspace.getModel(); - ViewSet views = workspace.getViews(); - assertEquals(1, workspace.getModel().getSoftwareSystems().size()); - SoftwareSystem softwareSystem = model.getSoftwareSystemWithName("Software System"); + assertEquals(1, model.getSoftwareSystems().size()); + assertNotNull(model.getSoftwareSystemWithName("Software System")); - assertEquals("workspace {\n" + - "\n" + - " model {\n" + - " softwareSystem = softwareSystem \"Software System\" {\n" + - " !docs docs\n" + - " }\n" + - " }\n" + - "\n" + - "}", new String(Base64.getDecoder().decode(workspace.getProperties().get("structurizr.dsl")))); + assertEquals(""" + workspace { + + model { + !include https://raw.githubusercontent.com/structurizr/dsl/master/src/test/dsl/include/model.dsl + } + + }""", DslUtils.getDsl(workspace)); } @Test @@ -435,10 +407,35 @@ void test_includeLocalFile_ThrowsAnException_WhenRunningInRestrictedMode() { } } - @ParameterizedTest + @Test + @Tag("IntegrationTest") + void test_extendWorkspaceFromJsonFile() throws Exception { + String dslFile = "src/test/resources/dsl/extend/extend-workspace-from-json-file.dsl"; + StructurizrDslParser parser = new StructurizrDslParser(); + parser.parse(new File(dslFile)); + + Workspace workspace = parser.getWorkspace(); + Model model = workspace.getModel(); + assertEquals(CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy.class, model.getImpliedRelationshipsStrategy().getClass()); + + assertEquals(1, model.getPeople().size()); + Person user = model.getPersonWithName("User"); + + assertEquals(3, workspace.getModel().getSoftwareSystems().size()); + SoftwareSystem softwareSystem = model.getSoftwareSystemWithName("Software System 1"); + assertTrue(user.hasEfferentRelationshipWith(softwareSystem, "Uses")); + + assertEquals(2, softwareSystem.getContainers().size()); + assertNotNull(softwareSystem.getContainers().stream().filter(c -> c.getName().equals("Web Application 1")).findFirst()); + assertNotNull(softwareSystem.getContainers().stream().filter(c -> c.getName().equals("Web Application 2")).findFirst()); + + assertEquals("", DslUtils.getDsl(workspace)); + } + + @Test @Tag("IntegrationTest") - @ValueSource(strings = { "src/test/resources/dsl/extend/extend-workspace-from-json-file.dsl", "src/test/resources/dsl/extend/extend-workspace-from-json-url.dsl" }) - void test_extendWorkspaceFromJson(String dslFile) throws Exception { + void test_extendWorkspaceFromJsonUrl() throws Exception { + String dslFile = "src/test/resources/dsl/extend/extend-workspace-from-json-url.dsl"; StructurizrDslParser parser = new StructurizrDslParser(); parser.parse(new File(dslFile)); @@ -456,6 +453,26 @@ void test_extendWorkspaceFromJson(String dslFile) throws Exception { assertEquals(2, softwareSystem.getContainers().size()); assertNotNull(softwareSystem.getContainers().stream().filter(c -> c.getName().equals("Web Application 1")).findFirst()); assertNotNull(softwareSystem.getContainers().stream().filter(c -> c.getName().equals("Web Application 2")).findFirst()); + + assertEquals(""" + workspace extends https://raw.githubusercontent.com/structurizr/dsl/master/src/test/dsl/extend/workspace.json { + + model { + // !element with DSL identifier + !element softwareSystem1 { + webapp1 = container "Web Application 1" + } + + // !element with canonical name + !element "SoftwareSystem://Software System 1" { + webapp2 = container "Web Application 2" + } + + user -> softwareSystem1 "Uses" + softwareSystem3.webapp -> softwareSystem3.db + } + + }""", DslUtils.getDsl(workspace)); } @Test @@ -726,6 +743,9 @@ void test_pluginWithoutParameters() throws Exception { parser.parse(new File("src/test/resources/dsl/plugin-without-parameters.dsl")); assertNotNull(parser.getWorkspace().getModel().getPersonWithName("Java")); + + // check source isn't retained + assertEquals("", DslUtils.getDsl(parser.getWorkspace())); } @Test @@ -734,6 +754,9 @@ void test_pluginWithParameters() throws Exception { parser.parse(new File("src/test/resources/dsl/plugin-with-parameters.dsl")); assertNotNull(parser.getWorkspace().getModel().getPersonWithName("Java")); + + // check source isn't retained + assertEquals("", DslUtils.getDsl(parser.getWorkspace())); } @Test @@ -749,21 +772,27 @@ void test_script_ThrowsAnException_WhenTheParserIsInRestrictedMode() { } @Test - void test_script() throws Exception { + void test_externalScript() throws Exception { StructurizrDslParser parser = new StructurizrDslParser(); parser.parse(new File("src/test/resources/dsl/script-external.dsl")); assertNotNull(parser.getWorkspace().getModel().getPersonWithName("Groovy")); assertNotNull(parser.getWorkspace().getModel().getPersonWithName("Kotlin")); assertNotNull(parser.getWorkspace().getModel().getPersonWithName("Ruby")); + + // check source isn't retained + assertEquals("", DslUtils.getDsl(parser.getWorkspace())); } @Test - void test_scriptWithParameters() throws Exception { + void test_externalScriptWithParameters() throws Exception { StructurizrDslParser parser = new StructurizrDslParser(); parser.parse(new File("src/test/resources/dsl/script-external-with-parameters.dsl")); assertNotNull(parser.getWorkspace().getModel().getPersonWithName("Groovy")); + + // check source isn't retained + assertEquals("", DslUtils.getDsl(parser.getWorkspace())); } @Test @@ -778,6 +807,9 @@ void test_inlineScript() throws Exception { assertTrue(parser.getWorkspace().getModel().getPersonWithName("User").hasTag("Groovy")); assertTrue(parser.getWorkspace().getModel().getPersonWithName("User").getRelationships().iterator().next().hasTag("Groovy")); assertEquals("Groovy", parser.getWorkspace().getViews().getSystemLandscapeViews().iterator().next().getDescription()); + + // check source isn't retained + assertEquals("", DslUtils.getDsl(parser.getWorkspace())); } @Test @@ -818,6 +850,9 @@ void test_docs() throws Exception { Content...""", sections.iterator().next().getContent()); assertEquals(1, component.getDocumentation().getSections().size()); + + // check source isn't retained + assertEquals("", DslUtils.getDsl(parser.getWorkspace())); } @Test @@ -850,6 +885,9 @@ void test_decisions() throws Exception { // log4brains decisions assertEquals(4, component.getDocumentation().getDecisions().size()); + + // check source isn't retained + assertEquals("", DslUtils.getDsl(parser.getWorkspace())); } @Test @@ -1092,6 +1130,9 @@ void test_imageViews_ViaFiles() throws Exception { assertEquals("image.svg", svgView.getTitle()); assertEquals("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXMtYXNjaWkiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiBjb250ZW50U3R5bGVUeXBlPSJ0ZXh0L2NzcyIgaGVpZ2h0PSIxMjBweCIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSIgc3R5bGU9IndpZHRoOjExM3B4O2hlaWdodDoxMjBweDtiYWNrZ3JvdW5kOiNGRkZGRkY7IiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCAxMTMgMTIwIiB3aWR0aD0iMTEzcHgiIHpvb21BbmRQYW49Im1hZ25pZnkiPjxkZWZzLz48Zz48bGluZSBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjAuNTtzdHJva2UtZGFzaGFycmF5OjUuMCw1LjA7IiB4MT0iMjYiIHgyPSIyNiIgeTE9IjM2LjI5NjkiIHkyPSI4NS40Mjk3Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDowLjU7c3Ryb2tlLWRhc2hhcnJheTo1LjAsNS4wOyIgeDE9IjgyIiB4Mj0iODIiIHkxPSIzNi4yOTY5IiB5Mj0iODUuNDI5NyIvPjxyZWN0IGZpbGw9IiNFMkUyRjAiIGhlaWdodD0iMzAuMjk2OSIgcng9IjIuNSIgcnk9IjIuNSIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDowLjU7IiB3aWR0aD0iNDMiIHg9IjUiIHk9IjUiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIyOSIgeD0iMTIiIHk9IjI0Ljk5NTEiPkJvYjwvdGV4dD48cmVjdCBmaWxsPSIjRTJFMkYwIiBoZWlnaHQ9IjMwLjI5NjkiIHJ4PSIyLjUiIHJ5PSIyLjUiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MC41OyIgd2lkdGg9IjQzIiB4PSI1IiB5PSI4NC40Mjk3Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTQiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iMjkiIHg9IjEyIiB5PSIxMDQuNDI0OCI+Qm9iPC90ZXh0PjxyZWN0IGZpbGw9IiNFMkUyRjAiIGhlaWdodD0iMzAuMjk2OSIgcng9IjIuNSIgcnk9IjIuNSIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDowLjU7IiB3aWR0aD0iNDkiIHg9IjU4IiB5PSI1Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTQiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iMzUiIHg9IjY1IiB5PSIyNC45OTUxIj5BbGljZTwvdGV4dD48cmVjdCBmaWxsPSIjRTJFMkYwIiBoZWlnaHQ9IjMwLjI5NjkiIHJ4PSIyLjUiIHJ5PSIyLjUiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MC41OyIgd2lkdGg9IjQ5IiB4PSI1OCIgeT0iODQuNDI5NyIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjM1IiB4PSI2NSIgeT0iMTA0LjQyNDgiPkFsaWNlPC90ZXh0Pjxwb2x5Z29uIGZpbGw9IiMxODE4MTgiIHBvaW50cz0iNzAuNSw2My40Mjk3LDgwLjUsNjcuNDI5Nyw3MC41LDcxLjQyOTcsNzQuNSw2Ny40Mjk3IiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiIHgxPSIyNi41IiB4Mj0iNzYuNSIgeTE9IjY3LjQyOTciIHkyPSI2Ny40Mjk3Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTMiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iMzAiIHg9IjMzLjUiIHk9IjYyLjM2MzgiPmhlbGxvPC90ZXh0PjwhLS1TUkM9W1N5ZkZLajJyS3QzQ29LbkVMUjFJbzRaRG9TYTcwMDAwXS0tPjwvZz48L3N2Zz4=", svgView.getContent()); assertEquals("image/svg+xml", svgView.getContentType()); + + // check that source isn't retained + assertEquals("", DslUtils.getDsl(workspace)); } @Test @@ -1127,6 +1168,44 @@ void test_imageViews_ViaUrls() throws Exception { assertEquals("image.svg", svgView.getTitle()); assertEquals("https://raw.githubusercontent.com/structurizr/java/master/structurizr-dsl/src/test/resources/dsl/image-views/image.svg", svgView.getContent()); assertEquals("image/svg+xml", svgView.getContentType()); + + // check that source is retained + assertEquals(""" + workspace { + + views { + properties { + "plantuml.url" "http://localhost:7777" + "plantuml.format" "svg" + "mermaid.url" "http://localhost:8888" + "mermaid.format" "svg" + "mermaid.compress" "false" + "kroki.url" "http://localhost:9999" + "kroki.format" "svg" + } + + image * "plantuml" { + plantuml https://raw.githubusercontent.com/structurizr/java/master/structurizr-dsl/src/test/resources/dsl/image-views/diagram.puml + } + + image * "mermaid" { + mermaid https://raw.githubusercontent.com/structurizr/java/master/structurizr-dsl/src/test/resources/dsl/image-views/diagram.mmd + } + + image * "kroki" { + kroki graphviz https://raw.githubusercontent.com/structurizr/java/master/structurizr-dsl/src/test/resources/dsl/image-views/diagram.dot + } + + image * "png" { + image https://raw.githubusercontent.com/structurizr/java/master/structurizr-dsl/src/test/resources/dsl/image-views/image.png + } + + image * "svg" { + image https://raw.githubusercontent.com/structurizr/java/master/structurizr-dsl/src/test/resources/dsl/image-views/image.svg + } + } + + }""", DslUtils.getDsl(workspace)); } @Test @@ -1429,9 +1508,8 @@ void springPetClinic() throws Exception { assertNotNull(ownerController.getEfferentRelationshipWith(ownerRepository)); assertNotNull(vetController.getEfferentRelationshipWith(vetRepository)); - // this checks that the component forEach { ... } lines don't get repeated in the outputted DSL source - String content = Files.readString(workspaceFile.toPath()); - assertEquals(content, new String(Base64.getDecoder().decode(parser.getWorkspace().getProperties().get("structurizr.dsl")))); + // checks that source isn't retained + assertEquals("", DslUtils.getDsl(workspace)); } else { System.out.println("Skipping Spring PetClinic example..."); @@ -1475,18 +1553,12 @@ void test_sourceIsRetained() throws Exception { }""", DslUtils.getDsl(workspace)); + // source not retained because workspace extends a file-based resource File childDslFile = new File("src/test/resources/dsl/source-child.dsl"); parser = new StructurizrDslParser(); parser.parse(childDslFile); workspace = parser.getWorkspace(); - assertEquals(""" -workspace extends source-parent.dsl { - - model { - b = softwareSystem "B" - } - -}""", DslUtils.getDsl(workspace)); + assertEquals("", DslUtils.getDsl(workspace)); } @Test @@ -1625,11 +1697,27 @@ void test_textBlock() throws Exception { StructurizrDslParser parser = new StructurizrDslParser(); parser.parse(new File("src/test/resources/dsl/text-block.dsl")); - SoftwareSystem softwareSystem = parser.getWorkspace().getModel().getSoftwareSystemWithName("Name"); + workspace = parser.getWorkspace(); + SoftwareSystem softwareSystem = workspace.getModel().getSoftwareSystemWithName("Name"); assertEquals(""" - Line 1 - Line 2 - Line 3""", softwareSystem.getDescription()); + + assertEquals(""" + workspace { + + model { + softwareSystem = softwareSystem "Name" { + description ""\" + - Line 1 + - Line 2 + - Line 3 + ""\" + } + } + + }""", DslUtils.getDsl(workspace)); } @Test diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/IncludeParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/IncludeParserTests.java index cc5cad518..3a7e7e41c 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/IncludeParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/IncludeParserTests.java @@ -7,12 +7,12 @@ class IncludeParserTests extends AbstractTests { - private IncludeParser parser = new IncludeParser(); + private final IncludeParser parser = new IncludeParser(); @Test void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { try { - parser.parse(new IncludedDslContext(null), tokens("!include", "file", "extra")); + parser.parse(context(), null, tokens("!include", "file", "extra")); fail(); } catch (Exception e) { assertEquals("Too many tokens, expected: !include ", e.getMessage()); @@ -22,7 +22,7 @@ void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { @Test void test_parse_ThrowsAnException_WhenAFileIsNotSpecified() { try { - parser.parse(new IncludedDslContext(null), tokens("!include")); + parser.parse(context(), null, tokens("!include")); fail(); } catch (Exception e) { assertEquals("Expected: !include ", e.getMessage()); From f33f7edb4ba852b8afb0b263cf807d775a71fe2f Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 11 Sep 2025 09:19:27 +0100 Subject: [PATCH 669/717] theme should not be available when running in restricted mode. --- .../structurizr/dsl/StructurizrDslParser.java | 4 +-- .../java/com/structurizr/dsl/ThemeParser.java | 36 +++++++++++-------- .../com/structurizr/dsl/ThemeParserTests.java | 33 +++++++++++------ 3 files changed, 45 insertions(+), 28 deletions(-) diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index 7eb8bd7a7..5561e2e17 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -1114,10 +1114,10 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn new DynamicViewRelationshipParser().parseUrl(getContext(DynamicViewRelationshipContext.class), tokens.withoutContextStartToken()); } else if (THEME_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ViewsDslContext.class) || inContext(StylesDslContext.class))) { - new ThemeParser().parseTheme(getContext(), dslFile, tokens); + new ThemeParser().parseTheme(getContext(), dslFile, tokens, restricted); } else if (THEMES_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ViewsDslContext.class) || inContext(StylesDslContext.class))) { - new ThemeParser().parseThemes(getContext(), dslFile, tokens); + new ThemeParser().parseThemes(getContext(), dslFile, tokens, restricted); } else if (TERMINOLOGY_TOKEN.equalsIgnoreCase(firstToken) && inContext(ViewsDslContext.class)) { startContext(new TerminologyDslContext()); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ThemeParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ThemeParser.java index c16c531c5..d228972ca 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ThemeParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ThemeParser.java @@ -12,7 +12,7 @@ final class ThemeParser extends AbstractParser { private final static int FIRST_THEME_INDEX = 1; - void parseTheme(DslContext context, File dslFile, Tokens tokens) { + void parseTheme(DslContext context, File dslFile, Tokens tokens, boolean restricted) { // theme if (tokens.hasMoreThan(FIRST_THEME_INDEX)) { throw new RuntimeException("Too many tokens, expected: theme "); @@ -22,21 +22,21 @@ void parseTheme(DslContext context, File dslFile, Tokens tokens) { throw new RuntimeException("Expected: theme "); } - addTheme(context, dslFile, tokens.get(FIRST_THEME_INDEX)); + addTheme(context, dslFile, tokens.get(FIRST_THEME_INDEX), restricted); } - void parseThemes(DslContext context, File dslFile, Tokens tokens) { + void parseThemes(DslContext context, File dslFile, Tokens tokens, boolean restricted) { // themes [url|file] ... [url|file] if (!tokens.includes(FIRST_THEME_INDEX)) { throw new RuntimeException("Expected: themes [url|file] ... [url|file]"); } for (int i = FIRST_THEME_INDEX; i < tokens.size(); i++) { - addTheme(context, dslFile, tokens.get(i)); + addTheme(context, dslFile, tokens.get(i), restricted); } } - private void addTheme(DslContext context, File dslFile, String theme) { + private void addTheme(DslContext context, File dslFile, String theme, boolean restricted) { if (DEFAULT_THEME_NAME.equalsIgnoreCase(theme)) { theme = DEFAULT_THEME_URL; } @@ -45,20 +45,26 @@ private void addTheme(DslContext context, File dslFile, String theme) { // this adds the theme to the list of theme URLs in the workspace context.getWorkspace().getViews().getConfiguration().addTheme(theme); } else { - // this inlines the file-based theme into the workspace - File file = new File(dslFile.getParentFile(), theme); - if (file.exists()) { - if (file.isFile()) { - try { - ThemeUtils.inlineTheme(context.getWorkspace(), file); - } catch (Exception e) { - throw new RuntimeException("Error loading theme from " + file.getAbsolutePath() + ": " + e.getMessage()); + if (!restricted) { + context.setDslPortable(false); + + // this inlines the file-based theme into the workspace + File file = new File(dslFile.getParentFile(), theme); + if (file.exists()) { + if (file.isFile()) { + try { + ThemeUtils.inlineTheme(context.getWorkspace(), file); + } catch (Exception e) { + throw new RuntimeException("Error loading theme from " + file.getAbsolutePath() + ": " + e.getMessage()); + } + } else { + throw new RuntimeException(file.getAbsolutePath() + " is not a file"); } } else { - throw new RuntimeException(file.getAbsolutePath() + " is not a file"); + throw new RuntimeException(file.getAbsolutePath() + " does not exist"); } } else { - throw new RuntimeException(file.getAbsolutePath() + " does not exist"); + throw new RuntimeException("File-based themes are not supported when the DSL parser is running in restricted mode"); } } } diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ThemeParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ThemeParserTests.java index 3a3c9312b..5a0075aa6 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ThemeParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ThemeParserTests.java @@ -13,7 +13,7 @@ class ThemeParserTests extends AbstractTests { @Test void test_parseTheme_ThrowsAnException_WhenThereAreTooManyTokens() { try { - parser.parseTheme(context(), null, tokens("theme", "url", "extra")); + parser.parseTheme(context(), null, tokens("theme", "url", "extra"), false); fail(); } catch (Exception e) { assertEquals("Too many tokens, expected: theme ", e.getMessage()); @@ -23,7 +23,7 @@ void test_parseTheme_ThrowsAnException_WhenThereAreTooManyTokens() { @Test void test_parseTheme_ThrowsAnException_WhenNoThemeIsSpecified() { try { - parser.parseTheme(context(), null, tokens("theme")); + parser.parseTheme(context(), null, tokens("theme"), false); fail(); } catch (Exception e) { assertEquals("Expected: theme ", e.getMessage()); @@ -32,7 +32,7 @@ void test_parseTheme_ThrowsAnException_WhenNoThemeIsSpecified() { @Test void test_parseTheme_AddsTheTheme_WhenAThemeIsSpecified() { - parser.parseTheme(context(), null, tokens("theme", "http://example.com/1")); + parser.parseTheme(context(), null, tokens("theme", "http://example.com/1"), false); assertEquals(1, workspace.getViews().getConfiguration().getThemes().length); assertEquals("http://example.com/1", workspace.getViews().getConfiguration().getThemes()[0]); @@ -40,7 +40,7 @@ void test_parseTheme_AddsTheTheme_WhenAThemeIsSpecified() { @Test void test_parseTheme_AddsTheTheme_WhenTheDefaultThemeIsSpecified() { - parser.parseTheme(context(), null, tokens("theme", "default")); + parser.parseTheme(context(), null, tokens("theme", "default"), false); assertEquals(1, workspace.getViews().getConfiguration().getThemes().length); assertEquals("https://static.structurizr.com/themes/default/theme.json", workspace.getViews().getConfiguration().getThemes()[0]); @@ -49,7 +49,7 @@ void test_parseTheme_AddsTheTheme_WhenTheDefaultThemeIsSpecified() { @Test void test_parseThemes_ThrowsAnException_WhenNoThemesAreSpecified() { try { - parser.parseThemes(context(), null, tokens("themes")); + parser.parseThemes(context(), null, tokens("themes"), false); fail(); } catch (Exception e) { assertEquals("Expected: themes [url|file] ... [url|file]", e.getMessage()); @@ -58,7 +58,7 @@ void test_parseThemes_ThrowsAnException_WhenNoThemesAreSpecified() { @Test void test_parseThemes_AddsTheTheme_WhenOneThemeIsSpecified() { - parser.parseThemes(context(), null, tokens("themes", "http://example.com/1")); + parser.parseThemes(context(), null, tokens("themes", "http://example.com/1"), false); assertEquals(1, workspace.getViews().getConfiguration().getThemes().length); assertEquals("http://example.com/1", workspace.getViews().getConfiguration().getThemes()[0]); @@ -66,7 +66,7 @@ void test_parseThemes_AddsTheTheme_WhenOneThemeIsSpecified() { @Test void test_parseThemes_AddsTheThemes_WhenMultipleThemesAreSpecified() { - parser.parseThemes(context(), null, tokens("themes", "http://example.com/1", "http://example.com/2", "http://example.com/3")); + parser.parseThemes(context(), null, tokens("themes", "http://example.com/1", "http://example.com/2", "http://example.com/3"), false); assertEquals(3, workspace.getViews().getConfiguration().getThemes().length); assertEquals("http://example.com/1", workspace.getViews().getConfiguration().getThemes()[0]); @@ -76,7 +76,7 @@ void test_parseThemes_AddsTheThemes_WhenMultipleThemesAreSpecified() { @Test void test_parseThemes_AddsTheTheme_WhenTheDefaultThemeIsSpecified() { - parser.parseThemes(context(), null, tokens("themes", "default")); + parser.parseThemes(context(), null, tokens("themes", "default"), false); assertEquals(1, workspace.getViews().getConfiguration().getThemes().length); assertEquals("https://static.structurizr.com/themes/default/theme.json", workspace.getViews().getConfiguration().getThemes()[0]); @@ -86,7 +86,7 @@ void test_parseThemes_AddsTheTheme_WhenTheDefaultThemeIsSpecified() { void test_parseTheme_ThrowsAnException_WhenTheThemeFileDoesNotExist() { File dslFile = new File("src/test/resources/themes/workspace.dsl"); try { - parser.parseTheme(context(), dslFile, tokens("theme", "my-theme.json")); + parser.parseTheme(context(), dslFile, tokens("theme", "my-theme.json"), false); fail(); } catch (Exception e) { assertTrue(e.getMessage().endsWith("/src/test/resources/themes/my-theme.json does not exist")); @@ -97,7 +97,7 @@ void test_parseTheme_ThrowsAnException_WhenTheThemeFileDoesNotExist() { void test_parseTheme_ThrowsAnException_WhenTheThemeFileIsADirectory() { File dslFile = new File("src/test/resources/workspace.dsl"); try { - parser.parseTheme(context(), dslFile, tokens("theme", "themes")); + parser.parseTheme(context(), dslFile, tokens("theme", "themes"), false); fail(); } catch (Exception e) { assertTrue(e.getMessage().endsWith("/src/test/resources/themes is not a file")); @@ -107,11 +107,22 @@ void test_parseTheme_ThrowsAnException_WhenTheThemeFileIsADirectory() { @Test void test_parseTheme_InlinesTheTheme_WhenAThemeFileIsSpecified() { File dslFile = new File("src/test/resources/themes/workspace.dsl"); - parser.parseTheme(context(), dslFile, tokens("theme", "theme.json")); + parser.parseTheme(context(), dslFile, tokens("theme", "theme.json"), false); assertEquals(0, workspace.getViews().getConfiguration().getThemes().length); assertEquals("#ff0000", workspace.getViews().getConfiguration().getStyles().getElementStyle("Tag").getBackground()); assertEquals("#00ff00", workspace.getViews().getConfiguration().getStyles().getRelationshipStyle("Tag").getColor()); } + @Test + void test_parseTheme_ThrowsAnException_WhenAThemeFileIsSpecifiedAndTheParserIsRunningInRestrictedMode() { + try { + File dslFile = new File("src/test/resources/themes/workspace.dsl"); + parser.parseTheme(context(), dslFile, tokens("theme", "theme.json"), true); + fail(); + } catch (Exception e) { + assertEquals("File-based themes are not supported when the DSL parser is running in restricted mode", e.getMessage()); + } + } + } \ No newline at end of file From aaddf56b1aad63ef4ad1b8d5a5917c6a53009f83 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 14 Sep 2025 11:10:12 +0100 Subject: [PATCH 670/717] Removes support for deprecated enterprise and location concepts. --- changelog.md | 4 +++- .../api/BackwardsCompatibilityTests.java | 17 --------------- .../java/com/structurizr/model/Model.java | 12 ----------- .../java/com/structurizr/model/Person.java | 16 -------------- .../com/structurizr/model/SoftwareSystem.java | 21 ------------------- .../export/AbstractDiagramExporter.java | 3 --- .../structurizr/export/dot/DOTExporter.java | 21 ------------------- .../mermaid/MermaidDiagramExporter.java | 15 ------------- .../export/plantuml/C4PlantUMLExporter.java | 13 ------------ .../plantuml/StructurizrPlantUMLExporter.java | 20 ------------------ .../WebSequenceDiagramsExporter.java | 8 ------- 11 files changed, 3 insertions(+), 147 deletions(-) diff --git a/changelog.md b/changelog.md index 068d5d942..281ac63aa 100644 --- a/changelog.md +++ b/changelog.md @@ -2,7 +2,8 @@ ## v5.0.0 (unreleased) -- structurizr-java: Fixes https://github.com/structurizr/java/issues/437 (Make ComponentFinder.run() not fail on empty Set). +- structurizr-core: Removes support for deprecated enterprise and location concepts. +- structurizr-component: Fixes https://github.com/structurizr/java/issues/437 (Make ComponentFinder.run() not fail on empty Set). - structurizr-dsl: Adds support for `iconPosition` on element styles (options are `Top`, `Bottom`, `Left`). - structurizr-dsl: Adds support for defining element and relationship styles for light and dark mode. - structurizr-dsl: Adds a `Bucket` shape. @@ -16,6 +17,7 @@ - structurizr-dsl: PlantUML, Mermaid, and Kroki image views can now be defined by an inline source block. - structurizr-dsl: Constants and variables are inherited when extending a DSL workspace. - structurizr-dsl: DSL source is only stored in the JSON workspace when the DSL is deemed as "portable" (i.e. no files, plugins, scripts). +- structurizr-export: Removes support for deprecated enterprise and location concepts. - structurizr-import: Adds support for `plantuml.inline`, `mermaid.inline`, and `kroki.inline` properties to inline the resulting PNG/SVG file into the workspace. - structurizr-inspection: Adds a way to disable inspections via a workspace property named `structurizr.inspection` (`false` to disable). - structurizr-inspection: Default inspector adds a summary of error/warning/info/ignore counts as workspace properties. diff --git a/structurizr-client/src/integrationTest/java/com/structurizr/api/BackwardsCompatibilityTests.java b/structurizr-client/src/integrationTest/java/com/structurizr/api/BackwardsCompatibilityTests.java index 6ae4cb020..ecdc97a12 100644 --- a/structurizr-client/src/integrationTest/java/com/structurizr/api/BackwardsCompatibilityTests.java +++ b/structurizr-client/src/integrationTest/java/com/structurizr/api/BackwardsCompatibilityTests.java @@ -22,23 +22,6 @@ void test() throws Exception { } } - @Test - void enterprise_and_location() throws Exception { - File file = new File(PATH_TO_WORKSPACE_FILES, "structurizr-36141-workspace.json"); - Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(file); - - assertEquals("Big Bank plc", workspace.getModel().getEnterprise().getName()); - assertEquals(Location.Internal, workspace.getModel().getPersonWithName("Back Office Staff").getLocation()); - assertEquals(Location.External, workspace.getModel().getPersonWithName("Personal Banking Customer").getLocation()); - - // make sure enterprise and location information is not lost when going to/from JSON - workspace = WorkspaceUtils.fromJson(WorkspaceUtils.toJson(workspace, false)); - - assertEquals("Big Bank plc", workspace.getModel().getEnterprise().getName()); - assertEquals(Location.Internal, workspace.getModel().getPersonWithName("Back Office Staff").getLocation()); - assertEquals(Location.External, workspace.getModel().getPersonWithName("Personal Banking Customer").getLocation()); - } - @Test void documentation() throws Exception { Workspace workspace = new Workspace("Name", "Description"); diff --git a/structurizr-core/src/main/java/com/structurizr/model/Model.java b/structurizr-core/src/main/java/com/structurizr/model/Model.java index 95964fa88..da7058f49 100644 --- a/structurizr-core/src/main/java/com/structurizr/model/Model.java +++ b/structurizr-core/src/main/java/com/structurizr/model/Model.java @@ -23,8 +23,6 @@ public final class Model implements PropertyHolder { private final Set relationships = new TreeSet<>(); private final Map relationshipsById = new HashMap<>(); - private Enterprise enterprise; - private Set people = new TreeSet<>(); private Set softwareSystems = new TreeSet<>(); private Set deploymentNodes = new TreeSet<>(); @@ -37,16 +35,6 @@ public final class Model implements PropertyHolder { Model() { } - @Deprecated - public Enterprise getEnterprise() { - return enterprise; - } - - @Deprecated - void setEnterprise(Enterprise enterprise) { - this.enterprise = enterprise; - } - /** * Creates a software system (with an unspecified location) and adds it to the model. * diff --git a/structurizr-core/src/main/java/com/structurizr/model/Person.java b/structurizr-core/src/main/java/com/structurizr/model/Person.java index dd9916897..84d445b1c 100644 --- a/structurizr-core/src/main/java/com/structurizr/model/Person.java +++ b/structurizr-core/src/main/java/com/structurizr/model/Person.java @@ -12,8 +12,6 @@ */ public final class Person extends StaticStructureElement { - private Location location = Location.Unspecified; - @Override @JsonIgnore public Element getParent() { @@ -23,20 +21,6 @@ public Element getParent() { Person() { } - @Deprecated - public Location getLocation() { - return location; - } - - @Deprecated - void setLocation(Location location) { - if (location != null) { - this.location = location; - } else { - this.location = Location.Unspecified; - } - } - @Override public String getCanonicalName() { return new CanonicalNameGenerator().generate(this); diff --git a/structurizr-core/src/main/java/com/structurizr/model/SoftwareSystem.java b/structurizr-core/src/main/java/com/structurizr/model/SoftwareSystem.java index 005cdda30..13d7a450c 100644 --- a/structurizr-core/src/main/java/com/structurizr/model/SoftwareSystem.java +++ b/structurizr-core/src/main/java/com/structurizr/model/SoftwareSystem.java @@ -16,8 +16,6 @@ */ public final class SoftwareSystem extends StaticStructureElement implements Documentable { - private Location location = Location.Unspecified; - private Set containers = new TreeSet<>(); private Documentation documentation = new Documentation(); @@ -36,25 +34,6 @@ public Element getParent() { SoftwareSystem() { } - @Deprecated - public Location getLocation() { - return location; - } - - /** - * Sets the location of this software system. - * - * @param location a Location instance - */ - @Deprecated - void setLocation(Location location) { - if (location != null) { - this.location = location; - } else { - this.location = Location.Unspecified; - } - } - void add(Container container) { containers.add(container); } diff --git a/structurizr-export/src/main/java/com/structurizr/export/AbstractDiagramExporter.java b/structurizr-export/src/main/java/com/structurizr/export/AbstractDiagramExporter.java index 56cef1709..f3d1839a1 100644 --- a/structurizr-export/src/main/java/com/structurizr/export/AbstractDiagramExporter.java +++ b/structurizr-export/src/main/java/com/structurizr/export/AbstractDiagramExporter.java @@ -602,9 +602,6 @@ protected void writeRelationships(ModelView view, IndentingWriter writer) { protected abstract void writeHeader(ModelView view, IndentingWriter writer); protected abstract void writeFooter(ModelView view, IndentingWriter writer); - protected abstract void startEnterpriseBoundary(ModelView view, String enterpriseName, IndentingWriter writer); - protected abstract void endEnterpriseBoundary(ModelView view, IndentingWriter writer); - protected abstract void startGroupBoundary(ModelView view, String group, IndentingWriter writer); protected abstract void endGroupBoundary(ModelView view, IndentingWriter writer); diff --git a/structurizr-export/src/main/java/com/structurizr/export/dot/DOTExporter.java b/structurizr-export/src/main/java/com/structurizr/export/dot/DOTExporter.java index b4671c4ca..26c7aa549 100644 --- a/structurizr-export/src/main/java/com/structurizr/export/dot/DOTExporter.java +++ b/structurizr-export/src/main/java/com/structurizr/export/dot/DOTExporter.java @@ -78,27 +78,6 @@ protected void writeFooter(ModelView view, IndentingWriter writer) { writer.writeLine("}"); } - @Override - protected void startEnterpriseBoundary(ModelView view, String enterpriseName, IndentingWriter writer) { - writer.writeLine("subgraph cluster_enterprise {"); - - writer.indent(); - writer.writeLine("margin=" + clusterInternalMargin); - writer.writeLine(String.format("label=<
    %s

    [Enterprise]>", enterpriseName)); - writer.writeLine("labelloc=b"); - writer.writeLine("color=\"#444444\""); - writer.writeLine("fontcolor=\"#444444\""); - writer.writeLine("fillcolor=\"#ffffff\""); - writer.writeLine(); - } - - @Override - protected void endEnterpriseBoundary(ModelView view, IndentingWriter writer) { - writer.outdent(); - writer.writeLine("}"); - writer.writeLine(); - } - @Override protected void startGroupBoundary(ModelView view, String group, IndentingWriter writer) { String color = "#cccccc"; diff --git a/structurizr-export/src/main/java/com/structurizr/export/mermaid/MermaidDiagramExporter.java b/structurizr-export/src/main/java/com/structurizr/export/mermaid/MermaidDiagramExporter.java index cb0569258..3082176a3 100644 --- a/structurizr-export/src/main/java/com/structurizr/export/mermaid/MermaidDiagramExporter.java +++ b/structurizr-export/src/main/java/com/structurizr/export/mermaid/MermaidDiagramExporter.java @@ -78,21 +78,6 @@ protected void writeFooter(ModelView view, IndentingWriter writer) { writer.outdent(); } - @Override - protected void startEnterpriseBoundary(ModelView view, String enterpriseName, IndentingWriter writer) { - writer.writeLine("subgraph enterprise [\"" + enterpriseName + "\"]"); - writer.indent(); - writer.writeLine("style enterprise fill:#ffffff,stroke:#444444,color:#444444"); - writer.writeLine(); - } - - @Override - protected void endEnterpriseBoundary(ModelView view, IndentingWriter writer) { - writer.outdent(); - writer.writeLine("end"); - writer.writeLine(); - } - @Override protected void startGroupBoundary(ModelView view, String group, IndentingWriter writer) { groupId++; diff --git a/structurizr-export/src/main/java/com/structurizr/export/plantuml/C4PlantUMLExporter.java b/structurizr-export/src/main/java/com/structurizr/export/plantuml/C4PlantUMLExporter.java index 7566c7b32..cdd26bafc 100644 --- a/structurizr-export/src/main/java/com/structurizr/export/plantuml/C4PlantUMLExporter.java +++ b/structurizr-export/src/main/java/com/structurizr/export/plantuml/C4PlantUMLExporter.java @@ -270,19 +270,6 @@ protected void writeFooter(ModelView view, IndentingWriter writer) { super.writeFooter(view, writer); } - @Override - protected void startEnterpriseBoundary(ModelView view, String enterpriseName, IndentingWriter writer) { - writer.writeLine(String.format("Enterprise_Boundary(enterprise, \"%s\") {", enterpriseName)); - writer.indent(); - } - - @Override - protected void endEnterpriseBoundary(ModelView view, IndentingWriter writer) { - writer.outdent(); - writer.writeLine("}"); - writer.writeLine(); - } - @Override protected void startGroupBoundary(ModelView view, String group, IndentingWriter writer) { groupId++; diff --git a/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java b/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java index 2c8d3c775..230099a1c 100644 --- a/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java +++ b/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java @@ -146,26 +146,6 @@ protected void writeHeader(ModelView view, IndentingWriter writer) { writer.writeLine(); } - @Override - protected void startEnterpriseBoundary(ModelView view, String enterpriseName, IndentingWriter writer) { - if (!renderAsSequenceDiagram(view)) { - writer.writeLine(String.format("rectangle \"%s\" <> {", enterpriseName)); - writer.indent(); - writer.writeLine("skinparam RectangleBorderColor<> #444444"); - writer.writeLine("skinparam RectangleFontColor<> #444444"); - writer.writeLine(); - } - } - - @Override - protected void endEnterpriseBoundary(ModelView view, IndentingWriter writer) { - if (!renderAsSequenceDiagram(view)) { - writer.outdent(); - writer.writeLine("}"); - writer.writeLine(); - } - } - @Override protected void startGroupBoundary(ModelView view, String group, IndentingWriter writer) { groupId++; diff --git a/structurizr-export/src/main/java/com/structurizr/export/websequencediagrams/WebSequenceDiagramsExporter.java b/structurizr-export/src/main/java/com/structurizr/export/websequencediagrams/WebSequenceDiagramsExporter.java index b731844a5..e05832792 100644 --- a/structurizr-export/src/main/java/com/structurizr/export/websequencediagrams/WebSequenceDiagramsExporter.java +++ b/structurizr-export/src/main/java/com/structurizr/export/websequencediagrams/WebSequenceDiagramsExporter.java @@ -83,14 +83,6 @@ protected void writeHeader(ModelView view, IndentingWriter writer) { protected void writeFooter(ModelView view, IndentingWriter writer) { } - @Override - protected void startEnterpriseBoundary(ModelView view, String enterpriseName, IndentingWriter writer) { - } - - @Override - protected void endEnterpriseBoundary(ModelView view, IndentingWriter writer) { - } - @Override protected void startGroupBoundary(ModelView view, String group, IndentingWriter writer) { } From f797cff1d93ec58ec9cead20840dbe4a9dd79dfa Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 16 Sep 2025 16:59:06 +0100 Subject: [PATCH 671/717] Adds support for an "image.inline" property. --- changelog.md | 2 +- .../dsl/ImageViewContentParser.java | 8 ++-- .../diagrams/image/ImageImporter.java | 44 +++++++++++++++++++ .../diagrams/image/ImageImporterTests.java | 37 ++++++++++++++++ 4 files changed, 86 insertions(+), 5 deletions(-) create mode 100644 structurizr-import/src/main/java/com/structurizr/importer/diagrams/image/ImageImporter.java create mode 100644 structurizr-import/src/test/java/com/structurizr/importer/diagrams/image/ImageImporterTests.java diff --git a/changelog.md b/changelog.md index 281ac63aa..8f40fbc6e 100644 --- a/changelog.md +++ b/changelog.md @@ -18,7 +18,7 @@ - structurizr-dsl: Constants and variables are inherited when extending a DSL workspace. - structurizr-dsl: DSL source is only stored in the JSON workspace when the DSL is deemed as "portable" (i.e. no files, plugins, scripts). - structurizr-export: Removes support for deprecated enterprise and location concepts. -- structurizr-import: Adds support for `plantuml.inline`, `mermaid.inline`, and `kroki.inline` properties to inline the resulting PNG/SVG file into the workspace. +- structurizr-import: Adds support for `plantuml.inline`, `mermaid.inline`, `kroki.inline`, and `image.inline` properties to inline the resulting PNG/SVG file into the workspace. - structurizr-inspection: Adds a way to disable inspections via a workspace property named `structurizr.inspection` (`false` to disable). - structurizr-inspection: Default inspector adds a summary of error/warning/info/ignore counts as workspace properties. - structurizr-inspection: Fixes `model.deploymentnode.technology` (it was checking the description property rather than technology). diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewContentParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewContentParser.java index 5c049f4f6..2264a16b8 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewContentParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewContentParser.java @@ -2,6 +2,7 @@ import com.structurizr.export.mermaid.MermaidDiagramExporter; import com.structurizr.export.plantuml.StructurizrPlantUMLExporter; +import com.structurizr.importer.diagrams.image.ImageImporter; import com.structurizr.importer.diagrams.kroki.KrokiImporter; import com.structurizr.importer.diagrams.mermaid.MermaidImporter; import com.structurizr.importer.diagrams.plantuml.PlantUMLImporter; @@ -12,6 +13,7 @@ import com.structurizr.view.View; import java.io.File; +import java.net.URL; final class ImageViewContentParser extends AbstractParser { @@ -193,15 +195,13 @@ void parseImage(ImageViewDslContext context, File dslFile, Tokens tokens) { try { if (Url.isUrl(source)) { - context.getView().setContent(source); - context.getView().setTitle(source.substring(source.lastIndexOf("/")+1)); + new ImageImporter().importDiagram(context.getView(), source); } else { if (!restricted) { File file = new File(dslFile.getParentFile(), source); if (file.exists()) { context.setDslPortable(false); - context.getView().setContent(ImageUtils.getImageAsDataUri(file)); - context.getView().setTitle(file.getName()); + new ImageImporter().importDiagram(context.getView(), file); } else { throw new RuntimeException("The file at " + file.getAbsolutePath() + " does not exist"); } diff --git a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/image/ImageImporter.java b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/image/ImageImporter.java new file mode 100644 index 000000000..dbdf10b60 --- /dev/null +++ b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/image/ImageImporter.java @@ -0,0 +1,44 @@ +package com.structurizr.importer.diagrams.image; + +import com.structurizr.importer.diagrams.AbstractDiagramImporter; +import com.structurizr.util.ImageUtils; +import com.structurizr.view.ImageView; + +import java.io.File; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; + +public class ImageImporter extends AbstractDiagramImporter { + + public static final String IMAGE_INLINE_PROPERTY = "image.inline"; + + public void importDiagram(ImageView view, File file) throws Exception { + view.setContent(ImageUtils.getImageAsDataUri(file)); + view.setContentType(ImageUtils.getContentType(file)); + view.setTitle(file.getName()); + } + + public void importDiagram(ImageView view, String url) throws Exception { + String inline = getViewOrViewSetProperty(view, IMAGE_INLINE_PROPERTY); + if ("true".equals(inline)) { + String imageFormat = ImageUtils.getContentType(url); + if (!imageFormat.equals(CONTENT_TYPE_IMAGE_PNG) && !imageFormat.equals(CONTENT_TYPE_IMAGE_SVG)) { + throw new IllegalArgumentException(String.format("Found %s - expected a format of %s or %s", imageFormat, PNG_FORMAT, SVG_FORMAT)); + } + + if (imageFormat.equals(CONTENT_TYPE_IMAGE_SVG)) { + view.setContent(ImageUtils.getSvgAsDataUri(new URL(url))); + } else { + view.setContent(ImageUtils.getPngAsDataUri(new URL(url))); + } + + view.setContentType(imageFormat); + } else { + view.setContent(url); + } + + view.setTitle(url.substring(url.lastIndexOf("/")+1)); + } + +} \ No newline at end of file diff --git a/structurizr-import/src/test/java/com/structurizr/importer/diagrams/image/ImageImporterTests.java b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/image/ImageImporterTests.java new file mode 100644 index 000000000..4a916217f --- /dev/null +++ b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/image/ImageImporterTests.java @@ -0,0 +1,37 @@ +package com.structurizr.importer.diagrams.image; + +import com.structurizr.Workspace; +import com.structurizr.view.ImageView; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class ImageImporterTests { + + @Test + public void importDiagram_Url() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + ImageView view = workspace.getViews().createImageView("key"); + + new ImageImporter().importDiagram(view, "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/alexa-for-business.png"); + assertEquals("https://static.structurizr.com/themes/amazon-web-services-2020.04.30/alexa-for-business.png", view.getContent()); + assertNull(view.getContentType()); + assertEquals("alexa-for-business.png", view.getTitle()); + } + + @Test + @Tag("IntegrationTest") + public void importDiagram_Url_Inline() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getViews().getConfiguration().addProperty(ImageImporter.IMAGE_INLINE_PROPERTY, "true"); + ImageView view = workspace.getViews().createImageView("key"); + + new ImageImporter().importDiagram(view, "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/alexa-for-business.png"); + assertEquals("data:image/png;base64,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", view.getContent()); + assertEquals("image/png", view.getContentType()); + assertEquals("alexa-for-business.png", view.getTitle()); + } + +} \ No newline at end of file From b3fb2c3ac78250828fcb61c6f6a22dac62d25ddb Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 16 Sep 2025 17:08:40 +0100 Subject: [PATCH 672/717] Removes support for deprecated enterprise and location concepts. --- .../autolayout/graphviz/DOTExporter.java | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/DOTExporter.java b/structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/DOTExporter.java index 013cb3446..a28303f2b 100644 --- a/structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/DOTExporter.java +++ b/structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/DOTExporter.java @@ -53,20 +53,6 @@ protected void writeFooter(ModelView view, IndentingWriter writer) { writer.writeLine("}"); } - @Override - protected void startEnterpriseBoundary(ModelView view, String enterpriseName, IndentingWriter writer) { - writer.writeLine("subgraph cluster_enterprise {"); - writer.indent(); - writer.writeLine("margin=" + CLUSTER_INTERNAL_MARGIN); - } - - @Override - protected void endEnterpriseBoundary(ModelView view, IndentingWriter writer) { - writer.outdent(); - writer.writeLine("}"); - writer.writeLine(); - } - @Override protected void startGroupBoundary(ModelView view, String group, IndentingWriter writer) { writer.writeLine("subgraph \"cluster_group_" + (groupId++) + "\" {"); From 53656dc1b01ed94ab1ffce178e4427da0eb74305 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 17 Sep 2025 08:12:42 +0100 Subject: [PATCH 673/717] Adds some constructor/method versions without description parameters. --- .../main/java/com/structurizr/Workspace.java | 9 ++ .../java/com/structurizr/view/ViewSet.java | 145 ++++++++++++++++++ 2 files changed, 154 insertions(+) diff --git a/structurizr-core/src/main/java/com/structurizr/Workspace.java b/structurizr-core/src/main/java/com/structurizr/Workspace.java index 46e2b7fdd..0b785e708 100644 --- a/structurizr-core/src/main/java/com/structurizr/Workspace.java +++ b/structurizr-core/src/main/java/com/structurizr/Workspace.java @@ -30,6 +30,15 @@ public final class Workspace extends AbstractWorkspace implements Documentable { Workspace() { } + /** + * Creates a new workspace. + * + * @param name the name of the workspace + */ + public Workspace(String name) { + this(name, ""); + } + /** * Creates a new workspace. * diff --git a/structurizr-core/src/main/java/com/structurizr/view/ViewSet.java b/structurizr-core/src/main/java/com/structurizr/view/ViewSet.java index ff395e202..f597f4027 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/ViewSet.java +++ b/structurizr-core/src/main/java/com/structurizr/view/ViewSet.java @@ -53,6 +53,18 @@ public final class ViewSet { this.model = model; } + /** + * Creates a custom view. + * + * @param key the key for the view (must be unique) + * @param title a title of the view + * @return a CustomView object + * @throws IllegalArgumentException if the key is not unique + */ + public CustomView createCustomView(String key, String title) { + return createCustomView(key, title, ""); + } + /** * Creates a custom view. * @@ -80,6 +92,17 @@ public CustomView createCustomView(String key, String title, String description) return view; } + /** + * Creates a system landscape view. + * + * @param key the key for the view (must be unique) + * @return a SystemLandscapeView object + * @throws IllegalArgumentException if the key is not unique + */ + public SystemLandscapeView createSystemLandscapeView(String key) { + return createSystemLandscapeView(key, ""); + } + /** * Creates a system landscape view. * @@ -107,6 +130,18 @@ public SystemLandscapeView createSystemLandscapeView(String key, String descript return view; } + /** + * Creates a system context view, where the scope of the view is the specified software system. + * + * @param softwareSystem the SoftwareSystem object representing the scope of the view + * @param key the key for the view (must be unique) + * @return a SystemContextView object + * @throws IllegalArgumentException if the software system is null or the key is not unique + */ + public SystemContextView createSystemContextView(SoftwareSystem softwareSystem, String key) { + return createSystemContextView(softwareSystem, key, ""); + } + /** * Creates a system context view, where the scope of the view is the specified software system. * @@ -135,6 +170,18 @@ public SystemContextView createSystemContextView(SoftwareSystem softwareSystem, return view; } + /** + * Creates a container view, where the scope of the view is the specified software system. + * + * @param softwareSystem the SoftwareSystem object representing the scope of the view + * @param key the key for the view (must be unique) + * @return a ContainerView object + * @throws IllegalArgumentException if the software system is null or the key is not unique + */ + public ContainerView createContainerView(SoftwareSystem softwareSystem, String key) { + return createContainerView(softwareSystem, key, ""); + } + /** * Creates a container view, where the scope of the view is the specified software system. * @@ -163,6 +210,18 @@ public ContainerView createContainerView(SoftwareSystem softwareSystem, String k return view; } + /** + * Creates a component view, where the scope of the view is the specified container. + * + * @param container the Container object representing the scope of the view + * @param key the key for the view (must be unique) + * @return a ContainerView object + * @throws IllegalArgumentException if the container is null or the key is not unique + */ + public ComponentView createComponentView(Container container, String key) { + return createComponentView(container, key, ""); + } + /** * Creates a component view, where the scope of the view is the specified container. * @@ -191,6 +250,17 @@ public ComponentView createComponentView(Container container, String key, String return view; } + /** + * Creates a dynamic view. + * + * @param key the key for the view (must be unique) + * @return a DynamicView object + * @throws IllegalArgumentException if the key is not unique + */ + public DynamicView createDynamicView(String key) { + return createDynamicView(key, ""); + } + /** * Creates a dynamic view. * @@ -217,6 +287,25 @@ public DynamicView createDynamicView(String key, String description) { return view; } + /** + * Creates a dynamic view, where the scope is the specified software system. The following + * elements can be added to the resulting view: + * + *
      + *
    • People
    • + *
    • Software systems
    • + *
    • Containers that reside inside the specified software system
    • + *
    + * + * @param softwareSystem the SoftwareSystem object representing the scope of the view + * @param key the key for the view (must be unique) + * @return a DynamicView object + * @throws IllegalArgumentException if the software system is null or the key is not unique + */ + public DynamicView createDynamicView(SoftwareSystem softwareSystem, String key) { + return createDynamicView(softwareSystem, key, ""); + } + /** * Creates a dynamic view, where the scope is the specified software system. The following * elements can be added to the resulting view: @@ -252,6 +341,26 @@ public DynamicView createDynamicView(SoftwareSystem softwareSystem, String key, return view; } + /** + * Creates a dynamic view, where the scope is the specified container. The following + * elements can be added to the resulting view: + * + *
      + *
    • People
    • + *
    • Software systems
    • + *
    • Containers with the same parent software system as the specified container
    • + *
    • Components within the specified container
    • + *
    + * + * @param container the Container object representing the scope of the view + * @param key the key for the view (must be unique) + * @return a DynamicView object + * @throws IllegalArgumentException if the container is null or the key is not unique + */ + public DynamicView createDynamicView(Container container, String key) { + return createDynamicView(container, key, ""); + } + /** * Creates a dynamic view, where the scope is the specified container. The following * elements can be added to the resulting view: @@ -288,6 +397,17 @@ public DynamicView createDynamicView(Container container, String key, String des return view; } + /** + * Creates a deployment view. + * + * @param key the key for the deployment view (must be unique) + * @return a DeploymentView object + * @throws IllegalArgumentException if the key is not unique + */ + public DeploymentView createDeploymentView(String key) { + return createDeploymentView(key, ""); + } + /** * Creates a deployment view. * @@ -314,6 +434,18 @@ public DeploymentView createDeploymentView(String key, String description) { return view; } + /** + * Creates a deployment view, where the scope of the view is the specified software system. + * + * @param softwareSystem the SoftwareSystem object representing the scope of the view + * @param key the key for the deployment view (must be unique) + * @return a DeploymentView object + * @throws IllegalArgumentException if the software system is null or the key is not unique + */ + public DeploymentView createDeploymentView(SoftwareSystem softwareSystem, String key) { + return createDeploymentView(softwareSystem, key, ""); + } + /** * Creates a deployment view, where the scope of the view is the specified software system. * @@ -342,6 +474,19 @@ public DeploymentView createDeploymentView(SoftwareSystem softwareSystem, String return view; } + /** + * Creates a FilteredView on top of an existing static view. + * + * @param view the static view to base the FilteredView upon + * @param key the key for the filtered view (must be unique) + * @param mode whether to Include or Exclude elements/relationships based upon their tag + * @param tags the tags to include or exclude + * @return a FilteredView object + */ + public FilteredView createFilteredView(StaticView view, String key, FilterMode mode, String... tags) { + return createFilteredView(view, key, "", mode, tags); + } + /** * Creates a FilteredView on top of an existing static view. * From b40cdfe68e8734db65935855968dad37b989f1a0 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 17 Sep 2025 08:43:25 +0100 Subject: [PATCH 674/717] . --- .../src/test/java/com/structurizr/view/ViewSetTests.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/structurizr-core/src/test/java/com/structurizr/view/ViewSetTests.java b/structurizr-core/src/test/java/com/structurizr/view/ViewSetTests.java index 675345cf7..94591bb7e 100644 --- a/structurizr-core/src/test/java/com/structurizr/view/ViewSetTests.java +++ b/structurizr-core/src/test/java/com/structurizr/view/ViewSetTests.java @@ -273,7 +273,7 @@ void createComponentView() { @Test void createDynamicView_GeneratesAKey_WhenANullKeyIsSpecified() { Workspace workspace = new Workspace("Name", "Description"); - DynamicView view = workspace.getViews().createDynamicView(null, "Description"); + DynamicView view = workspace.getViews().createDynamicView(null); assertEquals("Dynamic-001", view.getKey()); assertTrue(view.isGeneratedKey()); } @@ -397,7 +397,7 @@ void createDynamicViewForContainer() { @Test void createDeploymentView_GeneratesAKey_WhenANullKeyIsSpecified() { Workspace workspace = new Workspace("Name", "Description"); - DeploymentView view = workspace.getViews().createDeploymentView(null, "Description"); + DeploymentView view = workspace.getViews().createDeploymentView(null); assertEquals("Deployment-001", view.getKey()); assertTrue(view.isGeneratedKey()); } From bb2428a74c21c6e944aec226398a1bc47917dc63 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 17 Sep 2025 09:06:10 +0100 Subject: [PATCH 675/717] *PlantUML exporters: replaces skinparams with styles + adds support for dark mode exports. --- changelog.md | 2 + .../autolayout/graphviz/DOTExporterTests.java | 357 +- .../GraphvizAutomaticLayoutTests.java | 35 +- .../com/structurizr/view/AbstractStyle.java | 56 +- .../com/structurizr/view/ElementStyle.java | 29 +- .../structurizr/view/RelationshipStyle.java | 22 +- .../java/com/structurizr/view/Styles.java | 141 +- .../com/structurizr/view/StylesTests.java | 98 +- .../java/com/structurizr/dsl/DslTests.java | 2 +- .../dsl/ImageViewContentParserTests.java | 5 +- .../src/test/resources/dsl/include-url.dsl | 2 +- .../export/AbstractDiagramExporter.java | 193 +- .../structurizr/export/IndentingWriter.java | 9 +- .../plantuml/AbstractPlantUMLExporter.java | 92 +- .../export/plantuml/C4PlantUMLExporter.java | 83 +- .../plantuml/PlantUMLBoundaryStyle.java | 81 + .../export/plantuml/PlantUMLElementStyle.java | 107 + .../export/plantuml/PlantUMLGroupStyle.java | 81 + .../export/plantuml/PlantUMLLegendStyle.java | 44 + .../plantuml/PlantUMLRelationshipStyle.java | 71 + .../export/plantuml/PlantUMLRootStyle.java | 52 + .../export/plantuml/PlantUMLStyle.java | 26 + .../plantuml/StructurizrPlantUMLExporter.java | 582 +-- .../export/dot/36141-Components.dot | 43 - .../export/dot/36141-Containers.dot | 37 - .../dot/36141-DevelopmentDeployment.dot | 97 - .../export/dot/36141-LiveDeployment.dot | 152 - .../structurizr/export/dot/36141-SignIn.dot | 29 - .../export/dot/36141-SystemContext.dot | 28 - .../export/dot/36141-SystemLandscape.dot | 36 - .../dot/54915-AmazonWebServicesDeployment.dot | 75 - .../export/dot/DOTDiagramExporterTests.java | 1076 ++++- .../export/dot/groups-Components.dot | 35 - .../export/dot/groups-Containers.dot | 35 - .../export/dot/groups-SystemLandscape.dot | 49 - .../structurizr/export/dot/nested-groups.dot | 69 - .../export/ilograph/36141.ilograph | 616 --- .../export/ilograph/54915.ilograph | 127 - .../ilograph/IlographExporterTests.java | 792 +++- .../54915-AmazonWebServicesDeployment.mmd | 48 - .../mermaid/MermaidDiagramExporterTests.java | 477 ++- .../export/mermaid/groups-Components.mmd | 26 - .../export/mermaid/groups-Containers.mmd | 26 - .../export/mermaid/groups-SystemLandscape.mmd | 34 - .../export/mermaid/nested-groups.mmd | 43 - .../C4PlantUMLDiagramExporterTests.java | 1600 ++++++-- .../plantuml/PlantUMLBoundaryStyleTests.java | 29 + .../plantuml/PlantUMLElementStyleTests.java | 32 + .../plantuml/PlantUMLGroupStyleTests.java | 31 + .../plantuml/PlantUMLLegendStyleTests.java | 23 + .../PlantUMLRelationshipStyleTests.java | 58 + .../plantuml/PlantUMLRootStyleTests.java | 53 + ...ructurizrPlantUMLDiagramExporterTests.java | 3556 +++++++++++++---- .../plantuml/c4plantuml/36141-Components.puml | 52 - .../plantuml/c4plantuml/36141-Containers.puml | 46 - .../36141-DevelopmentDeployment.puml | 55 - .../c4plantuml/36141-LiveDeployment.puml | 77 - .../c4plantuml/36141-SignIn-sequence.puml | 26 - .../plantuml/c4plantuml/36141-SignIn.puml | 36 - .../c4plantuml/36141-SystemContext.puml | 31 - .../c4plantuml/36141-SystemLandscape.puml | 40 - ...-AmazonWebServicesDeployment-WithTags.puml | 52 - ...azonWebServicesDeployment-WithoutTags.puml | 39 - .../plantuml/c4plantuml/group-styles-1.puml | 28 - .../plantuml/c4plantuml/group-styles-2.puml | 28 - .../c4plantuml/groups-Components.puml | 26 - .../c4plantuml/groups-Containers.puml | 26 - .../c4plantuml/groups-SystemLandscape.puml | 32 - .../nested-groups-with-dot-separator.puml | 26 - .../plantuml/c4plantuml/nested-groups.puml | 38 - .../printProperties-containerView.puml | 28 - .../printProperties-deploymentView.puml | 21 - .../structurizr/36141-Components.puml | 118 - .../structurizr/36141-Containers.puml | 94 - .../36141-DevelopmentDeployment.puml | 130 - .../structurizr/36141-LiveDeployment.puml | 192 - .../structurizr/36141-SignIn-sequence.puml | 49 - .../plantuml/structurizr/36141-SignIn.puml | 62 - .../structurizr/36141-SystemContext.puml | 59 - .../structurizr/36141-SystemLandscape.puml | 85 - ...15-AmazonWebServicesDeployment-Legend.puml | 102 - .../54915-AmazonWebServicesDeployment.puml | 113 - ...onent-view-with-external-components-1.puml | 56 - ...onent-view-with-external-components-2.puml | 62 - ...mic-view-container-scoped-with-groups.puml | 62 - ...ew-software-system-scoped-with-groups.puml | 62 - .../dynamic-view-unscoped-with-groups.puml | 46 - ...namic-view-with-external-components-1.puml | 56 - ...namic-view-with-external-components-2.puml | 62 - .../plantuml/structurizr/group-styles-1.puml | 60 - .../plantuml/structurizr/group-styles-2.puml | 60 - .../structurizr/groups-Components.puml | 58 - .../structurizr/groups-Containers.puml | 58 - .../structurizr/groups-SystemLandscape.puml | 72 - .../plantuml/structurizr/nested-groups.puml | 88 - .../websequencediagrams/36141-SignIn.wsd | 13 - .../WebSequenceDiagramsExporterTests.java | 33 +- .../test/resources/amazon-web-services.dsl | 83 + .../test/resources/amazon-web-services.json | 257 ++ ...36141-workspace.json => big-bank-plc.json} | 0 .../structurizr-54915-workspace.json | 353 -- .../diagrams/plantuml/PlantUMLImporter.java | 21 +- .../plantuml/PlantUMLImporterTests.java | 3 +- 103 files changed, 8143 insertions(+), 6635 deletions(-) create mode 100644 structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLBoundaryStyle.java create mode 100644 structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLElementStyle.java create mode 100644 structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLGroupStyle.java create mode 100644 structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLLegendStyle.java create mode 100644 structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLRelationshipStyle.java create mode 100644 structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLRootStyle.java create mode 100644 structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLStyle.java delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/dot/36141-Components.dot delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/dot/36141-Containers.dot delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/dot/36141-DevelopmentDeployment.dot delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/dot/36141-LiveDeployment.dot delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/dot/36141-SignIn.dot delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/dot/36141-SystemContext.dot delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/dot/36141-SystemLandscape.dot delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/dot/54915-AmazonWebServicesDeployment.dot delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/dot/groups-Components.dot delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/dot/groups-Containers.dot delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/dot/groups-SystemLandscape.dot delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/dot/nested-groups.dot delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/ilograph/36141.ilograph delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/ilograph/54915.ilograph delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/mermaid/54915-AmazonWebServicesDeployment.mmd delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-Components.mmd delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-Containers.mmd delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-SystemLandscape.mmd delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/mermaid/nested-groups.mmd create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/PlantUMLBoundaryStyleTests.java create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/PlantUMLElementStyleTests.java create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/PlantUMLGroupStyleTests.java create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/PlantUMLLegendStyleTests.java create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/PlantUMLRelationshipStyleTests.java create mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/PlantUMLRootStyleTests.java delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Components.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Containers.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-DevelopmentDeployment.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-LiveDeployment.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn-sequence.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemContext.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemLandscape.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/54915-AmazonWebServicesDeployment-WithTags.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/54915-AmazonWebServicesDeployment-WithoutTags.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/group-styles-1.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/group-styles-2.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Components.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Containers.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-SystemLandscape.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/nested-groups-with-dot-separator.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/nested-groups.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/printProperties-containerView.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/printProperties-deploymentView.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-Components.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-Containers.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-DevelopmentDeployment.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-LiveDeployment.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SignIn-sequence.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SignIn.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemContext.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemLandscape.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/54915-AmazonWebServicesDeployment-Legend.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/54915-AmazonWebServicesDeployment.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/component-view-with-external-components-1.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/component-view-with-external-components-2.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-container-scoped-with-groups.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-software-system-scoped-with-groups.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-unscoped-with-groups.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-with-external-components-1.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-with-external-components-2.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/group-styles-1.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/group-styles-2.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-Components.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-Containers.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-SystemLandscape.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/nested-groups.puml delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/websequencediagrams/36141-SignIn.wsd create mode 100644 structurizr-export/src/test/resources/amazon-web-services.dsl create mode 100644 structurizr-export/src/test/resources/amazon-web-services.json rename structurizr-export/src/test/resources/{structurizr-36141-workspace.json => big-bank-plc.json} (100%) delete mode 100644 structurizr-export/src/test/resources/structurizr-54915-workspace.json diff --git a/changelog.md b/changelog.md index 8f40fbc6e..56f85766d 100644 --- a/changelog.md +++ b/changelog.md @@ -18,6 +18,8 @@ - structurizr-dsl: Constants and variables are inherited when extending a DSL workspace. - structurizr-dsl: DSL source is only stored in the JSON workspace when the DSL is deemed as "portable" (i.e. no files, plugins, scripts). - structurizr-export: Removes support for deprecated enterprise and location concepts. +- structurizr-export: PlantUML exporters - replaces skinparams with styles. +- structurizr-export: PlantUML exporters - adds support for dark mode exports. - structurizr-import: Adds support for `plantuml.inline`, `mermaid.inline`, `kroki.inline`, and `image.inline` properties to inline the resulting PNG/SVG file into the workspace. - structurizr-inspection: Adds a way to disable inspections via a workspace property named `structurizr.inspection` (`false` to disable). - structurizr-inspection: Default inspector adds a summary of error/warning/info/ignore counts as workspace properties. diff --git a/structurizr-autolayout/src/test/java/com/structurizr/autolayout/graphviz/DOTExporterTests.java b/structurizr-autolayout/src/test/java/com/structurizr/autolayout/graphviz/DOTExporterTests.java index fc58220ce..96bdabf8f 100644 --- a/structurizr-autolayout/src/test/java/com/structurizr/autolayout/graphviz/DOTExporterTests.java +++ b/structurizr-autolayout/src/test/java/com/structurizr/autolayout/graphviz/DOTExporterTests.java @@ -31,17 +31,19 @@ public void test_writeCustomView() { Diagram diagram = exporter.export(view); String content = diagram.getDefinition(); - assertEquals("digraph {\n" + - " compound=true\n" + - " graph [splines=polyline,rankdir=TB,ranksep=1.0,nodesep=1.0,fontsize=5]\n" + - " node [shape=box,fontsize=5]\n" + - " edge []\n" + - "\n" + - " 1 [width=1.500000,height=1.000000,fixedsize=true,id=1,label=\"1: Box 1\"]\n" + - " 2 [width=1.500000,height=1.000000,fixedsize=true,id=2,label=\"2: Box 2\"]\n" + - "\n" + - " 1 -> 2 [id=3]\n" + - "}", content); + assertEquals(""" + digraph { + compound=true + graph [splines=polyline,rankdir=TB,ranksep=1.0,nodesep=1.0,fontsize=5] + node [shape=box,fontsize=5] + edge [] + + 1 [width=1.500000,height=1.000000,fixedsize=true,id=1,label="1: Box 1"] + 2 [width=1.500000,height=1.000000,fixedsize=true,id=2,label="2: Box 2"] + + 1 -> 2 [id=3] + + }""", content); } @Test @@ -60,18 +62,20 @@ public void test_writeSystemLandscapeView() { Diagram diagram = exporter.export(view); String content = diagram.getDefinition(); - assertEquals("digraph {\n" + - " compound=true\n" + - " graph [splines=polyline,rankdir=TB,ranksep=1.0,nodesep=1.0,fontsize=5]\n" + - " node [shape=box,fontsize=5]\n" + - " edge []\n" + - "\n" + - " 1 [width=1.500000,height=1.000000,fixedsize=true,id=1,label=\"1: Box\"]\n" + - " 2 [width=1.500000,height=1.000000,fixedsize=true,id=2,label=\"2: User\"]\n" + - " 3 [width=1.500000,height=1.000000,fixedsize=true,id=3,label=\"3: Software System\"]\n" + - "\n" + - " 2 -> 3 [id=4]\n" + - "}", content); + assertEquals(""" + digraph { + compound=true + graph [splines=polyline,rankdir=TB,ranksep=1.0,nodesep=1.0,fontsize=5] + node [shape=box,fontsize=5] + edge [] + + 1 [width=1.500000,height=1.000000,fixedsize=true,id=1,label="1: Box"] + 2 [width=1.500000,height=1.000000,fixedsize=true,id=2,label="2: User"] + 3 [width=1.500000,height=1.000000,fixedsize=true,id=3,label="3: Software System"] + + 2 -> 3 [id=4] + + }""", content); } @Test @@ -92,26 +96,28 @@ public void test_writeSystemLandscapeViewWithGroupedElements() throws Exception Diagram diagram = exporter.export(view); String content = diagram.getDefinition(); - assertEquals("digraph {\n" + - " compound=true\n" + - " graph [splines=polyline,rankdir=TB,ranksep=1.0,nodesep=1.0,fontsize=5]\n" + - " node [shape=box,fontsize=5]\n" + - " edge []\n" + - "\n" + - " subgraph \"cluster_group_1\" {\n" + - " margin=25\n" + - " 2 [width=1.500000,height=1.000000,fixedsize=true,id=2,label=\"2: User\"]\n" + - " }\n" + - "\n" + - " subgraph \"cluster_group_2\" {\n" + - " margin=25\n" + - " 3 [width=1.500000,height=1.000000,fixedsize=true,id=3,label=\"3: Software System\"]\n" + - " }\n" + - "\n" + - " 1 [width=1.500000,height=1.000000,fixedsize=true,id=1,label=\"1: Box\"]\n" + - "\n" + - " 2 -> 3 [id=4]\n" + - "}", content); + assertEquals(""" + digraph { + compound=true + graph [splines=polyline,rankdir=TB,ranksep=1.0,nodesep=1.0,fontsize=5] + node [shape=box,fontsize=5] + edge [] + + subgraph "cluster_group_1" { + margin=25 + 2 [width=1.500000,height=1.000000,fixedsize=true,id=2,label="2: User"] + } + + subgraph "cluster_group_2" { + margin=25 + 3 [width=1.500000,height=1.000000,fixedsize=true,id=3,label="3: Software System"] + } + + 1 [width=1.500000,height=1.000000,fixedsize=true,id=1,label="1: Box"] + + 2 -> 3 [id=4] + + }""", content); } @Test @@ -194,18 +200,20 @@ public void test_writeSystemLandscapeViewInGermanLocale() throws Exception { Diagram diagram = exporter.export(view); String content = diagram.getDefinition(); - assertEquals("digraph {\n" + - " compound=true\n" + - " graph [splines=polyline,rankdir=TB,ranksep=1.0,nodesep=1.0,fontsize=5]\n" + - " node [shape=box,fontsize=5]\n" + - " edge []\n" + - "\n" + - " 1 [width=1.500000,height=1.000000,fixedsize=true,id=1,label=\"1: Box\"]\n" + - " 2 [width=1.500000,height=1.000000,fixedsize=true,id=2,label=\"2: User\"]\n" + - " 3 [width=1.500000,height=1.000000,fixedsize=true,id=3,label=\"3: Software System\"]\n" + - "\n" + - " 2 -> 3 [id=4]\n" + - "}", content); + assertEquals(""" + digraph { + compound=true + graph [splines=polyline,rankdir=TB,ranksep=1.0,nodesep=1.0,fontsize=5] + node [shape=box,fontsize=5] + edge [] + + 1 [width=1.500000,height=1.000000,fixedsize=true,id=1,label="1: Box"] + 2 [width=1.500000,height=1.000000,fixedsize=true,id=2,label="2: User"] + 3 [width=1.500000,height=1.000000,fixedsize=true,id=3,label="3: Software System"] + + 2 -> 3 [id=4] + + }""", content); } @Test @@ -224,18 +232,20 @@ public void test_writeSystemContextView() throws Exception { Diagram diagram = exporter.export(view); String content = diagram.getDefinition(); - assertEquals("digraph {\n" + - " compound=true\n" + - " graph [splines=polyline,rankdir=TB,ranksep=1.0,nodesep=1.0,fontsize=5]\n" + - " node [shape=box,fontsize=5]\n" + - " edge []\n" + - "\n" + - " 1 [width=1.500000,height=1.000000,fixedsize=true,id=1,label=\"1: Box\"]\n" + - " 2 [width=1.500000,height=1.000000,fixedsize=true,id=2,label=\"2: User\"]\n" + - " 3 [width=1.500000,height=1.000000,fixedsize=true,id=3,label=\"3: Software System\"]\n" + - "\n" + - " 2 -> 3 [id=4]\n" + - "}", content); + assertEquals(""" + digraph { + compound=true + graph [splines=polyline,rankdir=TB,ranksep=1.0,nodesep=1.0,fontsize=5] + node [shape=box,fontsize=5] + edge [] + + 1 [width=1.500000,height=1.000000,fixedsize=true,id=1,label="1: Box"] + 2 [width=1.500000,height=1.000000,fixedsize=true,id=2,label="2: User"] + 3 [width=1.500000,height=1.000000,fixedsize=true,id=3,label="3: Software System"] + + 2 -> 3 [id=4] + + }""", content); } @@ -257,26 +267,28 @@ public void test_writeSystemContextViewWithGroupedElements() throws Exception { Diagram diagram = exporter.export(view); String content = diagram.getDefinition(); - assertEquals("digraph {\n" + - " compound=true\n" + - " graph [splines=polyline,rankdir=TB,ranksep=1.0,nodesep=1.0,fontsize=5]\n" + - " node [shape=box,fontsize=5]\n" + - " edge []\n" + - "\n" + - " subgraph \"cluster_group_1\" {\n" + - " margin=25\n" + - " 2 [width=1.500000,height=1.000000,fixedsize=true,id=2,label=\"2: User\"]\n" + - " }\n" + - "\n" + - " subgraph \"cluster_group_2\" {\n" + - " margin=25\n" + - " 3 [width=1.500000,height=1.000000,fixedsize=true,id=3,label=\"3: Software System\"]\n" + - " }\n" + - "\n" + - " 1 [width=1.500000,height=1.000000,fixedsize=true,id=1,label=\"1: Box\"]\n" + - "\n" + - " 2 -> 3 [id=4]\n" + - "}", content); + assertEquals(""" + digraph { + compound=true + graph [splines=polyline,rankdir=TB,ranksep=1.0,nodesep=1.0,fontsize=5] + node [shape=box,fontsize=5] + edge [] + + subgraph "cluster_group_1" { + margin=25 + 2 [width=1.500000,height=1.000000,fixedsize=true,id=2,label="2: User"] + } + + subgraph "cluster_group_2" { + margin=25 + 3 [width=1.500000,height=1.000000,fixedsize=true,id=3,label="3: Software System"] + } + + 1 [width=1.500000,height=1.000000,fixedsize=true,id=1,label="1: Box"] + + 2 -> 3 [id=4] + + }""", content); } @Test @@ -301,32 +313,34 @@ public void test_writeContainerViewWithGroupedElementsInASingleSoftwareSystem() Diagram diagram = exporter.export(view); String content = diagram.getDefinition(); - assertEquals("digraph {\n" + - " compound=true\n" + - " graph [splines=polyline,rankdir=TB,ranksep=1.0,nodesep=1.0,fontsize=5]\n" + - " node [shape=box,fontsize=5]\n" + - " edge []\n" + - "\n" + - " 1 [width=1.500000,height=1.000000,fixedsize=true,id=1,label=\"1: Box\"]\n" + - "\n" + - " subgraph cluster_2 {\n" + - " margin=25\n" + - " subgraph \"cluster_group_1\" {\n" + - " margin=25\n" + - " 3 [width=1.500000,height=1.000000,fixedsize=true,id=3,label=\"3: Container 1\"]\n" + - " }\n" + - "\n" + - " subgraph \"cluster_group_2\" {\n" + - " margin=25\n" + - " 4 [width=1.500000,height=1.000000,fixedsize=true,id=4,label=\"4: Container 2\"]\n" + - " }\n" + - "\n" + - " 5 [width=1.500000,height=1.000000,fixedsize=true,id=5,label=\"5: Container 3\"]\n" + - " }\n" + - "\n" + - " 3 -> 4 [id=6]\n" + - " 4 -> 5 [id=7]\n" + - "}", content); + assertEquals(""" + digraph { + compound=true + graph [splines=polyline,rankdir=TB,ranksep=1.0,nodesep=1.0,fontsize=5] + node [shape=box,fontsize=5] + edge [] + + 1 [width=1.500000,height=1.000000,fixedsize=true,id=1,label="1: Box"] + + subgraph cluster_2 { + margin=25 + subgraph "cluster_group_1" { + margin=25 + 3 [width=1.500000,height=1.000000,fixedsize=true,id=3,label="3: Container 1"] + } + + subgraph "cluster_group_2" { + margin=25 + 4 [width=1.500000,height=1.000000,fixedsize=true,id=4,label="4: Container 2"] + } + + 5 [width=1.500000,height=1.000000,fixedsize=true,id=5,label="5: Container 3"] + } + + 3 -> 4 [id=6] + 4 -> 5 [id=7] + + }""", content); } @Test @@ -351,32 +365,34 @@ public void test_writeContainerViewWithGroupedElementsInMultipleSoftwareSystems( Diagram diagram = exporter.export(view); String content = diagram.getDefinition(); - assertEquals("digraph {\n" + - " compound=true\n" + - " graph [splines=polyline,rankdir=TB,ranksep=1.0,nodesep=1.0,fontsize=5]\n" + - " node [shape=box,fontsize=5]\n" + - " edge []\n" + - "\n" + - " subgraph cluster_1 {\n" + - " margin=25\n" + - " subgraph \"cluster_group_1\" {\n" + - " margin=25\n" + - " 2 [width=1.500000,height=1.000000,fixedsize=true,id=2,label=\"2: Container 1\"]\n" + - " }\n" + - "\n" + - " }\n" + - "\n" + - " subgraph cluster_3 {\n" + - " margin=25\n" + - " subgraph \"cluster_group_2\" {\n" + - " margin=25\n" + - " 4 [width=1.500000,height=1.000000,fixedsize=true,id=4,label=\"4: Container 2\"]\n" + - " }\n" + - "\n" + - " }\n" + - "\n" + - " 2 -> 4 [id=5]\n" + - "}", content); + assertEquals(""" + digraph { + compound=true + graph [splines=polyline,rankdir=TB,ranksep=1.0,nodesep=1.0,fontsize=5] + node [shape=box,fontsize=5] + edge [] + + subgraph cluster_1 { + margin=25 + subgraph "cluster_group_1" { + margin=25 + 2 [width=1.500000,height=1.000000,fixedsize=true,id=2,label="2: Container 1"] + } + + } + + subgraph cluster_3 { + margin=25 + subgraph "cluster_group_2" { + margin=25 + 4 [width=1.500000,height=1.000000,fixedsize=true,id=4,label="4: Container 2"] + } + + } + + 2 -> 4 [id=5] + + }""", content); } @Test @@ -402,32 +418,34 @@ public void test_writeComponentViewWithGroupedElements() throws Exception { Diagram diagram = exporter.export(view); String content = diagram.getDefinition(); - assertEquals("digraph {\n" + - " compound=true\n" + - " graph [splines=polyline,rankdir=TB,ranksep=1.0,nodesep=1.0,fontsize=5]\n" + - " node [shape=box,fontsize=5]\n" + - " edge []\n" + - "\n" + - " 1 [width=1.500000,height=1.000000,fixedsize=true,id=1,label=\"1: Box\"]\n" + - "\n" + - " subgraph cluster_3 {\n" + - " margin=25\n" + - " subgraph \"cluster_group_1\" {\n" + - " margin=25\n" + - " 5 [width=1.500000,height=1.000000,fixedsize=true,id=5,label=\"5: Component 2\"]\n" + - " }\n" + - "\n" + - " subgraph \"cluster_group_2\" {\n" + - " margin=25\n" + - " 6 [width=1.500000,height=1.000000,fixedsize=true,id=6,label=\"6: Component 3\"]\n" + - " }\n" + - "\n" + - " 4 [width=1.500000,height=1.000000,fixedsize=true,id=4,label=\"4: Component 1\"]\n" + - " }\n" + - "\n" + - " 4 -> 5 [id=7]\n" + - " 5 -> 6 [id=8]\n" + - "}", content); + assertEquals(""" + digraph { + compound=true + graph [splines=polyline,rankdir=TB,ranksep=1.0,nodesep=1.0,fontsize=5] + node [shape=box,fontsize=5] + edge [] + + 1 [width=1.500000,height=1.000000,fixedsize=true,id=1,label="1: Box"] + + subgraph cluster_3 { + margin=25 + subgraph "cluster_group_1" { + margin=25 + 5 [width=1.500000,height=1.000000,fixedsize=true,id=5,label="5: Component 2"] + } + + subgraph "cluster_group_2" { + margin=25 + 6 [width=1.500000,height=1.000000,fixedsize=true,id=6,label="6: Component 3"] + } + + 4 [width=1.500000,height=1.000000,fixedsize=true,id=4,label="4: Component 1"] + } + + 4 -> 5 [id=7] + 5 -> 6 [id=8] + + }""", content); } @Test @@ -486,9 +504,7 @@ public void test_AmazonWebServicesExample() throws Exception { DOTExporter exporter = new DOTExporter(RankDirection.LeftRight, 300, 300); Diagram diagram = exporter.export(workspace.getViews().getDeploymentViews().iterator().next()); - String content = diagram.getDefinition(); - - String expectedResult = """ + assertEquals(""" digraph { compound=true graph [splines=polyline,rankdir=LR,ranksep=1.0,nodesep=1.0,fontsize=5] @@ -526,9 +542,8 @@ public void test_AmazonWebServicesExample() throws Exception { 11 -> 14 [id=15] 7 -> 8 [id=16] 8 -> 11 [id=17] - }"""; - - assertEquals(expectedResult, content); + + }""", diagram.getDefinition()); } } \ No newline at end of file diff --git a/structurizr-autolayout/src/test/java/com/structurizr/autolayout/graphviz/GraphvizAutomaticLayoutTests.java b/structurizr-autolayout/src/test/java/com/structurizr/autolayout/graphviz/GraphvizAutomaticLayoutTests.java index c499674a4..710553436 100644 --- a/structurizr-autolayout/src/test/java/com/structurizr/autolayout/graphviz/GraphvizAutomaticLayoutTests.java +++ b/structurizr-autolayout/src/test/java/com/structurizr/autolayout/graphviz/GraphvizAutomaticLayoutTests.java @@ -8,6 +8,7 @@ import com.structurizr.view.AutomaticLayout; import com.structurizr.view.Shape; import com.structurizr.view.SystemContextView; +import com.structurizr.view.SystemLandscapeView; import org.junit.jupiter.api.Test; import java.io.File; @@ -22,36 +23,36 @@ public void apply_Workspace() throws Exception { File tempDir = Files.createTempDirectory("graphviz").toFile(); GraphvizAutomaticLayout graphviz = new GraphvizAutomaticLayout(tempDir); - Workspace workspace = new Workspace("Name", ""); - Person user = workspace.getModel().addPerson("User"); - SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); - user.uses(softwareSystem, "Uses"); + Workspace workspace = new Workspace("Name"); + SoftwareSystem a = workspace.getModel().addSoftwareSystem("A"); + SoftwareSystem b = workspace.getModel().addSoftwareSystem("B"); + a.uses(b, "Uses"); - SystemContextView view = workspace.getViews().createSystemContextView(softwareSystem, "SystemContext", ""); + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key"); view.addAllElements(); workspace.getViews().getConfiguration().getStyles().addElementStyle(Tags.PERSON).shape(Shape.Person); - assertEquals(0, view.getElementView(user).getX()); - assertEquals(0, view.getElementView(user).getY()); - assertEquals(0, view.getElementView(softwareSystem).getX()); - assertEquals(0, view.getElementView(softwareSystem).getY()); + assertEquals(0, view.getElementView(a).getX()); + assertEquals(0, view.getElementView(a).getY()); + assertEquals(0, view.getElementView(b).getX()); + assertEquals(0, view.getElementView(b).getY()); graphviz.apply(workspace); // no change - the view doesn't have automatic layout configured - assertEquals(0, view.getElementView(user).getX()); - assertEquals(0, view.getElementView(user).getY()); - assertEquals(0, view.getElementView(softwareSystem).getX()); - assertEquals(0, view.getElementView(softwareSystem).getY()); + assertEquals(0, view.getElementView(a).getX()); + assertEquals(0, view.getElementView(a).getY()); + assertEquals(0, view.getElementView(b).getX()); + assertEquals(0, view.getElementView(b).getY()); view.enableAutomaticLayout(AutomaticLayout.RankDirection.TopBottom); graphviz.apply(workspace); - assertEquals(233, view.getElementView(user).getX()); - assertEquals(208, view.getElementView(user).getY()); - assertEquals(208, view.getElementView(softwareSystem).getX()); - assertEquals(908, view.getElementView(softwareSystem).getY()); + assertEquals(208, view.getElementView(a).getX()); + assertEquals(208, view.getElementView(a).getY()); + assertEquals(208, view.getElementView(b).getX()); + assertEquals(808, view.getElementView(b).getY()); } } \ No newline at end of file diff --git a/structurizr-core/src/main/java/com/structurizr/view/AbstractStyle.java b/structurizr-core/src/main/java/com/structurizr/view/AbstractStyle.java index 39ad047be..168519a8e 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/AbstractStyle.java +++ b/structurizr-core/src/main/java/com/structurizr/view/AbstractStyle.java @@ -1,17 +1,43 @@ package com.structurizr.view; import com.structurizr.PropertyHolder; +import com.structurizr.util.StringUtils; import java.util.Collections; import java.util.HashMap; import java.util.Map; -public abstract class AbstractStyle implements PropertyHolder { +public abstract class AbstractStyle implements PropertyHolder, Comparable { + private String tag; private ColorScheme colorScheme = null; - private Map properties = new HashMap<>(); + AbstractStyle() { + } + + AbstractStyle(String tag) { + this.tag = tag; + } + + AbstractStyle(String tag, ColorScheme colorScheme) { + this.tag = tag; + this.colorScheme = colorScheme; + } + + /** + * The tag to which this style applies. + * + * @return the tag, as a String + */ + public String getTag() { + return tag; + } + + void setTag(String tag) { + this.tag = tag; + } + /** * Gets the color scheme of this style. * @@ -46,11 +72,11 @@ public Map getProperties() { * @param value the value of the property */ public void addProperty(String name, String value) { - if (name == null || name.trim().length() == 0) { + if (StringUtils.isNullOrEmpty(name)) { throw new IllegalArgumentException("A property name must be specified."); } - if (value == null || value.trim().length() == 0) { + if (StringUtils.isNullOrEmpty(value)) { throw new IllegalArgumentException("A property value must be specified."); } @@ -63,4 +89,26 @@ void setProperties(Map properties) { } } + @Override + public String toString() { + return this.tag + " (" + this.colorScheme + ")"; + } + + @Override + public int compareTo(AbstractStyle other) { + if (this.colorScheme == null && other.colorScheme == null) { + return this.tag.compareTo(other.tag); + } + + if (this.colorScheme == null) { + return -1; + } + + if (other.colorScheme == null) { + return 1; + } + + return (this.colorScheme + "/" + this.tag).compareTo(other.colorScheme + "/" + other.tag); + } + } \ No newline at end of file diff --git a/structurizr-core/src/main/java/com/structurizr/view/ElementStyle.java b/structurizr-core/src/main/java/com/structurizr/view/ElementStyle.java index 90bd169c3..fe4799b60 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/ElementStyle.java +++ b/structurizr-core/src/main/java/com/structurizr/view/ElementStyle.java @@ -12,8 +12,6 @@ public final class ElementStyle extends AbstractStyle { public static final int DEFAULT_WIDTH = 450; public static final int DEFAULT_HEIGHT = 300; - private String tag; - @JsonInclude(value = JsonInclude.Include.NON_NULL) private Integer width; @@ -59,13 +57,12 @@ public final class ElementStyle extends AbstractStyle { ElementStyle() { } - ElementStyle(String tag) { - this.tag = tag; + public ElementStyle(String tag) { + super(tag); } ElementStyle(String tag, ColorScheme colorScheme) { - this.tag = tag; - setColorScheme(colorScheme); + super(tag, colorScheme); } public ElementStyle(String tag, Integer width, Integer height, String background, String color, Integer fontSize) { @@ -73,7 +70,8 @@ public ElementStyle(String tag, Integer width, Integer height, String background } public ElementStyle(String tag, Integer width, Integer height, String background, String color, Integer fontSize, Shape shape) { - this.tag = tag; + super(tag); + this.width = width; this.height = height; setBackground(background); @@ -82,19 +80,6 @@ public ElementStyle(String tag, Integer width, Integer height, String background this.shape = shape; } - /** - * The tag to which this element style applies. - * - * @return the tag, as a String - */ - public String getTag() { - return tag; - } - - public void setTag(String tag) { - this.tag = tag; - } - /** * Gets the width of the element, in pixels. * @@ -443,6 +428,10 @@ void copyFrom(ElementStyle elementStyle) { this.setIcon(elementStyle.getIcon()); } + if (elementStyle.getIconPosition() != null) { + this.setIconPosition(elementStyle.getIconPosition()); + } + if (elementStyle.getBorder() != null) { this.setBorder(elementStyle.getBorder()); } diff --git a/structurizr-core/src/main/java/com/structurizr/view/RelationshipStyle.java b/structurizr-core/src/main/java/com/structurizr/view/RelationshipStyle.java index be966314e..5f77360b4 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/RelationshipStyle.java +++ b/structurizr-core/src/main/java/com/structurizr/view/RelationshipStyle.java @@ -8,9 +8,6 @@ public final class RelationshipStyle extends AbstractStyle { private static final int START_OF_LINE = 0; private static final int END_OF_LINE = 100; - /** the name of the tag to which this style applies */ - private String tag; - /** the thickness of the line, in pixels */ @JsonInclude(value = JsonInclude.Include.NON_NULL) private Integer thickness; @@ -54,21 +51,12 @@ public final class RelationshipStyle extends AbstractStyle { RelationshipStyle() { } - RelationshipStyle(String tag) { - this.tag = tag; + public RelationshipStyle(String tag) { + super(tag); } RelationshipStyle(String tag, ColorScheme colorScheme) { - this.tag = tag; - setColorScheme(colorScheme); - } - - public String getTag() { - return tag; - } - - public void setTag(String tag) { - this.tag = tag; + super(tag, colorScheme); } public Integer getThickness() { @@ -253,6 +241,10 @@ void copyFrom(RelationshipStyle relationshipStyle) { this.setRouting(relationshipStyle.getRouting()); } + if (relationshipStyle.getJump() != null) { + this.setJump(relationshipStyle.getJump()); + } + if (relationshipStyle.getFontSize() != null) { this.setFontSize(relationshipStyle.getFontSize()); } diff --git a/structurizr-core/src/main/java/com/structurizr/view/Styles.java b/structurizr-core/src/main/java/com/structurizr/view/Styles.java index 805ed188b..ba0c7e7ee 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/Styles.java +++ b/structurizr-core/src/main/java/com/structurizr/view/Styles.java @@ -8,16 +8,16 @@ public final class Styles { - private static final Integer DEFAULT_WIDTH_OF_ELEMENT = 450; - private static final Integer DEFAULT_HEIGHT_OF_ELEMENT = 300; + public static final String DEFAULT_BACKGROUND_LIGHT = "#ffffff"; + public static final String DEFAULT_COLOR_LIGHT = "#444444"; - private static final Integer DEFAULT_WIDTH_OF_PERSON = 400; - private static final Integer DEFAULT_HEIGHT_OF_PERSON = 400; + public static final String DEFAULT_BACKGROUND_DARK = "#111111"; + public static final String DEFAULT_COLOR_DARK = "#cccccc"; - private Collection elements = new LinkedList<>(); - private Collection relationships = new LinkedList<>(); + private Collection elements = new TreeSet<>(); + private Collection relationships = new TreeSet<>(); - private List themes = new ArrayList<>(); + private final List themes = new ArrayList<>(); public Collection getElements() { return elements; @@ -100,7 +100,7 @@ public RelationshipStyle addRelationshipStyle(String tag, ColorScheme colorSchem } /** - * Gets the element style that has been defined (in this workspace) for the given tag. + * Gets the element style that has been defined (in this workspace) for the given tag (without a color scheme). * * @param tag the tag (a String) * @return an ElementStyle instance, or null if no element style has been defined in this workspace @@ -125,14 +125,24 @@ public ElementStyle getElementStyle(String tag, ColorScheme colorScheme) { } /** - * Finds the element style for the given tag. This method creates an empty style, - * and copies properties from any element styles (from the workspace and any themes) for the given tag. - * + * Finds the element style for the given tag and light color scheme. * * @param tag the tag (a String) * @return an ElementStyle instance, or null if there is no style for the given tag */ public ElementStyle findElementStyle(String tag) { + return findElementStyle(tag, ColorScheme.Light); + } + + /** + * Finds the element style for the given tag and color scheme. This method creates an empty style, + * and copies properties from any element styles (from the workspace and any themes) for the given tag. + * + * @param tag the tag (a String) + * @param colorScheme the target color scheme + * @return an ElementStyle instance, or null if there is no style for the given tag + */ + public ElementStyle findElementStyle(String tag, ColorScheme colorScheme) { if (tag == null) { return null; } @@ -149,8 +159,10 @@ public ElementStyle findElementStyle(String tag) { for (ElementStyle elementStyle : elementStyles) { if (elementStyle != null && elementStyle.getTag().equals(tag)) { - elementStyleExists = true; - style.copyFrom(elementStyle); + if (elementStyle.getColorScheme() == null || elementStyle.getColorScheme() == colorScheme) { + elementStyleExists = true; + style.copyFrom(elementStyle); + } } } @@ -162,7 +174,7 @@ public ElementStyle findElementStyle(String tag) { } /** - * Gets the relationship style that has been defined (in this workspace) for the given tag. + * Gets the relationship style that has been defined (in this workspace) for the given tag (without a color scheme). * * @param tag the tag (a String) * @return an RelationshipStyle instance, or null if no relationship style has been defined in this workspace @@ -187,14 +199,25 @@ public RelationshipStyle getRelationshipStyle(String tag, ColorScheme colorSchem } /** - * Finds the relationship style for the given tag. This method creates an empty style, - * and copies properties from any relationship styles (from the workspace and any themes) for the given tag. + * Finds the relationship style for the given tag with a light color scheme. * * * @param tag the tag (a String) * @return a RelationshipStyle instance, or null if there is no style for the given tag */ public RelationshipStyle findRelationshipStyle(String tag) { + return findRelationshipStyle(tag, ColorScheme.Light); + } + + /** + * Finds the relationship style for the given tag and color scheme. This method creates an empty style, + * and copies properties from any relationship styles (from the workspace and any themes) for the given tag. + * + * @param tag the tag (a String) + * @param colorScheme the target color scheme + * @return a RelationshipStyle instance, or null if there is no style for the given tag + */ + public RelationshipStyle findRelationshipStyle(String tag, ColorScheme colorScheme) { if (tag == null) { return null; } @@ -211,8 +234,10 @@ public RelationshipStyle findRelationshipStyle(String tag) { for (RelationshipStyle relationshipStyle : relationshipStyles) { if (relationshipStyle != null && relationshipStyle.getTag().equals(tag)) { - style.copyFrom(relationshipStyle); - relationshipStyleExists = true; + if (relationshipStyle.getColorScheme() == null || relationshipStyle.getColorScheme() == colorScheme) { + style.copyFrom(relationshipStyle); + relationshipStyleExists = true; + } } } @@ -224,23 +249,28 @@ public RelationshipStyle findRelationshipStyle(String tag) { } /** - * Finds the element style used to render the specified element, according to the following rules: + * Finds the element style used to render the specified element with a light color scheme. + * + * @param element an Element object + * @return an ElementStyle object + */ + public ElementStyle findElementStyle(Element element) { + return findElementStyle(element, ColorScheme.Light); + } + + /** + * Finds the element style used to render the specified element and color scheme, according to the following rules: * * 1. Start with a default style. * 2. Calculate set of tags associated with the element. * 3. Find the style properties for each tag (themes first, followed by workspace styles) * * @param element an Element object + * @param colorScheme a ColorScheme (Light or Dark) * @return an ElementStyle object */ - public ElementStyle findElementStyle(Element element) { - ElementStyle style = new ElementStyle(Tags.ELEMENT).background("#dddddd").color("#000000").shape(Shape.Box).fontSize(24).border(Border.Solid).opacity(100).metadata(true).description(true); - - if (element instanceof DeploymentNode) { - style.setBackground("#ffffff"); - style.setColor("#000000"); - style.setStroke("#888888"); - } + public ElementStyle findElementStyle(Element element, ColorScheme colorScheme) { + ElementStyle style = new ElementStyle(Tags.ELEMENT).shape(Shape.Box).width(ElementStyle.DEFAULT_WIDTH).height(ElementStyle.DEFAULT_HEIGHT).fontSize(24).border(Border.Solid).opacity(100).metadata(true).description(true); if (element != null) { Set tagsUsedToComposeStyle = new LinkedHashSet<>(); @@ -257,7 +287,7 @@ public ElementStyle findElementStyle(Element element) { for (String tag : tags.split(",")) { if (!StringUtils.isNullOrEmpty(tag)) { - ElementStyle elementStyle = findElementStyle(tag); + ElementStyle elementStyle = findElementStyle(tag, colorScheme); if (elementStyle != null) { style.copyFrom(elementStyle); tagsUsedToComposeStyle.add(elementStyle.getTag()); @@ -268,42 +298,57 @@ public ElementStyle findElementStyle(Element element) { style.setTag(TagUtils.toString(tagsUsedToComposeStyle)); } - if (style.getWidth() == null) { - if (style.getShape() == Shape.Person) { - style.setWidth(DEFAULT_WIDTH_OF_PERSON); + if (style.getBackground() == null) { + if (colorScheme == ColorScheme.Dark) { + style.background(DEFAULT_BACKGROUND_DARK); + if (style.getStroke() == null) { + style.stroke(DEFAULT_COLOR_DARK); + } } else { - style.setWidth(DEFAULT_WIDTH_OF_ELEMENT); + style.background(DEFAULT_BACKGROUND_LIGHT); + if (style.getStroke() == null) { + style.stroke(DEFAULT_COLOR_LIGHT); + } + } + } else { + if (style.getStroke() == null) { + java.awt.Color color = java.awt.Color.decode(style.getBackground()); + style.setStroke(String.format("#%06X", (0xFFFFFF & color.darker().getRGB()))); } } - if (style.getHeight() == null) { - if (style.getShape() == Shape.Person || style.getShape() == Shape.Robot) { - style.setHeight(DEFAULT_HEIGHT_OF_PERSON); + if (style.getColor() == null) { + if (colorScheme == ColorScheme.Dark) { + style.color(DEFAULT_COLOR_DARK); } else { - style.setHeight(DEFAULT_HEIGHT_OF_ELEMENT); + style.color(DEFAULT_COLOR_LIGHT); } } - if (style.getStroke() == null) { - java.awt.Color color = java.awt.Color.decode(style.getBackground()); - style.setStroke(String.format("#%06X", (0xFFFFFF & color.darker().getRGB()))); - } return style; } /** - * Finds the relationship style used to render the specified relationship, according to the following rules: + * Finds the relationship style used to render the specified relationship with a light color scheme. + */ + public RelationshipStyle findRelationshipStyle(Relationship relationship) { + return findRelationshipStyle(relationship, ColorScheme.Light); + } + + /** + * Finds the relationship style used to render the specified relationship and color scheme, according to the following rules: * * 1. Start with a default style. * 2. Calculate set of tags associated with the relationship, and any linked relationship(s). * 3. Find the style properties for each tag (themes first, followed by workspace styles) * * @param relationship a Relationship object + * @param colorScheme a ColorScheme (Light or Dark) * @return a RelationshipStyle object */ - public RelationshipStyle findRelationshipStyle(Relationship relationship) { - RelationshipStyle style = new RelationshipStyle(Tags.RELATIONSHIP).thickness(2).color("#707070").dashed(true).routing(Routing.Direct).fontSize(24).width(200).position(50).opacity(100); + public RelationshipStyle findRelationshipStyle(Relationship relationship, ColorScheme colorScheme) { + RelationshipStyle style = new RelationshipStyle(Tags.RELATIONSHIP).thickness(2).style(LineStyle.Dashed).dashed(true).routing(Routing.Direct).fontSize(24).width(200).position(50).opacity(100); if (relationship != null) { Set tagsUsedToComposeStyle = new LinkedHashSet<>(); @@ -322,7 +367,7 @@ public RelationshipStyle findRelationshipStyle(Relationship relationship) { for (String tag : tags.split(",")) { if (!StringUtils.isNullOrEmpty(tag)) { - RelationshipStyle relationshipStyle = findRelationshipStyle(tag); + RelationshipStyle relationshipStyle = findRelationshipStyle(tag, colorScheme); if (relationshipStyle != null) { style.copyFrom(relationshipStyle); tagsUsedToComposeStyle.add(relationshipStyle.getTag()); @@ -333,6 +378,14 @@ public RelationshipStyle findRelationshipStyle(Relationship relationship) { style.setTag(TagUtils.toString(tagsUsedToComposeStyle)); } + if (style.getColor() == null) { + if (colorScheme == ColorScheme.Dark) { + style.color(DEFAULT_COLOR_DARK); + } else { + style.color(DEFAULT_COLOR_LIGHT); + } + } + return style; } diff --git a/structurizr-core/src/test/java/com/structurizr/view/StylesTests.java b/structurizr-core/src/test/java/com/structurizr/view/StylesTests.java index caf878673..f0c6baab6 100644 --- a/structurizr-core/src/test/java/com/structurizr/view/StylesTests.java +++ b/structurizr-core/src/test/java/com/structurizr/view/StylesTests.java @@ -4,6 +4,8 @@ import com.structurizr.model.*; import org.junit.jupiter.api.Test; +import java.util.LinkedList; +import java.util.List; import java.util.Set; import static org.junit.jupiter.api.Assertions.*; @@ -12,18 +14,54 @@ public class StylesTests extends AbstractWorkspaceTestBase { private final Styles styles = new Styles(); + @Test + void test_sortingOfElementStyles() { + ElementStyle softwareLight = styles.addElementStyle(Tags.SOFTWARE_SYSTEM, ColorScheme.Light); + ElementStyle softwareDark = styles.addElementStyle(Tags.SOFTWARE_SYSTEM, ColorScheme.Dark); + ElementStyle software = styles.addElementStyle(Tags.SOFTWARE_SYSTEM); + ElementStyle elementDark = styles.addElementStyle(Tags.ELEMENT, ColorScheme.Dark); + ElementStyle elementLight = styles.addElementStyle(Tags.ELEMENT, ColorScheme.Light); + ElementStyle element = styles.addElementStyle(Tags.ELEMENT); + + List elementStyles = new LinkedList<>(styles.getElements()); + assertSame(element, elementStyles.get(0)); + assertSame(software, elementStyles.get(1)); + assertSame(elementDark, elementStyles.get(2)); + assertSame(softwareDark, elementStyles.get(3)); + assertSame(elementLight, elementStyles.get(4)); + assertSame(softwareLight, elementStyles.get(5)); + } + + @Test + void test_sortingOfRelationshipStyles() { + RelationshipStyle tag2Light = styles.addRelationshipStyle("Tag 2", ColorScheme.Light); + RelationshipStyle tag2Dark = styles.addRelationshipStyle("Tag 2", ColorScheme.Dark); + RelationshipStyle tag2 = styles.addRelationshipStyle("Tag 2"); + RelationshipStyle tag1Light = styles.addRelationshipStyle("Tag 1", ColorScheme.Light); + RelationshipStyle tag1Dark = styles.addRelationshipStyle("Tag 1", ColorScheme.Dark); + RelationshipStyle tag1 = styles.addRelationshipStyle("Tag 1"); + + List relationshipStyles = new LinkedList<>(styles.getRelationships()); + assertSame(tag1, relationshipStyles.get(0)); + assertSame(tag2, relationshipStyles.get(1)); + assertSame(tag1Dark, relationshipStyles.get(2)); + assertSame(tag2Dark, relationshipStyles.get(3)); + assertSame(tag1Light, relationshipStyles.get(4)); + assertSame(tag2Light, relationshipStyles.get(5)); + } + @Test void findElementStyle_ReturnsTheDefaultStyle_WhenPassedNull() { ElementStyle style = styles.findElementStyle((Element) null); assertEquals(Integer.valueOf(450), style.getWidth()); assertEquals(Integer.valueOf(300), style.getHeight()); - assertEquals("#dddddd", style.getBackground()); - assertEquals("#000000", style.getColor()); + assertEquals("#ffffff", style.getBackground()); + assertEquals("#444444", style.getColor()); + assertEquals("#444444", style.getStroke()); assertEquals(Integer.valueOf(24), style.getFontSize()); assertEquals(Shape.Box, style.getShape()); assertNull(style.getIcon()); assertEquals(Border.Solid, style.getBorder()); - assertEquals("#9a9a9a", style.getStroke()); assertNull(style.getStrokeWidth()); assertEquals(Integer.valueOf(100), style.getOpacity()); assertEquals(true, style.getMetadata()); @@ -36,13 +74,32 @@ void findElementStyle_ReturnsTheDefaultStyle_WhenNoStylesAreDefined() { ElementStyle style = styles.findElementStyle(element); assertEquals(Integer.valueOf(450), style.getWidth()); assertEquals(Integer.valueOf(300), style.getHeight()); - assertEquals("#dddddd", style.getBackground()); - assertEquals("#000000", style.getColor()); + assertEquals("#ffffff", style.getBackground()); + assertEquals("#444444", style.getColor()); + assertEquals("#444444", style.getStroke()); + assertEquals(Integer.valueOf(24), style.getFontSize()); + assertEquals(Shape.Box, style.getShape()); + assertNull(style.getIcon()); + assertEquals(Border.Solid, style.getBorder()); + assertNull(style.getStrokeWidth()); + assertEquals(Integer.valueOf(100), style.getOpacity()); + assertEquals(true, style.getMetadata()); + assertEquals(true, style.getDescription()); + } + + @Test + void findElementStyleForDarkMode_ReturnsTheDefaultStyle_WhenNoStylesAreDefined() { + SoftwareSystem element = model.addSoftwareSystem("Name", "Description"); + ElementStyle style = styles.findElementStyle(element, ColorScheme.Dark); + assertEquals(Integer.valueOf(450), style.getWidth()); + assertEquals(Integer.valueOf(300), style.getHeight()); + assertEquals("#111111", style.getBackground()); + assertEquals("#cccccc", style.getColor()); + assertEquals("#cccccc", style.getStroke()); assertEquals(Integer.valueOf(24), style.getFontSize()); assertEquals(Shape.Box, style.getShape()); assertNull(style.getIcon()); assertEquals(Border.Solid, style.getBorder()); - assertEquals("#9a9a9a", style.getStroke()); assertNull(style.getStrokeWidth()); assertEquals(Integer.valueOf(100), style.getOpacity()); assertEquals(true, style.getMetadata()); @@ -116,24 +173,23 @@ void findElementStyle_ReturnsTheDefaultElementSize_WhenTheShapeIsABox() { } @Test - void findElementStyle_ReturnsTheDefaultElementSize_WhenTheShapeIsAPerson() { - SoftwareSystem element = model.addSoftwareSystem("Name", "Description"); - element.addTags("Some Tag"); - - styles.addElementStyle(Tags.SOFTWARE_SYSTEM).background("#ff0000").color("#ffffff"); - styles.addElementStyle("Some Tag").shape(Shape.Person); - - ElementStyle style = styles.findElementStyle(element); - assertEquals(Shape.Person, style.getShape()); - assertEquals(Integer.valueOf(400), style.getWidth()); - assertEquals(Integer.valueOf(400), style.getHeight()); + void findRelationshipStyle_ReturnsTheDefaultStyle_WhenPassedNull_ForLightColorScheme() { + RelationshipStyle style = styles.findRelationshipStyle((Relationship) null); + assertEquals(Integer.valueOf(2), style.getThickness()); + assertEquals("#444444", style.getColor()); + assertTrue(style.getDashed()); + assertEquals(Routing.Direct, style.getRouting()); + assertEquals(Integer.valueOf(24), style.getFontSize()); + assertEquals(Integer.valueOf(200), style.getWidth()); + assertEquals(Integer.valueOf(50), style.getPosition()); + assertEquals(Integer.valueOf(100), style.getOpacity()); } @Test - void findRelationshipStyle_ReturnsTheDefaultStyle_WhenPassedNull() { - RelationshipStyle style = styles.findRelationshipStyle((Relationship) null); + void findRelationshipStyle_ReturnsTheDefaultStyle_WhenPassedNull_ForDarkColorScheme() { + RelationshipStyle style = styles.findRelationshipStyle((Relationship) null, ColorScheme.Dark); assertEquals(Integer.valueOf(2), style.getThickness()); - assertEquals("#707070", style.getColor()); + assertEquals("#cccccc", style.getColor()); assertTrue(style.getDashed()); assertEquals(Routing.Direct, style.getRouting()); assertEquals(Integer.valueOf(24), style.getFontSize()); @@ -148,7 +204,7 @@ void findRelationshipStyle_ReturnsTheDefaultStyle_WhenNoStylesAreDefined() { Relationship relationship = element.uses(element, "Uses"); RelationshipStyle style = styles.findRelationshipStyle(relationship); assertEquals(Integer.valueOf(2), style.getThickness()); - assertEquals("#707070", style.getColor()); + assertEquals("#444444", style.getColor()); assertTrue(style.getDashed()); assertEquals(Routing.Direct, style.getRouting()); assertEquals(Integer.valueOf(24), style.getFontSize()); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index 43fcb6e0f..022b748d2 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -386,7 +386,7 @@ void test_includeUrl() throws Exception { workspace { model { - !include https://raw.githubusercontent.com/structurizr/dsl/master/src/test/dsl/include/model.dsl + !include https://raw.githubusercontent.com/structurizr/java/refs/heads/master/structurizr-dsl/src/test/resources/dsl/include/model.dsl } }""", DslUtils.getDsl(workspace)); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewContentParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewContentParserTests.java index 52e59ccb3..4a2266c73 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewContentParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewContentParserTests.java @@ -55,7 +55,8 @@ void test_parsePlantUML_WithViewKey() { parser = new ImageViewContentParser(true); parser.parsePlantUML(context, null, tokens("plantuml", "SystemLandscape")); - assertEquals("https://plantuml.com/plantuml/svg/HP2nJiD038RtUmghF00f6oYD6f2OO2eI0p2Od9EUUh4Zdwkq8DwTkrB0bZpylsL_zZePgkt7w18P99fGqKI1XSbPi4YmEIQZ4HwGVUfm8kTC9Z21Tp6J4NnGwYm8EvTsWSk44JuT0AhAV2zic_11iAoovAd7VRGdEbWRmy0ZiK6N2sbsPyNfENZRmbLLkaSyF59AED1vGkM-dDi6Jv2HbCIE1UT_Qm517YBLTTiq9uXRx7Q3ofxzdSHys8K_HNOAsLchJb6wHJtfMRt6abbDM_Go1nwWnvYeGFnjWiLgrRvodJBXpR9gNZRIsupw-xUt-h9OpG9-c311wzoQsEUdVmC0", imageView.getContent()); + assertEquals("System Landscape", imageView.getTitle()); + assertEquals("https://plantuml.com/plantuml/svg/ZLBBJiCm4BpxArRb32rLuH2IgE4b3kL243q01pVU9bOJRsHlr0VYtt6Qj1n0Y9LihMTjpth6KyVISbELWZMN2A7JEmp6apZTEiOAPj8ebyaQms5RYT_CSSSjkipgcZMPlYY4GmQ7jRIIoO8XWuAf1YPO43DLeBJ5h3qY2gqGF8T5ucsDGeIEjwM_1C0ICNpu1E1QPglSKcFK3PLa0pXPxkDgNxqdmmTyieyM__HZE8Ix4Yiqx1TdVNhwDD-KY_bBev8e-XV1J1lyIT3XQTjk0ADlvBdGsSgWSm6C_sgmmzDMHnZto0DPlVEeB9DIvwPjDu3CpsYx3MaX5QsroO-KZtAZgwQQQyL509EBKVTuRqOdf6YbbYRtjWwYA3bOTtuPlwQqvBMq29tDxxs10mZ3txIAOv0E4Y6cQ9J_B5y0", imageView.getContent()); } @Test @@ -94,7 +95,7 @@ void test_parseMermaid_WithViewKey() { parser = new ImageViewContentParser(true); parser.parseMermaid(context, null, tokens("mermaid", "SystemLandscape")); - assertEquals("https://mermaid.ink/svg/pako:eJxlkMtuwjAQRX9lNAhlE9SwqupCpLLuLt0RFiYeJxZ-RLYppYh_bxJHVR93NrM4c3U0N8DGCUKGred9B2-72gJoZU9VvGoCQZKfdQSptGYLOaW2IxPOx3QiFB8WA_saq2uIZOCVWxEa3lONhxEd4FQ2kz_L8hC9O9HvboD10LYR6j1dbjPpbFxdSLVdZHB0WmTly-ZhAMp_VFCfxOCxWD6D4b5VdhVdz6DoP7JyXzkZL9wTJNVD6vjjuZ4NxZRvwyc-Tt447TxbFFOSL1mBOaAhb7gSyG4YOzLjV-f_4f3-BQMfekI=", imageView.getContent()); + assertEquals("https://mermaid.ink/svg/pako:eJxtkMtqwzAQRX9lmFK8cWgChYKaGNp1d-4uzkKxRraIHkaaNE1D_r12lEJfs5qBM4fLPQG2QREK7KIcenh9bjyANX5X89ESKNJybxm0sVbc6Ms0fmLSfptflJHj4mDdYH1MTA5epFeplQM1uJnQEc6yK_ldViaOYUc_3QCL0bZU5i1_rgodPM8OZLqeBWyDVUX1tLwbgeoPlcwHCXiY3z6Ck7EzfsZhEDAf3otqXQfNBxkJctRNdvzKufg_4f1lyjbYEL-unJe8whLQUXTSKBQn5J7c1Oq1PzyfPwHoMXnQ", imageView.getContent()); } @Test diff --git a/structurizr-dsl/src/test/resources/dsl/include-url.dsl b/structurizr-dsl/src/test/resources/dsl/include-url.dsl index 9739a90f7..e8e813e37 100644 --- a/structurizr-dsl/src/test/resources/dsl/include-url.dsl +++ b/structurizr-dsl/src/test/resources/dsl/include-url.dsl @@ -1,7 +1,7 @@ workspace { model { - !include https://raw.githubusercontent.com/structurizr/dsl/master/src/test/dsl/include/model.dsl + !include https://raw.githubusercontent.com/structurizr/java/refs/heads/master/structurizr-dsl/src/test/resources/dsl/include/model.dsl } } \ No newline at end of file diff --git a/structurizr-export/src/main/java/com/structurizr/export/AbstractDiagramExporter.java b/structurizr-export/src/main/java/com/structurizr/export/AbstractDiagramExporter.java index f3d1839a1..81894c514 100644 --- a/structurizr-export/src/main/java/com/structurizr/export/AbstractDiagramExporter.java +++ b/structurizr-export/src/main/java/com/structurizr/export/AbstractDiagramExporter.java @@ -13,8 +13,18 @@ public abstract class AbstractDiagramExporter extends AbstractExporter implement protected static final String GROUP_SEPARATOR_PROPERTY_NAME = "structurizr.groupSeparator"; + protected final ColorScheme colorScheme; + private Object frame = null; + public AbstractDiagramExporter() { + this(ColorScheme.Light); + } + + public AbstractDiagramExporter(ColorScheme colorScheme) { + this.colorScheme = colorScheme; + } + /** * Exports all views in the workspace. * @@ -120,14 +130,13 @@ private Diagram export(CustomView view, Integer animationStep) { IndentingWriter writer = new IndentingWriter(); writeHeader(view, writer); - List elements = new ArrayList<>(); - for (ElementView elementView : view.getElements()) { - elements.add((CustomElement)elementView.getElement()); - } - + List elements = getGroupableElements(view, null); writeElements(view, elements, writer); - writer.writeLine(); + if (!elements.isEmpty()) { + writer.writeLine(); + } + writeRelationships(view, writer); writeFooter(view, writer); @@ -154,13 +163,13 @@ private Diagram export(SystemLandscapeView view, Integer animationStep) { IndentingWriter writer = new IndentingWriter(); writeHeader(view, writer); - List elements = new ArrayList<>(); - for (ElementView elementView : view.getElements()) { - elements.add((GroupableElement)elementView.getElement()); - } + List elements = getGroupableElements(view, null); writeElements(view, elements, writer); - writer.writeLine(); + if (!elements.isEmpty()) { + writer.writeLine(); + } + writeRelationships(view, writer); writeFooter(view, writer); @@ -187,13 +196,13 @@ private Diagram export(SystemContextView view, Integer animationStep) { IndentingWriter writer = new IndentingWriter(); writeHeader(view, writer); - List elements = new ArrayList<>(); - for (ElementView elementView : view.getElements()) { - elements.add((GroupableElement)elementView.getElement()); - } + List elements = getGroupableElements(view, null); writeElements(view, elements, writer); - writer.writeLine(); + if (!elements.isEmpty()) { + writer.writeLine(); + } + writeRelationships(view, writer); writeFooter(view, writer); @@ -235,13 +244,7 @@ public Diagram export(ContainerView view, Integer animationStep) { for (SoftwareSystem softwareSystem : softwareSystems) { startSoftwareSystemBoundary(view, softwareSystem, writer); - List scopedElements = new ArrayList<>(); - for (ElementView elementView : view.getElements()) { - if (elementView.getElement().getParent() == softwareSystem) { - scopedElements.add((StaticStructureElement) elementView.getElement()); - } - } - + List scopedElements = getGroupableElements(view, softwareSystem); writeElements(view, scopedElements, writer); endSoftwareSystemBoundary(view, writer); @@ -307,12 +310,7 @@ public Diagram export(ComponentView view, Integer animationStep) { if (container.getSoftwareSystem() == softwareSystem) { startContainerBoundary(view, container, writer); - List scopedElements = new ArrayList<>(); - for (ElementView elementView : view.getElements()) { - if (elementView.getElement().getParent() == container) { - scopedElements.add((StaticStructureElement) elementView.getElement()); - } - } + List scopedElements = getGroupableElements(view, container); writeElements(view, scopedElements, writer); endContainerBoundary(view, writer); @@ -369,11 +367,12 @@ public Diagram export(DynamicView view, String order) { if (element == null) { // dynamic view with no scope - List elements = new ArrayList<>(); - for (ElementView elementView : view.getElements()) { - elements.add((StaticStructureElement) elementView.getElement()); - } + List elements = getGroupableElements(view, null); writeElements(view, elements, writer); + + if (!elements.isEmpty()) { + elementsWritten = true; + } } else { if (element instanceof SoftwareSystem) { // dynamic view with software system scope @@ -381,13 +380,7 @@ public Diagram export(DynamicView view, String order) { for (SoftwareSystem softwareSystem : softwareSystems) { startSoftwareSystemBoundary(view, softwareSystem, writer); - List scopedElements = new ArrayList<>(); - for (ElementView elementView : view.getElements()) { - if (elementView.getElement().getParent() == softwareSystem) { - scopedElements.add((StaticStructureElement) elementView.getElement()); - } - } - + List scopedElements = getGroupableElements(view, softwareSystem); writeElements(view, scopedElements, writer); endSoftwareSystemBoundary(view, writer); @@ -416,12 +409,7 @@ public Diagram export(DynamicView view, String order) { if (container.getSoftwareSystem() == softwareSystem) { startContainerBoundary(view, container, writer); - List scopedElements = new ArrayList<>(); - for (ElementView elementView : view.getElements()) { - if (elementView.getElement().getParent() == container) { - scopedElements.add((StaticStructureElement) elementView.getElement()); - } - } + List scopedElements = getGroupableElements(view, container); writeElements(view, scopedElements, writer); endContainerBoundary(view, writer); @@ -472,14 +460,7 @@ public Diagram export(DeploymentView view, Integer animationStep) { IndentingWriter writer = new IndentingWriter(); writeHeader(view, writer); - List elements = new ArrayList<>(); - - for (ElementView elementView : view.getElements()) { - if (elementView.getElement() instanceof DeploymentNode && elementView.getElement().getParent() == null) { - elements.add((DeploymentNode)elementView.getElement()); - } - } - + List elements = getGroupableElements(view, null); writeElements(view, elements, writer); writeRelationships(view, writer); @@ -488,7 +469,7 @@ public Diagram export(DeploymentView view, Integer animationStep) { return createDiagram(view, writer.toString()); } - protected void writeElements(ModelView view, List elements, IndentingWriter writer) { + protected List findGroups(ModelView view, List elements) { String groupSeparator = view.getModel().getProperties().get(GROUP_SEPARATOR_PROPERTY_NAME); boolean nested = !StringUtils.isNullOrEmpty(groupSeparator); @@ -513,8 +494,16 @@ protected void writeElements(ModelView view, List elements, In List groupsAsList = new ArrayList<>(groupsAsSet); Collections.sort(groupsAsList); + return groupsAsList; + } + + protected void writeElements(ModelView view, List elements, IndentingWriter writer) { + String groupSeparator = view.getModel().getProperties().get(GROUP_SEPARATOR_PROPERTY_NAME); + boolean nested = !StringUtils.isNullOrEmpty(groupSeparator); + List groupsAsList = findGroups(view, elements); + // first render grouped elements - if (groupsAsList.size() > 0) { + if (!groupsAsList.isEmpty()) { if (nested) { String context = ""; @@ -585,18 +574,24 @@ protected void writeElements(ModelView view, List elements, In } } - protected void writeRelationships(ModelView view, IndentingWriter writer) { - Collection relationshipList; - + protected Collection getRelationshipsInView(ModelView view) { if (view instanceof DynamicView) { - relationshipList = view.getRelationships(); + return view.getRelationships(); } else { - relationshipList = view.getRelationships().stream().sorted(Comparator.comparing(rv -> rv.getRelationship().getId())).collect(Collectors.toList()); + return view.getRelationships().stream().sorted(Comparator.comparing(rv -> rv.getRelationship().getId())).collect(Collectors.toList()); } + } + + protected void writeRelationships(ModelView view, IndentingWriter writer) { + Collection relationshipList = getRelationshipsInView(view); for (RelationshipView relationshipView : relationshipList) { writeRelationship(view, relationshipView, writer); } + + if (!relationshipList.isEmpty()) { + writer.writeLine(); + } } protected abstract void writeHeader(ModelView view, IndentingWriter writer); @@ -732,4 +727,80 @@ protected String getViewOrViewSetProperty(ModelView view, String name, String de ); } + @Override + protected ElementStyle findElementStyle(ModelView view, Element element) { + return view.getViewSet().getConfiguration().getStyles().findElementStyle(element, colorScheme); + } + + protected ElementStyle findElementStyle(ModelView view, String tag) { + return view.getViewSet().getConfiguration().getStyles().findElementStyle(tag, colorScheme); + } + + @Override + protected RelationshipStyle findRelationshipStyle(ModelView view, Relationship relationship) { + return view.getViewSet().getConfiguration().getStyles().findRelationshipStyle(relationship, colorScheme); + } + + protected List getGroupableElements(ModelView view, Element parent) { + List elements = new ArrayList<>(); + + if (view instanceof CustomView) { + for (ElementView elementView : view.getElements()) { + elements.add((CustomElement)elementView.getElement()); + } + } else if (view instanceof SystemLandscapeView) { + for (ElementView elementView : view.getElements()) { + elements.add((GroupableElement)elementView.getElement()); + } + } else if (view instanceof SystemContextView) { + for (ElementView elementView : view.getElements()) { + elements.add((GroupableElement)elementView.getElement()); + } + } else if (view instanceof ContainerView) { + for (ElementView elementView : view.getElements()) { + if (elementView.getElement() instanceof Container) { + elements.add((StaticStructureElement) elementView.getElement()); + } + } + } else if (view instanceof ComponentView) { + for (ElementView elementView : view.getElements()) { + if (elementView.getElement() instanceof Component) { + elements.add((StaticStructureElement) elementView.getElement()); + } + } + } else if (view instanceof DynamicView) { + DynamicView dynamicView = (DynamicView)view; + Element element = dynamicView.getElement(); + if (element == null) { + for (ElementView elementView : view.getElements()) { + elements.add((StaticStructureElement) elementView.getElement()); + } + } else if (element instanceof SoftwareSystem) { + for (ElementView elementView : view.getElements()) { + if (elementView.getElement() instanceof Container) { + elements.add((StaticStructureElement) elementView.getElement()); + } + } + } else if (element instanceof Container) { + for (ElementView elementView : view.getElements()) { + if (elementView.getElement() instanceof Component) { + elements.add((StaticStructureElement) elementView.getElement()); + } + } + } + } else if (view instanceof DeploymentView) { + for (ElementView elementView : view.getElements()) { + if (elementView.getElement() instanceof DeploymentNode && elementView.getElement().getParent() == null) { + elements.add((DeploymentNode)elementView.getElement()); + } + } + } + + if (parent != null) { + return elements.stream().filter(e -> e.getParent() == parent).collect(Collectors.toList()); + } else { + return elements; + } + } + } \ No newline at end of file diff --git a/structurizr-export/src/main/java/com/structurizr/export/IndentingWriter.java b/structurizr-export/src/main/java/com/structurizr/export/IndentingWriter.java index f7bf0a0bc..214fb00e2 100644 --- a/structurizr-export/src/main/java/com/structurizr/export/IndentingWriter.java +++ b/structurizr-export/src/main/java/com/structurizr/export/IndentingWriter.java @@ -6,7 +6,7 @@ public final class IndentingWriter { private IndentType indentType = IndentType.Spaces; private int indentQuantity = 2; - private StringBuilder buf = new StringBuilder(); + private final StringBuilder buf = new StringBuilder(); public IndentingWriter() { } @@ -49,6 +49,13 @@ public void writeLine(String content) { buf.append(String.format("%s%s\n", padding(), content.replace("\n", "\\n"))); } + public void replace(String before, String after) { + int start = buf.indexOf(before); + int end = start + before.length(); + + buf.replace(start, end, after); + } + @Override public String toString() { String s = buf.toString(); diff --git a/structurizr-export/src/main/java/com/structurizr/export/plantuml/AbstractPlantUMLExporter.java b/structurizr-export/src/main/java/com/structurizr/export/plantuml/AbstractPlantUMLExporter.java index 7424cbd2e..52a974b27 100644 --- a/structurizr-export/src/main/java/com/structurizr/export/plantuml/AbstractPlantUMLExporter.java +++ b/structurizr-export/src/main/java/com/structurizr/export/plantuml/AbstractPlantUMLExporter.java @@ -5,6 +5,7 @@ import com.structurizr.export.IndentingWriter; import com.structurizr.model.*; import com.structurizr.util.StringUtils; +import com.structurizr.view.ColorScheme; import com.structurizr.view.ElementStyle; import com.structurizr.view.ModelView; import com.structurizr.view.Shape; @@ -13,32 +14,25 @@ import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.net.URL; -import java.util.LinkedHashMap; -import java.util.Map; - -import static java.lang.String.format; public abstract class AbstractPlantUMLExporter extends AbstractDiagramExporter { + protected static final int DEFAULT_FONT_SIZE = 24; + public static final String PLANTUML_TITLE_PROPERTY = "plantuml.title"; public static final String PLANTUML_INCLUDES_PROPERTY = "plantuml.includes"; public static final String PLANTUML_ANIMATION_PROPERTY = "plantuml.animation"; public static final String PLANTUML_SEQUENCE_DIAGRAM_PROPERTY = "plantuml.sequenceDiagram"; - private static final double MAX_ICON_SIZE = 30.0; - - private final Map skinParams = new LinkedHashMap<>(); - - protected Map getSkinParams() { - return skinParams; - } + public static final String DIAGRAM_TITLE_TAG = "Diagram:Title"; + public static final String DIAGRAM_DESCRIPTION_TAG = "Diagram:Description"; - public void addSkinParam(String name, String value) { - skinParams.put(name, value); + public AbstractPlantUMLExporter() { + this(ColorScheme.Light); } - public void clearSkinParams() { - skinParams.clear(); + public AbstractPlantUMLExporter(ColorScheme colorScheme) { + super(colorScheme); } String plantUMLShapeOf(ModelView view, Element element) { @@ -178,38 +172,54 @@ protected boolean isAnimationSupported(ModelView view) { @Override protected void writeHeader(ModelView view, IndentingWriter writer) { writer.writeLine("@startuml"); - writer.writeLine("set separator none"); if (includeTitle(view)) { - String viewTitle = view.getTitle(); - if (StringUtils.isNullOrEmpty(viewTitle)) { - viewTitle = view.getName(); - } - writer.writeLine("title " + viewTitle); - } + ElementStyle titleStyle = findElementStyle(view, DIAGRAM_TITLE_TAG); + ElementStyle descriptionStyle = findElementStyle(view, DIAGRAM_DESCRIPTION_TAG); - writer.writeLine(); - } + String title = view.getTitle(); + if (StringUtils.isNullOrEmpty(title)) { + title = view.getName(); + } - protected void writeSkinParams(IndentingWriter writer) { - if (!skinParams.isEmpty()) { - writer.writeLine("skinparam {"); - writer.indent(); - for (final String name : skinParams.keySet()) { - writer.writeLine(format("%s %s", name, skinParams.get(name))); + String description = view.getDescription(); + if (StringUtils.isNullOrEmpty(description)) { + writer.writeLine( + String.format( + "title %s", + titleStyle != null ? titleStyle.getFontSize() : DEFAULT_FONT_SIZE, + title + ) + ); + } else { + writer.writeLine( + String.format( + "title %s\\n%s", + titleStyle != null ? titleStyle.getFontSize() : DEFAULT_FONT_SIZE, + title, + descriptionStyle != null ? descriptionStyle.getFontSize() : DEFAULT_FONT_SIZE, + description + ) + ); } - writer.outdent(); - writer.writeLine("}"); } + + writer.writeLine(); + writer.writeLine("set separator none"); } protected void writeIncludes(ModelView view, IndentingWriter writer) { - String[] includes = getViewOrViewSetProperty(view, PLANTUML_INCLUDES_PROPERTY, "").split(","); - for (String include : includes) { - if (!StringUtils.isNullOrEmpty(include)) { - include = include.trim(); - writer.writeLine("!include " + include); + String commaSeparatedIncludes = getViewOrViewSetProperty(view, PLANTUML_INCLUDES_PROPERTY, ""); + if (!StringUtils.isNullOrEmpty(commaSeparatedIncludes)) { + String[] includes = commaSeparatedIncludes.split(","); + + for (String include : includes) { + if (!StringUtils.isNullOrEmpty(include)) { + include = include.trim(); + writer.writeLine("!include " + include); + } } + writer.writeLine(); } } @@ -223,11 +233,11 @@ protected Diagram createDiagram(ModelView view, String definition) { return new PlantUMLDiagram(view, definition); } - protected boolean elementStyleHasSupportedIcon(ElementStyle elementStyle) { - return !StringUtils.isNullOrEmpty(elementStyle.getIcon()) && elementStyle.getIcon().startsWith("http"); + protected boolean isSupportedIcon(String icon) { + return !StringUtils.isNullOrEmpty(icon) && icon.startsWith("http"); } - protected double calculateIconScale(String iconUrl) { + protected double calculateIconScale(String iconUrl, int maxIconSize) { double scale = 0.5; try { @@ -237,7 +247,7 @@ protected double calculateIconScale(String iconUrl) { int width = bi.getWidth(); int height = bi.getHeight(); - scale = MAX_ICON_SIZE / Math.max(width, height); + scale = ((double)maxIconSize) / Math.max(width, height); } catch (UnsupportedOperationException | UnsatisfiedLinkError | IIOException e) { // This is a known issue on native builds since AWT packages aren't available. // So we just swallow the error and use the default scale diff --git a/structurizr-export/src/main/java/com/structurizr/export/plantuml/C4PlantUMLExporter.java b/structurizr-export/src/main/java/com/structurizr/export/plantuml/C4PlantUMLExporter.java index cdd26bafc..ef3c2a718 100644 --- a/structurizr-export/src/main/java/com/structurizr/export/plantuml/C4PlantUMLExporter.java +++ b/structurizr-export/src/main/java/com/structurizr/export/plantuml/C4PlantUMLExporter.java @@ -7,7 +7,6 @@ import com.structurizr.view.*; import java.util.*; -import java.util.stream.Collectors; import static java.lang.String.format; @@ -22,6 +21,8 @@ public class C4PlantUMLExporter extends AbstractPlantUMLExporter { public static final String C4PLANTUML_SPRITE = "c4plantuml.sprite"; public static final String C4PLANTUML_SHADOW = "c4plantuml.shadow"; + private static final int MAX_ICON_SIZE = 30; + /** *

    Set this property to true by calling {@link Configuration#addProperty(String, String)} in your * {@link ViewSet} in order to have all {@link ModelItem#getProperties()} for {@link Component}s @@ -51,28 +52,16 @@ public class C4PlantUMLExporter extends AbstractPlantUMLExporter { public C4PlantUMLExporter() { } + public C4PlantUMLExporter(ColorScheme colorScheme) { + super(colorScheme); + } + @Override protected void writeHeader(ModelView view, IndentingWriter writer) { super.writeHeader(view, writer); groupId = 0; - Font font = view.getViewSet().getConfiguration().getBranding().getFont(); - if (font != null) { - String fontName = font.getName(); - if (!StringUtils.isNullOrEmpty(fontName)) { - addSkinParam("defaultFontName", "\"" + fontName + "\""); - } - } - - writeSkinParams(writer); - - if (renderAsSequenceDiagram(view)) { - if (usePlantUMLStandardLibrary(view)) { - writer.writeLine("!include "); - } else { - writer.writeLine("!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Sequence.puml"); - } - } else { + if (!renderAsSequenceDiagram(view)) { if (view.getAutomaticLayout() != null) { switch (view.getAutomaticLayout().getRankDirection()) { case LeftRight: @@ -85,9 +74,41 @@ protected void writeHeader(ModelView view, IndentingWriter writer) { } else { writer.writeLine("top to bottom direction"); } + } - writer.writeLine(); + writer.writeLine(); + writer.writeLine(""); + writer.writeLine(); + + if (renderAsSequenceDiagram(view)) { + if (usePlantUMLStandardLibrary(view)) { + writer.writeLine("!include "); + } else { + writer.writeLine("!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Sequence.puml"); + } + } else { if (usePlantUMLStandardLibrary(view)) { writer.writeLine("!include "); writer.writeLine("!include "); @@ -120,6 +141,7 @@ protected void writeHeader(ModelView view, IndentingWriter writer) { } } } + writer.writeLine(); writeIncludes(view, writer); @@ -169,15 +191,13 @@ protected void writeHeader(ModelView view, IndentingWriter writer) { } if (!elementStyles.isEmpty()) { - writer.writeLine(); - for (String tagList : elementStyles.keySet()) { ElementStyle elementStyle = elementStyles.get(tagList); tagList = tagList.replaceFirst("Element,", ""); String sprite = ""; - if (elementStyleHasSupportedIcon(elementStyle)) { - double scale = calculateIconScale(elementStyle.getIcon()); + if (isSupportedIcon(elementStyle.getIcon())) { + double scale = calculateIconScale(elementStyle.getIcon(), MAX_ICON_SIZE); sprite = "img:" + elementStyle.getIcon() + "{scale=" + scale + "}"; } sprite = elementStyle.getProperties().getOrDefault(C4PLANTUML_SPRITE, sprite); @@ -201,11 +221,11 @@ protected void writeHeader(ModelView view, IndentingWriter writer) { line = line.replace(", $borderThickness=\"1\")", ")"); writer.writeLine(line); } - } - if (!relationshipStyles.isEmpty()) { writer.writeLine(); + } + if (!relationshipStyles.isEmpty()) { for (String tagList : relationshipStyles.keySet()) { RelationshipStyle relationshipStyle = relationshipStyles.get(tagList); tagList = tagList.replaceFirst("Relationship,", ""); @@ -224,6 +244,8 @@ protected void writeHeader(ModelView view, IndentingWriter writer) { lineStyle )); } + + writer.writeLine(); } if (!boundaryStyles.isEmpty()) { @@ -253,19 +275,12 @@ protected void writeHeader(ModelView view, IndentingWriter writer) { } } } - - writer.writeLine(); } @Override protected void writeFooter(ModelView view, IndentingWriter writer) { - if (includeLegend(view)) { - writer.writeLine(); - writer.writeLine("SHOW_LEGEND(" + !(includeStereotypes(view)) + ")"); - } else { - writer.writeLine(); - writer.writeLine((includeStereotypes(view) ? "show" : "hide") + " stereotypes"); - } + writer.writeLine("SHOW_LEGEND(" + includeLegend(view) + ")"); + writer.writeLine((includeStereotypes(view) ? "show" : "hide") + " stereotypes"); super.writeFooter(view, writer); } diff --git a/structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLBoundaryStyle.java b/structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLBoundaryStyle.java new file mode 100644 index 000000000..ad6cd4f6a --- /dev/null +++ b/structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLBoundaryStyle.java @@ -0,0 +1,81 @@ +package com.structurizr.export.plantuml; + +import com.structurizr.export.IndentingWriter; +import com.structurizr.view.Border; + +import java.util.Base64; + +import static java.lang.String.format; + +class PlantUMLBoundaryStyle extends PlantUMLStyle { + + private final String background; + private final String color; + private final String stroke; + private final int strokeWidth; + private final String lineStyle; + private final int fontSize; + private final boolean shadow; + + PlantUMLBoundaryStyle(String name, String background, String color, String stroke, int strokeWidth, Border border, int fontSize, boolean shadow) { + super(name); + + this.background = background; + this.color = color; + this.stroke = stroke; + this.strokeWidth = strokeWidth; + + switch (border) { + case Dotted: + this.lineStyle = (strokeWidth) + "-" + (strokeWidth); + break; + case Dashed: + this.lineStyle = (strokeWidth * 5) + "-" + (strokeWidth * 5); + break; + default: + this.lineStyle = "0"; + break; + } + + this.fontSize = fontSize; + this.shadow = shadow; + } + + @Override + String getClassSelector() { + return "Boundary-" + Base64.getEncoder().encodeToString(name.getBytes()); + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + PlantUMLBoundaryStyle that = (PlantUMLBoundaryStyle) o; + return getClassSelector().equals(that.getClassSelector()); + } + + @Override + public String toString() { + IndentingWriter writer = new IndentingWriter(); + writer.indent(); + writer.writeLine(format("// %s", name)); + writer.writeLine(format(".%s {", getClassSelector())); + writer.indent(); + + writer.writeLine(String.format("BackgroundColor: %s;", background)); + writer.writeLine(String.format("LineColor: %s;", stroke)); + writer.writeLine(String.format("LineStyle: %s;", lineStyle)); + writer.writeLine(String.format("LineThickness: %s;", strokeWidth)); + writer.writeLine(String.format("FontColor: %s;", color)); + writer.writeLine(String.format("FontSize: %s;", fontSize)); + writer.writeLine("HorizontalAlignment: center;"); + writer.writeLine(String.format("Shadowing: %s;", shadow ? SHADOW_DISTANCE : 0)); + + writer.outdent(); + writer.writeLine("}"); + writer.outdent(); + writer.writeLine(); + + return writer.toString(); + } + +} \ No newline at end of file diff --git a/structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLElementStyle.java b/structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLElementStyle.java new file mode 100644 index 000000000..06da055f0 --- /dev/null +++ b/structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLElementStyle.java @@ -0,0 +1,107 @@ +package com.structurizr.export.plantuml; + +import com.structurizr.export.IndentingWriter; +import com.structurizr.view.Border; +import com.structurizr.view.Shape; + +import java.util.Base64; + +import static java.lang.String.format; + +class PlantUMLElementStyle extends PlantUMLStyle { + + private final Shape shape; + private int width; + private final String background; + private final String color; + private final String stroke; + private final int strokeWidth; + private final String lineStyle; + private final int fontSize; + private final String icon; + private final boolean shadow; + + PlantUMLElementStyle(String name, Shape shape, int width, String background, String color, String stroke, int strokeWidth, Border border, int fontSize, String icon, boolean shadow) { + super(name); + + this.shape = shape; + this.width = width; + this.background = background; + this.color = color; + this.stroke = stroke; + this.strokeWidth = strokeWidth; + + switch (border) { + case Dotted: + this.lineStyle = (strokeWidth) + "-" + (strokeWidth); + break; + case Dashed: + this.lineStyle = (strokeWidth * 5) + "-" + (strokeWidth * 5); + break; + default: + this.lineStyle = "0"; + break; + } + + this.fontSize = fontSize; + this.icon = icon; + this.shadow = shadow; + } + + Shape getShape() { + return shape; + } + + int getFontSize() { + return fontSize; + } + + String getIcon() { + return icon; + } + + void setWidth(int width) { + this.width = width; + } + + @Override + String getClassSelector() { + return "Element-" + Base64.getEncoder().encodeToString(name.getBytes()); + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + PlantUMLElementStyle that = (PlantUMLElementStyle) o; + return getClassSelector().equals(that.getClassSelector()); + } + + @Override + public String toString() { + IndentingWriter writer = new IndentingWriter(); + writer.indent(); + writer.writeLine(format("// %s", name)); + writer.writeLine(format(".%s {", getClassSelector())); + writer.indent(); + + writer.writeLine(String.format("BackgroundColor: %s;", background)); + writer.writeLine(String.format("LineColor: %s;", stroke)); + writer.writeLine(String.format("LineStyle: %s;", lineStyle)); + writer.writeLine(String.format("LineThickness: %s;", strokeWidth)); + if (shape == Shape.RoundedBox) { + writer.writeLine("RoundCorner: 20;"); + } + writer.writeLine(String.format("FontColor: %s;", color)); + writer.writeLine(String.format("FontSize: %s;", fontSize)); + writer.writeLine("HorizontalAlignment: center;"); + writer.writeLine(String.format("Shadowing: %s;", shadow ? SHADOW_DISTANCE : 0)); + writer.writeLine(String.format("MaximumWidth: %s;", width)); + + writer.outdent(); + writer.writeLine("}"); + writer.outdent(); + writer.writeLine(); + + return writer.toString(); + } +} \ No newline at end of file diff --git a/structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLGroupStyle.java b/structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLGroupStyle.java new file mode 100644 index 000000000..19507abff --- /dev/null +++ b/structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLGroupStyle.java @@ -0,0 +1,81 @@ +package com.structurizr.export.plantuml; + +import com.structurizr.export.IndentingWriter; +import com.structurizr.view.Border; + +import java.util.Base64; + +import static java.lang.String.format; + +class PlantUMLGroupStyle extends PlantUMLStyle { + + private final String background; + private final String color; + private final String stroke; + private final int strokeWidth; + private final String lineStyle; + private final int fontSize; + private final boolean shadow; + + PlantUMLGroupStyle(String name, String background, String color, String stroke, int strokeWidth, Border border, int fontSize, boolean shadow) { + super(name); + + this.background = background; + this.color = color; + this.stroke = stroke; + this.strokeWidth = strokeWidth; + + switch (border) { + case Dotted: + this.lineStyle = (strokeWidth) + "-" + (strokeWidth); + break; + case Dashed: + this.lineStyle = (strokeWidth * 5) + "-" + (strokeWidth * 5); + break; + default: + this.lineStyle = "0"; + break; + } + + this.fontSize = fontSize; + this.shadow = shadow; + } + + @Override + String getClassSelector() { + return "Group-" + Base64.getEncoder().encodeToString(name.getBytes()); + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + PlantUMLGroupStyle that = (PlantUMLGroupStyle) o; + return getClassSelector().equals(that.getClassSelector()); + } + + @Override + public String toString() { + IndentingWriter writer = new IndentingWriter(); + writer.indent(); + writer.writeLine(format("// %s", name)); + writer.writeLine(format(".%s {", getClassSelector())); + writer.indent(); + + writer.writeLine(String.format("BackgroundColor: %s;", background)); + writer.writeLine(String.format("LineColor: %s;", stroke)); + writer.writeLine(String.format("LineStyle: %s;", lineStyle)); + writer.writeLine(String.format("LineThickness: %s;", strokeWidth)); + writer.writeLine(String.format("FontColor: %s;", color)); + writer.writeLine(String.format("FontSize: %s;", fontSize)); + writer.writeLine("HorizontalAlignment: center;"); + writer.writeLine(String.format("Shadowing: %s;", shadow ? SHADOW_DISTANCE : 0)); + + writer.outdent(); + writer.writeLine("}"); + writer.outdent(); + writer.writeLine(); + + return writer.toString(); + } + +} \ No newline at end of file diff --git a/structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLLegendStyle.java b/structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLLegendStyle.java new file mode 100644 index 000000000..8274cb74e --- /dev/null +++ b/structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLLegendStyle.java @@ -0,0 +1,44 @@ +package com.structurizr.export.plantuml; + +import com.structurizr.export.IndentingWriter; + +import static java.lang.String.format; + +class PlantUMLLegendStyle extends PlantUMLStyle { + + PlantUMLLegendStyle() { + super("Element-Transparent"); + } + + @Override + String getClassSelector() { + return "Element-Transparent"; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + PlantUMLLegendStyle that = (PlantUMLLegendStyle) o; + return getClassSelector().equals(that.getClassSelector()); + } + + @Override + public String toString() { + IndentingWriter writer = new IndentingWriter(); + writer.indent(); + writer.writeLine("// transparent element for relationships in legend"); + writer.writeLine(format(".%s {", getClassSelector())); + writer.indent(); + + writer.writeLine("BackgroundColor: transparent;"); + writer.writeLine("LineColor: transparent;"); + writer.writeLine("FontColor: transparent;"); + + writer.outdent(); + writer.writeLine("}"); + writer.outdent(); + writer.writeLine(); + + return writer.toString(); + } +} \ No newline at end of file diff --git a/structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLRelationshipStyle.java b/structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLRelationshipStyle.java new file mode 100644 index 000000000..06379cd53 --- /dev/null +++ b/structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLRelationshipStyle.java @@ -0,0 +1,71 @@ +package com.structurizr.export.plantuml; + +import com.structurizr.export.IndentingWriter; +import com.structurizr.view.LineStyle; + +import java.util.Base64; + +import static java.lang.String.format; + +class PlantUMLRelationshipStyle extends PlantUMLStyle { + + private final String color; + private final String lineStyle; + private final int thickness; + private final int fontSize; + + PlantUMLRelationshipStyle(String name, String color, LineStyle lineStyle, int thickness, int fontSize) { + super(name); + + this.color = color; + this.thickness = thickness; + this.fontSize = fontSize; + + switch (lineStyle) { + case Dotted: + this.lineStyle = (thickness) + "-" + (thickness); + break; + case Dashed: + this.lineStyle = (thickness * 5) + "-" + (thickness * 5); + break; + default: + this.lineStyle = "0"; + break; + } + } + + @Override + String getClassSelector() { + return "Relationship-" + Base64.getEncoder().encodeToString(name.getBytes()); + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + PlantUMLRelationshipStyle that = (PlantUMLRelationshipStyle) o; + return getClassSelector().equals(that.getClassSelector()); + } + + @Override + public String toString() { + IndentingWriter writer = new IndentingWriter(); + writer.indent(); + writer.writeLine(format("// %s", name)); + writer.writeLine(format(".%s {", getClassSelector())); + writer.indent(); + + writer.writeLine(String.format("LineThickness: %s;", thickness)); + writer.writeLine(String.format("LineStyle: %s;", lineStyle)); + writer.writeLine(String.format("LineColor: %s;", color)); + writer.writeLine(String.format("FontColor: %s;", color)); + writer.writeLine(String.format("FontSize: %s;", fontSize)); + + writer.outdent(); + writer.writeLine("}"); + writer.outdent(); + writer.writeLine(); + + return writer.toString(); + } + +} \ No newline at end of file diff --git a/structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLRootStyle.java b/structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLRootStyle.java new file mode 100644 index 000000000..769b21584 --- /dev/null +++ b/structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLRootStyle.java @@ -0,0 +1,52 @@ +package com.structurizr.export.plantuml; + +import com.structurizr.export.IndentingWriter; +import com.structurizr.util.StringUtils; + +import static java.lang.String.format; + +class PlantUMLRootStyle extends PlantUMLStyle { + + private final String background; + private final String color; + private final String fontName; + + PlantUMLRootStyle(String background, String color, String fontName) { + super(".root"); + + this.background = background; + this.color = color; + this.fontName = fontName; + } + + @Override + String getClassSelector() { + return "root"; + } + + @Override + public boolean equals(Object o) { + return o != null && getClass() == o.getClass(); + } + + @Override + public String toString() { + IndentingWriter writer = new IndentingWriter(); + writer.indent(); + writer.writeLine(format("%s {", getClassSelector())); + writer.indent(); + + writer.writeLine(String.format("BackgroundColor: %s;", background)); + writer.writeLine(String.format("FontColor: %s;", color)); + if (!StringUtils.isNullOrEmpty(fontName)) { + writer.writeLine(String.format("FontName: %s;", fontName)); + } + + writer.outdent(); + writer.writeLine("}"); + writer.outdent(); + writer.writeLine(); + + return writer.toString(); + } +} \ No newline at end of file diff --git a/structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLStyle.java b/structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLStyle.java new file mode 100644 index 000000000..6eaccf324 --- /dev/null +++ b/structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLStyle.java @@ -0,0 +1,26 @@ +package com.structurizr.export.plantuml; + +import java.util.Objects; + +abstract class PlantUMLStyle { + + protected static final int SHADOW_DISTANCE = 10; + + protected final String name; + + PlantUMLStyle(String name) { + this.name = name; + } + + String getName() { + return name; + } + + abstract String getClassSelector(); + + @Override + public final int hashCode() { + return Objects.hashCode(getClassSelector()); + } + +} \ No newline at end of file diff --git a/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java b/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java index 230099a1c..6dccc103c 100644 --- a/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java +++ b/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java @@ -8,7 +8,6 @@ import com.structurizr.view.*; import java.util.*; -import java.util.stream.Collectors; import static java.lang.String.format; @@ -16,19 +15,24 @@ public class StructurizrPlantUMLExporter extends AbstractPlantUMLExporter { public static final String PLANTUML_SHADOW = "plantuml.shadow"; - private int groupId = 0; + private static final int DEFAULT_STROKE_WIDTH = 2; + private static final double METADATA_FONT_SIZE_RATIO = 0.7; + private static final int MAX_ICON_SIZE_RATIO = 3; + + private Set plantUMLStyles; public StructurizrPlantUMLExporter() { - addSkinParam("arrowFontSize", "10"); - addSkinParam("defaultTextAlignment", "center"); - addSkinParam("wrapWidth", "200"); - addSkinParam("maxMessageSize", "100"); + this(ColorScheme.Light); + } + + public StructurizrPlantUMLExporter(ColorScheme colorScheme) { + super(colorScheme); } @Override protected void writeHeader(ModelView view, IndentingWriter writer) { + plantUMLStyles = new HashSet<>(); super.writeHeader(view, writer); - groupId = 0; if (view instanceof DynamicView && renderAsSequenceDiagram(view)) { // do nothing @@ -51,104 +55,59 @@ protected void writeHeader(ModelView view, IndentingWriter writer) { } else { writer.writeLine("top to bottom direction"); } - - writer.writeLine(); } + writer.writeLine("hide stereotype"); + writer.writeLine(); + + writer.writeLine(""); + writer.writeLine(); + + String fontName = null; Font font = view.getViewSet().getConfiguration().getBranding().getFont(); if (font != null) { - String fontName = font.getName(); + fontName = font.getName(); if (!StringUtils.isNullOrEmpty(fontName)) { - addSkinParam("defaultFontName", "\"" + fontName + "\""); + writer.writeLine("FontName: " + fontName); } } + if (colorScheme == ColorScheme.Dark) { + plantUMLStyles.add(new PlantUMLRootStyle( + Styles.DEFAULT_BACKGROUND_DARK, + Styles.DEFAULT_COLOR_DARK, + fontName)); + } else { + plantUMLStyles.add(new PlantUMLRootStyle( + Styles.DEFAULT_BACKGROUND_LIGHT, + Styles.DEFAULT_COLOR_LIGHT, + fontName)); + } - writeSkinParams(writer); writeIncludes(view, writer); + } - writer.writeLine(); - writer.writeLine("hide stereotype"); - writer.writeLine(); - - List elements = view.getElements().stream().map(ElementView::getElement).sorted(Comparator.comparing(Element::getName)).collect(Collectors.toList()); - for (Element element : elements) { - String id = idOf(element); - - String type = plantUMLShapeOf(view, element); - if ("actor".equals(type)) { - type = "rectangle"; // the actor shape is not supported in this implementation - } - - ElementStyle elementStyle = findElementStyle(view, element); - - String background = elementStyle.getBackground(); - String stroke = elementStyle.getStroke(); - String color = elementStyle.getColor(); - Shape shape = elementStyle.getShape(); - - if (view instanceof DynamicView && renderAsSequenceDiagram(view)) { - type = "sequenceParticipant"; - } - - writer.writeLine(format("skinparam %s<<%s>> {", type, id)); - writer.indent(); - if (element instanceof DeploymentNode) { - writer.writeLine("BackgroundColor #ffffff"); - } else { - writer.writeLine(String.format("BackgroundColor %s", background)); - } - writer.writeLine(String.format("FontColor %s", color)); - writer.writeLine(String.format("BorderColor %s", stroke)); - - if (shape == Shape.RoundedBox) { - writer.writeLine("roundCorner 20"); - } - - boolean shadow = "true".equalsIgnoreCase(elementStyle.getProperties().getOrDefault(PLANTUML_SHADOW, "false")); - writer.writeLine(String.format("shadowing %s", shadow)); - - writer.outdent(); - writer.writeLine("}"); - } + @Override + protected void writeFooter(ModelView view, IndentingWriter writer) { + super.writeFooter(view, writer); + writeStyles(writer); + } - if (!renderAsSequenceDiagram(view)) { - // boundaries - List boundaryElements = new ArrayList<>(); - if (view instanceof ContainerView) { - boundaryElements.addAll(getBoundarySoftwareSystems(view)); - } else if (view instanceof ComponentView) { - boundaryElements.addAll(getBoundaryContainers(view)); - } else if (view instanceof DynamicView) { - DynamicView dynamicView = (DynamicView) view; - if (dynamicView.getElement() instanceof SoftwareSystem) { - boundaryElements.addAll(getBoundarySoftwareSystems(view)); - } else if (dynamicView.getElement() instanceof Container) { - boundaryElements.addAll(getBoundaryContainers(view)); - } - } + private void writeStyles(IndentingWriter writer) { + StringBuilder styles = new StringBuilder(); + List sortedStyles = plantUMLStyles.stream().sorted(Comparator.comparing(PlantUMLStyle::getName)).toList(); - for (Element boundaryElement : boundaryElements) { - ElementStyle elementStyle = view.getViewSet().getConfiguration().getStyles().findElementStyle(boundaryElement); - String id = idOf(boundaryElement); - String color = elementStyle.getStroke(); - boolean shadow = "true".equalsIgnoreCase(elementStyle.getProperties().getOrDefault(PLANTUML_SHADOW, "false")); - - writer.writeLine(format("skinparam rectangle<<%s>> {", id)); - writer.indent(); - writer.writeLine(String.format("BorderColor %s", color)); - writer.writeLine(String.format("FontColor %s", color)); - writer.writeLine(String.format("shadowing %s", shadow)); - writer.outdent(); - writer.writeLine("}"); - } - } + sortedStyles.stream().filter(style -> style instanceof PlantUMLRootStyle).forEach(style -> styles.append(style.toString())); + sortedStyles.stream().filter(style -> style instanceof PlantUMLElementStyle).forEach(style -> styles.append(style.toString())); + sortedStyles.stream().filter(style -> style instanceof PlantUMLRelationshipStyle).forEach(style -> styles.append(style.toString())); + sortedStyles.stream().filter(style -> style instanceof PlantUMLBoundaryStyle).forEach(style -> styles.append(style.toString())); + sortedStyles.stream().filter(style -> style instanceof PlantUMLGroupStyle).forEach(style -> styles.append(style.toString())); + sortedStyles.stream().filter(style -> style instanceof PlantUMLLegendStyle).forEach(style -> styles.append(style.toString())); - writer.writeLine(); + writer.replace("", ""); } @Override protected void startGroupBoundary(ModelView view, String group, IndentingWriter writer) { - groupId++; String groupName = group; String groupSeparator = view.getModel().getProperties().get(GROUP_SEPARATOR_PROPERTY_NAME); @@ -156,37 +115,37 @@ protected void startGroupBoundary(ModelView view, String group, IndentingWriter groupName = group.substring(group.lastIndexOf(groupSeparator) + groupSeparator.length()); } - if (!renderAsSequenceDiagram(view)) { - String color = "#cccccc"; - String icon = ""; - - ElementStyle elementStyleForGroup = view.getViewSet().getConfiguration().getStyles().findElementStyle("Group:" + group); - ElementStyle elementStyleForAllGroups = view.getViewSet().getConfiguration().getStyles().findElementStyle("Group"); - - if (elementStyleForGroup != null && !StringUtils.isNullOrEmpty(elementStyleForGroup.getColor())) { - color = elementStyleForGroup.getColor(); - } else if (elementStyleForAllGroups != null && !StringUtils.isNullOrEmpty(elementStyleForAllGroups.getColor())) { - color = elementStyleForAllGroups.getColor(); - } - - if (elementStyleForGroup != null && elementStyleHasSupportedIcon(elementStyleForGroup)) { - icon = elementStyleForGroup.getIcon(); - } else if (elementStyleForAllGroups != null && elementStyleHasSupportedIcon(elementStyleForAllGroups)) { - icon = elementStyleForAllGroups.getColor(); - } + ElementStyle elementStyle = findGroupStyle(view, group); + PlantUMLGroupStyle plantUMLBoundaryStyle = new PlantUMLGroupStyle( + group, + elementStyle.getBackground(), + elementStyle.getColor(), + elementStyle.getStroke(), + elementStyle.getStrokeWidth() != null ? elementStyle.getStrokeWidth() : DEFAULT_STROKE_WIDTH, + elementStyle.getBorder(), + elementStyle.getFontSize(), + "true".equalsIgnoreCase(elementStyle.getProperties().getOrDefault(PLANTUML_SHADOW, "false")) + ); + plantUMLStyles.add(plantUMLBoundaryStyle); + if (!renderAsSequenceDiagram(view)) { + String icon = elementStyle.getIcon(); if (!StringUtils.isNullOrEmpty(icon)) { - double scale = calculateIconScale(icon); + double scale = calculateIconScale(icon, elementStyle.getFontSize() * MAX_ICON_SIZE_RATIO); icon = "\\n\\n"; + } else { + icon = ""; } - writer.writeLine(String.format("rectangle \"%s%s\" <> as group%s {", groupName, icon, groupId, groupId)); + writer.writeLine( + String.format( + "rectangle \"%s%s\" <<%s>> as group%s {", + groupName, + icon, + classSelectorForGroup(group), + Base64.getEncoder().encodeToString(group.getBytes())) + ); writer.indent(); - writer.writeLine(String.format("skinparam RectangleBorderColor<> %s", groupId, color)); - writer.writeLine(String.format("skinparam RectangleFontColor<> %s", groupId, color)); - writer.writeLine(String.format("skinparam RectangleBorderStyle<> dashed", groupId)); - - writer.writeLine(); } } @@ -202,7 +161,28 @@ protected void endGroupBoundary(ModelView view, IndentingWriter writer) { @Override protected void startSoftwareSystemBoundary(ModelView view, SoftwareSystem softwareSystem, IndentingWriter writer) { if (!renderAsSequenceDiagram(view)) { - writer.writeLine(String.format("rectangle \"%s\\n%s\" <<%s>> {", softwareSystem.getName(), typeOf(view, softwareSystem, true), idOf(softwareSystem))); + ElementStyle elementStyle = findBoundaryStyle(view, softwareSystem); + PlantUMLBoundaryStyle plantUMLBoundaryStyle = new PlantUMLBoundaryStyle( + softwareSystem.getName(), + elementStyle.getBackground(), + elementStyle.getColor(), + elementStyle.getStroke(), + elementStyle.getStrokeWidth() != null ? elementStyle.getStrokeWidth() : DEFAULT_STROKE_WIDTH, + elementStyle.getBorder(), + elementStyle.getFontSize(), + "true".equalsIgnoreCase(elementStyle.getProperties().getOrDefault(PLANTUML_SHADOW, "false")) + ); + plantUMLStyles.add(plantUMLBoundaryStyle); + + writer.writeLine( + String.format( + "rectangle \"%s\\n%s\" <<%s>> {", + softwareSystem.getName(), + calculateMetadataFontSize(elementStyle.getFontSize()), + typeOf(view, softwareSystem, true), + plantUMLBoundaryStyle.getClassSelector() + ) + ); writer.indent(); } } @@ -219,7 +199,25 @@ protected void endSoftwareSystemBoundary(ModelView view, IndentingWriter writer) @Override protected void startContainerBoundary(ModelView view, Container container, IndentingWriter writer) { if (!renderAsSequenceDiagram(view)) { - writer.writeLine(String.format("rectangle \"%s\\n%s\" <<%s>> {", container.getName(), typeOf(view, container, true), idOf(container))); + ElementStyle elementStyle = findBoundaryStyle(view, container); + PlantUMLBoundaryStyle plantUMLBoundaryStyle = new PlantUMLBoundaryStyle( + container.getName(), + elementStyle.getBackground(), + elementStyle.getColor(), + elementStyle.getStroke(), + elementStyle.getStrokeWidth() != null ? elementStyle.getStrokeWidth() : DEFAULT_STROKE_WIDTH, + elementStyle.getBorder(), + elementStyle.getFontSize(), + "true".equalsIgnoreCase(elementStyle.getProperties().getOrDefault(PLANTUML_SHADOW, "false")) + ); + plantUMLStyles.add(plantUMLBoundaryStyle); + + writer.writeLine( + String.format( + "rectangle \"%s\\n%s\" <<%s>> {", + container.getName(), + calculateMetadataFontSize(findBoundaryStyle(view, container).getFontSize()), typeOf(view, container, true), + plantUMLBoundaryStyle.getClassSelector())); writer.indent(); } } @@ -237,9 +235,24 @@ protected void endContainerBoundary(ModelView view, IndentingWriter writer) { protected void startDeploymentNodeBoundary(DeploymentView view, DeploymentNode deploymentNode, IndentingWriter writer) { ElementStyle elementStyle = findElementStyle(view, deploymentNode); + PlantUMLElementStyle plantUMLElementStyle = new PlantUMLElementStyle( + elementStyle.getTag(), + elementStyle.getShape(), + elementStyle.getWidth(), + elementStyle.getBackground(), + elementStyle.getColor(), + elementStyle.getStroke(), + elementStyle.getStrokeWidth() != null ? elementStyle.getStrokeWidth() : DEFAULT_STROKE_WIDTH, + elementStyle.getBorder(), + elementStyle.getFontSize(), + elementStyle.getIcon(), + "true".equalsIgnoreCase(elementStyle.getProperties().getOrDefault(PLANTUML_SHADOW, "false")) + ); + plantUMLStyles.add(plantUMLElementStyle); + String icon = ""; - if (elementStyleHasSupportedIcon(elementStyle)) { - double scale = calculateIconScale(elementStyle.getIcon()); + if (isSupportedIcon(elementStyle.getIcon())) { + double scale = calculateIconScale(elementStyle.getIcon(), elementStyle.getFontSize() * MAX_ICON_SIZE_RATIO); icon = "\\n\\n"; } @@ -251,11 +264,13 @@ protected void startDeploymentNodeBoundary(DeploymentView view, DeploymentNode d } writer.writeLine( - format("rectangle \"%s\\n%s%s\" <<%s>> as %s%s {", + format( + "rectangle \"%s\\n%s%s\" <<%s>> as %s%s {", deploymentNode.getName() + (!"1".equals(deploymentNode.getInstances()) ? " (x" + deploymentNode.getInstances() + ")" : ""), + calculateMetadataFontSize(findBoundaryStyle(view, deploymentNode).getFontSize()), typeOf(view, deploymentNode, true), icon, - idOf(deploymentNode), + classSelectorFor(elementStyle), idOf(deploymentNode), url ) @@ -290,10 +305,17 @@ public Diagram export(DynamicView view) { writeElement(view, element, writer); } + if (!elements.isEmpty()) { + writer.writeLine(); + } + writeRelationships(view, writer); writeFooter(view, writer); - return createDiagram(view, writer.toString()); + Diagram diagram = createDiagram(view, writer.toString()); + diagram.setLegend(createLegend(view)); + + return diagram; } else { return super.export(view); } @@ -303,19 +325,34 @@ public Diagram export(DynamicView view) { protected void writeElement(ModelView view, Element element, IndentingWriter writer) { ElementStyle elementStyle = findElementStyle(view, element); + PlantUMLElementStyle plantUMLElementStyle = new PlantUMLElementStyle( + elementStyle.getTag(), + elementStyle.getShape(), + elementStyle.getWidth(), + elementStyle.getBackground(), + elementStyle.getColor(), + elementStyle.getStroke(), + elementStyle.getStrokeWidth() != null ? elementStyle.getStrokeWidth() : DEFAULT_STROKE_WIDTH, + elementStyle.getBorder(), + elementStyle.getFontSize(), + elementStyle.getIcon(), + "true".equalsIgnoreCase(elementStyle.getProperties().getOrDefault(PLANTUML_SHADOW, "false")) + ); + plantUMLStyles.add(plantUMLElementStyle); + + int metadataFontSize = calculateMetadataFontSize(elementStyle.getFontSize()); + if (view instanceof DynamicView && renderAsSequenceDiagram(view)) { - writer.writeLine(String.format("%s \"%s\\n%s\" as %s <<%s>> %s", + writer.writeLine(String.format("%s \"%s\\n%s\" as %s <<%s>> %s", plantumlSequenceType(view, element), element.getName(), + metadataFontSize, typeOf(view, element, true), idOf(element), - idOf(element), + plantUMLElementStyle.getClassSelector(), elementStyle.getBackground())); } else { String shape = plantUMLShapeOf(view, element); - if ("actor".equals(shape)) { - shape = "rectangle"; - } String name = element.getName(); String description = element.getDescription(); String type = typeOf(view, element, true); @@ -350,23 +387,24 @@ protected void writeElement(ModelView view, Element element, IndentingWriter wri if (StringUtils.isNullOrEmpty(type) || false == elementStyle.getMetadata()) { type = ""; } else { - type = String.format("\\n%s", type); + type = String.format("\\n%s", metadataFontSize, type); } - if (elementStyleHasSupportedIcon(elementStyle)) { - double scale = calculateIconScale(elementStyle.getIcon()); + if (isSupportedIcon(elementStyle.getIcon())) { + double scale = calculateIconScale(elementStyle.getIcon(), elementStyle.getFontSize() * MAX_ICON_SIZE_RATIO); icon = "\\n\\n"; } + String classSelector = plantUMLElementStyle.getClassSelector(); String id = idOf(element); writer.writeLine(format("%s \"==%s%s%s%s\" <<%s>> as %s%s", shape, name, type, - description, icon, - id, + description, + classSelector, id, url) ); @@ -382,6 +420,15 @@ protected void writeRelationship(ModelView view, RelationshipView relationshipVi Relationship relationship = relationshipView.getRelationship(); RelationshipStyle style = findRelationshipStyle(view, relationship); + PlantUMLRelationshipStyle plantUMLRelationshipStyle = new PlantUMLRelationshipStyle( + style.getTag(), + style.getColor(), + style.getStyle(), + style.getThickness(), + style.getFontSize() + ); + plantUMLStyles.add(plantUMLRelationshipStyle); + String description = ""; String technology = relationship.getTechnology(); @@ -405,46 +452,36 @@ protected void writeRelationship(ModelView view, RelationshipView relationshipVi } writer.writeLine( - String.format("%s %s[%s]%s %s : %s", + String.format("%s %s%s %s <<%s>> : %s", idOf(relationship.getSource()), arrowStart, - style.getColor(), arrowEnd, idOf(relationship.getDestination()), + plantUMLRelationshipStyle.getClassSelector(), description)); } else { - boolean solid = style.getStyle() == LineStyle.Solid || false == style.getDashed(); - - String arrowStart; - String arrowEnd; - String relationshipStyle = style.getColor(); - - if (style.getThickness() != null) { - relationshipStyle += ",thickness=" + style.getThickness(); - } + String arrow; if (relationshipView.isResponse() != null && relationshipView.isResponse()) { - arrowStart = solid ? "<-" : "<."; - arrowEnd = solid ? "-" : "."; + arrow = "<--"; } else { - arrowStart = solid ? "-" : "."; - arrowEnd = solid ? "->" : ".>"; + arrow = "-->"; } - if (!isVisible(view, relationshipView)) { - relationshipStyle = "hidden"; - } +// if (!isVisible(view, relationshipView)) { +// relationshipStyle = "hidden"; +// } + + int metadataFontSize = calculateMetadataFontSize(style.getFontSize()); - // 1 .[#rrggbb,thickness=n].> 2 : "...\n... - writer.writeLine(format("%s %s[%s]%s %s : \"%s%s\"", + // 1 --> 2 : "...\n... + writer.writeLine(format("%s %s %s <<%s>> : \"%s%s\"", idOf(relationship.getSource()), - arrowStart, - relationshipStyle, - arrowEnd, + arrow, idOf(relationship.getDestination()), - style.getColor(), + plantUMLRelationshipStyle.getClassSelector(), description, - (StringUtils.isNullOrEmpty(technology) ? "" : "\\n[" + technology + "]") + (StringUtils.isNullOrEmpty(technology) ? "" : "\\n[" + technology + "]") )); } } @@ -452,156 +489,66 @@ protected void writeRelationship(ModelView view, RelationshipView relationshipVi @Override protected Legend createLegend(ModelView view) { IndentingWriter writer = new IndentingWriter(); - int id = 0; writer.writeLine("@startuml"); - writer.writeLine("set separator none"); writer.writeLine(); - - writer.writeLine("skinparam {"); - writer.indent(); - writer.writeLine("shadowing false"); - writer.writeLine("arrowFontSize 15"); - writer.writeLine("defaultTextAlignment center"); - writer.writeLine("wrapWidth 100"); - writer.writeLine("maxMessageSize 100"); - Font font = view.getViewSet().getConfiguration().getBranding().getFont(); - if (font != null) { - String fontName = font.getName(); - if (!StringUtils.isNullOrEmpty(fontName)) { - writer.writeLine("defaultFontName \"" + fontName + "\""); - } - } - writer.outdent(); - writer.writeLine("}"); - + writer.writeLine("set separator none"); writer.writeLine("hide stereotype"); writer.writeLine(); - writer.writeLine("skinparam rectangle<<_transparent>> {"); - writer.indent(); - writer.writeLine("BorderColor transparent"); - writer.writeLine("BackgroundColor transparent"); - writer.writeLine("FontColor transparent"); - writer.outdent(); - writer.writeLine("}"); + writer.writeLine(""); writer.writeLine(); - Map elementStyles = new HashMap<>(); - List elements = view.getElements().stream().map(ElementView::getElement).collect(Collectors.toList()); - for (Element element : elements) { - ElementStyle elementStyle = findElementStyle(view, element); - - if (element instanceof DeploymentNode) { - // deployment node backgrounds are always white - elementStyle.setBackground("#ffffff"); - } - - if (!StringUtils.isNullOrEmpty(elementStyle.getTag()) ) { - elementStyles.put(elementStyle.getTag(), elementStyle); - }; - } - - List sortedElementStyles = elementStyles.values().stream().sorted(Comparator.comparing(ElementStyle::getTag)).collect(Collectors.toList());; - for (ElementStyle elementStyle : sortedElementStyles) { - id++; - Shape shape = elementStyle.getShape(); - String type = plantUMLShapeOf(elementStyle.getShape()); - if ("actor".equals(type)) { - type = "rectangle"; // the actor shape is not supported in this implementation - } - - String background = elementStyle.getBackground(); - String stroke = elementStyle.getStroke(); - String color = elementStyle.getColor(); - - if (view instanceof DynamicView && renderAsSequenceDiagram(view)) { - type = "sequenceParticipant"; - } - - writer.writeLine(format("skinparam %s<<%s>> {", type, id)); - writer.indent(); - writer.writeLine(String.format("BackgroundColor %s", background)); - writer.writeLine(String.format("FontColor %s", color)); - writer.writeLine(String.format("BorderColor %s", stroke)); - - if (shape == Shape.RoundedBox) { - writer.writeLine("roundCorner 20"); - } - writer.outdent(); - writer.writeLine("}"); - - String description = elementStyle.getTag(); + plantUMLStyles.stream().sorted(Comparator.comparing(PlantUMLStyle::getName)).filter(style -> style instanceof PlantUMLElementStyle).map(style -> (PlantUMLElementStyle)style).forEach(style -> { + style.setWidth(200); + String description = style.getName(); if (description.startsWith("Element,")) { description = description.substring("Element,".length()); } description = description.replaceAll(",", ", "); String icon = ""; - if (elementStyleHasSupportedIcon(elementStyle)) { - double scale = calculateIconScale(elementStyle.getIcon()); - icon = "\\n\\n"; + if (isSupportedIcon(style.getIcon())) { + double scale = calculateIconScale(style.getIcon(), style.getFontSize() * MAX_ICON_SIZE_RATIO); + icon = "\\n\\n"; } writer.writeLine(format("%s \"==%s%s\" <<%s>>", - type, + plantUMLShapeOf(style.getShape()), description, icon, - id) + style.getClassSelector()) ); writer.writeLine(); - } - - Map relationshipStyles = new HashMap<>(); - List relationships = view.getRelationships().stream().map(RelationshipView::getRelationship).collect(Collectors.toList()); - for (Relationship relationship : relationships) { - RelationshipStyle relationshipStyle = findRelationshipStyle(view, relationship); + }); - if (!StringUtils.isNullOrEmpty(relationshipStyle.getTag())) { - relationshipStyles.put(relationshipStyle.getTag(), relationshipStyle); - } - } - - List sortedRelationshipStyles = relationshipStyles.values().stream().sorted(Comparator.comparing(RelationshipStyle::getTag)).collect(Collectors.toList());; - for (RelationshipStyle relationshipStyle : sortedRelationshipStyles) { + int id = 0; + List relationshipStyles = plantUMLStyles.stream().sorted(Comparator.comparing(PlantUMLStyle::getName)).filter(style -> style instanceof PlantUMLRelationshipStyle).map(style -> (PlantUMLRelationshipStyle)style).toList(); + for (PlantUMLRelationshipStyle relationshipStyle : relationshipStyles) { id++; - - String description = relationshipStyle.getTag(); + String description = relationshipStyle.getName(); if (description.startsWith("Relationship,")) { description = description.substring("Relationship,".length()); } description = description.replaceAll(",", ", "); - writer.writeLine(format("rectangle \".\" <<_transparent>> as %s", id)); - - boolean solid = relationshipStyle.getStyle() == LineStyle.Solid || false == relationshipStyle.getDashed(); - - String arrowStart = solid ? "-" : "."; - String arrowEnd = solid ? "->" : ".>"; - String buf = relationshipStyle.getColor(); - - if (relationshipStyle.getThickness() != null) { - buf += ",thickness=" + relationshipStyle.getThickness(); - } - - // 1 .[#rrggbb,thickness=n].> 2 : "..." - writer.writeLine(format("%s %s[%s]%s %s : \"%s\"", + // id --> id : "..." + writer.writeLine(format("rectangle \".\" <<.Element-Transparent>> as %s", id)); + writer.writeLine(format("%s --> %s <<%s>> : \"%s\"", id, - arrowStart, - buf, - arrowEnd, id, - relationshipStyle.getColor(), + relationshipStyle.getClassSelector(), description) ); writer.writeLine(); - } - - writer.writeLine(); + }; writer.writeLine("@enduml"); + plantUMLStyles.add(new PlantUMLLegendStyle()); + writeStyles(writer); + return new Legend(writer.toString()); } @@ -609,4 +556,101 @@ protected boolean renderAsSequenceDiagram(ModelView view) { return view instanceof DynamicView && "true".equalsIgnoreCase(getViewOrViewSetProperty(view, PLANTUML_SEQUENCE_DIAGRAM_PROPERTY, "false")); } -} + private String classSelectorFor(ElementStyle elementStyle) { + return "Element-" + Base64.getEncoder().encodeToString(elementStyle.getTag().getBytes()); + } + + private String classSelectorForBoundary(Element element) { + return "Boundary-" + Base64.getEncoder().encodeToString(element.getName().getBytes()); + } + + private ElementStyle findBoundaryStyle(ModelView view, Element element) { + return findElementStyle(view, element); + } + + private String classSelectorForGroup(String group) { + return "Group-" + Base64.getEncoder().encodeToString(group.getBytes()); + } + + private ElementStyle findGroupStyle(ModelView view, String group) { + String background = colorScheme == ColorScheme.Dark ? Styles.DEFAULT_BACKGROUND_DARK : Styles.DEFAULT_BACKGROUND_LIGHT; + String stroke = colorScheme == ColorScheme.Dark ? Styles.DEFAULT_COLOR_DARK : Styles.DEFAULT_COLOR_LIGHT; + int strokeWidth = DEFAULT_STROKE_WIDTH; + Border border = Border.Dotted; + String color = colorScheme == ColorScheme.Dark ? Styles.DEFAULT_COLOR_DARK : Styles.DEFAULT_COLOR_LIGHT; + String icon = ""; + int fontSize = DEFAULT_FONT_SIZE; + + ElementStyle style = new ElementStyle(""); + ElementStyle elementStyleForGroup = findElementStyle(view, "Group:" + group); + ElementStyle elementStyleForAllGroups = findElementStyle(view, "Group"); + + if (elementStyleForGroup != null && !StringUtils.isNullOrEmpty(elementStyleForGroup.getBackground())) { + background = elementStyleForGroup.getBackground(); + } else if (elementStyleForAllGroups != null && !StringUtils.isNullOrEmpty(elementStyleForAllGroups.getBackground())) { + background = elementStyleForAllGroups.getBackground(); + } + style.setBackground(background); + + if (elementStyleForGroup != null && !StringUtils.isNullOrEmpty(elementStyleForGroup.getStroke())) { + stroke = elementStyleForGroup.getStroke(); + } else if (elementStyleForAllGroups != null && !StringUtils.isNullOrEmpty(elementStyleForAllGroups.getStroke())) { + stroke = elementStyleForAllGroups.getStroke(); + } + style.setStroke(stroke); + + if (elementStyleForGroup != null && elementStyleForGroup.getStrokeWidth() != null) { + strokeWidth = elementStyleForGroup.getStrokeWidth(); + } else if (elementStyleForAllGroups != null && elementStyleForAllGroups.getStrokeWidth() != null) { + strokeWidth = elementStyleForAllGroups.getStrokeWidth(); + } + style.setStrokeWidth(strokeWidth); + + if (elementStyleForGroup != null && !StringUtils.isNullOrEmpty(elementStyleForGroup.getColor())) { + color = elementStyleForGroup.getColor(); + } else if (elementStyleForAllGroups != null && !StringUtils.isNullOrEmpty(elementStyleForAllGroups.getColor())) { + color = elementStyleForAllGroups.getColor(); + } + style.setColor(color); + + if (elementStyleForGroup != null && elementStyleForGroup.getBorder() != null) { + border = elementStyleForGroup.getBorder(); + } else if (elementStyleForAllGroups != null && elementStyleForAllGroups.getBorder() != null) { + border = elementStyleForAllGroups.getBorder(); + } + style.setBorder(border); + + if (elementStyleForGroup != null && isSupportedIcon(elementStyleForGroup.getIcon())) { + icon = elementStyleForGroup.getIcon(); + } else if (elementStyleForAllGroups != null && isSupportedIcon(elementStyleForAllGroups.getIcon())) { + icon = elementStyleForAllGroups.getColor(); + } + style.setIcon(icon); + + if (elementStyleForGroup != null && elementStyleForGroup.getFontSize() != null) { + fontSize = elementStyleForGroup.getFontSize(); + } else if (elementStyleForAllGroups != null && elementStyleForGroup.getFontSize() != null) { + fontSize = elementStyleForAllGroups.getFontSize(); + } + style.setFontSize(fontSize); + + return style; + } + + private int calculateMetadataFontSize(int fontSize) { + return (int)Math.floor(fontSize * METADATA_FONT_SIZE_RATIO); + } + + private String toLineStyle(ElementStyle elementStyle) { + int strokeWidth = elementStyle.getStrokeWidth() != null ? elementStyle.getStrokeWidth() : DEFAULT_STROKE_WIDTH; + switch (elementStyle.getBorder()) { + case Dotted: + return (strokeWidth * 1) + "-" + (strokeWidth * 1); + case Dashed: + return (strokeWidth * 5) + "-" + (strokeWidth * 5); + default: + return "0"; + } + } + +} \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/36141-Components.dot b/structurizr-export/src/test/java/com/structurizr/export/dot/36141-Components.dot deleted file mode 100644 index 679e725e2..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/dot/36141-Components.dot +++ /dev/null @@ -1,43 +0,0 @@ -digraph { - compound=true - graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] - node [fontname="Arial", shape=box, margin="0.4,0.3"] - edge [fontname="Arial"] - label=<
    Internet Banking System - API Application - Components
    The component diagram for the API Application.> - - 4 [id=4,shape=rect, label=<Mainframe Banking
    System

    [Software System]

    Stores all of the core banking
    information about customers,
    accounts, transactions, etc.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] - 5 [id=5,shape=rect, label=<E-mail System
    [Software System]

    The internal Microsoft
    Exchange e-mail system.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] - 8 [id=8,shape=rect, label=<Single-Page
    Application

    [Container: JavaScript and Angular]

    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] - 9 [id=9,shape=rect, label=<Mobile App
    [Container: Xamarin]

    Provides a limited subset of
    the Internet banking
    functionality to customers via
    their mobile device.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] - 18 [id=18,shape=cylinder, label=<Database
    [Container: Oracle Database Schema]

    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] - - subgraph cluster_11 { - margin=25 - label=<
    API Application

    [Container: Java and Spring MVC]> - labelloc=b - color="#444444" - fontcolor="#444444" - fillcolor="#444444" - - 12 [id=12,shape=rect, label=<Sign In Controller
    [Component: Spring MVC Rest Controller]

    Allows users to sign in to the
    Internet Banking System.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] - 13 [id=13,shape=rect, label=<Accounts Summary
    Controller

    [Component: Spring MVC Rest Controller]

    Provides customers with a
    summary of their bank
    accounts.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] - 14 [id=14,shape=rect, label=<Reset Password
    Controller

    [Component: Spring MVC Rest Controller]

    Allows users to reset their
    passwords with a single use
    URL.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] - 15 [id=15,shape=rect, label=<Security Component
    [Component: Spring Bean]

    Provides functionality related
    to signing in, changing
    passwords, etc.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] - 16 [id=16,shape=rect, label=<Mainframe Banking
    System Facade

    [Component: Spring Bean]

    A facade onto the mainframe
    banking system.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] - 17 [id=17,shape=rect, label=<E-mail Component
    [Component: Spring Bean]

    Sends e-mails to users.>, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] - } - - 8 -> 12 [id=32, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] - 8 -> 13 [id=34, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] - 8 -> 14 [id=35, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] - 9 -> 12 [id=36, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] - 9 -> 13 [id=38, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] - 9 -> 14 [id=39, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] - 12 -> 15 [id=40, label=<Uses>, style="dashed", color="#707070", fontcolor="#707070"] - 13 -> 16 [id=41, label=<Uses>, style="dashed", color="#707070", fontcolor="#707070"] - 14 -> 15 [id=42, label=<Uses>, style="dashed", color="#707070", fontcolor="#707070"] - 14 -> 17 [id=43, label=<Uses>, style="dashed", color="#707070", fontcolor="#707070"] - 15 -> 18 [id=44, label=<Reads from and writes to
    [SQL/TCP]>, style="dashed", color="#707070", fontcolor="#707070"] - 16 -> 4 [id=46, label=<Makes API calls to
    [XML/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] - 17 -> 5 [id=48, label=<Sends e-mail using>, style="dashed", color="#707070", fontcolor="#707070"] -} \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/36141-Containers.dot b/structurizr-export/src/test/java/com/structurizr/export/dot/36141-Containers.dot deleted file mode 100644 index a0deff955..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/dot/36141-Containers.dot +++ /dev/null @@ -1,37 +0,0 @@ -digraph { - compound=true - graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] - node [fontname="Arial", shape=box, margin="0.4,0.3"] - edge [fontname="Arial"] - label=<
    Internet Banking System - Containers
    The container diagram for the Internet Banking System.> - - 1 [id=1,shape=rect, label=<Personal Banking
    Customer

    [Person]

    A customer of the bank, with
    personal bank accounts.
    >, style=filled, color="#052e56", fillcolor="#08427b", fontcolor="#ffffff"] - 4 [id=4,shape=rect, label=<Mainframe Banking
    System

    [Software System]

    Stores all of the core banking
    information about customers,
    accounts, transactions, etc.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] - 5 [id=5,shape=rect, label=<E-mail System
    [Software System]

    The internal Microsoft
    Exchange e-mail system.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] - - subgraph cluster_7 { - margin=25 - label=<
    Internet Banking System

    [Software System]> - labelloc=b - color="#444444" - fontcolor="#444444" - fillcolor="#444444" - - 10 [id=10,shape=rect, label=<Web Application
    [Container: Java and Spring MVC]

    Delivers the static content
    and the Internet banking
    single page application.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] - 11 [id=11,shape=rect, label=<API Application
    [Container: Java and Spring MVC]

    Provides Internet banking
    functionality via a JSON/HTTPS
    API.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] - 18 [id=18,shape=cylinder, label=<Database
    [Container: Oracle Database Schema]

    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] - 8 [id=8,shape=rect, label=<Single-Page
    Application

    [Container: JavaScript and Angular]

    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] - 9 [id=9,shape=rect, label=<Mobile App
    [Container: Xamarin]

    Provides a limited subset of
    the Internet banking
    functionality to customers via
    their mobile device.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] - } - - 5 -> 1 [id=22, label=<Sends e-mails to>, style="dashed", color="#707070", fontcolor="#707070"] - 1 -> 10 [id=28, label=<Visits bigbank.com/ib
    using

    [HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] - 1 -> 8 [id=29, label=<Views account balances,
    and makes payments using
    >, style="dashed", color="#707070", fontcolor="#707070"] - 1 -> 9 [id=30, label=<Views account balances,
    and makes payments using
    >, style="dashed", color="#707070", fontcolor="#707070"] - 10 -> 8 [id=31, label=<Delivers to the customer's
    web browser
    >, style="dashed", color="#707070", fontcolor="#707070"] - 8 -> 11 [id=33, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] - 9 -> 11 [id=37, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] - 11 -> 18 [id=45, label=<Reads from and writes to
    [SQL/TCP]>, style="dashed", color="#707070", fontcolor="#707070"] - 11 -> 4 [id=47, label=<Makes API calls to
    [XML/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] - 11 -> 5 [id=49, label=<Sends e-mail using>, style="dashed", color="#707070", fontcolor="#707070"] -} \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/36141-DevelopmentDeployment.dot b/structurizr-export/src/test/java/com/structurizr/export/dot/36141-DevelopmentDeployment.dot deleted file mode 100644 index 784eaeae4..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/dot/36141-DevelopmentDeployment.dot +++ /dev/null @@ -1,97 +0,0 @@ -digraph { - compound=true - graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] - node [fontname="Arial", shape=box, margin="0.4,0.3"] - edge [fontname="Arial"] - label=<
    Internet Banking System - Deployment - Development
    An example development deployment scenario for the Internet Banking System.> - - subgraph cluster_50 { - margin=25 - label=<Developer Laptop
    [Deployment Node: Microsoft Windows 10 or Apple macOS]> - labelloc=b - color="#888888" - fontcolor="#000000" - fillcolor="#ffffff" - - subgraph cluster_51 { - margin=25 - label=<Web Browser
    [Deployment Node: Chrome, Firefox, Safari, or Edge]> - labelloc=b - color="#888888" - fontcolor="#000000" - fillcolor="#ffffff" - - 52 [id=52,shape=rect, label=<Single-Page
    Application

    [Container: JavaScript and Angular]

    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] - } - - subgraph cluster_53 { - margin=25 - label=<Docker Container - Web Server
    [Deployment Node: Docker]> - labelloc=b - color="#888888" - fontcolor="#000000" - fillcolor="#ffffff" - - subgraph cluster_54 { - margin=25 - label=<Apache Tomcat
    [Deployment Node: Apache Tomcat 8.x]> - labelloc=b - color="#888888" - fontcolor="#000000" - fillcolor="#ffffff" - - 55 [id=55,shape=rect, label=<Web Application
    [Container: Java and Spring MVC]

    Delivers the static content
    and the Internet banking
    single page application.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] - 57 [id=57,shape=rect, label=<API Application
    [Container: Java and Spring MVC]

    Provides Internet banking
    functionality via a JSON/HTTPS
    API.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] - } - - } - - subgraph cluster_59 { - margin=25 - label=<Docker Container - Database Server
    [Deployment Node: Docker]> - labelloc=b - color="#888888" - fontcolor="#000000" - fillcolor="#ffffff" - - subgraph cluster_60 { - margin=25 - label=<Database Server
    [Deployment Node: Oracle 12c]> - labelloc=b - color="#888888" - fontcolor="#000000" - fillcolor="#ffffff" - - 61 [id=61,shape=cylinder, label=<Database
    [Container: Oracle Database Schema]

    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] - } - - } - - } - - subgraph cluster_63 { - margin=25 - label=<Big Bank plc
    [Deployment Node: Big Bank plc data center]> - labelloc=b - color="#888888" - fontcolor="#000000" - fillcolor="#ffffff" - - subgraph cluster_64 { - margin=25 - label=<bigbank-dev001
    [Deployment Node]> - labelloc=b - color="#888888" - fontcolor="#000000" - fillcolor="#ffffff" - - 65 [id=65,shape=rect, label=<Mainframe Banking
    System

    [Software System]

    Stores all of the core banking
    information about customers,
    accounts, transactions, etc.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] - } - - } - - 55 -> 52 [id=56, label=<Delivers to the customer's
    web browser
    >, style="dashed", color="#707070", fontcolor="#707070"] - 52 -> 57 [id=58, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] - 57 -> 61 [id=62, label=<Reads from and writes to
    [SQL/TCP]>, style="dashed", color="#707070", fontcolor="#707070"] - 57 -> 65 [id=66, label=<Makes API calls to
    [XML/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] -} \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/36141-LiveDeployment.dot b/structurizr-export/src/test/java/com/structurizr/export/dot/36141-LiveDeployment.dot deleted file mode 100644 index 5ac04100c..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/dot/36141-LiveDeployment.dot +++ /dev/null @@ -1,152 +0,0 @@ -digraph { - compound=true - graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] - node [fontname="Arial", shape=box, margin="0.4,0.3"] - edge [fontname="Arial"] - label=<
    Internet Banking System - Deployment - Live
    An example live deployment scenario for the Internet Banking System.> - - subgraph cluster_67 { - margin=25 - label=<Customer's mobile device
    [Deployment Node: Apple iOS or Android]> - labelloc=b - color="#888888" - fontcolor="#000000" - fillcolor="#ffffff" - - 68 [id=68,shape=rect, label=<Mobile App
    [Container: Xamarin]

    Provides a limited subset of
    the Internet banking
    functionality to customers via
    their mobile device.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] - } - - subgraph cluster_69 { - margin=25 - label=<Customer's computer
    [Deployment Node: Microsoft Windows or Apple macOS]> - labelloc=b - color="#888888" - fontcolor="#000000" - fillcolor="#ffffff" - - subgraph cluster_70 { - margin=25 - label=<Web Browser
    [Deployment Node: Chrome, Firefox, Safari, or Edge]> - labelloc=b - color="#888888" - fontcolor="#000000" - fillcolor="#ffffff" - - 71 [id=71,shape=rect, label=<Single-Page
    Application

    [Container: JavaScript and Angular]

    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] - } - - } - - subgraph cluster_72 { - margin=25 - label=<Big Bank plc
    [Deployment Node: Big Bank plc data center]> - labelloc=b - color="#888888" - fontcolor="#000000" - fillcolor="#ffffff" - - subgraph cluster_73 { - margin=25 - label=<bigbank-web***
    [Deployment Node: Ubuntu 16.04 LTS]> - labelloc=b - color="#888888" - fontcolor="#000000" - fillcolor="#ffffff" - - subgraph cluster_74 { - margin=25 - label=<Apache Tomcat
    [Deployment Node: Apache Tomcat 8.x]> - labelloc=b - color="#888888" - fontcolor="#000000" - fillcolor="#ffffff" - - 75 [id=75,shape=rect, label=<Web Application
    [Container: Java and Spring MVC]

    Delivers the static content
    and the Internet banking
    single page application.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] - } - - } - - subgraph cluster_77 { - margin=25 - label=<bigbank-api***
    [Deployment Node: Ubuntu 16.04 LTS]> - labelloc=b - color="#888888" - fontcolor="#000000" - fillcolor="#ffffff" - - subgraph cluster_78 { - margin=25 - label=<Apache Tomcat
    [Deployment Node: Apache Tomcat 8.x]> - labelloc=b - color="#888888" - fontcolor="#000000" - fillcolor="#ffffff" - - 79 [id=79,shape=rect, label=<API Application
    [Container: Java and Spring MVC]

    Provides Internet banking
    functionality via a JSON/HTTPS
    API.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] - } - - } - - subgraph cluster_82 { - margin=25 - label=<bigbank-db01
    [Deployment Node: Ubuntu 16.04 LTS]> - labelloc=b - color="#888888" - fontcolor="#000000" - fillcolor="#ffffff" - - subgraph cluster_83 { - margin=25 - label=<Oracle - Primary
    [Deployment Node: Oracle 12c]> - labelloc=b - color="#888888" - fontcolor="#000000" - fillcolor="#ffffff" - - 84 [id=84,shape=cylinder, label=<Database
    [Container: Oracle Database Schema]

    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] - } - - } - - subgraph cluster_86 { - margin=25 - label=<bigbank-db02
    [Deployment Node: Ubuntu 16.04 LTS]> - labelloc=b - color="#888888" - fontcolor="#000000" - fillcolor="#ffffff" - - subgraph cluster_87 { - margin=25 - label=<Oracle - Secondary
    [Deployment Node: Oracle 12c]> - labelloc=b - color="#888888" - fontcolor="#000000" - fillcolor="#ffffff" - - 88 [id=88,shape=cylinder, label=<Database
    [Container: Oracle Database Schema]

    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] - } - - } - - subgraph cluster_90 { - margin=25 - label=<bigbank-prod001
    [Deployment Node]> - labelloc=b - color="#888888" - fontcolor="#000000" - fillcolor="#ffffff" - - 91 [id=91,shape=rect, label=<Mainframe Banking
    System

    [Software System]

    Stores all of the core banking
    information about customers,
    accounts, transactions, etc.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] - } - - } - - 75 -> 71 [id=76, label=<Delivers to the customer's
    web browser
    >, style="dashed", color="#707070", fontcolor="#707070"] - 68 -> 79 [id=80, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] - 71 -> 79 [id=81, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] - 79 -> 84 [id=85, label=<Reads from and writes to
    [SQL/TCP]>, style="dashed", color="#707070", fontcolor="#707070"] - 79 -> 88 [id=89, label=<Reads from and writes to
    [SQL/TCP]>, style="dashed", color="#707070", fontcolor="#707070"] - 79 -> 91 [id=92, label=<Makes API calls to
    [XML/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] - 84 -> 88 [id=93, label=<Replicates data to>, style="dashed", color="#707070", fontcolor="#707070",ltail=cluster_83,lhead=cluster_87] -} \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/36141-SignIn.dot b/structurizr-export/src/test/java/com/structurizr/export/dot/36141-SignIn.dot deleted file mode 100644 index 3e840c871..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/dot/36141-SignIn.dot +++ /dev/null @@ -1,29 +0,0 @@ -digraph { - compound=true - graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] - node [fontname="Arial", shape=box, margin="0.4,0.3"] - edge [fontname="Arial"] - label=<
    API Application - Dynamic
    Summarises how the sign in feature works in the single-page application.> - - subgraph cluster_11 { - margin=25 - label=<
    API Application

    [Container: Java and Spring MVC]> - labelloc=b - color="#444444" - fontcolor="#444444" - fillcolor="#444444" - - 12 [id=12,shape=rect, label=<Sign In Controller
    [Component: Spring MVC Rest Controller]

    Allows users to sign in to the
    Internet Banking System.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] - 15 [id=15,shape=rect, label=<Security Component
    [Component: Spring Bean]

    Provides functionality related
    to signing in, changing
    passwords, etc.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] - } - - 8 [id=8,shape=rect, label=<Single-Page
    Application

    [Container: JavaScript and Angular]

    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] - 18 [id=18,shape=cylinder, label=<Database
    [Container: Oracle Database Schema]

    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] - - 8 -> 12 [id=32, label=<1. Submits credentials to
    [JSON/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] - 12 -> 15 [id=40, label=<2. Validates credentials
    using
    >, style="dashed", color="#707070", fontcolor="#707070"] - 15 -> 18 [id=44, label=<3. select * from users
    where username = ?

    [SQL/TCP]>, style="dashed", color="#707070", fontcolor="#707070"] - 18 -> 15 [id=44, label=<4. Returns user data to
    [SQL/TCP]>, style="dashed", color="#707070", fontcolor="#707070"] - 15 -> 12 [id=40, label=<5. Returns true if the
    hashed password matches
    >, style="dashed", color="#707070", fontcolor="#707070"] - 12 -> 8 [id=32, label=<6. Sends back an
    authentication token to

    [JSON/HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] -} \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/36141-SystemContext.dot b/structurizr-export/src/test/java/com/structurizr/export/dot/36141-SystemContext.dot deleted file mode 100644 index 73780f17e..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/dot/36141-SystemContext.dot +++ /dev/null @@ -1,28 +0,0 @@ -digraph { - compound=true - graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] - node [fontname="Arial", shape=box, margin="0.4,0.3"] - edge [fontname="Arial"] - label=<
    Internet Banking System - System Context
    The system context diagram for the Internet Banking System.> - - subgraph "cluster_group_Big Bank plc" { - margin=25 - label=<
    Big Bank plc
    > - labelloc=b - color="#cccccc" - fontcolor="#cccccc" - fillcolor="#ffffff" - style="dashed" - - 4 [id=4,shape=rect, label=<Mainframe Banking
    System

    [Software System]

    Stores all of the core banking
    information about customers,
    accounts, transactions, etc.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] - 5 [id=5,shape=rect, label=<E-mail System
    [Software System]

    The internal Microsoft
    Exchange e-mail system.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] - 7 [id=7,shape=rect, label=<Internet Banking
    System

    [Software System]

    Allows customers to view
    information about their bank
    accounts, and make payments.
    >, style=filled, color="#0b4884", fillcolor="#1168bd", fontcolor="#ffffff"] - } - - 1 [id=1,shape=rect, label=<Personal Banking
    Customer

    [Person]

    A customer of the bank, with
    personal bank accounts.
    >, style=filled, color="#052e56", fillcolor="#08427b", fontcolor="#ffffff"] - - 1 -> 7 [id=19, label=<Views account balances,
    and makes payments using
    >, style="dashed", color="#707070", fontcolor="#707070"] - 7 -> 4 [id=20, label=<Gets account information
    from, and makes payments
    using
    >, style="dashed", color="#707070", fontcolor="#707070"] - 7 -> 5 [id=21, label=<Sends e-mail using>, style="dashed", color="#707070", fontcolor="#707070"] - 5 -> 1 [id=22, label=<Sends e-mails to>, style="dashed", color="#707070", fontcolor="#707070"] -} \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/36141-SystemLandscape.dot b/structurizr-export/src/test/java/com/structurizr/export/dot/36141-SystemLandscape.dot deleted file mode 100644 index 2b691cc0d..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/dot/36141-SystemLandscape.dot +++ /dev/null @@ -1,36 +0,0 @@ -digraph { - compound=true - graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] - node [fontname="Arial", shape=box, margin="0.4,0.3"] - edge [fontname="Arial"] - label=<
    System Landscape> - - subgraph "cluster_group_Big Bank plc" { - margin=25 - label=<
    Big Bank plc
    > - labelloc=b - color="#cccccc" - fontcolor="#cccccc" - fillcolor="#ffffff" - style="dashed" - - 2 [id=2,shape=rect, label=<Customer Service
    Staff

    [Person]

    Customer service staff within
    the bank.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] - 3 [id=3,shape=rect, label=<Back Office Staff
    [Person]

    Administration and support
    staff within the bank.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] - 4 [id=4,shape=rect, label=<Mainframe Banking
    System

    [Software System]

    Stores all of the core banking
    information about customers,
    accounts, transactions, etc.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] - 5 [id=5,shape=rect, label=<E-mail System
    [Software System]

    The internal Microsoft
    Exchange e-mail system.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] - 6 [id=6,shape=rect, label=<ATM
    [Software System]

    Allows customers to withdraw
    cash.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] - 7 [id=7,shape=rect, label=<Internet Banking
    System

    [Software System]

    Allows customers to view
    information about their bank
    accounts, and make payments.
    >, style=filled, color="#0b4884", fillcolor="#1168bd", fontcolor="#ffffff"] - } - - 1 [id=1,shape=rect, label=<Personal Banking
    Customer

    [Person]

    A customer of the bank, with
    personal bank accounts.
    >, style=filled, color="#052e56", fillcolor="#08427b", fontcolor="#ffffff"] - - 1 -> 7 [id=19, label=<Views account balances,
    and makes payments using
    >, style="dashed", color="#707070", fontcolor="#707070"] - 7 -> 4 [id=20, label=<Gets account information
    from, and makes payments
    using
    >, style="dashed", color="#707070", fontcolor="#707070"] - 7 -> 5 [id=21, label=<Sends e-mail using>, style="dashed", color="#707070", fontcolor="#707070"] - 5 -> 1 [id=22, label=<Sends e-mails to>, style="dashed", color="#707070", fontcolor="#707070"] - 1 -> 2 [id=23, label=<Asks questions to
    [Telephone]>, style="dashed", color="#707070", fontcolor="#707070"] - 2 -> 4 [id=24, label=<Uses>, style="dashed", color="#707070", fontcolor="#707070"] - 1 -> 6 [id=25, label=<Withdraws cash using>, style="dashed", color="#707070", fontcolor="#707070"] - 6 -> 4 [id=26, label=<Uses>, style="dashed", color="#707070", fontcolor="#707070"] - 3 -> 4 [id=27, label=<Uses>, style="dashed", color="#707070", fontcolor="#707070"] -} \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/54915-AmazonWebServicesDeployment.dot b/structurizr-export/src/test/java/com/structurizr/export/dot/54915-AmazonWebServicesDeployment.dot deleted file mode 100644 index 9c493cabe..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/dot/54915-AmazonWebServicesDeployment.dot +++ /dev/null @@ -1,75 +0,0 @@ -digraph { - compound=true - graph [fontname="Arial", rankdir=LR, ranksep=1.0, nodesep=1.0] - node [fontname="Arial", shape=box, margin="0.4,0.3"] - edge [fontname="Arial"] - label=<
    Spring PetClinic - Deployment - Live> - - subgraph cluster_5 { - margin=25 - label=<Amazon Web Services
    [Deployment Node]> - labelloc=b - color="#232f3e" - fontcolor="#232f3e" - fillcolor="#ffffff" - - subgraph cluster_6 { - margin=25 - label=<US-East-1
    [Deployment Node]> - labelloc=b - color="#147eba" - fontcolor="#147eba" - fillcolor="#ffffff" - - subgraph cluster_12 { - margin=25 - label=<Amazon RDS
    [Deployment Node]> - labelloc=b - color="#3b48cc" - fontcolor="#3b48cc" - fillcolor="#ffffff" - - subgraph cluster_13 { - margin=25 - label=<MySQL
    [Deployment Node]> - labelloc=b - color="#3b48cc" - fontcolor="#3b48cc" - fillcolor="#ffffff" - - 14 [id=14,shape=cylinder, label=<Database
    [Container: Relational database schema]

    Stores information regarding
    the veterinarians, the
    clients, and their pets.
    >, style=filled, color="#b2b2b2", fillcolor="#ffffff", fontcolor="#000000"] - } - - } - - 7 [id=7,shape=rect, label=<Route 53
    [Infrastructure Node]

    Highly available and scalable
    cloud DNS service.
    >, style=filled, color="#693cc5", fillcolor="#ffffff", fontcolor="#693cc5"] - 8 [id=8,shape=rect, label=<Elastic Load Balancer
    [Infrastructure Node]

    Automatically distributes
    incoming application traffic.
    >, style=filled, color="#693cc5", fillcolor="#ffffff", fontcolor="#693cc5"] - subgraph cluster_9 { - margin=25 - label=<Autoscaling group
    [Deployment Node]> - labelloc=b - color="#cc2264" - fontcolor="#cc2264" - fillcolor="#ffffff" - - subgraph cluster_10 { - margin=25 - label=<Amazon EC2
    [Deployment Node]> - labelloc=b - color="#d86613" - fontcolor="#d86613" - fillcolor="#ffffff" - - 11 [id=11,shape=rect, label=<Web Application
    [Container: Java and Spring Boot]

    Allows employees to view and
    manage information regarding
    the veterinarians, the
    clients, and their pets.
    >, style=filled, color="#b2b2b2", fillcolor="#ffffff", fontcolor="#000000"] - } - - } - - } - - } - - 11 -> 14 [id=15, label=<Reads from and writes to
    [MySQL Protocol/SSL]>, style="dashed", color="#707070", fontcolor="#707070"] - 7 -> 8 [id=16, label=<Forwards requests to
    [HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] - 8 -> 11 [id=17, label=<Forwards requests to
    [HTTPS]>, style="dashed", color="#707070", fontcolor="#707070"] -} \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/DOTDiagramExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/dot/DOTDiagramExporterTests.java index b19a96f36..02aa78296 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/dot/DOTDiagramExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/dot/DOTDiagramExporterTests.java @@ -18,45 +18,467 @@ public class DOTDiagramExporterTests extends AbstractExporterTests { @Test public void test_BigBankPlcExample() throws Exception { - Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/structurizr-36141-workspace.json")); + Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/big-bank-plc.json")); DOTExporter dotWriter = new DOTExporter(); Collection diagrams = dotWriter.export(workspace); assertEquals(7, diagrams.size()); Diagram diagram = diagrams.stream().filter(md -> md.getKey().equals("SystemLandscape")).findFirst().get(); - String expected = readFile(new File("./src/test/java/com/structurizr/export/dot/36141-SystemLandscape.dot")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + digraph { + compound=true + graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] + node [fontname="Arial", shape=box, margin="0.4,0.3"] + edge [fontname="Arial"] + label=<
    System Landscape> + + subgraph "cluster_group_Big Bank plc" { + margin=25 + label=<
    Big Bank plc
    > + labelloc=b + color="#cccccc" + fontcolor="#cccccc" + fillcolor="#ffffff" + style="dashed" + + 2 [id=2,shape=rect, label=<Customer Service Staff
    [Person]

    Customer service staff within the
    bank.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] + 3 [id=3,shape=rect, label=<Back Office Staff
    [Person]

    Administration and support staff
    within the bank.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] + 4 [id=4,shape=rect, label=<Mainframe Banking
    System

    [Software System]

    Stores all of the core banking
    information about customers,
    accounts, transactions, etc.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] + 5 [id=5,shape=rect, label=<E-mail System
    [Software System]

    The internal Microsoft
    Exchange e-mail system.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] + 6 [id=6,shape=rect, label=<ATM
    [Software System]

    Allows customers to withdraw
    cash.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] + 7 [id=7,shape=rect, label=<Internet Banking
    System

    [Software System]

    Allows customers to view
    information about their bank
    accounts, and make payments.
    >, style=filled, color="#0b4884", fillcolor="#1168bd", fontcolor="#ffffff"] + } + + 1 [id=1,shape=rect, label=<Personal Banking
    Customer

    [Person]

    A customer of the bank, with
    personal bank accounts.
    >, style=filled, color="#052e56", fillcolor="#08427b", fontcolor="#ffffff"] + + 1 -> 7 [id=19, label=<Views account balances,
    and makes payments using
    >, style="dashed", color="#444444", fontcolor="#444444"] + 7 -> 4 [id=20, label=<Gets account information
    from, and makes payments
    using
    >, style="dashed", color="#444444", fontcolor="#444444"] + 7 -> 5 [id=21, label=<Sends e-mail using>, style="dashed", color="#444444", fontcolor="#444444"] + 5 -> 1 [id=22, label=<Sends e-mails to>, style="dashed", color="#444444", fontcolor="#444444"] + 1 -> 2 [id=23, label=<Asks questions to
    [Telephone]>, style="dashed", color="#444444", fontcolor="#444444"] + 2 -> 4 [id=24, label=<Uses>, style="dashed", color="#444444", fontcolor="#444444"] + 1 -> 6 [id=25, label=<Withdraws cash using>, style="dashed", color="#444444", fontcolor="#444444"] + 6 -> 4 [id=26, label=<Uses>, style="dashed", color="#444444", fontcolor="#444444"] + 3 -> 4 [id=27, label=<Uses>, style="dashed", color="#444444", fontcolor="#444444"] + + }""", diagram.getDefinition()); diagram = diagrams.stream().filter(md -> md.getKey().equals("SystemContext")).findFirst().get(); - expected = readFile(new File("./src/test/java/com/structurizr/export/dot/36141-SystemContext.dot")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + digraph { + compound=true + graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] + node [fontname="Arial", shape=box, margin="0.4,0.3"] + edge [fontname="Arial"] + label=<
    Internet Banking System - System Context
    The system context diagram for the Internet Banking System.> + + subgraph "cluster_group_Big Bank plc" { + margin=25 + label=<
    Big Bank plc
    > + labelloc=b + color="#cccccc" + fontcolor="#cccccc" + fillcolor="#ffffff" + style="dashed" + + 4 [id=4,shape=rect, label=<Mainframe Banking
    System

    [Software System]

    Stores all of the core banking
    information about customers,
    accounts, transactions, etc.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] + 5 [id=5,shape=rect, label=<E-mail System
    [Software System]

    The internal Microsoft
    Exchange e-mail system.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] + 7 [id=7,shape=rect, label=<Internet Banking
    System

    [Software System]

    Allows customers to view
    information about their bank
    accounts, and make payments.
    >, style=filled, color="#0b4884", fillcolor="#1168bd", fontcolor="#ffffff"] + } + + 1 [id=1,shape=rect, label=<Personal Banking
    Customer

    [Person]

    A customer of the bank, with
    personal bank accounts.
    >, style=filled, color="#052e56", fillcolor="#08427b", fontcolor="#ffffff"] + + 1 -> 7 [id=19, label=<Views account balances,
    and makes payments using
    >, style="dashed", color="#444444", fontcolor="#444444"] + 7 -> 4 [id=20, label=<Gets account information
    from, and makes payments
    using
    >, style="dashed", color="#444444", fontcolor="#444444"] + 7 -> 5 [id=21, label=<Sends e-mail using>, style="dashed", color="#444444", fontcolor="#444444"] + 5 -> 1 [id=22, label=<Sends e-mails to>, style="dashed", color="#444444", fontcolor="#444444"] + + }""", diagram.getDefinition()); diagram = diagrams.stream().filter(md -> md.getKey().equals("Containers")).findFirst().get(); - expected = readFile(new File("./src/test/java/com/structurizr/export/dot/36141-Containers.dot")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + digraph { + compound=true + graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] + node [fontname="Arial", shape=box, margin="0.4,0.3"] + edge [fontname="Arial"] + label=<
    Internet Banking System - Containers
    The container diagram for the Internet Banking System.> + + 1 [id=1,shape=rect, label=<Personal Banking
    Customer

    [Person]

    A customer of the bank, with
    personal bank accounts.
    >, style=filled, color="#052e56", fillcolor="#08427b", fontcolor="#ffffff"] + 4 [id=4,shape=rect, label=<Mainframe Banking
    System

    [Software System]

    Stores all of the core banking
    information about customers,
    accounts, transactions, etc.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] + 5 [id=5,shape=rect, label=<E-mail System
    [Software System]

    The internal Microsoft
    Exchange e-mail system.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] + + subgraph cluster_7 { + margin=25 + label=<
    Internet Banking System

    [Software System]> + labelloc=b + color="#444444" + fontcolor="#444444" + fillcolor="#444444" + + 10 [id=10,shape=rect, label=<Web Application
    [Container: Java and Spring MVC]

    Delivers the static content
    and the Internet banking
    single page application.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + 11 [id=11,shape=rect, label=<API Application
    [Container: Java and Spring MVC]

    Provides Internet banking
    functionality via a JSON/HTTPS
    API.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + 18 [id=18,shape=cylinder, label=<Database
    [Container: Oracle Database Schema]

    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + 8 [id=8,shape=rect, label=<Single-Page
    Application

    [Container: JavaScript and Angular]

    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + 9 [id=9,shape=rect, label=<Mobile App
    [Container: Xamarin]

    Provides a limited subset of
    the Internet banking
    functionality to customers via
    their mobile device.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + } + + 5 -> 1 [id=22, label=<Sends e-mails to>, style="dashed", color="#444444", fontcolor="#444444"] + 1 -> 10 [id=28, label=<Visits bigbank.com/ib
    using

    [HTTPS]>, style="dashed", color="#444444", fontcolor="#444444"] + 1 -> 8 [id=29, label=<Views account balances,
    and makes payments using
    >, style="dashed", color="#444444", fontcolor="#444444"] + 1 -> 9 [id=30, label=<Views account balances,
    and makes payments using
    >, style="dashed", color="#444444", fontcolor="#444444"] + 10 -> 8 [id=31, label=<Delivers to the customer's
    web browser
    >, style="dashed", color="#444444", fontcolor="#444444"] + 8 -> 11 [id=33, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#444444", fontcolor="#444444"] + 9 -> 11 [id=37, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#444444", fontcolor="#444444"] + 11 -> 18 [id=45, label=<Reads from and writes to
    [SQL/TCP]>, style="dashed", color="#444444", fontcolor="#444444"] + 11 -> 4 [id=47, label=<Makes API calls to
    [XML/HTTPS]>, style="dashed", color="#444444", fontcolor="#444444"] + 11 -> 5 [id=49, label=<Sends e-mail using>, style="dashed", color="#444444", fontcolor="#444444"] + + }""", diagram.getDefinition()); diagram = diagrams.stream().filter(md -> md.getKey().equals("Components")).findFirst().get(); - expected = readFile(new File("./src/test/java/com/structurizr/export/dot/36141-Components.dot")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + digraph { + compound=true + graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] + node [fontname="Arial", shape=box, margin="0.4,0.3"] + edge [fontname="Arial"] + label=<
    Internet Banking System - API Application - Components
    The component diagram for the API Application.> + + 4 [id=4,shape=rect, label=<Mainframe Banking
    System

    [Software System]

    Stores all of the core banking
    information about customers,
    accounts, transactions, etc.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] + 5 [id=5,shape=rect, label=<E-mail System
    [Software System]

    The internal Microsoft
    Exchange e-mail system.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] + 8 [id=8,shape=rect, label=<Single-Page
    Application

    [Container: JavaScript and Angular]

    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + 9 [id=9,shape=rect, label=<Mobile App
    [Container: Xamarin]

    Provides a limited subset of
    the Internet banking
    functionality to customers via
    their mobile device.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + 18 [id=18,shape=cylinder, label=<Database
    [Container: Oracle Database Schema]

    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + + subgraph cluster_11 { + margin=25 + label=<
    API Application

    [Container: Java and Spring MVC]> + labelloc=b + color="#444444" + fontcolor="#444444" + fillcolor="#444444" + + 12 [id=12,shape=rect, label=<Sign In Controller
    [Component: Spring MVC Rest Controller]

    Allows users to sign in to the
    Internet Banking System.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] + 13 [id=13,shape=rect, label=<Accounts Summary
    Controller

    [Component: Spring MVC Rest Controller]

    Provides customers with a
    summary of their bank
    accounts.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] + 14 [id=14,shape=rect, label=<Reset Password
    Controller

    [Component: Spring MVC Rest Controller]

    Allows users to reset their
    passwords with a single use
    URL.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] + 15 [id=15,shape=rect, label=<Security Component
    [Component: Spring Bean]

    Provides functionality related
    to signing in, changing
    passwords, etc.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] + 16 [id=16,shape=rect, label=<Mainframe Banking
    System Facade

    [Component: Spring Bean]

    A facade onto the mainframe
    banking system.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] + 17 [id=17,shape=rect, label=<E-mail Component
    [Component: Spring Bean]

    Sends e-mails to users.>, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] + } + + 8 -> 12 [id=32, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#444444", fontcolor="#444444"] + 8 -> 13 [id=34, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#444444", fontcolor="#444444"] + 8 -> 14 [id=35, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#444444", fontcolor="#444444"] + 9 -> 12 [id=36, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#444444", fontcolor="#444444"] + 9 -> 13 [id=38, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#444444", fontcolor="#444444"] + 9 -> 14 [id=39, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#444444", fontcolor="#444444"] + 12 -> 15 [id=40, label=<Uses>, style="dashed", color="#444444", fontcolor="#444444"] + 13 -> 16 [id=41, label=<Uses>, style="dashed", color="#444444", fontcolor="#444444"] + 14 -> 15 [id=42, label=<Uses>, style="dashed", color="#444444", fontcolor="#444444"] + 14 -> 17 [id=43, label=<Uses>, style="dashed", color="#444444", fontcolor="#444444"] + 15 -> 18 [id=44, label=<Reads from and writes to
    [SQL/TCP]>, style="dashed", color="#444444", fontcolor="#444444"] + 16 -> 4 [id=46, label=<Makes API calls to
    [XML/HTTPS]>, style="dashed", color="#444444", fontcolor="#444444"] + 17 -> 5 [id=48, label=<Sends e-mail using>, style="dashed", color="#444444", fontcolor="#444444"] + + }""", diagram.getDefinition()); diagram = diagrams.stream().filter(md -> md.getKey().equals("SignIn")).findFirst().get(); - expected = readFile(new File("./src/test/java/com/structurizr/export/dot/36141-SignIn.dot")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + digraph { + compound=true + graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] + node [fontname="Arial", shape=box, margin="0.4,0.3"] + edge [fontname="Arial"] + label=<
    API Application - Dynamic
    Summarises how the sign in feature works in the single-page application.> + + subgraph cluster_11 { + margin=25 + label=<
    API Application

    [Container: Java and Spring MVC]> + labelloc=b + color="#444444" + fontcolor="#444444" + fillcolor="#444444" + + 12 [id=12,shape=rect, label=<Sign In Controller
    [Component: Spring MVC Rest Controller]

    Allows users to sign in to the
    Internet Banking System.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] + 15 [id=15,shape=rect, label=<Security Component
    [Component: Spring Bean]

    Provides functionality related
    to signing in, changing
    passwords, etc.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] + } + + 8 [id=8,shape=rect, label=<Single-Page
    Application

    [Container: JavaScript and Angular]

    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + 18 [id=18,shape=cylinder, label=<Database
    [Container: Oracle Database Schema]

    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + + 8 -> 12 [id=32, label=<1. Submits credentials to
    [JSON/HTTPS]>, style="dashed", color="#444444", fontcolor="#444444"] + 12 -> 15 [id=40, label=<2. Validates credentials
    using
    >, style="dashed", color="#444444", fontcolor="#444444"] + 15 -> 18 [id=44, label=<3. select * from users
    where username = ?

    [SQL/TCP]>, style="dashed", color="#444444", fontcolor="#444444"] + 18 -> 15 [id=44, label=<4. Returns user data to
    [SQL/TCP]>, style="dashed", color="#444444", fontcolor="#444444"] + 15 -> 12 [id=40, label=<5. Returns true if the
    hashed password matches
    >, style="dashed", color="#444444", fontcolor="#444444"] + 12 -> 8 [id=32, label=<6. Sends back an
    authentication token to

    [JSON/HTTPS]>, style="dashed", color="#444444", fontcolor="#444444"] + + }""", diagram.getDefinition()); diagram = diagrams.stream().filter(md -> md.getKey().equals("DevelopmentDeployment")).findFirst().get(); - expected = readFile(new File("./src/test/java/com/structurizr/export/dot/36141-DevelopmentDeployment.dot")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + digraph { + compound=true + graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] + node [fontname="Arial", shape=box, margin="0.4,0.3"] + edge [fontname="Arial"] + label=<
    Internet Banking System - Deployment - Development
    An example development deployment scenario for the Internet Banking System.> + + subgraph cluster_50 { + margin=25 + label=<Developer Laptop
    [Deployment Node: Microsoft Windows 10 or Apple macOS]> + labelloc=b + color="#444444" + fontcolor="#444444" + fillcolor="#ffffff" + + subgraph cluster_51 { + margin=25 + label=<Web Browser
    [Deployment Node: Chrome, Firefox, Safari, or Edge]> + labelloc=b + color="#444444" + fontcolor="#444444" + fillcolor="#ffffff" + + 52 [id=52,shape=rect, label=<Single-Page
    Application

    [Container: JavaScript and Angular]

    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + } + + subgraph cluster_53 { + margin=25 + label=<Docker Container - Web Server
    [Deployment Node: Docker]> + labelloc=b + color="#444444" + fontcolor="#444444" + fillcolor="#ffffff" + + subgraph cluster_54 { + margin=25 + label=<Apache Tomcat
    [Deployment Node: Apache Tomcat 8.x]> + labelloc=b + color="#444444" + fontcolor="#444444" + fillcolor="#ffffff" + + 55 [id=55,shape=rect, label=<Web Application
    [Container: Java and Spring MVC]

    Delivers the static content
    and the Internet banking
    single page application.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + 57 [id=57,shape=rect, label=<API Application
    [Container: Java and Spring MVC]

    Provides Internet banking
    functionality via a JSON/HTTPS
    API.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + } + + } + + subgraph cluster_59 { + margin=25 + label=<Docker Container - Database Server
    [Deployment Node: Docker]> + labelloc=b + color="#444444" + fontcolor="#444444" + fillcolor="#ffffff" + + subgraph cluster_60 { + margin=25 + label=<Database Server
    [Deployment Node: Oracle 12c]> + labelloc=b + color="#444444" + fontcolor="#444444" + fillcolor="#ffffff" + + 61 [id=61,shape=cylinder, label=<Database
    [Container: Oracle Database Schema]

    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + } + + } + + } + + subgraph cluster_63 { + margin=25 + label=<Big Bank plc
    [Deployment Node: Big Bank plc data center]> + labelloc=b + color="#444444" + fontcolor="#444444" + fillcolor="#ffffff" + + subgraph cluster_64 { + margin=25 + label=<bigbank-dev001
    [Deployment Node]> + labelloc=b + color="#444444" + fontcolor="#444444" + fillcolor="#ffffff" + + 65 [id=65,shape=rect, label=<Mainframe Banking
    System

    [Software System]

    Stores all of the core banking
    information about customers,
    accounts, transactions, etc.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] + } + + } + + 55 -> 52 [id=56, label=<Delivers to the customer's
    web browser
    >, style="dashed", color="#444444", fontcolor="#444444"] + 52 -> 57 [id=58, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#444444", fontcolor="#444444"] + 57 -> 61 [id=62, label=<Reads from and writes to
    [SQL/TCP]>, style="dashed", color="#444444", fontcolor="#444444"] + 57 -> 65 [id=66, label=<Makes API calls to
    [XML/HTTPS]>, style="dashed", color="#444444", fontcolor="#444444"] + + }""", diagram.getDefinition()); diagram = diagrams.stream().filter(md -> md.getKey().equals("LiveDeployment")).findFirst().get(); - expected = readFile(new File("./src/test/java/com/structurizr/export/dot/36141-LiveDeployment.dot")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + digraph { + compound=true + graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] + node [fontname="Arial", shape=box, margin="0.4,0.3"] + edge [fontname="Arial"] + label=<
    Internet Banking System - Deployment - Live
    An example live deployment scenario for the Internet Banking System.> + + subgraph cluster_67 { + margin=25 + label=<Customer's mobile device
    [Deployment Node: Apple iOS or Android]> + labelloc=b + color="#444444" + fontcolor="#444444" + fillcolor="#ffffff" + + 68 [id=68,shape=rect, label=<Mobile App
    [Container: Xamarin]

    Provides a limited subset of
    the Internet banking
    functionality to customers via
    their mobile device.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + } + + subgraph cluster_69 { + margin=25 + label=<Customer's computer
    [Deployment Node: Microsoft Windows or Apple macOS]> + labelloc=b + color="#444444" + fontcolor="#444444" + fillcolor="#ffffff" + + subgraph cluster_70 { + margin=25 + label=<Web Browser
    [Deployment Node: Chrome, Firefox, Safari, or Edge]> + labelloc=b + color="#444444" + fontcolor="#444444" + fillcolor="#ffffff" + + 71 [id=71,shape=rect, label=<Single-Page
    Application

    [Container: JavaScript and Angular]

    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + } + + } + + subgraph cluster_72 { + margin=25 + label=<Big Bank plc
    [Deployment Node: Big Bank plc data center]> + labelloc=b + color="#444444" + fontcolor="#444444" + fillcolor="#ffffff" + + subgraph cluster_73 { + margin=25 + label=<bigbank-web***
    [Deployment Node: Ubuntu 16.04 LTS]> + labelloc=b + color="#444444" + fontcolor="#444444" + fillcolor="#ffffff" + + subgraph cluster_74 { + margin=25 + label=<Apache Tomcat
    [Deployment Node: Apache Tomcat 8.x]> + labelloc=b + color="#444444" + fontcolor="#444444" + fillcolor="#ffffff" + + 75 [id=75,shape=rect, label=<Web Application
    [Container: Java and Spring MVC]

    Delivers the static content
    and the Internet banking
    single page application.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + } + + } + + subgraph cluster_77 { + margin=25 + label=<bigbank-api***
    [Deployment Node: Ubuntu 16.04 LTS]> + labelloc=b + color="#444444" + fontcolor="#444444" + fillcolor="#ffffff" + + subgraph cluster_78 { + margin=25 + label=<Apache Tomcat
    [Deployment Node: Apache Tomcat 8.x]> + labelloc=b + color="#444444" + fontcolor="#444444" + fillcolor="#ffffff" + + 79 [id=79,shape=rect, label=<API Application
    [Container: Java and Spring MVC]

    Provides Internet banking
    functionality via a JSON/HTTPS
    API.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + } + + } + + subgraph cluster_82 { + margin=25 + label=<bigbank-db01
    [Deployment Node: Ubuntu 16.04 LTS]> + labelloc=b + color="#444444" + fontcolor="#444444" + fillcolor="#ffffff" + + subgraph cluster_83 { + margin=25 + label=<Oracle - Primary
    [Deployment Node: Oracle 12c]> + labelloc=b + color="#444444" + fontcolor="#444444" + fillcolor="#ffffff" + + 84 [id=84,shape=cylinder, label=<Database
    [Container: Oracle Database Schema]

    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + } + + } + + subgraph cluster_86 { + margin=25 + label=<bigbank-db02
    [Deployment Node: Ubuntu 16.04 LTS]> + labelloc=b + color="#444444" + fontcolor="#444444" + fillcolor="#ffffff" + + subgraph cluster_87 { + margin=25 + label=<Oracle - Secondary
    [Deployment Node: Oracle 12c]> + labelloc=b + color="#444444" + fontcolor="#444444" + fillcolor="#ffffff" + + 88 [id=88,shape=cylinder, label=<Database
    [Container: Oracle Database Schema]

    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + } + + } + + subgraph cluster_90 { + margin=25 + label=<bigbank-prod001
    [Deployment Node]> + labelloc=b + color="#444444" + fontcolor="#444444" + fillcolor="#ffffff" + + 91 [id=91,shape=rect, label=<Mainframe Banking
    System

    [Software System]

    Stores all of the core banking
    information about customers,
    accounts, transactions, etc.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] + } + + } + + 75 -> 71 [id=76, label=<Delivers to the customer's
    web browser
    >, style="dashed", color="#444444", fontcolor="#444444"] + 68 -> 79 [id=80, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#444444", fontcolor="#444444"] + 71 -> 79 [id=81, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#444444", fontcolor="#444444"] + 79 -> 84 [id=85, label=<Reads from and writes to
    [SQL/TCP]>, style="dashed", color="#444444", fontcolor="#444444"] + 79 -> 88 [id=89, label=<Reads from and writes to
    [SQL/TCP]>, style="dashed", color="#444444", fontcolor="#444444"] + 79 -> 91 [id=92, label=<Makes API calls to
    [XML/HTTPS]>, style="dashed", color="#444444", fontcolor="#444444"] + 84 -> 88 [id=93, label=<Replicates data to>, style="dashed", color="#444444", fontcolor="#444444",ltail=cluster_83,lhead=cluster_87] + + }""", diagram.getDefinition()); } @Test @Tag("IntegrationTest") public void test_AmazonWebServicesExample() throws Exception { - Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/structurizr-54915-workspace.json")); + Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/amazon-web-services.json")); ThemeUtils.loadThemes(workspace); workspace.getViews().getDeploymentViews().iterator().next().enableAutomaticLayout(AutomaticLayout.RankDirection.LeftRight, 300, 300); @@ -65,8 +487,83 @@ public void test_AmazonWebServicesExample() throws Exception { assertEquals(1, diagrams.size()); Diagram diagram = diagrams.stream().findFirst().get(); - String expected = readFile(new File("./src/test/java/com/structurizr/export/dot/54915-AmazonWebServicesDeployment.dot")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + digraph { + compound=true + graph [fontname="Arial", rankdir=LR, ranksep=1.0, nodesep=1.0] + node [fontname="Arial", shape=box, margin="0.4,0.3"] + edge [fontname="Arial"] + label=<
    X - Deployment - Live> + + subgraph cluster_5 { + margin=25 + label=<Amazon Web Services
    [Deployment Node]> + labelloc=b + color="#232f3e" + fontcolor="#232f3e" + fillcolor="#ffffff" + + subgraph cluster_6 { + margin=25 + label=<US-East-1
    [Deployment Node]> + labelloc=b + color="#147eba" + fontcolor="#147eba" + fillcolor="#ffffff" + + subgraph cluster_10 { + margin=25 + label=<Autoscaling group
    [Deployment Node]> + labelloc=b + color="#cc2264" + fontcolor="#cc2264" + fillcolor="#ffffff" + + subgraph cluster_11 { + margin=25 + label=<Amazon EC2 - Ubuntu server
    [Deployment Node]> + labelloc=b + color="#d86613" + fontcolor="#d86613" + fillcolor="#ffffff" + + 12 [id=12,shape=rect, label=<Web Application
    [Container: Java and Spring Boot]>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] + } + + } + + subgraph cluster_14 { + margin=25 + label=<Amazon RDS
    [Deployment Node]> + labelloc=b + color="#3b48cc" + fontcolor="#3b48cc" + fillcolor="#ffffff" + + subgraph cluster_15 { + margin=25 + label=<MySQL
    [Deployment Node]> + labelloc=b + color="#3b48cc" + fontcolor="#3b48cc" + fillcolor="#ffffff" + + 16 [id=16,shape=cylinder, label=<Database Schema
    [Container]>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] + } + + } + + 7 [id=7,shape=rect, label=<DNS router
    [Infrastructure Node: Route 53]

    Routes incoming requests based
    upon domain name.
    >, style=filled, color="#693cc5", fillcolor="#ffffff", fontcolor="#693cc5"] + 8 [id=8,shape=rect, label=<Load Balancer
    [Infrastructure Node: Elastic Load Balancer]

    Automatically distributes
    incoming application traffic.
    >, style=filled, color="#693cc5", fillcolor="#ffffff", fontcolor="#693cc5"] + } + + } + + 8 -> 12 [id=13, label=<Forwards requests to
    [HTTPS]>, style="dashed", color="#444444", fontcolor="#444444"] + 12 -> 16 [id=17, label=<Reads from and writes to
    [MySQL Protocol/SSL]>, style="dashed", color="#444444", fontcolor="#444444"] + 7 -> 8 [id=9, label=<Forwards requests to
    [HTTPS]>, style="dashed", color="#444444", fontcolor="#444444"] + + }""", diagram.getDefinition()); } @Test @@ -79,16 +576,135 @@ public void test_GroupsExample() throws Exception { assertEquals(3, diagrams.size()); Diagram diagram = diagrams.stream().filter(md -> md.getKey().equals("SystemLandscape")).findFirst().get(); - String expected = readFile(new File("./src/test/java/com/structurizr/export/dot/groups-SystemLandscape.dot")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + digraph { + compound=true + graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] + node [fontname="Arial", shape=box, margin="0.4,0.3"] + edge [fontname="Arial"] + label=<
    System Landscape> + + subgraph "cluster_group_Group 1" { + margin=25 + label=<
    Group 1
    > + labelloc=b + color="#cccccc" + fontcolor="#cccccc" + fillcolor="#ffffff" + style="dashed" + + 2 [id=2,shape=rect, label=<B
    [Software System]>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] + } + + subgraph "cluster_group_Group 2" { + margin=25 + label=<
    Group 2
    > + labelloc=b + color="#cccccc" + fontcolor="#cccccc" + fillcolor="#ffffff" + style="dashed" + + 3 [id=3,shape=rect, label=<C
    [Software System]>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] + subgraph "cluster_group_Group 3" { + margin=25 + label=<
    Group 3
    > + labelloc=b + color="#cccccc" + fontcolor="#cccccc" + fillcolor="#ffffff" + style="dashed" + + 4 [id=4,shape=rect, label=<D
    [Software System]>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] + } + + } + + 1 [id=1,shape=rect, label=<A
    [Software System]>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] + + 2 -> 3 [id=10, label=<>, style="dashed", color="#444444", fontcolor="#444444"] + 3 -> 4 [id=12, label=<>, style="dashed", color="#444444", fontcolor="#444444"] + 1 -> 2 [id=9, label=<>, style="dashed", color="#444444", fontcolor="#444444"] + + }""", diagram.getDefinition()); diagram = diagrams.stream().filter(md -> md.getKey().equals("Containers")).findFirst().get(); - expected = readFile(new File("./src/test/java/com/structurizr/export/dot/groups-Containers.dot")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + digraph { + compound=true + graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] + node [fontname="Arial", shape=box, margin="0.4,0.3"] + edge [fontname="Arial"] + label=<
    D - Containers> + + 3 [id=3,shape=rect, label=<C
    [Software System]>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] + + subgraph cluster_4 { + margin=25 + label=<
    D

    [Software System]> + labelloc=b + color="#444444" + fontcolor="#444444" + fillcolor="#444444" + + subgraph "cluster_group_Group 4" { + margin=25 + label=<
    Group 4
    > + labelloc=b + color="#cccccc" + fontcolor="#cccccc" + fillcolor="#ffffff" + style="dashed" + + 6 [id=6,shape=rect, label=<F
    [Container]>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] + } + + 5 [id=5,shape=rect, label=<E
    [Container]>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] + } + + 3 -> 5 [id=11, label=<>, style="dashed", color="#444444", fontcolor="#444444"] + 3 -> 6 [id=14, label=<>, style="dashed", color="#444444", fontcolor="#444444"] + + }""", diagram.getDefinition()); diagram = diagrams.stream().filter(md -> md.getKey().equals("Components")).findFirst().get(); - expected = readFile(new File("./src/test/java/com/structurizr/export/dot/groups-Components.dot")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + digraph { + compound=true + graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] + node [fontname="Arial", shape=box, margin="0.4,0.3"] + edge [fontname="Arial"] + label=<
    D - F - Components> + + 3 [id=3,shape=rect, label=<C
    [Software System]>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] + + subgraph cluster_6 { + margin=25 + label=<
    F

    [Container]> + labelloc=b + color="#444444" + fontcolor="#444444" + fillcolor="#444444" + + subgraph "cluster_group_Group 5" { + margin=25 + label=<
    Group 5
    > + labelloc=b + color="#cccccc" + fontcolor="#cccccc" + fillcolor="#ffffff" + style="dashed" + + 8 [id=8,shape=rect, label=<H
    [Component]>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] + } + + 7 [id=7,shape=rect, label=<G
    [Component]>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] + } + + 3 -> 7 [id=13, label=<>, style="dashed", color="#444444", fontcolor="#444444"] + 3 -> 8 [id=15, label=<>, style="dashed", color="#444444", fontcolor="#444444"] + + }""", diagram.getDefinition()); } @Test @@ -118,8 +734,76 @@ public void test_NestedGroupsExample() throws Exception { Collection diagrams = exporter.export(workspace); Diagram diagram = diagrams.stream().filter(md -> md.getKey().equals("SystemLandscape")).findFirst().get(); - String expected = readFile(new File("./src/test/java/com/structurizr/export/dot/nested-groups.dot")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + digraph { + compound=true + graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] + node [fontname="Arial", shape=box, margin="0.4,0.3"] + edge [fontname="Arial"] + label=<
    System Landscape
    Description> + + subgraph "cluster_group_Organisation 1" { + margin=25 + label=<
    Organisation 1
    > + labelloc=b + color="#cccccc" + fontcolor="#cccccc" + fillcolor="#ffffff" + style="dashed" + + 3 [id=3,shape=rect, label=<Organisation 1
    [Software System]>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] + subgraph "cluster_group_Department 1" { + margin=25 + label=<
    Department 1
    > + labelloc=b + color="#cccccc" + fontcolor="#cccccc" + fillcolor="#ffffff" + style="dashed" + + 5 [id=5,shape=rect, label=<Department 1
    [Software System]>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] + subgraph "cluster_group_Team 1" { + margin=25 + label=<
    Team 1
    > + labelloc=b + color="#cccccc" + fontcolor="#cccccc" + fillcolor="#ffffff" + style="dashed" + + 1 [id=1,shape=rect, label=<Team 1
    [Software System]>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] + } + + subgraph "cluster_group_Team 2" { + margin=25 + label=<
    Team 2
    > + labelloc=b + color="#cccccc" + fontcolor="#cccccc" + fillcolor="#ffffff" + style="dashed" + + 2 [id=2,shape=rect, label=<Team 2
    [Software System]>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] + } + + } + + } + + subgraph "cluster_group_Organisation 2" { + margin=25 + label=<
    Organisation 2
    > + labelloc=b + color="#cccccc" + fontcolor="#cccccc" + fillcolor="#ffffff" + style="dashed" + + 4 [id=4,shape=rect, label=<Organisation 2
    [Software System]>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] + } + + + }""", diagram.getDefinition()); } @Test @@ -137,37 +821,39 @@ public void test_renderContainerDiagramWithExternalContainers() { containerView.add(container2); Diagram diagram = new DOTExporter().export(containerView); - assertEquals("digraph {\n" + - " compound=true\n" + - " graph [fontname=\"Arial\", rankdir=TB, ranksep=1.0, nodesep=1.0]\n" + - " node [fontname=\"Arial\", shape=box, margin=\"0.4,0.3\"]\n" + - " edge [fontname=\"Arial\"]\n" + - " label=<
    Software System 1 - Containers>\n" + - "\n" + - " subgraph cluster_1 {\n" + - " margin=25\n" + - " label=<
    Software System 1

    [Software System]>\n" + - " labelloc=b\n" + - " color=\"#444444\"\n" + - " fontcolor=\"#444444\"\n" + - " fillcolor=\"#444444\"\n" + - "\n" + - " 2 [id=2,shape=rect, label=<Container 1
    [Container]>, style=filled, color=\"#9a9a9a\", fillcolor=\"#dddddd\", fontcolor=\"#000000\"]\n" + - " }\n" + - "\n" + - " subgraph cluster_3 {\n" + - " margin=25\n" + - " label=<
    Software System 2

    [Software System]>\n" + - " labelloc=b\n" + - " color=\"#cccccc\"\n" + - " fontcolor=\"#cccccc\"\n" + - " fillcolor=\"#cccccc\"\n" + - "\n" + - " 4 [id=4,shape=rect, label=<Container 2
    [Container]>, style=filled, color=\"#9a9a9a\", fillcolor=\"#dddddd\", fontcolor=\"#000000\"]\n" + - " }\n" + - "\n" + - " 2 -> 4 [id=5, label=<Uses>, style=\"dashed\", color=\"#707070\", fontcolor=\"#707070\"]\n" + - "}", diagram.getDefinition()); + assertEquals(""" + digraph { + compound=true + graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] + node [fontname="Arial", shape=box, margin="0.4,0.3"] + edge [fontname="Arial"] + label=<
    Software System 1 - Containers> + + subgraph cluster_1 { + margin=25 + label=<
    Software System 1

    [Software System]> + labelloc=b + color="#444444" + fontcolor="#444444" + fillcolor="#444444" + + 2 [id=2,shape=rect, label=<Container 1
    [Container]>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] + } + + subgraph cluster_3 { + margin=25 + label=<
    Software System 2

    [Software System]> + labelloc=b + color="#cccccc" + fontcolor="#cccccc" + fillcolor="#cccccc" + + 4 [id=4,shape=rect, label=<Container 2
    [Container]>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] + } + + 2 -> 4 [id=5, label=<Uses>, style="dashed", color="#444444", fontcolor="#444444"] + + }""", diagram.getDefinition()); } @Test @@ -187,37 +873,39 @@ public void test_renderComponentDiagramWithExternalComponents() { componentView.add(component2); Diagram diagram = new DOTExporter().export(componentView); - assertEquals("digraph {\n" + - " compound=true\n" + - " graph [fontname=\"Arial\", rankdir=TB, ranksep=1.0, nodesep=1.0]\n" + - " node [fontname=\"Arial\", shape=box, margin=\"0.4,0.3\"]\n" + - " edge [fontname=\"Arial\"]\n" + - " label=<
    Software System 1 - Container 1 - Components>\n" + - "\n" + - " subgraph cluster_2 {\n" + - " margin=25\n" + - " label=<
    Container 1

    [Container]>\n" + - " labelloc=b\n" + - " color=\"#444444\"\n" + - " fontcolor=\"#444444\"\n" + - " fillcolor=\"#444444\"\n" + - "\n" + - " 3 [id=3,shape=rect, label=<Component 1
    [Component]>, style=filled, color=\"#9a9a9a\", fillcolor=\"#dddddd\", fontcolor=\"#000000\"]\n" + - " }\n" + - "\n" + - " subgraph cluster_5 {\n" + - " margin=25\n" + - " label=<
    Container 2

    [Container]>\n" + - " labelloc=b\n" + - " color=\"#cccccc\"\n" + - " fontcolor=\"#cccccc\"\n" + - " fillcolor=\"#cccccc\"\n" + - "\n" + - " 6 [id=6,shape=rect, label=<Component 2
    [Component]>, style=filled, color=\"#9a9a9a\", fillcolor=\"#dddddd\", fontcolor=\"#000000\"]\n" + - " }\n" + - "\n" + - " 3 -> 6 [id=7, label=<Uses>, style=\"dashed\", color=\"#707070\", fontcolor=\"#707070\"]\n" + - "}", diagram.getDefinition()); + assertEquals(""" + digraph { + compound=true + graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] + node [fontname="Arial", shape=box, margin="0.4,0.3"] + edge [fontname="Arial"] + label=<
    Software System 1 - Container 1 - Components> + + subgraph cluster_2 { + margin=25 + label=<
    Container 1

    [Container]> + labelloc=b + color="#444444" + fontcolor="#444444" + fillcolor="#444444" + + 3 [id=3,shape=rect, label=<Component 1
    [Component]>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] + } + + subgraph cluster_5 { + margin=25 + label=<
    Container 2

    [Container]> + labelloc=b + color="#cccccc" + fontcolor="#cccccc" + fillcolor="#cccccc" + + 6 [id=6,shape=rect, label=<Component 2
    [Component]>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] + } + + 3 -> 6 [id=7, label=<Uses>, style="dashed", color="#444444", fontcolor="#444444"] + + }""", diagram.getDefinition()); } @Test @@ -235,100 +923,102 @@ public void test_renderGroupStyles() { DOTExporter exporter = new DOTExporter(); Diagram diagram = exporter.export(view); - assertEquals("digraph {\n" + - " compound=true\n" + - " graph [fontname=\"Arial\", rankdir=TB, ranksep=1.0, nodesep=1.0]\n" + - " node [fontname=\"Arial\", shape=box, margin=\"0.4,0.3\"]\n" + - " edge [fontname=\"Arial\"]\n" + - " label=<
    System Landscape>\n" + - "\n" + - " subgraph \"cluster_group_Group 1\" {\n" + - " margin=25\n" + - " label=<
    Group 1
    >\n" + - " labelloc=b\n" + - " color=\"#111111\"\n" + - " fontcolor=\"#111111\"\n" + - " fillcolor=\"#ffffff\"\n" + - " style=\"dashed\"\n" + - "\n" + - " 1 [id=1,shape=rect, label=<User 1
    [Person]>, style=filled, color=\"#9a9a9a\", fillcolor=\"#dddddd\", fontcolor=\"#000000\"]\n" + - " }\n" + - "\n" + - " subgraph \"cluster_group_Group 2\" {\n" + - " margin=25\n" + - " label=<
    Group 2
    >\n" + - " labelloc=b\n" + - " color=\"#222222\"\n" + - " fontcolor=\"#222222\"\n" + - " fillcolor=\"#ffffff\"\n" + - " style=\"dashed\"\n" + - "\n" + - " 2 [id=2,shape=rect, label=<User 2
    [Person]>, style=filled, color=\"#9a9a9a\", fillcolor=\"#dddddd\", fontcolor=\"#000000\"]\n" + - " }\n" + - "\n" + - " subgraph \"cluster_group_Group 3\" {\n" + - " margin=25\n" + - " label=<
    Group 3
    >\n" + - " labelloc=b\n" + - " color=\"#cccccc\"\n" + - " fontcolor=\"#cccccc\"\n" + - " fillcolor=\"#ffffff\"\n" + - " style=\"dashed\"\n" + - "\n" + - " 3 [id=3,shape=rect, label=<User 3
    [Person]>, style=filled, color=\"#9a9a9a\", fillcolor=\"#dddddd\", fontcolor=\"#000000\"]\n" + - " }\n" + - "\n" + - "\n" + - "}", diagram.getDefinition()); + assertEquals(""" + digraph { + compound=true + graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] + node [fontname="Arial", shape=box, margin="0.4,0.3"] + edge [fontname="Arial"] + label=<
    System Landscape> + + subgraph "cluster_group_Group 1" { + margin=25 + label=<
    Group 1
    > + labelloc=b + color="#111111" + fontcolor="#111111" + fillcolor="#ffffff" + style="dashed" + + 1 [id=1,shape=rect, label=<User 1
    [Person]>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] + } + + subgraph "cluster_group_Group 2" { + margin=25 + label=<
    Group 2
    > + labelloc=b + color="#222222" + fontcolor="#222222" + fillcolor="#ffffff" + style="dashed" + + 2 [id=2,shape=rect, label=<User 2
    [Person]>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] + } + + subgraph "cluster_group_Group 3" { + margin=25 + label=<
    Group 3
    > + labelloc=b + color="#cccccc" + fontcolor="#cccccc" + fillcolor="#ffffff" + style="dashed" + + 3 [id=3,shape=rect, label=<User 3
    [Person]>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] + } + + + }""", diagram.getDefinition()); workspace.getViews().getConfiguration().getStyles().addElementStyle("Group").color("#aabbcc"); diagram = exporter.export(view); - assertEquals("digraph {\n" + - " compound=true\n" + - " graph [fontname=\"Arial\", rankdir=TB, ranksep=1.0, nodesep=1.0]\n" + - " node [fontname=\"Arial\", shape=box, margin=\"0.4,0.3\"]\n" + - " edge [fontname=\"Arial\"]\n" + - " label=<
    System Landscape>\n" + - "\n" + - " subgraph \"cluster_group_Group 1\" {\n" + - " margin=25\n" + - " label=<
    Group 1
    >\n" + - " labelloc=b\n" + - " color=\"#111111\"\n" + - " fontcolor=\"#111111\"\n" + - " fillcolor=\"#ffffff\"\n" + - " style=\"dashed\"\n" + - "\n" + - " 1 [id=1,shape=rect, label=<User 1
    [Person]>, style=filled, color=\"#9a9a9a\", fillcolor=\"#dddddd\", fontcolor=\"#000000\"]\n" + - " }\n" + - "\n" + - " subgraph \"cluster_group_Group 2\" {\n" + - " margin=25\n" + - " label=<
    Group 2
    >\n" + - " labelloc=b\n" + - " color=\"#222222\"\n" + - " fontcolor=\"#222222\"\n" + - " fillcolor=\"#ffffff\"\n" + - " style=\"dashed\"\n" + - "\n" + - " 2 [id=2,shape=rect, label=<User 2
    [Person]>, style=filled, color=\"#9a9a9a\", fillcolor=\"#dddddd\", fontcolor=\"#000000\"]\n" + - " }\n" + - "\n" + - " subgraph \"cluster_group_Group 3\" {\n" + - " margin=25\n" + - " label=<
    Group 3
    >\n" + - " labelloc=b\n" + - " color=\"#aabbcc\"\n" + - " fontcolor=\"#aabbcc\"\n" + - " fillcolor=\"#ffffff\"\n" + - " style=\"dashed\"\n" + - "\n" + - " 3 [id=3,shape=rect, label=<User 3
    [Person]>, style=filled, color=\"#9a9a9a\", fillcolor=\"#dddddd\", fontcolor=\"#000000\"]\n" + - " }\n" + - "\n" + - "\n" + - "}", diagram.getDefinition()); + assertEquals(""" + digraph { + compound=true + graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] + node [fontname="Arial", shape=box, margin="0.4,0.3"] + edge [fontname="Arial"] + label=<
    System Landscape> + + subgraph "cluster_group_Group 1" { + margin=25 + label=<
    Group 1
    > + labelloc=b + color="#111111" + fontcolor="#111111" + fillcolor="#ffffff" + style="dashed" + + 1 [id=1,shape=rect, label=<User 1
    [Person]>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] + } + + subgraph "cluster_group_Group 2" { + margin=25 + label=<
    Group 2
    > + labelloc=b + color="#222222" + fontcolor="#222222" + fillcolor="#ffffff" + style="dashed" + + 2 [id=2,shape=rect, label=<User 2
    [Person]>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] + } + + subgraph "cluster_group_Group 3" { + margin=25 + label=<
    Group 3
    > + labelloc=b + color="#aabbcc" + fontcolor="#aabbcc" + fillcolor="#ffffff" + style="dashed" + + 3 [id=3,shape=rect, label=<User 3
    [Person]>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] + } + + + }""", diagram.getDefinition()); } @Test @@ -344,18 +1034,20 @@ public void test_renderCustomView() { view.addDefaultElements(); Diagram diagram = new DOTExporter().export(view); - assertEquals("digraph {\n" + - " compound=true\n" + - " graph [fontname=\"Arial\", rankdir=TB, ranksep=1.0, nodesep=1.0]\n" + - " node [fontname=\"Arial\", shape=box, margin=\"0.4,0.3\"]\n" + - " edge [fontname=\"Arial\"]\n" + - " label=<
    Title
    Description>\n" + - "\n" + - " 1 [id=1,shape=rect, label=<A>, style=filled, color=\"#9a9a9a\", fillcolor=\"#dddddd\", fontcolor=\"#000000\"]\n" + - " 2 [id=2,shape=rect, label=<B
    [Custom]

    Description>, style=filled, color=\"#9a9a9a\", fillcolor=\"#dddddd\", fontcolor=\"#000000\"]\n" + - "\n" + - " 1 -> 2 [id=3, label=<Uses>, style=\"dashed\", color=\"#707070\", fontcolor=\"#707070\"]\n" + - "}", diagram.getDefinition()); + assertEquals(""" + digraph { + compound=true + graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] + node [fontname="Arial", shape=box, margin="0.4,0.3"] + edge [fontname="Arial"] + label=<
    Title
    Description> + + 1 [id=1,shape=rect, label=<A>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] + 2 [id=2,shape=rect, label=<B
    [Custom]

    Description>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] + + 1 -> 2 [id=3, label=<Uses>, style="dashed", color="#444444", fontcolor="#444444"] + + }""", diagram.getDefinition()); } @Test @@ -394,7 +1086,7 @@ public void test_writeContainerViewWithGroupedElements_WithAndWithoutAGroupSepar " fillcolor=\"#ffffff\"\n" + " style=\"dashed\"\n" + "\n" + - " 2 [id=2,shape=rect, label=<Container 1
    [Container]>, style=filled, color=\"#9a9a9a\", fillcolor=\"#dddddd\", fontcolor=\"#000000\"]\n" + + " 2 [id=2,shape=rect, label=<Container 1
    [Container]>, style=filled, color=\"#444444\", fillcolor=\"#ffffff\", fontcolor=\"#444444\"]\n" + " }\n" + "\n" + " subgraph \"cluster_group_Group 2\" {\n" + @@ -406,7 +1098,7 @@ public void test_writeContainerViewWithGroupedElements_WithAndWithoutAGroupSepar " fillcolor=\"#ffffff\"\n" + " style=\"dashed\"\n" + "\n" + - " 3 [id=3,shape=rect, label=<Container 2
    [Container]>, style=filled, color=\"#9a9a9a\", fillcolor=\"#dddddd\", fontcolor=\"#000000\"]\n" + + " 3 [id=3,shape=rect, label=<Container 2
    [Container]>, style=filled, color=\"#444444\", fillcolor=\"#ffffff\", fontcolor=\"#444444\"]\n" + " }\n" + "\n" + " }\n" + diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/groups-Components.dot b/structurizr-export/src/test/java/com/structurizr/export/dot/groups-Components.dot deleted file mode 100644 index cc868ffe8..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/dot/groups-Components.dot +++ /dev/null @@ -1,35 +0,0 @@ -digraph { - compound=true - graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] - node [fontname="Arial", shape=box, margin="0.4,0.3"] - edge [fontname="Arial"] - label=<
    D - F - Components> - - 3 [id=3,shape=rect, label=<C
    [Software System]>, style=filled, color="#9a9a9a", fillcolor="#dddddd", fontcolor="#000000"] - - subgraph cluster_6 { - margin=25 - label=<
    F

    [Container]> - labelloc=b - color="#444444" - fontcolor="#444444" - fillcolor="#444444" - - subgraph "cluster_group_Group 5" { - margin=25 - label=<
    Group 5
    > - labelloc=b - color="#cccccc" - fontcolor="#cccccc" - fillcolor="#ffffff" - style="dashed" - - 8 [id=8,shape=rect, label=<H
    [Component]>, style=filled, color="#9a9a9a", fillcolor="#dddddd", fontcolor="#000000"] - } - - 7 [id=7,shape=rect, label=<G
    [Component]>, style=filled, color="#9a9a9a", fillcolor="#dddddd", fontcolor="#000000"] - } - - 3 -> 7 [id=13, label=<>, style="dashed", color="#707070", fontcolor="#707070"] - 3 -> 8 [id=15, label=<>, style="dashed", color="#707070", fontcolor="#707070"] -} \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/groups-Containers.dot b/structurizr-export/src/test/java/com/structurizr/export/dot/groups-Containers.dot deleted file mode 100644 index 013ecfbf2..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/dot/groups-Containers.dot +++ /dev/null @@ -1,35 +0,0 @@ -digraph { - compound=true - graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] - node [fontname="Arial", shape=box, margin="0.4,0.3"] - edge [fontname="Arial"] - label=<
    D - Containers> - - 3 [id=3,shape=rect, label=<C
    [Software System]>, style=filled, color="#9a9a9a", fillcolor="#dddddd", fontcolor="#000000"] - - subgraph cluster_4 { - margin=25 - label=<
    D

    [Software System]> - labelloc=b - color="#444444" - fontcolor="#444444" - fillcolor="#444444" - - subgraph "cluster_group_Group 4" { - margin=25 - label=<
    Group 4
    > - labelloc=b - color="#cccccc" - fontcolor="#cccccc" - fillcolor="#ffffff" - style="dashed" - - 6 [id=6,shape=rect, label=<F
    [Container]>, style=filled, color="#9a9a9a", fillcolor="#dddddd", fontcolor="#000000"] - } - - 5 [id=5,shape=rect, label=<E
    [Container]>, style=filled, color="#9a9a9a", fillcolor="#dddddd", fontcolor="#000000"] - } - - 3 -> 5 [id=11, label=<>, style="dashed", color="#707070", fontcolor="#707070"] - 3 -> 6 [id=14, label=<>, style="dashed", color="#707070", fontcolor="#707070"] -} \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/groups-SystemLandscape.dot b/structurizr-export/src/test/java/com/structurizr/export/dot/groups-SystemLandscape.dot deleted file mode 100644 index 5147526da..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/dot/groups-SystemLandscape.dot +++ /dev/null @@ -1,49 +0,0 @@ -digraph { - compound=true - graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] - node [fontname="Arial", shape=box, margin="0.4,0.3"] - edge [fontname="Arial"] - label=<
    System Landscape> - - subgraph "cluster_group_Group 1" { - margin=25 - label=<
    Group 1
    > - labelloc=b - color="#cccccc" - fontcolor="#cccccc" - fillcolor="#ffffff" - style="dashed" - - 2 [id=2,shape=rect, label=<B
    [Software System]>, style=filled, color="#9a9a9a", fillcolor="#dddddd", fontcolor="#000000"] - } - - subgraph "cluster_group_Group 2" { - margin=25 - label=<
    Group 2
    > - labelloc=b - color="#cccccc" - fontcolor="#cccccc" - fillcolor="#ffffff" - style="dashed" - - 3 [id=3,shape=rect, label=<C
    [Software System]>, style=filled, color="#9a9a9a", fillcolor="#dddddd", fontcolor="#000000"] - subgraph "cluster_group_Group 3" { - margin=25 - label=<
    Group 3
    > - labelloc=b - color="#cccccc" - fontcolor="#cccccc" - fillcolor="#ffffff" - style="dashed" - - 4 [id=4,shape=rect, label=<D
    [Software System]>, style=filled, color="#9a9a9a", fillcolor="#dddddd", fontcolor="#000000"] - } - - } - - 1 [id=1,shape=rect, label=<A
    [Software System]>, style=filled, color="#9a9a9a", fillcolor="#dddddd", fontcolor="#000000"] - - 2 -> 3 [id=10, label=<>, style="dashed", color="#707070", fontcolor="#707070"] - 3 -> 4 [id=12, label=<>, style="dashed", color="#707070", fontcolor="#707070"] - 1 -> 2 [id=9, label=<>, style="dashed", color="#707070", fontcolor="#707070"] -} \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/nested-groups.dot b/structurizr-export/src/test/java/com/structurizr/export/dot/nested-groups.dot deleted file mode 100644 index 702f73d43..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/dot/nested-groups.dot +++ /dev/null @@ -1,69 +0,0 @@ -digraph { - compound=true - graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] - node [fontname="Arial", shape=box, margin="0.4,0.3"] - edge [fontname="Arial"] - label=<
    System Landscape
    Description> - - subgraph "cluster_group_Organisation 1" { - margin=25 - label=<
    Organisation 1
    > - labelloc=b - color="#cccccc" - fontcolor="#cccccc" - fillcolor="#ffffff" - style="dashed" - - 3 [id=3,shape=rect, label=<Organisation 1
    [Software System]>, style=filled, color="#9a9a9a", fillcolor="#dddddd", fontcolor="#000000"] - subgraph "cluster_group_Department 1" { - margin=25 - label=<
    Department 1
    > - labelloc=b - color="#cccccc" - fontcolor="#cccccc" - fillcolor="#ffffff" - style="dashed" - - 5 [id=5,shape=rect, label=<Department 1
    [Software System]>, style=filled, color="#9a9a9a", fillcolor="#dddddd", fontcolor="#000000"] - subgraph "cluster_group_Team 1" { - margin=25 - label=<
    Team 1
    > - labelloc=b - color="#cccccc" - fontcolor="#cccccc" - fillcolor="#ffffff" - style="dashed" - - 1 [id=1,shape=rect, label=<Team 1
    [Software System]>, style=filled, color="#9a9a9a", fillcolor="#dddddd", fontcolor="#000000"] - } - - subgraph "cluster_group_Team 2" { - margin=25 - label=<
    Team 2
    > - labelloc=b - color="#cccccc" - fontcolor="#cccccc" - fillcolor="#ffffff" - style="dashed" - - 2 [id=2,shape=rect, label=<Team 2
    [Software System]>, style=filled, color="#9a9a9a", fillcolor="#dddddd", fontcolor="#000000"] - } - - } - - } - - subgraph "cluster_group_Organisation 2" { - margin=25 - label=<
    Organisation 2
    > - labelloc=b - color="#cccccc" - fontcolor="#cccccc" - fillcolor="#ffffff" - style="dashed" - - 4 [id=4,shape=rect, label=<Organisation 2
    [Software System]>, style=filled, color="#9a9a9a", fillcolor="#dddddd", fontcolor="#000000"] - } - - -} \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/ilograph/36141.ilograph b/structurizr-export/src/test/java/com/structurizr/export/ilograph/36141.ilograph deleted file mode 100644 index 6a416a4d3..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/ilograph/36141.ilograph +++ /dev/null @@ -1,616 +0,0 @@ -resources: - - id: "1" - name: "Personal Banking Customer" - subtitle: "[Person]" - description: "A customer of the bank, with personal bank accounts." - backgroundColor: "#08427b" - color: "#ffffff" - - - id: "2" - name: "Customer Service Staff" - subtitle: "[Person]" - description: "Customer service staff within the bank." - backgroundColor: "#999999" - color: "#ffffff" - - - id: "3" - name: "Back Office Staff" - subtitle: "[Person]" - description: "Administration and support staff within the bank." - backgroundColor: "#999999" - color: "#ffffff" - - - id: "4" - name: "Mainframe Banking System" - subtitle: "[Software System]" - description: "Stores all of the core banking information about customers, accounts, transactions, etc." - backgroundColor: "#999999" - color: "#ffffff" - - - id: "5" - name: "E-mail System" - subtitle: "[Software System]" - description: "The internal Microsoft Exchange e-mail system." - backgroundColor: "#999999" - color: "#ffffff" - - - id: "6" - name: "ATM" - subtitle: "[Software System]" - description: "Allows customers to withdraw cash." - backgroundColor: "#999999" - color: "#ffffff" - - - id: "7" - name: "Internet Banking System" - subtitle: "[Software System]" - description: "Allows customers to view information about their bank accounts, and make payments." - backgroundColor: "#1168bd" - color: "#ffffff" - - children: - - id: "10" - name: "Web Application" - subtitle: "[Container: Java and Spring MVC]" - description: "Delivers the static content and the Internet banking single page application." - backgroundColor: "#438dd5" - color: "#ffffff" - - - id: "11" - name: "API Application" - subtitle: "[Container: Java and Spring MVC]" - description: "Provides Internet banking functionality via a JSON/HTTPS API." - backgroundColor: "#438dd5" - color: "#ffffff" - - children: - - id: "12" - name: "Sign In Controller" - subtitle: "[Component: Spring MVC Rest Controller]" - description: "Allows users to sign in to the Internet Banking System." - backgroundColor: "#85bbf0" - color: "#000000" - - - id: "13" - name: "Accounts Summary Controller" - subtitle: "[Component: Spring MVC Rest Controller]" - description: "Provides customers with a summary of their bank accounts." - backgroundColor: "#85bbf0" - color: "#000000" - - - id: "14" - name: "Reset Password Controller" - subtitle: "[Component: Spring MVC Rest Controller]" - description: "Allows users to reset their passwords with a single use URL." - backgroundColor: "#85bbf0" - color: "#000000" - - - id: "15" - name: "Security Component" - subtitle: "[Component: Spring Bean]" - description: "Provides functionality related to signing in, changing passwords, etc." - backgroundColor: "#85bbf0" - color: "#000000" - - - id: "16" - name: "Mainframe Banking System Facade" - subtitle: "[Component: Spring Bean]" - description: "A facade onto the mainframe banking system." - backgroundColor: "#85bbf0" - color: "#000000" - - - id: "17" - name: "E-mail Component" - subtitle: "[Component: Spring Bean]" - description: "Sends e-mails to users." - backgroundColor: "#85bbf0" - color: "#000000" - - - id: "18" - name: "Database" - subtitle: "[Container: Oracle Database Schema]" - description: "Stores user registration information, hashed authentication credentials, access logs, etc." - backgroundColor: "#438dd5" - color: "#ffffff" - - - id: "8" - name: "Single-Page Application" - subtitle: "[Container: JavaScript and Angular]" - description: "Provides all of the Internet banking functionality to customers via their web browser." - backgroundColor: "#438dd5" - color: "#ffffff" - - - id: "9" - name: "Mobile App" - subtitle: "[Container: Xamarin]" - description: "Provides a limited subset of the Internet banking functionality to customers via their mobile device." - backgroundColor: "#438dd5" - color: "#ffffff" - - - id: "50" - name: "Developer Laptop" - subtitle: "[Deployment Node: Microsoft Windows 10 or Apple macOS]" - backgroundColor: "#ffffff" - color: "#000000" - - children: - - id: "51" - name: "Web Browser" - subtitle: "[Deployment Node: Chrome, Firefox, Safari, or Edge]" - backgroundColor: "#ffffff" - color: "#000000" - - children: - - id: "52" - name: "Single-Page Application" - subtitle: "[Container: JavaScript and Angular]" - description: "Provides all of the Internet banking functionality to customers via their web browser." - backgroundColor: "#438dd5" - color: "#ffffff" - - - id: "53" - name: "Docker Container - Web Server" - subtitle: "[Deployment Node: Docker]" - backgroundColor: "#ffffff" - color: "#000000" - - children: - - id: "54" - name: "Apache Tomcat" - subtitle: "[Deployment Node: Apache Tomcat 8.x]" - backgroundColor: "#ffffff" - color: "#000000" - - children: - - id: "55" - name: "Web Application" - subtitle: "[Container: Java and Spring MVC]" - description: "Delivers the static content and the Internet banking single page application." - backgroundColor: "#438dd5" - color: "#ffffff" - - - id: "57" - name: "API Application" - subtitle: "[Container: Java and Spring MVC]" - description: "Provides Internet banking functionality via a JSON/HTTPS API." - backgroundColor: "#438dd5" - color: "#ffffff" - - - id: "59" - name: "Docker Container - Database Server" - subtitle: "[Deployment Node: Docker]" - backgroundColor: "#ffffff" - color: "#000000" - - children: - - id: "60" - name: "Database Server" - subtitle: "[Deployment Node: Oracle 12c]" - backgroundColor: "#ffffff" - color: "#000000" - - children: - - id: "61" - name: "Database" - subtitle: "[Container: Oracle Database Schema]" - description: "Stores user registration information, hashed authentication credentials, access logs, etc." - backgroundColor: "#438dd5" - color: "#ffffff" - - - id: "63" - name: "Big Bank plc" - subtitle: "[Deployment Node: Big Bank plc data center]" - backgroundColor: "#ffffff" - color: "#000000" - - children: - - id: "64" - name: "bigbank-dev001" - subtitle: "[Deployment Node]" - backgroundColor: "#ffffff" - color: "#000000" - - children: - - id: "65" - name: "Mainframe Banking System" - subtitle: "[Software System]" - description: "Stores all of the core banking information about customers, accounts, transactions, etc." - backgroundColor: "#999999" - color: "#ffffff" - - - id: "67" - name: "Customer's mobile device" - subtitle: "[Deployment Node: Apple iOS or Android]" - backgroundColor: "#ffffff" - color: "#000000" - - children: - - id: "68" - name: "Mobile App" - subtitle: "[Container: Xamarin]" - description: "Provides a limited subset of the Internet banking functionality to customers via their mobile device." - backgroundColor: "#438dd5" - color: "#ffffff" - - - id: "69" - name: "Customer's computer" - subtitle: "[Deployment Node: Microsoft Windows or Apple macOS]" - backgroundColor: "#ffffff" - color: "#000000" - - children: - - id: "70" - name: "Web Browser" - subtitle: "[Deployment Node: Chrome, Firefox, Safari, or Edge]" - backgroundColor: "#ffffff" - color: "#000000" - - children: - - id: "71" - name: "Single-Page Application" - subtitle: "[Container: JavaScript and Angular]" - description: "Provides all of the Internet banking functionality to customers via their web browser." - backgroundColor: "#438dd5" - color: "#ffffff" - - - id: "72" - name: "Big Bank plc" - subtitle: "[Deployment Node: Big Bank plc data center]" - backgroundColor: "#ffffff" - color: "#000000" - - children: - - id: "73" - name: "bigbank-web***" - subtitle: "[Deployment Node: Ubuntu 16.04 LTS]" - backgroundColor: "#ffffff" - color: "#000000" - - children: - - id: "74" - name: "Apache Tomcat" - subtitle: "[Deployment Node: Apache Tomcat 8.x]" - backgroundColor: "#ffffff" - color: "#000000" - - children: - - id: "75" - name: "Web Application" - subtitle: "[Container: Java and Spring MVC]" - description: "Delivers the static content and the Internet banking single page application." - backgroundColor: "#438dd5" - color: "#ffffff" - - - id: "77" - name: "bigbank-api***" - subtitle: "[Deployment Node: Ubuntu 16.04 LTS]" - backgroundColor: "#ffffff" - color: "#000000" - - children: - - id: "78" - name: "Apache Tomcat" - subtitle: "[Deployment Node: Apache Tomcat 8.x]" - backgroundColor: "#ffffff" - color: "#000000" - - children: - - id: "79" - name: "API Application" - subtitle: "[Container: Java and Spring MVC]" - description: "Provides Internet banking functionality via a JSON/HTTPS API." - backgroundColor: "#438dd5" - color: "#ffffff" - - - id: "82" - name: "bigbank-db01" - subtitle: "[Deployment Node: Ubuntu 16.04 LTS]" - backgroundColor: "#ffffff" - color: "#000000" - - children: - - id: "83" - name: "Oracle - Primary" - subtitle: "[Deployment Node: Oracle 12c]" - backgroundColor: "#ffffff" - color: "#000000" - - children: - - id: "84" - name: "Database" - subtitle: "[Container: Oracle Database Schema]" - description: "Stores user registration information, hashed authentication credentials, access logs, etc." - backgroundColor: "#438dd5" - color: "#ffffff" - - - id: "86" - name: "bigbank-db02" - subtitle: "[Deployment Node: Ubuntu 16.04 LTS]" - backgroundColor: "#ffffff" - color: "#000000" - - children: - - id: "87" - name: "Oracle - Secondary" - subtitle: "[Deployment Node: Oracle 12c]" - backgroundColor: "#ffffff" - color: "#000000" - - children: - - id: "88" - name: "Database" - subtitle: "[Container: Oracle Database Schema]" - description: "Stores user registration information, hashed authentication credentials, access logs, etc." - backgroundColor: "#438dd5" - color: "#ffffff" - - - id: "90" - name: "bigbank-prod001" - subtitle: "[Deployment Node]" - backgroundColor: "#ffffff" - color: "#000000" - - children: - - id: "91" - name: "Mainframe Banking System" - subtitle: "[Software System]" - description: "Stores all of the core banking information about customers, accounts, transactions, etc." - backgroundColor: "#999999" - color: "#ffffff" - -perspectives: - - name: Static Structure - relations: - - from: "1" - to: "7" - label: "Views account balances, and makes payments using" - color: "#707070" - - - from: "1" - to: "2" - label: "Asks questions to" - description: "Telephone" - color: "#707070" - - - from: "1" - to: "6" - label: "Withdraws cash using" - color: "#707070" - - - from: "2" - to: "4" - label: "Uses" - color: "#707070" - - - from: "3" - to: "4" - label: "Uses" - color: "#707070" - - - from: "5" - to: "1" - label: "Sends e-mails to" - color: "#707070" - - - from: "6" - to: "4" - label: "Uses" - color: "#707070" - - - from: "7" - to: "4" - label: "Gets account information from, and makes payments using" - color: "#707070" - - - from: "7" - to: "5" - label: "Sends e-mail using" - color: "#707070" - - - from: "1" - to: "10" - label: "Visits bigbank.com/ib using" - description: "HTTPS" - color: "#707070" - - - from: "1" - to: "8" - label: "Views account balances, and makes payments using" - color: "#707070" - - - from: "1" - to: "9" - label: "Views account balances, and makes payments using" - color: "#707070" - - - from: "10" - to: "8" - label: "Delivers to the customer's web browser" - color: "#707070" - - - from: "11" - to: "18" - label: "Reads from and writes to" - description: "SQL/TCP" - color: "#707070" - - - from: "11" - to: "4" - label: "Makes API calls to" - description: "XML/HTTPS" - color: "#707070" - - - from: "11" - to: "5" - label: "Sends e-mail using" - color: "#707070" - - - from: "8" - to: "11" - label: "Makes API calls to" - description: "JSON/HTTPS" - color: "#707070" - - - from: "9" - to: "11" - label: "Makes API calls to" - description: "JSON/HTTPS" - color: "#707070" - - - from: "12" - to: "15" - label: "Uses" - color: "#707070" - - - from: "13" - to: "16" - label: "Uses" - color: "#707070" - - - from: "14" - to: "15" - label: "Uses" - color: "#707070" - - - from: "14" - to: "17" - label: "Uses" - color: "#707070" - - - from: "15" - to: "18" - label: "Reads from and writes to" - description: "SQL/TCP" - color: "#707070" - - - from: "16" - to: "4" - label: "Makes API calls to" - description: "XML/HTTPS" - color: "#707070" - - - from: "17" - to: "5" - label: "Sends e-mail using" - color: "#707070" - - - from: "8" - to: "12" - label: "Makes API calls to" - description: "JSON/HTTPS" - color: "#707070" - - - from: "8" - to: "13" - label: "Makes API calls to" - description: "JSON/HTTPS" - color: "#707070" - - - from: "8" - to: "14" - label: "Makes API calls to" - description: "JSON/HTTPS" - color: "#707070" - - - from: "9" - to: "12" - label: "Makes API calls to" - description: "JSON/HTTPS" - color: "#707070" - - - from: "9" - to: "13" - label: "Makes API calls to" - description: "JSON/HTTPS" - color: "#707070" - - - from: "9" - to: "14" - label: "Makes API calls to" - description: "JSON/HTTPS" - color: "#707070" - - - name: Dynamic - API Application - Dynamic - sequence: - start: "8" - steps: - - to: "12" - label: "1. Submits credentials to" - description: "JSON/HTTPS" - color: "#707070" - - - to: "15" - label: "2. Validates credentials using" - color: "#707070" - - - to: "18" - label: "3. select * from users where username = ?" - description: "SQL/TCP" - color: "#707070" - - - to: "15" - label: "4. Returns user data to" - description: "SQL/TCP" - color: "#707070" - - - to: "12" - label: "5. Returns true if the hashed password matches" - color: "#707070" - - - to: "8" - label: "6. Sends back an authentication token to" - description: "JSON/HTTPS" - color: "#707070" - - - name: Deployment - Development - relations: - - from: "52" - to: "57" - label: "Makes API calls to" - description: "JSON/HTTPS" - color: "#707070" - - from: "55" - to: "52" - label: "Delivers to the customer's web browser" - color: "#707070" - - from: "57" - to: "61" - label: "Reads from and writes to" - description: "SQL/TCP" - color: "#707070" - - from: "57" - to: "65" - label: "Makes API calls to" - description: "XML/HTTPS" - color: "#707070" - - name: Deployment - Live - relations: - - from: "68" - to: "79" - label: "Makes API calls to" - description: "JSON/HTTPS" - color: "#707070" - - from: "71" - to: "79" - label: "Makes API calls to" - description: "JSON/HTTPS" - color: "#707070" - - from: "75" - to: "71" - label: "Delivers to the customer's web browser" - color: "#707070" - - from: "79" - to: "84" - label: "Reads from and writes to" - description: "SQL/TCP" - color: "#707070" - - from: "79" - to: "88" - label: "Reads from and writes to" - description: "SQL/TCP" - color: "#707070" - - from: "79" - to: "91" - label: "Makes API calls to" - description: "XML/HTTPS" - color: "#707070" \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/ilograph/54915.ilograph b/structurizr-export/src/test/java/com/structurizr/export/ilograph/54915.ilograph deleted file mode 100644 index 5b37222b4..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/ilograph/54915.ilograph +++ /dev/null @@ -1,127 +0,0 @@ -resources: - - id: "1" - name: "Spring PetClinic" - subtitle: "[Software System]" - description: "Allows employees to view and manage information regarding the veterinarians, the clients, and their pets." - backgroundColor: "#ffffff" - color: "#000000" - - children: - - id: "2" - name: "Web Application" - subtitle: "[Container: Java and Spring Boot]" - description: "Allows employees to view and manage information regarding the veterinarians, the clients, and their pets." - backgroundColor: "#ffffff" - color: "#000000" - - - id: "3" - name: "Database" - subtitle: "[Container: Relational database schema]" - description: "Stores information regarding the veterinarians, the clients, and their pets." - backgroundColor: "#ffffff" - color: "#000000" - - - id: "5" - name: "Amazon Web Services" - subtitle: "[Deployment Node]" - backgroundColor: "#ffffff" - color: "#232f3e" - icon: "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/aws-cloud.png" - - children: - - id: "6" - name: "US-East-1" - subtitle: "[Deployment Node]" - backgroundColor: "#ffffff" - color: "#147eba" - icon: "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/region.png" - - children: - - id: "12" - name: "Amazon RDS" - subtitle: "[Deployment Node]" - backgroundColor: "#ffffff" - color: "#3b48cc" - icon: "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/amazon-rds.png" - - children: - - id: "13" - name: "MySQL" - subtitle: "[Deployment Node]" - backgroundColor: "#ffffff" - color: "#3b48cc" - icon: "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/amazon-rds-mysql-instance.png" - - children: - - id: "14" - name: "Database" - subtitle: "[Container: Relational database schema]" - description: "Stores information regarding the veterinarians, the clients, and their pets." - backgroundColor: "#ffffff" - color: "#000000" - - - id: "9" - name: "Autoscaling group" - subtitle: "[Deployment Node]" - backgroundColor: "#ffffff" - color: "#cc2264" - icon: "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/aws-auto-scaling.png" - - children: - - id: "10" - name: "Amazon EC2" - subtitle: "[Deployment Node]" - backgroundColor: "#ffffff" - color: "#d86613" - icon: "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/amazon-ec2.png" - - children: - - id: "11" - name: "Web Application" - subtitle: "[Container: Java and Spring Boot]" - description: "Allows employees to view and manage information regarding the veterinarians, the clients, and their pets." - backgroundColor: "#ffffff" - color: "#000000" - - - id: "7" - name: "Route 53" - subtitle: "[Infrastructure Node]" - description: "Highly available and scalable cloud DNS service." - backgroundColor: "#ffffff" - color: "#693cc5" - icon: "AWS/Networking/Route-53.svg" - - - id: "8" - name: "Elastic Load Balancer" - subtitle: "[Infrastructure Node]" - description: "Automatically distributes incoming application traffic." - backgroundColor: "#ffffff" - color: "#693cc5" - icon: "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/elastic-load-balancing.png" - -perspectives: - - name: Static Structure - relations: - - from: "2" - to: "3" - label: "Reads from and writes to" - description: "MySQL Protocol/SSL" - color: "#707070" - - - name: Deployment - Live - relations: - - from: "11" - to: "14" - label: "Reads from and writes to" - description: "MySQL Protocol/SSL" - color: "#707070" - - from: "7" - to: "8" - label: "Forwards requests to" - description: "HTTPS" - color: "#707070" - - from: "8" - to: "11" - label: "Forwards requests to" - description: "HTTPS" - color: "#707070" \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/ilograph/IlographExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/ilograph/IlographExporterTests.java index 4761312d2..596b7592c 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/ilograph/IlographExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/ilograph/IlographExporterTests.java @@ -18,26 +18,762 @@ public class IlographExporterTests extends AbstractExporterTests { @Test public void test_BigBankPlcExample() throws Exception { - Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/structurizr-36141-workspace.json")); + Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/big-bank-plc.json")); IlographExporter ilographExporter = new IlographExporter(); WorkspaceExport export = ilographExporter.export(workspace); - String expected = readFile(new File("./src/test/java/com/structurizr/export/ilograph/36141.ilograph")); - assertEquals(expected, export.getDefinition()); + assertEquals(""" +resources: + - id: "1" + name: "Personal Banking Customer" + subtitle: "[Person]" + description: "A customer of the bank, with personal bank accounts." + backgroundColor: "#08427b" + color: "#ffffff" + + - id: "2" + name: "Customer Service Staff" + subtitle: "[Person]" + description: "Customer service staff within the bank." + backgroundColor: "#999999" + color: "#ffffff" + + - id: "3" + name: "Back Office Staff" + subtitle: "[Person]" + description: "Administration and support staff within the bank." + backgroundColor: "#999999" + color: "#ffffff" + + - id: "4" + name: "Mainframe Banking System" + subtitle: "[Software System]" + description: "Stores all of the core banking information about customers, accounts, transactions, etc." + backgroundColor: "#999999" + color: "#ffffff" + + - id: "5" + name: "E-mail System" + subtitle: "[Software System]" + description: "The internal Microsoft Exchange e-mail system." + backgroundColor: "#999999" + color: "#ffffff" + + - id: "6" + name: "ATM" + subtitle: "[Software System]" + description: "Allows customers to withdraw cash." + backgroundColor: "#999999" + color: "#ffffff" + + - id: "7" + name: "Internet Banking System" + subtitle: "[Software System]" + description: "Allows customers to view information about their bank accounts, and make payments." + backgroundColor: "#1168bd" + color: "#ffffff" + + children: + - id: "10" + name: "Web Application" + subtitle: "[Container: Java and Spring MVC]" + description: "Delivers the static content and the Internet banking single page application." + backgroundColor: "#438dd5" + color: "#ffffff" + + - id: "11" + name: "API Application" + subtitle: "[Container: Java and Spring MVC]" + description: "Provides Internet banking functionality via a JSON/HTTPS API." + backgroundColor: "#438dd5" + color: "#ffffff" + + children: + - id: "12" + name: "Sign In Controller" + subtitle: "[Component: Spring MVC Rest Controller]" + description: "Allows users to sign in to the Internet Banking System." + backgroundColor: "#85bbf0" + color: "#000000" + + - id: "13" + name: "Accounts Summary Controller" + subtitle: "[Component: Spring MVC Rest Controller]" + description: "Provides customers with a summary of their bank accounts." + backgroundColor: "#85bbf0" + color: "#000000" + + - id: "14" + name: "Reset Password Controller" + subtitle: "[Component: Spring MVC Rest Controller]" + description: "Allows users to reset their passwords with a single use URL." + backgroundColor: "#85bbf0" + color: "#000000" + + - id: "15" + name: "Security Component" + subtitle: "[Component: Spring Bean]" + description: "Provides functionality related to signing in, changing passwords, etc." + backgroundColor: "#85bbf0" + color: "#000000" + + - id: "16" + name: "Mainframe Banking System Facade" + subtitle: "[Component: Spring Bean]" + description: "A facade onto the mainframe banking system." + backgroundColor: "#85bbf0" + color: "#000000" + + - id: "17" + name: "E-mail Component" + subtitle: "[Component: Spring Bean]" + description: "Sends e-mails to users." + backgroundColor: "#85bbf0" + color: "#000000" + + - id: "18" + name: "Database" + subtitle: "[Container: Oracle Database Schema]" + description: "Stores user registration information, hashed authentication credentials, access logs, etc." + backgroundColor: "#438dd5" + color: "#ffffff" + + - id: "8" + name: "Single-Page Application" + subtitle: "[Container: JavaScript and Angular]" + description: "Provides all of the Internet banking functionality to customers via their web browser." + backgroundColor: "#438dd5" + color: "#ffffff" + + - id: "9" + name: "Mobile App" + subtitle: "[Container: Xamarin]" + description: "Provides a limited subset of the Internet banking functionality to customers via their mobile device." + backgroundColor: "#438dd5" + color: "#ffffff" + + - id: "50" + name: "Developer Laptop" + subtitle: "[Deployment Node: Microsoft Windows 10 or Apple macOS]" + backgroundColor: "#ffffff" + color: "#444444" + + children: + - id: "51" + name: "Web Browser" + subtitle: "[Deployment Node: Chrome, Firefox, Safari, or Edge]" + backgroundColor: "#ffffff" + color: "#444444" + + children: + - id: "52" + name: "Single-Page Application" + subtitle: "[Container: JavaScript and Angular]" + description: "Provides all of the Internet banking functionality to customers via their web browser." + backgroundColor: "#438dd5" + color: "#ffffff" + + - id: "53" + name: "Docker Container - Web Server" + subtitle: "[Deployment Node: Docker]" + backgroundColor: "#ffffff" + color: "#444444" + + children: + - id: "54" + name: "Apache Tomcat" + subtitle: "[Deployment Node: Apache Tomcat 8.x]" + backgroundColor: "#ffffff" + color: "#444444" + + children: + - id: "55" + name: "Web Application" + subtitle: "[Container: Java and Spring MVC]" + description: "Delivers the static content and the Internet banking single page application." + backgroundColor: "#438dd5" + color: "#ffffff" + + - id: "57" + name: "API Application" + subtitle: "[Container: Java and Spring MVC]" + description: "Provides Internet banking functionality via a JSON/HTTPS API." + backgroundColor: "#438dd5" + color: "#ffffff" + + - id: "59" + name: "Docker Container - Database Server" + subtitle: "[Deployment Node: Docker]" + backgroundColor: "#ffffff" + color: "#444444" + + children: + - id: "60" + name: "Database Server" + subtitle: "[Deployment Node: Oracle 12c]" + backgroundColor: "#ffffff" + color: "#444444" + + children: + - id: "61" + name: "Database" + subtitle: "[Container: Oracle Database Schema]" + description: "Stores user registration information, hashed authentication credentials, access logs, etc." + backgroundColor: "#438dd5" + color: "#ffffff" + + - id: "63" + name: "Big Bank plc" + subtitle: "[Deployment Node: Big Bank plc data center]" + backgroundColor: "#ffffff" + color: "#444444" + + children: + - id: "64" + name: "bigbank-dev001" + subtitle: "[Deployment Node]" + backgroundColor: "#ffffff" + color: "#444444" + + children: + - id: "65" + name: "Mainframe Banking System" + subtitle: "[Software System]" + description: "Stores all of the core banking information about customers, accounts, transactions, etc." + backgroundColor: "#999999" + color: "#ffffff" + + - id: "67" + name: "Customer's mobile device" + subtitle: "[Deployment Node: Apple iOS or Android]" + backgroundColor: "#ffffff" + color: "#444444" + + children: + - id: "68" + name: "Mobile App" + subtitle: "[Container: Xamarin]" + description: "Provides a limited subset of the Internet banking functionality to customers via their mobile device." + backgroundColor: "#438dd5" + color: "#ffffff" + + - id: "69" + name: "Customer's computer" + subtitle: "[Deployment Node: Microsoft Windows or Apple macOS]" + backgroundColor: "#ffffff" + color: "#444444" + + children: + - id: "70" + name: "Web Browser" + subtitle: "[Deployment Node: Chrome, Firefox, Safari, or Edge]" + backgroundColor: "#ffffff" + color: "#444444" + + children: + - id: "71" + name: "Single-Page Application" + subtitle: "[Container: JavaScript and Angular]" + description: "Provides all of the Internet banking functionality to customers via their web browser." + backgroundColor: "#438dd5" + color: "#ffffff" + + - id: "72" + name: "Big Bank plc" + subtitle: "[Deployment Node: Big Bank plc data center]" + backgroundColor: "#ffffff" + color: "#444444" + + children: + - id: "73" + name: "bigbank-web***" + subtitle: "[Deployment Node: Ubuntu 16.04 LTS]" + backgroundColor: "#ffffff" + color: "#444444" + + children: + - id: "74" + name: "Apache Tomcat" + subtitle: "[Deployment Node: Apache Tomcat 8.x]" + backgroundColor: "#ffffff" + color: "#444444" + + children: + - id: "75" + name: "Web Application" + subtitle: "[Container: Java and Spring MVC]" + description: "Delivers the static content and the Internet banking single page application." + backgroundColor: "#438dd5" + color: "#ffffff" + + - id: "77" + name: "bigbank-api***" + subtitle: "[Deployment Node: Ubuntu 16.04 LTS]" + backgroundColor: "#ffffff" + color: "#444444" + + children: + - id: "78" + name: "Apache Tomcat" + subtitle: "[Deployment Node: Apache Tomcat 8.x]" + backgroundColor: "#ffffff" + color: "#444444" + + children: + - id: "79" + name: "API Application" + subtitle: "[Container: Java and Spring MVC]" + description: "Provides Internet banking functionality via a JSON/HTTPS API." + backgroundColor: "#438dd5" + color: "#ffffff" + + - id: "82" + name: "bigbank-db01" + subtitle: "[Deployment Node: Ubuntu 16.04 LTS]" + backgroundColor: "#ffffff" + color: "#444444" + + children: + - id: "83" + name: "Oracle - Primary" + subtitle: "[Deployment Node: Oracle 12c]" + backgroundColor: "#ffffff" + color: "#444444" + + children: + - id: "84" + name: "Database" + subtitle: "[Container: Oracle Database Schema]" + description: "Stores user registration information, hashed authentication credentials, access logs, etc." + backgroundColor: "#438dd5" + color: "#ffffff" + + - id: "86" + name: "bigbank-db02" + subtitle: "[Deployment Node: Ubuntu 16.04 LTS]" + backgroundColor: "#ffffff" + color: "#444444" + + children: + - id: "87" + name: "Oracle - Secondary" + subtitle: "[Deployment Node: Oracle 12c]" + backgroundColor: "#ffffff" + color: "#444444" + + children: + - id: "88" + name: "Database" + subtitle: "[Container: Oracle Database Schema]" + description: "Stores user registration information, hashed authentication credentials, access logs, etc." + backgroundColor: "#438dd5" + color: "#ffffff" + + - id: "90" + name: "bigbank-prod001" + subtitle: "[Deployment Node]" + backgroundColor: "#ffffff" + color: "#444444" + + children: + - id: "91" + name: "Mainframe Banking System" + subtitle: "[Software System]" + description: "Stores all of the core banking information about customers, accounts, transactions, etc." + backgroundColor: "#999999" + color: "#ffffff" + +perspectives: + - name: Static Structure + relations: + - from: "1" + to: "7" + label: "Views account balances, and makes payments using" + color: "#444444" + + - from: "1" + to: "2" + label: "Asks questions to" + description: "Telephone" + color: "#444444" + + - from: "1" + to: "6" + label: "Withdraws cash using" + color: "#444444" + + - from: "2" + to: "4" + label: "Uses" + color: "#444444" + + - from: "3" + to: "4" + label: "Uses" + color: "#444444" + + - from: "5" + to: "1" + label: "Sends e-mails to" + color: "#444444" + + - from: "6" + to: "4" + label: "Uses" + color: "#444444" + + - from: "7" + to: "4" + label: "Gets account information from, and makes payments using" + color: "#444444" + + - from: "7" + to: "5" + label: "Sends e-mail using" + color: "#444444" + + - from: "1" + to: "10" + label: "Visits bigbank.com/ib using" + description: "HTTPS" + color: "#444444" + + - from: "1" + to: "8" + label: "Views account balances, and makes payments using" + color: "#444444" + + - from: "1" + to: "9" + label: "Views account balances, and makes payments using" + color: "#444444" + + - from: "10" + to: "8" + label: "Delivers to the customer's web browser" + color: "#444444" + + - from: "11" + to: "18" + label: "Reads from and writes to" + description: "SQL/TCP" + color: "#444444" + + - from: "11" + to: "4" + label: "Makes API calls to" + description: "XML/HTTPS" + color: "#444444" + + - from: "11" + to: "5" + label: "Sends e-mail using" + color: "#444444" + + - from: "8" + to: "11" + label: "Makes API calls to" + description: "JSON/HTTPS" + color: "#444444" + + - from: "9" + to: "11" + label: "Makes API calls to" + description: "JSON/HTTPS" + color: "#444444" + + - from: "12" + to: "15" + label: "Uses" + color: "#444444" + + - from: "13" + to: "16" + label: "Uses" + color: "#444444" + + - from: "14" + to: "15" + label: "Uses" + color: "#444444" + + - from: "14" + to: "17" + label: "Uses" + color: "#444444" + + - from: "15" + to: "18" + label: "Reads from and writes to" + description: "SQL/TCP" + color: "#444444" + + - from: "16" + to: "4" + label: "Makes API calls to" + description: "XML/HTTPS" + color: "#444444" + + - from: "17" + to: "5" + label: "Sends e-mail using" + color: "#444444" + + - from: "8" + to: "12" + label: "Makes API calls to" + description: "JSON/HTTPS" + color: "#444444" + + - from: "8" + to: "13" + label: "Makes API calls to" + description: "JSON/HTTPS" + color: "#444444" + + - from: "8" + to: "14" + label: "Makes API calls to" + description: "JSON/HTTPS" + color: "#444444" + + - from: "9" + to: "12" + label: "Makes API calls to" + description: "JSON/HTTPS" + color: "#444444" + + - from: "9" + to: "13" + label: "Makes API calls to" + description: "JSON/HTTPS" + color: "#444444" + + - from: "9" + to: "14" + label: "Makes API calls to" + description: "JSON/HTTPS" + color: "#444444" + + - name: Dynamic - API Application - Dynamic + sequence: + start: "8" + steps: + - to: "12" + label: "1. Submits credentials to" + description: "JSON/HTTPS" + color: "#444444" + + - to: "15" + label: "2. Validates credentials using" + color: "#444444" + + - to: "18" + label: "3. select * from users where username = ?" + description: "SQL/TCP" + color: "#444444" + + - to: "15" + label: "4. Returns user data to" + description: "SQL/TCP" + color: "#444444" + + - to: "12" + label: "5. Returns true if the hashed password matches" + color: "#444444" + + - to: "8" + label: "6. Sends back an authentication token to" + description: "JSON/HTTPS" + color: "#444444" + + - name: Deployment - Development + relations: + - from: "52" + to: "57" + label: "Makes API calls to" + description: "JSON/HTTPS" + color: "#444444" + - from: "55" + to: "52" + label: "Delivers to the customer's web browser" + color: "#444444" + - from: "57" + to: "61" + label: "Reads from and writes to" + description: "SQL/TCP" + color: "#444444" + - from: "57" + to: "65" + label: "Makes API calls to" + description: "XML/HTTPS" + color: "#444444" + - name: Deployment - Live + relations: + - from: "68" + to: "79" + label: "Makes API calls to" + description: "JSON/HTTPS" + color: "#444444" + - from: "71" + to: "79" + label: "Makes API calls to" + description: "JSON/HTTPS" + color: "#444444" + - from: "75" + to: "71" + label: "Delivers to the customer's web browser" + color: "#444444" + - from: "79" + to: "84" + label: "Reads from and writes to" + description: "SQL/TCP" + color: "#444444" + - from: "79" + to: "88" + label: "Reads from and writes to" + description: "SQL/TCP" + color: "#444444" + - from: "79" + to: "91" + label: "Makes API calls to" + description: "XML/HTTPS" + color: "#444444\"""", export.getDefinition()); } @Test @Tag("IntegrationTest") void test_AmazonWebServicesExample() throws Exception { - Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/structurizr-54915-workspace.json")); + Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/amazon-web-services.json")); workspace.getViews().getConfiguration().getStyles().addElementStyle("Amazon Web Services - Route 53").addProperty(IlographExporter.ILOGRAPH_ICON, "AWS/Networking/Route-53.svg"); ThemeUtils.loadThemes(workspace); IlographExporter ilographExporter = new IlographExporter(); WorkspaceExport export = ilographExporter.export(workspace); - String expected = readFile(new File("./src/test/java/com/structurizr/export/ilograph/54915.ilograph")); - assertEquals(expected, export.getDefinition()); + assertEquals(""" + resources: + - id: "1" + name: "X" + subtitle: "[Software System]" + backgroundColor: "#ffffff" + color: "#444444" + + children: + - id: "2" + name: "Web Application" + subtitle: "[Container: Java and Spring Boot]" + backgroundColor: "#ffffff" + color: "#444444" + + - id: "3" + name: "Database Schema" + subtitle: "[Container]" + backgroundColor: "#ffffff" + color: "#444444" + + - id: "5" + name: "Amazon Web Services" + subtitle: "[Deployment Node]" + backgroundColor: "#ffffff" + color: "#232f3e" + icon: "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/aws-cloud.png" + + children: + - id: "6" + name: "US-East-1" + subtitle: "[Deployment Node]" + backgroundColor: "#ffffff" + color: "#147eba" + icon: "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/region.png" + + children: + - id: "10" + name: "Autoscaling group" + subtitle: "[Deployment Node]" + backgroundColor: "#ffffff" + color: "#cc2264" + icon: "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/aws-auto-scaling.png" + + children: + - id: "11" + name: "Amazon EC2 - Ubuntu server" + subtitle: "[Deployment Node]" + backgroundColor: "#ffffff" + color: "#d86613" + icon: "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/amazon-ec2.png" + + children: + - id: "12" + name: "Web Application" + subtitle: "[Container: Java and Spring Boot]" + backgroundColor: "#ffffff" + color: "#444444" + + - id: "14" + name: "Amazon RDS" + subtitle: "[Deployment Node]" + backgroundColor: "#ffffff" + color: "#3b48cc" + icon: "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/amazon-rds.png" + + children: + - id: "15" + name: "MySQL" + subtitle: "[Deployment Node]" + backgroundColor: "#ffffff" + color: "#3b48cc" + icon: "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/amazon-rds-mysql-instance.png" + + children: + - id: "16" + name: "Database Schema" + subtitle: "[Container]" + backgroundColor: "#ffffff" + color: "#444444" + + - id: "7" + name: "DNS router" + subtitle: "[Infrastructure Node: Route 53]" + description: "Routes incoming requests based upon domain name." + backgroundColor: "#ffffff" + color: "#693cc5" + icon: "AWS/Networking/Route-53.svg" + + - id: "8" + name: "Load Balancer" + subtitle: "[Infrastructure Node: Elastic Load Balancer]" + description: "Automatically distributes incoming application traffic." + backgroundColor: "#ffffff" + color: "#693cc5" + icon: "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/elastic-load-balancing.png" + + perspectives: + - name: Static Structure + relations: + - from: "2" + to: "3" + label: "Reads from and writes to" + description: "MySQL Protocol/SSL" + color: "#444444" + + - name: Deployment - Live + relations: + - from: "12" + to: "16" + label: "Reads from and writes to" + description: "MySQL Protocol/SSL" + color: "#444444" + - from: "7" + to: "8" + label: "Forwards requests to" + description: "HTTPS" + color: "#444444" + - from: "8" + to: "12" + label: "Forwards requests to" + description: "HTTPS" + color: "#444444\"""", export.getDefinition()); } @Test @@ -50,27 +786,29 @@ void test_renderCustomElements() { a.uses(b, "Uses"); WorkspaceExport export = new IlographExporter().export(workspace); - assertEquals("resources:\n" + - " - id: \"1\"\n" + - " name: \"A\"\n" + - " subtitle: \"\"\n" + - " backgroundColor: \"#dddddd\"\n" + - " color: \"#000000\"\n" + - "\n" + - " - id: \"2\"\n" + - " name: \"B\"\n" + - " subtitle: \"[Custom]\"\n" + - " description: \"Description\"\n" + - " backgroundColor: \"#dddddd\"\n" + - " color: \"#000000\"\n" + - "\n" + - "perspectives:\n" + - " - name: Static Structure\n" + - " relations:\n" + - " - from: \"1\"\n" + - " to: \"2\"\n" + - " label: \"Uses\"\n" + - " color: \"#707070\"\n", export.getDefinition()); + assertEquals(""" + resources: + - id: "1" + name: "A" + subtitle: "" + backgroundColor: "#ffffff" + color: "#444444" + + - id: "2" + name: "B" + subtitle: "[Custom]" + description: "Description" + backgroundColor: "#ffffff" + color: "#444444" + + perspectives: + - name: Static Structure + relations: + - from: "1" + to: "2" + label: "Uses" + color: "#444444" + """, export.getDefinition()); } @Test diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/54915-AmazonWebServicesDeployment.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/54915-AmazonWebServicesDeployment.mmd deleted file mode 100644 index 4af05234c..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/54915-AmazonWebServicesDeployment.mmd +++ /dev/null @@ -1,48 +0,0 @@ -graph LR - linkStyle default fill:#ffffff - - subgraph diagram ["Spring PetClinic - Deployment - Live"] - style diagram fill:#ffffff,stroke:#ffffff - - subgraph 5 ["Amazon Web Services"] - style 5 fill:#ffffff,stroke:#232f3e,color:#232f3e - - subgraph 6 ["US-East-1"] - style 6 fill:#ffffff,stroke:#147eba,color:#147eba - - subgraph 12 ["Amazon RDS"] - style 12 fill:#ffffff,stroke:#3b48cc,color:#3b48cc - - subgraph 13 ["MySQL"] - style 13 fill:#ffffff,stroke:#3b48cc,color:#3b48cc - - 14[("

    Database
    [Container: Relational database schema]
    Stores information regarding
    the veterinarians, the
    clients, and their pets.
    ")] - style 14 fill:#ffffff,stroke:#b2b2b2,color:#000000 - end - - end - - 7("
    Route 53
    [Infrastructure Node]
    Highly available and scalable
    cloud DNS service.
    ") - style 7 fill:#ffffff,stroke:#693cc5,color:#693cc5 - 8("
    Elastic Load Balancer
    [Infrastructure Node]
    Automatically distributes
    incoming application traffic.
    ") - style 8 fill:#ffffff,stroke:#693cc5,color:#693cc5 - subgraph 9 ["Autoscaling group"] - style 9 fill:#ffffff,stroke:#cc2264,color:#cc2264 - - subgraph 10 ["Amazon EC2"] - style 10 fill:#ffffff,stroke:#d86613,color:#d86613 - - 11("
    Web Application
    [Container: Java and Spring Boot]
    Allows employees to view and
    manage information regarding
    the veterinarians, the
    clients, and their pets.
    ") - style 11 fill:#ffffff,stroke:#b2b2b2,color:#000000 - end - - end - - end - - end - - 11-. "
    Reads from and writes to
    [MySQL Protocol/SSL]
    " .->14 - 7-. "
    Forwards requests to
    [HTTPS]
    " .->8 - 8-. "
    Forwards requests to
    [HTTPS]
    " .->11 - end \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/MermaidDiagramExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/mermaid/MermaidDiagramExporterTests.java index ed49a4431..065b56220 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/MermaidDiagramExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/MermaidDiagramExporterTests.java @@ -6,6 +6,7 @@ import com.structurizr.model.*; import com.structurizr.util.WorkspaceUtils; import com.structurizr.view.*; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @@ -16,54 +17,10 @@ public class MermaidDiagramExporterTests extends AbstractExporterTests { - @Test - public void test_BigBankPlcExample() throws Exception { - Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/structurizr-36141-workspace.json")); - MermaidDiagramExporter exporter = new MermaidDiagramExporter(); - - Collection diagrams = exporter.export(workspace); - assertEquals(7, diagrams.size()); - - Diagram diagram = diagrams.stream().filter(d -> d.getKey().equals("SystemLandscape")).findFirst().get(); - String expected = readFile(new File("./src/test/java/com/structurizr/export/mermaid/36141-SystemLandscape.mmd")); - assertEquals(expected, diagram.getDefinition()); - - diagram = diagrams.stream().filter(d -> d.getKey().equals("SystemContext")).findFirst().get(); - expected = readFile(new File("./src/test/java/com/structurizr/export/mermaid/36141-SystemContext.mmd")); - assertEquals(expected, diagram.getDefinition()); - - diagram = diagrams.stream().filter(d -> d.getKey().equals("Containers")).findFirst().get(); - expected = readFile(new File("./src/test/java/com/structurizr/export/mermaid/36141-Containers.mmd")); - assertEquals(expected, diagram.getDefinition()); - - diagram = diagrams.stream().filter(d -> d.getKey().equals("Components")).findFirst().get(); - expected = readFile(new File("./src/test/java/com/structurizr/export/mermaid/36141-Components.mmd")); - assertEquals(expected, diagram.getDefinition()); - - diagram = diagrams.stream().filter(d -> d.getKey().equals("SignIn")).findFirst().get(); - expected = readFile(new File("./src/test/java/com/structurizr/export/mermaid/36141-SignIn.mmd")); - assertEquals(expected, diagram.getDefinition()); - - diagram = diagrams.stream().filter(md -> md.getKey().equals("DevelopmentDeployment")).findFirst().get(); - expected = readFile(new File("./src/test/java/com/structurizr/export/mermaid/36141-DevelopmentDeployment.mmd")); - assertEquals(expected, diagram.getDefinition()); - - diagram = diagrams.stream().filter(md -> md.getKey().equals("LiveDeployment")).findFirst().get(); - expected = readFile(new File("./src/test/java/com/structurizr/export/mermaid/36141-LiveDeployment.mmd")); - assertEquals(expected, diagram.getDefinition()); - - // and the sequence diagram version - workspace.getViews().getConfiguration().addProperty(exporter.MERMAID_SEQUENCE_DIAGRAM_PROPERTY, "true"); - diagrams = exporter.export(workspace); - diagram = diagrams.stream().filter(d -> d.getKey().equals("SignIn")).findFirst().get(); - expected = readFile(new File("./src/test/java/com/structurizr/export/mermaid/36141-SignIn-sequence.mmd")); - assertEquals(expected, diagram.getDefinition()); - } - @Test @Tag("IntegrationTest") public void test_AmazonWebServicesExample() throws Exception { - Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/structurizr-54915-workspace.json")); + Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/amazon-web-services.json")); ThemeUtils.loadThemes(workspace); workspace.getViews().getDeploymentViews().iterator().next().enableAutomaticLayout(AutomaticLayout.RankDirection.LeftRight, 300, 300); @@ -72,8 +29,56 @@ public void test_AmazonWebServicesExample() throws Exception { assertEquals(1, diagrams.size()); Diagram diagram = diagrams.stream().findFirst().get(); - String expected = readFile(new File("./src/test/java/com/structurizr/export/mermaid/54915-AmazonWebServicesDeployment.mmd")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + graph LR + linkStyle default fill:#ffffff + + subgraph diagram ["X - Deployment - Live"] + style diagram fill:#ffffff,stroke:#ffffff + + subgraph 5 ["Amazon Web Services"] + style 5 fill:#ffffff,stroke:#232f3e,color:#232f3e + + subgraph 6 ["US-East-1"] + style 6 fill:#ffffff,stroke:#147eba,color:#147eba + + subgraph 10 ["Autoscaling group"] + style 10 fill:#ffffff,stroke:#cc2264,color:#cc2264 + + subgraph 11 ["Amazon EC2 - Ubuntu server"] + style 11 fill:#ffffff,stroke:#d86613,color:#d86613 + + 12("
    Web Application
    [Container: Java and Spring Boot]
    ") + style 12 fill:#ffffff,stroke:#444444,color:#444444 + end + + end + + subgraph 14 ["Amazon RDS"] + style 14 fill:#ffffff,stroke:#3b48cc,color:#3b48cc + + subgraph 15 ["MySQL"] + style 15 fill:#ffffff,stroke:#3b48cc,color:#3b48cc + + 16[("
    Database Schema
    [Container]
    ")] + style 16 fill:#ffffff,stroke:#444444,color:#444444 + end + + end + + 7["
    DNS router
    [Infrastructure Node: Route 53]
    Routes incoming requests
    based upon domain name.
    "] + style 7 fill:#ffffff,stroke:#693cc5,color:#693cc5 + 8["
    Load Balancer
    [Infrastructure Node: Elastic Load Balancer]
    Automatically distributes
    incoming application traffic.
    "] + style 8 fill:#ffffff,stroke:#693cc5,color:#693cc5 + end + + end + + 8-. "
    Forwards requests to
    [HTTPS]
    " .->12 + 12-. "
    Reads from and writes to
    [MySQL Protocol/SSL]
    " .->16 + 7-. "
    Forwards requests to
    [HTTPS]
    " .->8 + + end""", diagram.getDefinition()); } @Test @@ -86,16 +91,102 @@ public void test_GroupsExample() throws Exception { assertEquals(3, diagrams.size()); Diagram diagram = diagrams.stream().filter(md -> md.getKey().equals("SystemLandscape")).findFirst().get(); - String expected = readFile(new File("./src/test/java/com/structurizr/export/mermaid/groups-SystemLandscape.mmd")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + graph TB + linkStyle default fill:#ffffff + + subgraph diagram ["System Landscape"] + style diagram fill:#ffffff,stroke:#ffffff + + subgraph group1 ["Group 1"] + style group1 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 + + 2["
    B
    [Software System]
    "] + style 2 fill:#ffffff,stroke:#444444,color:#444444 + end + + subgraph group2 ["Group 2"] + style group2 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 + + 3["
    C
    [Software System]
    "] + style 3 fill:#ffffff,stroke:#444444,color:#444444 + subgraph group3 ["Group 3"] + style group3 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 + + 4["
    D
    [Software System]
    "] + style 4 fill:#ffffff,stroke:#444444,color:#444444 + end + + end + + 1["
    A
    [Software System]
    "] + style 1 fill:#ffffff,stroke:#444444,color:#444444 + + 2-. "
    " .->3 + 3-. "
    " .->4 + 1-. "
    " .->2 + + end""", diagram.getDefinition()); diagram = diagrams.stream().filter(md -> md.getKey().equals("Containers")).findFirst().get(); - expected = readFile(new File("./src/test/java/com/structurizr/export/mermaid/groups-Containers.mmd")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + graph TB + linkStyle default fill:#ffffff + + subgraph diagram ["D - Containers"] + style diagram fill:#ffffff,stroke:#ffffff + + 3["
    C
    [Software System]
    "] + style 3 fill:#ffffff,stroke:#444444,color:#444444 + + subgraph 4 ["D"] + style 4 fill:#ffffff,stroke:#444444,color:#444444 + + subgraph group1 ["Group 4"] + style group1 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 + + 6["
    F
    [Container]
    "] + style 6 fill:#ffffff,stroke:#444444,color:#444444 + end + + 5["
    E
    [Container]
    "] + style 5 fill:#ffffff,stroke:#444444,color:#444444 + end + + 3-. "
    " .->5 + 3-. "
    " .->6 + + end""", diagram.getDefinition()); diagram = diagrams.stream().filter(md -> md.getKey().equals("Components")).findFirst().get(); - expected = readFile(new File("./src/test/java/com/structurizr/export/mermaid/groups-Components.mmd")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + graph TB + linkStyle default fill:#ffffff + + subgraph diagram ["D - F - Components"] + style diagram fill:#ffffff,stroke:#ffffff + + 3["
    C
    [Software System]
    "] + style 3 fill:#ffffff,stroke:#444444,color:#444444 + + subgraph 6 ["F"] + style 6 fill:#ffffff,stroke:#444444,color:#444444 + + subgraph group1 ["Group 5"] + style group1 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 + + 8["
    H
    [Component]
    "] + style 8 fill:#ffffff,stroke:#444444,color:#444444 + end + + 7["
    G
    [Component]
    "] + style 7 fill:#ffffff,stroke:#444444,color:#444444 + end + + 3-. "
    " .->7 + 3-. "
    " .->8 + + end""", diagram.getDefinition()); } @Test @@ -125,8 +216,50 @@ public void test_NestedGroupsExample() throws Exception { Collection diagrams = exporter.export(workspace); Diagram diagram = diagrams.stream().filter(md -> md.getKey().equals("SystemLandscape")).findFirst().get(); - String expected = readFile(new File("./src/test/java/com/structurizr/export/mermaid/nested-groups.mmd")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + graph TB + linkStyle default fill:#ffffff + + subgraph diagram ["System Landscape"] + style diagram fill:#ffffff,stroke:#ffffff + + subgraph group1 ["Organisation 1"] + style group1 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 + + 3["
    Organisation 1
    [Software System]
    "] + style 3 fill:#ffffff,stroke:#444444,color:#444444 + subgraph group2 ["Department 1"] + style group2 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 + + 5["
    Department 1
    [Software System]
    "] + style 5 fill:#ffffff,stroke:#444444,color:#444444 + subgraph group3 ["Team 1"] + style group3 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 + + 1["
    Team 1
    [Software System]
    "] + style 1 fill:#ffffff,stroke:#444444,color:#444444 + end + + subgraph group4 ["Team 2"] + style group4 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 + + 2["
    Team 2
    [Software System]
    "] + style 2 fill:#ffffff,stroke:#444444,color:#444444 + end + + end + + end + + subgraph group5 ["Organisation 2"] + style group5 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 + + 4["
    Organisation 2
    [Software System]
    "] + style 4 fill:#ffffff,stroke:#444444,color:#444444 + end + + + end""", diagram.getDefinition()); } @Test @@ -145,28 +278,29 @@ public void test_renderContainerDiagramWithExternalContainers() { Diagram diagram = new MermaidDiagramExporter().export(containerView); assertEquals(""" -graph TB - linkStyle default fill:#ffffff - - subgraph diagram ["Software System 1 - Containers"] - style diagram fill:#ffffff,stroke:#ffffff - - subgraph 1 ["Software System 1"] - style 1 fill:#ffffff,stroke:#9a9a9a,color:#9a9a9a - - 2["
    Container 1
    [Container]
    "] - style 2 fill:#dddddd,stroke:#9a9a9a,color:#000000 - end - - subgraph 3 ["Software System 2"] - style 3 fill:#ffffff,stroke:#9a9a9a,color:#9a9a9a - - 4["
    Container 2
    [Container]
    "] - style 4 fill:#dddddd,stroke:#9a9a9a,color:#000000 - end - - 2-. "
    Uses
    " .->4 - end""", diagram.getDefinition()); + graph TB + linkStyle default fill:#ffffff + + subgraph diagram ["Software System 1 - Containers"] + style diagram fill:#ffffff,stroke:#ffffff + + subgraph 1 ["Software System 1"] + style 1 fill:#ffffff,stroke:#444444,color:#444444 + + 2["
    Container 1
    [Container]
    "] + style 2 fill:#ffffff,stroke:#444444,color:#444444 + end + + subgraph 3 ["Software System 2"] + style 3 fill:#ffffff,stroke:#444444,color:#444444 + + 4["
    Container 2
    [Container]
    "] + style 4 fill:#ffffff,stroke:#444444,color:#444444 + end + + 2-. "
    Uses
    " .->4 + + end""", diagram.getDefinition()); } @Test @@ -187,28 +321,29 @@ public void test_renderComponentDiagramWithExternalComponents() { Diagram diagram = new MermaidDiagramExporter().export(componentView); assertEquals(""" -graph TB - linkStyle default fill:#ffffff - - subgraph diagram ["Software System 1 - Container 1 - Components"] - style diagram fill:#ffffff,stroke:#ffffff - - subgraph 2 ["Container 1"] - style 2 fill:#ffffff,stroke:#9a9a9a,color:#9a9a9a - - 3["
    Component 1
    [Component]
    "] - style 3 fill:#dddddd,stroke:#9a9a9a,color:#000000 - end - - subgraph 5 ["Container 2"] - style 5 fill:#ffffff,stroke:#9a9a9a,color:#9a9a9a - - 6["
    Component 2
    [Component]
    "] - style 6 fill:#dddddd,stroke:#9a9a9a,color:#000000 - end - - 3-. "
    Uses
    " .->6 - end""", diagram.getDefinition()); + graph TB + linkStyle default fill:#ffffff + + subgraph diagram ["Software System 1 - Container 1 - Components"] + style diagram fill:#ffffff,stroke:#ffffff + + subgraph 2 ["Container 1"] + style 2 fill:#ffffff,stroke:#444444,color:#444444 + + 3["
    Component 1
    [Component]
    "] + style 3 fill:#ffffff,stroke:#444444,color:#444444 + end + + subgraph 5 ["Container 2"] + style 5 fill:#ffffff,stroke:#444444,color:#444444 + + 6["
    Component 2
    [Component]
    "] + style 6 fill:#ffffff,stroke:#444444,color:#444444 + end + + 3-. "
    Uses
    " .->6 + + end""", diagram.getDefinition()); } @Test @@ -227,69 +362,69 @@ public void test_renderGroupStyles() { MermaidDiagramExporter exporter = new MermaidDiagramExporter(); Diagram diagram = exporter.export(view); assertEquals(""" -graph TB - linkStyle default fill:#ffffff - - subgraph diagram ["System Landscape"] - style diagram fill:#ffffff,stroke:#ffffff - - subgraph group1 ["Group 1"] - style group1 fill:#ffffff,stroke:#111111,color:#111111,stroke-dasharray:5 - - 1["
    User 1
    [Person]
    "] - style 1 fill:#dddddd,stroke:#9a9a9a,color:#000000 - end - - subgraph group2 ["Group 2"] - style group2 fill:#ffffff,stroke:#222222,color:#222222,stroke-dasharray:5 - - 2["
    User 2
    [Person]
    "] - style 2 fill:#dddddd,stroke:#9a9a9a,color:#000000 - end - - subgraph group3 ["Group 3"] - style group3 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 - - 3["
    User 3
    [Person]
    "] - style 3 fill:#dddddd,stroke:#9a9a9a,color:#000000 - end - - - end""", diagram.getDefinition()); + graph TB + linkStyle default fill:#ffffff + + subgraph diagram ["System Landscape"] + style diagram fill:#ffffff,stroke:#ffffff + + subgraph group1 ["Group 1"] + style group1 fill:#ffffff,stroke:#111111,color:#111111,stroke-dasharray:5 + + 1["
    User 1
    [Person]
    "] + style 1 fill:#ffffff,stroke:#444444,color:#444444 + end + + subgraph group2 ["Group 2"] + style group2 fill:#ffffff,stroke:#222222,color:#222222,stroke-dasharray:5 + + 2["
    User 2
    [Person]
    "] + style 2 fill:#ffffff,stroke:#444444,color:#444444 + end + + subgraph group3 ["Group 3"] + style group3 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 + + 3["
    User 3
    [Person]
    "] + style 3 fill:#ffffff,stroke:#444444,color:#444444 + end + + + end""", diagram.getDefinition()); workspace.getViews().getConfiguration().getStyles().addElementStyle("Group").color("#aabbcc"); diagram = exporter.export(view); assertEquals(""" -graph TB - linkStyle default fill:#ffffff - - subgraph diagram ["System Landscape"] - style diagram fill:#ffffff,stroke:#ffffff - - subgraph group1 ["Group 1"] - style group1 fill:#ffffff,stroke:#111111,color:#111111,stroke-dasharray:5 - - 1["
    User 1
    [Person]
    "] - style 1 fill:#dddddd,stroke:#9a9a9a,color:#000000 - end - - subgraph group2 ["Group 2"] - style group2 fill:#ffffff,stroke:#222222,color:#222222,stroke-dasharray:5 - - 2["
    User 2
    [Person]
    "] - style 2 fill:#dddddd,stroke:#9a9a9a,color:#000000 - end - - subgraph group3 ["Group 3"] - style group3 fill:#ffffff,stroke:#aabbcc,color:#aabbcc,stroke-dasharray:5 - - 3["
    User 3
    [Person]
    "] - style 3 fill:#dddddd,stroke:#9a9a9a,color:#000000 - end - - - end""", diagram.getDefinition()); + graph TB + linkStyle default fill:#ffffff + + subgraph diagram ["System Landscape"] + style diagram fill:#ffffff,stroke:#ffffff + + subgraph group1 ["Group 1"] + style group1 fill:#ffffff,stroke:#111111,color:#111111,stroke-dasharray:5 + + 1["
    User 1
    [Person]
    "] + style 1 fill:#ffffff,stroke:#444444,color:#444444 + end + + subgraph group2 ["Group 2"] + style group2 fill:#ffffff,stroke:#222222,color:#222222,stroke-dasharray:5 + + 2["
    User 2
    [Person]
    "] + style 2 fill:#ffffff,stroke:#444444,color:#444444 + end + + subgraph group3 ["Group 3"] + style group3 fill:#ffffff,stroke:#aabbcc,color:#aabbcc,stroke-dasharray:5 + + 3["
    User 3
    [Person]
    "] + style 3 fill:#ffffff,stroke:#444444,color:#444444 + end + + + end""", diagram.getDefinition()); } @Test @@ -305,19 +440,21 @@ public void test_renderCustomView() { view.addDefaultElements(); Diagram diagram = new MermaidDiagramExporter().export(view); - assertEquals("graph TB\n" + - " linkStyle default fill:#ffffff\n" + - "\n" + - " subgraph diagram [\"Title\"]\n" + - " style diagram fill:#ffffff,stroke:#ffffff\n" + - "\n" + - " 1[\"
    A
    \"]\n" + - " style 1 fill:#dddddd,stroke:#9a9a9a,color:#000000\n" + - " 2[\"
    B
    [Custom]
    Description
    \"]\n" + - " style 2 fill:#dddddd,stroke:#9a9a9a,color:#000000\n" + - "\n" + - " 1-. \"
    Uses
    \" .->2\n" + - " end", diagram.getDefinition()); + assertEquals(""" + graph TB + linkStyle default fill:#ffffff + + subgraph diagram ["Title"] + style diagram fill:#ffffff,stroke:#ffffff + + 1["
    A
    "] + style 1 fill:#ffffff,stroke:#444444,color:#444444 + 2["
    B
    [Custom]
    Description
    "] + style 2 fill:#ffffff,stroke:#444444,color:#444444 + + 1-. "
    Uses
    " .->2 + + end""", diagram.getDefinition()); } } \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-Components.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-Components.mmd deleted file mode 100644 index 62d5a7742..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-Components.mmd +++ /dev/null @@ -1,26 +0,0 @@ -graph TB - linkStyle default fill:#ffffff - - subgraph diagram ["D - F - Components"] - style diagram fill:#ffffff,stroke:#ffffff - - 3["
    C
    [Software System]
    "] - style 3 fill:#dddddd,stroke:#9a9a9a,color:#000000 - - subgraph 6 ["F"] - style 6 fill:#ffffff,stroke:#9a9a9a,color:#9a9a9a - - subgraph group1 ["Group 5"] - style group1 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 - - 8["
    H
    [Component]
    "] - style 8 fill:#dddddd,stroke:#9a9a9a,color:#000000 - end - - 7["
    G
    [Component]
    "] - style 7 fill:#dddddd,stroke:#9a9a9a,color:#000000 - end - - 3-. "
    " .->7 - 3-. "
    " .->8 - end \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-Containers.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-Containers.mmd deleted file mode 100644 index 768970480..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-Containers.mmd +++ /dev/null @@ -1,26 +0,0 @@ -graph TB - linkStyle default fill:#ffffff - - subgraph diagram ["D - Containers"] - style diagram fill:#ffffff,stroke:#ffffff - - 3["
    C
    [Software System]
    "] - style 3 fill:#dddddd,stroke:#9a9a9a,color:#000000 - - subgraph 4 ["D"] - style 4 fill:#ffffff,stroke:#9a9a9a,color:#9a9a9a - - subgraph group1 ["Group 4"] - style group1 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 - - 6["
    F
    [Container]
    "] - style 6 fill:#dddddd,stroke:#9a9a9a,color:#000000 - end - - 5["
    E
    [Container]
    "] - style 5 fill:#dddddd,stroke:#9a9a9a,color:#000000 - end - - 3-. "
    " .->5 - 3-. "
    " .->6 - end \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-SystemLandscape.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-SystemLandscape.mmd deleted file mode 100644 index 65efa9926..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/groups-SystemLandscape.mmd +++ /dev/null @@ -1,34 +0,0 @@ -graph TB - linkStyle default fill:#ffffff - - subgraph diagram ["System Landscape"] - style diagram fill:#ffffff,stroke:#ffffff - - subgraph group1 ["Group 1"] - style group1 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 - - 2["
    B
    [Software System]
    "] - style 2 fill:#dddddd,stroke:#9a9a9a,color:#000000 - end - - subgraph group2 ["Group 2"] - style group2 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 - - 3["
    C
    [Software System]
    "] - style 3 fill:#dddddd,stroke:#9a9a9a,color:#000000 - subgraph group3 ["Group 3"] - style group3 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 - - 4["
    D
    [Software System]
    "] - style 4 fill:#dddddd,stroke:#9a9a9a,color:#000000 - end - - end - - 1["
    A
    [Software System]
    "] - style 1 fill:#dddddd,stroke:#9a9a9a,color:#000000 - - 2-. "
    " .->3 - 3-. "
    " .->4 - 1-. "
    " .->2 - end \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/nested-groups.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/nested-groups.mmd deleted file mode 100644 index 74a0cfe4d..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/nested-groups.mmd +++ /dev/null @@ -1,43 +0,0 @@ -graph TB - linkStyle default fill:#ffffff - - subgraph diagram ["System Landscape"] - style diagram fill:#ffffff,stroke:#ffffff - - subgraph group1 ["Organisation 1"] - style group1 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 - - 3["
    Organisation 1
    [Software System]
    "] - style 3 fill:#dddddd,stroke:#9a9a9a,color:#000000 - subgraph group2 ["Department 1"] - style group2 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 - - 5["
    Department 1
    [Software System]
    "] - style 5 fill:#dddddd,stroke:#9a9a9a,color:#000000 - subgraph group3 ["Team 1"] - style group3 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 - - 1["
    Team 1
    [Software System]
    "] - style 1 fill:#dddddd,stroke:#9a9a9a,color:#000000 - end - - subgraph group4 ["Team 2"] - style group4 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 - - 2["
    Team 2
    [Software System]
    "] - style 2 fill:#dddddd,stroke:#9a9a9a,color:#000000 - end - - end - - end - - subgraph group5 ["Organisation 2"] - style group5 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 - - 4["
    Organisation 2
    [Software System]
    "] - style 4 fill:#dddddd,stroke:#9a9a9a,color:#000000 - end - - - end \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java index b544dbdcf..02cf8273a 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java @@ -6,6 +6,7 @@ import com.structurizr.model.*; import com.structurizr.util.WorkspaceUtils; import com.structurizr.view.*; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @@ -18,7 +19,7 @@ public class C4PlantUMLDiagramExporterTests extends AbstractExporterTests { @Test public void test_BigBankPlcExample() throws Exception { - Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/structurizr-36141-workspace.json")); + Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/big-bank-plc.json")); workspace.getViews().getConfiguration().addProperty(C4PlantUMLExporter.C4PLANTUML_TAGS_PROPERTY, "true"); C4PlantUMLExporter exporter = new C4PlantUMLExporter(); @@ -26,45 +27,465 @@ public void test_BigBankPlcExample() throws Exception { assertEquals(7, diagrams.size()); Diagram diagram = diagrams.stream().filter(d -> d.getKey().equals("SystemLandscape")).findFirst().get(); - String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemLandscape.puml")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + @startuml + title System Landscape + + set separator none + top to bottom direction + + + + !include + !include + + AddElementTag("Software System", $bgColor="#1168bd", $borderColor="#0b4884", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") + AddElementTag("Person,Bank Staff", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") + AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") + AddElementTag("Person,Customer", $bgColor="#08427b", $borderColor="#052e56", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") + + AddRelTag("Relationship", $textColor="#444444", $lineColor="#444444", $lineStyle = DashedLine()) + + AddBoundaryTag("Big Bank plc", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") + Boundary(group_1, "Big Bank plc", $tags="Big Bank plc") { + Person(CustomerServiceStaff, "Customer Service Staff", $descr="Customer service staff within the bank.", $tags="Person,Bank Staff", $link="") + Person(BackOfficeStaff, "Back Office Staff", $descr="Administration and support staff within the bank.", $tags="Person,Bank Staff", $link="") + System(MainframeBankingSystem, "Mainframe Banking System", $descr="Stores all of the core banking information about customers, accounts, transactions, etc.", $tags="Software System,Existing System", $link="") + System(EmailSystem, "E-mail System", $descr="The internal Microsoft Exchange e-mail system.", $tags="Software System,Existing System", $link="") + System(ATM, "ATM", $descr="Allows customers to withdraw cash.", $tags="Software System,Existing System", $link="") + System(InternetBankingSystem, "Internet Banking System", $descr="Allows customers to view information about their bank accounts, and make payments.", $tags="Software System", $link="") + } + + Person(PersonalBankingCustomer, "Personal Banking Customer", $descr="A customer of the bank, with personal bank accounts.", $tags="Person,Customer", $link="") + + Rel(PersonalBankingCustomer, InternetBankingSystem, "Views account balances, and makes payments using", $techn="", $tags="Relationship", $link="") + Rel(InternetBankingSystem, MainframeBankingSystem, "Gets account information from, and makes payments using", $techn="", $tags="Relationship", $link="") + Rel(InternetBankingSystem, EmailSystem, "Sends e-mail using", $techn="", $tags="Relationship", $link="") + Rel(EmailSystem, PersonalBankingCustomer, "Sends e-mails to", $techn="", $tags="Relationship", $link="") + Rel(PersonalBankingCustomer, CustomerServiceStaff, "Asks questions to", $techn="Telephone", $tags="Relationship", $link="") + Rel(CustomerServiceStaff, MainframeBankingSystem, "Uses", $techn="", $tags="Relationship", $link="") + Rel(PersonalBankingCustomer, ATM, "Withdraws cash using", $techn="", $tags="Relationship", $link="") + Rel(ATM, MainframeBankingSystem, "Uses", $techn="", $tags="Relationship", $link="") + Rel(BackOfficeStaff, MainframeBankingSystem, "Uses", $techn="", $tags="Relationship", $link="") + + SHOW_LEGEND(true) + hide stereotypes + @enduml""", diagram.getDefinition()); diagram = diagrams.stream().filter(d -> d.getKey().equals("SystemContext")).findFirst().get(); - expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemContext.puml")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + @startuml + title Internet Banking System - System Context\\nThe system context diagram for the Internet Banking System. + + set separator none + top to bottom direction + + + + !include + !include + + AddElementTag("Software System", $bgColor="#1168bd", $borderColor="#0b4884", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") + AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") + AddElementTag("Person,Customer", $bgColor="#08427b", $borderColor="#052e56", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") + + AddRelTag("Relationship", $textColor="#444444", $lineColor="#444444", $lineStyle = DashedLine()) + + AddBoundaryTag("Big Bank plc", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") + Boundary(group_1, "Big Bank plc", $tags="Big Bank plc") { + System(MainframeBankingSystem, "Mainframe Banking System", $descr="Stores all of the core banking information about customers, accounts, transactions, etc.", $tags="Software System,Existing System", $link="") + System(EmailSystem, "E-mail System", $descr="The internal Microsoft Exchange e-mail system.", $tags="Software System,Existing System", $link="") + System(InternetBankingSystem, "Internet Banking System", $descr="Allows customers to view information about their bank accounts, and make payments.", $tags="Software System", $link="") + } + + Person(PersonalBankingCustomer, "Personal Banking Customer", $descr="A customer of the bank, with personal bank accounts.", $tags="Person,Customer", $link="") + + Rel(PersonalBankingCustomer, InternetBankingSystem, "Views account balances, and makes payments using", $techn="", $tags="Relationship", $link="") + Rel(InternetBankingSystem, MainframeBankingSystem, "Gets account information from, and makes payments using", $techn="", $tags="Relationship", $link="") + Rel(InternetBankingSystem, EmailSystem, "Sends e-mail using", $techn="", $tags="Relationship", $link="") + Rel(EmailSystem, PersonalBankingCustomer, "Sends e-mails to", $techn="", $tags="Relationship", $link="") + + SHOW_LEGEND(true) + hide stereotypes + @enduml""", diagram.getDefinition()); diagram = diagrams.stream().filter(d -> d.getKey().equals("Containers")).findFirst().get(); - expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Containers.puml")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + @startuml + title Internet Banking System - Containers\\nThe container diagram for the Internet Banking System. + + set separator none + top to bottom direction + + + + !include + !include + !include + + AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") + AddElementTag("Container,Mobile App", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") + AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") + AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") + AddElementTag("Container", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") + AddElementTag("Person,Customer", $bgColor="#08427b", $borderColor="#052e56", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") + + AddRelTag("Relationship", $textColor="#444444", $lineColor="#444444", $lineStyle = DashedLine()) + + + AddBoundaryTag("Software System", $bgColor="#ffffff", $borderColor="#0b4884", $fontColor="#0b4884", $shadowing="", $borderStyle="solid") + Person(PersonalBankingCustomer, "Personal Banking Customer", $descr="A customer of the bank, with personal bank accounts.", $tags="Person,Customer", $link="") + System(MainframeBankingSystem, "Mainframe Banking System", $descr="Stores all of the core banking information about customers, accounts, transactions, etc.", $tags="Software System,Existing System", $link="") + System(EmailSystem, "E-mail System", $descr="The internal Microsoft Exchange e-mail system.", $tags="Software System,Existing System", $link="") + + System_Boundary("InternetBankingSystem_boundary", "Internet Banking System", $tags="Software System") { + Container(InternetBankingSystem.WebApplication, "Web Application", $techn="Java and Spring MVC", $descr="Delivers the static content and the Internet banking single page application.", $tags="Container", $link="") + Container(InternetBankingSystem.APIApplication, "API Application", $techn="Java and Spring MVC", $descr="Provides Internet banking functionality via a JSON/HTTPS API.", $tags="Container", $link="") + ContainerDb(InternetBankingSystem.Database, "Database", $techn="Oracle Database Schema", $descr="Stores user registration information, hashed authentication credentials, access logs, etc.", $tags="Container,Database", $link="") + Container(InternetBankingSystem.SinglePageApplication, "Single-Page Application", $techn="JavaScript and Angular", $descr="Provides all of the Internet banking functionality to customers via their web browser.", $tags="Container,Web Browser", $link="") + Container(InternetBankingSystem.MobileApp, "Mobile App", $techn="Xamarin", $descr="Provides a limited subset of the Internet banking functionality to customers via their mobile device.", $tags="Container,Mobile App", $link="") + } + + Rel(EmailSystem, PersonalBankingCustomer, "Sends e-mails to", $techn="", $tags="Relationship", $link="") + Rel(PersonalBankingCustomer, InternetBankingSystem.WebApplication, "Visits bigbank.com/ib using", $techn="HTTPS", $tags="Relationship", $link="") + Rel(PersonalBankingCustomer, InternetBankingSystem.SinglePageApplication, "Views account balances, and makes payments using", $techn="", $tags="Relationship", $link="") + Rel(PersonalBankingCustomer, InternetBankingSystem.MobileApp, "Views account balances, and makes payments using", $techn="", $tags="Relationship", $link="") + Rel(InternetBankingSystem.WebApplication, InternetBankingSystem.SinglePageApplication, "Delivers to the customer's web browser", $techn="", $tags="Relationship", $link="") + Rel(InternetBankingSystem.SinglePageApplication, InternetBankingSystem.APIApplication, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") + Rel(InternetBankingSystem.MobileApp, InternetBankingSystem.APIApplication, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") + Rel(InternetBankingSystem.APIApplication, InternetBankingSystem.Database, "Reads from and writes to", $techn="SQL/TCP", $tags="Relationship", $link="") + Rel(InternetBankingSystem.APIApplication, MainframeBankingSystem, "Makes API calls to", $techn="XML/HTTPS", $tags="Relationship", $link="") + Rel(InternetBankingSystem.APIApplication, EmailSystem, "Sends e-mail using", $techn="", $tags="Relationship", $link="") + + SHOW_LEGEND(true) + hide stereotypes + @enduml""", diagram.getDefinition()); diagram = diagrams.stream().filter(d -> d.getKey().equals("Components")).findFirst().get(); - expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Components.puml")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + @startuml + title Internet Banking System - API Application - Components\\nThe component diagram for the API Application. + + set separator none + top to bottom direction + + + + !include + !include + !include + !include + + AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") + AddElementTag("Component", $bgColor="#85bbf0", $borderColor="#5d82a8", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="solid") + AddElementTag("Container,Mobile App", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") + AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") + AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") + + AddRelTag("Relationship", $textColor="#444444", $lineColor="#444444", $lineStyle = DashedLine()) + + + AddBoundaryTag("Container", $bgColor="#ffffff", $borderColor="#2e6295", $fontColor="#2e6295", $shadowing="", $borderStyle="solid") + System(MainframeBankingSystem, "Mainframe Banking System", $descr="Stores all of the core banking information about customers, accounts, transactions, etc.", $tags="Software System,Existing System", $link="") + System(EmailSystem, "E-mail System", $descr="The internal Microsoft Exchange e-mail system.", $tags="Software System,Existing System", $link="") + Container(InternetBankingSystem.SinglePageApplication, "Single-Page Application", $techn="JavaScript and Angular", $descr="Provides all of the Internet banking functionality to customers via their web browser.", $tags="Container,Web Browser", $link="") + Container(InternetBankingSystem.MobileApp, "Mobile App", $techn="Xamarin", $descr="Provides a limited subset of the Internet banking functionality to customers via their mobile device.", $tags="Container,Mobile App", $link="") + ContainerDb(InternetBankingSystem.Database, "Database", $techn="Oracle Database Schema", $descr="Stores user registration information, hashed authentication credentials, access logs, etc.", $tags="Container,Database", $link="") + + Container_Boundary("InternetBankingSystem.APIApplication_boundary", "API Application", $tags="Container") { + Component(InternetBankingSystem.APIApplication.SignInController, "Sign In Controller", $techn="Spring MVC Rest Controller", $descr="Allows users to sign in to the Internet Banking System.", $tags="Component", $link="") + Component(InternetBankingSystem.APIApplication.AccountsSummaryController, "Accounts Summary Controller", $techn="Spring MVC Rest Controller", $descr="Provides customers with a summary of their bank accounts.", $tags="Component", $link="") + Component(InternetBankingSystem.APIApplication.ResetPasswordController, "Reset Password Controller", $techn="Spring MVC Rest Controller", $descr="Allows users to reset their passwords with a single use URL.", $tags="Component", $link="") + Component(InternetBankingSystem.APIApplication.SecurityComponent, "Security Component", $techn="Spring Bean", $descr="Provides functionality related to signing in, changing passwords, etc.", $tags="Component", $link="") + Component(InternetBankingSystem.APIApplication.MainframeBankingSystemFacade, "Mainframe Banking System Facade", $techn="Spring Bean", $descr="A facade onto the mainframe banking system.", $tags="Component", $link="") + Component(InternetBankingSystem.APIApplication.EmailComponent, "E-mail Component", $techn="Spring Bean", $descr="Sends e-mails to users.", $tags="Component", $link="") + } + + Rel(InternetBankingSystem.SinglePageApplication, InternetBankingSystem.APIApplication.SignInController, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") + Rel(InternetBankingSystem.SinglePageApplication, InternetBankingSystem.APIApplication.AccountsSummaryController, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") + Rel(InternetBankingSystem.SinglePageApplication, InternetBankingSystem.APIApplication.ResetPasswordController, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") + Rel(InternetBankingSystem.MobileApp, InternetBankingSystem.APIApplication.SignInController, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") + Rel(InternetBankingSystem.MobileApp, InternetBankingSystem.APIApplication.AccountsSummaryController, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") + Rel(InternetBankingSystem.MobileApp, InternetBankingSystem.APIApplication.ResetPasswordController, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") + Rel(InternetBankingSystem.APIApplication.SignInController, InternetBankingSystem.APIApplication.SecurityComponent, "Uses", $techn="", $tags="Relationship", $link="") + Rel(InternetBankingSystem.APIApplication.AccountsSummaryController, InternetBankingSystem.APIApplication.MainframeBankingSystemFacade, "Uses", $techn="", $tags="Relationship", $link="") + Rel(InternetBankingSystem.APIApplication.ResetPasswordController, InternetBankingSystem.APIApplication.SecurityComponent, "Uses", $techn="", $tags="Relationship", $link="") + Rel(InternetBankingSystem.APIApplication.ResetPasswordController, InternetBankingSystem.APIApplication.EmailComponent, "Uses", $techn="", $tags="Relationship", $link="") + Rel(InternetBankingSystem.APIApplication.SecurityComponent, InternetBankingSystem.Database, "Reads from and writes to", $techn="SQL/TCP", $tags="Relationship", $link="") + Rel(InternetBankingSystem.APIApplication.MainframeBankingSystemFacade, MainframeBankingSystem, "Makes API calls to", $techn="XML/HTTPS", $tags="Relationship", $link="") + Rel(InternetBankingSystem.APIApplication.EmailComponent, EmailSystem, "Sends e-mail using", $techn="", $tags="Relationship", $link="") + + SHOW_LEGEND(true) + hide stereotypes + @enduml""", diagram.getDefinition()); diagram = diagrams.stream().filter(d -> d.getKey().equals("SignIn")).findFirst().get(); - expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn.puml")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + @startuml + title API Application - Dynamic\\nSummarises how the sign in feature works in the single-page application. + + set separator none + top to bottom direction + + + + !include + !include + !include + !include + + AddElementTag("Component", $bgColor="#85bbf0", $borderColor="#5d82a8", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="solid") + AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") + AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") + + AddRelTag("Relationship", $textColor="#444444", $lineColor="#444444", $lineStyle = DashedLine()) + + + AddBoundaryTag("Container", $bgColor="#ffffff", $borderColor="#2e6295", $fontColor="#2e6295", $shadowing="", $borderStyle="solid") + Container_Boundary("InternetBankingSystem.APIApplication_boundary", "API Application", $tags="Container") { + Component(InternetBankingSystem.APIApplication.SignInController, "Sign In Controller", $techn="Spring MVC Rest Controller", $descr="Allows users to sign in to the Internet Banking System.", $tags="Component", $link="") + Component(InternetBankingSystem.APIApplication.SecurityComponent, "Security Component", $techn="Spring Bean", $descr="Provides functionality related to signing in, changing passwords, etc.", $tags="Component", $link="") + } + + Container(InternetBankingSystem.SinglePageApplication, "Single-Page Application", $techn="JavaScript and Angular", $descr="Provides all of the Internet banking functionality to customers via their web browser.", $tags="Container,Web Browser", $link="") + ContainerDb(InternetBankingSystem.Database, "Database", $techn="Oracle Database Schema", $descr="Stores user registration information, hashed authentication credentials, access logs, etc.", $tags="Container,Database", $link="") + + Rel(InternetBankingSystem.SinglePageApplication, InternetBankingSystem.APIApplication.SignInController, "1. Submits credentials to", $techn="JSON/HTTPS", $tags="Relationship", $link="") + Rel(InternetBankingSystem.APIApplication.SignInController, InternetBankingSystem.APIApplication.SecurityComponent, "2. Validates credentials using", $techn="", $tags="Relationship", $link="") + Rel(InternetBankingSystem.APIApplication.SecurityComponent, InternetBankingSystem.Database, "3. select * from users where username = ?", $techn="SQL/TCP", $tags="Relationship", $link="") + Rel(InternetBankingSystem.Database, InternetBankingSystem.APIApplication.SecurityComponent, "4. Returns user data to", $techn="SQL/TCP", $tags="Relationship", $link="") + Rel(InternetBankingSystem.APIApplication.SecurityComponent, InternetBankingSystem.APIApplication.SignInController, "5. Returns true if the hashed password matches", $techn="", $tags="Relationship", $link="") + Rel(InternetBankingSystem.APIApplication.SignInController, InternetBankingSystem.SinglePageApplication, "6. Sends back an authentication token to", $techn="JSON/HTTPS", $tags="Relationship", $link="") + + SHOW_LEGEND(true) + hide stereotypes + @enduml""", diagram.getDefinition()); diagram = diagrams.stream().filter(md -> md.getKey().equals("DevelopmentDeployment")).findFirst().get(); - expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-DevelopmentDeployment.puml")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + @startuml + title Internet Banking System - Deployment - Development\\nAn example development deployment scenario for the Internet Banking System. + + set separator none + top to bottom direction + + + + !include + !include + !include + !include + + AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") + AddElementTag("Element", $bgColor="#ffffff", $borderColor="#444444", $fontColor="#444444", $sprite="", $shadowing="", $borderStyle="solid") + AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") + AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") + AddElementTag("Container", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") + + AddRelTag("Relationship", $textColor="#444444", $lineColor="#444444", $lineStyle = DashedLine()) + + Deployment_Node(Development.DeveloperLaptop, "Developer Laptop", $type="Microsoft Windows 10 or Apple macOS", $descr="", $tags="Element", $link="") { + Deployment_Node(Development.DeveloperLaptop.WebBrowser, "Web Browser", $type="Chrome, Firefox, Safari, or Edge", $descr="", $tags="Element", $link="") { + Container(Development.DeveloperLaptop.WebBrowser.SinglePageApplication_1, "Single-Page Application", $techn="JavaScript and Angular", $descr="Provides all of the Internet banking functionality to customers via their web browser.", $tags="Container,Web Browser", $link="") + } + + Deployment_Node(Development.DeveloperLaptop.DockerContainerWebServer, "Docker Container - Web Server", $type="Docker", $descr="", $tags="Element", $link="") { + Deployment_Node(Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat, "Apache Tomcat", $type="Apache Tomcat 8.x", $descr="", $tags="Element", $link="") { + Container(Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.WebApplication_1, "Web Application", $techn="Java and Spring MVC", $descr="Delivers the static content and the Internet banking single page application.", $tags="Container", $link="") + Container(Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.APIApplication_1, "API Application", $techn="Java and Spring MVC", $descr="Provides Internet banking functionality via a JSON/HTTPS API.", $tags="Container", $link="") + } + + } + + Deployment_Node(Development.DeveloperLaptop.DockerContainerDatabaseServer, "Docker Container - Database Server", $type="Docker", $descr="", $tags="Element", $link="") { + Deployment_Node(Development.DeveloperLaptop.DockerContainerDatabaseServer.DatabaseServer, "Database Server", $type="Oracle 12c", $descr="", $tags="Element", $link="") { + ContainerDb(Development.DeveloperLaptop.DockerContainerDatabaseServer.DatabaseServer.Database_1, "Database", $techn="Oracle Database Schema", $descr="Stores user registration information, hashed authentication credentials, access logs, etc.", $tags="Container,Database", $link="") + } + + } + + } + + Deployment_Node(Development.BigBankplc, "Big Bank plc", $type="Big Bank plc data center", $descr="", $tags="Element", $link="") { + Deployment_Node(Development.BigBankplc.bigbankdev001, "bigbank-dev001", $type="", $descr="", $tags="Element", $link="") { + System(Development.BigBankplc.bigbankdev001.MainframeBankingSystem_1, "Mainframe Banking System", $descr="Stores all of the core banking information about customers, accounts, transactions, etc.", $tags="Software System,Existing System", $link="") + } + + } + + Rel(Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.WebApplication_1, Development.DeveloperLaptop.WebBrowser.SinglePageApplication_1, "Delivers to the customer's web browser", $techn="", $tags="Relationship", $link="") + Rel(Development.DeveloperLaptop.WebBrowser.SinglePageApplication_1, Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.APIApplication_1, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") + Rel(Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.APIApplication_1, Development.DeveloperLaptop.DockerContainerDatabaseServer.DatabaseServer.Database_1, "Reads from and writes to", $techn="SQL/TCP", $tags="Relationship", $link="") + Rel(Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.APIApplication_1, Development.BigBankplc.bigbankdev001.MainframeBankingSystem_1, "Makes API calls to", $techn="XML/HTTPS", $tags="Relationship", $link="") + + SHOW_LEGEND(true) + hide stereotypes + @enduml""", diagram.getDefinition()); diagram = diagrams.stream().filter(md -> md.getKey().equals("LiveDeployment")).findFirst().get(); - expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-LiveDeployment.puml")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + @startuml + title Internet Banking System - Deployment - Live\\nAn example live deployment scenario for the Internet Banking System. + + set separator none + top to bottom direction + + + + !include + !include + !include + !include + + AddElementTag("Failover", $bgColor="#ffffff", $borderColor="#444444", $fontColor="#444444", $sprite="", $shadowing="", $borderStyle="solid") + AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") + AddElementTag("Element", $bgColor="#ffffff", $borderColor="#444444", $fontColor="#444444", $sprite="", $shadowing="", $borderStyle="solid") + AddElementTag("Container,Mobile App", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") + AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") + AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") + AddElementTag("Container", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") + + AddRelTag("Relationship", $textColor="#444444", $lineColor="#444444", $lineStyle = DashedLine()) + + Deployment_Node(Live.Customersmobiledevice, "Customer's mobile device", $type="Apple iOS or Android", $descr="", $tags="Element", $link="") { + Container(Live.Customersmobiledevice.MobileApp_1, "Mobile App", $techn="Xamarin", $descr="Provides a limited subset of the Internet banking functionality to customers via their mobile device.", $tags="Container,Mobile App", $link="") + } + + Deployment_Node(Live.Customerscomputer, "Customer's computer", $type="Microsoft Windows or Apple macOS", $descr="", $tags="Element", $link="") { + Deployment_Node(Live.Customerscomputer.WebBrowser, "Web Browser", $type="Chrome, Firefox, Safari, or Edge", $descr="", $tags="Element", $link="") { + Container(Live.Customerscomputer.WebBrowser.SinglePageApplication_1, "Single-Page Application", $techn="JavaScript and Angular", $descr="Provides all of the Internet banking functionality to customers via their web browser.", $tags="Container,Web Browser", $link="") + } + + } + + Deployment_Node(Live.BigBankplc, "Big Bank plc", $type="Big Bank plc data center", $descr="", $tags="Element", $link="") { + Deployment_Node(Live.BigBankplc.bigbankweb, "bigbank-web*** (x4)", $type="Ubuntu 16.04 LTS", $descr="", $tags="Element", $link="") { + Deployment_Node(Live.BigBankplc.bigbankweb.ApacheTomcat, "Apache Tomcat", $type="Apache Tomcat 8.x", $descr="", $tags="Element", $link="") { + Container(Live.BigBankplc.bigbankweb.ApacheTomcat.WebApplication_1, "Web Application", $techn="Java and Spring MVC", $descr="Delivers the static content and the Internet banking single page application.", $tags="Container", $link="") + } + + } + + Deployment_Node(Live.BigBankplc.bigbankapi, "bigbank-api*** (x8)", $type="Ubuntu 16.04 LTS", $descr="", $tags="Element", $link="") { + Deployment_Node(Live.BigBankplc.bigbankapi.ApacheTomcat, "Apache Tomcat", $type="Apache Tomcat 8.x", $descr="", $tags="Element", $link="") { + Container(Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1, "API Application", $techn="Java and Spring MVC", $descr="Provides Internet banking functionality via a JSON/HTTPS API.", $tags="Container", $link="") + } + + } + + Deployment_Node(Live.BigBankplc.bigbankdb01, "bigbank-db01", $type="Ubuntu 16.04 LTS", $descr="", $tags="Element", $link="") { + Deployment_Node(Live.BigBankplc.bigbankdb01.OraclePrimary, "Oracle - Primary", $type="Oracle 12c", $descr="", $tags="Element", $link="") { + ContainerDb(Live.BigBankplc.bigbankdb01.OraclePrimary.Database_1, "Database", $techn="Oracle Database Schema", $descr="Stores user registration information, hashed authentication credentials, access logs, etc.", $tags="Container,Database", $link="") + } + + } + + Deployment_Node(Live.BigBankplc.bigbankdb02, "bigbank-db02", $type="Ubuntu 16.04 LTS", $descr="", $tags="Failover", $link="") { + Deployment_Node(Live.BigBankplc.bigbankdb02.OracleSecondary, "Oracle - Secondary", $type="Oracle 12c", $descr="", $tags="Failover", $link="") { + ContainerDb(Live.BigBankplc.bigbankdb02.OracleSecondary.Database_1, "Database", $techn="Oracle Database Schema", $descr="Stores user registration information, hashed authentication credentials, access logs, etc.", $tags="Container,Database", $link="") + } + + } + + Deployment_Node(Live.BigBankplc.bigbankprod001, "bigbank-prod001", $type="", $descr="", $tags="Element", $link="") { + System(Live.BigBankplc.bigbankprod001.MainframeBankingSystem_1, "Mainframe Banking System", $descr="Stores all of the core banking information about customers, accounts, transactions, etc.", $tags="Software System,Existing System", $link="") + } + + } + + Rel(Live.BigBankplc.bigbankweb.ApacheTomcat.WebApplication_1, Live.Customerscomputer.WebBrowser.SinglePageApplication_1, "Delivers to the customer's web browser", $techn="", $tags="Relationship", $link="") + Rel(Live.Customersmobiledevice.MobileApp_1, Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") + Rel(Live.Customerscomputer.WebBrowser.SinglePageApplication_1, Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") + Rel(Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1, Live.BigBankplc.bigbankdb01.OraclePrimary.Database_1, "Reads from and writes to", $techn="SQL/TCP", $tags="Relationship", $link="") + Rel(Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1, Live.BigBankplc.bigbankdb02.OracleSecondary.Database_1, "Reads from and writes to", $techn="SQL/TCP", $tags="Relationship", $link="") + Rel(Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1, Live.BigBankplc.bigbankprod001.MainframeBankingSystem_1, "Makes API calls to", $techn="XML/HTTPS", $tags="Relationship", $link="") + Rel(Live.BigBankplc.bigbankdb01.OraclePrimary, Live.BigBankplc.bigbankdb02.OracleSecondary, "Replicates data to", $techn="", $tags="Relationship", $link="") + + SHOW_LEGEND(true) + hide stereotypes + @enduml""", diagram.getDefinition()); // and the sequence diagram version workspace.getViews().getConfiguration().addProperty(exporter.PLANTUML_SEQUENCE_DIAGRAM_PROPERTY, "true"); diagrams = exporter.export(workspace); diagram = diagrams.stream().filter(d -> d.getKey().equals("SignIn")).findFirst().get(); - expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn-sequence.puml")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + @startuml + title API Application - Dynamic\\nSummarises how the sign in feature works in the single-page application. + + set separator none + + + + !include + + AddElementTag("Component", $bgColor="#85bbf0", $borderColor="#5d82a8", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="solid") + AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") + AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") + + AddRelTag("Relationship", $textColor="#444444", $lineColor="#444444", $lineStyle = DashedLine()) + + Container(InternetBankingSystem.SinglePageApplication, "Single-Page Application", $techn="JavaScript and Angular", $descr="Provides all of the Internet banking functionality to customers via their web browser.", $tags="Container,Web Browser", $link="") + Component(InternetBankingSystem.APIApplication.SignInController, "Sign In Controller", $techn="Spring MVC Rest Controller", $descr="Allows users to sign in to the Internet Banking System.", $tags="Component", $link="") + Component(InternetBankingSystem.APIApplication.SecurityComponent, "Security Component", $techn="Spring Bean", $descr="Provides functionality related to signing in, changing passwords, etc.", $tags="Component", $link="") + ContainerDb(InternetBankingSystem.Database, "Database", $techn="Oracle Database Schema", $descr="Stores user registration information, hashed authentication credentials, access logs, etc.", $tags="Container,Database", $link="") + + Rel(InternetBankingSystem.SinglePageApplication, InternetBankingSystem.APIApplication.SignInController, "Submits credentials to", $techn="JSON/HTTPS", $tags="Relationship", $link="") + Rel(InternetBankingSystem.APIApplication.SignInController, InternetBankingSystem.APIApplication.SecurityComponent, "Validates credentials using", $techn="", $tags="Relationship", $link="") + Rel(InternetBankingSystem.APIApplication.SecurityComponent, InternetBankingSystem.Database, "select * from users where username = ?", $techn="SQL/TCP", $tags="Relationship", $link="") + Rel(InternetBankingSystem.Database, InternetBankingSystem.APIApplication.SecurityComponent, "Returns user data to", $techn="SQL/TCP", $tags="Relationship", $link="") + Rel(InternetBankingSystem.APIApplication.SecurityComponent, InternetBankingSystem.APIApplication.SignInController, "Returns true if the hashed password matches", $techn="", $tags="Relationship", $link="") + Rel(InternetBankingSystem.APIApplication.SignInController, InternetBankingSystem.SinglePageApplication, "Sends back an authentication token to", $techn="JSON/HTTPS", $tags="Relationship", $link="") + + SHOW_LEGEND(true) + hide stereotypes + @enduml""", diagram.getDefinition()); } @Test @Tag("IntegrationTest") public void test_AmazonWebServicesExampleWithoutTags() throws Exception { - Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/structurizr-54915-workspace.json")); + Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/amazon-web-services.json")); ThemeUtils.loadThemes(workspace); workspace.getViews().getDeploymentViews().iterator().next().enableAutomaticLayout(AutomaticLayout.RankDirection.LeftRight, 300, 300); workspace.getViews().getViews().forEach(v -> v.addProperty(C4PlantUMLExporter.C4PLANTUML_TAGS_PROPERTY, "false")); @@ -74,14 +495,60 @@ public void test_AmazonWebServicesExampleWithoutTags() throws Exception { assertEquals(1, diagrams.size()); Diagram diagram = diagrams.stream().findFirst().get(); - String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/c4plantuml/54915-AmazonWebServicesDeployment-WithoutTags.puml")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + @startuml + title X - Deployment - Live + + set separator none + left to right direction + + + + !include + !include + !include + !include + + Deployment_Node(Live.AmazonWebServices, "Amazon Web Services", $type="", $descr="", $tags="", $link="") { + Deployment_Node(Live.AmazonWebServices.USEast1, "US-East-1", $type="", $descr="", $tags="", $link="") { + Deployment_Node(Live.AmazonWebServices.USEast1.Autoscalinggroup, "Autoscaling group", $type="", $descr="", $tags="", $link="") { + Deployment_Node(Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2Ubuntuserver, "Amazon EC2 - Ubuntu server", $type="", $descr="", $tags="", $link="") { + Container(Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2Ubuntuserver.WebApplication_1, "Web Application", $techn="Java and Spring Boot", $descr="", $tags="", $link="") + } + + } + + Deployment_Node(Live.AmazonWebServices.USEast1.AmazonRDS, "Amazon RDS", $type="", $descr="", $tags="", $link="") { + Deployment_Node(Live.AmazonWebServices.USEast1.AmazonRDS.MySQL, "MySQL", $type="", $descr="", $tags="", $link="") { + ContainerDb(Live.AmazonWebServices.USEast1.AmazonRDS.MySQL.DatabaseSchema_1, "Database Schema", $techn="", $descr="", $tags="", $link="") + } + + } + + Deployment_Node(Live.AmazonWebServices.USEast1.DNSrouter, "DNS router", $type="Route 53", $descr="Routes incoming requests based upon domain name.", $tags="", $link="") + Deployment_Node(Live.AmazonWebServices.USEast1.LoadBalancer, "Load Balancer", $type="Elastic Load Balancer", $descr="Automatically distributes incoming application traffic.", $tags="", $link="") + } + + } + + Rel(Live.AmazonWebServices.USEast1.LoadBalancer, Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2Ubuntuserver.WebApplication_1, "Forwards requests to", $techn="HTTPS", $tags="", $link="") + Rel(Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2Ubuntuserver.WebApplication_1, Live.AmazonWebServices.USEast1.AmazonRDS.MySQL.DatabaseSchema_1, "Reads from and writes to", $techn="MySQL Protocol/SSL", $tags="", $link="") + Rel(Live.AmazonWebServices.USEast1.DNSrouter, Live.AmazonWebServices.USEast1.LoadBalancer, "Forwards requests to", $techn="HTTPS", $tags="", $link="") + + SHOW_LEGEND(true) + hide stereotypes + @enduml""", diagram.getDefinition()); } @Test @Tag("IntegrationTest") public void test_AmazonWebServicesExampleWithTags() throws Exception { - Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/structurizr-54915-workspace.json")); + Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/amazon-web-services.json")); ThemeUtils.loadThemes(workspace); workspace.getViews().getDeploymentViews().iterator().next().enableAutomaticLayout(AutomaticLayout.RankDirection.LeftRight, 300, 300); workspace.getViews().getConfiguration().addProperty(C4PlantUMLExporter.C4PLANTUML_TAGS_PROPERTY, "true"); @@ -91,30 +558,193 @@ public void test_AmazonWebServicesExampleWithTags() throws Exception { assertEquals(1, diagrams.size()); Diagram diagram = diagrams.stream().findFirst().get(); - String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/c4plantuml/54915-AmazonWebServicesDeployment-WithTags.puml")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + @startuml + title X - Deployment - Live + + set separator none + left to right direction + + + + !include + !include + !include + !include + + AddElementTag("Amazon Web Services - RDS", $bgColor="#ffffff", $borderColor="#3b48cc", $fontColor="#3b48cc", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/amazon-rds.png{scale=0.1}", $shadowing="", $borderStyle="solid") + AddElementTag("Amazon Web Services - Auto Scaling", $bgColor="#ffffff", $borderColor="#cc2264", $fontColor="#cc2264", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/aws-auto-scaling.png{scale=0.1}", $shadowing="", $borderStyle="solid") + AddElementTag("Amazon Web Services - Route 53", $bgColor="#ffffff", $borderColor="#693cc5", $fontColor="#693cc5", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/amazon-route-53.png{scale=0.1}", $shadowing="", $borderStyle="solid") + AddElementTag("Amazon Web Services - EC2", $bgColor="#ffffff", $borderColor="#d86613", $fontColor="#d86613", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/amazon-ec2.png{scale=0.1}", $shadowing="", $borderStyle="solid") + AddElementTag("Amazon Web Services - Region", $bgColor="#ffffff", $borderColor="#147eba", $fontColor="#147eba", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/region.png{scale=0.21428571428571427}", $shadowing="", $borderStyle="solid") + AddElementTag("Amazon Web Services - Elastic Load Balancing", $bgColor="#ffffff", $borderColor="#693cc5", $fontColor="#693cc5", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/elastic-load-balancing.png{scale=0.1}", $shadowing="", $borderStyle="solid") + AddElementTag("Application", $bgColor="#ffffff", $borderColor="#444444", $fontColor="#444444", $sprite="", $shadowing="", $borderStyle="solid") + AddElementTag("Amazon Web Services - RDS MySQL instance", $bgColor="#ffffff", $borderColor="#3b48cc", $fontColor="#3b48cc", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/amazon-rds-mysql-instance.png{scale=0.15}", $shadowing="", $borderStyle="solid") + AddElementTag("Amazon Web Services - Cloud", $bgColor="#ffffff", $borderColor="#232f3e", $fontColor="#232f3e", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/aws-cloud.png{scale=0.21428571428571427}", $shadowing="", $borderStyle="solid") + AddElementTag("Database", $bgColor="#ffffff", $borderColor="#444444", $fontColor="#444444", $sprite="", $shadowing="", $borderStyle="solid") + + AddRelTag("Relationship", $textColor="#444444", $lineColor="#444444", $lineStyle = DashedLine()) + + Deployment_Node(Live.AmazonWebServices, "Amazon Web Services", $type="", $descr="", $tags="Amazon Web Services - Cloud", $link="") { + Deployment_Node(Live.AmazonWebServices.USEast1, "US-East-1", $type="", $descr="", $tags="Amazon Web Services - Region", $link="") { + Deployment_Node(Live.AmazonWebServices.USEast1.Autoscalinggroup, "Autoscaling group", $type="", $descr="", $tags="Amazon Web Services - Auto Scaling", $link="") { + Deployment_Node(Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2Ubuntuserver, "Amazon EC2 - Ubuntu server", $type="", $descr="", $tags="Amazon Web Services - EC2", $link="") { + Container(Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2Ubuntuserver.WebApplication_1, "Web Application", $techn="Java and Spring Boot", $descr="", $tags="Application", $link="") + } + + } + + Deployment_Node(Live.AmazonWebServices.USEast1.AmazonRDS, "Amazon RDS", $type="", $descr="", $tags="Amazon Web Services - RDS", $link="") { + Deployment_Node(Live.AmazonWebServices.USEast1.AmazonRDS.MySQL, "MySQL", $type="", $descr="", $tags="Amazon Web Services - RDS MySQL instance", $link="") { + ContainerDb(Live.AmazonWebServices.USEast1.AmazonRDS.MySQL.DatabaseSchema_1, "Database Schema", $techn="", $descr="", $tags="Database", $link="") + } + + } + + Deployment_Node(Live.AmazonWebServices.USEast1.DNSrouter, "DNS router", $type="Route 53", $descr="Routes incoming requests based upon domain name.", $tags="Amazon Web Services - Route 53", $link="") + Deployment_Node(Live.AmazonWebServices.USEast1.LoadBalancer, "Load Balancer", $type="Elastic Load Balancer", $descr="Automatically distributes incoming application traffic.", $tags="Amazon Web Services - Elastic Load Balancing", $link="") + } + + } + + Rel(Live.AmazonWebServices.USEast1.LoadBalancer, Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2Ubuntuserver.WebApplication_1, "Forwards requests to", $techn="HTTPS", $tags="Relationship", $link="") + Rel(Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2Ubuntuserver.WebApplication_1, Live.AmazonWebServices.USEast1.AmazonRDS.MySQL.DatabaseSchema_1, "Reads from and writes to", $techn="MySQL Protocol/SSL", $tags="Relationship", $link="") + Rel(Live.AmazonWebServices.USEast1.DNSrouter, Live.AmazonWebServices.USEast1.LoadBalancer, "Forwards requests to", $techn="HTTPS", $tags="Relationship", $link="") + + SHOW_LEGEND(true) + hide stereotypes + @enduml""", diagram.getDefinition()); } @Test public void test_GroupsExample() throws Exception { Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/groups.json")); - ThemeUtils.loadThemes(workspace); C4PlantUMLExporter exporter = new C4PlantUMLExporter(); Collection diagrams = exporter.export(workspace); assertEquals(3, diagrams.size()); Diagram diagram = diagrams.stream().filter(md -> md.getKey().equals("SystemLandscape")).findFirst().get(); - String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-SystemLandscape.puml")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + @startuml + title System Landscape + + set separator none + top to bottom direction + + + + !include + !include + + AddBoundaryTag("Group 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") + Boundary(group_1, "Group 1", $tags="Group 1") { + System(B, "B", $descr="", $tags="", $link="") + } + + AddBoundaryTag("Group 2", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") + Boundary(group_2, "Group 2", $tags="Group 2") { + System(C, "C", $descr="", $tags="", $link="") + AddBoundaryTag("Group 2/Group 3", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") + Boundary(group_3, "Group 3", $tags="Group 2/Group 3") { + System(D, "D", $descr="", $tags="", $link="") + } + + } + + System(A, "A", $descr="", $tags="", $link="") + + Rel(B, C, "", $techn="", $tags="", $link="") + Rel(C, D, "", $techn="", $tags="", $link="") + Rel(A, B, "", $techn="", $tags="", $link="") + + SHOW_LEGEND(true) + hide stereotypes + @enduml""", diagram.getDefinition()); diagram = diagrams.stream().filter(md -> md.getKey().equals("Containers")).findFirst().get(); - expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Containers.puml")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + @startuml + title D - Containers + + set separator none + top to bottom direction + + + + !include + !include + !include + + System(C, "C", $descr="", $tags="", $link="") + + System_Boundary("D_boundary", "D", $tags="") { + AddBoundaryTag("Group 4", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") + Boundary(group_1, "Group 4", $tags="Group 4") { + Container(D.F, "F", $techn="", $descr="", $tags="", $link="") + } + + Container(D.E, "E", $techn="", $descr="", $tags="", $link="") + } + + Rel(C, D.E, "", $techn="", $tags="", $link="") + Rel(C, D.F, "", $techn="", $tags="", $link="") + + SHOW_LEGEND(true) + hide stereotypes + @enduml""", diagram.getDefinition()); diagram = diagrams.stream().filter(md -> md.getKey().equals("Components")).findFirst().get(); - expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Components.puml")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + @startuml + title D - F - Components + + set separator none + top to bottom direction + + + + !include + !include + !include + + System(C, "C", $descr="", $tags="", $link="") + + Container_Boundary("D.F_boundary", "F", $tags="") { + AddBoundaryTag("Group 5", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") + Boundary(group_1, "Group 5", $tags="Group 5") { + Component(D.F.H, "H", $techn="", $descr="", $tags="", $link="") + } + + Component(D.F.G, "G", $techn="", $descr="", $tags="", $link="") + } + + Rel(C, D.F.G, "", $techn="", $tags="", $link="") + Rel(C, D.F.H, "", $techn="", $tags="", $link="") + + SHOW_LEGEND(true) + hide stereotypes + @enduml""", diagram.getDefinition()); } @Test @@ -144,8 +774,52 @@ public void test_NestedGroupsExample() throws Exception { Collection diagrams = exporter.export(workspace); Diagram diagram = diagrams.stream().filter(md -> md.getKey().equals("SystemLandscape")).findFirst().get(); - String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/c4plantuml/nested-groups.puml")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + @startuml + title System Landscape\\nDescription + + set separator none + top to bottom direction + + + + !include + !include + + AddBoundaryTag("Organisation 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") + Boundary(group_1, "Organisation 1", $tags="Organisation 1") { + System(Organisation1, "Organisation 1", $descr="", $tags="", $link="") + AddBoundaryTag("Organisation 1/Department 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") + Boundary(group_2, "Department 1", $tags="Organisation 1/Department 1") { + System(Department1, "Department 1", $descr="", $tags="", $link="") + AddBoundaryTag("Organisation 1/Department 1/Team 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") + Boundary(group_3, "Team 1", $tags="Organisation 1/Department 1/Team 1") { + System(Team1, "Team 1", $descr="", $tags="", $link="") + } + + AddBoundaryTag("Organisation 1/Department 1/Team 2", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") + Boundary(group_4, "Team 2", $tags="Organisation 1/Department 1/Team 2") { + System(Team2, "Team 2", $descr="", $tags="", $link="") + } + + } + + } + + AddBoundaryTag("Organisation 2", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") + Boundary(group_5, "Organisation 2", $tags="Organisation 2") { + System(Organisation2, "Organisation 2", $descr="", $tags="", $link="") + } + + + SHOW_LEGEND(true) + hide stereotypes + @enduml""", diagram.getDefinition()); } @Test @@ -163,8 +837,40 @@ public void test_NestedGroupsExample_WithDotAsGroupSeparator() throws Exception Collection diagrams = exporter.export(workspace); Diagram diagram = diagrams.stream().filter(md -> md.getKey().equals("SystemLandscape")).findFirst().get(); - String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/c4plantuml/nested-groups-with-dot-separator.puml")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + @startuml + title System Landscape\\nDescription + + set separator none + top to bottom direction + + + + !include + !include + + AddBoundaryTag("Organisation 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") + Boundary(group_1, "Organisation 1", $tags="Organisation 1") { + AddBoundaryTag("Organisation 1.Department 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") + Boundary(group_2, "Department 1", $tags="Organisation 1.Department 1") { + AddBoundaryTag("Organisation 1.Department 1.Team 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") + Boundary(group_3, "Team 1", $tags="Organisation 1.Department 1.Team 1") { + System(Team1, "Team 1", $descr="", $tags="", $link="") + } + + } + + } + + + SHOW_LEGEND(true) + hide stereotypes + @enduml""", diagram.getDefinition()); } @Test @@ -182,20 +888,88 @@ public void test_renderGroupStyles() throws Exception { C4PlantUMLExporter exporter = new C4PlantUMLExporter() { @Override - protected double calculateIconScale(String iconUrl) { + protected double calculateIconScale(String iconUrl, int maxIconSize) { return 1.0; } }; Diagram diagram = exporter.export(view); - String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/c4plantuml/group-styles-1.puml")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + @startuml + title System Landscape + + set separator none + top to bottom direction + + + + !include + !include + + AddBoundaryTag("Group 1", $borderColor="#111111", $fontColor="#111111", $borderStyle="dashed") + Boundary(group_1, "Group 1", $tags="Group 1") { + Person(User1, "User 1", $descr="", $tags="", $link="") + } + + AddBoundaryTag("Group 2", $borderColor="#222222", $fontColor="#222222", $borderStyle="dashed") + Boundary(group_2, "Group 2", $tags="Group 2") { + Person(User2, "User 2", $descr="", $tags="", $link="") + } + + AddBoundaryTag("Group 3", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") + Boundary(group_3, "Group 3", $tags="Group 3") { + Person(User3, "User 3", $descr="", $tags="", $link="") + } + + + SHOW_LEGEND(true) + hide stereotypes + @enduml""", diagram.getDefinition()); workspace.getViews().getConfiguration().getStyles().addElementStyle("Group").color("#aabbcc"); diagram = exporter.export(view); - expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/c4plantuml/group-styles-2.puml")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + @startuml + title System Landscape + + set separator none + top to bottom direction + + + + !include + !include + + AddBoundaryTag("Group 1", $borderColor="#111111", $fontColor="#111111", $borderStyle="dashed") + Boundary(group_1, "Group 1", $tags="Group 1") { + Person(User1, "User 1", $descr="", $tags="", $link="") + } + + AddBoundaryTag("Group 2", $borderColor="#222222", $fontColor="#222222", $borderStyle="dashed") + Boundary(group_2, "Group 2", $tags="Group 2") { + Person(User2, "User 2", $descr="", $tags="", $link="") + } + + AddBoundaryTag("Group 3", $borderColor="#aabbcc", $fontColor="#aabbcc", $borderStyle="dashed") + Boundary(group_3, "Group 3", $tags="Group 3") { + Person(User3, "User 3", $descr="", $tags="", $link="") + } + + + SHOW_LEGEND(true) + hide stereotypes + @enduml""", diagram.getDefinition()); } @Test @@ -214,28 +988,37 @@ public void test_renderContainerDiagramWithExternalContainers() { containerView.add(container2); Diagram diagram = new C4PlantUMLExporter().export(containerView); - assertEquals("@startuml\n" + - "set separator none\n" + - "title Software System 1 - Containers\n" + - "\n" + - "top to bottom direction\n" + - "\n" + - "!include \n" + - "!include \n" + - "!include \n" + - "\n" + - "System_Boundary(\"SoftwareSystem1_boundary\", \"Software System 1\", $tags=\"\") {\n" + - " Container(SoftwareSystem1.Container1, \"Container 1\", $techn=\"\", $descr=\"\", $tags=\"\", $link=\"\")\n" + - "}\n" + - "\n" + - "System_Boundary(\"SoftwareSystem2_boundary\", \"Software System 2\", $tags=\"\") {\n" + - " Container(SoftwareSystem2.Container2, \"Container 2\", $techn=\"\", $descr=\"\", $tags=\"\", $link=\"\")\n" + - "}\n" + - "\n" + - "Rel(SoftwareSystem1.Container1, SoftwareSystem2.Container2, \"Uses\", $techn=\"\", $tags=\"\", $link=\"\")\n" + - "\n" + - "SHOW_LEGEND(true)\n" + - "@enduml", diagram.getDefinition()); + assertEquals(""" + @startuml + title Software System 1 - Containers + + set separator none + top to bottom direction + + + + !include + !include + !include + + System_Boundary("SoftwareSystem1_boundary", "Software System 1", $tags="") { + Container(SoftwareSystem1.Container1, "Container 1", $techn="", $descr="", $tags="", $link="") + } + + System_Boundary("SoftwareSystem2_boundary", "Software System 2", $tags="") { + Container(SoftwareSystem2.Container2, "Container 2", $techn="", $descr="", $tags="", $link="") + } + + Rel(SoftwareSystem1.Container1, SoftwareSystem2.Container2, "Uses", $techn="", $tags="", $link="") + + SHOW_LEGEND(true) + hide stereotypes + @enduml""", diagram.getDefinition()); } @Test @@ -256,28 +1039,37 @@ public void test_renderComponentDiagramWithExternalComponents() { componentView.add(component2); Diagram diagram = new C4PlantUMLExporter().export(componentView); - assertEquals("@startuml\n" + - "set separator none\n" + - "title Software System 1 - Container 1 - Components\n" + - "\n" + - "top to bottom direction\n" + - "\n" + - "!include \n" + - "!include \n" + - "!include \n" + - "\n" + - "Container_Boundary(\"SoftwareSystem1.Container1_boundary\", \"Container 1\", $tags=\"\") {\n" + - " Component(SoftwareSystem1.Container1.Component1, \"Component 1\", $techn=\"\", $descr=\"\", $tags=\"\", $link=\"\")\n" + - "}\n" + - "\n" + - "Container_Boundary(\"SoftwareSystem2.Container2_boundary\", \"Container 2\", $tags=\"\") {\n" + - " Component(SoftwareSystem2.Container2.Component2, \"Component 2\", $techn=\"\", $descr=\"\", $tags=\"\", $link=\"\")\n" + - "}\n" + - "\n" + - "Rel(SoftwareSystem1.Container1.Component1, SoftwareSystem2.Container2.Component2, \"Uses\", $techn=\"\", $tags=\"\", $link=\"\")\n" + - "\n" + - "SHOW_LEGEND(true)\n" + - "@enduml", diagram.getDefinition()); + assertEquals(""" + @startuml + title Software System 1 - Container 1 - Components + + set separator none + top to bottom direction + + + + !include + !include + !include + + Container_Boundary("SoftwareSystem1.Container1_boundary", "Container 1", $tags="") { + Component(SoftwareSystem1.Container1.Component1, "Component 1", $techn="", $descr="", $tags="", $link="") + } + + Container_Boundary("SoftwareSystem2.Container2_boundary", "Container 2", $tags="") { + Component(SoftwareSystem2.Container2.Component2, "Component 2", $techn="", $descr="", $tags="", $link="") + } + + Rel(SoftwareSystem1.Container1.Component1, SoftwareSystem2.Container2.Component2, "Uses", $techn="", $tags="", $link="") + + SHOW_LEGEND(true) + hide stereotypes + @enduml""", diagram.getDefinition()); } @Test @@ -291,20 +1083,28 @@ public void test_renderDiagramWithElementUrls() { view.addDefaultElements(); Diagram diagram = new C4PlantUMLExporter().export(view); - assertEquals("@startuml\n" + - "set separator none\n" + - "title System Landscape\n" + - "\n" + - "top to bottom direction\n" + - "\n" + - "!include \n" + - "!include \n" + - "\n" + - "System(SoftwareSystem, \"Software System\", $descr=\"\", $tags=\"\", $link=\"https://structurizr.com\")\n" + - "\n" + - "\n" + - "SHOW_LEGEND(true)\n" + - "@enduml", diagram.getDefinition()); + assertEquals(""" + @startuml + title System Landscape\\nDescription + + set separator none + top to bottom direction + + + + !include + !include + + System(SoftwareSystem, "Software System", $descr="", $tags="", $link="https://structurizr.com") + + SHOW_LEGEND(true) + hide stereotypes + @enduml""", diagram.getDefinition()); } @Test @@ -318,21 +1118,30 @@ public void test_renderDiagramWithIncludes() { view.addProperty(C4PlantUMLExporter.PLANTUML_INCLUDES_PROPERTY, "styles.puml"); Diagram diagram = new C4PlantUMLExporter().export(view); - assertEquals("@startuml\n" + - "set separator none\n" + - "title System Landscape\n" + - "\n" + - "top to bottom direction\n" + - "\n" + - "!include \n" + - "!include \n" + - "!include styles.puml\n" + - "\n" + - "System(SoftwareSystem, \"Software System\", $descr=\"\", $tags=\"\", $link=\"\")\n" + - "\n" + - "\n" + - "SHOW_LEGEND(true)\n" + - "@enduml", diagram.getDefinition()); + assertEquals(""" + @startuml + title System Landscape\\nDescription + + set separator none + top to bottom direction + + + + !include + !include + + !include styles.puml + + System(SoftwareSystem, "Software System", $descr="", $tags="", $link="") + + SHOW_LEGEND(true) + hide stereotypes + @enduml""", diagram.getDefinition()); } @Test @@ -345,20 +1154,28 @@ public void test_renderDiagramWithNewLineCharacterInElementName() { view.addDefaultElements(); Diagram diagram = new C4PlantUMLExporter().export(view); - assertEquals("@startuml\n" + - "set separator none\n" + - "title System Landscape\n" + - "\n" + - "top to bottom direction\n" + - "\n" + - "!include \n" + - "!include \n" + - "\n" + - "System(SoftwareSystem, \"Software\\nSystem\", $descr=\"\", $tags=\"\", $link=\"\")\n" + - "\n" + - "\n" + - "SHOW_LEGEND(true)\n" + - "@enduml", diagram.getDefinition()); + assertEquals(""" + @startuml + title System Landscape\\nDescription + + set separator none + top to bottom direction + + + + !include + !include + + System(SoftwareSystem, "Software\\nSystem", $descr="", $tags="", $link="") + + SHOW_LEGEND(true) + hide stereotypes + @enduml""", diagram.getDefinition()); } @Test @@ -371,23 +1188,31 @@ public void test_renderInfrastructureNodeWithTechnology() { view.addDefaultElements(); Diagram diagram = new C4PlantUMLExporter().export(view); - assertEquals("@startuml\n" + - "set separator none\n" + - "title Deployment - Default\n" + - "\n" + - "top to bottom direction\n" + - "\n" + - "!include \n" + - "!include \n" + - "!include \n" + - "\n" + - "Deployment_Node(Default.Deploymentnode, \"Deployment node\", $type=\"\", $descr=\"\", $tags=\"\", $link=\"\") {\n" + - " Deployment_Node(Default.Deploymentnode.Infrastructurenode, \"Infrastructure node\", $type=\"technology\", $descr=\"description\", $tags=\"\", $link=\"\")\n" + - "}\n" + - "\n" + - "\n" + - "SHOW_LEGEND(true)\n" + - "@enduml", diagram.getDefinition()); + assertEquals(""" + @startuml + title Deployment - Default\\nview description + + set separator none + top to bottom direction + + + + !include + !include + !include + + Deployment_Node(Default.Deploymentnode, "Deployment node", $type="", $descr="", $tags="", $link="") { + Deployment_Node(Default.Deploymentnode.Infrastructurenode, "Infrastructure node", $type="technology", $descr="description", $tags="", $link="") + } + + SHOW_LEGEND(true) + hide stereotypes + @enduml""", diagram.getDefinition()); } @Test @@ -412,9 +1237,43 @@ public void test_printProperties() throws Exception { view.addDefaultElements(); Diagram diagram = new C4PlantUMLExporter().export(view); - - String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/c4plantuml/printProperties-containerView.puml")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + @startuml + title SoftwareSystem - Containers + + set separator none + top to bottom direction + + + + !include + !include + !include + + System_Boundary("SoftwareSystem_boundary", "SoftwareSystem", $tags="") { + WithoutPropertyHeader() + AddProperty("IP","127.0.0.1") + AddProperty("Region","East") + Container(SoftwareSystem.Container1, "Container 1", $techn="", $descr="", $tags="", $link="") + WithoutPropertyHeader() + AddProperty("IP","127.0.0.2") + AddProperty("Region","West") + Container(SoftwareSystem.Container2, "Container 2", $techn="", $descr="", $tags="", $link="") + } + + WithoutPropertyHeader() + AddProperty("Prop1","Value1") + AddProperty("Prop2","Value2") + Rel(SoftwareSystem.Container1, SoftwareSystem.Container2, "", $techn="", $tags="", $link="") + + SHOW_LEGEND(true) + hide stereotypes + @enduml""", diagram.getDefinition()); } @Test @@ -432,9 +1291,35 @@ public void test_deploymentViewPrintProperties() throws Exception { deploymentView.addDefaultElements(); Diagram diagram = new C4PlantUMLExporter().export(deploymentView); - - String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/c4plantuml/printProperties-deploymentView.puml")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + @startuml + title Deployment - Default + + set separator none + top to bottom direction + + + + !include + !include + !include + + WithoutPropertyHeader() + AddProperty("Prop1","Value1") + Deployment_Node(Default.Deploymentnode, "Deployment node", $type="", $descr="", $tags="", $link="") { + WithoutPropertyHeader() + AddProperty("Prop2","Value2") + Deployment_Node(Default.Deploymentnode.Infrastructurenode, "Infrastructure node", $type="technology", $descr="description", $tags="", $link="") + } + + SHOW_LEGEND(true) + hide stereotypes + @enduml""", diagram.getDefinition()); } @Test @@ -449,77 +1334,109 @@ public void test_legendAndStereotypes() { view.addProperty(C4PlantUMLExporter.C4PLANTUML_LEGEND_PROPERTY, "true"); view.addProperty(C4PlantUMLExporter.C4PLANTUML_STEREOTYPES_PROPERTY, "false"); Diagram diagram = new C4PlantUMLExporter().export(view); - assertEquals("@startuml\n" + - "set separator none\n" + - "title System Landscape\n" + - "\n" + - "top to bottom direction\n" + - "\n" + - "!include \n" + - "!include \n" + - "\n" + - "System(Name, \"Name\", $descr=\"\", $tags=\"\", $link=\"\")\n" + - "\n" + - "\n" + - "SHOW_LEGEND(true)\n" + - "@enduml", diagram.getDefinition()); + assertEquals(""" + @startuml + title System Landscape\\nDescription + + set separator none + top to bottom direction + + + + !include + !include + + System(Name, "Name", $descr="", $tags="", $link="") + + SHOW_LEGEND(true) + hide stereotypes + @enduml""", diagram.getDefinition()); // legend (true) and stereotypes (true) view.addProperty(C4PlantUMLExporter.C4PLANTUML_LEGEND_PROPERTY, "true"); view.addProperty(C4PlantUMLExporter.C4PLANTUML_STEREOTYPES_PROPERTY, "true"); diagram = new C4PlantUMLExporter().export(view); - assertEquals("@startuml\n" + - "set separator none\n" + - "title System Landscape\n" + - "\n" + - "top to bottom direction\n" + - "\n" + - "!include \n" + - "!include \n" + - "\n" + - "System(Name, \"Name\", $descr=\"\", $tags=\"\", $link=\"\")\n" + - "\n" + - "\n" + - "SHOW_LEGEND(false)\n" + - "@enduml", diagram.getDefinition()); + assertEquals(""" + @startuml + title System Landscape\\nDescription + + set separator none + top to bottom direction + + + + !include + !include + + System(Name, "Name", $descr="", $tags="", $link="") + + SHOW_LEGEND(true) + show stereotypes + @enduml""", diagram.getDefinition()); // legend (false) and stereotypes (false) view.addProperty(C4PlantUMLExporter.C4PLANTUML_LEGEND_PROPERTY, "false"); view.addProperty(C4PlantUMLExporter.C4PLANTUML_STEREOTYPES_PROPERTY, "false"); diagram = new C4PlantUMLExporter().export(view); - assertEquals("@startuml\n" + - "set separator none\n" + - "title System Landscape\n" + - "\n" + - "top to bottom direction\n" + - "\n" + - "!include \n" + - "!include \n" + - "\n" + - "System(Name, \"Name\", $descr=\"\", $tags=\"\", $link=\"\")\n" + - "\n" + - "\n" + - "hide stereotypes\n" + - "@enduml", diagram.getDefinition()); + assertEquals(""" + @startuml + title System Landscape\\nDescription + + set separator none + top to bottom direction + + + + !include + !include + + System(Name, "Name", $descr="", $tags="", $link="") + + SHOW_LEGEND(false) + hide stereotypes + @enduml""", diagram.getDefinition()); // legend (false) and stereotypes (true) view.addProperty(C4PlantUMLExporter.C4PLANTUML_LEGEND_PROPERTY, "false"); view.addProperty(C4PlantUMLExporter.C4PLANTUML_STEREOTYPES_PROPERTY, "true"); diagram = new C4PlantUMLExporter().export(view); - assertEquals("@startuml\n" + - "set separator none\n" + - "title System Landscape\n" + - "\n" + - "top to bottom direction\n" + - "\n" + - "!include \n" + - "!include \n" + - "\n" + - "System(Name, \"Name\", $descr=\"\", $tags=\"\", $link=\"\")\n" + - "\n" + - "\n" + - "show stereotypes\n" + - "@enduml", diagram.getDefinition()); + assertEquals(""" + @startuml + title System Landscape\\nDescription + + set separator none + top to bottom direction + + + + !include + !include + + System(Name, "Name", $descr="", $tags="", $link="") + + SHOW_LEGEND(false) + show stereotypes + @enduml""", diagram.getDefinition()); } @Test @@ -546,26 +1463,34 @@ public void test_renderContainerShapes() throws Exception { workspace.getViews().getConfiguration().getStyles().addElementStyle("Robot").shape(Shape.Robot); Diagram diagram = new C4PlantUMLExporter().export(containerView); - assertEquals("@startuml\n" + - "set separator none\n" + - "title Software System - Containers\n" + - "\n" + - "top to bottom direction\n" + - "\n" + - "!include \n" + - "!include \n" + - "!include \n" + - "\n" + - "System_Boundary(\"SoftwareSystem_boundary\", \"Software System\", $tags=\"\") {\n" + - " Container(SoftwareSystem.DefaultContainer, \"Default Container\", $techn=\"\", $descr=\"\", $tags=\"\", $link=\"\")\n" + - " ContainerDb(SoftwareSystem.CylinderContainer, \"Cylinder Container\", $techn=\"\", $descr=\"\", $tags=\"\", $link=\"\")\n" + - " ContainerQueue(SoftwareSystem.PipeContainer, \"Pipe Container\", $techn=\"\", $descr=\"\", $tags=\"\", $link=\"\")\n" + - " Container(SoftwareSystem.RobotContainer, \"Robot Container\", $techn=\"\", $descr=\"\", $tags=\"\", $link=\"\")\n" + - "}\n" + - "\n" + - "\n" + - "SHOW_LEGEND(true)\n" + - "@enduml", diagram.getDefinition()); + assertEquals(""" + @startuml + title Software System - Containers + + set separator none + top to bottom direction + + + + !include + !include + !include + + System_Boundary("SoftwareSystem_boundary", "Software System", $tags="") { + Container(SoftwareSystem.DefaultContainer, "Default Container", $techn="", $descr="", $tags="", $link="") + ContainerDb(SoftwareSystem.CylinderContainer, "Cylinder Container", $techn="", $descr="", $tags="", $link="") + ContainerQueue(SoftwareSystem.PipeContainer, "Pipe Container", $techn="", $descr="", $tags="", $link="") + Container(SoftwareSystem.RobotContainer, "Robot Container", $techn="", $descr="", $tags="", $link="") + } + + SHOW_LEGEND(true) + hide stereotypes + @enduml""", diagram.getDefinition()); } @Test @@ -595,26 +1520,34 @@ public void test_renderComponentShapes() throws Exception { workspace.getViews().getConfiguration().getStyles().addElementStyle("Robot").shape(Shape.Robot); Diagram diagram = new C4PlantUMLExporter().export(componentView); - assertEquals("@startuml\n" + - "set separator none\n" + - "title Software System - Container - Components\n" + - "\n" + - "top to bottom direction\n" + - "\n" + - "!include \n" + - "!include \n" + - "!include \n" + - "\n" + - "Container_Boundary(\"SoftwareSystem.Container_boundary\", \"Container\", $tags=\"\") {\n" + - " Component(SoftwareSystem.Container.DefaultComponent, \"Default Component\", $techn=\"\", $descr=\"\", $tags=\"\", $link=\"\")\n" + - " ComponentDb(SoftwareSystem.Container.CylinderComponent, \"Cylinder Component\", $techn=\"\", $descr=\"\", $tags=\"\", $link=\"\")\n" + - " ComponentQueue(SoftwareSystem.Container.PipeComponent, \"Pipe Component\", $techn=\"\", $descr=\"\", $tags=\"\", $link=\"\")\n" + - " Component(SoftwareSystem.Container.RobotComponent, \"Robot Component\", $techn=\"\", $descr=\"\", $tags=\"\", $link=\"\")\n" + - "}\n" + - "\n" + - "\n" + - "SHOW_LEGEND(true)\n" + - "@enduml", diagram.getDefinition()); + assertEquals(""" + @startuml + title Software System - Container - Components + + set separator none + top to bottom direction + + + + !include + !include + !include + + Container_Boundary("SoftwareSystem.Container_boundary", "Container", $tags="") { + Component(SoftwareSystem.Container.DefaultComponent, "Default Component", $techn="", $descr="", $tags="", $link="") + ComponentDb(SoftwareSystem.Container.CylinderComponent, "Cylinder Component", $techn="", $descr="", $tags="", $link="") + ComponentQueue(SoftwareSystem.Container.PipeComponent, "Pipe Component", $techn="", $descr="", $tags="", $link="") + Component(SoftwareSystem.Container.RobotComponent, "Robot Component", $techn="", $descr="", $tags="", $link="") + } + + SHOW_LEGEND(true) + hide stereotypes + @enduml""", diagram.getDefinition()); } @Test @@ -626,23 +1559,29 @@ public void testFont() { workspace.getViews().getConfiguration().getBranding().setFont(new Font("Courier")); Diagram diagram = new C4PlantUMLExporter().export(view); - assertEquals("@startuml\n" + - "set separator none\n" + - "title System Landscape\n" + - "\n" + - "skinparam {\n" + - " defaultFontName \"Courier\"\n" + - "}\n" + - "top to bottom direction\n" + - "\n" + - "!include \n" + - "!include \n" + - "\n" + - "Person(User, \"User\", $descr=\"\", $tags=\"\", $link=\"\")\n" + - "\n" + - "\n" + - "SHOW_LEGEND(true)\n" + - "@enduml", diagram.getDefinition().toString()); + assertEquals(""" + @startuml + title System Landscape\\nDescription + + set separator none + top to bottom direction + + + + !include + !include + + Person(User, "User", $descr="", $tags="", $link="") + + SHOW_LEGEND(true) + hide stereotypes + @enduml""", diagram.getDefinition()); } @@ -655,20 +1594,28 @@ public void stdlib_false() { view.addProperty(C4PlantUMLExporter.C4PLANTUML_STANDARD_LIBRARY_PROPERTY, "false"); Diagram diagram = new C4PlantUMLExporter().export(view); - assertEquals("@startuml\n" + - "set separator none\n" + - "title System Landscape\n" + - "\n" + - "top to bottom direction\n" + - "\n" + - "!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4.puml\n" + - "!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Context.puml\n" + - "\n" + - "Person(User, \"User\", $descr=\"\", $tags=\"\", $link=\"\")\n" + - "\n" + - "\n" + - "SHOW_LEGEND(true)\n" + - "@enduml", diagram.getDefinition().toString()); + assertEquals(""" + @startuml + title System Landscape\\nDescription + + set separator none + top to bottom direction + + + + !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4.puml + !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Context.puml + + Person(User, "User", $descr="", $tags="", $link="") + + SHOW_LEGEND(true) + hide stereotypes + @enduml""", diagram.getDefinition().toString()); } @@ -682,23 +1629,31 @@ public void componentWithoutTechnology() { view.addAllElements(); Diagram diagram = new C4PlantUMLExporter().export(view); - assertEquals("@startuml\n" + - "set separator none\n" + - "title Name - Name - Components\n" + - "\n" + - "top to bottom direction\n" + - "\n" + - "!include \n" + - "!include \n" + - "!include \n" + - "\n" + - "Container_Boundary(\"Name.Name_boundary\", \"Name\", $tags=\"\") {\n" + - " Component(Name.Name.Name, \"Name\", $techn=\"\", $descr=\"Description\", $tags=\"\", $link=\"\")\n" + - "}\n" + - "\n" + - "\n" + - "SHOW_LEGEND(true)\n" + - "@enduml", diagram.getDefinition()); + assertEquals(""" + @startuml + title Name - Name - Components\\nDescription + + set separator none + top to bottom direction + + + + !include + !include + !include + + Container_Boundary("Name.Name_boundary", "Name", $tags="") { + Component(Name.Name.Name, "Name", $techn="", $descr="Description", $tags="", $link="") + } + + SHOW_LEGEND(true) + hide stereotypes + @enduml""", diagram.getDefinition()); } @Test @@ -711,22 +1666,30 @@ public void borderStyling() { workspace.getViews().getConfiguration().getStyles().addElementStyle(Tags.ELEMENT).stroke("green").border(Border.Dashed).strokeWidth(2); Diagram diagram = new C4PlantUMLExporter().export(view); - assertEquals("@startuml\n" + - "set separator none\n" + - "title System Landscape\n" + - "\n" + - "top to bottom direction\n" + - "\n" + - "!include \n" + - "!include \n" + - "\n" + - "AddElementTag(\"Element\", $bgColor=\"#dddddd\", $borderColor=\"#008000\", $fontColor=\"#000000\", $sprite=\"\", $shadowing=\"\", $borderStyle=\"dashed\", $borderThickness=\"2\")\n" + - "\n" + - "System(Name, \"Name\", $descr=\"\", $tags=\"Element\", $link=\"\")\n" + - "\n" + - "\n" + - "SHOW_LEGEND(true)\n" + - "@enduml", diagram.getDefinition()); + assertEquals(""" + @startuml + title System Landscape\\nDescription + + set separator none + top to bottom direction + + + + !include + !include + + AddElementTag("Element", $bgColor="#ffffff", $borderColor="#008000", $fontColor="#444444", $sprite="", $shadowing="", $borderStyle="dashed", $borderThickness="2") + + System(Name, "Name", $descr="", $tags="Element", $link="") + + SHOW_LEGEND(true) + hide stereotypes + @enduml""", diagram.getDefinition()); } @Test @@ -740,18 +1703,25 @@ public void elementWithUrl() { Diagram diagram = new C4PlantUMLExporter().export(view); assertEquals(""" @startuml + title System Landscape\\nDescription + set separator none - title System Landscape - top to bottom direction - + + + !include !include - + System(Name, "Name", $descr="", $tags="", $link="https://example.com") - - + SHOW_LEGEND(true) + hide stereotypes @enduml""", diagram.getDefinition()); } diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/PlantUMLBoundaryStyleTests.java b/structurizr-export/src/test/java/com/structurizr/export/plantuml/PlantUMLBoundaryStyleTests.java new file mode 100644 index 000000000..0abedaa56 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/PlantUMLBoundaryStyleTests.java @@ -0,0 +1,29 @@ +package com.structurizr.export.plantuml; + +import com.structurizr.view.Border; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class PlantUMLBoundaryStyleTests { + + @Test + void test() { + PlantUMLBoundaryStyle style = new PlantUMLBoundaryStyle("Name", "#ffffff", "#444444", "#ff0000", 4, Border.Dotted, 24, false); + + assertEquals(""" + // Name + .Boundary-TmFtZQ== { + BackgroundColor: #ffffff; + LineColor: #ff0000; + LineStyle: 4-4; + LineThickness: 4; + FontColor: #444444; + FontSize: 24; + HorizontalAlignment: center; + Shadowing: 0; + } + """, style.toString()); + } + +} diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/PlantUMLElementStyleTests.java b/structurizr-export/src/test/java/com/structurizr/export/plantuml/PlantUMLElementStyleTests.java new file mode 100644 index 000000000..dee150a42 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/PlantUMLElementStyleTests.java @@ -0,0 +1,32 @@ +package com.structurizr.export.plantuml; + +import com.structurizr.view.Border; +import com.structurizr.view.Shape; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class PlantUMLElementStyleTests { + + @Test + void test() { + PlantUMLElementStyle style = new PlantUMLElementStyle("Name", Shape.RoundedBox, 450, "#ffffff", "#444444", "#ff0000", 4, Border.Dotted, 24, "icon", false); + + assertEquals(""" + // Name + .Element-TmFtZQ== { + BackgroundColor: #ffffff; + LineColor: #ff0000; + LineStyle: 4-4; + LineThickness: 4; + RoundCorner: 20; + FontColor: #444444; + FontSize: 24; + HorizontalAlignment: center; + Shadowing: 0; + MaximumWidth: 450; + } + """, style.toString()); + } + +} diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/PlantUMLGroupStyleTests.java b/structurizr-export/src/test/java/com/structurizr/export/plantuml/PlantUMLGroupStyleTests.java new file mode 100644 index 000000000..dd146d0c2 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/PlantUMLGroupStyleTests.java @@ -0,0 +1,31 @@ +package com.structurizr.export.plantuml; + +import com.structurizr.view.Border; +import com.structurizr.view.LineStyle; +import com.structurizr.view.RelationshipStyle; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class PlantUMLGroupStyleTests { + + @Test + void test() { + PlantUMLGroupStyle style = new PlantUMLGroupStyle("Name", "#ffffff", "#444444", "#ff0000", 4, Border.Dotted, 24, false); + + assertEquals(""" + // Name + .Group-TmFtZQ== { + BackgroundColor: #ffffff; + LineColor: #ff0000; + LineStyle: 4-4; + LineThickness: 4; + FontColor: #444444; + FontSize: 24; + HorizontalAlignment: center; + Shadowing: 0; + } + """, style.toString()); + } + +} diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/PlantUMLLegendStyleTests.java b/structurizr-export/src/test/java/com/structurizr/export/plantuml/PlantUMLLegendStyleTests.java new file mode 100644 index 000000000..d56e3e786 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/PlantUMLLegendStyleTests.java @@ -0,0 +1,23 @@ +package com.structurizr.export.plantuml; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class PlantUMLLegendStyleTests { + + @Test + void test() { + PlantUMLLegendStyle style = new PlantUMLLegendStyle(); + + assertEquals(""" + // transparent element for relationships in legend + .Element-Transparent { + BackgroundColor: transparent; + LineColor: transparent; + FontColor: transparent; + } + """, style.toString()); + } + +} diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/PlantUMLRelationshipStyleTests.java b/structurizr-export/src/test/java/com/structurizr/export/plantuml/PlantUMLRelationshipStyleTests.java new file mode 100644 index 000000000..c73cee2c8 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/PlantUMLRelationshipStyleTests.java @@ -0,0 +1,58 @@ +package com.structurizr.export.plantuml; + +import com.structurizr.view.LineStyle; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class PlantUMLRelationshipStyleTests { + + @Test + void solid() { + PlantUMLRelationshipStyle style = new PlantUMLRelationshipStyle("Relationship", "#ff0000", LineStyle.Solid, 3, 24); + + assertEquals(""" + // Relationship + .Relationship-UmVsYXRpb25zaGlw { + LineThickness: 3; + LineStyle: 0; + LineColor: #ff0000; + FontColor: #ff0000; + FontSize: 24; + } + """, style.toString()); + } + + @Test + void dashed() { + PlantUMLRelationshipStyle style = new PlantUMLRelationshipStyle("Relationship", "#ff0000", LineStyle.Dashed, 3, 24); + + assertEquals(""" + // Relationship + .Relationship-UmVsYXRpb25zaGlw { + LineThickness: 3; + LineStyle: 15-15; + LineColor: #ff0000; + FontColor: #ff0000; + FontSize: 24; + } + """, style.toString()); + } + + @Test + void dotted() { + PlantUMLRelationshipStyle style = new PlantUMLRelationshipStyle("Relationship", "#ff0000", LineStyle.Dotted, 3, 24); + + assertEquals(""" + // Relationship + .Relationship-UmVsYXRpb25zaGlw { + LineThickness: 3; + LineStyle: 3-3; + LineColor: #ff0000; + FontColor: #ff0000; + FontSize: 24; + } + """, style.toString()); + } + +} diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/PlantUMLRootStyleTests.java b/structurizr-export/src/test/java/com/structurizr/export/plantuml/PlantUMLRootStyleTests.java new file mode 100644 index 000000000..b59703243 --- /dev/null +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/PlantUMLRootStyleTests.java @@ -0,0 +1,53 @@ +package com.structurizr.export.plantuml; + +import com.structurizr.view.LineStyle; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class PlantUMLRootStyleTests { + + @Test + void noFont() { + PlantUMLRootStyle style = new PlantUMLRootStyle( + "#ffffff", + "#444444", + null); + + assertEquals(""" + root { + BackgroundColor: #ffffff; + FontColor: #444444; + } + """, style.toString()); + + style = new PlantUMLRootStyle( + "#ffffff", + "#444444", + ""); + + assertEquals(""" + root { + BackgroundColor: #ffffff; + FontColor: #444444; + } + """, style.toString()); + } + + @Test + void font() { + PlantUMLRootStyle style = new PlantUMLRootStyle( + "#ffffff", + "#444444", + "Courier"); + + assertEquals(""" + root { + BackgroundColor: #ffffff; + FontColor: #444444; + FontName: Courier; + } + """, style.toString()); + } + +} diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java index 136e48cb9..8a6e1ff66 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java @@ -11,109 +11,991 @@ import java.io.File; import java.util.Collection; +import java.util.List; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class StructurizrPlantUMLDiagramExporterTests extends AbstractExporterTests { @Test - public void test_BigBankPlcExample() throws Exception { - Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/structurizr-36141-workspace.json")); - workspace.getViews().getConfiguration().addProperty(StructurizrPlantUMLExporter.PLANTUML_ANIMATION_PROPERTY, "true"); + public void systemLandscapeView_NoStyling_Light() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem a = workspace.getModel().addSoftwareSystem("A", "Description."); + SoftwareSystem b = workspace.getModel().addSoftwareSystem("B", "Description."); + a.uses(b, "Description", "Technology"); + + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "Description"); + view.addDefaultElements(); + + StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(ColorScheme.Light); + Diagram diagram = exporter.export(view); + + assertEquals(""" + @startuml + title System Landscape\\nDescription + + set separator none + top to bottom direction + hide stereotype + + + + rectangle "==A\\n[Software System]\\n\\nDescription." <> as A + rectangle "==B\\n[Software System]\\n\\nDescription." <> as B + + A --> B <> : "Description\\n[Technology]" + + @enduml""", diagram.getDefinition()); + + assertEquals(""" + @startuml + + set separator none + hide stereotype + + + + rectangle "==Element" <> + + rectangle "." <<.Element-Transparent>> as 1 + 1 --> 1 <> : "Relationship" + + @enduml""", diagram.getLegend().getDefinition()); + } + + @Test + public void systemLandscapeView_NoStyling_Dark() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem a = workspace.getModel().addSoftwareSystem("A", "Description."); + SoftwareSystem b = workspace.getModel().addSoftwareSystem("B", "Description."); + a.uses(b, "Description", "Technology"); + + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "Description"); + view.addDefaultElements(); + + StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(ColorScheme.Dark); + Diagram diagram = exporter.export(view); + + assertEquals(""" + @startuml + title System Landscape\\nDescription + + set separator none + top to bottom direction + hide stereotype + + + + rectangle "==A\\n[Software System]\\n\\nDescription." <> as A + rectangle "==B\\n[Software System]\\n\\nDescription." <> as B + + A --> B <> : "Description\\n[Technology]" + + @enduml""", diagram.getDefinition()); + + assertEquals(""" + @startuml + + set separator none + hide stereotype + + + + rectangle "==Element" <> + + rectangle "." <<.Element-Transparent>> as 1 + 1 --> 1 <> : "Relationship" + + @enduml""", diagram.getLegend().getDefinition()); + } + + @Test + public void systemContextView_NoStyling_Light() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem a = workspace.getModel().addSoftwareSystem("A", "Description."); + SoftwareSystem b = workspace.getModel().addSoftwareSystem("B", "Description."); + a.uses(b, "Description", "Technology"); + + SystemContextView view = workspace.getViews().createSystemContextView(a, "key", "Description"); + view.addDefaultElements(); + + StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(ColorScheme.Light); + Diagram diagram = exporter.export(view); + + assertEquals(""" + @startuml + title A - System Context\\nDescription + + set separator none + top to bottom direction + hide stereotype + + + + rectangle "==A\\n[Software System]\\n\\nDescription." <> as A + rectangle "==B\\n[Software System]\\n\\nDescription." <> as B + + A --> B <> : "Description\\n[Technology]" + + @enduml""", diagram.getDefinition()); + } + + @Test + public void containerView_NoStyling_Light() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem a = workspace.getModel().addSoftwareSystem("Software System A", "Description."); + Container container1 = a.addContainer("Container 1", "Description", "Technology"); + Container container2 = a.addContainer("Container 2", "Description", "Technology"); + container1.uses(container2, "Description", "Technology"); + + ContainerView view = workspace.getViews().createContainerView(a, "key", "Description"); + view.addDefaultElements(); + + StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(ColorScheme.Light); + Diagram diagram = exporter.export(view); + + assertEquals(""" + @startuml + title Software System A - Containers\\nDescription + + set separator none + top to bottom direction + hide stereotype + + + + rectangle "Software System A\\n[Software System]" <> { + rectangle "==Container 1\\n[Container: Technology]\\n\\nDescription" <> as SoftwareSystemA.Container1 + rectangle "==Container 2\\n[Container: Technology]\\n\\nDescription" <> as SoftwareSystemA.Container2 + } + + SoftwareSystemA.Container1 --> SoftwareSystemA.Container2 <> : "Description\\n[Technology]" + + @enduml""", diagram.getDefinition()); + } + + @Test + public void componentView_NoStyling_Light() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem a = workspace.getModel().addSoftwareSystem("Software System", "Description."); + Container container = a.addContainer("Container", "Description", "Technology"); + Component component1 = container.addComponent("Component 1", "Description", "Technology"); + Component component2 = container.addComponent("Component 2", "Description", "Technology"); + component1.uses(component2, "Description", "Technology"); + + ComponentView view = workspace.getViews().createComponentView(container, "key", "Description"); + view.addDefaultElements(); + + StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(ColorScheme.Light); + Diagram diagram = exporter.export(view); + + assertEquals(""" + @startuml + title Software System - Container - Components\\nDescription + + set separator none + top to bottom direction + hide stereotype + + + + rectangle "Container\\n[Container: Technology]" <> { + rectangle "==Component 1\\n[Component: Technology]\\n\\nDescription" <> as SoftwareSystem.Container.Component1 + rectangle "==Component 2\\n[Component: Technology]\\n\\nDescription" <> as SoftwareSystem.Container.Component2 + } + + SoftwareSystem.Container.Component1 --> SoftwareSystem.Container.Component2 <> : "Description\\n[Technology]" + + @enduml""", diagram.getDefinition()); + } + + @Test + public void deploymentView_NoStyling_Light() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem a = workspace.getModel().addSoftwareSystem("A"); + DeploymentNode node1 = workspace.getModel().addDeploymentNode("Node 1"); + node1.addDeploymentNode("Node 2").add(a); + node1.addDeploymentNode("Node 3").addInfrastructureNode("Infrastructure Node"); + + DeploymentView view = workspace.getViews().createDeploymentView("deployment", "Default"); + view.addDefaultElements(); + + StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(ColorScheme.Light); + Diagram diagram = exporter.export(view); + + assertEquals(""" + @startuml + title Deployment - Default\\nDefault + + set separator none + top to bottom direction + hide stereotype + + + + rectangle "Node 1\\n[Deployment Node]" <> as Default.Node1 { + rectangle "Node 2\\n[Deployment Node]" <> as Default.Node1.Node2 { + rectangle "==A\\n[Software System]" <> as Default.Node1.Node2.A_1 + } + + rectangle "Node 3\\n[Deployment Node]" <> as Default.Node1.Node3 { + rectangle "==Infrastructure Node\\n[Infrastructure Node]" <> as Default.Node1.Node3.InfrastructureNode + } + + } + + @enduml""", diagram.getDefinition()); + } + + @Test + public void dynamicView_CollaborationStyle_NoStyling_Light() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem a = workspace.getModel().addSoftwareSystem("A"); + SoftwareSystem b = workspace.getModel().addSoftwareSystem("B"); + + a.uses(b, "Uses"); + + DynamicView view = workspace.getViews().createDynamicView("key", "Description"); + view.add(a, b); StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(); + Diagram diagram = exporter.export(view); - Collection diagrams = exporter.export(workspace); - assertEquals(7, diagrams.size()); - - Diagram diagram = diagrams.stream().filter(d -> d.getKey().equals("SystemLandscape")).findFirst().get(); - String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemLandscape.puml")); - assertEquals(expected, diagram.getDefinition()); - assertEquals(0, diagram.getFrames().size()); - - //assertEquals("", diagram.getLegend().getDefinition()); - - diagram = diagrams.stream().filter(d -> d.getKey().equals("SystemContext")).findFirst().get(); - expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemContext.puml")); - assertEquals(expected, diagram.getDefinition()); - assertEquals(4, diagram.getFrames().size()); - - diagram = diagrams.stream().filter(d -> d.getKey().equals("Containers")).findFirst().get(); - expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/36141-Containers.puml")); - assertEquals(expected, diagram.getDefinition()); - assertEquals(6, diagram.getFrames().size()); - - diagram = diagrams.stream().filter(d -> d.getKey().equals("Components")).findFirst().get(); - expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/36141-Components.puml")); - assertEquals(expected, diagram.getDefinition()); - assertEquals(4, diagram.getFrames().size()); - - diagram = diagrams.stream().filter(d -> d.getKey().equals("SignIn")).findFirst().get(); - expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/36141-SignIn.puml")); - assertEquals(expected, diagram.getDefinition()); - assertEquals(6, diagram.getFrames().size()); - - diagram = diagrams.stream().filter(md -> md.getKey().equals("DevelopmentDeployment")).findFirst().get(); - expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/36141-DevelopmentDeployment.puml")); - assertEquals(expected, diagram.getDefinition()); - assertEquals(3, diagram.getFrames().size()); - - diagram = diagrams.stream().filter(md -> md.getKey().equals("LiveDeployment")).findFirst().get(); - expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/36141-LiveDeployment.puml")); - assertEquals(expected, diagram.getDefinition()); - assertEquals(5, diagram.getFrames().size()); - - // and the sequence diagram version - workspace.getViews().getConfiguration().addProperty(exporter.PLANTUML_SEQUENCE_DIAGRAM_PROPERTY, "true"); - diagrams = exporter.export(workspace); - diagram = diagrams.stream().filter(d -> d.getKey().equals("SignIn")).findFirst().get(); - expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/36141-SignIn-sequence.puml")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + @startuml + title Dynamic\\nDescription + + set separator none + top to bottom direction + hide stereotype + + + + rectangle "==A\\n[Software System]" <> as A + rectangle "==B\\n[Software System]" <> as B + + A --> B <> : "1. Uses" + + @enduml""", diagram.getDefinition()); } @Test - @Tag("IntegrationTest") - public void test_AmazonWebServicesExample() throws Exception { - Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/structurizr-54915-workspace.json")); - ThemeUtils.loadThemes(workspace); - workspace.getViews().getDeploymentViews().iterator().next().enableAutomaticLayout(AutomaticLayout.RankDirection.LeftRight, 300, 300); + public void dynamicView_CollaborationStyle_Frames() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem a = workspace.getModel().addSoftwareSystem("A"); + SoftwareSystem b = workspace.getModel().addSoftwareSystem("B"); + SoftwareSystem c = workspace.getModel().addSoftwareSystem("C"); + + a.uses(b, "Uses"); + b.uses(c, "Uses"); + + DynamicView view = workspace.getViews().createDynamicView("key", "Description"); + view.add(a, b); + view.add(b, c); + view.addProperty(StructurizrPlantUMLExporter.PLANTUML_ANIMATION_PROPERTY, "true"); StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(); - Collection diagrams = exporter.export(workspace); - assertEquals(1, diagrams.size()); + List frames = exporter.export(view).getFrames(); + assertEquals(2, frames.size()); - Diagram diagram = diagrams.stream().findFirst().get(); - String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/54915-AmazonWebServicesDeployment.puml")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + @startuml + title Dynamic\\nDescription + + set separator none + top to bottom direction + hide stereotype + + + + rectangle "==A\\n[Software System]" <> as A + rectangle "==B\\n[Software System]" <> as B + rectangle "==C\\n[Software System]" <> as C + hide C + + A --> B <> : "1. Uses" + B --> C <> : "2. Uses" + + @enduml""", frames.get(0).getDefinition()); + + assertEquals(""" + @startuml + title Dynamic\\nDescription + + set separator none + top to bottom direction + hide stereotype + + + + rectangle "==A\\n[Software System]" <> as A + hide A + rectangle "==B\\n[Software System]" <> as B + rectangle "==C\\n[Software System]" <> as C + + A --> B <> : "1. Uses" + B --> C <> : "2. Uses" + + @enduml""", frames.get(1).getDefinition()); + } + + @Test + public void dynamicView_SequenceStyle_NoStyling_Light() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem a = workspace.getModel().addSoftwareSystem("A"); + SoftwareSystem b = workspace.getModel().addSoftwareSystem("B"); + + a.uses(b, "Uses"); + + DynamicView view = workspace.getViews().createDynamicView("key", "Description"); + view.add(a, b); + view.addProperty(StructurizrPlantUMLExporter.PLANTUML_SEQUENCE_DIAGRAM_PROPERTY, "true"); + + StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(); + Diagram diagram = exporter.export(view); + + assertEquals(""" + @startuml + title Dynamic\\nDescription + + set separator none + hide stereotype + + + + participant "A\\n[Software System]" as A <> #ffffff + participant "B\\n[Software System]" as B <> #ffffff + + A -> B <> : Uses + + @enduml""", diagram.getDefinition()); - expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/54915-AmazonWebServicesDeployment-Legend.puml")); - assertEquals(expected, diagram.getLegend().getDefinition()); + assertEquals(""" + @startuml + + set separator none + hide stereotype + + + + rectangle "==Element" <> + + rectangle "." <<.Element-Transparent>> as 1 + 1 --> 1 <> : "Relationship" + + @enduml""", diagram.getLegend().getDefinition()); } @Test - public void test_GroupsExample() throws Exception { + public void groups() throws Exception { Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/groups.json")); - ThemeUtils.loadThemes(workspace); StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(); Collection diagrams = exporter.export(workspace); assertEquals(3, diagrams.size()); Diagram diagram = diagrams.stream().filter(md -> md.getKey().equals("SystemLandscape")).findFirst().get(); - String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/groups-SystemLandscape.puml")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + @startuml + title System Landscape + + set separator none + top to bottom direction + skinparam ranksep 60 + skinparam nodesep 30 + hide stereotype + + + + rectangle "Group 1" <> as groupR3JvdXAgMQ== { + rectangle "==B\\n[Software System]" <> as B + } + + rectangle "Group 2" <> as groupR3JvdXAgMg== { + rectangle "==C\\n[Software System]" <> as C + rectangle "Group 3" <> as groupR3JvdXAgMi9Hcm91cCAz { + rectangle "==D\\n[Software System]" <> as D + } + + } + + rectangle "==A\\n[Software System]" <> as A + + B --> C <> : "" + C --> D <> : "" + A --> B <> : "" + + @enduml""", diagram.getDefinition()); diagram = diagrams.stream().filter(md -> md.getKey().equals("Containers")).findFirst().get(); - expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/groups-Containers.puml")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + @startuml + title D - Containers + + set separator none + top to bottom direction + skinparam ranksep 60 + skinparam nodesep 30 + hide stereotype + + + + rectangle "==C\\n[Software System]" <> as C + + rectangle "D\\n[Software System]" <> { + rectangle "Group 4" <> as groupR3JvdXAgNA== { + rectangle "==F\\n[Container]" <> as D.F + } + + rectangle "==E\\n[Container]" <> as D.E + } + + C --> D.E <> : "" + C --> D.F <> : "" + + @enduml""", diagram.getDefinition()); diagram = diagrams.stream().filter(md -> md.getKey().equals("Components")).findFirst().get(); - expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/groups-Components.puml")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + @startuml + title D - F - Components + + set separator none + top to bottom direction + skinparam ranksep 60 + skinparam nodesep 30 + hide stereotype + + + + rectangle "==C\\n[Software System]" <> as C + + rectangle "F\\n[Container]" <> { + rectangle "Group 5" <> as groupR3JvdXAgNQ== { + rectangle "==H\\n[Component]" <> as D.F.H + } + + rectangle "==G\\n[Component]" <> as D.F.G + } + + C --> D.F.G <> : "" + C --> D.F.H <> : "" + + @enduml""", diagram.getDefinition()); } @Test - public void test_NestedGroupsExample() throws Exception { + public void nestedGroups() { Workspace workspace = new Workspace("Name", "Description"); workspace.getModel().addProperty("structurizr.groupSeparator", "/"); @@ -132,7 +1014,7 @@ public void test_NestedGroupsExample() throws Exception { SoftwareSystem e = workspace.getModel().addSoftwareSystem("Department 1"); e.setGroup("Organisation 1/Department 1"); - SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("SystemLandscape", "Description"); + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("SystemLandscape"); view.addAllElements(); workspace.getViews().getConfiguration().getStyles().addElementStyle("Group:Organisation 1/Department 1/Team 1").color("#ff0000"); @@ -142,44 +1024,115 @@ public void test_NestedGroupsExample() throws Exception { Collection diagrams = exporter.export(workspace); Diagram diagram = diagrams.stream().filter(md -> md.getKey().equals("SystemLandscape")).findFirst().get(); - String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/nested-groups.puml")); - assertEquals(expected, diagram.getDefinition()); - } - - @Test - public void test_renderGroupStyles() throws Exception { - Workspace workspace = new Workspace("Name", "Description"); - workspace.getModel().addPerson("User 1").setGroup("Group 1"); - workspace.getModel().addPerson("User 2").setGroup("Group 2"); - workspace.getModel().addPerson("User 3").setGroup("Group 3"); - - SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", ""); - view.addDefaultElements(); - - workspace.getViews().getConfiguration().getStyles().addElementStyle("Group:Group 1").color("#111111").icon("https://example.com/icon1.png"); - workspace.getViews().getConfiguration().getStyles().addElementStyle("Group:Group 2").color("#222222").icon("https://example.com/icon2.png"); - - StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter() { - @Override - protected double calculateIconScale(String iconUrl) { - return 1.0; - } - }; - - Diagram diagram = exporter.export(view); - String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/group-styles-1.puml")); - assertEquals(expected, diagram.getDefinition()); - - workspace.getViews().getConfiguration().getStyles().addElementStyle("Group").color("#aabbcc"); - - diagram = exporter.export(view); - expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/group-styles-2.puml")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + @startuml + title System Landscape + + set separator none + top to bottom direction + hide stereotype + + + + rectangle "Organisation 1" <> as groupT3JnYW5pc2F0aW9uIDE= { + rectangle "==Organisation 1\\n[Software System]" <> as Organisation1 + rectangle "Department 1" <> as groupT3JnYW5pc2F0aW9uIDEvRGVwYXJ0bWVudCAx { + rectangle "==Department 1\\n[Software System]" <> as Department1 + rectangle "Team 1" <> as groupT3JnYW5pc2F0aW9uIDEvRGVwYXJ0bWVudCAxL1RlYW0gMQ== { + rectangle "==Team 1\\n[Software System]" <> as Team1 + } + + rectangle "Team 2" <> as groupT3JnYW5pc2F0aW9uIDEvRGVwYXJ0bWVudCAxL1RlYW0gMg== { + rectangle "==Team 2\\n[Software System]" <> as Team2 + } + + } + + } + + rectangle "Organisation 2" <> as groupT3JnYW5pc2F0aW9uIDI= { + rectangle "==Organisation 2\\n[Software System]" <> as Organisation2 + } + + + @enduml""", diagram.getDefinition()); } @Test - public void test_renderContainerDiagramWithExternalContainers() { - Workspace workspace = new Workspace("Name", "Description"); + public void containerDiagramWithExternalContainers() { + Workspace workspace = new Workspace("Name"); SoftwareSystem softwareSystem1 = workspace.getModel().addSoftwareSystem("Software System 1"); Container container1 = softwareSystem1.addContainer("Container 1"); SoftwareSystem softwareSystem2 = workspace.getModel().addSoftwareSystem("Software System 2"); @@ -192,59 +1145,79 @@ public void test_renderContainerDiagramWithExternalContainers() { containerView.add(container2); Diagram diagram = new StructurizrPlantUMLExporter().export(containerView); - assertEquals("@startuml\n" + - "set separator none\n" + - "title Software System 1 - Containers\n" + - "\n" + - "top to bottom direction\n" + - "\n" + - "skinparam {\n" + - " arrowFontSize 10\n" + - " defaultTextAlignment center\n" + - " wrapWidth 200\n" + - " maxMessageSize 100\n" + - "}\n" + - "\n" + - "hide stereotype\n" + - "\n" + - "skinparam rectangle<> {\n" + - " BackgroundColor #dddddd\n" + - " FontColor #000000\n" + - " BorderColor #9a9a9a\n" + - " shadowing false\n" + - "}\n" + - "skinparam rectangle<> {\n" + - " BackgroundColor #dddddd\n" + - " FontColor #000000\n" + - " BorderColor #9a9a9a\n" + - " shadowing false\n" + - "}\n" + - "skinparam rectangle<> {\n" + - " BorderColor #9a9a9a\n" + - " FontColor #9a9a9a\n" + - " shadowing false\n" + - "}\n" + - "skinparam rectangle<> {\n" + - " BorderColor #9a9a9a\n" + - " FontColor #9a9a9a\n" + - " shadowing false\n" + - "}\n" + - "\n" + - "rectangle \"Software System 1\\n[Software System]\" <> {\n" + - " rectangle \"==Container 1\\n[Container]\" <> as SoftwareSystem1.Container1\n" + - "}\n" + - "\n" + - "rectangle \"Software System 2\\n[Software System]\" <> {\n" + - " rectangle \"==Container 2\\n[Container]\" <> as SoftwareSystem2.Container2\n" + - "}\n" + - "\n" + - "SoftwareSystem1.Container1 .[#707070,thickness=2].> SoftwareSystem2.Container2 : \"Uses\"\n" + - "@enduml", diagram.getDefinition()); + assertEquals(""" + @startuml + title Software System 1 - Containers + + set separator none + top to bottom direction + hide stereotype + + + + rectangle "Software System 1\\n[Software System]" <> { + rectangle "==Container 1\\n[Container]" <> as SoftwareSystem1.Container1 + } + + rectangle "Software System 2\\n[Software System]" <> { + rectangle "==Container 2\\n[Container]" <> as SoftwareSystem2.Container2 + } + + SoftwareSystem1.Container1 --> SoftwareSystem2.Container2 <> : "Uses" + + @enduml""", diagram.getDefinition()); } @Test - public void test_renderComponentDiagramWithExternalComponents() throws Exception { - Workspace workspace = new Workspace("Name", "Description"); + public void componentDiagramWithExternalComponents() { + Workspace workspace = new Workspace("Name"); SoftwareSystem softwareSystem1 = workspace.getModel().addSoftwareSystem("Software System 1"); Container container1 = softwareSystem1.addContainer("Container 1"); @@ -264,13 +1237,81 @@ public void test_renderComponentDiagramWithExternalComponents() throws Exception componentView.add(component3); Diagram diagram = new StructurizrPlantUMLExporter().export(componentView); - String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/component-view-with-external-components-1.puml")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + @startuml + title Software System 1 - Container 1 - Components + + set separator none + top to bottom direction + hide stereotype + + + + rectangle "Container 1\\n[Container]" <> { + rectangle "==Component 1\\n[Component]" <> as SoftwareSystem1.Container1.Component1 + rectangle "==Component 2\\n[Component]" <> as SoftwareSystem1.Container1.Component2 + } + + rectangle "Container 2\\n[Container]" <> { + rectangle "==Component 3\\n[Component]" <> as SoftwareSystem2.Container2.Component3 + } + + SoftwareSystem1.Container1.Component1 --> SoftwareSystem1.Container1.Component2 <> : "Uses" + SoftwareSystem1.Container1.Component2 --> SoftwareSystem2.Container2.Component3 <> : "Uses" + + @enduml""", diagram.getDefinition()); } @Test - public void test_renderComponentDiagramWithExternalComponentsAndSoftwareSystemBoundariesIncluded() throws Exception { - Workspace workspace = new Workspace("Name", "Description"); + public void componentDiagramWithExternalComponentsAndSoftwareSystemBoundariesIncluded() { + Workspace workspace = new Workspace("Name"); SoftwareSystem softwareSystem1 = workspace.getModel().addSoftwareSystem("Software System 1"); Container container1 = softwareSystem1.addContainer("Container 1"); @@ -291,13 +1332,109 @@ public void test_renderComponentDiagramWithExternalComponentsAndSoftwareSystemBo componentView.addProperty("structurizr.softwareSystemBoundaries", "true"); Diagram diagram = new StructurizrPlantUMLExporter().export(componentView); - String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/component-view-with-external-components-2.puml")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + @startuml + title Software System 1 - Container 1 - Components + + set separator none + top to bottom direction + hide stereotype + + + + rectangle "Software System 1\\n[Software System]" <> { + rectangle "Container 1\\n[Container]" <> { + rectangle "==Component 1\\n[Component]" <> as SoftwareSystem1.Container1.Component1 + rectangle "==Component 2\\n[Component]" <> as SoftwareSystem1.Container1.Component2 + } + + } + + rectangle "Software System 2\\n[Software System]" <> { + rectangle "Container 2\\n[Container]" <> { + rectangle "==Component 3\\n[Component]" <> as SoftwareSystem2.Container2.Component3 + } + + } + + SoftwareSystem1.Container1.Component1 --> SoftwareSystem1.Container1.Component2 <> : "Uses" + SoftwareSystem1.Container1.Component2 --> SoftwareSystem2.Container2.Component3 <> : "Uses" + + @enduml""", diagram.getDefinition()); } @Test - public void test_renderDynamicDiagramWithExternalContainers() { - Workspace workspace = new Workspace("Name", "Description"); + public void dynamicView_ExternalContainers() { + Workspace workspace = new Workspace("Name"); SoftwareSystem softwareSystem1 = workspace.getModel().addSoftwareSystem("Software System 1"); Container container1 = softwareSystem1.addContainer("Container 1"); SoftwareSystem softwareSystem2 = workspace.getModel().addSoftwareSystem("Software System 2"); @@ -309,59 +1446,79 @@ public void test_renderDynamicDiagramWithExternalContainers() { dynamicView.add(container1, container2); Diagram diagram = new StructurizrPlantUMLExporter().export(dynamicView); - assertEquals("@startuml\n" + - "set separator none\n" + - "title Software System 1 - Dynamic\n" + - "\n" + - "top to bottom direction\n" + - "\n" + - "skinparam {\n" + - " arrowFontSize 10\n" + - " defaultTextAlignment center\n" + - " wrapWidth 200\n" + - " maxMessageSize 100\n" + - "}\n" + - "\n" + - "hide stereotype\n" + - "\n" + - "skinparam rectangle<> {\n" + - " BackgroundColor #dddddd\n" + - " FontColor #000000\n" + - " BorderColor #9a9a9a\n" + - " shadowing false\n" + - "}\n" + - "skinparam rectangle<> {\n" + - " BackgroundColor #dddddd\n" + - " FontColor #000000\n" + - " BorderColor #9a9a9a\n" + - " shadowing false\n" + - "}\n" + - "skinparam rectangle<> {\n" + - " BorderColor #9a9a9a\n" + - " FontColor #9a9a9a\n" + - " shadowing false\n" + - "}\n" + - "skinparam rectangle<> {\n" + - " BorderColor #9a9a9a\n" + - " FontColor #9a9a9a\n" + - " shadowing false\n" + - "}\n" + - "\n" + - "rectangle \"Software System 1\\n[Software System]\" <> {\n" + - " rectangle \"==Container 1\\n[Container]\" <> as SoftwareSystem1.Container1\n" + - "}\n" + - "\n" + - "rectangle \"Software System 2\\n[Software System]\" <> {\n" + - " rectangle \"==Container 2\\n[Container]\" <> as SoftwareSystem2.Container2\n" + - "}\n" + - "\n" + - "SoftwareSystem1.Container1 .[#707070,thickness=2].> SoftwareSystem2.Container2 : \"1. Uses\"\n" + - "@enduml", diagram.getDefinition()); + assertEquals(""" + @startuml + title Software System 1 - Dynamic + + set separator none + top to bottom direction + hide stereotype + + + + rectangle "Software System 1\\n[Software System]" <> { + rectangle "==Container 1\\n[Container]" <> as SoftwareSystem1.Container1 + } + + rectangle "Software System 2\\n[Software System]" <> { + rectangle "==Container 2\\n[Container]" <> as SoftwareSystem2.Container2 + } + + SoftwareSystem1.Container1 --> SoftwareSystem2.Container2 <> : "1. Uses" + + @enduml""", diagram.getDefinition()); } @Test - public void test_renderDynamicDiagramWithExternalComponents() throws Exception { - Workspace workspace = new Workspace("Name", "Description"); + public void dynamicView_ExternalComponents() { + Workspace workspace = new Workspace("Name"); SoftwareSystem softwareSystem1 = workspace.getModel().addSoftwareSystem("Software System 1"); Container container1 = softwareSystem1.addContainer("Container 1"); @@ -380,13 +1537,81 @@ public void test_renderDynamicDiagramWithExternalComponents() throws Exception { dynamicView.add(component2, component3); Diagram diagram = new StructurizrPlantUMLExporter().export(dynamicView); - String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-with-external-components-1.puml")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + @startuml + title Container 1 - Dynamic + + set separator none + top to bottom direction + hide stereotype + + + + rectangle "Container 1\\n[Container]" <> { + rectangle "==Component 1\\n[Component]" <> as SoftwareSystem1.Container1.Component1 + rectangle "==Component 2\\n[Component]" <> as SoftwareSystem1.Container1.Component2 + } + + rectangle "Container 2\\n[Container]" <> { + rectangle "==Component 3\\n[Component]" <> as SoftwareSystem2.Container2.Component3 + } + + SoftwareSystem1.Container1.Component1 --> SoftwareSystem1.Container1.Component2 <> : "1. Uses" + SoftwareSystem1.Container1.Component2 --> SoftwareSystem2.Container2.Component3 <> : "2. Uses" + + @enduml""", diagram.getDefinition()); } @Test - public void test_renderDynamicDiagramWithExternalComponentsAndSoftwareSystemBoundariesIncluded() throws Exception { - Workspace workspace = new Workspace("Name", "Description"); + public void dynamicView_ExternalComponentsAndSoftwareSystemBoundariesIncluded() { + Workspace workspace = new Workspace("Name"); SoftwareSystem softwareSystem1 = workspace.getModel().addSoftwareSystem("Software System 1"); Container container1 = softwareSystem1.addContainer("Container 1"); @@ -406,341 +1631,401 @@ public void test_renderDynamicDiagramWithExternalComponentsAndSoftwareSystemBoun dynamicView.addProperty("structurizr.softwareSystemBoundaries", "true"); Diagram diagram = new StructurizrPlantUMLExporter().export(dynamicView); - String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-with-external-components-2.puml")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + @startuml + title Container 1 - Dynamic + + set separator none + top to bottom direction + hide stereotype + + + + rectangle "Software System 1\\n[Software System]" <> { + rectangle "Container 1\\n[Container]" <> { + rectangle "==Component 1\\n[Component]" <> as SoftwareSystem1.Container1.Component1 + rectangle "==Component 2\\n[Component]" <> as SoftwareSystem1.Container1.Component2 + } + + } + + rectangle "Software System 2\\n[Software System]" <> { + rectangle "Container 2\\n[Container]" <> { + rectangle "==Component 3\\n[Component]" <> as SoftwareSystem2.Container2.Component3 + } + + } + + SoftwareSystem1.Container1.Component1 --> SoftwareSystem1.Container1.Component2 <> : "1. Uses" + SoftwareSystem1.Container1.Component2 --> SoftwareSystem2.Container2.Component3 <> : "2. Uses" + + @enduml""", diagram.getDefinition()); } @Test - public void test_renderDiagramWithElementUrls() { - Workspace workspace = new Workspace("Name", "Description"); + public void elementUrls() { + Workspace workspace = new Workspace("Name"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); softwareSystem.setUrl("https://structurizr.com"); - SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "Description"); + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key"); view.addDefaultElements(); Diagram diagram = new StructurizrPlantUMLExporter().export(view); - assertTrue(diagram.getDefinition().contains("rectangle \"==Software System\\n[Software System]\" <> as SoftwareSystem [[https://structurizr.com]]\n")); + assertTrue(diagram.getDefinition().contains("as SoftwareSystem [[https://structurizr.com]]")); } @Test - public void test_renderDiagramWithIncludes() { - Workspace workspace = new Workspace("Name", "Description"); - workspace.getModel().addSoftwareSystem("Software System"); + public void elementInstanceUrl() { + Workspace workspace = new Workspace("Name"); + SoftwareSystem a = workspace.getModel().addSoftwareSystem("A"); + a.setUrl("https://example.com/url1"); + SoftwareSystemInstance aInstance = workspace.getModel().addDeploymentNode("Node").add(a); - SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "Description"); - view.addDefaultElements(); + DeploymentView view = workspace.getViews().createDeploymentView("deployment", "Default"); + view.add(aInstance); - view.getViewSet().getConfiguration().addProperty(StructurizrPlantUMLExporter.PLANTUML_INCLUDES_PROPERTY, "styles.puml"); + assertTrue(new StructurizrPlantUMLExporter().export(view).getDefinition().contains("as Default.Node.A_1 [[https://example.com/url1]]")); - Diagram diagram = new StructurizrPlantUMLExporter().export(view); - assertTrue(diagram.getDefinition().contains("!include styles.puml\n")); + aInstance.setUrl("https://example.com/url2"); + assertTrue(new StructurizrPlantUMLExporter().export(view).getDefinition().contains("as Default.Node.A_1 [[https://example.com/url2]]")); } @Test - public void test_renderDiagramWithNewLineCharacterInElementName() { - Workspace workspace = new Workspace("Name", "Description"); + public void newLineCharacterInElementName() { + Workspace workspace = new Workspace("Name"); workspace.getModel().addSoftwareSystem("Software\nSystem"); - SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "Description"); + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key"); view.addDefaultElements(); Diagram diagram = new StructurizrPlantUMLExporter().export(view); - assertTrue(diagram.getDefinition().contains("rectangle \"==Software\\nSystem\\n[Software System]\" <> as SoftwareSystem")); + assertEquals(""" + @startuml + title System Landscape + + set separator none + top to bottom direction + hide stereotype + + + + rectangle "==Software\\nSystem\\n[Software System]" <> as SoftwareSystem + + @enduml""", diagram.getDefinition()); } @Test - public void test_renderCustomView() { - Workspace workspace = new Workspace("Name", "Description"); + public void customView() { + Workspace workspace = new Workspace("Name"); Model model = workspace.getModel(); CustomElement a = model.addCustomElement("A"); CustomElement b = model.addCustomElement("B", "Custom", "Description"); a.uses(b, "Uses"); - CustomView view = workspace.getViews().createCustomView("key", "Title", "Description"); + CustomView view = workspace.getViews().createCustomView("key", "Title"); view.addDefaultElements(); Diagram diagram = new StructurizrPlantUMLExporter().export(view); - assertEquals("@startuml\nset separator none\n" + - "title Title\n" + - "\n" + - "top to bottom direction\n" + - "\n" + - "skinparam {\n" + - " arrowFontSize 10\n" + - " defaultTextAlignment center\n" + - " wrapWidth 200\n" + - " maxMessageSize 100\n" + - "}\n" + - "\n" + - "hide stereotype\n" + - "\n" + - "skinparam rectangle<<1>> {\n" + - " BackgroundColor #dddddd\n" + - " FontColor #000000\n" + - " BorderColor #9a9a9a\n" + - " shadowing false\n" + - "}\n" + - "skinparam rectangle<<2>> {\n" + - " BackgroundColor #dddddd\n" + - " FontColor #000000\n" + - " BorderColor #9a9a9a\n" + - " shadowing false\n" + - "}\n" + - "\n" + - "rectangle \"==A\" <<1>> as 1\n" + - "rectangle \"==B\\n[Custom]\\n\\nDescription\" <<2>> as 2\n" + - "\n" + - "1 .[#707070,thickness=2].> 2 : \"Uses\"\n" + - "@enduml", diagram.getDefinition()); + assertEquals(""" + @startuml + title Title + + set separator none + top to bottom direction + hide stereotype + + + + rectangle "==A" <> as 1 + rectangle "==B\\n[Custom]\\n\\nDescription" <> as 2 + + 1 --> 2 <> : "Uses" + + @enduml""", diagram.getDefinition()); } @Test void renderWorkspaceWithUnicodeElementName() { - Workspace workspace = new Workspace("Name", "Description"); + Workspace workspace = new Workspace("Name"); workspace.getModel().addPerson("Пользователь"); workspace.getViews().createSystemLandscapeView("key", "Description").addDefaultElements(); String diagramDefinition = new StructurizrPlantUMLExporter().export(workspace).stream().findFirst().get().getDefinition(); - assertTrue(diagramDefinition.contains("skinparam rectangle<<Пользователь>> {")); - assertTrue(diagramDefinition.contains("rectangle \"==Пользователь\\n[Person]\" <<Пользователь>> as Пользователь")); - } - - @Test - public void testLegend() { - Workspace workspace = new Workspace("Name", "Description"); - Model model = workspace.getModel(); - - CustomElement a = model.addCustomElement("A"); - a.addTags("Tag 1"); - CustomElement b = model.addCustomElement("B"); - b.addTags("Tag 2"); - a.uses(b, "...").addTags("Tag 3"); - b.uses(a, "...").addTags("Tag 4"); - - CustomView view = workspace.getViews().createCustomView("key", "Title", "Description"); - view.addDefaultElements(); - - Diagram diagram = new StructurizrPlantUMLExporter().export(view); - assertEquals("@startuml\nset separator none\n" + - "\n" + - "skinparam {\n" + - " shadowing false\n" + - " arrowFontSize 15\n" + - " defaultTextAlignment center\n" + - " wrapWidth 100\n" + - " maxMessageSize 100\n" + - "}\n" + - "hide stereotype\n" + - "\n" + - "skinparam rectangle<<_transparent>> {\n" + - " BorderColor transparent\n" + - " BackgroundColor transparent\n" + - " FontColor transparent\n" + - "}\n" + - "\n" + - "skinparam rectangle<<1>> {\n" + - " BackgroundColor #dddddd\n" + - " FontColor #000000\n" + - " BorderColor #9a9a9a\n" + - "}\n" + - "rectangle \"==Element\" <<1>>\n" + - "\n" + - "rectangle \".\" <<_transparent>> as 2\n" + - "2 .[#707070,thickness=2].> 2 : \"Relationship\"\n" + - "\n" + - "\n" + - "@enduml", diagram.getLegend().getDefinition()); - - workspace.getViews().getConfiguration().getStyles().addElementStyle("Tag 1").background("#ff0000").color("#ffffff").shape(Shape.RoundedBox); - workspace.getViews().getConfiguration().getStyles().addElementStyle("Tag 2").background("#00ff00").color("#ffffff").shape(Shape.Hexagon); - workspace.getViews().getConfiguration().getStyles().addRelationshipStyle("Tag 3").color("#0000ff"); - workspace.getViews().getConfiguration().getStyles().addRelationshipStyle("Tag 4").color("#ff00ff").thickness(3).style(LineStyle.Solid); - - diagram = new StructurizrPlantUMLExporter().export(view); - assertEquals("@startuml\nset separator none\n" + - "\n" + - "skinparam {\n" + - " shadowing false\n" + - " arrowFontSize 15\n" + - " defaultTextAlignment center\n" + - " wrapWidth 100\n" + - " maxMessageSize 100\n" + - "}\n" + - "hide stereotype\n" + - "\n" + - "skinparam rectangle<<_transparent>> {\n" + - " BorderColor transparent\n" + - " BackgroundColor transparent\n" + - " FontColor transparent\n" + - "}\n" + - "\n" + - "skinparam rectangle<<1>> {\n" + - " BackgroundColor #ff0000\n" + - " FontColor #ffffff\n" + - " BorderColor #b20000\n" + - " roundCorner 20\n" + - "}\n" + - "rectangle \"==Tag 1\" <<1>>\n" + - "\n" + - "skinparam hexagon<<2>> {\n" + - " BackgroundColor #00ff00\n" + - " FontColor #ffffff\n" + - " BorderColor #00b200\n" + - "}\n" + - "hexagon \"==Tag 2\" <<2>>\n" + - "\n" + - "rectangle \".\" <<_transparent>> as 3\n" + - "3 .[#0000ff,thickness=2].> 3 : \"Tag 3\"\n" + - "\n" + - "rectangle \".\" <<_transparent>> as 4\n" + - "4 -[#ff00ff,thickness=3]-> 4 : \"Tag 4\"\n" + - "\n" + - "\n" + - "@enduml", diagram.getLegend().getDefinition()); - } - - @Test - public void staticDiagramsAreUnchangedWhenSequenceDiagramsAreEnabled() { - Workspace workspace = new Workspace("Name", "Description"); - Model model = workspace.getModel(); - - model.addSoftwareSystem("Software System").setGroup("Group"); - SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "Description"); - view.addAllElements(); - - StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(); - Diagram diagram; - String expected = """ + assertEquals(""" @startuml + title System Landscape\\nDescription + set separator none - title System Landscape - top to bottom direction - - skinparam { - arrowFontSize 10 - defaultTextAlignment center - wrapWidth 200 - maxMessageSize 100 - } - hide stereotype - - skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false - } - - rectangle "Group" <> as group1 { - skinparam RectangleBorderColor<> #cccccc - skinparam RectangleFontColor<> #cccccc - skinparam RectangleBorderStyle<> dashed - - rectangle "==Software System\\n[Software System]" <> as SoftwareSystem - } - - - @enduml"""; - - diagram = exporter.export(view); - assertEquals(expected, diagram.getDefinition()); - - workspace.getViews().getConfiguration().addProperty(StructurizrPlantUMLExporter.PLANTUML_SEQUENCE_DIAGRAM_PROPERTY, "true"); - - diagram = exporter.export(view); - assertEquals(expected, diagram.getDefinition()); + + + + rectangle "==Пользователь\\n[Person]" <> as Пользователь + + @enduml""", diagramDefinition); } @Test - public void testFont() { - Workspace workspace = new Workspace("Name", "Description"); + public void font() { + Workspace workspace = new Workspace("Name"); workspace.getModel().addPerson("User"); - SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "Description"); + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key"); view.addAllElements(); workspace.getViews().getConfiguration().getBranding().setFont(new Font("Courier")); Diagram diagram = new StructurizrPlantUMLExporter().export(view); - assertEquals("@startuml\nset separator none\n" + - "title System Landscape\n" + - "\n" + - "top to bottom direction\n" + - "\n" + - "skinparam {\n" + - " arrowFontSize 10\n" + - " defaultTextAlignment center\n" + - " wrapWidth 200\n" + - " maxMessageSize 100\n" + - " defaultFontName \"Courier\"\n" + - "}\n" + - "\n" + - "hide stereotype\n" + - "\n" + - "skinparam rectangle<> {\n" + - " BackgroundColor #dddddd\n" + - " FontColor #000000\n" + - " BorderColor #9a9a9a\n" + - " shadowing false\n" + - "}\n" + - "\n" + - "rectangle \"==User\\n[Person]\" <> as User\n" + - "\n" + - "@enduml", diagram.getDefinition().toString()); - - assertEquals("@startuml\nset separator none\n" + - "\n" + - "skinparam {\n" + - " shadowing false\n" + - " arrowFontSize 15\n" + - " defaultTextAlignment center\n" + - " wrapWidth 100\n" + - " maxMessageSize 100\n" + - " defaultFontName \"Courier\"\n" + - "}\n" + - "hide stereotype\n" + - "\n" + - "skinparam rectangle<<_transparent>> {\n" + - " BorderColor transparent\n" + - " BackgroundColor transparent\n" + - " FontColor transparent\n" + - "}\n" + - "\n" + - "skinparam rectangle<<1>> {\n" + - " BackgroundColor #dddddd\n" + - " FontColor #000000\n" + - " BorderColor #9a9a9a\n" + - "}\n" + - "rectangle \"==Element\" <<1>>\n" + - "\n" + - "\n" + - "@enduml", diagram.getLegend().getDefinition()); + assertTrue(diagram.getDefinition().contains(""" + + + rectangle "Group 1" <> as groupR3JvdXAgMQ== { + rectangle "==A\\n[Software System]" <> as A + } + + rectangle "Group 2" <> as groupR3JvdXAgMg== { + rectangle "==B\\n[Software System]" <> as B + } + + + A --> B <> : "1. Uses" + + @enduml""", diagram.getDefinition()); } @Test - public void dynamicView_SoftwareSystemScopedWithGroups() throws Exception { - Workspace workspace = new Workspace("Name", "Description"); + public void dynamicView_SoftwareSystemScopedWithGroups() { + Workspace workspace = new Workspace("Name"); SoftwareSystem softwareSystemA = workspace.getModel().addSoftwareSystem("A"); Container containerA = softwareSystemA.addContainer("A"); containerA.setGroup("Group 1"); @@ -749,18 +2034,112 @@ public void dynamicView_SoftwareSystemScopedWithGroups() throws Exception { containerB.setGroup("Group 2"); containerA.uses(containerB, "Uses"); - DynamicView view = workspace.getViews().createDynamicView(softwareSystemA, "key", "Description"); + DynamicView view = workspace.getViews().createDynamicView(softwareSystemA, "key"); view.add(containerA, containerB); StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(); Diagram diagram = exporter.export(view); - String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-software-system-scoped-with-groups.puml")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + @startuml + title A - Dynamic + + set separator none + top to bottom direction + hide stereotype + + + + rectangle "A\\n[Software System]" <> { + rectangle "Group 1" <> as groupR3JvdXAgMQ== { + rectangle "==A\\n[Container]" <> as A.A + } + + } + + rectangle "B\\n[Software System]" <> { + rectangle "Group 2" <> as groupR3JvdXAgMg== { + rectangle "==B\\n[Container]" <> as B.B + } + + } + + A.A --> B.B <> : "1. Uses" + + @enduml""", diagram.getDefinition()); } @Test - public void dynamicView_ContainerScopedWithGroups() throws Exception { - Workspace workspace = new Workspace("Name", "Description"); + public void dynamicView_ContainerScopedWithGroups() { + Workspace workspace = new Workspace("Name"); SoftwareSystem softwareSystemA = workspace.getModel().addSoftwareSystem("A"); Container containerA = softwareSystemA.addContainer("A"); Component componentA = containerA.addComponent("A"); @@ -771,91 +2150,107 @@ public void dynamicView_ContainerScopedWithGroups() throws Exception { componentB.setGroup("Group 2"); componentA.uses(componentB, "Uses"); - DynamicView view = workspace.getViews().createDynamicView(containerA, "key", "Description"); + DynamicView view = workspace.getViews().createDynamicView(containerA, "key"); view.add(componentA, componentB); StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(); Diagram diagram = exporter.export(view); - String expected = readFile(new File("./src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-container-scoped-with-groups.puml")); - assertEquals(expected, diagram.getDefinition()); - } - - @Test - public void test_writeContainerViewWithGroupedElements_WithAndWithoutAGroupSeparator() { - Workspace workspace = new Workspace("Name", ""); - SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", ""); - Container container1 = softwareSystem.addContainer("Container 1"); - container1.setGroup("Group 1"); - Container container2 = softwareSystem.addContainer("Container 2"); - container2.setGroup("Group 2"); - - ContainerView view = workspace.getViews().createContainerView(softwareSystem, "Containers", ""); - view.addAllElements(); - - String expectedResult = """ + assertEquals(""" @startuml + title A - Dynamic + set separator none - title Software System - Containers - top to bottom direction - - skinparam { - arrowFontSize 10 - defaultTextAlignment center - wrapWidth 200 - maxMessageSize 100 - } - hide stereotype - - skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false - } - skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false - } - skinparam rectangle<> { - BorderColor #9a9a9a - FontColor #9a9a9a - shadowing false + + + + rectangle "A\\n[Container]" <> { + rectangle "Group 1" <> as groupR3JvdXAgMQ== { + rectangle "==A\\n[Component]" <> as A.A.A + } + } - - rectangle "Software System\\n[Software System]" <> { - rectangle "Group 1" <> as group1 { - skinparam RectangleBorderColor<> #cccccc - skinparam RectangleFontColor<> #cccccc - skinparam RectangleBorderStyle<> dashed - - rectangle "==Container 1\\n[Container]" <> as SoftwareSystem.Container1 - } - - rectangle "Group 2" <> as group2 { - skinparam RectangleBorderColor<> #cccccc - skinparam RectangleFontColor<> #cccccc - skinparam RectangleBorderStyle<> dashed - - rectangle "==Container 2\\n[Container]" <> as SoftwareSystem.Container2 - } - + + rectangle "B\\n[Container]" <> { + rectangle "Group 2" <> as groupR3JvdXAgMg== { + rectangle "==B\\n[Component]" <> as B.B.B + } + } - - @enduml"""; - - StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(); - Diagram diagram = exporter.export(view); - assertEquals(expectedResult, diagram.getDefinition()); - - // this should be the same - workspace.getModel().addProperty("structurizr.groupSeparator", "/"); - exporter = new StructurizrPlantUMLExporter(); - diagram = exporter.export(view); - assertEquals(expectedResult, diagram.getDefinition()); + + A.A.A --> B.B.B <> : "1. Uses" + + @enduml""", diagram.getDefinition()); } @Test @@ -873,212 +2268,907 @@ public void deploymentView_WithGroups() { softwareSystemInstance.setGroup("Group 2"); infrastructureNode2.setGroup("Group 2"); - DeploymentView view = workspace.getViews().createDeploymentView("key", "Description"); + DeploymentView view = workspace.getViews().createDeploymentView("key"); view.add(infrastructureNode1); view.add(infrastructureNode2); view.add(softwareSystemInstance); - String expectedResult = """ + StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(); + Diagram diagram = exporter.export(view); + assertEquals(""" @startuml + title Deployment - Default + set separator none - title Deployment - Default - top to bottom direction - - skinparam { - arrowFontSize 10 - defaultTextAlignment center - wrapWidth 200 - maxMessageSize 100 - } - hide stereotype - - skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false - } - skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false - } - skinparam rectangle<> { - BackgroundColor #ffffff - FontColor #000000 - BorderColor #888888 - shadowing false - } - skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false - } - - rectangle "Group 1" <> as group1 { - skinparam RectangleBorderColor<> #cccccc - skinparam RectangleFontColor<> #cccccc - skinparam RectangleBorderStyle<> dashed - - rectangle "Server 1\\n[Deployment Node]" <> as Default.Server1 { - rectangle "Group 2" <> as group2 { - skinparam RectangleBorderColor<> #cccccc - skinparam RectangleFontColor<> #cccccc - skinparam RectangleBorderStyle<> dashed - - rectangle "==Infrastructure Node 2\\n[Infrastructure Node]" <> as Default.Server1.InfrastructureNode2 - rectangle "==Software System\\n[Software System]" <> as Default.Server1.SoftwareSystem_1 + + + + rectangle "Group 1" <> as groupR3JvdXAgMQ== { + rectangle "Server 1\\n[Deployment Node]" <> as Default.Server1 { + rectangle "Group 2" <> as groupR3JvdXAgMg== { + rectangle "==Infrastructure Node 2\\n[Infrastructure Node]" <> as Default.Server1.InfrastructureNode2 + rectangle "==Software System\\n[Software System]" <> as Default.Server1.SoftwareSystem_1 } - - rectangle "==Infrastructure Node 1\\n[Infrastructure Node]" <> as Default.Server1.InfrastructureNode1 + + rectangle "==Infrastructure Node 1\\n[Infrastructure Node]" <> as Default.Server1.InfrastructureNode1 } - + } - - @enduml"""; - - StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(); - Diagram diagram = exporter.export(view); - assertEquals(expectedResult, diagram.getDefinition()); - - // this should be the same - workspace.getModel().addProperty("structurizr.groupSeparator", "/"); - exporter = new StructurizrPlantUMLExporter(); - diagram = exporter.export(view); - assertEquals(expectedResult, diagram.getDefinition()); + + @enduml""", diagram.getDefinition()); } @Test - public void test_ElementInstanceUrl() { - Workspace workspace = new Workspace("Name", "Description"); - SoftwareSystem a = workspace.getModel().addSoftwareSystem("A"); - a.setUrl("https://example.com/url1"); - SoftwareSystemInstance aInstance = workspace.getModel().addDeploymentNode("Node").add(a); - - DeploymentView view = workspace.getViews().createDeploymentView("deployment", "Default"); - view.add(aInstance); + void light_group() { + Workspace workspace = new Workspace("Name"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name"); + softwareSystem.setGroup("Name"); - assertEquals(""" -@startuml -set separator none -title Deployment - Default - -top to bottom direction - -skinparam { - arrowFontSize 10 - defaultTextAlignment center - wrapWidth 200 - maxMessageSize 100 -} - -hide stereotype - -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BackgroundColor #ffffff - FontColor #000000 - BorderColor #888888 - shadowing false -} - -rectangle "Node\\n[Deployment Node]" <> as Default.Node { - rectangle "==A\\n[Software System]" <> as Default.Node.A_1 [[https://example.com/url1]] -} - -@enduml""", new StructurizrPlantUMLExporter().export(view).getDefinition()); + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key"); + view.add(softwareSystem); - aInstance.setUrl("https://example.com/url2"); + StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(); + Diagram diagram = exporter.export(view); assertEquals(""" -@startuml -set separator none -title Deployment - Default - -top to bottom direction - -skinparam { - arrowFontSize 10 - defaultTextAlignment center - wrapWidth 200 - maxMessageSize 100 -} - -hide stereotype - -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BackgroundColor #ffffff - FontColor #000000 - BorderColor #888888 - shadowing false -} - -rectangle "Node\\n[Deployment Node]" <> as Default.Node { - rectangle "==A\\n[Software System]" <> as Default.Node.A_1 [[https://example.com/url2]] -} - -@enduml""", new StructurizrPlantUMLExporter().export(view).getDefinition()); - + @startuml + title System Landscape + + set separator none + top to bottom direction + hide stereotype + + + + rectangle "Name" <> as groupTmFtZQ== { + rectangle "==Name\\n[Software System]" <> as Name + } + + + @enduml""", diagram.getDefinition()); } @Test - void groupAndSoftwareSystemNameAreTheSame() { - Workspace workspace = new Workspace("Name", "Description"); + void dark_group() { + Workspace workspace = new Workspace("Name"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name"); softwareSystem.setGroup("Name"); - SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key", "Description"); + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key"); view.add(softwareSystem); - StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(); + StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(ColorScheme.Dark); Diagram diagram = exporter.export(view); assertEquals(""" @startuml + title System Landscape + set separator none - title System Landscape - top to bottom direction - - skinparam { - arrowFontSize 10 - defaultTextAlignment center - wrapWidth 200 - maxMessageSize 100 + hide stereotype + + + + rectangle "Name" <> as groupTmFtZQ== { + rectangle "==Name\\n[Software System]" <> as Name } - + + + @enduml""", diagram.getDefinition()); + } + + @Test + @Tag("IntegrationTest") + public void amazonWebServicesExample_Light() throws Exception { + Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/amazon-web-services.json")); + ThemeUtils.loadThemes(workspace); + + StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(ColorScheme.Light); + Collection diagrams = exporter.export(workspace); + assertEquals(1, diagrams.size()); + + Diagram diagram = diagrams.stream().findFirst().get(); + assertEquals(""" + @startuml + title X - Deployment - Live + + set separator none + left to right direction + skinparam ranksep 60 + skinparam nodesep 30 hide stereotype - - skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false + + + + rectangle "Amazon Web Services\\n[Deployment Node]\\n\\n" <> as Live.AmazonWebServices { + rectangle "US-East-1\\n[Deployment Node]\\n\\n" <> as Live.AmazonWebServices.USEast1 { + rectangle "Autoscaling group\\n[Deployment Node]\\n\\n" <> as Live.AmazonWebServices.USEast1.Autoscalinggroup { + rectangle "Amazon EC2 - Ubuntu server\\n[Deployment Node]\\n\\n" <> as Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2Ubuntuserver { + rectangle "==Web Application\\n[Container: Java and Spring Boot]" <> as Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2Ubuntuserver.WebApplication_1 + } + + } + + rectangle "Amazon RDS\\n[Deployment Node]\\n\\n" <> as Live.AmazonWebServices.USEast1.AmazonRDS { + rectangle "MySQL\\n[Deployment Node]\\n\\n" <> as Live.AmazonWebServices.USEast1.AmazonRDS.MySQL { + database "==Database Schema\\n[Container]" <> as Live.AmazonWebServices.USEast1.AmazonRDS.MySQL.DatabaseSchema_1 + } + + } + + rectangle "==DNS router\\n[Infrastructure Node: Route 53]\\n\\n\\n\\nRoutes incoming requests based upon domain name." <> as Live.AmazonWebServices.USEast1.DNSrouter + rectangle "==Load Balancer\\n[Infrastructure Node: Elastic Load Balancer]\\n\\n\\n\\nAutomatically distributes incoming application traffic." <> as Live.AmazonWebServices.USEast1.LoadBalancer + } + } - - rectangle "Name" <> as group1 { - skinparam RectangleBorderColor<> #cccccc - skinparam RectangleFontColor<> #cccccc - skinparam RectangleBorderStyle<> dashed - - rectangle "==Name\\n[Software System]" <> as Name + + Live.AmazonWebServices.USEast1.LoadBalancer --> Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2Ubuntuserver.WebApplication_1 <> : "Forwards requests to\\n[HTTPS]" + Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2Ubuntuserver.WebApplication_1 --> Live.AmazonWebServices.USEast1.AmazonRDS.MySQL.DatabaseSchema_1 <> : "Reads from and writes to\\n[MySQL Protocol/SSL]" + Live.AmazonWebServices.USEast1.DNSrouter --> Live.AmazonWebServices.USEast1.LoadBalancer <> : "Forwards requests to\\n[HTTPS]" + + @enduml""", diagram.getDefinition()); + + assertEquals(""" + @startuml + + set separator none + hide stereotype + + + + rectangle "==Amazon Web Services - Auto Scaling\\n\\n" <> + + rectangle "==Amazon Web Services - Cloud\\n\\n" <> + + rectangle "==Amazon Web Services - EC2\\n\\n" <> + + rectangle "==Amazon Web Services - Elastic Load Balancing\\n\\n" <> + + rectangle "==Amazon Web Services - RDS\\n\\n" <> + + rectangle "==Amazon Web Services - RDS MySQL instance\\n\\n" <> + + rectangle "==Amazon Web Services - Region\\n\\n" <> + + rectangle "==Amazon Web Services - Route 53\\n\\n" <> + + rectangle "==Application" <> + + database "==Database" <> + + rectangle "." <<.Element-Transparent>> as 1 + 1 --> 1 <> : "Relationship" + + @enduml""", diagram.getLegend().getDefinition()); + } + + @Test + @Tag("IntegrationTest") + public void amazonWebServicesExample_Dark() throws Exception { + Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/amazon-web-services.json")); + ThemeUtils.loadThemes(workspace); + + StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(ColorScheme.Dark); + Collection diagrams = exporter.export(workspace); + assertEquals(1, diagrams.size()); + + Diagram diagram = diagrams.stream().findFirst().get(); + assertEquals(""" + @startuml + title X - Deployment - Live + + set separator none + left to right direction + skinparam ranksep 60 + skinparam nodesep 30 + hide stereotype + + + + rectangle "Amazon Web Services\\n[Deployment Node]\\n\\n" <> as Live.AmazonWebServices { + rectangle "US-East-1\\n[Deployment Node]\\n\\n" <> as Live.AmazonWebServices.USEast1 { + rectangle "Autoscaling group\\n[Deployment Node]\\n\\n" <> as Live.AmazonWebServices.USEast1.Autoscalinggroup { + rectangle "Amazon EC2 - Ubuntu server\\n[Deployment Node]\\n\\n" <> as Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2Ubuntuserver { + rectangle "==Web Application\\n[Container: Java and Spring Boot]" <> as Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2Ubuntuserver.WebApplication_1 + } + + } + + rectangle "Amazon RDS\\n[Deployment Node]\\n\\n" <> as Live.AmazonWebServices.USEast1.AmazonRDS { + rectangle "MySQL\\n[Deployment Node]\\n\\n" <> as Live.AmazonWebServices.USEast1.AmazonRDS.MySQL { + database "==Database Schema\\n[Container]" <> as Live.AmazonWebServices.USEast1.AmazonRDS.MySQL.DatabaseSchema_1 + } + + } + + rectangle "==DNS router\\n[Infrastructure Node: Route 53]\\n\\n\\n\\nRoutes incoming requests based upon domain name." <> as Live.AmazonWebServices.USEast1.DNSrouter + rectangle "==Load Balancer\\n[Infrastructure Node: Elastic Load Balancer]\\n\\n\\n\\nAutomatically distributes incoming application traffic." <> as Live.AmazonWebServices.USEast1.LoadBalancer + } + } - - + + Live.AmazonWebServices.USEast1.LoadBalancer --> Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2Ubuntuserver.WebApplication_1 <> : "Forwards requests to\\n[HTTPS]" + Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2Ubuntuserver.WebApplication_1 --> Live.AmazonWebServices.USEast1.AmazonRDS.MySQL.DatabaseSchema_1 <> : "Reads from and writes to\\n[MySQL Protocol/SSL]" + Live.AmazonWebServices.USEast1.DNSrouter --> Live.AmazonWebServices.USEast1.LoadBalancer <> : "Forwards requests to\\n[HTTPS]" + @enduml""", diagram.getDefinition()); + + assertEquals(""" + @startuml + + set separator none + hide stereotype + + + + rectangle "==Amazon Web Services - Auto Scaling\\n\\n" <> + + rectangle "==Amazon Web Services - Cloud\\n\\n" <> + + rectangle "==Amazon Web Services - EC2\\n\\n" <> + + rectangle "==Amazon Web Services - Elastic Load Balancing\\n\\n" <> + + rectangle "==Amazon Web Services - RDS\\n\\n" <> + + rectangle "==Amazon Web Services - RDS MySQL instance\\n\\n" <> + + rectangle "==Amazon Web Services - Region\\n\\n" <> + + rectangle "==Amazon Web Services - Route 53\\n\\n" <> + + rectangle "==Application" <> + + database "==Database" <> + + rectangle "." <<.Element-Transparent>> as 1 + 1 --> 1 <> : "Relationship" + + @enduml""", diagram.getLegend().getDefinition()); } } \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Components.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Components.puml deleted file mode 100644 index 7bce7a631..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Components.puml +++ /dev/null @@ -1,52 +0,0 @@ -@startuml -set separator none -title Internet Banking System - API Application - Components - -top to bottom direction - -!include -!include -!include -!include - -AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") -AddElementTag("Component", $bgColor="#85bbf0", $borderColor="#5d82a8", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="solid") -AddElementTag("Container,Mobile App", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") -AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") -AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") - -AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") - -AddBoundaryTag("Container", $bgColor="#ffffff", $borderColor="#2e6295", $fontColor="#2e6295", $shadowing="", $borderStyle="solid") - -System(MainframeBankingSystem, "Mainframe Banking System", $descr="Stores all of the core banking information about customers, accounts, transactions, etc.", $tags="Software System,Existing System", $link="") -System(EmailSystem, "E-mail System", $descr="The internal Microsoft Exchange e-mail system.", $tags="Software System,Existing System", $link="") -Container(InternetBankingSystem.SinglePageApplication, "Single-Page Application", $techn="JavaScript and Angular", $descr="Provides all of the Internet banking functionality to customers via their web browser.", $tags="Container,Web Browser", $link="") -Container(InternetBankingSystem.MobileApp, "Mobile App", $techn="Xamarin", $descr="Provides a limited subset of the Internet banking functionality to customers via their mobile device.", $tags="Container,Mobile App", $link="") -ContainerDb(InternetBankingSystem.Database, "Database", $techn="Oracle Database Schema", $descr="Stores user registration information, hashed authentication credentials, access logs, etc.", $tags="Container,Database", $link="") - -Container_Boundary("InternetBankingSystem.APIApplication_boundary", "API Application", $tags="Container") { - Component(InternetBankingSystem.APIApplication.SignInController, "Sign In Controller", $techn="Spring MVC Rest Controller", $descr="Allows users to sign in to the Internet Banking System.", $tags="Component", $link="") - Component(InternetBankingSystem.APIApplication.AccountsSummaryController, "Accounts Summary Controller", $techn="Spring MVC Rest Controller", $descr="Provides customers with a summary of their bank accounts.", $tags="Component", $link="") - Component(InternetBankingSystem.APIApplication.ResetPasswordController, "Reset Password Controller", $techn="Spring MVC Rest Controller", $descr="Allows users to reset their passwords with a single use URL.", $tags="Component", $link="") - Component(InternetBankingSystem.APIApplication.SecurityComponent, "Security Component", $techn="Spring Bean", $descr="Provides functionality related to signing in, changing passwords, etc.", $tags="Component", $link="") - Component(InternetBankingSystem.APIApplication.MainframeBankingSystemFacade, "Mainframe Banking System Facade", $techn="Spring Bean", $descr="A facade onto the mainframe banking system.", $tags="Component", $link="") - Component(InternetBankingSystem.APIApplication.EmailComponent, "E-mail Component", $techn="Spring Bean", $descr="Sends e-mails to users.", $tags="Component", $link="") -} - -Rel(InternetBankingSystem.SinglePageApplication, InternetBankingSystem.APIApplication.SignInController, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") -Rel(InternetBankingSystem.SinglePageApplication, InternetBankingSystem.APIApplication.AccountsSummaryController, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") -Rel(InternetBankingSystem.SinglePageApplication, InternetBankingSystem.APIApplication.ResetPasswordController, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") -Rel(InternetBankingSystem.MobileApp, InternetBankingSystem.APIApplication.SignInController, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") -Rel(InternetBankingSystem.MobileApp, InternetBankingSystem.APIApplication.AccountsSummaryController, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") -Rel(InternetBankingSystem.MobileApp, InternetBankingSystem.APIApplication.ResetPasswordController, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") -Rel(InternetBankingSystem.APIApplication.SignInController, InternetBankingSystem.APIApplication.SecurityComponent, "Uses", $techn="", $tags="Relationship", $link="") -Rel(InternetBankingSystem.APIApplication.AccountsSummaryController, InternetBankingSystem.APIApplication.MainframeBankingSystemFacade, "Uses", $techn="", $tags="Relationship", $link="") -Rel(InternetBankingSystem.APIApplication.ResetPasswordController, InternetBankingSystem.APIApplication.SecurityComponent, "Uses", $techn="", $tags="Relationship", $link="") -Rel(InternetBankingSystem.APIApplication.ResetPasswordController, InternetBankingSystem.APIApplication.EmailComponent, "Uses", $techn="", $tags="Relationship", $link="") -Rel(InternetBankingSystem.APIApplication.SecurityComponent, InternetBankingSystem.Database, "Reads from and writes to", $techn="SQL/TCP", $tags="Relationship", $link="") -Rel(InternetBankingSystem.APIApplication.MainframeBankingSystemFacade, MainframeBankingSystem, "Makes API calls to", $techn="XML/HTTPS", $tags="Relationship", $link="") -Rel(InternetBankingSystem.APIApplication.EmailComponent, EmailSystem, "Sends e-mail using", $techn="", $tags="Relationship", $link="") - -SHOW_LEGEND(true) -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Containers.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Containers.puml deleted file mode 100644 index 789bfdee0..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-Containers.puml +++ /dev/null @@ -1,46 +0,0 @@ -@startuml -set separator none -title Internet Banking System - Containers - -top to bottom direction - -!include -!include -!include - -AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") -AddElementTag("Container,Mobile App", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") -AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") -AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") -AddElementTag("Container", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") -AddElementTag("Person,Customer", $bgColor="#08427b", $borderColor="#052e56", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") - -AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") - -AddBoundaryTag("Software System", $bgColor="#ffffff", $borderColor="#0b4884", $fontColor="#0b4884", $shadowing="", $borderStyle="solid") - -Person(PersonalBankingCustomer, "Personal Banking Customer", $descr="A customer of the bank, with personal bank accounts.", $tags="Person,Customer", $link="") -System(MainframeBankingSystem, "Mainframe Banking System", $descr="Stores all of the core banking information about customers, accounts, transactions, etc.", $tags="Software System,Existing System", $link="") -System(EmailSystem, "E-mail System", $descr="The internal Microsoft Exchange e-mail system.", $tags="Software System,Existing System", $link="") - -System_Boundary("InternetBankingSystem_boundary", "Internet Banking System", $tags="Software System") { - Container(InternetBankingSystem.WebApplication, "Web Application", $techn="Java and Spring MVC", $descr="Delivers the static content and the Internet banking single page application.", $tags="Container", $link="") - Container(InternetBankingSystem.APIApplication, "API Application", $techn="Java and Spring MVC", $descr="Provides Internet banking functionality via a JSON/HTTPS API.", $tags="Container", $link="") - ContainerDb(InternetBankingSystem.Database, "Database", $techn="Oracle Database Schema", $descr="Stores user registration information, hashed authentication credentials, access logs, etc.", $tags="Container,Database", $link="") - Container(InternetBankingSystem.SinglePageApplication, "Single-Page Application", $techn="JavaScript and Angular", $descr="Provides all of the Internet banking functionality to customers via their web browser.", $tags="Container,Web Browser", $link="") - Container(InternetBankingSystem.MobileApp, "Mobile App", $techn="Xamarin", $descr="Provides a limited subset of the Internet banking functionality to customers via their mobile device.", $tags="Container,Mobile App", $link="") -} - -Rel(EmailSystem, PersonalBankingCustomer, "Sends e-mails to", $techn="", $tags="Relationship", $link="") -Rel(PersonalBankingCustomer, InternetBankingSystem.WebApplication, "Visits bigbank.com/ib using", $techn="HTTPS", $tags="Relationship", $link="") -Rel(PersonalBankingCustomer, InternetBankingSystem.SinglePageApplication, "Views account balances, and makes payments using", $techn="", $tags="Relationship", $link="") -Rel(PersonalBankingCustomer, InternetBankingSystem.MobileApp, "Views account balances, and makes payments using", $techn="", $tags="Relationship", $link="") -Rel(InternetBankingSystem.WebApplication, InternetBankingSystem.SinglePageApplication, "Delivers to the customer's web browser", $techn="", $tags="Relationship", $link="") -Rel(InternetBankingSystem.SinglePageApplication, InternetBankingSystem.APIApplication, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") -Rel(InternetBankingSystem.MobileApp, InternetBankingSystem.APIApplication, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") -Rel(InternetBankingSystem.APIApplication, InternetBankingSystem.Database, "Reads from and writes to", $techn="SQL/TCP", $tags="Relationship", $link="") -Rel(InternetBankingSystem.APIApplication, MainframeBankingSystem, "Makes API calls to", $techn="XML/HTTPS", $tags="Relationship", $link="") -Rel(InternetBankingSystem.APIApplication, EmailSystem, "Sends e-mail using", $techn="", $tags="Relationship", $link="") - -SHOW_LEGEND(true) -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-DevelopmentDeployment.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-DevelopmentDeployment.puml deleted file mode 100644 index 0158ada25..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-DevelopmentDeployment.puml +++ /dev/null @@ -1,55 +0,0 @@ -@startuml -set separator none -title Internet Banking System - Deployment - Development - -top to bottom direction - -!include -!include -!include -!include - -AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") -AddElementTag("Element", $bgColor="#ffffff", $borderColor="#888888", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="solid") -AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") -AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") -AddElementTag("Container", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") - -AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") - -Deployment_Node(Development.DeveloperLaptop, "Developer Laptop", $type="Microsoft Windows 10 or Apple macOS", $descr="", $tags="Element", $link="") { - Deployment_Node(Development.DeveloperLaptop.WebBrowser, "Web Browser", $type="Chrome, Firefox, Safari, or Edge", $descr="", $tags="Element", $link="") { - Container(Development.DeveloperLaptop.WebBrowser.SinglePageApplication_1, "Single-Page Application", $techn="JavaScript and Angular", $descr="Provides all of the Internet banking functionality to customers via their web browser.", $tags="Container,Web Browser", $link="") - } - - Deployment_Node(Development.DeveloperLaptop.DockerContainerWebServer, "Docker Container - Web Server", $type="Docker", $descr="", $tags="Element", $link="") { - Deployment_Node(Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat, "Apache Tomcat", $type="Apache Tomcat 8.x", $descr="", $tags="Element", $link="") { - Container(Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.WebApplication_1, "Web Application", $techn="Java and Spring MVC", $descr="Delivers the static content and the Internet banking single page application.", $tags="Container", $link="") - Container(Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.APIApplication_1, "API Application", $techn="Java and Spring MVC", $descr="Provides Internet banking functionality via a JSON/HTTPS API.", $tags="Container", $link="") - } - - } - - Deployment_Node(Development.DeveloperLaptop.DockerContainerDatabaseServer, "Docker Container - Database Server", $type="Docker", $descr="", $tags="Element", $link="") { - Deployment_Node(Development.DeveloperLaptop.DockerContainerDatabaseServer.DatabaseServer, "Database Server", $type="Oracle 12c", $descr="", $tags="Element", $link="") { - ContainerDb(Development.DeveloperLaptop.DockerContainerDatabaseServer.DatabaseServer.Database_1, "Database", $techn="Oracle Database Schema", $descr="Stores user registration information, hashed authentication credentials, access logs, etc.", $tags="Container,Database", $link="") - } - - } - -} - -Deployment_Node(Development.BigBankplc, "Big Bank plc", $type="Big Bank plc data center", $descr="", $tags="Element", $link="") { - Deployment_Node(Development.BigBankplc.bigbankdev001, "bigbank-dev001", $type="", $descr="", $tags="Element", $link="") { - System(Development.BigBankplc.bigbankdev001.MainframeBankingSystem_1, "Mainframe Banking System", $descr="Stores all of the core banking information about customers, accounts, transactions, etc.", $tags="Software System,Existing System", $link="") - } - -} - -Rel(Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.WebApplication_1, Development.DeveloperLaptop.WebBrowser.SinglePageApplication_1, "Delivers to the customer's web browser", $techn="", $tags="Relationship", $link="") -Rel(Development.DeveloperLaptop.WebBrowser.SinglePageApplication_1, Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.APIApplication_1, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") -Rel(Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.APIApplication_1, Development.DeveloperLaptop.DockerContainerDatabaseServer.DatabaseServer.Database_1, "Reads from and writes to", $techn="SQL/TCP", $tags="Relationship", $link="") -Rel(Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.APIApplication_1, Development.BigBankplc.bigbankdev001.MainframeBankingSystem_1, "Makes API calls to", $techn="XML/HTTPS", $tags="Relationship", $link="") - -SHOW_LEGEND(true) -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-LiveDeployment.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-LiveDeployment.puml deleted file mode 100644 index 177f25979..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-LiveDeployment.puml +++ /dev/null @@ -1,77 +0,0 @@ -@startuml -set separator none -title Internet Banking System - Deployment - Live - -top to bottom direction - -!include -!include -!include -!include - -AddElementTag("Failover", $bgColor="#ffffff", $borderColor="#888888", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="solid") -AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") -AddElementTag("Element", $bgColor="#ffffff", $borderColor="#888888", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="solid") -AddElementTag("Container,Mobile App", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") -AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") -AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") -AddElementTag("Container", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") - -AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") - -Deployment_Node(Live.Customersmobiledevice, "Customer's mobile device", $type="Apple iOS or Android", $descr="", $tags="Element", $link="") { - Container(Live.Customersmobiledevice.MobileApp_1, "Mobile App", $techn="Xamarin", $descr="Provides a limited subset of the Internet banking functionality to customers via their mobile device.", $tags="Container,Mobile App", $link="") -} - -Deployment_Node(Live.Customerscomputer, "Customer's computer", $type="Microsoft Windows or Apple macOS", $descr="", $tags="Element", $link="") { - Deployment_Node(Live.Customerscomputer.WebBrowser, "Web Browser", $type="Chrome, Firefox, Safari, or Edge", $descr="", $tags="Element", $link="") { - Container(Live.Customerscomputer.WebBrowser.SinglePageApplication_1, "Single-Page Application", $techn="JavaScript and Angular", $descr="Provides all of the Internet banking functionality to customers via their web browser.", $tags="Container,Web Browser", $link="") - } - -} - -Deployment_Node(Live.BigBankplc, "Big Bank plc", $type="Big Bank plc data center", $descr="", $tags="Element", $link="") { - Deployment_Node(Live.BigBankplc.bigbankweb, "bigbank-web*** (x4)", $type="Ubuntu 16.04 LTS", $descr="", $tags="Element", $link="") { - Deployment_Node(Live.BigBankplc.bigbankweb.ApacheTomcat, "Apache Tomcat", $type="Apache Tomcat 8.x", $descr="", $tags="Element", $link="") { - Container(Live.BigBankplc.bigbankweb.ApacheTomcat.WebApplication_1, "Web Application", $techn="Java and Spring MVC", $descr="Delivers the static content and the Internet banking single page application.", $tags="Container", $link="") - } - - } - - Deployment_Node(Live.BigBankplc.bigbankapi, "bigbank-api*** (x8)", $type="Ubuntu 16.04 LTS", $descr="", $tags="Element", $link="") { - Deployment_Node(Live.BigBankplc.bigbankapi.ApacheTomcat, "Apache Tomcat", $type="Apache Tomcat 8.x", $descr="", $tags="Element", $link="") { - Container(Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1, "API Application", $techn="Java and Spring MVC", $descr="Provides Internet banking functionality via a JSON/HTTPS API.", $tags="Container", $link="") - } - - } - - Deployment_Node(Live.BigBankplc.bigbankdb01, "bigbank-db01", $type="Ubuntu 16.04 LTS", $descr="", $tags="Element", $link="") { - Deployment_Node(Live.BigBankplc.bigbankdb01.OraclePrimary, "Oracle - Primary", $type="Oracle 12c", $descr="", $tags="Element", $link="") { - ContainerDb(Live.BigBankplc.bigbankdb01.OraclePrimary.Database_1, "Database", $techn="Oracle Database Schema", $descr="Stores user registration information, hashed authentication credentials, access logs, etc.", $tags="Container,Database", $link="") - } - - } - - Deployment_Node(Live.BigBankplc.bigbankdb02, "bigbank-db02", $type="Ubuntu 16.04 LTS", $descr="", $tags="Failover", $link="") { - Deployment_Node(Live.BigBankplc.bigbankdb02.OracleSecondary, "Oracle - Secondary", $type="Oracle 12c", $descr="", $tags="Failover", $link="") { - ContainerDb(Live.BigBankplc.bigbankdb02.OracleSecondary.Database_1, "Database", $techn="Oracle Database Schema", $descr="Stores user registration information, hashed authentication credentials, access logs, etc.", $tags="Container,Database", $link="") - } - - } - - Deployment_Node(Live.BigBankplc.bigbankprod001, "bigbank-prod001", $type="", $descr="", $tags="Element", $link="") { - System(Live.BigBankplc.bigbankprod001.MainframeBankingSystem_1, "Mainframe Banking System", $descr="Stores all of the core banking information about customers, accounts, transactions, etc.", $tags="Software System,Existing System", $link="") - } - -} - -Rel(Live.BigBankplc.bigbankweb.ApacheTomcat.WebApplication_1, Live.Customerscomputer.WebBrowser.SinglePageApplication_1, "Delivers to the customer's web browser", $techn="", $tags="Relationship", $link="") -Rel(Live.Customersmobiledevice.MobileApp_1, Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") -Rel(Live.Customerscomputer.WebBrowser.SinglePageApplication_1, Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") -Rel(Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1, Live.BigBankplc.bigbankdb01.OraclePrimary.Database_1, "Reads from and writes to", $techn="SQL/TCP", $tags="Relationship", $link="") -Rel(Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1, Live.BigBankplc.bigbankdb02.OracleSecondary.Database_1, "Reads from and writes to", $techn="SQL/TCP", $tags="Relationship", $link="") -Rel(Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1, Live.BigBankplc.bigbankprod001.MainframeBankingSystem_1, "Makes API calls to", $techn="XML/HTTPS", $tags="Relationship", $link="") -Rel(Live.BigBankplc.bigbankdb01.OraclePrimary, Live.BigBankplc.bigbankdb02.OracleSecondary, "Replicates data to", $techn="", $tags="Relationship", $link="") - -SHOW_LEGEND(true) -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn-sequence.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn-sequence.puml deleted file mode 100644 index 306185a43..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn-sequence.puml +++ /dev/null @@ -1,26 +0,0 @@ -@startuml -set separator none -title API Application - Dynamic - -!include - -AddElementTag("Component", $bgColor="#85bbf0", $borderColor="#5d82a8", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="solid") -AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") -AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") - -AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") - -Container(InternetBankingSystem.SinglePageApplication, "Single-Page Application", $techn="JavaScript and Angular", $descr="Provides all of the Internet banking functionality to customers via their web browser.", $tags="Container,Web Browser", $link="") -Component(InternetBankingSystem.APIApplication.SignInController, "Sign In Controller", $techn="Spring MVC Rest Controller", $descr="Allows users to sign in to the Internet Banking System.", $tags="Component", $link="") -Component(InternetBankingSystem.APIApplication.SecurityComponent, "Security Component", $techn="Spring Bean", $descr="Provides functionality related to signing in, changing passwords, etc.", $tags="Component", $link="") -ContainerDb(InternetBankingSystem.Database, "Database", $techn="Oracle Database Schema", $descr="Stores user registration information, hashed authentication credentials, access logs, etc.", $tags="Container,Database", $link="") - -Rel(InternetBankingSystem.SinglePageApplication, InternetBankingSystem.APIApplication.SignInController, "Submits credentials to", $techn="JSON/HTTPS", $tags="Relationship", $link="") -Rel(InternetBankingSystem.APIApplication.SignInController, InternetBankingSystem.APIApplication.SecurityComponent, "Validates credentials using", $techn="", $tags="Relationship", $link="") -Rel(InternetBankingSystem.APIApplication.SecurityComponent, InternetBankingSystem.Database, "select * from users where username = ?", $techn="SQL/TCP", $tags="Relationship", $link="") -Rel(InternetBankingSystem.Database, InternetBankingSystem.APIApplication.SecurityComponent, "Returns user data to", $techn="SQL/TCP", $tags="Relationship", $link="") -Rel(InternetBankingSystem.APIApplication.SecurityComponent, InternetBankingSystem.APIApplication.SignInController, "Returns true if the hashed password matches", $techn="", $tags="Relationship", $link="") -Rel(InternetBankingSystem.APIApplication.SignInController, InternetBankingSystem.SinglePageApplication, "Sends back an authentication token to", $techn="JSON/HTTPS", $tags="Relationship", $link="") - -SHOW_LEGEND(true) -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn.puml deleted file mode 100644 index 2ed68bacd..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SignIn.puml +++ /dev/null @@ -1,36 +0,0 @@ -@startuml -set separator none -title API Application - Dynamic - -top to bottom direction - -!include -!include -!include -!include - -AddElementTag("Component", $bgColor="#85bbf0", $borderColor="#5d82a8", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="solid") -AddElementTag("Container,Database", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") -AddElementTag("Container,Web Browser", $bgColor="#438dd5", $borderColor="#2e6295", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") - -AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") - -AddBoundaryTag("Container", $bgColor="#ffffff", $borderColor="#2e6295", $fontColor="#2e6295", $shadowing="", $borderStyle="solid") - -Container_Boundary("InternetBankingSystem.APIApplication_boundary", "API Application", $tags="Container") { - Component(InternetBankingSystem.APIApplication.SignInController, "Sign In Controller", $techn="Spring MVC Rest Controller", $descr="Allows users to sign in to the Internet Banking System.", $tags="Component", $link="") - Component(InternetBankingSystem.APIApplication.SecurityComponent, "Security Component", $techn="Spring Bean", $descr="Provides functionality related to signing in, changing passwords, etc.", $tags="Component", $link="") -} - -Container(InternetBankingSystem.SinglePageApplication, "Single-Page Application", $techn="JavaScript and Angular", $descr="Provides all of the Internet banking functionality to customers via their web browser.", $tags="Container,Web Browser", $link="") -ContainerDb(InternetBankingSystem.Database, "Database", $techn="Oracle Database Schema", $descr="Stores user registration information, hashed authentication credentials, access logs, etc.", $tags="Container,Database", $link="") - -Rel(InternetBankingSystem.SinglePageApplication, InternetBankingSystem.APIApplication.SignInController, "1. Submits credentials to", $techn="JSON/HTTPS", $tags="Relationship", $link="") -Rel(InternetBankingSystem.APIApplication.SignInController, InternetBankingSystem.APIApplication.SecurityComponent, "2. Validates credentials using", $techn="", $tags="Relationship", $link="") -Rel(InternetBankingSystem.APIApplication.SecurityComponent, InternetBankingSystem.Database, "3. select * from users where username = ?", $techn="SQL/TCP", $tags="Relationship", $link="") -Rel(InternetBankingSystem.Database, InternetBankingSystem.APIApplication.SecurityComponent, "4. Returns user data to", $techn="SQL/TCP", $tags="Relationship", $link="") -Rel(InternetBankingSystem.APIApplication.SecurityComponent, InternetBankingSystem.APIApplication.SignInController, "5. Returns true if the hashed password matches", $techn="", $tags="Relationship", $link="") -Rel(InternetBankingSystem.APIApplication.SignInController, InternetBankingSystem.SinglePageApplication, "6. Sends back an authentication token to", $techn="JSON/HTTPS", $tags="Relationship", $link="") - -SHOW_LEGEND(true) -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemContext.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemContext.puml deleted file mode 100644 index 5151c41db..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemContext.puml +++ /dev/null @@ -1,31 +0,0 @@ -@startuml -set separator none -title Internet Banking System - System Context - -top to bottom direction - -!include -!include - -AddElementTag("Software System", $bgColor="#1168bd", $borderColor="#0b4884", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") -AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") -AddElementTag("Person,Customer", $bgColor="#08427b", $borderColor="#052e56", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") - -AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") - -AddBoundaryTag("Big Bank plc", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") -Boundary(group_1, "Big Bank plc", $tags="Big Bank plc") { - System(MainframeBankingSystem, "Mainframe Banking System", $descr="Stores all of the core banking information about customers, accounts, transactions, etc.", $tags="Software System,Existing System", $link="") - System(EmailSystem, "E-mail System", $descr="The internal Microsoft Exchange e-mail system.", $tags="Software System,Existing System", $link="") - System(InternetBankingSystem, "Internet Banking System", $descr="Allows customers to view information about their bank accounts, and make payments.", $tags="Software System", $link="") -} - -Person(PersonalBankingCustomer, "Personal Banking Customer", $descr="A customer of the bank, with personal bank accounts.", $tags="Person,Customer", $link="") - -Rel(PersonalBankingCustomer, InternetBankingSystem, "Views account balances, and makes payments using", $techn="", $tags="Relationship", $link="") -Rel(InternetBankingSystem, MainframeBankingSystem, "Gets account information from, and makes payments using", $techn="", $tags="Relationship", $link="") -Rel(InternetBankingSystem, EmailSystem, "Sends e-mail using", $techn="", $tags="Relationship", $link="") -Rel(EmailSystem, PersonalBankingCustomer, "Sends e-mails to", $techn="", $tags="Relationship", $link="") - -SHOW_LEGEND(true) -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemLandscape.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemLandscape.puml deleted file mode 100644 index c49923d47..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/36141-SystemLandscape.puml +++ /dev/null @@ -1,40 +0,0 @@ -@startuml -set separator none -title System Landscape - -top to bottom direction - -!include -!include - -AddElementTag("Software System", $bgColor="#1168bd", $borderColor="#0b4884", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") -AddElementTag("Person,Bank Staff", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") -AddElementTag("Software System,Existing System", $bgColor="#999999", $borderColor="#6b6b6b", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") -AddElementTag("Person,Customer", $bgColor="#08427b", $borderColor="#052e56", $fontColor="#ffffff", $sprite="", $shadowing="", $borderStyle="solid") - -AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") - -AddBoundaryTag("Big Bank plc", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") -Boundary(group_1, "Big Bank plc", $tags="Big Bank plc") { - Person(CustomerServiceStaff, "Customer Service Staff", $descr="Customer service staff within the bank.", $tags="Person,Bank Staff", $link="") - Person(BackOfficeStaff, "Back Office Staff", $descr="Administration and support staff within the bank.", $tags="Person,Bank Staff", $link="") - System(MainframeBankingSystem, "Mainframe Banking System", $descr="Stores all of the core banking information about customers, accounts, transactions, etc.", $tags="Software System,Existing System", $link="") - System(EmailSystem, "E-mail System", $descr="The internal Microsoft Exchange e-mail system.", $tags="Software System,Existing System", $link="") - System(ATM, "ATM", $descr="Allows customers to withdraw cash.", $tags="Software System,Existing System", $link="") - System(InternetBankingSystem, "Internet Banking System", $descr="Allows customers to view information about their bank accounts, and make payments.", $tags="Software System", $link="") -} - -Person(PersonalBankingCustomer, "Personal Banking Customer", $descr="A customer of the bank, with personal bank accounts.", $tags="Person,Customer", $link="") - -Rel(PersonalBankingCustomer, InternetBankingSystem, "Views account balances, and makes payments using", $techn="", $tags="Relationship", $link="") -Rel(InternetBankingSystem, MainframeBankingSystem, "Gets account information from, and makes payments using", $techn="", $tags="Relationship", $link="") -Rel(InternetBankingSystem, EmailSystem, "Sends e-mail using", $techn="", $tags="Relationship", $link="") -Rel(EmailSystem, PersonalBankingCustomer, "Sends e-mails to", $techn="", $tags="Relationship", $link="") -Rel(PersonalBankingCustomer, CustomerServiceStaff, "Asks questions to", $techn="Telephone", $tags="Relationship", $link="") -Rel(CustomerServiceStaff, MainframeBankingSystem, "Uses", $techn="", $tags="Relationship", $link="") -Rel(PersonalBankingCustomer, ATM, "Withdraws cash using", $techn="", $tags="Relationship", $link="") -Rel(ATM, MainframeBankingSystem, "Uses", $techn="", $tags="Relationship", $link="") -Rel(BackOfficeStaff, MainframeBankingSystem, "Uses", $techn="", $tags="Relationship", $link="") - -SHOW_LEGEND(true) -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/54915-AmazonWebServicesDeployment-WithTags.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/54915-AmazonWebServicesDeployment-WithTags.puml deleted file mode 100644 index da23c6199..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/54915-AmazonWebServicesDeployment-WithTags.puml +++ /dev/null @@ -1,52 +0,0 @@ -@startuml -set separator none -title Spring PetClinic - Deployment - Live - -left to right direction - -!include -!include -!include -!include - -AddElementTag("Container,Application", $bgColor="#ffffff", $borderColor="#b2b2b2", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="solid") -AddElementTag("Amazon Web Services - RDS", $bgColor="#ffffff", $borderColor="#3b48cc", $fontColor="#3b48cc", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/amazon-rds.png{scale=0.1}", $shadowing="", $borderStyle="solid") -AddElementTag("Amazon Web Services - Auto Scaling", $bgColor="#ffffff", $borderColor="#cc2264", $fontColor="#cc2264", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/aws-auto-scaling.png{scale=0.1}", $shadowing="", $borderStyle="solid") -AddElementTag("Amazon Web Services - Route 53", $bgColor="#ffffff", $borderColor="#693cc5", $fontColor="#693cc5", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/amazon-route-53.png{scale=0.1}", $shadowing="", $borderStyle="solid") -AddElementTag("Amazon Web Services - EC2", $bgColor="#ffffff", $borderColor="#d86613", $fontColor="#d86613", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/amazon-ec2.png{scale=0.1}", $shadowing="", $borderStyle="solid") -AddElementTag("Amazon Web Services - Region", $bgColor="#ffffff", $borderColor="#147eba", $fontColor="#147eba", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/region.png{scale=0.21428571428571427}", $shadowing="", $borderStyle="solid") -AddElementTag("Amazon Web Services - Elastic Load Balancing", $bgColor="#ffffff", $borderColor="#693cc5", $fontColor="#693cc5", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/elastic-load-balancing.png{scale=0.1}", $shadowing="", $borderStyle="solid") -AddElementTag("Amazon Web Services - RDS MySQL instance", $bgColor="#ffffff", $borderColor="#3b48cc", $fontColor="#3b48cc", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/amazon-rds-mysql-instance.png{scale=0.15}", $shadowing="", $borderStyle="solid") -AddElementTag("Container,Database", $bgColor="#ffffff", $borderColor="#b2b2b2", $fontColor="#000000", $sprite="", $shadowing="", $borderStyle="solid") -AddElementTag("Amazon Web Services - Cloud", $bgColor="#ffffff", $borderColor="#232f3e", $fontColor="#232f3e", $sprite="img:https://static.structurizr.com/themes/amazon-web-services-2020.04.30/aws-cloud.png{scale=0.21428571428571427}", $shadowing="", $borderStyle="solid") - -AddRelTag("Relationship", $textColor="#707070", $lineColor="#707070", $lineStyle = "") - -Deployment_Node(Live.AmazonWebServices, "Amazon Web Services", $type="", $descr="", $tags="Amazon Web Services - Cloud", $link="") { - Deployment_Node(Live.AmazonWebServices.USEast1, "US-East-1", $type="", $descr="", $tags="Amazon Web Services - Region", $link="") { - Deployment_Node(Live.AmazonWebServices.USEast1.AmazonRDS, "Amazon RDS", $type="", $descr="", $tags="Amazon Web Services - RDS", $link="") { - Deployment_Node(Live.AmazonWebServices.USEast1.AmazonRDS.MySQL, "MySQL", $type="", $descr="", $tags="Amazon Web Services - RDS MySQL instance", $link="") { - ContainerDb(Live.AmazonWebServices.USEast1.AmazonRDS.MySQL.Database_1, "Database", $techn="Relational database schema", $descr="Stores information regarding the veterinarians, the clients, and their pets.", $tags="Container,Database", $link="") - } - - } - - Deployment_Node(Live.AmazonWebServices.USEast1.Route53, "Route 53", $type="", $descr="Highly available and scalable cloud DNS service.", $tags="Amazon Web Services - Route 53", $link="") - Deployment_Node(Live.AmazonWebServices.USEast1.ElasticLoadBalancer, "Elastic Load Balancer", $type="", $descr="Automatically distributes incoming application traffic.", $tags="Amazon Web Services - Elastic Load Balancing", $link="") - Deployment_Node(Live.AmazonWebServices.USEast1.Autoscalinggroup, "Autoscaling group", $type="", $descr="", $tags="Amazon Web Services - Auto Scaling", $link="") { - Deployment_Node(Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2, "Amazon EC2", $type="", $descr="", $tags="Amazon Web Services - EC2", $link="") { - Container(Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2.WebApplication_1, "Web Application", $techn="Java and Spring Boot", $descr="Allows employees to view and manage information regarding the veterinarians, the clients, and their pets.", $tags="Container,Application", $link="") - } - - } - - } - -} - -Rel(Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2.WebApplication_1, Live.AmazonWebServices.USEast1.AmazonRDS.MySQL.Database_1, "Reads from and writes to", $techn="MySQL Protocol/SSL", $tags="Relationship", $link="") -Rel(Live.AmazonWebServices.USEast1.Route53, Live.AmazonWebServices.USEast1.ElasticLoadBalancer, "Forwards requests to", $techn="HTTPS", $tags="Relationship", $link="") -Rel(Live.AmazonWebServices.USEast1.ElasticLoadBalancer, Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2.WebApplication_1, "Forwards requests to", $techn="HTTPS", $tags="Relationship", $link="") - -SHOW_LEGEND(true) -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/54915-AmazonWebServicesDeployment-WithoutTags.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/54915-AmazonWebServicesDeployment-WithoutTags.puml deleted file mode 100644 index f6a6505d3..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/54915-AmazonWebServicesDeployment-WithoutTags.puml +++ /dev/null @@ -1,39 +0,0 @@ -@startuml -set separator none -title Spring PetClinic - Deployment - Live - -left to right direction - -!include -!include -!include -!include - -Deployment_Node(Live.AmazonWebServices, "Amazon Web Services", $type="", $descr="", $tags="", $link="") { - Deployment_Node(Live.AmazonWebServices.USEast1, "US-East-1", $type="", $descr="", $tags="", $link="") { - Deployment_Node(Live.AmazonWebServices.USEast1.AmazonRDS, "Amazon RDS", $type="", $descr="", $tags="", $link="") { - Deployment_Node(Live.AmazonWebServices.USEast1.AmazonRDS.MySQL, "MySQL", $type="", $descr="", $tags="", $link="") { - ContainerDb(Live.AmazonWebServices.USEast1.AmazonRDS.MySQL.Database_1, "Database", $techn="Relational database schema", $descr="Stores information regarding the veterinarians, the clients, and their pets.", $tags="", $link="") - } - - } - - Deployment_Node(Live.AmazonWebServices.USEast1.Route53, "Route 53", $type="", $descr="Highly available and scalable cloud DNS service.", $tags="", $link="") - Deployment_Node(Live.AmazonWebServices.USEast1.ElasticLoadBalancer, "Elastic Load Balancer", $type="", $descr="Automatically distributes incoming application traffic.", $tags="", $link="") - Deployment_Node(Live.AmazonWebServices.USEast1.Autoscalinggroup, "Autoscaling group", $type="", $descr="", $tags="", $link="") { - Deployment_Node(Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2, "Amazon EC2", $type="", $descr="", $tags="", $link="") { - Container(Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2.WebApplication_1, "Web Application", $techn="Java and Spring Boot", $descr="Allows employees to view and manage information regarding the veterinarians, the clients, and their pets.", $tags="", $link="") - } - - } - - } - -} - -Rel(Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2.WebApplication_1, Live.AmazonWebServices.USEast1.AmazonRDS.MySQL.Database_1, "Reads from and writes to", $techn="MySQL Protocol/SSL", $tags="", $link="") -Rel(Live.AmazonWebServices.USEast1.Route53, Live.AmazonWebServices.USEast1.ElasticLoadBalancer, "Forwards requests to", $techn="HTTPS", $tags="", $link="") -Rel(Live.AmazonWebServices.USEast1.ElasticLoadBalancer, Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2.WebApplication_1, "Forwards requests to", $techn="HTTPS", $tags="", $link="") - -SHOW_LEGEND(true) -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/group-styles-1.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/group-styles-1.puml deleted file mode 100644 index 180f8c3aa..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/group-styles-1.puml +++ /dev/null @@ -1,28 +0,0 @@ -@startuml -set separator none -title System Landscape - -top to bottom direction - -!include -!include - -AddBoundaryTag("Group 1", $borderColor="#111111", $fontColor="#111111", $borderStyle="dashed") -Boundary(group_1, "Group 1", $tags="Group 1") { - Person(User1, "User 1", $descr="", $tags="", $link="") -} - -AddBoundaryTag("Group 2", $borderColor="#222222", $fontColor="#222222", $borderStyle="dashed") -Boundary(group_2, "Group 2", $tags="Group 2") { - Person(User2, "User 2", $descr="", $tags="", $link="") -} - -AddBoundaryTag("Group 3", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") -Boundary(group_3, "Group 3", $tags="Group 3") { - Person(User3, "User 3", $descr="", $tags="", $link="") -} - - - -SHOW_LEGEND(true) -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/group-styles-2.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/group-styles-2.puml deleted file mode 100644 index ff23c0a76..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/group-styles-2.puml +++ /dev/null @@ -1,28 +0,0 @@ -@startuml -set separator none -title System Landscape - -top to bottom direction - -!include -!include - -AddBoundaryTag("Group 1", $borderColor="#111111", $fontColor="#111111", $borderStyle="dashed") -Boundary(group_1, "Group 1", $tags="Group 1") { - Person(User1, "User 1", $descr="", $tags="", $link="") -} - -AddBoundaryTag("Group 2", $borderColor="#222222", $fontColor="#222222", $borderStyle="dashed") -Boundary(group_2, "Group 2", $tags="Group 2") { - Person(User2, "User 2", $descr="", $tags="", $link="") -} - -AddBoundaryTag("Group 3", $borderColor="#aabbcc", $fontColor="#aabbcc", $borderStyle="dashed") -Boundary(group_3, "Group 3", $tags="Group 3") { - Person(User3, "User 3", $descr="", $tags="", $link="") -} - - - -SHOW_LEGEND(true) -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Components.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Components.puml deleted file mode 100644 index ff496fc56..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Components.puml +++ /dev/null @@ -1,26 +0,0 @@ -@startuml -set separator none -title D - F - Components - -top to bottom direction - -!include -!include -!include - -System(C, "C", $descr="", $tags="", $link="") - -Container_Boundary("D.F_boundary", "F", $tags="") { - AddBoundaryTag("Group 5", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") - Boundary(group_1, "Group 5", $tags="Group 5") { - Component(D.F.H, "H", $techn="", $descr="", $tags="", $link="") - } - - Component(D.F.G, "G", $techn="", $descr="", $tags="", $link="") -} - -Rel(C, D.F.G, "", $techn="", $tags="", $link="") -Rel(C, D.F.H, "", $techn="", $tags="", $link="") - -SHOW_LEGEND(true) -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Containers.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Containers.puml deleted file mode 100644 index 2f92cc66f..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-Containers.puml +++ /dev/null @@ -1,26 +0,0 @@ -@startuml -set separator none -title D - Containers - -top to bottom direction - -!include -!include -!include - -System(C, "C", $descr="", $tags="", $link="") - -System_Boundary("D_boundary", "D", $tags="") { - AddBoundaryTag("Group 4", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") - Boundary(group_1, "Group 4", $tags="Group 4") { - Container(D.F, "F", $techn="", $descr="", $tags="", $link="") - } - - Container(D.E, "E", $techn="", $descr="", $tags="", $link="") -} - -Rel(C, D.E, "", $techn="", $tags="", $link="") -Rel(C, D.F, "", $techn="", $tags="", $link="") - -SHOW_LEGEND(true) -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-SystemLandscape.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-SystemLandscape.puml deleted file mode 100644 index 0e045ac3d..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/groups-SystemLandscape.puml +++ /dev/null @@ -1,32 +0,0 @@ -@startuml -set separator none -title System Landscape - -top to bottom direction - -!include -!include - -AddBoundaryTag("Group 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") -Boundary(group_1, "Group 1", $tags="Group 1") { - System(B, "B", $descr="", $tags="", $link="") -} - -AddBoundaryTag("Group 2", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") -Boundary(group_2, "Group 2", $tags="Group 2") { - System(C, "C", $descr="", $tags="", $link="") - AddBoundaryTag("Group 2/Group 3", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") - Boundary(group_3, "Group 3", $tags="Group 2/Group 3") { - System(D, "D", $descr="", $tags="", $link="") - } - -} - -System(A, "A", $descr="", $tags="", $link="") - -Rel(B, C, "", $techn="", $tags="", $link="") -Rel(C, D, "", $techn="", $tags="", $link="") -Rel(A, B, "", $techn="", $tags="", $link="") - -SHOW_LEGEND(true) -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/nested-groups-with-dot-separator.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/nested-groups-with-dot-separator.puml deleted file mode 100644 index 25c92424c..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/nested-groups-with-dot-separator.puml +++ /dev/null @@ -1,26 +0,0 @@ -@startuml -set separator none -title System Landscape - -top to bottom direction - -!include -!include - -AddBoundaryTag("Organisation 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") -Boundary(group_1, "Organisation 1", $tags="Organisation 1") { - AddBoundaryTag("Organisation 1.Department 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") - Boundary(group_2, "Department 1", $tags="Organisation 1.Department 1") { - AddBoundaryTag("Organisation 1.Department 1.Team 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") - Boundary(group_3, "Team 1", $tags="Organisation 1.Department 1.Team 1") { - System(Team1, "Team 1", $descr="", $tags="", $link="") - } - - } - -} - - - -SHOW_LEGEND(true) -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/nested-groups.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/nested-groups.puml deleted file mode 100644 index 17b40ad7c..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/nested-groups.puml +++ /dev/null @@ -1,38 +0,0 @@ -@startuml -set separator none -title System Landscape - -top to bottom direction - -!include -!include - -AddBoundaryTag("Organisation 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") -Boundary(group_1, "Organisation 1", $tags="Organisation 1") { - System(Organisation1, "Organisation 1", $descr="", $tags="", $link="") - AddBoundaryTag("Organisation 1/Department 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") - Boundary(group_2, "Department 1", $tags="Organisation 1/Department 1") { - System(Department1, "Department 1", $descr="", $tags="", $link="") - AddBoundaryTag("Organisation 1/Department 1/Team 1", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") - Boundary(group_3, "Team 1", $tags="Organisation 1/Department 1/Team 1") { - System(Team1, "Team 1", $descr="", $tags="", $link="") - } - - AddBoundaryTag("Organisation 1/Department 1/Team 2", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") - Boundary(group_4, "Team 2", $tags="Organisation 1/Department 1/Team 2") { - System(Team2, "Team 2", $descr="", $tags="", $link="") - } - - } - -} - -AddBoundaryTag("Organisation 2", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") -Boundary(group_5, "Organisation 2", $tags="Organisation 2") { - System(Organisation2, "Organisation 2", $descr="", $tags="", $link="") -} - - - -SHOW_LEGEND(true) -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/printProperties-containerView.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/printProperties-containerView.puml deleted file mode 100644 index 3a505ee33..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/printProperties-containerView.puml +++ /dev/null @@ -1,28 +0,0 @@ -@startuml -set separator none -title SoftwareSystem - Containers - -top to bottom direction - -!include -!include -!include - -System_Boundary("SoftwareSystem_boundary", "SoftwareSystem", $tags="") { - WithoutPropertyHeader() - AddProperty("IP","127.0.0.1") - AddProperty("Region","East") - Container(SoftwareSystem.Container1, "Container 1", $techn="", $descr="", $tags="", $link="") - WithoutPropertyHeader() - AddProperty("IP","127.0.0.2") - AddProperty("Region","West") - Container(SoftwareSystem.Container2, "Container 2", $techn="", $descr="", $tags="", $link="") -} - -WithoutPropertyHeader() -AddProperty("Prop1","Value1") -AddProperty("Prop2","Value2") -Rel(SoftwareSystem.Container1, SoftwareSystem.Container2, "", $techn="", $tags="", $link="") - -SHOW_LEGEND(true) -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/printProperties-deploymentView.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/printProperties-deploymentView.puml deleted file mode 100644 index f96e7c97a..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/c4plantuml/printProperties-deploymentView.puml +++ /dev/null @@ -1,21 +0,0 @@ -@startuml -set separator none -title Deployment - Default - -top to bottom direction - -!include -!include -!include - -WithoutPropertyHeader() -AddProperty("Prop1","Value1") -Deployment_Node(Default.Deploymentnode, "Deployment node", $type="", $descr="", $tags="", $link="") { - WithoutPropertyHeader() - AddProperty("Prop2","Value2") - Deployment_Node(Default.Deploymentnode.Infrastructurenode, "Infrastructure node", $type="technology", $descr="description", $tags="", $link="") -} - - -SHOW_LEGEND(true) -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-Components.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-Components.puml deleted file mode 100644 index 09c551dda..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-Components.puml +++ /dev/null @@ -1,118 +0,0 @@ -@startuml -set separator none -title Internet Banking System - API Application - Components - -top to bottom direction -skinparam ranksep 60 -skinparam nodesep 30 - -skinparam { - arrowFontSize 10 - defaultTextAlignment center - wrapWidth 200 - maxMessageSize 100 -} - -hide stereotype - -skinparam rectangle<> { - BackgroundColor #85bbf0 - FontColor #000000 - BorderColor #5d82a8 - shadowing false -} -skinparam database<> { - BackgroundColor #438dd5 - FontColor #ffffff - BorderColor #2e6295 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #85bbf0 - FontColor #000000 - BorderColor #5d82a8 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #999999 - FontColor #ffffff - BorderColor #6b6b6b - shadowing false -} -skinparam rectangle<> { - BackgroundColor #999999 - FontColor #ffffff - BorderColor #6b6b6b - shadowing false -} -skinparam rectangle<> { - BackgroundColor #85bbf0 - FontColor #000000 - BorderColor #5d82a8 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #438dd5 - FontColor #ffffff - BorderColor #2e6295 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #85bbf0 - FontColor #000000 - BorderColor #5d82a8 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #85bbf0 - FontColor #000000 - BorderColor #5d82a8 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #85bbf0 - FontColor #000000 - BorderColor #5d82a8 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #438dd5 - FontColor #ffffff - BorderColor #2e6295 - shadowing false -} -skinparam rectangle<> { - BorderColor #2e6295 - FontColor #2e6295 - shadowing false -} - -rectangle "==Mainframe Banking System\n[Software System]\n\nStores all of the core banking information about customers, accounts, transactions, etc." <> as MainframeBankingSystem -rectangle "==E-mail System\n[Software System]\n\nThe internal Microsoft Exchange e-mail system." <> as EmailSystem -rectangle "==Single-Page Application\n[Container: JavaScript and Angular]\n\nProvides all of the Internet banking functionality to customers via their web browser." <> as InternetBankingSystem.SinglePageApplication -rectangle "==Mobile App\n[Container: Xamarin]\n\nProvides a limited subset of the Internet banking functionality to customers via their mobile device." <> as InternetBankingSystem.MobileApp -database "==Database\n[Container: Oracle Database Schema]\n\nStores user registration information, hashed authentication credentials, access logs, etc." <> as InternetBankingSystem.Database - -rectangle "API Application\n[Container: Java and Spring MVC]" <> { - rectangle "==Sign In Controller\n[Component: Spring MVC Rest Controller]\n\nAllows users to sign in to the Internet Banking System." <> as InternetBankingSystem.APIApplication.SignInController - rectangle "==Accounts Summary Controller\n[Component: Spring MVC Rest Controller]\n\nProvides customers with a summary of their bank accounts." <> as InternetBankingSystem.APIApplication.AccountsSummaryController - rectangle "==Reset Password Controller\n[Component: Spring MVC Rest Controller]\n\nAllows users to reset their passwords with a single use URL." <> as InternetBankingSystem.APIApplication.ResetPasswordController - rectangle "==Security Component\n[Component: Spring Bean]\n\nProvides functionality related to signing in, changing passwords, etc." <> as InternetBankingSystem.APIApplication.SecurityComponent - rectangle "==Mainframe Banking System Facade\n[Component: Spring Bean]\n\nA facade onto the mainframe banking system." <> as InternetBankingSystem.APIApplication.MainframeBankingSystemFacade - rectangle "==E-mail Component\n[Component: Spring Bean]\n\nSends e-mails to users." <> as InternetBankingSystem.APIApplication.EmailComponent -} - -InternetBankingSystem.SinglePageApplication .[#707070,thickness=2].> InternetBankingSystem.APIApplication.SignInController : "Makes API calls to\n[JSON/HTTPS]" -InternetBankingSystem.SinglePageApplication .[#707070,thickness=2].> InternetBankingSystem.APIApplication.AccountsSummaryController : "Makes API calls to\n[JSON/HTTPS]" -InternetBankingSystem.SinglePageApplication .[#707070,thickness=2].> InternetBankingSystem.APIApplication.ResetPasswordController : "Makes API calls to\n[JSON/HTTPS]" -InternetBankingSystem.MobileApp .[#707070,thickness=2].> InternetBankingSystem.APIApplication.SignInController : "Makes API calls to\n[JSON/HTTPS]" -InternetBankingSystem.MobileApp .[#707070,thickness=2].> InternetBankingSystem.APIApplication.AccountsSummaryController : "Makes API calls to\n[JSON/HTTPS]" -InternetBankingSystem.MobileApp .[#707070,thickness=2].> InternetBankingSystem.APIApplication.ResetPasswordController : "Makes API calls to\n[JSON/HTTPS]" -InternetBankingSystem.APIApplication.SignInController .[#707070,thickness=2].> InternetBankingSystem.APIApplication.SecurityComponent : "Uses" -InternetBankingSystem.APIApplication.AccountsSummaryController .[#707070,thickness=2].> InternetBankingSystem.APIApplication.MainframeBankingSystemFacade : "Uses" -InternetBankingSystem.APIApplication.ResetPasswordController .[#707070,thickness=2].> InternetBankingSystem.APIApplication.SecurityComponent : "Uses" -InternetBankingSystem.APIApplication.ResetPasswordController .[#707070,thickness=2].> InternetBankingSystem.APIApplication.EmailComponent : "Uses" -InternetBankingSystem.APIApplication.SecurityComponent .[#707070,thickness=2].> InternetBankingSystem.Database : "Reads from and writes to\n[SQL/TCP]" -InternetBankingSystem.APIApplication.MainframeBankingSystemFacade .[#707070,thickness=2].> MainframeBankingSystem : "Makes API calls to\n[XML/HTTPS]" -InternetBankingSystem.APIApplication.EmailComponent .[#707070,thickness=2].> EmailSystem : "Sends e-mail using" -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-Containers.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-Containers.puml deleted file mode 100644 index dab94a130..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-Containers.puml +++ /dev/null @@ -1,94 +0,0 @@ -@startuml -set separator none -title Internet Banking System - Containers - -top to bottom direction -skinparam ranksep 60 -skinparam nodesep 30 - -skinparam { - arrowFontSize 10 - defaultTextAlignment center - wrapWidth 200 - maxMessageSize 100 -} - -hide stereotype - -skinparam rectangle<> { - BackgroundColor #438dd5 - FontColor #ffffff - BorderColor #2e6295 - shadowing false -} -skinparam database<> { - BackgroundColor #438dd5 - FontColor #ffffff - BorderColor #2e6295 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #999999 - FontColor #ffffff - BorderColor #6b6b6b - shadowing false -} -skinparam rectangle<> { - BackgroundColor #999999 - FontColor #ffffff - BorderColor #6b6b6b - shadowing false -} -skinparam rectangle<> { - BackgroundColor #438dd5 - FontColor #ffffff - BorderColor #2e6295 - shadowing false -} -skinparam person<> { - BackgroundColor #08427b - FontColor #ffffff - BorderColor #052e56 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #438dd5 - FontColor #ffffff - BorderColor #2e6295 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #438dd5 - FontColor #ffffff - BorderColor #2e6295 - shadowing false -} -skinparam rectangle<> { - BorderColor #0b4884 - FontColor #0b4884 - shadowing false -} - -person "==Personal Banking Customer\n[Person]\n\nA customer of the bank, with personal bank accounts." <> as PersonalBankingCustomer -rectangle "==Mainframe Banking System\n[Software System]\n\nStores all of the core banking information about customers, accounts, transactions, etc." <> as MainframeBankingSystem -rectangle "==E-mail System\n[Software System]\n\nThe internal Microsoft Exchange e-mail system." <> as EmailSystem - -rectangle "Internet Banking System\n[Software System]" <> { - rectangle "==Web Application\n[Container: Java and Spring MVC]\n\nDelivers the static content and the Internet banking single page application." <> as InternetBankingSystem.WebApplication - rectangle "==API Application\n[Container: Java and Spring MVC]\n\nProvides Internet banking functionality via a JSON/HTTPS API." <> as InternetBankingSystem.APIApplication - database "==Database\n[Container: Oracle Database Schema]\n\nStores user registration information, hashed authentication credentials, access logs, etc." <> as InternetBankingSystem.Database - rectangle "==Single-Page Application\n[Container: JavaScript and Angular]\n\nProvides all of the Internet banking functionality to customers via their web browser." <> as InternetBankingSystem.SinglePageApplication - rectangle "==Mobile App\n[Container: Xamarin]\n\nProvides a limited subset of the Internet banking functionality to customers via their mobile device." <> as InternetBankingSystem.MobileApp -} - -EmailSystem .[#707070,thickness=2].> PersonalBankingCustomer : "Sends e-mails to" -PersonalBankingCustomer .[#707070,thickness=2].> InternetBankingSystem.WebApplication : "Visits bigbank.com/ib using\n[HTTPS]" -PersonalBankingCustomer .[#707070,thickness=2].> InternetBankingSystem.SinglePageApplication : "Views account balances, and makes payments using" -PersonalBankingCustomer .[#707070,thickness=2].> InternetBankingSystem.MobileApp : "Views account balances, and makes payments using" -InternetBankingSystem.WebApplication .[#707070,thickness=2].> InternetBankingSystem.SinglePageApplication : "Delivers to the customer's web browser" -InternetBankingSystem.SinglePageApplication .[#707070,thickness=2].> InternetBankingSystem.APIApplication : "Makes API calls to\n[JSON/HTTPS]" -InternetBankingSystem.MobileApp .[#707070,thickness=2].> InternetBankingSystem.APIApplication : "Makes API calls to\n[JSON/HTTPS]" -InternetBankingSystem.APIApplication .[#707070,thickness=2].> InternetBankingSystem.Database : "Reads from and writes to\n[SQL/TCP]" -InternetBankingSystem.APIApplication .[#707070,thickness=2].> MainframeBankingSystem : "Makes API calls to\n[XML/HTTPS]" -InternetBankingSystem.APIApplication .[#707070,thickness=2].> EmailSystem : "Sends e-mail using" -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-DevelopmentDeployment.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-DevelopmentDeployment.puml deleted file mode 100644 index b09bc9df7..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-DevelopmentDeployment.puml +++ /dev/null @@ -1,130 +0,0 @@ -@startuml -set separator none -title Internet Banking System - Deployment - Development - -top to bottom direction -skinparam ranksep 60 -skinparam nodesep 30 - -skinparam { - arrowFontSize 10 - defaultTextAlignment center - wrapWidth 200 - maxMessageSize 100 -} - -hide stereotype - -skinparam rectangle<> { - BackgroundColor #438dd5 - FontColor #ffffff - BorderColor #2e6295 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #ffffff - FontColor #000000 - BorderColor #888888 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #ffffff - FontColor #000000 - BorderColor #888888 - shadowing false -} -skinparam database<> { - BackgroundColor #438dd5 - FontColor #ffffff - BorderColor #2e6295 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #ffffff - FontColor #000000 - BorderColor #888888 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #ffffff - FontColor #000000 - BorderColor #888888 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #ffffff - FontColor #000000 - BorderColor #888888 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #ffffff - FontColor #000000 - BorderColor #888888 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #999999 - FontColor #ffffff - BorderColor #6b6b6b - shadowing false -} -skinparam rectangle<> { - BackgroundColor #438dd5 - FontColor #ffffff - BorderColor #2e6295 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #438dd5 - FontColor #ffffff - BorderColor #2e6295 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #ffffff - FontColor #000000 - BorderColor #888888 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #ffffff - FontColor #000000 - BorderColor #888888 - shadowing false -} - -rectangle "Developer Laptop\n[Deployment Node: Microsoft Windows 10 or Apple macOS]" <> as Development.DeveloperLaptop { - rectangle "Web Browser\n[Deployment Node: Chrome, Firefox, Safari, or Edge]" <> as Development.DeveloperLaptop.WebBrowser { - rectangle "==Single-Page Application\n[Container: JavaScript and Angular]\n\nProvides all of the Internet banking functionality to customers via their web browser." <> as Development.DeveloperLaptop.WebBrowser.SinglePageApplication_1 - } - - rectangle "Docker Container - Web Server\n[Deployment Node: Docker]" <> as Development.DeveloperLaptop.DockerContainerWebServer { - rectangle "Apache Tomcat\n[Deployment Node: Apache Tomcat 8.x]" <> as Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat { - rectangle "==Web Application\n[Container: Java and Spring MVC]\n\nDelivers the static content and the Internet banking single page application." <> as Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.WebApplication_1 - rectangle "==API Application\n[Container: Java and Spring MVC]\n\nProvides Internet banking functionality via a JSON/HTTPS API." <> as Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.APIApplication_1 - } - - } - - rectangle "Docker Container - Database Server\n[Deployment Node: Docker]" <> as Development.DeveloperLaptop.DockerContainerDatabaseServer { - rectangle "Database Server\n[Deployment Node: Oracle 12c]" <> as Development.DeveloperLaptop.DockerContainerDatabaseServer.DatabaseServer { - database "==Database\n[Container: Oracle Database Schema]\n\nStores user registration information, hashed authentication credentials, access logs, etc." <> as Development.DeveloperLaptop.DockerContainerDatabaseServer.DatabaseServer.Database_1 - } - - } - -} - -rectangle "Big Bank plc\n[Deployment Node: Big Bank plc data center]" <> as Development.BigBankplc { - rectangle "bigbank-dev001\n[Deployment Node]" <> as Development.BigBankplc.bigbankdev001 { - rectangle "==Mainframe Banking System\n[Software System]\n\nStores all of the core banking information about customers, accounts, transactions, etc." <> as Development.BigBankplc.bigbankdev001.MainframeBankingSystem_1 - } - -} - -Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.WebApplication_1 .[#707070,thickness=2].> Development.DeveloperLaptop.WebBrowser.SinglePageApplication_1 : "Delivers to the customer's web browser" -Development.DeveloperLaptop.WebBrowser.SinglePageApplication_1 .[#707070,thickness=2].> Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.APIApplication_1 : "Makes API calls to\n[JSON/HTTPS]" -Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.APIApplication_1 .[#707070,thickness=2].> Development.DeveloperLaptop.DockerContainerDatabaseServer.DatabaseServer.Database_1 : "Reads from and writes to\n[SQL/TCP]" -Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.APIApplication_1 .[#707070,thickness=2].> Development.BigBankplc.bigbankdev001.MainframeBankingSystem_1 : "Makes API calls to\n[XML/HTTPS]" -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-LiveDeployment.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-LiveDeployment.puml deleted file mode 100644 index 3f6d368ca..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-LiveDeployment.puml +++ /dev/null @@ -1,192 +0,0 @@ -@startuml -set separator none -title Internet Banking System - Deployment - Live - -top to bottom direction -skinparam ranksep 60 -skinparam nodesep 30 - -skinparam { - arrowFontSize 10 - defaultTextAlignment center - wrapWidth 200 - maxMessageSize 100 -} - -hide stereotype - -skinparam rectangle<> { - BackgroundColor #438dd5 - FontColor #ffffff - BorderColor #2e6295 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #ffffff - FontColor #000000 - BorderColor #888888 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #ffffff - FontColor #000000 - BorderColor #888888 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #ffffff - FontColor #000000 - BorderColor #888888 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #ffffff - FontColor #000000 - BorderColor #888888 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #ffffff - FontColor #000000 - BorderColor #888888 - shadowing false -} -skinparam database<> { - BackgroundColor #438dd5 - FontColor #ffffff - BorderColor #2e6295 - shadowing false -} -skinparam database<> { - BackgroundColor #438dd5 - FontColor #ffffff - BorderColor #2e6295 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #999999 - FontColor #ffffff - BorderColor #6b6b6b - shadowing false -} -skinparam rectangle<> { - BackgroundColor #438dd5 - FontColor #ffffff - BorderColor #2e6295 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #ffffff - FontColor #000000 - BorderColor #888888 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #ffffff - FontColor #000000 - BorderColor #888888 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #438dd5 - FontColor #ffffff - BorderColor #2e6295 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #438dd5 - FontColor #ffffff - BorderColor #2e6295 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #ffffff - FontColor #000000 - BorderColor #888888 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #ffffff - FontColor #000000 - BorderColor #888888 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #ffffff - FontColor #000000 - BorderColor #888888 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #ffffff - FontColor #000000 - BorderColor #888888 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #ffffff - FontColor #000000 - BorderColor #888888 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #ffffff - FontColor #000000 - BorderColor #888888 - shadowing false -} - -rectangle "Customer's mobile device\n[Deployment Node: Apple iOS or Android]" <> as Live.Customersmobiledevice { - rectangle "==Mobile App\n[Container: Xamarin]\n\nProvides a limited subset of the Internet banking functionality to customers via their mobile device." <> as Live.Customersmobiledevice.MobileApp_1 -} - -rectangle "Customer's computer\n[Deployment Node: Microsoft Windows or Apple macOS]" <> as Live.Customerscomputer { - rectangle "Web Browser\n[Deployment Node: Chrome, Firefox, Safari, or Edge]" <> as Live.Customerscomputer.WebBrowser { - rectangle "==Single-Page Application\n[Container: JavaScript and Angular]\n\nProvides all of the Internet banking functionality to customers via their web browser." <> as Live.Customerscomputer.WebBrowser.SinglePageApplication_1 - } - -} - -rectangle "Big Bank plc\n[Deployment Node: Big Bank plc data center]" <> as Live.BigBankplc { - rectangle "bigbank-web*** (x4)\n[Deployment Node: Ubuntu 16.04 LTS]" <> as Live.BigBankplc.bigbankweb { - rectangle "Apache Tomcat\n[Deployment Node: Apache Tomcat 8.x]" <> as Live.BigBankplc.bigbankweb.ApacheTomcat { - rectangle "==Web Application\n[Container: Java and Spring MVC]\n\nDelivers the static content and the Internet banking single page application." <> as Live.BigBankplc.bigbankweb.ApacheTomcat.WebApplication_1 - } - - } - - rectangle "bigbank-api*** (x8)\n[Deployment Node: Ubuntu 16.04 LTS]" <> as Live.BigBankplc.bigbankapi { - rectangle "Apache Tomcat\n[Deployment Node: Apache Tomcat 8.x]" <> as Live.BigBankplc.bigbankapi.ApacheTomcat { - rectangle "==API Application\n[Container: Java and Spring MVC]\n\nProvides Internet banking functionality via a JSON/HTTPS API." <> as Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1 - } - - } - - rectangle "bigbank-db01\n[Deployment Node: Ubuntu 16.04 LTS]" <> as Live.BigBankplc.bigbankdb01 { - rectangle "Oracle - Primary\n[Deployment Node: Oracle 12c]" <> as Live.BigBankplc.bigbankdb01.OraclePrimary { - database "==Database\n[Container: Oracle Database Schema]\n\nStores user registration information, hashed authentication credentials, access logs, etc." <> as Live.BigBankplc.bigbankdb01.OraclePrimary.Database_1 - } - - } - - rectangle "bigbank-db02\n[Deployment Node: Ubuntu 16.04 LTS]" <> as Live.BigBankplc.bigbankdb02 { - rectangle "Oracle - Secondary\n[Deployment Node: Oracle 12c]" <> as Live.BigBankplc.bigbankdb02.OracleSecondary { - database "==Database\n[Container: Oracle Database Schema]\n\nStores user registration information, hashed authentication credentials, access logs, etc." <> as Live.BigBankplc.bigbankdb02.OracleSecondary.Database_1 - } - - } - - rectangle "bigbank-prod001\n[Deployment Node]" <> as Live.BigBankplc.bigbankprod001 { - rectangle "==Mainframe Banking System\n[Software System]\n\nStores all of the core banking information about customers, accounts, transactions, etc." <> as Live.BigBankplc.bigbankprod001.MainframeBankingSystem_1 - } - -} - -Live.BigBankplc.bigbankweb.ApacheTomcat.WebApplication_1 .[#707070,thickness=2].> Live.Customerscomputer.WebBrowser.SinglePageApplication_1 : "Delivers to the customer's web browser" -Live.Customersmobiledevice.MobileApp_1 .[#707070,thickness=2].> Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1 : "Makes API calls to\n[JSON/HTTPS]" -Live.Customerscomputer.WebBrowser.SinglePageApplication_1 .[#707070,thickness=2].> Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1 : "Makes API calls to\n[JSON/HTTPS]" -Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1 .[#707070,thickness=2].> Live.BigBankplc.bigbankdb01.OraclePrimary.Database_1 : "Reads from and writes to\n[SQL/TCP]" -Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1 .[#707070,thickness=2].> Live.BigBankplc.bigbankdb02.OracleSecondary.Database_1 : "Reads from and writes to\n[SQL/TCP]" -Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1 .[#707070,thickness=2].> Live.BigBankplc.bigbankprod001.MainframeBankingSystem_1 : "Makes API calls to\n[XML/HTTPS]" -Live.BigBankplc.bigbankdb01.OraclePrimary .[#707070,thickness=2].> Live.BigBankplc.bigbankdb02.OracleSecondary : "Replicates data to" -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SignIn-sequence.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SignIn-sequence.puml deleted file mode 100644 index eb84e8096..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SignIn-sequence.puml +++ /dev/null @@ -1,49 +0,0 @@ -@startuml -set separator none -title API Application - Dynamic - -skinparam { - arrowFontSize 10 - defaultTextAlignment center - wrapWidth 200 - maxMessageSize 100 -} - -hide stereotype - -skinparam sequenceParticipant<> { - BackgroundColor #438dd5 - FontColor #ffffff - BorderColor #2e6295 - shadowing false -} -skinparam sequenceParticipant<> { - BackgroundColor #85bbf0 - FontColor #000000 - BorderColor #5d82a8 - shadowing false -} -skinparam sequenceParticipant<> { - BackgroundColor #85bbf0 - FontColor #000000 - BorderColor #5d82a8 - shadowing false -} -skinparam sequenceParticipant<> { - BackgroundColor #438dd5 - FontColor #ffffff - BorderColor #2e6295 - shadowing false -} - -participant "Single-Page Application\n[Container: JavaScript and Angular]" as InternetBankingSystem.SinglePageApplication <> #438dd5 -participant "Sign In Controller\n[Component: Spring MVC Rest Controller]" as InternetBankingSystem.APIApplication.SignInController <> #85bbf0 -participant "Security Component\n[Component: Spring Bean]" as InternetBankingSystem.APIApplication.SecurityComponent <> #85bbf0 -database "Database\n[Container: Oracle Database Schema]" as InternetBankingSystem.Database <> #438dd5 -InternetBankingSystem.SinglePageApplication -[#707070]> InternetBankingSystem.APIApplication.SignInController : Submits credentials to -InternetBankingSystem.APIApplication.SignInController -[#707070]> InternetBankingSystem.APIApplication.SecurityComponent : Validates credentials using -InternetBankingSystem.APIApplication.SecurityComponent -[#707070]> InternetBankingSystem.Database : select * from users where username = ? -InternetBankingSystem.APIApplication.SecurityComponent <-[#707070]- InternetBankingSystem.Database : Returns user data to -InternetBankingSystem.APIApplication.SignInController <-[#707070]- InternetBankingSystem.APIApplication.SecurityComponent : Returns true if the hashed password matches -InternetBankingSystem.SinglePageApplication <-[#707070]- InternetBankingSystem.APIApplication.SignInController : Sends back an authentication token to -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SignIn.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SignIn.puml deleted file mode 100644 index f70ca4a45..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SignIn.puml +++ /dev/null @@ -1,62 +0,0 @@ -@startuml -set separator none -title API Application - Dynamic - -top to bottom direction -skinparam ranksep 60 -skinparam nodesep 30 - -skinparam { - arrowFontSize 10 - defaultTextAlignment center - wrapWidth 200 - maxMessageSize 100 -} - -hide stereotype - -skinparam database<> { - BackgroundColor #438dd5 - FontColor #ffffff - BorderColor #2e6295 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #85bbf0 - FontColor #000000 - BorderColor #5d82a8 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #85bbf0 - FontColor #000000 - BorderColor #5d82a8 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #438dd5 - FontColor #ffffff - BorderColor #2e6295 - shadowing false -} -skinparam rectangle<> { - BorderColor #2e6295 - FontColor #2e6295 - shadowing false -} - -rectangle "API Application\n[Container: Java and Spring MVC]" <> { - rectangle "==Sign In Controller\n[Component: Spring MVC Rest Controller]\n\nAllows users to sign in to the Internet Banking System." <> as InternetBankingSystem.APIApplication.SignInController - rectangle "==Security Component\n[Component: Spring Bean]\n\nProvides functionality related to signing in, changing passwords, etc." <> as InternetBankingSystem.APIApplication.SecurityComponent -} - -rectangle "==Single-Page Application\n[Container: JavaScript and Angular]\n\nProvides all of the Internet banking functionality to customers via their web browser." <> as InternetBankingSystem.SinglePageApplication -database "==Database\n[Container: Oracle Database Schema]\n\nStores user registration information, hashed authentication credentials, access logs, etc." <> as InternetBankingSystem.Database - -InternetBankingSystem.SinglePageApplication .[#707070,thickness=2].> InternetBankingSystem.APIApplication.SignInController : "1. Submits credentials to\n[JSON/HTTPS]" -InternetBankingSystem.APIApplication.SignInController .[#707070,thickness=2].> InternetBankingSystem.APIApplication.SecurityComponent : "2. Validates credentials using" -InternetBankingSystem.APIApplication.SecurityComponent .[#707070,thickness=2].> InternetBankingSystem.Database : "3. select * from users where username = ?\n[SQL/TCP]" -InternetBankingSystem.APIApplication.SecurityComponent <.[#707070,thickness=2]. InternetBankingSystem.Database : "4. Returns user data to\n[SQL/TCP]" -InternetBankingSystem.APIApplication.SignInController <.[#707070,thickness=2]. InternetBankingSystem.APIApplication.SecurityComponent : "5. Returns true if the hashed password matches" -InternetBankingSystem.SinglePageApplication <.[#707070,thickness=2]. InternetBankingSystem.APIApplication.SignInController : "6. Sends back an authentication token to\n[JSON/HTTPS]" -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemContext.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemContext.puml deleted file mode 100644 index a3a399968..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemContext.puml +++ /dev/null @@ -1,59 +0,0 @@ -@startuml -set separator none -title Internet Banking System - System Context - -top to bottom direction -skinparam ranksep 60 -skinparam nodesep 30 - -skinparam { - arrowFontSize 10 - defaultTextAlignment center - wrapWidth 200 - maxMessageSize 100 -} - -hide stereotype - -skinparam rectangle<> { - BackgroundColor #999999 - FontColor #ffffff - BorderColor #6b6b6b - shadowing false -} -skinparam rectangle<> { - BackgroundColor #1168bd - FontColor #ffffff - BorderColor #0b4884 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #999999 - FontColor #ffffff - BorderColor #6b6b6b - shadowing false -} -skinparam person<> { - BackgroundColor #08427b - FontColor #ffffff - BorderColor #052e56 - shadowing false -} - -rectangle "Big Bank plc" <> as group1 { - skinparam RectangleBorderColor<> #cccccc - skinparam RectangleFontColor<> #cccccc - skinparam RectangleBorderStyle<> dashed - - rectangle "==Mainframe Banking System\n[Software System]\n\nStores all of the core banking information about customers, accounts, transactions, etc." <> as MainframeBankingSystem - rectangle "==E-mail System\n[Software System]\n\nThe internal Microsoft Exchange e-mail system." <> as EmailSystem - rectangle "==Internet Banking System\n[Software System]\n\nAllows customers to view information about their bank accounts, and make payments." <> as InternetBankingSystem -} - -person "==Personal Banking Customer\n[Person]\n\nA customer of the bank, with personal bank accounts." <> as PersonalBankingCustomer - -PersonalBankingCustomer .[#707070,thickness=2].> InternetBankingSystem : "Views account balances, and makes payments using" -InternetBankingSystem .[#707070,thickness=2].> MainframeBankingSystem : "Gets account information from, and makes payments using" -InternetBankingSystem .[#707070,thickness=2].> EmailSystem : "Sends e-mail using" -EmailSystem .[#707070,thickness=2].> PersonalBankingCustomer : "Sends e-mails to" -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemLandscape.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemLandscape.puml deleted file mode 100644 index 89d127718..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/36141-SystemLandscape.puml +++ /dev/null @@ -1,85 +0,0 @@ -@startuml -set separator none -title System Landscape - -top to bottom direction -skinparam ranksep 60 -skinparam nodesep 30 - -skinparam { - arrowFontSize 10 - defaultTextAlignment center - wrapWidth 200 - maxMessageSize 100 -} - -hide stereotype - -skinparam rectangle<> { - BackgroundColor #999999 - FontColor #ffffff - BorderColor #6b6b6b - shadowing false -} -skinparam person<> { - BackgroundColor #999999 - FontColor #ffffff - BorderColor #6b6b6b - shadowing false -} -skinparam person<> { - BackgroundColor #999999 - FontColor #ffffff - BorderColor #6b6b6b - shadowing false -} -skinparam rectangle<> { - BackgroundColor #999999 - FontColor #ffffff - BorderColor #6b6b6b - shadowing false -} -skinparam rectangle<> { - BackgroundColor #1168bd - FontColor #ffffff - BorderColor #0b4884 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #999999 - FontColor #ffffff - BorderColor #6b6b6b - shadowing false -} -skinparam person<> { - BackgroundColor #08427b - FontColor #ffffff - BorderColor #052e56 - shadowing false -} - -rectangle "Big Bank plc" <> as group1 { - skinparam RectangleBorderColor<> #cccccc - skinparam RectangleFontColor<> #cccccc - skinparam RectangleBorderStyle<> dashed - - person "==Customer Service Staff\n[Person]\n\nCustomer service staff within the bank." <> as CustomerServiceStaff - person "==Back Office Staff\n[Person]\n\nAdministration and support staff within the bank." <> as BackOfficeStaff - rectangle "==Mainframe Banking System\n[Software System]\n\nStores all of the core banking information about customers, accounts, transactions, etc." <> as MainframeBankingSystem - rectangle "==E-mail System\n[Software System]\n\nThe internal Microsoft Exchange e-mail system." <> as EmailSystem - rectangle "==ATM\n[Software System]\n\nAllows customers to withdraw cash." <> as ATM - rectangle "==Internet Banking System\n[Software System]\n\nAllows customers to view information about their bank accounts, and make payments." <> as InternetBankingSystem -} - -person "==Personal Banking Customer\n[Person]\n\nA customer of the bank, with personal bank accounts." <> as PersonalBankingCustomer - -PersonalBankingCustomer .[#707070,thickness=2].> InternetBankingSystem : "Views account balances, and makes payments using" -InternetBankingSystem .[#707070,thickness=2].> MainframeBankingSystem : "Gets account information from, and makes payments using" -InternetBankingSystem .[#707070,thickness=2].> EmailSystem : "Sends e-mail using" -EmailSystem .[#707070,thickness=2].> PersonalBankingCustomer : "Sends e-mails to" -PersonalBankingCustomer .[#707070,thickness=2].> CustomerServiceStaff : "Asks questions to\n[Telephone]" -CustomerServiceStaff .[#707070,thickness=2].> MainframeBankingSystem : "Uses" -PersonalBankingCustomer .[#707070,thickness=2].> ATM : "Withdraws cash using" -ATM .[#707070,thickness=2].> MainframeBankingSystem : "Uses" -BackOfficeStaff .[#707070,thickness=2].> MainframeBankingSystem : "Uses" -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/54915-AmazonWebServicesDeployment-Legend.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/54915-AmazonWebServicesDeployment-Legend.puml deleted file mode 100644 index 12bb6a1ce..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/54915-AmazonWebServicesDeployment-Legend.puml +++ /dev/null @@ -1,102 +0,0 @@ -@startuml -set separator none - -skinparam { - shadowing false - arrowFontSize 15 - defaultTextAlignment center - wrapWidth 100 - maxMessageSize 100 -} -hide stereotype - -skinparam rectangle<<_transparent>> { - BorderColor transparent - BackgroundColor transparent - FontColor transparent -} - -skinparam rectangle<<1>> { - BackgroundColor #ffffff - FontColor #cc2264 - BorderColor #cc2264 - roundCorner 20 -} -rectangle "==Amazon Web Services - Auto Scaling\n\n" <<1>> - -skinparam rectangle<<2>> { - BackgroundColor #ffffff - FontColor #232f3e - BorderColor #232f3e - roundCorner 20 -} -rectangle "==Amazon Web Services - Cloud\n\n" <<2>> - -skinparam rectangle<<3>> { - BackgroundColor #ffffff - FontColor #d86613 - BorderColor #d86613 - roundCorner 20 -} -rectangle "==Amazon Web Services - EC2\n\n" <<3>> - -skinparam rectangle<<4>> { - BackgroundColor #ffffff - FontColor #693cc5 - BorderColor #693cc5 - roundCorner 20 -} -rectangle "==Amazon Web Services - Elastic Load Balancing\n\n" <<4>> - -skinparam rectangle<<5>> { - BackgroundColor #ffffff - FontColor #3b48cc - BorderColor #3b48cc - roundCorner 20 -} -rectangle "==Amazon Web Services - RDS\n\n" <<5>> - -skinparam rectangle<<6>> { - BackgroundColor #ffffff - FontColor #3b48cc - BorderColor #3b48cc - roundCorner 20 -} -rectangle "==Amazon Web Services - RDS MySQL instance\n\n" <<6>> - -skinparam rectangle<<7>> { - BackgroundColor #ffffff - FontColor #147eba - BorderColor #147eba - roundCorner 20 -} -rectangle "==Amazon Web Services - Region\n\n" <<7>> - -skinparam rectangle<<8>> { - BackgroundColor #ffffff - FontColor #693cc5 - BorderColor #693cc5 - roundCorner 20 -} -rectangle "==Amazon Web Services - Route 53\n\n" <<8>> - -skinparam rectangle<<9>> { - BackgroundColor #ffffff - FontColor #000000 - BorderColor #b2b2b2 - roundCorner 20 -} -rectangle "==Container, Application" <<9>> - -skinparam database<<10>> { - BackgroundColor #ffffff - FontColor #000000 - BorderColor #b2b2b2 -} -database "==Container, Database" <<10>> - -rectangle "." <<_transparent>> as 11 -11 .[#707070,thickness=2].> 11 : "Relationship" - - -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/54915-AmazonWebServicesDeployment.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/54915-AmazonWebServicesDeployment.puml deleted file mode 100644 index f1753149b..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/54915-AmazonWebServicesDeployment.puml +++ /dev/null @@ -1,113 +0,0 @@ -@startuml -set separator none -title Spring PetClinic - Deployment - Live - -left to right direction -skinparam ranksep 60 -skinparam nodesep 30 - -skinparam { - arrowFontSize 10 - defaultTextAlignment center - wrapWidth 200 - maxMessageSize 100 -} - -hide stereotype - -skinparam rectangle<> { - BackgroundColor #ffffff - FontColor #d86613 - BorderColor #d86613 - roundCorner 20 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #ffffff - FontColor #3b48cc - BorderColor #3b48cc - roundCorner 20 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #ffffff - FontColor #232f3e - BorderColor #232f3e - roundCorner 20 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #ffffff - FontColor #cc2264 - BorderColor #cc2264 - roundCorner 20 - shadowing false -} -skinparam database<> { - BackgroundColor #ffffff - FontColor #000000 - BorderColor #b2b2b2 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #ffffff - FontColor #693cc5 - BorderColor #693cc5 - roundCorner 20 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #ffffff - FontColor #3b48cc - BorderColor #3b48cc - roundCorner 20 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #ffffff - FontColor #693cc5 - BorderColor #693cc5 - roundCorner 20 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #ffffff - FontColor #147eba - BorderColor #147eba - roundCorner 20 - shadowing false -} -skinparam rectangle<> { - BackgroundColor #ffffff - FontColor #000000 - BorderColor #b2b2b2 - roundCorner 20 - shadowing false -} - -rectangle "Amazon Web Services\n[Deployment Node]\n\n" <> as Live.AmazonWebServices { - rectangle "US-East-1\n[Deployment Node]\n\n" <> as Live.AmazonWebServices.USEast1 { - rectangle "Amazon RDS\n[Deployment Node]\n\n" <> as Live.AmazonWebServices.USEast1.AmazonRDS { - rectangle "MySQL\n[Deployment Node]\n\n" <> as Live.AmazonWebServices.USEast1.AmazonRDS.MySQL { - database "==Database\n[Container: Relational database schema]\n\nStores information regarding the veterinarians, the clients, and their pets." <> as Live.AmazonWebServices.USEast1.AmazonRDS.MySQL.Database_1 - } - - } - - rectangle "==Route 53\n[Infrastructure Node]\n\nHighly available and scalable cloud DNS service.\n\n" <> as Live.AmazonWebServices.USEast1.Route53 - rectangle "==Elastic Load Balancer\n[Infrastructure Node]\n\nAutomatically distributes incoming application traffic.\n\n" <> as Live.AmazonWebServices.USEast1.ElasticLoadBalancer - rectangle "Autoscaling group\n[Deployment Node]\n\n" <> as Live.AmazonWebServices.USEast1.Autoscalinggroup { - rectangle "Amazon EC2\n[Deployment Node]\n\n" <> as Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2 { - rectangle "==Web Application\n[Container: Java and Spring Boot]\n\nAllows employees to view and manage information regarding the veterinarians, the clients, and their pets." <> as Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2.WebApplication_1 - } - - } - - } - -} - -Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2.WebApplication_1 .[#707070,thickness=2].> Live.AmazonWebServices.USEast1.AmazonRDS.MySQL.Database_1 : "Reads from and writes to\n[MySQL Protocol/SSL]" -Live.AmazonWebServices.USEast1.Route53 .[#707070,thickness=2].> Live.AmazonWebServices.USEast1.ElasticLoadBalancer : "Forwards requests to\n[HTTPS]" -Live.AmazonWebServices.USEast1.ElasticLoadBalancer .[#707070,thickness=2].> Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2.WebApplication_1 : "Forwards requests to\n[HTTPS]" -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/component-view-with-external-components-1.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/component-view-with-external-components-1.puml deleted file mode 100644 index 524f39cc9..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/component-view-with-external-components-1.puml +++ /dev/null @@ -1,56 +0,0 @@ -@startuml -set separator none -title Software System 1 - Container 1 - Components - -top to bottom direction - -skinparam { - arrowFontSize 10 - defaultTextAlignment center - wrapWidth 200 - maxMessageSize 100 -} - -hide stereotype - -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BorderColor #9a9a9a - FontColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BorderColor #9a9a9a - FontColor #9a9a9a - shadowing false -} - -rectangle "Container 1\n[Container]" <> { - rectangle "==Component 1\n[Component]" <> as SoftwareSystem1.Container1.Component1 - rectangle "==Component 2\n[Component]" <> as SoftwareSystem1.Container1.Component2 -} - -rectangle "Container 2\n[Container]" <> { - rectangle "==Component 3\n[Component]" <> as SoftwareSystem2.Container2.Component3 -} - -SoftwareSystem1.Container1.Component1 .[#707070,thickness=2].> SoftwareSystem1.Container1.Component2 : "Uses" -SoftwareSystem1.Container1.Component2 .[#707070,thickness=2].> SoftwareSystem2.Container2.Component3 : "Uses" -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/component-view-with-external-components-2.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/component-view-with-external-components-2.puml deleted file mode 100644 index 4888c4988..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/component-view-with-external-components-2.puml +++ /dev/null @@ -1,62 +0,0 @@ -@startuml -set separator none -title Software System 1 - Container 1 - Components - -top to bottom direction - -skinparam { - arrowFontSize 10 - defaultTextAlignment center - wrapWidth 200 - maxMessageSize 100 -} - -hide stereotype - -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BorderColor #9a9a9a - FontColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BorderColor #9a9a9a - FontColor #9a9a9a - shadowing false -} - -rectangle "Software System 1\n[Software System]" <> { - rectangle "Container 1\n[Container]" <> { - rectangle "==Component 1\n[Component]" <> as SoftwareSystem1.Container1.Component1 - rectangle "==Component 2\n[Component]" <> as SoftwareSystem1.Container1.Component2 - } - - } - -rectangle "Software System 2\n[Software System]" <> { - rectangle "Container 2\n[Container]" <> { - rectangle "==Component 3\n[Component]" <> as SoftwareSystem2.Container2.Component3 - } - - } - -SoftwareSystem1.Container1.Component1 .[#707070,thickness=2].> SoftwareSystem1.Container1.Component2 : "Uses" -SoftwareSystem1.Container1.Component2 .[#707070,thickness=2].> SoftwareSystem2.Container2.Component3 : "Uses" -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-container-scoped-with-groups.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-container-scoped-with-groups.puml deleted file mode 100644 index 304b863f3..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-container-scoped-with-groups.puml +++ /dev/null @@ -1,62 +0,0 @@ -@startuml -set separator none -title A - Dynamic - -top to bottom direction - -skinparam { - arrowFontSize 10 - defaultTextAlignment center - wrapWidth 200 - maxMessageSize 100 -} - -hide stereotype - -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BorderColor #9a9a9a - FontColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BorderColor #9a9a9a - FontColor #9a9a9a - shadowing false -} - -rectangle "A\n[Container]" <> { - rectangle "Group 1" <> as group1 { - skinparam RectangleBorderColor<> #cccccc - skinparam RectangleFontColor<> #cccccc - skinparam RectangleBorderStyle<> dashed - - rectangle "==A\n[Component]" <> as A.A.A - } - -} - -rectangle "B\n[Container]" <> { - rectangle "Group 2" <> as group2 { - skinparam RectangleBorderColor<> #cccccc - skinparam RectangleFontColor<> #cccccc - skinparam RectangleBorderStyle<> dashed - - rectangle "==B\n[Component]" <> as B.B.B - } - -} - -A.A.A .[#707070,thickness=2].> B.B.B : "1. Uses" -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-software-system-scoped-with-groups.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-software-system-scoped-with-groups.puml deleted file mode 100644 index a430197fb..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-software-system-scoped-with-groups.puml +++ /dev/null @@ -1,62 +0,0 @@ -@startuml -set separator none -title A - Dynamic - -top to bottom direction - -skinparam { - arrowFontSize 10 - defaultTextAlignment center - wrapWidth 200 - maxMessageSize 100 -} - -hide stereotype - -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} -skinparam rectangle<
    > { - BorderColor #9a9a9a - FontColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BorderColor #9a9a9a - FontColor #9a9a9a - shadowing false -} - -rectangle "A\n[Software System]" <> { - rectangle "Group 1" <> as group1 { - skinparam RectangleBorderColor<> #cccccc - skinparam RectangleFontColor<> #cccccc - skinparam RectangleBorderStyle<> dashed - - rectangle "==A\n[Container]" <> as A.A - } - -} - -rectangle "B\n[Software System]" <> { - rectangle "Group 2" <> as group2 { - skinparam RectangleBorderColor<> #cccccc - skinparam RectangleFontColor<> #cccccc - skinparam RectangleBorderStyle<> dashed - - rectangle "==B\n[Container]" <> as B.B - } - -} - -A.A .[#707070,thickness=2].> B.B : "1. Uses" -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-unscoped-with-groups.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-unscoped-with-groups.puml deleted file mode 100644 index 8dc424024..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-unscoped-with-groups.puml +++ /dev/null @@ -1,46 +0,0 @@ -@startuml -set separator none -title Dynamic - -top to bottom direction - -skinparam { - arrowFontSize 10 - defaultTextAlignment center - wrapWidth 200 - maxMessageSize 100 -} - -hide stereotype - -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} - -rectangle "Group 1" <> as group1 { - skinparam RectangleBorderColor<> #cccccc - skinparam RectangleFontColor<> #cccccc - skinparam RectangleBorderStyle<> dashed - - rectangle "==A\n[Software System]" <> as A -} - -rectangle "Group 2" <> as group2 { - skinparam RectangleBorderColor<> #cccccc - skinparam RectangleFontColor<> #cccccc - skinparam RectangleBorderStyle<> dashed - - rectangle "==B\n[Software System]" <> as B -} - -A .[#707070,thickness=2].> B : "1. Uses" -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-with-external-components-1.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-with-external-components-1.puml deleted file mode 100644 index ba9a23a8a..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-with-external-components-1.puml +++ /dev/null @@ -1,56 +0,0 @@ -@startuml -set separator none -title Container 1 - Dynamic - -top to bottom direction - -skinparam { - arrowFontSize 10 - defaultTextAlignment center - wrapWidth 200 - maxMessageSize 100 -} - -hide stereotype - -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BorderColor #9a9a9a - FontColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BorderColor #9a9a9a - FontColor #9a9a9a - shadowing false -} - -rectangle "Container 1\n[Container]" <> { - rectangle "==Component 1\n[Component]" <> as SoftwareSystem1.Container1.Component1 - rectangle "==Component 2\n[Component]" <> as SoftwareSystem1.Container1.Component2 -} - -rectangle "Container 2\n[Container]" <> { - rectangle "==Component 3\n[Component]" <> as SoftwareSystem2.Container2.Component3 -} - -SoftwareSystem1.Container1.Component1 .[#707070,thickness=2].> SoftwareSystem1.Container1.Component2 : "1. Uses" -SoftwareSystem1.Container1.Component2 .[#707070,thickness=2].> SoftwareSystem2.Container2.Component3 : "2. Uses" -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-with-external-components-2.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-with-external-components-2.puml deleted file mode 100644 index 588a26fb6..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/dynamic-view-with-external-components-2.puml +++ /dev/null @@ -1,62 +0,0 @@ -@startuml -set separator none -title Container 1 - Dynamic - -top to bottom direction - -skinparam { - arrowFontSize 10 - defaultTextAlignment center - wrapWidth 200 - maxMessageSize 100 -} - -hide stereotype - -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BorderColor #9a9a9a - FontColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BorderColor #9a9a9a - FontColor #9a9a9a - shadowing false -} - -rectangle "Software System 1\n[Software System]" <> { - rectangle "Container 1\n[Container]" <> { - rectangle "==Component 1\n[Component]" <> as SoftwareSystem1.Container1.Component1 - rectangle "==Component 2\n[Component]" <> as SoftwareSystem1.Container1.Component2 - } - - } - -rectangle "Software System 2\n[Software System]" <> { - rectangle "Container 2\n[Container]" <> { - rectangle "==Component 3\n[Component]" <> as SoftwareSystem2.Container2.Component3 - } - - } - -SoftwareSystem1.Container1.Component1 .[#707070,thickness=2].> SoftwareSystem1.Container1.Component2 : "1. Uses" -SoftwareSystem1.Container1.Component2 .[#707070,thickness=2].> SoftwareSystem2.Container2.Component3 : "2. Uses" -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/group-styles-1.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/group-styles-1.puml deleted file mode 100644 index 997f1738d..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/group-styles-1.puml +++ /dev/null @@ -1,60 +0,0 @@ -@startuml -set separator none -title System Landscape - -top to bottom direction - -skinparam { - arrowFontSize 10 - defaultTextAlignment center - wrapWidth 200 - maxMessageSize 100 -} - -hide stereotype - -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} - -rectangle "Group 1\n\n" <> as group1 { - skinparam RectangleBorderColor<> #111111 - skinparam RectangleFontColor<> #111111 - skinparam RectangleBorderStyle<> dashed - - rectangle "==User 1\n[Person]" <> as User1 -} - -rectangle "Group 2\n\n" <> as group2 { - skinparam RectangleBorderColor<> #222222 - skinparam RectangleFontColor<> #222222 - skinparam RectangleBorderStyle<> dashed - - rectangle "==User 2\n[Person]" <> as User2 -} - -rectangle "Group 3" <> as group3 { - skinparam RectangleBorderColor<> #cccccc - skinparam RectangleFontColor<> #cccccc - skinparam RectangleBorderStyle<> dashed - - rectangle "==User 3\n[Person]" <> as User3 -} - - -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/group-styles-2.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/group-styles-2.puml deleted file mode 100644 index 82c0a1b75..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/group-styles-2.puml +++ /dev/null @@ -1,60 +0,0 @@ -@startuml -set separator none -title System Landscape - -top to bottom direction - -skinparam { - arrowFontSize 10 - defaultTextAlignment center - wrapWidth 200 - maxMessageSize 100 -} - -hide stereotype - -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} - -rectangle "Group 1\n\n" <> as group1 { - skinparam RectangleBorderColor<> #111111 - skinparam RectangleFontColor<> #111111 - skinparam RectangleBorderStyle<> dashed - - rectangle "==User 1\n[Person]" <> as User1 -} - -rectangle "Group 2\n\n" <> as group2 { - skinparam RectangleBorderColor<> #222222 - skinparam RectangleFontColor<> #222222 - skinparam RectangleBorderStyle<> dashed - - rectangle "==User 2\n[Person]" <> as User2 -} - -rectangle "Group 3" <> as group3 { - skinparam RectangleBorderColor<> #aabbcc - skinparam RectangleFontColor<> #aabbcc - skinparam RectangleBorderStyle<> dashed - - rectangle "==User 3\n[Person]" <> as User3 -} - - -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-Components.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-Components.puml deleted file mode 100644 index 3a899a68f..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-Components.puml +++ /dev/null @@ -1,58 +0,0 @@ -@startuml -set separator none -title D - F - Components - -top to bottom direction -skinparam ranksep 60 -skinparam nodesep 30 - -skinparam { - arrowFontSize 10 - defaultTextAlignment center - wrapWidth 200 - maxMessageSize 100 -} - -hide stereotype - -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BorderColor #9a9a9a - FontColor #9a9a9a - shadowing false -} - -rectangle "==C\n[Software System]" <> as C - -rectangle "F\n[Container]" <> { - rectangle "Group 5" <> as group1 { - skinparam RectangleBorderColor<> #cccccc - skinparam RectangleFontColor<> #cccccc - skinparam RectangleBorderStyle<> dashed - - rectangle "==H\n[Component]" <> as D.F.H - } - - rectangle "==G\n[Component]" <> as D.F.G -} - -C .[#707070,thickness=2].> D.F.G : "" -C .[#707070,thickness=2].> D.F.H : "" -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-Containers.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-Containers.puml deleted file mode 100644 index 6b63e7509..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-Containers.puml +++ /dev/null @@ -1,58 +0,0 @@ -@startuml -set separator none -title D - Containers - -top to bottom direction -skinparam ranksep 60 -skinparam nodesep 30 - -skinparam { - arrowFontSize 10 - defaultTextAlignment center - wrapWidth 200 - maxMessageSize 100 -} - -hide stereotype - -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BorderColor #9a9a9a - FontColor #9a9a9a - shadowing false -} - -rectangle "==C\n[Software System]" <> as C - -rectangle "D\n[Software System]" <> { - rectangle "Group 4" <> as group1 { - skinparam RectangleBorderColor<> #cccccc - skinparam RectangleFontColor<> #cccccc - skinparam RectangleBorderStyle<> dashed - - rectangle "==F\n[Container]" <> as D.F - } - - rectangle "==E\n[Container]" <> as D.E -} - -C .[#707070,thickness=2].> D.E : "" -C .[#707070,thickness=2].> D.F : "" -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-SystemLandscape.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-SystemLandscape.puml deleted file mode 100644 index 09aa97ddd..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/groups-SystemLandscape.puml +++ /dev/null @@ -1,72 +0,0 @@ -@startuml -set separator none -title System Landscape - -top to bottom direction -skinparam ranksep 60 -skinparam nodesep 30 - -skinparam { - arrowFontSize 10 - defaultTextAlignment center - wrapWidth 200 - maxMessageSize 100 -} - -hide stereotype - -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} - -rectangle "Group 1" <> as group1 { - skinparam RectangleBorderColor<> #cccccc - skinparam RectangleFontColor<> #cccccc - skinparam RectangleBorderStyle<> dashed - - rectangle "==B\n[Software System]" <> as B -} - -rectangle "Group 2" <> as group2 { - skinparam RectangleBorderColor<> #cccccc - skinparam RectangleFontColor<> #cccccc - skinparam RectangleBorderStyle<> dashed - - rectangle "==C\n[Software System]" <> as C - rectangle "Group 3" <> as group3 { - skinparam RectangleBorderColor<> #cccccc - skinparam RectangleFontColor<> #cccccc - skinparam RectangleBorderStyle<> dashed - - rectangle "==D\n[Software System]" <> as D - } - -} - -rectangle "==A\n[Software System]" <> as A - -B .[#707070,thickness=2].> C : "" -C .[#707070,thickness=2].> D : "" -A .[#707070,thickness=2].> B : "" -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/nested-groups.puml b/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/nested-groups.puml deleted file mode 100644 index 04e94c3e8..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/structurizr/nested-groups.puml +++ /dev/null @@ -1,88 +0,0 @@ -@startuml -set separator none -title System Landscape - -top to bottom direction - -skinparam { - arrowFontSize 10 - defaultTextAlignment center - wrapWidth 200 - maxMessageSize 100 -} - -hide stereotype - -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} -skinparam rectangle<> { - BackgroundColor #dddddd - FontColor #000000 - BorderColor #9a9a9a - shadowing false -} - -rectangle "Organisation 1" <> as group1 { - skinparam RectangleBorderColor<> #cccccc - skinparam RectangleFontColor<> #cccccc - skinparam RectangleBorderStyle<> dashed - - rectangle "==Organisation 1\n[Software System]" <> as Organisation1 - rectangle "Department 1" <> as group2 { - skinparam RectangleBorderColor<> #cccccc - skinparam RectangleFontColor<> #cccccc - skinparam RectangleBorderStyle<> dashed - - rectangle "==Department 1\n[Software System]" <> as Department1 - rectangle "Team 1" <> as group3 { - skinparam RectangleBorderColor<> #ff0000 - skinparam RectangleFontColor<> #ff0000 - skinparam RectangleBorderStyle<> dashed - - rectangle "==Team 1\n[Software System]" <> as Team1 - } - - rectangle "Team 2" <> as group4 { - skinparam RectangleBorderColor<> #0000ff - skinparam RectangleFontColor<> #0000ff - skinparam RectangleBorderStyle<> dashed - - rectangle "==Team 2\n[Software System]" <> as Team2 - } - - } - -} - -rectangle "Organisation 2" <> as group5 { - skinparam RectangleBorderColor<> #cccccc - skinparam RectangleFontColor<> #cccccc - skinparam RectangleBorderStyle<> dashed - - rectangle "==Organisation 2\n[Software System]" <> as Organisation2 -} - - -@enduml \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/websequencediagrams/36141-SignIn.wsd b/structurizr-export/src/test/java/com/structurizr/export/websequencediagrams/36141-SignIn.wsd deleted file mode 100644 index 240ee181c..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/websequencediagrams/36141-SignIn.wsd +++ /dev/null @@ -1,13 +0,0 @@ -title API Application - Dynamic - SignIn - -participant <>\nSingle-Page Application as Single-Page Application -participant <>\nSign In Controller as Sign In Controller -participant <>\nSecurity Component as Security Component -participant <>\nDatabase as Database - -Single-Page Application->Sign In Controller: Submits credentials to -Sign In Controller->Security Component: Validates credentials using -Security Component->Database: select * from users where username = ? -Database-->Security Component: Returns user data to -Security Component-->Sign In Controller: Returns true if the hashed password matches -Sign In Controller-->Single-Page Application: Sends back an authentication token to \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/websequencediagrams/WebSequenceDiagramsExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/websequencediagrams/WebSequenceDiagramsExporterTests.java index e0a4c5229..25319e27f 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/websequencediagrams/WebSequenceDiagramsExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/websequencediagrams/WebSequenceDiagramsExporterTests.java @@ -17,15 +17,28 @@ public class WebSequenceDiagramsExporterTests extends AbstractExporterTests { @Test public void test_BigBankPlcExample() throws Exception { - Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/structurizr-36141-workspace.json")); + Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/big-bank-plc.json")); WebSequenceDiagramsExporter exporter = new WebSequenceDiagramsExporter(); Collection diagrams = exporter.export(workspace); assertEquals(1, diagrams.size()); Diagram diagram = diagrams.stream().filter(d -> d.getKey().equals("SignIn")).findFirst().get(); - String expected = readFile(new File("./src/test/java/com/structurizr/export/websequencediagrams/36141-SignIn.wsd")); - assertEquals(expected, diagram.getDefinition()); + assertEquals(""" + title API Application - Dynamic - SignIn + + participant <>\\nSingle-Page Application as Single-Page Application + participant <>\\nSign In Controller as Sign In Controller + participant <>\\nSecurity Component as Security Component + participant <>\\nDatabase as Database + + Single-Page Application->Sign In Controller: Submits credentials to + Sign In Controller->Security Component: Validates credentials using + Security Component->Database: select * from users where username = ? + Database-->Security Component: Returns user data to + Security Component-->Sign In Controller: Returns true if the hashed password matches + Sign In Controller-->Single-Page Application: Sends back an authentication token to + """, diagram.getDefinition()); } @Test @@ -42,12 +55,14 @@ public void test_dynamicViewThatDoeNotOverrideRelationshipDescriptions() throws Collection diagrams = exporter.export(workspace); Diagram diagram = diagrams.iterator().next(); - assertEquals("title Dynamic - key\n" + - "\n" + - "participant <>\\nA as A\n" + - "participant <>\\nB as B\n" + - "\n" + - "A->B: Uses", diagram.getDefinition()); + assertEquals(""" + title Dynamic - key + + participant <>\\nA as A + participant <>\\nB as B + + A->B: Uses + """, diagram.getDefinition()); } } \ No newline at end of file diff --git a/structurizr-export/src/test/resources/amazon-web-services.dsl b/structurizr-export/src/test/resources/amazon-web-services.dsl new file mode 100644 index 000000000..316375363 --- /dev/null +++ b/structurizr-export/src/test/resources/amazon-web-services.dsl @@ -0,0 +1,83 @@ +workspace "Amazon Web Services Example" "An example AWS deployment architecture." { + + !identifiers hierarchical + + model { + x = softwaresystem "X" { + wa = container "Web Application" { + technology "Java and Spring Boot" + tags "Application" + } + db = container "Database Schema" { + tags "Database" + } + + wa -> db "Reads from and writes to" "MySQL Protocol/SSL" + } + + live = deploymentEnvironment "Live" { + deploymentNode "Amazon Web Services" { + tags "Amazon Web Services - Cloud" + + region = deploymentNode "US-East-1" { + tags "Amazon Web Services - Region" + + dns = infrastructureNode "DNS router" { + technology "Route 53" + description "Routes incoming requests based upon domain name." + tags "Amazon Web Services - Route 53" + } + + lb = infrastructureNode "Load Balancer" { + technology "Elastic Load Balancer" + description "Automatically distributes incoming application traffic." + tags "Amazon Web Services - Elastic Load Balancing" + dns -> this "Forwards requests to" "HTTPS" + } + + deploymentNode "Autoscaling group" { + tags "Amazon Web Services - Auto Scaling" + + deploymentNode "Amazon EC2 - Ubuntu server" { + tags "Amazon Web Services - EC2" + + webApplicationInstance = containerInstance x.wa { + lb -> this "Forwards requests to" "HTTPS" + } + } + } + + deploymentNode "Amazon RDS" { + tags "Amazon Web Services - RDS" + + deploymentNode "MySQL" { + tags "Amazon Web Services - RDS MySQL instance" + + databaseInstance = containerInstance x.db + } + } + + } + } + } + } + + views { + deployment x live "AmazonWebServicesDeployment" { + include * + autolayout lr + } + + styles { + element "Application" { + shape roundedbox + } + element "Database" { + shape cylinder + } + } + + themes https://static.structurizr.com/themes/amazon-web-services-2020.04.30/theme.json + } + +} \ No newline at end of file diff --git a/structurizr-export/src/test/resources/amazon-web-services.json b/structurizr-export/src/test/resources/amazon-web-services.json new file mode 100644 index 000000000..5214dea5f --- /dev/null +++ b/structurizr-export/src/test/resources/amazon-web-services.json @@ -0,0 +1,257 @@ +{ + "configuration" : { }, + "description" : "An example AWS deployment architecture.", + "documentation" : { }, + "id" : 0, + "model" : { + "deploymentNodes" : [ { + "children" : [ { + "children" : [ { + "children" : [ { + "containerInstances" : [ { + "containerId" : "2", + "deploymentGroups" : [ "Default" ], + "environment" : "Live", + "id" : "12", + "instanceId" : 1, + "properties" : { + "structurizr.dsl.identifier" : "live.4955ad68-6fbf-42a0-a268-21d790224ef4.region.d846537e-73a4-40fa-b64a-0abf118a5241.53a89646-c975-4c46-a3d5-422634503b7e.webapplicationinstance" + }, + "relationships" : [ { + "description" : "Reads from and writes to", + "destinationId" : "16", + "id" : "17", + "linkedRelationshipId" : "4", + "sourceId" : "12", + "technology" : "MySQL Protocol/SSL" + } ], + "tags" : "Container Instance" + } ], + "environment" : "Live", + "id" : "11", + "instances" : "1", + "name" : "Amazon EC2 - Ubuntu server", + "properties" : { + "structurizr.dsl.identifier" : "live.4955ad68-6fbf-42a0-a268-21d790224ef4.region.d846537e-73a4-40fa-b64a-0abf118a5241.53a89646-c975-4c46-a3d5-422634503b7e" + }, + "tags" : "Element,Deployment Node,Amazon Web Services - EC2" + } ], + "environment" : "Live", + "id" : "10", + "instances" : "1", + "name" : "Autoscaling group", + "properties" : { + "structurizr.dsl.identifier" : "live.4955ad68-6fbf-42a0-a268-21d790224ef4.region.d846537e-73a4-40fa-b64a-0abf118a5241" + }, + "tags" : "Element,Deployment Node,Amazon Web Services - Auto Scaling" + }, { + "children" : [ { + "containerInstances" : [ { + "containerId" : "3", + "deploymentGroups" : [ "Default" ], + "environment" : "Live", + "id" : "16", + "instanceId" : 1, + "properties" : { + "structurizr.dsl.identifier" : "live.4955ad68-6fbf-42a0-a268-21d790224ef4.region.989c5763-5a99-4014-9a7e-397dbc6f755e.91b9b9b5-2ca3-4bcb-8395-9d9f4d49de40.databaseinstance" + }, + "tags" : "Container Instance" + } ], + "environment" : "Live", + "id" : "15", + "instances" : "1", + "name" : "MySQL", + "properties" : { + "structurizr.dsl.identifier" : "live.4955ad68-6fbf-42a0-a268-21d790224ef4.region.989c5763-5a99-4014-9a7e-397dbc6f755e.91b9b9b5-2ca3-4bcb-8395-9d9f4d49de40" + }, + "tags" : "Element,Deployment Node,Amazon Web Services - RDS MySQL instance" + } ], + "environment" : "Live", + "id" : "14", + "instances" : "1", + "name" : "Amazon RDS", + "properties" : { + "structurizr.dsl.identifier" : "live.4955ad68-6fbf-42a0-a268-21d790224ef4.region.989c5763-5a99-4014-9a7e-397dbc6f755e" + }, + "tags" : "Element,Deployment Node,Amazon Web Services - RDS" + } ], + "environment" : "Live", + "id" : "6", + "infrastructureNodes" : [ { + "description" : "Routes incoming requests based upon domain name.", + "environment" : "Live", + "id" : "7", + "name" : "DNS router", + "properties" : { + "structurizr.dsl.identifier" : "live.4955ad68-6fbf-42a0-a268-21d790224ef4.region.dns" + }, + "relationships" : [ { + "description" : "Forwards requests to", + "destinationId" : "8", + "id" : "9", + "sourceId" : "7", + "tags" : "Relationship", + "technology" : "HTTPS" + } ], + "tags" : "Element,Infrastructure Node,Amazon Web Services - Route 53", + "technology" : "Route 53" + }, { + "description" : "Automatically distributes incoming application traffic.", + "environment" : "Live", + "id" : "8", + "name" : "Load Balancer", + "properties" : { + "structurizr.dsl.identifier" : "live.4955ad68-6fbf-42a0-a268-21d790224ef4.region.lb" + }, + "relationships" : [ { + "description" : "Forwards requests to", + "destinationId" : "12", + "id" : "13", + "sourceId" : "8", + "tags" : "Relationship", + "technology" : "HTTPS" + } ], + "tags" : "Element,Infrastructure Node,Amazon Web Services - Elastic Load Balancing", + "technology" : "Elastic Load Balancer" + } ], + "instances" : "1", + "name" : "US-East-1", + "properties" : { + "structurizr.dsl.identifier" : "live.4955ad68-6fbf-42a0-a268-21d790224ef4.region" + }, + "tags" : "Element,Deployment Node,Amazon Web Services - Region" + } ], + "environment" : "Live", + "id" : "5", + "instances" : "1", + "name" : "Amazon Web Services", + "properties" : { + "structurizr.dsl.identifier" : "live.4955ad68-6fbf-42a0-a268-21d790224ef4" + }, + "tags" : "Element,Deployment Node,Amazon Web Services - Cloud" + } ], + "softwareSystems" : [ { + "containers" : [ { + "documentation" : { }, + "id" : "2", + "name" : "Web Application", + "properties" : { + "structurizr.dsl.identifier" : "x.wa" + }, + "relationships" : [ { + "description" : "Reads from and writes to", + "destinationId" : "3", + "id" : "4", + "sourceId" : "2", + "tags" : "Relationship", + "technology" : "MySQL Protocol/SSL" + } ], + "tags" : "Element,Container,Application", + "technology" : "Java and Spring Boot" + }, { + "documentation" : { }, + "id" : "3", + "name" : "Database Schema", + "properties" : { + "structurizr.dsl.identifier" : "x.db" + }, + "tags" : "Element,Container,Database" + } ], + "documentation" : { }, + "id" : "1", + "location" : "Unspecified", + "name" : "X", + "properties" : { + "structurizr.dsl.identifier" : "x" + }, + "tags" : "Element,Software System" + } ] + }, + "name" : "Amazon Web Services Example", + "properties" : { + "structurizr.inspection.error" : "23", + "structurizr.dsl" : "d29ya3NwYWNlICJBbWF6b24gV2ViIFNlcnZpY2VzIEV4YW1wbGUiICJBbiBleGFtcGxlIEFXUyBkZXBsb3ltZW50IGFyY2hpdGVjdHVyZS4iIHsKCiAgICAhaWRlbnRpZmllcnMgaGllcmFyY2hpY2FsCgogICAgbW9kZWwgewogICAgICAgIHggPSBzb2Z0d2FyZXN5c3RlbSAiWCIgewogICAgICAgICAgICB3YSA9IGNvbnRhaW5lciAiV2ViIEFwcGxpY2F0aW9uIiB7CiAgICAgICAgICAgICAgICB0ZWNobm9sb2d5ICJKYXZhIGFuZCBTcHJpbmcgQm9vdCIKICAgICAgICAgICAgICAgIHRhZ3MgIkFwcGxpY2F0aW9uIgogICAgICAgICAgICB9CiAgICAgICAgICAgIGRiID0gY29udGFpbmVyICJEYXRhYmFzZSBTY2hlbWEiIHsKICAgICAgICAgICAgICAgIHRhZ3MgIkRhdGFiYXNlIgogICAgICAgICAgICB9CgogICAgICAgICAgICB3YSAtPiBkYiAiUmVhZHMgZnJvbSBhbmQgd3JpdGVzIHRvIiAiTXlTUUwgUHJvdG9jb2wvU1NMIgogICAgICAgIH0KCiAgICAgICAgbGl2ZSA9IGRlcGxveW1lbnRFbnZpcm9ubWVudCAiTGl2ZSIgewogICAgICAgICAgICBkZXBsb3ltZW50Tm9kZSAiQW1hem9uIFdlYiBTZXJ2aWNlcyIgewogICAgICAgICAgICAgICAgdGFncyAiQW1hem9uIFdlYiBTZXJ2aWNlcyAtIENsb3VkIgoKICAgICAgICAgICAgICAgIHJlZ2lvbiA9IGRlcGxveW1lbnROb2RlICJVUy1FYXN0LTEiIHsKICAgICAgICAgICAgICAgICAgICB0YWdzICJBbWF6b24gV2ViIFNlcnZpY2VzIC0gUmVnaW9uIgoKICAgICAgICAgICAgICAgICAgICBkbnMgPSBpbmZyYXN0cnVjdHVyZU5vZGUgIkROUyByb3V0ZXIiIHsKICAgICAgICAgICAgICAgICAgICAgICAgdGVjaG5vbG9neSAiUm91dGUgNTMiCiAgICAgICAgICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uICJSb3V0ZXMgaW5jb21pbmcgcmVxdWVzdHMgYmFzZWQgdXBvbiBkb21haW4gbmFtZS4iCiAgICAgICAgICAgICAgICAgICAgICAgIHRhZ3MgIkFtYXpvbiBXZWIgU2VydmljZXMgLSBSb3V0ZSA1MyIKICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgIGxiID0gaW5mcmFzdHJ1Y3R1cmVOb2RlICJMb2FkIEJhbGFuY2VyIiB7CiAgICAgICAgICAgICAgICAgICAgICAgIHRlY2hub2xvZ3kgIkVsYXN0aWMgTG9hZCBCYWxhbmNlciIKICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb24gIkF1dG9tYXRpY2FsbHkgZGlzdHJpYnV0ZXMgaW5jb21pbmcgYXBwbGljYXRpb24gdHJhZmZpYy4iCiAgICAgICAgICAgICAgICAgICAgICAgIHRhZ3MgIkFtYXpvbiBXZWIgU2VydmljZXMgLSBFbGFzdGljIExvYWQgQmFsYW5jaW5nIgogICAgICAgICAgICAgICAgICAgICAgICBkbnMgLT4gdGhpcyAiRm9yd2FyZHMgcmVxdWVzdHMgdG8iICJIVFRQUyIKICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgIGRlcGxveW1lbnROb2RlICJBdXRvc2NhbGluZyBncm91cCIgewogICAgICAgICAgICAgICAgICAgICAgICB0YWdzICJBbWF6b24gV2ViIFNlcnZpY2VzIC0gQXV0byBTY2FsaW5nIgoKICAgICAgICAgICAgICAgICAgICAgICAgZGVwbG95bWVudE5vZGUgIkFtYXpvbiBFQzIgLSBVYnVudHUgc2VydmVyIiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0YWdzICJBbWF6b24gV2ViIFNlcnZpY2VzIC0gRUMyIgoKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdlYkFwcGxpY2F0aW9uSW5zdGFuY2UgPSBjb250YWluZXJJbnN0YW5jZSB4LndhIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYiAtPiB0aGlzICJGb3J3YXJkcyByZXF1ZXN0cyB0byIgIkhUVFBTIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgICAgICBkZXBsb3ltZW50Tm9kZSAiQW1hem9uIFJEUyIgewogICAgICAgICAgICAgICAgICAgICAgICB0YWdzICJBbWF6b24gV2ViIFNlcnZpY2VzIC0gUkRTIgoKICAgICAgICAgICAgICAgICAgICAgICAgZGVwbG95bWVudE5vZGUgIk15U1FMIiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0YWdzICJBbWF6b24gV2ViIFNlcnZpY2VzIC0gUkRTIE15U1FMIGluc3RhbmNlIgoKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGFiYXNlSW5zdGFuY2UgPSBjb250YWluZXJJbnN0YW5jZSB4LmRiCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgfQogICAgfQoKICAgIHZpZXdzIHsKICAgICAgICBkZXBsb3ltZW50IHggbGl2ZSAiQW1hem9uV2ViU2VydmljZXNEZXBsb3ltZW50IiB7CiAgICAgICAgICAgIGluY2x1ZGUgKgogICAgICAgICAgICBhdXRvbGF5b3V0IGxyCiAgICAgICAgfQoKICAgICAgICBzdHlsZXMgewogICAgICAgICAgICBlbGVtZW50ICJBcHBsaWNhdGlvbiIgewogICAgICAgICAgICAgICAgc2hhcGUgcm91bmRlZGJveAogICAgICAgICAgICB9CiAgICAgICAgICAgIGVsZW1lbnQgIkRhdGFiYXNlIiB7CiAgICAgICAgICAgICAgICBzaGFwZSBjeWxpbmRlcgogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICB0aGVtZXMgaHR0cHM6Ly9zdGF0aWMuc3RydWN0dXJpenIuY29tL3RoZW1lcy9hbWF6b24td2ViLXNlcnZpY2VzLTIwMjAuMDQuMzAvdGhlbWUuanNvbgogICAgfQoKfQ==", + "structurizr.inspection.info" : "0", + "structurizr.inspection.ignore" : "0", + "structurizr.inspection.warning" : "0" + }, + "views" : { + "configuration" : { + "branding" : { }, + "styles" : { + "elements" : [ { + "shape" : "RoundedBox", + "tag" : "Application" + }, { + "shape" : "Cylinder", + "tag" : "Database" + } ] + }, + "terminology" : { }, + "themes" : [ "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/theme.json" ] + }, + "deploymentViews" : [ { + "automaticLayout" : { + "applied" : false, + "edgeSeparation" : 0, + "implementation" : "Graphviz", + "nodeSeparation" : 300, + "rankDirection" : "LeftRight", + "rankSeparation" : 300, + "vertices" : false + }, + "elements" : [ { + "id" : "5", + "x" : 0, + "y" : 0 + }, { + "id" : "6", + "x" : 0, + "y" : 0 + }, { + "id" : "7", + "x" : 0, + "y" : 0 + }, { + "id" : "8", + "x" : 0, + "y" : 0 + }, { + "id" : "10", + "x" : 0, + "y" : 0 + }, { + "id" : "11", + "x" : 0, + "y" : 0 + }, { + "id" : "12", + "x" : 0, + "y" : 0 + }, { + "id" : "14", + "x" : 0, + "y" : 0 + }, { + "id" : "15", + "x" : 0, + "y" : 0 + }, { + "id" : "16", + "x" : 0, + "y" : 0 + } ], + "environment" : "Live", + "key" : "AmazonWebServicesDeployment", + "order" : 1, + "relationships" : [ { + "id" : "13" + }, { + "id" : "17" + }, { + "id" : "9" + } ], + "softwareSystemId" : "1" + } ] + } +} \ No newline at end of file diff --git a/structurizr-export/src/test/resources/structurizr-36141-workspace.json b/structurizr-export/src/test/resources/big-bank-plc.json similarity index 100% rename from structurizr-export/src/test/resources/structurizr-36141-workspace.json rename to structurizr-export/src/test/resources/big-bank-plc.json diff --git a/structurizr-export/src/test/resources/structurizr-54915-workspace.json b/structurizr-export/src/test/resources/structurizr-54915-workspace.json deleted file mode 100644 index 28e62ac94..000000000 --- a/structurizr-export/src/test/resources/structurizr-54915-workspace.json +++ /dev/null @@ -1,353 +0,0 @@ -{ - "id": 54915, - "name": "Amazon Web Services Example", - "description": "An example AWS deployment architecture.", - "model": { - "softwareSystems": [ - { - "id": "1", - "tags": "Element,Software System", - "name": "Spring PetClinic", - "description": "Allows employees to view and manage information regarding the veterinarians, the clients, and their pets.", - "location": "Unspecified", - "containers": [ - { - "id": "3", - "tags": "Element,Container,Database", - "name": "Database", - "description": "Stores information regarding the veterinarians, the clients, and their pets.", - "technology": "Relational database schema" - }, - { - "id": "2", - "tags": "Element,Container,Application", - "name": "Web Application", - "description": "Allows employees to view and manage information regarding the veterinarians, the clients, and their pets.", - "relationships": [ - { - "id": "4", - "tags": "Relationship", - "sourceId": "2", - "destinationId": "3", - "description": "Reads from and writes to", - "technology": "MySQL Protocol/SSL" - } - ], - "technology": "Java and Spring Boot" - } - ], - "documentation": {} - } - ], - "deploymentNodes": [ - { - "id": "5", - "tags": "Element,Deployment Node,Amazon Web Services - Cloud", - "name": "Amazon Web Services", - "environment": "Live", - "instances": 1, - "children": [ - { - "id": "6", - "tags": "Element,Deployment Node,Amazon Web Services - Region", - "name": "US-East-1", - "environment": "Live", - "instances": 1, - "children": [ - { - "id": "12", - "tags": "Element,Deployment Node,Amazon Web Services - RDS", - "name": "Amazon RDS", - "environment": "Live", - "instances": 1, - "children": [ - { - "id": "13", - "tags": "Element,Deployment Node,Amazon Web Services - RDS MySQL instance", - "name": "MySQL", - "environment": "Live", - "instances": 1, - "containerInstances": [ - { - "id": "14", - "tags": "Container Instance", - "environment": "Live", - "deploymentGroups": [ - "Default" - ], - "instanceId": 1, - "containerId": "3" - } - ], - "children": [], - "softwareSystemInstances": [], - "infrastructureNodes": [] - } - ], - "softwareSystemInstances": [], - "containerInstances": [], - "infrastructureNodes": [] - }, - { - "id": "9", - "tags": "Element,Deployment Node,Amazon Web Services - Auto Scaling", - "name": "Autoscaling group", - "environment": "Live", - "instances": 1, - "children": [ - { - "id": "10", - "tags": "Element,Deployment Node,Amazon Web Services - EC2", - "name": "Amazon EC2", - "environment": "Live", - "instances": 1, - "containerInstances": [ - { - "id": "11", - "tags": "Container Instance", - "relationships": [ - { - "id": "15", - "sourceId": "11", - "destinationId": "14", - "description": "Reads from and writes to", - "technology": "MySQL Protocol/SSL", - "linkedRelationshipId": "4" - } - ], - "environment": "Live", - "deploymentGroups": [ - "Default" - ], - "instanceId": 1, - "containerId": "2" - } - ], - "children": [], - "softwareSystemInstances": [], - "infrastructureNodes": [] - } - ], - "softwareSystemInstances": [], - "containerInstances": [], - "infrastructureNodes": [] - } - ], - "infrastructureNodes": [ - { - "id": "8", - "tags": "Element,Infrastructure Node,Amazon Web Services - Elastic Load Balancing", - "name": "Elastic Load Balancer", - "description": "Automatically distributes incoming application traffic.", - "relationships": [ - { - "id": "17", - "tags": "Relationship", - "sourceId": "8", - "destinationId": "11", - "description": "Forwards requests to", - "technology": "HTTPS" - } - ], - "environment": "Live" - }, - { - "id": "7", - "tags": "Element,Infrastructure Node,Amazon Web Services - Route 53", - "name": "Route 53", - "description": "Highly available and scalable cloud DNS service.", - "relationships": [ - { - "id": "16", - "tags": "Relationship", - "sourceId": "7", - "destinationId": "8", - "description": "Forwards requests to", - "technology": "HTTPS" - } - ], - "environment": "Live" - } - ], - "softwareSystemInstances": [], - "containerInstances": [] - } - ], - "softwareSystemInstances": [], - "containerInstances": [], - "infrastructureNodes": [] - } - ], - "customElements": [], - "people": [] - }, - "documentation": { - "sections": [], - "decisions": [], - "images": [] - }, - "views": { - "deploymentViews": [ - { - "softwareSystemId": "1", - "key": "AmazonWebServicesDeployment", - "order": 1, - "paperSize": "A3_Landscape", - "dimensions": { - "width": 3925, - "height": 1816 - }, - "automaticLayout": { - "implementation": "Graphviz", - "rankDirection": "LeftRight", - "rankSeparation": 300, - "nodeSeparation": 300, - "edgeSeparation": 0, - "vertices": false - }, - "environment": "Live", - "animations": [ - { - "order": 1, - "elements": [ - "5", - "6", - "7" - ] - }, - { - "order": 2, - "elements": [ - "8" - ], - "relationships": [ - "16" - ] - }, - { - "order": 3, - "elements": [ - "11", - "9", - "10" - ], - "relationships": [ - "17" - ] - }, - { - "order": 4, - "elements": [ - "12", - "13", - "14" - ], - "relationships": [ - "15" - ] - } - ], - "elements": [ - { - "id": "11", - "x": 1987, - "y": 672 - }, - { - "id": "12", - "x": 175, - "y": 175 - }, - { - "id": "13", - "x": 175, - "y": 175 - }, - { - "id": "14", - "x": 2887, - "y": 672 - }, - { - "id": "5", - "x": 175, - "y": 175 - }, - { - "id": "6", - "x": 175, - "y": 175 - }, - { - "id": "7", - "x": 487, - "y": 672 - }, - { - "id": "8", - "x": 1237, - "y": 672 - }, - { - "id": "9", - "x": 175, - "y": 175 - }, - { - "id": "10", - "x": 175, - "y": 175 - } - ], - "relationships": [ - { - "id": "17" - }, - { - "id": "16" - }, - { - "id": "15" - } - ] - } - ], - "configuration": { - "branding": {}, - "styles": { - "elements": [ - { - "tag": "Element", - "background": "#ffffff", - "shape": "RoundedBox" - }, - { - "tag": "Container", - "background": "#ffffff" - }, - { - "tag": "Application", - "background": "#ffffff" - }, - { - "tag": "Database", - "shape": "Cylinder" - } - ], - "relationships": [] - }, - "themes": [ - "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/theme.json" - ], - "terminology": {}, - "lastSavedView": "AmazonWebServicesDeployment" - }, - "customViews": [], - "systemLandscapeViews": [], - "systemContextViews": [], - "containerViews": [], - "componentViews": [], - "dynamicViews": [], - "filteredViews": [] - } -} \ No newline at end of file diff --git a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporter.java b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporter.java index 24176d02b..b7ae51afe 100644 --- a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporter.java +++ b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporter.java @@ -59,10 +59,27 @@ public void importDiagram(ImageView view, String content) throws Exception { String[] lines = content.split(NEWLINE); for (String line : lines) { if (line.startsWith(TITLE_STRING)) { - String title = line.substring(TITLE_STRING.length()); - view.setTitle(title); + view.setTitle(extractTitle(line)); } } } + private String extractTitle(String line) { + String title = line.substring(TITLE_STRING.length()); + + if (title.contains(NEWLINE)) { + title = title.split(NEWLINE)[0]; + } + + if (title.startsWith("") + 1); + + if (title.endsWith("")) { + title = title.substring(0, title.indexOf("")); + } + } + + return title; + } + } \ No newline at end of file diff --git a/structurizr-import/src/test/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporterTests.java b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporterTests.java index 0de289d83..7ca514921 100644 --- a/structurizr-import/src/test/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporterTests.java +++ b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporterTests.java @@ -63,7 +63,8 @@ public void importDiagram_AsInlinePNG() throws Exception { ImageView view = workspace.getViews().createImageView("key"); new PlantUMLImporter().importDiagram(view, new File("./src/test/resources/diagrams/plantuml/with-title.puml")); - assertEquals("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPUAAACdCAMAAABbyM5gAAAA0lBMVEUAAAAHBwgKCgoPDxAXFxcWFhgYGBgeHiAnJycrKysuLjE3Nzc0NDg7Oz8+PkJCQkJGRkpISEhOTlNXV1dUVFlfX19fX2VnZ2dlZWtoaG5paXBzc3NxcXh4eH9+foWBgYaFhY2MjIyMjJWPj5iWlpaTk5ybm5uYmKKnp6eioqymprCrq6upqbStrbi3t7e0tL+1tcC6urq5ucW8vMjFxcXCws7ExNDJycnIyNXNzdnU1NTR0d7W1uTb29vZ2efe3uzm5ubh4e/i4vDu7u739/f////69aUWAAAAKnRFWHRjb3B5bGVmdABHZW5lcmF0ZWQgYnkgaHR0cHM6Ly9wbGFudHVtbC5jb212zsofAAAA6WlUWHRwbGFudHVtbAABAAAAeJxFj8FOw0AMRO/+Ch/bQyooFKEcUCkFpJCIipLencSEFbvesPEG+Hu2SKhXz7wZz3pUChqdBTVqGff8GVlaxs5QH8ghf5MbLMPGN5jd4K01Sczxna31sGbpjizsLInWVYkTh9F4wfPF8my5WqwaVrqY1fIh/kuw9W4wqUWN4znMHncljj6Gv7pRg2miJngOBU2EL1GOvhyfB5Zi+/R/wHuZTPDiWBSKQ3UyXF1mG6NpQ0hv4KGCLb9RtJqI1ndG+hzr14fsGkqSPlKfslngzqfc8JO0PfwCQdBbUjEOHvMAAAloSURBVHja7V37V9rIHv8mhPASEBFxa61arVK756x6uttz3N17f7r/9t5zX+09217buj62rhbRAiJg5JkQQriT8EgIgkFDTDfzOWpm5vv9zsxnMvlmmBlG4m9gQ5APXQHMGrPGrDFrzBqzxqwxa8was7YhMGv7ALO2D6ieWLHgoKeMbogEQ6/D71xowUxew8tTs+besdIlNm9sBQpZN0COBVNZDy9PzXqPpcJUtSCOoxbP+AkzSd8CNesCzK8ANJsomElWqclVqa8f5ajH9bx7HXaFyDzEGW9MkR5wIW9CmF5FarkvVfA+86kMZRzlyCdyIM8JIUjkeaEl7eaKsnCdUS+7EhSnkrAUPcrRy6FOLu1Mc2fkC2d9X5yLKhn16su5iXKF1IZDWVN8EsIhgkCVSoCbLWV/RLVIotagedRH8wL6w+Q5lfSKLfEAFXIFjuPIMVbCPsVQxl4aYJ+WQlcsSsqUXFAqXW3L6a1cr9gKB25FkucKAsBusACVnb84203XznT6uPT+h4+ML6qoa/TbFeK/7TUkh7FePOLjcTLyLVlPwMKq+A/uKFZH7bBV+W+PiSIF4DenXrO5lXoc/D+QlR6RpJnutY4FEPPdStGjTufm59muBF3EV47XUNnm34mpBU15G28KvxbILVCra/SFV4EP2fSaU1vRwawXAucML2b4l1nkzXdQwjVcASyBL8ioTRQpQHAaAmxdSlohwQcplUi6wVrrnYqIvEaZldNDeTnNF0OGHQmiEQwAJYR8KK2mLc+9tl+A525Qq2v0UWwhC1dRbUUHs4ZQCLgPJQYaAKhiFEVLIYdaSfJ0ihSAbiU22iG1qBVVW9ffip6wmJGSpfQ2vBqJrE6raqXK1NMSqdU1+qQcbsANtRnA+vd5H7i9JRKCAKuotZqCFLoMQEnWFDgQpZAi7QIlpVC7NzUilbXc9iK8cmYyAFMAX9bFgqr7dCQ3QclU3AM3t/8zOUQdFXYJMKExHMY6fe52CRUIQSBYOMhTXH5+xecvxctVTpL6ufyvrJSBIu1aoqTzorc4P98rUllLQHfqtwnkx8AdzifzvKrkruQmKOV94Dw//ofdeTlEnX/tzYIvoDHUaqndW9TNFRDpDYCXESGZyLgmATY8kKUjkjTmhkIwLIUUaRffR6GQrtBakWItITQH+XP5RbYRIYW5EBD9kpvQyTSeh01ik2SOh6iH6Sy4v9MY9mkRPetcTfTSJdohh6sVqtDO3Yz7ZxTiHM6uXkfaZ6oRIWuVksi2sq+jxOY/+chGn+Rm9JU3QP3fbGSjzvuGGLbQOw4nfP0hJQ/3DXp9SRpRb4xsRxNJP1ngyWf9kpvRV94QdadzmOFNrAeAopx61PRj8pIRqfCa7/45aSuqi4+mh9sF9vx8jVnbB5i1fYBZ2weYtX1gT9b6xq23gsnq04uElHDzRJ6O1QeCWCZ0K5vEOiUu61PMsI+64ZM5Wp+RDP5kZQTtW2BMD69+o1MxWlbCjVFIA23k4oTZz7U1/Ig1aoFZY9aYNWZ9Xxg0SumgXAOC9jhUKX+4b98E0LxyyasVx84F+fdrY/05R4FALc8pKdf+260uDt3b0rWI3uDlkd7i1mAN/u+hcXA06xjJKOMvZTsrJBsjWVqENYIjmq2iG3yaFXxL6Nr8nIXo4jADnoklLzqsj6lFSKdrVPgpQDrFe55MjYH1GLwZm6a9qK+fhhaF9zWAbOFJ+PP5MIMv1Ew031l4LJbh9NC7NMujdvsUXPLuXusoclQYfa9LvwA4Xzigcf54BWbexGPg2CCgfj7MpV2GHY9Okl0n1jh7vNq6PnkKs9zpGPq80ax9L4BP7W1NVBvTqK8HqgAB9AlxMt0Y/KRfV1HThC+7rEvCTPtaPWo2+brxpA1nTaJXUOhfybWGnLOj1tqVgBz7YNYpOAIQuFLH2TfaGxnQlSAgOJpjfBjWLYjghQLiX/GgX5C2KbkG6jZy09Ii+5dkZ8+Mv/22m4DA3O1FWYK1WIJ6SpgGeupsynNeXkYdNTlXTc4MtrgQVqStKbVssz1ZQofPAsHGVcQ1feqbhAI7a33WlbdAetbQa2ht7w0JT8IAkbMTwT9kHiTjl0jDo+RFZ2ri+cH/KGEyAusHO5RIPjaetEErucdLNyRW+YlWm5YIZTdlXOH/aXBT1FiXtN0I+CrVHd6erIJhGM9zLcPrbQd0jEk1cLXdAD2m4ak9P3Nh1vYBZm0fYNZ3x1i+NGF51hMZnYoZrxJ28DqNZPBG9kqDdtkxOV3Lk8S0NdY0x7e3kAmZazcKxufN8ibbWYO1lYFZ2wfjYx022c4arO/qik1w4biH2wiYtbFgTLazBms8NrMaMGv7AI/NjIWVx2YOnVuc74F32iWfnaYf/ZhAbiBMeK65hjahLv38yVkDNJk2ySLTM1NWrDwQ6/GtaSrzX8ypg326hO7xW4Ji16Ndzu8d0NzyDbb7Glnnu7Uv/0SkDhHrD5EYZA5mOnOdu5F12Pu4PdhufDCjh39DwKyInuVStFj0iJ0jNTg2BrBSeZAHfIyr9uoyCGg4WfgDRfwd38ZKLe4C1uCv8VuGdRsTsKlm6JHOlKjKB7+YDhPHZs7gb8iD5zpRt+8Q4JPfeavdGDC+e93vlDY//kILrulO9Lv3fwfXFgqIyTlymJ3xMPc0iWbVo+5cdWjdaWY/sBgwsx4mjEhVIOieJTpHe1eVx5+4SIkBA5fvLMV6EDzBbKN4XvC47p/VA7NmRvHOnkBWbHKXSXTDR7K7I8Y4Ntsd2UQUT8+eX3/VI1L46yjKzD5VQ+/RcIwcx95/E1mPRhpq4FmI3j+nr4g1syeSkZh5c3jjYz3CGIvZ981F72BnQdb6nZJY3jZ5bGaFHk4afLCxjhIfmvKDALM2FnhN0ww7a7C2MjBr+wCvaRoLK69p2rOHGzUiTehaw+g9HyNVHqHNvY/065rF+nBe17wPe/hcieT1Hg8mI23kqp8xPTyhjzR4FhJKJDfSV4xndZ4bZyLrut4ZPpf6QRhpJtjQaWN7ejPM2j7ArO0De7LG55sZAHy+mX7g883w+Wb4fDNLs8bnm+HzzfD5Zr3A55u1gM83w+ebGQd7fubCrO0DzNo+wKzvDmdNp2JN9TUPQv+JVwgjKZvDeuFMH+3amWpSLHIxShHdaQcjYNAoJaZzJVf9X8dDbFx/AeKEkav5Ro3N7jKxaeSK9GjA3sw+sCfr/wPfJ1KOzvjPgAAAAABJRU5ErkJggg==", view.getContent()); + assertEquals("Sequence diagram example", view.getTitle()); + assertEquals("data:image/png;base64,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", view.getContent()); assertEquals("image/png", view.getContentType()); } From 1e7a9c29bba430b51a79fa986c1ea4df6316d1d1 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 17 Sep 2025 10:49:15 +0100 Subject: [PATCH 676/717] Adds support for filtered deployment views (https://github.com/structurizr/java/issues/409). --- changelog.md | 1 + .../com/structurizr/view/FilteredView.java | 6 ++--- .../java/com/structurizr/view/ViewSet.java | 24 ++++++++++++++++--- .../com/structurizr/view/ViewSetTests.java | 18 ++++++++++++-- .../structurizr/dsl/FilteredViewParser.java | 22 ++++++++--------- .../dsl/FilteredViewParserTests.java | 6 ++--- 6 files changed, 55 insertions(+), 22 deletions(-) diff --git a/changelog.md b/changelog.md index 56f85766d..91d9b037a 100644 --- a/changelog.md +++ b/changelog.md @@ -3,6 +3,7 @@ ## v5.0.0 (unreleased) - structurizr-core: Removes support for deprecated enterprise and location concepts. +- structurizr-core: Adds support for filtered deployment views (https://github.com/structurizr/java/issues/409). - structurizr-component: Fixes https://github.com/structurizr/java/issues/437 (Make ComponentFinder.run() not fail on empty Set). - structurizr-dsl: Adds support for `iconPosition` on element styles (options are `Top`, `Bottom`, `Left`). - structurizr-dsl: Adds support for defining element and relationship styles for light and dark mode. diff --git a/structurizr-core/src/main/java/com/structurizr/view/FilteredView.java b/structurizr-core/src/main/java/com/structurizr/view/FilteredView.java index 084090afe..256bc3763 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/FilteredView.java +++ b/structurizr-core/src/main/java/com/structurizr/view/FilteredView.java @@ -11,7 +11,7 @@ */ public final class FilteredView extends View { - private StaticView view; + private ModelView view; private String baseViewKey; private FilterMode mode = FilterMode.Exclude; @@ -20,7 +20,7 @@ public final class FilteredView extends View { FilteredView() { } - FilteredView(StaticView view, String key, String description, FilterMode mode, String... tags) { + FilteredView(ModelView view, String key, String description, FilterMode mode, String... tags) { this.view = view; setKey(key); setDescription(description); @@ -33,7 +33,7 @@ public View getView() { return view; } - void setView(StaticView view) { + void setView(ModelView view) { this.view = view; } diff --git a/structurizr-core/src/main/java/com/structurizr/view/ViewSet.java b/structurizr-core/src/main/java/com/structurizr/view/ViewSet.java index f597f4027..ddb5241a2 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/ViewSet.java +++ b/structurizr-core/src/main/java/com/structurizr/view/ViewSet.java @@ -498,6 +498,24 @@ public FilteredView createFilteredView(StaticView view, String key, FilterMode m * @return a FilteredView object */ public FilteredView createFilteredView(StaticView view, String key, String description, FilterMode mode, String... tags) { + return newFilteredView(view, key, description, mode, tags); + } + + /** + * Creates a FilteredView on top of an existing deployment view. + * + * @param view the deployment view to base the FilteredView upon + * @param key the key for the filtered view (must be unique) + * @param description a description + * @param mode whether to Include or Exclude elements/relationships based upon their tag + * @param tags the tags to include or exclude + * @return a FilteredView object + */ + public FilteredView createFilteredView(DeploymentView view, String key, String description, FilterMode mode, String... tags) { + return newFilteredView(view, key, description, mode, tags); + } + + private FilteredView newFilteredView(ModelView view, String key, String description, FilterMode mode, String... tags) { boolean keyIsAutomaticallyGenerated = false; if (StringUtils.isNullOrEmpty(key)) { @@ -900,11 +918,11 @@ void hydrate(Model model) { ); } - if (view instanceof StaticView) { - filteredView.setView((StaticView)view); + if (view instanceof StaticView || view instanceof DeploymentView) { + filteredView.setView((ModelView)view); } else { throw new WorkspaceValidationException( - String.format("The filtered view with key \"%s\" is based upon a view (key=%s), but that view is not a static view.", + String.format("The filtered view with key \"%s\" is based upon a view (key=%s), but that view is not a static or deployment view.", filteredView.getKey(), filteredView.getBaseViewKey()) ); } diff --git a/structurizr-core/src/test/java/com/structurizr/view/ViewSetTests.java b/structurizr-core/src/test/java/com/structurizr/view/ViewSetTests.java index 94591bb7e..d862adf33 100644 --- a/structurizr-core/src/test/java/com/structurizr/view/ViewSetTests.java +++ b/structurizr-core/src/test/java/com/structurizr/view/ViewSetTests.java @@ -491,7 +491,7 @@ void createDeploymentViewForSoftwareSystem() { void createFilteredView_ThrowsAnException_WhenANullViewIsSpecified() { try { Workspace workspace = new Workspace("Name", "Description"); - workspace.getViews().createFilteredView(null, "key", "Description", FilterMode.Include, "tag1", "tag2"); + workspace.getViews().createFilteredView((SystemLandscapeView)null, "key", "Description", FilterMode.Include, "tag1", "tag2"); fail(); } catch (IllegalArgumentException iae) { assertEquals("A view must be specified.", iae.getMessage()); @@ -530,7 +530,7 @@ void createFilteredView_ThrowsAnException_WhenADuplicateKeyIsUsed() { } @Test - void createFilteredView() { + void createFilteredView_OnStaticView() { Workspace workspace = new Workspace("Name", "Description"); SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("systemLandscape", "Description"); FilteredView filteredView = workspace.getViews().createFilteredView(view, "key", "Description", FilterMode.Include, "tag1", "tag2"); @@ -543,6 +543,20 @@ void createFilteredView() { assertTrue(filteredView.getTags().contains("tag2")); } + @Test + void createFilteredView_OnDeploymentView() { + Workspace workspace = new Workspace("Name", "Description"); + DeploymentView view = workspace.getViews().createDeploymentView("deployment"); + FilteredView filteredView = workspace.getViews().createFilteredView(view, "key", "Description", FilterMode.Include, "tag1", "tag2"); + + assertEquals("key", filteredView.getKey()); + assertEquals("Description", filteredView.getDescription()); + assertEquals(FilterMode.Include, filteredView.getMode()); + assertEquals(2, filteredView.getTags().size()); + assertTrue(filteredView.getTags().contains("tag1")); + assertTrue(filteredView.getTags().contains("tag2")); + } + @Test void copyLayoutInformationFrom_WhenAViewKeyIsNotSetButTheViewTitlesMatch() { Workspace workspace1 = createWorkspace(); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/FilteredViewParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/FilteredViewParser.java index 1c45cea8a..8f03f9b4c 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/FilteredViewParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/FilteredViewParser.java @@ -2,9 +2,7 @@ import com.structurizr.Workspace; import com.structurizr.util.StringUtils; -import com.structurizr.view.FilterMode; -import com.structurizr.view.FilteredView; -import com.structurizr.view.StaticView; +import com.structurizr.view.*; import java.text.DecimalFormat; import java.util.HashSet; @@ -37,7 +35,7 @@ FilteredView parse(DslContext context, Tokens tokens) { Workspace workspace = context.getWorkspace(); String key = ""; - StaticView baseView; + View baseView; String baseKey = tokens.get(BASE_KEY_INDEX); String mode = tokens.get(MODE_INDEX); String tagsAsString = tokens.get(TAGS_INDEX); @@ -64,13 +62,9 @@ FilteredView parse(DslContext context, Tokens tokens) { throw new RuntimeException("Filter mode should be include or exclude"); } - if (workspace.getViews().getViews().stream().noneMatch(v -> v.getKey().equals(baseKey))) { - throw new RuntimeException("The view \"" + baseKey + "\" does not exist"); - } - - baseView = (StaticView)workspace.getViews().getViews().stream().filter(v -> v instanceof StaticView && v.getKey().equals(baseKey)).findFirst().orElse(null); + baseView = workspace.getViews().getViewWithKey(baseKey); if (baseView == null) { - throw new RuntimeException("The view \"" + baseKey + "\" must be a System Landscape, System Context, Container, or Component view"); + throw new RuntimeException("The view \"" + baseKey + "\" does not exist"); } if (tokens.includes(KEY_INDEX)) { @@ -78,7 +72,13 @@ FilteredView parse(DslContext context, Tokens tokens) { validateViewKey(key); } - return workspace.getViews().createFilteredView(baseView, key, description, filterMode, tags.toArray(new String[0])); + if (baseView instanceof StaticView) { + return workspace.getViews().createFilteredView((StaticView)baseView, key, description, filterMode, tags.toArray(new String[0])); + } else if (baseView instanceof DeploymentView) { + return workspace.getViews().createFilteredView((DeploymentView)baseView, key, description, filterMode, tags.toArray(new String[0])); + } else { + throw new RuntimeException("The view \"" + baseKey + "\" must be a System Landscape, System Context, Container, Component, or Deployment view"); + } } } \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/FilteredViewParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/FilteredViewParserTests.java index 5e5c5699c..c34afec00 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/FilteredViewParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/FilteredViewParserTests.java @@ -80,14 +80,14 @@ void test_parse_ThrowsAnException_WhenTheBaseViewDoesNotExist() { } @Test - void test_parse_ThrowsAnException_WhenTheBaseViewIsNotAStaticView() { + void test_parse_ThrowsAnException_WhenTheBaseViewIsNotAStaticOrDeploymentView() { DslContext context = context(); - views.createDeploymentView("baseKey", "Description"); + views.createDynamicView("baseKey", "Description"); try { parser.parse(context, tokens("filtered", "baseKey", "include", "Tag 1, Tag 2", "key")); fail(); } catch (RuntimeException iae) { - assertEquals("The view \"baseKey\" must be a System Landscape, System Context, Container, or Component view", iae.getMessage()); + assertEquals("The view \"baseKey\" must be a System Landscape, System Context, Container, Component, or Deployment view", iae.getMessage()); } } From 102010a9ef793f5a47e674c3160f0e837881ba20 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 17 Sep 2025 13:56:21 +0100 Subject: [PATCH 677/717] Adds some caching when inlining PlantUML, Mermaid, and Kroki images. --- .../java/com/structurizr/util/ImageUtils.java | 76 +++++++++++++------ .../diagrams/image/ImageImporter.java | 4 +- .../diagrams/kroki/KrokiImporter.java | 4 +- .../diagrams/mermaid/MermaidImporter.java | 4 +- .../diagrams/plantuml/PlantUMLImporter.java | 4 +- 5 files changed, 60 insertions(+), 32 deletions(-) diff --git a/structurizr-core/src/main/java/com/structurizr/util/ImageUtils.java b/structurizr-core/src/main/java/com/structurizr/util/ImageUtils.java index bed31efc7..e0baf42e6 100644 --- a/structurizr-core/src/main/java/com/structurizr/util/ImageUtils.java +++ b/structurizr-core/src/main/java/com/structurizr/util/ImageUtils.java @@ -13,6 +13,8 @@ import java.net.http.HttpResponse; import java.nio.file.Files; import java.util.Base64; +import java.util.HashMap; +import java.util.Map; /** * Some utility methods for dealing with images. @@ -28,6 +30,8 @@ public class ImageUtils { public static final String CONTENT_TYPE_IMAGE_JPG = "image/jpeg"; public static final String CONTENT_TYPE_IMAGE_SVG = "image/svg+xml"; + private static final Map imageCache = new HashMap<>(); + /** * Gets the content type of the specified file representing an image. * @@ -128,33 +132,57 @@ public static String getImageAsDataUri(File file) throws IOException { } public static String getSvgAsDataUri(@Nonnull URL url) throws Exception { - HttpRequest request = HttpRequest.newBuilder() - .uri(url.toURI()) - .header("accept", CONTENT_TYPE_IMAGE_SVG) - .build(); - HttpClient client = HttpClient.newBuilder() - .followRedirects(HttpClient.Redirect.ALWAYS) - .build(); - - HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); - String svg = response.body(); - - return DATA_URI_PREFIX + CONTENT_TYPE_IMAGE_SVG + ";base64," + Base64.getEncoder().encodeToString(svg.getBytes()); + return getSvgAsDataUri(url, false); + } + + public static String getSvgAsDataUri(@Nonnull URL url, boolean cache) throws Exception { + String urlAsString = url.toString(); + String dataUri = cache ? imageCache.get(urlAsString) : null; + + if (StringUtils.isNullOrEmpty(dataUri)) { + HttpRequest request = HttpRequest.newBuilder() + .uri(url.toURI()) + .header("accept", CONTENT_TYPE_IMAGE_SVG) + .build(); + HttpClient client = HttpClient.newBuilder() + .followRedirects(HttpClient.Redirect.ALWAYS) + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + String svg = response.body(); + + dataUri = DATA_URI_PREFIX + CONTENT_TYPE_IMAGE_SVG + ";base64," + Base64.getEncoder().encodeToString(svg.getBytes()); + imageCache.put(urlAsString, dataUri); + } + + return dataUri; } public static String getPngAsDataUri(@Nonnull URL url) throws Exception { - HttpRequest request = HttpRequest.newBuilder() - .uri(url.toURI()) - .header("accept", CONTENT_TYPE_IMAGE_PNG) - .build(); - HttpClient client = HttpClient.newBuilder() - .followRedirects(HttpClient.Redirect.ALWAYS) - .build(); - - HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofByteArray()); - byte[] png = response.body(); - - return DATA_URI_PREFIX + CONTENT_TYPE_IMAGE_PNG + ";base64," + Base64.getEncoder().encodeToString(png); + return getPngAsDataUri(url, false); + } + + public static String getPngAsDataUri(@Nonnull URL url, boolean cache) throws Exception { + String urlAsString = url.toString(); + String dataUri = cache ? imageCache.get(urlAsString) : null; + + if (StringUtils.isNullOrEmpty(dataUri)) { + HttpRequest request = HttpRequest.newBuilder() + .uri(url.toURI()) + .header("accept", CONTENT_TYPE_IMAGE_PNG) + .build(); + HttpClient client = HttpClient.newBuilder() + .followRedirects(HttpClient.Redirect.ALWAYS) + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofByteArray()); + byte[] png = response.body(); + + dataUri = DATA_URI_PREFIX + CONTENT_TYPE_IMAGE_PNG + ";base64," + Base64.getEncoder().encodeToString(png); + imageCache.put(urlAsString, dataUri); + } + + return dataUri; } public static void validateImage(String imageDescriptor) { diff --git a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/image/ImageImporter.java b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/image/ImageImporter.java index dbdf10b60..6708f0f06 100644 --- a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/image/ImageImporter.java +++ b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/image/ImageImporter.java @@ -28,9 +28,9 @@ public void importDiagram(ImageView view, String url) throws Exception { } if (imageFormat.equals(CONTENT_TYPE_IMAGE_SVG)) { - view.setContent(ImageUtils.getSvgAsDataUri(new URL(url))); + view.setContent(ImageUtils.getSvgAsDataUri(new URL(url), false)); } else { - view.setContent(ImageUtils.getPngAsDataUri(new URL(url))); + view.setContent(ImageUtils.getPngAsDataUri(new URL(url), false)); } view.setContentType(imageFormat); diff --git a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/kroki/KrokiImporter.java b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/kroki/KrokiImporter.java index 5277bd99b..67b3d4413 100644 --- a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/kroki/KrokiImporter.java +++ b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/kroki/KrokiImporter.java @@ -44,9 +44,9 @@ public void importDiagram(ImageView view, String format, String content) throws String inline = getViewOrViewSetProperty(view, KROKI_INLINE_PROPERTY); if ("true".equals(inline)) { if (imageFormat.equals(SVG_FORMAT)) { - view.setContent(ImageUtils.getSvgAsDataUri(new URL(url))); + view.setContent(ImageUtils.getSvgAsDataUri(new URL(url), true)); } else { - view.setContent(ImageUtils.getPngAsDataUri(new URL(url))); + view.setContent(ImageUtils.getPngAsDataUri(new URL(url), true)); } } else { view.setContent(url); diff --git a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/mermaid/MermaidImporter.java b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/mermaid/MermaidImporter.java index 2be38290c..ab1f1a00c 100644 --- a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/mermaid/MermaidImporter.java +++ b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/mermaid/MermaidImporter.java @@ -55,9 +55,9 @@ public void importDiagram(ImageView view, String content) throws Exception { String inline = getViewOrViewSetProperty(view, MERMAID_INLINE_PROPERTY); if ("true".equals(inline)) { if (format.equals(SVG_FORMAT)) { - view.setContent(ImageUtils.getSvgAsDataUri(new URL(url))); + view.setContent(ImageUtils.getSvgAsDataUri(new URL(url), true)); } else { - view.setContent(ImageUtils.getPngAsDataUri(new URL(url))); + view.setContent(ImageUtils.getPngAsDataUri(new URL(url), true)); } } else { view.setContent(url); diff --git a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporter.java b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporter.java index b7ae51afe..28ecf1c3e 100644 --- a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporter.java +++ b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporter.java @@ -47,9 +47,9 @@ public void importDiagram(ImageView view, String content) throws Exception { String inline = getViewOrViewSetProperty(view, PLANTUML_INLINE_PROPERTY); if ("true".equals(inline)) { if (format.equals(SVG_FORMAT)) { - view.setContent(ImageUtils.getSvgAsDataUri(new URL(url))); + view.setContent(ImageUtils.getSvgAsDataUri(new URL(url), true)); } else { - view.setContent(ImageUtils.getPngAsDataUri(new URL(url))); + view.setContent(ImageUtils.getPngAsDataUri(new URL(url), true)); } } else { view.setContent(url); From f52092dfaabb684f36a3671db70b489f794291d0 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 18 Sep 2025 12:06:08 +0100 Subject: [PATCH 678/717] Modifies how view names are generated to make them all consistent. --- .../com/structurizr/view/ComponentView.java | 2 +- .../com/structurizr/view/ContainerView.java | 2 +- .../java/com/structurizr/view/CustomView.java | 2 +- .../com/structurizr/view/DeploymentView.java | 8 +- .../com/structurizr/view/DynamicView.java | 9 +- .../structurizr/view/SystemContextView.java | 2 +- .../structurizr/view/SystemLandscapeView.java | 2 +- .../main/java/com/structurizr/view/View.java | 1 - .../structurizr/view/ComponentViewTests.java | 2 +- .../structurizr/view/ContainerViewTests.java | 2 +- .../structurizr/view/DeploymentViewTests.java | 8 +- .../structurizr/view/DynamicViewTests.java | 7 + .../view/SystemContextViewTests.java | 2 +- .../view/SystemLandscapeViewTests.java | 3 +- .../java/com/structurizr/view/ViewTests.java | 7 - .../export/ilograph/IlographExporter.java | 7 +- .../plantuml/StructurizrPlantUMLExporter.java | 2 +- .../WebSequenceDiagramsExporter.java | 6 +- .../export/dot/DOTDiagramExporterTests.java | 125 +++++++++--------- .../ilograph/IlographExporterTests.java | 2 +- .../export/mermaid/36141-Components.mmd | 48 ------- .../export/mermaid/36141-Containers.mmd | 39 ------ .../mermaid/36141-DevelopmentDeployment.mmd | 61 --------- .../export/mermaid/36141-LiveDeployment.mmd | 92 ------------- .../export/mermaid/36141-SignIn-sequence.mmd | 13 -- .../export/mermaid/36141-SignIn.mmd | 27 ---- .../export/mermaid/36141-SystemContext.mmd | 25 ---- .../export/mermaid/36141-SystemLandscape.mmd | 36 ----- .../mermaid/MermaidDiagramExporterTests.java | 22 +-- .../C4PlantUMLDiagramExporterTests.java | 72 +++++----- ...ructurizrPlantUMLDiagramExporterTests.java | 72 +++++----- .../WebSequenceDiagramsExporterTests.java | 4 +- 32 files changed, 192 insertions(+), 520 deletions(-) delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-Components.mmd delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-Containers.mmd delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-DevelopmentDeployment.mmd delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-LiveDeployment.mmd delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SignIn-sequence.mmd delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SignIn.mmd delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SystemContext.mmd delete mode 100644 structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SystemLandscape.mmd diff --git a/structurizr-core/src/main/java/com/structurizr/view/ComponentView.java b/structurizr-core/src/main/java/com/structurizr/view/ComponentView.java index b543dc876..c9168cee6 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/ComponentView.java +++ b/structurizr-core/src/main/java/com/structurizr/view/ComponentView.java @@ -148,7 +148,7 @@ public void remove(Component component) { */ @Override public String getName() { - return getSoftwareSystem().getName() + " - " + getContainer().getName() + " - Components"; + return "Component View: " + getSoftwareSystem().getName() + " - " + getContainer().getName(); } /** diff --git a/structurizr-core/src/main/java/com/structurizr/view/ContainerView.java b/structurizr-core/src/main/java/com/structurizr/view/ContainerView.java index 664164974..a5b3e4bf0 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/ContainerView.java +++ b/structurizr-core/src/main/java/com/structurizr/view/ContainerView.java @@ -77,7 +77,7 @@ public void remove(Container container) { */ @Override public String getName() { - return getSoftwareSystem().getName() + " - Containers"; + return "Container View: " + getSoftwareSystem().getName(); } /** diff --git a/structurizr-core/src/main/java/com/structurizr/view/CustomView.java b/structurizr-core/src/main/java/com/structurizr/view/CustomView.java index 81558f604..35469259a 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/CustomView.java +++ b/structurizr-core/src/main/java/com/structurizr/view/CustomView.java @@ -40,7 +40,7 @@ public final class CustomView extends ModelView implements AnimatedView { */ @Override public String getName() { - return "Custom - " + getTitle(); + return "Custom View: " + getTitle(); } /** diff --git a/structurizr-core/src/main/java/com/structurizr/view/DeploymentView.java b/structurizr-core/src/main/java/com/structurizr/view/DeploymentView.java index e38113e9a..444bb2250 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/DeploymentView.java +++ b/structurizr-core/src/main/java/com/structurizr/view/DeploymentView.java @@ -248,13 +248,9 @@ public RelationshipView add(@Nonnull Relationship relationship) { public String getName() { String name; if (getSoftwareSystem() != null) { - name = getSoftwareSystem().getName() + " - Deployment"; + name = "Deployment View: " + getSoftwareSystem().getName() + " - " + getEnvironment(); } else { - name = "Deployment"; - } - - if (!StringUtils.isNullOrEmpty(getEnvironment())) { - name = name + " - " + getEnvironment(); + name = "Deployment View: " + getEnvironment(); } return name; diff --git a/structurizr-core/src/main/java/com/structurizr/view/DynamicView.java b/structurizr-core/src/main/java/com/structurizr/view/DynamicView.java index 04d0fbdfb..5f206caca 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/DynamicView.java +++ b/structurizr-core/src/main/java/com/structurizr/view/DynamicView.java @@ -254,9 +254,14 @@ private RelationshipView addRelationship(Relationship relationship, String descr @Override public String getName() { if (element != null) { - return element.getName() + " - Dynamic"; + if (element instanceof Container) { + Container container = (Container)element; + return "Dynamic View: " + container.getParent().getName() + " - " + container.getName(); + } else { + return "Dynamic View: " + element.getName(); + } } else { - return "Dynamic"; + return "Dynamic View"; } } diff --git a/structurizr-core/src/main/java/com/structurizr/view/SystemContextView.java b/structurizr-core/src/main/java/com/structurizr/view/SystemContextView.java index 513fe02c1..3c88717da 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/SystemContextView.java +++ b/structurizr-core/src/main/java/com/structurizr/view/SystemContextView.java @@ -31,7 +31,7 @@ public final class SystemContextView extends StaticView { */ @Override public String getName() { - return getSoftwareSystem().getName() + " - System Context"; + return "System Context View: " + getSoftwareSystem().getName(); } /** diff --git a/structurizr-core/src/main/java/com/structurizr/view/SystemLandscapeView.java b/structurizr-core/src/main/java/com/structurizr/view/SystemLandscapeView.java index 359245180..2284e575c 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/SystemLandscapeView.java +++ b/structurizr-core/src/main/java/com/structurizr/view/SystemLandscapeView.java @@ -31,7 +31,7 @@ public final class SystemLandscapeView extends StaticView { */ @Override public String getName() { - return "System Landscape"; + return "System Landscape View"; } /** diff --git a/structurizr-core/src/main/java/com/structurizr/view/View.java b/structurizr-core/src/main/java/com/structurizr/view/View.java index f6ee5539d..54588d8cf 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/View.java +++ b/structurizr-core/src/main/java/com/structurizr/view/View.java @@ -111,7 +111,6 @@ public void setTitle(String title) { * * @return the name, as a String */ - @JsonIgnore public abstract String getName(); void setViewSet(@Nonnull ViewSet viewSet) { diff --git a/structurizr-core/src/test/java/com/structurizr/view/ComponentViewTests.java b/structurizr-core/src/test/java/com/structurizr/view/ComponentViewTests.java index db21ee52e..a1408b08b 100644 --- a/structurizr-core/src/test/java/com/structurizr/view/ComponentViewTests.java +++ b/structurizr-core/src/test/java/com/structurizr/view/ComponentViewTests.java @@ -25,7 +25,7 @@ public void setUp() { @Test void construction() { - assertEquals("The System - Web Application - Components", view.getName()); + assertEquals("Component View: The System - Web Application", view.getName()); assertEquals("Some description", view.getDescription()); assertEquals(0, view.getElements().size()); assertSame(softwareSystem, view.getSoftwareSystem()); diff --git a/structurizr-core/src/test/java/com/structurizr/view/ContainerViewTests.java b/structurizr-core/src/test/java/com/structurizr/view/ContainerViewTests.java index a9caf817d..7d877de13 100644 --- a/structurizr-core/src/test/java/com/structurizr/view/ContainerViewTests.java +++ b/structurizr-core/src/test/java/com/structurizr/view/ContainerViewTests.java @@ -20,7 +20,7 @@ public void setUp() { @Test void construction() { - assertEquals("The System - Containers", view.getName()); + assertEquals("Container View: The System", view.getName()); assertEquals("Description", view.getDescription()); assertEquals(0, view.getElements().size()); assertSame(softwareSystem, view.getSoftwareSystem()); diff --git a/structurizr-core/src/test/java/com/structurizr/view/DeploymentViewTests.java b/structurizr-core/src/test/java/com/structurizr/view/DeploymentViewTests.java index 0fcafacca..56b0a0038 100644 --- a/structurizr-core/src/test/java/com/structurizr/view/DeploymentViewTests.java +++ b/structurizr-core/src/test/java/com/structurizr/view/DeploymentViewTests.java @@ -18,21 +18,21 @@ public void setup() { @Test void getName_WithNoSoftwareSystemAndNoEnvironment() { deploymentView = views.createDeploymentView("deployment", "Description"); - assertEquals("Deployment - Default", deploymentView.getName()); + assertEquals("Deployment View: Default", deploymentView.getName()); } @Test void getName_WithNoSoftwareSystemAndAnEnvironment() { deploymentView = views.createDeploymentView("deployment", "Description"); deploymentView.setEnvironment("Live"); - assertEquals("Deployment - Live", deploymentView.getName()); + assertEquals("Deployment View: Live", deploymentView.getName()); } @Test void getName_WithASoftwareSystemAndNoEnvironment() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); deploymentView = views.createDeploymentView(softwareSystem, "deployment", "Description"); - assertEquals("Software System - Deployment - Default", deploymentView.getName()); + assertEquals("Deployment View: Software System - Default", deploymentView.getName()); } @Test @@ -40,7 +40,7 @@ void getName_WithASoftwareSystemAndAnEnvironment() { SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System", ""); deploymentView = views.createDeploymentView(softwareSystem, "deployment", "Description"); deploymentView.setEnvironment("Live"); - assertEquals("Software System - Deployment - Live", deploymentView.getName()); + assertEquals("Deployment View: Software System - Live", deploymentView.getName()); } @Test diff --git a/structurizr-core/src/test/java/com/structurizr/view/DynamicViewTests.java b/structurizr-core/src/test/java/com/structurizr/view/DynamicViewTests.java index 029224428..bcbea49d6 100644 --- a/structurizr-core/src/test/java/com/structurizr/view/DynamicViewTests.java +++ b/structurizr-core/src/test/java/com/structurizr/view/DynamicViewTests.java @@ -43,6 +43,13 @@ public void setup() { containerB1 = softwareSystemB.addContainer("Container B1", "", ""); } + @Test + void name() { + assertEquals("Dynamic View", views.createDynamicView("key1").getName()); + assertEquals("Dynamic View: Software System A", views.createDynamicView(softwareSystemA, "key2").getName()); + assertEquals("Dynamic View: Software System A - Container A1", views.createDynamicView(containerA1, "key3").getName()); + } + @Test void add_ThrowsAnException_WhenPassedANullSourceElement() { try { diff --git a/structurizr-core/src/test/java/com/structurizr/view/SystemContextViewTests.java b/structurizr-core/src/test/java/com/structurizr/view/SystemContextViewTests.java index bc7694728..b35075f43 100644 --- a/structurizr-core/src/test/java/com/structurizr/view/SystemContextViewTests.java +++ b/structurizr-core/src/test/java/com/structurizr/view/SystemContextViewTests.java @@ -20,7 +20,7 @@ public void setUp() { @Test void construction() { - assertEquals("The System - System Context", view.getName()); + assertEquals("System Context View: The System", view.getName()); assertEquals(1, view.getElements().size()); assertSame(view.getElements().iterator().next().getElement(), softwareSystem); assertSame(softwareSystem, view.getSoftwareSystem()); diff --git a/structurizr-core/src/test/java/com/structurizr/view/SystemLandscapeViewTests.java b/structurizr-core/src/test/java/com/structurizr/view/SystemLandscapeViewTests.java index 274afed64..481c1c77c 100644 --- a/structurizr-core/src/test/java/com/structurizr/view/SystemLandscapeViewTests.java +++ b/structurizr-core/src/test/java/com/structurizr/view/SystemLandscapeViewTests.java @@ -18,14 +18,13 @@ public void setUp() { @Test void construction() { - assertEquals("System Landscape", view.getName()); assertEquals(0, view.getElements().size()); assertSame(model, view.getModel()); } @Test void getName() { - assertEquals("System Landscape", view.getName()); + assertEquals("System Landscape View", view.getName()); } @Test diff --git a/structurizr-core/src/test/java/com/structurizr/view/ViewTests.java b/structurizr-core/src/test/java/com/structurizr/view/ViewTests.java index b4cb96bcd..068c17991 100644 --- a/structurizr-core/src/test/java/com/structurizr/view/ViewTests.java +++ b/structurizr-core/src/test/java/com/structurizr/view/ViewTests.java @@ -255,13 +255,6 @@ void copyLayoutInformationFrom() { assertEquals(Routing.Direct, dynamicView2.getRelationshipView(personUsesSoftwareSystem2).getRouting()); } - @Test - void getName() { - SoftwareSystem softwareSystem = model.addSoftwareSystem("The System", "Description"); - SystemContextView systemContextView = new SystemContextView(softwareSystem, "context", "Description"); - assertEquals("The System - System Context", systemContextView.getName()); - } - @Test void removeElementsThatAreUnreachableFrom_DoesNothing_WhenANullElementIsSpecified() { SoftwareSystem softwareSystem = model.addSoftwareSystem("The System", "Description"); diff --git a/structurizr-export/src/main/java/com/structurizr/export/ilograph/IlographExporter.java b/structurizr-export/src/main/java/com/structurizr/export/ilograph/IlographExporter.java index 06aefe67d..9a80784e1 100644 --- a/structurizr-export/src/main/java/com/structurizr/export/ilograph/IlographExporter.java +++ b/structurizr-export/src/main/java/com/structurizr/export/ilograph/IlographExporter.java @@ -298,8 +298,13 @@ private void writeRelationshipsForStaticStructurePerspective(Configuration confi } private void writeDynamicView(DynamicView dynamicView, IndentingWriter writer) { + String scope = dynamicView.getName(); + scope = scope.substring("Dynamic View".length()); + if (scope.startsWith(": ")) { + scope = scope.substring(2); + } writer.indent(); - writer.writeLine("- name: Dynamic - " + dynamicView.getName()); + writer.writeLine("- name: Dynamic: " + scope); writer.indent(); writer.writeLine("sequence:"); diff --git a/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java b/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java index 6dccc103c..18238eb08 100644 --- a/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java +++ b/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java @@ -629,7 +629,7 @@ private ElementStyle findGroupStyle(ModelView view, String group) { if (elementStyleForGroup != null && elementStyleForGroup.getFontSize() != null) { fontSize = elementStyleForGroup.getFontSize(); - } else if (elementStyleForAllGroups != null && elementStyleForGroup.getFontSize() != null) { + } else if (elementStyleForAllGroups != null && elementStyleForAllGroups.getFontSize() != null) { fontSize = elementStyleForAllGroups.getFontSize(); } style.setFontSize(fontSize); diff --git a/structurizr-export/src/main/java/com/structurizr/export/websequencediagrams/WebSequenceDiagramsExporter.java b/structurizr-export/src/main/java/com/structurizr/export/websequencediagrams/WebSequenceDiagramsExporter.java index e05832792..c7932abcc 100644 --- a/structurizr-export/src/main/java/com/structurizr/export/websequencediagrams/WebSequenceDiagramsExporter.java +++ b/structurizr-export/src/main/java/com/structurizr/export/websequencediagrams/WebSequenceDiagramsExporter.java @@ -75,7 +75,11 @@ public Diagram export(DeploymentView view) { @Override protected void writeHeader(ModelView view, IndentingWriter writer) { - writer.writeLine("title " + view.getName() + " - " + view.getKey()); + if (!StringUtils.isNullOrEmpty(view.getDescription())) { + writer.writeLine("title " + view.getName() + "\n" + view.getDescription()); + } else { + writer.writeLine("title " + view.getName()); + } writer.writeLine(); } diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/DOTDiagramExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/dot/DOTDiagramExporterTests.java index 02aa78296..937af8766 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/dot/DOTDiagramExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/dot/DOTDiagramExporterTests.java @@ -31,7 +31,7 @@ public void test_BigBankPlcExample() throws Exception { graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] node [fontname="Arial", shape=box, margin="0.4,0.3"] edge [fontname="Arial"] - label=<
    System Landscape> + label=<
    System Landscape View> subgraph "cluster_group_Big Bank plc" { margin=25 @@ -71,7 +71,7 @@ public void test_BigBankPlcExample() throws Exception { graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] node [fontname="Arial", shape=box, margin="0.4,0.3"] edge [fontname="Arial"] - label=<
    Internet Banking System - System Context
    The system context diagram for the Internet Banking System.> + label=<
    System Context View: Internet Banking System
    The system context diagram for the Internet Banking System.> subgraph "cluster_group_Big Bank plc" { margin=25 @@ -103,7 +103,7 @@ public void test_BigBankPlcExample() throws Exception { graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] node [fontname="Arial", shape=box, margin="0.4,0.3"] edge [fontname="Arial"] - label=<
    Internet Banking System - Containers
    The container diagram for the Internet Banking System.> + label=<
    Container View: Internet Banking System
    The container diagram for the Internet Banking System.> 1 [id=1,shape=rect, label=<Personal Banking
    Customer

    [Person]

    A customer of the bank, with
    personal bank accounts.
    >, style=filled, color="#052e56", fillcolor="#08427b", fontcolor="#ffffff"] 4 [id=4,shape=rect, label=<Mainframe Banking
    System

    [Software System]

    Stores all of the core banking
    information about customers,
    accounts, transactions, etc.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] @@ -144,7 +144,7 @@ public void test_BigBankPlcExample() throws Exception { graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] node [fontname="Arial", shape=box, margin="0.4,0.3"] edge [fontname="Arial"] - label=<
    Internet Banking System - API Application - Components
    The component diagram for the API Application.> + label=<
    Component View: Internet Banking System - API Application
    The component diagram for the API Application.> 4 [id=4,shape=rect, label=<Mainframe Banking
    System

    [Software System]

    Stores all of the core banking
    information about customers,
    accounts, transactions, etc.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] 5 [id=5,shape=rect, label=<E-mail System
    [Software System]

    The internal Microsoft
    Exchange e-mail system.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] @@ -191,7 +191,7 @@ public void test_BigBankPlcExample() throws Exception { graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] node [fontname="Arial", shape=box, margin="0.4,0.3"] edge [fontname="Arial"] - label=<
    API Application - Dynamic
    Summarises how the sign in feature works in the single-page application.> + label=<
    Dynamic View: Internet Banking System - API Application
    Summarises how the sign in feature works in the single-page application.> subgraph cluster_11 { margin=25 @@ -224,7 +224,7 @@ public void test_BigBankPlcExample() throws Exception { graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] node [fontname="Arial", shape=box, margin="0.4,0.3"] edge [fontname="Arial"] - label=<
    Internet Banking System - Deployment - Development
    An example development deployment scenario for the Internet Banking System.> + label=<
    Deployment View: Internet Banking System - Development
    An example development deployment scenario for the Internet Banking System.> subgraph cluster_50 { margin=25 @@ -325,7 +325,7 @@ public void test_BigBankPlcExample() throws Exception { graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] node [fontname="Arial", shape=box, margin="0.4,0.3"] edge [fontname="Arial"] - label=<
    Internet Banking System - Deployment - Live
    An example live deployment scenario for the Internet Banking System.> + label=<
    Deployment View: Internet Banking System - Live
    An example live deployment scenario for the Internet Banking System.> subgraph cluster_67 { margin=25 @@ -493,7 +493,7 @@ public void test_AmazonWebServicesExample() throws Exception { graph [fontname="Arial", rankdir=LR, ranksep=1.0, nodesep=1.0] node [fontname="Arial", shape=box, margin="0.4,0.3"] edge [fontname="Arial"] - label=<
    X - Deployment - Live> + label=<
    Deployment View: X - Live> subgraph cluster_5 { margin=25 @@ -582,7 +582,7 @@ public void test_GroupsExample() throws Exception { graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] node [fontname="Arial", shape=box, margin="0.4,0.3"] edge [fontname="Arial"] - label=<
    System Landscape> + label=<
    System Landscape View> subgraph "cluster_group_Group 1" { margin=25 @@ -635,7 +635,7 @@ public void test_GroupsExample() throws Exception { graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] node [fontname="Arial", shape=box, margin="0.4,0.3"] edge [fontname="Arial"] - label=<
    D - Containers> + label=<
    Container View: D> 3 [id=3,shape=rect, label=<C
    [Software System]>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] @@ -674,7 +674,7 @@ public void test_GroupsExample() throws Exception { graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] node [fontname="Arial", shape=box, margin="0.4,0.3"] edge [fontname="Arial"] - label=<
    D - F - Components> + label=<
    Component View: D - F> 3 [id=3,shape=rect, label=<C
    [Software System]>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] @@ -740,7 +740,7 @@ public void test_NestedGroupsExample() throws Exception { graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] node [fontname="Arial", shape=box, margin="0.4,0.3"] edge [fontname="Arial"] - label=<
    System Landscape
    Description> + label=<
    System Landscape View
    Description> subgraph "cluster_group_Organisation 1" { margin=25 @@ -827,7 +827,7 @@ public void test_renderContainerDiagramWithExternalContainers() { graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] node [fontname="Arial", shape=box, margin="0.4,0.3"] edge [fontname="Arial"] - label=<
    Software System 1 - Containers> + label=<
    Container View: Software System 1> subgraph cluster_1 { margin=25 @@ -879,7 +879,7 @@ public void test_renderComponentDiagramWithExternalComponents() { graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] node [fontname="Arial", shape=box, margin="0.4,0.3"] edge [fontname="Arial"] - label=<
    Software System 1 - Container 1 - Components> + label=<
    Component View: Software System 1 - Container 1> subgraph cluster_2 { margin=25 @@ -929,7 +929,7 @@ public void test_renderGroupStyles() { graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] node [fontname="Arial", shape=box, margin="0.4,0.3"] edge [fontname="Arial"] - label=<
    System Landscape> + label=<
    System Landscape View> subgraph "cluster_group_Group 1" { margin=25 @@ -979,7 +979,7 @@ public void test_renderGroupStyles() { graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] node [fontname="Arial", shape=box, margin="0.4,0.3"] edge [fontname="Arial"] - label=<
    System Landscape> + label=<
    System Landscape View> subgraph "cluster_group_Group 1" { margin=25 @@ -1062,58 +1062,51 @@ public void test_writeContainerViewWithGroupedElements_WithAndWithoutAGroupSepar ContainerView view = workspace.getViews().createContainerView(softwareSystem, "Containers", ""); view.addAllElements(); - String expectedResult = "digraph {\n" + - " compound=true\n" + - " graph [fontname=\"Arial\", rankdir=TB, ranksep=1.0, nodesep=1.0]\n" + - " node [fontname=\"Arial\", shape=box, margin=\"0.4,0.3\"]\n" + - " edge [fontname=\"Arial\"]\n" + - " label=<
    Software System - Containers>\n" + - "\n" + - " subgraph cluster_1 {\n" + - " margin=25\n" + - " label=<
    Software System

    [Software System]>\n" + - " labelloc=b\n" + - " color=\"#444444\"\n" + - " fontcolor=\"#444444\"\n" + - " fillcolor=\"#444444\"\n" + - "\n" + - " subgraph \"cluster_group_Group 1\" {\n" + - " margin=25\n" + - " label=<
    Group 1
    >\n" + - " labelloc=b\n" + - " color=\"#cccccc\"\n" + - " fontcolor=\"#cccccc\"\n" + - " fillcolor=\"#ffffff\"\n" + - " style=\"dashed\"\n" + - "\n" + - " 2 [id=2,shape=rect, label=<Container 1
    [Container]>, style=filled, color=\"#444444\", fillcolor=\"#ffffff\", fontcolor=\"#444444\"]\n" + - " }\n" + - "\n" + - " subgraph \"cluster_group_Group 2\" {\n" + - " margin=25\n" + - " label=<
    Group 2
    >\n" + - " labelloc=b\n" + - " color=\"#cccccc\"\n" + - " fontcolor=\"#cccccc\"\n" + - " fillcolor=\"#ffffff\"\n" + - " style=\"dashed\"\n" + - "\n" + - " 3 [id=3,shape=rect, label=<Container 2
    [Container]>, style=filled, color=\"#444444\", fillcolor=\"#ffffff\", fontcolor=\"#444444\"]\n" + - " }\n" + - "\n" + - " }\n" + - "\n" + - "}"; - DOTExporter exporter = new DOTExporter(); Diagram diagram = exporter.export(view); - assertEquals(expectedResult, diagram.getDefinition()); - - // this should be the same - workspace.getModel().addProperty("structurizr.groupSeparator", "/"); - exporter = new DOTExporter(); - diagram = exporter.export(view); - assertEquals(expectedResult, diagram.getDefinition()); + assertEquals(""" + digraph { + compound=true + graph [fontname="Arial", rankdir=TB, ranksep=1.0, nodesep=1.0] + node [fontname="Arial", shape=box, margin="0.4,0.3"] + edge [fontname="Arial"] + label=<
    Container View: Software System> + + subgraph cluster_1 { + margin=25 + label=<
    Software System

    [Software System]> + labelloc=b + color="#444444" + fontcolor="#444444" + fillcolor="#444444" + + subgraph "cluster_group_Group 1" { + margin=25 + label=<
    Group 1
    > + labelloc=b + color="#cccccc" + fontcolor="#cccccc" + fillcolor="#ffffff" + style="dashed" + + 2 [id=2,shape=rect, label=<Container 1
    [Container]>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] + } + + subgraph "cluster_group_Group 2" { + margin=25 + label=<
    Group 2
    > + labelloc=b + color="#cccccc" + fontcolor="#cccccc" + fillcolor="#ffffff" + style="dashed" + + 3 [id=3,shape=rect, label=<Container 2
    [Container]>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] + } + + } + + }""", diagram.getDefinition()); } } \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/ilograph/IlographExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/ilograph/IlographExporterTests.java index 596b7592c..a9057c03d 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/ilograph/IlographExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/ilograph/IlographExporterTests.java @@ -555,7 +555,7 @@ public void test_BigBankPlcExample() throws Exception { description: "JSON/HTTPS" color: "#444444" - - name: Dynamic - API Application - Dynamic + - name: Dynamic: Internet Banking System - API Application sequence: start: "8" steps: diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-Components.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-Components.mmd deleted file mode 100644 index 505e7fa62..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-Components.mmd +++ /dev/null @@ -1,48 +0,0 @@ -graph TB - linkStyle default fill:#ffffff - - subgraph diagram ["Internet Banking System - API Application - Components"] - style diagram fill:#ffffff,stroke:#ffffff - - 4["
    Mainframe Banking System
    [Software System]
    Stores all of the core
    banking information about
    customers, accounts,
    transactions, etc.
    "] - style 4 fill:#999999,stroke:#6b6b6b,color:#ffffff - 5["
    E-mail System
    [Software System]
    The internal Microsoft
    Exchange e-mail system.
    "] - style 5 fill:#999999,stroke:#6b6b6b,color:#ffffff - 8["
    Single-Page Application
    [Container: JavaScript and Angular]
    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    "] - style 8 fill:#438dd5,stroke:#2e6295,color:#ffffff - 9["
    Mobile App
    [Container: Xamarin]
    Provides a limited subset of
    the Internet banking
    functionality to customers
    via their mobile device.
    "] - style 9 fill:#438dd5,stroke:#2e6295,color:#ffffff - 18[("
    Database
    [Container: Oracle Database Schema]
    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    ")] - style 18 fill:#438dd5,stroke:#2e6295,color:#ffffff - - subgraph 11 ["API Application"] - style 11 fill:#ffffff,stroke:#2e6295,color:#2e6295 - - 12["
    Sign In Controller
    [Component: Spring MVC Rest Controller]
    Allows users to sign in to
    the Internet Banking System.
    "] - style 12 fill:#85bbf0,stroke:#5d82a8,color:#000000 - 13["
    Accounts Summary Controller
    [Component: Spring MVC Rest Controller]
    Provides customers with a
    summary of their bank
    accounts.
    "] - style 13 fill:#85bbf0,stroke:#5d82a8,color:#000000 - 14["
    Reset Password Controller
    [Component: Spring MVC Rest Controller]
    Allows users to reset their
    passwords with a single use
    URL.
    "] - style 14 fill:#85bbf0,stroke:#5d82a8,color:#000000 - 15["
    Security Component
    [Component: Spring Bean]
    Provides functionality
    related to signing in,
    changing passwords, etc.
    "] - style 15 fill:#85bbf0,stroke:#5d82a8,color:#000000 - 16["
    Mainframe Banking System Facade
    [Component: Spring Bean]
    A facade onto the mainframe
    banking system.
    "] - style 16 fill:#85bbf0,stroke:#5d82a8,color:#000000 - 17["
    E-mail Component
    [Component: Spring Bean]
    Sends e-mails to users.
    "] - style 17 fill:#85bbf0,stroke:#5d82a8,color:#000000 - end - - 8-. "
    Makes API calls to
    [JSON/HTTPS]
    " .->12 - 8-. "
    Makes API calls to
    [JSON/HTTPS]
    " .->13 - 8-. "
    Makes API calls to
    [JSON/HTTPS]
    " .->14 - 9-. "
    Makes API calls to
    [JSON/HTTPS]
    " .->12 - 9-. "
    Makes API calls to
    [JSON/HTTPS]
    " .->13 - 9-. "
    Makes API calls to
    [JSON/HTTPS]
    " .->14 - 12-. "
    Uses
    " .->15 - 13-. "
    Uses
    " .->16 - 14-. "
    Uses
    " .->15 - 14-. "
    Uses
    " .->17 - 15-. "
    Reads from and writes to
    [SQL/TCP]
    " .->18 - 16-. "
    Makes API calls to
    [XML/HTTPS]
    " .->4 - 17-. "
    Sends e-mail using
    " .->5 - end \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-Containers.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-Containers.mmd deleted file mode 100644 index e962db529..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-Containers.mmd +++ /dev/null @@ -1,39 +0,0 @@ -graph TB - linkStyle default fill:#ffffff - - subgraph diagram ["Internet Banking System - Containers"] - style diagram fill:#ffffff,stroke:#ffffff - - 1["
    Personal Banking Customer
    [Person]
    A customer of the bank, with
    personal bank accounts.
    "] - style 1 fill:#08427b,stroke:#052e56,color:#ffffff - 4["
    Mainframe Banking System
    [Software System]
    Stores all of the core
    banking information about
    customers, accounts,
    transactions, etc.
    "] - style 4 fill:#999999,stroke:#6b6b6b,color:#ffffff - 5["
    E-mail System
    [Software System]
    The internal Microsoft
    Exchange e-mail system.
    "] - style 5 fill:#999999,stroke:#6b6b6b,color:#ffffff - - subgraph 7 ["Internet Banking System"] - style 7 fill:#ffffff,stroke:#0b4884,color:#0b4884 - - 10["
    Web Application
    [Container: Java and Spring MVC]
    Delivers the static content
    and the Internet banking
    single page application.
    "] - style 10 fill:#438dd5,stroke:#2e6295,color:#ffffff - 11["
    API Application
    [Container: Java and Spring MVC]
    Provides Internet banking
    functionality via a
    JSON/HTTPS API.
    "] - style 11 fill:#438dd5,stroke:#2e6295,color:#ffffff - 18[("
    Database
    [Container: Oracle Database Schema]
    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    ")] - style 18 fill:#438dd5,stroke:#2e6295,color:#ffffff - 8["
    Single-Page Application
    [Container: JavaScript and Angular]
    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    "] - style 8 fill:#438dd5,stroke:#2e6295,color:#ffffff - 9["
    Mobile App
    [Container: Xamarin]
    Provides a limited subset of
    the Internet banking
    functionality to customers
    via their mobile device.
    "] - style 9 fill:#438dd5,stroke:#2e6295,color:#ffffff - end - - 5-. "
    Sends e-mails to
    " .->1 - 1-. "
    Visits bigbank.com/ib using
    [HTTPS]
    " .->10 - 1-. "
    Views account balances, and
    makes payments using
    " .->8 - 1-. "
    Views account balances, and
    makes payments using
    " .->9 - 10-. "
    Delivers to the customer's
    web browser
    " .->8 - 8-. "
    Makes API calls to
    [JSON/HTTPS]
    " .->11 - 9-. "
    Makes API calls to
    [JSON/HTTPS]
    " .->11 - 11-. "
    Reads from and writes to
    [SQL/TCP]
    " .->18 - 11-. "
    Makes API calls to
    [XML/HTTPS]
    " .->4 - 11-. "
    Sends e-mail using
    " .->5 - end \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-DevelopmentDeployment.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-DevelopmentDeployment.mmd deleted file mode 100644 index 0347c49a8..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-DevelopmentDeployment.mmd +++ /dev/null @@ -1,61 +0,0 @@ -graph TB - linkStyle default fill:#ffffff - - subgraph diagram ["Internet Banking System - Deployment - Development"] - style diagram fill:#ffffff,stroke:#ffffff - - subgraph 50 ["Developer Laptop"] - style 50 fill:#ffffff,stroke:#888888,color:#000000 - - subgraph 51 ["Web Browser"] - style 51 fill:#ffffff,stroke:#888888,color:#000000 - - 52["
    Single-Page Application
    [Container: JavaScript and Angular]
    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    "] - style 52 fill:#438dd5,stroke:#2e6295,color:#ffffff - end - - subgraph 53 ["Docker Container - Web Server"] - style 53 fill:#ffffff,stroke:#888888,color:#000000 - - subgraph 54 ["Apache Tomcat"] - style 54 fill:#ffffff,stroke:#888888,color:#000000 - - 55["
    Web Application
    [Container: Java and Spring MVC]
    Delivers the static content
    and the Internet banking
    single page application.
    "] - style 55 fill:#438dd5,stroke:#2e6295,color:#ffffff - 57["
    API Application
    [Container: Java and Spring MVC]
    Provides Internet banking
    functionality via a
    JSON/HTTPS API.
    "] - style 57 fill:#438dd5,stroke:#2e6295,color:#ffffff - end - - end - - subgraph 59 ["Docker Container - Database Server"] - style 59 fill:#ffffff,stroke:#888888,color:#000000 - - subgraph 60 ["Database Server"] - style 60 fill:#ffffff,stroke:#888888,color:#000000 - - 61[("
    Database
    [Container: Oracle Database Schema]
    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    ")] - style 61 fill:#438dd5,stroke:#2e6295,color:#ffffff - end - - end - - end - - subgraph 63 ["Big Bank plc"] - style 63 fill:#ffffff,stroke:#888888,color:#000000 - - subgraph 64 ["bigbank-dev001"] - style 64 fill:#ffffff,stroke:#888888,color:#000000 - - 65["
    Mainframe Banking System
    [Software System]
    Stores all of the core
    banking information about
    customers, accounts,
    transactions, etc.
    "] - style 65 fill:#999999,stroke:#6b6b6b,color:#ffffff - end - - end - - 55-. "
    Delivers to the customer's
    web browser
    " .->52 - 52-. "
    Makes API calls to
    [JSON/HTTPS]
    " .->57 - 57-. "
    Reads from and writes to
    [SQL/TCP]
    " .->61 - 57-. "
    Makes API calls to
    [XML/HTTPS]
    " .->65 - end \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-LiveDeployment.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-LiveDeployment.mmd deleted file mode 100644 index e4cedb00e..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-LiveDeployment.mmd +++ /dev/null @@ -1,92 +0,0 @@ -graph TB - linkStyle default fill:#ffffff - - subgraph diagram ["Internet Banking System - Deployment - Live"] - style diagram fill:#ffffff,stroke:#ffffff - - subgraph 67 ["Customer's mobile device"] - style 67 fill:#ffffff,stroke:#888888,color:#000000 - - 68["
    Mobile App
    [Container: Xamarin]
    Provides a limited subset of
    the Internet banking
    functionality to customers
    via their mobile device.
    "] - style 68 fill:#438dd5,stroke:#2e6295,color:#ffffff - end - - subgraph 69 ["Customer's computer"] - style 69 fill:#ffffff,stroke:#888888,color:#000000 - - subgraph 70 ["Web Browser"] - style 70 fill:#ffffff,stroke:#888888,color:#000000 - - 71["
    Single-Page Application
    [Container: JavaScript and Angular]
    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    "] - style 71 fill:#438dd5,stroke:#2e6295,color:#ffffff - end - - end - - subgraph 72 ["Big Bank plc"] - style 72 fill:#ffffff,stroke:#888888,color:#000000 - - subgraph 73 ["bigbank-web***"] - style 73 fill:#ffffff,stroke:#888888,color:#000000 - - subgraph 74 ["Apache Tomcat"] - style 74 fill:#ffffff,stroke:#888888,color:#000000 - - 75["
    Web Application
    [Container: Java and Spring MVC]
    Delivers the static content
    and the Internet banking
    single page application.
    "] - style 75 fill:#438dd5,stroke:#2e6295,color:#ffffff - end - - end - - subgraph 77 ["bigbank-api***"] - style 77 fill:#ffffff,stroke:#888888,color:#000000 - - subgraph 78 ["Apache Tomcat"] - style 78 fill:#ffffff,stroke:#888888,color:#000000 - - 79["
    API Application
    [Container: Java and Spring MVC]
    Provides Internet banking
    functionality via a
    JSON/HTTPS API.
    "] - style 79 fill:#438dd5,stroke:#2e6295,color:#ffffff - end - - end - - subgraph 82 ["bigbank-db01"] - style 82 fill:#ffffff,stroke:#888888,color:#000000 - - subgraph 83 ["Oracle - Primary"] - style 83 fill:#ffffff,stroke:#888888,color:#000000 - - 84[("
    Database
    [Container: Oracle Database Schema]
    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    ")] - style 84 fill:#438dd5,stroke:#2e6295,color:#ffffff - end - - end - - subgraph 86 ["bigbank-db02"] - style 86 fill:#ffffff,stroke:#888888,color:#000000 - - subgraph 87 ["Oracle - Secondary"] - style 87 fill:#ffffff,stroke:#888888,color:#000000 - - 88[("
    Database
    [Container: Oracle Database Schema]
    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    ")] - style 88 fill:#438dd5,stroke:#2e6295,color:#ffffff - end - - end - - subgraph 90 ["bigbank-prod001"] - style 90 fill:#ffffff,stroke:#888888,color:#000000 - - 91["
    Mainframe Banking System
    [Software System]
    Stores all of the core
    banking information about
    customers, accounts,
    transactions, etc.
    "] - style 91 fill:#999999,stroke:#6b6b6b,color:#ffffff - end - - end - - 75-. "
    Delivers to the customer's
    web browser
    " .->71 - 68-. "
    Makes API calls to
    [JSON/HTTPS]
    " .->79 - 71-. "
    Makes API calls to
    [JSON/HTTPS]
    " .->79 - 79-. "
    Reads from and writes to
    [SQL/TCP]
    " .->84 - 79-. "
    Reads from and writes to
    [SQL/TCP]
    " .->88 - 79-. "
    Makes API calls to
    [XML/HTTPS]
    " .->91 - end \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SignIn-sequence.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SignIn-sequence.mmd deleted file mode 100644 index f25511e2f..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SignIn-sequence.mmd +++ /dev/null @@ -1,13 +0,0 @@ -sequenceDiagram - - participant 8 as Single-Page Application
    [Container: JavaScript and Angular] - participant 12 as Sign In Controller
    [Component: Spring MVC Rest Controller] - participant 15 as Security Component
    [Component: Spring Bean] - participant 18 as Database
    [Container: Oracle Database Schema] - - 8->>12: Submits credentials to
    [JSON/HTTPS] - 12->>15: Validates credentials using - 15->>18: select * from users where username = ?
    [SQL/TCP] - 18-->>15: Returns user data to
    [SQL/TCP] - 15-->>12: Returns true if the hashed password matches - 12-->>8: Sends back an authentication token to
    [JSON/HTTPS] \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SignIn.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SignIn.mmd deleted file mode 100644 index 378a93c7a..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SignIn.mmd +++ /dev/null @@ -1,27 +0,0 @@ -graph TB - linkStyle default fill:#ffffff - - subgraph diagram ["API Application - Dynamic"] - style diagram fill:#ffffff,stroke:#ffffff - - subgraph 11 ["API Application"] - style 11 fill:#ffffff,stroke:#2e6295,color:#2e6295 - - 12["
    Sign In Controller
    [Component: Spring MVC Rest Controller]
    Allows users to sign in to
    the Internet Banking System.
    "] - style 12 fill:#85bbf0,stroke:#5d82a8,color:#000000 - 15["
    Security Component
    [Component: Spring Bean]
    Provides functionality
    related to signing in,
    changing passwords, etc.
    "] - style 15 fill:#85bbf0,stroke:#5d82a8,color:#000000 - end - - 8["
    Single-Page Application
    [Container: JavaScript and Angular]
    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    "] - style 8 fill:#438dd5,stroke:#2e6295,color:#ffffff - 18[("
    Database
    [Container: Oracle Database Schema]
    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    ")] - style 18 fill:#438dd5,stroke:#2e6295,color:#ffffff - - 8-. "
    1. Submits credentials to
    [JSON/HTTPS]
    " .->12 - 12-. "
    2. Validates credentials
    using
    " .->15 - 15-. "
    3. select * from users where
    username = ?
    [SQL/TCP]
    " .->18 - 18-. "
    4. Returns user data to
    [SQL/TCP]
    " .->15 - 15-. "
    5. Returns true if the hashed
    password matches
    " .->12 - 12-. "
    6. Sends back an
    authentication token to
    [JSON/HTTPS]
    " .->8 - end \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SystemContext.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SystemContext.mmd deleted file mode 100644 index 1dd22f429..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SystemContext.mmd +++ /dev/null @@ -1,25 +0,0 @@ -graph TB - linkStyle default fill:#ffffff - - subgraph diagram ["Internet Banking System - System Context"] - style diagram fill:#ffffff,stroke:#ffffff - - subgraph group1 ["Big Bank plc"] - style group1 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 - - 4["
    Mainframe Banking System
    [Software System]
    Stores all of the core
    banking information about
    customers, accounts,
    transactions, etc.
    "] - style 4 fill:#999999,stroke:#6b6b6b,color:#ffffff - 5["
    E-mail System
    [Software System]
    The internal Microsoft
    Exchange e-mail system.
    "] - style 5 fill:#999999,stroke:#6b6b6b,color:#ffffff - 7["
    Internet Banking System
    [Software System]
    Allows customers to view
    information about their bank
    accounts, and make payments.
    "] - style 7 fill:#1168bd,stroke:#0b4884,color:#ffffff - end - - 1["
    Personal Banking Customer
    [Person]
    A customer of the bank, with
    personal bank accounts.
    "] - style 1 fill:#08427b,stroke:#052e56,color:#ffffff - - 1-. "
    Views account balances, and
    makes payments using
    " .->7 - 7-. "
    Gets account information
    from, and makes payments
    using
    " .->4 - 7-. "
    Sends e-mail using
    " .->5 - 5-. "
    Sends e-mails to
    " .->1 - end \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SystemLandscape.mmd b/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SystemLandscape.mmd deleted file mode 100644 index 83023a9e9..000000000 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/36141-SystemLandscape.mmd +++ /dev/null @@ -1,36 +0,0 @@ -graph TB - linkStyle default fill:#ffffff - - subgraph diagram ["System Landscape"] - style diagram fill:#ffffff,stroke:#ffffff - - subgraph group1 ["Big Bank plc"] - style group1 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 - - 2["
    Customer Service Staff
    [Person]
    Customer service staff within
    the bank.
    "] - style 2 fill:#999999,stroke:#6b6b6b,color:#ffffff - 3["
    Back Office Staff
    [Person]
    Administration and support
    staff within the bank.
    "] - style 3 fill:#999999,stroke:#6b6b6b,color:#ffffff - 4["
    Mainframe Banking System
    [Software System]
    Stores all of the core
    banking information about
    customers, accounts,
    transactions, etc.
    "] - style 4 fill:#999999,stroke:#6b6b6b,color:#ffffff - 5["
    E-mail System
    [Software System]
    The internal Microsoft
    Exchange e-mail system.
    "] - style 5 fill:#999999,stroke:#6b6b6b,color:#ffffff - 6["
    ATM
    [Software System]
    Allows customers to withdraw
    cash.
    "] - style 6 fill:#999999,stroke:#6b6b6b,color:#ffffff - 7["
    Internet Banking System
    [Software System]
    Allows customers to view
    information about their bank
    accounts, and make payments.
    "] - style 7 fill:#1168bd,stroke:#0b4884,color:#ffffff - end - - 1["
    Personal Banking Customer
    [Person]
    A customer of the bank, with
    personal bank accounts.
    "] - style 1 fill:#08427b,stroke:#052e56,color:#ffffff - - 1-. "
    Views account balances, and
    makes payments using
    " .->7 - 7-. "
    Gets account information
    from, and makes payments
    using
    " .->4 - 7-. "
    Sends e-mail using
    " .->5 - 5-. "
    Sends e-mails to
    " .->1 - 1-. "
    Asks questions to
    [Telephone]
    " .->2 - 2-. "
    Uses
    " .->4 - 1-. "
    Withdraws cash using
    " .->6 - 6-. "
    Uses
    " .->4 - 3-. "
    Uses
    " .->4 - end \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/MermaidDiagramExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/mermaid/MermaidDiagramExporterTests.java index 065b56220..fe176f043 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/MermaidDiagramExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/MermaidDiagramExporterTests.java @@ -33,7 +33,7 @@ public void test_AmazonWebServicesExample() throws Exception { graph LR linkStyle default fill:#ffffff - subgraph diagram ["X - Deployment - Live"] + subgraph diagram ["Deployment View: X - Live"] style diagram fill:#ffffff,stroke:#ffffff subgraph 5 ["Amazon Web Services"] @@ -95,7 +95,7 @@ public void test_GroupsExample() throws Exception { graph TB linkStyle default fill:#ffffff - subgraph diagram ["System Landscape"] + subgraph diagram ["System Landscape View"] style diagram fill:#ffffff,stroke:#ffffff subgraph group1 ["Group 1"] @@ -133,7 +133,7 @@ public void test_GroupsExample() throws Exception { graph TB linkStyle default fill:#ffffff - subgraph diagram ["D - Containers"] + subgraph diagram ["Container View: D"] style diagram fill:#ffffff,stroke:#ffffff 3["
    C
    [Software System]
    "] @@ -163,7 +163,7 @@ public void test_GroupsExample() throws Exception { graph TB linkStyle default fill:#ffffff - subgraph diagram ["D - F - Components"] + subgraph diagram ["Component View: D - F"] style diagram fill:#ffffff,stroke:#ffffff 3["
    C
    [Software System]
    "] @@ -220,7 +220,7 @@ public void test_NestedGroupsExample() throws Exception { graph TB linkStyle default fill:#ffffff - subgraph diagram ["System Landscape"] + subgraph diagram ["System Landscape View"] style diagram fill:#ffffff,stroke:#ffffff subgraph group1 ["Organisation 1"] @@ -281,7 +281,7 @@ public void test_renderContainerDiagramWithExternalContainers() { graph TB linkStyle default fill:#ffffff - subgraph diagram ["Software System 1 - Containers"] + subgraph diagram ["Container View: Software System 1"] style diagram fill:#ffffff,stroke:#ffffff subgraph 1 ["Software System 1"] @@ -299,7 +299,7 @@ public void test_renderContainerDiagramWithExternalContainers() { end 2-. "
    Uses
    " .->4 - + end""", diagram.getDefinition()); } @@ -324,7 +324,7 @@ public void test_renderComponentDiagramWithExternalComponents() { graph TB linkStyle default fill:#ffffff - subgraph diagram ["Software System 1 - Container 1 - Components"] + subgraph diagram ["Component View: Software System 1 - Container 1"] style diagram fill:#ffffff,stroke:#ffffff subgraph 2 ["Container 1"] @@ -342,7 +342,7 @@ public void test_renderComponentDiagramWithExternalComponents() { end 3-. "
    Uses
    " .->6 - + end""", diagram.getDefinition()); } @@ -365,7 +365,7 @@ public void test_renderGroupStyles() { graph TB linkStyle default fill:#ffffff - subgraph diagram ["System Landscape"] + subgraph diagram ["System Landscape View"] style diagram fill:#ffffff,stroke:#ffffff subgraph group1 ["Group 1"] @@ -399,7 +399,7 @@ public void test_renderGroupStyles() { graph TB linkStyle default fill:#ffffff - subgraph diagram ["System Landscape"] + subgraph diagram ["System Landscape View"] style diagram fill:#ffffff,stroke:#ffffff subgraph group1 ["Group 1"] diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java index 02cf8273a..784c6fe7a 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java @@ -29,7 +29,7 @@ public void test_BigBankPlcExample() throws Exception { Diagram diagram = diagrams.stream().filter(d -> d.getKey().equals("SystemLandscape")).findFirst().get(); assertEquals(""" @startuml - title System Landscape + title System Landscape View set separator none top to bottom direction @@ -80,7 +80,7 @@ public void test_BigBankPlcExample() throws Exception { diagram = diagrams.stream().filter(d -> d.getKey().equals("SystemContext")).findFirst().get(); assertEquals(""" @startuml - title Internet Banking System - System Context\\nThe system context diagram for the Internet Banking System. + title System Context View: Internet Banking System\\nThe system context diagram for the Internet Banking System. set separator none top to bottom direction @@ -122,7 +122,7 @@ public void test_BigBankPlcExample() throws Exception { diagram = diagrams.stream().filter(d -> d.getKey().equals("Containers")).findFirst().get(); assertEquals(""" @startuml - title Internet Banking System - Containers\\nThe container diagram for the Internet Banking System. + title Container View: Internet Banking System\\nThe container diagram for the Internet Banking System. set separator none top to bottom direction @@ -179,7 +179,7 @@ public void test_BigBankPlcExample() throws Exception { diagram = diagrams.stream().filter(d -> d.getKey().equals("Components")).findFirst().get(); assertEquals(""" @startuml - title Internet Banking System - API Application - Components\\nThe component diagram for the API Application. + title Component View: Internet Banking System - API Application\\nThe component diagram for the API Application. set separator none top to bottom direction @@ -242,7 +242,7 @@ public void test_BigBankPlcExample() throws Exception { diagram = diagrams.stream().filter(d -> d.getKey().equals("SignIn")).findFirst().get(); assertEquals(""" @startuml - title API Application - Dynamic\\nSummarises how the sign in feature works in the single-page application. + title Dynamic View: Internet Banking System - API Application\\nSummarises how the sign in feature works in the single-page application. set separator none top to bottom direction @@ -289,7 +289,7 @@ public void test_BigBankPlcExample() throws Exception { diagram = diagrams.stream().filter(md -> md.getKey().equals("DevelopmentDeployment")).findFirst().get(); assertEquals(""" @startuml - title Internet Banking System - Deployment - Development\\nAn example development deployment scenario for the Internet Banking System. + title Deployment View: Internet Banking System - Development\\nAn example development deployment scenario for the Internet Banking System. set separator none top to bottom direction @@ -355,7 +355,7 @@ public void test_BigBankPlcExample() throws Exception { diagram = diagrams.stream().filter(md -> md.getKey().equals("LiveDeployment")).findFirst().get(); assertEquals(""" @startuml - title Internet Banking System - Deployment - Live\\nAn example live deployment scenario for the Internet Banking System. + title Deployment View: Internet Banking System - Live\\nAn example live deployment scenario for the Internet Banking System. set separator none top to bottom direction @@ -446,7 +446,7 @@ public void test_BigBankPlcExample() throws Exception { diagram = diagrams.stream().filter(d -> d.getKey().equals("SignIn")).findFirst().get(); assertEquals(""" @startuml - title API Application - Dynamic\\nSummarises how the sign in feature works in the single-page application. + title Dynamic View: Internet Banking System - API Application\\nSummarises how the sign in feature works in the single-page application. set separator none @@ -497,7 +497,7 @@ public void test_AmazonWebServicesExampleWithoutTags() throws Exception { Diagram diagram = diagrams.stream().findFirst().get(); assertEquals(""" @startuml - title X - Deployment - Live + title Deployment View: X - Live set separator none left to right direction @@ -560,7 +560,7 @@ public void test_AmazonWebServicesExampleWithTags() throws Exception { Diagram diagram = diagrams.stream().findFirst().get(); assertEquals(""" @startuml - title X - Deployment - Live + title Deployment View: X - Live set separator none left to right direction @@ -632,7 +632,7 @@ public void test_GroupsExample() throws Exception { Diagram diagram = diagrams.stream().filter(md -> md.getKey().equals("SystemLandscape")).findFirst().get(); assertEquals(""" @startuml - title System Landscape + title System Landscape View set separator none top to bottom direction @@ -675,7 +675,7 @@ public void test_GroupsExample() throws Exception { diagram = diagrams.stream().filter(md -> md.getKey().equals("Containers")).findFirst().get(); assertEquals(""" @startuml - title D - Containers + title Container View: D set separator none top to bottom direction @@ -712,7 +712,7 @@ public void test_GroupsExample() throws Exception { diagram = diagrams.stream().filter(md -> md.getKey().equals("Components")).findFirst().get(); assertEquals(""" @startuml - title D - F - Components + title Component View: D - F set separator none top to bottom direction @@ -776,7 +776,7 @@ public void test_NestedGroupsExample() throws Exception { Diagram diagram = diagrams.stream().filter(md -> md.getKey().equals("SystemLandscape")).findFirst().get(); assertEquals(""" @startuml - title System Landscape\\nDescription + title System Landscape View\\nDescription set separator none top to bottom direction @@ -839,7 +839,7 @@ public void test_NestedGroupsExample_WithDotAsGroupSeparator() throws Exception Diagram diagram = diagrams.stream().filter(md -> md.getKey().equals("SystemLandscape")).findFirst().get(); assertEquals(""" @startuml - title System Landscape\\nDescription + title System Landscape View\\nDescription set separator none top to bottom direction @@ -896,7 +896,7 @@ protected double calculateIconScale(String iconUrl, int maxIconSize) { Diagram diagram = exporter.export(view); assertEquals(""" @startuml - title System Landscape + title System Landscape View set separator none top to bottom direction @@ -936,7 +936,7 @@ protected double calculateIconScale(String iconUrl, int maxIconSize) { diagram = exporter.export(view); assertEquals(""" @startuml - title System Landscape + title System Landscape View set separator none top to bottom direction @@ -990,7 +990,7 @@ public void test_renderContainerDiagramWithExternalContainers() { Diagram diagram = new C4PlantUMLExporter().export(containerView); assertEquals(""" @startuml - title Software System 1 - Containers + title Container View: Software System 1 set separator none top to bottom direction @@ -1041,7 +1041,7 @@ public void test_renderComponentDiagramWithExternalComponents() { Diagram diagram = new C4PlantUMLExporter().export(componentView); assertEquals(""" @startuml - title Software System 1 - Container 1 - Components + title Component View: Software System 1 - Container 1 set separator none top to bottom direction @@ -1085,7 +1085,7 @@ public void test_renderDiagramWithElementUrls() { Diagram diagram = new C4PlantUMLExporter().export(view); assertEquals(""" @startuml - title System Landscape\\nDescription + title System Landscape View\\nDescription set separator none top to bottom direction @@ -1120,7 +1120,7 @@ public void test_renderDiagramWithIncludes() { Diagram diagram = new C4PlantUMLExporter().export(view); assertEquals(""" @startuml - title System Landscape\\nDescription + title System Landscape View\\nDescription set separator none top to bottom direction @@ -1156,7 +1156,7 @@ public void test_renderDiagramWithNewLineCharacterInElementName() { Diagram diagram = new C4PlantUMLExporter().export(view); assertEquals(""" @startuml - title System Landscape\\nDescription + title System Landscape View\\nDescription set separator none top to bottom direction @@ -1190,7 +1190,7 @@ public void test_renderInfrastructureNodeWithTechnology() { Diagram diagram = new C4PlantUMLExporter().export(view); assertEquals(""" @startuml - title Deployment - Default\\nview description + title Deployment View: Default\\nview description set separator none top to bottom direction @@ -1239,7 +1239,7 @@ public void test_printProperties() throws Exception { Diagram diagram = new C4PlantUMLExporter().export(view); assertEquals(""" @startuml - title SoftwareSystem - Containers + title Container View: SoftwareSystem set separator none top to bottom direction @@ -1293,7 +1293,7 @@ public void test_deploymentViewPrintProperties() throws Exception { Diagram diagram = new C4PlantUMLExporter().export(deploymentView); assertEquals(""" @startuml - title Deployment - Default + title Deployment View: Default set separator none top to bottom direction @@ -1336,7 +1336,7 @@ public void test_legendAndStereotypes() { Diagram diagram = new C4PlantUMLExporter().export(view); assertEquals(""" @startuml - title System Landscape\\nDescription + title System Landscape View\\nDescription set separator none top to bottom direction @@ -1363,7 +1363,7 @@ public void test_legendAndStereotypes() { diagram = new C4PlantUMLExporter().export(view); assertEquals(""" @startuml - title System Landscape\\nDescription + title System Landscape View\\nDescription set separator none top to bottom direction @@ -1390,7 +1390,7 @@ public void test_legendAndStereotypes() { diagram = new C4PlantUMLExporter().export(view); assertEquals(""" @startuml - title System Landscape\\nDescription + title System Landscape View\\nDescription set separator none top to bottom direction @@ -1417,7 +1417,7 @@ public void test_legendAndStereotypes() { diagram = new C4PlantUMLExporter().export(view); assertEquals(""" @startuml - title System Landscape\\nDescription + title System Landscape View\\nDescription set separator none top to bottom direction @@ -1465,7 +1465,7 @@ public void test_renderContainerShapes() throws Exception { Diagram diagram = new C4PlantUMLExporter().export(containerView); assertEquals(""" @startuml - title Software System - Containers + title Container View: Software System set separator none top to bottom direction @@ -1522,7 +1522,7 @@ public void test_renderComponentShapes() throws Exception { Diagram diagram = new C4PlantUMLExporter().export(componentView); assertEquals(""" @startuml - title Software System - Container - Components + title Component View: Software System - Container set separator none top to bottom direction @@ -1561,7 +1561,7 @@ public void testFont() { Diagram diagram = new C4PlantUMLExporter().export(view); assertEquals(""" @startuml - title System Landscape\\nDescription + title System Landscape View\\nDescription set separator none top to bottom direction @@ -1596,7 +1596,7 @@ public void stdlib_false() { Diagram diagram = new C4PlantUMLExporter().export(view); assertEquals(""" @startuml - title System Landscape\\nDescription + title System Landscape View\\nDescription set separator none top to bottom direction @@ -1631,7 +1631,7 @@ public void componentWithoutTechnology() { Diagram diagram = new C4PlantUMLExporter().export(view); assertEquals(""" @startuml - title Name - Name - Components\\nDescription + title Component View: Name - Name\\nDescription set separator none top to bottom direction @@ -1668,7 +1668,7 @@ public void borderStyling() { Diagram diagram = new C4PlantUMLExporter().export(view); assertEquals(""" @startuml - title System Landscape\\nDescription + title System Landscape View\\nDescription set separator none top to bottom direction @@ -1703,7 +1703,7 @@ public void elementWithUrl() { Diagram diagram = new C4PlantUMLExporter().export(view); assertEquals(""" @startuml - title System Landscape\\nDescription + title System Landscape View\\nDescription set separator none top to bottom direction diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java index 8a6e1ff66..6e16d8b9b 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java @@ -33,7 +33,7 @@ public void systemLandscapeView_NoStyling_Light() { assertEquals(""" @startuml - title System Landscape\\nDescription + title System Landscape View\\nDescription set separator none top to bottom direction @@ -135,7 +135,7 @@ public void systemLandscapeView_NoStyling_Dark() { assertEquals(""" @startuml - title System Landscape\\nDescription + title System Landscape View\\nDescription set separator none top to bottom direction @@ -237,7 +237,7 @@ public void systemContextView_NoStyling_Light() { assertEquals(""" @startuml - title A - System Context\\nDescription + title System Context View: A\\nDescription set separator none top to bottom direction @@ -294,7 +294,7 @@ public void containerView_NoStyling_Light() { assertEquals(""" @startuml - title Software System A - Containers\\nDescription + title Container View: Software System A\\nDescription set separator none top to bottom direction @@ -365,7 +365,7 @@ public void componentView_NoStyling_Light() { assertEquals(""" @startuml - title Software System - Container - Components\\nDescription + title Component View: Software System - Container\\nDescription set separator none top to bottom direction @@ -435,7 +435,7 @@ public void deploymentView_NoStyling_Light() { assertEquals(""" @startuml - title Deployment - Default\\nDefault + title Deployment View: Default\\nDefault set separator none top to bottom direction @@ -490,7 +490,7 @@ public void dynamicView_CollaborationStyle_NoStyling_Light() { assertEquals(""" @startuml - title Dynamic\\nDescription + title Dynamic View\\nDescription set separator none top to bottom direction @@ -552,7 +552,7 @@ public void dynamicView_CollaborationStyle_Frames() { assertEquals(""" @startuml - title Dynamic\\nDescription + title Dynamic View\\nDescription set separator none top to bottom direction @@ -597,7 +597,7 @@ public void dynamicView_CollaborationStyle_Frames() { assertEquals(""" @startuml - title Dynamic\\nDescription + title Dynamic View\\nDescription set separator none top to bottom direction @@ -658,7 +658,7 @@ public void dynamicView_SequenceStyle_NoStyling_Light() { assertEquals(""" @startuml - title Dynamic\\nDescription + title Dynamic View\\nDescription set separator none hide stereotype @@ -755,7 +755,7 @@ public void groups() throws Exception { Diagram diagram = diagrams.stream().filter(md -> md.getKey().equals("SystemLandscape")).findFirst().get(); assertEquals(""" @startuml - title System Landscape + title System Landscape View set separator none top to bottom direction @@ -846,7 +846,7 @@ public void groups() throws Exception { diagram = diagrams.stream().filter(md -> md.getKey().equals("Containers")).findFirst().get(); assertEquals(""" @startuml - title D - Containers + title Container View: D set separator none top to bottom direction @@ -921,7 +921,7 @@ public void groups() throws Exception { diagram = diagrams.stream().filter(md -> md.getKey().equals("Components")).findFirst().get(); assertEquals(""" @startuml - title D - F - Components + title Component View: D - F set separator none top to bottom direction @@ -1026,7 +1026,7 @@ public void nestedGroups() { Diagram diagram = diagrams.stream().filter(md -> md.getKey().equals("SystemLandscape")).findFirst().get(); assertEquals(""" @startuml - title System Landscape + title System Landscape View set separator none top to bottom direction @@ -1147,7 +1147,7 @@ public void containerDiagramWithExternalContainers() { Diagram diagram = new StructurizrPlantUMLExporter().export(containerView); assertEquals(""" @startuml - title Software System 1 - Containers + title Container View: Software System 1 set separator none top to bottom direction @@ -1239,7 +1239,7 @@ public void componentDiagramWithExternalComponents() { Diagram diagram = new StructurizrPlantUMLExporter().export(componentView); assertEquals(""" @startuml - title Software System 1 - Container 1 - Components + title Component View: Software System 1 - Container 1 set separator none top to bottom direction @@ -1334,7 +1334,7 @@ public void componentDiagramWithExternalComponentsAndSoftwareSystemBoundariesInc Diagram diagram = new StructurizrPlantUMLExporter().export(componentView); assertEquals(""" @startuml - title Software System 1 - Container 1 - Components + title Component View: Software System 1 - Container 1 set separator none top to bottom direction @@ -1448,7 +1448,7 @@ public void dynamicView_ExternalContainers() { Diagram diagram = new StructurizrPlantUMLExporter().export(dynamicView); assertEquals(""" @startuml - title Software System 1 - Dynamic + title Dynamic View: Software System 1 set separator none top to bottom direction @@ -1539,7 +1539,7 @@ public void dynamicView_ExternalComponents() { Diagram diagram = new StructurizrPlantUMLExporter().export(dynamicView); assertEquals(""" @startuml - title Container 1 - Dynamic + title Dynamic View: Software System 1 - Container 1 set separator none top to bottom direction @@ -1633,7 +1633,7 @@ public void dynamicView_ExternalComponentsAndSoftwareSystemBoundariesIncluded() Diagram diagram = new StructurizrPlantUMLExporter().export(dynamicView); assertEquals(""" @startuml - title Container 1 - Dynamic + title Dynamic View: Software System 1 - Container 1 set separator none top to bottom direction @@ -1771,7 +1771,7 @@ public void newLineCharacterInElementName() { Diagram diagram = new StructurizrPlantUMLExporter().export(view); assertEquals(""" @startuml - title System Landscape + title System Landscape View set separator none top to bottom direction @@ -1867,7 +1867,7 @@ void renderWorkspaceWithUnicodeElementName() { assertEquals(""" @startuml - title System Landscape\\nDescription + title System Landscape View\\nDescription set separator none top to bottom direction @@ -1954,7 +1954,7 @@ public void dynamicView_UnscopedWithGroups() { Diagram diagram = exporter.export(view); assertEquals(""" @startuml - title Dynamic + title Dynamic View set separator none top to bottom direction @@ -2041,7 +2041,7 @@ public void dynamicView_SoftwareSystemScopedWithGroups() { Diagram diagram = exporter.export(view); assertEquals(""" @startuml - title A - Dynamic + title Dynamic View: A set separator none top to bottom direction @@ -2157,7 +2157,7 @@ public void dynamicView_ContainerScopedWithGroups() { Diagram diagram = exporter.export(view); assertEquals(""" @startuml - title A - Dynamic + title Dynamic View: A - A set separator none top to bottom direction @@ -2277,7 +2277,7 @@ public void deploymentView_WithGroups() { Diagram diagram = exporter.export(view); assertEquals(""" @startuml - title Deployment - Default + title Deployment View: Default set separator none top to bottom direction @@ -2352,7 +2352,7 @@ void light_group() { Diagram diagram = exporter.export(view); assertEquals(""" @startuml - title System Landscape + title System Landscape View set separator none top to bottom direction @@ -2409,7 +2409,7 @@ void dark_group() { Diagram diagram = exporter.export(view); assertEquals(""" @startuml - title System Landscape + title System Landscape View set separator none top to bottom direction @@ -2466,7 +2466,7 @@ public void amazonWebServicesExample_Light() throws Exception { Diagram diagram = diagrams.stream().findFirst().get(); assertEquals(""" @startuml - title X - Deployment - Live + title Deployment View: X - Live set separator none left to right direction @@ -2825,7 +2825,7 @@ public void amazonWebServicesExample_Dark() throws Exception { Diagram diagram = diagrams.stream().findFirst().get(); assertEquals(""" @startuml - title X - Deployment - Live + title Deployment View: X - Live set separator none left to right direction @@ -3171,4 +3171,16 @@ public void amazonWebServicesExample_Dark() throws Exception { @enduml""", diagram.getLegend().getDefinition()); } + @Test + void c4model() throws Exception { + Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("/Users/simon/Desktop/c4model/c4model/01-original/workspace.json")); + StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(); + + View view = workspace.getViews().getViewWithKey("Dynamic-SignIn-Collaboration"); + + Diagram diagram = exporter.export((DynamicView)view); + System.out.println(diagram.getDefinition()); + + } + } \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/websequencediagrams/WebSequenceDiagramsExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/websequencediagrams/WebSequenceDiagramsExporterTests.java index 25319e27f..9e9d6b8a3 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/websequencediagrams/WebSequenceDiagramsExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/websequencediagrams/WebSequenceDiagramsExporterTests.java @@ -25,7 +25,7 @@ public void test_BigBankPlcExample() throws Exception { Diagram diagram = diagrams.stream().filter(d -> d.getKey().equals("SignIn")).findFirst().get(); assertEquals(""" - title API Application - Dynamic - SignIn + title Dynamic View: Internet Banking System - API Application\\nSummarises how the sign in feature works in the single-page application. participant <>\\nSingle-Page Application as Single-Page Application participant <>\\nSign In Controller as Sign In Controller @@ -56,7 +56,7 @@ public void test_dynamicViewThatDoeNotOverrideRelationshipDescriptions() throws Collection diagrams = exporter.export(workspace); Diagram diagram = diagrams.iterator().next(); assertEquals(""" - title Dynamic - key + title Dynamic View\\nDescription participant <>\\nA as A participant <>\\nB as B From 146a514388e8921e60204f4382776059a6ec6efb Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 18 Sep 2025 12:06:59 +0100 Subject: [PATCH 679/717] Propagates view title/name and description to image views based upon a model view. --- .../structurizr/dsl/ImageViewContentParser.java | 15 +++++++++++++++ .../dsl/ImageViewContentParserTests.java | 9 ++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewContentParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewContentParser.java index 2264a16b8..5e8021fcb 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewContentParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewContentParser.java @@ -7,6 +7,7 @@ import com.structurizr.importer.diagrams.mermaid.MermaidImporter; import com.structurizr.importer.diagrams.plantuml.PlantUMLImporter; import com.structurizr.util.ImageUtils; +import com.structurizr.util.StringUtils; import com.structurizr.util.Url; import com.structurizr.view.ImageView; import com.structurizr.view.ModelView; @@ -56,6 +57,13 @@ void parsePlantUML(ImageViewDslContext context, File dslFile, Tokens tokens) { StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(); String plantuml = exporter.export((ModelView) viewWithKey).getDefinition(); new PlantUMLImporter().importDiagram(context.getView(), plantuml); + + if (!StringUtils.isNullOrEmpty(viewWithKey.getTitle())) { + context.getView().setTitle(viewWithKey.getTitle()); + } else { + context.getView().setTitle(viewWithKey.getName()); + } + context.getView().setDescription(viewWithKey.getDescription()); } else { if (Url.isUrl(source)) { RemoteContent content = readFromUrl(source); @@ -107,6 +115,13 @@ void parseMermaid(ImageViewDslContext context, File dslFile, Tokens tokens) { MermaidDiagramExporter exporter = new MermaidDiagramExporter(); String mermaid = exporter.export((ModelView) viewWithKey).getDefinition(); new MermaidImporter().importDiagram(context.getView(), mermaid); + + if (!StringUtils.isNullOrEmpty(viewWithKey.getTitle())) { + context.getView().setTitle(viewWithKey.getTitle()); + } else { + context.getView().setTitle(viewWithKey.getName()); + } + context.getView().setDescription(viewWithKey.getDescription()); } else { if (Url.isUrl(source)) { RemoteContent content = readFromUrl(source); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewContentParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewContentParserTests.java index 4a2266c73..6de21a3b0 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewContentParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewContentParserTests.java @@ -55,8 +55,9 @@ void test_parsePlantUML_WithViewKey() { parser = new ImageViewContentParser(true); parser.parsePlantUML(context, null, tokens("plantuml", "SystemLandscape")); - assertEquals("System Landscape", imageView.getTitle()); - assertEquals("https://plantuml.com/plantuml/svg/ZLBBJiCm4BpxArRb32rLuH2IgE4b3kL243q01pVU9bOJRsHlr0VYtt6Qj1n0Y9LihMTjpth6KyVISbELWZMN2A7JEmp6apZTEiOAPj8ebyaQms5RYT_CSSSjkipgcZMPlYY4GmQ7jRIIoO8XWuAf1YPO43DLeBJ5h3qY2gqGF8T5ucsDGeIEjwM_1C0ICNpu1E1QPglSKcFK3PLa0pXPxkDgNxqdmmTyieyM__HZE8Ix4Yiqx1TdVNhwDD-KY_bBev8e-XV1J1lyIT3XQTjk0ADlvBdGsSgWSm6C_sgmmzDMHnZto0DPlVEeB9DIvwPjDu3CpsYx3MaX5QsroO-KZtAZgwQQQyL509EBKVTuRqOdf6YbbYRtjWwYA3bOTtuPlwQqvBMq29tDxxs10mZ3txIAOv0E4Y6cQ9J_B5y0", imageView.getContent()); + assertEquals("System Landscape View", imageView.getTitle()); + assertEquals("Description", imageView.getDescription()); + assertEquals("https://plantuml.com/plantuml/svg/ZLBBJiCm4BpxArPmXfQgS0X9rF0IXt8Xg3q01pVP9bOTRsHlr0VYtt6Qj1n0Y9LihMTjpth64yVISbDfmOerGkZK3eFHE4wtZh62gJIvosIDC5Eu3WTjENupnsrtw3AhQbPa-g8G3XaSrj9A9Wk630gc6fXWGSnKGQuiPkqHKQeSmHDP9DxMA4JeUAlz9G2MYE739m0tCbiLbXgJtv8c6y3fSX_N--e36JxWutsq-ASVWm7SQwpGi5-Sz-dPytoZ5_DPaoTHz2-2gJBuaw33qxRT08RVo4kfifL1vm8O_TLWXwUjZZ3gaKUoQkTHgHEj2jEs6q3cPxJTXhIKEQsLAOwKJtAZggQQgvpB0CQNm-xntenEID5ABKtXlJs9ekHWtSLL_9hIajVI8dHUl_S6da0O_gPL78Dqa0WnGPFx7_C5", imageView.getContent()); } @Test @@ -95,7 +96,9 @@ void test_parseMermaid_WithViewKey() { parser = new ImageViewContentParser(true); parser.parseMermaid(context, null, tokens("mermaid", "SystemLandscape")); - assertEquals("https://mermaid.ink/svg/pako:eJxtkMtqwzAQRX9lmFK8cWgChYKaGNp1d-4uzkKxRraIHkaaNE1D_r12lEJfs5qBM4fLPQG2QREK7KIcenh9bjyANX5X89ESKNJybxm0sVbc6Ms0fmLSfptflJHj4mDdYH1MTA5epFeplQM1uJnQEc6yK_ldViaOYUc_3QCL0bZU5i1_rgodPM8OZLqeBWyDVUX1tLwbgeoPlcwHCXiY3z6Ck7EzfsZhEDAf3otqXQfNBxkJctRNdvzKufg_4f1lyjbYEL-unJe8whLQUXTSKBQn5J7c1Oq1PzyfPwHoMXnQ", imageView.getContent()); + assertEquals("System Landscape View", imageView.getTitle()); + assertEquals("Description", imageView.getDescription()); + assertEquals("https://mermaid.ink/svg/pako:eJxtkM1rwkAQxf-VYYrkEqmCUNjaQD33luLFeFizs8nifoTd1WjF_72Ja6Ffc5qB33vzeBfA2glCho3nXQvvq8oCaGX3ZTxrAkGSH3QEqbRmD_I2lR2ZcNgliVB8WAxsKizPIZKBN25FqHlHsFbUV7gd-UGRHO_4d8c8RO_29PMBwHywXAp1TMqXTDobpz2ppo0Mdk6LrHhdPg5A8YcK6oMYPM0mz2C4b5SdRtcxmHWnrNiUTsaee4KUd5s8fuWc_59wcZu8dtr5ryvlJSswBzTkDVcC2QVjS2as9l4iXq-fYuV7iw==", imageView.getContent()); } @Test From 2adf519a76687bab3ecd5a9117608ba6c6f0e96f Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 19 Sep 2025 13:17:28 +0100 Subject: [PATCH 680/717] Adds support for separate images for light and dark color schemes. --- changelog.md | 3 +- .../java/com/structurizr/view/ImageView.java | 54 +++- .../com/structurizr/view/ImageViewTests.java | 27 ++ .../dsl/ImageViewContentParser.java | 271 +++++++++--------- .../structurizr/dsl/ImageViewDslContext.java | 30 +- .../com/structurizr/dsl/NameValuePair.java | 3 +- .../structurizr/dsl/StructurizrDslParser.java | 49 +++- .../java/com/structurizr/dsl/DslTests.java | 39 ++- .../dsl/ImageViewContentParserTests.java | 181 +++++++++++- .../dsl/ImageViewDslContextTests.java | 31 ++ .../src/test/resources/dsl/test.dsl | 10 + .../src/test/resources/dsl/text-block.dsl | 31 +- .../diagrams/image/ImageImporter.java | 16 +- .../diagrams/kroki/KrokiImporter.java | 17 +- .../diagrams/mermaid/MermaidImporter.java | 17 +- .../diagrams/plantuml/PlantUMLImporter.java | 17 +- .../diagrams/image/ImageImporterTests.java | 3 +- 17 files changed, 605 insertions(+), 194 deletions(-) create mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewDslContextTests.java diff --git a/changelog.md b/changelog.md index 91d9b037a..be3db6f30 100644 --- a/changelog.md +++ b/changelog.md @@ -3,7 +3,8 @@ ## v5.0.0 (unreleased) - structurizr-core: Removes support for deprecated enterprise and location concepts. -- structurizr-core: Adds support for filtered deployment views (https://github.com/structurizr/java/issues/409). +- structurizr-core: Adds support for filtered deployment views (https://github.com/structurizr/java/issues/409). +- structurizr-core: Adds support for separate images for light and dark color schemes. - structurizr-component: Fixes https://github.com/structurizr/java/issues/437 (Make ComponentFinder.run() not fail on empty Set). - structurizr-dsl: Adds support for `iconPosition` on element styles (options are `Top`, `Bottom`, `Left`). - structurizr-dsl: Adds support for defining element and relationship styles for light and dark mode. diff --git a/structurizr-core/src/main/java/com/structurizr/view/ImageView.java b/structurizr-core/src/main/java/com/structurizr/view/ImageView.java index b67845a32..bd66943d2 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/ImageView.java +++ b/structurizr-core/src/main/java/com/structurizr/view/ImageView.java @@ -13,6 +13,8 @@ public final class ImageView extends View { private Element element; private String elementId; private String content; + private String contentLight; + private String contentDark; private String contentType; ImageView() { @@ -62,17 +64,58 @@ public String getContent() { return content; } + /** + * Gets the content of this view (a URL or a data URI), for the light color scheme. + * + * @return the content, as a String + */ + public String getContentLight() { + return contentLight; + } + + /** + * Gets the content of this view (a URL or a data URI), for the dark color scheme. + * + * @return the content, as a String + */ + public String getContentDark() { + return contentDark; + } + /** * Sets the content of this image view, which needs to be a URL or a data URI. * * @param content the content of this view */ public void setContent(String content) { + setContent(content, null); + } + + /** + * Sets the content of this image view, which needs to be a URL or a data URI. + * + * @param content the content of this view + */ + public void setContent(String content, ColorScheme colorScheme) { if (StringUtils.isNullOrEmpty(content)) { - this.content = null; + if (colorScheme == ColorScheme.Dark) { + this.contentDark = null; + } else if (colorScheme == ColorScheme.Light) { + this.contentLight = null; + } else { + this.content = null; + } } else { ImageUtils.validateImage(content); - this.content = content.trim(); + content = content.trim(); + + if (colorScheme == ColorScheme.Dark) { + this.contentDark = content; + } else if (colorScheme == ColorScheme.Light) { + this.contentLight = content; + } else { + this.content = content; + } } } @@ -99,4 +142,11 @@ public String getName() { return getTitle(); } + public boolean hasContent() { + return + !StringUtils.isNullOrEmpty(content) || + !StringUtils.isNullOrEmpty(contentLight) || + !StringUtils.isNullOrEmpty(contentDark); + } + } \ No newline at end of file diff --git a/structurizr-core/src/test/java/com/structurizr/view/ImageViewTests.java b/structurizr-core/src/test/java/com/structurizr/view/ImageViewTests.java index b7b921099..97056284e 100644 --- a/structurizr-core/src/test/java/com/structurizr/view/ImageViewTests.java +++ b/structurizr-core/src/test/java/com/structurizr/view/ImageViewTests.java @@ -27,4 +27,31 @@ void construction_WhenAnElementIsSpecified() { assertEquals(softwareSystem.getId(), view.getElementId()); } + @Test + void hasContent_WhenNoContent() { + ImageView view = views.createImageView("key"); + assertFalse(view.hasContent()); + } + + @Test + void hasContent_WhenContent() { + ImageView view = views.createImageView("key"); + view.setContent("https://example.com/image.png"); + assertTrue(view.hasContent()); + } + + @Test + void hasContent_WhenContentLight() { + ImageView view = views.createImageView("key"); + view.setContent("https://example.com/image.png", ColorScheme.Light); + assertTrue(view.hasContent()); + } + + @Test + void hasContent_WhenContentDark() { + ImageView view = views.createImageView("key"); + view.setContent("https://example.com/image.png", ColorScheme.Dark); + assertTrue(view.hasContent()); + } + } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewContentParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewContentParser.java index 5e8021fcb..b37b55dc8 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewContentParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewContentParser.java @@ -6,15 +6,13 @@ import com.structurizr.importer.diagrams.kroki.KrokiImporter; import com.structurizr.importer.diagrams.mermaid.MermaidImporter; import com.structurizr.importer.diagrams.plantuml.PlantUMLImporter; -import com.structurizr.util.ImageUtils; import com.structurizr.util.StringUtils; import com.structurizr.util.Url; -import com.structurizr.view.ImageView; +import com.structurizr.view.ColorScheme; import com.structurizr.view.ModelView; import com.structurizr.view.View; import java.io.File; -import java.net.URL; final class ImageViewContentParser extends AbstractParser { @@ -24,9 +22,12 @@ final class ImageViewContentParser extends AbstractParser { private static final String IMAGE_GRAMMAR = "image "; private static final int PLANTUML_SOURCE_INDEX = 1; + private static final int MERMAID_SOURCE_INDEX = 1; + private static final int KROKI_FORMAT_INDEX = 1; private static final int KROKI_SOURCE_INDEX = 2; + private static final int IMAGE_SOURCE_INDEX = 1; private boolean restricted = false; @@ -38,199 +39,207 @@ final class ImageViewContentParser extends AbstractParser { void parsePlantUML(ImageViewDslContext context, File dslFile, Tokens tokens) { // plantuml + if (!tokens.includes(PLANTUML_SOURCE_INDEX)) { + throw new RuntimeException("Expected: " + PLANTUML_GRAMMAR); + } + if (tokens.hasMoreThan(PLANTUML_SOURCE_INDEX)) { throw new RuntimeException("Too many tokens, expected: " + PLANTUML_GRAMMAR); } - ImageView view = context.getView(); - if (view != null) { - if (tokens.size() == 2) { - String source = tokens.get(PLANTUML_SOURCE_INDEX); + String source = tokens.get(PLANTUML_SOURCE_INDEX); + ColorScheme colorScheme = context.getColorScheme(); + + try { + if (source.contains("\n")) { + // inline source + new PlantUMLImporter().importDiagram(context.getView(), source, colorScheme); + } else { + View viewWithKey = context.getWorkspace().getViews().getViewWithKey(source); + if (viewWithKey instanceof ModelView) { + String plantumlLight = new StructurizrPlantUMLExporter(ColorScheme.Light).export((ModelView) viewWithKey).getDefinition(); + new PlantUMLImporter().importDiagram(context.getView(), plantumlLight, ColorScheme.Light); + + String plantumlDark = new StructurizrPlantUMLExporter(ColorScheme.Dark).export((ModelView) viewWithKey).getDefinition(); + new PlantUMLImporter().importDiagram(context.getView(), plantumlDark, ColorScheme.Dark); - try { - if (source.contains("\n")) { - // inline source - new PlantUMLImporter().importDiagram(context.getView(), source); + if (!StringUtils.isNullOrEmpty(viewWithKey.getTitle())) { + context.getView().setTitle(viewWithKey.getTitle()); } else { - View viewWithKey = context.getWorkspace().getViews().getViewWithKey(source); - if (viewWithKey instanceof ModelView) { - StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(); - String plantuml = exporter.export((ModelView) viewWithKey).getDefinition(); - new PlantUMLImporter().importDiagram(context.getView(), plantuml); - - if (!StringUtils.isNullOrEmpty(viewWithKey.getTitle())) { - context.getView().setTitle(viewWithKey.getTitle()); + context.getView().setTitle(viewWithKey.getName()); + } + context.getView().setDescription(viewWithKey.getDescription()); + } else { + if (Url.isUrl(source)) { + RemoteContent content = readFromUrl(source); + new PlantUMLImporter().importDiagram(context.getView(), content.getContent(), colorScheme); + context.getView().setTitle(source.substring(source.lastIndexOf("/") + 1)); + } else { + if (!restricted) { + File file = new File(dslFile.getParentFile(), source); + if (file.exists()) { + context.setDslPortable(false); + new PlantUMLImporter().importDiagram(context.getView(), file, colorScheme); } else { - context.getView().setTitle(viewWithKey.getName()); + throw new RuntimeException("The file at " + file.getAbsolutePath() + " does not exist"); } - context.getView().setDescription(viewWithKey.getDescription()); } else { - if (Url.isUrl(source)) { - RemoteContent content = readFromUrl(source); - new PlantUMLImporter().importDiagram(context.getView(), content.getContent()); - context.getView().setTitle(source.substring(source.lastIndexOf("/") + 1)); - } else { - if (!restricted) { - File file = new File(dslFile.getParentFile(), source); - if (file.exists()) { - context.setDslPortable(false); - new PlantUMLImporter().importDiagram(context.getView(), file); - } else { - throw new RuntimeException("The file at " + file.getAbsolutePath() + " does not exist"); - } - } else { - throw new RuntimeException("PlantUML source must be specified as a URL when running in restricted mode"); - } - } + throw new RuntimeException("PlantUML source must be specified as a URL when running in restricted mode"); } } - } catch (Exception e) { - throw new RuntimeException(e.getMessage()); } - } else { - throw new RuntimeException("Expected: " + PLANTUML_GRAMMAR); } + } catch (Exception e) { + throw new RuntimeException(e.getMessage()); } } void parseMermaid(ImageViewDslContext context, File dslFile, Tokens tokens) { // mermaid + if (!tokens.includes(MERMAID_SOURCE_INDEX)) { + throw new RuntimeException("Expected: " + MERMAID_GRAMMAR); + } + if (tokens.hasMoreThan(MERMAID_SOURCE_INDEX)) { throw new RuntimeException("Too many tokens, expected: " + MERMAID_GRAMMAR); } - ImageView view = context.getView(); - if (view != null) { - if (tokens.size() == 2) { - String source = tokens.get(MERMAID_SOURCE_INDEX); + String source = tokens.get(MERMAID_SOURCE_INDEX); + ColorScheme colorScheme = context.getColorScheme(); - try { - if (source.contains("\n")) { - // inline source - new MermaidImporter().importDiagram(context.getView(), source); + try { + if (source.contains("\n")) { + // inline source + new MermaidImporter().importDiagram(context.getView(), source, colorScheme); + } else { + View viewWithKey = context.getWorkspace().getViews().getViewWithKey(source); + if (viewWithKey instanceof ModelView) { + MermaidDiagramExporter exporter = new MermaidDiagramExporter(); + String mermaid = exporter.export((ModelView) viewWithKey).getDefinition(); + new MermaidImporter().importDiagram(context.getView(), mermaid, colorScheme); + + if (!StringUtils.isNullOrEmpty(viewWithKey.getTitle())) { + context.getView().setTitle(viewWithKey.getTitle()); } else { - View viewWithKey = context.getWorkspace().getViews().getViewWithKey(source); - if (viewWithKey instanceof ModelView) { - MermaidDiagramExporter exporter = new MermaidDiagramExporter(); - String mermaid = exporter.export((ModelView) viewWithKey).getDefinition(); - new MermaidImporter().importDiagram(context.getView(), mermaid); - - if (!StringUtils.isNullOrEmpty(viewWithKey.getTitle())) { - context.getView().setTitle(viewWithKey.getTitle()); + context.getView().setTitle(viewWithKey.getName()); + } + context.getView().setDescription(viewWithKey.getDescription()); + } else { + if (Url.isUrl(source)) { + RemoteContent content = readFromUrl(source); + new MermaidImporter().importDiagram(context.getView(), content.getContent(), colorScheme); + context.getView().setTitle(source.substring(source.lastIndexOf("/") + 1)); + } else { + if (!restricted) { + File file = new File(dslFile.getParentFile(), source); + if (file.exists()) { + context.setDslPortable(false); + new MermaidImporter().importDiagram(context.getView(), file, colorScheme); } else { - context.getView().setTitle(viewWithKey.getName()); + throw new RuntimeException("The file at " + file.getAbsolutePath() + " does not exist"); } - context.getView().setDescription(viewWithKey.getDescription()); } else { - if (Url.isUrl(source)) { - RemoteContent content = readFromUrl(source); - new MermaidImporter().importDiagram(context.getView(), content.getContent()); - context.getView().setTitle(source.substring(source.lastIndexOf("/") + 1)); - } else { - if (!restricted) { - File file = new File(dslFile.getParentFile(), source); - if (file.exists()) { - context.setDslPortable(false); - new MermaidImporter().importDiagram(context.getView(), file); - } else { - throw new RuntimeException("The file at " + file.getAbsolutePath() + " does not exist"); - } - } else { - throw new RuntimeException("Mermaid source must be specified as a URL when running in restricted mode"); - } - } + throw new RuntimeException("Mermaid source must be specified as a URL when running in restricted mode"); } } - } catch (Exception e) { - throw new RuntimeException(e.getMessage()); } - } else { - throw new RuntimeException("Expected: " + MERMAID_GRAMMAR); } + } catch (Exception e) { + throw new RuntimeException(e.getMessage()); } } void parseKroki(ImageViewDslContext context, File dslFile, Tokens tokens) { // kroki + if (!tokens.includes(KROKI_SOURCE_INDEX)) { + throw new RuntimeException("Expected: " + KROKI_GRAMMAR); + } + if (tokens.hasMoreThan(KROKI_SOURCE_INDEX)) { throw new RuntimeException("Too many tokens, expected: " + KROKI_GRAMMAR); } - ImageView view = context.getView(); - if (view != null) { - if (tokens.size() == 3) { - String format = tokens.get(KROKI_FORMAT_INDEX); - String source = tokens.get(KROKI_SOURCE_INDEX); + String format = tokens.get(KROKI_FORMAT_INDEX); + String source = tokens.get(KROKI_SOURCE_INDEX); + ColorScheme colorScheme = context.getColorScheme(); - try { - if (source.contains("\n")) { - // inline source - new KrokiImporter().importDiagram(context.getView(), format, source); - } else { - if (Url.isUrl(source)) { - RemoteContent content = readFromUrl(source); - new KrokiImporter().importDiagram(context.getView(), format, content.getContent()); - context.getView().setTitle(source.substring(source.lastIndexOf("/") + 1)); + try { + if (source.contains("\n")) { + // inline source + new KrokiImporter().importDiagram(context.getView(), format, source, colorScheme); + } else { + if (Url.isUrl(source)) { + RemoteContent content = readFromUrl(source); + new KrokiImporter().importDiagram(context.getView(), format, content.getContent(), colorScheme); + context.getView().setTitle(source.substring(source.lastIndexOf("/") + 1)); + } else { + if (!restricted) { + File file = new File(dslFile.getParentFile(), source); + if (file.exists()) { + context.setDslPortable(false); + new KrokiImporter().importDiagram(context.getView(), format, file, colorScheme); } else { - if (!restricted) { - File file = new File(dslFile.getParentFile(), source); - if (file.exists()) { - context.setDslPortable(false); - new KrokiImporter().importDiagram(context.getView(), format, file); - } else { - throw new RuntimeException("The file at " + file.getAbsolutePath() + " does not exist"); - } - } else { - throw new RuntimeException("Kroki source must be specified as a URL when running in restricted mode"); - } + throw new RuntimeException("The file at " + file.getAbsolutePath() + " does not exist"); } + } else { + throw new RuntimeException("Kroki source must be specified as a URL when running in restricted mode"); } - } catch (Exception e) { - throw new RuntimeException(e.getMessage()); } - } else { - throw new RuntimeException("Expected: " + KROKI_GRAMMAR); } + } catch (Exception e) { + throw new RuntimeException(e.getMessage()); } } void parseImage(ImageViewDslContext context, File dslFile, Tokens tokens) { // image + if (!tokens.includes(IMAGE_SOURCE_INDEX)) { + throw new RuntimeException("Expected: " + IMAGE_GRAMMAR); + } + if (tokens.hasMoreThan(IMAGE_SOURCE_INDEX)) { throw new RuntimeException("Too many tokens, expected: " + IMAGE_GRAMMAR); } - ImageView view = context.getView(); - if (view != null) { - if (tokens.size() == 2) { - String source = tokens.get(IMAGE_SOURCE_INDEX); + String source = tokens.get(IMAGE_SOURCE_INDEX); + ColorScheme colorScheme = context.getColorScheme(); - try { - if (Url.isUrl(source)) { - new ImageImporter().importDiagram(context.getView(), source); + try { + if (Url.isUrl(source)) { + new ImageImporter().importDiagram(context.getView(), source, colorScheme); + } else { + if (!restricted) { + File file = new File(dslFile.getParentFile(), source); + if (file.exists()) { + context.setDslPortable(false); + new ImageImporter().importDiagram(context.getView(), file, colorScheme); } else { - if (!restricted) { - File file = new File(dslFile.getParentFile(), source); - if (file.exists()) { - context.setDslPortable(false); - new ImageImporter().importDiagram(context.getView(), file); - } else { - throw new RuntimeException("The file at " + file.getAbsolutePath() + " does not exist"); - } - } else { - throw new RuntimeException("Images must be specified as a URL when running in restricted mode"); - } + throw new RuntimeException("The file at " + file.getAbsolutePath() + " does not exist"); } - } catch (Exception e) { - throw new RuntimeException(e.getMessage()); + } else { + throw new RuntimeException("Images must be specified as a URL when running in restricted mode"); } + } + } catch (Exception e) { + throw new RuntimeException(e.getMessage()); + } + } + + private ColorScheme calculateColorScheme(Tokens tokens, int index) { + if (tokens.includes(index)) { + if (ColorScheme.Dark.toString().equalsIgnoreCase(tokens.get(index))) { + return ColorScheme.Dark; + } else if (ColorScheme.Light.toString().equalsIgnoreCase(tokens.get(index))) { + return ColorScheme.Light; } else { - throw new RuntimeException("Expected: " + IMAGE_GRAMMAR); + throw new RuntimeException("Invalid color scheme \"" + tokens.get(index) + "\" - expected: light or dark"); } } + + return null; } } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewDslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewDslContext.java index 1a7a996a5..b33c4cbe7 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewDslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewDslContext.java @@ -2,12 +2,15 @@ import com.structurizr.util.ImageUtils; import com.structurizr.util.StringUtils; +import com.structurizr.view.ColorScheme; import com.structurizr.view.ImageView; import java.io.IOException; class ImageViewDslContext extends ViewDslContext { + private ColorScheme colorScheme; + ImageViewDslContext(ImageView view) { super(view); } @@ -16,6 +19,14 @@ ImageView getView() { return (ImageView)super.getView(); } + ColorScheme getColorScheme() { + return colorScheme; + } + + void setColorScheme(ColorScheme colorScheme) { + this.colorScheme = colorScheme; + } + @Override protected String[] getPermittedTokens() { return new String[] { @@ -32,19 +43,12 @@ protected String[] getPermittedTokens() { void end() { super.end(); - // try to set the content type if it hasn't been set ... this helps the diagram render with image sizing/scaling - ImageView imageView = getView(); - if (StringUtils.isNullOrEmpty(imageView.getContentType())) { - if (ImageUtils.isSupportedDataUri(imageView.getContent())) { - imageView.setContentType(ImageUtils.getContentTypeFromDataUri(imageView.getContent())); - } else { - try { - imageView.setContentType(ImageUtils.getContentType(imageView.getContent())); - } catch (IOException e) { - e.printStackTrace(); - // ignore - } - } + if (colorScheme != null) { + colorScheme = null; + } + + if (!getView().hasContent()) { + throw new RuntimeException("The image view \"" + getView().getKey() + "\" has no content"); } } } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/NameValuePair.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/NameValuePair.java index 225e77144..d415d50e5 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/NameValuePair.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/NameValuePair.java @@ -32,6 +32,7 @@ String getValue() { enum NameValueType { Constant, - Variable + Variable, + TextBlock } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index 5561e2e17..61e9503df 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -1099,6 +1099,16 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn } else if (IMAGE_VIEW_TOKEN.equalsIgnoreCase(firstToken) && inContext(ImageViewDslContext.class)) { new ImageViewContentParser(restricted).parseImage(getContext(ImageViewDslContext.class), dslFile, tokens); + } else if (LIGHT_COLOR_SCHEME_TOKEN.equalsIgnoreCase(firstToken) && inContext(ImageViewDslContext.class) && shouldStartContext(tokens)) { + ImageViewDslContext context = getContext(ImageViewDslContext.class); + context.setColorScheme(ColorScheme.Light); + startContext(context); + + } else if (DARK_COLOR_SCHEME_TOKEN.equalsIgnoreCase(firstToken) && inContext(ImageViewDslContext.class) && shouldStartContext(tokens)) { + ImageViewDslContext context = getContext(ImageViewDslContext.class); + context.setColorScheme(ColorScheme.Dark); + startContext(context); + } else if (inContext(DynamicViewDslContext.class)) { RelationshipView relationshipView = new DynamicViewContentParser().parseRelationship(getContext(DynamicViewDslContext.class), tokens); @@ -1230,11 +1240,7 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn } else if (VAR_TOKEN.equalsIgnoreCase(firstToken)) { NameValuePair nameValuePair = new NameValueParser().parseVariable(tokens); - - if (constantsAndVariables.containsKey(nameValuePair.getName()) && constantsAndVariables.get(nameValuePair.getName()).getType() == NameValueType.Constant) { - throw new StructurizrDslParserException("A constant \"" + nameValuePair.getName() + "\" already exists"); - } - constantsAndVariables.put(nameValuePair.getName(), nameValuePair); + addVariable(nameValuePair); } else if (IDENTIFIERS_TOKEN.equalsIgnoreCase(firstToken) && (inContext(WorkspaceDslContext.class) || inContext(ModelDslContext.class))) { setIdentifierScope(new IdentifierScopeParser().parse(getContext(), tokens)); @@ -1337,10 +1343,10 @@ private List preProcessLines(List lines) { if (source.endsWith(TEXT_BLOCK_MARKER)) { String[] parts = source.split(TEXT_BLOCK_MARKER); - String constantName = UUID.randomUUID().toString(); - String constantValue = parts[1].substring(0, parts[1].length() - 1); // remove final line break - addConstant(constantName, constantValue); - dslLines.add(new DslLine(parts[0] + "\"" + String.format(STRING_SUBSTITUTION_TEMPLATE, constantName) + "\"", lineNumber)); + String textBlockName = UUID.randomUUID().toString(); + String textBlockValue = parts[1].substring(0, parts[1].length() - 1); // remove final line break + addTextBlock(textBlockName, textBlockValue); + dslLines.add(new DslLine(parts[0] + "\"" + String.format(STRING_SUBSTITUTION_TEMPLATE, textBlockName) + "\"", lineNumber)); } else { dslLines.add(new DslLine(source, lineNumber)); } @@ -1365,7 +1371,13 @@ private String substituteStrings(String token) { String after = null; String name = before.substring(2, before.length()-1); if (constantsAndVariables.containsKey(name)) { - after = constantsAndVariables.get(name).getValue(); + NameValuePair nameValuePair = constantsAndVariables.get(name); + + if (nameValuePair.getType() == NameValueType.TextBlock) { + after = substituteStrings(nameValuePair.getValue()); + } else { + after = nameValuePair.getValue(); + } } else { if (!restricted) { String environmentVariable = System.getenv().get(name); @@ -1540,6 +1552,23 @@ private void addConstant(NameValuePair nameValuePair) { constantsAndVariables.put(nameValuePair.getName(), nameValuePair); } + private void addVariable(NameValuePair nameValuePair) { + if (constantsAndVariables.containsKey(nameValuePair.getName()) && constantsAndVariables.get(nameValuePair.getName()).getType() == NameValueType.Constant) { + throw new IllegalArgumentException("A constant \"" + nameValuePair.getName() + "\" already exists"); + } + constantsAndVariables.put(nameValuePair.getName(), nameValuePair); + } + + private void addTextBlock(String name, String value) { + if (StringUtils.isNullOrEmpty(name)) { + throw new IllegalArgumentException("A text block name must be specified"); + } + + NameValuePair nameValuePair = new NameValuePair(name, value); + nameValuePair.setType(NameValueType.TextBlock); + addConstant(nameValuePair); + } + private boolean inContext(Class clazz) { if (contextStack.empty()) { return false; diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index 022b748d2..f45817bd5 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -1698,22 +1698,39 @@ void test_textBlock() throws Exception { parser.parse(new File("src/test/resources/dsl/text-block.dsl")); workspace = parser.getWorkspace(); - SoftwareSystem softwareSystem = workspace.getModel().getSoftwareSystemWithName("Name"); - assertEquals(""" - - Line 1 - - Line 2 - - Line 3""", softwareSystem.getDescription()); + + ImageView view = (ImageView)workspace.getViews().getViewWithKey("image"); + assertEquals("https://plantuml.com/plantuml/svg/SoWkIImgAStDuUAoAIwfp4cruohApozHgEPI00AdnEJizAByqhmKv_oS_28h1UKqCB3cgkMoqOUgvqhEIImkLl2jT0RHN0wfUIb0ym00", view.getContent()); assertEquals(""" workspace { - model { - softwareSystem = softwareSystem "Name" { - description ""\" - - Line 1 - - Line 2 - - Line 3 + views { + properties { + "plantuml.url" "https://plantuml.com/plantuml" + } + + !const SOURCE ""\" + class MyClass ""\" + + !var STYLES ""\" + + ""\" + + image * "image" { + plantuml ""\" + @startuml + + ${STYLES} + + ${SOURCE} + @enduml + ""\" } } diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewContentParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewContentParserTests.java index 6de21a3b0..28b826974 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewContentParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewContentParserTests.java @@ -1,13 +1,14 @@ package com.structurizr.dsl; +import com.structurizr.importer.diagrams.kroki.KrokiImporter; import com.structurizr.importer.diagrams.mermaid.MermaidImporter; import com.structurizr.importer.diagrams.plantuml.PlantUMLImporter; +import com.structurizr.view.ColorScheme; import com.structurizr.view.ImageView; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.*; class ImageViewContentParserTests extends AbstractTests { @@ -45,6 +46,49 @@ void test_parsePlantUML_ThrowsAnException_WhenUsingAFileNameInRestrictedMode() { } } + @Test + void test_parsePlantUML_Source() { + String source = """ + @startuml + Bob -> Alice : hello + @enduml"""; + + workspace.getViews().getConfiguration().addProperty(PlantUMLImporter.PLANTUML_URL_PROPERTY, "https://plantuml.com/plantuml"); + parser = new ImageViewContentParser(true); + parser.parsePlantUML(new ImageViewDslContext(imageView), null, tokens("plantuml", source)); + assertEquals("https://plantuml.com/plantuml/svg/SoWkIImgAStDuNBAJrBGjLDmpCbCJbMmKiX8pSd9vt98pKi1IW80", imageView.getContent()); + } + + @Test + void test_parsePlantUML_Source_Light() { + String source = """ + @startuml + Bob -> Alice : hello + @enduml"""; + + workspace.getViews().getConfiguration().addProperty(PlantUMLImporter.PLANTUML_URL_PROPERTY, "https://plantuml.com/plantuml"); + parser = new ImageViewContentParser(true); + ImageViewDslContext context = new ImageViewDslContext(imageView); + context.setColorScheme(ColorScheme.Light); + parser.parsePlantUML(context, null, tokens("plantuml", source)); + assertEquals("https://plantuml.com/plantuml/svg/SoWkIImgAStDuNBAJrBGjLDmpCbCJbMmKiX8pSd9vt98pKi1IW80", imageView.getContentLight()); + } + + @Test + void test_parsePlantUML_Source_Dark() { + String source = """ + @startuml + Bob -> Alice : hello + @enduml"""; + + workspace.getViews().getConfiguration().addProperty(PlantUMLImporter.PLANTUML_URL_PROPERTY, "https://plantuml.com/plantuml"); + parser = new ImageViewContentParser(true); + ImageViewDslContext context = new ImageViewDslContext(imageView); + context.setColorScheme(ColorScheme.Dark); + parser.parsePlantUML(context, null, tokens("plantuml", source)); + assertEquals("https://plantuml.com/plantuml/svg/SoWkIImgAStDuNBAJrBGjLDmpCbCJbMmKiX8pSd9vt98pKi1IW80", imageView.getContentDark()); + } + @Test void test_parsePlantUML_WithViewKey() { ImageViewDslContext context = new ImageViewDslContext(imageView); @@ -57,7 +101,9 @@ void test_parsePlantUML_WithViewKey() { parser.parsePlantUML(context, null, tokens("plantuml", "SystemLandscape")); assertEquals("System Landscape View", imageView.getTitle()); assertEquals("Description", imageView.getDescription()); - assertEquals("https://plantuml.com/plantuml/svg/ZLBBJiCm4BpxArPmXfQgS0X9rF0IXt8Xg3q01pVP9bOTRsHlr0VYtt6Qj1n0Y9LihMTjpth64yVISbDfmOerGkZK3eFHE4wtZh62gJIvosIDC5Eu3WTjENupnsrtw3AhQbPa-g8G3XaSrj9A9Wk630gc6fXWGSnKGQuiPkqHKQeSmHDP9DxMA4JeUAlz9G2MYE739m0tCbiLbXgJtv8c6y3fSX_N--e36JxWutsq-ASVWm7SQwpGi5-Sz-dPytoZ5_DPaoTHz2-2gJBuaw33qxRT08RVo4kfifL1vm8O_TLWXwUjZZ3gaKUoQkTHgHEj2jEs6q3cPxJTXhIKEQsLAOwKJtAZggQQgvpB0CQNm-xntenEID5ABKtXlJs9ekHWtSLL_9hIajVI8dHUl_S6da0O_gPL78Dqa0WnGPFx7_C5", imageView.getContent()); + assertNull(imageView.getContent()); + assertEquals("https://plantuml.com/plantuml/svg/ZLBBJiCm4BpxArPmXfQgS0X9rF0IXt8Xg3q01pVP9bOTRsHlr0VYtt6Qj1n0Y9LihMTjpth64yVISbDfmOerGkZK3eFHE4wtZh62gJIvosIDC5Eu3WTjENupnsrtw3AhQbPa-g8G3XaSrj9A9Wk630gc6fXWGSnKGQuiPkqHKQeSmHDP9DxMA4JeUAlz9G2MYE739m0tCbiLbXgJtv8c6y3fSX_N--e36JxWutsq-ASVWm7SQwpGi5-Sz-dPytoZ5_DPaoTHz2-2gJBuaw33qxRT08RVo4kfifL1vm8O_TLWXwUjZZ3gaKUoQkTHgHEj2jEs6q3cPxJTXhIKEQsLAOwKJtAZggQQgvpB0CQNm-xntenEID5ABKtXlJs9ekHWtSLL_9hIajVI8dHUl_S6da0O_gPL78Dqa0WnGPFx7_C5", imageView.getContentLight()); + assertEquals("https://plantuml.com/plantuml/svg/ZLBBJiCm4BpxArRb37seS0X9rF0IXt8Xg3q01pTU4gkEDxAtwWFnxpXDAGSGOYLRwrdRivxnnBDqlAgDOCq68VPwXz5edEPRprZ3L5hb2zaWp3IkutvRJb_iSTiD-iBfXZNPGr48ZmmU6-aaamDB5WLJ0qom86QgGMc7HNj4L5eX12A7nDi6XOWzRqsu1C0HCRo71E1A5ilIqSggQpBa8ZWPxkDoNxqZorzuiOyM_mYZtuTRWpLQ3ekpGthwED-OnNosKbcI_8jWgYt-9EZml6qtWi4tybJfOcdH-mX6VpNOuNch8up67N9FJky2AarcT6dRTYCemeoksv1NKj5Qs_98-I0tkbxLSwsuYc1yFkWU7ypeX1IjrDAMmTjUacHVrWqlqkUStdWj7KBdzUl1m1x4yMzQfIb83vaG4xGg_9XF", imageView.getContentDark()); } @Test @@ -86,6 +132,58 @@ void test_parseMermaid_ThrowsAnException_WhenUsingAFileNameInRestrictedMode() { } } + @Test + void test_parseMermaid_Source() { + String source = """ + flowchart TD + A[Christmas] -->|Get money| B(Go shopping) + B --> C{Let me think} + C -->|One| D[Laptop] + C -->|Two| E[iPhone] + C -->|Three| F[fa:fa-car Car]"""; + + workspace.getViews().getConfiguration().addProperty(MermaidImporter.MERMAID_URL_PROPERTY, "https://mermaid.ink"); + parser = new ImageViewContentParser(true); + parser.parseMermaid(new ImageViewDslContext(imageView), null, tokens("mermaid", source)); + assertEquals("https://mermaid.ink/svg/pako:eJxVj70OgjAUhV_lppMm8gIMJlKUhUQHtspwAxfbSH9SaoihvLsgi571-85JzgSssS2xlHW9HRuJPkCV3w0sOQkuvRqCxqGGJDnGggJoa-gdIdsVFgZpnVPmsd_8bJWAT-WqEQSpzHPeEP_2r4Yi5KJEF6yrf0k12ghnoW5ymf8n0tPSuogO0w6TBj1w9DU7ANPkNaqWpRMLkvR6oqUOX31g8_wBLY9E1w==", imageView.getContent()); + } + + @Test + void test_parseMermaid_Source_Light() { + String source = """ + flowchart TD + A[Christmas] -->|Get money| B(Go shopping) + B --> C{Let me think} + C -->|One| D[Laptop] + C -->|Two| E[iPhone] + C -->|Three| F[fa:fa-car Car]"""; + + workspace.getViews().getConfiguration().addProperty(MermaidImporter.MERMAID_URL_PROPERTY, "https://mermaid.ink"); + parser = new ImageViewContentParser(true); + ImageViewDslContext context = new ImageViewDslContext(imageView); + context.setColorScheme(ColorScheme.Light); + parser.parseMermaid(context, null, tokens("mermaid", source)); + assertEquals("https://mermaid.ink/svg/pako:eJxVj70OgjAUhV_lppMm8gIMJlKUhUQHtspwAxfbSH9SaoihvLsgi571-85JzgSssS2xlHW9HRuJPkCV3w0sOQkuvRqCxqGGJDnGggJoa-gdIdsVFgZpnVPmsd_8bJWAT-WqEQSpzHPeEP_2r4Yi5KJEF6yrf0k12ghnoW5ymf8n0tPSuogO0w6TBj1w9DU7ANPkNaqWpRMLkvR6oqUOX31g8_wBLY9E1w==", imageView.getContentLight()); + } + + @Test + void test_parseMermaid_Source_Dark() { + String source = """ + flowchart TD + A[Christmas] -->|Get money| B(Go shopping) + B --> C{Let me think} + C -->|One| D[Laptop] + C -->|Two| E[iPhone] + C -->|Three| F[fa:fa-car Car]"""; + + workspace.getViews().getConfiguration().addProperty(MermaidImporter.MERMAID_URL_PROPERTY, "https://mermaid.ink"); + parser = new ImageViewContentParser(true); + ImageViewDslContext context = new ImageViewDslContext(imageView); + context.setColorScheme(ColorScheme.Dark); + parser.parseMermaid(context, null, tokens("mermaid", source)); + assertEquals("https://mermaid.ink/svg/pako:eJxVj70OgjAUhV_lppMm8gIMJlKUhUQHtspwAxfbSH9SaoihvLsgi571-85JzgSssS2xlHW9HRuJPkCV3w0sOQkuvRqCxqGGJDnGggJoa-gdIdsVFgZpnVPmsd_8bJWAT-WqEQSpzHPeEP_2r4Yi5KJEF6yrf0k12ghnoW5ymf8n0tPSuogO0w6TBj1w9DU7ANPkNaqWpRMLkvR6oqUOX31g8_wBLY9E1w==", imageView.getContentDark()); + } + @Test void test_parseMermaid_WithViewKey() { ImageViewDslContext context = new ImageViewDslContext(imageView); @@ -125,6 +223,58 @@ void test_parseKroki_ThrowsAnException_WhenUsingAFileNameInRestrictedMode() { } } + @Test + void test_parseKroki_Source() { + String source = """ + flowchart TD + A[Christmas] -->|Get money| B(Go shopping) + B --> C{Let me think} + C -->|One| D[Laptop] + C -->|Two| E[iPhone] + C -->|Three| F[fa:fa-car Car]"""; + + workspace.getViews().getConfiguration().addProperty(KrokiImporter.KROKI_URL_PROPERTY, "https://kroki.io"); + parser = new ImageViewContentParser(true); + parser.parseKroki(new ImageViewDslContext(imageView), null, tokens("kroki", "mermaid", source)); + assertEquals("https://kroki.io/mermaid/png/eNpVjLEOwiAURXe_4o068AMOJpZqlyZ16EYYXhrwES2PAEljxH-XdtK7nnOuffIyEcYMY7uDurOSFF3KMyYNQpxKZzLM7M2rQLPvGBJxCM7fD5verA7Id79aBjI5__hsRG714E2BVvUYMgf9A8aFC1yUu1H9_gMUTW2uyuLRopgwgsSovzbHM0c=", imageView.getContent()); + } + + @Test + void test_parseKroki_Source_Light() { + String source = """ + flowchart TD + A[Christmas] -->|Get money| B(Go shopping) + B --> C{Let me think} + C -->|One| D[Laptop] + C -->|Two| E[iPhone] + C -->|Three| F[fa:fa-car Car]"""; + + workspace.getViews().getConfiguration().addProperty(KrokiImporter.KROKI_URL_PROPERTY, "https://kroki.io"); + parser = new ImageViewContentParser(true); + ImageViewDslContext context = new ImageViewDslContext(imageView); + context.setColorScheme(ColorScheme.Light); + parser.parseKroki(context, null, tokens("kroki", "mermaid", source)); + assertEquals("https://kroki.io/mermaid/png/eNpVjLEOwiAURXe_4o068AMOJpZqlyZ16EYYXhrwES2PAEljxH-XdtK7nnOuffIyEcYMY7uDurOSFF3KMyYNQpxKZzLM7M2rQLPvGBJxCM7fD5verA7Id79aBjI5__hsRG714E2BVvUYMgf9A8aFC1yUu1H9_gMUTW2uyuLRopgwgsSovzbHM0c=", imageView.getContentLight()); + } + + @Test + void test_parseKroki_Source_Dark() { + String source = """ + flowchart TD + A[Christmas] -->|Get money| B(Go shopping) + B --> C{Let me think} + C -->|One| D[Laptop] + C -->|Two| E[iPhone] + C -->|Three| F[fa:fa-car Car]"""; + + workspace.getViews().getConfiguration().addProperty(KrokiImporter.KROKI_URL_PROPERTY, "https://kroki.io"); + parser = new ImageViewContentParser(true); + ImageViewDslContext context = new ImageViewDslContext(imageView); + context.setColorScheme(ColorScheme.Dark); + parser.parseKroki(context, null, tokens("kroki", "mermaid", source)); + assertEquals("https://kroki.io/mermaid/png/eNpVjLEOwiAURXe_4o068AMOJpZqlyZ16EYYXhrwES2PAEljxH-XdtK7nnOuffIyEcYMY7uDurOSFF3KMyYNQpxKZzLM7M2rQLPvGBJxCM7fD5verA7Id79aBjI5__hsRG714E2BVvUYMgf9A8aFC1yUu1H9_gMUTW2uyuLRopgwgsSovzbHM0c=", imageView.getContentDark()); + } + @Test void test_parseImage_ThrowsAnException_WhenUsingAFileNameInRestrictedMode() { try { @@ -136,4 +286,29 @@ void test_parseImage_ThrowsAnException_WhenUsingAFileNameInRestrictedMode() { } } + @Test + void test_parseImage() { + parser = new ImageViewContentParser(true); + parser.parseImage(new ImageViewDslContext(imageView), null, tokens("image", "https://example.com/image.png")); + assertEquals("https://example.com/image.png", imageView.getContent()); + } + + @Test + void test_parseImage_Url_Light() { + parser = new ImageViewContentParser(true); + ImageViewDslContext context = new ImageViewDslContext(imageView); + context.setColorScheme(ColorScheme.Light); + parser.parseImage(context, null, tokens("image", "https://example.com/image.png")); + assertEquals("https://example.com/image.png", imageView.getContentLight()); + } + + @Test + void test_parseImage_Url_Dark() { + parser = new ImageViewContentParser(true); + ImageViewDslContext context = new ImageViewDslContext(imageView); + context.setColorScheme(ColorScheme.Dark); + parser.parseImage(context, null, tokens("image", "https://example.com/image.png")); + assertEquals("https://example.com/image.png", imageView.getContentDark()); + } + } \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewDslContextTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewDslContextTests.java new file mode 100644 index 000000000..b0e450edb --- /dev/null +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewDslContextTests.java @@ -0,0 +1,31 @@ +package com.structurizr.dsl; + +import com.structurizr.view.ImageView; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +class ImageViewDslContextTests extends AbstractTests { + + @Test + void end_ThrowsAnException_WhenThereIsNoContent() { + try { + ImageView imageView = workspace.getViews().createImageView("key"); + ImageViewDslContext context = new ImageViewDslContext(imageView); + context.end(); + fail(); + } catch (Exception e) { + assertEquals("The image view \"key\" has no content", e.getMessage()); + } + } + + @Test + void end_WhenThereIsContent() { + ImageView imageView = workspace.getViews().createImageView("key"); + imageView.setContent("http://example.com/image.png"); + ImageViewDslContext context = new ImageViewDslContext(imageView); + context.end(); + } + +} \ No newline at end of file diff --git a/structurizr-dsl/src/test/resources/dsl/test.dsl b/structurizr-dsl/src/test/resources/dsl/test.dsl index cf0ef4407..2073a6fc1 100644 --- a/structurizr-dsl/src/test/resources/dsl/test.dsl +++ b/structurizr-dsl/src/test/resources/dsl/test.dsl @@ -328,6 +328,16 @@ workspace "Name" "Description" { default } + image * { + light { + image logo.png + } + dark { + image logo.png + } + image logo.png + } + styles { element "Element" { shape roundedbox diff --git a/structurizr-dsl/src/test/resources/dsl/text-block.dsl b/structurizr-dsl/src/test/resources/dsl/text-block.dsl index a96042760..1b442a781 100644 --- a/structurizr-dsl/src/test/resources/dsl/text-block.dsl +++ b/structurizr-dsl/src/test/resources/dsl/text-block.dsl @@ -1,12 +1,31 @@ workspace { - model { - softwareSystem = softwareSystem "Name" { - description """ - - Line 1 - - Line 2 - - Line 3 + views { + properties { + "plantuml.url" "https://plantuml.com/plantuml" + } + + !const SOURCE """ + class MyClass """ + + !var STYLES """ + + """ + + image * "image" { + plantuml """ + @startuml + + ${STYLES} + + ${SOURCE} + @enduml + """ } } diff --git a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/image/ImageImporter.java b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/image/ImageImporter.java index 6708f0f06..ea1584d6c 100644 --- a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/image/ImageImporter.java +++ b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/image/ImageImporter.java @@ -2,6 +2,7 @@ import com.structurizr.importer.diagrams.AbstractDiagramImporter; import com.structurizr.util.ImageUtils; +import com.structurizr.view.ColorScheme; import com.structurizr.view.ImageView; import java.io.File; @@ -14,12 +15,20 @@ public class ImageImporter extends AbstractDiagramImporter { public static final String IMAGE_INLINE_PROPERTY = "image.inline"; public void importDiagram(ImageView view, File file) throws Exception { + importDiagram(view, file, null); + } + + public void importDiagram(ImageView view, File file, ColorScheme colorScheme) throws Exception { view.setContent(ImageUtils.getImageAsDataUri(file)); view.setContentType(ImageUtils.getContentType(file)); view.setTitle(file.getName()); } public void importDiagram(ImageView view, String url) throws Exception { + importDiagram(view, url, null); + } + + public void importDiagram(ImageView view, String url, ColorScheme colorScheme) throws Exception { String inline = getViewOrViewSetProperty(view, IMAGE_INLINE_PROPERTY); if ("true".equals(inline)) { String imageFormat = ImageUtils.getContentType(url); @@ -28,14 +37,15 @@ public void importDiagram(ImageView view, String url) throws Exception { } if (imageFormat.equals(CONTENT_TYPE_IMAGE_SVG)) { - view.setContent(ImageUtils.getSvgAsDataUri(new URL(url), false)); + view.setContent(ImageUtils.getSvgAsDataUri(new URL(url), false), colorScheme); } else { - view.setContent(ImageUtils.getPngAsDataUri(new URL(url), false)); + view.setContent(ImageUtils.getPngAsDataUri(new URL(url), false), colorScheme); } view.setContentType(imageFormat); } else { - view.setContent(url); + view.setContent(url, colorScheme); + view.setContentType(ImageUtils.getContentType(url)); } view.setTitle(url.substring(url.lastIndexOf("/")+1)); diff --git a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/kroki/KrokiImporter.java b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/kroki/KrokiImporter.java index 67b3d4413..d47ff0c30 100644 --- a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/kroki/KrokiImporter.java +++ b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/kroki/KrokiImporter.java @@ -3,6 +3,7 @@ import com.structurizr.importer.diagrams.AbstractDiagramImporter; import com.structurizr.util.ImageUtils; import com.structurizr.util.StringUtils; +import com.structurizr.view.ColorScheme; import com.structurizr.view.ImageView; import java.io.File; @@ -17,13 +18,21 @@ public class KrokiImporter extends AbstractDiagramImporter { public static final String KROKI_INLINE_PROPERTY = "kroki.inline"; public void importDiagram(ImageView view, String format, File file) throws Exception { + importDiagram(view, format, file, null); + } + + public void importDiagram(ImageView view, String format, File file, ColorScheme colorScheme) throws Exception { String content = new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8); view.setTitle(file.getName()); - importDiagram(view, format, content); + importDiagram(view, format, content, colorScheme); } public void importDiagram(ImageView view, String format, String content) throws Exception { + importDiagram(view, format, content, null); + } + + public void importDiagram(ImageView view, String format, String content, ColorScheme colorScheme) throws Exception { String krokiServer = getViewOrViewSetProperty(view, KROKI_URL_PROPERTY); if (StringUtils.isNullOrEmpty(krokiServer)) { throw new IllegalArgumentException("Please define a view/viewset property named " + KROKI_URL_PROPERTY + " to specify your Kroki server"); @@ -44,12 +53,12 @@ public void importDiagram(ImageView view, String format, String content) throws String inline = getViewOrViewSetProperty(view, KROKI_INLINE_PROPERTY); if ("true".equals(inline)) { if (imageFormat.equals(SVG_FORMAT)) { - view.setContent(ImageUtils.getSvgAsDataUri(new URL(url), true)); + view.setContent(ImageUtils.getSvgAsDataUri(new URL(url), true), colorScheme); } else { - view.setContent(ImageUtils.getPngAsDataUri(new URL(url), true)); + view.setContent(ImageUtils.getPngAsDataUri(new URL(url), true), colorScheme); } } else { - view.setContent(url); + view.setContent(url, colorScheme); } view.setContentType(CONTENT_TYPES_BY_FORMAT.get(imageFormat)); } diff --git a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/mermaid/MermaidImporter.java b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/mermaid/MermaidImporter.java index ab1f1a00c..49711aef9 100644 --- a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/mermaid/MermaidImporter.java +++ b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/mermaid/MermaidImporter.java @@ -3,6 +3,7 @@ import com.structurizr.importer.diagrams.AbstractDiagramImporter; import com.structurizr.util.ImageUtils; import com.structurizr.util.StringUtils; +import com.structurizr.view.ColorScheme; import com.structurizr.view.ImageView; import java.io.File; @@ -18,13 +19,21 @@ public class MermaidImporter extends AbstractDiagramImporter { public static final String MERMAID_INLINE_PROPERTY = "mermaid.inline"; public void importDiagram(ImageView view, File file) throws Exception { + importDiagram(view, file, null); + } + + public void importDiagram(ImageView view, File file, ColorScheme colorScheme) throws Exception { String content = new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8); view.setTitle(file.getName()); - importDiagram(view, content); + importDiagram(view, content, colorScheme); } public void importDiagram(ImageView view, String content) throws Exception { + importDiagram(view, content, null); + } + + public void importDiagram(ImageView view, String content, ColorScheme colorScheme) throws Exception { String mermaidServer = getViewOrViewSetProperty(view, MERMAID_URL_PROPERTY); if (StringUtils.isNullOrEmpty(mermaidServer)) { throw new IllegalArgumentException("Please define a view/viewset property named " + MERMAID_URL_PROPERTY + " to specify your Mermaid server"); @@ -55,12 +64,12 @@ public void importDiagram(ImageView view, String content) throws Exception { String inline = getViewOrViewSetProperty(view, MERMAID_INLINE_PROPERTY); if ("true".equals(inline)) { if (format.equals(SVG_FORMAT)) { - view.setContent(ImageUtils.getSvgAsDataUri(new URL(url), true)); + view.setContent(ImageUtils.getSvgAsDataUri(new URL(url), true), colorScheme); } else { - view.setContent(ImageUtils.getPngAsDataUri(new URL(url), true)); + view.setContent(ImageUtils.getPngAsDataUri(new URL(url), true), colorScheme); } } else { - view.setContent(url); + view.setContent(url, colorScheme); } view.setContentType(CONTENT_TYPES_BY_FORMAT.get(format)); } diff --git a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporter.java b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporter.java index 28ecf1c3e..33605447a 100644 --- a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporter.java +++ b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporter.java @@ -4,6 +4,7 @@ import com.structurizr.util.ImageUtils; import com.structurizr.util.StringUtils; import com.structurizr.util.Url; +import com.structurizr.view.ColorScheme; import com.structurizr.view.ImageView; import java.io.File; @@ -20,13 +21,21 @@ public class PlantUMLImporter extends AbstractDiagramImporter { private static final String NEWLINE = "\n"; public void importDiagram(ImageView view, File file) throws Exception { + importDiagram(view, file, null); + } + + public void importDiagram(ImageView view, File file, ColorScheme colorScheme) throws Exception { String content = new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8); view.setTitle(file.getName()); - importDiagram(view, content); + importDiagram(view, content, colorScheme); } public void importDiagram(ImageView view, String content) throws Exception { + importDiagram(view, content, null); + } + + public void importDiagram(ImageView view, String content, ColorScheme colorScheme) throws Exception { String plantUMLServer = getViewOrViewSetProperty(view, PLANTUML_URL_PROPERTY); if (StringUtils.isNullOrEmpty(plantUMLServer)) { throw new IllegalArgumentException("Please define a view/viewset property named " + PLANTUML_URL_PROPERTY + " to specify your PlantUML server"); @@ -47,12 +56,12 @@ public void importDiagram(ImageView view, String content) throws Exception { String inline = getViewOrViewSetProperty(view, PLANTUML_INLINE_PROPERTY); if ("true".equals(inline)) { if (format.equals(SVG_FORMAT)) { - view.setContent(ImageUtils.getSvgAsDataUri(new URL(url), true)); + view.setContent(ImageUtils.getSvgAsDataUri(new URL(url), true), colorScheme); } else { - view.setContent(ImageUtils.getPngAsDataUri(new URL(url), true)); + view.setContent(ImageUtils.getPngAsDataUri(new URL(url), true), colorScheme); } } else { - view.setContent(url); + view.setContent(url, colorScheme); } view.setContentType(CONTENT_TYPES_BY_FORMAT.get(format)); diff --git a/structurizr-import/src/test/java/com/structurizr/importer/diagrams/image/ImageImporterTests.java b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/image/ImageImporterTests.java index 4a916217f..1ea507825 100644 --- a/structurizr-import/src/test/java/com/structurizr/importer/diagrams/image/ImageImporterTests.java +++ b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/image/ImageImporterTests.java @@ -11,13 +11,14 @@ public class ImageImporterTests { @Test + @Tag("IntegrationTest") public void importDiagram_Url() throws Exception { Workspace workspace = new Workspace("Name", "Description"); ImageView view = workspace.getViews().createImageView("key"); new ImageImporter().importDiagram(view, "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/alexa-for-business.png"); assertEquals("https://static.structurizr.com/themes/amazon-web-services-2020.04.30/alexa-for-business.png", view.getContent()); - assertNull(view.getContentType()); + assertEquals("image/png", view.getContentType()); assertEquals("alexa-for-business.png", view.getTitle()); } From d9e71d770b6a7233b7ce7ba38eead9cd9da7bc02 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 19 Sep 2025 13:28:38 +0100 Subject: [PATCH 681/717] . --- .../StructurizrPlantUMLDiagramExporterTests.java | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java index 6e16d8b9b..5f2218db0 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java @@ -3171,16 +3171,4 @@ public void amazonWebServicesExample_Dark() throws Exception { @enduml""", diagram.getLegend().getDefinition()); } - @Test - void c4model() throws Exception { - Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("/Users/simon/Desktop/c4model/c4model/01-original/workspace.json")); - StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(); - - View view = workspace.getViews().getViewWithKey("Dynamic-SignIn-Collaboration"); - - Diagram diagram = exporter.export((DynamicView)view); - System.out.println(diagram.getDefinition()); - - } - } \ No newline at end of file From 428bd25ae1b2ac51946eaf764b21bc0b8945c9d9 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 23 Sep 2025 14:39:51 +0100 Subject: [PATCH 682/717] Typo. --- .../src/main/java/com/structurizr/view/ViewSet.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structurizr-core/src/main/java/com/structurizr/view/ViewSet.java b/structurizr-core/src/main/java/com/structurizr/view/ViewSet.java index ddb5241a2..334cf0698 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/ViewSet.java +++ b/structurizr-core/src/main/java/com/structurizr/view/ViewSet.java @@ -792,7 +792,7 @@ public Collection getImageViews() { return new TreeSet<>(imageViews); } - void setImageView(Set imageViews) { + void setImageViews(Set imageViews) { if (imageViews != null) { this.imageViews = new TreeSet<>(imageViews); } From 831cbff4fdee4171db1334c62e17ccede711601c Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 23 Sep 2025 17:54:18 +0100 Subject: [PATCH 683/717] structurizr-autolayout: Adds support for custom padding view/viewset properties: `structurizr.groupPadding`,`structurizr.boundaryPadding`, and `structurizr.deploymentNodePadding`. --- changelog.md | 1 + .../autolayout/graphviz/DOTExporter.java | 365 +----------------- .../autolayout/graphviz/DOTExporterTests.java | 139 ++++++- 3 files changed, 139 insertions(+), 366 deletions(-) diff --git a/changelog.md b/changelog.md index be3db6f30..ab7ecffea 100644 --- a/changelog.md +++ b/changelog.md @@ -2,6 +2,7 @@ ## v5.0.0 (unreleased) +- structurizr-autolayout: Adds support for custom padding view/viewset properties: `structurizr.groupPadding`,`structurizr.boundaryPadding`, and `structurizr.deploymentNodePadding`. - structurizr-core: Removes support for deprecated enterprise and location concepts. - structurizr-core: Adds support for filtered deployment views (https://github.com/structurizr/java/issues/409). - structurizr-core: Adds support for separate images for light and dark color schemes. diff --git a/structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/DOTExporter.java b/structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/DOTExporter.java index a28303f2b..489cf1bb6 100644 --- a/structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/DOTExporter.java +++ b/structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/DOTExporter.java @@ -17,7 +17,10 @@ */ class DOTExporter extends AbstractDiagramExporter { - private static final int CLUSTER_INTERNAL_MARGIN = 25; + private static final String DEFAULT_CLUSTER_INTERNAL_PADDING = "25"; + private static final String GROUP_PADDING_PROPERTY_NAME = "structurizr.groupPadding"; + private static final String BOUNDARY_PADDING_PROPERTY_NAME = "structurizr.boundaryPadding"; + private static final String DEPLOYMENT_NODE_PADDING_PROPERTY_NAME = "structurizr.deploymentNodePadding"; private Locale locale = Locale.US; private final RankDirection rankDirection; @@ -56,9 +59,8 @@ protected void writeFooter(ModelView view, IndentingWriter writer) { @Override protected void startGroupBoundary(ModelView view, String group, IndentingWriter writer) { writer.writeLine("subgraph \"cluster_group_" + (groupId++) + "\" {"); - writer.indent(); - writer.writeLine("margin=" + CLUSTER_INTERNAL_MARGIN); + writer.writeLine("margin=" + Integer.parseInt(getViewOrViewSetProperty(view, GROUP_PADDING_PROPERTY_NAME, DEFAULT_CLUSTER_INTERNAL_PADDING))); } @Override @@ -72,7 +74,7 @@ protected void endGroupBoundary(ModelView view, IndentingWriter writer) { protected void startSoftwareSystemBoundary(ModelView view, SoftwareSystem softwareSystem, IndentingWriter writer) { writer.writeLine(String.format("subgraph cluster_%s {", softwareSystem.getId())); writer.indent(); - writer.writeLine("margin=" + CLUSTER_INTERNAL_MARGIN); + writer.writeLine("margin=" + Integer.parseInt(getViewOrViewSetProperty(view, BOUNDARY_PADDING_PROPERTY_NAME, DEFAULT_CLUSTER_INTERNAL_PADDING))); } @Override @@ -86,7 +88,7 @@ protected void endSoftwareSystemBoundary(ModelView view, IndentingWriter writer) protected void startContainerBoundary(ModelView view, Container container, IndentingWriter writer) { writer.writeLine(String.format("subgraph cluster_%s {", container.getId())); writer.indent(); - writer.writeLine("margin=" + CLUSTER_INTERNAL_MARGIN); + writer.writeLine("margin=" + Integer.parseInt(getViewOrViewSetProperty(view, BOUNDARY_PADDING_PROPERTY_NAME, DEFAULT_CLUSTER_INTERNAL_PADDING))); } @Override @@ -100,7 +102,7 @@ protected void endContainerBoundary(ModelView view, IndentingWriter writer) { protected void startDeploymentNodeBoundary(DeploymentView view, DeploymentNode deploymentNode, IndentingWriter writer) { writer.writeLine(String.format("subgraph cluster_%s {", deploymentNode.getId())); writer.indent(); - writer.writeLine("margin=" + CLUSTER_INTERNAL_MARGIN); + writer.writeLine("margin=" + Integer.parseInt(getViewOrViewSetProperty(view, DEPLOYMENT_NODE_PADDING_PROPERTY_NAME, DEFAULT_CLUSTER_INTERNAL_PADDING))); } @Override @@ -175,350 +177,6 @@ protected Diagram createDiagram(ModelView view, String definition) { return new DOTDiagram(view, definition); } -// private void write(ModelView view, boolean enterpriseBoundaryIsVisible) throws Exception { -// File file = new File(path, view.getKey() + ".dot"); -// FileWriter fileWriter = new FileWriter(file); -// writeHeader(fileWriter, view); -// -// if (enterpriseBoundaryIsVisible) { -// fileWriter.write(" subgraph cluster_enterprise {\n"); -// fileWriter.write(" margin=" + CLUSTER_INTERNAL_MARGIN + "\n"); -// Set elementsInsideEnterpriseBoundary = new LinkedHashSet<>(); -// for (ElementView elementView : view.getElements()) { -// if (elementView.getElement() instanceof Person && ((Person)elementView.getElement()).getLocation() == Location.Internal) { -// elementsInsideEnterpriseBoundary.add((StaticStructureElement)elementView.getElement()); -// } -// if (elementView.getElement() instanceof SoftwareSystem && ((SoftwareSystem)elementView.getElement()).getLocation() == Location.Internal) { -// elementsInsideEnterpriseBoundary.add((StaticStructureElement)elementView.getElement()); -// } -// } -// writeElements(view, " ", elementsInsideEnterpriseBoundary, fileWriter); -// fileWriter.write(" }\n\n"); -// -// Set elementsOutsideEnterpriseBoundary = new LinkedHashSet<>(); -// for (ElementView elementView : view.getElements()) { -// if (elementView.getElement() instanceof Person && ((Person)elementView.getElement()).getLocation() != Location.Internal) { -// elementsOutsideEnterpriseBoundary.add((StaticStructureElement)elementView.getElement()); -// } -// if (elementView.getElement() instanceof SoftwareSystem && ((SoftwareSystem)elementView.getElement()).getLocation() != Location.Internal) { -// elementsOutsideEnterpriseBoundary.add((StaticStructureElement)elementView.getElement()); -// } -// if (elementView.getElement() instanceof CustomElement) { -// elementsOutsideEnterpriseBoundary.add((CustomElement)elementView.getElement()); -// } -// } -// -// writeElements(view, " ", elementsOutsideEnterpriseBoundary, fileWriter); -// } else { -// Set elements = new LinkedHashSet<>(); -// for (ElementView elementView : view.getElements()) { -// elements.add((GroupableElement)elementView.getElement()); -// } -// writeElements(view, " ", elements, fileWriter); -// } -// -// writeRelationships(view, fileWriter); -// writeFooter(fileWriter); -// fileWriter.close(); -// } -// -// void write(ContainerView view) throws Exception { -// File file = new File(path, view.getKey() + ".dot"); -// FileWriter fileWriter = new FileWriter(file); -// writeHeader(fileWriter, view); -// -// Set softwareSystems = new HashSet<>(); -// for (ElementView elementView : view.getElements()) { -// if (elementView.getElement().getParent() instanceof SoftwareSystem) { -// softwareSystems.add((SoftwareSystem)elementView.getElement().getParent()); -// } -// } -// List sortedSoftwareSystems = new ArrayList<>(softwareSystems); -// sortedSoftwareSystems.sort(Comparator.comparing(Element::getId)); -// -// for (SoftwareSystem softwareSystem : sortedSoftwareSystems) { -// fileWriter.write(String.format(locale, " subgraph cluster_%s {\n", softwareSystem.getId())); -// fileWriter.write(" margin=" + CLUSTER_INTERNAL_MARGIN + "\n"); -// -// Set scopedElements = new LinkedHashSet<>(); -// for (ElementView elementView : view.getElements()) { -// if (elementView.getElement().getParent() == softwareSystem) { -// scopedElements.add((StaticStructureElement) elementView.getElement()); -// } -// } -// writeElements(view, " ", scopedElements, fileWriter); -// fileWriter.write(" }\n"); -// -// } -// -// for (ElementView elementView : view.getElements()) { -// if (elementView.getElement().getParent() == null) { -// writeElement(view, " ", elementView.getElement(), fileWriter); -// } -// } -// -// writeRelationships(view, fileWriter); -// -// writeFooter(fileWriter); -// fileWriter.close(); -// } -// -// void write(ComponentView view) throws Exception { -// File file = new File(path, view.getKey() + ".dot"); -// FileWriter fileWriter = new FileWriter(file); -// writeHeader(fileWriter, view); -// -// Set containers = new HashSet<>(); -// for (ElementView elementView : view.getElements()) { -// if (elementView.getElement().getParent() instanceof Container) { -// containers.add((Container)elementView.getElement().getParent()); -// } -// } -// List sortedContainers = new ArrayList<>(containers); -// sortedContainers.sort(Comparator.comparing(Element::getId)); -// -// for (Container container : sortedContainers) { -// fileWriter.write(String.format(locale, " subgraph cluster_%s {\n", container.getId())); -// fileWriter.write(" margin=" + CLUSTER_INTERNAL_MARGIN + "\n"); -// -// Set scopedElements = new LinkedHashSet<>(); -// for (ElementView elementView : view.getElements()) { -// if (elementView.getElement().getParent() == container) { -// scopedElements.add((StaticStructureElement) elementView.getElement()); -// } -// } -// writeElements(view, " ", scopedElements, fileWriter); -// fileWriter.write(" }\n"); -// } -// -// for (ElementView elementView : view.getElements()) { -// if (!(elementView.getElement().getParent() instanceof Container)) { -// writeElement(view, " ", elementView.getElement(), fileWriter); -// } -// } -// -// writeRelationships(view, fileWriter); -// -// writeFooter(fileWriter); -// fileWriter.close(); -// } -// -// void write(DynamicView view) throws Exception { -// File file = new File(path, view.getKey() + ".dot"); -// FileWriter fileWriter = new FileWriter(file); -// writeHeader(fileWriter, view); -// -// Element element = view.getElement(); -// -// if (element == null) { -// for (ElementView elementView : view.getElements()) { -// writeElement(view, " ", elementView.getElement(), fileWriter); -// } -// } else if (element instanceof SoftwareSystem) { -// List softwareSystems = new ArrayList<>(view.getElements().stream().map(ElementView::getElement).filter(e -> e instanceof Container).map(c -> ((Container)c).getSoftwareSystem()).collect(Collectors.toSet())); -// softwareSystems.sort(Comparator.comparing(Element::getId)); -// -// for (SoftwareSystem softwareSystem : softwareSystems) { -// fileWriter.write(String.format(locale, " subgraph cluster_%s {\n", softwareSystem.getId())); -// fileWriter.write(" margin=" + CLUSTER_INTERNAL_MARGIN + "\n"); -// for (ElementView elementView : view.getElements()) { -// if (elementView.getElement().getParent() == softwareSystem) { -// writeElement(view, " ", elementView.getElement(), fileWriter); -// } -// } -// fileWriter.write(" }\n"); -// } -// -// for (ElementView elementView : view.getElements()) { -// if (elementView.getElement().getParent() == null) { -// writeElement(view, " ", elementView.getElement(), fileWriter); -// } -// } -// } else if (element instanceof Container) { -// List containers = new ArrayList<>(view.getElements().stream().map(ElementView::getElement).filter(e -> e instanceof Component).map(c -> ((Component)c).getContainer()).collect(Collectors.toSet())); -// containers.sort(Comparator.comparing(Element::getId)); -// -// for (Container container : containers) { -// fileWriter.write(String.format(locale, " subgraph cluster_%s {\n", container.getId())); -// fileWriter.write(" margin=" + CLUSTER_INTERNAL_MARGIN + "\n"); -// for (ElementView elementView : view.getElements()) { -// if (elementView.getElement().getParent() == container) { -// writeElement(view, " ", elementView.getElement(), fileWriter); -// } -// } -// fileWriter.write(" }\n"); -// } -// -// for (ElementView elementView : view.getElements()) { -// if (!(elementView.getElement().getParent() instanceof Container)) { -// writeElement(view, " ", elementView.getElement(), fileWriter); -// } -// } -// } -// -// writeRelationships(view, fileWriter); -// -// writeFooter(fileWriter); -// fileWriter.close(); -// } -// -// void write(DeploymentView view) throws Exception { -// File file = new File(path, view.getKey() + ".dot"); -// FileWriter fileWriter = new FileWriter(file); -// writeHeader(fileWriter, view); -// -// for (ElementView elementView : view.getElements()) { -// if (elementView.getElement() instanceof DeploymentNode && elementView.getElement().getParent() == null) { -// write(view, (DeploymentNode)elementView.getElement(), fileWriter, ""); -// } else if (elementView.getElement() instanceof CustomElement) { -// writeElement(view, " ", elementView.getElement(), fileWriter); -// } -// } -// -// writeRelationships(view, fileWriter); -// -// writeFooter(fileWriter); -// fileWriter.close(); -// } -// -// private void write(DeploymentView view, DeploymentNode deploymentNode, FileWriter fileWriter, String indent) throws Exception { -// fileWriter.write(String.format(locale, indent + "subgraph cluster_%s {\n", deploymentNode.getId())); -// fileWriter.write(indent + " margin=" + CLUSTER_INTERNAL_MARGIN + "\n"); -// fileWriter.write(String.format(locale, indent + " label=\"%s: %s\"\n", deploymentNode.getId(), deploymentNode.getName())); -// -// for (DeploymentNode child : deploymentNode.getChildren()) { -// if (view.isElementInView(child)) { -// write(view, child, fileWriter, indent + " "); -// -// } -// } -// -// for (InfrastructureNode infrastructureNode : deploymentNode.getInfrastructureNodes()) { -// if (view.isElementInView(infrastructureNode)) { -// writeElement(view, indent + " ", infrastructureNode, fileWriter); -// } -// } -// -// for (SoftwareSystemInstance softwareSystemInstance : deploymentNode.getSoftwareSystemInstances()) { -// if (view.isElementInView(softwareSystemInstance)) { -// writeElement(view, indent + " ", softwareSystemInstance, fileWriter); -// } -// } -// -// for (ContainerInstance containerInstance : deploymentNode.getContainerInstances()) { -// if (view.isElementInView(containerInstance)) { -// writeElement(view, indent + " ", containerInstance, fileWriter); -// } -// } -// -// fileWriter.write(indent + "}\n"); -// } -// -// private void writeElements(ModelView view, String padding, Set elements, Writer writer) throws Exception { -// String groupSeparator = view.getModel().getProperties().get(GROUP_SEPARATOR_PROPERTY_NAME); -// boolean nested = !StringUtils.isNullOrEmpty(groupSeparator); -// -// Set groups = new HashSet<>(); -// for (GroupableElement element : elements) { -// String group = element.getGroup(); -// -// if (!StringUtils.isNullOrEmpty(group)) { -// groups.add(group); -// -// if (nested) { -// while (group.contains(groupSeparator)) { -// group = group.substring(0, group.lastIndexOf(groupSeparator)); -// groups.add(group); -// } -// } -// } -// } -// -// List sortedGroups = new ArrayList<>(groups); -// sortedGroups.sort(String::compareTo); -// -// // first render grouped elements -// if (nested) { -// if (groups.size() > 0) { -// String context = ""; -// for (String group : sortedGroups) { -// int groupCount = group.split(groupSeparator).length; -// int contextCount = context.split(groupSeparator).length; -// -// if (groupCount > contextCount) { -// // moved from a to a/b -// // - increase padding -// padding = padding + INDENT; -// } else if (groupCount == contextCount) { -// // moved from a/b to a/c -// // - close off previous subgraph -// if (context.length() > 0) { -// writer.write(padding + "}\n"); -// } -// } else { -// // moved from a/b/c to a/b or a -// // - close off previous subgraphs -// // - close off current subgraph -// for (int i = 0; i < (contextCount - groupCount); i++) { -// writer.write(padding + "}\n"); -// padding = padding.substring(0, padding.length() - INDENT.length()); -// } -// writer.write(padding + "}\n"); -// } -// -// writer.write(padding + "subgraph cluster_group_" + groupId + " {\n"); -//// writer.write(padding + " // " + group + "\n"); -// writer.write(padding + " margin=" + CLUSTER_INTERNAL_MARGIN + "\n"); -// for (GroupableElement element : elements) { -// if (group.equals(element.getGroup())) { -// writeElement(view, padding + INDENT, element, writer); -// } -// } -// groupId++; -// context = group; -// } -// -// int contextCount = context.split(groupSeparator).length; -// for (int i = 0; i < contextCount; i++) { -// writer.write(padding + "}\n"); -// padding = padding.substring(0, padding.length() - INDENT.length()); -// } -// } -// } else { -// for (String group : sortedGroups) { -// writer.write(padding + "subgraph cluster_group_" + groupId + " {\n"); -// writer.write(padding + " margin=" + CLUSTER_INTERNAL_MARGIN + "\n"); -// for (GroupableElement element : elements) { -// if (group.equals(element.getGroup())) { -// writeElement(view, padding + INDENT, element, writer); -// } -// } -// writer.write(padding + "}\n"); -// groupId++; -// } -// } -// -// // then render ungrouped elements -// for (GroupableElement element : elements) { -// if (StringUtils.isNullOrEmpty(element.getGroup())) { -// writeElement(view, padding, element, writer); -// } -// } -// } -// -// private void writeElement(ModelView view, String padding, Element element, Writer writer) throws Exception { -// writer.write(String.format(locale, "%s%s [width=%f,height=%f,fixedsize=true,id=%s,label=\"%s: %s\"]", -// padding, -// element.getId(), -// getElementWidth(view, element.getId()) / Constants.STRUCTURIZR_DPI, // convert Structurizr dimensions to inches -// getElementHeight(view, element.getId()) / Constants.STRUCTURIZR_DPI, // convert Structurizr dimensions to inches -// element.getId(), -// element.getId(), -// escape(element.getName()) -// )); -// writer.write("\n"); -// } -// private String escape(String s) { if (StringUtils.isNullOrEmpty(s)) { return s; @@ -526,13 +184,6 @@ private String escape(String s) { return s.replaceAll("\"", "\\\\\""); } } -// -// private void writeRelationships(ModelView view, Writer writer) throws Exception { -// writer.write("\n"); -// -// for (RelationshipView relationshipView : view.getRelationships()) { -// } -// } private Element findElementInside(DeploymentNode deploymentNode, ModelView view) { for (SoftwareSystemInstance softwareSystemInstance : deploymentNode.getSoftwareSystemInstances()) { diff --git a/structurizr-autolayout/src/test/java/com/structurizr/autolayout/graphviz/DOTExporterTests.java b/structurizr-autolayout/src/test/java/com/structurizr/autolayout/graphviz/DOTExporterTests.java index 96bdabf8f..4e03672a6 100644 --- a/structurizr-autolayout/src/test/java/com/structurizr/autolayout/graphviz/DOTExporterTests.java +++ b/structurizr-autolayout/src/test/java/com/structurizr/autolayout/graphviz/DOTExporterTests.java @@ -79,7 +79,7 @@ public void test_writeSystemLandscapeView() { } @Test - public void test_writeSystemLandscapeViewWithGroupedElements() throws Exception { + public void test_writeSystemLandscapeViewWithGroupedElements() { Workspace workspace = new Workspace("Name", ""); CustomElement box = workspace.getModel().addCustomElement("Box"); Person user = workspace.getModel().addPerson("User", ""); @@ -121,7 +121,50 @@ public void test_writeSystemLandscapeViewWithGroupedElements() throws Exception } @Test - public void test_writeSystemLandscapeViewWithNestedGroupedElements() throws Exception { + public void test_writeSystemLandscapeViewWithGroupedElementsAndGroupPadding() { + Workspace workspace = new Workspace("Name", ""); + CustomElement box = workspace.getModel().addCustomElement("Box"); + Person user = workspace.getModel().addPerson("User", ""); + user.setGroup("External"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", ""); + softwareSystem.setGroup("Internal"); + user.uses(softwareSystem, "Uses"); + + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("SystemLandscape", ""); + view.addAllElements(); + view.add(box); + view.addProperty("structurizr.groupPadding", "50"); + + DOTExporter exporter = new DOTExporter(RankDirection.TopBottom, 300, 300); + Diagram diagram = exporter.export(view); + + String content = diagram.getDefinition(); + assertEquals(""" + digraph { + compound=true + graph [splines=polyline,rankdir=TB,ranksep=1.0,nodesep=1.0,fontsize=5] + node [shape=box,fontsize=5] + edge [] + + subgraph "cluster_group_1" { + margin=50 + 2 [width=1.500000,height=1.000000,fixedsize=true,id=2,label="2: User"] + } + + subgraph "cluster_group_2" { + margin=50 + 3 [width=1.500000,height=1.000000,fixedsize=true,id=3,label="3: Software System"] + } + + 1 [width=1.500000,height=1.000000,fixedsize=true,id=1,label="1: Box"] + + 2 -> 3 [id=4] + + }""", content); + } + + @Test + public void test_writeSystemLandscapeViewWithNestedGroupedElements() { Workspace workspace = new Workspace("Name", ""); workspace.getModel().addProperty("structurizr.groupSeparator", "/"); @@ -183,7 +226,7 @@ public void test_writeSystemLandscapeViewWithNestedGroupedElements() throws Exce } @Test - public void test_writeSystemLandscapeViewInGermanLocale() throws Exception { + public void test_writeSystemLandscapeViewInGermanLocale() { // ranksep=1.0 was being output as ranksep=1,0 Locale.setDefault(new Locale("de", "DE")); Workspace workspace = new Workspace("Name", ""); @@ -217,7 +260,7 @@ public void test_writeSystemLandscapeViewInGermanLocale() throws Exception { } @Test - public void test_writeSystemContextView() throws Exception { + public void test_writeSystemContextView() { Workspace workspace = new Workspace("Name", ""); CustomElement box = workspace.getModel().addCustomElement("Box"); Person user = workspace.getModel().addPerson("User", ""); @@ -250,7 +293,7 @@ public void test_writeSystemContextView() throws Exception { @Test - public void test_writeSystemContextViewWithGroupedElements() throws Exception { + public void test_writeSystemContextViewWithGroupedElements() { Workspace workspace = new Workspace("Name", ""); CustomElement box = workspace.getModel().addCustomElement("Box"); Person user = workspace.getModel().addPerson("User", ""); @@ -292,7 +335,7 @@ public void test_writeSystemContextViewWithGroupedElements() throws Exception { } @Test - public void test_writeContainerViewWithGroupedElementsInASingleSoftwareSystem() throws Exception { + public void test_writeContainerViewWithGroupedElementsInASingleSoftwareSystem() { Workspace workspace = new Workspace("Name", ""); CustomElement box = workspace.getModel().addCustomElement("Box"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", ""); @@ -344,7 +387,7 @@ public void test_writeContainerViewWithGroupedElementsInASingleSoftwareSystem() } @Test - public void test_writeContainerViewWithGroupedElementsInMultipleSoftwareSystems() throws Exception { + public void test_writeContainerViewWithGroupedElementsInMultipleSoftwareSystems() { Workspace workspace = new Workspace("Name", ""); SoftwareSystem softwareSystem1 = workspace.getModel().addSoftwareSystem("Software System 1"); @@ -396,7 +439,36 @@ public void test_writeContainerViewWithGroupedElementsInMultipleSoftwareSystems( } @Test - public void test_writeComponentViewWithGroupedElements() throws Exception { + public void test_writeContainerViewWithBoundaryPadding() { + Workspace workspace = new Workspace("Name", ""); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", ""); + softwareSystem.addContainer("Container"); + + ContainerView view = workspace.getViews().createContainerView(softwareSystem, "key"); + view.addAllElements(); + workspace.getViews().getConfiguration().addProperty("structurizr.boundaryPadding", "50"); + + DOTExporter exporter = new DOTExporter(RankDirection.TopBottom, 300, 300); + Diagram diagram = exporter.export(view); + + String content = diagram.getDefinition(); + assertEquals(""" + digraph { + compound=true + graph [splines=polyline,rankdir=TB,ranksep=1.0,nodesep=1.0,fontsize=5] + node [shape=box,fontsize=5] + edge [] + + subgraph cluster_1 { + margin=50 + 2 [width=1.500000,height=1.000000,fixedsize=true,id=2,label="2: Container"] + } + + }""", content); + } + + @Test + public void test_writeComponentViewWithGroupedElements() { Workspace workspace = new Workspace("Name", ""); CustomElement box = workspace.getModel().addCustomElement("Box"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", ""); @@ -449,7 +521,7 @@ public void test_writeComponentViewWithGroupedElements() throws Exception { } @Test - public void test_writeContainerViewWithGroupedElements_WithAndWithoutAGroupSeparator() throws Exception { + public void test_writeContainerViewWithGroupedElements_WithAndWithoutAGroupSeparator() { Workspace workspace = new Workspace("Name", ""); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System", ""); Container container1 = softwareSystem.addContainer("Container 1"); @@ -546,4 +618,53 @@ public void test_AmazonWebServicesExample() throws Exception { }""", diagram.getDefinition()); } + @Test + public void test_AmazonWebServicesExampleWithDeploymentNodePadding() throws Exception { + Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("src/test/resources/structurizr-54915-workspace.json")); + workspace.getViews().getConfiguration().addProperty("structurizr.deploymentNodePadding", "50"); + DOTExporter exporter = new DOTExporter(RankDirection.LeftRight, 300, 300); + Diagram diagram = exporter.export(workspace.getViews().getDeploymentViews().iterator().next()); + + assertEquals(""" + digraph { + compound=true + graph [splines=polyline,rankdir=LR,ranksep=1.0,nodesep=1.0,fontsize=5] + node [shape=box,fontsize=5] + edge [] + + subgraph cluster_5 { + margin=50 + subgraph cluster_6 { + margin=50 + subgraph cluster_12 { + margin=50 + subgraph cluster_13 { + margin=50 + 14 [width=1.500000,height=1.000000,fixedsize=true,id=14,label="14: Database"] + } + + } + + 7 [width=1.500000,height=1.000000,fixedsize=true,id=7,label="7: Route 53"] + 8 [width=1.500000,height=1.000000,fixedsize=true,id=8,label="8: Elastic Load Balancer"] + subgraph cluster_9 { + margin=50 + subgraph cluster_10 { + margin=50 + 11 [width=1.500000,height=1.000000,fixedsize=true,id=11,label="11: Web Application"] + } + + } + + } + + } + + 11 -> 14 [id=15] + 7 -> 8 [id=16] + 8 -> 11 [id=17] + + }""", diagram.getDefinition()); + } + } \ No newline at end of file From f3b721341a1beaf1434272f4e0061fa578934618 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 24 Sep 2025 15:35:29 +0100 Subject: [PATCH 684/717] Allows the order to be changed. --- .../src/main/java/com/structurizr/view/View.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/structurizr-core/src/main/java/com/structurizr/view/View.java b/structurizr-core/src/main/java/com/structurizr/view/View.java index 54588d8cf..b4132bcd7 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/View.java +++ b/structurizr-core/src/main/java/com/structurizr/view/View.java @@ -84,7 +84,12 @@ public int getOrder() { return order; } - void setOrder(int order) { + /** + * Sets the order of this view. + * + * @param order a positive integer + */ + public void setOrder(int order) { this.order = Math.max(1, order); } From aceacb974e112aed21b38bd1b114f89f111deb13 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 24 Sep 2025 15:35:50 +0100 Subject: [PATCH 685/717] Adds a way to remove views from the workspace. --- .../java/com/structurizr/view/ViewSet.java | 43 ++++++ .../com/structurizr/view/ViewSetTests.java | 137 ++++++++++++++++++ 2 files changed, 180 insertions(+) diff --git a/structurizr-core/src/main/java/com/structurizr/view/ViewSet.java b/structurizr-core/src/main/java/com/structurizr/view/ViewSet.java index 334cf0698..c91c3c4e3 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/ViewSet.java +++ b/structurizr-core/src/main/java/com/structurizr/view/ViewSet.java @@ -630,6 +630,49 @@ public View getViewWithKey(String key) { return getViews().stream().filter(v -> key.equals(v.getKey())).findFirst().orElse(null); } + /** + * Removes the view with the specified key. + * + * @param key the key + * @throws IllegalArgumentException if a view with the specified key could not be found + */ + public void removeViewWithKey(String key) { + if (StringUtils.isNullOrEmpty(key)) { + throw new IllegalArgumentException("A view key must be specified."); + } + + View view = getViewWithKey(key); + if (view == null) { + throw new IllegalArgumentException("A view with key \"" + key + "\" does not exist."); + } + + for (FilteredView filteredView : filteredViews) { + if (filteredView.getBaseViewKey().equals(key)) { + throw new IllegalArgumentException("A filtered view based upon \"" + key + "\" exists - please remove this first."); + } + } + + if (view instanceof CustomView) { + customViews.remove(view); + } else if (view instanceof SystemLandscapeView) { + systemLandscapeViews.remove(view); + } else if (view instanceof SystemContextView) { + systemContextViews.remove(view); + } else if (view instanceof ContainerView) { + containerViews.remove(view); + } else if (view instanceof ComponentView) { + componentViews.remove(view); + } else if (view instanceof DynamicView) { + dynamicViews.remove(view); + } else if (view instanceof DeploymentView) { + deploymentViews.remove(view); + } else if (view instanceof ImageView) { + imageViews.remove(view); + } else if (view instanceof FilteredView) { + filteredViews.remove(view); + } + } + /** * Finds the filtered view with the specified key, or null if the view does not exist. * diff --git a/structurizr-core/src/test/java/com/structurizr/view/ViewSetTests.java b/structurizr-core/src/test/java/com/structurizr/view/ViewSetTests.java index d862adf33..4bec2222f 100644 --- a/structurizr-core/src/test/java/com/structurizr/view/ViewSetTests.java +++ b/structurizr-core/src/test/java/com/structurizr/view/ViewSetTests.java @@ -1090,4 +1090,141 @@ public void createDefaultViews_ForSoftwareSystemsWithNamesUsingUTF8Characters() assertSame(ss3, workspace.getViews().getSystemContextViews().stream().filter(v -> v.getKey().equals("SystemContext-003")).findFirst().get().getSoftwareSystem()); } + @Test + void removeViewWithKey_ThrowsAndException_WhenNoKeyIsSpecified() { + try { + new Workspace("Name").getViews().removeViewWithKey(null); + fail(); + } catch (Exception e) { + assertEquals("A view key must be specified.", e.getMessage()); + } + + try { + new Workspace("Name").getViews().removeViewWithKey(""); + fail(); + } catch (Exception e) { + assertEquals("A view key must be specified.", e.getMessage()); + } + } + + @Test + void removeViewWithKey_ThrowsAndException_WhenNoViewExists() { + try { + new Workspace("Name").getViews().removeViewWithKey("key"); + fail(); + } catch (Exception e) { + assertEquals("A view with key \"key\" does not exist.", e.getMessage()); + } + } + + @Test + void removeViewWithKey_ThrowsAndException_WhenABaseViewExistsForTheSpecifiedFilteredView() { + Workspace workspace = new Workspace("Name"); + + try { + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("landscape"); + workspace.getViews().createFilteredView(view, "filtered", FilterMode.Include, Tags.ELEMENT); + + workspace.getViews().removeViewWithKey("landscape"); + fail(); + } catch (Exception e) { + assertEquals("A filtered view based upon \"landscape\" exists - please remove this first.", e.getMessage()); + } + } + + @Test + void removeViewWithKey_CustomView() { + Workspace workspace = new Workspace("Name"); + workspace.getViews().createCustomView("key", "title"); + + assertFalse(workspace.getViews().getCustomViews().isEmpty()); + workspace.getViews().removeViewWithKey("key"); + assertTrue(workspace.getViews().getCustomViews().isEmpty()); + } + + @Test + void removeViewWithKey_SystemLandscapeView() { + Workspace workspace = new Workspace("Name"); + workspace.getViews().createSystemLandscapeView("key"); + + assertFalse(workspace.getViews().getSystemLandscapeViews().isEmpty()); + workspace.getViews().removeViewWithKey("key"); + assertTrue(workspace.getViews().getSystemLandscapeViews().isEmpty()); + } + + @Test + void removeViewWithKey_SystemContextView() { + Workspace workspace = new Workspace("Name"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name"); + workspace.getViews().createSystemContextView(softwareSystem, "key"); + + assertFalse(workspace.getViews().getSystemContextViews().isEmpty()); + workspace.getViews().removeViewWithKey("key"); + assertTrue(workspace.getViews().getSystemContextViews().isEmpty()); + } + + @Test + void removeViewWithKey_ContainerView() { + Workspace workspace = new Workspace("Name"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name"); + workspace.getViews().createContainerView(softwareSystem, "key"); + + assertFalse(workspace.getViews().getContainerViews().isEmpty()); + workspace.getViews().removeViewWithKey("key"); + assertTrue(workspace.getViews().getContainerViews().isEmpty()); + } + + @Test + void removeViewWithKey_ComponentView() { + Workspace workspace = new Workspace("Name"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name"); + Container container = softwareSystem.addContainer("Name"); + workspace.getViews().createComponentView(container, "key"); + + assertFalse(workspace.getViews().getComponentViews().isEmpty()); + workspace.getViews().removeViewWithKey("key"); + assertTrue(workspace.getViews().getComponentViews().isEmpty()); + } + + @Test + void removeViewWithKey_DynamicView() { + Workspace workspace = new Workspace("Name"); + workspace.getViews().createDynamicView("key"); + + assertFalse(workspace.getViews().getDynamicViews().isEmpty()); + workspace.getViews().removeViewWithKey("key"); + assertTrue(workspace.getViews().getDynamicViews().isEmpty()); + } + + @Test + void removeViewWithKey_DeploymentView() { + Workspace workspace = new Workspace("Name"); + workspace.getViews().createDeploymentView("key"); + + assertFalse(workspace.getViews().getDeploymentViews().isEmpty()); + workspace.getViews().removeViewWithKey("key"); + assertTrue(workspace.getViews().getDeploymentViews().isEmpty()); + } + + @Test + void removeViewWithKey_FilteredView() { + Workspace workspace = new Workspace("Name"); + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("landscape"); + workspace.getViews().createFilteredView(view, "key", FilterMode.Include, Tags.ELEMENT); + + assertFalse(workspace.getViews().getFilteredViews().isEmpty()); + workspace.getViews().removeViewWithKey("key"); + assertTrue(workspace.getViews().getFilteredViews().isEmpty()); + } + + @Test + void removeViewWithKey_ImageView() { + Workspace workspace = new Workspace("Name"); + workspace.getViews().createImageView("key"); + + assertFalse(workspace.getViews().getImageViews().isEmpty()); + workspace.getViews().removeViewWithKey("key"); + assertTrue(workspace.getViews().getImageViews().isEmpty()); + } + } \ No newline at end of file From c8ba7f90be3ce065451164ecd782e1d2565a635a Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 24 Sep 2025 15:36:32 +0100 Subject: [PATCH 686/717] Better rendering for sequence diagrams. --- .../plantuml/StructurizrPlantUMLExporter.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java b/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java index 18238eb08..1cec09214 100644 --- a/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java +++ b/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java @@ -34,7 +34,7 @@ protected void writeHeader(ModelView view, IndentingWriter writer) { plantUMLStyles = new HashSet<>(); super.writeHeader(view, writer); - if (view instanceof DynamicView && renderAsSequenceDiagram(view)) { + if (renderAsSequenceDiagram(view)) { // do nothing } else { if (view.getAutomaticLayout() != null) { @@ -324,7 +324,6 @@ public Diagram export(DynamicView view) { @Override protected void writeElement(ModelView view, Element element, IndentingWriter writer) { ElementStyle elementStyle = findElementStyle(view, element); - PlantUMLElementStyle plantUMLElementStyle = new PlantUMLElementStyle( elementStyle.getTag(), elementStyle.getShape(), @@ -332,7 +331,7 @@ protected void writeElement(ModelView view, Element element, IndentingWriter wri elementStyle.getBackground(), elementStyle.getColor(), elementStyle.getStroke(), - elementStyle.getStrokeWidth() != null ? elementStyle.getStrokeWidth() : DEFAULT_STROKE_WIDTH, + renderAsSequenceDiagram(view) ? DEFAULT_STROKE_WIDTH : elementStyle.getStrokeWidth() != null ? elementStyle.getStrokeWidth() : DEFAULT_STROKE_WIDTH, elementStyle.getBorder(), elementStyle.getFontSize(), elementStyle.getIcon(), @@ -342,7 +341,7 @@ protected void writeElement(ModelView view, Element element, IndentingWriter wri int metadataFontSize = calculateMetadataFontSize(elementStyle.getFontSize()); - if (view instanceof DynamicView && renderAsSequenceDiagram(view)) { + if (renderAsSequenceDiagram(view)) { writer.writeLine(String.format("%s \"%s\\n%s\" as %s <<%s>> %s", plantumlSequenceType(view, element), element.getName(), @@ -424,7 +423,7 @@ protected void writeRelationship(ModelView view, RelationshipView relationshipVi style.getTag(), style.getColor(), style.getStyle(), - style.getThickness(), + renderAsSequenceDiagram(view) ? DEFAULT_STROKE_WIDTH : style.getThickness(), style.getFontSize() ); plantUMLStyles.add(plantUMLRelationshipStyle); @@ -432,7 +431,7 @@ protected void writeRelationship(ModelView view, RelationshipView relationshipVi String description = ""; String technology = relationship.getTechnology(); - if (view instanceof DynamicView && renderAsSequenceDiagram(view)) { + if (renderAsSequenceDiagram(view)) { // do nothing - sequence diagrams don't need the order } else { if (!StringUtils.isNullOrEmpty(relationshipView.getOrder())) { @@ -442,7 +441,7 @@ protected void writeRelationship(ModelView view, RelationshipView relationshipVi description += (hasValue(relationshipView.getDescription()) ? relationshipView.getDescription() : hasValue(relationshipView.getRelationship().getDescription()) ? relationshipView.getRelationship().getDescription() : ""); - if (view instanceof DynamicView && renderAsSequenceDiagram(view)) { + if (renderAsSequenceDiagram(view)) { String arrowStart = "-"; String arrowEnd = ">"; From 837fe69b3af77f6004777c6da6fe78f77df766b5 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 26 Sep 2025 11:25:05 +0100 Subject: [PATCH 687/717] structurizr-export: StructurizrPlantUMLExporter - adds technology to sequence diagrams. Closes #425. --- changelog.md | 1 + .../export/plantuml/StructurizrPlantUMLExporter.java | 12 ++++-------- .../StructurizrPlantUMLDiagramExporterTests.java | 6 +++--- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/changelog.md b/changelog.md index ab7ecffea..edf6b0519 100644 --- a/changelog.md +++ b/changelog.md @@ -23,6 +23,7 @@ - structurizr-export: Removes support for deprecated enterprise and location concepts. - structurizr-export: PlantUML exporters - replaces skinparams with styles. - structurizr-export: PlantUML exporters - adds support for dark mode exports. +- structurizr-export: StructurizrPlantUMLExporter - adds technology to sequence diagrams (https://github.com/structurizr/java/issues/425) - structurizr-import: Adds support for `plantuml.inline`, `mermaid.inline`, `kroki.inline`, and `image.inline` properties to inline the resulting PNG/SVG file into the workspace. - structurizr-inspection: Adds a way to disable inspections via a workspace property named `structurizr.inspection` (`false` to disable). - structurizr-inspection: Default inspector adds a summary of error/warning/info/ignore counts as workspace properties. diff --git a/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java b/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java index 1cec09214..5553d6117 100644 --- a/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java +++ b/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java @@ -428,6 +428,7 @@ protected void writeRelationship(ModelView view, RelationshipView relationshipVi ); plantUMLStyles.add(plantUMLRelationshipStyle); + int metadataFontSize = calculateMetadataFontSize(style.getFontSize()); String description = ""; String technology = relationship.getTechnology(); @@ -451,13 +452,14 @@ protected void writeRelationship(ModelView view, RelationshipView relationshipVi } writer.writeLine( - String.format("%s %s%s %s <<%s>> : %s", + String.format("%s %s%s %s <<%s>> : %s%s", idOf(relationship.getSource()), arrowStart, arrowEnd, idOf(relationship.getDestination()), plantUMLRelationshipStyle.getClassSelector(), - description)); + description, + (StringUtils.isNullOrEmpty(technology) ? "" : "\\n[" + technology + "]"))); } else { String arrow; @@ -467,12 +469,6 @@ protected void writeRelationship(ModelView view, RelationshipView relationshipVi arrow = "-->"; } -// if (!isVisible(view, relationshipView)) { -// relationshipStyle = "hidden"; -// } - - int metadataFontSize = calculateMetadataFontSize(style.getFontSize()); - // 1 --> 2 : "...\n... writer.writeLine(format("%s %s %s <<%s>> : \"%s%s\"", idOf(relationship.getSource()), diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java index 5f2218db0..846fa0f3d 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java @@ -647,7 +647,7 @@ public void dynamicView_SequenceStyle_NoStyling_Light() { SoftwareSystem a = workspace.getModel().addSoftwareSystem("A"); SoftwareSystem b = workspace.getModel().addSoftwareSystem("B"); - a.uses(b, "Uses"); + a.uses(b, "Uses", "JSON/HTTPS"); DynamicView view = workspace.getViews().createDynamicView("key", "Description"); view.add(a, b); @@ -692,8 +692,8 @@ public void dynamicView_SequenceStyle_NoStyling_Light() { participant "A\\n[Software System]" as A <> #ffffff participant "B\\n[Software System]" as B <> #ffffff - - A -> B <> : Uses + + A -> B <> : 1. Uses\\n[JSON/HTTPS] @enduml""", diagram.getDefinition()); From cb57d23ef1a814b9be8a381143f1aff8bd71c1f4 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 26 Sep 2025 11:29:55 +0100 Subject: [PATCH 688/717] structurizr-export: PlantUML exporters - adds order number to relationships in sequence diagrams. Closes #431. --- changelog.md | 1 + .../export/plantuml/C4PlantUMLExporter.java | 8 ++---- .../plantuml/StructurizrPlantUMLExporter.java | 8 ++---- .../C4PlantUMLDiagramExporterTests.java | 24 ++++++++--------- ...ructurizrPlantUMLDiagramExporterTests.java | 26 +++++++++---------- 5 files changed, 30 insertions(+), 37 deletions(-) diff --git a/changelog.md b/changelog.md index edf6b0519..ec4b6548e 100644 --- a/changelog.md +++ b/changelog.md @@ -23,6 +23,7 @@ - structurizr-export: Removes support for deprecated enterprise and location concepts. - structurizr-export: PlantUML exporters - replaces skinparams with styles. - structurizr-export: PlantUML exporters - adds support for dark mode exports. +- structurizr-export: PlantUML exporters - adds order number to relationships in sequence diagrams. - structurizr-export: StructurizrPlantUMLExporter - adds technology to sequence diagrams (https://github.com/structurizr/java/issues/425) - structurizr-import: Adds support for `plantuml.inline`, `mermaid.inline`, `kroki.inline`, and `image.inline` properties to inline the resulting PNG/SVG file into the workspace. - structurizr-inspection: Adds a way to disable inspections via a workspace property named `structurizr.inspection` (`false` to disable). diff --git a/structurizr-export/src/main/java/com/structurizr/export/plantuml/C4PlantUMLExporter.java b/structurizr-export/src/main/java/com/structurizr/export/plantuml/C4PlantUMLExporter.java index ef3c2a718..58bb31d28 100644 --- a/structurizr-export/src/main/java/com/structurizr/export/plantuml/C4PlantUMLExporter.java +++ b/structurizr-export/src/main/java/com/structurizr/export/plantuml/C4PlantUMLExporter.java @@ -611,12 +611,8 @@ protected void writeRelationship(ModelView view, RelationshipView relationshipVi String description = ""; - if (renderAsSequenceDiagram(view)) { - // do nothing - sequence diagrams don't need the order - } else { - if (!StringUtils.isNullOrEmpty(relationshipView.getOrder())) { - description = relationshipView.getOrder() + ". "; - } + if (!StringUtils.isNullOrEmpty(relationshipView.getOrder())) { + description = relationshipView.getOrder() + ": "; } description += (hasValue(relationshipView.getDescription()) ? relationshipView.getDescription() : hasValue(relationshipView.getRelationship().getDescription()) ? relationshipView.getRelationship().getDescription() : ""); diff --git a/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java b/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java index 5553d6117..c71534276 100644 --- a/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java +++ b/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java @@ -432,12 +432,8 @@ protected void writeRelationship(ModelView view, RelationshipView relationshipVi String description = ""; String technology = relationship.getTechnology(); - if (renderAsSequenceDiagram(view)) { - // do nothing - sequence diagrams don't need the order - } else { - if (!StringUtils.isNullOrEmpty(relationshipView.getOrder())) { - description = relationshipView.getOrder() + ". "; - } + if (!StringUtils.isNullOrEmpty(relationshipView.getOrder())) { + description = relationshipView.getOrder() + ": "; } description += (hasValue(relationshipView.getDescription()) ? relationshipView.getDescription() : hasValue(relationshipView.getRelationship().getDescription()) ? relationshipView.getRelationship().getDescription() : ""); diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java index 784c6fe7a..a8d073c18 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java @@ -275,12 +275,12 @@ public void test_BigBankPlcExample() throws Exception { Container(InternetBankingSystem.SinglePageApplication, "Single-Page Application", $techn="JavaScript and Angular", $descr="Provides all of the Internet banking functionality to customers via their web browser.", $tags="Container,Web Browser", $link="") ContainerDb(InternetBankingSystem.Database, "Database", $techn="Oracle Database Schema", $descr="Stores user registration information, hashed authentication credentials, access logs, etc.", $tags="Container,Database", $link="") - Rel(InternetBankingSystem.SinglePageApplication, InternetBankingSystem.APIApplication.SignInController, "1. Submits credentials to", $techn="JSON/HTTPS", $tags="Relationship", $link="") - Rel(InternetBankingSystem.APIApplication.SignInController, InternetBankingSystem.APIApplication.SecurityComponent, "2. Validates credentials using", $techn="", $tags="Relationship", $link="") - Rel(InternetBankingSystem.APIApplication.SecurityComponent, InternetBankingSystem.Database, "3. select * from users where username = ?", $techn="SQL/TCP", $tags="Relationship", $link="") - Rel(InternetBankingSystem.Database, InternetBankingSystem.APIApplication.SecurityComponent, "4. Returns user data to", $techn="SQL/TCP", $tags="Relationship", $link="") - Rel(InternetBankingSystem.APIApplication.SecurityComponent, InternetBankingSystem.APIApplication.SignInController, "5. Returns true if the hashed password matches", $techn="", $tags="Relationship", $link="") - Rel(InternetBankingSystem.APIApplication.SignInController, InternetBankingSystem.SinglePageApplication, "6. Sends back an authentication token to", $techn="JSON/HTTPS", $tags="Relationship", $link="") + Rel(InternetBankingSystem.SinglePageApplication, InternetBankingSystem.APIApplication.SignInController, "1: Submits credentials to", $techn="JSON/HTTPS", $tags="Relationship", $link="") + Rel(InternetBankingSystem.APIApplication.SignInController, InternetBankingSystem.APIApplication.SecurityComponent, "2: Validates credentials using", $techn="", $tags="Relationship", $link="") + Rel(InternetBankingSystem.APIApplication.SecurityComponent, InternetBankingSystem.Database, "3: select * from users where username = ?", $techn="SQL/TCP", $tags="Relationship", $link="") + Rel(InternetBankingSystem.Database, InternetBankingSystem.APIApplication.SecurityComponent, "4: Returns user data to", $techn="SQL/TCP", $tags="Relationship", $link="") + Rel(InternetBankingSystem.APIApplication.SecurityComponent, InternetBankingSystem.APIApplication.SignInController, "5: Returns true if the hashed password matches", $techn="", $tags="Relationship", $link="") + Rel(InternetBankingSystem.APIApplication.SignInController, InternetBankingSystem.SinglePageApplication, "6: Sends back an authentication token to", $techn="JSON/HTTPS", $tags="Relationship", $link="") SHOW_LEGEND(true) hide stereotypes @@ -470,12 +470,12 @@ public void test_BigBankPlcExample() throws Exception { Component(InternetBankingSystem.APIApplication.SecurityComponent, "Security Component", $techn="Spring Bean", $descr="Provides functionality related to signing in, changing passwords, etc.", $tags="Component", $link="") ContainerDb(InternetBankingSystem.Database, "Database", $techn="Oracle Database Schema", $descr="Stores user registration information, hashed authentication credentials, access logs, etc.", $tags="Container,Database", $link="") - Rel(InternetBankingSystem.SinglePageApplication, InternetBankingSystem.APIApplication.SignInController, "Submits credentials to", $techn="JSON/HTTPS", $tags="Relationship", $link="") - Rel(InternetBankingSystem.APIApplication.SignInController, InternetBankingSystem.APIApplication.SecurityComponent, "Validates credentials using", $techn="", $tags="Relationship", $link="") - Rel(InternetBankingSystem.APIApplication.SecurityComponent, InternetBankingSystem.Database, "select * from users where username = ?", $techn="SQL/TCP", $tags="Relationship", $link="") - Rel(InternetBankingSystem.Database, InternetBankingSystem.APIApplication.SecurityComponent, "Returns user data to", $techn="SQL/TCP", $tags="Relationship", $link="") - Rel(InternetBankingSystem.APIApplication.SecurityComponent, InternetBankingSystem.APIApplication.SignInController, "Returns true if the hashed password matches", $techn="", $tags="Relationship", $link="") - Rel(InternetBankingSystem.APIApplication.SignInController, InternetBankingSystem.SinglePageApplication, "Sends back an authentication token to", $techn="JSON/HTTPS", $tags="Relationship", $link="") + Rel(InternetBankingSystem.SinglePageApplication, InternetBankingSystem.APIApplication.SignInController, "1: Submits credentials to", $techn="JSON/HTTPS", $tags="Relationship", $link="") + Rel(InternetBankingSystem.APIApplication.SignInController, InternetBankingSystem.APIApplication.SecurityComponent, "2: Validates credentials using", $techn="", $tags="Relationship", $link="") + Rel(InternetBankingSystem.APIApplication.SecurityComponent, InternetBankingSystem.Database, "3: select * from users where username = ?", $techn="SQL/TCP", $tags="Relationship", $link="") + Rel(InternetBankingSystem.Database, InternetBankingSystem.APIApplication.SecurityComponent, "4: Returns user data to", $techn="SQL/TCP", $tags="Relationship", $link="") + Rel(InternetBankingSystem.APIApplication.SecurityComponent, InternetBankingSystem.APIApplication.SignInController, "5: Returns true if the hashed password matches", $techn="", $tags="Relationship", $link="") + Rel(InternetBankingSystem.APIApplication.SignInController, InternetBankingSystem.SinglePageApplication, "6: Sends back an authentication token to", $techn="JSON/HTTPS", $tags="Relationship", $link="") SHOW_LEGEND(true) hide stereotypes diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java index 846fa0f3d..ea8e19b5f 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java @@ -526,7 +526,7 @@ public void dynamicView_CollaborationStyle_NoStyling_Light() { rectangle "==A\\n[Software System]" <> as A rectangle "==B\\n[Software System]" <> as B - A --> B <> : "1. Uses" + A --> B <> : "1: Uses" @enduml""", diagram.getDefinition()); } @@ -590,8 +590,8 @@ public void dynamicView_CollaborationStyle_Frames() { rectangle "==C\\n[Software System]" <> as C hide C - A --> B <> : "1. Uses" - B --> C <> : "2. Uses" + A --> B <> : "1: Uses" + B --> C <> : "2: Uses" @enduml""", frames.get(0).getDefinition()); @@ -635,8 +635,8 @@ public void dynamicView_CollaborationStyle_Frames() { rectangle "==B\\n[Software System]" <> as B rectangle "==C\\n[Software System]" <> as C - A --> B <> : "1. Uses" - B --> C <> : "2. Uses" + A --> B <> : "1: Uses" + B --> C <> : "2: Uses" @enduml""", frames.get(1).getDefinition()); } @@ -1511,7 +1511,7 @@ public void dynamicView_ExternalContainers() { rectangle "==Container 2\\n[Container]" <> as SoftwareSystem2.Container2 } - SoftwareSystem1.Container1 --> SoftwareSystem2.Container2 <> : "1. Uses" + SoftwareSystem1.Container1 --> SoftwareSystem2.Container2 <> : "1: Uses" @enduml""", diagram.getDefinition()); } @@ -1603,8 +1603,8 @@ public void dynamicView_ExternalComponents() { rectangle "==Component 3\\n[Component]" <> as SoftwareSystem2.Container2.Component3 } - SoftwareSystem1.Container1.Component1 --> SoftwareSystem1.Container1.Component2 <> : "1. Uses" - SoftwareSystem1.Container1.Component2 --> SoftwareSystem2.Container2.Component3 <> : "2. Uses" + SoftwareSystem1.Container1.Component1 --> SoftwareSystem1.Container1.Component2 <> : "1: Uses" + SoftwareSystem1.Container1.Component2 --> SoftwareSystem2.Container2.Component3 <> : "2: Uses" @enduml""", diagram.getDefinition()); } @@ -1725,8 +1725,8 @@ public void dynamicView_ExternalComponentsAndSoftwareSystemBoundariesIncluded() } - SoftwareSystem1.Container1.Component1 --> SoftwareSystem1.Container1.Component2 <> : "1. Uses" - SoftwareSystem1.Container1.Component2 --> SoftwareSystem2.Container2.Component3 <> : "2. Uses" + SoftwareSystem1.Container1.Component1 --> SoftwareSystem1.Container1.Component2 <> : "1: Uses" + SoftwareSystem1.Container1.Component2 --> SoftwareSystem2.Container2.Component3 <> : "2: Uses" @enduml""", diagram.getDefinition()); } @@ -2018,7 +2018,7 @@ public void dynamicView_UnscopedWithGroups() { } - A --> B <> : "1. Uses" + A --> B <> : "1: Uses" @enduml""", diagram.getDefinition()); } @@ -2132,7 +2132,7 @@ public void dynamicView_SoftwareSystemScopedWithGroups() { } - A.A --> B.B <> : "1. Uses" + A.A --> B.B <> : "1: Uses" @enduml""", diagram.getDefinition()); } @@ -2248,7 +2248,7 @@ public void dynamicView_ContainerScopedWithGroups() { } - A.A.A --> B.B.B <> : "1. Uses" + A.A.A --> B.B.B <> : "1: Uses" @enduml""", diagram.getDefinition()); } From ee0e4c07996659b5e809764214396a4e7592e121 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 26 Sep 2025 11:32:59 +0100 Subject: [PATCH 689/717] . --- .../plantuml/StructurizrPlantUMLDiagramExporterTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java index ea8e19b5f..116cdf144 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java @@ -693,7 +693,7 @@ public void dynamicView_SequenceStyle_NoStyling_Light() { participant "A\\n[Software System]" as A <> #ffffff participant "B\\n[Software System]" as B <> #ffffff - A -> B <> : 1. Uses\\n[JSON/HTTPS] + A -> B <> : 1: Uses\\n[JSON/HTTPS] @enduml""", diagram.getDefinition()); From 83f33e951fe7684d45e2eca0d3af7e7957bea269 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 26 Sep 2025 14:01:23 +0100 Subject: [PATCH 690/717] Fixes an issue where containers could be rendered outside of their software system boundary on component views. --- .../autolayout/graphviz/DOTExporterTests.java | 20 ++-- .../export/AbstractDiagramExporter.java | 68 ++++++++---- .../export/plantuml/C4PlantUMLExporter.java | 4 +- .../export/dot/DOTDiagramExporterTests.java | 96 ++++++++++++----- .../mermaid/MermaidDiagramExporterTests.java | 49 ++++++--- .../C4PlantUMLDiagramExporterTests.java | 76 ++++++++----- ...ructurizrPlantUMLDiagramExporterTests.java | 101 +++++++++++------- 7 files changed, 271 insertions(+), 143 deletions(-) diff --git a/structurizr-autolayout/src/test/java/com/structurizr/autolayout/graphviz/DOTExporterTests.java b/structurizr-autolayout/src/test/java/com/structurizr/autolayout/graphviz/DOTExporterTests.java index 4e03672a6..3bc79c14a 100644 --- a/structurizr-autolayout/src/test/java/com/structurizr/autolayout/graphviz/DOTExporterTests.java +++ b/structurizr-autolayout/src/test/java/com/structurizr/autolayout/graphviz/DOTExporterTests.java @@ -499,19 +499,23 @@ public void test_writeComponentViewWithGroupedElements() { 1 [width=1.500000,height=1.000000,fixedsize=true,id=1,label="1: Box"] - subgraph cluster_3 { + subgraph cluster_2 { margin=25 - subgraph "cluster_group_1" { + subgraph cluster_3 { margin=25 - 5 [width=1.500000,height=1.000000,fixedsize=true,id=5,label="5: Component 2"] - } + subgraph "cluster_group_1" { + margin=25 + 5 [width=1.500000,height=1.000000,fixedsize=true,id=5,label="5: Component 2"] + } - subgraph "cluster_group_2" { - margin=25 - 6 [width=1.500000,height=1.000000,fixedsize=true,id=6,label="6: Component 3"] + subgraph "cluster_group_2" { + margin=25 + 6 [width=1.500000,height=1.000000,fixedsize=true,id=6,label="6: Component 3"] + } + + 4 [width=1.500000,height=1.000000,fixedsize=true,id=4,label="4: Component 1"] } - 4 [width=1.500000,height=1.000000,fixedsize=true,id=4,label="4: Component 1"] } 4 -> 5 [id=7] diff --git a/structurizr-export/src/main/java/com/structurizr/export/AbstractDiagramExporter.java b/structurizr-export/src/main/java/com/structurizr/export/AbstractDiagramExporter.java index 81894c514..58df90d5f 100644 --- a/structurizr-export/src/main/java/com/structurizr/export/AbstractDiagramExporter.java +++ b/structurizr-export/src/main/java/com/structurizr/export/AbstractDiagramExporter.java @@ -283,30 +283,38 @@ public Diagram export(ComponentView view, Integer animationStep) { IndentingWriter writer = new IndentingWriter(); writeHeader(view, writer); - boolean elementsWritten = false; - for (ElementView elementView : view.getElements()) { - if (!(elementView.getElement() instanceof Component)) { - writeElement(view, elementView.getElement(), writer); - elementsWritten = true; - } + List customElements = getCustomElements(view); + for (CustomElement customElement : customElements) { + writeElement(view, customElement, writer); } - - if (elementsWritten) { + if (!customElements.isEmpty()) { writer.writeLine(); } - boolean includeSoftwareSystemBoundaries = "true".equals(view.getProperties().getOrDefault("structurizr.softwareSystemBoundaries", "false")); + List people = getPeople(view); + for (Person person : people) { + writeElement(view, person, writer); + } + if (!people.isEmpty()) { + writer.writeLine(); + } - List containers = getBoundaryContainers(view); - Set softwareSystems = containers.stream().map(Container::getSoftwareSystem).collect(Collectors.toCollection(LinkedHashSet::new)); + List softwareSystems = getSoftwareSystems(view); for (SoftwareSystem softwareSystem : softwareSystems) { + writeElement(view, softwareSystem, writer); + } + if (!softwareSystems.isEmpty()) { + writer.writeLine(); + } - if (includeSoftwareSystemBoundaries) { - startSoftwareSystemBoundary(view, softwareSystem, writer); - writer.indent(); - } + List boundaryContainers = getBoundaryContainers(view); + List containers = getContainers(view); + Set boundarySoftwareSystems = boundaryContainers.stream().map(Container::getSoftwareSystem).collect(Collectors.toCollection(LinkedHashSet::new)); + for (SoftwareSystem softwareSystem : boundarySoftwareSystems) { - for (Container container : containers) { + startSoftwareSystemBoundary(view, softwareSystem, writer); + + for (Container container : boundaryContainers) { if (container.getSoftwareSystem() == softwareSystem) { startContainerBoundary(view, container, writer); @@ -317,10 +325,13 @@ public Diagram export(ComponentView view, Integer animationStep) { } } - if (includeSoftwareSystemBoundaries) { - endSoftwareSystemBoundary(view, writer); - writer.outdent(); + for (Container container : containers) { + if (container.getSoftwareSystem() == softwareSystem) { + writeElement(view, container, writer); + } } + + endSoftwareSystemBoundary(view, writer); } writeRelationships(view, writer); @@ -330,11 +341,24 @@ public Diagram export(ComponentView view, Integer animationStep) { return createDiagram(view, writer.toString()); } + protected List getCustomElements(ModelView view) { + return view.getElements().stream().map(ElementView::getElement).filter(e -> e instanceof CustomElement).map(c -> ((CustomElement) c)).distinct().sorted(Comparator.comparing(Element::getId)).collect(Collectors.toList()); + } + + protected List getPeople(ModelView view) { + return view.getElements().stream().map(ElementView::getElement).filter(e -> e instanceof Person).map(c -> ((Person) c)).distinct().sorted(Comparator.comparing(Element::getId)).collect(Collectors.toList()); + } + + protected List getSoftwareSystems(ModelView view) { + return view.getElements().stream().map(ElementView::getElement).filter(e -> e instanceof SoftwareSystem).map(c -> ((SoftwareSystem) c)).distinct().sorted(Comparator.comparing(Element::getId)).collect(Collectors.toList()); + } + protected List getBoundaryContainers(ModelView view) { - List containers = new ArrayList<>(view.getElements().stream().map(ElementView::getElement).filter(e -> e instanceof Component).map(c -> ((Component)c).getContainer()).collect(Collectors.toSet())); - containers.sort(Comparator.comparing(Element::getId)); + return view.getElements().stream().map(ElementView::getElement).filter(e -> e instanceof Component).map(c -> ((Component) c).getContainer()).distinct().sorted(Comparator.comparing(Element::getId)).collect(Collectors.toList()); + } - return containers; + protected List getContainers(ModelView view) { + return view.getElements().stream().map(ElementView::getElement).filter(e -> e instanceof Container).map(c -> ((Container) c)).distinct().sorted(Comparator.comparing(Element::getId)).collect(Collectors.toList()); } public Diagram export(DynamicView view) { diff --git a/structurizr-export/src/main/java/com/structurizr/export/plantuml/C4PlantUMLExporter.java b/structurizr-export/src/main/java/com/structurizr/export/plantuml/C4PlantUMLExporter.java index 58bb31d28..4f309a149 100644 --- a/structurizr-export/src/main/java/com/structurizr/export/plantuml/C4PlantUMLExporter.java +++ b/structurizr-export/src/main/java/com/structurizr/export/plantuml/C4PlantUMLExporter.java @@ -249,8 +249,6 @@ protected void writeHeader(ModelView view, IndentingWriter writer) { } if (!boundaryStyles.isEmpty()) { - writer.writeLine(); - for (String tagList : boundaryStyles.keySet()) { ElementStyle elementStyle = boundaryStyles.get(tagList); tagList = tagList.replaceFirst("Element,", ""); @@ -273,6 +271,8 @@ protected void writeHeader(ModelView view, IndentingWriter writer) { line = line.replace(", $borderThickness=\"1\")", ")"); writer.writeLine(line); } + + writer.writeLine(); } } } diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/DOTDiagramExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/dot/DOTDiagramExporterTests.java index 937af8766..35cebec48 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/dot/DOTDiagramExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/dot/DOTDiagramExporterTests.java @@ -148,24 +148,34 @@ public void test_BigBankPlcExample() throws Exception { 4 [id=4,shape=rect, label=<Mainframe Banking
    System

    [Software System]

    Stores all of the core banking
    information about customers,
    accounts, transactions, etc.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] 5 [id=5,shape=rect, label=<E-mail System
    [Software System]

    The internal Microsoft
    Exchange e-mail system.
    >, style=filled, color="#6b6b6b", fillcolor="#999999", fontcolor="#ffffff"] - 8 [id=8,shape=rect, label=<Single-Page
    Application

    [Container: JavaScript and Angular]

    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] - 9 [id=9,shape=rect, label=<Mobile App
    [Container: Xamarin]

    Provides a limited subset of
    the Internet banking
    functionality to customers via
    their mobile device.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] - 18 [id=18,shape=cylinder, label=<Database
    [Container: Oracle Database Schema]

    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] - subgraph cluster_11 { + subgraph cluster_7 { margin=25 - label=<
    API Application

    [Container: Java and Spring MVC]> + label=<
    Internet Banking System

    [Software System]> labelloc=b color="#444444" fontcolor="#444444" fillcolor="#444444" - 12 [id=12,shape=rect, label=<Sign In Controller
    [Component: Spring MVC Rest Controller]

    Allows users to sign in to the
    Internet Banking System.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] - 13 [id=13,shape=rect, label=<Accounts Summary
    Controller

    [Component: Spring MVC Rest Controller]

    Provides customers with a
    summary of their bank
    accounts.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] - 14 [id=14,shape=rect, label=<Reset Password
    Controller

    [Component: Spring MVC Rest Controller]

    Allows users to reset their
    passwords with a single use
    URL.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] - 15 [id=15,shape=rect, label=<Security Component
    [Component: Spring Bean]

    Provides functionality related
    to signing in, changing
    passwords, etc.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] - 16 [id=16,shape=rect, label=<Mainframe Banking
    System Facade

    [Component: Spring Bean]

    A facade onto the mainframe
    banking system.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] - 17 [id=17,shape=rect, label=<E-mail Component
    [Component: Spring Bean]

    Sends e-mails to users.>, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] + subgraph cluster_11 { + margin=25 + label=<
    API Application

    [Container: Java and Spring MVC]> + labelloc=b + color="#444444" + fontcolor="#444444" + fillcolor="#444444" + + 12 [id=12,shape=rect, label=<Sign In Controller
    [Component: Spring MVC Rest Controller]

    Allows users to sign in to the
    Internet Banking System.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] + 13 [id=13,shape=rect, label=<Accounts Summary
    Controller

    [Component: Spring MVC Rest Controller]

    Provides customers with a
    summary of their bank
    accounts.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] + 14 [id=14,shape=rect, label=<Reset Password
    Controller

    [Component: Spring MVC Rest Controller]

    Allows users to reset their
    passwords with a single use
    URL.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] + 15 [id=15,shape=rect, label=<Security Component
    [Component: Spring Bean]

    Provides functionality related
    to signing in, changing
    passwords, etc.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] + 16 [id=16,shape=rect, label=<Mainframe Banking
    System Facade

    [Component: Spring Bean]

    A facade onto the mainframe
    banking system.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] + 17 [id=17,shape=rect, label=<E-mail Component
    [Component: Spring Bean]

    Sends e-mails to users.>, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] + } + + 18 [id=18,shape=cylinder, label=<Database
    [Container: Oracle Database Schema]

    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + 8 [id=8,shape=rect, label=<Single-Page
    Application

    [Container: JavaScript and Angular]

    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + 9 [id=9,shape=rect, label=<Mobile App
    [Container: Xamarin]

    Provides a limited subset of
    the Internet banking
    functionality to customers via
    their mobile device.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] } 8 -> 12 [id=32, label=<Makes API calls to
    [JSON/HTTPS]>, style="dashed", color="#444444", fontcolor="#444444"] @@ -181,7 +191,7 @@ public void test_BigBankPlcExample() throws Exception { 15 -> 18 [id=44, label=<Reads from and writes to
    [SQL/TCP]>, style="dashed", color="#444444", fontcolor="#444444"] 16 -> 4 [id=46, label=<Makes API calls to
    [XML/HTTPS]>, style="dashed", color="#444444", fontcolor="#444444"] 17 -> 5 [id=48, label=<Sends e-mail using>, style="dashed", color="#444444", fontcolor="#444444"] - + }""", diagram.getDefinition()); diagram = diagrams.stream().filter(md -> md.getKey().equals("SignIn")).findFirst().get(); @@ -678,27 +688,37 @@ public void test_GroupsExample() throws Exception { 3 [id=3,shape=rect, label=<C
    [Software System]>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] - subgraph cluster_6 { + subgraph cluster_4 { margin=25 - label=<
    F

    [Container]> + label=<
    D

    [Software System]> labelloc=b color="#444444" fontcolor="#444444" fillcolor="#444444" - subgraph "cluster_group_Group 5" { + subgraph cluster_6 { margin=25 - label=<
    Group 5
    > + label=<
    F

    [Container]> labelloc=b - color="#cccccc" - fontcolor="#cccccc" - fillcolor="#ffffff" - style="dashed" + color="#444444" + fontcolor="#444444" + fillcolor="#444444" - 8 [id=8,shape=rect, label=<H
    [Component]>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] + subgraph "cluster_group_Group 5" { + margin=25 + label=<
    Group 5
    > + labelloc=b + color="#cccccc" + fontcolor="#cccccc" + fillcolor="#ffffff" + style="dashed" + + 8 [id=8,shape=rect, label=<H
    [Component]>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] + } + + 7 [id=7,shape=rect, label=<G
    [Component]>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] } - 7 [id=7,shape=rect, label=<G
    [Component]>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] } 3 -> 7 [id=13, label=<>, style="dashed", color="#444444", fontcolor="#444444"] @@ -881,26 +901,46 @@ public void test_renderComponentDiagramWithExternalComponents() { edge [fontname="Arial"] label=<
    Component View: Software System 1 - Container 1> - subgraph cluster_2 { + subgraph cluster_1 { margin=25 - label=<
    Container 1

    [Container]> + label=<
    Software System 1

    [Software System]> labelloc=b color="#444444" fontcolor="#444444" fillcolor="#444444" - 3 [id=3,shape=rect, label=<Component 1
    [Component]>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] + subgraph cluster_2 { + margin=25 + label=<
    Container 1

    [Container]> + labelloc=b + color="#444444" + fontcolor="#444444" + fillcolor="#444444" + + 3 [id=3,shape=rect, label=<Component 1
    [Component]>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] + } + } - subgraph cluster_5 { + subgraph cluster_4 { margin=25 - label=<
    Container 2

    [Container]> + label=<
    Software System 2

    [Software System]> labelloc=b color="#cccccc" fontcolor="#cccccc" fillcolor="#cccccc" - 6 [id=6,shape=rect, label=<Component 2
    [Component]>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] + subgraph cluster_5 { + margin=25 + label=<
    Container 2

    [Container]> + labelloc=b + color="#cccccc" + fontcolor="#cccccc" + fillcolor="#cccccc" + + 6 [id=6,shape=rect, label=<Component 2
    [Component]>, style=filled, color="#444444", fillcolor="#ffffff", fontcolor="#444444"] + } + } 3 -> 6 [id=7, label=<Uses>, style="dashed", color="#444444", fontcolor="#444444"] diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/MermaidDiagramExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/mermaid/MermaidDiagramExporterTests.java index fe176f043..31097f38b 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/MermaidDiagramExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/MermaidDiagramExporterTests.java @@ -169,23 +169,28 @@ public void test_GroupsExample() throws Exception { 3["
    C
    [Software System]
    "] style 3 fill:#ffffff,stroke:#444444,color:#444444 - subgraph 6 ["F"] - style 6 fill:#ffffff,stroke:#444444,color:#444444 + subgraph 4 ["D"] + style 4 fill:#ffffff,stroke:#444444,color:#444444 - subgraph group1 ["Group 5"] - style group1 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 + subgraph 6 ["F"] + style 6 fill:#ffffff,stroke:#444444,color:#444444 + + subgraph group1 ["Group 5"] + style group1 fill:#ffffff,stroke:#cccccc,color:#cccccc,stroke-dasharray:5 - 8["
    H
    [Component]
    "] - style 8 fill:#ffffff,stroke:#444444,color:#444444 + 8["
    H
    [Component]
    "] + style 8 fill:#ffffff,stroke:#444444,color:#444444 + end + + 7["
    G
    [Component]
    "] + style 7 fill:#ffffff,stroke:#444444,color:#444444 end - 7["
    G
    [Component]
    "] - style 7 fill:#ffffff,stroke:#444444,color:#444444 end 3-. "
    " .->7 3-. "
    " .->8 - + end""", diagram.getDefinition()); } @@ -327,18 +332,28 @@ public void test_renderComponentDiagramWithExternalComponents() { subgraph diagram ["Component View: Software System 1 - Container 1"] style diagram fill:#ffffff,stroke:#ffffff - subgraph 2 ["Container 1"] - style 2 fill:#ffffff,stroke:#444444,color:#444444 + subgraph 1 ["Software System 1"] + style 1 fill:#ffffff,stroke:#444444,color:#444444 + + subgraph 2 ["Container 1"] + style 2 fill:#ffffff,stroke:#444444,color:#444444 + + 3["
    Component 1
    [Component]
    "] + style 3 fill:#ffffff,stroke:#444444,color:#444444 + end - 3["
    Component 1
    [Component]
    "] - style 3 fill:#ffffff,stroke:#444444,color:#444444 end - subgraph 5 ["Container 2"] - style 5 fill:#ffffff,stroke:#444444,color:#444444 + subgraph 4 ["Software System 2"] + style 4 fill:#ffffff,stroke:#444444,color:#444444 + + subgraph 5 ["Container 2"] + style 5 fill:#ffffff,stroke:#444444,color:#444444 + + 6["
    Component 2
    [Component]
    "] + style 6 fill:#ffffff,stroke:#444444,color:#444444 + end - 6["
    Component 2
    [Component]
    "] - style 6 fill:#ffffff,stroke:#444444,color:#444444 end 3-. "
    Uses
    " .->6 diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java index a8d073c18..dcca9468d 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java @@ -147,8 +147,8 @@ public void test_BigBankPlcExample() throws Exception { AddRelTag("Relationship", $textColor="#444444", $lineColor="#444444", $lineStyle = DashedLine()) - AddBoundaryTag("Software System", $bgColor="#ffffff", $borderColor="#0b4884", $fontColor="#0b4884", $shadowing="", $borderStyle="solid") + Person(PersonalBankingCustomer, "Personal Banking Customer", $descr="A customer of the bank, with personal bank accounts.", $tags="Person,Customer", $link="") System(MainframeBankingSystem, "Mainframe Banking System", $descr="Stores all of the core banking information about customers, accounts, transactions, etc.", $tags="Software System,Existing System", $link="") System(EmailSystem, "E-mail System", $descr="The internal Microsoft Exchange e-mail system.", $tags="Software System,Existing System", $link="") @@ -204,21 +204,24 @@ public void test_BigBankPlcExample() throws Exception { AddRelTag("Relationship", $textColor="#444444", $lineColor="#444444", $lineStyle = DashedLine()) - AddBoundaryTag("Container", $bgColor="#ffffff", $borderColor="#2e6295", $fontColor="#2e6295", $shadowing="", $borderStyle="solid") + System(MainframeBankingSystem, "Mainframe Banking System", $descr="Stores all of the core banking information about customers, accounts, transactions, etc.", $tags="Software System,Existing System", $link="") System(EmailSystem, "E-mail System", $descr="The internal Microsoft Exchange e-mail system.", $tags="Software System,Existing System", $link="") - Container(InternetBankingSystem.SinglePageApplication, "Single-Page Application", $techn="JavaScript and Angular", $descr="Provides all of the Internet banking functionality to customers via their web browser.", $tags="Container,Web Browser", $link="") - Container(InternetBankingSystem.MobileApp, "Mobile App", $techn="Xamarin", $descr="Provides a limited subset of the Internet banking functionality to customers via their mobile device.", $tags="Container,Mobile App", $link="") - ContainerDb(InternetBankingSystem.Database, "Database", $techn="Oracle Database Schema", $descr="Stores user registration information, hashed authentication credentials, access logs, etc.", $tags="Container,Database", $link="") - Container_Boundary("InternetBankingSystem.APIApplication_boundary", "API Application", $tags="Container") { - Component(InternetBankingSystem.APIApplication.SignInController, "Sign In Controller", $techn="Spring MVC Rest Controller", $descr="Allows users to sign in to the Internet Banking System.", $tags="Component", $link="") - Component(InternetBankingSystem.APIApplication.AccountsSummaryController, "Accounts Summary Controller", $techn="Spring MVC Rest Controller", $descr="Provides customers with a summary of their bank accounts.", $tags="Component", $link="") - Component(InternetBankingSystem.APIApplication.ResetPasswordController, "Reset Password Controller", $techn="Spring MVC Rest Controller", $descr="Allows users to reset their passwords with a single use URL.", $tags="Component", $link="") - Component(InternetBankingSystem.APIApplication.SecurityComponent, "Security Component", $techn="Spring Bean", $descr="Provides functionality related to signing in, changing passwords, etc.", $tags="Component", $link="") - Component(InternetBankingSystem.APIApplication.MainframeBankingSystemFacade, "Mainframe Banking System Facade", $techn="Spring Bean", $descr="A facade onto the mainframe banking system.", $tags="Component", $link="") - Component(InternetBankingSystem.APIApplication.EmailComponent, "E-mail Component", $techn="Spring Bean", $descr="Sends e-mails to users.", $tags="Component", $link="") + System_Boundary("InternetBankingSystem_boundary", "Internet Banking System", $tags="Software System") { + Container_Boundary("InternetBankingSystem.APIApplication_boundary", "API Application", $tags="Container") { + Component(InternetBankingSystem.APIApplication.SignInController, "Sign In Controller", $techn="Spring MVC Rest Controller", $descr="Allows users to sign in to the Internet Banking System.", $tags="Component", $link="") + Component(InternetBankingSystem.APIApplication.AccountsSummaryController, "Accounts Summary Controller", $techn="Spring MVC Rest Controller", $descr="Provides customers with a summary of their bank accounts.", $tags="Component", $link="") + Component(InternetBankingSystem.APIApplication.ResetPasswordController, "Reset Password Controller", $techn="Spring MVC Rest Controller", $descr="Allows users to reset their passwords with a single use URL.", $tags="Component", $link="") + Component(InternetBankingSystem.APIApplication.SecurityComponent, "Security Component", $techn="Spring Bean", $descr="Provides functionality related to signing in, changing passwords, etc.", $tags="Component", $link="") + Component(InternetBankingSystem.APIApplication.MainframeBankingSystemFacade, "Mainframe Banking System Facade", $techn="Spring Bean", $descr="A facade onto the mainframe banking system.", $tags="Component", $link="") + Component(InternetBankingSystem.APIApplication.EmailComponent, "E-mail Component", $techn="Spring Bean", $descr="Sends e-mails to users.", $tags="Component", $link="") + } + + ContainerDb(InternetBankingSystem.Database, "Database", $techn="Oracle Database Schema", $descr="Stores user registration information, hashed authentication credentials, access logs, etc.", $tags="Container,Database", $link="") + Container(InternetBankingSystem.SinglePageApplication, "Single-Page Application", $techn="JavaScript and Angular", $descr="Provides all of the Internet banking functionality to customers via their web browser.", $tags="Container,Web Browser", $link="") + Container(InternetBankingSystem.MobileApp, "Mobile App", $techn="Xamarin", $descr="Provides a limited subset of the Internet banking functionality to customers via their mobile device.", $tags="Container,Mobile App", $link="") } Rel(InternetBankingSystem.SinglePageApplication, InternetBankingSystem.APIApplication.SignInController, "Makes API calls to", $techn="JSON/HTTPS", $tags="Relationship", $link="") @@ -265,8 +268,8 @@ public void test_BigBankPlcExample() throws Exception { AddRelTag("Relationship", $textColor="#444444", $lineColor="#444444", $lineStyle = DashedLine()) - AddBoundaryTag("Container", $bgColor="#ffffff", $borderColor="#2e6295", $fontColor="#2e6295", $shadowing="", $borderStyle="solid") + Container_Boundary("InternetBankingSystem.APIApplication_boundary", "API Application", $tags="Container") { Component(InternetBankingSystem.APIApplication.SignInController, "Sign In Controller", $techn="Spring MVC Rest Controller", $descr="Allows users to sign in to the Internet Banking System.", $tags="Component", $link="") Component(InternetBankingSystem.APIApplication.SecurityComponent, "Security Component", $techn="Spring Bean", $descr="Provides functionality related to signing in, changing passwords, etc.", $tags="Component", $link="") @@ -730,13 +733,16 @@ public void test_GroupsExample() throws Exception { System(C, "C", $descr="", $tags="", $link="") - Container_Boundary("D.F_boundary", "F", $tags="") { - AddBoundaryTag("Group 5", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") - Boundary(group_1, "Group 5", $tags="Group 5") { - Component(D.F.H, "H", $techn="", $descr="", $tags="", $link="") + System_Boundary("D_boundary", "D", $tags="") { + Container_Boundary("D.F_boundary", "F", $tags="") { + AddBoundaryTag("Group 5", $borderColor="#cccccc", $fontColor="#cccccc", $borderStyle="dashed") + Boundary(group_1, "Group 5", $tags="Group 5") { + Component(D.F.H, "H", $techn="", $descr="", $tags="", $link="") + } + + Component(D.F.G, "G", $techn="", $descr="", $tags="", $link="") } - Component(D.F.G, "G", $techn="", $descr="", $tags="", $link="") } Rel(C, D.F.G, "", $techn="", $tags="", $link="") @@ -1057,12 +1063,18 @@ public void test_renderComponentDiagramWithExternalComponents() { !include !include - Container_Boundary("SoftwareSystem1.Container1_boundary", "Container 1", $tags="") { - Component(SoftwareSystem1.Container1.Component1, "Component 1", $techn="", $descr="", $tags="", $link="") + System_Boundary("SoftwareSystem1_boundary", "Software System 1", $tags="") { + Container_Boundary("SoftwareSystem1.Container1_boundary", "Container 1", $tags="") { + Component(SoftwareSystem1.Container1.Component1, "Component 1", $techn="", $descr="", $tags="", $link="") + } + } - Container_Boundary("SoftwareSystem2.Container2_boundary", "Container 2", $tags="") { - Component(SoftwareSystem2.Container2.Component2, "Component 2", $techn="", $descr="", $tags="", $link="") + System_Boundary("SoftwareSystem2_boundary", "Software System 2", $tags="") { + Container_Boundary("SoftwareSystem2.Container2_boundary", "Container 2", $tags="") { + Component(SoftwareSystem2.Container2.Component2, "Component 2", $techn="", $descr="", $tags="", $link="") + } + } Rel(SoftwareSystem1.Container1.Component1, SoftwareSystem2.Container2.Component2, "Uses", $techn="", $tags="", $link="") @@ -1538,11 +1550,14 @@ public void test_renderComponentShapes() throws Exception { !include !include - Container_Boundary("SoftwareSystem.Container_boundary", "Container", $tags="") { - Component(SoftwareSystem.Container.DefaultComponent, "Default Component", $techn="", $descr="", $tags="", $link="") - ComponentDb(SoftwareSystem.Container.CylinderComponent, "Cylinder Component", $techn="", $descr="", $tags="", $link="") - ComponentQueue(SoftwareSystem.Container.PipeComponent, "Pipe Component", $techn="", $descr="", $tags="", $link="") - Component(SoftwareSystem.Container.RobotComponent, "Robot Component", $techn="", $descr="", $tags="", $link="") + System_Boundary("SoftwareSystem_boundary", "Software System", $tags="") { + Container_Boundary("SoftwareSystem.Container_boundary", "Container", $tags="") { + Component(SoftwareSystem.Container.DefaultComponent, "Default Component", $techn="", $descr="", $tags="", $link="") + ComponentDb(SoftwareSystem.Container.CylinderComponent, "Cylinder Component", $techn="", $descr="", $tags="", $link="") + ComponentQueue(SoftwareSystem.Container.PipeComponent, "Pipe Component", $techn="", $descr="", $tags="", $link="") + Component(SoftwareSystem.Container.RobotComponent, "Robot Component", $techn="", $descr="", $tags="", $link="") + } + } SHOW_LEGEND(true) @@ -1647,8 +1662,11 @@ public void componentWithoutTechnology() { !include !include - Container_Boundary("Name.Name_boundary", "Name", $tags="") { - Component(Name.Name.Name, "Name", $techn="", $descr="Description", $tags="", $link="") + System_Boundary("Name_boundary", "Name", $tags="") { + Container_Boundary("Name.Name_boundary", "Name", $tags="") { + Component(Name.Name.Name, "Name", $techn="", $descr="Description", $tags="", $link="") + } + } SHOW_LEGEND(true) diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java index 116cdf144..0d55ea296 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java @@ -407,11 +407,25 @@ public void componentView_NoStyling_Light() { HorizontalAlignment: center; Shadowing: 0; } + // Software System + .Boundary-U29mdHdhcmUgU3lzdGVt { + BackgroundColor: #ffffff; + LineColor: #444444; + LineStyle: 0; + LineThickness: 2; + FontColor: #444444; + FontSize: 24; + HorizontalAlignment: center; + Shadowing: 0; + } - rectangle "Container\\n[Container: Technology]" <> { - rectangle "==Component 1\\n[Component: Technology]\\n\\nDescription" <> as SoftwareSystem.Container.Component1 - rectangle "==Component 2\\n[Component: Technology]\\n\\nDescription" <> as SoftwareSystem.Container.Component2 + rectangle "Software System\\n[Software System]" <> { + rectangle "Container\\n[Container: Technology]" <> { + rectangle "==Component 1\\n[Component: Technology]\\n\\nDescription" <> as SoftwareSystem.Container.Component1 + rectangle "==Component 2\\n[Component: Technology]\\n\\nDescription" <> as SoftwareSystem.Container.Component2 + } + } SoftwareSystem.Container.Component1 --> SoftwareSystem.Container.Component2 <> : "Description\\n[Technology]" @@ -954,6 +968,17 @@ public void groups() throws Exception { FontColor: #444444; FontSize: 24; } + // D + .Boundary-RA== { + BackgroundColor: #ffffff; + LineColor: #444444; + LineStyle: 0; + LineThickness: 2; + FontColor: #444444; + FontSize: 24; + HorizontalAlignment: center; + Shadowing: 0; + } // F .Boundary-Rg== { BackgroundColor: #ffffff; @@ -980,12 +1005,15 @@ public void groups() throws Exception { rectangle "==C\\n[Software System]" <> as C - rectangle "F\\n[Container]" <> { - rectangle "Group 5" <> as groupR3JvdXAgNQ== { - rectangle "==H\\n[Component]" <> as D.F.H + rectangle "D\\n[Software System]" <> { + rectangle "F\\n[Container]" <> { + rectangle "Group 5" <> as groupR3JvdXAgNQ== { + rectangle "==H\\n[Component]" <> as D.F.H + } + + rectangle "==G\\n[Component]" <> as D.F.G } - rectangle "==G\\n[Component]" <> as D.F.G } C --> D.F.G <> : "" @@ -1216,25 +1244,20 @@ public void containerDiagramWithExternalContainers() { } @Test - public void componentDiagramWithExternalComponents() { + public void componentDiagram() { Workspace workspace = new Workspace("Name"); SoftwareSystem softwareSystem1 = workspace.getModel().addSoftwareSystem("Software System 1"); Container container1 = softwareSystem1.addContainer("Container 1"); Component component1 = container1.addComponent("Component 1"); Component component2 = container1.addComponent("Component 2"); - - SoftwareSystem softwareSystem2 = workspace.getModel().addSoftwareSystem("Software System 2"); - Container container2 = softwareSystem2.addContainer("Container 2"); - Component component3 = container2.addComponent("Component 3"); + Container container2 = softwareSystem1.addContainer("Container 2"); component1.uses(component2, "Uses"); - component2.uses(component3, "Uses"); + component2.uses(container2, "Uses"); ComponentView componentView = workspace.getViews().createComponentView(container1, "Components", ""); - componentView.add(component1); - componentView.add(component2); - componentView.add(component3); + componentView.addDefaultElements(); Diagram diagram = new StructurizrPlantUMLExporter().export(componentView); assertEquals(""" @@ -1281,8 +1304,8 @@ public void componentDiagramWithExternalComponents() { HorizontalAlignment: center; Shadowing: 0; } - // Container 2 - .Boundary-Q29udGFpbmVyIDI= { + // Software System 1 + .Boundary-U29mdHdhcmUgU3lzdGVtIDE= { BackgroundColor: #ffffff; LineColor: #444444; LineStyle: 0; @@ -1294,23 +1317,23 @@ public void componentDiagramWithExternalComponents() { } - rectangle "Container 1\\n[Container]" <> { - rectangle "==Component 1\\n[Component]" <> as SoftwareSystem1.Container1.Component1 - rectangle "==Component 2\\n[Component]" <> as SoftwareSystem1.Container1.Component2 - } + rectangle "Software System 1\\n[Software System]" <> { + rectangle "Container 1\\n[Container]" <> { + rectangle "==Component 1\\n[Component]" <> as SoftwareSystem1.Container1.Component1 + rectangle "==Component 2\\n[Component]" <> as SoftwareSystem1.Container1.Component2 + } - rectangle "Container 2\\n[Container]" <> { - rectangle "==Component 3\\n[Component]" <> as SoftwareSystem2.Container2.Component3 + rectangle "==Container 2\\n[Container]" <> as SoftwareSystem1.Container2 } SoftwareSystem1.Container1.Component1 --> SoftwareSystem1.Container1.Component2 <> : "Uses" - SoftwareSystem1.Container1.Component2 --> SoftwareSystem2.Container2.Component3 <> : "Uses" + SoftwareSystem1.Container1.Component2 --> SoftwareSystem1.Container2 <> : "Uses" @enduml""", diagram.getDefinition()); } @Test - public void componentDiagramWithExternalComponentsAndSoftwareSystemBoundariesIncluded() { + public void componentDiagramWithExternalComponents() { Workspace workspace = new Workspace("Name"); SoftwareSystem softwareSystem1 = workspace.getModel().addSoftwareSystem("Software System 1"); @@ -1321,15 +1344,17 @@ public void componentDiagramWithExternalComponentsAndSoftwareSystemBoundariesInc SoftwareSystem softwareSystem2 = workspace.getModel().addSoftwareSystem("Software System 2"); Container container2 = softwareSystem2.addContainer("Container 2"); Component component3 = container2.addComponent("Component 3"); + Container container4 = softwareSystem2.addContainer("Container 4"); component1.uses(component2, "Uses"); component2.uses(component3, "Uses"); + component3.uses(container4, "Uses"); ComponentView componentView = workspace.getViews().createComponentView(container1, "Components", ""); componentView.add(component1); componentView.add(component2); componentView.add(component3); - componentView.addProperty("structurizr.softwareSystemBoundaries", "true"); + componentView.add(container4); Diagram diagram = new StructurizrPlantUMLExporter().export(componentView); assertEquals(""" @@ -1412,22 +1437,24 @@ public void componentDiagramWithExternalComponentsAndSoftwareSystemBoundariesInc rectangle "Software System 1\\n[Software System]" <> { - rectangle "Container 1\\n[Container]" <> { - rectangle "==Component 1\\n[Component]" <> as SoftwareSystem1.Container1.Component1 - rectangle "==Component 2\\n[Component]" <> as SoftwareSystem1.Container1.Component2 - } - + rectangle "Container 1\\n[Container]" <> { + rectangle "==Component 1\\n[Component]" <> as SoftwareSystem1.Container1.Component1 + rectangle "==Component 2\\n[Component]" <> as SoftwareSystem1.Container1.Component2 } - rectangle "Software System 2\\n[Software System]" <> { - rectangle "Container 2\\n[Container]" <> { - rectangle "==Component 3\\n[Component]" <> as SoftwareSystem2.Container2.Component3 - } + } + rectangle "Software System 2\\n[Software System]" <> { + rectangle "Container 2\\n[Container]" <> { + rectangle "==Component 3\\n[Component]" <> as SoftwareSystem2.Container2.Component3 } - SoftwareSystem1.Container1.Component1 --> SoftwareSystem1.Container1.Component2 <> : "Uses" + rectangle "==Container 4\\n[Container]" <> as SoftwareSystem2.Container4 + } + SoftwareSystem1.Container1.Component2 --> SoftwareSystem2.Container2.Component3 <> : "Uses" + SoftwareSystem2.Container2.Component3 --> SoftwareSystem2.Container4 <> : "Uses" + SoftwareSystem1.Container1.Component1 --> SoftwareSystem1.Container1.Component2 <> : "Uses" @enduml""", diagram.getDefinition()); } From 9fd9660dc61c9b9116c6b1a9117d3bad8c0681f6 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 26 Sep 2025 14:48:33 +0100 Subject: [PATCH 691/717] Fixes an issue where containers could be rendered outside of their software system boundary on container scoped dynamic views (collaboration style). --- .../export/AbstractDiagramExporter.java | 47 ++++-- .../export/dot/DOTDiagramExporterTests.java | 26 +++- .../C4PlantUMLDiagramExporterTests.java | 11 +- ...ructurizrPlantUMLDiagramExporterTests.java | 137 ++++-------------- 4 files changed, 89 insertions(+), 132 deletions(-) diff --git a/structurizr-export/src/main/java/com/structurizr/export/AbstractDiagramExporter.java b/structurizr-export/src/main/java/com/structurizr/export/AbstractDiagramExporter.java index 58df90d5f..4677e7501 100644 --- a/structurizr-export/src/main/java/com/structurizr/export/AbstractDiagramExporter.java +++ b/structurizr-export/src/main/java/com/structurizr/export/AbstractDiagramExporter.java @@ -308,7 +308,6 @@ public Diagram export(ComponentView view, Integer animationStep) { } List boundaryContainers = getBoundaryContainers(view); - List containers = getContainers(view); Set boundarySoftwareSystems = boundaryContainers.stream().map(Container::getSoftwareSystem).collect(Collectors.toCollection(LinkedHashSet::new)); for (SoftwareSystem softwareSystem : boundarySoftwareSystems) { @@ -325,7 +324,7 @@ public Diagram export(ComponentView view, Integer animationStep) { } } - for (Container container : containers) { + for (Container container : getContainers(view)) { if (container.getSoftwareSystem() == softwareSystem) { writeElement(view, container, writer); } @@ -418,18 +417,37 @@ public Diagram export(DynamicView view, String order) { } } else if (element instanceof Container) { // dynamic view with container scope - boolean includeSoftwareSystemBoundaries = "true".equals(view.getProperties().getOrDefault("structurizr.softwareSystemBoundaries", "false")); + List customElements = getCustomElements(view); + for (CustomElement customElement : customElements) { + writeElement(view, customElement, writer); + } + if (!customElements.isEmpty()) { + writer.writeLine(); + } + + List people = getPeople(view); + for (Person person : people) { + writeElement(view, person, writer); + } + if (!people.isEmpty()) { + writer.writeLine(); + } - List containers = getBoundaryContainers(view); - Set softwareSystems = containers.stream().map(Container::getSoftwareSystem).collect(Collectors.toCollection(LinkedHashSet::new)); + List softwareSystems = getSoftwareSystems(view); for (SoftwareSystem softwareSystem : softwareSystems) { + writeElement(view, softwareSystem, writer); + } + if (!softwareSystems.isEmpty()) { + writer.writeLine(); + } - if (includeSoftwareSystemBoundaries) { - startSoftwareSystemBoundary(view, softwareSystem, writer); - writer.indent(); - } + List boundaryContainers = getBoundaryContainers(view); + Set boundarySoftwareSystems = boundaryContainers.stream().map(Container::getSoftwareSystem).collect(Collectors.toCollection(LinkedHashSet::new)); + for (SoftwareSystem softwareSystem : boundarySoftwareSystems) { + + startSoftwareSystemBoundary(view, softwareSystem, writer); - for (Container container : containers) { + for (Container container : boundaryContainers) { if (container.getSoftwareSystem() == softwareSystem) { startContainerBoundary(view, container, writer); @@ -440,10 +458,13 @@ public Diagram export(DynamicView view, String order) { } } - if (includeSoftwareSystemBoundaries) { - endSoftwareSystemBoundary(view, writer); - writer.outdent(); + for (Container container : getContainers(view)) { + if (container.getSoftwareSystem() == softwareSystem) { + writeElement(view, container, writer); + } } + + endSoftwareSystemBoundary(view, writer); } for (ElementView elementView : view.getElements()) { diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/DOTDiagramExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/dot/DOTDiagramExporterTests.java index 35cebec48..81da0720a 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/dot/DOTDiagramExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/dot/DOTDiagramExporterTests.java @@ -203,16 +203,28 @@ public void test_BigBankPlcExample() throws Exception { edge [fontname="Arial"] label=<
    Dynamic View: Internet Banking System - API Application
    Summarises how the sign in feature works in the single-page application.> - subgraph cluster_11 { + subgraph cluster_7 { margin=25 - label=<
    API Application

    [Container: Java and Spring MVC]> + label=<
    Internet Banking System

    [Software System]> labelloc=b - color="#444444" - fontcolor="#444444" - fillcolor="#444444" + color="#cccccc" + fontcolor="#cccccc" + fillcolor="#cccccc" + + subgraph cluster_11 { + margin=25 + label=<
    API Application

    [Container: Java and Spring MVC]> + labelloc=b + color="#444444" + fontcolor="#444444" + fillcolor="#444444" - 12 [id=12,shape=rect, label=<Sign In Controller
    [Component: Spring MVC Rest Controller]

    Allows users to sign in to the
    Internet Banking System.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] - 15 [id=15,shape=rect, label=<Security Component
    [Component: Spring Bean]

    Provides functionality related
    to signing in, changing
    passwords, etc.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] + 12 [id=12,shape=rect, label=<Sign In Controller
    [Component: Spring MVC Rest Controller]

    Allows users to sign in to the
    Internet Banking System.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] + 15 [id=15,shape=rect, label=<Security Component
    [Component: Spring Bean]

    Provides functionality related
    to signing in, changing
    passwords, etc.
    >, style=filled, color="#5d82a8", fillcolor="#85bbf0", fontcolor="#000000"] + } + + 18 [id=18,shape=cylinder, label=<Database
    [Container: Oracle Database Schema]

    Stores user registration
    information, hashed
    authentication credentials,
    access logs, etc.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] + 8 [id=8,shape=rect, label=<Single-Page
    Application

    [Container: JavaScript and Angular]

    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] } 8 [id=8,shape=rect, label=<Single-Page
    Application

    [Container: JavaScript and Angular]

    Provides all of the Internet
    banking functionality to
    customers via their web
    browser.
    >, style=filled, color="#2e6295", fillcolor="#438dd5", fontcolor="#ffffff"] diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java index dcca9468d..f35eb809e 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java @@ -270,9 +270,14 @@ public void test_BigBankPlcExample() throws Exception { AddBoundaryTag("Container", $bgColor="#ffffff", $borderColor="#2e6295", $fontColor="#2e6295", $shadowing="", $borderStyle="solid") - Container_Boundary("InternetBankingSystem.APIApplication_boundary", "API Application", $tags="Container") { - Component(InternetBankingSystem.APIApplication.SignInController, "Sign In Controller", $techn="Spring MVC Rest Controller", $descr="Allows users to sign in to the Internet Banking System.", $tags="Component", $link="") - Component(InternetBankingSystem.APIApplication.SecurityComponent, "Security Component", $techn="Spring Bean", $descr="Provides functionality related to signing in, changing passwords, etc.", $tags="Component", $link="") + System_Boundary("InternetBankingSystem_boundary", "Internet Banking System", $tags="Software System") { + Container_Boundary("InternetBankingSystem.APIApplication_boundary", "API Application", $tags="Container") { + Component(InternetBankingSystem.APIApplication.SignInController, "Sign In Controller", $techn="Spring MVC Rest Controller", $descr="Allows users to sign in to the Internet Banking System.", $tags="Component", $link="") + Component(InternetBankingSystem.APIApplication.SecurityComponent, "Security Component", $techn="Spring Bean", $descr="Provides functionality related to signing in, changing passwords, etc.", $tags="Component", $link="") + } + + ContainerDb(InternetBankingSystem.Database, "Database", $techn="Oracle Database Schema", $descr="Stores user registration information, hashed authentication credentials, access logs, etc.", $tags="Container,Database", $link="") + Container(InternetBankingSystem.SinglePageApplication, "Single-Page Application", $techn="JavaScript and Angular", $descr="Provides all of the Internet banking functionality to customers via their web browser.", $tags="Container,Web Browser", $link="") } Container(InternetBankingSystem.SinglePageApplication, "Single-Page Application", $techn="JavaScript and Angular", $descr="Provides all of the Internet banking functionality to customers via their web browser.", $tags="Container,Web Browser", $link="") diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java index 0d55ea296..880320ba9 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java @@ -1555,107 +1555,16 @@ public void dynamicView_ExternalComponents() { SoftwareSystem softwareSystem2 = workspace.getModel().addSoftwareSystem("Software System 2"); Container container2 = softwareSystem2.addContainer("Container 2"); Component component3 = container2.addComponent("Component 3"); + Container container4 = softwareSystem2.addContainer("Container 4"); component1.uses(component2, "Uses"); component2.uses(component3, "Uses"); + component3.uses(container4, "Uses"); DynamicView dynamicView = workspace.getViews().createDynamicView(container1, "Dynamic", ""); dynamicView.add(component1, component2); dynamicView.add(component2, component3); - - Diagram diagram = new StructurizrPlantUMLExporter().export(dynamicView); - assertEquals(""" - @startuml - title Dynamic View: Software System 1 - Container 1 - - set separator none - top to bottom direction - hide stereotype - - - - rectangle "Container 1\\n[Container]" <> { - rectangle "==Component 1\\n[Component]" <> as SoftwareSystem1.Container1.Component1 - rectangle "==Component 2\\n[Component]" <> as SoftwareSystem1.Container1.Component2 - } - - rectangle "Container 2\\n[Container]" <> { - rectangle "==Component 3\\n[Component]" <> as SoftwareSystem2.Container2.Component3 - } - - SoftwareSystem1.Container1.Component1 --> SoftwareSystem1.Container1.Component2 <> : "1: Uses" - SoftwareSystem1.Container1.Component2 --> SoftwareSystem2.Container2.Component3 <> : "2: Uses" - - @enduml""", diagram.getDefinition()); - } - - @Test - public void dynamicView_ExternalComponentsAndSoftwareSystemBoundariesIncluded() { - Workspace workspace = new Workspace("Name"); - - SoftwareSystem softwareSystem1 = workspace.getModel().addSoftwareSystem("Software System 1"); - Container container1 = softwareSystem1.addContainer("Container 1"); - Component component1 = container1.addComponent("Component 1"); - Component component2 = container1.addComponent("Component 2"); - - SoftwareSystem softwareSystem2 = workspace.getModel().addSoftwareSystem("Software System 2"); - Container container2 = softwareSystem2.addContainer("Container 2"); - Component component3 = container2.addComponent("Component 3"); - - component1.uses(component2, "Uses"); - component2.uses(component3, "Uses"); - - DynamicView dynamicView = workspace.getViews().createDynamicView(container1, "Dynamic", ""); - dynamicView.add(component1, component2); - dynamicView.add(component2, component3); - dynamicView.addProperty("structurizr.softwareSystemBoundaries", "true"); + dynamicView.add(component3, container4); Diagram diagram = new StructurizrPlantUMLExporter().export(dynamicView); assertEquals(""" @@ -1738,22 +1647,26 @@ public void dynamicView_ExternalComponentsAndSoftwareSystemBoundariesIncluded() rectangle "Software System 1\\n[Software System]" <> { - rectangle "Container 1\\n[Container]" <> { - rectangle "==Component 1\\n[Component]" <> as SoftwareSystem1.Container1.Component1 - rectangle "==Component 2\\n[Component]" <> as SoftwareSystem1.Container1.Component2 - } - + rectangle "Container 1\\n[Container]" <> { + rectangle "==Component 1\\n[Component]" <> as SoftwareSystem1.Container1.Component1 + rectangle "==Component 2\\n[Component]" <> as SoftwareSystem1.Container1.Component2 } - rectangle "Software System 2\\n[Software System]" <> { - rectangle "Container 2\\n[Container]" <> { - rectangle "==Component 3\\n[Component]" <> as SoftwareSystem2.Container2.Component3 - } + } + rectangle "Software System 2\\n[Software System]" <> { + rectangle "Container 2\\n[Container]" <> { + rectangle "==Component 3\\n[Component]" <> as SoftwareSystem2.Container2.Component3 } + rectangle "==Container 4\\n[Container]" <> as SoftwareSystem2.Container4 + } + + rectangle "==Container 4\\n[Container]" <> as SoftwareSystem2.Container4 + SoftwareSystem1.Container1.Component1 --> SoftwareSystem1.Container1.Component2 <> : "1: Uses" SoftwareSystem1.Container1.Component2 --> SoftwareSystem2.Container2.Component3 <> : "2: Uses" + SoftwareSystem2.Container2.Component3 --> SoftwareSystem2.Container4 <> : "3: Uses" @enduml""", diagram.getDefinition()); } @@ -2261,16 +2174,22 @@ public void dynamicView_ContainerScopedWithGroups() { } - rectangle "A\\n[Container]" <> { - rectangle "Group 1" <> as groupR3JvdXAgMQ== { - rectangle "==A\\n[Component]" <> as A.A.A + rectangle "A\\n[Software System]" <> { + rectangle "A\\n[Container]" <> { + rectangle "Group 1" <> as groupR3JvdXAgMQ== { + rectangle "==A\\n[Component]" <> as A.A.A + } + } } - rectangle "B\\n[Container]" <> { - rectangle "Group 2" <> as groupR3JvdXAgMg== { - rectangle "==B\\n[Component]" <> as B.B.B + rectangle "B\\n[Software System]" <> { + rectangle "B\\n[Container]" <> { + rectangle "Group 2" <> as groupR3JvdXAgMg== { + rectangle "==B\\n[Component]" <> as B.B.B + } + } } From 6e9f8e727d99f8c1a83fa178f166d6b978eb077e Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 26 Sep 2025 15:47:31 +0100 Subject: [PATCH 692/717] Adds back the original Big Bank example tests (and some special handling for sequence diagrams). --- .../export/AbstractDiagramExporter.java | 2 +- .../plantuml/StructurizrPlantUMLExporter.java | 97 +- ...ructurizrPlantUMLDiagramExporterTests.java | 1091 ++++++++++++++++- 3 files changed, 1181 insertions(+), 9 deletions(-) diff --git a/structurizr-export/src/main/java/com/structurizr/export/AbstractDiagramExporter.java b/structurizr-export/src/main/java/com/structurizr/export/AbstractDiagramExporter.java index 4677e7501..f982fd5f1 100644 --- a/structurizr-export/src/main/java/com/structurizr/export/AbstractDiagramExporter.java +++ b/structurizr-export/src/main/java/com/structurizr/export/AbstractDiagramExporter.java @@ -22,7 +22,7 @@ public AbstractDiagramExporter() { } public AbstractDiagramExporter(ColorScheme colorScheme) { - this.colorScheme = colorScheme; + this.colorScheme = colorScheme != null ? colorScheme : ColorScheme.Light; } /** diff --git a/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java b/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java index c71534276..531ce9a6a 100644 --- a/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java +++ b/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java @@ -267,7 +267,7 @@ protected void startDeploymentNodeBoundary(DeploymentView view, DeploymentNode d format( "rectangle \"%s\\n%s%s\" <<%s>> as %s%s {", deploymentNode.getName() + (!"1".equals(deploymentNode.getInstances()) ? " (x" + deploymentNode.getInstances() + ")" : ""), - calculateMetadataFontSize(findBoundaryStyle(view, deploymentNode).getFontSize()), + calculateMetadataFontSize(findElementStyle(view, deploymentNode).getFontSize()), typeOf(view, deploymentNode, true), icon, classSelectorFor(elementStyle), @@ -324,6 +324,15 @@ public Diagram export(DynamicView view) { @Override protected void writeElement(ModelView view, Element element, IndentingWriter writer) { ElementStyle elementStyle = findElementStyle(view, element); + String sequenceDiagramShape = plantumlSequenceType(view, element); + + if (renderAsSequenceDiagram(view)) { + // actor and database require special treatment because the label sits outside the shape + if ("actor".equals(sequenceDiagramShape) || "database".equals(sequenceDiagramShape)) { + elementStyle.color(elementStyle.getStroke()); + } + } + PlantUMLElementStyle plantUMLElementStyle = new PlantUMLElementStyle( elementStyle.getTag(), elementStyle.getShape(), @@ -342,14 +351,14 @@ protected void writeElement(ModelView view, Element element, IndentingWriter wri int metadataFontSize = calculateMetadataFontSize(elementStyle.getFontSize()); if (renderAsSequenceDiagram(view)) { - writer.writeLine(String.format("%s \"%s\\n%s\" as %s <<%s>> %s", - plantumlSequenceType(view, element), + writer.writeLine(String.format("%s \"%s\\n%s\" as %s <<%s>>", + sequenceDiagramShape, element.getName(), metadataFontSize, typeOf(view, element, true), idOf(element), - plantUMLElementStyle.getClassSelector(), - elementStyle.getBackground())); + plantUMLElementStyle.getClassSelector() + )); } else { String shape = plantUMLShapeOf(view, element); String name = element.getName(); @@ -556,7 +565,83 @@ private String classSelectorForBoundary(Element element) { } private ElementStyle findBoundaryStyle(ModelView view, Element element) { - return findElementStyle(view, element); + String background = colorScheme == ColorScheme.Dark ? Styles.DEFAULT_BACKGROUND_DARK : Styles.DEFAULT_BACKGROUND_LIGHT; + String stroke = colorScheme == ColorScheme.Dark ? Styles.DEFAULT_COLOR_DARK : Styles.DEFAULT_COLOR_LIGHT; + int strokeWidth = DEFAULT_STROKE_WIDTH; + Border border = Border.Dotted; + String color = colorScheme == ColorScheme.Dark ? Styles.DEFAULT_COLOR_DARK : Styles.DEFAULT_COLOR_LIGHT; + String icon = ""; + int fontSize = DEFAULT_FONT_SIZE; + + String type = element instanceof SoftwareSystem ? "SoftwareSystem" : "Container"; + + ElementStyle style = new ElementStyle(""); + ElementStyle elementStyleForBoundary = findElementStyle(view, "Boundary:" + type); + ElementStyle elementStyleForAllBoundaries = findElementStyle(view, "Boundary"); + ElementStyle elementStyleForElement = findElementStyle(view, element); + + if (elementStyleForBoundary != null && !StringUtils.isNullOrEmpty(elementStyleForBoundary.getBackground())) { + background = elementStyleForBoundary.getBackground(); + } else if (elementStyleForAllBoundaries != null && !StringUtils.isNullOrEmpty(elementStyleForAllBoundaries.getBackground())) { + background = elementStyleForAllBoundaries.getBackground(); + } + style.setBackground(background); + + if (elementStyleForBoundary != null && !StringUtils.isNullOrEmpty(elementStyleForBoundary.getStroke())) { + stroke = elementStyleForBoundary.getStroke(); + } else if (elementStyleForAllBoundaries != null && !StringUtils.isNullOrEmpty(elementStyleForAllBoundaries.getStroke())) { + stroke = elementStyleForAllBoundaries.getStroke(); + } else if (!StringUtils.isNullOrEmpty(elementStyleForElement.getStroke())) { + stroke = elementStyleForElement.getStroke(); + } + style.setStroke(stroke); + + if (elementStyleForBoundary != null && elementStyleForBoundary.getStrokeWidth() != null) { + strokeWidth = elementStyleForBoundary.getStrokeWidth(); + } else if (elementStyleForAllBoundaries != null && elementStyleForAllBoundaries.getStrokeWidth() != null) { + strokeWidth = elementStyleForAllBoundaries.getStrokeWidth(); + } else if (elementStyleForElement.getStrokeWidth() != null) { + strokeWidth = elementStyleForElement.getStrokeWidth(); + } + style.setStrokeWidth(strokeWidth); + + if (elementStyleForBoundary != null && !StringUtils.isNullOrEmpty(elementStyleForBoundary.getColor())) { + color = elementStyleForBoundary.getColor(); + } else if (elementStyleForAllBoundaries != null && !StringUtils.isNullOrEmpty(elementStyleForAllBoundaries.getColor())) { + color = elementStyleForAllBoundaries.getColor(); + } else if (!StringUtils.isNullOrEmpty(elementStyleForElement.getColor())) { + color = elementStyleForElement.getColor(); + } + style.setColor(color); + + if (elementStyleForBoundary != null && elementStyleForBoundary.getBorder() != null) { + border = elementStyleForBoundary.getBorder(); + } else if (elementStyleForAllBoundaries != null && elementStyleForAllBoundaries.getBorder() != null) { + border = elementStyleForAllBoundaries.getBorder(); + } else if (elementStyleForElement.getBorder() != null) { + border = elementStyleForElement.getBorder(); + } + style.setBorder(border); + + if (elementStyleForBoundary != null && isSupportedIcon(elementStyleForBoundary.getIcon())) { + icon = elementStyleForBoundary.getIcon(); + } else if (elementStyleForAllBoundaries != null && isSupportedIcon(elementStyleForAllBoundaries.getIcon())) { + icon = elementStyleForAllBoundaries.getIcon(); + } else if (isSupportedIcon(elementStyleForElement.getIcon())) { + icon = elementStyleForElement.getIcon(); + } + style.setIcon(icon); + + if (elementStyleForBoundary != null && elementStyleForBoundary.getFontSize() != null) { + fontSize = elementStyleForBoundary.getFontSize(); + } else if (elementStyleForAllBoundaries != null && elementStyleForAllBoundaries.getFontSize() != null) { + fontSize = elementStyleForAllBoundaries.getFontSize(); + } else if (elementStyleForElement.getFontSize() != null) { + fontSize = elementStyleForElement.getFontSize(); + } + style.setFontSize(fontSize); + + return style; } private String classSelectorForGroup(String group) { diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java index 880320ba9..62a37a4ed 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java @@ -18,6 +18,959 @@ public class StructurizrPlantUMLDiagramExporterTests extends AbstractExporterTests { + @Test + public void test_BigBankPlcExample() throws Exception { + Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/big-bank-plc.json")); + workspace.getViews().getConfiguration().getStyles().addElementStyle("Boundary:SoftwareSystem").color("#0b4884"); + workspace.getViews().getConfiguration().getStyles().addElementStyle("Boundary:Container").color("#438dd5"); + + StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(); + Collection diagrams = exporter.export(workspace); + assertEquals(7, diagrams.size()); + + Diagram diagram = diagrams.stream().filter(d -> d.getKey().equals("SystemLandscape")).findFirst().get(); + assertEquals(""" + @startuml + title System Landscape View + + set separator none + top to bottom direction + skinparam ranksep 60 + skinparam nodesep 30 + hide stereotype + + + + rectangle "Big Bank plc" <> as groupQmlnIEJhbmsgcGxj { + person "==Customer Service Staff\\n[Person]\\n\\nCustomer service staff within the bank." <> as CustomerServiceStaff + person "==Back Office Staff\\n[Person]\\n\\nAdministration and support staff within the bank." <> as BackOfficeStaff + rectangle "==Mainframe Banking System\\n[Software System]\\n\\nStores all of the core banking information about customers, accounts, transactions, etc." <> as MainframeBankingSystem + rectangle "==E-mail System\\n[Software System]\\n\\nThe internal Microsoft Exchange e-mail system." <> as EmailSystem + rectangle "==ATM\\n[Software System]\\n\\nAllows customers to withdraw cash." <> as ATM + rectangle "==Internet Banking System\\n[Software System]\\n\\nAllows customers to view information about their bank accounts, and make payments." <> as InternetBankingSystem + } + + person "==Personal Banking Customer\\n[Person]\\n\\nA customer of the bank, with personal bank accounts." <> as PersonalBankingCustomer + + PersonalBankingCustomer --> InternetBankingSystem <> : "Views account balances, and makes payments using" + InternetBankingSystem --> MainframeBankingSystem <> : "Gets account information from, and makes payments using" + InternetBankingSystem --> EmailSystem <> : "Sends e-mail using" + EmailSystem --> PersonalBankingCustomer <> : "Sends e-mails to" + PersonalBankingCustomer --> CustomerServiceStaff <> : "Asks questions to\\n[Telephone]" + CustomerServiceStaff --> MainframeBankingSystem <> : "Uses" + PersonalBankingCustomer --> ATM <> : "Withdraws cash using" + ATM --> MainframeBankingSystem <> : "Uses" + BackOfficeStaff --> MainframeBankingSystem <> : "Uses" + + @enduml""", diagram.getDefinition()); + + diagram = diagrams.stream().filter(d -> d.getKey().equals("SystemContext")).findFirst().get(); + assertEquals(""" + @startuml + title System Context View: Internet Banking System\\nThe system context diagram for the Internet Banking System. + + set separator none + top to bottom direction + skinparam ranksep 60 + skinparam nodesep 30 + hide stereotype + + + + rectangle "Big Bank plc" <> as groupQmlnIEJhbmsgcGxj { + rectangle "==Mainframe Banking System\\n[Software System]\\n\\nStores all of the core banking information about customers, accounts, transactions, etc." <> as MainframeBankingSystem + rectangle "==E-mail System\\n[Software System]\\n\\nThe internal Microsoft Exchange e-mail system." <> as EmailSystem + rectangle "==Internet Banking System\\n[Software System]\\n\\nAllows customers to view information about their bank accounts, and make payments." <> as InternetBankingSystem + } + + person "==Personal Banking Customer\\n[Person]\\n\\nA customer of the bank, with personal bank accounts." <> as PersonalBankingCustomer + + PersonalBankingCustomer --> InternetBankingSystem <> : "Views account balances, and makes payments using" + InternetBankingSystem --> MainframeBankingSystem <> : "Gets account information from, and makes payments using" + InternetBankingSystem --> EmailSystem <> : "Sends e-mail using" + EmailSystem --> PersonalBankingCustomer <> : "Sends e-mails to" + + @enduml""", diagram.getDefinition()); + + diagram = diagrams.stream().filter(d -> d.getKey().equals("Containers")).findFirst().get(); + assertEquals(""" + @startuml + title Container View: Internet Banking System\\nThe container diagram for the Internet Banking System. + + set separator none + top to bottom direction + skinparam ranksep 60 + skinparam nodesep 30 + hide stereotype + + + + person "==Personal Banking Customer\\n[Person]\\n\\nA customer of the bank, with personal bank accounts." <> as PersonalBankingCustomer + rectangle "==Mainframe Banking System\\n[Software System]\\n\\nStores all of the core banking information about customers, accounts, transactions, etc." <> as MainframeBankingSystem + rectangle "==E-mail System\\n[Software System]\\n\\nThe internal Microsoft Exchange e-mail system." <> as EmailSystem + + rectangle "Internet Banking System\\n[Software System]" <> { + rectangle "==Web Application\\n[Container: Java and Spring MVC]\\n\\nDelivers the static content and the Internet banking single page application." <> as InternetBankingSystem.WebApplication + rectangle "==API Application\\n[Container: Java and Spring MVC]\\n\\nProvides Internet banking functionality via a JSON/HTTPS API." <> as InternetBankingSystem.APIApplication + database "==Database\\n[Container: Oracle Database Schema]\\n\\nStores user registration information, hashed authentication credentials, access logs, etc." <> as InternetBankingSystem.Database + rectangle "==Single-Page Application\\n[Container: JavaScript and Angular]\\n\\nProvides all of the Internet banking functionality to customers via their web browser." <> as InternetBankingSystem.SinglePageApplication + rectangle "==Mobile App\\n[Container: Xamarin]\\n\\nProvides a limited subset of the Internet banking functionality to customers via their mobile device." <> as InternetBankingSystem.MobileApp + } + + EmailSystem --> PersonalBankingCustomer <> : "Sends e-mails to" + PersonalBankingCustomer --> InternetBankingSystem.WebApplication <> : "Visits bigbank.com/ib using\\n[HTTPS]" + PersonalBankingCustomer --> InternetBankingSystem.SinglePageApplication <> : "Views account balances, and makes payments using" + PersonalBankingCustomer --> InternetBankingSystem.MobileApp <> : "Views account balances, and makes payments using" + InternetBankingSystem.WebApplication --> InternetBankingSystem.SinglePageApplication <> : "Delivers to the customer's web browser" + InternetBankingSystem.SinglePageApplication --> InternetBankingSystem.APIApplication <> : "Makes API calls to\\n[JSON/HTTPS]" + InternetBankingSystem.MobileApp --> InternetBankingSystem.APIApplication <> : "Makes API calls to\\n[JSON/HTTPS]" + InternetBankingSystem.APIApplication --> InternetBankingSystem.Database <> : "Reads from and writes to\\n[SQL/TCP]" + InternetBankingSystem.APIApplication --> MainframeBankingSystem <> : "Makes API calls to\\n[XML/HTTPS]" + InternetBankingSystem.APIApplication --> EmailSystem <> : "Sends e-mail using" + + @enduml""", diagram.getDefinition()); + + diagram = diagrams.stream().filter(d -> d.getKey().equals("Components")).findFirst().get(); + assertEquals(""" + @startuml + title Component View: Internet Banking System - API Application\\nThe component diagram for the API Application. + + set separator none + top to bottom direction + skinparam ranksep 60 + skinparam nodesep 30 + hide stereotype + + + + rectangle "==Mainframe Banking System\\n[Software System]\\n\\nStores all of the core banking information about customers, accounts, transactions, etc." <> as MainframeBankingSystem + rectangle "==E-mail System\\n[Software System]\\n\\nThe internal Microsoft Exchange e-mail system." <> as EmailSystem + + rectangle "Internet Banking System\\n[Software System]" <> { + rectangle "API Application\\n[Container: Java and Spring MVC]" <> { + rectangle "==Sign In Controller\\n[Component: Spring MVC Rest Controller]\\n\\nAllows users to sign in to the Internet Banking System." <> as InternetBankingSystem.APIApplication.SignInController + rectangle "==Accounts Summary Controller\\n[Component: Spring MVC Rest Controller]\\n\\nProvides customers with a summary of their bank accounts." <> as InternetBankingSystem.APIApplication.AccountsSummaryController + rectangle "==Reset Password Controller\\n[Component: Spring MVC Rest Controller]\\n\\nAllows users to reset their passwords with a single use URL." <> as InternetBankingSystem.APIApplication.ResetPasswordController + rectangle "==Security Component\\n[Component: Spring Bean]\\n\\nProvides functionality related to signing in, changing passwords, etc." <> as InternetBankingSystem.APIApplication.SecurityComponent + rectangle "==Mainframe Banking System Facade\\n[Component: Spring Bean]\\n\\nA facade onto the mainframe banking system." <> as InternetBankingSystem.APIApplication.MainframeBankingSystemFacade + rectangle "==E-mail Component\\n[Component: Spring Bean]\\n\\nSends e-mails to users." <> as InternetBankingSystem.APIApplication.EmailComponent + } + + database "==Database\\n[Container: Oracle Database Schema]\\n\\nStores user registration information, hashed authentication credentials, access logs, etc." <> as InternetBankingSystem.Database + rectangle "==Single-Page Application\\n[Container: JavaScript and Angular]\\n\\nProvides all of the Internet banking functionality to customers via their web browser." <> as InternetBankingSystem.SinglePageApplication + rectangle "==Mobile App\\n[Container: Xamarin]\\n\\nProvides a limited subset of the Internet banking functionality to customers via their mobile device." <> as InternetBankingSystem.MobileApp + } + + InternetBankingSystem.SinglePageApplication --> InternetBankingSystem.APIApplication.SignInController <> : "Makes API calls to\\n[JSON/HTTPS]" + InternetBankingSystem.SinglePageApplication --> InternetBankingSystem.APIApplication.AccountsSummaryController <> : "Makes API calls to\\n[JSON/HTTPS]" + InternetBankingSystem.SinglePageApplication --> InternetBankingSystem.APIApplication.ResetPasswordController <> : "Makes API calls to\\n[JSON/HTTPS]" + InternetBankingSystem.MobileApp --> InternetBankingSystem.APIApplication.SignInController <> : "Makes API calls to\\n[JSON/HTTPS]" + InternetBankingSystem.MobileApp --> InternetBankingSystem.APIApplication.AccountsSummaryController <> : "Makes API calls to\\n[JSON/HTTPS]" + InternetBankingSystem.MobileApp --> InternetBankingSystem.APIApplication.ResetPasswordController <> : "Makes API calls to\\n[JSON/HTTPS]" + InternetBankingSystem.APIApplication.SignInController --> InternetBankingSystem.APIApplication.SecurityComponent <> : "Uses" + InternetBankingSystem.APIApplication.AccountsSummaryController --> InternetBankingSystem.APIApplication.MainframeBankingSystemFacade <> : "Uses" + InternetBankingSystem.APIApplication.ResetPasswordController --> InternetBankingSystem.APIApplication.SecurityComponent <> : "Uses" + InternetBankingSystem.APIApplication.ResetPasswordController --> InternetBankingSystem.APIApplication.EmailComponent <> : "Uses" + InternetBankingSystem.APIApplication.SecurityComponent --> InternetBankingSystem.Database <> : "Reads from and writes to\\n[SQL/TCP]" + InternetBankingSystem.APIApplication.MainframeBankingSystemFacade --> MainframeBankingSystem <> : "Makes API calls to\\n[XML/HTTPS]" + InternetBankingSystem.APIApplication.EmailComponent --> EmailSystem <> : "Sends e-mail using" + + @enduml""", diagram.getDefinition()); + + diagram = diagrams.stream().filter(d -> d.getKey().equals("SignIn")).findFirst().get(); + assertEquals(""" + @startuml + title Dynamic View: Internet Banking System - API Application\\nSummarises how the sign in feature works in the single-page application. + + set separator none + top to bottom direction + skinparam ranksep 60 + skinparam nodesep 30 + hide stereotype + + + + rectangle "Internet Banking System\\n[Software System]" <> { + rectangle "API Application\\n[Container: Java and Spring MVC]" <> { + rectangle "==Sign In Controller\\n[Component: Spring MVC Rest Controller]\\n\\nAllows users to sign in to the Internet Banking System." <> as InternetBankingSystem.APIApplication.SignInController + rectangle "==Security Component\\n[Component: Spring Bean]\\n\\nProvides functionality related to signing in, changing passwords, etc." <> as InternetBankingSystem.APIApplication.SecurityComponent + } + + database "==Database\\n[Container: Oracle Database Schema]\\n\\nStores user registration information, hashed authentication credentials, access logs, etc." <> as InternetBankingSystem.Database + rectangle "==Single-Page Application\\n[Container: JavaScript and Angular]\\n\\nProvides all of the Internet banking functionality to customers via their web browser." <> as InternetBankingSystem.SinglePageApplication + } + + rectangle "==Single-Page Application\\n[Container: JavaScript and Angular]\\n\\nProvides all of the Internet banking functionality to customers via their web browser." <> as InternetBankingSystem.SinglePageApplication + database "==Database\\n[Container: Oracle Database Schema]\\n\\nStores user registration information, hashed authentication credentials, access logs, etc." <> as InternetBankingSystem.Database + + InternetBankingSystem.SinglePageApplication --> InternetBankingSystem.APIApplication.SignInController <> : "1: Submits credentials to\\n[JSON/HTTPS]" + InternetBankingSystem.APIApplication.SignInController --> InternetBankingSystem.APIApplication.SecurityComponent <> : "2: Validates credentials using" + InternetBankingSystem.APIApplication.SecurityComponent --> InternetBankingSystem.Database <> : "3: select * from users where username = ?\\n[SQL/TCP]" + InternetBankingSystem.APIApplication.SecurityComponent <-- InternetBankingSystem.Database <> : "4: Returns user data to\\n[SQL/TCP]" + InternetBankingSystem.APIApplication.SignInController <-- InternetBankingSystem.APIApplication.SecurityComponent <> : "5: Returns true if the hashed password matches" + InternetBankingSystem.SinglePageApplication <-- InternetBankingSystem.APIApplication.SignInController <> : "6: Sends back an authentication token to\\n[JSON/HTTPS]" + + @enduml""", diagram.getDefinition()); + + diagram = diagrams.stream().filter(md -> md.getKey().equals("DevelopmentDeployment")).findFirst().get(); + assertEquals(""" + @startuml + title Deployment View: Internet Banking System - Development\\nAn example development deployment scenario for the Internet Banking System. + + set separator none + top to bottom direction + skinparam ranksep 60 + skinparam nodesep 30 + hide stereotype + + + + rectangle "Developer Laptop\\n[Deployment Node: Microsoft Windows 10 or Apple macOS]" <> as Development.DeveloperLaptop { + rectangle "Web Browser\\n[Deployment Node: Chrome, Firefox, Safari, or Edge]" <> as Development.DeveloperLaptop.WebBrowser { + rectangle "==Single-Page Application\\n[Container: JavaScript and Angular]\\n\\nProvides all of the Internet banking functionality to customers via their web browser." <> as Development.DeveloperLaptop.WebBrowser.SinglePageApplication_1 + } + + rectangle "Docker Container - Web Server\\n[Deployment Node: Docker]" <> as Development.DeveloperLaptop.DockerContainerWebServer { + rectangle "Apache Tomcat\\n[Deployment Node: Apache Tomcat 8.x]" <> as Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat { + rectangle "==Web Application\\n[Container: Java and Spring MVC]\\n\\nDelivers the static content and the Internet banking single page application." <> as Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.WebApplication_1 + rectangle "==API Application\\n[Container: Java and Spring MVC]\\n\\nProvides Internet banking functionality via a JSON/HTTPS API." <> as Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.APIApplication_1 + } + + } + + rectangle "Docker Container - Database Server\\n[Deployment Node: Docker]" <> as Development.DeveloperLaptop.DockerContainerDatabaseServer { + rectangle "Database Server\\n[Deployment Node: Oracle 12c]" <> as Development.DeveloperLaptop.DockerContainerDatabaseServer.DatabaseServer { + database "==Database\\n[Container: Oracle Database Schema]\\n\\nStores user registration information, hashed authentication credentials, access logs, etc." <> as Development.DeveloperLaptop.DockerContainerDatabaseServer.DatabaseServer.Database_1 + } + + } + + } + + rectangle "Big Bank plc\\n[Deployment Node: Big Bank plc data center]" <> as Development.BigBankplc { + rectangle "bigbank-dev001\\n[Deployment Node]" <> as Development.BigBankplc.bigbankdev001 { + rectangle "==Mainframe Banking System\\n[Software System]\\n\\nStores all of the core banking information about customers, accounts, transactions, etc." <> as Development.BigBankplc.bigbankdev001.MainframeBankingSystem_1 + } + + } + + Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.WebApplication_1 --> Development.DeveloperLaptop.WebBrowser.SinglePageApplication_1 <> : "Delivers to the customer's web browser" + Development.DeveloperLaptop.WebBrowser.SinglePageApplication_1 --> Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.APIApplication_1 <> : "Makes API calls to\\n[JSON/HTTPS]" + Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.APIApplication_1 --> Development.DeveloperLaptop.DockerContainerDatabaseServer.DatabaseServer.Database_1 <> : "Reads from and writes to\\n[SQL/TCP]" + Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.APIApplication_1 --> Development.BigBankplc.bigbankdev001.MainframeBankingSystem_1 <> : "Makes API calls to\\n[XML/HTTPS]" + + @enduml""", diagram.getDefinition()); + + diagram = diagrams.stream().filter(md -> md.getKey().equals("LiveDeployment")).findFirst().get(); + assertEquals(""" + @startuml + title Deployment View: Internet Banking System - Live\\nAn example live deployment scenario for the Internet Banking System. + + set separator none + top to bottom direction + skinparam ranksep 60 + skinparam nodesep 30 + hide stereotype + + + + rectangle "Customer's mobile device\\n[Deployment Node: Apple iOS or Android]" <> as Live.Customersmobiledevice { + rectangle "==Mobile App\\n[Container: Xamarin]\\n\\nProvides a limited subset of the Internet banking functionality to customers via their mobile device." <> as Live.Customersmobiledevice.MobileApp_1 + } + + rectangle "Customer's computer\\n[Deployment Node: Microsoft Windows or Apple macOS]" <> as Live.Customerscomputer { + rectangle "Web Browser\\n[Deployment Node: Chrome, Firefox, Safari, or Edge]" <> as Live.Customerscomputer.WebBrowser { + rectangle "==Single-Page Application\\n[Container: JavaScript and Angular]\\n\\nProvides all of the Internet banking functionality to customers via their web browser." <> as Live.Customerscomputer.WebBrowser.SinglePageApplication_1 + } + + } + + rectangle "Big Bank plc\\n[Deployment Node: Big Bank plc data center]" <> as Live.BigBankplc { + rectangle "bigbank-web*** (x4)\\n[Deployment Node: Ubuntu 16.04 LTS]" <> as Live.BigBankplc.bigbankweb { + rectangle "Apache Tomcat\\n[Deployment Node: Apache Tomcat 8.x]" <> as Live.BigBankplc.bigbankweb.ApacheTomcat { + rectangle "==Web Application\\n[Container: Java and Spring MVC]\\n\\nDelivers the static content and the Internet banking single page application." <> as Live.BigBankplc.bigbankweb.ApacheTomcat.WebApplication_1 + } + + } + + rectangle "bigbank-api*** (x8)\\n[Deployment Node: Ubuntu 16.04 LTS]" <> as Live.BigBankplc.bigbankapi { + rectangle "Apache Tomcat\\n[Deployment Node: Apache Tomcat 8.x]" <> as Live.BigBankplc.bigbankapi.ApacheTomcat { + rectangle "==API Application\\n[Container: Java and Spring MVC]\\n\\nProvides Internet banking functionality via a JSON/HTTPS API." <> as Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1 + } + + } + + rectangle "bigbank-db01\\n[Deployment Node: Ubuntu 16.04 LTS]" <> as Live.BigBankplc.bigbankdb01 { + rectangle "Oracle - Primary\\n[Deployment Node: Oracle 12c]" <> as Live.BigBankplc.bigbankdb01.OraclePrimary { + database "==Database\\n[Container: Oracle Database Schema]\\n\\nStores user registration information, hashed authentication credentials, access logs, etc." <> as Live.BigBankplc.bigbankdb01.OraclePrimary.Database_1 + } + + } + + rectangle "bigbank-db02\\n[Deployment Node: Ubuntu 16.04 LTS]" <> as Live.BigBankplc.bigbankdb02 { + rectangle "Oracle - Secondary\\n[Deployment Node: Oracle 12c]" <> as Live.BigBankplc.bigbankdb02.OracleSecondary { + database "==Database\\n[Container: Oracle Database Schema]\\n\\nStores user registration information, hashed authentication credentials, access logs, etc." <> as Live.BigBankplc.bigbankdb02.OracleSecondary.Database_1 + } + + } + + rectangle "bigbank-prod001\\n[Deployment Node]" <> as Live.BigBankplc.bigbankprod001 { + rectangle "==Mainframe Banking System\\n[Software System]\\n\\nStores all of the core banking information about customers, accounts, transactions, etc." <> as Live.BigBankplc.bigbankprod001.MainframeBankingSystem_1 + } + + } + + Live.BigBankplc.bigbankweb.ApacheTomcat.WebApplication_1 --> Live.Customerscomputer.WebBrowser.SinglePageApplication_1 <> : "Delivers to the customer's web browser" + Live.Customersmobiledevice.MobileApp_1 --> Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1 <> : "Makes API calls to\\n[JSON/HTTPS]" + Live.Customerscomputer.WebBrowser.SinglePageApplication_1 --> Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1 <> : "Makes API calls to\\n[JSON/HTTPS]" + Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1 --> Live.BigBankplc.bigbankdb01.OraclePrimary.Database_1 <> : "Reads from and writes to\\n[SQL/TCP]" + Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1 --> Live.BigBankplc.bigbankdb02.OracleSecondary.Database_1 <> : "Reads from and writes to\\n[SQL/TCP]" + Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1 --> Live.BigBankplc.bigbankprod001.MainframeBankingSystem_1 <> : "Makes API calls to\\n[XML/HTTPS]" + Live.BigBankplc.bigbankdb01.OraclePrimary --> Live.BigBankplc.bigbankdb02.OracleSecondary <> : "Replicates data to" + + @enduml""", diagram.getDefinition()); + + // and the sequence diagram version + workspace.getViews().getConfiguration().addProperty(exporter.PLANTUML_SEQUENCE_DIAGRAM_PROPERTY, "true"); + diagrams = exporter.export(workspace); + diagram = diagrams.stream().filter(d -> d.getKey().equals("SignIn")).findFirst().get(); + assertEquals(""" + @startuml + title Dynamic View: Internet Banking System - API Application\\nSummarises how the sign in feature works in the single-page application. + + set separator none + hide stereotype + + + + participant "Single-Page Application\\n[Container: JavaScript and Angular]" as InternetBankingSystem.SinglePageApplication <> + participant "Sign In Controller\\n[Component: Spring MVC Rest Controller]" as InternetBankingSystem.APIApplication.SignInController <> + participant "Security Component\\n[Component: Spring Bean]" as InternetBankingSystem.APIApplication.SecurityComponent <> + database "Database\\n[Container: Oracle Database Schema]" as InternetBankingSystem.Database <> + + InternetBankingSystem.SinglePageApplication -> InternetBankingSystem.APIApplication.SignInController <> : 1: Submits credentials to\\n[JSON/HTTPS] + InternetBankingSystem.APIApplication.SignInController -> InternetBankingSystem.APIApplication.SecurityComponent <> : 2: Validates credentials using + InternetBankingSystem.APIApplication.SecurityComponent -> InternetBankingSystem.Database <> : 3: select * from users where username = ?\\n[SQL/TCP] + InternetBankingSystem.APIApplication.SecurityComponent <-- InternetBankingSystem.Database <> : 4: Returns user data to\\n[SQL/TCP] + InternetBankingSystem.APIApplication.SignInController <-- InternetBankingSystem.APIApplication.SecurityComponent <> : 5: Returns true if the hashed password matches + InternetBankingSystem.SinglePageApplication <-- InternetBankingSystem.APIApplication.SignInController <> : 6: Sends back an authentication token to\\n[JSON/HTTPS] + + @enduml""", diagram.getDefinition()); + } + @Test public void systemLandscapeView_NoStyling_Light() { Workspace workspace = new Workspace("Name", "Description"); @@ -704,8 +1657,8 @@ public void dynamicView_SequenceStyle_NoStyling_Light() { } - participant "A\\n[Software System]" as A <> #ffffff - participant "B\\n[Software System]" as B <> #ffffff + participant "A\\n[Software System]" as A <> + participant "B\\n[Software System]" as B <> A -> B <> : 1: Uses\\n[JSON/HTTPS] @@ -758,6 +1711,140 @@ public void dynamicView_SequenceStyle_NoStyling_Light() { @enduml""", diagram.getLegend().getDefinition()); } + @Test + public void dynamicView_SequenceStyle_Styling_Light() { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem a = workspace.getModel().addSoftwareSystem("A"); + a.addTags("A"); + SoftwareSystem b = workspace.getModel().addSoftwareSystem("B"); + b.addTags("B"); + + a.uses(b, "Uses", "JSON/HTTPS"); + + workspace.getViews().getConfiguration().getStyles().addElementStyle("A").shape(Shape.Person).color("#ff0000"); + workspace.getViews().getConfiguration().getStyles().addElementStyle("B").shape(Shape.Cylinder).color("#00ff00"); + + DynamicView view = workspace.getViews().createDynamicView("key", "Description"); + view.add(a, b); + view.addProperty(StructurizrPlantUMLExporter.PLANTUML_SEQUENCE_DIAGRAM_PROPERTY, "true"); + + StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(); + Diagram diagram = exporter.export(view); + + assertEquals(""" + @startuml + title Dynamic View\\nDescription + + set separator none + hide stereotype + + + + actor "A\\n[Software System]" as A <> + database "B\\n[Software System]" as B <> + + A -> B <> : 1: Uses\\n[JSON/HTTPS] + + @enduml""", diagram.getDefinition()); + + assertEquals(""" + @startuml + + set separator none + hide stereotype + + + + person "==A" <> + + database "==B" <> + + rectangle "." <<.Element-Transparent>> as 1 + 1 --> 1 <> : "Relationship" + + @enduml""", diagram.getLegend().getDefinition()); + } + @Test public void groups() throws Exception { Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/groups.json")); From 6201c03a3a12888f12cd91e7495a94e28d670b53 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 30 Sep 2025 08:39:07 +0100 Subject: [PATCH 693/717] Tidies up element width/height handling, which needs to differ across diagram exporters. --- .../autolayout/graphviz/DOTExporter.java | 17 +- .../autolayout/graphviz/SVGReader.java | 2 +- .../autolayout/graphviz/StyleUtils.java | 38 ++ .../GraphvizAutomaticLayoutTests.java | 35 +- .../com/structurizr/view/ThemeUtilsTests.java | 4 +- .../com/structurizr/view/ElementStyle.java | 3 - .../java/com/structurizr/view/Styles.java | 2 +- .../com/structurizr/view/StylesTests.java | 26 +- .../structurizr/export/dot/DOTExporter.java | 10 + .../plantuml/PlantUMLDeploymentNodeStyle.java | 99 +++ .../plantuml/StructurizrPlantUMLExporter.java | 50 +- ...ructurizrPlantUMLDiagramExporterTests.java | 597 +++++++++--------- 12 files changed, 518 insertions(+), 365 deletions(-) create mode 100644 structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/StyleUtils.java create mode 100644 structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLDeploymentNodeStyle.java diff --git a/structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/DOTExporter.java b/structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/DOTExporter.java index 489cf1bb6..783ce7967 100644 --- a/structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/DOTExporter.java +++ b/structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/DOTExporter.java @@ -6,6 +6,7 @@ import com.structurizr.model.*; import com.structurizr.util.StringUtils; import com.structurizr.view.DeploymentView; +import com.structurizr.view.ElementStyle; import com.structurizr.view.ModelView; import com.structurizr.view.RelationshipView; @@ -114,10 +115,12 @@ protected void endDeploymentNodeBoundary(ModelView view, IndentingWriter writer) @Override protected void writeElement(ModelView view, Element element, IndentingWriter writer) { + ElementStyle elementStyle = StyleUtils.findElementStyle(view, element); + writer.writeLine(String.format(locale, "%s [width=%f,height=%f,fixedsize=true,id=%s,label=\"%s: %s\"]", element.getId(), - getElementWidth(view, element.getId()) / Constants.STRUCTURIZR_DPI, // convert Structurizr dimensions to inches - getElementHeight(view, element.getId()) / Constants.STRUCTURIZR_DPI, // convert Structurizr dimensions to inches + elementStyle.getWidth() / Constants.STRUCTURIZR_DPI, // convert Structurizr dimensions to inches + elementStyle.getHeight() / Constants.STRUCTURIZR_DPI, // convert Structurizr dimensions to inches element.getId(), element.getId(), escape(element.getName()) @@ -217,14 +220,4 @@ private Element findElementInside(DeploymentNode deploymentNode, ModelView view) return null; } - private int getElementWidth(ModelView view, String elementId) { - Element element = view.getModel().getElement(elementId); - return view.getViewSet().getConfiguration().getStyles().findElementStyle(element).getWidth(); - } - - private int getElementHeight(ModelView view, String elementId) { - Element element = view.getModel().getElement(elementId); - return view.getViewSet().getConfiguration().getStyles().findElementStyle(element).getHeight(); - } - } \ No newline at end of file diff --git a/structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/SVGReader.java b/structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/SVGReader.java index 359c2edb5..adc73c6d0 100644 --- a/structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/SVGReader.java +++ b/structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/SVGReader.java @@ -89,7 +89,7 @@ void parseAndApplyLayout(ModelView view) throws Exception { minimumX = Math.min(elementView.getX(), minimumX); minimumY = Math.min(elementView.getY(), minimumY); - ElementStyle style = view.getViewSet().getConfiguration().getStyles().findElementStyle(view.getModel().getElement(elementView.getId())); + ElementStyle style = StyleUtils.findElementStyle(view, view.getModel().getElement(elementView.getId())); maximumX = Math.max(elementView.getX() + style.getWidth(), maximumX); maximumY = Math.max(elementView.getY() + style.getHeight(), maximumY); diff --git a/structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/StyleUtils.java b/structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/StyleUtils.java new file mode 100644 index 000000000..ef89674f9 --- /dev/null +++ b/structurizr-autolayout/src/main/java/com/structurizr/autolayout/graphviz/StyleUtils.java @@ -0,0 +1,38 @@ +package com.structurizr.autolayout.graphviz; + +import com.structurizr.model.Element; +import com.structurizr.view.ElementStyle; +import com.structurizr.view.Shape; +import com.structurizr.view.View; + +class StyleUtils { + + private static final int DEFAULT_WIDTH = 450; + private static final int DEFAULT_HEIGHT = 300; + + private static final int DEFAULT_WIDTH_PERSON = 400; + private static final int DEFAULT_HEIGHT_PERSON = 400; + + static ElementStyle findElementStyle(View view, Element element) { + ElementStyle style = view.getViewSet().getConfiguration().getStyles().findElementStyle(element); + + if (style.getWidth() == null) { + if (style.getShape() == Shape.Person || style.getShape() == Shape.Robot) { + style.setWidth(DEFAULT_WIDTH_PERSON); + } else { + style.setWidth(DEFAULT_WIDTH); + } + } + + if (style.getHeight() == null) { + if (style.getShape() == Shape.Person || style.getShape() == Shape.Robot) { + style.setHeight(DEFAULT_HEIGHT_PERSON); + } else { + style.setHeight(DEFAULT_HEIGHT); + } + } + + return style; + } + +} diff --git a/structurizr-autolayout/src/test/java/com/structurizr/autolayout/graphviz/GraphvizAutomaticLayoutTests.java b/structurizr-autolayout/src/test/java/com/structurizr/autolayout/graphviz/GraphvizAutomaticLayoutTests.java index 710553436..c499674a4 100644 --- a/structurizr-autolayout/src/test/java/com/structurizr/autolayout/graphviz/GraphvizAutomaticLayoutTests.java +++ b/structurizr-autolayout/src/test/java/com/structurizr/autolayout/graphviz/GraphvizAutomaticLayoutTests.java @@ -8,7 +8,6 @@ import com.structurizr.view.AutomaticLayout; import com.structurizr.view.Shape; import com.structurizr.view.SystemContextView; -import com.structurizr.view.SystemLandscapeView; import org.junit.jupiter.api.Test; import java.io.File; @@ -23,36 +22,36 @@ public void apply_Workspace() throws Exception { File tempDir = Files.createTempDirectory("graphviz").toFile(); GraphvizAutomaticLayout graphviz = new GraphvizAutomaticLayout(tempDir); - Workspace workspace = new Workspace("Name"); - SoftwareSystem a = workspace.getModel().addSoftwareSystem("A"); - SoftwareSystem b = workspace.getModel().addSoftwareSystem("B"); - a.uses(b, "Uses"); + Workspace workspace = new Workspace("Name", ""); + Person user = workspace.getModel().addPerson("User"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); + user.uses(softwareSystem, "Uses"); - SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key"); + SystemContextView view = workspace.getViews().createSystemContextView(softwareSystem, "SystemContext", ""); view.addAllElements(); workspace.getViews().getConfiguration().getStyles().addElementStyle(Tags.PERSON).shape(Shape.Person); - assertEquals(0, view.getElementView(a).getX()); - assertEquals(0, view.getElementView(a).getY()); - assertEquals(0, view.getElementView(b).getX()); - assertEquals(0, view.getElementView(b).getY()); + assertEquals(0, view.getElementView(user).getX()); + assertEquals(0, view.getElementView(user).getY()); + assertEquals(0, view.getElementView(softwareSystem).getX()); + assertEquals(0, view.getElementView(softwareSystem).getY()); graphviz.apply(workspace); // no change - the view doesn't have automatic layout configured - assertEquals(0, view.getElementView(a).getX()); - assertEquals(0, view.getElementView(a).getY()); - assertEquals(0, view.getElementView(b).getX()); - assertEquals(0, view.getElementView(b).getY()); + assertEquals(0, view.getElementView(user).getX()); + assertEquals(0, view.getElementView(user).getY()); + assertEquals(0, view.getElementView(softwareSystem).getX()); + assertEquals(0, view.getElementView(softwareSystem).getY()); view.enableAutomaticLayout(AutomaticLayout.RankDirection.TopBottom); graphviz.apply(workspace); - assertEquals(208, view.getElementView(a).getX()); - assertEquals(208, view.getElementView(a).getY()); - assertEquals(208, view.getElementView(b).getX()); - assertEquals(808, view.getElementView(b).getY()); + assertEquals(233, view.getElementView(user).getX()); + assertEquals(208, view.getElementView(user).getY()); + assertEquals(208, view.getElementView(softwareSystem).getX()); + assertEquals(908, view.getElementView(softwareSystem).getY()); } } \ No newline at end of file diff --git a/structurizr-client/src/test/java/com/structurizr/view/ThemeUtilsTests.java b/structurizr-client/src/test/java/com/structurizr/view/ThemeUtilsTests.java index 3ebc16983..1ecae3995 100644 --- a/structurizr-client/src/test/java/com/structurizr/view/ThemeUtilsTests.java +++ b/structurizr-client/src/test/java/com/structurizr/view/ThemeUtilsTests.java @@ -94,8 +94,8 @@ void findElementStyle_WithThemes() { workspace.getViews().getConfiguration().getStyles().addStylesFromTheme(new Theme(elementStyles, relationshipStyles)); ElementStyle style = workspace.getViews().getConfiguration().getStyles().findElementStyle(softwareSystem); - assertEquals(Integer.valueOf(450), style.getWidth()); - assertEquals(Integer.valueOf(300), style.getHeight()); + assertNull(style.getWidth()); + assertNull(style.getHeight()); assertEquals("#ff0000", style.getBackground()); // from theme 2 assertEquals("#ffffff", style.getColor()); // from theme 1 assertEquals(Integer.valueOf(24), style.getFontSize()); diff --git a/structurizr-core/src/main/java/com/structurizr/view/ElementStyle.java b/structurizr-core/src/main/java/com/structurizr/view/ElementStyle.java index fe4799b60..2787334b1 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/ElementStyle.java +++ b/structurizr-core/src/main/java/com/structurizr/view/ElementStyle.java @@ -9,9 +9,6 @@ */ public final class ElementStyle extends AbstractStyle { - public static final int DEFAULT_WIDTH = 450; - public static final int DEFAULT_HEIGHT = 300; - @JsonInclude(value = JsonInclude.Include.NON_NULL) private Integer width; diff --git a/structurizr-core/src/main/java/com/structurizr/view/Styles.java b/structurizr-core/src/main/java/com/structurizr/view/Styles.java index ba0c7e7ee..2d2db79a7 100644 --- a/structurizr-core/src/main/java/com/structurizr/view/Styles.java +++ b/structurizr-core/src/main/java/com/structurizr/view/Styles.java @@ -270,7 +270,7 @@ public ElementStyle findElementStyle(Element element) { * @return an ElementStyle object */ public ElementStyle findElementStyle(Element element, ColorScheme colorScheme) { - ElementStyle style = new ElementStyle(Tags.ELEMENT).shape(Shape.Box).width(ElementStyle.DEFAULT_WIDTH).height(ElementStyle.DEFAULT_HEIGHT).fontSize(24).border(Border.Solid).opacity(100).metadata(true).description(true); + ElementStyle style = new ElementStyle(Tags.ELEMENT).shape(Shape.Box).fontSize(24).border(Border.Solid).opacity(100).metadata(true).description(true); if (element != null) { Set tagsUsedToComposeStyle = new LinkedHashSet<>(); diff --git a/structurizr-core/src/test/java/com/structurizr/view/StylesTests.java b/structurizr-core/src/test/java/com/structurizr/view/StylesTests.java index f0c6baab6..befbcebcd 100644 --- a/structurizr-core/src/test/java/com/structurizr/view/StylesTests.java +++ b/structurizr-core/src/test/java/com/structurizr/view/StylesTests.java @@ -53,8 +53,8 @@ void test_sortingOfRelationshipStyles() { @Test void findElementStyle_ReturnsTheDefaultStyle_WhenPassedNull() { ElementStyle style = styles.findElementStyle((Element) null); - assertEquals(Integer.valueOf(450), style.getWidth()); - assertEquals(Integer.valueOf(300), style.getHeight()); + assertNull(style.getWidth()); + assertNull(style.getHeight()); assertEquals("#ffffff", style.getBackground()); assertEquals("#444444", style.getColor()); assertEquals("#444444", style.getStroke()); @@ -72,8 +72,8 @@ void findElementStyle_ReturnsTheDefaultStyle_WhenPassedNull() { void findElementStyle_ReturnsTheDefaultStyle_WhenNoStylesAreDefined() { SoftwareSystem element = model.addSoftwareSystem("Name", "Description"); ElementStyle style = styles.findElementStyle(element); - assertEquals(Integer.valueOf(450), style.getWidth()); - assertEquals(Integer.valueOf(300), style.getHeight()); + assertNull(style.getWidth()); + assertNull(style.getHeight()); assertEquals("#ffffff", style.getBackground()); assertEquals("#444444", style.getColor()); assertEquals("#444444", style.getStroke()); @@ -91,8 +91,8 @@ void findElementStyle_ReturnsTheDefaultStyle_WhenNoStylesAreDefined() { void findElementStyleForDarkMode_ReturnsTheDefaultStyle_WhenNoStylesAreDefined() { SoftwareSystem element = model.addSoftwareSystem("Name", "Description"); ElementStyle style = styles.findElementStyle(element, ColorScheme.Dark); - assertEquals(Integer.valueOf(450), style.getWidth()); - assertEquals(Integer.valueOf(300), style.getHeight()); + assertNull(style.getWidth()); + assertNull(style.getHeight()); assertEquals("#111111", style.getBackground()); assertEquals("#cccccc", style.getColor()); assertEquals("#cccccc", style.getStroke()); @@ -158,20 +158,6 @@ void findElementStyle_ReturnsTheCorrectStyleForAnElementInstance_WhenStylesAreDe assertEquals("value", style.getProperties().get("name")); } - @Test - void findElementStyle_ReturnsTheDefaultElementSize_WhenTheShapeIsABox() { - SoftwareSystem element = model.addSoftwareSystem("Name", "Description"); - element.addTags("Some Tag"); - - styles.addElementStyle(Tags.SOFTWARE_SYSTEM).background("#ff0000").color("#ffffff"); - styles.addElementStyle("Some Tag").shape(Shape.Box); - - ElementStyle style = styles.findElementStyle(element); - assertEquals(Shape.Box, style.getShape()); - assertEquals(Integer.valueOf(450), style.getWidth()); - assertEquals(Integer.valueOf(300), style.getHeight()); - } - @Test void findRelationshipStyle_ReturnsTheDefaultStyle_WhenPassedNull_ForLightColorScheme() { RelationshipStyle style = styles.findRelationshipStyle((Relationship) null); diff --git a/structurizr-export/src/main/java/com/structurizr/export/dot/DOTExporter.java b/structurizr-export/src/main/java/com/structurizr/export/dot/DOTExporter.java index 26c7aa549..708ea4b46 100644 --- a/structurizr-export/src/main/java/com/structurizr/export/dot/DOTExporter.java +++ b/structurizr-export/src/main/java/com/structurizr/export/dot/DOTExporter.java @@ -12,6 +12,8 @@ */ public class DOTExporter extends AbstractDiagramExporter { + private static final int DEFAULT_WIDTH = 450; + private static final int DEFAULT_HEIGHT = 300; private static final String DEFAULT_FONT = "Arial"; private int clusterInternalMargin = 25; @@ -209,6 +211,14 @@ protected void endDeploymentNodeBoundary(ModelView view, IndentingWriter writer) protected void writeElement(ModelView view, Element element, IndentingWriter writer) { ElementStyle elementStyle = view.getViewSet().getConfiguration().getStyles().findElementStyle(element); + if (elementStyle.getWidth() == null) { + elementStyle.setWidth(DEFAULT_WIDTH); + } + + if (elementStyle.getHeight() == null) { + elementStyle.setHeight(DEFAULT_HEIGHT); + } + int nameFontSize = elementStyle.getFontSize() + 10; int metadataFontSize = elementStyle.getFontSize() - 5; int descriptionFontSize = elementStyle.getFontSize(); diff --git a/structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLDeploymentNodeStyle.java b/structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLDeploymentNodeStyle.java new file mode 100644 index 000000000..18915a0f6 --- /dev/null +++ b/structurizr-export/src/main/java/com/structurizr/export/plantuml/PlantUMLDeploymentNodeStyle.java @@ -0,0 +1,99 @@ +package com.structurizr.export.plantuml; + +import com.structurizr.export.IndentingWriter; +import com.structurizr.view.Border; + +import java.util.Base64; + +import static java.lang.String.format; + +class PlantUMLDeploymentNodeStyle extends PlantUMLStyle { + + private final String background; + private final String color; + private final String stroke; + private final int strokeWidth; + private final String lineStyle; + private final int fontSize; + private final String icon; + private final boolean shadow; + private Integer width; // only used for the legend + + PlantUMLDeploymentNodeStyle(String name, String background, String color, String stroke, int strokeWidth, Border border, int fontSize, String icon, boolean shadow) { + super(name); + + this.background = background; + this.color = color; + this.stroke = stroke; + this.strokeWidth = strokeWidth; + + switch (border) { + case Dotted: + this.lineStyle = (strokeWidth) + "-" + (strokeWidth); + break; + case Dashed: + this.lineStyle = (strokeWidth * 5) + "-" + (strokeWidth * 5); + break; + default: + this.lineStyle = "0"; + break; + } + + this.fontSize = fontSize; + this.icon = icon; + this.shadow = shadow; + } + + public int getFontSize() { + return fontSize; + } + + public String getIcon() { + return icon; + } + + public void setWidth(int width) { + this.width = width; + } + + @Override + String getClassSelector() { + return "DeploymentNode-" + Base64.getEncoder().encodeToString(name.getBytes()); + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + PlantUMLDeploymentNodeStyle that = (PlantUMLDeploymentNodeStyle) o; + return getClassSelector().equals(that.getClassSelector()); + } + + @Override + public String toString() { + IndentingWriter writer = new IndentingWriter(); + writer.indent(); + writer.writeLine(format("// %s", name)); + writer.writeLine(format(".%s {", getClassSelector())); + writer.indent(); + + writer.writeLine(String.format("BackgroundColor: %s;", background)); + writer.writeLine(String.format("LineColor: %s;", stroke)); + writer.writeLine(String.format("LineStyle: %s;", lineStyle)); + writer.writeLine(String.format("LineThickness: %s;", strokeWidth)); + writer.writeLine(String.format("FontColor: %s;", color)); + writer.writeLine(String.format("FontSize: %s;", fontSize)); + writer.writeLine("HorizontalAlignment: center;"); + writer.writeLine(String.format("Shadowing: %s;", shadow ? SHADOW_DISTANCE : 0)); + if (width != null) { + writer.writeLine(String.format("MaximumWidth: %s;", width)); + } + + writer.outdent(); + writer.writeLine("}"); + writer.outdent(); + writer.writeLine(); + + return writer.toString(); + } + +} \ No newline at end of file diff --git a/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java b/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java index 531ce9a6a..30229f0e4 100644 --- a/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java +++ b/structurizr-export/src/main/java/com/structurizr/export/plantuml/StructurizrPlantUMLExporter.java @@ -15,6 +15,8 @@ public class StructurizrPlantUMLExporter extends AbstractPlantUMLExporter { public static final String PLANTUML_SHADOW = "plantuml.shadow"; + private static final int DEFAULT_WIDTH = 450; + private static final int DEFAULT_HEIGHT = 300; private static final int DEFAULT_STROKE_WIDTH = 2; private static final double METADATA_FONT_SIZE_RATIO = 0.7; private static final int MAX_ICON_SIZE_RATIO = 3; @@ -99,6 +101,7 @@ private void writeStyles(IndentingWriter writer) { sortedStyles.stream().filter(style -> style instanceof PlantUMLRootStyle).forEach(style -> styles.append(style.toString())); sortedStyles.stream().filter(style -> style instanceof PlantUMLElementStyle).forEach(style -> styles.append(style.toString())); sortedStyles.stream().filter(style -> style instanceof PlantUMLRelationshipStyle).forEach(style -> styles.append(style.toString())); + sortedStyles.stream().filter(style -> style instanceof PlantUMLDeploymentNodeStyle).forEach(style -> styles.append(style.toString())); sortedStyles.stream().filter(style -> style instanceof PlantUMLBoundaryStyle).forEach(style -> styles.append(style.toString())); sortedStyles.stream().filter(style -> style instanceof PlantUMLGroupStyle).forEach(style -> styles.append(style.toString())); sortedStyles.stream().filter(style -> style instanceof PlantUMLLegendStyle).forEach(style -> styles.append(style.toString())); @@ -235,10 +238,8 @@ protected void endContainerBoundary(ModelView view, IndentingWriter writer) { protected void startDeploymentNodeBoundary(DeploymentView view, DeploymentNode deploymentNode, IndentingWriter writer) { ElementStyle elementStyle = findElementStyle(view, deploymentNode); - PlantUMLElementStyle plantUMLElementStyle = new PlantUMLElementStyle( + PlantUMLDeploymentNodeStyle plantUMLDeploymentNodeStyle = new PlantUMLDeploymentNodeStyle( elementStyle.getTag(), - elementStyle.getShape(), - elementStyle.getWidth(), elementStyle.getBackground(), elementStyle.getColor(), elementStyle.getStroke(), @@ -248,7 +249,7 @@ protected void startDeploymentNodeBoundary(DeploymentView view, DeploymentNode d elementStyle.getIcon(), "true".equalsIgnoreCase(elementStyle.getProperties().getOrDefault(PLANTUML_SHADOW, "false")) ); - plantUMLStyles.add(plantUMLElementStyle); + plantUMLStyles.add(plantUMLDeploymentNodeStyle); String icon = ""; if (isSupportedIcon(elementStyle.getIcon())) { @@ -270,7 +271,7 @@ protected void startDeploymentNodeBoundary(DeploymentView view, DeploymentNode d calculateMetadataFontSize(findElementStyle(view, deploymentNode).getFontSize()), typeOf(view, deploymentNode, true), icon, - classSelectorFor(elementStyle), + plantUMLDeploymentNodeStyle.getClassSelector(), idOf(deploymentNode), url ) @@ -324,6 +325,15 @@ public Diagram export(DynamicView view) { @Override protected void writeElement(ModelView view, Element element, IndentingWriter writer) { ElementStyle elementStyle = findElementStyle(view, element); + + if (elementStyle.getWidth() == null) { + elementStyle.setWidth(DEFAULT_WIDTH); + } + + if (elementStyle.getHeight() == null) { + elementStyle.setHeight(DEFAULT_HEIGHT); + } + String sequenceDiagramShape = plantumlSequenceType(view, element); if (renderAsSequenceDiagram(view)) { @@ -499,6 +509,28 @@ protected Legend createLegend(ModelView view) { writer.writeLine(""); writer.writeLine(); + plantUMLStyles.stream().sorted(Comparator.comparing(PlantUMLStyle::getName)).filter(style -> style instanceof PlantUMLDeploymentNodeStyle).map(style -> (PlantUMLDeploymentNodeStyle)style).forEach(style -> { + style.setWidth(200); + String description = style.getName(); + if (description.startsWith("Element,")) { + description = description.substring("Element,".length()); + } + description = description.replaceAll(",", ", "); + + String icon = ""; + if (isSupportedIcon(style.getIcon())) { + double scale = calculateIconScale(style.getIcon(), style.getFontSize() * MAX_ICON_SIZE_RATIO); + icon = "\\n\\n"; + } + + writer.writeLine(format("rectangle \"==%s%s\" <<%s>>", + description, + icon, + style.getClassSelector()) + ); + writer.writeLine(); + }); + plantUMLStyles.stream().sorted(Comparator.comparing(PlantUMLStyle::getName)).filter(style -> style instanceof PlantUMLElementStyle).map(style -> (PlantUMLElementStyle)style).forEach(style -> { style.setWidth(200); String description = style.getName(); @@ -556,14 +588,6 @@ protected boolean renderAsSequenceDiagram(ModelView view) { return view instanceof DynamicView && "true".equalsIgnoreCase(getViewOrViewSetProperty(view, PLANTUML_SEQUENCE_DIAGRAM_PROPERTY, "false")); } - private String classSelectorFor(ElementStyle elementStyle) { - return "Element-" + Base64.getEncoder().encodeToString(elementStyle.getTag().getBytes()); - } - - private String classSelectorForBoundary(Element element) { - return "Boundary-" + Base64.getEncoder().encodeToString(element.getName().getBytes()); - } - private ElementStyle findBoundaryStyle(ModelView view, Element element) { String background = colorScheme == ColorScheme.Dark ? Styles.DEFAULT_BACKGROUND_DARK : Styles.DEFAULT_BACKGROUND_LIGHT; String stroke = colorScheme == ColorScheme.Dark ? Styles.DEFAULT_COLOR_DARK : Styles.DEFAULT_COLOR_LIGHT; diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java index 62a37a4ed..9ab8c70f4 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java @@ -622,18 +622,6 @@ public void test_BigBankPlcExample() throws Exception { BackgroundColor: #ffffff; FontColor: #444444; } - // Element - .Element-RWxlbWVudA== { - BackgroundColor: #ffffff; - LineColor: #444444; - LineStyle: 0; - LineThickness: 2; - FontColor: #444444; - FontSize: 24; - HorizontalAlignment: center; - Shadowing: 0; - MaximumWidth: 450; - } // Element,Container .Element-RWxlbWVudCxDb250YWluZXI= { BackgroundColor: #438dd5; @@ -690,23 +678,34 @@ public void test_BigBankPlcExample() throws Exception { FontColor: #444444; FontSize: 24; } + // Element + .DeploymentNode-RWxlbWVudA== { + BackgroundColor: #ffffff; + LineColor: #444444; + LineStyle: 0; + LineThickness: 2; + FontColor: #444444; + FontSize: 24; + HorizontalAlignment: center; + Shadowing: 0; + } - rectangle "Developer Laptop\\n[Deployment Node: Microsoft Windows 10 or Apple macOS]" <> as Development.DeveloperLaptop { - rectangle "Web Browser\\n[Deployment Node: Chrome, Firefox, Safari, or Edge]" <> as Development.DeveloperLaptop.WebBrowser { + rectangle "Developer Laptop\\n[Deployment Node: Microsoft Windows 10 or Apple macOS]" <> as Development.DeveloperLaptop { + rectangle "Web Browser\\n[Deployment Node: Chrome, Firefox, Safari, or Edge]" <> as Development.DeveloperLaptop.WebBrowser { rectangle "==Single-Page Application\\n[Container: JavaScript and Angular]\\n\\nProvides all of the Internet banking functionality to customers via their web browser." <> as Development.DeveloperLaptop.WebBrowser.SinglePageApplication_1 } - rectangle "Docker Container - Web Server\\n[Deployment Node: Docker]" <> as Development.DeveloperLaptop.DockerContainerWebServer { - rectangle "Apache Tomcat\\n[Deployment Node: Apache Tomcat 8.x]" <> as Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat { + rectangle "Docker Container - Web Server\\n[Deployment Node: Docker]" <> as Development.DeveloperLaptop.DockerContainerWebServer { + rectangle "Apache Tomcat\\n[Deployment Node: Apache Tomcat 8.x]" <> as Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat { rectangle "==Web Application\\n[Container: Java and Spring MVC]\\n\\nDelivers the static content and the Internet banking single page application." <> as Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.WebApplication_1 rectangle "==API Application\\n[Container: Java and Spring MVC]\\n\\nProvides Internet banking functionality via a JSON/HTTPS API." <> as Development.DeveloperLaptop.DockerContainerWebServer.ApacheTomcat.APIApplication_1 } } - rectangle "Docker Container - Database Server\\n[Deployment Node: Docker]" <> as Development.DeveloperLaptop.DockerContainerDatabaseServer { - rectangle "Database Server\\n[Deployment Node: Oracle 12c]" <> as Development.DeveloperLaptop.DockerContainerDatabaseServer.DatabaseServer { + rectangle "Docker Container - Database Server\\n[Deployment Node: Docker]" <> as Development.DeveloperLaptop.DockerContainerDatabaseServer { + rectangle "Database Server\\n[Deployment Node: Oracle 12c]" <> as Development.DeveloperLaptop.DockerContainerDatabaseServer.DatabaseServer { database "==Database\\n[Container: Oracle Database Schema]\\n\\nStores user registration information, hashed authentication credentials, access logs, etc." <> as Development.DeveloperLaptop.DockerContainerDatabaseServer.DatabaseServer.Database_1 } @@ -714,8 +713,8 @@ public void test_BigBankPlcExample() throws Exception { } - rectangle "Big Bank plc\\n[Deployment Node: Big Bank plc data center]" <> as Development.BigBankplc { - rectangle "bigbank-dev001\\n[Deployment Node]" <> as Development.BigBankplc.bigbankdev001 { + rectangle "Big Bank plc\\n[Deployment Node: Big Bank plc data center]" <> as Development.BigBankplc { + rectangle "bigbank-dev001\\n[Deployment Node]" <> as Development.BigBankplc.bigbankdev001 { rectangle "==Mainframe Banking System\\n[Software System]\\n\\nStores all of the core banking information about customers, accounts, transactions, etc." <> as Development.BigBankplc.bigbankdev001.MainframeBankingSystem_1 } @@ -744,18 +743,6 @@ public void test_BigBankPlcExample() throws Exception { BackgroundColor: #ffffff; FontColor: #444444; } - // Element - .Element-RWxlbWVudA== { - BackgroundColor: #ffffff; - LineColor: #444444; - LineStyle: 0; - LineThickness: 2; - FontColor: #444444; - FontSize: 24; - HorizontalAlignment: center; - Shadowing: 0; - MaximumWidth: 450; - } // Element,Container .Element-RWxlbWVudCxDb250YWluZXI= { BackgroundColor: #438dd5; @@ -804,18 +791,6 @@ public void test_BigBankPlcExample() throws Exception { Shadowing: 0; MaximumWidth: 450; } - // Element,Failover - .Element-RWxlbWVudCxGYWlsb3Zlcg== { - BackgroundColor: #ffffff; - LineColor: #444444; - LineStyle: 0; - LineThickness: 2; - FontColor: #444444; - FontSize: 24; - HorizontalAlignment: center; - Shadowing: 0; - MaximumWidth: 450; - } // Element,Software System,Existing System .Element-RWxlbWVudCxTb2Z0d2FyZSBTeXN0ZW0sRXhpc3RpbmcgU3lzdGVt { BackgroundColor: #999999; @@ -836,49 +811,71 @@ public void test_BigBankPlcExample() throws Exception { FontColor: #444444; FontSize: 24; } + // Element + .DeploymentNode-RWxlbWVudA== { + BackgroundColor: #ffffff; + LineColor: #444444; + LineStyle: 0; + LineThickness: 2; + FontColor: #444444; + FontSize: 24; + HorizontalAlignment: center; + Shadowing: 0; + } + // Element,Failover + .DeploymentNode-RWxlbWVudCxGYWlsb3Zlcg== { + BackgroundColor: #ffffff; + LineColor: #444444; + LineStyle: 0; + LineThickness: 2; + FontColor: #444444; + FontSize: 24; + HorizontalAlignment: center; + Shadowing: 0; + } - rectangle "Customer's mobile device\\n[Deployment Node: Apple iOS or Android]" <> as Live.Customersmobiledevice { + rectangle "Customer's mobile device\\n[Deployment Node: Apple iOS or Android]" <> as Live.Customersmobiledevice { rectangle "==Mobile App\\n[Container: Xamarin]\\n\\nProvides a limited subset of the Internet banking functionality to customers via their mobile device." <> as Live.Customersmobiledevice.MobileApp_1 } - rectangle "Customer's computer\\n[Deployment Node: Microsoft Windows or Apple macOS]" <> as Live.Customerscomputer { - rectangle "Web Browser\\n[Deployment Node: Chrome, Firefox, Safari, or Edge]" <> as Live.Customerscomputer.WebBrowser { + rectangle "Customer's computer\\n[Deployment Node: Microsoft Windows or Apple macOS]" <> as Live.Customerscomputer { + rectangle "Web Browser\\n[Deployment Node: Chrome, Firefox, Safari, or Edge]" <> as Live.Customerscomputer.WebBrowser { rectangle "==Single-Page Application\\n[Container: JavaScript and Angular]\\n\\nProvides all of the Internet banking functionality to customers via their web browser." <> as Live.Customerscomputer.WebBrowser.SinglePageApplication_1 } } - rectangle "Big Bank plc\\n[Deployment Node: Big Bank plc data center]" <> as Live.BigBankplc { - rectangle "bigbank-web*** (x4)\\n[Deployment Node: Ubuntu 16.04 LTS]" <> as Live.BigBankplc.bigbankweb { - rectangle "Apache Tomcat\\n[Deployment Node: Apache Tomcat 8.x]" <> as Live.BigBankplc.bigbankweb.ApacheTomcat { + rectangle "Big Bank plc\\n[Deployment Node: Big Bank plc data center]" <> as Live.BigBankplc { + rectangle "bigbank-web*** (x4)\\n[Deployment Node: Ubuntu 16.04 LTS]" <> as Live.BigBankplc.bigbankweb { + rectangle "Apache Tomcat\\n[Deployment Node: Apache Tomcat 8.x]" <> as Live.BigBankplc.bigbankweb.ApacheTomcat { rectangle "==Web Application\\n[Container: Java and Spring MVC]\\n\\nDelivers the static content and the Internet banking single page application." <> as Live.BigBankplc.bigbankweb.ApacheTomcat.WebApplication_1 } } - rectangle "bigbank-api*** (x8)\\n[Deployment Node: Ubuntu 16.04 LTS]" <> as Live.BigBankplc.bigbankapi { - rectangle "Apache Tomcat\\n[Deployment Node: Apache Tomcat 8.x]" <> as Live.BigBankplc.bigbankapi.ApacheTomcat { + rectangle "bigbank-api*** (x8)\\n[Deployment Node: Ubuntu 16.04 LTS]" <> as Live.BigBankplc.bigbankapi { + rectangle "Apache Tomcat\\n[Deployment Node: Apache Tomcat 8.x]" <> as Live.BigBankplc.bigbankapi.ApacheTomcat { rectangle "==API Application\\n[Container: Java and Spring MVC]\\n\\nProvides Internet banking functionality via a JSON/HTTPS API." <> as Live.BigBankplc.bigbankapi.ApacheTomcat.APIApplication_1 } } - rectangle "bigbank-db01\\n[Deployment Node: Ubuntu 16.04 LTS]" <> as Live.BigBankplc.bigbankdb01 { - rectangle "Oracle - Primary\\n[Deployment Node: Oracle 12c]" <> as Live.BigBankplc.bigbankdb01.OraclePrimary { + rectangle "bigbank-db01\\n[Deployment Node: Ubuntu 16.04 LTS]" <> as Live.BigBankplc.bigbankdb01 { + rectangle "Oracle - Primary\\n[Deployment Node: Oracle 12c]" <> as Live.BigBankplc.bigbankdb01.OraclePrimary { database "==Database\\n[Container: Oracle Database Schema]\\n\\nStores user registration information, hashed authentication credentials, access logs, etc." <> as Live.BigBankplc.bigbankdb01.OraclePrimary.Database_1 } } - rectangle "bigbank-db02\\n[Deployment Node: Ubuntu 16.04 LTS]" <> as Live.BigBankplc.bigbankdb02 { - rectangle "Oracle - Secondary\\n[Deployment Node: Oracle 12c]" <> as Live.BigBankplc.bigbankdb02.OracleSecondary { + rectangle "bigbank-db02\\n[Deployment Node: Ubuntu 16.04 LTS]" <> as Live.BigBankplc.bigbankdb02 { + rectangle "Oracle - Secondary\\n[Deployment Node: Oracle 12c]" <> as Live.BigBankplc.bigbankdb02.OracleSecondary { database "==Database\\n[Container: Oracle Database Schema]\\n\\nStores user registration information, hashed authentication credentials, access logs, etc." <> as Live.BigBankplc.bigbankdb02.OracleSecondary.Database_1 } } - rectangle "bigbank-prod001\\n[Deployment Node]" <> as Live.BigBankplc.bigbankprod001 { + rectangle "bigbank-prod001\\n[Deployment Node]" <> as Live.BigBankplc.bigbankprod001 { rectangle "==Mainframe Banking System\\n[Software System]\\n\\nStores all of the core banking information about customers, accounts, transactions, etc." <> as Live.BigBankplc.bigbankprod001.MainframeBankingSystem_1 } @@ -1425,14 +1422,25 @@ public void deploymentView_NoStyling_Light() { Shadowing: 0; MaximumWidth: 450; } + // Element + .DeploymentNode-RWxlbWVudA== { + BackgroundColor: #ffffff; + LineColor: #444444; + LineStyle: 0; + LineThickness: 2; + FontColor: #444444; + FontSize: 24; + HorizontalAlignment: center; + Shadowing: 0; + } - rectangle "Node 1\\n[Deployment Node]" <> as Default.Node1 { - rectangle "Node 2\\n[Deployment Node]" <> as Default.Node1.Node2 { + rectangle "Node 1\\n[Deployment Node]" <> as Default.Node1 { + rectangle "Node 2\\n[Deployment Node]" <> as Default.Node1.Node2 { rectangle "==A\\n[Software System]" <> as Default.Node1.Node2.A_1 } - rectangle "Node 3\\n[Deployment Node]" <> as Default.Node1.Node3 { + rectangle "Node 3\\n[Deployment Node]" <> as Default.Node1.Node3 { rectangle "==Infrastructure Node\\n[Infrastructure Node]" <> as Default.Node1.Node3.InfrastructureNode } @@ -3333,6 +3341,17 @@ public void deploymentView_WithGroups() { Shadowing: 0; MaximumWidth: 450; } + // Element + .DeploymentNode-RWxlbWVudA== { + BackgroundColor: #ffffff; + LineColor: #444444; + LineStyle: 0; + LineThickness: 2; + FontColor: #444444; + FontSize: 24; + HorizontalAlignment: center; + Shadowing: 0; + } // Group 1 .Group-R3JvdXAgMQ== { BackgroundColor: #ffffff; @@ -3358,7 +3377,7 @@ public void deploymentView_WithGroups() { rectangle "Group 1" <> as groupR3JvdXAgMQ== { - rectangle "Server 1\\n[Deployment Node]" <> as Default.Server1 { + rectangle "Server 1\\n[Deployment Node]" <> as Default.Server1 { rectangle "Group 2" <> as groupR3JvdXAgMg== { rectangle "==Infrastructure Node 2\\n[Infrastructure Node]" <> as Default.Server1.InfrastructureNode2 rectangle "==Software System\\n[Software System]" <> as Default.Server1.SoftwareSystem_1 @@ -3512,148 +3531,142 @@ public void amazonWebServicesExample_Light() throws Exception { BackgroundColor: #ffffff; FontColor: #444444; } - // Element,Amazon Web Services - Auto Scaling - .Element-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gQXV0byBTY2FsaW5n { + // Element,Amazon Web Services - Elastic Load Balancing + .Element-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gRWxhc3RpYyBMb2FkIEJhbGFuY2luZw== { BackgroundColor: #ffffff; - LineColor: #cc2264; + LineColor: #693cc5; LineStyle: 0; LineThickness: 2; - FontColor: #cc2264; + FontColor: #693cc5; FontSize: 24; HorizontalAlignment: center; Shadowing: 0; MaximumWidth: 450; } - // Element,Amazon Web Services - Cloud - .Element-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gQ2xvdWQ= { + // Element,Amazon Web Services - Route 53 + .Element-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gUm91dGUgNTM= { BackgroundColor: #ffffff; - LineColor: #232f3e; + LineColor: #693cc5; LineStyle: 0; LineThickness: 2; - FontColor: #232f3e; + FontColor: #693cc5; FontSize: 24; HorizontalAlignment: center; Shadowing: 0; MaximumWidth: 450; } - // Element,Amazon Web Services - EC2 - .Element-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gRUMy { + // Element,Application + .Element-RWxlbWVudCxBcHBsaWNhdGlvbg== { BackgroundColor: #ffffff; - LineColor: #d86613; + LineColor: #444444; LineStyle: 0; LineThickness: 2; - FontColor: #d86613; + RoundCorner: 20; + FontColor: #444444; FontSize: 24; HorizontalAlignment: center; Shadowing: 0; MaximumWidth: 450; } - // Element,Amazon Web Services - Elastic Load Balancing - .Element-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gRWxhc3RpYyBMb2FkIEJhbGFuY2luZw== { + // Element,Database + .Element-RWxlbWVudCxEYXRhYmFzZQ== { BackgroundColor: #ffffff; - LineColor: #693cc5; + LineColor: #444444; LineStyle: 0; LineThickness: 2; - FontColor: #693cc5; + FontColor: #444444; FontSize: 24; HorizontalAlignment: center; Shadowing: 0; MaximumWidth: 450; } - // Element,Amazon Web Services - RDS - .Element-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gUkRT { + // Relationship + .Relationship-UmVsYXRpb25zaGlw { + LineThickness: 2; + LineStyle: 10-10; + LineColor: #444444; + FontColor: #444444; + FontSize: 24; + } + // Element,Amazon Web Services - Auto Scaling + .DeploymentNode-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gQXV0byBTY2FsaW5n { BackgroundColor: #ffffff; - LineColor: #3b48cc; + LineColor: #cc2264; LineStyle: 0; LineThickness: 2; - FontColor: #3b48cc; + FontColor: #cc2264; FontSize: 24; HorizontalAlignment: center; Shadowing: 0; - MaximumWidth: 450; } - // Element,Amazon Web Services - RDS MySQL instance - .Element-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gUkRTIE15U1FMIGluc3RhbmNl { + // Element,Amazon Web Services - Cloud + .DeploymentNode-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gQ2xvdWQ= { BackgroundColor: #ffffff; - LineColor: #3b48cc; + LineColor: #232f3e; LineStyle: 0; LineThickness: 2; - FontColor: #3b48cc; + FontColor: #232f3e; FontSize: 24; HorizontalAlignment: center; Shadowing: 0; - MaximumWidth: 450; } - // Element,Amazon Web Services - Region - .Element-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gUmVnaW9u { + // Element,Amazon Web Services - EC2 + .DeploymentNode-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gRUMy { BackgroundColor: #ffffff; - LineColor: #147eba; + LineColor: #d86613; LineStyle: 0; LineThickness: 2; - FontColor: #147eba; + FontColor: #d86613; FontSize: 24; HorizontalAlignment: center; Shadowing: 0; - MaximumWidth: 450; } - // Element,Amazon Web Services - Route 53 - .Element-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gUm91dGUgNTM= { + // Element,Amazon Web Services - RDS + .DeploymentNode-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gUkRT { BackgroundColor: #ffffff; - LineColor: #693cc5; + LineColor: #3b48cc; LineStyle: 0; LineThickness: 2; - FontColor: #693cc5; + FontColor: #3b48cc; FontSize: 24; HorizontalAlignment: center; Shadowing: 0; - MaximumWidth: 450; } - // Element,Application - .Element-RWxlbWVudCxBcHBsaWNhdGlvbg== { + // Element,Amazon Web Services - RDS MySQL instance + .DeploymentNode-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gUkRTIE15U1FMIGluc3RhbmNl { BackgroundColor: #ffffff; - LineColor: #444444; + LineColor: #3b48cc; LineStyle: 0; LineThickness: 2; - RoundCorner: 20; - FontColor: #444444; + FontColor: #3b48cc; FontSize: 24; HorizontalAlignment: center; Shadowing: 0; - MaximumWidth: 450; } - // Element,Database - .Element-RWxlbWVudCxEYXRhYmFzZQ== { + // Element,Amazon Web Services - Region + .DeploymentNode-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gUmVnaW9u { BackgroundColor: #ffffff; - LineColor: #444444; + LineColor: #147eba; LineStyle: 0; LineThickness: 2; - FontColor: #444444; + FontColor: #147eba; FontSize: 24; HorizontalAlignment: center; Shadowing: 0; - MaximumWidth: 450; - } - // Relationship - .Relationship-UmVsYXRpb25zaGlw { - LineThickness: 2; - LineStyle: 10-10; - LineColor: #444444; - FontColor: #444444; - FontSize: 24; } - rectangle "Amazon Web Services\\n[Deployment Node]\\n\\n" <> as Live.AmazonWebServices { - rectangle "US-East-1\\n[Deployment Node]\\n\\n" <> as Live.AmazonWebServices.USEast1 { - rectangle "Autoscaling group\\n[Deployment Node]\\n\\n" <> as Live.AmazonWebServices.USEast1.Autoscalinggroup { - rectangle "Amazon EC2 - Ubuntu server\\n[Deployment Node]\\n\\n" <> as Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2Ubuntuserver { + rectangle "Amazon Web Services\\n[Deployment Node]\\n\\n" <> as Live.AmazonWebServices { + rectangle "US-East-1\\n[Deployment Node]\\n\\n" <> as Live.AmazonWebServices.USEast1 { + rectangle "Autoscaling group\\n[Deployment Node]\\n\\n" <> as Live.AmazonWebServices.USEast1.Autoscalinggroup { + rectangle "Amazon EC2 - Ubuntu server\\n[Deployment Node]\\n\\n" <> as Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2Ubuntuserver { rectangle "==Web Application\\n[Container: Java and Spring Boot]" <> as Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2Ubuntuserver.WebApplication_1 } } - rectangle "Amazon RDS\\n[Deployment Node]\\n\\n" <> as Live.AmazonWebServices.USEast1.AmazonRDS { - rectangle "MySQL\\n[Deployment Node]\\n\\n" <> as Live.AmazonWebServices.USEast1.AmazonRDS.MySQL { + rectangle "Amazon RDS\\n[Deployment Node]\\n\\n" <> as Live.AmazonWebServices.USEast1.AmazonRDS { + rectangle "MySQL\\n[Deployment Node]\\n\\n" <> as Live.AmazonWebServices.USEast1.AmazonRDS.MySQL { database "==Database Schema\\n[Container]" <> as Live.AmazonWebServices.USEast1.AmazonRDS.MySQL.DatabaseSchema_1 } @@ -3682,135 +3695,135 @@ public void amazonWebServicesExample_Light() throws Exception { BackgroundColor: #ffffff; FontColor: #444444; } - // Element,Amazon Web Services - Auto Scaling - .Element-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gQXV0byBTY2FsaW5n { + // Element,Amazon Web Services - Elastic Load Balancing + .Element-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gRWxhc3RpYyBMb2FkIEJhbGFuY2luZw== { BackgroundColor: #ffffff; - LineColor: #cc2264; + LineColor: #693cc5; LineStyle: 0; LineThickness: 2; - FontColor: #cc2264; + FontColor: #693cc5; FontSize: 24; HorizontalAlignment: center; Shadowing: 0; MaximumWidth: 200; } - // Element,Amazon Web Services - Cloud - .Element-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gQ2xvdWQ= { + // Element,Amazon Web Services - Route 53 + .Element-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gUm91dGUgNTM= { BackgroundColor: #ffffff; - LineColor: #232f3e; + LineColor: #693cc5; LineStyle: 0; LineThickness: 2; - FontColor: #232f3e; + FontColor: #693cc5; FontSize: 24; HorizontalAlignment: center; Shadowing: 0; MaximumWidth: 200; } - // Element,Amazon Web Services - EC2 - .Element-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gRUMy { + // Element,Application + .Element-RWxlbWVudCxBcHBsaWNhdGlvbg== { BackgroundColor: #ffffff; - LineColor: #d86613; + LineColor: #444444; LineStyle: 0; LineThickness: 2; - FontColor: #d86613; + RoundCorner: 20; + FontColor: #444444; FontSize: 24; HorizontalAlignment: center; Shadowing: 0; MaximumWidth: 200; } - // Element,Amazon Web Services - Elastic Load Balancing - .Element-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gRWxhc3RpYyBMb2FkIEJhbGFuY2luZw== { + // Element,Database + .Element-RWxlbWVudCxEYXRhYmFzZQ== { BackgroundColor: #ffffff; - LineColor: #693cc5; + LineColor: #444444; LineStyle: 0; LineThickness: 2; - FontColor: #693cc5; + FontColor: #444444; FontSize: 24; HorizontalAlignment: center; Shadowing: 0; MaximumWidth: 200; } - // Element,Amazon Web Services - RDS - .Element-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gUkRT { + // Relationship + .Relationship-UmVsYXRpb25zaGlw { + LineThickness: 2; + LineStyle: 10-10; + LineColor: #444444; + FontColor: #444444; + FontSize: 24; + } + // Element,Amazon Web Services - Auto Scaling + .DeploymentNode-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gQXV0byBTY2FsaW5n { BackgroundColor: #ffffff; - LineColor: #3b48cc; + LineColor: #cc2264; LineStyle: 0; LineThickness: 2; - FontColor: #3b48cc; + FontColor: #cc2264; FontSize: 24; HorizontalAlignment: center; Shadowing: 0; MaximumWidth: 200; } - // Element,Amazon Web Services - RDS MySQL instance - .Element-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gUkRTIE15U1FMIGluc3RhbmNl { + // Element,Amazon Web Services - Cloud + .DeploymentNode-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gQ2xvdWQ= { BackgroundColor: #ffffff; - LineColor: #3b48cc; + LineColor: #232f3e; LineStyle: 0; LineThickness: 2; - FontColor: #3b48cc; + FontColor: #232f3e; FontSize: 24; HorizontalAlignment: center; Shadowing: 0; MaximumWidth: 200; } - // Element,Amazon Web Services - Region - .Element-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gUmVnaW9u { + // Element,Amazon Web Services - EC2 + .DeploymentNode-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gRUMy { BackgroundColor: #ffffff; - LineColor: #147eba; + LineColor: #d86613; LineStyle: 0; LineThickness: 2; - FontColor: #147eba; + FontColor: #d86613; FontSize: 24; HorizontalAlignment: center; Shadowing: 0; MaximumWidth: 200; } - // Element,Amazon Web Services - Route 53 - .Element-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gUm91dGUgNTM= { + // Element,Amazon Web Services - RDS + .DeploymentNode-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gUkRT { BackgroundColor: #ffffff; - LineColor: #693cc5; + LineColor: #3b48cc; LineStyle: 0; LineThickness: 2; - FontColor: #693cc5; + FontColor: #3b48cc; FontSize: 24; HorizontalAlignment: center; Shadowing: 0; MaximumWidth: 200; } - // Element,Application - .Element-RWxlbWVudCxBcHBsaWNhdGlvbg== { + // Element,Amazon Web Services - RDS MySQL instance + .DeploymentNode-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gUkRTIE15U1FMIGluc3RhbmNl { BackgroundColor: #ffffff; - LineColor: #444444; + LineColor: #3b48cc; LineStyle: 0; LineThickness: 2; - RoundCorner: 20; - FontColor: #444444; + FontColor: #3b48cc; FontSize: 24; HorizontalAlignment: center; Shadowing: 0; MaximumWidth: 200; } - // Element,Database - .Element-RWxlbWVudCxEYXRhYmFzZQ== { + // Element,Amazon Web Services - Region + .DeploymentNode-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gUmVnaW9u { BackgroundColor: #ffffff; - LineColor: #444444; + LineColor: #147eba; LineStyle: 0; LineThickness: 2; - FontColor: #444444; + FontColor: #147eba; FontSize: 24; HorizontalAlignment: center; Shadowing: 0; MaximumWidth: 200; } - // Relationship - .Relationship-UmVsYXRpb25zaGlw { - LineThickness: 2; - LineStyle: 10-10; - LineColor: #444444; - FontColor: #444444; - FontSize: 24; - } // transparent element for relationships in legend .Element-Transparent { BackgroundColor: transparent; @@ -3819,19 +3832,19 @@ public void amazonWebServicesExample_Light() throws Exception { } - rectangle "==Amazon Web Services - Auto Scaling\\n\\n" <> + rectangle "==Amazon Web Services - Auto Scaling\\n\\n" <> - rectangle "==Amazon Web Services - Cloud\\n\\n" <> + rectangle "==Amazon Web Services - Cloud\\n\\n" <> - rectangle "==Amazon Web Services - EC2\\n\\n" <> + rectangle "==Amazon Web Services - EC2\\n\\n" <> - rectangle "==Amazon Web Services - Elastic Load Balancing\\n\\n" <> + rectangle "==Amazon Web Services - RDS\\n\\n" <> - rectangle "==Amazon Web Services - RDS\\n\\n" <> + rectangle "==Amazon Web Services - RDS MySQL instance\\n\\n" <> - rectangle "==Amazon Web Services - RDS MySQL instance\\n\\n" <> + rectangle "==Amazon Web Services - Region\\n\\n" <> - rectangle "==Amazon Web Services - Region\\n\\n" <> + rectangle "==Amazon Web Services - Elastic Load Balancing\\n\\n" <> rectangle "==Amazon Web Services - Route 53\\n\\n" <> @@ -3871,148 +3884,142 @@ public void amazonWebServicesExample_Dark() throws Exception { BackgroundColor: #111111; FontColor: #cccccc; } - // Element,Amazon Web Services - Auto Scaling - .Element-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gQXV0byBTY2FsaW5n { + // Element,Amazon Web Services - Elastic Load Balancing + .Element-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gRWxhc3RpYyBMb2FkIEJhbGFuY2luZw== { BackgroundColor: #111111; - LineColor: #cc2264; + LineColor: #693cc5; LineStyle: 0; LineThickness: 2; - FontColor: #cc2264; + FontColor: #693cc5; FontSize: 24; HorizontalAlignment: center; Shadowing: 0; MaximumWidth: 450; } - // Element,Amazon Web Services - Cloud - .Element-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gQ2xvdWQ= { + // Element,Amazon Web Services - Route 53 + .Element-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gUm91dGUgNTM= { BackgroundColor: #111111; - LineColor: #232f3e; + LineColor: #693cc5; LineStyle: 0; LineThickness: 2; - FontColor: #232f3e; + FontColor: #693cc5; FontSize: 24; HorizontalAlignment: center; Shadowing: 0; MaximumWidth: 450; } - // Element,Amazon Web Services - EC2 - .Element-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gRUMy { + // Element,Application + .Element-RWxlbWVudCxBcHBsaWNhdGlvbg== { BackgroundColor: #111111; - LineColor: #d86613; + LineColor: #cccccc; LineStyle: 0; LineThickness: 2; - FontColor: #d86613; + RoundCorner: 20; + FontColor: #cccccc; FontSize: 24; HorizontalAlignment: center; Shadowing: 0; MaximumWidth: 450; } - // Element,Amazon Web Services - Elastic Load Balancing - .Element-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gRWxhc3RpYyBMb2FkIEJhbGFuY2luZw== { + // Element,Database + .Element-RWxlbWVudCxEYXRhYmFzZQ== { BackgroundColor: #111111; - LineColor: #693cc5; + LineColor: #cccccc; LineStyle: 0; LineThickness: 2; - FontColor: #693cc5; + FontColor: #cccccc; FontSize: 24; HorizontalAlignment: center; Shadowing: 0; MaximumWidth: 450; } - // Element,Amazon Web Services - RDS - .Element-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gUkRT { + // Relationship + .Relationship-UmVsYXRpb25zaGlw { + LineThickness: 2; + LineStyle: 10-10; + LineColor: #cccccc; + FontColor: #cccccc; + FontSize: 24; + } + // Element,Amazon Web Services - Auto Scaling + .DeploymentNode-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gQXV0byBTY2FsaW5n { BackgroundColor: #111111; - LineColor: #3b48cc; + LineColor: #cc2264; LineStyle: 0; LineThickness: 2; - FontColor: #3b48cc; + FontColor: #cc2264; FontSize: 24; HorizontalAlignment: center; Shadowing: 0; - MaximumWidth: 450; } - // Element,Amazon Web Services - RDS MySQL instance - .Element-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gUkRTIE15U1FMIGluc3RhbmNl { + // Element,Amazon Web Services - Cloud + .DeploymentNode-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gQ2xvdWQ= { BackgroundColor: #111111; - LineColor: #3b48cc; + LineColor: #232f3e; LineStyle: 0; LineThickness: 2; - FontColor: #3b48cc; + FontColor: #232f3e; FontSize: 24; HorizontalAlignment: center; Shadowing: 0; - MaximumWidth: 450; } - // Element,Amazon Web Services - Region - .Element-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gUmVnaW9u { + // Element,Amazon Web Services - EC2 + .DeploymentNode-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gRUMy { BackgroundColor: #111111; - LineColor: #147eba; + LineColor: #d86613; LineStyle: 0; LineThickness: 2; - FontColor: #147eba; + FontColor: #d86613; FontSize: 24; HorizontalAlignment: center; Shadowing: 0; - MaximumWidth: 450; } - // Element,Amazon Web Services - Route 53 - .Element-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gUm91dGUgNTM= { + // Element,Amazon Web Services - RDS + .DeploymentNode-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gUkRT { BackgroundColor: #111111; - LineColor: #693cc5; + LineColor: #3b48cc; LineStyle: 0; LineThickness: 2; - FontColor: #693cc5; + FontColor: #3b48cc; FontSize: 24; HorizontalAlignment: center; Shadowing: 0; - MaximumWidth: 450; } - // Element,Application - .Element-RWxlbWVudCxBcHBsaWNhdGlvbg== { + // Element,Amazon Web Services - RDS MySQL instance + .DeploymentNode-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gUkRTIE15U1FMIGluc3RhbmNl { BackgroundColor: #111111; - LineColor: #cccccc; + LineColor: #3b48cc; LineStyle: 0; LineThickness: 2; - RoundCorner: 20; - FontColor: #cccccc; + FontColor: #3b48cc; FontSize: 24; HorizontalAlignment: center; Shadowing: 0; - MaximumWidth: 450; } - // Element,Database - .Element-RWxlbWVudCxEYXRhYmFzZQ== { + // Element,Amazon Web Services - Region + .DeploymentNode-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gUmVnaW9u { BackgroundColor: #111111; - LineColor: #cccccc; + LineColor: #147eba; LineStyle: 0; LineThickness: 2; - FontColor: #cccccc; + FontColor: #147eba; FontSize: 24; HorizontalAlignment: center; Shadowing: 0; - MaximumWidth: 450; - } - // Relationship - .Relationship-UmVsYXRpb25zaGlw { - LineThickness: 2; - LineStyle: 10-10; - LineColor: #cccccc; - FontColor: #cccccc; - FontSize: 24; } - rectangle "Amazon Web Services\\n[Deployment Node]\\n\\n" <> as Live.AmazonWebServices { - rectangle "US-East-1\\n[Deployment Node]\\n\\n" <> as Live.AmazonWebServices.USEast1 { - rectangle "Autoscaling group\\n[Deployment Node]\\n\\n" <> as Live.AmazonWebServices.USEast1.Autoscalinggroup { - rectangle "Amazon EC2 - Ubuntu server\\n[Deployment Node]\\n\\n" <> as Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2Ubuntuserver { + rectangle "Amazon Web Services\\n[Deployment Node]\\n\\n" <> as Live.AmazonWebServices { + rectangle "US-East-1\\n[Deployment Node]\\n\\n" <> as Live.AmazonWebServices.USEast1 { + rectangle "Autoscaling group\\n[Deployment Node]\\n\\n" <> as Live.AmazonWebServices.USEast1.Autoscalinggroup { + rectangle "Amazon EC2 - Ubuntu server\\n[Deployment Node]\\n\\n" <> as Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2Ubuntuserver { rectangle "==Web Application\\n[Container: Java and Spring Boot]" <> as Live.AmazonWebServices.USEast1.Autoscalinggroup.AmazonEC2Ubuntuserver.WebApplication_1 } } - rectangle "Amazon RDS\\n[Deployment Node]\\n\\n" <> as Live.AmazonWebServices.USEast1.AmazonRDS { - rectangle "MySQL\\n[Deployment Node]\\n\\n" <> as Live.AmazonWebServices.USEast1.AmazonRDS.MySQL { + rectangle "Amazon RDS\\n[Deployment Node]\\n\\n" <> as Live.AmazonWebServices.USEast1.AmazonRDS { + rectangle "MySQL\\n[Deployment Node]\\n\\n" <> as Live.AmazonWebServices.USEast1.AmazonRDS.MySQL { database "==Database Schema\\n[Container]" <> as Live.AmazonWebServices.USEast1.AmazonRDS.MySQL.DatabaseSchema_1 } @@ -4041,135 +4048,135 @@ public void amazonWebServicesExample_Dark() throws Exception { BackgroundColor: #111111; FontColor: #cccccc; } - // Element,Amazon Web Services - Auto Scaling - .Element-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gQXV0byBTY2FsaW5n { + // Element,Amazon Web Services - Elastic Load Balancing + .Element-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gRWxhc3RpYyBMb2FkIEJhbGFuY2luZw== { BackgroundColor: #111111; - LineColor: #cc2264; + LineColor: #693cc5; LineStyle: 0; LineThickness: 2; - FontColor: #cc2264; + FontColor: #693cc5; FontSize: 24; HorizontalAlignment: center; Shadowing: 0; MaximumWidth: 200; } - // Element,Amazon Web Services - Cloud - .Element-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gQ2xvdWQ= { + // Element,Amazon Web Services - Route 53 + .Element-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gUm91dGUgNTM= { BackgroundColor: #111111; - LineColor: #232f3e; + LineColor: #693cc5; LineStyle: 0; LineThickness: 2; - FontColor: #232f3e; + FontColor: #693cc5; FontSize: 24; HorizontalAlignment: center; Shadowing: 0; MaximumWidth: 200; } - // Element,Amazon Web Services - EC2 - .Element-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gRUMy { + // Element,Application + .Element-RWxlbWVudCxBcHBsaWNhdGlvbg== { BackgroundColor: #111111; - LineColor: #d86613; + LineColor: #cccccc; LineStyle: 0; LineThickness: 2; - FontColor: #d86613; + RoundCorner: 20; + FontColor: #cccccc; FontSize: 24; HorizontalAlignment: center; Shadowing: 0; MaximumWidth: 200; } - // Element,Amazon Web Services - Elastic Load Balancing - .Element-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gRWxhc3RpYyBMb2FkIEJhbGFuY2luZw== { + // Element,Database + .Element-RWxlbWVudCxEYXRhYmFzZQ== { BackgroundColor: #111111; - LineColor: #693cc5; + LineColor: #cccccc; LineStyle: 0; LineThickness: 2; - FontColor: #693cc5; + FontColor: #cccccc; FontSize: 24; HorizontalAlignment: center; Shadowing: 0; MaximumWidth: 200; } - // Element,Amazon Web Services - RDS - .Element-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gUkRT { + // Relationship + .Relationship-UmVsYXRpb25zaGlw { + LineThickness: 2; + LineStyle: 10-10; + LineColor: #cccccc; + FontColor: #cccccc; + FontSize: 24; + } + // Element,Amazon Web Services - Auto Scaling + .DeploymentNode-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gQXV0byBTY2FsaW5n { BackgroundColor: #111111; - LineColor: #3b48cc; + LineColor: #cc2264; LineStyle: 0; LineThickness: 2; - FontColor: #3b48cc; + FontColor: #cc2264; FontSize: 24; HorizontalAlignment: center; Shadowing: 0; MaximumWidth: 200; } - // Element,Amazon Web Services - RDS MySQL instance - .Element-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gUkRTIE15U1FMIGluc3RhbmNl { + // Element,Amazon Web Services - Cloud + .DeploymentNode-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gQ2xvdWQ= { BackgroundColor: #111111; - LineColor: #3b48cc; + LineColor: #232f3e; LineStyle: 0; LineThickness: 2; - FontColor: #3b48cc; + FontColor: #232f3e; FontSize: 24; HorizontalAlignment: center; Shadowing: 0; MaximumWidth: 200; } - // Element,Amazon Web Services - Region - .Element-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gUmVnaW9u { + // Element,Amazon Web Services - EC2 + .DeploymentNode-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gRUMy { BackgroundColor: #111111; - LineColor: #147eba; + LineColor: #d86613; LineStyle: 0; LineThickness: 2; - FontColor: #147eba; + FontColor: #d86613; FontSize: 24; HorizontalAlignment: center; Shadowing: 0; MaximumWidth: 200; } - // Element,Amazon Web Services - Route 53 - .Element-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gUm91dGUgNTM= { + // Element,Amazon Web Services - RDS + .DeploymentNode-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gUkRT { BackgroundColor: #111111; - LineColor: #693cc5; + LineColor: #3b48cc; LineStyle: 0; LineThickness: 2; - FontColor: #693cc5; + FontColor: #3b48cc; FontSize: 24; HorizontalAlignment: center; Shadowing: 0; MaximumWidth: 200; } - // Element,Application - .Element-RWxlbWVudCxBcHBsaWNhdGlvbg== { + // Element,Amazon Web Services - RDS MySQL instance + .DeploymentNode-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gUkRTIE15U1FMIGluc3RhbmNl { BackgroundColor: #111111; - LineColor: #cccccc; + LineColor: #3b48cc; LineStyle: 0; LineThickness: 2; - RoundCorner: 20; - FontColor: #cccccc; + FontColor: #3b48cc; FontSize: 24; HorizontalAlignment: center; Shadowing: 0; MaximumWidth: 200; } - // Element,Database - .Element-RWxlbWVudCxEYXRhYmFzZQ== { + // Element,Amazon Web Services - Region + .DeploymentNode-RWxlbWVudCxBbWF6b24gV2ViIFNlcnZpY2VzIC0gUmVnaW9u { BackgroundColor: #111111; - LineColor: #cccccc; + LineColor: #147eba; LineStyle: 0; LineThickness: 2; - FontColor: #cccccc; + FontColor: #147eba; FontSize: 24; HorizontalAlignment: center; Shadowing: 0; MaximumWidth: 200; } - // Relationship - .Relationship-UmVsYXRpb25zaGlw { - LineThickness: 2; - LineStyle: 10-10; - LineColor: #cccccc; - FontColor: #cccccc; - FontSize: 24; - } // transparent element for relationships in legend .Element-Transparent { BackgroundColor: transparent; @@ -4178,19 +4185,19 @@ public void amazonWebServicesExample_Dark() throws Exception { } - rectangle "==Amazon Web Services - Auto Scaling\\n\\n" <> + rectangle "==Amazon Web Services - Auto Scaling\\n\\n" <> - rectangle "==Amazon Web Services - Cloud\\n\\n" <> + rectangle "==Amazon Web Services - Cloud\\n\\n" <> - rectangle "==Amazon Web Services - EC2\\n\\n" <> + rectangle "==Amazon Web Services - EC2\\n\\n" <> - rectangle "==Amazon Web Services - Elastic Load Balancing\\n\\n" <> + rectangle "==Amazon Web Services - RDS\\n\\n" <> - rectangle "==Amazon Web Services - RDS\\n\\n" <> + rectangle "==Amazon Web Services - RDS MySQL instance\\n\\n" <> - rectangle "==Amazon Web Services - RDS MySQL instance\\n\\n" <> + rectangle "==Amazon Web Services - Region\\n\\n" <> - rectangle "==Amazon Web Services - Region\\n\\n" <> + rectangle "==Amazon Web Services - Elastic Load Balancing\\n\\n" <> rectangle "==Amazon Web Services - Route 53\\n\\n" <> From 2b8191a839ef16484cb978d3d452c82f821e35ef Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 30 Sep 2025 13:08:23 +0100 Subject: [PATCH 694/717] structurizr-inspection: Fixes a bug preventing inspection severity to be specified via linked relationships. --- changelog.md | 1 + .../PropertyBasedSeverityStrategy.java | 2 +- .../PropertyBasedSeverityStrategyTests.java | 22 +++++++++++++++---- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/changelog.md b/changelog.md index ec4b6548e..17922f372 100644 --- a/changelog.md +++ b/changelog.md @@ -29,6 +29,7 @@ - structurizr-inspection: Adds a way to disable inspections via a workspace property named `structurizr.inspection` (`false` to disable). - structurizr-inspection: Default inspector adds a summary of error/warning/info/ignore counts as workspace properties. - structurizr-inspection: Fixes `model.deploymentnode.technology` (it was checking the description property rather than technology). +- structurizr-inspection: Fixes a bug preventing inspection severity to be specified via linked relationships. ## v4.1.0 (28th May 2025) diff --git a/structurizr-inspection/src/main/java/com/structurizr/inspection/PropertyBasedSeverityStrategy.java b/structurizr-inspection/src/main/java/com/structurizr/inspection/PropertyBasedSeverityStrategy.java index 755a1c703..0483cba69 100644 --- a/structurizr-inspection/src/main/java/com/structurizr/inspection/PropertyBasedSeverityStrategy.java +++ b/structurizr-inspection/src/main/java/com/structurizr/inspection/PropertyBasedSeverityStrategy.java @@ -69,7 +69,7 @@ public Severity getSeverity(Inspection inspection, Relationship relationship) { Element source = relationship.getSource(); Relationship linkedRelationship = null; if (!StringUtils.isNullOrEmpty(relationship.getLinkedRelationshipId())) { - inspection.getWorkspace().getModel().getRelationship(relationship.getLinkedRelationshipId()); + linkedRelationship = inspection.getWorkspace().getModel().getRelationship(relationship.getLinkedRelationshipId()); } String allRelationshipsType = inspection.getType(); diff --git a/structurizr-inspection/src/test/java/com/structurizr/inspection/PropertyBasedSeverityStrategyTests.java b/structurizr-inspection/src/test/java/com/structurizr/inspection/PropertyBasedSeverityStrategyTests.java index e99e4b57e..e6bf91594 100644 --- a/structurizr-inspection/src/test/java/com/structurizr/inspection/PropertyBasedSeverityStrategyTests.java +++ b/structurizr-inspection/src/test/java/com/structurizr/inspection/PropertyBasedSeverityStrategyTests.java @@ -5,10 +5,7 @@ import com.structurizr.inspection.model.RelationshipDescriptionInspection; import com.structurizr.inspection.model.RelationshipTechnologyInspection; import com.structurizr.inspection.workspace.WorkspaceScopeInspection; -import com.structurizr.model.Component; -import com.structurizr.model.Container; -import com.structurizr.model.Relationship; -import com.structurizr.model.SoftwareSystem; +import com.structurizr.model.*; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -254,4 +251,21 @@ void getSeverityForRelationship_BetweenComponents_WhenSpecifiedByRelationshipTyp assertEquals(Severity.INFO, severityStrategy.getSeverity(inspection, relationship)); } + @Test + void getSeverityForRelationship_WhenSpecifiedInLinkedRelationship() { + inspection = new RelationshipTechnologyInspection(inspector); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Software System"); + workspace.getModel().setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); + Container container1 = softwareSystem.addContainer("Container 1"); + Component component1 = container1.addComponent("Component 1"); + Container container2 = softwareSystem.addContainer("Container 2"); + Component component2 = container2.addComponent("Component 2"); + Relationship relationship = component1.uses(component2, ""); + Relationship impliedRelationship = container1.getEfferentRelationshipWith(container2); + + // specify in original relationship + relationship.addProperty("structurizr.inspection.model.relationship.technology", "info"); + assertEquals(Severity.INFO, severityStrategy.getSeverity(inspection, impliedRelationship)); + } + } \ No newline at end of file From ba4fd46c4f4a8007ef95fc119ea878337f00a794 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 18 Oct 2025 14:52:57 +0100 Subject: [PATCH 695/717] Fixes intermittently failing tests. --- .../encryption/AesEncryptionStrategy.java | 3 +- .../AesEncryptionStrategyTests.java | 57 +++++++++---------- 2 files changed, 29 insertions(+), 31 deletions(-) diff --git a/structurizr-client/src/main/java/com/structurizr/encryption/AesEncryptionStrategy.java b/structurizr-client/src/main/java/com/structurizr/encryption/AesEncryptionStrategy.java index 2279304e2..4f43b7862 100644 --- a/structurizr-client/src/main/java/com/structurizr/encryption/AesEncryptionStrategy.java +++ b/structurizr-client/src/main/java/com/structurizr/encryption/AesEncryptionStrategy.java @@ -7,6 +7,7 @@ import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.SecretKeySpec; import javax.xml.bind.DatatypeConverter; +import java.nio.charset.StandardCharsets; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.spec.InvalidKeySpecException; @@ -79,7 +80,7 @@ public String decrypt(String ciphertext) throws Exception { cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(DatatypeConverter.parseHexBinary(iv))); byte[] unencrypted = cipher.doFinal(Base64.getDecoder().decode(ciphertext)); - return new String(unencrypted, "UTF-8"); + return new String(unencrypted, StandardCharsets.UTF_8); } private SecretKey createSecretKey() throws NoSuchAlgorithmException, InvalidKeySpecException { diff --git a/structurizr-client/src/test/java/com/structurizr/encryption/AesEncryptionStrategyTests.java b/structurizr-client/src/test/java/com/structurizr/encryption/AesEncryptionStrategyTests.java index 3af514905..33171b4b3 100644 --- a/structurizr-client/src/test/java/com/structurizr/encryption/AesEncryptionStrategyTests.java +++ b/structurizr-client/src/test/java/com/structurizr/encryption/AesEncryptionStrategyTests.java @@ -2,33 +2,30 @@ import org.junit.jupiter.api.Test; -import javax.crypto.BadPaddingException; -import java.security.InvalidKeyException; - -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; public class AesEncryptionStrategyTests { @Test void encrypt_EncryptsPlaintext() throws Exception { AesEncryptionStrategy strategy = new AesEncryptionStrategy(128, 1000, "06DC30A48ADEEE72D98E33C2CEAEAD3E", "ED124530AF64A5CAD8EF463CF5628434", "password"); - String ciphertext = strategy.encrypt("Hello world"); + assertEquals("A/DzjV17WVS6ZAKsLOaC/Q==", ciphertext); } @Test void decrypt_decryptsTheCiphertext_WhenTheSameStrategyInstanceIsUsed() throws Exception { AesEncryptionStrategy strategy = new AesEncryptionStrategy(128, 1000, "password"); - String ciphertext = strategy.encrypt("Hello world"); + assertEquals("Hello world", strategy.decrypt(ciphertext)); } @Test void decrypt_decryptsTheCiphertext_WhenTheSameConfigurationIsUsed() throws Exception { AesEncryptionStrategy strategy = new AesEncryptionStrategy(128, 1000, "password"); - String ciphertext = strategy.encrypt("Hello world"); strategy = new AesEncryptionStrategy(strategy.getKeySize(), strategy.getIterationCount(), strategy.getSalt(), strategy.getIv(), "password"); @@ -39,65 +36,65 @@ void decrypt_decryptsTheCiphertext_WhenTheSameConfigurationIsUsed() throws Excep void decrypt_doesNotDecryptTheCiphertext_WhenTheIncorrectKeySizeIsUsed() throws Exception { try { AesEncryptionStrategy strategy = new AesEncryptionStrategy(128, 1000, "password"); - String ciphertext = strategy.encrypt("Hello world"); strategy = new AesEncryptionStrategy(256, strategy.getIterationCount(), strategy.getSalt(), strategy.getIv(), "password"); - strategy.decrypt(ciphertext); - } catch (BadPaddingException | InvalidKeyException bpe) { - // BadPaddingException is thrown on Mac and Linux - // InvalidKeyException is thrown in Windows + assertNotEquals("Hello world", strategy.decrypt(ciphertext)); } catch (Exception e) { - fail(); + // this is okay } } @Test void decrypt_doesNotDecryptTheCiphertext_WhenTheIncorrectIterationCountIsUsed() throws Exception { - assertThrows(BadPaddingException.class, () -> { + try { AesEncryptionStrategy strategy = new AesEncryptionStrategy(128, 1000, "password"); - String ciphertext = strategy.encrypt("Hello world"); strategy = new AesEncryptionStrategy(strategy.getKeySize(), 2000, strategy.getSalt(), strategy.getIv(), "password"); - strategy.decrypt(ciphertext); - }); + assertNotEquals("Hello world", strategy.decrypt(ciphertext)); + } catch (Exception e) { + // this is okay + } } @Test void decrypt_doesNotDecryptTheCiphertext_WhenTheIncorrectSaltIsUsed() throws Exception { - assertThrows(BadPaddingException.class, () -> { + try { AesEncryptionStrategy strategy = new AesEncryptionStrategy(128, 1000, "password"); - String ciphertext = strategy.encrypt("Hello world"); strategy = new AesEncryptionStrategy(strategy.getKeySize(), strategy.getIterationCount(), "133D30C2A658B3081279A97FD3B1F7CDE10C4FB61D39EEA8", strategy.getIv(), "password"); - strategy.decrypt(ciphertext); - }); + assertNotEquals("Hello world", strategy.decrypt(ciphertext)); + } catch (Exception e) { + // this is okay + } } @Test void decrypt_doesNotDecryptTheCiphertext_WhenTheIncorrectIvIsUsed() throws Exception { - assertThrows(BadPaddingException.class, () -> { + try { AesEncryptionStrategy strategy = new AesEncryptionStrategy(128, 1000, "password"); - String ciphertext = strategy.encrypt("Hello world"); strategy = new AesEncryptionStrategy(strategy.getKeySize(), strategy.getIterationCount(), strategy.getSalt(), "1DED89E4FB15F61DC6433E3BADA4A891", "password"); - strategy.decrypt(ciphertext); - }); + assertNotEquals("Hello world", strategy.decrypt(ciphertext)); + } catch (Exception e) { + // this is okay + } } @Test void decrypt_doesNotDecryptTheCiphertext_WhenTheIncorrectPassphraseIsUsed() throws Exception { - assertThrows(BadPaddingException.class, () -> { + try { AesEncryptionStrategy strategy = new AesEncryptionStrategy(128, 1000, "password"); - String ciphertext = strategy.encrypt("Hello world"); strategy = new AesEncryptionStrategy(strategy.getKeySize(), strategy.getIterationCount(), strategy.getSalt(), strategy.getIv(), "The Wrong Password"); - strategy.decrypt(ciphertext); - }); + assertNotEquals("Hello world", strategy.decrypt(ciphertext)); + } catch (Exception e) { + // this is okay + } } -} +} \ No newline at end of file From ee765b6250e8bc064ea7fea2a307792f761fc46b Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 19 Oct 2025 10:18:36 +0100 Subject: [PATCH 696/717] structurizr-dsl: Deprecates `setRestricted(boolean)` in favour of finer-grained features. --- changelog.md | 1 + structurizr-client/build.gradle | 4 +- .../java/com/structurizr/http/HttpClient.java | 129 ++++++++++++++ .../com/structurizr/http/RemoteContent.java | 38 +++++ .../java/com/structurizr/view/ThemeUtils.java | 82 ++++----- .../com/structurizr/http/HttpClientTests.java | 50 ++++++ structurizr-core/build.gradle | 2 +- .../util/FeatureNotEnabledException.java | 13 ++ .../java/com/structurizr/util/ImageUtils.java | 93 ++--------- .../main/java/com/structurizr/util/Url.java | 23 +++ .../com/structurizr/util/ImageUtilsTests.java | 22 --- .../com/structurizr/dsl/AbstractParser.java | 29 ---- .../com/structurizr/dsl/BrandingParser.java | 24 ++- .../java/com/structurizr/dsl/DslContext.java | 20 +++ .../com/structurizr/dsl/DslParserContext.java | 8 +- .../structurizr/dsl/ElementStyleParser.java | 24 ++- .../java/com/structurizr/dsl/Features.java | 16 ++ .../dsl/ImageViewContentParser.java | 107 ++++++------ .../dsl/ImpliedRelationshipsParser.java | 7 +- .../com/structurizr/dsl/IncludeParser.java | 54 ++++-- .../com/structurizr/dsl/RemoteContent.java | 24 --- .../structurizr/dsl/StructurizrDslParser.java | 146 +++++++++------- .../java/com/structurizr/dsl/ThemeParser.java | 15 +- .../com/structurizr/dsl/WorkspaceParser.java | 42 +++-- .../structurizr/dsl/BrandingParserTests.java | 49 +++++- .../java/com/structurizr/dsl/DslTests.java | 47 +++--- .../dsl/ElementStyleParserTests.java | 67 +++++++- .../dsl/ImageViewContentParserTests.java | 158 ++++++++++++++---- .../dsl/ImpliedRelationshipsParserTests.java | 38 +++-- .../structurizr/dsl/IncludeParserTests.java | 59 ++++++- .../com/structurizr/dsl/ThemeParserTests.java | 47 ++++-- .../structurizr/dsl/WorkspaceParserTests.java | 28 +++- structurizr-import/build.gradle | 2 +- .../diagrams/AbstractDiagramImporter.java | 13 +- .../diagrams/image/ImageImporter.java | 32 ++-- .../diagrams/kroki/KrokiImporter.java | 16 +- .../diagrams/mermaid/MermaidImporter.java | 16 +- .../diagrams/plantuml/PlantUMLImporter.java | 17 +- .../mermaid/MermaidImporterTests.java | 4 +- .../plantuml/PlantUMLImporterTests.java | 2 +- 40 files changed, 1073 insertions(+), 495 deletions(-) create mode 100644 structurizr-client/src/main/java/com/structurizr/http/HttpClient.java create mode 100644 structurizr-client/src/main/java/com/structurizr/http/RemoteContent.java create mode 100644 structurizr-client/src/test/java/com/structurizr/http/HttpClientTests.java create mode 100644 structurizr-core/src/main/java/com/structurizr/util/FeatureNotEnabledException.java delete mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/RemoteContent.java diff --git a/changelog.md b/changelog.md index 17922f372..0f3098447 100644 --- a/changelog.md +++ b/changelog.md @@ -20,6 +20,7 @@ - structurizr-dsl: PlantUML, Mermaid, and Kroki image views can now be defined by an inline source block. - structurizr-dsl: Constants and variables are inherited when extending a DSL workspace. - structurizr-dsl: DSL source is only stored in the JSON workspace when the DSL is deemed as "portable" (i.e. no files, plugins, scripts). +- structurizr-dsl: Deprecates `setRestricted(boolean)` in favour of finer-grained features. - structurizr-export: Removes support for deprecated enterprise and location concepts. - structurizr-export: PlantUML exporters - replaces skinparams with styles. - structurizr-export: PlantUML exporters - adds support for dark mode exports. diff --git a/structurizr-client/build.gradle b/structurizr-client/build.gradle index e078cd9cc..a868ccf81 100644 --- a/structurizr-client/build.gradle +++ b/structurizr-client/build.gradle @@ -2,8 +2,8 @@ dependencies { api project(':structurizr-core') - api 'com.fasterxml.jackson.core:jackson-databind:2.18.3' - api 'org.apache.httpcomponents.client5:httpclient5:5.4.3' + api 'com.fasterxml.jackson.core:jackson-databind:2.20.0' + api 'org.apache.httpcomponents.client5:httpclient5:5.5.1' api 'javax.xml.bind:jaxb-api:2.4.0-b180830.0359' } diff --git a/structurizr-client/src/main/java/com/structurizr/http/HttpClient.java b/structurizr-client/src/main/java/com/structurizr/http/HttpClient.java new file mode 100644 index 000000000..69e2db865 --- /dev/null +++ b/structurizr-client/src/main/java/com/structurizr/http/HttpClient.java @@ -0,0 +1,129 @@ +package com.structurizr.http; + +import org.apache.hc.client5.http.classic.methods.HttpGet; +import org.apache.hc.client5.http.config.ConnectionConfig; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; +import org.apache.hc.client5.http.impl.io.BasicHttpClientConnectionManager; +import org.apache.hc.core5.http.io.entity.EntityUtils; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +/** + * Wrapper for the HTTPClient in Apache HttpComponents, with optional caching and allowed URLs (via regexes). + */ +public class HttpClient { + + public static final String CONTENT_TYPE_IMAGE_PNG = "image/png"; + + private static final int HTTP_OK_STATUS = 200; + + private int timeout = 10000; // milliseconds + private final Set allowedUrlRegexes = new HashSet<>(); + + private final Map contentCache = new HashMap<>(); + + public HttpClient() { + } + + /** + * Sets the timeout in milliseconds. + * + * @param timeoutInMilliseconds the timeout in milliseconds + */ + public void setTimeout(int timeoutInMilliseconds) { + if (timeoutInMilliseconds < 0) { + throw new IllegalArgumentException("Timeout must be a positive integer"); + } + + this.timeout = timeoutInMilliseconds; + } + + /** + * HTTP GET of a URL, without caching. + * + * @param url the URL, as a String + * @return a RemoteContent object representing the response + */ + public RemoteContent get(String url) { + return get(url, false); + } + + /** + * HTTP GET of a URL. + * + * @param url the URL, as a String + * @param cache true if the result should be cached, false otherwise + * @return a RemoteContent object representing the response + */ + public RemoteContent get(String url, boolean cache) { + if (!isAllowed(url)) { + throw new RuntimeException("Access to " + url + " is not permitted"); + } + + RemoteContent remoteContent = contentCache.get(url); + if (remoteContent == null) { + ConnectionConfig connectionConfig = ConnectionConfig.custom() + .setConnectTimeout(timeout, TimeUnit.MILLISECONDS) + .setSocketTimeout(timeout, TimeUnit.MILLISECONDS) + .build(); + + BasicHttpClientConnectionManager cm = new BasicHttpClientConnectionManager(); + cm.setConnectionConfig(connectionConfig); + + try (CloseableHttpClient httpClient = HttpClientBuilder.create() + .useSystemProperties() + .setConnectionManager(cm) + .build()) { + + HttpGet httpGet = new HttpGet(url); + CloseableHttpResponse response = httpClient.execute(httpGet); + + int httpStatus = response.getCode(); + if (httpStatus == HTTP_OK_STATUS) { + String contentType = response.getEntity().getContentType(); + if (CONTENT_TYPE_IMAGE_PNG.equals(contentType)) { + remoteContent = new RemoteContent(EntityUtils.toByteArray(response.getEntity()), contentType); + } else { + remoteContent = new RemoteContent(EntityUtils.toString(response.getEntity()), contentType); + } + + if (cache) { + contentCache.put(url, remoteContent); + } + } else { + throw new RuntimeException("The content from " + url + " could not be loaded: HTTP status=" + httpStatus); + } + } catch (Exception ioe) { + throw new RuntimeException("The content from " + url + " could not be loaded: " + ioe.getMessage()); + } + } + + return remoteContent; + } + + /** + * Adds an allowed URL regex. + * + * @param regex the regex to allow + */ + public void allow(String regex) { + allowedUrlRegexes.add(regex); + } + + private boolean isAllowed(String url) { + for (String regex : allowedUrlRegexes) { + if (url.matches(regex)) { + return true; + } + } + + return false; + } + +} \ No newline at end of file diff --git a/structurizr-client/src/main/java/com/structurizr/http/RemoteContent.java b/structurizr-client/src/main/java/com/structurizr/http/RemoteContent.java new file mode 100644 index 000000000..4a4b9ae0d --- /dev/null +++ b/structurizr-client/src/main/java/com/structurizr/http/RemoteContent.java @@ -0,0 +1,38 @@ +package com.structurizr.http; + +/** + * Wrapper for remote content loaded via HTTP. + */ +public final class RemoteContent { + + public static final String CONTENT_TYPE_JSON = "application/json"; + + private final String content; + private final byte[] bytes; + private final String contentType; + + RemoteContent(String content, String contentType) { + this.content = content; + this.bytes = null; + this.contentType = contentType; + } + + RemoteContent(byte[] content, String contentType) { + this.content = null; + this.bytes = content; + this.contentType = contentType; + } + + public String getContentAsString() { + return content; + } + + public byte[] getContentAsBytes() { + return bytes; + } + + public String getContentType() { + return contentType; + } + +} \ No newline at end of file diff --git a/structurizr-client/src/main/java/com/structurizr/view/ThemeUtils.java b/structurizr-client/src/main/java/com/structurizr/view/ThemeUtils.java index 283ec0f3a..0ecd531c6 100644 --- a/structurizr-client/src/main/java/com/structurizr/view/ThemeUtils.java +++ b/structurizr-client/src/main/java/com/structurizr/view/ThemeUtils.java @@ -5,32 +5,22 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.structurizr.Workspace; +import com.structurizr.http.HttpClient; +import com.structurizr.http.RemoteContent; import com.structurizr.io.WorkspaceWriterException; -import com.structurizr.model.Relationship; import com.structurizr.util.ImageUtils; import com.structurizr.util.StringUtils; import com.structurizr.util.Url; -import org.apache.hc.client5.http.classic.methods.HttpGet; -import org.apache.hc.client5.http.config.ConnectionConfig; -import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; -import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; -import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; -import org.apache.hc.client5.http.impl.classic.HttpClients; -import org.apache.hc.client5.http.impl.io.BasicHttpClientConnectionManager; -import org.apache.hc.core5.http.io.entity.EntityUtils; import java.io.*; import java.nio.charset.StandardCharsets; import java.nio.file.Files; -import java.util.concurrent.TimeUnit; /** * Some utility methods for exporting themes to JSON. */ public final class ThemeUtils { - private static final int HTTP_OK_STATUS = 200; - private static final int DEFAULT_TIMEOUT_IN_MILLISECONDS = 10000; /** @@ -90,27 +80,38 @@ public static void loadThemes(Workspace workspace) throws Exception { * @throws Exception if something goes wrong */ public static void loadThemes(Workspace workspace, int timeoutInMilliseconds) throws Exception { + HttpClient httpClient = new HttpClient(); + httpClient.setTimeout(timeoutInMilliseconds); + + loadThemes(workspace, httpClient); + } + + public static void loadThemes(Workspace workspace, HttpClient httpClient) throws Exception { for (String themeLocation : workspace.getViews().getConfiguration().getThemes()) { if (Url.isUrl(themeLocation)) { - String json = loadFrom(themeLocation, timeoutInMilliseconds); - Theme theme = fromJson(json); - String baseUrl = themeLocation.substring(0, themeLocation.lastIndexOf('/') + 1); - - for (ElementStyle elementStyle : theme.getElements()) { - String icon = elementStyle.getIcon(); - if (!StringUtils.isNullOrEmpty(icon)) { - if (icon.startsWith("http")) { - // okay, image served over HTTP - } else if (icon.startsWith("data:image")) { - // also okay, data URI - } else { - // convert the relative icon filename into a full URL - elementStyle.setIcon(baseUrl + icon); + RemoteContent remoteContent = httpClient.get(themeLocation); + if (remoteContent.getContentType().equals(RemoteContent.CONTENT_TYPE_JSON)) { + Theme theme = fromJson(remoteContent.getContentAsString()); + String baseUrl = themeLocation.substring(0, themeLocation.lastIndexOf('/') + 1); + + for (ElementStyle elementStyle : theme.getElements()) { + String icon = elementStyle.getIcon(); + if (!StringUtils.isNullOrEmpty(icon)) { + if (Url.isHttpUrl(icon) || Url.isHttpsUrl(icon)) { + // okay, image served over HTTP or HTTPS + } else if (icon.startsWith("data:image")) { + // also okay, data URI + } else { + // convert the relative icon filename into a full URL + elementStyle.setIcon(baseUrl + icon); + } } } - } - workspace.getViews().getConfiguration().getStyles().addStylesFromTheme(theme); + workspace.getViews().getConfiguration().getStyles().addStylesFromTheme(theme); + } else { + throw new RuntimeException(String.format("%s - expected content type of %s, actual content type is %s", themeLocation, RemoteContent.CONTENT_TYPE_JSON, remoteContent.getContentType())); + } } } } @@ -144,31 +145,6 @@ public static void inlineTheme(Workspace workspace, File file) throws Exception workspace.getViews().getConfiguration().getStyles().inlineTheme(theme); } - private static String loadFrom(String url, int timeoutInMilliseconds) throws Exception { - ConnectionConfig connectionConfig = ConnectionConfig.custom() - .setConnectTimeout(timeoutInMilliseconds, TimeUnit.MILLISECONDS) - .setSocketTimeout(timeoutInMilliseconds, TimeUnit.MILLISECONDS) - .build(); - - BasicHttpClientConnectionManager cm = new BasicHttpClientConnectionManager(); - cm.setConnectionConfig(connectionConfig); - - try (CloseableHttpClient httpClient = HttpClientBuilder.create() - .useSystemProperties() - .setConnectionManager(cm) - .build()) { - - HttpGet httpGet = new HttpGet(url); - - CloseableHttpResponse response = httpClient.execute(httpGet); - if (response.getCode() == HTTP_OK_STATUS) { - return EntityUtils.toString(response.getEntity()); - } - } - - return ""; - } - private static Theme fromJson(String json) throws Exception { ObjectMapper objectMapper = new ObjectMapper(); objectMapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT); diff --git a/structurizr-client/src/test/java/com/structurizr/http/HttpClientTests.java b/structurizr-client/src/test/java/com/structurizr/http/HttpClientTests.java new file mode 100644 index 000000000..a98d05db7 --- /dev/null +++ b/structurizr-client/src/test/java/com/structurizr/http/HttpClientTests.java @@ -0,0 +1,50 @@ +package com.structurizr.http; + +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class HttpClientTests { + + @Test + @Tag("IntegrationTest") + void get_WhenNoAllowedUrlsAreConfigured() { + HttpClient httpClient = new HttpClient(); + + try { + httpClient.get("https://static.structurizr.com/themes/microsoft-azure-2024.07.15/icons.json"); + } catch (Exception e) { + assertEquals("Access to https://static.structurizr.com/themes/microsoft-azure-2024.07.15/icons.json is not permitted", e.getMessage()); + } + } + + @Test + @Tag("IntegrationTest") + void get_WithAllowedUrl() { + HttpClient httpClient = new HttpClient(); + httpClient.allow("https://static.structurizr.com/themes/amazon-web-services.*"); + + httpClient.get("https://static.structurizr.com/themes/amazon-web-services-2023.01.31/icons.json"); + + try { + httpClient.get("https://static.structurizr.com/themes/microsoft-azure-2024.07.15/icons.json"); + } catch (Exception e) { + assertEquals("Access to https://static.structurizr.com/themes/microsoft-azure-2024.07.15/icons.json is not permitted", e.getMessage()); + } + } + + @Test + @Tag("IntegrationTest") + void get_WithDisallowedUrl() { + HttpClient httpClient = new HttpClient(); + httpClient.allow("https://static.structurizr.com/.*"); + + try { + httpClient.get("https://example.com"); + } catch (Exception e) { + assertEquals("Access to https://example.com is not permitted", e.getMessage()); + } + } + +} \ No newline at end of file diff --git a/structurizr-core/build.gradle b/structurizr-core/build.gradle index 362d12137..2ac80b7d1 100644 --- a/structurizr-core/build.gradle +++ b/structurizr-core/build.gradle @@ -1,6 +1,6 @@ dependencies { - api 'com.fasterxml.jackson.core:jackson-annotations:2.18.3' + api 'com.fasterxml.jackson.core:jackson-annotations:2.20' api 'com.google.code.findbugs:jsr305:3.0.2' api 'commons-logging:commons-logging:1.3.5' diff --git a/structurizr-core/src/main/java/com/structurizr/util/FeatureNotEnabledException.java b/structurizr-core/src/main/java/com/structurizr/util/FeatureNotEnabledException.java new file mode 100644 index 000000000..9e75481c7 --- /dev/null +++ b/structurizr-core/src/main/java/com/structurizr/util/FeatureNotEnabledException.java @@ -0,0 +1,13 @@ +package com.structurizr.util; + +public final class FeatureNotEnabledException extends RuntimeException { + + public FeatureNotEnabledException(String feature) { + super("Feature " + feature + " is not enabled"); + } + + public FeatureNotEnabledException(String feature, String message) { + super(String.format("%s (feature %s is not enabled)", message, feature)); + } + +} \ No newline at end of file diff --git a/structurizr-core/src/main/java/com/structurizr/util/ImageUtils.java b/structurizr-core/src/main/java/com/structurizr/util/ImageUtils.java index e0baf42e6..65a9f8260 100644 --- a/structurizr-core/src/main/java/com/structurizr/util/ImageUtils.java +++ b/structurizr-core/src/main/java/com/structurizr/util/ImageUtils.java @@ -6,18 +6,12 @@ import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; -import java.net.URL; import java.net.URLConnection; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; import java.nio.file.Files; import java.util.Base64; -import java.util.HashMap; -import java.util.Map; /** - * Some utility methods for dealing with images. + * Some utility methods for dealing with images as files and data URIs. */ public class ImageUtils { @@ -30,8 +24,6 @@ public class ImageUtils { public static final String CONTENT_TYPE_IMAGE_JPG = "image/jpeg"; public static final String CONTENT_TYPE_IMAGE_SVG = "image/svg+xml"; - private static final Map imageCache = new HashMap<>(); - /** * Gets the content type of the specified file representing an image. * @@ -56,23 +48,6 @@ public static String getContentType(@Nonnull File file) throws IOException { return contentType; } - /** - * Gets the content type of the specified URL representing an image. - * - * @param url a URL pointing to an image - * @return a content type (e.g. "image/png") - * @throws IOException if there is an error reading the file - */ - public static String getContentType(String url) throws IOException { - if (StringUtils.isNullOrEmpty(url)) { - throw new IllegalArgumentException("A URL must be specified."); - } - - URLConnection connection = new URL(url).openConnection(); - connection.setConnectTimeout(1000 * 30); - return connection.getContentType(); - } - /** * Gets the content type of the specified data URI representing an image. * @@ -131,58 +106,24 @@ public static String getImageAsDataUri(File file) throws IOException { return DATA_URI_PREFIX + contentType + ";base64," + base64Content; } - public static String getSvgAsDataUri(@Nonnull URL url) throws Exception { - return getSvgAsDataUri(url, false); - } - - public static String getSvgAsDataUri(@Nonnull URL url, boolean cache) throws Exception { - String urlAsString = url.toString(); - String dataUri = cache ? imageCache.get(urlAsString) : null; - - if (StringUtils.isNullOrEmpty(dataUri)) { - HttpRequest request = HttpRequest.newBuilder() - .uri(url.toURI()) - .header("accept", CONTENT_TYPE_IMAGE_SVG) - .build(); - HttpClient client = HttpClient.newBuilder() - .followRedirects(HttpClient.Redirect.ALWAYS) - .build(); - - HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); - String svg = response.body(); - - dataUri = DATA_URI_PREFIX + CONTENT_TYPE_IMAGE_SVG + ";base64," + Base64.getEncoder().encodeToString(svg.getBytes()); - imageCache.put(urlAsString, dataUri); - } - - return dataUri; - } - - public static String getPngAsDataUri(@Nonnull URL url) throws Exception { - return getPngAsDataUri(url, false); + /** + * Converts an SVG string to a data URI. + * + * @param svg the SVG string + * @return a data URI + */ + public static String getSvgAsDataUri(String svg) { + return DATA_URI_PREFIX + CONTENT_TYPE_IMAGE_SVG + ";base64," + Base64.getEncoder().encodeToString(svg.getBytes()); } - public static String getPngAsDataUri(@Nonnull URL url, boolean cache) throws Exception { - String urlAsString = url.toString(); - String dataUri = cache ? imageCache.get(urlAsString) : null; - - if (StringUtils.isNullOrEmpty(dataUri)) { - HttpRequest request = HttpRequest.newBuilder() - .uri(url.toURI()) - .header("accept", CONTENT_TYPE_IMAGE_PNG) - .build(); - HttpClient client = HttpClient.newBuilder() - .followRedirects(HttpClient.Redirect.ALWAYS) - .build(); - - HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofByteArray()); - byte[] png = response.body(); - - dataUri = DATA_URI_PREFIX + CONTENT_TYPE_IMAGE_PNG + ";base64," + Base64.getEncoder().encodeToString(png); - imageCache.put(urlAsString, dataUri); - } - - return dataUri; + /** + * Converts an PNG to a data URI. + * + * @param png the PNG as a byte array + * @return a data URI + */ + public static String getPngAsDataUri(byte[] png) { + return DATA_URI_PREFIX + CONTENT_TYPE_IMAGE_PNG + ";base64," + Base64.getEncoder().encodeToString(png); } public static void validateImage(String imageDescriptor) { diff --git a/structurizr-core/src/main/java/com/structurizr/util/Url.java b/structurizr-core/src/main/java/com/structurizr/util/Url.java index adf0989a4..ee26a38bb 100644 --- a/structurizr-core/src/main/java/com/structurizr/util/Url.java +++ b/structurizr-core/src/main/java/com/structurizr/util/Url.java @@ -8,6 +8,9 @@ */ public class Url { + private static final String HTTPS_PROTOCOL = "https://"; + private static final String HTTP_PROTOCOL = "http://"; + public static final String INTRA_WORKSPACE_URL_PREFIX = "{workspace}"; public static final String INTER_WORKSPACE_URL_REGEX = "\\{workspace:\\d+\\}.*"; @@ -30,4 +33,24 @@ public static boolean isUrl(String urlAsString) { return false; } + /** + * Determines whether the supplied string is a valid HTTPS URL. + * + * @param urlAsString the URL, as a String + * @return true if the URL is valid, false otherwise + */ + public static boolean isHttpsUrl(String urlAsString) { + return isUrl(urlAsString) && urlAsString.toLowerCase().startsWith(HTTPS_PROTOCOL); + } + + /** + * Determines whether the supplied string is a valid HTTP URL. + * + * @param urlAsString the URL, as a String + * @return true if the URL is valid, false otherwise + */ + public static boolean isHttpUrl(String urlAsString) { + return isUrl(urlAsString) && urlAsString.toLowerCase().startsWith(HTTP_PROTOCOL); + } + } \ No newline at end of file diff --git a/structurizr-core/src/test/java/com/structurizr/util/ImageUtilsTests.java b/structurizr-core/src/test/java/com/structurizr/util/ImageUtilsTests.java index fe5b5883f..5f2a700bc 100644 --- a/structurizr-core/src/test/java/com/structurizr/util/ImageUtilsTests.java +++ b/structurizr-core/src/test/java/com/structurizr/util/ImageUtilsTests.java @@ -62,28 +62,6 @@ void getContentType_ReturnsTheContentType_WhenASVGFileIsSpecified() throws Excep assertEquals("image/svg+xml", contentType); } - @Test - void getContentType_ThrowsAnException_WhenANullUrlIsSpecified() throws Exception { - try { - ImageUtils.getContentType((String)null); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("A URL must be specified.", iae.getMessage()); - } - } - - @Test - void getContentType_ReturnsTheContentType_WhenAPNGUrlIsSpecified() throws Exception { - String contentType = ImageUtils.getContentType(new File("../structurizr-core/test/unit/com/structurizr/util/image.png").toURI().toURL().toExternalForm()); - assertEquals("image/png", contentType); - } - - @Test - void getContentType_ReturnsTheContentType_WhenASVGUrlIsSpecified() throws Exception { - String contentType = ImageUtils.getContentType(new File("../structurizr-core/test/unit/com/structurizr/util/image.svg").toURI().toURL().toExternalForm()); - assertEquals("image/svg+xml", contentType); - } - @Test void getContentTypeFromDataUri_ThrowsAnException_WhenANullDataUriIsSpecified() throws Exception { try { diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractParser.java index 5d41ec735..d45b3a2f6 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/AbstractParser.java @@ -1,33 +1,4 @@ package com.structurizr.dsl; -import org.apache.hc.client5.http.classic.methods.HttpGet; -import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; -import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; -import org.apache.hc.client5.http.impl.classic.HttpClients; -import org.apache.hc.core5.http.io.entity.EntityUtils; - abstract class AbstractParser { - - private static final int HTTP_OK_STATUS = 200; - - String removeNonWordCharacters(String name) { - return name.replaceAll("\\W", ""); - } - - protected RemoteContent readFromUrl(String url) { - try (CloseableHttpClient httpClient = HttpClients.createSystem()) { - HttpGet httpGet = new HttpGet(url); - CloseableHttpResponse response = httpClient.execute(httpGet); - - int httpStatus = response.getCode(); - if (httpStatus == HTTP_OK_STATUS) { - return new RemoteContent(EntityUtils.toString(response.getEntity()), response.getEntity().getContentType()); - } else { - throw new RuntimeException("The content from " + url + " could not be loaded: HTTP status=" + httpStatus); - } - } catch (Exception ioe) { - throw new RuntimeException("The content from " + url + " could not be loaded: " + ioe.getMessage()); - } - } - } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/BrandingParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/BrandingParser.java index 5b82b6594..add3a4712 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/BrandingParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/BrandingParser.java @@ -1,6 +1,8 @@ package com.structurizr.dsl; +import com.structurizr.util.FeatureNotEnabledException; import com.structurizr.util.ImageUtils; +import com.structurizr.util.Url; import com.structurizr.view.Font; import java.io.File; @@ -15,7 +17,7 @@ final class BrandingParser extends AbstractParser { private static final int FONT_NAME_INDEX = 1; private static final int FONT_URL_INDEX = 2; - void parseLogo(BrandingDslContext context, Tokens tokens, boolean restricted) { + void parseLogo(BrandingDslContext context, Tokens tokens) { // logo if (tokens.hasMoreThan(LOGO_FILE_INDEX)) { @@ -23,11 +25,25 @@ void parseLogo(BrandingDslContext context, Tokens tokens, boolean restricted) { } else if (tokens.includes(LOGO_FILE_INDEX)) { String path = tokens.get(1); - if (path.startsWith("data:image/") || path.startsWith("https://") || path.startsWith("http://")) { + if (path.startsWith("data:image/")) { ImageUtils.validateImage(path); context.getWorkspace().getViews().getConfiguration().getBranding().setLogo(path); + } else if (Url.isHttpsUrl(path)) { + if (context.getFeatures().isEnabled(Features.HTTPS)) { + ImageUtils.validateImage(path); + context.getWorkspace().getViews().getConfiguration().getBranding().setLogo(path); + } else { + throw new FeatureNotEnabledException(Features.HTTPS, "Icons via HTTPS are not permitted"); + } + } else if (Url.isHttpUrl(path)) { + if (context.getFeatures().isEnabled(Features.HTTP)) { + ImageUtils.validateImage(path); + context.getWorkspace().getViews().getConfiguration().getBranding().setLogo(path); + } else { + throw new FeatureNotEnabledException(Features.HTTP, "Icons via HTTP are not permitted"); + } } else { - if (!restricted) { + if (context.getFeatures().isEnabled(Features.FILE_SYSTEM)) { File file = new File(context.getFile().getParent(), path); if (file.exists() && !file.isDirectory()) { context.setDslPortable(false); @@ -40,6 +56,8 @@ void parseLogo(BrandingDslContext context, Tokens tokens, boolean restricted) { } else { throw new RuntimeException(path + " does not exist"); } + } else { + throw new FeatureNotEnabledException(Features.FILE_SYSTEM, "!branding is not permitted"); } } } else { diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DslContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DslContext.java index 4998d6864..3c31f3e76 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/DslContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DslContext.java @@ -1,6 +1,7 @@ package com.structurizr.dsl; import com.structurizr.Workspace; +import com.structurizr.http.HttpClient; import com.structurizr.model.Element; import com.structurizr.model.Relationship; @@ -21,6 +22,9 @@ abstract class DslContext { protected IdentifiersRegister identifiersRegister = new IdentifiersRegister(); + private Features features = new Features(); + private HttpClient httpClient = new HttpClient(); + Workspace getWorkspace() { return workspace; } @@ -116,6 +120,22 @@ Relationship getRelationship(String identifier) { return identifiersRegister.getRelationship(identifier.toLowerCase()); } + Features getFeatures() { + return features; + } + + void setFeatures(Features features) { + this.features = features; + } + + HttpClient getHttpClient() { + return httpClient; + } + + void setHttpClient(HttpClient httpClient) { + this.httpClient = httpClient; + } + protected Class loadClass(String fqn, File dslFile) throws Exception { File pluginsDirectory = new File(dslFile.getParent(), PLUGINS_DIRECTORY_NAME); URL[] urls = new URL[0]; diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/DslParserContext.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/DslParserContext.java index ed964b481..8713345de 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/DslParserContext.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/DslParserContext.java @@ -6,12 +6,10 @@ final class DslParserContext extends DslContext { private final StructurizrDslParser parser; private final File file; - private final boolean restricted; - DslParserContext(StructurizrDslParser parser, File file, boolean restricted) { + DslParserContext(StructurizrDslParser parser, File file) { this.parser = parser; this.file = file; - this.restricted = restricted; } StructurizrDslParser getParser() { @@ -22,10 +20,6 @@ File getFile() { return file; } - boolean isRestricted() { - return restricted; - } - void copyFrom(IdentifiersRegister identifersRegister) { for (String identifier : identifersRegister.getElementIdentifiers()) { this.identifiersRegister.register(identifier, identifersRegister.getElement(identifier)); diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementStyleParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementStyleParser.java index dd83c431e..4fe23bde3 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementStyleParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ElementStyleParser.java @@ -1,8 +1,10 @@ package com.structurizr.dsl; import com.structurizr.Workspace; +import com.structurizr.util.FeatureNotEnabledException; import com.structurizr.util.ImageUtils; import com.structurizr.util.StringUtils; +import com.structurizr.util.Url; import com.structurizr.view.*; import java.io.File; @@ -284,7 +286,7 @@ void parseDescription(ElementStyleDslContext context, Tokens tokens) { } } - void parseIcon(ElementStyleDslContext context, Tokens tokens, boolean restricted) { + void parseIcon(ElementStyleDslContext context, Tokens tokens) { ElementStyle style = context.getStyle(); if (tokens.hasMoreThan(FIRST_PROPERTY_INDEX)) { @@ -294,11 +296,25 @@ void parseIcon(ElementStyleDslContext context, Tokens tokens, boolean restricted if (tokens.includes(FIRST_PROPERTY_INDEX)) { String path = tokens.get(1); - if (path.startsWith("data:image/") || path.startsWith("https://") || path.startsWith("http://")) { + if (path.startsWith("data:image/")) { ImageUtils.validateImage(path); style.setIcon(path); + } else if (Url.isHttpsUrl(path)) { + if (context.getFeatures().isEnabled(Features.HTTPS)) { + ImageUtils.validateImage(path); + style.setIcon(path); + } else { + throw new FeatureNotEnabledException(Features.HTTPS, "Icons via HTTPS are not permitted"); + } + } else if (Url.isHttpUrl(path)) { + if (context.getFeatures().isEnabled(Features.HTTP)) { + ImageUtils.validateImage(path); + style.setIcon(path); + } else { + throw new FeatureNotEnabledException(Features.HTTP, "Icons via HTTP are not permitted"); + } } else { - if (!restricted) { + if (context.getFeatures().isEnabled(Features.FILE_SYSTEM)) { File file = new File(context.getFile().getParent(), path); if (file.exists() && !file.isDirectory()) { try { @@ -310,6 +326,8 @@ void parseIcon(ElementStyleDslContext context, Tokens tokens, boolean restricted } else { throw new RuntimeException(path + " does not exist"); } + } else { + throw new FeatureNotEnabledException(Features.FILE_SYSTEM, "!icon is not permitted"); } } } else { diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/Features.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/Features.java index 3d40aabb4..4e96d07ac 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/Features.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/Features.java @@ -1,4 +1,20 @@ package com.structurizr.dsl; public final class Features extends com.structurizr.util.Features { + + public static final String PLUGINS = "structurizr.feature.dsl.plugins"; + public static final String SCRIPTS = "structurizr.feature.dsl.scripts"; + + public static final String COMPONENT_FINDER = "structurizr.feature.dsl.componentfinder"; + + public static final String INCLUDE = "structurizr.feature.dsl.include"; + + public static final String DOCUMENTATION = "structurizr.feature.dsl.documentation"; + public static final String DECISIONS = "structurizr.feature.dsl.decisions"; + + public static final String ENVIRONMENT = "structurizr.feature.dsl.environment"; + public static final String FILE_SYSTEM = "structurizr.feature.dsl.filesystem"; + public static final String HTTP = "structurizr.feature.dsl.http"; + public static final String HTTPS = "structurizr.feature.dsl.https"; + } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewContentParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewContentParser.java index b37b55dc8..e78d72ba3 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewContentParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImageViewContentParser.java @@ -2,10 +2,12 @@ import com.structurizr.export.mermaid.MermaidDiagramExporter; import com.structurizr.export.plantuml.StructurizrPlantUMLExporter; +import com.structurizr.http.RemoteContent; import com.structurizr.importer.diagrams.image.ImageImporter; import com.structurizr.importer.diagrams.kroki.KrokiImporter; import com.structurizr.importer.diagrams.mermaid.MermaidImporter; import com.structurizr.importer.diagrams.plantuml.PlantUMLImporter; +import com.structurizr.util.FeatureNotEnabledException; import com.structurizr.util.StringUtils; import com.structurizr.util.Url; import com.structurizr.view.ColorScheme; @@ -30,10 +32,7 @@ final class ImageViewContentParser extends AbstractParser { private static final int IMAGE_SOURCE_INDEX = 1; - private boolean restricted = false; - - ImageViewContentParser(boolean restricted) { - this.restricted = restricted; + ImageViewContentParser() { } void parsePlantUML(ImageViewDslContext context, File dslFile, Tokens tokens) { @@ -53,15 +52,15 @@ void parsePlantUML(ImageViewDslContext context, File dslFile, Tokens tokens) { try { if (source.contains("\n")) { // inline source - new PlantUMLImporter().importDiagram(context.getView(), source, colorScheme); + new PlantUMLImporter(context.getHttpClient()).importDiagram(context.getView(), source, colorScheme); } else { View viewWithKey = context.getWorkspace().getViews().getViewWithKey(source); if (viewWithKey instanceof ModelView) { String plantumlLight = new StructurizrPlantUMLExporter(ColorScheme.Light).export((ModelView) viewWithKey).getDefinition(); - new PlantUMLImporter().importDiagram(context.getView(), plantumlLight, ColorScheme.Light); + new PlantUMLImporter(context.getHttpClient()).importDiagram(context.getView(), plantumlLight, ColorScheme.Light); String plantumlDark = new StructurizrPlantUMLExporter(ColorScheme.Dark).export((ModelView) viewWithKey).getDefinition(); - new PlantUMLImporter().importDiagram(context.getView(), plantumlDark, ColorScheme.Dark); + new PlantUMLImporter(context.getHttpClient()).importDiagram(context.getView(), plantumlDark, ColorScheme.Dark); if (!StringUtils.isNullOrEmpty(viewWithKey.getTitle())) { context.getView().setTitle(viewWithKey.getTitle()); @@ -70,21 +69,28 @@ void parsePlantUML(ImageViewDslContext context, File dslFile, Tokens tokens) { } context.getView().setDescription(viewWithKey.getDescription()); } else { - if (Url.isUrl(source)) { - RemoteContent content = readFromUrl(source); - new PlantUMLImporter().importDiagram(context.getView(), content.getContent(), colorScheme); + if (Url.isHttpsUrl(source) || Url.isHttpUrl(source)) { + if (Url.isHttpsUrl(source) && !context.getFeatures().isEnabled(Features.HTTPS)) { + throw new FeatureNotEnabledException(Features.HTTPS, "Image views via HTTPS are not permitted"); + } + if (Url.isHttpUrl(source) && !context.getFeatures().isEnabled(Features.HTTP)) { + throw new FeatureNotEnabledException(Features.HTTP, "Image views via HTTP are not permitted"); + } + + RemoteContent content = context.getHttpClient().get(source); + new PlantUMLImporter(context.getHttpClient()).importDiagram(context.getView(), content.getContentAsString(), colorScheme); context.getView().setTitle(source.substring(source.lastIndexOf("/") + 1)); } else { - if (!restricted) { + if (context.getFeatures().isEnabled(Features.FILE_SYSTEM)) { File file = new File(dslFile.getParentFile(), source); if (file.exists()) { context.setDslPortable(false); - new PlantUMLImporter().importDiagram(context.getView(), file, colorScheme); + new PlantUMLImporter(context.getHttpClient()).importDiagram(context.getView(), file, colorScheme); } else { throw new RuntimeException("The file at " + file.getAbsolutePath() + " does not exist"); } } else { - throw new RuntimeException("PlantUML source must be specified as a URL when running in restricted mode"); + throw new FeatureNotEnabledException(Features.FILE_SYSTEM, "plantuml is not permitted"); } } } @@ -111,13 +117,13 @@ void parseMermaid(ImageViewDslContext context, File dslFile, Tokens tokens) { try { if (source.contains("\n")) { // inline source - new MermaidImporter().importDiagram(context.getView(), source, colorScheme); + new MermaidImporter(context.getHttpClient()).importDiagram(context.getView(), source, colorScheme); } else { View viewWithKey = context.getWorkspace().getViews().getViewWithKey(source); if (viewWithKey instanceof ModelView) { MermaidDiagramExporter exporter = new MermaidDiagramExporter(); String mermaid = exporter.export((ModelView) viewWithKey).getDefinition(); - new MermaidImporter().importDiagram(context.getView(), mermaid, colorScheme); + new MermaidImporter(context.getHttpClient()).importDiagram(context.getView(), mermaid, colorScheme); if (!StringUtils.isNullOrEmpty(viewWithKey.getTitle())) { context.getView().setTitle(viewWithKey.getTitle()); @@ -126,21 +132,28 @@ void parseMermaid(ImageViewDslContext context, File dslFile, Tokens tokens) { } context.getView().setDescription(viewWithKey.getDescription()); } else { - if (Url.isUrl(source)) { - RemoteContent content = readFromUrl(source); - new MermaidImporter().importDiagram(context.getView(), content.getContent(), colorScheme); + if (Url.isHttpsUrl(source) || Url.isHttpUrl(source)) { + if (Url.isHttpsUrl(source) && !context.getFeatures().isEnabled(Features.HTTPS)) { + throw new FeatureNotEnabledException(Features.HTTPS, "Image views via HTTPS are not permitted"); + } + if (Url.isHttpUrl(source) && !context.getFeatures().isEnabled(Features.HTTP)) { + throw new FeatureNotEnabledException(Features.HTTP, "Image views via HTTP are not permitted"); + } + + RemoteContent content = context.getHttpClient().get(source); + new MermaidImporter(context.getHttpClient()).importDiagram(context.getView(), content.getContentAsString(), colorScheme); context.getView().setTitle(source.substring(source.lastIndexOf("/") + 1)); } else { - if (!restricted) { + if (context.getFeatures().isEnabled(Features.FILE_SYSTEM)) { File file = new File(dslFile.getParentFile(), source); if (file.exists()) { context.setDslPortable(false); - new MermaidImporter().importDiagram(context.getView(), file, colorScheme); + new MermaidImporter(context.getHttpClient()).importDiagram(context.getView(), file, colorScheme); } else { throw new RuntimeException("The file at " + file.getAbsolutePath() + " does not exist"); } } else { - throw new RuntimeException("Mermaid source must be specified as a URL when running in restricted mode"); + throw new FeatureNotEnabledException(Features.FILE_SYSTEM, "mermaid is not permitted"); } } } @@ -168,23 +181,30 @@ void parseKroki(ImageViewDslContext context, File dslFile, Tokens tokens) { try { if (source.contains("\n")) { // inline source - new KrokiImporter().importDiagram(context.getView(), format, source, colorScheme); + new KrokiImporter(context.getHttpClient()).importDiagram(context.getView(), format, source, colorScheme); } else { - if (Url.isUrl(source)) { - RemoteContent content = readFromUrl(source); - new KrokiImporter().importDiagram(context.getView(), format, content.getContent(), colorScheme); + if (Url.isHttpsUrl(source) || Url.isHttpUrl(source)) { + if (Url.isHttpsUrl(source) && !context.getFeatures().isEnabled(Features.HTTPS)) { + throw new FeatureNotEnabledException(Features.HTTPS, "Image views via HTTPS are not permitted"); + } + if (Url.isHttpUrl(source) && !context.getFeatures().isEnabled(Features.HTTP)) { + throw new FeatureNotEnabledException(Features.HTTP, "Image views via HTTP are not permitted"); + } + + RemoteContent content = context.getHttpClient().get(source); + new KrokiImporter(context.getHttpClient()).importDiagram(context.getView(), format, content.getContentAsString(), colorScheme); context.getView().setTitle(source.substring(source.lastIndexOf("/") + 1)); } else { - if (!restricted) { + if (context.getFeatures().isEnabled(Features.FILE_SYSTEM)) { File file = new File(dslFile.getParentFile(), source); if (file.exists()) { context.setDslPortable(false); - new KrokiImporter().importDiagram(context.getView(), format, file, colorScheme); + new KrokiImporter(context.getHttpClient()).importDiagram(context.getView(), format, file, colorScheme); } else { throw new RuntimeException("The file at " + file.getAbsolutePath() + " does not exist"); } } else { - throw new RuntimeException("Kroki source must be specified as a URL when running in restricted mode"); + throw new FeatureNotEnabledException(Features.FILE_SYSTEM, "kroki " + format + " is not permitted"); } } } @@ -208,19 +228,26 @@ void parseImage(ImageViewDslContext context, File dslFile, Tokens tokens) { ColorScheme colorScheme = context.getColorScheme(); try { - if (Url.isUrl(source)) { - new ImageImporter().importDiagram(context.getView(), source, colorScheme); + if (Url.isHttpsUrl(source) || Url.isHttpUrl(source)) { + if (Url.isHttpsUrl(source) && !context.getFeatures().isEnabled(Features.HTTPS)) { + throw new FeatureNotEnabledException(Features.HTTPS, "Image views via HTTPS are not permitted"); + } + if (Url.isHttpUrl(source) && !context.getFeatures().isEnabled(Features.HTTP)) { + throw new FeatureNotEnabledException(Features.HTTP, "Image views via HTTP are not permitted"); + } + + new ImageImporter(context.getHttpClient()).importDiagram(context.getView(), source, colorScheme); } else { - if (!restricted) { + if (context.getFeatures().isEnabled(Features.FILE_SYSTEM)) { File file = new File(dslFile.getParentFile(), source); if (file.exists()) { context.setDslPortable(false); - new ImageImporter().importDiagram(context.getView(), file, colorScheme); + new ImageImporter(context.getHttpClient()).importDiagram(context.getView(), file, colorScheme); } else { throw new RuntimeException("The file at " + file.getAbsolutePath() + " does not exist"); } } else { - throw new RuntimeException("Images must be specified as a URL when running in restricted mode"); + throw new FeatureNotEnabledException(Features.FILE_SYSTEM, "image is not permitted"); } } } catch (Exception e) { @@ -228,18 +255,4 @@ void parseImage(ImageViewDslContext context, File dslFile, Tokens tokens) { } } - private ColorScheme calculateColorScheme(Tokens tokens, int index) { - if (tokens.includes(index)) { - if (ColorScheme.Dark.toString().equalsIgnoreCase(tokens.get(index))) { - return ColorScheme.Dark; - } else if (ColorScheme.Light.toString().equalsIgnoreCase(tokens.get(index))) { - return ColorScheme.Light; - } else { - throw new RuntimeException("Invalid color scheme \"" + tokens.get(index) + "\" - expected: light or dark"); - } - } - - return null; - } - } \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImpliedRelationshipsParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImpliedRelationshipsParser.java index e880f25a7..086fdb9b3 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ImpliedRelationshipsParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ImpliedRelationshipsParser.java @@ -3,6 +3,7 @@ import com.structurizr.model.CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy; import com.structurizr.model.DefaultImpliedRelationshipsStrategy; import com.structurizr.model.ImpliedRelationshipsStrategy; +import com.structurizr.util.FeatureNotEnabledException; import java.io.File; import java.lang.reflect.Constructor; @@ -22,7 +23,7 @@ final class ImpliedRelationshipsParser extends AbstractParser { private static final String TRUE = "true"; private static final String FALSE = "false"; - void parse(DslContext context, Tokens tokens, File dslFile, boolean restricted) { + void parse(DslContext context, Tokens tokens, File dslFile) { // impliedRelationships if (tokens.hasMoreThan(OPTION_INDEX)) { @@ -40,9 +41,9 @@ void parse(DslContext context, Tokens tokens, File dslFile, boolean restricted) } else if (option.equalsIgnoreCase(TRUE)) { context.getWorkspace().getModel().setImpliedRelationshipsStrategy(new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy()); } else { - if (restricted) { + if (!context.getFeatures().isEnabled(Features.PLUGINS)) { if (!BUILT_IN_IMPLIED_RELATIONSHIPS_STRATEGIES.contains(option)) { - throw new RuntimeException("The implied relationships strategy " + option + " is not available when the DSL parser is running in restricted mode"); + throw new FeatureNotEnabledException(Features.PLUGINS, "The implied relationships strategy " + option + " is not available"); } } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/IncludeParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/IncludeParser.java index bfea57783..d8ac5caef 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/IncludeParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/IncludeParser.java @@ -1,5 +1,9 @@ package com.structurizr.dsl; +import com.structurizr.http.RemoteContent; +import com.structurizr.util.FeatureNotEnabledException; +import com.structurizr.util.Url; + import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -17,6 +21,10 @@ final class IncludeParser extends AbstractParser { List parse(DslContext context, File dslFile, Tokens tokens) { // !include + if (!context.getFeatures().isEnabled(Features.INCLUDE)) { + throw new FeatureNotEnabledException(Features.INCLUDE, "!include is not permitted"); + } + List includedFiles = new ArrayList<>(); if (tokens.hasMoreThan(SOURCE_INDEX)) { @@ -28,24 +36,40 @@ List parse(DslContext context, File dslFile, Tokens tokens) { } String source = tokens.get(SOURCE_INDEX); - if (source.startsWith("https://") || source.startsWith("http://")) { - RemoteContent content = readFromUrl(source); - List lines = Arrays.asList(content.getContent().split("\n")); - includedFiles.add(new IncludedFile(dslFile, lines)); + if (Url.isHttpsUrl(source)) { + if (context.getFeatures().isEnabled(Features.HTTPS)) { + RemoteContent content = context.getHttpClient().get(source); + List lines = Arrays.asList(content.getContentAsString().split("\n")); + includedFiles.add(new IncludedFile(dslFile, lines)); + } else { + throw new FeatureNotEnabledException(Features.HTTPS, "Includes via HTTPS are not permitted"); + } + } else if (Url.isHttpUrl(source)) { + if (context.getFeatures().isEnabled(Features.HTTP)) { + RemoteContent content = context.getHttpClient().get(source); + List lines = Arrays.asList(content.getContentAsString().split("\n")); + includedFiles.add(new IncludedFile(dslFile, lines)); + } else { + throw new FeatureNotEnabledException(Features.HTTP, "Includes via HTTP are not permitted"); + } } else { - if (dslFile != null) { - File path = new File(dslFile.getParent(), source); - - try { - if (!path.exists()) { - throw new RuntimeException(path.getCanonicalPath() + " could not be found"); + if (context.getFeatures().isEnabled(Features.FILE_SYSTEM)) { + if (dslFile != null) { + File path = new File(dslFile.getParent(), source); + + try { + if (!path.exists()) { + throw new RuntimeException(path.getCanonicalPath() + " could not be found"); + } + + includedFiles.addAll(readFiles(path)); + context.setDslPortable(false); + } catch (IOException e) { + throw new RuntimeException("Error including " + path.getAbsolutePath() + ": " + e.getMessage()); } - - includedFiles.addAll(readFiles(path)); - context.setDslPortable(false); - } catch (IOException e) { - throw new RuntimeException("Error including " + path.getAbsolutePath() + ": " + e.getMessage()); } + } else { + throw new FeatureNotEnabledException(Features.FILE_SYSTEM, "!include is not permitted"); } } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/RemoteContent.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/RemoteContent.java deleted file mode 100644 index 929943296..000000000 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/RemoteContent.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.structurizr.dsl; - -final class RemoteContent { - - static final String CONTENT_TYPE_JSON = "application/json"; - static final String TEXT_PLAIN_JSON = "text/plain"; - - private final String content; - private final String contentType; - - RemoteContent(String content, String contentType) { - this.content = content; - this.contentType = contentType; - } - - String getContent() { - return content; - } - - String getContentType() { - return contentType; - } - -} \ No newline at end of file diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java index 61e9503df..faf98ce36 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/StructurizrDslParser.java @@ -2,7 +2,9 @@ import com.structurizr.PropertyHolder; import com.structurizr.Workspace; +import com.structurizr.http.HttpClient; import com.structurizr.model.*; +import com.structurizr.util.FeatureNotEnabledException; import com.structurizr.util.StringUtils; import com.structurizr.view.*; import org.apache.commons.logging.Log; @@ -46,7 +48,8 @@ public final class StructurizrDslParser extends StructurizrDslTokens { private final Set parsedTokens = new HashSet<>(); private final IdentifiersRegister identifiersRegister; private Map constantsAndVariables; - private final Features features = new Features(); + private Features features = new Features(); + private HttpClient httpClient = new HttpClient(); private Map> archetypes = Map.of( StructurizrDslTokens.GROUP_TOKEN, new HashMap<>(), @@ -65,8 +68,6 @@ public final class StructurizrDslParser extends StructurizrDslTokens { private Workspace workspace; private boolean extendingWorkspace = false; - private boolean restricted = false; - /** * Creates a new instance of the parser. */ @@ -74,6 +75,20 @@ public StructurizrDslParser() { contextStack = new Stack<>(); identifiersRegister = new IdentifiersRegister(); constantsAndVariables = new HashMap<>(); + + features.enable(Features.ENVIRONMENT); + features.enable(Features.FILE_SYSTEM); + features.enable(Features.HTTP); + features.enable(Features.HTTPS); + + features.enable(Features.PLUGINS); + features.enable(Features.SCRIPTS); + features.enable(Features.COMPONENT_FINDER); + + features.enable(Features.DOCUMENTATION); + features.enable(Features.DECISIONS); + + features.enable(Features.INCLUDE); } void configureFrom(StructurizrDslParser parser) { @@ -113,8 +128,17 @@ private void setIdentifierScope(IdentifierScope identifierScope) { * * @param restricted true for restricted mode, false otherwise */ + @Deprecated public void setRestricted(boolean restricted) { - this.restricted = restricted; + features.configure(Features.ENVIRONMENT, !restricted); + features.configure(Features.FILE_SYSTEM, !restricted); + + features.configure(Features.PLUGINS, !restricted); + features.configure(Features.SCRIPTS, !restricted); + features.configure(Features.COMPONENT_FINDER, !restricted); + + features.configure(Features.DOCUMENTATION, !restricted); + features.configure(Features.DECISIONS, !restricted); } /** @@ -269,28 +293,24 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn endContext(); } else if (INCLUDE_FILE_TOKEN.equalsIgnoreCase(firstToken)) { - if (!restricted || tokens.get(1).startsWith("https://") || tokens.get(1).startsWith("http://")) { - String leadingSpace = line.substring(0, line.indexOf(INCLUDE_FILE_TOKEN)); - - List files = new IncludeParser().parse(getContext(), dslFile, tokens); - for (IncludedFile includedFile : files) { - List paddedLines = new ArrayList<>(); - for (String unpaddedLine : includedFile.getLines()) { - if (unpaddedLine.startsWith(BOM)) { - // this caters for files encoded as "UTF-8 with BOM" - unpaddedLine = unpaddedLine.substring(1); - } - paddedLines.add(leadingSpace + unpaddedLine); + String leadingSpace = line.substring(0, line.indexOf(INCLUDE_FILE_TOKEN)); + + List files = new IncludeParser().parse(getContext(), dslFile, tokens); + for (IncludedFile includedFile : files) { + List paddedLines = new ArrayList<>(); + for (String unpaddedLine : includedFile.getLines()) { + if (unpaddedLine.startsWith(BOM)) { + // this caters for files encoded as "UTF-8 with BOM" + unpaddedLine = unpaddedLine.substring(1); } - - parse(paddedLines, includedFile.getFile(), true, false); + paddedLines.add(leadingSpace + unpaddedLine); } - } else { - throwRestrictedModeException(firstToken + " "); + + parse(paddedLines, includedFile.getFile(), true, false); } } else if (PLUGIN_TOKEN.equalsIgnoreCase(firstToken)) { - if (!restricted) { + if (features.isEnabled(Features.PLUGINS)) { String fullyQualifiedClassName = new PluginParser().parse(getContext(), tokens.withoutContextStartToken()); startContext(new PluginDslContext(fullyQualifiedClassName, dslFile, this)); if (!shouldStartContext(tokens)) { @@ -298,14 +318,14 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn endContext(); } } else { - throwRestrictedModeException(firstToken); + throw new FeatureNotEnabledException(Features.PLUGINS, firstToken + " is not permitted"); } } else if (inContext(PluginDslContext.class)) { new PluginParser().parseParameter(getContext(PluginDslContext.class), tokens); } else if (SCRIPT_TOKEN.equalsIgnoreCase(firstToken)) { - if (!restricted) { + if (features.isEnabled(Features.SCRIPTS)) { ScriptParser scriptParser = new ScriptParser(); if (scriptParser.isInlineScript(tokens)) { String language = scriptParser.parseInline(tokens.withoutContextStartToken()); @@ -321,7 +341,7 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn } } } else { - throwRestrictedModeException(firstToken); + throw new FeatureNotEnabledException(Features.SCRIPTS, firstToken + " is not permitted"); } } else if (inContext(ExternalScriptDslContext.class)) { @@ -504,12 +524,12 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn registerIdentifier(identifier, component); } else if (COMPONENT_FINDER_TOKEN.equalsIgnoreCase(firstToken) && inContext(ContainerDslContext.class)) { - if (!restricted) { + if (features.isEnabled(Features.COMPONENT_FINDER)) { if (shouldStartContext(tokens)) { startContext(new ComponentFinderDslContext(this, getContext(ContainerDslContext.class))); } } else { - throwRestrictedModeException(firstToken); + throw new FeatureNotEnabledException(Features.COMPONENT_FINDER, firstToken + " is not permitted"); } } else if (COMPONENT_FINDER_CLASSES_TOKEN.equalsIgnoreCase(firstToken) && inContext(ComponentFinderDslContext.class)) { @@ -674,8 +694,10 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn if (parsedTokens.contains(WORKSPACE_TOKEN)) { throw new RuntimeException("Multiple workspaces are not permitted in a DSL definition"); } - DslParserContext dslParserContext = new DslParserContext(this, dslFile, restricted); + DslParserContext dslParserContext = new DslParserContext(this, dslFile); dslParserContext.setIdentifierRegister(identifiersRegister); + dslParserContext.setFeatures(features); + dslParserContext.setHttpClient(httpClient); workspace = new WorkspaceParser().parse(dslParserContext, tokens.withoutContextStartToken()); extendingWorkspace = !workspace.getModel().isEmpty(); @@ -685,7 +707,7 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn parsedTokens.add(WORKSPACE_TOKEN); } else if (IMPLIED_RELATIONSHIPS_TOKEN.equalsIgnoreCase(firstToken) || IMPLIED_RELATIONSHIPS_TOKEN.substring(1).equalsIgnoreCase(firstToken)) { - new ImpliedRelationshipsParser().parse(getContext(), tokens, dslFile, restricted); + new ImpliedRelationshipsParser().parse(getContext(), tokens, dslFile); } else if (NAME_TOKEN.equalsIgnoreCase(firstToken) && inContext(WorkspaceDslContext.class)) { new WorkspaceParser().parseName(getContext(), tokens); @@ -817,7 +839,7 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn startContext(new BrandingDslContext(dslFile)); } else if (BRANDING_LOGO_TOKEN.equalsIgnoreCase(firstToken) && inContext(BrandingDslContext.class)) { - new BrandingParser().parseLogo(getContext(BrandingDslContext.class), tokens, restricted); + new BrandingParser().parseLogo(getContext(BrandingDslContext.class), tokens); } else if (BRANDING_FONT_TOKEN.equalsIgnoreCase(firstToken) && inContext(BrandingDslContext.class)) { new BrandingParser().parseFont(getContext(BrandingDslContext.class), tokens); @@ -872,7 +894,7 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn new ElementStyleParser().parseDescription(getContext(ElementStyleDslContext.class), tokens); } else if (ELEMENT_STYLE_ICON_TOKEN.equalsIgnoreCase(firstToken) && inContext(ElementStyleDslContext.class)) { - new ElementStyleParser().parseIcon(getContext(ElementStyleDslContext.class), tokens, restricted); + new ElementStyleParser().parseIcon(getContext(ElementStyleDslContext.class), tokens); } else if (ELEMENT_STYLE_ICON_POSITION_TOKEN.equalsIgnoreCase(firstToken) && inContext(ElementStyleDslContext.class)) { new ElementStyleParser().parseIconPosition(getContext(ElementStyleDslContext.class), tokens); @@ -1088,16 +1110,16 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn new ViewParser().parseDescription(getContext(ViewDslContext.class), tokens); } else if (PLANTUML_TOKEN.equalsIgnoreCase(firstToken) && inContext(ImageViewDslContext.class)) { - new ImageViewContentParser(restricted).parsePlantUML(getContext(ImageViewDslContext.class), dslFile, tokens); + new ImageViewContentParser().parsePlantUML(getContext(ImageViewDslContext.class), dslFile, tokens); } else if (MERMAID_TOKEN.equalsIgnoreCase(firstToken) && inContext(ImageViewDslContext.class)) { - new ImageViewContentParser(restricted).parseMermaid(getContext(ImageViewDslContext.class), dslFile, tokens); + new ImageViewContentParser().parseMermaid(getContext(ImageViewDslContext.class), dslFile, tokens); } else if (KROKI_TOKEN.equalsIgnoreCase(firstToken) && inContext(ImageViewDslContext.class)) { - new ImageViewContentParser(restricted).parseKroki(getContext(ImageViewDslContext.class), dslFile, tokens); + new ImageViewContentParser().parseKroki(getContext(ImageViewDslContext.class), dslFile, tokens); } else if (IMAGE_VIEW_TOKEN.equalsIgnoreCase(firstToken) && inContext(ImageViewDslContext.class)) { - new ImageViewContentParser(restricted).parseImage(getContext(ImageViewDslContext.class), dslFile, tokens); + new ImageViewContentParser().parseImage(getContext(ImageViewDslContext.class), dslFile, tokens); } else if (LIGHT_COLOR_SCHEME_TOKEN.equalsIgnoreCase(firstToken) && inContext(ImageViewDslContext.class) && shouldStartContext(tokens)) { ImageViewDslContext context = getContext(ImageViewDslContext.class); @@ -1124,10 +1146,10 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn new DynamicViewRelationshipParser().parseUrl(getContext(DynamicViewRelationshipContext.class), tokens.withoutContextStartToken()); } else if (THEME_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ViewsDslContext.class) || inContext(StylesDslContext.class))) { - new ThemeParser().parseTheme(getContext(), dslFile, tokens, restricted); + new ThemeParser().parseTheme(getContext(), dslFile, tokens); } else if (THEMES_TOKEN.equalsIgnoreCase(firstToken) && (inContext(ViewsDslContext.class) || inContext(StylesDslContext.class))) { - new ThemeParser().parseThemes(getContext(), dslFile, tokens, restricted); + new ThemeParser().parseThemes(getContext(), dslFile, tokens); } else if (TERMINOLOGY_TOKEN.equalsIgnoreCase(firstToken) && inContext(ViewsDslContext.class)) { startContext(new TerminologyDslContext()); @@ -1172,59 +1194,59 @@ void parse(List lines, File dslFile, boolean fragment, boolean includeIn new UserRoleParser().parse(getContext(), tokens); } else if (DOCS_TOKEN.equalsIgnoreCase(firstToken) && inContext(WorkspaceDslContext.class)) { - if (!restricted) { + if (features.isEnabled(Features.DOCUMENTATION)) { new DocsParser().parse(getContext(WorkspaceDslContext.class), dslFile, tokens); } else { - throwRestrictedModeException(firstToken); + throw new FeatureNotEnabledException(Features.DOCUMENTATION, firstToken + " is not permitted"); } } else if (DOCS_TOKEN.equalsIgnoreCase(firstToken) && inContext(SoftwareSystemDslContext.class)) { - if (!restricted) { + if (features.isEnabled(Features.DOCUMENTATION)) { new DocsParser().parse(getContext(SoftwareSystemDslContext.class), dslFile, tokens); } else { - throwRestrictedModeException(firstToken); + throw new FeatureNotEnabledException(Features.DOCUMENTATION, firstToken + " is not permitted"); } } else if (DOCS_TOKEN.equalsIgnoreCase(firstToken) && inContext(ContainerDslContext.class)) { - if (!restricted) { + if (features.isEnabled(Features.DOCUMENTATION)) { new DocsParser().parse(getContext(ContainerDslContext.class), dslFile, tokens); } else { - throwRestrictedModeException(firstToken); + throw new FeatureNotEnabledException(Features.DOCUMENTATION, firstToken + " is not permitted"); } } else if (DOCS_TOKEN.equalsIgnoreCase(firstToken) && inContext(ComponentDslContext.class)) { - if (!restricted) { + if (features.isEnabled(Features.DOCUMENTATION)) { new DocsParser().parse(getContext(ComponentDslContext.class), dslFile, tokens); } else { - throwRestrictedModeException(firstToken); + throw new FeatureNotEnabledException(Features.DOCUMENTATION, firstToken + " is not permitted"); } } else if ((ADRS_TOKEN.equalsIgnoreCase(firstToken) || DECISIONS_TOKEN.equalsIgnoreCase(firstToken)) && inContext(WorkspaceDslContext.class)) { - if (!restricted) { + if (features.isEnabled(Features.DECISIONS)) { new DecisionsParser().parse(getContext(WorkspaceDslContext.class), dslFile, tokens); } else { - throwRestrictedModeException(firstToken); + throw new FeatureNotEnabledException(Features.DECISIONS, firstToken + " is not permitted"); } } else if ((ADRS_TOKEN.equalsIgnoreCase(firstToken) || DECISIONS_TOKEN.equalsIgnoreCase(firstToken)) && inContext(SoftwareSystemDslContext.class)) { - if (!restricted) { + if (features.isEnabled(Features.DECISIONS)) { new DecisionsParser().parse(getContext(SoftwareSystemDslContext.class), dslFile, tokens); } else { - throwRestrictedModeException(firstToken); + throw new FeatureNotEnabledException(Features.DECISIONS, firstToken + " is not permitted"); } } else if ((ADRS_TOKEN.equalsIgnoreCase(firstToken) || DECISIONS_TOKEN.equalsIgnoreCase(firstToken)) && inContext(ContainerDslContext.class)) { - if (!restricted) { + if (features.isEnabled(Features.DECISIONS)) { new DecisionsParser().parse(getContext(ContainerDslContext.class), dslFile, tokens); } else { - throwRestrictedModeException(firstToken); + throw new FeatureNotEnabledException(Features.DECISIONS, firstToken + " is not permitted"); } } else if ((ADRS_TOKEN.equalsIgnoreCase(firstToken) || DECISIONS_TOKEN.equalsIgnoreCase(firstToken)) && inContext(ComponentDslContext.class)) { - if (!restricted) { + if (features.isEnabled(Features.DECISIONS)) { new DecisionsParser().parse(getContext(ComponentDslContext.class), dslFile, tokens); } else { - throwRestrictedModeException(firstToken); + throw new FeatureNotEnabledException(Features.DECISIONS, firstToken + " is not permitted"); } } else if (CONSTANT_TOKEN.equalsIgnoreCase(firstToken)) { @@ -1360,10 +1382,6 @@ private List preProcessLines(List lines) { return dslLines; } - private void throwRestrictedModeException(String firstToken) { - throw new RuntimeException(firstToken + " is not available when the parser is running in restricted mode"); - } - private String substituteStrings(String token) { Matcher m = STRING_SUBSTITUTION_PATTERN.matcher(token); while (m.find()) { @@ -1379,7 +1397,7 @@ private String substituteStrings(String token) { after = nameValuePair.getValue(); } } else { - if (!restricted) { + if (getFeatures().isEnabled(Features.ENVIRONMENT)) { String environmentVariable = System.getenv().get(name); if (environmentVariable != null) { after = environmentVariable; @@ -1403,6 +1421,8 @@ private void startContext(DslContext context) { context.setWorkspace(workspace); context.setIdentifierRegister(identifiersRegister); context.setExtendingWorkspace(extendingWorkspace); + context.setFeatures(features); + context.setHttpClient(httpClient); contextStack.push(context); } @@ -1445,6 +1465,18 @@ public Features getFeatures() { return features; } + void setFeatures(Features features) { + this.features = features; + } + + public HttpClient getHttpClient() { + return httpClient; + } + + void setHttpClient(HttpClient httpClient) { + this.httpClient = httpClient; + } + private boolean isElementKeywordOrArchetype(String token, String keyword) { if (token.equalsIgnoreCase(keyword)) { return true; diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/ThemeParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/ThemeParser.java index d228972ca..90b5e3e73 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/ThemeParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/ThemeParser.java @@ -1,5 +1,6 @@ package com.structurizr.dsl; +import com.structurizr.util.FeatureNotEnabledException; import com.structurizr.util.Url; import com.structurizr.view.ThemeUtils; @@ -12,7 +13,7 @@ final class ThemeParser extends AbstractParser { private final static int FIRST_THEME_INDEX = 1; - void parseTheme(DslContext context, File dslFile, Tokens tokens, boolean restricted) { + void parseTheme(DslContext context, File dslFile, Tokens tokens) { // theme if (tokens.hasMoreThan(FIRST_THEME_INDEX)) { throw new RuntimeException("Too many tokens, expected: theme "); @@ -22,21 +23,21 @@ void parseTheme(DslContext context, File dslFile, Tokens tokens, boolean restric throw new RuntimeException("Expected: theme "); } - addTheme(context, dslFile, tokens.get(FIRST_THEME_INDEX), restricted); + addTheme(context, dslFile, tokens.get(FIRST_THEME_INDEX)); } - void parseThemes(DslContext context, File dslFile, Tokens tokens, boolean restricted) { + void parseThemes(DslContext context, File dslFile, Tokens tokens) { // themes [url|file] ... [url|file] if (!tokens.includes(FIRST_THEME_INDEX)) { throw new RuntimeException("Expected: themes [url|file] ... [url|file]"); } for (int i = FIRST_THEME_INDEX; i < tokens.size(); i++) { - addTheme(context, dslFile, tokens.get(i), restricted); + addTheme(context, dslFile, tokens.get(i)); } } - private void addTheme(DslContext context, File dslFile, String theme, boolean restricted) { + private void addTheme(DslContext context, File dslFile, String theme) { if (DEFAULT_THEME_NAME.equalsIgnoreCase(theme)) { theme = DEFAULT_THEME_URL; } @@ -45,7 +46,7 @@ private void addTheme(DslContext context, File dslFile, String theme, boolean re // this adds the theme to the list of theme URLs in the workspace context.getWorkspace().getViews().getConfiguration().addTheme(theme); } else { - if (!restricted) { + if (context.getFeatures().isEnabled(Features.FILE_SYSTEM)) { context.setDslPortable(false); // this inlines the file-based theme into the workspace @@ -64,7 +65,7 @@ private void addTheme(DslContext context, File dslFile, String theme, boolean re throw new RuntimeException(file.getAbsolutePath() + " does not exist"); } } else { - throw new RuntimeException("File-based themes are not supported when the DSL parser is running in restricted mode"); + throw new FeatureNotEnabledException(Features.FILE_SYSTEM, "File-based themes are not permitted"); } } } diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/WorkspaceParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/WorkspaceParser.java index e116de60a..59ebc4a54 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/WorkspaceParser.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/WorkspaceParser.java @@ -1,9 +1,12 @@ package com.structurizr.dsl; import com.structurizr.Workspace; +import com.structurizr.http.RemoteContent; import com.structurizr.model.CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy; import com.structurizr.model.Element; import com.structurizr.model.Relationship; +import com.structurizr.util.FeatureNotEnabledException; +import com.structurizr.util.Url; import com.structurizr.util.WorkspaceUtils; import java.io.File; @@ -36,24 +39,32 @@ Workspace parse(DslParserContext context, Tokens tokens) { String source = tokens.get(SECOND_INDEX); try { - if (source.startsWith("https://") || source.startsWith("http://")) { - RemoteContent content = readFromUrl(source); + if (Url.isHttpsUrl(source) || Url.isHttpUrl(source)) { + if (Url.isHttpsUrl(source) && !context.getFeatures().isEnabled(Features.HTTPS)) { + throw new FeatureNotEnabledException(Features.HTTPS, "Extends via HTTPS are not permitted"); + } + if (Url.isHttpUrl(source) && !context.getFeatures().isEnabled(Features.HTTP)) { + throw new FeatureNotEnabledException(Features.HTTP, "Extends via HTTP are not permitted"); + } + + RemoteContent remoteContent = context.getHttpClient().get(source); - if (source.endsWith(".json") || content.getContentType().startsWith(RemoteContent.CONTENT_TYPE_JSON)) { - String json = content.getContent(); + if (source.toLowerCase().endsWith(".json") || remoteContent.getContentType().startsWith(RemoteContent.CONTENT_TYPE_JSON)) { + String json = remoteContent.getContentAsString(); workspace = WorkspaceUtils.fromJson(json); registerIdentifiers(workspace, context); } else { - String dsl = content.getContent(); - StructurizrDslParser structurizrDslParser = new StructurizrDslParser(); - structurizrDslParser.setRestricted(context.isRestricted()); + String dsl = remoteContent.getContentAsString(); + + StructurizrDslParser structurizrDslParser = createParser(context); structurizrDslParser.parse(context, dsl); + workspace = structurizrDslParser.getWorkspace(); context.getParser().configureFrom(structurizrDslParser); } } else { - if (context.isRestricted()) { - throw new RuntimeException("Cannot import workspace from a file when running in restricted mode"); + if (!context.getFeatures().isEnabled(Features.FILE_SYSTEM)) { + throw new FeatureNotEnabledException(Features.FILE_SYSTEM, "Extending a file-based workspace is not permitted"); } if (context.getFile() != null) { @@ -66,12 +77,13 @@ Workspace parse(DslParserContext context, Tokens tokens) { throw new RuntimeException(file.getCanonicalPath() + " should be a single file"); } - if (source.endsWith(".json")) { + if (source.toLowerCase().endsWith(".json")) { workspace = WorkspaceUtils.loadWorkspaceFromJson(file); registerIdentifiers(workspace, context); } else { - StructurizrDslParser structurizrDslParser = new StructurizrDslParser(); + StructurizrDslParser structurizrDslParser = createParser(context); structurizrDslParser.parse(context, file); + workspace = structurizrDslParser.getWorkspace(); context.getParser().configureFrom(structurizrDslParser); } @@ -100,6 +112,14 @@ Workspace parse(DslParserContext context, Tokens tokens) { return workspace; } + private StructurizrDslParser createParser(DslParserContext context) { + StructurizrDslParser parser = new StructurizrDslParser(); + parser.setFeatures(context.getFeatures()); + parser.setHttpClient(context.getHttpClient()); + + return parser; + } + private void registerIdentifiers(Workspace workspace, DslParserContext context) { for (Element element : workspace.getModel().getElements()) { if (element.getProperties().containsKey(STRUCTURIZR_DSL_IDENTIFIER_PROPERTY_NAME)) { diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/BrandingParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/BrandingParserTests.java index dec579440..1bf47b5a1 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/BrandingParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/BrandingParserTests.java @@ -15,7 +15,7 @@ void test_parseLogo_ThrowsAnException_WhenThereAreTooManyTokens() { BrandingDslContext context = new BrandingDslContext(null); try { - parser.parseLogo(context, tokens("logo", "path", "extra"), false); + parser.parseLogo(context, tokens("logo", "path", "extra")); fail(); } catch (Exception e) { assertEquals("Too many tokens, expected: logo ", e.getMessage()); @@ -27,7 +27,7 @@ void test_parseLogo_ThrowsAnException_WhenNoPathIsSpecified() { BrandingDslContext context = new BrandingDslContext(null); try { - parser.parseLogo(context, tokens("logo"), false); + parser.parseLogo(context, tokens("logo")); fail(); } catch (Exception e) { assertEquals("Expected: logo ", e.getMessage()); @@ -37,9 +37,10 @@ void test_parseLogo_ThrowsAnException_WhenNoPathIsSpecified() { @Test void test_parseLogo_ThrowsAnException_WhenTheLogoDoesNotExist() { BrandingDslContext context = new BrandingDslContext(new File(".")); + context.getFeatures().enable(Features.FILE_SYSTEM); try { - parser.parseLogo(context, tokens("logo", "hello.png"), false); + parser.parseLogo(context, tokens("logo", "hello.png")); fail(); } catch (Exception e) { assertEquals("hello.png does not exist", e.getMessage()); @@ -49,9 +50,10 @@ void test_parseLogo_ThrowsAnException_WhenTheLogoDoesNotExist() { @Test void test_parseLogo_ThrowsAnException_WhenTheFileIsNotSupported() { BrandingDslContext context = new BrandingDslContext(new File(".")); + context.getFeatures().enable(Features.FILE_SYSTEM); try { - parser.parseLogo(context, tokens("logo", "src/test/resources/dsl/getting-started.dsl"), false); + parser.parseLogo(context, tokens("logo", "src/test/resources/dsl/getting-started.dsl")); fail(); } catch (Exception e) { e.printStackTrace(); @@ -63,8 +65,9 @@ void test_parseLogo_ThrowsAnException_WhenTheFileIsNotSupported() { void test_parseLogo_SetsTheLogo_WhenTheLogoDoesExist() { BrandingDslContext context = new BrandingDslContext(new File(".")); context.setWorkspace(workspace); + context.getFeatures().enable(Features.FILE_SYSTEM); - parser.parseLogo(context, tokens("logo", "src/test/resources/dsl/logo.png"), false); + parser.parseLogo(context, tokens("logo", "src/test/resources/dsl/logo.png")); assertTrue(workspace.getViews().getConfiguration().getBranding().getLogo().startsWith("data:image/png;base64,")); } @@ -73,25 +76,55 @@ void test_parseLogo_SetsTheLogoFromADataUri() { BrandingDslContext context = new BrandingDslContext(new File(".")); context.setWorkspace(workspace); - parser.parseLogo(context, tokens("logo", "data:image/png;base64,123456789012345678901234567890"), true); + parser.parseLogo(context, tokens("logo", "data:image/png;base64,123456789012345678901234567890")); assertTrue(workspace.getViews().getConfiguration().getBranding().getLogo().startsWith("data:image/png;base64,123456789012345678901234567890")); } + @Test + void test_parseLogo_ThrowsAnException_WithAHttpIconAndHttpIsNotEnabled() { + BrandingDslContext context = new BrandingDslContext(new File(".")); + context.setWorkspace(workspace); + context.getFeatures().disable(Features.HTTP); + + try { + parser.parseLogo(context, tokens("logo", "http://structurizr.com/logo.png")); + fail(); + } catch (Exception e) { + assertEquals("Icons via HTTP are not permitted (feature structurizr.feature.dsl.http is not enabled)", e.getMessage()); + } + } + @Test void test_parseLogo_SetsTheLogoFromAHttpUrl() { BrandingDslContext context = new BrandingDslContext(new File(".")); context.setWorkspace(workspace); + context.getFeatures().enable(Features.HTTP); - parser.parseLogo(context, tokens("logo", "http://structurizr.com/logo.png"), true); + parser.parseLogo(context, tokens("logo", "http://structurizr.com/logo.png")); assertEquals("http://structurizr.com/logo.png", workspace.getViews().getConfiguration().getBranding().getLogo()); } + @Test + void test_parseLogo_ThrowsAnException_WithAHttpsIconAndHttpsIsNotEnabled() { + BrandingDslContext context = new BrandingDslContext(new File(".")); + context.setWorkspace(workspace); + context.getFeatures().disable(Features.HTTPS); + + try { + parser.parseLogo(context, tokens("logo", "https://structurizr.com/logo.png")); + fail(); + } catch (Exception e) { + assertEquals("Icons via HTTPS are not permitted (feature structurizr.feature.dsl.https is not enabled)", e.getMessage()); + } + } + @Test void test_parseLogo_SetsTheLogoFromAHttpsUrl() { BrandingDslContext context = new BrandingDslContext(new File(".")); context.setWorkspace(workspace); + context.getFeatures().enable(Features.HTTPS); - parser.parseLogo(context, tokens("logo", "https://structurizr.com/logo.png"), true); + parser.parseLogo(context, tokens("logo", "https://structurizr.com/logo.png")); assertEquals("https://structurizr.com/logo.png", workspace.getViews().getConfiguration().getBranding().getLogo()); } diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index f45817bd5..fd3284149 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -4,7 +4,6 @@ import com.structurizr.documentation.Section; import com.structurizr.model.*; import com.structurizr.util.StringUtils; -import com.structurizr.util.WorkspaceUtils; import com.structurizr.view.*; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @@ -23,6 +22,7 @@ class DslTests extends AbstractTests { @Test void test_test() throws Exception { StructurizrDslParser parser = new StructurizrDslParser(); + parser.getFeatures().configure(Features.FILE_SYSTEM, true); parser.parse(new File("src/test/resources/dsl/test.dsl")); assertFalse(parser.getWorkspace().isEmpty()); @@ -374,6 +374,7 @@ void test_includeLocalDirectory_WhenThereAreHiddenFiles() throws Exception { @Tag("IntegrationTest") void test_includeUrl() throws Exception { StructurizrDslParser parser = new StructurizrDslParser(); + parser.getHttpClient().allow(".*"); parser.parse(new File("src/test/resources/dsl/include-url.dsl")); Workspace workspace = parser.getWorkspace(); @@ -392,21 +393,6 @@ void test_includeUrl() throws Exception { }""", DslUtils.getDsl(workspace)); } - @Test - void test_includeLocalFile_ThrowsAnException_WhenRunningInRestrictedMode() { - try { - StructurizrDslParser parser = new StructurizrDslParser(); - parser.setRestricted(true); - - // the model include will be ignored, so no software systems - parser.parse(new File("src/test/resources/dsl/include-file.dsl")); - fail(); - } catch (Exception e) { - System.out.println(e.getMessage()); - assertTrue(e.getMessage().startsWith("!include is not available when the parser is running in restricted mode")); - } - } - @Test @Tag("IntegrationTest") void test_extendWorkspaceFromJsonFile() throws Exception { @@ -437,6 +423,7 @@ void test_extendWorkspaceFromJsonFile() throws Exception { void test_extendWorkspaceFromJsonUrl() throws Exception { String dslFile = "src/test/resources/dsl/extend/extend-workspace-from-json-url.dsl"; StructurizrDslParser parser = new StructurizrDslParser(); + parser.getHttpClient().allow(".*"); parser.parse(new File(dslFile)); Workspace workspace = parser.getWorkspace(); @@ -487,7 +474,7 @@ void test_extendWorkspaceFromJsonFile_WhenRunningInRestrictedMode() throws Excep parser.parse(dslFile); fail(); } catch (StructurizrDslParserException e) { - assertEquals("Cannot import workspace from a file when running in restricted mode at line 1 of " + dslFile.getAbsolutePath() + ": workspace extends workspace.json {", e.getMessage()); + assertEquals("Extending a file-based workspace is not permitted (feature structurizr.feature.dsl.filesystem is not enabled) at line 1 of " + dslFile.getAbsolutePath() + ": workspace extends workspace.json {", e.getMessage()); } } @@ -496,6 +483,7 @@ void test_extendWorkspaceFromJsonFile_WhenRunningInRestrictedMode() throws Excep @ValueSource(strings = { "src/test/resources/dsl/extend/extend-workspace-from-dsl-file.dsl", "src/test/resources/dsl/extend/extend-workspace-from-dsl-url.dsl" }) void test_extendWorkspaceFromDsl(String dslFile) throws Exception { StructurizrDslParser parser = new StructurizrDslParser(); + parser.getHttpClient().allow(".*"); parser.parse(new File(dslFile)); Workspace workspace = parser.getWorkspace(); @@ -526,7 +514,7 @@ void test_extendWorkspaceFromDslFile_WhenRunningInRestrictedMode() throws Except parser.parse(dslFile); fail(); } catch (StructurizrDslParserException e) { - assertEquals("Cannot import workspace from a file when running in restricted mode at line 1 of " + dslFile.getAbsolutePath() +": workspace extends workspace.dsl {", e.getMessage()); + assertEquals("Extending a file-based workspace is not permitted (feature structurizr.feature.dsl.filesystem is not enabled) at line 1 of " + dslFile.getAbsolutePath() +": workspace extends workspace.dsl {", e.getMessage()); } } @@ -725,15 +713,15 @@ void test_hierarchicalIdentifiersAndDeploymentNodes_WhenSoftwareContainerClashes } @Test - void test_plugin_ThrowsAnException_WhenTheParserIsRunningInRestrictedMode() { + void test_plugin_ThrowsAnException_WhenPluginsAreNotEnabled() { try { StructurizrDslParser parser = new StructurizrDslParser(); - parser.setRestricted(true); + parser.getFeatures().disable(Features.PLUGINS); parser.parse(new File("src/test/resources/dsl/plugin-without-parameters.dsl")); fail(); } catch (Exception e) { System.out.println(e.getMessage()); - assertTrue(e.getMessage().startsWith("!plugin is not available when the parser is running in restricted mode")); + assertTrue(e.getMessage().startsWith("!plugin is not permitted (feature structurizr.feature.dsl.plugins is not enabled)")); } } @@ -760,14 +748,15 @@ void test_pluginWithParameters() throws Exception { } @Test - void test_script_ThrowsAnException_WhenTheParserIsInRestrictedMode() { + void test_script_ThrowsAnException_WhenScriptsAreNotEnabled() { try { StructurizrDslParser parser = new StructurizrDslParser(); - parser.setRestricted(true); + parser.getFeatures().disable(Features.SCRIPTS); parser.parse(new File("src/test/resources/dsl/script-external.dsl")); fail(); } catch (Exception e) { - assertTrue(e.getMessage().startsWith("!script is not available when the parser is running in restricted mode")); + System.out.println(e.getMessage()); + assertTrue(e.getMessage().startsWith("!script is not permitted (feature structurizr.feature.dsl.scripts is not enabled)")); } } @@ -863,7 +852,8 @@ void test_docs_ThrowsAnException_WhenTheParserIsInRestrictedMode() { parser.parse(new File("src/test/resources/dsl/docs/workspace.dsl")); fail(); } catch (Exception e) { - assertTrue(e.getMessage().startsWith("!docs is not available when the parser is running in restricted mode")); + System.out.println(e.getMessage()); + assertTrue(e.getMessage().startsWith("!docs is not permitted (feature structurizr.feature.dsl.documentation is not enabled)")); } } @@ -898,7 +888,8 @@ void test_decisions_ThrowsAnException_WhenTheParserIsInRestrictedMode() { parser.parse(new File("src/test/resources/dsl/decisions/workspace.dsl")); fail(); } catch (Exception e) { - assertTrue(e.getMessage().startsWith("!adrs is not available when the parser is running in restricted mode")); + System.out.println(e.getMessage()); + assertTrue(e.getMessage().startsWith("!adrs is not permitted (feature structurizr.feature.dsl.decisions is not enabled)")); } } @@ -1101,6 +1092,7 @@ void test_relationshipWithoutIdentifier() throws Exception { @Test void test_imageViews_ViaFiles() throws Exception { StructurizrDslParser parser = new StructurizrDslParser(); + parser.getFeatures().configure(Features.FILE_SYSTEM, true); parser.parse(new File("src/test/resources/dsl/image-views/workspace-via-file.dsl")); Workspace workspace = parser.getWorkspace(); @@ -1139,6 +1131,7 @@ void test_imageViews_ViaFiles() throws Exception { @Tag("IntegrationTest") void test_imageViews_ViaUrls() throws Exception { StructurizrDslParser parser = new StructurizrDslParser(); + parser.getHttpClient().allow(".*"); parser.parse(new File("src/test/resources/dsl/image-views/workspace-via-url.dsl")); Workspace workspace = parser.getWorkspace(); @@ -1534,7 +1527,7 @@ void test_ImageView_WhenParserIsInRestrictedMode() { parser.parse(dslFile); fail(); } catch (StructurizrDslParserException e) { - assertEquals("Images must be specified as a URL when running in restricted mode at line 5 of " + dslFile.getAbsolutePath() + ": image image.png", e.getMessage()); + assertEquals("image is not permitted (feature structurizr.feature.dsl.filesystem is not enabled) at line 5 of " + dslFile.getAbsolutePath() + ": image image.png", e.getMessage()); } } diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementStyleParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementStyleParserTests.java index 83e72cb77..65baa3c09 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementStyleParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ElementStyleParserTests.java @@ -12,7 +12,7 @@ class ElementStyleParserTests extends AbstractTests { - private ElementStyleParser parser = new ElementStyleParser(); + private final ElementStyleParser parser = new ElementStyleParser(); private ElementStyle elementStyle; private ElementStyleDslContext elementStyleDslContext() { @@ -499,7 +499,7 @@ void test_parseDescription_SetsTheDescription() { @Test void test_parseIcon_ThrowsAnException_WhenThereAreTooManyTokens() { try { - parser.parseIcon(elementStyleDslContext(), tokens("icon", "file", "extra"), false); + parser.parseIcon(elementStyleDslContext(), tokens("icon", "file", "extra")); fail(); } catch (Exception e) { assertEquals("Too many tokens, expected: icon ", e.getMessage()); @@ -509,7 +509,7 @@ void test_parseIcon_ThrowsAnException_WhenThereAreTooManyTokens() { @Test void test_parseIcon_ThrowsAnException_WhenTheIconIsMissing() { try { - parser.parseIcon(elementStyleDslContext(), tokens("icon"), false); + parser.parseIcon(elementStyleDslContext(), tokens("icon")); fail(); } catch (Exception e) { assertEquals("Expected: icon ", e.getMessage()); @@ -519,7 +519,10 @@ void test_parseIcon_ThrowsAnException_WhenTheIconIsMissing() { @Test void test_parseIcon_ThrowsAnException_WhenTheIconDoesNotExist() { try { - parser.parseIcon(elementStyleDslContext(), tokens("icon", "hello.png"), false); + ElementStyleDslContext context = elementStyleDslContext(); + context.getFeatures().enable(Features.FILE_SYSTEM); + + parser.parseIcon(context, tokens("icon", "hello.png")); fail(); } catch (Exception e) { assertEquals("hello.png does not exist", e.getMessage()); @@ -528,29 +531,77 @@ void test_parseIcon_ThrowsAnException_WhenTheIconDoesNotExist() { @Test void test_parseIcon_SetsTheIconFromADataUri() { - parser.parseIcon(elementStyleDslContext(), tokens("icon", "data:image/png;base64,123456789012345678901234567890"), true); + parser.parseIcon(elementStyleDslContext(), tokens("icon", "data:image/png;base64,123456789012345678901234567890")); assertTrue(elementStyle.getIcon().startsWith("data:image/png;base64,123456789012345678901234567890")); } + @Test + void test_parseIcon_ThrowsAnException_WithAHttpIconAndHttpIsNotEnabled() { + try { + ElementStyleDslContext context = elementStyleDslContext(); + context.getFeatures().disable(Features.HTTP); + + parser.parseIcon(context, tokens("icon", "http://structurizr.com/logo.png")); + fail(); + } catch (Exception e) { + assertEquals("Icons via HTTP are not permitted (feature structurizr.feature.dsl.http is not enabled)", e.getMessage()); + } + } + @Test void test_parseIcon_SetsTheIconFromAHttpUrl() { - parser.parseIcon(elementStyleDslContext(), tokens("icon", "http://structurizr.com/logo.png"), true); + ElementStyleDslContext context = elementStyleDslContext(); + context.getFeatures().enable(Features.HTTP); + + parser.parseIcon(context, tokens("icon", "http://structurizr.com/logo.png")); assertEquals("http://structurizr.com/logo.png", elementStyle.getIcon()); } + @Test + void test_parseIcon_ThrowsAnException_WithAHttpsIconAndHttpsIsNotEnabled() { + try { + ElementStyleDslContext context = elementStyleDslContext(); + context.getFeatures().disable(Features.HTTPS); + + parser.parseIcon(context, tokens("icon", "https://structurizr.com/logo.png")); + fail(); + } catch (Exception e) { + assertEquals("Icons via HTTPS are not permitted (feature structurizr.feature.dsl.https is not enabled)", e.getMessage()); + } + } + @Test void test_parseIcon_SetsTheIconFromAHttpsUrl() { - parser.parseIcon(elementStyleDslContext(), tokens("icon", "https://structurizr.com/logo.png"), true); + ElementStyleDslContext context = elementStyleDslContext(); + context.getFeatures().enable(Features.HTTPS); + + parser.parseIcon(context, tokens("icon", "https://structurizr.com/logo.png")); assertEquals("https://structurizr.com/logo.png", elementStyle.getIcon()); } @Test void test_parseIcon_SetsTheIconFromAFile() { - parser.parseIcon(elementStyleDslContext(), tokens("icon", "src/test/resources/dsl/logo.png"), false); + ElementStyleDslContext context = elementStyleDslContext(); + context.getFeatures().enable(Features.FILE_SYSTEM); + + parser.parseIcon(context, tokens("icon", "src/test/resources/dsl/logo.png")); System.out.println(elementStyle.getIcon()); assertTrue(elementStyle.getIcon().startsWith("data:image/png;base64,")); } + @Test + void test_parseIcon_ThrowsAnException_WhenFileSystemAccessIsNotEnabled() { + try { + ElementStyleDslContext context = elementStyleDslContext(); + context.getFeatures().disable(Features.FILE_SYSTEM); + + parser.parseIcon(context, tokens("icon", "src/test/resources/dsl/logo.png")); + fail(); + } catch (Exception e) { + assertEquals("!icon is not permitted (feature structurizr.feature.dsl.filesystem is not enabled)", e.getMessage()); + } + } + @Test void test_parseIconPosition_ThrowsAnException_WhenThereAreTooManyTokens() { try { diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewContentParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewContentParserTests.java index 28b826974..90c274bc6 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewContentParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewContentParserTests.java @@ -25,7 +25,7 @@ void test_parsePlantUML_ThrowsAnException_WithTooFewTokens() { try { ImageViewDslContext context = new ImageViewDslContext(imageView); context.setWorkspace(workspace); - parser = new ImageViewContentParser(true); + parser = new ImageViewContentParser(); parser.parsePlantUML(context, null, tokens("plantuml")); fail(); } catch (Exception e) { @@ -38,11 +38,39 @@ void test_parsePlantUML_ThrowsAnException_WhenUsingAFileNameInRestrictedMode() { try { ImageViewDslContext context = new ImageViewDslContext(imageView); context.setWorkspace(workspace); - parser = new ImageViewContentParser(true); + parser = new ImageViewContentParser(); parser.parsePlantUML(context, null, tokens("plantuml", "image.puml")); fail(); } catch (Exception e) { - assertEquals("PlantUML source must be specified as a URL when running in restricted mode", e.getMessage()); + assertEquals("plantuml is not permitted (feature structurizr.feature.dsl.filesystem is not enabled)", e.getMessage()); + } + } + + @Test + void test_parsePlantUML_ThrowsAnException_WhenUsingAHttpsUrlAndHttpsIsNotEnabled() { + try { + ImageViewDslContext context = new ImageViewDslContext(imageView); + context.setWorkspace(workspace); + context.getFeatures().disable(Features.HTTPS); + parser = new ImageViewContentParser(); + parser.parsePlantUML(context, null, tokens("plantuml", "https://example.com")); + fail(); + } catch (Exception e) { + assertEquals("Image views via HTTPS are not permitted (feature structurizr.feature.dsl.https is not enabled)", e.getMessage()); + } + } + + @Test + void test_parsePlantUML_ThrowsAnException_WhenUsingAHttpUrlAndHttpIsNotEnabled() { + try { + ImageViewDslContext context = new ImageViewDslContext(imageView); + context.setWorkspace(workspace); + context.getFeatures().disable(Features.HTTP); + parser = new ImageViewContentParser(); + parser.parsePlantUML(context, null, tokens("plantuml", "http://example.com")); + fail(); + } catch (Exception e) { + assertEquals("Image views via HTTP are not permitted (feature structurizr.feature.dsl.http is not enabled)", e.getMessage()); } } @@ -54,7 +82,7 @@ void test_parsePlantUML_Source() { @enduml"""; workspace.getViews().getConfiguration().addProperty(PlantUMLImporter.PLANTUML_URL_PROPERTY, "https://plantuml.com/plantuml"); - parser = new ImageViewContentParser(true); + parser = new ImageViewContentParser(); parser.parsePlantUML(new ImageViewDslContext(imageView), null, tokens("plantuml", source)); assertEquals("https://plantuml.com/plantuml/svg/SoWkIImgAStDuNBAJrBGjLDmpCbCJbMmKiX8pSd9vt98pKi1IW80", imageView.getContent()); } @@ -67,7 +95,7 @@ void test_parsePlantUML_Source_Light() { @enduml"""; workspace.getViews().getConfiguration().addProperty(PlantUMLImporter.PLANTUML_URL_PROPERTY, "https://plantuml.com/plantuml"); - parser = new ImageViewContentParser(true); + parser = new ImageViewContentParser(); ImageViewDslContext context = new ImageViewDslContext(imageView); context.setColorScheme(ColorScheme.Light); parser.parsePlantUML(context, null, tokens("plantuml", source)); @@ -82,7 +110,7 @@ void test_parsePlantUML_Source_Dark() { @enduml"""; workspace.getViews().getConfiguration().addProperty(PlantUMLImporter.PLANTUML_URL_PROPERTY, "https://plantuml.com/plantuml"); - parser = new ImageViewContentParser(true); + parser = new ImageViewContentParser(); ImageViewDslContext context = new ImageViewDslContext(imageView); context.setColorScheme(ColorScheme.Dark); parser.parsePlantUML(context, null, tokens("plantuml", source)); @@ -97,7 +125,7 @@ void test_parsePlantUML_WithViewKey() { workspace.getViews().createSystemLandscapeView("SystemLandscape", "Description").addAllElements(); workspace.getViews().getConfiguration().addProperty(PlantUMLImporter.PLANTUML_URL_PROPERTY, "https://plantuml.com/plantuml"); - parser = new ImageViewContentParser(true); + parser = new ImageViewContentParser(); parser.parsePlantUML(context, null, tokens("plantuml", "SystemLandscape")); assertEquals("System Landscape View", imageView.getTitle()); assertEquals("Description", imageView.getDescription()); @@ -111,7 +139,7 @@ void test_parseMermaid_ThrowsAnException_WithTooFewTokens() { try { ImageViewDslContext context = new ImageViewDslContext(imageView); context.setWorkspace(workspace); - parser = new ImageViewContentParser(true); + parser = new ImageViewContentParser(); parser.parseMermaid(context, null, tokens("mermaid")); fail(); } catch (Exception e) { @@ -124,11 +152,39 @@ void test_parseMermaid_ThrowsAnException_WhenUsingAFileNameInRestrictedMode() { try { ImageViewDslContext context = new ImageViewDslContext(imageView); context.setWorkspace(workspace); - parser = new ImageViewContentParser(true); - parser.parseMermaid(context, null, tokens("mermaid", "image.puml")); + parser = new ImageViewContentParser(); + parser.parseMermaid(context, null, tokens("mermaid", "image.mmd")); + fail(); + } catch (Exception e) { + assertEquals("mermaid is not permitted (feature structurizr.feature.dsl.filesystem is not enabled)", e.getMessage()); + } + } + + @Test + void test_parseMermaid_ThrowsAnException_WhenUsingAHttpsUrlAndHttpsIsNotEnabled() { + try { + ImageViewDslContext context = new ImageViewDslContext(imageView); + context.setWorkspace(workspace); + context.getFeatures().disable(Features.HTTPS); + parser = new ImageViewContentParser(); + parser.parseMermaid(context, null, tokens("mermaid", "https://example.com")); + fail(); + } catch (Exception e) { + assertEquals("Image views via HTTPS are not permitted (feature structurizr.feature.dsl.https is not enabled)", e.getMessage()); + } + } + + @Test + void test_parseMermaid_ThrowsAnException_WhenUsingAHttpUrlAndHttpIsNotEnabled() { + try { + ImageViewDslContext context = new ImageViewDslContext(imageView); + context.setWorkspace(workspace); + context.getFeatures().disable(Features.HTTP); + parser = new ImageViewContentParser(); + parser.parseMermaid(context, null, tokens("mermaid", "http://example.com")); fail(); } catch (Exception e) { - assertEquals("Mermaid source must be specified as a URL when running in restricted mode", e.getMessage()); + assertEquals("Image views via HTTP are not permitted (feature structurizr.feature.dsl.http is not enabled)", e.getMessage()); } } @@ -143,7 +199,7 @@ void test_parseMermaid_Source() { C -->|Three| F[fa:fa-car Car]"""; workspace.getViews().getConfiguration().addProperty(MermaidImporter.MERMAID_URL_PROPERTY, "https://mermaid.ink"); - parser = new ImageViewContentParser(true); + parser = new ImageViewContentParser(); parser.parseMermaid(new ImageViewDslContext(imageView), null, tokens("mermaid", source)); assertEquals("https://mermaid.ink/svg/pako:eJxVj70OgjAUhV_lppMm8gIMJlKUhUQHtspwAxfbSH9SaoihvLsgi571-85JzgSssS2xlHW9HRuJPkCV3w0sOQkuvRqCxqGGJDnGggJoa-gdIdsVFgZpnVPmsd_8bJWAT-WqEQSpzHPeEP_2r4Yi5KJEF6yrf0k12ghnoW5ymf8n0tPSuogO0w6TBj1w9DU7ANPkNaqWpRMLkvR6oqUOX31g8_wBLY9E1w==", imageView.getContent()); } @@ -159,7 +215,7 @@ void test_parseMermaid_Source_Light() { C -->|Three| F[fa:fa-car Car]"""; workspace.getViews().getConfiguration().addProperty(MermaidImporter.MERMAID_URL_PROPERTY, "https://mermaid.ink"); - parser = new ImageViewContentParser(true); + parser = new ImageViewContentParser(); ImageViewDslContext context = new ImageViewDslContext(imageView); context.setColorScheme(ColorScheme.Light); parser.parseMermaid(context, null, tokens("mermaid", source)); @@ -177,7 +233,7 @@ void test_parseMermaid_Source_Dark() { C -->|Three| F[fa:fa-car Car]"""; workspace.getViews().getConfiguration().addProperty(MermaidImporter.MERMAID_URL_PROPERTY, "https://mermaid.ink"); - parser = new ImageViewContentParser(true); + parser = new ImageViewContentParser(); ImageViewDslContext context = new ImageViewDslContext(imageView); context.setColorScheme(ColorScheme.Dark); parser.parseMermaid(context, null, tokens("mermaid", source)); @@ -192,7 +248,7 @@ void test_parseMermaid_WithViewKey() { workspace.getViews().createSystemLandscapeView("SystemLandscape", "Description").addAllElements(); workspace.getViews().getConfiguration().addProperty(MermaidImporter.MERMAID_URL_PROPERTY, "https://mermaid.ink"); - parser = new ImageViewContentParser(true); + parser = new ImageViewContentParser(); parser.parseMermaid(context, null, tokens("mermaid", "SystemLandscape")); assertEquals("System Landscape View", imageView.getTitle()); assertEquals("Description", imageView.getDescription()); @@ -204,7 +260,7 @@ void test_parseKroki_ThrowsAnException_WithTooFewTokens() { try { ImageViewDslContext context = new ImageViewDslContext(imageView); context.setWorkspace(workspace); - parser = new ImageViewContentParser(true); + parser = new ImageViewContentParser(); parser.parseKroki(context, null, tokens("kroki")); fail(); } catch (Exception e) { @@ -215,11 +271,39 @@ void test_parseKroki_ThrowsAnException_WithTooFewTokens() { @Test void test_parseKroki_ThrowsAnException_WhenUsingAFileNameInRestrictedMode() { try { - parser = new ImageViewContentParser(true); + parser = new ImageViewContentParser(); parser.parseKroki(new ImageViewDslContext(imageView), null, tokens("kroki", "plantuml", "image.puml")); fail(); } catch (Exception e) { - assertEquals("Kroki source must be specified as a URL when running in restricted mode", e.getMessage()); + assertEquals("kroki plantuml is not permitted (feature structurizr.feature.dsl.filesystem is not enabled)", e.getMessage()); + } + } + + @Test + void test_parseKroki_ThrowsAnException_WhenUsingAHttpsUrlAndHttpsIsNotEnabled() { + try { + ImageViewDslContext context = new ImageViewDslContext(imageView); + context.setWorkspace(workspace); + context.getFeatures().disable(Features.HTTPS); + parser = new ImageViewContentParser(); + parser.parseKroki(context, null, tokens("kroki", "plantuml", "https://example.com")); + fail(); + } catch (Exception e) { + assertEquals("Image views via HTTPS are not permitted (feature structurizr.feature.dsl.https is not enabled)", e.getMessage()); + } + } + + @Test + void test_parseKroki_ThrowsAnException_WhenUsingAHttpUrlAndHttpIsNotEnabled() { + try { + ImageViewDslContext context = new ImageViewDslContext(imageView); + context.setWorkspace(workspace); + context.getFeatures().disable(Features.HTTP); + parser = new ImageViewContentParser(); + parser.parseKroki(context, null, tokens("kroki", "plantuml", "http://example.com")); + fail(); + } catch (Exception e) { + assertEquals("Image views via HTTP are not permitted (feature structurizr.feature.dsl.http is not enabled)", e.getMessage()); } } @@ -234,7 +318,7 @@ void test_parseKroki_Source() { C -->|Three| F[fa:fa-car Car]"""; workspace.getViews().getConfiguration().addProperty(KrokiImporter.KROKI_URL_PROPERTY, "https://kroki.io"); - parser = new ImageViewContentParser(true); + parser = new ImageViewContentParser(); parser.parseKroki(new ImageViewDslContext(imageView), null, tokens("kroki", "mermaid", source)); assertEquals("https://kroki.io/mermaid/png/eNpVjLEOwiAURXe_4o068AMOJpZqlyZ16EYYXhrwES2PAEljxH-XdtK7nnOuffIyEcYMY7uDurOSFF3KMyYNQpxKZzLM7M2rQLPvGBJxCM7fD5verA7Id79aBjI5__hsRG714E2BVvUYMgf9A8aFC1yUu1H9_gMUTW2uyuLRopgwgsSovzbHM0c=", imageView.getContent()); } @@ -250,7 +334,7 @@ void test_parseKroki_Source_Light() { C -->|Three| F[fa:fa-car Car]"""; workspace.getViews().getConfiguration().addProperty(KrokiImporter.KROKI_URL_PROPERTY, "https://kroki.io"); - parser = new ImageViewContentParser(true); + parser = new ImageViewContentParser(); ImageViewDslContext context = new ImageViewDslContext(imageView); context.setColorScheme(ColorScheme.Light); parser.parseKroki(context, null, tokens("kroki", "mermaid", source)); @@ -268,7 +352,7 @@ void test_parseKroki_Source_Dark() { C -->|Three| F[fa:fa-car Car]"""; workspace.getViews().getConfiguration().addProperty(KrokiImporter.KROKI_URL_PROPERTY, "https://kroki.io"); - parser = new ImageViewContentParser(true); + parser = new ImageViewContentParser(); ImageViewDslContext context = new ImageViewDslContext(imageView); context.setColorScheme(ColorScheme.Dark); parser.parseKroki(context, null, tokens("kroki", "mermaid", source)); @@ -278,37 +362,47 @@ void test_parseKroki_Source_Dark() { @Test void test_parseImage_ThrowsAnException_WhenUsingAFileNameInRestrictedMode() { try { - parser = new ImageViewContentParser(true); + parser = new ImageViewContentParser(); parser.parseImage(new ImageViewDslContext(imageView), null, tokens("image", "image.png")); fail(); } catch (Exception e) { - assertEquals("Images must be specified as a URL when running in restricted mode", e.getMessage()); + assertEquals("image is not permitted (feature structurizr.feature.dsl.filesystem is not enabled)", e.getMessage()); } } @Test void test_parseImage() { - parser = new ImageViewContentParser(true); - parser.parseImage(new ImageViewDslContext(imageView), null, tokens("image", "https://example.com/image.png")); - assertEquals("https://example.com/image.png", imageView.getContent()); + parser = new ImageViewContentParser(); + ImageViewDslContext context = new ImageViewDslContext(imageView); + context.getFeatures().enable(Features.HTTPS); + context.getHttpClient().allow(".*"); + + parser.parseImage(context, null, tokens("image", "https://static.structurizr.com/img/structurizr-banner.png")); + assertEquals("https://static.structurizr.com/img/structurizr-banner.png", imageView.getContent()); } @Test void test_parseImage_Url_Light() { - parser = new ImageViewContentParser(true); + parser = new ImageViewContentParser(); ImageViewDslContext context = new ImageViewDslContext(imageView); context.setColorScheme(ColorScheme.Light); - parser.parseImage(context, null, tokens("image", "https://example.com/image.png")); - assertEquals("https://example.com/image.png", imageView.getContentLight()); + context.getFeatures().enable(Features.HTTPS); + context.getHttpClient().allow(".*"); + + parser.parseImage(context, null, tokens("image", "https://static.structurizr.com/img/structurizr-banner.png")); + assertEquals("https://static.structurizr.com/img/structurizr-banner.png", imageView.getContentLight()); } @Test void test_parseImage_Url_Dark() { - parser = new ImageViewContentParser(true); + parser = new ImageViewContentParser(); ImageViewDslContext context = new ImageViewDslContext(imageView); context.setColorScheme(ColorScheme.Dark); - parser.parseImage(context, null, tokens("image", "https://example.com/image.png")); - assertEquals("https://example.com/image.png", imageView.getContentDark()); + context.getFeatures().enable(Features.HTTPS); + context.getHttpClient().allow(".*"); + + parser.parseImage(context, null, tokens("image", "https://static.structurizr.com/img/structurizr-banner.png")); + assertEquals("https://static.structurizr.com/img/structurizr-banner.png", imageView.getContentDark()); } } \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ImpliedRelationshipsParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ImpliedRelationshipsParserTests.java index eae9a875c..04d17f55a 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ImpliedRelationshipsParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ImpliedRelationshipsParserTests.java @@ -9,12 +9,12 @@ class ImpliedRelationshipsParserTests extends AbstractTests { - private ImpliedRelationshipsParser parser = new ImpliedRelationshipsParser(); + private final ImpliedRelationshipsParser parser = new ImpliedRelationshipsParser(); @Test void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { try { - parser.parse(context(), tokens("!impliedRelationships", "boolean", "extra"), null, false); + parser.parse(context(), tokens("!impliedRelationships", "boolean", "extra"), null); fail(); } catch (Exception e) { assertEquals("Too many tokens, expected: !impliedRelationships ", e.getMessage()); @@ -24,7 +24,7 @@ void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { @Test void test_parse_ThrowsAnException_WhenNoFlagIsSpecified() { try { - parser.parse(context(), tokens("!impliedRelationships"), null, false); + parser.parse(context(), tokens("!impliedRelationships"), null); fail(); } catch (Exception e) { assertEquals("Expected: !impliedRelationships ", e.getMessage()); @@ -33,46 +33,52 @@ void test_parse_ThrowsAnException_WhenNoFlagIsSpecified() { @Test void test_parse_SetsTheStrategy_WhenFalseIsSpecified() { - parser.parse(context(), tokens("!impliedRelationships", "false"), null, false); + parser.parse(context(), tokens("!impliedRelationships", "false"), null); assertEquals("com.structurizr.model.DefaultImpliedRelationshipsStrategy", workspace.getModel().getImpliedRelationshipsStrategy().getClass().getCanonicalName()); } @Test void test_parse_SetsTheStrategy_WhenTrueIsSpecified() { - parser.parse(context(), tokens("!impliedRelationships", "true"), null, false); + parser.parse(context(), tokens("!impliedRelationships", "true"), null); assertEquals("com.structurizr.model.CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy", workspace.getModel().getImpliedRelationshipsStrategy().getClass().getCanonicalName()); } @Test void test_parse_SetsTheStrategy_WhenABuiltInStrategyIsUsedInUnrestrictedMode() { - parser.parse(context(), tokens("!impliedRelationships", "com.structurizr.model.CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy"), new File("."), false); + parser.parse(context(), tokens("!impliedRelationships", "com.structurizr.model.CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy"), new File(".")); assertEquals("com.structurizr.model.CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy", workspace.getModel().getImpliedRelationshipsStrategy().getClass().getCanonicalName()); } @Test - void test_parse_SetsTheStrategy_WhenABuiltInStrategyIsUsedInRestrictedMode() { - parser.parse(context(), tokens("!impliedRelationships", "com.structurizr.model.CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy"), new File("."), true); + void test_parse_SetsTheStrategy_WhenABuiltInStrategyIsUsedAndCustomStrategiesAreNotEnabled() { + DslContext context = context(); + context.getFeatures().disable(Features.PLUGINS); + parser.parse(context, tokens("!impliedRelationships", "com.structurizr.model.CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy"), new File(".")); assertEquals("com.structurizr.model.CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy", workspace.getModel().getImpliedRelationshipsStrategy().getClass().getCanonicalName()); } @Test - void test_parse_ThrowsAnException_WhenACustomStrategyIsUsedInRestrictedMode() { + void test_parse_ThrowsAnException_WhenACustomStrategyIsUsedAndCustomStrategiesAreNotEnabled() { try { - parser.parse(context(), tokens("!impliedRelationships", "com.example.CustomImpliedRelationshipsStrategy"), new File("."), true); + DslContext context = context(); + context.getFeatures().disable(Features.PLUGINS); + parser.parse(context, tokens("!impliedRelationships", "com.example.CustomImpliedRelationshipsStrategy"), new File(".")); fail(); } catch (Exception e) { - assertEquals("The implied relationships strategy com.example.CustomImpliedRelationshipsStrategy is not available when the DSL parser is running in restricted mode", e.getMessage()); + assertEquals("The implied relationships strategy com.example.CustomImpliedRelationshipsStrategy is not available (feature structurizr.feature.dsl.plugins is not enabled)", e.getMessage()); } } @Test - void test_parse_ThrowsAnException_WhenACustomStrategyIsUsedInUnrestrictedModeButCannotBeLoaded() { + void test_parse_ThrowsAnException_WhenACustomStrategyIsUsedButCannotBeLoaded() { try { - parser.parse(context(), tokens("!impliedRelationships", "com.example.CustomImpliedRelationshipsStrategy"), new File("."), false); + DslContext context = context(); + context.getFeatures().enable(Features.PLUGINS); + parser.parse(context, tokens("!impliedRelationships", "com.example.CustomImpliedRelationshipsStrategy"), new File(".")); fail(); } catch (Exception e) { assertEquals("Error loading implied relationships strategy: com.example.CustomImpliedRelationshipsStrategy was not found", e.getMessage()); @@ -80,8 +86,10 @@ void test_parse_ThrowsAnException_WhenACustomStrategyIsUsedInUnrestrictedModeBut } @Test - void test_parse_SetsTheStrategy_WhenACustomStrategyIsUsedInUnrestrictedMode() { - parser.parse(context(), tokens("!impliedRelationships", "com.structurizr.dsl.example.CustomImpliedRelationshipsStrategy"), new File("."), false); + void test_parse_SetsTheStrategy_WhenACustomStrategyIsUsed() { + DslContext context = context(); + context.getFeatures().enable(Features.PLUGINS); + parser.parse(context, tokens("!impliedRelationships", "com.structurizr.dsl.example.CustomImpliedRelationshipsStrategy"), new File(".")); assertEquals("com.structurizr.dsl.example.CustomImpliedRelationshipsStrategy", workspace.getModel().getImpliedRelationshipsStrategy().getClass().getCanonicalName()); } diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/IncludeParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/IncludeParserTests.java index 3a7e7e41c..803a9a545 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/IncludeParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/IncludeParserTests.java @@ -9,10 +9,24 @@ class IncludeParserTests extends AbstractTests { private final IncludeParser parser = new IncludeParser(); + @Test + void test_parse_ThrowsAnException_WhenTheIncludeFeatureIsNotEnabled() { + try { + DslContext context = context(); + context.getFeatures().disable(Features.INCLUDE); + parser.parse(context, null, tokens("!include", "file", "extra")); + fail(); + } catch (Exception e) { + assertEquals("!include is not permitted (feature structurizr.feature.dsl.include is not enabled)", e.getMessage()); + } + } + @Test void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { try { - parser.parse(context(), null, tokens("!include", "file", "extra")); + DslContext context = context(); + context.getFeatures().enable(Features.INCLUDE); + parser.parse(context, null, tokens("!include", "file", "extra")); fail(); } catch (Exception e) { assertEquals("Too many tokens, expected: !include ", e.getMessage()); @@ -22,11 +36,52 @@ void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { @Test void test_parse_ThrowsAnException_WhenAFileIsNotSpecified() { try { - parser.parse(context(), null, tokens("!include")); + DslContext context = context(); + context.getFeatures().enable(Features.INCLUDE); + parser.parse(context, null, tokens("!include")); fail(); } catch (Exception e) { assertEquals("Expected: !include ", e.getMessage()); } } + @Test + void test_parse_ThrowsAnException_WhenTheFileSystemAccessFeatureIsNotEnabled() { + try { + DslContext context = context(); + context.getFeatures().enable(Features.INCLUDE); + context.getFeatures().disable(Features.FILE_SYSTEM); + parser.parse(context, null, tokens("!include", "file")); + fail(); + } catch (Exception e) { + assertEquals("!include is not permitted (feature structurizr.feature.dsl.filesystem is not enabled)", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenIncludingFromHttpsAndHttpsIsNotEnabled() { + try { + DslContext context = context(); + context.getFeatures().enable(Features.INCLUDE); + context.getFeatures().disable(Features.HTTPS); + parser.parse(context, null, tokens("!include", "https://example.com")); + fail(); + } catch (Exception e) { + assertEquals("Includes via HTTPS are not permitted (feature structurizr.feature.dsl.https is not enabled)", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenIncludingFromHttpAndHttpIsNotEnabled() { + try { + DslContext context = context(); + context.getFeatures().enable(Features.INCLUDE); + context.getFeatures().disable(Features.HTTP); + parser.parse(context, null, tokens("!include", "http://example.com")); + fail(); + } catch (Exception e) { + assertEquals("Includes via HTTP are not permitted (feature structurizr.feature.dsl.http is not enabled)", e.getMessage()); + } + } + } \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ThemeParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ThemeParserTests.java index 5a0075aa6..28d3daa2b 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ThemeParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ThemeParserTests.java @@ -13,7 +13,7 @@ class ThemeParserTests extends AbstractTests { @Test void test_parseTheme_ThrowsAnException_WhenThereAreTooManyTokens() { try { - parser.parseTheme(context(), null, tokens("theme", "url", "extra"), false); + parser.parseTheme(context(), null, tokens("theme", "url", "extra")); fail(); } catch (Exception e) { assertEquals("Too many tokens, expected: theme ", e.getMessage()); @@ -23,7 +23,7 @@ void test_parseTheme_ThrowsAnException_WhenThereAreTooManyTokens() { @Test void test_parseTheme_ThrowsAnException_WhenNoThemeIsSpecified() { try { - parser.parseTheme(context(), null, tokens("theme"), false); + parser.parseTheme(context(), null, tokens("theme")); fail(); } catch (Exception e) { assertEquals("Expected: theme ", e.getMessage()); @@ -32,7 +32,7 @@ void test_parseTheme_ThrowsAnException_WhenNoThemeIsSpecified() { @Test void test_parseTheme_AddsTheTheme_WhenAThemeIsSpecified() { - parser.parseTheme(context(), null, tokens("theme", "http://example.com/1"), false); + parser.parseTheme(context(), null, tokens("theme", "http://example.com/1")); assertEquals(1, workspace.getViews().getConfiguration().getThemes().length); assertEquals("http://example.com/1", workspace.getViews().getConfiguration().getThemes()[0]); @@ -40,7 +40,7 @@ void test_parseTheme_AddsTheTheme_WhenAThemeIsSpecified() { @Test void test_parseTheme_AddsTheTheme_WhenTheDefaultThemeIsSpecified() { - parser.parseTheme(context(), null, tokens("theme", "default"), false); + parser.parseTheme(context(), null, tokens("theme", "default")); assertEquals(1, workspace.getViews().getConfiguration().getThemes().length); assertEquals("https://static.structurizr.com/themes/default/theme.json", workspace.getViews().getConfiguration().getThemes()[0]); @@ -49,7 +49,7 @@ void test_parseTheme_AddsTheTheme_WhenTheDefaultThemeIsSpecified() { @Test void test_parseThemes_ThrowsAnException_WhenNoThemesAreSpecified() { try { - parser.parseThemes(context(), null, tokens("themes"), false); + parser.parseThemes(context(), null, tokens("themes")); fail(); } catch (Exception e) { assertEquals("Expected: themes [url|file] ... [url|file]", e.getMessage()); @@ -58,7 +58,7 @@ void test_parseThemes_ThrowsAnException_WhenNoThemesAreSpecified() { @Test void test_parseThemes_AddsTheTheme_WhenOneThemeIsSpecified() { - parser.parseThemes(context(), null, tokens("themes", "http://example.com/1"), false); + parser.parseThemes(context(), null, tokens("themes", "http://example.com/1")); assertEquals(1, workspace.getViews().getConfiguration().getThemes().length); assertEquals("http://example.com/1", workspace.getViews().getConfiguration().getThemes()[0]); @@ -66,7 +66,7 @@ void test_parseThemes_AddsTheTheme_WhenOneThemeIsSpecified() { @Test void test_parseThemes_AddsTheThemes_WhenMultipleThemesAreSpecified() { - parser.parseThemes(context(), null, tokens("themes", "http://example.com/1", "http://example.com/2", "http://example.com/3"), false); + parser.parseThemes(context(), null, tokens("themes", "http://example.com/1", "http://example.com/2", "http://example.com/3")); assertEquals(3, workspace.getViews().getConfiguration().getThemes().length); assertEquals("http://example.com/1", workspace.getViews().getConfiguration().getThemes()[0]); @@ -76,7 +76,7 @@ void test_parseThemes_AddsTheThemes_WhenMultipleThemesAreSpecified() { @Test void test_parseThemes_AddsTheTheme_WhenTheDefaultThemeIsSpecified() { - parser.parseThemes(context(), null, tokens("themes", "default"), false); + parser.parseThemes(context(), null, tokens("themes", "default")); assertEquals(1, workspace.getViews().getConfiguration().getThemes().length); assertEquals("https://static.structurizr.com/themes/default/theme.json", workspace.getViews().getConfiguration().getThemes()[0]); @@ -84,9 +84,13 @@ void test_parseThemes_AddsTheTheme_WhenTheDefaultThemeIsSpecified() { @Test void test_parseTheme_ThrowsAnException_WhenTheThemeFileDoesNotExist() { - File dslFile = new File("src/test/resources/themes/workspace.dsl"); try { - parser.parseTheme(context(), dslFile, tokens("theme", "my-theme.json"), false); + DslContext context = context(); + context.getFeatures().enable(Features.FILE_SYSTEM); + + File dslFile = new File("src/test/resources/themes/workspace.dsl"); + + parser.parseTheme(context, dslFile, tokens("theme", "my-theme.json")); fail(); } catch (Exception e) { assertTrue(e.getMessage().endsWith("/src/test/resources/themes/my-theme.json does not exist")); @@ -95,9 +99,13 @@ void test_parseTheme_ThrowsAnException_WhenTheThemeFileDoesNotExist() { @Test void test_parseTheme_ThrowsAnException_WhenTheThemeFileIsADirectory() { - File dslFile = new File("src/test/resources/workspace.dsl"); try { - parser.parseTheme(context(), dslFile, tokens("theme", "themes"), false); + DslContext context = context(); + context.getFeatures().enable(Features.FILE_SYSTEM); + + File dslFile = new File("src/test/resources/workspace.dsl"); + + parser.parseTheme(context, dslFile, tokens("theme", "themes")); fail(); } catch (Exception e) { assertTrue(e.getMessage().endsWith("/src/test/resources/themes is not a file")); @@ -106,8 +114,12 @@ void test_parseTheme_ThrowsAnException_WhenTheThemeFileIsADirectory() { @Test void test_parseTheme_InlinesTheTheme_WhenAThemeFileIsSpecified() { + DslContext context = context(); + context.getFeatures().enable(Features.FILE_SYSTEM); + File dslFile = new File("src/test/resources/themes/workspace.dsl"); - parser.parseTheme(context(), dslFile, tokens("theme", "theme.json"), false); + + parser.parseTheme(context, dslFile, tokens("theme", "theme.json")); assertEquals(0, workspace.getViews().getConfiguration().getThemes().length); assertEquals("#ff0000", workspace.getViews().getConfiguration().getStyles().getElementStyle("Tag").getBackground()); @@ -115,13 +127,16 @@ void test_parseTheme_InlinesTheTheme_WhenAThemeFileIsSpecified() { } @Test - void test_parseTheme_ThrowsAnException_WhenAThemeFileIsSpecifiedAndTheParserIsRunningInRestrictedMode() { + void test_parseTheme_ThrowsAnException_WhenAThemeFileIsSpecifiedAndFileSystemAccessIsNotEnabled() { try { + DslContext context = context(); + context.getFeatures().disable(Features.FILE_SYSTEM); + File dslFile = new File("src/test/resources/themes/workspace.dsl"); - parser.parseTheme(context(), dslFile, tokens("theme", "theme.json"), true); + parser.parseTheme(context, dslFile, tokens("theme", "theme.json")); fail(); } catch (Exception e) { - assertEquals("File-based themes are not supported when the DSL parser is running in restricted mode", e.getMessage()); + assertEquals("File-based themes are not permitted (feature structurizr.feature.dsl.filesystem is not enabled)", e.getMessage()); } } diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/WorkspaceParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/WorkspaceParserTests.java index 009498f92..3f3ba44b4 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/WorkspaceParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/WorkspaceParserTests.java @@ -7,7 +7,7 @@ class WorkspaceParserTests extends AbstractTests { - private WorkspaceParser parser = new WorkspaceParser(); + private final WorkspaceParser parser = new WorkspaceParser(); @Test void test_parseTitle_ThrowsAnException_WhenThereAreTooManyTokens() { @@ -46,6 +46,32 @@ void test_parse_SetsTheWorkspaceNameAndDescription_WhenANameAndDescriptionAreSpe assertEquals("New Description", workspace.getDescription()); } + @Test + void test_parse_ThrowsAnException_WhenExtendingAHttpsUrlAndHttpsIsNotEnabled() { + try { + DslParserContext context = new DslParserContext(null, null); + context.getFeatures().disable(Features.HTTPS); + + parser.parse(context, tokens("workspace", "extends", "https://example.com")); + fail(); + } catch (Exception e) { + assertEquals("Extends via HTTPS are not permitted (feature structurizr.feature.dsl.https is not enabled)", e.getMessage()); + } + } + + @Test + void test_parse_ThrowsAnException_WhenExtendingAHttpUrlAndHttpIsNotEnabled() { + try { + DslParserContext context = new DslParserContext(null, null); + context.getFeatures().disable(Features.HTTPS); + + parser.parse(context, tokens("workspace", "extends", "http://example.com")); + fail(); + } catch (Exception e) { + assertEquals("Extends via HTTP are not permitted (feature structurizr.feature.dsl.http is not enabled)", e.getMessage()); + } + } + @Test void test_parseName_ThrowsAnException_WhenThereAreTooManyTokens() { try { diff --git a/structurizr-import/build.gradle b/structurizr-import/build.gradle index 7b5c3b32f..53caa555b 100644 --- a/structurizr-import/build.gradle +++ b/structurizr-import/build.gradle @@ -1,6 +1,6 @@ dependencies { - api project(':structurizr-core') + api project(':structurizr-client') } diff --git a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/AbstractDiagramImporter.java b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/AbstractDiagramImporter.java index 04682876a..16c951e31 100644 --- a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/AbstractDiagramImporter.java +++ b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/AbstractDiagramImporter.java @@ -1,5 +1,6 @@ package com.structurizr.importer.diagrams; +import com.structurizr.http.HttpClient; import com.structurizr.view.View; import com.structurizr.view.ViewSet; @@ -21,6 +22,16 @@ public abstract class AbstractDiagramImporter { CONTENT_TYPES_BY_FORMAT.put(SVG_FORMAT, CONTENT_TYPE_IMAGE_SVG); } + protected HttpClient httpClient; + + public AbstractDiagramImporter() { + this.httpClient = new HttpClient(); + } + + public AbstractDiagramImporter(HttpClient httpClient) { + this.httpClient = httpClient; + } + protected String getViewOrViewSetProperty(View view, String name) { ViewSet views = view.getViewSet(); @@ -30,4 +41,4 @@ protected String getViewOrViewSetProperty(View view, String name) { ); } -} +} \ No newline at end of file diff --git a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/image/ImageImporter.java b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/image/ImageImporter.java index ea1584d6c..600f10d21 100644 --- a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/image/ImageImporter.java +++ b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/image/ImageImporter.java @@ -1,19 +1,25 @@ package com.structurizr.importer.diagrams.image; +import com.structurizr.http.HttpClient; +import com.structurizr.http.RemoteContent; import com.structurizr.importer.diagrams.AbstractDiagramImporter; import com.structurizr.util.ImageUtils; import com.structurizr.view.ColorScheme; import com.structurizr.view.ImageView; import java.io.File; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; public class ImageImporter extends AbstractDiagramImporter { public static final String IMAGE_INLINE_PROPERTY = "image.inline"; + public ImageImporter() { + } + + public ImageImporter(HttpClient httpClient) { + super(httpClient); + } + public void importDiagram(ImageView view, File file) throws Exception { importDiagram(view, file, null); } @@ -29,23 +35,27 @@ public void importDiagram(ImageView view, String url) throws Exception { } public void importDiagram(ImageView view, String url, ColorScheme colorScheme) throws Exception { + RemoteContent remoteContent = httpClient.get(url, false); + String inline = getViewOrViewSetProperty(view, IMAGE_INLINE_PROPERTY); if ("true".equals(inline)) { - String imageFormat = ImageUtils.getContentType(url); - if (!imageFormat.equals(CONTENT_TYPE_IMAGE_PNG) && !imageFormat.equals(CONTENT_TYPE_IMAGE_SVG)) { - throw new IllegalArgumentException(String.format("Found %s - expected a format of %s or %s", imageFormat, PNG_FORMAT, SVG_FORMAT)); + + String contentType = remoteContent.getContentType(); + + if (!contentType.equals(CONTENT_TYPE_IMAGE_PNG) && !contentType.equals(CONTENT_TYPE_IMAGE_SVG)) { + throw new IllegalArgumentException(String.format("Found %s - expected a format of %s or %s", contentType, PNG_FORMAT, SVG_FORMAT)); } - if (imageFormat.equals(CONTENT_TYPE_IMAGE_SVG)) { - view.setContent(ImageUtils.getSvgAsDataUri(new URL(url), false), colorScheme); + if (contentType.equals(CONTENT_TYPE_IMAGE_SVG)) { + view.setContent(ImageUtils.getSvgAsDataUri(remoteContent.getContentAsString()), colorScheme); } else { - view.setContent(ImageUtils.getPngAsDataUri(new URL(url), false), colorScheme); + view.setContent(ImageUtils.getPngAsDataUri(remoteContent.getContentAsBytes()), colorScheme); } - view.setContentType(imageFormat); + view.setContentType(contentType); } else { view.setContent(url, colorScheme); - view.setContentType(ImageUtils.getContentType(url)); + view.setContentType(remoteContent.getContentType()); } view.setTitle(url.substring(url.lastIndexOf("/")+1)); diff --git a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/kroki/KrokiImporter.java b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/kroki/KrokiImporter.java index d47ff0c30..fd06aa1c3 100644 --- a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/kroki/KrokiImporter.java +++ b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/kroki/KrokiImporter.java @@ -1,5 +1,7 @@ package com.structurizr.importer.diagrams.kroki; +import com.structurizr.http.HttpClient; +import com.structurizr.http.RemoteContent; import com.structurizr.importer.diagrams.AbstractDiagramImporter; import com.structurizr.util.ImageUtils; import com.structurizr.util.StringUtils; @@ -7,7 +9,6 @@ import com.structurizr.view.ImageView; import java.io.File; -import java.net.URL; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -17,6 +18,13 @@ public class KrokiImporter extends AbstractDiagramImporter { public static final String KROKI_FORMAT_PROPERTY = "kroki.format"; public static final String KROKI_INLINE_PROPERTY = "kroki.inline"; + public KrokiImporter() { + } + + public KrokiImporter(HttpClient httpClient) { + super(httpClient); + } + public void importDiagram(ImageView view, String format, File file) throws Exception { importDiagram(view, format, file, null); } @@ -52,10 +60,12 @@ public void importDiagram(ImageView view, String format, String content, ColorSc String inline = getViewOrViewSetProperty(view, KROKI_INLINE_PROPERTY); if ("true".equals(inline)) { + RemoteContent remoteContent = httpClient.get(url, true); + if (imageFormat.equals(SVG_FORMAT)) { - view.setContent(ImageUtils.getSvgAsDataUri(new URL(url), true), colorScheme); + view.setContent(ImageUtils.getSvgAsDataUri(remoteContent.getContentAsString()), colorScheme); } else { - view.setContent(ImageUtils.getPngAsDataUri(new URL(url), true), colorScheme); + view.setContent(ImageUtils.getPngAsDataUri(remoteContent.getContentAsBytes()), colorScheme); } } else { view.setContent(url, colorScheme); diff --git a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/mermaid/MermaidImporter.java b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/mermaid/MermaidImporter.java index 49711aef9..190b2b2d5 100644 --- a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/mermaid/MermaidImporter.java +++ b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/mermaid/MermaidImporter.java @@ -1,5 +1,7 @@ package com.structurizr.importer.diagrams.mermaid; +import com.structurizr.http.HttpClient; +import com.structurizr.http.RemoteContent; import com.structurizr.importer.diagrams.AbstractDiagramImporter; import com.structurizr.util.ImageUtils; import com.structurizr.util.StringUtils; @@ -7,7 +9,6 @@ import com.structurizr.view.ImageView; import java.io.File; -import java.net.URL; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -18,6 +19,13 @@ public class MermaidImporter extends AbstractDiagramImporter { public static final String MERMAID_COMPRESS_PROPERTY = "mermaid.compress"; public static final String MERMAID_INLINE_PROPERTY = "mermaid.inline"; + public MermaidImporter() { + } + + public MermaidImporter(HttpClient httpClient) { + super(httpClient); + } + public void importDiagram(ImageView view, File file) throws Exception { importDiagram(view, file, null); } @@ -63,10 +71,12 @@ public void importDiagram(ImageView view, String content, ColorScheme colorSchem String inline = getViewOrViewSetProperty(view, MERMAID_INLINE_PROPERTY); if ("true".equals(inline)) { + RemoteContent remoteContent = httpClient.get(url, true); + if (format.equals(SVG_FORMAT)) { - view.setContent(ImageUtils.getSvgAsDataUri(new URL(url), true), colorScheme); + view.setContent(ImageUtils.getSvgAsDataUri(remoteContent.getContentAsString()), colorScheme); } else { - view.setContent(ImageUtils.getPngAsDataUri(new URL(url), true), colorScheme); + view.setContent(ImageUtils.getPngAsDataUri(remoteContent.getContentAsBytes()), colorScheme); } } else { view.setContent(url, colorScheme); diff --git a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporter.java b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporter.java index 33605447a..0e52bcad6 100644 --- a/structurizr-import/src/main/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporter.java +++ b/structurizr-import/src/main/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporter.java @@ -1,14 +1,14 @@ package com.structurizr.importer.diagrams.plantuml; +import com.structurizr.http.HttpClient; +import com.structurizr.http.RemoteContent; import com.structurizr.importer.diagrams.AbstractDiagramImporter; import com.structurizr.util.ImageUtils; import com.structurizr.util.StringUtils; -import com.structurizr.util.Url; import com.structurizr.view.ColorScheme; import com.structurizr.view.ImageView; import java.io.File; -import java.net.URL; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -20,6 +20,13 @@ public class PlantUMLImporter extends AbstractDiagramImporter { private static final String TITLE_STRING = "title "; private static final String NEWLINE = "\n"; + public PlantUMLImporter() { + } + + public PlantUMLImporter(HttpClient httpClient) { + super(httpClient); + } + public void importDiagram(ImageView view, File file) throws Exception { importDiagram(view, file, null); } @@ -55,10 +62,12 @@ public void importDiagram(ImageView view, String content, ColorScheme colorSchem String inline = getViewOrViewSetProperty(view, PLANTUML_INLINE_PROPERTY); if ("true".equals(inline)) { + RemoteContent remoteContent = httpClient.get(url, true); + if (format.equals(SVG_FORMAT)) { - view.setContent(ImageUtils.getSvgAsDataUri(new URL(url), true), colorScheme); + view.setContent(ImageUtils.getSvgAsDataUri(remoteContent.getContentAsString()), colorScheme); } else { - view.setContent(ImageUtils.getPngAsDataUri(new URL(url), true), colorScheme); + view.setContent(ImageUtils.getPngAsDataUri(remoteContent.getContentAsBytes()), colorScheme); } } else { view.setContent(url, colorScheme); diff --git a/structurizr-import/src/test/java/com/structurizr/importer/diagrams/mermaid/MermaidImporterTests.java b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/mermaid/MermaidImporterTests.java index bf8762878..0aebd6a12 100644 --- a/structurizr-import/src/test/java/com/structurizr/importer/diagrams/mermaid/MermaidImporterTests.java +++ b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/mermaid/MermaidImporterTests.java @@ -59,7 +59,7 @@ public void importDiagram_AsInlinePNG() throws Exception { assertNull(view.getElement()); assertNull(view.getElementId()); assertEquals("flowchart.mmd", view.getTitle()); - assertEquals("data:image/png;base64,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", view.getContent()); + assertEquals("data:image/png;base64,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z3XzCTy2cpoDbiKB16ldu/bVsLCwq/a/ly9fHsyNLd933nknLdTy5ctnJYB70K5du7jGjRsnyPXExMSCM2bMsJUsWTLlxRdfjLOvI435mzdvovZ9Bx4fctyt95OAe/bZZxP69OkT43jff/7zn4SPPvqowJgxYwpMmzbtcnh4eK716CIjI5tywF1EwHkvbkwlFy5cOG2m46+//mrx9fX14cbPHWc/8nicDRNP4G7Kli3rOHPxwhdffBHGJUxrxu3r1KlTJoIseXzIbdy4MTAoKMjG3fYbGe8zGo305ptvxrz66qtFV65cmYe79DeOHj3q07t37yLjxo27wuEYvHXr1oCCBQtan3jiicTXX3/9ho/Prf8lV69eNX722Wd5pfwpLSUOtaSOHTvGlipVytK/f/++JpPJzGNwvQngDnibKdqkSZP4V155RbW+r1+/bujUqVMQVxZ8uIyZ9ksEL730UtGmTZvGdejQIT4hIcEwefLkfHv37vWPi4szcms9pWHDhgktW7ZMIPBa3Diy8NCInETgelbrTJw4Md/PP/8cVKBAASuXLhN5X6f2i/b93rvvvhvNJfb8ISEhVu4ZXk5JSaG5c+eGyH6Q93kmGeLh7TCe94dpla/srOPOPH7iyf79+/2qVauWFBgYmGnTWAKsUqVKyQcOHJDaNnFLW603ZcqU/PXr109csWLF+X79+l3j8ZU8GzZsUGehMJvNxCXOQvv27fPnYIzhjeKylJ14vSJcjgrmVfwQcJAd1atXT5KGkv1vrjz4FypUyMbbo6/9Nh5DNl27ds3I47tqp/Hee+8VvHDhgs+QIUOieedy8fHHH785a9asfLw9+hJ4NW64J3JDqWhm9y1cuDCkatWqySNHjrzavHnzuHXr1uX55Zdf1K9dcONd7fek6iX39erVSwXl1KlT8/G+L1hCa86cORelsT927NiC3HlI+5WM7Kzjzjw+5K5cuWLincYdy5BcUrJIC8TxNvmgGjRocJNDT0qPyaGhoZbDhw+rncju3bv9zp075/P2229fk7E8fn6r9ALz5s1r+/777wO5FziGALKhRo0aSdLAspcnedvy523KIr01CTe5LSoqyo+3LWvFihXNf/31l7+EIre6r1epUiVFWuTc84uThtrXX38dQuD1eFu5wkFXJOPtERERSTxskyjlzLZt28bLfk+qAXKf/ZAV2R7l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", view.getContent()); assertEquals("image/png", view.getContentType()); } @@ -95,7 +95,7 @@ public void importDiagram_AsInlineSVG() throws Exception { assertNull(view.getElement()); assertNull(view.getElementId()); assertEquals("flowchart.mmd", view.getTitle()); - assertEquals("data:image/svg+xml;base64,PHN2ZyBpZD0ibWVybWFpZC1zdmciIHdpZHRoPSIxMDAlIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGNsYXNzPSJmbG93Y2hhcnQiIHN0eWxlPSJtYXgtd2lkdGg6IDQ0MXB4OyIgdmlld0JveD0iMCAwIDQ0MSA1MTcuMTU2MjUiIHJvbGU9ImdyYXBoaWNzLWRvY3VtZW50IGRvY3VtZW50IiBhcmlhLXJvbGVkZXNjcmlwdGlvbj0iZmxvd2NoYXJ0LXYyIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+PHN0eWxlIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hodG1sIj5AaW1wb3J0IHVybCgiaHR0cHM6Ly9jZG5qcy5jbG91ZGZsYXJlLmNvbS9hamF4L2xpYnMvZm9udC1hd2Vzb21lLzYuNy4yL2Nzcy9hbGwubWluLmNzcyIpOzwvc3R5bGU+PHN0eWxlPiNtZXJtYWlkLXN2Z3tmb250LWZhbWlseToidHJlYnVjaGV0IG1zIix2ZXJkYW5hLGFyaWFsLHNhbnMtc2VyaWY7Zm9udC1zaXplOjE2cHg7ZmlsbDojMzMzO31Aa2V5ZnJhbWVzIGVkZ2UtYW5pbWF0aW9uLWZyYW1le2Zyb217c3Ryb2tlLWRhc2hvZmZzZXQ6MDt9fUBrZXlmcmFtZXMgZGFzaHt0b3tzdHJva2UtZGFzaG9mZnNldDowO319I21lcm1haWQtc3ZnIC5lZGdlLWFuaW1hdGlvbi1zbG93e3N0cm9rZS1kYXNoYXJyYXk6OSw1IWltcG9ydGFudDtzdHJva2UtZGFzaG9mZnNldDo5MDA7YW5pbWF0aW9uOmRhc2ggNTBzIGxpbmVhciBpbmZpbml0ZTtzdHJva2UtbGluZWNhcDpyb3VuZDt9I21lcm1haWQtc3ZnIC5lZGdlLWFuaW1hdGlvbi1mYXN0e3N0cm9rZS1kYXNoYXJyYXk6OSw1IWltcG9ydGFudDtzdHJva2UtZGFzaG9mZnNldDo5MDA7YW5pbWF0aW9uOmRhc2ggMjBzIGxpbmVhciBpbmZpbml0ZTtzdHJva2UtbGluZWNhcDpyb3VuZDt9I21lcm1haWQtc3ZnIC5lcnJvci1pY29ue2ZpbGw6IzU1MjIyMjt9I21lcm1haWQtc3ZnIC5lcnJvci10ZXh0e2ZpbGw6IzU1MjIyMjtzdHJva2U6IzU1MjIyMjt9I21lcm1haWQtc3ZnIC5lZGdlLXRoaWNrbmVzcy1ub3JtYWx7c3Ryb2tlLXdpZHRoOjFweDt9I21lcm1haWQtc3ZnIC5lZGdlLXRoaWNrbmVzcy10aGlja3tzdHJva2Utd2lkdGg6My41cHg7fSNtZXJtYWlkLXN2ZyAuZWRnZS1wYXR0ZXJuLXNvbGlke3N0cm9rZS1kYXNoYXJyYXk6MDt9I21lcm1haWQtc3ZnIC5lZGdlLXRoaWNrbmVzcy1pbnZpc2libGV7c3Ryb2tlLXdpZHRoOjA7ZmlsbDpub25lO30jbWVybWFpZC1zdmcgLmVkZ2UtcGF0dGVybi1kYXNoZWR7c3Ryb2tlLWRhc2hhcnJheTozO30jbWVybWFpZC1zdmcgLmVkZ2UtcGF0dGVybi1kb3R0ZWR7c3Ryb2tlLWRhc2hhcnJheToyO30jbWVybWFpZC1zdmcgLm1hcmtlcntmaWxsOiMzMzMzMzM7c3Ryb2tlOiMzMzMzMzM7fSNtZXJtYWlkLXN2ZyAubWFya2VyLmNyb3Nze3N0cm9rZTojMzMzMzMzO30jbWVybWFpZC1zdmcgc3Zne2ZvbnQtZmFtaWx5OiJ0cmVidWNoZXQgbXMiLHZlcmRhbmEsYXJpYWwsc2Fucy1zZXJpZjtmb250LXNpemU6MTZweDt9I21lcm1haWQtc3ZnIHB7bWFyZ2luOjA7fSNtZXJtYWlkLXN2ZyAubGFiZWx7Zm9udC1mYW1pbHk6InRyZWJ1Y2hldCBtcyIsdmVyZGFuYSxhcmlhbCxzYW5zLXNlcmlmO2NvbG9yOiMzMzM7fSNtZXJtYWlkLXN2ZyAuY2x1c3Rlci1sYWJlbCB0ZXh0e2ZpbGw6IzMzMzt9I21lcm1haWQtc3ZnIC5jbHVzdGVyLWxhYmVsIHNwYW57Y29sb3I6IzMzMzt9I21lcm1haWQtc3ZnIC5jbHVzdGVyLWxhYmVsIHNwYW4gcHtiYWNrZ3JvdW5kLWNvbG9yOnRyYW5zcGFyZW50O30jbWVybWFpZC1zdmcgLmxhYmVsIHRleHQsI21lcm1haWQtc3ZnIHNwYW57ZmlsbDojMzMzO2NvbG9yOiMzMzM7fSNtZXJtYWlkLXN2ZyAubm9kZSByZWN0LCNtZXJtYWlkLXN2ZyAubm9kZSBjaXJjbGUsI21lcm1haWQtc3ZnIC5ub2RlIGVsbGlwc2UsI21lcm1haWQtc3ZnIC5ub2RlIHBvbHlnb24sI21lcm1haWQtc3ZnIC5ub2RlIHBhdGh7ZmlsbDojRUNFQ0ZGO3N0cm9rZTojOTM3MERCO3N0cm9rZS13aWR0aDoxcHg7fSNtZXJtYWlkLXN2ZyAucm91Z2gtbm9kZSAubGFiZWwgdGV4dCwjbWVybWFpZC1zdmcgLm5vZGUgLmxhYmVsIHRleHQsI21lcm1haWQtc3ZnIC5pbWFnZS1zaGFwZSAubGFiZWwsI21lcm1haWQtc3ZnIC5pY29uLXNoYXBlIC5sYWJlbHt0ZXh0LWFuY2hvcjptaWRkbGU7fSNtZXJtYWlkLXN2ZyAubm9kZSAua2F0ZXggcGF0aHtmaWxsOiMwMDA7c3Ryb2tlOiMwMDA7c3Ryb2tlLXdpZHRoOjFweDt9I21lcm1haWQtc3ZnIC5yb3VnaC1ub2RlIC5sYWJlbCwjbWVybWFpZC1zdmcgLm5vZGUgLmxhYmVsLCNtZXJtYWlkLXN2ZyAuaW1hZ2Utc2hhcGUgLmxhYmVsLCNtZXJtYWlkLXN2ZyAuaWNvbi1zaGFwZSAubGFiZWx7dGV4dC1hbGlnbjpjZW50ZXI7fSNtZXJtYWlkLXN2ZyAubm9kZS5jbGlja2FibGV7Y3Vyc29yOnBvaW50ZXI7fSNtZXJtYWlkLXN2ZyAucm9vdCAuYW5jaG9yIHBhdGh7ZmlsbDojMzMzMzMzIWltcG9ydGFudDtzdHJva2Utd2lkdGg6MDtzdHJva2U6IzMzMzMzMzt9I21lcm1haWQtc3ZnIC5hcnJvd2hlYWRQYXRoe2ZpbGw6IzMzMzMzMzt9I21lcm1haWQtc3ZnIC5lZGdlUGF0aCAucGF0aHtzdHJva2U6IzMzMzMzMztzdHJva2Utd2lkdGg6Mi4wcHg7fSNtZXJtYWlkLXN2ZyAuZmxvd2NoYXJ0LWxpbmt7c3Ryb2tlOiMzMzMzMzM7ZmlsbDpub25lO30jbWVybWFpZC1zdmcgLmVkZ2VMYWJlbHtiYWNrZ3JvdW5kLWNvbG9yOnJnYmEoMjMyLDIzMiwyMzIsIDAuOCk7dGV4dC1hbGlnbjpjZW50ZXI7fSNtZXJtYWlkLXN2ZyAuZWRnZUxhYmVsIHB7YmFja2dyb3VuZC1jb2xvcjpyZ2JhKDIzMiwyMzIsMjMyLCAwLjgpO30jbWVybWFpZC1zdmcgLmVkZ2VMYWJlbCByZWN0e29wYWNpdHk6MC41O2JhY2tncm91bmQtY29sb3I6cmdiYSgyMzIsMjMyLDIzMiwgMC44KTtmaWxsOnJnYmEoMjMyLDIzMiwyMzIsIDAuOCk7fSNtZXJtYWlkLXN2ZyAubGFiZWxCa2d7YmFja2dyb3VuZC1jb2xvcjpyZ2JhKDIzMiwgMjMyLCAyMzIsIDAuNSk7fSNtZXJtYWlkLXN2ZyAuY2x1c3RlciByZWN0e2ZpbGw6I2ZmZmZkZTtzdHJva2U6I2FhYWEzMztzdHJva2Utd2lkdGg6MXB4O30jbWVybWFpZC1zdmcgLmNsdXN0ZXIgdGV4dHtmaWxsOiMzMzM7fSNtZXJtYWlkLXN2ZyAuY2x1c3RlciBzcGFue2NvbG9yOiMzMzM7fSNtZXJtYWlkLXN2ZyBkaXYubWVybWFpZFRvb2x0aXB7cG9zaXRpb246YWJzb2x1dGU7dGV4dC1hbGlnbjpjZW50ZXI7bWF4LXdpZHRoOjIwMHB4O3BhZGRpbmc6MnB4O2ZvbnQtZmFtaWx5OiJ0cmVidWNoZXQgbXMiLHZlcmRhbmEsYXJpYWwsc2Fucy1zZXJpZjtmb250LXNpemU6MTJweDtiYWNrZ3JvdW5kOmhzbCg4MCwgMTAwJSwgOTYuMjc0NTA5ODAzOSUpO2JvcmRlcjoxcHggc29saWQgI2FhYWEzMztib3JkZXItcmFkaXVzOjJweDtwb2ludGVyLWV2ZW50czpub25lO3otaW5kZXg6MTAwO30jbWVybWFpZC1zdmcgLmZsb3djaGFydFRpdGxlVGV4dHt0ZXh0LWFuY2hvcjptaWRkbGU7Zm9udC1zaXplOjE4cHg7ZmlsbDojMzMzO30jbWVybWFpZC1zdmcgcmVjdC50ZXh0e2ZpbGw6bm9uZTtzdHJva2Utd2lkdGg6MDt9I21lcm1haWQtc3ZnIC5pY29uLXNoYXBlLCNtZXJtYWlkLXN2ZyAuaW1hZ2Utc2hhcGV7YmFja2dyb3VuZC1jb2xvcjpyZ2JhKDIzMiwyMzIsMjMyLCAwLjgpO3RleHQtYWxpZ246Y2VudGVyO30jbWVybWFpZC1zdmcgLmljb24tc2hhcGUgcCwjbWVybWFpZC1zdmcgLmltYWdlLXNoYXBlIHB7YmFja2dyb3VuZC1jb2xvcjpyZ2JhKDIzMiwyMzIsMjMyLCAwLjgpO3BhZGRpbmc6MnB4O30jbWVybWFpZC1zdmcgLmljb24tc2hhcGUgcmVjdCwjbWVybWFpZC1zdmcgLmltYWdlLXNoYXBlIHJlY3R7b3BhY2l0eTowLjU7YmFja2dyb3VuZC1jb2xvcjpyZ2JhKDIzMiwyMzIsMjMyLCAwLjgpO2ZpbGw6cmdiYSgyMzIsMjMyLDIzMiwgMC44KTt9I21lcm1haWQtc3ZnIC5sYWJlbC1pY29ue2Rpc3BsYXk6aW5saW5lLWJsb2NrO2hlaWdodDoxZW07b3ZlcmZsb3c6dmlzaWJsZTt2ZXJ0aWNhbC1hbGlnbjotMC4xMjVlbTt9I21lcm1haWQtc3ZnIC5ub2RlIC5sYWJlbC1pY29uIHBhdGh7ZmlsbDpjdXJyZW50Q29sb3I7c3Ryb2tlOnJldmVydDtzdHJva2Utd2lkdGg6cmV2ZXJ0O30jbWVybWFpZC1zdmcgOnJvb3R7LS1tZXJtYWlkLWZvbnQtZmFtaWx5OiJ0cmVidWNoZXQgbXMiLHZlcmRhbmEsYXJpYWwsc2Fucy1zZXJpZjt9PC9zdHlsZT48Zz48bWFya2VyIGlkPSJtZXJtYWlkLXN2Z19mbG93Y2hhcnQtdjItcG9pbnRFbmQiIGNsYXNzPSJtYXJrZXIgZmxvd2NoYXJ0LXYyIiB2aWV3Qm94PSIwIDAgMTAgMTAiIHJlZlg9IjUiIHJlZlk9IjUiIG1hcmtlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgbWFya2VyV2lkdGg9IjgiIG1hcmtlckhlaWdodD0iOCIgb3JpZW50PSJhdXRvIj48cGF0aCBkPSJNIDAgMCBMIDEwIDUgTCAwIDEwIHoiIGNsYXNzPSJhcnJvd01hcmtlclBhdGgiIHN0eWxlPSJzdHJva2Utd2lkdGg6IDE7IHN0cm9rZS1kYXNoYXJyYXk6IDEsIDA7Ii8+PC9tYXJrZXI+PG1hcmtlciBpZD0ibWVybWFpZC1zdmdfZmxvd2NoYXJ0LXYyLXBvaW50U3RhcnQiIGNsYXNzPSJtYXJrZXIgZmxvd2NoYXJ0LXYyIiB2aWV3Qm94PSIwIDAgMTAgMTAiIHJlZlg9IjQuNSIgcmVmWT0iNSIgbWFya2VyVW5pdHM9InVzZXJTcGFjZU9uVXNlIiBtYXJrZXJXaWR0aD0iOCIgbWFya2VySGVpZ2h0PSI4IiBvcmllbnQ9ImF1dG8iPjxwYXRoIGQ9Ik0gMCA1IEwgMTAgMTAgTCAxMCAwIHoiIGNsYXNzPSJhcnJvd01hcmtlclBhdGgiIHN0eWxlPSJzdHJva2Utd2lkdGg6IDE7IHN0cm9rZS1kYXNoYXJyYXk6IDEsIDA7Ii8+PC9tYXJrZXI+PG1hcmtlciBpZD0ibWVybWFpZC1zdmdfZmxvd2NoYXJ0LXYyLWNpcmNsZUVuZCIgY2xhc3M9Im1hcmtlciBmbG93Y2hhcnQtdjIiIHZpZXdCb3g9IjAgMCAxMCAxMCIgcmVmWD0iMTEiIHJlZlk9IjUiIG1hcmtlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgbWFya2VyV2lkdGg9IjExIiBtYXJrZXJIZWlnaHQ9IjExIiBvcmllbnQ9ImF1dG8iPjxjaXJjbGUgY3g9IjUiIGN5PSI1IiByPSI1IiBjbGFzcz0iYXJyb3dNYXJrZXJQYXRoIiBzdHlsZT0ic3Ryb2tlLXdpZHRoOiAxOyBzdHJva2UtZGFzaGFycmF5OiAxLCAwOyIvPjwvbWFya2VyPjxtYXJrZXIgaWQ9Im1lcm1haWQtc3ZnX2Zsb3djaGFydC12Mi1jaXJjbGVTdGFydCIgY2xhc3M9Im1hcmtlciBmbG93Y2hhcnQtdjIiIHZpZXdCb3g9IjAgMCAxMCAxMCIgcmVmWD0iLTEiIHJlZlk9IjUiIG1hcmtlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgbWFya2VyV2lkdGg9IjExIiBtYXJrZXJIZWlnaHQ9IjExIiBvcmllbnQ9ImF1dG8iPjxjaXJjbGUgY3g9IjUiIGN5PSI1IiByPSI1IiBjbGFzcz0iYXJyb3dNYXJrZXJQYXRoIiBzdHlsZT0ic3Ryb2tlLXdpZHRoOiAxOyBzdHJva2UtZGFzaGFycmF5OiAxLCAwOyIvPjwvbWFya2VyPjxtYXJrZXIgaWQ9Im1lcm1haWQtc3ZnX2Zsb3djaGFydC12Mi1jcm9zc0VuZCIgY2xhc3M9Im1hcmtlciBjcm9zcyBmbG93Y2hhcnQtdjIiIHZpZXdCb3g9IjAgMCAxMSAxMSIgcmVmWD0iMTIiIHJlZlk9IjUuMiIgbWFya2VyVW5pdHM9InVzZXJTcGFjZU9uVXNlIiBtYXJrZXJXaWR0aD0iMTEiIG1hcmtlckhlaWdodD0iMTEiIG9yaWVudD0iYXV0byI+PHBhdGggZD0iTSAxLDEgbCA5LDkgTSAxMCwxIGwgLTksOSIgY2xhc3M9ImFycm93TWFya2VyUGF0aCIgc3R5bGU9InN0cm9rZS13aWR0aDogMjsgc3Ryb2tlLWRhc2hhcnJheTogMSwgMDsiLz48L21hcmtlcj48bWFya2VyIGlkPSJtZXJtYWlkLXN2Z19mbG93Y2hhcnQtdjItY3Jvc3NTdGFydCIgY2xhc3M9Im1hcmtlciBjcm9zcyBmbG93Y2hhcnQtdjIiIHZpZXdCb3g9IjAgMCAxMSAxMSIgcmVmWD0iLTEiIHJlZlk9IjUuMiIgbWFya2VyVW5pdHM9InVzZXJTcGFjZU9uVXNlIiBtYXJrZXJXaWR0aD0iMTEiIG1hcmtlckhlaWdodD0iMTEiIG9yaWVudD0iYXV0byI+PHBhdGggZD0iTSAxLDEgbCA5LDkgTSAxMCwxIGwgLTksOSIgY2xhc3M9ImFycm93TWFya2VyUGF0aCIgc3R5bGU9InN0cm9rZS13aWR0aDogMjsgc3Ryb2tlLWRhc2hhcnJheTogMSwgMDsiLz48L21hcmtlcj48ZyBjbGFzcz0icm9vdCI+PGcgY2xhc3M9ImNsdXN0ZXJzIi8+PGcgY2xhc3M9ImVkZ2VQYXRocyI+PHBhdGggZD0iTTIyMS44NTIsNjJMMjIxLjg1Miw2OC4xNjdDMjIxLjg1Miw3NC4zMzMsMjIxLjg1Miw4Ni42NjcsMjIxLjkyNiw5OC40MTdDMjIyLDExMC4xNjcsMjIyLjE0OSwxMjEuMzM0LDIyMi4yMjQsMTI2LjkxN0wyMjIuMjk4LDEzMi41IiBpZD0iTF9BX0JfMCIgY2xhc3M9IiBlZGdlLXRoaWNrbmVzcy1ub3JtYWwgZWRnZS1wYXR0ZXJuLXNvbGlkIGVkZ2UtdGhpY2tuZXNzLW5vcm1hbCBlZGdlLXBhdHRlcm4tc29saWQgZmxvd2NoYXJ0LWxpbmsiIHN0eWxlPSIiIG1hcmtlci1lbmQ9InVybCgjbWVybWFpZC1zdmdfZmxvd2NoYXJ0LXYyLXBvaW50RW5kKSIvPjxwYXRoIGQ9Ik0yMjIuMzUyLDE5MC41TDIyMi4yNjgsMTk0LjU4M0MyMjIuMTg1LDE5OC42NjcsMjIyLjAxOCwyMDYuODMzLDIyMi4wMDUsMjE0LjVDMjIxLjk5MiwyMjIuMTY3LDIyMi4xMzMsMjI5LjMzNCwyMjIuMjAzLDIzMi45MTdMMjIyLjI3MywyMzYuNTAxIiBpZD0iTF9CX0NfMCIgY2xhc3M9IiBlZGdlLXRoaWNrbmVzcy1ub3JtYWwgZWRnZS1wYXR0ZXJuLXNvbGlkIGVkZ2UtdGhpY2tuZXNzLW5vcm1hbCBlZGdlLXBhdHRlcm4tc29saWQgZmxvd2NoYXJ0LWxpbmsiIHN0eWxlPSIiIG1hcmtlci1lbmQ9InVybCgjbWVybWFpZC1zdmdfZmxvd2NoYXJ0LXYyLXBvaW50RW5kKSIvPjxwYXRoIGQ9Ik0xODAuMjE1LDMzOS41MTlMMTYwLjU5LDM1Mi42MjVDMTQwLjk2NiwzNjUuNzMyLDEwMS43MTcsMzkxLjk0NCw4Mi4wOTMsNDEwLjU1QzYyLjQ2OSw0MjkuMTU2LDYyLjQ2OSw0NDAuMTU2LDYyLjQ2OSw0NDUuNjU2TDYyLjQ2OSw0NTEuMTU2IiBpZD0iTF9DX0RfMCIgY2xhc3M9IiBlZGdlLXRoaWNrbmVzcy1ub3JtYWwgZWRnZS1wYXR0ZXJuLXNvbGlkIGVkZ2UtdGhpY2tuZXNzLW5vcm1hbCBlZGdlLXBhdHRlcm4tc29saWQgZmxvd2NoYXJ0LWxpbmsiIHN0eWxlPSIiIG1hcmtlci1lbmQ9InVybCgjbWVybWFpZC1zdmdfZmxvd2NoYXJ0LXYyLXBvaW50RW5kKSIvPjxwYXRoIGQ9Ik0yMjIuMzUyLDM4MS42NTZMMjIyLjI2OCwzODcuNzRDMjIyLjE4NSwzOTMuODIzLDIyMi4wMTgsNDA1Ljk5LDIyMS45MzUsNDE3LjU3M0MyMjEuODUyLDQyOS4xNTYsMjIxLjg1Miw0NDAuMTU2LDIyMS44NTIsNDQ1LjY1NkwyMjEuODUyLDQ1MS4xNTYiIGlkPSJMX0NfRV8wIiBjbGFzcz0iIGVkZ2UtdGhpY2tuZXNzLW5vcm1hbCBlZGdlLXBhdHRlcm4tc29saWQgZWRnZS10aGlja25lc3Mtbm9ybWFsIGVkZ2UtcGF0dGVybi1zb2xpZCBmbG93Y2hhcnQtbGluayIgc3R5bGU9IiIgbWFya2VyLWVuZD0idXJsKCNtZXJtYWlkLXN2Z19mbG93Y2hhcnQtdjItcG9pbnRFbmQpIi8+PHBhdGggZD0iTTI2NC4zNDQsMzM5LjY2NEwyODMuNiwzNTIuNzQ2QzMwMi44NTcsMzY1LjgyOCwzNDEuMzcsMzkxLjk5MiwzNjAuNjI2LDQxMC41NzRDMzc5Ljg4Myw0MjkuMTU2LDM3OS44ODMsNDQwLjE1NiwzNzkuODgzLDQ0NS42NTZMMzc5Ljg4Myw0NTEuMTU2IiBpZD0iTF9DX0ZfMCIgY2xhc3M9IiBlZGdlLXRoaWNrbmVzcy1ub3JtYWwgZWRnZS1wYXR0ZXJuLXNvbGlkIGVkZ2UtdGhpY2tuZXNzLW5vcm1hbCBlZGdlLXBhdHRlcm4tc29saWQgZmxvd2NoYXJ0LWxpbmsiIHN0eWxlPSIiIG1hcmtlci1lbmQ9InVybCgjbWVybWFpZC1zdmdfZmxvd2NoYXJ0LXYyLXBvaW50RW5kKSIvPjwvZz48ZyBjbGFzcz0iZWRnZUxhYmVscyI+PGcgY2xhc3M9ImVkZ2VMYWJlbCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjIxLjg1MTU2MjUsIDk5KSI+PGcgY2xhc3M9ImxhYmVsIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMzkuMTMyODEyNSwgLTEyKSI+PGZvcmVpZ25PYmplY3Qgd2lkdGg9Ijc4LjI2NTYyNSIgaGVpZ2h0PSIyNCI+PGRpdiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94aHRtbCIgY2xhc3M9ImxhYmVsQmtnIiBzdHlsZT0iZGlzcGxheTogdGFibGUtY2VsbDsgd2hpdGUtc3BhY2U6IG5vd3JhcDsgbGluZS1oZWlnaHQ6IDEuNTsgbWF4LXdpZHRoOiAyMDBweDsgdGV4dC1hbGlnbjogY2VudGVyOyI+PHNwYW4gY2xhc3M9ImVkZ2VMYWJlbCAiPjxwPkdldCBtb25leTwvcD48L3NwYW4+PC9kaXY+PC9mb3JlaWduT2JqZWN0PjwvZz48L2c+PGcgY2xhc3M9ImVkZ2VMYWJlbCI+PGcgY2xhc3M9ImxhYmVsIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwLCAwKSI+PGZvcmVpZ25PYmplY3Qgd2lkdGg9IjAiIGhlaWdodD0iMCI+PGRpdiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94aHRtbCIgY2xhc3M9ImxhYmVsQmtnIiBzdHlsZT0iZGlzcGxheTogdGFibGUtY2VsbDsgd2hpdGUtc3BhY2U6IG5vd3JhcDsgbGluZS1oZWlnaHQ6IDEuNTsgbWF4LXdpZHRoOiAyMDBweDsgdGV4dC1hbGlnbjogY2VudGVyOyI+PHNwYW4gY2xhc3M9ImVkZ2VMYWJlbCAiPjwvc3Bhbj48L2Rpdj48L2ZvcmVpZ25PYmplY3Q+PC9nPjwvZz48ZyBjbGFzcz0iZWRnZUxhYmVsIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg2Mi40Njg3NSwgNDE4LjE1NjI1KSI+PGcgY2xhc3M9ImxhYmVsIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMTUuMTI1LCAtMTIpIj48Zm9yZWlnbk9iamVjdCB3aWR0aD0iMzAuMjUiIGhlaWdodD0iMjQiPjxkaXYgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGh0bWwiIGNsYXNzPSJsYWJlbEJrZyIgc3R5bGU9ImRpc3BsYXk6IHRhYmxlLWNlbGw7IHdoaXRlLXNwYWNlOiBub3dyYXA7IGxpbmUtaGVpZ2h0OiAxLjU7IG1heC13aWR0aDogMjAwcHg7IHRleHQtYWxpZ246IGNlbnRlcjsiPjxzcGFuIGNsYXNzPSJlZGdlTGFiZWwgIj48cD5PbmU8L3A+PC9zcGFuPjwvZGl2PjwvZm9yZWlnbk9iamVjdD48L2c+PC9nPjxnIGNsYXNzPSJlZGdlTGFiZWwiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDIyMS44NTE1NjI1LCA0MTguMTU2MjUpIj48ZyBjbGFzcz0ibGFiZWwiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNC42NzE4NzUsIC0xMikiPjxmb3JlaWduT2JqZWN0IHdpZHRoPSIyOS4zNDM3NSIgaGVpZ2h0PSIyNCI+PGRpdiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94aHRtbCIgY2xhc3M9ImxhYmVsQmtnIiBzdHlsZT0iZGlzcGxheTogdGFibGUtY2VsbDsgd2hpdGUtc3BhY2U6IG5vd3JhcDsgbGluZS1oZWlnaHQ6IDEuNTsgbWF4LXdpZHRoOiAyMDBweDsgdGV4dC1hbGlnbjogY2VudGVyOyI+PHNwYW4gY2xhc3M9ImVkZ2VMYWJlbCAiPjxwPlR3bzwvcD48L3NwYW4+PC9kaXY+PC9mb3JlaWduT2JqZWN0PjwvZz48L2c+PGcgY2xhc3M9ImVkZ2VMYWJlbCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMzc5Ljg4MjgxMjUsIDQxOC4xNTYyNSkiPjxnIGNsYXNzPSJsYWJlbCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTIwLjg5ODQzNzUsIC0xMikiPjxmb3JlaWduT2JqZWN0IHdpZHRoPSI0MS43OTY4NzUiIGhlaWdodD0iMjQiPjxkaXYgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGh0bWwiIGNsYXNzPSJsYWJlbEJrZyIgc3R5bGU9ImRpc3BsYXk6IHRhYmxlLWNlbGw7IHdoaXRlLXNwYWNlOiBub3dyYXA7IGxpbmUtaGVpZ2h0OiAxLjU7IG1heC13aWR0aDogMjAwcHg7IHRleHQtYWxpZ246IGNlbnRlcjsiPjxzcGFuIGNsYXNzPSJlZGdlTGFiZWwgIj48cD5UaHJlZTwvcD48L3NwYW4+PC9kaXY+PC9mb3JlaWduT2JqZWN0PjwvZz48L2c+PC9nPjxnIGNsYXNzPSJub2RlcyI+PGcgY2xhc3M9Im5vZGUgZGVmYXVsdCAgIiBpZD0iZmxvd2NoYXJ0LUEtMCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjIxLjg1MTU2MjUsIDM1KSI+PHJlY3QgY2xhc3M9ImJhc2ljIGxhYmVsLWNvbnRhaW5lciIgc3R5bGU9IiIgeD0iLTY2LjAwNzgxMjUiIHk9Ii0yNyIgd2lkdGg9IjEzMi4wMTU2MjUiIGhlaWdodD0iNTQiLz48ZyBjbGFzcz0ibGFiZWwiIHN0eWxlPSIiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0zNi4wMDc4MTI1LCAtMTIpIj48cmVjdC8+PGZvcmVpZ25PYmplY3Qgd2lkdGg9IjcyLjAxNTYyNSIgaGVpZ2h0PSIyNCI+PGRpdiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94aHRtbCIgc3R5bGU9ImRpc3BsYXk6IHRhYmxlLWNlbGw7IHdoaXRlLXNwYWNlOiBub3dyYXA7IGxpbmUtaGVpZ2h0OiAxLjU7IG1heC13aWR0aDogMjAwcHg7IHRleHQtYWxpZ246IGNlbnRlcjsiPjxzcGFuIGNsYXNzPSJub2RlTGFiZWwgIj48cD5DaHJpc3RtYXM8L3A+PC9zcGFuPjwvZGl2PjwvZm9yZWlnbk9iamVjdD48L2c+PC9nPjxnIGNsYXNzPSJub2RlIGRlZmF1bHQgICIgaWQ9ImZsb3djaGFydC1CLTEiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDIyMS44NTE1NjI1LCAxNjMpIj48ZyBjbGFzcz0iYmFzaWMgbGFiZWwtY29udGFpbmVyIG91dGVyLXBhdGgiPjxwYXRoIGQ9Ik0tNTUuMzY3MTg3NSAtMjcgQy0zMC4yMDQ5NTY4MTg5MTIyMzcgLTI3LCAtNS4wNDI3MjYxMzc4MjQ0NzQgLTI3LCA1NS4zNjcxODc1IC0yNyBDNTUuMzY3MTg3NSAtMjcsIDU1LjM2NzE4NzUgLTI3LCA1NS4zNjcxODc1IC0yNyBDNTUuNDgxOTY0MTg0NzQ3NjkgLTI2Ljk5NTI1MjgwMTIwMzY4LCA1NS41OTY3NDA4Njk0OTUzOCAtMjYuOTkwNTA1NjAyNDA3MzY0LCA1NS43ODAwODQyMjczNjE2NiAtMjYuOTgyOTIyNDY1MDMzMzQ3IEM1NS45MzU4OTE1NDUzNjU0NSAtMjYuOTYzNTAxMTAzMDk4MDYsIDU2LjA5MTY5ODg2MzM2OTIzIC0yNi45NDQwNzk3NDExNjI3NzQsIDU2LjE5MDE2MDQ1MTQwMzY3IC0yNi45MzE4MDY1MTcwMTM2MTIgQzU2LjMwNTQ4MjkxMDI0NDczIC0yNi45MDc2MjU5NDAyMDMzNzgsIDU2LjQyMDgwNTM2OTA4NTc4NCAtMjYuODgzNDQ1MzYzMzkzMTQzLCA1Ni41OTQ2MTQ5MzU3MDM5OTQgLTI2Ljg0NzAwMTMyOTY5NjY1MyBDNTYuNzIyNDc4MjM5MzM3MjQgLTI2LjgwODkzNDc3NDkwMDkyNiwgNTYuODUwMzQxNTQyOTcwNSAtMjYuNzcwODY4MjIwMTA1MTk2LCA1Ni45OTA2ODQ4NDYwMjM0MiAtMjYuNzI5MDg2MjA4NTAzMTczIEM1Ny4wNjg1Njk5NzEzMDQ2NzYgLTI2LjY5ODY5NTMzODMxMjEzLCA1Ny4xNDY0NTUwOTY1ODU5MyAtMjYuNjY4MzA0NDY4MTIxMDg2LCA1Ny4zNzU2NjQ2MjMyNjQ4NDYgLTI2LjU3ODg2NjYzMzI3NTI4NiBDNTcuNDk5MzQ0NDA2MTA4MjIgLTI2LjUxODQwMzI3MTc0NDQzLCA1Ny42MjMwMjQxODg5NTE2MDQgLTI2LjQ1NzkzOTkxMDIxMzU3NSwgNTcuNzQ2OTI0NDY1MTg1MzY2IC0yNi4zOTczNjg3NTYwMzI0NDYgQzU3Ljg0MjcxNTMwMjI0MzkyIC0yNi4zNDAyODk4MDY0NzMzMTUsIDU3LjkzODUwNjEzOTMwMjQ3NiAtMjYuMjgzMjEwODU2OTE0MTg0LCA1OC4xMDE5MjgyOTA2MTIxMzYgLTI2LjE4NTgzMjM5MTMxMjY0NCBDNTguMjA3MjQ3MDcwNTc5MiAtMjYuMTEwNjM2MjI3NzQyNDA0LCA1OC4zMTI1NjU4NTA1NDYyNyAtMjYuMDM1NDQwMDY0MTcyMTY0LCA1OC40MzgyNTEwNjM0NDgzNCAtMjUuOTQ1NzAyNTQ2OTgxOTcgQzU4LjU1NDEyNjAwMDEwNzc2NSAtMjUuODQ3NTYxNDgxODM0NTMsIDU4LjY3MDAwMDkzNjc2NzE5IC0yNS43NDk0MjA0MTY2ODcwODQsIDU4Ljc1MzU5NTM1ODEyODcwNiAtMjUuNjc4NjE5NTUzMzY1NjU3IEM1OC44NTUwMzgwMjg2MDczNiAtMjUuNTc3MTc2ODgyODg3MDAyLCA1OC45NTY0ODA2OTkwODYwMTUgLTI1LjQ3NTczNDIxMjQwODM0OCwgNTkuMDQ1ODA3MDUzMzY1NjYgLTI1LjM4NjQwNzg1ODEyODcwNiBDNTkuMTAwMjg1MzM2Njc2NjggLTI1LjMyMjA4NTQ3MDU2ODU2NywgNTkuMTU0NzYzNjE5OTg3NyAtMjUuMjU3NzYzMDgzMDA4NDMsIDU5LjMxMjg5MDA0Njk4MTk3IC0yNS4wNzEwNjM1NjM0NDgzNCBDNTkuMzY1NDk3Nzc5ODYwNTkgLTI0Ljk5NzM4MTg0OTEwODgyNywgNTkuNDE4MTA1NTEyNzM5MjIgLTI0LjkyMzcwMDEzNDc2OTMxLCA1OS41NTMwMTk4OTEzMTI2NDQgLTI0LjczNDc0MDc5MDYxMjEzNiBDNTkuNjM2MTkyMjU2NjkxNzg1IC0yNC41OTUxNTk1NTE2MjA0NDQsIDU5LjcxOTM2NDYyMjA3MDkzNCAtMjQuNDU1NTc4MzEyNjI4NzU2LCA1OS43NjQ1NTYyNTYwMzI0NSAtMjQuMzc5NzM2OTY1MTg1MzcgQzU5LjgyMTI3NTk1MTk2NDQ3IC0yNC4yNjM3MTQ5NzI3MjI0NSwgNTkuODc3OTk1NjQ3ODk2NTA1IC0yNC4xNDc2OTI5ODAyNTk1MzUsIDU5Ljk0NjA1NDEzMzI3NTI5IC0yNC4wMDg0NzcxMjMyNjQ4NDYgQzU5Ljk4NjIxNTc4NTA3MDAyIC0yMy45MDU1NTE2MzA3Nzc0LCA2MC4wMjYzNzc0MzY4NjQ3NiAtMjMuODAyNjI2MTM4Mjg5OTUyLCA2MC4wOTYyNzM3MDg1MDMxNzYgLTIzLjYyMzQ5NzM0NjAyMzQxNyBDNjAuMTMyNzU0ODU4NTU5ODIgLTIzLjUwMDk1OTMyMzAzNjcyNCwgNjAuMTY5MjM2MDA4NjE2NDY1IC0yMy4zNzg0MjEzMDAwNTAwMzIsIDYwLjIxNDE4ODgyOTY5NjY1IC0yMy4yMjc0Mjc0MzU3MDM5OTQgQzYwLjIzMzE0MDkyMjIwMzk3IC0yMy4xMzcwNDA3NjE5MTcwMiwgNjAuMjUyMDkzMDE0NzExMyAtMjMuMDQ2NjU0MDg4MTMwMDQ3LCA2MC4yOTg5OTQwMTcwMTM2MSAtMjIuODIyOTcyOTUxNDAzNjcgQzYwLjMxNjkwMjMyNTk2MjgxIC0yMi42NzkzMDQwNTc0OTkzOSwgNjAuMzM0ODEwNjM0OTEyIC0yMi41MzU2MzUxNjM1OTUxMSwgNjAuMzUwMTA5OTY1MDMzMzUgLTIyLjQxMjg5NjcyNzM2MTY2MiBDNjAuMzU0NDU4NTI3OTA5ODkgLTIyLjMwNzc1ODE3MDU0MDc1OCwgNjAuMzU4ODA3MDkwNzg2NDQgLTIyLjIwMjYxOTYxMzcxOTg1NCwgNjAuMzY3MTg3NSAtMjIgQzYwLjM2NzE4NzUgLTIyLCA2MC4zNjcxODc1IC0yMiwgNjAuMzY3MTg3NSAtMjIgQzYwLjM2NzE4NzUgLTYuNjA5NzgyODc3MTEyOTA4LCA2MC4zNjcxODc1IDguNzgwNDM0MjQ1Nzc0MTg1LCA2MC4zNjcxODc1IDIyIEM2MC4zNjcxODc1IDIyLCA2MC4zNjcxODc1IDIyLCA2MC4zNjcxODc1IDIyIEM2MC4zNjMxMDk0MTQzMTA1NjQgMjIuMDk4NTk5MDIxMzY2Njg1LCA2MC4zNTkwMzEzMjg2MjExMzYgMjIuMTk3MTk4MDQyNzMzMzczLCA2MC4zNTAxMDk5NjUwMzMzNSAyMi40MTI4OTY3MjczNjE2NjIgQzYwLjMzODgxNDQxMzMzMjkxIDIyLjUwMzUxNDk2NjI3MjA3NSwgNjAuMzI3NTE4ODYxNjMyNDg0IDIyLjU5NDEzMzIwNTE4MjQ4NywgNjAuMjk4OTk0MDE3MDEzNjEgMjIuODIyOTcyOTUxNDAzNjcgQzYwLjI2NTY3MzY2NDU5NjI3IDIyLjk4MTg4NDk5ODg4NTk5LCA2MC4yMzIzNTMzMTIxNzg5NCAyMy4xNDA3OTcwNDYzNjgzMSwgNjAuMjE0MTg4ODI5Njk2NjUgMjMuMjI3NDI3NDM1NzAzOTk0IEM2MC4xODQ1MDc0NDQxNTI1NDQgMjMuMzI3MTI1NDUyMzA3OTk2LCA2MC4xNTQ4MjYwNTg2MDg0NCAyMy40MjY4MjM0Njg5MTIsIDYwLjA5NjI3MzcwODUwMzE3NiAyMy42MjM0OTczNDYwMjM0MTcgQzYwLjA0MjY3MTUwOTM2NTI5IDIzLjc2MDg2ODAwOTI4Mzg0LCA1OS45ODkwNjkzMTAyMjc0MSAyMy44OTgyMzg2NzI1NDQyNjcsIDU5Ljk0NjA1NDEzMzI3NTI5IDI0LjAwODQ3NzEyMzI2NDg0NiBDNTkuOTAyMDYyMjEyOTQ3NjU1IDI0LjA5ODQ2NDAzNDU5ODA2LCA1OS44NTgwNzAyOTI2MjAwMiAyNC4xODg0NTA5NDU5MzEyNzYsIDU5Ljc2NDU1NjI1NjAzMjQ1IDI0LjM3OTczNjk2NTE4NTM2NiBDNTkuNjg1NjQ4MDMxNTg5MTUgMjQuNTEyMTYyMDUyMzQyODg3LCA1OS42MDY3Mzk4MDcxNDU4NSAyNC42NDQ1ODcxMzk1MDA0MDUsIDU5LjU1MzAxOTg5MTMxMjY0NCAyNC43MzQ3NDA3OTA2MTIxMzMgQzU5LjQ1OTc1Njc2ODgzODQyNSAyNC44NjUzNjM5MjA5NDk0OCwgNTkuMzY2NDkzNjQ2MzY0MjA1IDI0Ljk5NTk4NzA1MTI4NjgyNSwgNTkuMzEyODkwMDQ2OTgxOTcgMjUuMDcxMDYzNTYzNDQ4MzQgQzU5LjIzNzczNjUyMDE2NTk0IDI1LjE1OTc5NzE2NDg4NjgxLCA1OS4xNjI1ODI5OTMzNDk5MSAyNS4yNDg1MzA3NjYzMjUyODUsIDU5LjA0NTgwNzA1MzM2NTY2IDI1LjM4NjQwNzg1ODEyODcwNiBDNTguOTMzMTA4MjAxNDY3NzkgMjUuNDk5MTA2NzEwMDI2NTc0LCA1OC44MjA0MDkzNDk1Njk5MiAyNS42MTE4MDU1NjE5MjQ0NCwgNTguNzUzNTk1MzU4MTI4NzA2IDI1LjY3ODYxOTU1MzM2NTY1NyBDNTguNjUxOTM0NzA3MDgxMDU0IDI1Ljc2NDcyMTczMjcwNjg0NiwgNTguNTUwMjc0MDU2MDMzNCAyNS44NTA4MjM5MTIwNDgwNCwgNTguNDM4MjUxMDYzNDQ4MzQgMjUuOTQ1NzAyNTQ2OTgxOTcgQzU4LjM2MjY5NzIzMjc3NTEgMjUuOTk5NjQ2OTQ1MjMxMTcsIDU4LjI4NzE0MzQwMjEwMTg1NCAyNi4wNTM1OTEzNDM0ODAzNywgNTguMTAxOTI4MjkwNjEyMTM2IDI2LjE4NTgzMjM5MTMxMjY0NCBDNTguMDAxNzI2ODI4NjczODIgMjYuMjQ1NTM5NTAyODQxODIyLCA1Ny45MDE1MjUzNjY3MzU1IDI2LjMwNTI0NjYxNDM3MSwgNTcuNzQ2OTI0NDY1MTg1MzY2IDI2LjM5NzM2ODc1NjAzMjQ0NiBDNTcuNjU5NzUyNzU1OTA3NzI1IDI2LjQzOTk4NDQwNzkzNzI2LCA1Ny41NzI1ODEwNDY2MzAwOSAyNi40ODI2MDAwNTk4NDIwNzQsIDU3LjM3NTY2NDYyMzI2NDg0NiAyNi41Nzg4NjY2MzMyNzUyODYgQzU3LjI4NzYxMjIwOTIyMDkzIDI2LjYxMzIyNDc5MTg1MjQ2MywgNTcuMTk5NTU5Nzk1MTc3MDIgMjYuNjQ3NTgyOTUwNDI5NjM3LCA1Ni45OTA2ODQ4NDYwMjM0MiAyNi43MjkwODYyMDg1MDMxNzMgQzU2Ljg5MDc2NzQ0NzIzMTc1IDI2Ljc1ODgzMjkwNjk1NDM1LCA1Ni43OTA4NTAwNDg0NDAwODUgMjYuNzg4NTc5NjA1NDA1NTI3LCA1Ni41OTQ2MTQ5MzU3MDM5OTQgMjYuODQ3MDAxMzI5Njk2NjUzIEM1Ni40OTAzMjk4MTI5NjQwNSAyNi44Njg4Njc2MjA0OTQ4MTgsIDU2LjM4NjA0NDY5MDIyNDEgMjYuODkwNzMzOTExMjkyOTg3LCA1Ni4xOTAxNjA0NTE0MDM2NyAyNi45MzE4MDY1MTcwMTM2MTIgQzU2LjAyOTUyMzkyMjY4NTQ3IDI2Ljk1MTgyOTgzOTQzNjY4NywgNTUuODY4ODg3MzkzOTY3MjggMjYuOTcxODUzMTYxODU5NzYyLCA1NS43ODAwODQyMjczNjE2NiAyNi45ODI5MjI0NjUwMzMzNDcgQzU1LjY1MTg4NTcyODY5NTI2IDI2Ljk4ODIyNDc5NDE1OTY1NiwgNTUuNTIzNjg3MjMwMDI4ODYgMjYuOTkzNTI3MTIzMjg1OTcsIDU1LjM2NzE4NzUgMjcgQzU1LjM2NzE4NzUgMjcsIDU1LjM2NzE4NzUgMjcsIDU1LjM2NzE4NzUgMjcgQzI4LjU1MDIzMDcyMDQwMjE2NSAyNywgMS43MzMyNzM5NDA4MDQzMzEgMjcsIC01NS4zNjcxODc1IDI3IEMtNTUuMzY3MTg3NSAyNywgLTU1LjM2NzE4NzUgMjcsIC01NS4zNjcxODc1IDI3IEMtNTUuNDkwNDAxNzQxOTI5Nzk0IDI2Ljk5NDkwMzgyMTI1NzIxOCwgLTU1LjYxMzYxNTk4Mzg1OTU4IDI2Ljk4OTgwNzY0MjUxNDQzLCAtNTUuNzgwMDg0MjI3MzYxNjYgMjYuOTgyOTIyNDY1MDMzMzQ3IEMtNTUuODcwMDc2NjQ2NjAxMTM0IDI2Ljk3MTcwNDkyMTY3MzU0NSwgLTU1Ljk2MDA2OTA2NTg0MDYgMjYuOTYwNDg3Mzc4MzEzNzQ0LCAtNTYuMTkwMTYwNDUxNDAzNjcgMjYuOTMxODA2NTE3MDEzNjEyIEMtNTYuMzA0MDA5NDM2MDQ0NDk1IDI2LjkwNzkzNDg5NTI1MzcxNywgLTU2LjQxNzg1ODQyMDY4NTMyIDI2Ljg4NDA2MzI3MzQ5MzgyMywgLTU2LjU5NDYxNDkzNTcwMzk5NCAyNi44NDcwMDEzMjk2OTY2NTMgQy01Ni42ODIyNTYyMjI4OTgwOCAyNi44MjA5MDkzODgwMTcxOTIsIC01Ni43Njk4OTc1MTAwOTIxNyAyNi43OTQ4MTc0NDYzMzc3MywgLTU2Ljk5MDY4NDg0NjAyMzQyIDI2LjcyOTA4NjIwODUwMzE3MyBDLTU3LjE0MDk1ODA2MzM4MzEyIDI2LjY3MDQ0OTQxNzEzMjg0MywgLTU3LjI5MTIzMTI4MDc0MjgxNCAyNi42MTE4MTI2MjU3NjI1MTQsIC01Ny4zNzU2NjQ2MjMyNjQ4NDYgMjYuNTc4ODY2NjMzMjc1Mjg2IEMtNTcuNDUxOTQ5NzMxMjY5NDUgMjYuNTQxNTczMTE2NDMxNTIyLCAtNTcuNTI4MjM0ODM5Mjc0MDU2IDI2LjUwNDI3OTU5OTU4Nzc1NCwgLTU3Ljc0NjkyNDQ2NTE4NTM2NiAyNi4zOTczNjg3NTYwMzI0NDYgQy01Ny44NDc2ODIwMzkzMTI2NyAyNi4zMzczMzAyNzM1NjQ2NzgsIC01Ny45NDg0Mzk2MTM0Mzk5OCAyNi4yNzcyOTE3OTEwOTY5MDcsIC01OC4xMDE5MjgyOTA2MTIxMzYgMjYuMTg1ODMyMzkxMzEyNjQ0IEMtNTguMjI4NDIzMjg3ODk3MzEgMjYuMDk1NTE2Njk5MTg1ODcsIC01OC4zNTQ5MTgyODUxODI0ODUgMjYuMDA1MjAxMDA3MDU5MDkyLCAtNTguNDM4MjUxMDYzNDQ4MzQgMjUuOTQ1NzAyNTQ2OTgxOTcgQy01OC41NjIxNTI4MDA2NzQ1NDYgMjUuODQwNzYzMTI4NTQwMzYzLCAtNTguNjg2MDU0NTM3OTAwNzUgMjUuNzM1ODIzNzEwMDk4NzU3LCAtNTguNzUzNTk1MzU4MTI4NzA2IDI1LjY3ODYxOTU1MzM2NTY2IEMtNTguODE2MzM1MTU3ODUxMjg1IDI1LjYxNTg3OTc1MzY0MzA4MiwgLTU4Ljg3OTA3NDk1NzU3Mzg1NiAyNS41NTMxMzk5NTM5MjA1MDcsIC01OS4wNDU4MDcwNTMzNjU2NiAyNS4zODY0MDc4NTgxMjg3MDYgQy01OS4xMzc0NDE0MTkyMjEwOCAyNS4yNzgyMTUzNjY4MDY0MjgsIC01OS4yMjkwNzU3ODUwNzY1IDI1LjE3MDAyMjg3NTQ4NDE1LCAtNTkuMzEyODkwMDQ2OTgxOTcgMjUuMDcxMDYzNTYzNDQ4MzQgQy01OS4zNzkwNzc1MDY0MTUxIDI0Ljk3ODM2MjI1ODY3ODkwMywgLTU5LjQ0NTI2NDk2NTg0ODI0IDI0Ljg4NTY2MDk1MzkwOTQ2NSwgLTU5LjU1MzAxOTg5MTMxMjY0NCAyNC43MzQ3NDA3OTA2MTIxMzMgQy01OS42MTg1NzgzNDUyNTk3NiAyNC42MjQ3MTk1MDkwNjUwMTMsIC01OS42ODQxMzY3OTkyMDY4OCAyNC41MTQ2OTgyMjc1MTc4OTQsIC01OS43NjQ1NTYyNTYwMzI0NCAyNC4zNzk3MzY5NjUxODUzNyBDLTU5LjgxMzYxMTQ1NzU2NzY3IDI0LjI3OTM5Mjk0NjU5OTUyLCAtNTkuODYyNjY2NjU5MTAyOSAyNC4xNzkwNDg5MjgwMTM2NjgsIC01OS45NDYwNTQxMzMyNzUyOCAyNC4wMDg0NzcxMjMyNjQ4NSBDLTU5Ljk5MDQ3OTQzNTIyMDk2IDIzLjg5NDYyNDgzMTkwNDExNCwgLTYwLjAzNDkwNDczNzE2NjY1IDIzLjc4MDc3MjU0MDU0MzM4LCAtNjAuMDk2MjczNzA4NTAzMTc2IDIzLjYyMzQ5NzM0NjAyMzQxNyBDLTYwLjEyNDc3MzE2MTY2NzAxNSAyMy41Mjc3NjkzNzAzMDIzNzMsIC02MC4xNTMyNzI2MTQ4MzA4NSAyMy40MzIwNDEzOTQ1ODEzMjYsIC02MC4yMTQxODg4Mjk2OTY2NSAyMy4yMjc0Mjc0MzU3MDM5OTQgQy02MC4yMzY3MDE1MjUyNjU3IDIzLjEyMDA1OTQ2NzI0OTkyNCwgLTYwLjI1OTIxNDIyMDgzNDc1IDIzLjAxMjY5MTQ5ODc5NTg1LCAtNjAuMjk4OTk0MDE3MDEzNjEgMjIuODIyOTcyOTUxNDAzNjcgQy02MC4zMTEyNjI3MjE5NDc4NyAyMi43MjQ1NDc2MTg2NDA5MSwgLTYwLjMyMzUzMTQyNjg4MjEzIDIyLjYyNjEyMjI4NTg3ODE0NywgLTYwLjM1MDEwOTk2NTAzMzM1IDIyLjQxMjg5NjcyNzM2MTY2MiBDLTYwLjM1NjM5NjM0OTE4NDI0IDIyLjI2MDkwNTk3MTc5MjQyMiwgLTYwLjM2MjY4MjczMzMzNTEzNCAyMi4xMDg5MTUyMTYyMjMxNzgsIC02MC4zNjcxODc1IDIyIEMtNjAuMzY3MTg3NSAyMiwgLTYwLjM2NzE4NzUgMjIsIC02MC4zNjcxODc1IDIyIEMtNjAuMzY3MTg3NSA5LjcyMDAzMjY0MjAyNzc2MiwgLTYwLjM2NzE4NzUgLTIuNTU5OTM0NzE1OTQ0NDc1NCwgLTYwLjM2NzE4NzUgLTIyIEMtNjAuMzY3MTg3NSAtMjIsIC02MC4zNjcxODc1IC0yMiwgLTYwLjM2NzE4NzUgLTIyIEMtNjAuMzYyODU3NDk5OTA1MzQgLTIyLjEwNDY4OTc1MDEzMzc5NCwgLTYwLjM1ODUyNzQ5OTgxMDY4NCAtMjIuMjA5Mzc5NTAwMjY3NTg0LCAtNjAuMzUwMTA5OTY1MDMzMzUgLTIyLjQxMjg5NjcyNzM2MTY2IEMtNjAuMzM1ODAwMTMxODUyODYgLTIyLjUyNzY5Njk1MjgyMjgxNSwgLTYwLjMyMTQ5MDI5ODY3MjM2NiAtMjIuNjQyNDk3MTc4MjgzOTc1LCAtNjAuMjk4OTk0MDE3MDEzNjEgLTIyLjgyMjk3Mjk1MTQwMzY3IEMtNjAuMjgxMzQyNDkxMTA3NzM2IC0yMi45MDcxNTY5Mzg2NDgzNzQsIC02MC4yNjM2OTA5NjUyMDE4NSAtMjIuOTkxMzQwOTI1ODkzMDgsIC02MC4yMTQxODg4Mjk2OTY2NSAtMjMuMjI3NDI3NDM1NzAzOTk0IEMtNjAuMTY4MDA3MzI5NjMwMjg1IC0yMy4zODI1NDgzNjAwMTQzNzIsIC02MC4xMjE4MjU4Mjk1NjM5MiAtMjMuNTM3NjY5Mjg0MzI0NzQ2LCAtNjAuMDk2MjczNzA4NTAzMTc2IC0yMy42MjM0OTczNDYwMjM0MTcgQy02MC4wNTQ2NTAzNjUzNTQ0NSAtMjMuNzMwMTY4ODMyNDA2ODIsIC02MC4wMTMwMjcwMjIyMDU3MSAtMjMuODM2ODQwMzE4NzkwMjI3LCAtNTkuOTQ2MDU0MTMzMjc1MjkgLTI0LjAwODQ3NzEyMzI2NDg0NiBDLTU5Ljg3NTQxNjk3NjMwOTc2IC0yNC4xNTI5Njc3MzcyOTkxMzYsIC01OS44MDQ3Nzk4MTkzNDQyMjYgLTI0LjI5NzQ1ODM1MTMzMzQyLCAtNTkuNzY0NTU2MjU2MDMyNDUgLTI0LjM3OTczNjk2NTE4NTM2NiBDLTU5LjcwMDYyMTA5NTI1Nzk5IC0yNC40ODcwMzQwMDkyODU5LCAtNTkuNjM2Njg1OTM0NDgzNTQgLTI0LjU5NDMzMTA1MzM4NjQzLCAtNTkuNTUzMDE5ODkxMzEyNjQ0IC0yNC43MzQ3NDA3OTA2MTIxMzMgQy01OS40OTM3MDc1NjU0MzEwMiAtMjQuODE3ODEyODcxNzE2NzI1LCAtNTkuNDM0Mzk1MjM5NTQ5NCAtMjQuOTAwODg0OTUyODIxMzIsIC01OS4zMTI4OTAwNDY5ODE5NyAtMjUuMDcxMDYzNTYzNDQ4MzQgQy01OS4yMjI0MTUyNTI0OTI2MSAtMjUuMTc3ODg2OTUxNDQ1Nzk2LCAtNTkuMTMxOTQwNDU4MDAzMjYgLTI1LjI4NDcxMDMzOTQ0MzI1NCwgLTU5LjA0NTgwNzA1MzM2NTY2IC0yNS4zODY0MDc4NTgxMjg3MDYgQy01OC45NTQ4NjY5NzI1Njg4NDQgLTI1LjQ3NzM0NzkzODkyNTUyLCAtNTguODYzOTI2ODkxNzcyMDMgLTI1LjU2ODI4ODAxOTcyMjMzLCAtNTguNzUzNTk1MzU4MTI4NzA2IC0yNS42Nzg2MTk1NTMzNjU2NTcgQy01OC42ODU3ODExMzMyMTU0OSAtMjUuNzM2MDU1MjcyMDU1MTI1LCAtNTguNjE3OTY2OTA4MzAyMjggLTI1Ljc5MzQ5MDk5MDc0NDU5OCwgLTU4LjQzODI1MTA2MzQ0ODM0IC0yNS45NDU3MDI1NDY5ODE5NjYgQy01OC4zNjQ4NDkyMDg0NjE2MzUgLTI1Ljk5ODExMDQ2NDEyMzI5MywgLTU4LjI5MTQ0NzM1MzQ3NDkyIC0yNi4wNTA1MTgzODEyNjQ2MiwgLTU4LjEwMTkyODI5MDYxMjEzNiAtMjYuMTg1ODMyMzkxMzEyNjQ0IEMtNTcuOTc1NTMyNDU5MzkwMDIgLTI2LjI2MTE0Nzk1OTAyNjA5NywgLTU3Ljg0OTEzNjYyODE2NzkwNSAtMjYuMzM2NDYzNTI2NzM5NTQ2LCAtNTcuNzQ2OTI0NDY1MTg1MzY2IC0yNi4zOTczNjg3NTYwMzI0NDYgQy01Ny42MDkzMjgwNzM5MTc1NSAtMjYuNDY0NjM1NTMyODU5Njg1LCAtNTcuNDcxNzMxNjgyNjQ5NzQgLTI2LjUzMTkwMjMwOTY4NjkyLCAtNTcuMzc1NjY0NjIzMjY0ODQ2IC0yNi41Nzg4NjY2MzMyNzUyODYgQy01Ny4yNzQzNzA3ODI1MDY2OSAtMjYuNjE4MzkxNjEyNTg5NTg3LCAtNTcuMTczMDc2OTQxNzQ4NTQgLTI2LjY1NzkxNjU5MTkwMzg4NCwgLTU2Ljk5MDY4NDg0NjAyMzQyIC0yNi43MjkwODYyMDg1MDMxNzMgQy01Ni44ODU5NjU3MDA2Njk2MTUgLTI2Ljc2MDI2MjQ0ODg0MzQ1MiwgLTU2Ljc4MTI0NjU1NTMxNTgyIC0yNi43OTE0Mzg2ODkxODM3MywgLTU2LjU5NDYxNDkzNTcwMzk5NCAtMjYuODQ3MDAxMzI5Njk2NjUzIEMtNTYuNDkwMjQ4NTEyNjIwOTIgLTI2Ljg2ODg4NDY2NzM4NDEzNywgLTU2LjM4NTg4MjA4OTUzNzg2IC0yNi44OTA3NjgwMDUwNzE2MTcsIC01Ni4xOTAxNjA0NTE0MDM2NyAtMjYuOTMxODA2NTE3MDEzNjEyIEMtNTYuMDg2MzE4NTkyNjY3NjEgLTI2Ljk0NDc1MDM5MTIwNjMzLCAtNTUuOTgyNDc2NzMzOTMxNTU2IC0yNi45NTc2OTQyNjUzOTkwNTIsIC01NS43ODAwODQyMjczNjE2NiAtMjYuOTgyOTIyNDY1MDMzMzQ3IEMtNTUuNjk1MzIyNTMxNTM1NjIgLTI2Ljk4NjQyODIzNDcwNDk4LCAtNTUuNjEwNTYwODM1NzA5NTg1IC0yNi45ODk5MzQwMDQzNzY2MTgsIC01NS4zNjcxODc1IC0yNyBDLTU1LjM2NzE4NzUgLTI3LCAtNTUuMzY3MTg3NSAtMjcsIC01NS4zNjcxODc1IC0yNyIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjAiIGZpbGw9IiNFQ0VDRkYiIHN0eWxlPSIiLz48cGF0aCBkPSJNLTU1LjM2NzE4NzUgLTI3IEMtMjAuMjQyNzk3ODM4MTEyNzA4IC0yNywgMTQuODgxNTkxODIzNzc0NTg1IC0yNywgNTUuMzY3MTg3NSAtMjcgTS01NS4zNjcxODc1IC0yNyBDLTEyLjEwNTEzNzc2Mzc0NzU2NCAtMjcsIDMxLjE1NjkxMTk3MjUwNDg3MiAtMjcsIDU1LjM2NzE4NzUgLTI3IE01NS4zNjcxODc1IC0yNyBDNTUuMzY3MTg3NSAtMjcsIDU1LjM2NzE4NzUgLTI3LCA1NS4zNjcxODc1IC0yNyBNNTUuMzY3MTg3NSAtMjcgQzU1LjM2NzE4NzUgLTI3LCA1NS4zNjcxODc1IC0yNywgNTUuMzY3MTg3NSAtMjcgTTU1LjM2NzE4NzUgLTI3IEM1NS40Njg1MTUyMzY1NTI1NCAtMjYuOTk1ODA5MDUzODE1NjUsIDU1LjU2OTg0Mjk3MzEwNTA4IC0yNi45OTE2MTgxMDc2MzEyOTcsIDU1Ljc4MDA4NDIyNzM2MTY2IC0yNi45ODI5MjI0NjUwMzMzNDcgTTU1LjM2NzE4NzUgLTI3IEM1NS40ODYyNzY3MzcyNDMzMSAtMjYuOTk1MDc0NDMyNzEzMDY4LCA1NS42MDUzNjU5NzQ0ODY2MiAtMjYuOTkwMTQ4ODY1NDI2MTMyLCA1NS43ODAwODQyMjczNjE2NiAtMjYuOTgyOTIyNDY1MDMzMzQ3IE01NS43ODAwODQyMjczNjE2NiAtMjYuOTgyOTIyNDY1MDMzMzQ3IEM1NS44NjU4ODcxNDgzNDkwNCAtMjYuOTcyMjI3MTQyMDg2MDYyLCA1NS45NTE2OTAwNjkzMzY0MSAtMjYuOTYxNTMxODE5MTM4NzgsIDU2LjE5MDE2MDQ1MTQwMzY3IC0yNi45MzE4MDY1MTcwMTM2MTIgTTU1Ljc4MDA4NDIyNzM2MTY2IC0yNi45ODI5MjI0NjUwMzMzNDcgQzU1LjkxNjIwMzA1NzQ0NDk3NCAtMjYuOTY1OTU1MjcwNTU3OTc3LCA1Ni4wNTIzMjE4ODc1MjgyOSAtMjYuOTQ4OTg4MDc2MDgyNjEsIDU2LjE5MDE2MDQ1MTQwMzY3IC0yNi45MzE4MDY1MTcwMTM2MTIgTTU2LjE5MDE2MDQ1MTQwMzY3IC0yNi45MzE4MDY1MTcwMTM2MTIgQzU2LjMzMTIyODgxNDEwMDc3NiAtMjYuOTAyMjI3NTkxODUwNDUsIDU2LjQ3MjI5NzE3Njc5Nzg4IC0yNi44NzI2NDg2NjY2ODcyODcsIDU2LjU5NDYxNDkzNTcwMzk5NCAtMjYuODQ3MDAxMzI5Njk2NjUzIE01Ni4xOTAxNjA0NTE0MDM2NyAtMjYuOTMxODA2NTE3MDEzNjEyIEM1Ni4yNzkzNjMxMzA3MTE4MiAtMjYuOTEzMTAyNjgyMDQxOTUsIDU2LjM2ODU2NTgxMDAxOTk3IC0yNi44OTQzOTg4NDcwNzAyODUsIDU2LjU5NDYxNDkzNTcwMzk5NCAtMjYuODQ3MDAxMzI5Njk2NjUzIE01Ni41OTQ2MTQ5MzU3MDM5OTQgLTI2Ljg0NzAwMTMyOTY5NjY1MyBDNTYuNzE1NTI0NTQwNjYzNTQgLTI2LjgxMTAwNDk4MDY5MTU5NiwgNTYuODM2NDM0MTQ1NjIzMDcgLTI2Ljc3NTAwODYzMTY4NjUzNiwgNTYuOTkwNjg0ODQ2MDIzNDIgLTI2LjcyOTA4NjIwODUwMzE3MyBNNTYuNTk0NjE0OTM1NzAzOTk0IC0yNi44NDcwMDEzMjk2OTY2NTMgQzU2LjcxNDI3NDc0MDkzMDA4IC0yNi44MTEzNzcwNjIxOTMzNjUsIDU2LjgzMzkzNDU0NjE1NjE2NSAtMjYuNzc1NzUyNzk0NjkwMDc4LCA1Ni45OTA2ODQ4NDYwMjM0MiAtMjYuNzI5MDg2MjA4NTAzMTczIE01Ni45OTA2ODQ4NDYwMjM0MiAtMjYuNzI5MDg2MjA4NTAzMTczIEM1Ny4xMzU2MTAyMTcwNjk4NSAtMjYuNjcyNTM2MTUzMjM5NjU1LCA1Ny4yODA1MzU1ODgxMTYyOSAtMjYuNjE1OTg2MDk3OTc2MTQsIDU3LjM3NTY2NDYyMzI2NDg0NiAtMjYuNTc4ODY2NjMzMjc1Mjg2IE01Ni45OTA2ODQ4NDYwMjM0MiAtMjYuNzI5MDg2MjA4NTAzMTczIEM1Ny4xNDAxNTYyNTQzNzQxNyAtMjYuNjcwNzYyMjgzOTc5LCA1Ny4yODk2Mjc2NjI3MjQ5MSAtMjYuNjEyNDM4MzU5NDU0ODIsIDU3LjM3NTY2NDYyMzI2NDg0NiAtMjYuNTc4ODY2NjMzMjc1Mjg2IE01Ny4zNzU2NjQ2MjMyNjQ4NDYgLTI2LjU3ODg2NjYzMzI3NTI4NiBDNTcuNDc0MzUyNDc4NzY0MTggLTI2LjUzMDYyMTA4MDUzNzU1NywgNTcuNTczMDQwMzM0MjYzNTI0IC0yNi40ODIzNzU1Mjc3OTk4MjUsIDU3Ljc0NjkyNDQ2NTE4NTM2NiAtMjYuMzk3MzY4NzU2MDMyNDQ2IE01Ny4zNzU2NjQ2MjMyNjQ4NDYgLTI2LjU3ODg2NjYzMzI3NTI4NiBDNTcuNDg3Mjg4NjYyNjM2NiAtMjYuNTI0Mjk2OTY1NTk4NzU0LCA1Ny41OTg5MTI3MDIwMDgzNTYgLTI2LjQ2OTcyNzI5NzkyMjIyMywgNTcuNzQ2OTI0NDY1MTg1MzY2IC0yNi4zOTczNjg3NTYwMzI0NDYgTTU3Ljc0NjkyNDQ2NTE4NTM2NiAtMjYuMzk3MzY4NzU2MDMyNDQ2IEM1Ny44MzkwOTM1MDM0MzY3IC0yNi4zNDI0NDc5MzAxMjg3NTYsIDU3LjkzMTI2MjU0MTY4ODAzIC0yNi4yODc1MjcxMDQyMjUwNjcsIDU4LjEwMTkyODI5MDYxMjEzNiAtMjYuMTg1ODMyMzkxMzEyNjQ0IE01Ny43NDY5MjQ0NjUxODUzNjYgLTI2LjM5NzM2ODc1NjAzMjQ0NiBDNTcuODI5OTI3ODU5OTM0MDMgLTI2LjM0NzkwOTQ2ODE5NjgwNCwgNTcuOTEyOTMxMjU0NjgyNzA0IC0yNi4yOTg0NTAxODAzNjExNjUsIDU4LjEwMTkyODI5MDYxMjEzNiAtMjYuMTg1ODMyMzkxMzEyNjQ0IE01OC4xMDE5MjgyOTA2MTIxMzYgLTI2LjE4NTgzMjM5MTMxMjY0NCBDNTguMTczNjUzMzA4ODYyNTUgLTI2LjEzNDYyMTcxMjU4OTIyLCA1OC4yNDUzNzgzMjcxMTI5NiAtMjYuMDgzNDExMDMzODY1Nzk0LCA1OC40MzgyNTEwNjM0NDgzNCAtMjUuOTQ1NzAyNTQ2OTgxOTcgTTU4LjEwMTkyODI5MDYxMjEzNiAtMjYuMTg1ODMyMzkxMzEyNjQ0IEM1OC4xOTE4OTA1Njc1MDcwMSAtMjYuMTIxNjAwNTYwMTkzMzE1LCA1OC4yODE4NTI4NDQ0MDE4OSAtMjYuMDU3MzY4NzI5MDczOTksIDU4LjQzODI1MTA2MzQ0ODM0IC0yNS45NDU3MDI1NDY5ODE5NyBNNTguNDM4MjUxMDYzNDQ4MzQgLTI1Ljk0NTcwMjU0Njk4MTk3IEM1OC41MjYwOTM4MjY2NzgzNyAtMjUuODcxMzAzNTIxNjQxMTU2LCA1OC42MTM5MzY1ODk5MDg0MDUgLTI1Ljc5NjkwNDQ5NjMwMDM0LCA1OC43NTM1OTUzNTgxMjg3MDYgLTI1LjY3ODYxOTU1MzM2NTY1NyBNNTguNDM4MjUxMDYzNDQ4MzQgLTI1Ljk0NTcwMjU0Njk4MTk3IEM1OC41MDk4MTA0MjQ2Mjc2IC0yNS44ODUwOTQ4NTk2NzgxNiwgNTguNTgxMzY5Nzg1ODA2ODY0IC0yNS44MjQ0ODcxNzIzNzQzNDcsIDU4Ljc1MzU5NTM1ODEyODcwNiAtMjUuNjc4NjE5NTUzMzY1NjU3IE01OC43NTM1OTUzNTgxMjg3MDYgLTI1LjY3ODYxOTU1MzM2NTY1NyBDNTguODQzNDgyODQ4MjU2NDcgLTI1LjU4ODczMjA2MzIzNzg5NSwgNTguOTMzMzcwMzM4Mzg0MjMgLTI1LjQ5ODg0NDU3MzExMDEzLCA1OS4wNDU4MDcwNTMzNjU2NiAtMjUuMzg2NDA3ODU4MTI4NzA2IE01OC43NTM1OTUzNTgxMjg3MDYgLTI1LjY3ODYxOTU1MzM2NTY1NyBDNTguODcwMDQxMjA3OTAxOTIgLTI1LjU2MjE3MzcwMzU5MjQ0MywgNTguOTg2NDg3MDU3Njc1MTM0IC0yNS40NDU3Mjc4NTM4MTkyMzIsIDU5LjA0NTgwNzA1MzM2NTY2IC0yNS4zODY0MDc4NTgxMjg3MDYgTTU5LjA0NTgwNzA1MzM2NTY2IC0yNS4zODY0MDc4NTgxMjg3MDYgQzU5LjEzMjAyMzI2ODgzMjY3NSAtMjUuMjg0NjEyNTY0ODgxNzEyLCA1OS4yMTgyMzk0ODQyOTk2OTQgLTI1LjE4MjgxNzI3MTYzNDcyLCA1OS4zMTI4OTAwNDY5ODE5NyAtMjUuMDcxMDYzNTYzNDQ4MzQgTTU5LjA0NTgwNzA1MzM2NTY2IC0yNS4zODY0MDc4NTgxMjg3MDYgQzU5LjExMTE5MDI0MTMxNjEyIC0yNS4zMDkyMTAwNzM5OTA0NDIsIDU5LjE3NjU3MzQyOTI2NjU5IC0yNS4yMzIwMTIyODk4NTIxNzUsIDU5LjMxMjg5MDA0Njk4MTk3IC0yNS4wNzEwNjM1NjM0NDgzNCBNNTkuMzEyODkwMDQ2OTgxOTcgLTI1LjA3MTA2MzU2MzQ0ODM0IEM1OS4zNjY3MjA5MTA1NTE5OSAtMjQuOTk1NjY4NzQ3OTg3MTQzLCA1OS40MjA1NTE3NzQxMjIwMSAtMjQuOTIwMjczOTMyNTI1OTQ0LCA1OS41NTMwMTk4OTEzMTI2NDQgLTI0LjczNDc0MDc5MDYxMjEzNiBNNTkuMzEyODkwMDQ2OTgxOTcgLTI1LjA3MTA2MzU2MzQ0ODM0IEM1OS4zOTA3NTQwMDY5MjY1NiAtMjQuOTYyMDA4MzAyMTc2ODI3LCA1OS40Njg2MTc5NjY4NzExMzYgLTI0Ljg1Mjk1MzA0MDkwNTMxMiwgNTkuNTUzMDE5ODkxMzEyNjQ0IC0yNC43MzQ3NDA3OTA2MTIxMzYgTTU5LjU1MzAxOTg5MTMxMjY0NCAtMjQuNzM0NzQwNzkwNjEyMTM2IEM1OS42MjMxMzIzMzUwOTU3MSAtMjQuNjE3MDc2OTI4MDEzNDkzLCA1OS42OTMyNDQ3Nzg4Nzg3OCAtMjQuNDk5NDEzMDY1NDE0ODUzLCA1OS43NjQ1NTYyNTYwMzI0NSAtMjQuMzc5NzM2OTY1MTg1MzcgTTU5LjU1MzAxOTg5MTMxMjY0NCAtMjQuNzM0NzQwNzkwNjEyMTM2IEM1OS42MDA5NjkxMzYzMTM3NjUgLTI0LjY1NDI3MTU3NDcxOTc3LCA1OS42NDg5MTgzODEzMTQ4OSAtMjQuNTczODAyMzU4ODI3NDA0LCA1OS43NjQ1NTYyNTYwMzI0NSAtMjQuMzc5NzM2OTY1MTg1MzcgTTU5Ljc2NDU1NjI1NjAzMjQ1IC0yNC4zNzk3MzY5NjUxODUzNyBDNTkuODMzNzk2NTI5MzcwNTQgLTI0LjIzODEwMzcyMjI4MTcxLCA1OS45MDMwMzY4MDI3MDg2MzQgLTI0LjA5NjQ3MDQ3OTM3ODA1NiwgNTkuOTQ2MDU0MTMzMjc1MjkgLTI0LjAwODQ3NzEyMzI2NDg0NiBNNTkuNzY0NTU2MjU2MDMyNDUgLTI0LjM3OTczNjk2NTE4NTM3IEM1OS44MTI4NTE0MTA5OTIxNiAtMjQuMjgwOTQ3NjQ2NzE5MjQ1LCA1OS44NjExNDY1NjU5NTE4NyAtMjQuMTgyMTU4MzI4MjUzMTIsIDU5Ljk0NjA1NDEzMzI3NTI5IC0yNC4wMDg0NzcxMjMyNjQ4NDYgTTU5Ljk0NjA1NDEzMzI3NTI5IC0yNC4wMDg0NzcxMjMyNjQ4NDYgQzU5Ljk5Nzk5OTg5MjYzNjU2IC0yMy44NzUzNTE1NTEzMzMyMjUsIDYwLjA0OTk0NTY1MTk5NzgyNCAtMjMuNzQyMjI1OTc5NDAxNiwgNjAuMDk2MjczNzA4NTAzMTc2IC0yMy42MjM0OTczNDYwMjM0MTcgTTU5Ljk0NjA1NDEzMzI3NTI5IC0yNC4wMDg0NzcxMjMyNjQ4NDYgQzU5Ljk5MjQ0NTA2MDg3MDQ3IC0yMy44ODk1ODczNjUwOTIzNTMsIDYwLjAzODgzNTk4ODQ2NTY2NCAtMjMuNzcwNjk3NjA2OTE5ODYsIDYwLjA5NjI3MzcwODUwMzE3NiAtMjMuNjIzNDk3MzQ2MDIzNDE3IE02MC4wOTYyNzM3MDg1MDMxNzYgLTIzLjYyMzQ5NzM0NjAyMzQxNyBDNjAuMTIzNjI3MTk1OTcxNDE2IC0yMy41MzE2MTg2MDEyMjEwOTgsIDYwLjE1MDk4MDY4MzQzOTY2IC0yMy40Mzk3Mzk4NTY0MTg3OCwgNjAuMjE0MTg4ODI5Njk2NjUgLTIzLjIyNzQyNzQzNTcwMzk5NCBNNjAuMDk2MjczNzA4NTAzMTc2IC0yMy42MjM0OTczNDYwMjM0MTcgQzYwLjEzNjgzMTc1MjQ0MDk3NiAtMjMuNDg3MjY1Mjc3ODgzMzI4LCA2MC4xNzczODk3OTYzNzg3OCAtMjMuMzUxMDMzMjA5NzQzMjQsIDYwLjIxNDE4ODgyOTY5NjY1IC0yMy4yMjc0Mjc0MzU3MDM5OTQgTTYwLjIxNDE4ODgyOTY5NjY1IC0yMy4yMjc0Mjc0MzU3MDM5OTQgQzYwLjIzNjE2MTUzMjk3Nzk0NSAtMjMuMTIyNjM0ODA4NTYwNzQ3LCA2MC4yNTgxMzQyMzYyNTkyNCAtMjMuMDE3ODQyMTgxNDE3NSwgNjAuMjk4OTk0MDE3MDEzNjEgLTIyLjgyMjk3Mjk1MTQwMzY3IE02MC4yMTQxODg4Mjk2OTY2NSAtMjMuMjI3NDI3NDM1NzAzOTk0IEM2MC4yMzE0OTkzMTQ3MTY3OCAtMjMuMTQ0ODY5OTQ3MDI1NDQsIDYwLjI0ODgwOTc5OTczNjkgLTIzLjA2MjMxMjQ1ODM0Njg4OCwgNjAuMjk4OTk0MDE3MDEzNjEgLTIyLjgyMjk3Mjk1MTQwMzY3IE02MC4yOTg5OTQwMTcwMTM2MSAtMjIuODIyOTcyOTUxNDAzNjcgQzYwLjMxNjY3MzgwODg0NDE4IC0yMi42ODExMzczMjk1MTY1NzcsIDYwLjMzNDM1MzYwMDY3NDc0NiAtMjIuNTM5MzAxNzA3NjI5NDgsIDYwLjM1MDEwOTk2NTAzMzM1IC0yMi40MTI4OTY3MjczNjE2NjIgTTYwLjI5ODk5NDAxNzAxMzYxIC0yMi44MjI5NzI5NTE0MDM2NyBDNjAuMzEwMzg3NTU5MjA5NiAtMjIuNzMxNTY4NTg2NTU4NjgsIDYwLjMyMTc4MTEwMTQwNTU4NCAtMjIuNjQwMTY0MjIxNzEzNjkzLCA2MC4zNTAxMDk5NjUwMzMzNSAtMjIuNDEyODk2NzI3MzYxNjYyIE02MC4zNTAxMDk5NjUwMzMzNSAtMjIuNDEyODk2NzI3MzYxNjYyIEM2MC4zNTY3MzA5NDkwMDU5IC0yMi4yNTI4MTYwOTM5NzI5MDcsIDYwLjM2MzM1MTkzMjk3ODQ0NiAtMjIuMDkyNzM1NDYwNTg0MTQ4LCA2MC4zNjcxODc1IC0yMiBNNjAuMzUwMTA5OTY1MDMzMzUgLTIyLjQxMjg5NjcyNzM2MTY2MiBDNjAuMzUzOTgyMzQwMjUyODcgLTIyLjMxOTI3MTMyNjYwMDk0LCA2MC4zNTc4NTQ3MTU0NzI0IC0yMi4yMjU2NDU5MjU4NDAyMiwgNjAuMzY3MTg3NSAtMjIgTTYwLjM2NzE4NzUgLTIyIEM2MC4zNjcxODc1IC0yMiwgNjAuMzY3MTg3NSAtMjIsIDYwLjM2NzE4NzUgLTIyIE02MC4zNjcxODc1IC0yMiBDNjAuMzY3MTg3NSAtMjIsIDYwLjM2NzE4NzUgLTIyLCA2MC4zNjcxODc1IC0yMiBNNjAuMzY3MTg3NSAtMjIgQzYwLjM2NzE4NzUgLTEwLjM4OTE3MTUxNDIyNDUzMiwgNjAuMzY3MTg3NSAxLjIyMTY1Njk3MTU1MDkzNTEsIDYwLjM2NzE4NzUgMjIgTTYwLjM2NzE4NzUgLTIyIEM2MC4zNjcxODc1IC0xMC45NTU2NjIwMjIwMzk5OTcsIDYwLjM2NzE4NzUgMC4wODg2NzU5NTU5MjAwMDY1NCwgNjAuMzY3MTg3NSAyMiBNNjAuMzY3MTg3NSAyMiBDNjAuMzY3MTg3NSAyMiwgNjAuMzY3MTg3NSAyMiwgNjAuMzY3MTg3NSAyMiBNNjAuMzY3MTg3NSAyMiBDNjAuMzY3MTg3NSAyMiwgNjAuMzY3MTg3NSAyMiwgNjAuMzY3MTg3NSAyMiBNNjAuMzY3MTg3NSAyMiBDNjAuMzYyOTg3NDI5NzQ2ODEgMjIuMTAxNTQ4MzM2MTk4MjEzLCA2MC4zNTg3ODczNTk0OTM2MjYgMjIuMjAzMDk2NjcyMzk2NDMsIDYwLjM1MDEwOTk2NTAzMzM1IDIyLjQxMjg5NjcyNzM2MTY2MiBNNjAuMzY3MTg3NSAyMiBDNjAuMzYzMTk3NDcwMDg2NTA0IDIyLjA5NjQ3MDAyODk5Mjc1LCA2MC4zNTkyMDc0NDAxNzMwMSAyMi4xOTI5NDAwNTc5ODU1MDYsIDYwLjM1MDEwOTk2NTAzMzM1IDIyLjQxMjg5NjcyNzM2MTY2MiBNNjAuMzUwMTA5OTY1MDMzMzUgMjIuNDEyODk2NzI3MzYxNjYyIEM2MC4zMzAwMTYwODE0MTExOSAyMi41NzQwOTkzMzEyNzE3NCwgNjAuMzA5OTIyMTk3Nzg5MDM1IDIyLjczNTMwMTkzNTE4MTgxMywgNjAuMjk4OTk0MDE3MDEzNjEgMjIuODIyOTcyOTUxNDAzNjcgTTYwLjM1MDEwOTk2NTAzMzM1IDIyLjQxMjg5NjcyNzM2MTY2MiBDNjAuMzMyNzE3NDc5NDU0ODIgMjIuNTUyNDI3NDQzMDk4MjUsIDYwLjMxNTMyNDk5Mzg3NjI5IDIyLjY5MTk1ODE1ODgzNDg0LCA2MC4yOTg5OTQwMTcwMTM2MSAyMi44MjI5NzI5NTE0MDM2NyBNNjAuMjk4OTk0MDE3MDEzNjEgMjIuODIyOTcyOTUxNDAzNjcgQzYwLjI3NjIzMTM1Mzg3MjA2IDIyLjkzMTUzMzA2OTg3NjI0NSwgNjAuMjUzNDY4NjkwNzMwNTEgMjMuMDQwMDkzMTg4MzQ4ODE2LCA2MC4yMTQxODg4Mjk2OTY2NSAyMy4yMjc0Mjc0MzU3MDM5OTQgTTYwLjI5ODk5NDAxNzAxMzYxIDIyLjgyMjk3Mjk1MTQwMzY3IEM2MC4yNzcyOTI1Nzc2OTc2IDIyLjkyNjQ3MTg2MTM3Mzk3NiwgNjAuMjU1NTkxMTM4MzgxNTggMjMuMDI5OTcwNzcxMzQ0MjgsIDYwLjIxNDE4ODgyOTY5NjY1IDIzLjIyNzQyNzQzNTcwMzk5NCBNNjAuMjE0MTg4ODI5Njk2NjUgMjMuMjI3NDI3NDM1NzAzOTk0IEM2MC4xNzg5NzE4ODQzMjMyMiAyMy4zNDU3MTkwNzAwMTM0OTQsIDYwLjE0Mzc1NDkzODk0OTc5IDIzLjQ2NDAxMDcwNDMyMjk5LCA2MC4wOTYyNzM3MDg1MDMxNzYgMjMuNjIzNDk3MzQ2MDIzNDE3IE02MC4yMTQxODg4Mjk2OTY2NSAyMy4yMjc0Mjc0MzU3MDM5OTQgQzYwLjE3NzA4NTY1MzM3NzczIDIzLjM1MjA1NDgwODA3NTQyLCA2MC4xMzk5ODI0NzcwNTg4MSAyMy40NzY2ODIxODA0NDY4NDcsIDYwLjA5NjI3MzcwODUwMzE3NiAyMy42MjM0OTczNDYwMjM0MTcgTTYwLjA5NjI3MzcwODUwMzE3NiAyMy42MjM0OTczNDYwMjM0MTcgQzYwLjA2MzM2NzU4NjA0NTQ2NiAyMy43MDc4Mjg1MTA0MTY4MSwgNjAuMDMwNDYxNDYzNTg3NzU2IDIzLjc5MjE1OTY3NDgxMDIsIDU5Ljk0NjA1NDEzMzI3NTI5IDI0LjAwODQ3NzEyMzI2NDg0NiBNNjAuMDk2MjczNzA4NTAzMTc2IDIzLjYyMzQ5NzM0NjAyMzQxNyBDNjAuMDQyMTU5NDc4NjU3ODIgMjMuNzYyMTgwMjMxNTI1MiwgNTkuOTg4MDQ1MjQ4ODEyNDcgMjMuOTAwODYzMTE3MDI2OTgzLCA1OS45NDYwNTQxMzMyNzUyOSAyNC4wMDg0NzcxMjMyNjQ4NDYgTTU5Ljk0NjA1NDEzMzI3NTI5IDI0LjAwODQ3NzEyMzI2NDg0NiBDNTkuODg5MTA1NTc1NDIyNjEgMjQuMTI0OTY3MjYwMjY5MDM1LCA1OS44MzIxNTcwMTc1Njk5MjYgMjQuMjQxNDU3Mzk3MjczMjIzLCA1OS43NjQ1NTYyNTYwMzI0NSAyNC4zNzk3MzY5NjUxODUzNjYgTTU5Ljk0NjA1NDEzMzI3NTI5IDI0LjAwODQ3NzEyMzI2NDg0NiBDNTkuOTAwNDc0Njg2NTEzNzYgMjQuMTAxNzExMzcxODIyMywgNTkuODU0ODk1MjM5NzUyMjQgMjQuMTk0OTQ1NjIwMzc5NzU1LCA1OS43NjQ1NTYyNTYwMzI0NSAyNC4zNzk3MzY5NjUxODUzNjYgTTU5Ljc2NDU1NjI1NjAzMjQ1IDI0LjM3OTczNjk2NTE4NTM2NiBDNTkuNzEzNjQxNjIyNTI1NTggMjQuNDY1MTgyNzQ1MTA2MzgyLCA1OS42NjI3MjY5ODkwMTg3MSAyNC41NTA2Mjg1MjUwMjc0LCA1OS41NTMwMTk4OTEzMTI2NDQgMjQuNzM0NzQwNzkwNjEyMTMzIE01OS43NjQ1NTYyNTYwMzI0NSAyNC4zNzk3MzY5NjUxODUzNjYgQzU5LjY4OTUxMzkzNzYxMDk1NCAyNC41MDU2NzQyMjQ5MjcwMywgNTkuNjE0NDcxNjE5MTg5NDUgMjQuNjMxNjExNDg0NjY4NjkyLCA1OS41NTMwMTk4OTEzMTI2NDQgMjQuNzM0NzQwNzkwNjEyMTMzIE01OS41NTMwMTk4OTEzMTI2NDQgMjQuNzM0NzQwNzkwNjEyMTMzIEM1OS40NzAwNzQ1ODc5ODEzOCAyNC44NTA5MTI5MTYxMDQ3NzUsIDU5LjM4NzEyOTI4NDY1MDExIDI0Ljk2NzA4NTA0MTU5NzQyLCA1OS4zMTI4OTAwNDY5ODE5NyAyNS4wNzEwNjM1NjM0NDgzNCBNNTkuNTUzMDE5ODkxMzEyNjQ0IDI0LjczNDc0MDc5MDYxMjEzMyBDNTkuNDg3MTkzNjE4OTYzNTY2IDI0LjgyNjkzNjIyMTM5NTA5OCwgNTkuNDIxMzY3MzQ2NjE0NDkgMjQuOTE5MTMxNjUyMTc4MDY2LCA1OS4zMTI4OTAwNDY5ODE5NyAyNS4wNzEwNjM1NjM0NDgzNCBNNTkuMzEyODkwMDQ2OTgxOTcgMjUuMDcxMDYzNTYzNDQ4MzQgQzU5LjI1NzY2OTMyNDY0ODMyNiAyNS4xMzYyNjI1NDcxMDYwNTMsIDU5LjIwMjQ0ODYwMjMxNDY4IDI1LjIwMTQ2MTUzMDc2Mzc2NSwgNTkuMDQ1ODA3MDUzMzY1NjYgMjUuMzg2NDA3ODU4MTI4NzA2IE01OS4zMTI4OTAwNDY5ODE5NyAyNS4wNzEwNjM1NjM0NDgzNCBDNTkuMjExMjI3MzQzNTcyODIgMjUuMTkxMDk2NDkwNTQ2MzIsIDU5LjEwOTU2NDY0MDE2MzY3NiAyNS4zMTExMjk0MTc2NDQzLCA1OS4wNDU4MDcwNTMzNjU2NiAyNS4zODY0MDc4NTgxMjg3MDYgTTU5LjA0NTgwNzA1MzM2NTY2IDI1LjM4NjQwNzg1ODEyODcwNiBDNTguOTM4NzA5MzQ0NzMyODMgMjUuNDkzNTA1NTY2NzYxNTM0LCA1OC44MzE2MTE2MzYxIDI1LjYwMDYwMzI3NTM5NDM2MywgNTguNzUzNTk1MzU4MTI4NzA2IDI1LjY3ODYxOTU1MzM2NTY1NyBNNTkuMDQ1ODA3MDUzMzY1NjYgMjUuMzg2NDA3ODU4MTI4NzA2IEM1OC45MzYzMjM5ODExODM2OSAyNS40OTU4OTA5MzAzMTA2NzIsIDU4LjgyNjg0MDkwOTAwMTcyNCAyNS42MDUzNzQwMDI0OTI2NCwgNTguNzUzNTk1MzU4MTI4NzA2IDI1LjY3ODYxOTU1MzM2NTY1NyBNNTguNzUzNTk1MzU4MTI4NzA2IDI1LjY3ODYxOTU1MzM2NTY1NyBDNTguNjc1NDMwMTIwNzQzMzIgMjUuNzQ0ODIyMTMyNDE5MDYzLCA1OC41OTcyNjQ4ODMzNTc5NCAyNS44MTEwMjQ3MTE0NzI0NzQsIDU4LjQzODI1MTA2MzQ0ODM0IDI1Ljk0NTcwMjU0Njk4MTk3IE01OC43NTM1OTUzNTgxMjg3MDYgMjUuNjc4NjE5NTUzMzY1NjU3IEM1OC42Njk3NTAwNDg2MjYxOCAyNS43NDk2MzI5MDgxMDMyOCwgNTguNTg1OTA0NzM5MTIzNjQgMjUuODIwNjQ2MjYyODQwOTAzLCA1OC40MzgyNTEwNjM0NDgzNCAyNS45NDU3MDI1NDY5ODE5NyBNNTguNDM4MjUxMDYzNDQ4MzQgMjUuOTQ1NzAyNTQ2OTgxOTcgQzU4LjM1ODc4NjE5NjMyNjY3IDI2LjAwMjQzOTM3MTU4MjgzNSwgNTguMjc5MzIxMzI5MjA1IDI2LjA1OTE3NjE5NjE4MzcwMywgNTguMTAxOTI4MjkwNjEyMTM2IDI2LjE4NTgzMjM5MTMxMjY0NCBNNTguNDM4MjUxMDYzNDQ4MzQgMjUuOTQ1NzAyNTQ2OTgxOTcgQzU4LjM2MDgyODk3NTc0MzgzNiAyNi4wMDA5ODA4NTUxMTI3NSwgNTguMjgzNDA2ODg4MDM5MzMgMjYuMDU2MjU5MTYzMjQzNTM1LCA1OC4xMDE5MjgyOTA2MTIxMzYgMjYuMTg1ODMyMzkxMzEyNjQ0IE01OC4xMDE5MjgyOTA2MTIxMzYgMjYuMTg1ODMyMzkxMzEyNjQ0IEM1Ny45NzM1NTYxMjY4OTg1NSAyNi4yNjIzMjU1OTc1Nzc1MSwgNTcuODQ1MTgzOTYzMTg0OTY1IDI2LjMzODgxODgwMzg0MjM4MywgNTcuNzQ2OTI0NDY1MTg1MzY2IDI2LjM5NzM2ODc1NjAzMjQ0NiBNNTguMTAxOTI4MjkwNjEyMTM2IDI2LjE4NTgzMjM5MTMxMjY0NCBDNTguMDI3NTc5OTQ3MDEzMzEgMjYuMjMwMTM0Mzg4MDgzODgzLCA1Ny45NTMyMzE2MDM0MTQ0OCAyNi4yNzQ0MzYzODQ4NTUxMjUsIDU3Ljc0NjkyNDQ2NTE4NTM2NiAyNi4zOTczNjg3NTYwMzI0NDYgTTU3Ljc0NjkyNDQ2NTE4NTM2NiAyNi4zOTczNjg3NTYwMzI0NDYgQzU3LjYyMDQxMDYxMDE0MzQ0IDI2LjQ1OTIxNzYxMTAzNjg5OCwgNTcuNDkzODk2NzU1MTAxNTIgMjYuNTIxMDY2NDY2MDQxMzUsIDU3LjM3NTY2NDYyMzI2NDg0NiAyNi41Nzg4NjY2MzMyNzUyODYgTTU3Ljc0NjkyNDQ2NTE4NTM2NiAyNi4zOTczNjg3NTYwMzI0NDYgQzU3LjY0MjUxNzk3NDcwMTAyIDI2LjQ0ODQwOTk3OTA2MTczMywgNTcuNTM4MTExNDg0MjE2NjggMjYuNDk5NDUxMjAyMDkxMDIsIDU3LjM3NTY2NDYyMzI2NDg0NiAyNi41Nzg4NjY2MzMyNzUyODYgTTU3LjM3NTY2NDYyMzI2NDg0NiAyNi41Nzg4NjY2MzMyNzUyODYgQzU3LjI4NTE1MjMyMzM5MjcxIDI2LjYxNDE4NDY0MjI4MTE2LCA1Ny4xOTQ2NDAwMjM1MjA1NyAyNi42NDk1MDI2NTEyODcwMzMsIDU2Ljk5MDY4NDg0NjAyMzQyIDI2LjcyOTA4NjIwODUwMzE3MyBNNTcuMzc1NjY0NjIzMjY0ODQ2IDI2LjU3ODg2NjYzMzI3NTI4NiBDNTcuMjg2NDI3OTE5MjY2NzkgMjYuNjEzNjg2OTAzMjI2NTUsIDU3LjE5NzE5MTIxNTI2ODc0IDI2LjY0ODUwNzE3MzE3NzgxNiwgNTYuOTkwNjg0ODQ2MDIzNDIgMjYuNzI5MDg2MjA4NTAzMTczIE01Ni45OTA2ODQ4NDYwMjM0MiAyNi43MjkwODYyMDg1MDMxNzMgQzU2Ljg4NDIzMDIxMDE5OTY0IDI2Ljc2MDc3OTEyNjc0MjM5MiwgNTYuNzc3Nzc1NTc0Mzc1ODY2IDI2Ljc5MjQ3MjA0NDk4MTYxLCA1Ni41OTQ2MTQ5MzU3MDM5OTQgMjYuODQ3MDAxMzI5Njk2NjUzIE01Ni45OTA2ODQ4NDYwMjM0MiAyNi43MjkwODYyMDg1MDMxNzMgQzU2Ljg3NzI1NTQyMTI0MTQxNSAyNi43NjI4NTU2MTEzODI4MDgsIDU2Ljc2MzgyNTk5NjQ1OTQxIDI2Ljc5NjYyNTAxNDI2MjQ0LCA1Ni41OTQ2MTQ5MzU3MDM5OTQgMjYuODQ3MDAxMzI5Njk2NjUzIE01Ni41OTQ2MTQ5MzU3MDM5OTQgMjYuODQ3MDAxMzI5Njk2NjUzIEM1Ni41MDYxNTUwMjAwMDczIDI2Ljg2NTU0OTQyMzUxNjYsIDU2LjQxNzY5NTEwNDMxMDYgMjYuODg0MDk3NTE3MzM2NTQ2LCA1Ni4xOTAxNjA0NTE0MDM2NyAyNi45MzE4MDY1MTcwMTM2MTIgTTU2LjU5NDYxNDkzNTcwMzk5NCAyNi44NDcwMDEzMjk2OTY2NTMgQzU2LjQ3ODc1NDU5NDg0Nzk3IDI2Ljg3MTI5NDY4ODUwNTM3LCA1Ni4zNjI4OTQyNTM5OTE5NCAyNi44OTU1ODgwNDczMTQwODUsIDU2LjE5MDE2MDQ1MTQwMzY3IDI2LjkzMTgwNjUxNzAxMzYxMiBNNTYuMTkwMTYwNDUxNDAzNjcgMjYuOTMxODA2NTE3MDEzNjEyIEM1Ni4wNDg4Njg5MjU1Mzg2MzUgMjYuOTQ5NDE4NDg3MzQ1NzYsIDU1LjkwNzU3NzM5OTY3MzU5IDI2Ljk2NzAzMDQ1NzY3NzkwNCwgNTUuNzgwMDg0MjI3MzYxNjYgMjYuOTgyOTIyNDY1MDMzMzQ3IE01Ni4xOTAxNjA0NTE0MDM2NyAyNi45MzE4MDY1MTcwMTM2MTIgQzU2LjA3Nzk5NTA0Mzk5MzAyIDI2Ljk0NTc4NzkyMDQ2NjYxNCwgNTUuOTY1ODI5NjM2NTgyMzggMjYuOTU5NzY5MzIzOTE5NjE2LCA1NS43ODAwODQyMjczNjE2NiAyNi45ODI5MjI0NjUwMzMzNDcgTTU1Ljc4MDA4NDIyNzM2MTY2IDI2Ljk4MjkyMjQ2NTAzMzM0NyBDNTUuNjgwNTI4MDE2NjgyNzg2IDI2Ljk4NzA0MDE0MDM2NTU4LCA1NS41ODA5NzE4MDYwMDM5MSAyNi45OTExNTc4MTU2OTc4MDcsIDU1LjM2NzE4NzUgMjcgTTU1Ljc4MDA4NDIyNzM2MTY2IDI2Ljk4MjkyMjQ2NTAzMzM0NyBDNTUuNjgyMjQ3Nzc4OTM5NjMgMjYuOTg2OTY5MDEwNDcyNDg1LCA1NS41ODQ0MTEzMzA1MTc2IDI2Ljk5MTAxNTU1NTkxMTYyMywgNTUuMzY3MTg3NSAyNyBNNTUuMzY3MTg3NSAyNyBDNTUuMzY3MTg3NSAyNywgNTUuMzY3MTg3NSAyNywgNTUuMzY3MTg3NSAyNyBNNTUuMzY3MTg3NSAyNyBDNTUuMzY3MTg3NSAyNywgNTUuMzY3MTg3NSAyNywgNTUuMzY3MTg3NSAyNyBNNTUuMzY3MTg3NSAyNyBDMTEuMzI3ODIxNDU3NTYzMjEgMjcsIC0zMi43MTE1NDQ1ODQ4NzM1OCAyNywgLTU1LjM2NzE4NzUgMjcgTTU1LjM2NzE4NzUgMjcgQzI3LjkyOTA3NDcyNTYwMTY5NCAyNywgMC40OTA5NjE5NTEyMDMzODg1IDI3LCAtNTUuMzY3MTg3NSAyNyBNLTU1LjM2NzE4NzUgMjcgQy01NS4zNjcxODc1IDI3LCAtNTUuMzY3MTg3NSAyNywgLTU1LjM2NzE4NzUgMjcgTS01NS4zNjcxODc1IDI3IEMtNTUuMzY3MTg3NSAyNywgLTU1LjM2NzE4NzUgMjcsIC01NS4zNjcxODc1IDI3IE0tNTUuMzY3MTg3NSAyNyBDLTU1LjUwNjM2NTY3MjAzNTM3IDI2Ljk5NDI0MzU0ODIyMzk5LCAtNTUuNjQ1NTQzODQ0MDcwNzQ0IDI2Ljk4ODQ4NzA5NjQ0Nzk4MywgLTU1Ljc4MDA4NDIyNzM2MTY2IDI2Ljk4MjkyMjQ2NTAzMzM0NyBNLTU1LjM2NzE4NzUgMjcgQy01NS40NTg4OTU3Mzg3MDc1OSAyNi45OTYyMDY5MTkxODk1MzYsIC01NS41NTA2MDM5Nzc0MTUxOCAyNi45OTI0MTM4MzgzNzkwNzIsIC01NS43ODAwODQyMjczNjE2NiAyNi45ODI5MjI0NjUwMzMzNDcgTS01NS43ODAwODQyMjczNjE2NiAyNi45ODI5MjI0NjUwMzMzNDcgQy01NS45MDE2Nzc1Nzc2MzU5ODYgMjYuOTY3NzY1ODY5NzI3OTU1LCAtNTYuMDIzMjcwOTI3OTEwMzEgMjYuOTUyNjA5Mjc0NDIyNTY0LCAtNTYuMTkwMTYwNDUxNDAzNjcgMjYuOTMxODA2NTE3MDEzNjEyIE0tNTUuNzgwMDg0MjI3MzYxNjYgMjYuOTgyOTIyNDY1MDMzMzQ3IEMtNTUuOTAwOTczNjAzOTE5NTUgMjYuOTY3ODUzNjE5OTYwMiwgLTU2LjAyMTg2Mjk4MDQ3NzQ1IDI2Ljk1Mjc4NDc3NDg4NzA0NiwgLTU2LjE5MDE2MDQ1MTQwMzY3IDI2LjkzMTgwNjUxNzAxMzYxMiBNLTU2LjE5MDE2MDQ1MTQwMzY3IDI2LjkzMTgwNjUxNzAxMzYxMiBDLTU2LjMxNTY0MTA5MTE2OTA0IDI2LjkwNTQ5NTk5MzY0NjkwMiwgLTU2LjQ0MTEyMTczMDkzNDQyIDI2Ljg3OTE4NTQ3MDI4MDE5LCAtNTYuNTk0NjE0OTM1NzAzOTk0IDI2Ljg0NzAwMTMyOTY5NjY1MyBNLTU2LjE5MDE2MDQ1MTQwMzY3IDI2LjkzMTgwNjUxNzAxMzYxMiBDLTU2LjMxNDc1NTQ3Mzg3OTQ1IDI2LjkwNTY4MTY4ODA2NTA2MiwgLTU2LjQzOTM1MDQ5NjM1NTIzIDI2Ljg3OTU1Njg1OTExNjUxNiwgLTU2LjU5NDYxNDkzNTcwMzk5NCAyNi44NDcwMDEzMjk2OTY2NTMgTS01Ni41OTQ2MTQ5MzU3MDM5OTQgMjYuODQ3MDAxMzI5Njk2NjUzIEMtNTYuNjg3MDE1MzM3MzM2Mzc2IDI2LjgxOTQ5MjUzODI2MTI3LCAtNTYuNzc5NDE1NzM4OTY4NzUgMjYuNzkxOTgzNzQ2ODI1ODg1LCAtNTYuOTkwNjg0ODQ2MDIzNDIgMjYuNzI5MDg2MjA4NTAzMTczIE0tNTYuNTk0NjE0OTM1NzAzOTk0IDI2Ljg0NzAwMTMyOTY5NjY1MyBDLTU2LjcyNTE2MDU2ODg2MDQ3IDI2LjgwODEzNjIxMDgwMjU5LCAtNTYuODU1NzA2MjAyMDE2OTQgMjYuNzY5MjcxMDkxOTA4NTMsIC01Ni45OTA2ODQ4NDYwMjM0MiAyNi43MjkwODYyMDg1MDMxNzMgTS01Ni45OTA2ODQ4NDYwMjM0MiAyNi43MjkwODYyMDg1MDMxNzMgQy01Ny4wOTcxODQ2Mjk0Njc2NiAyNi42ODc1Mjk4NjQwNDkyMDcsIC01Ny4yMDM2ODQ0MTI5MTE5IDI2LjY0NTk3MzUxOTU5NTI0LCAtNTcuMzc1NjY0NjIzMjY0ODQ2IDI2LjU3ODg2NjYzMzI3NTI4NiBNLTU2Ljk5MDY4NDg0NjAyMzQyIDI2LjcyOTA4NjIwODUwMzE3MyBDLTU3LjEwNTEzODQ2NjY2ODc5IDI2LjY4NDQyNjI2NzE0MjUxMywgLTU3LjIxOTU5MjA4NzMxNDE3IDI2LjYzOTc2NjMyNTc4MTg1NiwgLTU3LjM3NTY2NDYyMzI2NDg0NiAyNi41Nzg4NjY2MzMyNzUyODYgTS01Ny4zNzU2NjQ2MjMyNjQ4NDYgMjYuNTc4ODY2NjMzMjc1Mjg2IEMtNTcuNDcxNTI4MzU1NDc4OTggMjYuNTMyMDAxNzEwMjgzNzg3LCAtNTcuNTY3MzkyMDg3NjkzMTE1IDI2LjQ4NTEzNjc4NzI5MjI4NywgLTU3Ljc0NjkyNDQ2NTE4NTM2NiAyNi4zOTczNjg3NTYwMzI0NDYgTS01Ny4zNzU2NjQ2MjMyNjQ4NDYgMjYuNTc4ODY2NjMzMjc1Mjg2IEMtNTcuNTE2ODAxODczNjk3NTk1IDI2LjUwOTg2ODgzNTg4MTA4NiwgLTU3LjY1NzkzOTEyNDEzMDM0NCAyNi40NDA4NzEwMzg0ODY4ODMsIC01Ny43NDY5MjQ0NjUxODUzNjYgMjYuMzk3MzY4NzU2MDMyNDQ2IE0tNTcuNzQ2OTI0NDY1MTg1MzY2IDI2LjM5NzM2ODc1NjAzMjQ0NiBDLTU3Ljg1MzU5NjQ4MzIwMjM0NSAyNi4zMzM4MDYwMjk5NjQ2MDYsIC01Ny45NjAyNjg1MDEyMTkzMiAyNi4yNzAyNDMzMDM4OTY3NywgLTU4LjEwMTkyODI5MDYxMjEzNiAyNi4xODU4MzIzOTEzMTI2NDQgTS01Ny43NDY5MjQ0NjUxODUzNjYgMjYuMzk3MzY4NzU2MDMyNDQ2IEMtNTcuODgwNTI4MDEwMTg2MjEgMjYuMzE3NzU4MzIzMTMzMDc3LCAtNTguMDE0MTMxNTU1MTg3MDUgMjYuMjM4MTQ3ODkwMjMzNzEyLCAtNTguMTAxOTI4MjkwNjEyMTM2IDI2LjE4NTgzMjM5MTMxMjY0NCBNLTU4LjEwMTkyODI5MDYxMjEzNiAyNi4xODU4MzIzOTEzMTI2NDQgQy01OC4yMTQ4OTAxOTE0OTQ5NiAyNi4xMDUxNzkxNDQyOTc3NiwgLTU4LjMyNzg1MjA5MjM3Nzc4IDI2LjAyNDUyNTg5NzI4Mjg3NCwgLTU4LjQzODI1MTA2MzQ0ODM0IDI1Ljk0NTcwMjU0Njk4MTk3IE0tNTguMTAxOTI4MjkwNjEyMTM2IDI2LjE4NTgzMjM5MTMxMjY0NCBDLTU4LjE4OTIyNDkxNjM4MjUyIDI2LjEyMzUwMzc5ODUxNDU0NSwgLTU4LjI3NjUyMTU0MjE1MjkgMjYuMDYxMTc1MjA1NzE2NDQ1LCAtNTguNDM4MjUxMDYzNDQ4MzQgMjUuOTQ1NzAyNTQ2OTgxOTcgTS01OC40MzgyNTEwNjM0NDgzNCAyNS45NDU3MDI1NDY5ODE5NyBDLTU4LjUwNzEyNzYxOTg1MTQ0IDI1Ljg4NzM2NzA3OTQxNzAxLCAtNTguNTc2MDA0MTc2MjU0NTM2IDI1LjgyOTAzMTYxMTg1MjA1MiwgLTU4Ljc1MzU5NTM1ODEyODcwNiAyNS42Nzg2MTk1NTMzNjU2NiBNLTU4LjQzODI1MTA2MzQ0ODM0IDI1Ljk0NTcwMjU0Njk4MTk3IEMtNTguNTAyMzA5MTcwNTg4MDIgMjUuODkxNDQ4MDk3NzY5MjQyLCAtNTguNTY2MzY3Mjc3NzI3NjkgMjUuODM3MTkzNjQ4NTU2NTE0LCAtNTguNzUzNTk1MzU4MTI4NzA2IDI1LjY3ODYxOTU1MzM2NTY2IE0tNTguNzUzNTk1MzU4MTI4NzA2IDI1LjY3ODYxOTU1MzM2NTY2IEMtNTguODQ5MTc1MzE1MTMwNzEgMjUuNTgzMDM5NTk2MzYzNjU1LCAtNTguOTQ0NzU1MjcyMTMyNzIgMjUuNDg3NDU5NjM5MzYxNjUsIC01OS4wNDU4MDcwNTMzNjU2NiAyNS4zODY0MDc4NTgxMjg3MDYgTS01OC43NTM1OTUzNTgxMjg3MDYgMjUuNjc4NjE5NTUzMzY1NjYgQy01OC44Mjc0MzgwNDE3MjIwOSAyNS42MDQ3NzY4Njk3NzIyNzcsIC01OC45MDEyODA3MjUzMTU0NyAyNS41MzA5MzQxODYxNzg4OTMsIC01OS4wNDU4MDcwNTMzNjU2NiAyNS4zODY0MDc4NTgxMjg3MDYgTS01OS4wNDU4MDcwNTMzNjU2NiAyNS4zODY0MDc4NTgxMjg3MDYgQy01OS4xMjI2OTYyMDk4NzQwMSAyNS4yOTU2MjUwMDI1ODQ4MywgLTU5LjE5OTU4NTM2NjM4MjM2NSAyNS4yMDQ4NDIxNDcwNDA5NiwgLTU5LjMxMjg5MDA0Njk4MTk3IDI1LjA3MTA2MzU2MzQ0ODM0IE0tNTkuMDQ1ODA3MDUzMzY1NjYgMjUuMzg2NDA3ODU4MTI4NzA2IEMtNTkuMTQxNjE4Nzc4ODUzOTc0IDI1LjI3MzI4MzE2NzYwNzg4LCAtNTkuMjM3NDMwNTA0MzQyMjkgMjUuMTYwMTU4NDc3MDg3MDQ4LCAtNTkuMzEyODkwMDQ2OTgxOTcgMjUuMDcxMDYzNTYzNDQ4MzQgTS01OS4zMTI4OTAwNDY5ODE5NyAyNS4wNzEwNjM1NjM0NDgzNCBDLTU5LjM3NTI5MjQ2ODA4Nzc0IDI0Ljk4MzY2MzUzNDY5OTM3LCAtNTkuNDM3Njk0ODg5MTkzNTIgMjQuODk2MjYzNTA1OTUwMzk3LCAtNTkuNTUzMDE5ODkxMzEyNjQ0IDI0LjczNDc0MDc5MDYxMjEzMyBNLTU5LjMxMjg5MDA0Njk4MTk3IDI1LjA3MTA2MzU2MzQ0ODM0IEMtNTkuMzk3MTgzNjMwNjgwNjcgMjQuOTUzMDAzMDUzODA2Njk3LCAtNTkuNDgxNDc3MjE0Mzc5Mzc1IDI0LjgzNDk0MjU0NDE2NTA1MiwgLTU5LjU1MzAxOTg5MTMxMjY0NCAyNC43MzQ3NDA3OTA2MTIxMzMgTS01OS41NTMwMTk4OTEzMTI2NDQgMjQuNzM0NzQwNzkwNjEyMTMzIEMtNTkuNjI4NTA1MjE3NzY2NzQgMjQuNjA4MDYwMDY3NDY0MTg3LCAtNTkuNzAzOTkwNTQ0MjIwODMgMjQuNDgxMzc5MzQ0MzE2MjQsIC01OS43NjQ1NTYyNTYwMzI0NCAyNC4zNzk3MzY5NjUxODUzNyBNLTU5LjU1MzAxOTg5MTMxMjY0NCAyNC43MzQ3NDA3OTA2MTIxMzMgQy01OS42MzQxNTIxMDU2ODMzMiAyNC41OTg1ODMzNjY3ODA2MDIsIC01OS43MTUyODQzMjAwNTM5OTYgMjQuNDYyNDI1OTQyOTQ5MDc1LCAtNTkuNzY0NTU2MjU2MDMyNDQgMjQuMzc5NzM2OTY1MTg1MzcgTS01OS43NjQ1NTYyNTYwMzI0NCAyNC4zNzk3MzY5NjUxODUzNyBDLTU5LjgxNTk2MzgwNzkyMTE2NSAyNC4yNzQ1ODExMzcwNDIwNCwgLTU5Ljg2NzM3MTM1OTgwOTg4IDI0LjE2OTQyNTMwODg5ODcxMywgLTU5Ljk0NjA1NDEzMzI3NTI4IDI0LjAwODQ3NzEyMzI2NDg1IE0tNTkuNzY0NTU2MjU2MDMyNDQgMjQuMzc5NzM2OTY1MTg1MzcgQy01OS44MTE1MjQyNDQyMzQzMDYgMjQuMjgzNjYyNDA5NzEzNzUyLCAtNTkuODU4NDkyMjMyNDM2MTYgMjQuMTg3NTg3ODU0MjQyMTM1LCAtNTkuOTQ2MDU0MTMzMjc1MjggMjQuMDA4NDc3MTIzMjY0ODUgTS01OS45NDYwNTQxMzMyNzUyOCAyNC4wMDg0NzcxMjMyNjQ4NSBDLTU5Ljk4NzUxMTgzNjA5NDk2IDIzLjkwMjIzMDEzNjY2Mzc1LCAtNjAuMDI4OTY5NTM4OTE0NjQgMjMuNzk1OTgzMTUwMDYyNjU0LCAtNjAuMDk2MjczNzA4NTAzMTc2IDIzLjYyMzQ5NzM0NjAyMzQxNyBNLTU5Ljk0NjA1NDEzMzI3NTI4IDI0LjAwODQ3NzEyMzI2NDg1IEMtNTkuOTgwMzc3NjQ0OTM4NDY0IDIzLjkyMDUxMzUwMTY1MTY5OCwgLTYwLjAxNDcwMTE1NjYwMTY0IDIzLjgzMjU0OTg4MDAzODU1LCAtNjAuMDk2MjczNzA4NTAzMTc2IDIzLjYyMzQ5NzM0NjAyMzQxNyBNLTYwLjA5NjI3MzcwODUwMzE3NiAyMy42MjM0OTczNDYwMjM0MTcgQy02MC4xMzY4Mzk3NTE1MDkyIDIzLjQ4NzIzODQwOTQ4NjgwMywgLTYwLjE3NzQwNTc5NDUxNTIyIDIzLjM1MDk3OTQ3Mjk1MDE5LCAtNjAuMjE0MTg4ODI5Njk2NjUgMjMuMjI3NDI3NDM1NzAzOTk0IE0tNjAuMDk2MjczNzA4NTAzMTc2IDIzLjYyMzQ5NzM0NjAyMzQxNyBDLTYwLjEyOTQwMDYxMjk3NTg1IDIzLjUxMjIyNjAzNTM0Nzk2NSwgLTYwLjE2MjUyNzUxNzQ0ODUzIDIzLjQwMDk1NDcyNDY3MjUxMywgLTYwLjIxNDE4ODgyOTY5NjY1IDIzLjIyNzQyNzQzNTcwMzk5NCBNLTYwLjIxNDE4ODgyOTY5NjY1IDIzLjIyNzQyNzQzNTcwMzk5NCBDLTYwLjIzNDk5NDY3NTE0NjMzIDIzLjEyODE5OTgwODczOTAxLCAtNjAuMjU1ODAwNTIwNTk2MDEgMjMuMDI4OTcyMTgxNzc0MDIzLCAtNjAuMjk4OTk0MDE3MDEzNjEgMjIuODIyOTcyOTUxNDAzNjcgTS02MC4yMTQxODg4Mjk2OTY2NSAyMy4yMjc0Mjc0MzU3MDM5OTQgQy02MC4yMzI0Mjg1MDMwNzQ3MSAyMy4xNDA0Mzg0NDQ1NDI5NCwgLTYwLjI1MDY2ODE3NjQ1Mjc1NiAyMy4wNTM0NDk0NTMzODE4ODQsIC02MC4yOTg5OTQwMTcwMTM2MSAyMi44MjI5NzI5NTE0MDM2NyBNLTYwLjI5ODk5NDAxNzAxMzYxIDIyLjgyMjk3Mjk1MTQwMzY3IEMtNjAuMzE3MTQyNzY3NDEwNDUgMjIuNjc3Mzc1MTIyODk1MzM4LCAtNjAuMzM1MjkxNTE3ODA3MyAyMi41MzE3NzcyOTQzODcwMSwgLTYwLjM1MDEwOTk2NTAzMzM1IDIyLjQxMjg5NjcyNzM2MTY2MiBNLTYwLjI5ODk5NDAxNzAxMzYxIDIyLjgyMjk3Mjk1MTQwMzY3IEMtNjAuMzE4MjcyODkwMjUxNDYgMjIuNjY4MzA4NzQ0ODgwMzYsIC02MC4zMzc1NTE3NjM0ODkyOTQgMjIuNTEzNjQ0NTM4MzU3MDUsIC02MC4zNTAxMDk5NjUwMzMzNSAyMi40MTI4OTY3MjczNjE2NjIgTS02MC4zNTAxMDk5NjUwMzMzNSAyMi40MTI4OTY3MjczNjE2NjIgQy02MC4zNTQ0NjI2NDExMjkwMjUgMjIuMzA3NjU4NzIyMDcxMDU3LCAtNjAuMzU4ODE1MzE3MjI0NyAyMi4yMDI0MjA3MTY3ODA0NTUsIC02MC4zNjcxODc1IDIyIE0tNjAuMzUwMTA5OTY1MDMzMzUgMjIuNDEyODk2NzI3MzYxNjYyIEMtNjAuMzU1OTI0NTY4NjAxNjcgMjIuMjcyMzEyNTc0NjE2MjIsIC02MC4zNjE3MzkxNzIxNyAyMi4xMzE3Mjg0MjE4NzA3OCwgLTYwLjM2NzE4NzUgMjIgTS02MC4zNjcxODc1IDIyIEMtNjAuMzY3MTg3NSAyMiwgLTYwLjM2NzE4NzUgMjIsIC02MC4zNjcxODc1IDIyIE0tNjAuMzY3MTg3NSAyMiBDLTYwLjM2NzE4NzUgMjIsIC02MC4zNjcxODc1IDIyLCAtNjAuMzY3MTg3NSAyMiBNLTYwLjM2NzE4NzUgMjIgQy02MC4zNjcxODc1IDUuMjA5NjMxOTkzOTAxNzg3LCAtNjAuMzY3MTg3NSAtMTEuNTgwNzM2MDEyMTk2NDI1LCAtNjAuMzY3MTg3NSAtMjIgTS02MC4zNjcxODc1IDIyIEMtNjAuMzY3MTg3NSA1LjM3NDAwMTYwMTcxOTE0NCwgLTYwLjM2NzE4NzUgLTExLjI1MTk5Njc5NjU2MTcxMywgLTYwLjM2NzE4NzUgLTIyIE0tNjAuMzY3MTg3NSAtMjIgQy02MC4zNjcxODc1IC0yMiwgLTYwLjM2NzE4NzUgLTIyLCAtNjAuMzY3MTg3NSAtMjIgTS02MC4zNjcxODc1IC0yMiBDLTYwLjM2NzE4NzUgLTIyLCAtNjAuMzY3MTg3NSAtMjIsIC02MC4zNjcxODc1IC0yMiBNLTYwLjM2NzE4NzUgLTIyIEMtNjAuMzYzNjY5MjAwNjY1OTUgLTIyLjA4NTA2NDYzNTEzMjkxNiwgLTYwLjM2MDE1MDkwMTMzMTkgLTIyLjE3MDEyOTI3MDI2NTgzNiwgLTYwLjM1MDEwOTk2NTAzMzM1IC0yMi40MTI4OTY3MjczNjE2NiBNLTYwLjM2NzE4NzUgLTIyIEMtNjAuMzYzNTM4MjU1NzU1MDk1IC0yMi4wODgyMzA1OTExODM0NDYsIC02MC4zNTk4ODkwMTE1MTAxOSAtMjIuMTc2NDYxMTgyMzY2ODkzLCAtNjAuMzUwMTA5OTY1MDMzMzUgLTIyLjQxMjg5NjcyNzM2MTY2IE0tNjAuMzUwMTA5OTY1MDMzMzUgLTIyLjQxMjg5NjcyNzM2MTY2IEMtNjAuMzM5NDk4MzUwMzU0NDMgLTIyLjQ5ODAyODEwMTE3MTYsIC02MC4zMjg4ODY3MzU2NzU1MiAtMjIuNTgzMTU5NDc0OTgxNTQsIC02MC4yOTg5OTQwMTcwMTM2MSAtMjIuODIyOTcyOTUxNDAzNjcgTS02MC4zNTAxMDk5NjUwMzMzNSAtMjIuNDEyODk2NzI3MzYxNjYgQy02MC4zMzIyMTI0MjYzNTk4NzQgLTIyLjU1NjQ3OTIxNzAzODQ1OCwgLTYwLjMxNDMxNDg4NzY4NjQgLTIyLjcwMDA2MTcwNjcxNTI1NywgLTYwLjI5ODk5NDAxNzAxMzYxIC0yMi44MjI5NzI5NTE0MDM2NyBNLTYwLjI5ODk5NDAxNzAxMzYxIC0yMi44MjI5NzI5NTE0MDM2NyBDLTYwLjI4MDc4NTQ4MjM1NjQ4NSAtMjIuOTA5ODEzNDM1MTk0OTMsIC02MC4yNjI1NzY5NDc2OTkzNiAtMjIuOTk2NjUzOTE4OTg2MTk0LCAtNjAuMjE0MTg4ODI5Njk2NjUgLTIzLjIyNzQyNzQzNTcwMzk5NCBNLTYwLjI5ODk5NDAxNzAxMzYxIC0yMi44MjI5NzI5NTE0MDM2NyBDLTYwLjI3NzUwMDA0MTE5MzMzNSAtMjIuOTI1NDgyNDIyNTkyNzc2LCAtNjAuMjU2MDA2MDY1MzczMDYgLTIzLjAyNzk5MTg5Mzc4MTg3NywgLTYwLjIxNDE4ODgyOTY5NjY1IC0yMy4yMjc0Mjc0MzU3MDM5OTQgTS02MC4yMTQxODg4Mjk2OTY2NSAtMjMuMjI3NDI3NDM1NzAzOTk0IEMtNjAuMTc3OTEwMTYyODQ1NTMgLTIzLjM0OTI4NTMyOTU5MjM5NCwgLTYwLjE0MTYzMTQ5NTk5NDQxIC0yMy40NzExNDMyMjM0ODA3OTQsIC02MC4wOTYyNzM3MDg1MDMxNzYgLTIzLjYyMzQ5NzM0NjAyMzQxNyBNLTYwLjIxNDE4ODgyOTY5NjY1IC0yMy4yMjc0Mjc0MzU3MDM5OTQgQy02MC4xNzE0MTMxNjc3OTUwMSAtMjMuMzcxMTA4MzUxMjc5MTksIC02MC4xMjg2Mzc1MDU4OTMzNiAtMjMuNTE0Nzg5MjY2ODU0Mzg2LCAtNjAuMDk2MjczNzA4NTAzMTc2IC0yMy42MjM0OTczNDYwMjM0MTcgTS02MC4wOTYyNzM3MDg1MDMxNzYgLTIzLjYyMzQ5NzM0NjAyMzQxNyBDLTYwLjA0MzQ4Nzg1NjM4Nzk1IC0yMy43NTg3NzU4OTExNjYzNjIsIC01OS45OTA3MDIwMDQyNzI3MiAtMjMuODk0MDU0NDM2MzA5MzA3LCAtNTkuOTQ2MDU0MTMzMjc1MjkgLTI0LjAwODQ3NzEyMzI2NDg0NiBNLTYwLjA5NjI3MzcwODUwMzE3NiAtMjMuNjIzNDk3MzQ2MDIzNDE3IEMtNjAuMDQ2Nzc2ODI4OTExNCAtMjMuNzUwMzQ2OTc2OTc1MTgsIC01OS45OTcyNzk5NDkzMTk2MiAtMjMuODc3MTk2NjA3OTI2OTQ4LCAtNTkuOTQ2MDU0MTMzMjc1MjkgLTI0LjAwODQ3NzEyMzI2NDg0NiBNLTU5Ljk0NjA1NDEzMzI3NTI5IC0yNC4wMDg0NzcxMjMyNjQ4NDYgQy01OS44ODA5NTcwOTAwOTc4NDQgLTI0LjE0MTYzNTI1MzQxMzE5NSwgLTU5LjgxNTg2MDA0NjkyMDQgLTI0LjI3NDc5MzM4MzU2MTU0OCwgLTU5Ljc2NDU1NjI1NjAzMjQ1IC0yNC4zNzk3MzY5NjUxODUzNjYgTS01OS45NDYwNTQxMzMyNzUyOSAtMjQuMDA4NDc3MTIzMjY0ODQ2IEMtNTkuODgzOTMyMDkxMTM3NzIgLTI0LjEzNTU0OTc5MTUxOTMyOCwgLTU5LjgyMTgxMDA0OTAwMDE2IC0yNC4yNjI2MjI0NTk3NzM4MDUsIC01OS43NjQ1NTYyNTYwMzI0NSAtMjQuMzc5NzM2OTY1MTg1MzY2IE0tNTkuNzY0NTU2MjU2MDMyNDUgLTI0LjM3OTczNjk2NTE4NTM2NiBDLTU5LjcwMTIzNDY5OTYwMzM4IC0yNC40ODYwMDQyNDgzMjYzNiwgLTU5LjYzNzkxMzE0MzE3NDMxNiAtMjQuNTkyMjcxNTMxNDY3MzUsIC01OS41NTMwMTk4OTEzMTI2NDQgLTI0LjczNDc0MDc5MDYxMjEzMyBNLTU5Ljc2NDU1NjI1NjAzMjQ1IC0yNC4zNzk3MzY5NjUxODUzNjYgQy01OS43MDg2OTYxNDMzNjY2NiAtMjQuNDczNDgyMzMwMDEzNzM4LCAtNTkuNjUyODM2MDMwNzAwODcgLTI0LjU2NzIyNzY5NDg0MjExLCAtNTkuNTUzMDE5ODkxMzEyNjQ0IC0yNC43MzQ3NDA3OTA2MTIxMzMgTS01OS41NTMwMTk4OTEzMTI2NDQgLTI0LjczNDc0MDc5MDYxMjEzMyBDLTU5LjUwNDYwNzU5MTM5NDYyNCAtMjQuODAyNTQ2NDM1NDgzOTMzLCAtNTkuNDU2MTk1MjkxNDc2NjA0IC0yNC44NzAzNTIwODAzNTU3MywgLTU5LjMxMjg5MDA0Njk4MTk3IC0yNS4wNzEwNjM1NjM0NDgzNCBNLTU5LjU1MzAxOTg5MTMxMjY0NCAtMjQuNzM0NzQwNzkwNjEyMTMzIEMtNTkuNDYxNzM5MzQwODUyMjEgLTI0Ljg2MjU4NzE1NjA3NTExLCAtNTkuMzcwNDU4NzkwMzkxNzggLTI0Ljk5MDQzMzUyMTUzODA5LCAtNTkuMzEyODkwMDQ2OTgxOTcgLTI1LjA3MTA2MzU2MzQ0ODM0IE0tNTkuMzEyODkwMDQ2OTgxOTcgLTI1LjA3MTA2MzU2MzQ0ODM0IEMtNTkuMjE4MTkwNTgwNzAyODc0IC0yNS4xODI4NzUwMTIwMDIzNzMsIC01OS4xMjM0OTExMTQ0MjM3ODYgLTI1LjI5NDY4NjQ2MDU1NjQwNiwgLTU5LjA0NTgwNzA1MzM2NTY2IC0yNS4zODY0MDc4NTgxMjg3MDYgTS01OS4zMTI4OTAwNDY5ODE5NyAtMjUuMDcxMDYzNTYzNDQ4MzQgQy01OS4yMjI2MDEwMDQ0NjUyMiAtMjUuMTc3NjY3NjM0NTA2MTY4LCAtNTkuMTMyMzExOTYxOTQ4NDcgLTI1LjI4NDI3MTcwNTU2NCwgLTU5LjA0NTgwNzA1MzM2NTY2IC0yNS4zODY0MDc4NTgxMjg3MDYgTS01OS4wNDU4MDcwNTMzNjU2NiAtMjUuMzg2NDA3ODU4MTI4NzA2IEMtNTguOTMxNzg1Njk1MjEzNzU1IC0yNS41MDA0MjkyMTYyODA2MDgsIC01OC44MTc3NjQzMzcwNjE4NSAtMjUuNjE0NDUwNTc0NDMyNTE0LCAtNTguNzUzNTk1MzU4MTI4NzA2IC0yNS42Nzg2MTk1NTMzNjU2NTcgTS01OS4wNDU4MDcwNTMzNjU2NiAtMjUuMzg2NDA3ODU4MTI4NzA2IEMtNTguOTcwMTE5MjM1NDczNjYgLTI1LjQ2MjA5NTY3NjAyMDcwNiwgLTU4Ljg5NDQzMTQxNzU4MTY2IC0yNS41Mzc3ODM0OTM5MTI3MDIsIC01OC43NTM1OTUzNTgxMjg3MDYgLTI1LjY3ODYxOTU1MzM2NTY1NyBNLTU4Ljc1MzU5NTM1ODEyODcwNiAtMjUuNjc4NjE5NTUzMzY1NjU3IEMtNTguNjgyMjg2MDcwNDgyNzYgLTI1LjczOTAxNTQzOTE5MDgsIC01OC42MTA5NzY3ODI4MzY4MTUgLTI1Ljc5OTQxMTMyNTAxNTk0NiwgLTU4LjQzODI1MTA2MzQ0ODM0IC0yNS45NDU3MDI1NDY5ODE5NjYgTS01OC43NTM1OTUzNTgxMjg3MDYgLTI1LjY3ODYxOTU1MzM2NTY1NyBDLTU4LjYzMjY3OTcwNTA1MjQ0NiAtMjUuNzgxMDI5ODg3NTQxNTUsIC01OC41MTE3NjQwNTE5NzYxOSAtMjUuODgzNDQwMjIxNzE3NDQ2LCAtNTguNDM4MjUxMDYzNDQ4MzQgLTI1Ljk0NTcwMjU0Njk4MTk2NiBNLTU4LjQzODI1MTA2MzQ0ODM0IC0yNS45NDU3MDI1NDY5ODE5NjYgQy01OC4zMTQxNDQyNzEwMDEzNTQgLTI2LjAzNDMxMzA5MzYyODQ0NSwgLTU4LjE5MDAzNzQ3ODU1NDM2IC0yNi4xMjI5MjM2NDAyNzQ5MjMsIC01OC4xMDE5MjgyOTA2MTIxMzYgLTI2LjE4NTgzMjM5MTMxMjY0NCBNLTU4LjQzODI1MTA2MzQ0ODM0IC0yNS45NDU3MDI1NDY5ODE5NjYgQy01OC4zMjgzMTEyOTA1MjI5NSAtMjYuMDI0MTk4MDM2MTA4OTYsIC01OC4yMTgzNzE1MTc1OTc1NTQgLTI2LjEwMjY5MzUyNTIzNTk1MywgLTU4LjEwMTkyODI5MDYxMjEzNiAtMjYuMTg1ODMyMzkxMzEyNjQ0IE0tNTguMTAxOTI4MjkwNjEyMTM2IC0yNi4xODU4MzIzOTEzMTI2NDQgQy01Ny45NjYxNjgzOTcwNzc4NjUgLTI2LjI2NjcyNzcyOTA0MTY0OCwgLTU3LjgzMDQwODUwMzU0MzU5NSAtMjYuMzQ3NjIzMDY2NzcwNjUsIC01Ny43NDY5MjQ0NjUxODUzNjYgLTI2LjM5NzM2ODc1NjAzMjQ0NiBNLTU4LjEwMTkyODI5MDYxMjEzNiAtMjYuMTg1ODMyMzkxMzEyNjQ0IEMtNTguMDA3NzU4MjQzMjkyODYgLTI2LjI0MTk0NTU1OTgxNTcyNiwgLTU3LjkxMzU4ODE5NTk3MzU4IC0yNi4yOTgwNTg3MjgzMTg4MSwgLTU3Ljc0NjkyNDQ2NTE4NTM2NiAtMjYuMzk3MzY4NzU2MDMyNDQ2IE0tNTcuNzQ2OTI0NDY1MTg1MzY2IC0yNi4zOTczNjg3NTYwMzI0NDYgQy01Ny42MjExMjgwMDk0NTA2NCAtMjYuNDU4ODY2ODk1ODg2MjQsIC01Ny40OTUzMzE1NTM3MTU5MTQgLTI2LjUyMDM2NTAzNTc0MDAzNSwgLTU3LjM3NTY2NDYyMzI2NDg0NiAtMjYuNTc4ODY2NjMzMjc1Mjg2IE0tNTcuNzQ2OTI0NDY1MTg1MzY2IC0yNi4zOTczNjg3NTYwMzI0NDYgQy01Ny42MDY4ODkwODc4NTgyOCAtMjYuNDY1ODI3ODgwNDg5MDIzLCAtNTcuNDY2ODUzNzEwNTMxMTkgLTI2LjUzNDI4NzAwNDk0NTYsIC01Ny4zNzU2NjQ2MjMyNjQ4NDYgLTI2LjU3ODg2NjYzMzI3NTI4NiBNLTU3LjM3NTY2NDYyMzI2NDg0NiAtMjYuNTc4ODY2NjMzMjc1Mjg2IEMtNTcuMjIzODIwNDkwODE4MzYgLTI2LjYzODExNjM5NzYxNDYwMywgLTU3LjA3MTk3NjM1ODM3MTg4IC0yNi42OTczNjYxNjE5NTM5MjMsIC01Ni45OTA2ODQ4NDYwMjM0MiAtMjYuNzI5MDg2MjA4NTAzMTczIE0tNTcuMzc1NjY0NjIzMjY0ODQ2IC0yNi41Nzg4NjY2MzMyNzUyODYgQy01Ny4yNDQ5MzM4NDM5MDY1OCAtMjYuNjI5ODc3OTQxNjc0NzI0LCAtNTcuMTE0MjAzMDY0NTQ4MzEgLTI2LjY4MDg4OTI1MDA3NDE2MywgLTU2Ljk5MDY4NDg0NjAyMzQyIC0yNi43MjkwODYyMDg1MDMxNzMgTS01Ni45OTA2ODQ4NDYwMjM0MiAtMjYuNzI5MDg2MjA4NTAzMTczIEMtNTYuODc5MjE4NjQ4MDM0OTMgLTI2Ljc2MjI3MTEzMzQ0Mjc4OCwgLTU2Ljc2Nzc1MjQ1MDA0NjQ0IC0yNi43OTU0NTYwNTgzODI0LCAtNTYuNTk0NjE0OTM1NzAzOTk0IC0yNi44NDcwMDEzMjk2OTY2NTMgTS01Ni45OTA2ODQ4NDYwMjM0MiAtMjYuNzI5MDg2MjA4NTAzMTczIEMtNTYuODkzMDQzMzQ2MTkwNTk1IC0yNi43NTgxNTUzNDI0Nzc1NjUsIC01Ni43OTU0MDE4NDYzNTc3NiAtMjYuNzg3MjI0NDc2NDUxOTU3LCAtNTYuNTk0NjE0OTM1NzAzOTk0IC0yNi44NDcwMDEzMjk2OTY2NTMgTS01Ni41OTQ2MTQ5MzU3MDM5OTQgLTI2Ljg0NzAwMTMyOTY5NjY1MyBDLTU2LjQ0NjYxODI5MjY5MDU3IC0yNi44NzgwMzI5NjI0NzY2NiwgLTU2LjI5ODYyMTY0OTY3NzE0NSAtMjYuOTA5MDY0NTk1MjU2NjY2LCAtNTYuMTkwMTYwNDUxNDAzNjcgLTI2LjkzMTgwNjUxNzAxMzYxMiBNLTU2LjU5NDYxNDkzNTcwMzk5NCAtMjYuODQ3MDAxMzI5Njk2NjUzIEMtNTYuNDQ2NjE3NzEzOTc0NjkgLTI2Ljg3ODAzMzA4MzgyMDYyLCAtNTYuMjk4NjIwNDkyMjQ1MzkgLTI2LjkwOTA2NDgzNzk0NDU4NCwgLTU2LjE5MDE2MDQ1MTQwMzY3IC0yNi45MzE4MDY1MTcwMTM2MTIgTS01Ni4xOTAxNjA0NTE0MDM2NyAtMjYuOTMxODA2NTE3MDEzNjEyIEMtNTYuMDgxMzQxNzUzNTI1MjIgLTI2Ljk0NTM3MDc1MzU1ODQ3NSwgLTU1Ljk3MjUyMzA1NTY0Njc4IC0yNi45NTg5MzQ5OTAxMDMzMzcsIC01NS43ODAwODQyMjczNjE2NiAtMjYuOTgyOTIyNDY1MDMzMzQ3IE0tNTYuMTkwMTYwNDUxNDAzNjcgLTI2LjkzMTgwNjUxNzAxMzYxMiBDLTU2LjA4NzE2NTYxNzkwMDI3NiAtMjYuOTQ0NjQ0ODA5NjIxMTg4LCAtNTUuOTg0MTcwNzg0Mzk2ODggLTI2Ljk1NzQ4MzEwMjIyODc2NCwgLTU1Ljc4MDA4NDIyNzM2MTY2IC0yNi45ODI5MjI0NjUwMzMzNDcgTS01NS43ODAwODQyMjczNjE2NiAtMjYuOTgyOTIyNDY1MDMzMzQ3IEMtNTUuNjYwMjczMzEzNzI0MjQgLTI2Ljk4Nzg3Nzg4MTA3NjczNCwgLTU1LjU0MDQ2MjQwMDA4NjgyNCAtMjYuOTkyODMzMjk3MTIwMTE4LCAtNTUuMzY3MTg3NSAtMjcgTS01NS43ODAwODQyMjczNjE2NiAtMjYuOTgyOTIyNDY1MDMzMzQ3IEMtNTUuNjg0ODA4ODg5NjgwMDg1IC0yNi45ODY4NjMwODIxNDg3MDUsIC01NS41ODk1MzM1NTE5OTg1MTQgLTI2Ljk5MDgwMzY5OTI2NDA2MywgLTU1LjM2NzE4NzUgLTI3IE0tNTUuMzY3MTg3NSAtMjcgQy01NS4zNjcxODc1IC0yNywgLTU1LjM2NzE4NzUgLTI3LCAtNTUuMzY3MTg3NSAtMjcgTS01NS4zNjcxODc1IC0yNyBDLTU1LjM2NzE4NzUgLTI3LCAtNTUuMzY3MTg3NSAtMjcsIC01NS4zNjcxODc1IC0yNyIgc3Ryb2tlPSIjOTM3MERCIiBzdHJva2Utd2lkdGg9IjEuMyIgZmlsbD0ibm9uZSIgc3Ryb2tlLWRhc2hhcnJheT0iMCAwIiBzdHlsZT0iIi8+PC9nPjxnIGNsYXNzPSJsYWJlbCIgc3R5bGU9IiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTQ1LjM2NzE4NzUsIC0xMikiPjxyZWN0Lz48Zm9yZWlnbk9iamVjdCB3aWR0aD0iOTAuNzM0Mzc1IiBoZWlnaHQ9IjI0Ij48ZGl2IHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hodG1sIiBzdHlsZT0iZGlzcGxheTogdGFibGUtY2VsbDsgd2hpdGUtc3BhY2U6IG5vd3JhcDsgbGluZS1oZWlnaHQ6IDEuNTsgbWF4LXdpZHRoOiAyMDBweDsgdGV4dC1hbGlnbjogY2VudGVyOyI+PHNwYW4gY2xhc3M9Im5vZGVMYWJlbCAiPjxwPkdvIHNob3BwaW5nPC9wPjwvc3Bhbj48L2Rpdj48L2ZvcmVpZ25PYmplY3Q+PC9nPjwvZz48ZyBjbGFzcz0ibm9kZSBkZWZhdWx0ICAiIGlkPSJmbG93Y2hhcnQtQy0zIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyMjEuODUxNTYyNSwgMzEwLjU3ODEyNSkiPjxwb2x5Z29uIHBvaW50cz0iNzAuNTc4MTI1LDAgMTQxLjE1NjI1LC03MC41NzgxMjUgNzAuNTc4MTI1LC0xNDEuMTU2MjUgMCwtNzAuNTc4MTI1IiBjbGFzcz0ibGFiZWwtY29udGFpbmVyIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtNzAuMDc4MTI1LCA3MC41NzgxMjUpIi8+PGcgY2xhc3M9ImxhYmVsIiBzdHlsZT0iIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtNDMuNTc4MTI1LCAtMTIpIj48cmVjdC8+PGZvcmVpZ25PYmplY3Qgd2lkdGg9Ijg3LjE1NjI1IiBoZWlnaHQ9IjI0Ij48ZGl2IHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hodG1sIiBzdHlsZT0iZGlzcGxheTogdGFibGUtY2VsbDsgd2hpdGUtc3BhY2U6IG5vd3JhcDsgbGluZS1oZWlnaHQ6IDEuNTsgbWF4LXdpZHRoOiAyMDBweDsgdGV4dC1hbGlnbjogY2VudGVyOyI+PHNwYW4gY2xhc3M9Im5vZGVMYWJlbCAiPjxwPkxldCBtZSB0aGluazwvcD48L3NwYW4+PC9kaXY+PC9mb3JlaWduT2JqZWN0PjwvZz48L2c+PGcgY2xhc3M9Im5vZGUgZGVmYXVsdCAgIiBpZD0iZmxvd2NoYXJ0LUQtNSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNjIuNDY4NzUsIDQ4Mi4xNTYyNSkiPjxyZWN0IGNsYXNzPSJiYXNpYyBsYWJlbC1jb250YWluZXIiIHN0eWxlPSIiIHg9Ii01NC40Njg3NSIgeT0iLTI3IiB3aWR0aD0iMTA4LjkzNzUiIGhlaWdodD0iNTQiLz48ZyBjbGFzcz0ibGFiZWwiIHN0eWxlPSIiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yNC40Njg3NSwgLTEyKSI+PHJlY3QvPjxmb3JlaWduT2JqZWN0IHdpZHRoPSI0OC45Mzc1IiBoZWlnaHQ9IjI0Ij48ZGl2IHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hodG1sIiBzdHlsZT0iZGlzcGxheTogdGFibGUtY2VsbDsgd2hpdGUtc3BhY2U6IG5vd3JhcDsgbGluZS1oZWlnaHQ6IDEuNTsgbWF4LXdpZHRoOiAyMDBweDsgdGV4dC1hbGlnbjogY2VudGVyOyI+PHNwYW4gY2xhc3M9Im5vZGVMYWJlbCAiPjxwPkxhcHRvcDwvcD48L3NwYW4+PC9kaXY+PC9mb3JlaWduT2JqZWN0PjwvZz48L2c+PGcgY2xhc3M9Im5vZGUgZGVmYXVsdCAgIiBpZD0iZmxvd2NoYXJ0LUUtNyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjIxLjg1MTU2MjUsIDQ4Mi4xNTYyNSkiPjxyZWN0IGNsYXNzPSJiYXNpYyBsYWJlbC1jb250YWluZXIiIHN0eWxlPSIiIHg9Ii01NC45MTQwNjI1IiB5PSItMjciIHdpZHRoPSIxMDkuODI4MTI1IiBoZWlnaHQ9IjU0Ii8+PGcgY2xhc3M9ImxhYmVsIiBzdHlsZT0iIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjQuOTE0MDYyNSwgLTEyKSI+PHJlY3QvPjxmb3JlaWduT2JqZWN0IHdpZHRoPSI0OS44MjgxMjUiIGhlaWdodD0iMjQiPjxkaXYgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGh0bWwiIHN0eWxlPSJkaXNwbGF5OiB0YWJsZS1jZWxsOyB3aGl0ZS1zcGFjZTogbm93cmFwOyBsaW5lLWhlaWdodDogMS41OyBtYXgtd2lkdGg6IDIwMHB4OyB0ZXh0LWFsaWduOiBjZW50ZXI7Ij48c3BhbiBjbGFzcz0ibm9kZUxhYmVsICI+PHA+aVBob25lPC9wPjwvc3Bhbj48L2Rpdj48L2ZvcmVpZ25PYmplY3Q+PC9nPjwvZz48ZyBjbGFzcz0ibm9kZSBkZWZhdWx0ICAiIGlkPSJmbG93Y2hhcnQtRi05IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgzNzkuODgyODEyNSwgNDgyLjE1NjI1KSI+PHJlY3QgY2xhc3M9ImJhc2ljIGxhYmVsLWNvbnRhaW5lciIgc3R5bGU9IiIgeD0iLTUzLjExNzE4NzUiIHk9Ii0yNyIgd2lkdGg9IjEwNi4yMzQzNzUiIGhlaWdodD0iNTQiLz48ZyBjbGFzcz0ibGFiZWwiIHN0eWxlPSIiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMy4xMTcxODc1LCAtMTIpIj48cmVjdC8+PGZvcmVpZ25PYmplY3Qgd2lkdGg9IjQ2LjIzNDM3NSIgaGVpZ2h0PSIyNCI+PGRpdiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94aHRtbCIgc3R5bGU9ImRpc3BsYXk6IHRhYmxlLWNlbGw7IHdoaXRlLXNwYWNlOiBub3dyYXA7IGxpbmUtaGVpZ2h0OiAxLjU7IG1heC13aWR0aDogMjAwcHg7IHRleHQtYWxpZ246IGNlbnRlcjsiPjxzcGFuIGNsYXNzPSJub2RlTGFiZWwgIj48cD48aSBjbGFzcz0iZmEgZmEtY2FyIj48L2k+IENhcjwvcD48L3NwYW4+PC9kaXY+PC9mb3JlaWduT2JqZWN0PjwvZz48L2c+PC9nPjwvZz48L2c+PC9zdmc+", view.getContent()); + assertEquals("data:image/svg+xml;base64,PHN2ZyBpZD0ibWVybWFpZElua1N2ZyIgd2lkdGg9IjEwMCUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgY2xhc3M9ImZsb3djaGFydCIgc3R5bGU9Im1heC13aWR0aDogNDQxcHg7IiB2aWV3Qm94PSIwIDAgNDQxIDUxNy4xNTYyNSIgcm9sZT0iZ3JhcGhpY3MtZG9jdW1lbnQgZG9jdW1lbnQiIGFyaWEtcm9sZWRlc2NyaXB0aW9uPSJmbG93Y2hhcnQtdjIiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj48c3R5bGUgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGh0bWwiPkBpbXBvcnQgdXJsKCJodHRwczovL2NkbmpzLmNsb3VkZmxhcmUuY29tL2FqYXgvbGlicy9mb250LWF3ZXNvbWUvNi43LjIvY3NzL2FsbC5taW4uY3NzIik7PC9zdHlsZT48c3R5bGU+I21lcm1haWRJbmtTdmd7Zm9udC1mYW1pbHk6InRyZWJ1Y2hldCBtcyIsdmVyZGFuYSxhcmlhbCxzYW5zLXNlcmlmO2ZvbnQtc2l6ZToxNnB4O2ZpbGw6IzMzMzt9QGtleWZyYW1lcyBlZGdlLWFuaW1hdGlvbi1mcmFtZXtmcm9te3N0cm9rZS1kYXNob2Zmc2V0OjA7fX1Aa2V5ZnJhbWVzIGRhc2h7dG97c3Ryb2tlLWRhc2hvZmZzZXQ6MDt9fSNtZXJtYWlkSW5rU3ZnIC5lZGdlLWFuaW1hdGlvbi1zbG93e3N0cm9rZS1kYXNoYXJyYXk6OSw1IWltcG9ydGFudDtzdHJva2UtZGFzaG9mZnNldDo5MDA7YW5pbWF0aW9uOmRhc2ggNTBzIGxpbmVhciBpbmZpbml0ZTtzdHJva2UtbGluZWNhcDpyb3VuZDt9I21lcm1haWRJbmtTdmcgLmVkZ2UtYW5pbWF0aW9uLWZhc3R7c3Ryb2tlLWRhc2hhcnJheTo5LDUhaW1wb3J0YW50O3N0cm9rZS1kYXNob2Zmc2V0OjkwMDthbmltYXRpb246ZGFzaCAyMHMgbGluZWFyIGluZmluaXRlO3N0cm9rZS1saW5lY2FwOnJvdW5kO30jbWVybWFpZElua1N2ZyAuZXJyb3ItaWNvbntmaWxsOiM1NTIyMjI7fSNtZXJtYWlkSW5rU3ZnIC5lcnJvci10ZXh0e2ZpbGw6IzU1MjIyMjtzdHJva2U6IzU1MjIyMjt9I21lcm1haWRJbmtTdmcgLmVkZ2UtdGhpY2tuZXNzLW5vcm1hbHtzdHJva2Utd2lkdGg6MXB4O30jbWVybWFpZElua1N2ZyAuZWRnZS10aGlja25lc3MtdGhpY2t7c3Ryb2tlLXdpZHRoOjMuNXB4O30jbWVybWFpZElua1N2ZyAuZWRnZS1wYXR0ZXJuLXNvbGlke3N0cm9rZS1kYXNoYXJyYXk6MDt9I21lcm1haWRJbmtTdmcgLmVkZ2UtdGhpY2tuZXNzLWludmlzaWJsZXtzdHJva2Utd2lkdGg6MDtmaWxsOm5vbmU7fSNtZXJtYWlkSW5rU3ZnIC5lZGdlLXBhdHRlcm4tZGFzaGVke3N0cm9rZS1kYXNoYXJyYXk6Mzt9I21lcm1haWRJbmtTdmcgLmVkZ2UtcGF0dGVybi1kb3R0ZWR7c3Ryb2tlLWRhc2hhcnJheToyO30jbWVybWFpZElua1N2ZyAubWFya2Vye2ZpbGw6IzMzMzMzMztzdHJva2U6IzMzMzMzMzt9I21lcm1haWRJbmtTdmcgLm1hcmtlci5jcm9zc3tzdHJva2U6IzMzMzMzMzt9I21lcm1haWRJbmtTdmcgc3Zne2ZvbnQtZmFtaWx5OiJ0cmVidWNoZXQgbXMiLHZlcmRhbmEsYXJpYWwsc2Fucy1zZXJpZjtmb250LXNpemU6MTZweDt9I21lcm1haWRJbmtTdmcgcHttYXJnaW46MDt9I21lcm1haWRJbmtTdmcgLmxhYmVse2ZvbnQtZmFtaWx5OiJ0cmVidWNoZXQgbXMiLHZlcmRhbmEsYXJpYWwsc2Fucy1zZXJpZjtjb2xvcjojMzMzO30jbWVybWFpZElua1N2ZyAuY2x1c3Rlci1sYWJlbCB0ZXh0e2ZpbGw6IzMzMzt9I21lcm1haWRJbmtTdmcgLmNsdXN0ZXItbGFiZWwgc3Bhbntjb2xvcjojMzMzO30jbWVybWFpZElua1N2ZyAuY2x1c3Rlci1sYWJlbCBzcGFuIHB7YmFja2dyb3VuZC1jb2xvcjp0cmFuc3BhcmVudDt9I21lcm1haWRJbmtTdmcgLmxhYmVsIHRleHQsI21lcm1haWRJbmtTdmcgc3BhbntmaWxsOiMzMzM7Y29sb3I6IzMzMzt9I21lcm1haWRJbmtTdmcgLm5vZGUgcmVjdCwjbWVybWFpZElua1N2ZyAubm9kZSBjaXJjbGUsI21lcm1haWRJbmtTdmcgLm5vZGUgZWxsaXBzZSwjbWVybWFpZElua1N2ZyAubm9kZSBwb2x5Z29uLCNtZXJtYWlkSW5rU3ZnIC5ub2RlIHBhdGh7ZmlsbDojRUNFQ0ZGO3N0cm9rZTojOTM3MERCO3N0cm9rZS13aWR0aDoxcHg7fSNtZXJtYWlkSW5rU3ZnIC5yb3VnaC1ub2RlIC5sYWJlbCB0ZXh0LCNtZXJtYWlkSW5rU3ZnIC5ub2RlIC5sYWJlbCB0ZXh0LCNtZXJtYWlkSW5rU3ZnIC5pbWFnZS1zaGFwZSAubGFiZWwsI21lcm1haWRJbmtTdmcgLmljb24tc2hhcGUgLmxhYmVse3RleHQtYW5jaG9yOm1pZGRsZTt9I21lcm1haWRJbmtTdmcgLm5vZGUgLmthdGV4IHBhdGh7ZmlsbDojMDAwO3N0cm9rZTojMDAwO3N0cm9rZS13aWR0aDoxcHg7fSNtZXJtYWlkSW5rU3ZnIC5yb3VnaC1ub2RlIC5sYWJlbCwjbWVybWFpZElua1N2ZyAubm9kZSAubGFiZWwsI21lcm1haWRJbmtTdmcgLmltYWdlLXNoYXBlIC5sYWJlbCwjbWVybWFpZElua1N2ZyAuaWNvbi1zaGFwZSAubGFiZWx7dGV4dC1hbGlnbjpjZW50ZXI7fSNtZXJtYWlkSW5rU3ZnIC5ub2RlLmNsaWNrYWJsZXtjdXJzb3I6cG9pbnRlcjt9I21lcm1haWRJbmtTdmcgLnJvb3QgLmFuY2hvciBwYXRoe2ZpbGw6IzMzMzMzMyFpbXBvcnRhbnQ7c3Ryb2tlLXdpZHRoOjA7c3Ryb2tlOiMzMzMzMzM7fSNtZXJtYWlkSW5rU3ZnIC5hcnJvd2hlYWRQYXRoe2ZpbGw6IzMzMzMzMzt9I21lcm1haWRJbmtTdmcgLmVkZ2VQYXRoIC5wYXRoe3N0cm9rZTojMzMzMzMzO3N0cm9rZS13aWR0aDoyLjBweDt9I21lcm1haWRJbmtTdmcgLmZsb3djaGFydC1saW5re3N0cm9rZTojMzMzMzMzO2ZpbGw6bm9uZTt9I21lcm1haWRJbmtTdmcgLmVkZ2VMYWJlbHtiYWNrZ3JvdW5kLWNvbG9yOnJnYmEoMjMyLDIzMiwyMzIsIDAuOCk7dGV4dC1hbGlnbjpjZW50ZXI7fSNtZXJtYWlkSW5rU3ZnIC5lZGdlTGFiZWwgcHtiYWNrZ3JvdW5kLWNvbG9yOnJnYmEoMjMyLDIzMiwyMzIsIDAuOCk7fSNtZXJtYWlkSW5rU3ZnIC5lZGdlTGFiZWwgcmVjdHtvcGFjaXR5OjAuNTtiYWNrZ3JvdW5kLWNvbG9yOnJnYmEoMjMyLDIzMiwyMzIsIDAuOCk7ZmlsbDpyZ2JhKDIzMiwyMzIsMjMyLCAwLjgpO30jbWVybWFpZElua1N2ZyAubGFiZWxCa2d7YmFja2dyb3VuZC1jb2xvcjpyZ2JhKDIzMiwgMjMyLCAyMzIsIDAuNSk7fSNtZXJtYWlkSW5rU3ZnIC5jbHVzdGVyIHJlY3R7ZmlsbDojZmZmZmRlO3N0cm9rZTojYWFhYTMzO3N0cm9rZS13aWR0aDoxcHg7fSNtZXJtYWlkSW5rU3ZnIC5jbHVzdGVyIHRleHR7ZmlsbDojMzMzO30jbWVybWFpZElua1N2ZyAuY2x1c3RlciBzcGFue2NvbG9yOiMzMzM7fSNtZXJtYWlkSW5rU3ZnIGRpdi5tZXJtYWlkVG9vbHRpcHtwb3NpdGlvbjphYnNvbHV0ZTt0ZXh0LWFsaWduOmNlbnRlcjttYXgtd2lkdGg6MjAwcHg7cGFkZGluZzoycHg7Zm9udC1mYW1pbHk6InRyZWJ1Y2hldCBtcyIsdmVyZGFuYSxhcmlhbCxzYW5zLXNlcmlmO2ZvbnQtc2l6ZToxMnB4O2JhY2tncm91bmQ6aHNsKDgwLCAxMDAlLCA5Ni4yNzQ1MDk4MDM5JSk7Ym9yZGVyOjFweCBzb2xpZCAjYWFhYTMzO2JvcmRlci1yYWRpdXM6MnB4O3BvaW50ZXItZXZlbnRzOm5vbmU7ei1pbmRleDoxMDA7fSNtZXJtYWlkSW5rU3ZnIC5mbG93Y2hhcnRUaXRsZVRleHR7dGV4dC1hbmNob3I6bWlkZGxlO2ZvbnQtc2l6ZToxOHB4O2ZpbGw6IzMzMzt9I21lcm1haWRJbmtTdmcgcmVjdC50ZXh0e2ZpbGw6bm9uZTtzdHJva2Utd2lkdGg6MDt9I21lcm1haWRJbmtTdmcgLmljb24tc2hhcGUsI21lcm1haWRJbmtTdmcgLmltYWdlLXNoYXBle2JhY2tncm91bmQtY29sb3I6cmdiYSgyMzIsMjMyLDIzMiwgMC44KTt0ZXh0LWFsaWduOmNlbnRlcjt9I21lcm1haWRJbmtTdmcgLmljb24tc2hhcGUgcCwjbWVybWFpZElua1N2ZyAuaW1hZ2Utc2hhcGUgcHtiYWNrZ3JvdW5kLWNvbG9yOnJnYmEoMjMyLDIzMiwyMzIsIDAuOCk7cGFkZGluZzoycHg7fSNtZXJtYWlkSW5rU3ZnIC5pY29uLXNoYXBlIHJlY3QsI21lcm1haWRJbmtTdmcgLmltYWdlLXNoYXBlIHJlY3R7b3BhY2l0eTowLjU7YmFja2dyb3VuZC1jb2xvcjpyZ2JhKDIzMiwyMzIsMjMyLCAwLjgpO2ZpbGw6cmdiYSgyMzIsMjMyLDIzMiwgMC44KTt9I21lcm1haWRJbmtTdmcgLmxhYmVsLWljb257ZGlzcGxheTppbmxpbmUtYmxvY2s7aGVpZ2h0OjFlbTtvdmVyZmxvdzp2aXNpYmxlO3ZlcnRpY2FsLWFsaWduOi0wLjEyNWVtO30jbWVybWFpZElua1N2ZyAubm9kZSAubGFiZWwtaWNvbiBwYXRoe2ZpbGw6Y3VycmVudENvbG9yO3N0cm9rZTpyZXZlcnQ7c3Ryb2tlLXdpZHRoOnJldmVydDt9I21lcm1haWRJbmtTdmcgOnJvb3R7LS1tZXJtYWlkLWZvbnQtZmFtaWx5OiJ0cmVidWNoZXQgbXMiLHZlcmRhbmEsYXJpYWwsc2Fucy1zZXJpZjt9PC9zdHlsZT48Zz48bWFya2VyIGlkPSJtZXJtYWlkSW5rU3ZnX2Zsb3djaGFydC12Mi1wb2ludEVuZCIgY2xhc3M9Im1hcmtlciBmbG93Y2hhcnQtdjIiIHZpZXdCb3g9IjAgMCAxMCAxMCIgcmVmWD0iNSIgcmVmWT0iNSIgbWFya2VyVW5pdHM9InVzZXJTcGFjZU9uVXNlIiBtYXJrZXJXaWR0aD0iOCIgbWFya2VySGVpZ2h0PSI4IiBvcmllbnQ9ImF1dG8iPjxwYXRoIGQ9Ik0gMCAwIEwgMTAgNSBMIDAgMTAgeiIgY2xhc3M9ImFycm93TWFya2VyUGF0aCIgc3R5bGU9InN0cm9rZS13aWR0aDogMTsgc3Ryb2tlLWRhc2hhcnJheTogMSwgMDsiLz48L21hcmtlcj48bWFya2VyIGlkPSJtZXJtYWlkSW5rU3ZnX2Zsb3djaGFydC12Mi1wb2ludFN0YXJ0IiBjbGFzcz0ibWFya2VyIGZsb3djaGFydC12MiIgdmlld0JveD0iMCAwIDEwIDEwIiByZWZYPSI0LjUiIHJlZlk9IjUiIG1hcmtlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgbWFya2VyV2lkdGg9IjgiIG1hcmtlckhlaWdodD0iOCIgb3JpZW50PSJhdXRvIj48cGF0aCBkPSJNIDAgNSBMIDEwIDEwIEwgMTAgMCB6IiBjbGFzcz0iYXJyb3dNYXJrZXJQYXRoIiBzdHlsZT0ic3Ryb2tlLXdpZHRoOiAxOyBzdHJva2UtZGFzaGFycmF5OiAxLCAwOyIvPjwvbWFya2VyPjxtYXJrZXIgaWQ9Im1lcm1haWRJbmtTdmdfZmxvd2NoYXJ0LXYyLWNpcmNsZUVuZCIgY2xhc3M9Im1hcmtlciBmbG93Y2hhcnQtdjIiIHZpZXdCb3g9IjAgMCAxMCAxMCIgcmVmWD0iMTEiIHJlZlk9IjUiIG1hcmtlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgbWFya2VyV2lkdGg9IjExIiBtYXJrZXJIZWlnaHQ9IjExIiBvcmllbnQ9ImF1dG8iPjxjaXJjbGUgY3g9IjUiIGN5PSI1IiByPSI1IiBjbGFzcz0iYXJyb3dNYXJrZXJQYXRoIiBzdHlsZT0ic3Ryb2tlLXdpZHRoOiAxOyBzdHJva2UtZGFzaGFycmF5OiAxLCAwOyIvPjwvbWFya2VyPjxtYXJrZXIgaWQ9Im1lcm1haWRJbmtTdmdfZmxvd2NoYXJ0LXYyLWNpcmNsZVN0YXJ0IiBjbGFzcz0ibWFya2VyIGZsb3djaGFydC12MiIgdmlld0JveD0iMCAwIDEwIDEwIiByZWZYPSItMSIgcmVmWT0iNSIgbWFya2VyVW5pdHM9InVzZXJTcGFjZU9uVXNlIiBtYXJrZXJXaWR0aD0iMTEiIG1hcmtlckhlaWdodD0iMTEiIG9yaWVudD0iYXV0byI+PGNpcmNsZSBjeD0iNSIgY3k9IjUiIHI9IjUiIGNsYXNzPSJhcnJvd01hcmtlclBhdGgiIHN0eWxlPSJzdHJva2Utd2lkdGg6IDE7IHN0cm9rZS1kYXNoYXJyYXk6IDEsIDA7Ii8+PC9tYXJrZXI+PG1hcmtlciBpZD0ibWVybWFpZElua1N2Z19mbG93Y2hhcnQtdjItY3Jvc3NFbmQiIGNsYXNzPSJtYXJrZXIgY3Jvc3MgZmxvd2NoYXJ0LXYyIiB2aWV3Qm94PSIwIDAgMTEgMTEiIHJlZlg9IjEyIiByZWZZPSI1LjIiIG1hcmtlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgbWFya2VyV2lkdGg9IjExIiBtYXJrZXJIZWlnaHQ9IjExIiBvcmllbnQ9ImF1dG8iPjxwYXRoIGQ9Ik0gMSwxIGwgOSw5IE0gMTAsMSBsIC05LDkiIGNsYXNzPSJhcnJvd01hcmtlclBhdGgiIHN0eWxlPSJzdHJva2Utd2lkdGg6IDI7IHN0cm9rZS1kYXNoYXJyYXk6IDEsIDA7Ii8+PC9tYXJrZXI+PG1hcmtlciBpZD0ibWVybWFpZElua1N2Z19mbG93Y2hhcnQtdjItY3Jvc3NTdGFydCIgY2xhc3M9Im1hcmtlciBjcm9zcyBmbG93Y2hhcnQtdjIiIHZpZXdCb3g9IjAgMCAxMSAxMSIgcmVmWD0iLTEiIHJlZlk9IjUuMiIgbWFya2VyVW5pdHM9InVzZXJTcGFjZU9uVXNlIiBtYXJrZXJXaWR0aD0iMTEiIG1hcmtlckhlaWdodD0iMTEiIG9yaWVudD0iYXV0byI+PHBhdGggZD0iTSAxLDEgbCA5LDkgTSAxMCwxIGwgLTksOSIgY2xhc3M9ImFycm93TWFya2VyUGF0aCIgc3R5bGU9InN0cm9rZS13aWR0aDogMjsgc3Ryb2tlLWRhc2hhcnJheTogMSwgMDsiLz48L21hcmtlcj48ZyBjbGFzcz0icm9vdCI+PGcgY2xhc3M9ImNsdXN0ZXJzIi8+PGcgY2xhc3M9ImVkZ2VQYXRocyI+PHBhdGggZD0iTTIyMS44NTIsNjJMMjIxLjg1Miw2OC4xNjdDMjIxLjg1Miw3NC4zMzMsMjIxLjg1Miw4Ni42NjcsMjIxLjkyNiw5OC40MTdDMjIyLDExMC4xNjcsMjIyLjE0OSwxMjEuMzM0LDIyMi4yMjQsMTI2LjkxN0wyMjIuMjk4LDEzMi41IiBpZD0iTF9BX0JfMCIgY2xhc3M9ImVkZ2UtdGhpY2tuZXNzLW5vcm1hbCBlZGdlLXBhdHRlcm4tc29saWQgZWRnZS10aGlja25lc3Mtbm9ybWFsIGVkZ2UtcGF0dGVybi1zb2xpZCBmbG93Y2hhcnQtbGluayIgc3R5bGU9IjsiIGRhdGEtZWRnZT0idHJ1ZSIgZGF0YS1ldD0iZWRnZSIgZGF0YS1pZD0iTF9BX0JfMCIgZGF0YS1wb2ludHM9Ilczc2llQ0k2TWpJeExqZzFNVFUyTWpVc0lua2lPall5ZlN4N0luZ2lPakl5TVM0NE5URTFOakkxTENKNUlqbzVPWDBzZXlKNElqb3lNakl1TXpVeE5UWXlOU3dpZVNJNk1UTTJMalY5WFE9PSIgbWFya2VyLWVuZD0idXJsKCNtZXJtYWlkSW5rU3ZnX2Zsb3djaGFydC12Mi1wb2ludEVuZCkiLz48cGF0aCBkPSJNMjIyLjM1MiwxOTAuNUwyMjIuMjY4LDE5NC41ODNDMjIyLjE4NSwxOTguNjY3LDIyMi4wMTgsMjA2LjgzMywyMjEuOTM1LDIxNC40MTdDMjIxLjg1MiwyMjIsMjIxLjg1MiwyMjksMjIxLjg1MiwyMzIuNUwyMjEuODUyLDIzNiIgaWQ9IkxfQl9DXzAiIGNsYXNzPSJlZGdlLXRoaWNrbmVzcy1ub3JtYWwgZWRnZS1wYXR0ZXJuLXNvbGlkIGVkZ2UtdGhpY2tuZXNzLW5vcm1hbCBlZGdlLXBhdHRlcm4tc29saWQgZmxvd2NoYXJ0LWxpbmsiIHN0eWxlPSI7IiBkYXRhLWVkZ2U9InRydWUiIGRhdGEtZXQ9ImVkZ2UiIGRhdGEtaWQ9IkxfQl9DXzAiIGRhdGEtcG9pbnRzPSJXM3NpZUNJNk1qSXlMak0xTVRVMk1qVXNJbmtpT2pFNU1DNDFmU3g3SW5naU9qSXlNUzQ0TlRFMU5qSTFMQ0o1SWpveU1UVjlMSHNpZUNJNk1qSXhMamcxTVRVMk1qVXNJbmtpT2pJME1IMWQiIG1hcmtlci1lbmQ9InVybCgjbWVybWFpZElua1N2Z19mbG93Y2hhcnQtdjItcG9pbnRFbmQpIi8+PHBhdGggZD0iTTE3OS43MTUsMzM5LjAxOUwxNjAuMTc0LDM1Mi4yMDlDMTQwLjYzMywzNjUuMzk4LDEwMS41NTEsMzkxLjc3Nyw4Mi4wMSw0MTAuNDY3QzYyLjQ2OSw0MjkuMTU2LDYyLjQ2OSw0NDAuMTU2LDYyLjQ2OSw0NDUuNjU2TDYyLjQ2OSw0NTEuMTU2IiBpZD0iTF9DX0RfMCIgY2xhc3M9ImVkZ2UtdGhpY2tuZXNzLW5vcm1hbCBlZGdlLXBhdHRlcm4tc29saWQgZWRnZS10aGlja25lc3Mtbm9ybWFsIGVkZ2UtcGF0dGVybi1zb2xpZCBmbG93Y2hhcnQtbGluayIgc3R5bGU9IjsiIGRhdGEtZWRnZT0idHJ1ZSIgZGF0YS1ldD0iZWRnZSIgZGF0YS1pZD0iTF9DX0RfMCIgZGF0YS1wb2ludHM9Ilczc2llQ0k2TVRjNUxqY3hORFV6TmpFeU9EazFORE0yTENKNUlqb3pNemt1TURFNU1qSXpOakk0T1RVME16WjlMSHNpZUNJNk5qSXVORFk0TnpVc0lua2lPalF4T0M0eE5UWXlOWDBzZXlKNElqbzJNaTQwTmpnM05Td2llU0k2TkRVMUxqRTFOakkxZlYwPSIgbWFya2VyLWVuZD0idXJsKCNtZXJtYWlkSW5rU3ZnX2Zsb3djaGFydC12Mi1wb2ludEVuZCkiLz48cGF0aCBkPSJNMjIxLjg1MiwzODEuMTU2TDIyMS44NTIsMzg3LjMyM0MyMjEuODUyLDM5My40OSwyMjEuODUyLDQwNS44MjMsMjIxLjg1Miw0MTcuNDlDMjIxLjg1Miw0MjkuMTU2LDIyMS44NTIsNDQwLjE1NiwyMjEuODUyLDQ0NS42NTZMMjIxLjg1Miw0NTEuMTU2IiBpZD0iTF9DX0VfMCIgY2xhc3M9ImVkZ2UtdGhpY2tuZXNzLW5vcm1hbCBlZGdlLXBhdHRlcm4tc29saWQgZWRnZS10aGlja25lc3Mtbm9ybWFsIGVkZ2UtcGF0dGVybi1zb2xpZCBmbG93Y2hhcnQtbGluayIgc3R5bGU9IjsiIGRhdGEtZWRnZT0idHJ1ZSIgZGF0YS1ldD0iZWRnZSIgZGF0YS1pZD0iTF9DX0VfMCIgZGF0YS1wb2ludHM9Ilczc2llQ0k2TWpJeExqZzFNVFUyTWpVc0lua2lPak00TVM0eE5UWXlOWDBzZXlKNElqb3lNakV1T0RVeE5UWXlOU3dpZVNJNk5ERTRMakUxTmpJMWZTeDdJbmdpT2pJeU1TNDROVEUxTmpJMUxDSjVJam8wTlRVdU1UVTJNalY5WFE9PSIgbWFya2VyLWVuZD0idXJsKCNtZXJtYWlkSW5rU3ZnX2Zsb3djaGFydC12Mi1wb2ludEVuZCkiLz48cGF0aCBkPSJNMjYzLjg0NCwzMzkuMTY0TDI4My4xODQsMzUyLjMyOUMzMDIuNTI0LDM2NS40OTUsMzQxLjIwMywzOTEuODI1LDM2MC41NDMsNDEwLjQ5MUMzNzkuODgzLDQyOS4xNTYsMzc5Ljg4Myw0NDAuMTU2LDM3OS44ODMsNDQ1LjY1NkwzNzkuODgzLDQ1MS4xNTYiIGlkPSJMX0NfRl8wIiBjbGFzcz0iZWRnZS10aGlja25lc3Mtbm9ybWFsIGVkZ2UtcGF0dGVybi1zb2xpZCBlZGdlLXRoaWNrbmVzcy1ub3JtYWwgZWRnZS1wYXR0ZXJuLXNvbGlkIGZsb3djaGFydC1saW5rIiBzdHlsZT0iOyIgZGF0YS1lZGdlPSJ0cnVlIiBkYXRhLWV0PSJlZGdlIiBkYXRhLWlkPSJMX0NfRl8wIiBkYXRhLXBvaW50cz0iVzNzaWVDSTZNall6TGpnME16ZzJOVE0xTmpBMU1ETTBMQ0o1SWpvek16a3VNVFl6T1RRM01UUXpPVFE1TmpaOUxIc2llQ0k2TXpjNUxqZzRNamd4TWpVc0lua2lPalF4T0M0eE5UWXlOWDBzZXlKNElqb3pOemt1T0RneU9ERXlOU3dpZVNJNk5EVTFMakUxTmpJMWZWMD0iIG1hcmtlci1lbmQ9InVybCgjbWVybWFpZElua1N2Z19mbG93Y2hhcnQtdjItcG9pbnRFbmQpIi8+PC9nPjxnIGNsYXNzPSJlZGdlTGFiZWxzIj48ZyBjbGFzcz0iZWRnZUxhYmVsIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyMjEuODUxNTYyNSwgOTkpIj48ZyBjbGFzcz0ibGFiZWwiIGRhdGEtaWQ9IkxfQV9CXzAiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0zOS4xMzI4MTI1LCAtMTIpIj48Zm9yZWlnbk9iamVjdCB3aWR0aD0iNzguMjY1NjI1IiBoZWlnaHQ9IjI0Ij48ZGl2IHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hodG1sIiBjbGFzcz0ibGFiZWxCa2ciIHN0eWxlPSJkaXNwbGF5OiB0YWJsZS1jZWxsOyB3aGl0ZS1zcGFjZTogbm93cmFwOyBsaW5lLWhlaWdodDogMS41OyBtYXgtd2lkdGg6IDIwMHB4OyB0ZXh0LWFsaWduOiBjZW50ZXI7Ij48c3BhbiBjbGFzcz0iZWRnZUxhYmVsIj48cD5HZXQgbW9uZXk8L3A+PC9zcGFuPjwvZGl2PjwvZm9yZWlnbk9iamVjdD48L2c+PC9nPjxnIGNsYXNzPSJlZGdlTGFiZWwiPjxnIGNsYXNzPSJsYWJlbCIgZGF0YS1pZD0iTF9CX0NfMCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCwgMCkiPjxmb3JlaWduT2JqZWN0IHdpZHRoPSIwIiBoZWlnaHQ9IjAiPjxkaXYgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGh0bWwiIGNsYXNzPSJsYWJlbEJrZyIgc3R5bGU9ImRpc3BsYXk6IHRhYmxlLWNlbGw7IHdoaXRlLXNwYWNlOiBub3dyYXA7IGxpbmUtaGVpZ2h0OiAxLjU7IG1heC13aWR0aDogMjAwcHg7IHRleHQtYWxpZ246IGNlbnRlcjsiPjxzcGFuIGNsYXNzPSJlZGdlTGFiZWwiPjwvc3Bhbj48L2Rpdj48L2ZvcmVpZ25PYmplY3Q+PC9nPjwvZz48ZyBjbGFzcz0iZWRnZUxhYmVsIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg2Mi40Njg3NSwgNDE4LjE1NjI1KSI+PGcgY2xhc3M9ImxhYmVsIiBkYXRhLWlkPSJMX0NfRF8wIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMTUuMTI1LCAtMTIpIj48Zm9yZWlnbk9iamVjdCB3aWR0aD0iMzAuMjUiIGhlaWdodD0iMjQiPjxkaXYgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGh0bWwiIGNsYXNzPSJsYWJlbEJrZyIgc3R5bGU9ImRpc3BsYXk6IHRhYmxlLWNlbGw7IHdoaXRlLXNwYWNlOiBub3dyYXA7IGxpbmUtaGVpZ2h0OiAxLjU7IG1heC13aWR0aDogMjAwcHg7IHRleHQtYWxpZ246IGNlbnRlcjsiPjxzcGFuIGNsYXNzPSJlZGdlTGFiZWwiPjxwPk9uZTwvcD48L3NwYW4+PC9kaXY+PC9mb3JlaWduT2JqZWN0PjwvZz48L2c+PGcgY2xhc3M9ImVkZ2VMYWJlbCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjIxLjg1MTU2MjUsIDQxOC4xNTYyNSkiPjxnIGNsYXNzPSJsYWJlbCIgZGF0YS1pZD0iTF9DX0VfMCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTE0LjY3MTg3NSwgLTEyKSI+PGZvcmVpZ25PYmplY3Qgd2lkdGg9IjI5LjM0Mzc1IiBoZWlnaHQ9IjI0Ij48ZGl2IHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hodG1sIiBjbGFzcz0ibGFiZWxCa2ciIHN0eWxlPSJkaXNwbGF5OiB0YWJsZS1jZWxsOyB3aGl0ZS1zcGFjZTogbm93cmFwOyBsaW5lLWhlaWdodDogMS41OyBtYXgtd2lkdGg6IDIwMHB4OyB0ZXh0LWFsaWduOiBjZW50ZXI7Ij48c3BhbiBjbGFzcz0iZWRnZUxhYmVsIj48cD5Ud288L3A+PC9zcGFuPjwvZGl2PjwvZm9yZWlnbk9iamVjdD48L2c+PC9nPjxnIGNsYXNzPSJlZGdlTGFiZWwiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDM3OS44ODI4MTI1LCA0MTguMTU2MjUpIj48ZyBjbGFzcz0ibGFiZWwiIGRhdGEtaWQ9IkxfQ19GXzAiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMC44OTg0Mzc1LCAtMTIpIj48Zm9yZWlnbk9iamVjdCB3aWR0aD0iNDEuNzk2ODc1IiBoZWlnaHQ9IjI0Ij48ZGl2IHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hodG1sIiBjbGFzcz0ibGFiZWxCa2ciIHN0eWxlPSJkaXNwbGF5OiB0YWJsZS1jZWxsOyB3aGl0ZS1zcGFjZTogbm93cmFwOyBsaW5lLWhlaWdodDogMS41OyBtYXgtd2lkdGg6IDIwMHB4OyB0ZXh0LWFsaWduOiBjZW50ZXI7Ij48c3BhbiBjbGFzcz0iZWRnZUxhYmVsIj48cD5UaHJlZTwvcD48L3NwYW4+PC9kaXY+PC9mb3JlaWduT2JqZWN0PjwvZz48L2c+PC9nPjxnIGNsYXNzPSJub2RlcyI+PGcgY2xhc3M9Im5vZGUgZGVmYXVsdCIgaWQ9ImZsb3djaGFydC1BLTAiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDIyMS44NTE1NjI1LCAzNSkiPjxyZWN0IGNsYXNzPSJiYXNpYyBsYWJlbC1jb250YWluZXIiIHN0eWxlPSIiIHg9Ii02Ni4wMDc4MTI1IiB5PSItMjciIHdpZHRoPSIxMzIuMDE1NjI1IiBoZWlnaHQ9IjU0Ii8+PGcgY2xhc3M9ImxhYmVsIiBzdHlsZT0iIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMzYuMDA3ODEyNSwgLTEyKSI+PHJlY3QvPjxmb3JlaWduT2JqZWN0IHdpZHRoPSI3Mi4wMTU2MjUiIGhlaWdodD0iMjQiPjxkaXYgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGh0bWwiIHN0eWxlPSJkaXNwbGF5OiB0YWJsZS1jZWxsOyB3aGl0ZS1zcGFjZTogbm93cmFwOyBsaW5lLWhlaWdodDogMS41OyBtYXgtd2lkdGg6IDIwMHB4OyB0ZXh0LWFsaWduOiBjZW50ZXI7Ij48c3BhbiBjbGFzcz0ibm9kZUxhYmVsIj48cD5DaHJpc3RtYXM8L3A+PC9zcGFuPjwvZGl2PjwvZm9yZWlnbk9iamVjdD48L2c+PC9nPjxnIGNsYXNzPSJub2RlIGRlZmF1bHQiIGlkPSJmbG93Y2hhcnQtQi0xIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyMjEuODUxNTYyNSwgMTYzKSI+PGcgY2xhc3M9ImJhc2ljIGxhYmVsLWNvbnRhaW5lciBvdXRlci1wYXRoIj48cGF0aCBkPSJNLTU1LjM2NzE4NzUgLTI3IEMtMjYuMDY1Mjk0MzIwMjQwNjMyIC0yNywgMy4yMzY1OTg4NTk1MTg3MzYgLTI3LCA1NS4zNjcxODc1IC0yNyBDNTUuMzY3MTg3NSAtMjcsIDU1LjM2NzE4NzUgLTI3LCA1NS4zNjcxODc1IC0yNyBDNTUuNTI3NDU4NTE3MTQzNjYgLTI2Ljk5MzM3MTE0MTY5NzEsIDU1LjY4NzcyOTUzNDI4NzMxNCAtMjYuOTg2NzQyMjgzMzk0MiwgNTUuNzgwMDg0MjI3MzYxNjYgLTI2Ljk4MjkyMjQ2NTAzMzM0NyBDNTUuODkxMzQ1NjE0NTE2ODIgLTI2Ljk2OTA1Mzc0NzU4NzY3NiwgNTYuMDAyNjA3MDAxNjcxOTggLTI2Ljk1NTE4NTAzMDE0MjAwNSwgNTYuMTkwMTYwNDUxNDAzNjcgLTI2LjkzMTgwNjUxNzAxMzYxMiBDNTYuMzE5NTkzOTgwMDgxMjE2IC0yNi45MDQ2NjcxNjAwMDE0NTYsIDU2LjQ0OTAyNzUwODc1ODc2IC0yNi44Nzc1Mjc4MDI5ODkzLCA1Ni41OTQ2MTQ5MzU3MDM5OTQgLTI2Ljg0NzAwMTMyOTY5NjY1MyBDNTYuNzQwOTk1NDAxMzg2MzE0IC0yNi44MDM0MjE5NzY5MDY3MTYsIDU2Ljg4NzM3NTg2NzA2ODY0IC0yNi43NTk4NDI2MjQxMTY3OCwgNTYuOTkwNjg0ODQ2MDIzNDIgLTI2LjcyOTA4NjIwODUwMzE3MyBDNTcuMTEyMjk4NDc0NzQ4ODA1IC0yNi42ODE2MzI0MjMzMjAwOCwgNTcuMjMzOTEyMTAzNDc0MTkgLTI2LjYzNDE3ODYzODEzNjk4NywgNTcuMzc1NjY0NjIzMjY0ODQ2IC0yNi41Nzg4NjY2MzMyNzUyODYgQzU3LjQ2MTAwMTkxNDY0NzY5NiAtMjYuNTM3MTQ3NzczNjMzNjk1LCA1Ny41NDYzMzkyMDYwMzA1NSAtMjYuNDk1NDI4OTEzOTkyMTA0LCA1Ny43NDY5MjQ0NjUxODUzNjYgLTI2LjM5NzM2ODc1NjAzMjQ0NiBDNTcuODI3MDU1Nzc2NTMzNzMgLTI2LjM0OTYyMDg1ODQzNTg4OCwgNTcuOTA3MTg3MDg3ODgyMSAtMjYuMzAxODcyOTYwODM5MzI2LCA1OC4xMDE5MjgyOTA2MTIxMzYgLTI2LjE4NTgzMjM5MTMxMjY0NCBDNTguMTgwMzI0ODY5NzgwOTIgLTI2LjEyOTg1ODMwOTY0OTY1MiwgNTguMjU4NzIxNDQ4OTQ5NyAtMjYuMDczODg0MjI3OTg2NjYsIDU4LjQzODI1MTA2MzQ0ODM0IC0yNS45NDU3MDI1NDY5ODE5NyBDNTguNTM1NzA1MzU2OTYyNyAtMjUuODYzMTYyOTcwNzQyMDk2LCA1OC42MzMxNTk2NTA0NzcwNyAtMjUuNzgwNjIzMzk0NTAyMjI1LCA1OC43NTM1OTUzNTgxMjg3MDYgLTI1LjY3ODYxOTU1MzM2NTY1NyBDNTguODYzMzIyODE4MzY4NTk0IC0yNS41Njg4OTIwOTMxMjU3NywgNTguOTczMDUwMjc4NjA4NDggLTI1LjQ1OTE2NDYzMjg4NTg3NywgNTkuMDQ1ODA3MDUzMzY1NjYgLTI1LjM4NjQwNzg1ODEyODcwNiBDNTkuMTA2Njk3ODUwNDczMzUgLTI1LjMxNDUxNDIyOTgzNTYzOCwgNTkuMTY3NTg4NjQ3NTgxMDUgLTI1LjI0MjYyMDYwMTU0MjU2NiwgNTkuMzEyODkwMDQ2OTgxOTcgLTI1LjA3MTA2MzU2MzQ0ODM0IEM1OS4zNjUxNDAxOTEyNTc5MTUgLTI0Ljk5Nzg4MjY4MzEwODU0NiwgNTkuNDE3MzkwMzM1NTMzODUgLTI0LjkyNDcwMTgwMjc2ODc1NSwgNTkuNTUzMDE5ODkxMzEyNjQ0IC0yNC43MzQ3NDA3OTA2MTIxMzYgQzU5LjYxOTI0MDc1ODU2Mzk0IC0yNC42MjM2MDc4MzYxMDM2OSwgNTkuNjg1NDYxNjI1ODE1MjM2IC0yNC41MTI0NzQ4ODE1OTUyNSwgNTkuNzY0NTU2MjU2MDMyNDUgLTI0LjM3OTczNjk2NTE4NTM3IEM1OS44MTU3NjcyMzk5NzU5NSAtMjQuMjc0OTgzMjIzMjAwNzI4LCA1OS44NjY5NzgyMjM5MTk0NCAtMjQuMTcwMjI5NDgxMjE2MDg2LCA1OS45NDYwNTQxMzMyNzUyOSAtMjQuMDA4NDc3MTIzMjY0ODQ2IEM1OS45OTg4NTkxODcyNjUzNDYgLTIzLjg3MzE0OTM2NzkzNDE5NywgNjAuMDUxNjY0MjQxMjU1NCAtMjMuNzM3ODIxNjEyNjAzNTQ3LCA2MC4wOTYyNzM3MDg1MDMxNzYgLTIzLjYyMzQ5NzM0NjAyMzQxNyBDNjAuMTM0OTI0MjY5NzY5MjM2IC0yMy40OTM2NzIzOTkyMzU0MiwgNjAuMTczNTc0ODMxMDM1MjkgLTIzLjM2Mzg0NzQ1MjQ0NzQzLCA2MC4yMTQxODg4Mjk2OTY2NSAtMjMuMjI3NDI3NDM1NzAzOTk0IEM2MC4yMzQ1MzcxMTQ3NDM5NiAtMjMuMTMwMzgyMDE0MzYxNDg0LCA2MC4yNTQ4ODUzOTk3OTEyNyAtMjMuMDMzMzM2NTkzMDE4OTcsIDYwLjI5ODk5NDAxNzAxMzYxIC0yMi44MjI5NzI5NTE0MDM2NyBDNjAuMzEzNTQyMjIzMTI3MDk0IC0yMi43MDYyNjAzODU5Mzc4NDYsIDYwLjMyODA5MDQyOTI0MDU3NSAtMjIuNTg5NTQ3ODIwNDcyMDIsIDYwLjM1MDEwOTk2NTAzMzM1IC0yMi40MTI4OTY3MjczNjE2NjIgQzYwLjM1NTgwNzMzMzM0MzE3IC0yMi4yNzUxNDcwNjE4MzM1MzYsIDYwLjM2MTUwNDcwMTY1MyAtMjIuMTM3Mzk3Mzk2MzA1NDA2LCA2MC4zNjcxODc1IC0yMiBDNjAuMzY3MTg3NSAtMjIsIDYwLjM2NzE4NzUgLTIyLCA2MC4zNjcxODc1IC0yMiBDNjAuMzY3MTg3NSAtNi42NDk0ODg2MDMwNTIwNjcsIDYwLjM2NzE4NzUgOC43MDEwMjI3OTM4OTU4NjYsIDYwLjM2NzE4NzUgMjIgQzYwLjM2NzE4NzUgMjIsIDYwLjM2NzE4NzUgMjIsIDYwLjM2NzE4NzUgMjIgQzYwLjM2MjUyMjUzMzM0NzkzIDIyLjExMjc4ODQ5NDgwNjM2NywgNjAuMzU3ODU3NTY2Njk1ODU2IDIyLjIyNTU3Njk4OTYxMjczMywgNjAuMzUwMTA5OTY1MDMzMzUgMjIuNDEyODk2NzI3MzYxNjYyIEM2MC4zMzY1MzQ4NDg4NDM0MTQgMjIuNTIxODAyNzA2ODgwMDI0LCA2MC4zMjI5NTk3MzI2NTM0OCAyMi42MzA3MDg2ODYzOTgzODYsIDYwLjI5ODk5NDAxNzAxMzYxIDIyLjgyMjk3Mjk1MTQwMzY3IEM2MC4yNzQyOTgzNzYzMjY0MSAyMi45NDA3NTE4NjI1MTI5MDQsIDYwLjI0OTYwMjczNTYzOTIyIDIzLjA1ODUzMDc3MzYyMjE0LCA2MC4yMTQxODg4Mjk2OTY2NSAyMy4yMjc0Mjc0MzU3MDM5OTQgQzYwLjE3OTUwMDUyMzQ4NjE0IDIzLjM0Mzk0MzQwMjM2NjAzNywgNjAuMTQ0ODEyMjE3Mjc1NjMgMjMuNDYwNDU5MzY5MDI4MDc2LCA2MC4wOTYyNzM3MDg1MDMxNzYgMjMuNjIzNDk3MzQ2MDIzNDE3IEM2MC4wNjQ2MTI5OTA1NTA2NCAyMy43MDQ2MzY4MTIyMDkzNTUsIDYwLjAzMjk1MjI3MjU5ODExNiAyMy43ODU3NzYyNzgzOTUyOTMsIDU5Ljk0NjA1NDEzMzI3NTI5IDI0LjAwODQ3NzEyMzI2NDg0NiBDNTkuODkzOTA4ODA3NDAzMDQgMjQuMTE1MTQyMDkyMjEyODAzLCA1OS44NDE3NjM0ODE1MzA3NzYgMjQuMjIxODA3MDYxMTYwNzY0LCA1OS43NjQ1NTYyNTYwMzI0NSAyNC4zNzk3MzY5NjUxODUzNjYgQzU5LjY5NDUxMjQ5Njg0OTQ5IDI0LjQ5NzI4NTU2MDE1MjI3OCwgNTkuNjI0NDY4NzM3NjY2NTMgMjQuNjE0ODM0MTU1MTE5MTksIDU5LjU1MzAxOTg5MTMxMjY0NCAyNC43MzQ3NDA3OTA2MTIxMzMgQzU5LjQ4MjUwNTgwODI3MjU1NiAyNC44MzM1MDE5MDg3ODE5NjUsIDU5LjQxMTk5MTcyNTIzMjQ2IDI0LjkzMjI2MzAyNjk1MTgsIDU5LjMxMjg5MDA0Njk4MTk3IDI1LjA3MTA2MzU2MzQ0ODM0IEM1OS4yMjQwNjg5NTQxNCAyNS4xNzU5MzQ0Mjk2MDAyNiwgNTkuMTM1MjQ3ODYxMjk4MDI2IDI1LjI4MDgwNTI5NTc1MjE4LCA1OS4wNDU4MDcwNTMzNjU2NiAyNS4zODY0MDc4NTgxMjg3MDYgQzU4Ljk0MTgzMTU4ODIzMjAxIDI1LjQ5MDM4MzMyMzI2MjM1LCA1OC44Mzc4NTYxMjMwOTgzNyAyNS41OTQzNTg3ODgzOTU5OTQsIDU4Ljc1MzU5NTM1ODEyODcwNiAyNS42Nzg2MTk1NTMzNjU2NTcgQzU4LjY3MjY5NTcxMDc4NTMgMjUuNzQ3MTM4MDU5NTE3NDI1LCA1OC41OTE3OTYwNjM0NDE4OSAyNS44MTU2NTY1NjU2NjkxOSwgNTguNDM4MjUxMDYzNDQ4MzQgMjUuOTQ1NzAyNTQ2OTgxOTcgQzU4LjMzNDg0MzY4NjgyNjQ1IDI2LjAxOTUzMzk5NDc5NDYwMywgNTguMjMxNDM2MzEwMjA0NTY1IDI2LjA5MzM2NTQ0MjYwNzI0LCA1OC4xMDE5MjgyOTA2MTIxMzYgMjYuMTg1ODMyMzkxMzEyNjQ0IEM1OC4wMTQwNDc1MzI0OTY5NzQgMjYuMjM4MTk3OTU2ODg5NzMsIDU3LjkyNjE2Njc3NDM4MTgxIDI2LjI5MDU2MzUyMjQ2NjgxNSwgNTcuNzQ2OTI0NDY1MTg1MzY2IDI2LjM5NzM2ODc1NjAzMjQ0NiBDNTcuNjUzMTQ5MzQwNzQ3NDA0IDI2LjQ0MzIxMjYyMDg5OTc4MiwgNTcuNTU5Mzc0MjE2MzA5NDUgMjYuNDg5MDU2NDg1NzY3MTE1LCA1Ny4zNzU2NjQ2MjMyNjQ4NDYgMjYuNTc4ODY2NjMzMjc1Mjg2IEM1Ny4yODc4NjgyMjQ3Mzg3MSAyNi42MTMxMjQ4OTQyODc0MywgNTcuMjAwMDcxODI2MjEyNTcgMjYuNjQ3MzgzMTU1Mjk5NTgsIDU2Ljk5MDY4NDg0NjAyMzQyIDI2LjcyOTA4NjIwODUwMzE3MyBDNTYuODYxMTUxMTkyMjAxMDE0IDI2Ljc2NzY1MDA0ODA5NTk5OCwgNTYuNzMxNjE3NTM4Mzc4NjE1IDI2LjgwNjIxMzg4NzY4ODgyMywgNTYuNTk0NjE0OTM1NzAzOTk0IDI2Ljg0NzAwMTMyOTY5NjY1MyBDNTYuNTA1NzcwMjgzMjAxMzY1IDI2Ljg2NTYzMDA5NDM0MTUzLCA1Ni40MTY5MjU2MzA2OTg3MzUgMjYuODg0MjU4ODU4OTg2NDA2LCA1Ni4xOTAxNjA0NTE0MDM2NyAyNi45MzE4MDY1MTcwMTM2MTIgQzU2LjA3NzExMTk1NjE5OTggMjYuOTQ1ODk3OTk3MjQ1MjQzLCA1NS45NjQwNjM0NjA5OTU5MiAyNi45NTk5ODk0Nzc0NzY4NzgsIDU1Ljc4MDA4NDIyNzM2MTY2IDI2Ljk4MjkyMjQ2NTAzMzM0NyBDNTUuNjgzOTkwODYyNzI0NzA2IDI2Ljk4Njg5NjkxNTk5Mzg1LCA1NS41ODc4OTc0OTgwODc3NSAyNi45OTA4NzEzNjY5NTQzNSwgNTUuMzY3MTg3NSAyNyBDNTUuMzY3MTg3NSAyNywgNTUuMzY3MTg3NSAyNywgNTUuMzY3MTg3NSAyNyBDMjUuNjQ2MTg2Mzc2MDY1NzkgMjcsIC00LjA3NDgxNDc0Nzg2ODQyMyAyNywgLTU1LjM2NzE4NzUgMjcgQy01NS4zNjcxODc1IDI3LCAtNTUuMzY3MTg3NSAyNywgLTU1LjM2NzE4NzUgMjcgQy01NS40ODI5NTI2OTQwMzAwMiAyNi45OTUyMTE5MTYxNTczMzgsIC01NS41OTg3MTc4ODgwNjAwNDQgMjYuOTkwNDIzODMyMzE0NjcyLCAtNTUuNzgwMDg0MjI3MzYxNjYgMjYuOTgyOTIyNDY1MDMzMzQ3IEMtNTUuOTExODU3MzEyMjEzNzYgMjYuOTY2NDk2OTY3MTM2MTMsIC01Ni4wNDM2MzAzOTcwNjU4NiAyNi45NTAwNzE0NjkyMzg5MTcsIC01Ni4xOTAxNjA0NTE0MDM2NyAyNi45MzE4MDY1MTcwMTM2MTIgQy01Ni4zNDY1MzAzMDI5MTMzNDQgMjYuODk5MDE5MjA3MDM3NDUsIC01Ni41MDI5MDAxNTQ0MjMwMSAyNi44NjYyMzE4OTcwNjEyOTQsIC01Ni41OTQ2MTQ5MzU3MDM5OTQgMjYuODQ3MDAxMzI5Njk2NjUzIEMtNTYuNjc3NTUyNjk3NDYzNDkgMjYuODIyMzA5Njg4MjA5Njc1LCAtNTYuNzYwNDkwNDU5MjIyOTkgMjYuNzk3NjE4MDQ2NzIyNjkzLCAtNTYuOTkwNjg0ODQ2MDIzNDIgMjYuNzI5MDg2MjA4NTAzMTczIEMtNTcuMTA1MDIwMjYwNDY5MSAyNi42ODQ0NzIzOTEzNDQ3NjIsIC01Ny4yMTkzNTU2NzQ5MTQ3NyAyNi42Mzk4NTg1NzQxODYzNTYsIC01Ny4zNzU2NjQ2MjMyNjQ4NDYgMjYuNTc4ODY2NjMzMjc1Mjg2IEMtNTcuNDg3NTAzNDYyNjE4NTQgMjYuNTI0MTkxOTU2Mjg2MjcsIC01Ny41OTkzNDIzMDE5NzIyMyAyNi40Njk1MTcyNzkyOTcyNSwgLTU3Ljc0NjkyNDQ2NTE4NTM2NiAyNi4zOTczNjg3NTYwMzI0NDYgQy01Ny44ODg1MTU4NzU5MTI1MyAyNi4zMTI5OTg1ODgyODkxMzcsIC01OC4wMzAxMDcyODY2Mzk3IDI2LjIyODYyODQyMDU0NTgyNCwgLTU4LjEwMTkyODI5MDYxMjEzNiAyNi4xODU4MzIzOTEzMTI2NDQgQy01OC4xODcwOTIyODg5MTA0MiAyNi4xMjUwMjY0NjUyNjI4MzUsIC01OC4yNzIyNTYyODcyMDg3MSAyNi4wNjQyMjA1MzkyMTMwMiwgLTU4LjQzODI1MTA2MzQ0ODM0IDI1Ljk0NTcwMjU0Njk4MTk3IEMtNTguNTMyNzg4OTAyMzM1NzMgMjUuODY1NjMzMDgxODEwMDkyLCAtNTguNjI3MzI2NzQxMjIzMTE1IDI1Ljc4NTU2MzYxNjYzODIxNCwgLTU4Ljc1MzU5NTM1ODEyODcwNiAyNS42Nzg2MTk1NTMzNjU2NiBDLTU4LjgzOTAxNTE3ODQxNzEyIDI1LjU5MzE5OTczMzA3NzI0MiwgLTU4LjkyNDQzNDk5ODcwNTUzNiAyNS41MDc3Nzk5MTI3ODg4MjcsIC01OS4wNDU4MDcwNTMzNjU2NiAyNS4zODY0MDc4NTgxMjg3MDYgQy01OS4xMTIzNjYyNzI3NjY0OCAyNS4zMDc4MjE1MzYyODA4MiwgLTU5LjE3ODkyNTQ5MjE2NzMxIDI1LjIyOTIzNTIxNDQzMjkzLCAtNTkuMzEyODkwMDQ2OTgxOTcgMjUuMDcxMDYzNTYzNDQ4MzQgQy01OS4zOTQwNjMxNzM1MjIwOSAyNC45NTczNzM1MjU5ODYwOTIsIC01OS40NzUyMzYzMDAwNjIyMTQgMjQuODQzNjgzNDg4NTIzODQ0LCAtNTkuNTUzMDE5ODkxMzEyNjQ0IDI0LjczNDc0MDc5MDYxMjEzMyBDLTU5LjYwNjgwNjk3NTc1MDA3IDI0LjY0NDQ3NDQxNjAzODA1LCAtNTkuNjYwNTk0MDYwMTg3NDg1IDI0LjU1NDIwODA0MTQ2Mzk3LCAtNTkuNzY0NTU2MjU2MDMyNDQgMjQuMzc5NzM2OTY1MTg1MzcgQy01OS44MjA2MDkxNTU3NjY5NSAyNC4yNjUwNzg5MjYxNDUxNzYsIC01OS44NzY2NjIwNTU1MDE0NyAyNC4xNTA0MjA4ODcxMDQ5ODIsIC01OS45NDYwNTQxMzMyNzUyOCAyNC4wMDg0NzcxMjMyNjQ4NSBDLTU5Ljk3NzQ3MjU1ODYzOCAyMy45Mjc5NTg1OTk3Njk3MTcsIC02MC4wMDg4OTA5ODQwMDA3MiAyMy44NDc0NDAwNzYyNzQ1ODQsIC02MC4wOTYyNzM3MDg1MDMxNzYgMjMuNjIzNDk3MzQ2MDIzNDE3IEMtNjAuMTMwMjQ0OTA1NjIxOTUgMjMuNTA5MzkwMTA2MzQwNCwgLTYwLjE2NDIxNjEwMjc0MDcyNCAyMy4zOTUyODI4NjY2NTczODIsIC02MC4yMTQxODg4Mjk2OTY2NSAyMy4yMjc0Mjc0MzU3MDM5OTQgQy02MC4yMzc0MTA1NTU4MDk5MyAyMy4xMTY2Nzc5NDU1MjcxLCAtNjAuMjYwNjMyMjgxOTIzMjEgMjMuMDA1OTI4NDU1MzUwMjA4LCAtNjAuMjk4OTk0MDE3MDEzNjEgMjIuODIyOTcyOTUxNDAzNjcgQy02MC4zMTEyODU5OTM2NDg4NSAyMi43MjQzNjA5MjIwODg1NTcsIC02MC4zMjM1Nzc5NzAyODQwOSAyMi42MjU3NDg4OTI3NzM0NDQsIC02MC4zNTAxMDk5NjUwMzMzNSAyMi40MTI4OTY3MjczNjE2NjIgQy02MC4zNTY0NzgyMjYxMDYwNiAyMi4yNTg5MjYzNzAzMzc1MTIsIC02MC4zNjI4NDY0ODcxNzg3NyAyMi4xMDQ5NTYwMTMzMTMzNjYsIC02MC4zNjcxODc1IDIyIEMtNjAuMzY3MTg3NSAyMiwgLTYwLjM2NzE4NzUgMjIsIC02MC4zNjcxODc1IDIyIEMtNjAuMzY3MTg3NSAxMi43MTU1Mjg2MTg3NDA0MzIsIC02MC4zNjcxODc1IDMuNDMxMDU3MjM3NDgwODY0MywgLTYwLjM2NzE4NzUgLTIyIEMtNjAuMzY3MTg3NSAtMjIsIC02MC4zNjcxODc1IC0yMiwgLTYwLjM2NzE4NzUgLTIyIEMtNjAuMzYzNjIyODk5MDY0MDMgLTIyLjA4NjE4NDEwNDY1NDY3NywgLTYwLjM2MDA1ODI5ODEyODA3NCAtMjIuMTcyMzY4MjA5MzA5MzU0LCAtNjAuMzUwMTA5OTY1MDMzMzUgLTIyLjQxMjg5NjcyNzM2MTY2IEMtNjAuMzM4MDM3MDc1MTY5NTk1IC0yMi41MDk3NTExMzkzNDg1MTMsIC02MC4zMjU5NjQxODUzMDU4NCAtMjIuNjA2NjA1NTUxMzM1MzcsIC02MC4yOTg5OTQwMTcwMTM2MSAtMjIuODIyOTcyOTUxNDAzNjcgQy02MC4yNzkwMTM0MTUxOTg1MzQgLTIyLjkxODI2NDgxMTAwMzI4LCAtNjAuMjU5MDMyODEzMzgzNDYgLTIzLjAxMzU1NjY3MDYwMjg4NSwgLTYwLjIxNDE4ODgyOTY5NjY1IC0yMy4yMjc0Mjc0MzU3MDM5OTQgQy02MC4xNzIyOTkzNDc4ODA4IC0yMy4zNjgxMzE3MjQ4NDE4NDUsIC02MC4xMzA0MDk4NjYwNjQ5NDQgLTIzLjUwODgzNjAxMzk3OTY5NiwgLTYwLjA5NjI3MzcwODUwMzE3NiAtMjMuNjIzNDk3MzQ2MDIzNDE3IEMtNjAuMDYwMjYyODU1MjM3NDI2IC0yMy43MTU3ODUyNTM1NTgwNTMsIC02MC4wMjQyNTIwMDE5NzE2NyAtMjMuODA4MDczMTYxMDkyNjksIC01OS45NDYwNTQxMzMyNzUyOSAtMjQuMDA4NDc3MTIzMjY0ODQ2IEMtNTkuODgxNTIwNjg4MTU0NjUgLTI0LjE0MDQ4MjM5NTE2MTMyLCAtNTkuODE2OTg3MjQzMDM0MDIgLTI0LjI3MjQ4NzY2NzA1Nzc5MiwgLTU5Ljc2NDU1NjI1NjAzMjQ1IC0yNC4zNzk3MzY5NjUxODUzNjYgQy01OS43MTIwNTcxMzA2Mjc3MSAtMjQuNDY3ODQxODY1NjExOTU1LCAtNTkuNjU5NTU4MDA1MjIyOTc2IC0yNC41NTU5NDY3NjYwMzg1NDUsIC01OS41NTMwMTk4OTEzMTI2NDQgLTI0LjczNDc0MDc5MDYxMjEzMyBDLTU5LjQ5MTI1MjA4NTgxNTI5IC0yNC44MjEyNTE5ODQ4OTg2MjcsIC01OS40Mjk0ODQyODAzMTc5NCAtMjQuOTA3NzYzMTc5MTg1MTI1LCAtNTkuMzEyODkwMDQ2OTgxOTcgLTI1LjA3MTA2MzU2MzQ0ODM0IEMtNTkuMjE1MjA2NjAzMDE1NTggLTI1LjE4NjM5ODE4NzgwMDI5LCAtNTkuMTE3NTIzMTU5MDQ5MTg1IC0yNS4zMDE3MzI4MTIxNTIyMzUsIC01OS4wNDU4MDcwNTMzNjU2NiAtMjUuMzg2NDA3ODU4MTI4NzA2IEMtNTguOTYxNTM5OTY5ODEwMzk2IC0yNS40NzA2NzQ5NDE2ODM5NjcsIC01OC44NzcyNzI4ODYyNTUxMzUgLTI1LjU1NDk0MjAyNTIzOTIyOCwgLTU4Ljc1MzU5NTM1ODEyODcwNiAtMjUuNjc4NjE5NTUzMzY1NjU3IEMtNTguNjg1Mjg2MTI1MTM0MDQgLTI1LjczNjQ3NDUyMjUxNDAzNywgLTU4LjYxNjk3Njg5MjEzOTM3IC0yNS43OTQzMjk0OTE2NjI0MTMsIC01OC40MzgyNTEwNjM0NDgzNCAtMjUuOTQ1NzAyNTQ2OTgxOTY2IEMtNTguMzEwMjQzNTEyOTYyMTMgLTI2LjAzNzA5ODE4MTMzNjk4NywgLTU4LjE4MjIzNTk2MjQ3NTkzIC0yNi4xMjg0OTM4MTU2OTIwMDcsIC01OC4xMDE5MjgyOTA2MTIxMzYgLTI2LjE4NTgzMjM5MTMxMjY0NCBDLTU3Ljk2ODExOTg5MzcxODAwNCAtMjYuMjY1NTY0ODg5NDQ1NDIsIC01Ny44MzQzMTE0OTY4MjM4NyAtMjYuMzQ1Mjk3Mzg3NTc4MjAzLCAtNTcuNzQ2OTI0NDY1MTg1MzY2IC0yNi4zOTczNjg3NTYwMzI0NDYgQy01Ny42NTU3ODAwNjUxODA2NCAtMjYuNDQxOTI2NTM4MDkxMTEsIC01Ny41NjQ2MzU2NjUxNzU5MyAtMjYuNDg2NDg0MzIwMTQ5Nzc0LCAtNTcuMzc1NjY0NjIzMjY0ODQ2IC0yNi41Nzg4NjY2MzMyNzUyODYgQy01Ny4yNjc1ODQ5MzM5MTY4NCAtMjYuNjIxMDM5NDU4OTI1OTE0LCAtNTcuMTU5NTA1MjQ0NTY4ODM0IC0yNi42NjMyMTIyODQ1NzY1MzgsIC01Ni45OTA2ODQ4NDYwMjM0MiAtMjYuNzI5MDg2MjA4NTAzMTczIEMtNTYuODY3NTk2ODExMDUzNjY2IC0yNi43NjU3MzExMDQyMjE3NiwgLTU2Ljc0NDUwODc3NjA4MzkxIC0yNi44MDIzNzU5OTk5NDAzNSwgLTU2LjU5NDYxNDkzNTcwMzk5NCAtMjYuODQ3MDAxMzI5Njk2NjUzIEMtNTYuNDYzMzIzMTkzNzExMzYgLTI2Ljg3NDUzMDMxMzA2ODIxLCAtNTYuMzMyMDMxNDUxNzE4NzIgLTI2LjkwMjA1OTI5NjQzOTc2NiwgLTU2LjE5MDE2MDQ1MTQwMzY3IC0yNi45MzE4MDY1MTcwMTM2MTIgQy01Ni4wMjY1OTMxMTY1OTA5MjQgLTI2Ljk1MjE5NTE2NDAzNTM4OCwgLTU1Ljg2MzAyNTc4MTc3ODE4IC0yNi45NzI1ODM4MTEwNTcxNjMsIC01NS43ODAwODQyMjczNjE2NiAtMjYuOTgyOTIyNDY1MDMzMzQ3IEMtNTUuNjc3MzkzMTUzNDkxMDcgLTI2Ljk4NzE2OTc5OTI2NjI4LCAtNTUuNTc0NzAyMDc5NjIwNDY1IC0yNi45OTE0MTcxMzM0OTkyMSwgLTU1LjM2NzE4NzUgLTI3IEMtNTUuMzY3MTg3NSAtMjcsIC01NS4zNjcxODc1IC0yNywgLTU1LjM2NzE4NzUgLTI3IiBzdHJva2U9Im5vbmUiIHN0cm9rZS13aWR0aD0iMCIgZmlsbD0iI0VDRUNGRiIgc3R5bGU9IiIvPjxwYXRoIGQ9Ik0tNTUuMzY3MTg3NSAtMjcgQy0yMC44NzY5OTY4ODI1OTMzNCAtMjcsIDEzLjYxMzE5MzczNDgxMzMyMiAtMjcsIDU1LjM2NzE4NzUgLTI3IE0tNTUuMzY3MTg3NSAtMjcgQy0xOC41MzAwMTUxNTE2ODU0MiAtMjcsIDE4LjMwNzE1NzE5NjYyOTE2MyAtMjcsIDU1LjM2NzE4NzUgLTI3IE01NS4zNjcxODc1IC0yNyBDNTUuMzY3MTg3NSAtMjcsIDU1LjM2NzE4NzUgLTI3LCA1NS4zNjcxODc1IC0yNyBNNTUuMzY3MTg3NSAtMjcgQzU1LjM2NzE4NzUgLTI3LCA1NS4zNjcxODc1IC0yNywgNTUuMzY3MTg3NSAtMjcgTTU1LjM2NzE4NzUgLTI3IEM1NS40ODQzMzQ1NDgwOTcxNDUgLTI2Ljk5NTE1NDc2MjI1MDM1LCA1NS42MDE0ODE1OTYxOTQyOCAtMjYuOTkwMzA5NTI0NTAwNzAyLCA1NS43ODAwODQyMjczNjE2NiAtMjYuOTgyOTIyNDY1MDMzMzQ3IE01NS4zNjcxODc1IC0yNyBDNTUuNDg4NDQ1MjU0NjU4NjY0IC0yNi45OTQ5ODQ3NDIxNjkzMjYsIDU1LjYwOTcwMzAwOTMxNzMzIC0yNi45ODk5Njk0ODQzMzg2NDgsIDU1Ljc4MDA4NDIyNzM2MTY2IC0yNi45ODI5MjI0NjUwMzMzNDcgTTU1Ljc4MDA4NDIyNzM2MTY2IC0yNi45ODI5MjI0NjUwMzMzNDcgQzU1Ljk0MjU2MTM4MDkwODAyNSAtMjYuOTYyNjY5NzA4OTY0NzEsIDU2LjEwNTAzODUzNDQ1NDM5NCAtMjYuOTQyNDE2OTUyODk2MDc1LCA1Ni4xOTAxNjA0NTE0MDM2NyAtMjYuOTMxODA2NTE3MDEzNjEyIE01NS43ODAwODQyMjczNjE2NiAtMjYuOTgyOTIyNDY1MDMzMzQ3IEM1NS45MzQ5NDA0NTU5NzQ0NCAtMjYuOTYzNjE5NjU2MjY3MDE0LCA1Ni4wODk3OTY2ODQ1ODcyMiAtMjYuOTQ0MzE2ODQ3NTAwNjgsIDU2LjE5MDE2MDQ1MTQwMzY3IC0yNi45MzE4MDY1MTcwMTM2MTIgTTU2LjE5MDE2MDQ1MTQwMzY3IC0yNi45MzE4MDY1MTcwMTM2MTIgQzU2LjMyMDYyNzAxMjcwMjIxIC0yNi45MDQ0NTA1NTU4Mzg3NCwgNTYuNDUxMDkzNTc0MDAwNzUgLTI2Ljg3NzA5NDU5NDY2Mzg3LCA1Ni41OTQ2MTQ5MzU3MDM5OTQgLTI2Ljg0NzAwMTMyOTY5NjY1MyBNNTYuMTkwMTYwNDUxNDAzNjcgLTI2LjkzMTgwNjUxNzAxMzYxMiBDNTYuMjg3MjM2NzYwNjYwNjcgLTI2LjkxMTQ1MTc1NTQ1MTcsIDU2LjM4NDMxMzA2OTkxNzY3IC0yNi44OTEwOTY5OTM4ODk3OSwgNTYuNTk0NjE0OTM1NzAzOTk0IC0yNi44NDcwMDEzMjk2OTY2NTMgTTU2LjU5NDYxNDkzNTcwMzk5NCAtMjYuODQ3MDAxMzI5Njk2NjUzIEM1Ni43MzIyOTk3Mjk5NjM3NSAtMjYuODA2MDEwNzkwNDU0MzYyLCA1Ni44Njk5ODQ1MjQyMjM1MDQgLTI2Ljc2NTAyMDI1MTIxMjA3NSwgNTYuOTkwNjg0ODQ2MDIzNDIgLTI2LjcyOTA4NjIwODUwMzE3MyBNNTYuNTk0NjE0OTM1NzAzOTk0IC0yNi44NDcwMDEzMjk2OTY2NTMgQzU2Ljc0Mzk0OTkyMjYwMzcwNCAtMjYuODAyNTQyMzc3ODMwMDM2LCA1Ni44OTMyODQ5MDk1MDM0MTQgLTI2Ljc1ODA4MzQyNTk2MzQyNCwgNTYuOTkwNjg0ODQ2MDIzNDIgLTI2LjcyOTA4NjIwODUwMzE3MyBNNTYuOTkwNjg0ODQ2MDIzNDIgLTI2LjcyOTA4NjIwODUwMzE3MyBDNTcuMDkwOTU4Mjk1NTkwNTcgLTI2LjY4OTk1OTM4NzA2NzEzNCwgNTcuMTkxMjMxNzQ1MTU3NzE1IC0yNi42NTA4MzI1NjU2MzEwOTUsIDU3LjM3NTY2NDYyMzI2NDg0NiAtMjYuNTc4ODY2NjMzMjc1Mjg2IE01Ni45OTA2ODQ4NDYwMjM0MiAtMjYuNzI5MDg2MjA4NTAzMTczIEM1Ny4wNzkzNzExMjc2Mzk4NDUgLTI2LjY5NDQ4MDcxNDAzMTY5LCA1Ny4xNjgwNTc0MDkyNTYyNjQgLTI2LjY1OTg3NTIxOTU2MDIxMywgNTcuMzc1NjY0NjIzMjY0ODQ2IC0yNi41Nzg4NjY2MzMyNzUyODYgTTU3LjM3NTY2NDYyMzI2NDg0NiAtMjYuNTc4ODY2NjMzMjc1Mjg2IEM1Ny40NTQ1MDYxMDIwNDY5MiAtMjYuNTQwMzIzMzgyOTEwMjMsIDU3LjUzMzM0NzU4MDgyOSAtMjYuNTAxNzgwMTMyNTQ1MTc1LCA1Ny43NDY5MjQ0NjUxODUzNjYgLTI2LjM5NzM2ODc1NjAzMjQ0NiBNNTcuMzc1NjY0NjIzMjY0ODQ2IC0yNi41Nzg4NjY2MzMyNzUyODYgQzU3LjQ4MDMwODU4MDY2MzY2IC0yNi41Mjc3MDkzMTk3NDU0MjYsIDU3LjU4NDk1MjUzODA2MjQ3NCAtMjYuNDc2NTUyMDA2MjE1NTYzLCA1Ny43NDY5MjQ0NjUxODUzNjYgLTI2LjM5NzM2ODc1NjAzMjQ0NiBNNTcuNzQ2OTI0NDY1MTg1MzY2IC0yNi4zOTczNjg3NTYwMzI0NDYgQzU3Ljg0ODAwMDYwMDI0NTY3IC0yNi4zMzcxNDA0NTI0NTA0MiwgNTcuOTQ5MDc2NzM1MzA1OTcgLTI2LjI3NjkxMjE0ODg2ODM5NCwgNTguMTAxOTI4MjkwNjEyMTM2IC0yNi4xODU4MzIzOTEzMTI2NDQgTTU3Ljc0NjkyNDQ2NTE4NTM2NiAtMjYuMzk3MzY4NzU2MDMyNDQ2IEM1Ny44NDkwOTUyNzkyMTUzMSAtMjYuMzM2NDg4MTY1MzY3MzI4LCA1Ny45NTEyNjYwOTMyNDUyNjYgLTI2LjI3NTYwNzU3NDcwMjIwNywgNTguMTAxOTI4MjkwNjEyMTM2IC0yNi4xODU4MzIzOTEzMTI2NDQgTTU4LjEwMTkyODI5MDYxMjEzNiAtMjYuMTg1ODMyMzkxMzEyNjQ0IEM1OC4xNzcxNTk3NTk1OTQzOSAtMjYuMTMyMTE4MTU0ODg2OTMsIDU4LjI1MjM5MTIyODU3NjYzNCAtMjYuMDc4NDAzOTE4NDYxMjE2LCA1OC40MzgyNTEwNjM0NDgzNCAtMjUuOTQ1NzAyNTQ2OTgxOTcgTTU4LjEwMTkyODI5MDYxMjEzNiAtMjYuMTg1ODMyMzkxMzEyNjQ0IEM1OC4yMTU0MzkxMTM4NTI5NjYgLTI2LjEwNDc4NzIyMTI2NzIsIDU4LjMyODk0OTkzNzA5Mzc5IC0yNi4wMjM3NDIwNTEyMjE3NSwgNTguNDM4MjUxMDYzNDQ4MzQgLTI1Ljk0NTcwMjU0Njk4MTk3IE01OC40MzgyNTEwNjM0NDgzNCAtMjUuOTQ1NzAyNTQ2OTgxOTcgQzU4LjUzMjI0NTkxMTIyMjU5IC0yNS44NjYwOTI5NzE4MjM4MDQsIDU4LjYyNjI0MDc1ODk5NjgzIC0yNS43ODY0ODMzOTY2NjU2NCwgNTguNzUzNTk1MzU4MTI4NzA2IC0yNS42Nzg2MTk1NTMzNjU2NTcgTTU4LjQzODI1MTA2MzQ0ODM0IC0yNS45NDU3MDI1NDY5ODE5NyBDNTguNTM5MzI2OTE2MTk1MDE2IC0yNS44NjAwOTU2NjY1MzYzNywgNTguNjQwNDAyNzY4OTQxNyAtMjUuNzc0NDg4Nzg2MDkwNzY3LCA1OC43NTM1OTUzNTgxMjg3MDYgLTI1LjY3ODYxOTU1MzM2NTY1NyBNNTguNzUzNTk1MzU4MTI4NzA2IC0yNS42Nzg2MTk1NTMzNjU2NTcgQzU4Ljg2NDk4MTMyODAwMTQ2IC0yNS41NjcyMzM1ODM0OTI5MDQsIDU4Ljk3NjM2NzI5Nzg3NDIxIC0yNS40NTU4NDc2MTM2MjAxNSwgNTkuMDQ1ODA3MDUzMzY1NjYgLTI1LjM4NjQwNzg1ODEyODcwNiBNNTguNzUzNTk1MzU4MTI4NzA2IC0yNS42Nzg2MTk1NTMzNjU2NTcgQzU4LjgzMzk2NTc4MzEwNTQwNSAtMjUuNTk4MjQ5MTI4Mzg4OTU3LCA1OC45MTQzMzYyMDgwODIxMDUgLTI1LjUxNzg3ODcwMzQxMjI1OCwgNTkuMDQ1ODA3MDUzMzY1NjYgLTI1LjM4NjQwNzg1ODEyODcwNiBNNTkuMDQ1ODA3MDUzMzY1NjYgLTI1LjM4NjQwNzg1ODEyODcwNiBDNTkuMTAzOTQ4MjQzNDczODYgLTI1LjMxNzc2MDY4NDY4NTM1NywgNTkuMTYyMDg5NDMzNTgyMDYgLTI1LjI0OTExMzUxMTI0MjAwNSwgNTkuMzEyODkwMDQ2OTgxOTcgLTI1LjA3MTA2MzU2MzQ0ODM0IE01OS4wNDU4MDcwNTMzNjU2NiAtMjUuMzg2NDA3ODU4MTI4NzA2IEM1OS4xNTE3NjI2NzY1MjgzMzUgLTI1LjI2MTMwNjI5MDI1NTU2LCA1OS4yNTc3MTgyOTk2OTEwMiAtMjUuMTM2MjA0NzIyMzgyNDEzLCA1OS4zMTI4OTAwNDY5ODE5NyAtMjUuMDcxMDYzNTYzNDQ4MzQgTTU5LjMxMjg5MDA0Njk4MTk3IC0yNS4wNzEwNjM1NjM0NDgzNCBDNTkuMzYzNjg2OTQ0MzcxNzcgLTI0Ljk5OTkxODA4MjAxNDE4NywgNTkuNDE0NDgzODQxNzYxNTcgLTI0LjkyODc3MjYwMDU4MDAzMiwgNTkuNTUzMDE5ODkxMzEyNjQ0IC0yNC43MzQ3NDA3OTA2MTIxMzYgTTU5LjMxMjg5MDA0Njk4MTk3IC0yNS4wNzEwNjM1NjM0NDgzNCBDNTkuMzgyMDIxNzk5NzM0MzQgLTI0Ljk3NDIzODUxOTMwODExLCA1OS40NTExNTM1NTI0ODY3MTYgLTI0Ljg3NzQxMzQ3NTE2Nzg3NCwgNTkuNTUzMDE5ODkxMzEyNjQ0IC0yNC43MzQ3NDA3OTA2MTIxMzYgTTU5LjU1MzAxOTg5MTMxMjY0NCAtMjQuNzM0NzQwNzkwNjEyMTM2IEM1OS42MjUzMjQ3ODUwNzY2MiAtMjQuNjEzMzk3NTIyMTk5MjI1LCA1OS42OTc2Mjk2Nzg4NDA1OSAtMjQuNDkyMDU0MjUzNzg2MzEsIDU5Ljc2NDU1NjI1NjAzMjQ1IC0yNC4zNzk3MzY5NjUxODUzNyBNNTkuNTUzMDE5ODkxMzEyNjQ0IC0yNC43MzQ3NDA3OTA2MTIxMzYgQzU5LjYyMDMwNDY4Mzk3OTUyIC0yNC42MjE4MjIzMzg4NzcyNDgsIDU5LjY4NzU4OTQ3NjY0NjM5IC0yNC41MDg5MDM4ODcxNDIzNjMsIDU5Ljc2NDU1NjI1NjAzMjQ1IC0yNC4zNzk3MzY5NjUxODUzNyBNNTkuNzY0NTU2MjU2MDMyNDUgLTI0LjM3OTczNjk2NTE4NTM3IEM1OS44MjA5NTkyOTA1MzY4MzQgLTI0LjI2NDM2MjcxNDAyNTg0NywgNTkuODc3MzYyMzI1MDQxMjIgLTI0LjE0ODk4ODQ2Mjg2NjMyNCwgNTkuOTQ2MDU0MTMzMjc1MjkgLTI0LjAwODQ3NzEyMzI2NDg0NiBNNTkuNzY0NTU2MjU2MDMyNDUgLTI0LjM3OTczNjk2NTE4NTM3IEM1OS44MzY4MzI2MzM5Mjg2MTYgLTI0LjIzMTg5MzI3MTE0MzYxLCA1OS45MDkxMDkwMTE4MjQ3OCAtMjQuMDg0MDQ5NTc3MTAxODUsIDU5Ljk0NjA1NDEzMzI3NTI5IC0yNC4wMDg0NzcxMjMyNjQ4NDYgTTU5Ljk0NjA1NDEzMzI3NTI5IC0yNC4wMDg0NzcxMjMyNjQ4NDYgQzYwLjAwMTgzMTQ4OTAwNzEyIC0yMy44NjU1MzIwMTE0MDMyMjUsIDYwLjA1NzYwODg0NDczODk0NCAtMjMuNzIyNTg2ODk5NTQxNjA3LCA2MC4wOTYyNzM3MDg1MDMxNzYgLTIzLjYyMzQ5NzM0NjAyMzQxNyBNNTkuOTQ2MDU0MTMzMjc1MjkgLTI0LjAwODQ3NzEyMzI2NDg0NiBDNTkuOTc4OTU0MTU4MDg3NDkgLTIzLjkyNDE2MTU4NTc5NzYyNCwgNjAuMDExODU0MTgyODk5NjggLTIzLjgzOTg0NjA0ODMzMDM5NywgNjAuMDk2MjczNzA4NTAzMTc2IC0yMy42MjM0OTczNDYwMjM0MTcgTTYwLjA5NjI3MzcwODUwMzE3NiAtMjMuNjIzNDk3MzQ2MDIzNDE3IEM2MC4xMjIzOTgxNTI1OTYyMDUgLTIzLjUzNTc0Njg4NTE0NjYyNiwgNjAuMTQ4NTIyNTk2Njg5MjMgLTIzLjQ0Nzk5NjQyNDI2OTg0LCA2MC4yMTQxODg4Mjk2OTY2NSAtMjMuMjI3NDI3NDM1NzAzOTk0IE02MC4wOTYyNzM3MDg1MDMxNzYgLTIzLjYyMzQ5NzM0NjAyMzQxNyBDNjAuMTMwODQzOTQwOTY3Njk0IC0yMy41MDczNzc5ODIwODMwMDcsIDYwLjE2NTQxNDE3MzQzMjIxIC0yMy4zOTEyNTg2MTgxNDI2LCA2MC4yMTQxODg4Mjk2OTY2NSAtMjMuMjI3NDI3NDM1NzAzOTk0IE02MC4yMTQxODg4Mjk2OTY2NSAtMjMuMjI3NDI3NDM1NzAzOTk0IEM2MC4yMzI0NzQ3ODY0MDIxMiAtMjMuMTQwMjE3NzA5MjMyODA0LCA2MC4yNTA3NjA3NDMxMDc2IC0yMy4wNTMwMDc5ODI3NjE2MTcsIDYwLjI5ODk5NDAxNzAxMzYxIC0yMi44MjI5NzI5NTE0MDM2NyBNNjAuMjE0MTg4ODI5Njk2NjUgLTIzLjIyNzQyNzQzNTcwMzk5NCBDNjAuMjQ1MTI2MDA0MzUzMDcgLTIzLjA3OTg4MTI4NDEzODk1LCA2MC4yNzYwNjMxNzkwMDk0OSAtMjIuOTMyMzM1MTMyNTczOSwgNjAuMjk4OTk0MDE3MDEzNjEgLTIyLjgyMjk3Mjk1MTQwMzY3IE02MC4yOTg5OTQwMTcwMTM2MSAtMjIuODIyOTcyOTUxNDAzNjcgQzYwLjMxNjQ1MjIwMTc4MzAzIC0yMi42ODI5MTUxNjU3OTYyLCA2MC4zMzM5MTAzODY1NTI0NDYgLTIyLjU0Mjg1NzM4MDE4ODcyOCwgNjAuMzUwMTA5OTY1MDMzMzUgLTIyLjQxMjg5NjcyNzM2MTY2MiBNNjAuMjk4OTk0MDE3MDEzNjEgLTIyLjgyMjk3Mjk1MTQwMzY3IEM2MC4zMTI4ODE3NDgzNjYzOTQgLTIyLjcxMTU1OTAyNTcyNTE0LCA2MC4zMjY3Njk0Nzk3MTkxNzUgLTIyLjYwMDE0NTEwMDA0NjYxLCA2MC4zNTAxMDk5NjUwMzMzNSAtMjIuNDEyODk2NzI3MzYxNjYyIE02MC4zNTAxMDk5NjUwMzMzNSAtMjIuNDEyODk2NzI3MzYxNjYyIEM2MC4zNTUzNzIxMTAwNjkzNTQgLTIyLjI4NTY2OTc5MDQyMjk3LCA2MC4zNjA2MzQyNTUxMDUzNyAtMjIuMTU4NDQyODUzNDg0Mjc0LCA2MC4zNjcxODc1IC0yMiBNNjAuMzUwMTA5OTY1MDMzMzUgLTIyLjQxMjg5NjcyNzM2MTY2MiBDNjAuMzU1NDQ0OTUwNDc2NjQgLTIyLjI4MzkwODY3MTc0NTk1LCA2MC4zNjA3Nzk5MzU5MTk5MzYgLTIyLjE1NDkyMDYxNjEzMDIzNywgNjAuMzY3MTg3NSAtMjIgTTYwLjM2NzE4NzUgLTIyIEM2MC4zNjcxODc1IC0yMiwgNjAuMzY3MTg3NSAtMjIsIDYwLjM2NzE4NzUgLTIyIE02MC4zNjcxODc1IC0yMiBDNjAuMzY3MTg3NSAtMjIsIDYwLjM2NzE4NzUgLTIyLCA2MC4zNjcxODc1IC0yMiBNNjAuMzY3MTg3NSAtMjIgQzYwLjM2NzE4NzUgLTExLjE3MjE4NTYxMjY0NDIwOCwgNjAuMzY3MTg3NSAtMC4zNDQzNzEyMjUyODg0MTYxLCA2MC4zNjcxODc1IDIyIE02MC4zNjcxODc1IC0yMiBDNjAuMzY3MTg3NSAtNi42MTY3ODAwMDU2MTI3NTMsIDYwLjM2NzE4NzUgOC43NjY0Mzk5ODg3NzQ0OTMsIDYwLjM2NzE4NzUgMjIgTTYwLjM2NzE4NzUgMjIgQzYwLjM2NzE4NzUgMjIsIDYwLjM2NzE4NzUgMjIsIDYwLjM2NzE4NzUgMjIgTTYwLjM2NzE4NzUgMjIgQzYwLjM2NzE4NzUgMjIsIDYwLjM2NzE4NzUgMjIsIDYwLjM2NzE4NzUgMjIgTTYwLjM2NzE4NzUgMjIgQzYwLjM2MDUzNTEzOTM4MDYgMjIuMTYwODM5MjUwNzY2NzE3LCA2MC4zNTM4ODI3Nzg3NjEyMDQgMjIuMzIxNjc4NTAxNTMzNDMsIDYwLjM1MDEwOTk2NTAzMzM1IDIyLjQxMjg5NjcyNzM2MTY2MiBNNjAuMzY3MTg3NSAyMiBDNjAuMzYzMzAwMTU2NDk0NjI0IDIyLjA5Mzk4NzMwMDU0NjM1MywgNjAuMzU5NDEyODEyOTg5MjQgMjIuMTg3OTc0NjAxMDkyNzA1LCA2MC4zNTAxMDk5NjUwMzMzNSAyMi40MTI4OTY3MjczNjE2NjIgTTYwLjM1MDEwOTk2NTAzMzM1IDIyLjQxMjg5NjcyNzM2MTY2MiBDNjAuMzM3NjEwNjU1NDYyNDkgMjIuNTEzMTcyMDc5MTk0NTQzLCA2MC4zMjUxMTEzNDU4OTE2MyAyMi42MTM0NDc0MzEwMjc0MiwgNjAuMjk4OTk0MDE3MDEzNjEgMjIuODIyOTcyOTUxNDAzNjcgTTYwLjM1MDEwOTk2NTAzMzM1IDIyLjQxMjg5NjcyNzM2MTY2MiBDNjAuMzMxNzcwMTAxNTgwODk0IDIyLjU2MDAyNzc1NDg2NjUzNSwgNjAuMzEzNDMwMjM4MTI4NDMgMjIuNzA3MTU4NzgyMzcxNDEyLCA2MC4yOTg5OTQwMTcwMTM2MSAyMi44MjI5NzI5NTE0MDM2NyBNNjAuMjk4OTk0MDE3MDEzNjEgMjIuODIyOTcyOTUxNDAzNjcgQzYwLjI3MTg3NzQ0NzMzMTM4IDIyLjk1MjI5NzgwMjMyMTQ2NCwgNjAuMjQ0NzYwODc3NjQ5MTUgMjMuMDgxNjIyNjUzMjM5MjU4LCA2MC4yMTQxODg4Mjk2OTY2NSAyMy4yMjc0Mjc0MzU3MDM5OTQgTTYwLjI5ODk5NDAxNzAxMzYxIDIyLjgyMjk3Mjk1MTQwMzY3IEM2MC4yNjkxNTcyNzQyMDkyOCAyMi45NjUyNzA5MDI4MTM0OTcsIDYwLjIzOTMyMDUzMTQwNDkzNSAyMy4xMDc1Njg4NTQyMjMzMjcsIDYwLjIxNDE4ODgyOTY5NjY1IDIzLjIyNzQyNzQzNTcwMzk5NCBNNjAuMjE0MTg4ODI5Njk2NjUgMjMuMjI3NDI3NDM1NzAzOTk0IEM2MC4xNzU4ODc5MDM2MTY2MiAyMy4zNTYwNzc5Nzg2MDQwNzMsIDYwLjEzNzU4Njk3NzUzNjU4NiAyMy40ODQ3Mjg1MjE1MDQxNTYsIDYwLjA5NjI3MzcwODUwMzE3NiAyMy42MjM0OTczNDYwMjM0MTcgTTYwLjIxNDE4ODgyOTY5NjY1IDIzLjIyNzQyNzQzNTcwMzk5NCBDNjAuMTg3MzI4NDI3ODI1ODc1IDIzLjMxNzY0OTkzNTE4MDA5MywgNjAuMTYwNDY4MDI1OTU1MSAyMy40MDc4NzI0MzQ2NTYxOTUsIDYwLjA5NjI3MzcwODUwMzE3NiAyMy42MjM0OTczNDYwMjM0MTcgTTYwLjA5NjI3MzcwODUwMzE3NiAyMy42MjM0OTczNDYwMjM0MTcgQzYwLjA0NzI0NTYzMDE4MTM3IDIzLjc0OTE0NTU0MjI4NzI1LCA1OS45OTgyMTc1NTE4NTk1NzUgMjMuODc0NzkzNzM4NTUxMDc3LCA1OS45NDYwNTQxMzMyNzUyOSAyNC4wMDg0NzcxMjMyNjQ4NDYgTTYwLjA5NjI3MzcwODUwMzE3NiAyMy42MjM0OTczNDYwMjM0MTcgQzYwLjA2MzcyNjE3NDE0OTQ5NCAyMy43MDY5MDk1Mjc4NjYwODgsIDYwLjAzMTE3ODYzOTc5NTgyIDIzLjc5MDMyMTcwOTcwODc2LCA1OS45NDYwNTQxMzMyNzUyOSAyNC4wMDg0NzcxMjMyNjQ4NDYgTTU5Ljk0NjA1NDEzMzI3NTI5IDI0LjAwODQ3NzEyMzI2NDg0NiBDNTkuODg0ODA5ODA3MzMwMTM2IDI0LjEzMzc1NDM5NDM1MTA4LCA1OS44MjM1NjU0ODEzODQ5OCAyNC4yNTkwMzE2NjU0MzczMSwgNTkuNzY0NTU2MjU2MDMyNDUgMjQuMzc5NzM2OTY1MTg1MzY2IE01OS45NDYwNTQxMzMyNzUyOSAyNC4wMDg0NzcxMjMyNjQ4NDYgQzU5Ljg5MzY0NzM0MDk3NzA3NiAyNC4xMTU2NzY5MzAzMzU2NjcsIDU5Ljg0MTI0MDU0ODY3ODg2IDI0LjIyMjg3NjczNzQwNjQ5LCA1OS43NjQ1NTYyNTYwMzI0NSAyNC4zNzk3MzY5NjUxODUzNjYgTTU5Ljc2NDU1NjI1NjAzMjQ1IDI0LjM3OTczNjk2NTE4NTM2NiBDNTkuNzA5Nzc5OTM3NTg0OTggMjQuNDcxNjYzNDg4NjMzOTY1LCA1OS42NTUwMDM2MTkxMzc1MSAyNC41NjM1OTAwMTIwODI1NjQsIDU5LjU1MzAxOTg5MTMxMjY0NCAyNC43MzQ3NDA3OTA2MTIxMzMgTTU5Ljc2NDU1NjI1NjAzMjQ1IDI0LjM3OTczNjk2NTE4NTM2NiBDNTkuNjk0NTk5NDQ2NDQ3OTYgMjQuNDk3MTM5NjM5ODk4MjU4LCA1OS42MjQ2NDI2MzY4NjM0NiAyNC42MTQ1NDIzMTQ2MTExNDYsIDU5LjU1MzAxOTg5MTMxMjY0NCAyNC43MzQ3NDA3OTA2MTIxMzMgTTU5LjU1MzAxOTg5MTMxMjY0NCAyNC43MzQ3NDA3OTA2MTIxMzMgQzU5LjQ3MjYwMzAwOTA3NzY0IDI0Ljg0NzM3MTY0MjAyMjk4NSwgNTkuMzkyMTg2MTI2ODQyNjQgMjQuOTYwMDAyNDkzNDMzODM3LCA1OS4zMTI4OTAwNDY5ODE5NyAyNS4wNzEwNjM1NjM0NDgzNCBNNTkuNTUzMDE5ODkxMzEyNjQ0IDI0LjczNDc0MDc5MDYxMjEzMyBDNTkuNDY2OTA5ODYyNDY0NjkgMjQuODU1MzQ1Mzg5OTc3NDIsIDU5LjM4MDc5OTgzMzYxNjc0IDI0Ljk3NTk0OTk4OTM0MjcwMiwgNTkuMzEyODkwMDQ2OTgxOTcgMjUuMDcxMDYzNTYzNDQ4MzQgTTU5LjMxMjg5MDA0Njk4MTk3IDI1LjA3MTA2MzU2MzQ0ODM0IEM1OS4yNDYxNTg1OTk0MDcyNyAyNS4xNDk4NTMyMzQ3MTY4NiwgNTkuMTc5NDI3MTUxODMyNTggMjUuMjI4NjQyOTA1OTg1Mzg1LCA1OS4wNDU4MDcwNTMzNjU2NiAyNS4zODY0MDc4NTgxMjg3MDYgTTU5LjMxMjg5MDA0Njk4MTk3IDI1LjA3MTA2MzU2MzQ0ODM0IEM1OS4yMTcwNDA3NTM0NTc2IDI1LjE4NDIzMjYxMDQ2NTUxOCwgNTkuMTIxMTkxNDU5OTMzMjM2IDI1LjI5NzQwMTY1NzQ4MjY5NSwgNTkuMDQ1ODA3MDUzMzY1NjYgMjUuMzg2NDA3ODU4MTI4NzA2IE01OS4wNDU4MDcwNTMzNjU2NiAyNS4zODY0MDc4NTgxMjg3MDYgQzU4Ljk1NTE2NDQ4MDI3NjA0NiAyNS40NzcwNTA0MzEyMTgzMiwgNTguODY0NTIxOTA3MTg2NDMgMjUuNTY3NjkzMDA0MzA3OTM0LCA1OC43NTM1OTUzNTgxMjg3MDYgMjUuNjc4NjE5NTUzMzY1NjU3IE01OS4wNDU4MDcwNTMzNjU2NiAyNS4zODY0MDc4NTgxMjg3MDYgQzU4Ljk1NjMxOTkyODEzMjcyIDI1LjQ3NTg5NDk4MzM2MTY0LCA1OC44NjY4MzI4MDI4OTk3ODYgMjUuNTY1MzgyMTA4NTk0NTc2LCA1OC43NTM1OTUzNTgxMjg3MDYgMjUuNjc4NjE5NTUzMzY1NjU3IE01OC43NTM1OTUzNTgxMjg3MDYgMjUuNjc4NjE5NTUzMzY1NjU3IEM1OC42NTg4OTc0MDkxMTg3NSAyNS43NTg4MjQ2MjQ4OTQwMzYsIDU4LjU2NDE5OTQ2MDEwODc5IDI1LjgzOTAyOTY5NjQyMjQxLCA1OC40MzgyNTEwNjM0NDgzNCAyNS45NDU3MDI1NDY5ODE5NyBNNTguNzUzNTk1MzU4MTI4NzA2IDI1LjY3ODYxOTU1MzM2NTY1NyBDNTguNjQ4MDgyODk0MDUxNyAyNS43Njc5ODQwNTE5MzA5LCA1OC41NDI1NzA0Mjk5NzQ2OTUgMjUuODU3MzQ4NTUwNDk2MTM4LCA1OC40MzgyNTEwNjM0NDgzNCAyNS45NDU3MDI1NDY5ODE5NyBNNTguNDM4MjUxMDYzNDQ4MzQgMjUuOTQ1NzAyNTQ2OTgxOTcgQzU4LjMzMjQ3NDU0OTQ4NDcxIDI2LjAyMTIyNTUyNjM0NDExOCwgNTguMjI2Njk4MDM1NTIxMDcgMjYuMDk2NzQ4NTA1NzA2MjY2LCA1OC4xMDE5MjgyOTA2MTIxMzYgMjYuMTg1ODMyMzkxMzEyNjQ0IE01OC40MzgyNTEwNjM0NDgzNCAyNS45NDU3MDI1NDY5ODE5NyBDNTguMzE0OTgyNzUwNTM3ODEgMjYuMDMzNzE0NDMwNzQ2MTY2LCA1OC4xOTE3MTQ0Mzc2MjcyNyAyNi4xMjE3MjYzMTQ1MTAzNiwgNTguMTAxOTI4MjkwNjEyMTM2IDI2LjE4NTgzMjM5MTMxMjY0NCBNNTguMTAxOTI4MjkwNjEyMTM2IDI2LjE4NTgzMjM5MTMxMjY0NCBDNTcuOTYxMDQwNTEwNTE0MzQgMjYuMjY5NzgzMjg2MjA2NTM2LCA1Ny44MjAxNTI3MzA0MTY1MyAyNi4zNTM3MzQxODExMDA0MjUsIDU3Ljc0NjkyNDQ2NTE4NTM2NiAyNi4zOTczNjg3NTYwMzI0NDYgTTU4LjEwMTkyODI5MDYxMjEzNiAyNi4xODU4MzIzOTEzMTI2NDQgQzU4LjAwMTAwMzA5Nzg5ODUzIDI2LjI0NTk3MDc1Mjc3ODUxNSwgNTcuOTAwMDc3OTA1MTg0OTIgMjYuMzA2MTA5MTE0MjQ0Mzg2LCA1Ny43NDY5MjQ0NjUxODUzNjYgMjYuMzk3MzY4NzU2MDMyNDQ2IE01Ny43NDY5MjQ0NjUxODUzNjYgMjYuMzk3MzY4NzU2MDMyNDQ2IEM1Ny42NTIxMzMwMTA1ODcyNiAyNi40NDM3MDk0NzQ0Mzk1ODYsIDU3LjU1NzM0MTU1NTk4OTE0IDI2LjQ5MDA1MDE5Mjg0NjcyNywgNTcuMzc1NjY0NjIzMjY0ODQ2IDI2LjU3ODg2NjYzMzI3NTI4NiBNNTcuNzQ2OTI0NDY1MTg1MzY2IDI2LjM5NzM2ODc1NjAzMjQ0NiBDNTcuNTk5OTQzNjk4OTE3NDMgMjYuNDY5MjIzMjc0MjQ1Nzc4LCA1Ny40NTI5NjI5MzI2NDk0OSAyNi41NDEwNzc3OTI0NTkxMSwgNTcuMzc1NjY0NjIzMjY0ODQ2IDI2LjU3ODg2NjYzMzI3NTI4NiBNNTcuMzc1NjY0NjIzMjY0ODQ2IDI2LjU3ODg2NjYzMzI3NTI4NiBDNTcuMjgwMTA3ODMyNDEyMDEgMjYuNjE2MTUzMDA4NzY5ODkyLCA1Ny4xODQ1NTEwNDE1NTkxNzUgMjYuNjUzNDM5Mzg0MjY0NDk1LCA1Ni45OTA2ODQ4NDYwMjM0MiAyNi43MjkwODYyMDg1MDMxNzMgTTU3LjM3NTY2NDYyMzI2NDg0NiAyNi41Nzg4NjY2MzMyNzUyODYgQzU3LjI0MDA2NTc0NjUxNzQ5NCAyNi42MzE3Nzc0NzkxNzA0MjQsIDU3LjEwNDQ2Njg2OTc3MDE1IDI2LjY4NDY4ODMyNTA2NTU2LCA1Ni45OTA2ODQ4NDYwMjM0MiAyNi43MjkwODYyMDg1MDMxNzMgTTU2Ljk5MDY4NDg0NjAyMzQyIDI2LjcyOTA4NjIwODUwMzE3MyBDNTYuODM5NTE3OTY2OTEwNDggMjYuNzc0MDkwNTM4MzExMTc4LCA1Ni42ODgzNTEwODc3OTc1MyAyNi44MTkwOTQ4NjgxMTkxODcsIDU2LjU5NDYxNDkzNTcwMzk5NCAyNi44NDcwMDEzMjk2OTY2NTMgTTU2Ljk5MDY4NDg0NjAyMzQyIDI2LjcyOTA4NjIwODUwMzE3MyBDNTYuOTA1Mjc2ODMzNTA0NTE2IDI2Ljc1NDUxMzI3NTUwNDksIDU2LjgxOTg2ODgyMDk4NTYwNCAyNi43Nzk5NDAzNDI1MDY2MywgNTYuNTk0NjE0OTM1NzAzOTk0IDI2Ljg0NzAwMTMyOTY5NjY1MyBNNTYuNTk0NjE0OTM1NzAzOTk0IDI2Ljg0NzAwMTMyOTY5NjY1MyBDNTYuNTExODgzMDI3NzE2ODA2IDI2Ljg2NDM0ODM4NjU5OTkzLCA1Ni40MjkxNTExMTk3Mjk2MiAyNi44ODE2OTU0NDM1MDMyMDUsIDU2LjE5MDE2MDQ1MTQwMzY3IDI2LjkzMTgwNjUxNzAxMzYxMiBNNTYuNTk0NjE0OTM1NzAzOTk0IDI2Ljg0NzAwMTMyOTY5NjY1MyBDNTYuNDc2Njg2Mjg5OTE1MzYgMjYuODcxNzI4MzY2NDQ0NTM2LCA1Ni4zNTg3NTc2NDQxMjY3MjYgMjYuODk2NDU1NDAzMTkyNDIsIDU2LjE5MDE2MDQ1MTQwMzY3IDI2LjkzMTgwNjUxNzAxMzYxMiBNNTYuMTkwMTYwNDUxNDAzNjcgMjYuOTMxODA2NTE3MDEzNjEyIEM1Ni4wNDYwODE0NjY4NjMzOCAyNi45NDk3NjU5NDM3MDcyNzcsIDU1LjkwMjAwMjQ4MjMyMzA5IDI2Ljk2NzcyNTM3MDQwMDk0NSwgNTUuNzgwMDg0MjI3MzYxNjYgMjYuOTgyOTIyNDY1MDMzMzQ3IE01Ni4xOTAxNjA0NTE0MDM2NyAyNi45MzE4MDY1MTcwMTM2MTIgQzU2LjEwNDE1NjUxNzk3MDI1IDI2Ljk0MjUyNjg5NjEzNjE2NSwgNTYuMDE4MTUyNTg0NTM2ODMgMjYuOTUzMjQ3Mjc1MjU4NzE4LCA1NS43ODAwODQyMjczNjE2NiAyNi45ODI5MjI0NjUwMzMzNDcgTTU1Ljc4MDA4NDIyNzM2MTY2IDI2Ljk4MjkyMjQ2NTAzMzM0NyBDNTUuNjc4Nzc2NTg4OTc4ODM1IDI2Ljk4NzExMjU3OTk1MTI1LCA1NS41Nzc0Njg5NTA1OTYwMTQgMjYuOTkxMzAyNjk0ODY5MTU1LCA1NS4zNjcxODc1IDI3IE01NS43ODAwODQyMjczNjE2NiAyNi45ODI5MjI0NjUwMzMzNDcgQzU1LjY0OTg0OTUzOTMxNDIzIDI2Ljk4ODMwOTAxMTU3NTQxNCwgNTUuNTE5NjE0ODUxMjY2OCAyNi45OTM2OTU1NTgxMTc0ODQsIDU1LjM2NzE4NzUgMjcgTTU1LjM2NzE4NzUgMjcgQzU1LjM2NzE4NzUgMjcsIDU1LjM2NzE4NzUgMjcsIDU1LjM2NzE4NzUgMjcgTTU1LjM2NzE4NzUgMjcgQzU1LjM2NzE4NzUgMjcsIDU1LjM2NzE4NzUgMjcsIDU1LjM2NzE4NzUgMjcgTTU1LjM2NzE4NzUgMjcgQzE2LjA1MjEyNTAxODQ5NjgzOCAyNywgLTIzLjI2MjkzNzQ2MzAwNjMyNCAyNywgLTU1LjM2NzE4NzUgMjcgTTU1LjM2NzE4NzUgMjcgQzE4LjU2NzM3OTM4NDUzNjA0NiAyNywgLTE4LjIzMjQyODczMDkyNzkwNyAyNywgLTU1LjM2NzE4NzUgMjcgTS01NS4zNjcxODc1IDI3IEMtNTUuMzY3MTg3NSAyNywgLTU1LjM2NzE4NzUgMjcsIC01NS4zNjcxODc1IDI3IE0tNTUuMzY3MTg3NSAyNyBDLTU1LjM2NzE4NzUgMjcsIC01NS4zNjcxODc1IDI3LCAtNTUuMzY3MTg3NSAyNyBNLTU1LjM2NzE4NzUgMjcgQy01NS41MDY3MzE1OTc5MzMwNiAyNi45OTQyMjg0MTM0MTY5MTMsIC01NS42NDYyNzU2OTU4NjYxMiAyNi45ODg0NTY4MjY4MzM4MjIsIC01NS43ODAwODQyMjczNjE2NiAyNi45ODI5MjI0NjUwMzMzNDcgTS01NS4zNjcxODc1IDI3IEMtNTUuNTAyMDQwODE0NTA0NDcgMjYuOTk0NDIyNDI1NjU0NjMsIC01NS42MzY4OTQxMjkwMDg5MyAyNi45ODg4NDQ4NTEzMDkyNjMsIC01NS43ODAwODQyMjczNjE2NiAyNi45ODI5MjI0NjUwMzMzNDcgTS01NS43ODAwODQyMjczNjE2NiAyNi45ODI5MjI0NjUwMzMzNDcgQy01NS44NzU3NDczODQzMDg5NjYgMjYuOTcwOTk4MDY0OTU1NDIsIC01NS45NzE0MTA1NDEyNTYyNyAyNi45NTkwNzM2NjQ4Nzc0OTQsIC01Ni4xOTAxNjA0NTE0MDM2NyAyNi45MzE4MDY1MTcwMTM2MTIgTS01NS43ODAwODQyMjczNjE2NiAyNi45ODI5MjI0NjUwMzMzNDcgQy01NS44OTI5MzE2OTE4NDU2NzUgMjYuOTY4ODU2MDQzMjU0ODE0LCAtNTYuMDA1Nzc5MTU2MzI5NjkgMjYuOTU0Nzg5NjIxNDc2MjgsIC01Ni4xOTAxNjA0NTE0MDM2NyAyNi45MzE4MDY1MTcwMTM2MTIgTS01Ni4xOTAxNjA0NTE0MDM2NyAyNi45MzE4MDY1MTcwMTM2MTIgQy01Ni4yODU4NjYwNTkwMDI1MiAyNi45MTE3MzkxNjEzNjYwNTQsIC01Ni4zODE1NzE2NjY2MDEzNzQgMjYuODkxNjcxODA1NzE4NSwgLTU2LjU5NDYxNDkzNTcwMzk5NCAyNi44NDcwMDEzMjk2OTY2NTMgTS01Ni4xOTAxNjA0NTE0MDM2NyAyNi45MzE4MDY1MTcwMTM2MTIgQy01Ni4zMjg1OTE4ODQ3OTQzNSAyNi45MDI3ODA0OTc3ODI4OTIsIC01Ni40NjcwMjMzMTgxODUwMyAyNi44NzM3NTQ0Nzg1NTIxNzYsIC01Ni41OTQ2MTQ5MzU3MDM5OTQgMjYuODQ3MDAxMzI5Njk2NjUzIE0tNTYuNTk0NjE0OTM1NzAzOTk0IDI2Ljg0NzAwMTMyOTY5NjY1MyBDLTU2LjczNjY3NjEwMjE3NTY5IDI2LjgwNDcwNzg4Nzk5NjIsIC01Ni44Nzg3MzcyNjg2NDczODQgMjYuNzYyNDE0NDQ2Mjk1NzU0LCAtNTYuOTkwNjg0ODQ2MDIzNDIgMjYuNzI5MDg2MjA4NTAzMTczIE0tNTYuNTk0NjE0OTM1NzAzOTk0IDI2Ljg0NzAwMTMyOTY5NjY1MyBDLTU2Ljc1MDk0MjgxMDcxNDUzIDI2LjgwMDQ2MDUwNDgzODQzNywgLTU2LjkwNzI3MDY4NTcyNTA2IDI2Ljc1MzkxOTY3OTk4MDIxOCwgLTU2Ljk5MDY4NDg0NjAyMzQyIDI2LjcyOTA4NjIwODUwMzE3MyBNLTU2Ljk5MDY4NDg0NjAyMzQyIDI2LjcyOTA4NjIwODUwMzE3MyBDLTU3LjA3ODUxMzIyNjUzMTY5IDI2LjY5NDgxNTQ2ODA4Mjg3MiwgLTU3LjE2NjM0MTYwNzAzOTk2IDI2LjY2MDU0NDcyNzY2MjU3LCAtNTcuMzc1NjY0NjIzMjY0ODQ2IDI2LjU3ODg2NjYzMzI3NTI4NiBNLTU2Ljk5MDY4NDg0NjAyMzQyIDI2LjcyOTA4NjIwODUwMzE3MyBDLTU3LjEzNjM0MzYxMTMzMDc3IDI2LjY3MjI0OTk4MTkxMTAyMywgLTU3LjI4MjAwMjM3NjYzODEzIDI2LjYxNTQxMzc1NTMxODg3MywgLTU3LjM3NTY2NDYyMzI2NDg0NiAyNi41Nzg4NjY2MzMyNzUyODYgTS01Ny4zNzU2NjQ2MjMyNjQ4NDYgMjYuNTc4ODY2NjMzMjc1Mjg2IEMtNTcuNTEzMDUzMjM0NDk2NDMgMjYuNTExNzAxNDMzOTE4MTk0LCAtNTcuNjUwNDQxODQ1NzI4MDIgMjYuNDQ0NTM2MjM0NTYxMSwgLTU3Ljc0NjkyNDQ2NTE4NTM2NiAyNi4zOTczNjg3NTYwMzI0NDYgTS01Ny4zNzU2NjQ2MjMyNjQ4NDYgMjYuNTc4ODY2NjMzMjc1Mjg2IEMtNTcuNTAwMjY5NjUxMjc1NzkgMjYuNTE3OTUwOTQ2OTQ0MTcsIC01Ny42MjQ4NzQ2NzkyODY3MyAyNi40NTcwMzUyNjA2MTMwNTQsIC01Ny43NDY5MjQ0NjUxODUzNjYgMjYuMzk3MzY4NzU2MDMyNDQ2IE0tNTcuNzQ2OTI0NDY1MTg1MzY2IDI2LjM5NzM2ODc1NjAzMjQ0NiBDLTU3LjgyMDEwNDI3MDEzNTEzIDI2LjM1Mzc2MzA1NzE2MDQxOCwgLTU3Ljg5MzI4NDA3NTA4NDkxIDI2LjMxMDE1NzM1ODI4ODM4NiwgLTU4LjEwMTkyODI5MDYxMjEzNiAyNi4xODU4MzIzOTEzMTI2NDQgTS01Ny43NDY5MjQ0NjUxODUzNjYgMjYuMzk3MzY4NzU2MDMyNDQ2IEMtNTcuODMxNzI2MTE1MjkyMjMgMjYuMzQ2ODM3OTQwNTg0ODA1LCAtNTcuOTE2NTI3NzY1Mzk5MDk2IDI2LjI5NjMwNzEyNTEzNzE2NSwgLTU4LjEwMTkyODI5MDYxMjEzNiAyNi4xODU4MzIzOTEzMTI2NDQgTS01OC4xMDE5MjgyOTA2MTIxMzYgMjYuMTg1ODMyMzkxMzEyNjQ0IEMtNTguMTkzOTgzNTYwNjc4MDUgMjYuMTIwMTA2MTkxNzkyMDY1LCAtNTguMjg2MDM4ODMwNzQzOTcgMjYuMDU0Mzc5OTkyMjcxNDg1LCAtNTguNDM4MjUxMDYzNDQ4MzQgMjUuOTQ1NzAyNTQ2OTgxOTcgTS01OC4xMDE5MjgyOTA2MTIxMzYgMjYuMTg1ODMyMzkxMzEyNjQ0IEMtNTguMTc1NDIzMTk3OTAxNDggMjYuMTMzMzU4MDM2MTA0MjIzLCAtNTguMjQ4OTE4MTA1MTkwODI1IDI2LjA4MDg4MzY4MDg5NTgwNiwgLTU4LjQzODI1MTA2MzQ0ODM0IDI1Ljk0NTcwMjU0Njk4MTk3IE0tNTguNDM4MjUxMDYzNDQ4MzQgMjUuOTQ1NzAyNTQ2OTgxOTcgQy01OC41NDYzNDY5MDM4NDQyMyAyNS44NTQxNTAwNDAyODU1MzYsIC01OC42NTQ0NDI3NDQyNDAxMyAyNS43NjI1OTc1MzM1ODkxMDUsIC01OC43NTM1OTUzNTgxMjg3MDYgMjUuNjc4NjE5NTUzMzY1NjYgTS01OC40MzgyNTEwNjM0NDgzNCAyNS45NDU3MDI1NDY5ODE5NyBDLTU4LjUzOTgzMTk2MTk2MTcyNCAyNS44NTk2Njc5MTQ1OTE3MTgsIC01OC42NDE0MTI4NjA0NzUxIDI1Ljc3MzYzMzI4MjIwMTQ3LCAtNTguNzUzNTk1MzU4MTI4NzA2IDI1LjY3ODYxOTU1MzM2NTY2IE0tNTguNzUzNTk1MzU4MTI4NzA2IDI1LjY3ODYxOTU1MzM2NTY2IEMtNTguODQ0OTAxNTE3NTcyNDQgMjUuNTg3MzEzMzkzOTIxOTMsIC01OC45MzYyMDc2NzcwMTYxNjQgMjUuNDk2MDA3MjM0NDc4MiwgLTU5LjA0NTgwNzA1MzM2NTY2IDI1LjM4NjQwNzg1ODEyODcwNiBNLTU4Ljc1MzU5NTM1ODEyODcwNiAyNS42Nzg2MTk1NTMzNjU2NiBDLTU4Ljg0MzYzNDEyMDk4MTIgMjUuNTg4NTgwNzkwNTEzMTY2LCAtNTguOTMzNjcyODgzODMzNjk0IDI1LjQ5ODU0MjAyNzY2MDY3MiwgLTU5LjA0NTgwNzA1MzM2NTY2IDI1LjM4NjQwNzg1ODEyODcwNiBNLTU5LjA0NTgwNzA1MzM2NTY2IDI1LjM4NjQwNzg1ODEyODcwNiBDLTU5LjEyNTc3NDEyNDg0MDcgMjUuMjkxOTkwOTE1MjUwNzIsIC01OS4yMDU3NDExOTYzMTU3NSAyNS4xOTc1NzM5NzIzNzI3MzcsIC01OS4zMTI4OTAwNDY5ODE5NyAyNS4wNzEwNjM1NjM0NDgzNCBNLTU5LjA0NTgwNzA1MzM2NTY2IDI1LjM4NjQwNzg1ODEyODcwNiBDLTU5LjE0MjQxMTUyNjk5MDk3NiAyNS4yNzIzNDcxNzE2NTExOTgsIC01OS4yMzkwMTYwMDA2MTYyOTYgMjUuMTU4Mjg2NDg1MTczNjksIC01OS4zMTI4OTAwNDY5ODE5NyAyNS4wNzEwNjM1NjM0NDgzNCBNLTU5LjMxMjg5MDA0Njk4MTk3IDI1LjA3MTA2MzU2MzQ0ODM0IEMtNTkuMzg5MjE3MTg5NjE2MzMgMjQuOTY0MTYwNzQ4NzQ0MDQyLCAtNTkuNDY1NTQ0MzMyMjUwNyAyNC44NTcyNTc5MzQwMzk3NDQsIC01OS41NTMwMTk4OTEzMTI2NDQgMjQuNzM0NzQwNzkwNjEyMTMzIE0tNTkuMzEyODkwMDQ2OTgxOTcgMjUuMDcxMDYzNTYzNDQ4MzQgQy01OS4zOTc1NzY2MTQ4NDA1MjQgMjQuOTUyNDUyNjQ1MjQ0NjUsIC01OS40ODIyNjMxODI2OTkwOCAyNC44MzM4NDE3MjcwNDA5NiwgLTU5LjU1MzAxOTg5MTMxMjY0NCAyNC43MzQ3NDA3OTA2MTIxMzMgTS01OS41NTMwMTk4OTEzMTI2NDQgMjQuNzM0NzQwNzkwNjEyMTMzIEMtNTkuNjA3Mjc4NjI3MTgwMTkgMjQuNjQzNjgyODgyODA4MzU2LCAtNTkuNjYxNTM3MzYzMDQ3NzUgMjQuNTUyNjI0OTc1MDA0NTgsIC01OS43NjQ1NTYyNTYwMzI0NCAyNC4zNzk3MzY5NjUxODUzNyBNLTU5LjU1MzAxOTg5MTMxMjY0NCAyNC43MzQ3NDA3OTA2MTIxMzMgQy01OS42MTE0ODE3MzgyNTI2NyAyNC42MzY2MjkxNTIzMDAxMzQsIC01OS42Njk5NDM1ODUxOTI3IDI0LjUzODUxNzUxMzk4ODE0LCAtNTkuNzY0NTU2MjU2MDMyNDQgMjQuMzc5NzM2OTY1MTg1MzcgTS01OS43NjQ1NTYyNTYwMzI0NCAyNC4zNzk3MzY5NjUxODUzNyBDLTU5LjgxMTMwOTg2NjA3NzMxIDI0LjI4NDEwMDkyNzI0MjkxNCwgLTU5Ljg1ODA2MzQ3NjEyMjE3NiAyNC4xODg0NjQ4ODkzMDA0NjIsIC01OS45NDYwNTQxMzMyNzUyOCAyNC4wMDg0NzcxMjMyNjQ4NSBNLTU5Ljc2NDU1NjI1NjAzMjQ0IDI0LjM3OTczNjk2NTE4NTM3IEMtNTkuODI2NTI3Mzk5OTI3OTcgMjQuMjUyOTcyOTY0MjE4NTU3LCAtNTkuODg4NDk4NTQzODIzNDkgMjQuMTI2MjA4OTYzMjUxNzQ1LCAtNTkuOTQ2MDU0MTMzMjc1MjggMjQuMDA4NDc3MTIzMjY0ODUgTS01OS45NDYwNTQxMzMyNzUyOCAyNC4wMDg0NzcxMjMyNjQ4NSBDLTYwLjAwNTc5MTA0NTQ4MTA3IDIzLjg1NTM4NDUzNzgzMzI1LCAtNjAuMDY1NTI3OTU3Njg2ODU0IDIzLjcwMjI5MTk1MjQwMTY1LCAtNjAuMDk2MjczNzA4NTAzMTc2IDIzLjYyMzQ5NzM0NjAyMzQxNyBNLTU5Ljk0NjA1NDEzMzI3NTI4IDI0LjAwODQ3NzEyMzI2NDg1IEMtNTkuOTk4MzgzODI4NDU0MzE1IDIzLjg3NDM2NzYwODE1OTU2LCAtNjAuMDUwNzEzNTIzNjMzMzQgMjMuNzQwMjU4MDkzMDU0MjcsIC02MC4wOTYyNzM3MDg1MDMxNzYgMjMuNjIzNDk3MzQ2MDIzNDE3IE0tNjAuMDk2MjczNzA4NTAzMTc2IDIzLjYyMzQ5NzM0NjAyMzQxNyBDLTYwLjEzMDc5MjA2MjEwOTA5NCAyMy41MDc1NTIyNDAwOTcyODcsIC02MC4xNjUzMTA0MTU3MTUwMSAyMy4zOTE2MDcxMzQxNzExNTYsIC02MC4yMTQxODg4Mjk2OTY2NSAyMy4yMjc0Mjc0MzU3MDM5OTQgTS02MC4wOTYyNzM3MDg1MDMxNzYgMjMuNjIzNDk3MzQ2MDIzNDE3IEMtNjAuMTM0Mjk5NzA3NjE4NzUgMjMuNDk1NzcwMjY2NTE4NSwgLTYwLjE3MjMyNTcwNjczNDMyIDIzLjM2ODA0MzE4NzAxMzU4MywgLTYwLjIxNDE4ODgyOTY5NjY1IDIzLjIyNzQyNzQzNTcwMzk5NCBNLTYwLjIxNDE4ODgyOTY5NjY1IDIzLjIyNzQyNzQzNTcwMzk5NCBDLTYwLjIzNDU2NDM2NDE3OTExIDIzLjEzMDI1MjA1NTg0NjEwNywgLTYwLjI1NDkzOTg5ODY2MTU2IDIzLjAzMzA3NjY3NTk4ODIyLCAtNjAuMjk4OTk0MDE3MDEzNjEgMjIuODIyOTcyOTUxNDAzNjcgTS02MC4yMTQxODg4Mjk2OTY2NSAyMy4yMjc0Mjc0MzU3MDM5OTQgQy02MC4yNDMyMjYxNTYyNzYyNiAyMy4wODg5NDIwNzUwOTM2ODQsIC02MC4yNzIyNjM0ODI4NTU4NiAyMi45NTA0NTY3MTQ0ODMzNzYsIC02MC4yOTg5OTQwMTcwMTM2MSAyMi44MjI5NzI5NTE0MDM2NyBNLTYwLjI5ODk5NDAxNzAxMzYxIDIyLjgyMjk3Mjk1MTQwMzY3IEMtNjAuMzE2NzI1MzI1MTQ5MTMgMjIuNjgwNzI0MDQxNDQwNTksIC02MC4zMzQ0NTY2MzMyODQ2NCAyMi41Mzg0NzUxMzE0Nzc1MSwgLTYwLjM1MDEwOTk2NTAzMzM1IDIyLjQxMjg5NjcyNzM2MTY2MiBNLTYwLjI5ODk5NDAxNzAxMzYxIDIyLjgyMjk3Mjk1MTQwMzY3IEMtNjAuMzEwMTYwMzAxMTUwMDU2IDIyLjczMzM5MTc1NzgxMDU0OCwgLTYwLjMyMTMyNjU4NTI4NjQ5IDIyLjY0MzgxMDU2NDIxNzQzLCAtNjAuMzUwMTA5OTY1MDMzMzUgMjIuNDEyODk2NzI3MzYxNjYyIE0tNjAuMzUwMTA5OTY1MDMzMzUgMjIuNDEyODk2NzI3MzYxNjYyIEMtNjAuMzU1NjE1NzMwNzgxNjMgMjIuMjc5Nzc5NTg0NjYzNjgsIC02MC4zNjExMjE0OTY1Mjk5MSAyMi4xNDY2NjI0NDE5NjU2OTUsIC02MC4zNjcxODc1IDIyIE0tNjAuMzUwMTA5OTY1MDMzMzUgMjIuNDEyODk2NzI3MzYxNjYyIEMtNjAuMzU2NTIwMTAzODYzNDEgMjIuMjU3OTEzODU5NTE1OTYsIC02MC4zNjI5MzAyNDI2OTM0NyAyMi4xMDI5MzA5OTE2NzAyNTIsIC02MC4zNjcxODc1IDIyIE0tNjAuMzY3MTg3NSAyMiBDLTYwLjM2NzE4NzUgMjIsIC02MC4zNjcxODc1IDIyLCAtNjAuMzY3MTg3NSAyMiBNLTYwLjM2NzE4NzUgMjIgQy02MC4zNjcxODc1IDIyLCAtNjAuMzY3MTg3NSAyMiwgLTYwLjM2NzE4NzUgMjIgTS02MC4zNjcxODc1IDIyIEMtNjAuMzY3MTg3NSA0LjQ1MDM4OTkzODg2MTMzNSwgLTYwLjM2NzE4NzUgLTEzLjA5OTIyMDEyMjI3NzMzLCAtNjAuMzY3MTg3NSAtMjIgTS02MC4zNjcxODc1IDIyIEMtNjAuMzY3MTg3NSA1LjM3ODAwNDcwMDYxNzUxNywgLTYwLjM2NzE4NzUgLTExLjI0Mzk5MDU5ODc2NDk2NywgLTYwLjM2NzE4NzUgLTIyIE0tNjAuMzY3MTg3NSAtMjIgQy02MC4zNjcxODc1IC0yMiwgLTYwLjM2NzE4NzUgLTIyLCAtNjAuMzY3MTg3NSAtMjIgTS02MC4zNjcxODc1IC0yMiBDLTYwLjM2NzE4NzUgLTIyLCAtNjAuMzY3MTg3NSAtMjIsIC02MC4zNjcxODc1IC0yMiBNLTYwLjM2NzE4NzUgLTIyIEMtNjAuMzYxNDYwMTkyNjMwNzkgLTIyLjEzODQ3MzUyNTI0NTkxNywgLTYwLjM1NTczMjg4NTI2MTU3NCAtMjIuMjc2OTQ3MDUwNDkxODM1LCAtNjAuMzUwMTA5OTY1MDMzMzUgLTIyLjQxMjg5NjcyNzM2MTY2IE0tNjAuMzY3MTg3NSAtMjIgQy02MC4zNjE1OTQ3MjczNDAyNiAtMjIuMTM1MjIwNzc1ODY2NzksIC02MC4zNTYwMDE5NTQ2ODA1MyAtMjIuMjcwNDQxNTUxNzMzNTgzLCAtNjAuMzUwMTA5OTY1MDMzMzUgLTIyLjQxMjg5NjcyNzM2MTY2IE0tNjAuMzUwMTA5OTY1MDMzMzUgLTIyLjQxMjg5NjcyNzM2MTY2IEMtNjAuMzMyODUxNzMyODY2MTMgLTIyLjU1MTM1MDM5ODk2NDEzLCAtNjAuMzE1NTkzNTAwNjk4OTEgLTIyLjY4OTgwNDA3MDU2NjYsIC02MC4yOTg5OTQwMTcwMTM2MSAtMjIuODIyOTcyOTUxNDAzNjcgTS02MC4zNTAxMDk5NjUwMzMzNSAtMjIuNDEyODk2NzI3MzYxNjYgQy02MC4zMzEwMzAwNzEzMzY0MyAtMjIuNTY1OTY0NjI2MjM2MTEsIC02MC4zMTE5NTAxNzc2Mzk1IC0yMi43MTkwMzI1MjUxMTA1NiwgLTYwLjI5ODk5NDAxNzAxMzYxIC0yMi44MjI5NzI5NTE0MDM2NyBNLTYwLjI5ODk5NDAxNzAxMzYxIC0yMi44MjI5NzI5NTE0MDM2NyBDLTYwLjI4MTgyNDc2NjE5MjM1IC0yMi45MDQ4NTY4NjMzMDE0NDcsIC02MC4yNjQ2NTU1MTUzNzEwOSAtMjIuOTg2NzQwNzc1MTk5MjIzLCAtNjAuMjE0MTg4ODI5Njk2NjUgLTIzLjIyNzQyNzQzNTcwMzk5NCBNLTYwLjI5ODk5NDAxNzAxMzYxIC0yMi44MjI5NzI5NTE0MDM2NyBDLTYwLjI3MzgzMzQ2NzEwNzI0NSAtMjIuOTQyOTY5MTE2MjQ5Nzg1LCAtNjAuMjQ4NjcyOTE3MjAwODg1IC0yMy4wNjI5NjUyODEwOTU5LCAtNjAuMjE0MTg4ODI5Njk2NjUgLTIzLjIyNzQyNzQzNTcwMzk5NCBNLTYwLjIxNDE4ODgyOTY5NjY1IC0yMy4yMjc0Mjc0MzU3MDM5OTQgQy02MC4xNzIwMDkyNjUyMjY3OCAtMjMuMzY5MTA2MDk1MzAwODg2LCAtNjAuMTI5ODI5NzAwNzU2ODkgLTIzLjUxMDc4NDc1NDg5Nzc4LCAtNjAuMDk2MjczNzA4NTAzMTc2IC0yMy42MjM0OTczNDYwMjM0MTcgTS02MC4yMTQxODg4Mjk2OTY2NSAtMjMuMjI3NDI3NDM1NzAzOTk0IEMtNjAuMTc0Nzg2NzQ5MjUyOTUgLTIzLjM1OTc3NjY5MDkxMjEzNSwgLTYwLjEzNTM4NDY2ODgwOTI2IC0yMy40OTIxMjU5NDYxMjAyNzUsIC02MC4wOTYyNzM3MDg1MDMxNzYgLTIzLjYyMzQ5NzM0NjAyMzQxNyBNLTYwLjA5NjI3MzcwODUwMzE3NiAtMjMuNjIzNDk3MzQ2MDIzNDE3IEMtNjAuMDQ2MjgzODUzMzk3OTggLTIzLjc1MTYxMDM2NDkzOTQ1MiwgLTU5Ljk5NjI5Mzk5ODI5Mjc5IC0yMy44Nzk3MjMzODM4NTU0ODUsIC01OS45NDYwNTQxMzMyNzUyOSAtMjQuMDA4NDc3MTIzMjY0ODQ2IE0tNjAuMDk2MjczNzA4NTAzMTc2IC0yMy42MjM0OTczNDYwMjM0MTcgQy02MC4wNTE0Njg3NDUyMjMyNiAtMjMuNzM4MzIyNjI1OTk1MzgsIC02MC4wMDY2NjM3ODE5NDMzNDUgLTIzLjg1MzE0NzkwNTk2NzM0NywgLTU5Ljk0NjA1NDEzMzI3NTI5IC0yNC4wMDg0NzcxMjMyNjQ4NDYgTS01OS45NDYwNTQxMzMyNzUyOSAtMjQuMDA4NDc3MTIzMjY0ODQ2IEMtNTkuODgzMzkwNzcwNjUwNzIgLTI0LjEzNjY1NzA4MDI3MzgxNCwgLTU5LjgyMDcyNzQwODAyNjE1IC0yNC4yNjQ4MzcwMzcyODI3ODUsIC01OS43NjQ1NTYyNTYwMzI0NSAtMjQuMzc5NzM2OTY1MTg1MzY2IE0tNTkuOTQ2MDU0MTMzMjc1MjkgLTI0LjAwODQ3NzEyMzI2NDg0NiBDLTU5LjkwNzUxNDgxNTMyMDU3IC0yNC4wODczMTA1NTgxNzMwMiwgLTU5Ljg2ODk3NTQ5NzM2NTg0NSAtMjQuMTY2MTQzOTkzMDgxMTk2LCAtNTkuNzY0NTU2MjU2MDMyNDUgLTI0LjM3OTczNjk2NTE4NTM2NiBNLTU5Ljc2NDU1NjI1NjAzMjQ1IC0yNC4zNzk3MzY5NjUxODUzNjYgQy01OS42ODYxOTkyNjI0NjUwNiAtMjQuNTExMjM2OTY3NTcxMjksIC01OS42MDc4NDIyNjg4OTc2NyAtMjQuNjQyNzM2OTY5OTU3MjEzLCAtNTkuNTUzMDE5ODkxMzEyNjQ0IC0yNC43MzQ3NDA3OTA2MTIxMzMgTS01OS43NjQ1NTYyNTYwMzI0NSAtMjQuMzc5NzM2OTY1MTg1MzY2IEMtNTkuNjk4NjE2NjM0MDczODggLTI0LjQ5MDM5NzkyOTE5MjYzMiwgLTU5LjYzMjY3NzAxMjExNTMxNiAtMjQuNjAxMDU4ODkzMTk5ODk4LCAtNTkuNTUzMDE5ODkxMzEyNjQ0IC0yNC43MzQ3NDA3OTA2MTIxMzMgTS01OS41NTMwMTk4OTEzMTI2NDQgLTI0LjczNDc0MDc5MDYxMjEzMyBDLTU5LjQ3NDcyMzM3OTAxODE5NCAtMjQuODQ0NDAxODc5MTQzOTM0LCAtNTkuMzk2NDI2ODY2NzIzNzQgLTI0Ljk1NDA2Mjk2NzY3NTc0LCAtNTkuMzEyODkwMDQ2OTgxOTcgLTI1LjA3MTA2MzU2MzQ0ODM0IE0tNTkuNTUzMDE5ODkxMzEyNjQ0IC0yNC43MzQ3NDA3OTA2MTIxMzMgQy01OS41MDQ1Nzk2OTc4MjI0NyAtMjQuODAyNTg1NTAyODYyNDg4LCAtNTkuNDU2MTM5NTA0MzMyMyAtMjQuODcwNDMwMjE1MTEyODQ3LCAtNTkuMzEyODkwMDQ2OTgxOTcgLTI1LjA3MTA2MzU2MzQ0ODM0IE0tNTkuMzEyODkwMDQ2OTgxOTcgLTI1LjA3MTA2MzU2MzQ0ODM0IEMtNTkuMjQ0MDkzMTg2OTk1MjYgLTI1LjE1MjI5MTg2MjU0MzY0MiwgLTU5LjE3NTI5NjMyNzAwODU0NiAtMjUuMjMzNTIwMTYxNjM4OTQzLCAtNTkuMDQ1ODA3MDUzMzY1NjYgLTI1LjM4NjQwNzg1ODEyODcwNiBNLTU5LjMxMjg5MDA0Njk4MTk3IC0yNS4wNzEwNjM1NjM0NDgzNCBDLTU5LjIxNDgxNjAwNzI3MDc3IC0yNS4xODY4NTkzNjMzMjQ3MzIsIC01OS4xMTY3NDE5Njc1NTk1OCAtMjUuMzAyNjU1MTYzMjAxMTIzLCAtNTkuMDQ1ODA3MDUzMzY1NjYgLTI1LjM4NjQwNzg1ODEyODcwNiBNLTU5LjA0NTgwNzA1MzM2NTY2IC0yNS4zODY0MDc4NTgxMjg3MDYgQy01OC45NDYzMDU2MjE3MjExNSAtMjUuNDg1OTA5Mjg5NzczMjE1LCAtNTguODQ2ODA0MTkwMDc2NjQgLTI1LjU4NTQxMDcyMTQxNzcyLCAtNTguNzUzNTk1MzU4MTI4NzA2IC0yNS42Nzg2MTk1NTMzNjU2NTcgTS01OS4wNDU4MDcwNTMzNjU2NiAtMjUuMzg2NDA3ODU4MTI4NzA2IEMtNTguOTU4NzgyNjI2OTY3NjA0IC0yNS40NzM0MzIyODQ1MjY3NiwgLTU4Ljg3MTc1ODIwMDU2OTU1IC0yNS41NjA0NTY3MTA5MjQ4MDcsIC01OC43NTM1OTUzNTgxMjg3MDYgLTI1LjY3ODYxOTU1MzM2NTY1NyBNLTU4Ljc1MzU5NTM1ODEyODcwNiAtMjUuNjc4NjE5NTUzMzY1NjU3IEMtNTguNjMxMjA4MDE4MTQ4NTM0IC0yNS43ODIyNzYzNDI3NjcxMzcsIC01OC41MDg4MjA2NzgxNjgzNiAtMjUuODg1OTMzMTMyMTY4NjE3LCAtNTguNDM4MjUxMDYzNDQ4MzQgLTI1Ljk0NTcwMjU0Njk4MTk2NiBNLTU4Ljc1MzU5NTM1ODEyODcwNiAtMjUuNjc4NjE5NTUzMzY1NjU3IEMtNTguNjc2MTM4MTg1NjgwNTg1IC0yNS43NDQyMjI0MzIwMDgwODcsIC01OC41OTg2ODEwMTMyMzI0NjUgLTI1LjgwOTgyNTMxMDY1MDUyLCAtNTguNDM4MjUxMDYzNDQ4MzQgLTI1Ljk0NTcwMjU0Njk4MTk2NiBNLTU4LjQzODI1MTA2MzQ0ODM0IC0yNS45NDU3MDI1NDY5ODE5NjYgQy01OC4zMzQxMjA3OTEzMDE1IC0yNi4wMjAwNTAxMzIyNzg4NzUsIC01OC4yMjk5OTA1MTkxNTQ2NTQgLTI2LjA5NDM5NzcxNzU3NTc4NCwgLTU4LjEwMTkyODI5MDYxMjEzNiAtMjYuMTg1ODMyMzkxMzEyNjQ0IE0tNTguNDM4MjUxMDYzNDQ4MzQgLTI1Ljk0NTcwMjU0Njk4MTk2NiBDLTU4LjMzMzQxNTUwNzUwNTUxIC0yNi4wMjA1NTM2OTUyMzAzNTgsIC01OC4yMjg1Nzk5NTE1NjI2NyAtMjYuMDk1NDA0ODQzNDc4NzUsIC01OC4xMDE5MjgyOTA2MTIxMzYgLTI2LjE4NTgzMjM5MTMxMjY0NCBNLTU4LjEwMTkyODI5MDYxMjEzNiAtMjYuMTg1ODMyMzkxMzEyNjQ0IEMtNTcuOTg1NTUyMjI5Mjg0MDk2IC0yNi4yNTUxNzc0NzIwOTkxNjMsIC01Ny44NjkxNzYxNjc5NTYwNTYgLTI2LjMyNDUyMjU1Mjg4NTY4LCAtNTcuNzQ2OTI0NDY1MTg1MzY2IC0yNi4zOTczNjg3NTYwMzI0NDYgTS01OC4xMDE5MjgyOTA2MTIxMzYgLTI2LjE4NTgzMjM5MTMxMjY0NCBDLTU3Ljk2MjYxMzUyNTg2NDYgLTI2LjI2ODg0NTk3MjUwNzMzLCAtNTcuODIzMjk4NzYxMTE3MDYgLTI2LjM1MTg1OTU1MzcwMjAxMywgLTU3Ljc0NjkyNDQ2NTE4NTM2NiAtMjYuMzk3MzY4NzU2MDMyNDQ2IE0tNTcuNzQ2OTI0NDY1MTg1MzY2IC0yNi4zOTczNjg3NTYwMzI0NDYgQy01Ny42MDI1MDg3OTcyNDUwMyAtMjYuNDY3OTY5Mjc0MDg1MjEzLCAtNTcuNDU4MDkzMTI5MzA0NjkgLTI2LjUzODU2OTc5MjEzNzk4NCwgLTU3LjM3NTY2NDYyMzI2NDg0NiAtMjYuNTc4ODY2NjMzMjc1Mjg2IE0tNTcuNzQ2OTI0NDY1MTg1MzY2IC0yNi4zOTczNjg3NTYwMzI0NDYgQy01Ny42MTMwMjA2NzMwNjY4MDYgLTI2LjQ2MjgzMDMzMTEzNDU3NiwgLTU3LjQ3OTExNjg4MDk0ODI1NCAtMjYuNTI4MjkxOTA2MjM2NzAzLCAtNTcuMzc1NjY0NjIzMjY0ODQ2IC0yNi41Nzg4NjY2MzMyNzUyODYgTS01Ny4zNzU2NjQ2MjMyNjQ4NDYgLTI2LjU3ODg2NjYzMzI3NTI4NiBDLTU3LjI2MTk3MTEzNjQzNzAzIC0yNi42MjMyMjk5Njk0OTk5MzUsIC01Ny4xNDgyNzc2NDk2MDkyMyAtMjYuNjY3NTkzMzA1NzI0NTgsIC01Ni45OTA2ODQ4NDYwMjM0MiAtMjYuNzI5MDg2MjA4NTAzMTczIE0tNTcuMzc1NjY0NjIzMjY0ODQ2IC0yNi41Nzg4NjY2MzMyNzUyODYgQy01Ny4yNTk3MzY4Nzc0MjY3MiAtMjYuNjI0MTAxNzgwMDcxMDg2LCAtNTcuMTQzODA5MTMxNTg4NTkgLTI2LjY2OTMzNjkyNjg2Njg4MywgLTU2Ljk5MDY4NDg0NjAyMzQyIC0yNi43MjkwODYyMDg1MDMxNzMgTS01Ni45OTA2ODQ4NDYwMjM0MiAtMjYuNzI5MDg2MjA4NTAzMTczIEMtNTYuODMzMDQ3MzMxNjM3Nzg2IC0yNi43NzYwMTY5Mjk4OTYzNSwgLTU2LjY3NTQwOTgxNzI1MjE0NSAtMjYuODIyOTQ3NjUxMjg5NTI1LCAtNTYuNTk0NjE0OTM1NzAzOTk0IC0yNi44NDcwMDEzMjk2OTY2NTMgTS01Ni45OTA2ODQ4NDYwMjM0MiAtMjYuNzI5MDg2MjA4NTAzMTczIEMtNTYuOTA4MTY4MjY0ODA3NDkgLTI2Ljc1MzY1MjQ1OTEwOTU0LCAtNTYuODI1NjUxNjgzNTkxNTYgLTI2Ljc3ODIxODcwOTcxNTkwNSwgLTU2LjU5NDYxNDkzNTcwMzk5NCAtMjYuODQ3MDAxMzI5Njk2NjUzIE0tNTYuNTk0NjE0OTM1NzAzOTk0IC0yNi44NDcwMDEzMjk2OTY2NTMgQy01Ni40OTE3NzM4ODQ4OTkyNDYgLTI2Ljg2ODU2NDgzMDQ1MTE1LCAtNTYuMzg4OTMyODM0MDk0NDkgLTI2Ljg5MDEyODMzMTIwNTY1LCAtNTYuMTkwMTYwNDUxNDAzNjcgLTI2LjkzMTgwNjUxNzAxMzYxMiBNLTU2LjU5NDYxNDkzNTcwMzk5NCAtMjYuODQ3MDAxMzI5Njk2NjUzIEMtNTYuNDM5NDQxOTE2NjE2MTI2IC0yNi44Nzk1Mzc2OTAzMDM1ODIsIC01Ni4yODQyNjg4OTc1MjgyNSAtMjYuOTEyMDc0MDUwOTEwNTEyLCAtNTYuMTkwMTYwNDUxNDAzNjcgLTI2LjkzMTgwNjUxNzAxMzYxMiBNLTU2LjE5MDE2MDQ1MTQwMzY3IC0yNi45MzE4MDY1MTcwMTM2MTIgQy01Ni4wMjk3NzYyNjk4MDI4MyAtMjYuOTUxNzk4Mzg0NDAxMzEsIC01NS44NjkzOTIwODgyMDIgLTI2Ljk3MTc5MDI1MTc4OTAxLCAtNTUuNzgwMDg0MjI3MzYxNjYgLTI2Ljk4MjkyMjQ2NTAzMzM0NyBNLTU2LjE5MDE2MDQ1MTQwMzY3IC0yNi45MzE4MDY1MTcwMTM2MTIgQy01Ni4wNTk0MzcxNzg5NDQ2OSAtMjYuOTQ4MTAxMTU1OTMyNDk1LCAtNTUuOTI4NzEzOTA2NDg1NzA0IC0yNi45NjQzOTU3OTQ4NTEzNzcsIC01NS43ODAwODQyMjczNjE2NiAtMjYuOTgyOTIyNDY1MDMzMzQ3IE0tNTUuNzgwMDg0MjI3MzYxNjYgLTI2Ljk4MjkyMjQ2NTAzMzM0NyBDLTU1LjY2NTIzNDQ0MDkyNDg4IC0yNi45ODc2NzI2ODczMzc4OSwgLTU1LjU1MDM4NDY1NDQ4ODA5IC0yNi45OTI0MjI5MDk2NDI0MzYsIC01NS4zNjcxODc1IC0yNyBNLTU1Ljc4MDA4NDIyNzM2MTY2IC0yNi45ODI5MjI0NjUwMzMzNDcgQy01NS42NjM0NzM5NDM5NzU3NCAtMjYuOTg3NzQ1NTAyMDMwMzIsIC01NS41NDY4NjM2NjA1ODk4MiAtMjYuOTkyNTY4NTM5MDI3MjkyLCAtNTUuMzY3MTg3NSAtMjcgTS01NS4zNjcxODc1IC0yNyBDLTU1LjM2NzE4NzUgLTI3LCAtNTUuMzY3MTg3NSAtMjcsIC01NS4zNjcxODc1IC0yNyBNLTU1LjM2NzE4NzUgLTI3IEMtNTUuMzY3MTg3NSAtMjcsIC01NS4zNjcxODc1IC0yNywgLTU1LjM2NzE4NzUgLTI3IiBzdHJva2U9IiM5MzcwREIiIHN0cm9rZS13aWR0aD0iMS4zIiBmaWxsPSJub25lIiBzdHJva2UtZGFzaGFycmF5PSIwIDAiIHN0eWxlPSIiLz48L2c+PGcgY2xhc3M9ImxhYmVsIiBzdHlsZT0iIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtNDUuMzY3MTg3NSwgLTEyKSI+PHJlY3QvPjxmb3JlaWduT2JqZWN0IHdpZHRoPSI5MC43MzQzNzUiIGhlaWdodD0iMjQiPjxkaXYgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGh0bWwiIHN0eWxlPSJkaXNwbGF5OiB0YWJsZS1jZWxsOyB3aGl0ZS1zcGFjZTogbm93cmFwOyBsaW5lLWhlaWdodDogMS41OyBtYXgtd2lkdGg6IDIwMHB4OyB0ZXh0LWFsaWduOiBjZW50ZXI7Ij48c3BhbiBjbGFzcz0ibm9kZUxhYmVsIj48cD5HbyBzaG9wcGluZzwvcD48L3NwYW4+PC9kaXY+PC9mb3JlaWduT2JqZWN0PjwvZz48L2c+PGcgY2xhc3M9Im5vZGUgZGVmYXVsdCIgaWQ9ImZsb3djaGFydC1DLTMiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDIyMS44NTE1NjI1LCAzMTAuNTc4MTI1KSI+PHBvbHlnb24gcG9pbnRzPSI3MC41NzgxMjUsMCAxNDEuMTU2MjUsLTcwLjU3ODEyNSA3MC41NzgxMjUsLTE0MS4xNTYyNSAwLC03MC41NzgxMjUiIGNsYXNzPSJsYWJlbC1jb250YWluZXIiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC03MC4wNzgxMjUsIDcwLjU3ODEyNSkiLz48ZyBjbGFzcz0ibGFiZWwiIHN0eWxlPSIiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC00My41NzgxMjUsIC0xMikiPjxyZWN0Lz48Zm9yZWlnbk9iamVjdCB3aWR0aD0iODcuMTU2MjUiIGhlaWdodD0iMjQiPjxkaXYgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGh0bWwiIHN0eWxlPSJkaXNwbGF5OiB0YWJsZS1jZWxsOyB3aGl0ZS1zcGFjZTogbm93cmFwOyBsaW5lLWhlaWdodDogMS41OyBtYXgtd2lkdGg6IDIwMHB4OyB0ZXh0LWFsaWduOiBjZW50ZXI7Ij48c3BhbiBjbGFzcz0ibm9kZUxhYmVsIj48cD5MZXQgbWUgdGhpbms8L3A+PC9zcGFuPjwvZGl2PjwvZm9yZWlnbk9iamVjdD48L2c+PC9nPjxnIGNsYXNzPSJub2RlIGRlZmF1bHQiIGlkPSJmbG93Y2hhcnQtRC01IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg2Mi40Njg3NSwgNDgyLjE1NjI1KSI+PHJlY3QgY2xhc3M9ImJhc2ljIGxhYmVsLWNvbnRhaW5lciIgc3R5bGU9IiIgeD0iLTU0LjQ2ODc1IiB5PSItMjciIHdpZHRoPSIxMDguOTM3NSIgaGVpZ2h0PSI1NCIvPjxnIGNsYXNzPSJsYWJlbCIgc3R5bGU9IiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTI0LjQ2ODc1LCAtMTIpIj48cmVjdC8+PGZvcmVpZ25PYmplY3Qgd2lkdGg9IjQ4LjkzNzUiIGhlaWdodD0iMjQiPjxkaXYgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGh0bWwiIHN0eWxlPSJkaXNwbGF5OiB0YWJsZS1jZWxsOyB3aGl0ZS1zcGFjZTogbm93cmFwOyBsaW5lLWhlaWdodDogMS41OyBtYXgtd2lkdGg6IDIwMHB4OyB0ZXh0LWFsaWduOiBjZW50ZXI7Ij48c3BhbiBjbGFzcz0ibm9kZUxhYmVsIj48cD5MYXB0b3A8L3A+PC9zcGFuPjwvZGl2PjwvZm9yZWlnbk9iamVjdD48L2c+PC9nPjxnIGNsYXNzPSJub2RlIGRlZmF1bHQiIGlkPSJmbG93Y2hhcnQtRS03IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyMjEuODUxNTYyNSwgNDgyLjE1NjI1KSI+PHJlY3QgY2xhc3M9ImJhc2ljIGxhYmVsLWNvbnRhaW5lciIgc3R5bGU9IiIgeD0iLTU0LjkxNDA2MjUiIHk9Ii0yNyIgd2lkdGg9IjEwOS44MjgxMjUiIGhlaWdodD0iNTQiLz48ZyBjbGFzcz0ibGFiZWwiIHN0eWxlPSIiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yNC45MTQwNjI1LCAtMTIpIj48cmVjdC8+PGZvcmVpZ25PYmplY3Qgd2lkdGg9IjQ5LjgyODEyNSIgaGVpZ2h0PSIyNCI+PGRpdiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94aHRtbCIgc3R5bGU9ImRpc3BsYXk6IHRhYmxlLWNlbGw7IHdoaXRlLXNwYWNlOiBub3dyYXA7IGxpbmUtaGVpZ2h0OiAxLjU7IG1heC13aWR0aDogMjAwcHg7IHRleHQtYWxpZ246IGNlbnRlcjsiPjxzcGFuIGNsYXNzPSJub2RlTGFiZWwiPjxwPmlQaG9uZTwvcD48L3NwYW4+PC9kaXY+PC9mb3JlaWduT2JqZWN0PjwvZz48L2c+PGcgY2xhc3M9Im5vZGUgZGVmYXVsdCIgaWQ9ImZsb3djaGFydC1GLTkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDM3OS44ODI4MTI1LCA0ODIuMTU2MjUpIj48cmVjdCBjbGFzcz0iYmFzaWMgbGFiZWwtY29udGFpbmVyIiBzdHlsZT0iIiB4PSItNTMuMTE3MTg3NSIgeT0iLTI3IiB3aWR0aD0iMTA2LjIzNDM3NSIgaGVpZ2h0PSI1NCIvPjxnIGNsYXNzPSJsYWJlbCIgc3R5bGU9IiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTIzLjExNzE4NzUsIC0xMikiPjxyZWN0Lz48Zm9yZWlnbk9iamVjdCB3aWR0aD0iNDYuMjM0Mzc1IiBoZWlnaHQ9IjI0Ij48ZGl2IHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hodG1sIiBzdHlsZT0iZGlzcGxheTogdGFibGUtY2VsbDsgd2hpdGUtc3BhY2U6IG5vd3JhcDsgbGluZS1oZWlnaHQ6IDEuNTsgbWF4LXdpZHRoOiAyMDBweDsgdGV4dC1hbGlnbjogY2VudGVyOyI+PHNwYW4gY2xhc3M9Im5vZGVMYWJlbCI+PHA+PGkgY2xhc3M9ImZhIGZhLWNhciI+PC9pPiBDYXI8L3A+PC9zcGFuPjwvZGl2PjwvZm9yZWlnbk9iamVjdD48L2c+PC9nPjwvZz48L2c+PC9nPjwvc3ZnPg==", view.getContent()); assertEquals("image/svg+xml", view.getContentType()); } diff --git a/structurizr-import/src/test/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporterTests.java b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporterTests.java index 7ca514921..6081c96e5 100644 --- a/structurizr-import/src/test/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporterTests.java +++ b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporterTests.java @@ -64,7 +64,7 @@ public void importDiagram_AsInlinePNG() throws Exception { new PlantUMLImporter().importDiagram(view, new File("./src/test/resources/diagrams/plantuml/with-title.puml")); assertEquals("Sequence diagram example", view.getTitle()); - assertEquals("data:image/png;base64,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", view.getContent()); + assertEquals("data:image/png;base64,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", view.getContent()); assertEquals("image/png", view.getContentType()); } From c514f4a41471a60c1673485531230c857756a6f0 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 19 Oct 2025 11:54:31 +0100 Subject: [PATCH 697/717] Adds a specific exception for HttpClient errors. --- .../src/main/java/com/structurizr/http/HttpClient.java | 6 +++--- .../java/com/structurizr/http/HttpClientException.java | 9 +++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 structurizr-client/src/main/java/com/structurizr/http/HttpClientException.java diff --git a/structurizr-client/src/main/java/com/structurizr/http/HttpClient.java b/structurizr-client/src/main/java/com/structurizr/http/HttpClient.java index 69e2db865..b82a75a65 100644 --- a/structurizr-client/src/main/java/com/structurizr/http/HttpClient.java +++ b/structurizr-client/src/main/java/com/structurizr/http/HttpClient.java @@ -63,7 +63,7 @@ public RemoteContent get(String url) { */ public RemoteContent get(String url, boolean cache) { if (!isAllowed(url)) { - throw new RuntimeException("Access to " + url + " is not permitted"); + throw new HttpClientException("Access to " + url + " is not permitted"); } RemoteContent remoteContent = contentCache.get(url); @@ -97,10 +97,10 @@ public RemoteContent get(String url, boolean cache) { contentCache.put(url, remoteContent); } } else { - throw new RuntimeException("The content from " + url + " could not be loaded: HTTP status=" + httpStatus); + throw new HttpClientException("The content from " + url + " could not be loaded: HTTP status=" + httpStatus); } } catch (Exception ioe) { - throw new RuntimeException("The content from " + url + " could not be loaded: " + ioe.getMessage()); + throw new HttpClientException("The content from " + url + " could not be loaded: " + ioe.getMessage()); } } diff --git a/structurizr-client/src/main/java/com/structurizr/http/HttpClientException.java b/structurizr-client/src/main/java/com/structurizr/http/HttpClientException.java new file mode 100644 index 000000000..d298f3223 --- /dev/null +++ b/structurizr-client/src/main/java/com/structurizr/http/HttpClientException.java @@ -0,0 +1,9 @@ +package com.structurizr.http; + +public class HttpClientException extends RuntimeException { + + public HttpClientException(String message) { + super(message); + } + +} \ No newline at end of file From 10895ef7d3e842f02c2a52680b7788beb1fdfa58 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 19 Oct 2025 13:37:53 +0100 Subject: [PATCH 698/717] . --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 0f3098447..4a5a08bdd 100644 --- a/changelog.md +++ b/changelog.md @@ -20,7 +20,7 @@ - structurizr-dsl: PlantUML, Mermaid, and Kroki image views can now be defined by an inline source block. - structurizr-dsl: Constants and variables are inherited when extending a DSL workspace. - structurizr-dsl: DSL source is only stored in the JSON workspace when the DSL is deemed as "portable" (i.e. no files, plugins, scripts). -- structurizr-dsl: Deprecates `setRestricted(boolean)` in favour of finer-grained features. +- structurizr-dsl: Deprecates `StructurizrDSLParser.setRestricted(boolean)` in favour of finer-grained features. - structurizr-export: Removes support for deprecated enterprise and location concepts. - structurizr-export: PlantUML exporters - replaces skinparams with styles. - structurizr-export: PlantUML exporters - adds support for dark mode exports. From 35a6a57db645e9e4b8bf2aaf925abba656ddaa86 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 19 Oct 2025 13:38:07 +0100 Subject: [PATCH 699/717] Switch to new staging API. --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 86760cb7a..18adb8a7b 100644 --- a/build.gradle +++ b/build.gradle @@ -56,7 +56,7 @@ subprojects { proj -> repositories { maven { name = "ossrh" - url = "https://oss.sonatype.org/service/local/staging/deploy/maven2/" + url = "https://ossrh-staging-api.central.sonatype.com/service/local/staging/deploy/maven2/" credentials { username = findProperty('ossrhUsername') password = findProperty('ossrhPassword') From 62baaef90651b952d5540d8d68dae7f1fbe0ac3c Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 19 Oct 2025 17:12:21 +0100 Subject: [PATCH 700/717] Clean up, bump some dependencies, fixes tests. --- .gitignore | 1 + structurizr-client/build.gradle | 9 --------- .../com/structurizr/api/BackwardsCompatibilityTests.java | 3 +-- .../api/WorkspaceApiClientIntegrationTests.java | 0 .../structurizr/api/WorkspaceRulesValidationTests.java | 2 +- .../test/java/com/structurizr/view/ThemeUtilsTests.java | 9 +++++++-- .../backwardsCompatibility/structurizr-31-workspace.json | 0 .../structurizr-36141-workspace.json | 0 .../structurizr-39459-workspace.json | 0 .../backwardsCompatibility/views-without-order.json | 0 .../ChildDeploymentNodeNamesAreNotUnique.json | 0 .../workspaceValidation/ComponentNamesAreNotUnique.json | 0 ...AssociatedWithComponentViewIsMissingFromTheModel.json | 0 .../workspaceValidation/ContainerNamesAreNotUnique.json | 0 ...ntAssociatedWithDynamicViewIsMissingFromTheModel.json | 0 .../ElementReferencedByViewIsMissingFromTheModel.json | 0 .../PeopleAndSoftwareSystemNamesAreNotUnique.json | 0 .../RelationshipDescriptionsAreNotUnique.json | 0 ...elationshipReferencedByViewIsMissingFromTheModel.json | 0 ...AssociatedWithContainerViewIsMissingFromTheModel.json | 0 ...ssociatedWithDeploymentViewIsMissingFromTheModel.json | 0 ...ciatedWithSystemContextViewIsMissingFromTheModel.json | 0 .../TopLevelDeploymentNodeNamesAreNotUnique.json | 0 ...sAreNotUniqueButTheyExistInDifferentEnvironments.json | 0 ...ociatedWithFilteredViewIsMissingFromTheWorkspace.json | 0 .../workspaceValidation/ViewKeysAreNotUnique.json | 0 structurizr-dsl/build.gradle | 2 +- .../src/test/java/com/structurizr/dsl/DslTests.java | 2 +- .../com/structurizr/dsl/ImageViewContentParserTests.java | 4 ++++ 29 files changed, 16 insertions(+), 16 deletions(-) rename structurizr-client/src/{integrationTest => test}/java/com/structurizr/api/BackwardsCompatibilityTests.java (93%) rename structurizr-client/src/{integrationTest => test}/java/com/structurizr/api/WorkspaceApiClientIntegrationTests.java (100%) rename structurizr-client/src/{integrationTest => test}/java/com/structurizr/api/WorkspaceRulesValidationTests.java (99%) rename structurizr-client/src/{integrationTest => test}/resources/backwardsCompatibility/structurizr-31-workspace.json (100%) rename structurizr-client/src/{integrationTest => test}/resources/backwardsCompatibility/structurizr-36141-workspace.json (100%) rename structurizr-client/src/{integrationTest => test}/resources/backwardsCompatibility/structurizr-39459-workspace.json (100%) rename structurizr-client/src/{integrationTest => test}/resources/backwardsCompatibility/views-without-order.json (100%) rename structurizr-client/src/{integrationTest => test}/resources/workspaceValidation/ChildDeploymentNodeNamesAreNotUnique.json (100%) rename structurizr-client/src/{integrationTest => test}/resources/workspaceValidation/ComponentNamesAreNotUnique.json (100%) rename structurizr-client/src/{integrationTest => test}/resources/workspaceValidation/ContainerAssociatedWithComponentViewIsMissingFromTheModel.json (100%) rename structurizr-client/src/{integrationTest => test}/resources/workspaceValidation/ContainerNamesAreNotUnique.json (100%) rename structurizr-client/src/{integrationTest => test}/resources/workspaceValidation/ElementAssociatedWithDynamicViewIsMissingFromTheModel.json (100%) rename structurizr-client/src/{integrationTest => test}/resources/workspaceValidation/ElementReferencedByViewIsMissingFromTheModel.json (100%) rename structurizr-client/src/{integrationTest => test}/resources/workspaceValidation/PeopleAndSoftwareSystemNamesAreNotUnique.json (100%) rename structurizr-client/src/{integrationTest => test}/resources/workspaceValidation/RelationshipDescriptionsAreNotUnique.json (100%) rename structurizr-client/src/{integrationTest => test}/resources/workspaceValidation/RelationshipReferencedByViewIsMissingFromTheModel.json (100%) rename structurizr-client/src/{integrationTest => test}/resources/workspaceValidation/SoftwareSystemAssociatedWithContainerViewIsMissingFromTheModel.json (100%) rename structurizr-client/src/{integrationTest => test}/resources/workspaceValidation/SoftwareSystemAssociatedWithDeploymentViewIsMissingFromTheModel.json (100%) rename structurizr-client/src/{integrationTest => test}/resources/workspaceValidation/SoftwareSystemAssociatedWithSystemContextViewIsMissingFromTheModel.json (100%) rename structurizr-client/src/{integrationTest => test}/resources/workspaceValidation/TopLevelDeploymentNodeNamesAreNotUnique.json (100%) rename structurizr-client/src/{integrationTest => test}/resources/workspaceValidation/TopLevelDeploymentNodeNamesAreNotUniqueButTheyExistInDifferentEnvironments.json (100%) rename structurizr-client/src/{integrationTest => test}/resources/workspaceValidation/ViewAssociatedWithFilteredViewIsMissingFromTheWorkspace.json (100%) rename structurizr-client/src/{integrationTest => test}/resources/workspaceValidation/ViewKeysAreNotUnique.json (100%) diff --git a/.gitignore b/.gitignore index a5e1d9c61..4a491ce38 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,7 @@ build out bin **/bin/ +**/target/ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* diff --git a/structurizr-client/build.gradle b/structurizr-client/build.gradle index a868ccf81..fd7c55622 100644 --- a/structurizr-client/build.gradle +++ b/structurizr-client/build.gradle @@ -6,13 +6,4 @@ dependencies { api 'org.apache.httpcomponents.client5:httpclient5:5.5.1' api 'javax.xml.bind:jaxb-api:2.4.0-b180830.0359' -} - -sourceSets { - test { - java { - srcDir 'src/test/java' - srcDir 'src/integrationTest/java' - } - } } \ No newline at end of file diff --git a/structurizr-client/src/integrationTest/java/com/structurizr/api/BackwardsCompatibilityTests.java b/structurizr-client/src/test/java/com/structurizr/api/BackwardsCompatibilityTests.java similarity index 93% rename from structurizr-client/src/integrationTest/java/com/structurizr/api/BackwardsCompatibilityTests.java rename to structurizr-client/src/test/java/com/structurizr/api/BackwardsCompatibilityTests.java index ecdc97a12..58933c9c0 100644 --- a/structurizr-client/src/integrationTest/java/com/structurizr/api/BackwardsCompatibilityTests.java +++ b/structurizr-client/src/test/java/com/structurizr/api/BackwardsCompatibilityTests.java @@ -3,7 +3,6 @@ import com.structurizr.Workspace; import com.structurizr.documentation.Format; import com.structurizr.documentation.Section; -import com.structurizr.model.Location; import com.structurizr.util.WorkspaceUtils; import org.junit.jupiter.api.Test; @@ -13,7 +12,7 @@ class BackwardsCompatibilityTests { - private static final File PATH_TO_WORKSPACE_FILES = new File("./src/integrationTest/resources/backwardsCompatibility"); + private static final File PATH_TO_WORKSPACE_FILES = new File("./src/test/resources/backwardsCompatibility"); @Test void test() throws Exception { diff --git a/structurizr-client/src/integrationTest/java/com/structurizr/api/WorkspaceApiClientIntegrationTests.java b/structurizr-client/src/test/java/com/structurizr/api/WorkspaceApiClientIntegrationTests.java similarity index 100% rename from structurizr-client/src/integrationTest/java/com/structurizr/api/WorkspaceApiClientIntegrationTests.java rename to structurizr-client/src/test/java/com/structurizr/api/WorkspaceApiClientIntegrationTests.java diff --git a/structurizr-client/src/integrationTest/java/com/structurizr/api/WorkspaceRulesValidationTests.java b/structurizr-client/src/test/java/com/structurizr/api/WorkspaceRulesValidationTests.java similarity index 99% rename from structurizr-client/src/integrationTest/java/com/structurizr/api/WorkspaceRulesValidationTests.java rename to structurizr-client/src/test/java/com/structurizr/api/WorkspaceRulesValidationTests.java index 85f5ea596..6ff8ebe84 100644 --- a/structurizr-client/src/integrationTest/java/com/structurizr/api/WorkspaceRulesValidationTests.java +++ b/structurizr-client/src/test/java/com/structurizr/api/WorkspaceRulesValidationTests.java @@ -10,7 +10,7 @@ public class WorkspaceRulesValidationTests { - private static final File PATH_TO_WORKSPACE_FILES = new File("./src/integrationTest/resources/workspaceValidation"); + private static final File PATH_TO_WORKSPACE_FILES = new File("./src/test/resources/workspaceValidation"); @Test void exceptionThrown_WhenViewKeysAreNotUnique() throws Exception { diff --git a/structurizr-client/src/test/java/com/structurizr/view/ThemeUtilsTests.java b/structurizr-client/src/test/java/com/structurizr/view/ThemeUtilsTests.java index 1ecae3995..ae4fe5a30 100644 --- a/structurizr-client/src/test/java/com/structurizr/view/ThemeUtilsTests.java +++ b/structurizr-client/src/test/java/com/structurizr/view/ThemeUtilsTests.java @@ -1,6 +1,7 @@ package com.structurizr.view; import com.structurizr.Workspace; +import com.structurizr.http.HttpClient; import com.structurizr.model.Relationship; import com.structurizr.model.SoftwareSystem; import com.structurizr.model.Tags; @@ -31,7 +32,9 @@ void loadThemes_LoadsThemesWhenThemesAreDefined() throws Exception { softwareSystem.addTags("Amazon Web Services - Alexa For Business"); workspace.getViews().getConfiguration().setThemes("https://static.structurizr.com/themes/amazon-web-services-2020.04.30/theme.json"); - ThemeUtils.loadThemes(workspace); + HttpClient httpClient = new HttpClient(); + httpClient.allow(".*"); + ThemeUtils.loadThemes(workspace, httpClient); // there should still be zero styles in the workspace assertEquals(0, workspace.getViews().getConfiguration().getStyles().getElements().size()); @@ -146,7 +149,9 @@ void loadThemes_ReplacesRelativeIconReferences() throws Exception { softwareSystem.addTags("Amazon Web Services - Alexa For Business"); workspace.getViews().getConfiguration().setThemes("https://static.structurizr.com/themes/amazon-web-services-2020.04.30/theme.json"); - ThemeUtils.loadThemes(workspace); + HttpClient httpClient = new HttpClient(); + httpClient.allow(".*"); + ThemeUtils.loadThemes(workspace, httpClient); // there should still be zero styles in the workspace assertEquals(0, workspace.getViews().getConfiguration().getStyles().getElements().size()); diff --git a/structurizr-client/src/integrationTest/resources/backwardsCompatibility/structurizr-31-workspace.json b/structurizr-client/src/test/resources/backwardsCompatibility/structurizr-31-workspace.json similarity index 100% rename from structurizr-client/src/integrationTest/resources/backwardsCompatibility/structurizr-31-workspace.json rename to structurizr-client/src/test/resources/backwardsCompatibility/structurizr-31-workspace.json diff --git a/structurizr-client/src/integrationTest/resources/backwardsCompatibility/structurizr-36141-workspace.json b/structurizr-client/src/test/resources/backwardsCompatibility/structurizr-36141-workspace.json similarity index 100% rename from structurizr-client/src/integrationTest/resources/backwardsCompatibility/structurizr-36141-workspace.json rename to structurizr-client/src/test/resources/backwardsCompatibility/structurizr-36141-workspace.json diff --git a/structurizr-client/src/integrationTest/resources/backwardsCompatibility/structurizr-39459-workspace.json b/structurizr-client/src/test/resources/backwardsCompatibility/structurizr-39459-workspace.json similarity index 100% rename from structurizr-client/src/integrationTest/resources/backwardsCompatibility/structurizr-39459-workspace.json rename to structurizr-client/src/test/resources/backwardsCompatibility/structurizr-39459-workspace.json diff --git a/structurizr-client/src/integrationTest/resources/backwardsCompatibility/views-without-order.json b/structurizr-client/src/test/resources/backwardsCompatibility/views-without-order.json similarity index 100% rename from structurizr-client/src/integrationTest/resources/backwardsCompatibility/views-without-order.json rename to structurizr-client/src/test/resources/backwardsCompatibility/views-without-order.json diff --git a/structurizr-client/src/integrationTest/resources/workspaceValidation/ChildDeploymentNodeNamesAreNotUnique.json b/structurizr-client/src/test/resources/workspaceValidation/ChildDeploymentNodeNamesAreNotUnique.json similarity index 100% rename from structurizr-client/src/integrationTest/resources/workspaceValidation/ChildDeploymentNodeNamesAreNotUnique.json rename to structurizr-client/src/test/resources/workspaceValidation/ChildDeploymentNodeNamesAreNotUnique.json diff --git a/structurizr-client/src/integrationTest/resources/workspaceValidation/ComponentNamesAreNotUnique.json b/structurizr-client/src/test/resources/workspaceValidation/ComponentNamesAreNotUnique.json similarity index 100% rename from structurizr-client/src/integrationTest/resources/workspaceValidation/ComponentNamesAreNotUnique.json rename to structurizr-client/src/test/resources/workspaceValidation/ComponentNamesAreNotUnique.json diff --git a/structurizr-client/src/integrationTest/resources/workspaceValidation/ContainerAssociatedWithComponentViewIsMissingFromTheModel.json b/structurizr-client/src/test/resources/workspaceValidation/ContainerAssociatedWithComponentViewIsMissingFromTheModel.json similarity index 100% rename from structurizr-client/src/integrationTest/resources/workspaceValidation/ContainerAssociatedWithComponentViewIsMissingFromTheModel.json rename to structurizr-client/src/test/resources/workspaceValidation/ContainerAssociatedWithComponentViewIsMissingFromTheModel.json diff --git a/structurizr-client/src/integrationTest/resources/workspaceValidation/ContainerNamesAreNotUnique.json b/structurizr-client/src/test/resources/workspaceValidation/ContainerNamesAreNotUnique.json similarity index 100% rename from structurizr-client/src/integrationTest/resources/workspaceValidation/ContainerNamesAreNotUnique.json rename to structurizr-client/src/test/resources/workspaceValidation/ContainerNamesAreNotUnique.json diff --git a/structurizr-client/src/integrationTest/resources/workspaceValidation/ElementAssociatedWithDynamicViewIsMissingFromTheModel.json b/structurizr-client/src/test/resources/workspaceValidation/ElementAssociatedWithDynamicViewIsMissingFromTheModel.json similarity index 100% rename from structurizr-client/src/integrationTest/resources/workspaceValidation/ElementAssociatedWithDynamicViewIsMissingFromTheModel.json rename to structurizr-client/src/test/resources/workspaceValidation/ElementAssociatedWithDynamicViewIsMissingFromTheModel.json diff --git a/structurizr-client/src/integrationTest/resources/workspaceValidation/ElementReferencedByViewIsMissingFromTheModel.json b/structurizr-client/src/test/resources/workspaceValidation/ElementReferencedByViewIsMissingFromTheModel.json similarity index 100% rename from structurizr-client/src/integrationTest/resources/workspaceValidation/ElementReferencedByViewIsMissingFromTheModel.json rename to structurizr-client/src/test/resources/workspaceValidation/ElementReferencedByViewIsMissingFromTheModel.json diff --git a/structurizr-client/src/integrationTest/resources/workspaceValidation/PeopleAndSoftwareSystemNamesAreNotUnique.json b/structurizr-client/src/test/resources/workspaceValidation/PeopleAndSoftwareSystemNamesAreNotUnique.json similarity index 100% rename from structurizr-client/src/integrationTest/resources/workspaceValidation/PeopleAndSoftwareSystemNamesAreNotUnique.json rename to structurizr-client/src/test/resources/workspaceValidation/PeopleAndSoftwareSystemNamesAreNotUnique.json diff --git a/structurizr-client/src/integrationTest/resources/workspaceValidation/RelationshipDescriptionsAreNotUnique.json b/structurizr-client/src/test/resources/workspaceValidation/RelationshipDescriptionsAreNotUnique.json similarity index 100% rename from structurizr-client/src/integrationTest/resources/workspaceValidation/RelationshipDescriptionsAreNotUnique.json rename to structurizr-client/src/test/resources/workspaceValidation/RelationshipDescriptionsAreNotUnique.json diff --git a/structurizr-client/src/integrationTest/resources/workspaceValidation/RelationshipReferencedByViewIsMissingFromTheModel.json b/structurizr-client/src/test/resources/workspaceValidation/RelationshipReferencedByViewIsMissingFromTheModel.json similarity index 100% rename from structurizr-client/src/integrationTest/resources/workspaceValidation/RelationshipReferencedByViewIsMissingFromTheModel.json rename to structurizr-client/src/test/resources/workspaceValidation/RelationshipReferencedByViewIsMissingFromTheModel.json diff --git a/structurizr-client/src/integrationTest/resources/workspaceValidation/SoftwareSystemAssociatedWithContainerViewIsMissingFromTheModel.json b/structurizr-client/src/test/resources/workspaceValidation/SoftwareSystemAssociatedWithContainerViewIsMissingFromTheModel.json similarity index 100% rename from structurizr-client/src/integrationTest/resources/workspaceValidation/SoftwareSystemAssociatedWithContainerViewIsMissingFromTheModel.json rename to structurizr-client/src/test/resources/workspaceValidation/SoftwareSystemAssociatedWithContainerViewIsMissingFromTheModel.json diff --git a/structurizr-client/src/integrationTest/resources/workspaceValidation/SoftwareSystemAssociatedWithDeploymentViewIsMissingFromTheModel.json b/structurizr-client/src/test/resources/workspaceValidation/SoftwareSystemAssociatedWithDeploymentViewIsMissingFromTheModel.json similarity index 100% rename from structurizr-client/src/integrationTest/resources/workspaceValidation/SoftwareSystemAssociatedWithDeploymentViewIsMissingFromTheModel.json rename to structurizr-client/src/test/resources/workspaceValidation/SoftwareSystemAssociatedWithDeploymentViewIsMissingFromTheModel.json diff --git a/structurizr-client/src/integrationTest/resources/workspaceValidation/SoftwareSystemAssociatedWithSystemContextViewIsMissingFromTheModel.json b/structurizr-client/src/test/resources/workspaceValidation/SoftwareSystemAssociatedWithSystemContextViewIsMissingFromTheModel.json similarity index 100% rename from structurizr-client/src/integrationTest/resources/workspaceValidation/SoftwareSystemAssociatedWithSystemContextViewIsMissingFromTheModel.json rename to structurizr-client/src/test/resources/workspaceValidation/SoftwareSystemAssociatedWithSystemContextViewIsMissingFromTheModel.json diff --git a/structurizr-client/src/integrationTest/resources/workspaceValidation/TopLevelDeploymentNodeNamesAreNotUnique.json b/structurizr-client/src/test/resources/workspaceValidation/TopLevelDeploymentNodeNamesAreNotUnique.json similarity index 100% rename from structurizr-client/src/integrationTest/resources/workspaceValidation/TopLevelDeploymentNodeNamesAreNotUnique.json rename to structurizr-client/src/test/resources/workspaceValidation/TopLevelDeploymentNodeNamesAreNotUnique.json diff --git a/structurizr-client/src/integrationTest/resources/workspaceValidation/TopLevelDeploymentNodeNamesAreNotUniqueButTheyExistInDifferentEnvironments.json b/structurizr-client/src/test/resources/workspaceValidation/TopLevelDeploymentNodeNamesAreNotUniqueButTheyExistInDifferentEnvironments.json similarity index 100% rename from structurizr-client/src/integrationTest/resources/workspaceValidation/TopLevelDeploymentNodeNamesAreNotUniqueButTheyExistInDifferentEnvironments.json rename to structurizr-client/src/test/resources/workspaceValidation/TopLevelDeploymentNodeNamesAreNotUniqueButTheyExistInDifferentEnvironments.json diff --git a/structurizr-client/src/integrationTest/resources/workspaceValidation/ViewAssociatedWithFilteredViewIsMissingFromTheWorkspace.json b/structurizr-client/src/test/resources/workspaceValidation/ViewAssociatedWithFilteredViewIsMissingFromTheWorkspace.json similarity index 100% rename from structurizr-client/src/integrationTest/resources/workspaceValidation/ViewAssociatedWithFilteredViewIsMissingFromTheWorkspace.json rename to structurizr-client/src/test/resources/workspaceValidation/ViewAssociatedWithFilteredViewIsMissingFromTheWorkspace.json diff --git a/structurizr-client/src/integrationTest/resources/workspaceValidation/ViewKeysAreNotUnique.json b/structurizr-client/src/test/resources/workspaceValidation/ViewKeysAreNotUnique.json similarity index 100% rename from structurizr-client/src/integrationTest/resources/workspaceValidation/ViewKeysAreNotUnique.json rename to structurizr-client/src/test/resources/workspaceValidation/ViewKeysAreNotUnique.json diff --git a/structurizr-dsl/build.gradle b/structurizr-dsl/build.gradle index 262cdde49..11cd9dd1a 100644 --- a/structurizr-dsl/build.gradle +++ b/structurizr-dsl/build.gradle @@ -5,7 +5,7 @@ dependencies { api project(':structurizr-export') api project(':structurizr-component') - testImplementation 'org.codehaus.groovy:groovy-jsr223:3.0.24' + testImplementation 'org.codehaus.groovy:groovy-jsr223:3.0.25' testImplementation 'org.jetbrains.kotlin:kotlin-scripting-jsr223:1.9.25' testImplementation 'org.jruby:jruby-core:9.4.12.0' diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index fd3284149..1e40f4e02 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -1416,7 +1416,7 @@ void springPetClinic() throws Exception { fail(); } catch (Exception e) { System.out.println(e.getMessage()); - assertTrue(e.getMessage().startsWith("!components is not available when the parser is running in restricted mode")); + assertTrue(e.getMessage().startsWith("!components is not permitted (feature structurizr.feature.dsl.componentfinder is not enabled)")); } File workspaceFile = new File("src/test/resources/dsl/spring-petclinic/workspace.dsl"); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewContentParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewContentParserTests.java index 90c274bc6..40cb94dab 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewContentParserTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/ImageViewContentParserTests.java @@ -6,6 +6,7 @@ import com.structurizr.view.ColorScheme; import com.structurizr.view.ImageView; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; @@ -371,6 +372,7 @@ void test_parseImage_ThrowsAnException_WhenUsingAFileNameInRestrictedMode() { } @Test + @Tag("IntegrationTest") void test_parseImage() { parser = new ImageViewContentParser(); ImageViewDslContext context = new ImageViewDslContext(imageView); @@ -382,6 +384,7 @@ void test_parseImage() { } @Test + @Tag("IntegrationTest") void test_parseImage_Url_Light() { parser = new ImageViewContentParser(); ImageViewDslContext context = new ImageViewDslContext(imageView); @@ -394,6 +397,7 @@ void test_parseImage_Url_Light() { } @Test + @Tag("IntegrationTest") void test_parseImage_Url_Dark() { parser = new ImageViewContentParser(); ImageViewDslContext context = new ImageViewDslContext(imageView); From b68b29f3a5e25fe1a9a6c05f8030e7449a058748 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Mon, 20 Oct 2025 13:56:15 +0100 Subject: [PATCH 701/717] Moving neo4j example. --- settings.gradle | 3 +- structurizr-neo4j/README.md | 5 --- structurizr-neo4j/build.gradle | 8 ----- .../com/structurizr/neo4j/SimpleLoader.java | 36 ------------------- .../test/java/com/structurizr/Example.java | 28 --------------- 5 files changed, 1 insertion(+), 79 deletions(-) delete mode 100644 structurizr-neo4j/README.md delete mode 100644 structurizr-neo4j/build.gradle delete mode 100644 structurizr-neo4j/src/main/java/com/structurizr/neo4j/SimpleLoader.java delete mode 100644 structurizr-neo4j/src/test/java/com/structurizr/Example.java diff --git a/settings.gradle b/settings.gradle index 092c80c08..4226e5c13 100644 --- a/settings.gradle +++ b/settings.gradle @@ -8,5 +8,4 @@ include 'structurizr-core' include 'structurizr-dsl' include 'structurizr-export' include 'structurizr-import' -include 'structurizr-inspection' -include 'structurizr-neo4j' \ No newline at end of file +include 'structurizr-inspection' \ No newline at end of file diff --git a/structurizr-neo4j/README.md b/structurizr-neo4j/README.md deleted file mode 100644 index c01eb1a5e..000000000 --- a/structurizr-neo4j/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# structurizr-neo4j - -[![Maven Central](https://img.shields.io/maven-central/v/com.structurizr/structurizr-neo4j.svg?label=Maven%20Central)](https://search.maven.org/artifact/com.structurizr/structurizr-neo4j) - -This library provides utilities to import a Structurizr workspace into Neo4j. \ No newline at end of file diff --git a/structurizr-neo4j/build.gradle b/structurizr-neo4j/build.gradle deleted file mode 100644 index 4703501bf..000000000 --- a/structurizr-neo4j/build.gradle +++ /dev/null @@ -1,8 +0,0 @@ -dependencies { - - api project(':structurizr-core') - implementation 'org.neo4j.driver:neo4j-java-driver:5.28.4' - - testImplementation project(':structurizr-client') - -} \ No newline at end of file diff --git a/structurizr-neo4j/src/main/java/com/structurizr/neo4j/SimpleLoader.java b/structurizr-neo4j/src/main/java/com/structurizr/neo4j/SimpleLoader.java deleted file mode 100644 index 727b06c5e..000000000 --- a/structurizr-neo4j/src/main/java/com/structurizr/neo4j/SimpleLoader.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.structurizr.neo4j; - -import com.structurizr.Workspace; -import com.structurizr.model.Element; -import com.structurizr.model.Relationship; -import com.structurizr.util.StringUtils; -import org.neo4j.driver.Driver; -import org.neo4j.driver.SessionConfig; - -public class SimpleLoader { - - public void load(Workspace workspace, Driver driver, String database) { - try (var session = driver.session(SessionConfig.builder().withDatabase(database).build())) { - for (Element element : workspace.getModel().getElements()) { - session.run(String.format( - "CREATE ( :Element { id: '%s', name: \"%s\", type: \"%s\" })", - element.getId(), element.getName(), element.getClass().getSimpleName().toLowerCase() - )); - } - - session.run("CREATE INDEX element_index FOR (n:Element) ON (n.id)"); - - for (Relationship relationship : workspace.getModel().getRelationships()) { - session.run(String.format( - """ - MATCH ( from:Element { id: '%s' } ), ( to:Element { id: '%s' } ) - CREATE (from)-[:HAS_RELATIONSHIP_WITH {role: '%s'}]->(to)""", - relationship.getSource().getId(), - relationship.getDestination().getId(), - !StringUtils.isNullOrEmpty(relationship.getDescription()) ? relationship.getDescription() : "uses" - )); - } - } - } - -} \ No newline at end of file diff --git a/structurizr-neo4j/src/test/java/com/structurizr/Example.java b/structurizr-neo4j/src/test/java/com/structurizr/Example.java deleted file mode 100644 index 4ef9f5290..000000000 --- a/structurizr-neo4j/src/test/java/com/structurizr/Example.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.structurizr; - -import com.structurizr.neo4j.SimpleLoader; -import com.structurizr.util.WorkspaceUtils; -import org.neo4j.driver.AuthTokens; -import org.neo4j.driver.Driver; -import org.neo4j.driver.GraphDatabase; -import org.neo4j.driver.Result; - -import java.io.File; - -public class Example { - - public static void main(String[] args) throws Exception { - Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("workspace.json")); - - try (Driver driver = GraphDatabase.driver("bolt://localhost:7687", AuthTokens.basic("neo4j", "password"))) { - try (var session = driver.session()) { - session.run("DROP DATABASE structurizr IF EXISTS"); - Result result = session.run("CREATE DATABASE structurizr"); - System.out.println(result.consume()); - } - - new SimpleLoader().load(workspace, driver, "structurizr"); - } - } - -} \ No newline at end of file From ea34e48a032789da2f19c4ecd0ca099bea31d7e2 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 21 Oct 2025 11:43:08 +0100 Subject: [PATCH 702/717] structurizr-dsl: Identifiers are no longer stored as lower case in the JSON (`structurizr.dsl.identifier` property on elements and relationships). --- changelog.md | 1 + .../structurizr/dsl/IdentifiersRegister.java | 45 +++++++++---- .../java/com/structurizr/dsl/DslTests.java | 10 +-- .../dsl/IdentifierRegisterTests.java | 66 ++++++++++++++++++- 4 files changed, 102 insertions(+), 20 deletions(-) diff --git a/changelog.md b/changelog.md index 4a5a08bdd..c8d49cf06 100644 --- a/changelog.md +++ b/changelog.md @@ -21,6 +21,7 @@ - structurizr-dsl: Constants and variables are inherited when extending a DSL workspace. - structurizr-dsl: DSL source is only stored in the JSON workspace when the DSL is deemed as "portable" (i.e. no files, plugins, scripts). - structurizr-dsl: Deprecates `StructurizrDSLParser.setRestricted(boolean)` in favour of finer-grained features. +- structurizr-dsl: Identifiers are no longer stored as lower case in the JSON (`structurizr.dsl.identifier` property on elements and relationships). - structurizr-export: Removes support for deprecated enterprise and location concepts. - structurizr-export: PlantUML exporters - replaces skinparams with styles. - structurizr-export: PlantUML exporters - adds support for dark mode exports. diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/IdentifiersRegister.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/IdentifiersRegister.java index 654538752..3ef9f1a37 100644 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/IdentifiersRegister.java +++ b/structurizr-dsl/src/main/java/com/structurizr/dsl/IdentifiersRegister.java @@ -70,8 +70,13 @@ public Set getRelationshipIdentifiers() { * @return an Element, or null if one doesn't exist */ public Element getElement(String identifier) { - identifier = identifier.toLowerCase(); - return elementsByIdentifier.get(identifier); + for (String key : elementsByIdentifier.keySet()) { + if (key.equalsIgnoreCase(identifier)) { + return elementsByIdentifier.get(key); + } + } + + return null; } /** @@ -89,8 +94,6 @@ public void register(String identifier, Element element) { identifier = UUID.randomUUID().toString(); } - identifier = identifier.toLowerCase(); - if (identifierScope == IdentifierScope.Hierarchical) { identifier = calculateHierarchicalIdentifier(identifier, element); } @@ -99,17 +102,17 @@ public void register(String identifier, Element element) { for (String id : elementsByIdentifier.keySet()) { Element e = elementsByIdentifier.get(id); - if (e.equals(element) && !id.equals(identifier)) { + if (e.equals(element) && !id.equalsIgnoreCase(identifier)) { if (id.matches("[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}")) { - throw new RuntimeException("Please assign an identifier to \"" + element.getCanonicalName() + "\" before using it with !ref"); + throw new RuntimeException("Please assign an identifier to \"" + element.getCanonicalName() + "\" before using it"); } else { throw new RuntimeException("The element is already registered with an identifier of \"" + id + "\""); } } } - Element e = elementsByIdentifier.get(identifier); - Relationship r = relationshipsByIdentifier.get(identifier); + Element e = getElement(identifier); + Relationship r = getRelationship(identifier); if ((e == null && r == null) || (e == element)) { elementsByIdentifier.put(identifier, element); @@ -125,8 +128,13 @@ public void register(String identifier, Element element) { * @return a Relationship, or null if one doesn't exist */ public Relationship getRelationship(String identifier) { - identifier = identifier.toLowerCase(); - return relationshipsByIdentifier.get(identifier); + for (String key : relationshipsByIdentifier.keySet()) { + if (key.equalsIgnoreCase(identifier)) { + return relationshipsByIdentifier.get(key); + } + } + + return null; } /** @@ -144,10 +152,21 @@ public void register(String identifier, Relationship relationship) { identifier = UUID.randomUUID().toString(); } - identifier = identifier.toLowerCase(); + // check whether this relationship has already been registered with another identifier + for (String id : relationshipsByIdentifier.keySet()) { + Relationship r = relationshipsByIdentifier.get(id); + + if (r.equals(relationship) && !id.equalsIgnoreCase(identifier)) { + if (id.matches("[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}")) { + throw new RuntimeException("Please assign an identifier to \"" + relationship.getCanonicalName() + "\" before using it"); + } else { + throw new RuntimeException("The relationship is already registered with an identifier of \"" + id + "\""); + } + } + } - Element e = elementsByIdentifier.get(identifier); - Relationship r = relationshipsByIdentifier.get(identifier); + Element e = getElement(identifier); + Relationship r = getRelationship(identifier); if ((e == null && r == null) || (r == relationship)) { relationshipsByIdentifier.put(identifier, relationship); diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java index 1e40f4e02..55e03d01e 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java @@ -1059,18 +1059,20 @@ void test_identifiers() throws Exception { IdentifiersRegister register = parser.getIdentifiersRegister(); assertEquals("user", register.findIdentifier(user)); - assertEquals("softwaresystem", register.findIdentifier(softwareSystem)); - assertEquals("softwaresystem.container", register.findIdentifier(container)); + assertEquals("softwareSystem", register.findIdentifier(softwareSystem)); + assertEquals("softwareSystem.container", register.findIdentifier(container)); assertEquals("rel", register.findIdentifier(relationship)); assertSame(user, register.getElement("user")); assertSame(softwareSystem, register.getElement("softwareSystem")); + assertSame(softwareSystem, register.getElement("softwaresystem")); assertSame(container, register.getElement("softwareSystem.container")); + assertSame(container, register.getElement("softwaresystem.container")); assertSame(relationship, register.getRelationship("rel")); assertEquals("user", user.getProperties().get("structurizr.dsl.identifier")); - assertEquals("softwaresystem", softwareSystem.getProperties().get("structurizr.dsl.identifier")); - assertEquals("softwaresystem.container", container.getProperties().get("structurizr.dsl.identifier")); + assertEquals("softwareSystem", softwareSystem.getProperties().get("structurizr.dsl.identifier")); + assertEquals("softwareSystem.container", container.getProperties().get("structurizr.dsl.identifier")); assertEquals("rel", relationship.getProperties().get("structurizr.dsl.identifier")); assertNull(impliedRelationship.getProperties().get("structurizr.dsl.identifier")); } diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/IdentifierRegisterTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/IdentifierRegisterTests.java index 15936e9ee..8a141ce65 100644 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/IdentifierRegisterTests.java +++ b/structurizr-dsl/src/test/java/com/structurizr/dsl/IdentifierRegisterTests.java @@ -1,10 +1,10 @@ package com.structurizr.dsl; +import com.structurizr.model.Relationship; import com.structurizr.model.SoftwareSystem; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.*; class IdentifierRegisterTests extends AbstractTests { @@ -56,7 +56,67 @@ void test_register_ThrowsAnException_WhenTheElementHasAlreadyBeenRegisteredWithA register.register("x", softwareSystem); fail(); } catch (Exception e) { - assertEquals("Please assign an identifier to \"SoftwareSystem://Software System\" before using it with !ref", e.getMessage()); + assertEquals("Please assign an identifier to \"SoftwareSystem://Software System\" before using it", e.getMessage()); + } + } + + @Test + void test_register_WhenTheElementHasAlreadyBeenRegisteredWithTheSameIdentifierCasedDifferently() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + register.register("SoftwareSystem", softwareSystem); + register.register("softwareSystem", softwareSystem); + register.register("softwaresystem", softwareSystem); + register.register("SOFTWARESYSTEM", softwareSystem); + } + + @Test + void test_getElement() { + SoftwareSystem softwareSystem = model.addSoftwareSystem("Software System"); + register.register("SoftwareSystem", softwareSystem); + + assertSame(softwareSystem, register.getElement("SoftwareSystem")); + assertSame(softwareSystem, register.getElement("softwareSystem")); + assertSame(softwareSystem, register.getElement("softwaresystem")); + assertSame(softwareSystem, register.getElement("SOFTWARESYSTEM")); + } + + @Test + void test_getRelationships() { + SoftwareSystem a = model.addSoftwareSystem("A"); + SoftwareSystem b = model.addSoftwareSystem("B"); + Relationship rel = a.uses(b, "Uses"); + register.register("Rel", rel); + + assertSame(rel, register.getRelationship("Rel")); + assertSame(rel, register.getRelationship("rel")); + assertSame(rel, register.getRelationship("REL")); + } + + @Test + void test_register_ThrowsAnException_WhenTheRelationshipHasAlreadyBeenRegisteredWithADifferentIdentifier() { + SoftwareSystem a = model.addSoftwareSystem("A"); + SoftwareSystem b = model.addSoftwareSystem("B"); + Relationship rel = a.uses(b, "Uses"); + try { + register.register("Rel1", rel); + register.register("Rel2", rel); + fail(); + } catch (Exception e) { + assertEquals("The relationship is already registered with an identifier of \"Rel1\"", e.getMessage()); + } + } + + @Test + void test_register_ThrowsAnException_WhenTheRelationshipHasAlreadyBeenRegisteredWithAnInternalIdentifier() { + SoftwareSystem a = model.addSoftwareSystem("A"); + SoftwareSystem b = model.addSoftwareSystem("B"); + Relationship rel = a.uses(b, "Uses"); + try { + register.register("", rel); + register.register("Rel", rel); + fail(); + } catch (Exception e) { + assertEquals("Please assign an identifier to \"Relationship://SoftwareSystem://A -> SoftwareSystem://B (Uses)\" before using it", e.getMessage()); } } From 471b99c9c786cbd62fb89f48b47b54047ee15bd3 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Wed, 22 Oct 2025 08:42:43 +0100 Subject: [PATCH 703/717] Removes !ref parser code. --- .../java/com/structurizr/dsl/RefParser.java | 52 ------------ .../com/structurizr/dsl/RefParserTests.java | 79 ------------------- 2 files changed, 131 deletions(-) delete mode 100644 structurizr-dsl/src/main/java/com/structurizr/dsl/RefParser.java delete mode 100644 structurizr-dsl/src/test/java/com/structurizr/dsl/RefParserTests.java diff --git a/structurizr-dsl/src/main/java/com/structurizr/dsl/RefParser.java b/structurizr-dsl/src/main/java/com/structurizr/dsl/RefParser.java deleted file mode 100644 index d9df9b1f8..000000000 --- a/structurizr-dsl/src/main/java/com/structurizr/dsl/RefParser.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.structurizr.dsl; - -import com.structurizr.model.ModelItem; -import com.structurizr.model.StaticStructureElement; - -final class RefParser extends AbstractParser { - - private static final String GRAMMAR = "%s "; - - private final static int IDENTIFIER_INDEX = 1; - - ModelItem parse(DslContext context, Tokens tokens) { - // !ref - - if (tokens.hasMoreThan(IDENTIFIER_INDEX)) { - throw new RuntimeException("Too many tokens, expected: " + String.format(GRAMMAR, tokens.get(0))); - } - - if (!tokens.includes(IDENTIFIER_INDEX)) { - throw new RuntimeException("Expected: " + String.format(GRAMMAR, tokens.get(0))); - } - - String s = tokens.get(IDENTIFIER_INDEX); - - ModelItem modelItem; - - if (s.contains("://")) { - modelItem = context.getWorkspace().getModel().getElementWithCanonicalName(s); - } else { - modelItem = context.getElement(s); - - if (modelItem == null) { - modelItem = context.getRelationship(s); - } - } - - if (modelItem == null) { - throw new RuntimeException("An element/relationship identified by \"" + s + "\" could not be found"); - } - - if (context instanceof GroupableDslContext && modelItem instanceof StaticStructureElement) { - GroupableDslContext groupableDslContext = (GroupableDslContext)context; - StaticStructureElement staticStructureElement = (StaticStructureElement)modelItem; - if (groupableDslContext.hasGroup()) { - staticStructureElement.setGroup(groupableDslContext.getGroup().getName()); - } - } - - return modelItem; - } - -} \ No newline at end of file diff --git a/structurizr-dsl/src/test/java/com/structurizr/dsl/RefParserTests.java b/structurizr-dsl/src/test/java/com/structurizr/dsl/RefParserTests.java deleted file mode 100644 index 10e0cf947..000000000 --- a/structurizr-dsl/src/test/java/com/structurizr/dsl/RefParserTests.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.structurizr.dsl; - -import com.structurizr.model.ModelItem; -import com.structurizr.model.Person; -import com.structurizr.model.Relationship; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.*; - -class RefParserTests extends AbstractTests { - - private RefParser parser = new RefParser(); - - @Test - void test_parse_ThrowsAnException_WhenThereAreTooManyTokens() { - try { - parser.parse(context(), tokens("!ref", "name", "tokens")); - fail(); - } catch (Exception e) { - assertEquals("Too many tokens, expected: !ref ", e.getMessage()); - } - } - - @Test - void test_parse_ThrowsAnException_WhenTheIdentifierOrCanonicalNameIsNotSpecified() { - try { - parser.parse(context(), tokens("!extend")); - fail(); - } catch (Exception e) { - assertEquals("Expected: !extend ", e.getMessage()); - } - } - - @Test - void test_parse_ThrowsAnException_WhenTheReferencedElementCannotBeFound() { - try { - parser.parse(context(), tokens("!ref", "Person://User")); - fail(); - } catch (Exception e) { - assertEquals("An element/relationship identified by \"Person://User\" could not be found", e.getMessage()); - } - } - - @Test - void test_parse_FindsAnElementByCanonicalName() { - Person user = workspace.getModel().addPerson("User"); - ModelItem element = parser.parse(context(), tokens("!ref", "Person://User")); - - assertSame(user, element); - } - - @Test - void test_parse_FindsAnElementByIdentifier() { - Person user = workspace.getModel().addPerson("User"); - - ModelDslContext context = context(); - IdentifiersRegister register = new IdentifiersRegister(); - register.register("user", user); - context.setIdentifierRegister(register); - - ModelItem modelItem = parser.parse(context, tokens("!ref", "user")); - assertSame(modelItem, user); - } - - @Test - void test_parse_FindsARelationshipByIdentifier() { - Person user = workspace.getModel().addPerson("User"); - Relationship relationship = user.interactsWith(user, "Description"); - - ModelDslContext context = context(); - IdentifiersRegister register = new IdentifiersRegister(); - register.register("rel", relationship); - context.setIdentifierRegister(register); - - ModelItem modelItem = parser.parse(context, tokens("!ref", "rel")); - assertSame(modelItem, relationship); - } - -} \ No newline at end of file From 96c264f12bbf6d0d63f659c19daf89313e4dab3d Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 23 Oct 2025 10:41:37 +0100 Subject: [PATCH 704/717] Fixes failing integration tests. --- .../export/dot/DOTDiagramExporterTests.java | 6 +++++- .../export/ilograph/IlographExporterTests.java | 6 +++++- .../export/mermaid/MermaidDiagramExporterTests.java | 6 +++++- .../plantuml/C4PlantUMLDiagramExporterTests.java | 11 +++++++++-- .../StructurizrPlantUMLDiagramExporterTests.java | 9 +++++++-- .../importer/diagrams/image/ImageImporterTests.java | 11 +++++++++-- .../importer/diagrams/kroki/KrokiImporterTests.java | 11 +++++++++-- .../diagrams/mermaid/MermaidImporterTests.java | 11 +++++++++-- .../diagrams/plantuml/PlantUMLImporterTests.java | 13 ++++++++++--- 9 files changed, 68 insertions(+), 16 deletions(-) diff --git a/structurizr-export/src/test/java/com/structurizr/export/dot/DOTDiagramExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/dot/DOTDiagramExporterTests.java index 81da0720a..ca459bddd 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/dot/DOTDiagramExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/dot/DOTDiagramExporterTests.java @@ -3,6 +3,7 @@ import com.structurizr.Workspace; import com.structurizr.export.AbstractExporterTests; import com.structurizr.export.Diagram; +import com.structurizr.http.HttpClient; import com.structurizr.model.*; import com.structurizr.util.WorkspaceUtils; import com.structurizr.view.*; @@ -501,7 +502,10 @@ public void test_BigBankPlcExample() throws Exception { @Tag("IntegrationTest") public void test_AmazonWebServicesExample() throws Exception { Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/amazon-web-services.json")); - ThemeUtils.loadThemes(workspace); + HttpClient httpClient = new HttpClient(); + httpClient.allow(".*"); + ThemeUtils.loadThemes(workspace, httpClient); + workspace.getViews().getDeploymentViews().iterator().next().enableAutomaticLayout(AutomaticLayout.RankDirection.LeftRight, 300, 300); DOTExporter exporter = new DOTExporter(); diff --git a/structurizr-export/src/test/java/com/structurizr/export/ilograph/IlographExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/ilograph/IlographExporterTests.java index a9057c03d..88d8184f4 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/ilograph/IlographExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/ilograph/IlographExporterTests.java @@ -3,6 +3,7 @@ import com.structurizr.Workspace; import com.structurizr.export.AbstractExporterTests; import com.structurizr.export.WorkspaceExport; +import com.structurizr.http.HttpClient; import com.structurizr.model.CustomElement; import com.structurizr.model.Model; import com.structurizr.util.WorkspaceUtils; @@ -647,7 +648,10 @@ void test_AmazonWebServicesExample() throws Exception { Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/amazon-web-services.json")); workspace.getViews().getConfiguration().getStyles().addElementStyle("Amazon Web Services - Route 53").addProperty(IlographExporter.ILOGRAPH_ICON, "AWS/Networking/Route-53.svg"); - ThemeUtils.loadThemes(workspace); + HttpClient httpClient = new HttpClient(); + httpClient.allow(".*"); + ThemeUtils.loadThemes(workspace, httpClient); + IlographExporter ilographExporter = new IlographExporter(); WorkspaceExport export = ilographExporter.export(workspace); diff --git a/structurizr-export/src/test/java/com/structurizr/export/mermaid/MermaidDiagramExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/mermaid/MermaidDiagramExporterTests.java index 31097f38b..1259c57be 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/mermaid/MermaidDiagramExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/mermaid/MermaidDiagramExporterTests.java @@ -3,6 +3,7 @@ import com.structurizr.Workspace; import com.structurizr.export.AbstractExporterTests; import com.structurizr.export.Diagram; +import com.structurizr.http.HttpClient; import com.structurizr.model.*; import com.structurizr.util.WorkspaceUtils; import com.structurizr.view.*; @@ -21,7 +22,10 @@ public class MermaidDiagramExporterTests extends AbstractExporterTests { @Tag("IntegrationTest") public void test_AmazonWebServicesExample() throws Exception { Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/amazon-web-services.json")); - ThemeUtils.loadThemes(workspace); + HttpClient httpClient = new HttpClient(); + httpClient.allow(".*"); + ThemeUtils.loadThemes(workspace, httpClient); + workspace.getViews().getDeploymentViews().iterator().next().enableAutomaticLayout(AutomaticLayout.RankDirection.LeftRight, 300, 300); MermaidDiagramExporter exporter = new MermaidDiagramExporter(); diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java index f35eb809e..76230eec7 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java @@ -3,6 +3,7 @@ import com.structurizr.Workspace; import com.structurizr.export.AbstractExporterTests; import com.structurizr.export.Diagram; +import com.structurizr.http.HttpClient; import com.structurizr.model.*; import com.structurizr.util.WorkspaceUtils; import com.structurizr.view.*; @@ -494,7 +495,10 @@ public void test_BigBankPlcExample() throws Exception { @Tag("IntegrationTest") public void test_AmazonWebServicesExampleWithoutTags() throws Exception { Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/amazon-web-services.json")); - ThemeUtils.loadThemes(workspace); + HttpClient httpClient = new HttpClient(); + httpClient.allow(".*"); + ThemeUtils.loadThemes(workspace, httpClient); + workspace.getViews().getDeploymentViews().iterator().next().enableAutomaticLayout(AutomaticLayout.RankDirection.LeftRight, 300, 300); workspace.getViews().getViews().forEach(v -> v.addProperty(C4PlantUMLExporter.C4PLANTUML_TAGS_PROPERTY, "false")); @@ -557,7 +561,10 @@ public void test_AmazonWebServicesExampleWithoutTags() throws Exception { @Tag("IntegrationTest") public void test_AmazonWebServicesExampleWithTags() throws Exception { Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/amazon-web-services.json")); - ThemeUtils.loadThemes(workspace); + HttpClient httpClient = new HttpClient(); + httpClient.allow(".*"); + ThemeUtils.loadThemes(workspace, httpClient); + workspace.getViews().getDeploymentViews().iterator().next().enableAutomaticLayout(AutomaticLayout.RankDirection.LeftRight, 300, 300); workspace.getViews().getConfiguration().addProperty(C4PlantUMLExporter.C4PLANTUML_TAGS_PROPERTY, "true"); diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java index 9ab8c70f4..999a1a5b3 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java @@ -3,6 +3,7 @@ import com.structurizr.Workspace; import com.structurizr.export.AbstractExporterTests; import com.structurizr.export.Diagram; +import com.structurizr.http.HttpClient; import com.structurizr.model.*; import com.structurizr.util.WorkspaceUtils; import com.structurizr.view.*; @@ -3509,7 +3510,9 @@ void dark_group() { @Tag("IntegrationTest") public void amazonWebServicesExample_Light() throws Exception { Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/amazon-web-services.json")); - ThemeUtils.loadThemes(workspace); + HttpClient httpClient = new HttpClient(); + httpClient.allow(".*"); + ThemeUtils.loadThemes(workspace, httpClient); StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(ColorScheme.Light); Collection diagrams = exporter.export(workspace); @@ -3862,7 +3865,9 @@ public void amazonWebServicesExample_Light() throws Exception { @Tag("IntegrationTest") public void amazonWebServicesExample_Dark() throws Exception { Workspace workspace = WorkspaceUtils.loadWorkspaceFromJson(new File("./src/test/resources/amazon-web-services.json")); - ThemeUtils.loadThemes(workspace); + HttpClient httpClient = new HttpClient(); + httpClient.allow(".*"); + ThemeUtils.loadThemes(workspace, httpClient); StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(ColorScheme.Dark); Collection diagrams = exporter.export(workspace); diff --git a/structurizr-import/src/test/java/com/structurizr/importer/diagrams/image/ImageImporterTests.java b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/image/ImageImporterTests.java index 1ea507825..722ccc141 100644 --- a/structurizr-import/src/test/java/com/structurizr/importer/diagrams/image/ImageImporterTests.java +++ b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/image/ImageImporterTests.java @@ -1,6 +1,7 @@ package com.structurizr.importer.diagrams.image; import com.structurizr.Workspace; +import com.structurizr.http.HttpClient; import com.structurizr.view.ImageView; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @@ -16,7 +17,10 @@ public void importDiagram_Url() throws Exception { Workspace workspace = new Workspace("Name", "Description"); ImageView view = workspace.getViews().createImageView("key"); - new ImageImporter().importDiagram(view, "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/alexa-for-business.png"); + HttpClient httpClient = new HttpClient(); + httpClient.allow(".*"); + + new ImageImporter(httpClient).importDiagram(view, "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/alexa-for-business.png"); assertEquals("https://static.structurizr.com/themes/amazon-web-services-2020.04.30/alexa-for-business.png", view.getContent()); assertEquals("image/png", view.getContentType()); assertEquals("alexa-for-business.png", view.getTitle()); @@ -29,7 +33,10 @@ public void importDiagram_Url_Inline() throws Exception { workspace.getViews().getConfiguration().addProperty(ImageImporter.IMAGE_INLINE_PROPERTY, "true"); ImageView view = workspace.getViews().createImageView("key"); - new ImageImporter().importDiagram(view, "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/alexa-for-business.png"); + HttpClient httpClient = new HttpClient(); + httpClient.allow(".*"); + + new ImageImporter(httpClient).importDiagram(view, "https://static.structurizr.com/themes/amazon-web-services-2020.04.30/alexa-for-business.png"); assertEquals("data:image/png;base64,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", view.getContent()); assertEquals("image/png", view.getContentType()); assertEquals("alexa-for-business.png", view.getTitle()); diff --git a/structurizr-import/src/test/java/com/structurizr/importer/diagrams/kroki/KrokiImporterTests.java b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/kroki/KrokiImporterTests.java index cfcb4d461..9fac27fd2 100644 --- a/structurizr-import/src/test/java/com/structurizr/importer/diagrams/kroki/KrokiImporterTests.java +++ b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/kroki/KrokiImporterTests.java @@ -1,6 +1,7 @@ package com.structurizr.importer.diagrams.kroki; import com.structurizr.Workspace; +import com.structurizr.http.HttpClient; import com.structurizr.view.ImageView; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @@ -51,7 +52,10 @@ public void importDiagram_AsInlinePNG() throws Exception { workspace.getViews().getConfiguration().addProperty(KrokiImporter.KROKI_INLINE_PROPERTY, "true"); ImageView view = workspace.getViews().createImageView("key"); - new KrokiImporter().importDiagram(view, "graphviz", new File("./src/test/resources/diagrams/kroki/diagram.dot")); + HttpClient httpClient = new HttpClient(); + httpClient.allow(".*"); + + new KrokiImporter(httpClient).importDiagram(view, "graphviz", new File("./src/test/resources/diagrams/kroki/diagram.dot")); assertEquals("key", view.getKey()); assertNull(view.getElement()); assertNull(view.getElementId()); @@ -85,7 +89,10 @@ public void importDiagram_AsInlineSVG() throws Exception { workspace.getViews().getConfiguration().addProperty(KrokiImporter.KROKI_INLINE_PROPERTY, "true"); ImageView view = workspace.getViews().createImageView("key"); - new KrokiImporter().importDiagram(view, "graphviz", new File("./src/test/resources/diagrams/kroki/diagram.dot")); + HttpClient httpClient = new HttpClient(); + httpClient.allow(".*"); + + new KrokiImporter(httpClient).importDiagram(view, "graphviz", new File("./src/test/resources/diagrams/kroki/diagram.dot")); assertEquals("key", view.getKey()); assertNull(view.getElement()); assertNull(view.getElementId()); diff --git a/structurizr-import/src/test/java/com/structurizr/importer/diagrams/mermaid/MermaidImporterTests.java b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/mermaid/MermaidImporterTests.java index 0aebd6a12..ca4cd4452 100644 --- a/structurizr-import/src/test/java/com/structurizr/importer/diagrams/mermaid/MermaidImporterTests.java +++ b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/mermaid/MermaidImporterTests.java @@ -1,6 +1,7 @@ package com.structurizr.importer.diagrams.mermaid; import com.structurizr.Workspace; +import com.structurizr.http.HttpClient; import com.structurizr.view.ImageView; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @@ -54,7 +55,10 @@ public void importDiagram_AsInlinePNG() throws Exception { workspace.getViews().getConfiguration().addProperty(MermaidImporter.MERMAID_INLINE_PROPERTY, "true"); ImageView view = workspace.getViews().createImageView("key"); - new MermaidImporter().importDiagram(view, new File("./src/test/resources/diagrams/mermaid/flowchart.mmd")); + HttpClient httpClient = new HttpClient(); + httpClient.allow(".*"); + + new MermaidImporter(httpClient).importDiagram(view, new File("./src/test/resources/diagrams/mermaid/flowchart.mmd")); assertEquals("key", view.getKey()); assertNull(view.getElement()); assertNull(view.getElementId()); @@ -90,7 +94,10 @@ public void importDiagram_AsInlineSVG() throws Exception { workspace.getViews().getConfiguration().addProperty(MermaidImporter.MERMAID_INLINE_PROPERTY, "true"); ImageView view = workspace.getViews().createImageView("key"); - new MermaidImporter().importDiagram(view, new File("./src/test/resources/diagrams/mermaid/flowchart.mmd")); + HttpClient httpClient = new HttpClient(); + httpClient.allow(".*"); + + new MermaidImporter(httpClient).importDiagram(view, new File("./src/test/resources/diagrams/mermaid/flowchart.mmd")); assertEquals("key", view.getKey()); assertNull(view.getElement()); assertNull(view.getElementId()); diff --git a/structurizr-import/src/test/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporterTests.java b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporterTests.java index 6081c96e5..a9c270024 100644 --- a/structurizr-import/src/test/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporterTests.java +++ b/structurizr-import/src/test/java/com/structurizr/importer/diagrams/plantuml/PlantUMLImporterTests.java @@ -1,6 +1,7 @@ package com.structurizr.importer.diagrams.plantuml; import com.structurizr.Workspace; +import com.structurizr.http.HttpClient; import com.structurizr.view.ImageView; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @@ -62,9 +63,12 @@ public void importDiagram_AsInlinePNG() throws Exception { workspace.getViews().getConfiguration().addProperty(PlantUMLImporter.PLANTUML_INLINE_PROPERTY, "true"); ImageView view = workspace.getViews().createImageView("key"); - new PlantUMLImporter().importDiagram(view, new File("./src/test/resources/diagrams/plantuml/with-title.puml")); + HttpClient httpClient = new HttpClient(); + httpClient.allow(".*"); + + new PlantUMLImporter(httpClient).importDiagram(view, new File("./src/test/resources/diagrams/plantuml/with-title.puml")); assertEquals("Sequence diagram example", view.getTitle()); - assertEquals("data:image/png;base64,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", view.getContent()); + assertEquals("data:image/png;base64,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", view.getContent()); assertEquals("image/png", view.getContentType()); } @@ -89,7 +93,10 @@ public void importDiagram_AsInlineSVG() throws Exception { workspace.getViews().getConfiguration().addProperty(PlantUMLImporter.PLANTUML_INLINE_PROPERTY, "true"); ImageView view = workspace.getViews().createImageView("key"); - new PlantUMLImporter().importDiagram(view, new File("./src/test/resources/diagrams/plantuml/with-title.puml")); + HttpClient httpClient = new HttpClient(); + httpClient.allow(".*"); + + new PlantUMLImporter(httpClient).importDiagram(view, new File("./src/test/resources/diagrams/plantuml/with-title.puml")); assertEquals("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiBjb250ZW50U3R5bGVUeXBlPSJ0ZXh0L2NzcyIgZGF0YS1kaWFncmFtLXR5cGU9IlNFUVVFTkNFIiBoZWlnaHQ9IjE1OHB4IiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIiBzdHlsZT0id2lkdGg6MjQ2cHg7aGVpZ2h0OjE1OHB4O2JhY2tncm91bmQ6I0ZGRkZGRjsiIHZlcnNpb249IjEuMSIgdmlld0JveD0iMCAwIDI0NiAxNTgiIHdpZHRoPSIyNDZweCIgem9vbUFuZFBhbj0ibWFnbmlmeSI+PHRpdGxlPlNlcXVlbmNlIGRpYWdyYW0gZXhhbXBsZTwvdGl0bGU+PGRlZnMvPjxnPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmb250LXdlaWdodD0iYm9sZCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIyMTguNjc0OCIgeD0iMTAiIHk9IjI3Ljk5NTEiPlNlcXVlbmNlIGRpYWdyYW0gZXhhbXBsZTwvdGV4dD48Zz48dGl0bGU+Qm9iPC90aXRsZT48cmVjdCBmaWxsPSIjMDAwMDAwIiBmaWxsLW9wYWNpdHk9IjAuMDAwMDAiIGhlaWdodD0iNDkuMTMyOCIgd2lkdGg9IjgiIHg9Ijg2LjQ3NzUiIHk9IjczLjU5MzgiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjAuNTtzdHJva2UtZGFzaGFycmF5OjUsNTsiIHgxPSI4OS45NDkyIiB4Mj0iODkuOTQ5MiIgeTE9IjczLjU5MzgiIHkyPSIxMjIuNzI2NiIvPjwvZz48Zz48dGl0bGU+QWxpY2U8L3RpdGxlPjxyZWN0IGZpbGw9IiMwMDAwMDAiIGZpbGwtb3BhY2l0eT0iMC4wMDAwMCIgaGVpZ2h0PSI0OS4xMzI4IiB3aWR0aD0iOCIgeD0iMTQxLjg5MjEiIHk9IjczLjU5MzgiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjAuNTtzdHJva2UtZGFzaGFycmF5OjUsNTsiIHgxPSIxNDUuMDU4NiIgeDI9IjE0NS4wNTg2IiB5MT0iNzMuNTkzOCIgeTI9IjEyMi43MjY2Ii8+PC9nPjxnIGNsYXNzPSJwYXJ0aWNpcGFudCBwYXJ0aWNpcGFudC1oZWFkIiBkYXRhLXBhcnRpY2lwYW50PSJCb2IiPjxyZWN0IGZpbGw9IiNFMkUyRjAiIGhlaWdodD0iMzAuMjk2OSIgcng9IjIuNSIgcnk9IjIuNSIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDowLjU7IiB3aWR0aD0iNDEuMDU2NiIgeD0iNjkuOTQ5MiIgeT0iNDIuMjk2OSIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjI3LjA1NjYiIHg9Ijc2Ljk0OTIiIHk9IjYyLjI5MiI+Qm9iPC90ZXh0PjwvZz48ZyBjbGFzcz0icGFydGljaXBhbnQgcGFydGljaXBhbnQtdGFpbCIgZGF0YS1wYXJ0aWNpcGFudD0iQm9iIj48cmVjdCBmaWxsPSIjRTJFMkYwIiBoZWlnaHQ9IjMwLjI5NjkiIHJ4PSIyLjUiIHJ5PSIyLjUiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MC41OyIgd2lkdGg9IjQxLjA1NjYiIHg9IjY5Ljk0OTIiIHk9IjEyMS43MjY2Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTQiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iMjcuMDU2NiIgeD0iNzYuOTQ5MiIgeT0iMTQxLjcyMTciPkJvYjwvdGV4dD48L2c+PGcgY2xhc3M9InBhcnRpY2lwYW50IHBhcnRpY2lwYW50LWhlYWQiIGRhdGEtcGFydGljaXBhbnQ9IkFsaWNlIj48cmVjdCBmaWxsPSIjRTJFMkYwIiBoZWlnaHQ9IjMwLjI5NjkiIHJ4PSIyLjUiIHJ5PSIyLjUiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MC41OyIgd2lkdGg9IjQ3LjY2NyIgeD0iMTIyLjA1ODYiIHk9IjQyLjI5NjkiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIzMy42NjciIHg9IjEyOS4wNTg2IiB5PSI2Mi4yOTIiPkFsaWNlPC90ZXh0PjwvZz48ZyBjbGFzcz0icGFydGljaXBhbnQgcGFydGljaXBhbnQtdGFpbCIgZGF0YS1wYXJ0aWNpcGFudD0iQWxpY2UiPjxyZWN0IGZpbGw9IiNFMkUyRjAiIGhlaWdodD0iMzAuMjk2OSIgcng9IjIuNSIgcnk9IjIuNSIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDowLjU7IiB3aWR0aD0iNDcuNjY3IiB4PSIxMjIuMDU4NiIgeT0iMTIxLjcyNjYiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIzMy42NjciIHg9IjEyOS4wNTg2IiB5PSIxNDEuNzIxNyI+QWxpY2U8L3RleHQ+PC9nPjxnIGNsYXNzPSJtZXNzYWdlIiBkYXRhLXBhcnRpY2lwYW50LTE9IkJvYiIgZGF0YS1wYXJ0aWNpcGFudC0yPSJBbGljZSI+PHBvbHlnb24gZmlsbD0iIzE4MTgxOCIgcG9pbnRzPSIxMzMuODkyMSwxMDAuNzI2NiwxNDMuODkyMSwxMDQuNzI2NiwxMzMuODkyMSwxMDguNzI2NiwxMzcuODkyMSwxMDQuNzI2NiIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MTsiIHgxPSI5MC40Nzc1IiB4Mj0iMTM5Ljg5MjEiIHkxPSIxMDQuNzI2NiIgeTI9IjEwNC43MjY2Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTMiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iMzEuNDE0NiIgeD0iOTcuNDc3NSIgeT0iOTkuNjYwNiI+aGVsbG88L3RleHQ+PC9nPjwhLS1TUkM9W0F5YWlvS2JMMjR1akI0dERJcXZMSUNiQ0oyekFwNUw4aEtaQ0JTWDl2TkJBSnJCR2pMRG1wQ2E0SWJlZlBBSmN2RUczMDAwMF0tLT48L2c+PC9zdmc+", view.getContent()); assertEquals("image/svg+xml", view.getContentType()); } From bccf1b3c1926fb9c65ec979f90006b7be9d3aaa7 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 24 Oct 2025 17:01:08 +0100 Subject: [PATCH 705/717] Tidies up changelog. --- changelog.md | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/changelog.md b/changelog.md index c8d49cf06..907833ea6 100644 --- a/changelog.md +++ b/changelog.md @@ -5,23 +5,21 @@ - structurizr-autolayout: Adds support for custom padding view/viewset properties: `structurizr.groupPadding`,`structurizr.boundaryPadding`, and `structurizr.deploymentNodePadding`. - structurizr-core: Removes support for deprecated enterprise and location concepts. - structurizr-core: Adds support for filtered deployment views (https://github.com/structurizr/java/issues/409). -- structurizr-core: Adds support for separate images for light and dark color schemes. +- structurizr-core: Image views can have separate images for light and dark color schemes. - structurizr-component: Fixes https://github.com/structurizr/java/issues/437 (Make ComponentFinder.run() not fail on empty Set). - structurizr-dsl: Adds support for `iconPosition` on element styles (options are `Top`, `Bottom`, `Left`). -- structurizr-dsl: Adds support for defining element and relationship styles for light and dark mode. -- structurizr-dsl: Adds a `Bucket` shape. -- structurizr-dsl: Adds a `Shell` shape. -- structurizr-dsl: Adds a `Terminal` shape. -- structurizr-dsl: Adds an 'instanceOf' keyword (an alternative for `softwareSystemInstance` and `containerInstance`). +- structurizr-dsl: Adds support for defining element and relationship styles for light and dark color schemes. +- structurizr-dsl: Added `Bucket`, `Shell`, and `Terminal` shapes. +- structurizr-dsl: Adds an `instanceOf` keyword as an alternative for `softwareSystemInstance` and `containerInstance`. - structurizr-dsl: Relationships to/from software system/container instances can be now defined by using the software system/container identifier. - structurizr-dsl: Fixes https://github.com/structurizr/java/issues/435 (Relationship archetype not applied to implicit-source relationships). -- structurizr-dsl: Adds support for removing relationships between software system instance/container instances, with a view to redefining them via infrastructure nodes. +- structurizr-dsl: Adds a new operator (`-/>`) for removing relationships between software system/container instances, with a view to redefining them via infrastructure nodes. - structurizr-dsl: Adds support for a `jump` property on relationship styles. - structurizr-dsl: PlantUML, Mermaid, and Kroki image views can now be defined by an inline source block. -- structurizr-dsl: Constants and variables are inherited when extending a DSL workspace. -- structurizr-dsl: DSL source is only stored in the JSON workspace when the DSL is deemed as "portable" (i.e. no files, plugins, scripts). +- structurizr-dsl: Constants and variables are now inherited when extending a DSL workspace. +- structurizr-dsl: DSL source is only stored in the JSON workspace when the DSL is deemed as "portable" (i.e. no file references, plugins, scripts). - structurizr-dsl: Deprecates `StructurizrDSLParser.setRestricted(boolean)` in favour of finer-grained features. -- structurizr-dsl: Identifiers are no longer stored as lower case in the JSON (`structurizr.dsl.identifier` property on elements and relationships). +- structurizr-dsl: Identifiers are no longer stored as lower case in the JSON (the `structurizr.dsl.identifier` property on elements and relationships). - structurizr-export: Removes support for deprecated enterprise and location concepts. - structurizr-export: PlantUML exporters - replaces skinparams with styles. - structurizr-export: PlantUML exporters - adds support for dark mode exports. From 69ca73706a3e9dbe4211b09824a311e60ad23268 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 28 Oct 2025 11:58:04 +0100 Subject: [PATCH 706/717] Bumps dependencies prior to release. --- structurizr-component/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/structurizr-component/build.gradle b/structurizr-component/build.gradle index 1c296c092..79940da9f 100644 --- a/structurizr-component/build.gradle +++ b/structurizr-component/build.gradle @@ -1,8 +1,8 @@ dependencies { api project(':structurizr-core') - implementation 'org.apache.bcel:bcel:6.10.0' - implementation 'com.github.javaparser:javaparser-symbol-solver-core:3.26.2' + implementation 'org.apache.bcel:bcel:6.11.0' + implementation 'com.github.javaparser:javaparser-symbol-solver-core:3.27.1' testImplementation project(':structurizr-annotation') From d34a43abb7c3d7eb5331a634811b1312fd9b66e5 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Tue, 28 Oct 2025 13:28:22 +0100 Subject: [PATCH 707/717] Updated to reflect release. --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 907833ea6..b31e9ce0b 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,6 @@ # Changelog -## v5.0.0 (unreleased) +## v5.0.0 (28th October 2025) - structurizr-autolayout: Adds support for custom padding view/viewset properties: `structurizr.groupPadding`,`structurizr.boundaryPadding`, and `structurizr.deploymentNodePadding`. - structurizr-core: Removes support for deprecated enterprise and location concepts. From 5012bb259c4a138f83733cc25e63cdcc028fdbe1 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Thu, 30 Oct 2025 15:48:55 +0000 Subject: [PATCH 708/717] Fixes #449. --- changelog.md | 4 +++ gradle.properties | 2 +- .../com/structurizr/http/RemoteContent.java | 1 + .../java/com/structurizr/view/ThemeUtils.java | 2 +- .../com/structurizr/view/ThemeUtilsTests.java | 25 ++++++++++++++++++- 5 files changed, 31 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index b31e9ce0b..d162e48ad 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,9 @@ # Changelog +## v5.0.1 (unreleased) + +-structurizr-core: Fixes https://github.com/structurizr/java/issues/449 (allow text/plain content types when loading themes). + ## v5.0.0 (28th October 2025) - structurizr-autolayout: Adds support for custom padding view/viewset properties: `structurizr.groupPadding`,`structurizr.boundaryPadding`, and `structurizr.deploymentNodePadding`. diff --git a/gradle.properties b/gradle.properties index 638aa1b58..3e9c95f94 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,4 +5,4 @@ signing.secretKeyRingFile=/some/path ossrhUsername=username ossrhPassword=password -version=5.0.0 \ No newline at end of file +version=5.0.1 \ No newline at end of file diff --git a/structurizr-client/src/main/java/com/structurizr/http/RemoteContent.java b/structurizr-client/src/main/java/com/structurizr/http/RemoteContent.java index 4a4b9ae0d..337668bee 100644 --- a/structurizr-client/src/main/java/com/structurizr/http/RemoteContent.java +++ b/structurizr-client/src/main/java/com/structurizr/http/RemoteContent.java @@ -6,6 +6,7 @@ public final class RemoteContent { public static final String CONTENT_TYPE_JSON = "application/json"; + public static final String CONTENT_TYPE_PLAIN_TEXT = "text/plain"; private final String content; private final byte[] bytes; diff --git a/structurizr-client/src/main/java/com/structurizr/view/ThemeUtils.java b/structurizr-client/src/main/java/com/structurizr/view/ThemeUtils.java index 0ecd531c6..869c7bd5f 100644 --- a/structurizr-client/src/main/java/com/structurizr/view/ThemeUtils.java +++ b/structurizr-client/src/main/java/com/structurizr/view/ThemeUtils.java @@ -90,7 +90,7 @@ public static void loadThemes(Workspace workspace, HttpClient httpClient) throws for (String themeLocation : workspace.getViews().getConfiguration().getThemes()) { if (Url.isUrl(themeLocation)) { RemoteContent remoteContent = httpClient.get(themeLocation); - if (remoteContent.getContentType().equals(RemoteContent.CONTENT_TYPE_JSON)) { + if (remoteContent.getContentType().startsWith(RemoteContent.CONTENT_TYPE_JSON) || remoteContent.getContentType().startsWith(RemoteContent.CONTENT_TYPE_PLAIN_TEXT)) { Theme theme = fromJson(remoteContent.getContentAsString()); String baseUrl = themeLocation.substring(0, themeLocation.lastIndexOf('/') + 1); diff --git a/structurizr-client/src/test/java/com/structurizr/view/ThemeUtilsTests.java b/structurizr-client/src/test/java/com/structurizr/view/ThemeUtilsTests.java index ae4fe5a30..9d61853f2 100644 --- a/structurizr-client/src/test/java/com/structurizr/view/ThemeUtilsTests.java +++ b/structurizr-client/src/test/java/com/structurizr/view/ThemeUtilsTests.java @@ -26,7 +26,7 @@ void loadThemes_DoesNothingWhenNoThemesAreDefined() throws Exception { @Test @Tag("IntegrationTest") - void loadThemes_LoadsThemesWhenThemesAreDefined() throws Exception { + void loadThemes_LoadsThemesWhenThemesAreDefined_AndContentTypeIsApplicationJson() throws Exception { Workspace workspace = new Workspace("Name", "Description"); SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name"); softwareSystem.addTags("Amazon Web Services - Alexa For Business"); @@ -47,6 +47,29 @@ void loadThemes_LoadsThemesWhenThemesAreDefined() throws Exception { assertEquals("https://static.structurizr.com/themes/amazon-web-services-2020.04.30/alexa-for-business.png", style.getIcon()); } + @Test + @Tag("IntegrationTest") + void loadThemes_LoadsThemesWhenThemesAreDefined_AndContentTypeIsPlainText() throws Exception { + Workspace workspace = new Workspace("Name", "Description"); + SoftwareSystem softwareSystem = workspace.getModel().addSoftwareSystem("Name"); + softwareSystem.addTags("Amazon Web Services - Alexa For Business"); + workspace.getViews().getConfiguration().setThemes("https://raw.githubusercontent.com/structurizr/themes/refs/heads/master/amazon-web-services-2020.04.30/theme.json"); + + HttpClient httpClient = new HttpClient(); + httpClient.allow(".*"); + ThemeUtils.loadThemes(workspace, httpClient); + + // there should still be zero styles in the workspace + assertEquals(0, workspace.getViews().getConfiguration().getStyles().getElements().size()); + + // but we should be able to find a style included in the theme + ElementStyle style = workspace.getViews().getConfiguration().getStyles().findElementStyle(softwareSystem); + assertNotNull(style); + assertEquals("#d6242d", style.getStroke()); + assertEquals("#d6242d", style.getColor()); + assertEquals("https://raw.githubusercontent.com/structurizr/themes/refs/heads/master/amazon-web-services-2020.04.30/alexa-for-business.png", style.getIcon()); + } + @Test void toJson() throws Exception { Workspace workspace = new Workspace("Name", "Description"); From 15c75f2a679a42413905818fc8069cbe16df0909 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 1 Nov 2025 08:34:17 +0000 Subject: [PATCH 709/717] Updated for release. --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index d162e48ad..5c0d34f22 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,6 @@ # Changelog -## v5.0.1 (unreleased) +## v5.0.1 (1st November 2025) -structurizr-core: Fixes https://github.com/structurizr/java/issues/449 (allow text/plain content types when loading themes). From 79fa039094e726132250f50d6f8579b67a86337f Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 8 Nov 2025 09:10:13 +0000 Subject: [PATCH 710/717] Fixes a NPE. --- .../AbstractDocumentableInspection.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/structurizr-inspection/src/main/java/com/structurizr/inspection/documentation/AbstractDocumentableInspection.java b/structurizr-inspection/src/main/java/com/structurizr/inspection/documentation/AbstractDocumentableInspection.java index d4e3fe5ac..7b2eb945d 100644 --- a/structurizr-inspection/src/main/java/com/structurizr/inspection/documentation/AbstractDocumentableInspection.java +++ b/structurizr-inspection/src/main/java/com/structurizr/inspection/documentation/AbstractDocumentableInspection.java @@ -40,12 +40,14 @@ public final Violation run(Documentable documentable) { protected Set findEmbeddedViewKeys(Documentable documentable) { Set keys = new LinkedHashSet<>(); - for (Section section : documentable.getDocumentation().getSections()) { - keys.addAll(findEmbeddedViewKeys(section)); - } + if (documentable.getDocumentation() != null) { + for (Section section : documentable.getDocumentation().getSections()) { + keys.addAll(findEmbeddedViewKeys(section)); + } - for (Decision decision : documentable.getDocumentation().getDecisions()) { - keys.addAll(findEmbeddedViewKeys(decision)); + for (Decision decision : documentable.getDocumentation().getDecisions()) { + keys.addAll(findEmbeddedViewKeys(decision)); + } } return keys; From 04def03f4a02437e1e4ad3bf17cf133453a4e0a6 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 8 Nov 2025 09:10:30 +0000 Subject: [PATCH 711/717] Bumps version. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 3e9c95f94..731f3e4c3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,4 +5,4 @@ signing.secretKeyRingFile=/some/path ossrhUsername=username ossrhPassword=password -version=5.0.1 \ No newline at end of file +version=5.0.2 \ No newline at end of file From 8ad78b1a16d83cc99128bf9e50f0ade0bb95bd31 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 8 Nov 2025 09:12:57 +0000 Subject: [PATCH 712/717] structurizr-client: Adds a `getWorkspaceAsJson()` to `WorkspaceApiClient`. --- changelog.md | 4 ++ .../structurizr/api/WorkspaceApiClient.java | 60 ++++++++++++------- 2 files changed, 43 insertions(+), 21 deletions(-) diff --git a/changelog.md b/changelog.md index 5c0d34f22..eae71b392 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,9 @@ # Changelog +## v5.0.2 (unreleased) + +- structurizr-client: Adds a `getWorkspaceAsJson()` to `WorkspaceApiClient`. + ## v5.0.1 (1st November 2025) -structurizr-core: Fixes https://github.com/structurizr/java/issues/449 (allow text/plain content types when loading themes). diff --git a/structurizr-client/src/main/java/com/structurizr/api/WorkspaceApiClient.java b/structurizr-client/src/main/java/com/structurizr/api/WorkspaceApiClient.java index ce68fa41f..f45d0bdb6 100644 --- a/structurizr-client/src/main/java/com/structurizr/api/WorkspaceApiClient.java +++ b/structurizr-client/src/main/java/com/structurizr/api/WorkspaceApiClient.java @@ -227,6 +227,44 @@ private boolean manageLockForWorkspace(long workspaceId, boolean lock) throws St * @throws StructurizrClientException if there are problems related to the network, authorization, JSON deserialization, etc */ public Workspace getWorkspace(long workspaceId) throws StructurizrClientException { + String json = getWorkspaceAsJson(workspaceId); + + try { + if (encryptionStrategy == null) { + if (json.contains("\"encryptionStrategy\"") && json.contains("\"ciphertext\"")) { + log.warn("The JSON may contain a client-side encrypted workspace, but no passphrase has been specified."); + } + + JsonReader jsonReader = new JsonReader(); + jsonReader.setIdGenerator(idGenerator); + return jsonReader.read(new StringReader(json)); + } else { + EncryptedWorkspace encryptedWorkspace = new EncryptedJsonReader().read(new StringReader(json)); + + if (encryptedWorkspace.getEncryptionStrategy() != null) { + encryptedWorkspace.getEncryptionStrategy().setPassphrase(encryptionStrategy.getPassphrase()); + return encryptedWorkspace.getWorkspace(); + } else { + // this workspace isn't encrypted, even though the client has an encryption strategy set + JsonReader jsonReader = new JsonReader(); + jsonReader.setIdGenerator(idGenerator); + return jsonReader.read(new StringReader(json)); + } + } + } catch (Exception e) { + log.error(e); + throw new StructurizrClientException(e); + } + } + + /** + * Gets the workspace with the given ID, as a JSON string. + * + * @param workspaceId the workspace ID + * @return a JSON string + * @throws StructurizrClientException if there are problems related to the network, authorization, JSON deserialization, etc + */ + public String getWorkspaceAsJson(long workspaceId) throws StructurizrClientException { if (workspaceId <= 0) { throw new IllegalArgumentException("The workspace ID must be a positive integer."); } @@ -251,27 +289,7 @@ public Workspace getWorkspace(long workspaceId) throws StructurizrClientExceptio if (response.getCode() == HttpStatus.SC_OK) { archiveWorkspace(workspaceId, json); - if (encryptionStrategy == null) { - if (json.contains("\"encryptionStrategy\"") && json.contains("\"ciphertext\"")) { - log.warn("The JSON may contain a client-side encrypted workspace, but no passphrase has been specified."); - } - - JsonReader jsonReader = new JsonReader(); - jsonReader.setIdGenerator(idGenerator); - return jsonReader.read(new StringReader(json)); - } else { - EncryptedWorkspace encryptedWorkspace = new EncryptedJsonReader().read(new StringReader(json)); - - if (encryptedWorkspace.getEncryptionStrategy() != null) { - encryptedWorkspace.getEncryptionStrategy().setPassphrase(encryptionStrategy.getPassphrase()); - return encryptedWorkspace.getWorkspace(); - } else { - // this workspace isn't encrypted, even though the client has an encryption strategy set - JsonReader jsonReader = new JsonReader(); - jsonReader.setIdGenerator(idGenerator); - return jsonReader.read(new StringReader(json)); - } - } + return json; } else { ApiResponse apiResponse = ApiResponse.parse(json); throw new StructurizrClientException(apiResponse.getMessage()); From efe39afae9b36ee467ca3a130282d1a46fe0a7a7 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sat, 8 Nov 2025 09:13:07 +0000 Subject: [PATCH 713/717] . --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index eae71b392..26c0c9183 100644 --- a/changelog.md +++ b/changelog.md @@ -6,7 +6,7 @@ ## v5.0.1 (1st November 2025) --structurizr-core: Fixes https://github.com/structurizr/java/issues/449 (allow text/plain content types when loading themes). +- structurizr-core: Fixes https://github.com/structurizr/java/issues/449 (allow text/plain content types when loading themes). ## v5.0.0 (28th October 2025) From 8ab8684c1da25c84ccc29715e27b69679e9d0312 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 9 Nov 2025 08:56:52 +0000 Subject: [PATCH 714/717] structurizr-client: Adds branches and users information to the admin API response. --- changelog.md | 1 + .../structurizr/api/WorkspaceMetadata.java | 20 +++++++++ .../com/structurizr/api/WorkspaceUsers.java | 42 +++++++++++++++++++ 3 files changed, 63 insertions(+) create mode 100644 structurizr-client/src/main/java/com/structurizr/api/WorkspaceUsers.java diff --git a/changelog.md b/changelog.md index 26c0c9183..17bc04ad7 100644 --- a/changelog.md +++ b/changelog.md @@ -3,6 +3,7 @@ ## v5.0.2 (unreleased) - structurizr-client: Adds a `getWorkspaceAsJson()` to `WorkspaceApiClient`. +- structurizr-client: Adds branches and users information to the admin API response. ## v5.0.1 (1st November 2025) diff --git a/structurizr-client/src/main/java/com/structurizr/api/WorkspaceMetadata.java b/structurizr-client/src/main/java/com/structurizr/api/WorkspaceMetadata.java index 81df2367d..c6c5e1bba 100644 --- a/structurizr-client/src/main/java/com/structurizr/api/WorkspaceMetadata.java +++ b/structurizr-client/src/main/java/com/structurizr/api/WorkspaceMetadata.java @@ -12,6 +12,10 @@ public class WorkspaceMetadata { private String publicUrl; private String shareableUrl; + private String[] branches; + + private WorkspaceUsers users; + WorkspaceMetadata() { } @@ -79,4 +83,20 @@ void setShareableUrl(String shareableUrl) { this.shareableUrl = shareableUrl; } + public String[] getBranches() { + return branches; + } + + void setBranches(String[] branches) { + this.branches = branches; + } + + public WorkspaceUsers getUsers() { + return users; + } + + void setUsers(WorkspaceUsers users) { + this.users = users; + } + } \ No newline at end of file diff --git a/structurizr-client/src/main/java/com/structurizr/api/WorkspaceUsers.java b/structurizr-client/src/main/java/com/structurizr/api/WorkspaceUsers.java new file mode 100644 index 000000000..1c94eb959 --- /dev/null +++ b/structurizr-client/src/main/java/com/structurizr/api/WorkspaceUsers.java @@ -0,0 +1,42 @@ +package com.structurizr.api; + +public class WorkspaceUsers { + + private String owner; + private String[] admin; + private String[] write; + private String[] read; + + public String getOwner() { + return owner; + } + + void setOwner(String owner) { + this.owner = owner; + } + + public String[] getAdmin() { + return admin; + } + + void setAdmin(String[] admin) { + this.admin = admin; + } + + public String[] getWrite() { + return write; + } + + void setWrite(String[] write) { + this.write = write; + } + + public String[] getRead() { + return read; + } + + void setRead(String[] read) { + this.read = read; + } + +} \ No newline at end of file From 493710fba13dfec09261b929d8c625f79160b683 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 9 Nov 2025 12:04:49 +0000 Subject: [PATCH 715/717] Updated to reflect release. --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 17bc04ad7..d2c53e6f2 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,6 @@ # Changelog -## v5.0.2 (unreleased) +## v5.0.2 (9th November 2025) - structurizr-client: Adds a `getWorkspaceAsJson()` to `WorkspaceApiClient`. - structurizr-client: Adds branches and users information to the admin API response. From 155da59dca85b2cd6d791c2e6355abaa0349f7f3 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Fri, 21 Nov 2025 14:10:29 +0000 Subject: [PATCH 716/717] structurizr-export: Adds the `addSkinParam()` method back to the PlantUML exporters. --- changelog.md | 4 ++ gradle.properties | 2 +- .../plantuml/AbstractPlantUMLExporter.java | 33 +++++++++++++ .../export/plantuml/C4PlantUMLExporter.java | 3 +- .../plantuml/StructurizrPlantUMLExporter.java | 3 +- .../C4PlantUMLDiagramExporterTests.java | 40 ++++++++++++++++ ...ructurizrPlantUMLDiagramExporterTests.java | 48 +++++++++++++++++++ 7 files changed, 130 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index d2c53e6f2..396b73915 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,9 @@ # Changelog +## v5.0.3 (21st November 2025) + +- structurizr-export: Adds the `addSkinParam()` method back to the PlantUML exporters. + ## v5.0.2 (9th November 2025) - structurizr-client: Adds a `getWorkspaceAsJson()` to `WorkspaceApiClient`. diff --git a/gradle.properties b/gradle.properties index 731f3e4c3..8e1b695b3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,4 +5,4 @@ signing.secretKeyRingFile=/some/path ossrhUsername=username ossrhPassword=password -version=5.0.2 \ No newline at end of file +version=5.0.3 \ No newline at end of file diff --git a/structurizr-export/src/main/java/com/structurizr/export/plantuml/AbstractPlantUMLExporter.java b/structurizr-export/src/main/java/com/structurizr/export/plantuml/AbstractPlantUMLExporter.java index 52a974b27..d078aa65e 100644 --- a/structurizr-export/src/main/java/com/structurizr/export/plantuml/AbstractPlantUMLExporter.java +++ b/structurizr-export/src/main/java/com/structurizr/export/plantuml/AbstractPlantUMLExporter.java @@ -14,6 +14,10 @@ import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.net.URL; +import java.util.LinkedHashMap; +import java.util.Map; + +import static java.lang.String.format; public abstract class AbstractPlantUMLExporter extends AbstractDiagramExporter { @@ -27,6 +31,20 @@ public abstract class AbstractPlantUMLExporter extends AbstractDiagramExporter { public static final String DIAGRAM_TITLE_TAG = "Diagram:Title"; public static final String DIAGRAM_DESCRIPTION_TAG = "Diagram:Description"; + private final Map skinParams = new LinkedHashMap<>(); + + protected Map getSkinParams() { + return skinParams; + } + + public void addSkinParam(String name, String value) { + skinParams.put(name, value); + } + + public void clearSkinParams() { + skinParams.clear(); + } + public AbstractPlantUMLExporter() { this(ColorScheme.Light); } @@ -208,6 +226,21 @@ protected void writeHeader(ModelView view, IndentingWriter writer) { writer.writeLine("set separator none"); } + protected void writeSkinParams(IndentingWriter writer) { + if (!skinParams.isEmpty()) { + writer.writeLine(); + writer.writeLine("skinparam {"); + writer.indent(); + for (final String name : skinParams.keySet()) { + writer.writeLine(format("%s %s", name, skinParams.get(name))); + } + writer.outdent(); + writer.writeLine("}"); + } + + writer.writeLine(); + } + protected void writeIncludes(ModelView view, IndentingWriter writer) { String commaSeparatedIncludes = getViewOrViewSetProperty(view, PLANTUML_INCLUDES_PROPERTY, ""); if (!StringUtils.isNullOrEmpty(commaSeparatedIncludes)) { diff --git a/structurizr-export/src/main/java/com/structurizr/export/plantuml/C4PlantUMLExporter.java b/structurizr-export/src/main/java/com/structurizr/export/plantuml/C4PlantUMLExporter.java index 4f309a149..bba5d53c5 100644 --- a/structurizr-export/src/main/java/com/structurizr/export/plantuml/C4PlantUMLExporter.java +++ b/structurizr-export/src/main/java/com/structurizr/export/plantuml/C4PlantUMLExporter.java @@ -76,7 +76,8 @@ protected void writeHeader(ModelView view, IndentingWriter writer) { } } - writer.writeLine(); + writeSkinParams(writer); + writer.writeLine(""); writer.writeLine(); diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java index 76230eec7..9497bb5c9 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/C4PlantUMLDiagramExporterTests.java @@ -1755,4 +1755,44 @@ public void elementWithUrl() { @enduml""", diagram.getDefinition()); } + @Test + void skinparams() { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getModel().addSoftwareSystem("A"); + + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key"); + view.addAllElements(); + + C4PlantUMLExporter exporter = new C4PlantUMLExporter(); + exporter.addSkinParam("linetype", "ortho"); + Diagram diagram = exporter.export(view); + + assertEquals(""" + @startuml + title System Landscape View + + set separator none + top to bottom direction + + skinparam { + linetype ortho + } + + + + !include + !include + + System(A, "A", $descr="", $tags="", $link="") + + SHOW_LEGEND(true) + hide stereotypes + @enduml""", diagram.getDefinition()); + } + } \ No newline at end of file diff --git a/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java b/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java index 999a1a5b3..b781d9a21 100644 --- a/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java +++ b/structurizr-export/src/test/java/com/structurizr/export/plantuml/StructurizrPlantUMLDiagramExporterTests.java @@ -4216,4 +4216,52 @@ public void amazonWebServicesExample_Dark() throws Exception { @enduml""", diagram.getLegend().getDefinition()); } + @Test + void skinparams() { + Workspace workspace = new Workspace("Name", "Description"); + workspace.getModel().addSoftwareSystem("A"); + + SystemLandscapeView view = workspace.getViews().createSystemLandscapeView("key"); + view.addAllElements(); + + StructurizrPlantUMLExporter exporter = new StructurizrPlantUMLExporter(); + exporter.addSkinParam("linetype", "ortho"); + Diagram diagram = exporter.export(view); + + assertEquals(""" + @startuml + title System Landscape View + + set separator none + top to bottom direction + hide stereotype + + skinparam { + linetype ortho + } + + + + rectangle "==A\\n[Software System]" <> as A + + @enduml""", diagram.getDefinition()); + } + } \ No newline at end of file From 5783db719d701978e3a833bd5596736e69545121 Mon Sep 17 00:00:00 2001 From: Simon Brown Date: Sun, 1 Feb 2026 14:29:39 +0000 Subject: [PATCH 717/717] . --- README.md | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/README.md b/README.md index d486bd395..d6ec8cb2d 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,3 @@ # Structurizr for Java -This repository contains the source code for the following libraries: - -- [structurizr-client](structurizr-client): JSON serialisation/deserialisation utilities, and clients for the cloud service/on-premises workspace/admin APIs. -- [structurizr-core](structurizr-core): The core library for creating a workspace with Java code. -- [structurizr-component](structurizr-component): A library to discover components from Java code. -- [structurizr-dsl](structurizr-dsl): A text-based DSL wrapper around Structurizr for Java. -- [structurizr-export](structurizr-export): Export models and views to external formats (e.g. PlantUML, Mermaid, etc). -- [structurizr-import](structurizr-import): Utilities to import diagrams and documentation into a Structurizr workspace. -- [structurizr-autolayout](structurizr-autolayout): Apply Graphviz automatic layout to views. -- [structurizr-inspection](structurizr-inspection): A Checkstyle inspired approach to verifying workspace content. - -- [Documentation](https://docs.structurizr.com) -- [Changelog](changelog.md) \ No newline at end of file +> The code in this repo has been moved to https://github.com/structurizr/structurizr \ No newline at end of file

    dd3DUkU34M6&(8%_rNx;>CX|5w1Q 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 diff --git a/docs/images/styling-elements-3.png b/docs/images/styling-elements-3.png deleted file mode 100644 index 9fe17c82c4f9d0d1b9c5081adb7b91e6afa338b6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 175296 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г^knELgSyJ}0FHy41ke$kItjWHUvbGS}moP>c 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 z zp8R#t#qswmf9_^78tI`yUs|_@{$fG={gxm#2H{4rj)Ix-->>||dr8m{d3>&FG}a4LF19J3&k;R|IMJ>J75sKP*Fwh zU!3dTZ@E<(NP<%{WhMMqgG#_4UQ=VI|LWfO|2Nb>=GQ!e{O5@Rnj6g4A3QpafPZSr Lx0Q+%%!B_2pSdZ= diff --git a/docs/images/styling-elements-4.png b/docs/images/styling-elements-4.png deleted file mode 100644 index 58ef6d5d98cfb09ece933349cf8329e8e388f71f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 193247 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

    oam$W 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 diff --git a/docs/images/styling-elements-1.png b/docs/images/styling-elements-1.png deleted file mode 100644 index 661a3e857462ecdefe128d406865c997b0243aed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 171437 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ª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

    E(wuwN+TzO`5 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%0AT>3ni3)7uPNReL*pMzZ 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 PsE>k-iZoc#B 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 Ii0S$N4?l6|?*IS* diff --git a/docs/images/structurizr-banner.png b/docs/images/structurizr-banner.png deleted file mode 100644 index a9a0b0abc1be158cdfb68820cca61d401ef6ebde..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 53460 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 diff --git a/docs/images/structurizr-logo.png b/docs/images/structurizr-logo.png deleted file mode 100644 index 9324ae8dc299b4e8a912e01157dfc8505027cf56..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10206 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 z�#4qoPii*ayDail2+Udp_9A+JKdFfHtCqfpKX^H3XT^@6~y4Z*VNN_C@8@-j?L{ z?yE-h=A_il)0oz4AOD&$3&`D6K>KHAr@!S*y&&v}k3CfM`X&3Zbyzb=P8ivKefxn3TUQa=w>A+PI{%(Jjnv+eq(=+M4VYX;AW-0d(@{}IXM8EdzlqNweqrk74I?Rlv0 zxPJ~wUbAkzNS3}Qudg{wI6hsaN5B71EP!W4^+R#dxFJob!8O`k&nK_I6+)= zfkub5Jx)0~7TSA)U5U+s z2P{d^=T&2T5cynz_rAMgfr?>f5!rj_q>BzQEwT0sv(Qh2DdBo6M6zI%04(jDg6i3zC|;xTohpdf)5eEpqfk}k zVPYsbjCX#1!yb!oV;^NHu5F9iRG4j3Fs7LP(k9p3FS=FH(fo$@6Jjb1+}m7I4Mm!b zvHySI54Ej~pcz1XKZ~gwFvmx2Hz+y?3y^*dUnmmuv{{ADp|_DtNIyrab|n^R@{lme z$;1`Z%inEN8|REYd@6P^M3naU`+QmzrgR@{d%`c@rigFwkz_rMAw?U#iyCUBpZ-$F zeP>f|A60oYJ{&&aDtVo31H0iUApPBmwbk}U=%(H94g2BkmQNtKK&gl*2RkJ3I znw?7x*AK$H90{p;WDjR_4NSvHOn?0K;o9yx7oQpj)jF!$kg|BheDru@=25c9?t^Q1 zQU>Cdi$+fm6?EUmvs_zmE*~h5_V+Zvtu8QTn z92Nz;=RIoUcYkC8q_6AQ%gMnuY$6kyV%cwWq?WBUGJA$T76)@%e-Mb-kaMSrvd;yl^j!;7Bd#w^j3NPt#Y=`wl?&d}k

    Um-sI;#Xuxx*03)=D2HDK&$JbAAb~hqTtU__M!*WRxeOdY?gm|MXvDXmjst1xV-j=8WDget;-Aa{;3*bkLmMvGtma+- zpdP#DNQBJ`i*u+I0`v=|RW82uKa{{v1Z#siI603mY{kWcfqZ2?-LnGzl-F2YIRs!U zTbeqOg>8%n06@HZMnm%je`9A`Ew29)8Kz`6^2{dngAt(=@eGEbo57U4fp4N{r$NZE zleB0_>1(R1U2?PuiNaQ4Kh7}M>uC|6vQDL*o!i?)_unLtA7L>5H2fhwSb#--i zK*un*R0~UY)7}AaTm62d9$DaXAY^C*# zj^;c5*kH%Dx6S#ot6}2J3+KTQr2-DatyIb!UCVza!Cd}jt}hJhP4Fuoc(NPOe-USV zi-GNzVDNI^S|w-~a|O#F&;MgwLWpfBQyhhMKo?z7L`39+I>m!XsOj8bWAG-qAj7Lk z8>~u)4UWTG&o>!CD_Zi6C7#;|*roXdl1^Q}!nt4ObOP2UJRWr z2N>%dr{2*`%?!3;0tdDwX@2{Alr`HU5aABOc(Xd*ZJ*wL#h3=#T|P=#9aVt5W5_30 z-=|%v-{G_K$i5To!Y~|2<-x&3z2MAQg5(A~z`*M(N5)}Y5|YpcQK47^^P2CU{Vy`Y zCE9cj%#LAYx-BmgMoHb@@Bk;~%0>EdH!~b0vuT)+MJu9ConOdzQxDnSLmBc#cQ832_fcNF^kMj@Qe(Y1Gkn=0wMGjMukmo?fwRU#gK$*2e>PBrSU{AP(LrX zs_OZYE76DUx@4^dT!OYtz$4!MbT|-;kb?RVql`pFLhk@wnDm8ByK?Z7O^}d)J?Ihv z!QNOvLFauZgmRQkg2+|Cmx5yRXBP)JeI+1$Xr%~8(28!%ACMKIoO>6D$A&!f^g*Z^ ztRzmp@Xb?oDqkT*YVJk;PRz%^iP+Q&J;RSw=O9xa6})>i=;Ly>v>deo&um(8sD!^c z+FE9tTZshD7h7fK_Ea-Hz%%xmNcY{G;{Y^+xa7$WzCBe#DAg*qzx-qC3N%G)m{TDd z3mBMfmbS4Miu%a%0@-$%;T@TsMZI;W2uRZP6M${uXd%g#sCIk+Kidd@}cJ znAm5Wk7|p08jHyjay*pr{qNC=Pf$V6RJz&@&=V$`wB6KxXB`gOMDpPH3&1)MN&5%FDi2k#ojVOTppEt`$g%~4LEQ%2aQh*{AvBh=~L zsL?0ynH{59?C6g>?#0u$@2p9>klNo%f_`s0g4SGaG9o+{7V@=pXPIOF0eVUWwFqJ7 z9?O5Q0EMzhh>S=2cV}Kv9@hO$y$S4@mq@Bc{gq$dJ`4%utTYZBe;S`e0aMG0OxFXdkX@d6A zUfYmwz;|7=k^)`k^{p8KEXIxC1+9AJd9a>wLO|R0E1n&I+2^I!08GbpgY7%LYaq-* zqvv@X$HUUN2*5(s03+GT#SvLRJ`<6gOti7S9tqS?B&@Bidp&!9PJ#Js6KB4sXf z*n|wUk(t#v@9CZa-5$krd$80#$#vZF>CTUY@GBt4M`|MwL)^R^Ep0efsIb~fL^pc! zl9yL@ab%u%@w5T#pxfC4Yf{dWm6pJgJ87V-kqAG~XWqI3%Wk3pFD%7`_W6T?_-5z5 z0T^G%II2ii`v?Fvjjp|3Q}Y%~axv6ea?{Pr z=>O6R>sG!f``Z+MBr@69I%(P#{VjWvCY*M$ZYBN@tf3kJW6Fk*93+d4bULiMq)mQW zKhWZSdRRj+WG0~cn!IcbA2aCbgKL|_G9uP8ymhS_exe@A6 z7Xt0^yReTit0y=WWBDCiSW2dKa7-0%8R9CJNZHSih|FpPgG+ZLNJ&a_OAZP(ZL@#1t}lJk5dG#eEKnjDX=JsN<5rIl4k|TN#qw{at%c>55>@r zvB_&iNr(cevm`}yCary%#_J6viM3q+%xsBQ+ ztLpTvsJD5wT>g&m-gY(<07B}o3Mjui{Hy~>6?=sybtO23Uq1jw*qq@mzT2{*M#T_3 z&5jhLH%SyAGT0h{UmjyrT=!a322F$!Wn;o=7{RTzaNRQ%reh~33E2{@AdXaHwfeFB3ho7RpoR=D zZ}?|~VFsF;CGmW>wS$cxND67cBYFOl6he%w_%zfn4!oUTpsg*>$IEac9A<+z>zb}~ z?Y>sZbVNXVtOME(__r~PdafF`%9|A#8do%ZnHB}6#`BJvWI75gERF9~8YSOtZZRB` zda=3{GkDfmUZFh;q~BRq00wd3_^9=et^eIyeby?4GKc)Q)946?HFKj@5PS_wbrZ^J zHasvi?0Zg!+Gm02bg-A!@G24TPbYxeEXhk|c?v|%wo2R*%~NLDUio{4yp#6e+Q}G9 z=Im3x|7^p7ZNs-B-i^~N=6Mp)~W|c0V(dL(VG_HU2$+ElaSOA4;7mMp&ysF z6RC&#qo|I3;~;P?ACqic_y?Vis7#HzLM4En<15RCwE(S ztou%2ww{-uo2+OFG7F;1Va_}nuz9vuy$bBPd+oW!sP#i)@*4eT$ew`em&DspTivTmhlyd<`Kn5rX7Z^GFv!&`9ZQO zY{X3VBrB^VK0dy6@A#As!lYY!ZfVi~jJH7KvK0C(OXMKsoC=iY?N;K{@-GEm59+mg zxGqxXVf2`0t^@MT`s6OD^IX)gEZW?A(MB^!&*vLw#PYpnNr(0`gO-638S9Tbs&B+w z8iOVQ-D--UVWichdiXG=#2prC2KpQHOA^?oKq6g*`O|)9?BR%Ie^L*NR^&6>WudG} zUm6OY2XVWX5R@EfJ&#+@4tzNTQr#!9@|mcgYl}7BzHYSelr0}B*fAg4%Z+&>wJ`!R zJJ}cG0&K1`gZm&!Oj9&as2E?to=b3Sw#Tkhz@I5~7Tu`Y+R)k&^ zkDGTHpF{8NIYWyE-Xf|Mgq+UgL!vP;4j%k9cplOVdw5k)_s+-u8gkZI&(#rKSFyb?RS5p{Z(9nQyO@*{(nC!up<)n(RB!rK2uQnN z>}1@Zp~^X08LTQO%~xAI0=O-vUb!k=V0+{|i?VTW%7_XsFRlb#jG~6^{?)c)dI@_~`1Ns4^ z0JARD0+=^(njG%#o?3{TPI)+SrlOyjKnJMqsnRDRB>|O$bE^VM0>&{K;jlIh%6fe| z!@&9Pr>aw5&+mN3?ox5OoWlZ4==FDWM0}joBTvl)#5vN8Ju3#y7vX8Y<&)!d`@Gdx zHTV=rW!tzY>-2^E&JSHwx-N-VKfD5`gqPjLc^k`=Ap-aDcH@mkz0fgeEUI{81EGn1BmI<@Zb?_=J;zP7NXFLZv zyN`CPy|u)`Ns^qpXYT7zokU%>TpcP!L-LIyJ+vl#EPEAnHYPZ++9sM zD~~jgB9}rbGVnmFRP*&=nd;-SPz19g^4Ka_H@@PlutXij;-(xjE^dV}(Ka8Xv>fa6 zAtyZcsTs)HjbT@dlyf&?Nc(!n$O%FA4y@^QLD2VKgAvEy*TLIgVCY8%hW)YF zS%5nm)1n!VnfC#qb@{t~eEq&-+0s}FAzh9IOSiTW0$5cM>+R%arqH?cnwG zr!A$F#ei}3bVs$eF_eB1L95A9weCt0dFp$)lO!QB=@zaWUzDx%n8&0`{;~4+nj$U# zffQ~Q>Su-aMIFsx^Tg2i6~Ou)vDPwl*BwzI`))Ka=YkFO{HQ=u41k|q;xA=0c7D8h zHlxN_%z|uk_s4c*IQn5M#h-~uEUb0)*>p{UVCGa15-+md?3k?w*ZhW9ek0iy*l6eBI7uA@LamSztmH1{#>QLPBy`x?kQT*-rUO# z&@(Tp0n(FJpw^a*Fk88JyW+)xovW2Z6W}KkSMTz**hRft*-sh`d*kE-jfe8UXYA?? z6D(esi^P50=+QydKV=nWsa`phRQBcLyo@p?8MtkHzfFGsNaf*PA=-LbzqV1TpUAI9 zgi#|Q<2PUY8sB>!JLc1cjlU~%HHd$gIW7tEK5VFk&p2N2tucR1>VdLr+<&rb02E1SUj4Xm3mZ`d z{^?fH=n|TC3fk`+Md9ez7nBX`*%N!shGVpjK zW2E0T27SVrZGxzXU_CN@KWz|YO_+Fz&xYpYgF@7z+WVWa9xr&>^F8MQwXjsHo|(zM z{4jmbt#{1>OKc#GR}Jum?KChv*GvPs=AVqS+=VJanp@*fsP{bxI~3(C!$o|7>-<4& z`lZ5aMzRm1<%yMZA$yFZ4h$KnOlwagDHBbD`(=ndRW5$b&d@0D@K*I(e%j11m1ip8 zwZ}pFZ?2e40tmLw{RAyHLjk%n&@~)!8zBNvBp@S};=MYvK}8J$a3pE0Wc^OMRbC-w z%9jW4@r|Iiwy7gro3Rp-oP~+NW?EIt&jAPcBECL-wAWHdVm<(~gyJF?Bin+XdgHFWA(t(bUq-WJ z)Qa=U?l*=3jfjcL2kBB|r|bi@Pv?pFu&okQr|eC23Z1+vukpUQmx8p#LmGIKrM(VS ztz~+S+Nwy~qIgZXf?LwT({Xz;c{TbY$4>9RFDP;um`}N&@WF!|x6XfiwSnx#JDiXb zigrHG@;6s=0Ya>s>;mWl9w(`8vt$NGN-}uX9FDfW=ul1r(2I;b+sTb zfjWT$h;SLiJc!!^x#T@BpyZW~E=hgO{1G8VqK?!t8;mELLDSg?L`eEA=285LJh%Hj z-0ks}%}UJxD(2PnJ(Y*u=*ldx0esK|gwzX1!BO_L0E83)`@RGgf-<@SEh`#w&DT~x zI|El+D1O_#@?la!;Yz!UMX-7C5!JM=H&->*0|@*`)sN$*sK=noI%TaPIwB#Vh$T2v$B?}?Nq8J}`R7n3TP}{mjKDkSjrQXNgS06^*OOU=pGF#|?DzF9KT} z`WAp**A|R1K!vv*A3N9x=;h`g7{I-Dr)$0`zm?`o1z3anH;%l^pl*A<=lE;o9t3A& z27OF)u<%mwJoH8mje-D#+eSKD?AzRC0GgbqHfGY^c%3*fQBZZYhhU@&(4alc^{A5$ zc>0>e$FV%1mum!wj)e7d(sKgJ6@d8|4@(i9PgnLA*KCox^D2f`E`DNSqPFj!!+s0i zL0@h7b|BysaJd3;6{E#gPd2cihETwx0ljR)DXpoc40Rr#N%JWYU*B5yBd$@dTEzrU zpqDfpArriC4O$^FK&4m$ZqkkIz3EcX@Czg9h3(relYU7=F49x50>`{7es`+DHV0yo zc(U*SP2OFNVfh7c7Oxqw#x7#1+Vgjei#y}{E+pR8Rca5^5B7khqaY1PKqZ%P9Y3ZE zO)zhM1A0mB)7OMxs#aUS6otrCd4;iwv)jHU;`vU6O)U17zYoP*;(8*3- ztX?pc)!@}k9<(6F71-gqZXahmm+24%ZYVKbZoK8yLw(#Lc+P;FfNzcmgDq?}iQzle zQLA!4g4RLPSgWyn5Gv8ftEN%l*I3Hh^WRCIlGFp<1xSqynlc7It<|I-Xm~e`1IE$< z0}ym*j6UUMu`OBlBeLmv{eNof6zTN0O*a_Q|4j#x|3@_N2M(}Z<|I1kC&R(CYqf#? zw`DM}Po*|Myn*Dx&L@1WR$QTicYbCc7)T7DWPjS51H5{-4k4APZ86T!+}S@mU?y~R z9x3MotvubRPcv+M#WvlQH=Esh(91|as0O!@vuhjWPaSvo?))h*^cbw*j0UY+_N9sB z4Vm7mKvQPx(5AW#APJ!OW_)ODr+zVA$wkUvkw|9DQUNz&d5uk}uE7EErR5qpH%LSv z{{{Xb39NaVA!Ryl{UGVojz0xbora|1K^rEqPdPs|XO@~pr>VB0SX(%(;l~qTuQ6bJ z4%sV;9IMXF_>Zmx1hnddkr%gDmm0dPc8a$HUmL)zLbB`9 zB6xr|kN`USM`_MLdT`h$!PBtG+1a_iv0gQxIznbvX;ak~tiTkL(gWT6pZr+Asmab# zOD0!F_hjyy){)c+o`rIc!ehYr=m0E|Np^kX4DoQv!A&OQu``^YNI{(pC*gVfDh~8( zgctTq&a5blPe&~sPJu2Hs9pBzyP`q5-UikQ7=bNiu!}rYXl^-Od;n2NDm-`0J=b6^ zW}qu_)tQ}CoIx!};6#-)Am_z(Pa{He`WcT+!Rfe{d8VA_NDGVd=5#8*JA8c(-`&v7 zn+~o+s{>Fvj$B~NwJf+G8wz;KLYjqsf)I#NzjTZ3{x7zDVK_LA z%?A5PWe9ALT8j$OuBTx9#)(KVzbPm3hzMU2^BYf5+nb$_lVC{a0KYpXli9t|HTxHdbPEp^DhJ=%F76lKO;N=HnuLeHO4(=t zWGn!DYbiSW2yGDOSF*oAKxqJcZoS&EPU-aw30B>Jws2Ns{{jLKl0{F!x>x=~-QIM7 zmNUoEXI!byX1e)}+z<$DsdU)n=F|V8k^oo-oaQ5G6*Js8IJh03UG2>|{0H@Tev1hOXdf=soAUeyb_Ae` zgn-NQ2&BekkNL})^1wqXnvLLtivnydutR>K+y5ty;V;IkW`Qt;VpUGzFw$B zK4QU!4_H6jfYqT;}a87K=+`lV{f`f z90+f3GNp3lfbjJJq%+W+2MUpQZtnbJA%LNkhVX@;7w-(UekVPKwV+SFBj5}e%V+`P z)yEqgCTKaO2`GZZF@y33IJQj7YBoj((P$JIidROZfX3uA(DH#qJ{rHr-Lzo1ZlAK; zBY%Sf@+m^-AX3CVh{=ck7T@Guw@_7G|q0r4Ajzv2Bmd8>HY394uG_6i%WU^!wHQ;W{(}Vof?d9lFb5x_ z{N`eXEB*)HIEeEz9nuKd>JKzuumeWw-MgHziHYQ13t)24j2)D1Hlo+XeK5u!98>M$Osq6apN~98?X9>Z9$05eu(nTK7<$!}?`!&LW zgo3er?mSHyMtOGK%B_IP8E`(oQP=-%Ig7vv-^JNkdv&d<8R#X9N+Vh9CNV+)>jCK1 zm!9q0TLTliy+*(Zg1ng%!qNd)Blmkn4DFX$8eVK`hUl0>rY{&q7D%7teHB=kr~J6c zE8$qxFkP|tQg(oBeo9XMj>Yw0iMz+KUT%V&4!D4KfXNa|y#Hrwg-twowI==IcGkwJ z;|zkWUZ2ivWv<(jTu(|jFOSQGW50N>eeG3*<#^m>I2lEzadq}w<7l^|)KZYo z*)LVFT=K~8}8d!;&fMfzg0 zt?#e3T10#bGg*9R!UoW)lb#OoUvrV{qrMD|CfNa2?rlca!hkYWcd00CC` z93)X>De^%#nC?hWGQ*XbZykXvzjP{}k5E>der3wIchvQ-e5wQQia8y;tz|tnQxzY* zPYos6o;}BU=W4Y|bC`$`5E}IlGW%_m8HBvkZYPl7HOC^VrFEYWZ6wO=Q8PX=! zimuhU_dZ-*tT1Mf#9g!ZFRt}oIhWHHz`I2j2mX>fSuV`d3;y|>k;)r|a`a4Xl7|2* z?pnZJrk4~>x*6~8U0>(JtF{;IaZT!%(51`oxLXQ2*jwL>b~A}=UW-_`fzCeyxC*B6 zh}!^Zd!InKGbc3V+aSt3%YoamgP1uVR2JV9a^ETUG;Fh9~+#$e>`s1xl9@3Rt zZv2B^Oz(34J>P-d3PV-i>}r=JB3#?6L#ekhmkIe2=rQ)o)%2_1%#uCx>pg--zO-1=@>NmZy}+NKfgyvwbLfpM0QW_&}V3k78S2Hh*nqu2l!0nVBda zH&eiWs4Rs4^~1Ajv=10)z40h$(rA_V|^!p1b&L$$%*f?$pbr z@`L2Zh+;ku^^u!!kYx0?q?-H9jgW8tmpoNFGS^O$K9UwYjg0;h3#Qx?{Ug7hqPAH5 z$&&{o2pu!CL0^aC2t*Ys5tGWwvw*WnmDWH}!Iu5>lqpo~Bs_Du5CP-<2~~!%mA0cL z)kZxEzkLe78Bqk49}lA`a6a- zQ$)LUp(q$ulAm305-RD*A!EXg;)W6$jj%0;@0~Bb{&tv=N)g-Mc-~3(N;0`n-W%iK zl+tk5+cP&*;LmF5M%ht7T5nI6vDir7#?RQt4=MKVYCK%=!RRxCErwvsI1F9(y(ia+ z7b2humbdX?@!eiu+Y)prr;slQO^SU1h7MEB*YUQ42rL9Pv@K~GEQ_Z0LLd3<`?e7vE zlppwT0N2Bv{NQ0#D9Y@;)ZMVSwSbGJ2=MrG-y+5JZ*4*!c?K7erm~LqoUdL5`TTfG zoRX4KSxrqGNcAggXh`z#@Tdm={2Piu)s8mxKp!dPgq78|j1ZTm@;7%N?L$_#j?9E( z<~vK*sqt@GaQz(mr=pa^Cx+ERKucU)AwBuxQ&c)V^r%QHvc-2I#t$1W-tEC(#O9_td5^hI1 z+igs1jnL7v37rb)W0sP%AROeTvHtR{n*Z|gN#rp}U2&6GFMVOQiu(s-bh(PF%j8Ey z-omjID}-8@zA!T4JM;LhUKR}AgXVoLvLtX*Zf=Ju7II?y1W{p=;Hss_ z()!=3`E4nqPV!loAADh31bOsSFRwo$Y1ZZp*}P@^OC4<{-=w5b{OFE<=3(%-tJB{E zyzDp?6H6v1&i}j{!N5`f+V~E4uA2;RbzEZ4q}7$P9S@^wYH3 zQ=;>ec1Lk1`YR`+^l@>&i+ad2m{aS!s^2&Y@c5Zw;B2*s5&K6QLWSJiUh3HOP(Q-O z6=`if-;)zIcZ^!*y(M%@O z7^9`s1Gzk5E&-NoLvy#R$UIlDyoW0Cm!Z3nvGwL8)71A73(qk_z7~p>8pMZ}Iw$=a zZaasg$>n0g-cx(;=K_6u&51vSE48qC&i9Zy=v!R>5}OhO6VoB;Wa$+9Aa3VnM8?)O5Yo|}N5vXK8?o~kjg zw*2e+%xnWQ@J+o|d+VT}A(X`}7W0&`QcJHNp$?k~hI@UPi_%+i!<+mADy5-A!4>BN zbC_7@XadV^+$*D_+y$_1wUU!m+(RNxenIS|uL0-0$00xEt`GckYOj^^=J(wi{JI_f zIOL%c@HdCz^v_XO5>mJ7j0%1rYh|0>aEpUKG1x9rku_wr{y?eDKU#UUlC*)m!ckM! zP@V48di0*zRlz#qB&{^_Z%J2gSA71!YWe)BB0AB)lRgd%ZGK7F{ca1eEj0TR-=3VCq^xY+G$^i^BwXg8qqckqt}(tfO(o~MPGmUT z^D*y*g{prQPDGFYyY=Cc=pD9WTP&7`x_=fW&yOzRzr#HK&N0EVL-WU-@@vEM+iWG~ zR?Qx0ktzq{%#|vjtIARu&I-bh-me<@F^aOUESvOgt?#;OBv;d?*WwyUuZCBtsj}Wd zVEb`>4wHrpoaq1EZp{WTRXAr!tk>e@tIo}qxr~Q5!ASu3|2nmLJqZ~N4sIo-E+rh@ zqf3&=Su)Mo9O>E)jI#3t>w!&?mS5W;0u;vl*OIdjrhZHcunB00AZxZ=^Ia{~C^ao} z#hRpY4!%&D*SUURnBTm>XR$_3^wVrb*)uaHa-0a6i^_;!9R7CDyZ#_i=ODTM%Y)KX z*q=V@!CiBy4V|!H1?~3v!vI(L{Nr)iGu7~WrJRVd7&LYCuHrQvK5l;;s@j$3QS{eqbtlxU(K$%#W$x$EZN*L! zmY!6?Mc);8MBHeFK&2Yap{S6?o)Oai zNo-oUg=nMvd-S`-QofrlscxHHWE?L)w}yuE5j-x)FBguLP}r;a^L$R?D2BR`2q}|x zkUe7D5%La`)8@MLz~rDiwI&4Jsrrah?Q{ut@h|3>TFn)Se@`|fo7|P3%=0L^&v!d_ z2&rC5|9OA7yDN?8CE&!hPW?KGT2tfoS>UYO$XA$wQ$4+OB9Tt;hm!?dyrxmtPC;ed za6Vyuk)Gis>nYr&@4w89^auueD7MSuf)0wDLAiG}tMwL5bz-+$kIO$eT``?@0Qcl? zE6TL8x~fSR=I*Ez8VF8tJ}U?Qv$h|{HjKw4c?WH_-!!@u|dKe5Ibeig! ztdrS{FsPr_zK`~4hl8CA49su>MVNGo3$s`zBRE;EopScufamzNB49$RCQiR}1|ugr zHiMiYt6-93@-dVDb%C_mMqeu1OuCX3Zv;o&-4>89r8fw~Wxu-QIHmt~$CUwS>odzn zWE$}A=yCk$+LKcq>AJIqf-VbBWmh>evXUlL^k5$Ef6zS-Vn=H?GqvG#4yF0|*P<3W zDg~`#RRCyFLRr7B!{-?Sv4xjgqD;WlqIXK1`LTTb|Z zN%zN>z_ZcxCA{oUMHXThSPkWvwRhUQ-$)lMpwC3pZ$8)BGd%20BL#lk@q~2s_{Ocb zqd9N^>t6NbJ)cQyDXQpQ^NrZ=JB>ioYk#l5TKF_8%Dbrbn?llSjm}2`(H6!bs;crq z-t69Qg7RgG6%lvfS+)+>>SfWK9^clKCAGpW;wE#w)#Wv%6HYJ+hLfU*RP2vbE`kTp zESe8{VN1R^7#RCmu+K}5cs0V99;)+0gro%k)9@gy1fuAKGYzz`kL+nr-9QS;NZoSo@Bj#`r!vP6$%I}n+k z-)p3Flhh0Ug*7#bb$4LYL@>4BUW~~4iGl5t^eij4!Pq#Hzza82@D5y2#mNHyO$1n= zE3Vsp_$mBB1DHS2Gc&)bmzI^qQc+QfYHJfjxqCOJ!un&y(9{%NZ!npFMTW$(rD1#}>(8Ow+ulQj zKYbE#?Nu}eC^mGaSrpl6k;cU|F4HSVC79$`J`;CAp(c~7;QLUkw+#2g2Ws3c1nOwX z?*`uMyT8iTHTIlMbx6;v0g)0u0MGeEcAE$I4upgF^2t0ntQ$u6?#1!32-mw~DUS(G zj*)lL5$g<$#AgZe^9%7h4Bb-vZ|~4EadpcdYqB&JaH3f#1%DfE$*EiB$h$^wv*}31 z%wj9EFp=6aJtt;*L0_Tq`#w3yRJb?()a~ETP`CXazrXUnod)kP7i<43{z8gyrOtfh zK8lLPG`RL$RzFO4B3e@z-@9%GdHRrB=VDJRJV&lE=l-Aou>0v*%G-CQcltFv>G>q2 zxs>(_9_G8{IR&3$6qs1Op%NUgbSn;8I1U@*EHwabYsy5I`af>#HSh*$&ept}y+}w% zrd^?gj~?aX#dHr01(gfr+%%VyZo|gRR@HXuVKP?N zX4%5~P@_Q633jgcNSB^;Y*TWdzPPOSKJEXA$v@7XBh*H9keWmqfaHsLBGekBw`s z%@${#l^g#;4S#PR-aDW<&3kMhW#`#OkFTqd5qx*j8VR59$WCVpoh(r|#;VA# zSZHxfwonG?8}Eb<6p~TmUVSqb!?|?OiyQJipJqGw9EQ{LeYa?({|E7$7`QoBBHefX zNuybjcA&GL!ShPjzKaOFbGJGKufWhZo{6PbLhg~7my(h)?TMyt5}5;p=0~_WvfaMP z(_klDU@>vG=D>UN&5nA@SuuGfVotMI%AP(`*@jbq53g_LgN;0ul9lDWDJKAo|*rrRdbjxUbi&+ zmg{jzyt=~kT8mfY7|I=*( zc^nSei^IGm=`4#2`>kQXhxS!7oEVUre=jAy_ZAK(u5ZNAUNBI8 zz{!Fr4D>j}Qs%GdW#@&SYPGm=3Jc3jKY8-RbaSkz3AqL=r}@B^DDNmD$cVMVw+QBA zO@NulVI+V7_xlHdKG(lk?roG=qQJF6-&+|ApFZ{NmuT!SWlE>0rmKd4LaqJZ z{TPt{{pS0>{@49vD*;!@^p}rmJ=TmvWkC8_ce;&n91s9sq*KmZoU=RVXyV|G2LOan)YeX=#CLUdt#v$DqFKz;BN5Ipm>-YQju7Al+#_4Z0- zO$AkFK)i|ab0P^_Fq2>2tB}PJ;Tezr(*2sm=4 zU~@QEqIO+Rnh3&U&=k4N{y;yFe9R?C`=wu8N=1h)n4F!mGgKF9VrDw zPn>JbzBlO4#!&Y=8QzG$7Q$++4j43dKFMzlIU!t8&G0)&Ms@p>M#782`~P*tCP5n& zv^Iahg+U>0y15m+-Szx{<>y_G6OpR@Go&leqUwBKyL%<3U(25;Ik`l~zEj2@kEJYr z;~uUyG`Pt7_OwVvYhFR$pCv4p%8{KxE(E8@o}e}zY4)bKX_|3lr~>4s-=ejTTYMKC z)N@jPIkB%%WR=NtSo5r8xrbf<__EP%_l#^3y7J}?zjgjo>H5?|DXsN1I$R_K;k_n> z8V|!bdMCc}n9zH_>I7Y|lZnL0*w~CYERPM@bl`W7yF_ zm(tPig?OTEI-|qWq79;v!~!%6=Lo4>Uh&O@5RK;gL|tv?5vwF}zM+V0k*jTinpgTM z*RK(oW)uo}=;%sDMDqOdF)ixj)67I@?uTdfh`c3bxKCHp7KZeb#k#v7uDQR(Ws^h> ztD=(_ddJ+`NV7L(HPp~agu2nIRc8I^s(2$L(?~{TYl1w&89Rd3TgDu%4~KiVHTflb zZPj)(VfKw5ECS5M;6!J^Ze}){Us$QMvy%Cjuq?$+>Ihzs4$|bBdYB&zBz_&2nQNC& zD1|!uT5bU+8O0oR^Wft8bQn)CpLjz3I9aV%W2TE?@7>CGqmU=J)01Z~cb65QVo>3Q zE9<{q!hX;?(n=8-z)cp_2isT1YE0ymT7+vg>#frAD-<;39SIfKuR_r*z+TW=qdxrJ zo}Q`{@&L}MjJUYGGmo2e+pr=XyLtvNI3=QCV;?t$bAro}Qina2@@29D0+5>xhMd9j zAvjKHIGP1Tx#ePO3pF#_TV&tVf=MA={~f6(loOB1%FAX@QUxxy*_AGO(;oZ6p=N|; zEblYl{?WTZ=oaZ>wdN5fT#p$QUdf+>zSLc)W)nZe{d~W zYJG$AB+tL;MCogZ^*yM*#HTwX`S)V2L1wU4>0pw(I3FqXj*V)buyc2Tl*j03Iry7J zhQ={VKhZBq#erFn!YZr0iqha6yZZ4h>$0d?G&d?cIo;b-c@uZ(uaV_Fo*^`~NVF`3 z0_HZe+QG)a@mIdGTr~~J(`sKZBG>UBT4W#xHLC9Lh_jRl8etfN?qjm$fzKYBw3IK=<-%k81ZSdBakz<-d) z0mI45&rfc;)2mmaS~gNxwehf#HM1I)N2c>1ECA%m<>^v@G`#!UzqPd-NJRq@fnZ;k z{n6FwBJQIu63z#++LV=q(=MB1^qMVhb;eSKpCDvf@*v2pXtqV!pHya9Yywf#on0(! zY<#D!INJFZcW%IIP;6nVAG8DZ0wRGaK$Z`F2E^U{yPuv`pT1h*_Sa6OWO@&J6x>0K{ zKNU+lSqOdh%$)>(kSOYK-DS z&TY|Yd2;gXYb&N9{&AP-%H>uWjC<8Ar`gJn8bCeXX)|jOzPoln1fBl&uIhTG4BV(trj@H7Q<73IR=Qv|dNRQ8=QpthoS`-_vhb@h_VB=jd z=)xD^@$iP)+&rUV=&mlQtCmce&TVE2CU--QeoM|fo}zsS@^&(acv@+EI=0E)eKilc zz}$Vh+;>D5z#%8OBa?L{|aoa9hHHZr1fHo|x7oSgi~RumX`64xBsWsH=Pq%`O`g9GJt!^Z#Qb$#Mhhl4A0 z53mn#kS-|bib7=Fx;^%9CmBu&e|ebF_M?U7_p|R9=Jy9np{>Z~>k(CyAOu|;p;^$O zyfjySQdH!ChP@}C`i6FJQr@sq@K_onzjNfJFgE)W{hzOq{Ccb{86Wr_ig(ctzoysB zdp`9xus!JSNII2C$!jhrMMIz6EX7s_Q!HM+wi51RNwj=u1jbyLO%A+N{kL9AmIuaq zQ4&hQU(I!w=_bvUam32)-JR%}<~1ZJkbmV#W_-~X)@-Jr5BSx2iJtdvS5IFK)SPmU zD=O=NOVTo@>|0{H9W<~al|jP?&AV!hIYe=B97?|)N{xWtZi%F^B7@{-{2EbOMyH&L z0tueBfShyQJ^q z`yZTo4S7Nur}^v7G3~rhp;^eq;{!C{iibEKCbAm%sW<5MG?#~O7R_Uva=H%jRf z^Sg2AfCZ7N2BSyZBKm!|U8ZIuZZLseU?97hxuxKn{xgW-a>Hou<`_WwX>1^$5FT~< z|4{aoVO4k0mkLNphosU-Dbgh+NFxo>C@ml$-Q5kB?(UZElJ0In>F$O(*Vp&`&pgk} zhxw>*xxYAPpS{;!Yi$n{+mrB*9iIe36%1Pnd3G>oe4zkWZq^pO%j88WO-Z`yNd<~_ zPR~8!W+9!^m)&X!9TuOy2m@!qEqO2gLimo z`rpQS{uC_ess~qIJ4wQ}F!>sV4U?^shZnYAe*QsAxU)H;>1GT9K74qoU~`B=i$L{S zFh#z`Y&QFJqVKJr!F#{U(TuuyoZV6-O6^;vblKT=R+ATzQ9BV)Nmu>`hu-qnNj2(y zeuT4M^j)h;_i46zBSIwj=Rx*aHrp59toxC8@OdT`SHdjG>Ek*K37bBHvt9K)^C|ZI z4L%UStw@AkKx>+MYVePpgq!`4La-A#_L1j*BR}I!k4lpm?kd zM>=EWJ@BU|lUMHde84mM-t!l@t(w-I<=U{}zzF?$F1nn|!hmWF$v1x@%?m@c@3ygo zvTcxNltZlTz%Q}hO0A-9f-w`CQk=tevZqaHKledJD+6EOriJxY^7T&MHdhA($#e)~ zbw?R`-s66+W&E$cz6Zg+*yZ9;CwX`cx#u0LZ(hFyq3U%d5-qE7nsSrW70)vAY&SA_ z3h7$#U~tyU5}8TLS6uRy^7BTD? zP)Jo@J7i+)`CiR2ii0{zTi;x{2t`I-il!n1z z7$UGC-p3yti2bR|)c;my&$r~xUDX$nyfb&#=!s5@4+4<=p57f@>Lt9T{6UM4sK&Ug zBs}ihJ>R7GhP+Jr1+fr7&20-3sBvd>9%-q)<)fX<-zL)f%y6^Lpvm#Zw;e`(JH4>K zLr1E~ow-ar6xmwM5%&wi#j6qcd|tC$2C{8yY@Fx8if1EhD$};P07>~4*z}F>HQARZ zF8ezuV54ve`IoZ%8xEJC4`$AeKNymJ+6Ug%mn=`zW#cwHL!*^YmY9ixBGS*os}6s= z8ej7MNw5M&PG6P&DYf#-iiWm0MFJ=Kn2SW<*e@GE$D%oVN$>rd&FKga7omFat?gy5 zAa|g}cDc$PpD3dlXLkG;Ly5Au27uZz`vTxEjVHHHr%-SAlT)P+??xMGeRXX1${tE< zF0#mY+mljW&aU_=M4$e$v$DBKT%>TQoV?Eo`5pQd$^xn(`~4W&3l4(zE??IlY1Nv7 z^ll}mOgsm(7HF|(xYZKFt3k=k?L4Cu;R4j{knnIF4qF!&-gHQw+F)@-MI;#L`3_WI zXl9QIP_w0h%bRf?qTl25{Y6OtP~z!=0BC~xo`qglt!ln+CWj-5hZ&~W9lEvB{-Tvt z?by>keyox@g~jHSaOY3^zijzMMSs66WXXZK#Zqg06K`w$_Wicr?JpluTR~jjlTk~| zGUZqsx!7{WuhVRInVZc_;yBL>l&}Q&AvAY%H4xWA8U!nGHT*k}$4dmDO)Opk&_u25 z4#lOSG)}9w_@!pj6qfz#;sU(*v%Xj&C}{;2Cs5BG=@hiNFd&m!wM}^0-3Z3(Z3!)W z@*)p4w{a_5Ct1mMfKxQBi6ecq@)%9ru$6vC3is85R$A|n3eg+~)5o82v#&Z=;y8I` zrfv9f#+|fN_u6HM7#E|U8C;an@yCA?eSsbD@T;e~XGT$EUo=!`XXW(I9&^__X~l?~ z^%^l`VkN0pN^D&e9-|eDyGCVNpv zRHmo*$&}+;nEnDqZ2;&F_}^S&!$-ZE5Tl*;zlt_SjYzg)ZF{ zg!8eIUgf)}7HOlagyW^waY;uZt{Z8mUtbJBSP5r%C(>K&>$8-@F(#Wc0PLor%45-J zAi(+w)wo>Hcc=TFVc8}oW}o#uYH&Z|ZG&6toTN^P>CE__IyK%|If)m+d65lTI}(0% z#`_cD0Nae^5AHIkZtJ-hjwrCWThaeI6WEg}xVknA=|Dw;+f&7fIl}<=0m@1t+tke6 z4O|tCPsp2p0fhYKig93~B<^x^KB=UqK$oCO<=J~Aw|=@B#5gqrcRUEdxL31uJnCfe zs?Y`ZnqZg%5KaGdNJ#&7NZy`8j^6Bfwn__q&R5Y0CvEqTinc3BnA(5Ee%IdFV+7}S z^e(eDLn|-EgY^mScT|L6ZG+$;|Fod-cBHKDBLdoVC3)Cc6<)c8ZvzK^PRJ5YGfEU; z5^UO>g?!(gugpPmPpP(&GN8=vej+FAPl6jgDfe0L(If{eR^L+5)qawsiKs6x&PF(w zwwkKUN}H>7Nn_~YJKYV;8)mlG(yicn>A++AA^Lx|QHpi<@YHv>VTN$ZAouC1rD2Cc z{0mCIM_#g%m(foCSc&pru-SG-eeZMmbosnrtzwCSUG{Q`bm!_a+#e|};EBBg z;EpMHG>Q8oV(>9zZ4IeRjn|0wZ-NG730m)kjR3Tkd(06c4aMk6q4l&@IjSifCX+`< zm`4rS2Fq>mVMk<#fS6F`f zzIw7y#iYl|z~3fQBn0@B)r;v4gQ?j@KpVJfML`}{KauB%fpI3wj$4~I53IOkXcE6n ztp551?SjCFicbk@(^Im5m^2^Ner}HpO}iuX{zl5yYRGU`qH{y)(0FwJW{*>Dj~w9t7K#VZ=EhPJzv=UT(~C6d|2R|E zBSX_DI-bEDJ-8w$OUlIF()`bdD8UbYRsQc=bQ2S>B%$Vs*T6-=#yr5^KMjmIjw~%L zT?r@k0Kkl<)nbIEtPBeww%&J}ohc+LuGwIT)NO)n%H*@nQLmDwb2Uv|b{72n=&iU` z19-Om@mc5<+xq?8jNCWBhIe%T!4fQd!8=JJ>zw|VxAFJ@x^b7PNKB=7piSzjju=q` zIBsU>_4hP%SA$bnzzKSGoeQUKC_`x)eCSKQ?mTepG;xA1#f^;%7OJ6&1H}0?bzfpr zGUmZBG#|CZc!L=0_1^M{Kui2YN&f`FxEpI>@cwfa+5SGo&`Yt(!N0*eMHpN`if~jn z$b$Z%imH)7lV!diZ@uiGKqr{$CTJkpkSWAqum#cfCA$I#H#j9un+BlX zd>=1kD6M&peJAKn#BEJ;b#uehu(bslZOQcH17fYCGc;I zyZwFyCO7?g^Ut9;yG`vYU9Z2-wVo6D09O&xp1w*&GSxJybjS~OhccQB9y9FsL3<|d zFyJFcmWR)v`{ET(n=K9I=5#5ErbXKm93momNDFaQWF*#HiyQwQklEt+x3#sgf=Qp6 z$FXXAQe33Fah#UqRFssQRY(#Rb#--66wr@7otUQ2b308y)%>-#X2XssILEXtP)H@;%t6f`m_>KFz$oc04{i!v({?$K+uM`sC_ld94NT zVKGsE(Gc}~2CkgxU@~HEnuw^V0&vs5#KpxE9KL<~CR``;6C-DeX`QBqz$|JPDzFMTV5G}8!;k+UNbxA2?O-YuArs)#{76_Jvq3#iDcadO+jdlp23=5 z&&JL^2FP8s{QS$&pRS?N&@h5O<3|A z$p1b*Mra_=s--o>0;|Uw5qcS9)%lY6y&!WgAJnvskX09|8wD1i?`DTbMr1V^WRFie z(D`>OIFH&1JW7JW7kqCR{tHA#w-DOz{oS6>W7Zk%Z*al)2!$3D+*7XQReyh;0$rpj zT~VSSN_vz)(U1dDbe`7*3J3^18cgDff`NqvTm7smnqCD<%=8b*6B;$y z_fr04A2l(;X)*n?_}zKI{r$pj`?o`C%>ccl^7EkLzcPVC2Jj0f3@L93q4)b7`cs?p zxp|d7(EC{?s~%Q@6VL(vNAORcJmDf<1=Gi&Z9A3Rl^bY5;q^yj0RK0`w>ivB9Q?}) zupol2DpWG`|5_d2X`xSAq1J)<-zN<%JhiIHOwpj0_8`WU@74Vz{I>BwyZkEBZ%RIF1_rO&%#Yg zK@A3!YnD(i!tGj;z_+Bn@Tr+72Q4f!qKN|9N1Job%j%dZUP?XQhM%*`cxM+j@cN5= zsfFYEI%`3(c^bmAwUHw$HUo2;8Ai(1SSYoxuV80u*hI!&Pf7;$c-VN0m6XbR>VRKn zm&E#Q$xo>rllim&`DAON<|HwuDqq3yQ#WQv8 zX?)$4B$Pnpv&`73)Qm}|A=U5gPW53sc*ZO_+} zStOiCZn-qKJI$!S7Ew5QBy;&ydCC%$O@qYo0{lruX%An`QkglK(R8WNNK@SiBs-d- z*-*cq_#aii`h_O)i|4xJ4N*s>r-RCVKU{T5<4q$JqP9SIEYqTG)%~ow#spgBlr%jg zJDic0NvHB9dwO~8#i(j)%YZcM6!_LCSXy>Fh%U!p1Wx}z{QeRwgYk?%JA|9g12u~k zBNPHxjorn@YN8cmRzre6A3}}hpX0B?KZulPPO4Uw>#sE%x4b>P4s!50tH$;<2d%&G zr|x?;)|hW*={i>1h09gFW{UJ*pR_#pEXMx6pc|9+v* zl+<4cne$`Rx;V=h*pr>KKYSR=U3MA{%T+PRQfSG-Xz$MgG*1qV(%szY$G_VE-o9Cy zn({q3_Sm;Mpv`(!BZR6V=GawcH&y+q#fd@qOGat*nwzBvp?zLC!LV1npTEF2im3u3 zZ@bss<{IkN_c0&Cwh7_l1#y>k!T|1FN3hEK{CxcWBhS2rMvMi7({iL<>$_I`29F88 zZW8JXj21W8Tby{^K|9Da>&WtSa+WlLI)85g{q;oY;%wqiH?9-dMSi5`hHwN31^HGf zBkQ!wgUH;+@dYI&SwOSm#IQ0{gxbd`CFk&b@#vbYLp`;DDkNOT*SEH>eb3O82{V(D zKHeYq`L^it3;JAb7mEq@=E{S>yO;DvA5WZ*srlv&?37zbz_oRR9e{M(qXZh)>-U$O z=KDZMf~G+BgdP(Uipc2Z$6P?dO!M+(_qf8LJy@?$V0Qlxo>F{mL^1BPzxnmUWF;{H z8E$Pem)eIGttl*0Q>BhYVh#8YM1tz^i&@TX2rz5$V`|8UC)sgt_>V8n(xGHtnV8=R38)?qcIyU+ z--_vnJ@Uop(y+xkg`9llT!biqKA_=NqsAluLoRp>3jVdQ|HR@)6hD=qhx6TDI3JO7 zWkv<}@x=*dU#q#BoWNz%bt=gj+2x}#H+q`Y^p zO+l%7R@@u62%H~q?1en0%OHi^U+K2{4-wmgR#tOP2==c3#B+~x=*g*e3QY;bMPHVA z^jKf0t3tWj^ZJmBM4lWX4>~2lX)ZPAlTO_=k(+o&8`^&(@;%_1uuq*-omf!FoL+i1 zL0f{^B2Y?;YIdunA8T_66Y2NHJY>xz8d0BJ$LxD`Xf26>wnmTZcBL=#bGs0!wA0I+ zZc-Dv+2H_#UFn+ka($adH!c9de$H-(W~TK+6;?AQChGmp0OnD3wnwXPZG!W@ z9QCEgd#;4e8oaMN;&{0}0i5#-PiD!{Hu~&*q{j4M5}k5`{(l~>Az%*OekjqT6^E=# z1|yO}SRF2(n~g{xe1aoLxO^6dgBaa_mMs6*rp+lNHJ^%Q&TAmQ0_qZm!V6sL{H){VuBqTfVtYRzAV< zTI^KR>)RBbYPN0m3pYEV0z@p&I7-Qs)yg*BcB7>!n1nZ^5)gdU(-T|?vF^Zvt-3H7 zqdt>A`mYyQhV=PrS_(2duADLtOB3=UN+syC5XfqfQ8P!yNU+9gU ziE;o98y(U40bt$>!=Ge4H#EBThqHuFA5cim}EYO)z6{vAi4f)bLJEhai52 zS)K4avHVx>j)G6dr|_0A+1W^B)I_wDTsN~Gyp0QvX&Wf*9PqVd!bNw8h_+huCCc5t zEq;4AyqIg+5Dr*=^0y-32E!Eih!m%n>T^pk zQIU@4lCIV#Vpk-WoGpt;tJ7C54nVqf`a4k9)-_yJyPD1Ai^?rWQSa`7<>kTUBC3>c zmQR42-!WK-4>8ZPr`;y}K;P3?^aBC!ReHJhr{Bw5CtX>H(FEhDngXz06nR*A`Kz=`&5+1|GEshsh-S^lc23kOQ25SMkO zza3p`DE2kwa8XTPj8d-L=>X-S`#*g91Ie^PYk+a`E@;)l4eKEtPrwcGTP;BSggc+w zr_X6|nR~~~?5Kl*?_%v-L7I>bK+Y%dqq5J|&W2BnXKUWCn(NadbEVWj07WmxgW2E|}JEv}kZK9Ig}HIf2>LsDYMlX4Q99JWdMBojMK2$j2;eE)qN#Kb`F@pE?|< zU@L4wMYkBH{dBklXMJZ%(`?8Uk)|JqWu9$}k>q9t=rkbWGxm&z|JHL8g<}s$k8FjX zceRubgl7!S-OUx%5gNI=Ltv(@jfeB+jdBWjFgM^2q~Atd!w_V3Hj?YF2WevsDFWSv zOh%+tPQt$W%qcShJ3}3#0!9)9_2$(L(DVi|=+6%9x6J-5sdPo;{PiA3OW7UYUp~WV zXty;egzCa@t}lpY`Xx(_;Bg{r*_t!$bh45xRYlDxB-`x_jba}*0JT%oaebPK#YuMIi}ukG8SXtb=HsEcldEJR!DU6!~>O)CwM3b zi{q5(24%;EgJvVIz$EMEx}kc&ORcXoy{!fpiYX7FT3DdwuYY)Z=(p$pi1@x{$x#x+ zW#SbpD}8Nj*5fAa1@& ziXxjx#?Wy!F#$v88_t3L$ig&|l#;fef`&zHIA(g(t>)QC?lRufc4SXGk9)q;yIzUw z-bf9mJ$e==Y-S^UTQ<+bG3rylSECZIC*0^_DzkaQz zq-sZzn~J(y0R{W%c8;@rtw4T%aZU_D}Zh0CceA*2M#AaNI9zpyvY&85w$23h-UXMZ|l^@H!~oz^O7)0Pgw2FwGX5ckzo4y`rPYPc_`Dxn~mgB z^nV`xDvUIC`asasR4kD6GyuCNoS8>UT&EDIoS)1O`!{t3Jz5~aU41#Uo^&Ox@8Pj5 zIX+HXQx>SaOP6t%OSc6S>zgr>tDR3_^UtZYk#~7i1s`gP#8)2cYJom=bKfi6Vb5r* z9KX`Y5uXF(w|Jx%`KgcB-hQN*^0QgxS&dBjboyg`+`77GNJNLlN~`E?N8bT_qv_S2 zp92^qEWt?D{{r>ArJ7dz?YLEmSXO?=udaSPhm_Io-INT*SPYx94T9xvM0|=gYGscOU` zwW_|?{!g&Tdb|PJqynw&3VW&+425;P%-I@MYGn-*j(nNgBtpHLhd+p8x4c}5A`d`m zUK9Ei=}k_=#~-IHj?Vb?Ji;*RY4$gkW>b@^3FSKu6}PaC8r9oL0zqY1w1%+g`N%*> zCr#yjGQk7QgLCl=IxlYVU!J)Pgo=}vDHtS=@=;x% zMxsXZX%(dOeKo7kQ}QuW6km{%sn!cLc9zZWf|*QZ#m;nS-${T*m!YI?(xg;lX$3mI9!$CkITW;s2BqX;x5hWT>gd ztctsn6C_E-u|H8F39-ZlOX`6)6%9MPvMw5(^6qj=YPH$?dSw21u1rnDG#x$t6OXUJ zFwz?t1=qYc>v3VY%t@|%I0#<3 zbv|rEP}I;c)u&yE#VmuOY;&+}f6jIykG))#m*c2jpOXxJ_fdF6zOd@H#&!c#QiW&v zrp%lHt)&cgG_al$W3jsrZc+8)N_fItRHiJaDG&QE8N5nyILWX~{Fo(?!lc^{|5VZHON9dGPvy@EY@G~KZrjG3HX@1P1_<)(ZryWBhZdR*e zu7N=$hb2^M8cNwN&K!IE#6PmtzZ}zD`^`(QcQ7ty+fE{et-JR-i{{jb46p-V*f`JZ z9c?+~C&w!hW|~}NeC;h^#wgF#2o7W6B%3x+*Hi^j*uDTpw@mnlzjTZD$4PmWozWNS zT!tbibiF~FUK?;(vf*uqk!b%&U1xF*a5BFoC=Ulk8-6_Kf-!BoCTS|z;!=nS?^>2m z7r4{NvmBXMIQ;~ZxFWaqG}yf#F%L<52tEO@Wu@cZw5itEHY_Q_u@9U~D9B(|wCZ_9 zxdFjg(7-zgTGQ2f;ykKf$y+7>u^jK$49x+Eg|9@UbfNA8!PD&~FW`Jy~kd>#fZezSkTMUL^q zU`iCU^ujS^UQPbu;(;|d;;7sZlW25O%s8x?U?QYHdco417;okNn}>i=?z++uPPoai zug@XYwU|5S5Wt<}Or!BFIZ~9)hQKgH)d+c{_lv#mlt+JdP*|m68b(7P@*!kZ4E25N`80yIUh_u_Y zY3*^~#A`wXo0|Cr5i*n3Go`9#Vetk)#)Bd_a51FCk7aKCvOs@81xh^-jYaWk0DVm? z#WUz2x3Ew%{W4mG99et&+8HYyGV#brcRhWE{eDC-kSpi#K81D=vI?gj}d`@`oZC|Fyswa-0Gs}5bXO(Ii(qQXk_94$8w!wuj>n8;sgM?JiQ)#LZJf#fxM z5hALws0}ZO`Do~M9+rfD$b=XwDkV<508P|+FB@r@^K_ygD(+O0OA}r6!h}n@k)J$y z1x`o>1AWiuaE%i(lJpF}$Ph&Qe)j?(RAa6{$nll`n3hT^{oeO7;9IWa>@ZMR%l5q6 z3O>}~Z-|Wf_6jkNqU?2LWcmkENt%LQ!O$MwlKqw!_XeJ=_Mbc$Ol{m}gP()XWL^u8 zOyY^iISW4meD}yyl7L_ver*yWOPPIbq0Dpr(Bm}^c_frnerx?m`^R2b95gi)!#)e$ z|A>VWvI6DcpXpVsEvMjSam^Mbv50OE!KvO(^N`JyC>ArYF|;TM;J+6q<&A!~7XRkY zv2{#LBMGa^D^HLKbsn(pvLYi=+`oQlqU|3ys}S4<$8Nan@0^TT7V7@O1GVi5r~k%~ zakMPy<1nmbiCNp7%Ic-(ABUr4_>5KFeP_d&2@g02&FXpDrn@Z}H>_Gq6K!aP(|l?7 zV;1b5`x3&tW|fziT#4f6(-fy-W00|z;VmkImlbjI@u|8$V&%d{pS5&PQc@an7L*bd z&j-6vPIj)8l8d9z9gdO6$1bz7q)^Nk|1>ukj%G?|*wCN7QV4Y> zSNu8zc|qCh%1tP#Xatng;!+h{fk^SHH&1KMaI%=IT3$z=3}DUt+)2O9r7ScXq|06| z?W?3xCtHGxK7D8?X^<6C{2^5qQ?tJ%$@>|`%$7#dN8-%&wDMY$%<|j0l)8&@{TDY- z%uOb5*!)G`tf39tDOKAFm+5rMGBD-D5E2zdfg&L#g#*Xl84S#nje#!(yO8BDU>QgO zQ5$b`EYF9cF?G$FuZx{Cr2VfnYDYd0><;l9;2`TusL$b%4G*~XQpJvxed>UdGXp}Z z?@kwA=UahaqbCg_b80Tpdgp>Hw}}i)qDJhugY@HW(1JOzpC$|ghs)g`hIDdBtM6Du zOD(m}DI&9DSQE+{!f4Tm_!62bzcE~qY$m2^T(EvWYNw1Q9=1426DZmW>x@6uDY&7R zRK|BXGviZy=cZw`uyS4GY(^JgkxF7JU)6BkZX`FRP;9jBze~L_z?h%Sv;4NG5CX5C zfAfP{u8co0mX~o~GRTU{hF*{fFGMdW!bgB{uruvQaJz@1`YQ zgA1IG05};4Sm6tHXM;P%=}!aGv~j6=RC_k(8XYRlWcB!Yk2 zDk|!AV78?JCg&JnSg_ge&Q(e3F~OLNZD<3{^LN)f$0Gt$fuPXP9AKqCuB4(#HC3(K zjW!3|@y$Y8TLs9Z!p4V#((kKkiKKH$Gm*?M(s$S7;|Re z(!AvMg$kWgU4^>i$GEdF_rdu}7VogeZ+4h>CPK~9HUwGp;o&0#p}B@FJeTy{3Z>(6 z16_gS4d2ixmQh`TZ|yarI+<9IY&OlQsJ-vEtAs*GkUfoLuSEjJ$c#Ttl&Y7>W;rxTIdrHbA8!lJJ4b5JZm3|-P=1o8d)a>QeoNJcAEc3EUs=kZs-(o1*rNYG z&q+C~8H@%vT2xfa7BGW1$eyatF{3fJ()V)R{V}X56|)@mG2jo7S2OghWc2wkPX*eO ze2&@ltE~!d%V<&b7Sh0_)AtXcQ<}wK{K4yARiaw?!(a(;@U{SJ3#&lapCz-`0kgl$ z86SJPyc(FA2lDxkXS_oaWAgW*@O>4PfN2$Dr42l$Ckj;t~$>72qOQfBXzFGuU_=fA(y{lvJMDa+oG;Lh>JE074$ z-5*bA5RJ*y_~muFR6fHU@;9P$l*^3A7rM) zIv;APP^!((gf@h-+RA@>=+*kZ34X?h4K!C~vAd>|q3!Tj@BSR$m6$USs^^qj1Ea9y zmEmVNlNPBAP3g)s3JCin(-LL!1_e_b7%tK35{YQhvP1E7^%dGUgZP-{Dln507$mm% zm^0xvA@XEQ>E~HoVD6w>fhSr@$(W{BB_n>*j-#MqzMAiS#c3uM2b_}jY#gIRNCDB| zl0#NDT()8aU10%UHiJMgeW`KUD?bUiE2wYX^)G2&;qwZ5IQHP6B3+uYeB271LWOt+GeDt2%^|rP{;gR9Ze#61k9_-t+O=@vv$7IT0 zbnlXf+YSDVr+i+wJj^pc?fY{mRoWulfH%7}EOrII=c|^hLGxY6($7w>L+TAdlBS>V zB@_x!XWXGvLjSKbZUki(a5xoopGqs?SBZO^Dm*$7aQOpZnYZ1eN3ZJcuXnb<{O;;R z_%`ft*g;m{2_YD8!wKb|?Wdw=E?>w$mMrzoTq+vNm8E}_S<1kRm~AQEe1zjM7|8gG z7ABFWrMq}PZf+f5e6OxVvpxyVC19hY$ z6CThWeB-M+$T|I`{cy{gu9s6Gp8X}>d&AU~o{4NAlW52Ib$|YM*x}XCUHR+cK+=LG z?6X_3Mkl~WfO&9hJ7@U=EZ@c6*I{}{t1_QPxpc8|vqZMTxa_TR#?1rJ_xm(JqqZx@ zlx_eAq;UUjY2x4dvl4LEs)s6~_8QQrJWwsg!b3;&w!|?~Fsz<@>XU<=EjC|nu6|9* z(W|G{sUGnr*n75Nzh07qFF;STsgG1Fvg@Om#rrXOe^nz1A5I%C3CbaADyjgupCEdx zgdYhcL`oYIA!?J>c_6}i;$Qc68tETbQC=*5>wiTqs9s7&@KKsO>JB4T3l)j#{j^H={Oz`gm`jXoA7>r^K?0fzx>t7)pa}qIh2$7tj|HwOR_5|d) z{f&(xoejS~_&Hx9Y5(XdlmZ(e^I63%iUX@)stlxB^bc&&#(TQj=BP=9rO>sVB5K7H_ zuiGAX-#}ssm$WQc&+2`w8NHu``hw;+HB4T_)m-%0#^;iwM#SQXiu)mQT*ysz6OVuK zA+?=$p5>Ecln#j~8%pZF4aok+e^{qQsy0_JAREhmXDmUenegI6#RMH6M`0^`WOBnz zsb5GY>6?_GSi`KK$a2&W$_eylE_s7qv`i`ctJQbXGm(tKWgK5%i1-w+VUHR;SZVs7 zo_#>f*g^P#QPF&mynMVu=Z&0_jbtD5B~ilQbJ4tCSHM|es!ipFTIsSo z1Os5M)^hbYdOu7U18up(z}AlbyS0&}K6(r$V|QLK!(1Z*0w{!vLy+B*oQPO$*7bTk zHL!?*wUUzh)a9oKg0OG2DWLLVt9~Au(~s2@v0Je8D<;C3BGxaU?N-2=33o9RfqDL$ z4ese_!iyK%u3($%m$^OMEThG~>{TNDk$y@G^K?k<-8)ax^4_1J^?Es#H_4i!hwucR zLxe+c{zmd^gK}%VQje-vDYDl0nq;Alx*--f$ zV(Cu1-jM>!!g}iGNP@E`fDgBYYNE8ya}82#^_1J8PY7W9-vBn)pY4y@pWxzSO?l4!7;2#Xu#)McS~vVu}A_BoE`ITwclgo8^#O|(VTb|Tc8^CnPu zE=7|LQUlV^b-@1!vc*AGctJdkbNb`0EwU;1jmN#C$0m>wk@X3Y&l6lW-o5w0Cu01x zPc$wa{OV%3?`in3E9b%a{m$KZ)&)7D?cnppkdkc@DnxA~EW5%DSp$_qNhI-#sAH?$ zt$r3K1)v^nm66tgwM9(w5~J4UZse}y(6@d1%#g(6hzOy32GO(+507zSN`Yo4y%!Iu zC#$r;?0bKlO2Kar6l5jd$_**>Wo^y*WT1DexOt7)*$P$Kvx`9@l3DmA7XtB~lQtK$ zC_shHfKODT@olP)Wmg>J5xmjM#-S1i*50Y|MXghqw)rNrd7?T_yXD>9u*h}hc^b5n zjX#U{z>MwoS>KK;fIoYMJ~wedB6{|W8fWh3t>e9y?cM=M}@R~5ws*+=)|SPS6^<}(SHMEQKFIWHXq z$Z+%Bxw>GE&`;Q3{ibiuj9U}P48xozJn+Mp(sX?JX!IRUH|25xM92T_w^&4S?bk)_ zhOO|&h_p^{${3`w>~Ox}ynZ>NIvw@tYVe+9=W?ZFdG4g}QzfQ|_=E8*-4kA<;vO^V za0VFmtuMfAX^r4liQl9EY%>*vA_P}Df&5Kk5>z%y{e8c*;4(38Wru>U;#&>A(AP!5 z{t0eJ7Qb(L;gAPBg`-v2Q?P!s%nE!IT167mhq4Ikbn7vZB{PSkNZ9Xgyeja$-0 z83@w*H3Dt3F&`|m$56frm<@TNQl!QF7Ag15C2Pw{XQABW zWuWo>CMMFIs#`gR4G@L~f^)wQ7cd&@59@!9YAHp3-cyQ0$Ah&|%s9jps& zLz+b1I?tCSBns4jxd4vC-=**&7lO05fKEBX8|3)KPQLiVvtWJ=S*lh2ZG*LjcT4@q zru%Mg&<6JZ)v3>}#Nj#Sj0I}Bgz6MFJ;E%Cj&D0lhuXAa%l%7{OsPl&e9|;ay^Z(S5r9TwaG%z1DGV<2ll{RHBnS2yC34 z^@5F_?Fo^szHD^;k*iwyq43AgR;w#$4?2=)+VZE<4uPT96fM!jmh6~<@`^0hB81Ke zjPk%LH|We2WQScMEhBsJR6Q7Yb{@y8jiMgu&v@DI03!pl^H%mw6G6TxH~}vBb>GMr zi0(%A+K;4UkA#frOL|$-Q(}Q#-Of#BlS&7mxuSunPY^XErqeN&0;qc!w^68TR1w)h z0wxyjrLvzAc=**ZvLTOo!woSsM0?>*aUaC!CdP}fW>Q*!@@-72nl}FnowU!l=dcZa z@_^ZR#G%-@y+cIHzMu_vH>!g$k))H_fNY8{6Vu%K;3q$E)#vCTWFOA^EOaV&0A7Us z^zLcG?zlTQIBaVt(JzKRrkcQ4h_W15zE~=F z9}eZ!7k>FjDosY}0iQ7j1}N#$-yx3SOw5sLhx)cYx))i0o}ecFG_}nk{BG^t#4k@A zcqPsS^vGEGz!z40$2}D75hAc2NS$1>5IAQpPY+P_K;UUz~&BTujUl$|K z-(Pm~Bl~Hv!;wuOn{L_xPby>|j-RkMhpB44C=xcZMc5Catn6dU5BGKvZ7+!sbz5%N z7`vLbZB|$`gh&={KrQq|%+zXQ2zS=q2*?#MDZ%%73mW=8{S^y=7@b=Hs}0-ZGYPEt zrT7lVgO&%^@)tjk{rGPMgXt3~ceiLKKOI=YTQHV9Tn)Qb(=+x16t|)0p9f)l-$l$jy>Ze6b~|x~&ki z@?AnlA%>+3U#uS95}8Se@{-(v@EnY$i32lwUMnsia$wa`#iQ+Xa$|(SuOOFe1P2Xg zCnS7-x&!YZ9q(VPC?J%CC81Xl2)`R{X;CMVdDrDE``$~9@CO9d(rPA%x>x&~bld@$ zPyQ@mOI}=BH4^87G@Y2qB>V_6=--;7I1QVR5tl~h8idb1MQItK+f%o889Ia(IaZDi zbO13go2=#l62I3B<@84l3FxrUVLMLiBfayHgr|t^ex#R0(voOG-{CH>+!^%Q8#&ID z)baM<=JR#8;VaeA$X#D~G^=%vh|hXaZS4!~UlJP9wg^34O9ytP7Zb!~E>W@)-GCL1 zLSE#B*TBgZI;dkQcHt7Q~Q#Vqf6tFve18N#V@JDMd6rGZA`a%Nd z<=N67In_w^Jt}atK8sb+W->nu{qdL!BU=?yK*`|f9oIolkgl1;r?DS^3g>v9`Rn_M zSUf2eABDfctH&lZHj6yoS|XT9 znc{&;%u796C&j!}X)~#FylDUtKfb`bx04T#g&^yf?6xfND;-Pam3j=s*p&d~a{EG` zK^NLWt}~NQiy&Vj8>8e2SBZ~g0-^Tf}A#yRbe-{u9_FG zv-yj@lEK^pGgzA5{I`{@d>c}@g5?%_&7ZI5b6<)VtmRLKZk&b{hRxGo$8#EPTbB9B z>PdwdMW>a`(puHCM&hr zM-eCmPjG7iQphfPSn1a&w|k*ltek9+vZ0%2bKNzN3$CDtk9Q?HPuMq> zhYcJ7^ob&|@E?o$KdEa31B;MO)Q+GOw$d{!tORd5J%=99^YkB7(xU%ZODJ zt^_`g_JDhU=gkscAaL$Uo=BO$LioAGKMIYQQx?)KtbzUIlhLmqA=s201UJeDVZeU! z<7+xsv2Q5X^p@Ygw}3OS&ndz?_>%hbZzFl652}&4y}eNTR00+Q|G36ouY1`XFXluB z$-rR@9?@Ba1up|(ohYYuqgR06m=L_axr;vt^1kO=^KCa~z}(z63^cg@@W^R#D}!IW zhj0X-o68M)%16rmpc7-V{B*CVIjw`IXLrFQx+MVjT=4QSrX;&D5wpMG591*Cs-lx< z8&B(1#HYOK0>A57d6SxXv_ADOoPQr|EhzDz;cV?zj7Q<(_y6z_dtyz1=G#Q@dB^4p zh(GZNueax|3vFqhT6pE#(K+3Rv)$^?2rvs^+yxuwFH;l=RO~)5^(1D_^ET81rbKA0 zy;CxvSe456LJAmBd$=}R-zMqq%As-4Wr_EWjM|s=PFndMAg1T6qdV=U8a7qYrEm~D6S|q$@l9m@XdL)kCWHOrycnLN%-P1%n0WVWXsYYd_ z7vu|O^N~!yw8vc66SAD~5zglSfSZj3oItm|iJ@Q%D`6dl{fNe8BWVK*QIRzu(frKf z16)f!uxQ);qtg9-TTcL!Nc{dkH31BD^_Eq+3R_$q$I$31?;}*57YT*~8I7l2%%b0~Qt>7#Drz2efcB{b|N0hNxA93j zR=t6M0*ulpKn@ewXyaJt*? zd_x5o&Ji1j82WeX{d0S=WQV!5#=z>lRu*1>fOZo$Pjb^1XXK+taptb#bh$!UtL@#{ zbkK2#dbMf!g&X>KT?&gyvWP!`tf573?%;AfB4O}8Wf|7E9(RCxH#@N8fd-DaI_cXkx+G=6|BY)x&1^Z)tyJbWA-Nflm zNzg|%=ToYTJ8An8#f-n~K}A8oR%~%T`Ra>M(XbZ^SpfSa@L%RB7B-#Wiq~kpyfk6G zFnMMhJW(V5^yfh09ZLD;c<#^dR&kig9GyrpjMX6e=^y{Ad~_oQT$8;oiOZudRkCpU zhmIMlmk~G>gp_Qm9v(HSfAy;DQyG|-gD*oiWD`jj9vOh9ntHS$8qDUik4AY29Wc4A z&<^H`Hlo`hGX6-`;@Kq7$Y44vFK9OrgpZV~ra*v~!$h;ye|J&V@^-v^F3!g`I22y+ zK`8ajz3XY;lE@#O_u4;)2lOX~wAucyiS{|3#iu_ZF{uDe@_Jmh0NaIwgCmq8{>cxL zrys!!A2nr67y7TvSrb#{+)qn@aP%B9y{qbHHH?~@1tVK?g|`7;w_T# z0THKZt27v%yCi=Yg=lMQODoIdF3n{lnsuTm3(sZi@Z~K0zNG5-R*v*`Ja=rU6uT$k z662U1>Y4j*kY0G9+*}7*UP~-2iG04jKUly0uwDQ5O)#DpMjCFVp1{~&L^1jyM8kts zeuxMGy0~IGa*QiEXvn@9F3^?Lf>jKI`1kGhkhWp@TRa(}sMkMya*M?=9T&OFW#K^3 z*w}uWy|0<%1ckVJ27S>_0blB@w)ifptxsygH|esjB6ARRR=Q1Kc1`+Ox9e6%8P~|W zh}cKI$&ve=uPw@xMXrr*(#1-bK4A^XNb$bMY0)uz-Cy0exC%};CZ3?4hW%srhOef= zUd0U^1FPDjn1vV7K`FP<0!#oc9O?qUEx&c=|JGExGH?G~PsIO9_4xF!p-%sO2VKV} z5y%iCqMOn(I8banQ7SSCzRBFb17|BMiBqXqX;Yyggb$q{^3qgP8vZ!A&z@@$MmU)f z5)+>%+ieW14h%^Cj&w0MZ`j&9ZdKMk*K+RBILMxgf#`dEkdv_sq(p0_%Ho!jhqg42 zk`Lmk78=yL5S`1itGJz7P1RNJQs7~y2!qZV2X^cDE&BBE2kVia7$x<*H*w)-E7{2= zAU)pT@nVLAX>~g_q|*-@CAyz~RMAzVge2Xjk@(b9p*c?&?83;2qXKbd%$;nxwg_o{hx6s!6zSP(z^H z(Nv&HeWOInPRHc{7za=V;uRBaH-q+5uR!P?#2$F?AxYmmL0m^k{EXyqrs@QxmvB%k z?cdzoK@Sd`qP6m-@(Vd~$v+dFs5Lo3sBpr%+~039DWvoaqs)E6M$I_Rx0UtzUQno0 z-`gttfdzj%abeeZFe-0LBE6KzIQA_)ErWa@etTH`?1JBf1}y?979<17NEfmU;T zU&B@NyH_(DzH9k7%60xRy}-iTlO^PV|7fzCT`Ad6oK}JTE4K)rjqbe+wT0^yB~XNZ z$x4odRMVKgP2F18;bUzu(XMYOGE)t64ml9&R%`Hs7s}^Cb(1O4c&fx_U-@onJ<-5h z|Fv4L=T*eQajx&V-`og|F$ybJisk1dM_ittG+_T?Lkm01$y2YY>u*if4?z((C2EX# zZ>rK9<~!%8znb`F9ITWiF&4A+NqkckLfc7VN<=|?AuZlZUPkII+~VtjI|_+rS!cf_ zZ!xlkzPD}xXY}Jxby1Wa_bSTk<_KH4~)HL zpEGiWK-QqHK z%GNDU|f4H*iipNakn77<8zF3&y!=2o8yr2``2bCs{uhwa43t~}bG z3`QyrySAV)ycOloThkU$1Uq2u&srsY9R?#F7JhGm2|(M#-tPr*MkBVWZ)`A9!rxb< zafcDz&Bh{_h*Qf8sgrb#;huW4S^3;2*FkRYO?4g2l0#GC@w*JFh=O&268Gt6C-6(# zvG^4xB8kTFR*!i(@O*Y2kRfoXQ?M`x=Vy;O&mzZ8bIutY7R@}3XZBTtIKywGZVek$ zAYw6GZ55@AVpx3*xn9d-i1GGyyfD*o`_`Lh>BuUSYd#Crt6x$xptsTrcI|eo&@DD8 zxmb(0!UN)Q)LB^Tfs!_)11#jLla+SKyMkHuxv2)opD2+}a=*-K-$?cnPfek;t%PpT zCG(rU+rY8zOACAjdbU>J=l7A7={{wA;4JGoquU*@YtOI%RTel4=z*5<7}N~fA(W~p zEXkOpxJOHaj2_i1CyGHAm`Ncr>%Z2*zu}YJQdN!54J6CAYW`4F1QoVsB&C|W@Q?gi zCzp%V2I7LtsR@S)7r)&UVgP?Vf532c73J9cy&0n~R!S{UU z*HD_<&fJer_kI@SS)khWvg1A2(0E9~-di_*!8-TRX1MhUyT+wh(i(hD1ElV9&{0$f zouEayp^j$uX{xwH#q68g+>ao_;^BN>VSQMms`z#JO&xLCchHNP99d54NC^EB-oI6p zf>lJg?~fhnaBKrcYJSX&TlGgF5t#k_?fcB~!xICEDOmTUA+PH}Xb+i44)Edz0&8u|n=%d?p zfz?AxWk#D;m#erU#*|EVx!BiOI#ow*lKZqcsr4>}5u0F%usHP2?DJ$m1>3ZKCqP561v(ggb69h9t+2p3yHu&iO99DsTx%}j z?8~FZziptA+j)dh-6p_?F|0j0*bB!qW@(^p*VGoLn2g$(l}pLLd(QliZysZ3pu@7=I5iS1K^ z%nrjBJtW0gOQ|?1;=pOj4!qCZpoYA{QO%PNz21N`s!|bjnBn7|4mtF3IKDaco8l+6 z8EH(M2u~=C=c}>UV9xz$i2nZmi^T|_S3XHy*(KJ!bU*Lx{}e0~OhNv*$J|E^8N?C! z!>ag2BYPW_3w2#WcC3?F0yre)J_`>#OBFFvWT0N>=HT~KuuALb`g2_8+x0FEWHY3x zI(2_YGi^XDY%WsO@T-HAdk}gVOE3R9ieaOUV95gv_0i;p<;*nJ@pp=RU?rKi@F45V z7e*z0*9PNm=fWO%Dq#GC6s&G=8uRNZSbdt{|KA*ZSn3A`bZ_~t_5{!yBW|UYp~aBH z5NKn3pFoA(^nK7&{n>eUX|M(}2}WhUdxGTAro=)RO{#UuUj0DAOljaCxmoyEvd~>U zNQ@m82cQ0-tp7#n>O(ulU?%c0)13+woP)v!X#)xZ`CT_(`NcR!eQYrXb4%3owRoyQ zN52e*`Jd3>^6dzESd9o`G-VfUPp|(;)FE8Bn33^@QDk0Ed38(f7LEV< zsp{tjG_L-P#Xpgf_??F!{wGqwSbdODNKuoJh%Bkga<7b->t+Izfl=bslaj`bkOeR7 z*@MpYyH{Tj#oX`9w?y*Vei#F8K8i9b0aeH;q@ZV1_5RvOPYm9au36{T*{>ia!Q?gr z&DF0g;_U(o$OPzenKeK2ee+~*;UbCnr>=iwc|St*m*XBh{e)%cdr_{GEM>r{UBnG) zcEWC&+(G}0eSCGOZ3W;^(S7&OU-Ai|6(&A{sI9B@L2z$FM+K`YeE<49beIdcPIb?i z5anR|KW8x{5j^qN**uC_SH}Ms^F+?WuIcNbs6uX_g?%r*Xhm!jte2q0(ARIh_qs&* zomVwRB#@(E{2-W+F}wW-WQ~^g1{b5MfiKga{*-*AB$5LP)LKB-nVDGw9SQsp=2M~oY}A;ab&V;`XxgJE z+@G)9{2XL11S;H(f92^am|+au-g4>IH&tdm$V31krO8yCdjp$Rp`Io%3LL=jhi59r zw1cMJIIzAT652Fb|2gJApvlkz2!aFviRVy+Rk0$G^+443-1 zE*y+}e7gNl^qYCQrGOj&=UnKTY@_*9<@_SF(otePs@59FbGzh804e3`ti>>7+X zK?PWiB}T;ny$uM+F4|+RDU^?E^xruXU_uPP_G?$>rmB*9@9OzTr?JoA!uPO>EG5A` zw0FWE{ijwmm|++fviMGm%fHXTfJpH0YM3#!Wk?r5gbR%ViKsI`PS6qbT|1i*d{yd! zzz%T%#Y$Ygg$kcxn}Ed=y&y^ZmTih zMBgB)%9;Qli0FgEuC6czYZE9(r;!kje_L_@Tl!LE+>x{ph6Ndgxy|B^fws@ZP=Mxr zR=i(*H$|rL;xL{`0xxGBgwa~?aWJrLA=W%;hnUdn1_TW@;L~5_V0`dx$T(hqs~le# zX$z*4#B{}rh?_zAf-gAYuDqURO*An&tK$%>@6$($@x?c$plBkMH8rfxVC+nYA{~e>_=RWaxetASR>JDG) z&wOt&+J+b!FSlL*KIM_O2^Qj*0yZ$^zQiyF2=&J1m%A~I3V?SYi#Pg?hW9?0PWKsf zrK4th1Nu~!z?-msc@))dieU(^drt>N3xUx~AX9=`Xt9GJUYRc(=q>2YH^Tn}_jMM~ zPtunE&fn~af%u=o!Ae6Cq8?)vt!!S0b1`H0n5kb$iXUdZehgy530DV@mI8hNdr0eT zj785EDZ>!FPLH;jQU=aX_NYa(UIZWDXqb(IB1ar_2Y4n}?Y*Y@YXQOKk1lt@9Ou3h zPV8`~r3DNY>W^&EA*(T|o(ue(Eqm~hgR4>^*{M{^Vg`r zV-|gfGwLYBg8o8KDSGx_n4N<4nH|~n*VR)y1_XObhg;KIRcg!SWLOAT^lLFriXWGxLJ*7U*crP3-;>J zj02YSdikR%+5aRC_Z1X^;30p}z6aZO)tpjDVrtCqEtRLA?oJdZJ~Xcz$M~PXnnK%( ztLBI-$IEipN59E6nSs>Zp`sH|mTpb%PiE!KxIuG7_H~H*h#BNol*lTSf1(W#c9)~CEcG|bSi*WxUJa@;t#6B z`42?sAn{+wnJ3yJ?`1kJv;!k6`Ak>p>cYQg0`-m+9C#J^%{E~Jq{)>55jTg%(0oM z=m+qE^T}Issqkc<)3$PDq&;MmpP&B^ATmW_%!Uv-y5bj;uY@7F=fW>BmQgP@^rD!J z>~jjcd3yDEIVB||k9^bDf1;WrO@Vnt@8qtOm6yvu<5$9krbug(!XEwWpM?fYkNcpD z?Y0m-QV-N2d;8AspZZ?zF*(aJ4ud&BnDVk$E~U)nLice^(j&B>K@=~>Q_X)}X*X&4 zo3c>@NTTmgpaBGd_@!V6)mo|rIuCG*qwgDx$G9{H5c1ni)up)4Qj?y{JR`-kvn*~n z&4VY>cPb%my5fZRtp?dggqtJv0qfXcfc2%n4mP4K!?ff;hz>$87s57CVB~xCyU@@6 zGk#Z&AwAQrE43TGP6Xfc-F&$bMMBVmM3J2xZIO#-SirzC55F1zjnsw)*vM2x-+ufK z$4$HC!aB$(bm_&J|KRYSM4NSY2_W%2j86k?(ys2S4s%~HjxLV~O~1$QZ?u6Sfq%AV zTcFpYxOLTMWC}s8id2uaSl{kKhw1t8j^m%!Yx*nq8;)OBuVlp|Bm?d!ErVKZ+jyF_ zsT(|ct)Qx^XmzsGJPd1;0yfxfHI&CnV50|yf|@YM{_>%O&qonx`zb(#DPQGl7bjU^ zk-?hh9=?{Y(tE&k^*~jj7;qAoQ~v4(Z7a!(uR?R_G%h$6qNeyW5&;|(@1J*F|JrkU zfkAf|@o4Mr{sMfaXJ>~S-f0D|s?{mxC`XJGK-QN@=Bvt0oiw~;--d##M4B74I+{+0>O2`#s29HYmNE}QZ2Dz51o6jNr_rKZdePs89E6J z;zP&|5=0$jFQ#*qZ>`HpDrAoY6IfdT$Z=J%Vhx}jHs8}nrtJazWB;Nu1cZw}gINFh zN=*P>(4YDG&)I6KO&xoEr65C!fTfQwV!4Get#5t>Xa0!6f($}JEV0XkOj;?X+xA4J zy%^v4{h*#oIOLj+b}r^b?d?&15oK$Jvjs znwFO7SK78yLOv4Rt-*M!-T`Bet`gZc_Hz`t;CoxV$59Q^%2ul7tC`* z2r8eo0qUNj0h*-_BJd)K6kSplB}hG#(G%1wMbUsnj(9u`lGKPF=$~Q<)EE>&P>e~l zoB&?xxkTPjf$mEHI;J#NGVc@D)v~W-37Co641q^f_J0&U-YTr&Y)q zCb1g=VCv4Em$!-lLSvApU0fu1a(T9eYh;EAr@$yn7chyrjCw;jRfdX)2jKeo(z1h= zbaiv2z{d#8O;z)g$M|h&pSaKd1*S0Ibu2z+%}T{+x+_oT%3n`KTG#ksA^!jL(W#Ix zMkq-@#CxO7g3nGy72cija2GJKPvVGrOtw{pt%tc!K%x+<2BL$&q;`NUctk;*eCMvk zN`EGUB+AIC8eoP*64(W}q8)^A0TJknW0Kj&*USdXKs^Cht70%cE=7L}C_r2z{xC+#yi2?pyf8S%78W;dTOBT^hjH4yc3~XF zj7-1{*5$d0#$zUpM~b_wZ=1f=7ApHCSWA&aU{0h;W|~q+e1m)2v@iAPY-Se`YDFF> z(FjZq(5>seyUp7Fg74cs;#(5#phJkaZ+Zd^%uU8+b8_X31h%-}=E!SfM8~Px3-+Vc z`{ct$=9Trcz*xgLs}k=2If9!+Um^N;XX1)NA@Q|~N-|fw4b8_u9N$>4Adc_#J7WS@ zU)au)z6%ti%v%H)BcKIQ1jXg`n%x>2DG*M`mdSR43ow^M9%us$2LWpoXp15#Y?QF~ z=^LhA7hqu&La(!$029{07#|)@4KMYO7chN?hc@GBvANmH7_g(Hq7nl)x}!=ao7lXj z4+i)4zOKhYY+K=Pxs1PI*DT!zPJ8Zl?l@!}I+_0eLjZt|ppe8x(FAxGfnM#3pksF? ztlr}qwK9_D_Q}`SD|J9rg>et@!>`;!qT}CPi~nBCcOrRg_`pXR@)j^IVwLvbGfA~N$bjwX16BqOlCzgLC*dol~)w$Vc53SHEx5}kR z%fbwxwz{J^fhoPNoOD#IQ@K+SMifv8E(Vop{N(C76fjDT70p~ie5LbLXQ*@VIcu)Y2hTslExl8G!>_$*B?0ZkF?ccA%-c^2%N{kkWa&J>`Ub%u$LazYvbkluRJ%(jXrRf zs`~k-V)Xn2Wd!ThYJsnbh4|JNop}|Yf;=l1*yloYe?joowaxvvopb;%U_$gAw~mq* zR|gOt0|dWAv0PU>jUA3)2})Ed7&|8+6+nX9 zlO#R8HdcPuR+d@xHLIM0V*6ImI>9U19op^xk)f^+%%)Oyj~zG!bV^;23&q>+RMdyI zr6O#Q8{x$|-(IiY5l_4(MP(#!sllvqkXQ4!_ zF zEllbJEoSZz)7;-z#70~7rpQEm`LcK>5eJ!wG4$cUv|iev8bA6@siN@YNf6*<>qvou zb(O>s#;#svv{9D!!&o7#d0<@7RS z{dnI2BJNB8 z>#e3^VjJMEUzqrfl>BEbBP~n)jWVA{N7#cjn?wNlw`aa@531MQ{rmGg=~tnd^G*)hKwBeXLeHc%cffq1I#=JcDd_k&?@bRy z9!stCjP)J~R$ux`wL>$r+UIon<(1Yylqjfy#5{idV|lnL86uB-M>j0r@{n!b z6J}|mI_|M4UzpQl#VwxtyLaGnVy5|~Dc3DL-(g6@J=dIpL%k{-F`mqNF|E%;-bG~5 zl3bK?*R?D!MJoB*VmL`+4G-rALbo=9WD}-%FfI7uWbb8V+rCh6n!3tuDs|Px=Vj$= z=o>bVbzvp!$n9u?_-c?0FpRmzPnjxEzA;s=pX9mu@X^=xta#f9fFO;lu;t@d0L-GT z@$!IIpTZg(_b%w_cCvdX>YhYKY=v1Do%`8FCALH+^urk_rMwRy_CwaEHwOVpqWu$t zI3l>n=i(F#xTWGMiDIl6s4B%vT)GHAP5p)XBGjNxn5Q}7Ooz~qo$Wa>$i9ee+Imm!MN5k%IxmehV+Nlhpvs&4E4{AHPoxD#Vz;_ zlY-Ps+>FLtwlns<%?&>2-PHSbY;$QcLZ@D!z;fbpz(Olzu5E$K=PXYoG1~BM@{O9i zvtgZ>Q?}RTUiGy2HR@Y$JBxp-Y13&wi!N9%-Hizo&T;nTdp|TTj%Q%}nG8tU{Gh@c zTzmUg!Gd`}l>*HcIn>kS2`4s+ zo76^RjqIWP!o@W4TnkwXKWtz~qPAqbr?{=xL zIQQGEggtgYuO&@xmaWbS9)0ozrA_g#^SNE^SvCg zCmk%gpJ7VvG2FNRCR9BvlI(xnZKjP9mGha)u5PO}&>Tl|F?G6-GZmshqX<1Kh zBU|3dn+maUvu1TC`Of4eP`x3v;wHN#*bWoAlnv<^gA)rYUNvOn|c~t!L%jDF|V=taOzJ+m^pO;$=AvAwB z6=1x3D;xZ9YY=ewzkP<{Z8(+%Xly2#lDlNaK4^W^3UKH@|HW@bRkG#AfL65&bpL44 zM45rz|3;4=lzldy9_yh9ES{1Pv2>;=ZmxGMqQYhl;~=o`wNae}h|x zLiH}0Dk%s>3(Ke7=1CjZQY{vSpTe@SLNU2Nc}9c7k5`V!Bqm(*Mz9pcytb# z5slWC=>4-WGer-*cN)CfuQ;Rb-wqKw*#E<`dr@ddVLYSX3Jm;GmjI|QG! z21I5??F@{Viv-U4iFc6Tsgw8%Z9s3Vb8S)0CX5RtLRt3AZEFQNw#V=mk5@P?DK8jF z0J=#EAQGz*D38Frn*;xXhua`!$zv~wNx@uKL>4Y(((=#bwR4)V zr<6B()v>_SYke}WJ3g-O-@QCf7*OSNgLvec8*pfZ%wAkyk%?ON?#sT|IxQ7ZT&|maAFHko^_vH z+FRl#YajIZjPn+Ey*p0{9XVWhEVkFHDB%u&PmTV9u>NI#rmrd&;<1DNLUTmD7J~6* zUgz^F_88&{qVdAak*Q_SPgqrWDuZkmxH&ce4<$;&Fsu9yw%ra0 zvd&`p4VdZo*M=9|3ufJTk zJ2#EinygKgT=m67kZH#BLUe}K$!pH(5}scsf4?Cx4!}&Ahy*{wqiCl&0vENe2*;=S zMC#>;2M?VN3o@wg-xViMjM`B=9P{XxJz=1a(VV54N+?waD=H!zKF&bkM-6Hv`MHuMESp;B$=7U z9qRwH9XhbPs`U4Awa&Mp=A8zYzDD9FU(YGq#@l_rY-zF-Gy5M?>|{KG~Z=Wsi(@2 zGm7)nh9#))DNG!#IsTOE38fq}U+5YWzZi#A$~vt4u1OorRh^6x+52ueh7wV1&~uO; zUKcHTCzmW{sH(gY>|O(x%d$IPvTG$=p*ts1zBuaWV>`V50q^P8<;N|m=45xu7=X;Ck z?UWgb?y((X**&K#Z;bceWK!16Hj&-x_!0}A6HWE31z#8i(T$|u?Yf5!s~zhtAN6cmG16gUW^s=K=uqEz1xPNlZL)-cpvTGwg^#4W}F_@@H zgb`L3COHnQf|Z`KrkLIDYvqe?Sp>b)+jC1SjI&N9-=hMw2YCy9TE4Jr=}dhse|f`B zY}LA8WcssCwxMog*{S5^j~@KNnt^Ur!@>`9j{IjIKE#zO#%U1PdefFeFC%=vroex?#<}A%(x{lB zd^mM~>{V&~{sviY5=?2+QeU_KJb3!-e_Vh8RME9^*9R8uTGKV65#@d(F75_g;=f<8 zp@S+$@^xg(AFU12X1Xti3Vb?at5GVp7Rng$#GAFC0@Q#muvZ-iHNN*d(4BU+9;m z(Ed!_i1&p)V6S0ymt_k|>mbKxcGertHX}jOB=-+nXXf6~%D~h6)qk!x?rzVm1M+~H zy$;APf;M;^JumgxA@r-aq`ayioBgoXH{!B8fMYsX#%MABQblrMPr{tx+28?a?#FIt z=K|~Ptk7wRwGyS6nz1M4V_x+y>ea^fZZcD0*Zc98+k`k*dL6Sk&yTutpRh0teL?GC zCHFQkCo=0aiRjRs+c_T)e2Iz;fUh=vbO4f=(ZQOKiCGwUhpII!EVp{^|+C z%ZCu2E`3k=KsKQla?Ycw$&D;8i!7t|SaZ7&OEx_Ge>U?u72o|$y%$YIG35DZ{mTR5 z9zW?(lfS+j`NwG1S}8Z^$<(@2()i6EW*7BLi0uRInBcB)l`ZR>)o(%hULe-8)ROGl zISKTcKI+3`f(+$264FlAe;L=Ppc72ESTmwMU(lLnEw!~6sY%&3KiSG$u>MEUhO>N4c~Y>y{x3Fnj;t{PnM!|Z^xtK&?9baZ7e zxwx`}hbD%`?fSl6*u>=9ECPqQLHshO{V-2J^>)n{G~TjgXMI1R)IW*WB;H&WjnA+| z5@A#{zd+8+D?Wj`r+6uL==$jR+R@ab+CkORf@vq&E?seKL1DLctpoLru1@|g&j$%L z5CzheBud*~-JMH>g1)Ce<9wXPI;G8A4rDh@Cg_5llM{ z0qZUS{kbuBh^ceL>J^hu#g!g;<;ZNHreB$yRkFFrOH!rnsX`s`qW73Z9@1KfK z{uBGP%kkf8bnq6T^+)jTF4E}jkIiwZ5HrZLzxb!7y1RDs@PuG~b$S z>4vewgJ>22(doebG4^@Z21ul-&5D7%Sz&wAdqIIgb9U8XFu~IlOh+Ip@&sL0HQiJw z>F7o6Q=)kEh2hH-SX8j-Hw1lfNW3;V!WI%Ql#m-VErUeK7<(1>`0CRHnt{I`kQ9H zR<5tOH)H-62>0q_b!p+fKES6WWInJj@I2b&928pY{2ofV0MMIRsul?uDhS#I6g1{c z8&vUvrVaF!9w33_P6Th+dLaE7U|X&>7`k`{fQmVPW3sNtJkQ_%Um)=rH&oaulAb=B z^2>VU=Tg^)-EN2iU`lHI6`n6(Jv{qlmOn7DEgwMYfQZ`a>ipvCnxCqiZ6#O35Uk9p zf}q7yHN{=(BvnYHq0ip-$0>dd>70osq@Nb;E10E0XfaCp=(T5L9Sie(o5hnRb4EXz z{wON>MAn{YJsjNzgShf7*Utm( z{pP$ZBZfPz?jhi1!+9YHK14&qbfaGs_ZRF(rXUJ32v*4@y~s1|sebt@^yr#^lk(3v z=Xh^qrKnqhzvOw_lSkx6W;Lq2e@hjeEqyPmA4`r032WVdIaql@#H;7(i-L6#MqSJz z@fvoZA*FEdEDd%8d3Vo#UQYW{*OLn4-Ikh`c15Inp2znO4E*k6(4oJsTX|c1H9;rk zORXH3N)AiHUIOFEzZZap={{g(30e=S;l5n`=(bPq>{GfL?$1!)Ey*x<&50bgq00O7 zo68f219jZ2B|M1*Nnd@NT=lFR@&+tEmu;Pj=fP)q-g?y=U%k9su{mDe?oEfl{z&vT z;><)zWWpPgsdYT)e7|mD@&NDYHisWfoe8ksfR{8$ z_x@Hj)Z`UeRVVl?fAhN^tDC)GmyFQZp7!*$VrF#|+5fzFRDW+!GJx`I+?r{T5%$g! z-oN$=uRv(o%r~5@C(;IAnbUg%<_{;SSnp9c&P(Q4{x$m8bBM#uCbg4V;%%LT;e+DD z#pi;_wY`uYh5erO`Socst{xJ18$eyua~OVle5Vyu>OMRZdstH#W4SNiU8h^`x;o%HNvU)2+VvDj_J0kN0tL zvOid<>(E%T-px2`w{AGig(KJK)*t_So{*0%4P!~X!9d~dcTNKW`aMOdpwX_n^$;wRJ``G=W zPy_~m>$1+sm1^X((U-33$ArSD(EOHSlA7GCvRrLFP3r?xpz6EIidI$nlSN`|t}^V4 zU%XS<&EGs86rX(Sd-njHRXa4n(C!9iDNdVxn_?FhDgEX*{+N$HsSC(BtxVJgWQR?@WpNw5vuAMs={HCyDMysbz!bWMRheRF>D^j z6P878xTCU#T%)5T^_gEhQgJ7o?&>o#_>!T&6BlZ4NvJ^m`3BD3j#MLt+D;|hLH#05 z5=FU@028lEnbCaI>$r2-^?1EkUe|8A8 za*zUA>!9)fq2;<`st1-WH-BKqS4#=YFmR!R>{GWRZ1k!c?>s$}Qt{I{zBs-luU$yT zU8+k?Kd(F;jufN)F?3M5xEY~xW5Wq&6m}_R&=kiIG#!8Bg_^?e)~R>X2h{?T^xH(V zsVaZJR<*f?ch@CM(q_w;m`kzWFNQ1h z$W5h&t83mMBAk}H$;wEa3vYx94}0I0&OnaX^N}8QbkFaimz~x+bvk+Wk_77~(tW(i z=X?V1o!M#iami+=IA9?@bGDxE6d>tAFw&AvS66_xtv}GJ-fic)s4?-7q6g@P7ocgB zAjbHsH*41ZfI=Q^%bmG%khhEks6#s-?&Z{_u$Y*MLgIsFw`Cbko6hh%??>fE9&V$} zmW;YcSm=1j5!iSns(cJH58X;JeakfTXH)S<-b?_n%R4h(IKT_r9&5R2-y|6Dljcr} zUQf)a`I+F4Dz)OwM@C0yHjBjqeMOPITi(xu+O=h$HXpQ z^*j_V0L@XA%mmP-CWXfv(9Z))XVMC1R6@z0Ao8vl+T~zKn@iz(PcjA}F_OQNzzZt{ zCA}K&{tE^0Mo|u@tBKB;E+=z7>5v}veAins*9Kg10$K(AZm(t05c!#x-S6@brhG8V zJ&+zIgd?9^IgFQZj2JvT4YWX4<^4_ePEgx-rXD$x(hOkrk(m*^C(>ZEY_shV8}An-5HM(BtZ^~Bfd@IGy$N%C6PN`;mjG}Yqme%3wTTRFF;N{dXQ z+IJ{|vELtgE&I0ek2?|fT)Xw@?UG%sb8&Sr<*H~EQCtQg$cn6>?>(#h4G~6a@z(E0 z5OLw>tET#&&olkq-g{+6`a5(xX`2sEfrRlVMb;*8R6kR5X(<2%egTBY8m16=Vtn`j zV`t}XAFzbh!v(Rs2(tsU%lWaxe?-FLy1H$2h4EA64C?%$A_IL4M-2DF*Pv~Ef^6Kv zvZlA)cfus*h>wDM2vMwUr0up(USMQ};<>t~^JKkerBa+AhqOK#jU4M->cMoz2NfLO zh-e6TRp}7#$I*A;GCO!EmhVfT+)je)Hy}tpd+|b(<;gXxK`RoLRo!+JC7T%fMg?Ez z&Ci%j?gAGIqAxGd#0EIV!Un9um#C9Mk%ogMmnpY|W}jt=apTWJ#L;7tltd3iQo2YL ztVN|t3q=exU>BkGegj&JQG=7cACF1s|9I z*aQFkEuAxBGKSUgyv{JNtt_tkK`qk3@)m1`+?Gf7yM%x~ohAU|QI7(yI2?PH$mg0(aSg#@PgB&M4~5ErkL( z`y3)^l3^B69KVBy>$9IUGp$P#S`>+eP1++Ph4XzUE{2`4lf%gw(bH!yv7HsVEdMe; zI>f7!exe-ws@Xt4){_l(_CfgTL2Ocf;a=N@^v5^>*TLu~n(E$XJe=niXPnnpr9PG$ zr24-!(|^J1VwT)ly{zFQYAuwF4Od=cyLc!u%^(`LK|Hqdhec}KsY4>_&GC23>OJ4f z;Kz1|r2FJ9IeB&G!D3{6p5v@q&j9_>T4Ide7Z;F69y%?yg8DHwx7c^#G27#BRn?Fz zWw8+Y6utmj6IlX)P0w$@F2IQmq)Gyd_PN&tgJX z*9i6L;6fIHWG)er3_#KVrL><$tiw`Db_wv?#_`EtIdut1@hY^+AwRnEQxp6r12dA8 zNz6t_lMPupJ!%_J^2V$rAp3Ow1UKx}IOH*pURf)zA^?M;v{}(Abc|EAuEj)>R`?AD zj%}>!-(Ek)Rk+j12CoJrUv?d`>4)1|)nHhlAp?O8A%u>FPfd#1!tHasq-Vv33Au_ox!fgp0|nOP(jQn$9-L$^j3 zI%4qMiv3*_h{Ne259?NK#><|)qvA+wXxXW^HSf&@oQw7|Iuhd#J94xrP}QH;5C;zL zN#`}>u-I>q;WhzP$Ub{N^bB*1SA>;^tAnM|8J0zUj+d#H$x9&pJHZk;eaNCSXl{M)*ou+wsrPasfjf z)R8hGBkFsrGdlyjy`$6KumyXMzhO3gRH)(cwf9KX(+@sDXA!KUPIpE}zaDru zZibMY-(LEYHN?1~@j^_9XNkZGFTTSzC3)gKV`Vk<>MI9&%BM)4BUW{kJ?gWB>+d8_ z+nyW+U{>F2$SnRJvs(F|S)H)BvU2SCVg;iNTWz_F;5ap>=t5>8q?FOZP;)~_rz7l8uj?88p3}>HINXwZ z@e8BKBr(9hqKu|(kA$8wv6Hyuv~4iXJNtqF+2P6E609bOaNRa`2wv}cvF}Cx>0gk! z%7gus0>$=HH=Bu2v!)!)pI#)2X*TIWs(yZA$iMUkl;OOXV&pGSCk07?2DSX{5{7?R zA2(tUH*keP5pGQ%PKdln6RD#gLI8=c;3XHRISqD#9*3(hZ5@0o*9}MnQn`LNXz@SYG8?LUbT?Xvh{Q z0H?`W`xbp=kL}y3~7ZOM8mT@WV}D=@AfxK}gYFPe3T| zp)sL7u&{tcK}>X6C#wj~sE)bL2S@9(EQ@wqcZy3;PUPZ>t#~(@!0|z-ef`IR* zCJbR>A8FFXes%KZ6=x=oi(s0li>ARJ?-U3!C~K*}zO+c#jQ&@qm6(G7bIiM@zEh{4 zSk4VlR^%NI-)d*oBG>3T#0uF#d!t<~nk{2>+Ld-g<&R$riJ10o&2Gasz)6D6gSBZB z`d9gz3oo^BI_tTA%L~X(>taQE#j@uZ+KAoLhyt8W@nZHTcQ01LMUGa3MB31ThCY-~ zd`=M3)%fiFMefg({VCN}o}s}8P5+AqxscqcFoCa0ZaOH<0awv8YlD}RMw0rsDb4bt zZ{E@#NQI+?_FONI;5uz%nJ1mc)wU`gKM{l0(fW^kW)vOxynxDX5pK5HIda}vU;yAtjg}4#@(|87oR$*|DWIh=6}0NH#!^Y^ToryRH%-6 zFs?%LkHeD`UY`p>^1jQ(VbgCHS91hjJ2f{*G)*t)?2)Mh-Tl~4^OsK+u8HfNPg|UH zW9ZT-;rIKsJ?#<~sZk5{dARF{fNTM@PP=R>wT4ry{;bP8W^1?VtFA9X{j*qI5nwb1 zGWvK1m%M3fAZ3rM7TcIi-x3*%D|qtHF4#9aVFa1~@#*pUjG_OR|M?#)evM_UKE3E7 z5-T-`n-4TaKHvggkkfGu%JH?A7Ed6Q;(@5rbZhR+>F&`QP&Ws;XAN?PkovBF(fS;o z5`is?s3Fz*$BtY|>xN<`ipTXNG!h(7^AKdhC z8+5%w0j`gZatUKGgnd`JN&bEda$&&9JI=QIfgcMnQ(pb@s=x(AhEqmpgg>5TN3rvQ zCO{6dyOLK7K%k;A@<^&s;ebF0$z!4bfdD;wdKKvPqUTyv@kl`VCz@qr^X1V5z)kxj zT=f!YcFqtsU+S6dw(xtPMlomBp*9rRXpY#eMie(=9K@H9GzO*gJ#4^C=)ujT;h9>O zcudGmPbmHvF5|uy2FtP=#T2yZdeGUNUZ>nUzW*@5DQredjaK~47W(DeFeO3_dHsCq zL0pB^IegzxY#4k4(sqLk7yIKS)YTHq$T9fJE`Te`jDuUGavN~G8|qhH5%^A@=Pl}C zDFYV(M-9pGeY0epH}jyOw?q77dSZHmqpNGalE%%MZ&68Z*<-qBz}_sy3hB_S{Mmq(;JTvp39>&y z<%lX01Gz2`9{;0BuU>7>pd%^5I^naBjt22x!($H|M(AvbLHE5Q&|uqfNzHHfKRHZq zTCcS)=xo{GlN=Mi+?IZUbB{etfOjK342q;XLJLVbneg*+8D?wGZW+X_xvH4a{K7){ zS;g4QDB#=5lzsmZdb$w<`{{p0t;FLlsA(v1rTOf5GfJ^oS4_+s07?;petn`31?CMY zStqxeDVaDpK1seuWv`)6zj5zhbO^H^xUn_2ayT^~s4F$NblcH1g%U?ixy%dTUoxO8Ih6vpikQe)n#!3;L|d7+dp&UEQgep+o&? zaRreOeLL2yNv)>Kc+X#|+a=I-^J04brloQx;i0+KIrn`Rd^6LG?PTe*f^ok60ab&M z)v>hujUa1#G!y$2D617Alr5z#*JtxPdCQ=c;=3gEy292|WxiO0MK^$uV-7fSeA!u6 zlQwB@|AaHM>jexwSDUe8rmO(38%lBvcY{Iv0xTYdTynKWt%M!}*3@$_eNDx95zJRx z9neu0uDoj$vFsx@2j&(-Qpn0X{?PI}ioc*j3?wb4j;7BeQzaT?S&*TX!N_u4bSi)! zFvh9{3tS;|DP(m{3>cJYh0QR{iJOSiCtssGVsT(DQ)?>O2WTSoCTZAI*u@`(JbH-Pg?U69nwXZ1pm|7{nN*A2A6e}Q^Ds|@Qpc> zeu!o0OaqXiUJl{@HShWx4iyaX(6d{OE}@2paijxpWSb6zNcR7<0L&EaLo55E1asT# zG%pNT_QC~XMjJRRgEnyqOrr!j9mBgDeTkaC6+V8o~L`nMt(;pX=DXa$^f z4sMKEkM@4mWm$#Ybqcb#xA{bWURP48=x1_#kWS?C!?*t1r=EoX0?Y$vZ}{IbQ{MHv zgmxX$`t2SrMuLnk_*G~QCdlznlHUgXC5A;IV7(+9?c_mI?XbVSl|4tqscK^&mVdX` zwjr}S5F2C&C+04jn?#_7HJ@@ZQ3k?8KQFV5Mp}Um#CZV=^Q#I?vd`1I=c%!V?lg(n z&q6V;r|7NAE?Nh1xkr# zPmc!a`k{mRa@-yGWAza}fBdbF6BI)2=U67thmC>et7mg%3r41IsfnAt$adZ5h8##X zQksr3@p5cyvw{BN^TGf_=y*xrx>44L35;}G=Q!g%u_#gAnaHTt=>+1E%`=~dDUzm zB;5IIDavVzF^ReE`y2z3%fvyXcG8~ep3%Y2tr=PnUEV?}-pRZ0v>X3(u@?VIM-Y*gU7vsLaAh zrDzCiAx4rvUf68CqIM^?D$C+Ijf4c`izp{$CgKk60Xa8=LNY1Ghu5RsAz7{xBzj}w z(YW|?FPz4biwx+fNYq{A4ku|y(1K=|-W}=;h2)pg6uIKaiL(R{wQ5&7#tOCz*atuq zDv7LKSGbuku116DFka@zPiS>>%jj1ri!Q%o_DNLUR zQ;luJ^WN6onUXtricQJxkB{66i(#FW(8a?vHz;x{{F?Glr*yP_jn-0fvo6^xmIGZ> zqZgrrWX0eDeS9J{&Xv~+>F{KcIQP4atK;(@;m0GP%~I)se3PUhbs-?2i#@opDK3oP z*%~>XsOyX54gWs*Bf^6JGfCe-l=4vuB>RkFbNzGq5|b#jZUzNT zB|tc9&DZHg_iN{IhW%%<`M<4;Nt8znzc$t}LlZ?)DAwI@R3`0~u-(N=1;`bC4DGhA z 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 diff --git a/docs/images/deployment-diagram-1.png b/docs/images/deployment-diagram-1.png deleted file mode 100644 index cb8600ad7c4966d5b8f661e67f4aee249ce4db7e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 424108 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 X>aXt{ASR6=ndBjegitW7JZ^^U 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 z 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≪OM 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
    PZRBT+t5eTSWazNk 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 diff --git a/docs/images/styling-elements-6.png b/docs/images/styling-elements-6.png index 99be63edfa3697d497962d6836677c4b2612e1f2..4cc44cd5b84f41674fdd257841f47592ae0f65d0 100644 GIT binary patch literal 75001 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 z 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

    E(wuwN+TzO`5 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%0AT>3ni3)7uPNReL*pMzZ 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 PsE>k-iZoc#B~9Lb9);EMu4K+YDtX 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

    qO93e+KlC^uX=UDJCU^Z9&(az*`Z=u)7 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

    EV})YWW>{IB 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 csYc38M&WGSM{Jw%k*rk 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Ξ<)hCC+OhxP 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 Bwe$c0 diff --git a/docs/images/supporting-types-7.png b/docs/images/supporting-types-7.png deleted file mode 100644 index 7a1918dcad2cb5e740a786e0322de99cab7a8f98..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 103949 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 diff --git a/docs/images/supporting-types-8.png b/docs/images/supporting-types-8.png deleted file mode 100644 index 8288890ebcb8b1e35114e8740e94064943cfda11..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 99623 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 OfWXt$&t;ucLK6VZwDaWv diff --git a/docs/images/supporting-types-6.gif b/docs/images/supporting-types-6.gif deleted file mode 100644 index f6507048697c0badd31d7b37161eecf323ba3dc7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1606481 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